blob: 425022fbbdab0e3bb9d2c5db23c20e3649c0fb84 [file] [log] [blame]
/**
* @file ax_sssEngine_ecc.c
* @author NXP Semiconductors
* @version 1.0
* @par License
*
* Copyright 2018-2020 NXP
* SPDX-License-Identifier: Apache-2.0
*
* @par Description
* OpenSSL Engine for NXP Embedded Secure Element over SSS API's
*
* The following operations are supported by this engine:
* - Random number generation
* - ECC sign
* - ECC verify
* - ECDH compute_key
*
* When dealing with an EC key argument whose a public key is used:
* - In case the key is a 'reference key' -> use the referenced public key
* - In case the above does not apply; at compile time one can choose between two
* strategies:
* (1) return a fail
* (2) delegate the operation to the OpenSSL SW implementation
*
* When dealing with an EC key argument whose private key is used:
* - In case the key is a 'reference key' -> use the referenced private key
* - In case the above does not apply; at compile time one can choose between two
* strategies:
* (1) return a fail
* (2) delegate the operation to the OpenSSL SW implementation
*
* @note
* Compatible with:
* - OpenSSL 1.0.2
* - OpenSSL 1.1.0
*
*/
/*
* This file contains source code form OpenSSL distribution that is covered
* by the LICENSE-OpenSSL file to be found in the root of this source code
* distribution tree.
*/
#if defined(SSS_USE_FTR_FILE)
#include "fsl_sss_ftr.h"
#else
#include "fsl_sss_ftr_default.h"
#endif
#if SSS_HAVE_ECC
#include <ex_sss.h>
#include <stdlib.h>
//#include <malloc.h>
#include <fsl_sss_util_asn1_der.h>
#include <openssl/bn.h>
#include <openssl/crypto.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include "ax_api.h"
#include "ax_cryptoIpc.h"
#include "ax_embSeEngine.h"
#include "ax_embSeEngine_Internal.h"
#include "sm_printf.h"
#define ECDH_MAX_LEN 32
#define EMBSE_MAX_ECC_PUBKEY_BUF (2 * 96 + 1) // Corresponds to 768 bit ECC key
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
#else
EC_KEY_METHOD *EmbSe_EC = NULL;
const EC_KEY_METHOD *EmbSe_EC_Default = NULL;
#endif
/* ecdsa_method struct definition from */
struct ecdsa_method
{
const char *name;
ECDSA_SIG *(*ecdsa_do_sign)(
const unsigned char *dgst, int dgst_len, const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey);
int (*ecdsa_sign_setup)(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv, BIGNUM **r);
int (*ecdsa_do_verify)(const unsigned char *dgst, int dgst_len, const ECDSA_SIG *sig, EC_KEY *eckey);
#if 0
int (*init)(EC_KEY *eckey);
int (*finish)(EC_KEY *eckey);
#endif
int flags;
char *app_data;
};
/* ecdh_method struct definition from ech_locl.h*/
struct ecdh_method
{
const char *name;
int (*compute_key)(void *key,
size_t outlen,
const EC_POINT *pub_key,
EC_KEY *ecdh,
void *(*KDF)(const void *in, size_t inlen, void *out, size_t *outlen));
#if 0
int (*init)(EC_KEY *eckey);
int (*finish)(EC_KEY *eckey);
#endif
int flags;
char *app_data;
};
/**
* Either zero sign extend \p pIn so it becomes \p expectedLen byte long
* or truncate the right most byte.
* The caller must ensure \p expectedLen is bigger than \p actualLen
* @param[in,out] pOut
* @param[in] expectedLen Zero sign extend/truncate until this length.
* @param[in] pIn Array representation of big number, to be zero sign extended or truncated
* @param[in] actualLen Length of incoming array \p pIn
*
* @retval SW_OK In case of successfull execution
* @retval ERR_API_ERROR Requested adjustment would result in truncation
*/
static U16 axAdaptSize(U8 *pOut, U16 expectedLen, const U8 *pIn, U16 actualLen)
{
U16 sw = SW_OK;
int numExtraByte = (int)expectedLen - (int)actualLen;
if (numExtraByte == 0) {
memcpy(pOut, pIn, actualLen);
}
else if (numExtraByte < 0) {
memcpy(pOut, pIn, expectedLen);
}
else {
memcpy(pOut + numExtraByte, pIn, actualLen);
memset(pOut, 0x00, numExtraByte);
}
return sw;
}
/**
Return SW_OK when the ecKey passed as argument is a reference key.
Upon successfull execution keyId is retrieved from the reference key.
@return ERR_PATTERN_COMPARE_FAILED Not a reference key
@return ERR_IDENT_IDX_RANGE Not a valid keyId (for future expansion)
@return ERR_NO_PRIVATE_KEY No private key present
*/
static U16 getEcKeyReference(const EC_KEY *eckey, uint32_t *keyId)
{
U16 sw = ERR_PATTERN_COMPARE_FAILED;
const BIGNUM *prv_key_bn;
U8 tmpBuf[EMBSE_MAX_ECC_PUBKEY_BUF];
U16 privKeylen = 0;
U8 Ident = 0;
U8 Index = 0;
U32 Coeff[2] = {0, 0};
int i = 0;
int j = 0;
*keyId = 0;
/* Test for private key */
prv_key_bn = EC_KEY_get0_private_key(eckey);
if (prv_key_bn) {
privKeylen = BN_bn2bin(prv_key_bn, tmpBuf);
/* Get Ident and Index, not used */
Ident = tmpBuf[privKeylen - 2];
Index = tmpBuf[privKeylen - 1];
/* Get double ID string */
for (j = 0; j < 2; j++) {
for (i = 3; i < 7; i++) {
Coeff[j] |= tmpBuf[privKeylen - i - (j * 4)] << 8 * (i - 3);
}
}
if (((unsigned int)Coeff[0] == (unsigned int)EMBSE_REFKEY_ID) &&
((unsigned int)Coeff[1] == (unsigned int)EMBSE_REFKEY_ID)) {
j = 2;
for (i = 3; i < 7; i++) {
*keyId |= tmpBuf[privKeylen - i - (j * 4)] << 8 * (i - 3);
}
sw = SW_OK;
EmbSe_Print(LOG_DBG_ON, "Using keyId=0x%08X\n", *keyId);
}
else {
sw = ERR_PATTERN_COMPARE_FAILED;
}
}
else {
sw = ERR_NO_PRIVATE_KEY;
}
return sw;
}
static U16 getMatchingShaAlgo(U16 dgstLen, sss_algorithm_t *shaAlgo)
{
switch (dgstLen) {
case 20:
*shaAlgo = kAlgorithm_SSS_SHA1;
break;
case 28:
*shaAlgo = kAlgorithm_SSS_SHA224;
break;
case 32:
*shaAlgo = kAlgorithm_SSS_SHA256;
break;
case 48:
*shaAlgo = kAlgorithm_SSS_SHA384;
break;
case 64:
*shaAlgo = kAlgorithm_SSS_SHA512;
break;
default:
EmbSe_Print(LOG_ERR_ON, "Cannot handle matching digest size %d\n", dgstLen);
return ERR_PATTERN_COMPARE_FAILED;
}
EmbSe_Print(LOG_DBG_ON, "shaAlgo: %d\n", *shaAlgo);
return SW_OK;
}
/**
* The RAW public key value must be encapsulated in a proper ASN.1 structure.
* This is equivalent to concatenating a fixed header (per keytype) with the raw public data
* We call this process 'decorating'.
* \param[in] nid OpenSSL specific value
* \param[in,out] decoratedKey IN: Buffer provided by caller; OUT: Concatenation of ASN.1 header and raw public key
* \param[in,out] decoratedKeyLen IN: Length of buffer provided by caller; OUT: Size of decoratedKey
* \param[in] keyValue Raw public key The length of the info data passed as argument
* \param[in] keyValueLen Length of raw public key
* \retval ::SW_OK Successfull execution
*/
static U16 decoratePublicKey(int nid, U8 *decoratedKey, U16 *decoratedKeyLen, const U8 *keyValue, U16 keyValueLen)
{
const U8 *decoration = NULL;
size_t decorationLen = 0;
switch (nid) {
case NID_X9_62_prime192v1:
decoration = gecc_der_header_nist192;
decorationLen = der_ecc_nistp192_header_len;
// memcpy(decoratedKey, gecc_der_header_nist192, der_ecc_nistp192_header_len);
break;
case NID_secp224r1:
decoration = gecc_der_header_nist224;
decorationLen = der_ecc_nistp224_header_len;
break;
case NID_X9_62_prime256v1:
decoration = gecc_der_header_nist256;
decorationLen = der_ecc_nistp256_header_len;
break;
case NID_secp384r1:
decoration = gecc_der_header_nist384;
decorationLen = der_ecc_nistp384_header_len;
break;
case NID_secp521r1:
decoration = gecc_der_header_nist521;
decorationLen = der_ecc_nistp521_header_len;
break;
case NID_brainpoolP160r1:
decoration = gecc_der_header_bp160;
decorationLen = der_ecc_bp160_header_len;
break;
case NID_brainpoolP192r1:
decoration = gecc_der_header_bp192;
decorationLen = der_ecc_bp192_header_len;
break;
case NID_brainpoolP224r1:
decoration = gecc_der_header_bp224;
decorationLen = der_ecc_bp224_header_len;
break;
case NID_brainpoolP256r1:
decoration = gecc_der_header_bp256;
decorationLen = der_ecc_bp256_header_len;
break;
case NID_brainpoolP320r1:
decoration = gecc_der_header_bp320;
decorationLen = der_ecc_bp320_header_len;
break;
case NID_brainpoolP384r1:
decoration = gecc_der_header_bp384;
decorationLen = der_ecc_bp384_header_len;
break;
case NID_brainpoolP512r1:
decoration = gecc_der_header_bp512;
decorationLen = der_ecc_bp512_header_len;
break;
case NID_secp160k1:
decoration = gecc_der_header_160k;
decorationLen = der_ecc_160k_header_len;
break;
case NID_secp192k1:
decoration = gecc_der_header_192k;
decorationLen = der_ecc_192k_header_len;
break;
case NID_secp224k1:
decoration = gecc_der_header_224k;
decorationLen = der_ecc_224k_header_len;
break;
case NID_secp256k1:
decoration = gecc_der_header_256k;
decorationLen = der_ecc_256k_header_len;
break;
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
#else
case NID_X448:
decoration = gecc_der_header_mont_dh_448;
decorationLen = der_ecc_mont_dh_448_header_len;
break;
#endif
default:
return ERR_PATTERN_COMPARE_FAILED;
}
if ((decorationLen + keyValueLen) > *decoratedKeyLen) {
// Avoid bufferoverlow
return ERR_BUF_TOO_SMALL;
}
memcpy(decoratedKey, decoration, decorationLen);
memcpy(decoratedKey + decorationLen, keyValue, keyValueLen);
*decoratedKeyLen = (U16)(decorationLen + keyValueLen);
return SW_OK;
}
static U16 getCipherTypeFromNid(int nid, uint32_t *cipherType)
{
U16 status = SW_OK;
switch (nid) {
case NID_X9_62_prime192v1:
case NID_secp224r1:
case NID_X9_62_prime256v1:
case NID_secp384r1:
case NID_secp521r1:
*cipherType = kSSS_CipherType_EC_NIST_P;
break;
case NID_brainpoolP192r1:
case NID_brainpoolP224r1:
case NID_brainpoolP320r1:
case NID_brainpoolP384r1:
case NID_brainpoolP160r1:
case NID_brainpoolP256r1:
case NID_brainpoolP512r1:
*cipherType = kSSS_CipherType_EC_BRAINPOOL;
break;
case NID_secp160k1:
case NID_secp192k1:
case NID_secp224k1:
case NID_secp256k1:
*cipherType = kSSS_CipherType_EC_NIST_K;
break;
default:
EmbSe_Print(LOG_DBG_ON, "nid %d not supported\n", nid);
status = ERR_PATTERN_COMPARE_FAILED;
break;
}
return status;
}
// EmbSE ECDSA Implementation
// --------------------------
static ECDSA_SIG *EmbSe_ECDSA_Do_Sign(
const unsigned char *dgst, int dgst_len, const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey)
{
U8 sigDER[256];
size_t sigDERLen = sizeof(sigDER);
U16 sw;
sss_status_t status;
sss_object_t keyPair;
sss_asymmetric_t asymm;
ECDSA_SIG *pSig;
EC_KEY *dup_eckey = NULL;
U8 *pp;
uint32_t keyId;
U8 dgstBuf[96];
U16 dgstBufLen = sizeof(dgstBuf);
sw = getEcKeyReference(eckey, &keyId);
if (sw == SW_OK) {
int dgstLenMatchingKey = 0;
sss_algorithm_t shaAlgo_matchOnSize = kAlgorithm_SSS_SHA256;
// Only adapt hash-size to key length for A71CH
#if SSS_HAVE_A71CH || SSS_HAVE_A71CH_SIM
if (dgst_len > dgstBufLen) {
EmbSe_Print(LOG_ERR_ON, "Buffer allocated for digest too small.\n");
return NULL;
}
dgstLenMatchingKey = 32;
#else
dgstLenMatchingKey = dgst_len;
if (dgstLenMatchingKey > dgstBufLen) {
EmbSe_Print(LOG_ERR_ON, "Buffer allocated for digest too small.\n");
return NULL;
}
#endif
sw = getMatchingShaAlgo(dgstLenMatchingKey, &shaAlgo_matchOnSize);
if (sw != SW_OK) {
return NULL;
}
axAdaptSize(dgstBuf, dgstLenMatchingKey, dgst, dgst_len);
axCi_MutexLock();
EmbSe_Print(LOG_FLOW_ON, "SSS based sign (keyId=0x%08X, dgstLen=%d)\n", keyId, dgst_len);
status = sss_key_object_init(&keyPair, &gpCtx->ks);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "sss_key_object_init for keyPair failed\n");
return NULL;
}
status = sss_key_object_get_handle(&keyPair, keyId);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "sss_key_object_get_handle for keyPair failed\n");
sss_key_object_free(&keyPair);
return NULL;
}
status = sss_asymmetric_context_init(&asymm, &gpCtx->session, &keyPair, shaAlgo_matchOnSize, kMode_SSS_Sign);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "sss_asymmetric_context_init for sign failed\n");
sss_key_object_free(&keyPair);
return NULL;
}
/* Do Signing */
status = sss_asymmetric_sign_digest(&asymm, dgstBuf, dgstLenMatchingKey, sigDER, &sigDERLen);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "sss_asymmetric_sign_digest failed\n");
sss_asymmetric_context_free(&asymm);
sss_key_object_free(&keyPair);
return NULL;
}
axCi_MutexUnlock();
EmbSe_Print(LOG_FLOW_ON, "SSS based sign called successfully (sigDERLen=%d)\n", sigDERLen);
/* sig is DER encoded. Transform to ECDSA_SIG and return this */
pp = (U8 *)sigDER;
pSig = ECDSA_SIG_new();
if (pSig == NULL) {
EmbSe_Print(LOG_ERR_ON, "ECDSA_SIG_new call failed\n");
sss_asymmetric_context_free(&asymm);
sss_key_object_free(&keyPair);
return NULL;
}
if (d2i_ECDSA_SIG((ECDSA_SIG **)&pSig, (const unsigned char **)&pp, (long)sigDERLen) == NULL) {
EmbSe_Print(LOG_ERR_ON, "d2i_ECDSA_SIG failed\n");
sss_asymmetric_context_free(&asymm);
sss_key_object_free(&keyPair);
return NULL;
}
// EmbSe_Print(LOG_FLOW_ON, "Clean up SSS data structures.\n");
sss_asymmetric_context_free(&asymm);
sss_key_object_free(&keyPair);
EmbSe_Print(LOG_FLOW_ON, "EmbSe_ECDSA_Do_Sign success.\n");
return pSig;
}
else if (sw == ERR_NO_PRIVATE_KEY) {
EmbSe_Print(LOG_ERR_ON, "Expecting private key (by value or reference): 0x%04X.\n", sw);
return NULL;
}
else if (sw == ERR_PATTERN_COMPARE_FAILED) {
#ifdef PRIVATE_KEY_HANDOVER_TO_SW
// Invoke OpenSSL sign API if no valid key reference is detected
EmbSe_Print(LOG_FLOW_ON, "No matching key in Secure Element. Invoking OpenSSL API: ECDSA_do_sign_ex.\n");
/* Create a duplicate key */
dup_eckey = EC_KEY_dup(eckey);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
/* Attach OpenSSL's SW method to duplicate key */
if (!ECDSA_set_method(dup_eckey, ECDSA_OpenSSL())) {
EmbSe_Print(LOG_ERR_ON, "OpenSSL ECDSA_set_method failure..\n");
return NULL;
}
#else
/* Attach OpenSSL's SW method to duplicate key */
if (!EC_KEY_set_method(dup_eckey, EC_KEY_OpenSSL())) {
EmbSe_Print(LOG_ERR_ON, "OpenSSL EC_KEY_set_method failure..\n");
return NULL;
}
#endif
/* Invoke OpenSSL's sign API and return result */
return ECDSA_do_sign_ex(dgst, dgst_len, inv, rp, dup_eckey);
#else
EmbSe_Print(LOG_ERR_ON, "EmbSe_ECDSA_Do_Sign expected a reference key: 0x%04X.\n", sw);
return NULL;
#endif
}
else {
EmbSe_Print(LOG_ERR_ON, "EmbSe_ECDSA_Do_Sign unexpected key type: 0x%04X.\n", sw);
return NULL;
}
}
static int EmbSe_ECDSA_Sign_Setup(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv, BIGNUM **r)
{
return 1;
}
static int EmbSe_ECDSA_Do_Verify(const unsigned char *dgst, int dgst_len, const ECDSA_SIG *sig, EC_KEY *eckey)
{
U16 sw;
sss_status_t status;
sss_object_t pubKey;
sss_asymmetric_t asymm;
int nRet = 0;
int flagHandleKey = AX_ENGINE_INVOKE_NOTHING;
EC_KEY *dup_eckey = NULL;
U8 *pSignatureDER, *pSigTmp;
U16 sigLen;
uint32_t keyId;
EmbSe_Print(LOG_FLOW_ON, "Invoking EmbSe_ECDSA_Do_Verify(..)\n");
if (!eckey) {
EmbSe_Print(LOG_ERR_ON, "EmbSe_ECDSA_Do_Verify: No EC Key provided as input.\n");
return -1;
}
if (!sig) {
EmbSe_Print(LOG_ERR_ON, "EmbSe_ECDSA_Do_Verify: No signature provided as input.\n");
return -1;
}
/* Convert ECDSA_SIG to DER and print */
sigLen = i2d_ECDSA_SIG((ECDSA_SIG *)sig, NULL);
if (sigLen != 0) {
pSignatureDER = (U8 *)OPENSSL_malloc(sigLen);
pSigTmp = pSignatureDER;
// The pointer passed as second argument will point past the end of the returned signature
// upon return. Which explains pointer copy operation before the call.
i2d_ECDSA_SIG((ECDSA_SIG *)sig, &pSigTmp);
}
else {
EmbSe_Print(LOG_ERR_ON, "Call to i2d_ECDSA_SIG failed\n");
return -1;
}
EmbSe_Print(LOG_DBG_ON, "====>SIGNATURE (len=%d)\n", sigLen);
EmbSe_PrintPayload(LOG_DBG_ON, pSignatureDER, sigLen, "");
EmbSe_PrintPayload(LOG_DBG_ON, dgst, dgst_len, "====>DIGEST");
sw = getEcKeyReference(eckey, &keyId);
if (sw == SW_OK) {
flagHandleKey = AX_ENGINE_INVOKE_SE;
}
else if ((sw == ERR_NO_PRIVATE_KEY) || (sw == ERR_PATTERN_COMPARE_FAILED)) {
flagHandleKey = AX_ENGINE_INVOKE_OPENSSL_SW;
}
else {
EmbSe_Print(LOG_ERR_ON, "EmbSe_ECDSA_Do_Verify: No matching/valid public key\n");
nRet = -1;
goto clean_mem_up;
}
if (flagHandleKey == AX_ENGINE_INVOKE_SE) {
U8 dgstBuf[96];
U16 dgstBufLen = sizeof(dgstBuf);
int dgstLenMatchingKey = 0;
sss_algorithm_t shaAlgo_matchOnSize = kAlgorithm_SSS_SHA256;
// Only adapt hash-size to key length for A71CH
#if SSS_HAVE_A71CH || SSS_HAVE_A71CH_SIM
if (dgst_len > dgstBufLen) {
EmbSe_Print(LOG_ERR_ON, "Buffer allocated for digest too small.\n");
nRet = -1;
goto clean_mem_up;
}
dgstLenMatchingKey = 32;
#else
dgstLenMatchingKey = dgst_len;
if (dgstLenMatchingKey > dgstBufLen) {
EmbSe_Print(LOG_ERR_ON, "Buffer allocated for digest too small.\n");
nRet = -1;
goto clean_mem_up;
}
#endif
sw = getMatchingShaAlgo(dgstLenMatchingKey, &shaAlgo_matchOnSize);
if (sw != SW_OK) {
nRet = -1;
goto clean_mem_up;
}
axAdaptSize(dgstBuf, dgstLenMatchingKey, dgst, dgst_len);
axCi_MutexLock();
EmbSe_Print(LOG_FLOW_ON, "SSS based verify (keyId=0x%08X, dgst_len=%d, sigLen=%d)\n", keyId, dgst_len, sigLen);
status = sss_key_object_init(&pubKey, &gpCtx->ks);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "sss_key_object_init for pubKey failed\n");
nRet = -1;
goto clean_mem_up;
}
status = sss_key_object_get_handle(&pubKey, keyId);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "sss_key_object_get_handle for pubKey failed\n");
nRet = -1;
sss_key_object_free(&pubKey);
goto clean_mem_up;
}
status = sss_asymmetric_context_init(&asymm, &gpCtx->session, &pubKey, shaAlgo_matchOnSize, kMode_SSS_Verify);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "sss_asymmetric_context_init for sign failed\n");
nRet = -1;
sss_key_object_free(&pubKey);
goto clean_mem_up;
}
/* Do Signing */
status = sss_asymmetric_verify_digest(&asymm, dgstBuf, dgstLenMatchingKey, pSignatureDER, sigLen);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "sss_asymmetric_verify_digest failed\n");
nRet = -1;
sss_asymmetric_context_free(&asymm);
sss_key_object_free(&pubKey);
goto clean_mem_up;
}
axCi_MutexUnlock();
// At this point we know the verification succeeded, use OpenSSL convention for a valid signature
// NOTE: an invalid signature would map on '0' as return value, but sss_asymmetric_verify_digest does not
// distinguish between an error and a wrong signature.
nRet = 1;
// EmbSe_Print(LOG_FLOW_ON, "Clean up SSS data structures.\n");
sss_asymmetric_context_free(&asymm);
sss_key_object_free(&pubKey);
EmbSe_Print(LOG_FLOW_ON, "Verification PASS\n");
}
else {
#ifdef PUBLIC_KEY_HANDOVER_TO_SW
EmbSe_Print(LOG_FLOW_ON, "No matching key in Secure Element. Invoking OpenSSL API: ECDSA_do_verify.\n");
/* Create a duplicate key */
dup_eckey = EC_KEY_dup(eckey);
if (dup_eckey == NULL) {
EmbSe_Print(LOG_ERR_ON, "OpenSSL verify: Failed to duplicate key.\n");
nRet = -1;
goto clean_mem_up;
}
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
/* Attach OpenSSL's SW methods to duplicate key */
if (!ECDSA_set_method(dup_eckey, ECDSA_OpenSSL())) {
EmbSe_Print(LOG_ERR_ON, "OpenSSL ECDSA_set_method failure..\n");
nRet = -1;
EC_KEY_free(dup_eckey);
goto clean_mem_up;
}
#else
/* Attach OpenSSL's SW methods to duplicate key */
if (!EC_KEY_set_method(dup_eckey, EC_KEY_OpenSSL())) {
EmbSe_Print(LOG_ERR_ON, "OpenSSL EC_KEY_set_method failure..\n");
nRet = -1;
EC_KEY_free(dup_eckey);
goto clean_mem_up;
}
#endif // (OPENSSL_VERSION_NUMBER < 0x10100000L)
/* Invoke OpenSSL verify and return result */
nRet = ECDSA_do_verify(dgst, dgst_len, sig, dup_eckey);
if (nRet == 1) {
EmbSe_Print(LOG_FLOW_ON, "Verification by OpenSSL PASS\n");
}
else {
EmbSe_Print(LOG_FLOW_ON, "Verification by OpenSSL FAIL (nRet=%d)\n", nRet);
}
EC_KEY_free(dup_eckey);
#else
EmbSe_Print(LOG_ERR_ON, "EmbSe_ECDSA_Do_Verify expected a reference key.\n");
nRet = -1;
goto clean_mem_up;
#endif // PUBLIC_KEY_HANDOVER_TO_SW
}
clean_mem_up:
OPENSSL_free(pSignatureDER);
return nRet;
}
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
/**
* Engine API implementation for computing shared secret, based on local private key, remote public key and an
* optional KDF(Key Derivation Function).
*
* @param[out] sh_secret buffer that will contain the computed shared secret (raw value if KDF is NULL).
* @param[in] sec_len length of computed shared secret.
* @param[in] pub_key public key of remote entity.
* @param[in] ecdh reference to private key object of local entity.
* @param[in] (*KDF) reference to a function that implements Key Derivation Function (hash on raw secret)
*
* @param: (*KDF)in- Reference to buffer containing the generated shared secret.
* @param: (*KDF)inlen- Length of the input
* @param: (*KDF)out - Buffer that returns final output on running KDF
* @param: (*KDF)outlen - returns length of computed output on running KDF
* @return: On failure, returns -1; On success returns length of computed secret.
*/
static int EmbSe_Compute_Key(void *sh_secret,
size_t sec_len,
const EC_POINT *pub_key,
EC_KEY *ecdh,
void *(*KDF)(const void *in, size_t inlen, void *out, size_t *outlen))
#else
static int EmbSe_Simple_Compute_Key(unsigned char **pout, size_t *poutlen, const EC_POINT *pub_key, const EC_KEY *ecdh)
#endif
{
U16 sw;
sss_status_t status;
sss_object_t keyPair;
sss_derive_key_t deriveKeyContext;
U16 field_size_bits = 0;
const EC_GROUP *key_group = NULL;
U8 *pubKeyBuf = NULL;
U16 pubKeyBufLen = 0;
U8 pubKeyDerBuf[256];
U16 pubKeyDerBufLen = sizeof(pubKeyDerBuf);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
int ret = -1;
#else
int ret = 0;
#endif
U8 *shSecBuf = NULL;
size_t shSecBufLen_Bits = 0;
size_t shSecBufLen = 0;
uint32_t keyId;
int nid;
size_t maxSharedSecretByteCount = 66;
uint32_t cipherType = 0;
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EmbSe_Print(LOG_FLOW_ON, "EmbSe_Compute_Key invoked (ecdh)\n");
EmbSe_Print(LOG_DBG_ON, "Requested secret = %d\n", sec_len);
#else
EmbSe_Print(LOG_FLOW_ON, "EmbSe_Simple_Compute_Key invoked (ecdh)\n");
#endif
/* Get the key group */
key_group = EC_KEY_get0_group(ecdh);
if (!key_group) {
EmbSe_Print(LOG_ERR_ON, "Unable to extract ECDH key group.\n");
goto err;
}
else { /* Calculate length of field element for the key group */
field_size_bits = (U16)EC_GROUP_get_degree(key_group);
if (!field_size_bits) {
EmbSe_Print(LOG_ERR_ON, "Unable to extract ECDH key field length.\n");
goto err;
}
}
nid = EC_GROUP_get_curve_name(key_group);
if (nid == 0) {
EmbSe_Print(LOG_ERR_ON, "Unable to get curve name.\n");
goto err;
}
else {
EmbSe_Print(LOG_DBG_ON, "** nid = %d **\n", nid);
}
sw = getCipherTypeFromNid(nid, &cipherType);
if (sw != SW_OK) {
EmbSe_Print(LOG_ERR_ON, "Unable to get cipherType from nid=%d\n", nid);
goto err;
}
/* Extract Public Key Data */
/****************************/
// Check if pub key is on the curve group
if (!EC_POINT_is_on_curve(key_group, pub_key, NULL)) {
EmbSe_Print(LOG_ERR_ON, "ECDH Public key error(incompatible group).\n");
goto err;
}
// Get the size of public key -> pass NULL for buffer
pubKeyBufLen = (U16)EC_POINT_point2oct(key_group, pub_key, POINT_CONVERSION_UNCOMPRESSED, NULL, pubKeyBufLen, NULL);
// Allocate memory for public key data & check allocation
pubKeyBuf = SSS_MALLOC(pubKeyBufLen * sizeof(U8));
if (!pubKeyBuf) {
EmbSe_Print(LOG_ERR_ON, "malloc failure for ECDH public key data.\n");
goto err;
}
// Get public key data
if (!EC_POINT_point2oct(key_group, pub_key, POINT_CONVERSION_UNCOMPRESSED, pubKeyBuf, pubKeyBufLen, NULL)) {
EmbSe_Print(LOG_ERR_ON, "ECDH public key data error (EC_POINT_point2oct).\n");
goto err;
}
/* Secure Element Call (if applicable) */
/***************************************/
shSecBufLen = (U16)(field_size_bits + 7) / 8;
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
shSecBuf = SSS_MALLOC(shSecBufLen * sizeof(U8));
#else
shSecBuf = OPENSSL_malloc(shSecBufLen * sizeof(U8));
#endif
shSecBufLen_Bits = shSecBufLen * 8;
sw = getEcKeyReference(ecdh, &keyId);
if (sw == SW_OK) {
sss_session_t cpSession;
sss_key_store_t cpKs;
sss_object_t extPubkey;
sss_object_t derivedKey;
uint32_t keyId_extPubKey = 0x11001100;
uint32_t keyId_derivedKey = 0x22002200;
axCi_MutexLock();
EmbSe_Print(LOG_FLOW_ON,
"SSS based (ECDH) compute_key (keyId=0x%08X, pubKeyLen=%d, shSecBufLen=%d)\n",
keyId,
pubKeyBufLen,
shSecBufLen);
// Create OpenSSL context & keystore on the fly
// This is used to contain the public key and the calculated shared secret
status = sss_session_open(&cpSession, kType_SSS_OpenSSL, 0, kSSS_ConnectionType_Plain, ".");
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "ECDH: OpenSSL session open failed.\n");
goto err;
}
status = sss_key_store_context_init(&cpKs, &cpSession);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "ECDH: sss_key_store_context_init failed.\n");
goto err;
}
status = sss_key_store_allocate(&cpKs, __LINE__);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "ECDH: sss_key_store_allocate failed.\n");
goto err;
}
// Public Key
status = sss_key_object_init(&extPubkey, &cpKs);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "ECDH: sss_key_object_init failed (extPubkey).\n");
goto err;
}
status = sss_key_object_allocate_handle(
&extPubkey, keyId_extPubKey, kSSS_KeyPart_Public, cipherType, pubKeyDerBufLen, kKeyObject_Mode_Transient);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "ECDH: sss_key_object_allocate_handle failed (extPubkey).\n");
goto err;
}
// Encapsulate Raw public key in proper ASN.1 structure
sw = decoratePublicKey(nid, pubKeyDerBuf, &pubKeyDerBufLen, pubKeyBuf, pubKeyBufLen);
if (sw != SW_OK) {
EmbSe_Print(LOG_ERR_ON, "ECDH: decoratePublicKey failed (err=0x%04X).\n", sw);
goto err;
}
EmbSe_PrintPayload(LOG_DBG_ON, pubKeyDerBuf, pubKeyDerBufLen, "pubKeyDerBuf");
status = sss_key_store_set_key(&cpKs, &extPubkey, pubKeyDerBuf, pubKeyDerBufLen, field_size_bits, NULL, 0);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "ECDH: sss_key_store_set_key failed (extPubkey).\n");
goto err;
}
// Shared secret (Symmetric Key)
status = sss_key_object_init(&derivedKey, &cpKs);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "ECDH: sss_key_object_init failed (derivedKey).\n");
goto err;
}
status = sss_key_object_allocate_handle(&derivedKey,
keyId_derivedKey,
kSSS_KeyPart_Default,
kSSS_CipherType_AES,
maxSharedSecretByteCount,
kKeyObject_Mode_Transient);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "ECDH: sss_key_object_allocate_handle failed (derivedKey).\n");
goto err;
}
// Keypair stored in secure element
status = sss_key_object_init(&keyPair, &gpCtx->ks);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "ECDH: sss_key_object_init for keyPair failed\n");
goto err;
}
status = sss_key_object_get_handle(&keyPair, keyId);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "ECDH: sss_key_object_get_handle for pubKey failed\n");
sss_key_object_free(&keyPair);
goto err;
}
status = sss_derive_key_context_init(
&deriveKeyContext, &gpCtx->session, &keyPair, kAlgorithm_SSS_ECDH, kMode_SSS_ComputeSharedSecret);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "ECDH: sss_derive_key_context_init for kMode_SSS_ComputeSharedSecret failed\n");
sss_key_object_free(&keyPair);
goto err;
}
EmbSe_Print(LOG_FLOW_ON, " After sss_derive_key_context_init.\n");
status = sss_derive_key_dh(&deriveKeyContext, &extPubkey, &derivedKey);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "ECDH: sss_derive_key_dh failed\n");
sss_key_object_free(&keyPair);
goto err;
}
// Retrieve the shared secret into shSecBuf
status = sss_key_store_get_key(&cpKs, &derivedKey, shSecBuf, &shSecBufLen, &shSecBufLen_Bits);
if (status != kStatus_SSS_Success) {
EmbSe_Print(LOG_ERR_ON, "ECDH: sss_key_store_get_key failed.\n");
goto err;
}
// What did we get? Don't print this to console!
// EmbSe_Print(LOG_DBG_ON, "ECDH: shSecBufLen_Bits=%d\n", shSecBufLen_Bits);
// EmbSe_PrintPayload(LOG_DBG_ON, shSecBuf, shSecBufLen, "shSecBuf");
axCi_MutexUnlock();
}
else if (sw == ERR_NO_PRIVATE_KEY) {
EmbSe_Print(LOG_ERR_ON, "Expecting private key (by value or reference): 0x%04X.\n", sw);
goto err;
}
else if (sw == ERR_PATTERN_COMPARE_FAILED) {
#ifdef PRIVATE_KEY_HANDOVER_TO_SW
EC_KEY *dup_ecdh = NULL;
int ecdh_ret = -1;
// Delegate to OpenSSL SW implementation
EmbSe_Print(LOG_FLOW_ON, "No matching key in SE. Invoking OpenSSL API: ECDH_compute_key.\n");
/* Create a duplicate key */
dup_ecdh = EC_KEY_dup(ecdh);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
/* Attach OpenSSL's SW method to duplicate key */
if (!ECDH_set_method(dup_ecdh, ECDH_OpenSSL())) {
EmbSe_Print(LOG_ERR_ON, "OpenSSL ECDH_set_method failure.\n");
goto err;
}
#else
/* Attach OpenSSL's SW method to duplicate key */
if (!EC_KEY_set_method(dup_ecdh, EC_KEY_OpenSSL())) {
EmbSe_Print(LOG_ERR_ON, "OpenSSL EC_KEY_set_method failure..\n");
goto err;
}
#endif
/* Invoke OpenSSL ECDH_compute_key and return result */
// int ECDH_compute_key(void *out, size_t outlen, const EC_POINT *pub_key, EC_KEY *ecdh,
// void *(*KDF)(const void *in, size_t inlen, void *out, size_t *outlen));
ecdh_ret = ECDH_compute_key(shSecBuf, shSecBufLen, pub_key, dup_ecdh, NULL);
EC_KEY_free(dup_ecdh);
if (0 < ecdh_ret) {
EmbSe_Print(LOG_FLOW_ON, "ECDH_compute_key by OpenSSL PASS\n");
shSecBufLen = (U16)ecdh_ret;
}
else {
EmbSe_Print(LOG_ERR_ON, "ECDH_compute_key by OpenSSL FAILS with %d.\n", ecdh_ret);
goto err;
}
#else
EmbSe_Print(LOG_ERR_ON, "EmbSe_Compute_Key expected a reference key: 0x%04X.\n", sw);
goto err;
#endif
}
else {
EmbSe_Print(LOG_ERR_ON, "EmbSe_Compute_Key unexpected key type: 0x%04X.\n", sw);
goto err;
}
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
/* Finally run the KDF, if provided */
memset(sh_secret, 0, shSecBufLen);
if (KDF != 0) {
if (KDF(shSecBuf, shSecBufLen, sh_secret, &sec_len) == NULL) {
EmbSe_Print(LOG_ERR_ON, "KDF failed.\n");
goto err;
}
ret = sec_len;
}
else {
/* When KDF=NULL, return raw secret, copy asked length */
if (sec_len > shSecBufLen) {
sec_len = shSecBufLen;
}
memcpy(sh_secret, shSecBuf, sec_len);
ret = sec_len;
}
#else
*pout = shSecBuf;
*poutlen = shSecBufLen;
ret = 1;
#endif
// Never print shared secret
// EmbSe_PrintPayload(LOG_DBG_ON, sh_secret, sec_len, "Shared Secret: ");
err:
/* Free all allocated memory */
if (pubKeyBuf)
SSS_FREE(pubKeyBuf);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
if (shSecBuf)
SSS_FREE(shSecBuf);
#endif
return ret;
}
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
ECDSA_METHOD EmbSe_ECDSA = {"e2se_ecdsa", *EmbSe_ECDSA_Do_Sign, EmbSe_ECDSA_Sign_Setup, EmbSe_ECDSA_Do_Verify, 0, NULL};
ECDH_METHOD EmbSe_ECDH = {"e2se_ecdh", *EmbSe_Compute_Key, 0, NULL};
#else
// Renamed 'ossl_ecdsa_sign' from openssl-1.1.0j/crypto/ec/ecdsa_ossl.c
static int my_ossl_ecdsa_sign(int type,
const unsigned char *dgst,
int dlen,
unsigned char *sig,
unsigned int *siglen,
const BIGNUM *kinv,
const BIGNUM *r,
EC_KEY *eckey)
{
ECDSA_SIG *s;
RAND_seed(dgst, dlen);
s = ECDSA_do_sign_ex(dgst, dlen, kinv, r, eckey);
if (s == NULL) {
*siglen = 0;
return 0;
}
*siglen = i2d_ECDSA_SIG(s, &sig);
ECDSA_SIG_free(s);
return 1;
}
// Renamed 'ossl_ecdsa_verify' from openssl-1.1.0j/crypto/ec/ecdsa_ossl.c
static int my_ossl_ecdsa_verify(
int type, const unsigned char *dgst, int dgst_len, const unsigned char *sigbuf, int sig_len, EC_KEY *eckey)
{
ECDSA_SIG *s;
const unsigned char *p = sigbuf;
unsigned char *der = NULL;
int derlen = -1;
int ret = -1;
s = ECDSA_SIG_new();
if (s == NULL)
return (ret);
if (d2i_ECDSA_SIG(&s, &p, sig_len) == NULL)
goto err;
/* Ensure signature uses DER and doesn't have trailing garbage */
derlen = i2d_ECDSA_SIG(s, &der);
if (derlen != sig_len || memcmp(sigbuf, der, derlen) != 0)
goto err;
ret = ECDSA_do_verify(dgst, dgst_len, s, eckey);
err:
OPENSSL_clear_free(der, derlen);
ECDSA_SIG_free(s);
return (ret);
}
int EmbSe_Simple_Key_gen(EC_KEY *key)
{
int (*openssl_Key_gen_sw)(EC_KEY * key) = NULL;
EC_KEY_METHOD_get_keygen((EC_KEY_METHOD *)EmbSe_EC_Default, &openssl_Key_gen_sw);
return openssl_Key_gen_sw(key);
}
int setup_ec_key_method(void)
{
EmbSe_EC_Default = EC_KEY_get_default_method();
EmbSe_EC = EC_KEY_METHOD_new(NULL);
if (EmbSe_EC == NULL) {
return 0;
}
// NOTE: Equivalent of set_name does not exist for OpenSSL 1.1
// EC_KEY_METHOD_set_name(EmbSe_EC, "e2se_ecdsa");
EC_KEY_METHOD_set_sign(EmbSe_EC, my_ossl_ecdsa_sign, EmbSe_ECDSA_Sign_Setup, EmbSe_ECDSA_Do_Sign);
EC_KEY_METHOD_set_verify(EmbSe_EC, my_ossl_ecdsa_verify, EmbSe_ECDSA_Do_Verify);
EC_KEY_METHOD_set_compute_key(EmbSe_EC, EmbSe_Simple_Compute_Key);
EC_KEY_METHOD_set_keygen(EmbSe_EC, EmbSe_Simple_Key_gen);
return 1;
}
#endif // (OPENSSL_VERSION_NUMBER < 0x10100000L)
#endif //#if SSS_HAVE_ECC