Expand description
Lint engine for X.509 certificate chains — structured soft-fail and advisory results.
§What this crate provides
pkix-path returns Result<ValidatedPath, Error> — hard pass or fail.
That model cannot express “this certificate is RFC 5280 valid but violates
CA/B Forum BR §7.1.4.2” without aborting the chain entirely.
pkix-lint adds an advisory layer:
Lint— the unit of evaluation. Each lint has a stable ID, a normative citation, a severity, a scope (certificate vs. full chain path), and a subject-kind filter (leaf, intermediate CA, etc.).LintResult—Pass | NotApplicable | Warn | Error | Fatal.WarnandErrorcarry a&'static strdetail message.Fatalwithinpkix-lintmeans “stop evaluating further lints” — it is not a TLS hard-fail. See the advisory-only contract below.Finding— a lint ID paired with aLintResult, optionally referencing the chain index of the offending certificate.LintRunner— evaluates a slice ofdyn Lintobjects against a certificate or validated path and returnsVec<Finding>.LintProfile— extendspkix_path::Profilewith alints()method so that a profile can bundle its own lint set.
§Finding ID stability
Finding IDs (returned by Lint::id) are part of the public API.
They MUST NOT change between crate versions without a semver-major bump.
Format convention: <regime>.<section>.<noun>, e.g.:
"cabf.br.tls.validity.max""cabf.smime.san.type""rfc5280.basic_constraints.ca_flag"
§Advisory-only contract
pkix-lint findings never cause a certificate to be rejected. All runner
methods return Vec<Finding> — they never return Result::Err and they never
cause a TLS stack to abort a connection. Findings are advisory signals.
Whether to act on a finding (reject a TLS connection, block a cert, alert an
operator) is the caller’s decision, configured per finding-ID at the integration
layer (e.g., pkix-chain or a TLS stack binding). This design is intentional:
pkix-lintdoes not know whether you are in audit, monitoring, or enforcement context. The caller does.- Spec ambiguity (CA/B Forum CPs, FPKI CPs, etc.) means some findings require human judgment before enforcement. Hard-fail by default would cause outages.
- The deviation/waiver mechanism (PKIX-jge) operates at this layer, not in
pkix-lintcore.
The only in-engine effect of LintResult::Fatal is stopping further lint
evaluation for the current item — it does not escape as an error.
§Design rationale
Inspired by zlint and certlint but with several deliberate differences:
- Trait-based, not enum-based: external crates can implement
Lintand passBox<dyn Lint>toLintRunnerwithout modifying this crate. - Cow detail messages:
LintResult::Warn,Error, andFatalcarryCow<'static, str>detail. Static string literals are zero-allocation (Cow::Borrowed); runtime-formatted strings such asformat!(...)useCow::Ownedwithout leaking memory. - Temporality-aware:
LintRunner::run_certtakesnow_unix: u64so lints can enforce rules that have effective dates (e.g., SC-081 validity caps). - Scope-separated: certificate lints and path lints run in separate passes so path lints can see the full validated output.
§Example
// `cert` and `now_unix` are obtained from the calling context (e.g., loaded
// from DER and current wall-clock time). They are not defined here so the
// example cannot be run in a doctest harness without external fixtures.
use pkix_lint::{Lint, LintResult, LintRunner, Scope, Severity, SubjectKind};
use x509_cert::Certificate;
#[derive(Clone)]
struct MyLint;
impl Lint for MyLint {
fn id(&self) -> &'static str { "example.my_lint" }
fn citation(&self) -> &'static str { "Example Corp Policy §1.2" }
fn severity(&self) -> Severity { Severity::Warn }
fn scope(&self) -> Scope { Scope::Certificate }
fn applies_to(&self) -> SubjectKind { SubjectKind::Leaf }
fn check_cert(&self, cert: &Certificate, _kind: SubjectKind, _now_unix: u64) -> LintResult {
if cert.tbs_certificate.subject.to_string().is_empty() {
LintResult::warn("empty Subject DN")
} else {
LintResult::Pass
}
}
}
let cert: Certificate = unimplemented!("load from DER");
let now_unix: u64 = unimplemented!("current Unix epoch seconds");
let runner = LintRunner::new(vec![Box::new(MyLint)]);
let findings = runner.run_cert(&cert, SubjectKind::Leaf, 0, now_unix);
for f in &findings {
println!("{}: {:?}", f.lint_id, f.result);
}§Limitations
- Framework, not a comprehensive rule set. This crate ships the
Linttrait,LintRunner, and a small RFC-conformance lint set in therfc5280,rfc6125,rfc8398, andrfc8551modules. Comprehensive industry-forum lint coverage is the job of policy adapter crates (pkix-policy-zlintfor zlint’s ~700 rules,pkix-policy-pkilintfor pkilint’s S/MIME BR + ETSI coverage). CA/B Forum reference lints live in the siblingpkix-lint-cabfcrate; that crate is also explicitly small and curated. - Advisory-only. Findings never cause a TLS rejection by themselves (see the contract above). Plumbing findings into hard-fail or waiver decisions is the integration layer’s job.
- OSCAL adapter is one supported output, not the canonical format.
The
oscalfeature emits OSCAL Assessment Results JSON and parses OSCAL Risk-based deviations back intodeviation::DeviationStore. The workspace does not prescribe OSCAL as a canonical inter-tool wire format (AGENTS.md non-negotiable #5, three-mode policy architecture); each policy-adapter crate consumes its upstream tool’s natural format. - No site-local policy DSL. Site-local policy is the deployer’s
responsibility; implement
Lint(or load lints from any deployer-chosen format) and feedLintRunner.
Modules§
- deviation
- Deviation (waiver) mechanism for
pkix-lint. - oscal
oscal - NIST OSCAL bridge for pkix-lint outputs.
- report
- Evidence pack:
EvaluationReportbundles all findings from a lint run. - rfc5280
- RFC 5280 conformance lints.
- rfc6125
- RFC 6125 conformance lints.
- rfc8398
- RFC 8398 conformance lints.
- rfc8551
- RFC 8551 conformance lints.
Structs§
- Finding
- A recorded lint outcome, associating a lint ID with its result.
- Lint
Parameter - Descriptor for a tunable parameter exposed by a
Lintimplementation. - Lint
Runner - Evaluates a collection of
Lints against certificates or a validated path. - Validated
Path - The result of a successful certificate path validation.
- Validation
Policy - Policy parameters controlling path validation.
Enums§
- Lint
Result - The outcome of evaluating a single lint against a certificate or path.
- Parameter
Error - Error reported by
Lint::set_parameter. - Scope
- Whether a lint evaluates a single certificate or the complete validated path.
- Severity
- How seriously to treat a lint finding.
- Subject
Kind - Which certificate positions in the chain a lint applies to.
Traits§
- Lint
- A single, independently evaluable lint check.
- Lint
Clone - Supertrait of
Lintthat letsBox<dyn Lint>be cloned. - Lint
Profile - A
Profilethat also bundles a set of lints. - Profile
- A PKI regime profile that bundles identity, citation, and a validation policy.
Functions§
- check_
shape - Run the certificate-scope lints of
profileagainstcertand report pass/fail without walking a chain or verifying signatures.