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