Skip to main content

[TOC]

FIT

Preface​

This chapter will introduce the FIT format and the security/non-security boot scheme details based on the FIT format. For ease of introduction, this chapter mainly uses boot.img as the subject of explanation and operation throughout, but it is equally applicable to recovery.img.

Introduction​

Basic Introduction​

FIT (flattened image tree) is a new firmware type boot scheme supported by U-Boot, supporting arbitrary packaging and verification of multiple images. FIT uses an its (image source file) file to describe image information, and finally generates an itb (flattened image tree blob) image through the mkimage tool. The its file uses DTS syntax rules and is very flexible; you can directly use the libfdt library and related tools.

FIT is the default and recommended firmware format of U-Boot. Both the SPL and U-Boot stages support booting FIT format firmware. For more information, please refer to:

./doc/uImage.FIT/

Because the official FIT function cannot meet actual product requirements, the RK platform has adapted and optimized FIT. Therefore, in the FIT scheme, you must use the mkimage tool generated by RK U-Boot compilation, not the system's built-in mkimage.

Example Introduction​

The following uses u-boot.its and u-boot.itb as examples for introduction.

  • /images : Statically defines all resources, equivalent to a dtsi file.
  • /configurations : Each config node describes a set of bootable configurations, equivalent to a board-level dts file.
  • default = : Specifies the default config to enable.
/dts-v1/;

/ {
description = "Simple image with OP-TEE support";
#address-cells = <1>;

images {
uboot {
description = "U-Boot";
data = /incbin/("./u-boot-nodtb.bin");
type = "standalone";
os = "U-Boot";
arch = "arm";
compression = "none";
load = <0x00400000>;
hash {
algo = "sha256";
};
};
optee {
description = "OP-TEE";
data = /incbin/("./tee.bin");
type = "firmware";
arch = "arm";
os = "op-tee";
compression = "none";
load = <0x8400000>;
entry = <0x8400000>;
hash {
algo = "sha256";
};
};
fdt {
description = "U-Boot dtb";
data = /incbin/("./u-boot.dtb");
type = "flat_dt";
compression = "none";
hash {
algo = "sha256";
};
};
};

// configurations can define any number of different conf nodes, but actual products only need one conf.
configurations {
default = "conf";
conf {
description = "Rockchip armv7 with OP-TEE";
rollback-index = <0x0>;
firmware = "optee";
loadables = "uboot";
fdt = "fdt";
signature {
algo = "sha256,rsa2048";
padding = "pss";
key-name-hint = "dev";
sign-images = "fdt", "firmware", "loadables";
};
};
};
};

Using the mkimage tool and the its file, you can generate the itb file:

mkimage + dtc
[u-boot.its] + [images] =========> [u-boot.itb]

The fdtdump command can view the itb file contents:

cjh@ubuntu:~/uboot-nextdev/u-boot$ fdtdump fit/u-boot.itb | less

/dts-v1/;
// magic: 0xd00dfeed
// totalsize: 0x600 (1536)
// off_dt_struct: 0x48
// off_dt_strings: 0x48c
// off_mem_rsvmap: 0x28
// version: 17
// last_comp_version: 16
// boot_cpuid_phys: 0x0
// size_dt_strings: 0xc3
// size_dt_struct: 0x444

/memreserve/ 7f34d3411000 600;
/ {
version = <0x00000001>; // New firmware version number
totalsize = <0x000bb600>; // New field describing the size of the entire itb file
timestamp = <0x5ecb3553>; // New timestamp of when the current firmware was generated
description = "Simple image with OP-TEE support";
#address-cells = <0x00000001>;
images {
uboot {
data-size = <0x0007ed54>; // New field describing firmware size
data-position = <0x00000a00>; // New field describing firmware offset
description = "U-Boot";
type = "standalone";
os = "U-Boot";
arch = "arm";
compression = "none";
load = <0x00400000>;
hash {
// New sha256 checksum of firmware
value = <0xeda8cd52 0x8f058118 0x00000003 0x35360000 0x6f707465 0x0000009f 0x00000091 0x00000000>;
algo = "sha256";
};
};
optee {
data-size = <0x0003a058>;
data-position = <0x0007f800>;
description = "OP-TEE";
type = "firmware";
arch = "arm";
os = "op-tee";
compression = "none";
load = <0x08400000>;
entry = <0x08400000>;
hash {
value = <0xa569b7fc 0x2450ed39 0x00000003 0x35360000 0x66647400 0x00001686 0x000b9a00 0x552d426f>;
algo = "sha256";
};
};
fdt {
data-size = <0x00001686>;
data-position = <0x000b9a00>;
description = "U-Boot dtb";
type = "flat_dt";
compression = "none";
hash {
value = <0x0f718794 0x78ece7b2 0x00000003 0x35360000 0x00000001 0x6e730000 0x636f6e66 0x00000000>;
algo = "sha256";
};
};
};
configurations {
default = "conf";
conf {
description = "Rockchip armv7 with OP-TEE";
rollback-index = <0x00000001>; // Firmware anti-rollback version number, defaults to 0 if not manually specified
firmware = "optee";
loadables = "uboot";
fdt = "fdt";
signature {
algo = "sha256,rsa2048";
padding = "pss";
key-name-hint = "dev";
sign-images = "fdt", "firmware", "loadables";
};
};
};
};

ITB Structure​

itb is essentially a file set of fdt_blob + images. There are two packaging methods as follows. The RK platform scheme uses structure 2.

fdt blob
|-----------------------------------|
| |------| |------| |------| |
| | img0 | | img1 | | img2 | | Structure 1: images inside fdt_blob, i.e., itb = fdt_blob(including img)
| |------| |------| |------| |
|-----------------------------------|

|--------------|------|------|------|
| | | | |
| fdt blob | img0 | img1 | img2 | Structure 2: images outside fdt_blob, i.e., itb = fdt_blob + img
| | | | |
|--------------|------|------|------|

Platform Configuration​

Support List​

Platforms currently released as official features in the SDK:

Supported Platforms
RV1126/RV1109
RK3566/RK3568

Code Configuration​

Code:

// Framework code
./common/image.c
./common/image-fit.c
./common/spl/spl_fit.c

// Platform code:
./arch/arm/mach-rockchip/fit.c
./cmd/bootfit.c

// Tool code
./tools/mkimage.c
./tools/fit_image.c

Configuration:

// U-Boot stage supports FIT
CONFIG_ROCKCHIP_FIT_IMAGE=y

// U-Boot stage: secure boot, anti-rollback, hardware crypto
CONFIG_FIT_SIGNATURE=y
CONFIG_FIT_ROLLBACK_PROTECT=y
CONFIG_FIT_HW_CRYPTO=y

// SPL stage: secure boot, anti-rollback, hardware crypto
CONFIG_SPL_FIT_SIGNATURE=y
CONFIG_SPL_FIT_ROLLBACK_PROTECT=y
CONFIG_SPL_FIT_HW_CRYPTO=y

// uboot.img contains several copies of uboot.itb, each uboot.itb size
CONFIG_SPL_FIT_IMAGE_KB=2048
CONFIG_SPL_FIT_IMAGE_MULTIPLE=2

// The uboot project outputs fit format uboot.img by default after compilation; otherwise traditional RK format uboot.img and trust.img.
CONFIG_ROCKCHIP_FIT_IMAGE_PACK=y

If the FIT scheme is an officially released feature in the SDK, most basic configurations are already enabled. Options users need to configure themselves:

// U-Boot secure boot and anti-rollback mechanism
CONFIG_FIT_SIGNATURE=y
CONFIG_FIT_ROLLBACK_PROTECT=y

// SPL secure boot and anti-rollback mechanism
CONFIG_SPL_FIT_SIGNATURE=y
CONFIG_SPL_FIT_ROLLBACK_PROTECT=y
  • CONFIG_FIT_SIGNATURE not enabled: uboot can support booting three firmware formats: android, uimage, fit (released SDK selects which to enable based on platform requirements).
  • CONFIG_FIT_SIGNATURE enabled: uboot only supports booting fit firmware.

Image Files​

The FIT scheme ultimately outputs two FIT format firmware for flashing: uboot.img (without trust.img) and boot.img, plus an SPL file for packaging into a loader.

  • uboot.img File

    uboot.itb = trust + u-boot.bin + mcu.bin(option)

    uboot.img = uboot.itb * N copies (N is generally 2)

    trust and mcu files come from the rkbin project; the compilation script automatically indexes and retrieves them from the rkbin project.

  • boot.img File

    boot.itb = kernel + fdt + resource + ramdisk(optional)

    boot.img = boot.itb * M copies (M is generally 1)

  • MCU Configuration

    Some platforms may include MCU firmware. Different products can decide whether to enable it based on the corresponding TRUST ini configuration. For example:

    // File: RKTRUST/RV1126TOS_TB.ini, used for fast boot products, MCU enabled.
    [TOS]
    TOSTA=bin/rv11/rv1126_tee_ta_tb_v1.04.bin
    ADDR=0x00040000

    // MCU configuration format: firmware path, boot address, status (okay or disabled).
    // If disabled, the mcu will not be packaged into uboot.img.
    [MCU]
    MCU=bin/rv11/rv1126_mcu_v1.02.bin,0x108000,okay
  • Firmware Compression

    Some platforms can support compression of sub-firmware inside uboot.img. Supported configurations:

    PlatformCompression FormatFirmware
    RV1126gzip, noneu-boot.bin, trust, mcu(optional)

    Users can add attributes in the corresponding TRUST ini file in the rkbin project to enable it. For example:

    // File: RKTRUST/RV1126TOS_SPI_NOR_TINY.ini, used for small capacity SPI Nor products.
    [TOS]
    TOS=bin/rv11/rv1126_tee_v1.02.bin
    ADDR=0x08400000
    [MCU]
    MCU=bin/rv11/rv1126_mcu_v1.00.bin,0x208000,disabled

    // Compression format: gzip or none. If the following configuration field does not exist, it defaults to uncompressed.
    [COMPRESSION]
    COMPRESSION=gzip
  • SPL File

    The SPL file refers to spl/u-boot-spl.bin generated after compilation completion, responsible for booting the FIT format uboot.img. Users need to use it to replace the closed-source miniloader on the RK platform, ultimately packaging the loader.

  • ./fit Directory

    After U-Boot compilation, the ./fit folder is generated in the directory, containing some intermediate files, which will be introduced in subsequent sections.

boot.img and uboot.img are compiled and generated in the sdk project and uboot project respectively. However, boot.img supporting secure boot must be placed in the U-Boot project for repackaging and signing, as described in subsequent sections.

ITS Files​

  • The its file for uboot is ./fit/u-boot.its, dynamically created by the script specified in defconfig via CONFIG_SPL_FIT_GENERATOR. Visible after successful firmware compilation.

  • The its file for boot is located in the SDK project:

device/rockchip/[platform]/xxx.its // [platform] is the platform directory
// Core packaging tool, automatically generated after compilation completion, available in both U-Boot and rkbin repositories (the U-Boot repository generates it in real-time during compilation).
./tools/mkimage
// Firmware packaging script
./make.sh
// Firmware re-signing script
scripts/fit-resign.sh
// Firmware unpacking script
scripts/fit-unpack.sh
// Firmware replacement script
./scripts/fit-repack.sh

The use of script tools will be introduced in subsequent sections. Here, we first focus on the parameters of make.sh:

Optional parameters (users decide whether to pass based on actual situation):

  • --spl-new: Passing this parameter means using the spl file from the current compilation to package the loader; otherwise, use the spl file from the rkbin project.
  • --version-uboot [n]: Specify the firmware version number of uboot.img. n must be a positive decimal integer.
  • --version-boot [n]: Specify the firmware version number of boot.img. n must be a positive decimal integer.
  • --version-recovery [n]: Specify the firmware version number of recovery.img. n must be a positive decimal integer.

Required parameters (when enabling secure boot):

  • --rollback-index-uboot [n]: Specify the anti-rollback version number of uboot.img firmware. n must be a positive decimal integer.
  • --rollback-index-boot [n]: Specify the anti-rollback version number of boot.img firmware. n must be a positive decimal integer.
  • --rollback-index-recovery [n]: Specify the anti-rollback version number of recovery.img firmware. n must be a positive decimal integer.
  • --no-check: Used when packaging secure firmware, to skip the self-verification of the secure firmware packaging script.

Description:

  1. Firmware anti-rollback version number: Only allowed to be activated when secure boot is enabled. This version number is saved in OTP or other secure storage. Main purpose: To prevent firmware version rollback for vulnerability attacks.

  2. Firmware version number: Optional. Defaults to 0 if not specified. Main purpose: Serves as a firmware version identifier, convenient for users to perform version management of firmware.

Non-Secure Boot​

uboot.img​

Compilation command:

./make.sh rv1126 --spl-new --uboot-version 10 // --spl-new and --uboot-version are optional

Compilation result:

......
CC spl/common/spl/spl.o
CC spl/lib/display_options.o
LD spl/common/spl/built-in.o
LD spl/lib/built-in.o
LD spl/u-boot-spl
OBJCOPY spl/u-boot-spl-nodtb.bin
CAT spl/u-boot-spl-dtb.bin
COPY spl/u-boot-spl.bin
CFGCHK u-boot.cfg

out:rv1126_spl_loader_v1.00.100.bin
fix opt:rv1126_spl_loader_v1.00.100.bin
merge success(rv1126_spl_loader_v1.00.100.bin)
/home4/cjh/uboot-nextdev

// Generate rv1126_spl_loader_v1.00.100.bin (using spl to replace the traditional miniloader on RK platform)
// loader ini file source
pack loader(SPL) okay! Input: /home4/cjh/rkbin/RKBOOT/RV1126MINIALL.ini
// Prompt from the --spl-new parameter; users can choose not to add this parameter.
pack loader with new: spl/u-boot-spl.bin

// Generate uboot.img (including trust and uboot), version number 10
Image(no-signed, version=10): uboot.img (FIT with uboot, trust...) is ready
// trust ini file source
pack uboot.img okay! Input: /home4/cjh/rkbin/RKTRUST/RV1126TOS.ini

Platform RV1126 is build OK, with exist .config

Backup packaging: Specify multiple backups of uboot.img via defconfig configuration:

CONFIG_SPL_FIT_IMAGE_KB=2048 // Single itb size
CONFIG_SPL_FIT_IMAGE_MULTIPLE=2 // Number of copies

SPL uses this configuration to detect and boot U-Boot and trust, mainly to handle firmware corruption caused by abnormal power failure during the OTA upgrade process, which would otherwise prevent boot.

boot.img​

If the FIT scheme is an officially released feature in the SDK, the SDK compilation will generate a FIT format boot.img.

To generate boot.img for secure boot, the boot.img generated by the SDK must be placed in the U-Boot project for repackaging and signing, because the signing tool, configuration, parameters, etc., for secure firmware packaging all come from the U-Boot project.

Secure Boot​

The FIT scheme supports secure boot with the following related features:

  • sha256 + rsa2048 + pkcs-v2.1(pss) padding
  • Firmware anti-rollback
  • Firmware re-signing (remote signing)
  • Crypto hardware acceleration

Principle​

Verification Flow​

  • Maskrom verifies loader (including SPL, ddr, usbplug)
  • SPL verifies uboot.img (including trust, U-Boot...)
  • U-Boot verifies boot.img (including kernel, fdt, ramdisk...)

Currently, only sha256+rsa2048+pkcs-v2.1(pss) padding secure verification mode is supported by default.

Key Storage​

RSA key is packaged into u-boot.dtb and u-boot-spl.dtb by mkimage, which are then packaged into u-boot.bin and u-boot-spl.bin.

The format of the RSA key in u-boot.dtb is as follows (similarly for u-boot-spl.dtb):

cjh@ubuntu:~/uboot-nextdev$ fdtdump u-boot.dtb | less
/dts-v1/;
....

/ {
#address-cells = <0x00000001>;
#size-cells = <0x00000001>;
compatible = "rockchip,rv1126-evb", "rockchip,rv1126";
model = "Rockchip RV1126 Evaluation Board";

// The signature node is automatically inserted by the mkimage tool, containing RSA-SHA algorithm type, RSA core factor parameters, etc.
signature {
key-dev {
required = "conf";
algo = "sha256,rsa2048";
rsa,np = <0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x1327f633 0x00000003 0x00000003 0x00000003 0xc7aead6a 0xb4c79f40 0xa82bdf76 0xfb2f8387 0xa1e06dce 0xd451a706 0xc7f865e3 0x3e2d7ca8 0x6a71762e 0x125f1828 0x36ab1a41 0xb7e9e852 0x7bd0011a 0x7279e0b8 0xf37e189c 0x8cf00963 0x00000100 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000377 0x00000004 0x00000004 0x00000004 0x00000002 0x00000003 0x69616c40 0x00000003 0x6d634066 0x00000010 0x66633630 0x73797363>;
rsa,c = <0x00000000>;
rsa,r-squared = <0x00000000>;
rsa,modulus = <0xc25ae693 0xc359f2a4 0xa866c89d 0xb7b1994f 0xf9f9f690 0x518d54a7 0xda0b83e8 0x06606e12 0x6ad1cbf9 0x92438edd 0x81e039c0 0x5d7322cc 0x124cdc80 0xa0c3288a 0x9265c3ae 0x6ac47a4b 0x00000003 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000008 0x00000003 0x00000003 0x00000003 0x00000002 0x73657300 0x2f736572 0x00000000 0x2f64776d 0x00000003 0x6d634066 0x00000001 0x30303000 0x726f636b 0x67726600 0x00000008 0x00000003 0x00000004 0x00000001 0x30303000 0x726f636b 0x706d7567 0x00000003 0x00001000 0x00000003 0x00000002 0x6e616765 0x30000000 0x726f636b 0x706d7500 0x00000008>;
rsa,exponent-BN = <0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000003 0x00010001 0xe95771c5 0x00000800 0x64657600 0x616c6961 0x0000002c 0x30303030 0x00000034 0x30303000 0x2f64776d 0x00000002 0x65303030 0x0000001b 0x3132362d 0x00000003 0x00020000 0x00000003 0x00000002 0x65303230 0x0000001b 0x3132362d 0x6e000000 0xfe020000 0x00000042 0x0000006d 0x722d6d61 0x65303030 0x0000001b 0x3132362d 0x00000003 0x00001000 0x00000002 0x6e74726f 0x30000000 0x726f636b 0x706d7563 0x0000003e 0x00000004 0x00000004 0x00000004 0x00000000 0x00000050 0x636c6f63 0x40666634 0x00000014 0x2c727631 0x00000008>;
rsa,exponent = <0x00000000 0x00000368>;
rsa,n0-inverse = <0xe95771c5>;
rsa,num-bits = <0x00000800>;
key-name-hint = "dev";
};
};

SPL supports burning key hash. The key-dev in u-boot-spl.dtb will have an additional burn-key-hash = <0x00000001>;.

Key Usage​

From Maskrom to kernel, a single RSA public key is used for security verification:

  • Maskrom verifies loader.

    The RSA public key needs to be written into the loader file header using the PC tool rk_sign_tool. During secure boot, Maskrom first obtains the RSA public key from the loader firmware header and verifies its validity; then uses this public key to verify the loader's firmware signature.

    rk_sign_tool can be obtained from the rkbin repository. U-Boot will automatically complete the loader signing.

  • SPL verifies U-Boot and trust.

    SPL stores the RSA public key in u-boot-spl.dtb, which is packaged into u-boot-spl.bin (ultimately packaged into the loader). During secure boot, SPL reads the RSA public key from its dtb file to perform security verification on uboot.img.

  • U-Boot verifies boot.

    U-Boot stores the RSA public key in u-boot.dtb, which is packaged into u-boot.bin (ultimately packaged as uboot.img). During secure boot, U-Boot reads the RSA public key from its dtb file to verify boot.img.

Therefore, the RSA Key at the current level is already part of its own firmware, and its security is ensured by the previous loader's security verification.

Signature Storage​

The RSA signature result is stored in the itb file. The signed content is specified by hashed-nodes: including the entire conf node attributes, packaged firmware nodes, etc.

Below is the signature information of u-boot.itb, similarly for boot.itb:

cjh@ubuntu:~/uboot-nextdev$ fdtdump uboot.img | less
/dts-v1/;
......

configurations {
default = "conf";
conf {
description = "Rockchip armv7 with OP-TEE";
// Current firmware version number
rollback-index = <0x0000001c>;
firmware = "optee";
loadables = "uboot";
fdt = "fdt";

// Signed content and signature result, automatically inserted by mkimage
signature {
hashed-strings = <0x00000000 0x000000da>;
// Specify signed content
hashed-nodes = "/", "/configurations", "/configurations/conf", "/images/fdt", "/images/fdt/hash", "/images/optee", "/images/optee/hash", "/images/uboot", "/images/uboot/hash";
// Signing time, signer, version
timestamp = <0x5e9427b4>;
signer-version = "2017.09-g8bb63db-200413-dirty #cjh";
signer-name = "mkimage";
// Signature result!! (uses sha256+rsa2048)
value = <0x78397d5d 0xb9219a0b 0xa7cb91a7 0xe1f32867 0x62719d9b 0x8901200c 0xfcbac03a 0x1295ccc8 ...>;
algo = "sha256,rsa2048";
key-name-hint = "dev";
sign-images = "fdt", "firmware", "loadables";
};
};
};

Anti-Rollback​

  • Secure boot supports specifying the current firmware version number for boot.img and uboot.img respectively. If the current firmware version number is less than the minimum version number on the machine, boot is not allowed.

  • Minimum version number update: After completing security verification and confirming the system can boot normally, it is updated to OTP or secure storage.

Preliminary Preparation​

Key​

Executing the following three commands in the U-Boot project can generate an RSA key pair for signing. Usually, it only needs to be generated once. After that, this key pair is used to sign and verify firmware. Please keep it safe.

// 1. Key directory: keys
mkdir -p keys

// 2. Use RK's "rk_sign_tool" tool to generate RSA2048 private key privateKey.pem and public key publicKey.pem, rename and store them as: keys/dev.key and keys/dev.pubkey. Command:
../rkbin/tools/rk_sign_tool kk --bits 2048 --out .

// 3. Use -x509 and private key to generate a self-signed certificate: keys/dev.crt (functionally equivalent to public key)
openssl req -batch -new -x509 -key keys/dev.key -out keys/dev.crt

If there is an error about missing .rnd file in the user directory:

Can't load /home4/cjh//.rnd into RNG ... Please create it manually first: touch ~/.rnd

ls keys/ to view results:

dev.crt dev.key dev.pubkey

Note: The names "keys", "dev.key", "dev.crt", and "dev.pubkey" must not be changed. These names are already statically defined in the its file; changing them will cause packaging failure.

Configuration​

Enable the following configurations in U-Boot's defconfig:

// Required.
CONFIG_FIT_SIGNATURE=y
CONFIG_SPL_FIT_SIGNATURE=y

// Optional.
CONFIG_FIT_ROLLBACK_PROTECT=y // boot.img anti-rollback
CONFIG_SPL_FIT_ROLLBACK_PROTECT=y // uboot.img anti-rollback

It is recommended to select the configuration via make menuconfig, then update the original defconfig file via make savedefconfig. This avoids compilation failures caused by incorrect dependency relationships due to forced defconfig configuration.

Firmware​

Copy a copy of boot.img generated in the SDK project to the U-Boot root directory.

Compilation Package​

(1) Basic command (without anti-rollback):

./make.sh rv1126 --spl-new --boot_img boot.img --recovery_img recovery.img

Compilation result:

......

// After compilation, signed uboot.img and boot.img are generated.
start to sign rv1126_spl_loader_v1.00.100.bin
......
sign loader ok.
......
Image(signed, version=0): uboot.img (FIT with uboot, trust...) is ready
Image(signed, version=0): recovery.img (FIT with kernel, fdt, resource...) is ready
Image(signed, version=0): boot.img (FIT with kernel, fdt, resource...) is ready
Image(signed): rv1126_spl_loader_v1.05.106.bin (with spl, ddr, usbplug) is ready
pack uboot.img okay! Input: /home4/cjh/rkbin/RKTRUST/RV1126TOS.ini

Platform RV1126 is build OK, with new .config(make rv1126-secure_defconfig)

(2) Extended command 1:

If anti-rollback is enabled, rollback parameters must be added to the above (1). For example:

// Specify the minimum version numbers of uboot.img and boot.img as 10 and 12 respectively.
./make.sh rv1126 --spl-new --boot_img boot.img --recovery_img recovery.img --rollback-index-uboot 10 --rollback-index-boot 12 --rollback-index-recovery 12

Compilation result:

......

// After compilation, signed uboot.img and boot.img are generated, including anti-rollback version numbers.
start to sign rv1126_spl_loader_v1.00.100.bin
......
sign loader ok.
......
Image(signed, version=0, rollback-index=10): uboot.img (FIT with uboot, trust) is ready
Image(signed, version=0, rollback-index=12): recovery.img (FIT with kernel, fdt, resource...) is ready
Image(signed, version=0, rollback-index=12): boot.img (FIT with kernel, fdt, resource...) is ready
Image(signed): rv1126_spl_loader_v1.00.100.bin (with spl, ddr, usbplug) is ready

(3) Extended command 2:

If the public key hash needs to be burned into OTP/eFUSE, add the --burn-key-hash parameter to the above (1) or (2). For example:

// Specify the minimum version numbers of uboot.img and boot.img as 10 and 12.
// Require SPL stage to burn the public key hash into OTP/eFUSE.
./make.sh rv1126 --spl-new --boot_img boot.img --recovery_img recovery.img --rollback-index-uboot 10 --rollback-index-boot 12 --rollback-index-recovery 12 --burn-key-hash

Compilation result:

......

// Enable burn-key-hash
## spl/u-boot-spl.dtb: burn-key-hash=1

// After compilation, signed uboot.img and boot.img are generated, including anti-rollback version numbers.
start to sign rv1126_spl_loader_v1.00.100.bin
......
sign loader ok.
......
Image(signed, version=0, rollback-index=10): uboot.img (FIT with uboot, trust) is ready
Image(signed, version=0, rollback-index=12): recovery.img (FIT with kernel, fdt, resource...) is ready
Image(signed, version=0, rollback-index=12): boot.img (FIT with kernel, fdt, resource...) is ready
Image(signed): rv1126_spl_loader_v1.00.100.bin (with spl, ddr, usbplug) is ready

When powering on, you can see SPL print: RSA: Write key hash successfully.

(4) Precautions:

  • --boot_img : Optional. Specifies the boot.img to be signed.

  • --recovery_img : Optional. Specifies the recovery.img to be signed.

  • --rollback-index-uboot, --rollback-index-boot, --rollback-index-recovery : Optional. Specifies the anti-rollback version number.

  • --spl-new : If this parameter is not included in the compile command, the spl file in rkbin is used by default to package the loader; otherwise, the currently compiled spl file is used.

    Because u-boot-spl.dtb needs to package the RSA public key (from the user), the RK SDK does not submit spl files that support secure boot in the rkbin repository. Therefore, users should specify this parameter for compilation. However, users can also submit their own spl version to the rkbin project, after which they no longer need to specify this parameter for each firmware compilation, always using the stable version of the spl file.

  • After compilation, three firmware files are generated: loader, uboot.img, boot.img. As long as the RSA key has not been changed, any of these firmware files can be updated individually.

Boot Information​

Below is the secure boot information:

BW=32 Col=10 Bk=8 CS0 Row=15 CS=1 Die BW=16 Size=1024MB
out
U-Boot SPL board init
U-Boot SPL 2017.09-gacb99c5-200408-dirty #cjh (Apr 09 2020 - 20:51:21)
unrecognized JEDEC id bytes: 00, 00, 00

Trying to boot from MMC1
// SPL completes signature verification
sha256,rsa2048:dev+
// Anti-rollback detection: current uboot.img firmware version is 10, machine's minimum version is 9
rollback index: 10 >= 9, OK
// SPL completes hash verification of sub-images
## Checking optee ... sha256+ OK
## Checking uboot ... sha256+ OK
## Checking fdt ... sha256+ OK

Jumping to U-Boot via OP-TEE
I/TC:
E/TC:0 0 plat_rockchip_pmu_init:2003 0
E/TC:0 0 plat_rockchip_pmu_init:2006 cpu off
E/TC:0 0 plat_rockchip_pmusram_prepare:1945 pmu sram prepare 0x14b10000 0x8400880 0x1c
E/TC:0 0 plat_rockchip_pmu_init:2020 pmu sram prepare
E/TC:0 0 plat_rockchip_pmu_init:2056 remap
I/TC: OP-TEE version: 3.6.0-233-g35ecf936 #1 Tue Mar 31 08:46:13 UTC 2020 arm
I/TC: Next entry point address: 0x00400000
I/TC: Initialized


U-Boot 2017.09-gacb99c5-200408-dirty #cjh (Apr 09 2020 - 20:51:21 +0800)

Model: Rockchip RV1126 Evaluation Board
PreSerial: 2
DRAM: 1023.5 MiB
Sysmem: init
Relocation Offset: 00000000, fdt: 3df404e0
Using default environment

dwmmc@ffc50000: 0
Bootdev(atags): mmc 0
MMC0: HS200, 200Mhz
PartType: EFI
boot mode: normal
conf: sha256,rsa2048:dev+
resource: sha256+
DTB: rk-kernel.dtb
FIT: signed, conf required
HASH(c): OK

I2c0 speed: 400000Hz
PMIC: RK8090 (on=0x10, off=0x00)
vdd_logic 800000 uV
vdd_arm 800000 uV
vdd_npu init 800000 uV
vdd_vepu init 800000 uV
......

Hit key to stop autoboot('CTRL+C'): 0
## Booting FIT Image at 0x3d8122c0 with size 0x0052b200
Fdt Ramdisk skip relocation
## Loading kernel from FIT Image at 3d8122c0 ...
Using 'conf' configuration
// uboot completes signature verification
Verifying Hash Integrity ... sha256,rsa2048:dev+ OK
// Anti-rollback detection: current boot.img firmware version is 22, machine's minimum version is 21
Verifying Rollback-index ... 22 >= 21, OK
Trying 'kernel' kernel subimage
Description: Kernel for arm
Type: Kernel Image
Compression: uncompressed
Data Start: 0x3d8234c0
Data Size: 5349248 Bytes = 5.1 MiB
Architecture: ARM
OS: Linux
Load Address: 0x02008000
Entry Point: 0x02008000
Hash algo: sha256
Hash value: 64b4a0333f7862967be052a67ee3858884fcefebf4565db5c3828a941a15f34a
Verifying Hash Integrity ... sha256+ OK // kernel hash verification completed
## Loading ramdisk from FIT Image at 3d8122c0 ...
Using 'conf' configuration
Trying 'ramdisk' ramdisk subimage
Description: Ramdisk for arm
Type: RAMDisk Image
Compression: uncompressed
Data Start: 0x3dd3d4c0
Data Size: 0 Bytes = 0 Bytes
Architecture: ARM
OS: Linux
Load Address: 0x0a200000
Entry Point: unavailable
Hash algo: sha256
Hash value: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Verifying Hash Integrity ... sha256+ OK // ramdisk hash verification completed
Loading ramdisk from 0x3dd3d4c0 to 0x0a200000
## Loading fdt from FIT Image at 3d8122c0 ...
Using 'conf' configuration
Trying 'fdt' fdt subimage
Description: Device tree blob for arm
Type: Flat Device Tree
Compression: uncompressed
Data Start: 0x3d812ec0
Data Size: 66974 Bytes = 65.4 KiB
Architecture: ARM
Load Address: 0x08300000
Hash algo: sha256
Hash value: 8fb1f170766270ed4f37cce4b082a51614cb346c223f96ddfe3526fafc5729d7
Verifying Hash Integrity ... sha256+ OK // fdt hash verification completed
Loading fdt from 0x3d812ec0 to 0x08300000
Booting using the fdt blob at 0x8300000
Loading Kernel Image from 0x3d8234c0 to 0x02008000 ... OK
Using Device Tree in place at 08300000, end 0831359d
Adding bank: 0x00000000 - 0x08400000 (size: 0x08400000)
Adding bank: 0x0848a000 - 0x40000000 (size: 0x37b76000)
Total: 236.327 ms

Starting kernel ...

[ 0.000000] Booting Linux on physical CPU 0xf00
[ 0.000000] Linux version 4.19.111 (cjh@ubuntu) (gcc version 6.3.1 20170404 (Linaro GCC 6.3-2017.05)) #28 SMP PREEMPT Wed Mar 25 16:03:27 CST 2020
[ 0.000000] CPU: ARMv7 Processor [410fc075] revision 5 (ARMv7), cr=10c5387d

Remote Signing​

From the above sections, creating secure firmware requires users to do it on their local PC, meaning users must possess: RSA key pair and firmware. However, in practice, users may need to upload firmware to a remote server, where the server holds the RSA private key to complete the signing, and then return the signed firmware to the local user. For this situation, the RK FIT scheme implements it through "re-signing".

Implementation Approach​

  • Since users can only obtain the server's public key, they first use a temporary private key + server's public key to perform one packaging sign on the local PC, generating a secure firmware with a temporary signature and signed data.

    The public key is used to package the public key into the dtb file for use during the secure boot flow; the private key is used for temporary signing.

  • Users send the signed data to the server (not the entire firmware, saving time), and the server uses the private key to sign the signed data and returns the signature to the user.

  • Users replace the temporary signature in the secure firmware with this signature to obtain the final secure firmware for burning.

Signed Data​

The signed data mentioned in the above section includes: fdt blob configuration + sub-image hash collection.

  • fdt blob node configuration

    hashed-nodes specifies a series of nodes, and the contents of these nodes are included in the signed data.

    cjh@ubuntu:~/uboot-nextdev$ fdtdump uboot.img | less
    /dts-v1/;
    ......

    configurations {
    default = "conf";
    conf {
    description = "Rockchip armv7 with OP-TEE";
    rollback-index = <0x0000001c>;
    firmware = "optee";
    loadables = "uboot";
    fdt = "fdt";

    signature {
    hashed-strings = <0x00000000 0x000000da>;
    // These node contents will be included in the signed data
    hashed-nodes = "/", "/configurations/conf", "/images/fdt", "/images/fdt/hash", "/images/optee", "/images/optee/hash", "/images/uboot", "/images/uboot/hash";
    ......
  • Sub-image hash collection.

    mkimage automatically generates hash values for each sub-image and appends them to the hash node. All sub-image hash values specified by sign-images are included in the signed data (essentially specified through hashed-nodes specifying hash nodes). For example:

    cjh@ubuntu:~/uboot-nextdev/u-boot$ fdtdump fit/u-boot.itb | less

    /dts-v1/;
    ......

    / {
    totalsize = <0x000bb600>;
    timestamp = <0x5ecb3553>;
    description = "Simple image with OP-TEE support";
    #address-cells = <0x00000001>;
    images {
    uboot {
    data-size = <0x0007ed54>;
    data-position = <0x00000a00>;
    description = "U-Boot";
    type = "standalone";
    os = "U-Boot";
    arch = "arm";
    compression = "none";
    load = <0x00400000>;
    hash {
    // uboot image hash, automatically calculated by mkimage
    value = <0xeda8cd52 0x8f058118 0x00000003 0x35360000 0x6f707465 0x0000009f 0x00000091 0x00000000>;
    algo = "sha256";
    };
    };
    ......

Specific Steps​

The RSA key pair for signing firmware is: dev.key, dev.pubkey, and dev.crt. dev.key is held by the remote server as the private key, while users only have dev.pubkey and dev.crt.

Step 1:

In the local U-Boot project environment: users put dev.crt in the keys directory, then use RK's "rk_sign_tool" tool to randomly generate a temporary private key, name it dev.key, and put it in the keys directory. Refer to the above section (but add --no-check to the compilation parameters) to generate signed firmware uboot.img and boot.img (these will not be used in the end; users need the intermediate files).

Note: The compile command must include the parameter --no-check, otherwise the packaging script will fail self-verification because dev.key and dev.crt do not match. For example:

./make.sh rv1126 --spl-new --boot_img boot.img --rollback-index-uboot 10 --rollback-index-boot 12 --no-check

Besides generating signed firmware uboot.img and boot.img, users can also get intermediate files in the fit/ directory:

// Signed content (data2sign means data to sign)
fit/uboot.data2sign
fit/boot.data2sign

// Signed itb file (using temporary private key), from which our img file is obtained after multiple backups.
fit/uboot.itb
fit/boot.itb

Step 2:

Users send uboot.data2sign to the remote server. Assuming the server holds the private key dev.key, use the following command to sign and output the signature result: uboot.sig

openssl dgst -sha256 -sign dev.key -sigopt rsa_padding_mode:pss -out uboot.sig uboot.data2sign

The server returns the signature result file uboot.sig to the user, who then uses uboot.sig to replace the temporary signature in uboot.itb:

./scripts/fit-resign.sh -f fit/uboot.itb -s uboot.sig // Will generate a new uboot.img for flashing

Similarly for the boot.itb file. Thus the user obtains the final valid signed firmware uboot.img and boot.img.

Precautions:

  • The -f parameter of fit-resign.sh specifies the itb file, not the img file. The script re-signs the itb and generates an img file.
  • The itb file used when executing fit-resign.sh must be the one generated in step 1, because the itb file and data2sign file have a one-to-one correspondence. The data2sign information contains the timestamp of generating the itb file, i.e., /timestamp = <...>. Therefore, even if there is no code change, recompiling to obtain a new uboot.itb and replacing uboot.sig into the new uboot.itb will cause secure boot failure!
  • Since there is no private key, the loader needs to be sent to the server separately for signing.

Other Schemes​

Besides the "re-signing" method, is it possible to directly upload the entire firmware (boot.img, uboot.img) or discrete images (u-boot.bin, fdt, ramdisk, kernel ...) to the server for signing?

Based on the design principles and implementation of FIT, implementing other schemes is relatively difficult. The following explains:

  • Scheme 1: Upload non-secure boot.img and uboot.img to the server for repackaging + signing.

    Problem: Also need to upload configuration information from the local U-Boot compilation environment, u-boot-spl.bin file, etc.

  • Scheme 2: Upload secure boot.img and uboot.img to the server for repackaging + signing.

    Problem: The local compilation of firmware has already packaged the RSA public key; the server would package the RSA public key a second time.

  • Scheme 3: Upload all discrete images (kernel, dtb, ramdisk, resource...) for packaging + signing.

    Problem: Too many files to upload, which is cumbersome, and also has the same issues as Scheme 1.

Common problem with all the above schemes: The server must use the RK mkimage tool, which may be updated by RK.

Therefore, the current "re-signing" method is the simplest to operate, has no dependencies, and is the least error-prone: users only need to upload the signed data, and the server only needs to use the openssl command to sign.

Firmware Unpacking​

Users can use a script to unpack firmware, e.g., boot.img:

cjh@ubuntu:~/uboot-nextdev$ ./scripts/fit-unpack.sh -f boot.img -o out
Unpack to directory out:
fdt : 82813 bytes... sha256+
kernel : 5844640 bytes... sha256+
ramdisk : 0 bytes... sha256+
resource : 120832 bytes... sha256+

If the img contains multiple backups, the script only unpacks the first itb; sha256+ indicates the firmware is not damaged, otherwise sha256- is displayed.

Firmware Replacement​

Users can use a script to batch replace sub-firmware. For example: use sub-firmware existing in the out directory to replace sub-firmware with the same name in uboot.img.

cjh@ubuntu:~/uboot-nextdev$ ./scripts/fit-repack.sh -f uboot.img -d out/
Unpack to directory out/repack/:
uboot : 6 bytes... sha256+
optee : 6 bytes... sha256+
fdt : 4 bytes... sha256+
....
Image(repack): uboot.img is ready

Security Verification Step-by-Step​

  1. Enter the u-boot directory, open the corresponding platform's configs/rxxxxx_defconfig, and select the following configurations:
// Required.
CONFIG_FIT_SIGNATURE=y
CONFIG_SPL_FIT_SIGNATURE=y

// Optional.
CONFIG_FIT_ROLLBACK_PROTECT=y // boot.img anti-rollback
CONFIG_SPL_FIT_ROLLBACK_PROTECT=y // uboot.img anti-rollback
  1. Execute the following to generate keys:
mkdir -p keys
../rkbin/tools/rk_sign_tool kk --bits 2048 --out .
cp privateKey.pem keys/dev.key && cp publicKey.pem keys/dev.pubkey
openssl req -batch -new -x509 -key keys/dev.key -out keys/dev.crt

Note: Execute this step only once, then keep these keys safe.

  1. Compile and sign, using rv1126 as an example (to compile and sign firmware for other chips like rk3566, replace rv1126 with rk3566 in the following commands):
// Linux: Copy boot.img, recovery.img to the u-boot directory, execute the following script to sign loader, uboot, boot, recovery, set the anti-rollback version numbers for uboot, boot, recovery. Note that the anti-rollback version numbers are configured as needed.
./make.sh rv1126 --spl-new --boot_img boot.img --recovery_img recovery.img --rollback-index-uboot 1 --rollback-index-boot 2

// Android: Sign loader and uboot, set the anti-rollback version number for uboot. Note that the anti-rollback version number is configured as needed.
./make.sh rv1126 --spl-new --rollback-index-uboot 1

If compilation shows:

Can't load XXXXXX//.rnd into RNG

Execute:

touch ~/.rnd
  1. Public key hash burning:
// Linux: Copy boot.img, recovery.img to the u-boot directory, execute the following script to sign loader, uboot, boot, recovery, set anti-rollback version numbers for uboot, boot, recovery, enable key hash burning.
./make.sh rv1126 --spl-new --boot_img boot.img --recovery_img recovery.img --rollback-index-uboot 1 --rollback-index-boot 2 --burn-key-hash

// Android: Sign loader, uboot, set the anti-rollback version number for uboot, enable key hash burning.
./make.sh rv1126 --spl-new --rollback-index-uboot 1 --burn-key-hash

Note: Configure --burn-key-hash only after the entire product development and verification is complete; otherwise, once security is enabled, only signed firmware can be updated during product development.

  1. Other Android firmware signing:

Refer to "Rockchip_Developer_Guide_Secure_Boot_for_UBoot_Next_Dev_CN.md"