Scoping SeedSigner
Why SeedSigner is the first project in the AI-assisted Red Team cut, and which surfaces I will treat as the system under test.
SeedSigner is the first project in the initial cut. This is a scope, not a review result.
What it is
SeedSigner is a DIY, air-gapped Bitcoin signing device built from off-the-shelf parts — typically a Raspberry Pi Zero (the 1.3, with no Wi-Fi or Bluetooth), a small LCD HAT, and a camera. Software is MIT-licensed, volunteer-run, and split across two public repos:
SeedSigner/seedsigner— the Python applicationSeedSigner/seedsigner-os— a Buildroot image that boots from RAM so the microSD card can be removed after startup
The security story the project tells about itself is specific. The device is stateless: seeds live in RAM and are wiped on power-off. It is supposed to receive data only by scanning QR codes and send data only by displaying them. It helps with BIP39 seed creation (including dice and camera entropy), SeedQR backup, xpub export, PSBT signing, and address verification, including multisig. Cryptographic work in the app goes through embit.
If that story is true, the QR camera and the screen are the trust boundary. That is why it belongs in a Red Team cut aimed at Bitcoin open source, not because it is fashionable DIY hardware.
Why it is first
Blast radius is keys. Users build this to hold seed material and to sign. A wrong PSBT decode, a wrong address on the screen, or a seed path that is not what the UI claims is a funds bug.
The surface is bounded. Python plus a known library, plus an OS image whose job is to remove kernel features (networking, HDMI, USB, swap). That is a system I can hold in my head. Bitcoin Core is not.
AI is actually usable here. Models are mediocre at novel cryptography and decent at “does this parser accept a shape the UI then mislabels.” SeedSigner is full of parsers and labels: QR payloads, PSBT fields, BIP39 wordlists, derivation paths, network constants.
It is also a stack, not a single file. Application code can be correct while the OS image still ships a module the air-gap story forbids. Reviewing only seedsigner/ would be half a test plan.
Surfaces I will treat as in-scope
I am allowed to study the public source. I am not going to probe other people’s devices or mainnet coins.
Seed creation and import. BIP39 checksum behavior, dice and camera entropy, passphrases, Electrum-style paths if present, SeedQR / CompactSeedQR encode and decode. The question is not “is BIP39 implemented.” It is whether the device can believe it imported one seed and be holding another.
PSBT parse and display. What the screen shows versus what will be signed. Change detection, destination addresses, amounts, fee, multisig policy, script types. A signing device that signs correctly and displays incorrectly is still a failure.
QR as I/O. Animated QR, UR types, Specter-style payloads, camera decode. Malformed or concatenated payloads. The air-gap is only as strict as the decoder.
Settings and leftover state. The device claims to be stateless for secrets. Settings can persist. Anything that writes to the SD card, or survives a reboot in RAM longer than the UI admits, is in scope.
SeedSigner OS. Buildroot config, removed kernel modules, init, what is still reachable if someone uses a Pi with radios, reproducible-build assumptions. The application cannot save an air-gap the OS does not enforce.
embit usage. Not a full audit of embit in this pass. How SeedSigner calls it: network magic, xpub versions, taproot/script types, whether a user-visible network setting actually reaches the signer.
seed # create, import, SeedQR, passphrase
psbt # parse, display, sign, change
qr # camera in, screen out
state # RAM wipe vs settings vs SD
os # Buildroot image vs the air-gap claim
Out of scope for this pass: enclosure supply chains, camera hardware defects, and other people’s companion wallets except as documented QR formats.
How I will use AI here
Read modules against the project’s own invariants (“stateless”, “QR only”, “RAM wipe”). Ask for missing tests around parsers, not for exploit recipes. Run whatever test suite the repo already has. Treat every model claim as a hypothesis to reproduce on my own checkout.
Most output will be wrong. That is expected. The useful residue is a mismatch between a documented guarantee and a code path.
Disclosure
Findings go through SeedSigner’s own project channel (GitHub for the public repos), not this blog. I will not publish exploit details or reproduction steps here.
Next in this cut: bitcoinjs-lib, then Zeus.