/* * FreeRTOS V202203.00 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * 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 ); /* 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; } /* Best effort clean-up of the existing object, if it exists. */ if( xResult == CKR_OK ) { xDestroyProvidedObjects( xSession, &pucLabel, &xCertificateClass, 1 ); } /* 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 ) /* 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 ); } /*-----------------------------------------------------------*/