An independent, open-source firmware for Coldcard hardware, written from scratch in
Rust. Bitcoin first, but not Bitcoin-only — see docs/ROADMAP.md.
MIT licensed. Copyright © 2026 Karpeles Lab Inc.
Status: runs on real hardware, not ready for funds. Dev-signed images install and run on every board -- mk3, mk4, mk5 and Q1. The device boots, logs in against the real secure element, generates and stores a seed, reads a settings store that stock firmware wrote, and takes its own firmware upgrades over USB. Signing, multisig and the Q1's QR and NFC transports are written and covered by host tests against the standards' own vectors, but have not been exercised end to end on a device.
Do not put funds on a device running this. Keep the words of any seed you let it store, and expect to reinstall stock firmware.
Want to try it? Install it from your browser with CatCard Manager: plug the device in over USB, pick a release, and approve the install on the device's own screen. It runs entirely in the page (WebHID, no server), switches a stock Coldcard to CatCard, updates a CatCard, and can put official Coldcard firmware back. Nothing is installed until you approve it on the device.
Taking a .dfu from a release, or from
CI, or building it yourself instead: Images built for you.
The stock Coldcard firmware derives its BIP-39 wallet seed from two chained software
PRNGs, not from the hardware TRNG the chip provides. On mk3 the resulting seed has on
the order of 22 bits of real entropy; on mk4 a partial mitigation raises the floor to
about 32. That is the immediate reason this project exists, and it is why the entropy
subsystem is the first thing built here rather than the last. See
docs/ENTROPY.md.
The secondary reason is licensing. The original firmware is MIT plus the Commons
Clause, which is not open source. CatCard is MIT-only, which requires that it be
genuinely independent of that source — see CLEANROOM.md.
- Multi-source RNG. A seed is drawn from a pool fed by every noise source the board
has — the STM32 TRNG read directly, the bootloader's own TRNG read, the secure
elements' TRNGs, and keypress timing — combined with SHA-512 so a weak source can fail
to help but can never cancel a good one. Each hardware source runs continuous SP 800-90B
health tests, public values such as the unique ID are credited zero bits, and a pool
that has not earned its entropy refuses to produce a seed instead of carrying on.
See
docs/ENTROPY.md. - No MicroPython. The whole firmware is Rust, compiled to native code: no interpreter and no garbage collector. Private-key work runs with interrupts masked, which the wallet crate enforces through its types, so a host cannot time a derivation while it is running.
- Better keypad responsiveness. The pad is scanned by native code on every pass of the loop rather than through an interpreter. Every key is debounced separately, a key held from the previous screen cannot skip the next one, and each press is timestamped at its electrical edge (where the board supports it) so the timing feeds the RNG.
- A drawn UI, not a text one. Stock is text on amber. Here the menus a person lives in -- the main one, Derive, Utils, Sign -- are grids of drawn icons on the Q1, chains carry their own logos in full colour, and the screens that wait on the secure element show a picture rather than a word. One layout engine fits the 128x64 OLED and the Q1's 320x240 colour screen alike, so the mono boards get the same shapes at their own size.
- Login protections. A scrambled number row and a login countdown, each switched on only after it has proved itself on the device, plus Test login. Release builds add a kill key and microSD 2FA; development builds leave those out, so a bench unit cannot erase itself.
- Large SD cards. Cards up to 2 TB (the SDXC ceiling) work, where stock stops at 32 GB. Reading covers FAT12, FAT16, FAT32 and exFAT, the format most large cards come with, and Format writes FAT16, FAT32 or exFAT to suit the card's size, so a new card needs no reformatting on a computer first.
- Optional multichain support. One firmware, no per-coin apps to install. Chains are
a build option, so a Bitcoin-only image has the other chains' code absent, not just
hidden — a smaller attack surface, not only a smaller menu. With
MULTICHAIN=1the registry covers Bitcoin, Ethereum, Solana, Litecoin, Bitcoin Cash, Dogecoin, Tron, Monacoin, Namecoin and Electra Protocol, each with its own derivations and address formats. A multichain build also signs the unified opt-in signature hash (SIGHASH_UNIFIED, hash type bit0x20) where a host asks for it, checked against the 166 vectors its specification publishes. - Games and cats. Block Mine, Block Cutter and (on the Q1) Flappy Cat live under Utils and can be left out of a build, and the device boots to a cat.
- Login. Two-part PIN against the bootloader's gate, anti-phishing words, nickname, attempt counter; Test login, scrambled keys and a login countdown.
- Wallets. Create a seed from the entropy pool (12 or 24 words) or import one, as words, an XPRV node or a raw master secret -- all three shapes stock can store; passphrase; Import key, which takes words, an XPRV or a WIF key in for the session without touching the stored one; Seed XOR split and join; Codex32 (BIP-93) generate, import, Shamir split, recover and derive shares; SeedQR, shown and scanned back on the Q1; BIP-85 children (words, XPRV, WIF, password, hex), where the words, XPRV and WIF children can be put in force; a key vault in stock's own format, so keys saved by either firmware are readable by the other.
- Addresses. An explorer over all four single-sig types, accounts and both chains, with QR codes; the account and the starting index typed rather than stepped to; a derivation path of your own, shown in every type it could be spent as; a CSV of index, path and address to the card; Verify address; registered multisig wallets; the other chains' formats on a multichain build.
- Signing. PSBT (v0 and BIP-370 v2, handed back in the version it came in) signing for single-sig (P2PKH, P2SH-P2WPKH, P2WPKH, bare P2PK and P2TR key path) and registered multisig, with the fee shown and capped, change proven by re-deriving it, and a warning on change parked on an unusual path. Messages too: typed or read off a text file, signed the legacy way or under BIP-322, and a signed file checked against the address it names.
- Transfer. USB HID, microSD (browse, format, import, export), firmware upgrade from
either, and on the Q1 a QR scanner with BBQr and BC-UR. The NFC tag carries three
things: a fully signed transaction goes out as a link, and a phone that taps the device
is what sends it -- this hardware has no network of its own; an address from the
explorer goes out as a
bitcoin:URI a phone can pay to; and a transaction a phone writes to the tag comes in to the same signing screen the card and the camera use. - Storage. An authenticated, power-fail-safe settings store, compatible with the one stock writes: a device that has run stock keeps its settings, notes and seed vault.
- Danger zone. View the words of the key in force, destroy the seed, or lock a loaded key down as the stored one — each labelled at every step.
Stock feature by stock feature, with what is left: docs/PARITY.md.
| board | MCU | firmware base | status |
|---|---|---|---|
mk3 |
STM32L496RG | 0x0800_8000 |
runs; no settings medium wired yet (SPI-NOR) |
mk4 |
STM32L4S5xx | 0x0802_0000 |
runs; installs over USB |
mk5 |
STM32L4S5xx | 0x0802_0000 |
runs; same image as mk4, which the header claims for both |
q1 |
STM32L4S5xx | 0x0802_0000 |
runs; colour screen, keyboard, QR scanner, PSRAM |
The bootloader below our firmware is protected flash and cannot be replaced. CatCard
builds against its fixed contract: image format, signature, and the callgate ABI for
PIN, secrets and secure-element entropy. The callgate entry point is read from the table
the bootloader publishes at 0x0800_0040 and validated before use — it moves between
bootloader versions, so it must never be hardcoded.
Bench units are locked at RDP=2, where a signed image that boots but hangs cannot be
recovered. New low-level work therefore goes behind a Debug menu entry before it goes on
the boot path; see docs/VALIDATION.md.
make # dev images for every board, into out/
make q1 # just the Q1 -> out/catcard-q1.{bin,dfu}
make mk4-mk5 # one image, both boards
make q1 MULTICHAIN=1 # every chain the registry knows, not just Bitcoin
make q1 SHIP=1 # a real release: the USB debug crutches stripped
make test lint # host tests / clippy, as CI runs themA default build carries the bench crutches — host key injection, and a peek/poke/jsr
memory monitor — because that is how the device is driven while the firmware is being
built out. SHIP=1 is what strips them, and nothing else should ever be given to
someone else: usb-debug-mem reads the seed and the PIN out of RAM and runs arbitrary
code. See docs/USB.md.
catcard-image flattens the ELF, writes the 128-byte header at offset 0x3F80, pads
to a 512-byte multiple, signs the double-SHA256 digest with the published developer
key, and wraps the result as DfuSe. Signing with the dev key is what makes an image
loadable by anyone; it also means the device boots it with a 25-second warning and a
red "not genuine" light, and that no dev-signed image is attributable to any author.
Getting it onto hardware: docs/FLASHING.md.
You do not have to build anything to try this. The simplest way in is CatCard Manager, which picks the right image for the connected device, checks it, and offers it to the device over USB.
To handle the files yourself, take them from a release:
Releases → — the newest is at the
top. Each carries the twelve signed .dfu files described below and a SHA256SUMS to
check them against, and stays downloadable. (Releases are marked pre-release while
CatCard is alpha, so GitHub's "latest release" link skips them; use the list.)
For something newer than the last release, CI builds every shape on every push and
attaches the same signed .dfu files to the run:
Latest builds →
— open the newest green run and scroll to Artifacts at the bottom of the summary
page. Each one downloads as a zip holding one .dfu.
Twelve of them, named catcard-<board>-<chains>[-games]:
| board | mk3, mk4-mk5 (one image serves both), q1 |
| chains | bitcoin, or multichain for every chain the registry knows |
-games |
the same image with Flappy Cat in it |
So catcard-q1-multichain is the Q1 build most people want, and catcard-mk4-mk5-bitcoin
is a Bitcoin-only mk4 or mk5.
Two things to know before flashing one:
- They are signed with the published developer key, so the device boots them with a red "not genuine" light and a 25-second warning. That is what lets anyone load them; it also means no dev-signed image is attributable to any author.
- They carry no debug monitor and no key injection, unlike a local
makebuild. CI checks that rather than assuming it, because these are downloadable by anyone.
Artifacts expire after GitHub's retention window, so an old run may list none. Build it yourself, or use a newer run.
crates/
catcard-board board tables: memory maps, pin assignments, peripheral presence
catcard-fwhdr signed image header + the digest the bootloader verifies
catcard-callgate bootloader callgate ABI (PIN, secrets, SE entropy, DFU)
catcard-entropy entropy accumulator, SP 800-90B health tests, HMAC-DRBG
catcard-wallet mnemonics, HD derivation, encodings, addresses, transactions,
PSBT review, multisig, descriptors, and the chain registry
catcard-sign deterministic ECDSA (RFC 6979) and BIP-340 Schnorr
catcard-pin the login sequence over callgate 18
catcard-upgrade staging and validating a firmware image
catcard-usb HID transport framing, descriptors, control transfers
catcard-qr the Q1's QR scanner protocol
catcard-bbqr BBQr: a payload split across QR codes, as stock writes them
catcard-bcur BC-UR: the same, plus the registry items wallets exchange
catcard-sd SD card bring-up and block reads; FAT12/16/32 and exFAT
catcard-flash SPI-NOR driver
catcard-settings authenticated, power-fail-safe settings store
catcard-alloc a small heap that reports failure instead of panicking
catcard-kernel a small preemptive kernel: real tasks, stacks, context switch
catcard-log a ring of bytes that outlives whatever wrote it
catcard-hal STM32L4/L4+ register-level drivers (RNG, SPI, DMA, GPIO, SDMMC,
USART, clocks)
catcard-ui framebuffers, SSD1306 and ST7789 drivers, fonts, text, art,
menus, keypad and keyboard
catcard-fw the firmware binary
tools/
catcard-image build, sign, verify and package images
artgen/ turn art into the indexed and deflated forms the firmware draws
fontgen/ turn fonts into the tables the renderer walks
reference/ independent reference implementations used to cross-check crypto
emu/ the emulator harness, for what can be checked without a device
usbclient.py drive a device over USB: install, unlock, read the log
keys/
dev-privkey.pem the published Coldcard developer key (public by design)
Everything except catcard-fw builds and tests on the host, which is where the
correctness-critical logic lives.
CLEANROOM.md |
what may and may not be consulted, and why |
THIRD-PARTY-NOTICES.md |
licences that travel with the distribution |
docs/ARCHITECTURE.md |
crate boundaries and the fixed-vs-ours split |
docs/ENTROPY.md |
the seed RNG design and the bug it replaces |
docs/SECRETS-AND-SETTINGS.md |
how secrets and settings are stored, and read back from stock |
docs/MENU.md |
our menu next to stock's, item by item |
docs/PARITY.md |
every stock feature, whether CatCard has it, and the order the gaps close |
docs/HARDWARE-OPEN-ITEMS.md |
unknowns, and what would settle each |
docs/VALIDATION.md |
the hardware bring-up plan, in running order |
docs/ROADMAP.md |
what is next, in order |
docs/KERNEL.md |
the task kernel and what runs under it |
docs/PSRAM.md |
the Q1's PSRAM: what owns it and the write rules |
docs/CALLGATE-DMA.md |
driving the panel by DMA around a blocking callgate |
docs/FLASHING.md |
the three dev loops |
docs/USB.md |
USB identity, transport, and the debug features a release strips |
docs/RELEASING.md |
reproducible builds and release signing |
Read CLEANROOM.md first — it is the constraint everything else follows from. New
hardware facts must cite a source and carry a confidence tag; anything unconfirmed
belongs in docs/HARDWARE-OPEN-ITEMS.md as well as in the code. make test lint is
what CI runs; a change to the boot path needs a word about how it was tried on a device
that cannot be recovered if it hangs.