/* | |
* @author NXP Semiconductors | |
* @version 1.0 | |
* @par License | |
* | |
* Copyright 2016,2020 NXP | |
* SPDX-License-Identifier: Apache-2.0 | |
* | |
* @par History | |
* 1.0 2016-aug-29 : Initial version | |
* | |
*/ | |
/** | |
* @file a71ch_module.c | |
* @par Description | |
* Wrap module centric APDU functionality of the A71CH | |
*/ | |
#include <stdio.h> | |
#include <string.h> | |
#include "scp.h" | |
#include "a71ch_api.h" | |
#include "sm_apdu.h" | |
#include "sm_errors.h" | |
#include "ax_common_private.h" | |
#if defined(SSS_USE_FTR_FILE) | |
#include "fsl_sss_ftr.h" | |
#else | |
#include "fsl_sss_ftr_default.h" | |
#endif | |
/// @cond | |
#define NX_LOG_ENABLE_HOSTLIB_DEBUG 1 | |
#include <nxLog_hostLib.h> | |
static U16 A71_GetChallengeGeneric(U8 challengeType, U8 *challenge, U16 *challengeLen) | |
{ | |
U16 rv = 0; | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
U8 isOk; | |
#ifndef A71_IGNORE_PARAM_CHECK | |
if ((challenge == NULL) || (challengeLen == NULL)) { | |
return ERR_API_ERROR; | |
} | |
#endif | |
pApdu->cla = A71CH_CLA; | |
pApdu->ins = A71CH_INS_GET_MODULE; | |
pApdu->p1 = challengeType; | |
pApdu->p2 = P2_CHALLENGE; | |
if (*challengeLen < A71CH_MODULE_UNLOCK_CHALLENGE_LEN) | |
{ | |
return ERR_BUF_TOO_SMALL; | |
} | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
rv = smGetSw(pApdu, &isOk); | |
if (isOk) | |
{ | |
rv = smApduGetResponseBody(pApdu, challenge, challengeLen); | |
if (*challengeLen != A71CH_MODULE_UNLOCK_CHALLENGE_LEN) | |
{ | |
rv = ERR_WRONG_RESPONSE; | |
} | |
} | |
} | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} | |
static U16 A71_GetRandomGeneric(U8 *random, U8 randomLen, U8 mode) | |
{ | |
U8 isOk = 0; | |
U16 rv; | |
U16 randomBufLen = randomLen; | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
#ifndef A71_IGNORE_PARAM_CHECK | |
if (random == NULL) { | |
return ERR_API_ERROR; | |
} | |
#endif | |
pApdu->cla = AX_CLA; | |
pApdu->ins = A71CH_INS_MODULE_GET_RANDOM; | |
pApdu->p1 = mode; | |
pApdu->p2 = (mode == P1_RANDOM_PLAIN) ? randomLen : 0x00; | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
rv = smGetSw(pApdu, &isOk); | |
if (isOk) | |
{ | |
rv = smApduGetResponseBody(pApdu, random, &randomBufLen); | |
if (rv == SW_OK) | |
{ | |
if (randomBufLen != randomLen) | |
{ | |
rv = ERR_WRONG_RESPONSE; | |
} | |
} | |
} | |
} | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} | |
/// @endcond | |
/** | |
* Get credential info from Module (in raw format) | |
* @param[in,out] map | |
* @param[in,out] mapLen | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_GetCredentialInfo(U8 *map, U16 *mapLen) | |
{ | |
U16 rv = 0; | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
U8 isOk; | |
#ifndef A71_IGNORE_PARAM_CHECK | |
if ((map == NULL) || (mapLen == NULL)) { | |
return ERR_API_ERROR; | |
} | |
#endif | |
pApdu->cla = A71CH_CLA; | |
pApdu->ins = A71CH_INS_GET_MODULE; | |
pApdu->p1 = 0x00; | |
pApdu->p2 = P2_CREDENTIAL_MAP; | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, 0); | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
rv = smGetSw(pApdu, &isOk); | |
if (isOk) | |
{ | |
rv = smApduGetResponseBody(pApdu, map, mapLen); | |
} | |
else | |
{ | |
*mapLen = 0; | |
} | |
} | |
else | |
{ | |
*mapLen = 0; | |
} | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} | |
/** | |
* Get info on Module | |
* @param[out] selectResponse Encodes applet revision and whether Debug Mode is available | |
* @param[out] debugOn Equals 0x01 when the Debug Mode is available | |
* @param[out] restrictedKpIdx Either the index of the restricted keypair or ::A71CH_NO_RESTRICTED_KP | |
* @param[out] transportLockState The value retieved is one of ::A71CH_TRANSPORT_LOCK_STATE_LOCKED, | |
* A71CH_TRANSPORT_LOCK_STATE_UNLOCKED or A71CH_TRANSPORT_LOCK_STATE_ALLOW_LOCK | |
* @param[out] scpState The value retrieved is on of ::A71CH_SCP_MANDATORY, ::A71CH_SCP_NOT_SET_UP | |
* or ::A71CH_SCP_KEYS_SET | |
* @param[out] injectLockState The value retrieved is one of ::A71CH_INJECT_LOCK_STATE_LOCKED or ::A71CH_INJECT_LOCK_STATE_UNLOCKED | |
* @param[out] gpStorageSize Total storage size (in byte) of the General Purpose data store | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_GetModuleInfo(U16 *selectResponse, U8 *debugOn, U8 *restrictedKpIdx, U8 *transportLockState, U8 *scpState, U8 *injectLockState, U16 *gpStorageSize) | |
{ | |
U16 rv = 0; | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
U8 isOk; | |
U8 data[64]; | |
U16 dataLen = sizeof(data); | |
#ifndef A71_IGNORE_PARAM_CHECK | |
if ((selectResponse == NULL) || (debugOn == NULL) || (restrictedKpIdx == NULL) || (transportLockState == NULL) || | |
(scpState == NULL) || (injectLockState == NULL) || (gpStorageSize == NULL)) { | |
return ERR_API_ERROR; | |
} | |
#endif | |
pApdu->cla = A71CH_CLA; | |
pApdu->ins = A71CH_INS_GET_MODULE; | |
pApdu->p1 = 0x00; | |
pApdu->p2 = P2_APPLET_INFO; | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
*selectResponse = 0; | |
*debugOn = 0; | |
*restrictedKpIdx = 0; | |
*transportLockState = 0; | |
*injectLockState = 0; | |
*gpStorageSize = 0; | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
rv = smGetSw(pApdu, &isOk); | |
if (isOk) | |
{ | |
rv = smApduGetResponseBody(pApdu, data, &dataLen); | |
if (dataLen == AX_MODULE_INFO_RESPONSE_LEN) | |
{ | |
*selectResponse = (data[0] << 8) + data[1]; | |
*debugOn = *selectResponse & 0x01; | |
*restrictedKpIdx = (data[2] & 0xF0) >> 4; | |
*transportLockState = data[2] & 0x0F; | |
*scpState = (data[3] & 0xF0) >> 4; | |
*injectLockState = data[3] & 0x0F; | |
*gpStorageSize = (data[4] << 8) + data[5]; | |
} | |
else | |
{ | |
rv = ERR_WRONG_RESPONSE; | |
} | |
} | |
} | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} | |
/** | |
* Get Unique Identifier from the Secure Module | |
* @param[in,out] uid IN: buffer to contain uid; OUT: uid retrieved from Secure Module | |
* @param[in,out] uidLen IN: Size of buffer provided (at least ::A71CH_MODULE_UNIQUE_ID_LEN byte); | |
OUT: length of retrieved unique identifier (expected to be ::A71CH_MODULE_UNIQUE_ID_LEN byte) | |
* @retval ::SW_OK Upon successful execution | |
* @retval ::ERR_WRONG_RESPONSE In case an identifier with a length different from ::A71CH_MODULE_UNIQUE_ID_LEN was retrieved | |
*/ | |
U16 A71_GetUniqueID(U8 *uid, U16 *uidLen) | |
{ | |
U16 rv = 0; | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
U8 isOk; | |
#ifndef A71_IGNORE_PARAM_CHECK | |
if ((uid == NULL) || (uidLen == NULL)) { | |
return ERR_API_ERROR; | |
} | |
#endif | |
pApdu->cla = A71CH_CLA; | |
pApdu->ins = A71CH_INS_GET_MODULE; | |
pApdu->p1 = 0x00; | |
pApdu->p2 = P2_UNIQUE_ID; | |
if (*uidLen < A71CH_MODULE_UNIQUE_ID_LEN) | |
{ | |
return ERR_BUF_TOO_SMALL; | |
} | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
rv = smGetSw(pApdu, &isOk); | |
if (isOk) | |
{ | |
rv = smApduGetResponseBody(pApdu, uid, uidLen); | |
if (*uidLen != A71CH_MODULE_UNIQUE_ID_LEN) | |
{ | |
rv = ERR_WRONG_RESPONSE; | |
} | |
} | |
} | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} | |
/** | |
* Get cert uid from the Secure Module. The cert uid is a subset of the Secure Module Unique Identifier | |
* @param[in,out] certUid IN: buffer to contain cert uid; OUT: cert uid retrieved from Secure Module | |
* @param[in,out] certUidLen IN: Size of buffer provided (at least ::A71CH_MODULE_CERT_UID_LEN byte); | |
* OUT: length of retrieved unique identifier (expected to be ::A71CH_MODULE_CERT_UID_LEN byte) | |
* | |
* @retval ::SW_OK Upon successful execution | |
* @retval ::ERR_WRONG_RESPONSE In case the Secure Module Unique Identifier (i.e. the base uid) did not have the expected length | |
*/ | |
U16 A71_GetCertUid(U8 *certUid, U16 *certUidLen) | |
{ | |
U16 rv = 0; | |
U8 uid[A71CH_MODULE_UNIQUE_ID_LEN] = {0}; | |
U16 uidLen = A71CH_MODULE_UNIQUE_ID_LEN; | |
int idx = 0; | |
if (*certUidLen < A71CH_MODULE_CERT_UID_LEN) | |
{ | |
return ERR_BUF_TOO_SMALL; | |
} | |
rv = A71_GetUniqueID(uid, &uidLen); | |
if (rv == SMCOM_OK) | |
{ | |
idx = 0; | |
certUid[idx++] = uid[A71CH_UID_IC_TYPE_OFFSET]; | |
certUid[idx++] = uid[A71CH_UID_IC_TYPE_OFFSET + 1]; | |
certUid[idx++] = uid[A71CH_UID_IC_FABRICATION_DATA_OFFSET]; | |
certUid[idx++] = uid[A71CH_UID_IC_FABRICATION_DATA_OFFSET + 1]; | |
certUid[idx++] = uid[A71CH_UID_IC_SERIAL_NR_OFFSET]; | |
certUid[idx++] = uid[A71CH_UID_IC_SERIAL_NR_OFFSET + 1]; | |
certUid[idx++] = uid[A71CH_UID_IC_SERIAL_NR_OFFSET + 2]; | |
certUid[idx++] = uid[A71CH_UID_IC_BATCH_ID_OFFSET]; | |
certUid[idx++] = uid[A71CH_UID_IC_BATCH_ID_OFFSET + 1]; | |
certUid[idx++] = uid[A71CH_UID_IC_BATCH_ID_OFFSET + 2]; | |
*certUidLen = A71CH_MODULE_CERT_UID_LEN; | |
} | |
else | |
{ | |
*certUidLen = 0; | |
} | |
return rv; | |
} | |
/** | |
* Get Unlock challenge from the Secure Module | |
* @param[in,out] challenge IN: buffer to contain challenge; OUT: challenge retrieved from Secure Module | |
* @param[in,out] challengeLen IN: Size of buffer provided (at least ::A71CH_MODULE_UNLOCK_CHALLENGE_LEN byte); | |
OUT: length of retrieved unique identifier (must be ::A71CH_MODULE_UNLOCK_CHALLENGE_LEN byte) | |
* @retval ::SW_OK Upon successful execution | |
* @retval ::ERR_WRONG_RESPONSE In case an identifier with a length different from ::A71CH_MODULE_UNLOCK_CHALLENGE_LEN was retrieved | |
*/ | |
U16 A71_GetUnlockChallenge(U8 *challenge, U16 *challengeLen) | |
{ | |
return A71_GetChallengeGeneric(P1_MODULE_UNLOCK_CHALLENGE, challenge, challengeLen); | |
} | |
/** | |
* Get Unlock challenge for a Keypair | |
* @param[in,out] challenge IN: buffer to contain challenge; OUT: challenge retrieved from Secure Module | |
* @param[in,out] challengeLen IN: Size of buffer provided (at least ::A71CH_MODULE_UNLOCK_CHALLENGE_LEN byte); | |
OUT: length of retrieved unique identifier (must be ::A71CH_MODULE_UNLOCK_CHALLENGE_LEN byte) | |
* @retval ::SW_OK Upon successful execution | |
* @retval ::ERR_WRONG_RESPONSE In case an identifier with a length different from ::A71CH_MODULE_UNLOCK_CHALLENGE_LEN was retrieved | |
*/ | |
U16 A71_GetKeyPairChallenge(U8 *challenge, U16 *challengeLen) | |
{ | |
return A71_GetChallengeGeneric(P1_KEYPAIR_CHALLENGE, challenge, challengeLen); | |
} | |
/** | |
* Get Unlock challenge for a Public Key | |
* @param[in,out] challenge IN: buffer to contain challenge; OUT: challenge retrieved from Secure Module | |
* @param[in,out] challengeLen IN: Size of buffer provided (at least ::A71CH_MODULE_UNLOCK_CHALLENGE_LEN byte); | |
OUT: length of retrieved unique identifier (must be ::A71CH_MODULE_UNLOCK_CHALLENGE_LEN byte) | |
* @retval ::SW_OK Upon successful execution | |
* @retval ::ERR_WRONG_RESPONSE In case an identifier with a length different from ::A71CH_MODULE_UNLOCK_CHALLENGE_LEN was retrieved | |
*/ | |
U16 A71_GetPublicKeyChallenge(U8 *challenge, U16 *challengeLen) | |
{ | |
return A71_GetChallengeGeneric(P1_PUBLIC_KEY_CHALLENGE, challenge, challengeLen); | |
} | |
/** | |
* Retrieves a random byte array of size randomLen from the Secure Module. | |
* The maximum amount of data that can be retrieved depends on | |
* whether an authenticated channel (SCP03) has been set up. | |
* In case SCP03 has been set up, this (worst-case) maximum is ::A71CH_SCP03_MAX_PAYLOAD_SIZE | |
* @param[in,out] random IN: buffer to contain random value (at least of size randomLen); | |
OUT: retrieved random data | |
* @param[in] randomLen Amount of byte to retrieve | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_GetRandom(U8 *random, U8 randomLen) | |
{ | |
return A71_GetRandomGeneric(random, randomLen, P1_RANDOM_PLAIN); | |
} | |
/** | |
* Updates a 32 byte random value inside the A71CH and returns this value to the caller. | |
* \post A71CH is in a state it will accept ::A71_PskDeriveMasterSecret or ::A71_EcdhPskDeriveMasterSecret as an API call. | |
* | |
* @param[in,out] clientHello IN: buffer to contain random value (at least of size randomLen); | |
OUT: retrieved random data | |
* @param[in] clientHelloLen Amount of byte to retrieve (must be equal to ::AX_TLS_PSK_HELLO_RANDOM_LEN) | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_CreateClientHelloRandom(U8 *clientHello, U8 clientHelloLen) | |
{ | |
if (clientHelloLen != AX_TLS_PSK_HELLO_RANDOM_LEN) | |
{ | |
return ERR_API_ERROR; | |
} | |
return A71_GetRandomGeneric(clientHello, clientHelloLen, P1_RANDOM_CLIENT_HELLO); | |
} | |
/** | |
* Get the index of the restricted key pair (\p idx) together with the number of modifiable blocks | |
* (\p nBlocks) in the locked GP storage area that is associated with the restricted key pair. | |
* Detailed info on block offset and block length is contained in the \p blockInfo byte array. | |
* Per block 2 bytes indicate the offset into GP storage and two bytes indicate the length of | |
* the modifiable block. | |
* | |
* @param[out] idx Index of restricted key pair. ::A71CH_NO_RESTRICTED_KP in case there is no restricted key pair | |
* @param[out] nBlocks Number of modifiable blocks | |
* @param[in,out] blockInfo IN: Storage to contain blockInfo; OUT: Raw info on block offset and block lenght per block. | |
* @param[in,out] blockInfoLen IN: Size of blockInfo (in byte); OUT: effective size of blockInfo | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_GetRestrictedKeyPairInfo(U8 *idx, U16 *nBlocks, U8 *blockInfo, U16 *blockInfoLen) | |
{ | |
U16 rv = 0; | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
U8 isOk; | |
U8 localBuf[255]; | |
U16 localBufLen = sizeof(localBuf); | |
#ifndef A71_IGNORE_PARAM_CHECK | |
if ((idx == NULL) || (nBlocks == NULL) || (blockInfo == NULL) || (blockInfoLen == NULL)) { | |
return ERR_API_ERROR; | |
} | |
#endif | |
*idx = A71CH_NO_RESTRICTED_KP; | |
*nBlocks = 0; | |
pApdu->cla = A71CH_CLA; | |
pApdu->ins = A71CH_INS_GET_MODULE; | |
pApdu->p1 = 0x00; | |
pApdu->p2 = P2_RESTRICTED_INFO; | |
// Ensure there's at least space to contain Offset|Lenght description of one block. | |
if (*blockInfoLen < 4) | |
{ | |
return ERR_BUF_TOO_SMALL; | |
} | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
rv = smGetSw(pApdu, &isOk); | |
if (isOk) | |
{ | |
rv = smApduGetResponseBody(pApdu, localBuf, &localBufLen); | |
if (rv == SW_OK) | |
{ | |
if (localBufLen == 0) | |
{ | |
// There is no restricted keypair and associated modifiable block(s) | |
*blockInfoLen = 0; | |
} | |
else if (localBufLen == 2) | |
{ | |
// Workaround for Applet 1.3 Issue | |
if ( (localBuf[0] == 0xFF) && (localBuf[1] == 0x00) ) | |
{ | |
// There is no restricted keypair and associated modifiable block(s) | |
*blockInfoLen = 0; | |
} | |
else | |
{ | |
*blockInfoLen = 0; | |
rv = ERR_WRONG_RESPONSE; | |
} | |
} | |
else if (localBufLen > 2) | |
{ | |
if (*blockInfoLen < (localBufLen-2) ) | |
{ | |
*blockInfoLen = 0; | |
rv = ERR_BUF_TOO_SMALL; | |
} | |
else | |
{ | |
*idx = localBuf[0]; | |
*nBlocks = localBuf[1]; | |
*blockInfoLen = localBufLen - 2; | |
memcpy(blockInfo, &localBuf[2], *blockInfoLen); | |
} | |
} | |
else | |
{ | |
*blockInfoLen = 0; | |
rv = ERR_WRONG_RESPONSE; | |
} | |
} | |
} | |
} | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} | |
/** | |
* Calculates the SHA256 value of the data provided as input. | |
* @param[in] data Data buffer for which the SHA256 must be calculated | |
* @param[in] dataLen The length of data passed as argument | |
* @param[in,out] sha IN: caller passes a buffer of at least 32 byte; | |
OUT: contains the calculated SHA256 | |
* @param[in,out] shaLen IN: length of the sha buffer passed; | |
OUT: because SHA256 is used this is 32 byte exact | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_GetSha256(U8 *data, U16 dataLen, U8 *sha, U16 *shaLen) | |
{ | |
U8 isOk = 0; | |
U16 rv; | |
U16 maxChunk = 0; | |
U16 remainingData = dataLen; | |
U16 toSend = 0; | |
U16 dataOffset = 0; | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
#ifndef A71_IGNORE_PARAM_CHECK | |
if ((data == NULL) || (sha == NULL) || (shaLen == NULL)) { | |
return ERR_API_ERROR; | |
} | |
#endif | |
pApdu->cla = AX_CLA; | |
pApdu->ins = A71CH_INS_MODULE_GET_SHA256; | |
pApdu->p1 = 0x00; | |
pApdu->p2 = P2_SHA256_ONE_SHOT; | |
AllocateAPDUBuffer(pApdu); | |
maxChunk = A71CH_SHA256_MAX_DATA_CHUNK; | |
// Depending on dataLen we can do a one-shot SHA256 or need to split up across multiple APDU's | |
if (dataLen <= maxChunk) | |
{ | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
if (dataLen != 0){ | |
smApduAppendCmdData(pApdu, data, dataLen); | |
} | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
rv = smGetSw(pApdu, &isOk); | |
if (isOk) | |
{ | |
rv = smApduGetResponseBody(pApdu, sha, shaLen); | |
if (rv == SW_OK) | |
{ | |
if (32 != *shaLen) | |
{ | |
rv = ERR_WRONG_RESPONSE; | |
} | |
} | |
} | |
} | |
} | |
else | |
{ | |
// Split up in multiple transactions ... | |
// Send out first chunk (SHA256_Init) | |
pApdu->p2 = P2_SHA256_INIT; | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
toSend = maxChunk; | |
remainingData -= toSend; | |
smApduAppendCmdData(pApdu, data, toSend); | |
dataOffset += toSend; | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
if (rv == SMCOM_OK) | |
{ | |
// No response data expected | |
rv = CheckNoResponseData(pApdu); | |
if (rv != SW_OK) { goto LBL_LEAVE_A71_GetSha256; } | |
} | |
} | |
while (remainingData > maxChunk) | |
{ | |
// Send out 'middle' chunks (SHA256_Update) | |
pApdu->p2 = P2_SHA256_UPDATE; | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
toSend = maxChunk; | |
remainingData -= toSend; | |
smApduAppendCmdData(pApdu, &data[dataOffset], toSend); | |
dataOffset += toSend; | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
if (rv == SMCOM_OK) | |
{ | |
// No response data expected | |
rv = CheckNoResponseData(pApdu); | |
if (rv != SW_OK) { goto LBL_LEAVE_A71_GetSha256; } | |
} | |
} | |
} | |
// Always close with SHA256_Final | |
pApdu->p2 = P2_SHA256_FINAL; | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
toSend = remainingData; | |
// remainingData -= toSend; | |
// dataOffset += toSend; | |
smApduAppendCmdData(pApdu, &data[dataOffset], toSend); | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
{ | |
rv = smGetSw(pApdu, &isOk); | |
if (isOk) | |
{ | |
rv = smApduGetResponseBody(pApdu, sha, shaLen); | |
if (rv == SW_OK) | |
{ | |
if (32 != *shaLen) | |
{ | |
rv = ERR_WRONG_RESPONSE; | |
} | |
} | |
} | |
} | |
} | |
LBL_LEAVE_A71_GetSha256: | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} | |
/** | |
* Initialise multistep SHA256. | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_Sha256Init(void) | |
{ | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
U16 rv; | |
pApdu->cla = AX_CLA; | |
pApdu->ins = A71CH_INS_MODULE_GET_SHA256; | |
pApdu->p1 = 0x00; | |
pApdu->p2 = P2_SHA256_INIT; | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
// No response data expected | |
rv = CheckNoResponseData(pApdu); | |
} | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} | |
/** | |
* Update the data for calulating SHA256 value (in multistep). | |
* @param[in] data Data buffer for which the SHA256 must be calculated | |
* @param[in] dataLen The length of data passed as argument | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_Sha256Update(U8 *data, U16 dataLen) | |
{ | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *)&apdu; | |
U16 rv = 0; | |
U16 maxChunk = A71CH_SHA256_MAX_DATA_CHUNK; | |
U16 remainingData = dataLen; | |
U16 toSend = 0; | |
U16 dataSent = 0; | |
#ifndef A71_IGNORE_PARAM_CHECK | |
if (data == NULL) { | |
return ERR_API_ERROR; | |
} | |
#endif | |
pApdu->cla = AX_CLA; | |
pApdu->ins = A71CH_INS_MODULE_GET_SHA256; | |
pApdu->p1 = 0x00; | |
pApdu->p2 = P2_SHA256_UPDATE; | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
if (dataLen == 0) { | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
// No response data expected | |
rv = CheckNoResponseData(pApdu); | |
if (rv != SW_OK) { goto LBL_LEAVE_A71_Sha256Update; } | |
} | |
} | |
else { | |
while (remainingData != 0) | |
{ | |
toSend = (remainingData > maxChunk) ? maxChunk : remainingData; | |
smApduAppendCmdData(pApdu, (data + dataSent), toSend); | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
// No response data expected | |
rv = CheckNoResponseData(pApdu); | |
if (rv != SW_OK) { goto LBL_LEAVE_A71_Sha256Update; } | |
} | |
dataSent += toSend; | |
remainingData -= toSend; | |
} | |
} | |
LBL_LEAVE_A71_Sha256Update: | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} | |
/** | |
* calulating SHA256 value (in multistep). | |
* @param[in,out] sha IN: caller passes a buffer of at least 32 byte; | |
OUT: contains the calculated SHA256 | |
* @param[in,out] shaLen IN: length of the sha buffer passed; | |
OUT: because SHA256 is used this is 32 byte exact | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_Sha256Final(U8 *sha, U16 *shaLen) | |
{ | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *)&apdu; | |
U16 rv; | |
U8 isOk = 0; | |
#ifndef A71_IGNORE_PARAM_CHECK | |
if ((sha == NULL) || (shaLen == NULL)) { | |
return ERR_API_ERROR; | |
} | |
#endif | |
pApdu->cla = AX_CLA; | |
pApdu->ins = A71CH_INS_MODULE_GET_SHA256; | |
pApdu->p1 = 0x00; | |
pApdu->p2 = P2_SHA256_FINAL; | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
rv = smGetSw(pApdu, &isOk); | |
if (isOk) | |
{ | |
rv = smApduGetResponseBody(pApdu, sha, shaLen); | |
if (rv == SW_OK) | |
{ | |
if (32 != *shaLen) | |
{ | |
rv = ERR_WRONG_RESPONSE; | |
} | |
} | |
} | |
} | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} | |
/** | |
* This function disables - at device level - the ability to | |
- Set symmetric keys without prior wrapping | |
- Erase symmetric keys | |
- Set ECC key pairs (private key part) without prior wrapping | |
- Set ECC public key without prior wrapping | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_InjectLock() | |
{ | |
U16 rv; | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
pApdu->cla = AX_CLA; | |
pApdu->ins = A71CH_INS_SET_MODULE; | |
pApdu->p1 = 0x00; | |
pApdu->p2 = P2_SMF_LOCK_PLAIN_INJECTION; | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
// No response data expected | |
rv = CheckNoResponseData(pApdu); | |
} | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} | |
/** | |
* This function locks the module (typically to protect the module during transport to production | |
facilities). When the A71CH is locked the functionality is reduced to the following subset: | |
- ::A71_GetUniqueID | |
- ::A71_GetUnlockChallenge | |
- ::A71_UnlockModule | |
- ::A71_GetModuleInfo | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_LockModule() | |
{ | |
U16 rv; | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
pApdu->cla = AX_CLA; | |
pApdu->ins = A71CH_INS_SET_MODULE; | |
pApdu->p1 = 0x00; | |
pApdu->p2 = P2_SMF_LOCK_MODULE; | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
// No response data expected | |
rv = CheckNoResponseData(pApdu); | |
} | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} | |
/** | |
* This function unlocks the module provided the correct code is provided as input argument. | |
The A71CH can only be unlocked once: if the device is already unlocked, the device cannot be locked | |
or unlocked again (it will remain unlocked). | |
The unlock code is calculated as follows: | |
- Request a challenge from A71CH using ::A71_GetUnlockChallenge. | |
- Decrypt the challenge in ECB mode using the appropriate configuration key value (the same as stored at index ::A71CH_CFG_KEY_IDX_MODULE_LOCK). | |
- The decrypted value is the unlock \p code | |
* @param[in] code Value of unlock code | |
* @param[in] codeLen Length of unlock code (must be 16) | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_UnlockModule(U8 *code, U16 codeLen) | |
{ | |
U16 rv; | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
#ifndef A71_IGNORE_PARAM_CHECK | |
if (code == NULL) { | |
return ERR_API_ERROR; | |
} | |
#endif | |
pApdu->cla = AX_CLA; | |
pApdu->ins = A71CH_INS_SET_MODULE; | |
pApdu->p1 = 0x00; | |
pApdu->p2 = P2_SMF_UNLOCK_MODULE; | |
if (codeLen != 16) | |
{ | |
return ERR_API_ERROR; | |
} | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
smApduAppendCmdData(pApdu, code, codeLen); | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
// No response data expected | |
rv = CheckNoResponseData(pApdu); | |
} | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} | |
/** | |
* Sets the label that is used when calling ::A71_EcdhPskDeriveMasterSecret or | |
* ::A71_PskDeriveMasterSecret. Calling this function is optional. By default the label | |
* used by the A71CH is \p 'master secret' (no quotes) as applicable for TLS 1.2. | |
* The maximum size of the label that can be set is 24 byte. | |
* @param[in] label Value to be stored and used as 'label' in TLS 1.2 protocol | |
* @param[in] labelLen Length of label (less than or equal to ::A71CH_TLS_MAX_LABEL) | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_SetTlsLabel(const U8* label, U16 labelLen) | |
{ | |
U16 rv; | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
#ifndef A71_IGNORE_PARAM_CHECK | |
if (label == NULL) { | |
return ERR_API_ERROR; | |
} | |
#endif | |
pApdu->cla = AX_CLA; | |
pApdu->ins = A71CH_INS_SET_MODULE; | |
pApdu->p1 = 0x00; | |
pApdu->p2 = P2_SMF_LABEL; | |
if (labelLen > A71CH_TLS_MAX_LABEL) | |
{ | |
return ERR_API_ERROR; | |
} | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
smApduAppendCmdData(pApdu, label, labelLen); | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
// No response data expected | |
rv = CheckNoResponseData(pApdu); | |
} | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} | |
/** | |
* Verifies whether \p pSignature is the signature of \p pHash | |
* using \p pKeyData as the verifying public key. | |
* | |
* As opposed to function ::A71_EccVerify the public key value is passed as | |
* an argument to the A71CH. | |
* | |
* @param[in] pKeyData Public key passed as byte array in ANSI X9.62 uncompressed format | |
* @param[in] keyDataLen Length of public key passed as argument | |
* @param[in] pHash Pointer to the provided hash (or any other bytestring). | |
* @param[in] hashLen Length of the provided hash. | |
* @param[in] pSignature Pointer to the provided signature. | |
* @param[in] signatureLen Length of the provided signature. | |
* @param[out] pResult Pointer to the computed result of the verification. | |
Points to a value of 0x01 in case of successful verification | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_EccVerifyWithKey(const U8 *pKeyData, U16 keyDataLen, const U8 *pHash, U16 hashLen, const U8 *pSignature, U16 signatureLen, U8 *pResult) | |
{ | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
U16 rv; | |
U8 isOk = 0; | |
#ifndef A71_IGNORE_PARAM_CHECK | |
if ((pKeyData == NULL) || (pHash == NULL) || (pSignature == NULL) || (pResult == NULL)) { | |
return ERR_API_ERROR; | |
} | |
#endif | |
if (keyDataLen != A71CH_PUB_KEY_LEN) { return ERR_API_ERROR; } | |
if (hashLen != AX_SHA256_LEN) { return ERR_API_ERROR; } | |
*pResult = 0; | |
pApdu->cla = A71CH_CLA; | |
pApdu->ins = A71CH_INS_VERIFY_SIG_ECC_PUBLIC_KEY; | |
pApdu->p1 = 0x00; | |
pApdu->p2 = P2_PUBKEY_PRESENT; | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
smApduAppendCmdData(pApdu, pHash, hashLen); | |
smApduAppendCmdData(pApdu, pSignature, signatureLen); | |
smApduAppendCmdData(pApdu, pKeyData, keyDataLen); | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
U8 result = AX_VERIFY_FAILURE; | |
U16 resultLen = 1; | |
rv = smGetSw(pApdu, &isOk); | |
if (isOk) | |
{ | |
rv = smApduGetResponseBody(pApdu, &result, &resultLen); | |
if ((rv != SW_OK) || (resultLen != 1) || ((result != AX_VERIFY_SUCCESS) && (result != AX_VERIFY_FAILURE))) | |
{ | |
rv = ERR_WRONG_RESPONSE; | |
} | |
else | |
{ | |
if (result == AX_VERIFY_SUCCESS) | |
{ | |
*pResult = 1; | |
} | |
else | |
{ | |
*pResult = 0; | |
} | |
} | |
} | |
} | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} |