[DAP-17] TaskConf AAD Part 4: bind TaskConfiguration into HPKE AADs - #4714
Conversation
Bind each task's TaskConfiguration into the input-share and aggregate-share HPKE AADs (draft-ietf-ppm-dap-18, #4402). Both ends recompute each AAD independently, so byte-identical encoding across every party is mandatory or everything errors out. - InputShareAad gains task_configuration as field 2. - AggregateShareAad becomes {task_id, task_configuration, collection_job_req}, dropping aggregation_parameter + batch_selector, now matching DAP-18. - AggregateShareReq becomes {collection_job_req, batch_selector, report_count, checksum}: - collection_job_req replaces the standalone aggregation_parameter. - The Leader forwards its stored CollectionJobReq verbatim and the Helper binds it directly (no reconstruction); - the aggregation parameter is now reached via req.collection_job_req().aggregation_ parameter(). Note: `core/src/vdaf.rs`: `to_vdaf_config()` (which is `#[cfg(test-util)]`) maps `FakeFailsVerifyInit` and `FakeFailsVerifyStep` to `VdafConfig::Fake`, so the Helper can build an `InputShareAad` before the intentional verify failure.
Update the interop binaries to build a byte-identical TaskConfiguration. - Flip AggregatorAddTaskRequest.leader/.helper to janus_messages::Url so endpoint bytes survive interop provisioning verbatim (no url::Url re-encoding); the interop aggregator binds them directly. - Add a shared INTEROP_TASK_INFO placeholder (the interop test API has no task_info concept) and an interop_batch_config() helper, used identically by the interop aggregator, client, and collector. - Carry min_batch_size + batch_mode in the interop upload request, and the helper endpoint + min_batch_size in the collector add_task request; new fields are serde-optional so a non-Janus runner still parses. - Test harness: source real task_info/task_interval/helper endpoint into the collector (collect_aggregate_result_generic now takes a helper_port) and send min_batch_size + batch_mode from the container client. - Ignore the janus_janus_* container tests: a follow-up will re-enable them.
When we removed BatchSelector from AggregateShareAad, the HPKE no longer implicitly rejects a substituted batch selector. Add the two checks DAP-19 §4.6.4 requires of the helper, both before the datastore transaction so they apply to cached requests too Also while we're here, refuse aggregate share collection in tools/collect rather than decrypt against an AAD built from placeholder TaskConfiguration values. (Issue #4713).
I interpret this spec text as describing how the query relates to internal state, not the batch selector. Later on, 5.1.2 says that for the time interval mode, "The payload of BatchSelector.config is Edit: Nevermind, the next paragraph gives the more flexible rules for when the batch selector is consistent query, so I'm now getting mixed messages from this section. |
|
I think we should correct the spec text to resolve some ambiguities here about whether the query and (full) batch selector need to match exactly. IMO no, they do not, but David is right that the text is unclear at best, if not contradictory here. I wrote up github.com/ietf-wg-ppm/draft-ietf-ppm-dap/issues/791 to track draft-ietf-ppm-dap fixes. Returning to this PR: I'm not comfortable with the decision to require the aggregate share job's batch selector to exactly match the collection job request's query in the time interval case. It seems reasonable to me to allow leaders to emit batch selectors that are narrower than the query and I think Janus should handle this since we're the reference implementation of DAP. We went to the trouble of making this possible in the spec by including the collection job req in the aggregate share req, so I think we should wire this up properly. I'm sensitive to the point that persisting the collection job req on the helper side expands the scope of this PR, though, so I'd be OK leaving that as a TODO and doing it in a later PR. Or perhaps resequencing the stack of changes so that you first make a change to persist the collection job req on the helper side and then rebase this change to start folding that into the AAD. I think the latter would be better, but you're doing the actual typing, J.C., so I defer to your preference. |
tgeoghegan
left a comment
There was a problem hiding this comment.
The code changes in here look fine, though I think it needs re-review after rebasing on #4707 and potentially the change to persist collection job req on the Helper side.
| .map_err(Error::MessageEncode)?, | ||
| BatchSelector::<B>::new(aggregate_share_job.batch_identifier().clone()), | ||
| task.task_configuration()?, | ||
| // On the poll path we no longer have the Collector's original CollectionJobReq |
There was a problem hiding this comment.
As discussed in another comment, we should either take a TODO to persist the collection job req and use it in the AAD here, or rebase this change on another that persists the collect req.
This is already based on #4707. I've opened #4743 for the |
I'm afraid this is another draw the rest of the owl that can't split up worth a darn and have green tests. Except for the ones I'm marking to ignore!
This is broken into sub-commits which deal with these specific headings... but I feel like this one is just as easy to review as a big blob of
crazyhigh quality codeBind each task's TaskConfiguration into the input-share and aggregate-share HPKE AADs.
Both ends recompute each AAD independently, so byte-identical encoding across every party is mandatory or everything errors out.
InputShareAadgainstask_configurationas field 2AggregateShareAadbecomes{task_id, task_configuration, collection_job_req}, droppingaggregation_parameter + batch_selector, now matching DAP-18AggregateShareReqbecomes{collection_job_req, batch_selector, report_count, checksum}:collection_job_reqreplaces the standaloneaggregation_parameterCollectionJobReqverbatim and the Helper binds it directly (no reconstruction)req.collection_job_req().aggregation_parameter()Important
§4.6.4 defines consistency per batch mode, and for time-interval permits any selector within the queried interval (containment):
all batch buckets whose batch bucket identifiers are contained within the batch interval specified in the CollectionJobReq's queryHere I implement the stricter requirement that the selector reconstruct the query exactly (equality). The reason is that the
PUTpath storesbatch_selector().batch_identifier(), and the poll/GETpath rebuilds the AAD's query from that stored identifier alone viaquery_for_collection_identifier. That reconstruction is lossy for anything narrower than the query, so a strict sub-interval would decrypt on thePUTpath and silently fail on the poll path. Equality fails withbatchInvalidatPUT; containment would accept and fail silently later. (invalidMessagevsbatchInvalidis a judgment call imo, I'm open to changing. I've already changed my mind once.)The consequence is that Janus rejects some spec-conformant sub-interval requests -- if we want true containment, the Helper needs to persist the query rather than re-derive it, which yet another big expansion of scope for this already big change. I'm erroring on YAGNI here, but if we think we want that, I want to file an issue and take it as a follow-on. I have a test for this, noting the choice.
For leader-selected the check is trivially satisfied, since the query body is empty. (phew)
Note
core/src/vdaf.rs:to_vdaf_config()(which is#[cfg(test-util)]) mapsFakeFailsVerifyInitandFakeFailsVerifySteptoVdafConfig::Fake, so the Helper can build anInputShareAadbefore the intentional verify failure.Update the interop binaries to build a byte-identical
TaskConfiguration.janus_messages::Urlso endpoint bytes survive interop provisioning verbatim (nourl::Urlre-encoding); the interop aggregator binds them directly.INTEROP_TASK_INFOplaceholder (the interop test API has notask_infoconcept) and aninterop_batch_config()helper, used identically by the interop aggregator, client, and collector.min_batch_size + batch_modein the interop upload request, and the helperendpoint + min_batch_sizein the collectoradd_taskrequest; new fields are serde-optional so a non-Janus runner still parses.task_info/task_interval/helperendpoint into the collector (collect_aggregate_result_generic now takes a helper_port) and sendmin_batch_size + batch_modefrom the container client.janus_janus_*container tests: a follow-up will re-enable them.... somehow.A Stake in
tools/collect(see #4713)new_collectorfabricates itsTaskConfigurationvalues to make things work. But now they're actively wrong. I've made the two AAD-consuming paths (run_collectionandrun_poll_job) now fail fast with an error naming the missing parameters. #4713 (David's suggested--task-config <blob>) is probably The Way.new-jobis unaffected and still works as it never computes an AAD. Success through laziness!