blob: 8d9652bca362bdff344dc6212a8ad915285b4d14 [file] [log] [blame]
/*
* @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