This lesson has one job: define the behaviour when authentication fails. A device that halts with secrets in registers, retries forever on attacker-controlled input, or leaks which check failed gives away the rest of the design. The failure path needs the same specification effort as the pass path: a safe state, a bounded recovery, and silence about the cause.
On failure the ROM enters a defined fail state: no jump, interrupts held, secrets cleared from reachable registers, and the device waits for an authorised recovery rather than retrying the rejected image. Recovery is a second, independently authenticated image path, typically a minimal loader over the same SPI link, itself verified against the same fused keys before it runs; an unauthenticated recovery path is a back door wearing a maintenance badge. The external world learns only that boot failed, never which of the four checks fired: distinguishing responses invite oracle attacks that probe one check at a time. Attempt counting with a fused monotonic counter can rate-limit physical probing, at the cost of a denial path the product must own deliberately.
# not-run: recovery budget thinking, NanoSoC teaching values
Recovery loader: a minimal SPI image under 2 KB, verified like any image.
Fail-state power: clocks gated except the always-on domain; wake only on
authorised recovery request, never on retry timers owned by the ROM.
What the ROM never does: jump on fail, retry the same bytes, or report
which check failed over any observable interface.The fail state is specified register by register: what is cleared, what is held, what stays powered.
Recovery is authenticated to the same root with its own test images: good recovery boots, bad recovery refuses.
Failure observables are enumerated and carry no check-specific information: one fail indication for all causes.
Fail each of the four checks separately and confirm identical external behaviour in all four cases: same indication, same safe state, same recovery offer. Then corrupt the recovery image and confirm it refuses too. If any failing case jumps, retries silently, or names its check on the UART, the failure path was specified as prose and implemented as hope.
Because distinguishable failures let an attacker fix one check at a time against the device as an oracle. Uniform failure forces the attacker to satisfy every check blind, which is the whole point of having four of them.