Internet-Draft | Bidding and Auction Services | October 2024 |
Kocoj & Hamilton | Expires 28 April 2025 | [Page] |
The Bidding and Auction Services provide a way for advertising auctions to execute in remote environments while preserving user privacy.¶
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.¶
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 28 April 2025.¶
Copyright (c) 2024 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.¶
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.¶
Higher utility of ad auctions.¶
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.¶
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.¶
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.¶
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.¶
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-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] |
Bidding and Auction Services requests and responses have the following framing:¶
Byte | 0 | 0 | 1 to 4 | 5 to Size+4 | Size+5 to end |
---|---|---|---|---|---|
Bits | 7-5 | 4-0 | * | * | * |
Contents | Version | Compression | Size | Request Payload | Padding |
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 |
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.¶
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.¶
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:¶
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.¶
Build a sequence of bytes (info
) by concatenating the ASCII-encoded string
"message/auction request", a zero byte, and hdr
.¶
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
.¶
Encrypt request
by invoking the Seal()
method on sctxt
(Section 5.2 of [HPKE])
with empty associated data aad
, yielding ciphertext ct
.¶
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, or more explicitly as follows:¶
Parse enc_request
into version
, key_id
, kem_id
, kdf_id
, aead_id
,
enc
, and ct
.¶
If version
is not 0, return an error.¶
Find the matching HPKE private key, skR
, corresponding to key_id
. If
there is no matching key, return an error.¶
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.¶
Create a receiving HPKE context, rctxt
, by invoking SetupBaseR()
(Section 5.1.1 of [HPKE])
with skR
, enc
, and info
.¶
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.¶
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.¶
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.¶
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.¶
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.¶
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
, and a config
consisting of the publisher
,
an optional desired total size
, an optional list of interest group owners
to
include each with an optional desired size
, and the [HPKE] public key
and
its associated key ID
. It returns an encrypted request
and a request context
tuple.¶
Let included_groups
be an empty map.¶
If desired total size
is not specified, but the list of interest group owners
includes at least one entry with a specified desired size
:¶
Set desired total size
to the sum of all specified desired size
in the
list of interest group owners
.¶
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
.¶
If the list of interest group owners
is specified, remove interest groups
whose owner is not on the list.¶
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 true.¶
Set current_size
to be the serialized size of the encrypted request
created from request
without padding.¶
Set remaining_allocated_size
to 0.¶
Set remaining_unsized_owners
to 0.¶
For each interest group owner
, interest group list
in
interest group map
:¶
For each interest group owner
, interest group list
in
interest group map
where there is a desired size
specified for
interest group owner
:¶
If the number of unsized_owners
is not 0:¶
Set the allowed_interest_group_size
to the desired size
for this
interest group owner
. This is a fixed size allocation.¶
Otherwise:¶
Set remaining_allocated_size
= remaining_allocated_size
-current_size
.¶
[CBOR] encode the interest group list
into serialized list
.¶
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.¶
Set request["interestGroups"][interest group owner]
to
compressed list
.¶
Set included_groups[interest group owner]
to interest group list
.¶
Set current_size
to be the serialized size of the encrypted request
created from request
without padding.¶
For each interest group owner
, interest group list
in
interest group map
where there is not desired size
specified for
interest group owner
:¶
Let remaining_size
be equal to the desired total size
-current_size
.¶
Set the allowed_interest_group_size
to
remaining_size
*/remaining_unsized_owners
. This is a
equal size allocation.¶
Decrement remaining_unsized_owners
by 1.¶
[CBOR] encode the interest group list
into serialized list
.¶
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.¶
Set request["interestGroups"][interest group owner]
to
compressed list
.¶
Set included_groups[interest group owner]
to interest group list
.¶
Set current_size
to be the serialized size of the encrypted request
created from request
without padding.¶
If there are no interest groups in the request, discard the request
and
return failure.¶
Prepend the framing header to request
with Compression
set to 2.¶
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.¶
Encrypt request
using the public key
and its key id
as in
Section 2.2.1 to get the encrypted message
and hpke context
.¶
Let the request context
be the tuple (included_groups
, hpke context
)¶
Return the request
and the request context
.¶
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.¶
Let encrypted request
be the request received from the client.¶
Let error_msg
be an empty string.¶
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
.¶
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
.¶
If the framing header
's Version
field is not 0, return failure.¶
If the framing header
's Compression
field is not supported, return
failure. Otherwise, save the Compression
field value as compression type
.¶
Let length
be equal to the framing header
's Size
field.¶
If length
is greater than the length of the remaining bytes in
framed request
, return failure.¶
Take the first length
remaining bytes in framed response
as
decodable request
, discarding the rest.¶
[CBOR] decode decodable request
into the message represented in Section 2.2.3.
Let this be request
.¶
Let processed request
be an empty struct.¶
If request
is not a map, return failure.¶
If request["version"]
does not exist or is not 0, return failure.¶
If request["publisher"]
does not exist or is not a string, return failure.¶
Set processed request["publisher"]
to request["publisher"]
.¶
If request["generationId"]
does not exist or is not a string, return failure.¶
Set processed request["generationId"]
to request["generationId"]
.¶
If request["enableDebugReporting]
exists:¶
If request["interestGroups]
does not exist or is not a map, return failure.¶
Set processed request["interestGroups"]
to an empty map.¶
For each key
, value
map entry of request["interestGroups"]
:¶
If key
is not a string, append an error message to
error_msg
. Proceed to Section 2.2.5.1.¶
Set processed request["interestGroups"]
[key]
to an empty list.¶
Decompress value
according to compression type
and set as
buyer input cbor
. If decompression fails, return failure.¶
[CBOR] decode buyer input cbor
into buyer input
. If decoding fails, return failure.¶
If buyer input
is not an array, return failure.¶
For each interest group
in buyer input
:¶
If the interest groups
is not a map, append an error message to
error_msg
. Proceed to Section 2.2.5.1.¶
Let ig
be an empty struct similar to Section 2.2.3.1.¶
If interest group["name"]
does not exist or is not a string, return failure.¶
Set ig["name"]
to interest group["name"]
.¶
If interest group["userBiddingSignals"]
exists:¶
If interest group["biddingSignalsKeys"]
exists:¶
If interest group["ads"]
exists:¶
If interest group["component"]
exists:¶
If interest group["browserSignals"]
exists:¶
If interest group["browserSignals"]
is not a map, return failure.¶
Let igbs
be an empty struct similar to browserSignals
as
defined in Section 2.2.3.1.¶
Let signals
be interest group["browserSignals"]
.¶
If signals["bidCount"]
exists:¶
If signals["joinCount"]
exists:¶
If signals["recencyMs"]
exists:¶
If signals["prevWins"]
exists:¶
Set ig["browserSignals"]
to igbs
.¶
Append ig
to processed request["interestGroups"]
[ key ]
.¶
Return processed request
and rctxt
to the Bidding and Auction
Services.¶
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.¶
If the failure happens before or during decryption, respond with an empty message.¶
Otherwise abort processing the request.¶
Let error
be a new map with key-value pairs:
[("code", 400), ("error", error_msg)]
.¶
Let response
be a new Section 2.3.3.¶
Set response["error"]
to error
.¶
Serialize and send response
to the client per Section 2.3.¶
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.¶
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:¶
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
.¶
Generate a random value of length max(Nn, Nk)
bytes, called response_nonce
.¶
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
.¶
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".¶
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".¶
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
.¶
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)¶
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.¶
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 = { ; 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, ; 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, } ; 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 } }¶
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.¶
Let cbor payload
equal the deterministically encoded CBOR
payload
. Return an empty response
on CBOR encoding failure.¶
Let compressed payload
equal the [GZIP] compressed cbor payload
,
returning an empty response
on compression failure.¶
Create a framed payload, as described in Section 2.3.2:¶
Create a framing header
.¶
Set the framing header
Compression
to 2.¶
Set the framing header
Version
to 0.¶
Set the framing header
Size
to the size of compressed payload
.¶
Let framed payload
equal the result of prepend the framing header
to compressed payload
.¶
Padding MAY be added to framing header
, as described in
Section 2.3.2.¶
Return an empty response
on failure of any of the previous steps.¶
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.¶
Return 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
.¶
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
.¶
If error
is not null, return failure.¶
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
.¶
If the framing header
's Version
field is not 0, return failure.¶
Let length
be equal to the framing header
's Size
field.¶
If length
is greater than the length of the remaining bytes in
framed response
, return failure.¶
Take the first length
remaining bytes in framed response
as
compressed response
, discarding the rest.¶
Decompress the compressed response
into serialized response
using the
method indicated by framing header
's Compression
field, returning
failure if decompression fails.¶
[CBOR] decode the serialized response
into response
, returning failure
if decompression fails.¶
If response
is not a map, return failure.¶
If response["error"]
exists, return failure.¶
If response["isChaff"]
exists and is either not a boolean or is true,
return failure.¶
Let processed response
be a new structure analogous to
server auction response.¶
If response["adRenderURL"]
does not exist, return failure.¶
Set processed response["ad render url"]
to response["adRenderURL"]
parsed
as a [URL], returning failure if there is an error.¶
If response["components"]
exists:¶
If response["components"]
is not an array, return failure.¶
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.¶
If response["interestGroupName"]
does not exist or is not a string, return failure.¶
Set processed response["interest group name"]
to response["interestGroupName"]
.¶
If response["interestGroupOwner"]
does not exist or is not a string, return failure.¶
Set processed response["interest group owner"]
to response["interestGroupOwner"]
parsed as an [ORIGIN], returning failure if there is an error.¶
If response["biddingGroups"]
does not exist or is not a map, return failure.¶
For each key
, value
in response["biddingGroups"]
:¶
Let owner
be equal to key
parsed as an [ORIGIN], returning failure if
there is an error.¶
request context
's included_groups
does not contain owner
as a key, return failure.¶
If value
is not a list, return failure.¶
For each element
in value
:
1. If element
is not an integer or element < 0
, return failure.
1. If element
is greater than or equal to the length of
included_groups[owner]
, return failure.
1. Let name
be the interest group name
for included_groups[owner][element]
.
1. Append the tuple (owner
, name
) to processed response["bidding groups"]
.¶
If response["updateGroups"]
exists and is a map:¶
For each key
, value
in response["updateGroups"]
:
1. Let owner
be equal to key
parsed as an [ORIGIN], continuing the next
iteration of this loop if there is an error.
1. If request context
's included_groups
does not contain owner
as a
key, continue the next iteration of this loop.
1. If value
is not a list, return failure.
1. For each element
in value
:¶
If element
is not a map, continue the next iteration of this loop.¶
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.¶
If element["index"]
is not an integer or element["index"] < 0
,
continue the next iteration of this loop.¶
If element["index"]
is greater than or equal to the length of
included_groups[owner]
, continue the next iteration of this loop.¶
Let name
be the interest group name
for included_groups[owner][element]
.¶
Let interest group key
be the tuple (owner
, name
).¶
Let update duration
be element["updateIfOlderThanMs"]
, parsed into a time
duration as integer milliseconds.¶
Set processed response["update groups"][intereset group key]
to
update duration
.¶
If response["score"]
exists:¶
If response["score"]
is not a floating point value, return failure.¶
Set processed response["score"]
to response["score"]
.¶
If response["bid"]
exists:¶
If response["bid"]
is not a floating point value, return failure.¶
Let bid
be a new structure analogous to bid with currency.¶
Set bid
s value
field to response["bid"]
.¶
If response["bidCurrency"]
exists:
1. If response["bidCurrency"]
is not a string, return failure.
1. If response["bidCUrrency"]
is not 3 bytes long or contains characters
other than upper case ASCII letters, return failure.
1. Set bid
's currency
field to response["bidCurrency"]
.¶
Set processed response["bid"]
to bid
.¶
If response["winReportingURLs"]
exists and is a map:¶
If response["winReportingURLs"]["buyerReportingURLs"]
exists:
1. Let buyer reporting
be the result of Section 2.3.5.1 on
response["winReportingURLs"]["buyerReportingURLs"]
.
1. Set processed response["buyer reporting"]
to buyer reporting
.¶
If response["winReportingURLs"]["topLevelSellerReportingURLs"]
exists:
1. Let top level seller reporting
be the result of Section 2.3.5.1
on response["winReportingURLs"]["topLevelSellerReportingURLs"]
.
1. Set processed response["top level seller reporting"]
to
top level seller reporting
.¶
If response["winReportingURLs"]["componentSellerReportingURLs"]
exists:
1. Let component seller reporting
be the result of Section 2.3.5.1
on response["winReportingURLs"]["componentSellerReportingURLs"]
.
1. Set processed response["component seller reporting"
to
component seller reporting
.¶
If response["topLevelSeller"]
exists:¶
If response["topLevelSeller"]
is not a string, return failure.¶
Set processed response["top level seller"]
to response["topLevelSeller"]
parsed as a [URL], returning failure if there is an error.¶
If response["adMetadata"]
exists and is a string set
processed response["ad metadata"]
to response["adMetadata"]
.¶
If response["buyerReportingId"]
exists and is a string set
processed response["buyer reporting id"]
to response["buyerReportingId"]
.¶
If response["buyerAndSellerReportingId"]
exists and is a string set
processed response["buyer and seller reporting id"]
to
response["buyerAndSellerReportingId"]
.¶
Return processed response
.¶
To parse reporting URLs on a [CBOR] map reporting URLs
with a schema like
reportingUrls
from Section 2.3.3:¶
Let processed reporting URLs
be a new structure analogous to
server auction reporting info.¶
If reporting URLs["reportingURL"]
exists and is a string:¶
Let reporting URL
be reporting URLs["reportingURL"]
parsed as a [URL],
or null if there is an error.¶
If reporting URL
is not null, set
processed reporting URLs["reporting url"]
to reporting URL
.¶
If reporting URLs["interactionReportingURLs"]
exists and is a map:¶
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
.¶
Return processed reporting URLs
.¶
This document introduces no additional considerations for IANA.¶
TODO¶