Skip to content

PKIX validation scope and roadmap

This file is the canonical support boundary, claim-language guide, and forward-work backlog for the PKIX-facing surface.

Standards status

AreaStatusNotes
RFC 5280 path validationcompletecore path validation, name constraints across all GeneralName forms (enforce or fail-closed), initial subtree inputs, RFC 9618 policy processing, and malformed-DER coverage ship; validated against the full NIST PKITS suite (224 test procedures, 249 runs incl. documented subtest variations; 4.1.4/4.1.5 DSA chains expected-fail per the WebCrypto algorithm boundary). Revocation is a separate API by design
RFC 6960 + 9919 OCSPcompletethe full validation surface ships: request/response parsing, signature checks, responder binding/authorization (incl. local trusted responders, id-pkix-ocsp-nocheck, and responder revocation policy), nonce/request matching, freshness checks, full request coverage, and chain-level revocation orchestration with freshest-evidence combination; CertID hashing defaults to SHA-256 per RFC 9919 §3.1.1 with explicit SHA-1 interop; HTTP transport is caller-provided by design (scope boundary, not a gap)
RFC 9525 service identitycompletematchServiceIdentity() and the verification helpers (verifyCertificateChain, validateForTlsServer, …) ship every RFC 9525 identity type: DNS-ID, IP-ID, URI-ID, SRV-ID, wildcard, IDNA, and opt-in RFC 6125 CN-compat checks
RFC 9618 policy validationcompleteRFC 9618-style policy state, enforcement, and outputs ship; the full PKITS policy sections (4.8–4.12, every documented subtest variation) pass
RFC 7468 PEM textual encodingscompletestrict generator/parser conformance: strict-mode encapsulation, label handling, and base64 rules ship, with non-canonical final quanta rejected per RFC 4648 §3.5; RFC 1421 folded encapsulated headers unfold for legacy traditional PEM; section-complete executable suite
RFC 8410 + 9295 safe-curve profilescompleteEd25519 end-to-end (key import/export/generation, CSRs, certificates, signature verification); Ed448/X25519/X448 algorithm identifiers and subject keys parse, with RFC 9295 §3 key-usage enforcement for all four OIDs; Ed448/X25519/X448 key operations sit outside the WebCrypto algorithm boundary; section-complete executable suite
PKCS containers: RFC 5652, 7292, 8018partialSignedData sign/parse/verify with per-signer certificate resolution and cert bags (RFC 5652 subset; no enveloped/encrypted content types); PFX create/parse with PKCS#12 MAC (RFC 7292 subset); PBES2 with PBKDF2 HMAC-SHA-1/256 and AES-CBC (RFC 8018 subset)

Current conformance evidence:

1. Define the boundary up front

  • Treat certification path validation as a function over a prospective certification path plus validation inputs, not as “build whatever chain you can find and hope for the best.”

    RFC 5280 Section 6.1.1 defines the algorithm in terms of a candidate path
    and nine inputs. (IETF Datatracker[^rfc5280])
    
  • Keep path building/discovery separate from path validation.

  • Keep service identity matching separate from path validation.

  • Keep revocation separate from path validation.

2. Required inputs for RFC 5280-style path validation

  • Prospective certification path.
  • Validation time.
  • Trust anchor information: trusted issuer name, trusted public key algorithm, trusted public key, and optional trusted key parameters.
  • User-initial-policy-set.
  • Initial policy-mapping inhibit flag.
  • Initial explicit-policy flag.
  • Initial anyPolicy-inhibit flag.
  • Initial permitted subtrees.
  • Initial excluded subtrees. (IETF Datatracker^rfc5280)

3. Core certificate/path checks

  • Parse DER strictly enough to reject malformed certificates.
  • Verify issuer/subject chaining across the candidate path.
  • Verify each certificate signature using the evolving working public key.
  • Check validity time (notBefore / notAfter) against the chosen validation time.
  • Enforce basicConstraints for CA certificates.
  • Enforce pathLenConstraint where applicable.
  • Enforce keyUsage, especially keyCertSign for CAs used to sign subordinate certs.
  • Process self-issued vs non-self-issued certs correctly for path length and name constraints.
  • Reject a certificate whose id-ecPublicKey key locates no namedCurve domain parameters. (IETF Datatracker^rfc5480)
  • Reject the path if any required path-processing step fails. (IETF Datatracker^rfc5280)

4. Extension handling

  • Parse and preserve all extensions, including unknown ones.
  • Reject certificates containing an unsupported critical extension or a critical extension whose contents cannot be processed.
  • Process recognized non-critical extensions when relevant to path processing.
  • Expose raw extension data so callers can layer application-specific policy on top. (IETF Datatracker^rfc5280)

5. Name constraints

  • Support nameConstraints on CA certificates.
  • Support initial permitted/excluded subtrees as validator inputs.
  • Apply constraints across supported name forms, not just DNS SANs.
  • Handle self-issued certificates correctly when evaluating constraints.
  • Fail closed per RFC 5280 §4.2.1.10 when a critical nameConstraints extension imposes a form the validator cannot process (otherName, x400Address, ediPartyName, registeredID) and an instance of that form appears in a subsequent certificate's SANs; chains where the form never appears stay acceptable, and unsupported forms in non-critical extensions are ignored. (IETF Datatracker^rfc5280)

Current GeneralName matrix for nameConstraints:

FormParser roleValidator roleStatus
rfc822Name / dNSNamedecode to typed email/DNS valuesenforcecomplete
uniformResourceIdentifierdecode to typed URI valuesenforce host-based matchingcomplete
iPAddressdecode to address+mask bytesenforcecomplete
directoryNamepreserve structured DN payloadenforce with RFC 5280 semantic comparecomplete
otherNamepreserved as raw payloadfail closed when critical and form appearscomplete
x400Addresspreserved as raw payloadfail closed when critical and form appearscomplete
ediPartyNamepreserved as raw payloadfail closed when critical and form appearscomplete
registeredIDdecoded OID, preservedfail closed when critical and form appearscomplete
  • Parser responsibility: preserve enough tag/type information that validation can make a deterministic supported-vs-unsupported decision.
  • Validator responsibility: enforce supported forms and reject critical under-enforced cases instead of silently widening trust.

6. Certificate policy processing

  • Support certificatePolicies.
  • Support policyConstraints.
  • Support policyMappings.
  • Support inhibitAnyPolicy.
  • Use the RFC 9618 update rather than the older RFC 5280 policy-tree algorithm, because RFC 9618 replaced it with an equivalent, more efficient algorithm to avoid worst-case exponential blowups and DoS risk. (IETF Datatracker^rfc9618)
  • Conformance evidence landed: the full PKITS policy sections (4.8–4.12) pass, with every manifest expectation verified against the official PKITS document (docs/rfc/pkits.txt).

7. Trust-anchor model

  • Accept trust anchors as structured input, not only as “root cert PEM”.
  • Allow trust anchor info to come from a self-signed certificate as a convenience, but treat the trust anchor as out-of-band trust input.
  • Do not assume every self-signed cert is a trust anchor. (IETF Datatracker^rfc5280)

8. Application/service identity checks

  • Keep hostname/service-name matching in a separate API from path validation.
  • For each supported identity type (dNSName, iPAddress, URI-ID, SRV-ID), match subjectAltName entries of the corresponding type first.
  • matchServiceIdentity() supports dNSName, iPAddress, URI-ID, and SRV-ID matching with wildcard and IDNA coverage.
  • Verification helpers (verifyCertificateChain, validateForTlsServer, …) accept the same identity union as matchServiceIdentity().
  • Only support CN fallback as an explicit RFC 6125 compatibility mode; RFC 9525 forbids using the Common Name RDN to identify a service. (IETF Datatracker^rfc6125, ^rfc9525)
  • Make wildcard behavior explicit and test it hard.

Focused RFC 9525 identity fixtures live in test/identity-fixtures.test.ts.

9. EKU / purpose checks

  • Keep EKU checks separate from raw path validity.
  • Allow callers to request purposes such as serverAuth, clientAuth, etc.
  • Distinguish “certificate is path-valid” from “certificate is acceptable for this application”.

10. OCSP support checklist

  • Build CertID from issuer name hash, issuer key hash, serial number, and hash algorithm.
  • Discover the responder from AIA id-ad-ocsp or let callers provide a responder URL explicitly.
  • Parse and verify BasicOCSPResponse.
  • Check that the response fully and correctly refers to the requested certificate set.
  • Validate the OCSP response signature.
  • Validate responder authorization exhaustively.
    • Land RFC 6960 §4.2.2.2 criterion 1: explicit local signer acceptance scoped to the issuing CA (trustedOcspResponders on validateOcspResponse(), trustedOcspResponders on chain orchestration).
    • Decide and enforce responder-certificate revocation policy — see §11 for the canonical breakdown.
    • Add fixture coverage for configured-responder accept/reject and historical-time validation.
  • Enforce response freshness using thisUpdate / nextUpdate and configurable clock skew.
  • Return good, revoked, and unknown distinctly.
  • Support optional nonce handling if you want replay binding between request and response. RFC 9654 defines the updated nonce extension details. (IETF Datatracker^rfc6960)
  • Consume caller-supplied OCSP responses in chain-level revocation orchestration (checkChainRevocation() / verifyCertificateChain()), with fail-closed combination against CRL evidence: a validated revoked verdict from either source always wins; when both validate as good, the default 'best-available' preference reports the source with the fresher thisUpdate (an applied delta CRL counts as its own thisUpdate; ties favor OCSP), surfaced as source.thisUpdate on the per-certificate status.

11. OCSP responder authorization rules

  • Accept an OCSP response signer if it matches local responder configuration for the certificate in question.
    • Add a validation input binding trusted responder certs to issuer CA scopes (trustedOcspResponders — per-call, and each call is scoped to one issuer).
    • Thread local responder policy through validateOcspResponse() before falling back to issuer-cert or delegated-responder rules.
    • Add positive and negative fixtures for locally authorized signer scope.
  • Accept it if the signer is the issuing CA certificate itself.
  • Accept it if the signer cert contains EKU id-kp-OCSPSigning and was issued directly by the CA that issued the target certificate.
  • Reject the response if the signer certificate meets none of those conditions.
  • Decide and document responder-certificate revocation policy (RFC 6960 §4.2.2.2.1).
    • Parse and expose id-pkix-ocsp-nocheck (hasOcspNoCheckExtension()).
    • Define validator policy knobs: responderRevocationPolicy = 'honor-nocheck' (default) / 'require-evidence' / 'skip', with responderRevocationCrls as evidence and trustedOcspResponders as the caller-local override.
    • Add fixtures for each policy branch.
    • Pass caller evaluation time through delegated responder chain validation.

Focused OCSP auth/completeness/freshness fixtures live in test/ocsp-fixtures.test.ts.

12. CRL support checklist

  • Treat CRL validation as a separate revocation subsystem.
  • Parse CRLs and CRL extensions.
  • Verify CRL signatures and issuer linkage.
  • Enforce CRL time/freshness semantics.
  • Parse CRL distribution points and enforce distribution-point scope during CRL applicability; CRL discovery/fetch hooks are not shipped.
  • Add delta CRL handling only if you actually want to live in that swamp. RFC 5280 defines CRL validation separately from path validation. (IETF Datatracker^rfc5280)

13. API design checklist

  • Keep path building separate from path validation.
  • Keep service identity matching in a separate API from path validation.
  • Keep revocation checking separate from path validation.
  • Expose structured validation inputs instead of hiding policy/name-constraint knobs.
  • Return typed failure reasons for currently implemented path-validation, CRL, and OCSP checks.
  • Distinguish hard validation failure from revocation status unknown in revocation orchestration; chain, CRL, and OCSP validators still return binary success/failure results.

14. Test/conformance checklist

  • Add fixed RFC-style test vectors for builders, parsers, and validators.
  • Add round-trip tests for certs, CSRs, names, and extensions.
  • Run the full NIST PKITS suite: all 224 test procedures across sections 4.1–4.16, expanded to 249 runs including every documented subtest variation; manifest expectations verified against the official PKITS document (docs/rfc/pkits.txt). 4.1.4/4.1.5 (DSA) are expected-fail per the WebCrypto algorithm boundary and tracked with it.failing. See test/pkits.test.ts.
  • Add malformed DER / fuzz tests.
  • Differential-test against at least one mature implementation. See test/differential.test.ts.
  • Run the validator against NIST PKITS as a gap-report harness, which NIST describes as a comprehensive X.509 path validation test suite for relying parties. (NIST Computer Security Resource Center^x-509-path-validation)

Last updated:

Released under the MIT License.