Skip to content

Secure Boot on STM32MP25x

This pages explains the bootloader authentication and secure boot setup, for STM32MP25x machines.

References:

Acronyms:

  • BL2: Boot Loader stage 2
  • CoT: Chain Of Trust
  • FIP: Firmware Image Package
  • PKH: Public Key Hash
  • PKHTH: Public Key Hash Table Hash
  • PKI: Public Key Infrastructure
  • ROTPK: Root Of Trust Public Key
  • TBBR: Trusted Board Boot Requirements
  • TFA: Trusted Firmware-A

STM32 Secure boot Overview

When the SoC powers on, the ROM code (BL1) loads the TF-A BL2 binary, which must be in STM32 format (refer to the STM32 BL2 structure) to be recognized.

The ROM code starts the BL2 authentication process. Upon successful authentication, BL2 is executed to initialise the board, locates and reads the metadata to load the corresponding FIP file (refer to the STM32 FIP structure) in DDR.

The secure boot steps are detailed in Secure boot stages section

STM32 BL2 structure

The STM32 BL2 built image is structured as follows and is meant to be loaded into the SoC's internal SRAM:

       00h+-----------------------+----- ^ ------ ^
------>48h|     STM32 HEADER      |      |        |
 Signed   |                       |      |        |
  Data    | (PK, PKH table, key   |   512 Bytes   | 
------>78h|    index, signature)  |      |        |
------>80h|                       |      |        |
      ^   |                       |      |        |
      |   +-----------------------+----- v        |
      |   |    BL2 Device tree    |               |
   Signed +-----------------------+        Must fit in SRAM
    Data  |                       |               |
      |   |      BL2 Binary       |               |
      |   | (The actual TF-A BL2  |               |
      |   |         ELF)          |               |
      v   |                       |               |
--------> +-----------------------+-------------- v

The STM32 header consists of a base header and three extensions: authentication, decryption, and padding.

The base header primarily includes the signature and option flags that enable each of the three extensions. The signature is computed from offset 0x48 to the end of the binary, excluding the bytes between offsets 0x78 and 0x80.

The authentication extension primarily contains the index of the public key used for the authentication process, along with a table of eight public key hashes.

In the Welma Secure Boot implementation, the decryption extension is not used. The padding extension then handles the remaining bytes.

STM32 FIP structure

The FIP (Firmware Image Package) is utilized by the TF-A BL2 firmware to load and authenticate the next-stage binaries. It consolidates all boot firmware, configuration files, and certificates into a single archive.

The FIP file can be created and modified using the TF-A fiptool. This utility enables the addition, removal, or replacement of elements within the FIP file.

Each element within the FIP is assigned a command-line flag to enable access to it. In the context of STM32 secure boot, an ST version of fiptool is used (patched-version of mainline tool) to handle STM32-specific files.

The table below outlines the included binaries along with their corresponding command-line flags.

+----------------------------+-----------------+
|          Binary            |  Command line   |
+----------------------------+-----------------+
|    BL31 Secure Monitor     |    --soc-fw     |
+----------------------------+-----------------+
|     BL31 Device Tree       | --soc-fw-config |
+----------------------------+-----------------+
|       BL32 Firmware        |    --tos-fw     |
+----------------------------+-----------------+
|     Trusted OS pager       | --tos-fw-extra1 |
+----------------------------+-----------------+
|    Trusted OS pageable     | --tos-fw-extra2 |
+----------------------------+-----------------+
| Firmware conf file (FCONF) |   --fw-config   |
+----------------------------+-----------------+
|         BL33 DTB           |  --hw-config    |
+----------------------------+-----------------+
|       BL33 Firmware        |    --nt-fw      |
+----------------------------+-----------------+
|       DDR Firmware         |    --ddr-fw     |
+----------------------------+-----------------+

To guarantee the authenticity of the binaries within the FIP, TF-A employs a standard PKI mechanism known as the Chain of Trust. This authentication process is integrated into the BL2 firmware through the TBBR implementation. It relies on key certificates to validate public keys and content certificates, which are signed using the corresponding private keys. The content certificates include the hash of the firmware image, ensuring that only verified and untampered binaries are loaded and executed.

CoT description form STM32MP25x is defined in the BL2 device tree (stm32mp2-cot-descriptors.dtsi) as follow:

                   +-------+
                   | ROTPK |
                   +---+---+
                       |
         +-------------+-------------+
         |                           |
  +------+------+             +------+------+
  | STM32MP CFG |             | Trusted Key |
  | Certificate |             | Certificate |
  +-------------+             +------+------+                          
                                     |
                        +------------+-------------+
                        |                          |
                +-------+--------+        +--------+-------+
                | Trusted OS Key |        | Non-Trusted OS |   
                |   certificate  |        | Key certificate| 
                +-------+--------+        +--------+-------+
                        |                          | 
               +--------+---------+    +-----------+----------+ 
               |Trusted OS Content|    |Non-Trusted OS Content|
               |   certificate    |    |     certificate      |
               +------------------+    +----------------------+

In the Chain of Trust, the Root of Trust Public Key must be the same key used to authenticate BL2. This PKI structure is established within the FIP by incorporating the necessary certificates using the following options:

+-------------------------------+--------------------+
|        Certificate            |    Command line    |
+-------------------------------+--------------------+
| Root of trust key certificate | --trusted-key-cert |
+-------------------------------+--------------------+
|     BL31 key certificate      | --soc-fw-key-cert  |
+-------------------------------+--------------------+
|   BL31 content certificate    |   --soc-fw-cert    |
+-------------------------------+--------------------+
|     BL32 key certificate      | --tos-fw-key-cert  |
+-------------------------------+--------------------+
|   BL32 content certificate    |   --tos-fw-cert    |
+-------------------------------+--------------------+
|     BL33 key certificate      |  --nt-fw-key-cert  | 
+-------------------------------+--------------------+
|   BL33 content certificate    |    --nt-fw-cert    |
+-------------------------------+--------------------+
|  STM32MP config certificate   | --stm32mp-cfg-cert |
+-------------------------------+--------------------+

These certificates can be generated manually using TF-A's cert_create tool (with the ST version), which will always be verified by the ROTPK. In this case, the private keys associated with the certificates are not kept, and they are used only once for each FIP. See Offline signing of BL2 and FIP for more details on how to use cert_create and fiptool.

Secure boot stages

The secure boot process involves multiple stages of verification. It begins with the ROM code and continues through the loading and execution of all bootloader firmware components.

Throughout all authentication and signature processes, we use ECDSA key pairs (256-bits) based on the NIST P-256 curve algorithm and SHA-256 for hash computation.

  1. TF-A BL2 Authentication:

    • Assets contained in the TF-A BL2:

      • ROTPK (Public key)
      • Public key index (0..7)
      • PKH: table of the 8 ECDSA public key Hashes.
      • Image Signature
    • Authentication:

      • The ROM loads the BL2 on-chip SRAM.
      • The ROM Code authenticates the PKH table using the PKHTH stored in the OTP fuses.
      • The ROM Code authenticates ROTPK by comparing its SHA256 to the hash contained in the PKHTH using the Public Key Index.
      • The ROM Code authenticates the BL2 using the Public Key to verify the signature stored in the BL2 binary.
  2. The ROM code starts the BL2 firmware

    • BL2 identifies and load metadata that contains required information to identify the FIP.
  3. FIP Authentication

    • Assets contained in the FIP:

      • BL31 Firmware, DTB and their certificates
      • BL32 Firmware and its certificates
      • BL33 Firmware and its certificates
      • BL33 DTB, DDR firmware, FCONF and their certificate
    • Authentication:

      • BL2 authenticates FCONF, DDR firmware and BL33 config file (U-Boot dtb) using STM32MP Config Certificate
      • BL2 initializes the DDR PHY and reads FCONF to load next boot stages
      • BL2 authenticates BL31 using SoC key and content certificates
      • BL2 authenticates BL32 using trusted OS key and content certificates
      • BL2 authenticates BL33 using non-trusted Firmware key and content certificates
  4. BL2 starts BL31

  5. BL31 starts BL32 (OP-TEE) and BL33 (U-Boot)

Setting up your Secure Boot

This section will guide you through the process of building and running an image with secure boot.

It is assumed the following context:

  • STM32MP25 machine (eg: stm32mp25-disco-welma)
  • Default Welma partition layout
  • Default Welma development keys used in the Yocto build
  • Production keys owned by a separate entity (other than the development team)

Building your Image

Welma requirements:

  • machine.conf:

    • Set BOOT_SIGNING_MECHANISM = "stm32mp2"
  • In local.conf

    • Activate secure boot by defining WELMA_SECURE_BOOT = "1" (this is the default)
    • Adjust or keep the default values for WELMA_KEY_SWK1_PUB and WELMA_KEY_SWK1_PRIV
  • Other variables:

    • PRIVATE_KEYS_PREFIX: Prefix used in private key files: <prefix><index>.pem
    • PUBLIC_KEYS_PREFIX: Prefix used in public key files: <prefix><index>.pem
    • PRIVATE_KEY_INDEX: Index of the private key to be used, which must be between 0 and 7.
    • FORCE_FLAG: Must always be unset, except in the case of key revocation by choosing a private key index greater than 0, where it must be set to FORCE_FLAG = "1".
    • WELMA_ROT_SIGN_KEY: Path to the private key to be used. Built from PRIVATE_KEYS_PREFIX and PRIVATE_KEY_INDEX variables.
  • TF-A BL2:

    • Have TF-A BL2 compiled with TBB support (TRUSTED_BOARD_BOOT=1) and signed with the development keys (execute stm32mp2-sign)
    • For development devices only, compile TF-A with DYN_DISABLE_AUTH=1 option
    • Have WELMA_KEY_SWK1_PUB injected (execute inject-pubkey-uboot-dtb)
    • Have FIP file embed all necessary boot stages and certificates (execute cert_create and fiptool)

Warning

DYN_DISABLE_AUTH=1 option should only be enabled for development platforms.

  • u-boot:

    • Activate FIT signature (CONFIG_FIT_SIGNATURE)
    • Disable the U-Boot command line or restrict its access by a password (not done in Welma), because it would allow an attacker to bypass the secure boot mechanism.
  • In SYSRO and APPRO: have your programs not start other programs located in SYSRW, as this filesystem is not covered by secure boot. Keep in mind that /home is located in SYSRW and make sure that files such as ~/.profile, ~/.bashrc, etc. do not get executed automatically if they were created by an attacker.

Signing your Image with Production Keys

Input:

  • Generated by the build:

    • BL2 Firmware
    • FIP archive
    • fitImage (BOOT)
    • .verity images (SYSRO, APPRO)
  • Production Keys: ROT keys, SWK1.priv, SWK1.crt

Ouput (signed images provisioned with necessary public keys and certificates):

  • BL2 Firmware
  • FIP archive
  • BOOT vfat partition
  • SYSRO and APPRO verity partitions

Steps:

  • Inject SWK1.crt into U-Boot DTB
    • Extract u-boot.dtb from FIP archive using fiptool (with ST support):
      fiptool unpack --hw-config u-boot.dtb <FIP Archive>
      
    • Inject SWK1.crt into u-boot.dtb:
      welma-signing-tools/common/inject-pubkey-uboot-dtb \
                      SWK1.crt \
                      u-boot.dtb \
                      sha256,rsa4096'
      
    • Replace u-boot.dtb in the FIP archive
      fiptool update --hw-config u-boot.dtb <FIP Archive>
      
  • Sign TF-A BL2
  • Sign and pack the FIP

  • Sign fitImage:

    welma-signing-tools/common/sign-fitimage SWK1.priv fitImage
    

    • Create the BOOT partition (vfat) populated with the signed fitImage, using mkdosfs & mcopy
  • Sign .verity images:

    welma-signing-tools/common/sign-verity-device \
                --device <appro.verity> \
                --key SWK1.priv
    
    welma-signing-tools/common/sign-verity-device \
                --device <sysro.verity> \
                --key SWK1.priv
    

Fusing your Products

Offline signing of BL2 and FIP

BL2 signing

In order to sign BL2 with other keys, after a Yocto build (and out of the Yocto build environment), two scripts can to be used:

  1. stm32mp2-pkhth-gen: will create the PKHTH table for the new public keys
  2. stm32mp2-sign: will sign the binary file with the new private keys and insert the PKHTH table

These two scripts are Python implementations of the STM32MP_KeyGen_CLI and STM32MP_SigningTool_CLI precompiled tools provided by ST. This Python implementation offers more flexibility and easier integration into the Yocto environment.

However, stm32mp2-sign only supports signing BL2 binary files with private keys passed as arguments. For PKCS#11 support, please use STM32MP_SigningTool_CLI.

Alongside these two scripts, the stm32mp2-sigcheck tool can be used to verify the signature of a binary before deploying it to the board. This tool replicates the signature verification process performed by the ROM code on the BL2 binary.

Pre-requisites:

  • python3
  • pycryptodome module, v3.21.0 or later

Signing example:

$ stm32mp2-pkhth-gen --keys /path/to/publicKey*.pem \
    -o publicKeysHasheTableHash.bin

$ stm32mp2-sign --binary-image <TF-A BL2> \
    --output tf-a-bl2-signed.stm32 \
    --public-keys path/to/publicKey*.pem \
    --private-key path/to/privateKey0.pem

To check the binary's signature, stm32mp2-sigcheck can be used as follow:

$ stm32mp2-sigcheck -i tf-a-bl2-signed.stm32 \
    -k path/to/privateKey0.pem
If the private key is protected by a password, it can be provided to stm32mp2-sigcheck using the -p option.

FIP signing

To authenticate a FIP, the key and content certificates of its elements must be added, two tools can to be used (with ST support version):

  1. cert_create: will generate all required certificates
  2. fiptool: will create the FIP containing the generated certificates

Warning

The tools typically included in Linux distributions do not natively support ST-specific options. The ST-modified version of these tools should be used instead. These modified tools can be found in the TF-A build artifacts.

Example:

cert_create -n \
    --tfw-nvctr 0 \
    --ntfw-nvctr 0 \
    --key-alg ecdsa \
    --hash-alg sha256 \
    --rot-key /path/to/privateKey.pem \
    --fw-config /path/to/<TF-A FCONF>.dtb \
    --soc-fw /path/to/<BL31 Firmware> \
    --soc-fw-config /path/to/<BL31 DTB> \
    --ddr-fw /path/to/<DDR Firmware> \
    --tos-fw /path/to/<BL32 Firmware> \
    --tos-fw-extra1 /path/to/<BL32 Firmware EXTRA 1> \
    --tos-fw-extra2 /path/to/<BL32 Firmware EXTRA 2> \
    --nt-fw /path/to/<BL33 Firmware> \
    --hw-config /path/to/<BL33 DTB> \
    --trusted-key-cert /path/to/<Output Trusted key certificate> \
    --nt-fw-cert /path/to/<Output BL33 content certificate> \
    --nt-fw-key-cert  /path/to/<Output BL33 key certificate> \
    --tos-fw-cert  /path/to/<Output BL32 content certificate> \
    --tos-fw-key-cert /path/to/<Output BL32 key certificate> \
    --stm32mp-cfg-cert /path/to/<Output SMT32MP CFG certificate> \
    --soc-fw-cert /path/to/<Output BL31 content certificate> \
    --soc-fw-key-cert /path/to/<Output BL31 key certificate> \

fiptool create \
    --fw-config /path/to/<FCONF DTB> \
    --soc-fw /path/to/<BL31 Firmware>  \
    --soc-fw-config /path/to/<BL31 DTB> \
    --ddr-fw /path/to/<DDR Firmware> \
    --tos-fw /path/to/<BL32 Firmware> \
    --tos-fw-extra1 /path/to/<BL32 EXTRA 1> \
    --tos-fw-extra2 /path/to/<BL32 EXTRA 2> \
    --nt-fw /path/to/<BL33 Firmware> \
    --hw-config /path/to/<BL33 DTB> \
    --soc-fw-cert /path/to/<BL31 Content Certificate> \
    --soc-fw-key-cert /path/to/<BL31 Key Certificate> \
    --trusted-key-cert /path/to/<Trusted Key Certificate> \
    --nt-fw-cert /path/to/<BL33 Content Certificate> \
    --nt-fw-key-cert /path/to/<BL33 Key Certificate> \
    --tos-fw-cert /path/to/<BL32 Content Certificate> \
    --tos-fw-key-cert /path/to/<BL32 Key Certificate> \
    --stm32mp-cfg-cert /path/to/<STM32MP CFG Certificate> \
    <Output FIP Archive>

Burning the eFuses

The PKHTH generated by stm32mp2-pkhth-gen can be used in u-boot command line interpreter to burn the eFuses.

Warning

These commands are writing to One-Time Programmable (OTP) eFuses. Be absolutely certain that you want to write to the OTP before running these commands. Once written, the data cannot be changed or erased. Before closing the device, ensure there are no authentication error when loading BL2.

PKHTH provisioning

Example:

STM32MP> setenv ipaddr 169.254.142.1 && setenv serverip 169.254.142.224
STM32MP> tftp publicKeysHasheTableHash.bin
Using ethernet@30bf0000 device
TFTP from server 169.254.142.224; our IP address is 169.254.142.1
Filename 'publicKeysHasheTableHash.bin'.
Load address: 0x84000000
Loading: #
         67.4 KiB/s
done
Bytes transferred = 32 (20 hex)
STM32MP> stm32key read ${loadaddr} # Read the downloaded PKHTH
Read OEM-KEY1 at 0x84000000
OEM-KEY1 OTP 144: [84000000] 0ad86eb8
OEM-KEY1 OTP 145: [84000004] de5f9ad1
OEM-KEY1 OTP 146: [84000008] 4fc7850e
OEM-KEY1 OTP 147: [8400000c] 6c3592f4
OEM-KEY1 OTP 148: [84000010] 073dcc7b
OEM-KEY1 OTP 149: [84000014] cea61272
OEM-KEY1 OTP 150: [84000018] c59462c0
OEM-KEY1 OTP 151: [8400001c] a37f9be9
STM32MP> stm32key select OEM-KEY1 # Select PKHTH OTP
OEM-KEY1 selected
STM32MP> stm32key read # Read the current PKHTH OTP
OEM-KEY1 OTP 144: 00000000 lock : 00000000
OEM-KEY1 OTP 145: 00000000 lock : 00000000
OEM-KEY1 OTP 146: 00000000 lock : 00000000
OEM-KEY1 OTP 147: 00000000 lock : 00000000
OEM-KEY1 OTP 148: 00000000 lock : 00000000
OEM-KEY1 OTP 149: 00000000 lock : 00000000
OEM-KEY1 OTP 150: 00000000 lock : 00000000
OEM-KEY1 OTP 151: 00000000 lock : 00000000
OEM-KEY1 is not locked!
OEM-KEY1 is free!
STM32MP> stm32key fuse ${loadaddr} # Burning the eFuses
OEM-KEY1 OTP 144: 00000000 lock : 00000000
OEM-KEY1 OTP 145: 00000000 lock : 00000000
OEM-KEY1 OTP 146: 00000000 lock : 00000000
OEM-KEY1 OTP 147: 00000000 lock : 00000000
OEM-KEY1 OTP 148: 00000000 lock : 00000000
OEM-KEY1 OTP 149: 00000000 lock : 00000000
OEM-KEY1 OTP 150: 00000000 lock : 00000000
OEM-KEY1 OTP 151: 00000000 lock : 00000000
OEM-KEY1 is not locked!
OEM-KEY1 is free!
Writing OEM-KEY1 with
OEM-KEY1 OTP 144: [84000000] 0ad86eb8
OEM-KEY1 OTP 145: [84000004] de5f9ad1
OEM-KEY1 OTP 146: [84000008] 4fc7850e
OEM-KEY1 OTP 147: [8400000c] 6c3592f4
OEM-KEY1 OTP 148: [84000010] 073dcc7b
OEM-KEY1 OTP 149: [84000014] cea61272
OEM-KEY1 OTP 150: [84000018] c59462c0
OEM-KEY1 OTP 151: [8400001c] a37f9be9
Warning: Programming fuses is an irreversible operation!
         This may brick your system.
         Use this command only if you are sure of what you are doing!

Really perform this fuse programming? <y/N> y
Fuse OEM-KEY1 OTP 144 : 0ad86eb8
Fuse OEM-KEY1 OTP 145 : de5f9ad1
Fuse OEM-KEY1 OTP 146 : 4fc7850e
Fuse OEM-KEY1 OTP 147 : 6c3592f4
Fuse OEM-KEY1 OTP 148 : 073dcc7b
Fuse OEM-KEY1 OTP 149 : cea61272
Fuse OEM-KEY1 OTP 150 : c59462c0
Fuse OEM-KEY1 OTP 151 : a37f9be9
OEM-KEY1 updated !

Check the values written in the fuses with stm32key read in U-Boot:

STM32MP> stm32key read
OEM-KEY1 OTP 144: 0ad86eb8 lock : 40000000
OEM-KEY1 OTP 145: de5f9ad1 lock : 40000000
OEM-KEY1 OTP 146: 4fc7850e lock : 40000000
OEM-KEY1 OTP 147: 6c3592f4 lock : 40000000
OEM-KEY1 OTP 148: 073dcc7b lock : 40000000
OEM-KEY1 OTP 149: cea61272 lock : 40000000
OEM-KEY1 OTP 150: c59462c0 lock : 40000000
OEM-KEY1 OTP 151: a37f9be9 lock : 40000000

Closing the device

Before closing the device, verify that the authentication process is properly implemented in both the ROM code and TF-A. To close the device, stm32key is used as follow:

STM32MP> stm32key close

Test cases

The following test cases are designed to verify the secure boot process. The main objective is to ensure that there are no authentication error in BL2 or FIP that might indicate a security breach or a failure in the secure boot process.

BL2 and FIP signed with matching keys

When BL2 and FIP are signed with the keys corresponding to the PKHTH fused, a notice message is displayed when BL2 is compiled with DYN_DISABLE_AUTH=1:

Bootrom authentication succeeded

Corrupted BL2 signature or content

If an installed BL2 has a corrupted signature or content but the public key contained in its header is correct, the following Boot ROM error message appears and the boot continues because the FIP will be authenticated with the matching key:

Bootrom authentication failed

Corrupted FIP or signed with a wrong key

When a wrong key is used to sign the FIP, the authentication of the FIP elements by BL2 will fail and an -EAUTH (-80) error message is displayed:

ERROR:   BL2: Failed to load image id 26 (-80)

The image identifier depends on the FIP element whose authentication failed. You can find the list of image ids in TF-A source code tbbr_img_def_exp.h.