open-standards/wai/live-conformance/age/README.md
Transaction Science 1a8c1a5ead age assurance: one scope per room (live-rooms §10.4) refused by name otherwise, window redemption contexts, admission by the room's service over HTTP, ratings over MOQT
live-rooms §10.4's subscribing scopes did not fit the 255 bytes draft-ietf-moq-privacy-pass-auth-03
gives one MoQAuthorizationInfo: the room's scope is now one MoQAuthScope, the subscribing
actions on MATCH_PREFIX of [R] and on every track name, 78 bytes. A challenge's origin_info is
read by its binding: over MOQT the room's scope byte for byte (scope_malformed, scope_mismatch,
the previous revision's scopes among them); over HTTP RFC 9577's list of server names holding
the service's own. The corpus gains the scope and HTTP cases and RFC 9578 A.2's issuance, which
both verifiers check.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-21 16:15:15 -04:00

77 lines
5.3 KiB
Markdown

# live-conformance/age — age assurance
The corpus of [`extensions/age-assurance.md`](../../extensions/age-assurance.md) §12, in one
file, `vectors.json`:
- `principals`: each Ed25519 key, by name (informative);
- `issuer_keys`: the three issuer keys of the policies, each its `source`, `issuer`,
`threshold` and `key` (RFC 9578 §6.5's SubjectPublicKeyInfo, hexadecimal), informative;
- `rfc9578_a2_vector1`: RFC 9578 Appendix A.2's first test vector, as published: `pkI`,
`token_challenge`, the client's `nonce`, its `blind` *r* and its PSS `salt`, the
`token_request` and `token_response` of its issuance, and the `token`, in hexadecimal. A
conforming verifier finds that the token's `challenge_digest` is the SHA-256 of
`token_challenge`, its `token_key_id` the SHA-256 of `pkI`, and its authenticator *s* a valid
RSASSA-PSS signature under `pkI` (RFC 9578 §6.4); and that the issuance holds (§6.1 to §6.3
there, RFC 9474 §4.2 to §4.4): the request's blinded message is (*s*·*r*)^e mod *n*, and the
response *s*·*r* mod *n*. A client of the issuance protocol reproduces `token_request` and
`token` from the others;
- `admission_logs`: admission logs by name, each a list of admissions, `t` (room-clock
microseconds), `challenges` (the TokenChallenges the relay sent that session, hexadecimal)
and `token` (hexadecimal);
- `cases`: each a `name`, an `about`, a `kind` (one of §9.1's), its inputs, and `expect`, what
a verifier gives it.
Every record is a JSON text (§2). A room's records are hexadecimal: a `descriptor`, `grants`
and `chains` (each chain a list of `WEV1` records), and `key_log` (`null` throughout), as the
live-rooms corpus gives them.
**Cases.** By `kind`:
- `ratings`: `claims`, rating claims. `expect`: `accept` with `claims`, each its `link`,
`signer`, `subject` (the subject's `type`), `age` and `derived` (1 for a claim with inputs, 0
otherwise); or `refuse` at `["claim", i]`.
- `admission`: a room `descriptor`, an age `policy`, the session's `challenges`, the nonces
`redeemed`, a `token`, the age `required` and the binding's `origin`: `null` over MOQT, where
a challenge's `origin_info` is the room's scope of live-rooms §10.4, or the service's server
name over HTTP (§5.6), where it is RFC 9577's list of server names. `expect`: `accept` with
the `issuer`, the `threshold` and the `key_id` the token admits under; or `refuse` at
`["policy"]` or `["token"]`.
- `serving`: a room `descriptor`, a `track` of it (`namespace`, a list of its fields, and
`name`), `claims`, and `requests`, each a `threshold`, a `group` and `held`, the indexes of
the claims the relay holds when it serves the request. `expect`: `accept` with the `track`'s
digest and, for each request, the group's `age` and its `outcome`, `serve` or `refuse` with the
`reason`; or `refuse` at `["claim", i]`.
- `predicate`: a sink's `anchor` (each key's `issuer`, `threshold` and `key`), its `credentials`
(tokens, hexadecimal), the `claims` it holds, and `objects`, each a position: a
`content_hash`, a `track` digest and a `group`, any of them `null`. `expect`: `accept` with the
sink's `state` and, for each object, its `age` and `verdict`, `present` or `withhold`.
- `receipt`: a room `descriptor`, an age `policy`, a `receipt`, and `admissions`: the name of an
admission log, or `null` for none. `expect`: `accept` with the receipt's `link` and its
`cells` (each `window`, `issuer`, `threshold`, `admitted` and `suppressed`); or `refuse` at
`["policy"]`, `["admission", i]` or `["receipt"]`.
- `room`: a room. `expect`: `accept` with the room's own outcome (`room_outcome`, `accept` or
`gap`), `meets_profile` (1 or 0), each grant's `thresholds` in the order given, and the
`minor_grants` that took effect, each a grant digest in the merged order; or `refuse` at
`["room", …]` with the room verifier's own place (`["room", "record", c, i]` for record `i` of
chain `c`), `["grant", i]` or `["record", c, i]`.
A verifier that reaches another outcome, reason, place or statement does not conform.
**Provenance.** Generated here: `wai_age_assurance_vectors gen` writes `vectors.json` from
records authored in code, from fixed seeds. Every Ed25519 key is SHA-256 of a fixed label; every
time is a constant (the rooms' `t0` is 2026-10-21T00:00:00Z); every content hash, token nonce,
redemption context and RSASSA-PSS salt is SHA-256 output of a fixed label; and Ed25519 signing is
deterministic, so the file is the same bytes on every machine. The issuer keys are published test
keys whose private keys are published with them: RFC 7517 Appendix A.2's RSA key (threshold 13),
RFC 9500 §2.1's `testRSA2048` (16) and RFC 9578 Appendix A.2's issuer key (18), each written as
RFC 9578 §6.5's SubjectPublicKeyInfo; the generator signs each token's input with the key
directly, the signature the blind protocol would also yield. `rfc9578_a2_vector1` is that RFC's
vector as printed. The challenges over MOQT carry the room's scope of live-rooms §10.4, and the
refused ones the scopes of the revision before it, or a scope broken one way; those over HTTP
carry `room.example`, the corpus's service. The rating schemes, their values and the issuers are `.example` names (RFC
2606) and a `tag:` URI written for the corpus: no scheme here is any classification body's, and
nothing in the corpus states what any content deserves. No recorded data.
```
python3 -I ../age_assurance_verify.py .
```