Skip to main content

rustls/client/
handy.rs

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/// An implementer of `ClientSessionStore` which does nothing.
10#[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        // Zero or one TLS1.2 sessions.
51        tls12: Option<Tls12Session>,
52
53        // Up to MAX_TLS13_TICKETS_PER_SERVER TLS1.3 tickets, oldest first.
54        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    /// An implementer of `ClientSessionStore` that stores everything
68    /// in memory.
69    ///
70    /// It enforces a limit on the number of entries to bound memory usage.
71    pub struct ClientSessionMemoryCache {
72        servers: Mutex<limited_cache::LimitedCache<ClientSessionKey<'static>, ServerData>>,
73    }
74
75    impl ClientSessionMemoryCache {
76        /// Make a new ClientSessionMemoryCache.  `size` is the
77        /// maximum number of stored sessions.
78        pub fn new(size: usize) -> Self {
79            let max_servers = size.saturating_add(MAX_TLS13_TICKETS_PER_SERVER - 1)
80                / MAX_TLS13_TICKETS_PER_SERVER;
81            Self {
82                servers: Mutex::new(limited_cache::LimitedCache::new(max_servers)),
83            }
84        }
85    }
86
87    impl ClientSessionStore for ClientSessionMemoryCache {
88        fn set_kx_hint(&self, key: ClientSessionKey<'static>, group: NamedGroup) {
89            self.servers
90                .lock()
91                .unwrap()
92                .get_or_insert_default_and_edit(key, |data| data.kx_hint = Some(group));
93        }
94
95        fn kx_hint(&self, key: &ClientSessionKey<'_>) -> Option<NamedGroup> {
96            self.servers
97                .lock()
98                .unwrap()
99                .get(key)
100                .and_then(|sd| sd.kx_hint)
101        }
102
103        fn set_tls12_session(&self, key: ClientSessionKey<'static>, value: Tls12Session) {
104            self.servers
105                .lock()
106                .unwrap()
107                .get_or_insert_default_and_edit(key.clone(), |data| data.tls12 = Some(value));
108        }
109
110        fn tls12_session(&self, key: &ClientSessionKey<'_>) -> Option<Tls12Session> {
111            self.servers
112                .lock()
113                .unwrap()
114                .get(key)
115                .and_then(|sd| sd.tls12.as_ref().cloned())
116        }
117
118        fn remove_tls12_session(&self, key: &ClientSessionKey<'static>) {
119            self.servers
120                .lock()
121                .unwrap()
122                .get_mut(key)
123                .and_then(|data| data.tls12.take());
124        }
125
126        fn insert_tls13_ticket(&self, key: ClientSessionKey<'static>, value: Tls13Session) {
127            self.servers
128                .lock()
129                .unwrap()
130                .get_or_insert_default_and_edit(key.clone(), |data| {
131                    if data.tls13.len() == data.tls13.capacity() {
132                        data.tls13.pop_front();
133                    }
134                    data.tls13.push_back(value);
135                });
136        }
137
138        fn take_tls13_ticket(&self, key: &ClientSessionKey<'static>) -> Option<Tls13Session> {
139            self.servers
140                .lock()
141                .unwrap()
142                .get_mut(key)
143                .and_then(|data| data.tls13.pop_back())
144        }
145    }
146
147    impl fmt::Debug for ClientSessionMemoryCache {
148        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149            // Note: we omit self.servers as it may contain sensitive data.
150            f.debug_struct("ClientSessionMemoryCache")
151                .finish_non_exhaustive()
152        }
153    }
154}
155
156pub use cache::ClientSessionMemoryCache;
157
158#[derive(Debug)]
159pub(super) struct FailResolveClientCert {}
160
161impl ClientCredentialResolver for FailResolveClientCert {
162    fn resolve(&self, _: &CredentialRequest<'_>) -> Option<SelectedCredential> {
163        None
164    }
165
166    fn supported_certificate_types(&self) -> &'static [CertificateType] {
167        &[]
168    }
169
170    fn hash_config(&self, _: &mut dyn Hasher) {}
171}
172
173#[cfg(test)]
174mod tests {
175    use alloc::vec::Vec;
176    use core::time::Duration;
177
178    use pki_types::{CertificateDer, ServerName, UnixTime};
179
180    use super::NoClientSessionStorage;
181    use crate::client::{
182        ClientSessionKey, ClientSessionStore, Tls12Session, Tls13ClientSessionInput, Tls13Session,
183    };
184    use crate::crypto::kx::NamedGroup;
185    use crate::crypto::{
186        CertificateIdentity, CipherSuite, Identity, TEST_PROVIDER, tls12_suite, tls13_suite,
187    };
188    use crate::msgs::{SessionId, SizedPayload};
189    use crate::sync::Arc;
190
191    #[test]
192    fn test_noclientsessionstorage_does_nothing() {
193        let c = NoClientSessionStorage {};
194        let server_name = ServerName::try_from("example.com").unwrap();
195        let key = ClientSessionKey {
196            config_hash: Default::default(),
197            server_name,
198        };
199        let now = UnixTime::now();
200
201        c.set_kx_hint(key.clone(), NamedGroup::X25519);
202        assert_eq!(None, c.kx_hint(&key));
203
204        {
205            c.set_tls12_session(
206                key.clone(),
207                Tls12Session::new(
208                    tls12_suite(CipherSuite::Unknown(0xff12), &TEST_PROVIDER),
209                    SessionId::empty(),
210                    Arc::new(SizedPayload::empty()),
211                    &[0u8; 48],
212                    Identity::X509(CertificateIdentity {
213                        end_entity: CertificateDer::from(&[][..]),
214                        intermediates: Vec::new(),
215                    }),
216                    now,
217                    Duration::ZERO,
218                    true,
219                ),
220            );
221            assert!(c.tls12_session(&key).is_none());
222            c.remove_tls12_session(&key);
223        }
224
225        c.insert_tls13_ticket(
226            key.clone(),
227            Tls13Session::new(
228                Tls13ClientSessionInput {
229                    suite: tls13_suite(CipherSuite::Unknown(0xff13), &TEST_PROVIDER),
230                    peer_identity: Identity::X509(CertificateIdentity {
231                        end_entity: CertificateDer::from(&[][..]),
232                        intermediates: Vec::new(),
233                    }),
234                    quic_params: None,
235                },
236                Arc::new(SizedPayload::empty()),
237                &[],
238                now,
239                Duration::ZERO,
240                0,
241                0,
242            ),
243        );
244
245        assert!(c.take_tls13_ticket(&key).is_none());
246    }
247}