{"id":"GHSA-6889-vf6x-f3g5","summary":"Forge: Ed25519 signature verification accepts small-order public keys","details":"### Summary\n`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.\n\n### Details\n`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 \u003e= p`.\n\nWith `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 \u003c L` check added for CVE-2026-33895 (GHSA-q67f-28xg-22rw) doesn't apply, because `S = 0` is canonical.\n\nKeys 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.\n\nRFC 8032 doesn't require rejecting small-order public keys, and implementations differ. libsodium rejects them, and Node.js `crypto.verify()` (OpenSSL) rejects these signatures.\n\nThe fix rejects a public key `A` when `[8]A` is the identity point. This covers all small-order points and their non-canonical encodings.\n\n#### Patches\nFixed in node-forge 1.4.3. `ed25519.verify()` returns `false` for small-order public keys.\n\n#### Workarounds\nBefore 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.\n\n#### Resources\n- RFC 8032, Section 5.1.7 (Ed25519 verification): https://www.rfc-editor.org/rfc/rfc8032#section-5.1.7\n- 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\n\n#### Severity\nCVSS 3.1: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N (3.7, Low)\nCVSS 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)\nCVSS 4.0 rates this Moderate because its integrity weighting is higher; the severity rating follows CVSS 3.1.\n\n#### Credits\nReported by @analisaperlengkapan.\n\n### Proof of concept\nIdentity public key `A = 01 00 … 00` (32 bytes) and signature `R = 01 00 … 00`, `S = 00 … 00` (64 bytes):\n\n    forge.ed25519.verify({message: 'admin=true', encoding: 'utf8',\n      signature: SIG, publicKey: IDENTITY});\n    // \u003c= 1.4.2: true for any message; node:crypto.verify(): false\n    // 1.4.3: false\n\nOther 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`.\n\nRegression tests are in tests/unit/ed25519.js, and the reporter's PoC is in tests/pocs/ghsa-6889-vf6x-f3g5.js.\n\n### Impact\nApplications 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.","aliases":["CVE-2026-125335"],"modified":"2026-11-10T21:30:03.999616287Z","published":"2026-11-10T21:18:23Z","database_specific":{"nvd_published_at":null,"cwe_ids":["CWE-347"],"severity":"LOW","github_reviewed":true,"github_reviewed_at":"2026-11-10T21:18:23Z"},"references":[{"type":"WEB","url":"https://github.com/digitalbazaar/forge/security/advisories/GHSA-6889-vf6x-f3g5"},{"type":"WEB","url":"https://github.com/digitalbazaar/forge/pull/1169"},{"type":"WEB","url":"https://github.com/digitalbazaar/forge/commit/1b77e6f20082d7484479303d0252d5f427651295"},{"type":"PACKAGE","url":"https://github.com/digitalbazaar/forge"},{"type":"WEB","url":"https://github.com/digitalbazaar/forge/releases/tag/v1.4.3"}],"affected":[{"package":{"name":"node-forge","ecosystem":"npm","purl":"pkg:npm/node-forge"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"1.4.3"}]}],"database_specific":{"last_known_affected_version_range":"\u003c= 1.4.2","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/11/GHSA-6889-vf6x-f3g5/GHSA-6889-vf6x-f3g5.json"}}],"schema_version":"1.9.2","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N"}]}