From 8161b155b3800954d14dd557dd352f159898e047 Mon Sep 17 00:00:00 2001 From: Matt Feddersen Date: Thu, 11 Jan 2024 16:21:56 -0800 Subject: [PATCH] Add option to generate ECC key in OPTEE port Adds option to OPTEE port to generate ECC or RSA key types in OEMCrypto_GenerateCertificateKeyPair(). This option is set by the make variable OEMCRYPTO_GEN_KEYPAIR_TYPE. The default is RSA. --- oemcrypto/opk/ports/optee/Makefile | 3 + oemcrypto/opk/ports/optee/README.md | 22 +++ .../ta/common/wtpi_impl/genkeypair_ecc.c | 150 ++++++++++++++++++ .../ta/common/wtpi_impl/genkeypair_rsa.c | 136 ++++++++++++++++ .../optee/ta/common/wtpi_impl/sources.mk | 8 + .../ta/common/wtpi_impl/util/der_parse.c | 105 +++++++++++- .../ta/common/wtpi_impl/util/der_parse.h | 20 +++ .../ta/common/wtpi_impl/wtpi_provisioning_4.c | 132 --------------- 8 files changed, 440 insertions(+), 136 deletions(-) create mode 100644 oemcrypto/opk/ports/optee/ta/common/wtpi_impl/genkeypair_ecc.c create mode 100644 oemcrypto/opk/ports/optee/ta/common/wtpi_impl/genkeypair_rsa.c diff --git a/oemcrypto/opk/ports/optee/Makefile b/oemcrypto/opk/ports/optee/Makefile index 786a9d1..1686be9 100644 --- a/oemcrypto/opk/ports/optee/Makefile +++ b/oemcrypto/opk/ports/optee/Makefile @@ -38,6 +38,9 @@ DEVICE_FORM_FACTOR := test # overridden later by OP-TEE $(PLATFORM) var. Feel fr IMPLEMENTER := your-name-here OPTEE_PROVISIONING_METHOD := OEMCrypto_Keybox +# set to ECC to generate an EC keypair instead of RSA +OEMCRYPTO_GEN_KEYPAIR_TYPE := RSA + # Default toolchain dir from the optee repositories OPTEE_TOOLCHAIN_DIR ?= $(OPTEE_DIR)/toolchains diff --git a/oemcrypto/opk/ports/optee/README.md b/oemcrypto/opk/ports/optee/README.md index 6ecb3b7..9720f26 100644 --- a/oemcrypto/opk/ports/optee/README.md +++ b/oemcrypto/opk/ports/optee/README.md @@ -44,6 +44,28 @@ using the OPTEE_PLATFORM build variable, eg `make -j32 -C supported platforms, see the Makefile. Note that the OPTEE_PLATFORM values do not match OP-TEE's values for the PLATFORM build variable. +#### Configuration + +Like other OPK ports, the TA can be configured at build time with various +macros. A full list can be found in +oemcrypto/opk/oemcrypto_ta/wtpi_reference/config/default.h, and the current +OP-TEE values can be found in this directory under +ta/common/wtpi_impl/opk_config.h. A common configuration change is to set +OPK_CONFIG_PROVISIONING_METHOD to OEMCrypto_BootCertificateChain for +Provisioning 4 support. + +In addition to the base OPK configuration macros, the following configurations +can be specified at build time specifically for the OP-TEE port: + +- OEMCRYPTO_GEN_KEYPAIR_TYPE: This is a make variable defined +in the top level Makefile. The default value is "RSA", meaning +that OEMCrypto_GenerateCertificateKeyPair() will generate an RSA +keypair by default. If this make variable is set to "ECC", then +OEMCrypto_GenerateCertificateKeyPair() will generate an EC P256 keypair +instead. We have found that ECC operations are significantly faster than the +RSA alternative. To reduce the risk of unintended behavior for existing devices +being upgraded to the newest OPK, we have made this configuration opt-in only. + **For 64-bit targets**: If you are using provisioning 4 (OPK_CONFIG_PROVISIONING_METHOD is set to OEMCrypto_BootCertificateChain), WPTI_GenerateRandomCertificateKeyPair() will fail due to insufficient memory. diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/genkeypair_ecc.c b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/genkeypair_ecc.c new file mode 100644 index 0000000..b906804 --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/genkeypair_ecc.c @@ -0,0 +1,150 @@ +/* + * Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine + * License Agreement. + */ +#include "asymmetric_key.h" +#include "der_parse.h" +#include "oemcrypto_check_macros.h" +#include "opk_config.h" +#include "wtpi_provisioning_4_interface.h" + +static OEMCryptoResult NewEccKeyPair(uint8_t* private_key_data, + size_t* private_key_data_length, + uint8_t* public_key_data, + size_t* public_key_data_length) { + // GlobalPlatform ECC generation, then der_parse ECC encoding + uint8_t raw_pub_x[KEY_SIZE_256] = {0}; + uint8_t raw_pub_y[KEY_SIZE_256] = {0}; + uint8_t raw_priv[KEY_SIZE_256] = {0}; + size_t raw_pub_x_len = sizeof(raw_pub_x); + size_t raw_pub_y_len = sizeof(raw_pub_y); + size_t raw_priv_len = sizeof(raw_priv); + + uint32_t curve_type = TEE_ECC_CURVE_NIST_P256; + + TEE_Result tee_res = TEE_SUCCESS; + TEE_ObjectHandle key = TEE_HANDLE_NULL; + rfc5915_eckey ec = {0}; + + tee_res = TEE_AllocateTransientObject(TEE_TYPE_ECDSA_KEYPAIR, + KEY_SIZE_256 * 8, &key); + if (tee_res != TEE_SUCCESS) { + EMSG("TEE_AllocateTransientObject failed with result 0x%x", tee_res); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + TEE_Attribute attr; + TEE_InitValueAttribute(&attr, TEE_ATTR_ECC_CURVE, curve_type, 0); + + tee_res = TEE_GenerateKey(key, KEY_SIZE_256 * 8, &attr, 1); + if (tee_res != TEE_SUCCESS) { + EMSG("TEE_GenerateKey failed with result 0x%x", tee_res); + TEE_FreeTransientObject(key); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + tee_res = TEE_GetObjectBufferAttribute(key, TEE_ATTR_ECC_PUBLIC_VALUE_X, + raw_pub_x, &raw_pub_x_len); + if (tee_res != TEE_SUCCESS) { + EMSG("TEE_GetObjectBufferAttribute failed with result 0x%x", tee_res); + TEE_FreeTransientObject(key); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + tee_res = TEE_GetObjectBufferAttribute(key, TEE_ATTR_ECC_PUBLIC_VALUE_Y, + raw_pub_y, &raw_pub_y_len); + if (tee_res != TEE_SUCCESS) { + EMSG("TEE_GetObjectBufferAttribute failed with result 0x%x", tee_res); + TEE_FreeTransientObject(key); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + tee_res = TEE_GetObjectBufferAttribute(key, TEE_ATTR_ECC_PRIVATE_VALUE, + raw_priv, &raw_priv_len); + if (tee_res != TEE_SUCCESS) { + EMSG("TEE_GetObjectBufferAttribute failed with result 0x%x", tee_res); + TEE_FreeTransientObject(key); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + TEE_FreeTransientObject(key); + + ec.private_val = raw_priv; + ec.private_val_len = raw_priv_len; + ec.public_x = raw_pub_x; + ec.public_x_len = raw_pub_x_len; + ec.public_y = raw_pub_y; + ec.public_y_len = raw_pub_y_len; + ec.ecc_curve_type = curve_type; + ec.ecc_curve_bits = 256; + ec.max_signature_size = 0; // not needed for this operation + + OEMCryptoResult oemcrypto_res = + EncodeECCPublicKey(&ec, public_key_data, public_key_data_length); + if (oemcrypto_res != OEMCrypto_SUCCESS) { + return oemcrypto_res; + } + + oemcrypto_res = + EncodeECCPrivateKey(&ec, private_key_data, private_key_data_length); + if (oemcrypto_res != OEMCrypto_SUCCESS) { + return oemcrypto_res; + } + + return OEMCrypto_SUCCESS; +} + +OEMCryptoResult WTPI_GenerateRandomCertificateKeyPair( + AsymmetricKeyType* key_type, uint8_t* wrapped_private_key, + size_t* wrapped_private_key_length, uint8_t* public_key, + size_t* public_key_length) { + RETURN_INVALID_CONTEXT_IF_NULL(key_type); + RETURN_INVALID_CONTEXT_IF_NULL(wrapped_private_key_length); + RETURN_INVALID_CONTEXT_IF_NULL(public_key_length); + + // This implementation generates ECC key. An alternative is RSA key. + *key_type = DRM_ECC_PRIVATE_KEY; + // Check buffer sizes. + size_t required_wrapped_private_key_length = 0; + OEMCryptoResult result = WTPI_GetWrappedAsymmetricKeySize( + PKCS8_ECC_KEY_MAX_SIZE + AES_BLOCK_SIZE, *key_type, + &required_wrapped_private_key_length); + if (result != OEMCrypto_SUCCESS) return result; + const size_t required_public_key_length = PKCS8_ECC_KEY_MAX_SIZE; + if (wrapped_private_key == NULL || + *wrapped_private_key_length < required_wrapped_private_key_length || + public_key == NULL || *public_key_length < required_public_key_length) { + *wrapped_private_key_length = required_wrapped_private_key_length; + *public_key_length = required_public_key_length; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + uint8_t clear_private_key[PKCS8_ECC_KEY_MAX_SIZE + AES_BLOCK_SIZE]; + size_t clear_private_key_length = sizeof(clear_private_key); + result = NewEccKeyPair(clear_private_key, &clear_private_key_length, + public_key, public_key_length); + if (result != OEMCrypto_SUCCESS) { + return result; + } + + // Add padding. + const uint8_t padding = + AES_BLOCK_SIZE - (clear_private_key_length % AES_BLOCK_SIZE); + TEE_MemFill(clear_private_key + clear_private_key_length, padding, padding); + clear_private_key_length += padding; + + size_t actual_wrapped_private_key_length = 0; + result = WTPI_GetWrappedAsymmetricKeySize(clear_private_key_length, *key_type, + &actual_wrapped_private_key_length); + if (result != OEMCrypto_SUCCESS) return result; + if (*wrapped_private_key_length < actual_wrapped_private_key_length) { + // This should not happen as we have checked buffer size. + *wrapped_private_key_length = actual_wrapped_private_key_length; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + *wrapped_private_key_length = actual_wrapped_private_key_length; + return WTPI_WrapAsymmetricKey(wrapped_private_key, + *wrapped_private_key_length, *key_type, + clear_private_key, clear_private_key_length); +} diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/genkeypair_rsa.c b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/genkeypair_rsa.c new file mode 100644 index 0000000..5f2cf8f --- /dev/null +++ b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/genkeypair_rsa.c @@ -0,0 +1,136 @@ +/* + * Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary + * source code may only be used and distributed under the Widevine + * License Agreement. + */ + +#include "asymmetric_key.h" +#include "der_parse.h" +#include "oemcrypto_check_macros.h" +#include "opk_config.h" +#include "wtpi_provisioning_4_interface.h" + +OEMCryptoResult WTPI_GenerateRandomCertificateKeyPair( + AsymmetricKeyType* key_type, uint8_t* wrapped_private_key, + size_t* wrapped_private_key_length, uint8_t* public_key, + size_t* public_key_length) { + RETURN_INVALID_CONTEXT_IF_NULL(key_type); + RETURN_INVALID_CONTEXT_IF_NULL(wrapped_private_key_length); + RETURN_INVALID_CONTEXT_IF_NULL(public_key_length); + + // This implementation generates RSA key. An alternative is ECC key. + *key_type = DRM_RSA_PRIVATE_KEY; + + // temporary buffers to hold the raw RSA data generated by GlobalPlatform + uint8_t raw_modulus[KEY_SIZE_2048]; + size_t raw_modulus_len = sizeof(raw_modulus); + uint8_t raw_pub[KEY_SIZE_2048]; + size_t raw_pub_len = sizeof(raw_pub); + uint8_t raw_priv[KEY_SIZE_2048]; + size_t raw_priv_len = sizeof(raw_priv); + + TEE_Result tee_res = TEE_SUCCESS; + TEE_ObjectHandle key = TEE_HANDLE_NULL; + + pkcs1_rsa rsa_key = {0}; + + // Encode private key to temporary buffer before wrapping + uint8_t encoded_priv[MAX_WRAPPED_ASYMMETRIC_KEY_SIZE + AES_BLOCK_SIZE]; + size_t encoded_priv_len = sizeof(encoded_priv); + + size_t required_size = 0; + + // Check buffer sizes. + size_t required_wrapped_private_key_length = 0; + OEMCryptoResult result = WTPI_GetWrappedAsymmetricKeySize( + PKCS8_2048BIT_RSA_KEY_MAX_SIZE, *key_type, + &required_wrapped_private_key_length); + if (result != OEMCrypto_SUCCESS) return result; + + // Public key size plus DER encoding overhead + const size_t required_public_key_length = KEY_SIZE_2048 + 38; + if (wrapped_private_key == NULL || + *wrapped_private_key_length < required_wrapped_private_key_length || + public_key == NULL || *public_key_length < required_public_key_length) { + *wrapped_private_key_length = required_wrapped_private_key_length; + *public_key_length = required_public_key_length; + return OEMCrypto_ERROR_SHORT_BUFFER; + } + + tee_res = TEE_AllocateTransientObject(TEE_TYPE_RSA_KEYPAIR, KEY_SIZE_2048 * 8, + &key); + if (tee_res != TEE_SUCCESS) { + EMSG("TEE_AllocateTransientObject failed with result 0x%x", tee_res); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + tee_res = TEE_GenerateKey(key, KEY_SIZE_2048 * 8, NULL, 0); + if (tee_res != TEE_SUCCESS) { + EMSG("TEE_GenerateKey failed with result 0x%x", tee_res); + TEE_FreeTransientObject(key); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + tee_res = TEE_GetObjectBufferAttribute(key, TEE_ATTR_RSA_MODULUS, raw_modulus, + &raw_modulus_len); + if (tee_res != TEE_SUCCESS) { + EMSG("TEE_GetObjectBufferAttribute failed with result 0x%x", tee_res); + TEE_FreeTransientObject(key); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + tee_res = TEE_GetObjectBufferAttribute(key, TEE_ATTR_RSA_PUBLIC_EXPONENT, + raw_pub, &raw_pub_len); + if (tee_res != TEE_SUCCESS) { + EMSG("TEE_GetObjectBufferAttribute failed with result 0x%x", tee_res); + TEE_FreeTransientObject(key); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + tee_res = TEE_GetObjectBufferAttribute(key, TEE_ATTR_RSA_PRIVATE_EXPONENT, + raw_priv, &raw_priv_len); + if (tee_res != TEE_SUCCESS) { + EMSG("TEE_GetObjectBufferAttribute failed with result 0x%x", tee_res); + TEE_FreeTransientObject(key); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + TEE_FreeTransientObject(key); + + rsa_key.modulus = raw_modulus; + rsa_key.modulus_len = raw_modulus_len; + rsa_key.public_exp = raw_pub; + rsa_key.public_exp_len = raw_pub_len; + rsa_key.private_exp = raw_priv; + rsa_key.private_exp_len = raw_priv_len; + + // Encode public key directly to output parameters + result = EncodeRSAPublicKey(&rsa_key, public_key, public_key_length); + if (result != OEMCrypto_SUCCESS) { + EMSG("EncodeRSAPublicKey failed"); + return result; + } + + result = EncodeRSAPrivateKey(&rsa_key, encoded_priv, &encoded_priv_len); + if (result != OEMCrypto_SUCCESS) { + EMSG("EncodeRSAPrivateKey failed"); + return result; + } + + // If the encoded key length is not a multiple of the AES block size, pad + // until it is. This is required for the WrapAsymmetricKey step + const uint8_t padding = AES_BLOCK_SIZE - (encoded_priv_len % AES_BLOCK_SIZE); + TEE_MemFill(encoded_priv + encoded_priv_len, padding, padding); + encoded_priv_len += padding; + + result = WTPI_GetWrappedAsymmetricKeySize( + encoded_priv_len, DRM_RSA_PRIVATE_KEY, &required_size); + if (result != OEMCrypto_SUCCESS) { + EMSG("WTPI_GetWrappedAsymmetricKeySize failed with result %d", result); + return result; + } + *wrapped_private_key_length = required_size; + + return WTPI_WrapAsymmetricKey(wrapped_private_key, + *wrapped_private_key_length, *key_type, + encoded_priv, encoded_priv_len); +} diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/sources.mk b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/sources.mk index 356dd1b..2af3fc7 100644 --- a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/sources.mk +++ b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/sources.mk @@ -69,6 +69,14 @@ wtpi_impl_sources += \ $(tos_impl_dir)/optee_secure_buffers.c \ $(dice_sources) \ +ifeq ($(OEMCRYPTO_GEN_KEYPAIR_TYPE), ECC) +wtpi_impl_sources += $(wtpi_impl_dir)/genkeypair_ecc.c +else ifeq ($(OEMCRYPTO_GEN_KEYPAIR_TYPE), RSA) +wtpi_impl_sources += $(wtpi_impl_dir)/genkeypair_rsa.c +else +$(error OEMCRYPTO_GEN_KEYPAIR_TYPE make variable must be either ECC or RSA) +endif + wtpi_impl_includes += \ $(wtpi_impl_dir) \ $(wtpi_impl_dir)/util \ diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/util/der_parse.c b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/util/der_parse.c index c484c54..7af8c68 100644 --- a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/util/der_parse.c +++ b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/util/der_parse.c @@ -166,6 +166,7 @@ static OEMCryptoResult Helper_EncodeRSAKey( int result = mbedtls_pk_setup(&pk_ctx, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)); if (result != 0) { + mbedtls_pk_free(&pk_ctx); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } @@ -303,7 +304,7 @@ static size_t CurveNumBits(mbedtls_ecp_group_id id) { return 0; } -static size_t ECCSize(size_t num_bits) { +static size_t ECCSignatureSize(size_t num_bits) { // ECC signature size is the length of the ASN1-encoded SEQUENCE containing // two INTEGER elements, each large enough to contain |num_bits| of data. // These integers represent the curve point and proof portions of the @@ -379,7 +380,7 @@ OEMCryptoResult DecodePKCS8ECCPrivateKey(const uint8_t* input, output->ecc_curve_type = GlobalPlatformCurveId(ec_ctx->grp.id); output->ecc_curve_bits = CurveNumBits(ec_ctx->grp.id); - output->max_signature_size = ECCSize(output->ecc_curve_bits); + output->max_signature_size = ECCSignatureSize(output->ecc_curve_bits); const size_t ecc_curve_bytes = (output->ecc_curve_bits + 7) / 8; if ((res = extract_mbedtls_mpi_param(&(ec_ctx->d), &output->private_val, @@ -423,7 +424,7 @@ OEMCryptoResult DecodeECCPublicKey(const uint8_t* input, size_t input_length, output->ecc_curve_type = GlobalPlatformCurveId(ec_ctx->grp.id); output->ecc_curve_bits = CurveNumBits(ec_ctx->grp.id); - output->max_signature_size = ECCSize(output->ecc_curve_bits); + output->max_signature_size = ECCSignatureSize(output->ecc_curve_bits); const size_t ecc_curve_bytes = (output->ecc_curve_bits + 7) / 8; if ((res = extract_mbedtls_mpi_param(&(ec_ctx->Q.X), &output->public_x, @@ -557,7 +558,7 @@ OEMCryptoResult DeriveEccKey(const uint8_t* seed, size_t seed_length, // Copy results into output struct output->ecc_curve_type = curve_id; output->ecc_curve_bits = curve_len_bits; - output->max_signature_size = ECCSize(curve_len_bits); + output->max_signature_size = ECCSignatureSize(curve_len_bits); if ((res = extract_mbedtls_mpi_param(&privkey, &output->private_val, &output->private_val_len)) != @@ -584,3 +585,99 @@ cleanup: mbedtls_mpi_free(&privkey); return res; } + +static OEMCryptoResult Helper_EncodeECCKey( + rfc5915_eckey* key, uint8_t* output, size_t* output_length, + int (*mbedtls_write_fn)(mbedtls_pk_context* ctx, unsigned char* buf, + size_t size)) { + mbedtls_pk_context pk_ctx; + mbedtls_pk_init(&pk_ctx); + int mbedtls_res = + mbedtls_pk_setup(&pk_ctx, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)); + if (mbedtls_res != 0) { + mbedtls_pk_free(&pk_ctx); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + mbedtls_ecp_keypair_init(mbedtls_pk_ec(pk_ctx)); + + mbedtls_res = mbedtls_ecp_group_load(&(mbedtls_pk_ec(pk_ctx)->grp), + MbedTlsCurveId(key->ecc_curve_type)); + if (mbedtls_res < 0) { + mbedtls_pk_free(&pk_ctx); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + // Priv key + mbedtls_res = mbedtls_mpi_read_binary(&(mbedtls_pk_ec(pk_ctx)->d), + key->private_val, key->private_val_len); + if (mbedtls_res < 0) { + mbedtls_pk_free(&pk_ctx); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + // Public x, y, z. Set Z to const 1. + mbedtls_res = mbedtls_mpi_read_binary(&(mbedtls_pk_ec(pk_ctx)->Q.X), + key->public_x, key->public_x_len); + if (mbedtls_res < 0) { + mbedtls_pk_free(&pk_ctx); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + mbedtls_res = mbedtls_mpi_read_binary(&(mbedtls_pk_ec(pk_ctx)->Q.Y), + key->public_y, key->public_y_len); + if (mbedtls_res < 0) { + mbedtls_pk_free(&pk_ctx); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + mbedtls_res = mbedtls_mpi_lset(&(mbedtls_pk_ec(pk_ctx)->Q.Z), 1); + if (mbedtls_res < 0) { + mbedtls_pk_free(&pk_ctx); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + // write EC data in DER encoding to output + size_t original_output_length = *output_length; + int bytes_written = mbedtls_write_fn(&pk_ctx, output, *output_length); + if (bytes_written <= 0) { + mbedtls_pk_free(&pk_ctx); + EMSG("mbedtls write function returned %x", bytes_written); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + *output_length = bytes_written; + + // mbedtls ASN1 write functions write backwards from the end of the buffer. + // Re-align memory to the beginning of the buffer. + TEE_MemMove(output, output + original_output_length - *output_length, + *output_length); + + mbedtls_pk_free(&pk_ctx); + return OEMCrypto_SUCCESS; +} + +// Encodes ECC public key as X509 SubjectPublicKeyInfo +OEMCryptoResult EncodeECCPublicKey(rfc5915_eckey* key, uint8_t* output, + size_t* output_length) { + if (key == NULL || key->private_val == NULL || key->public_x == NULL || + key->public_y == NULL || key->private_val_len == 0 || + key->public_x_len == 0 || key->public_y_len == 0 || output == NULL || + output_length == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + return Helper_EncodeECCKey(key, output, output_length, + mbedtls_pk_write_pubkey_der); +} + +OEMCryptoResult EncodeECCPrivateKey(rfc5915_eckey* key, uint8_t* output, + size_t* output_length) { + if (key == NULL || key->private_val == NULL || key->public_x == NULL || + key->public_y == NULL || key->private_val_len == 0 || + key->public_x_len == 0 || key->public_y_len == 0 || output == NULL || + output_length == NULL) { + return OEMCrypto_ERROR_INVALID_CONTEXT; + } + + return Helper_EncodeECCKey(key, output, output_length, mbedtls_pk_write_key_der); +} diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/util/der_parse.h b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/util/der_parse.h index 0f42300..0f7339f 100644 --- a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/util/der_parse.h +++ b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/util/der_parse.h @@ -125,4 +125,24 @@ OEMCryptoResult EncodeECDSASignature(const uint8_t* sig, size_t sig_length, OEMCryptoResult DeriveEccKey(const uint8_t* seed, size_t seed_length, uint32_t curve_id, rfc5915_eckey* output); + +/* + * Encodes the EC key data as a SubjectPublicKeyData struct. The input data must + * have the public X,Y keys and private key set, as well as the curve type. + * + * Sets |output_length| to the number of bytes written. + */ +OEMCryptoResult EncodeECCPublicKey(rfc5915_eckey* key, uint8_t* output, + size_t* output_length); + +/* + * Encodes the EC key data as a PKCS8 PrivateKey structure in |output|. The + * input data must have the public X and Y keys, private key, and curve type + * values set. + * + * Sets |output_length| to number of bytes written. + */ +OEMCryptoResult EncodeECCPrivateKey(rfc5915_eckey* key, uint8_t* output, + size_t* output_length); + #endif diff --git a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_provisioning_4.c b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_provisioning_4.c index 06fc831..ebc3ff9 100644 --- a/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_provisioning_4.c +++ b/oemcrypto/opk/ports/optee/ta/common/wtpi_impl/wtpi_provisioning_4.c @@ -16,138 +16,6 @@ #define BCC_PAYLOAD_MAX_SIZE 2048 #define COSE_SIGN1_MAX_SIZE 2048 -OEMCryptoResult WTPI_GenerateRandomCertificateKeyPair( - AsymmetricKeyType* key_type, uint8_t* wrapped_private_key, - size_t* wrapped_private_key_length, uint8_t* public_key, - size_t* public_key_length) { - RETURN_INVALID_CONTEXT_IF_NULL(key_type); - RETURN_INVALID_CONTEXT_IF_NULL(wrapped_private_key_length); - RETURN_INVALID_CONTEXT_IF_NULL(public_key_length); - - // This implementation generates RSA key. An alternative is ECC key. - *key_type = DRM_RSA_PRIVATE_KEY; - - // temporary buffers to hold the raw RSA data generated by GlobalPlatform - uint8_t raw_modulus[KEY_SIZE_2048]; - size_t raw_modulus_len = sizeof(raw_modulus); - uint8_t raw_pub[KEY_SIZE_2048]; - size_t raw_pub_len = sizeof(raw_pub); - uint8_t raw_priv[KEY_SIZE_2048]; - size_t raw_priv_len = sizeof(raw_priv); - - TEE_Result tee_res = TEE_SUCCESS; - TEE_ObjectHandle key = TEE_HANDLE_NULL; - - pkcs1_rsa rsa_key = {0}; - - // Encode private key to temporary buffer before wrapping - uint8_t encoded_priv[MAX_WRAPPED_ASYMMETRIC_KEY_SIZE]; - size_t encoded_priv_len = MAX_WRAPPED_ASYMMETRIC_KEY_SIZE; - - size_t required_size = 0; - - // Check buffer sizes. - size_t required_wrapped_private_key_length = 0; - OEMCryptoResult result = WTPI_GetWrappedAsymmetricKeySize( - PKCS8_2048BIT_RSA_KEY_MAX_SIZE, *key_type, - &required_wrapped_private_key_length); - if (result != OEMCrypto_SUCCESS) return result; - - // Public key size plus DER encoding overhead - const size_t required_public_key_length = KEY_SIZE_2048 + 38; - if (wrapped_private_key == NULL || - *wrapped_private_key_length < required_wrapped_private_key_length || - public_key == NULL || *public_key_length < required_public_key_length) { - *wrapped_private_key_length = required_wrapped_private_key_length; - *public_key_length = required_public_key_length; - return OEMCrypto_ERROR_SHORT_BUFFER; - } - - tee_res = TEE_AllocateTransientObject(TEE_TYPE_RSA_KEYPAIR, KEY_SIZE_2048 * 8, - &key); - if (tee_res != TEE_SUCCESS) { - EMSG("TEE_AllocateTransientObject failed with result 0x%x", tee_res); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; - } - - tee_res = TEE_GenerateKey(key, KEY_SIZE_2048 * 8, NULL, 0); - if (tee_res != TEE_SUCCESS) { - EMSG("TEE_GenerateKey failed with result 0x%x", tee_res); - goto cleanup; - } - - tee_res = TEE_GetObjectBufferAttribute(key, TEE_ATTR_RSA_MODULUS, raw_modulus, - &raw_modulus_len); - if (tee_res != TEE_SUCCESS) { - EMSG("TEE_GetObjectBufferAttribute failed with result 0x%x", tee_res); - goto cleanup; - } - - tee_res = TEE_GetObjectBufferAttribute(key, TEE_ATTR_RSA_PUBLIC_EXPONENT, - raw_pub, &raw_pub_len); - if (tee_res != TEE_SUCCESS) { - EMSG("TEE_GetObjectBufferAttribute failed with result 0x%x", tee_res); - goto cleanup; - } - - tee_res = TEE_GetObjectBufferAttribute(key, TEE_ATTR_RSA_PRIVATE_EXPONENT, - raw_priv, &raw_priv_len); - if (tee_res != TEE_SUCCESS) { - EMSG("TEE_GetObjectBufferAttribute failed with result 0x%x", tee_res); - goto cleanup; - } - - rsa_key.modulus = raw_modulus; - rsa_key.modulus_len = raw_modulus_len; - rsa_key.public_exp = raw_pub; - rsa_key.public_exp_len = raw_pub_len; - rsa_key.private_exp = raw_priv; - rsa_key.private_exp_len = raw_priv_len; - - // Encode public key directly to output parameters - result = EncodeRSAPublicKey(&rsa_key, public_key, public_key_length); - if (result != OEMCrypto_SUCCESS) { - EMSG("EncodeRSAPublicKey failed"); - goto cleanup; - } - - result = EncodeRSAPrivateKey(&rsa_key, encoded_priv, &encoded_priv_len); - if (result != OEMCrypto_SUCCESS) { - EMSG("EncodeRSAPrivateKey failed"); - goto cleanup; - } - - // If the encoded key length is not a multiple of the AES block size, pad - // until it is. This is required for the WrapAsymmetricKey step - while (encoded_priv_len % AES_BLOCK_SIZE != 0 && - encoded_priv_len < MAX_WRAPPED_ASYMMETRIC_KEY_SIZE) { - encoded_priv[encoded_priv_len++] = 0; - } - - result = WTPI_GetWrappedAsymmetricKeySize( - encoded_priv_len, DRM_RSA_PRIVATE_KEY, &required_size); - if (result != OEMCrypto_SUCCESS) { - EMSG("WTPI_GetWrappedAsymmetricKeySize failed with result %d", result); - goto cleanup; - } - *wrapped_private_key_length = required_size; - - result = - WTPI_WrapAsymmetricKey(wrapped_private_key, *wrapped_private_key_length, - *key_type, encoded_priv, encoded_priv_len); - if (result != OEMCrypto_SUCCESS) { - EMSG("WTPI_WrapAsymmetricKey failed with result %d", result); - goto cleanup; - } - - TEE_FreeTransientObject(key); - return OEMCrypto_SUCCESS; - -cleanup: - TEE_FreeTransientObject(key); - return OEMCrypto_ERROR_UNKNOWN_FAILURE; -} - // Generate device-unique asymmetric key handle that is consistent across // reboots. ECDSA static OEMCryptoResult Helper_GetDeviceAsymmetricKeyIntoHandle(