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/cached_blind_sign_auth.h"
6 
7 #include <memory>
8 #include <optional>
9 #include <string>
10 #include <utility>
11 #include <vector>
12 
13 #include "absl/status/status.h"
14 #include "absl/status/statusor.h"
15 #include "absl/strings/str_cat.h"
16 #include "absl/strings/str_format.h"
17 #include "absl/time/clock.h"
18 #include "absl/time/time.h"
19 #include "absl/types/span.h"
20 #include "quiche/blind_sign_auth/blind_sign_auth_interface.h"
21 #include "quiche/blind_sign_auth/test_tools/mock_blind_sign_auth_interface.h"
22 #include "quiche/common/platform/api/quiche_mutex.h"
23 #include "quiche/common/platform/api/quiche_test.h"
24 #include "quiche/common/test_tools/quiche_test_utils.h"
25 
26 namespace quiche {
27 namespace test {
28 namespace {
29 
30 using ::testing::_;
31 using ::testing::InvokeArgument;
32 using ::testing::Unused;
33 
34 class CachedBlindSignAuthTest : public QuicheTest {
35  protected:
SetUp()36   void SetUp() override {
37     cached_blind_sign_auth_ =
38         std::make_unique<CachedBlindSignAuth>(&mock_blind_sign_auth_interface_);
39   }
40 
TearDown()41   void TearDown() override {
42     fake_tokens_.clear();
43     cached_blind_sign_auth_.reset();
44   }
45 
46  public:
MakeFakeTokens(int num_tokens)47   std::vector<BlindSignToken> MakeFakeTokens(int num_tokens) {
48     std::vector<BlindSignToken> fake_tokens;
49     for (int i = 0; i < kBlindSignAuthRequestMaxTokens; i++) {
50       fake_tokens.push_back(BlindSignToken{absl::StrCat("token:", i),
51                                            absl::Now() + absl::Hours(1)});
52     }
53     return fake_tokens;
54   }
55 
MakeExpiredTokens(int num_tokens)56   std::vector<BlindSignToken> MakeExpiredTokens(int num_tokens) {
57     std::vector<BlindSignToken> fake_tokens;
58     for (int i = 0; i < kBlindSignAuthRequestMaxTokens; i++) {
59       fake_tokens.push_back(BlindSignToken{absl::StrCat("token:", i),
60                                            absl::Now() - absl::Hours(1)});
61     }
62     return fake_tokens;
63   }
64 
65   MockBlindSignAuthInterface mock_blind_sign_auth_interface_;
66   std::unique_ptr<CachedBlindSignAuth> cached_blind_sign_auth_;
67   std::optional<std::string> oauth_token_ = "oauth_token";
68   std::vector<BlindSignToken> fake_tokens_;
69 };
70 
TEST_F(CachedBlindSignAuthTest,TestGetTokensOneCallSuccessful)71 TEST_F(CachedBlindSignAuthTest, TestGetTokensOneCallSuccessful) {
72   EXPECT_CALL(mock_blind_sign_auth_interface_,
73               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
74       .Times(1)
75       .WillOnce(
76           [this](Unused, int num_tokens, Unused, SignedTokenCallback callback) {
77             fake_tokens_ = MakeFakeTokens(num_tokens);
78             std::move(callback)(absl::MakeSpan(fake_tokens_));
79           });
80 
81   int num_tokens = 5;
82   QuicheNotification done;
83   SignedTokenCallback callback =
84       [num_tokens, &done](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
85         QUICHE_EXPECT_OK(tokens);
86         EXPECT_EQ(num_tokens, tokens->size());
87         for (int i = 0; i < num_tokens; i++) {
88           EXPECT_EQ(tokens->at(i).token, absl::StrCat("token:", i));
89         }
90         done.Notify();
91       };
92 
93   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
94                                      ProxyLayer::kProxyA, std::move(callback));
95   done.WaitForNotification();
96 }
97 
TEST_F(CachedBlindSignAuthTest,TestGetTokensMultipleRemoteCallsSuccessful)98 TEST_F(CachedBlindSignAuthTest, TestGetTokensMultipleRemoteCallsSuccessful) {
99   EXPECT_CALL(mock_blind_sign_auth_interface_,
100               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
101       .Times(2)
102       .WillRepeatedly(
103           [this](Unused, int num_tokens, Unused, SignedTokenCallback callback) {
104             fake_tokens_ = MakeFakeTokens(num_tokens);
105             std::move(callback)(absl::MakeSpan(fake_tokens_));
106           });
107 
108   int num_tokens = kBlindSignAuthRequestMaxTokens - 1;
109   QuicheNotification first;
110   SignedTokenCallback first_callback =
111       [num_tokens, &first](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
112         QUICHE_EXPECT_OK(tokens);
113         EXPECT_EQ(num_tokens, tokens->size());
114         for (int i = 0; i < num_tokens; i++) {
115           EXPECT_EQ(tokens->at(i).token, absl::StrCat("token:", i));
116         }
117         first.Notify();
118       };
119 
120   cached_blind_sign_auth_->GetTokens(
121       oauth_token_, num_tokens, ProxyLayer::kProxyA, std::move(first_callback));
122   first.WaitForNotification();
123 
124   QuicheNotification second;
125   SignedTokenCallback second_callback =
126       [num_tokens, &second](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
127         QUICHE_EXPECT_OK(tokens);
128         EXPECT_EQ(num_tokens, tokens->size());
129         EXPECT_EQ(tokens->at(0).token,
130                   absl::StrCat("token:", kBlindSignAuthRequestMaxTokens - 1));
131         for (int i = 1; i < num_tokens; i++) {
132           EXPECT_EQ(tokens->at(i).token, absl::StrCat("token:", i - 1));
133         }
134         second.Notify();
135       };
136 
137   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
138                                      ProxyLayer::kProxyA,
139                                      std::move(second_callback));
140   second.WaitForNotification();
141 }
142 
TEST_F(CachedBlindSignAuthTest,TestGetTokensSecondRequestFilledFromCache)143 TEST_F(CachedBlindSignAuthTest, TestGetTokensSecondRequestFilledFromCache) {
144   EXPECT_CALL(mock_blind_sign_auth_interface_,
145               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
146       .Times(1)
147       .WillOnce(
148           [this](Unused, int num_tokens, Unused, SignedTokenCallback callback) {
149             fake_tokens_ = MakeFakeTokens(num_tokens);
150             std::move(callback)(absl::MakeSpan(fake_tokens_));
151           });
152 
153   int num_tokens = kBlindSignAuthRequestMaxTokens / 2;
154   QuicheNotification first;
155   SignedTokenCallback first_callback =
156       [num_tokens, &first](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
157         QUICHE_EXPECT_OK(tokens);
158         EXPECT_EQ(num_tokens, tokens->size());
159         for (int i = 0; i < num_tokens; i++) {
160           EXPECT_EQ(tokens->at(i).token, absl::StrCat("token:", i));
161         }
162         first.Notify();
163       };
164 
165   cached_blind_sign_auth_->GetTokens(
166       oauth_token_, num_tokens, ProxyLayer::kProxyA, std::move(first_callback));
167   first.WaitForNotification();
168 
169   QuicheNotification second;
170   SignedTokenCallback second_callback =
171       [num_tokens, &second](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
172         QUICHE_EXPECT_OK(tokens);
173         EXPECT_EQ(num_tokens, tokens->size());
174         for (int i = 0; i < num_tokens; i++) {
175           EXPECT_EQ(tokens->at(i).token,
176                     absl::StrCat("token:", i + num_tokens));
177         }
178         second.Notify();
179       };
180 
181   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
182                                      ProxyLayer::kProxyA,
183                                      std::move(second_callback));
184   second.WaitForNotification();
185 }
186 
TEST_F(CachedBlindSignAuthTest,TestGetTokensThirdRequestRefillsCache)187 TEST_F(CachedBlindSignAuthTest, TestGetTokensThirdRequestRefillsCache) {
188   EXPECT_CALL(mock_blind_sign_auth_interface_,
189               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
190       .Times(2)
191       .WillRepeatedly(
192           [this](Unused, int num_tokens, Unused, SignedTokenCallback callback) {
193             fake_tokens_ = MakeFakeTokens(num_tokens);
194             std::move(callback)(absl::MakeSpan(fake_tokens_));
195           });
196 
197   int num_tokens = kBlindSignAuthRequestMaxTokens / 2;
198   QuicheNotification first;
199   SignedTokenCallback first_callback =
200       [num_tokens, &first](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
201         QUICHE_EXPECT_OK(tokens);
202         EXPECT_EQ(num_tokens, tokens->size());
203         for (int i = 0; i < num_tokens; i++) {
204           EXPECT_EQ(tokens->at(i).token, absl::StrCat("token:", i));
205         }
206         first.Notify();
207       };
208 
209   cached_blind_sign_auth_->GetTokens(
210       oauth_token_, num_tokens, ProxyLayer::kProxyA, std::move(first_callback));
211   first.WaitForNotification();
212 
213   QuicheNotification second;
214   SignedTokenCallback second_callback =
215       [num_tokens, &second](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
216         QUICHE_EXPECT_OK(tokens);
217         EXPECT_EQ(num_tokens, tokens->size());
218         for (int i = 0; i < num_tokens; i++) {
219           EXPECT_EQ(tokens->at(i).token,
220                     absl::StrCat("token:", i + num_tokens));
221         }
222         second.Notify();
223       };
224 
225   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
226                                      ProxyLayer::kProxyA,
227                                      std::move(second_callback));
228   second.WaitForNotification();
229 
230   QuicheNotification third;
231   int third_request_tokens = 10;
232   SignedTokenCallback third_callback =
233       [third_request_tokens,
234        &third](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
235         QUICHE_EXPECT_OK(tokens);
236         EXPECT_EQ(third_request_tokens, tokens->size());
237         for (int i = 0; i < third_request_tokens; i++) {
238           EXPECT_EQ(tokens->at(i).token, absl::StrCat("token:", i));
239         }
240         third.Notify();
241       };
242 
243   cached_blind_sign_auth_->GetTokens(oauth_token_, third_request_tokens,
244                                      ProxyLayer::kProxyA,
245                                      std::move(third_callback));
246   third.WaitForNotification();
247 }
248 
TEST_F(CachedBlindSignAuthTest,TestGetTokensRequestTooLarge)249 TEST_F(CachedBlindSignAuthTest, TestGetTokensRequestTooLarge) {
250   EXPECT_CALL(mock_blind_sign_auth_interface_,
251               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
252       .Times(0);
253 
254   int num_tokens = kBlindSignAuthRequestMaxTokens + 1;
255   SignedTokenCallback callback =
256       [](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
257         EXPECT_THAT(tokens.status().code(), absl::StatusCode::kInvalidArgument);
258         EXPECT_THAT(
259             tokens.status().message(),
260             absl::StrFormat("Number of tokens requested exceeds maximum: %d",
261                             kBlindSignAuthRequestMaxTokens));
262       };
263 
264   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
265                                      ProxyLayer::kProxyA, std::move(callback));
266 }
267 
TEST_F(CachedBlindSignAuthTest,TestGetTokensRequestNegative)268 TEST_F(CachedBlindSignAuthTest, TestGetTokensRequestNegative) {
269   EXPECT_CALL(mock_blind_sign_auth_interface_,
270               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
271       .Times(0);
272 
273   int num_tokens = -1;
274   SignedTokenCallback callback =
275       [num_tokens](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
276         EXPECT_THAT(tokens.status().code(), absl::StatusCode::kInvalidArgument);
277         EXPECT_THAT(tokens.status().message(),
278                     absl::StrFormat("Negative number of tokens requested: %d",
279                                     num_tokens));
280       };
281 
282   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
283                                      ProxyLayer::kProxyA, std::move(callback));
284 }
285 
TEST_F(CachedBlindSignAuthTest,TestHandleGetTokensResponseErrorHandling)286 TEST_F(CachedBlindSignAuthTest, TestHandleGetTokensResponseErrorHandling) {
287   EXPECT_CALL(mock_blind_sign_auth_interface_,
288               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
289       .Times(2)
290       .WillOnce(
291           [](Unused, int num_tokens, Unused, SignedTokenCallback callback) {
292             std::move(callback)(absl::InternalError("AuthAndSign failed"));
293           })
294       .WillOnce(
295           [this](Unused, int num_tokens, Unused, SignedTokenCallback callback) {
296             fake_tokens_ = MakeFakeTokens(num_tokens);
297             fake_tokens_.pop_back();
298             std::move(callback)(absl::MakeSpan(fake_tokens_));
299           });
300 
301   int num_tokens = kBlindSignAuthRequestMaxTokens;
302   QuicheNotification first;
303   SignedTokenCallback first_callback =
304       [&first](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
305         EXPECT_THAT(tokens.status().code(), absl::StatusCode::kInternal);
306         EXPECT_THAT(tokens.status().message(), "AuthAndSign failed");
307         first.Notify();
308       };
309 
310   cached_blind_sign_auth_->GetTokens(
311       oauth_token_, num_tokens, ProxyLayer::kProxyA, std::move(first_callback));
312   first.WaitForNotification();
313 
314   QuicheNotification second;
315   SignedTokenCallback second_callback =
316       [&second](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
317         EXPECT_THAT(tokens.status().code(),
318                     absl::StatusCode::kResourceExhausted);
319         second.Notify();
320       };
321 
322   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
323                                      ProxyLayer::kProxyA,
324                                      std::move(second_callback));
325   second.WaitForNotification();
326 }
327 
TEST_F(CachedBlindSignAuthTest,TestGetTokensZeroTokensRequested)328 TEST_F(CachedBlindSignAuthTest, TestGetTokensZeroTokensRequested) {
329   EXPECT_CALL(mock_blind_sign_auth_interface_,
330               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
331       .Times(0);
332 
333   int num_tokens = 0;
334   SignedTokenCallback callback =
335       [](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
336         QUICHE_EXPECT_OK(tokens);
337         EXPECT_EQ(tokens->size(), 0);
338       };
339 
340   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
341                                      ProxyLayer::kProxyA, std::move(callback));
342 }
343 
TEST_F(CachedBlindSignAuthTest,TestExpiredTokensArePruned)344 TEST_F(CachedBlindSignAuthTest, TestExpiredTokensArePruned) {
345   EXPECT_CALL(mock_blind_sign_auth_interface_,
346               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
347       .Times(1)
348       .WillOnce(
349           [this](Unused, int num_tokens, Unused, SignedTokenCallback callback) {
350             fake_tokens_ = MakeExpiredTokens(num_tokens);
351             std::move(callback)(absl::MakeSpan(fake_tokens_));
352           });
353 
354   int num_tokens = kBlindSignAuthRequestMaxTokens;
355   QuicheNotification first;
356   SignedTokenCallback first_callback =
357       [&first](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
358         EXPECT_THAT(tokens.status().code(),
359                     absl::StatusCode::kResourceExhausted);
360         first.Notify();
361       };
362 
363   cached_blind_sign_auth_->GetTokens(
364       oauth_token_, num_tokens, ProxyLayer::kProxyA, std::move(first_callback));
365   first.WaitForNotification();
366 }
367 
TEST_F(CachedBlindSignAuthTest,TestClearCacheRemovesTokens)368 TEST_F(CachedBlindSignAuthTest, TestClearCacheRemovesTokens) {
369   EXPECT_CALL(mock_blind_sign_auth_interface_,
370               GetTokens(oauth_token_, kBlindSignAuthRequestMaxTokens, _, _))
371       .Times(2)
372       .WillRepeatedly(
373           [this](Unused, int num_tokens, Unused, SignedTokenCallback callback) {
374             fake_tokens_ = MakeExpiredTokens(num_tokens);
375             std::move(callback)(absl::MakeSpan(fake_tokens_));
376           });
377 
378   int num_tokens = kBlindSignAuthRequestMaxTokens / 2;
379   QuicheNotification first;
380   SignedTokenCallback first_callback =
381       [&first](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
382         EXPECT_THAT(tokens.status().code(),
383                     absl::StatusCode::kResourceExhausted);
384         first.Notify();
385       };
386 
387   cached_blind_sign_auth_->GetTokens(
388       oauth_token_, num_tokens, ProxyLayer::kProxyA, std::move(first_callback));
389   first.WaitForNotification();
390 
391   cached_blind_sign_auth_->ClearCache();
392 
393   QuicheNotification second;
394   SignedTokenCallback second_callback =
395       [&second](absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
396         EXPECT_THAT(tokens.status().code(),
397                     absl::StatusCode::kResourceExhausted);
398         second.Notify();
399       };
400 
401   cached_blind_sign_auth_->GetTokens(oauth_token_, num_tokens,
402                                      ProxyLayer::kProxyA,
403                                      std::move(second_callback));
404   second.WaitForNotification();
405 }
406 
407 }  // namespace
408 }  // namespace test
409 }  // namespace quiche
410