Back to Blog
tutorials

How to Verify a Document Proof Package Without the Vendor

Daan van Tongeren

PDFen Team

August 07, 2026
How to Verify a Document Proof Package Without the Vendor

A proof package is worth having only if you can check it offline, with tools already on the machine, against a trust root you fetch yourself instead of one that travelled inside the package. If the check stops working the day the supplier stops trading, you are not holding proof. You are holding a subscription.

Key Takeaways

  • A proof package must let a stranger recompute the claim from scratch. That takes four things: your document, the timestamp token, the path from your document's hash back to the stamped root, and an instruction written for someone who has never seen any of this. Only the document stays with you.

  • A Merkle tree is a privacy decision before it is a speed decision. A flat list would force you to hand over the hashes of every other document anchored that day, including other people's.

  • Under eIDAS Article 41(2), a qualified electronic time stamp enjoys the presumption of accuracy of the date and time and of the integrity of the bound data. The presumption sits in Article 41, not 42.

  • Proof retention at PDFen defaults to twenty years, set separately from how long the document itself is kept.

  • Keep the original bytes exactly as received. Re-saving breaks the match, and the Council of State has seen that happen inside the courts' own pipeline.

Two hands comparing two printed copies of the same report side by side, a pen marking one line in the margin The other side checks its own copy, never ours: a package carrying our copy of the document would only ever prove something about our copy.

What should be in a proof package, and why?

Four things have to be present before a stranger can recompute the claim. The document itself. The token binding a hash to a moment. The path from that document's hash up to the stamped root. And a written instruction, because a third party forced to guess the construction is not verifying anything.

One of the four is missing from what we send, and it is the document. In the ordinary case the package holds four files: proof.json, README.txt, timestamp.tsr and the intermediate certificates in tsa-chain.pem. The last two appear once the round has actually been stamped and the authority has supplied its chain.

The reasoning behind the absent document is uncomfortable but sound. A package containing our copy would only prove something about our copy. The copy in dispute sits on your desk, so that is the one belonging beside these files during the check. Two side effects follow: a leaked link hands over no client material, and the package stays small enough to travel as an e-mail attachment.

proof.json carries the document's SHA-256, its position in the tree, the sibling hashes with their left or right side recorded, the root hash, the name of the timestamping authority, and a written-out rule for how leaves and nodes are combined. That last item matters more than it looks. Without it a third party has to guess how the tree was built, and a guess is not a verification.

Why a Merkle tree instead of a flat list?

Because of privacy, not throughput. That ordering is the whole argument.

Suppose a day's documents were anchored as a flat list, with the root a hash over that list. To prove your contract was in it, you would have to reproduce the list. All of it. That means handing the other side the hashes of every other document stamped that day, including documents belonging to strangers to your dispute. You would leak a client list to win a point about one PDF.

A tree removes the problem. You supply only the nodes sitting directly beside your path upward. Every doubling of the batch adds exactly one more sibling hash, so a busy day costs a few extra lines in a JSON file rather than a disclosure incident.

Anchoring runs once a day, at 03:00 Europe/Amsterdam. One round, one tree, one call to the timestamping authority, whether the round holds four documents or four thousand. Only the root hash leaves the building. That economy is what lets the daily round cover everything that came in, instead of only the documents somebody remembered to flag. Stamping one specific PDF on demand is a separate, paid action: the qualified timestamp tool does that, one file at a time.

Why does the proof survive when the file is long gone?

Because the record and the document are different objects with different lifespans, and we treat them that way deliberately.

A proof record is a hash, a leaf position, a short list of sibling hashes and a reference to its round. A few hundred bytes, with no storage cost worth naming. The document it describes is another matter: it follows the ordinary retention period and gets cleaned up on schedule, as it should.

Proof retention here defaults to twenty years, configured independently of file retention. Deleting the result does not remove the hash record. Five years after a matter closed, with the PDF long since purged, the arithmetic still runs, provided the other party kept their copy.

This is where supplier incentives quietly bend. If your evidence lives inside someone's archive, they need you to keep paying for that archive, and unlimited storage stops being generosity and becomes the lock.

Why is the root certificate deliberately missing?

Because validating a token against a root that arrived in the same envelope is an integrity check wearing the costume of a trust check.

Consider what you are testing. The chain in tsa-chain.pem shows the token is internally consistent. Fine. But consistency with a root we handed you only proves that we are consistent with ourselves. Anyone able to forge the token could forge the root beside it. The check passes, the conclusion is worthless, and the whole thing looks like real verification.

So the sequence has three steps:

  1. Fetch the root certificate yourself, from the timestamping authority's own publication or through the EU Trusted List Browser. proof.json names the authority, so you know whose root you are looking for. Ours is Disig a.s. in Slovakia.

  2. Recompute the root hash from your document's SHA-256 and the sibling hashes, following the rule written out in proof.json, then check that your result equals the root hash recorded there.

  3. Verify the token over that root hash:

openssl ts -verify -digest <root_hash> -in timestamp.tsr \

-untrusted tsa-chain.pem -CAfile <root you fetched yourself>

The obvious counter-question is how you know you fetched the right root. You do not need to know in advance: point the command at the wrong one and it fails rather than quietly passing. The root either signs the chain behind your token or it does not.

The same reasoning governs the word "qualified". A policy identifier inside a token is a self-declaration by whoever built the token. Qualified status follows instead from a provider's entry on a national Trusted List, which is public and readable without us. Ours is listed there for TSA/QTST with status granted. Go and look.

Why does anchoring never touch the file itself?

Because the hash is computed over bytes, and writing the outcome back into the document would change those bytes.

It is a small loop that catches a surprising number of products. Hash the document, obtain an anchor, then helpfully stamp that anchor into the PDF so the reader can see it. The stored PDF now no longer matches the hash you just anchored. The proof invalidates itself in the act of announcing itself. So the stored file stays byte-for-byte identical: nothing written into it, nothing re-saved, nothing optimised. The record lives beside the document, never inside it.

The same discipline applies upstream. Under RFC 3161 only the message imprint travels to the authority, and a signed token over that imprint travels back. For privileged material, that is often the reason the conversation is possible at all.

What must the receiving party do, and what breaks it?

Keep the original document exactly as it arrived. Do not re-save it. Do not open it in Acrobat and save. Do not run it through a converter to tidy it up for the case file. A hash over bytes is unforgiving, and re-saving rewrites bytes even when nothing visible changed.

Dutch procedural law points the same way for its own reasons. Article 156a of the Code of Civil Procedure permits an electronic deed to be stored electronically, on condition that the storage allows an unchanged reproduction of the content for a period suited to its purpose. Unchanged reproduction is the standard your archiving step has to meet. A helpful re-save fails it silently.

That failure is not theoretical. In ECLI:NL:RVS:2024:4237 the Council of State tried to validate an electronically signed detention order and got an error. The expert report of 3 September 2024 explained why: after signing, the ministry sent the file to the courts, where it was converted to a format for digital archiving. That conversion, in the ruling's own words, damaged information in the part of the file where the electronic signature sits.

Read the sequence again. The signature was fine; the expert validated it in the original. The archiving step produced the broken copy, and that broken copy went into the digital case file. The ruling does not say which format it was converted to, so neither will we.

The rule for anyone handed a document with proof attached: file the received bytes untouched and work from a duplicate. Already saved over it? Ask the sender for the original.

How does this compare with a document timestamp in Adobe Acrobat?

Acrobat's document timestamp is a real mechanism, and a third party can check it. The differences are about where the proof lives, who picks the time source, and what happens to the document.

 

Proof package (PDFen)

Document timestamp in Adobe Acrobat

Who supplies the time

A qualified provider on the EU Trusted List; anchoring runs daily

Nobody, until you configure it. You enter a timestamp server yourself in the signature preferences (DigiCert's walkthrough for Acrobat Reader)

Does the document change

No. The stored PDF stays byte-for-byte identical

Yes. The timestamp is written into the PDF

Where the proof lives

In a separate record of a few hundred bytes

Inside the document. Lose those exact bytes and you lose the timestamp

Trust root

Not supplied. You fetch it yourself

The viewer's own trust store decides what displays as trusted

Third-party recomputation

Yes, with openssl ts -verify

Yes, if revocation data was captured too (SSL.com)

What you need first

The document

A timestamping authority you have already chosen

Retention of the proof

Set separately, twenty years by default

As long as you keep that exact file

Which Acrobat editions and plans carry which signing features is Adobe's to state, on its own pricing pages. We are not going to quote figures that move.

Neither column is the villain here. If you already know which timestamping authority you trust and why, Acrobat's built-in route is respectable. Most people fail at that second clause.

What does this package not prove?

That the document existed no later than the moment recorded in the token, and that it has not changed since. That is the entire claim, and stretching it is how people lose credibility in a hearing.

It does not prove who made the document. There is no identity in the construction: no signature, nobody vouching for authorship. Authorship is a signature question, and our legal overview sets out where that line runs. Nor does it prove the contents are true. A meticulously anchored invoice for work that never happened is still an anchored lie.

An anchor is an upper bound, never a birth certificate. Existed no later than. Not created on. We keep a list of phrasings barred from our own copy, and "created on" sits on it, beside "legally valid" and "certified". The narrow version survives contact with a courtroom. The marketing version does not.

Within those limits the value is real. Article 41(2) attaches a presumption of accurate time, and of integrity of the bound data, to a qualified stamp. In practice that puts the onus on whoever wants to dispute the date.

Frequently asked questions

Do I have to send my document to the timestamping authority?

No. Under RFC 3161 the client sends only the message imprint, which is a hash. The document never leaves your environment, which is why this remains usable for privileged material you could not upload to a validation website.

Can I still check this if PDFen stops existing?

Yes, and that is why the package ships a token rather than a link. Everything the check needs sits in the package, plus a root you fetch from the authority or the Trusted List, and the tool is openssl rather than our software. Our README puts the verify page last, as a courtesy, below the offline command.

Can somebody just change the creation date inside a PDF?

Yes. Creation and modification dates inside a PDF are ordinary metadata fields that can be rewritten without special skill, and the machine clock can be set back before the document is ever generated. A date worth relying on therefore has to come from outside the file, signed by a party with no stake in it.

I opened the file and saved it. Did I break the proof?

For that copy, probably. Saving rewrites bytes even when nothing visible changed, and the hash covers bytes. Go back to the copy exactly as received, and if it is gone, ask the sender to supply it again. A fingerprint comparison can still indicate whether the content shifted, though that is a weaker statement than a byte match.

Why is the root certificate not included in the package?

Because a root that travelled with the token proves nothing about the token. Validating against it is an integrity check dressed as a trust check. Fetch the root from the provider or the EU Trusted List Browser, then verify against that instead.

Where does this leave the document on your desk?

If a document might one day be argued about, the useful moment is now, while nobody is arguing yet. Fix the bytes, get them anchored, keep the received copy untouched. The discussion then moves from "do we believe this file" to "what does this file say".

Handed a document with proof attached? /verify accepts a file, a hash or a verification code, and reports the outcomes separately rather than collapsing them into a single green tick. It is a convenience, and it says so on the page. The commands in the package are the proof.

Daan van Tongeren is the founder of PDFen, where he designed the proof package described here, including the README line that tells you not to trust the root inside it. Corrections, or an account of a case where this went wrong in practice, are welcome via the contact page.