High Assurance Boot
1 HAB introduction
HAB is an optional feature in the i.MX SOC family, which allows you to make sure only software images signed by you can be executed on the SOC.
It incorporates boot ROM level security which cannot be altered after programming the appropriate one-time electrically programmable fuses (eFuses). The boot ROM is responsible for loading the initial software image from the boot medium (usually this initial software is a bootloader such as SPL/U-Boot. HAB enables the boot ROM to authenticate the initial software image by using digital signatures. It also provides a mechanism to establish a chain of trust for the remaining software components (such as the kernel image) and thus to establish a secure state of the system.
HAB authentication is based on public key cryptography using the RSA algorithm.
It consists of the following stages:
1. Offline signing of the software images using private keys.
The image data is signed offline using a series of private keys. This is done using NXP's Code Signing Tool, and Variscite's scripts, which make the process extremely easy and simple.
2. Fusing the i.MX SOC with the corresponding public keys.
The key structure is called a PKI tree and Super Root Keys (SRK) are components of it. A table of the public SRKs are hashed and permanently written to the SOC using eFuses.
You have the option to let the processor keep running unsigned images, while creating useful HAB messages, until you decide to “close” it by writing a dedicated bit using another eFuse. This allows you to test the sign-authenticate process and verify that it was done correctly before completely and permanently “closing” the processor to only execute your signed images.
3. Authentication of the software images on the target during boot time.
The signed image data is verified on the i.MX processor using the corresponding public keys.
HAB evaluates the SRK table included in the signature by hashing it and comparing the result to the SRK fuse values. If the SRK verification is successful, this establishes the root of trust, and the remainder of the signature can be processed to authenticate the image.
Once the initial bootloader is authenticated and executed, the chain of trust continues by authenticating each of the next loaded images before executing them.
E.g. The boot ROM authenticates SPL, SPL authenticates U-Boot, and U-Boot authenticates the Linux kernel.
- i.MX Applications Processor Trust Architecture
- i.MX Secure Boot on HABv4 Supported Devices
- i.MX 6 Linux High Assurance Boot (HAB) User's Guide
3 Code signing step by step instructions
3.1 Toolchain installation for out of Yocto builds
To install the toolchain, follow.
3.2 Build U-Boot with secure boot support
$ cd ~ $ git clone https://github.com/varigit/uboot-imx.git $ cd uboot-imx $ git checkout imx_v2015.04_4.1.15_1.1.0_ga_var03
$ source /opt/fslc-x11/2.3.1/environment-setup-armv7at2hf-neon-fslc-linux-gnueabi
$ make mrproper
Choose one of the following configurations: For booting from SD card/eMMC: $ make mx6ul_var_dart_mmc_SECURE_BOOT_defconfig For booting from NAND flash: $ make mx6ul_var_dart_nand_SECURE_BOOT_defconfig
$ make -j4
Make sure you get the following files:
SPL SPL.log u-boot-ivt.img u-boot-ivt.img.log
3.3 Download and unpack the Freescale Code Signing Tool (CST)
Download the Freescale Code Signing Tool (CST) for the High Assurance Boot (HAB) library from the NXP website (registration required):
Unpack the downloaded archive:
$ cd ~ $ tar xf ~/Downloads/cst-2.3.3.tar.gz
You are encouraged to read the documents under ~/cst-2.3.3/docs
3.4 Download the Variscite CST scripts
$ cd ~/cst-2.3.3/linux64/bin $ git init $ git remote add origin https://github.com/varigit/var-hab-cst-scripts.git $ git pull origin master
3.5 Generate Public Key Infrastructure (PKI) tree
$ cd ~/cst-2.3.3/keys
Create a text file called "serial", which contains 8 digits. It will be used for the certificate serial numbers.
$ echo 12481632 > serial
Create a text file called "key_pass.txt", which contains two lines of a password repeated twice.
This password will be used to protect the generated private keys.
All private keys in the PKI tree are in PKCS #8 format will be protected by the same password.
$ echo Variscite_password > key_pass.txt $ echo Variscite_password >> key_pass.txt
Now, to generate the PKI tree, run the following:
And complete the interactive questions. For example:
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): 10 How many Super Root Keys should be generated? 4 Do you want the SRK certificates to have the CA flag set? (y/n)?: y
This example tree creates a new CA, uses 4096 bit keys, lasts for 10 years (HAB does not consider the duration), and has 4 SRKs.
For all Variscite products (technically, for all i.MX series HABv4 enabled parts except the i.MX6SX), the last question regarding the “CA flag” in the SRK must be answered as "y".
The resulting private keys are placed in the keys directory of the CST, and the corresponding X.509 certificates are placed in the crts directory.
Note that the names of the created certificate files reflect the options you chose here (such as the key length), so if you chose different values than the above example you may need to make adjustments to the file names in the next section (Generate SRK table), and in the default csf file (var-default.csf).
3.6 Generate Super Root Key (SRK) table
Using the X.509v3 public key certificates from the previous step, create the SRK table (a table of the public SRKs) and the SRK fuse table to be programmed into the SOC fuses:
$ cd ~/cst-2.3.3/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
3.7 Program the SRK (public keys) to the SOC e-fuses
Run the following script to display the U-Boot commands for programming the SRK fuses with the values from your SRK fuse table:
$ cd ~/cst-2.3.3/linux64/bin $ ./var-u-boot_fuse_commands.sh mx6ul
Boot the board, hit any key at the right time to stop autoboot and get to the U-Boot command line, and run the commands outputted by the above script.
The commands should look similar to the following.
- The following values are just an example - you should use your own values!
- These are One-Time Programmable e-fuses. Once you write them you can't go back, so get it right the first time.
fuse prog 3 0 0x512193BE fuse prog 3 1 0x220E1AD7 fuse prog 3 2 0x7B3F68C5 fuse prog 3 3 0x41DEA681 fuse prog 3 4 0xFBCA5CEF fuse prog 3 5 0xA537BF95 fuse prog 3 6 0x888E9AF fuse prog 3 7 0x1136264D
3.8 Sign the U-Boot and kernel images
3.8.1 The signing process
- General usage of the Variscite var-som_sign_image.sh script:
For an i.MX6UL SOC:
Usage: SOC=mx6ul var-som_sign_image.sh IMGFILE [IMGEFILE]...
For an i.MX6ULL SOC:
Usage: SOC=mx6ull var-som_sign_image.sh IMGFILE [IMGEFILE]...
Note: for U-Boot/SPL images, make sure their log file is present in the same directory
- What the script does:
In the build process of U-Boot with secure boot support, the U-Boot image size is already aligned to 4KB and its Image Vector Table (IVT) is already appended to it (that's why the result image is named u-boot-ivt.img). Also, the HAB Blocks data is saved to the SPL.log and u-boot-ivt.img.log files.
When a log file is not present for an image, the script assumes it is a regular kernel image, so the image is first padded to align its size to 4KB, and an IVT is created and appended to it.
The kernel IVT binary content is this:
0x412000D1 # IVT Header: Tag=0xD1, Length=0x0020, Ver=0x41 loadaddr+0x1000 # Jump Location 0x0 # Reserved1 0x0 # DCD pointer 0x0 # Boot Data loadaddr+IVT_offset # Self Pointer loadaddr+IVT_offset+IVT_size # CSF Pointer 0x0 # Reserved2
The script uses the following default command sequence file (csf) data (for i.MX6ULL we use "Engine = SW" because it doesn't have CAAM) and fills in the correct Blocks values under the "Authenticate Data" section:
[Header] Version = 4.1 Hash Algorithm = sha256 Engine = ANY Engine Configuration = 0 Certificate Format = X509 Signature Format = CMS [Install SRK] File = "../crts/SRK_1_2_3_4_table.bin" Source index = 0 # Index of the key location in the SRK table to be installed [Install CSFK] # Key used to authenticate the CSF data File = "../crts/CSF1_1_sha256_4096_65537_v3_usr_crt.pem" [Authenticate CSF] [Install Key] # Key slot index used to authenticate the key to be installed Verification index = 0 # Target key slot in HAB key store where key will be installed Target index = 2 # Key to install File = "../crts/IMG1_1_sha256_4096_65537_v3_usr_crt.pem" [Authenticate Data] # Key slot index used to authenticate the image data Verification index = 2 # Address Offset Length Data File Path Blocks =
The meanings of the Blocks values are:
Address - the RAM address to start the authentication from (where the image will be loaded) Offset - the signed area offset inside the image Length - the signed area size Data File Path - the path to the image to be signed
If a log file is present for an image, the script takes the HAB Blocks values from it.
For a kernel image (no log file) the HAB Blocks values are filled like this: Address is set to the default kernel loadaddr (0x82000000), Offset is set to 0x0, and Length is set to the size of the new 4KB aligned+IVT attached image.
For an IMX Boot Image, the following is also added to the csf, in order to prevent HAB from initializing the RNG and defer it for the Linux CAAM driver (this is still needed in CST 2.3.2 even though it's supposed to be added automatically by CST since 2.3):
[Unlock] Engine = CAAM Features = RNG
Next, the script generates the csf binary, and finally, appends it to the image.
3.8.2 Sign the images
Copy the images to the linux64/bin directory of CST.
Use the U-Boot images created in the Build U-Boot with secure boot support section above.
The kernel image can be obtained from either the recovery SD card, a Yocto build or built directly from source code (replace <kernel_dir> below, accordingly).
$ cd ~/cst-2.3.3/linux64/bin $ cp ~/uboot-imx/SPL ~/uboot-imx/SPL.log ~/uboot-imx/u-boot-ivt.img ~/uboot-imx/u-boot-ivt.img.log . $ cp <kernel_dir>/zImage .
Sign the images:
For an i.MX6UL SOC:
$ SOC=mx6ul ./var-som_sign_image.sh SPL u-boot-ivt.img zImage
For an i.MX6ULL SOC:
$ SOC=mx6ull ./var-som_sign_image.sh SPL u-boot-ivt.img zImage
Make sure you get the following files:
SPL_signed u-boot-ivt.img_signed zImage-ivt_signed
3.9 Install the signed images on an SD card
Replace the regular images with the signed ones.
For example, to install on an SD card:
$ sudo dd if=SPL_signed of=/dev/sdX bs=1K seek=1; sync $ sudo dd if=u-boot-ivt.img_signed of=/dev/sdX bs=1K seek=69; sync $ sudo cp zImage-ivt_signed /media/BOOT-VAR6UL/zImage (Replace /dev/sdX with the correct device and /media/BOOT-VAR6UL with the correct mount directory of the boot partition)
If you want to use our recovery SD card to flash the signed images to NAND flash/eMMC, then copy them to the appropriate location in the SD card, while overwriting the regular images:
$ sudo cp SPL_signed /media/rootfs/opt/images/... $ sudo cp u-boot-ivt.img_signed /media/rootfs/opt/images/... $ sudo cp zImage-ivt_signed /media/rootfs/opt/images/...
And eject the SD card gracefully from host machine.
3.10 Verify HAB successfully authenticates the signed image
HAB generates events when processing the commands if it encounters issues.
The U-Boot "hab_status" command displays any events that were generated.
Run it at the U-Boot command line:
If everything is okay you should get the following output:
Secure boot disabled HAB Configuration: 0xf0, HAB State: 0x66 No HAB Events Found!
Make sure there are not events before proceeding to the next step.
3.11 Secure the device
After the device successfully boots a signed image without generating any HAB events, it is safe to
secure, or "close", the device.
This is the last step in the process, and is completed by blowing the SEC_CONFIG fuse bit.
Once the fuse is blown, the chip does not load an image that has not been signed using the correct PKI tree.
- This is again a One-Time Programmable e-fuse. Once you write it you can't go back, so get it right the first time.
- If anything in the previous steps wasn't done correctly, the SOM will not boot after writing this bit.
fuse prog 0 6 0x2
4 Field Return
After securing the device, the FIELD_RETURN e-fuse may be used to disable secure boot. Once the FIELD_RETURN e-fuse is blown, the SoC will permanently be able to boot unsigned images.
This can be helpful for:
- Debugging returned units
- Complying with open source licenses and allow end users to disable secure boot
- Booting unsigned images
Enabling FIELD_RETURN requires the following process:
- Read the SoC UID (factory programmed by NXP)
- Modify the [Unlock] section of SPL.csf
- Sign a new image using the updated SPL.csf
- Blow the FIELD_RETURN bit
Read the SoC UID
=> fuse read 0 2 Reading bank 0: Word 0x00000002: 01234567 => fuse read 0 1 Reading bank 0: Word 0x00000001: 89abcdef
Modify the [Unlock] section of SPL.csf:
[Unlock] Engine = OCOTP Features = FIELD RETURN UID = 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01
Comment out the following lines in the var-sign_image.sh script: https://github.com/varigit/var-hab-cst-scripts/blob/master/var-sign_image.sh#L31-L41
Re-sign the SPL image:
For an i.MX6UL SOC:
$ SOC=mx6ul var-som_sign_image.sh SPL
For an i.MX6ULL SOC:
$ SOC=mx6ull var-som_sign_image.sh SPL
Install the signed images on an SD card (See Install the signed images on an SD card)
Blow the field return fuse:
=> fuse prog 5 6 1 Programming bank 5 word 0x00000006 to 0x00000001... 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
After performing this process, rebuild SPL and U-Boot with secure boot disabled to boot unsigned images.