Every CVE Search/ Get API key

CVE-2026-73567

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

9.1 CriticalFix available

sm-crypto provides JavaScript implementations of the Chinese cryptographic algorithms SM2, SM3, and SM4. Prior to 0.5.0, the default no-argument sm2.generateKeyPairHex() path in Node.js uses the module-wide SecureRandom instance in src/sm2/utils.js, supplied by [email protected], which seeds an ARC4 stream from Math.random() and new Date().getTime() because window.crypto.getRandomValues is unavailable even though globalThis.crypto exists. An attacker who can observe the process's Math.random() outputs and estimate the key-generation time can reconstruct the seed, recover generated SM2 private keys, and predict signing ephemeral scalars used to forge signatures. This issue is fixed in version 0.5.0.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
High
Integrity
High
Availability
None

Exploit likelihood

0.44%Up 0.1 points since Aug 14, 2026
90%50%10%1%

What each source reports

CVE List Score used 9.1

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

sm-crypto provides JavaScript implementations of the Chinese cryptographic algorithms SM2, SM3, and SM4. Prior to 0.5.0, the default no-argument sm2.generateKeyPairHex() path in Node.js uses the module-wide SecureRandom instance in src/sm2/utils.js, supplied by [email protected], which seeds an ARC4 stream from Math.random() and new Date().getTime() because window.crypto.getRandomValues is unavailable even though globalThis.crypto exists. An attacker who can observe the process's Math.random() outputs and estimate the key-generation time can reconstruct the seed, recover generated SM2 private keys, and predict signing ephemeral scalars used to forge signatures. This issue is fixed in version 0.5.0.

Record
CVE-2026-73567
CVSS 3.1
9.1 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
Weakness
CWE-338
Updated
2026-11-02
EUVDEUVD-2026-58182 9.1

sm-crypto provides JavaScript implementations of the Chinese cryptographic algorithms SM2, SM3, and SM4. Prior to 0.5.0, the default no-argument sm2.generateKeyPairHex() path in Node.js uses the module-wide SecureRandom instance in src/sm2/utils.js, supplied by [email protected], which seeds an ARC4 stream from Math.random() and new Date().getTime() because window.crypto.getRandomValues is unavailable even though globalThis.crypto exists. An attacker who can observe the process's Math.random() outputs and estimate the key-generation time can reconstruct the seed, recover generated SM2 private keys, and predict signing ephemeral scalars used to forge signatures. This issue is fixed in version 0.5.0.

Record
EUVD-2026-58182
CVSS 3.1
9.1 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-vh45-f885-3848 9.1

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 ([email protected] + [email protected]); 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

[email protected] 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_pptr = 0;
  rng_seed_time();                                           //     + Date.getTime()
}

sm-crypto src/sm2/utils.js:

const { SecureRandom } = require('jsbn');
const rng = new SecureRandom();                              // single module-wide RNG
...
function generateKeyPairHex(a, b, c) {
  const random = a ? new BigInteger(a, b, c)
                   : new BigInteger(n.bitLength(), rng);     // uses rng
  const d = random.mod(n.subtract(BigInteger.ONE)).add(BigInteger.ONE); // private key
  ...
}

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.

PoC

The 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

WORK=$(mktemp -d) && cd "$WORK" && npm init -y >/dev/null \
  && npm install [email protected] [email protected] >/dev/null \
  && export NODE_PATH="$WORK/node_modules" \
  && node poc.js probe && node poc.js deterministic && node poc.js deterministic

poc.js

/*
 * PoC for sm-crypto predictable default RNG in Node.js.
 *
 * sm-crypto (npm 0.4.0) generates SM2 private keys / ephemeral scalars using
 * jsbn's SecureRandom. In a browser jsbn seeds ARC4 from window.crypto, but in
 * Node.js `window` is undefined so the CSPRNG branch is skipped and the pool is
 * filled from Math.random() plus new Date().getTime(). Both are
 * non-cryptographic; the time is…
Record
GHSA-vh45-f885-3848
CVSS 3.1
9.1 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
Weakness
CWE-338
Updated
2026-11-02
NVD 9.1

sm-crypto provides JavaScript implementations of the Chinese cryptographic algorithms SM2, SM3, and SM4. Prior to 0.5.0, the default no-argument sm2.generateKeyPairHex() path in Node.js uses the module-wide SecureRandom instance in src/sm2/utils.js, supplied by [email protected], which seeds an ARC4 stream from Math.random() and new Date().getTime() because window.crypto.getRandomValues is unavailable even though globalThis.crypto exists. An attacker who can observe the process's Math.random() outputs and estimate the key-generation time can reconstruct the seed, recover generated SM2 private keys, and predict signing ephemeral scalars used to forge signatures. This issue is fixed in version 0.5.0.

Record
CVE-2026-73567
CVSS 3.1
9.1 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
Weakness
CWE-338
Updated
2026-11-02
OSV 9.1

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

sm-crypto provides JavaScript implementations of the Chinese cryptographic algorithms SM2, SM3, and SM4. Prior to 0.5.0, the default no-argument sm2.generateKeyPairHex() path in Node.js uses the module-wide SecureRandom instance in src/sm2/utils.js, supplied by [email protected], which seeds an ARC4 stream from Math.random() and new Date().getTime() because window.crypto.getRandomValues is unavailable even though globalThis.crypto exists. An attacker who can observe the process's Math.random() outputs and estimate the key-generation time can reconstruct the seed, recover generated SM2 private keys, and predict signing ephemeral scalars used to forge signatures. This issue is fixed in version 0.5.0.

Record
CVE-2026-73567
CVSS 3.1
9.1 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
Weakness
CWE-338
Updated
2026-11-02
OSVGHSA-vh45-f885-3848 9.1

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 ([email protected] + [email protected]); 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

[email protected] 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_pptr = 0;
  rng_seed_time();                                           //     + Date.getTime()
}

sm-crypto src/sm2/utils.js:

const { SecureRandom } = require('jsbn');
const rng = new SecureRandom();                              // single module-wide RNG
...
function generateKeyPairHex(a, b, c) {
  const random = a ? new BigInteger(a, b, c)
                   : new BigInteger(n.bitLength(), rng);     // uses rng
  const d = random.mod(n.subtract(BigInteger.ONE)).add(BigInteger.ONE); // private key
  ...
}

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.

PoC

The 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

WORK=$(mktemp -d) && cd "$WORK" && npm init -y >/dev/null \
  && npm install [email protected] [email protected] >/dev/null \
  && export NODE_PATH="$WORK/node_modules" \
  && node poc.js probe && node poc.js deterministic && node poc.js deterministic

poc.js

/*
 * PoC for sm-crypto predictable default RNG in Node.js.
 *
 * sm-crypto (npm 0.4.0) generates SM2 private keys / ephemeral scalars using
 * jsbn's SecureRandom. In a browser jsbn seeds ARC4 from window.crypto, but in
 * Node.js `window` is undefined so the CSPRNG branch is skipped and the pool is
 * filled from Math.random() plus new Date().getTime(). Both are
 * non-cryptographic; the time is…
Record
GHSA-vh45-f885-3848
CVSS 3.1
9.1 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
Weakness
CWE-338
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
sm-crypto
npm
before 0.5.0
0.5.0

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-73567 is a critical vulnerability (CVSS 9.1) in Juneandgreen Sm-crypto, published July 24, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.44%, the 38th percentile. Fixed in sm-crypto 0.5.0.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open