Zephyr Project member David Brown, a Senior Engineer with Linaro Ltd., shares the best practices for security in this blog post, which first ran on Brownian Motion.
This is the first in what I hope to be a series of posts about the MCUboot bootloader from a security perspective. Please note that although I work in security, I am by no means a cryptographer. I appreciate any feedback on any and all flaws in my analysis.
The MCUboot project
The MCUboot Project is a secure bootloader for 32-bit MCUs. The goal of MCUboot is to define a common infrastructure for the bootloader, system flash layout on microcontroller systems, and to provide a secure bootloader that enables easy software upgrade.
The essential problem that MCUboot seeks to solve is how to allow firmware updates, while still maintaining some kind of integrity and control over what firmware can be run on the device. The easiest way to prevent unauthorized firmware from running on a device is to configure the flash to be immutable. Unfortunately, this prevents potential security updates (as well as functionality improvements). MCUboot solves this by itself being a small amount of code that can be placed in an immutable section of flash. It then can verify the main code before allowing it to execute, as well as control updates to that code.
MCUboot is configurable, and these configuration choices affect the security promises that MCUboot is able to make.
CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256control the type of digital signature MCUboot uses to verify the image. It is possible to define neither of these, and it will only ensure that there is a SHA256 hash present. With neither signature algorithm, the image is not protected against intentional tampering, but merely acts as a checksum, to prevent a corrupted image from booting.
CONFIG_BOOT_VALIDATE_SLOT0 controls whether MCUboot validates the signature of the booted image on every boot (or just when upgrading). This can be disabled to make the boot process slightly faster, at the cost of no longer verifying a path of trust to the running image.
CONFIG_BOOT_UPGRADE_ONLY controls how upgrades are performed. If this is unset, MCUboot uses a (complex) swapping algorithm to exchange the new upgrade image with the old image. This will allow an easy revert if the system determines that the image will not boot. If this is enabled, the new image will overwrite the old image, and if it doesn’t boot, there is no fallback image.
From a security perspective, I will assume that one of the signature options is set, and that SLOT0 is validated. In a future post, I will discuss the upgrade only option, and demonstrate why neither of these is a good solution, from a security perspective. It will offer a suggestion for how to better handle reverts.
MCUboot assumes that it is running on a small 32-bit microcontroller where the code executes directly out of flash (eXecute In Place, or XIP). Although support for non-XIP targets is desirable, it has not been implemented at the time of writing.
MCUboot divides flash into several regions. We will refer to them as B for the bootloader area, P for the primary region, U for the upgrade region, and S for the scratch region. There are constraints on the layout and sizes of these partitions which are not relevant to our analysis here. We make the following assumptions of this flash memory:
- The flash memory is internal to the MCU itself. External flash memory is easier for a malicious party to modify.
- The B region can be permanently marked as immutable. Some MCUs will still allow a full-chip erase, which would allow a determined party to replace the entire firmware with their own.
- The code at B is the first configurable code that the MCU will run. The MCU may have internal boot code, however if it can be configured to support some type of recovery process, this must be permanently disabled (to prevent arbitrary code from running).
- JTAG and SWD debugging ports will be disabled in production devices. Limited debugging can be permitted as long as the B region cannot be tampered with.
MCUboot (the code in B) verifies the image in P before running it. This image begins with a small header Hd, the executable image itself, Ex, and a set of metadata after the image, M1, M2, etc. The format of the payload then looks like (Hd||Ex||M1||M2||…).
Each piece of metadata is in tag-length-value format, with the following tags:
IMAGE_TLV_SHA256: The associated data is the SHA256 hash of (Hd||Ex). This tag is required in all configurations.
IMAGE_TLV_KEYHASH: The associated data is the SHA256 hash of a public key. This is used to indicate what public key a following signature uses.
IMAGE_TLV_RSA2048_PSS: A digital signature made with RSA-2048 PKCS v2.1 PSS. The signature is made with the same hash as
IMAGE_TLV_ECDSA224: A digital signature of the hash made with ECDSA using the NIST P-224 curve.
IMAGE_TLV_ECDSA256: A digital signature of the hash made with ECDSA using the NIST P-256 curve.
If MCUBOOT has been configured to support one of the signature types, then at the following tags must be present:
IMAGE_TLV_KEYHASH, and one signature that matches the signature type compiled into the code. There can be more than one pair of keyhash and signature pairs present, and the bootloader will ignore any that it doesn’t understand.
MCUboot will reject the image if the metadata does not meet these requirements.
Upon booting, MCUboot examines P, U, and S to determine its internal state. If it detects it was in the middle of an operation, it will try to continue that operation. If U is valid, and is marked to indicate an upgrade, it will begin the upgrade process. This will either copy U onto P entirely, or use a swap algorithm using S to exchange the contents of P and U (depending on configuration).
If P contains a valid image, MCUboot will transfer control to the execution address of this image (indicated in a CPU specific manner; for Arm, the address and initial stack pointer are at the beginning of Ex).
If none of this is true, MCUboot will stop to prevent malicious code from running.
Stay tuned for more:
- Security implications of not verifying SLOT0.
- Security implications of not using a digital signature.
- Deeper dive into “swap”, its complexities, and some discussion of an easier idea.
For more blogs from David, visit his personal webpage here.