Article 07 assumed failures were accidental. Rails dip, peripherals glitch, bits flip, and the architecture responds. Secure boot starts from the opposite assumption.
An attacker is a different kind of fault source. They read your recovery documentation and pick the path that helps them. Your bounded retry limit becomes a denial of service. Your diagnostic detail becomes an oracle. Your recovery mode becomes the way in, because it’s the smallest firmware with the fewest checks and it was written in a hurry.
So the question changes from “what happens when this fails” to “what happens when someone makes this fail, deliberately, repeatedly, with the device on their bench.”
This article covers what makes firmware trustworthy: the root of trust, what secure boot actually verifies and what it costs in milliseconds, key hierarchy and rotation across a ten-year life, anti-rollback and the physical budget it consumes, and debug lock decisions that can’t be undone. Article 12 covers getting a new image onto the device safely — different problem, and the two get conflated constantly.
The Attack Surface You Actually Have
Threat modelling in the abstract produces generic advice. Here’s the KIC-400’s real surface, which is more instructive:
| Interface | Reachable by | What it gets them | Realistic control |
|---|---|---|---|
| Modbus TCP :502 | Anyone on the plant network | Read and write registers | None at the protocol level. Segment the network. |
| Modbus RTU (RS-485) | Physical access to the bus | Same, plus bus disruption | None. Physical security. |
| MQTT over TLS | Network path to the broker | Telemetry, remote commands | Mutual TLS, per-device identity |
| Update endpoint | Network | Code execution, persistence | Signature verification, anti-rollback |
| SWD header | Physical access | Firmware readout, key extraction | RDP level, and it’s a hard choice |
| External EEPROM | Physical access, I2C probe | Configuration, any stored secret | Don’t store secrets there |
The first two rows are the honest part, and most security write-ups skip it: Modbus has no security. Not weak security — none. No authentication, no integrity, no confidentiality, in either RTU or TCP form. Anyone who can reach port 502 can write any holding register.
You cannot fix that in firmware. What you can do is state it plainly in your documentation, refuse to let Modbus writes reach anything safety-relevant without a second channel, and design assuming the fieldbus is hostile. A product that claims Modbus TCP is “secured” because the device does secure boot has misunderstood what secure boot is for.
That distinction matters architecturally. Secure boot protects what executes. It does nothing about what a running, authentic image accepts.

Root of Trust: What “Immutable” Actually Means
Every chain of trust terminates somewhere an attacker can’t change. If it terminates somewhere they can, the whole structure is decorative.
On an STM32H7, the candidates and what each is worth:
Option bytes with the security bit set. The MCU boots into a defined secure area rather than the reset vector, and that area is protected from erase. Changing it requires an unlock sequence that triggers a mass erase.
Write-protected flash sectors (WRP). The bootloader’s sectors can’t be written or erased while protection is active.
PCROP (proprietary code readout protection). Marks a region execute-only — readable by the CPU as instructions, not as data. Useful for keeping key material or verification code from being dumped by a firmware image that’s authentic but compromised.
OTP. One-time-programmable storage, where the part has it. Bits go from 1 to 0 and never back, which makes it the natural home for a monotonic security counter, the subject of the anti-rollback section below. Check your exact part: some H7 variants, such as the H7A3/H7B3, have an OTP area, but the H743 in the KIC-400 does not. On the H743 the counter needs another home, such as the secure element below or a flash sector that only the protected bootloader can write, used append-only.
A separate secure element. An external chip that holds keys and performs operations without ever exposing the private material. Adds a BOM line and an I2C bus, and buys you key storage the MCU genuinely cannot leak.
The architectural question is which of these your part supports and which you’ve actually enabled, and those are frequently different answers. A product with a beautiful signature verification routine, executing from flash that’s readable over SWD because RDP was never raised, has a root of trust an attacker can edit.

One thing to be clear about, because the assumption is widespread: Cortex-M7 has no TrustZone. TrustZone-M is a Cortex-M33 feature — STM32L5, U5, H5. On an H7 your isolation primitives are the MPU and the privileged/unprivileged split, and they’re weaker. Plan accordingly rather than discovering it during a security review.
Integrity, Authenticity, and Why CRC Isn’t Either
Briefly, because this is table stakes and the series has already used CRCs for their proper purpose.
A CRC answers “did this change by accident.” It’s excellent at that, cheap, and article 02 uses it on configuration for exactly that reason.
It answers nothing about deliberate change, because CRC32 is trivially invertible — modify the image, compute the new CRC, write it in. A hash answers “did this change,” including deliberately, but only if the stored hash is itself protected. A signature answers “did this come from someone holding the private key,” which is the question that matters, and it’s the only one of the three that survives an attacker who can rewrite the whole flash image including its metadata.
So: CRC for accidental corruption, signature for authenticity, and never substitute one for the other.

What Secure Boot Actually Verifies
A signature check is the headline, but a bootloader that only checks a signature will happily install a correctly signed image built for a different board revision.
The full check list, in the order you want it, because each cheap check that fails saves you an expensive one:
1. Header magic and structure valid ~free
2. Image size within slot bounds ~free
3. Hardware revision compatible ~free
4. Security version ≥ stored minimum ~free ← anti-rollback
5. Vector table points inside the slot ~free
6. SHA-256 over the image expensive
7. ECDSA verify of the signature over hash moderate
Check 5 is worth calling out. An image whose reset vector points outside its own slot is either corrupt or hostile, and checking it costs two comparisons. Skip it and a partially-written image can transfer control somewhere arbitrary before anything else notices.
And what it costs
Nobody puts numbers on this, and it’s a boot-time budget item.
Hashing dominates. Software SHA-256 on a Cortex-M runs in the region of 25–40 cycles per byte, so a 1 MB image at 480 MHz is roughly:
1,048,576 bytes × ~30 cycles = ~31 M cycles ÷ 480 MHz ≈ 65 ms
Parts in the family with a hardware HASH peripheral, such as the H753, cut that substantially, because hashing becomes bound by bus throughput rather than the core. The H743 in the KIC-400 has no HASH or CRYP peripheral, so it pays the software figure. ECDSA P-256 verification is a few million cycles in software, so call it 5–20 ms depending on your library and curve optimisations. Ed25519 verification is typically faster than P-256 if you’re free to choose.
So secure boot on this product costs somewhere around 70 to 85 ms, and closer to 15 ms on a part with hardware hashing. Measure yours — these are order-of-magnitude figures to size a budget, not numbers to quote.
That has a real architectural consequence. If your product has a boot-time requirement — an industrial controller that must resume control within a defined window after a power blip — 85 ms of hashing is a line item you have to plan for, and discovering it late means either dropping verification or missing the requirement.

The tempting shortcut, and its cost. Verify on update, store a “verified” flag, and skip the hash on every subsequent boot. Boot time drops to a few milliseconds. What you’ve done is move your trust from the signature to the flag — so now the flag has to live somewhere an attacker can’t write, which usually means OTP or a protected sector, and you’ve traded a cryptographic guarantee for a storage-protection one. Defensible on a part with strong write protection and a fast boot requirement. Indefensible if the flag sits in ordinary flash.
Key Hierarchy, and Rotation Across Ten Years
The most common key mistake isn’t weak crypto. It’s one key doing four jobs.
| Key | Purpose | Where the private half lives | Rotatable? |
|---|---|---|---|
| Firmware signing | Proves an image is yours | HSM, offline, in your building | Hard — see below |
| Device identity | Proves this unit is yours | On the device, ideally never exported | Per-device, via re-provisioning |
| TLS client | Authenticates to the broker | Derived from or same as identity | Yes, with a provisioning path |
| Storage encryption | Protects data at rest | Derived from a device-unique secret | Yes, with re-encryption |
Separation limits blast radius. A compromised storage key exposes one device’s configuration. A compromised signing key means anyone can produce firmware your entire fleet accepts, which is an unrecoverable event for products that have already shipped — and that asymmetry is why signing keys live in an HSM and never on a build machine.

The rotation problem nobody plans for
Here’s the ten-year question. Your signing key is compromised in year six. What now?
If the public verification key is burned into OTP or baked into an immutable bootloader, you cannot rotate it. Every fielded device will verify against a key an attacker now controls, for the rest of the product’s life, and the only fix is physical access to every unit.
Three designs that keep the option open, in ascending cost:
Multiple key slots. Burn two or three public keys at provisioning, with a revocation bitmap in OTP. Compromise slot 0, mark it revoked, sign with slot 1. Simple, bounded — you get as many rotations as you provisioned slots, and no more.
A signed key-update path. The bootloader accepts a key-update package signed by the current key. Elegant, and it has a bootstrap problem: if the current key is compromised, the attacker can also update keys. Only useful for planned rotation, not for compromise recovery.
A separate, offline root. A root key signs intermediate signing keys with limited validity. The root is used a handful of times in its life and lives in a safe. This is what a real PKI looks like, and it’s proportionate once your fleet is large enough that a signing key compromise is a company-ending event rather than a bad quarter.
Pick one at architecture time. Retrofitting key rotation onto a fielded fleet is not an engineering task, it’s a recall.

Anti-Rollback, and the Budget It Spends
An attacker installs firmware 3.0 — correctly signed, genuinely yours, and containing the vulnerability you fixed in 5.0. Signature verification passes, because the signature is valid. It is your firmware.
The defence is a security version separate from the marketing version, stored monotonically, checked at boot.
Separate from the firmware version, and that distinction matters. Firmware version goes 5.0.1, 5.0.2, 5.1.0 — it moves for every release. Security version moves only when a release fixes something that must never be downgraded past. Most releases don’t bump it. A product might ship forty firmware versions and reach security version 6.
That’s not fastidiousness, it’s arithmetic. The security version has to be stored somewhere an attacker can’t decrement, which means OTP, or on a part without it a secure element’s monotonic counter, and that storage is one-way and finite. Spend one bit per increment — a thermometer code, where version n means n bits burned — and a 1 KB OTP area gives you thousands of increments, though you won’t be dedicating all of it. Bump the security version on every release and you’ll eventually run out; bump it only on security-relevant releases and you never will.
The check is trivial once the storage decision is right:
if (image_hdr.security_version < otp_read_security_version()) {
fault_record(SEC_ROLLBACK_ATTEMPT, image_hdr.security_version);
return BOOT_REJECT;
}
And the ordering rule: burn the new security version only after the new image has proven itself. Burn it at install time and a failed update leaves you unable to roll back to the working image, because the working image is now below the minimum. That’s a self-inflicted brick, and it’s the tightest coupling point between this article and article 12’s boot confirmation.

Provisioning: The Factory Is a Trust Boundary
Every device needs a unique identity. How it gets one determines whether your private keys ever exist somewhere they can be stolen in bulk.
Injected keys. A provisioning station generates a keypair and writes the private key to the device. Simple, works with any part. It also means every private key in your fleet existed, at some point, on a PC in a factory — possibly one you don’t own, in a country whose legal system you haven’t thought about. Compromise that station and you have the whole production run.
On-device generation. The device generates its own keypair using its hardware TRNG, exports only the public key, and the factory station forwards that to a CA for signing. The private key never leaves the part and never existed anywhere else.
On-device generation is strictly better and the H7 has the TRNG for it. The costs are real but modest: a few hundred milliseconds per unit at test, a provisioning protocol rather than a flash write, and a CA the factory can reach. For anything with a meaningful fleet size, it’s the right answer.

Either way, decide what happens to the provisioning capability afterwards. A device that will re-provision on request has a permanent identity-replacement interface, which is either a service feature or a vulnerability depending entirely on how it’s authenticated.
Debug Lock: The Decision You Can’t Undo
STM32 readout protection has three levels, and the choice between the last two is one of the more consequential irreversible decisions in a product.
Level 0. No protection. Full SWD access, flash readable. Development only.
Level 1. Flash is inaccessible to the debugger. Reverting to Level 0 triggers a mass erase — the chip is usable again, the firmware and any stored secrets are gone. Reversible at the cost of everything on the part.
Level 2. Debug permanently disabled. The JTAG/SWD port is dead. There is no unlock sequence, no ST recovery procedure, no regression path. Permanent.
Most products should ship at Level 1, and I’d defend that against the instinct to choose maximum protection.
Level 1 already prevents firmware and key extraction over SWD — the mass-erase-on-regression means an attacker who reverts to Level 0 gets a blank chip. What Level 2 additionally buys is protection against attacks that work despite Level 1, which is a narrower set than it sounds.
What Level 2 costs is every failure analysis you’ll ever want to do. A unit comes back from the field with an unexplained fault. At Level 1 you can, with the right procedure, still work with it. At Level 2 you have a brick with an interesting story and no way to hear it. Over a ten-year product life with field returns, that’s a real and recurring cost.
Level 2 is the right call when physical compromise of a single unit threatens the fleet — shared secrets in flash, or a device whose extracted firmware enables attacks elsewhere. If your keys are per-device and your firmware isn’t secret, Level 1 plus per-device identity gets you most of the protection and keeps your failure analysis.

Whichever you choose: test the transition on real hardware, early. The option-byte sequence, the timing of when it’s applied in production, and the interaction with your programming tooling all have sharp edges, and finding them during a production ramp is expensive.
Runtime: Authentic Isn’t the Same as Safe
Secure boot guarantees the code that started was yours. It says nothing about what that code does with a malformed Modbus frame.
Without TrustZone on an H7, your runtime tools are the MPU and the privilege split. What they’re genuinely worth:
Keep protocol parsers unprivileged. The Modbus and MQTT parsers handle attacker-controlled bytes. Run them unprivileged with an MPU region covering only their own stack and buffers. A parser bug then faults instead of writing wherever the bug points — it becomes a recoverable fault from article 07 rather than arbitrary code execution.
Make key material unreadable from parsing contexts. If the MPU denies the parser’s region access to the key storage area, a memory disclosure bug in the parser cannot leak keys, even if it’s exploitable for other things.
Mark flash regions no-execute where possible, so a buffer that receives downloaded data can’t become code.
None of this stops a determined attacker who finds a privileged bug. It converts a large class of parser bugs from “device compromise” to “device faults and records the reason,” and given that parser bugs are the most likely vulnerability you’ll actually ship, that conversion is worth the MPU configuration work.

Input validation is the other half, and article 07’s defensive boundary applies unchanged — lengths, indices, counts, ranges, checked at the point untrusted data enters, every time.
When Verification Fails
A production device must do something better than while(1).
The options and their trade-offs:
Boot the previous known-good image. Best for availability. Needs anti-rollback to distinguish “reverting to the image we were running yesterday” from “an attacker forcing a downgrade” — and that distinction is why the previous image’s security version must still be at or above the stored minimum.
Enter recovery mode. Article 07’s minimal firmware. Good, with one caveat worth stating: an attacker who can corrupt flash can force you into recovery mode. If recovery mode has fewer checks than the main image — and it usually does, because it’s small and was written under time pressure — you’ve given them a downgrade attack. Recovery firmware needs the same signature verification and the same scrutiny as the main image.
Halt in a safe state. Right for safety-critical products where operating on unverified code is worse than not operating. Wrong for most industrial gear, where a controller that refuses to boot is itself the incident.
Record the failure either way, with the reason and the rejected image’s version, in the backup-SRAM record article 07 built. A device that has rejected forty images in a week is telling you something that no single boot log would.

And bound the retries. An attacker who can trigger unlimited verification attempts gets both a timing oracle and a power-consumption attack on a battery product. Verification failures should back off, and they should count.
The Regulatory Floor
Security is becoming a market-access requirement rather than a differentiator.
The EU Cyber Resilience Act sets obligations for products with digital elements sold into the EU: security by design, vulnerability handling for a defined support period, coordinated disclosure, and reporting of actively exploited vulnerabilities. The dates are staged: reporting of actively exploited vulnerabilities has applied since 11 September 2026, and the main obligations apply from 11 December 2027, so a product being architected today will ship into both.
The practical consequences reach straight back into this series. You need a way to ship security fixes for the product’s entire declared support life, which is article 12’s OTA architecture and article 10’s ten-year concerns. You need a software bill of materials, which means knowing every third-party component in your image and its version. And you need to know which units are running what, which is article 11’s observability.
None of that is bolt-on. Check the current dates and scope for your product category before committing to a compliance plan — this is a moving area and secondary sources disagree.
Next
Every mechanism in this article has the same weakness: it’s only as good as its failure path, and its failure path is the least-tested code in the product.
Does your bootloader actually reject an image with a flipped bit in the signature? Does anti-rollback actually block a downgrade, or does the comparison have an off-by-one? Does the parser actually fault instead of writing past its buffer? You can’t answer those by reading the code, and you can’t answer them by testing on hardware alone.
Which raises the question this series has deferred since article 01, when the interface pattern first appeared: how do you build firmware you can actually test — on a host, exhaustively, including the paths that only execute when something is wrong?
Article 09: Designing Firmware for Testability — unit testing, mocks, fault injection, and hardware-in-the-loop.
Series Index
Phase 1 — Architecture
- 01. Embedded Firmware Architecture Fundamentals
- 02. Production-Grade Firmware Architecture
- 03. Firmware Architecture Anti-Patterns
Phase 2 — Implementation
Phase 3 — Failure & Resilience
- 07. Error Handling & Recovery
- 08. Firmware Security Architecture (you are here)
Phase 4 — Verification
- 09. Designing Firmware for Testability
Phase 5 — Product Lifecycle
- 10. Designing Firmware for 10-Year Products
- 11. Field Diagnostics & Observability
- 12. OTA & Safe Firmware Updates