From a1660bc016cfe9f0b5fa15ee4192876ee9e82da4 Mon Sep 17 00:00:00 2001 From: RRFZ <247457381+Plastic-time@users.noreply.github.com> Date: Wed, 4 Nov 2026 12:18:40 -0500 Subject: [PATCH] fix(pe): reject certificates whose signature algorithms disagree (#8922) * fix(pe): reject certificates whose signature algorithms disagree * test(pe): keep the signature algorithm case apart from the version case --- src/native/pe/PeCertificateAsn1.ts | 9 ++++++++ src/native/pe/PeCertificateParser.test.ts | 25 +++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/native/pe/PeCertificateAsn1.ts b/src/native/pe/PeCertificateAsn1.ts index f4f1c20e8b..0b8f760798 100644 --- a/src/native/pe/PeCertificateAsn1.ts +++ b/src/native/pe/PeCertificateAsn1.ts @@ -484,6 +484,15 @@ export const parseCertificate = ( "TBSCertificate is missing serial, signature, issuer, validity, subject, or subjectPublicKeyInfo.", ); algorithmOid(tbsSignature); + // RFC 5280 4.1.1.2: signatureAlgorithm MUST equal the TBSCertificate + // signature field. Compare encodings, parameters included. + if ( + signatureAlgorithm.tag !== tbsSignature.tag || + !signatureAlgorithm.content.equals(tbsSignature.content) + ) + return fail( + "Certificate signatureAlgorithm differs from the TBSCertificate signature.", + ); requireSubjectPublicKeyInfo(subjectPublicKeyInfo); finishTbsCertificate(tbsChildren, index + 6); const validityChildren = sequence(validity, "Validity"); diff --git a/src/native/pe/PeCertificateParser.test.ts b/src/native/pe/PeCertificateParser.test.ts index 3befe7b42b..66bb62bb26 100644 --- a/src/native/pe/PeCertificateParser.test.ts +++ b/src/native/pe/PeCertificateParser.test.ts @@ -1035,3 +1035,28 @@ it.each([ expect(certificate.subject.attributes[0]?.value).toBe(value); }, ); + +it("rejects a certificate whose signatureAlgorithm differs from its TBS signature", () => { + const name = directoryName(der(0x0c, Buffer.from("Fixture"))); + const certificate = (outer: Buffer) => + readAsn1( + derSeq( + derSeq(derInt(1), SHA256_ALG, name, validity, name, spki), + outer, + der(0x03, Buffer.from([0])), + ), + ); + const sha1 = derSeq(der(0x06, Buffer.from([0x2b, 0x0e, 0x03, 0x02, 0x1a]))); + // The same OID with explicit NULL parameters is still a different identifier. + const sha256WithNull = der( + 0x30, + Buffer.concat([SHA256_ALG.subarray(2), der(0x05, Buffer.alloc(0))]), + ); + for (const outer of [sha1, sha256WithNull]) + expect(() => parseCertificate(certificate(outer))).toThrow( + /signatureAlgorithm differs from the TBSCertificate signature/u, + ); + expect( + parseCertificate(certificate(SHA256_ALG)).signature_algorithm_oid, + ).toBe("2.16.840.1.101.3.4.2.1"); +});