Release note · August 6, 2026
A signature ByteRange cannot hide an indirect signature value
A signature /ByteRange describes a byte-level digest scope, but its surrounding dictionary still has an object-layout requirement. The PDF 2.0 signature errata require direct signature-dictionary values when a byte-range digest is present. A general PDF parser often resolves an indirect reference for the caller, which makes that stored distinction easy to lose during review.
PDFFence 1.20.0 makes that one narrow property reviewable. It adds aggregate direct-value evidence and an opt-in rule, PFP012. It does not turn object-layout inspection into cryptographic signature validation.
The distinction a resolved value erases
A direct text string and an indirect reference to a text string can resolve to the same application-level value. For a signature dictionary with /ByteRange, they are not equivalent stored representations. The directness question is deliberately shallow: it asks whether every top-level dictionary value is direct. It neither recursively restricts nested containers nor exposes a referenced object, key name, or value.
PDFFence applies this only to semantic signature roots: catalog-reachable AcroForm signature fields, including inherited /FT, and catalog /Perms DocMDP, UR, or UR3 entries. A private /PieceInfo dictionary that happens to resemble /Type /Sig remains ordinary stored-byte or reachability evidence.
PFP012: require direct ByteRange signature values
The opt-in require_direct_byte_range_signature_values rule fails when either inspected PDF has a semantic signature dictionary with /ByteRange and at least one indirect top-level value. Unsigned PDFs pass, as do semantic signature dictionaries without /ByteRange. The gate does not depend on whether a range reaches the current EOF or whether its omitted gap exactly matches /Contents.
version: 1
rules:
require_direct_byte_range_signature_values: true
PFP009 asks whether current-file coverage regressed. PFP010 requires a current-file boundary. PFP011 requires that a current two-pair boundary exclude exactly direct /Contents. PFP012 is separate: it asks whether the signature dictionary carrying a ByteRange keeps every one of its own top-level values direct.
A control that holds every boundary still
PDF Change Assurance Benchmark 1.20.0 adds a deterministic pair tailored to that separation. Both sides retain a well-formed, current-file ByteRange whose only excluded span is exactly direct hexadecimal /Contents. The candidate changes one otherwise ordinary top-level signature value to an indirect reference while keeping its target independently catalog-reachable.
That means the expected public result is only the generic direct-value inventory event and PFP012. PFP009 through PFP011 correctly remain quiet. The prior PDFFence 1.19 source sees stored bytes alone on the pair; the new control closes that specific static-review blind spot.
Evidence, not authenticity
PDFFence records aggregate counts only. It does not emit object references, values, offsets, signature bytes, digests, certificates, transforms, permissions, or trust decisions. It does not calculate a digest, validate /Contents, validate a certificate chain, or decide whether an update is authentic, permitted, safe, or malicious. Those are jobs for a conforming signature validator and the surrounding review process.
PDFFence passed 248 tests and Ruff; PDFCAB passed 87 tests and Ruff. The final source candidates scored all 157 benchmark pairs through the public process boundary. Both 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 clean Python 3.12 source-archive install; every installed combination verified the fixtures and scored 157 of 157.
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.20.0/pdffence-1.20.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.