Skip to content

RSA PKCS#1 v1.5 signature forgery via garbage in DigestAlgorithm (incomplete CVE-2026-33894 fix) #1149

Description

@geo-chen

reported via email on 4 June 2026 - no response.

The CVE-2026-33894 fix (af094e6) is incomplete. It only checks the top-level DigestInfo element count:

lib/rsa.js:1174 if(!asn1.validate(obj, digestInfoValidator, capture, errors) || obj.value.length !== 2) { ... }

The nested DigestAlgorithm SEQUENCE has no element-count check, and asn1.validate ignores extra children. An attacker can stuff garbage bytes inside DigestAlgorithm; node-forge still accepts the DigestInfo and captures the attacker's digest. Because _parseAllDigestBytes defaults to true, trailing garbage is blocked, but garbage inside the structure is not.

I confirmed the parser gap on 1.4.0 (a DigestAlgorithm with [OID, NULL, garbage] validates, top-level length stays 2, attacker digest captured), and then forged a working signature end to end on an e=3 key with no private key:

node-forge version: 1.4.0
pub.verify(forged) => true (message "forged-by-attacker-no-privkey")
control (forged sig vs different message) => false

The forgery uses the standard low-exponent technique: the 32-byte digest is fixed at the bottom of the block, the ASN.1/padding framing at the top, and ~314 free "garbage" bytes inside DigestAlgorithm absorb the middle of S^3 so that S^3 is an exact cube matching a block node-forge accepts.

Suggested fix: require the DigestAlgorithm AlgorithmIdentifier SEQUENCE to contain exactly the OID plus optional NULL (add a nested value.length check), or make asn1.validate reject unconsumed children. Validating element counts at every nesting level (not just the outer SEQUENCE) closes the remaining slack.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions