Last edited 9 months ago

How to protect the coprocessor firmware

1 Article purpose[edit source]

This article explains how to protect the software loaded by main processor in the coprocessor. It will ensure the authentication of the loaded firmware.

2 Introduction[edit source]

The Linux® OS through the remoteproc framework allows to load firmware and control remote processors. Thanks to a specific OP-TEE Trusted Application (TA) running on the Arm® Trustzone but also the ETZPC peripheral , it is possible to authenticate a Cortex-M4 firmware and to install its on isolated RAMs to ensure its integrity during the execution .

From an user's point of view the management of a non authenticated or an authenticated firmware does not change, whether the firmware is managed by the Linux or the 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 Principle[edit source]

The protection of the coprocessor firmware is relying hash computation and encryption key, to authenticate a firmware, but also the isolation of the execution memories, for execution integrity.

  • The firmware is signed using a OP-TEE signature script. This script adds to the elf binary a header that contains, among firmware information, elements for the authentication:
  • a hash table which contain the hash of the segment to load,
  • a signature computed with the private key.
  • During the execution phase:
  • The Firmware is read from the file system by the remoteproc framework and the image is provide to the OP-TEE Trusted Application.
  • The firmware is authenticated and installed in the protected Cortex-M memories by the OP-TEE Trusted Application.

Authenticate rproc fw.png

3.1 Firmware signature[edit source]

A common strategy to sign a firmware consists in adding a header that will contain the signature required for authentication. The signature is generated by computing a hash on the whole binary with the header and encrypting this hash using a private key.
On the STM32MP1, the signature procedure is similar but optimized to minimize the memory need and accelerate the authentication process. The principle is to compute the hash of the segments to load instead of computing the hash of the full image.


STM32CubeMP1 firmware generationKeys generationSign the STM32CubeMP1_firmwarebuild OPTEE with public KeySignature procedure
  • The signature consists in:
  1. Parse the program table in the ELF file to identify the segments to load.
  2. Compute the hash of each segment and save the result in the hash table chunk of the header.
  3. Compute the hash of the header and sign the hash with the private key.
  • The public key is embedded in the OPTEE-OS firmware that will be in charge of the Cortex-M4 firmware authentication.

3.2 Memory Management[edit source]

The MCU SRAM and the RETRAM memories can be dedicated to Cortex-M usage (isolated), shared with the Cortex-A non-secure context or protected for Cortex-A secure access only.
Using the ETZPC mechanism:

  • During the load and authentication phase: the memories can be locked for Cortex-A secure access only.
  • During the execution phase: the memories can be isolated for MCU usage (e.g. execution code and data) or shared with the Cortex-A non-secure context (e.g. RPMsg shared buffers)
Info white.png Information
Developer has to pay attention for Cortex-M firmware build to pack in a same memory bank data shared with the Cortex-A. This allows to isolate the rest of the memory banks for the Cortex-M context.

Descriptions provided in this article respects the mapping described in memory mapping section:

  • The RETRAM, MCU SRAM1 and MCU SRAM2 are used for execution code and data. They are accessible by the Cortex-A secure only when no firmware is running and assigned to Cortex-M context during authenticated firmware execution.
  • The MCU SRAM3 (IPC Buffers) is shared between the Cortex-A non-secure context and the Cortex-M. No protection is applied on this memory bank.
  • The MCU SRAM4 is reserved for the Cortex-A non-secure context so free for other usage.

Of course, this mapping can be customized depending on project need. In this case the code as to be updated in a synchronized manner in STM32CubeMP1 firmware, Linux Kernel device-tree and the OP-TEE rproc PTA code.

3.3 Authentication[edit source]

The Authentication is executed in the secure context by an OP-TEE trusted application.

Authentication Fw load phase1.png

1. The firmware in copied by the Linux Kernel (or U-boot) in a non-secure DDR memory.
2. The firmware header is copied in a secure memory to ensure its integrity during the authentication steps.
3. The Header hash is computed.
4. The signature is decrypted using the public key. The result is the hash of the header 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 can start: load the firmware in MCU memories:

Authentication Fw load phase2.png

For each segment to load:
6. The segment is copied in the MCU RAMs which are accessible only by the secure context to ensure their integrity.
7. The hash of the copied segment is computes and compare to the hash stored in the firmware header.
8. At this step all segments have been copied and authenticated, the firmware is loaded and read for execution.

3.4 Authenticated firmware live-cycle[edit source]

Authentication software overview.png

The management of the firmware live-cycle does not change from user point of view and is described in the remoteproc article.

The main difference with a non-authenticated firmware is that the remote processor framework delegates the coprocessor management to the OP-TEE.

Info white.png Information
The authenticated firmware can also be pre-loaded and started by the U-boot.

3.4.1 Firmware load and authentication[edit source]

  • As with a non authenticated Firmware, The firmware is read from the file system by the remoteproc framework, then the image is sent to the OP-TTE trusted application.
  • The authentication and the MCU memory isolation are managed in the secure context by the OP-TEE trusted application.

3.4.2 Firmware start and stop[edit source]

  • As with a non authenticated Firmware, the firmware start and stop actions are treated by the remoteproc framework but in this case delegated to the Arm® Trustzone.
  • The firmware start and stop are executed by the OP-TEE. The reset and RAMs access rights are only accessible by the secure context.
  • Before starting the firmware, the MCU RAMs access rights are updated to isolate execution RAMs for the Cortex-M.
  • After stopping the firmware, the MCU RAMs are cleaned and access rights are updated to Cortex-A secure context.

3.4.3 RPMsg management[edit source]

As the resource tables and RPMsg vrings and buffers are in a MCU shared memory accessible by both Cortexes, it is possible to communicate with the authenticated firmware.

3.4.4 Firmware core dump[edit source]

As the memories are protected it is not possible to dump the firmware on crash.

4 Implementation[edit source]

The authentication mechanisms requests STM32MPU Embedded Software distribution with OP-TEE running in the Arm® Trustzone context.

4.1 Updating the built image to enable the feature[edit source]

4.1.1 Install OP-TEE Image[edit source]

Refer to How to populate and boot a board with OP-TEE for details.

4.1.2 OP-TEE remoteproc TA generation[edit source]

As prerequisite the OP-TEE OS build environment has to be installed.

The OPTEE OS firmware has to be re-generated to include (see here for how to):

  • the remote processor Trusted Application (TA),
  • the remote processor platform Pseudo Trusted Application (PTA),
  • the public key.
4.1.2.1 Configuration[edit source]

Enable the CFG_RPROC_PTA in core/arch/arm/plat-stm32mp1/conf.mk

CFG_RPROC_PTA ?= y
4.1.2.2 Build the OP-TEE OS[edit source]

Additional directives to build OP-TEE OS:

  • CFG_IN_TREE_EARLY_TAS=remoteproc/80a4c275-0a47-4905-8285-1486a9771a08 : add remoteproc early TA.
  • CFG_RPROC_SIGN_KEY= <key file path name>: specify the public key file to authenticate the remote processor firmware. This directive is optional. If not defined the keys/default_rproc.pem default key will be used.

Example of build command for the STM32MP157x-DK2 board:

  • Using the default key available in the OP-TEE repository to build the OP-TEE OS (that can be updated): keys/default_rproc.pem
 make PLATFORM=stm32mp1 CFG_EMBED_DTB_SOURCE_FILE=stm32mp157c-dk2.dts CFG_SECURE_DT=stm32mp157c-dk2 O=out all comp-cflagscore=--sysroot=$SDKTARGETSYSROOT CFG_IN_TREE_EARLY_TAS=remoteproc/80a4c275-0a47-4905-8285-1486a9771a08
  • Or specify the key to use by defining CFG_RPROC_SIGN_KEY:
 make PLATFORM=stm32mp1 CFG_EMBED_DTB_SOURCE_FILE=stm32mp157c-dk2.dts CFG_SECURE_DT=stm32mp157c-dk2 O=out all comp-cflagscore=--sysroot=$SDKTARGETSYSROOT CFG_IN_TREE_EARLY_TAS=remoteproc/80a4c275-0a47-4905-8285-1486a9771a08 CFG_RPROC_SIGN_KEY=my_public_key.pem
4.1.2.3 Update the software on board[edit source]

Refer to update OP-TEE software on board for details.

4.1.3 Linux Kernel update[edit source]

4.1.3.1 Kernel configuration[edit source]

Activate the remoteproc tee in the kernel configuration using the Linux Menuconfig tool: Menuconfig or how to configure kernel.

Device drivers --->
   Remoteproc drivers --->
     <*> Support for Remote Processor subsystem
     <*> STM32 remoteproc support
     <*> Trusted firmware support by a Trusted Application 
4.1.3.2 Device tree configuration[edit source]

To be compliant with the memory mapping and protection the memory regions properties described in the Linux remoteproc framework overview has to be updated.
Example:

  • Declare a memory region for the resource table and remoteproc trace memory region. the Address and size have to be aligned with stm32Cube linker script m_rsc_table definition .
reserved-memory {
 	...
	mcu_rsc_table: mcu_rsc_table@10048000 {
		compatible = "shared-dma-pool";
		reg = <0x10048000 0x8000>;
		no-map;
	};
};

  • In the remoteproc DT node:
  • Cleanup retram, mcusram and mcusram2 regions which are now handles by OP-TEE,
  • add the mcu_rsc_table region,
  • update compatibility field.
 /* stm32 M4 remoteproc device */
 m4_rproc: m4@0 {
	...
       compatible = "st,stm32mp1-m4_optee";
       memory-region = <&vdev0vring0>, <&vdev0vring1>, <&vdev0buffer>, <&mcu_rsc_table>;
	...
};
Info white.png Information
The firmware memory mapping must be set according to these values in the STM32Cube firmware linker script.

4.1.4 U-Boot update[edit source]

This chapter is relevant only for coprocessor firmware pre-loaded by the bootloaderbr>

To enable the management of a pre-loaded firmware of an authenticated firmware, the compatible field of the remote proc node has to be updated in the device tree configuration. In arch/arm/dts/stm32mp151.dtsi :

m4_rproc: m4@10000000 {
      compatible = "st,stm32mp1-m4_optee";
};

4.2 STM32CubeMP1 firmware generation[edit source]

Most of the STM32CubeMP1 firmware can be isolated. Developer has to pay attention to the memories shared with the Cortex-A7 non secure context:

  • 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.

4.2.1 The resource table[edit source]

In the STM32CubeMP1 examples, the resource table is linked by default with other firmware data. A specific memory region has to be defined in the linker script to isolate it in a new region. In following example, we create a new memory region for the resource table at the end of the MCU_SRAM3.

/* Memories definition */
MEMORY
{
 m_interrupts (RX)  : ORIGIN = 0x00000000, LENGTH = 0x00000298
 m_text       (RX)  : ORIGIN = 0x10000000, LENGTH = 0x00020000
 m_data       (RW)  : ORIGIN = 0x10020000, LENGTH = 0x00020000
 m_ipc_shm    (RW)  : ORIGIN = 0x10040000, LENGTH = 0x00008000
 m_rsc_table  (RW)  : ORIGIN = 0x10048000, LENGTH = 0x00008000
}

Then we map the resource table and log buffer in this region

.resource_table :
{
  . = ALIGN(4);
  KEEP (*(.resource_table*))
  . = ALIGN(4);
} > m_rsc_table

4.2.2 log buffer[edit source]

The log buffer can be enable in the resource table to get Cortex-M log from the Cortex-A non-secure context. To define it:

  • an attribute has to be added to the system_log_buf buffer:
#if defined(__GNUC__) 
#define __section_t(S)          __attribute__((__section__(#S)))
#define __resource              __section_t(.sys_logs)
#endif

char  __resource __attribute__((used)); system_log_buf[SYSTEM_TRACE_BUF_SZ];
  • Update the linker script to add the trace buffer in the m_rsc_table memory region.
.sys_logs :
{
  . = ALIGN(4);
  KEEP (*(.sys_logs*))
  . = ALIGN(4);
} > m_rsc_table

4.2.3 RPMsg shared buffers[edit source]

The m_ipc_shm memory region is already reserved for the RPMsg Virtio rings and buffers.

4.3 Cortex-M4 firmware signature[edit source]

4.3.1 Generate private and public key[edit source]

To sign and authenticate the Cortex-M firmware an RSA key has to be generated. For instance, the ssh-keygen command can be used.

 ssh-keygen

Two files are created:

  • id_rsa: the private key to sign the firmware.
  • id_rsa.pub: the public key to authenticate the firmware.

4.3.2 Firmware signature[edit source]

The OP-TEE ST-Microelectronics distribution integrates the sign_rproc_fw.py that allows to sign the firmware binary ELF file.
This script:

  • adds a header on top of the ELF binary,
  • computes the hash of the program segments that will be loaded in the Cortex-M memories and fill the hash table included in the header,
  • computes the hash of the header and sign it using the private key.

To sign the firmware :

 ./scripts/sign_rproc_fw.py sign --in <firmware name>
  • Or specify the key to use as parameter:
 ./scripts/sign_rproc_fw.py sign --in <firmware name> --key <id_rsa private key>
  • For more script options:
 ./scripts/sign_rproc_fw.py sign --help

The signed firmware is available in the ELF file directory with a .sign extension.

5 Code source[edit source]

5.1 OP-TEE[edit source]

 ta/remoteproc/remoteproc_core.c  
 core/arch/arm/plat-stm32mp1/remoteproc_pta.c  

5.2 Linux Kernel[edit source]

drivers/remoteproc/tee_remoteproc.c  
drivers/remoteproc/stm32_rproc.c  

5.3 U-boot[edit source]

drivers/remoteproc/rproc-optee.c