xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/crypto/quic_client_session_cache.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2021 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/quic/core/crypto/quic_client_session_cache.h"
6 
7 #include "quiche/quic/core/quic_clock.h"
8 
9 namespace quic {
10 
11 namespace {
12 
13 const size_t kDefaultMaxEntries = 1024;
14 // Returns false if the SSL |session| doesn't exist or it is expired at |now|.
IsValid(SSL_SESSION * session,uint64_t now)15 bool IsValid(SSL_SESSION* session, uint64_t now) {
16   if (!session) return false;
17 
18   // now_u64 may be slightly behind because of differences in how
19   // time is calculated at this layer versus BoringSSL.
20   // Add a second of wiggle room to account for this.
21   return !(now + 1 < SSL_SESSION_get_time(session) ||
22            now >= SSL_SESSION_get_time(session) +
23                       SSL_SESSION_get_timeout(session));
24 }
25 
DoApplicationStatesMatch(const ApplicationState * state,ApplicationState * other)26 bool DoApplicationStatesMatch(const ApplicationState* state,
27                               ApplicationState* other) {
28   if ((state && !other) || (!state && other)) return false;
29   if ((!state && !other) || *state == *other) return true;
30   return false;
31 }
32 
33 }  // namespace
34 
QuicClientSessionCache()35 QuicClientSessionCache::QuicClientSessionCache()
36     : QuicClientSessionCache(kDefaultMaxEntries) {}
37 
QuicClientSessionCache(size_t max_entries)38 QuicClientSessionCache::QuicClientSessionCache(size_t max_entries)
39     : cache_(max_entries) {}
40 
~QuicClientSessionCache()41 QuicClientSessionCache::~QuicClientSessionCache() { Clear(); }
42 
Insert(const QuicServerId & server_id,bssl::UniquePtr<SSL_SESSION> session,const TransportParameters & params,const ApplicationState * application_state)43 void QuicClientSessionCache::Insert(const QuicServerId& server_id,
44                                     bssl::UniquePtr<SSL_SESSION> session,
45                                     const TransportParameters& params,
46                                     const ApplicationState* application_state) {
47   QUICHE_DCHECK(session) << "TLS session is not inserted into client cache.";
48   auto iter = cache_.Lookup(server_id);
49   if (iter == cache_.end()) {
50     CreateAndInsertEntry(server_id, std::move(session), params,
51                          application_state);
52     return;
53   }
54 
55   QUICHE_DCHECK(iter->second->params);
56   // The states are both the same, so only need to insert sessions.
57   if (params == *iter->second->params &&
58       DoApplicationStatesMatch(application_state,
59                                iter->second->application_state.get())) {
60     iter->second->PushSession(std::move(session));
61     return;
62   }
63   // Erase the existing entry because this Insert call must come from a
64   // different QUIC session.
65   cache_.Erase(iter);
66   CreateAndInsertEntry(server_id, std::move(session), params,
67                        application_state);
68 }
69 
Lookup(const QuicServerId & server_id,QuicWallTime now,const SSL_CTX *)70 std::unique_ptr<QuicResumptionState> QuicClientSessionCache::Lookup(
71     const QuicServerId& server_id, QuicWallTime now, const SSL_CTX* /*ctx*/) {
72   auto iter = cache_.Lookup(server_id);
73   if (iter == cache_.end()) return nullptr;
74 
75   if (!IsValid(iter->second->PeekSession(), now.ToUNIXSeconds())) {
76     QUIC_DLOG(INFO) << "TLS Session expired for host:" << server_id.host();
77     cache_.Erase(iter);
78     return nullptr;
79   }
80   auto state = std::make_unique<QuicResumptionState>();
81   state->tls_session = iter->second->PopSession();
82   if (iter->second->params != nullptr) {
83     state->transport_params =
84         std::make_unique<TransportParameters>(*iter->second->params);
85   }
86   if (iter->second->application_state != nullptr) {
87     state->application_state =
88         std::make_unique<ApplicationState>(*iter->second->application_state);
89   }
90   if (!iter->second->token.empty()) {
91     state->token = iter->second->token;
92     // Clear token after use.
93     iter->second->token.clear();
94   }
95 
96   return state;
97 }
98 
ClearEarlyData(const QuicServerId & server_id)99 void QuicClientSessionCache::ClearEarlyData(const QuicServerId& server_id) {
100   auto iter = cache_.Lookup(server_id);
101   if (iter == cache_.end()) return;
102   for (auto& session : iter->second->sessions) {
103     if (session) {
104       QUIC_DLOG(INFO) << "Clear early data for for host: " << server_id.host();
105       session.reset(SSL_SESSION_copy_without_early_data(session.get()));
106     }
107   }
108 }
109 
OnNewTokenReceived(const QuicServerId & server_id,absl::string_view token)110 void QuicClientSessionCache::OnNewTokenReceived(const QuicServerId& server_id,
111                                                 absl::string_view token) {
112   if (token.empty()) {
113     return;
114   }
115   auto iter = cache_.Lookup(server_id);
116   if (iter == cache_.end()) {
117     return;
118   }
119   iter->second->token = std::string(token);
120 }
121 
RemoveExpiredEntries(QuicWallTime now)122 void QuicClientSessionCache::RemoveExpiredEntries(QuicWallTime now) {
123   auto iter = cache_.begin();
124   while (iter != cache_.end()) {
125     if (!IsValid(iter->second->PeekSession(), now.ToUNIXSeconds())) {
126       iter = cache_.Erase(iter);
127     } else {
128       ++iter;
129     }
130   }
131 }
132 
Clear()133 void QuicClientSessionCache::Clear() { cache_.Clear(); }
134 
CreateAndInsertEntry(const QuicServerId & server_id,bssl::UniquePtr<SSL_SESSION> session,const TransportParameters & params,const ApplicationState * application_state)135 void QuicClientSessionCache::CreateAndInsertEntry(
136     const QuicServerId& server_id, bssl::UniquePtr<SSL_SESSION> session,
137     const TransportParameters& params,
138     const ApplicationState* application_state) {
139   auto entry = std::make_unique<Entry>();
140   entry->PushSession(std::move(session));
141   entry->params = std::make_unique<TransportParameters>(params);
142   if (application_state) {
143     entry->application_state =
144         std::make_unique<ApplicationState>(*application_state);
145   }
146   cache_.Insert(server_id, std::move(entry));
147 }
148 
149 QuicClientSessionCache::Entry::Entry() = default;
150 QuicClientSessionCache::Entry::Entry(Entry&&) = default;
151 QuicClientSessionCache::Entry::~Entry() = default;
152 
PushSession(bssl::UniquePtr<SSL_SESSION> session)153 void QuicClientSessionCache::Entry::PushSession(
154     bssl::UniquePtr<SSL_SESSION> session) {
155   if (sessions[0] != nullptr) {
156     sessions[1] = std::move(sessions[0]);
157   }
158   sessions[0] = std::move(session);
159 }
160 
PopSession()161 bssl::UniquePtr<SSL_SESSION> QuicClientSessionCache::Entry::PopSession() {
162   if (sessions[0] == nullptr) return nullptr;
163   bssl::UniquePtr<SSL_SESSION> session = std::move(sessions[0]);
164   sessions[0] = std::move(sessions[1]);
165   sessions[1] = nullptr;
166   return session;
167 }
168 
PeekSession()169 SSL_SESSION* QuicClientSessionCache::Entry::PeekSession() {
170   return sessions[0].get();
171 }
172 
173 }  // namespace quic
174