Forge: Ed25519 signature verification accepts small-order public keys
Published 10 Nov 2026 · Updated 10 Nov 2026
Is your project on an affected node-forge?
Affected package
- Affected
- <1.4.3
- Fixed in
- 1.4.3
npm install node-forge@1.4.3Details
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
crypto_sign_open() 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](-A). For a small-order A, [k](-A) 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.
Is GHSA-6889-vf6x-f3g5 in any of your client repos?
Checking one package by hand is fine. Checking it across forty client projects is not. NPMscan reads the lockfiles in every GitHub repo you pick, finds each project with an affected node-forge, and gives you the command that fixes it.
More advisories for node-forge
- MediumCVE-2026-125332Forge: TLS client crash on an unsupported ServerHello compression method · 10 Nov 2026
- MediumCVE-2026-125333Forge: ReDoS in forge.http response header and cookie trimming · 10 Nov 2026
- MediumCVE-2026-125334Forge: TLS client crash on a handshake record received without a session · 10 Nov 2026
- LowCVE-2026-118774Forge: Certificate signature verified with the issuer's RSA signature scheme · 10 Nov 2026
- LowCVE-2026-118776Forge: Incomplete PKCS#7 padding validation in AES/DES CBC and ECB decryption · 10 Nov 2026
- MediumCVE-2026-119189Forge: X.509 pathLenConstraint not enforced by verifyCertificateChain() for CA certificates without keyUsage · 10 Nov 2026
References
Advisory data from OSV.dev (opens OSV.dev in a new tab), refreshed daily.