From ad05bb27783022945c299fb1cc4393ab415696db Mon Sep 17 00:00:00 2001 From: Russ Hamilton Date: Mon, 9 Dec 2024 19:05:51 +0000 Subject: [PATCH 1/3] Add support for k-anonymity enforcement --- draft-ietf-bidding-and-auction-services.md | 140 ++++++++++++++++++++- 1 file changed, 139 insertions(+), 1 deletion(-) diff --git a/draft-ietf-bidding-and-auction-services.md b/draft-ietf-bidding-and-auction-services.md index 4311fa8..9473a15 100644 --- a/draft-ietf-bidding-and-auction-services.md +++ b/draft-ietf-bidding-and-auction-services.md @@ -322,6 +322,7 @@ request = { * interestGroupOwner => bstr }, ? enableDebugReporting: bool + ? enforceKAnon: bool ; default false } ~~~~~ @@ -411,7 +412,8 @@ tuple. 1. Construct a request, `request` with `request["publisher"]` set to `publisher`, `request["version"]` set to 0, `request["generationId"]` set to a new [UUID] [Version 4](https://www.rfc-editor.org/rfc/rfc9562.html#section-5.4), - and `request["enableDebugReporting"]` set to true. + `request["enableDebugReporting"]` set to true, and `request["enforceKAnon"]` + set to true. 1. Set `current_size` to be the serialized size of the encrypted request created from `request` without padding. 1. Set `remaining_allocated_size` to 0. @@ -519,6 +521,9 @@ consume along with an HPKE context. 1. If `request["enableDebugReporting"]` is not a boolean, return failure. 1. Set `processed request["enableDebugReporting"]` to `request["enableDebugReporting"]`. +1. If `request["enforceKAnon"]` exists: + 1. If `request["enforceKAnon"]` is not a boolean, return failure. + 1. Set `processed request["enforceKAnon"]` to `request["enforceKAnon"]`. 1. If `request["interestGroups]` does not exist or is not a map, return failure. 1. Set `processed request["interestGroups"]` to an empty map. 1. For each `key`, `value` map entry of `request["interestGroups"]`: @@ -818,6 +823,10 @@ response = { ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-top-level-seller. ; If not present, map as Null. ? topLevelSeller: origin, + + ; Fields used for k-anonymity enforcement + ? kAnonWinnerJoinCandidates: KAnonJoinCandidate, + ? kAnonGhostWinners: [1* KAnonGhostWinner] } ; Defines the structure for reporting URLs. @@ -830,6 +839,34 @@ reportingUrls = { ? interactionReportingUrls: { * tstr => tstr } } +; Defines k-anonymity keys to increment the count for after the auction is over. +KAnonJoinCandidate = { + adRenderURLHash: bstr, ; sha256 hash + ? adComponentRenderURLsHash: [1* bstr], ; sha256 hash + reportingIdHash: bstr ; sha256 hash +} + +; Describes details about the auction's non-k-anonymous winner. +KAnonGhostWinner = { + kAnonJoinCandidates: KAnonJoinCandidate, + interestGroupIndex: int, ; index into groups that were sent in the request + owner: tstr, ; origin + ? ghostWinnerForTopLevelAuction: GhostWinnerForTopLevelAuction, +} + +; Additional details from the non-k-anonymous winner in case this is a +; component auction. +GhostWinnerForTopLevelAuction = { + adRenderURL: tstr, ; URL + ? adComponentRenderURLs: [* tstr], ; URLs + modifiedBid: float, + ? bidCurrency: tstr, ; 3 character ISO 4217 currency code + ? adMetadata: tstr, + ? buyerReportingId: tstr, + ? buyerAndSellerReportingId: tstr, + ? selectedBuyerAndSellerReportingId: tstr, +} + ~~~~~ ### Generating a Response {#response-generate} @@ -979,6 +1016,13 @@ response from Bidding and Auction Services. It takes as input the 1. If `response["buyerAndSellerReportingId"]` exists and is a string set `processed response["buyer and seller reporting id"]` to `response["buyerAndSellerReportingId"]`. +1. If `response["kAnonWinnerJoinCandidates"]` exists and is a map: + 1. Set `processed response["winner join candidates"]` to the result of + {{response-parsing-kanon-join-candidates}} on `response["kAnonWinnerJoinCandidates"]`. +1. If `response["kAnonGhostWinners"]` exists and is an array and has at least 1 element: + 1. Set `processed response["ghost winner"]` to the result of + {{response-parsing-ghost-winner}} on `response["kAnonGhostWinners"][0]` and + `request context`'s `included_groups`. 1. Return `processed response`. #### Parsing reporting URLs {#response-parsing-reporting} @@ -1001,6 +1045,100 @@ To parse reporting URLs on a [CBOR] map `reporting URLs` with a schema like 1. Set `processed reporting URLs["beacon urls"][key]` to `reporting URL`. 1. Return `processed reporting URLs`. +#### Parsing k-Anonymity Join Candidates {#response-parsing-kanon-join-candidates} + +To parse k-Anonymity Join Candidates on a [CBOR] map `candidates` with a schema +like `KAnonJoinCandidate` from {{response-message}}: + +1. Let `winner join candidates` be a new structure analogous to + [server auction join candidates](https://wicg.github.io/turtledove/#server-auction-join-candidates). +1. If `candidates["adRenderURLHash"]` does not exist or is not a byte string, return null. +1. Set `winner join candidates["ad render url hash"]` to `candidates["adRenderURLHash"]`. +1. If `candidates["adComponentRenderURLsHash"]` exists: + 1. If `candidates["adComponentRenderURLsHash"]` is not an array, return null. + 1. For each `component` in `candidates["adComponentRenderURLsHash"]`: + 1. If `component` is not a byte string, return null. + 1. Append `component` to `winner join candidates["ad component render url hashes"]`. +1. If `candidates["reportingIdHash"]` does not exist or is not a byte string, return null. +1. Set `winner join candidates["reporting id hash"]` to `candidates["reportingIdHash"]`. +1. Return `winner join candidates`. + +#### Parsing a Ghost Winner {#response-parsing-ghost-winner} + +To parse a Ghost Winner on a [CBOR] map `ghost winner` with a schema like +`KAnonGhostWinner` from {{response-message}} and `included_groups` map included +in the tuple returned from {{request-generate}}: + +1. Let `result` be a new structure analogous to + [server auction ghost winner](https://wicg.github.io/turtledove/#server-auction-ghost-winner). +1. If `ghost winner["kAnonJoinCandidates"]` does not exist or is not a map return null. +1. Let `candidates` be the result of {{response-parsing-kanon-join-candidates}} on + `ghost winner["kAnonJoinCandidates"]`. +1. If `candidates` is null, return null. +1. Set `result["candidates"]` to `candidates`. +1. If `ghost winner["owner"]` does not exist or is not a string, return null. +1. Let `owner` be equal to `ghost winner["owner"]` parsed as an [ORIGIN], + returning null if there is an error. +1. Set `result["interest group owner"]` to `owner`. +1. If `included_groups` does not contain `owner` as a key, return null. +1. If `ghost winner["interestGroupIndex"]` does not exist or is not an integer, return null. +1. Let `index` be equal to `ghost winner["interestGroupIndex"]`. +1. If `index < 0` or `index` is greater than or equal to the length of + `included_groups[owner]`, return null. +1. Let `name` be the `interest group name` for `included_groups[owner][element]`. +1. Set `result["interest group name"]` to `name`. +1. If `ghost winner["ghostWinnerForTopLevelAuction"]` exists: + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]` is not a map, return null. + 1. Let `result["ghost winner bid info"]` be a new structure analogous to + [server auction ghost winner bid info](https://wicg.github.io/turtledove/#server-auction-ghost-winner-bid-info). + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adRenderURL"]` does not + exist, return null. + 1. Set `result["ghost winner bid info"]["ad render url"]` to + `ghost winner["ghostWinnerForTopLevelAuction"]["adRenderURL"]` parsed as a + [URL], returning null if there is an error. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adComponentRenderURLs"]` exists: + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adComponentRenderURLs"]` + is not an array, return null. + 1. For each `component` in `ghost winner["ghostWinnerForTopLevelAuction"]["adComponentRenderURLs"]`: + 1. Append `component` parsed as a [URL] to + `result["ghost winner bid info"]["ad components"]`, returning null if + there is an error. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["modifiedBid"]` does not + exist or is not a floating point number, return null. + 1. Let `bid` be a new structure analogous to + [bid with currency](https://wicg.github.io/turtledove/#bid-with-currency). + 1. Set `bid`s `value` field to `ghost winner["ghostWinnerForTopLevelAuction"]["modifiedBid"]`. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["bidCurrency"]` exists: + 1. If ``ghost winner["ghostWinnerForTopLevelAuction"]["bidCurrency"]` is not + a string, return null. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["bidCurrency"]` is not + 3 bytes long or contains characters other than upper case ASCII letters, + return null. + 1. Set `bid`'s `currency` field to + `ghost winner["ghostWinnerForTopLevelAuction"]["bidCurrency"]`. + 1. Set `result["ghost winner bid info"]["modified bid"]` to `bid`. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adMetadata"]` exists: + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adMetadata"]` is not a + string, return null. + 1. Set `result["ghost winner bid info"]["ad metadata"]` to + `ghost winner["ghostWinnerForTopLevelAuction"]["adMetadata"]`. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["buyerReportingId"]` exists: + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["buyerReportingId"]` is not a + string, return null. + 1. Set `result["ghost winner bid info"]["buyer reporting id"]` to + `ghost winner["ghostWinnerForTopLevelAuction"]["buyerReportingId"]`. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["buyerAndSellerReportingId"]` exists: + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["buyerAndSellerReportingId"]` is not a + string, return null. + 1. Set `result["ghost winner bid info"]["buyer and seller reporting id"]` to + `ghost winner["ghostWinnerForTopLevelAuction"]["buyerAndSellerReportingId"]`. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["selectedBuyerAndSellerReportingId"]` exists: + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["selectedBuyerAndSellerReportingId"]` is not a + string, return null. + 1. Set `result["ghost winner bid info"]["selected buyer and seller reporting id"]` to + `ghost winner["ghostWinnerForTopLevelAuction"]["selectedBuyerAndSellerReportingId"]`. +1. Return `result` + # Security Considerations TODO From 5e728011e6a9fe116480cbd3e940899221ecb8fe Mon Sep 17 00:00:00 2001 From: Russ Hamilton Date: Mon, 16 Dec 2024 16:01:16 +0000 Subject: [PATCH 2/3] Address comment --- draft-ietf-bidding-and-auction-services.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/draft-ietf-bidding-and-auction-services.md b/draft-ietf-bidding-and-auction-services.md index 9473a15..a8c385b 100644 --- a/draft-ietf-bidding-and-auction-services.md +++ b/draft-ietf-bidding-and-auction-services.md @@ -1097,12 +1097,12 @@ in the tuple returned from {{request-generate}}: `ghost winner["ghostWinnerForTopLevelAuction"]["adRenderURL"]` parsed as a [URL], returning null if there is an error. 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adComponentRenderURLs"]` exists: - 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adComponentRenderURLs"]` - is not an array, return null. - 1. For each `component` in `ghost winner["ghostWinnerForTopLevelAuction"]["adComponentRenderURLs"]`: - 1. Append `component` parsed as a [URL] to - `result["ghost winner bid info"]["ad components"]`, returning null if - there is an error. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adComponentRenderURLs"]` + is not an array, return null. + 1. For each `component` in `ghost winner["ghostWinnerForTopLevelAuction"]["adComponentRenderURLs"]`: + 1. Append `component` parsed as a [URL] to + `result["ghost winner bid info"]["ad components"]`, returning null if + there is an error. 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["modifiedBid"]` does not exist or is not a floating point number, return null. 1. Let `bid` be a new structure analogous to From fbba49300a399a94d3bbb2c3c3205f5e27df0e81 Mon Sep 17 00:00:00 2001 From: Russ Hamilton Date: Wed, 18 Dec 2024 17:00:25 +0000 Subject: [PATCH 3/3] Fix formatting --- draft-ietf-bidding-and-auction-services.md | 262 ++++++++++----------- 1 file changed, 131 insertions(+), 131 deletions(-) diff --git a/draft-ietf-bidding-and-auction-services.md b/draft-ietf-bidding-and-auction-services.md index a8c385b..cb56cb7 100644 --- a/draft-ietf-bidding-and-auction-services.md +++ b/draft-ietf-bidding-and-auction-services.md @@ -933,11 +933,11 @@ response from Bidding and Auction Services. It takes as input the 1. Set `processed response["ad render url"]` to `response["adRenderURL"]` parsed as a [URL], returning failure if there is an error. 1. If `response["components"]` exists: - 1. If `response["components"]` is not an array, return failure. - 1. For each `component` in `response["components"]`: - 1. Append `component` parsed as a [URL] to - `processed response["ad components"]`, returning failure if there is an - error. + 1. If `response["components"]` is not an array, return failure. + 1. For each `component` in `response["components"]`: + 1. Append `component` parsed as a [URL] to + `processed response["ad components"]`, returning failure if + there is an error. 1. If `response["interestGroupName"]` does not exist or is not a string, return failure. 1. Set `processed response["interest group name"]` to `response["interestGroupName"]`. 1. If `response["interestGroupOwner"]` does not exist or is not a string, return failure. @@ -945,70 +945,70 @@ response from Bidding and Auction Services. It takes as input the parsed as an [ORIGIN], returning failure if there is an error. 1. If `response["biddingGroups"]` does not exist or is not a map, return failure. 1. For each `key`, `value` in `response["biddingGroups"]`: - 1. Let `owner` be equal to `key` parsed as an [ORIGIN], returning failure if - there is an error. - 1. `request context`'s `included_groups` does not contain `owner` as a key, return failure. - 1. If `value` is not a list, return failure. - 1. For each `element` in `value`: - 1. If `element` is not an integer or `element < 0`, return failure. - 1. If `element` is greater than or equal to the length of - `included_groups[owner]`, return failure. - 1. Let `name` be the `interest group name` for `included_groups[owner][element]`. - 1. Append the tuple (`owner`, `name`) to `processed response["bidding groups"]`. -1. If `response["updateGroups"]` exists and is a map: - 1. For each `key`, `value` in `response["updateGroups"]`: - 1. Let `owner` be equal to `key` parsed as an [ORIGIN], continuing the next - iteration of this loop if there is an error. - 1. If `request context`'s `included_groups` does not contain `owner` as a - key, continue the next iteration of this loop. - 1. If `value` is not a list, return failure. - 1. For each `element` in `value`: - 1. If `element` is not a map, continue the next iteration of this loop. - 1. If `element["index"]` does not exist or is not an integer or - `element["updateIfOlderThanMs"]` does not exist or is not an integer, continue the - next iteration of this loop. - 1. If `element["index"]` is not an integer or `element["index"] < 0`, - continue the next iteration of this loop. - 1. If `element["index"]` is greater than or equal to the length of - `included_groups[owner]`, continue the next iteration of this loop. + 1. Let `owner` be equal to `key` parsed as an [ORIGIN], returning failure if + there is an error. + 1. `request context`'s `included_groups` does not contain `owner` as a key, return failure. + 1. If `value` is not a list, return failure. + 1. For each `element` in `value`: + 1. If `element` is not an integer or `element < 0`, return failure. + 1. If `element` is greater than or equal to the length of + `included_groups[owner]`, return failure. 1. Let `name` be the `interest group name` for `included_groups[owner][element]`. - 1. Let `interest group key` be the tuple (`owner`, `name`). - 1. Let `update duration` be `element["updateIfOlderThanMs"]`, parsed into a time - duration as integer milliseconds. - 1. Set `processed response["update groups"][intereset group key]` to - `update duration`. + 1. Append the tuple (`owner`, `name`) to `processed response["bidding groups"]`. +1. If `response["updateGroups"]` exists and is a map: + 1. For each `key`, `value` in `response["updateGroups"]`: + 1. Let `owner` be equal to `key` parsed as an [ORIGIN], continuing the next + iteration of this loop if there is an error. + 1. If `request context`'s `included_groups` does not contain `owner` as a + key, continue the next iteration of this loop. + 1. If `value` is not a list, return failure. + 1. For each `element` in `value`: + 1. If `element` is not a map, continue the next iteration of this loop. + 1. If `element["index"]` does not exist or is not an integer or + `element["updateIfOlderThanMs"]` does not exist or is not an integer, continue the + next iteration of this loop. + 1. If `element["index"]` is not an integer or `element["index"] < 0`, + continue the next iteration of this loop. + 1. If `element["index"]` is greater than or equal to the length of + `included_groups[owner]`, continue the next iteration of this loop. + 1. Let `name` be the `interest group name` for `included_groups[owner][element]`. + 1. Let `interest group key` be the tuple (`owner`, `name`). + 1. Let `update duration` be `element["updateIfOlderThanMs"]`, parsed into a time + duration as integer milliseconds. + 1. Set `processed response["update groups"][intereset group key]` to + `update duration`. 1. If `response["score"]` exists: - 1. If `response["score"]` is not a floating point value, return failure. - 1. Set `processed response["score"]` to `response["score"]`. + 1. If `response["score"]` is not a floating point value, return failure. + 1. Set `processed response["score"]` to `response["score"]`. 1. If `response["bid"]` exists: - 1. If `response["bid"]` is not a floating point value, return failure. - 1. Let `bid` be a new structure analogous to [bid with currency](https://wicg.github.io/turtledove/#bid-with-currency). - 1. Set `bid`s `value` field to `response["bid"]`. - 1. If `response["bidCurrency"]` exists: - 1. If `response["bidCurrency"]` is not a string, return failure. - 1. If `response["bidCUrrency"]` is not 3 bytes long or contains characters - other than upper case ASCII letters, return failure. - 1. Set `bid`'s `currency` field to `response["bidCurrency"]`. - 1. Set `processed response["bid"]` to `bid`. + 1. If `response["bid"]` is not a floating point value, return failure. + 1. Let `bid` be a new structure analogous to [bid with currency](https://wicg.github.io/turtledove/#bid-with-currency). + 1. Set `bid`s `value` field to `response["bid"]`. + 1. If `response["bidCurrency"]` exists: + 1. If `response["bidCurrency"]` is not a string, return failure. + 1. If `response["bidCUrrency"]` is not 3 bytes long or contains characters + other than upper case ASCII letters, return failure. + 1. Set `bid`'s `currency` field to `response["bidCurrency"]`. + 1. Set `processed response["bid"]` to `bid`. 1. If `response["winReportingURLs"]` exists and is a map: - 1. If `response["winReportingURLs"]["buyerReportingURLs"]` exists: - 1. Let `buyer reporting` be the result of {{response-parsing-reporting}} on - `response["winReportingURLs"]["buyerReportingURLs"]`. - 1. Set `processed response["buyer reporting"]` to `buyer reporting`. - 1. If `response["winReportingURLs"]["topLevelSellerReportingURLs"]` exists: - 1. Let `top level seller reporting` be the result of {{response-parsing-reporting}} - on `response["winReportingURLs"]["topLevelSellerReportingURLs"]`. - 1. Set `processed response["top level seller reporting"]` to - `top level seller reporting`. - 1. If `response["winReportingURLs"]["componentSellerReportingURLs"]` exists: - 1. Let `component seller reporting` be the result of {{response-parsing-reporting}} - on `response["winReportingURLs"]["componentSellerReportingURLs"]`. - 1. Set `processed response["component seller reporting"` to - `component seller reporting`. + 1. If `response["winReportingURLs"]["buyerReportingURLs"]` exists: + 1. Let `buyer reporting` be the result of {{response-parsing-reporting}} on + `response["winReportingURLs"]["buyerReportingURLs"]`. + 1. Set `processed response["buyer reporting"]` to `buyer reporting`. + 1. If `response["winReportingURLs"]["topLevelSellerReportingURLs"]` exists: + 1. Let `top level seller reporting` be the result of {{response-parsing-reporting}} + on `response["winReportingURLs"]["topLevelSellerReportingURLs"]`. + 1. Set `processed response["top level seller reporting"]` to + `top level seller reporting`. + 1. If `response["winReportingURLs"]["componentSellerReportingURLs"]` exists: + 1. Let `component seller reporting` be the result of {{response-parsing-reporting}} + on `response["winReportingURLs"]["componentSellerReportingURLs"]`. + 1. Set `processed response["component seller reporting"` to + `component seller reporting`. 1. If `response["topLevelSeller"]` exists: - 1. If `response["topLevelSeller"]` is not a string, return failure. - 1. Set `processed response["top level seller"]` to `response["topLevelSeller"]` - parsed as a [URL], returning failure if there is an error. + 1. If `response["topLevelSeller"]` is not a string, return failure. + 1. Set `processed response["top level seller"]` to `response["topLevelSeller"]` + parsed as a [URL], returning failure if there is an error. 1. If `response["adMetadata"]` exists and is a string set `processed response["ad metadata"]` to `response["adMetadata"]`. 1. If `response["buyerReportingId"]` exists and is a string set @@ -1017,12 +1017,12 @@ response from Bidding and Auction Services. It takes as input the `processed response["buyer and seller reporting id"]` to `response["buyerAndSellerReportingId"]`. 1. If `response["kAnonWinnerJoinCandidates"]` exists and is a map: - 1. Set `processed response["winner join candidates"]` to the result of - {{response-parsing-kanon-join-candidates}} on `response["kAnonWinnerJoinCandidates"]`. + 1. Set `processed response["winner join candidates"]` to the result of + {{response-parsing-kanon-join-candidates}} on `response["kAnonWinnerJoinCandidates"]`. 1. If `response["kAnonGhostWinners"]` exists and is an array and has at least 1 element: - 1. Set `processed response["ghost winner"]` to the result of - {{response-parsing-ghost-winner}} on `response["kAnonGhostWinners"][0]` and - `request context`'s `included_groups`. + 1. Set `processed response["ghost winner"]` to the result of + {{response-parsing-ghost-winner}} on `response["kAnonGhostWinners"][0]` and + `request context`'s `included_groups`. 1. Return `processed response`. #### Parsing reporting URLs {#response-parsing-reporting} @@ -1033,16 +1033,16 @@ To parse reporting URLs on a [CBOR] map `reporting URLs` with a schema like 1. Let `processed reporting URLs` be a new structure analogous to [server auction reporting info](https://wicg.github.io/turtledove/#server-auction-reporting-info). 1. If `reporting URLs["reportingURL"]` exists and is a string: - 1. Let `reporting URL` be `reporting URLs["reportingURL"]` parsed as a [URL], - or null if there is an error. - 1. If `reporting URL` is not null, set - `processed reporting URLs["reporting url"]` to `reporting URL`. + 1. Let `reporting URL` be `reporting URLs["reportingURL"]` parsed as a [URL], + or null if there is an error. + 1. If `reporting URL` is not null, set + `processed reporting URLs["reporting url"]` to `reporting URL`. 1. If `reporting URLs["interactionReportingURLs"]` exists and is a map: - 1. For each `key`, `value` in `reporting URLs["interactionReportingURLs"]`: - 1. If `key` is not a string, continue with the next iteration. - 1. Let `reporting URL` be `value` parsed as a [URL]. If there is an error, - continue with the next iteration. - 1. Set `processed reporting URLs["beacon urls"][key]` to `reporting URL`. + 1. For each `key`, `value` in `reporting URLs["interactionReportingURLs"]`: + 1. If `key` is not a string, continue with the next iteration. + 1. Let `reporting URL` be `value` parsed as a [URL]. If there is an error, + continue with the next iteration. + 1. Set `processed reporting URLs["beacon urls"][key]` to `reporting URL`. 1. Return `processed reporting URLs`. #### Parsing k-Anonymity Join Candidates {#response-parsing-kanon-join-candidates} @@ -1055,10 +1055,10 @@ like `KAnonJoinCandidate` from {{response-message}}: 1. If `candidates["adRenderURLHash"]` does not exist or is not a byte string, return null. 1. Set `winner join candidates["ad render url hash"]` to `candidates["adRenderURLHash"]`. 1. If `candidates["adComponentRenderURLsHash"]` exists: - 1. If `candidates["adComponentRenderURLsHash"]` is not an array, return null. - 1. For each `component` in `candidates["adComponentRenderURLsHash"]`: - 1. If `component` is not a byte string, return null. - 1. Append `component` to `winner join candidates["ad component render url hashes"]`. + 1. If `candidates["adComponentRenderURLsHash"]` is not an array, return null. + 1. For each `component` in `candidates["adComponentRenderURLsHash"]`: + 1. If `component` is not a byte string, return null. + 1. Append `component` to `winner join candidates["ad component render url hashes"]`. 1. If `candidates["reportingIdHash"]` does not exist or is not a byte string, return null. 1. Set `winner join candidates["reporting id hash"]` to `candidates["reportingIdHash"]`. 1. Return `winner join candidates`. @@ -1088,55 +1088,55 @@ in the tuple returned from {{request-generate}}: 1. Let `name` be the `interest group name` for `included_groups[owner][element]`. 1. Set `result["interest group name"]` to `name`. 1. If `ghost winner["ghostWinnerForTopLevelAuction"]` exists: - 1. If `ghost winner["ghostWinnerForTopLevelAuction"]` is not a map, return null. - 1. Let `result["ghost winner bid info"]` be a new structure analogous to - [server auction ghost winner bid info](https://wicg.github.io/turtledove/#server-auction-ghost-winner-bid-info). - 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adRenderURL"]` does not - exist, return null. - 1. Set `result["ghost winner bid info"]["ad render url"]` to - `ghost winner["ghostWinnerForTopLevelAuction"]["adRenderURL"]` parsed as a - [URL], returning null if there is an error. - 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adComponentRenderURLs"]` exists: - 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adComponentRenderURLs"]` - is not an array, return null. - 1. For each `component` in `ghost winner["ghostWinnerForTopLevelAuction"]["adComponentRenderURLs"]`: - 1. Append `component` parsed as a [URL] to - `result["ghost winner bid info"]["ad components"]`, returning null if - there is an error. - 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["modifiedBid"]` does not - exist or is not a floating point number, return null. - 1. Let `bid` be a new structure analogous to - [bid with currency](https://wicg.github.io/turtledove/#bid-with-currency). - 1. Set `bid`s `value` field to `ghost winner["ghostWinnerForTopLevelAuction"]["modifiedBid"]`. - 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["bidCurrency"]` exists: - 1. If ``ghost winner["ghostWinnerForTopLevelAuction"]["bidCurrency"]` is not - a string, return null. - 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["bidCurrency"]` is not - 3 bytes long or contains characters other than upper case ASCII letters, - return null. - 1. Set `bid`'s `currency` field to - `ghost winner["ghostWinnerForTopLevelAuction"]["bidCurrency"]`. - 1. Set `result["ghost winner bid info"]["modified bid"]` to `bid`. - 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adMetadata"]` exists: - 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adMetadata"]` is not a - string, return null. - 1. Set `result["ghost winner bid info"]["ad metadata"]` to - `ghost winner["ghostWinnerForTopLevelAuction"]["adMetadata"]`. - 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["buyerReportingId"]` exists: - 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["buyerReportingId"]` is not a - string, return null. - 1. Set `result["ghost winner bid info"]["buyer reporting id"]` to - `ghost winner["ghostWinnerForTopLevelAuction"]["buyerReportingId"]`. - 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["buyerAndSellerReportingId"]` exists: - 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["buyerAndSellerReportingId"]` is not a - string, return null. - 1. Set `result["ghost winner bid info"]["buyer and seller reporting id"]` to - `ghost winner["ghostWinnerForTopLevelAuction"]["buyerAndSellerReportingId"]`. - 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["selectedBuyerAndSellerReportingId"]` exists: - 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["selectedBuyerAndSellerReportingId"]` is not a - string, return null. - 1. Set `result["ghost winner bid info"]["selected buyer and seller reporting id"]` to - `ghost winner["ghostWinnerForTopLevelAuction"]["selectedBuyerAndSellerReportingId"]`. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]` is not a map, return null. + 1. Let `result["ghost winner bid info"]` be a new structure analogous to + [server auction ghost winner bid info](https://wicg.github.io/turtledove/#server-auction-ghost-winner-bid-info). + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adRenderURL"]` does not + exist, return null. + 1. Set `result["ghost winner bid info"]["ad render url"]` to + `ghost winner["ghostWinnerForTopLevelAuction"]["adRenderURL"]` parsed as a + [URL], returning null if there is an error. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adComponentRenderURLs"]` exists: + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adComponentRenderURLs"]` + is not an array, return null. + 1. For each `component` in `ghost winner["ghostWinnerForTopLevelAuction"]["adComponentRenderURLs"]`: + 1. Append `component` parsed as a [URL] to + `result["ghost winner bid info"]["ad components"]`, returning null if + there is an error. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["modifiedBid"]` does not + exist or is not a floating point number, return null. + 1. Let `bid` be a new structure analogous to + [bid with currency](https://wicg.github.io/turtledove/#bid-with-currency). + 1. Set `bid`s `value` field to `ghost winner["ghostWinnerForTopLevelAuction"]["modifiedBid"]`. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["bidCurrency"]` exists: + 1. If ``ghost winner["ghostWinnerForTopLevelAuction"]["bidCurrency"]` is not + a string, return null. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["bidCurrency"]` is not + 3 bytes long or contains characters other than upper case ASCII letters, + return null. + 1. Set `bid`'s `currency` field to + `ghost winner["ghostWinnerForTopLevelAuction"]["bidCurrency"]`. + 1. Set `result["ghost winner bid info"]["modified bid"]` to `bid`. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adMetadata"]` exists: + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["adMetadata"]` is not a + string, return null. + 1. Set `result["ghost winner bid info"]["ad metadata"]` to + `ghost winner["ghostWinnerForTopLevelAuction"]["adMetadata"]`. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["buyerReportingId"]` exists: + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["buyerReportingId"]` is not a + string, return null. + 1. Set `result["ghost winner bid info"]["buyer reporting id"]` to + `ghost winner["ghostWinnerForTopLevelAuction"]["buyerReportingId"]`. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["buyerAndSellerReportingId"]` exists: + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["buyerAndSellerReportingId"]` is not a + string, return null. + 1. Set `result["ghost winner bid info"]["buyer and seller reporting id"]` to + `ghost winner["ghostWinnerForTopLevelAuction"]["buyerAndSellerReportingId"]`. + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["selectedBuyerAndSellerReportingId"]` exists: + 1. If `ghost winner["ghostWinnerForTopLevelAuction"]["selectedBuyerAndSellerReportingId"]` is not a + string, return null. + 1. Set `result["ghost winner bid info"]["selected buyer and seller reporting id"]` to + `ghost winner["ghostWinnerForTopLevelAuction"]["selectedBuyerAndSellerReportingId"]`. 1. Return `result` # Security Considerations