GHSA-vh45-f885-3848 sm-crypto: Predictable SM2 key generation in Node.js: default RNG uses Math.random + wall clock
Summary`sm-crypto` (npm package **0.4.0**, the latest release, published 2026-01-20)
generates SM2 private keys and signing ephemeral scalars from a single
module-wide RNG instance (`src/sm2/utils.js`: `const rng = new SecureRandom()`).
`SecureRandom` is jsbn's PRNG, which seeds an **ARC4** stream from
`window.crypto.getRandomValues` when available. **In Node.js — sm-crypto's
primary runtime — `window` is `undefined`, so the CSPRNG branch is skipped**
and the seed pool is instead filled from `Math.random()` (V8 `xorshift128+`,
recoverable from a few outputs) plus `new Date().getTime()` (wall clock,
attacker-estimable).Node *does* expose Web Crypto as `globalThis.crypto`, but jsbn checks
`window.crypto`, not `globalThis.crypto`, so the secure path is never taken.
Consequently every SM2 private key produced by the default
`sm2.generateKeyPairHex()` and every signing ephemeral scalar is derived from
non-cryptographic sources and is **predictable** by an attacker who can observe
a few `Math.random()` outputs and estimate the generation time.This is the library's **default** (no-argument) path; no caller-selected
parameter or configuration is required to trigger it. It is reproduced
end-to-end against the unmodified real npm packages (`sm-crypto@0.4.0` +
`jsbn@1.1.0`); the PoC below runs against the real installed package, not a
copy. The defect is still present on the latest published version (0.4.0) and
is not covered by any existing `JuneAndGreen/sm-crypto` issue (0 afldl issues
exist; the most recent issues are unrelated SM3/HKDF/PBKDF2 feature requests).Details`jsbn@1.1.0` `index.js` — RNG pool initialization (fallback taken in Node):`sm-crypto` `src/sm2/utils.js`:The default (no-argument) call path uses `rng`, the jsbn ARC4 instance seeded
from `Math.random()` + time. The same `rng` feeds the signing ephemeral
scalar during SM2 signing.PoCThe PoC runs against the real installed npm packages. It pins `Math.random`
and `Date` **before** `require('sm-crypto')` so jsbn's seed pool is built from
controlled inputs. Three independent fresh Node processes then produce the
**same** SM2 private key, proving the key is a pure deterministic function of
those non-cryptographic sources. It also prints a probe confirming the fallback
branch is taken in Node.One-line reproducer`poc.js`Real captured output:The two deterministic runs produce the **identical** SM2 private key,
demonstrating the key is a pure function of `Math.random()` + wall-clock time.Impact**Private-key recovery / signature forgery of any SM2 keypair generated with
the default API in Node.js.** This is the most serious class of defect for a
maintained SM2 library: the *default* key-generation path is
non-cryptographic on its primary runtime.**Private-key recovery.** Any SM2 keypair generated with the default API in
Node is derived from `Math.random()` + wall-clock time. An attacker who can
observe a few `Math.random()` outputs (V8 `xorshift128+` state is
recoverable from ~4 observed doubles) and estimate the generation time can
reproduce the private key and forge signatures.**Signing ephemeral reuse / forgery.** The same RNG feeds the ephemeral
scalar `k` during SM2 signing; a predictable `k` leaks the private key from a
single signature (SM2 is EC-Schnorr-like: `s = (k^-1)(e + d·r) mod n`).Pre-authentication / no privilege required: anyone who can induce a victim
to generate a key or sign a message (the normal API use) is positioned to
predict the secret material.Suggested fixSeed the RNG from a CSPRNG in Node. The simplest fix in `sm-crypto` is to
replace the jsbn ARC4 instance with Web Crypto / `crypto.randomBytes`:and use it to generate the private key / ephemeral directly, or to reseed the
jsbn pool. A separate (upstream) fix belongs in jsbn to check
`globalThis.crypto` in addition to `window.crypto`.Affected versionsnpm `sm-crypto` **0.4.0** (latest, published 2026-01-20). Depends on
`jsbn ^1.1.0` (`jsbn@1.1.0`, whose `index.js` RNG is the root cause).Runtime: Node.js (the primary runtime; in a browser the jsbn CSPRNG branch
is taken).CreditReported by the diff/ambidiff security research effort (afldl).
CVSS v3.1 score: 9.1