Secure boot and a verified root filesystem on an STM32MP157F-DK2

Four links of an authenticated boot chain on an STM32MP157F-DK2 — ROM, TF-A, U-Boot FIT and dm-verity — built, signed, broken on purpose and put back, with the one-time-programmable fuses left completely blank the whole way through.

The STM32MP157F-DK2 on the bench — ST-LINK, Ethernet and the USER buttons along the front edge
The STM32MP157F-DK2 on the bench — ST-LINK, Ethernet and the USER buttons along the front edge

Secure boot is usually taught as a checklist that ends in "now blow the fuses". This is the opposite series: the fuses stay blank the whole way through, and that turns out to be the interesting constraint rather than a limitation.

The board is an ST STM32MP157F-DK2 — a development kit anyone can buy, not an industrial gateway. Everything here is reproducible on one.

Check the letter before you buy. STM32MP15 comes in four variants and only C and F carry the security features — the secure boot ROM, the fused key hash, the closed-device bit. A and D do not. So the STM32MP157D-DK1 cannot do link 1 at all, and parts of this series will not reproduce on it. Mine is the F, on a DK2.

Links 2, 3 and 4 are software and work on any of them — which, given that this series never fuses anything, is most of what is here. But the first link needs the right silicon.

"Secure boot" on this platform is not one thing, it is four:

Link What verifies what Needs fuses?
1 The ROM verifies TF-A's BL2 against a fused key hash Yes — so not enforced here
2 BL2 verifies the secure monitor and U-Boot against a certificate chain in the FIP No
3 U-Boot verifies the kernel via a FIT image signature No
4 Linux verifies the root filesystem with dm-verity No

A note on "verified", not "signed". The root filesystem is never signed. dm-verity hashes it into a Merkle tree and checks each block as it is read; what gets protected is the single root hash at the top of that tree. That hash is then compiled into the kernel's own command line with CONFIG_CMDLINE_FORCE=y, so it travels inside an artifact that link 3 does sign — and the kernel ignores whatever the bootloader tries to pass it.

So the chain is: a signature protects the kernel, the kernel carries the root hash, the root hash judges the filesystem. Changing the filesystem means changing the hash, which means signing a new kernel. Calling that "a signed rootfs" is shorter and wrong, and the difference is exactly the kind of thing this series is about.

Three of the four are software, and three of the four refuse for real on completely blank silicon — genuine failures, genuine error messages, nothing risked. The fourth can be built, signed, parsed and verified by hand right up to the point of writing 32 bytes into one-time-programmable memory, and then not written.

That is roughly 90% of the work and 100% of the understanding, at 0% of the risk.

Along the way: a bit flipped in a 52 MB filesystem and a board that refuses, two U-Boot console messages that were simply untrue, an escape hatch that turns kernel signing into theatre if you leave it enabled, and three occasions where the test lied to me before the hardware did not.

Part 1
8 min

Why I am not burning the fuses on an STM32MP157F-DK2

Secure boot on this chip comes down to 32 bytes in one-time-programmable memory, and a bit you can never un-blow. I started the project with a rule forbidding its own last step — and three of the four links in the chain turn out to enforce for real on completely blank silicon.

Part 2
6 min

Four different things people call “security” on an embedded device

Authenticity, integrity, confidentiality and identity are four separate problems with four separate answers. Three private keys that must never be mixed up, why the firmware trust anchor does not identify a device, and the one I had backwards.

Part 3
9 min

What secure boot actually is on an STM32MP1: the header, field by field

Signing is not encrypting — the thing burned into silicon is public and you could print it on a billboard. The chicken-and-egg the 32 bytes solve, the STM32 header field by field, four locks wearing one name, and the two things I got wrong and had to correct.

Part 4
7 min

Signing BL2 and the FIP tamper test: one bit flipped, EAUTH, clean halt

TF-A verifying the next stage against a certificate chain inside the FIP — software, on blank fuses, refusing for real. Flipping one bit produced EAUTH on BL33 and a clean halt. Plus the honest limits of what that proves.

Part 5
11 min

Signing the kernel with a FIT image, and the escape hatch that makes it theatre

You cannot sign a zImage. Why the signature goes on the FIT configuration rather than the kernel, the escape hatch that turns the whole thing into theatre, why the negative test needed two tampered images and not one, and the three ways the test lied to me.

Part 6
9 min

dm-verity on a signed rootfs, and two console messages that were simply untrue

You cannot put a filesystem's hash inside that filesystem. Two U-Boot messages that were lies — “press any key to stop autoboot” when no key worked, and quotes vanishing from the kernel command line. Four attempts, three instructive failures, and a hardware SHA engine I did not know I was using.

Part 7
5 min

Breaking dm-verity on purpose: one bit in 52 MB, and the board refuses

The negative test that proves the chain is real: flip a single bit in a 52 MB root filesystem, get `data block 0 is corrupted`, and watch the board refuse. Why you cannot sabotage your own firmware the two obvious ways.

Part 8
6 min

Moving the kernel out of the rootfs, and feeding the bootloader garbage

The hash of a filesystem cannot live inside it, so the kernel had to move somewhere signed. Then the two-command test: hand the bootloader rubbish and watch the kernel ignore it entirely. What that cost, and where the chain now ends.