/* | |
* @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 <string.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#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 <nxEnsure.h> | |
/// @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]; | |
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 |