key-release §15 (new). The key provider is the key holder's service a packager asks, in
the workflow the CPIX guidelines call the encryptor consumer. Its routes are neutral: they
carry CPIX documents and signed records and name no content protection system.
- A request is a CPIX document that names a content's keys (KID, scheme, key period) and
carries none, signed by the requester over its bytes and its clock.
- The provider decides it from signed inputs (a requester it serves, the proof, freshness,
replay, a document that reads, a content it serves), answers a provision with the same
keys' document and a refusal with nothing, signs the commitment list §4.4 derives from the
answer's exact bytes, and signs a receipt naming request, answer and list by digest.
- A verifier holding the three checks each against the others (§15.5).
Reference: key_release::provider (wai-rs), key_release::cpix::{request, write}, and
wai-key-release provider (wai-key-proxy/src/provider.rs), whose keys are test keys derived
from a seed and are stated as such. tests/provider.rs asks it for a rotating channel's
keys, loads the list in a signing proxy and the answer in a key server, and has a device's
Clear Key request released by a ticket whose key is the provider's.
Planted faults: an answer whose key is not the one the list commits to, under a receipt
signed for it, is refused cpix_mismatch (n_response_not_the_lists, and over HTTP in
tests/provider.rs); a key of a period after the entitlement ends is refused
DeniedPeriodOutside; and a negative control for every other reason of §15.5.
live-conformance/keys/provider.json holds 26 exchanges; key_release_verify.py verifies each
from the text, derives every key again from the seed, and requires twelve mutated copies
to fail. The 51 logs of vectors.json keep their bytes (its principals list gains two).
The WAI-Provider-* fields and the receipt's kind are registered. Decision D17. Not done and
not claimed: CPIX's encrypted delivery, and any content protection system's own key or
licence exchange.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
5.3 KiB
live-conformance/keys — key release
The corpus of 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: thereason(§11) and where,at:["list", i],["cpix", i],["receipt", i]or["ticket", i](§10.2);accept: for each receipt (§10.3), itsidentity,decision(§7.2) andreleased; thekeysit released, each itskidandkey_commit; and itslicence_expiryandrenewal.
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). The receipts of those two 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. 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 and an untrusted attester) 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 three
provisions (two keys; three keys with a period each; a content with a release time), every
refusal of §15.3, 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). Every key
is derived from the file's seed as §15.3 has the reference provider derive it.
Layout
provider.json: the key provider's exchanges, above.vectors.json:t0,principals(the keys, by name, informative),listsandvectors.film.cpix.xml,episode.cpix.xml,channel.cpix.xml: the CPIX documents the lists are derived from (channelwith three key periods);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, written
from the extension's text and sharing no code with wai-rs:
python3 -I live-conformance/key_release_verify.py live-conformance/keys