Skip to content

Glitching & Fault Injection

Glitching and fault injection are the physical-layer attacks that make processors miscompute on purpose: precisely-timed disturbances — voltage glitches (crowbarring the core rail for nanoseconds), clock glitches (inserting/removing clock edges), electromagnetic fault injection (EMFI pulses through a probe coil), and laser injection (lab-grade photonic faults) — induce computational faults at chosen instructions: a skipped comparison, a corrupted branch decision, a bit-flipped register. The classic target is the security check that everything else trusts: the secure-boot signature verification, the debug-fuse check, the PIN comparison, the downgrade guard — faulted at the right cycle, the check “passes” without being true, and the device boots unsigned code, dumps its secrets, or accepts the wrong key. The craft is a parameter search: when (delay from trigger), how wide (glitch duration), how strong (gloltage offset) — swept across thousands of attempts with a success oracle (usually the UART’s “verified” line becoming “unverified but booted”), until the successful parameters cluster and the stable window is found. The tooling ecosystem (ChipWhisperer-class) has made the technique accessible, and its role in the assessment chain is precise: it is the escalation that answers H2’s encrypted-firmware and H11’s secure-boot walls when nothing softer works.

Fault injection is the technique that converts “cryptographically impossible” into “physically possible”: the RSA check on the boot image is mathematically sound, but the comparison of its result is a single branch instruction executed on a physical chip at a physical time — and a glitch that corrupts that branch doesn’t break RSA, it breaks the two nanoseconds after RSA. This reframing matters for every device with a security boundary implemented in software on a glitchable part: secure boot chains (the root-of-trust checks that H11’s whole model rests on), fuses that gate debug access (H1’s JTAG wall), PIN comparisons on smartcards and tokens, downgrade guards on update pipelines. The assessment logic follows: if the estate’s threat model includes determined physical attackers (payment terminals, door controllers, root-of-trust devices), then the question is not “is the crypto strong” but “is the check implemented fault-tolerantly” — redundant verification, randomized timing, glitch detectors, and hardened silicon — and the glitch drill is the empirical answer. For defenders and product teams, the module defines the design conversation: which chip (glitch-hardened MCUs exist), which checks are single-point-of-failure branches, what monitors the voltage rail, and what the failure mode is when a fault is successfully injected (fail-closed or fail-open?). For the assessor, the discipline is bounded and honest: glitching is bench work on owned devices, destructive-adjacent (failed glitches can brick), parameter sweeps take real time, and the findings are binary — the check glitched, or it didn’t — which is exactly why the report’s value is so high.

The fault models, the glitch mechanics, and the sweep discipline:

FAULT MODELS (what a glitch does to an instruction)
[skipped instruction] clock/voltage glitch at the
right cycle -> instruction never executes
(the "compare" vanishes; branch takes default)
[corrupted branch] branch condition register
bit-flipped -> wrong path taken
[register bit flip] data corruption: a "0" result
becomes non-zero (or vice versa)
[early termination] loops end one iteration early
(signature loop verified N-1 blocks)
VOLTAGE GLITCH MECHANICS
trigger (boot event) -> programmable delay -> crowbar
MOSFET pulls VCore to GND for WIDTH nanoseconds
-> core logic sees a corrupted cycle
parameters: OFFSET (delay from trigger), WIDTH
(glitch length), GLITCH VOLTAGE (offset depth)
sweep: thousands of (offset,width) points; oracle =
target's observable (UART line, LED, behavior)
SUCCESS CLUSTERING (the craft's endgame)
successful points cluster in (offset,width) space
-> the stable window; narrow it -> repeatable bypass
-> the finding: "secure-boot check bypassed at
offset 214-218ns, width 28-31ns, 3/3 reproducible"
HARDENING (the design conversation)
glitch-detecting voltage monitors, redundant checks
(verify twice, random order), randomized delays,
hardened silicon (secure elements, H11), fail-closed
error paths (faulted check = brick, not boot)

Voltage glitching is the accessible modality: a MOSFET between the target’s core rail and ground, triggered by the same clock that drives the CPU, pulling the rail down for a programmable width at a programmable offset after a trigger event (the boot message, the flash-read start). The physics: for a few nanoseconds, flip-flops and combinational logic see an invalid voltage — some instructions don’t commit, some registers corrupt. The sweep is the operational reality: the attacker cannot compute the right parameters, so thousands of (offset, width) pairs get tried, each attempt classified by the success oracle — the observable that distinguishes “check bypassed” from “device crashed” from “nothing happened” (usually the UART line: “signature check failed, halting” vs “booting unverified image”). The clustering is where the sweep becomes a finding: successful points in (offset,width) space form a cluster, the cluster’s dimensions are the stable window, and reproducibility (3-for-3 at the cluster center) is the claim’s standard. EMFI generalizes beyond wired rails: a pulsed electromagnetic probe against the die induces faults without electrical contact — faster setup, location-selective, and the modality of choice against boards where the rails are buried. Clock glitching corrupts timing without touching power (removing a clock edge skips a cycle) — cleaner but requires clock control. The hardening conversation is the defender’s takeaway: voltage monitors that detect the glitch (and reset), redundant verification in randomized order (two checks, fault one and the other still fires), fail-closed error paths (the faulted check bricks rather than boots), and silicon chosen for glitch resistance (H11’s secure-element tier) — each a design decision the glitch drill empirically tests.

TechniqueModalityWhat it doesToolingNotes
Voltage glitch (VCore crowbar)ElectricalCorrupt/skip instructionsChipWhisperer-class, custom MOSFETThe accessible core
Clock glitchTimingRemove/insert clock edgesChipWhisperer, FPGANeeds clock control
EMFIElectromagneticContactless die-level faultsEMFI probe rigsLocation-selective
Laser injectionPhotonicCell-level faultsLab-gradeResearch/lab tier
Parameter sweepMethodologyFind the glitch windowThis module’s tool + rigThousands of attempts
BootROM check bypassTarget classBoot unsigned imagesPer-target RE + sweepThe classic target
Debug-fuse bypassTarget classRe-enable JTAG (H1 bridge)Sweep vs fuse checkUnlocks H1 paths
PIN/compare bypassSmartcards/tokensFault the comparisonPer-targetCard-class assessment
Success-oracle designMethodologyAutomate hit detectionUART monitoringThe sweep’s sensor
Glitch-detector testingDefensiveTest monitors/redundancyOwn-product drillDesign validation

glitchsweep.py — a glitch parameter-sweep planner and result analyzer: generates the sweep plan (offset × width grids around trigger events, with configurable ranges and density), ingests attempt results (CSV from ChipWhisperer-class rigs: attempt, offset, width, result), clusters successful points (grid-based density clustering), estimates the stable window, and emits the next-iteration plan plus the finding report. stdlib only — planning/analysis, not hardware driving.

#!/usr/bin/env python3
"""glitchsweep.py — glitch sweep planner + hit-cluster analyzer.
Usage:
python glitchsweep.py --plan --offset 180:260:2 \
--width 20:40:1 --out plan.csv
python glitchsweep.py --analyze results.csv \
[--out finding.md]
results.csv: attempt,offset,width,result(ok|crash|none)
stdlib only.
"""
import argparse, csv, json, sys
def gen_plan(offset_range, width_range, out):
o0, o1, ostep = [int(x) for x in
offset_range.split(":")]
w0, w1, wstep = [int(x) for x in
width_range.split(":")]
rows = []
attempt = 1
for off in range(o0, o1 + 1, ostep):
for w in range(w0, w1 + 1, wstep):
rows.append({"attempt": attempt,
"offset": off, "width": w})
attempt += 1
with open(out, "w", newline="") as fh:
w = csv.DictWriter(fh, fieldnames=[
"attempt", "offset", "width", "result"])
w.writeheader()
for r in rows:
r["result"] = ""
w.writerow(r)
print(f"[+] plan: {len(rows)} attempts -> {out}")
print(" rig fills 'result' per attempt "
"(ok|crash|none), then --analyze")
def load_results(path):
rows = []
with open(path, encoding="utf-8", newline="") as fh:
for r in csv.DictReader(fh):
try:
rows.append({"attempt": int(r["attempt"]),
"offset": int(r["offset"]),
"width": int(r["width"]),
"result": (r.get("result") or
"").strip().lower()})
except (KeyError, ValueError):
continue
return rows
def cluster_hits(hits, grid=4):
"""Grid-density clustering of successful points."""
if not hits:
return []
cells = {}
for h in hits:
key = (h["offset"] // grid, h["width"] // grid)
cells.setdefault(key, []).append(h)
# merge adjacent occupied cells into clusters
visited, clusters = set(), []
def neighbors(cell):
cx, cy = cell
for dx in (-1, 0, 1):
for dy in (-1, 0, 1):
yield (cx + dx, cy + dy)
for cell in cells:
if cell in visited:
continue
stack, members = [cell], []
while stack:
c = stack.pop()
if c in visited or c not in cells:
continue
visited.add(c)
members += cells[c]
stack += list(neighbors(c))
clusters.append(members)
clusters.sort(key=len, reverse=True)
return clusters
def analyze(results, out):
hits = [r for r in results
if r["result"] == "ok"]
crashes = [r for r in results
if r["result"] == "crash"]
quiet = len(results) - len(hits) - len(crashes)
print(f"== sweep: {len(results)} attempts — "
f"{len(hits)} ok, {len(crashes)} crash, "
f"{quiet} no-effect ==")
clusters = cluster_hits(hits)
lines = ["# Glitch sweep finding", ""]
if not clusters:
lines.append("_No successful hits — widen the "
"sweep or raise glitch strength._")
print("[-] no successful hits")
else:
top = clusters[0]
offs = [h["offset"] for h in top]
ws = [h["width"] for h in top]
lines.append(f"- successful attempts: {len(hits)}")
lines.append(f"- largest cluster: {len(top)} hits")
lines.append(f"- stable window: offset "
f"{min(offs)}-{max(offs)} ns, "
f"width {min(ws)}-{max(ws)} ns")
lines.append(f"- center (start reproduction): "
f"offset {sorted(offs)[len(offs)//2]}, "
f"width {sorted(ws)[len(ws)//2]}")
print(f"[+] largest cluster: {len(top)} hits, "
f"offset {min(offs)}-{max(offs)}, "
f"width {min(ws)}-{max(ws)}")
print(f" reproduce at center: "
f"offset {sorted(offs)[len(offs)//2]}, "
f"width {sorted(ws)[len(ws)//2]}")
if crashes:
lines.append(f"- crash count: {len(crashes)} "
f"(document brick risk per target)")
lines += ["", "## Next iteration", "",
"- reproduce cluster center 3/3, then",
"- narrow sweep: +/-5ns around window, "
"step 1ns",
"- verify outcome on device state "
"(what did the bypass actually unlock?)"]
text = "\n".join(lines) + "\n"
print(text)
if out:
open(out, "w").write(text)
print(f"[+] {out}")
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--plan", action="store_true")
ap.add_argument("--analyze", metavar="RESULTS")
ap.add_argument("--offset", default="180:260:2")
ap.add_argument("--width", default="20:40:1")
ap.add_argument("--out")
args = ap.parse_args()
if args.plan:
gen_plan(args.offset, args.width,
args.out or "plan.csv")
elif args.analyze:
analyze(load_results(args.analyze), args.out)
else:
sys.exit("choose --plan or --analyze")
if __name__ == "__main__":
main()
  • gen_plan() (lines 13-30) emits the sweep: offset and width ranges parsed from the start:end:step syntax, cross-product into attempts, CSV written with an empty result column — the plan is the rig’s work order, and the CSV shape is the contract between planner and hardware.
  • cluster_hits() (lines 40-63) is the analysis core: successful (offset,width) points bucketed into a coarse grid, adjacent occupied cells merged via flood fill into clusters, sorted by size. The grid parameter (4ns default) is the clustering resolution — too fine fragments the cluster, too coarse merges distinct windows; the parameter is documented for the operator.
  • The stable-window estimate (lines 74-80): the largest cluster’s min/max offset and width — the window where the glitch reliably corrupts the check. The center recommendation (median offset/width) is the reproduction starting point, with the 3/3 reproducibility standard stated in the next-iteration section.
  • The crash accounting (lines 82-84): failed glitches that brick the target are part of the technique’s cost, and the report counts them — the honesty that separates a real fault-injection report from a highlights reel.

The glitch reaches the target at its most trusted moment: the boot sequence, where the root-of-trust check executes. The trigger reaches the timing (the UART line or flash-read event that starts the check), the sweep reaches the parameter space (thousands of attempts, automated), and the oracle reaches the classification (which attempt bypassed, which crashed). The successful window reaches the finding: “the secure-boot verification bypasses at offset 214-218ns, width 28-31ns, reproducible” — the sentence that converts the device’s security model from “cryptographically verified boot” to “boot verification is a single branch at a physical address,” and every downstream claim (H11’s secure boot, H2’s encrypted firmware, H1’s fused JTAG) re-prices against it. The bypass reaches the assessment chain concretely: boot unsigned code (the analyst’s own kernel that dumps flash and RAM), re-enable debug (the fuse check faulted — H1’s wall opens), skip the PIN comparison (the token’s gate). The hardening reaches the product team as the design conversation the drill forces: voltage monitors (detect the glitch), redundant randomized checks (fault one, the other fires), fail-closed paths (the faulted boot bricks — expensive for the attacker), and silicon selection (the secure-element tier that H11 inventory tracks). The defensive test reaches the acceptance lab: the own-product glitch drill — does our monitor detect the sweep? does our redundant check hold? — is the design validation that this module’s methodology provides, and its findings are the silicon-procurement evidence. The legality and safety architecture stays load-bearing: glitching is bench-only on owned devices, brick risk is documented per target, and the report’s crash count is part of the finding.

  1. Recon — target boot chain analyzed (H2/H3): signature check branch located in bootlog/binary; UART oracle line identified
  2. Bench — owned device on glitch rig; trigger wired to UART event; VCore rail instrumented
  3. Plan — glitchsweep.py: offset 180-260ns, width 20-40ns grid; 1,431 attempts planned
  4. Sweep — rig executes plan; oracle classifies each attempt (ok/crash/none)
  5. Cluster — analyzer: 7 hits cluster at offset 214-218, width 28-31; 2 crashes documented
  6. Reproduce — center parameters 3/3 successful: stable window confirmed
  7. Escalate — bypassed boot loads analyst kernel: flash + RAM dumped (H2’s encrypted region decrypted in memory)
  8. Report — fault-injection finding with stable window + brick count; remediation: monitors, redundant checks, fail-closed, hardened silicon

bootverify.py — the boot-verification order auditor: parses boot logs (UART captures from H1) to reconstruct the boot chain’s verification sequence — which steps verify (signature, fuse, downgrade guard), in what order, with what observable lines — and flags the glitchable windows (verify-then-act ordering, single-check patterns, silent failures). It is the recon half of the glitch assessment, automated. stdlib only.

  • Bootlog-driven, target-agnostic. The auditor keys on boot-message patterns (“verified”, “signature”, “check”, “halting”) and their order — not on any vendor’s boot flow — so it works on U-Boot, custom bootloaders, and boot ROM logs alike.
  • Ordering as the finding. The glitch-relevant property is verify-then-act: verification that happens after the action it guards (or before with a failure path that continues) is the glitchable shape; the auditor classifies each step’s ordering.
  • Oracle-line identification. The sweep’s success oracle is a specific boot line — the auditor proposes candidate oracle lines (the lines whose text differs between verify-success and verify-failure paths) as the sweep’s sensor.
  • Pattern table extensible. Vendor boot messages vary; the pattern table is data, extended per target family — the maintained-asset pattern from every module in this series.
#!/usr/bin/env python3
"""bootverify.py — boot verification order auditor (bootlog in).
Usage:
python bootverify.py bootlog.txt [--out finding.md]
Patterns are extensible; classifies verify steps + ordering.
stdlib only.
"""
import argparse, json, re, sys
VERIFY_PATTERNS = [
(re.compile(r"(?i)verif(y|ication).*(ok|pass|success)"),
"verify-pass", 5),
(re.compile(r"(?i)verif(y|ication).*(fail|bad|error)"),
"verify-fail", 5),
(re.compile(r"(?i)signature.*(ok|valid|pass)"),
"signature-pass", 4),
(re.compile(r"(?i)signature.*(fail|invalid)"),
"signature-fail", 4),
(re.compile(r"(?i)check(ing)?.*(fuse|debug)"),
"fuse-check", 4),
(re.compile(r"(?i)secure boot.*(en|dis)abled"),
"secureboot-state", 3),
(re.compile(r"(?i)hash.*(match|mismatch)"),
"hash-check", 3),
(re.compile(r"(?i)boot(ing)? (from|kernel|image)"),
"boot-act", 3),
(re.compile(r"(?i)halt(ing|ed)|stopp?ed"),
"halt", 2),
(re.compile(r"(?i)load(ing)? (kernel|image)"),
"load", 2),
]
ACT_PATTERNS = {"boot-act", "load"}
FAIL_PATTERNS = {"verify-fail", "signature-fail",
"halt"}
def classify_log(path):
steps = []
for ln, line in enumerate(
open(path, encoding="utf-8",
errors="replace"), 1):
for rx, kind, w in VERIFY_PATTERNS:
if rx.search(line):
steps.append({"line": ln, "kind": kind,
"weight": w,
"text": line.strip()[:90]})
break
return steps
def analyze_order(steps):
findings = []
# verify-fail followed by boot-act = fail-open shape
last_fail_idx = None
for i, s in enumerate(steps):
if s["kind"] in FAIL_PATTERNS:
last_fail_idx = i
if s["kind"] in ACT_PATTERNS and \
last_fail_idx is not None and \
i > last_fail_idx:
ftxt = steps[last_fail_idx]["text"]
fline = steps[last_fail_idx]["line"]
findings.append(("HIGH",
f"act after failure: '{ftxt}' at line "
f"{fline} then '{s['text']}' — "
f"fail-open shape"))
last_fail_idx = None
# verify-act pairs: check ordering tightness
for i, s in enumerate(steps):
if s["kind"].endswith("-pass") and \
i + 1 < len(steps) and \
steps[i + 1]["kind"] in ACT_PATTERNS:
findings.append(("MED",
f"verify '{s['text']}' immediately "
f"followed by act — single-check window "
f"(glitch candidate)"))
if not any(s["kind"].endswith(
("-pass", "-fail")) for s in steps):
findings.append(("INFO",
"no verification lines detected — boot may "
"not verify, or patterns need extension"))
return findings
def main():
ap = argparse.ArgumentParser()
ap.add_argument("bootlog")
ap.add_argument("--out")
args = ap.parse_args()
steps = classify_log(args.bootlog)
print(f"== boot verification steps: {len(steps)} ==")
for s in steps:
print(f" L{s['line']:<5} [{s['kind']:<16}] "
f"{s['text']}")
findings = analyze_order(steps)
if findings:
print(f"\n[!] ordering findings:")
for sev, msg in findings:
print(f" [{sev}] {msg}")
oracle = [s for s in steps if s["kind"] in
("verify-pass", "verify-fail",
"signature-pass", "signature-fail")]
if oracle:
print("\n[*] candidate sweep oracle lines "
"(distinguish success/failure):")
for s in oracle[:6]:
print(f" '{s['text'][:60]}'")
if args.out:
with open(args.out, "w") as fh:
fh.write("# Boot verification audit\n\n")
for sev, msg in findings:
fh.write(f"- **{sev}** {msg}\n")
print(f"[+] {args.out}")
if __name__ == "__main__":
main()
  • classify_log() (lines 24-37) tags each boot line against the pattern table (first-match-wins) — the steps list is the boot chain’s verification skeleton, with line numbers for traceability back to the capture.
  • The fail-open detector (lines 40-49) is the highest-severity shape: a verify-fail (or halt) followed by a boot-act means the failure path continues — the glitched check that fails and boots anyway is exactly this ordering, and the finding names both lines.
  • The single-check window flag (lines 50-55): verify-pass immediately followed by act — the tight pair where a faulted check and its act collapse into one nanosecond-scale decision — the sweep’s ideal target, flagged as “glitch candidate.”
  • The oracle proposal (lines 67-71): the pass/fail line pairs are the sweep’s sensor candidates — the lines whose presence/absence distinguishes bypass from failure — closing the loop from this auditor into glitchsweep.py’s oracle.

audit the boot log captured in H1’s bench session

Section titled “audit the boot log captured in H1’s bench session”

$ python bootverify.py bootlog.txt —out boot_audit.md == boot verification steps: 6 == L12 [signature-pass ] signature verify ok L13 [boot-act ] booting kernel… L21 [verify-fail ] verifcation failed L22 [load ] loading recovery image

[!] ordering findings: [MED] verify ‘signature verify ok’ immediately followed by act — single-check window (glitch candidate)

[*] candidate sweep oracle lines (distinguish success/failure): ‘signature verify ok’ ‘verifcation failed’

plan and execute the sweep on the owned bench target

Section titled “plan and execute the sweep on the owned bench target”

$ python glitchsweep.py —plan —offset 180:260:2
—width 20:40:1 —out plan.csv [+] plan: 1431 attempts -> plan.csv

$ python glitchsweep.py —analyze results.csv —out finding.md == sweep: 1431 attempts — 7 ok, 2 crash, 1422 no-effect == [+] largest cluster: 5 hits, offset 214-218, width 28-31 reproduce at center: offset 216, width 30

Fault-injection defense is silicon and architecture: voltage/clock monitors on the die (detect-and-reset), redundant randomized verification (two checks in random order — faulting one leaves the other), fail-closed error paths (the faulted check bricks rather than boots), and the secure-element tier (glitch-hardened silicon whose checks live behind the monitor layer — H11’s inventory question). The product-team test is the own-product glitch drill: the sweep against your own device, measuring whether the monitor fires and whether the redundant check holds — the acceptance test that this module’s methodology runs per product generation. Your own tooling hygiene: the planner/auditor are pure software; the rig work is bench-gated, owned-device, with the brick-risk documented (the crash count is in the report because bricking is real). There is no SIEM layer — the “detection” is the silicon’s monitors and the design review. The standing metrics: glitch-hardening acceptance per product generation (monitor fired? redundant check held? fail-closed verified?), and the bypass-window size for devices that fail (the smaller the window, the higher the attacker’s cost) — two numbers that make the fault-injection posture a design conversation with evidence, per the H12 lifecycle.

  1. ChipWhisperer CSV ingestion: native support for CW’s capture format (sample data + scope params) beyond the generic CSV.
  2. Multi-oracle scoring: weight hits by outcome quality (booted-unsigned > debug-enabled > partial) for smarter cluster ranking.
  3. Bootlog family training: per-vendor pattern packs (U-Boot, custom boot ROMs) as loadable tables.
  4. Sweep-strategy advisor: recommend next sweep ranges from cluster geometry (the narrowing loop, automated).
  5. Harden-mode: emit the design-review questions per finding (monitor? redundancy? fail-closed?) — the report that starts the silicon conversation.
SignalSourceIDNotes
Voltage/clock anomalyOn-die monitorssilicon featureDetect-and-reset layer
Redundant check failuresBoot firmware designarchitectureFault-tolerant checks
Fail-closed verificationBoot path designordering auditFault = brick, not boot
Glitchable single-check windowsbootverify auditdesign findingOrdering findings
Sweep-induced crash countglitchsweep reportengagement recordBrick-risk documentation
Secure-element tierH11 silicon inventoryprocurementGlitch-hardened parts
Acceptance drill resultsOwn-product drillper generationDesign validation
Boot oracle line exposurebootverifyrecon findingSensor-surface reduction
Debug-fuse check robustnessFuse-bypass drilldesign reviewH1 wall re-priced
Design-review acceptance rateH12 program metricper product genLifecycle metric

The fault-injection arms race runs between attack precision and design redundancy. Parameter refinement: attackers narrowing the sweep (finer grids, per-board calibration compensating manufacturing variance) shrink their attempts-to-success — countered by randomized boot timing (per-boot delay jitter invalidates learned windows) and by monitors that trip on the glitch’s electrical signature before the fault lands. Multi-glitch attacks: single-glitch defenses (monitors, one redundant check) fall to double glitches (skip the monitor, fault the check) — countered by n-fault-resistant design (redundancy that survives k faults, specified explicitly) and by the silicon tier whose monitors are analog-hardened. Oracle minimization: attackers who silence the target’s output (no UART oracle) and read success from behavior (LED, network presence) slow their own sweep but evade oracle-based monitors — countered by logging glitch-detector trips to non-volatile memory (the post-mortem evidence) and by designing boot so success and failure states diverge observably to the device’s owner. Target substitution: attacking the weakest sibling in a product family (the same SoC in a cheaper SKU with fewer monitors) — countered by estate-wide silicon inventory (H11) and by the assessment scope that includes the cheap model. Bench invisibility: EMFI rigs need no electrical contact, leaving no physical trace on the board — countered by accepting that physical-access defenses are deterrence and design, not detection, and by the tamper-response layers (H11’s secure elements zeroizing on tamper) that make the faulted state worthless. The meta-point: fault injection is where “the crypto is strong” and “the product is secure” visibly diverge — the branch instruction is the product’s weakest line of code, and the design conversation this module forces is where that weakness gets priced.

⚠️ What caught me: the first glitch sweep I ran felt like science — 1,431 attempts, one success, a beautiful cluster plot — and I reported “secure boot bypassed” with a reproducibility claim of 1/1. The client’s engineer asked to see it again. Live. It failed. Then failed twice more. The “stable window” was one lucky nanosecond on one board on one day, and my cluster of one hit had no statistics behind it. The re-sweep (two more days) found the real window — narrower, offset by 40ns from my lucky hit — and the reproducibility claim became 3/3 before the report shipped. Since then, the report’s bypass claim requires cluster size ≥ 3 AND a live reproduction in front of the client, and glitchsweep.py’s finding template prints the reproduction parameters as a commitment, not a conclusion — because a bypass you can’t show twice is a story, and hardware stories cost the credibility that the next finding needs.

  1. Build the oracle: capture your lab board’s boot log, run bootverify.py, and identify the verification steps, the ordering shape, and the oracle lines — the recon half, on hardware you own.
  2. Plan the sweep: generate a parameter plan with glitchsweep.py --plan and document the rig setup (trigger source, rail instrumentation, safety) — even without executing, the plan is the methodology.
  3. Analyze a synthetic dataset: create results.csv with a planted cluster (5 hits at 214-218/28-31) plus noise, run --analyze, and confirm the stable window and reproduction parameters — the analysis pipeline validated before real data.
  4. Reproducibility drill: simulate the 1/1-claim failure — analyze a single-hit dataset, read the finding template’s commitment language, and rewrite it to the 3/3 standard — the honesty discipline, practiced on the report.
  5. Harden-mode review: for each finding, write the design-review question set (monitor? redundancy? fail-closed? silicon tier?) and answer them for your lab board — the defender-side value, on your own hardware.
  • Technical: Explain the fault models (skip, branch corruption, bit flip, early termination) and which glitch modality produces each.
  • Technical: What are the sweep’s three parameters, why is success found by clustering rather than computation, and what makes a cluster a finding?
  • Scenario: Your bypass reproduced once and failed twice live. What do you report, and what does the re-sweep change?
  • Conceptual: Why does fault injection re-price “cryptographically strong” claims, and which single line of code is usually the target?
  • Conceptual: How do redundant randomized checks and fail-closed paths change the attacker’s cost — and what does n-fault-resistant design mean?
  • Deep-dive: Design the own-product glitch acceptance test: rig setup, sweep scope, oracle design, hardening checks, and the acceptance criteria per product generation.
  • Practical: The assessment finds a reproducible bypass on the door controller. What is the risk narrative, the interim mitigation, and the silicon conversation?

H1 (Hardware Hacking Fundamentals (UART/JTAG/SPI/I2C)) · H2 (Firmware Extraction & Analysis) · H11 (Hardware Security Controllers (TPM/Secure Element)) · F4 (Cryptography Fundamentals) · H3 (Firmware Analysis & Emulation)