Release note · August 6, 2026

A PDF signature ByteRange belongs to its own revision

By SybilGambleyyu · PDFFence 1.21.0

A PDF can accumulate signatures as incremental updates. That means an earlier signature's /ByteRange should normally stop at the end of its own revision, not at the end of the latest file. Treating every historical signature as stale just because another revision exists throws away an important distinction.

PDFFence 1.21.0 adds that distinction as aggregate static evidence and an opt-in policy gate, PFP013. It is deliberately not a cryptographic signature validator.

The boundary is a revision boundary

The PDF 2.0 signature errata describe the multiple-signature case: the byte range runs from the PDF header through the %%EOF marker, with a possible optional line ending, that terminates the incremental update adding that signature dictionary.

This explains why current-file coverage and signature coverage are different questions. A final signature can reach the latest EOF. An older, correctly preserved signature should still end at the EOF of its signing revision. Neither fact establishes certificate trust, digest validity, permissions, or whether a later update is acceptable.

PFP013: require own-revision ByteRange coverage

The opt-in require_signature_byte_range_own_revision_coverage rule requires every semantic signature dictionary with /ByteRange to have this bounded own-revision evidence on both sides of a comparison. Unsigned PDFs and semantic signature dictionaries without /ByteRange pass.

version: 1
rules:
  require_signature_byte_range_own_revision_coverage: true

PDFFence scopes the check to catalog-reachable AcroForm signature fields, including inherited /FT, and catalog /Perms DocMDP, UR, and UR3 entries. It follows a structurally confirmed /Prev-linked xref chain, finds an unambiguous matching revision footer within a bounded scan, and maps the current xref address of an indirect signature dictionary to the first footer after that object. The public report reveals only counts: never offsets, object references, revision boundaries, signature bytes, digests, certificates, transforms, or trust decisions.

The rule fails closed. A direct or compressed signature object, an unavailable or ambiguous revision chain, a malformed range, or an over-limit scan does not earn positive evidence. This is a review boundary, not a permissive parser fallback.

Why this does not replace the existing coverage gates

PFP009 reports a loss of current-file coverage across a comparison. PFP010 requires a current-file boundary, and PFP011 additionally requires that a current two-pair range exclude exactly direct hexadecimal /Contents. Those are useful policies when the latest stored file must be fully covered. PFP013 answers a different question: whether each signature's range reaches the revision that introduced it.

That makes PFP013 useful in multi-signature workflows where a correct historical signature should remain distinguishable from an arbitrary earlier endpoint.

A benchmark pair that stays historically stale on both sides

PDF Change Assurance Benchmark 1.21.0 adds its 158th deterministic pair for this exact separation. Both PDFs contain a semantic signature followed by a later incremental update, so neither range reaches the current physical end. The baseline ends at the signing revision's footer. The candidate stops short of it while retaining the same stale current-file status and well-formed range.

Only signature_own_revision_coverage_inventory_changed and PFP013 are expected. PFP009 and PFP012 stay quiet. PDFFence 1.20 sees changed bytes alone on this pair, showing that the fixture captures a newly implemented boundary rather than restating an old signal.

Evidence, not authenticity

PDFFence 1.21 passed 254 tests and Ruff; PDFCAB passed 88 tests and Ruff. The public process-bound score was exact at 158 of 158 pairs. The packages built reproducibly with fixed timestamps, passed Twine metadata checks, and passed clean paired wheel installs on Python 3.12 and 3.13 plus a Python 3.12 source-archive install. A local pyHanko two-signature exercise counted both signatures at their own revisions while only the newest reached current EOF.

None of that validates a digest, /Contents, certificate chain, transform, permission, or trust decision. It does not decide whether a change is authentic, permitted, safe, or malicious. Use a conforming signature validator and the surrounding review process for those decisions.

The tagged policy reference, threat model, and validation record define the supported boundary.

python -m pip install https://github.com/SybilGambleyyu/pdffence/releases/download/v1.21.0/pdffence-1.21.0-py3-none-any.whl

pdffence check before.pdf after.pdf --policy pdffence.yml --format sarif

PDFFence is MIT-licensed and available on GitHub. The release assets include SHA-256 checksums for the wheel and source archive.