Mudrit
Reference

Signing Entry Points

sign_pdf, sign_file, sign_pdf_reported, PdfReader, add_document_timestamp, add_locked_signature, and SigSize — exact signatures

Part of mudrit-pdfsign

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 password

add_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.

Next

On this page