The SRK fuse and closing the device: what is left, and why I stopped

Two fuse writes stand between a signed board and a locked one: the SRK hash, and the bit that makes the chip refuse unsigned code. Both are permanent. Here is exactly what they are, how to read their current state, and why I have not burned either.

Everything up to here is reversible. The bootloader is signed, the kernel is signed, the chip verifies both and reports them clean — and the board would still happily boot an unsigned image tomorrow, because it is open.

Two fuse writes end that. Both are one-way. This post is what they are, how to check where your board stands, and why mine is still open.

Where the board is right now

Ask it, at the U-Boot prompt:

iotdin-imx8p=> fuse read 6 0 4
Reading bank 6:
Word 0x00000000: 00000000 00000000 00000000 00000000
iotdin-imx8p=> fuse read 7 0 4
Reading bank 7:
Word 0x00000000: 00000000 00000000 00000000 00000000
iotdin-imx8p=> fuse read 1 3 1
Reading bank 1:
Word 0x00000003: 88000000

Three facts in that output:

  • Banks 6 and 7 are all zeros. That is where the 256-bit SRK hash lives — eight 32-bit words across two banks. Nothing has been written. The chip trusts no key.
  • Bank 1 word 3 reads 0x88000000. This is SEC_CONFIG. The close bit is clear, so the device is open.

I have re-run these checks at the end of every session in this project, across several months, and they have never once drifted. That is worth doing: it is two commands, and it turns "I am fairly sure I have not burned anything" into a fact.

Fuse one: the SRK hash

The first write puts the fingerprint from part 3 — SRK_1_2_3_4_fuse.bin, eight 32-bit words — permanently into banks 6 and 7.

# NOT RUN on my board. The shape of it, for when it is.
u-boot=> fuse prog 6 0 <word0>
u-boot=> fuse prog 6 1 <word1>
...
u-boot=> fuse prog 7 3 <word7>

After this the chip knows which keys it trusts. On its own it changes nothing about booting: an open device still boots unsigned code. It is the precondition for the second fuse, not a security control by itself.

It is also the point of no return for your keys. Lose the private halves after this and the board can never be given new firmware you signed — the recovery is to revoke an SRK and move to the next of the four, which you can do three times, ever.

Verify before you write. The words go in one at a time, by hand, and a transposition is not correctable:

hexdump -C SRK_1_2_3_4_fuse.bin

Compare against the value you recorded when you generated it, and read it back with fuse read 6 0 4 and fuse read 7 0 4 after writing.

Fuse two: closing the device

The second write sets SEC_CONFIG in bank 1. After it, the boot ROM refuses to run anything that does not verify against the fused SRK hash.

That is the whole point of the project, and it is also the moment the board stops being forgiving. Every mistake that has been a learning experience in this series — a wrong offset, a CSF that signed the wrong range, a bootloader built for the wrong DRAM variant — becomes a board that does not start and cannot be recovered by flashing a known-good image, because the known-good image is not signed either.

Why I have not burned them

Not because it is technically hard. Both are a handful of fuse prog commands.

Because nothing yet requires it. Everything I set out to learn, I have learned on an open device: the chip checks the signature, records the result, and tells me honestly — while booting regardless. hab_status returning No HAB Events Found! on a signed image is the same evidence on an open device as on a closed one. The difference is only what happens when it fails.

Because the value is in the process, not the fuse. A closed board with an unreproducible signing procedure is worse than an open board with a documented one. The signing script that recomputes offsets from each build's own artefacts is the actual deliverable. The fuse is the last five minutes.

Because the remaining work is not signing work. Before closing a device that ships, I would want: key custody settled properly (where the private keys live, who can reach them, what happens when I am not available), a signed-firmware OTA path proven end to end, and a decision about the TPM — which is a different project, as part 1 argued.

And because the honest state of this project is "proven, not deployed." Writing it up as finished when the irreversible step has not been taken would be a lie by omission, and the step is not the hard part.

If you do decide to burn them

The sequence, in the order that keeps every failure recoverable for as long as possible:

  1. Sign and verify everything first, on an open device, until hab_status is clean on every image you care about — bootloader and kernel both.
  2. Back up the keys to somewhere you will still have in five years, and verify the backup restores. Part 4 applies to keys exactly as much as to the eMMC.
  3. Record the fuse words outside the keys directory.
  4. Burn the SRK hash. Read it back. The device is still open; unsigned code still boots. Spend time here.
  5. Only then set SEC_CONFIG.

Between steps 4 and 5 you have a board that knows your keys and still forgives mistakes. That gap is the most useful state in the whole process, and there is no reason to leave it quickly.

What this series covered

Ten posts from a bare gateway to a signed, verified boot chain: the hardware and how to confirm which variant you have, the HAB key hierarchy, generating it with CST, backing up everything first, pinning four upstream sources to exact commits, a first unsigned build, breaking the board and recovering it over USB, enabling HAB and signing imx-boot, extending the chain to the kernel, and — here — the two writes that would make it permanent.

The board on my bench runs signed code and verifies it, and remains completely recoverable. For a piece of work whose point was to understand the mechanism rather than to ship a locked device, that is the right place to stop.