mirror of
https://github.com/morluto/rea
synced 2026-10-26 06:16:17 -04:00
fix(pe): separate certificate content from counted alignment
This commit is contained in:
@@ -28,6 +28,11 @@ The inline Evidence result contains the artifact path, SHA-256, and byte length.
|
||||
absent or zero. Each `entries[]` item reports `WIN_CERTIFICATE` length,
|
||||
revision, type, and the payload SHA-256.
|
||||
|
||||
Type-2 entries may count their zero alignment padding in `dwLength`. The PE
|
||||
entry owner accepts only the exact gap to the next eight-byte boundary after
|
||||
the parsed ContentInfo; extra data or nonzero counted padding fails. Entry and
|
||||
payload ranges and hashes retain all declared bytes, including counted padding.
|
||||
|
||||
For `WIN_CERTIFICATE` type 2 (PKCS#7 signedData) `pkcs7` contains digest
|
||||
algorithm OIDs, embedded X.509 serial/issuer/subject/validity fields, and
|
||||
signer identifier facts. A signer identified by subject key identifier reports
|
||||
|
||||
@@ -569,9 +569,7 @@ const parseSignedData = (
|
||||
};
|
||||
|
||||
/** Decode ContentInfo wrapping SignedData; other content types remain unsupported. */
|
||||
export const parsePkcs7SignedData = (bytes: Buffer): ParsedSignedData => {
|
||||
const root = readAsn1(bytes);
|
||||
if (root.end !== bytes.length) return fail("ContentInfo has trailing data.");
|
||||
export const parsePkcs7SignedData = (root: Asn1Node): ParsedSignedData => {
|
||||
const children = sequence(root, "ContentInfo");
|
||||
const type = children[0];
|
||||
const content = children[1];
|
||||
|
||||
@@ -72,6 +72,47 @@ it("reads PE32 and PE32+ PKCS#7 certificate tables", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it.each([false, true])(
|
||||
"preserves PE certificate bytes when dwLength counts alignment padding (PE32+ = %s)",
|
||||
(plus) => {
|
||||
// Choose a fixture with nonzero alignment padding so the control distinguishes
|
||||
// an ASN.1 terminator from the end of the WIN_CERTIFICATE payload.
|
||||
const payload = ["Padding", "Paddingx"]
|
||||
.map((name) => pkcs7SignedDataFixture(name))
|
||||
.find((value) => (value.length + 8) % 8 !== 0)!;
|
||||
const bytes = peCertificateFixture(plus, payload);
|
||||
const control = parse(bytes);
|
||||
const entry = control.entries[0]!;
|
||||
const aligned = Math.ceil(entry.length / 8) * 8;
|
||||
expect(aligned).toBeGreaterThan(entry.length);
|
||||
bytes.writeUInt32LE(aligned, entry.location.offset);
|
||||
const report = peCertificatesSchema.parse(parse(bytes));
|
||||
expect(report.entries[0]?.pkcs7).toEqual(entry.pkcs7);
|
||||
expect(report.entries[0]?.length).toBe(aligned);
|
||||
expect(report.entries[0]?.payload.sha256).toBe(
|
||||
createHash("sha256")
|
||||
.update(
|
||||
bytes.subarray(
|
||||
entry.payload.location.offset,
|
||||
entry.location.offset + aligned,
|
||||
),
|
||||
)
|
||||
.digest("hex"),
|
||||
);
|
||||
bytes[entry.location.offset + aligned - 1] = 1;
|
||||
expect(() => parse(bytes)).toThrow(/ContentInfo has trailing data/);
|
||||
},
|
||||
);
|
||||
|
||||
it("refuses surplus zero bytes beyond the PE entry alignment gap", () => {
|
||||
const payload = pkcs7SignedDataFixture();
|
||||
expect(() =>
|
||||
parse(
|
||||
peCertificateFixture(false, Buffer.concat([payload, Buffer.alloc(8)])),
|
||||
),
|
||||
).toThrow(/ContentInfo has trailing data/);
|
||||
});
|
||||
|
||||
it("reads BER indefinite constructed ContentInfo and preserves certificate bytes", () => {
|
||||
const payload = pkcs7SignedDataIndefiniteEnvelopeFixture();
|
||||
const root = readAsn1(payload);
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
oidName,
|
||||
parsePkcs7SignedData,
|
||||
PeCertificateAsn1Failure,
|
||||
readAsn1,
|
||||
} from "./PeCertificateAsn1.js";
|
||||
import { readPeHeaders } from "./PeResourceLayout.js";
|
||||
|
||||
@@ -115,7 +116,16 @@ export const parsePeCertificates = (
|
||||
let pkcs7: PeCertificates["entries"][number]["pkcs7"] = null;
|
||||
if (peCertificateTypeName(certificateType) === "pkcs7") {
|
||||
try {
|
||||
const parsed = parsePkcs7SignedData(payload);
|
||||
const content = readAsn1(payload);
|
||||
// The PE entry owns alignment outside the ASN.1 ContentInfo. Accept
|
||||
// either uncounted padding or the exact counted zero-padding extent.
|
||||
if (
|
||||
content.end !== payload.length &&
|
||||
(payload.length !== align8(8 + content.end) - 8 ||
|
||||
!payload.subarray(content.end).every((byte) => byte === 0))
|
||||
)
|
||||
throw new PeCertificateAsn1Failure("ContentInfo has trailing data.");
|
||||
const parsed = parsePkcs7SignedData(content);
|
||||
teletexStringCompatibilityUsed ||=
|
||||
parsed.teletexStringCompatibilityUsed;
|
||||
pkcs7 = {
|
||||
|
||||
Reference in New Issue
Block a user