Signed FIT Image Support for meta-oe (fitimage.bbclass)
FIT images provide a versatile way to bundle components used to boot embedded Linux systems, like the kernel, device tree, ramdisk, firmware, etc. They also support cryptographic signatures, making them ideal for verified boot setups. This blog post introduces the new fitimage.bbclass in meta-oe as an alternative to oe-core's kernel-fitimage.bbclass, with a clear focus on enhancing both versatility and signing support.
While the ROM loader verifies the first-stage bootloader, and the root filesystem's integrity is often verified using the dm-verity kernel device mapper, the FIT image bridges the gap between these stages. Bootloaders like barebox or U-Boot support loading and verifying the components defined in the FIT image. This process typically involves starting a kernel with the appropriate device tree and booting into an initramfs, which then sets up the dm-verity device mapper.
For more information about verified boot, see Rouven Czerwinski's Talk at ELC-E 2024 for example.
The FIT Image Format
The FIT image definition originates from U-Boot but has since seen wider adoption and moved to the Open Source Firmware Foundation.
The FIT image format closely utilizes the device tree format and the device tree library and compiler (dtc). Its source format is an .its file, analogous to a .dts file for device trees, while its compiled format is an .itb file, similar to a .dtb for device trees. An .its file defines images and configurations where an image is typically a binary artifact like the kernel, the dtb, or the initramfs and a configuration describes a certain combination of images. The standard way to compile a FIT image is to call the mkimage tool.
The FIT specification also supports storing hashes of the images to allow checking their integrity. It also supports signing these hashes with a private key and storing the resulting signature in the FIT. During boot, bootloaders like barebox or U-Boot can then cryptographically verify all components contained in the FIT (including the configuration itself).
Why a New FIT Image Class?
Those familiar with FIT images and OE might wonder whether there isn't already a class for FIT image generation with kernel-fitimage.bbclass.
The answer is, "yes, but with several limitations" (which are also listed in the class' documentation).
The most notable aspect of the kernel-fitimage.bbclass is that it is tightly coupled to the kernel recipe and is intended to extend the kernel recipe using KERNEL_CLASSES. This not only requires the inclusion of a kernel, but also limits support to a single kernel. Additionally, the class is closely tied to U-Boot, containing many U-Boot-specific variables and assumptions, which makes it challenging to use with other bootloader environments.
The new meta-oe fitimage.bbclass was designed with the versatility of FIT images and their various use cases in mind. It comes with no U-Boot-specific variables and is designed to be used from a separate recipe. This does not only allow creating multiple FIT images for a single kernel, it potentially also allows adding none or multiple kernels and other artifacts. It also well cooperates with the meta-oe signing class.
The new fitimage class was initially developed by Phytec and maintained in meta-phytec. When Pengutronix required a more adaptable FIT image class, the Phytec implementation — despite some limitations — provided a solid foundation. My colleagues and I refined the class over several iterations to fix bugs, make it more 'pythonic', and eliminate traces of earlier classes that had clearly served as inspiration.
We then submitted it to the meta-oe GitHub repository as a Pull Request , which, thanks to Khem Raj, was quickly accepted and merged.
Basic Usage
To create a simple FIT image using the fitimage.bbclass, start by creating a recipe (e.g. named simple-fit-recipe.bb) that inherits the class.
Next, define the images that should land in the FIT image. For each image, add a (freely choosable) name to the (space-separated) list in FITIMAGE_IMAGES. For each name in this list, specify its source by setting FITIMAGE_IMAGE_<image-name> to the recipe providing the artifact, and use the [type] varflag to indicate the recipe type:
inherit fitimage
FITIMAGE_IMAGES = "kernel devicetree initramfs"
FITIMAGE_IMAGE_kernel = "linux-yocto"
FITIMAGE_IMAGE_kernel[type] = "kernel"
FITIMAGE_IMAGE_devicetree = "linux-yocto"
FITIMAGE_IMAGE_devicetree[type] = "fdt"
FITIMAGE_IMAGE_initramfs = "initramfs-image"
FITIMAGE_IMAGE_initramfs[type] = "ramdisk"
Based on the chosen type, the class has some heuristic about what tasks to depend on and where and how artifacts generated by the class are stored. For cases where this is not sufficient or where the recipe deviates from the standard, other varflags like [file], [name], [fstype] etc. allow fine-tuning.
Currently supported types are:
- kernel: a kernel from a kernel recipe (inheriting kernel.bbclass)
- fdt: devicetree(s) from a kernel (based on KERNEL_DEVICETREE) or a devicetree recipe (inheriting devicetree.bbclass)
- ramdisk: an initramfs image from an image recipe (inheriting image.bbclass)
- bootscript: a boot script (like u-boot.scr) (from the deploy directory)
- fdto: like fdt, but for device tree overlays
- fdtapply: merge a base dtb and some dtbo's together to a new dtb
The load address or entry point values, as e.g. required by some bootloaders like U-Boot, can be set via bootloader-agnostic variables like FITIMAGE_LOADADDRESS, FITIMAGE_ENTRYPOINT, etc.
For a more detailed documentation, consult the class' blurb.
Signing Support
The key material used for signing is sensitive and should not be stored and handled in simple files. A common way to address this problem is to use an HSM (Hardware Security Module), which provides indirect access to the key material via PKCS#11 (as per RFC 7512). This ensures the key remains protected and cannot be directly copied or misused.
Support for generic PKCS#11-based signing is available in meta-oe through the signing.bbclass. This class allows for secure integration of hardware-backed or software-emulated HSMs into the signing process, making it suitable for both development and production environments. By using the signing class, FIT images can be signed securely, protecting the authenticity and integrity of system components.
To use the signing class for FIT signing, enable this by setting FITIMAGE_SIGN and configure mkimage to handle PKCS#11 URIs:
FITIMAGE_SIGN = "1"
FITIMAGE_MKIMAGE_EXTRA_ARGS = "--engine pkcs11"
FITIMAGE_SIGN_KEYDIR = "${PKCS11_URI}" # "${PKCS11_URI#pkcs11:}" for older mkimage version
Then simply inherit the signing class, depend on your key provider (abstracted by virtual/fit-signing here), and prepend the do_fitimage task with the class's utility methods for preparing and using the appropriate key role ("fit") here.
inherit signing
DEPENDS += "virtual/fit-signing"
do_fitimage:prepend() {
signing_prepare
signing_use_role "fit"
}
Building the FIT image
To finally build the FIT image, run:
$ bitbake simple-fit-recipe
To inspect and debug the generated .its file, you can check the logs in temp/log.do_fitimage as shown in the right example.
Other recipes can use the generated FIT image by depending on the recipe's do_fitimage task and consuming the image from the DEPLOYDIR.
Contribute
If you have ideas or improvements that can help enhance the functionality of the class, please feel free to contribute. We are looking forward to the class addressing more use cases and make the class even more versatile for the community.
Further Readings
Bringing Barebox into OE-Core (Yocto)
This blog post chronicles the multi-year journey to get Barebox accepted into OE-Core—from the early attempts to the eventual success in October 2024. Along the way, we’ll explore the technical hurdles we faced, the community discussions that shaped the process, and the improvements we added to both OE and Barebox.Tutorial: Start With RAUC Bundle Encryption Using meta-rauc
In its current master branch, RAUC now supports encrypted Bundles. This tutorial will introduce you to the basics of using encryption in RAUC and show how to use it in a simplified Yocto setup with the meta-rauc Layer.
Tutorial: Evaluating RAUC on QEMU - A Quick Setup With Yocto
RAUC is an update framework for safely deploying verified updates on your embedded Linux devices. It ensures atomicity of the update process to protect from sudden power outages, hardware failures, etc. So, why would one like to run RAUC on an emulated platform?
ELC Europe 2016, Berlin
At the ELC Europe 2016 in Berlin our colleagues Jan Lübbe and Marc Kleine-Budde are talking about two interesting and important presentations about Kernel longterm maintenance strategies and verified boot.