/** * @file ex_walkthrough.c * @author NXP Semiconductors * @version 1.0 * @par License * * Copyright 2016 NXP * SPDX-License-Identifier: Apache-2.0 * * @par Description * Refer to \ref walkthrough for more information */ #include #include #include #include // project specific include files #include "ax_api.h" #include "a71ch_ex.h" #include "ax_util.h" #include "a71_debug.h" #include "sm_types.h" #include "sm_apdu.h" #include "tst_sm_util.h" #include "tst_a71ch_util.h" #include "nxLog_hostLib.h" #include "axHostCrypto.h" #include "tstHostCrypto.h" #include "HostCryptoAPI.h" static U8 exWtSetConfigKeys(U8 *moduleConfigKey, U8 *privateKeyConfigKey, U8 *publicKeyConfigKey); static U8 exWtProvision(void); static U8 exWtPrepareShipment(void); static U8 exWtUseCrypto(void); static EC_KEY *eccKeyRootCA_0 = NULL; //!< OpenSSL specific datastructure to contain keypair, whose public key is stored at index 0 static EC_KEY *eccKeyRootCA_1 = NULL; //!< OpenSSL specific datastructure to contain keypair, whose public key is stored at index 1 static eccKeyComponents_t eccKcTls_0; //!< Crypto library independent datastructure to contain keypair to be stored at index 0 static eccKeyComponents_t eccKcTls_1; //!< Crypto library independent datastructure to contain keypair to be stored at index 1 static eccKeyComponents_t eccKcRootCA_0; //!< Crypto library independent datastructure to contain keypair, whose public key is stored at index 0 static eccKeyComponents_t eccKcRootCA_1; //!< Crypto library independent datastructure to contain keypair, whose public key is stored at index 1 static U8 configKeyModuleLock[16] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x11, 0x40, 0x00, 0xAA, 0x40, 0x01, 0xAA}; //!< Configuration Key for Module, acting as transport key (index ::A71CH_CFG_KEY_IDX_MODULE_LOCK) static U8 configKeyPrivateKey[16] = {0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0x00, 0x11, 0x40, 0x00, 0xAA, 0x40, 0x01, 0xAA}; //!< Configuration Key for Private Key encryption (index ::A71CH_CFG_KEY_IDX_PRIVATE_KEYS) static U8 configKeyPublicKey[16] = {0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0x11, 0x40, 0x00, 0xAA, 0x40, 0x01, 0xAA}; //!< Configuration Key for Public Key encryption (index ::A71CH_CFG_KEY_IDX_PUBLIC_KEYS) /// @cond static U8 aesBasePattern[] = { 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF }; static U16 aesBasePatternLen = sizeof(aesBasePattern); static U8 aesRef[A71CH_SYM_KEY_MAX][16]; static U16 productDataOffset = 0x00; static U8 gpProductData[] = { 0xAA, 0x10, 0x00, 0xAA, 0x20, 0x01, 0xAA, 0x30, 0x00, 0xAA, 0x40, 0x00, 0xAA, 0x40, 0x01, 0xAA, 0x50, 0x00, 0xAA, 0x60, 0x01, 0xAA, 0x70, 0x00, 0xAA, 0x80, 0x00, 0xAA, 0x90, 0x01, 0x00, 0x00 }; /// @endcond #define USE_SCP03 /** * Demonstrate usage of A71CH from a system integrator's perspective * - An optional binding and setting up of an SCP03 channel between Host and A71CH (::axExCreateAndSetInitialHostScpKeys * and ::axExAuthenticate) * - Provisioning step (::exWtProvision) * - Prepare for shipment (::exWtPrepareShipment) * - Demonstrate Crypyto usage (::exWtUseCrypto) * * \note The example relies on the availability of the debug reset to bring the A71CH * into its initial state. * */ U8 exWalkthrough() { U8 result = 1; U8 initMode = INIT_MODE_RESET; #ifdef USE_SCP03 U8 scpKeyEncBase[SCP_KEY_SIZE]; U8 scpKeyMacBase[SCP_KEY_SIZE]; U8 scpKeyDekBase[SCP_KEY_SIZE]; #endif LOG_I("-----------Start exWalkthrough(%s)------------", getInitModeAsString(initMode)); DEV_ClearChannelState(); LOG_I("Initialize Secure Module"); result &= a71chInitModule(initMode); // STAGE-0: // - The A71CH has been forced into the initial state through the DBG Interface // NOTE: The function DBG_RESET is not available in production samples #ifdef USE_SCP03 result &= axExCreateAndSetInitialHostScpKeys(scpKeyEncBase, scpKeyMacBase, scpKeyDekBase); result &= axExAuthenticate(scpKeyEncBase, scpKeyMacBase, scpKeyDekBase); // STAGE-1: // - A set of KNOWN (but randon) HOST Static keys has been injected into the SM // - The SCP03 session has been established #endif // Set the configuration keys result &= exWtSetConfigKeys(configKeyModuleLock, configKeyPrivateKey, configKeyPublicKey); // Provision the A71CH result &= exWtProvision(); // Prepare for end-product shipment result &= exWtPrepareShipment(); // Use provisioned credentials result &= exWtUseCrypto(); HOSTCRYPTO_FreeEccKey(&eccKeyRootCA_0); HOSTCRYPTO_FreeEccKey(&eccKeyRootCA_1); // overall result if (result == 1){ LOG_I("-----------End exWalkthrough(%s), result = %s------------", getInitModeAsString(initMode),"OK"); } else { LOG_E("-----------End exWalkthrough(%s), result = %s------------", getInitModeAsString(initMode), "FAILED"); } return result; } /** * Set config keys to enable full walkthrough */ static U8 exWtSetConfigKeys(U8 *moduleConfigKey, U8 *privateKeyConfigKey, U8 *publicKeyConfigKey) { U8 result = 1; U16 err; U8 indexCfgKey = A71CH_CFG_KEY_IDX_MODULE_LOCK; LOG_I("-----------Start exWtSetConfigKeys()------------"); // Store all configure keys (value is stored in variables with module scope) indexCfgKey = A71CH_CFG_KEY_IDX_MODULE_LOCK; LOG_I("A71_SetConfigKey(0x%02x)", indexCfgKey); err = A71_SetConfigKey((SST_Index_t)indexCfgKey, moduleConfigKey, 16); result &= AX_CHECK_SW(err, SW_OK, "err"); axPrintByteArray("configKeyModuleLock", moduleConfigKey, 16, AX_COLON_32); indexCfgKey = A71CH_CFG_KEY_IDX_PRIVATE_KEYS; LOG_I("A71_SetConfigKey(0x%02x)", indexCfgKey); err = A71_SetConfigKey((SST_Index_t)indexCfgKey, privateKeyConfigKey, 16); result &= AX_CHECK_SW(err, SW_OK, "err"); axPrintByteArray("configKeyPrivateKey", privateKeyConfigKey, 16, AX_COLON_32); indexCfgKey = A71CH_CFG_KEY_IDX_PUBLIC_KEYS; LOG_I("A71_SetConfigKey(0x%02x)", indexCfgKey); err = A71_SetConfigKey((SST_Index_t)indexCfgKey, publicKeyConfigKey, 16); result &= AX_CHECK_SW(err, SW_OK, "err"); axPrintByteArray("configKeyPublicKey", publicKeyConfigKey, 16, AX_COLON_32); LOG_I( "-----------End exWtSetConfigKeys(), result = %s------------", ((result == 1)? "OK": "FAILED")); return result; } /** * Demonstrate crypto operations using the provisioned key material */ static U8 exWtUseCrypto() { U8 result = 1; U16 err; SST_Index_t index; HLSE_RET_CODE retcode; int nRet; U8 isOk = 0; int i; HLSE_MECHANISM_INFO mechInfo; U8 symSecret[16]; U8 preCookedSha256[] = { 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, 0xa1, 0xb1, 0xc1, 0xd1, 0xe1, 0xf1 }; U8 dataToSign[] = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x33, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x44, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x11, 0x11, 0x11 }; U8 dataToSignLen = 68; U8 hashSha256[32]; U16 hashSha256Len = sizeof(hashSha256); U8 signature[256]; U16 signatureLen = sizeof(signature); U8 signatureOnHost[256]; U16 signatureOnHostLen = sizeof(signatureOnHost); U32 nSigLen = 0; U8 sharedSecretOnA71CH[32]; U16 sharedSecretOnA71CHLen = 0; U8 sharedSecretOnHost[32]; U16 sharedSecretOnHostLen = 0; U16 expectedSharedSecretLen = sizeof(sharedSecretOnHost); ECCCurve_t eccCurve = ECCCurve_NIST_P256; EC_KEY *eccKeyTls_Host = NULL; eccKeyComponents_t eccKcTls_Host; EC_KEY *eccKeyTls_0 = NULL; // const U16 expectedPubKeyLen = 65; const U16 expectedPrivKeyLen = 32; U8 nBlock; U8 salt[] = {0xAE, 0xBE, 0x11, 0x22}; U16 saltLen = sizeof(salt); U8 info[256]; U16 infoLen = sizeof(info); U8 derivedData[128]; U16 derivedDataLen = sizeof(derivedData); U8 derivedDataOnHost[128]; U8 uid[A71CH_MODULE_UNIQUE_ID_LEN] = {0}; U16 uidLen = sizeof(uid); LOG_I( "-----------Start exWtUseCrypto()------------"); // Sign a precooked hash on the A71CH with the first key pair, do the subsequent verification on the Host // ------------------------------------------------------------------------------------------------------ index = A71CH_KEY_PAIR_0; LOG_I("A71_EccSign(0x%02x) on A71CH.", index); signatureLen = sizeof(signature); err = A71_EccSign(index, preCookedSha256, sizeof(preCookedSha256), signature, &signatureLen); result &= AX_CHECK_SW(err, SW_OK, "err"); axPrintByteArray("signature", signature, signatureLen, AX_COLON_32); // .. now verify on host // First construct an OpenSSL ECC key containing the public key; we // We've already built the eccKcTls_0 data structure while provisioning the A71CH. err = HOSTCRYPTO_EccCreateOpenSslEccFromComponents(&eccKeyTls_0, &eccKcTls_0); result &= AX_CHECK_SW(err, SW_OK, "err"); LOG_I("(Host)ECDSA_verify API with eccKeyTls_0 (verify signature created on A71CH)."); memset(&mechInfo, 0, sizeof(mechInfo)); mechInfo.mechanism = HLSE_ECDSA_VERIFY; retcode = HLCRYPT_Verify(&mechInfo,(U8 *)eccKeyTls_0,0,preCookedSha256,sizeof(preCookedSha256),signature,signatureLen); if (retcode == HLSE_SW_OK) { LOG_I("Verification OK for eccKeyTls_0."); } else { LOG_E("Return value: %d, Verification Not OK for eccKeyTls_0. Test Failed!", retcode); result &= 0; } // Sign data on the host, do the subsequent verification on the A71CH // ------------------------------------------------------------------ // On the Host calculate SHA256 on data, sign the hash using eccKcRootCA_0. hashSha256Len = sizeof(hashSha256); nRet = HOST_SHA256_Get(dataToSign, dataToSignLen, hashSha256); assert(nRet == HOST_CRYPTO_OK); if (nRet != HOST_CRYPTO_OK) { LOG_E("HOST_SHA256_Get operation failed."); result &= 0; } LOG_I("(Host)ECDSA_sign API with eccKeyRootCA_0."); memset(&mechInfo, 0, sizeof(mechInfo)); mechInfo.mechanism = HLSE_ECDSA_SIGN; retcode = HLCRYPT_Sign(&mechInfo, (U8 *)eccKeyRootCA_0, 0,hashSha256, hashSha256Len, signatureOnHost, &nSigLen); LOG_I("ECDSA_sign returned: 0x%02X, Siglen is %ld", retcode, nSigLen); if (retcode != HLSE_SW_OK) { LOG_E("ECDSA_sign operation failed."); result &= 0; } else { axPrintByteArray("signatureOnHost", signatureOnHost, (U16)nSigLen, AX_COLON_32); } // ... do the subsequent verification on the A71CH with the matching public key object. index = A71CH_PUBLIC_KEY_0; LOG_I("A71_EccVerify(0x%02x) on A71CH.", index); signatureOnHostLen = (U16)nSigLen; isOk = 0x00; err = A71_EccVerify(index, hashSha256, sizeof(hashSha256), signatureOnHost, signatureOnHostLen, &isOk); result &= AX_CHECK_SW(err, SW_OK, "err"); result &= AX_CHECK_U8(isOk, 1, "Signature verification failed"); if (isOk == 1) { LOG_I("Verification on A71CH is OK, Test Passed"); } // NEGATIVE TEST: verifying the signature with the wrong public key must fail index = A71CH_PUBLIC_KEY_1; LOG_I("A71_EccVerify(0x%02x) on A71CH.", index); signatureOnHostLen = (U16)nSigLen; isOk = 0x00; err = A71_EccVerify(index, hashSha256, sizeof(hashSha256), signatureOnHost, signatureOnHostLen, &isOk); result &= AX_CHECK_SW(err, SW_OK, "err"); result &= AX_CHECK_U8(isOk, 0, "Negative test: Signature verification must fail"); if (isOk == 0) { LOG_I("Negative Test Passed"); } // Create and compare a shared secret on A71CH and Host // ---------------------------------------------------- // The Second ECC key pair was generated during provisioning, now use it in combination with // eccKeyTls_Host to create a shared secret // First create a new keypair on the host. err = HOSTCRYPTO_GenerateEccKey(eccCurve, &eccKeyTls_Host); result &= AX_CHECK_SW(err, SW_OK, "err"); // Break down the ECC key generated in OpenSSL into eccKeyComponents err = HOSTCRYPTO_GetPublicKey(eccKeyTls_Host, eccKcTls_Host.pub, &(eccKcTls_Host.pubLen), (64 << 1)+1); result &= AX_CHECK_SW(err, SW_OK, "err"); err = HOSTCRYPTO_GetPrivateKey(eccKeyTls_Host, eccKcTls_Host.priv, &(eccKcTls_Host.privLen), 64); result &= AX_CHECK_SW(err, SW_OK, "err"); eccKcTls_Host.bits = expectedPrivKeyLen << 3; eccKcTls_Host.curve = eccCurve; index = A71CH_KEY_PAIR_1; LOG_I("A71_EcdhGetSharedSecret(0x%02x) on A71CH", index); sharedSecretOnA71CHLen = sizeof(sharedSecretOnA71CH); err = A71_EcdhGetSharedSecret(index, eccKcTls_Host.pub, eccKcTls_Host.pubLen, sharedSecretOnA71CH, &sharedSecretOnA71CHLen); result &= AX_CHECK_SW(err, SW_OK, "err"); axPrintByteArray("sharedSecretOnA71CH", sharedSecretOnA71CH, sharedSecretOnA71CHLen, AX_COLON_32); LOG_I("A71_EcdhGetSharedSecret() on Host"); sharedSecretOnHostLen = sizeof(sharedSecretOnHost); err = HOSTCRYPTO_ECC_ComputeSharedSecret(eccKeyTls_Host, eccKcTls_1.pub, eccKcTls_1.pubLen, sharedSecretOnHost, &sharedSecretOnHostLen); result &= AX_CHECK_SW(err, SW_OK, "err"); axPrintByteArray("sharedSecretOnHost", sharedSecretOnHost, sharedSecretOnHostLen, AX_COLON_32); result &= AX_COMPARE_BYTE_ARRAY( "sharedSecretOnA71CH", sharedSecretOnA71CH, expectedSharedSecretLen, "sharedSecretOnHost", sharedSecretOnHost, expectedSharedSecretLen, AX_COLON_32); // Use the stored symmetric secret on index A71CH_SYM_KEY_0 to derive a secret from // -------------------------------------------------------------------------------- // - salt = 0xAEBE1122 // - info = uid | 0x00 0x01 0x02 0x03 0x04 // Fetch UID from A71CH err = A71_GetUniqueID(uid, &uidLen); result &= AX_CHECK_SW(err, SW_OK, "err"); memcpy(info, uid, uidLen); for (i=0; i<5; i++) { info[uidLen+i] = (U8)i; } infoLen = uidLen + 5; nBlock = 1; derivedDataLen = 16; LOG_I("A71_HkdfSymKey() on Host"); err = A71_HkdfSymKey(A71CH_SYM_KEY_0, nBlock, salt, saltLen, info, infoLen, derivedData, derivedDataLen); result &= AX_CHECK_SW(err, SW_OK, "err"); axPrintByteArray("derivedData from A71CH", derivedData, derivedDataLen, AX_COLON_32); // Assuming e.g. the backend knows the shared secret (the stored symmetric secret) it can // calculate the same derived data and use this memcpy(symSecret, aesBasePattern, 16); symSecret[0] = 0x00; LOG_I("HOSTCRYPTO_HkdfFullSha256(KeyLen=%d, infoLen=%d, derivedDataLen=%d)", 16, infoLen, derivedDataLen); err = HOSTCRYPTO_HkdfFullSha256(salt, saltLen, symSecret, 16, info, infoLen, derivedDataOnHost, derivedDataLen); result &= AX_CHECK_SW(err, SW_OK, "err"); result &= AX_COMPARE_BYTE_ARRAY( "derivedData from A71CH", derivedData, derivedDataLen, "derivedData calculated outside secure module", derivedDataOnHost, derivedDataLen, AX_COLON_32); HOSTCRYPTO_FreeEccKey(&eccKeyTls_Host); HOSTCRYPTO_FreeEccKey(&eccKeyTls_0); LOG_I( "-----------End exWtUseCrypto(), result = %s------------", ((result == 1)? "OK": "FAILED")); return result; } /** * Provision the A71CH with key material */ static U8 exWtProvision() { U8 result = 1; U16 err = 0; ECCCurve_t eccCurve = ECCCurve_NIST_P256; int indexAesKey = 0; const U16 expectedPubKeyLen = 65; const U16 expectedPrivKeyLen = 32; SST_Index_t index; U8 uid[A71CH_MODULE_UNIQUE_ID_LEN] = {0}; U16 uidLen = sizeof(uid); LOG_I( "-----------Start exWtProvision()------------"); // In a typical provisioning scenario the keypair matching the public key // to be inserted would be created and stored securely on a back-end system. // It's also possible to create the keypairs to be contained in the A71CH externally // and to insert both private and public key during the provisioning phase. In this // example the keypairs are generated on the A71CH itself and the private key never // leaves the A71CH. err = HOSTCRYPTO_GenerateEccKey(eccCurve, &eccKeyRootCA_0); result &= AX_CHECK_SW(err, SW_OK, "err"); err = HOSTCRYPTO_GenerateEccKey(eccCurve, &eccKeyRootCA_1); result &= AX_CHECK_SW(err, SW_OK, "err"); err = HOSTCRYPTO_GetPublicKey(eccKeyRootCA_0, eccKcRootCA_0.pub, &(eccKcRootCA_0.pubLen), (64 << 1)+1); result &= AX_CHECK_SW(err, SW_OK, "err"); err = HOSTCRYPTO_GetPrivateKey(eccKeyRootCA_0, eccKcRootCA_0.priv, &(eccKcRootCA_0.privLen), 64); result &= AX_CHECK_SW(err, SW_OK, "err"); eccKcRootCA_0.bits = expectedPrivKeyLen << 3; eccKcRootCA_0.curve = eccCurve; err = HOSTCRYPTO_GetPublicKey(eccKeyRootCA_1, eccKcRootCA_1.pub, &(eccKcRootCA_1.pubLen), (64 << 1)+1); result &= AX_CHECK_SW(err, SW_OK, "err"); err = HOSTCRYPTO_GetPrivateKey(eccKeyRootCA_1, eccKcRootCA_1.priv, &(eccKcRootCA_1.privLen), 64); result &= AX_CHECK_SW(err, SW_OK, "err"); eccKcRootCA_1.bits = expectedPrivKeyLen << 3; eccKcRootCA_1.curve = eccCurve; // Generate the first ECC keyPair (Extract the public key) index = A71CH_KEY_PAIR_0; LOG_I("A71_GenerateEccKeyPair(0x%02x)", index); err = A71_GenerateEccKeyPair(index); result &= AX_CHECK_SW(err, SW_OK, "err"); // Generate the second ECC keyPair (Extract the public key) index = A71CH_KEY_PAIR_1; LOG_I("A71_GenerateEccKeyPair(0x%02x)", index); err = A71_GenerateEccKeyPair(index); result &= AX_CHECK_SW(err, SW_OK, "err"); // Both ECC Key pairs have been created, now extract the value of the public key index = A71CH_KEY_PAIR_0; LOG_I( "SST_GetPublicKeyECCKeyPair(0x%02x)", index); eccKcTls_0.pubLen = sizeof(eccKcTls_0.pub); err = A71_GetPublicKeyEccKeyPair(index, eccKcTls_0.pub, &(eccKcTls_0.pubLen)); result &= AX_CHECK_SW(err, SW_OK, "err"); result &= AX_CHECK_U16(eccKcTls_0.pubLen, expectedPubKeyLen, "Wrong public key length"); // Complete eccKcTls_0 datastructure if (err == SW_OK) { eccKcTls_0.bits = 256; eccKcTls_0.curve = eccCurve; eccKcTls_0.privLen = 0; } index = A71CH_KEY_PAIR_1; LOG_I( "SST_GetPublicKeyECCKeyPair(0x%02x)", index); eccKcTls_1.pubLen = sizeof(eccKcTls_1.pub); err = A71_GetPublicKeyEccKeyPair(index, eccKcTls_1.pub, &(eccKcTls_1.pubLen)); result &= AX_CHECK_SW(err, SW_OK, "err"); result &= AX_CHECK_U16(eccKcTls_1.pubLen, expectedPubKeyLen, "Wrong public key length"); // Complete eccKcTls_1 datastructure if (err == SW_OK) { eccKcTls_1.bits = 256; eccKcTls_1.curve = eccCurve; eccKcTls_1.privLen = 0; } // Set the public key of eccKcRootCA_0 on the A71CH (index=0) index = A71CH_PUBLIC_KEY_0; LOG_I("A71_SetEccPublicKey(0x%02x)", index); err = A71_SetEccPublicKey(index, eccKcRootCA_0.pub, eccKcRootCA_0.pubLen); result &= AX_CHECK_SW(err, SW_OK, "err"); // Set the public key of eccKcRootCA_1 on the A71CH (index=1) index = A71CH_PUBLIC_KEY_1; LOG_I("A71_SetEccPublicKey(0x%02x)", index); err = A71_SetEccPublicKey(index, eccKcRootCA_1.pub, eccKcRootCA_1.pubLen); result &= AX_CHECK_SW(err, SW_OK, "err"); // Fill the SYM key store with A71CH_SYM_KEY_MAX keys (128 bits long) // The keys being written are based upon a Base pattern, with the first key written having a 0x00 byte // in position 0, the second key written a 0x00 byte in position 1 and so on // Base Pattern: 0xF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF // First Key : 0x00F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF // Second Key : 0xF000F2F3F4F5F6F7F8F9FAFBFCFDFEFF // .. for (indexAesKey=0; indexAesKey