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.
This commit is contained in:
Matt Feddersen
2024-01-11 16:21:56 -08:00
parent 6cd8677952
commit 8161b155b3
8 changed files with 440 additions and 136 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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 \

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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(