telemetry-aggregation §6.1's rule that `rejected` counts no report the aggregator aggregated had no test, and impression_aggregate_verify.py did not apply it. The flight flow now sends the helper a report it committed, and the Python flow filters as the Rust does. impression-aggregates §7.3 says what a reader does with a statement of version 1 beside one of version 2, and the corpus adds a receipt and a table of each. The ledger's corpus takes these receipts, so it is regenerated by the commit after this one. Co-Authored-By: Claude Opus 5.5 <[email protected]>
29 KiB
WAI Extension: QoE Aggregation
Status: Draft. Registers the record
wai.telemetry.aggregate: quality-of-experience figures the service receives only as sums. A sink reports each session's measures, the session metrics of delivery-telemetry §7 (its §7.2) cast as Prio3 measurements, through the Distributed Aggregation Protocol ofdraft-ietf-ppm-dap-20(DAP): it secret-shares each measurement between a leader and a helper aggregator, and encrypts each share to its aggregator. The aggregators check each measurement with the range proof of its Prio3 VDAF (draft-irtf-cfrg-vdaf-22) without either of them seeing it, sum the valid ones, and release an aggregate share only for a batch of at least the task's minimum size, which this extension never lets fall below fifty reports. Each aggregator signs a statement of its aggregate share with the batch's parameters (WQS1), and the collector, the service, signs an aggregate receipt (WQA1) that carries the task, both statements and the result: a reader recomputes the result from the two shares and knows what was summed, over how many reports, and by whom. With two aggregators, neither learns a measurement as long as one of them follows the protocol. A room's quality-of-experience cell (live-metering §4.7) is computed the same way. Leader and helper are roles any party can run. No new transport: DAP runs over HTTP, as its own text defines. The reference implementation is thetelemetry_aggregatemodule ofwai-rs(featuretelemetry_aggregate), with a reference leader, helper and collector that run in its tests; the corpus islive-conformance/aggregate/, and its second implementation islive-conformance/telemetry_aggregate_verify.py. Keywords MUST, MUST NOT, SHOULD, MAY are RFC 2119/8174.Texts read for this revision:
draft-ietf-ppm-dap-20(all of it; §4 to §8 and Appendix B are used here) anddraft-irtf-cfrg-vdaf-22(§4 to §7, §9, Appendices A to C), both Internet-Drafts, with the test vectors the VDAF draft's [TestVectors] reference names; RFC 9180 (§4, §5.1, §6.1, §7.1, Appendix A.1); RFC 9861 (§2.1, §5); RFC 5869; RFC 8032; FIPS 180-4; CTA-5004-B through delivery-telemetry. A section number of a draft in this text is that revision's.
1. Scope
In scope: the measures and the measurement each takes from a session's metrics (§3); the DAP
task a measure runs under, the WAI layout of its task_info, its id and the rules a task
meets (§4); the roles of DAP as this extension uses them (§5); the statement an aggregator
signs (§6); the aggregate receipt (§7); its verification (§8); where aggregation replaces
the suppression of delivery-telemetry §8 (its §8.4), where that
suppression still applies, and a room's cell (§9).
Out of scope: running aggregators in production; how a deployment chooses its aggregators
and agrees a task with them, which DAP leaves out of band (draft-ietf-ppm-dap-20 §4.2);
client authentication and anonymizing proxies (DAP §8.3, §8.4), except that a task of measure 5
authenticates its reports by the token of impression-aggregates §7.2;
differential privacy (DAP §8.5); any VDAF but the four Prio3 variants of §3.
Relationship to the core spec: wai.telemetry.aggregate is a record (SPEC
§5): a signed record a WAI tool emits and verifies. It names no payload format and is not
dispatchable.
2. Terms
- DAP, task, report, report share, input share, leader, helper,
collector, batch, batch bucket, batch interval, minimum batch size,
aggregate share, aggregate result: as
draft-ietf-ppm-dap-20§1.2.1 defines them. This extension's collector is the content's own service. - Prio3, measurement, verification, verifier share: as
draft-irtf-cfrg-vdaf-22defines them (§5, §7). - Session metrics: the figures delivery-telemetry §7 computes from one session's log (its §7.2).
- Measure: a quantity of §3, aggregated by one task.
- Statement: an aggregator's signed account of the aggregate share it released (§6).
- Aggregate receipt: the collector's signed record of an aggregate result (§7).
3. Measures
§3.1 A measure takes one figure, or a few, from a session's metrics and aggregates them with
one Prio3 variant of draft-irtf-cfrg-vdaf-22 §7.4, with two aggregators and one proof:
| code | measure | VDAF (§7.4 there) | configuration (DAP Appendix B) | measurement, from the session's metrics |
|---|---|---|---|---|
| 1 | outcomes |
Prio3SumVec | length 4, max_measurement 1, chunk_length 2 |
[started, start_failed, exit_before_start, vst_ms ≠ null], each 0 or 1 |
| 2 | stalls |
Prio3Sum | max_measurement 65535 |
stalls, at most 65535 |
| 3 | durations |
Prio3SumVec | length 3, max_measurement 4294967295, chunk_length 10 |
[stall_ms, engaged_ms, vst_ms], vst_ms 0 when null, each at most 4294967295 |
| 4 | start_time |
Prio3Histogram | length 8, chunk_length 3 |
the bucket of vst_ms: 0 to 5 for a time below 250, 500, 1000, 2000, 4000 and 8000 ms, the first that holds; 6 for a later start; 7 for null |
| 5 | impressions |
Prio3Histogram | length L and chunk_length ⌈√L⌉ of the catalogue whose digest is scope (impression-aggregates §4, §5) |
not a session's: one presentation's cell (impression-aggregates §6.2) |
§3.2 A figure past its measure's largest value is reported as that value, and a sum counts it at that value. Each largest value is past what one session's figures reach in practice: a stall count of 65535, and 49 days in milliseconds.
§3.3 The range proof is the VDAF's: Prio3Sum and Prio3SumVec encode each value as the bits
of draft-irtf-cfrg-vdaf-22 §7.4.2 and prove each bit is 0 or 1, so a value past the
largest cannot be encoded, and Prio3Histogram proves its vector has one 1 and every other
element 0 (§7.4.4 there). An aggregator that verifies a report whose measurement is out of
range finds the proof fails and rejects the report, vdaf_verify_error (DAP §4.5.3.2),
without learning the measurement.
§3.4 Prio3Count (VDAF §7.4.1) is implemented with the others and passes the draft's vectors; no measure of this revision uses it.
4. The task
4.1 task_info
A measure runs under one DAP task (draft-ietf-ppm-dap-20 §4.2). Its configuration's
task_info, which DAP leaves to the deployment, is laid out as follows, 153 bytes:
| field | size | meaning |
|---|---|---|
label |
8 | wai-qoe1 (ASCII) |
measure |
1 | the measure's code (§3.1) |
scope |
32 | 32 zero bytes for a task of the service's own; the room id for a room's (§9.2); the digest of its catalogue for a task of measure 5 (impression-aggregates §5.1) |
salt |
16 | chosen by the task's author, so that two tasks of one configuration have two ids |
leader |
32 | the Ed25519 key the leader signs its statements with |
helper |
32 | the helper's |
collector |
32 | the collector's, which signs the aggregate receipt |
The task configuration enters the authenticated data of every input share and of every aggregate share (DAP §4.4.2.1, §4.6.7), so a report is bound at upload to the measure, the scope and the three keys, and an aggregator that holds another configuration cannot decrypt it.
4.2 The task id
The task id is SHA-256 of the 29 bytes wai:telemetry-aggregate/task (ASCII) and 0x00,
followed by the TaskConfiguration's encoding. DAP admits a task id derived from the task's
parameters (§8.6.1 there); the salt makes it unpredictable.
4.3 Rules
A task configuration is refused, in this order:
task_invalidwhen it does not read as a TaskConfiguration; itstask_infois not 153 bytes beginningwai-qoe1; itstime_precisionis 0; itsbatch_modeis not time-interval (1) or itsbatch_configis not empty; or an extension is other thantask_interval(1) holding one Interval, or the extensions are not in ascending order of type;measure_unknownwhenmeasureis no code of §3.1;measure_mismatchwhenvdaf_typeandvdaf_configurationare not the measure's: its VDAF identifier (VDAF Table 19) and its DAP Appendix B encoding. For measure 5, this rule is impression-aggregates §5.3 steps 2 to 4 against the catalogue whose digest isscope; a verifier given no such catalogue refuses the taskcatalogue_mismatch;floor_too_lowwhenmin_batch_sizeis below 50, the floor of delivery-telemetry §8 (its §8.4);aggregator_alonewhenleaderandhelperare one key.
The time-interval batch mode is the only one, because a leader-selected batch is one the
leader composes (DAP §8.2); a task's time_precision SHOULD be an hour or more, which
also coarsens the report times an aggregator sees (DAP §8.4).
4.4 The verification key
The aggregators share the VDAF verification key of the task, kept from every client (DAP
§8.6.2). The reference derives it as that section shows: HKDF-Expand of
HKDF-Extract(verify_key, a seed the aggregators agree) with the task id. Its verification
key id is 0.
5. The protocol
§5.1 The sink. When a session ends or is abandoned, the sink computes its metrics
(delivery-telemetry §7.2) and, for each measure the service runs, a report: a fresh random
report id, the time of the session's end or abandon in units of the task's
time_precision, and the measurement of §3.1 sharded and encrypted as DAP §4.4.2.1 does, with
the application context dap-20 followed by the task id. It uploads the reports to the
leader (DAP §4.4.2). A session gives at most one report to each task. Its reports carry no
sid, no principal and no extension. A task of measure 5 takes its reports from
impression-aggregates §6.3 instead: one for each presentation, its
time the presentation's, and with attestation one private extension.
§5.2 The aggregators. The leader and the helper run DAP §4.5 and §4.6 as written, over HTTP, with the HPKE suite of DAP §7 (DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, AES-128-GCM):
- each decrypts its own input share, checks it (§4.5.3.3, §4.5.3.4) and runs Prio3's
one-round verification with its peer by the ping-pong exchange of VDAF §5.7.1, so a report
whose proof fails is rejected by both,
vdaf_verify_error; - each commits a valid report's output share to the batch bucket of its time, once
(
report_replayedfor a second), and keeps per bucket the aggregate share, the count and the checksum, the XOR of the SHA-256 of each report id (§4.5.4.3); - each refuses to release an aggregate share for a batch below the task's minimum batch
size: the leader fails the collection job and the helper the aggregate share request,
invalidBatchSize(§4.6.1, §4.6.4); the helper also refuses a report count or checksum not its own,batchMismatch, and a batch already collected,batchOverlap; - each encrypts its aggregate share to the collector (§4.6.7) and signs a statement of it (§6).
A batch below the minimum therefore yields no aggregate as long as one aggregator follows the protocol: the result needs both shares.
§5.3 The collector. The collector opens both aggregate shares and both statements, requires each statement's share and count to be the ones DAP delivered, unshards the shares (VDAF §7.2.5) and signs the aggregate receipt (§7).
§5.4 What each party learns. The leader and the helper each hold one share of each measurement: a share alone, and the verification exchange, reveal nothing about it but whether it is valid (VDAF §7.1; DAP §2.2). Each sees the reports' ids, times and lengths and which reports were rejected. The collector, and a reader of a receipt, learn the aggregate, its report count and its batch, nothing per session. Two aggregators that pool their shares learn every measurement: the deployment chooses aggregators that do not, and DAP §8.7 says why their infrastructure should not be shared.
6. The aggregate share statement
6.1 Layout
When an aggregator releases its aggregate share for a batch, it signs a statement, WQS1:
| field | size | meaning |
|---|---|---|
magic |
4 | WQS1 (ASCII) |
version |
1 | 1, or 2 for a statement that states rejected |
role |
1 | 2 for the leader, 3 for the helper (DAP §4.1) |
task_id |
32 | the task id (§4.2) |
collection |
32 | SHA-256 of the CollectionJobReq the share answers (DAP §4.6.1) |
batch |
16 | the batch: its interval, start and duration (u64 each) in units of the task's time_precision |
report_count |
8 | u64: the reports aggregated in the batch |
checksum |
32 | the batch's checksum (DAP §4.5.4.3) |
rejected |
8, in version 1 absent | version 2: u64, the reports this aggregator rejected, below |
agg_share |
4 + n | u32 length, then the aggregator's aggregate share, encoded as VDAF §7.2.7.5 encodes it |
signer |
32 | the aggregator's key, the one task_info names for its role |
sig |
64 | Ed25519 by signer over the 30 bytes wai:telemetry-aggregate/share (ASCII) and 0x01, followed by every byte before sig, verified strictly (SPEC §7 Signatures) |
Integers are big-endian. A statement is read exactly: its magic, version 1 or 2, a role of 2 or
3, and no byte past its end; otherwise it is refused statement_invalid.
rejected is the number of distinct reports, each with a time in the batch and none of them
aggregated, that this aggregator rejected with a report error during aggregation (DAP §4.5.3.4,
§4.5.4.3): a report it committed and was sent again is one it aggregated, and is not counted. A
leader counts every report of an aggregation job that did not commit, the ones its
helper rejected among them; a helper counts the ones it was sent and rejected itself, and never
sees one the leader refused first. So each aggregator states its own count and the two need not
agree. A report refused at upload (DAP §4.4.2.2) reached no aggregation and is counted by neither.
The count is that aggregator's word: no verifier recomputes it, since a rejected report leaves no
trace in report_count, checksum or the share. The signature covers it, so a statement with
its count changed is refused bad_signature.
6.2 When an aggregator signs
An aggregator signs a statement only for a batch whose aggregate share it releases under DAP
§4.6: a task that meets §4.3, a batch of at least the minimum batch size, and, at the helper,
a report count and checksum that are its own. Its batch is the batch selector's interval,
report_count and checksum are its batch's, and agg_share is the share it encrypted to
the collector. An aggregator of a task of measure 5 signs version 2
(impression-aggregates §7.3); of a task of another measure it signs
version 1 or 2.
6.3 How a statement travels
A statement carries an aggregate share in clear, so it travels as the share does: sealed to
the collector with SealBase (RFC 9180 §6.1) under the collector's HPKE configuration, with
the info string wai-telemetry-aggregate statement (ASCII) followed by the sender's role and
the collector's (0x00), and the AggregateShareAad of DAP §4.6.7 as authenticated data. The
leader that relays the helper's learns nothing from it. How the sealed statement reaches the
collector beside DAP's own messages is the deployment's.
7. The aggregate receipt
7.1 Layout
The collector signs the aggregate receipt, WQA1:
| field | size | meaning |
|---|---|---|
magic |
4 | WQA1 (ASCII) |
version |
1 | 1 |
task |
2 + n | u16 length, then the TaskConfiguration (DAP §4.2) |
collection |
4 + n | u32 length, then the CollectionJobReq (DAP §4.6.1) |
interval |
16 | the CollectionJobResp's interval: the smallest interval holding the batch's reports |
leader |
4 + n | u32 length, then the leader's statement |
helper |
4 + n | u32 length, then the helper's statement |
result |
2 + 16 × k | u16 count, then each element of the aggregate result as a u128 |
signer |
32 | the collector's key, the one task_info names |
sig |
64 | Ed25519 by signer over the 32 bytes wai:telemetry-aggregate/receipt (ASCII) and 0x01, followed by every byte before sig, verified strictly |
The receipt's digest is SHA-256 over every byte before sig.
7.2 Reading
A reader reads exactly the bytes it is given: truncated when they end before a field does,
malformed for another magic or a byte past sig, and unknown_version for a version other
than 1. A helper of length zero reads, and is refused at §8.1 step 5.
8. Verification
8.1 Steps
A verifier is given the receipt and, where it has them, the batch's reports: each report's id, its time and the task it was uploaded to. It runs these steps and stops at the first refusal, which it reports with the reason and where:
- The receipt reads (§7.2), at
receipt. - The task reads as a TaskConfiguration and meets §4.3, at
task:task_invalid,measure_unknown,measure_mismatch,floor_too_low,aggregator_alone. A task of measure 5 is verified by impression-aggregates §8.1, which is given its catalogue. - The collector:
signeris thecollectorkey oftask_info, otherwisewrong_signer; the signature verifies (bad_key,bad_signature), atreceipt. - The collection reads as a CollectionJobReq with a time-interval query of a duration of
at least 1, an empty aggregation parameter and no extension (Prio3's only aggregation
parameter is the empty one, VDAF §7.2.3): otherwise
collection_invalid, atcollection. - The statements: both are present, and their signers differ and their roles differ,
otherwise
aggregator_alone; each reads (§6.1,statement_invalid); the first has role 2 and the leader's key and the second role 3 and the helper's, otherwisewrong_signer; each signature verifies (bad_key,bad_signature), atleaderorhelper. - The binding: each statement's
task_idis the task's id (§4.2) and itscollectionthe SHA-256 of the receipt'scollection, otherwisetask_mismatch. - The batch: each statement's batch has a duration of at least 1 and falls within the
query's interval, and the receipt's
intervalhas a duration of at least 1 and falls within the batch (DAP §4.1.1), otherwisebatch_invalid. - Agreement: the two statements state one batch, one report count and one checksum,
otherwise
statements_disagree. Theirrejectedcounts are each aggregator's own, and are not compared. - The floor: the report count is at least the task's
min_batch_size, otherwisebatch_too_small. - The result: each
agg_shareis an aggregate share of the task's VDAF (its length, and each element below the field's modulus), otherwiseshare_invalid; the result is the two shares unsharded (VDAF §7.2.5), otherwiseresult_mismatch. - The reports, where given: each was uploaded to the receipt's task, each id appears
once, each time falls within
interval, and their number and checksum are the statements', otherwisebatch_mismatch, atreports.
8.2 Outcome
A receipt that passes is accepted: its digest; the task id, the measure, the VDAF and its
configuration, the scope, the time precision and the minimum batch size; the batch and the
interval; the report count; the result; the rejected of each statement of version 2, by its
aggregator; and the leader's, the helper's and the collector's keys. What was summed is the
measure's VDAF over that task's reports, over the report count the two aggregators each signed,
by the aggregators the task named.
An accepted receipt shows that two keys each signed its share of the batch and that the result is their sum. It does not show that the aggregators ran verification honestly: DAP assures a correct result only when both do (DAP §8). A statement makes each aggregator's part attributable to its key.
9. Where aggregation replaces suppression
9.1 Event and stream reporting
delivery-telemetry §8 suppresses a published cell of fewer than 50 sessions (its §8.4); the service holds each session's observations and refrains. Under this extension the service never holds a session's measures: it holds aggregates, each over at least the task's minimum batch size, enforced by each aggregator (§5.2) and checked by every reader (§8.1 step 9). For a figure a measure of §3 aggregates, a deployment MAY report it by DAP instead of in an Event-mode report, and the published figure is the receipt's.
§8.4's suppression still applies where reporting is a stream rather than a sum after the fact:
- Request-mode reports, which travel with each request to the host that serves it (delivery-telemetry §8.3): a host acts on them per request, and a deployment that keeps them to the delivery class does so with delivery-only Request mode (delivery-telemetry §4.3);
- Event-mode reports a service consumes as they arrive, for one session's playback while it plays, which no batch can wait for;
- a figure no measure of §3 aggregates.
9.2 A room's cell
A room's clients MAY report their sessions' quality of experience under this extension
instead of in the room's logs document (live-metering §6.1). The room's
outcomes, stalls and durations tasks have the room id as their scope. Over one batch,
the cell of live-metering §4.7 is, from the three accepted receipts of that batch and scope:
| cell field | from |
|---|---|
started, start_failed, exit_before_start, vst_count |
outcomes, elements 0 to 3 |
stalls |
stalls |
stall_ms, engaged_ms, vst_ms |
durations, elements 0 to 2 |
each with the report count of its receipt. A set of receipts that is not one of each of the
three measures, or that names more than one scope, a zero scope, or more than one batch, is
refused cell_mismatch. Within the clamps of §3.2 these are the figures live-metering §4.7
computes from the same sessions' logs. The room-session receipt then names no logs (qoe
zero) and its cell is suppressed, as that section requires of a receipt with fewer than 50
logs; the room's cell is the aggregate receipts', whose floor the aggregators enforce.
10. What a report reveals
The key classes of delivery-telemetry §3 (its §3.4) state, for each key of the CMCD mapping,
what it reveals and to whom. A DAP report reveals to each aggregator its id, its time, its length, which task it
belongs to and whether its proof verified, and to no party its measurement (§5.4). This
extension sets no consent requirement for the service's own mechanics: using a distributor's
WAI service is the person's agreement to how that service works, its aggregation among them.
Where a regime asks for a lawful basis for processing quality-of-experience data, the service
records it in a lawful-basis claim (jwp-receipts §2.4.6).
11. Reasons
The codes this extension adds, each with where it is raised:
| reason | raised at |
|---|---|
task_invalid |
§4.3, §8.1 step 2 |
measure_unknown |
§4.3, §8.1 step 2 |
measure_mismatch |
§4.3, §8.1 step 2 |
floor_too_low |
§4.3, §8.1 step 2 |
wrong_signer |
§8.1 steps 3 and 5 |
collection_invalid |
§8.1 step 4 |
statement_invalid |
§6.1, §8.1 step 5 |
aggregator_alone |
§4.3, §8.1 step 5 |
task_mismatch |
§8.1 step 6 |
batch_invalid |
§8.1 step 7 |
statements_disagree |
§8.1 step 8 |
batch_too_small |
§8.1 step 9 |
share_invalid |
§8.1 step 10 |
result_mismatch |
§8.1 step 10 |
batch_mismatch |
§8.1 step 11 |
cell_mismatch |
§9.2 |
measurement_key_in_delivery |
delivery-telemetry §4 (its §4.3) |
A verifier of this extension also raises truncated, malformed and unknown_version for a
receipt that does not read, and bad_key and bad_signature for its signatures. The
reference aggregators raise DAP's report errors (draft-ietf-ppm-dap-20 §4.1: among them
vdaf_verify_error, hpke_decrypt_error, report_replayed) and its problem types (§3.6
there: among them invalidBatchSize, batchMismatch, batchOverlap, batchInvalid), as
DAP names them.
12. Conformance
live-conformance/aggregate/ holds:
vdaf-22/: the Prio3Count, Prio3Sum, Prio3SumVec and Prio3Histogram test vectors and the XofTurboShake128 vector ofdraft-irtf-cfrg-vdaf-22Appendix C, as published at the commit its [TestVectors] reference names;vectors.json, written from fixed seeds bywai_telemetry_aggregate_vectors gen:- the keys: each aggregator's and the collector's HPKE configuration and private key, the three statement keys and a stranger's, and the seed of the verification keys (test material from fixed seeds, as every corpus here);
- fifty sessions' metrics, and a flow for each measure: its task, the reports (fifty
sessions, one with an out-of-range measurement, and in one flow a report replayed and in
another one whose helper share does not decrypt), the upload request and the reports it
refused, the aggregation job's request and response with each report's outcome, the
collection request, the aggregate share request, the helper's aggregate share, the
collection response, both sealed statements and the result; the
outcomesflow also holds the collections each aggregator refuses (a batch below the minimum, a checksum not the batch's, a batch past the query, a batch collected twice); - receipts: the four flows' receipts, accepted, and refused ones, each a control of one defect: every reason of §11 the receipt raises, the reading and signature codes, a receipt signed for a task the reports were not uploaded to, a batch below the minimum, a leader alone and a leader signing as both aggregators;
- cells: the room's cell from its three receipts, with the plaintext cell of live-metering §4.7 over the same sessions, which it equals, and a set of receipts of two scopes;
- delivery-only reports (delivery-telemetry §4.3): observations with their delivery-only reports and header shards, and reports that carry a key past the delivery class.
Statements of version 2 are in the corpus of impression-aggregates §11, whose aggregators sign them.
A conforming verifier gives every vector its outcome. wai_telemetry_aggregate_vectors verify and the test tests/telemetry_aggregate.rs check that vectors.json regenerates
byte for byte, that vdaf-22/ is the files as published and that wai-rs passes every one,
that every receipt, cell and delivery-only report gets its outcome, that every reason of §11
and every reused code refuses a vector, that §11 lists exactly the reference's codes, and
that delivery-telemetry §3 (its §3.4) classes exactly the keys the reference does.
live-conformance/telemetry_aggregate_verify.py is the second implementation, written from
this text and the drafts it cites: it shares no code with wai-rs; it evaluates Prio3 itself
(the fields, TurboSHAKE128 and the XOF, the FLP in the Lagrange basis) and passes the draft's
vectors; it opens every report with its own HPKE (X25519, HKDF-SHA256 and AES-128-GCM written
from RFC 7748, RFC 5869 and the AES and GCM specifications), recombines each measurement and
requires it to be its session's, runs both aggregators' verification and requires the
aggregation job's request and response byte for byte, recomputes every batch bucket, every
refusal, every aggregate share and every result; verifies every receipt and cell with WAI's
strict Ed25519 rule (claim-conformance/_ed25519.py); reads the key classes from
delivery-telemetry §3 (its §3.4) and this text's reasons from §11; and requires every mutated copy of
the corpus to fail.
13. Security and privacy considerations
- Non-collusion. Privacy rests on the two aggregators not pooling their shares (§5.4). A statement attributes each share to a key, so a reader sees which two parties a result depended on; it cannot see whether they colluded.
- Correctness. The range proof stops a sink from poisoning a sum with an out-of-range value (§3.3); it does not stop a sink from reporting a false value in range (DAP §2.2). An aggregator can still corrupt a result by an arbitrary amount with a wrong share; the statement makes that share its signed statement.
- Small batches. A batch at the floor reveals what fifty sessions sum to. A deployment that publishes overlapping figures across tasks of one population should consider what their differences reveal; DAP collects each batch once (§2.3 there), which this extension keeps, and §8.5 there describes noise an aggregator can add.
- Linking a session's reports. A session sends one report to each task, at once; an aggregator that sees their arrival together can link them, though not their values. A deployment that wants them apart sends them at different times or through a proxy (DAP §8.4).
- Sybil reports. Reports are not authenticated here; one party can submit many, and so approach a batch's floor with its own (DAP §8.1). The floor is a floor of reports, not of people.
- Verification key. A sink that knew the verification key could forge a report that passes verification (DAP §8.6.2); it is the aggregators' alone.