Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for k-anonymity enforcement #16

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 139 additions & 1 deletion draft-ietf-bidding-and-auction-services.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ request = {
* interestGroupOwner => bstr
},
? enableDebugReporting: bool
? enforceKAnon: bool ; default false
}
~~~~~

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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"]`:
Expand Down Expand Up @@ -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.
Expand All @@ -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}
Expand Down Expand Up @@ -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}
Expand All @@ -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"]`
brusshamilton marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down
Loading