Signing Entry Points
sign_pdf, sign_file, sign_pdf_reported, PdfReader, add_document_timestamp, add_locked_signature, and SigSize — exact signatures
Every signature Mudrit produces goes through one of the functions on this page. All of them are
key-source agnostic — they take &dyn Signer, so a .pfx, a PKCS#11 token, the Windows store,
or a custom backend all flow through the same code.
sign_pdf
pub fn sign_pdf(input: impl Into<PdfReader>, signer: &dyn Signer, cfg: &SignConfig) -> Result<Vec<u8>>The core entry point. input is anything convertible into a PdfReader — &[u8],
Vec<u8>, &Vec<u8>, [u8; N], or a PdfReader itself — so a buffer already in memory signs
without a round-trip through disk.
Smart routing
If input is already signed, sign_pdf automatically appends a new signature as an
incremental revision — the existing signatures stay valid — instead of a fresh sign that would
rebuild (and break) them. If that already-signed input is also encrypted, the appended revision
is encrypted with the document's own key (recovered from the open password), so the output stays
protected with the same password and cipher. A fresh (unsigned) input takes the normal path;
cfg.encrypt then password-protects the output.
For a password-protected PDF, pass a PdfReader carrying the open password:
PdfReader::open(path)?.password("…").
sign_file
pub fn sign_file(
input_path: impl AsRef<Path>,
output_path: impl AsRef<Path>,
signer: &dyn Signer,
cfg: &SignConfig,
) -> Result<()>Convenience over sign_pdf: read the PDF at input_path, sign it per cfg, and write the signed
PDF to output_path. Both paths accept anything AsRef<Path> (&str, String, PathBuf, …). For
a password-protected input, use sign_pdf with a PdfReader instead (sign_file has no password
parameter).
sign_pdf_reported
pub fn sign_pdf_reported(
input: impl Into<PdfReader>,
signer: &dyn Signer,
cfg: &SignConfig,
) -> Result<(Vec<u8>, LtvReport)>Like sign_pdf, but also returns an LtvReport of the validation material that actually landed in
the signed document's /DSS — so an LtvPolicy::BestEffort sign is never
silent about an incomplete LTV. An LtvPolicy::Require sign instead fails with
Error::LtvIncomplete before returning.
let (signed, report) = sign_pdf_reported(&pdf, signer, &SignConfig::default())?;
if !report.complete {
eprintln!("warning: signed without revocation info ({} certs, no CRL/OCSP)", report.certs);
}PdfReader
The PDF to sign — its bytes plus an optional open password for an encrypted document. Takes a
PDF in whatever form you have it: in-memory bytes, a file path, or any std::io::Read stream.
Prop
Type
PdfReader also implements From<Vec<u8>>, From<&[u8]>, From<&Vec<u8>>, From<Box<[u8]>>,
From<[u8; N]>, and From<&[u8; N]>, so plain bytes convert automatically wherever impl Into<PdfReader> is expected — the common case stays a one-liner: sign_pdf(bytes, &signer, &cfg).
Its Debug impl hides the password and prints only the byte length.
let from_path = PdfReader::open("in.pdf")?; // file
let from_stream = PdfReader::from_reader(std::io::stdin())?; // any std::io::Read
let locked = PdfReader::open("locked.pdf")?.password("secret"); // + open passwordadd_document_timestamp
pub fn add_document_timestamp(input: impl Into<PdfReader>, timestamp: &dyn Timestamper) -> Result<Vec<u8>>Append a document timestamp (the PAdES-B-LTA archive stamp, /DocTimeStamp,
SubFilter /ETSI.RFC3161) to an already-signed PDF as its own incremental revision — an invisible
signature field whose /Contents is an RFC-3161 TimeStampToken computed over the whole ByteRange. It
carries no signer certificate of its own — the token is the TSA's. Re-appliable: each call stamps
the current document state, which is how long-term archives are renewed. Output is plaintext. See
Timestamps for Timestamper / UrlTimestamper.
let stamped = add_document_timestamp(&signed_pdf, &UrlTimestamper::new("http://timestamp.comodoca.com"))?;PadesLevel::B_LTA (via SignConfigBuilder::pades) applies this automatically after signing — call
add_document_timestamp directly only when renewing an archive later or when not using the PAdES
builder path.
add_locked_signature
pub fn add_locked_signature(
input: impl Into<PdfReader>,
page: u32,
rect: impl Into<Rect>,
field_name: &str,
signer: &dyn Signer,
tsa_url: Option<&str>,
) -> Result<Vec<u8>>Append a signature to an already-signed document as one incremental revision: a visible field on
page at rect, a FieldMDP /Action All lock (freezes the document from that point on), an
embedded DSS (LTV) for the signer's own chain, and — if tsa_url is Some(...) — an RFC-3161
signature timestamp from the built-in UrlTimestamper. Pass None for no timestamp.
Pass the open password on the input (PdfReader::from(bytes).password("…")) when the document is
encrypted and its protection must be kept: it is opened with the password and the new revision's
objects are encrypted with its file key. A plain &[u8] / PdfReader with no password signs an
unencrypted document.
This is the lower-level append API: it has no Metadata parameter, uses a fixed reason
("Document signed - no further changes"), and never emits the optional CMS signingTime attribute.
For a configurable chained/appended signature, use sign_pdf with Method::MultiChained or a
second sign_pdf call instead.
SigSize
pub enum SigSize {
Fixed(usize),
Auto,
}How many bytes to reserve for a signature's /Contents placeholder. The actual CMS is written into
this space and the remainder is zero-padded, so the reservation only needs to be large enough —
too small fails the sign with a "CMS too big" error.
Prop
Type
SigSize's own Default impl is Fixed(SIG_LEN), but SignConfig::default()
and SignConfig::builder() both explicitly set sig_size: SigSize::Auto — so unless you override
it, a config built either way uses Auto.
pub const SIG_LEN: usize = 16384;SIG_LEN is the byte count SigSize::Fixed's own default reserves — the default bytes reserved for
the CMS blob in the /Contents placeholder.