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
|
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_K_ANONYMITY_SERVICE_K_ANONYMITY_SERVICE_CLIENT_H_
#define CHROME_BROWSER_K_ANONYMITY_SERVICE_K_ANONYMITY_SERVICE_CLIENT_H_
#include <memory>
#include <string>
#include <vector>
#include "base/base64.h"
#include "base/containers/circular_deque.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/time/time.h"
#include "chrome/browser/k_anonymity_service/k_anonymity_service_storage.h"
#include "chrome/browser/k_anonymity_service/k_anonymity_trust_token_getter.h"
#include "chrome/browser/k_anonymity_service/remote_trust_token_query_answerer.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/k_anonymity_service_delegate.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/unique_receiver_set.h"
#include "net/base/isolation_info.h"
#include "services/data_decoder/public/cpp/data_decoder.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/oblivious_http_request.mojom-forward.h"
#include "services/network/public/mojom/trust_tokens.mojom.h"
// This class implements the KAnonymityServiceDelegate by sending requests
// to the Chrome k-anonymity Service. This class will eventually send requests
// over OHTTP in order to anonymize the source of the requests. The requests are
// internally queued and performed serially.
//
// In the case of JoinSet this is necessary because we need
// to ensure that there is a trust token available to attach to the call, and
// the network service does not expose a method to get the count.
//
// In the case of QuerySets, requests are performed serially in order to
// simplify implementation. With only one request out at a time 1) it is clear
// which request the responses are associated with and 2) the limit on the
// number of outstanding requests can be handled by the caller.
//
// IDs passed into the KAnonymityServiceClient will be hashed with SHA256
// to convert them to a fixed size before being sent to the KAnonymity service.
class KAnonymityServiceClient : public content::KAnonymityServiceDelegate,
public KeyedService {
public:
// The profile must outlive the KAnonymityServiceClient.
explicit KAnonymityServiceClient(Profile* profile);
~KAnonymityServiceClient() override;
// Implementation of content::KAnonymityServiceDelegate.
// JoinSet corresponds directly to the JoinSet endpoint provided by the
// K-anonymity Service endpoint. The endpoint requires us to provide a trust
// token for rate limiting for each call, so we need to check that before
// performing the OHTTP request (assuming we have the key).
//
// The trust token interface (and network isolation configuration) assume
// there exists both a "top frame" and a "current frame" for any requests.
// The KAnonymityServiceClient makes requests in the background so those
// concepts don't really apply here, but for the purposes of the interface we
// choose to use the kKAnonymityAuthServer's origin as the origin for both
// frames. Not only does this ensure that requests that can be cached are
// cached with the appropriate isolation applied, but also avoids the less
// common (and less tested) configuration where trust tokens are issued in a
// third-party frame.
void JoinSet(std::string id,
base::OnceCallback<void(bool)> callback) override;
// QuerySets is implemented as multiple calls to the QuerySet endpoint of
// the K-anonymity Service.
void QuerySets(std::vector<std::string> set_ids,
base::OnceCallback<void(std::vector<bool>)> callback) override;
base::TimeDelta GetJoinInterval() override;
base::TimeDelta GetQueryInterval() override;
// Returns true if the profile is allowed to use the k-anonymity service. This
// currently checks if the primary profile CanRunChromePrivacySandboxTrials.
// This is partially to prevent exposing minors' data to the k-anonymity
// service.
static bool CanUseKAnonymityService(Profile* profile);
private:
struct PendingJoinRequest {
PendingJoinRequest(std::string set_id,
base::OnceCallback<void(bool)> callback);
~PendingJoinRequest();
std::string id;
base::TimeTicks request_start;
int retries = 0;
base::OnceCallback<void(bool)> callback;
};
struct PendingQueryRequest {
PendingQueryRequest(std::vector<std::string> set_id,
base::OnceCallback<void(std::vector<bool>)> callback);
~PendingQueryRequest();
std::vector<std::string> ids;
base::TimeTicks request_start;
base::OnceCallback<void(std::vector<bool>)> callback;
};
// Called when storage is ready to for us to make requests.
void JoinSetOnStorageReady(KAnonymityServiceStorage::InitStatus status);
// Starts processing items in the queue by calling JoinSetCheckTrustTokens()
void JoinSetStartNextQueued();
// Checks that the cached OHTTP Key is still valid and if not calls
// RequestJoinSetOHTTPKey to refresh it.
void JoinSetCheckOHTTPKey();
// Starts the HTTP fetch to obtain the OHTTP key from the JoinSet endpoint.
void RequestJoinSetOHTTPKey();
// Asynchronously called by the join_url_loader_ with a response containing
// the OHTTP key for the JoinSet endpoint. The key is cached and then
// JoinSetCheckTrustTokens is called to continue processing the current
// request.
void OnGotJoinSetOHTTPKey(std::unique_ptr<std::string> response);
// Calls the token_getter_ to ensure there is a trust token for the request.
void JoinSetCheckTrustTokens(OHTTPKeyAndExpiration ohttp_key);
// Asynchronous callback from token_getter_ which is passed the key commitment
// and non-unique client ID that are needed to complete JoinSet. If the
// provided optional is not empty this triggers the JoinSet request.
void OnMaybeHasTrustTokens(
OHTTPKeyAndExpiration ohttp_key,
std::optional<KeyAndNonUniqueUserId> maybe_key_and_id);
// Starts the OHTTP JoinSet request for the join_queue_.front() request.
void JoinSetSendRequest(OHTTPKeyAndExpiration ohttp_key,
KeyAndNonUniqueUserId key_and_id);
// Handle the response to the JoinSet request and call CompleteJoinSetRequest
// if successful.
void JoinSetOnGotResponse(const std::optional<std::string>& response,
int error_code);
// Calls DoJoinSetCallback indicating the current request completed
// successfully. If there are other items in the queue calls
// JoinSetStartNextQueued to start processing them.
void CompleteJoinSetRequest();
// Calls DoJoinSetCallback for each of the requests in the join_queue_
// indicating the requests were unsuccessful.
void FailJoinSetRequests();
// Asynchronously calls the callback for the join_queue_.front() request with
// the provided status and removes it from the queue.
void DoJoinSetCallback(bool status);
// Called when storage is ready to for us to make requests.
void QuerySetsOnStorageReady(KAnonymityServiceStorage::InitStatus status);
// Checks that the cached OHTTP Key is still valid and if not calls
// RequestQuerySetOHTTPKey to refresh it.
void QuerySetsCheckOHTTPKey();
// Starts the HTTP fetch to obtain the OHTTP key from the QuerySet endpoint.
void RequestQuerySetOHTTPKey();
// Asynchronous response containing the OHTTP key for the QuerySet endpoint.
// The key is cached and then QuerySetsSendRequests is called to continue
// processing the current request.
void OnGotQuerySetOHTTPKey(std::unique_ptr<std::string> response);
// Handles failures in fetching the OHTTP key. Schedules callbacks indicating
// failure for all requests on the query_queue_.
void FailFetchingQueryOHTTPKey();
// Starts the OHTTP QuerySet request for the current QueryRequest.
void QuerySetsSendRequest(OHTTPKeyAndExpiration ohttp_key);
// Called as an asynchronous response to the OHTTP request started by
// QuerySetsSendRequest. Passes the JSON response received to be decoded and
// handled in QuerySetsOnParsedResponse.
void QuerySetsOnGotResponse(const std::optional<std::string>& response,
int error_code);
// Called asynchronously when the QuerySet response from
// QuerySetsOnGotResponse has been decoded.
void QuerySetsOnParsedResponse(
data_decoder::DataDecoder::ValueOrError result);
// Calls DoQuerySetsCallback indicating the current request completed
// successfully. If there are other items in the queue calls
// QuerySetsCheckOHTTPKey to start processing them.
void CompleteQuerySetsRequest(std::vector<bool> result);
// Called when the request started by QuerySetsSendRequest fails
// either because of network errors are parse failure. Calls
// DoQuerySetsCallback for each of the requests in the query_queue_ indicating
// the requests were unsuccessful.
void FailQuerySetsRequests();
// Called by QuerySetsOnParsedResponse and FailQuerySetRequest. Schedules
// a callback containing the result and starts work on the next request in the
// query_queue_.
void DoQuerySetsCallback(std::vector<bool> result);
// queues
base::circular_deque<std::unique_ptr<PendingJoinRequest>> join_queue_;
base::circular_deque<std::unique_ptr<PendingQueryRequest>> query_queue_;
mojo::UniqueReceiverSet<network::mojom::ObliviousHttpClient>
ohttp_client_receivers_;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
std::unique_ptr<network::SimpleURLLoader> join_url_loader_;
std::unique_ptr<network::SimpleURLLoader> query_url_loader_;
bool enable_ohttp_requests_;
net::IsolationInfo isolation_info_;
std::unique_ptr<KAnonymityServiceStorage> storage_;
RemoteTrustTokenQueryAnswerer trust_token_answerer_;
KAnonymityTrustTokenGetter token_getter_;
url::Origin join_origin_;
url::Origin query_origin_;
raw_ptr<Profile> profile_;
base::WeakPtrFactory<KAnonymityServiceClient> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_K_ANONYMITY_SERVICE_K_ANONYMITY_SERVICE_CLIENT_H_
|