Some checks failed
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 1m54s
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 3s
no-vanity-metrics / WAI public text names no comparison (pull_request) Successful in 3s
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 2m7s
no-vanity-metrics / WAI public text names no company or product (pull_request) Successful in 3s
standards workspaces build + test / arl-rs (pull_request) Successful in 1m9s
standards workspaces build + test / aac-rs (pull_request) Successful in 1m18s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 3m36s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 48s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 32s
standards workspaces build + test / eoc-rs (pull_request) Successful in 1m55s
standards workspaces build + test / map-rs (pull_request) Successful in 28s
standards workspaces build + test / mesh-rs (pull_request) Successful in 23s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m33s
standards workspaces build + test / openpay (pull_request) Successful in 1m29s
wai cross-architecture byte-equality / linux-aarch64 (pull_request) Successful in 22s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 1m8s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 1m10s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 5m38s
wai cross-architecture byte-equality / macos-arm64 (reference) (pull_request) Successful in 29s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m16s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m17s
standards workspaces build + test / sense-rs (pull_request) Successful in 1m6s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 8m7s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 35s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 3m39s
wai cross-architecture byte-equality / linux-x86_64 (pull_request) Successful in 27s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 2m41s
wai-pack / wai-pack · linux-arm64 (pull_request) Failing after 59s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 6m36s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 13s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 12m59s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 8s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 24s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 45s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 1m40s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 31m50s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 2m3s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 10m46s
wai-pack / wai-pack · macos (pull_request) Failing after 16s
WAI HTTP delivery / HTTP corpus · falsification and differential (pull_request) Successful in 1h19m18s
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Successful in 1m4s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 1m5s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m10s
wai-pack / wai-pack · linux (pull_request) Failing after 15s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Failing after 55s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 20s
wai-pack / wai-pack · dependency licences (pull_request) Failing after 1m8s
WAI section references / cited sections exist (pull_request) Successful in 10s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 4m22s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Failing after 2m24s
workflow hygiene / every workflow supersedes its own queue (pull_request) Successful in 3s
jwp-receipts §2.4.12 (classical_declared and rule 9), the CHANGELOG and the claim-conformance README (the envelopes context and the sweep table's SPEC §6.4 row, the one that cites rule 9): this revision derives the classical length from the WAI2 envelopes held in full; a sink that received a WAI2 only as byte ranges holds its head, not the envelope, and cannot yet state its classical rendition. Recording such a session is HTTP session work still to come. Co-Authored-By: Claude Opus 5.5 <[email protected]>
553 lines
46 KiB
Markdown
553 lines
46 KiB
Markdown
# claim-conformance
|
||
|
||
Conformance for [typed receipt classes and the soft binding class](../extensions/jwp-receipts.md)
|
||
(§2.4.3–§2.4.12, §2.6, §3.1–§3.2): `claim` receipts of class `energy`,
|
||
`lawful-basis`, `log-inclusion`, `soft-binding`, `binding-recovery`, `measurement`,
|
||
`operating-point`, `stage`, `delivery-gap` and `reconstruct-session`, and recovery
|
||
through a soft binding under the ancestor rule.
|
||
|
||
**Provenance.** Generated here by `wai_claim_vectors`, from fixed seeds and fixed values (below). No media.
|
||
|
||
Each vector declares **one outcome**: `accept` — with the claim's `link_hash`, which
|
||
pins the canonical form, or for a recovery the ancestor it attests — or `reject` at a
|
||
named §3.1 step with a named reason. A verifier that rejects for another reason, or at
|
||
another step, does not conform.
|
||
|
||
## Files
|
||
|
||
| file | what it is |
|
||
|---|---|
|
||
| `vectors.json` | The corpus. Every vector declares one outcome, and every reason of §3.1 is exercised at least once. |
|
||
| `claim_verify.py` | A second verifier, written from the extension text, that shares no code with `wai-rs`. |
|
||
| `differential.py` | Generates inputs the corpus does not hold, runs both verifiers on them, and reports every input on which their verdicts differ. |
|
||
|
||
Two procedures appear. `claim` verifies one receipt (§3.1), with optional context the
|
||
verifier holds: `previous` (this signer's previous claim, step 5), `object` (the
|
||
delivery receipt's `content_hash` and `joules_micro`), `oer` (an OER receipt and its
|
||
signer keys), `declaration` (the claim a decision or a recovery record names),
|
||
`publisher` (for a recovery record: the key of a verified delivery receipt for the
|
||
declaration's subject — §2.6.3 makes the declaration context only together with it, so
|
||
a vector that gives a recovery record one without the other is malformed), and
|
||
`exact_match_algs` (the soft-binding algorithms the verifier has a matcher for; the
|
||
corpus's matcher is byte equality), and `figure` (the receipt that carries the figure a
|
||
`measurement` or `operating-point` claim names), and `gap_object` (the object receipt at
|
||
a `delivery-gap` claim's coordinates, as the verifier holds it once verified, by
|
||
inclusion at least: `{track_alias, group_id, object_id, content_hash}`), and, for a
|
||
`reconstruct-session` claim, `outputs` (its presentations, `{unit, layers, decoder,
|
||
out_digest}`), `redecoded` (what a verifier that decoded presentations again found,
|
||
`{index, out_digest}`, the index into `outputs`), `objects` (each object received,
|
||
`{content_hash, path, counted, transferred, presented}`, `counted` `null` for an object
|
||
not countable, each (content hash, path) once), `envelopes` (each `WAI2` envelope the
|
||
session received and any `WAI1` envelope the verifier holds, `{envelope}`, its bytes in
|
||
hex, inert bytes included; an object's `counted` is checked against its envelope's
|
||
counted bytes, a decoder entry naming one as its `object` is checked against it, a held
|
||
layer's against what a sink can apply and its `first_unit` against its base's entry's, a
|
||
presentation through such an entry against the layer count and the units it gives, and
|
||
the classical rendition is read from each held `WAI2` envelope, readable or encrypted, by
|
||
§2.4.12's rule 9 — envelopes held in full: a `WAI2` a sink received only as byte ranges is
|
||
not among them, and a session cannot yet state its classical rendition) and
|
||
`stage_claims` (the wire forms of the `stage`
|
||
claims it names). An `object` context may also carry the
|
||
class its leaf is labelled with (jwp-receipts §2.2), in energy-measurement §2.7's JSON
|
||
members, read as that section reads them. A `figure` context is one of two forms:
|
||
`claim`, an `energy` claim's wire form, viewed under the `moq-jwp:claim-link` profile;
|
||
or `view`, a receipt the verifier has already verified, given as `{profile, identity,
|
||
group, figures: [{index, joules_micro, energy_* members}], signer}`, so that neither
|
||
verifier needs to parse every receipt family a figure profile names. `recover` runs §3.2 from a `declaration`, the
|
||
`publisher` key taken from a verified delivery receipt, the `observed_value`, and the
|
||
object in hand's `observed_hash` (or `null`). Both procedures report
|
||
`recovery_signer_mismatch` for a declaration the ancestor's publisher did not sign:
|
||
`recover_signer_mismatch` and `binding_recovery_declaration_not_publishers` are the same
|
||
defect, in a recovery performed and in one recorded.
|
||
|
||
Everything is synthetic. Keys are Ed25519 from fixed seeds (0x11 publisher, 0x22 meter,
|
||
0x33 operator, 0x44 sink, 0x55 resolver, 0x66 relay) and attest nothing; outputs,
|
||
parameter sets and envelope payloads are synthetic bytes; figures, uncertainties and
|
||
calibration references are invented; algorithm identifiers, registries, endpoints, logs
|
||
and calibration certificates use reserved example domains; vocabulary terms are
|
||
illustrative and no verifier here checks them against a vocabulary.
|
||
|
||
## The acquisition class
|
||
|
||
An `energy` claim carries its class as energy-measurement §2.7 writes a class in JSON:
|
||
`energy_provenance`, with `energy_uncertainty`, `energy_counter_filtering` and
|
||
`energy_calibration_ref` present and `null` where the class has no such evidence.
|
||
These vectors are about the class and its evidence:
|
||
|
||
- **Accepted:** an `OnChipCounter` figure with a declared uncertainty (2 % over 1 s) and
|
||
its filtering state `Unknown`, and with `Off`; a `CalibratedInstrument` figure with a
|
||
declared uncertainty (0.5 % over 1 s) and a calibration reference; a counter graded
|
||
`derived`, below what it supports; a 1 µJ figure declared at exactly the resolution
|
||
floor (288,676 ppm); and a counter graded `measured` that names an OER receipt grading
|
||
the same figure `measured`.
|
||
- **Rejected at step 4:** legacy `HwShunt`, alone and with evidence beside it
|
||
(`energy_legacy_acquisition`); a label in another case (`energy_unknown_acquisition`);
|
||
and, as `energy_acquisition_evidence`, a counter without its uncertainty or without its
|
||
filtering state (`null` is not `Unknown`), with a state outside `On`/`Off`/`Unknown`, or
|
||
with a calibration reference; an instrument without its reference, with a reference of
|
||
white space only (a space and U+3000), or with a filtering state; a `ModelBased` figure
|
||
with an uncertainty; an uncertainty of zero ppm, of a zero window, or of 2³² ppm; and a
|
||
1 µJ figure at 288,675 ppm, one below the floor. An `Estimator` graded `simulated` is
|
||
`energy_grade_exceeds_acquisition`.
|
||
- **Rejected at step 2:** an uncertainty without its window (`missing_required_field`),
|
||
with a coverage factor beside it (`unknown_member`), with its relative part as a
|
||
string, written as an array, and a filtering state written as a number (`field_type`).
|
||
- **Rejected at step 3:** a `ModelBased` claim rewritten in transit to `OnChipCounter`
|
||
with an uncertainty and a filtering state: the class and its evidence are signed.
|
||
|
||
## Reports about a figure: `measurement` and `operating-point`
|
||
|
||
A `measurement` claim publishes how one figure was measured — its bracket's values as
|
||
integers, with the liveness facts of each bracket, the build and the power reader — and
|
||
an `operating-point` claim records the operating point it was spent at. Both name the
|
||
receipt that seals the figure by a figure profile (energy-measurement §6.1) and restate
|
||
the figure with its class. The vectors cover:
|
||
|
||
- **The arithmetic of energy-measurement §6.2:** declarations at the least a bracket
|
||
supports and one ppm below it; a figure at a half microjoule, which rounds up; brackets
|
||
whose products exceed 128 and 200 bits; a calibrated instrument declaring more than the
|
||
least; and the declaration a sealing path computes from unrounded samples, which the
|
||
rule refuses. Those values are checked by `claim_verify.py` with Python's integers, and
|
||
the bracket table `wai-rs` pins in `energy_report` is recomputed by it too.
|
||
- **Liveness (§3.1):** a bracket whose rail advanced in too few samples; a frozen rail
|
||
refreshed once, which gives a flat idle bracket and an infinite ratio; a reader holding
|
||
a stale non-zero value, stepped once by a batch refresh, with its honest facts and with
|
||
facts claiming every sample live; a flat idle bracket whose facts claim the rail live
|
||
(a repeated sample is not an advance, so a bracket with `sd_uw` 0 is never live); a
|
||
refresh published in two steps in each bracket, which passes a rule of two consecutive
|
||
advances; facts that describe no sequence; and, accepted, a `ModelBased` figure whose
|
||
bracket shows a dead rail, since liveness binds the hardware classes only.
|
||
- **The power reader (§2.4.8 rule 4):** a hardware figure's report that names no reader
|
||
is refused, with a bracket and without one; accepted, an `OnChipCounter` report with no
|
||
bracket that names its reader.
|
||
- **Every other rule of §2.4.8 rule 5 and §2.4.9,** each with its reason, and two defects
|
||
at once.
|
||
- **Step 2:** derived quantities are never carried (a bracket with its ratio is
|
||
`unknown_member`); the opening and build-profile enumerations; integers, terms and
|
||
absolute URIs where the tables require them.
|
||
- **Step 6:** against an `energy` claim and against views of a provenance receipt, a
|
||
delivered object (named by its leaf index and its group), and an energy binding, which
|
||
no Ed25519 key signs — a `measurement` claim about it is `figure_signer_unknown`, an
|
||
`operating-point` claim about it is accepted; a report by a key other than the figure's
|
||
signer; and an operating point recorded against an unmetered or unlabelled figure. A
|
||
`measurement` claim checked without the receipt that carries its figure is
|
||
`figure_not_held`, whether a stranger or the figure's own signer signed it: only step 6
|
||
shows a report is the figure signer's.
|
||
- **The labelled delivery leaf:** an `energy` claim against an object whose leaf carries
|
||
a class, agreeing, and disagreeing in figure, class or evidence; an `OnChipCounter`
|
||
label with no filtering state reads as `Unknown`.
|
||
|
||
## What a party observed of a stage set: `stage` and `delivery-gap`
|
||
|
||
A `stage` claim (§2.4.10) is signed by the party that observed a stage set
|
||
([staged-delivery](../extensions/staged-delivery.md) §10.2) about its base. A
|
||
`delivery-gap` claim (§2.4.11) is signed by a relay about an object it did not forward.
|
||
The vectors cover:
|
||
|
||
- **Accepted:** a set requested, committed, delivered and presented at stage 2, four
|
||
per-frame first layers and a second layer over the four frames; the second layer
|
||
dropped at a relay, its receipt verified by inclusion, so the stage delivered is 1 and
|
||
a reader derives that the commitment was not met; a base whose receipt was not
|
||
verified first, whose layers applied with no evidence and which states no delivered
|
||
stage, so rule 6 does not apply, and a layer over such a base applied on inclusion
|
||
evidence; a second layer delivered and late for one frame (`partial`,
|
||
`deadline-missed`); two first layers for one frame, both refused and the frame frozen;
|
||
the base alone at `entropy-consistency`, with no stage hash; both layers delivered and
|
||
refused, which meets a commitment to stage 2 with stage 0 presented; a span past the
|
||
set refused `refines-malformed`, which lists its units as stated; and such a layer
|
||
delivered over its bytes, which delivers nothing to the set.
|
||
- **Rejected at step 4,** each rule with its reason: the vocabulary, a point with a
|
||
second member, above 255 or empty, and a request with no delivered stage; a stage hash
|
||
without `decode-equivalence` and the converse; no unit, a last stage with no unit, a
|
||
presented stage other than the lowest with units, more misses than units; objects out
|
||
of order or listed twice, outside the units (and a span past the set refused for a
|
||
reason decided after it was checked), at layer 0, with a reason where none belongs or
|
||
none where one does, a reason staged delivery does not define, an object applied on
|
||
inclusion alone or with no receipt beside a stated delivered stage, a dropped object
|
||
said to be delivered or counted with bytes, objects that arrived with no bytes, more
|
||
object bytes than the set's or all of them, and one layer applied twice to a unit;
|
||
units at a stage no applied object of that layer reaches, a partial first layer
|
||
counted as though applied to all four frames (with all four at stage 1 or at stage 2),
|
||
and a second layer applied over frames the first never reached
|
||
(`stage_units_unsupported`), with the honest partial layer, late for one frame,
|
||
accepted beside them; a partial object over one unit;
|
||
and a delivered stage the objects do not give, among them one over objects that leave
|
||
a unit uncovered.
|
||
- **Rejected at steps 1–3:** a subject that is a receipt; a status, an evidence or a tier
|
||
outside its enumeration; `presented` null; an object without `reason`; units with a
|
||
member they do not define; a count written as a string; two defects, read in the
|
||
table's order; and `presented` rewritten after signing.
|
||
- **`delivery-gap`:** accepted against the object receipt at its coordinates, without
|
||
it, and chained to the relay's previous gap; refused when that receipt names another
|
||
object (`gap_subject_mismatch`), when the receipt given is at other coordinates or is
|
||
given to a claim that is not a gap (`context_mismatch`), for a reason outside its
|
||
enumeration, a date that does not exist, or a subject that is a receipt.
|
||
|
||
## What a sink reconstructed: `reconstruct-session`
|
||
|
||
A `reconstruct-session` claim (§2.4.12) is signed by a sink (key 0x44) about the entry
|
||
object of one session. Most vectors edit one model session: six units. Four are a stage
|
||
set over a pinned base — the session's entry object, a `WAI1` envelope holding a
|
||
four-frame `WIV1` clip — refined to up to three layers, with two late layer objects never
|
||
presented; each decoder entry names the object it decoded, so each layer applied has an
|
||
entry of its own. One unit is decoded from the first rendition of a `WAI2` envelope, a
|
||
one-frame clip, built by `wai-rs`'s container module, whose classical rendition the sink
|
||
did not need. The sixth is decoded on the fallback path after a switch and presented late.
|
||
Each object's figures are what its envelope gives, where the envelope is held: its
|
||
counted bytes (staged-measurement §3), its length, and whether an entry names it.
|
||
The vectors cover:
|
||
|
||
- **Accepted:** the model unmetered and metered under `OnChipCounter`, each against every
|
||
context: its presentations, re-decodes of byte-exact presentations that match, the
|
||
objects received, its base's `WAI1` envelope and the `WAI2` envelope, and the `stage`
|
||
claim it names; the model with no
|
||
context, and with its `stage` claim but neither presentations nor objects; a session
|
||
with no classical length beside an envelope whose second rendition is WAI's own; a
|
||
classical rendition of two other video capabilities; a base forgotten under the record
|
||
budget and decoded again with no layer sent again (`staged-conformance/`'s
|
||
`record_budget`, its envelopes and the `stage` claim of each epoch as the sink's
|
||
observer states them), naming both epochs' claims and only the first's; the base
|
||
fetched twice and listed once; an encrypted `WAI2` envelope received and named by no
|
||
entry, uncounted, and one named by an entry, decoded by a sink holding its key; inert
|
||
bytes no entry names; entries naming a `WAI2` rendition by its exploded envelope's
|
||
content hash; bases carrying a stray `refines`; refinements beside a conforming
|
||
companion; a session holding its base's and a
|
||
layer's envelopes, and a layer with no span through which a unit of its base is
|
||
presented, with its base held and not; output trees
|
||
of 1 to 33 leaves, half through a
|
||
decoder whose replicate class makes it byte-exact and half through one checked for
|
||
membership only, each re-decoded (the second with another digest, which is not
|
||
checked); the caps of switches and missed presentations, written in full and up to the
|
||
cap; and a `measurement` claim on a metered session's figure, under
|
||
`moq-jwp:claim-link`, by the session's key.
|
||
- **Rejected at step 4,** each rule with its reason: units, leaves and paths that do not
|
||
add up, a root without leaves or leaves without a root, three workers, a decoder
|
||
identifier of the wrong grammar, a tier without a pin or one SPEC §3.1 does not define,
|
||
module statuses that do not fit, a rendition past 65535, an entry twice or with no
|
||
leaves, a refinement entry pinned above its class's tier, switches between one path and
|
||
itself, for an unlisted reason, out of order, or written in another number than their
|
||
total (or 1024), missed presentations with no deadline, above the leaves, written in
|
||
another number than `missed` (or 256), zero microseconds late or out of order, a stage
|
||
set listed twice, more bytes discarded than counted, a classical length without its
|
||
source or with an unregistered one, and a figure of zero or of every acquisition-class
|
||
defect.
|
||
- **Rejected at steps 1–3:** a subject that is a receipt; a missing and an unknown member;
|
||
`media`, `framing`, a host profile and a decoder path outside their grammar or
|
||
enumeration; a count written as a string; the host read before the decoders; an energy
|
||
object missing a member; and a figure written into an unmetered session after signing.
|
||
- **Rejected at step 6:** a presentation whose output differs, a list one short, a
|
||
(unit, layers) twice (`session_output_mismatch`); leaves moved between entries, a
|
||
presentation naming an entry that does not exist, entries out of first-use order, paths
|
||
and units that the presentations do not give, a missed presentation never made
|
||
(`session_output_inconsistent`); a byte-exact re-decode that differs
|
||
(`session_output_irreproducible`), and a byte-exact entry relabelled to a class checked
|
||
for membership only by each route — the base's `WAI1` entry, a `WAI2` entry with its
|
||
rendition kept or nulled, a capability another received envelope has — refused against
|
||
the envelope of the object it names, as is an entry naming a rendition whose envelope is
|
||
not held (`session_source_mismatch`); an entry object never received
|
||
(`session_entry_not_received`); an entry's object, or an envelope held, the session did
|
||
not receive (`session_source_not_received`); objects whose counted, uncounted or
|
||
discarded bytes differ, and an object's counted bytes stated above what its held
|
||
envelope gives (`session_counted_mismatch`); an object listed twice
|
||
(`session_object_duplicate`); an object a decoder entry names marked not presented, and
|
||
one no entry names marked presented, the sums restated to agree
|
||
(`session_presented_mismatch`); a `WAI1` envelope with no manifest members and bytes
|
||
of neither magic, each named by an entry (`session_envelope_unreadable`); an
|
||
encrypted envelope named by one, checked against its manifest and rendition table in
|
||
the clear, refused for a classical length unstated, a rendition out of its table,
|
||
another capability, no rendition, a base at two layers, a companion-only capability,
|
||
a nonconforming refinement and a rendition whose `sha256` is malformed (uppercase, 63
|
||
digits, not hex) named by its rendition and by its exploded envelope's content hash,
|
||
and accepted with a well-formed `sha256` its payload does not match; a held inert
|
||
envelope listed with counted bytes 0
|
||
(`session_counted_mismatch`); entries a sink cannot have decoded through — a `WAI2`
|
||
companion, a rendition or refinement whose payload does not match its `sha256`, a
|
||
`WAI1` whose capability is companion-only, a fallback that is
|
||
(`session_source_mismatch`); held layers a sink cannot apply, each row of the table
|
||
below that names rule 5 (`session_layer_not_applicable`); a layer's entry at another
|
||
`first_unit` than its base's entry's, as a `WAI1` layer, as a `WAI2` refinement
|
||
component and across a `WAI2` component and its exploded envelope, and the entries of
|
||
a `stage` claim's base and of a layer it lists at another than the unit `stage_sets`
|
||
names the claim at (`session_first_unit_mismatch`); a unit presented at more layers
|
||
than its layer envelope, its refinement component or its base gives
|
||
(`session_layers_mismatch`); a unit past the base's frames, the same past the frames
|
||
of a base decoded through its manifest's `fallback`, in the fallback's format where
|
||
the capability's is another, outside a layer's span, a `WAI2` rendition's frame, a
|
||
refinement component's base or a `WAI2` base a `WAI1` layer refines, and through an
|
||
entry whose `first_unit` is moved (`session_unit_outside_object`); a second rendition of a capability the session decoded,
|
||
of another media, WAI's own, a companion, a record, a parameter set, a neural codec, an
|
||
unregistered string, or a text capability in a text envelope — none a classical
|
||
rendition the session did not need — and a length one byte off
|
||
(`session_classical_mismatch`); a `stage`
|
||
claim whose signature is broken (`context_invalid`), signed by another key, or not named
|
||
(`context_mismatch`); a `stage` claim counting more units at stage 2 than the session
|
||
presented there, a set placed so that one of its units was never presented, units
|
||
presented at stages no applied layer reaches — among them units presented through a
|
||
first and a second layer the claim lists as late and a first layer it lists as
|
||
refused — a `stage` claim about a base the session never received, objects received
|
||
that hold none of the layers it says arrived, and the forgotten base's session naming
|
||
only its last epoch's claim, which lists no layer, or presenting a unit, through a
|
||
layer it did not hold whole, past both epochs' layers (`session_stage_disagrees`); a session context given to a `stage` claim;
|
||
and a
|
||
`measurement` claim on an unmetered session, which carries no figure.
|
||
|
||
### The sink's refusals, and what the session claim does with each
|
||
|
||
Every refusal a sink makes that decides whether a presentation can happen —
|
||
staged-delivery §3, §4.1 (each step), §4.3–§4.5 and §9; SPEC §4 (step 5, the fallback),
|
||
§5 and §7.1; and `WaiMulti::check_component_roles`'s refinement codes — is either refused
|
||
by a rule of jwp-receipts §2.4.12 against the envelopes, with a vector here that reaches
|
||
it, or left to the signer's statement by the sentence of §2.4.12 that names it ("So,
|
||
deliberately, is each refusal a sink makes that the rules above do not check"). A row
|
||
with neither is a hole; there is none.
|
||
|
||
| source | refusal | §2.4.12 | vectors |
|
||
|---|---|---|---|
|
||
| staged-delivery §3 | `refines` malformed: member types and ranges, `prev`'s forms, adjacent runs of one producer, an array of one run, a producer other than `base` at layer 1, runs that do not sum to the span | rule 5, `session_layer_not_applicable` | `session_refines_layer_zero`, `session_refines_span_empty`, `session_wai2_refines_id_malformed` |
|
||
| staged-delivery §3 | `refines` absent on a refinement-only capability | rule 5 | `session_refines_absent` |
|
||
| staged-delivery §3, SPEC §4 | `refines` on a manifest or rendition whose capability is not refinement-only: a sink routes by capability, so it is a base and the `refines` is ignored (in a `WAI2`, a broken component: no refinement of that envelope is offered) | rules 4 and 7 read it as a base | accepted: `session_stray_refines_base_accept`, `session_stray_refines_layer_over_it_accept`, `session_stray_refines_wai2_rendition_accept`; refused: `session_refines_on_a_base_capability` (at two layers, `session_layers_mismatch`), `session_wai2_refines_on_a_rendition` |
|
||
| staged-delivery §3, §4.1 step 2 | with the base: a span outside its units, runs that do not sum to the units covered | rule 5 | `session_refines_span_past_base_alone`, `session_wai2_refines_span_past_base_alone` (the payload's count right, so the span check alone decides), `session_refines_span_past_base`, `session_wai2_refines_span_past_base`, `session_refines_runs_short`; accepted: `session_refines_runs_whole_accept` |
|
||
| §4.1 step 1 | `capability-not-refinement`: a layer with a fallback (an envelope of any other capability is a base, the row above) | rule 5 | `session_layer_with_fallback` |
|
||
| §4.1 step 2 | `base-absent`: no base decoded with the content hash `refines.base` | where the base is held and is a layer, rule 5; otherwise the signer's statement | `session_refines_base_is_a_layer`, `session_wai2_refines_base_is_a_refinement` |
|
||
| §4.1 step 3 | `base-via-fallback` | rule 5 (applied over an entry decoding the base through its own capability); rule 6 ties the layer only to such entries | `session_layer_base_via_fallback`, `session_layer_tied_to_its_base_not_the_fallback` |
|
||
| §4.1 step 4 | `base-not-admitted` (SPEC §5, *Base admission*) | rule 5 | `session_layer_base_not_admitted`, `session_layer_base_not_admitted_unnamed`, `session_wai2_layer_base_not_admitted` |
|
||
| §4.1 step 5 | `refinement-unattested` | the signer's statement: no context carries a delivery receipt | — |
|
||
| §4.1 step 6 | `payload-malformed`: the payload does not parse, or holds another number of units than the layer covers | rule 5, where the count of units covered is read | `session_layer_payload_not_wir1`, `session_layer_payload_units_short` |
|
||
| §4.1 step 6 | `payload-malformed` for a stream that does not decode canonically; `cost-over-limit`; `chain-over-limit` | the signer's statement; a re-decode of a byte-exact presentation checks its output | — |
|
||
| §4.1 steps 7–10 | `layer-conflict`, `prev-mismatch`, `layer-gap`, `geometry-mismatch`, `deadline-missed` | the signer's statement; rules 7 and 8 bound a layer's presentations to its count and units, and the `stage` claims the stages a unit reached | `session_layers_past_the_object`, `session_unit_outside_span`, `session_stage_beyond_applied` |
|
||
| §4.1, after step 10 | a unit presented from a layer that failed for it | rules 7 and 8 | as above |
|
||
| §4.3–§4.5 | the hold bound, batches, the chain budget for a base | the signer's statement | — |
|
||
| staged-delivery §9 | a `WAI2` component and its exploded envelope are one object | rules 5, 6 and 8 read a base in either form, and rules 4–8 read an entry naming a rendition by its exploded envelope's content hash, with only the `WAI2` held, as naming the rendition; the exploded envelope's own bytes, held, are a separate delivery, and whether they were received on their own is the signer's statement | `session_alias_layer_on_component_accept`, `session_alias_layer_first_unit_not_its_base`, `session_alias_layer_unit_past_the_base`, `session_alias_exploded_held_first_unit`, `session_alias_component_layer_named_as_exploded`, `session_alias_entry_accept`, `session_alias_entry_relabelled`, `session_alias_entry_unit_past_the_base`, `session_alias_refinement_entry_first_unit`, `session_alias_refinement_entry_not_admitted`; a separate delivery: `session_roles_exploded_layer_delivered_alone_accept` |
|
||
| SPEC §4 steps 3, 5 | a `WAI1` decoded through its `capability`, else its `fallback`, and nothing else | rule 4, `session_source_mismatch` | `session_relabel_wai1_base`, `session_relabel_av1` |
|
||
| SPEC §4 | a capability that is not dispatchable never taken; a `WAI1` whose capability is companion-only inert, its fallback never taken | rule 4 | `session_wai1_fallback_not_dispatchable`, `session_wai1_companion_only_capability` |
|
||
| SPEC §4 step 5 | the fallback decodes the same payload | rule 8 reads the units in the payload's own format | `session_unit_fallback_past_the_title`, `session_unit_fallback_format_past_the_title`, `session_unit_fallback_format_audio`; accepted: `session_unit_fallback_accept`, `session_unit_fallback_format_accept` |
|
||
| SPEC §3.1, §4 | a pin that does not resolve, a module not held, a key log's revocation | the signer's statement, which a decoder entry's `pin` and `tier` state | — |
|
||
| SPEC §6.4 | an encrypted envelope is inert at a sink without its key; one holding the key decrypts and dispatches it (step 3), its manifest and rendition table in the clear | rule 3 admits an entry naming it; rules 4–9 read its manifest and table as any held envelope's, a `sha256` that is not well formed included, and what needs a payload — whether a payload matches a well-formed `sha256`, a base's units, a layer's `WIR1` payload — is the signer's statement. Rule 9 reads the classical length from `WAI2` envelopes held in full: of one a sink received only as byte ranges it holds the head and not the envelope, and this revision cannot yet state that envelope's classical rendition (a limitation; recording such a session is HTTP session work still to come) | `session_envelope_encrypted`, `session_envelope_encrypted_layer_accept`, `session_envelope_encrypted_sha256_unmatched_accept`; refused: `session_envelope_encrypted_sha256_uppercase`, `session_envelope_encrypted_sha256_63_digits`, `session_envelope_encrypted_sha256_not_hex` (each `_exploded` too), `session_envelope_encrypted_classical_unstated`, `session_envelope_encrypted_rendition_out_of_range`, `session_envelope_encrypted_relabelled`, `session_envelope_encrypted_no_rendition`, `session_envelope_encrypted_base_layers`, `session_envelope_encrypted_companion_only`, `session_envelope_encrypted_roles` |
|
||
| SPEC §5 | a refinement-only capability is never a `capability` or `fallback` a sink dispatches, a selectable rendition or a companion | rules 4 and 5 | `session_layer_with_fallback`, `session_wai2_refines_on_a_rendition` |
|
||
| SPEC §7.1, *Selection algorithm* | a companion, or a capability that is not dispatchable, is never picked | rule 4 | `session_wai2_entry_names_a_companion` |
|
||
| SPEC §7.1, *Component identity* | `component_digest_mismatch`: a rendition whose payload does not match its `sha256` is skipped, a refinement is not offered | rule 4 | `session_wai2_rendition_digest_mismatch`, `session_wai2_refinement_digest_mismatch` |
|
||
| SPEC §7.1, *Component identity* | an `id`, `sha256` or `companion_of_id` that is not a string: the reader rejects the envelope | rule 3 | `session_wai2_component_id_not_a_string` |
|
||
| SPEC §7.1, *Component identity* | a component, in any role, whose capability is a record or a derivation (`record-or-derivation-as-component`): the envelope does not conform | rule 5, the whole envelope | `session_roles_record_rendition`, `session_roles_record_companion`, `session_roles_derivation_companion`; `pin-conformance/selection.json`: `record_as_rendition`, `derivation_as_rendition`, `record_as_companion`, `derivation_as_companion` |
|
||
| SPEC §7.1, *Refinement components*, *Selection* (every `WaiMulti::check_component_roles` code: the refinement codes, `refines-on-non-refinement`, `refinement-only-as-component`, the companion codes, `component-id-malformed`, `duplicate-component-id`) | a sink offers a `WAI2`'s refinements only when every component conforms | rule 5, the whole envelope | `session_wai2_refinement_identity_missing`, `session_wai2_refines_base_missing`, `session_wai2_refines_base_companion`, `session_wai2_layer_base_not_admitted`, `session_wai2_refinement_prev_missing`, `session_wai2_refinement_before_selectable`, `session_roles_refinement_before_selectable_exploded`, `session_wai2_refines_on_a_rendition`, `session_roles_base_without_sha256`, `session_roles_layer_before_its_prev`, `session_roles_unbound_companion`, `session_roles_record_companion`, `session_roles_derivation_companion`, `session_roles_record_rendition`; accepted: `session_roles_companion_and_refinement_accept`, `session_roles_companion_by_id_and_refinement_accept` |
|
||
| SPEC §7.1, *Companion components* | a companion that breaks rules 1–3 or its `companion_of_id`; an enhancement layer whose base is not unique | for the rendition picked, no bearing: a sink does not apply the companion and may still present the rendition, the output's digest holding whatever was applied; for the envelope's refinements, the row above | `session_roles_unbound_companion` |
|
||
|
||
## Steps 1 and 2: types, and the order a body is read in
|
||
|
||
§3.1 step 2 checks exactly the type a member table gives, member by member in the
|
||
table's order; whatever a class's rules add to a type is step 4's, with the rule's own
|
||
reason. Step-4 vectors pin that boundary from the rules' side: a zero figure
|
||
(`energy_zero_figure`), a zero work count (`energy_zero_work`), an empty basis
|
||
(`lawful_basis_purpose_without_basis`) and an `http` endpoint
|
||
(`soft_binding_endpoint_not_https`). Others pin what the text settles on the other
|
||
side, each signed again after its edit so the edit is its only defect:
|
||
|
||
- **Step 1:** a byte-order mark before the text is `malformed` (RFC 8259 lets a parser
|
||
ignore one); a `class` written as a number is `field_type`, not `unknown_class`.
|
||
- **Step 2, types:** an empty `alg` is not a term; an endpoint with no scheme is not an
|
||
absolute URI; a work unit holding U+3000, a White_Space character outside ASCII, is not
|
||
a term. Each is `field_type` at step 2: `alg` is a term, not a string left whole to
|
||
step 4's registry form; an endpoint's type is an absolute URI, and only `https` is step
|
||
4's; a term's white space is Unicode's White_Space, not ASCII's.
|
||
- **Step 2, order:** a lawful-basis body whose `record` names neither record is
|
||
`field_type`, since `record` is read first; a declaration whose vocabulary has no scheme
|
||
and whose purpose has no basis is `field_type`, since the table reaches `vocabulary`
|
||
first and its encoding is checked as it is read; and one whose first purpose's term
|
||
holds a space and whose second carries an extra member is `field_type`, since each
|
||
purpose is read whole before the next. A reader that checks every purpose's members
|
||
before the other members, or every encoding after every member, reports these two as
|
||
`missing_required_field` and `unknown_member`.
|
||
|
||
## The OER receipts
|
||
|
||
The OER receipts in `oer` contexts are sealed with `wai-rs`'s OER module (its canonical
|
||
form and content address), and read and verified by each verifier's own OER verifier
|
||
under [oer-receipts](../extensions/oer-receipts.md): the reference verifier
|
||
(`../oer-conformance/oer2_verify.py`) reports the valid ones valid, the one altered after
|
||
sealing as `receipt_id_mismatch` and `receipt_hash_mismatch`, and as `malformed` the six
|
||
whose figure is not a quantity oer-receipts §4 reads or whose body holds a number §4
|
||
forbids: `joules_total` as a decimal string of 35 significand digits, as the JSON number
|
||
`0.0120005`, as `-0.0`, as the integer 2⁵³, spelt `+0.012000`, and as `true`. Two valid
|
||
ones state the claim's figure and grade but are not about its object (an unrelated
|
||
object, and the object's hash spelt in uppercase); step 6 rejects both.
|
||
|
||
Seven vectors state a figure step 6 reads at its edges, as oer-receipts §4 reads a
|
||
quantity and compares it exactly: `0.0120005`, exactly half-way, which admits 12,000 and
|
||
12,001 µJ; the same decimal written `1.20005e-2`; the integer `1`, one joule; and two
|
||
figures written with 34 significand digits, the most a decimal string holds — one exactly
|
||
half-way, and one (`0.0120004` followed by twenty-six nines) below half-way only in its
|
||
last digits, which is 12,000 µJ and not 12,001, although rounding it to 28 significant
|
||
digits would make it a tie.
|
||
|
||
## Signatures
|
||
|
||
Seven vectors test §3.1 step 3 beyond an ordinary bad signature.
|
||
|
||
- **Six satisfy the verification equation** — the cofactorless one, and so the
|
||
cofactored one — with the key decoded leniently (`y` reduced mod p, the sign bit
|
||
ignored when x = 0) and `S` reduced mod L. The generator asserts this for each. Step 3
|
||
refuses them on two different grounds:
|
||
- *RFC 8032's own rules*, which step 3 restates: the identity key written with its sign
|
||
bit set, and written with `y` = p + 1, each with a signature made without a private
|
||
key (§5.1.3 decoding refuses both encodings; the point they spell is also of small
|
||
order, so step 3's choice refuses them too); and `S` replaced by `S` + L (§5.1.7
|
||
requires `S` < L).
|
||
- *The choices step 3 fixes*: the identity key and a key of order 8, each "signed" with
|
||
no private key, and a genuine key signing with `R` the identity. An RFC 8032 verifier
|
||
that accepts keys and `R` of small order accepts these three.
|
||
- **One does not** (`signer_key_y_not_reduced`): a key of large order written with
|
||
`y` = p + j. Nobody can sign for that point, so the vector carries another key's
|
||
signature, which fails the equation under any decoding (the generator asserts that
|
||
too). It pins only the reason — `bad_signer_key`, not `signature_verify_failed` — and
|
||
does not show that a lenient verifier would accept a forgery.
|
||
|
||
The `cryptography` package's Ed25519 on its own is more lenient than RFC 8032's
|
||
decoding. Called on each vector's key, signature and signing input (`cryptography`
|
||
50.0.1 over OpenSSL 3.6.4, Python 3.14.7, macOS on arm64), it accepted five of the six
|
||
that satisfy the equation — both non-canonical spellings of the identity and the three
|
||
vectors of the second group — and of the seven refused only `S` + L and the `y` = p + j
|
||
vector. `claim_verify.py` therefore checks no signature with `cryptography`: step 3 is
|
||
`_ed25519.py`, WAI's one Ed25519 rule (SPEC §7 *Signatures*) written from RFC 8032's
|
||
definitions with the standard library, which the verifiers of the JWP, signer, HTTP and
|
||
packaging corpora call too.
|
||
|
||
## Running
|
||
|
||
```sh
|
||
# wai-rs: the canonical fixture, byte-identical regeneration, and every vector
|
||
cargo run --no-default-features --features receipt_claims --bin wai_claim_vectors -- verify ../claim-conformance
|
||
|
||
# the second verifier (needs Python 3 and `cryptography`, with which it signs the fixture)
|
||
python3 claim_verify.py
|
||
|
||
# the two verifiers' verdicts on generated inputs the corpus does not hold
|
||
cargo build --no-default-features --features receipt_claims --bin wai_claim_vectors
|
||
python3 differential.py <target-dir>/debug/wai_claim_vectors [--random N] [--seed S] [--keep FILE]
|
||
```
|
||
|
||
CI runs the first on the conformance matrix (Linux x86_64, Linux arm64, macOS arm64), and the second
|
||
and third on the linux runner, with a fixed seed, in `.github/workflows/wai-conformance.yml`.
|
||
|
||
`differential.py` writes its inputs in the corpus's format with no declared outcome
|
||
and runs each verifier's `verdicts` mode (`wai_claim_vectors verdicts <file>`,
|
||
`claim_verify.py verdicts <file>`), which prints one verdict per input. Its families:
|
||
JSON numbers and strings at the limits of what parsers accept, nesting depth, calendar
|
||
dates, URI authorities, acquisition classes with every combination of label,
|
||
uncertainty, filtering state and calibration reference around energy-measurement
|
||
§2.3–§2.5's rules (calibration references of every White_Space character, and of
|
||
U+001C, U+001F, U+200B and U+FEFF, which are not White_Space), OER figures near a
|
||
half-microjoule and at the edges of oer-receipts §4's quantities, OER receipts whose sums
|
||
and floors sit at §6's tolerances, OER receipts holding numbers §4 forbids in a body
|
||
(`-0`, fractions, exponents, integers beyond I-JSON's range) in members of every kind, a
|
||
delivered object's microjoules spelt as the corpus never gives them, context receipts
|
||
that do not themselves verify, bodies of every class with one defect from a fixed list
|
||
and with every pair of those at two different positions (a member, or a member of a
|
||
nested object), where the order §3.1 step 2 reads a body in decides the reason, and, with
|
||
`--random N`, a seeded random sample of dates, endpoints, figures, sums and class
|
||
combinations, and of sessions: each built from a random presentation list as an honest
|
||
sink states it, then given a defect or two in the body or in a context (units, leaves and
|
||
paths off by one, switches and missed presentations past their caps, a `null` deadline
|
||
with misses, presences that disagree, decoder indices out of range, leaves moved between
|
||
entries, entries out of first-use order, outputs dropped or changed, re-decodes that match
|
||
and that do not, objects counted otherwise, listed twice or marked presented otherwise,
|
||
counted bytes that are not a held envelope's, a `first_unit` moved, inert bytes received
|
||
and named by an entry or not, every acquisition class), checked against its presentations,
|
||
objects, WAI2 envelopes and a `stage` claim; and sessions holding their envelopes — a base
|
||
of one to six frames, a `WAI1` envelope whose manifest may name a fallback its entry
|
||
decodes through, of the capability's format or another, or a companion-only capability,
|
||
the base component of a `WAI2` envelope, named by its envelope or by its exploded
|
||
envelope, held or not, or such a component that `WAI1` layers refine by its exploded
|
||
envelope's content hash, and layers refining it at
|
||
layer 1 or 2 over a span or all of its units, as `WAI1` envelopes or refinement
|
||
components, now and then naming another base or a layer as their base, at layer 0, with
|
||
an empty span or one past the base, with no `refines`, of a capability that does not
|
||
admit the base, with a `WIR1` payload a unit short, or, as components, with no `sha256`,
|
||
no role or another payload's digest, over a base that is a companion — with presentations through
|
||
each at one to three layers, within what each envelope covers and past it, entries whose
|
||
`first_unit` is the base's or one off, the base's and the layers' each, and objects whose
|
||
figures are each envelope's or one told otherwise, listed twice, fetched again, or beside
|
||
inert bytes, the base held or not, and entries naming a `WAI2` base or refinement by its
|
||
exploded envelope's content hash, its own bytes held or not; and, over every registered
|
||
capability and an unregistered string, a refinement beside a companion of that
|
||
capability bound to the base, and beside an HDR10+ descriptor and an enhancement layer
|
||
bound to a rendition of it (SPEC §7.1's component rules, which decide whether the
|
||
envelope offers its refinements); and the model session with its `WAI2` envelope
|
||
encrypted, its `security` well formed or not, the entry decoding it naming a rendition
|
||
in its table, out of it or none, its capability or another, the named rendition's
|
||
`sha256` kept, absent, well formed and unmatched, or malformed, and the classical length
|
||
stated as the table gives it or not. Further session families:
|
||
sessions that pass steps 1–5 against a `stage` claim whose first layer applied to some
|
||
units and arrived late for others, presented at one to three layers, with objects that
|
||
hold or lack the set's base and layers (both bounds of §2.4.12's check), the base's or
|
||
the layer's entry now and then at another first unit than the set's; sessions naming
|
||
one to three `stage` claims of one set, as a sink that forgot its base names one per
|
||
epoch, each listing first and second layers of every status, sometimes one named a unit
|
||
further on, presented within and past what the claims holding each unit applied, and an
|
||
entry now and then at another first unit than the claims'; the missed
|
||
presentations and switches written at, around and past their caps; WAI2 envelopes whose
|
||
manifests readers may read otherwise (members of the wrong type, pins that are not pins,
|
||
`security` members, duplicate members, numbers and nesting past every range, byte-order
|
||
marks, lone surrogates) and whose framing is short or long, and the base's WAI1 envelope
|
||
likewise, with a capability and a fallback the base's entry does or does not name — each
|
||
with the session signed again so the entry names that envelope as its object; random
|
||
sessions whose entries name their envelopes, a layer of their own or another object, with
|
||
envelopes and objects that hold them or not; and every registered capability as an
|
||
envelope's second rendition, in the session's media and in its own. Separately, each value that makes a whole file unreadable under
|
||
oer-receipts §4 — a number beyond the double range, `NaN`, an unpaired surrogate escape,
|
||
a repeated member name, nesting past 64 levels — goes in a file of its own beside
|
||
readable twins, and the two verifiers must both refuse the file or give it the same
|
||
verdicts. It exits 0 only if the two verdict lists are identical, every file is read or
|
||
refused alike, and neither verifier crashed; any difference exits 1, at whatever step.
|
||
|
||
Both verifiers read a corpus-format file as oer-receipts §4 reads a vector document,
|
||
since it holds OER receipts: `wai-rs` with `oer::load`, `claim_verify.py` with the OER
|
||
reference's `load`. A difference where one side reports `energy_oer_invalid` is marked,
|
||
since its cause is in the two OER verifiers rather than the claim rules, but it counts
|
||
the same. Those two are compared on their own by `../oer-conformance/fuzz_differential.py`.
|
||
|
||
## What each check can and cannot show
|
||
|
||
The expected outcomes are declared per vector in the generator (`wai-rs`,
|
||
`claim_receipt::corpus`), not copied from a verifier's output. The signed bytes,
|
||
though, are produced by the same module that verifies them, so a canonicalisation
|
||
defect would be baked into every signature and link hash and then confirmed. Two
|
||
checks close that:
|
||
|
||
- **The canonical fixture.** An `OnChipCounter` claim given with members out of order at
|
||
three levels, whitespace, and `\u` escapes in the input text. Its canonical bytes,
|
||
signature and link hash were computed by `claim_verify.py fixture` — Python's JSON
|
||
serialiser with members ordered by UTF-16 code units, `cryptography`'s Ed25519 signing, and
|
||
BLAKE3 written from its specification — and are pinned as literals in `wai-rs`
|
||
(`check_canonical_form`), which `wai_claim_vectors verify` checks first. Reversing
|
||
the member order, or changing the signing domain, fails it.
|
||
- **The second verifier.** `claim_verify.py` reports every vector's declared outcome and
|
||
every pinned link hash, including a claim whose canonical form spans three BLAKE3
|
||
chunks, and checks that the fixture it computes equals the literals in `wai-rs`. Its
|
||
§3.1 step 3 — RFC 8032 key decoding, the order of the key and of `R`, `S` < L and the
|
||
cofactorless equation — is `_ed25519.py`, written from the curve's definition. Its
|
||
acquisition-class rule is
|
||
written from energy-measurement §2.3–§2.5, where `wai-rs` admits the class through
|
||
`crate::energy_class`.
|
||
|
||
The corpus shows the two verifiers agree on its vectors and nothing more.
|
||
`differential.py` checks agreement on inputs no vector declares an outcome for: that
|
||
the two read the same inputs the same way. It does not show that either reading is the
|
||
extension's, and it covers only the families it generates.
|
||
|
||
The second verifier and the differential were written by the same author as `wai-rs`,
|
||
in a different language with no shared code. They catch disagreements in canonical
|
||
form, hashing, signatures and the reading of JSON, dates, hosts, classes and figures;
|
||
they are weaker evidence against a misreading of the extension that both verifiers share.
|
||
Two of the order vectors above (`lawful_basis_vocabulary_before_purposes`,
|
||
`lawful_basis_purposes_in_order`) exist because of one: both verifiers once read a
|
||
declaration's purposes before its other members, which §3.1 step 2 does not say, and
|
||
agreed with each other throughout. It was found by reading the text against the code;
|
||
neither check could have shown it.
|
||
|
||
## Regenerating
|
||
|
||
```sh
|
||
cargo run --no-default-features --features receipt_claims --bin wai_claim_vectors -- gen ../claim-conformance
|
||
```
|
||
|
||
Ed25519 signing is deterministic and the seeds are fixed, so the output is
|
||
byte-identical on every machine; `verify` fails if the pinned file differs from it.
|