Security
When randomness
looks random.
A hardware wallet incident was not a break in Bitcoin. It was a firmware integration failure, and a reminder that security lives in the path between components.
The Coldcard incident is not a story about someone breaking Bitcoin.
It is a story about randomness, and about how a system can look secure while quietly taking the wrong path underneath.
That is a more useful story for anyone who builds software.
A computer does not know how to roll a dice
Computers are deterministic machines. Give them the same input and they produce the same output. That is what makes them useful, and it is also a problem when we ask them to create something that must be genuinely unpredictable.
A computer cannot simply decide to be random. It needs entropy: information from the physical world that an attacker cannot know in advance. Tiny variations in electrical noise, timing, and other physical processes can provide that uncertainty.
A hardware random number generator tries to collect this kind of uncertainty inside the chip and turn it into bits.
A pseudo-random number generator is different. It is an algorithm that starts with an initial state and produces a sequence that looks random. The sequence can pass statistical tests and still be completely reproducible if someone knows, or can work out, the initial state.
Random-looking is not the same thing as unpredictable.
Why the distinction matters
Entropy is measured in bits. One bit is roughly a fair coin toss: two possible outcomes. Ten bits gives you about a thousand possible states. Twenty bits gives you about a million.
A 12-word BIP39 seed is built around roughly 128 bits of entropy. A 24-word seed is built around roughly 256. Those numbers are not decoration. They describe the size of the space an attacker would have to search.
If the real entropy is much lower, the search space shrinks dramatically. The wallet is not just a little less secure. Its seed may be enumerable offline.
This is also why the word “random” needs more care than it usually gets. A sequence can contain different numbers every time, have no obvious pattern, and still come from a small set of possible states. It can look chaotic to a person and be predictable to a machine with the right starting information.
What went wrong
In the incident, a firmware configuration and integration bug caused seed generation to follow a software PRNG path instead of calling the hardware RNG.
The fallback used values such as device identifiers and timers. Those values may be different from one device or boot to the next, but they are not cryptographic secrets. They can be constrained, reconstructed, or guessed much more easily than genuine physical entropy.
The important detail is that the hardware RNG did not necessarily have to be broken. The code simply did not use it on the path that mattered.
That is the kind of bug that is easy to miss in a review. The function exists. The build succeeds. The device displays a seed. The output looks random. The security property has already disappeared.
Why one weak seed affects the whole wallet
From a wallet seed, the rest of the key tree is derived deterministically. The private keys, addresses and accounts are not independently random events. They all come from that original source.
So if an attacker can reproduce or search the weak seed, they can derive the rest of the wallet offline. They do not need to touch the device, trigger a PIN prompt, or exploit the wallet remotely at the moment of the attack.
This is not a break in the cryptography used by Bitcoin. It is a reduction in the number of possible starting points.
Hashing cannot create missing entropy
Hashing is often treated as a kind of security magic. It is not.
A hash function can make data difficult to recognise or reverse in the normal sense. It cannot create uncertainty that was not present in the input.
If the input contains roughly 20 bits of real uncertainty, hashing it produces an output that may be hundreds of bits long, but it still comes from roughly 20 bits of uncertainty. The hash hides the shape of the input. It does not expand the search space.
An update cannot repair the past
A firmware update changes the code that will run in the future. It does not rewrite a seed that was already generated.
If a seed was created through a weak path, that seed remains weak after the update. The safe response is a new seed generated through a fixed path, followed by a careful migration.
This is a general software lesson as well. Fixing the code that creates bad state does not automatically fix the bad state already stored in the database. Sometimes a fix needs a migration, a rotation, or a deliberate user action. “The patch is deployed” and “the old data is safe” are different statements.
A passphrase and a PIN are different too. The PIN protects access to the device. A BIP39 passphrase is an additional secret that changes the derived wallet. A strong, unique passphrase can add another barrier, but it does not repair a weak seed. It only changes the cost of reaching the wallet behind it.
Self-custody is a responsibility shift
Cold wallets reduce important classes of risk. Keeping signing keys away from an always-connected computer is a meaningful improvement over leaving everything on an exchange or in a hot wallet.
But cold storage does not remove implementation risk. It moves more responsibility to the device firmware, the seed-generation process, the backup procedure and the person performing the migration.
Self-custody does not mean zero risk. It means that more of the risk is now yours to understand.
The new economics of finding bugs
There is another uncomfortable part to this.
Open source makes software inspectable, which is one of its greatest strengths. It also means that more people can study the same code. As AI tools and agentic workflows make code analysis cheaper and easier to run at scale, the cost of looking for subtle integration bugs will continue to fall.
That does not prove how this particular bug was found. It does mean that “nobody will look closely enough” is becoming a worse security assumption.
The attacker does not need to break every part of the system. They only need to find the one path where the security property was assumed rather than verified.
Why this matters to the software I build
At HigherHighLabs, I build applications that businesses use for real work: customer records, internal workflows, documents, schedules, payments and data that should not be visible to the wrong person.
Security is part of the quality bar for those systems, not a separate polish layer at the end. The exact controls depend on the data and the risk of each application, but the questions are consistent.
- Where are secrets created, stored and allowed to travel?
- What happens when a user does not have permission?
- Can a different client or API path bypass the intended rule?
- What happens when a third-party service fails?
- Does the system fail closed, or quietly fall back to something weaker?
- Can we see what changed, when it changed and who changed it?
- When a vulnerability is fixed, what old data or state still needs to be migrated?
I do not believe in promising that software can have zero risk. I do believe that critical assumptions should be explicit, testable and difficult to bypass accidentally.
The hardware can be perfect. If the software takes the wrong path, the randomness becomes theatre.
The most dangerous failure mode is not always the one that crashes the application. Sometimes it is the one that lets everything look normal while the security model underneath has already collapsed.