Fault Injection Turns Your Correct Code Into an Attacker's Tool.
Your signature check returns the right answer every time it runs. Fault injection is the art of making sure it doesn't run — a single skipped instruction, and the branch you trusted inverts.
Your firmware is correct. Every unit test passes, the signature verification returns false on a tampered image and true on a good one, and the logic reads cleanly in review. None of that is in dispute, and none of it is the point. Fault injection does not attack your logic; it attacks the execution of your logic — the physical act of the processor carrying out the instructions you wrote. A check that is perfectly correct every time it runs can be defeated by making sure it does not run.
This is the uncomfortable gap between software as you reason about it and software as the silicon executes it. You think in terms of statements and branches that always happen. The chip thinks in terms of voltages settling, clock edges arriving, and registers latching — all of which can be pushed out of spec for a few nanoseconds at a precisely chosen moment. An attacker who controls the power rail, the clock, or the electromagnetic field around the package reaches past every software control you built and operates on the machine underneath them.
Correctness is not robustness
A signature check that returns the right answer every time it runs is correct. Robust means something stronger: that it still protects you when the attacker is actively manipulating the conditions the processor runs under. These are different properties, and the overwhelming majority of firmware has only the first. Functional testing — the entire basis on which we usually declare code done — proves behaviour under normal conditions and says nothing whatsoever about behaviour when the supply voltage dips for ten nanoseconds at the instant a comparison executes.
That blind spot is not a testing failure; it is a category the test suite was never designed to cover. You cannot unit-test your way to glitch resistance, because the fault lives below the level your tests can express. The attacker's whole plan is to operate the chip outside the envelope your correctness proof quietly assumed, and in that envelope the ordinary guarantees of the instruction set simply stop holding.
What a glitch actually does
Processors are analog devices wearing a digital costume. A flip-flop captures the intended value only if voltage, clock timing, and temperature all stay within their specified bounds. Push one of them briefly out of bounds at the right moment and the chip does something undefined but often repeatable: it misreads an opcode, skips an instruction entirely, corrupts the value loaded into a register, or botches the result of a comparison. The device does not crash in a tidy way — it continues executing, now subtly wrong.
There are three common ways to deliver that disturbance, and none of them require opening the chip. A voltage glitch is a brief, sharp droop or spike on the power rail. A clock glitch is a malformed or over-fast clock edge that forces the core to act before a result has settled. Electromagnetic injection is a sharp pulse from a small coil held over the package, inducing currents inside without any electrical contact at all. The equipment to do these things on a bench is no longer the preserve of national laboratories.
The single effect an attacker prizes most is the skipped instruction. Make one instruction fail to execute — the one that sets a flag, takes a branch, or writes a lock bit — and the program marches on as though it had, straight into a state the programmer was certain could never be reached.
The single decisive branch
Security code has a habit of funnelling down to one decision. "Is the signature valid?" becomes, at the machine level, a single comparison followed by a single conditional branch. "Is secure boot enabled?" is one bit, tested once. "Is debug locked?" is one register, read once. The entire weight of the protection comes to rest on that one instruction executing, and resolving the way you intended.
That structure is precisely what fault injection feasts on. The attacker does not need to break your cryptography or guess a key; they need to make one branch go the wrong way, one time. If the only thing standing between an invalid image and execution is a single conditional, then a single well-timed glitch is a complete bypass — and the cryptography will have verified flawlessly right up until the instant its result was discarded. The maths was never the weak point. The one instruction that acted on the maths was.
A glitch does not forge a signature. It waits for your code to finish checking, and then skips the line that would have acted on the answer. Security that depends on one branch depends on that branch being un-glitchable — and no branch is.
Where glitches bite
Because the valuable decisions in a device are few, the high-value targets are predictable. Secure-boot signature verification is the classic one: skip it and the device will run anything. The debug-lock check is another: glitch it and the port that gives full memory access re-opens. Lifecycle and readout-protection checks can be glitched to step a device back to a less-protected state. PIN and password comparisons can be glitched to take the "correct" branch regardless of input. Any gate that answers "are we allowed to proceed?" with a single evaluation is a candidate.
The pattern worth internalising is this: wherever one instruction converts a secret or a check into a go or no-go, that instruction is a fault-injection target. A capable attacker enumerates those points on a device they bought on the open market, then spends attempts against them. Glitching is a numbers game — cheap, automated, repeated thousands of times with the timing nudged a little on each pass — and it only has to succeed once to hand over a result that persists.
Countermeasures — redundancy and flow integrity
You cannot make an ordinary microcontroller immune to glitching. What you can do is raise the number of faults an attacker must land at once, and arrange that every uncertain outcome resolves to "deny". The countermeasures are individually simple and collectively effective, and they share a single theme: never let one instruction be decisive.
- Check more than once, and compare — verify the signature twice, read the lock bit twice, and require the two results to agree, so that one corrupted evaluation is caught by the other rather than trusted on its own.
- Make success hard to reach by accident — do not let "authorised" be the value a zeroed or skipped register happens to hold; use non-trivial sentinel values so that a glitched or default state lands on "deny", never on "allow".
- Enforce control-flow integrity — track that the expected sequence of steps actually executed, with flow counters or step markers, so that a jumped-over verification is detected after the fact instead of silently assumed.
- Add timing the attacker cannot predict — random delays and jitter before critical operations deny a glitch the precise moment it depends on, turning a reliable attack into a rare and expensive one.
- Detect the attack conditions — on-die voltage, clock, and temperature sensors that trip when the environment leaves spec let the device react to the glitch attempt itself, not merely to its consequences.
- Fail closed and loudly — when redundant checks disagree, halt or reset into a safe state and treat the mismatch as evidence of attack, rather than retrying until it happens to pass.
Designing for the fault model
Fault injection is also a threat-model decision, and treating it as one keeps the effort proportionate. A device an attacker can never physically hold has little to fear from a bench glitcher, and loading it with countermeasures is wasted cost. A device sold on the open market, or deployed somewhere an adversary can reach it, has to assume the bench exists and that patient, repeated attempts will be made against its few decisive branches. Parts built with hardened cores, redundant logic, and integrated glitch sensors exist for exactly this situation, and selecting one is a design-time choice that cannot be retrofitted in firmware later.
When 6QURE reviews a design against fault injection, the work is to find the single branch — the one comparison, the one bit, the one register read — that the whole security story leans on, and then to ask what one skipped instruction would do to it. Where the answer is "everything", what you have is not a hardened device; it is a correct one waiting for a glitch. The remedy is never a stronger algorithm. It is refusing to let any single instruction carry that much weight, so that defeating the device requires landing several independent faults at once — which is the difference between a weekend on a bench and an attack most adversaries will never attempt.
Facing something like this in your own environment?