1 // Copyright (c) 2023 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/blind_sign_auth/blind_sign_auth.h"
6
7 #include <cstddef>
8 #include <cstdint>
9 #include <cstring>
10 #include <memory>
11 #include <optional>
12 #include <string>
13 #include <utility>
14 #include <vector>
15
16 #include "absl/functional/bind_front.h"
17 #include "absl/status/status.h"
18 #include "absl/status/statusor.h"
19 #include "absl/strings/escaping.h"
20 #include "absl/strings/str_cat.h"
21 #include "absl/strings/string_view.h"
22 #include "absl/time/clock.h"
23 #include "absl/time/time.h"
24 #include "absl/types/span.h"
25 #include "anonymous_tokens/cpp/crypto/crypto_utils.h"
26 #include "anonymous_tokens/cpp/privacy_pass/rsa_bssa_public_metadata_client.h"
27 #include "anonymous_tokens/cpp/privacy_pass/token_encodings.h"
28 #include "anonymous_tokens/cpp/shared/proto_utils.h"
29 #include "quiche/blind_sign_auth/blind_sign_auth_interface.h"
30 #include "quiche/blind_sign_auth/blind_sign_auth_protos.h"
31 #include "quiche/blind_sign_auth/blind_sign_http_response.h"
32 #include "quiche/blind_sign_auth/blind_sign_message_interface.h"
33 #include "quiche/common/platform/api/quiche_logging.h"
34 #include "quiche/common/quiche_random.h"
35
36 namespace quiche {
37 namespace {
38
39 template <typename T>
OmitDefault(T value)40 std::string OmitDefault(T value) {
41 return value == 0 ? "" : absl::StrCat(value);
42 }
43
44 constexpr absl::string_view kIssuerHostname =
45 "https://ipprotection-ppissuer.googleapis.com";
46
47 } // namespace
48
GetTokens(std::optional<std::string> oauth_token,int num_tokens,ProxyLayer proxy_layer,SignedTokenCallback callback)49 void BlindSignAuth::GetTokens(std::optional<std::string> oauth_token,
50 int num_tokens, ProxyLayer proxy_layer,
51 SignedTokenCallback callback) {
52 // Create GetInitialData RPC.
53 privacy::ppn::GetInitialDataRequest request;
54 request.set_use_attestation(false);
55 request.set_service_type("chromeipblinding");
56 request.set_location_granularity(
57 privacy::ppn::GetInitialDataRequest_LocationGranularity_CITY_GEOS);
58 // Validation version must be 2 to use ProxyLayer.
59 request.set_validation_version(2);
60 request.set_proxy_layer(QuicheProxyLayerToPpnProxyLayer(proxy_layer));
61
62 // Call GetInitialData on the HttpFetcher.
63 std::string body = request.SerializeAsString();
64 BlindSignHttpCallback initial_data_callback = absl::bind_front(
65 &BlindSignAuth::GetInitialDataCallback, this, oauth_token, num_tokens,
66 proxy_layer, std::move(callback));
67 http_fetcher_->DoRequest(BlindSignHttpRequestType::kGetInitialData,
68 oauth_token, body, std::move(initial_data_callback));
69 }
70
GetInitialDataCallback(std::optional<std::string> oauth_token,int num_tokens,ProxyLayer proxy_layer,SignedTokenCallback callback,absl::StatusOr<BlindSignHttpResponse> response)71 void BlindSignAuth::GetInitialDataCallback(
72 std::optional<std::string> oauth_token, int num_tokens,
73 ProxyLayer proxy_layer, SignedTokenCallback callback,
74 absl::StatusOr<BlindSignHttpResponse> response) {
75 if (!response.ok()) {
76 QUICHE_LOG(WARNING) << "GetInitialDataRequest failed: "
77 << response.status();
78 std::move(callback)(response.status());
79 return;
80 }
81 absl::StatusCode code = HttpCodeToStatusCode(response->status_code());
82 if (code != absl::StatusCode::kOk) {
83 std::string message =
84 absl::StrCat("GetInitialDataRequest failed with code: ", code);
85 QUICHE_LOG(WARNING) << message;
86 std::move(callback)(absl::Status(code, message));
87 return;
88 }
89 // Parse GetInitialDataResponse.
90 privacy::ppn::GetInitialDataResponse initial_data_response;
91 if (!initial_data_response.ParseFromString(response->body())) {
92 QUICHE_LOG(WARNING) << "Failed to parse GetInitialDataResponse";
93 std::move(callback)(
94 absl::InternalError("Failed to parse GetInitialDataResponse"));
95 return;
96 }
97
98 // Create token signing requests.
99 bool use_privacy_pass_client =
100 initial_data_response.has_privacy_pass_data() &&
101 auth_options_.enable_privacy_pass();
102
103 if (use_privacy_pass_client) {
104 QUICHE_DVLOG(1) << "Using Privacy Pass client";
105 GeneratePrivacyPassTokens(initial_data_response, std::move(oauth_token),
106 num_tokens, proxy_layer, std::move(callback));
107 } else {
108 QUICHE_LOG(ERROR) << "Non-Privacy Pass tokens are no longer supported";
109 std::move(callback)(absl::UnimplementedError(
110 "Non-Privacy Pass tokens are no longer supported"));
111 }
112 }
113
GeneratePrivacyPassTokens(privacy::ppn::GetInitialDataResponse initial_data_response,std::optional<std::string> oauth_token,int num_tokens,ProxyLayer proxy_layer,SignedTokenCallback callback)114 void BlindSignAuth::GeneratePrivacyPassTokens(
115 privacy::ppn::GetInitialDataResponse initial_data_response,
116 std::optional<std::string> oauth_token, int num_tokens,
117 ProxyLayer proxy_layer, SignedTokenCallback callback) {
118 // Set up values used in the token generation loop.
119 anonymous_tokens::RSAPublicKey public_key_proto;
120 if (!public_key_proto.ParseFromString(
121 initial_data_response.at_public_metadata_public_key()
122 .serialized_public_key())) {
123 std::move(callback)(
124 absl::InvalidArgumentError("Failed to parse Privacy Pass public key"));
125 return;
126 }
127 absl::StatusOr<bssl::UniquePtr<RSA>> bssl_rsa_key =
128 anonymous_tokens::CreatePublicKeyRSA(
129 public_key_proto.n(), public_key_proto.e());
130 if (!bssl_rsa_key.ok()) {
131 std::move(callback)(bssl_rsa_key.status());
132 return;
133 }
134 absl::StatusOr<anonymous_tokens::Extensions> extensions =
135 anonymous_tokens::DecodeExtensions(
136 initial_data_response.privacy_pass_data()
137 .public_metadata_extensions());
138 if (!extensions.ok()) {
139 QUICHE_LOG(WARNING) << "Failed to decode extensions: "
140 << extensions.status();
141 std::move(callback)(extensions.status());
142 return;
143 }
144 std::vector<uint16_t> kExpectedExtensionTypes = {
145 /*ExpirationTimestamp=*/0x0001, /*GeoHint=*/0x0002,
146 /*ServiceType=*/0xF001, /*DebugMode=*/0xF002, /*ProxyLayer=*/0xF003};
147 absl::Status result =
148 anonymous_tokens::ValidateExtensionsOrderAndValues(
149 *extensions, absl::MakeSpan(kExpectedExtensionTypes), absl::Now());
150 if (!result.ok()) {
151 QUICHE_LOG(WARNING) << "Failed to validate extensions: " << result;
152 std::move(callback)(result);
153 return;
154 }
155 absl::StatusOr<anonymous_tokens::ExpirationTimestamp>
156 expiration_timestamp = anonymous_tokens::
157 ExpirationTimestamp::FromExtension(extensions->extensions.at(0));
158 if (!expiration_timestamp.ok()) {
159 QUICHE_LOG(WARNING) << "Failed to parse expiration timestamp: "
160 << expiration_timestamp.status();
161 std::move(callback)(expiration_timestamp.status());
162 return;
163 }
164 absl::Time public_metadata_expiry_time =
165 absl::FromUnixSeconds(expiration_timestamp->timestamp);
166
167 // Create token challenge.
168 anonymous_tokens::TokenChallenge challenge;
169 challenge.issuer_name = kIssuerHostname;
170 absl::StatusOr<std::string> token_challenge =
171 anonymous_tokens::MarshalTokenChallenge(challenge);
172 if (!token_challenge.ok()) {
173 QUICHE_LOG(WARNING) << "Failed to marshal token challenge: "
174 << token_challenge.status();
175 std::move(callback)(token_challenge.status());
176 return;
177 }
178
179 QuicheRandom* random = QuicheRandom::GetInstance();
180 // Create vector of Privacy Pass clients, one for each token.
181 std::vector<anonymous_tokens::ExtendedTokenRequest>
182 extended_token_requests;
183 std::vector<std::unique_ptr<anonymous_tokens::
184 PrivacyPassRsaBssaPublicMetadataClient>>
185 privacy_pass_clients;
186 std::vector<std::string> privacy_pass_blinded_tokens;
187
188 for (int i = 0; i < num_tokens; i++) {
189 // Create client.
190 auto client = anonymous_tokens::
191 PrivacyPassRsaBssaPublicMetadataClient::Create(*bssl_rsa_key.value());
192 if (!client.ok()) {
193 QUICHE_LOG(WARNING) << "Failed to create Privacy Pass client: "
194 << client.status();
195 std::move(callback)(client.status());
196 return;
197 }
198
199 // Create nonce.
200 std::string nonce_rand(32, '\0');
201 random->RandBytes(nonce_rand.data(), nonce_rand.size());
202
203 // Create token request.
204 absl::StatusOr<anonymous_tokens::ExtendedTokenRequest>
205 extended_token_request = client.value()->CreateTokenRequest(
206 *token_challenge, nonce_rand,
207 initial_data_response.privacy_pass_data().token_key_id(),
208 *extensions);
209 if (!extended_token_request.ok()) {
210 QUICHE_LOG(WARNING) << "Failed to create ExtendedTokenRequest: "
211 << extended_token_request.status();
212 std::move(callback)(extended_token_request.status());
213 return;
214 }
215 privacy_pass_clients.push_back(*std::move(client));
216 extended_token_requests.push_back(*extended_token_request);
217 privacy_pass_blinded_tokens.push_back(absl::Base64Escape(
218 extended_token_request->request.blinded_token_request));
219 }
220
221 privacy::ppn::AuthAndSignRequest sign_request;
222 sign_request.set_service_type("chromeipblinding");
223 sign_request.set_key_type(privacy::ppn::AT_PUBLIC_METADATA_KEY_TYPE);
224 sign_request.set_key_version(
225 initial_data_response.at_public_metadata_public_key().key_version());
226 sign_request.mutable_blinded_token()->Assign(
227 privacy_pass_blinded_tokens.begin(), privacy_pass_blinded_tokens.end());
228 sign_request.mutable_public_metadata_extensions()->assign(
229 initial_data_response.privacy_pass_data().public_metadata_extensions());
230 // TODO(b/295924807): deprecate this option after AT server defaults to it
231 sign_request.set_do_not_use_rsa_public_exponent(true);
232 sign_request.set_proxy_layer(QuicheProxyLayerToPpnProxyLayer(proxy_layer));
233
234 absl::StatusOr<anonymous_tokens::AnonymousTokensUseCase>
235 use_case = anonymous_tokens::ParseUseCase(
236 initial_data_response.at_public_metadata_public_key().use_case());
237 if (!use_case.ok()) {
238 QUICHE_LOG(WARNING) << "Failed to parse use case: " << use_case.status();
239 std::move(callback)(use_case.status());
240 return;
241 }
242
243 BlindSignHttpCallback auth_and_sign_callback =
244 absl::bind_front(&BlindSignAuth::PrivacyPassAuthAndSignCallback, this,
245 std::move(initial_data_response.privacy_pass_data()
246 .public_metadata_extensions()),
247 public_metadata_expiry_time, *use_case,
248 std::move(privacy_pass_clients), std::move(callback));
249 // TODO(b/304811277): remove other usages of string.data()
250 http_fetcher_->DoRequest(BlindSignHttpRequestType::kAuthAndSign, oauth_token,
251 sign_request.SerializeAsString(),
252 std::move(auth_and_sign_callback));
253 }
254
PrivacyPassAuthAndSignCallback(std::string encoded_extensions,absl::Time public_key_expiry_time,anonymous_tokens::AnonymousTokensUseCase use_case,std::vector<std::unique_ptr<anonymous_tokens::PrivacyPassRsaBssaPublicMetadataClient>> privacy_pass_clients,SignedTokenCallback callback,absl::StatusOr<BlindSignHttpResponse> response)255 void BlindSignAuth::PrivacyPassAuthAndSignCallback(
256 std::string encoded_extensions, absl::Time public_key_expiry_time,
257 anonymous_tokens::AnonymousTokensUseCase use_case,
258 std::vector<std::unique_ptr<anonymous_tokens::
259 PrivacyPassRsaBssaPublicMetadataClient>>
260 privacy_pass_clients,
261 SignedTokenCallback callback,
262 absl::StatusOr<BlindSignHttpResponse> response) {
263 // Validate response.
264 if (!response.ok()) {
265 QUICHE_LOG(WARNING) << "AuthAndSign failed: " << response.status();
266 std::move(callback)(response.status());
267 return;
268 }
269 absl::StatusCode code = HttpCodeToStatusCode(response->status_code());
270 if (code != absl::StatusCode::kOk) {
271 std::string message = absl::StrCat("AuthAndSign failed with code: ", code);
272 QUICHE_LOG(WARNING) << message;
273 std::move(callback)(absl::Status(code, message));
274 return;
275 }
276
277 // Decode AuthAndSignResponse.
278 privacy::ppn::AuthAndSignResponse sign_response;
279 if (!sign_response.ParseFromString(response->body())) {
280 QUICHE_LOG(WARNING) << "Failed to parse AuthAndSignResponse";
281 std::move(callback)(
282 absl::InternalError("Failed to parse AuthAndSignResponse"));
283 return;
284 }
285 if (static_cast<size_t>(sign_response.blinded_token_signature_size()) !=
286 privacy_pass_clients.size()) {
287 QUICHE_LOG(WARNING) << "Number of signatures does not equal number of "
288 "Privacy Pass tokens sent";
289 std::move(callback)(
290 absl::InternalError("Number of signatures does not equal number of "
291 "Privacy Pass tokens sent"));
292 return;
293 }
294
295 // Create tokens using blinded signatures.
296 std::vector<BlindSignToken> tokens_vec;
297 for (int i = 0; i < sign_response.blinded_token_signature_size(); i++) {
298 std::string unescaped_blinded_sig;
299 if (!absl::Base64Unescape(sign_response.blinded_token_signature()[i],
300 &unescaped_blinded_sig)) {
301 QUICHE_LOG(WARNING) << "Failed to unescape blinded signature";
302 std::move(callback)(
303 absl::InternalError("Failed to unescape blinded signature"));
304 return;
305 }
306
307 absl::StatusOr<anonymous_tokens::Token> token =
308 privacy_pass_clients[i]->FinalizeToken(unescaped_blinded_sig);
309 if (!token.ok()) {
310 QUICHE_LOG(WARNING) << "Failed to finalize token: " << token.status();
311 std::move(callback)(token.status());
312 return;
313 }
314
315 absl::StatusOr<std::string> marshaled_token =
316 anonymous_tokens::MarshalToken(*token);
317 if (!marshaled_token.ok()) {
318 QUICHE_LOG(WARNING) << "Failed to marshal token: "
319 << marshaled_token.status();
320 std::move(callback)(marshaled_token.status());
321 return;
322 }
323
324 privacy::ppn::PrivacyPassTokenData privacy_pass_token_data;
325 privacy_pass_token_data.mutable_token()->assign(
326 absl::WebSafeBase64Escape(*marshaled_token));
327 privacy_pass_token_data.mutable_encoded_extensions()->assign(
328 absl::WebSafeBase64Escape(encoded_extensions));
329 privacy_pass_token_data.set_use_case_override(use_case);
330 tokens_vec.push_back(BlindSignToken{
331 privacy_pass_token_data.SerializeAsString(), public_key_expiry_time});
332 }
333
334 std::move(callback)(absl::Span<BlindSignToken>(tokens_vec));
335 }
336
HttpCodeToStatusCode(int http_code)337 absl::StatusCode BlindSignAuth::HttpCodeToStatusCode(int http_code) {
338 // copybara:strip_begin(golink)
339 // This mapping is from go/http-canonical-mapping
340 // copybara:strip_end
341 if (http_code >= 200 && http_code < 300) {
342 return absl::StatusCode::kOk;
343 } else if (http_code >= 300 && http_code < 400) {
344 return absl::StatusCode::kUnknown;
345 } else if (http_code == 400) {
346 return absl::StatusCode::kInvalidArgument;
347 } else if (http_code == 401) {
348 return absl::StatusCode::kUnauthenticated;
349 } else if (http_code == 403) {
350 return absl::StatusCode::kPermissionDenied;
351 } else if (http_code == 404) {
352 return absl::StatusCode::kNotFound;
353 } else if (http_code == 409) {
354 return absl::StatusCode::kAborted;
355 } else if (http_code == 416) {
356 return absl::StatusCode::kOutOfRange;
357 } else if (http_code == 429) {
358 return absl::StatusCode::kResourceExhausted;
359 } else if (http_code == 499) {
360 return absl::StatusCode::kCancelled;
361 } else if (http_code >= 400 && http_code < 500) {
362 return absl::StatusCode::kFailedPrecondition;
363 } else if (http_code == 501) {
364 return absl::StatusCode::kUnimplemented;
365 } else if (http_code == 503) {
366 return absl::StatusCode::kUnavailable;
367 } else if (http_code == 504) {
368 return absl::StatusCode::kDeadlineExceeded;
369 } else if (http_code >= 500 && http_code < 600) {
370 return absl::StatusCode::kInternal;
371 }
372 return absl::StatusCode::kUnknown;
373 }
374
QuicheProxyLayerToPpnProxyLayer(quiche::ProxyLayer proxy_layer)375 privacy::ppn::ProxyLayer BlindSignAuth::QuicheProxyLayerToPpnProxyLayer(
376 quiche::ProxyLayer proxy_layer) {
377 switch (proxy_layer) {
378 case ProxyLayer::kProxyA: {
379 return privacy::ppn::ProxyLayer::PROXY_A;
380 }
381 case ProxyLayer::kProxyB: {
382 return privacy::ppn::ProxyLayer::PROXY_B;
383 }
384 }
385 }
386
387 } // namespace quiche
388