xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/blind_sign_auth/blind_sign_auth_test.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 <cstdint>
8 #include <memory>
9 #include <string>
10 #include <utility>
11 
12 #include "absl/status/status.h"
13 #include "absl/status/statusor.h"
14 #include "absl/strings/escaping.h"
15 #include "absl/strings/string_view.h"
16 #include "absl/time/time.h"
17 #include "anonymous_tokens/cpp/crypto/crypto_utils.h"
18 #include "anonymous_tokens/cpp/privacy_pass/token_encodings.h"
19 #include "anonymous_tokens/cpp/testing/utils.h"
20 #include "openssl/base.h"
21 #include "openssl/digest.h"
22 #include "quiche/blind_sign_auth/blind_sign_auth_interface.h"
23 #include "quiche/blind_sign_auth/blind_sign_auth_protos.h"
24 #include "quiche/blind_sign_auth/blind_sign_http_response.h"
25 #include "quiche/blind_sign_auth/blind_sign_message_interface.h"
26 #include "quiche/blind_sign_auth/test_tools/mock_blind_sign_http_interface.h"
27 #include "quiche/common/platform/api/quiche_mutex.h"
28 #include "quiche/common/platform/api/quiche_test.h"
29 #include "quiche/common/test_tools/quiche_test_utils.h"
30 
31 namespace quiche {
32 namespace test {
33 namespace {
34 
35 using ::testing::_;
36 using ::testing::Eq;
37 using ::testing::InSequence;
38 using ::testing::Invoke;
39 using ::testing::StartsWith;
40 using ::testing::Unused;
41 
42 class BlindSignAuthTest : public QuicheTest {
43  protected:
SetUp()44   void SetUp() override {
45     // Create keypair and populate protos.
46     auto [test_rsa_public_key, test_rsa_private_key] =
47         anonymous_tokens::GetStrongTestRsaKeyPair2048();
48     ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
49         rsa_public_key_,
50         anonymous_tokens::CreatePublicKeyRSA(
51             test_rsa_public_key.n, test_rsa_public_key.e));
52     ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
53         rsa_private_key_,
54         anonymous_tokens::CreatePrivateKeyRSA(
55             test_rsa_private_key.n, test_rsa_private_key.e,
56             test_rsa_private_key.d, test_rsa_private_key.p,
57             test_rsa_private_key.q, test_rsa_private_key.dp,
58             test_rsa_private_key.dq, test_rsa_private_key.crt));
59 
60     anonymous_tokens::RSAPublicKey public_key;
61     public_key.set_n(test_rsa_public_key.n);
62     public_key.set_e(test_rsa_public_key.e);
63 
64     public_key_proto_.set_key_version(1);
65     public_key_proto_.set_use_case("TEST_USE_CASE");
66     public_key_proto_.set_serialized_public_key(public_key.SerializeAsString());
67     public_key_proto_.set_sig_hash_type(
68         anonymous_tokens::AT_HASH_TYPE_SHA384);
69     public_key_proto_.set_mask_gen_function(
70         anonymous_tokens::AT_MGF_SHA384);
71     public_key_proto_.set_salt_length(48);
72     public_key_proto_.set_key_size(256);
73     public_key_proto_.set_message_mask_type(
74         anonymous_tokens::AT_MESSAGE_MASK_NO_MASK);
75     public_key_proto_.set_message_mask_size(0);
76 
77     // Create expected GetInitialDataRequest.
78     expected_get_initial_data_request_.set_use_attestation(false);
79     expected_get_initial_data_request_.set_service_type("chromeipblinding");
80     expected_get_initial_data_request_.set_location_granularity(
81         privacy::ppn::GetInitialDataRequest_LocationGranularity_CITY_GEOS);
82     expected_get_initial_data_request_.set_validation_version(2);
83     expected_get_initial_data_request_.set_proxy_layer(privacy::ppn::PROXY_A);
84 
85     // Create fake GetInitialDataResponse.
86     privacy::ppn::GetInitialDataResponse fake_get_initial_data_response;
87     *fake_get_initial_data_response.mutable_at_public_metadata_public_key() =
88         public_key_proto_;
89     fake_get_initial_data_response_ = fake_get_initial_data_response;
90 
91     // Create PrivacyPassData.
92     privacy::ppn::GetInitialDataResponse::PrivacyPassData privacy_pass_data;
93     // token_key_id is derived from public key.
94     ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
95         std::string public_key_der,
96         anonymous_tokens::RsaSsaPssPublicKeyToDerEncoding(
97             rsa_public_key_.get()));
98     const EVP_MD* sha256 = EVP_sha256();
99     ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
100         token_key_id_, anonymous_tokens::ComputeHash(
101                            public_key_der, *sha256));
102 
103     // Create and serialize fake extensions.
104     anonymous_tokens::ExpirationTimestamp
105         expiration_timestamp;
106     int64_t one_hour_away = absl::ToUnixSeconds(absl::Now() + absl::Hours(1));
107     expiration_timestamp.timestamp = one_hour_away - (one_hour_away % 900);
108     expiration_timestamp.timestamp_precision = 900;
109     absl::StatusOr<anonymous_tokens::Extension>
110         expiration_extension = expiration_timestamp.AsExtension();
111     QUICHE_EXPECT_OK(expiration_extension);
112     extensions_.extensions.push_back(*expiration_extension);
113 
114     anonymous_tokens::GeoHint geo_hint;
115     geo_hint.geo_hint = "US,US-AL,ALABASTER";
116     absl::StatusOr<anonymous_tokens::Extension>
117         geo_hint_extension = geo_hint.AsExtension();
118     QUICHE_EXPECT_OK(geo_hint_extension);
119     extensions_.extensions.push_back(*geo_hint_extension);
120 
121     anonymous_tokens::ServiceType service_type;
122     service_type.service_type_id =
123         anonymous_tokens::ServiceType::kChromeIpBlinding;
124     absl::StatusOr<anonymous_tokens::Extension>
125         service_type_extension = service_type.AsExtension();
126     QUICHE_EXPECT_OK(service_type_extension);
127     extensions_.extensions.push_back(*service_type_extension);
128 
129     anonymous_tokens::DebugMode debug_mode;
130     debug_mode.mode = anonymous_tokens::DebugMode::kDebug;
131     absl::StatusOr<anonymous_tokens::Extension>
132         debug_mode_extension = debug_mode.AsExtension();
133     QUICHE_EXPECT_OK(debug_mode_extension);
134     extensions_.extensions.push_back(*debug_mode_extension);
135 
136     anonymous_tokens::ProxyLayer proxy_layer;
137     proxy_layer.layer =
138         anonymous_tokens::ProxyLayer::kProxyA;
139     absl::StatusOr<anonymous_tokens::Extension>
140         proxy_layer_extension = proxy_layer.AsExtension();
141     QUICHE_EXPECT_OK(proxy_layer_extension);
142     extensions_.extensions.push_back(*proxy_layer_extension);
143 
144     absl::StatusOr<std::string> serialized_extensions =
145         anonymous_tokens::EncodeExtensions(extensions_);
146     QUICHE_EXPECT_OK(serialized_extensions);
147 
148     privacy_pass_data.set_token_key_id(token_key_id_);
149     privacy_pass_data.set_public_metadata_extensions(*serialized_extensions);
150 
151     *fake_get_initial_data_response.mutable_public_metadata_info() =
152         public_metadata_info_;
153     *fake_get_initial_data_response.mutable_privacy_pass_data() =
154         privacy_pass_data;
155     fake_get_initial_data_response_ = fake_get_initial_data_response;
156 
157     // Create BlindSignAuthOptions.
158     privacy::ppn::BlindSignAuthOptions options;
159     options.set_enable_privacy_pass(true);
160 
161     blind_sign_auth_ =
162         std::make_unique<BlindSignAuth>(&mock_http_interface_, options);
163   }
164 
TearDown()165   void TearDown() override {
166     blind_sign_auth_.reset(nullptr);
167   }
168 
169  public:
CreateSignResponse(const std::string & body,bool use_privacy_pass)170   void CreateSignResponse(const std::string& body, bool use_privacy_pass) {
171     privacy::ppn::AuthAndSignRequest request;
172     ASSERT_TRUE(request.ParseFromString(body));
173 
174     // Validate AuthAndSignRequest.
175     EXPECT_EQ(request.service_type(), "chromeipblinding");
176     // Phosphor does not need the public key hash if the KeyType is
177     // privacy::ppn::AT_PUBLIC_METADATA_KEY_TYPE.
178     EXPECT_EQ(request.key_type(), privacy::ppn::AT_PUBLIC_METADATA_KEY_TYPE);
179     EXPECT_EQ(request.public_key_hash(), "");
180     EXPECT_EQ(request.key_version(), public_key_proto_.key_version());
181     EXPECT_EQ(request.do_not_use_rsa_public_exponent(), true);
182     EXPECT_NE(request.blinded_token().size(), 0);
183 
184     if (use_privacy_pass) {
185       EXPECT_EQ(request.public_metadata_extensions(),
186                 fake_get_initial_data_response_.privacy_pass_data()
187                     .public_metadata_extensions());
188     } else {
189       EXPECT_EQ(request.public_metadata_info().SerializeAsString(),
190                 public_metadata_info_.SerializeAsString());
191     }
192 
193     // Construct AuthAndSignResponse.
194     privacy::ppn::AuthAndSignResponse response;
195     for (const auto& request_token : request.blinded_token()) {
196       std::string decoded_blinded_token;
197       ASSERT_TRUE(absl::Base64Unescape(request_token, &decoded_blinded_token));
198       if (use_privacy_pass) {
199         absl::StatusOr<std::string> signature =
200             anonymous_tokens::TestSignWithPublicMetadata(
201                 decoded_blinded_token, request.public_metadata_extensions(),
202                 *rsa_private_key_, false);
203         QUICHE_EXPECT_OK(signature);
204         response.add_blinded_token_signature(absl::Base64Escape(*signature));
205       } else {
206         absl::StatusOr<std::string> serialized_token =
207             anonymous_tokens::TestSign(
208                 decoded_blinded_token, rsa_private_key_.get());
209         // TestSignWithPublicMetadata for privacy pass
210         QUICHE_EXPECT_OK(serialized_token);
211         response.add_blinded_token_signature(
212             absl::Base64Escape(*serialized_token));
213       }
214     }
215     sign_response_ = response;
216   }
217 
ValidateGetTokensOutput(absl::Span<BlindSignToken> tokens)218   void ValidateGetTokensOutput(absl::Span<BlindSignToken> tokens) {
219     for (const auto& token : tokens) {
220       privacy::ppn::SpendTokenData spend_token_data;
221       ASSERT_TRUE(spend_token_data.ParseFromString(token.token));
222       // Validate token structure.
223       EXPECT_EQ(spend_token_data.public_metadata().SerializeAsString(),
224                 public_metadata_info_.public_metadata().SerializeAsString());
225       EXPECT_THAT(spend_token_data.unblinded_token(), StartsWith("blind:"));
226       EXPECT_GE(spend_token_data.unblinded_token_signature().size(),
227                 spend_token_data.unblinded_token().size());
228       EXPECT_EQ(spend_token_data.signing_key_version(),
229                 public_key_proto_.key_version());
230       EXPECT_NE(spend_token_data.use_case(),
231                 anonymous_tokens::AnonymousTokensUseCase::
232                     ANONYMOUS_TOKENS_USE_CASE_UNDEFINED);
233       EXPECT_NE(spend_token_data.message_mask(), "");
234     }
235   }
236 
ValidatePrivacyPassTokensOutput(absl::Span<BlindSignToken> tokens)237   void ValidatePrivacyPassTokensOutput(absl::Span<BlindSignToken> tokens) {
238     for (const auto& token : tokens) {
239       privacy::ppn::PrivacyPassTokenData privacy_pass_token_data;
240       ASSERT_TRUE(privacy_pass_token_data.ParseFromString(token.token));
241       // Validate token structure.
242       std::string decoded_token;
243       ASSERT_TRUE(absl::WebSafeBase64Unescape(privacy_pass_token_data.token(),
244                                               &decoded_token));
245       std::string decoded_extensions;
246       ASSERT_TRUE(absl::WebSafeBase64Unescape(
247           privacy_pass_token_data.encoded_extensions(), &decoded_extensions));
248     }
249   }
250 
251   MockBlindSignHttpInterface mock_http_interface_;
252   std::unique_ptr<BlindSignAuth> blind_sign_auth_;
253   anonymous_tokens::RSABlindSignaturePublicKey
254       public_key_proto_;
255   bssl::UniquePtr<RSA> rsa_public_key_;
256   bssl::UniquePtr<RSA> rsa_private_key_;
257   std::string token_key_id_;
258   anonymous_tokens::Extensions extensions_;
259   privacy::ppn::PublicMetadataInfo public_metadata_info_;
260   privacy::ppn::AuthAndSignResponse sign_response_;
261   privacy::ppn::GetInitialDataResponse fake_get_initial_data_response_;
262   std::string oauth_token_ = "oauth_token";
263   privacy::ppn::GetInitialDataRequest expected_get_initial_data_request_;
264 };
265 
TEST_F(BlindSignAuthTest,TestGetTokensFailedNetworkError)266 TEST_F(BlindSignAuthTest, TestGetTokensFailedNetworkError) {
267   EXPECT_CALL(mock_http_interface_,
268               DoRequest(Eq(BlindSignHttpRequestType::kGetInitialData),
269                         Eq(oauth_token_), _, _))
270       .Times(1)
271       .WillOnce([=](auto&&, auto&&, auto&&, auto get_initial_data_cb) {
272         std::move(get_initial_data_cb)(
273             absl::InternalError("Failed to create socket"));
274       });
275 
276   EXPECT_CALL(mock_http_interface_,
277               DoRequest(Eq(BlindSignHttpRequestType::kAuthAndSign), _, _, _))
278       .Times(0);
279 
280   int num_tokens = 1;
281   QuicheNotification done;
282   SignedTokenCallback callback =
283       [&done](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
284         EXPECT_THAT(tokens.status().code(), absl::StatusCode::kInternal);
285         done.Notify();
286       };
287   blind_sign_auth_->GetTokens(oauth_token_, num_tokens, ProxyLayer::kProxyA,
288                               std::move(callback));
289   done.WaitForNotification();
290 }
291 
TEST_F(BlindSignAuthTest,TestGetTokensFailedBadGetInitialDataResponse)292 TEST_F(BlindSignAuthTest, TestGetTokensFailedBadGetInitialDataResponse) {
293   *fake_get_initial_data_response_.mutable_at_public_metadata_public_key()
294        ->mutable_use_case() = "SPAM";
295 
296   BlindSignHttpResponse fake_public_key_response(
297       200, fake_get_initial_data_response_.SerializeAsString());
298 
299   EXPECT_CALL(
300       mock_http_interface_,
301       DoRequest(Eq(BlindSignHttpRequestType::kGetInitialData), Eq(oauth_token_),
302                 Eq(expected_get_initial_data_request_.SerializeAsString()), _))
303       .Times(1)
304       .WillOnce([=](auto&&, auto&&, auto&&, auto get_initial_data_cb) {
305         std::move(get_initial_data_cb)(fake_public_key_response);
306       });
307 
308   EXPECT_CALL(mock_http_interface_,
309               DoRequest(Eq(BlindSignHttpRequestType::kAuthAndSign), _, _, _))
310       .Times(0);
311 
312   int num_tokens = 1;
313   QuicheNotification done;
314   SignedTokenCallback callback =
315       [&done](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
316         EXPECT_THAT(tokens.status().code(), absl::StatusCode::kInvalidArgument);
317         done.Notify();
318       };
319   blind_sign_auth_->GetTokens(oauth_token_, num_tokens, ProxyLayer::kProxyA,
320                               std::move(callback));
321   done.WaitForNotification();
322 }
323 
TEST_F(BlindSignAuthTest,TestGetTokensFailedBadAuthAndSignResponse)324 TEST_F(BlindSignAuthTest, TestGetTokensFailedBadAuthAndSignResponse) {
325   BlindSignHttpResponse fake_public_key_response(
326       200, fake_get_initial_data_response_.SerializeAsString());
327   {
328     InSequence seq;
329 
330     EXPECT_CALL(
331         mock_http_interface_,
332         DoRequest(
333             Eq(BlindSignHttpRequestType::kGetInitialData), Eq(oauth_token_),
334             Eq(expected_get_initial_data_request_.SerializeAsString()), _))
335         .Times(1)
336         .WillOnce([=](auto&&, auto&&, auto&&, auto get_initial_data_cb) {
337           std::move(get_initial_data_cb)(fake_public_key_response);
338         });
339 
340     EXPECT_CALL(mock_http_interface_,
341                 DoRequest(Eq(BlindSignHttpRequestType::kAuthAndSign),
342                           Eq(oauth_token_), _, _))
343         .Times(1)
344         .WillOnce(Invoke([this](Unused, Unused, const std::string& body,
345                                 BlindSignHttpCallback callback) {
346           CreateSignResponse(body, false);
347           // Add an invalid signature that can't be Base64 decoded.
348           sign_response_.add_blinded_token_signature("invalid_signature%");
349           BlindSignHttpResponse http_response(
350               200, sign_response_.SerializeAsString());
351           std::move(callback)(http_response);
352         }));
353   }
354 
355   int num_tokens = 1;
356   QuicheNotification done;
357   SignedTokenCallback callback =
358       [&done](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
359         EXPECT_THAT(tokens.status().code(), absl::StatusCode::kInternal);
360         done.Notify();
361       };
362   blind_sign_auth_->GetTokens(oauth_token_, num_tokens, ProxyLayer::kProxyA,
363                               std::move(callback));
364   done.WaitForNotification();
365 }
366 
TEST_F(BlindSignAuthTest,TestPrivacyPassGetTokensSucceeds)367 TEST_F(BlindSignAuthTest, TestPrivacyPassGetTokensSucceeds) {
368   BlindSignHttpResponse fake_public_key_response(
369       200, fake_get_initial_data_response_.SerializeAsString());
370   {
371     InSequence seq;
372 
373     EXPECT_CALL(
374         mock_http_interface_,
375         DoRequest(
376             Eq(BlindSignHttpRequestType::kGetInitialData), Eq(oauth_token_),
377             Eq(expected_get_initial_data_request_.SerializeAsString()), _))
378         .Times(1)
379         .WillOnce([=](auto&&, auto&&, auto&&, auto get_initial_data_cb) {
380           std::move(get_initial_data_cb)(fake_public_key_response);
381         });
382 
383     EXPECT_CALL(mock_http_interface_,
384                 DoRequest(Eq(BlindSignHttpRequestType::kAuthAndSign),
385                           Eq(oauth_token_), _, _))
386         .Times(1)
387         .WillOnce(Invoke([this](Unused, Unused, const std::string& body,
388                                 BlindSignHttpCallback callback) {
389           CreateSignResponse(body, /*use_privacy_pass=*/true);
390           BlindSignHttpResponse http_response(
391               200, sign_response_.SerializeAsString());
392           std::move(callback)(http_response);
393         }));
394   }
395 
396   int num_tokens = 1;
397   QuicheNotification done;
398   SignedTokenCallback callback =
399       [this, &done](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
400         QUICHE_EXPECT_OK(tokens);
401         ValidatePrivacyPassTokensOutput(*tokens);
402         done.Notify();
403       };
404   blind_sign_auth_->GetTokens(oauth_token_, num_tokens, ProxyLayer::kProxyA,
405                               std::move(callback));
406   done.WaitForNotification();
407 }
408 
TEST_F(BlindSignAuthTest,TestPrivacyPassGetTokensFailsWithBadExtensions)409 TEST_F(BlindSignAuthTest, TestPrivacyPassGetTokensFailsWithBadExtensions) {
410   privacy::ppn::BlindSignAuthOptions options;
411   options.set_enable_privacy_pass(true);
412   blind_sign_auth_ =
413       std::make_unique<BlindSignAuth>(&mock_http_interface_, options);
414 
415   public_key_proto_.set_message_mask_type(
416       anonymous_tokens::AT_MESSAGE_MASK_NO_MASK);
417   public_key_proto_.set_message_mask_size(0);
418   *fake_get_initial_data_response_.mutable_at_public_metadata_public_key() =
419       public_key_proto_;
420   fake_get_initial_data_response_.mutable_privacy_pass_data()
421       ->set_public_metadata_extensions("spam");
422   BlindSignHttpResponse fake_public_key_response(
423       200, fake_get_initial_data_response_.SerializeAsString());
424 
425   EXPECT_CALL(
426       mock_http_interface_,
427       DoRequest(Eq(BlindSignHttpRequestType::kGetInitialData), Eq(oauth_token_),
428                 Eq(expected_get_initial_data_request_.SerializeAsString()), _))
429       .Times(1)
430       .WillOnce([=](auto&&, auto&&, auto&&, auto get_initial_data_cb) {
431         std::move(get_initial_data_cb)(fake_public_key_response);
432       });
433 
434   int num_tokens = 1;
435   QuicheNotification done;
436   SignedTokenCallback callback =
437       [&done](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
438         EXPECT_THAT(tokens.status().code(), absl::StatusCode::kInvalidArgument);
439         done.Notify();
440       };
441   blind_sign_auth_->GetTokens(oauth_token_, num_tokens, ProxyLayer::kProxyA,
442                               std::move(callback));
443   done.WaitForNotification();
444 }
445 
446 }  // namespace
447 }  // namespace test
448 }  // namespace quiche
449