Device provisioning is composed of sequential steps, The main idea is to provide a unique identity for each device along with storing the authenticated identity in a secure space to safeguard against potential attacks by adversaries. In the context of the AWS IoT Core, these take the form of an AWS IoT client certificate stored in the device's protected storage and a private key imported to PSA Crypto.
The device provisioning binary (provisioning_data.bin) is built from a C source file that contains an instance of a provisioning bundle struct (ProvisioningParamsBundle_t) as shown below:
const ProvisioningParamsBundle_t provisioningBundle =
{
.provisioningMagic1 = PROVISIONING_MAGIC,
.provisioningParams =
{
.pucJITPCertificate = keyJITR_DEVICE_CERTIFICATE_AUTHORITY_PEM,
.pucClientCertificate = keyCLIENT_CERTIFICATE_PEM,
.pucClientPrivateKey = keyCLIENT_PRIVATE_KEY_PEM
},
.codeSigningPublicKey = keyCODE_SIGNING_PUBLIC_KEY_PEM,
.provisioningMagic2 = PROVISIONING_MAGIC
};-
PROVISIONING_MAGIC: a configurable value that can be set in provisioning_config.h header file, thePROVISIONING_MAGICvalue is checked at the beginning of the provisioning process to make sure that the provided bundle parameters are genuine. -
keyJITR_DEVICE_CERTIFICATE_AUTHORITY_PEM: contents of a *.pem file representing a Just-in-Time Registration of Device Certificate. This is not supported by the FRI as device certificates are generated prior to connecting to AWS IoT Core, hence this value is defined as an empty string. -
keyCLIENT_CERTIFICATE_PEM: contents of<certificate-name>.pemfile passed as part of the build command (--certificate_path <path-to-certificate-name.pem>). This is needed as AWS IoT Core authenticates device connections with the help of X.509 certificates. For more information on how to generate a X.509 certificate, please check setting_up_aws_connectivity.md document. -
keyCLIENT_PRIVATE_KEY_PEM: contents of<private-key-name>.pemfile passed as part of the build command (--private_key_path <path-to-private-key-name.pem>). This is used along with the certificate to validate the identity of the device. -
keyCODE_SIGNING_PUBLIC_KEY_PEM: contents ofimage_ns_signing_public_key.pemwhich is the public key component of the key pair generated by TF-M during the build process. This is needed to validate the authenticity of an OTA update image by verifying the associated signature. -
keyJITR_DEVICE_CERTIFICATE_AUTHORITY_PEM,keyCLIENT_CERTIFICATE_PEM,keyCLIENT_PRIVATE_KEY_PEM, andkeyCODE_SIGNING_PUBLIC_KEY_PEMare used to generate the credentials header during the build process using generate_credentials_header.py Python script. The credentials header can be generated manually using the following command from the project's top level directory:python3 ./tools/scripts/generate_credentials_header.py \ "<build_path>/helpers/provisioning" \ --path-to-client-private-key-pem "<path-to-private-key-name.pem>" \ --path-to-client-certificate-pem "<path-to-certificate-name.pem>" \ --path-to-code-signing-public-key-pem "$TFM_KEYS_PATH/image_ns_signing_public_key.pem"where
$TFM_KEYS_PATHis the path TF-M keys are generated in ($BUILD_PATH/iot_reference_arm_corstone3xx/components/security/trusted_firmware-m/integration/trusted_firmware-m-build-prefix/src/trusted_firmware-m-build-build/api_ns/image_signing/keys/).
The provisioning binary provisioning_data.bin is built into <build_path>/helpers/provisioning subdirectory where the binary is used to provision the device with the client certificate, associated private and public key pair, and the optional Just-in-Time registration certificate. If the private or public key is unavailable in storage, a new key pair is generated.
The provisioning_data.bin binary has to be loaded to NS_PROVISIONING_BUNDLE_LOAD_ADDRESS (platform dependant address specified at bsp/CMakeLists.txt) so that when the application runs, it detects that the non-secure provisioning bundle is present and writes the credentials into the protected storage. This step is done automatically as part of merging the output application binaries which takes place during the the build process through iot_reference_arm_corstone3xx_tf_m_merge_images CMake function.
This step can also be done manually at run time by using the following command:
FVP_Corstone_SSE-310 -a cpu0*=$TFM_BUILD_PATH/bl2.axf \
--data "$TFM_BUILD_PATH/tfm_s_signed.bin"@<S_IMAGE_LOAD_ADDRESS> \
--data "$BUILD_PATH/<application-name>_signed.bin"@<NS_IMAGE_LOAD_ADDRESS> \
--data "$TFM_BUILD_PATH/encrypted_provisioning_bundle.bin"@<S_PROVISIONING_BUNDLE_LOAD_ADDRESS> \
--data "$BUILD_PATH/helpers/provisioning/provisioning_data.bin"@<NS_PROVISIONING_BUNDLE_LOAD_ADDRESS>$BUILD_PATH is the path which is passed through --path option as part of the build command.
$TFM_BUILD_PATH is the path where TF-M binaries are generated ($BUILD_PATH/iot_reference_arm_corstone3xx/components/security/trusted_firmware-m/integration/trusted_firmware-m-build-prefix/src/trusted_firmware-m-build-build/api_ns/bin).
Note: If the contents of the certificate or private key
.pemfiles used for building the application (passed with--certificate_pathand--private_key_path) have changed, rebuild the non-secure provisioning binary using the following command:sh cmake --build build -j -- provisioning_data