1. Article purpose[edit | edit source]
This article explains how to protect the software loaded by one processor into the second processor and ensure the authentication of the loaded firmware.
![]() |
The protection of the second processor firmware by authentication is enabled by default on the STM32MP2 series. |
2. Introduction[edit | edit source]
The Linux® OS, (through the remoteproc framework) or the U-Boot enables the loading of firmware into control remote processors. Thanks to a specific OP-TEE trusted application (TA) and the resource isolation framework running on the Arm® TrustZone, it is possible to authenticate a Cortex®-M firmware image and install it on isolated RAMs to ensure its integrity during execution.
From the user's point of view, the management of a nonauthenticated or authenticated firmware does not differ, whether the firmware is managed by Linux or by U-Boot. The difference is that the management of the authenticated firmware is delegated to the OP-TEE firmware running in the Arm® TrustZone.
3. Principles[edit | edit source]
The protection of the second processor firmware relies on hash computation and on the encryption key to authenticate a firmware, as does the isolation of the execution memories to ensure execution integrity.
The firmware is signed using an OP-TEE signature script. This script concatenates one or several ELF binaries, then adds a header and TLV (type length value) metadata that provide elements for the authentication among other firmware information:
- A hash table, which contains the hash of the segment to load.
- A signature computed with the private key.
- During the execution phase:
- The signed firmware binary is read from the file system by the remoteproc framework and the image is provided to the OP-TEE trusted application.
- The firmware image is authenticated and installed by the OP-TEE trusted application in the protected Cortex®-M memories.
3.1. Firmware images signature[edit | edit source]
A common strategy to sign firmware consists of adding a header that contains the signature required for authentication. The signature is generated by computing a hash on the whole binary including the header, and encrypting this hash using a private key.
On the STM32MP microprocessors, the signature procedure is similar except that it is optimized to minimize memory usage and speed up the authentication process. The principle is to compute the hash of the segments to load, instead of computing the hash of the full image.
- The signature consists of:
- Parsing the program table in the ELF file to identify the segments to load.
- Computing the hash of each segment and saving the result in the hash table stored in the TLV chunk.
- Computing the hash of the header and the TLV and signing the hash with the private key.
- The public key can be:
- embedded in the OP-TEE OS firmware, which is responsible for authenticating the Cortex®-M firmware images.
- provided with the signed image during the signing process. OP-TEE verifies the public key using a hash stored in OTP registers (ecosystem release ≥ v6.1.0
).
- Supported key format:
- RSA
- ECC
3.2. Firmware images encryption (STM32MP2 series only)[edit | edit source]
It is possible to encrypt the firmware images as a pre-processing step before the signature. Based on an AES key, the encryption procedure involves encrypting the ELF segments to be loaded into the Cortex®-M memory before computing the firmware signature.
On the device, OP-TEE will authenticate the firmware image and decrypt the segments after copying them into the destination memory.
Supported key format: AES 256.
3.3. Memory management[edit | edit source]
3.3.1. STM32MP15x lines
[edit | edit source]
The MCU SRAM and the RETRAM memories can be dedicated to Cortex®-M usage (isolated), shared with the Cortex®-A nonsecure context, or protected for Cortex®-A secure access only.
The ETZPC mechanism is used:
- During the load and authentication phase: to lock the memories for Cortex®-A secure access only.
- During the execution phase: to isolate the memories for Cortex®-M usage (such as code execution and data) or share them with the Cortex®-A nonsecure context (such as RPMsg shared buffers).
- On Cortex®-M stop: to lock the memories for Cortex®-A secure access only to clean up the memories.
![]() |
When creating a Cortex®-M firmware build, developers must be careful to pack all data shared with the Cortex®-A (for example, resource table and trace buffer) in the same memory bank. This enables isolation of the rest of the memory banks for the Cortex®-M context. |
The information provided respects the memory mapping described in memory mapping:
- RETRAM, MCU SRAM1, and MCU SRAM2 are used for code execution and data. They are only accessible by the Cortex®-A secure context when no firmware is running, and assigned to Cortex®-M context during authenticated firmware execution.
- MCU SRAM3 (IPC buffers) is shared between the Cortex®-A nonsecure context and Cortex®-M. No protection is applied to this memory bank.
- MCU SRAM4 is reserved for the Cortex®-A nonsecure context and is free for other usage.
This memory mapping can be customized depending on project requirements. In this case, the code must be updated synchronously in STM32CubeMP15 firmware, Linux kernel device tree, and the OP-TEE rproc PTA code.
3.3.2. STM32MP2 series[edit | edit source]
The DDR, SRAM, and/or RETRAM memory regions can be dedicated to Cortex®-M usage (isolated), shared with the Cortex®-A nonsecure context, or protected for Cortex®-A secure access only.
The RIF is used:
- During the load and authentication phase: to lock the memories for Cortex®-A secure access only.
- During the execution phase: to apply the access rights defined in the OP-TEE device tree to isolate the memories for Cortex®-M usage.
- On Cortex®-M stop: to lock the memories for Cortex®-A secure access only to clean up the memories.
![]() |
When creating a Cortex®-M firmware build, developers must be careful to pack all data shared with the Cortex®-A (e.g., resource table and trace buffer) in the same memory bank. This enables isolation of the rest of the memory banks for the Cortex®-M context. |
The information respects the memory mapping described in memory mapping:
- Some DDR and/or MCU RAM memory regions are prereserved for code execution and data. They are only accessible by the Cortex®-M secure or nonsecure contexts thanks to the RIF CID filtering.
- A dedicated DDR and/or MCU RAM memory region is shared between the Cortex®-A nonsecure context and Cortex®-M. No protection is applied to this memory bank.
This memory mapping can be customized depending on project requirements. In this case, the code must be updated synchronously in the STM32CubeMP2, the TF-M firmware, the Linux kernel device tree, and the OP-TEE device tree code.
3.4. Authentication[edit | edit source]
The authentication is executed in the secure context by an OP-TEE trusted application.
1. The firmware is copied by the Linux kernel (or U-Boot) to nonsecure DDR memory.
2. The firmware header, signature, and TLV are copied to secure memory to ensure their integrity during the authentication steps.
3. The hash of the header and the TLV is computed.
4. The signature is decrypted using the public key. The result is the hash of the header and the TLV computed by the signing tools. Both hashes are compared to authenticate the firmware header.
5. At this step, the firmware header is valid. The next step, loading the firmware into Cortex®-M memories, can start:
For each segment to load:
- 6. Based on information stored in the hash table, the segment is copied to the MCU execution RAMs.
- 7. The hash of the copied segment is computed and compared to the hash stored in the hash table.
- 8. If the segment has been encrypted, it is decrypted.
- 9. At this step, all segments have been copied and authenticated. The firmware is loaded and ready for execution.
![]() |
During the firmware load sequence, the MCU RAMs can be protected to ensure their integrity by limiting access only to the secure context. See OP-TEE device tree chapter for details. |
3.5. Authenticated firmware live-cycle[edit | edit source]
From the user's point of view, the management of a firmware live-cycle does not change, whether the firmware is managed by OP-TEE or not. The management of firmware is described in the remoteproc article.
The main difference compared to a nonauthenticated firmware is that the Linux remote processor framework delegates Cortex®-M management to OP-TEE.
![]() |
Since authentication is activated, it is no longer possible to load and start a nonauthenticated firmware. |
![]() |
The authenticated firmware can also be preloaded and started by U-Boot. |
3.5.1. Firmware loading and authentication[edit | edit source]
- As with nonauthenticated firmware, the firmware is read from the file system by the remoteproc framework. The image is then sent to the OP-TEE trusted application.
- The OP-TEE remoteproc trusted application manages authentication and memory isolation in the secure context.
3.5.2. Firmware start and stop[edit | edit source]
- As with nonauthenticated firmware, the firmware start and stop requests are handled by the remoteproc framework, but delegated to the Arm® TrustZone.
- The firmware start and stop are executed by OP-TEE. The reset and RAM access rights are accessible only by the secure context.
- During firmware loading, the Cortex®-M memories’ access rights are updated to provide access to the Cortex®-A secure context. Once the firmware is loaded, the default memory access rights defined in the OP-TEE device tree are reapplied.
- After stopping the firmware, the Cortex®-M memories are cleaned by OP-TEE.
3.5.3. RPMsg management[edit | edit source]
Since the resource tables and RPMsg vrings and buffers reside in shared memory accessible by both Cortex processors, communication with the authenticated firmware is possible.
3.5.4. Firmware core dump[edit | edit source]
Since the memories are protected, it is not possible to dump the firmware when a crash occurs.
4. Implementation[edit | edit source]
The authentication mechanisms require the STM32MPU Embedded Software distribution with OP-TEE running in the Arm® TrustZone context.
4.1. Components configuration[edit | edit source]
4.1.1. OP-TEE configuration[edit | edit source]
The OP-TEE OS firmware must embed:
- The remote processor trusted application (TA).
- The remote processor platform pseudo trusted application (PTA).
- The stm32mp remoteproc driver.
- The public key to authenticate the firmware.
- A symmetric key in case of firmware image encryption.
See also How to configure OP-TEE for detailed OP-TEE configuration information.
4.1.1.1. Remoteproc framework and driver[edit | edit source]
The CFG_DRIVERS_REMOTEPROC and CFG_STM32MP_REMOTEPROC configurations enable the OP-TEE remoteproc driver. The CFG_REMOTEPROC_PTA configuration enables the OP-TEE remoteproc framework.
- For STM32MP15x lines
: To enable the remoteproc framework, the CFG_STM32MP_REMOTEPROC configuration should be enabled:
make <other directives> CFG_STM32MP_REMOTEPROC=y
![]() |
For STM32MP15x lines
Refer to OP-TEE OS configuration and TF-A configuration. |
- For STM32MP2 series: The build configurations are enabled by default.
4.1.1.2. Signature key[edit | edit source]
4.1.1.2.1. Key embedded in the OP-TEE binary (default)[edit | edit source]
The RPROC_SIGN_KEY=<key file path> directive can specify the RSA public key file used to authenticate the remote processor firmware. This directive is optional; if not defined, the default key keys/default.pem is used.
Example build command specifying the public key (replace <other directives> with usual directives to build OP-TEE OS):
make <other directives> RPROC_SIGN_KEY=my_public_key.pem
![]() |
The public key is integrated into the OP-TEE OS binary during the build step. |
Key provided with the signed image (CFG_REMOTEPROC_PUB_KEY_VERIFY=Y) ecosystem release ≥ v6.1.0
on STM32MP2 series[edit | edit source]
When CFG_REMOTEPROC_PUB_KEY_VERIFY is enabled, an RSA or ECC public key can be provided with the signed image during the signing process. In this mode, the remoteproc OP-TEE verifies the validity of the public key by computing its hash and comparing it with the hash provisioned in "RPROC-FW-PKH[0-7]" OTPs.
Example build command to enable public key verification:
make <other directives> CFG_REMOTEPROC_PUB_KEY_VERIFY=y
4.1.1.3. Encryption key (STM32MP2 series)[edit | edit source]
- ecosystem release v6.0.0
: Encryption is only enabled for testing. Enable the feature for testing by enabling the CFG_REMOTEPROC_ENC_TEST configuration, which uses the default null AES key rproc_enc_test_key.txt .
make <other directives> CFG_REMOTEPROC_ENC_TEST=y
- ecosystem release ≥ v6.1.0
:
- For encryption test only: Enable encryption by enabling CFG_REMOTEPROC_ENC_TESTKEY, which uses the default AES key rproc_enc_test_key.bin .
make <other directives> CFG_REMOTEPROC_ENC_TESTKEY=y
- For nominal encryption: No specific configuration is needed to support encryption with an AES key provided in RPROC-FW-ENC-KEY[0-7] OTPs.
4.1.2. Linux kernel configuration[edit | edit source]
Activate TEE_REMOTEPROC in the kernel configuration using the Linux menuconfig tool: Menuconfig or how to configure kernel.
Trusted firmware support by a trusted applicationDevice drivers ---> Remoteproc drivers ---> <*> Support for Remote Processor subsystem <*> STM32 remoteproc support <*>
4.1.3. U-Boot Configuration[edit | edit source]
Activate REMOTEPROC_OPTEE in the U-Boot configuration using the menuconfig tool:
Support for the remoteproc in OP-TEE [*] Support for STM32 coprocessorRemote Processor drivers ---> [*]
4.2. Device tree configuration[edit | edit source]
4.2.1. stm32-rproc compatible property[edit | edit source]
As described in Documentation/devicetree/bindings/remoteproc/stm32-rproc.yaml , a specific compatible string is used to select the management of the remoteproc firmware enabling signature support:
- Use "st,stm32mp1-m4" for the STM32MP15x lines
Cortex®-M4 coprocessor management by Linux (supports ELF format).
- Use "st,stm32mp1-m4-tee" for the STM32MP15x lines
Cortex®-M4 coprocessor management by OP-TEE (supports signed format).
- Use "st,stm32mp2-m33" for the STM32MP2 series Cortex®-M33 coprocessor management by Linux (supports ELF format).
- Use "st,stm32mp2-m33-tee" for the STM32MP2 series Cortex®-M33 coprocessor management by OP-TEE (supports signed format).
4.2.2. OP-TEE device tree[edit | edit source]
The stm32mp_remoteproc driver is responsible for configuring the memories based on the memory region properties declared in the OP-TEE device tree.
- Memory regions declarations:
- The memory regions are defined in the device tree. For example, on STM32MP25x lines
, the STM32CubeMP2 firmware and TF-M firmware memory regions in DDR are declared in the stm32mp257f-ev1-ca35tdcid-resmem.dtsi file.
reserved-memory {
...
tfm_code: tfm-code@80000000 {
compatible = "shared-dma-pool";
reg = <0x0 0x80000000 0x0 0x100000>;
no-map;
};
cm33_cube_fw: cm33-cube-fw@80100000 {
compatible = "shared-dma-pool";
reg = <0x0 0x80100000 0x0 0x800000>;
no-map;
};
tfm_data: tfm-data@80900000 {
compatible = "shared-dma-pool";
reg = <0x0 0x80900000 0x0 0x100000>;
no-map;
};
cm33_cube_data: cm33-cube-data@80a00000 {
compatible = "shared-dma-pool";
reg = <0x0 0x80a00000 0x0 0x800000>;
no-map;
};
};
- Memory regions access rights
- A firewall configuration must be associated with these memory regions to:
- Isolate the memory regions embedding the Cortex®-M firmware code and data.
- Share the memory regions used for interprocessor communication.
- The firewall configuration is applied using:
The memory access rights must be dynamically updated to efficiently protect the memory regions. This service is exposed by the OP-TEE access controller through device tree configurations. The OP-TEE remoteproc framework switches configurations upon starting and stopping the Cortex®-M.
- During firmware image loading, the memory regions' access rights must be configured for OP-TEE usage. The access rights are specified in the memory region using the access-controllers-conf-load property.
- During Cortex®-M firmware execution, the memory regions' access rights must be configured for Cortex®-M usage. The access rights are specified in the memory region using the access-controllers-conf-default property.
&retram {
/* During Cortex<sup>®</sup>-M execution the memory region containing the code is only accessible by the Cortex<sup>®</sup>-M */
access-controllers-conf-default = <&etzpc DECPROT(STM32MP1_ETZPC_RETRAM_ID, DECPROT_MCU_ISOLATION, DECPROT_UNLOCK)>;
/* On load the memory region containing the code is only accessible by OP-TEE */
access-controllers-conf-load = <&etzpc DECPROT(STM32MP1_ETZPC_RETRAM_ID, DECPROT_S_RW, DECPROT_UNLOCK)>;
};
&mcusram3 {
/* During Cortex<sup>®</sup>-M execution the memory region used for interprocessor communication is not protected */
access-controllers-conf-default = <&etzpc DECPROT(STM32MP1_ETZPC_SRAM3_ID, DECPROT_NS_RW, DECPROT_UNLOCK)>;
/* On load the memory region containing the code is only accessible by OP-TEE */
access-controllers-conf-load = <&etzpc DECPROT(STM32MP1_ETZPC_SRAM3_ID, DECPROT_S_RW, DECPROT_UNLOCK)>;
};
- STM32MP2 series memory-region examples
&cm33_cube_fw {
/* During Cortex<sup>®</sup>-M execution the memory region containing the code is only accessible by the Cortex<sup>®</sup>-M */
access-controllers-conf-default = <&risaf4 RISAFPROT(RISAF_REG_ID(2), RIF_CID2_BF, RIF_CID2_BF, RIF_UNUSED, RIF_NSEC, RIF_ENC_DIS, RIF_BREN_EN)>;
/* On load the memory region containing the code is only accessible by OP-TEE */
access-controllers-conf-load = <&risaf4 RISAFPROT(RISAF_REG_ID(2), RIF_CID1_BF, RIF_CID1_BF, RIF_PRIV, RIF_SEC, RIF_ENC_DIS, RIF_BREN_EN)>;
};
&ipc_shmem {
/* During Cortex<sup>®</sup>-M execution the memory region used for interprocessor communication is not protected */
access-controllers-conf-default = <&risaf4 RISAFPROT(RISAF_REG_ID(5), RIF_CID1_BF|RIF_CID2_BF, RIF_CID1_BF|RIF_CID2_BF, RIF_UNUSED, RIF_NSEC, RIF_ENC_DIS, RIF_BREN_EN)>;
/* On load the memory region containing the code is only accessible by OP-TEE */
access-controllers-conf-load = <&risaf4 RISAFPROT(RISAF_REG_ID(5), RIF_CID1_BF, RIF_CID1_BF, RIF_PRIV, RIF_SEC, RIF_ENC_DIS, RIF_BREN_EN)>;
};
![]() |
The memory regions used for code execution can be encrypted. In such cases, the RIF_ENC_DIS flag should be replaced by RIF_ENC_EN in the "access-controllers-conf-XXX" configurations. |
- Memory region references:
- During the firmware loading process, the OP-TEE remoteproc verifies whether the address and size of the segments to be loaded match the memory regions declared in the OP-TEE device tree. The memory regions that contain the Cortex®-M firmware must be referenced in the device tree remoteproc node.
&m33_rproc {
memory-region = <&cm33_cube_fw>, <&cm33_cube_data>,
<&ipc_shmem>, <&tfm_code>, <&tfm_data>;
};
- OPT declarationSTM32MP2 series only
- For detail on OTP mapping refer to STM32MP2 OTP mapping page.
&bsec {
/* Public key Hash RPROC-FW-PKH[0-7] OTPs */
oem_rproc_pkh: oem_rproc_pkh@2d0 {
/* 0x2D0 -> otp180 */
reg = <0x2D0 0x20>;
st,non-secure-otp-provisioning;
};
/* Encryption key RPROC-FW-ENC-KEY[0-7] OTPs */
oem_rproc_enc_key: oem_rproc_enc_key@530 {
/* 0x530 -> otp332 */
reg = <0x530 0x20>;
st,non-secure-otp-provisioning;
};
};
Refer to the OP-TEE remoteproc device tree configuration for more details.
4.2.3. Linux kernel device tree[edit | edit source]
The firmware images are loaded by the OP-TEE remoteproc framework. The Linux device only needs to reference memory regions shared with the Cortex®-M for interprocessor communication, including:
- The resource table.
- The log buffer shared through the resource table.
- The shared buffers for RPMsg communication.
- Other shared buffers defined by the developer for their project.
In the following example, the "ipc_shmem_1" memory region contains the resource table and the remoteproc trace buffer. The "vdev0<xxx>" memory regions reference the shared memories used by the RPMsg protocol.
- Memory regions declarations:
reserved-memory {
...
ipc_shmem_1: ipc-shmem-1@81200000 {
compatible = "shared-dma-pool";
reg = <0x0 0x81200000 0x0 0xf8000>;
no-map;
};
vdev0vring0: vdev0vring0@812f8000 {
compatible = "shared-dma-pool";
reg = <0x0 0x812f8000 0x0 0x1000>;
no-map;
};
vdev0vring1: vdev0vring1@812f9000 {
compatible = "shared-dma-pool";
reg = <0x0 0x812f9000 0x0 0x1000>;
no-map;
};
vdev0buffer: vdev0buffer@812fa000 {
compatible = "shared-dma-pool";
reg = <0x0 0x812fa000 0x0 0x6000>;
no-map;
};
};
Refer to the Linux kernel remoteproc device tree configuration for more details.
4.2.4. U-boot device tree[edit | edit source]
The U-Boot device tree can be aligned with the Linux kernel device tree, but the reserved memory declarations are optional (since there is no interprocessor communication in U-Boot). Only the 'compatible' field is important to specify that OP-TEE is responsible for authenticating and loading the firmware.
- Memory region references:
&m33_rproc {
memory-region {{=}} <&ipc_shmem_1>, <&vdev0vring0>,
<&vdev0vring1>, <&vdev0buffer>;
};
![]() |
on STM32MP2 series only, the RIF configuration must allow the Cortex®-A and Cortex®-M to access to theses shared memory regions. |
4.3. STM32Cube firmware[edit | edit source]
The memory mappings for the STM32Cube firmware are defined in the STM32Cube firmware linker script, which must be aligned with the values defined in the OP-TEE and Linux device trees.
Most of the STM32Cube firmware code and data can be isolated but some memories have to be shared with the Cortex®-A, including:
- The resource table.
- The log buffer shared through the resource table.
- The shared buffers for RPMsg communication.
- Other shared buffers defined by the developer for its project.
The following example shows a linker script for a stm32mp2Cube firmware on STM32MP25x lines , aligned with the device tree example above:
- The memory regions definition:
RAM (rwx) : ORIGIN = 0x80A00000, LENGTH = 8M VIRTIO_SHMEM (rw) : ORIGIN = 0x812F8000, LENGTH = 32K IPC_SHMEM (rw) : ORIGIN = 0x81200000, LENGTH = 8M - LENGTH(VIRTIO_SHMEM) }MEMORY { NS_VECTOR_TBL (xrw) : ORIGIN = 0x80100000, LENGTH = 0x00000600 FLASH (rx) : ORIGIN = 0x80100600, LENGTH = 8M - LENGTH(NS_VECTOR_TBL)
- The resource table and remoteproc trace buffer:
IPC_SHMEM /* remoteproc trace buffer */ .sys_logs : { . = ALIGN(16);}} KEEP (*(.sys_logs*))}} . = ALIGN(16);}} } > IPC_SHMEM}} ... }SECTIONS { ... /* resource table */ .resource_table : { . = ALIGN(16); KEEP (*(.resource_table*)) . = ALIGN(16); } >
- The RPMsg shared buffers:
The VIRTIO_SHMEM memory region is already reserved for the RPMsg virtio rings and buffers defining following symbol in the linker script:
OpenAMP to enable rpmsg */ __OPENAMP_region_start__ = ORIGIN(VIRTIO_SHMEM); __OPENAMP_region_end__ = ORIGIN(VIRTIO_SHMEM)+LENGTH(VIRTIO_SHMEM);/* Symbols needed for
4.4. Cortex®-M firmware signature[edit | edit source]
4.4.1. Keys generation[edit | edit source]
4.4.1.1. RSA Keys for the signature[edit | edit source]
An RSA key must be generated to sign and authenticate the Cortex®-M firmware. Additionally, if the `CFG_REMOTEPROC_PUB_KEY_VERIFY` OP-TEE configuration is enabled, the hash of the public key must be computed. Below is an example of key computations using the OpenSSL commands:
- Generate PEM private key:
openssl genrsa -out private_key.pem 2048
- Generate PEM public key from the private key (when `CFG_REMOTEPROC_PUB_KEY_VERIFY=N`):
openssl rsa -pubout -in private_key.pem -out public_key.pem
- Two files are created:
- private_key.pem: the private key used to sign the firmware.
- public_key.pem: the public key used to authenticate the firmware.
- Generate DER public key from the private key and compute associated hash (when `CFG_REMOTEPROC_PUB_KEY_VERIFY=Y`):
# Generate DER public key
openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der
# Extract modulus
dd if=public_key.der of=pub_mod.bin bs=1 skip=33 count=256
# Extract exponent
dd if=public_key.der of=pub_exp.bin bs=1 skip=291 count=3
# Generate hash binary
cat pub_mod.bin pub_exp.bin > public_key.bin
openssl dgst -sha256 -binary public_key.bin > public_key_hash.bin
- Three files are created:
- private_key.pem: the private key used to sign the firmware.
- public_key.der: the public key used to authenticate the firmware.
- public_key_hash.bin: the hash of the public key used for OTP provisioning.
ECC Keys for the signature (ecosystem release ≥ v6.1.0
)[edit | edit source]
The use of ECC keys is available only if the OP-TEE `CFG_REMOTEPROC_PUB_KEY_VERIFY` is enabled.
- Generate keys with STM32_KeyGen_CLI tool
- Refer to KeyGen_tool for installation and details on the tool.
- Generate PEM keys and hash binary protected with a passphrase:
STM32_KeyGen_CLI -abs keys -pwd <passphrase>
- Generate DER public key:
openssl ec -pubin -in ./keys/publicKey.pem -outform DER -pubout -out ./keys/publicKey.der
- Generate keys with OpenSSL tool
- Generate PEM private key:
openssl ecparam -name prime256v1 -genkey -noout -out private_key.pem
- Generate DER public key:
openssl ec -in private_key.pem -pubout -outform DER -out public_key.der
- Generate hash binary:
tail -c +28 public_key.der > public_key.bin
openssl dgst -sha256 -binary public_key.bin > public_key_hash.bin
Three files are created:
- private_key.pem: the private key used to sign the firmware.
- public_key.der: the public key used to authenticate the firmware.
- public_key_hash.bin: the hash of the public key used for OTP provisioning.
4.4.1.2. AES symmetric Key for the encryption[edit | edit source]
- Generate Key with STM32_KeyGen_CLI tool
STM32_KeyGen_CLI -rand 32 AESkey.bin
- Generate Key with OpenSSL tool
openssl rand -out AESkey.bin 32
![]() |
For ecosystem release ≥ v6.1.0 ![]()
openssl rand -hex -out AESkey.txt 32
|
4.4.2. Firmware signature[edit | edit source]
The OP-TEE STMicroelectronics distribution integrates the sign_rproc_fw.py, which can concatenate several firmware ELF binary files and sign them.
This script:
- Adds a header on top of the ELF binaries.
- Adds a TLV (Type Length Value) chunk that contains information to authenticate the firmware images.
- Computes the hash of the program segments of each ELF binary that is loaded in the Cortex®-M memories and fill the hash table included in the TLV.
- Computes the hash of the header and the TLV and signs it using the private key.
4.4.2.1. Prerequisites[edit | edit source]
Install Python libraries needed by OP-TEE signature Python script: pip install pyelftools pycryptodomex
4.4.2.2. Sign the firmware (CFG_REMOTEPROC_PUB_KEY_VERIFY=N )[edit | edit source]
Applicable on ecosystem release ≥ v6.0.0 .
Mandatory parameters:
- --in <firmware>: ELF images to include (--in <firmware 1> --in<firmware 2>),
- --out <signed firmware>: path of the signed firmware generated,
- --key <privatekey.pem>: path of the PEm private key for the signature,
- --plat-tlv BOOTADDR <boot address> : boot address that must correspond to the vector table in memory.
Optional parameters:
- --plat-tlv BOOTSEC 0x1: set to enable the Cortex®-M33 trust Zone
- --enc_key <AES key>: key for firmware image encryption
- Example of the signature of a firmware image running on the Cortex®-M33 non secure context, using the OP-TEE RSA default key keys/default.pem .
./scripts/sign_rproc_fw.py --in firmware.elf --out signed_firmware.bin --key keys/default.pem --plat-tlv BOOTADDR 0x80100000
- Example of the signature of 2 firmware images running on the Cortex®-M33, one in the Secure context and one in the non secure context.
A key is specified (the OP-TEE remotreproc RPROC_SIGN_KEY build configuration should be updated accordingly). the boot address is the boot address of the secure firmware:
./scripts/sign_rproc_fw.py --in sec_firmware.elf --in non_sec_firmware.elf --out signed_firmware.bin --key <id_rsa private key> --plat-tlv BOOTADDR 0x80000000 --plat-tlv BOOTSEC 0x1
4.4.2.3. Sign the firmware (CFG_REMOTEPROC_PUB_KEY_VERIFY=Y)[edit | edit source]
Applicable on ecosystem release ≥ v6.1.0 .
Mandatory parameters:
- --in <firmware>: ELF images to include (--in <firmware 1> --in<firmware 2>),
- --out <signed firmware>: path to the signed firmware generated,
- --key <private_key.pem>: path to the PEM private key for the signature,
- --plat-tlv BOOTADDR <boot address> : boot address that must correspond to the vector table in memory.
- --key_info <public_key.der>: path to the public Key in DER format.
Optional parameters:
- --plat-tlv BOOTSEC 0x1: set to enable the Cortex®-M33 trust Zone
- --enc_key <AES key>: key for firmware image encryption
- --key_pwd <private key password>: specify the password of the private key (if the key is protected by a password) avoiding entering it manually in the prompt.
- Example of the signature of a firmware image running on the Cortex®-M33 non secure context, using the OP-TEE RSA default key keys/default.pem . In this example the boot address is the boot address of the ELF firmware at 0x80010000:
./scripts/sign_rproc_fw.py --in firmware.elf --out signed_firmware.bin --key keys/default.pem --key_info <public_key.der> --plat-tlv BOOTADDR 0x80100000
- Example of the signature of 2 firmware images running on the Cortex®-M33, one in the Secure context and one in the non secure context. A key is specified (the OP-TEE remotreproc RPROC_SIGN_KEY build configuration should be updated accordingly). In this example the boot address is the boot address of the secure firmware (BOOTSEC is set to 0x1) at 0x80000000:
./scripts/sign_rproc_fw.py --in sec_firmware.elf --in non_sec_firmware.elf --out signed_firmware.bin --key <id_rsa private key> --key_info <public_key.der> --plat-tlv BOOTADDR 0x80000000 --plat-tlv BOOTSEC 0x1
4.4.2.4. encrypt the firmware[edit | edit source]
- ecosystem release v6.0.0
: Using the default RSA key (CFG_REMOTEPROC_ENC_TEST=y) available in the OP-TEE repository (that can be updated): keys/rproc_enc_test_key.txt
./scripts/sign_rproc_fw.py --in <firmware 1> --in<firmware 2> --out <signed firmware name> --key <id_rsa private key> --enc_key keys/rproc_enc_test_key.txt}
- ecosystem release ≥ v6.1.0
: Using the default RSA key (CFG_REMOTEPROC_ENC_TESTKEY=y) available in the OP-TEE repository (that can be updated): keys/rproc_enc_test_key.bin
./scripts/sign_rproc_fw.py --in <firmware 1> --in<firmware 2> --out <signed firmware name> --key <id_rsa private key> --enc_key keys/rproc_enc_test_key.bin}
- Specify the key to use (the OP-TEE remotreproc PTA should be updated to used the associated public key):
AES key>./scripts/sign_rproc_fw.py --in <firmware 1> --in<firmware 2> --out <signed firmware name> --key <id_rsa private key> --enc_key <
5. Code source[edit | edit source]
5.1. OP-TEE[edit | edit source]
ta/remoteproc/remoteproc_core.c core/pta/stm32mp/remoteproc_pta.c core/drivers/remoteproc/stm32_remoteproc.c
5.2. Linux Kernel[edit | edit source]
drivers/remoteproc/tee_remoteproc.c drivers/remoteproc/stm32_rproc.c
5.3. U-Boot[edit | edit source]
drivers/remoteproc/rproc-optee.c
5.4. STM32Cube example[edit | edit source]
Projects/STM32MP157C-DK2/Applications/OpenAMP/OpenAMP_for_signed_fw
Projects/STM32MP257F-EV1/Applications/TFM/TFM_Protected_Storage Projects/STM32MP257F-EV1/Applications/OpenAMP/OpenAMP_TTY_echo
Projects/STM32MP235F-DK/Applications/USBPD/USBPD_DRP_UCSI
Projects/STM32MP215F-DK/Applications/OpenAMP/OpenAMP_TTY_echo