* M9: read BER signatures; later changes are classified narrowly
Real-world signatures (DocuSign, Telekom QES, Documenso) are BER: indefinite
lengths and segmented OCTET STRINGs, which the strict DER reader turned away
("DER signature wrong"). Behind that, later revisions that add DSS data or
rewrite the XMP were classified as tampering.
- cms: the CMS structure is read with `Tlv::parse_ber` (the reader #453 added
for PKCS#12); certificates and signed attributes are still verified over
their own exact bytes, signed attributes with an indefinite length are
re-encoded as DER first (RFC 5652 §5.4), and the details say the signature
was BER. High tag numbers are not read.
- pdf: validation data added after signing is a permitted change, but only
objects reached through /DSS /Certs|CRLs|OCSPs|VRI by the keys the standard
defines, with the shape of their role, and new or only grown since signing.
A /DSS entry naming other objects (a page's contents) excuses nothing.
- pdf: only the signed catalog's /Metadata is the document's XMP. A
/Type /Metadata or /Type /DSS label on any other dictionary or stream, even
one it already had when signed (a page's Form XObject), makes nothing
permitted. Negative tests at every DocMDP level.
Six of the new tests fail on the previous code: the BER signature, nested
segments, the store-growth rules, and a dictionary or stream carrying a
/Type /Metadata or /Type /DSS label. The last one let a drawn Form XObject be
rewritten under a Valid signature.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* M9: comparing a grown document security store is linear for references and bounded otherwise
only_grew compared arrays element by element, so a crafted /Certs of ~100k entries could hang
validation. Reference arrays are compared as sets; other arrays only up to 10,000 elements.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>