Skip to content

test(seal): prove each signature carries its own seal - #218

Merged
lsnepomuceno merged 1 commit into
mainfrom
test/independent-seals
Aug 9, 2026
Merged

test(seal): prove each signature carries its own seal#218
lsnepomuceno merged 1 commit into
mainfrom
test/independent-seals

Conversation

@lsnepomuceno

Copy link
Copy Markdown
Owner

Closes the gap noticed while answering a question: the claim that each signature carries an independent seal was true by construction and untested.

SealTest covered a seal on one signature. The multi-signature tests covered several revisions without seals. Nothing covered both at once, so a regression would have stayed silent until someone opened a file in a reader.

What is asserted

One image and one form XObject per signature. Not one pair reused by both.

Two distinct rectangles, neither of them the invisible 0 0 0 0.

Different filters, DCTDecode for the rendered seal against FlateDecode for the supplied PNG, which shows the second signature carries the caller's image rather than a re-render of the certificate.

Both signatures then validate, and the report counts two.

Both assertions were checked against a wrong document first

An assertion that cannot fail is not an assertion. Measured before writing them:

Probe Result
One sealed signature images=1 forms=1, so counting two counts per revision rather than matching a constant
Two signatures at the same placement rectA=[155 250 205 275], rectB=[155 250 205 275], identical, so the difference assertion is sensitive to placement rather than trivially true

samples/two-seals.pdf

The same case shipped for readers rather than for the suite, since the point of this one is visual: the first seal is rendered from the certificate at x 150 / y 240, the second is src/Resources/img/sign-seal.png at x 30 / y 60.

Verified with poppler pdfsig 25.12:

#1  [0 - 9190],  [25576 - 42777]   Not total document signed
#2  [0 - 42906], [59292 - 76506]   Total document signed

The first covering only its own revision is the expected reading, not a defect: the second signature came after it. samples/README.md says so explicitly, since that line reads like a warning to anyone who has not met incremental signing before.

poc/sign-samples.php regenerates it alongside the others.

Verification

composer check on PHP 8.5 through .docker: green, 136 tests, one more than before.

The claim was true by construction and untested. SealTest covered a seal on
one signature, the multi-signature tests covered revisions without seals, and
nothing covered both at once, so a regression would have been silent until
somebody opened a file.

Two things are asserted, and both were checked against a deliberately wrong
document first, since an assertion that cannot fail is not one:

One image and one form XObject per signature. A single sealed signature
produces one of each, so counting two is counting per revision rather than
matching a constant.

Two distinct rectangles. Signing twice at the same placement produces
"155 250 205 275" both times, so the difference assertion is sensitive to
placement rather than trivially true.

The filters differ too, DCTDecode for the rendered seal against FlateDecode
for the supplied PNG, which shows the second signature carries the caller's
image and not a re-render of the certificate.

samples/two-seals.pdf ships the same case for readers rather than for the
suite. poppler 25.12 reports both valid, the first covering its own revision
and the second the whole file.
@lsnepomuceno
lsnepomuceno merged commit c6e210a into main Aug 9, 2026
3 checks passed
@lsnepomuceno lsnepomuceno self-assigned this Aug 9, 2026
@lsnepomuceno lsnepomuceno added documentation Improvements or additions to documentation php Pull requests that update php code labels Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation php Pull requests that update php code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant