From eb2bbcce0da62c1c2879ee94ea7429c111a52892 Mon Sep 17 00:00:00 2001 From: ID Bot Date: Fri, 3 Jan 2025 21:08:33 +0000 Subject: [PATCH] Script updating gh-pages from 8899ea6. [ci skip] --- ...aft-ietf-bidding-and-auction-services.html | 3355 +++++++++++++++++ ...raft-ietf-bidding-and-auction-services.txt | 1720 +++++++++ UpdateNonce/index.html | 45 + index.html | 8 + 4 files changed, 5128 insertions(+) create mode 100644 UpdateNonce/draft-ietf-bidding-and-auction-services.html create mode 100644 UpdateNonce/draft-ietf-bidding-and-auction-services.txt create mode 100644 UpdateNonce/index.html diff --git a/UpdateNonce/draft-ietf-bidding-and-auction-services.html b/UpdateNonce/draft-ietf-bidding-and-auction-services.html new file mode 100644 index 0000000..d26660b --- /dev/null +++ b/UpdateNonce/draft-ietf-bidding-and-auction-services.html @@ -0,0 +1,3355 @@ + + + + + + +Bidding and Auction Services + + + + + + + + + + + + + + + + + + + + + + + + +
Internet-DraftBidding and Auction ServicesJanuary 2025
Kocoj & HamiltonExpires 7 July 2025[Page]
+
+
+
+
Workgroup:
+
TBD
+
Internet-Draft:
+
draft-ietf-bidding-and-auction-services-latest
+
Published:
+
+ +
+
Intended Status:
+
Standards Track
+
Expires:
+
+
Authors:
+
+
+
D. Kocoj
+
Google
+
+
+
B. R. Hamilton
+
Google
+
+
+
+
+

Bidding and Auction Services

+
+

Abstract

+

The Bidding and Auction Services provide a way for advertising auctions to execute in +remote environments while preserving user privacy.

+
+
+

+Discussion Venues +

+

This note is to be removed before publishing as an RFC.

+

Source for this draft and an issue tracker can be found at + https://github.com/privacysandbox/draft-ietf-bidding-and-auction-services.

+
+
+
+

+Status of This Memo +

+

+ This Internet-Draft is submitted in full conformance with the + provisions of BCP 78 and BCP 79.

+

+ Internet-Drafts are working documents of the Internet Engineering Task + Force (IETF). Note that other groups may also distribute working + documents as Internet-Drafts. The list of current Internet-Drafts is + at https://datatracker.ietf.org/drafts/current/.

+

+ Internet-Drafts are draft documents valid for a maximum of six months + and may be updated, replaced, or obsoleted by other documents at any + time. It is inappropriate to use Internet-Drafts as reference + material or to cite them other than as "work in progress."

+

+ This Internet-Draft will expire on 7 July 2025.

+
+
+ +
+
+

+Table of Contents +

+ +
+
+
+
+

+1. Introduction +

+

Today, real-time bidding and ad auctions are executed on servers that may not +provide technical guarantees of security. Some users have concerns about how +their data is handled to generate relevant ads and in how that data is shared. +Protected Audience API (Android, +Chrome) provides +ways to preserve privacy and limit third-party data sharing by serving +personalized ads based on previous mobile app or web engagement.

+

This Bidding and Auction Services proposal outlines a way to allow Protected Audience +computation to take place on cloud servers in a +Trusted Execution Environment (TEE), +rather than running locally on a user's device. Running workloads +in a TEE in cloud has the following benefits:

+
    +
  • +

    Scalable ad auctions.

    +
      +
    • +

      A scalable ad auction may include several buyers and sellers and that +can demand more compute resources and network bandwidth.

      +
    • +
    +
  • +
  • +

    Lower latency of ad auctions.

    +
      +
    • +

      Server to server communication on the cloud is faster than multiple +device to server calls.

      +
    • +
    • +

      Adtech code can execute faster on servers with higher computing power.

      +
    • +
    +
  • +
  • +

    Higher utility of ad auctions.

    +
      +
    • +

      Servers have better processing power, therefore adtechs can run compute +intensive workloads on a server for better utility.

      +
    • +
    • +

      Lower latency of ad auctions also positively impact utility.

      +
    • +
    +
  • +
  • +

    Security protection

    +
      +
    • +

      TEEs can protect confidentiality of adtech code and signals.

      +
    • +
    +
  • +
  • +

    System health of the user's device.

    +
      +
    • +

      Ensure better system health of user's device by freeing up computational +cycles and network bandwidth.

      +
    • +
    +
  • +
+

Standardized protocols for interacting with Bidding and Auction Services are +essential to creating a diverse and healthy ecosystem for such services.

+
+
+

+1.1. Scope +

+

This document provides a specification for the request and response message +format that a client can use to communicate with remote services +that allows the client to offload much of the work involved in running an advertisement +selection auction as part of the client's implementation of the +Protected Audience API.

+

This document does not describe distribution of private keys to the Bidding +and Auction services.

+
+
+
+
+

+1.2. Terminology +

+

The key word "client" is to be interpreted as an implementation of this +document that creates Requests (Section 2.2) and consumes Responses +(Section 2.3). The key phrase "Bidding and Auction Services" is +to be interpreted as an implementation of this document that consumes +Requests and creates Responses.

+

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL +NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", +"MAY", and "OPTIONAL" in this document are to be interpreted as +described in BCP 14 [RFC2119] [RFC8174] when, and only when, they +appear in all capitals, as shown here.

+
+
+
+
+
+
+

+2. Message Format Specifications +

+
+
+

+2.1. Overview +

+

To understand this document, it is important to know that the +communication between the client and the remote services uses a +request-response message exchange pattern. The request will first reach a seller service, after which +the seller will forward parts of the request to buyer service. It is then up to the +seller service to gather buyer responses and form a final response for the client. More detail +about the seller and buyer services can be found in the server-side system design documentation.

+
+
+

+2.1.1. Common Definitions +

+

Section 2 makes frequent use of the following definitions.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 1
Term with CDDL DefinitionDetailed Reference
+ json = tstr + + [JSON] +
+ uuid = tstr .regexp "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + + [UUID] +
+ origin = tstr .regexp "https://([^/:](:[0-9]+)?/" + + [ORIGIN] +
+ currency = tstr .size 3 .regexp /^[A-Z]{3}$/ + + [ISO4217] +
+ adRenderUrl = tstr + + [URL] +
+ adRenderId = tstr + + [ADRENDERID] +
+ interestGroupOwner = origin + + [IGOWNER] +
+
+
+
+
+

+2.1.2. Message Framing and Padding +

+

Bidding and Auction Services requests and responses have the following framing:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 2
Byte001 to 45 to Size+4Size+5 to end
Bits7-54-0***
ContentsVersionCompressionSizeRequest PayloadPadding
+

where the the first 3 bits of the frame header specify the payload +version and the following 5 bits specify the compression algorithm. +The format described in this document corresponds to version 0.

+

The compression method's value in bits 4-0 in Section 2.2.2 +corresponds to the below table:

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 3
CompressionDescription
0No Compression
1Brotli [RFC7932] +
2GZIP [RFC1952] +
3-31Reserved
+

The amount of padding depends on the type of message and will be discussed for +each message type separately.

+

GZIP MUST be implemented by the Client and the Bidding and Auction Services, +while Brotli MAY be.

+
+
+
+
+
+
+

+2.2. Request Format +

+

This section discusses the request message sent from the client to the Bidding +and Auction Services endpoint.

+

The request from the Client to Bidding and Auction Services consists of an +[HPKE] encrypted payload with attached header (see Section 2.2.1). The +plaintext payload contains a framing header, outer request, and padding (see +Section 2.2.2). The request message Section 2.2.3 is a [CBOR] encoded +message that contains one or more compressed interest group lists Section 2.2.3.1.

+
+
+

+2.2.1. Encryption +

+

The request is encrypted with [HPKE]. The request uses a similar encapsulated message +format to that used by [OHTTP], with only an additional version field.

+
+
+Encapsulated Request {
+  Version (8),
+  Key Identifier (8),
+  HPKE KEM ID (16),
+  HPKE KDF ID (16),
+  HPKE AEAD ID (16),
+  Encapsulated KEM Shared Secret (8 * Nenc),
+  HPKE-Protected Request (..),
+}
+
+
+

The Version for this message SHOULD be 0. The HPKE KEM ID, HPKE KDF ID, +and HPKE AEAD ID are the key encapsulation mechanism, key derivation function +and authenticated encryption with associated data function parameters for +[HPKE]. For this protocol, a compliant implementation MUST support +DHKEM(X25519, HKDF-SHA256) (0x0020) for HPKE KEM ID, HKDF-SHA256 (0x0001) for +HPKE KDF ID, and AES-256-GCM (0x0002) for HPKE AEAD ID.

+

Encryption of the request is similar to in [OHTTP] +Section 4.3, +only with a different media type and the Version prepended to the encrypted +message:

+
    +
  1. +

    Construct a message header (hdr) by concatenating the values of the +Key Identifier, HPKE KEM ID, HPKE KDF ID, and HPKE AEAD ID in network +byte order.

    +
  2. +
  3. +

    Build a sequence of bytes (info) by concatenating the ASCII-encoded string +"message/auction request", a zero byte, and hdr.

    +
  4. +
  5. +

    Create a sending HPKE context by invoking SetupBaseS() +(Section 5.1.1 of [HPKE]) +with the public key of the receiver pkR and info. This yields the context +sctxt and an encapsulation key enc.

    +
  6. +
  7. +

    Encrypt request by invoking the Seal() method on sctxt +(Section 5.2 of [HPKE]) +with empty associated data aad, yielding ciphertext ct.

    +
  8. +
  9. +

    Concatenate the values of Version, hdr, enc, and ct.

    +
  10. +
+

In pseudocode, this procedure is as follows:

+
+
+hdr = concat(encode(1, key_id),
+             encode(2, kem_id),
+             encode(2, kdf_id),
+             encode(2, aead_id))
+info = concat(encode_str("message/auction request"),
+              encode(1, 0),
+              hdr)
+enc, sctxt = SetupBaseS(pkR, info)
+ct = sctxt.Seal("", request)
+enc_request = concat(encode(1, version), hdr, enc, ct)
+
+
+

A Bidding and Auction Services endpoint decrypts this encapsulated message in a +similar manner to [OHTTP] +Section 4.3, +or more explicitly as follows:

+
    +
  1. +

    Parse enc_request into version, key_id, kem_id, kdf_id, aead_id, +enc, and ct.

    +
  2. +
  3. +

    If version is not 0, return an error.

    +
  4. +
  5. +

    Find the matching HPKE private key, skR, corresponding to key_id. If +there is no matching key, return an error.

    +
  6. +
  7. +

    Build a sequence of bytes (info) by concatenating the ASCII-encoded string +"message/auction request"; a zero byte; key_id as an 8-bit integer; plus +kem_id, kdf_id, and aead_id as three 16-bit integers.

    +
  8. +
  9. +

    Create a receiving HPKE context, rctxt, by invoking SetupBaseR() +(Section 5.1.1 of [HPKE]) +with skR, enc, and info.

    +
  10. +
  11. +

    Decrypt ct by invoking the Open() method on rctxt +(Section 5.2 of [HPKE]), +with an empty associated data aad, yielding request and returning an +error on failure.

    +
  12. +
+

In pseudocode, this procedure is as follows:

+
+
+version, key_id, kem_id, kdf_id, aead_id, enc, ct = parse(enc_request)
+if version != 0 then return error
+info = concat(encode_str("message/auction request"),
+              encode(1, 0),
+              encode(1, key_id),
+              encode(2, kem_id),
+              encode(2, kdf_id),
+              encode(2, aead_id))
+rctxt = SetupBaseR(enc, skR, info)
+request, error = rctxt.Open("", ct)
+
+
+

Bidding and Auction Services retains the HPKE context, rctxt, so that it can +encapsulate a response.

+
+
+
+
+

+2.2.2. Framing and Padding +

+

The plaintext message uses the framing described in Section 2.1.2.

+

Messages MAY be zero padded so that the encrypted request is one of the +following bin sizes: 0KiB, 5KiB, 10KiB, 20KiB, 30KiB, 40KiB, 55KiB. An +implementation MAY need to remove some data from the payload to fit inside the +largest bucket.

+

A compatible implementation processing requests SHOULD NOT rely on a specific +padding scheme for requests.

+
+
+
+
+

+2.2.3. Request Message +

+

The request message is a [CBOR] encoded message with the following [CDDL] schema:

+
+
+request = {
+  ; Current version of the protocol.
+  ; In this document, it must be 0.
+  version: int,
+  ; Used by the Bidding and Auction Services to
+  ; keep track of a request (and corresponding response)
+  ; over its lifetime.
+  ; Must be a [UUID][Version 4].
+  generationId: uuid,
+  ; Represents the publisher initiating the request.
+  publisher: origin,
+  interestGroups: {
+    ; Map of interest group owner to CBOR encoded list of interest
+    ; groups compressed as described in § Generating a Request.
+    * interestGroupOwner => bstr
+  },
+  ? enableDebugReporting: bool
+}
+
+
+

The version field SHOULD be set to 0 for this version of the protocol. The +interestGroups field is a map from interest group owner to a compressed, +[CBOR]-encoded list of interest groups for that owner.

+
+
+
+2.2.3.1. List of Interest Groups +
+

A list of interest group is encoded in [CBOR] with the following [CDDL] schema:

+
+
+interestGroups = [ * interestGroup ]
+interestGroup = {
+  ; This interest group's name, see
+  ; https://wicg.github.io/turtledove/#interest-group-name.
+  name: tstr,
+
+  ; Keys used to look up real-time bidding signals, see
+  ; https://wicg.github.io/turtledove/#interest-group-trusted-bidding-signals-keys.
+  ? biddingSignalsKeys: [* tstr],
+  ; Data about the user that the bidder can use during bid calculation, see
+  ; https://wicg.github.io/turtledove/#interest-group-user-bidding-signals.
+  ? userBiddingSignals: json,
+  ; Contains various ads that the interest group might show. See
+  ; https://wicg.github.io/turtledove/#interest-group-ads.
+  ? ads: [* adRenderId],
+
+  ; Contains various ad components (or "products") that can be used to
+  ; construct ads composed of multiple pieces — a top-level ad template
+  ; "container" which includes some slots that can be filled in with
+  ; specific "products". See
+  ; https://wicg.github.io/turtledove/#interest-group-ad-components.
+  ? components: [* adRenderId],
+  ? browserSignals: {
+    ; Number of times the group was joined in the last 30 days.
+    ? joinCount: int,
+
+    ; Number of times the group bid in an auction in the last 30
+    ; days.
+    ? bidCount: int,
+
+    ; Tuple of time-ad pairs for a previous win for this interest
+    ; group that occurred in the last 30 days.
+    ; The time is specified in seconds before the containing
+    ; auctionBlob was requested.
+    ? prevWins: [* [int, adRenderId]],
+
+    ; The most recent join time for this group expressed
+    ; in milli seconds before the containing auctionBlob
+    ; was requested. This field will be used by newer client
+    ; versions. For older devices, the precison will be in seconds.
+    ; If recencyMs is present, this value will be used to offer
+    ; higher precision. If not, recency will be used. Only
+    ; one of the recency or recencyMs is expected to present in
+    ; the request.
+    ? recencyMs: int
+  }
+}
+
+
+

Each list is separately compressed with the compression method indicated in +the Section 2.1.2 header.

+
+
+
+
+
+
+

+2.2.4. Generating a Request +

+

This section describes how the client MAY form and serialize request messages +in order to communicate with the Bidding and Auction services.

+

This algorithm takes as input all of the relevant interest groups, a config +consisting of the publisher, a map of from (origin, string) tuple to origin +ig pagg coordinators, +an optional desired total size, an optional boolean debugging report locked out +defaults to false, an optional list of interest group owners to +include each with an optional desired size, and the [HPKE] public key with +its associated key ID. It returns an encrypted request and a request context +tuple.

+
    +
  1. +

    Let included_groups be an empty map.

    +
  2. +
  3. +

    If desired total size is not specified, but the list of interest group owners +includes at least one entry with a specified desired size:

    +
      +
    1. +

      Set desired total size to the sum of all specified desired size in the +list of interest group owners.

      +
    2. +
    +
  4. +
  5. +

    Group the list of relevant interest groups by owner into a map of from +interest group owner to a list of interest groups sorted by decreasing +priority, interest group map.

    +
  6. +
  7. +

    If the list of interest group owners is specified, remove interest groups +whose owner is not on the list.

    +
  8. +
  9. +

    Construct a request, request with request["publisher"] set to publisher, +request["version"] set to 0, request["generationId"] set to a new [UUID] +Version 4, +and request["enableDebugReporting"] set to debugging report locked out.

    +
  10. +
  11. +

    Set current_size to be the serialized size of the encrypted request +created from request without padding.

    +
  12. +
  13. +

    Set remaining_allocated_size to 0.

    +
  14. +
  15. +

    Set remaining_unsized_owners to 0.

    +
  16. +
  17. +

    For each interest group owner, interest group list in +interest group map:

    +
      +
    1. +

      If there is a desired size for interest group owner:

      +
        +
      1. +

        Increment remaining_allocated_size by desired size.

        +
      2. +
      +
    2. +
    3. +

      Otherwise

      +
        +
      1. +

        Increment remaining_unsized_owners by 1.

        +
      2. +
      +
    4. +
    +
  18. +
  19. +

    For each interest group owner, interest group list in +interest group map where there is a desired size specified for +interest group owner:

    +
      +
    1. +

      If the number of unsized_owners is not 0:

      +
        +
      1. +

        Set the allowed_interest_group_size to the desired size for this +interest group owner. This is a fixed size allocation.

        +
      2. +
      +
    2. +
    3. +

      Otherwise:

      +
        +
      1. +

        Let remaining_size be equal to the desired total size-current_size.

        +
      2. +
      3. +

        Set the allowed_interest_group_size to +remaining_size*desired_size/remaining_allocated_size. This is a +proportional allocation.

        +
      4. +
      +
    4. +
    5. +

      Set remaining_allocated_size = remaining_allocated_size-current_size.

      +
    6. +
    7. +

      [CBOR] encode the interest group list into serialized list.

      +
    8. +
    9. +

      [GZIP] the serialized list into compressed list.

      +
    10. +
    11. +

      If setting request["interestGroups"][interest group owner] to +compressed list would make it's serialized size more than +allowed_interest_group_size larger than the current size, then remove +the lowest priority interest group and repeat from the previous step.

      +
    12. +
    13. +

      Set request["interestGroups"][interest group owner] to +compressed list.

      +
    14. +
    15. +

      Set included_groups[interest group owner] to interest group list.

      +
    16. +
    17. +

      Set current_size to be the serialized size of the encrypted request +created from request without padding.

      +
    18. +
    +
  20. +
  21. +

    For each interest group owner, interest group list in +interest group map where there is not desired size specified for +interest group owner:

    +
      +
    1. +

      Let remaining_size be equal to the desired total size-current_size.

      +
    2. +
    3. +

      Set the allowed_interest_group_size to +remaining_size*/remaining_unsized_owners. This is a +equal size allocation.

      +
    4. +
    5. +

      Decrement remaining_unsized_owners by 1.

      +
    6. +
    7. +

      [CBOR] encode the interest group list into serialized list.

      +
    8. +
    9. +

      [GZIP] the serialized list into compressed list.

      +
    10. +
    11. +

      If adding the compressed list to request would make it more than +allowed_interest_group_size larger than the current size, then remove +the lowest priority interest group and repeat from the previous step.

      +
    12. +
    13. +

      Set request["interestGroups"][interest group owner] to +compressed list.

      +
    14. +
    15. +

      Set included_groups[interest group owner] to interest group list.

      +
    16. +
    17. +

      Set current_size to be the serialized size of the encrypted request +created from request without padding.

      +
    18. +
    +
  22. +
  23. +

    If there are no interest groups in the request, discard the request and +return failure.

    +
  24. +
  25. +

    Prepend the framing header to request with Compression set to 2.

    +
  26. +
  27. +

    If desired total size is set then zero pad request to desired total size. +Otherwise zero pad request up to the smallest bin size in Section 2.2.2 +larger than request.

    +
  28. +
  29. +

    Encrypt request using the public key and its key id as in +Section 2.2.1 to get the encrypted message and hpke context.

    +
  30. +
  31. +

    Let the request context be the tuple +(included_groups, hpke context, ig pagg coordinators).

    +
  32. +
  33. +

    Return the encrypted message and the request context.

    +
  34. +
+
+
+
+
+

+2.2.5. Parsing a Request +

+

This section describes how the Bidding and Auction Services MUST deserialize +request messages from the client.

+

The algorithm takes as input a serialized request message from the client +(Section 2.2.4) and a list of HPKE private keys (along with their +corresponding key IDs).

+

The output is either an error sent back to the client, an empty message sent +back to the client, or a request message the Bidding and Auction services can +consume along with an HPKE context.

+
    +
  1. +

    Let encrypted request be the request received from the client.

    +
  2. +
  3. +

    Let error_msg be an empty string.

    +
  4. +
  5. +

    De-encapsulate and decrypt encrypted request by using the input private key +corresponding to key_id, as described in Section 2.2.1, to get the +decrypted message and rctxt.

    +
      +
    1. +

      If decapsulation or decryption fails, return failure.

      +
    2. +
    3. +

      Else, save the decrypted output as framed request and save rctxt.

      +
    4. +
    +
  6. +
  7. +

    Remove and extract the first 5 bytes from framed request as the +framing header (described in Section 2.1.2), removing them from +framed request.

    +
  8. +
  9. +

    If the framing header's Version field is not 0, return failure.

    +
  10. +
  11. +

    If the framing header's Compression field is not supported, return +failure. Otherwise, save the Compression field value as compression type.

    +
  12. +
  13. +

    Let length be equal to the framing header's Size field.

    +
  14. +
  15. +

    If length is greater than the length of the remaining bytes in +framed request, return failure.

    +
  16. +
  17. +

    Take the first length remaining bytes in framed response as +decodable request, discarding the rest.

    +
  18. +
  19. +

    [CBOR] decode decodable request into the message represented in Section 2.2.3. +Let this be request.

    +
  20. +
  21. +

    If [CBOR] decoding fails, return failure.

    +
  22. +
  23. +

    Let processed request be an empty struct.

    +
  24. +
  25. +

    If request is not a map, return failure.

    +
  26. +
  27. +

    If request["version"] does not exist or is not 0, return failure.

    +
  28. +
  29. +

    If request["publisher"] does not exist or is not a string, return failure.

    +
  30. +
  31. +

    Set processed request["publisher"] to request["publisher"].

    +
  32. +
  33. +

    If request["generationId"] does not exist or is not a string, return failure.

    +
  34. +
  35. +

    Set processed request["generationId"] to request["generationId"].

    +
  36. +
  37. +

    If request["enableDebugReporting] exists:

    +
      +
    1. +

      If request["enableDebugReporting"] is not a boolean, return failure.

      +
    2. +
    3. +

      Set processed request["enableDebugReporting"] to +request["enableDebugReporting"].

      +
    4. +
    +
  38. +
  39. +

    If request["interestGroups] does not exist or is not a map, return failure.

    +
  40. +
  41. +

    Set processed request["interestGroups"] to an empty map.

    +
  42. +
  43. +

    For each key, value map entry of request["interestGroups"]:

    +
      +
    1. +

      If key is not a string, append an error message to +error_msg. Proceed to Section 2.2.5.1.

      +
    2. +
    3. +

      Set processed request["interestGroups"][key] to an empty list.

      +
    4. +
    5. +

      Decompress value according to compression type and set as +buyer input cbor. If decompression fails, return failure.

      +
    6. +
    7. +

      [CBOR] decode buyer input cbor into buyer input. If decoding fails, return failure.

      +
    8. +
    9. +

      If buyer input is not an array, return failure.

      +
    10. +
    11. +

      For each interest group in buyer input:

      +
        +
      1. +

        If the interest groups is not a map, append an error message to +error_msg. Proceed to Section 2.2.5.1.

        +
      2. +
      3. +

        Let ig be an empty struct similar to Section 2.2.3.1.

        +
      4. +
      5. +

        If interest group["name"] does not exist or is not a string, return failure.

        +
      6. +
      7. +

        Set ig["name"] to interest group["name"].

        +
      8. +
      9. +

        If interest group["userBiddingSignals"] exists:

        +
          +
        1. +

          If interest group["userBiddingSignals"] is not a string, return failure.

          +
        2. +
        3. +

          Set ig["userBiddingSignals"] to interest group["userBiddingSignals"].

          +
        4. +
        +
      10. +
      11. +

        If interest group["biddingSignalsKeys"] exists:

        +
          +
        1. +

          If interest group["biddingSignalsKeys"] is not an array of strings, +return failure.

          +
        2. +
        3. +

          Set ig["biddingSignalsKeys"] to interest group["biddingSignalsKeys"].

          +
        4. +
        +
      12. +
      13. +

        If interest group["ads"] exists:

        +
          +
        1. +

          If interest group["ads"] is not an array of strings, return failure.

          +
        2. +
        3. +

          Set ig["ads"] to interest group["ads"].

          +
        4. +
        +
      14. +
      15. +

        If interest group["component"] exists:

        +
          +
        1. +

          If interest group["component"] is not an array +of strings, return failure.

          +
        2. +
        3. +

          Set ig["component"] to interest group["component"].

          +
        4. +
        +
      16. +
      17. +

        If interest group["browserSignals"] exists:

        +
          +
        1. +

          If interest group["browserSignals"] is not a map, return failure.

          +
        2. +
        3. +

          Let igbs be an empty struct similar to browserSignals as +defined in Section 2.2.3.1.

          +
        4. +
        5. +

          Let signals be interest group["browserSignals"].

          +
        6. +
        7. +

          If signals["bidCount"] exists:

          +
            +
          1. +

            If signals["bidCount"] is not a valid 64-bit unsigned integer, +return failure.

            +
          2. +
          3. +

            Set igbs["bidCount"] to signals["bidCount"].

            +
          4. +
          +
        8. +
        9. +

          If signals["joinCount"] exists:

          +
            +
          1. +

            If signals["joinCount"] is not a valid 64-bit unsigned integer, +return failure.

            +
          2. +
          3. +

            Set igbs["joinCount"] to signals["joinCount"].

            +
          4. +
          +
        10. +
        11. +

          If signals["recencyMs"] exists:

          +
            +
          1. +

            If signals["recencyMs"] is not a valid 64-bit unsigned integer, +return failure.

            +
          2. +
          3. +

            Set igbs["recencyMs"] to signals["recencyMs"].

            +
          4. +
          +
        12. +
        13. +

          If signals["prevWins"] exists:

          +
            +
          1. +

            Let pw be an empty array.

            +
          2. +
          3. +

            If signals["prevWins"] is not an array, return failure.

            +
          4. +
          5. +

            For each prevWinTuple in signals["prevWins"]:

            +
              +
            1. +

              Let pwt be an empty array.

              +
            2. +
            3. +

              If prevWinTuple is not an array of size 2, return failure.

              +
            4. +
            5. +

              If prevWinTuple[0] is not a valid 64-bit unsigned integer, +return failure.

              +
            6. +
            7. +

              If prevWinTuple[1] is not a string, return failure.

              +
            8. +
            9. +

              Set pwt to prevWinTuple.

              +
            10. +
            11. +

              Append pwt to pw.

              +
            12. +
            +
          6. +
          7. +

            Set igbs["prevWins"] to pw.

            +
          8. +
          +
        14. +
        15. +

          Set ig["browserSignals"] to igbs.

          +
        16. +
        +
      18. +
      19. +

        Append ig to processed request["interestGroups"][ key ].

        +
      20. +
      +
    12. +
    +
  44. +
  45. +

    Return processed request and rctxt to the Bidding and Auction +Services.

    +
  46. +
+
+
+
+2.2.5.1. Request Parse Error Handling +
+

If Section 2.2.5 returns with failure, the following algorithm describes +how the Bidding and Auction Services MUST respond.

+

The input to this algorithm is error_msg, which MAY be returned from the point +of failure in Section 2.2.5.

+

The output is a response to the client.

+
    +
  1. +

    If the failure happens before or during decryption, respond with an empty +message.

    +
  2. +
  3. +

    Otherwise abort processing the request.

    +
  4. +
  5. +

    Let error be a new map with key-value pairs: +[("code", 400), ("error", error_msg)].

    +
  6. +
  7. +

    Let response be a new Section 2.3.3.

    +
  8. +
  9. +

    Set response["error"] to error.

    +
  10. +
  11. +

    Serialize and send response to the client per Section 2.3.

    +
  12. +
+
+
+
+
+
+
+
+
+

+2.3. Response Format +

+

This section discusses the request message sent from the Bidding and Auction +Services endpoint to the client in reply to a request.

+

The response from the Bidding and Auction Services endpoint consists of an +[HPKE] encrypted payload with attached header (see Section 2.3.1). The +plaintext payload contains a framing header, response message, and padding (see +Section 2.3.2). The response message Section 2.3.3 is a compressed +[CBOR] encoded message.

+
+
+

+2.3.1. Encryption +

+

The response uses a similar encapsulated response format to that used by +[OHTTP].

+
+
+Encapsulated Response {
+  Nonce (8 * max(Nn, Nk)),
+  AEAD-Protected Response (..),
+}
+
+
+

Encryption of the response is similar to in [OHTTP] Section 4.4, only with a different media type, +repeated below for clarity:

+
    +
  1. +

    Export a secret (secret) from context, using the string +"message/auction response" as the exporter_context parameter to +context.Export; see Section 5.3 +of [HPKE]. The length of this secret is max(Nn, Nk), where Nn and Nk are +the length of the AEAD key and nonce that are associated with context.

    +
  2. +
  3. +

    Generate a random value of length max(Nn, Nk) bytes, called response_nonce.

    +
  4. +
  5. +

    Extract a pseudorandom key (prk) using the Extract function provided by +the KDF algorithm associated with context. The ikm input to this function +is secret; the salt input is the concatenation of enc (from + enc_request) and response_nonce.

    +
  6. +
  7. +

    Use the Expand function provided by the same KDF to create an AEAD key, +key, of length Nk -- the length of the keys used by the AEAD associated +with context. Generating aead_key uses a label of "key".

    +
  8. +
  9. +

    Use the same Expand function to create a nonce, nonce, of length Nn +-- the length of the nonce used by the AEAD. Generating aead_nonce uses a + label of "nonce".

    +
  10. +
  11. +

    Encrypt response, passing the AEAD function Seal the values of aead_key, +aead_nonce, an empty aad, and a pt input of response. This yields ct.

    +
  12. +
  13. +

    Concatenate response_nonce and ct, yielding an Encapsulated Response, +enc_response. Note that response_nonce is of fixed length, so there is no + ambiguity in parsing either response_nonce or ct.

    +
  14. +
+

In pseudocode, this procedure is as follows:

+
+
+secret = context.Export("message/auction response", max(Nn, Nk))
+response_nonce = random(max(Nn, Nk))
+salt = concat(enc, response_nonce)
+prk = Extract(salt, secret)
+aead_key = Expand(prk, "key", Nk)
+aead_nonce = Expand(prk, "nonce", Nn)
+ct = Seal(aead_key, aead_nonce, "", response)
+enc_response = concat(response_nonce, ct)
+
+
+

Clients decrypt an Encapsulated Response by reversing this process. That is, +Clients first parse enc_response into response_nonce and ct. Then, they +follow the same process to derive values for aead_key and aead_nonce, using +their sending HPKE context, sctxt, as the HPKE context, context.

+

The Client uses these values to decrypt ct using the AEAD function Open. +Decrypting might produce an error, as follows:

+
+
+response, error = Open(aead_key, aead_nonce, "", ct)
+
+
+
+
+
+
+

+2.3.2. Framing and Padding +

+

The plaintext message uses the framing described in Section 2.1.2.

+

Messages MAY be exponentially padded so that the encrypted response is a power +of 2 in length.

+

A compatible implementation processing requests SHOULD NOT rely on a specific +padding scheme for requests.

+
+
+
+
+

+2.3.3. Response Message +

+

The response message is a [CBOR] encoded message, compressed using the method +indicated in {#request-framing}. The [CBOR] encoded message SHOULD be +serialized into deterministically encoded [CBOR] (as defined in +Section 4.2) +and follows the following [CDDL] schema:

+
+
+response = {
+  ; An unguessable nonce to use when authorizing this response.
+  ? nonce: tstr,
+
+  ; The ad to render.
+  ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-ad-render-url.
+  adRenderURL: adRenderUrl,
+
+  ; List of URLs for component ads displayed as part of this
+  ; ad.
+  ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-ad-components.
+  ; If not present, map as an empty list.
+  ? components: [* adRenderUrl],
+
+  ; Name of the interest group to which the ad belongs.
+  ; See https://wicg.github.io/turtledove/#interest-group-name.
+  ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-interest-group-name.
+  ; If not present, map as Null.
+  ? interestGroupName: tstr,
+
+  ; Origin of the Buyer who owns the interest group.
+  ; The original request for this response MUST contain this
+  ; interestGroupOwner, which additionally MUST provide an interest
+  ; group with interestGroupName.
+  ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-interest-group-owner.
+  ; If not present, map as Null.
+  ? interestGroupOwner: interestGroupOwner,
+
+  ; Indices of interest groups in the original request for this owner
+  ; that submitted a bid.
+  ; Maps to https://wicg.github.io/turtledove/#server-auction-response-bidding-groups.
+  ; If not present, map as an empty list.
+  ; Else,
+  ;   1. create an empty list
+  ;   2. for each interest group owner key in the biddingGroups map
+  ;   3.    for each index in biddingGroups[interest group owner]
+  ;   4.       interest group name equals the string at the index from (3)
+  ;            in Encryption Context's Interest Group Map (Section 2.2.4.1.2)
+  ;   5.       add a tuple to the list in (1) of [interest group owner (2),
+               interest group name (4)]
+  ;   6. return the list in (1)
+  ? biddingGroups: {
+    * interestGroupOwner => [* int]
+  },
+
+  ; Indices and update-if-older-than times of interest groups in the original
+  ; request for this owner for interest groups where an update-if-older-than
+  ; time (in milliseconds) was specified.
+  ; Maps to https://wicg.github.io/turtledove/#server-auction-response-update-groups.
+  ? updateGroups: {
+    * interestGroupOwner => [
+      * {
+        index: int,
+        updateIfOlderThanMs: int
+      }]
+  },
+
+  ; Score of the ad determined during the auction.
+  ; Any value that is zero or negative indicates that the ad cannot
+  ; win the auction.
+  ; The winner of the auction would be the ad that was given the
+  ; highest score.
+  ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-score.
+  ; If not present, map as Null.
+  ? score: float,
+
+  ; Bid price corresponding to an ad
+  ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-bid.
+  ; If not present, map as Null.
+  ? bid: float,
+
+  ; Optional currency of the bid.
+  ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-bid-currency.
+  ; If not present, map as Null.
+  ? bidCurrency: currency,
+
+  ; Optional BuyerReportingId of the winning Ad
+  ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-buyer-reporting-id.
+  ; If not present, map as Null.
+  ? buyerReportingId: tstr,
+
+  ; Optional BuyerAndSellerReportingId of the winning Ad
+  ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-buyer-and-seller-reporting-id.
+  ; If not present, map as Null.
+  ? buyerAndSellerReportingId: tstr,
+
+  ; Optional SelectedBuyerAndSellerReportingId of the winning Ad
+  ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-selected-buyer-and-seller-reporting-id
+  ; If not present, map as Null.
+  ? selectedBuyerAndSellerReportingId: tstr,
+
+  ; The auction result may be ignored if set to true.
+  ; Maps to https://wicg.github.io/turtledove/#server-auction-response-is-chaff.
+  ; If not present, map as false.
+  ? isChaff: bool,
+
+  ; Optional wrapper for various reporting URLs.
+  ? winReportingUrls: {
+    ; Maps to https://wicg.github.io/turtledove/#server-auction-response-buyer-reporting.
+    ; If not present, map as 'Null'.
+    ? buyerReportingUrls: reportingUrls,
+    ; Maps to https://wicg.github.io/turtledove/#server-auction-response-component-seller-reporting.
+    ; If not present, map as 'Null'.
+    ? componentSellerReportingUrls: reportingUrls,
+    ; Maps to https://wicg.github.io/turtledove/#server-auction-response-top-level-seller-reporting.
+    ; If not present, map as 'Null'.
+    ? topLevelSellerReportingUrls: reportingUrls
+  },
+
+  ; Contains an error message from the auction executed on the trusted auction
+  ; server.
+  ; May be used to provide additional context for the result of an auction.
+  ; Maps to https://wicg.github.io/turtledove/#server-auction-response-error.
+  ; If not present, map as Null.
+  ; Else, ignore the `code` field and use the `message` field directly
+  ; for the server auction response error field.
+  ? error: {
+    code: int,
+    message: tstr
+  },
+
+  ; Arbitrary metadata to pass to the top-level seller.
+  ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-ad-metadata.
+  ; If not present, map as Null.
+  ? adMetadata: json,
+
+  ; Optional name/domain for the top-level seller in case this is a
+  ; component auction.
+  ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-top-level-seller.
+  ; If not present, map as Null.
+  ? topLevelSeller: origin,
+
+  ; Optional list of forDebuggingOnly reports.
+  ; If not present, map as an empty list.
+  ; Maps to https://wicg.github.io/turtledove/#server-auction-response-component-win-debugging-only-reports
+  ; and https://wicg.github.io/turtledove/#server-auction-response-server-filtered-debugging-only-reports.
+  ? debugReports: [
+    * {
+      origin => [
+        * {
+          url tstr,
+          ? bool isWinReport,
+          ? bool isSellerReport,
+          ? bool componentWin
+
+  ; Optional list of private aggregation contributions.
+  ; If not present, map as an empty list.
+  ? paggResponse: [
+    * {
+      origin => [
+        * {
+          ? igIndex: int,
+          ? coordinator: origin,
+          ? componentWin: bool,
+          eventContributions: [
+            tstr => [
+              * {
+                bucket: blob,
+                value: int
+              }
+            ]
+          ]
+        }
+      ]
+    }
+  ],
+}
+
+; Defines the structure for reporting URLs.
+reportingUrls = {
+  ; Maps directly to https://wicg.github.io/turtledove/#server-auction-reporting-info-reporting-url.
+  ; If not present, map as Null.
+  ? reportingUrl: tstr,
+  ; Maps directly to https://wicg.github.io/turtledove/#server-auction-reporting-info-beacon-urls.
+  ; If not present, map as an empty ordered map (https://infra.spec.whatwg.org/#ordered-map).
+  ? interactionReportingUrls: { * tstr => tstr }
+}
+
+
+
+
+
+
+
+

+2.3.4. Generating a Response +

+

This algorithm describes how conforming Bidding and Auction Services MAY +generate a response to a request.

+

The input is a payload corresponding to Section 2.3.3 and the HPKE +receiver context saved in Section 2.2.5, rctxt.

+

The output is a response to be sent to a Client.

+
    +
  1. +

    Let cbor payload equal the deterministically encoded CBOR + payload. Return an empty response on CBOR encoding failure.

    +
  2. +
  3. +

    Let compressed payload equal the [GZIP] compressed cbor payload, +returning an empty response on compression failure.

    +
  4. +
  5. +

    Create a framed payload, as described in Section 2.3.2:

    +
      +
    1. +

      Create a framing header.

      +
    2. +
    3. +

      Set the framing header Compression to 2.

      +
    4. +
    5. +

      Set the framing header Version to 0.

      +
    6. +
    7. +

      Set the framing header Size to the size of compressed payload.

      +
    8. +
    9. +

      Let framed payload equal the result of prepend the framing header +to compressed payload.

      +
    10. +
    11. +

      Padding MAY be added to framing header, as described in +Section 2.3.2.

      +
    12. +
    13. +

      Return an empty response on failure of any of the previous steps.

      +
    14. +
    +
  6. +
  7. +

    Let response equal the result of the encryption and encapsulation of +framed payload with rctxt, as described in Section 2.3.1. +Return an empty response on failure.

    +
  8. +
  9. +

    Return response.

    +
  10. +
+
+
+
+
+

+2.3.5. Parsing a Response +

+

This algorithm describes how a conforming Client MUST parse and validate a +response from Bidding and Auction Services. It takes as input the +request context tuple returned from Section 2.2.4 in addition to the +encrypted response.

+
    +
  1. +

    Use request context's hpke context as the context for decryption and +follow the decryption steps in Section 2.3.1 to decrypt +encrypted response and obtain framed response and error.

    +
  2. +
  3. +

    If error is not null, return failure.

    +
  4. +
  5. +

    Remove and extract the first 5 bytes from framed response as the +framing header (described in Section 2.1.2), removing them from +framed response.

    +
  6. +
  7. +

    If the framing header's Version field is not 0, return failure.

    +
  8. +
  9. +

    Let length be equal to the framing header's Size field.

    +
  10. +
  11. +

    If length is greater than the length of the remaining bytes in +framed response, return failure.

    +
  12. +
  13. +

    Take the first length remaining bytes in framed response as +compressed response, discarding the rest.

    +
  14. +
  15. +

    Decompress the compressed response into serialized response using the +method indicated by framing header's Compression field, returning +failure if decompression fails.

    +
  16. +
  17. +

    [CBOR] decode the serialized response into response, returning failure +if decompression fails.

    +
  18. +
  19. +

    If response is not a map, return failure.

    +
  20. +
  21. +

    If response["error"] exists, return failure.

    +
  22. +
  23. +

    If response["isChaff"] exists and is either not a boolean or is true, +return failure.

    +
  24. +
  25. +

    Let processed response be a new structure analogous to +server auction response.

    +
  26. +
  27. +

    If response["nonce"] exists and is a valid [UUID], set +processed response["nonce"] to response["nonce"].

    +
  28. +
  29. +

    If response["adRenderURL"] does not exist, return failure.

    +
  30. +
  31. +

    Set processed response["ad render url"] to response["adRenderURL"] parsed +as a [URL], returning failure if there is an error.

    +
  32. +
  33. +

    If response["components"] exists:

    +
      +
    1. +

      If response["components"] is not an array, return failure.

      +
    2. +
    3. +

      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.

        +
      2. +
      +
    4. +
    +
  34. +
  35. +

    If response["interestGroupName"] does not exist or is not a string, return failure.

    +
  36. +
  37. +

    Set processed response["interest group name"] to response["interestGroupName"].

    +
  38. +
  39. +

    If response["interestGroupOwner"] does not exist or is not a string, return failure.

    +
  40. +
  41. +

    Set processed response["interest group owner"] to response["interestGroupOwner"] +parsed as an [ORIGIN], returning failure if there is an error.

    +
  42. +
  43. +

    If response["biddingGroups"] does not exist or is not a map, return failure.

    +
  44. +
  45. +

    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.

      +
    2. +
    3. +

      If request context's included_groups does not contain owner as a key, return failure.

      +
    4. +
    5. +

      If value is not a list, return failure.

      +
    6. +
    7. +

      For each element in value:

      +
        +
      1. +

        If element is not an integer or element < 0, return failure.

        +
      2. +
      3. +

        If element is greater than or equal to the length of +included_groups[owner], return failure.

        +
      4. +
      5. +

        Let name be the interest group name for included_groups[owner][element].

        +
      6. +
      7. +

        Append the tuple (owner, name) to processed response["bidding groups"].

        +
      8. +
      +
    8. +
    +
  46. +
  47. +

    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.

        +
      2. +
      3. +

        If request context's included_groups does not contain owner as a +key, continue the next iteration of this loop.

        +
      4. +
      5. +

        If value is not a list, return failure.

        +
      6. +
      7. +

        For each element in value:

        +
          +
        1. +

          If element is not a map, continue the next iteration of this loop.

          +
        2. +
        3. +

          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.

          +
        4. +
        5. +

          If element["index"] is not an integer or element["index"] < 0, +continue the next iteration of this loop.

          +
        6. +
        7. +

          If element["index"] is greater than or equal to the length of +included_groups[owner], continue the next iteration of this loop.

          +
        8. +
        9. +

          Let name be the interest group name for included_groups[owner][element].

          +
        10. +
        11. +

          Let interest group key be the tuple (owner, name).

          +
        12. +
        13. +

          Let update duration be element["updateIfOlderThanMs"], parsed into a time +duration as integer milliseconds.

          +
        14. +
        15. +

          Set processed response["update groups"][intereset group key] to +update duration.

          +
        16. +
        +
      8. +
      +
    2. +
    +
  48. +
  49. +

    If response["score"] exists:

    +
      +
    1. +

      If response["score"] is not a floating point value, return failure.

      +
    2. +
    3. +

      Set processed response["score"] to response["score"].

      +
    4. +
    +
  50. +
  51. +

    If response["bid"] exists:

    +
      +
    1. +

      If response["bid"] is not a floating point value, return failure.

      +
    2. +
    3. +

      Let bid be a new structure analogous to bid with currency.

      +
    4. +
    5. +

      Set bids value field to response["bid"].

      +
    6. +
    7. +

      If response["bidCurrency"] exists:

      +
        +
      1. +

        If response["bidCurrency"] is not a string, return failure.

        +
      2. +
      3. +

        If response["bidCUrrency"] is not 3 bytes long or contains characters +other than upper case ASCII letters, return failure.

        +
      4. +
      5. +

        Set bid's currency field to response["bidCurrency"].

        +
      6. +
      +
    8. +
    9. +

      Set processed response["bid"] to bid.

      +
    10. +
    +
  52. +
  53. +

    If response["winReportingURLs"] exists and is a map:

    +
      +
    1. +

      If response["winReportingURLs"]["buyerReportingURLs"] exists:

      +
        +
      1. +

        Let buyer reporting be the result of Section 2.3.5.1 on +response["winReportingURLs"]["buyerReportingURLs"].

        +
      2. +
      3. +

        Set processed response["buyer reporting"] to buyer reporting.

        +
      4. +
      +
    2. +
    3. +

      If response["winReportingURLs"]["topLevelSellerReportingURLs"] exists:

      +
        +
      1. +

        Let top level seller reporting be the result of Section 2.3.5.1 +on response["winReportingURLs"]["topLevelSellerReportingURLs"].

        +
      2. +
      3. +

        Set processed response["top level seller reporting"] to +top level seller reporting.

        +
      4. +
      +
    4. +
    5. +

      If response["winReportingURLs"]["componentSellerReportingURLs"] exists:

      +
        +
      1. +

        Let component seller reporting be the result of Section 2.3.5.1 +on response["winReportingURLs"]["componentSellerReportingURLs"].

        +
      2. +
      3. +

        Set processed response["component seller reporting" to +component seller reporting.

        +
      4. +
      +
    6. +
    +
  54. +
  55. +

    If response["topLevelSeller"] exists:

    +
      +
    1. +

      If response["topLevelSeller"] is not a string, return failure.

      +
    2. +
    3. +

      Set processed response["top level seller"] to response["topLevelSeller"] +parsed as a [URL], returning failure if there is an error.

      +
    4. +
    +
  56. +
  57. +

    If response["adMetadata"] exists and is a string set +processed response["ad metadata"] to response["adMetadata"].

    +
  58. +
  59. +

    If response["buyerReportingId"] exists and is a string, set +processed response["buyer reporting id"] to response["buyerReportingId"].

    +
  60. +
  61. +

    If response["buyerAndSellerReportingId"] exists and is a string, set +processed response["buyer and seller reporting id"] to +response["buyerAndSellerReportingId"].

    +
  62. +
  63. +

    If response["selectedBuyerAndSellerReportingId"] exists and is a string, set +processed response["selected buyer and seller reporting id"] to +response["selectedBuyerAndSellerReportingId"].

    +
  64. +
  65. +

    If response["debugReports"] exists and is an array:

    +
      +
    1. +

      For each per origin debug reports in response["debugReports"]:

      +
        +
      1. +

        If per origin debug reports["adTechOrigin"] does not exist or +is not a string, continue with the next iteration.

        +
      2. +
      3. +

        Let ad tech origin be per origin debug reports[ +"adTechOrigin"] parsed as an [ORIGIN], continue with the next +iteration if there is an error.

        +
      4. +
      5. +

        If per origin debug reports["reports"] does not exist or is +not an array, continue with the next iteration.

        +
      6. +
      7. +

        For each report in per origin debug reports["reports"]:

        +
          +
        1. +

          If report is not a map, continue with the next iteration.

          +
        2. +
        3. +

          Let component win be report["componentWin"] if it exists +and is a bool, otherwise false.

          +
        4. +
        5. +

          If report["url"] exists and is a string:

          +
            +
          1. +

            Let url be report["url"] parsed as a [URL], or +continue with the next iteration if there is an error.

            +
          2. +
          3. +

            If component win is false, set +processed response["server filtered debugging only +reports"][ad tech origin] to url, and continue with +the next iteration.

            +
          4. +
          5. +

            Let debug report key be a new structure analogous to +server auction debug report key.

            +
          6. +
          7. +

            Set debug report key["from seller"] to +report["isSellerReport"] if it exists and is a bool, +otherwise false.

            +
          8. +
          9. +

            Set debug report key["is debug win"] to +report["isWinReport"] if it exists and is a bool, +otherwise false.

            +
          10. +
          11. +

            Set processed response[ +"component win debugging only reports"][ +debug report key] to url.

            +
          12. +
          +
        6. +
        7. +

          Otherwise:

          +
            +
          1. +

            If component win is false and processed response[ +"server filtered debugging only reports"] does not +contain ad tech origin, set processed response[ +"server filtered debugging only reports"][ +ad tech origin] to an empty list.

            +
          2. +
          +
        8. +
        +
      8. +
      +
    2. +
    +
  66. +
  67. +

    If response["paggResponse"] exists and is an array:

    +
      +
    1. +

      For each per origin response in pagg response:

      +
        +
      1. +

        If per origin response is not a map, continue with the next iteration.

        +
      2. +
      3. +

        If per origin response["reportingOrigin"] does not exist or is not a string, +continue with the next iteration.

        +
      4. +
      5. +

        Let reporting origin be per origin response["reportingOrigin"] parsed as an [ORIGIN], +continue with the next iteration if there is an error.

        +
      6. +
      7. +

        If per origin response["igContributions"] does not exist or is not an array, +continue with the next iteration.

        +
      8. +
      9. +

        Let names be an empty array.

        +
      10. +
      11. +

        If request context's included_groups contains owner as a key, set names to its value.

        +
      12. +
      13. +

        For each ig contribution in per origin response["igContributions"]:

        +
          +
        1. +

          If ig contribution is not a map, continue with the next iteration.

          +
        2. +
        3. +

          Let coordinator be null.

          +
        4. +
        5. +

          If ig contribution["coordinator"] exists and is a string, set coordinator to +per origin response["reportingOrigin"] parsed as an [ORIGIN], continue with the next +iteration if there is an error.

          +
        6. +
        7. +

          Otherwise if ig contribution["igIndex"] exists and is an integer:

          +
            +
          1. +

            If ig contribution["igIndex"] < 0 or is greater than or equal to the length of names, + continue with the next iteration.

            +
          2. +
          3. +

            Let ig key be the tuple (owner, names[igIndex]).

            +
          4. +
          5. +

            If request context's ig pagg coordinators contains ig key, set coordinator to +request context's ig pagg coordinators[ig key].

            +
          6. +
          +
        8. +
        9. +

          Let is component win be false.

          +
        10. +
        11. +

          If ig contribution["componentWin"] exists and is a boolean, set is component win to it.

          +
        12. +
        13. +

          If ig contribution["eventContributions"] exists and is an array:

          +
            +
          1. +

            For each event contribution in ig contribution["eventContributions"]:

            +
              +
            1. +

              Continue with the next iteration if any of the following conditions hold:

              +
                +
              • +

                event contribution is not a map;

                +
              • +
              • +

                event contribution["event"] does not exist or is not a string;

                +
              • +
              • +

                event contribution["event"] starts with "reserved.", but is not one of "reserved.win", "reserved.loss", +or "reserved.always", continue with the next iteration.

                +
              • +
              +
            2. +
            3. +

              Let event be event contribution["event"].

              +
            4. +
            5. +

              If event contribution["contributions"] exists and is an array, for each contribution in it:

              +
                +
              1. +

                Continue with the next iteration if any of the following conditions hold:

                +
                  +
                • +

                  contribution is not a map;

                  +
                • +
                • +

                  contribution["bucket"] does not exist or is not a byte array or its size is greater than 16.

                  +
                • +
                • +

                  contribution["value"] does not exist or is not an integer.

                  +
                • +
                +
              2. +
              3. +

                Let private aggregation contribution be a new structure analogous to [PAExtendedHistogramContribution] +(https://wicg.github.io/turtledove/#dictdef-paextendedhistogramcontribution).

                +
              4. +
              5. +

                Set private aggregation contribution["bucket"] to contribution["bucket"] parsed as a big endian integer.

                +
              6. +
              7. +

                Set private aggregation contribution["value"] to contribution["value"].

                +
              8. +
              9. +

                If is component win is true:

                +
                  +
                1. +

                  Let key be a new structure analogous to [server auction private aggregation contribution key] +(https://wicg.github.io/turtledove/#server-auction-private-aggregation-contribution-key).

                  +
                2. +
                3. +

                  Set key["reporting origin"] to reporting origin.

                  +
                4. +
                5. +

                  Set key["coordinator"] to coordinator.

                  +
                6. +
                7. +

                  Set key["event"] to event.

                  +
                8. +
                9. +

                  If processed response["component win private aggregation contributions"] does not contain key, set +processed response["component win private aggregation contributions"][key] to a new array.

                  +
                10. +
                11. +

                  Append private aggregation contribution to processed response["component win private aggregation contributions"][key].

                  +
                12. +
                +
              10. +
              11. +

                Otherwise if event contribution["event"] starts with "reserved.", append private aggregation contribution +to processed response["server filtered private aggregation contributions reserved"][key].

                +
              12. +
              13. +

                Otherwise, append private aggregation contribution to +processed response["server filtered private aggregation contributions non reserved"][key].

                +
              14. +
              +
            6. +
            +
          2. +
          +
        14. +
        +
      14. +
      +
    2. +
    +
  68. +
  69. +

    Return processed response.

    +
  70. +
+
+
+
+2.3.5.1. Parsing reporting URLs +
+

To parse reporting URLs on a [CBOR] map reporting URLs with a schema like +reportingUrls from Section 2.3.3:

+
    +
  1. +

    Let processed reporting URLs be a new structure analogous to +server auction reporting info.

    +
  2. +
  3. +

    If reporting URLs["reportingURL"] exists and is a string:

    +
  4. +
  5. +

    Let reporting URL be reporting URLs["reportingURL"] parsed as a [URL], +or null if there is an error.

    +
  6. +
  7. +

    If reporting URL is not null, set +processed reporting URLs["reporting url"] to reporting URL.

    +
  8. +
  9. +

    If reporting URLs["interactionReportingURLs"] exists and is a map:

    +
  10. +
  11. +

    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.

    +
  12. +
  13. +

    Return processed reporting URLs.

    +
  14. +
+
+
+
+
+
+
+
+
+
+
+

+3. Security Considerations +

+

TODO

+
+
+
+
+

+4. IANA Considerations +

+

This document introduces no additional considerations for IANA.

+
+
+
+
+

+5. Normative References +

+
+
[ADRENDERID]
+
+"Protected Audience", , <https://wicg.github.io/turtledove/#server-auction-previous-win-ad-render-id>.
+
+
[CBOR]
+
+Bormann, C. and P. Hoffman, "Concise Binary Object Representation (CBOR)", STD 94, RFC 8949, DOI 10.17487/RFC8949, , <https://www.rfc-editor.org/rfc/rfc8949>.
+
+
[CDDL]
+
+Birkholz, H., Vigano, C., and C. Bormann, "Concise Data Definition Language (CDDL): A Notational Convention to Express Concise Binary Object Representation (CBOR) and JSON Data Structures", RFC 8610, DOI 10.17487/RFC8610, , <https://www.rfc-editor.org/rfc/rfc8610>.
+
+
[GZIP]
+
+Deutsch, P., "GZIP file format specification version 4.3", RFC 1952, DOI 10.17487/RFC1952, , <https://www.rfc-editor.org/rfc/rfc1952>.
+
+
[HPKE]
+
+Barnes, R., Bhargavan, K., Lipp, B., and C. Wood, "Hybrid Public Key Encryption", RFC 9180, DOI 10.17487/RFC9180, , <https://www.rfc-editor.org/rfc/rfc9180>.
+
+
[IGOWNER]
+
+"Protected Audience", , <https://wicg.github.io/turtledove/#server-auction-response-interest-group-owner>.
+
+
[ISO4217]
+
+"ISO 4217 Currency codes", , <https://www.iso.org/iso-4217-currency-codes.html>.
+
+
[JSON]
+
+Bray, T., Ed., "The JavaScript Object Notation (JSON) Data Interchange Format", STD 90, RFC 8259, DOI 10.17487/RFC8259, , <https://www.rfc-editor.org/rfc/rfc8259>.
+
+
[OHTTP]
+
+Thomson, M. and C. A. Wood, "Oblivious HTTP", RFC 9458, DOI 10.17487/RFC9458, , <https://www.rfc-editor.org/rfc/rfc9458>.
+
+
[ORIGIN]
+
+"HTML Living Standard", , <https://html.spec.whatwg.org/multipage/webappapis.html#concept-settings-object-origin>.
+
+
[RFC1952]
+
+Deutsch, P., "GZIP file format specification version 4.3", RFC 1952, DOI 10.17487/RFC1952, , <https://www.rfc-editor.org/rfc/rfc1952>.
+
+
[RFC2119]
+
+Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/rfc/rfc2119>.
+
+
[RFC7932]
+
+Alakuijala, J. and Z. Szabadka, "Brotli Compressed Data Format", RFC 7932, DOI 10.17487/RFC7932, , <https://www.rfc-editor.org/rfc/rfc7932>.
+
+
[RFC8174]
+
+Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/rfc/rfc8174>.
+
+
[URL]
+
+"URL Living Standard", , <https://url.spec.whatwg.org/#concept-url>.
+
+
[UUID]
+
+Davis, K., Peabody, B., and P. Leach, "Universally Unique IDentifiers (UUIDs)", RFC 9562, DOI 10.17487/RFC9562, , <https://www.rfc-editor.org/rfc/rfc9562>.
+
+
+
+
+
+
+

+Acknowledgments +

+

TODO

+
+
+
+
+

+Authors' Addresses +

+
+
Daniel Kocoj
+
Google
+ +
+
+
Benjamin "Russ" Hamilton
+
Google
+ +
+
+
+ + + diff --git a/UpdateNonce/draft-ietf-bidding-and-auction-services.txt b/UpdateNonce/draft-ietf-bidding-and-auction-services.txt new file mode 100644 index 0000000..eac57fb --- /dev/null +++ b/UpdateNonce/draft-ietf-bidding-and-auction-services.txt @@ -0,0 +1,1720 @@ + + + + +TBD D. Kocoj +Internet-Draft B. R. Hamilton +Intended status: Standards Track Google +Expires: 7 July 2025 3 January 2025 + + + Bidding and Auction Services + draft-ietf-bidding-and-auction-services-latest + +Abstract + + The Bidding and Auction Services provide a way for advertising + auctions to execute in remote environments while preserving user + privacy. + +Discussion Venues + + This note is to be removed before publishing as an RFC. + + Source for this draft and an issue tracker can be found at + https://github.com/privacysandbox/draft-ietf-bidding-and-auction- + services. + +Status of This Memo + + This Internet-Draft is submitted in full conformance with the + provisions of BCP 78 and BCP 79. + + Internet-Drafts are working documents of the Internet Engineering + Task Force (IETF). Note that other groups may also distribute + working documents as Internet-Drafts. The list of current Internet- + Drafts is at https://datatracker.ietf.org/drafts/current/. + + Internet-Drafts are draft documents valid for a maximum of six months + and may be updated, replaced, or obsoleted by other documents at any + time. It is inappropriate to use Internet-Drafts as reference + material or to cite them other than as "work in progress." + + This Internet-Draft will expire on 7 July 2025. + +Copyright Notice + + Copyright (c) 2025 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents (https://trustee.ietf.org/ + license-info) in effect on the date of publication of this document. + Please review these documents carefully, as they describe your rights + and restrictions with respect to this document. Code Components + extracted from this document must include Revised BSD License text as + described in Section 4.e of the Trust Legal Provisions and are + provided without warranty as described in the Revised BSD License. + +Table of Contents + + 1. Introduction + 1.1. Scope + 1.2. Terminology + 2. Message Format Specifications + 2.1. Overview + 2.1.1. Common Definitions + 2.1.2. Message Framing and Padding + 2.2. Request Format + 2.2.1. Encryption + 2.2.2. Framing and Padding + 2.2.3. Request Message + 2.2.4. Generating a Request + 2.2.5. Parsing a Request + 2.3. Response Format + 2.3.1. Encryption + 2.3.2. Framing and Padding + 2.3.3. Response Message + 2.3.4. Generating a Response + 2.3.5. Parsing a Response + 3. Security Considerations + 4. IANA Considerations + 5. Normative References + Acknowledgments + Authors' Addresses + +1. Introduction + + Today, real-time bidding and ad auctions are executed on servers that + may not provide technical guarantees of security. Some users have + concerns about how their data is handled to generate relevant ads and + in how that data is shared. Protected Audience API (Android + (https://developer.android.com/design-for-safety/privacy-sandbox/ + fledge), Chrome (https://developer.chrome.com/docs/privacy-sandbox/ + fledge/)) provides ways to preserve privacy and limit third-party + data sharing by serving personalized ads based on previous mobile app + or web engagement. + + This Bidding and Auction Services proposal outlines a way to allow + Protected Audience computation to take place on cloud servers in a + Trusted Execution Environment (TEE), rather than running locally on a + user's device. Running workloads in a TEE in cloud has the following + benefits: + + * Scalable ad auctions. + + - A scalable ad auction may include several buyers and sellers + and that can demand more compute resources and network + bandwidth. + + * Lower latency of ad auctions. + + - Server to server communication on the cloud is faster than + multiple device to server calls. + + - Adtech code can execute faster on servers with higher computing + power. + + * Higher utility of ad auctions. + + - Servers have better processing power, therefore adtechs can run + compute intensive workloads on a server for better utility. + + - Lower latency of ad auctions also positively impact utility. + + * Security protection + + - TEEs can protect confidentiality of adtech code and signals. + + * System health of the user's device. + + - Ensure better system health of user's device by freeing up + computational cycles and network bandwidth. + + Standardized protocols for interacting with Bidding and Auction + Services are essential to creating a diverse and healthy ecosystem + for such services. + +1.1. Scope + + This document provides a specification for the request and response + message format that a client can use to communicate with remote + services that allows the client to offload much of the work involved + in running an advertisement selection auction as part of the client's + implementation of the Protected Audience API. + + This document does not describe distribution of private keys to the + Bidding and Auction services. + +1.2. Terminology + + The key word "client" is to be interpreted as an implementation of + this document that creates Requests (Section 2.2) and consumes + Responses (Section 2.3). The key phrase "Bidding and Auction + Services" is to be interpreted as an implementation of this document + that consumes Requests and creates Responses. + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and + "OPTIONAL" in this document are to be interpreted as described in BCP + 14 [RFC2119] [RFC8174] when, and only when, they appear in all + capitals, as shown here. + +2. Message Format Specifications + +2.1. Overview + + To understand this document, it is important to know that the + communication between the client and the remote services uses a + request-response message exchange pattern. The request will first + reach a seller service, after which the seller will forward parts of + the request to buyer service. It is then up to the seller service to + gather buyer responses and form a final response for the client. + More detail about the seller and buyer services can be found in the + server-side system design documentation + (https://github.com/privacysandbox/protected-auction-services- + docs/blob/main/bidding_auction_services_system_design.md). + +2.1.1. Common Definitions + + Section 2 makes frequent use of the following definitions. + + +=========================================+==============+ + | Term with CDDL Definition | Detailed | + | | Reference | + +=========================================+==============+ + | json = tstr | [JSON] | + +-----------------------------------------+--------------+ + | uuid = tstr .regexp "[a-fA-F0-9]{8}-[a- | [UUID] | + | fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA- | | + | F0-9]{4}-[a-fA-F0-9]{12}" | | + +-----------------------------------------+--------------+ + | origin = tstr .regexp | [ORIGIN] | + | "https://([^/:](:[0-9]+)?/" | | + +-----------------------------------------+--------------+ + | currency = tstr .size 3 .regexp | [ISO4217] | + | /^[A-Z]{3}$/ | | + +-----------------------------------------+--------------+ + | adRenderUrl = tstr | [URL] | + +-----------------------------------------+--------------+ + | adRenderId = tstr | [ADRENDERID] | + +-----------------------------------------+--------------+ + | interestGroupOwner = origin | [IGOWNER] | + +-----------------------------------------+--------------+ + + Table 1 + +2.1.2. Message Framing and Padding + + Bidding and Auction Services requests and responses have the + following framing: + + +==========+=========+=============+========+=============+=========+ + | Byte | 0 | 0 | 1 to 4 | 5 to | Size+5 | + | | | | | Size+4 | to end | + +==========+=========+=============+========+=============+=========+ + | Bits | 7-5 | 4-0 | * | * | * | + +----------+---------+-------------+--------+-------------+---------+ + | Contents | Version | Compression | Size | Request | Padding | + | | | | | Payload | | + +----------+---------+-------------+--------+-------------+---------+ + + Table 2 + + where the the first 3 bits of the frame header specify the payload + version and the following 5 bits specify the compression algorithm. + The format described in this document corresponds to version 0. + + The compression method's value in bits 4-0 in Section 2.2.2 + corresponds to the below table: + + +=============+==================+ + | Compression | Description | + +=============+==================+ + | 0 | No Compression | + +-------------+------------------+ + | 1 | Brotli [RFC7932] | + +-------------+------------------+ + | 2 | GZIP [RFC1952] | + +-------------+------------------+ + | 3-31 | Reserved | + +-------------+------------------+ + + Table 3 + + The amount of padding depends on the type of message and will be + discussed for each message type separately. + + GZIP MUST be implemented by the Client and the Bidding and Auction + Services, while Brotli MAY be. + +2.2. Request Format + + This section discusses the request message sent from the client to + the Bidding and Auction Services endpoint. + + The request from the Client to Bidding and Auction Services consists + of an [HPKE] encrypted payload with attached header (see + Section 2.2.1). The plaintext payload contains a framing header, + outer request, and padding (see Section 2.2.2). The request message + Section 2.2.3 is a [CBOR] encoded message that contains one or more + compressed interest group lists Section 2.2.3.1. + +2.2.1. Encryption + + The request is encrypted with [HPKE]. The request uses a similar + encapsulated message format to that used by [OHTTP], with only an + additional version field. + + Encapsulated Request { + Version (8), + Key Identifier (8), + HPKE KEM ID (16), + HPKE KDF ID (16), + HPKE AEAD ID (16), + Encapsulated KEM Shared Secret (8 * Nenc), + HPKE-Protected Request (..), + } + + The Version for this message SHOULD be 0. The HPKE KEM ID, HPKE KDF + ID, and HPKE AEAD ID are the key encapsulation mechanism, key + derivation function and authenticated encryption with associated data + function parameters for [HPKE]. For this protocol, a compliant + implementation MUST support DHKEM(X25519, HKDF-SHA256) (0x0020) for + HPKE KEM ID, HKDF-SHA256 (0x0001) for HPKE KDF ID, and AES-256-GCM + (0x0002) for HPKE AEAD ID. + + Encryption of the request is similar to in [OHTTP] Section 4.3 + (https://www.rfc-editor.org/rfc/rfc9458#name-encapsulation-of- + requests), only with a different media type and the Version prepended + to the encrypted message: + + 1. Construct a message header (hdr) by concatenating the values of + the Key Identifier, HPKE KEM ID, HPKE KDF ID, and HPKE AEAD ID in + network byte order. + + 2. Build a sequence of bytes (info) by concatenating the ASCII- + encoded string "message/auction request", a zero byte, and hdr. + + 3. Create a sending HPKE context by invoking SetupBaseS() + (Section 5.1.1 (https://rfc-editor.org/rfc/rfc9180#section-5.1.1) + of [HPKE]) with the public key of the receiver pkR and info. + This yields the context sctxt and an encapsulation key enc. + + 4. Encrypt request by invoking the Seal() method on sctxt + (Section 5.2 (https://rfc-editor.org/rfc/rfc9180#section-5.2) of + [HPKE]) with empty associated data aad, yielding ciphertext ct. + + 5. Concatenate the values of Version, hdr, enc, and ct. + + In pseudocode, this procedure is as follows: + + hdr = concat(encode(1, key_id), + encode(2, kem_id), + encode(2, kdf_id), + encode(2, aead_id)) + info = concat(encode_str("message/auction request"), + encode(1, 0), + hdr) + enc, sctxt = SetupBaseS(pkR, info) + ct = sctxt.Seal("", request) + enc_request = concat(encode(1, version), hdr, enc, ct) + + A Bidding and Auction Services endpoint decrypts this encapsulated + message in a similar manner to [OHTTP] Section 4.3 (https://www.rfc- + editor.org/rfc/rfc9458#name-encapsulation-of-requests), or more + explicitly as follows: + + 1. Parse enc_request into version, key_id, kem_id, kdf_id, aead_id, + enc, and ct. + + 2. If version is not 0, return an error. + + 3. Find the matching HPKE private key, skR, corresponding to key_id. + If there is no matching key, return an error. + + 4. Build a sequence of bytes (info) by concatenating the ASCII- + encoded string "message/auction request"; a zero byte; key_id as + an 8-bit integer; plus kem_id, kdf_id, and aead_id as three + 16-bit integers. + + 5. Create a receiving HPKE context, rctxt, by invoking SetupBaseR() + (Section 5.1.1 (https://rfc-editor.org/rfc/rfc9180#section-5.1.1) + of [HPKE]) with skR, enc, and info. + + 6. Decrypt ct by invoking the Open() method on rctxt (Section 5.2 + (https://rfc-editor.org/rfc/rfc9180#section-5.2) of [HPKE]), with + an empty associated data aad, yielding request and returning an + error on failure. + + In pseudocode, this procedure is as follows: + + version, key_id, kem_id, kdf_id, aead_id, enc, ct = parse(enc_request) + if version != 0 then return error + info = concat(encode_str("message/auction request"), + encode(1, 0), + encode(1, key_id), + encode(2, kem_id), + encode(2, kdf_id), + encode(2, aead_id)) + rctxt = SetupBaseR(enc, skR, info) + request, error = rctxt.Open("", ct) + + Bidding and Auction Services retains the HPKE context, rctxt, so that + it can encapsulate a response. + +2.2.2. Framing and Padding + + The plaintext message uses the framing described in Section 2.1.2. + + Messages MAY be zero padded so that the encrypted request is one of + the following bin sizes: 0KiB, 5KiB, 10KiB, 20KiB, 30KiB, 40KiB, + 55KiB. An implementation MAY need to remove some data from the + payload to fit inside the largest bucket. + + A compatible implementation processing requests SHOULD NOT rely on a + specific padding scheme for requests. + +2.2.3. Request Message + + The request message is a [CBOR] encoded message with the following + [CDDL] schema: + + request = { + ; Current version of the protocol. + ; In this document, it must be 0. + version: int, + ; Used by the Bidding and Auction Services to + ; keep track of a request (and corresponding response) + ; over its lifetime. + ; Must be a [UUID][Version 4]. + generationId: uuid, + ; Represents the publisher initiating the request. + publisher: origin, + interestGroups: { + ; Map of interest group owner to CBOR encoded list of interest + ; groups compressed as described in § Generating a Request. + * interestGroupOwner => bstr + }, + ? enableDebugReporting: bool + } + + The version field SHOULD be set to 0 for this version of the + protocol. The interestGroups field is a map from interest group + owner to a compressed, [CBOR]-encoded list of interest groups for + that owner. + +2.2.3.1. List of Interest Groups + + A list of interest group is encoded in [CBOR] with the following + [CDDL] schema: + + interestGroups = [ * interestGroup ] + interestGroup = { + ; This interest group's name, see + ; https://wicg.github.io/turtledove/#interest-group-name. + name: tstr, + + ; Keys used to look up real-time bidding signals, see + ; https://wicg.github.io/turtledove/#interest-group-trusted-bidding-signals-keys. + ? biddingSignalsKeys: [* tstr], + ; Data about the user that the bidder can use during bid calculation, see + ; https://wicg.github.io/turtledove/#interest-group-user-bidding-signals. + ? userBiddingSignals: json, + ; Contains various ads that the interest group might show. See + ; https://wicg.github.io/turtledove/#interest-group-ads. + ? ads: [* adRenderId], + + ; Contains various ad components (or "products") that can be used to + ; construct ads composed of multiple pieces — a top-level ad template + ; "container" which includes some slots that can be filled in with + ; specific "products". See + ; https://wicg.github.io/turtledove/#interest-group-ad-components. + ? components: [* adRenderId], + ? browserSignals: { + ; Number of times the group was joined in the last 30 days. + ? joinCount: int, + + ; Number of times the group bid in an auction in the last 30 + ; days. + ? bidCount: int, + + ; Tuple of time-ad pairs for a previous win for this interest + ; group that occurred in the last 30 days. + ; The time is specified in seconds before the containing + ; auctionBlob was requested. + ? prevWins: [* [int, adRenderId]], + + ; The most recent join time for this group expressed + ; in milli seconds before the containing auctionBlob + ; was requested. This field will be used by newer client + ; versions. For older devices, the precison will be in seconds. + ; If recencyMs is present, this value will be used to offer + ; higher precision. If not, recency will be used. Only + ; one of the recency or recencyMs is expected to present in + ; the request. + ? recencyMs: int + } + } + + Each list is separately compressed with the compression method + indicated in the Section 2.1.2 header. + +2.2.4. Generating a Request + + This section describes how the client MAY form and serialize request + messages in order to communicate with the Bidding and Auction + services. + + This algorithm takes as input all of the relevant interest groups, a + config consisting of the publisher, a map of from (origin, string) + tuple to origin ig pagg coordinators, an optional desired total size, + an optional boolean debugging report locked out defaults to false, an + optional list of interest group owners to include each with an + optional desired size, and the [HPKE] public key with its associated + key ID. It returns an encrypted request and a request context tuple. + + 1. Let included_groups be an empty map. + + 2. If desired total size is not specified, but the list of interest + group owners includes at least one entry with a specified + desired size: + + 1. Set desired total size to the sum of all specified desired + size in the list of interest group owners. + + 3. Group the list of relevant interest groups by owner into a map + of from interest group owner to a list of interest groups sorted + by decreasing priority, interest group map. + + 4. If the list of interest group owners is specified, remove + interest groups whose owner is not on the list. + + 5. 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 debugging report locked out. + + 6. Set current_size to be the serialized size of the encrypted + request created from request without padding. + + 7. Set remaining_allocated_size to 0. + + 8. Set remaining_unsized_owners to 0. + + 9. For each interest group owner, interest group list in interest + group map: + + 1. If there is a desired size for interest group owner: + + 1. Increment remaining_allocated_size by desired size. + + 2. Otherwise + + 1. Increment remaining_unsized_owners by 1. + + 10. For each interest group owner, interest group list in interest + group map where there is a desired size specified for interest + group owner: + + 1. If the number of unsized_owners is not 0: + + 1. Set the allowed_interest_group_size to the desired size + for this interest group owner. This is a fixed size + allocation. + + 2. Otherwise: + + 1. Let remaining_size be equal to the desired total size- + current_size. + + 2. Set the allowed_interest_group_size to + remaining_size*desired_size/remaining_allocated_size. + This is a proportional allocation. + + 3. Set remaining_allocated_size = remaining_allocated_size- + current_size. + + 4. [CBOR] encode the interest group list into serialized list. + + 5. [GZIP] the serialized list into compressed list. + + 6. If setting request["interestGroups"][interest group owner] + to compressed list would make it's serialized size more than + allowed_interest_group_size larger than the current size, + then remove the lowest priority interest group and repeat + from the previous step. + + 7. Set request["interestGroups"][interest group owner] to + compressed list. + + 8. Set included_groups[interest group owner] to interest group + list. + + 9. Set current_size to be the serialized size of the encrypted + request created from request without padding. + + 11. For each interest group owner, interest group list in interest + group map where there is not desired size specified for interest + group owner: + + 1. Let remaining_size be equal to the desired total size- + current_size. + + 2. Set the allowed_interest_group_size to + remaining_size*/remaining_unsized_owners. This is a equal + size allocation. + + 3. Decrement remaining_unsized_owners by 1. + + 4. [CBOR] encode the interest group list into serialized list. + + 5. [GZIP] the serialized list into compressed list. + + 6. If adding the compressed list to request would make it more + than allowed_interest_group_size larger than the current + size, then remove the lowest priority interest group and + repeat from the previous step. + + 7. Set request["interestGroups"][interest group owner] to + compressed list. + + 8. Set included_groups[interest group owner] to interest group + list. + + 9. Set current_size to be the serialized size of the encrypted + request created from request without padding. + + 12. If there are no interest groups in the request, discard the + request and return failure. + + 13. Prepend the framing header to request with Compression set to 2. + + 14. If desired total size is set then zero pad request to desired + total size. Otherwise zero pad request up to the smallest bin + size in Section 2.2.2 larger than request. + + 15. Encrypt request using the public key and its key id as in + Section 2.2.1 to get the encrypted message and hpke context. + + 16. Let the request context be the tuple (included_groups, hpke + context, ig pagg coordinators). + + 17. Return the encrypted message and the request context. + +2.2.5. Parsing a Request + + This section describes how the Bidding and Auction Services MUST + deserialize request messages from the client. + + The algorithm takes as input a serialized request message from the + client (Section 2.2.4) and a list of HPKE private keys (along with + their corresponding key IDs). + + The output is either an error sent back to the client, an empty + message sent back to the client, or a request message the Bidding and + Auction services can consume along with an HPKE context. + + 1. Let encrypted request be the request received from the client. + + 2. Let error_msg be an empty string. + + 3. De-encapsulate and decrypt encrypted request by using the input + private key corresponding to key_id, as described in + Section 2.2.1, to get the decrypted message and rctxt. + + 1. If decapsulation or decryption fails, return failure. + + 2. Else, save the decrypted output as framed request and save + rctxt. + + 4. Remove and extract the first 5 bytes from framed request as the + framing header (described in Section 2.1.2), removing them from + framed request. + + 5. If the framing header's Version field is not 0, return failure. + + 6. If the framing header's Compression field is not supported, + return failure. Otherwise, save the Compression field value as + compression type. + + 7. Let length be equal to the framing header's Size field. + + 8. If length is greater than the length of the remaining bytes in + framed request, return failure. + + 9. Take the first length remaining bytes in framed response as + decodable request, discarding the rest. + + 10. [CBOR] decode decodable request into the message represented in + Section 2.2.3. Let this be request. + + 11. If [CBOR] decoding fails, return failure. + + 12. Let processed request be an empty struct. + + 13. If request is not a map, return failure. + + 14. If request["version"] does not exist or is not 0, return + failure. + + 15. If request["publisher"] does not exist or is not a string, + return failure. + + 16. Set processed request["publisher"] to request["publisher"]. + + 17. If request["generationId"] does not exist or is not a string, + return failure. + + 18. Set processed request["generationId"] to + request["generationId"]. + + 19. If request["enableDebugReporting] exists: + + 1. If request["enableDebugReporting"] is not a boolean, return + failure. + + 2. Set processed request["enableDebugReporting"] to + request["enableDebugReporting"]. + + 20. If request["interestGroups] does not exist or is not a map, + return failure. + + 21. Set processed request["interestGroups"] to an empty map. + + 22. For each key, value map entry of request["interestGroups"]: + + 1. If key is not a string, append an error message to + error_msg. Proceed to Section 2.2.5.1. + + 2. Set processed request["interestGroups"][key] to an empty + list. + + 3. Decompress value according to compression type and set as + buyer input cbor. If decompression fails, return failure. + + 4. [CBOR] decode buyer input cbor into buyer input. If + decoding fails, return failure. + + 5. If buyer input is not an array, return failure. + + 6. For each interest group in buyer input: + + 1. If the interest groups is not a map, append an error + message to error_msg. Proceed to Section 2.2.5.1. + + 2. Let ig be an empty struct similar to Section 2.2.3.1. + + 3. If interest group["name"] does not exist or is not a + string, return failure. + + 4. Set ig["name"] to interest group["name"]. + + 5. If interest group["userBiddingSignals"] exists: + + 1. If interest group["userBiddingSignals"] is not a + string, return failure. + + 2. Set ig["userBiddingSignals"] to interest + group["userBiddingSignals"]. + + 6. If interest group["biddingSignalsKeys"] exists: + + 1. If interest group["biddingSignalsKeys"] is not an + array of strings, return failure. + + 2. Set ig["biddingSignalsKeys"] to interest + group["biddingSignalsKeys"]. + + 7. If interest group["ads"] exists: + + 1. If interest group["ads"] is not an array of + strings, return failure. + + 2. Set ig["ads"] to interest group["ads"]. + + 8. If interest group["component"] exists: + + 1. If interest group["component"] is not an array of + strings, return failure. + + 2. Set ig["component"] to interest group["component"]. + + 9. If interest group["browserSignals"] exists: + + 1. If interest group["browserSignals"] is not a map, + return failure. + + 2. Let igbs be an empty struct similar to + browserSignals as defined in Section 2.2.3.1. + + 3. Let signals be interest group["browserSignals"]. + + 4. If signals["bidCount"] exists: + + 1. If signals["bidCount"] is not a valid 64-bit + unsigned integer, return failure. + + 2. Set igbs["bidCount"] to signals["bidCount"]. + + 5. If signals["joinCount"] exists: + + 1. If signals["joinCount"] is not a valid 64-bit + unsigned integer, return failure. + + 2. Set igbs["joinCount"] to signals["joinCount"]. + + 6. If signals["recencyMs"] exists: + + 1. If signals["recencyMs"] is not a valid 64-bit + unsigned integer, return failure. + + 2. Set igbs["recencyMs"] to signals["recencyMs"]. + + 7. If signals["prevWins"] exists: + + 1. Let pw be an empty array. + + 2. If signals["prevWins"] is not an array, return + failure. + + 3. For each prevWinTuple in signals["prevWins"]: + + 1. Let pwt be an empty array. + + 2. If prevWinTuple is not an array of size 2, + return failure. + + 3. If prevWinTuple[0] is not a valid 64-bit + unsigned integer, return failure. + + 4. If prevWinTuple[1] is not a string, return + failure. + + 5. Set pwt to prevWinTuple. + + 6. Append pwt to pw. + + 4. Set igbs["prevWins"] to pw. + + 8. Set ig["browserSignals"] to igbs. + + 10. Append ig to processed request["interestGroups"][ key + ]. + + 23. Return processed request and rctxt to the Bidding and Auction + Services. + +2.2.5.1. Request Parse Error Handling + + If Section 2.2.5 returns with failure, the following algorithm + describes how the Bidding and Auction Services MUST respond. + + The input to this algorithm is error_msg, which MAY be returned from + the point of failure in Section 2.2.5. + + The output is a response to the client. + + 1. If the failure happens before or during decryption, respond with + an empty message. + + 2. Otherwise abort processing the request. + + 3. Let error be a new map with key-value pairs: [("code", 400), + ("error", error_msg)]. + + 4. Let response be a new Section 2.3.3. + + 5. Set response["error"] to error. + + 6. Serialize and send response to the client per Section 2.3. + +2.3. Response Format + + This section discusses the request message sent from the Bidding and + Auction Services endpoint to the client in reply to a request. + + The response from the Bidding and Auction Services endpoint consists + of an [HPKE] encrypted payload with attached header (see + Section 2.3.1). The plaintext payload contains a framing header, + response message, and padding (see Section 2.3.2). The response + message Section 2.3.3 is a compressed [CBOR] encoded message. + +2.3.1. Encryption + + The response uses a similar encapsulated response format to that used + by [OHTTP]. + + Encapsulated Response { + Nonce (8 * max(Nn, Nk)), + AEAD-Protected Response (..), + } + + Encryption of the response is similar to in [OHTTP] Section 4.4 + (https://www.rfc-editor.org/rfc/rfc9458.html#name-encapsulation-of- + responses), only with a different media type, repeated below for + clarity: + + 1. Export a secret (secret) from context, using the string "message/ + auction response" as the exporter_context parameter to + context.Export; see Section 5.3 (https://www.rfc-editor.org/rfc/ + rfc9180.html#name-secret-export) of [HPKE]. The length of this + secret is max(Nn, Nk), where Nn and Nk are the length of the AEAD + key and nonce that are associated with context. + + 2. Generate a random value of length max(Nn, Nk) bytes, called + response_nonce. + + 3. Extract a pseudorandom key (prk) using the Extract function + provided by the KDF algorithm associated with context. The ikm + input to this function is secret; the salt input is the + concatenation of enc (from enc_request) and response_nonce. + + 4. Use the Expand function provided by the same KDF to create an + AEAD key, key, of length Nk -- the length of the keys used by the + AEAD associated with context. Generating aead_key uses a label + of "key". + + 5. Use the same Expand function to create a nonce, nonce, of length + Nn -- the length of the nonce used by the AEAD. Generating + aead_nonce uses a label of "nonce". + + 6. Encrypt response, passing the AEAD function Seal the values of + aead_key, aead_nonce, an empty aad, and a pt input of response. + This yields ct. + + 7. Concatenate response_nonce and ct, yielding an Encapsulated + Response, enc_response. Note that response_nonce is of fixed + length, so there is no ambiguity in parsing either response_nonce + or ct. + + In pseudocode, this procedure is as follows: + + secret = context.Export("message/auction response", max(Nn, Nk)) + response_nonce = random(max(Nn, Nk)) + salt = concat(enc, response_nonce) + prk = Extract(salt, secret) + aead_key = Expand(prk, "key", Nk) + aead_nonce = Expand(prk, "nonce", Nn) + ct = Seal(aead_key, aead_nonce, "", response) + enc_response = concat(response_nonce, ct) + + Clients decrypt an Encapsulated Response by reversing this process. + That is, Clients first parse enc_response into response_nonce and ct. + Then, they follow the same process to derive values for aead_key and + aead_nonce, using their sending HPKE context, sctxt, as the HPKE + context, context. + + The Client uses these values to decrypt ct using the AEAD function + Open. Decrypting might produce an error, as follows: + + response, error = Open(aead_key, aead_nonce, "", ct) + +2.3.2. Framing and Padding + + The plaintext message uses the framing described in Section 2.1.2. + + Messages MAY be exponentially padded so that the encrypted response + is a power of 2 in length. + + A compatible implementation processing requests SHOULD NOT rely on a + specific padding scheme for requests. + +2.3.3. Response Message + + The response message is a [CBOR] encoded message, compressed using + the method indicated in {#request-framing}. The [CBOR] encoded + message SHOULD be serialized into deterministically encoded [CBOR] + (as defined in Section 4.2 (https://www.rfc-editor.org/rfc/ + rfc8949.html#name-deterministically-encoded-c)) and follows the + following [CDDL] schema: + + response = { + ; An unguessable nonce to use when authorizing this response. + ? nonce: tstr, + + ; The ad to render. + ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-ad-render-url. + adRenderURL: adRenderUrl, + + ; List of URLs for component ads displayed as part of this + ; ad. + ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-ad-components. + ; If not present, map as an empty list. + ? components: [* adRenderUrl], + + ; Name of the interest group to which the ad belongs. + ; See https://wicg.github.io/turtledove/#interest-group-name. + ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-interest-group-name. + ; If not present, map as Null. + ? interestGroupName: tstr, + + ; Origin of the Buyer who owns the interest group. + ; The original request for this response MUST contain this + ; interestGroupOwner, which additionally MUST provide an interest + ; group with interestGroupName. + ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-interest-group-owner. + ; If not present, map as Null. + ? interestGroupOwner: interestGroupOwner, + + ; Indices of interest groups in the original request for this owner + ; that submitted a bid. + ; Maps to https://wicg.github.io/turtledove/#server-auction-response-bidding-groups. + ; If not present, map as an empty list. + ; Else, + ; 1. create an empty list + ; 2. for each interest group owner key in the biddingGroups map + ; 3. for each index in biddingGroups[interest group owner] + ; 4. interest group name equals the string at the index from (3) + ; in Encryption Context's Interest Group Map (Section 2.2.4.1.2) + ; 5. add a tuple to the list in (1) of [interest group owner (2), + interest group name (4)] + ; 6. return the list in (1) + ? biddingGroups: { + * interestGroupOwner => [* int] + }, + + ; Indices and update-if-older-than times of interest groups in the original + ; request for this owner for interest groups where an update-if-older-than + ; time (in milliseconds) was specified. + ; Maps to https://wicg.github.io/turtledove/#server-auction-response-update-groups. + ? updateGroups: { + * interestGroupOwner => [ + * { + index: int, + updateIfOlderThanMs: int + }] + }, + + ; Score of the ad determined during the auction. + ; Any value that is zero or negative indicates that the ad cannot + ; win the auction. + ; The winner of the auction would be the ad that was given the + ; highest score. + ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-score. + ; If not present, map as Null. + ? score: float, + + ; Bid price corresponding to an ad + ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-bid. + ; If not present, map as Null. + ? bid: float, + + ; Optional currency of the bid. + ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-bid-currency. + ; If not present, map as Null. + ? bidCurrency: currency, + + ; Optional BuyerReportingId of the winning Ad + ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-buyer-reporting-id. + ; If not present, map as Null. + ? buyerReportingId: tstr, + + ; Optional BuyerAndSellerReportingId of the winning Ad + ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-buyer-and-seller-reporting-id. + ; If not present, map as Null. + ? buyerAndSellerReportingId: tstr, + + ; Optional SelectedBuyerAndSellerReportingId of the winning Ad + ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-selected-buyer-and-seller-reporting-id + ; If not present, map as Null. + ? selectedBuyerAndSellerReportingId: tstr, + + ; The auction result may be ignored if set to true. + ; Maps to https://wicg.github.io/turtledove/#server-auction-response-is-chaff. + ; If not present, map as false. + ? isChaff: bool, + + ; Optional wrapper for various reporting URLs. + ? winReportingUrls: { + ; Maps to https://wicg.github.io/turtledove/#server-auction-response-buyer-reporting. + ; If not present, map as 'Null'. + ? buyerReportingUrls: reportingUrls, + ; Maps to https://wicg.github.io/turtledove/#server-auction-response-component-seller-reporting. + ; If not present, map as 'Null'. + ? componentSellerReportingUrls: reportingUrls, + ; Maps to https://wicg.github.io/turtledove/#server-auction-response-top-level-seller-reporting. + ; If not present, map as 'Null'. + ? topLevelSellerReportingUrls: reportingUrls + }, + + ; Contains an error message from the auction executed on the trusted auction + ; server. + ; May be used to provide additional context for the result of an auction. + ; Maps to https://wicg.github.io/turtledove/#server-auction-response-error. + ; If not present, map as Null. + ; Else, ignore the `code` field and use the `message` field directly + ; for the server auction response error field. + ? error: { + code: int, + message: tstr + }, + + ; Arbitrary metadata to pass to the top-level seller. + ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-ad-metadata. + ; If not present, map as Null. + ? adMetadata: json, + + ; Optional name/domain for the top-level seller in case this is a + ; component auction. + ; Maps directly to https://wicg.github.io/turtledove/#server-auction-response-top-level-seller. + ; If not present, map as Null. + ? topLevelSeller: origin, + + ; Optional list of forDebuggingOnly reports. + ; If not present, map as an empty list. + ; Maps to https://wicg.github.io/turtledove/#server-auction-response-component-win-debugging-only-reports + ; and https://wicg.github.io/turtledove/#server-auction-response-server-filtered-debugging-only-reports. + ? debugReports: [ + * { + origin => [ + * { + url tstr, + ? bool isWinReport, + ? bool isSellerReport, + ? bool componentWin + + ; Optional list of private aggregation contributions. + ; If not present, map as an empty list. + ? paggResponse: [ + * { + origin => [ + * { + ? igIndex: int, + ? coordinator: origin, + ? componentWin: bool, + eventContributions: [ + tstr => [ + * { + bucket: blob, + value: int + } + ] + ] + } + ] + } + ], + } + + ; Defines the structure for reporting URLs. + reportingUrls = { + ; Maps directly to https://wicg.github.io/turtledove/#server-auction-reporting-info-reporting-url. + ; If not present, map as Null. + ? reportingUrl: tstr, + ; Maps directly to https://wicg.github.io/turtledove/#server-auction-reporting-info-beacon-urls. + ; If not present, map as an empty ordered map (https://infra.spec.whatwg.org/#ordered-map). + ? interactionReportingUrls: { * tstr => tstr } + } + +2.3.4. Generating a Response + + This algorithm describes how conforming Bidding and Auction Services + MAY generate a response to a request. + + The input is a payload corresponding to Section 2.3.3 and the HPKE + receiver context saved in Section 2.2.5, rctxt. + + The output is a response to be sent to a Client. + + 1. Let cbor payload equal the deterministically encoded CBOR + (https://www.rfc-editor.org/rfc/rfc8949.html#name- + deterministically-encoded-c) payload. Return an empty response + on CBOR encoding failure. + + 2. Let compressed payload equal the [GZIP] compressed cbor payload, + returning an empty response on compression failure. + + 3. Create a framed payload, as described in Section 2.3.2: + + 1. Create a framing header. + + 2. Set the framing header Compression to 2. + + 3. Set the framing header Version to 0. + + 4. Set the framing header Size to the size of compressed + payload. + + 5. Let framed payload equal the result of prepend the framing + header to compressed payload. + + 6. Padding MAY be added to framing header, as described in + Section 2.3.2. + + 7. Return an empty response on failure of any of the previous + steps. + + 4. Let response equal the result of the encryption and encapsulation + of framed payload with rctxt, as described in Section 2.3.1. + Return an empty response on failure. + + 5. Return response. + +2.3.5. Parsing a Response + + This algorithm describes how a conforming Client MUST parse and + validate a response from Bidding and Auction Services. It takes as + input the request context tuple returned from Section 2.2.4 in + addition to the encrypted response. + + 1. Use request context's hpke context as the context for decryption + and follow the decryption steps in Section 2.3.1 to decrypt + encrypted response and obtain framed response and error. + + 2. If error is not null, return failure. + + 3. Remove and extract the first 5 bytes from framed response as the + framing header (described in Section 2.1.2), removing them from + framed response. + + 4. If the framing header's Version field is not 0, return failure. + + 5. Let length be equal to the framing header's Size field. + + 6. If length is greater than the length of the remaining bytes in + framed response, return failure. + + 7. Take the first length remaining bytes in framed response as + compressed response, discarding the rest. + + 8. Decompress the compressed response into serialized response + using the method indicated by framing header's Compression + field, returning failure if decompression fails. + + 9. [CBOR] decode the serialized response into response, returning + failure if decompression fails. + + 10. If response is not a map, return failure. + + 11. If response["error"] exists, return failure. + + 12. If response["isChaff"] exists and is either not a boolean or is + true, return failure. + + 13. Let processed response be a new structure analogous to server + auction response (https://wicg.github.io/turtledove/#server- + auction-response). + + 14. If response["nonce"] exists and is a valid [UUID], set processed + response["nonce"] to response["nonce"]. + + 15. If response["adRenderURL"] does not exist, return failure. + + 16. Set processed response["ad render url"] to + response["adRenderURL"] parsed as a [URL], returning failure if + there is an error. + + 17. If response["components"] exists: + + 1. If response["components"] is not an array, return failure. + + 2. 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. + + 18. If response["interestGroupName"] does not exist or is not a + string, return failure. + + 19. Set processed response["interest group name"] to + response["interestGroupName"]. + + 20. If response["interestGroupOwner"] does not exist or is not a + string, return failure. + + 21. Set processed response["interest group owner"] to + response["interestGroupOwner"] parsed as an [ORIGIN], returning + failure if there is an error. + + 22. If response["biddingGroups"] does not exist or is not a map, + return failure. + + 23. 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. + + 2. If request context's included_groups does not contain owner + as a key, return failure. + + 3. If value is not a list, return failure. + + 4. For each element in value: + + 1. If element is not an integer or element < 0, return + failure. + + 2. If element is greater than or equal to the length of + included_groups[owner], return failure. + + 3. Let name be the interest group name for + included_groups[owner][element]. + + 4. Append the tuple (owner, name) to processed + response["bidding groups"]. + + 24. 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. + + 2. If request context's included_groups does not contain + owner as a key, continue the next iteration of this + loop. + + 3. If value is not a list, return failure. + + 4. For each element in value: + + 1. If element is not a map, continue the next iteration + of this loop. + + 2. 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. + + 3. If element["index"] is not an integer or + element["index"] < 0, continue the next iteration of + this loop. + + 4. If element["index"] is greater than or equal to the + length of included_groups[owner], continue the next + iteration of this loop. + + 5. Let name be the interest group name for + included_groups[owner][element]. + + 6. Let interest group key be the tuple (owner, name). + + 7. Let update duration be + element["updateIfOlderThanMs"], parsed into a time + duration as integer milliseconds. + + 8. Set processed response["update groups"][intereset + group key] to update duration. + + 25. If response["score"] exists: + + 1. If response["score"] is not a floating point value, return + failure. + + 2. Set processed response["score"] to response["score"]. + + 26. If response["bid"] exists: + + 1. If response["bid"] is not a floating point value, return + failure. + + 2. Let bid be a new structure analogous to bid with currency + (https://wicg.github.io/turtledove/#bid-with-currency). + + 3. Set bids value field to response["bid"]. + + 4. If response["bidCurrency"] exists: + + 1. If response["bidCurrency"] is not a string, return + failure. + + 2. If response["bidCUrrency"] is not 3 bytes long or + contains characters other than upper case ASCII letters, + return failure. + + 3. Set bid's currency field to response["bidCurrency"]. + + 5. Set processed response["bid"] to bid. + + 27. If response["winReportingURLs"] exists and is a map: + + 1. If response["winReportingURLs"]["buyerReportingURLs"] + exists: + + 1. Let buyer reporting be the result of Section 2.3.5.1 on + response["winReportingURLs"]["buyerReportingURLs"]. + + 2. Set processed response["buyer reporting"] to buyer + reporting. + + 2. If + response["winReportingURLs"]["topLevelSellerReportingURLs"] + exists: + + 1. Let top level seller reporting be the result of + Section 2.3.5.1 on response["winReportingURLs"]["topLeve + lSellerReportingURLs"]. + + 2. Set processed response["top level seller reporting"] to + top level seller reporting. + + 3. If + response["winReportingURLs"]["componentSellerReportingURLs"] + exists: + + 1. Let component seller reporting be the result of + Section 2.3.5.1 on response["winReportingURLs"]["compone + ntSellerReportingURLs"]. + + 2. Set processed response["component seller reporting" to + component seller reporting. + + 28. If response["topLevelSeller"] exists: + + 1. If response["topLevelSeller"] is not a string, return + failure. + + 2. Set processed response["top level seller"] to + response["topLevelSeller"] parsed as a [URL], returning + failure if there is an error. + + 29. If response["adMetadata"] exists and is a string set processed + response["ad metadata"] to response["adMetadata"]. + + 30. If response["buyerReportingId"] exists and is a string, set + processed response["buyer reporting id"] to + response["buyerReportingId"]. + + 31. If response["buyerAndSellerReportingId"] exists and is a string, + set processed response["buyer and seller reporting id"] to + response["buyerAndSellerReportingId"]. + + 32. If response["selectedBuyerAndSellerReportingId"] exists and is a + string, set processed response["selected buyer and seller + reporting id"] to response["selectedBuyerAndSellerReportingId"]. + + 33. If response["debugReports"] exists and is an array: + + 1. For each per origin debug reports in + response["debugReports"]: + + 1. If per origin debug reports["adTechOrigin"] does not + exist or is not a string, continue with the next + iteration. + + 2. Let ad tech origin be per origin debug reports[ + "adTechOrigin"] parsed as an [ORIGIN], continue with the + next iteration if there is an error. + + 3. If per origin debug reports["reports"] does not exist or + is not an array, continue with the next iteration. + + 4. For each report in per origin debug reports["reports"]: + + 1. If report is not a map, continue with the next + iteration. + + 2. Let component win be report["componentWin"] if it + exists and is a bool, otherwise false. + + 3. If report["url"] exists and is a string: + + 1. Let url be report["url"] parsed as a [URL], or + continue with the next iteration if there is an + error. + + 2. If component win is false, set processed + response["server filtered debugging only + reports"][ad tech origin] to url, and continue + with the next iteration. + + 3. Let debug report key be a new structure + analogous to server auction debug report key + (https://wicg.github.io/turtledove/#server- + auction-debug-report-key). + + 4. Set debug report key["from seller"] to + report["isSellerReport"] if it exists and is a + bool, otherwise false. + + 5. Set debug report key["is debug win"] to + report["isWinReport"] if it exists and is a + bool, otherwise false. + + 6. Set processed response[ "component win debugging + only reports"][ debug report key] to url. + + 4. Otherwise: + + 1. If component win is false and processed + response[ "server filtered debugging only + reports"] does not contain ad tech origin, set + processed response[ "server filtered debugging + only reports"][ ad tech origin] to an empty + list. + + 34. If response["paggResponse"] exists and is an array: + + 1. For each per origin response in pagg response: + + 1. If per origin response is not a map, continue with the + next iteration. + + 2. If per origin response["reportingOrigin"] does not exist + or is not a string, continue with the next iteration. + + 3. Let reporting origin be per origin + response["reportingOrigin"] parsed as an [ORIGIN], + continue with the next iteration if there is an error. + + 4. If per origin response["igContributions"] does not exist + or is not an array, continue with the next iteration. + + 5. Let names be an empty array. + + 6. If request context's included_groups contains owner as a + key, set names to its value. + + 7. For each ig contribution in per origin + response["igContributions"]: + + 1. If ig contribution is not a map, continue with the + next iteration. + + 2. Let coordinator be null. + + 3. If ig contribution["coordinator"] exists and is a + string, set coordinator to per origin + response["reportingOrigin"] parsed as an [ORIGIN], + continue with the next iteration if there is an + error. + + 4. Otherwise if ig contribution["igIndex"] exists and + is an integer: + + 1. If ig contribution["igIndex"] < 0 or is greater + than or equal to the length of names, continue + with the next iteration. + + 2. Let ig key be the tuple (owner, names[igIndex]). + + 3. If request context's ig pagg coordinators + contains ig key, set coordinator to request + context's ig pagg coordinators[ig key]. + + 5. Let is component win be false. + + 6. If ig contribution["componentWin"] exists and is a + boolean, set is component win to it. + + 7. If ig contribution["eventContributions"] exists and + is an array: + + 1. For each event contribution in ig + contribution["eventContributions"]: + + 1. Continue with the next iteration if any of + the following conditions hold: + + * event contribution is not a map; + + * event contribution["event"] does not + exist or is not a string; + + * event contribution["event"] starts with + "reserved.", but is not one of + "reserved.win", "reserved.loss", or + "reserved.always", continue with the next + iteration. + + 2. Let event be event contribution["event"]. + + 3. If event contribution["contributions"] + exists and is an array, for each + contribution in it: + + 1. Continue with the next iteration if any + of the following conditions hold: + + * contribution is not a map; + + * contribution["bucket"] does not exist + or is not a byte array or its size is + greater than 16. + + * contribution["value"] does not exist + or is not an integer. + + 2. Let private aggregation contribution be + a new structure analogous to + [PAExtendedHistogramContribution] + (https://wicg.github.io/ + turtledove/#dictdef- + paextendedhistogramcontribution). + + 3. Set private aggregation + contribution["bucket"] to + contribution["bucket"] parsed as a big + endian integer. + + 4. Set private aggregation + contribution["value"] to + contribution["value"]. + + 5. If is component win is true: + + 1. Let key be a new structure analogous + to [server auction private + aggregation contribution key] + (https://wicg.github.io/ + turtledove/#server-auction-private- + aggregation-contribution-key). + + 2. Set key["reporting origin"] to + reporting origin. + + 3. Set key["coordinator"] to + coordinator. + + 4. Set key["event"] to event. + + 5. If processed response["component win + private aggregation contributions"] + does not contain key, set processed + response["component win private + aggregation contributions"][key] to + a new array. + + 6. Append private aggregation + contribution to processed + response["component win private + aggregation contributions"][key]. + + 6. Otherwise if event contribution["event"] + starts with "reserved.", append private + aggregation contribution to processed + response["server filtered private + aggregation contributions + reserved"][key]. + + 7. Otherwise, append private aggregation + contribution to processed + response["server filtered private + aggregation contributions non + reserved"][key]. + + 35. Return processed response. + +2.3.5.1. Parsing reporting URLs + + To parse reporting URLs on a [CBOR] map reporting URLs with a schema + like reportingUrls from Section 2.3.3: + + 1. Let processed reporting URLs be a new structure analogous to + server auction reporting info (https://wicg.github.io/ + turtledove/#server-auction-reporting-info). + + 2. If reporting URLs["reportingURL"] exists and is a string: + + 3. Let reporting URL be reporting URLs["reportingURL"] parsed as a + [URL], or null if there is an error. + + 4. If reporting URL is not null, set processed reporting + URLs["reporting url"] to reporting URL. + + 5. If reporting URLs["interactionReportingURLs"] exists and is a + map: + + 6. 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. + + 7. Return processed reporting URLs. + +3. Security Considerations + + TODO + +4. IANA Considerations + + This document introduces no additional considerations for IANA. + +5. Normative References + + [ADRENDERID] + "Protected Audience", 2024, . + + [CBOR] Bormann, C. and P. Hoffman, "Concise Binary Object + Representation (CBOR)", STD 94, RFC 8949, + DOI 10.17487/RFC8949, December 2020, + . + + [CDDL] Birkholz, H., Vigano, C., and C. Bormann, "Concise Data + Definition Language (CDDL): A Notational Convention to + Express Concise Binary Object Representation (CBOR) and + JSON Data Structures", RFC 8610, DOI 10.17487/RFC8610, + June 2019, . + + [GZIP] Deutsch, P., "GZIP file format specification version 4.3", + RFC 1952, DOI 10.17487/RFC1952, May 1996, + . + + [HPKE] Barnes, R., Bhargavan, K., Lipp, B., and C. Wood, "Hybrid + Public Key Encryption", RFC 9180, DOI 10.17487/RFC9180, + February 2022, . + + [IGOWNER] "Protected Audience", 2024, . + + [ISO4217] "ISO 4217 Currency codes", 2024, + . + + [JSON] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data + Interchange Format", STD 90, RFC 8259, + DOI 10.17487/RFC8259, December 2017, + . + + [OHTTP] Thomson, M. and C. A. Wood, "Oblivious HTTP", RFC 9458, + DOI 10.17487/RFC9458, January 2024, + . + + [ORIGIN] "HTML Living Standard", 2024, + . + + [RFC1952] Deutsch, P., "GZIP file format specification version 4.3", + RFC 1952, DOI 10.17487/RFC1952, May 1996, + . + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, + DOI 10.17487/RFC2119, March 1997, + . + + [RFC7932] Alakuijala, J. and Z. Szabadka, "Brotli Compressed Data + Format", RFC 7932, DOI 10.17487/RFC7932, July 2016, + . + + [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC + 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, + May 2017, . + + [URL] "URL Living Standard", 2024, + . + + [UUID] Davis, K., Peabody, B., and P. Leach, "Universally Unique + IDentifiers (UUIDs)", RFC 9562, DOI 10.17487/RFC9562, May + 2024, . + +Acknowledgments + + TODO + +Authors' Addresses + + Daniel Kocoj + Google + Email: dankocoj@google.com + + + Benjamin "Russ" Hamilton + Google + Email: behamilton@google.com diff --git a/UpdateNonce/index.html b/UpdateNonce/index.html new file mode 100644 index 0000000..4739c1d --- /dev/null +++ b/UpdateNonce/index.html @@ -0,0 +1,45 @@ + + + + privacysandbox/draft-ietf-bidding-and-auction-services UpdateNonce preview + + + + +

Editor's drafts for UpdateNonce branch of privacysandbox/draft-ietf-bidding-and-auction-services

+ + + + + + +
Bidding and Auction Servicesplain textsame as main
+ + + diff --git a/index.html b/index.html index 8659428..74e5d22 100644 --- a/index.html +++ b/index.html @@ -32,6 +32,14 @@

Preview for branch KAnon

diff with main +

Preview for branch UpdateNonce

+ + + + + + +
Bidding and Auction Servicesplain textdiff with main