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.