The HABv4 key hierarchy: SRK, CSF, IMG and the fuses

Why i.MX8M secure boot needs thirteen key pairs rather than one, what 'open' and 'closed' actually mean, and which single 256-bit value ends up permanently burned into the chip. The concepts, before any tool touches anything.

Before running a single tool I wanted to understand the cryptography underneath, because skipping this and following a guide leads to a point where something fails and you have no idea why.

So this post has no commands in it. It is the mental model the rest of the series assumes: NXP's mechanism is HABv4, High Assurance Boot, and it is simpler than the acronyms make it look once you see the chain written out.

The chain, top to bottom

power on
│
▼
┌──────────────────┐ Burned into silicon at the factory. Cannot be changed by
│ Boot ROM │ anyone, ever, including NXP.
└────────┬─────────┘
│ reads one-time-programmable fuses:
│ • SRK_HASH — the fingerprint of keys I trust
│ • SEC_CONFIG — am I open, or closed?
▼
┌──────────────────┐ ROM verifies imx-boot's signature against SRK_HASH.
│ imx-boot │ CLOSED: bad signature → nothing runs.
│ (SPL + ATF │ OPEN: bad signature → runs anyway, but RECORDS the failure.
│ + U-Boot) │
└────────┬─────────┘
│ each stage verifies the next, the same way
▼
┌──────────────────┐
│ Linux kernel │
└──────────────────┘

Every stage only trusts the next if it is properly signed. That is the entire idea: a chain of custody from the power switch to the operating system.

"Open" is where you should live for weeks

This is the part I appreciated most once it clicked, and it is what makes the whole project safe to learn on.

An open device still performs every signature check. It still records the result. It just does not refuse to boot on failure.

So you can flash a completely wrong, unsigned, garbage image, and the board will boot, and it will tell you honestly that the signature failed — rather than pretending everything was fine. You get the real behaviour of the security mechanism with none of the risk of bricking anything. From U-Boot:

u-boot=> hab_status

That reports whether HAB logged any event during this boot. On an open device, a signing mistake shows up here as a recorded failure while the board sits happily at a prompt. On a closed device the same mistake means the board does not reach a prompt at all.

Closing the device is a separate, one-way action — a fuse. After it, unsigned code will not run, full stop, forever. I kept that for last, after weeks of everything else working cleanly, and part 10 is about why I stopped short of it even then.

Why there is not just one key

My first assumption was: generate one key pair, sign with the private half, done. That is not how HAB works, and the reason is worth understanding rather than memorising.

CA
│
┌─────────┬───────┴───────┬─────────┐
SRK1 SRK2 SRK3 SRK4
│ │ │ │
┌──┴──┐ ┌──┴──┐ ┌──┴──┐ ┌──┴──┐
CSF IMG CSF IMG CSF IMG CSF IMG

Thirteen key pairs in total. Each level does one job:

  • CA — a notary. Its only function, ever, is to certify the four SRKs. It never signs a firmware image.
  • SRK1–4, the Super Root Keys — the actual masters. Only one is in use at a time; I use the first. The other three sit unused as a reserve.
  • CSF and IMG, under each SRK, with genuinely different jobs:
    • CSF (Command Sequence File) signs the instructions — which regions of the image to check, with which algorithm.
    • IMG signs the actual bytes of the firmware.

That split is NXP's design, not a convention you can ignore, and it means the signing step later produces two distinct signatures over two distinct things, not one blob getting one signature. If you are ever confused about why a signing procedure has two passes, this is why.

The four SRKs are your entire key-rotation story

This is the part that justifies the whole structure. If the SRK in use is ever compromised on a device that has already been closed, there is exactly one recovery: permanently burn a revocation fuse for that key and switch to the next one.

That is it. Four keys means at most three rotations, ever, and then the device can never be re-keyed again. It is also why you do not simply generate one key: you would have no recovery at all.

What actually gets burned into the chip

Only one value: a 256-bit hash of the SRK table — eight 32-bit words.

Not the keys. Not the certificates. The public keys and certificates travel along inside every signed image; the chip only needs the hash to know it is looking at the right table.

That is a good design detail to sit with for a moment, because it explains several things that would otherwise seem odd:

  • The fuse space required is tiny and fixed, regardless of key size.
  • You can inspect a signed image and see the whole public key material in it.
  • Rotating keys means changing what the chip trusts, which is why it needs its own fuses.

What has to stay secret, and what does not

Out of this entire tree, only the private halves matter for secrecy. The public certificates are fine to hand out, embed in images, publish — they are not the secret.

The private keys are a different matter, and the consequences run in both directions:

  • Lose them after the device is closed, and you can never sign firmware for that device again. Permanently. No OTA update, ever.
  • Leak them, and secure boot on that device is over — anyone with the key signs whatever they like and the chip trusts it.

So: keep them completely outside any source repository, back them up properly, and treat them as the actual crown jewels of the project. Part 4 covers how I backed them up, because it deserved its own space.

One sharp edge that is easy to miss: certificate expiry is irrelevant here. The boot ROM has no concept of today's date — there is no real-time clock involved in a signature check at power-on. Expiry is an ordinary-PKI idea, like a browser checking a website certificate, and it simply does not apply. The validity period you choose when generating keys exists only to keep the generation tooling happy.

Next: actually generating this tree with NXP's Code Signing Tool, and the thing that script does silently if you do not read it first.