open-standards/wai/extensions/robustness-evidence.md
Transaction Science be8585f926 wai.media.robustness: robustness records anyone recomputes bit for bit, and claims held to them
extensions/robustness-evidence.md: a corpus named item by item by the SHA-256
of its samples, the attack battery as registered integer transforms with
parameters and seeds (synthesis §7.5.7's battery with its parameters free, and
gamma), the signed robustness record whose table the harness recomputes and
whose rates are exact fractions, and the robustness claim that holds only
where every item of a cited record's corpus survived the attack and no
negative was read as marked. wai-rs module robustness (feature robustness),
wai_robustness_vectors, the corpus with wai-mark:tent64's records over the
measurement sets and its claim, and robustness_verify.py, which reruns the
conformance battery end to end with _wai_mark.py.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-22 03:40:50 -04:00

33 KiB
Raw Permalink Blame History

WAI Extension: Robustness Evidence for Soft Bindings

Status: Draft. Registers the record wai.media.robustness. A soft binding — a watermark read back out of the samples, or a fingerprint computed over them (jwp-receipts §2.6) — is worth what its algorithm survives, and a declaration states nothing about that. A robustness record measures it: the algorithm by term and definition digest, a corpus of pictures or clips each named by the SHA-256 of its samples, an attack battery of pinned, integer, seeded transforms with their exact parameters, the harness that runs them, and per attack the counts — items that survived out of the corpus, unmarked items read as marked out of the negatives — and rates derived from them as exact fractions, never floating point. Every step is an integer function of its inputs, so anyone who holds the corpus runs the battery, runs the detector and gets the same table, bit for bit; a verifier that does so reproduces the record, and one that cannot reads it as stated. A robustness claim — "survives crop-5%" — holds only where a record shows every item of its corpus survived that attack and no negative was read as marked; a claim with no record, or above what its records show, is refused. WAI's registered watermark, wai-mark:tent64 (synthesis §7.5), carries records over its measurement sets and a claim that is exactly the list its extension states. The record maps field for field onto the robustnessEvidence object a proposal to the C2PA soft-binding list describes (§11). No new transport. The reference implementation is the robustness module of wai-rs (feature robustness); the corpus is live-conformance/robustness/, and its second implementation is live-conformance/robustness_verify.py. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.

Texts read for this revision, each pinned to the version named: the C2PA Technical Specification, version 2.4, §9.3 and §18.10; and Robustness Evidence for C2PA Soft-Binding Algorithms, a proposal to the C2PA soft-binding and conformance task forces whose status is "proposal / reference results. Draft for discussion" and which states no version number, so this revision pins its text by digest: SHA-256 4ea8ab27711ef812a3cf8ade364cd40082ab797d071c87a6313217070dfbc9a2 as fetched on 22 October 2026. A section number of the C2PA text in this extension is that version's.

1. Scope

In scope: the records and their encodings (§2); the corpus (§3); the registered transforms (§4); the robustness record (§5) and the harness it names (§6); exact rates (§7); robustness claims and when one holds (§8); verification (§9); the records of WAI's own watermark (§10); the mapping onto the proposed robustnessEvidence object (§11).

Out of scope: any soft-binding algorithm itself, which its own definition specifies (WAI's is synthesis §7.5); a transport; which attacks a deployment ought to care about, which is the relying party's; transforms of audio and of text, which this revision does not register, so a corpus of either has no battery here yet (§3); and an adversary who knows the algorithm and searches for a transform outside every battery, which no measurement bounds (§14).

Relationship to the core spec: wai.media.robustness 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 jwp-receipts: a soft-binding match is evidence at the algorithm's own error rates (§3.2 there, What a match is worth). This extension is where those rates are measured and how a claim about them is held to its measurement. A recovery (jwp-receipts §3.2) is unchanged by it: a verifier MAY report beside a recovery the accepted claims and records it holds for the declaration's algorithm, and they change no step of the recovery.

Relationship to synthesis: synthesis §7.5.7 defines the attack battery of wai-mark:tent64 and §7.5.8 states what the scheme claims to survive. §4 here registers that battery's attacks as transforms with parameters, so a record names them by term; §10 gives the scheme's records and its claim, which a verifier checks under §8.

2. Records

Every record of this extension is a JSON object under the encodings and the reading of synthesis §2 — byte strings in lowercase hexadecimal, unsigned integers of at most 2^53 − 1, no other number, no boolean, strict reading of RFC 8259, exactly the members its section lists at every level, and a kind naming it; otherwise record_malformed — with one exception: a rate member (§7) is read after every other member, and one that holds anything but an exact fraction is rate_not_exact, never record_malformed.

A robustness record (§5) and a robustness claim (§8) are signed:

sig       = Ed25519(signer_key, "wai:robustness\x01" || JCS(record without "sig"))
link_hash = BLAKE3("wai:robustness-link\x01" || JCS(record))

JCS is RFC 8785, which on these values reduces to what jwp-receipts §2.4.3 states. The signature is verified by WAI's strict rule (SPEC §7 Signatures): a key that fails step 1 or 2 is bad_key, a signature that fails a later step bad_signature. A record is named by its link hash wherever another names it. issued_at is a date-time of jwp-receipts §2.4.3, self-asserted.

A corpus document (§3) is not signed: it is named by its digest, the SHA-256 of its JCS, which every record that measures it states.

The samples of a picture or a clip are its canonical buffer: RGB8, row-major, three bytes a pixel, no row padding (reconstruction-binding §2), frame after frame in presentation order, as synthesis §2 has it. An item is named by the SHA-256 of its samples.

3. The corpus

A corpus document, kind "wai-robustness-corpus":

member type meaning
kind string "wai-robustness-corpus"
name term A label for people; nothing is checked against it.
media string "image" or "video": what every item is.
items array The items an algorithm binds: each marked with its value (a watermark) or bound to it (a fingerprint), then attacked.
negatives array The items it does not bind, read after the same attacks to count false positives. They may be the same samples as items, unmarked.

Each entry of items and of negatives is an object of exactly these members:

member type meaning
name term The item's name, unique within its list.
frames integer How many frames; 1 for every item of an image corpus.
width, height integer The size of each frame, in pixels.
samples 32 B hex SHA-256 of its samples (§2).

A corpus document with no item or no negative, a name twice in one list, a frames, width or height of 0, an image item of more than one frame, or an item of more than 2^53 − 1 bytes (frames·width·height·3) is corpus_malformed, as is one that does not read under §2.

An item is held by its samples: a verifier that holds a buffer finds it by its SHA-256, and a buffer it holds for an item is frames·width·height·3 bytes (samples_mismatch). A corpus is a list of digests, so it can be published without its samples; a record over it is then stated to everyone who does not hold them (§9).

A corpus of audio or of text needs transforms of its medium, which §4 does not register: its media is a later revision's.

4. The transforms

A transform is named by a term and takes integer parameters. Applied to an item's frames at a seed, each one gives other frames, an integer function of the frames, the parameters and the seed alone, so its result is the same bytes on every machine. Below, rnd(n / d) is n / d rounded to the nearest integer, halves away from zero, and every sample is clamped to 0 … 255, as in synthesis §7.5.7, whose definitions these are where the table names them.

transform parameters media the frames it gives
wai-attack:none none both the frames unchanged
wai-attack:int-motion q, 1 to 255 both each frame a keyframe of wai.video.int_motion at step q (int-motion-qq)
wai-attack:staged-base q, 1 to 255 both each frame's staged base at step q (staged-base-q8's definition at q)
wai-attack:denoise3 n, 1 to 16 both wai-synthesis:denoise3 applied n times (synthesis §4.2)
wai-attack:scale n, d, each 1 to 16 both area resampling to ⌊W·n/d⌋ × ⌊H·n/d⌋ (scale-n/d)
wai-attack:aspect n, d, each 1 to 16 both area resampling of the width alone to ⌊W·n/d⌋ (aspect-3/4's definition at n/d)
wai-attack:upscale2 none both wai-synthesis:upscale2 (synthesis §4.1)
wai-attack:half-then-int-motion q, 1 to 255 both scale 1/2, then int-motion at q
wai-attack:crop p, 0 to 99 both ⌊W·p/200⌋ columns removed from each side and ⌊H·p/200⌋ rows from the top and from the bottom (crop-p%)
wai-attack:mirror none both the columns in reverse order
wai-attack:rotate-90 none both a quarter turn clockwise
wai-attack:rotate c, s, each 0 to 65536; k, 0 or 1 both the rotation of rotate-0.5's definition with cosine c and sine s at 2^16, the sine negated where k is 1
wai-attack:noise s, 0 to 255 both noise uniform in −s … s from the stream of noise-s at the seed
wai-attack:brightness b, 0 to 255; k, 0 or 1 both v + b, or v − b where k is 1
wai-attack:contrast n, d, each 1 to 16 both 128 + rnd((v − 128)·n / d)
wai-attack:gamma n, d, each 1 to 6 both below
wai-attack:local-blur none both local-blur
wai-attack:clip-int-motion q, 1 to 255 video the clip encoded as wai.video.int_motion with motion at step q (clip-int-motion-qq)
wai-attack:frame-drop none video frames 0, 2, 4, …
wai-attack:one-frame none video the first frame alone

Gamma. Each sample v becomes the greatest y from 0 to 255 such that y = 0 or (2y − 1)^d·255^n ≤ 2^d·v^n·255^d: 255·(v/255)^(n/d) rounded to the nearest integer, halves up, computed in integers. With n and d at most 6 every product is below 2^103. A gamma below 1 (n < d) lifts the dark samples, one above it darkens them; a display's response and an encoder's transfer function differ from a picture's by such a curve.

A transform is applied to every frame of an item and to every channel of every sample. The seed matters only to noise; every transform takes one. A transform's parameters, outside the table's ranges or of the wrong number, are transform_invalid. A transform applied to a corpus whose media the table does not list for it is battery_not_applicable. A term the table does not register is admitted in a record: what it does is its definer's, a verifier here cannot run it, and a record that names it is stated (§9).

The table is the battery of synthesis §7.5.7 with its parameters free, and gamma. More transforms are registrations in a later revision, each an integer function with its parameters' ranges.

5. The robustness record

The party that measured an algorithm signs a robustness record, kind "wai-robustness":

member type meaning
kind string "wai-robustness"
algorithm term The algorithm measured: a registered soft-binding algorithm's term, as a soft-binding claim's alg names it (jwp-receipts §2.6.2), or a WAI scheme's (synthesis §7.4).
algorithm_digest 32 B hex SHA-256 of the algorithm's definition.
binding_type string "watermark" or "fingerprint", as jwp-receipts §2.6.2 types it.
media string The corpus's media.
corpus 32 B hex The corpus document's digest (§2).
values array of hex One per item of the corpus, in its order: the value the item is marked with (a watermark) or bound to (a fingerprint). Each is at least one byte.
battery array The attacks, in order, below.
harness term The procedure that ran them: wai-robustness-harness:1 for §6.
table array One row per attack, in the battery's order, below.
issued_at date-time When it signed.
signer_pubkey, sig §2

An attack is an object of exactly attack (a term: its name, unique within the battery), transform (a term of §4, or one this revision does not register), params (an array of integers) and seed (an integer below 2^32: the four bytes a stream of synthesis §7.5.7 reads it as). A row is an object of exactly:

member type meaning
attack term The attack's name.
trials integer The corpus's items: how many marked items were attacked.
survived integer How many of them, attacked, gave their own value (§6).
wrong integer How many gave another value.
negatives integer The corpus's negatives: how many were attacked.
false_positives integer How many of them, attacked, gave a value (§6).
survival rate survived / trials (§7).
false_positive_rate rate false_positives / negatives.
balanced_accuracy rate The mean of survival and the true-negative rate (§7).
tpr_at_1pct_fpr rate or null survival where false_positives·100 ≤ negatives; null otherwise (§7).

missed, the items that gave nothing, is trials − survived − wrong.

Its checks, in order, after reading and the signature (§2):

  1. the battery has at least one attack, its names are unique and each seed is below 2^32 (battery_malformed); each registered transform's parameters fit §4 (transform_invalid), and then each one's media the record's (battery_not_applicable);
  2. the table has one row per attack, with the attack's name, in the battery's order (table_malformed);
  3. in each row: trials is the number of values and at least 1; survived + wrong ≤ trials; negatives is at least 1 and the same in every row; false_positives ≤ negatives (table_inconsistent);
  4. each rate is the one its row's counts give (§7, rate_mismatch);
  5. an algorithm this revision registers is named with its digest and its binding type — wai-mark:tent64 is a watermark — (algorithm_mismatch), and the values are values the algorithm takes: for wai-mark:tent64, 8-byte payloads whose first byte is a class code, 0 to 3 (synthesis §7.2) (values_invalid).

Then against what the verifier was given (§9.1): the corpus document, and the samples it holds.

  1. The corpus document reads under §3 (corpus_malformed); its digest is corpus and its media the record's (corpus_mismatch); it has as many items as values and as many negatives as each row's negatives (table_inconsistent).
  2. Each item and negative whose samples it holds is the size §3 gives (samples_mismatch).
  3. Reproduction. Where the verifier holds the corpus document and the samples of every item and negative, implements the algorithm, the record names the harness of §6, and every transform of the battery is registered, it runs the harness (§6): a fingerprint's values are its own value of each item (values_invalid), and the table it gets is the record's, row for row and count for count (table_irreproducible). The record is then reproduced; otherwise stated.

A reproduced record says that the algorithm, run by this harness on this corpus under this battery, gives this table: not that it survives an attack outside the battery, a parameter between two measured, or content unlike the corpus. A stated record is its signer's statement, checked for its arithmetic and nothing else.

6. The harness

wai-robustness-harness:1 is this procedure. For each attack of the battery, in order, with its transform T, parameters and seed s:

  • Watermark. For each item k, in order: its frames are marked with values[k] by the algorithm's embedding; T is applied to the marked frames at s; the algorithm's detector runs on the result. It survived where the detector reads values[k], is wrong where it reads another value, and is missed where it reads none. For each negative, in order: T is applied to its frames, unmarked, at s, and the detector runs on the result; a false positive is any value read.
  • Fingerprint. For each item k: values[k] is the algorithm's value of the item's frames; T is applied to the frames at s, and the value of the result is computed. It survived where that value matches values[k] under the algorithm's own rule, is wrong where it matches another item's value and not its own, and is missed otherwise. For each negative: the value of T applied to its frames is computed, and it is a false positive where that value matches any item's value.

Every attack sees the frames as marked or as given, never the result of another attack. Each step is an integer function where the algorithm's embedding and detector are, as tent64's are (synthesis §7.5), so the table is a function of the record's inputs, and a second run anywhere gives it bit for bit.

The seed is the record's: synthesis §7.5.7 seeds the attack at index i of its lists with i, and a record of that battery states those seeds (§10).

7. Rates

A rate is an exact fraction, written as an array of two integers [n, d]: 1 ≤ d, 0 ≤ n ≤ d, and n and d with no common factor above 1, so 0 is [0, 1], 1 is [1, 1], and every rate has one spelling. A number, a string, an array of another length or of other values, a fraction not in lowest terms or one above 1 is rate_not_exact. A rate is never floating point: a record's table is its counts, and its rates are the counts' quotients, which any reader recomputes exactly and compares as integers.

For a row with counts trials N, survived S, negatives U and false_positives F:

  • survival = S / N, the true-positive rate;
  • false_positive_rate = F / U;
  • balanced_accuracy = (S·U + (U − F)·N) / (2·N·U): the mean of the true-positive rate S/N and the true-negative rate (U − F)/U;
  • tpr_at_1pct_fpr = S / N where 100·F ≤ U, and null otherwise.

Each reduced to lowest terms. A detector that gives a value or none has one operating point, so its true-positive rate at a false-positive rate of at most 1% is its rate where its measured false-positive rate is at most 1%, and is not attained where it is above. The measured rate is an observation over U negatives: 0 of 32 says no negative of the corpus was read as marked, not that the algorithm's false-positive rate is below 1%, which only a bound of the algorithm's own definition gives (synthesis §7.5.6 for tent64).

8. Robustness claims

An algorithm's robustness claim is the list of attacks it claims to survive. Its registrant signs it, kind "wai-robustness-claim":

member type meaning
kind string "wai-robustness-claim"
algorithm, algorithm_digest As §5's: the algorithm the claim is about.
survives array The attacks claimed, at least one: each an object of exactly media ("image" or "video"), transform (a term) and params (an array of integers), no two equal.
evidence array of 32 B hex The link hashes of the robustness records it rests on, no two equal.
issued_at date-time When it signed.
signer_pubkey, sig §2

A claimed attack is named by its media, its transform and its parameters, and not by a seed: a claim is about the transform, and a record shows it at the seed it states. A record measures a claimed attack where its media is the attack's and its battery has an attack with that transform and those parameters; its row shows survival where survived = trials, wrong = 0 and false_positives = 0: every item of the corpus, attacked, gave its own value, and no negative, attacked, gave any. This is the rule synthesis §7.5.8 states for its scheme.

A claim holds where, after reading and the signature (§2), in this order:

  1. every record it cites was verified earlier in the case (§9.2) (evidence_not_held), and is about the claim's algorithm, by term and digest (evidence_mismatch);
  2. under a policy that requires reproduced evidence (§9.1), every record it cites was reproduced (evidence_not_reproduced);
  3. for each attack it claims, in order: at least one cited record measures it (claim_unevidenced), and every cited record that measures it shows survival (claim_exceeds_evidence).

So a claim with no record, a claim of an attack no record measured, and a claim of an attack a record shows lost — one item missed under it, or one negative read as marked — are refused, each with its own reason. A claim is as strong as the records it cites and no stronger: it covers the parameters measured and not those between them, the corpus's kind of content and not every kind, and a stated record's table as its signer stated it. A claim that holds over reproduced records is reproduced; over any stated record, stated.

9. Verification

9.1 Inputs

A verifier is given a case: records in an order, as texts; with any robustness record, the corpus document it was handed as that record's corpus, or none; the samples it holds, each found by its SHA-256; the algorithms it implements, by term (each with the digest §5 check 5 pins); and its policy, evidence: reproduced, a claim's records must be reproduced (§8 step 2), or stated. A verifier bounds what it runs: this revision's reproduces items whose frames are at most 2^26 bytes each and at most 2^30 bytes in all, the caps of the integer video encoder its transforms call (integer-payloads §7), and states a record over a larger item.

9.2 Steps

The records are verified in their order, each by its kind: a robustness record by §5, a robustness claim by §8. A record whose kind is neither, or that does not read, is record_malformed. The first check that fails is the case's refusal, with the record's position. A robustness record a claim cites is held only once it has been verified, earlier in the case.

9.3 What an accepted case states

For each record, in order, its kind, its link hash and its status: for a robustness record, reproduced or stated, with its algorithm, its corpus and its number of attacks; for a claim, reproduced or stated (§8), with its algorithm and its number of attacks claimed.

10. WAI's own schemes

wai-mark:tent64 (synthesis §7.5) carries three robustness records, one per measurement set of synthesis §7.5.8, and a robustness claim, in the corpus (§13):

  • The corpora. spectral and leaves, of media image, sixteen pictures of 512 × 512 each; clips, of media video, four clips of eight frames of 256 × 256: the sets the synthesis corpus generates from fixed seeds, each item named by the SHA-256 of its samples. Their negatives are the same samples, unmarked, as synthesis §7.5.8 reads them. The photographs of synthesis §7.5.8 are not in the repository, so they have no record here.

  • The values are the payloads synthesis §7.5.8 marked each item with: class 3, then the first seven bytes of SHA-256(wai-mark:battery payload ‖ 0x00 ‖ the item's name).

  • The battery is synthesis §7.5.7's lists, the pictures' for spectral and leaves and the clips' for clips, each attack under its name there, its transform and parameters by this table, and the seed of its index in its list:

    synthesis §7.5.7 transform and parameters
    none none
    int-motion-qq, clip-int-motion-qq, staged-base-qq, half-then-int-motion-qq int-motion, clip-int-motion, staged-base, half-then-int-motion, each [q]
    denoise3, denoise3-x4 denoise3 [1], [4]
    scale-n/d, aspect-n/d, contrast-n/d scale, aspect, contrast, each [n, d]
    upscale2, mirror, rotate-90, local-blur, frame-drop, one-frame the transform of that name
    crop-p% crop [p]
    rotate-0.5, rotate-1, rotate-2 rotate [65534, 572, 0], [65526, 1144, 0], [65496, 2287, 0]
    noise-s noise [s]
    brightness+32, brightness-32 brightness [32, 0], [32, 1]
  • The tables are the harness's on those inputs, and equal synthesis §7.5.8's columns spectral, leaves and clips: survived its count of pictures that gave their payload, wrong 0, false_positives the unmarked pictures that gave any, 0 in every row.

  • The claim is exactly the list of synthesis §7.5.8, claims to survive: its picture attacks with media image, its clip attacks with media video, citing the three records. It holds (§8): each attack it claims is measured, and shows survival, in every record of its media. scale-1/4, which both sets survive, is not claimed, because one photograph of synthesis §7.5.8 did not; a claim may claim less than its records show.

The registrant of wai-mark:tent64's records in the corpus is a key of the corpus, derived from a fixed seed: the records show the measurement, and their signature shows who stated it. Anyone who holds the generated sets reproduces them; the corpus holds the sets' digests and not their samples, so a verifier without the reference's generators reads them as stated, and checks their tables against the synthesis corpus's battery.json, a measurement written by another program (§13).

wai-mark:tent64s, the tiled scheme proposed beside it, is measured by the same battery with attacks of its own; its records are a registration of the revision that registers it.

11. The proposed robustnessEvidence object

Informative. Robustness Evidence for C2PA Soft-Binding Algorithms (pinned in the header) proposes an optional robustnessEvidence object in an entry of the C2PA soft-binding algorithm list (C2PA 2.4 §18.10.4), filled by the algorithm's owner: a methodology, a corpus, a harness, a date, and results, by attack, each with a balancedAccuracy. It observes that the list's entries carry prose robustness claims with no measured evidence and no shared way to produce it, and names, beside balanced accuracy, the survival (true-positive) rate and, for formal use, the true-positive rate at 1% false-positive rate. A robustness record gives each of its members:

robustnessEvidence from a robustness record
methodology the extension and section of harness: robustness-evidence §6, wai-robustness-harness:1
corpus sha256: and the hex of corpus; the corpus document names each item by the SHA-256 of its samples
harness harness, and the implementation that ran it
date the date of issued_at
results one member per row, under the row's attack
results.<attack>.balancedAccuracy balanced_accuracy [n, d], written as the decimal n/d
survival rate survival
true-positive rate at 1% false-positive rate tpr_at_1pct_fpr, absent where null
false-match rate false_positive_rate

The mapping runs one way. The object carries rates as decimal numbers, which a record never does, and not the counts, the battery's transforms with their parameters and seeds, the algorithm's digest, the corpus's items or a signature; so a record gives the object, and the object does not give a record. A verifier of the object reads the decimals as their signer wrote them; a verifier of the record recomputes the counts (§9). The proposal asks for each owner to measure its own algorithm and for no ranking across owners; a record carries no comparison either, and is about one algorithm on one corpus. The proposal's reference results are of text, whose transforms §4 does not register (§3).

12. Reasons

The codes this extension adds, each with where it is raised:

reason raised at
record_malformed §2, §9.2
rate_not_exact §2, §7
bad_key §2
bad_signature §2
battery_malformed §5 check 1
transform_invalid §4, §5 check 1
battery_not_applicable §4, §5 check 1
table_malformed §5 check 2
table_inconsistent §5 checks 3 and 6
rate_mismatch §5 check 4, §7
algorithm_mismatch §5 check 5
values_invalid §5 checks 5 and 8
corpus_mismatch §5 check 6
corpus_malformed §3, §5 check 6
samples_mismatch §3, §5 check 7
table_irreproducible §5 check 8
evidence_not_held §8 step 1
evidence_mismatch §8 step 1
evidence_not_reproduced §8 step 2
claim_unevidenced §8 step 3
claim_exceeds_evidence §8 step 3

13. Conformance

live-conformance/robustness/ holds the corpus, written from fixed seeds by wai_robustness_vectors gen. It has:

  • Corpus documents: conformance, three pictures of 256 × 256 whose samples the corpus holds; and tent64-spectral, tent64-leaves and tent64-clips, the measurement sets of §10, by their digests.
  • Robustness records: one over conformance with a battery of thirteen attacks — among them both gammas, a crop and a small rotation the scheme does not survive, and a quarter turn, a mirror and a re-encode it does — which any verifier holding its samples reproduces, the harness rerun end to end; and the three records of wai-mark:tent64 of §10, stated without their samples.
  • Robustness claims: wai-mark:tent64's claim of §10, and a claim over the conformance record under a policy that requires reproduced evidence.
  • Cases refused for each reason of §12, among them a record whose table does not recompute (one count changed and its rates with it, signed again), a record over a corpus digest that is not the corpus document's, a rate written as a decimal number, a rate not in lowest terms, a claim of an attack its records show lost, a claim of an attack no record measured, a claim citing no record, and a claim under a policy that requires reproduced evidence, citing stated records.

A conforming verifier gives every case the outcome, the reason, the record and the statements the corpus states. wai_robustness_vectors verify and the test tests/robustness.rs check that the files regenerate byte for byte — the records of wai-mark:tent64 among them, which needs the harness run over the generated sets — that wai-rs gives every case its outcome, that every reason of §12 appears, that §12 lists exactly the reference's codes, that each transform gives the bytes synthesis §7.5.7's battery gives for every attack of it, that the tent64 records' tables equal the synthesis corpus's battery.json, and that the tent64 claim is synthesis §7.5.8's list. live-conformance/robustness_verify.py is the second implementation, written from this text: it shares no code with wai-rs; it runs wai-mark:tent64 with the synthesis corpus's second implementation (_wai_mark.py), implements the transforms of §4, reruns the conformance record's battery end to end on the samples the corpus holds and requires the same table, checks every signature with WAI's strict rule (claim-conformance/_ed25519.py) and every link hash with BLAKE3 from claim-conformance/_blake3.py, checks the tent64 records against battery.json and the claim against synthesis §7.5.8's text; it mutates copies of the corpus and requires every copy to fail, and drops each of its own checks in turn and requires the corpus to catch the weakened verifier.

14. Security and privacy considerations

  • A battery is not an adversary. A record measures the transforms its battery names. An adversary who knows the algorithm searches for one outside them, and a key-free watermark (synthesis §7.5.6) can be read, and so attacked, by anyone. A claim is a floor under the transforms it names, never a statement that the binding cannot be removed.
  • Choosing the corpus. The party that measures chooses the corpus, and a corpus of content the algorithm favours shows more than one that it does not. A record names its corpus by digest so a reader can see what was measured, and a claim covers that kind of content and no other (§8). A relying party that needs a rate for its own content measures its own corpus.
  • Stated records. A record whose corpus a verifier does not hold is checked for its arithmetic and its signature only. Its table is its signer's statement; under a policy that requires reproduced evidence a claim resting on it is refused (§8 step 2).
  • What a negative count shows. False positives over U negatives bound nothing below 1/U (§7). An algorithm's false-positive bound is its definition's, derived and checked there.
  • Privacy. A corpus is content; a published record names its items by digest and carries no samples. A party that publishes a corpus of pictures of people publishes those pictures; the record does not require it.