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):
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
Original advisory: https://github.com/advisories/GHSA-vh45-f885-3848
More from GitHub Security Advisories
- mediumGHSA-jr6p-8pjj-mfx6: Capsule has an incomplete fix of CVE-2026-22872: TenantResource RawItems and Generators s…2026-07-31
- mediumGHSA-68cj-mvg9-rgm2: Capsule: CapsuleConfiguration NodeMetadata regex fields lack webhook validation, allowing…2026-07-31
- mediumGHSA-ff84-5f28-78qj: re2: Out-of-bounds heap read in `exec`/`test`/`match` via attacker-influenced `lastIndex`…2026-07-31
- mediumGHSA-6hxr-mr5r-9836: re2: Global `String.prototype.match` with an empty-matchable pattern never advances → inf…2026-07-31
- mediumGHSA-x83g-979r-f5fh: Sylius Mollie Plugin has unauthenticated IDOR that leaks order token and customer PII2026-07-31