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