When Randomness Fails
A random-number API can return a plausible-looking value while the program behind it has almost no secret choices left. The fault may be a packaging patch, a wrong symbol selected by the linker, an ignored hardware error, or a seed taken from the clock. Inspecting the output will not tell us which.
Part 3 measured four candidate generators against a funded-address snapshot. Here I want to connect the small-source problem to documented failures, then show an experiment whose expected match count we can actually observe. The benchmark made 150 billion synthetic probes. Its planted case counted 174 matches against 186.26 expected.
The historical cases fall into four useful groups. These identify failure mechanisms; they do not rank severity or assign a common search cost. The benchmark’s bit widths are experimental choices, separate from those incidents.
Integration bypassed the intended source
Debian’s CVE-2008-0166 record describes predictable key material after a packaging change removed entropy-mixing code. “32,768 possible keys” is an imprecise retelling. Debian’s weak-key guidance identifies 32,767 process-ID-derived streams per architecture; actual key sets also depend on key type and generation parameters. The important change was a collapse to a small, reproducible output family.
Android’s 2013 SecureRandom note describes improper initialization of the underlying pseudorandom number generator (PRNG). This could affect key generation and signing. A weak key and a repeated signing nonce are distinct routes to compromise, even when the same integration fault can contribute to both.
The 2026 Ill Bloom disclosure
traced weak BIP39 entropy to a vulnerable CryptoJS.lib.WordArray.random()
implementation using a custom Multiply-With-Carry generator. Exposure depended
on downstream wallet software using that output for mnemonic generation;
including CryptoJS as a dependency was insufficient. The researchers’
chain analysis estimated a
$5,690,922 lower bound through July 13, 2026. This was a longstanding
randomness weakness disclosed in 2026.
Coinkite’s COLDCARD disclosure is a particularly concrete integration failure. A build and link error routed seed generation through MicroPython’s software PRNG instead of the intended hardware random-number generator (RNG). The hardware source was healthy. The wallet-generation path simply did not use it.
Under its stated attack assumptions, Coinkite estimated about 40 bits of effective search for affected Mk2/Mk3 seeds and about 72 bits for later affected models with additional secure-element input. Those are the vendor’s model-specific estimates. A source-code review that finds the correct RNG implementation still has to establish which symbol the final binary calls. The linker gets a vote.
Startup and hardware interfaces supplied too little entropy
FreeBSD has two separate examples. A 2008 advisory
covered insufficient early-boot entropy for arc4random. A
2015 FreeBSD-CURRENT warning
concerned a modular RNG change that omitted randomdev_init_reader. The
affected read_random(9) and arc4random path could generate predictable keys
from revision r273872 until the fix at r278907. The warning called for key
regeneration; HardenedBSD reported downstream exposure.
These were separate faults in specific versions. The 2015 bug did not literally
return 7, either. Its technical analysis
describes a predictable fallback sequence, including a recurrence using 7^5.
That is memorable enough without improving the story.
Lenovo’s XClarity Controller advisory reports predictable hardware RNG values for the first 32 draws after startup or draws less than two microseconds apart. The affected component was a baseboard management controller (BMC) in servers. This is evidence about that controller’s entropy interface, with no demonstrated connection to Bitcoin wallet generation.
Bishop Fox’s IoT research shows what happens when callers ignore an RNG error from a hardware abstraction layer. They can proceed with zeroes, partial data, or uninitialized bytes in the output buffer. The study identifies a broad class of possible mistakes; it does not count demonstrated compromises across that entire population. A functioning physical source cannot help code that accepts a failed read.
The generator was unsuitable for secrets
Milk Sad and the Trust Wallet browser-extension investigation describe different wallet-generation paths beginning with a 32-bit PRNG input. A long mnemonic cannot recover the missing input uncertainty. Enumerating that input space is the relevant attack model, with costs set by each wallet’s complete derivation pipeline.
Mersenne Twister also permits a different attack: an observer with 624 consecutive full 32-bit MT19937 outputs can reconstruct its state. This technical demonstration shows the process. Wallet addresses, truncated values, and hashes do not satisfy that observation condition automatically. Small-seed enumeration and state cloning are separate attacks.
Dual_EC_DRBG had a conditional prediction route. The 2007 Shumow–Ferguson presentation showed that knowing a secret relationship between its public elliptic-curve parameters could permit output prediction. NIST removed the algorithm from its recommendations in 2014. The demonstration establishes a possible trapdoor; it does not prove who held the required secret for the standardized points. A bounded small-seed search cannot represent this mechanism.
Clocks and correlations gave the game away
Goldberg and Wagner’s Netscape analysis found that the UNIX browser seeded its SSL PRNG with the time of day, process ID, and parent process ID. An attacker could narrow those inputs. The paper describes different details on Windows and Mac, so there is no single seed formula to apply to every Netscape build.
The PlanetPoker researchers
found a bounded, clock-seeded shuffle and demonstrated a real-time deck
predictor. They reported no use of the weakness to win money. A deck selected
from a narrow seed population cannot provide the uncertainty of all 52!
permutations; the timing window and implementation determine the actual search.
Marsaglia’s paper, Random Numbers Fall Mainly in the Planes, illustrates a statistical failure of congruential generators. RANDU is a concrete example: its successive triples fall on only 15 planes. Those correlations matter for simulations that assume independent triples. They do not, by themselves, specify a wallet-search cost or establish a historical Bitcoin compromise.
After generation: signing and disclosure
Reusing a nonce in the Elliptic Curve Digital Signature Algorithm (ECDSA)
can expose a key. Two signatures with the right algebraic
relationship can expose the signing key without a seed search. Android’s 2013
failure also had a signing consequence. The separate
nonce_reuse.py example demonstrates that algebra
with synthetic signatures.
The reported implant in Part 0 copied the secret after generation. An ideal RNG cannot protect words read from the display. Both cases belong in the threat model, but neither is a small key-generation space to enumerate.
The strong-source model
BIP39 begins a
12-word mnemonic with 128 bits of entropy and a 24-word mnemonic with 256 bits.
For an independently chosen uniform secret, q distinct guesses have success
probability at most q/2^128 or q/2^256. A sound source leaves no small
initial candidate set for the attacker.
Real devices have to collect and condition entropy to approach that model. Apple’s platform-security guide and Ledger Donjon’s RNG threat model describe designs. NIST SP 800-90B provides entropy-source assessment and health-test guidance. Design documents and finite tests supply evidence about particular properties. They cannot establish that an unknown observer has no predictive information.
Giving the benchmark something to find
The scanner’s zero matches leave a measurement problem: how do we check a matching pipeline when its model predicts almost no matches? I used a separate synthetic-token benchmark, with a known target population large enough to produce observable hits. It generates no Bitcoin keys, mnemonics, addresses, or signatures.
The repository’s illustrative RNG lab is separate. Its supplied fixtures are synthetic; it has no historical wallet fixture corpus or current-holdings matching. Its documentation describes the implemented mappings and their limits. The historical incidents above have their own sources and attack models.
The first benchmark, on October 13, used a fictional fixed-output source and
bounded spaces of 2^12, 2^16, 2^20, and 2^23 states. Targets were placed
near the 75% rank so that the searches would do substantial work. The 23-bit
case found its target after 6,291,457 probes in 3.811 seconds, about
1.65 million tokens/s. Those attempt counts were fixed by construction;
the rate was measured. With --random-target, rank is uniform and expected
full-search work is (2^b + 1)/2, before any budget truncation.
The retained baseline JSON includes the measured timings and configuration. Its idealized 128- and 256-bit controls each made ten million independent draws with replacement and found zero matches. Their single-target hit-probability bounds were about 2.94 × 10^-32 and 8.64 × 10^-71. These budgets cannot distinguish such tiny rates. Running longer without changing the model would mostly produce a larger zero.
For the checkpointed benchmark, we added 2²⁰ distinct planted targets in a 2⁴⁸-state space. Each candidate is an independent uniform draw with replacement. The benchmark hashes it, looks up the token, and checks the full state. The planted target set remains fixed across chunks and resumes.
For M targets in 2^b states and q probes:
p = M / 2^b
expected matches = q*p
variance = q*p*(1-p)
P(at least one match) = 1 - (1-p)^q
Our density gives p = 2^-28. Repeated probes finding the same target count
separately. This is an invented population chosen for measurement; it assigns
no target count or entropy value to a historical wallet incident.
Before launch, a denser local check used 1,024 targets in 2^16 states and
200,000 probes per long case. It observed 3,146 planted matches against
3,125 expected, 0.38 standard deviations above expectation. The
validation record
retains its configuration, implementation hashes, and results.
The completed benchmark
The synthetic benchmark used 32 worker processes and reached the configured probe caps after 7 h 44 min 30 s, before the eight-hour limit.
The bounded calibration targets were uniformly placed on this run:
| Synthetic space | Probes until match |
|---|---|
| Fixed output: one state | 1 |
2^12 |
379 |
2^16 |
23,532 |
2^20 |
996,405 |
2^23 |
7,739,821 |
The three long cases each completed 50 billion probes:
| Synthetic case | Probes | Matches | Probes/s |
|---|---|---|---|
Planted: 2^20 targets in 2^48 states |
50,000,000,000 | 174 | 5.34 million |
| Uniform 128-bit control | 50,000,000,000 | 0 | 5.48 million |
| Uniform 256-bit control | 50,000,000,000 | 0 | 5.37 million |
At that planted budget, the model predicts 186.26 matches, with standard deviation 13.65. The observed 174 is 0.90 standard deviations below expectation. That comparison describes the observation against its model; it is not a pass/fail threshold for a random count.
Each strong-control worker subchunk draws a fresh uniform target. The single-target per-probe probability still applies, but these are not 50 billion attempts against one target fixed for the whole run. The probabilities of at least one match are bounded by 1.47 × 10^-28 and 4.32 × 10^-67.
The long cases ran in rotation, so their rates cannot be added as simultaneous throughput. Compared with an earlier interrupted one-worker execution on the same server, they were 20.3–20.7 times faster. Chunk sizes, schema, duration, and runtime conditions differed. This is a descriptive rate comparison, rather than a controlled CPU-scaling study. The linked guide explains the timing limitations and points to the original results and their implementation hashes.
What the measurements support
The planted count is the useful positive evidence. This implementation detected rare planted matches at the measured scale, with an aggregate count close to the model’s expectation. That does not establish correctness for every input path or reproduce a wallet incident. Small-space calibration and retained source identity provide separate checks.
The strong controls have much less statistical power. Their zero counts agree
with the ideal models, but cannot establish that a device supplied 128 or 256
bits of entropy. Also, the planted case has a million targets while each
strong-control subchunk has one. Space size, target count, and probe count
jointly determine the expected count. Here the density 2^20 / 2^48 = 2^-28
made the positive control observable.
More processors let us collect that evidence sooner. For a fixed workload,
target count, and time budget, a 20-fold rate improvement buys about
log2(20) = 4.3 more bits of searchable space at the same expected match count.
Even an ideal 32-fold gain buys five bits. That helps a bounded experiment;
it does little to close the gap to a uniformly chosen strong secret. These
rates measure synthetic tokens, not Bitcoin address generation.
The series started with a secret reportedly copied from a display. Along the way, we saw small generation spaces, unsuitable algorithms, integration faults, and signing failures. Each has a specific failure point. The benchmark gives us measured search costs for one stated model. The hardware story reminds us that an attacker who can read the secret has no reason to pay those costs.
A lottery with a block reward
Calling random key hunting a “Bitcoin lottery” makes the proposed prize sound rather more innocent than it is. A successful guess against a funded address would give access to somebody else’s coins. There is already a Bitcoin lottery that pays the winner for useful work: solo mining.
Take 1 TH/s of sustained mining and an $80 hardware budget. Solo Satoshi sells small miners in this class; its Bitaxe Gamma listing quotes 1.2 TH/s at $61–$72, depending on options. The $80 is a round-number budget, and I round the rate down to 1 TH/s. Delivery, electricity, and any solo-pool deductions are separate.
Bitcoin’s proof of work
asks for a block-header hash below the target. Freeze difficulty at
132.716 trillion, the historical value recorded at
block 969,696.
At difficulty D, finding a block takes approximately D * 2^32 hashes on
average. For h hashes per second and t seconds:
P(at least one block) ≈ 1 - exp(-h*t / (D*2^32))
For the key search, use Part 3’s 27,637 keys/s, with both legacy P2PKH forms checked for every key. Generously count all 21,198,062 legacy-shaped snapshot entries as valid targets. Under the ideal-hash model:
P(at least one snapshot match) ≤ 2 * keys_per_second * t * 21,198,062 / 2^160
Here are the probabilities over the same time windows, with full uptime and unchanging difficulty and targets:
| Process | One day | One year |
|---|---|---|
| Solo mining at 1 TH/s | ≈1.52 × 10⁻⁷ | ≈5.54 × 10⁻⁵, or 1 in 18,100 |
| Random keys at the measured 27,637 keys/s | ≤6.93 × 10⁻³² | ≤2.53 × 10⁻²⁹ |
| Even an imagined trillion random keys/s | ≤2.51 × 10⁻²⁴ | ≤9.15 × 10⁻²² |
The miner’s annual chance is only 0.0055%. Its mean waiting time at that fixed difficulty is about 18,000 years; a block does not become due when the clock reaches that number. Yet its chance is roughly 2.2 × 10²⁴ times the measured scanner’s upper bound. Even the invented trillion-key rate leaves solo mining about 6 × 10¹⁶ times ahead: sixty quadrillion.
A mining ASIC’s trillion hashes per second cannot be repurposed into a trillion private-key-and-address derivations. That last row deliberately gives the key search an enormous allowance. These are probability comparisons, not a claim that an $80 miner will pay for itself. Mining figures also ignore stale blocks; the address figures concern that fixed legacy snapshot, not every wallet or its later balance. The calculation and inputs are included with the companion scripts.
The failures in this series came from predictable inputs, broken integration, unsafe signing, or a secret being copied. Randomly guessing a sound secret remains a terrible plan. If the attraction is a tiny chance of receiving Bitcoin, solo mining offers much better odds and a reward the protocol actually intends you to collect.
For once, the honest lottery has the better odds.
The question entropy leaves open
The lottery comparison concerns guesses. Bitcoin also gives an attacker mathematical information about the secret: a public key calculated from it.
A private key d determines a public point Q through Q = dG. Here G
is a fixed, public base point on the elliptic curve called secp256k1.
Multiplication means repeated point addition, computed efficiently. The
network uses the public key to verify a transaction signature without being
given the private key. Bitcoin’s ECDSA and
Schnorr signatures
both use this curve.
For the legacy P2PKH addresses checked by our scanner, the diagram separates forward derivation from the two attack routes:
Here the address encodes a hash of the public key. Spending a P2PKH output reveals that public key. Other output types expose different information: Taproot includes an output public key from the start. A fresh receiving address does not automatically mean a hidden public key.
The recent CoinDesk report on Justin Drake’s warning concerns this other route. Drake raised the possibility of an AI-assisted mathematical breakthrough that could make private-key recovery practical from exposed public keys. The report presents a worst-case forecast; it supplies no working attack demonstrating that Bitcoin keys can be recovered this way.
Finding d from Q and G is the elliptic-curve discrete logarithm
problem. The security argument in
BIP340
explicitly assumes that this problem is hard. A practical method for solving
it on secp256k1 would threaten exposed public keys even if their private keys
had been generated with perfect randomness.
We have calculated guessing probabilities under stated assumptions and measured a deliberately weakened model. A huge keyspace and 150 billion synthetic probes cannot prove that no mathematical shortcut exists.
Entropy explains the cost of guessing d. The next question is what it would
take to recover d from dG.