| This article is only applicable to M33-TD flavor |
1. Article purpose[edit | edit source]
This article describes how keys and secrets are managed in STMicroelectronics implementation of TF-M. It only applies to STM32MP2 series with the M33-TD flavor
. The secrets presented in this page are the ones stored in OTP fuses.
2. Implementation[edit | edit source]
2.1. Required keys[edit | edit source]
TF-M requests several keys to be provisioned, see Platform Provisioning page. On OpenSTLinux, we have chosen their addresses in OTP mapping. For the provisioning of those keys, please check How_to_update_OTP page.
2.2. Builtin keys framework[edit | edit source]
In TF-M code, the keys are managed with the TF-M builtin keys framework. TF-M manages the OTP list through an enum tfm_otp_element_id_t. This list has been updated for STM32MP2 series in the file platform_otp_ids.h .
2.3. Device Tree mapping[edit | edit source]
The OTP definition (size and address) that corresponds to the defined mapping is done in the device tree files, in SoC or board files. For example in stm32mp251.dtsi for STM32MP25x lines
, in the bsec node, this is the description of the RPN OTP:
bsec: efuse@54000000 {
...
rpn_otp: otp9@24 {
#nvmem-cell-cells = <0>;
reg = <0x24 0x4>;
};
Then all the OTP fuses described in the bsec node are listed in an otp node, so that they can be used by the NVMEM framework:
otp {
compatible = "st,stm32mp2-otp";
nvmem-cells = <&implementation_id>,
<&rpn_otp>,
<&bootrom_config_9>,
<&id_otp>,
<&package_otp>,
<&hconf1_otp>,
<&stm32certif>,
<&tfm_fw_pkh>,
<&ddr_fw_pkh>,
<&ca35_fw_pkh>,
<&fip_edmk>,
<&iak>;
nvmem-cell-names = "implementation_id",
"rpn_otp",
"bootrom_config_9",
"id_otp",
"package_otp",
"hconf1_otp",
"stm32certif",
"tfm_fw_pkh",
"ddr_fw_pkh",
"ca35_fw_pkh",
"fip_edmk",
"iak";
status = "okay";
};
This follows the NVMEM bindings nvmem-consumer.yaml , similar to what is done in Linux.
2.4. NVMEM framework[edit | edit source]
To make the link with the awaited definitions of OTP by TF-M through enum tfm_otp_element_id_t and the description of OTP from device tree, an OTP/NVMEM framework is needed. Its entry point is the file otp.c . The function nvmem_dev_from_otp_id() makes the correspondance between the enum and the OTP DT node. This file also calls the BSEC functions that will read and write the OTP fuses.
2.5. Usage[edit | edit source]
There is a dedicated management of the IAK (requested for Initial Attestation service) and HUK. This is done in crypto_keys.c . The HUK is not directly used, it is derived thanks to SAES peripheral.
2.6. Tests[edit | edit source]
By default, the flag TFM_DUMMY_PROVISIONING is set to ON in platform/ext/target/stm/common/stm32mp2xx/config.cmake file, to allow testing the feature without burning the OTP fuse. You should then update tfm_psa_rot_provisioning_data_t structure and stm32_set_default_value() function to manage the dummy version of your secret. For production software this flag must be set to OFF.
3. How to manage new keys[edit | edit source]
This chapter will describe how to add a new key or secret in OTP, and how to manage it in TF-M.
First select in the OTP mapping where to place this secret. If this is a key or a secret, it should be in upper OTP region (after OTP word 256). If this is not a secret (an informative OTP, a public key hash...), this could be in middle OTP region.
As an example, we create a secret key (my_secret) of 256bit, starting at OTP word 268 and ending at word 275.
3.1. Device tree modification[edit | edit source]
In your board device tree file for TF-M secure, check the &bsec node exists. If not, create it and add the my_secret OTP description:
&bsec {
...
my_secret: otp268@430 {
#nvmem-cell-cells = <0>;
reg = <0x430 0x20>;
};
};
Then, if it does not exist, create the otp node in your board device tree file. This should be a copy of the node from SoC device tree file (except compatible and status properties). And add my_secret to the list of OTP:
/ {
...
otp {
nvmem-cells = <&implementation_id>,
<&rpn_otp>,
<&bootrom_config_9>,
<&id_otp>,
<&package_otp>,
<&hconf1_otp>,
<&stm32certif>,
<&tfm_fw_pkh>,
<&ddr_fw_pkh>,
<&ca35_fw_pkh>,
<&fip_edmk>,
<&my_secret>,
<&iak>;
nvmem-cell-names = "implementation_id",
"rpn_otp",
"bootrom_config_9",
"id_otp",
"package_otp",
"hconf1_otp",
"stm32certif",
"tfm_fw_pkh",
"ddr_fw_pkh",
"ca35_fw_pkh",
"fip_edmk",
"my_secret",
"iak";
};
};
3.2. Code modification[edit | edit source]
First you will need a new entry in enum tfm_otp_element_id_t in the file platform_otp_ids.h , for example PLAT_OTP_MY_SECRET. This can be placed anywhere in the enum, but must be placed before PLAT_OTP_ID_MAX.
Then you need to update the function nvmem_dev_from_otp_id() in otp.c file adding this in the switch/case:
case PLAT_OTP_MY_SECRET: dev = DT_INST_DEV_NVMEM(0, my_secret); break;
After that, you can access the OTP content with the following functions:
err = tfm_plat_otp_read(PLAT_OTP_MY_SECRET, *size, buf); if(err != TFM_PLAT_ERR_SUCCESS) return err; err = tfm_plat_otp_get_size(PLAT_OTP_MY_SECRET, &otp_size); if (err != TFM_PLAT_ERR_SUCCESS) return err;
Several usage examples can be found in crypto_keys.c , attest_hal.c or bl2/banner.c