High Assurance Boot MX8

From Variscite Wiki


i.MX8M 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

NXP Provides documentation for enabling HAB for the i.MX8M Family. The information in this wiki is derived from NXP's documentation.

The U-Boot source code provides a directory with documentation and examples: https://github.com/varigit/uboot-imx/blob/imx_v2020.04_5.4.70_2.3.2_var01/doc/imx/habv4/

The following documentation is helpful to review:


Please continue reading below to learn how Variscite's Yocto layer simplifies this process.

Code signing using Yocto

Introduction to meta-variscite-hab

Variscite provides a Yocto layer to simplify signing the imx-boot and Linux images and generate a fully signed recovery SD card image. meta-variscite-hab requires two inputs:

Meta-variscite-hab-io-v2.png

NXP Code Signing Tool

NXP proprietary tool for signing images. Users must download this tool from NXP and provide an absolute path to meta-variscite-hab.

Private Keys

Users must generate private keys and provide an absolute path or git repository to meta-variscite-hab.

meta-variscite-hab extends the imx-boot, uboot-variscite, and linux-variscite recipes to sign the imx-boot and Linux images using the NXP Code Signing Tool and private keys.

The general process for creating a signed image using meta-variscite-hab is:

  1. Follow the Build Yocto from source code guide to setup a build environment
  2. Download NXP Code Signing Tool (CST)
  3. Use CST to generate Public Key Infrastructure (PKI) tree (one time)
  4. Configure local.conf, machine.conf, or variscite.inc
    1. Enable hab machine override
    2. Provide absolute path to NXP Code Signing Tool
    3. Provide git repository for private keys
    4. Provide certificate serial and passkeys
    5. Provide U-Boot and kernel device tree names
  5. Build a signed SD card image
  6. Program a recovery SD card
  7. Program SOC e-fuses
    1. Boot recovery SD card and interrupt U-Boot
    2. U-Boot: Program the SRK (public keys) to the SOC e-fuses
    3. U-Boot: Verify public keys and signed image by running hab_status
    4. U-Boot: Secure (Close) the device

The remainder of the guide walks through this process.

Walkthough: Setup a build environment

This section assumes familiarity with building Yocto. For more information, please follow the Build Yocto from source code guide.

Download the latest revision:

$ mkdir ~/var-fslc-yocto && cd ~/var-fslc-yocto
$ repo init -u https://github.com/varigit/variscite-bsp-platform.git -b dunfell -m default.xml
$ repo sync -j4

Setup environment to build XWayland GUI demo image

$ cd ~/var-fslc-yocto
$ MACHINE=imx8mm-var-dart DISTRO=fslc-xwayland . setup-environment build_xwayland


Walkthough: Download NXP 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-3.1.0.tar.gz

You are encouraged to read the documents under ~/cst-3.1.0/docs

Walkthough: Generate Public Key Infrastructure (PKI) tree

$ cd ~/cst-3.1.0/keys

Create a text file called "serial", which contains 8 digits. It will be used for the certificate serial numbers.
For example:

$ echo 1248163E > 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): 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

Generate Super Root Key (SRK) table

$ cd ../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


Next, add the contents of the './crts' and './keys' to a git repository matching 'CST_CERTS_URI' (defined below) in local.conf. Make sure to remove './keys/serial' and './keys/key_pass.txt'. See Variscite's repository for an example: https://github.com/varigit/var-hab-certs

Note: Instead of using a git repository, you may configure CST_CERTS_URI to fetch an archive or any other Yocto compatible URL.

Walkthough: Configure local.conf, machine.conf, or variscite.inc

There are several variables that need to be configured in conf/local.conf, ../sources/meta-variscite-fslc/conf/machine/imx8mm-var-dart.conf, or ../sources/meta-variscite-fslc/conf/machine/variscite.inc (use whichever file you prefer):

Variable Description
OVERRIDES =. "hab:" Machine override used to enable Variscite HAB functions in meta-variscite-hab
CST_CERTS_URI SRC_URI definition for cloning certificates repository. Below is the default configuration for Variscite's example var-hab-certs repository:
CST_CERTS_URI ?= "git://github.com/varigit/var-hab-certs.git;protocol=https;branch=master;rev=${CST_CERTS_REV}"
CST_CERTS_REV Git commit ID for CST_CERTS_URI. Below is the default definition (commit ID may change):
CST_CERTS_REV ?= "56ad83a9962fb1cd8b4a18dc72993de7e7894bc5"
CST_SERIAL Certificate serial number from previous step
CST_KEYPASS Key password from previous step
NXP_CST_URI Path to downloaded NXP CST archive.
UBOOT_DTBS

imx-boot images for the i.MX8M family contain a fit image with one ore more U-Boot device tree files. Currently, signed imx-boot images can only contain a single device tree file. Therefore, an imx-boot image will be created, signed, and deployed for each UBOOT_DTBS entry.

Signed imx-boot images use the following naming convention:

  • More then one device tree: imx8mm-var-dart-<IMXBOOT_TARGET>-<Device Tree>
  • Single device tree: imx8mm-var-dart-<IMXBOOT_TARGET>

Only modify the default value of UBOOT_DTBS if you have added a new device tree to the U-Boot source code.

UBOOT_DTB_DEFAULT

When UBOOT_DTBS contains more then one device tree (and therefore multiple imx-boot images are deployed), UBOOT_DTB_DEFAULT is used to select the default imx-boot for the SD card image.

The default configuration is:

UBOOT_DTB_DEFAULT ?= ""
UBOOT_DTB_DEFAULT_mx8mm ?= "-imx8mm-var-som-symphony"
UBOOT_DTB_DEFAULT_mx8mp ?= "-imx8mp-var-som-symphony"
SIGN_DTB Required for i.MX8QM and i.MX8QXP Family Only, optional for i.MX8M family

Like UBOOT_DTBS, currently only a single Linux device tree can be signed. The default configuration is:

# mx8m family: Unsigned
SIGN_DTB ?= ""
# mx8qm/mx8qxp family:
SIGN_DTB_mx8qm ?= "${B}/${KERNEL_OUTPUT_DIR}/dts/freescale/imx8qm-var-som-lvds.dtb"
SIGN_DTB_mx8x ?= "${B}/${KERNEL_OUTPUT_DIR}/dts/freescale/imx8qxp-var-som-symphony-sd.dtb"

To get started using Variscite's default configuration and keys (not recommended for production), add the following to local.conf:

local.conf

...
# Enable Variscite HAB machine override
OVERRIDES =. "hab:"
...

You may override any of Variscite's default variables, see below for an example local.conf for the imx8mm-var-dart:

local.conf

...
# Enable Variscite HAB machine override
OVERRIDES =. "hab:"

# Provide path to NXP Code Signing archive
NXP_CST_URI="file://${HOME}/cst-3.1.0.tgz"

# Configure certificate serial and passwords (These should be secret)
CST_SERIAL="1248163E"
CST_KEYPASS="Variscite_password"

# sign imx-boot images for both imx8mm-var-dart and imx8mm-var-som symphony (default configuration)
UBOOT_DTBS_mx8mm="imx8mm-var-dart-customboard.dtb imx8mm-var-som-symphony.dtb"

# Use signed imx8mm-var-dart-customboard.dtb in the SD card image
UBOOT_DTB_DEFAULT_mx8mm="-imx8mm-var-dart-customboard"

# Sign kernel device tree file:
SIGN_DTB_mx8mm="${B}/${KERNEL_OUTPUT_DIR}/dts/freescale/imx8mm-var-dart-dt8mcustomboard.dtb"

# GIT Repository for private certificates
# Note: CST_CERTS_REV is mandatory and used by var-hab.bbclass as well as CST_CERTS_URI 
CST_CERTS_REV="56ad83a9962fb1cd8b4a18dc72993de7e7894bc5"
CST_CERTS_URI="git://github.com/varigit/var-hab-certs.git;protocol=https;branch=master;rev=${CST_CERTS_REV}"
...

Walkthough: Build a signed SD card image

To be safe, clean all relevant recipes:

$ bitbake -c cleansstate linux-variscite
$ bitbake -c cleansstate u-boot-variscite
$ bitbake -c cleansstate imx-boot
$ bitbake -c cleansstate fsl-image-gui
# Or...combine into a single command
$ bitbake -c cleansstate linux-variscite && bitbake -c cleansstate u-boot-variscite && bitbake -c cleansstate imx-boot && bitbake -c cleansstate fsl-image-gui

Build a signed image:

$ bitbake fsl-image-gui

Create a recovery SD card:

$ cd ~/var-fslc-yocto
$ sudo MACHINE=imx8mm-var-dart sources/meta-variscite-fslc/scripts/var_mk_yocto_sdcard/var-create-yocto-sdcard.sh <options> /dev/sdX
(Replace /dev/sdX with your actual device)

Walkthough: Program the SRK (public keys) to the SOC e-fuses

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.
- Do not run the command at the end of the script to close the device. We will do that later.

SRK_1_2_3_4_fuse.bin.u-boot-cmds:

$ cat build_xwayland/tmp/deploy/images/imx8mm-var-dart/SRK_1_2_3_4_fuse.bin.u-boot-cmds
# Note: 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 -y 6 0 0xDA6B9ADB
fuse prog -y 6 1 0xDC9B55A1
fuse prog -y 6 2 0x93D10134
fuse prog -y 6 3 0x5CDC8DA3
fuse prog -y 7 0 0x143709F8
fuse prog -y 7 1 0xC6E305A7
fuse prog -y 7 2 0x3E718DA2
fuse prog -y 7 3 0xC6AC85B6

# 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. 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 1 3 0x02000000

Walkthrough: 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!


Warning: Make sure there are not events before proceeding to the next step.

Walkthrough: 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 1 3 0x2000000