blob: f2bea0c07c3bebed1482cf818499c43f6a351450 [file] [log] [blame]
/*
* @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;
}