1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
|
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_INTEREST_GROUP_TRUSTED_SIGNALS_FETCHER_H_
#define CONTENT_BROWSER_INTEREST_GROUP_TRUSTED_SIGNALS_FETCHER_H_
#include <stdint.h>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <string_view>
#include <vector>
#include "base/containers/flat_set.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/types/expected.h"
#include "base/unguessable_token.h"
#include "base/values.h"
#include "content/browser/interest_group/data_decoder_manager.h"
#include "content/browser/interest_group/devtools_enums.h"
#include "content/common/content_export.h"
#include "content/public/browser/frame_tree_node_id.h"
#include "content/services/auction_worklet/public/mojom/trusted_signals_cache.mojom.h"
#include "net/http/http_response_headers.h"
#include "net/third_party/quiche/src/quiche/oblivious_http/buffers/oblivious_http_request.h"
#include "services/data_decoder/public/cpp/data_decoder.h"
#include "services/network/public/mojom/ip_address_space.mojom-forward.h"
#include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
#include "services/network/public/mojom/url_response_head.mojom-forward.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace auction_worklet {
class AuctionDownloader;
}
namespace content {
struct BiddingAndAuctionServerKey;
// Single-use network fetcher for versions 2+ of the key-value server API.
// It takes a list compression groups and partitions, and asynchronously returns
// a set of responses, one per compression group. The responses are provided as
// still compressed compression group bodies, so the cache layer can store
// compressed responses and to minimize IPC size. The responses will be
// decompressed before use in the appropriate Javascript process.
//
// Bidding and scoring signals need different structs when sending requests, but
// they use the same response format, since it's only the compressed data itself
// that varies based on signals type.
//
// TODO(https://crbug.com/333445540): This is currently only an API, with no
// implementation. Need to actually implement the API.
class CONTENT_EXPORT TrustedSignalsFetcher {
public:
static constexpr std::string_view kRequestMediaType =
"message/ad-auction-trusted-signals-request";
static constexpr std::string_view kResponseMediaType =
"message/ad-auction-trusted-signals-response";
// All the data needed to request a particular bidding signals partition.
struct CONTENT_EXPORT BiddingPartition {
// Pointer arguments must remain valid until the BiddingPartition is
// destroyed.
BiddingPartition(int partition_id,
const std::set<std::string>* interest_group_names,
const std::set<std::string>* keys,
const base::Value::Dict* additional_params,
const std::string* buyer_tkv_signals);
BiddingPartition(BiddingPartition&&);
~BiddingPartition();
BiddingPartition& operator=(BiddingPartition&&);
int partition_id;
base::raw_ref<const std::set<std::string>> interest_group_names;
base::raw_ref<const std::set<std::string>> keys;
// At the moment, valid keys are "experimentGroupId", "slotSize", and
// "allSlotsRequestedSizes". We could take them separately, but seems better
// to take one field rather than several?
base::raw_ref<const base::Value::Dict> additional_params;
raw_ptr<const std::string> buyer_tkv_signals;
};
// All the data needed to request a particular scoring signals partition.
struct CONTENT_EXPORT ScoringPartition {
// Pointer arguments must remain valid until the ScoringPartition is
// destroyed.
ScoringPartition(int partition_id,
const GURL* render_url,
const std::set<GURL>* component_render_urls,
const base::Value::Dict* additional_params,
const std::string* seller_tkv_signals);
ScoringPartition(ScoringPartition&&);
~ScoringPartition();
ScoringPartition& operator=(ScoringPartition&&);
int partition_id;
// Currently, TrustedSignalsCacheImpl puts the values from each bid in its
// own partition, so there will always be only one `render_url`.
base::raw_ref<const GURL> render_url;
base::raw_ref<const std::set<GURL>> component_render_urls;
// At the moment, valid keys are "experimentGroupId", "slotSize", and
// "allSlotsRequestedSizes". We could take them separately, but seems better
// to take one field rather than several?
base::raw_ref<const base::Value::Dict> additional_params;
raw_ptr<const std::string> seller_tkv_signals;
};
// While buying and scoring signals partitions need different structs when
// sending requests, the responses use the same format.
// The received result for a particular compression group. Only returned on
// success.
struct CONTENT_EXPORT CompressionGroupResult {
CompressionGroupResult();
CompressionGroupResult(CompressionGroupResult&&);
~CompressionGroupResult();
CompressionGroupResult& operator=(CompressionGroupResult&&);
// The compression scheme used by `compression_group_data`, as indicated by
// the server.
auction_worklet::mojom::TrustedSignalsCompressionScheme compression_scheme;
// The still-compressed data for the compression group.
base::Value::BlobStorage compression_group_data;
// Time until the response expires.
base::TimeDelta ttl;
};
// A map of compression group ids to results, in the case of success.
using CompressionGroupResultMap = std::map<int, CompressionGroupResult>;
// The result of a fetch. Either the entire fetch succeeds or it fails with a
// single error.
using SignalsFetchResult =
base::expected<CompressionGroupResultMap, std::string>;
using Callback = base::OnceCallback<void(SignalsFetchResult)>;
TrustedSignalsFetcher();
// Virtual for tests.
virtual ~TrustedSignalsFetcher();
TrustedSignalsFetcher(const TrustedSignalsFetcher&) = delete;
TrustedSignalsFetcher& operator=(const TrustedSignalsFetcher&) = delete;
// `data_decoder_manager` must outlive the fetcher.
//
// `frame_tree_node_id` and `devtools_auction_ids` are used to log events for
// devtools, if needed.
//
// `main_frame_origin` and `network_partition_nonce` are used to create an
// IsolationInfo identifying the network partition to use.
// `main_frame_origin`'s host is also sent as part of the encrypted request.
//
// `ip_address_space` is the IPAddressSpace of the frame that's running the
// auction. It's used to create a ClientSecurityState that has a
// PrivateNetworkRequestPolicy that will block the signals fetch if the
// signals URL maps to an IP on a less public address space. The other members
// of ClientSecurityState use default values. Default values are safe since
// these are credentialless requests. Any data taken from the frame would also
// potentially be a leak.
//
// `script_origin` is the owner of the interest group the request is for. Used
// as the initiator for CORS.
//
// `compression_groups` is a map of all partitions in the request, indexed by
// compression group id. Virtual for tests.
virtual void FetchBiddingSignals(
DataDecoderManager& data_decoder_manager,
network::mojom::URLLoaderFactory* url_loader_factory,
FrameTreeNodeId frame_tree_node_id,
base::flat_set<std::string> devtools_auction_ids,
const url::Origin& main_frame_origin,
network::mojom::IPAddressSpace ip_address_space,
base::UnguessableToken network_partition_nonce,
const url::Origin& script_origin,
const GURL& trusted_bidding_signals_url,
const BiddingAndAuctionServerKey& bidding_and_auction_key,
const std::map<int, std::vector<BiddingPartition>>& compression_groups,
Callback callback);
// `data_decoder_manager` must outlive the fetcher.
//
// `frame_tree_node_id` and `devtools_auction_ids` are used to log events for
// devtools, if needed.
//
// `main_frame_origin` and `network_partition_nonce` are used to create an
// IsolationInfo identifying the network partition to use.
// `main_frame_origin`'s host is also sent as part of the encrypted request.
//
// `ip_address_space` is the IPAddressSpace of the frame that's running the
// auction. It's used to create a ClientSecurityState that has a
// PrivateNetworkRequestPolicy that will block the signals fetch if the
// signals URL maps to an IP on a less public address space. The other members
// of ClientSecurityState use default values. Default values are safe since
// these are credentialless requests. Any data taken from the frame would also
// potentially be a leak.
//
// `script_origin` is the seller for the auction. Used as the initiator for
// CORS.
//
// `compression_groups` is a map of all partitions in the request, indexed by
// compression group id. Virtual for tests.
virtual void FetchScoringSignals(
DataDecoderManager& data_decoder_manager,
network::mojom::URLLoaderFactory* url_loader_factory,
FrameTreeNodeId frame_tree_node_id,
base::flat_set<std::string> devtools_auction_ids,
const url::Origin& main_frame_origin,
network::mojom::IPAddressSpace ip_address_space,
base::UnguessableToken network_partition_nonce,
const url::Origin& script_origin,
const GURL& trusted_scoring_signals_url,
const BiddingAndAuctionServerKey& bidding_and_auction_key,
const std::map<int, std::vector<ScoringPartition>>& compression_groups,
Callback callback);
private:
// Encrypts `plaintext_body` using `bidding_and_auction_key`, and then creates
// a SimpleURLLoader and starts a request. Once the request body has been
// created, everything else (including response body parsing) is identical for
// bidding and scoring signals, as only the data inside compression groups is
// different for bidding and scoring signals, and that layer is not parsed by
// this class.
void EncryptRequestBodyAndStart(
DataDecoderManager& data_decoder_manager,
network::mojom::URLLoaderFactory* url_loader_factory,
InterestGroupAuctionFetchType fetch_type,
FrameTreeNodeId frame_tree_node_id,
base::flat_set<std::string> devtools_auction_ids,
const url::Origin& main_frame_origin,
network::mojom::IPAddressSpace ip_address_space,
base::UnguessableToken network_partition_nonce,
const url::Origin& script_origin,
const GURL& trusted_signals_url,
const BiddingAndAuctionServerKey& bidding_and_auction_key,
std::string plaintext_request_body,
Callback callback);
void OnRequestComplete(std::unique_ptr<std::string> response_body,
scoped_refptr<net::HttpResponseHeaders> headers,
std::optional<std::string> error);
void OnCborParsed(data_decoder::DataDecoder::ValueOrError value_or_error);
// Attempts to parse the base::Value result from having the DataDecoder parse
// the CBOR contents of the fetch.
SignalsFetchResult ParseDataDecoderResult(
data_decoder::DataDecoder::ValueOrError value_or_error);
// Attempts to parse a single compression group object.
// `compression_group_value` should be a value from the `compressionGroups`
// array of the parsed CBOR value. On success, returns a
// CompressionGroupResult and sets `compression_group_id` to the ID from the
// passed in value. On failure, leaves `compression_group_id` alone, and
// returns a string.
base::expected<CompressionGroupResult, std::string> ParseCompressionGroup(
base::Value compression_group_value,
int& compression_group_id);
// Returns a string error message, prefixing the passed in message with the
// URL.
std::string CreateError(const std::string& error_message);
// The URL being fetched. Cached for using in error strings.
GURL trusted_signals_url_;
Callback callback_;
std::unique_ptr<auction_worklet::AuctionDownloader> auction_downloader_;
// Context needed to decrypt the response. Initialized while encrypting the
// request body.
std::unique_ptr<quiche::ObliviousHttpRequest::Context> ohttp_context_;
// Used to parse the CBOR response. Created when fetch starts, to pre-warm the
// decoder process.
std::unique_ptr<DataDecoderManager::Handle> decoder_handle_;
// Compression scheme used by all compression groups. Populated when reading
// the response.
auction_worklet::mojom::TrustedSignalsCompressionScheme compression_scheme_ =
auction_worklet::mojom::TrustedSignalsCompressionScheme::kNone;
base::WeakPtrFactory<TrustedSignalsFetcher> weak_ptr_factory_{this};
};
} // namespace content
#endif // CONTENT_BROWSER_INTEREST_GROUP_TRUSTED_SIGNALS_FETCHER_H_
|