1. Configure secure boot
To build signed artifacts, modify your conf/local.conf
file to include the following:
# Required to include trustfence support.
INHERIT += "trustfence"
Digi Embedded Yocto generates a PKI tree by default under a new folder called trustfence
inside your project.
To specify a custom path for the PKI tree, see Advanced options.
1.1. Hardware unique key (HUK)
The hardware unique key (HUK) is a symmetric encryption key stored in the OTP bits of the platform. The HUK is not used directly to encrypt data. Instead, it is used as a derivative for other keys. This means you can use different keys for different encryption purposes and know that the keys cannot be used to access data on a different device, even if they are leaked.
Each chip has a unique HUK pre-provisioned by STMicroelectronics.
1.2. U-Boot environment encryption
When TrustFence is enabled, the U-Boot environment is encrypted by default using a key derived from the chip hardware unique key (HUK).
To explicitly disable U-Boot environment encryption, see Advanced options. |
2. Build your target images
Once TrustFence is enabled and configured in your Digi Embedded Yocto project, you can build your target image. For example:
$ bitbake core-image-base
When the build process finishes, several secure artifacts appear in the deployment directory:
-
arm-trusted-firmware/tf-a-ccmp13-dvk-<ram_size>-nand.stm32_signed
: This is the signed ARM Trusted Firmware for booting from the NAND. -
fip/fip-ccmp13-dvk-<ram_size>-optee_Signed.bin
: This is the signed FIP image with the U-Boot bootloader and Trusted Execution Environment (OP-TEE). -
fitImage-ccmp13-dvk.bin
: This is a signed FIT image that contains the following artifacts with signed hashes:-
The Linux kernel
-
The device tree
-
Device tree overlays
-
Boot script
-
The fitImage-ccmp13-dvk.bin is contained inside the <image-name>-boot.ubifs image file.
|
Additionally, when not provided, TrustFence keys are generated at the specified TRUSTFENCE_SIGN_KEYS_PATH
location (by default, a subfolder in the project called trustfence/
).
For additional details on TrustFence keys, see Summary of TrustFence keys.
These files need to be securely stored in order to be used in the manufacturing of secure devices. |
3. Program the signed artifacts
Deploy the built artifacts.
You can use the instructions at Program images using Fastboot, but you must also pass option -t
for the install script to use TrustFence artifacts:
$ ./install_linux_fw_uuu.sh -t
The default U-Boot is prepared to boot a regular Linux kernel with zImage format.
This is determined by variable If you use a different method to deploy the images, you must set this variable manually in U-Boot as follows:
|
4. Boot the signed artifacts
Reset the device. Check the new messages that appear:
NOTICE: CPU: STM32MP133C Rev.Z
NOTICE: Model: Digi International ConnectCore MP13 Development Kit
NOTICE: Bootrom authentication failed
NOTICE: BL2: v2.6-stm32mp1-r2.0(release):dey-4.0-r2.2(56756557)
NOTICE: BL2: Built : 15:41:16, Mar 10 2023
NOTICE: TRUSTED_BOARD_BOOT support enabled
NOTICE: ROTPK is not deployed on platform. Skipping ROTPK verification.
NOTICE: ROTPK is not deployed on platform. Skipping ROTPK verification.
NOTICE: BL2: Booting BL32
optee optee: OP-TEE: revision 3.16 (da44f979)
U-Boot dub-2021.10-r2.2-00003-g7dd1458751a7 (Mar 21 2023 - 08:48:30 +0000)
[...]
The Bootrom authentication failed
message appears.
This means the ROM loader detected that it is booting a signed image, but it could not authenticate it.
This is expected because you have not yet programmed the hash of the eight ECC public keys hashes table in the OTP bits.
The next message is TRUSTED_BOARD_BOOT support enabled
.
This means the TF-A image (or first-stage bootloader) has been built with authentication support enabled; that is, FSBL will either authenticate the second-stage boot loader or refuse to boot.
The next message is ROTPK is not deployed on platform. Skipping ROTPK verification
.
This displays because the Root Of Trust Public Key (ROTPK) has not yet been programmed in the OTP bits, so the FSBL skips the authentication of the second-stage bootloader and just boots it.
Following that, OP-TEE runs and then U-Boot starts.
The U-Boot environment encryption feature is enabled by default, so U-Boot will import the MAC addresses (and any other write-once environment variables) from the previous (not encrypted) environment, and will reset any other variable to the default values. After that, a save operation will overwrite the environment with encrypted data.
To ensure that both copies of the environment get encrypted, save the environment twice right after booting a U-Boot with environment encryption support, as follows:
=> saveenv
Saving Environment to UBI... Writing to UBI... done
OK
=> saveenv
Saving Environment to UBI... Writing to redundant UBI... done
OK
4.1. Boot the FIT image
As part of the Digi Embedded Yocto project, the U-Boot recipe includes a U-Boot boot script to boot the ConnectCore MP13 Development Kit. This script determines the variant of the ConnectCore MP13 SoM and the version of the carrier board and selects the device tree overlays to apply on top of the base device tree (wireless, Bluetooth, etc.) before booting the kernel. This boot script is embedded and signed in the FIT image and is run by default when you start the device.
Let U-Boot run, or run the boot
command if you stopped at the U-Boot prompt.
Check the new messages that appear:
Loading file 'fitImage-ccmp13-dvk.bin' to addr 0xc2000000...
Done
## Executing script at c2000000
sha256,rsa2048:fitimg+ sha256+ Booting from system A
Skip re-loading of FIT image
Explanation of output:
-
The FIT image is loaded into RAM memory and the boot script is authenticated against the public key.
-
Authentication succeeds and the boot script prints the message:
"Booting from system A"
. -
The boot script calls the
dboot
command to boot the system. While this command typically loads the image into RAM, in this case the FIT image is not reloaded because it was already loaded to run the boot script.
Next, U-Boot successfully authenticates the default configuration:
## Loading kernel from FIT Image at c2000000 ...
Using 'conf-ccmp157-dvk.dtb' configuration
Verifying Hash Integrity ... sha256,rsa2048:fitcfg+ OK
Trying 'kernel-1' kernel subimage
Description: Linux kernel
Created: 2023-11-09 14:38:10 UTC
Type: Kernel Image
Compression: uncompressed
Data Start: 0xc20000e8
Data Size: 6840888 Bytes = 6.5 MiB
Architecture: ARM
OS: Linux
Load Address: 0xc0800000
Entry Point: 0xc0800000
Hash algo: sha256
Hash value: 9c146479659fb34f23c1f4cd2975f8c8be175b2efd2db385ab7f753cd276e6ba
Sign algo: sha256,rsa2048:fitimg
Sign value: a86ebef17395f6e1df4b67c6e421fd36d27ef1b4217f19d14492cb4b2e17d0992e377b36c6873f850c780ce408b88f04d04aca35c1acaf68488cfdcbc6c2c711d04c
Timestamp: 2023-11-09 14:38:10 UTC
Verifying Hash Integrity ... sha256,rsa2048:fitimg+ sha256+ OK
Explanation of output:
-
The first item in the default configuration is the Linux kernel.
-
U-Boot also authenticates the kernel image successfully.
-
Similar messages appear for the base device tree and the selected overlays that the boot script populated as a list in U-Boot environment variable
overlays
. -
After all the artifacts have been authenticated, the Linux system boots.
The FIT image is the only file that’s stored in the linux partition, as it includes all the required files to boot the system. |
5. Secure the device
The final step in configuring secure boot for a device is burning the secure OTP configuration.
The secure OTP configuration can only be written once and is irreversible. |
To secure the device:
5.1. Program the Public Key Hash Table Hash (PKHTH)
The PKHTH OTP bits (words 24..31) hold the hash of the eight ECC public keys hashes table. For additional details on TrustFence keys, see Summary of TrustFence keys.
The ConnectCore MP13 has two boot stages:
-
Initially, the ROM loader uses the PKHTH OTP bits to authenticate the FSBL (TF-A). In open devices, the FSBL will boot in any case and the log reports whether the authentication was successful or failed.
-
Next, the FSBL (built with authentication support when TrustFence is enabled) authenticates the second stage bootloader (SSBL), the FIP image. If the PKHTH is not programmed, it skips the authentication and boots it; if the PKHTH is programmed, it validates the signature and refuses to boot if the authentication fails.
To program the PKHTH:
-
Check the current status of the device:
=> trustfence status * PKHTH fuses: [NOT PROGRAMMED] [NOT LOCKED] * EDMK fuses: [NOT PROGRAMMED] [NOT LOCKED] * Secure boot: [OPEN] * JTAG: [OPEN]
The command reports the PKHTH is not programmed and the device is open.
-
Transfer the
publicKeysHashHashes.bin
(by default inside thetrustfence/
folder in your project) into an address in RAM, for example via TFTP:=> tftp ${loadaddr} publicKeysHashHashes.bin
-
Read the contents of that RAM address interpreted as PKHTH. This command prints the hexadecimal bytes that form the PKHTH:
=> trustfence read_pkhth ${loadaddr} Read PKHTH at 0xc2000000 PKHTH OTP 24: [c2000000] 5cda63dc PKHTH OTP 25: [c2000004] 5a2b8e15 PKHTH OTP 26: [c2000008] f0708804 PKHTH OTP 27: [c200000c] c2dfc733 PKHTH OTP 28: [c2000010] a34fb740 PKHTH OTP 29: [c2000014] 246b14e3 PKHTH OTP 30: [c2000018] ef53f16b PKHTH OTP 31: [c200001c] 53ef8f83
-
Verify the hexadecimal values match the contents of the
publicKeysHashHashes.bin
that corresponds to the PKI tree you are using to sign the files. Remember to back up this PKI tree to be able to sign future images. -
Program the PKHTH in the OTP bits (irreversible):
=> trustfence prog_pkhth ${loadaddr} PKHTH OTP 24: 00000000 lock : 10000000 PKHTH OTP 25: 00000000 lock : 10000000 PKHTH OTP 26: 00000000 lock : 10000000 PKHTH OTP 27: 00000000 lock : 10000000 PKHTH OTP 28: 00000000 lock : 10000000 PKHTH OTP 29: 00000000 lock : 10000000 PKHTH OTP 30: 00000000 lock : 10000000 PKHTH OTP 31: 00000000 lock : 10000000 PKHTH is not locked! PKHTH is free! Writing PKHTH with PKHTH OTP 24: 5cda63dc lock : 50000000 PKHTH OTP 25: 5a2b8e15 lock : 50000000 PKHTH OTP 26: 17125380 lock : 50000000 PKHTH OTP 27: c2dfc733 lock : 50000000 PKHTH OTP 28: a34fb740 lock : 50000000 PKHTH OTP 29: 246b14e3 lock : 50000000 PKHTH OTP 30: ef53f16b lock : 50000000 PKHTH OTP 31: 53ef8f83 lock : 50000000 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 PKHTH updated!
At this point, the PKHTH has been programmed but the device is still open and can still boot any unsigned images.
-
Reset the device, and check the new messages that appear:
NOTICE: CPU: STM32MP133C Rev.Z NOTICE: Model: Digi International ConnectCore MP13 Development Kit NOTICE: Bootrom authentication succeeded NOTICE: BL2: v2.6-stm32mp1-r2.0(release):dey-4.0-r2.2(56756557) NOTICE: BL2: Built : 15:41:16, Mar 10 2023 NOTICE: TRUSTED_BOARD_BOOT support enabled NOTICE: BL2: Booting BL32 optee optee: OP-TEE: revision 3.16 (da44f979) U-Boot dub-2021.10-r2.2-00003-g7dd1458751a7 (Mar 21 2023 - 08:48:30 +0000) [...] =>
The
Bootrom authentication succeeded
message appears. This means the ROM loader detected that it is booting a signed image and it could successfully authenticate it against the programmed PKHTH.The next message is
TRUSTED_BOARD_BOOT support enabled
. This means the TF-A image (or first-stage bootloader) has been built with authentication support enabled; that is, FSBL will either authenticate the second-stage bootloader or refuse to boot.If the boot continues at this point, the FIP image was authenticated successfully. If authentication fails, the FIP image won’t boot at all, even if the device is open.
-
Check the TrustFence status:
=> trustfence status * PKHTH fuses: [PROGRAMMED] [LOCKED] * EDMK fuses: [NOT PROGRAMMED] [NOT LOCKED] * Secure boot: [OPEN] * JTAG: [OPEN]
On open devices you can still boot unsigned images without authentication support (without TrustFence). |
5.2. Close the device
This step is irreversible and could brick your device. |
Before closing the device:
-
Verify you have programmed signed, not encrypted, TF-A and FIP images in the NAND.
-
Verify you have programmed the PKHTH in the OTP bits.
-
Verify the FSBL shows
Bootrom authentication succeeded
. -
Verify the FSBL shows
TRUSTED_BOARD_BOOT support enabled
. -
Verify the images boot up to Linux.
-
Make sure you have a backup of the generated PKI tree (by default in the
trustfence/
folder in your project).
To close the device:
=> trustfence close
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
Device is closed!
After that, the device will only boot properly signed images.
-
Reboot the device
-
Check the TrustFence status:
=> trustfence status * PKHTH fuses: [PROGRAMMED] [LOCKED] * EDMK fuses: [NOT PROGRAMMED] [NOT LOCKED] * Secure boot: [CLOSED] * JTAG: [OPEN]
Advanced options
Disable signing of images
Signing of images is automatically enabled as part of TrustFence.
To explicitly disable the generation of signed images, define TRUSTFENCE_SIGN
to 0:
# Disable signed images
TRUSTFENCE_SIGN = "0"
The use case for disabling image signing is to separate the generation of the image from the signing process. For instance, you may want to avoid managing security keys on development machines and instead only sign images on a separate, more secure computer.
Use a custom PKI tree path
Digi Embedded Yocto creates by default a folder called trustfence/
inside your project to hold all keys.
To customize the location of the TrustFence keys set variable TRUSTFENCE_SIGN_KEY_PATH
in your project’s conf/local.conf
:
# Use a custom path to the signature keys and certificates.
TRUSTFENCE_SIGN_KEYS_PATH = "/mnt/secure/PKI_tree"
Use custom FIT sign keys
Digi Embedded Yocto generates random keys to sign the artifacts in the FIT image, but you can optionally provide custom ones:
-
If it doesn’t already exist, create a subdirectory
fit
inside your PKI tree path to hold the key pairs.$ mkdir <your-trustfence-sign-keys-path>/fit
-
Optionally, edit your project config file
conf/local.conf
and set the following variables to give your keys custom names. (If not provided, the default filenames arefitcfg
andfitimg
.)conf/local.confTRUSTFENCE_FIT_CFG_SIGN_KEYNAME = "my-fitcfg" TRUSTFENCE_FIT_IMG_SIGN_KEYNAME = "my-fitimg"
-
Generate an RSA private key to sign the FIT image nodes and another one to sign the configuration nodes:
$ openssl genrsa -F4 -out <your-trustfence-sign-keys-path>/fit/<your-fitcfg-name>.key 2048 $ openssl genrsa -F4 -out <your-trustfence-sign-keys-path>/fit/<your-fitimg-name>.key 2048
-
Generate public certificates out of the private keys:
$ openssl req -batch -new -x509 -key <your-trustfence-sign-keys-path>/fit/<your-fitcfg-name>.key -out <your-trustfence-sign-keys-path>/fit/<your-fitcfg-name>.crt $ openssl req -batch -new -x509 -key <your-trustfence-sign-keys-path>/fit/<your-fitimg-name>.key -out <your-trustfence-sign-keys-path>/fit/<your-fitimg-name>.crt
-
Rebuild your project.
Disable U-Boot environment encryption
To explicitly disable U-Boot environment encryption, set TRUSTFENCE_ENCRYPT_ENVIRONMENT
to 0 as follows:
# Disable U-Boot environment encryption
TRUSTFENCE_ENCRYPT_ENVIRONMENT = "0"
When flashing U-Boot without the environment encryption feature in a device with an encrypted environment, all the values will be lost. Be sure to save any important data such as MAC addresses before you execute this procedure. |