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:
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
150
oemcrypto/opk/ports/optee/ta/common/wtpi_impl/genkeypair_ecc.c
Normal file
150
oemcrypto/opk/ports/optee/ta/common/wtpi_impl/genkeypair_ecc.c
Normal 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);
|
||||
}
|
||||
136
oemcrypto/opk/ports/optee/ta/common/wtpi_impl/genkeypair_rsa.c
Normal file
136
oemcrypto/opk/ports/optee/ta/common/wtpi_impl/genkeypair_rsa.c
Normal 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);
|
||||
}
|
||||
@@ -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 \
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user