Skip to main content

rustls/client/
handy.rs

1use core::hash::Hasher;
2
3use super::{
4    ClientSessionKey, CredentialRequest, Tls12ClientSessionValue, Tls13ClientSessionValue,
5};
6use crate::client;
7use crate::crypto::SelectedCredential;
8use crate::crypto::kx::NamedGroup;
9use crate::enums::CertificateType;
10
11/// An implementer of `ClientSessionStore` which does nothing.
12#[derive(Debug)]
13pub(super) struct NoClientSessionStorage;
14
15impl client::ClientSessionStore for NoClientSessionStorage {
16    fn set_kx_hint(&self, _: ClientSessionKey<'static>, _: NamedGroup) {}
17
18    fn kx_hint(&self, _: &ClientSessionKey<'_>) -> Option<NamedGroup> {
19        None
20    }
21
22    fn set_tls12_session(&self, _: ClientSessionKey<'static>, _: Tls12ClientSessionValue) {}
23
24    fn tls12_session(&self, _: &ClientSessionKey<'_>) -> Option<Tls12ClientSessionValue> {
25        None
26    }
27
28    fn remove_tls12_session(&self, _: &ClientSessionKey<'_>) {}
29
30    fn insert_tls13_ticket(&self, _: ClientSessionKey<'static>, _: Tls13ClientSessionValue) {}
31
32    fn take_tls13_ticket(&self, _: &ClientSessionKey<'_>) -> Option<Tls13ClientSessionValue> {
33        None
34    }
35}
36
37#[cfg(any(feature = "std", feature = "hashbrown"))]
38mod cache {
39    use alloc::collections::VecDeque;
40    use core::fmt;
41
42    use super::ClientSessionKey;
43    use crate::client::{Tls12ClientSessionValue, Tls13ClientSessionValue};
44    use crate::crypto::kx::NamedGroup;
45    use crate::limited_cache;
46    use crate::lock::Mutex;
47
48    const MAX_TLS13_TICKETS_PER_SERVER: usize = 8;
49
50    struct ServerData {
51        kx_hint: Option<NamedGroup>,
52
53        // Zero or one TLS1.2 sessions.
54        tls12: Option<Tls12ClientSessionValue>,
55
56        // Up to MAX_TLS13_TICKETS_PER_SERVER TLS1.3 tickets, oldest first.
57        tls13: VecDeque<Tls13ClientSessionValue>,
58    }
59
60    impl Default for ServerData {
61        fn default() -> Self {
62            Self {
63                kx_hint: None,
64                tls12: None,
65                tls13: VecDeque::with_capacity(MAX_TLS13_TICKETS_PER_SERVER),
66            }
67        }
68    }
69
70    /// An implementer of `ClientSessionStore` that stores everything
71    /// in memory.
72    ///
73    /// It enforces a limit on the number of entries to bound memory usage.
74    pub struct ClientSessionMemoryCache {
75        servers: Mutex<limited_cache::LimitedCache<ClientSessionKey<'static>, ServerData>>,
76    }
77
78    impl ClientSessionMemoryCache {
79        /// Make a new ClientSessionMemoryCache.  `size` is the
80        /// maximum number of stored sessions.
81        #[cfg(feature = "std")]
82        pub fn new(size: usize) -> Self {
83            let max_servers = size.saturating_add(MAX_TLS13_TICKETS_PER_SERVER - 1)
84                / MAX_TLS13_TICKETS_PER_SERVER;
85            Self {
86                servers: Mutex::new(limited_cache::LimitedCache::new(max_servers)),
87            }
88        }
89
90        /// Make a new ClientSessionMemoryCache.  `size` is the
91        /// maximum number of stored sessions.
92        #[cfg(not(feature = "std"))]
93        pub fn new<M: crate::lock::MakeMutex>(size: usize) -> Self {
94            let max_servers = size.saturating_add(MAX_TLS13_TICKETS_PER_SERVER - 1)
95                / MAX_TLS13_TICKETS_PER_SERVER;
96            Self {
97                servers: Mutex::new::<M>(limited_cache::LimitedCache::new(max_servers)),
98            }
99        }
100    }
101
102    impl super::client::ClientSessionStore for ClientSessionMemoryCache {
103        fn set_kx_hint(&self, key: ClientSessionKey<'static>, group: NamedGroup) {
104            self.servers
105                .lock()
106                .unwrap()
107                .get_or_insert_default_and_edit(key, |data| data.kx_hint = Some(group));
108        }
109
110        fn kx_hint(&self, key: &ClientSessionKey<'_>) -> Option<NamedGroup> {
111            self.servers
112                .lock()
113                .unwrap()
114                .get(key)
115                .and_then(|sd| sd.kx_hint)
116        }
117
118        fn set_tls12_session(
119            &self,
120            key: ClientSessionKey<'static>,
121            value: Tls12ClientSessionValue,
122        ) {
123            self.servers
124                .lock()
125                .unwrap()
126                .get_or_insert_default_and_edit(key.clone(), |data| data.tls12 = Some(value));
127        }
128
129        fn tls12_session(&self, key: &ClientSessionKey<'_>) -> Option<Tls12ClientSessionValue> {
130            self.servers
131                .lock()
132                .unwrap()
133                .get(key)
134                .and_then(|sd| sd.tls12.as_ref().cloned())
135        }
136
137        fn remove_tls12_session(&self, key: &ClientSessionKey<'static>) {
138            self.servers
139                .lock()
140                .unwrap()
141                .get_mut(key)
142                .and_then(|data| data.tls12.take());
143        }
144
145        fn insert_tls13_ticket(
146            &self,
147            key: ClientSessionKey<'static>,
148            value: Tls13ClientSessionValue,
149        ) {
150            self.servers
151                .lock()
152                .unwrap()
153                .get_or_insert_default_and_edit(key.clone(), |data| {
154                    if data.tls13.len() == data.tls13.capacity() {
155                        data.tls13.pop_front();
156                    }
157                    data.tls13.push_back(value);
158                });
159        }
160
161        fn take_tls13_ticket(
162            &self,
163            key: &ClientSessionKey<'static>,
164        ) -> Option<Tls13ClientSessionValue> {
165            self.servers
166                .lock()
167                .unwrap()
168                .get_mut(key)
169                .and_then(|data| data.tls13.pop_back())
170        }
171    }
172
173    impl fmt::Debug for ClientSessionMemoryCache {
174        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175            // Note: we omit self.servers as it may contain sensitive data.
176            f.debug_struct("ClientSessionMemoryCache")
177                .finish_non_exhaustive()
178        }
179    }
180}
181
182#[cfg(any(feature = "std", feature = "hashbrown"))]
183pub use cache::ClientSessionMemoryCache;
184
185#[derive(Debug)]
186pub(super) struct FailResolveClientCert {}
187
188impl client::ClientCredentialResolver for FailResolveClientCert {
189    fn resolve(&self, _: &CredentialRequest<'_>) -> Option<SelectedCredential> {
190        None
191    }
192
193    fn supported_certificate_types(&self) -> &'static [CertificateType] {
194        &[]
195    }
196
197    fn hash_config(&self, _: &mut dyn Hasher) {}
198}
199
200#[cfg(test)]
201mod tests {
202    use alloc::vec::Vec;
203    use core::time::Duration;
204
205    use pki_types::{CertificateDer, ServerName, UnixTime};
206
207    use super::NoClientSessionStorage;
208    use crate::client::{
209        ClientSessionKey, ClientSessionStore, Tls12ClientSessionValue, Tls13ClientSessionInput,
210        Tls13ClientSessionValue,
211    };
212    use crate::crypto::kx::NamedGroup;
213    use crate::crypto::{
214        CertificateIdentity, CipherSuite, Identity, TEST_PROVIDER, tls12_suite, tls13_suite,
215    };
216    use crate::msgs::{SessionId, SizedPayload};
217    use crate::sync::Arc;
218
219    #[test]
220    fn test_noclientsessionstorage_does_nothing() {
221        let c = NoClientSessionStorage {};
222        let server_name = ServerName::try_from("example.com").unwrap();
223        let key = ClientSessionKey {
224            config_hash: Default::default(),
225            server_name,
226        };
227        let now = UnixTime::now();
228
229        c.set_kx_hint(key.clone(), NamedGroup::X25519);
230        assert_eq!(None, c.kx_hint(&key));
231
232        {
233            c.set_tls12_session(
234                key.clone(),
235                Tls12ClientSessionValue::new(
236                    tls12_suite(CipherSuite::Unknown(0xff12), &TEST_PROVIDER),
237                    SessionId::empty(),
238                    Arc::new(SizedPayload::empty()),
239                    &[0u8; 48],
240                    Identity::X509(CertificateIdentity {
241                        end_entity: CertificateDer::from(&[][..]),
242                        intermediates: Vec::new(),
243                    }),
244                    now,
245                    Duration::ZERO,
246                    true,
247                ),
248            );
249            assert!(c.tls12_session(&key).is_none());
250            c.remove_tls12_session(&key);
251        }
252
253        c.insert_tls13_ticket(
254            key.clone(),
255            Tls13ClientSessionValue::new(
256                Tls13ClientSessionInput {
257                    suite: tls13_suite(CipherSuite::Unknown(0xff13), &TEST_PROVIDER),
258                    peer_identity: Identity::X509(CertificateIdentity {
259                        end_entity: CertificateDer::from(&[][..]),
260                        intermediates: Vec::new(),
261                    }),
262                    quic_params: None,
263                },
264                Arc::new(SizedPayload::empty()),
265                &[],
266                now,
267                Duration::ZERO,
268                0,
269                0,
270            ),
271        );
272
273        assert!(c.take_tls13_ticket(&key).is_none());
274    }
275}