CSIRTS // UNIFIED SECURITY ADVISORY FEEDSYS ● ONLINE · POWERED BY INTELFUSIONS.COM

GHSA-vh45-f885-3848: sm-crypto: Predictable SM2 key generation in Node.js: default RNG uses Math.random + wall clock

criticalCVSS 9.1
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): if (rng_pool == null) { rng_pool = new Array(); rng_pptr = 0; var t; if (typeof window !== "undefined" && window.crypto) { // <-- false in Node if (window.crypto.getRandomValues) { /* webcrypto */ } ... } while (rng_pptr < rng_psize) { // <-- fallback path t = Math.floor(65536 * Math.random()); // Math.random() rng_pool[rng_pptr++] = t >>> 8; rng_pool[rng_pptr++] = t & 255; } rng_ppt

Details

Source
GitHub Security Advisories (INTL · database · site)
Severity
critical — CVSS 9.1
Published
2026-07-24
Last updated
2026-07-24
Exploitation
Not in CISA KEV at last sync

Original advisory: https://github.com/advisories/GHSA-vh45-f885-3848

More from GitHub Security Advisories