open-standards/wai/signer-conformance/README.md
Transaction Science 3fac029a71 wai: every corpus states the provenance of its content
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]>
2026-10-07 10:09:21 -04:00

86 lines
5.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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