/* * 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 #include /* 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; } /*-----------------------------------------------------------*/