High Assurance Boot

From Variscite Wiki
Revision as of 20:31, 20 April 2022 by Nate (talk | contribs)
i.MX High Assurance Boot (HAB) / Secure Boot

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.

HAB operation.png

References


Code signing step by step instructions

Toolchain installation for out of Yocto builds

To install the toolchain, follow Yocto Toolchain installation.

Build U-Boot with secure boot support

Obtain sources:

$ 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

Build U-Boot:

$ source /opt/fslc-x11/2.2.1/environment-setup-armv7at2hf-neon-fslc-linux-gnueabi
$ make mrproper
Choose one of the following configurations:

For booting from SD card/eMMC:
$ make mx6var_som_sd_SECURE_BOOT_defconfig

For booting from NAND flash:
$ make mx6var_som_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


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):
https://www.nxp.com/webapp/sps/download/license.jsp?colCode=IMX_CST_TOOL

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

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

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.
For example:

$ 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.
For example:

$ echo Variscite_password >  key_pass.txt
$ echo Variscite_password >> key_pass.txt

Now, to generate the PKI tree, run the following:

$ ./hab4_pki_tree.sh

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

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

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 mx6

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.
Important notes:
- 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

Sign the U-Boot and kernel images

The signing process

  • General usage of the Variscite var-som_sign_image.sh script:
Usage: SOC=mx6 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 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 (0x12000000), 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.

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>/uImage .

Sign the images:

$ SOC=mx6 ./var-som_sign_image.sh SPL u-boot-ivt.img uImage

Make sure you get the following files:

SPL_signed
u-boot-ivt.img_signed
uImage-ivt_signed

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 uImage-ivt_signed /media/BOOT-VARMX6/uImage
(Replace /dev/sdX with the correct device
 and /media/BOOT-VARMX6 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 uImage-ivt_signed /media/rootfs/opt/images/...

And eject the SD card gracefully from host machine.

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:

=> hab_status

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.

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[1] fuse bit.
Once the fuse is blown, the chip does not load an image that has not been signed using the correct PKI tree.
Important notes:
- 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

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
  • etc...
Note: The FIELD_RETURN e-fuse can only be blown by booting a signed image after unlocking FIELD_RETURN in SPL.csf.

Enabling FIELD_RETURN requires the following process:

  1. Read the SoC UID (factory programmed by NXP)
  2. Modify the [Unlock] section of SPL.csf
  3. Sign a new image using the updated SPL.csf
  4. Blow the FIELD_RETURN bit

For example:

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

Rebuild SPL.csf:

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

Resign the SPL image:

$ SOC=mx6 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.

Warning: After blowing FIELD_RETURN e-fuse, it will be possible to boot unsigned images. This cannot be undone.