open-standards/wai/live-conformance/keys/README.md
Transaction Science 84f5e109a1
Some checks failed
joule-moq-agent AI participants / joule-moq-agent · does this change touch it (push) Failing after 2s
joule-moq-http live HTTP egress / joule-moq-http · does this change touch it (push) Failing after 2s
joule-moq-agent AI participants / joule-moq-agent, every target (push) Has been skipped
joule-moq-http live HTTP egress / joule-moq-http, every target (push) Has been skipped
joule-moq-remote host agent / joule-moq-remote · does this change touch it (push) Failing after 2s
wai-app and Create / wai-app · does this change touch it (push) Failing after 2s
joule-moq-room room service / joule-moq-room · does this change touch it (push) Failing after 2s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (push) Has been skipped
joule-moq-room room service / joule-moq-room, every target (push) Has been skipped
joule-moq-remote host agent / joule-moq-remote, every target (macos) (push) Has been skipped
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Has been skipped
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Has been skipped
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Failing after 3s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Has been skipped
wai-audit collector and player integration / does this change touch it (push) Failing after 2s
wai-fleet service / does this change touch it (push) Failing after 2s
WAI operation catalogue / does this change touch it (push) Failing after 3s
wai-audit collector and player integration / collector · linux (push) Has been skipped
wai-key-proxy / does this change touch it (push) Failing after 2s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Has been skipped
wai-fleet service / service · linux (push) Has been skipped
wai-fleet service / service · macos (push) Has been skipped
wai-key-proxy / proxy · linux (push) Has been skipped
wai-ledger service / does this change touch it (push) Failing after 1s
WAI operation catalogue / the catalogue and every door · linux (push) Has been skipped
wai-ledger service / service · linux (push) Has been skipped
wai-ledger service / service · macos (push) Has been skipped
wai-quantum studio and desktop / does this change touch it (push) Failing after 1s
wai-quantum studio and desktop / studio · browser (push) Has been skipped
wai-quantum for JavaScript / does this change touch it (push) Failing after 2s
wai-quantum studio and desktop / desktop app · macos (push) Has been skipped
wai-quantum studio and desktop / desktop app · linux (push) Has been skipped
wai-quantum for JavaScript / javascript · linux (push) Has been skipped
wai-quantum studio and desktop / desktop app · windows (push) Has been skipped
wai-pack / wai-pack · does this change touch it (push) Failing after 3s
wai-pack / wai-pack · linux (push) Has been skipped
wai-sink-android native sink library / does this change touch it (push) Failing after 2s
wai-pack / wai-pack · linux-arm64 (push) Has been skipped
wai-pack / wai-pack · macos (push) Has been skipped
wai-pack / wai-pack · independent verifier (Python) (push) Has been skipped
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Has been skipped
wai-pack / wai-pack · the floor through a media prober (push) Has been skipped
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Has been skipped
wai-pack / wai-pack · dependency licences (push) Has been skipped
WAI Studio / does this change touch it (push) Failing after 2s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Has been skipped
WAI Studio / Studio in Chromium and Firefox · macos (push) Has been skipped
wai-sink-swift native sink package / does this change touch it (push) Failing after 2s
WAI Studio / Studio in WebKit · linux (push) Has been skipped
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Has been skipped
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 25s
WAI examples / WAI examples · does this change touch them (push) Successful in 26s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 2m54s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 14m48s
WAI examples / WAI examples · portable · linux (push) Successful in 25m6s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Failing after 12m18s
WAI examples / WAI examples · android-app · linux-arm64 (push) Successful in 12m34s
wai protected floor in browsers / the protected floor in Chromium, Firefox and WebKit · macos (push) Successful in 21m49s
joule-moq-http in browsers / the egress and the protected room in Chromium, Firefox and WebKit · macos (push) Failing after 33m18s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (push) Successful in 17m6s
Key release: a signed checkpoint of a proxy's log, which later decisions read and a verifier goes on from; releases indexed by group (D90)
A checkpoint (wai-key-checkpoint, key-release §17) is a line of the log the
proxy signs at an instant: the proofs a later request could still present
fresh, each session's last release of each stream limit within a day, the
licences not yet lapsed, the count and the last receipt, and the
checkpoint before it. Decisions after it read it in place of the receipts
before it, and none is made before its instant; a verifier goes on from a
checkpoint that begins a log and holds a later one to the log before it
(checkpoint_invalid, checkpoint_mismatch). LogIndex goes on from a
checkpoint and indexes each group's releases in time; the decision reads
the log through Log::renewable and Log::stream_releases. wai-key-proxy
signs one every checkpoint_every receipts and on POST /v1/checkpoint,
never with a decided receipt unwritten, restarts from the last, and reads
earlier receipts from its file. The corpus adds checkpointed,
from_checkpoint and eleven negative controls; key_release_verify.py
decides the same and its ten weakened runs are caught.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-12 09:59:32 -05:00

110 lines
8.2 KiB
Markdown

# live-conformance/keys — key release
The corpus of [`extensions/key-release.md`](../../extensions/key-release.md) §12: logs of a
signing proxy, each over commitment lists (`WKL1`) derived from CPIX documents, with the
outcome its verification reaches (§10).
**Log equivalence.** For each vector, a conforming key-release verifier given its
`authorities`, `proxies`, `lists` (each named in the top-level `lists`, as hexadecimal of its
bytes), its `cpix` documents (each the file `file`, with the index in `lists` of the list it is
derived from), its `log` (form-2 `wai.video.keys` receipts in the JSON of §8.2, in the order
the proxy signed them) and its `tickets` (`WKT1`, hexadecimal) reaches the outcome the vector
states:
- `refuse`: the `reason` (§11) and where, `at`: `["list", i]`, `["cpix", i]`, `["receipt", i]`
or `["ticket", i]` (§10.2);
- `accept`: for each receipt (§10.3), its `identity`, `decision` (§7.2) and `released`; the
`keys` it released, each its `kid` and `key_commit`; and its `licence_expiry` and `renewal`.
A verifier that reaches another outcome, reason, place or fact does not conform.
The accepted logs give: streaming releases over two content protection systems, forwarded and
answered (`release_drm`, one receipt carrying a metered figure with its class); an HLS key
file and Clear Key releases by ticket, the key never relayed by the proxy
(`release_hls_key_uri`, `release_clear_key`); every refusal of §7.2 checks 1 to 8 (`refusals`,
`expired_entitlement`, `release_time`), the last over a list of version 2 whose release time
refuses every request decided before it, whatever its entitlement, and releases from it on;
key rotation over three periods (`rotation`); offline licences, their
renewal, their count, their lapse and their clamp to the entitlement (`offline`); and granted
requests the licence server refused or did not answer (`upstream_refused`); a limit of two
sessions streaming at once over a window of ten minutes, with the releases each decision
counted, a session that keeps its place by asking again, a release that lapses, and a group
whose records state two limits, which is undecided (`streams`); and an output level required,
released on a statement of an accepted class and undecided where none establishes a level
(`output`); one grant whose group is named for each content, streaming a film and an episode
at once under a limit of one, and another grant whose one group limits both
(`streams_per_content`, §14.7). The receipts of those logs are of form 3 (§14.5), a vector that
gives a verifier attesters states them in `attesters`, and an accepted form-3 receipt states its
`leases`. An entitlement authority's revocations (§16) of a session and of a group refuse
`DeniedRevoked` from their instant on, before a stale request is read as stale and after an
invalid entitlement is read as invalid, and name nothing else: not another authority's session of
the same bytes, another session, another group, or a group whose bytes a session's revocation
carries (`revoked`, given two authorities); a vector that gives a verifier revocations states
them in `revocations`, each a `WKR1` in hexadecimal. A content whose keys come in two spans
(§4.6) is a chain of the channel's list and `channel_next`, a list of version 3 extending it,
each derived from its CPIX document: the next span's key is unknown before its list is held and
released after, and a key of each span is released together (`extended`); lists that do not
extend the chain (one whose named list is not given, one with a period no later, one signed by
another key, one restating a KID, two extending one list, and one with a key of no period) are
refused. A log with two checkpoints (§17) holds, among its lines, the proxy's signed checkpoints:
the first leaves out a release two days old, a session's earlier release and a licence that
lapsed, and keeps one proof, two sessions' last releases and a licence; after it a replay of the
kept proof is refused, a third session counts the two releases it keeps, the kept licence is
renewed and the lapsed one's renewal is unknown; the second names the first (`checkpointed`). The
same log gone on from its first checkpoint gets the same decisions (`from_checkpoint`). An
accepted log with checkpoints states their identities in `checkpoints`. Its negative controls
leave out a proof, a release or a licence, miscount, name no previous checkpoint
(`checkpoint_mismatch` each), keep a lapsed licence, change a signature, sign by another proxy
(`checkpoint_invalid` each), date a receipt before the checkpoint it follows
(`checkpoint_mismatch`), restate a decision as the whole log would make it, or restate the replay
after the checkpoint as a release (`decision_mismatch`). Each `n_` vector
is a negative control, refused with a reason of §11; together they give every reason.
**Provenance.** Generated here: `wai_key_release_vectors gen` writes every file from logs
authored in code, from fixed seeds. Each principal's Ed25519 key (the key holder, the
entitlement authority, an untrusted authority, the proxy, an untrusted proxy, three devices, an
attester, an untrusted attester, two packagers and a second entitlement authority) is
SHA-256 of a fixed label; each content key, KID and session is SHA-256 of a fixed label,
truncated. Every time is a constant (the corpus's `t0` is 2026-10-25T00:00:00Z), every origin
is an `.example` name (RFC 2606), the three system IDs are values of the DASH-IF registry of
content protection system identifiers, and Ed25519 signing is deterministic (RFC 8032), so
every file is the same bytes on every machine. The CPIX documents are written for the corpus
with their keys in the clear (CPIX §8.1 calls that not recommended outside a test); the
encrypted one holds a cipher value that is SHA-256 of a fixed label and decrypts to nothing.
The licence challenges and licences are fixed strings, never a content protection system's
messages. The generator asserts that each vector reaches the outcome it was written to have,
and that the corpus gives every reason of §11 and every decision of §7.2, before it writes. No
recorded data.
**The key provider.** `provider.json` holds exchanges with a reference key provider (§15): for
each, the request, the provider's signed receipt and, for a provision, the answer and the
commitment list derived from it, with whether the provider's log already held the request's
proof (`seen`). A conforming verifier given the case's `providers` and the file's `requesters`
and `contents` reaches the stated outcome (§15.5): `accept`, with the receipt's identity and
decision and the keys the list commits to, or `refuse`, with its reason. The cases give five
provisions (two keys; three keys with a period each; a content with a release time; two keys of
a content whose key periods are a schedule; two keys of the keyed source), every refusal of
§15.3 (among them three of `DeniedPeriodUnknown`: a period past the schedule's count, an index
whose window is another period's, and a key with no period), and negative controls for every
reason of §15.5, among them an answer whose key is not the one the list commits to
(`n_response_not_the_lists`, `cpix_mismatch`) and an off-schedule key provided by a provider told
no schedule (`n_off_schedule_provided`, `provider_decision_mismatch`). A content that has a
schedule states it in `contents` (`origin`, `length`, `count`), and a verifier is told it. Every
key is derived from the file's `seed` by the test derivation of §15.3, or, for a case whose
`source` is `secret`, from the file's `secret` by the keyed derivation; both are test values that
attest nothing.
## Layout
- `provider.json`: the key provider's exchanges, above.
- `vectors.json`: `t0`, `principals` (the keys, by name, informative), `lists` and `vectors`.
- `film.cpix.xml`, `episode.cpix.xml`, `channel.cpix.xml`, `channel-next.cpix.xml`: the CPIX
documents the lists are derived from (`channel` with three key periods, and `channel-next`
with the three after them); `encrypted.cpix.xml`, a document whose key
is encrypted (`cpix_unsupported`); `broken.cpix.xml`, one that is not well-formed
(`cpix_malformed`).
The second implementation is [`../key_release_verify.py`](../key_release_verify.py), written
from the extension's text and sharing no code with `wai-rs`:
python3 -I live-conformance/key_release_verify.py live-conformance/keys