The Code That Lied
The commit message was one word: `runs`.
One thousand five hundred and thirty-four lines changed. Commit message: runs. And it was true. It did run. That was the entire problem.
A hardware wallet has one job: generate a number nobody else can guess, then never let it leave the box. The number is the wallet. If it's guessable the money is already gone — and nothing about the device will tell you.
In COLDCARD firmware v4.0.1, shipped March 2021, a developer overriding the STM32's hardware random number generator hit a duplicate-symbol error at link time. Two definitions, one name, toolchain unhappy. The correct fix is to work out which definition you meant. The fix that shipped was #define MICROPY_HW_ENABLE_RNG (0), with a comment explaining that "we have our own version of this code."
They did not have their own version of this code.
What the define did was worse than switching the hardware off. It made two pieces of software disagree about reality. MicroPython read the value — zero — and compiled in Yasmarang, its small, cheerful, entirely deterministic pseudorandom generator. Fine for shuffling a list. Catastrophic for holding a stranger's retirement. Meanwhile libngu, COLDCARD's own crypto library, guarded its hardware path with #ifndef MICROPY_HW_ENABLE_RNG — which asks whether the macro exists, not whether it means anything. It existed. Its value was zero. libngu bound itself to the fallback, believing it had bound itself to the chip.
Two files, one macro, opposite conclusions, no error. Both internally consistent. Neither correct.
Yasmarang seeded itself exactly once: the low 32 bits of the chip's unique ID, the SysTick counter, the real-time clock. None of those is a secret. Block's engineers, who published the analysis, put the SysTick contribution at fewer than 2^17 possible values on the Mk2 and Mk3. libngu then XORed that against a second Yasmarang seeded from hardcoded constants — the cryptographic equivalent of shuffling a marked deck into another marked deck. A twelve-word BIP-39 seed implies 128 bits of entropy. On an affected Mk3 the real figure was around 40.
Entropy has no smell. Thirty-two bytes of Yasmarang and thirty-two bytes of hardware noise are indistinguishable by inspection. Both look like static. The only difference lives outside the device — in whether some other machine, somewhere, can regenerate the sequence. There is no unit test for nobody else can guess this. There cannot be. You are asserting a fact about the rest of the universe from inside a box that can only see itself.
On July 30, 2026, the assertion came due. Galaxy Research traced a sweep that emptied 1,196 bitcoin addresses of 1,082.65 BTC — roughly $70.2 million — in forty-one minutes, and tied it to the flaw; its running count later reached 1,367.05 BTC across 4,585 addresses, with the analysts noting they had not computationally confirmed every address came from a weak seed. Coinkite's advisory the same day scoped it to v4.0.1 through v4.1.9 on Mk2 and Mk3, with later models carrying a partial reseed that lifts them to about 72 bits. Better. Still not 128.
One honest qualifier, since this is a piece about people not checking. Owners who supplied their own dice rolls — fifty or more throws, mixed in at generation — weren't leaning on the broken generator alone, and Coinkite's advisory turns on exactly that. The device offered a manual entropy path and some people walked it. Which means the population that lost money is, substantially, the population that trusted the hardware to do the one thing the hardware was for.
This failure mode deserves a name, because we keep meeting it and keep acting surprised: surface coherence sitting on structural incoherence. Every visible signal agrees. Every invisible one is broken. A system can only report on what it observes, and the fault is precisely what sits outside its instrumentation. A tumor is metabolically thriving. But security is where it kills, because there the invisible layer is the product. Everything else is a case and a screen.
Don't blame the developer. He hit a compile error at some hour of some night and made it stop, and every one of us has done that. The failure is architectural: a build in which a security-critical primitive could be swapped for a toy by a one-line define, silently, with no assertion at the point of use. random.bytes(32) should have refused to return. It should have panicked, bricked, screamed. Instead it did the most dangerous thing software can do. It returned successfully.
And then a review process looked at 1,534 lines under a one-word commit message and said yes.
We did this in 2006. Debian's OpenSSL maintainer commented out two lines to quiet a memory-checker warning, and for the next twenty months every key generated on a Debian box came out of a pool of roughly 32,768 possibilities — the range of a Linux process ID. Nobody noticed until May 2008. Fifteen years later a different maintainer in a different ecosystem silenced a different complaint, and the silence looked the way it always looks. Like success.
And the only reason you are reading about any of it is that somebody could read the code.
No device reported this fault; it had nothing to report and never would have. It was caught from outside, by engineers reading a diff in a public repository, five years after the merge. Slow, embarrassing, and the entire available mechanism. Once you accept that a system cannot verify this property from the inside — and this one couldn't, and neither can yours — the only substitute left for a proof is letting outsiders look. Auditability stops being a virtue and starts being load-bearing.
A closed-source wallet with this identical defect would be shipping tonight, and its owners would be watching the same green lights. Device boots. Seed generates. Everything fine.
The code didn't lie. Code can't lie. It did what it was told, reported accurately on what it did, and told the truth about everything except the one thing that mattered — which is worse than lying. That's a system executing perfectly, at speed, in a direction nobody was watching.
runs.
Yeah. It ran. It ran for five years, and then in forty-one minutes, so did somebody else.
Further reading
- Block Engineering Blog — Predictable RNG Fallback and 32-Bit Reseed in COLDCARD Firmware (2026)
- Coinkite Blog — COLDCARD Security Advisory: Mk3 Seed Generation Warning (2026-07-30)
- The Hacker News — Coldcard Hardware Wallet Flaw Linked to $70 Million Bitcoin Theft in 41 Minutes (2026-08-01)
threaded with
- beat · Tech
The Loneliness Was Already There
AI companion apps did not manufacture the loneliness — they found it fully formed. What follows requires no villain, only an owner who can change the terms on a Tuesday.
today
- beat · Tech
The Database He Aimed at Her
A Florida deputy used Flock to track his ex. Every control ran. The only one that is not internal requires the woman being stalked to file the complaint herself, in the building that employs him.
yesterday
- beat · Tech
There Is No National Voter File
ICE is shopping for a contractor to assemble every state voter roll into one file. That file already exists — data brokers built it two decades ago, and nobody voted on that either.
2 days ago