Add explainable certificate path construction
This commit is contained in:
1 parent
2d02590214
commit
f0dbc19807
12 files changed
+319
-2
No files matched your search
@@ -38,6 +38,20 @@ test("an understood name does not make an unsupported critical extension safe",
|
||||
assert.deepEqual(result.derived.unsupportedCriticalExtensions, ["2.5.29.32"]);
|
||||
});
|
||||
|
||||
test("DER explorer decodes SAN and Authority Key Identifier relationships", () => {
|
||||
const sanNames = tlv("30", "820b6578616d706c652e636f6d8704c0000201");
|
||||
const authorityKey = tlv("30", "800401020304");
|
||||
const extensions = tlv("30", extension("551d11", sanNames) + extension("551d23", authorityKey));
|
||||
const certificate = bytes(tlv("30", tlv("30", tlv("a3", extensions))));
|
||||
const result = exploreCertificateDer(certificate);
|
||||
|
||||
assert.deepEqual(result.derived.subjectAlternativeNames, [
|
||||
{ type: "dns", value: "example.com" },
|
||||
{ type: "ip", value: "192.0.2.1", valueHex: "c0000201" },
|
||||
]);
|
||||
assert.equal(result.derived.authorityKeyIdentifier, "01020304");
|
||||
});
|
||||
|
||||
function extension(oidHex, valueHex, critical = false) {
|
||||
return tlv("30", tlv("06", oidHex) + (critical ? "0101ff" : "") + tlv("04", valueHex));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { analyzeCertificatePaths } from "../src/path-analysis.js";
|
||||
|
||||
test("path analysis enumerates two valid issuer alternatives with attributed trust", () => {
|
||||
const certificates = [cert("leaf", false), cert("intermediate-a"), cert("intermediate-b"), cert("root-a", true), cert("root-b", true)];
|
||||
const relationships = [
|
||||
link("leaf", "intermediate-a"), link("leaf", "intermediate-b"),
|
||||
link("intermediate-a", "root-a"), link("intermediate-b", "root-b"),
|
||||
];
|
||||
const result = analyzeCertificatePaths(certificates, relationships, {
|
||||
trustedCertificateSha256: ["root-a"],
|
||||
});
|
||||
|
||||
assert.equal(result.paths.length, 2);
|
||||
assert.equal(result.paths.find((path) => path.certificateSha256.includes("root-a")).status, "trusted");
|
||||
assert.equal(result.paths.find((path) => path.certificateSha256.includes("root-b")).status, "untrusted");
|
||||
});
|
||||
|
||||
test("path analysis explains rejected and missing issuer paths", () => {
|
||||
const certificates = [cert("leaf", false), cert("wrong-issuer")];
|
||||
const relationships = [{ ...link("leaf", "wrong-issuer"), signatureValid: false }];
|
||||
const result = analyzeCertificatePaths(certificates, relationships);
|
||||
|
||||
assert.equal(result.paths[0].status, "incomplete");
|
||||
assert.equal(result.paths[0].terminalReason, "no-acceptable-issuer");
|
||||
assert.deepEqual(result.edges[0].reasons, ["signature-invalid"]);
|
||||
});
|
||||
|
||||
test("path analysis rejects issuer certificates that cannot sign certificates", () => {
|
||||
const issuer = { ...cert("issuer"), keyUsages: ["digitalSignature"] };
|
||||
const result = analyzeCertificatePaths([cert("leaf", false), issuer], [link("leaf", "issuer")]);
|
||||
assert.equal(result.edges[0].accepted, false);
|
||||
assert.match(result.edges[0].reasons.join(" "), /key-usage/);
|
||||
});
|
||||
|
||||
test("path validation explains an exceeded CA path-length constraint", () => {
|
||||
const intermediate = cert("intermediate");
|
||||
const root = { ...cert("root", true), pathLengthConstraint: 0 };
|
||||
const result = analyzeCertificatePaths(
|
||||
[cert("leaf", false), intermediate, root],
|
||||
[link("leaf", "intermediate"), link("intermediate", "root")],
|
||||
{ trustedCertificateSha256: ["root"] },
|
||||
);
|
||||
assert.equal(result.paths[0].status, "invalid");
|
||||
assert.deepEqual(result.paths[0].validationFailures.map((failure) => failure.code), ["path-length-constraint-exceeded"]);
|
||||
});
|
||||
|
||||
function cert(sha256, selfSigned = false) {
|
||||
return { sha256, subject: sha256, isCa: sha256 !== "leaf", keyUsages: ["keyCertSign"], selfSigned, unsupportedCriticalExtensions: [] };
|
||||
}
|
||||
function link(childSha256, issuerSha256) {
|
||||
return { childSha256, issuerSha256, issuerNameMatches: true, signatureValid: true, authorityKeyMatches: true };
|
||||
}
|
||||
Reference in new issue
Block a user