diff --git a/spec.bs b/spec.bs index 218b27a..bb7b119 100644 --- a/spec.bs +++ b/spec.bs @@ -131,6 +131,7 @@ interface PrivateAggregation { dictionary PAHistogramContribution { required bigint bucket; required long value; + bigint filteringId = 0; }; dictionary PADebugModeOptions { @@ -168,12 +169,22 @@ are: 1. If |contribution|["{{PAHistogramContribution/value}}"] is negative, [=exception/throw=] a {{RangeError}}. 1. Let |scopingDetails| be [=this=]'s [=PrivateAggregation/scoping details=]. +1. Let |batchingScope| be the result of running |scopingDetails|' [=scoping + details/get batching scope steps=]. +1. Let |filteringIdMaxBytes| be the [=default filtering ID max bytes=]. +1. If [=pre-specified report parameters map=][|batchingScope|] [=map/exists=]: + 1. Set |filteringIdMaxBytes| to [=pre-specified report parameters + map=][|batchingScope|]'s [=pre-specified report parameters/filtering ID + max bytes=]. +1. If |contribution|["{{PAHistogramContribution/filteringId}}"] is not [=set/ + contained=] in [=the exclusive range|the range=] 0 to + 256|filteringIdMaxBytes|, exclusive, [=exception/throw=] a + {{RangeError}}. 1. Let |entry| be a new [=contribution cache entry=] with the items: : [=contribution cache entry/contribution=] :: |contribution| : [=contribution cache entry/batching scope=] - :: The result of running |scopingDetails|' [=scoping details/get batching - scope steps=]. + :: |batchingScope| : [=contribution cache entry/debug scope=] :: The result of running |scopingDetails|' [=scoping details/get debug scope steps=]. @@ -349,6 +360,8 @@ An aggregatable report is a [=struct=] with the following items: :: An [=aggregation coordinator=] : context ID :: A [=string=] or null +: filtering ID max bytes +:: A positive integer : queued :: A [=boolean=] @@ -379,6 +392,8 @@ items:
: context ID (default: null) :: A [=string=] or null +: filtering ID max bytes (default: [=default filtering ID max bytes=]) +:: A positive integer
@@ -413,6 +428,18 @@ The user agent may expose controls that allow the user to delete data from the [=contribution cache=], the [=debug scope map=] and the [=pre-specified report parameters map=]. +Constants {#constants} +====================== + +Default filtering ID max bytes is a positive integer controlling the +max bytes used if none is explicitly chosen. Its value is 1. + +Valid filtering ID max bytes range is a [=set=] of positive integers +controlling the allowable values of max bytes. Its value is [=the inclusive +range|the range=] 1 to 8, inclusive. + +Issue: Consider adding more constants. + [=Implementation-defined=] values {#implementation-defined-values} ================================================================== @@ -510,12 +537,14 @@ To determine if a report should be sent deterministically given a steps. They return a [=boolean=]: 1. If |preSpecifiedParams|' [=pre-specified report parameters/context ID=] is not null, return true. +1. If |preSpecifiedParams|' [=pre-specified report parameters/filtering ID max + bytes=] is not the [=default filtering ID max bytes=], return true. 1. Return false. -Note: If a context ID was specified, a report is sent, even if there are no - contributions or there is insufficent budget for the requested - contributions. See [Protecting against leaks via the number of - reports](#protecting-against-leaks-via-the-number-of-reports). +Note: If a context ID or non-default filtering ID max bytes was specified, a + report is sent, even if there are no contributions or there is insufficent + budget for the requested contributions. See [Protecting against leaks via + the number of reports](#protecting-against-leaks-via-the-number-of-reports). To process contributions for a batching scope given a [=batching scope=] |batchingScope|, an [=origin=] |reportingOrigin|, a @@ -598,6 +627,10 @@ scope given a [=pre-specified report parameters=] |params| and a 1. Let |contextId| be |params|' [=pre-specified report parameters/context ID=]. 1. [=Assert=]: |contextId| is null or |contextId|'s [=string/length=] is not larger than 64. +1. Let |filteringIdMaxBytes| be |params|' [=pre-specified report parameters/ + filtering ID max bytes=]. +1. [=Assert=]: |filteringIdMaxBytes| is [=set/contained=] in the [=valid + filtering ID max bytes range=] 1. [=map/Set=] [=pre-specified report parameters map=][|batchingScope|] to |params|. @@ -681,6 +714,9 @@ perform the following steps. They return an [=aggregatable report=]. :: |aggregationCoordinator| : [=aggregatable report/context ID=] :: |preSpecifiedParams|' [=pre-specified report parameters/context ID=] + : [=aggregatable report/filtering ID max bytes=] + :: |preSpecifiedParams|' [=pre-specified report parameters/filtering ID max + bytes=] : [=aggregatable report/queued=] :: false 1. Return |report|. @@ -909,6 +945,8 @@ To obtain the plaintext payload given an [=aggregatable report=] :: 0 : {{PAHistogramContribution/value}} :: 0 + : {{PAHistogramContribution/filteringId}} + :: 0 1. [=list/Append=] |nullContribution| to |contributions|. Note: This padding protects against the number of contributions being leaked @@ -916,14 +954,23 @@ To obtain the plaintext payload given an [=aggregatable report=] [below](#protecting-against-leaks-via-payload-size). 1. [=list/iterate|For each=] |contribution| of |report|'s [=aggregatable report/ contributions=]: + 1. Let |filteringIdMaxBytes| be |report|'s [=aggregatable report/filtering + id max bytes=]. + 1. [=Assert=]: |contribution|["{{PAHistogramContribution/filteringId}}"] + is [=set/contained=] in [=the exclusive range|the range=] 0 to + 256|filteringIdMaxBytes|, exclusive. 1. Let |contributionData| be an [=ordered map=] of the following key/value pairs: : "`bucket`" :: The result of [=encoding an integer for the payload=] given - |contribution|["{{PAHistogramContribution/bucket}}"] and 128. + |contribution|["{{PAHistogramContribution/bucket}}"] and 16. : "`value`" :: The result of [=encoding an integer for the payload=] given - |contribution|["{{PAHistogramContribution/value}}"] and 32. + |contribution|["{{PAHistogramContribution/value}}"] and 4. + : "`id`" + :: The result of [=encoding an integer for the payload=] given + |contribution|[="{{PAHistogramContribution/filteringId}}"] and + |filteringIdMaxBytes|. 1. [=list/Append=] |contributionData| to |payloadData|. 1. Let |payload| be an [=ordered map=] of the following key/value pairs: : "`data`" @@ -953,9 +1000,9 @@ They return a [=byte sequence=] or an error. with |hpkeContext| and |aad|. To encode an integer for the payload given an integer |intToEncode| -and an integer |bitLength|, return the representation of |intToEncode| as a -big-endian [=byte sequence=] of length |bitLength| / 8, left padding with zeroes -as necessary. +and an integer |byteLength|, return the representation of |intToEncode| as a +big-endian [=byte sequence=] of length |byteLength|, left padding with zeroes as +necessary. To obtain a report's shared info given an [=aggregatable report=] |report|, perform the following steps. They return a [=string=]. @@ -973,7 +1020,7 @@ To obtain a report's shared info given an [=aggregatable report=] :: The number of seconds in |scheduledReportTime|, rounded down to the nearest number of whole seconds and [=serialize an integer|serialized=] : "`version`" - :: "`0.1`" + :: "`1.0`" 1. Return the result of [=serializing an infra value to a json string=] given |sharedInfo|. @@ -1038,6 +1085,7 @@ partial interface SharedStorageWorkletGlobalScope { dictionary SharedStoragePrivateAggregationConfig { USVString aggregationCoordinatorOrigin; USVString contextId; + [EnforceRange] unsigned long long filteringIdMaxBytes; }; partial dictionary SharedStorageRunOperationMethodOptions { @@ -1078,15 +1126,25 @@ steps. They return a [=pre-specified report parameters=], null, or a {{DOMException}}: 1. If |options|["{{SharedStorageRunOperationMethodOptions/privateAggregationConfig}}"] does not [=map/exist=], return null. +1. Let |privateAggregationConfig| be + |options|["{{SharedStorageRunOperationMethodOptions/privateAggregationConfig}}"]. 1. Let |contextId| be null. -1. If |options|["{{SharedStorageRunOperationMethodOptions/privateAggregationConfig}}"]["{{SharedStoragePrivateAggregationConfig/contextId}}"] +1. If |privateAggregationConfig|["{{SharedStoragePrivateAggregationConfig/contextId}}"] [=map/exists=], set |contextId| to - |options|["{{SharedStorageRunOperationMethodOptions/privateAggregationConfig}}"]["{{SharedStoragePrivateAggregationConfig/contextId}}"]. + |privateAggregationConfig|["{{SharedStoragePrivateAggregationConfig/contextId}}"]. 1. If |contextId|'s [=string/length=] is greater than 64, return a new {{DOMException}} with name "`DataError`". +1. Let |filteringIdMaxBytes| be the [=default filtering ID max bytes=]. +1. If |privateAggregationConfig|["{{SharedStoragePrivateAggregationConfig/filteringIdMaxBytes}}"] + [=map/exists=], set |filteringIdMaxBytes| to + |privateAggregationConfig|["{{SharedStoragePrivateAggregationConfig/filteringIdMaxBytes}}"]. +1. If |filteringIdMaxBytes| is not [=set/contained=] in the [=valid filtering ID + max bytes range=], return a new {{DOMException}} with name "`DataError`". 1. Return a new [=pre-specified report parameters=] with the items: : [=pre-specified report parameters/context ID=] :: |contextId| + : [=pre-specified report parameters/filtering ID max bytes=] + :: |filteringIdMaxBytes| The {{WindowSharedStorage}}'s {{WindowSharedStorage/run()}} method steps are modified in four ways. First, add the following steps just after step 2 ("If @@ -1282,6 +1340,7 @@ dictionary PASignalValue { dictionary PAExtendedHistogramContribution { required (PASignalValue or bigint) bucket; required (PASignalValue or long) value; + bigint filteringId = 0; }; [Exposed=InterestGroupScriptRunnerGlobalScope, SecureContext] @@ -1349,9 +1408,16 @@ event, PAExtendedHistogramContribution contribution) method steps are: throw=] a {{TypeError}}. 1. Otherwise, if |contribution|["{{PAHistogramContribution/value}}"] is negative, [=exception/throw=] a {{TypeError}}. +1. If |contribution|["{{PAExtendedHistogramContribution/filteringId}}"] is + not [=set/contained=] in [=the exclusive range|the range=] 0 to + 256[=default filtering ID max bytes=], exclusive, [=exception/ + throw=] a {{TypeError}}. Issue: Make the error types on validation issues here and above consistent with {{PrivateAggregation/contributeToHistogram(contribution)}}. + + Note: It is not currently possible to set a non-default filtering ID max + bytes for Protected Audience. 1. Let |batchingScope| be null. 1. If |event| [=string/starts with=] "`reserved.`", set |batchingScope| to the result of running |scopingDetails|' [=scoping details/get batching scope @@ -1974,7 +2040,10 @@ an auction config |auctionConfig| and a :: |bucket| : {{PAHistogramContribution/value}} :: |value| + : {{PAHistogramContribution/filteringId}} + :: 0 + Issue: Consider allowing the filtering ID to be set here. 1. [=map/For each=] |ig| of the [=user agent=]'s interest group set whose owner is @@ -2029,11 +2098,15 @@ following steps. They return a {{PAHistogramContribution}}. 1. Let |value| be |contribution|["{{PAExtendedHistogramContribution/value}}"]. 1. If |value| is a {{PASignalValue}}, set |value| to the result of [=filling in the signal value=] given |value|, 231−1 and |leadingBidInfo|. -1. Return a new {{PAHistogramContribution}} with the items: +1. Let |filledInContribution| be a new {{PAHistogramContribution}} with the + items: : {{PAHistogramContribution/bucket}} :: |bucket| : {{PAHistogramContribution/value}} :: |value| + : {{PAHistogramContribution/filteringId}} + :: |contribution|["{{PAExtendedHistogramContribution/filteringId}}"] +1. Return |filledInContribution|. To fill in the signal value given a {{PASignalValue}} |value|, an integer |maxAllowed| and a leading bid info @@ -2218,7 +2291,8 @@ However, the number of reports with the given metadata could expose some cross-site information. To protect against this, the API delays sending reports by a randomized amount of time to make it difficult to determine whether a report was sent or not from any particular event. In the case that a -[=aggregatable report/context ID=] is supplied, the API makes the number of +[=aggregatable report/context ID=] is supplied or a non-default [=aggregatable +report/filtering ID max bytes=] is specified, the API makes the number of reports sent deterministic (sending 'null reports' if necessary -- each containing only a contribution with a value of 0 in the payload). Additional mitigations may also be possible in the future, e.g. adding noise to the report