The Coldcard incident: fixing the culture that missed it
- The bug, briefly
- Where the process broke
- What vendors should do to improve validation
- What the community has to demand
- The pressing challenge of AI
- Moving Forward
- Appendix: Example Hardware Evaluation Framework
A cultural failure: execution-path trust over evaluation, and social reputation over development quality.
On July 30, 2026, someone started emptying Bitcoin wallets that had been dormant for years. No phishing, no malware, no compromised exchange. A five-year-old firmware bug had made the seed phrases guessable, and the attacker derived the private keys offline and swept the first wave of funds before anyone knew there was a problem.
This is not another write-up about the mechanics of the hack. The bug itself is almost boring: one check that tested whether a setting existed instead of whether it was turned on. What should raise eyebrows is that it lived for five years in open, widely-read code, inside the wallet the community trusted most, and nobody caught it. It went unnoticed because self-custody culture rewards the wrong things. It assumes a code path runs because the code is present, and it grades vendors on reputation instead of evidence. Those two habits, execution-path trust over verification and reputation over development practice, are the real vulnerability, and they will keep producing thefts like this one until the community trades trust for proof. What follows is less about Coldcard than about the standard that allowed it, and the one that has to replace it. The article closes with an example draft checklist that turns that standard into concrete questions you can put to any hardware wallet vendor.
Social media has reached for the easy lesson: self-custody is too hard, go back to letting a custodian hold the keys. That the average person recoils from self-custody in the short term is probably unavoidable. If it becomes a long-term trend, that will be because the Bitcoin ecosystem failed to correct its flawed culture around security.
Exchanges did not die after Mt. Gox and self-custody will not die after this. The exchange industry has evolved since Mt. Gox with multi-signature custody, proof-of-reserve practice, and regulatory structure that outlasted the operator who failed. Whether massive self-custody failures produce that kind of durable change is the open question. One-off audits are currently underway across every hardware wallet vendor, but the real lever is how we move forward when we talk about security in self-custody.
The bug, briefly
Coldcard’s board config defined the macro MICROPY_HW_ENABLE_RNG and set it to 0, because Coinkite supplies its own hardware RNG. A library the firmware migrated to in 2021 checked #ifndef MICROPY_HW_ENABLE_RNG, which asks whether the macro exists, not whether it is switched on. The macro existed. The build linked MicroPython’s software fallback PRNG, a toy generator meant for chips with no hardware RNG at all. The real hardware RNG code stayed in every affected binary, reviewed and intact. Seed generation simply never called it.
Coinkite puts effective entropy on Mk2 and Mk3 devices running firmware v4.0.1 through v4.1.9 at roughly 40 bits against a 128-bit design target. Forty bits is a weekend on commodity hardware. Block Engineering’s independent analysis is worse: depending on what an attacker already knows about the device UID and boot timing, the practical search space collapses toward 2^16, and the reseed on later models is capped at 32 bits because only four digest bytes ever reach it. Coinkite’s figure for Mk4, Q, and Mk5 is about 72 bits, still under the standard and inside the budget of a funded attacker.
Entropy generation is the critical step in self-custody that has no replacement. The seed is the root of every key the wallet will ever derive, so a perfect PIN, textbook multisig, and a signing screen that shows exactly what you are approving all rest on top of it, and none of them can save a seed an attacker can guess. That is why this one step is worth staring at while the rest of the device goes unmentioned.
That is the entire technical story, and it is the least interesting part of the incident. The bug is ordinary. What matters is that it shipped, passed review, survived a test suite, and sat in public source for five years underneath the device the community told everyone to buy.
Where the process broke
The development practices at Coinkite failed to catch this. The commit that introduced the bug, b18723dd on March 1, 2021, changed 120 files and 5,488 lines, swapped out the cryptographic primitives under seed generation and signing, and went straight to master with the message “First pass w/ libNgU.” The public history shows a single-parent commit with no merge, no pull request, and no CI run. A change to seed generation reaching users with no formal, published review process is not a one-off slip; it is a process with limited gates and no transparency.
Testing the output for entropy is nearly impossible, and that is the trap. The suite set random.seed(42) for reproducibility, and the only runtime check asserted a handful of distinct byte values, which any deterministic generator passes. No statistical test on a finished seed can separate unpredictable from reproducible: a 40-bit seed still expands into unique, normal-looking addresses. Entropy is a property of the process, not the bytes. What is tractable is testing the path, which nobody did: confirm the hardware RNG is actually reached, and that known inputs produce known outputs. Block Engineering found the failure there in hours. The hardware RNG exported different function names than the library expected, so the call bound to MicroPython’s software generator instead, and the correct code sat present and unreachable in every binary. It is the unverified backup: file there, checksum fine, empty the day you restore from it.
A closed secure element adds a variable you cannot account for. The reseed on Mk4, Q, and Mk5 comes from a chip whose firmware nobody outside Coinkite can audit, so the 72-bit figure rests on trust rather than measurement. Reproducible builds prove the shipped binary matches the public source, which matters, but they cannot tell you the source is correct, and they cannot let you feed known entropy into the secure element and check what comes back. Every closed component in the entropy path is one more thing taken on faith.
Most importantly, the “grading” of Coinkite as a reliable provider was done on reputation. Coldcard was the default recommendation for years, sold on marketing, feature lists, and word of mouth, and that recommendation was never attached to anything a buyer could inspect. Nobody traced the call path because everybody assumed somebody already had. That is the habit that has to change. A recommendation should turn on the strength of the development team, whether the code and build can actually be audited, the quality of the tests, and how much trust the design removes from any single part. Reputation without clear evidence is nothing but trust.
What vendors should do to improve validation
None of these are hard asks. Most are a weekend of engineering time, and the first three would have increased the odds of catching the bug.
- Require a pull request, a named reviewer, and a green CI run for any change touching cryptographic code. No maintainer exemption, no direct pushes to the main branch, no “first pass” commits that migrate a crypto library. Coinkite cleared none of it for this change. Make sure discussions are publicly visible if code is visible.
- Add known-answer entropy tests to CI. Feed a known entropy source into the device, generate a seed, and verify the output is a pure function of the input. Then disable the device’s own entropy and confirm the output changes. A working hardware RNG makes seeds depend on hardware entropy; a broken one makes them deterministic given a known input.
- Verify the call graph on the built binary, not the source. Confirm at the linker level that the RNG symbol resolves to the hardware path. Reading the code cannot tell you this, which is exactly how five years went by.
- Emit generation-time evidence. When a seed is created, the device should record which physical source each byte came from (the hardware RNG register, the secure element, the user’s dice) and confirm each was live, never the entropy values themselves, which are the seed. The record is about provenance, not quality: whitening makes even a broken generator’s output look random, so a statistical check passes on the exact fallback that caused this, and what cannot be faked is that the hardware path was actually read. Keep it as a signed, exportable/viewable attestation, and leave out any field that also feeds the seed, since a boot timestamp here would have leaked the timing the attack relied on. Sign it across the trust boundary: each component attests the other’s contribution, so one buggy component cannot forge both the seed and its own alibi. A birth certificate like that would let a user identify the exact code blocks responsible for their seed generation, instead of inferring it from a firmware version and their own memory.
- Re-run AI-assisted audits on old code with current tooling on a fixed schedule. Code that was clean under 2021 review methods is not clean now.
What the community has to demand
The audit wave now underway is the right instinct running on the wrong standard. Ledger has said its certified TRNG is unaffected. Trezor has said it does not share Coldcard’s code. Keystone, Foundation, and BitBox have issued similar statements. Every one of those is a claim, and Coldcard would have made the identical claim on any day of the last five years.
The question each vendor needs to answer is narrow and checkable: can you trace the entropy path from source to seed, end to end, on the actual shipped binary, and show that the hardware RNG is reachable from make_new_wallet or its equivalent? “We have a TRNG and it is in the code” is the answer that failed.
Open-source vendors have an advantage in answering this question, and should use it. Producing that trace means publishing the source path, the symbol resolution on the built binary, and the known-answer test vectors, none of which exposes a proprietary secure-element design. The closed part can stay closed; the entropy path can still be shown. A vendor that answers “trust our certification” when it could publish the trace is choosing opacity it does not need, and that choice should count against it.
Three levels of assurance need separate names, because collapsing them is how this happened.
- Source availability means you can read it.
- Reproducible builds mean you can confirm the binary matches what you read.
- End-to-end path verification means you can confirm the secure path actually executes.
Coldcard had the first two. Almost every discussion of hardware wallet security treats the first two as sufficient, which is the problem we need to fix now.
Two structural changes would make the validation process normal rather than heroic:
- One is a published audit framework with standard test vectors, so a vendor can be measured against a checklist that already exists instead of a checklist they wrote.
- The other is cross-vendor review or bug bounty program, funded jointly, on the argument that a design failure at any vendor damages every vendor’s customers.
This is at least the second time a bug like this has produced weak seeds at scale. In 2023, Milk Sad (CVE-2023-39910) revealed that Libbitcoin Explorer’s bx seed used a Mersenne Twister seeded from 32-bit time(), exposing 120,000 keys. Same root cause, same cultural failure: open source, publicly documented, nobody auditing the entropy path. The response was to fix Libbitcoin, warn users, and move on. That response worked once and cannot work twice. Every wallet that generates keys needs its entropy path traced on every release, and the traces need to be published.
Holders also have two levers to pull to mitigate risk in key generation: multi-vendor multisig and bring your own entropy (dice, coins, etc.). Both mechanisms reduce the reliance of wallet security on any single vendor’s implementation. In Coinkite’s case, multisig and dice-roll users are thought to be unaffected by this exploit. Critics will say that dice rolling can be unreliable. The reality is that the current state of industry means users either roll their own, or trust a vendor. The only way to actually improve this dynamic is to make it easier for users to verify the validity of entropy from a hardware wallet. Even with these options as part of the equation a robust future of self-custody heavily relies on the community getting the culture around vendor/code evaluation correct.
The pressing challenge of AI
Everything above points one direction: transparency is the advantage. A vendor that shows its work, in the code and in the build, needs less trust from the buyer, and needing less trust is the entire point of self-custody. But open code cuts both ways. Anyone can read it, which means anyone can also hand it to a frontier model.
Any code that has ever been public is now scannable by an attacker with a current model, across the full commit history, faster than any human review team can work. The five-year dormancy window that let this bug survive is closing, and it closes for every project at once. A clean audit in 2024 says nothing about 2026, because the tool doing the reading keeps getting better. Auditing becomes maintenance, not a milestone.
Hiding the source is the wrong reaction. Closed code delays attacker discovery, and delays defender discovery by more, because you find your own bugs only when the funds are already gone. The economics point the same way: AI review at scale runs into real token bills, and an open project can spread that cost across a community while a closed vendor pays alone and cuts corners in private. What fits the moment is a shared audit framework, one that lets the community hunt vulnerabilities early and often: full re-audits each time a more capable model ships, and AI-assisted review on every pull request, with human oversight and discussion before anything merges.
Moving Forward
It is tempting to lay this on Coinkite alone, or on the people who recommended the device for years. The blame is wider than that. Bitcoin worked. Self-custody worked. What failed sat around them: a critical bug in plain sight in public code, under a device holding a large share of the community’s savings, and no habit anywhere in the ecosystem was going to catch it.
That culture is being rebuilt now and our discussions will shape it. Over the next few months the audits underway will continue in two possible directions: traced entropy paths, published test vectors, and symbol resolution checked against real binaries, or another round of reassuring press releases from vendors. The Coldcard incident proved that the Bitcoin community had largely been trusting and not verifying, despite the common mantra to do the opposite.
This is not the time to recoil from our stated values. It is the time to shift from trust to verification. One preprocessor check tested the wrong thing for five years and nobody looked, because nobody had to; reputation stood in for proof. The wallets that deserve trust after this are the ones that never ask for it, the ones that hand you the proof and let you run it yourself.
Appendix: Example Hardware Evaluation Framework
Everything above reduces to one question a buyer or reviewer can put to any vendor: can you show, not assert, that the secure path runs? The checklist below turns that into concrete asks. A vendor’s answers sort them into the three assurance levels, and a “no” in the entropy-path section should count for more than a polished answer everywhere else.
Assurance level
- Is the firmware source available and complete, including the seed-generation path?
- Are builds reproducible, so you can rebuild and match the shipped binary byte for byte?
- Can they show end-to-end path verification, not just that the code exists?
The entropy path
- Can they trace the path from source to seed on the actual shipped binary, and show the hardware RNG is reachable from the seed-generation function?
- Do they verify at the linker level that the RNG symbol resolves to the hardware path rather than a software fallback?
- Do they publish known-answer vectors from a test build that pins every entropy source (hardware RNG, secure element, user-supplied dice) to a fixed value, and show that changing each source changes the seed?
- Have they audited every preprocessor guard in the entropy path for the existence-versus-value distinction (
#ifndef Xversus#if X != 0)?
Development process
- Are a pull request, a named reviewer, and a green CI run required for any change touching cryptographic code, with no direct-to-master exemptions?
- Are there commits that swapped cryptographic primitives without review or detailed discussion around the change?
Testing
- Do the tests cover seed-generation entropy, or only downstream behavior?
- Do they test the process (path reachability, known-answer vectors) rather than run statistics on output bytes, which a broken generator passes?
Closed components
- Which parts are closed (secure element, bootloader), and do any sit in the entropy path?
- For each closed component in that path, what exactly is taken on trust, and can they bound it, such as the real bit contribution of a reseed?
- Where you cannot inject a known value, can they at least show the component is load-bearing (force a reseed, confirm the output depends on it) and name what stays unverifiable?
Generation-time evidence
- Does the device record or show provenance, which physical source the entropy came from during seed generation, without ever persisting the entropy values?
- Is the record a signed, exportable attestation that leaves out any field that also feeds the seed?
- Is it signed across a trust boundary, each component attesting the other’s contribution?
Track record
- After their last disclosure, did they publish a root cause and traces, or reassurances?
- Do they re-audit old code as more capable models ship?
- Does the license permit independent commercial auditing, or does it deter the people who would build tooling against it?
Write a comment