Integrating with U-Boot and mkimage
Overview
U-Boot is a popular open-source bootloader for embedded devices. It uses the mkimage tool to create bootable firmware images. mkimage supports the PKCS#11 standard, allowing it to sign these images using keys managed by the Code Sign Client.
This guide provides a complete example of how to create and sign a U-Boot image using mkimage with a key from Next-Gen Trust Security (NGTS).
Before you begin
This guide assumes you are working in a Linux environment and have the necessary build tools (such as git and make) installed. You will also need:
- p11-kit and GnuTLS (p11tool) packages installed
- Code Sign Client installed
How do I get started?
This process involves compiling the U-Boot tools, finding your Signing Key, defining an image, and finally building and signing it.
Step 1: Create a module file to allow using the PKCS#11 driver with p11-kit
echo "module: /opt/venafi/codesign/lib/venafipkcs11.so" > /usr/share/p11-kit/modules/venafi.module
Step 2: Download and build the U-Boot tools
git clone https://source.denx.de/u-boot/u-boot.git
cd u-boot
make xconfig
# Apply the appropriate options needed for your specific project. The defaults are sufficient for signing.
make tools
# If prompted for a SYS_LOAD_ADDR or TEXT_BASE value, enter the appropriate addresses for your project.
# If uncertain, enter 0x0 and press Enter.
Step 3: Find the URL to use for signing
# Find the relevant token
p11tool --list-tokens
# Find the relevant private key URL
p11tool --login --list-privkeys "pkcs11:model=TPP;manufacturer=Venafi%2C%20Inc.;serial=1234;token=Remote%20Token"
Step 4: Create the .its file that defines the image
In this example, the file should be named kernel.its.
/dts-v1/;
/ {
description = "My Linux kernel";
#address-cells = <1>;
images {
kernel {
description = "My Linux kernel";
data = /incbin/("./vmlinuz-linux");
type = "kernel";
arch = "x86_64";
os = "linux";
compression = "gzip";
load = <00000000>;
entry = <00000000>;
hash-1 {
algo = "crc32";
};
hash-2 {
algo = "sha1";
};
signature {
algo = "sha256,rsa4096";
};
};
};
configurations {
default = "config-1";
config-1 {
description = "Boot Linux kernel";
kernel = "kernel";
};
};
};
Step 5: Copy a kernel image into the current directory to use in the image
In this example, the kernel from the Linux computer you currently booted from is used. In a real-world situation, use your boot image. The filename must match the filename referenced in the .its file.
cp /boot/vmlinuz-linux .
Step 6: Create and sign the image
The signature section in the .its file must match the key being used:
./mkimage -f ./kernel.its -k "model=TPP;manufacturer=Venafi%2C%20Inc.;serial=1234;token=Remote%20Token;id=%54%65%73%74%69%6E%67%2D%52%53%41%34%30%39%36;object=Testing-RSA4096;type=private" -N pkcs11 -r kernel.itb
Step 7: List the header information for the new image to verify the signature value
./mkimage -l kernel.itb
Related information
The mkimage tool has many command-line parameters. Here is the output from its usage help for reference:
Usage: ./mkimage -l image
-l ==> list image header information
./mkimage [-x] -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image
-A ==> set architecture to 'arch'
-O ==> set operating system to 'os'
-T ==> set image type to 'type'
-C ==> set compression type 'comp'
-a ==> set load address to 'addr' (hex)
-e ==> set entry point to 'ep' (hex)
-n ==> set image name to 'name'
-d ==> use image data from 'datafile'
-x ==> set XIP (execute in place)
./mkimage [-D dtc_options] [-f fit-image.its|-f auto|-F] [-b <dtb> [-b <dtb>]] [-E] [-B size] [-i <ramdisk.cpio.gz>] fit-image
<dtb> file is used with -f auto, it may occur multiple times.
-D => set all options for device tree compiler
-f => input filename for FIT source
-i => input filename for ramdisk file
-E => place data outside of the FIT structure
-B => align size in hex for FIT structure and header
Signing / verified boot options: [-k keydir] [-K dtb] [ -c <comment>] [-p addr] [-r] [-N engine]
-k => set directory containing private keys
-K => write public keys to this .dtb file
-G => use this signing key (in lieu of -k)
-c => add comment in signature node
-F => re-sign existing FIT image
-p => place external data at a static position
-r => mark keys used as 'required' in dtb
-N => openssl engine to use for signing
./mkimage -V ==> print version information and exit
Use '-T list' to see a list of available image types
For more information, see the U-Boot documentation.