Secure boot on microcontrollers: chain of trust, keys and anti-rollback
Secure boot guarantees that a microcontroller only runs code whose origin is proven. Most implementations fail not on the cryptography, which is the easy part, but on what surrounds it: where the public key lives, who can replace it, and what happens when someone installs an older version.
The chain of trust, stage by stage
The principle is a recursion: each stage verifies the next before handing over control.
Stage 0, the root of trust. Code in masked ROM, fixed when the silicon was manufactured, so physically unmodifiable. It is the one link you cannot verify; you trust it by construction. It holds, or knows how to find, the hash of the product manufacturer's public key.
Stage 1, the bootloader. Often MCUboot on Cortex-M. It is verified by the ROM, and in turn verifies the application.
Stage 2, the application. Signed, verified before execution.
The weak link is never the signature itself. It is the storage of the public key hash: if an attacker can replace it, they sign whatever they want and the whole chain becomes decorative.
Where the public key lives, and why that is the real question
The public key hash must be immutable after provisioning. Three mechanisms exist depending on the silicon.
OTP memory, one-time programmable: fuses you burn once. It is the strongest mechanism, and it is final. A provisioning mistake turns the board into a paperweight.
Locked option bytes, on STM32 for example, with a readout protection level that then forbids modification.
A flash region write-protected by the memory controller. This is the weakest option: it relies on software configuration, therefore on the absence of a flaw in the code that applies it.
You never store the full public key in OTP, only its SHA-256 hash, to save fuses. The full key lives in the firmware header, and the ROM checks that its hash matches.
The private key must never leave a hardware security module or a signing service. A private key in a Git repository, even a private one, compromises the whole product range for the rest of its support life.
Anti-rollback, the most common omission
Without rollback protection, your secure boot collapses the first time you publish a patch.
The scenario: you fix a critical vulnerability in version 1.4. An attacker takes version 1.3, which is signed with your real key, reinstalls it, and exploits the fixed flaw. The signature is valid; the chain of trust sees nothing wrong.
The countermeasure is a monotonic version counter stored in non-reversible storage, usually dedicated OTP fuses. The bootloader refuses any image whose security counter is below the stored value.
Two cautions when implementing it. The security version number must be separate from the product version: increment it only on a security fix, otherwise you exhaust the available fuses within two years. And the counter must only be advanced after confirming that the new image boots and works, never before, otherwise a failed update permanently blocks the return to a working version.
Updating: dual bank or swap
The CRA requires an update mechanism. Two architectures dominate.
Dual bank: two equally sized slots, one active, the other receiving the new image. The bootloader switches after verification. Simple, robust, instant rollback, but it costs twice the firmware size in flash.
Swap with a scratch area: MCUboot exchanges the contents using a spare sector, journalling each step so it survives a power cut mid-swap. Cheaper in flash, considerably more delicate, and flash wear increases since every update rewrites both slots.
In both cases, plan for the boot test: the new image is marked tentative, must confirm itself after a successful boot, and otherwise the bootloader reverts. Without that mechanism, a faulty update rolled out to the whole fleet is unrecoverable without physical access.
Debug ports, to close at the right moment
A perfect chain of trust is worthless if the SWD or JTAG port stays open: read the flash, write to RAM, bypass everything.
Disabling it is usually irreversible, which raises a real industrial problem: no more failure analysis on boards returned from the field. Intermediate levels, such as RDP level 1 on STM32, allow debugging but forbid reading the flash, at the cost of a full erase on any attempt to return to open mode.
That is a trade-off between security and maintainability, to settle before production, not after.
The mistakes that cancel everything
| Mistake | Effect |
|---|---|
| Key hash in unlocked flash | The attacker replaces the key and signs anything |
| No anti-rollback | Signed vulnerable version can be reinstalled |
| Private key outside an HSM | The whole product range is compromised |
| Debug port open in production | The chain is bypassed entirely |
| Verifying after copying to RAM | Attack window between copy and check |
| Bootloader missing from the SBOM | Bootloader vulnerabilities invisible to monitoring |
| Counter advanced before confirmation | No rollback possible after a failure |
The fifth row deserves a note. Verifying the signature of an image already loaded into RAM leaves an interval during which a fault injection attack, a glitch on the supply rail for instance, can alter the content between the check and execution. Serious implementations verify in place, then re-check critical points after the jump.
References
- MCUboot documentation, swap and overwrite modes, anti-rollback security counter
- Arm, Platform Security Model, root of trust and verification chain
- STMicroelectronics, Introduction to STM32 security, AN5156, RDP levels and option bytes
- NIST, SP 800-193, Platform Firmware Resiliency
Going further
These mechanisms become concrete once you have provisioned a real board, burned fuses, and discovered that a provisioning mistake cannot be undone. That is what our secure embedded development and advanced embedded security courses cover.