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