• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <utility>
8 #include <vector>
9 
10 #include "absl/functional/bind_front.h"
11 #include "absl/status/status.h"
12 #include "absl/status/statusor.h"
13 #include "absl/strings/str_format.h"
14 #include "absl/time/clock.h"
15 #include "absl/time/time.h"
16 #include "absl/types/span.h"
17 #include "quiche/blind_sign_auth/blind_sign_auth_interface.h"
18 #include "quiche/common/platform/api/quiche_logging.h"
19 #include "quiche/common/platform/api/quiche_mutex.h"
20 
21 namespace quiche {
22 
23 constexpr absl::Duration kFreshnessConstant = absl::Minutes(5);
24 
GetTokens(std::string oauth_token,int num_tokens,ProxyLayer proxy_layer,SignedTokenCallback callback)25 void CachedBlindSignAuth::GetTokens(std::string oauth_token, int num_tokens,
26                                     ProxyLayer proxy_layer,
27                                     SignedTokenCallback callback) {
28   if (num_tokens > max_tokens_per_request_) {
29     std::move(callback)(absl::InvalidArgumentError(
30         absl::StrFormat("Number of tokens requested exceeds maximum: %d",
31                         kBlindSignAuthRequestMaxTokens)));
32     return;
33   }
34   if (num_tokens < 0) {
35     std::move(callback)(absl::InvalidArgumentError(absl::StrFormat(
36         "Negative number of tokens requested: %d", num_tokens)));
37     return;
38   }
39 
40   std::vector<BlindSignToken> output_tokens;
41   {
42     QuicheWriterMutexLock lock(&mutex_);
43 
44     RemoveExpiredTokens();
45     // Try to fill the request from cache.
46     if (static_cast<size_t>(num_tokens) <= cached_tokens_.size()) {
47       output_tokens = CreateOutputTokens(num_tokens);
48     }
49   }
50 
51   if (!output_tokens.empty() || num_tokens == 0) {
52     std::move(callback)(absl::MakeSpan(output_tokens));
53     return;
54   }
55 
56   // Make a GetTokensRequest if the cache can't handle the request size.
57   SignedTokenCallback caching_callback =
58       absl::bind_front(&CachedBlindSignAuth::HandleGetTokensResponse, this,
59                        std::move(callback), num_tokens);
60   blind_sign_auth_->GetTokens(oauth_token, kBlindSignAuthRequestMaxTokens,
61                               proxy_layer, std::move(caching_callback));
62 }
63 
HandleGetTokensResponse(SignedTokenCallback callback,int num_tokens,absl::StatusOr<absl::Span<BlindSignToken>> tokens)64 void CachedBlindSignAuth::HandleGetTokensResponse(
65     SignedTokenCallback callback, int num_tokens,
66     absl::StatusOr<absl::Span<BlindSignToken>> tokens) {
67   if (!tokens.ok()) {
68     QUICHE_LOG(WARNING) << "BlindSignAuth::GetTokens failed: "
69                         << tokens.status();
70     std::move(callback)(tokens);
71     return;
72   }
73   if (tokens->size() < static_cast<size_t>(num_tokens) ||
74       tokens->size() > kBlindSignAuthRequestMaxTokens) {
75     QUICHE_LOG(WARNING) << "Expected " << num_tokens << " tokens, got "
76                         << tokens->size();
77   }
78 
79   std::vector<BlindSignToken> output_tokens;
80   size_t cache_size;
81   {
82     QuicheWriterMutexLock lock(&mutex_);
83 
84     // Add returned tokens to cache.
85     for (const BlindSignToken& token : *tokens) {
86       cached_tokens_.push_back(token);
87     }
88     RemoveExpiredTokens();
89     // Return tokens or a ResourceExhaustedError.
90     cache_size = cached_tokens_.size();
91     if (cache_size >= static_cast<size_t>(num_tokens)) {
92       output_tokens = CreateOutputTokens(num_tokens);
93     }
94   }
95 
96   if (!output_tokens.empty()) {
97     std::move(callback)(absl::MakeSpan(output_tokens));
98     return;
99   }
100   std::move(callback)(absl::ResourceExhaustedError(absl::StrFormat(
101       "Requested %d tokens, cache only has %d after GetTokensRequest",
102       num_tokens, cache_size)));
103 }
104 
CreateOutputTokens(int num_tokens)105 std::vector<BlindSignToken> CachedBlindSignAuth::CreateOutputTokens(
106     int num_tokens) {
107   std::vector<BlindSignToken> output_tokens;
108   if (cached_tokens_.size() < static_cast<size_t>(num_tokens)) {
109     QUICHE_LOG(FATAL) << "Check failed, not enough tokens in cache: "
110                       << cached_tokens_.size() << " < " << num_tokens;
111   }
112   for (int i = 0; i < num_tokens; i++) {
113     output_tokens.push_back(std::move(cached_tokens_.front()));
114     cached_tokens_.pop_front();
115   }
116   return output_tokens;
117 }
118 
RemoveExpiredTokens()119 void CachedBlindSignAuth::RemoveExpiredTokens() {
120   size_t original_size = cached_tokens_.size();
121   absl::Time now_plus_five_mins = absl::Now() + kFreshnessConstant;
122   for (size_t i = 0; i < original_size; i++) {
123     BlindSignToken token = std::move(cached_tokens_.front());
124     cached_tokens_.pop_front();
125     if (token.expiration > now_plus_five_mins) {
126       cached_tokens_.push_back(std::move(token));
127     }
128   }
129 }
130 
131 }  // namespace quiche
132