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