3.4 KiB
3.4 KiB
validate/trust-anchor — SKI-indexed trust-anchor store
Purpose
validate/trust-anchor is the core of the certificate path-validation pipeline. validateChain walks the certificate chain leaf-first up to the trust anchor, re-hashing each TBSCertificate, dispatching the signature verify through @suaveplan/crypto-rsa (for RSA-PKCS1v1.5 and RSA-PSS) or @suaveplan/crypto-curve (for ECDSA-P{256,384,521} and Ed25519), and checking validity, basic constraints, key usage, EKU, name chaining, name constraints, OCSP status (when an OcspResponderLike is injected), and critical-extension recognition per RFC 5280 paragraph 6.1.
Features
- Full 12-step algorithm per RFC 5280 paragraph 6.1 with the spec-pinned set of failure codes.
- Injectable
Clockso tests can probe expiry boundaries without time travel. - Injectable
AbortSignalso callers can interrupt slow chains (e.g. when an OCSP responder is unreachable). - Bounded chain depth (default 16) with caller override.
TrustAnchorStorevalue object with O(1) SKI lookup and PEM-bundle constructor.- Configurable duplicate-SKI policy (
last-winsdefault,first-wins, orthrow). - OpenTelemetry surface emitting
x509.validate_duration_ms,x509.validate_total,x509.chain_length, andx509.error_totalon every call.
Usage: Basic Example
import { validateChain, TrustAnchorStore } from "@suaveplan/x509/validate";
import { parseCertificate } from "@suaveplan/x509/certificate";
const result = await validateChain({
leaf: parseCertificate(leafBytes),
intermediates: [parseCertificate(intermediateBytes)],
trustAnchors: [parseCertificate(rootBytes)],
});
console.log(result.trustedPath.length); // 3
Usage: Advanced Example
import { validateChain, TrustAnchorStore } from "@suaveplan/x509/validate";
import { parseCertificate } from "@suaveplan/x509/certificate";
const store = new TrustAnchorStore(pemBundleString, { onDuplicate: "throw" });
const ac = new AbortController();
const result = await validateChain({
leaf: parseCertificate(leafBytes),
intermediates: intermediates.map(parseCertificate),
trustAnchors: store.toArray(),
clock: () => Date.UTC(2025, 0, 1),
signal: ac.signal,
maxChainDepth: 8,
ekuRequired: "serverAuth",
});
API Reference
validateChain(opts: ValidateChainOptions): Promise<ValidateChainResult>— async; throwsX509Erroron every failure path.TrustAnchorStore—findBySki,findIssuer,contains,toArray,size.DEFAULT_MAX_CHAIN_DEPTH—16.OcspResponderLike—check(cert, issuer, signal?) => Promise<OcspStatus>.
Implementation Notes
- The chain is ordered leaf-first by walking from the leaf upward: at each step the next certificate is chosen by canonical-name equality between the current cert's issuer and a candidate's subject. The algorithm terminates when a trust anchor is reached.
- The OCSP boundary is interface-only — no built-in responder ships. Implementation lives in
@suaveplan/x509-ocsp(follow-up). - Policy mapping / policy constraints are out of scope; the validator does not enforce them and rejects them as unknown critical extensions per RFC 5280 paragraph 6.1.4(f).
- Self-signed leaf rejection happens before the chain walk; the leaf is accepted only when it is byte-identical to a trust anchor.
- Pre-aborted
AbortSignalshort-circuits before any work; aborts between per-cert iterations are honoured.