/* * @author NXP Semiconductors * @version 1.0 * @par License * * Copyright 2016,2020 NXP * SPDX-License-Identifier: Apache-2.0 * * @note Execution flow and Error messages can be sent to the console by defining * FLOW_VERBOSE and ERROR_VERBOSE respectively at the start of the source code file. * @par History * 1.0 26-march-2014 : Initial version * */ /** * @file ax_scp.c * @par Description * Set up the SCP03 communication channel. */ #include #include #include #include "ax_api.h" #include "sm_apdu.h" #include "sm_errors.h" //#include "axHostCrypto.h" #include "HostCryptoAPI.h" #include "ax_util.h" #include "global_platf.h" #include "nxLog_scp.h" #include /// @cond static ScpState_t scpState[2]; /* * It is implicitly expected that ``keyEnc``, ``keyMac`` & ``keyDek`` points to a buffer * that holds an AES128 Key for SCP of appropriate length. * * ``mcv`` points to a buffer of 16 bytes. And similarly buffers and sizes as specified * by the Global Platform Specification for SCP03. * * Any violation to this rule would lead to nondeterministic * behaviour of the system. */ U16 SCP_HostLocal_SetDefaultValueIcvCCounter(ChannelId_t channelId) { U8 commandCounter[16] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01}; int stateIdx = 0; switch (channelId) { case AX_HOST_CHANNEL: stateIdx = HOST_CHANNEL_STATE_IDX; break; case AX_ADMIN_CHANNEL: stateIdx = ADMIN_CHANNEL_STATE_IDX; break; default: return SCP_UNDEFINED_CHANNEL_ID; } memcpy(scpState[stateIdx].session.cCounter, commandCounter, 16); return SCP_OK; } // No check on overflow U16 SCP_HostLocal_IncIcvCCounter(ChannelId_t channelId) { int stateIdx = 0; int i = 15; switch (channelId) { case AX_HOST_CHANNEL: stateIdx = HOST_CHANNEL_STATE_IDX; break; case AX_ADMIN_CHANNEL: stateIdx = ADMIN_CHANNEL_STATE_IDX; break; default: return SCP_UNDEFINED_CHANNEL_ID; } i = 15; while (i > 0) { if (scpState[stateIdx].session.cCounter[i] < 255) { scpState[stateIdx].session.cCounter[i] += 1; break; } else { scpState[stateIdx].session.cCounter[i] = 0; i--; } } return SCP_OK; } U16 SCP_HostLocal_SetKeysScp(ChannelId_t channelId, U8 *keyEnc, U8 *keyMac, U8 *keyDek, U16 keyBytes) { int stateIdx = 0; U16 ret = SCP_PARAMETER_ERROR; // Only 128 bit AES or 16-byte DES keys are supported ENSURE_OR_GO_EXIT(keyBytes == SCP_KEY_SIZE); switch (channelId) { case AX_HOST_CHANNEL: stateIdx = HOST_CHANNEL_STATE_IDX; break; case AX_ADMIN_CHANNEL: stateIdx = ADMIN_CHANNEL_STATE_IDX; break; default: ret = SCP_UNDEFINED_CHANNEL_ID; } if (ret != SCP_UNDEFINED_CHANNEL_ID) { memcpy(scpState[stateIdx].keyEnc, keyEnc, keyBytes); memcpy(scpState[stateIdx].keyMac, keyMac, keyBytes); memcpy(scpState[stateIdx].keyDek, keyDek, keyBytes); ret = SCP_OK; } exit: return ret; } U16 SCP_HostLocal_GetKeyDek(ChannelId_t channelId, U8 *keyDek) { int stateIdx = 0; switch (channelId) { case AX_HOST_CHANNEL: stateIdx = HOST_CHANNEL_STATE_IDX; break; case AX_ADMIN_CHANNEL: stateIdx = ADMIN_CHANNEL_STATE_IDX; break; default: return SCP_UNDEFINED_CHANNEL_ID; } memcpy(keyDek, scpState[stateIdx].keyDek, AES_KEY_LEN_nBYTE); return SCP_OK; } U16 SCP_HostLocal_GetKeyEnc(ChannelId_t channelId, U8 *keyEnc) { int stateIdx = 0; switch (channelId) { case AX_HOST_CHANNEL: stateIdx = HOST_CHANNEL_STATE_IDX; break; case AX_ADMIN_CHANNEL: stateIdx = ADMIN_CHANNEL_STATE_IDX; break; default: return SCP_UNDEFINED_CHANNEL_ID; } memcpy(keyEnc, scpState[stateIdx].keyEnc, AES_KEY_LEN_nBYTE); return SCP_OK; } U16 SCP_HostLocal_GetKeyMac(ChannelId_t channelId, U8 *keyMac) { int stateIdx = 0; switch (channelId) { case AX_HOST_CHANNEL: stateIdx = HOST_CHANNEL_STATE_IDX; break; case AX_ADMIN_CHANNEL: stateIdx = ADMIN_CHANNEL_STATE_IDX; break; default: return SCP_UNDEFINED_CHANNEL_ID; } memcpy(keyMac, scpState[stateIdx].keyMac, AES_KEY_LEN_nBYTE); return SCP_OK; } U16 SCP_HostLocal_ResetMacChainingValue(ChannelId_t channelId) { int stateIdx = 0; switch (channelId) { case AX_HOST_CHANNEL: stateIdx = HOST_CHANNEL_STATE_IDX; break; case AX_ADMIN_CHANNEL: stateIdx = ADMIN_CHANNEL_STATE_IDX; break; default: return SCP_UNDEFINED_CHANNEL_ID; } memset(scpState[stateIdx].session.mcv, 0, SCP_MCV_LEN); return SCP_OK; } U16 SCP_HostLocal_SetMacChainingValue(ChannelId_t channelId, U8 *mcv) { int stateIdx = 0; switch (channelId) { case AX_HOST_CHANNEL: stateIdx = HOST_CHANNEL_STATE_IDX; break; case AX_ADMIN_CHANNEL: stateIdx = ADMIN_CHANNEL_STATE_IDX; break; default: return SCP_UNDEFINED_CHANNEL_ID; } memcpy(scpState[stateIdx].session.mcv, mcv, SCP_MCV_LEN); return SCP_OK; } /// @endcond /** * Copy the session state into \p pSession. Caller must allocate memory of \p pSession. * @param[in] channelId Either ::AX_HOST_CHANNEL or ::AX_ADMIN_CHANNEL. Must be ::AX_HOST_CHANNEL in case of A71CH. * @param[in,out] pSession IN: pointer to allocated ::Scp03SessionState_t structure; OUT: retrieved state * @retval ::SW_OK Upon successful execution * @retval ::SCP_UNDEFINED_CHANNEL_ID In case an undefined ::ChannelId_t type was passed as parameter */ U16 SCP_HostLocal_GetSessionState(ChannelId_t channelId, Scp03SessionState_t *pSession) { int stateIdx = 0; switch (channelId) { case AX_HOST_CHANNEL: stateIdx = HOST_CHANNEL_STATE_IDX; break; case AX_ADMIN_CHANNEL: stateIdx = ADMIN_CHANNEL_STATE_IDX; break; default: return SCP_UNDEFINED_CHANNEL_ID; } memcpy(pSession, &(scpState[stateIdx].session), sizeof(Scp03SessionState_t)); return SCP_OK; } /// @cond U16 SCP_HostLocal_CalculateSessionKeys(ChannelId_t channelId, U8 *hostChallenge, U8 *cardChallenge) { int stateIdx = 0; U8 ddA[128]; U16 ddALen = sizeof(ddA); U8 context[128]; U16 contextLen = 0; U8 sessionEncKey[AES_KEY_LEN_nBYTE]; U8 sessionMacKey[AES_KEY_LEN_nBYTE]; U8 sessionRmacKey[AES_KEY_LEN_nBYTE]; U8 masterEncKey[AES_KEY_LEN_nBYTE]; U8 masterMacKey[AES_KEY_LEN_nBYTE]; HLSE_MECHANISM_INFO mechInfo; U32 signatureLen = sizeof(sessionMacKey); S32 ret; memset(&mechInfo, 0, sizeof(mechInfo)); mechInfo.mechanism = HLSE_AES_CMAC; switch (channelId) { case AX_HOST_CHANNEL: stateIdx = HOST_CHANNEL_STATE_IDX; break; case AX_ADMIN_CHANNEL: stateIdx = ADMIN_CHANNEL_STATE_IDX; break; default: return SCP_UNDEFINED_CHANNEL_ID; } LOG_I("HOST: Calculate session keys"); // Calculate the S-ENC key memcpy(context, hostChallenge, SCP_GP_HOST_CHALLENGE_LEN); memcpy(&context[SCP_GP_HOST_CHALLENGE_LEN], cardChallenge, SCP_GP_CARD_CHALLENGE_LEN); contextLen = SCP_GP_HOST_CHALLENGE_LEN + SCP_GP_CARD_CHALLENGE_LEN; DEV_setDataDerivationArray(ddA, &ddALen, DATA_DERIVATION_SENC, DATA_DERIVATION_L_128BIT, DATA_DERIVATION_KDF_CTR, context, contextLen); SCP_HostLocal_GetKeyEnc(channelId, masterEncKey); ret = HLCRYPT_Sign(&mechInfo, masterEncKey, AES_KEY_LEN_nBYTE, ddA, ddALen, sessionEncKey, &signatureLen); if (ret != HOST_CRYPTO_OK) { return ERR_CRYPTO_ENGINE_FAILED; } LOG_MAU8_D("sessionEncKey", sessionEncKey, AES_KEY_LEN_nBYTE); // Calculate the S-MAC key SCP_HostLocal_GetKeyMac(channelId, masterMacKey); DEV_setDataDerivationArray(ddA, &ddALen, DATA_DERIVATION_SMAC, DATA_DERIVATION_L_128BIT, DATA_DERIVATION_KDF_CTR, context, contextLen); ret = HLCRYPT_Sign(&mechInfo, masterMacKey, AES_KEY_LEN_nBYTE, ddA, ddALen, sessionMacKey, &signatureLen); if (ret != HOST_CRYPTO_OK) { return ERR_CRYPTO_ENGINE_FAILED; } LOG_MAU8_D("sessionMacKey", sessionMacKey, AES_KEY_LEN_nBYTE); // Calculate the S-RMAC key DEV_setDataDerivationArray(ddA, &ddALen, DATA_DERIVATION_SRMAC, DATA_DERIVATION_L_128BIT, DATA_DERIVATION_KDF_CTR, context, contextLen); ret = HLCRYPT_Sign(&mechInfo, masterMacKey, AES_KEY_LEN_nBYTE, ddA, ddALen, sessionRmacKey, &signatureLen); if (ret != HOST_CRYPTO_OK) { return ERR_CRYPTO_ENGINE_FAILED; } LOG_MAU8_D("sessionRmacKey", sessionRmacKey, AES_KEY_LEN_nBYTE); // Store the Session Keys in the appropriate Channel Session State memcpy(scpState[stateIdx].session.sEnc, sessionEncKey, AES_KEY_LEN_nBYTE); memcpy(scpState[stateIdx].session.sMac, sessionMacKey, AES_KEY_LEN_nBYTE); memcpy(scpState[stateIdx].session.sRMac, sessionRmacKey, AES_KEY_LEN_nBYTE); return SCP_OK; } U16 SCP_HostLocal_CalculateHostCryptogram(ChannelId_t channelId, U8 *hostChallenge, U8 *cardChallenge, U8 *hostCryptogram) { int stateIdx = 0; U8 ddA[128]; U16 ddALen = sizeof(ddA); U8 context[128]; U16 contextLen = 0; U8 sessionMacKey[AES_KEY_LEN_nBYTE]; U8 hostCryptogramFullLength[AES_KEY_LEN_nBYTE]; HLSE_MECHANISM_INFO mechInfo; U32 signatureLen = sizeof(hostCryptogramFullLength); S32 ret; memset(&mechInfo, 0, sizeof(mechInfo)); mechInfo.mechanism = HLSE_AES_CMAC; switch (channelId) { case AX_HOST_CHANNEL: stateIdx = HOST_CHANNEL_STATE_IDX; break; case AX_ADMIN_CHANNEL: stateIdx = ADMIN_CHANNEL_STATE_IDX; break; default: return SCP_UNDEFINED_CHANNEL_ID; } LOG_I("HOST: Calculate Host Cryptogram"); memcpy(context, hostChallenge, SCP_GP_HOST_CHALLENGE_LEN); memcpy(&context[SCP_GP_HOST_CHALLENGE_LEN], cardChallenge, SCP_GP_CARD_CHALLENGE_LEN); contextLen = SCP_GP_HOST_CHALLENGE_LEN + SCP_GP_CARD_CHALLENGE_LEN; DEV_setDataDerivationArray(ddA, &ddALen, DATA_HOST_CRYPTOGRAM, DATA_DERIVATION_L_64BIT, DATA_DERIVATION_KDF_CTR, context, contextLen); memcpy(sessionMacKey, &(scpState[stateIdx].session.sMac), AES_KEY_LEN_nBYTE); ret = HLCRYPT_Sign(&mechInfo, sessionMacKey, AES_KEY_LEN_nBYTE, ddA, ddALen, hostCryptogramFullLength, &signatureLen); if (ret != HOST_CRYPTO_OK) { return ERR_CRYPTO_ENGINE_FAILED; } // Chop of the tail of the hostCryptogramFullLength memcpy(hostCryptogram, hostCryptogramFullLength, SCP_GP_IU_CARD_CRYPTOGRAM_LEN); // PRINT_BYTE_STRING("hostCryptogram", hostCryptogram, SCP_GP_IU_CARD_CRYPTOGRAM_LEN); return SCP_OK; } U16 SCP_HostLocal_VerifyCardCryptogram(ChannelId_t channelId, U8 *hostChallenge, U8 *cardChallenge, U8 *cardCryptogram) { int stateIdx = 0; U8 ddA[128]; U16 ddALen = sizeof(ddA); U8 context[128]; U16 contextLen = 0; U8 sessionMacKey[AES_KEY_LEN_nBYTE]; U8 cardCryptogramFullLength[AES_KEY_LEN_nBYTE]; U16 rv = SCP_OK; HLSE_MECHANISM_INFO mechInfo; U32 signatureLen = sizeof(cardCryptogramFullLength); S32 ret; memset(&mechInfo, 0, sizeof(mechInfo)); mechInfo.mechanism = HLSE_AES_CMAC; switch (channelId) { case AX_HOST_CHANNEL: stateIdx = HOST_CHANNEL_STATE_IDX; break; case AX_ADMIN_CHANNEL: stateIdx = ADMIN_CHANNEL_STATE_IDX; break; default: return SCP_UNDEFINED_CHANNEL_ID; } LOG_I("HOST: Verify Card Cryptogram"); memcpy(context, hostChallenge, SCP_GP_HOST_CHALLENGE_LEN); memcpy(&context[SCP_GP_HOST_CHALLENGE_LEN], cardChallenge, SCP_GP_CARD_CHALLENGE_LEN); contextLen = SCP_GP_HOST_CHALLENGE_LEN + SCP_GP_CARD_CHALLENGE_LEN; DEV_setDataDerivationArray(ddA, &ddALen, DATA_CARD_CRYPTOGRAM, DATA_DERIVATION_L_64BIT, DATA_DERIVATION_KDF_CTR, context, contextLen); memcpy(sessionMacKey, &(scpState[stateIdx].session.sMac), AES_KEY_LEN_nBYTE); ret = HLCRYPT_Sign(&mechInfo, sessionMacKey, AES_KEY_LEN_nBYTE, ddA, ddALen, cardCryptogramFullLength, &signatureLen); if (ret != HOST_CRYPTO_OK) { return ERR_CRYPTO_ENGINE_FAILED; } LOG_MAU8_D("cardCryptogramFullLength - Verify", cardCryptogramFullLength, AES_KEY_LEN_nBYTE); // Verify whether the 8 left most byte of cardCryptogramFullLength match cardCryptogram if (memcmp(cardCryptogramFullLength, cardCryptogram, SCP_GP_IU_CARD_CRYPTOGRAM_LEN) != 0) { rv = SCP_CARD_CRYPTOGRAM_FAILS_TO_VERIFY; } return rv; } /// @endcond /** * Retrieve the SCP03 session state of the host - secure module channel from the Host Library. * * @param[in,out] scp03state IN: pointer to allocated structure; OUT: datastructure contains SCP03 session state * @returns ::SW_OK */ U16 SCP_GetScpSessionState(Scp03SessionState_t *scp03state) { U16 status = SCP_PARAMETER_ERROR; scp_CommandType_t channelCommandTypeDummy; ChannelId_t channelId; ENSURE_OR_GO_EXIT(scp03state); channelId = DEV_GetSelectedChannel(&channelCommandTypeDummy); SCP_HostLocal_GetSessionState(channelId, scp03state); status = SW_OK; exit: return (U16)status; } /** * Sets SCP03 session state of the host - secure module channel of the Host Library. * Can be used in a scenario where e.g. the bootloader has established the SCP03 link between * host and secure module and the Host OS must re-establish the communication with the * secure module without breaking the SCP03 session. * * @param[in] scp03state IN: SCP03 session state */ void SCP_SetScpSessionState(Scp03SessionState_t *scp03state) { memcpy(&(scpState[HOST_CHANNEL_STATE_IDX].session), scp03state, sizeof(Scp03SessionState_t)); } U16 SCP_GP_ExternalAuthenticate(ChannelId_t channelId, U8* hostCryptogram) { S32 nRet; U32 st = 0; U8 txBuf[128]; U8 cla = 0; U8 response[128]; U32 responseLen = 128; U16 rv = 0; U8 sessionMacKey[AES_KEY_LEN_nBYTE]; U8 mcv[AES_KEY_LEN_nBYTE]; U8 macToAdd[AES_KEY_LEN_nBYTE] = {0}; int stateIdx = ADMIN_CHANNEL_STATE_IDX; //axHcCmacCtx_t *cmacCtx; HLSE_CONTEXT_HANDLE hContext; HLSE_MECHANISM_INFO mechInfo; U32 signatureLen = sizeof(macToAdd); memset(&mechInfo, 0, sizeof(mechInfo)); mechInfo.mechanism = HLSE_AES_CMAC; LOG_I(">> %s: Enter", "SCP_GP_ExternalAuthenticate"); switch (channelId) { case AX_HOST_CHANNEL: stateIdx = HOST_CHANNEL_STATE_IDX; break; case AX_ADMIN_CHANNEL: stateIdx = ADMIN_CHANNEL_STATE_IDX; break; default: return SCP_UNDEFINED_CHANNEL_ID; } cla = 0x84; txBuf[0] = cla; txBuf[1] = INS_GP_EXTERNAL_AUTHENTICATE; txBuf[2] = SECLVL_CDEC_RENC_CMAC_RMAC; txBuf[3] = 0x00; txBuf[4] = 0x10; // The Lc valus is set as-if the MAC has already been appended (SCP03 spec p16. Fig.6-1) memcpy(&txBuf[5], hostCryptogram, SCP_GP_IU_CARD_CRYPTOGRAM_LEN); // Calculate the MAC value memcpy(sessionMacKey, scpState[stateIdx].session.sMac, AES_KEY_LEN_nBYTE); LOG_MAU8_D(">> sessionMacKey", sessionMacKey, AES_KEY_LEN_nBYTE); nRet = HLCRYPT_SignInit(&mechInfo, sessionMacKey, AES_KEY_LEN_nBYTE, &hContext); if (nRet != HOST_CRYPTO_OK) { return ERR_CRYPTO_ENGINE_FAILED; } /* * For the EXTERNAL AUTHENTICATE command MAC verification, the "MAC chaining value" is set to 16 * bytes '00'. (SCP03 spec p16) */ SCP_HostLocal_ResetMacChainingValue(channelId); memcpy(mcv, scpState[stateIdx].session.mcv, AES_KEY_LEN_nBYTE); nRet = HLCRYPT_SignUpdate(hContext, mcv, AES_KEY_LEN_nBYTE); // nRet = HOST_CMAC_Update(cmacCtx, mcv, AES_KEY_LEN_nBYTE); nRet &= HLCRYPT_SignUpdate(hContext, txBuf, 13); // nRet &= HOST_CMAC_Update(cmacCtx, txBuf, 13); nRet &= HLCRYPT_SignFinal(hContext, macToAdd, &signatureLen); // nRet &= HOST_CMAC_Finish(cmacCtx, macToAdd); if (nRet != HOST_CRYPTO_OK) { return ERR_CRYPTO_ENGINE_FAILED; } memcpy(scpState[stateIdx].session.mcv, macToAdd, AES_KEY_LEN_nBYTE); memcpy(&txBuf[5 + SCP_GP_IU_CARD_CRYPTOGRAM_LEN], macToAdd, SCP_GP_IU_CARD_CRYPTOGRAM_LEN); LOG_MAU8_D("sessionMacKey", sessionMacKey, 16); LOG_MAU8_D("mcv", mcv, 16); LOG_MAU8_D("txBuf", txBuf, 13); LOG_MAU8_D("macToAdd", macToAdd, 16); #ifdef TGT_EDEV if (channelId == AX_HOST_CHANNEL) { // Modify the CLA byte to tag this channel as a Host Channel cla = 0xE4; } txBuf[0] = cla; #endif st = smCom_TransceiveRaw(NULL, (U8*)txBuf, 5 + AES_KEY_LEN_nBYTE, response, &responseLen); if (st != SMCOM_OK) { LOG_E("SCP_GP_ExternalAuthenticate %lX", st); rv = ERR_GENERAL_ERROR; } else { rv = CheckNoResponseDataRaw(response, (U16)responseLen); } return rv; } U16 SCP_GP_InitializeUpdate(ChannelId_t channelId, U8 *hostChallenge, U16 hostChallengeLen, U8 *keyDivData, U16 *pKeyDivDataLen, U8 *keyInfo, U16 *pKeyInfoLen, U8 *cardChallenge, U16 *pCardChallengeLen, U8 *cardCryptoGram, U16 *pCardCryptoGramLen, U8 *seqCounter, U16 *pSeqCounterLen) { U32 st = 0; U8 txBuf[128]; U8 cla = 0; U8 keyVersion = 0; U8 response[128]; U32 responseLen = 128; U16 parsePos = 0; U16 sw = SCP_FAIL; U32 iuResponseLenSmall = SCP_GP_IU_KEY_DIV_DATA_LEN + SCP_GP_IU_KEY_INFO_LEN + SCP_GP_CARD_CHALLENGE_LEN + SCP_GP_IU_CARD_CRYPTOGRAM_LEN + SCP_GP_SW_LEN; U32 iuResponseLenBig = SCP_GP_IU_KEY_DIV_DATA_LEN + SCP_GP_IU_KEY_INFO_LEN + SCP_GP_CARD_CHALLENGE_LEN + SCP_GP_IU_CARD_CRYPTOGRAM_LEN + SCP_GP_IU_SEQ_COUNTER_LEN + SCP_GP_SW_LEN; LOG_I(">> %s: Enter", "SCP_GP_InitializeUpdate"); ENSURE_OR_GO_EXIT(hostChallengeLen == SCP_GP_HOST_CHALLENGE_LEN); ENSURE_OR_GO_EXIT(*pKeyDivDataLen == SCP_GP_IU_KEY_DIV_DATA_LEN); ENSURE_OR_GO_EXIT(*pKeyInfoLen == SCP_GP_IU_KEY_INFO_LEN); ENSURE_OR_GO_EXIT(*pCardChallengeLen == SCP_GP_CARD_CHALLENGE_LEN); ENSURE_OR_GO_EXIT(*pCardCryptoGramLen == SCP_GP_IU_CARD_CRYPTOGRAM_LEN); switch (channelId) { case AX_HOST_CHANNEL: #ifdef TGT_EDEV keyVersion = (U8)SST_HOST_SCP_KEYSET; cla = 0xE0; #else keyVersion = (U8)(SST_HOST_SCP_KEYSET >> 8); cla = 0x80; #endif break; case AX_ADMIN_CHANNEL: keyVersion = (U8)0x00; // Just use default key version cla = 0x80; break; default: return SCP_UNDEFINED_CHANNEL_ID; } txBuf[0] = cla; txBuf[1] = INS_GP_INITIALIZE_UPDATE; txBuf[2] = keyVersion; #ifdef TGT_EDEV txBuf[3] = SCP03_KEY_ID; #else txBuf[3] = 0x00; // InitializeUpdate as implemented in Applet 1.6 still expects an identifier equal to 0. #endif txBuf[4] = (U8)hostChallengeLen; // (GP.p205 Table D-4) memcpy(&txBuf[5], hostChallenge, hostChallengeLen); txBuf[5 + hostChallengeLen] = 0x00; st = smCom_TransceiveRaw(NULL, (U8*)txBuf, 6 + hostChallengeLen, response, &responseLen); if (st != SMCOM_OK) { LOG_E("SCP_GP_InitializeUpdate. Failure on communication Link (0x%04lX)", st); return (U16)st; } // Parse Response // The expected result length depends on random (HOST-Channel) or pseudo-random (ADMIN-Channel) challenge type. // The pseudo-random challenge case also includes a 3 byte sequence counter if ((responseLen != iuResponseLenSmall) && (responseLen != iuResponseLenBig)) { // Note: A response of length 2 (a proper SW) is also collapsed into return code SCP_FAIL LOG_E("Unexpected amount of data returned: %ld", responseLen); return SCP_FAIL; } memcpy(keyDivData, response, SCP_GP_IU_KEY_DIV_DATA_LEN); parsePos = SCP_GP_IU_KEY_DIV_DATA_LEN; memcpy(keyInfo, &(response[parsePos]), SCP_GP_IU_KEY_INFO_LEN); parsePos += SCP_GP_IU_KEY_INFO_LEN; memcpy(cardChallenge, &(response[parsePos]), SCP_GP_CARD_CHALLENGE_LEN); parsePos += SCP_GP_CARD_CHALLENGE_LEN; memcpy(cardCryptoGram, &(response[parsePos]), SCP_GP_IU_CARD_CRYPTOGRAM_LEN); parsePos += SCP_GP_IU_CARD_CRYPTOGRAM_LEN; if (responseLen == iuResponseLenBig) { memcpy(seqCounter, &(response[parsePos]), SCP_GP_IU_SEQ_COUNTER_LEN); *pSeqCounterLen = SCP_GP_IU_SEQ_COUNTER_LEN; } else { memset(seqCounter, 0, SCP_GP_IU_SEQ_COUNTER_LEN); *pSeqCounterLen = 0; } // Construct Return Value sw = (response[responseLen - 2] << 8) + response[responseLen - 1]; exit: return sw; } /// @cond /** * Utility function used by ::SCP_GP_PutKeys * @param[in] keyType * @param[in] key Pointer to the key @param[in] currentKeyDek Pointer to the current Dek key, in case the pointer is NULL no encryption will be applied (deviates from GP spec to enable initial provisioning) * @param[in,out] targetStore * @param[in,out] nCryptoStatus * @return Length */ static U8 createKeyDataField(U8 keyType, U8 *key, U8 *currentKeyDek, U8 *targetStore, S32 *nCryptoStatus) { U8 refOneArray[AES_KEY_LEN_nBYTE] = { 0 }; U8 refOneArrayCiphered[AES_KEY_LEN_nBYTE] = { 0 }; U8 encKey[AES_KEY_LEN_nBYTE]; if (currentKeyDek == NULL) { memcpy(encKey, key, AES_KEY_LEN_nBYTE); *nCryptoStatus = HOST_CRYPTO_OK; } else { HLSE_MECHANISM_INFO mechInfo; U32 outLen = sizeof(encKey); memset(&mechInfo, 0, sizeof(mechInfo)); mechInfo.mechanism = HLSE_AES_ECB_ENCRYPT; *nCryptoStatus = HLCRYPT_Encrypt(&mechInfo, currentKeyDek, AES_KEY_LEN_nBYTE, key, AES_KEY_LEN_nBYTE, encKey, &outLen); } targetStore[0] = keyType; targetStore[1] = AES_KEY_LEN_nBYTE + 1; // Length of the 'AES key data' targetStore[2] = AES_KEY_LEN_nBYTE; // Length of 'AES key' memcpy(&targetStore[3], encKey, AES_KEY_LEN_nBYTE); targetStore[3 + AES_KEY_LEN_nBYTE] = CRYPTO_KEY_CHECK_LEN; memset(refOneArray, 1, sizeof(refOneArray)); if (*nCryptoStatus == HOST_CRYPTO_OK) { HLSE_MECHANISM_INFO mechInfo; U32 outLen = sizeof(refOneArrayCiphered); memset(&mechInfo, 0, sizeof(mechInfo)); mechInfo.mechanism = HLSE_AES_ECB_ENCRYPT; *nCryptoStatus = HLCRYPT_Encrypt(&mechInfo, key, AES_KEY_LEN_nBYTE, refOneArray, AES_KEY_LEN_nBYTE, refOneArrayCiphered, &outLen); } // Append key check value memcpy(&targetStore[3 + AES_KEY_LEN_nBYTE + 1], &refOneArrayCiphered[0], CRYPTO_KEY_CHECK_LEN); return (3 + AES_KEY_LEN_nBYTE + 1 + CRYPTO_KEY_CHECK_LEN); } /// @endcond /** * Persistently stores the provided SCP03 base key set in the security module. * * This method must be called once before the Host - Secure Module SCP channel can be established. * * @param[in] keyVersion * @param[in] keyEnc SCP03 channel encryption base key * @param[in] keyMac SCP03 authentication base key * @param[in] keyDek SCP03 data encryption base key * @param[in] currentKeyDek Value of the data encryption base key already stored in secure module, may be NULL in case no key is currently stored. * @param[in] keyBytes Length (in byte) of the keys being set. Typically 16 (corresponding to 128 bits) * @returns ::SW_OK upon success */ U16 SCP_GP_PutKeys(U8 keyVersion, U8 *keyEnc, U8 *keyMac, U8 *keyDek, U8 *currentKeyDek, U16 keyBytes) { U32 st = 0; S32 cryptoStatus = HOST_CRYPTO_ERROR; U8 txBuf[128]; U8 len = 0; U8 response[128]; U32 responseLen = 128; U16 rv = SCP_PARAMETER_ERROR; ENSURE_OR_GO_EXIT(keyBytes == AES_KEY_LEN_nBYTE); #ifdef TGT_EDEV txBuf[0] = (keyVersion == SST_ADMIN_SCP_KEYSET) ? 0x80 : 0xE0; #else txBuf[0] = 0x80; #endif txBuf[1] = INS_GP_PUT_KEY; // For A71CH/CL the SCP03 keys can only be set once txBuf[2] = 0x00; txBuf[3] = PUT_KEYS_KEY_IDENTIFIER; // txBuf[4] = len; // Fill in length // First byte of data field is "New version number" (GP.p157 Table 11-67) txBuf[5 + len] = keyVersion; len += 1; // Construct the key data fields len += createKeyDataField(PUT_KEYS_KEY_TYPE_CODING_AES, keyEnc, currentKeyDek, &txBuf[5 + len], &cryptoStatus); if (cryptoStatus != HOST_CRYPTO_OK) { return ERR_CRYPTO_ENGINE_FAILED; } len += createKeyDataField(PUT_KEYS_KEY_TYPE_CODING_AES, keyMac, currentKeyDek, &txBuf[5 + len], &cryptoStatus); if (cryptoStatus != HOST_CRYPTO_OK) { return ERR_CRYPTO_ENGINE_FAILED; } len += createKeyDataField(PUT_KEYS_KEY_TYPE_CODING_AES, keyDek, currentKeyDek, &txBuf[5 + len], &cryptoStatus); if (cryptoStatus != HOST_CRYPTO_OK) { return ERR_CRYPTO_ENGINE_FAILED; } txBuf[4] = len; txBuf[5 + len] = 0x00; // In case there is a DEK key, proper command encryption and MACing shall be applied. if (currentKeyDek == NULL) { st = smCom_TransceiveRaw(NULL, (U8*)txBuf, 6 + len, response, &responseLen); if (st != SMCOM_OK) { LOG_E("SCP_GP_PutKeys %lx", st); rv = (U16)st; } else { rv = CheckNoResponseDataRaw(response, (U16)responseLen); } } else { apdu_t apdu; apdu_t * pApdu = (apdu_t *)&apdu; // Construct APDU pApdu->cla = 0x80; pApdu->ins = INS_GP_PUT_KEY; pApdu->p1 = keyVersion; pApdu->p2 = PUT_KEYS_KEY_IDENTIFIER; AllocateAPDUBuffer(pApdu); SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); smApduAppendCmdData(pApdu, txBuf + 5, len); rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); if (rv == SMCOM_OK) { // No response data expected rv = CheckNoResponseData(pApdu); } } exit: return rv; } /** * Performs an SCP03 authentication with the SM and - when successful - computes * the SCP03 session keys and initializes the current Session state. * * @param[in] keyEnc SCP03 channel encryption base key (aka static key) (16 bytes) * @param[in] keyMac SCP03 authentication base key (aka static key) (16 bytes) * @param[in] keyDek SCP03 data encryption base key (aka static key) (16 bytes) * @param[in] keyBytes Must be 16 * @param[in,out] sCounter SCP03 sequence counter (3 bytes) * @param[in,out] sCounterLen */ U16 SCP_Authenticate(U8 *keyEnc, U8 *keyMac, U8 *keyDek, U16 keyBytes, U8 *sCounter, U16 *sCounterLen) { ChannelId_t channelId; U8 hostChallenge[] = {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}; U8 keyDivData[SCP_GP_IU_KEY_DIV_DATA_LEN]; U16 keyDivDataLen = sizeof(keyDivData); U8 keyInfo[SCP_GP_IU_KEY_INFO_LEN]; U16 keyInfoLen = sizeof(keyInfo); U8 cardChallenge[SCP_GP_CARD_CHALLENGE_LEN]; U16 cardChallengeLen = sizeof(cardChallenge); U8 cardCryptoGram[SCP_GP_IU_CARD_CRYPTOGRAM_LEN]; U16 cardCryptoGramLen = sizeof(cardCryptoGram); U8 seqCounter[SCP_GP_IU_SEQ_COUNTER_LEN]; U16 seqCounterLen = sizeof(seqCounter); U8 hostCryptogram[SCP_GP_IU_CARD_CRYPTOGRAM_LEN]; scp_CommandType_t dummy; U16 err = SCP_PARAMETER_ERROR; S32 ret; ENSURE_OR_GO_EXIT(keyBytes==16); channelId = DEV_GetSelectedChannel(&dummy); // Storing Static Keys err = SCP_HostLocal_SetKeysScp(channelId, keyEnc, keyMac, keyDek, keyBytes); if (err != SW_OK) { LOG_E("SCP_HostLocal_SetKeysScp fails with status: 0x%04X", err); goto exit; } ret = HLCRYPT_GetRandom(sizeof(hostChallenge), hostChallenge); if (ret != HOST_CRYPTO_OK) { err = ERR_CRYPTO_ENGINE_FAILED; goto exit; } err = SCP_GP_InitializeUpdate(channelId, hostChallenge, sizeof(hostChallenge), keyDivData, &keyDivDataLen, keyInfo, &keyInfoLen, cardChallenge, &cardChallengeLen, cardCryptoGram, &cardCryptoGramLen, seqCounter, &seqCounterLen); if (err != SW_OK) { LOG_E("SCP_GP_InitializeUpdate fails with status: 0x%04X", err); goto exit; } LOG_MAU8_D("keyDivData", keyDivData, keyDivDataLen); LOG_MAU8_D("keyInfo", keyInfo, keyInfoLen); LOG_MAU8_D("cardChallenge", cardChallenge, cardChallengeLen); LOG_MAU8_D("cardCryptoGram", cardCryptoGram, cardCryptoGramLen); if (seqCounterLen == SCP_GP_IU_SEQ_COUNTER_LEN) { LOG_MAU8_D("seqCounter", seqCounter, seqCounterLen); if (*sCounterLen >= SCP_GP_IU_SEQ_COUNTER_LEN) { // Enough buffer space is provided by caller memcpy(sCounter, seqCounter, seqCounterLen); *sCounterLen = seqCounterLen; } else { goto exit; } } else { *sCounterLen = 0; } err = SCP_HostLocal_CalculateSessionKeys(channelId, hostChallenge, cardChallenge); if (err != SW_OK) { LOG_E("SCP_HostLocal_CalculateSessionKeys fails with status: 0x%04X", err); goto exit; } err = SCP_HostLocal_VerifyCardCryptogram(channelId, hostChallenge, cardChallenge, cardCryptoGram); if (err != SW_OK) { LOG_E("SCP_HostLocal_VerifyCardCryptogram fails with status: 0x%04X", err); goto exit; } err = SCP_HostLocal_CalculateHostCryptogram(channelId, hostChallenge, cardChallenge, hostCryptogram); LOG_MAU8_D("hostCryptogram", hostCryptogram, SCP_GP_IU_CARD_CRYPTOGRAM_LEN); if (err != SW_OK) { LOG_E("SCP_HostLocal_CalculateHostCryptogram fails with status: 0x%04X", err); goto exit; } err = SCP_GP_ExternalAuthenticate(channelId, hostCryptogram); if (err != SW_OK) { LOG_E("SCP_GP_ExternalAuthenticate fails with status: 0x%04X", err); goto exit; } // At this stage we have authenticated successfully. SCP_HostLocal_SetDefaultValueIcvCCounter(channelId); DEV_SetChannelCommandType(channelId, C_MAC_C_ENC_R_MAC_R_ENC); exit: return err; } #ifdef USE_SCP02 U16 SCP02_GP_InitializeUpdate(ChannelId_t channelId, U8 *hostChallenge, U16 hostChallengeLen, U8 *keyDivData, U16 *pKeyDivDataLen, U8 *keyInfo, U16 *pKeyInfoLen, U8 *cardChallenge, U16 *pCardChallengeLen, U8 *cardCryptoGram, U16 *pCardCryptoGramLen, U8 *seqCounter, U16 *pSeqCounterLen) { U32 st = 0; U8 txBuf[128]; U8 cla = 0; U8 response[128]; U32 responseLen = 128; U16 parsePos = 0; U16 sw = SCP_PARAMETER_ERROR; U32 iuResponseLen = SCP_GP_IU_KEY_DIV_DATA_LEN + SCP02_GP_IU_KEY_INFO_LEN + SCP02_GP_IU_SEQ_COUNTER_LEN + SCP02_GP_CARD_CHALLENGE_LEN + SCP_GP_IU_CARD_CRYPTOGRAM_LEN + SCP_GP_SW_LEN; LOG_I(">> %s: Enter", "SCP02_GP_InitializeUpdate"); ENSURE_OR_GO_EXIT(hostChallengeLen == SCP_GP_HOST_CHALLENGE_LEN); ENSURE_OR_GO_EXIT(*pKeyDivDataLen == SCP_GP_IU_KEY_DIV_DATA_LEN); ENSURE_OR_GO_EXIT(*pSeqCounterLen == SCP02_GP_IU_SEQ_COUNTER_LEN); ENSURE_OR_GO_EXIT(*pKeyInfoLen == SCP02_GP_IU_KEY_INFO_LEN); ENSURE_OR_GO_EXIT(*pCardChallengeLen == SCP02_GP_CARD_CHALLENGE_LEN); ENSURE_OR_GO_EXIT(*pCardCryptoGramLen == SCP_GP_IU_CARD_CRYPTOGRAM_LEN); switch (channelId) { case AX_HOST_CHANNEL: #ifdef TGT_EDEV //keyVersion = (U8)SST_HOST_SCP_KEYSET; cla = 0xE0; #else //keyVersion = (U8)(SST_HOST_SCP_KEYSET >> 8); cla = 0x80; #endif break; case AX_ADMIN_CHANNEL: //keyVersion = (U8)0x00; // Just use default key version cla = 0x80; break; default: return SCP_UNDEFINED_CHANNEL_ID; } txBuf[0] = cla; txBuf[1] = INS_GP_INITIALIZE_UPDATE; txBuf[2] = 0; //keyVersion; #ifdef TGT_EDEV txBuf[3] = SCP03_KEY_ID; #else txBuf[3] = 0x00; // InitializeUpdate as implemented in Applet 1.6 still expects an identifier equal to 0. #endif txBuf[4] = (U8)hostChallengeLen; // (GP.p205 Table D-4) memcpy(&txBuf[5], hostChallenge, hostChallengeLen); txBuf[5 + hostChallengeLen] = 0x00; st = smCom_TransceiveRaw(NULL, (U8*)txBuf, 6 + hostChallengeLen, response, &responseLen); if (st != SMCOM_OK) { LOG_E("SCP02_GP_InitializeUpdate. Failure on communication Link (0x%04lX)", st); return (U16)st; } // responseLen = sizeof(dummyResponse); // memcpy(response, dummyResponse, responseLen); // Parse Response // The expected result length depends on random (HOST-Channel) or pseudo-random (ADMIN-Channel) challenge type. // The pseudo-random challenge case also includes a 3 byte sequence counter if (responseLen != iuResponseLen) { // Note: A response of length 2 (a proper SW) is also collapsed into return code SCP_FAIL LOG_E("Unexpected amount of data returned: %ld", responseLen); return SCP_FAIL; } memcpy(keyDivData, response, SCP_GP_IU_KEY_DIV_DATA_LEN); parsePos = SCP_GP_IU_KEY_DIV_DATA_LEN; memcpy(keyInfo, &(response[parsePos]), SCP02_GP_IU_KEY_INFO_LEN); parsePos += SCP02_GP_IU_KEY_INFO_LEN; memcpy(seqCounter, &(response[parsePos]), SCP02_GP_IU_SEQ_COUNTER_LEN); parsePos += SCP02_GP_IU_SEQ_COUNTER_LEN; memcpy(cardChallenge, &(response[parsePos]), SCP02_GP_CARD_CHALLENGE_LEN); parsePos += SCP02_GP_CARD_CHALLENGE_LEN; memcpy(cardCryptoGram, &(response[parsePos]), SCP_GP_IU_CARD_CRYPTOGRAM_LEN); parsePos += SCP_GP_IU_CARD_CRYPTOGRAM_LEN; // Construct Return Value sw = (response[responseLen - 2] << 8) + response[responseLen - 1]; exit: return sw; } U16 SCP02_HostLocal_CalculateSessionKeys(ChannelId_t channelId, U8* seqCounter) { int stateIdx = 0; U8 zero_iv[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; U8 dDataSC02_C_MAC[] = { 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; U8 dDataSC02_R_MAC[] = { 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; U8 dDataSC02_S_ENC[] = { 0x01, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; //U8 dDataSC02_S_DEK[] = { 0x01, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; U8 sessionEncKey[DES_KEY_LEN_nBYTE]; U8 sessionMacKey[DES_KEY_LEN_nBYTE]; U8 sessionRmacKey[DES_KEY_LEN_nBYTE]; U8 masterEncKey[DES_KEY_LEN_nBYTE]; U8 masterMacKey[DES_KEY_LEN_nBYTE]; HLSE_MECHANISM_INFO mechInfo; U32 outLen = DES_KEY_LEN_nBYTE; HLSE_RET_CODE status; U16 sw = SCP_PARAMETER_ERROR; memset(&mechInfo, 0, sizeof(mechInfo)); mechInfo.mechanism = HLSE_DES_CBC_ENCRYPT; ENSURE_OR_GO_EXIT(seqCounter != NULL); switch (channelId) { case AX_HOST_CHANNEL: stateIdx = HOST_CHANNEL_STATE_IDX; break; case AX_ADMIN_CHANNEL: stateIdx = ADMIN_CHANNEL_STATE_IDX; break; default: return SCP_UNDEFINED_CHANNEL_ID; } LOG_I("HOST: Calculate session keys"); // Prepare derivation data dDataSC02_C_MAC[2] = seqCounter[0]; dDataSC02_C_MAC[3] = seqCounter[1]; dDataSC02_R_MAC[2] = seqCounter[0]; dDataSC02_R_MAC[3] = seqCounter[1]; dDataSC02_S_ENC[2] = seqCounter[0]; dDataSC02_S_ENC[3] = seqCounter[1]; //dDataSC02_S_DEK[2] = seqCounter[0]; //dDataSC02_S_DEK[3] = seqCounter[1]; // Calculate the S-ENC key SCP_HostLocal_GetKeyEnc(channelId, masterEncKey); mechInfo.pParameter = zero_iv; mechInfo.ulParameterLen = sizeof(zero_iv); status = HLCRYPT_Encrypt(&mechInfo, masterEncKey, DES_KEY_LEN_nBYTE, dDataSC02_S_ENC, sizeof(dDataSC02_S_ENC), sessionEncKey, &outLen); if (HOST_CRYPTO_OK != status) { sw = SCP_FAIL; goto exit; } //HOST_3DES_CBC_Process(masterEncKey, DES_KEY_LEN_nBYTE, zero_iv, HOST_ENCRYPT, dDataSC02_S_ENC, sizeof(dDataSC02_S_ENC), sessionEncKey); LOG_MAU8_D("sessionEncKey", sessionEncKey, DES_KEY_LEN_nBYTE); // Calculate the C-MAC key SCP_HostLocal_GetKeyMac(channelId, masterMacKey); memset(zero_iv, 0, 8); status = HLCRYPT_Encrypt(&mechInfo, masterMacKey, DES_KEY_LEN_nBYTE, dDataSC02_C_MAC, sizeof(dDataSC02_C_MAC), sessionMacKey, &outLen); if (HOST_CRYPTO_OK != status) { sw = SCP_FAIL; goto exit; } // HOST_3DES_CBC_Process(masterMacKey, DES_KEY_LEN_nBYTE, zero_iv, HOST_ENCRYPT, dDataSC02_C_MAC, sizeof(dDataSC02_C_MAC), sessionMacKey); LOG_MAU8_D("sessionMacKey", sessionMacKey, DES_KEY_LEN_nBYTE); // Calculate the S-RMAC key mechInfo.pParameter = zero_iv; mechInfo.ulParameterLen = sizeof(zero_iv); SCP_HostLocal_GetKeyMac(channelId, masterMacKey); memset(zero_iv, 0, 8); HLCRYPT_Encrypt(&mechInfo, masterMacKey, DES_KEY_LEN_nBYTE, dDataSC02_R_MAC, sizeof(dDataSC02_R_MAC), sessionRmacKey, &outLen); if (HOST_CRYPTO_OK != status) { sw = SCP_FAIL; goto exit; } //HOST_3DES_CBC_Process(masterMacKey, DES_KEY_LEN_nBYTE, zero_iv, HOST_ENCRYPT, dDataSC02_R_MAC, sizeof(dDataSC02_R_MAC), sessionRmacKey); LOG_MAU8_D("sessionRmacKey", sessionRmacKey, DES_KEY_LEN_nBYTE); // Store the Session Keys in the appropriate Channel Session State memcpy(scpState[stateIdx].session.sEnc, sessionEncKey, DES_KEY_LEN_nBYTE); memcpy(scpState[stateIdx].session.sMac, sessionMacKey, DES_KEY_LEN_nBYTE); memcpy(scpState[stateIdx].session.sRMac, sessionRmacKey, DES_KEY_LEN_nBYTE); sw = SCP_OK; exit: return sw; } U16 SCP02_HostLocal_VerifyCardCryptogram(ChannelId_t channelId, U8 *hostChallenge, U8* seqCounter, U8 *cardChallenge, U8 *cardCryptogram) { int stateIdx = 0; U8 zero_iv[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; U8 context[24]; U8 padding[] = { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; U8 sessionEncKey[DES_KEY_LEN_nBYTE]; U8 calculatedCardCryptogram[32]; U16 rv = SCP_OK; HLSE_MECHANISM_INFO mechInfo; S32 ret; U32 outLen = sizeof(calculatedCardCryptogram); memset(&mechInfo, 0, sizeof(mechInfo)); mechInfo.mechanism = HLSE_DES_CBC_ENCRYPT; switch (channelId) { case AX_HOST_CHANNEL: stateIdx = HOST_CHANNEL_STATE_IDX; break; case AX_ADMIN_CHANNEL: stateIdx = ADMIN_CHANNEL_STATE_IDX; break; default: return SCP_UNDEFINED_CHANNEL_ID; } LOG_I("HOST: Verify Card Cryptogram"); // Prepare the 24-byte data in order to calculate the card cryptogram memcpy(context, hostChallenge, SCP_GP_HOST_CHALLENGE_LEN); memcpy(&context[SCP_GP_HOST_CHALLENGE_LEN], seqCounter, SCP02_GP_IU_SEQ_COUNTER_LEN); memcpy(&context[SCP_GP_HOST_CHALLENGE_LEN + SCP02_GP_IU_SEQ_COUNTER_LEN], cardChallenge, SCP02_GP_CARD_CHALLENGE_LEN); memcpy(&context[SCP_GP_HOST_CHALLENGE_LEN + SCP02_GP_IU_SEQ_COUNTER_LEN + SCP02_GP_CARD_CHALLENGE_LEN], padding, sizeof(padding)); memcpy(sessionEncKey, scpState[stateIdx].session.sEnc, DES_KEY_LEN_nBYTE); mechInfo.pParameter = zero_iv; mechInfo.ulParameterLen = sizeof(zero_iv); ret = HLCRYPT_Encrypt(&mechInfo, sessionEncKey, DES_KEY_LEN_nBYTE, context, sizeof(context), calculatedCardCryptogram, &outLen); //ret = HOST_3DES_CBC_Process(sessionEncKey, DES_KEY_LEN_nBYTE, zero_iv, HOST_ENCRYPT, context, sizeof(context), calculatedCardCryptogram); if (ret != HOST_CRYPTO_OK) { return ERR_CRYPTO_ENGINE_FAILED; } LOG_MAU8_D("calculatedCardCryptogram - Verify", calculatedCardCryptogram + 16, 8); // Verify whether the 8 left most byte of cardCryptogramFullLength match cardCryptogram if (memcmp(calculatedCardCryptogram + 16, cardCryptogram, SCP_GP_IU_CARD_CRYPTOGRAM_LEN) != 0) { rv = SCP_CARD_CRYPTOGRAM_FAILS_TO_VERIFY; } return rv; } U16 SCP02_HostLocal_CalculateHostCryptogram(ChannelId_t channelId, U8 *hostChallenge, U8* seqCounter, U8 *cardChallenge, U8 *hostCryptogram) { int stateIdx = 0; U8 zero_iv[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; U8 context[24]; U8 padding[] = { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; U8 sessionEncKey[DES_KEY_LEN_nBYTE]; U8 calculatedHostCryptogram[32]; HLSE_MECHANISM_INFO mechInfo; S32 ret; U32 outLen = sizeof(calculatedHostCryptogram); memset(&mechInfo, 0, sizeof(mechInfo)); mechInfo.mechanism = HLSE_DES_CBC_ENCRYPT; switch (channelId) { case AX_HOST_CHANNEL: stateIdx = HOST_CHANNEL_STATE_IDX; break; case AX_ADMIN_CHANNEL: stateIdx = ADMIN_CHANNEL_STATE_IDX; break; default: return SCP_UNDEFINED_CHANNEL_ID; } LOG_I("HOST: Calculate Host Cryptogram"); // Prepare the 24-byte data in order to calculate the card cryptogram memcpy(context, seqCounter, SCP02_GP_IU_SEQ_COUNTER_LEN); //hostChallenge, SCP_GP_HOST_CHALLENGE_LEN); memcpy(&context[SCP02_GP_IU_SEQ_COUNTER_LEN], cardChallenge, SCP02_GP_CARD_CHALLENGE_LEN); memcpy(&context[SCP02_GP_IU_SEQ_COUNTER_LEN + SCP02_GP_CARD_CHALLENGE_LEN], hostChallenge, SCP_GP_HOST_CHALLENGE_LEN); memcpy(&context[SCP02_GP_IU_SEQ_COUNTER_LEN + SCP02_GP_CARD_CHALLENGE_LEN + SCP_GP_HOST_CHALLENGE_LEN], padding, sizeof(padding)); memcpy(sessionEncKey, &(scpState[stateIdx].session.sEnc), DES_KEY_LEN_nBYTE); mechInfo.pParameter = zero_iv; mechInfo.ulParameterLen = sizeof(zero_iv); ret = HLCRYPT_Encrypt(&mechInfo, sessionEncKey, DES_KEY_LEN_nBYTE, context, sizeof(context), calculatedHostCryptogram, &outLen); //ret = HOST_3DES_CBC_Process(sessionEncKey, DES_KEY_LEN_nBYTE, zero_iv, HOST_ENCRYPT, context, sizeof(context), calculatedHostCryptogram); if (ret != HOST_CRYPTO_OK) { return ERR_CRYPTO_ENGINE_FAILED; } LOG_MAU8_D("calculatedHostCryptogram - Verify", calculatedHostCryptogram + 16, SCP_GP_IU_CARD_CRYPTOGRAM_LEN); // Chop of the tail of the hostCryptogramFullLength memcpy(hostCryptogram, calculatedHostCryptogram + 16, SCP_GP_IU_CARD_CRYPTOGRAM_LEN); return SCP_OK; } U16 SCP02_GP_ExternalAuthenticate(ChannelId_t channelId, U8* hostCryptogram) { int stateIdx = ADMIN_CHANNEL_STATE_IDX; U8 zero_iv[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; U8 extauthcmd_block1[8]; U8 extauthcmd_block2[8]; // C-MAC only (0x13 for full) U8 extauthcmd[] = { 0x84, 0x82, SCP02_SECLVL_CDEC_CMAC, 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; U8 calchostmac[8]; U8 sessionMacKey[DES_KEY_LEN_nBYTE]; U8 response[128]; U32 responseLen = 128; U32 st = 0; HLSE_MECHANISM_INFO mechInfo; S32 ret; U32 outLen = 0; U8 encData[8]; LOG_I(">> %s: Enter", "SCP02_GP_ExternalAuthenticate"); switch (channelId) { case AX_HOST_CHANNEL: stateIdx = HOST_CHANNEL_STATE_IDX; break; case AX_ADMIN_CHANNEL: stateIdx = ADMIN_CHANNEL_STATE_IDX; break; default: return SCP_UNDEFINED_CHANNEL_ID; } // Compute the C-MAC of the ExtAuth command memcpy(extauthcmd + 5, hostCryptogram, 8); memcpy(extauthcmd_block1, extauthcmd, 8); memcpy(extauthcmd_block2, extauthcmd + 8, 8); memcpy(sessionMacKey, scpState[stateIdx].session.sMac, DES_KEY_LEN_nBYTE); //STEP 1 :- Encrypt the first 8 bytes of the 16bytes(padded) message using the first 8 bytes of the CMAC session key : //data-- > extauthcmd_block1 (8 bytes) //key-- > SessionMac key(8 bytes) //output -- > encData; ret = HLCRYPT_Single_DES_CBC_Encrypt(sessionMacKey, 8, zero_iv, 8, extauthcmd_block1, 8, encData, &outLen); if (ret != 1) { return ERR_CRYPTO_ENGINE_FAILED; } //STEP 2 :-Encrypt this 8bytes using the final TripleDES: //data-- > extauthcmd_block2 //key-- > SessionMac key (Complete 16 bytes) memset(&mechInfo, 0, sizeof(mechInfo)); mechInfo.mechanism = HLSE_DES_CBC_ENCRYPT; mechInfo.pParameter = encData; mechInfo.ulParameterLen = sizeof(encData); ret = HLCRYPT_Encrypt(&mechInfo, sessionMacKey, DES_KEY_LEN_nBYTE, extauthcmd_block2, sizeof(extauthcmd_block2), calchostmac, &outLen); if (ret != HOST_CRYPTO_OK) { return ERR_CRYPTO_ENGINE_FAILED; } memcpy(extauthcmd + 5 + 8, calchostmac, 8); // Retain the calculated mac, to be used as iv in next commands memcpy(scpState[stateIdx].session.mcv, calchostmac, 8); st = smCom_TransceiveRaw(NULL, (U8*)extauthcmd, 5 + 16, response, &responseLen); if (st != SMCOM_OK) { LOG_E("SCP01_GP_ExternalAuthenticate %lX", st); return ERR_GENERAL_ERROR; } else { return CheckNoResponseDataRaw(response, (U16)responseLen); } } /** * Performs an SCP02 authentication with the SM and - when successful - computes * the SCP02 session keys and initializes the current Session state. * * @param[in] keyEnc SCP02 channel encryption base key (aka static key) (16 bytes) * @param[in] keyMac SCP02 authentication base key (aka static key) (16 bytes) * @param[in] keyDek SCP02 data encryption base key (aka static key) (16 bytes) * @param[in] keyBytes Must be 16 * @param[in,out] sCounter SCP02 sequence counter (2 bytes) * @param[in,out] sCounterLen */ U16 SCP02_Authenticate(U8 *keyEnc, U8 *keyMac, U8 *keyDek, U16 keyBytes, U8 *sCounter, U16 *sCounterLen) { ChannelId_t channelId; U8 hostChallenge[] = { 0x0F, 0x8E, 0xA9, 0x27, 0xAF, 0x1C, 0x5A, 0x27}; U8 keyDivData[SCP_GP_IU_KEY_DIV_DATA_LEN]; U16 keyDivDataLen = sizeof(keyDivData); U8 keyInfo[SCP02_GP_IU_KEY_INFO_LEN]; U16 keyInfoLen = sizeof(keyInfo); U8 cardChallenge[SCP02_GP_CARD_CHALLENGE_LEN]; U16 cardChallengeLen = sizeof(cardChallenge); U8 cardCryptoGram[SCP_GP_IU_CARD_CRYPTOGRAM_LEN]; U16 cardCryptoGramLen = sizeof(cardCryptoGram); U8 seqCounter[SCP02_GP_IU_SEQ_COUNTER_LEN]; U16 seqCounterLen = sizeof(seqCounter); U8 hostCryptogram[SCP_GP_IU_CARD_CRYPTOGRAM_LEN]; scp_CommandType_t dummy; U16 err = SCP_PARAMETER_ERROR; S32 ret; ENSURE_OR_GO_EXIT(keyBytes == 16); channelId = DEV_GetSelectedChannel(&dummy); // Storing Static Keys err = SCP_HostLocal_SetKeysScp(channelId, keyEnc, keyMac, keyDek, keyBytes); if (err != SW_OK) { LOG_E("SCP_HostLocal_SetKeysScp fails with status: 0x%04X", err); goto exit; } ret = HLCRYPT_GetRandom(sizeof(hostChallenge), hostChallenge); if (ret != HOST_CRYPTO_OK) { err = ERR_CRYPTO_ENGINE_FAILED; goto exit; } err = SCP02_GP_InitializeUpdate(channelId, hostChallenge, sizeof(hostChallenge), keyDivData, &keyDivDataLen, keyInfo, &keyInfoLen, cardChallenge, &cardChallengeLen, cardCryptoGram, &cardCryptoGramLen, seqCounter, &seqCounterLen); if (err != SW_OK) { LOG_E("SCP02_GP_InitializeUpdate fails with status: 0x%04X", err); goto exit; } LOG_MAU8_D("keyDivData", keyDivData, keyDivDataLen); LOG_MAU8_D("keyInfo", keyInfo, keyInfoLen); LOG_MAU8_D("cardChallenge", cardChallenge, cardChallengeLen); LOG_MAU8_D("cardCryptoGram", cardCryptoGram, cardCryptoGramLen); if (seqCounterLen == SCP02_GP_IU_SEQ_COUNTER_LEN) { LOG_MAU8_D("seqCounter", seqCounter, seqCounterLen); if (*sCounterLen >= SCP02_GP_IU_SEQ_COUNTER_LEN) { // Enough buffer space is provided by caller memcpy(sCounter, seqCounter, seqCounterLen); *sCounterLen = seqCounterLen; } else { goto exit; } } else { *sCounterLen = 0; } err = SCP02_HostLocal_CalculateSessionKeys(channelId, seqCounter); if (err != SW_OK) { LOG_E("SCP02_HostLocal_CalculateSessionKeys fails with status: 0x%04X", err); goto exit; } err = SCP02_HostLocal_VerifyCardCryptogram(channelId, hostChallenge, seqCounter, cardChallenge, cardCryptoGram); if (err != SW_OK) { LOG_E("SCP02_HostLocal_VerifyCardCryptogram fails with status: 0x%04X", err); goto exit; } err = SCP02_HostLocal_CalculateHostCryptogram(channelId, hostChallenge, seqCounter, cardChallenge, hostCryptogram); LOG_MAU8_D("hostCryptogram", hostCryptogram, SCP_GP_IU_CARD_CRYPTOGRAM_LEN); if (err != SW_OK) { LOG_E("SCP02_HostLocal_CalculateHostCryptogram fails with status: 0x%04X", err); goto exit; } err = SCP02_GP_ExternalAuthenticate(channelId, hostCryptogram); if (err != SW_OK) { LOG_E("SCP02_GP_ExternalAuthenticate fails with status: 0x%04X", err); goto exit; } // At this stage we have authenticated successfully. SCP_HostLocal_SetDefaultValueIcvCCounter(channelId); DEV_SetChannelCommandType(channelId, SCP02_SECLVL_CDEC_CMAC); exit: return err; } #endif #ifdef TGT_EDEV #ifndef AX_SF /** * Performs a soft-reset of the Security Module, effectively resetting the SCP channels. * This function is only available for the administrator * @return status. */ U16 SM_SoftReset(void) { apdu_t apdu; apdu_t * pApdu = (apdu_t *) &apdu; U16 rv; pApdu->cla = AX_CLA; pApdu->ins = INS_AX_SM; pApdu->p1 = P1_SOFT_RESET; pApdu->p2 = 0x00; AllocateAPDUBuffer(pApdu); SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); scp_Transceive(pApdu, SCP_MODE); // no response data expected rv = CheckNoResponseData(pApdu); FreeAPDUBuffer(pApdu); return rv; } #endif // AX_SF #endif // TGT_EDEV