MCUXpresso_LPC55S69/boards/lpcxpresso55s69/aws_examples/shadow_wifi_serial/cm33_core0/aws_dev_mode_key_provisioni...

1284 lines
53 KiB
C

/*
* FreeRTOS V202203.00
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Copyright 2023 NXP
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file aws_dev_mode_key_provisioning.c
* @brief Simple key provisioning example using PKCS #11
*
* A simple example to demonstrate key and certificate provisioning in
* flash using PKCS #11 interface. This should be replaced
* by production ready key provisioning mechanism.
*/
/* Standard includes. */
#include <stdio.h>
#include <string.h>
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/* PKCS#11 includes. */
#include "core_pkcs11_config.h"
#include "core_pkcs11.h"
/* Client credential includes. */
#include "aws_clientcredential.h"
#include "aws_clientcredential_keys.h"
//#include "iot_default_root_certificates.h"
/* Key provisioning include. */
#include "aws_dev_mode_key_provisioning.h"
/* Utilities include. */
#include "core_pki_utils.h"
/* mbedTLS includes. */
#include "mbedtls/pk.h"
#include "mbedtls/oid.h"
/* Default FreeRTOS API for console logging. */
#define DEV_MODE_KEY_PROVISIONING_PRINT(X) vLoggingPrintf X
/* For writing log lines without a prefix. */
extern void vLoggingPrint(const char *pcFormat);
#define keyprovisioningDESTROY_OBJECT_SUPPORTED 0
/* Developer convenience override, for lab testing purposes, for generating
* a new default key pair, regardless of whether an existing key pair is present. */
#define keyprovisioningFORCE_GENERATE_NEW_KEY_PAIR 0
/* Delay before generating new key-pair, if keyprovisioningFORCE_GENERATE_NEW_KEY_PAIR
* is enabled. This is to avoid possible race-condition (due to devce reset) between
* execution of an existing image on device generates key-pair on device and flashing of
* new image on device. */
#ifndef keyprovisioningDELAY_BEFORE_KEY_PAIR_GENERATION_SECS
#define keyprovisioningDELAY_BEFORE_KEY_PAIR_GENERATION_SECS 180
#endif
/* Internal structure for parsing RSA keys. */
/* Length parameters for importing RSA-2048 private keys. */
#define MODULUS_LENGTH pkcs11RSA_2048_MODULUS_BITS / 8
#define E_LENGTH 3
#define D_LENGTH pkcs11RSA_2048_MODULUS_BITS / 8
#define PRIME_1_LENGTH 128
#define PRIME_2_LENGTH 128
#define EXPONENT_1_LENGTH 128
#define EXPONENT_2_LENGTH 128
#define COEFFICIENT_LENGTH 128
/* Adding one to all of the lengths because ASN1 may pad a leading 0 byte
* to numbers that could be interpreted as negative */
typedef struct RsaParams_t
{
CK_BYTE modulus[MODULUS_LENGTH + 1];
CK_BYTE e[E_LENGTH + 1];
CK_BYTE d[D_LENGTH + 1];
CK_BYTE prime1[PRIME_1_LENGTH + 1];
CK_BYTE prime2[PRIME_2_LENGTH + 1];
CK_BYTE exponent1[EXPONENT_1_LENGTH + 1];
CK_BYTE exponent2[EXPONENT_2_LENGTH + 1];
CK_BYTE coefficient[COEFFICIENT_LENGTH + 1];
} RsaParams_t;
/* Internal structure for capturing the provisioned state of the host device. */
typedef struct ProvisionedState_t
{
CK_OBJECT_HANDLE xPrivateKey;
CK_OBJECT_HANDLE xClientCertificate;
CK_OBJECT_HANDLE xPublicKey;
uint8_t *pucDerPublicKey;
uint32_t ulDerPublicKeyLength;
char *pcIdentifier; /* The token label. On some devices, a unique device
* ID might be stored here which can be used as a field
* in the subject of the device certificate. */
} ProvisionedState_t;
/* This function can be found in libraries/3rdparty/mbedtls_utils/mbedtls_utils.c. */
extern int convert_pem_to_der(const unsigned char *pucInput, size_t xLen, unsigned char *pucOutput, size_t *pxOlen);
/*-----------------------------------------------------------*/
/* Import the specified ECDSA private key into storage. */
static CK_RV prvProvisionPrivateECKey(CK_SESSION_HANDLE xSession,
uint8_t *pucLabel,
CK_OBJECT_HANDLE_PTR pxObjectHandle,
mbedtls_pk_context *pxMbedPkContext)
{
CK_RV xResult = CKR_OK;
CK_FUNCTION_LIST_PTR pxFunctionList = NULL;
CK_BYTE *pxD; /* Private value D. */
CK_BYTE *pxEcParams = NULL; /* DER-encoding of an ANSI X9.62 Parameters value */
int lMbedResult = 0;
CK_BBOOL xTrue = CK_TRUE;
CK_KEY_TYPE xPrivateKeyType = CKK_EC;
CK_OBJECT_CLASS xPrivateKeyClass = CKO_PRIVATE_KEY;
mbedtls_ecp_keypair *pxKeyPair = (mbedtls_ecp_keypair *)pxMbedPkContext->pk_ctx;
xResult = C_GetFunctionList(&pxFunctionList);
#define EC_PARAMS_LENGTH 10
#define EC_D_LENGTH 32
pxD = pvPortMalloc(EC_D_LENGTH);
if ((pxD == NULL))
{
xResult = CKR_HOST_MEMORY;
}
if (xResult == CKR_OK)
{
lMbedResult = mbedtls_mpi_write_binary(&(pxKeyPair->d), pxD, EC_D_LENGTH);
if (lMbedResult != 0)
{
DEV_MODE_KEY_PROVISIONING_PRINT(("Failed to parse EC private key components. \r\n"));
xResult = CKR_ATTRIBUTE_VALUE_INVALID;
}
}
if (xResult == CKR_OK)
{
if (pxKeyPair->grp.id == MBEDTLS_ECP_DP_SECP256R1)
{
pxEcParams = (CK_BYTE *)("\x06\x08" MBEDTLS_OID_EC_GRP_SECP256R1);
}
else
{
xResult = CKR_CURVE_NOT_SUPPORTED;
}
}
if (xResult == CKR_OK)
{
CK_ATTRIBUTE xPrivateKeyTemplate[] = {{CKA_CLASS, NULL /* &xPrivateKeyClass*/, sizeof(CK_OBJECT_CLASS)},
{CKA_KEY_TYPE, NULL /* &xPrivateKeyType*/, sizeof(CK_KEY_TYPE)},
{CKA_LABEL, pucLabel, (CK_ULONG)strlen((const char *)pucLabel)},
{CKA_TOKEN, NULL /* &xTrue*/, sizeof(CK_BBOOL)},
{CKA_SIGN, NULL /* &xTrue*/, sizeof(CK_BBOOL)},
{CKA_EC_PARAMS, NULL /* pxEcParams*/, EC_PARAMS_LENGTH},
{CKA_VALUE, NULL /* pxD*/, EC_D_LENGTH}};
/* Aggregate initializers must not use the address of an automatic variable. */
/* See MSVC Compiler Warning C4221 */
xPrivateKeyTemplate[0].pValue = &xPrivateKeyClass;
xPrivateKeyTemplate[1].pValue = &xPrivateKeyType;
xPrivateKeyTemplate[3].pValue = &xTrue;
xPrivateKeyTemplate[4].pValue = &xTrue;
xPrivateKeyTemplate[5].pValue = pxEcParams;
xPrivateKeyTemplate[6].pValue = pxD;
xResult = pxFunctionList->C_CreateObject(xSession, (CK_ATTRIBUTE_PTR)&xPrivateKeyTemplate,
sizeof(xPrivateKeyTemplate) / sizeof(CK_ATTRIBUTE), pxObjectHandle);
}
if (pxD != NULL)
{
vPortFree(pxD);
}
return xResult;
}
/*-----------------------------------------------------------*/
/* Import the specified RSA private key into storage. */
static CK_RV prvProvisionPrivateRSAKey(CK_SESSION_HANDLE xSession,
uint8_t *pucLabel,
CK_OBJECT_HANDLE_PTR pxObjectHandle,
mbedtls_pk_context *pxMbedPkContext)
{
CK_RV xResult = CKR_OK;
CK_FUNCTION_LIST_PTR pxFunctionList = NULL;
int lMbedResult = 0;
CK_KEY_TYPE xPrivateKeyType = CKK_RSA;
mbedtls_rsa_context *xRsaContext = pxMbedPkContext->pk_ctx;
CK_OBJECT_CLASS xPrivateKeyClass = CKO_PRIVATE_KEY;
RsaParams_t *pxRsaParams = NULL;
CK_BBOOL xTrue = CK_TRUE;
xResult = C_GetFunctionList(&pxFunctionList);
pxRsaParams = pvPortMalloc(sizeof(RsaParams_t));
if (pxRsaParams == NULL)
{
xResult = CKR_HOST_MEMORY;
}
if (xResult == CKR_OK)
{
memset(pxRsaParams, 0, sizeof(RsaParams_t));
lMbedResult = mbedtls_rsa_export_raw(xRsaContext, pxRsaParams->modulus, MODULUS_LENGTH + 1, pxRsaParams->prime1,
PRIME_1_LENGTH + 1, pxRsaParams->prime2, PRIME_2_LENGTH + 1,
pxRsaParams->d, D_LENGTH + 1, pxRsaParams->e, E_LENGTH + 1);
if (lMbedResult != 0)
{
DEV_MODE_KEY_PROVISIONING_PRINT(("Failed to parse RSA private key components. \r\n"));
xResult = CKR_ATTRIBUTE_VALUE_INVALID;
}
/* Export Exponent 1, Exponent 2, Coefficient. */
lMbedResult |= mbedtls_mpi_write_binary((mbedtls_mpi const *)&xRsaContext->DP, pxRsaParams->exponent1,
EXPONENT_1_LENGTH + 1);
lMbedResult |= mbedtls_mpi_write_binary((mbedtls_mpi const *)&xRsaContext->DQ, pxRsaParams->exponent2,
EXPONENT_2_LENGTH + 1);
lMbedResult |= mbedtls_mpi_write_binary((mbedtls_mpi const *)&xRsaContext->QP, pxRsaParams->coefficient,
COEFFICIENT_LENGTH + 1);
if (lMbedResult != 0)
{
DEV_MODE_KEY_PROVISIONING_PRINT(
("Failed to parse RSA private key Chinese Remainder Theorem variables. \r\n"));
xResult = CKR_ATTRIBUTE_VALUE_INVALID;
}
}
if (xResult == CKR_OK)
{
/* When importing the fields, the pointer is incremented by 1
* to remove the leading 0 padding (if it existed) and the original field length is used */
CK_ATTRIBUTE xPrivateKeyTemplate[] = {{CKA_CLASS, NULL /* &xPrivateKeyClass */, sizeof(CK_OBJECT_CLASS)},
{CKA_KEY_TYPE, NULL /* &xPrivateKeyType */, sizeof(CK_KEY_TYPE)},
{CKA_LABEL, pucLabel, (CK_ULONG)strlen((const char *)pucLabel)},
{CKA_TOKEN, NULL /* &xTrue */, sizeof(CK_BBOOL)},
{CKA_SIGN, NULL /* &xTrue */, sizeof(CK_BBOOL)},
{CKA_MODULUS, pxRsaParams->modulus + 1, MODULUS_LENGTH},
{CKA_PRIVATE_EXPONENT, pxRsaParams->d + 1, D_LENGTH},
{CKA_PUBLIC_EXPONENT, pxRsaParams->e + 1, E_LENGTH},
{CKA_PRIME_1, pxRsaParams->prime1 + 1, PRIME_1_LENGTH},
{CKA_PRIME_2, pxRsaParams->prime2 + 1, PRIME_2_LENGTH},
{CKA_EXPONENT_1, pxRsaParams->exponent1 + 1, EXPONENT_1_LENGTH},
{CKA_EXPONENT_2, pxRsaParams->exponent2 + 1, EXPONENT_2_LENGTH},
{CKA_COEFFICIENT, pxRsaParams->coefficient + 1, COEFFICIENT_LENGTH}};
/* Aggregate initializers must not use the address of an automatic variable. */
/* See MSVC Compiler Warning C4221 */
xPrivateKeyTemplate[0].pValue = &xPrivateKeyClass;
xPrivateKeyTemplate[1].pValue = &xPrivateKeyType;
xPrivateKeyTemplate[3].pValue = &xTrue;
xPrivateKeyTemplate[4].pValue = &xTrue;
xResult = pxFunctionList->C_CreateObject(xSession, (CK_ATTRIBUTE_PTR)&xPrivateKeyTemplate,
sizeof(xPrivateKeyTemplate) / sizeof(CK_ATTRIBUTE), pxObjectHandle);
}
if (NULL != pxRsaParams)
{
vPortFree(pxRsaParams);
}
return xResult;
}
/*-----------------------------------------------------------*/
/* Import the specified private key into storage. */
CK_RV xProvisionPrivateKey(CK_SESSION_HANDLE xSession,
uint8_t *pucPrivateKey,
size_t xPrivateKeyLength,
uint8_t *pucLabel,
CK_OBJECT_HANDLE_PTR pxObjectHandle)
{
CK_RV xResult = CKR_OK;
mbedtls_pk_type_t xMbedKeyType = MBEDTLS_PK_NONE;
int lMbedResult = 0;
mbedtls_pk_context xMbedPkContext = {0};
mbedtls_pk_init(&xMbedPkContext);
lMbedResult = mbedtls_pk_parse_key(&xMbedPkContext, pucPrivateKey, xPrivateKeyLength, NULL, 0);
if (lMbedResult != 0)
{
DEV_MODE_KEY_PROVISIONING_PRINT(("Unable to parse private key.\r\n"));
xResult = CKR_ARGUMENTS_BAD;
}
/* Determine whether the key to be imported is RSA or EC. */
if (xResult == CKR_OK)
{
xMbedKeyType = mbedtls_pk_get_type(&xMbedPkContext);
if (xMbedKeyType == MBEDTLS_PK_RSA)
{
xResult = prvProvisionPrivateRSAKey(xSession, pucLabel, pxObjectHandle, &xMbedPkContext);
}
else if ((xMbedKeyType == MBEDTLS_PK_ECDSA) || (xMbedKeyType == MBEDTLS_PK_ECKEY) ||
(xMbedKeyType == MBEDTLS_PK_ECKEY_DH))
{
xResult = prvProvisionPrivateECKey(xSession, pucLabel, pxObjectHandle, &xMbedPkContext);
}
else
{
DEV_MODE_KEY_PROVISIONING_PRINT(
("Invalid private key type provided. RSA-2048 and EC P-256 keys are supported.\r\n"));
xResult = CKR_ARGUMENTS_BAD;
}
}
mbedtls_pk_free(&xMbedPkContext);
return xResult;
}
/*-----------------------------------------------------------*/
/* Import the specified public key into storage. */
CK_RV xProvisionPublicKey(CK_SESSION_HANDLE xSession,
uint8_t *pucKey,
size_t xKeyLength,
CK_KEY_TYPE xPublicKeyType,
uint8_t *pucPublicKeyLabel,
CK_OBJECT_HANDLE_PTR pxPublicKeyHandle)
{
CK_RV xResult;
CK_BBOOL xTrue = CK_TRUE;
CK_FUNCTION_LIST_PTR pxFunctionList;
CK_OBJECT_CLASS xClass = CKO_PUBLIC_KEY;
int lMbedResult = 0;
mbedtls_pk_context xMbedPkContext = {0};
xResult = C_GetFunctionList(&pxFunctionList);
mbedtls_pk_init(&xMbedPkContext);
/* Try parsing the private key using mbedtls_pk_parse_key. */
lMbedResult = mbedtls_pk_parse_key(&xMbedPkContext, pucKey, xKeyLength, NULL, 0);
/* If mbedtls_pk_parse_key didn't work, maybe the private key is not included in the input passed in.
* Try to parse just the public key. */
if (lMbedResult != 0)
{
lMbedResult = mbedtls_pk_parse_public_key(&xMbedPkContext, pucKey, xKeyLength);
}
if (lMbedResult != 0)
{
DEV_MODE_KEY_PROVISIONING_PRINT(("Failed to parse the public key. \r\n"));
xResult = CKR_ARGUMENTS_BAD;
}
if ((xResult == CKR_OK) && (xPublicKeyType == CKK_RSA))
{
CK_BYTE xPublicExponent[] = {0x01, 0x00, 0x01};
CK_BYTE xModulus[MODULUS_LENGTH + 1] = {0};
lMbedResult = mbedtls_rsa_export_raw((mbedtls_rsa_context *)xMbedPkContext.pk_ctx, (unsigned char *)&xModulus,
MODULUS_LENGTH + 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0);
CK_ATTRIBUTE xPublicKeyTemplate[] = {{CKA_CLASS, NULL /* &xClass */, sizeof(CK_OBJECT_CLASS)},
{CKA_KEY_TYPE, NULL /* &xPublicKeyType */, sizeof(CK_KEY_TYPE)},
{CKA_TOKEN, NULL /* &xTrue */, sizeof(xTrue)},
{CKA_MODULUS, NULL /* &xModulus + 1 */,
MODULUS_LENGTH}, /* Extra byte allocated at beginning for 0 padding. */
{CKA_VERIFY, NULL /* &xTrue */, sizeof(xTrue)},
{CKA_PUBLIC_EXPONENT, NULL /* xPublicExponent */, sizeof(xPublicExponent)},
{CKA_LABEL, pucPublicKeyLabel, strlen((const char *)pucPublicKeyLabel)}};
/* Aggregate initializers must not use the address of an automatic variable. */
/* See MSVC Compiler Warning C4221 */
xPublicKeyTemplate[0].pValue = &xClass;
xPublicKeyTemplate[1].pValue = &xPublicKeyType;
xPublicKeyTemplate[2].pValue = &xTrue;
xPublicKeyTemplate[3].pValue = &xModulus + 1;
xPublicKeyTemplate[4].pValue = &xTrue;
xPublicKeyTemplate[5].pValue = xPublicExponent;
xResult = pxFunctionList->C_CreateObject(xSession, (CK_ATTRIBUTE_PTR)xPublicKeyTemplate,
sizeof(xPublicKeyTemplate) / sizeof(CK_ATTRIBUTE), pxPublicKeyHandle);
}
else if ((xResult == CKR_OK) && (xPublicKeyType == CKK_EC))
{
CK_BYTE xEcParams[] = pkcs11DER_ENCODED_OID_P256;
size_t xLength;
CK_BYTE xEcPoint[256] = {0};
mbedtls_ecdsa_context *pxEcdsaContext = (mbedtls_ecdsa_context *)xMbedPkContext.pk_ctx;
/* DER encoded EC point. Leave 2 bytes for the tag and length. */
lMbedResult =
mbedtls_ecp_point_write_binary(&pxEcdsaContext->grp, &pxEcdsaContext->Q, MBEDTLS_ECP_PF_UNCOMPRESSED,
&xLength, xEcPoint + 2, sizeof(xEcPoint) - 2);
xEcPoint[0] = 0x04; /* Octet string. */
xEcPoint[1] = (CK_BYTE)xLength;
CK_ATTRIBUTE xPublicKeyTemplate[] = {{CKA_CLASS, NULL /* &xClass */, sizeof(xClass)},
{CKA_KEY_TYPE, NULL /* &xPublicKeyType */, sizeof(xPublicKeyType)},
{CKA_TOKEN, NULL /* &xTrue */, sizeof(xTrue)},
{CKA_VERIFY, NULL /* &xTrue */, sizeof(xTrue)},
{CKA_EC_PARAMS, NULL /* xEcParams */, sizeof(xEcParams)},
{CKA_EC_POINT, NULL /* xEcPoint */, xLength + 2},
{CKA_LABEL, pucPublicKeyLabel, strlen((const char *)pucPublicKeyLabel)}};
/* Aggregate initializers must not use the address of an automatic variable. */
/* See MSVC Compiler Warning C4221 */
xPublicKeyTemplate[0].pValue = &xClass;
xPublicKeyTemplate[1].pValue = &xPublicKeyType;
xPublicKeyTemplate[2].pValue = &xTrue;
xPublicKeyTemplate[3].pValue = &xTrue;
xPublicKeyTemplate[4].pValue = xEcParams;
xPublicKeyTemplate[5].pValue = xEcPoint;
xResult = pxFunctionList->C_CreateObject(xSession, (CK_ATTRIBUTE_PTR)xPublicKeyTemplate,
sizeof(xPublicKeyTemplate) / sizeof(CK_ATTRIBUTE), pxPublicKeyHandle);
}
else
{
xResult = CKR_ATTRIBUTE_VALUE_INVALID;
configPRINTF(("Invalid key type. Supported options are CKK_RSA and CKK_EC"));
}
mbedtls_pk_free(&xMbedPkContext);
return xResult;
}
/*-----------------------------------------------------------*/
/* Generate a new 2048-bit RSA key pair. Please note that C_GenerateKeyPair for
* RSA keys is not supported by the FreeRTOS mbedTLS PKCS #11 port. */
CK_RV xProvisionGenerateKeyPairRSA(CK_SESSION_HANDLE xSession,
uint8_t *pucPrivateKeyLabel,
uint8_t *pucPublicKeyLabel,
CK_OBJECT_HANDLE_PTR pxPrivateKeyHandle,
CK_OBJECT_HANDLE_PTR pxPublicKeyHandle)
{
CK_RV xResult;
CK_MECHANISM xMechanism = {CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0};
CK_FUNCTION_LIST_PTR pxFunctionList;
CK_ULONG xModulusBits = pkcs11RSA_2048_MODULUS_BITS;
CK_BYTE xPublicExponent[] = pkcs11RSA_PUBLIC_EXPONENT;
CK_BBOOL xTrue = CK_TRUE;
CK_ATTRIBUTE xPublicKeyTemplate[] = {{CKA_ENCRYPT, NULL /* &xTrue */, sizeof(xTrue)},
{CKA_VERIFY, NULL /* &xTrue */, sizeof(xTrue)},
{CKA_MODULUS_BITS, NULL /* &xModulusBits */, sizeof(xModulusBits)},
{CKA_PUBLIC_EXPONENT, NULL /* xPublicExponent */, sizeof(xPublicExponent)},
{CKA_LABEL, pucPublicKeyLabel, strlen((const char *)pucPublicKeyLabel)}};
/* Aggregate initializers must not use the address of an automatic variable. */
/* See MSVC Compiler Warning C4221 */
xPublicKeyTemplate[0].pValue = &xTrue;
xPublicKeyTemplate[1].pValue = &xTrue;
xPublicKeyTemplate[2].pValue = &xModulusBits;
xPublicKeyTemplate[3].pValue = &xPublicExponent;
CK_ATTRIBUTE xPrivateKeyTemplate[] = {{CKA_TOKEN, NULL /* &xTrue */, sizeof(xTrue)},
{CKA_PRIVATE, NULL /* &xTrue */, sizeof(xTrue)},
{CKA_DECRYPT, NULL /* &xTrue */, sizeof(xTrue)},
{CKA_SIGN, NULL /* &xTrue */, sizeof(xTrue)},
{CKA_LABEL, pucPrivateKeyLabel, strlen((const char *)pucPrivateKeyLabel)}};
/* Aggregate initializers must not use the address of an automatic variable. */
/* See MSVC Compiler Warning C4221 */
xPrivateKeyTemplate[0].pValue = &xTrue;
xPrivateKeyTemplate[1].pValue = &xTrue;
xPrivateKeyTemplate[2].pValue = &xTrue;
xPrivateKeyTemplate[3].pValue = &xTrue;
xResult = C_GetFunctionList(&pxFunctionList);
xResult = pxFunctionList->C_GenerateKeyPair(
xSession, &xMechanism, xPublicKeyTemplate, sizeof(xPublicKeyTemplate) / sizeof(CK_ATTRIBUTE),
xPrivateKeyTemplate, sizeof(xPrivateKeyTemplate) / sizeof(CK_ATTRIBUTE), pxPublicKeyHandle, pxPrivateKeyHandle);
return xResult;
}
/*-----------------------------------------------------------*/
/* Generate a new ECDSA key pair using curve P256. */
CK_RV xProvisionGenerateKeyPairEC(CK_SESSION_HANDLE xSession,
uint8_t *pucPrivateKeyLabel,
uint8_t *pucPublicKeyLabel,
CK_OBJECT_HANDLE_PTR pxPrivateKeyHandle,
CK_OBJECT_HANDLE_PTR pxPublicKeyHandle)
{
CK_RV xResult;
CK_MECHANISM xMechanism = {CKM_EC_KEY_PAIR_GEN, NULL_PTR, 0};
CK_FUNCTION_LIST_PTR pxFunctionList;
CK_BYTE xEcParams[] = pkcs11DER_ENCODED_OID_P256; /* prime256v1 */
CK_KEY_TYPE xKeyType = CKK_EC;
CK_BBOOL xTrue = CK_TRUE;
CK_ATTRIBUTE xPublicKeyTemplate[] = {{CKA_KEY_TYPE, NULL /* &xKeyType */, sizeof(xKeyType)},
{CKA_VERIFY, NULL /* &xTrue */, sizeof(xTrue)},
{CKA_EC_PARAMS, NULL /* xEcParams */, sizeof(xEcParams)},
{CKA_LABEL, pucPublicKeyLabel, strlen((const char *)pucPublicKeyLabel)}};
/* Aggregate initializers must not use the address of an automatic variable. */
/* See MSVC Compiler Warning C4221 */
xPublicKeyTemplate[0].pValue = &xKeyType;
xPublicKeyTemplate[1].pValue = &xTrue;
xPublicKeyTemplate[2].pValue = &xEcParams;
CK_ATTRIBUTE xPrivateKeyTemplate[] = {{CKA_KEY_TYPE, NULL /* &xKeyType */, sizeof(xKeyType)},
{CKA_TOKEN, NULL /* &xTrue */, sizeof(xTrue)},
{CKA_PRIVATE, NULL /* &xTrue */, sizeof(xTrue)},
{CKA_SIGN, NULL /* &xTrue */, sizeof(xTrue)},
{CKA_LABEL, pucPrivateKeyLabel, strlen((const char *)pucPrivateKeyLabel)}};
/* Aggregate initializers must not use the address of an automatic variable. */
/* See MSVC Compiler Warning C4221 */
xPrivateKeyTemplate[0].pValue = &xKeyType;
xPrivateKeyTemplate[1].pValue = &xTrue;
xPrivateKeyTemplate[2].pValue = &xTrue;
xPrivateKeyTemplate[3].pValue = &xTrue;
xResult = C_GetFunctionList(&pxFunctionList);
xResult = pxFunctionList->C_GenerateKeyPair(
xSession, &xMechanism, xPublicKeyTemplate, sizeof(xPublicKeyTemplate) / sizeof(CK_ATTRIBUTE),
xPrivateKeyTemplate, sizeof(xPrivateKeyTemplate) / sizeof(CK_ATTRIBUTE), pxPublicKeyHandle, pxPrivateKeyHandle);
return xResult;
}
/*-----------------------------------------------------------*/
/* Import the specified X.509 client certificate into storage. */
CK_RV xProvisionCertificate(CK_SESSION_HANDLE xSession,
uint8_t *pucCertificate,
size_t xCertificateLength,
uint8_t *pucLabel,
CK_OBJECT_HANDLE_PTR pxObjectHandle)
{
PKCS11_CertificateTemplate_t xCertificateTemplate;
CK_OBJECT_CLASS xCertificateClass = CKO_CERTIFICATE;
CK_CERTIFICATE_TYPE xCertificateType = CKC_X_509;
CK_FUNCTION_LIST_PTR pxFunctionList;
CK_RV xResult;
uint8_t *pucDerObject = NULL;
int32_t lConversionReturn = 0;
size_t xDerLen = 0;
CK_BBOOL xTokenStorage = CK_TRUE;
/* TODO: Subject is a required attribute.
* Currently, this field is not used by FreeRTOS ports,
* this should be updated so that subject matches proper
* format for future ports. */
CK_BYTE xSubject[] = "TestSubject";
/* Initialize the client certificate template. */
xCertificateTemplate.xObjectClass.type = CKA_CLASS;
xCertificateTemplate.xObjectClass.pValue = &xCertificateClass;
xCertificateTemplate.xObjectClass.ulValueLen = sizeof(xCertificateClass);
xCertificateTemplate.xSubject.type = CKA_SUBJECT;
xCertificateTemplate.xSubject.pValue = xSubject;
xCertificateTemplate.xSubject.ulValueLen = strlen((const char *)xSubject);
xCertificateTemplate.xValue.type = CKA_VALUE;
xCertificateTemplate.xValue.pValue = (CK_VOID_PTR)pucCertificate;
xCertificateTemplate.xValue.ulValueLen = (CK_ULONG)xCertificateLength;
xCertificateTemplate.xLabel.type = CKA_LABEL;
xCertificateTemplate.xLabel.pValue = (CK_VOID_PTR)pucLabel;
xCertificateTemplate.xLabel.ulValueLen = strlen((const char *)pucLabel);
xCertificateTemplate.xCertificateType.type = CKA_CERTIFICATE_TYPE;
xCertificateTemplate.xCertificateType.pValue = &xCertificateType;
xCertificateTemplate.xCertificateType.ulValueLen = sizeof(CK_CERTIFICATE_TYPE);
xCertificateTemplate.xTokenObject.type = CKA_TOKEN;
xCertificateTemplate.xTokenObject.pValue = &xTokenStorage;
xCertificateTemplate.xTokenObject.ulValueLen = sizeof(xTokenStorage);
xResult = C_GetFunctionList(&pxFunctionList);
/* Test for a valid certificate: 0x2d is '-', as in ----- BEGIN CERTIFICATE. */
if ((pucCertificate == NULL) || (pucCertificate[0] != 0x2d))
{
xResult = CKR_ATTRIBUTE_VALUE_INVALID;
}
if (xResult == CKR_OK)
{
/* Convert the certificate to DER format if it was in PEM. The DER key
* should be about 3/4 the size of the PEM key, so mallocing the PEM key
* size is sufficient. */
pucDerObject = pvPortMalloc(xCertificateTemplate.xValue.ulValueLen);
xDerLen = xCertificateTemplate.xValue.ulValueLen;
if (pucDerObject != NULL)
{
lConversionReturn = convert_pem_to_der(xCertificateTemplate.xValue.pValue,
xCertificateTemplate.xValue.ulValueLen, pucDerObject, &xDerLen);
if (0 != lConversionReturn)
{
xResult = CKR_ARGUMENTS_BAD;
}
}
else
{
xResult = CKR_HOST_MEMORY;
}
}
if (xResult == CKR_OK)
{
/* Set the template pointers to refer to the DER converted objects. */
xCertificateTemplate.xValue.pValue = pucDerObject;
xCertificateTemplate.xValue.ulValueLen = xDerLen;
}
#if (keyprovisioningDESTROY_OBJECT_SUPPORTED == 1)
/* Best effort clean-up of the existing object, if it exists. */
if (xResult == CKR_OK)
{
xDestroyProvidedObjects(xSession, &pucLabel, &xCertificateClass, 1);
}
#endif
/* Create an object using the encoded client certificate. */
if (xResult == CKR_OK)
{
configPRINTF(("Write certificate...\r\n"));
xResult = pxFunctionList->C_CreateObject(xSession, (CK_ATTRIBUTE_PTR)&xCertificateTemplate,
sizeof(xCertificateTemplate) / sizeof(CK_ATTRIBUTE), pxObjectHandle);
}
if (pucDerObject != NULL)
{
vPortFree(pucDerObject);
}
return xResult;
}
/*-----------------------------------------------------------*/
/* Delete the specified crypto object from storage. */
CK_RV xDestroyProvidedObjects(CK_SESSION_HANDLE xSession,
CK_BYTE_PTR *ppxPkcsLabels,
CK_OBJECT_CLASS *xClass,
CK_ULONG ulCount)
{
CK_RV xResult;
CK_FUNCTION_LIST_PTR pxFunctionList;
CK_OBJECT_HANDLE xObjectHandle;
CK_BYTE *pxLabel;
CK_ULONG uiIndex = 0;
xResult = C_GetFunctionList(&pxFunctionList);
for (uiIndex = 0; uiIndex < ulCount; uiIndex++)
{
pxLabel = ppxPkcsLabels[uiIndex];
xResult = xFindObjectWithLabelAndClass(xSession, (char *)pxLabel, strlen((char *)pxLabel), xClass[uiIndex],
&xObjectHandle);
while ((xResult == CKR_OK) && (xObjectHandle != CK_INVALID_HANDLE))
{
xResult = pxFunctionList->C_DestroyObject(xSession, xObjectHandle);
/* PKCS #11 allows a module to maintain multiple objects with the same
* label and type. The intent of this loop is to try to delete all of them.
* However, to avoid getting stuck, we won't try to find another object
* of the same label/type if the previous delete failed. */
if (xResult == CKR_OK)
{
xResult = xFindObjectWithLabelAndClass(xSession, (char *)pxLabel, strlen((char *)pxLabel),
xClass[uiIndex], &xObjectHandle);
}
else
{
break;
}
}
if (xResult == CKR_FUNCTION_NOT_SUPPORTED)
{
break;
}
}
return xResult;
}
/*-----------------------------------------------------------*/
/* Delete well-known crypto objects from storage. */
CK_RV xDestroyDefaultCryptoObjects(CK_SESSION_HANDLE xSession)
{
CK_RV xResult;
CK_BYTE *pxPkcsLabels[] = {(CK_BYTE *)pkcs11configLABEL_DEVICE_CERTIFICATE_FOR_TLS,
(CK_BYTE *)pkcs11configLABEL_CODE_VERIFICATION_KEY,
(CK_BYTE *)pkcs11configLABEL_DEVICE_PRIVATE_KEY_FOR_TLS,
(CK_BYTE *)pkcs11configLABEL_DEVICE_PUBLIC_KEY_FOR_TLS};
CK_OBJECT_CLASS xClass[] = {CKO_CERTIFICATE, CKO_PUBLIC_KEY, CKO_PRIVATE_KEY, CKO_PUBLIC_KEY};
xResult = xDestroyProvidedObjects(xSession, pxPkcsLabels, xClass, sizeof(xClass) / sizeof(CK_OBJECT_CLASS));
return xResult;
}
/*-----------------------------------------------------------*/
static CK_RV prvExportPublicKey(CK_SESSION_HANDLE xSession,
CK_OBJECT_HANDLE xPublicKeyHandle,
uint8_t **ppucDerPublicKey,
uint32_t *pulDerPublicKeyLength)
{
CK_RV xResult;
CK_FUNCTION_LIST_PTR pxFunctionList;
CK_KEY_TYPE xKeyType = 0;
CK_ATTRIBUTE xTemplate = {0};
uint8_t pucEcP256AsnAndOid[] = {0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00};
uint8_t pucUnusedKeyTag[] = {0x04, 0x41};
/* This variable is used only for its size. This gets rid of compiler warnings. */
(void)pucUnusedKeyTag;
xResult = C_GetFunctionList(&pxFunctionList);
/* Query the key type. */
if (CKR_OK == xResult)
{
xTemplate.type = CKA_KEY_TYPE;
xTemplate.pValue = &xKeyType;
xTemplate.ulValueLen = sizeof(xKeyType);
xResult = pxFunctionList->C_GetAttributeValue(xSession, xPublicKeyHandle, &xTemplate, 1);
}
/* Scope to ECDSA keys only, since there's currently no use case for
* onboard keygen and certificate enrollment for RSA. */
if ((CKR_OK == xResult) && (CKK_ECDSA == xKeyType))
{
/* Query the size of the public key. */
xTemplate.type = CKA_EC_POINT;
xTemplate.pValue = NULL;
xTemplate.ulValueLen = 0;
xResult = pxFunctionList->C_GetAttributeValue(xSession, xPublicKeyHandle, &xTemplate, 1);
/* Allocate a buffer large enough for the full, encoded public key. */
if (CKR_OK == xResult)
{
/* Add space for the full DER header. */
xTemplate.ulValueLen += sizeof(pucEcP256AsnAndOid) - sizeof(pucUnusedKeyTag);
*pulDerPublicKeyLength = xTemplate.ulValueLen;
/* Get a heap buffer. */
*ppucDerPublicKey = pvPortMalloc(xTemplate.ulValueLen);
/* Check for resource exhaustion. */
if (NULL == *ppucDerPublicKey)
{
xResult = CKR_HOST_MEMORY;
}
}
/* Export the public key. */
if (CKR_OK == xResult)
{
xTemplate.pValue = *ppucDerPublicKey + sizeof(pucEcP256AsnAndOid) - sizeof(pucUnusedKeyTag);
xTemplate.ulValueLen -= (sizeof(pucEcP256AsnAndOid) - sizeof(pucUnusedKeyTag));
xResult = pxFunctionList->C_GetAttributeValue(xSession, xPublicKeyHandle, &xTemplate, 1);
}
/* Prepend the full DER header. */
if (CKR_OK == xResult)
{
memcpy(*ppucDerPublicKey, pucEcP256AsnAndOid, sizeof(pucEcP256AsnAndOid));
}
}
/* Free memory if there was an error after allocation. */
if ((NULL != *ppucDerPublicKey) && (CKR_OK != xResult))
{
vPortFree(*ppucDerPublicKey);
*ppucDerPublicKey = NULL;
}
return xResult;
}
/* Determine which required client crypto objects are already present in
* storage. */
static CK_RV prvGetProvisionedState(CK_SESSION_HANDLE xSession, ProvisionedState_t *pxProvisionedState)
{
CK_RV xResult;
CK_FUNCTION_LIST_PTR pxFunctionList;
CK_SLOT_ID_PTR pxSlotId = NULL;
CK_ULONG ulSlotCount = 0;
CK_TOKEN_INFO xTokenInfo = {0};
unsigned int i = 0;
xResult = C_GetFunctionList(&pxFunctionList);
/* Check for a private key. */
if (CKR_OK == xResult)
{
xResult = xFindObjectWithLabelAndClass(xSession, pkcs11configLABEL_DEVICE_PRIVATE_KEY_FOR_TLS,
sizeof(pkcs11configLABEL_DEVICE_PRIVATE_KEY_FOR_TLS) - 1,
CKO_PRIVATE_KEY, &pxProvisionedState->xPrivateKey);
}
if ((CKR_OK == xResult) && (CK_INVALID_HANDLE != pxProvisionedState->xPrivateKey))
{
/* Check also for the corresponding public. */
xResult = xFindObjectWithLabelAndClass(xSession, pkcs11configLABEL_DEVICE_PUBLIC_KEY_FOR_TLS,
sizeof(pkcs11configLABEL_DEVICE_PUBLIC_KEY_FOR_TLS) - 1, CKO_PUBLIC_KEY,
&pxProvisionedState->xPublicKey);
}
if ((CKR_OK == xResult) && (CK_INVALID_HANDLE != pxProvisionedState->xPublicKey))
{
/* Export the public key. */
xResult = prvExportPublicKey(xSession, pxProvisionedState->xPublicKey, &pxProvisionedState->pucDerPublicKey,
&pxProvisionedState->ulDerPublicKeyLength);
}
/* Check for the client certificate. */
if (CKR_OK == xResult)
{
xResult = xFindObjectWithLabelAndClass(xSession, pkcs11configLABEL_DEVICE_CERTIFICATE_FOR_TLS,
sizeof(pkcs11configLABEL_DEVICE_CERTIFICATE_FOR_TLS) - 1,
CKO_CERTIFICATE, &pxProvisionedState->xClientCertificate);
}
/* Check for a crypto element identifier. */
if (CKR_OK == xResult)
{
xResult = xGetSlotList(&pxSlotId, &ulSlotCount);
}
if (CKR_OK == xResult)
{
xResult = pxFunctionList->C_GetTokenInfo(pxSlotId[0], &xTokenInfo);
vPortFree(pxSlotId);
}
if ((CKR_OK == xResult) && ('\0' != xTokenInfo.label[0]) && (' ' != xTokenInfo.label[0]))
{
/* PKCS #11 requires that token info fields are padded out with space
* characters. However, a NULL terminated copy will be more useful to the
* caller. */
for (i = 0; i < sizeof(xTokenInfo.label); i++)
{
if (xTokenInfo.label[i] == ' ')
{
break;
}
}
if (0 != i)
{
pxProvisionedState->pcIdentifier = (char *)pvPortMalloc(1 + i * sizeof(xTokenInfo.label[0]));
if (NULL != pxProvisionedState->pcIdentifier)
{
memcpy(pxProvisionedState->pcIdentifier, xTokenInfo.label, i);
pxProvisionedState->pcIdentifier[i] = '\0';
}
else
{
xResult = CKR_HOST_MEMORY;
}
}
}
return xResult;
}
/*-----------------------------------------------------------*/
/* Write the ASN.1 encoded bytes of the device public key to the console.
* This is for debugging purposes as well as to faciliate developer-driven
* certificate enrollment for onboard crypto hardware (i.e. if available). */
static void prvWriteHexBytesToConsole(char *pcDescription, uint8_t *pucData, uint32_t ulDataLength)
{
#define BYTES_TO_DISPLAY_PER_ROW 16
char pcByteRow[1 + (BYTES_TO_DISPLAY_PER_ROW * 2) + (BYTES_TO_DISPLAY_PER_ROW / 2)];
char *pcNextChar = pcByteRow;
uint32_t ulIndex = 0;
uint8_t ucByteValue = 0;
/* Write help text to the console. */
configPRINTF(("%s, %d bytes:\r\n", pcDescription, ulDataLength));
/* Iterate over the bytes of the encoded public key. */
for (; ulIndex < ulDataLength; ulIndex++)
{
/* Convert one byte to ASCII hex. */
ucByteValue = *(pucData + ulIndex);
snprintf(pcNextChar, sizeof(pcByteRow) - (pcNextChar - pcByteRow), "%02x", ucByteValue);
pcNextChar += 2;
/* Check for the end of a two-byte display word. */
if (0 == ((ulIndex + 1) % sizeof(uint16_t)))
{
*pcNextChar = ' ';
pcNextChar++;
}
/* Check for the end of a row. */
if (0 == ((ulIndex + 1) % BYTES_TO_DISPLAY_PER_ROW))
{
*pcNextChar = '\0';
vLoggingPrint(pcByteRow);
vLoggingPrint("\r\n");
pcNextChar = pcByteRow;
}
}
/* Check for a partial line to print. */
if (pcNextChar > pcByteRow)
{
*pcNextChar = '\0';
vLoggingPrint(pcByteRow);
vLoggingPrint("\r\n");
}
}
/*-----------------------------------------------------------*/
/* Attempt to provision the device with a client certificate, associated
* private and public key pair, and optional Just-in-Time Registration certificate.
* If either component of the key pair is unavailable in storage, generate a new
* pair. */
CK_RV xProvisionDevice(CK_SESSION_HANDLE xSession, ProvisioningParams_t *pxParams)
{
CK_RV xResult;
CK_FUNCTION_LIST_PTR pxFunctionList;
ProvisionedState_t xProvisionedState = {0};
CK_OBJECT_HANDLE xObject = 0;
CK_BBOOL xImportedPrivateKey = CK_FALSE;
CK_BBOOL xKeyPairGenerationMode = CK_FALSE;
xResult = C_GetFunctionList(&pxFunctionList);
#if (pkcs11configIMPORT_PRIVATE_KEYS_SUPPORTED == 1) && (keyprovisioningDESTROY_OBJECT_SUPPORTED == 1)
/* Attempt to clean-up old crypto objects, but only if private key import is
* supported by this application, and only if the caller has provided new
* objects to use instead. */
if ((CKR_OK == xResult) && (NULL != pxParams->pucClientCertificate) && (NULL != pxParams->pucClientPrivateKey))
{
xResult = xDestroyDefaultCryptoObjects(xSession);
if (xResult != CKR_OK)
{
configPRINTF(("Warning: could not clean-up old crypto objects. %d \r\n", xResult));
}
}
#endif /* if ( pkcs11configIMPORT_PRIVATE_KEYS_SUPPORTED == 1 ) */
/* If a client certificate has been provided by the caller, attempt to
* import it. */
if ((xResult == CKR_OK) && (NULL != pxParams->pucClientCertificate))
{
xResult = xProvisionCertificate(xSession, pxParams->pucClientCertificate, pxParams->ulClientCertificateLength,
(uint8_t *)pkcs11configLABEL_DEVICE_CERTIFICATE_FOR_TLS, &xObject);
if ((xResult != CKR_OK) || (xObject == CK_INVALID_HANDLE))
{
configPRINTF(("ERROR: Failed to provision device certificate. %d \r\n", xResult));
}
}
#if (pkcs11configIMPORT_PRIVATE_KEYS_SUPPORTED == 1)
/* If this application supports importing private keys, and if a private
* key has been provided by the caller, attempt to import it. */
if ((xResult == CKR_OK) && (NULL != pxParams->pucClientPrivateKey))
{
xResult = xProvisionPrivateKey(xSession, pxParams->pucClientPrivateKey, pxParams->ulClientPrivateKeyLength,
(uint8_t *)pkcs11configLABEL_DEVICE_PRIVATE_KEY_FOR_TLS, &xObject);
if ((xResult != CKR_OK) || (xObject == CK_INVALID_HANDLE))
{
configPRINTF(("ERROR: Failed to provision device private key with status %d.\r\n", xResult));
}
else
{
xImportedPrivateKey = CK_TRUE;
}
}
#endif /* if ( pkcs11configIMPORT_PRIVATE_KEYS_SUPPORTED == 1 ) */
/* If a Just-in-Time Provisioning certificate has been provided by the
* caller, attempt to import it. Not all crypto tokens
* and PKCS #11 module implementations provide storage for this particular
* object. In that case, the statically defined object, if any, will be used
* during TLS session negotiation with AWS IoT. */
if ((xResult == CKR_OK) && (NULL != pxParams->pucJITPCertificate))
{
xResult = xProvisionCertificate(xSession, pxParams->pucJITPCertificate, pxParams->ulJITPCertificateLength,
(uint8_t *)pkcs11configLABEL_JITP_CERTIFICATE, &xObject);
if (xResult == CKR_DEVICE_MEMORY)
{
xResult = CKR_OK;
configPRINTF(
("Warning: no persistent storage is available for the JITP certificate. The certificate in "
"aws_clientcredential_keys.h will be used instead.\r\n"));
}
}
/* Check whether a key pair is now present. In order to support X.509
* certificate enrollment, the public and private key objects must both be
* available. */
if ((xResult == CKR_OK) && (CK_FALSE == xImportedPrivateKey))
{
xResult = prvGetProvisionedState(xSession, &xProvisionedState);
if ((CK_INVALID_HANDLE == xProvisionedState.xPrivateKey) ||
(CK_INVALID_HANDLE == xProvisionedState.xPublicKey) || (NULL == xProvisionedState.pucDerPublicKey))
{
xKeyPairGenerationMode = CK_TRUE;
}
/* Ignore errors, since the board may have been previously used with a
* different crypto middleware or app. If any of the above objects
* couldn't be read, try to generate new ones below. */
xResult = CKR_OK;
}
#if (1 == keyprovisioningFORCE_GENERATE_NEW_KEY_PAIR)
xKeyPairGenerationMode = CK_TRUE;
#endif
if ((xResult == CKR_OK) && (CK_TRUE == xKeyPairGenerationMode))
{
/* Add a delay before calling logic for generating new key-pair on device (if boards supports on-board key-pair
* generation) to avoid possible scenario of unexpectedly generating new keys on board during the flashing
* process of a new image on the board. If the flashing workflow of a device (for example, ESP32 boards)
* involves resetting the board before flashing a new image, then a race condition can occur between the
* execution of an already existing image on device (that is triggered by the device reset) and the flashing of
* the new image on the device. When the existing image present on the device is configured to generate new
* key-pair (through the keyprovisioningFORCE_GENERATE_NEW_KEY_PAIR config), then a possible scenario of
* unexpected key-pair generation on device can occur during flashing process, in which case, the certificate
* provisioned by user becomes stale and device cannot perform TLS connection with servers as the provisioned
* device certificate does not match the unexpectedly generated new key-pair. Thus, by adding a delay, the
* possibility of hitting the race condition of the device executing an old image that generates new key-pair is
* avoided because the logic of generating new key-pair is not executed before the flashing process starts
* loading the new image onto the board. Note: The delay of 150 seconds is used based on testing with an
* ESP32+ECC608A board. */
configPRINTF(("Waiting for %d seconds before generating key-pair",
keyprovisioningDELAY_BEFORE_KEY_PAIR_GENERATION_SECS));
vTaskDelay(pdMS_TO_TICKS(keyprovisioningDELAY_BEFORE_KEY_PAIR_GENERATION_SECS * 1000));
/* Generate a new default key pair. */
xResult = xProvisionGenerateKeyPairEC(xSession, (uint8_t *)pkcs11configLABEL_DEVICE_PRIVATE_KEY_FOR_TLS,
(uint8_t *)pkcs11configLABEL_DEVICE_PUBLIC_KEY_FOR_TLS,
&xProvisionedState.xPrivateKey, &xProvisionedState.xPublicKey);
if (CKR_OK == xResult)
{
/* Clean-up the previous buffer, if any. */
if (NULL != xProvisionedState.pucDerPublicKey)
{
vPortFree(xProvisionedState.pucDerPublicKey);
xProvisionedState.pucDerPublicKey = NULL;
}
/* Get the bytes of the new public key. */
prvExportPublicKey(xSession, xProvisionedState.xPublicKey, &xProvisionedState.pucDerPublicKey,
&xProvisionedState.ulDerPublicKeyLength);
}
/* Ensure that an error condition is set if either object is still
* missing. */
if ((CKR_OK == xResult) && ((CK_INVALID_HANDLE == xProvisionedState.xPrivateKey) ||
(CK_INVALID_HANDLE == xProvisionedState.xPublicKey)))
{
xResult = CKR_KEY_HANDLE_INVALID;
}
}
/* Log the device public key if one exists for developer convenience.
* This can be useful for verifying that the provisioned certificate for the device
* matches the public key on the device. */
if (CK_INVALID_HANDLE != xProvisionedState.xPublicKey)
{
configPRINTF(
("Printing device public key.\nMake sure that provisioned device certificate matches public key on "
"device."));
prvWriteHexBytesToConsole("Device public key", xProvisionedState.pucDerPublicKey,
xProvisionedState.ulDerPublicKeyLength);
}
/* Log the device public key for developer enrollment purposes, but only if
* there's not already a certificate, or if a new key was just generated. */
if ((CKR_OK == xResult) &&
((CK_INVALID_HANDLE == xProvisionedState.xClientCertificate) || (CK_TRUE == xKeyPairGenerationMode)) &&
(CK_FALSE == xImportedPrivateKey))
{
configPRINTF(
("Warning: the client certificate should be updated. Please see "
"https://aws.amazon.com/freertos/getting-started/.\r\n"));
if (NULL != xProvisionedState.pcIdentifier)
{
configPRINTF(("Recommended certificate subject name: CN=%s\r\n", xProvisionedState.pcIdentifier));
}
}
/* Free memory. */
if (NULL != xProvisionedState.pucDerPublicKey)
{
vPortFree(xProvisionedState.pucDerPublicKey);
}
if (NULL != xProvisionedState.pcIdentifier)
{
vPortFree(xProvisionedState.pcIdentifier);
}
return xResult;
}
/*-----------------------------------------------------------*/
/* Perform device provisioning using the specified TLS client credentials. */
CK_RV vAlternateKeyProvisioning(ProvisioningParams_t *xParams)
{
CK_RV xResult = CKR_OK;
CK_FUNCTION_LIST_PTR pxFunctionList = NULL;
CK_SESSION_HANDLE xSession = 0;
xResult = C_GetFunctionList(&pxFunctionList);
/* Initialize the PKCS Module */
if (xResult == CKR_OK)
{
xResult = xInitializePkcs11Token();
}
if (xResult == CKR_OK)
{
xResult = xInitializePkcs11Session(&xSession);
}
if (xResult == CKR_OK)
{
xResult = xProvisionDevice(xSession, xParams);
pxFunctionList->C_CloseSession(xSession);
}
return xResult;
}
/*-----------------------------------------------------------*/
/* Perform device provisioning using the default TLS client credentials. */
CK_RV vDevModeKeyProvisioning(void)
{
ProvisioningParams_t xParams;
xParams.pucJITPCertificate = (uint8_t *)keyJITR_DEVICE_CERTIFICATE_AUTHORITY_PEM;
xParams.pucClientPrivateKey = (uint8_t *)keyCLIENT_PRIVATE_KEY_PEM;
xParams.pucClientCertificate = (uint8_t *)keyCLIENT_CERTIFICATE_PEM;
/* If using a JITR flow, a JITR certificate must be supplied. If using credentials generated by
* AWS, this certificate is not needed. */
if ((NULL != xParams.pucJITPCertificate) && (0 != strcmp("", (const char *)xParams.pucJITPCertificate)))
{
/* We want the NULL terminator to be written to storage, so include it
* in the length calculation. */
xParams.ulJITPCertificateLength = sizeof(char) + strlen((const char *)xParams.pucJITPCertificate);
}
else
{
xParams.pucJITPCertificate = NULL;
}
/* The hard-coded client certificate and private key can be useful for
* first-time lab testing. They are optional after the first run, though, and
* not recommended at all for going into production. */
if ((NULL != xParams.pucClientPrivateKey) && (0 != strcmp("", (const char *)xParams.pucClientPrivateKey)))
{
/* We want the NULL terminator to be written to storage, so include it
* in the length calculation. */
xParams.ulClientPrivateKeyLength = sizeof(char) + strlen((const char *)xParams.pucClientPrivateKey);
}
else
{
xParams.pucClientPrivateKey = NULL;
}
if ((NULL != xParams.pucClientCertificate) && (0 != strcmp("", (const char *)xParams.pucClientCertificate)))
{
/* We want the NULL terminator to be written to storage, so include it
* in the length calculation. */
xParams.ulClientCertificateLength = sizeof(char) + strlen((const char *)xParams.pucClientCertificate);
}
else
{
xParams.pucClientCertificate = NULL;
}
return vAlternateKeyProvisioning(&xParams);
}
/*-----------------------------------------------------------*/
/* Perform device provisioning using public key for code signature verification. */
CK_RV vCodeVerifyPubKeyProvisioning()
{
CK_RV xResult = CKR_OK;
CK_FUNCTION_LIST_PTR pxFunctionList = NULL;
CK_SESSION_HANDLE xSession = 0;
CK_OBJECT_HANDLE xObject = 0;
xResult = C_GetFunctionList(&pxFunctionList);
/* Initialize the PKCS Module */
if (xResult == CKR_OK)
{
xResult = xInitializePkcs11Token();
}
if (xResult == CKR_OK)
{
xResult = xInitializePkcs11Session(&xSession);
}
if (xResult == CKR_OK)
{
xResult = xProvisionPublicKey(xSession, (uint8_t *)keyCODE_VERIFY_PUB_KEY_PEM, sizeof(keyCODE_VERIFY_PUB_KEY_PEM), CKK_EC,
(uint8_t *)pkcs11configLABEL_CODE_VERIFICATION_KEY, &xObject);
if ((xResult != CKR_OK) || (xObject == CK_INVALID_HANDLE))
{
configPRINTF(("ERROR: Failed to provision public key for code signature verification with status %d.\r\n", xResult));
}
pxFunctionList->C_CloseSession(xSession);
}
return xResult;
}
/*-----------------------------------------------------------*/