Change:
This partially addresses the problem of preventing inter-batch replay attacks described by NAME in . Along the way, clean up the description of collect requests so that it talks about protocol messages rather than API calls (as NAME suggested).
Similar to , it should not be possible for a share to be included in more than one batch. The timestamp currently gives us some way of ensuring that reports map to at most batch, but not a way of ensuring that reports aren't included two overlapping batch windows. It's not immediately clear if include a single report in two overlapping batch windows is a problem, or if a problem only arises when these windows are disjoint. We should probably clarify what are the inter-batch requirements here and what sort of replay prevention is required.
The aggregators (either the leader or helper) can prevent the collector from getting outputs from overlapping batches by remembering batch intervals. For example, suppose the leader responded successfully to a collect request for time interval . Then suppose that, some time later, the collector requests output shares for , where . The potential privacy violation occurs because the sets of reports in each batch are non-disjoint. To prevent this, the leader might respond to the second request with an error, or it might just include the reports in .
If aggregators are meant to use the report timestamp to protect against replay, then I think it needs to appear in the AEAD-ed message constructed by the client (i.e. ). Otherwise a leader could insert arbitrary timestamps into and trick helpers into exceeding privacy budgets or double counting inputs. Additionally, a batching client could insert arbitrary values into .
I created a PR that addresses this issue partially. What's left to do is allow multiple collect requests over the same batch, as required by the PDA protocol (e.g., Hits). I have an idea for how to do this, I'll follow up with a PR.
Closed by URL

Discussion:
instance, the collector might authenticate themselves by logging into a website that has some trust relationship with the leader.)] <del> [TODO: @chris-wood suggested we specify APIs for producing and consuming each of the messages in the protocol.  Specific PDA protocols would implement this API.] </del> *Error handling.* In this section, we will use the verbs "abort" and "alert with "[some error message]"" to describe how protocol participants react to various error conditions.  The behavior is

leader must have obtained the helper's encrypted output share (see pa-aggregate).  In general, the procedure by which the aggregators verify and aggregate reports depends on parameters carried by the <del> collect request.  This procedure is described below in pa-aggregate. </del> <ins> collect request. </ins> 3.4.1. <del> [TODO: Decide whether to specify things in terms of functions. @ekr pointed out that it would be clearer to just talk about protocol messages.] The collect protocol is an iterative procedure driven by the collector and parameterized by a PDAParam.  At a high level, it proceeds as follows.  First, the collector initializes local per- protocol state using the corresponding PDAParam.  This state object has the following member functions: CreateRequest(): Creates a PDACollectReq object, defined below, that is sent to the leader to complete one iteration of the protocol.  Along with any protocol-specific parameters, the message specifies a time interval "[batch_start, batch_end)" that determines the batch of reports to be aggregated.  It must be that "batch_end - batch_start >= PDAParam.batch_window" and "batch_start" and "batch_end" are multiples of "PDAParam.batch_window". Update(resp: PDACollectResp): Consumes an opaque PDACollectResp object, defined below, that is received from the leader in response to a PDACollectReq. Finished(): Returns a boolean indicating indicating whether or not the collection procedure is complete.  This function returns true when the collect iteration is complete. Output(): Returns the aggregate output corresponding to the protocol. </del> <ins> A collect request is associated with a PDA task.  Along with the task ID, the request includes a time interval that determines the batch of reports to be aggregated.  To make a collect request, the collector issues a POST request to "[leader]/collect", where "[leader]" is the leader's endpoint URL.  The body of the request is structured as follows: The batch window of the request is the interval "[batch_start, batch_end)".  A collect request is said to be valid if all of the following conditions hold (let "PDAParam" denote the parameters for the PDA task): The batch window of the request aligns with the size of the batch window for the task, i.e., "batch_start" and "batch_end" are multiples of "PDAParam.batch_window". </ins> <del> The collect procedure for a given PDAParam structure "param" is then driven with the following algorithm: </del> <ins> The batch window of the request is at least the minimum batch window for the task, i.e., "batch_end - batch_start >= PDAParam.batch_window". The batch window of the request does not overlap with the batch window of any previous request.  [TODO: Enforcing this condition breaks hits, which explicitly requires multiple collect requests on the same batch.  We'll need to fix this.] The leader responds to valid collect requests by first interacting with the helper as described in pa-aggregate.  Once it has obtained the helper's encrypted output share for the batch, it responds to the collector's request with the following message: </ins> [OPEN ISSUE: Describe how intra-protocol errors yield collect errors (see issue#57).  For example, how does a leader respond to a collect

following message: To respond to valid output share requests, the helper first checks <del> that "batch_start" and "batch_end" are multiples of "task.batch_window" and that "batch_end - batch_start >= task.batch_window".  Next, it extracts from its state the set of input shares that fall in the window "[batch_start, batch_end)".  If the size of the batch is less than "task.batch_size", then it aborts and alerts the leader with "insufficient data".  Otherwise, it computes its output share, which has the following structure: </del> <ins> that the request corresponds to a valid collect request.  Next, it extracts from its state the set of input shares that fall in the window "[batch_start, batch_end)".  If the size of the batch is less than "task.batch_size", then it aborts and alerts the leader with "insufficient data".  Otherwise, it computes its output share, which has the following structure: </ins> Next, it encrypts its output share under the collector's HPKE public key:

"R2.time == R1.time" but "R2.jitter <= R1.jitter", then we say that "R2" _does not follow_ "R1". <del> To prevent replay attacks, it suffices for each aggregator to ensure that each report it consumes follows the previous one it consumed. To prevent the adversary from tampering with the ordering of reports, honest clients incorporate the ordering-sensitive parameters "(time, jitter)" into the AAD for HPKE encryption. Note that this strategy may result in dropping reports that happen to have the same timestamp and jitter value. </del> <ins> To prevent replay attacks, each aggregator ensures that each report it consumes follows the previous one it consumed.  To prevent the adversary from tampering with the ordering of reports, honest clients incorporate the ordering-sensitive parameters "(time, jitter)" into the AAD for HPKE encryption.  Note that this strategy may result in dropping reports that happen to have the same timestamp and jitter value. Aggregators prevent the same report from being used in multiple batches by only consuming valid collect requests, as described in pa- collect. </ins> 8.8.