Math.random() is not a key generator. Everyone knows this. And yet — when a widely deployed SM2 library seeded its key material at module load, that's effectively what happened. Except worse: in Node.js, the randomness pool was never even filled.
This is the story of CVE-2026-73567, a vulnerability I reported and got credited for after independent reproduction by the maintainer, and what it teaches us about the whole class of weak-entropy key compromises.
The discovery
While auditing key-generation paths in SM2 (the Chinese national elliptic-curve standard, mandatory in regulated industries there), I noticed something odd in the dependency chain: a big-number library harvested entropy from Math.random() — the non-cryptographic, browser-grade PRNG — at module initialization, and used it to seed its internal state. That state then fed private-key generation.
The publicly discussed impact assumed ~128 Math.random() calls at startup. When I built an instrumented Node victim and counted, the real number was zero. V8 defers Math.random()'s pool initialization in a way that left the seeding path reading predictable values — the effective root cause was a clock-only seed, and the advisory's root cause was corrected accordingly.
The consequence: given roughly a millisecond-scale time window, private keys were recoverable by enumeration. Sub-second. On an ordinary laptop.
And it didn't stop at static keys. The same RNG stream also fed signing nonces and ephemeral encryption keys. That means a key used only for signing was equally recoverable, and ciphertext from affected versions could be decrypted without the key.
What happened next
- The maintainer independently reproduced my findings, corrected the advisory's root-cause description, expanded the impact statement (nonces, ephemerals), and shipped a fix requiring caller-supplied CSPRNG.
- My credit line — "Guigui Wang (Correctover)" — is now part of the public advisory.
- Versions before the fix remain vulnerable forever. Every key generated by an unpatched build in a predictable-time window should be considered compromised.
This is a class, not an incident
One library got caught. But weak entropy is a systematic failure mode across the key-management stack:
- V8's PRNG (
Math.random(), xorshift128+ / MWC1616 eras) can be fully state-recovered from a handful of consecutive outputs — meaning any JS wallet or signing code that ever touches non-CSPRNG randomness is one observation away from compromise. - Historical events (Randstorm-class bugs, 2014-era wallet RNG flaws) show the same pattern repeating across ecosystems.
- Cross-address clustering: weak-RNG victims often reuse exposed material across chains and address types, turning one bad key into a trail of them.
- The SM2 ecosystem is structurally exposed here: guomi compliance pushes SM2 into places where the surrounding JS/Node key hygiene was never designed for it.
If your product generates keys, signs, or encrypts — in JavaScript especially — the question isn't whether your crypto is strong. It's whether your entropy path is.
What we do about it
At Correctover we recover keys from weak entropy — to prove the exposure before someone malicious does, and to build evidence that holds up:
- Black-box weak-entropy health checks on key generation and signing paths
- Real recovery demonstrations in isolated environments, with signed, timestamped evidence receipts (Ed25519 + RFC 3161) — findings a court or auditor can verify without trusting us
- Coordinated disclosure done right: the advisory process above is our reference case
If you build wallets, custody, or exchange infrastructure and want your entropy path audited against this exact attack class, reach out: wangguigui@correctover.com.
Guigui Wang is the credited reporter of CVE-2026-73567 (GHSA-vh45-f885-3848). Correctover builds runtime verification and evidence infrastructure — receipts over promises.

