/** * @file ax_a71chEngine.c * @author NXP Semiconductors * @version 1.0 * @par License * * Copyright 2017,2020 NXP * SPDX-License-Identifier: Apache-2.0 * * @par Description * OpenSSL Engine for Embedded Secure Element (A71CH) * * This engine invokes the API of axHostSw/a71ch that wraps APDU communication * with the A71CH secure element. * * 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 public key is passed by value and it matches the value of a public key * stored in the Secure Element -> delegate the operation to the Secure Element * - If none of the two above cases 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. */ #include "ax_embSeEngine.h" #include "ax_cryptoIpc.h" #include // #include #include #include #include #include #include #include #include #include #if !defined(OPENSSL_SYS_MACOSX) #include #endif #include "ax_api.h" #include "ax_embSeEngine_Internal.h" #include "fsl_sscp_a71ch.h" #include "sm_printf.h" #include "nxLog_App.h" #include "nxEnsure.h" #if (OPENSSL_VERSION_NUMBER < 0x10100000L) #else #ifdef OPENSSL_NO_DYNAMIC_ENGINE #undef OPENSSL_NO_DYNAMIC_ENGINE #endif #endif // #define PRIVATE_KEY_HANDOVER_TO_SW #define PUBLIC_KEY_HANDOVER_TO_SW // /* Define ECDH secret key size */ #define ECDH_MAX_LEN 32 #define EMBSE_MAX_ECC_PUBKEY_BUF (2*96 + 1) // Corresponds to 768 bit ECC key /* Maximum Key Index supported by the A70CM SE */ #define EMBSE_A70CM_MAX_PUBLIC_KEY_INDEX 2 /* Logging related defines */ #define EMBSE_MAX_PRINT_BUF_SIZE (511) #define LOG_FLOW_MASK 0x01 #define LOG_DBG_MASK 0x02 #define LOG_ERR_MASK 0x04 #define LOG_FLOW_ON 0x01 #define LOG_DBG_ON 0x02 #define LOG_ERR_ON 0x04 // Adjust to the required default log level. static int EMBSE_LogControl = (LOG_ERR_ON | LOG_DBG_ON | LOG_FLOW_ON); // Full log // static int EMBSE_LogControl = (LOG_ERR_ON); // Only Errors static axKeyIdentifier_t eccSigningKeys[] = { {A71CH_SSI_KEY_PAIR, A71CH_KEY_PAIR_0}, {A71CH_SSI_KEY_PAIR, A71CH_KEY_PAIR_1}, {A71CH_SSI_KEY_PAIR, A71CH_KEY_PAIR_2}, {A71CH_SSI_KEY_PAIR, A71CH_KEY_PAIR_3} }; static axKeyIdentifier_t eccVerifyKeys[] = { {A71CH_SSI_PUBLIC_KEY, A71CH_PUBLIC_KEY_0}, {A71CH_SSI_PUBLIC_KEY, A71CH_PUBLIC_KEY_1}, {A71CH_SSI_PUBLIC_KEY, A71CH_PUBLIC_KEY_2} }; static axKeyIdentifier_t eccDhStaticKeys[] = { {A71CH_SSI_KEY_PAIR, A71CH_KEY_PAIR_0}, {A71CH_SSI_KEY_PAIR, A71CH_KEY_PAIR_1}, {A71CH_SSI_KEY_PAIR, A71CH_KEY_PAIR_2}, {A71CH_SSI_KEY_PAIR, A71CH_KEY_PAIR_3} }; // Locally used utility functions static U16 getEcKeyReference(const EC_KEY *eckey, axKeyIdentifier_t *validKeys, int nKeys, SST_Identifier_t *ident, SST_Index_t *idx); static U16 axAdaptSize(U8* pOut, U16 expectedLen, const U8 *pIn, U16 actualLen); static void EmbSe_Print(int flag, const char * format, ...); static void EmbSe_PrintPayload(int flag, const U8 *pPayload, U16 nLength, const char *title); static U16 EmbSe_A70Init(void); /* engine name */ static const char *embSe_id = OPENSSL_ENGINE_EMBSE_ID; static const char *embSe_name = "se hardware engine support"; /* Random Num Status, used when Get Rand Status is invoked */ unsigned short gRandStatus = 1; const int Version1 = 1; //-> Release Version1.Version2.Version3 const int Version2 = 0; const int Version3 = 1; /* 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; }; /* Engine API Declaration */ static int EmbSe_Rand(unsigned char *buf, int num); static int EmbSe_Rand_Status(void); static ECDSA_SIG *EmbSe_ECDSA_Do_Sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey); static int EmbSe_ECDSA_Sign_Setup(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv, BIGNUM **r); static int EmbSe_ECDSA_Do_Verify(const unsigned char *dgst, int dgst_len, const ECDSA_SIG *sig, EC_KEY *eckey); #if (OPENSSL_VERSION_NUMBER < 0x10100000L) 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); static int EmbSe_Simple_Key_gen(EC_KEY *key); #endif /* Fill in implemented Engine methods in respective data structures */ static RAND_METHOD EmbSe_RAND = { NULL, /* RAND_seed() */ EmbSe_Rand, /* RAND_bytes() */ NULL, /* RAND_cleanup() */ NULL, /* RAND_add() */ EmbSe_Rand, /* RAND_pseudo_rand() */ EmbSe_Rand_Status /* RAND_status() */ }; #if (OPENSSL_VERSION_NUMBER < 0x10100000L) static ECDSA_METHOD EmbSe_ECDSA = { "e2se_ecdsa", *EmbSe_ECDSA_Do_Sign, EmbSe_ECDSA_Sign_Setup, EmbSe_ECDSA_Do_Verify, 0, NULL }; static 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; int rv = 0; ENSURE_OR_GO_EXIT(siglen != NULL); 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); rv = 1; exit: return rv; } // 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); } static EC_KEY_METHOD *EmbSe_EC = NULL; static EC_KEY_METHOD *EmbSe_EC_Default = NULL; static int setup_ec_key_method(void) { EmbSe_EC_Default = (EC_KEY_METHOD *)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) #ifndef OPENSSL_NO_HW /* The definitions for control commands specific to this engine */ #define EMBSE_LOG_LEVEL ENGINE_CMD_BASE #define EMBSE_CMD_SO_PATH (ENGINE_CMD_BASE + 1) static const ENGINE_CMD_DEFN embSe_cmd_defns[] = { {EMBSE_LOG_LEVEL, "LOG_LEVEL", "Specifies the Log level (Error=0x04; Debug=0x02; Flow=0x01; Or'd combinations possible)", ENGINE_CMD_FLAG_NUMERIC}, {EMBSE_CMD_SO_PATH, "SO_PATH", "Specifies the path to the 'e2se ssl' shared library (not implemented)", ENGINE_CMD_FLAG_STRING}, {0, NULL, NULL, 0} }; /* Engine API's for initialization and cleanup */ static int EmbSe_Destroy(ENGINE *e) { AX_UNUSED_ARG(e); EmbSe_Print(LOG_FLOW_ON, "EmbSe_Destroy(): Entry\n"); return 1; } /****** ENGINE API's *******/ static int EmbSe_Init(ENGINE *e) { U16 sw = 0; AX_UNUSED_ARG(e); /* Initialize the Engine Mutex */ CryptoIpc_MutexInit(AX_CI_TRUE); sw = EmbSe_A70Init(); if (sw != SW_OK) { EmbSe_Print(LOG_ERR_ON, "Call to EmbSe_A70Init() failed with return code 0x%04X\n", sw); return -1; } EmbSe_Print(LOG_FLOW_ON, "Version: %d.%d.%d\n", Version1, Version2, Version3); #ifdef __gnu_linux__ mallopt(M_MMAP_THRESHOLD, 125); #endif /* Engine is ready to use */ return 1; } static int EmbSe_Finish(ENGINE *e) { AX_UNUSED_ARG(e); SM_Close(NULL, SMCOM_CLOSE_MODE_STD); EmbSe_Print(LOG_FLOW_ON, "EmbSe_Finish(): Entry\n"); return 1; } static int EmbSe_Ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void)) { AX_UNUSED_ARG(e); /* Values of String Variables are received in p, while values of Numeric ones are in i */ switch(cmd) { case EMBSE_LOG_LEVEL: EmbSe_Print(LOG_FLOW_ON, "Control Command EMBSE_LOG_LEVEL; requested log level = %ld\n", i); if (i < 0x08) { EMBSE_LogControl = i & 0x07; } else { EmbSe_Print(LOG_DBG_ON, "Invalid Control Command value for EMBSE_LOG_LEVEL\n"); } return 1; case EMBSE_CMD_SO_PATH: EmbSe_Print(LOG_ERR_ON, "Control command EMBSE_CMD_SO_PATH has not been implemented.\n"); return 0; default: break; } return 0; } /* This internal function is used by ENGINE_e2se() and possibly by the * "dynamic" ENGINE support too */ static int bind_helper(ENGINE *e) { if (!ENGINE_set_id(e, embSe_id) || !ENGINE_set_name(e, embSe_name) || !ENGINE_set_destroy_function(e, &EmbSe_Destroy) || !ENGINE_set_init_function(e, &EmbSe_Init) || !ENGINE_set_finish_function(e, &EmbSe_Finish) || !ENGINE_set_ctrl_function(e, &EmbSe_Ctrl) || !ENGINE_set_cmd_defns(e, &embSe_cmd_defns[0]) || !ENGINE_set_RAND(e, &EmbSe_RAND) || #if (OPENSSL_VERSION_NUMBER < 0x10100000L) !ENGINE_set_ECDSA(e, &EmbSe_ECDSA) || !ENGINE_set_ECDH(e, &EmbSe_ECDH)) #else !setup_ec_key_method() || !ENGINE_set_EC(e, EmbSe_EC)) #endif { return 0; } return 1; } #ifdef OPENSSL_NO_DYNAMIC_ENGINE static ENGINE *EngineEmbSe(void) { ENGINE *ret = ENGINE_new(); if (!ret) { return NULL; } if (!bind_helper(ret)) { ENGINE_free(ret); return NULL; } return ret; } void EngineEmbSe_Load(void) { /* Copied from eng_[openssl|dyn].c */ ENGINE *toadd = EngineEmbSe(); if (!toadd) return; ENGINE_add(toadd); ENGINE_free(toadd); ERR_clear_error(); EmbSe_Print(LOG_FLOW_ON, "EngineEmbSe_Load succeeded!\n"); } #else /* This stuff is needed if this ENGINE is being compiled into a self-contained * shared-library. */ static int bind_fn(ENGINE *e, const char *id) { if (id && (strcmp(id, embSe_id) != 0)) return 0; if (!bind_helper(e)) return 0; return 1; } IMPLEMENT_DYNAMIC_CHECK_FN() IMPLEMENT_DYNAMIC_BIND_FN(bind_fn) #endif /* OPENSSL_NO_DYNAMIC_ENGINE */ #endif /* !OPENSSL_NO_HW */ #define MAX_RND_CHUNK A71CH_SCP03_MAX_PAYLOAD_SIZE /** * Implementation of Engine API for Random Number Generation. Invokes Host API RND_GetRandom * @param[in,out] buf buffer to store the generated Random Number * @param[in] num number of random bytes requested * @retval 0 upon failure * @retval 1 upon success */ static int EmbSe_Rand(unsigned char *buf, int num) { U16 sw = ERR_GENERAL_ERROR; int requested = 0; int offset = 0; int chunk = 0; EmbSe_Print(LOG_FLOW_ON, "EmbSe_Rand invoked requesting %d random bytes\n", num); memset(buf,0,num); CryptoIpc_MutexLock(); requested = num; while (requested > 0) { if (requested > MAX_RND_CHUNK) { chunk = MAX_RND_CHUNK; } else { chunk = requested; } sw = A71_GetRandom (buf+offset, chunk); if (sw != SW_OK) { break; } offset += chunk; requested -= chunk; } CryptoIpc_MutexUnlock(); gRandStatus = sw; if (sw == SW_OK) { return 1; } else { EmbSe_Print(LOG_ERR_ON, "Call to RND_GetRandom failed with 0x%04X\n", sw); return 0; } } /** * @function EmbSe_Rand_Status * @description Engine API to return the status from invocation of RND_GetRandom() * @param void * @return value U16 of previous RND_GetRandom() API. */ static int EmbSe_Rand_Status(void) { EmbSe_Print(LOG_FLOW_ON, "EmbSe_Rand_Status invoked\n"); return (int)gRandStatus; } // EmbSe RSA API's /* Size of an SSL signature: MD5+SHA1 */ #define SSL_SIG_LENGTH 36 // 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]; U16 sigDERLen = sizeof(sigDER); U16 sw; ECDSA_SIG *pSig; EC_KEY *dup_eckey = NULL; U8 *pp; SST_Identifier_t ident; SST_Index_t idx; U8 dgstBuf[32]; U16 dgstBufLen = sizeof(dgstBuf); sw = getEcKeyReference(eckey, eccSigningKeys, sizeof(eccSigningKeys)/sizeof(axKeyIdentifier_t), &ident, &idx); if (sw == SW_OK) { EmbSe_Print(LOG_FLOW_ON, "ECC_Sign(ident=%d, idx=%d; dgstLen=%d)\n", ident, idx, dgst_len); axAdaptSize(dgstBuf, dgstBufLen, dgst, dgst_len); CryptoIpc_MutexLock(); sw = A71_EccSign(idx, (const U8*)dgstBuf, dgstBufLen, (U8*)sigDER, (U16*)&sigDERLen); CryptoIpc_MutexUnlock(); if (sw != SW_OK) { EmbSe_Print(LOG_ERR_ON, "SE signature creation error: 0x%04X.\n", sw); return NULL; } EmbSe_Print(LOG_FLOW_ON, "A71_EccSign 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"); return NULL; } if (d2i_ECDSA_SIG((ECDSA_SIG**)&pSig, (const unsigned char**)&pp, sigDERLen) == NULL) { EmbSe_Print(LOG_ERR_ON, "d2i_ECDSA_SIG failed\n"); return NULL; } EmbSe_Print(LOG_FLOW_ON, "EmbSe_ECDSA_Do_Sign success.\n"); return pSig; } else if (sw == ERR_IDENT_IDX_RANGE) { EmbSe_Print(LOG_ERR_ON, "Reference Key with identifier or index out of range: 0x%04X.\n", sw); return NULL; } 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 A71CH. 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; U8 retval = 0; int nRet = 0; int i; int flagHandleKey = AX_ENGINE_INVOKE_NOTHING; EC_KEY *dup_eckey = NULL; U8 *pSignatureDER, *pSigTmp; U16 sigLen; SST_Identifier_t ident; SST_Index_t idx; size_t pub_key_len; const EC_POINT *pub_key_point; U8 refPub[EMBSE_MAX_ECC_PUBKEY_BUF]; 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, eccVerifyKeys, sizeof(eccVerifyKeys)/sizeof(axKeyIdentifier_t), &ident, &idx); if (sw == SW_OK) { flagHandleKey = AX_ENGINE_INVOKE_SE; } else if ( (sw == ERR_NO_PRIVATE_KEY) || (sw == ERR_PATTERN_COMPARE_FAILED) ) { // Check whether the public key passed by value matches one of // the provisioned public keys. /* Extract public key */ pub_key_point = EC_KEY_get0_public_key(eckey); if (!pub_key_point) { EmbSe_Print(LOG_ERR_ON, "EmbSe_ECDSA_Do_Verify: EC Key public key error.\n"); nRet = -1; goto clean_mem_up; } pub_key_len = EC_POINT_point2oct(EC_KEY_get0_group(eckey), pub_key_point, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); if ( (pub_key_len == 0) || (pub_key_len > sizeof(refPub)) ) { EmbSe_Print(LOG_ERR_ON, "EmbSe_ECDSA_Do_Verify: Preparation to convert public key into byte array failed.\n"); nRet = -1; goto clean_mem_up; } if ( EC_POINT_point2oct(EC_KEY_get0_group(eckey), pub_key_point, POINT_CONVERSION_UNCOMPRESSED, refPub, pub_key_len, NULL) == 0 ) { EmbSe_Print(LOG_ERR_ON, "EmbSe_ECDSA_Do_Verify: Pub key data extraction error.\n"); nRet = -1; goto clean_mem_up; } EmbSe_PrintPayload(LOG_DBG_ON, refPub, (U16)pub_key_len, "PubKey (Verify)"); flagHandleKey = AX_ENGINE_INVOKE_OPENSSL_SW; // The local data structure eccVerifyKeys must match the storage // capability of the attached secure element. // Failure to retrieve a key from the secure element will not be // considered fatal (the value may not have been provisioned) CryptoIpc_MutexLock(); for (i=0; i<(int)(sizeof(eccVerifyKeys)/sizeof(axKeyIdentifier_t)); i++) { U8 fetchedPubKey[65]; U16 fetchedPubKeyLen = sizeof(fetchedPubKey); memset(fetchedPubKey, 0, fetchedPubKeyLen); idx = eccVerifyKeys[i].idx; EmbSe_Print(LOG_DBG_ON, "A71_GetEccPublicKey(0x%02X)\n", (SST_Index_t)idx); sw = A71_GetEccPublicKey((SST_Index_t)idx, fetchedPubKey, &fetchedPubKeyLen); if (sw == SW_OK) { if (memcmp(refPub, fetchedPubKey, sizeof(fetchedPubKey)) == 0) { // We have a match. flagHandleKey = AX_ENGINE_INVOKE_SE; EmbSe_Print(LOG_FLOW_ON, "EmbSe_ECDSA_Do_Verify: Found matching public key at index 0x%02X\n", idx); break; } } } CryptoIpc_MutexUnlock(); } 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[32]; U16 dgstBufLen = sizeof(dgstBuf); EmbSe_Print(LOG_FLOW_ON, "ECC_Verify(KeyIdent=%d, KeyIndex=%d, dgst_len=%d, sigLen=%d)\n", ident, idx, dgst_len, sigLen); axAdaptSize(dgstBuf, dgstBufLen, dgst, dgst_len); CryptoIpc_MutexLock(); sw = A71_EccVerify(idx, dgstBuf, dgstBufLen, pSignatureDER, sigLen, (U8*)&retval); CryptoIpc_MutexUnlock(); if (sw != SW_OK) { EmbSe_Print(LOG_ERR_ON, "A71_EccVerify returned with Error: 0x%04X\n", sw); nRet = -1; goto clean_mem_up; } else { if (retval == 1) EmbSe_Print(LOG_FLOW_ON, "Verification PASS\n"); else EmbSe_Print(LOG_FLOW_ON, "Verification FAIL\n"); } nRet = (int)retval; } else if (flagHandleKey == AX_ENGINE_INVOKE_OPENSSL_SW) { #ifdef PUBLIC_KEY_HANDOVER_TO_SW EmbSe_Print(LOG_FLOW_ON, "No matching key in A71CH. 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 /* 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 or a matching stored public key.\n"); nRet = -1; goto clean_mem_up; #endif } else { EmbSe_Print(LOG_ERR_ON, "EmbSe_ECDSA_Do_Verify: unexpected conditional branch (flagHandleKey=%d).\n", flagHandleKey); nRet = -1; goto clean_mem_up; } 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; U16 field_size_bits = 0; const EC_GROUP *key_group = NULL; U8 *pubKeyBuf = NULL; U16 pubKeyBufLen = 0; #if (OPENSSL_VERSION_NUMBER < 0x10100000L) int ret = -1; #else int ret = 0; #endif U8 *shSecBuf = NULL; U16 shSecBufLen = 0; SST_Identifier_t ident; SST_Index_t idx; #if (OPENSSL_VERSION_NUMBER < 0x10100000L) EmbSe_Print(LOG_FLOW_ON, "EmbSe_Compute_Key invoked (ecdh)\n"); /* Requested secret length limit check */ if (sec_len > ECDH_MAX_LEN) { EmbSe_Print(LOG_ERR_ON, "Requested secret too long. Try <= %d.\n", ECDH_MAX_LEN); return ret; } #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; } } /* 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 = 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 = malloc(shSecBufLen * sizeof(U8)); #else shSecBuf = OPENSSL_malloc(shSecBufLen * sizeof(U8)); #endif sw = getEcKeyReference(ecdh, eccDhStaticKeys, sizeof(eccDhStaticKeys)/sizeof(axKeyIdentifier_t), &ident, &idx); if (sw == SW_OK) { EmbSe_Print(LOG_FLOW_ON, "A71_EcdhGetSharedSecret(idx=%d, pubKeyLen=%d, shSecBufLen=%d)\n", idx, pubKeyBufLen, shSecBufLen); CryptoIpc_MutexLock(); sw = A71_EcdhGetSharedSecret(idx, pubKeyBuf, pubKeyBufLen, shSecBuf, &shSecBufLen); CryptoIpc_MutexUnlock(); if (sw == SW_OK) { EmbSe_Print(LOG_FLOW_ON, "A71CH: A71_EcdhGetSharedSecret OK: Status code 0x%04x\n", sw); } else { EmbSe_Print(LOG_ERR_ON, "A71CH: A71_EcdhGetSharedSecret Error: Status code 0x%04x\n", sw); goto err; } } else if (sw == ERR_IDENT_IDX_RANGE) { EmbSe_Print(LOG_ERR_ON, "Reference Key with identifier or index out of range: 0x%04X.\n", sw); goto err; } 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 A71CH. 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 = (int)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 = (int)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) free(pubKeyBuf); #if (OPENSSL_VERSION_NUMBER < 0x10100000L) if (shSecBuf) free(shSecBuf); #endif return ret; } #if (OPENSSL_VERSION_NUMBER < 0x10100000L) #else static 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); } #endif /* Support functions */ /**********************/ /** * Initialize communication with Secure Element. * Supporting function for Engine Init. * * @retval ::SW_OK Upon successful execution */ static U16 EmbSe_A70Init(void) { U16 connectStatus = 0; U8 Atr[64]; U16 AtrLen = sizeof(Atr); SmCommState_t commState; sm_printf(DBGOUT, "Connect to A71CH. Chunksize at link layer = %d.\n", MAX_CHUNK_LENGTH_LINK); #if defined(TDA8029_UART) || defined(SCI2C) || defined(PCSC)|| defined(T1oI2C) connectStatus = SM_Connect(NULL, &commState, Atr, &AtrLen); #elif defined(SMCOM_JRCP_V1) || defined(SMCOM_JRCP_V2) // { // if (CONF_modules_load_file(NULL, "rjct_server", // CONF_MFLAGS_IGNORE_MISSING_FILE) <= 0) // { // fprintf(stderr, "FATAL: error loading configuration file\n"); // ERR_print_errors_fp(stderr); // } // } { #ifdef SMCOM_JRCP_V2 commState.connType = kType_SE_Conn_Type_JRCP_V2; #endif #ifdef SMCOM_JRCP_V1 commState.connType = kType_SE_Conn_Type_JRCP_V1; #endif char *rjctServerAddress = NULL; rjctServerAddress = getenv("RJCT_SERVER_ADDR"); if (rjctServerAddress == NULL) { connectStatus = SM_RjctConnect(NULL, "127.0.0.1:8050", &commState, Atr, &AtrLen); } else { char szConnect[256]; size_t connectLen = sizeof(szConnect); char szColonPort[] = ":8050"; size_t colonPortLen = sizeof(szColonPort) + 1; if (sizeof(rjctServerAddress) > (connectLen-colonPortLen)) { sm_printf(DBGOUT, "Server address is too long: %s\n", rjctServerAddress); return ERR_NO_VALID_IP_PORT_PATTERN; } else { strcpy(szConnect, rjctServerAddress); //strcat(szConnect, szColonPort); sm_printf(DBGOUT, "Server address:port set to %s\n", szConnect); connectStatus = SM_RjctConnect(NULL, szConnect, &commState, Atr, &AtrLen); } } } #else #error "No communication channel defined" #endif // TDA8029 if ( (connectStatus == ERR_CONNECT_LINK_FAILED) || (connectStatus == ERR_CONNECT_SELECT_FAILED) ) { sm_printf(CONSOLE, "SM_Connect failed with status 0x%04X\n", connectStatus); return connectStatus; } else if ( connectStatus == SMCOM_COM_FAILED ) { sm_printf(CONSOLE, "SM_Connect failed with status 0x%04X (Could not open communication channel)\n", connectStatus); return connectStatus; } else if ( connectStatus == SMCOM_PROTOCOL_FAILED) { sm_printf(CONSOLE, "SM_Connect failed with status 0x%04X (Could not establish communication protocol)\n", connectStatus); return connectStatus; } else if ( connectStatus == ERR_NO_VALID_IP_PORT_PATTERN ) { sm_printf(DBGOUT, "Pass the IP address and port number as arguments, e.g. \"127.0.0.1:8050\"!\n"); return connectStatus; } else { int i=0; #if defined(SCI2C) sm_printf(CONSOLE, "SCI2C_"); // To highlight the ATR format for SCI2C deviates from ISO7816-3 #elif defined(SPI) sm_printf(CONSOLE, "SCSPI_"); #endif if (AtrLen > 0) { sm_printf(CONSOLE, "ATR=0x"); for (i=0; i