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.
A conversation I keep having
"Is the device secure?"
"It does secure boot."
"So the firmware is protected?"
And there it is. Two sentences in and we're already talking past each other, because "protected" means about four different things and I've only answered one of them.
I've been building the secure boot chain for this STM32MP157F gateway for a couple of weeks now, and the thing that's become clearest isn't technical. It's that security isn't a feature you add. It's a set of separate promises, and you have to say which ones you're making.
The four questions
Every one of these is a different promise, with different machinery, and you can have any combination of them:
Is this software really mine? That's authenticity. Someone can pull the SD card out of my gateway, write their own bootloader, and put it back. Now they own it, and from the outside it looks completely normal. Signatures fix this.
Has it been changed? Integrity. Related, but not the same. A signature proves who wrote something at the moment they signed it. Integrity is about catching changes afterwards — including to things that aren't bootloaders, like a root filesystem that's too big to hash at every boot.
Can anyone read it? Confidentiality. And here's the one that catches everybody:
Secure boot does not hide your firmware. At all.
Anyone with my SD card can read my bootloader, my kernel, my application. Disassemble them. Post them online. Nothing bad happens to the security of the boot chain, because that was never what it was for. Secure boot proves the firmware is mine. Hiding it is a completely separate feature with a separate key, and I'm not building it.
I've watched this assumption cause real confusion. Someone enables secure boot, then discovers their firmware is readable, and concludes secure boot is broken. It isn't. It's doing exactly what it says.
Which device is this, and may it talk to us? Identity. And this one took me longer to properly separate in my head than I'd like to admit.
The one I had backwards
Here's what I got wrong for a while.
I'd been thinking of the PKH — the hash of my public key that gets burned into the chip's fuses — as the device's identity. It's in the silicon, it's permanent, it's unique to...
...no. It's not unique to anything. Every single gateway I ever build with that key has the same PKH. Ten thousand devices, one fingerprint. It identifies the signing authority, not the unit.
So when the gateway connects to my MQTT broker over mTLS and the broker asks "which gateway are you?", the PKH is no help whatsoever. That needs the device to have its own private key and its own X.509 certificate, unique to it, issued during manufacturing.
Two completely different layers. Secure boot asks "may this software run?" Device identity asks "who is this box?" Neither answers the other's question.
Three private keys, and don't mix them up
Once I saw that, a lot of muddle cleared up. There are three different private keys in a product like this, and conflating any two of them in a design meeting is how you end up shipping something you really shouldn't:
The firmware signing key. Lives in a signing HSM. Signs bootloaders. Never goes near a gateway. If it leaks, every device that trusts it will boot an attacker's firmware forever — and on this chip there's no revocation.
The bootstrap credential. Lives on the gateway. Proves it's a genuine unit from my line, so it's allowed to ask for a real certificate. That's all it can do.
The device identity key. Lives on the individual gateway, generated by the gateway, and never leaves it. This is what does mTLS. If it leaks, one device is impersonated — bad, but bounded, and revocable.
That last word matters more than it looks. The firmware trust anchor can never be revoked. A device certificate can be revoked this afternoon. Which suggests an obvious design principle I hadn't articulated before: put the things that might need revoking in the layer that supports revoking them.
Why the device makes its own key
Small detail, big consequence. When the gateway enrols, it generates its own key pair on the device, then sends a CSR — a certificate signing request — containing only the public half.
The reason: a key that was generated somewhere else has, at some point, existed somewhere else. There's a copy. Maybe on a provisioning laptop, maybe in a log, maybe in a backup nobody thought about. If the device makes it and never exports it, there's nothing to steal, and possession of the key is the proof of identity.
The layer cake
This is roughly how I think about it now, bottom to top:
- ST's ROM — immutable, in the silicon. The thing everything else hangs off.
- TF-A BL2 — my first bootloader. Signed, checked against the fuses.
- The FIP — U-Boot and the secure monitor. Checked by BL2.
- The kernel — checked by U-Boot.
- The root filesystem — checked by Linux, block by block as it's read.
- The application — and here is where device identity and mTLS live.
Trust flows upward. Each layer vouches for the one above it. And the chain is only as strong as its first unverified link — if the bottom is perfect and the kernel check is missing, someone replaces the kernel and wins.
What none of it does
Worth being blunt, because this is where the real incidents happen:
It doesn't protect against bugs in my own running code. Secure boot guarantees I started from known-good software. A remote code execution flaw five minutes later is completely unaffected. Most breaches are this, not clever boot-chain attacks.
It doesn't stop rollback. An attacker can install an older, validly signed firmware with a known hole in it. Blocking that needs monotonic version counters, which on this chip are fuses.
It doesn't survive a leaked signing key. One leak, one fleet, forever.
It doesn't protect the supply chain. A perfectly signed build of compromised source is still compromised.
So how do you decide?
Not by turning everything on. The order that seems to actually work:
- Write down the threat model. Who's the attacker, what do they want, what can they physically touch? A box in a locked cabinet is a different problem from a gateway on a pole in a field.
- Pick the promises that address it. Physical access in scope? You need authenticity and integrity all the way up. Talking to a cloud service? You need identity regardless of anything else.
- Build from the bottom. An unverified bootloader makes everything above it pointless.
- Write down what you skipped, and why. This is the bit everyone forgets and the first thing an auditor asks for.
- Test the failures, not the successes. "It boots" proves nothing. Corrupt the image on purpose and confirm it refuses to boot. That's the evidence.
I still haven't written my threat model down. I'm aware of the irony. It's next.
Next up: what secure boot actually is under the hood — signing versus encrypting, why the chip only ever needs public keys, and how 32 bytes of one-time-programmable memory solve a chicken-and-egg problem that otherwise makes the whole thing impossible.