Each *-conformance/README.md now has a line that begins **Provenance.**: the generator that writes the corpus and its input, and for an input not generated here its source and licence. OER/2's corpus is settled and left as it is. byte-exact-conformance and quantum-ops-conformance get a README. wai-rs/tests/vectors/README.md gives a row per vector directory. Where the staged-delivery, staged-measurement and telemetry READMEs called their content "synthetic", that sentence is now their provenance line. Two vectors are recorded as not generated, from the provenance audit: int_video's video_real (a pan over a third-party picture) and neural-conformance's photo (a third-party picture, with a decoder trained on unrecorded images). Their lines say so and that they are to be replaced; the photo bullet no longer names the picture's source product. tests/corpus_provenance.rs requires the line in every corpus and a row for every vector directory; CI runs it with the carriage tests. CONTRIBUTING states the rule: corpus content is generated here, or recorded with a licence that permits redistribution, and a word like "synthetic" is not a provenance line. Co-Authored-By: Claude Opus 5.5 <[email protected]>
86 lines
5.8 KiB
Markdown
86 lines
5.8 KiB
Markdown
# signer-conformance
|
||
|
||
Conformance for [key discovery, signing authority and module authorisation](../extensions/jwp-receipts.md)
|
||
(§7): the forms of a `signer_id`, an origin's key log, anchoring, the confirmation a
|
||
verifier reports beside a receipt's or claim's verdict, and the checks a sink runs
|
||
before it runs a decoder or reconstruct module.
|
||
|
||
**Provenance.** Generated here by `wai_signer_vectors`, from published one-byte seeds (below). No media.
|
||
|
||
Each vector declares **one verdict**, set from the text in the generator and never
|
||
copied from a verifier. A verifier that reaches another verdict — another level, step,
|
||
reason, head or anchor path — does not conform.
|
||
|
||
## Files
|
||
|
||
| file | what it is |
|
||
|---|---|
|
||
| `vectors.json` | The corpus. Regenerate with `wai_signer_vectors gen .`; `wai_signer_vectors verify .` checks the canonical fixture, a byte-identical regeneration and every verdict. |
|
||
| `signer_verify.py` | A second verifier, written from §7, that shares no code with `wai-rs`. It takes from `../claim-conformance/claim_verify.py` only what §7 reuses of §3.1: the JSON reading, JCS, BLAKE3, the strict Ed25519 rule, the RFC 3339 syntax and claim verification. |
|
||
|
||
## Procedures
|
||
|
||
**`confirm`** (§7.4) runs on an item and reports `confirmed` with the origin, key-id,
|
||
head `revision_hash` and anchor path — and, where they apply, `moved_to`,
|
||
`retired_until`, `untimed=true`, `expected=` and `also_delivers=` — or `unconfirmed` or
|
||
`refused` at a §7.4 step (`1`–`7`, or `6a`) with a reason of §7.7. For a
|
||
`reconstruct-session` claim whose `energy` is not `null`, the verdict goes on with
|
||
`; figure ` and the confirmation of its energy figure, an item of its own (§7.5) that needs
|
||
`reconstruct-session` and the energy authority of its class. The item is either
|
||
`receipt`, a claim's wire form, which is first verified under §3.1 (a claim that fails
|
||
is `reject step=<n> reason=<label>`, and is never confirmed), or `context.item`: the
|
||
`kind` (`group` or `family`), `signer_pubkey`, `signer_id`, `classes` (for a group,
|
||
the acquisition classes its objects' labelled leaves and its labelled total carry; for a
|
||
family receipt, the acquisition classes its figures' labels carry) and its
|
||
signed `time` (or `null`). The authorities an item needs are derived from it by §7.5's
|
||
table. The context also holds `revisions` (revision texts the verifier holds, in any
|
||
order; one that fails rule 1 is not held), `anchors` (`origin`, `revision`, `path`,
|
||
`acquired_at`, and `pinned_revision` for a pinned record, which a forward move keeps)
|
||
and, optionally, `expected_origin` (`origin` and `source`, `relying-party` or
|
||
`subject`).
|
||
|
||
**`anchor`** (§7.3) runs on a key-log document (`log`) retrieved for `origin` by `path`
|
||
(`pinned` or `fetched`, with `acquired_at`), and optionally `current`: an anchor already
|
||
held, with the revisions held with it. It reports `anchored revision=<hash>
|
||
anchor=<path>` — with `pinned_revision=<hash>` where a pinned record moved forward —
|
||
`not_anchored reason=<label>` or `replaced reason=key_log_replaced`.
|
||
|
||
**`module`** (§7.9) runs on a module's bytes (`module`, hex), whether they are a module
|
||
or a pinned parameter set (`object`), a `grant`, the `capability` being decoded, the
|
||
sink's `module_origins` (each an `origin` and `source`), the `revisions`, `anchors` and
|
||
`statuses` the sink holds, its trusted clock (`now`, or `null` for none) and whether
|
||
the envelope leaves a `fallback`. It reports `run` with the granting key's origin,
|
||
key-id, head, anchor path, where its module origin came from (`expected=`) and
|
||
`kill_latency_s`, or `fallback` or `inert` at a §7.9 check with its reason; a digest
|
||
another anchored origin revokes adds `revoked_by=<origin>`. A parameter set that is not
|
||
held adds the refusal SPEC §3.1's check 5 reports: `refusal=prior_revoked` where its
|
||
digest is revoked, `refusal=prior_not_authorised` for any other check. A parameter set
|
||
whose digest the head of an anchor the sink holds revokes is reported at check 5 with
|
||
`revoked_by=<that origin>` and `prior_revoked` whichever check its grant failed first,
|
||
unless the grant reached check 5 itself. For a parameter set `grant` may be `null`, a
|
||
sink that holds no grant: the set is then refused as revoked where its digest is, and
|
||
otherwise reported as `no_grant refusal=prior_not_authorised`.
|
||
|
||
## Keys, origins and times
|
||
|
||
Everything is synthetic. Keys are Ed25519 from published one-byte seeds and attest
|
||
nothing: 0x61 the root in use, 0x62 the key it commits to, 0x68 the key that one commits
|
||
to, 0x63 and 0x66 delivery keys (0x66 also meters), 0x64 and 0x69 metering keys, 0x65
|
||
and 0x6e lawful-basis keys, 0x6b and 0x6c module-granting keys, 0x60 a key added later,
|
||
0x67, 0x6a and 0x6f keys an attacker holds, 0x70, 0x71 and 0x72 the root, committed key
|
||
and module-granting key of a second origin, 0x73 and 0x74 a measurement key and an
|
||
operating-point key, 0x75 a key that signs family receipts with a ModelBased figure,
|
||
0x76 and 0x77 a sink's key for `stage` and a relay's for `delivery-gap`, and 0x78 and 0x79
|
||
a sink's keys for `reconstruct-session`, the second also holding `energy/OnChipCounter`. jwp-receipts §7.8 says such a key must
|
||
never be listed in a real key log: every anchored verdict here carries
|
||
`note=published_test_key`, which is what a verifier reports on finding one. Origins are
|
||
reserved example names (`publisher.example`, with and without port 8443,
|
||
`service.example`, `other.example`). Times are in September 2026 and invented.
|
||
|
||
`signer_verify.py` needs Python 3 with the `cryptography` package. CI runs it on the
|
||
linux runner (`independent-verifiers` in `wai-conformance.yml`), and requires its
|
||
`verdicts` output to equal `wai_signer_vectors verdicts` line for line. By hand:
|
||
|
||
python3 signer_verify.py # every vector, and the fixture against wai-rs
|
||
python3 signer_verify.py fixture # the fixture's bytes, signature and hash
|
||
python3 signer_verify.py verdicts vectors.json
|