Long-Term Validation
Embed the certificate chain, CRLs, and OCSP responses in a DSS so a signature validates long after signing — online or fully offline
A digital signature is only as verifiable as the material a validator can reach at validation time. Once the signer's certificate expires — or its revocation endpoints go offline — an otherwise-perfect signature becomes "validity unknown". Long-Term Validation (LTV) fixes this by embedding everything a validator needs inside the PDF, so the signature stays checkable offline and after the certificate's lifetime.
What goes in the DSS
Mudrit writes a Document Security Store (/DSS, per PAdES / ISO 32000-2): the signer's
certificate chain plus its revocation evidence — CRLs and OCSP responses — and a
/VRI (Validation-Related Information) entry keyed to the signature. Adobe reports this as
"LTV enabled".
Turn it on with .ltv(true):
use mudrit_pdfsign::prelude::*;
let cfg = SignConfig::builder()
.place("F", [350, 60, 560, 160])?
.timestamp(Timestamp::url("http://timestamp.comodoca.com")) // trusted time to anchor validity
.ltv(true) // embed the DSS → "LTV enabled"
.build();
let signed = sign_pdf(&pdf, &signer, &cfg)?;"LTV enabled" describes the material embedded, not viewer trust. The green check still depends on the certificate chaining to a trusted root (Adobe AATL, the Windows store, a CCA root, …); the bundled test cert shows "validity unknown" even with a complete DSS.
Policy: .ltv(true) vs LtvPolicy
.ltv(true) is a convenience that maps to a policy. For full control, set one explicitly with
.ltv_policy(...) — an explicit policy always wins over the bool.
LtvPolicy | Behaviour | Selected by |
|---|---|---|
Off | Embed no /DSS. | .ltv(false) (default) |
BestEffort | Embed whatever revocation material is available; never fail on an incomplete DSS. | .ltv(true) |
Require | If the certificate advertises a CRL/OCSP endpoint but none can be obtained, fail loudly with Error::LtvIncomplete instead of writing a half-LTV document. | .ltv_policy(LtvPolicy::Require) |
.ltv_policy(LtvPolicy::Require) // no silent half-LTV — fail if revocation can't be fetchedRequire fails loud
LtvPolicy::Require returns Error::LtvIncomplete (with an actionable message) rather than produce
a document that only looks long-term-valid. Reach for it when a downstream archive contractually
demands embedded revocation data.
BestEffort never lies — sign_pdf_reported
BestEffort (what .ltv(true) selects) won't abort on a partial DSS, but it also won't hide it.
Sign with sign_pdf_reported to get an LtvReport of exactly what landed in the /DSS:
let (signed, report) = sign_pdf_reported(&pdf, &signer, &cfg)?;
if !report.complete {
eprintln!("signed without revocation info: {} certs, no CRL/OCSP", report.certs);
}LtvReport field | Meaning |
|---|---|
certs | Number of certificate streams in /DSS /Certs. |
crls | Number of CRL streams in /DSS /CRLs. |
ocsps | Number of OCSP-response streams in /DSS /OCSPs. |
complete | true if at least one revocation response (CRL or OCSP) was embedded. |
Pre-fetched material and going offline
By default the engine fetches CRLs and OCSP responses live over plain HTTP while signing. A
restricted or air-gapped deployment can instead inject material it already holds — a revocation
cache, an offline bundle — via ValidationMaterial, and set .offline(true) to skip the network
entirely and embed only what you supplied.
let vm = ValidationMaterial::new()
.add_cert(issuer_der) // an extra chain cert for /DSS /Certs
.add_crl(crl_der) // a pre-fetched CRL (DER)
.add_ocsp(ocsp_der) // a pre-fetched OCSP response (DER)
.offline(true); // use ONLY the injected material — no fetches
let cfg = SignConfig::builder()
.place("F", [350, 60, 560, 160])?
.ltv(true)
.validation_material(vm)
.build();ValidationMaterial method | Adds to the DSS |
|---|---|
.add_cert(der) | A certificate (DER) → /Certs. |
.add_crl(der) | A CRL (DER) → /CRLs. |
.add_ocsp(der) | An OCSP response (DER) → /OCSPs. |
.offline(true) | Skip all live fetches; embed only injected material. |
.offline(true) is also available directly on the builder as .offline(true) (a shorthand for
setting it on the ValidationMaterial). Combine injected material with LtvPolicy::Require for a
deterministic, network-free sign that still refuses to produce an incomplete DSS.