/* * * Copyright 2018,2020 NXP * SPDX-License-Identifier: Apache-2.0 */ #if defined(FLOW_VERBOSE) #define NX_LOG_ENABLE_SCP_DEBUG 1 #endif #include #include #include "smCom.h" #include #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] = {0}; 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); sss_host_symmetric_context_free(&symm); ENSURE_OR_GO_EXIT(status == kStatus_SSS_Success); LOG_MAU8_D(" Output:RespICV", pIcv, dataLen); 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; }