WAI Extension: Ad Presentation
Mirrored from the canonical text at commit b1681667 ().
Status: Draft. Registers the record
wai.live.ad_presentation: a player’s signed claim that it presented one impression of a live-ads break, with what it measured while it did. The claim,WPC1, names the impression by the break, the session and the slot of awai.video.ssaireceipt of the break form; it lists the creative’s media objects the player presented, by content hash, and spans of the creative’s media time with the fraction of pixels in view and whether the ad was audible; and it may carry a device attestation, a Privacy Pass token (RFC 9577) whose challenge binds it to this one impression. It is signed by the playback session’s own key, from which the session the receipt names is derived. An ad policy of version 2 can require a claim for each impression, a viewability rule, an audible run and an attestation; then a receipt’s spend counts only the impressions whose claim verifies, and the room verifier holds that spend to the decider grant’s ceiling. No new transport. The reference implementation is thepresentationmodule ofwai-rs’slive_ads(featurelive_ads); the corpus islive-conformance/presentation/, and its second implementation islive-conformance/ad_presentation_verify.py. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.Texts read for this revision, each at its source:
- Viewable Ad Impression Measurement Guidelines (Desktop), Version 2.0, 18 August 2015, in full. Its video requirements are word for word those of its Version 1.0, 30 June 2014. It numbers no section, so it is cited by heading and printed page.
- Mobile Viewable Ad Impression Measurement Guidelines, 28 June 2016: its video section only (pp. 10–11).
- Cross-Media Audience Measurement Standards (Phase I Video), September 2019: its executive summary and §2.1.1, §2.1.5, §2.2.1, §2.3.1 and §4.1.1, which hold all of its viewability, duration and audio text. The rest of it was not read.
- Server-Side Ad Insertion and OTT Guidance, August 2021, in full.
- Digital Video Impression Measurement Guidelines, Version 1.1, June 2018: §3.0, §4.1, §4.4 and §4.5.
- Video Ad Serving Template (VAST) 4.3, December 2022: §1.1 to §1.2.1, §2.3.4, §2.4 to §2.4.3, §3.4.4, §3.5 to §3.5.3, §3.14 to §3.14.2, §3.16 to §3.17.5 and §6. Its other sections were not read.
- Secure Interactive Media Interface Definition (SIMID) 1.2: §1 to §3.5.1, §4.2.10, §4.2.11, §4.4.5, §4.4.7, §8.1 and §8.4.
- Open Measurement SDK – OMID API, Version 1.5, June 2024: its sections on the impression, the media events,
volumeChange,playerStateChange,geometryChange, the ad view and its reasons.- Open Measurement Device Attestation Implementation Guidance, Version 1.0, November 2025, in full. Not read: the native SDKs that implement it, and any text of the exchange between a player and a verifier’s URL; no public text of that exchange was found.
- RFC 9576, RFC 9577 and RFC 9578 (June 2024), in full, and RFC 9474 §5. RFC 8017’s RSASSA-PSS is used as RFC 9578 §6.4 cites it, and this revision’s implementation of it is checked against the token of RFC 9578 Appendix A.2’s first test vector.
A section number of one of these texts is that revision’s.
1. Scope
In scope: the session key and the session it names (§3); the creative digest (§4); the presentation claim, its spans, its signature and its rules (§5); the viewability rules, the qualifying run and the quartile a claim reaches (§6); the device attestation a claim carries and how it is bound and verified (§7); the presentation fields of an ad policy (§8); each claim’s verdict, each impression’s count and the spend that counts (§9); and what a player measures and how (§10).
Out of scope: a transport, and how a claim reaches the decider or a buyer; how a player
obtains a token from an attester and its issuer (the issuance protocols of RFC 9578, run by a
platform); the detection of invalid traffic, which stays statistical and the measurer’s
(§13); attention, and anything a sensor of the viewer would measure; and the stitched form of
wai.video.ssai, whose receipts name no session of a room.
Relationship to the core spec: wai.live.ad_presentation is a record (SPEC
§5): a signed record a WAI tool emits and verifies. It names no payload format and is not
dispatchable.
Relationship to live-ads: this extension adds the presentation fields of an ad policy of version 2 (its §3.1, here §8), the session a session key names (its §2, here §3), and the spend its §9.3 step 12 counts under a policy that requires claims (here §9.3). Every other rule of live-ads stands, and a policy of version 1 is verified as before.
Relationship to jwp-receipts: a reconstruct-session claim (its §2.4.12)
is a sink’s record of a title it reconstructed, signed by its decoder host’s key, and “not
evidence that a person watched”. A presentation claim is a player’s record of one ad
impression, made for the billing rule of §9; it states what the player measured and is no more
evidence that a person watched (§13).
2. Terms
- Room, break, decider, pod, fill, asset list, session, policy: as in live-ads §2.
- Receipt: a
wai.video.ssaireceipt of the break form (live-ads §8). - Impression: one filled slot of a receipt: its
slot,ad_id,creative_hash,duration_ms,price_microsandquartiles_reached(live-ads §8.2). - Player: the software that presents the ad to a viewer and makes the claim (§10).
- Session key: an Ed25519 key a player makes for one playback session of one room (§3).
- Creative: an ad’s media, as an ordered list of media objects (§4).
- Span: a stretch of the creative’s media time the player presented at normal speed, over which its observations held (§5.2).
- Claim: a presentation claim (§5).
- Token, TokenChallenge, issuer, attester: as in RFC 9576 §2 and RFC 9577 §2.
- Basis point: one ten-thousandth; a fraction of pixels is written in basis points, 0 to 10 000.
Integers of the records here are little-endian unless this text says big-endian, as live-ads
§2 has them; a digest is SHA-256 (FIPS 180-4), 32 bytes. Ed25519 is RFC 8032’s, verified
strictly (SPEC §7 Signatures): a key that fails step 1 or 2 is bad_key, and a
signature that fails a later step is bad_signature. A long field is a u16 length and that
many bytes.
3. The session key
Under a policy that requires claims (§8), a player MUST make a fresh Ed25519 key for each playback session of a room, from its platform’s random generator, and MUST NOT use it for another session or another room, or derive it from an identifier of the viewer, the device or an account. It presents the key’s public part with each asset-list request of the session (live-ads §7), by a means this revision does not fix.
The session a session key names in a room is the first 16 bytes of SHA-256 over
wai:ad-presentation-session (ASCII) and 0x01, the room id (32 bytes) and the key (32 bytes).
Under such a policy the decider MUST give each receipt of the session that session
(live-ads §8.2 session), and a claim is counted only when its key names its session (§9.2
step 4). A player computes the session itself; the decider never chooses it.
The session is a function of a key made for this room alone, so it links nothing across rooms; live-ads §12’s rule that a session not be derived from a viewer’s key is met, since the session key is not one.
4. The creative digest
A creative is its media objects in presentation order: each object’s content hash, SHA-256 of the object’s bytes as served, and its presentation duration in milliseconds (a u32, at least 1). The object at index j holds the creative’s media time from the sum of the durations before it, s, to s + its duration.
The creative digest is SHA-256 over wai:ad-creative (ASCII) and 0x01, the objects’ count
(u32), and for each object in order its content hash and its duration (u32).
Under a policy that requires claims, an ad’s creative_hash in the pod, in the receipt and in
the asset list’s URIs (live-ads §7.2, §7.3) is its creative digest, and its duration_ms the
sum of its objects’ durations. A creative’s URI then names the very bytes a player presents,
object by object.
5. The presentation claim
5.1 Layout
| offset | size | field | meaning |
|---|---|---|---|
| 0 | 4 | magic | WPC1 (ASCII) |
| 4 | 1 | version | 1 in this revision |
| 5 | 32 | room | the room id |
| 37 | 32 | cue | the break’s digest (live-ads §4.4) |
| 69 | 16 | session | the session (§3) |
| 85 | 1 | slot | the impression’s slot, 0 to 15 |
| 86 | 32 | creative_hash | the creative presented: its creative digest (§4) |
| 118 | 32 | player | the session key (§3), which signs the claim |
| 150 | 1 | geometry | what the visible fractions are measured over: 1 ad, the ad’s own pixels, measured; 2 screen, a full-screen output to a television, whose pixels are assumed in view; 3 player, the player’s area, measured, where the ad’s own pixels are not known |
| 151 | 4 | frames | u32: the frames rendered over the spans, where the platform exposes a count; 4 294 967 295 where it does not |
| 155 | 2 | n | u16: the presented objects’ count |
| 157 | 32 × n | objects | the content hashes (§4) of the creative’s objects the spans touch, in order (§9.2 step 6) |
| … | 2 | m | u16: the spans’ count |
| … | 11 × m | spans | §5.2 |
| … | long | attestation | a token (§7.1), or no bytes |
| … | 64 | sig | §5.3 |
5.2 Spans
A span is 11 bytes:
| offset | size | field | meaning |
|---|---|---|---|
| 0 | 4 | start_ms | u32: where it starts, in the creative’s media time |
| 4 | 4 | length_ms | u32: its length, at least 1 |
| 8 | 2 | visible_bp | u16: the least fraction of the pixels in view the player observed over it, in basis points (§10) |
| 10 | 1 | flags | bit 0 audible: the ad was audible throughout it; bit 1 resumed: playback stopped between the span before it and this one |
A span’s end is start_ms + length_ms, computed without overflow. The spans are in
presentation order, and no media time is in two of them: a player that presents a stretch
again states it once. A span whose start is after the end of the span before it follows a
skip, and is a resumption.
5.3 Digest and signature
The digest of a claim is SHA-256 over its bytes from magic through attestation. The
signature is Ed25519 by player over wai:ad-presentation-claim (ASCII) and 0x01,
followed by the bytes the digest covers.
5.4 Reading and rules
A reader reads exactly the bytes it is given as one claim, in the order of §5.1, and refuses it
claim_malformed when:
- its magic is not
WPC1or its version not 1; - the bytes end before a field the layout requires, or there are bytes after
sig; slotis above 15, orgeometryis not 1, 2 or 3;nis 0 andmis not, ormis 0 andnis not;- a span’s
length_msis 0, itsvisible_bpabove 10 000 or itsflagsabove 3; - the first span has resumed set; or a later span starts before the end of the span before it, or after that end without resumed set.
6. Viewability and the quartile
6.1 The rules
A policy names one of two viewability rules (§8). Both require two continuous seconds; they differ in the pixels.
viewability | rule | a span qualifies when | admits geometry | text |
|---|---|---|---|---|
| 1 | video | visible_bp is at least 5 000 | 1, 3 | Viewable Ad Impression Measurement Guidelines (Desktop) 2.0, “Requirements for Viewable Video Advertising Impressions” (p. 8); the 2016 mobile guidelines’ video section states the same time |
| 2 | cross-media | visible_bp is 10 000 | 1, 2, 3 | Cross-Media Audience Measurement Standards (Phase I Video) §2.2.1 and §4.1.1 |
and, under a policy whose presentation sets audible, the span is audible too. A span of
a geometry the rule does not admit never qualifies.
- The video rule is the guidelines’ “50% of the ad’s pixels” and “2 continuous seconds of the video advertisement is played”, where “This required time is not necessarily the first 2 seconds of the video ad; any unduplicated content of the ad comprising 2 continuous seconds qualifies”. The same guidelines (footnote 2 of p. 8) ask that a measurement over the player’s pixels rather than the ad’s be disclosed: geometry 3 is that disclosure, and the verifier states it (§9.4).
- The cross-media rule is “a viewability qualification threshold of 100% of pixels on screen for at least two continuous seconds”. Of television it says: “Where ads and content are displayed to a TV (linear or OTT), it may be assumed that the 100% pixel criteria for viewability is met without direct measurement, although time threshold requirements still apply (2 continuous seconds for video)”. Geometry 2 is that assumption, and the video rule does not admit it.
- No text read for this revision defines a viewability rule for connected television of its own. The Server-Side Ad Insertion and OTT Guidance (2021) §2 holds such impressions “To the extent it can be measured” to the rules of online digital video; the cross-media rule above is the one that speaks of a television’s pixels. A room’s authority picks the rule its policy names.
- Audio: the video rule does not require it (“detection of audio is not currently a requirement for a viewable video ad impression”, p. 8); the cross-media standard requires “the presence of audio (non-zero volume or non-mute conditions) to be measured” where feasible (§2.1.5). A policy that wants an audible run says so (§8).
6.2 The qualifying run
Over a claim’s spans in order, a span continues the one before it when it starts at that
span’s end and does not have resumed set. A run is a sequence of qualifying spans each
of which, after the first, continues the one before it; its length is the sum of their
length_ms. A claim’s run is the length of its longest run, and its viewable time the
sum of length_ms over its qualifying spans. A claim is viewable when its run is at least
2 000 ms.
A pause, a stall or a skip between two spans ends a run, so two stretches of 1.5 s in view are not two continuous seconds. That spans state no media time twice is how a run holds only “unduplicated content”.
6.3 The quartile
The coverage of a claim’s spans is 0 when the first does not start at 0; otherwise it is
the end of the longest prefix of the spans in which each starts at the end of the one before
(a pause does not end it, a skip does). With D the impression’s duration_ms and C the
coverage, the quartile a claim reaches is:
- 0 when it has no span;
- otherwise 5 when C ≥ D; 4 when 4C ≥ 3D; 3 when 4C ≥ 2D; 2 when 4C ≥ D; and 1 otherwise.
These are quartiles_reached’s values (live-ads §8.2): 1 started, then a quarter, a half,
three quarters and all of it, at the marks VAST 4.3 §3.14.1 names for firstQuartile,
midpoint, thirdQuartile and complete (“The creative played continuously for at least 25%
of the total duration at normal speed”, and so on). A claim with a span has begun to play,
which is when an impression is counted (Digital Video Impression Measurement Guidelines 1.1
§4.1: “begins to play”).
7. Device attestation
7.1 The token
A claim’s attestation is a token of RFC 9577 §2.2.1:
struct {
uint16_t token_type;
uint8_t nonce[32];
uint8_t challenge_digest[32];
uint8_t token_key_id[Nid];
uint8_t authenticator[Nk];
} Token;
in its TLS presentation, big-endian. A policy names one of RFC 9578’s two token types, each of
Nid 32: 0x0001, VOPRF(P-384, SHA-384), privately verifiable, Nk 48 (§5, §8.2.1 there);
and 0x0002, Blind RSA (2048-bit), publicly verifiable, Nk 256 (§6, §8.2.2 there). A token
reads when it is exactly 98 + Nk bytes for the type its first two bytes name, and that
type is one of the two.
A token attests what its issuer’s attester checked of the device that asked for it (RFC 9576 §3): that a device of a kind the attester vouches for asked. It attests nothing of the rendering or of what was in view.
7.2 The challenge
The TokenChallenge a claim’s token answers is RFC 9577 §2.1.1’s:
struct {
uint16_t token_type;
opaque issuer_name<1..2^16-1>;
opaque redemption_context<0..32>;
opaque origin_info<0..2^16-1>;
} TokenChallenge;
with token_type and issuer_name the policy’s token_type and issuer (§8),
redemption_context the 32-byte redemption context below, and origin_info empty: in its
TLS presentation, the type as a big-endian u16, the issuer’s length as a big-endian u16 and its
bytes, the byte 32 and the context, and two zero bytes. The redemption context is SHA-256
over wai:ad-presentation-attest (ASCII) and 0x01, room, cue, session, slot (one
byte), creative_hash and player, the claim’s own.
The challenge is a function of the impression and the session key alone, each known before the
ad plays, so a party that issues it as a Privacy Pass origin (RFC 9577 §2.1) issues these
bytes, and every verifier recomputes them. A token answers one impression of one session key
and no other: lifted onto another claim, its challenge_digest is not that claim’s. This is
the Open Measurement Device Attestation Implementation Guidance’s “Against one impression ID,
only one attestation request may be counted” (§5.1), made a property of the token. A
TokenChallenge with no origin_info scopes the token to no origin (RFC 9577 §2.1.1.3), which
the redemption context makes unnecessary.
7.3 The issuer’s verifier
A token is verified by its issuer’s verifier. A verifier holds, for each issuer it trusts, the
issuer’s name, its token type and its keys, from its issuer directory (RFC 9578 §4); a key’s
token_key_id is SHA-256 of its encoding (for type 0x0002, the DER SubjectPublicKeyInfo of
§6.5 there; for 0x0001, SerializeElement(pkI) of §5.5).
- Type 0x0002 is publicly verifiable. The token verifies when the verifier holds a key of
the issuer whose
token_key_idis the token’s, the key is a SubjectPublicKeyInfo of the object identifier id-RSASSA-PSS (1.2.840.113549.1.1.10) with a 2048-bit modulus, and RSASSA-PSS-VERIFY (RFC 8017 §8.1.2), with SHA-384, MGF1 with SHA-384 and a 48-byte salt (RFC 9578 §6.4), accepts the token’sauthenticatoras a signature over its first 98 bytes (token_type,nonce,challenge_digest,token_key_id). RFC 9578 §8.2.2 also lists a variant of zero-length salt; §6.4 states 48 bytes, and this revision verifies with 48, so a token of the other variant does not verify here. - Type 0x0001 is privately verifiable: only its issuer, holding the private key, computes the authenticator (RFC 9578 §5.4). A verifier of a policy of this type asks its issuer, by a means this revision does not fix, and a verifier that cannot does not count the token. The reference implementation verifies type 0x0002 only.
The verifier requires, too, that the token’s challenge_digest is SHA-256 of the
TokenChallenge of §7.2: what WAI checks itself. Double spending (RFC 9577 §2.2.2) needs no
nonce ledger here: a token answers one impression, and an impression counts once (§9.2 step 5).
8. The policy’s presentation fields
An ad policy of version 2 (live-ads §3.1) carries, after creative_base and before sig:
| size | field | meaning |
|---|---|---|
| 1 | presentation | bit 0 claim: an impression counts only when its claim verifies; bit 1 attest: the claim carries a token that verifies (§7); bit 2 audible: a qualifying span is audible too (§6.1) |
| 1 | viewability | 0 none; 1 the video rule; 2 the cross-media rule (§6.1) |
| 2 | token_type | u16: the token type of §7.1 when attest is set, otherwise 0 |
| long | issuer | the issuer’s name, the TokenChallenge’s issuer_name (§7.2), when attest is set; otherwise no bytes |
A policy of version 2 is refused ad_policy_malformed (live-ads §3.3) when presentation sets
a bit above bit 2; sets attest or audible without claim; has viewability above 2,
or 0 with claim set, or not 0 with claim clear; or, with attest set, has a
token_type other than 1 or 2 or an empty issuer, or with it clear, a token_type other
than 0 or an issuer that is not empty. A policy of version 2 with presentation 0 is verified
as a policy of version 1.
9. Verification
9.1 Inputs
Under a policy that requires claims, an ads verifier (live-ads §9.1) is given, too:
- claims: the claims, a list of byte strings, in an order;
- creatives: the creatives it holds (§4), each its objects;
- issuers: the issuers it trusts (§7.3), each a name, a token type and its keys.
Under a policy that does not, it reads none of them.
9.2 Each claim
The claims are read once, before the receipts (steps 1 and 2), and the rest of each claim’s steps run when the verifier reaches the receipt whose impression it names, after that receipt’s live-ads §9.3 steps 1 to 11. Each claim gets one verdict, presented or the reason of the first step it fails:
- It reads (§5.4):
claim_malformed. - Its signature verifies under
player(§5.3):bad_key,bad_signature. - Its
room,cueandsessionare a receipt’s, among the receipts that passed steps 1 to 11, and that receipt has an impression of itsslot:claim_no_decision. A claim that no such receipt names when the verifier has read every receipt gets this verdict then. - Its
playernames itssession(§3):claim_key_unbound. - No claim before it in the list passed steps 1 to 4 for the same impression:
claim_duplicate. The first such claim is the impression’s own claim, whatever its verdict; steps 6 to 9 are for it alone. - The asset. Its
creative_hashis the impression’s; the verifier holds a creative whose creative digest that is (§4), and the sum of that creative’s durations is the impression’sduration_ms; every span’s end is at most thatduration_ms; andobjectsis exactly the content hashes of the creative’s objects that the spans touch, in the creative’s order, where a span touches the object of media time s to e when it starts before e and ends after s:claim_asset_mismatch. - The quartile. The quartile it reaches (§6.3), with D the impression’s
duration_ms, is the impression’squartiles_reached:claim_quartile_mismatch. - Viewability. It is viewable (§6.2) under the policy’s rule:
claim_not_viewable. - Attestation, when the policy sets attest:
attestationis not empty (attestation_absent); it reads as a token (§7.1) of the policy’stoken_type, itschallenge_digestis SHA-256 of the TokenChallenge of §7.2, and its issuer’s verifier accepts it under the policy’sissuer(§7.3):attestation_invalid.
An impression counts when its own claim is presented. An impression with no own claim is
stated claim_absent; one whose own claim was refused is stated with that claim’s reason.
9.3 The spend that counts
Under a policy that requires claims, a receipt’s presented spend is the sum of
price_micros over its impressions that count, and live-ads §9.3 step 12 holds the presented
spend of this receipt and every receipt before it, together, to the decider grant’s ceiling,
in place of their billable spend. A receipt’s own rules (live-ads §8.5) are unchanged: its
budget_spent_micros is still the decider’s billable spend, signed, and the verifier states
both.
So a stripped, redirected or spoofed beacon counts nothing: an impression counts when a claim
the session key signed is presented, and no other party holds that key. The decider’s
quartiles_reached must be the quartile the player’s own claim reaches (step 7), so buyer and
seller reconcile one figure from the same signed bytes.
9.4 What a verification states
With accept, beside live-ads §9.4, the verifier states:
- for each receipt: its presented spend, and each impression’s slot, the index of its own claim among the claims given or none, and whether it counts or the reason it does not;
- for each claim, in the order given: its digest when it reads; its verdict; from step 3 on, the receipt and the slot it names; and for an impression’s own claim, the quartile it reaches, its run and its viewable time;
- the spend of every receipt together, which under such a policy is their presented spend.
The claims’ facts state each claim’s geometry through its bytes; a buyer that weighs a
measurement over the player’s area (geometry 3) or a television assumption (geometry 2) reads
it there.
10. The player
A player that makes claims MUST:
- observe the ad at least every 200 ms while it plays, the minimum polling of the video guidelines (“for viewable video impression processes, 200 milliseconds”, p. 9), and begin a new span whenever its quantised visible fraction, its audible state or the continuity of playback changes;
- state as a span’s
visible_bpthe least fraction it observed over the span, of the ad’s rendered picture (geometry 1), of its own area (geometry 3), or 10 000 on a full-screen television output (geometry 2), less any part it knows its own overlays cover; - state a fraction of 0 while the page or application is hidden or in the background, while its window does not have focus, or while the display is locked: the guidelines’ “in-focus browser tab”;
- set audible only for a span throughout which the ad’s audio played at a volume above zero, unmuted, at the player and, where the platform exposes it, at the device;
- set resumed on a span that follows a pause, a stall, a seek or a stretch hidden from view; and state no media time twice;
- list as
objectsthe content hashes of the objects it decoded for the spans, which it computes over each object’s bytes as it received them; - state
framesas the frames presented and not dropped over the spans, where the platform exposes such a count.
A player that measures through an Open Measurement integration reads the same facts from it:
the ad view’s percentageInView (0 to 100, so visible_bp is 100 times it) and its
reasons, the mediaPlayerVolume and deviceVolume of volumeChange, and the media events
(OMID API 1.5). An interactive layer of SIMID 1.2 does not measure: “SIMID should not be set up
to measure viewability” (§3.5).
The reference producer is web/ads/presentation.js of the browser client joule-moq-web:
a recorder that turns observations (a visible fraction from an IntersectionObserver over the
video element, the page’s visibility and focus, the element’s volume and muted state, its
current time, and its playback-quality frame counts) into spans by the rules above, and writes
and signs the claim with the platform’s Ed25519. Its tests drive it with observation sequences
and require the claims of the corpus, byte for byte. An IntersectionObserver measures the
element’s box against the viewport, not what other elements obstruct; a page that can obstruct
its player states geometry 3 or measures obstruction itself.
11. Reasons
The codes this extension adds, each with where it is raised:
| reason | raised at |
|---|---|
claim_malformed | §5.4, §9.2 step 1 |
claim_no_decision | §9.2 step 3 |
claim_key_unbound | §9.2 step 4 |
claim_duplicate | §9.2 step 5 |
claim_asset_mismatch | §9.2 step 6 |
claim_quartile_mismatch | §9.2 step 7 |
claim_not_viewable | §9.2 step 8 |
attestation_absent | §9.2 step 9 |
attestation_invalid | §9.2 step 9 |
claim_absent | §9.2, an impression with no own claim |
A claim also gets live-events’ bad_key and bad_signature (§9.2 step 2), and a policy
live-ads’ ad_policy_malformed (§8). These verdicts never refuse a room: a room whose
receipts verify is accepted, and its claims’ verdicts decide what counts. A room is refused
only as live-ads §9 refuses it, funds_exceeded among them when the presented spend passes
the ceiling.
12. Conformance
live-conformance/presentation/ holds the corpus, written
from fixed seeds by wai_ad_presentation_vectors gen: vectors.json, and each accepted room’s
HLS tags as a file of its own, as live-ads §11 writes them. vectors.json holds the creatives
and the issuer the verifier holds; the public keys of the session keys; the first test vector of
RFC 9578 Appendix A.2 (its pkI, TokenChallenge and token), whose issuer key signs the corpus’s
tokens; and:
- Rooms, each a room, its policy of version 2, its receipts with their pods and its claims,
and the outcome with everything §9.4 and live-ads §9.4 state:
- a mid-roll under the video rule: impressions in view throughout, one partly out of view
with three continuous seconds at more than half its pixels, and one measured over the
player’s area; and a claim for each control: too few pixels, two runs broken by a pause,
a duplicate, a key that does not name the session, a presented object that is not the
creative’s, a quartile the receipt misstates, a flipped signature (whose impression is
then
claim_absent), a signed claim whose spans skip with no resumption stated, a session with no receipt, a truncated claim, and a television assumption the video rule does not admit; - a live break under the cross-media rule with an audible run and attestation: attested impressions on television outputs and screens, and a claim with no token, one carrying another impression’s token, one whose authenticator is altered, one muted and one with 90% of its pixels in view;
- two receipts that together pass the decider grant’s ceiling, one with a claim and one
without: accepted, the presented spend within it; and the same with both claims, refused
funds_exceeded; - a policy requiring an attestation without a claim, refused
ad_policy_malformed.
- a mid-roll under the video rule: impressions in view throughout, one partly out of view
with three continuous seconds at more than half its pixels, and one measured over the
player’s area; and a claim for each control: too few pixels, two runs broken by a pause,
a duplicate, a key that does not name the session, a presented object that is not the
creative’s, a quartile the receipt misstates, a flipped signature (whose impression is
then
A conforming verifier verifies RFC 9578’s own token under its key, and gives every room the
outcome, the reason, the place and everything else the corpus states, each claim its verdict
and facts, and every tag file byte for byte. wai_ad_presentation_vectors verify and the test
tests/ad_presentation.rs check that every file regenerates byte for byte, that wai-rs gives
every case its outcome, that every reason of §11 appears, and that §11 lists exactly the
reference’s codes. live-conformance/ad_presentation_verify.py is the second implementation,
written from this text: it shares no code with wai-rs; it verifies each room, policy and
receipt with the live-ads corpus’s second implementation (live_ads_verify.py), each Ed25519
signature with WAI’s strict rule (claim-conformance/_ed25519.py), and RSASSA-PSS with an
implementation of its own over the standard library; and it mutates copies of the corpus and
requires every copy to fail. The reference producer’s tests (§10) require the corpus’s claims
from observation sequences.
13. Security and privacy considerations
- What a claim proves without attestation. A claim proves that the holder of the session key the receipt’s session is derived from stated these facts about this impression. It does not prove that they are true. A modified player can state anything; so can a decider, or anyone, that makes a session key of its own, asks for asset lists under it and signs claims for the sessions it names. Without attestation the billing rule removes the third party who strips, redirects or forges beacons on the way, since such a party holds no session key; it does not remove a dishonest player or decider.
- What attestation bounds. A token is issued only to a device its attester vouched for, answering a challenge of this impression and this session key (§7.2). A party that makes claims of its own then needs a vouched device to ask for each token, impression by impression; one token cannot serve two claims. The bound is the attester’s: a genuine device running modified or malicious software, or a farm of genuine devices, attests truthfully and still signs what its software tells it to. Attestation makes such fraud cost a device per token and makes every claim attributable to a session key; it does not make it impossible.
- Invalid traffic. The detection of general and sophisticated invalid traffic stays statistical and the measurer’s. Claims give it signed, non-repudiable inputs: per-impression facts bound to a session key, and, with attestation, to a vouched device.
- What no claim states. That a person watched; that a television was on (the 2021 guidance does not require “TV Off” detection, §2); that nothing outside the player covered it, beyond what its geometry discloses (§10).
- Privacy. The session key is made for one session of one room (§3), and the session is derived from it and the room, so claims link nothing across rooms or to a viewer, device or account. A token is unlinkable to its issuance (RFC 9576 §3.3), and its redemption context is a digest of the impression. A claim carries no identifier of the device; the Device Attestation Implementation Guidance likewise states that “Device-level information such as Device ID will not be available” (§4.1.3).
- Withholding. A party between the player and the decider can withhold a claim, and the impression then does not count: the failure is the seller’s lost spend, never the buyer’s overpayment.
- Keys. A session key is held by the player for one session and then discarded; a claim whose key does not derive its session is never counted (§9.2 step 4), whoever signed it.