blob: e32e4ba3da71c1f87385e90701919f7c287ce0f9 [file] [log] [blame]
/*
*
* Copyright 2018,2020 NXP
* SPDX-License-Identifier: Apache-2.0
*/
#if defined(FLOW_VERBOSE)
#define NX_LOG_ENABLE_SCP_DEBUG 1
#endif
#include <string.h>
#include <assert.h>
#include "smCom.h"
#include <nxLog_scp.h>
#include "nxScp03_Apis.h"
#include "nxEnsure.h"
#include "se05x_const.h"
#if SSS_HAVE_SE05X_VER_GTE_06_00
#if defined(SE05X_MAX_BUF_SIZE_CMD) && (SE05X_MAX_BUF_SIZE_CMD != 1024)
# error "Expect hard coded for SE05X_MAX_BUF_SIZE_CMD = 1024"
#endif
#define NX_SCP03_MAX_BUFFER_SIZE 0x400 /* 0x400 = 1024 */
#else
#if defined(SE05X_MAX_BUF_SIZE_CMD) && (SE05X_MAX_BUF_SIZE_CMD != 892)
# error "Expect hard coded for SE05X_MAX_BUF_SIZE_CMD = 892"
#endif
#define NX_SCP03_MAX_BUFFER_SIZE 0x380 /* 0x380 = 896 */
#endif
/* ************************************************************************** */
/* Functions : Private function declaration */
/* ************************************************************************** */
/**
* To Apply Encryption on Plain Data
*/
static void nxSCP03_PadCommandAPDU(uint8_t *cmdBuf, size_t *pCmdBufLen);
/**
* To Maintain chaining of Sent commands
*/
static sss_status_t nxSCP03_Calculate_CommandICV(NXSCP03_DynCtx_t *pdySCP03SessCtx, uint8_t *pIcv);
/**
* To Maintain chaining of Receive commands
*/
static sss_status_t nxpSCP03_Get_ResponseICV(NXSCP03_DynCtx_t *pdySCP03SessCtx, uint8_t *pIcv, bool hasCmd);
/**
* To check plain data
*/
static uint16_t nxpSCP03_RestoreSw_RAPDU(
uint8_t *rspBuf, size_t *pRspBufLen, uint8_t *plaintextResponse, size_t plaintextRespLen, uint8_t *sw);
/**
* Decrement counter block for ICV calculation
*/
static void nxpSCP03_Dec_CommandCounter(uint8_t *pCtrblock);
sss_status_t nxSCP03_Encrypt_CommandAPDU(NXSCP03_DynCtx_t *pdySCP03SessCtx, uint8_t *cmdBuf, size_t *pCmdBufLen)
{
sss_status_t sss_status = kStatus_SSS_Fail;
size_t dataLen = 0;
ENSURE_OR_GO_CLEANUP(pCmdBufLen != NULL);
LOG_D("FN: %s", __FUNCTION__);
LOG_MAU8_D(" Input:cmdBuf", cmdBuf, *pCmdBufLen);
if (*pCmdBufLen != 0) {
sss_symmetric_t symm;
uint8_t iv[16] = {0};
uint8_t *pIv = (uint8_t *)iv;
uint8_t apduPayloadToEncrypt[NX_SCP03_MAX_BUFFER_SIZE] = {0};
/* Prior to encrypting the data, the data shall be padded as defined in section 4.1.4.
This padding becomes part of the data field.*/
nxSCP03_PadCommandAPDU(cmdBuf, pCmdBufLen);
sss_status = nxSCP03_Calculate_CommandICV(pdySCP03SessCtx, pIv);
ENSURE_OR_GO_CLEANUP(sss_status == kStatus_SSS_Success);
memcpy(apduPayloadToEncrypt, cmdBuf, *pCmdBufLen);
sss_status = sss_host_symmetric_context_init(&symm,
pdySCP03SessCtx->Enc.keyStore->session,
&pdySCP03SessCtx->Enc,
kAlgorithm_SSS_AES_CBC,
kMode_SSS_Encrypt);
ENSURE_OR_GO_CLEANUP(sss_status == kStatus_SSS_Success);
dataLen = *pCmdBufLen;
LOG_D("Encrypt CommandAPDU");
sss_status = sss_host_cipher_one_go(&symm, pIv, SCP_KEY_SIZE, apduPayloadToEncrypt, cmdBuf, dataLen);
ENSURE_OR_GO_CLEANUP(sss_status == kStatus_SSS_Success);
LOG_AU8_D(cmdBuf, dataLen);
LOG_MAU8_D("Output: EncryptedcmdBuf", cmdBuf, dataLen);
sss_host_symmetric_context_free(&symm);
}
else {
/* Nothing to encrypt */
sss_status = kStatus_SSS_Success;
}
cleanup:
return sss_status;
}
uint16_t nxpSCP03_Decrypt_ResponseAPDU(
NXSCP03_DynCtx_t *pdySCP03SessCtx, size_t cmdBufLen, uint8_t *rspBuf, size_t *pRspBufLen, uint8_t hasle)
{
sss_status_t sss_status = kStatus_SSS_Fail;
uint16_t status = SCP_FAIL;
sss_algorithm_t algorithm = kAlgorithm_SSS_CMAC_AES;
sss_mode_t mode = kMode_SSS_Mac;
sss_mac_t macCtx;
uint8_t sw[SCP_GP_SW_LEN];
uint8_t respMac[SCP_CMAC_SIZE];
size_t signatureLen = sizeof(respMac);
size_t compareoffset = 0;
size_t macSize = SCP_CMAC_SIZE;
uint8_t iv[SCP_IV_SIZE];
uint8_t *pIv = (uint8_t *)iv;
uint8_t response[NX_SCP03_MAX_BUFFER_SIZE];
uint8_t plaintextResponse[NX_SCP03_MAX_BUFFER_SIZE];
sss_algorithm_t algorithm_aes = kAlgorithm_SSS_AES_CBC;
sss_mode_t mode_aes = kMode_SSS_Decrypt;
sss_symmetric_t symm;
size_t actualRespLen = 0;
ENSURE_OR_GO_EXIT(pRspBufLen != NULL);
ENSURE_OR_GO_EXIT(pdySCP03SessCtx != NULL);
ENSURE_OR_GO_EXIT(rspBuf != NULL);
LOG_D("FN: %s", __FUNCTION__);
LOG_MAU8_D(" Input:rspBuf", rspBuf, *pRspBufLen);
if (*pRspBufLen >= (SCP_COMMAND_MAC_SIZE + SCP_GP_SW_LEN)) {
memcpy(sw, &(rspBuf[*pRspBufLen - SCP_GP_SW_LEN]), SCP_GP_SW_LEN);
sss_status = sss_host_mac_context_init(
&macCtx, pdySCP03SessCtx->Rmac.keyStore->session, &pdySCP03SessCtx->Rmac, algorithm, mode);
ENSURE_OR_GO_EXIT(sss_status == kStatus_SSS_Success);
sss_status = sss_host_mac_init(&macCtx);
ENSURE_OR_GO_EXIT(sss_status == kStatus_SSS_Success);
sss_status = sss_host_mac_update(&macCtx, pdySCP03SessCtx->MCV, macSize);
ENSURE_OR_GO_EXIT(sss_status == kStatus_SSS_Success);
sss_status = sss_host_mac_update(&macCtx, rspBuf, *pRspBufLen - SCP_COMMAND_MAC_SIZE - SCP_GP_SW_LEN);
ENSURE_OR_GO_EXIT(sss_status == kStatus_SSS_Success);
sss_status = sss_host_mac_update(&macCtx, sw, SCP_GP_SW_LEN);
ENSURE_OR_GO_EXIT(sss_status == kStatus_SSS_Success);
sss_status = sss_host_mac_finish(&macCtx, respMac, &signatureLen);
ENSURE_OR_GO_EXIT(sss_status == kStatus_SSS_Success);
LOG_MAU8_D(" Calculated RMAC :", respMac, signatureLen);
sss_host_mac_context_free(&macCtx);
LOG_D("Verify MAC");
// Do a comparison of the received and the calculated mac
compareoffset = *pRspBufLen - SCP_COMMAND_MAC_SIZE - SCP_GP_SW_LEN;
if (memcmp(respMac, &rspBuf[compareoffset], SCP_COMMAND_MAC_SIZE) != 0) {
LOG_E(" RESPONSE MAC DID NOT VERIFY %04X", status);
return status;
}
}
LOG_D("RMAC verified successfully...Decrypt Response Data");
// Decrypt Response Data Field in case Reponse Mac verified OK
if (*pRspBufLen > (SCP_COMMAND_MAC_SIZE + SCP_GP_SW_LEN)) {
// There is data payload in response
size_t dataLen = 0;
memcpy(response, rspBuf, (*pRspBufLen) - (SCP_COMMAND_MAC_SIZE + SCP_GP_SW_LEN));
//LOG_MAU8_D(" EncResponse", response, (*pRspBufLen) - 10);
memcpy(sw, &(rspBuf[*pRspBufLen - SCP_GP_SW_LEN]), SCP_GP_SW_LEN);
LOG_MAU8_D("Status Word: ", sw, 2);
// Calculate ICV to decrypt the response
sss_status = nxpSCP03_Get_ResponseICV(pdySCP03SessCtx, pIv, cmdBufLen == 0 ? FALSE : TRUE);
ENSURE_OR_GO_EXIT(sss_status == kStatus_SSS_Success);
sss_status = sss_host_symmetric_context_init(
&symm, pdySCP03SessCtx->Enc.keyStore->session, &pdySCP03SessCtx->Enc, algorithm_aes, mode_aes);
ENSURE_OR_GO_EXIT(sss_status == kStatus_SSS_Success);
dataLen = (*pRspBufLen) - (SCP_COMMAND_MAC_SIZE + SCP_GP_SW_LEN);
LOG_D("Decrypt the response");
// Decrypt the response
sss_status = sss_host_cipher_one_go(&symm, pIv, SCP_KEY_SIZE, response, plaintextResponse, dataLen);
ENSURE_OR_GO_EXIT(sss_status == kStatus_SSS_Success);
LOG_MAU8_D("PlainText", plaintextResponse, (*pRspBufLen) - (SCP_COMMAND_MAC_SIZE + SCP_GP_SW_LEN));
sss_host_symmetric_context_free(&symm);
actualRespLen = (*pRspBufLen) - (SCP_COMMAND_MAC_SIZE + SCP_GP_SW_LEN);
/*Remove the padding from the plaintextResponse*/
sss_status = kStatus_SSS_Fail;
status = nxpSCP03_RestoreSw_RAPDU(rspBuf, pRspBufLen, plaintextResponse, actualRespLen, sw);
if (status == SCP_OK) {
sss_status = kStatus_SSS_Success;
}
}
else if ((*pRspBufLen) == (SCP_COMMAND_MAC_SIZE + SCP_GP_SW_LEN)) {
// There's no data payload in response
memcpy(rspBuf, sw, SCP_GP_SW_LEN);
*pRspBufLen = SCP_GP_SW_LEN;
sss_status = kStatus_SSS_Success;
}
if (sss_status == kStatus_SSS_Success) {
status = SCP_OK;
}
if (((pdySCP03SessCtx->authType == kSSS_AuthType_AESKey) || (pdySCP03SessCtx->authType == kSSS_AuthType_ECKey)) ||
((pdySCP03SessCtx->authType == kSSS_AuthType_SCP03) && cmdBufLen > 0)) {
status = SCP_OK;
nxpSCP03_Inc_CommandCounter(pdySCP03SessCtx);
}
exit:
return status;
}
static uint16_t nxpSCP03_RestoreSw_RAPDU(
uint8_t *rspBuf, size_t *pRspBufLen, uint8_t *plaintextResponse, size_t plaintextRespLen, uint8_t *sw)
{
uint16_t status = SCP_DECODE_FAIL;
size_t i;
int removePaddingOk = 0;
i = plaintextRespLen;
ENSURE_OR_GO_EXIT(pRspBufLen != NULL);
ENSURE_OR_GO_EXIT(plaintextResponse != NULL);
ENSURE_OR_GO_EXIT(rspBuf != NULL);
ENSURE_OR_GO_EXIT(sw != NULL);
LOG_D("FN: %s", __FUNCTION__);
while ((i > 1) && (i > (plaintextRespLen - SCP_KEY_SIZE))) {
if (plaintextResponse[i - 1] == 0x00) {
i--;
}
else if (plaintextResponse[i - 1] == SCP_DATA_PAD_BYTE) {
// We have found padding delimitor
memcpy(&plaintextResponse[i - 1], sw, SCP_GP_SW_LEN);
memcpy(rspBuf, plaintextResponse, i + 1);
*pRspBufLen = (i + 1);
removePaddingOk = 1;
LOG_MAU8_D("PlainText+SW", rspBuf, *pRspBufLen);
break;
}
else {
// We've found a non-padding character while removing padding
// Most likely the cipher text was not properly decoded.
LOG_E("RAPDU Decoding failed No Padding found %04X", status);
break;
}
}
if (removePaddingOk == 0) {
return status;
}
status = SCP_OK;
exit:
return status;
}
static sss_status_t nxpSCP03_Get_ResponseICV(NXSCP03_DynCtx_t *pdySCP03SessCtx, uint8_t *pIcv, bool hasCmd)
{
uint8_t ivZero[SCP_IV_SIZE] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
sss_status_t status = kStatus_SSS_Fail;
sss_symmetric_t symm;
size_t dataLen = 0;
uint8_t paddedCounterBlock[SCP_IV_SIZE] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
ENSURE_OR_GO_EXIT(pdySCP03SessCtx != NULL);
LOG_D("FN: %s", __FUNCTION__);
memcpy(paddedCounterBlock, pdySCP03SessCtx->cCounter, SCP_KEY_SIZE);
if ((pdySCP03SessCtx->authType == kSSS_AuthType_SCP03) && (!hasCmd)) {
nxpSCP03_Dec_CommandCounter(paddedCounterBlock);
}
paddedCounterBlock[0] = SCP_DATA_PAD_BYTE; // MSB padded with 0x80 Section 6.2.7 of SCP03 spec
LOG_MAU8_D(" Input:Data", paddedCounterBlock, SCP_KEY_SIZE);
status = sss_host_symmetric_context_init(&symm,
pdySCP03SessCtx->Enc.keyStore->session,
&pdySCP03SessCtx->Enc,
kAlgorithm_SSS_AES_CBC,
kMode_SSS_Encrypt);
ENSURE_OR_GO_EXIT(status == kStatus_SSS_Success);
dataLen = SCP_KEY_SIZE;
status = sss_host_cipher_one_go(&symm, ivZero, SCP_KEY_SIZE, paddedCounterBlock, pIcv, dataLen);
ENSURE_OR_GO_EXIT(status == kStatus_SSS_Success);
LOG_MAU8_D(" Output:RespICV", pIcv, dataLen);
sss_host_symmetric_context_free(&symm);
exit:
return status;
}
void nxpSCP03_Inc_CommandCounter(NXSCP03_DynCtx_t *pdySCP03SessCtx)
{
int i = 15;
ENSURE_OR_GO_EXIT(pdySCP03SessCtx != NULL);
while (i > 0) {
if (pdySCP03SessCtx->cCounter[i] < 255) {
pdySCP03SessCtx->cCounter[i] += 1;
break;
}
else {
pdySCP03SessCtx->cCounter[i] = 0;
i--;
}
}
LOG_MAU8_D("Inc_CommandCounter value ", pdySCP03SessCtx->cCounter, SCP_KEY_SIZE);
exit:
return;
}
static void nxpSCP03_Dec_CommandCounter(uint8_t *pCtrblock)
{
int i = 15;
ENSURE_OR_GO_EXIT(pCtrblock != NULL);
while (i > 0) {
if (pCtrblock[i] == 0) {
pCtrblock[i] = 0xFF;
i--;
}
else {
pCtrblock[i]--;
break;
}
}
exit:
return;
}
sss_status_t nxpSCP03_CalculateMac_CommandAPDU(
NXSCP03_DynCtx_t *pdySCP03SessCtx, uint8_t *pCmdBuf, size_t cmdBufLen, uint8_t *mac, size_t *macLen)
{
sss_status_t sss_status = kStatus_SSS_Fail;
sss_mac_t macCtx;
sss_algorithm_t algorithm = kAlgorithm_SSS_CMAC_AES;
sss_mode_t mode = kMode_SSS_Mac;
ENSURE_OR_GO_EXIT(pdySCP03SessCtx != NULL);
ENSURE_OR_GO_EXIT(mac != NULL);
LOG_D("FN: %s", __FUNCTION__);
LOG_MAU8_D("Input: cmdBuf", pCmdBuf, cmdBufLen);
sss_status =
sss_host_mac_context_init(&macCtx, pdySCP03SessCtx->Mac.keyStore->session, &pdySCP03SessCtx->Mac, algorithm, mode);
ENSURE_OR_GO_EXIT(sss_status == kStatus_SSS_Success);
sss_status = sss_host_mac_init(&macCtx);
ENSURE_OR_GO_EXIT(sss_status == kStatus_SSS_Success);
sss_status = sss_host_mac_update(&macCtx, pdySCP03SessCtx->MCV, SCP_KEY_SIZE);
ENSURE_OR_GO_EXIT(sss_status == kStatus_SSS_Success);
sss_status = sss_host_mac_update(&macCtx, pCmdBuf, cmdBufLen);
ENSURE_OR_GO_EXIT(sss_status == kStatus_SSS_Success);
sss_status = sss_host_mac_finish(&macCtx, mac, macLen);
ENSURE_OR_GO_EXIT(sss_status == kStatus_SSS_Success);
LOG_MAU8_D("Output: mac", mac, SCP_COMMAND_MAC_SIZE);
sss_host_mac_context_free(&macCtx);
// Store updated mcv!
memcpy(pdySCP03SessCtx->MCV, mac, SCP_MCV_LEN);
exit:
return sss_status;
}
static sss_status_t nxSCP03_Calculate_CommandICV(NXSCP03_DynCtx_t *pdySCP03SessCtx, uint8_t *pIcv)
{
uint8_t ivZero[SCP_KEY_SIZE] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
sss_status_t status = kStatus_SSS_Fail;
sss_symmetric_t symm;
size_t dataLen = 0;
ENSURE_OR_GO_EXIT(pdySCP03SessCtx != NULL);
LOG_D("FN: %s", __FUNCTION__);
status = sss_host_symmetric_context_init(&symm,
pdySCP03SessCtx->Enc.keyStore->session,
&pdySCP03SessCtx->Enc,
kAlgorithm_SSS_AES_CBC,
kMode_SSS_Encrypt);
ENSURE_OR_GO_EXIT(status == kStatus_SSS_Success);
dataLen = SCP_KEY_SIZE;
status = sss_host_cipher_one_go(&symm, ivZero, SCP_KEY_SIZE, pdySCP03SessCtx->cCounter, pIcv, dataLen);
sss_host_symmetric_context_free(&symm);
LOG_MAU8_D(" Output:", pIcv, SCP_COMMAND_MAC_SIZE);
exit:
return status;
}
static void nxSCP03_PadCommandAPDU(uint8_t *cmdBuf, size_t *pCmdBufLen)
{
uint16_t zeroBytesToPad = 0;
ENSURE_OR_GO_EXIT(pCmdBufLen != NULL);
ENSURE_OR_GO_EXIT(cmdBuf != NULL);
LOG_D("FN: %s", __FUNCTION__);
LOG_MAU8_D("Input: cmdBuf", cmdBuf, *pCmdBufLen);
// pad the payload and adjust the length of the APDU
cmdBuf[(*pCmdBufLen)] = SCP_DATA_PAD_BYTE;
*pCmdBufLen += 1;
zeroBytesToPad = (SCP_KEY_SIZE - ((*pCmdBufLen) % SCP_KEY_SIZE)) % SCP_KEY_SIZE;
while (zeroBytesToPad > 0) {
cmdBuf[(*pCmdBufLen)] = 0x00;
*pCmdBufLen += 1;
zeroBytesToPad--;
}
LOG_MAU8_D("Ouput: cmdBuf", cmdBuf, *pCmdBufLen);
exit:
return;
}