DEV Community

Michael "Mike" K. Saleme
Michael "Mike" K. Saleme

Posted on

A signature proves who signed the receipt. It does not prove the receipt is true. Here is how you test the difference.

The industry is standardizing on agent receipts: signed records that an action was authorized, that a check ran, that a payment settled. The signature is the easy part. The hard part is that a correctly signed receipt can still be false.

A real receipt verifier and a plain signature checker look identical from the outside. Feed either one a valid receipt and both say "accepted." The difference only appears on a receipt that is correctly signed but whose claim is not supported: evidence omitted, evidence swapped after it was attested, a check bound to the wrong tool set, an authorization for different parameters, an acknowledgment for a different action. A signature checker accepts all of those. A verifier has to reject them.

So you cannot tell which one you have by reading the code or trusting a green badge. You can only tell by running it against receipts built to be validly signed and claim-invalid, and watching whether it rejects them.

That is a conformance suite, and we published one.

Nine receipts a conforming verifier must reject, plus two acceptance controls so an implementation cannot pass by rejecting everything. Each reject vector names exactly what the verifier has to recompute from the referenced evidence: the admitted action digest, the evidence digest, the tool set the check actually covered, the attesting authority and whether it is independent of the emitter, the freshness window. Naming the recomputation is the point. It stops a verifier from passing on string-level checks.

The receipt decomposes into four properties, and the signature supports only one of them: integrity, authorization, occurrence, and the check itself. Evidence for the last three has to come from distinct trust domains, not from the emitter, because the emitter is exactly the party the threat model permits to lie. A receipt attested only by the thing whose behavior it certifies is testimony, not evidence.

The two hardest vectors are a phase pair. One carries an authorization bound to different parameters than the action requested, and fails at admission, before anything runs. Another carries an execution acknowledgment linked to a different action than the one admitted, and fails after, on linkage. Same trust failure, different phase, different invariant. A verifier that only checks signatures accepts both.

If you are building or buying receipt verification, the question is not "does it check the signature." Every implementation checks the signature. The question is "does it recompute the claim," and the only honest way to answer it is to run it against vectors designed to be signed and wrong.

A receipt is only as trustworthy as the bindings a verifier is willing to recompute.

Everything it does not recompute, it is taking on the emitter's word.

The vectors are open, reproducible, and generated from a working verifier, not hand-authored. If you have a receipt or trust-envelope implementation, run it against them. If it accepts one of the nine, you have a signature checker, not a verifier.

Top comments (0)