1use core::hash::Hasher;
2
3use super::config::{ClientCredentialResolver, ClientSessionStore};
4use super::{ClientSessionKey, CredentialRequest, Tls12Session, Tls13Session};
5use crate::crypto::SelectedCredential;
6use crate::crypto::kx::NamedGroup;
7use crate::enums::CertificateType;
8
9#[derive(Debug)]
11pub(super) struct NoClientSessionStorage;
12
13impl ClientSessionStore for NoClientSessionStorage {
14 fn set_kx_hint(&self, _: ClientSessionKey<'static>, _: NamedGroup) {}
15
16 fn kx_hint(&self, _: &ClientSessionKey<'_>) -> Option<NamedGroup> {
17 None
18 }
19
20 fn set_tls12_session(&self, _: ClientSessionKey<'static>, _: Tls12Session) {}
21
22 fn tls12_session(&self, _: &ClientSessionKey<'_>) -> Option<Tls12Session> {
23 None
24 }
25
26 fn remove_tls12_session(&self, _: &ClientSessionKey<'_>) {}
27
28 fn insert_tls13_ticket(&self, _: ClientSessionKey<'static>, _: Tls13Session) {}
29
30 fn take_tls13_ticket(&self, _: &ClientSessionKey<'_>) -> Option<Tls13Session> {
31 None
32 }
33}
34
35mod cache {
36 use alloc::collections::VecDeque;
37 use core::fmt;
38
39 use super::*;
40 use crate::client::Tls13Session;
41 use crate::crypto::kx::NamedGroup;
42 use crate::limited_cache;
43 use crate::lock::Mutex;
44
45 const MAX_TLS13_TICKETS_PER_SERVER: usize = 8;
46
47 struct ServerData {
48 kx_hint: Option<NamedGroup>,
49
50 tls12: Option<Tls12Session>,
52
53 tls13: VecDeque<Tls13Session>,
55 }
56
57 impl Default for ServerData {
58 fn default() -> Self {
59 Self {
60 kx_hint: None,
61 tls12: None,
62 tls13: VecDeque::with_capacity(MAX_TLS13_TICKETS_PER_SERVER),
63 }
64 }
65 }
66
67 pub struct ClientSessionMemoryCache {
72 servers: Mutex<limited_cache::LimitedCache<ClientSessionKey<'static>, ServerData>>,
73 }
74
75 impl ClientSessionMemoryCache {
76 pub fn new(size: usize) -> Self {
87 let max_servers = size.saturating_add(MAX_TLS13_TICKETS_PER_SERVER - 1)
89 / MAX_TLS13_TICKETS_PER_SERVER;
90 Self {
91 servers: Mutex::new(limited_cache::LimitedCache::new(max_servers)),
92 }
93 }
94 }
95
96 impl ClientSessionStore for ClientSessionMemoryCache {
97 fn set_kx_hint(&self, key: ClientSessionKey<'static>, group: NamedGroup) {
98 self.servers
99 .lock()
100 .unwrap()
101 .get_or_insert_default_and_edit(key, |data| data.kx_hint = Some(group));
102 }
103
104 fn kx_hint(&self, key: &ClientSessionKey<'_>) -> Option<NamedGroup> {
105 self.servers
106 .lock()
107 .unwrap()
108 .get(key)
109 .and_then(|sd| sd.kx_hint)
110 }
111
112 fn set_tls12_session(&self, key: ClientSessionKey<'static>, value: Tls12Session) {
113 self.servers
114 .lock()
115 .unwrap()
116 .get_or_insert_default_and_edit(key, |data| data.tls12 = Some(value));
117 }
118
119 fn tls12_session(&self, key: &ClientSessionKey<'_>) -> Option<Tls12Session> {
120 self.servers
121 .lock()
122 .unwrap()
123 .get(key)
124 .and_then(|sd| sd.tls12.as_ref().cloned())
125 }
126
127 fn remove_tls12_session(&self, key: &ClientSessionKey<'static>) {
128 self.servers
129 .lock()
130 .unwrap()
131 .get_mut(key)
132 .and_then(|data| data.tls12.take());
133 }
134
135 fn insert_tls13_ticket(&self, key: ClientSessionKey<'static>, value: Tls13Session) {
136 self.servers
137 .lock()
138 .unwrap()
139 .get_or_insert_default_and_edit(key, |data| {
140 if data.tls13.len() == data.tls13.capacity() {
141 data.tls13.pop_front();
142 }
143 data.tls13.push_back(value);
144 });
145 }
146
147 fn take_tls13_ticket(&self, key: &ClientSessionKey<'static>) -> Option<Tls13Session> {
148 self.servers
149 .lock()
150 .unwrap()
151 .get_mut(key)
152 .and_then(|data| data.tls13.pop_back())
153 }
154 }
155
156 impl fmt::Debug for ClientSessionMemoryCache {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 f.debug_struct("ClientSessionMemoryCache")
160 .finish_non_exhaustive()
161 }
162 }
163}
164
165pub use cache::ClientSessionMemoryCache;
166
167#[derive(Debug)]
168pub(super) struct FailResolveClientCert {}
169
170impl ClientCredentialResolver for FailResolveClientCert {
171 fn resolve(&self, _: &CredentialRequest<'_>) -> Option<SelectedCredential> {
172 None
173 }
174
175 fn supported_certificate_types(&self) -> &'static [CertificateType] {
176 &[]
177 }
178
179 fn hash_config(&self, _: &mut dyn Hasher) {}
180}
181
182#[cfg(test)]
183mod tests {
184 use alloc::vec::Vec;
185 use core::time::Duration;
186
187 use pki_types::{CertificateDer, ServerName, UnixTime};
188
189 use super::{ClientSessionMemoryCache, NoClientSessionStorage};
190 use crate::client::{
191 ClientSessionKey, ClientSessionStore, Tls12Session, Tls13ClientSessionInput, Tls13Session,
192 };
193 use crate::crypto::kx::NamedGroup;
194 use crate::crypto::{
195 CertificateIdentity, CipherSuite, Identity, TEST_PROVIDER, tls12_suite, tls13_suite,
196 };
197 use crate::msgs::{
198 NewSessionTicketExtensions, NewSessionTicketPayloadTls13, SessionId, SizedPayload,
199 };
200 use crate::sync::Arc;
201 use crate::tls13::Tls13ProtocolSuite;
202 use crate::verify::VerifiedIdentity;
203
204 #[test]
205 fn test_noclientsessionstorage_does_nothing() {
206 let c = NoClientSessionStorage {};
207 let key = session_key("example.com");
208 let now = UnixTime::now();
209
210 c.set_kx_hint(key.clone(), NamedGroup::X25519);
211 assert_eq!(None, c.kx_hint(&key));
212
213 {
214 c.set_tls12_session(
215 key.clone(),
216 Tls12Session::new(
217 tls12_suite(CipherSuite(0xff12), &TEST_PROVIDER),
218 SessionId::empty(),
219 Arc::new(SizedPayload::empty()),
220 &[0u8; 48],
221 VerifiedIdentity::assertion(Identity::X509(CertificateIdentity {
222 end_entity: CertificateDer::from(&[][..]),
223 intermediates: Vec::new(),
224 })),
225 now,
226 Duration::ZERO,
227 true,
228 ),
229 );
230 assert!(c.tls12_session(&key).is_none());
231 c.remove_tls12_session(&key);
232 }
233
234 c.insert_tls13_ticket(key.clone(), tls13_session(now));
235 assert!(c.take_tls13_ticket(&key).is_none());
236 }
237
238 #[test]
241 fn test_clientsessionmemorycache_small_sizes_retain_server() {
242 for size in 1..=8 {
243 let c = ClientSessionMemoryCache::new(size);
244 let key = session_key("example.com");
245
246 c.set_kx_hint(key.clone(), NamedGroup::X25519);
247 assert_eq!(c.kx_hint(&key), Some(NamedGroup::X25519), "size {size}");
248
249 c.insert_tls13_ticket(key.clone(), tls13_session(UnixTime::now()));
250 assert!(c.take_tls13_ticket(&key).is_some(), "size {size}");
251 }
252 }
253
254 #[test]
257 fn test_clientsessionmemorycache_evicts_oldest_server() {
258 let c = ClientSessionMemoryCache::new(9);
259 let first = session_key("first.example.com");
260 let second = session_key("second.example.com");
261 let third = session_key("third.example.com");
262
263 c.set_kx_hint(first.clone(), NamedGroup::X25519);
264 c.set_kx_hint(second.clone(), NamedGroup::X25519);
265 c.set_kx_hint(third.clone(), NamedGroup::X25519);
266
267 assert_eq!(c.kx_hint(&first), None);
268 assert_eq!(c.kx_hint(&second), Some(NamedGroup::X25519));
269 assert_eq!(c.kx_hint(&third), Some(NamedGroup::X25519));
270 }
271
272 fn session_key(name: &'static str) -> ClientSessionKey<'static> {
275 ClientSessionKey {
276 config_hash: Default::default(),
277 server_name: ServerName::try_from(name).unwrap(),
278 }
279 }
280
281 fn tls13_session(now: UnixTime) -> Tls13Session {
285 Tls13Session::new(
286 &NewSessionTicketPayloadTls13 {
287 lifetime: Duration::ZERO,
288 age_add: 0,
289 nonce: SizedPayload::empty(),
290 ticket: Arc::new(SizedPayload::empty()),
291 extensions: NewSessionTicketExtensions {
292 max_early_data_size: None,
293 },
294 },
295 Tls13ClientSessionInput {
296 suite: Tls13ProtocolSuite::Tcp(tls13_suite(CipherSuite(0xff13), &TEST_PROVIDER)),
297 peer_identity: VerifiedIdentity::assertion(Identity::X509(CertificateIdentity {
298 end_entity: CertificateDer::from(&[][..]),
299 intermediates: Vec::new(),
300 })),
301 quic_params: None,
302 },
303 &[],
304 now,
305 )
306 }
307}