High Assurance boot in i.MX6 (Secure boot)

Introduction

Software updates are updated frequently. Any end user can install custom software in devices. This increases the threat of installing malwares. The consequences are unimaginable. Consideration security aspects, it is necessary that the hardware have some mechanism to ensure that the software it is running can be trusted. NXP's i.MX6 series chips provide High Assurance Boot (HAB) feature which meets such a requirement.
 

Note

There are huge source of documents in the community. But have few broken ends. This document links the broken ends. Hence you will find most of the data/text repeated.

Mechanism

An asymmetric encryption is adopted to implement the HAB feature. An utility called CST – Code Signing Tool is provided by Freescale to generate private key and corresponding public key pairs. For any system image the OEM want to release, the private key is used to do the encryption. This encryption generates a unique identifier for the image which is called a certificate.

The public key is also attached to the image. The hardware decrypts the certificate  using the public key before booting the image. Then, a comparison is performed to check if the certificate and the image match. The image is considered "trusted" only if a match occurs. Otherwise the image is deemed as "unsafe" and will not be permitted to be loaded and run. This process is called authentication.

A hacker can only have access to the public key. Per the property of asymmetric encryption, private key, which the OEM uses, cannot be deduced from that. Without the private key, the hacker cannot attach a valid certificate for his malicious system image. HAB will reject it in a very early stage. The OEM will burn the digest (hash) of the public key to the eFuses of i.MX6 chip. Once burned, it cannot be modified. This prevents the possibility that the hacker use another pair of private and public key to cheat. Figures below provide a more precise illustration.

Key generation




Certificate generation

   

Authentication


Hardware & Software

    • i.MX 6 Dual  based custom Hardware
    • U-Boot – Freescale imx_v2013.04_3.10.17_1.0.0_ga  + custom board patches = BSP 2.34
    • Kernel  - Freescale imx_3.10.17_1.0.0_ga + custom board patches = BSP 2.34
    • Build system – Yocto
    • Code signing tool (CST) – 2.3.1
    • Host machine  - Ubuntu 14.04.4 LTS, 64 bit

Scope

This document illustrates the construction of secure boot image and configuring the target to run securely. This documents describes about
    • How the hardware and software is configured.
    • What components are required and how they are built
    • Creating the image as per the required layout.

U-Boot layout

Performing secure boot in i.MX requires the u-boot image to be created in a specific layout as shown in figure 1.0. The two important data structures are

Image vector table (IVT)

Image vector table contains pointers to different offsets of the image like DCD, address of the first instruction to execute, address of the CSF (file containing the certificate) etc.

Command sequence file (CSF)

This file contains the configurations and commands which the HAB library from boot ROM executes during secure boot.

    • The addresses mentioned in the above picture are specific to custom i.MX6 board
    • IVT is a part of header used for i.MX U-Boot image. The type of header is described in the last section. (Refer tools/imximage.h in U-Boot source)
    • Boot ROM picks up the IVT from offset 0x400 when booting from NAND. Hence first 1KB (0x400) of the image is left blank.
    • The Image vector table starts at offset `load address + 0x400`. The IVT has pointers to other regions of the image and the CSF data. The boot ROM relies on IVT to understand various regions of the image.
    • The u-boot image is rounded off to 4KB by filling 0xFF(dummy). The number of bytes filled depends upon the size of u-boot image. This is discussed in the below sections.
    • CSF offset varies with respect to the U-Boot size
    • `mkimage` tool writes the CSF pointer appropriately in the IVT after rounding off the U-Boot file size to 4K.
    • The CSF data is also rounded off 4K by filling 0xFF(dummy).

Configuring secure u-boot

The i.MX U-Boot supports secure boot configurations and provide access to the HAV APIs exposed by the ROM vector table. The support is enabled by the following configurations.
    • Define the macro CONFIG_SECURE_BOOT in the board init file
#define CONFIG_SECURE_BOOT
    • Add a string SECURE_BOOT in the DCD table.
# Enable secure boot
SECURE_BOOT
    • Build U-Boot
$ bitbake -v virtual/bootloader
These two configurations creates the U-Boot binary in the required layout and place the corresponding pointers appropriately. The above two options will build U-Boot enabling DCD mode and switches ON secure boot as shown in the below build log.


U-Boot build stages

The build process of U-Boot in yocto is shown in the below image where the binary `u-boot_prod.bin` is generated as the final output. More precisely U-Boot recipe builds it in the name of `u-boot_prod-eagle-imx6-2013.04-ram-1G-r1.bin`. Then it is renamed as `u-boot_prod.bin` to keep it simple.







The final image `u-boot_prod.bin` will be signed as explained in the below sections.

Signing the image

HAB authentication is based on public key cryptography using the RSA algorithm in which image data is signed offline using as series of private keys. The resulting signed image is then verified on the i.MX processor using the corresponding public keys. This key structure is known as a PKI tree. Super Root Keys, or SRK, are components of the PKI tree. HAB relies on a table of the public SRKs to be hashed and placed in fuses on the target.
The Freescale Code Signing Tool, CST, is used to generate the HABv4 certificates for images using the PKI tree data and SRK table. On the target, 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. Detailed information for CST and HAB can be found in their respective user guides included in the CST package.

PKI tree

CST includes scripts for generating a PKI tree and SRK table. The PKI tree for this example is detailed
 in this figure.


The example in this document uses the first SRK as the root of trust. The CSF 1 and IMG 1 keys are used to sign the CSF
 data and the image respectively. CST contains a script in the “keys” directory that generate the above
 PKI tree.

CST Installation

    • Download the tool from freescale repository. Here is the link
http://www.freescale.com/webapp/sps/download/mod_download.jsp?
colCode=IMX_CST_TOOL&appType=moderatedWithoutFAE&fpsp=1&WT_TYPE=Initialization/Boot/Device
%20Driver%20Code
%20Generation&WT_VENDOR=FREESCALE&WT_FILE_FORMAT=zip&WT_ASSET=Downloads&sr=15&Paren
t_nodeId=1255017883505753547804&Parent_pageType=product

    • The version used for testing is 2.3.1
    • The file  `cst-2.3.1.tar.gz` should be downloaded. Extract the package

$ tar xvf cst-2.3.1.tar.gz
$ chmod +x linux64/* keys/*
Note: For 32 bit systems use the folder linux32. Here 64 bit system is assumed for exmaple
$ dos2unix keys/*.sh
$ cd keys
• Create a text file called serial, which contains 8 digits. Example,
$ echo “12345678” > serial
• Create a text file called key_pass.txt, which contains two lines of identical text, such as "cst_test". This will be taken as the password.
$ echo “password123” > key_pass.txt
$ echo “password123” >> key_pass.txt

Generate pki tree

    • Run the script “hab4_pki_tree.sh” to generate the pki tree
$ ./hab4_pki_tree.sh
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
This script is a part of the Code signing tools for Freescale's
High Assurance Boot. It generates a basic PKI tree. The PKI
tree consists of one or more Super Root Keys (SRK), with each
SRK having two subordinate keys:
+ a Command Sequence File (CSF) key
+ Image key.
Additional keys can be added to the PKI tree but a separate
script is available for this. This this script assumes openssl
is installed on your system and is included in your search
path. Finally, the private keys generated are password
protectedwith the password provided by the file key_pass.txt.
The format of the file is the password repeated twice:
my_password
my_password
All private keys in the PKI tree are in PKCS #8 format will be
protected by the same password.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Do you want to use an existing CA key (y/n)?: n
Enter key length in bits for PKI tree: 2048
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
• After completing the questions, the PKI tree is created. This example tree creates a new CA, uses 2048 bit keys, lasts for 10 years (HAB does not consider the duration), and has 4 SRKs. 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
• For question prompt, enter "n", "2048", "10", "4" one by one.
• This script will generate private key and public key pairs in the working directory.
$ cd ../crts
$ ../linux64/srktool -h 4 -t SRK_1_2_3_4_table.bin -e SRK_1_2_3_4_fuse.bin -d sha256 -c ./SRK1_sha256_2048_65537_v3_ca_crt.pem,./SRK2_sha256_2048_65537_v3_ca_crt.pem,./SRK3_sha256_2048_65537_v3_ca_crt.pem,./SRK4_sha256_2048_65537_v3_ca_crt.pem -f 1

Note: Don't leave space between the pem file names.
    • This command will generate root public key file "SRK_1_2_3_4_table.bin" and its corresponding hash "SRK_1_2_3_4_fuse.bin". The content of the latter will be later on burned to chip eFuse. Burning fuses will be discussed down the line.

Creating CSF description file

The CSF contains all the commands that the ROM executes during the secure boot. These commands instruct HAB on which memory areas of the image to authenticate, which keys to install and use, what data to write to a particular register, and so on. In addition, the necessary certificates and signatures involved in the verification of the image, as well as the SRK table, are attached to the CSF binary signature.

    • Create a folder for u-boot
$ cd <CST tool home folder>
$ mkdir u-boot
$ cd u-boot

    • Create the csf file `u-boot.csf` as shown below
#Illustrative Command Sequence File Description
[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_2048_65537_v3_usr_crt.pem"

[Authenticate CSF]

[Unlock]
Engine = CAAM
Features = RNG

[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_2048_65537_v3_usr_crt.pem"

[Authenticate Data]
# Key slot index used to authenticate the image data
Verification index = 2
# Blocks = <load address> <offset> <length> <Path of u-boot bin>
# @load address : u-boot load address
# @offset       : Offset of image from where CST should start signing
# @length       : Length of the image from offset to sign.
Blocks = 0x177fb000 0x400 0x25000 "<Path to U-Boot bin>/u-boot_prod.bin”
    • In the above CSF file 0x177fb000(Load address + 0x400) is the absolute address of the memory location(RAM) in which IVT would start from.
    • 0x400 is the offset in the binary file from where CST should start signing.
    • 0x25000 is the length of the file to be signed(U-Boot image + data fill). This depends upon the size of U-Boot binary. Refer diagram 4 u-boot layout for better understanding.
The length is calculated as,
length = U-Boot size rounded off to 4K

Sign the image

    • Copy `u-boot_prod.bin` from yocto build directory to current working directory.
    • Generate the below shell script `habimagegen.sh` to sign the image.
#!/bin/sh

UBOOT_BIN="u-boot_prod.bin" # Input file; Image generate from yocto build
LOAD_ADDR="0x177FAC00"
CSF_OFFSET="0x25400" # Varies w.r.t U-Boot size (rounded off to 4K)
END_OFFSET="0x31000" # Varies w.r.t CSF data size (rounded off to 4K)
FILL_BYTE="0xFF"

echo "Extending u-boot to $CSF_OFFSET"
objcopy -I binary -O binary --pad-to $CSF_OFFSET --gap-fill=$FILL_BYTE  \
    $UBOOT_BIN u-boot-pad.bin

echo "Generating CSF data"
../linux64/cst --o u-boot-csf.bin < u-boot.csf

echo "Merging u-boot image and CSF data"
cat u-boot-pad.bin u-boot-csf.bin > u-boot-signed.bin

echo "Extending final image to $END_OFFSET"
objcopy -I binary -O binary --pad-to $END_OFFSET --gap-fill=$FILL_BYTE \
    u-boot-signed.bin u-boot-signed-pad.bin
echo "Final image u-boot-signed-pad.bin is ready"
    • In the above script CSF_OFFSET and END_OFFSET depends upon the size of U-Boot binary. Appropriate values should be replaced in the script. Refer Drawing 4 for better understanding.
    • Execute the shell script
$ ./habimagegen.sh
A file `u-boot-signed-pad.bin` would be created which is the signed image of u-boot to be flashed in the hardware.
The script actual does the following activities.
    1. Image is padded before attaching the CSF data.
    2. Create the CSF data. This contains the encrypted signature and the public key needed to decrypt the signature. The CSF data is stored in `u-boot-csf.bin`.
    3. The CSF file is concatenated with the u-boot image.
    4. The padding is done as described in the above sections.

Burning SRK table

    • Dump the content of the efuse file.
$ cd <CST home folder>/crts
$ hexdump -e '/4 "0x"' -e '/4 "%X""\n"' SRK_1_2_3_4_fuse.bin
0xF6EB72DF
0xE93961C9
0xFB7185A7
0x77A327F1
0x98D67AE4
0x5425912A
0xF6E864B0
0xA604043
    • Freescale provides the driver for accessing the efuses and the related peripheral.  The driver provides sysfs interface for read, write access to the registers
    • Boot the target with any working kernel and rootfs
$ cd /sys/fsl_otp
    • Use the 8 dwords generated in last step and burn them to SRK fuse one by one.
$ echo 0xF6EB72DF > HW_OCOTP_SRK0
$ echo 0xE93961C9 > HW_OCOTP_SRK1
$ echo 0xFB7185A7 > HW_OCOTP_SRK2
$ echo 0x77A327F1 > HW_OCOTP_SRK3
$ echo 0x98D67AE4 > HW_OCOTP_SRK4
$ echo 0x5425912A > HW_OCOTP_SRK5
$ echo 0xF6E864B0 > HW_OCOTP_SRK6
$ echo 0xA604043 > HW_OCOTP_SRK7
    • U-Boot also supports flashing the fuses with the command `fuse`. Enable the command in U-Boot and use it as
> fuse prog 3 0 0xF6EB72DF
> fuse prog 3 1 0xE93961C9
> fuse prog 3 2 0xFB7185A7
> fuse prog 3 3 0x77A327F1
> fuse prog 3 4 0x98D67AE4
> fuse prog 3 5 0x5425912A
> fuse prog 3 6 0xF6E864B0
> fuse prog 3 7 0xA604043
    • Burn OTPMK. These fuse values are necessary to enable the hardware secure logic in the chip.
This step is only required for preproduction parts where the fuses that are normally programmed by Freescale are not programmed. On production parts, the OTPMK are be burned by Freescale prior to shipping the device. The value burned by Freescale is random and not recorded. The intent of the OTPMK is to be a secret key that is only known to CAAM(Cryptographic Acceleration and Assurance Module). So, the value mentioned is only meant for testing purposes. In order to use HAB, these fuses should be programmed. One can determine if a valid OTPMK has been burned by checking the OTPMK_ZERO bit in the SNVS_HP Status Register ."

$ echo 0x975b69a7 > HW_OCOTP_OTPMK0
$ echo 0xafae0b5d > HW_OCOTP_OTPMK1
$ echo 0x6f780499 > HW_OCOTP_OTPMK2
$ echo 0x3dda7a47 > HW_OCOTP_OTPMK3
$ echo 0x76fcba3c > HW_OCOTP_OTPMK4
$ echo 0x6d5c9ef6 > HW_OCOTP_OTPMK5
$ echo 0xb166b40a > HW_OCOTP_OTPMK6
$ echo 0x8f449c5d > HW_OCOTP_OTPMK7
Note: When tried writing these values, the driver(drivers/char/fsl_otp.c) didn't respond as the call got timedout in the function otp_wait_busy() since `BM_OCOTP_CTRL_ERROR` bit is always set.

Flashing U-Boot

Flash the signed u-boot using kobs-ng
$ kobs-ng init u-boot-signed-pad.bin

Testing Secure boot

There are two modes of secure boot
    1. Open configuration
This is the default mode where the processor loads the image even though the authentication process fails. This mode must be used for development purpose. As closed configuration can boot only signed images precisely.
    2. Closed configuration
This configuration boots only signed images. This mode can be enabled when HAB boots a singed image without any HAB events.

Development Goal

Do not put the device to closed configuration until the below status is obtained.  If HAB doesn't generate any events, the signed image is perfect to flash. This must be the goal of testing a signed image. We can go ahead and put the board to closed configuration after this.
U-Boot 2013.04-eagle-imx6-gce0ea25-r1 (Jun 02 2016 - 11:18:11)
I2C:   ready
DRAM:  1 GiB
NAND:  2048 MiB
Using default environment
In:    serial
Out:   serial
Err:   serial
HAB Configuration: 0xf0, HAB State: 0x66
No HAB Events Found!

When an invalid image is flashed HAB generates events as shown below.
HAB Configuration: 0xf0, HAB State: 0x66
--------- HAB Event 1 -----------------
event data:
    0xdb 0x00 0x08 0x41 0x33 0x22 0x0a 0x00
--------- HAB Event 2 -----------------
event data:
    0xdb 0x00 0x14 0x41 0x33 0x0c 0xa0 0x00
    0x00 0x00 0x00 0x00 0x17 0x7f 0xb0 0x00
    0x00 0x00 0x00 0x20
--------- HAB Event 3 -----------------
event data:
    0xdb 0x00 0x14 0x41 0x33 0x0c 0xa0 0x00
    0x00 0x00 0x00 0x00 0x17 0x7f 0xb0 0x2c
    0x00 0x00 0x03 0x18
--------- HAB Event 4 -----------------
event data:
    0xdb 0x00 0x14 0x41 0x33 0x0c 0xa0 0x00
    0x00 0x00 0x00 0x00 0x17 0x7f 0xb0 0x20
    0x00 0x00 0x00 0x01
--------- HAB Event 5 -----------------
event data:
    0xdb 0x00 0x14 0x41 0x33 0x0c 0xa0 0x00
    0x00 0x00 0x00 0x00 0x17 0x80 0x00 0x00
    0x00 0x00 0x00 0x04
This indicates that something is wrong.  Don't close the board as it may brick the chip permanently. BOOM!

Decoding HAB events

Refer Appendix A of the HAB4_API.pdf. It's part of CST tool tar ball. The documentation helps to decode the HAB events.

Secure the device

This is the final step on HAB. 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. Refer to the fuse map for the part before configuring the fuse to ensure its location is correct. Once the fuse is blown, the chip does not load in image that has not been signed using the correct PKI tree.
CAUTION: After this step there is no way back. Make sure that all the above steps are performed precisely
$ echo 0x2 > HW_OCOTP_CFG5
This will permanently turn on the chip security. Boot the board again, secure boot should work.
HAB Configuration: 0xcc, HAB State: 0x99
No HAB Events Found!

RNG TRIM fuses (Random Number Generator)

The example csf file handles this.  HAB can initialize the RNG, or defer the initialization for the CAAM(Cryptographic Acceleration and Assurance Module) Operating System driver to manage. RNG trim fuses provide HAB with a value to program in CAAM. This value setting causes a delay so that sufficient entropy can  be generated on the chip. This ensures that the RNG self-test passes during RNG initialization. If the self-test fails, HAB does not allow the device to continue booting if it is closed. Only HAB sources the RNG Trim Fuse value. The CAAM driver has to perform a similar RNG trim configuration, but the values it uses are built into the driver software.

Method 1

Program the RNG Trim fuse value using the fuse command available in
 i.MX U-Boot.
> fuse prog 1 0 0x00100000
Method 2 (Recommended)
Deferring the RNG instantiation is done by adding a command to the CSF  signature data. This command informs HAB to skip the instantiation. As of all CST versions 2.3 and greater, this command is included in the CSF signature by default, unless it is overridden by the CSF description file. Any operations requiring the RNG are not available to software until it is initialized, such as encryption and blob generation. This does not affect HAB-signed or encrypted boot features. Until the device is closed, HAB does not instantiate the RNG at boot. The CSF configuration file is discussed in later sections of this document. Deferring RNG instantiation is done by adding the following command to the CSF description file:
[Unlock]
Engine = CAAM
Features = RNG

Closer look at the image

Lets have a closer look at the final image generated. For futher details check U-Boot sources “tools/mkimage.*” and “tools/imximage.*”
 
      $ hexdump -C u-boot-signed-pad.bin



In the above image each elements of IVT is marked. Each word is 32 bit big indian. So has to be read from right to left except the first word. Each words are explained below.

Data

Description

D1 00 20 40

IVT header (tag + length + version)

17 80 00 00

Absolute address of first instruction to execute

00 00 00 00

Reserved

17 7f b0 2c

Absolute address of DCD

17 7f b0 20

Absolute address of boot data

17 82 00 00

Absoulute address of CSF data

00 00 00 00

Reserved



Below image shows the padded bytes between the u-boot and CSF data(0x25000)

Below image shows the padding done after the CSF data.

   
   The data structure used are 
       typedef struct {
               uint32_t start;
               uint32_t size;
               uint32_t plugin;
       } boot_data_t;
      
       typedef struct {
               uint8_t tag;
                 uint16_t length;
               uint8_t version;
       } __attribute__((packed)) ivt_header_t;

       typedef struct {
               ivt_header_t header;
               uint32_t entry;
               uint32_t reserved1;
               uint32_t dcd_ptr;
               uint32_t boot_data_ptr;
               uint32_t self;
               uint32_t csf;
               uint32_t reserved2;
       } flash_header_v2_t;
      
       typedef struct {
               flash_header_v2_t fhdr;
               boot_data_t boot_data;
               union {
                       dcd_v2_t dcd_table;
                       char plugin_code[MAX_PLUGIN_CODE_SIZE];
               } data;
       } imx_header_v2_t;
       struct imx_header {
               union {
                       imx_header_v1_t hdr_v1;
                       imx_header_v2_t hdr_v2;
               } header;
               uint32_t flash_offset;
               uint32_t iram_free_start;
               uint32_t plugin_size;
               uint32_t secure_enable;
       } __attribute__((aligned(0x1000)));

Next steps

    • The private key used for encryption has to be stored in a secured place. The following would have to be given a thought.
        ◦ The location to store the key and a method to issue it on demand must be determined.
        ◦ Who will be the owning authority of the private key ?
        ◦ Will the private key storage given a backup?
        ◦ How a developer/technician can use the key to sign an image without having the physical access?
    • The process of incorporating the security frameworks in manufacturing plant has to be determined.

Credits

    • https://boundarydevices.com/secure-boot-on-i-mx6-nitrogen6x-boards

References

    • AN4581.pdf
    • High_Assurance_Boot_L3.0.35_1.1.0.pdf
    • secure_boot_on_imx6.pdf
    • HAB4_API.pdf
    • How-to enable HAB in i.MX6.pdf
    • Security Features of i.MX Applications Processors.pptx
    • HABCST_UG.pdf
    • IMX6DQ6SDLSRM.pdf
    • https://community.nxp.com/thread/340961#comment-478157
    • https://community.nxp.com/message/534606
    • https://community.nxp.com/message/483519
    • https://community.nxp.com/docs/DOC-104724
    • https://freescale.jiveon.com/thread/304410

Comments

Post a Comment

Popular posts from this blog

How to find a machine is little or big endian?

Enable log in U-Boot