From 0058478ad0f82e8058c6bba15b2df66588132073 Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Mon, 7 Sep 2026 04:18:37 -0400 Subject: [PATCH] jwp-receipts: typed claims, key discovery, and a second binding class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three holes, all in the receipt layer, all found by the landscape survey and verified against this file. 1. THE RECEIPT ANSWERS ONE QUESTION (§2.4.3). `group` and `object` say what was delivered, by whom, at what energy cost. Three other bodies now issue per-object signed artifacts answering different questions over the same objects: a lawful-basis declaration bound to a media service (ABNT NBR 25608 §8.8), a transparency-log inclusion proof (RFC 9942 COSE Receipts), and a recovered-binding assertion (C2PA soft binding). Rather than grow a field set per question, `kind: "claim"` carries a class discriminator and a class-scoped body on the same chain. The rules are about refusal, not carriage: a verifier MUST reject a class it does not know rather than ignore it; the class MUST NOT be inferred from the body's shape; where a claim and a delivery receipt disagree the verifier MUST reject rather than choose; and a claim carries the authority of its signer and no more — `class` says what is asserted, never that it is true. 2. EVERY SIGNATURE PRESUMED THE VERIFIER ALREADY HELD THE KEY (§7). `signer_pubkey` rides in the receipt, which makes the signature checkable but not trustworthy — a forger supplies a key as easily as a signature. For a party who was never on the delivery path that presumption fails, and every guarantee in §6 fails with it. Adds a REQUIRED `signer_locator` (cert-uri / trust-record / did), borrowing the design H.274's `dsci` SEI already uses rather than inventing a third shape. A verifier that cannot resolve it MUST report "signature-valid, identity-unconfirmed" — a materially weaker statement that MUST NOT be reported as verification. The locator MUST be inside the signed payload: one a relay can rewrite is one an attacker can rewrite. 3. A RECEIPT DID NOT SURVIVE NORMAL DISTRIBUTION (§8). The hard binding is BLAKE3 over envelope bytes, destroyed by any transcode, remux or platform re-encode. Those are not attacks; they are what distribution does. With one binding class a re-encode left no recovery path: receipt intact, object no longer matching, nothing connecting them. Adds an explicitly weaker second class — a soft binding computed from the samples plus a resolution endpoint — with the ancestor rule as its centre: a recovered receipt attests THE ANCESTOR the object was derived from, not the object in hand, and an implementation that cannot express that distinction MUST NOT implement soft binding at all. A recovery presented as a hard binding is worse than no recovery, because it launders a transformation nobody attested. No new cryptography and no new transport. §1 already anticipated a MoQT object header as a carrier, which is the slot #80 §4.4 identifies. Co-Authored-By: Claude Opus 5 --- wai/extensions/jwp-receipts.md | 129 +++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/wai/extensions/jwp-receipts.md b/wai/extensions/jwp-receipts.md index 0aeac8fc5..4b7c320b4 100644 --- a/wai/extensions/jwp-receipts.md +++ b/wai/extensions/jwp-receipts.md @@ -27,6 +27,14 @@ In scope: parent-receipt linkage for cross-hop preservation. - Verifier behaviour, relay preservation requirements, and conformance test vectors. +- A typed **claim** receipt (§2.4.3) so statements other than delivery — + energy, task fidelity, lawful basis, log inclusion, recovered binding — + ride the same chain without growing a field set per question. +- **Key discovery** (§7): where a signer's key is authoritatively + published, so a party who was never on the delivery path can do more + than check that a signature is internally consistent. +- **Binding recovery** (§8): a second, explicitly weaker binding class + for objects whose bytes changed in normal distribution. Out of scope: @@ -154,6 +162,60 @@ Required fields: | `origin` | string | Datacenter / origin label. | | `merkle_proof` | object | Inclusion proof: `{ "siblings": [32 B hex, …], "direction_bits": u64, "leaf_index": u64 }`. `direction_bits` bit `i` is `1` iff the current node at level `i` is the right child. | +#### 2.4.3. `kind: "claim"` (typed claim receipt) + +`group` and `object` answer one question — *what was delivered, by whom, +at what energy cost*. Three other bodies now issue per-object artifacts +answering different questions over the same objects: a lawful-basis +declaration bound to a media service (ABNT NBR 25608 §8.8), a +transparency-log inclusion proof (RFC 9942 COSE Receipts), and a +recovered-binding assertion (C2PA soft binding, §8). Each is a signed +statement about an object that is not a delivery statement. + +Rather than grow a field set per question, a `claim` receipt carries a +**class discriminator** and a class-scoped body, and links into the same +chain as `group` and `object`. + +Required fields: + +| field | type | meaning | +|---|---|---| +| `kind` | string | MUST equal `"claim"`. | +| `class` | string | Registered class identifier; see the table below. | +| `subject` | 32 B hex | `content_hash` (§2.1) of the object the claim is about. | +| `body` | object | Class-scoped payload. Its shape is fixed by `class` and by nothing else. | +| `signer_pubkey` | 32 B hex | Ed25519 verifying key per RFC 8032. | +| `signer_id` | string | Bound identity string. | +| `sig` | 64 B hex | Ed25519 signature over the canonical bytes of the receipt with `sig` removed, under domain `"moq-jwp:claim\x01"`. | +| `parent_receipt_hash` | 32 B hex or `null` | As §2.4.1. | + +Classes defined here: + +| `class` | body asserts | signer is | +|---|---|---| +| `energy` | a measured energy figure with its acquisition class, per [energy-measurement](energy-measurement.md) | whoever performed the measurement | +| `task-fidelity` | a claim tuple per [task-fidelity](task-fidelity.md) §2 | whoever ran the test | +| `lawful-basis` | the basis on which the sink's observations may be processed | the service operator | +| `log-inclusion` | inclusion of a prior receipt in an append-only log | the log operator | +| `binding-recovery` | a binding recovered after the hard binding was destroyed (§8) | the resolver | + +Rules: + +- A verifier **MUST** reject a `claim` whose `class` it does not know. + Ignoring an unknown claim is not the same as accepting the object + without it, and silently discarding a class a publisher thought + load-bearing is the failure this rule exists to prevent. +- The `class` **MUST NOT** be inferred from the body's shape. +- A `claim` **MUST NOT** restate a field that `group` or `object` + already carries for the same subject. Where both exist and disagree, + a verifier **MUST** reject rather than choose. +- The signer of a `claim` **MAY** differ from the object's publisher — + that is the point of the class. A verifier **MUST NOT** treat a + claim signature as evidence about delivery, or a delivery signature + as evidence about a claim. +- A `claim` carries the authority of its signer and no more. `class` + says what is being asserted; it never says the assertion is true. + ### 2.5. Worked example Manifest JSON (group receipt, formatted for readability — wire form @@ -465,3 +527,70 @@ multi-signer container; the v1.0 wire format remains a strict subset. `joules_total` are unit-of-energy fields, not unit-of-account fields. Conversion to currency, carbon, or grid-mix CO₂ is performed by downstream aggregators and is not normative here. + +## 7. Key discovery (REQUIRED for a standalone verifier) + +Every signature in this extension is verified against `signer_pubkey`, +carried in the receipt. That makes the receipt self-describing and the +signature checkable — but it does not make it *trustworthy*: a forger +supplies a key as easily as a signature. Until now WAI has presumed the +verifier already holds the right key by some means outside the +specification. For a party who was never on the delivery path, that +presumption fails, and every guarantee in §6 fails with it. + +A receipt **MUST** carry a `signer_locator` object naming where the +signer's key is authoritatively published: + +| field | type | meaning | +|---|---|---| +| `scheme` | string | `"cert-uri"`, `"trust-record"`, or `"did"`. | +| `value` | string | The URI, trust-record identifier, or DID. | + +This follows the design ITU-T H.274's `dsci` SEI uses for the same +problem — a certificate URI or an ISO/IEC 21617-1 trust-record URI — +rather than inventing a third shape. + +- A verifier **MUST NOT** treat `signer_pubkey` as authoritative merely + because a signature over it validates. It **MUST** resolve + `signer_locator` and confirm the key, or report the receipt as + **signature-valid, identity-unconfirmed**, which is a materially + weaker statement and **MUST NOT** be reported as verification. +- A verifier that cannot resolve the locator **MUST NOT** fail closed on + that ground alone if it holds the key by prior arrangement; it + **MUST** record which path it used. +- `signer_locator` **MUST** be inside the signed payload. A locator a + relay can rewrite is a locator an attacker can rewrite. + +## 8. Binding recovery (the second binding class) + +§2.1 binds a receipt to an object by BLAKE3 over the envelope bytes. +That is a **hard binding**, and it is destroyed by any change to those +bytes — a transcode, a remux, a platform re-encode. Those are not +attacks; they are what normal distribution does. + +WAI has exactly one binding class, so today a re-encode leaves no +recovery path at all: the receipt is intact, the object no longer +matches it, and nothing connects them. + +A second class is therefore defined, and it is deliberately weaker: + +- A publisher **MAY** additionally register a **soft binding** — a + derived descriptor computed from the samples rather than the bytes, + which survives re-encoding — together with a `resolution_endpoint` + at which a receipt may be recovered from that descriptor. +- The algorithm **MUST** be named from a public registry and **MUST + NOT** be described only as a vendor name. C2PA's soft-binding + algorithm registry is a usable list. +- A receipt recovered by soft binding **MUST** be reported as a + `claim` of class `binding-recovery`, never as a hard binding. +- **The ancestor rule.** A recovered receipt attests **the ancestor** + from which the object in hand was derived — not the object in hand. + A verifier **MUST** state this when reporting a recovered binding. + Any implementation that cannot express the distinction **MUST NOT** + implement soft binding at all; a recovery presented as a hard + binding is worse than no recovery, because it launders a + transformation nobody attested. + +Soft binding does not repair the hard binding, and this section does not +claim it does. It supplies a path where there was none, at a stated and +strictly lower grade. -- 2.54.0 (Apple Git-157)