Hardware tools

Faultier Documentation

Performing voltage fault-injection attacks

For the hardware overview and buying options, see the Faultier product page.

Faultier fault-injection board and connector pinout

Reference for the faultier Python library.

Installing

pip3 install -U faultier

For debugger support also install:

pip3 install -U pyocd-faultier

Connecting

import faultier
ft = faultier.Faultier()

Important:

  • Linux needs a udev rule, otherwise you get “Access denied”:
    SUBSYSTEM=="usb", ATTR{idVendor}=="37de", ATTR{idProduct}=="fffd", MODE="0666", GROUP="plugdev"
    
    followed by sudo udevadm control --reload and a re-plug.

Serial port

The Faultier also exposes a USB-serial bridge to the target:

import serial
s = serial.Serial(ft.get_serial_path(), 115200)

get_serial_path() resolves the correct device path per platform (COM port on Windows, /dev/cu.* on macOS, a by-id path on Linux).

Glitcher

Configuration is local, not immediate

configure_glitcher() only mutates a local configuration object. Nothing is sent to the device and no IO changes until you call glitch() or power_cycle(). Any parameter you leave out keeps its previous value, so you can configure incrementally:

ft.configure_glitcher(
    trigger_type=faultier.TRIGGER_RISING_EDGE,
    trigger_source=faultier.TRIGGER_IN_EXT0,
    trigger_pull_configuration=faultier.TRIGGER_PULL_NONE,
    glitch_output=faultier.OUT_CROWBAR,
    power_cycle_output=faultier.OUT_MUX0,
    power_cycle_length=1_000_000,
)

# Later, only change one field:
ft.configure_glitcher(pulse=12)

To wipe the configuration back to “no trigger, no outputs, no pulls, delay/pulse 0”:

ft.default_settings()

This is also run automatically when the Faultier object is created.

Executing a glitch

ft.glitch(delay=5, pulse=1)

The sequence is: power-cycle (if configured) → wait for trigger (if configured) → wait delay cycles → drive the glitch output for pulse cycles.

delay and pulse are in CPU cycles at 250 MHz, i.e. 4 ns per cycle.

If the trigger never fires, the device returns a trigger-timeout, which the library raises as a ValueError("Trigger timeout!").

Power-cycle only, without glitching:

ft.power_cycle()

Internally this runs the same pipeline with trigger, delay, pulse and glitch output disabled — which means a plain power_cycle() still fills the ADC buffer.

Trigger types

Set trigger_type to TRIGGER_NONE to glitch immediately (e.g. a fixed delay after a power cycle) — there is no need to also clear trigger_source.

Trigger typeDescription
TRIGGER_NONENo trigger — wait delay cycles and glitch
TRIGGER_HIGHFires as soon as the line is high (fires immediately if already high)
TRIGGER_LOWFires as soon as the line is low (fires immediately if already low)
TRIGGER_RISING_EDGERising edge (0 → 1)
TRIGGER_FALLING_EDGEFalling edge (1 → 0)
TRIGGER_PULSE_POSITIVEPositive pulse (0 → 1 → 0)
TRIGGER_PULSE_NEGATIVENegative pulse (1 → 0 → 1)

Trigger sources

Trigger sourceDescription
TRIGGER_IN_NONEIgnored — use TRIGGER_NONE to disable
TRIGGER_IN_EXT0Use EXT0 as digital trigger input
TRIGGER_IN_EXT1Use EXT1 as digital trigger input

Pull-ups and pull-downs

trigger_pull_configuration enables the microcontroller’s internal weak resistor on the trigger pin:

ValueEffect
TRIGGER_PULL_NONEPulls disabled — pin floats when undriven (default)
TRIGGER_PULL_UPWeak pull-up: undriven pin reads high
TRIGGER_PULL_DOWNWeak pull-down: undriven pin reads low

The pull is applied to the pin selected by trigger_source (EXT0 or EXT1) and only when a trigger is configured — with TRIGGER_NONE the pull setting is never applied. It is (re-)applied every time a glitch runs, since that is when the configuration is pushed to the device. The resistor is weak (tens of kΩ), so it only defines the level of an undriven line; anything actively driving the pin wins easily.

Glitch and power-cycle outputs

The same enum is used for glitch_output and power_cycle_output.

OutputDescription
OUT_CROWBARDrives the gate of the N-channel crowbar MOSFET (the usual glitch output)
OUT_MUX0Analogue switch channel 0/X — the one on the SMA connector
OUT_MUX1Analogue switch channel 1/Y — on the 20-pin header
OUT_EXT03.3 V pulse on EXT0 — useful to trigger a ChipSHOUTER, laser, scope, …
OUT_EXT13.3 V pulse on EXT1 — same
OUT_NONEDisabled. Trigger, delay, power-cycle and ADC still run — ideal for testing

The MUX outputs switch the analogue switch from IN0 (idle) to IN1 (glitch/power-cycle active), which is how a target’s supply is cut and restored.

Non-blocking glitching

glitch() blocks until the glitch completes. When the trigger is generated by your own Python code (starting a debug transaction, sending a UART byte, …) you need to arm first and act afterwards:

ft.glitch_non_blocking(delay, pulse)

# ... do the thing that causes the trigger ...

# MANDATORY: read the response, otherwise the protocol desyncs.
ft.glitch_check_non_blocking_response()

The check call is where trigger-timeout and error exceptions surface.

ADC

The ADC is clocked from the CPU clock and takes 96 cycles per sample — about 2.6 MS/s at 250 MHz. clock_division divides that further:

sample rate = 250_000_000 / (96 * clock_division)
ft.configure_adc(faultier.ADC_CROWBAR, 3000)            # ~2.6 MS/s
ft.configure_adc(faultier.ADC_CROWBAR, 3000, 10)        # ~260 kS/s, 10x longer window
ADC sourceDescription
ADC_CROWBARDrain side of the crowbar MOSFET
ADC_MUX0Output of multiplexer channel 0
ADC_EXT1Whatever is attached to EXT1

Capture starts when the trigger fires (or immediately with TRIGGER_NONE) and the buffer is refilled on every glitch() and every power_cycle().

To capture without glitching, disable the glitch output:

ft.configure_glitcher(
    trigger_type=faultier.TRIGGER_NONE,
    glitch_output=faultier.OUT_NONE,
    power_cycle_output=faultier.OUT_NONE,
)
ft.glitch(delay=0, pulse=0)

values = ft.read_adc()   # list of floats, 0.0 – 1.0 (8-bit samples / 255)

SWD checker

The Faultier runs a full SWD stack on-device, which makes “is the target still locked?” checks fast enough to run inside a glitch loop — no OpenOCD round-trip.

ft.swd_check()      # True if an SWD device responds (e.g. STM32 RDP2 → RDP1 glitch)
ft.nrf52_check()    # True if nRF52 APPROTECT is disabled / flash is readable

nrf52_check() & swd_check() return False rather than raising when the debug transaction fails, so it is safe to call after a glitch that crashed the target.

Debugger support

The Faultier can be used as an SWD debugger using PyOCD. Install the faultier-compatible version using pip3 install -U pyocd-faultier.

To connect to a target you can run:

pyocd commander -t <Target>

For example for an STM32F205:

pyocd commander -t stm32f205rgtx

Helpers

RandomOrderGenerator

Walks a range in a shuffled but exhaustive order (no repeats, no buffering of the whole range). Useful for sweeping delays so partial runs still cover the whole space evenly:

from faultier import RandomOrderGenerator

gen = RandomOrderGenerator(1000, 2000)
delay = gen.next_value()   # raises StopIteration once every value has been visited
gen.reset()

Quick reference

CallPurpose
Faultier()Connect, handshake, reset to defaults
default_settings()Reset glitcher configuration
configure_glitcher(...)Set trigger/outputs/delay/pulse (local only)
configure_adc(source, count, div=1)Configure the ADC capture
glitch(delay, pulse)Run the full pipeline, blocking
glitch_non_blocking(delay, pulse)Arm without blocking
glitch_check_non_blocking_response()Mandatory completion read for the above
power_cycle()Power-cycle only (still fills the ADC)
read_adc()Fetch the sample buffer as floats 0.0–1.0
swd_check() / nrf52_check()Fast on-device lock checks
get_serial_path()Path to the USB-serial bridge