How to protect the coprocessor firmware

Revision as of 13:13, 19 October 2020 by Registered User (→‎STM32CubeMP1)
Under construction.png


1. Article purpose[edit source]

This article explains how to protect the software loaded by main processor in the coprocessor. It will ensure integrity 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 Arm@ Trustzone and resource isolation, it is possible to authenticate a firmware before loading it. It is required to ensure integrity of the running firmware. It could be required for a complete secure platform.

3. Principle[edit source]

The protection of the coprocessor firmware is relying on the signature of the firmware using hash computation and encryption key, but also the isolation of the execution memories.

  • The firmware is signed using a OP-TEE signature script
  • The Firmware is read from the file system by the remoteproc framework and the image is sent to the Arm@ Trustzone.
  • The firmware is authenticated and managed by OP-TEE in the Arm@ Trustzone.

Authenticate rproc fw.png

3.1. Firmware signature[edit source]

A basic strategy to sign a firmware consists in adding an header that will contains the signature required for authentication. The signature is generated by computing a hash of the binary with the header and to encrypt this hash using a key.
On the STM32MP1, the signature procedure is similar but optimized to minimize the memory 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.

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 result in the hash table chunk of the header.
  3. Compute the hash of the header and sign the hash with the private key.

3.2. Authentication[edit source]

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

  1. The Header is copied in a secure memory to prevent hacking
  2. The Header is verified using the signature and the public key embedded in OP-TEE
  3. The Elf segments are loaded in the MCU RAMs (assigned to secure context). Upon segment load, a hash is computed and compared to the hash stored in the header hash table during the firmware signature.
  4. The execution RAMs are isolated for the Cortex-M4 usage.

3.2.1. Memory Management[edit source]

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

  • During the load and authentication phase: The memories can be locked for 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-A7 non secure context.

Developer has to pay attention for Cortex-M4 firmware build to pack in a same memory bank data shared with the Cortex-A7. This allows to isolate the rest of the memories banks for the Cortex-M4 context.
As the default implementation in OP-TEE respected mapping described in memory mapping section. Descriptions provided in this article respect this mapping too.
The following mapping is considered:

  • 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 isolated to Cortex-M4 context during authenticated firmware execution.
  • The MCU SRAM3 (IPC Buffers) is shared between the Cortex-A7 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 not used.

Of course this mapping can be customized by developer depending on project need. In this case the code update as to de done in a synchronized manner in stm32cube firmware, Linux Kernel device-tree and the Op-TEE rproc PTA code.

4. Firmware signature[edit source]

STM32CubeMP1 firmware generationKeys generationSign the STM32CubeMP1_firmwarebuild OPTEE with public KeySignature procedure

4.1. STM32CubeMP1[edit source]

Most of the Stm32CUbeMP1 firmware can be isolated. Develloper 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 develloper for its project.

4.1.1. The resource table[edit source]

The resource table is linked by default with other firmware data. A specific memory region has to be defined in the linker script to define a new region. In following example we reserve 512 bytes at the begining of the MCU_SRAM3 for the resource table:

/* 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_rsc_table  (RW)  : ORIGIN = 0x10040000, LENGTH = 0x00000200
 m_ipc_shm    (RW)  : ORIGIN = 0x10040200, LENGTH = 0x0000FE00
}
.resource_table :
{
  . = ALIGN(4);
  KEEP (*(.resource_table*))
  . = ALIGN(4);
} > m_rsc_table

4.1.2. log buffer[edit source]

The log buffer can be enable in the resource table to get Cortex-M4 log from the Cortex-A7 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 extend the m_rsc_table memory region and add to it the log buffer
MEMORY
{
 m_interrupts (RX)  : ORIGIN = 0x00000000, LENGTH = 0x00000298
 m_text       (RX)  : ORIGIN = 0x10000000, LENGTH = 0x00020000
 m_data       (RW)  : ORIGIN = 0x10020000, LENGTH = 0x00020000
 m_rsc_table  (RW)  : ORIGIN = 0x10040000, LENGTH = 0x00001000
 m_ipc_shm    (RW)  : ORIGIN = 0x10041000, LENGTH = 0x0000F000
}

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

4.1.3. RPMsg shared buffer[edit source]

The memory region for these buffers are already reserved tnaks to the m_ipc_shm memory region defined in the linker script.

4.2. Generate private and public key[edit source]

4.3. Sign STM32CubeMP1 firmware[edit source]

4.4. OP-TEE remoteproc[edit source]

5. Firmware authentication[edit source]

5.1. Load firmware using RemoteProc framework[edit source]

The OpenSTLinux process to load the coprocessor firmware is to use the remoteproc framework.

The current framework authentication doesn't support other than elf binary load. In case of authentication firmware management, using remoteproc framework, some rework is needed. No defined support are available to manage information needed for authentication, it could be proprietary header (such as STM32 header).

In this usage, firmware must be sent directly to secure OS in a shared memory area to be authenticated and loaded. In case of STM32 header, the firmware is signed as a signle binary, so it could not be splitted before authentication. A new SIP service must be developed and called from the remoteproc driver to send the sign firmware to secure os.

Once authentication is confirmed, the secure OS will charge the binary in the correct SRAM (depending the elf section). A Elf parser is needed to properly parse section and load firmware at the correct offset.

Once done, resource table address must be return to remoteproc framework to allow rpmsg transactions.

5.2. Secure Protection[edit source]

5.2.1. Key Management[edit source]

In the same way as Secure boot authentication, the firmware authentication will use the same API (using bootrom services) but can use a different key to authenticate. The key hash must be store in OTP to be checked in secure OS.

5.3. Firmware Start[edit source]

As the firmware was loaded and verified, it is up to remoteProc processing to start the firmware in the standard way.

6. Firmware signature[edit source]

The coprocessor firmware can be signed in the same way as the bootloader using Signing Tool. It will add a complete header that will contains the public key and the signature required for authentication. Using this tool is compliant with the authentication processing that is already done for bootloader authentication. This authentication is ECDSA based.

./STM32AP_SigningTool_CLI -bin /home/User/Folder1/BinaryFile.bin –pubk /home/user/publicKey.pem –prvk /home/user/privateKey.pem –iv 0 –pwd azerty –s –la 0x20000000 –ep 0x08000000 –a 2 –o /home/user/Folder2/Folder3/signedFile.bin -type copro