Feb 15, 2026·6 min read·13 visits
The `sm-crypto` library (used for Chinese National Standard algorithms) failed to verify if ECC points belonged to the correct curve. Attackers can send malicious points on 'weak' curves to the decryption function, calculate the private key modulo small numbers, and reconstruct the full key, breaking all confidentiality and integrity.
A critical vulnerability in the popular `sm-crypto` library allows attackers to recover private keys via Invalid Curve Attacks. By failing to validate that incoming public keys and ciphertext points actually reside on the specified elliptic curve, the library performs arithmetic operations on weak, attacker-controlled curves. This enables a classic side-channel attack where the private key is leaked in small chunks and reconstructed using the Chinese Remainder Theorem.
In the world of cryptography, implementation details are everything. You can have the mathematically strongest algorithm on the planet—which SM2 (the Chinese National Standard equivalent to ECDSA/ECDH) claims to be—but if you implement it in JavaScript and forget the safety rails, you're asking for trouble.
sm-crypto is the de facto library for Node.js and browser environments needing to support SM2, SM3, and SM4 algorithms. It's widely used in applications that interact with Chinese financial systems, government portals, or any system adhering to GM/T standards. It's the bridge between modern web apps and national compliance.
But here's the catch: implementing Elliptic Curve Cryptography (ECC) is notoriously difficult. Unlike RSA, where the math is mostly big integer modular arithmetic, ECC relies on point addition and scalar multiplication on specific curves. CVE-2026-23966 is a textbook example of what happens when developers assume input data is benevolent. The library assumed that any point passed to it for decryption was a valid point on the SM2 curve. It was not.
To understand this vulnerability, you need a quick refresher on ECC. An elliptic curve is defined by an equation, typically $y^2 = x^3 + ax + b$. The security of the system relies on the "hard" problem of finding the private key $d$ given a public point $Q = dG$, where $G$ is a base point on the curve.
The SM2 decryption process involves the receiver using their private key $d$ to multiply a point $C_1$ provided by the sender ($S = [d]C_1$). The flaw in sm-crypto was strictly a logic error: it never checked if the point $C_1$ provided by the sender actually satisfied the equation $y^2 = x^3 + ax + b$ for the standard SM2 parameters.
Why does this matter? The point addition formulas used in ECC generally only depend on the parameter $a$, not $b$. If an attacker provides a point that lies on a different curve (one with the same $a$ but a different $b$), the library will happily perform the multiplication $d * C_1$ on that invalid curve. These invalid curves often have much smaller orders (fewer points), making the discrete logarithm problem trivial to solve. This is known as an Invalid Curve Attack.
The fix is surprisingly simple, which highlights how glaring the omission was. The developers simply added a validation step before processing the input. Below is the critical diff from src/sm2/index.js.
Before the fix, the code blindly decoded the hex string into a point object and started crunching numbers. It was like accepting a package and opening it without checking the shipping label.
// VULNERABLE CODE (Before)
// The code takes the ciphertext (encryptData), extracts C1, and uses it.
const c1 = _.getGlobalCurve().decodePointHex('04' + encryptData.substr(0, 128))
// ... proceeds to use c1 in scalar multiplication with private key ...After the fix, they invoke _.verifyPublicKey, which asserts that the point actually satisfies the curve equation.
// FIXED CODE (Commit b1c824e5)
const c1 = _.getGlobalCurve().decodePointHex('04' + encryptData.substr(0, 128))
// The Shield: Verify the point is actually on the curve
+ if (!c1 || !_.verifyPublicKey('04' + encryptData.substr(0, 128))) {
+ return output === 'array' ? [] : ''
+ }They also patched the signature verification logic to ensure the signer's public key $P_A$ is valid and that signature components $r, s$ are within the valid range $[1, n-1]$. This prevents similar shenanigans during signature verification.
So, how does an attacker actually steal the private key? It's not a one-shot exploit; it's an interactive interrogation of the server.
Step 1: Curve Selection The attacker generates a series of "invalid" curves. These curves share the $a$ parameter with the SM2 curve but have different $b$ parameters. The attacker specifically chooses curves where the number of points (the order) contains small prime factors (e.g., 3, 5, 7, ...).
Step 2: The Probe The attacker sends a ciphertext where the $C_1$ point lies on one of these weak curves. The server computes $S = [d]C_1$. If the decryption "succeeds" (or fails in a specific way that indicates the math worked but the padding/hash check failed), the attacker learns $d \pmod{\text{small_order}}$.
Step 3: Reconstruction By repeating this process with many different weak curves, the attacker collects a set of congruences: $d \equiv r_1 \pmod{p_1}$ $d \equiv r_2 \pmod{p_2}$ ... Using the Chinese Remainder Theorem (CRT), the attacker stitches these small pieces of information together to reconstruct the exact private key $d$. It's elegant, mathematical, and devastatingly effective.
The severity of this vulnerability cannot be overstated. We are talking about Private Key Recovery. This isn't just about decrypting one message; it's about owning the identity of the receiver.
The immediate fix is to upgrade sm-crypto to version 0.3.14 or later. This version includes the necessary point validation checks.
However, patching is not enough if you suspect your endpoint has been exposed to the public internet. Because this attack recovers the private key, you must assume your current keys are compromised.
Remediation Steps:
npm install sm-crypto@latest.CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
sm-crypto JuneAndGreen | < 0.3.14 | 0.3.14 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-345 |
| Attack Vector | Network |
| CVSS Score | 9.1 (Critical) |
| Impact | Private Key Recovery |
| Exploit Status | PoC Available (Theoretical) |
| EPSS Score | 0.00007 |
Insufficient Verification of Data Authenticity