Mudrit
Guides

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

Part of mudrit-pdfsign

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.

LtvPolicyBehaviourSelected by
OffEmbed no /DSS..ltv(false) (default)
BestEffortEmbed whatever revocation material is available; never fail on an incomplete DSS..ltv(true)
RequireIf 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 fetched

Require 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 fieldMeaning
certsNumber of certificate streams in /DSS /Certs.
crlsNumber of CRL streams in /DSS /CRLs.
ocspsNumber of OCSP-response streams in /DSS /OCSPs.
completetrue 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 methodAdds 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.

Next

On this page