mirror of
https://github.com/morluto/rea.git
synced 2026-11-11 17:28:08 +00:00
fix(pe): validate SignedData version INTEGER content (#10054)
fix(pe): validate SignedData version integer encoding
This commit is contained in:
1 parent
4a02ca95de
commit
8c40ba78de
2 files changed
+56
-5
No files matched your search
@@ -230,11 +230,7 @@ const decodeDirectoryString = (
|
||||
// X.690 8.3.1 and 8.3.2 hold for BER as well as DER: an INTEGER has at least
|
||||
// one content octet, and its first nine bits are never all zeros or all ones.
|
||||
// Padded encodings would otherwise collapse onto the minimal serial's hex.
|
||||
const integerHex = (
|
||||
content: Buffer,
|
||||
budget: DecodedNameBudget,
|
||||
label: string,
|
||||
): string => {
|
||||
const requireIntegerContent = (content: Buffer, label: string): void => {
|
||||
if (content.length === 0) return fail(`${label} INTEGER is empty.`);
|
||||
const second = content[1];
|
||||
if (
|
||||
@@ -243,6 +239,14 @@ const integerHex = (
|
||||
(content[0] === 0xff && second >= 0x80))
|
||||
)
|
||||
return fail(`${label} INTEGER is not minimally encoded.`);
|
||||
};
|
||||
|
||||
const integerHex = (
|
||||
content: Buffer,
|
||||
budget: DecodedNameBudget,
|
||||
label: string,
|
||||
): string => {
|
||||
requireIntegerContent(content, label);
|
||||
let start = 0;
|
||||
while (start < content.length - 1 && content[start] === 0) start += 1;
|
||||
chargeDecodedName(budget, (content.length - start) * 2);
|
||||
@@ -716,6 +720,7 @@ const parseSignedData = (
|
||||
const [version, digestSet, encapContentInfo] = children;
|
||||
if (version === undefined || version.tag !== 0x02)
|
||||
return fail("SignedData is missing version.");
|
||||
requireIntegerContent(version.content, "SignedData version");
|
||||
if (digestSet === undefined || digestSet.tag !== 0x31)
|
||||
return fail("SignedData is missing digestAlgorithms.");
|
||||
if (
|
||||
|
||||
@@ -1274,3 +1274,49 @@ it("rejects malformed and repeated certificate extensions", () => {
|
||||
).subject.text,
|
||||
).toBe("commonName=Fixture");
|
||||
});
|
||||
|
||||
it.each([[], [0x00, 0x01], [0xff, 0xff]].map((version) => ({ version })))(
|
||||
"rejects malformed SignedData version INTEGER content $version",
|
||||
({ version }) => {
|
||||
const original = pkcs7SignedDataFixture();
|
||||
const signed = readAsn1(original).children[1]!.children[0]!;
|
||||
const payload = contentInfo(
|
||||
derSeq(
|
||||
der(0x02, Buffer.from(version)),
|
||||
...signed.children
|
||||
.slice(1)
|
||||
.map((node) => original.subarray(node.start, node.end)),
|
||||
),
|
||||
);
|
||||
expect(() => parse(peCertificateFixture(false, payload))).toThrow(
|
||||
/SignedData version INTEGER/u,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
it.each([
|
||||
{ name: "short form", version: Buffer.from([0x02, 0x01, 0x01]) },
|
||||
{ name: "BER long form", version: Buffer.from([0x02, 0x81, 0x01, 0x01]) },
|
||||
])(
|
||||
"retains a valid SignedData version with $name length encoding",
|
||||
({ version }) => {
|
||||
const original = pkcs7SignedDataFixture();
|
||||
const signed = readAsn1(original).children[1]!.children[0]!;
|
||||
const payload = contentInfo(
|
||||
derSeq(
|
||||
version,
|
||||
...signed.children
|
||||
.slice(1)
|
||||
.map((node) => original.subarray(node.start, node.end)),
|
||||
),
|
||||
);
|
||||
const report = parse(peCertificateFixture(false, payload));
|
||||
expect(report.coverage).toMatchObject({
|
||||
status: "complete",
|
||||
certificates: 1,
|
||||
});
|
||||
expect(report.entries[0]?.pkcs7?.signers[0]).toMatchObject({
|
||||
serial_hex: "11223344",
|
||||
});
|
||||
},
|
||||
);
|
||||
Reference in new issue
Block a user