Commits (4)
......@@ -2,6 +2,7 @@
title: Apertis secure boot
- name: Sjoerd Simons
- name: Denis Pynkin
# Apertis secure boot
......@@ -235,15 +236,417 @@ well supported by upstream components. Furthermore an initial PoC for the early
boot stages was already done for the NXP Sabre Auto boards which are based on
the same SoC.
Assuming the first implementation will be on that SoC the following
initial implementation steps should be taken:
* Enabling of secure boot in the u-boot packages for Sabrelite
* Adjust the image build process to sign u-boot such that the ROM can verify
* Adjust the image build process to sign signed images for u-boot to load
(e.g. sign kernel and initramfs)
* Add documentation on how to configure the reference board for secure boot
testing and development
As next steps the following could be undertaken:
## SabreLite secure boot preparation
The [good introduction into HAB (High Assurance Boot)](https://boundarydevices.com/high-assurance-boot-hab-dummies/)
is prepared by Boundary Devices, also there are some [documentation](https://github.com/u-boot/u-boot/blob/master/doc/imx/habv4/introduction_habv4.txt)
and examples in U-Boot source tree.
The [NXP Code Signing Tool](https://gitlab.apertis.org/pkg/development/imx-code-signing-tool)
is needed to create keys, certificates and SRK hashes used during the signing
process. Apertis reference images use the [public git repository](https://gitlab.apertis.org/infrastructure/apertis-imx-srk)
with all secrets available, so it could be used for signing binaries during
development in case if board has been fused with Apertis SRK hash (**irreversible operation!!!**).
**_Caution_**: the SabreLite board can be fused with the SRK (Super Root Key)
hash only once!
To fuse the [Apertis SRK hash](https://gitlab.apertis.org/infrastructure/apertis-imx-srk/-/blob/master/SRK_1_2_3_4_fuse.bin)
we have to have the hexadecimal dump of the hash of the key. Command below will produce the
output with commands for Apertis SRK hash fusing:
$ hexdump -e '/4 "0x"' -e '/4 "%X""\n"' SRK_1_2_3_4_fuse.bin | for i in `seq 0 7`; do read h; echo fuse prog -y 3 $i $h; done
This command generates the list of commands to be executed in a U-Boot CLI.
For Apertis SRK hash fusing they are:
fuse prog -y 3 0 0xFD415383
fuse prog -y 3 1 0x519690F5
fuse prog -y 3 2 0xE844EB48
fuse prog -y 3 3 0x179B1826
fuse prog -y 3 4 0xEC0F8D7C
fuse prog -y 3 5 0x2F209598
fuse prog -y 3 6 0x9A98BE3
fuse prog -y 3 7 0xAAD9B3D6
After execution of commands above only [Apertis development keys](https://gitlab.apertis.org/infrastructure/apertis-imx-srk/)
can be used for signing the U-Boot binary.
The i.MX6 ROM does signature verification of the bootloader during
startup, and depending on the configured (fused) mode the behaviour is
different. The i.MX6 device may work in 2 modes:
- "open" -- the HAB ROM allows the use of unsigned bootloaders or bootloaders
signed with any key, without checking its validity.
In case of errors, it will only generate HAB secure events on boot without
halting the process.
- "closed" -- only signed with correct key U-Boot may be started, any
incorrectly signed bootloader will not be started.
**It is highly recommended not to use "closed" mode for development boards!**
To check if your device is booted with correctly signed bootloader, and
SRK key is fused, just type this in the U-Boot CLI:
=> hab_status
Secure boot enabled
HAB Configuration: 0xcc, HAB State: 0x99
No HAB Events Found!
The output shows if the device is in "closed" mode (secure boot enabled) and
booted without any security errors.
In case of errors in "open" mode the same command will show the
list of HAB events similar to:
--------- HAB Event 5 -----------------
event data:
0xdb 0x00 0x14 0x41 0x33 0x21 0xc0 0x00
0xbe 0x00 0x0c 0x00 0x03 0x17 0x00 0x00
0x00 0x00 0x00 0x50
ENG = HAB_ENG_ANY (0x00)
During Linux kernel verification it is possible to emulate the "closed" mode
with `fuse override` command and proceed with the boot:
=> fuse override 0 6 0x2
=> run bootcmd
_Note_: the only issue with closed mode emulation -- the device will
accept kernel signed with any key, but HAB events will be
generated and shown in that case.
To close a device you need to fuse the same values used for overriding.
**_Caution_**: the board can only use bootloaders signed with the Apertis development key after the
step below! This is irreversible operation:
=> fuse prog 0 6 0x2
## Secure boot in the U-Boot package for Sabrelite
The U-Boot bootloader must be configured with the option `CONFIG_SECURE_BOOT`
to enable support of HAB (High Assurance Boot) support on i.MX6 platform.
Upstream U-Boot has no protection based on the HAB engine to prevent executing
unsigned binaries. Verified boot with the usage of HAB ROM is enabled in
U-Boot for Apertis only for [FIT (Flattened uImage Tree)](https://github.com/u-boot/u-boot/blob/master/doc/uImage.FIT/source_file_format.txt)
format since it allows to embed Linux kernel, initramfs and DTB into a single image.
Hence the support of FIT images must be enabled in U-Boot configuration by
option `CONFIG_FIT`.
The [patch series](https://gitlab.apertis.org/pkg/target/u-boot/-/merge_requests/4)
enables verification of FIT image prior to execution of the Linux kernel. Patched U-Boot
do verification of the whole FIT binary prior to extraction kernel and initramfs images,
and this ensures that only verified initial system will be started.
All other format types like zImage, as well as other boot methods are
prohibited on fully secured device when "closed" mode is enabled or emulated.
## Sign U-Boot bootloader such that the ROM can verify
To sign the U-Boot for SabreLite we need `cst` tool installed in the system
and the [Apertis development keys repository](https://gitlab.apertis.org/infrastructure/apertis-imx-srk) need to be checked out. Please use the [csf/csf_uboot.txt](https://gitlab.apertis.org/infrastructure/apertis-imx-srk/-/blob/master/csf/csf_uboot.txt) file
as a template for your U-Boot binary.
U-Boot for SabreLite board doesn't use SPL, hence the whole `u-boot.imx` binary
must be signed. With enabled `CONFIG_SECURE_BOOT` option the build log
will contain following output (and file `u-boot.imx.log` as well):
Image Type: Freescale IMX Boot Image
Image Ver: 2 (i.MX53/6/7 compatible)
Mode: DCD
Data Size: 606208 Bytes = 592.00 KiB = 0.58 MiB
Load Address: 177ff420
Entry Point: 17800000
HAB Blocks: 0x177ff400 0x00000000 0x00091c00
DCD Blocks: 0x00910000 0x0000002c 0x00000310
we need values from the string started with "HAB Blocks:".
Those values must be used in "[Authenticate Data]" section of
[Authenticate Data]
Verification index = 2
Blocks = 0x177ff400 0x00000000 0x00091C00 "u-boot.imx"
To sign the U-Boot with `cst` tool simply call:
cst -i csf_uboot.txt -o csf_uboot.bin
File `csf_uboot.bin` will contain signatures which should be
appended to original `u-boot.imx` binary:
cat u-boot.imx csf_uboot.bin > u-boot.imx.signed
### Sign U-Boot bootloader for loading via USB serial downloader
In case if something goes wrong and the system does not boot anymore
it is still possible to boot with the help of [USB serial downloaders](https://community.nxp.com/docs/DOC-95604),
such as `imx_usb_loader` or `uuu`.
However the U-Boot must be signed in a slightly different way since
some changes are done by ROM in runtime while loading binary. Please
refer to section "What about imx_usb_loader?" of [High Assurance Boot (HAB) for dummies](https://boundarydevices.com/high-assurance-boot-hab-dummies/)
The template [csf_uboot.txt](https://gitlab.apertis.org/infrastructure/apertis-imx-srk/-/blob/master/csf/csf_uboot.txt)
for signing U-Boot to be loaded over serial downloader protocol should contain
additional block in "[Authenticate Data]" section:
[Authenticate Data]
Verification index = 2
Blocks = 0x177ff400 0x00000000 0x00091C00 "u-boot.imx", \
0x00910000 0x0000002c 0x00000310 "u-boot.imx"
With the help of [mod_4_mfgtool.sh](https://storage.googleapis.com/boundarydevices.com/mod_4_mfgtool.sh)
script we need to store and restore DCD address from original `u-boot.imx`
in addition to signing:
sh mod_4_mfgtool.sh clear_dcd_addr u-boot.imx
cst -i csf_uboot.txt -o csf_uboot.bin
sh mod_4_mfgtool.sh set_dcd_addr u-boot.imx
cat u-boot.imx csf_uboot.bin > u-boot.imx.signed_usb
## Sign kernel images for U-Boot to load
After the successful startup of U-Boot we need to load the Linux kernel,
initramfs and DTB file into the memory. All these bits must be verified before
transferring control to the kernel. With [FIT (Flattened uImage Tree)](https://github.com/u-boot/u-boot/blob/master/doc/uImage.FIT/source_file_format.txt)
format we can use single signed image with kernel, initramfs and DTB
embedded, and this allows to avoid "mix and match" attacks with
mixed versions of kernel, initramfs, DTB and configuration.
The signing procedure for kernel images is split into 2 parts:
- preparation of the kernel image in FIT format
- sign FIT image
### FIT image creation
[U-Boot documentation](https://github.com/u-boot/u-boot/tree/master/doc/uImage.FIT)
contains a lot of details and examples how to create FIT images
for different purposes.
To embed all bits into the single FIT image we need to prepare file in
image tree source format, for Apertis we use simple
containing configuration with 3 entries for kernel, initramfs and DTB respectively.
So values `{{kernel}}`, `{{ramdisk}}` and `{{dtb}}` should be substituted with
absolute or relative path to corresponding files.
Please pay attention to addresses in `load` fields, since the whole
FIT image is loaded into the memory by address `0x12000000` (check the
value of `kernel_addr_r` in U-Boot environment), it is
important to avoid intersections with embedded binaries since they will
be copied to configured memory regions after successful verification.
To create the FIT image you need to have `mkimage` command from the
package `u-boot-tools` compiled with FIT support. With FIT source file
prepared just run `mkimage` and generate the FIT binary:
$ mkimage -f vmlinuz.its vmlinuz.itb
FIT description: Apertis armhf kernel with dtb and initramfs
Created: Fri Mar 13 02:23:33 2020
Image 0 (kernel-0)
Description: Linux Kernel
Created: Fri Mar 13 02:23:33 2020
Type: Kernel Image
Compression: uncompressed
Data Size: 4526592 Bytes = 4420.50 KiB = 4.32 MiB
Architecture: ARM
OS: Linux
Load Address: 0x10800000
Entry Point: 0x10800000
Hash algo: sha1
Hash value: 8a64994bdab06d01450560ea229c9f44f1f0af14
Image 1 (ramdisk-0)
Description: ramdisk
Created: Fri Mar 13 02:23:33 2020
Type: RAMDisk Image
Compression: uncompressed
Data Size: 20285185 Bytes = 19809.75 KiB = 19.35 MiB
Architecture: ARM
OS: Linux
Load Address: 0x15000000
Entry Point: unavailable
Hash algo: sha1
Hash value: c12652573d1b301b191cf3e2a318913afc1ae4b7
Image 2 (fdt-0)
Description: Flattened Device Tree blob
Created: Fri Mar 13 02:23:33 2020
Type: Flat Device Tree
Compression: uncompressed
Data Size: 42366 Bytes = 41.37 KiB = 0.04 MiB
Architecture: ARM
Hash algo: sha1
Hash value: ace0dd1dea00568b1c4e6df3fb0420c912e3e091
Default Configuration: 'conf-0'
Configuration 0 (conf-0)
Description: Boot Apertis
Kernel: kernel-0
Init Ramdisk: ramdisk-0
FDT: fdt-0
Hash algo: sha1
Hash value: unavailable
CSF Processed successfully and signed data available in vmlinuz.itb
### Signing the FIT image
Now it is time to sign the produced image. The procedure is similar to
signing U-Boot with additional step -- we need to add the **IVT**
(Image Vector Table) for the kernel image. We skip this step for U-Boot
since it is prepared automatically during the build of the bootloader.
The IVT is needed for the HAB ROM and must be the part of the binary, it should
be aligned to `0x1000` boundary.
For instance, if the produced binary is:
$ stat -c "%s" vmlinuz.itb
we need to pad the file to nearest aligned value, which is `25559040`:
$ objcopy -I binary -O binary --pad-to=25559040 --gap-fill=0x00 vmlinuz.itb vmlinuz-pad.itb
The next step is IVT generation for the FIT image and the easiest method is to
use the [`genIVT` script](https://storage.googleapis.com/boundarydevices.com/genIVT)
provided by Boundary Devices with adaptation for padded FIT image:
- Jump Location -- 0x12000000
Here we expect the image will be loaded by U-Boot
- Self Pointer -- 0x13860000 (Jump Location + size of padded image)
Pointer to the IVT table itself, which will place after padded image
- CSF Pointer -- 0x13860020 (Jump Location + size of padded image + size of IVT)
Pointer to signature data, which we will add after IVT
So, the IVT generation is pretty simple:
$ perl genIVT
it will generate the binary named `ivt.bin` to be added to the image:
$ cat vmlinuz-pad.itb ivt.bin > vmlinuz-pad-ivt.itb
We need to prepare the config file for signing the padded FIT image with IVT.
This step is absolutely the same as for [U-Boot signing](#sign-uboot-bootloader-such-that-the-rom-can-verify).
Configuration file for FIT image is created from
template [csf_uboot.txt](https://gitlab.apertis.org/infrastructure/apertis-imx-srk/-/blob/master/csf/csf_uboot.txt),
and values in `[Authenticate Data]` section must be the same as used
for IVT calculation -- Jump Location and the size of generated file:
[Authenticate Data]
Verification index = 2
# Authenticate Start Address, Offset, Length and file
Blocks = 0x12000000 0x00000000 0x1860020 "vmlinuz-pad-ivt.itb"
At last we are able to sign the prepared FIT image:
$ cst -i vmlinuz-pad-ivt.csf -o vmlinuz-pad-ivt.bin
CSF Processed successfully and signed data available in vmlinuz-pad-ivt.bin
## Signing bootloader and kernel from the image build pipeline
Starting with v2021dev1 Apertis uses single signed FIT kernel image for OSTree-based
systems. The signed version of U-Boot is a part of U-Boot installer.
For signing binaries with the `cst` tool we need some files from the
[Apertis development keys](https://gitlab.apertis.org/infrastructure/apertis-imx-srk)
git repository. The minimal working setup should include only 6 files:
- `SRK_1_2_3_4_table.bin` -- Super Root Keys table
- `key_pass.txt` -- file with password
- CSF certificate and key in PEM format
- IMG certificate and key in PEM format
In addition we need a template for the FIT source file and CSF template suitable for
signing U-Boot and FIT kernel.
All files listed above are added into the git repository inside [sign/imx6](https://gitlab.apertis.org/infrastructure/apertis-image-recipes/-/tree/apertis/v2021dev1/sign/imx6)
subdirectory. Since all secrets for Apertis are public we are able to use them directly
from the repo. However this is not acceptable for production.
Fortunately the most of CI tools have possibility to add files as secrets
available only on several steps. Hence we add "private" keys and password
file as "Secret file" global credentials to demonstrate the integration
into the Jenkins pipeline:
For keys usage they should be available during the call of `cst` tool,
so we have to add into the Jenkins pipeline copying of these secret files
with the same names as used in [CSF template](https://gitlab.apertis.org/infrastructure/apertis-image-recipes/-/blob/apertis/v2021dev1/sign/imx6/fit_image_csf.template)
and remove them after the usage.
For instance the simple secrets copying for Jenkins:
withCredentials([ file(credentialsId: csf_csf_key, variable: 'CSF_CSFKEY'),
file(credentialsId: csf_img_key, variable: 'CSF_IMGKEY'),
file(credentialsId: csf_key_pass, variable: 'CSF_PASSWD')]) {
// Setup keys for cst tool from Jenkins secrets
// Have to keep keys and password file near certificates
sh(script: """
cd ${WORKSPACE}/sign/imx6
cp -af $CSF_CSFKEY ./
cp -af $CSF_IMGKEY ./
cp -af $CSF_PASSWD ./""")
### U-Boot signing
To sign the U-Boot the script [scripts/sign-u-boot.sh](https://gitlab.apertis.org/infrastructure/apertis-image-recipes/-/blob/apertis/v2021dev1/scripts/sign-u-boot.sh)
has been added. It automatically generates the CSF configuration
from the template [sign/imx6/fit_image_csf.template](https://gitlab.apertis.org/infrastructure/apertis-image-recipes/-/blob/apertis/v2021dev1/sign/imx6/fit_image_csf.template)
and call the `cst` tool to sign the U-Boot binary.
The script is called by the [Debos recipe for the SabreLite U-Boot installer
- action: run
description: Sign U-Boot
script: scripts/sign-u-boot.sh "${ROOTDIR}/deb-binaries/usr/lib/u-boot/{{ $target }}/u-boot.imx"
### FIT image creation and signing
The FIT image is more complex. So for Apertis we use 2 scripts:
- the [`scripts/generate_signed_fit_image.py` script](https://gitlab.apertis.org/infrastructure/apertis-image-recipes/-/blob/apertis/v2021dev1/scripts/generate_signed_fit_image.py)
is used for generation FIT image, padding, IVT calculation and signing.
This script can be used standalone to automate all steps described
in the section "[Sign kernel images for U-Boot to load](#sign-kernel-images-for-uboot-to-load)"
- the [`scripts/generate_fit_image.sh` script](https://gitlab.apertis.org/infrastructure/apertis-image-recipes/-/blob/apertis/v2021dev1/scripts/generate_fit_image.sh)
is a wrapper for the former providing it the paths
for kernel, initramfs and DTB to include them in the signed FIT image.
The integration with the build pipeline happens **after** the kernel is installed
by the [OSTree commit recipe](https://gitlab.apertis.org/infrastructure/apertis-image-recipes/-/blob/apertis/v2021dev1/apertis-ostree-commit.yaml) by adding the step below:
- action: run
description: Generate FIT image
script: scripts/generate_fit_image.sh
**NB**: this action must be done prior to ostree commit action
to add the signed FIT kernel into OSTree repository for OTA upgrades.
## As next steps the following could be undertaken:
* Integration of PCKS#11 support in the signing process to support HSM devices
* Automated testing of secure boot if possible