This post intends to provide all the information you need to understand and use the HAB (High Assurance Boot) on your Boundary Devices Nitrogen8 platform.
The goal is also to provide an update to our HAB for Dummies blog post so that new platforms are covered.
Indeed, the i.MX8M family (including i.MX8MQ, i.MX8M Mini and i.MX8M Nano) have a different boot process than older i.MX6/7 platforms. So although some of the details from the previous blog post still apply, a new version is required to explain/show the differences.
For simplicity and clarity, this guide and examples have been made on i.MX8M Mini based Nitrogen8M_Mini but the procedure applies to the entire Nitrogen8 family.
Please contact us if you need more details for other platforms.
- Code Signing Tools (CST)
- Account required on NXP website
- Provides valuable documentation
- U-Boot source code
- See latest branch from our U-Boot repo
- Boundary Devices Nitrogen8 platform
- Any of the platforms from the Nitrogen8 family
- Patience & focus
- If you miss a step, this can brick your platform for good
- You’ve been WARNED!
Before getting started, let’s explain a few acronyms related to this subject
- CSF: Command Sequence File
- CST: Code-Signing Tool
- DCD: Device Configuration Data
- DEK: Data Encryption Key
- HAB: High Assurance Boot
- IVT: Image Vector Table
- SRK: Super Root Key
The HAB library is a sub-component of the boot ROM on i.MX processors. It is responsible for verifying the digital signatures included as part of the product software and ensures that, when the processor is configured as a secure device, no unauthenticated code is allowed to run.
In short, enabling HAB/secure boot on your platform prevents hackers to alter the boot process, making sure only the software you have previously signed/approved can be ran. The ROM and HAB cannot be changed so they can be considered as trusted software components.
First, i.MX Boot ROM reads the eFuses to determine the security configuration of the SoC and the type of the boot device.
The ROM then loads the SPL image into the OCRAM memory along with the DDR training binaries. Next it verifies the authenticity of the SPL code both in flash and memory against the signature embedded in the binary (CSF).
That is the first stage verification. If it fails, execution is not allowed to leave the ROM for securely configured SoCs, also called “closed” devices.
However, with an “open” device, you will be able to see HAB events which will tell you if the image would pass the authentication process.
If it succeeds (or if the device is open), the Boot ROM executes the HDMI/DP Firmware first which in turn jumps to SPL. For other i.MX8M processors, there’s no such firmware so it goes straight to SPL.
SPL initializes the clocks, PMIC, (LP)DDR4 and UART. Then it is able to load and verify the rest of the components using the HAB APIs to extend the root of trust
A FIT image (see here for details) is used to package all those components which are:
- U-Boot binary + device tree blob (dtb)
- ARM Trusted Firmware (ATF)
- OP-TEE binary (TrustZone, optional, not covered in this blog post)
Finally, if that second stage verification succeeds, ATF is executed which will in turn execute U-Boot.
The root of trust can be extended again at U-Boot level to authenticate Kernel and M4 images.
In order to understand the signed image generation, it is best to have a look at the final
flash.bin image layout.
You can see there are 2 CSF binaries required, one per verification stage:
- CSF SPL that is used by Boot ROM to authenticate SPL + DDR FW
- CSF FIT that is used by SPL (through HAB APIs) to authenticate FIT components
How to enable/use it?
The below procedure will describe an example on how it has been done on one Nitrogen8M_Mini, make sure to modify the serial/password/keys values by your own!
1- Creation of the keys
First you need to unpack the Code Siging Tools package from NXP:
~$ tar xzf cst-3.3.0.tar.gz ~$ cd release/keys
Then a couple of files need to be created, the first one being name ‘serial’ with an 8-digit content. OpenSSL uses the contents of this file for the certificate serial numbers.
~/release/keys$ vi serial 42424242
Create a file called ‘key_pass.txt’ that contains your pass phrase that will protect the HAB code signing private keys.
The format of this file is the pass phrase repeated on the first and second lines of the file.
~/release/keys$ vi key_pass.txt Boundary123! Boundary123!
You can now create the signature keys.
~/release/keys$ ./hab4_pki_tree.sh ... Do you want to use an existing CA key (y/n)?: n Do you want to use Elliptic Curve Cryptography (y/n)?: n Enter key length in bits for PKI tree: 4096 Enter PKI tree duration (years): 20 How many Super Root Keys should be generated? 4 Do you want the SRK certificates to have the CA flag set? (y/n)?: y ...
Create the fuse table and binary to be flashed later.
~/release/keys$ cd ../crts/ ~/release/crts$ ../linux64/bin/srktool -h 4 -t SRK_1_2_3_4_table.bin -e SRK_1_2_3_4_fuse.bin -d sha256 -c \ ./SRK1_sha256_4096_65537_v3_ca_crt.pem,./SRK2_sha256_4096_65537_v3_ca_crt.pem,./SRK3_sha256_4096_65537_v3_ca_crt.pem,./SRK4_sha256_4096_65537_v3_ca_crt.pem -f 1
2- Flashing the keys
The fuse table generated in the previous section is what needs to be flashed to the device.
~/release/crts$ hexdump -e '/4 "0x"' -e '/4 "%X""\n"' < SRK_1_2_3_4_fuse.bin 0x3402271 0x67166C19 0x64679AE8 0x25056CEE 0x676D664 0xE46DD5CA 0x3A561C27 0x9E6742BA
The above command gives you what needs to be flashed in the proper order.
The commands below are made for i.MX8MQ, i.MX8M Mini and i.MX8M Nano only.
Make sure to use your own values here, otherwise your board will never be able to authenticate the boot image.
=> fuse prog -y 6 0 0x3402271 => fuse prog -y 6 1 0x67166C19 => fuse prog -y 6 2 0x64679AE8 => fuse prog -y 6 3 0x25056CEE => fuse prog -y 7 0 0x676D664 => fuse prog -y 7 1 0xE46DD5CA => fuse prog -y 7 2 0x3A561C27 => fuse prog -y 7 3 0x9E6742BA
If you flashed the above by mistake, without reading the note above, know that we share the set of keys used for this blog post:
3- Build U-Boot/SPL with security features
Build U-Boot with the CONFIG_SECURE_BOOT configuration enabled. Here is an example for Nitrogen8M_Mini:
~$ git clone https://github.com/boundarydevices/u-boot-imx6 \ -b boundary-v2018.07 ~$ cd u-boot-imx6 ~/u-boot-imx6$ export ARCH=arm64 ~/u-boot-imx6$ export CROSS_COMPILE=aarch64-linux-gnu- ~/u-boot-imx6$ make nitrogen8mm_2g_defconfig ~/u-boot-imx6$ make menuconfig <select 'ARM architecture' - 'Support i.MX HAB features'> ~/u-boot-imx6$ make flash.bin V=1 ... ========= OFFSET dump ========= Loader IMAGE: header_image_off 0x0 image_off 0x40 csf_off 0x2ae00 spl hab block: 0x7e0fc0 0x0 0x2ae00 Second Loader IMAGE: sld_header_off 0x57c00 sld_csf_off 0x58c20 sld hab block: 0x401fcdc0 0x57c00 0x1020
Note that the “hab block” lines are very important when creating the
- spl hab block: includes IVT (SPL) +
- sld hab block: includes FDT (FIT header) + IVT (FIT)
Also the “csf_off” are useful to know where to copy the signatures.
~/u-boot-imx6$ ./print_fit_hab.sh 0x40200000 0x5AC00 0xA0AF0 0x402A0AF0 0xFB6F0 0x72E0 0x920000 0x1029D0 0x9170
The above output gives you the addresses/offsets of (in that order):
bl31.bin(ARM Trusted Firmware)
4- Sign your flash.bin bootloader
Go to the CST tools again:
~$ cd ~/release/linux64/bin/ ~/release/linux64/bin$ cp ~/u-boot-imx6/flash.bin .
At this point you need to create two .
csf file using the information from previous section:
- csf_spl.txt: definition of the SPL signature which includes:
- IVT (SPL) +
u-boot-spl-ddr.bin(see spl hab block)
- IVT (SPL) +
- csf_fit.txt: definition of the FIT signature that includes:
- FDT (FIT) + IVT (FIT) (see sld hab block)
u-boot-nodtb.bin(see print_fit_hab.sh output)
u-boot.dtb(see print_fit_hab.sh output)
bl31.bin(see print_fit_hab.sh output)
You can download the files we used for this blog post:
~/release/linux64/bin$ wget https://storage.googleapis.com/boundarydevices.com/csf_spl.txt ~/release/linux64/bin$ wget https://storage.googleapis.com/boundarydevices.com/csf_fit.txt ... edit the size in the "Blocks = " lines in both files... ~/release/linux64/bin$ ./cst -i csf_spl.txt -o csf_spl.bin CSF Processed successfully and signed data available in csf_spl.bin ~/release/linux64/bin$ ./cst -i csf_fit.txt -o csf_fit.bin CSF Processed successfully and signed data available in csf_fit.bin
At this point, all the signatures are generated, you can now insert them into the (unsigned)
~/release/linux64/bin$ cp flash.bin signed_flash.bin ~/release/linux64/bin$ dd if=csf_spl.bin of=signed_flash.bin seek=$((0x2ae00)) bs=1 conv=notrunc ~/release/linux64/bin$ dd if=csf_fit.bin of=signed_flash.bin seek=$((0x58c20)) bs=1 conv=notrunc
You can see that the seek addresses match the ‘csf_off‘ info provided by U-Boot.
That’s a lot of offsets/addresses to remember!
You might wonder if there isn’t a way to make the whole process above simpler?
Writing this blog post required a lot of concentration not to get any address wrong, so we created scripts that can do everything for you. This script requires you to specify the path of the CST tool as well as the keys necessary for signing, that’s it. Here is the simplified version to build and sign U-Boot:
~/u-boot-imx6$ export CST_BIN=~/release/linux64/bin/cst ~/u-boot-imx6$ export SIGN_KEY=~/release/crts/CSF1_1_sha256_4096_65537_v3_usr_crt.pem ~/u-boot-imx6$ export SRK_TABLE=~/release/crts/SRK_1_2_3_4_table.bin ~/u-boot-imx6$ make flash.bin V=1 ~/u-boot-imx6$ ./sign_hab_imx8m.sh CSF Processed successfully and signed data available in csf_spl.bin CSF Processed successfully and signed data available in csf_fit.bin 6472+0 records in 6472+0 records out 6472 bytes (6.5 kB, 6.3 KiB) copied, 0.00416391 s, 1.6 MB/s 6488+0 records in 6488+0 records out 6488 bytes (6.5 kB, 6.3 KiB) copied, 0.00414051 s, 1.6 MB/s signed_flash.bin is ready!
~/release/linux64/bin$ cp signed_flash.bin <mount_point>/u-boot.nitrogen8mm_2g
5- Flash and test the device
Use our standard procedure to update U-Boot:
=> run upgradeu
The device should reboot, then check the HAB status:
=> hab_status Secure boot disabled HAB Configuration: 0xf0, HAB State: 0x66 No HAB Events Found!
The Secure boot disabled means that the device is open. But still the HAB engine will check the image and report errors (Events) if the signature isn’t right.
6- Closing the device?
Once you are *absolutely* sure you’ve understood what has been done so far and that you are sure it works, you can “close” the device.
Once again, this step is IRREVERSIBLE, better make sure there is no HAB Events in open configuration.
Below is the procedure to “close” the device on i.MX8MQ and i.MX8M Mini.
=> fuse prog 1 3 0x02000000 => reset ... => hab_status Secure boot enabled HAB Configuration: 0xcc, HAB State: 0x99 No HAB Events Found!
What about USB recovery?
Although this step was pretty complicated on i.MX6/7 due to the DCD reset, it is much easier now.
As the DCD pointer is always NULL now, you can simply turn on the USB recovery switch on your platform for it to appear as a NXP device:
~/u-boot-imx6$ lsusb | grep NXP Bus 001 Device 064: ID 1fc9:0134 NXP Semiconductors SP Blank M845S
At this point you just need to run
uuu tool and it will load your signed binary:
~/u-boot-imx6$ uuu -d signed_flash.bin
If you’re not familiar with
uuu, we highly recommend reading our blog post on the topic:
What about authenticating the kernel?
This part hasn’t changed since out previous blog post so you can have a look at it:
Boot time impact
We have not run any test to know how much impact HAB has on boot time yet.
Enabling the signature definitely increases the time it takes before U-Boot is loaded. Feel free to let us know if you have made some comparison.