The goal is also to provide an update on our older blog post on the subject as it required an old U-Boot version. Since then, HAB support has been added to mainline U-Boot and encryption is now possible on top of binary signature.
For simplicity and clarity, this guide and examples have been made on i.MX6Q Nitrogen6x so it applies to most our i.MX6 platforms.
However, for i.MX6SX Nit6_SoloX and i.MX7D Nitrogen7 some changes must be made because the fuse map and the RAM start address are different. Please contact us for instructions on those two platforms.
Vulnerability Errata: It has been disclosed by NXP that as of today (July 2017), all our i.MX-based platforms are vulnerable:
If you miss a step, this can brick your platform for good
You've been WARNED!
HAB architecture
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.
On processors supporting the feature, encrypted boot may also be used to provide image cloning protection and, depending on the use case, image confidentiality. The HAB library can not only be used to authenticate the first stage of the boot chain, but the other components of the boot chain as well such as the Linux kernel.
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 bootloader image to DDR memory. The image contains both the bootloader itself and digital signature data and public key certificate data which are collectively called CSF data. This latter is generated off-line using the HAB CST which is introduced in the next section.
Once the bootloader is loaded, execution is then passed to the HAB library which will verify the signatures of the bootloader stage. If signature verification fails, execution is not allowed to leave the ROM for securely configured SoCs, also called "closed" devices.
So as long as a device is not "closed", the ROM will execute the code loaded in RAM. 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. This last statement is very important since this is what will allow us to make sure the security is working before "closing" the device.
In order to understand the signed image generation, it is best to have a look at the final U-Boot image layout.
In case you want to use the encryption capability, then the image would look like the following.
Note that all the addresses in the previous diagrams are dependent on U-Boot size and therefore do not necessarily match your setup, it is more for an example.
How to enable/use it?
The below procedure will describe an example on how it has been done on one Nitrogen6x, 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-2.3.2.tar.gz ~$ cd cst-2.3.2/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.
~/cst-2.3.2/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 phase repeated on the first and second lines of the file.
~/cst-2.3.2/keys$ vi key_pass.txt
Boundary123!
Boundary123!
You can now create the signature keys.
~/cst-2.3.2/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): 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
...
Create the fuse table and binary to be flashed later.
The fuse table generated in the previous section is what needs to be flashed to the device. However, the hexdump command above doesn't show the values in their correct endianness, instead the command below will be more useful.
The above command gives you what needs to be flashed in the proper order.
The commands below are made for i.MX6QDL, not i.MX7D or i.MX6SX. Make sure to use your own values here, otherwise your board will never be able to authenticate the boot image.
Note that the last line "HAB Blocks" is very important when creating the .csf file later on.
4.a- Sign a U-Boot image
If you just want to sign your binary, follow this section and then go straight to section 5. Otherwise, if you want to encrypt and sign your binary, jump to next section directly.
Go to the CST tools again:
~$ cd ~/cst-2.3.2/linux64/
~/cst-2.3.2/linux64$ cp ~/u-boot-imx6/u-boot.imx .
At this point you need to create a u-boot.csf file using the "HAB Blocks" information from previous section and the template provided in the HABCST_UG.pdf documentation which is provided in the CST package.
For more convenience, we created a .csf file that applies to Nitrogen6x using latest U-Boot.
~/cst-2.3.2/linux64$ wget -O u-boot.csf
https://storage.googleapis.com/boundarydevices.com/u-boot_sign.csf
... edit the size in the "Blocks = " line...
~/cst-2.3.2/linux64$ ./cst --o u-boot_csf.bin --i u-boot.csf
CSF Processed successfully and signed data available in u-boot_csf.bin
You can now generate the final binary by concatenating the u-boot.imx image with the CSF binary:
You can copy this binary to the root of an SD card along with 6x_upgrade. Don't forget that the name of the binary must match the platform name (see U-Boot blog post for the explanation).
Then you can download the .csf we've prepared for encryption and build both the DEK blob and the CSF binary:
~$ cd ~/cst-2.3.2/linux64/
~/cst-2.3.2/linux64$ cp ~/u-boot-imx6/u-boot.imx .
~/cst-2.3.2/linux64$ wget -O u-boot.csf
https://storage.googleapis.com/boundarydevices.com/u-boot_encrypt.csf
... edit the size in the "Blocks = " line...
~/cst-2.3.2/linux64$ ./cst_encrypt --o u-boot_csf.bin --i u-boot.csf
CSF Processed successfully and signed data available in u-boot_csf.bin
At this point, make sure to modify the csf file to match your binary:
The [Authenticate Data] section must cover the IVT + DCD table size and NOT the padding
As an example: Blocks = 0x177ff400 0x0 0x344 "u-boot.imx"
The [Decrypt Data] section must cover the U-Boot data (after IVT + DCD + padding)
As an example: Blocks = 0x17800000 0x00000C00 0x00078000 "u-boot.imx"
The above will produce the following binaries:
u-boot.imx: encrypted version of u-boot
u-boot_csf.bin: CSF binary
dek.bin: DEK key that needs to be transformed into a blob
Each CPU generates a different blob!!
In order to generate the dek blob, the dek.bin must be provided to a U-Boot dek tool which will compute the blob using the secrete and unique key from the specific CPU it is running on. The easiest approach is to copy the dek.bin to the root of an sdcard and issue the following:
Write this blob to SDCard, here is the procedure to write to a EXT4 partition:
=> ext4write mmc 0 0x10801000 /dek_blob.bin 0x48
Here is the equivalent for a FAT partition:
=> fatwrite mmc 0 0x10801000 dek_blob.bin 0x48
Once you've retrieved the dek_blob.bin, you can generate the final signed & encrypted image. The cst_encrypt binary tool generated earlier will sign the binary and encrypt its content in place.
Note that the CSF region in U-Boot header is set to 0x2000 whereas we pad it to 0x1F00. The reason is that this region also includes the dek_blob.bin, otherwise the boot ROM won't load the dek blob to memory.
As a result we have encrypted boot image which can be loaded and executed by only current board since dek_blob.bin is unique per board/CPU.
Finally, you can copy this binary to the root of an SD card along with 6x_upgrade. Don't forget that the name of the binary must match the platform name (see U-Boot blog post for the explanation).
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/encryption 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.MX6QDL, for other devices such as the i.MX6SoloX or i.MX7D the fuse map most likely differ and requires another command.
=> fuse prog 0 6 0x2
Going further
What about imx_usb_loader?
As explained in the HAB application notes, it is possible to use the Serial Download Protocol (SDP) on close devices using the MFGTools.
Thanks to Jérémie Corbier from Prove&Run, support has been added to imx_usb_loader to load a signed binary. Make sure to use the HEAD of the master branch to have the best support from the imx_usb_loader tool.
As the documentation says, using SDP requires:
Modify the .csf file in order to check the DCD tabled loaded in OCRAM
Otherwise considered as a HAB error
Sign the u-boot.imx binary with the DCD address set to 0 in the IVT header
Since the SDP protocol clears the DCD table address
For the first modification to be done, simply add the following to your current
Note that you cant' have several sets of Blocks under one Authenticate Data tag. Instead you need to declare as many Authenticate Data sections as Blocks you want to be verified.
You might be wondering how to get the DCD length from the binary? It is actually given in the DCD Header (see TRM section 8.7.2). Here is a command to extract it from the u-boot.imx binary (courtesy of Eric Nelson):
$ dd if=u-boot.imx bs=1 skip=45 count=2 | od -t x2 -A none --endian=big
Then you need to get a custom script that allows to sign the binary without the DCD table address in the IVT header since it is cleared during the SDP boot process.
Now that your bootloader image is properly authenticated and that your device is secured, you can sign your kernel image so U-Boot ensures to load a known version. Here is a zImage layout example:
The first thing to do is to generate the IVT file. We are providing a script that allows you to do so:
The hexdump command above allows to learn the zImage size. Then you can modify the genIVT to reflect the proper sizes, in our example, the size of the zImage was 0x468628 so the next 4kB boundary was 0x469000 as you can see in the genIVT.
High Assurance Boot (HAB) for dummies
This post intends to provide all the information you need to understand and use the HAB on your Boundary Devices' platform.
For i.MX8 platforms, please see our newer post: HAB - i.MX8M Edition
The goal is also to provide an update on our older blog post on the subject as it required an old U-Boot version. Since then, HAB support has been added to mainline U-Boot and encryption is now possible on top of binary signature.
For simplicity and clarity, this guide and examples have been made on i.MX6Q Nitrogen6x so it applies to most our i.MX6 platforms.
However, for i.MX6SX Nit6_SoloX and i.MX7D Nitrogen7 some changes must be made because the fuse map and the RAM start address are different. Please contact us for instructions on those two platforms.
Vulnerability Errata: It has been disclosed by NXP that as of today (July 2017), all our i.MX-based platforms are vulnerable:
The Boot ROM on certain affected devices has been subsequently updated to prevent these vulnerabilities.
Please contact your NXP Support/ Sales representative for further information on mitigations or ordering revised silicon.
Prerequisites
HAB architecture
Before getting started, let's explain a few acronyms related to this subject
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.
On processors supporting the feature, encrypted boot may also be used to provide image cloning protection and, depending on the use case, image confidentiality. The HAB library can not only be used to authenticate the first stage of the boot chain, but the other components of the boot chain as well such as the Linux kernel.
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 bootloader image to DDR memory. The image contains both the bootloader itself and digital signature data and public key certificate data which are collectively called CSF data. This latter is generated off-line using the HAB CST which is introduced in the next section.
Once the bootloader is loaded, execution is then passed to the HAB library which will verify the signatures of the bootloader stage. If signature verification fails, execution is not allowed to leave the ROM for securely configured SoCs, also called "closed" devices.
So as long as a device is not "closed", the ROM will execute the code loaded in RAM. 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. This last statement is very important since this is what will allow us to make sure the security is working before "closing" the device.
In order to understand the signed image generation, it is best to have a look at the final U-Boot image layout.
In case you want to use the encryption capability, then the image would look like the following.
Note that all the addresses in the previous diagrams are dependent on U-Boot size and therefore do not necessarily match your setup, it is more for an example.
How to enable/use it?
The below procedure will describe an example on how it has been done on one Nitrogen6x, 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:
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.
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 phase repeated on the first and second lines of the file.
You can now create the signature keys.
Create the fuse table and binary to be flashed later.
2- Flashing the keys
The fuse table generated in the previous section is what needs to be flashed to the device. However, the hexdump command above doesn't show the values in their correct endianness, instead the command below will be more useful.
The above command gives you what needs to be flashed in the proper order.
The commands below are made for i.MX6QDL, not i.MX7D or i.MX6SX. Make sure to use your own values here, otherwise your board will never be able to authenticate the boot image.
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: https://storage.googleapis.com/boundarydevices.com/crts_bd_blog.tar.gz
As a customer pointed out, for i.MX6 CPU with the TO1.0 revision (pre-production), you also need to flash some random MasterKey (OTPMK):
3- Build U-Boot with security features
Build U-Boot with the CONFIG_SECURE_BOOT configuration enabled.
Here is an example for Nitrogen6q/BD-SL-iMX6:
Note that the last line "HAB Blocks" is very important when creating the
.csf
file later on.4.a- Sign a U-Boot image
If you just want to sign your binary, follow this section and then go straight to section 5. Otherwise, if you want to encrypt and sign your binary, jump to next section directly.
Go to the CST tools again:
At this point you need to create a u-boot.csf file using the "HAB Blocks" information from previous section and the template provided in the HABCST_UG.pdf documentation which is provided in the CST package.
For more convenience, we created a
.csf
file that applies to Nitrogen6x using latest U-Boot.You can now generate the final binary by concatenating the
u-boot.imx
image with the CSF binary:You can copy this binary to the root of an SD card along with
6x_upgrade
. Don't forget that the name of the binary must match the platform name (see U-Boot blog post for the explanation).You can now flash this signed version of U-Boot, see section 5.
4.b- Encrypt and sign an image
First, the CST binary provided in the NXP package doesn't allow to use encryption, so you need to build a new binary yourself:
Then you can download the
.csf
we've prepared for encryption and build both the DEK blob and the CSF binary:At this point, make sure to modify the csf file to match your binary:
[Authenticate Data]
section must cover the IVT + DCD table size and NOT the paddingBlocks = 0x177ff400 0x0 0x344 "u-boot.imx"
[Decrypt Data]
section must cover the U-Boot data (after IVT + DCD + padding)Blocks = 0x17800000 0x00000C00 0x00078000 "u-boot.imx"
The above will produce the following binaries:
u-boot.imx
: encrypted version of u-bootu-boot_csf.bin
: CSF binarydek.bin
: DEK key that needs to be transformed into a blobIn order to generate the dek blob, the
dek.bin
must be provided to a U-Boot dek tool which will compute the blob using the secrete and unique key from the specific CPU it is running on. The easiest approach is to copy thedek.bin
to the root of an sdcard and issue the following:Write this blob to SDCard, here is the procedure to write to a EXT4 partition:
Here is the equivalent for a FAT partition:
Once you've retrieved the
dek_blob.bin
, you can generate the final signed & encrypted image. Thecst_encrypt
binary tool generated earlier will sign the binary and encrypt its content in place.Note that the CSF region in U-Boot header is set to 0x2000 whereas we pad it to 0x1F00. The reason is that this region also includes the
dek_blob.bin
, otherwise the boot ROM won't load the dek blob to memory.As a result we have encrypted boot image which can be loaded and executed by only current board since
dek_blob.bin
is unique per board/CPU.Finally, you can copy this binary to the root of an SD card along with
6x_upgrade
. Don't forget that the name of the binary must match the platform name (see U-Boot blog post for the explanation).5- Flash and test the device
Use the our standard procedure to update U-Boot:
The device should reboot, then check the HAB status:
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/encryption 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.MX6QDL, for other devices such as the i.MX6SoloX or i.MX7D the fuse map most likely differ and requires another command.
Going further
What about imx_usb_loader?
As explained in the HAB application notes, it is possible to use the Serial Download Protocol (SDP) on close devices using the MFGTools.
Thanks to Jérémie Corbier from Prove&Run, support has been added to imx_usb_loader to load a signed binary. Make sure to use the HEAD of the master branch to have the best support from the imx_usb_loader tool.
As the documentation says, using SDP requires:
For the first modification to be done, simply add the following to your current
Note that you cant' have several sets of
Blocks
under oneAuthenticate Data
tag. Instead you need to declare as manyAuthenticate Data
sections asBlocks
you want to be verified.You might be wondering how to get the DCD length from the binary? It is actually given in the DCD Header (see TRM section 8.7.2). Here is a command to extract it from the
u-boot.imx
binary (courtesy of Eric Nelson):Then you need to get a custom script that allows to sign the binary without the DCD table address in the IVT header since it is cleared during the SDP boot process.
What about authenticating the kernel?
Now that your bootloader image is properly authenticated and that your device is secured, you can sign your kernel image so U-Boot ensures to load a known version. Here is a
zImage
layout example:The first thing to do is to generate the IVT file. We are providing a script that allows you to do so:
The
hexdump
command above allows to learn thezImage
size. Then you can modify thegenIVT
to reflect the proper sizes, in our example, the size of thezImage
was 0x468628 so the next 4kB boundary was 0x469000 as you can see in thegenIVT
.Then you can download the
.csf
file for the zImage in order to create the CSF blob and generate the signed image:That's it, you can now modify your U-Boot bootcmd so it includes the HAB command that checks the kernel:
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.
https://github.com/carloscn/imx6-bsp
References