Skip to content

Secure boot on STM32MP

Overview

This page explains the bootloader authentication on Welma, for machines with STM32MP25 and STM32MP13 processors.

References:

Acronyms:

  • BL2: Boot Loader stage 2
  • CoT: Chain Of Trust
  • FIP: Firmware Image Package, packaging format used by TF-A to package firmware images in a single binary
  • PKH: Public Key Hash
  • PKHTH: Public Key Hash Table Hash
  • ROT: Root Of Trust
  • ROTPK: Root Of Trust Public Key
  • TBBR: Trusted Board Boot Requirements
  • TF-A: Trusted Firmware-A

Runtime architecture

The bootloader is made of 2 parts:

  • BL2, which gets authenticated by the ROM code
  • FIP, which contains BL3 firmware images and gets authenticated by BL2

BL2 structure

The BL2 image is structured as follows:

       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 possible extensions: authentication, decryption, and padding. By default in Welma, the decryption extension is not used.

The base header primarily includes the signature and option flags that enable each of the three extensions. The signature is computed differently on STM32MP13 and STM32MP25. See STM32 header for binary files.

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.

FIP structure

The FIP consolidates all BL3 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     | (N/A on STM32MP13)
+----------------------------+-----------------+
|     BL31 Device Tree       | --soc-fw-config | (N/A on STM32MP13)
+----------------------------+-----------------+
|       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     | (N/A on STM32MP13)
+----------------------------+-----------------+

To guarantee the authenticity of the binaries within the FIP, TF-A employs a chain of certificates 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:

                   +-------+
                   | 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      |
               +------------------+    +----------------------+

Ref: TF-A BL2 Trusted Board Boot, STM32 MPU implementation

In the Chain of Trust, the Root of Trust Public Key must be the same key used to authenticate BL2. This 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  |  (N/A on STM32MP13)
+-------------------------------+--------------------+
|   BL31 content certificate    |   --soc-fw-cert    |  (N/A on STM32MP13)
+-------------------------------+--------------------+
|     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 using TF-A's cert_create tool (see FIP signing below). In this case, the private keys associated with the certificates are generated on the fly and not kept: new keys are generated each time a FIP is signed (except for the ROT key).

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, the algorithms used are ECDSA (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 BL2 into its 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 (N/A on STM32MP13)
      • 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 (N/A on STM32MP13)
      • 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 (N/A on STM32MP13), BL32 (OP-TEE) and BL33 (U-Boot)

Yocto configuration

This section explains how Welma configures secure boot in Yocto and points out the key variables that you should check.

  • 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 stm32mp-sign)
    • 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

In Welma, two images of tf-a get built: one for production and one for development (with "-dev" in the name). The latter is compiled with DISABLE_IMAGE_AUTH=1 (which does not prevent booting when the FIP authentication fails) and with DYN_DISABLE_AUTH=1 (for logging and testing purpose). The development image should not be used in production.

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

Sign images with production keys

The images resulting from the Yocto build are signed with development keys that should not be used for production, as they are publicly disclosed.

This section explains how to sign images with other keys, out of the Yocto build environment.

The following keys should be defined:

  • Root Of Trust key pair, using ECC algorithm, for signing BL2 and FIP
  • SWK1, using RSA algorithm, for signing the kernel image and the filesystems

BL2 signing

In order to sign BL2, two scripts can to be used:

  1. stm32mp-pkhth-gen: will create the PKHTH table for the new public keys
  2. stm32mp-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, stm32mp-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 stm32mp-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:

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

$ stm32mp-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, stm32mp-sigcheck can be used as follow:

$ stm32mp-sigcheck -i tf-a-bl2-signed.stm32 \
    -k path/to/publicKey0.pem

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 (STM32MP25):

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>

Global signing process

Input:

  • Generated by the Yocto build:

    • BL2 image
    • FIP image
    • fitImage (BOOT)
    • .verity images (SYSRO, APPRO)
  • Production Keys: ROT key pair, SWK1.priv, SWK1.crt

Output:

  • Signed images provisioned with necessary public keys and certificates):

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

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
    

Secure products (manufacturing)

In manufacturing, each product has to be secured with the following steps:

  • Burn (fuse) the PKHTH into the chip (see below)
  • Close the chip (see Closing the device)
  • Disable JTAG

Burning the eFuses

The PKHTH is generated by stm32mp-pkhth-gen (see BL2 signing). It can be burnt into the eFuses by u-boot commands.

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.

The commands to burn the PKHTH are:

  • on STM32MP13:
    STM32MP> tftp publicKeysHashTableHash.bin
    STM32MP> stm32key select PKHTH
    STM32MP> stm32key fuse ${loadaddr}
    
  • on STM32MP25:
    STM32MP> tftp publicKeysHashTableHash.bin
    STM32MP> stm32key select OEM-KEY1
    STM32MP> stm32key fuse ${loadaddr}
    

Detailed example (STM32MP25):

STM32MP> setenv ipaddr 169.254.142.1 && setenv serverip 169.254.142.224
STM32MP> tftp publicKeysHashTableHash.bin
Using ethernet@30bf0000 device
TFTP from server 169.254.142.224; our IP address is 169.254.142.1
Filename 'publicKeysHashTableHash.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 PKHTH is correctly fused
  • the ROM code properly authenticates BL2
  • BL2 properly authenticates FIP

To close the device, execute:

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 signed with matching keys

When BL2 is signed with keys matching the fused PKHTH, a notice message is displayed by BL2:

Bootrom authentication succeeded
(on STM32MP25, BL2 needs to be compiled with DYN_DISABLE_AUTH=1, for this message to be displayed)

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, BL2 displays this message:

Bootrom authentication failed
(on STM32MP25, BL2 needs to be compiled with DYN_DISABLE_AUTH=1, for this message to be displayed)

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.