CVE-2026-125335: Forge: Ed25519 signature verification accepts small-order public keys
Summary ed25519.verify() does not reject public keys of small order, such as the identity point. Under such a key, a signature with S = 0 and a small-order R verifies for many messages, or for every message with the identity key, without any private key. Fixed in node-forge 1.4.3.
Details cryptosignopen() in lib/ed25519.js decodes the public key A with unpackneg(), which only checks that the point is on the curve, and then checks R == [S]B - [k]A with k = SHA-512(R || A || M). Nothing rejects public keys of small order: the identity point, the other 7 points of order 2, 4 or 8, and their non-canonical encodings with y >= p.
With S = 0, the check becomes R == k. For a small-order A, k is one of at most 8 small-order points, so an attacker can make a valid signature by trying the small-order values of R. For the identity key (01 00 … 00), R = identity and S = 0 verifies for every message. For the other small-order keys, a given message can be forged with high probability. No private key is involved. The S < L check added for CVE-2026-33895 (GHSA-q67f-28xg-22rw) doesn't apply, because S = 0 is canonical.
Keys produced by Ed25519 key generation (ed25519.generateKeyPair() or any RFC 8032 implementation) are never small-order, so signatures under them cannot be forged this way. Applications are affected when they verify signatures against a public key an attacker can choose (for example a key included with the signed data, a self-registered key, or a key in an attacker-controlled document) and rely on the signature being bound to the message: proof of possession, attribution, non-repudiation or deduplication.
RFC 8032 doesn't require rejecting small-order public keys, and implementations differ. libsodium rejects them, and Node.js crypto.verify() (OpenSSL) rejects these signatures.
The fix rejects a public key A when [8]A is the identity point. This covers all small-order points and their non-canonical encodings.
Patches Fixed in node-forge 1.4.3. ed25519.verify() returns false for small-order public keys.
Workarounds Before calling ed25519.verify() with a public key from untrusted input, reject keys whose 32-byte encoding is one of the small-order points (the identity 01 00 … 00 and the other encodings that libsodium blocks), or only verify against keys you generated or have otherwise validated.
Resources - RFC 8032, Section 5.1.7 (Ed25519 verification): https://www.rfc-editor.org/rfc/rfc8032#section-5.1.7 - GHSA-q67f-28xg-22rw / CVE-2026-33895 (a different Ed25519 verification issue, fixed in 1.4.0): https://github.com/digitalbazaar/forge/security/advisories/GHSA-q67f-28xg-22rw
Severity CVSS 3.1: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N (3.7, Low) CVSS 4.0 (informational): CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N (6.3) CVSS 4.0 rates this Moderate because its integrity weighting is higher; the severity rating follows CVSS 3.1.
Credits Reported by @analisaperlengkapan.
Proof of concept Identity public key A = 01 00 … 00 (32 bytes) and signature R = 01 00 … 00, S = 00 … 00 (64 bytes):
forge.ed25519.verify({message: 'admin=true', encoding: 'utf8', signature: SIG, publicKey: IDENTITY}); // <= 1.4.2: true for any message; node:crypto.verify(): false // 1.4.3: false
Other small-order public keys (orders 2, 4 and 8, and non-canonical encodings) give forgeries for many messages with S = 0 and a small-order R.
Regression tests are in tests/unit/ed25519.js, and the reporter's PoC is in tests/pocs/ghsa-6889-vf6x-f3g5.js.
Impact Applications that verify Ed25519 signatures with node-forge against a public key that an attacker can choose are affected. An attacker can provide a small-order public key and then produce signatures that verify for many or all messages without a private key, defeating checks that rely on a signature being bound to its message under that key (proof of possession, attribution, non-repudiation, deduplication). Signatures under keys from honest key generation cannot be forged, and applications that only verify against their own or otherwise trusted keys are not affected.
Other sources
Forge (also called node-forge) is a native implementation of Transport Layer Security in JavaScript. Prior to 1.4.3, ed25519.verify() decodes a public key in cryptosignopen() without rejecting small-order points or their non-canonical encodings. With S = 0 and a small-order R value, an attacker who can choose the public key can produce signatures that verify for many messages, or all messages with the identity key, without a private key. This can defeat proof-of-possession, attribution, non-repudiation, or deduplication checks that rely on a signature being bound to the message. Keys produced by normal Ed25519 key generation are not small-order, so applications that verify only trusted or internally generated public keys are not affected. This issue is fixed in version 1.4.3.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
npm/node-forgeto a version that resolves this vulnerability.Fixed in 1.4.3 - Upgrade
Upgrade
node-forgeto a version that resolves this vulnerability.Fixed in 1.4.3 - Compensating control
Before calling ed25519.verify() with a public key from untrusted input, reject keys whose 32-byte encoding is a small-order point, including the identity point and other encodings blocked by libsodium; alternatively, verify only against keys generated by the application or otherwise validated.
Event History
Frequently Asked Questions
Which applications are exposed to this issue?
Applications are exposed if they use node-forge Ed25519 verification with public keys an attacker can choose or supply. Applications that verify only trusted or internally generated public keys are not affected, because normal Ed25519 key generation does not produce small-order keys.
What does an attacker need to exploit it?
The attacker needs to be able to choose the public key used for verification. They can then use a small-order public key and a signature with S = 0 and a small-order R value to obtain verification without possessing the corresponding private key.
What security controls can this bypass?
The issue can defeat checks that assume a verified signature is bound to a message, including proof-of-possession, attribution, non-repudiation, and deduplication checks. With the identity key, signatures may verify for all messages.
What version fixes the issue?
Upgrade node-forge to version 1.4.3. Versions prior to 1.4.3 do not reject small-order public-key points or their non-canonical encodings during Ed25519 verification.