xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/blind_sign_auth/blind_sign_auth.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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