Intelligence
criticalVulnerabilityActive

Cryptographic Fallback to Non-CSPRNG in sm-crypto: Node.js SM2 Key Generation Predictability

sm-crypto 0.4.0 falls back to Math.random() + wall-clock seeding for SM2 private key generation on Node.js because jsbn's SecureRandom checks window.crypto instead of globalThis.crypto. This renders all default SM2 keys cryptographically predictable.

S
Sebastion

Affected

sm-crypto/0.4.0jsbn/1.1.0

Vulnerability Description

The vulnerability is a cryptographic fallback to non-CSPRNG sources stemming from environment detection failure. The sm-crypto package (v0.4.0) relies on jsbn's SecureRandom class to seed ephemeral randomness for SM2 elliptic-curve private key generation. The root cause is a version mismatch between Web API standards: jsbn checks the legacy window.crypto object (browsers only), while Node.js exposes cryptographic randomness via globalThis.crypto.getRandomValues(). When window is undefined (Node.js), jsbn silently falls back to seeding its ARC4 stream from Math.random() (V8's xorshift128+ PRNG, state-recoverable from ~2-4 outputs) combined with new Date().getTime() (wall-clock milliseconds, within attacker estimation tolerance). This affects the default code pathsm2.generateKeyPairHex() with no arguments — meaning all applications using the library without custom RNG injection generate predictable private keys and signing nonces.

Proof-of-Concept Significance

The PoC validates end-to-end exploitation against real npm packages (sm-crypto@0.4.0 + jsbn@1.1.0) without code modification. Its significance is that it demonstrates: (1) the fallback is reliably triggered by default in Node.js environments, (2) the state of V8's xorshift128+ is recoverable from a small number of observed Math.random() outputs, and (3) an attacker observing key material generation time (or brute-forcing within a narrow window) can derive all future private keys and signing scalars. This is not a theoretical timing side-channel — it is a deterministic functional defect in the library's runtime environment detection logic. The PoC's importance for defenders is establishing that every sm-crypto-generated key in production Node.js environments is cryptographically broken.

Detection Guidance

Application-level signals:

  • Monitor npm audit / dependency scanner logs for sm-crypto versions < 1.0.0 (assumed patched version) in Node.js projects.
  • Check for imports of sm-crypto in production code; flag any use of generateKeyPairHex() without explicit CSPRNG injection.
  • Review package-lock.json / yarn.lock for sm-crypto@0.4.0 and jsbn@1.1.0 co-presence.

Runtime signals:

  • No direct binary signatures (no shellcode/exploit code), but monitor for: (a) repeated calls to sm2.generateKeyPairHex() in rapid succession (batch key generation is especially dangerous with weak randomness), (b) keys generated within a narrow time window (indicates wall-clock predictability).
  • Log all SM2 key generation events with timestamps and source; correlate with Math.random() seeding if available via profiling.

YARA / code inspection:

  • Scan source for patterns: require('sm-crypto') + generateKeyPairHex() without custom RNG parameter.
  • Dependency tree analysis: flag if jsbn is required and window.crypto checks exist without globalThis.crypto fallback.

Mitigation Steps

Immediate (within hours):

  1. Inventory: Identify all Node.js applications using sm-crypto. Request cryptographic audit of all SM2 keys generated during deployment window.
  2. Rotate keys: Do not reuse sm-crypto-generated SM2 private keys. Regenerate using a patched version or alternative library.
  3. Revoke signatures: SM2 signatures created with predictable nonces must be considered invalid; cryptographic proofs using these keys are compromised.

Short-term (patch/upgrade):

  1. Await patched release of sm-crypto (expected: jsbn fix to check globalThis.crypto on Node.js, or sm-crypto to use Node's native crypto.getRandomBytes() for seed).
  2. Workaround (if patch delayed): Inject a custom CSPRNG into sm2 module initialization:
    • Use Node's native require('crypto').randomBytes() to seed SecureRandom manually.
    • Wrap generateKeyPairHex() calls with explicit seeding before each invocation.
  3. Switch to alternative SM2 libraries with explicit Node.js support (e.g., native implementations not relying on legacy Web API assumptions).

Long-term:

  1. Enforce dependency policies: require CSPRNG-using libraries to explicitly check runtime (Web vs. Node vs. Worker) and use appropriate APIs.
  2. Automated testing: include cryptographic randomness validation in CI/CD (e.g., NIST entropy tests on key material).
  3. Code review: flag any use of Math.random() or Date.now() in cryptographic contexts.

Risk Assessment

Likelihood of exploitation in the wild: HIGH

  • sm-crypto is actively used in SM2/SM4 Chinese cryptography implementations and enterprise China-region deployments.
  • The vulnerability is trivial to exploit (no special privileges, network access, or user interaction needed) if an attacker can observe generated keys or has access to generation timestamps.
  • The default code path is what most developers will use; no configuration knowledge is required to trigger the defect.

Threat actor interest: CRITICAL

  • Nation-state and commercial cryptanalysis teams targeting Chinese cryptographic infrastructure will prioritize recovery of sm-crypto keys.
  • Financial/banking systems using SM2 for digital signatures in CITIC, Alipay, WeChat Pay deployments are high-value targets.
  • Backdating of signatures is possible if nonce recovery is feasible; affects compliance and forensic integrity.

CVSS estimate (v3.1): 9.1 CRITICAL CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N (cryptographic key recovery + signature forgery on default path).