/* | |
* @author NXP Semiconductors | |
* @version 1.2 | |
* @par License | |
* | |
* Copyright 2017,2020 NXP | |
* SPDX-License-Identifier: Apache-2.0 | |
* | |
* @par History | |
* 1.0 2016-Aug-26: Initial version | |
* 1.1 2017-Nov-30: Adding function A71_EccRestrictedSign | |
* 1.2 2018-Dec-12: Modified implementation of A71_EccSign to adapt reported size of ECDSA signature | |
* | |
*****************************************************************************/ | |
/** | |
* @file a71ch_crypto_ecc.c | |
* @par Description | |
* Wrap the ECC cryptographic functionality of the A71CH. | |
*/ | |
#include <stdio.h> | |
#include "a71ch_api.h" | |
#include "sm_apdu.h" | |
#include "sm_errors.h" | |
#include "hcAsn.h" | |
#include "axHostCrypto.h" | |
/// @cond | |
#define ASN_SKIP_INTEGER_NORMALIZE 0x00 | |
#define ASN_DO_INTEGER_NORMALIZE 0x01 | |
static U16 A71_EccSign_Local(SST_Index_t index, const U8 *pHash, U16 hashLen, U8 *pSignature, U16 *pSignatureLen, U8 normalize); | |
/// @endcond | |
/** | |
* Generates an ECC keypair at storage location \p index. | |
* @pre INJECTION_LOCKED has not been set | |
* @param[in] index Storage index of the keypair to be created. | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_GenerateEccKeyPair(SST_Index_t index) | |
{ | |
U16 rv = 0; | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
pApdu->cla = A71CH_CLA; | |
pApdu->ins = A71CH_INS_SET_ECC_KEYPAIR; | |
pApdu->p1 = index; | |
pApdu->p2 = 0x00; | |
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; | |
} | |
/** | |
* Generates an ECC keypair at storage location \p index. | |
* This function must be called instead of ::A71_GenerateEccKeyPair in case INJECTION_LOCKED was set. | |
* | |
* To use this function the value of the Key Pair configuration key must be known on the host. If | |
* this is not the case use ::A71_GenerateEccKeyPairWithCode instead. | |
* | |
* @param[in] index Storage index of the keypair to be created. | |
* @param[in] configKey Value of Key Pair configuration key. This value has a high level of | |
* confidentiality and may not be available to the Host. | |
* @param[in] configKeyLen Length of Key Pair configuration key | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_GenerateEccKeyPairWithChallenge(SST_Index_t index, const U8 *configKey, U16 configKeyLen) | |
{ | |
U16 rv; | |
S32 hcRet = 0; | |
U8 challenge[A71CH_MODULE_UNLOCK_CHALLENGE_LEN] = {0}; | |
U16 challengeLen = sizeof(challenge); | |
U8 code[A71CH_MODULE_UNLOCK_CHALLENGE_LEN] = {0}; | |
#ifndef A71_IGNORE_PARAM_CHECK | |
if (configKey == NULL) { | |
return ERR_API_ERROR; | |
} | |
#endif | |
if (configKeyLen != 16) | |
{ | |
return ERR_API_ERROR; | |
} | |
rv = A71_GetKeyPairChallenge(challenge, &challengeLen); | |
if (rv != SW_OK) { return rv; } | |
// Decrypt challenge | |
hcRet = HOST_AES_ECB_DECRYPT(code, challenge, configKey, configKeyLen); | |
if (hcRet != HOST_CRYPTO_OK) | |
{ | |
return ERR_CRYPTO_ENGINE_FAILED; | |
} | |
rv = A71_GenerateEccKeyPairWithCode(index, code, A71CH_MODULE_UNLOCK_CHALLENGE_LEN); | |
return rv; | |
} | |
/** | |
* Generates an ECC keypair at storage location \p index. | |
* This function must be called instead of ::A71_GenerateEccKeyPair in case INJECTION_LOCKED was set. | |
* | |
* The assumption is the value of the Key Pair configuration key is not known on the host. | |
* If this does not apply use ::A71_GenerateEccKeyPairWithChallenge instead. | |
* | |
* The 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 ::A71XX_CFG_KEY_IDX_PRIVATE_KEYS). | |
- The decrypted value is the value of \p code | |
* | |
* @param[in] index Storage index of the keypair to be created. | |
* @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_GenerateEccKeyPairWithCode(SST_Index_t index, const U8 *code, U16 codeLen) | |
{ | |
U16 rv; | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
pApdu->cla = AX_CLA; | |
pApdu->ins = A71CH_INS_SET_ECC_KEYPAIR; | |
pApdu->p1 = index; | |
pApdu->p2 = (U8)codeLen; | |
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; | |
} | |
/** | |
* Signs the hash \p pHash using the keypair at the indicated index. | |
* @param[in] index Storage index of the keypair (private key) to be used. | |
* @param[in] pHash Pointer to the provided hash (or any other bytestring). | |
* @param[in] hashLen Length of the provided hash. | |
* @param[in,out] pSignature Pointer to the computed signature. | |
* @param[in,out] pSignatureLen Pointer to the length of the computed signature. | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_EccSign(SST_Index_t index, const U8 *pHash, U16 hashLen, U8 *pSignature, U16 *pSignatureLen) | |
{ | |
return A71_EccSign_Local(index, pHash, hashLen, pSignature, pSignatureLen, ASN_SKIP_INTEGER_NORMALIZE); | |
} | |
/** | |
* Signs the hash \p pHash using the keypair at the indicated index. | |
* | |
* The integer representation of the ECDSA signatures' | |
* r and s component is modified to be in line with ASN.1 (Ensuring an integer value is always encoded in the | |
* smallest possible number of octets) | |
* @param[in] index Storage index of the keypair (private key) to be used. | |
* @param[in] pHash Pointer to the provided hash (or any other bytestring). | |
* @param[in] hashLen Length of the provided hash. | |
* @param[in,out] pSignature Pointer to the computed signature. | |
* @param[in,out] pSignatureLen Pointer to the length of the computed signature. | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_EccNormalizedAsnSign(SST_Index_t index, const U8 *pHash, U16 hashLen, U8 *pSignature, U16 *pSignatureLen) | |
{ | |
return A71_EccSign_Local(index, pHash, hashLen, pSignature, pSignatureLen, ASN_DO_INTEGER_NORMALIZE); | |
} | |
/// @cond | |
static U16 A71_EccSign_Local(SST_Index_t index, const U8 *pHash, U16 hashLen, U8 *pSignature, U16 *pSignatureLen, U8 normalize) | |
{ | |
U8 isOk = 0; | |
U16 rv; | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
#ifndef A71_IGNORE_PARAM_CHECK | |
if ((pHash == NULL) || (pSignature == NULL) || (pSignatureLen == NULL) || (*pSignatureLen < 72)) { | |
return ERR_API_ERROR; | |
} | |
#endif | |
pApdu->cla = A71CH_CLA; | |
pApdu->ins = A71CH_INS_SIGN_HASH_ECC_KEYPAIR; | |
pApdu->p1 = index; | |
pApdu->p2 = 0x00; | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
smApduAppendCmdData(pApdu, pHash, hashLen); | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
rv = smGetSw(pApdu, &isOk); | |
if (isOk) | |
{ | |
rv = smApduGetResponseBody(pApdu, pSignature, pSignatureLen); | |
if (rv != SW_OK) | |
{ | |
return ERR_WRONG_RESPONSE; | |
} | |
if (normalize == ASN_DO_INTEGER_NORMALIZE) | |
{ | |
if ( hcNormalizeAsnSignatureEcc(pSignature, pSignatureLen) != SW_OK) | |
{ | |
return ERR_WRONG_RESPONSE; | |
} | |
} | |
else | |
{ | |
U16 actualSignatureLen; | |
// Detect whether padded bytes must be removed from pSignature | |
// As we're not inspecting whether we received a valid ASN structure, at least ensure there | |
// is an 'L' byte in the signature | |
if (*pSignatureLen >= 2) | |
{ | |
actualSignatureLen = pSignature[1] + 2; | |
if (actualSignatureLen < *pSignatureLen) | |
{ | |
// Patch the value of *pSignatureLen | |
*pSignatureLen = actualSignatureLen; | |
} | |
} | |
else | |
{ | |
return ERR_WRONG_RESPONSE; | |
} | |
} | |
} | |
} | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} | |
/// @endcond | |
/** | |
* Patches a predetermined fixed size memory region in GP storage with the byte array \p updateBytes | |
* Creates a signed certificate - in place in GP storage - using a predetermined block of GP storage data | |
* | |
* @param[in] index Storage index of the keypair (private key) to be used. | |
* @param[in] updateBytes Byte array to be written into GP storage | |
* @param[in] updateBytesLen Length of the provided byte array (\p updateBytes). | |
* @param[out] invocationCount Amount of times the underlying APDU has been called succesfully. | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_EccRestrictedSign(SST_Index_t index, const U8 *updateBytes, U16 updateBytesLen, U8 *invocationCount) | |
{ | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
U16 rv; | |
U8 isOk = 0; | |
#ifndef A71_IGNORE_PARAM_CHECK | |
if ((updateBytes == NULL) || (invocationCount == NULL)) { | |
return ERR_API_ERROR; | |
} | |
#endif | |
if (updateBytesLen > (U16)0x00FF) | |
{ | |
return ERR_API_ERROR; | |
} | |
*invocationCount = 0; | |
pApdu->cla = A71CH_CLA; | |
pApdu->ins = A71CH_INS_SIGN_HASH_ECC_KEYPAIR; | |
pApdu->p1 = index; | |
pApdu->p2 = 0x00; | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
smApduAppendCmdData(pApdu, updateBytes, updateBytesLen); | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
U8 result = 0x00; | |
U16 resultLen = 1; | |
rv = smGetSw(pApdu, &isOk); | |
if (isOk) | |
{ | |
rv = smApduGetResponseBody(pApdu, &result, &resultLen); | |
if ((rv != SW_OK) || (resultLen != 1)) | |
{ | |
rv = ERR_WRONG_RESPONSE; | |
} | |
else | |
{ | |
*invocationCount = result; | |
} | |
} | |
} | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} | |
/** | |
* Verifies whether \p pSignature is the signature of \p pHash | |
* using the public key stored under \p index as the verifying public key. | |
* | |
* The index refers to an instance of the PUBLIC_KEY secure storage class on the A71CH. | |
* \note The public key of an ECC key pair cannot be used for a verify operation. | |
* \note ::A71_EccVerifyWithKey allows to pass the value of the public key rather than use a stored public key. | |
* | |
* @param[in] index Storage index of the key used for the verification. | |
* @param[in] pHash Pointer to the provided hash (or any other bytestring). | |
* @param[in] hashLen Length of the provided hash (\p pHash). | |
* @param[in] pSignature Pointer to the provided signature. | |
* @param[in] signatureLen Length of the provided signature (\p pSignature) | |
* @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_EccVerify(SST_Index_t index, 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 ((pHash == NULL) || (pSignature == NULL) || (pResult == NULL)) { | |
return ERR_API_ERROR; | |
} | |
#endif | |
*pResult = 0; | |
pApdu->cla = A71CH_CLA; | |
pApdu->ins = A71CH_INS_VERIFY_SIG_ECC_PUBLIC_KEY; | |
pApdu->p1 = index; | |
pApdu->p2 = P2_PUBKEY_ABSENT; | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
smApduAppendCmdData(pApdu, pHash, hashLen); | |
smApduAppendCmdData(pApdu, pSignature, signatureLen); | |
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; | |
} | |
/** | |
* Generates and retrieves a shared secret ECC point \p pSharedSecret using the private key stored | |
* at \p index and a public key \p pOtherPublicKey passed as argument. | |
* @param[in] index to the key pair (private key to be used) | |
* @param[in] pOtherPublicKey Pointer to the given public key. | |
* @param[in] otherPublicKeyLen Length of the given public key. | |
* @param[in,out] pSharedSecret Pointer to the computed shared secret. | |
* @param[in,out] pSharedSecretLen Pointer to the length of the computed shared secret. | |
* @retval ::SW_OK Upon successful execution | |
*/ | |
U16 A71_EcdhGetSharedSecret(U8 index, const U8 *pOtherPublicKey, U16 otherPublicKeyLen, U8 *pSharedSecret, U16 *pSharedSecretLen) | |
{ | |
U16 rv; | |
U8 isOk = 0; | |
apdu_t apdu; | |
apdu_t * pApdu = (apdu_t *) &apdu; | |
#ifndef A71_IGNORE_PARAM_CHECK | |
if ((pOtherPublicKey == NULL) || (pSharedSecret == NULL) || (pSharedSecretLen == NULL)) { | |
return ERR_API_ERROR; | |
} | |
#endif | |
pApdu->cla = A71CH_CLA; | |
pApdu->ins = A71CH_INS_SHARED_SECRET_ECC_KEYPAIR; | |
pApdu->p1 = index; | |
pApdu->p2 = 0x00; | |
AllocateAPDUBuffer(pApdu); | |
SetApduHeader(pApdu, USE_STANDARD_APDU_LEN); | |
smApduAppendCmdData(pApdu, pOtherPublicKey, otherPublicKeyLen); | |
rv = (U16)scp_Transceive(NULL, pApdu, SCP_MODE); | |
if (rv == SMCOM_OK) | |
{ | |
rv = smGetSw(pApdu, &isOk); | |
if (isOk) | |
{ | |
rv = smApduGetResponseBody(pApdu, pSharedSecret, pSharedSecretLen); | |
} | |
} | |
FreeAPDUBuffer(pApdu); | |
return rv; | |
} |