rustls/server/
handy.rs

1use alloc::vec::Vec;
2use core::fmt::Debug;
3
4use crate::server;
5
6/// Something which never stores sessions.
7#[expect(clippy::exhaustive_structs)]
8#[derive(Debug)]
9pub struct NoServerSessionStorage {}
10
11impl server::StoresServerSessions for NoServerSessionStorage {
12    fn put(&self, _id: Vec<u8>, _sec: Vec<u8>) -> bool {
13        false
14    }
15    fn get(&self, _id: &[u8]) -> Option<Vec<u8>> {
16        None
17    }
18    fn take(&self, _id: &[u8]) -> Option<Vec<u8>> {
19        None
20    }
21    fn can_cache(&self) -> bool {
22        false
23    }
24}
25
26#[cfg(any(feature = "std", feature = "hashbrown"))]
27mod cache {
28    use alloc::vec::Vec;
29    use core::fmt::{Debug, Formatter};
30
31    use crate::lock::Mutex;
32    use crate::sync::Arc;
33    use crate::{limited_cache, server};
34
35    /// An implementer of `StoresServerSessions` that stores everything
36    /// in memory.  If enforces a limit on the number of stored sessions
37    /// to bound memory usage.
38    pub struct ServerSessionMemoryCache {
39        cache: Mutex<limited_cache::LimitedCache<Vec<u8>, Vec<u8>>>,
40    }
41
42    impl ServerSessionMemoryCache {
43        /// Make a new ServerSessionMemoryCache.  `size` is the maximum
44        /// number of stored sessions, and may be rounded-up for
45        /// efficiency.
46        #[cfg(feature = "std")]
47        pub fn new(size: usize) -> Arc<Self> {
48            Arc::new(Self {
49                cache: Mutex::new(limited_cache::LimitedCache::new(size)),
50            })
51        }
52
53        /// Make a new ServerSessionMemoryCache.  `size` is the maximum
54        /// number of stored sessions, and may be rounded-up for
55        /// efficiency.
56        #[cfg(not(feature = "std"))]
57        pub fn new<M: crate::lock::MakeMutex>(size: usize) -> Arc<Self> {
58            Arc::new(Self {
59                cache: Mutex::new::<M>(limited_cache::LimitedCache::new(size)),
60            })
61        }
62    }
63
64    impl server::StoresServerSessions for ServerSessionMemoryCache {
65        fn put(&self, key: Vec<u8>, value: Vec<u8>) -> bool {
66            self.cache
67                .lock()
68                .unwrap()
69                .insert(key, value);
70            true
71        }
72
73        fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
74            self.cache
75                .lock()
76                .unwrap()
77                .get(key)
78                .cloned()
79        }
80
81        fn take(&self, key: &[u8]) -> Option<Vec<u8>> {
82            self.cache.lock().unwrap().remove(key)
83        }
84
85        fn can_cache(&self) -> bool {
86            true
87        }
88    }
89
90    impl Debug for ServerSessionMemoryCache {
91        fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
92            f.debug_struct("ServerSessionMemoryCache")
93                .finish()
94        }
95    }
96
97    #[cfg(test)]
98    mod tests {
99        use std::vec;
100
101        use super::*;
102        use crate::server::StoresServerSessions;
103
104        #[test]
105        fn test_serversessionmemorycache_accepts_put() {
106            let c = ServerSessionMemoryCache::new(4);
107            assert!(c.put(vec![0x01], vec![0x02]));
108        }
109
110        #[test]
111        fn test_serversessionmemorycache_persists_put() {
112            let c = ServerSessionMemoryCache::new(4);
113            assert!(c.put(vec![0x01], vec![0x02]));
114            assert_eq!(c.get(&[0x01]), Some(vec![0x02]));
115            assert_eq!(c.get(&[0x01]), Some(vec![0x02]));
116        }
117
118        #[test]
119        fn test_serversessionmemorycache_overwrites_put() {
120            let c = ServerSessionMemoryCache::new(4);
121            assert!(c.put(vec![0x01], vec![0x02]));
122            assert!(c.put(vec![0x01], vec![0x04]));
123            assert_eq!(c.get(&[0x01]), Some(vec![0x04]));
124        }
125
126        #[test]
127        fn test_serversessionmemorycache_drops_to_maintain_size_invariant() {
128            let c = ServerSessionMemoryCache::new(2);
129            assert!(c.put(vec![0x01], vec![0x02]));
130            assert!(c.put(vec![0x03], vec![0x04]));
131            assert!(c.put(vec![0x05], vec![0x06]));
132            assert!(c.put(vec![0x07], vec![0x08]));
133            assert!(c.put(vec![0x09], vec![0x0a]));
134
135            let count = c.get(&[0x01]).iter().count()
136                + c.get(&[0x03]).iter().count()
137                + c.get(&[0x05]).iter().count()
138                + c.get(&[0x07]).iter().count()
139                + c.get(&[0x09]).iter().count();
140
141            assert!(count < 5);
142        }
143    }
144}
145
146#[cfg(any(feature = "std", feature = "hashbrown"))]
147pub use cache::ServerSessionMemoryCache;
148
149#[cfg(any(feature = "std", feature = "hashbrown"))]
150mod sni_resolver {
151    use core::fmt::Debug;
152
153    use pki_types::{DnsName, ServerName};
154
155    use crate::crypto::{CertificateIdentity, Credentials, Identity, SelectedCredential};
156    use crate::error::{Error, PeerIncompatible};
157    use crate::hash_map::HashMap;
158    use crate::server::{self, ClientHello};
159    use crate::sync::Arc;
160    use crate::webpki::{ParsedCertificate, verify_server_name};
161
162    /// Something that resolves do different cert chains/keys based
163    /// on client-supplied server name (via SNI).
164    #[derive(Debug)]
165    pub struct ServerNameResolver {
166        by_name: HashMap<DnsName<'static>, Arc<Credentials>>,
167    }
168
169    impl ServerNameResolver {
170        /// Create a new and empty (i.e., knows no certificates) resolver.
171        pub fn new() -> Self {
172            Self {
173                by_name: HashMap::new(),
174            }
175        }
176
177        /// Add a new `Credentials` to be used for the given SNI `name`.
178        ///
179        /// This function fails if the `name` is not valid for the supplied certificate, or if
180        /// the certificate chain is syntactically faulty.
181        pub fn add(&mut self, name: DnsName<'static>, ck: Credentials) -> Result<(), Error> {
182            // Check the certificate chain for validity:
183            // - it should be non-empty list
184            // - the first certificate should be parsable as a x509v3,
185            // - the first certificate should quote the given server name
186            //   (if provided)
187            //
188            // These checks are not security-sensitive.  They are the
189            // *server* attempting to detect accidental misconfiguration.
190
191            let wrapped = ServerName::DnsName(name);
192            if let Identity::X509(CertificateIdentity { end_entity, .. }) = &*ck.identity {
193                let parsed = ParsedCertificate::try_from(end_entity)?;
194                verify_server_name(&parsed, &wrapped)?;
195            }
196
197            let ServerName::DnsName(name) = wrapped else {
198                unreachable!()
199            };
200
201            self.by_name.insert(name, Arc::new(ck));
202            Ok(())
203        }
204    }
205
206    impl server::ServerCredentialResolver for ServerNameResolver {
207        fn resolve(&self, client_hello: &ClientHello<'_>) -> Result<SelectedCredential, Error> {
208            let Some(name) = client_hello.server_name() else {
209                return Err(PeerIncompatible::NoServerNameProvided.into());
210            };
211
212            let Some(credentials) = self.by_name.get(name) else {
213                return Err(Error::NoSuitableCertificate);
214            };
215
216            match credentials.signer(client_hello.signature_schemes) {
217                Some(signer) => Ok(signer),
218                None => Err(PeerIncompatible::NoSignatureSchemesInCommon.into()),
219            }
220        }
221    }
222
223    #[cfg(test)]
224    mod tests {
225        use alloc::borrow::Cow;
226
227        use super::*;
228        use crate::server::ServerCredentialResolver;
229
230        #[test]
231        fn test_server_name_resolver_requires_sni() {
232            let rscsni = ServerNameResolver::new();
233            assert!(
234                rscsni
235                    .resolve(&ClientHello {
236                        server_name: None,
237                        signature_schemes: &[],
238                        alpn: None,
239                        server_cert_types: None,
240                        client_cert_types: None,
241                        cipher_suites: &[],
242                        certificate_authorities: None,
243                        named_groups: None,
244                    })
245                    .is_err()
246            );
247        }
248
249        #[test]
250        fn test_server_name_resolver_handles_unknown_name() {
251            let rscsni = ServerNameResolver::new();
252            let name = DnsName::try_from("hello.com")
253                .unwrap()
254                .to_owned();
255            assert!(
256                rscsni
257                    .resolve(&ClientHello {
258                        server_name: Some(Cow::Borrowed(&name)),
259                        signature_schemes: &[],
260                        alpn: None,
261                        server_cert_types: None,
262                        client_cert_types: None,
263                        cipher_suites: &[],
264                        certificate_authorities: None,
265                        named_groups: None,
266                    })
267                    .is_err()
268            );
269        }
270    }
271}
272
273#[cfg(any(feature = "std", feature = "hashbrown"))]
274pub use sni_resolver::ServerNameResolver;
275
276#[cfg(test)]
277mod tests {
278    use std::vec;
279
280    use super::*;
281    use crate::server::StoresServerSessions;
282
283    #[test]
284    fn test_noserversessionstorage_drops_put() {
285        let c = NoServerSessionStorage {};
286        assert!(!c.put(vec![0x01], vec![0x02]));
287    }
288
289    #[test]
290    fn test_noserversessionstorage_denies_gets() {
291        let c = NoServerSessionStorage {};
292        c.put(vec![0x01], vec![0x02]);
293        assert_eq!(c.get(&[]), None);
294        assert_eq!(c.get(&[0x01]), None);
295        assert_eq!(c.get(&[0x02]), None);
296    }
297
298    #[test]
299    fn test_noserversessionstorage_denies_takes() {
300        let c = NoServerSessionStorage {};
301        assert_eq!(c.take(&[]), None);
302        assert_eq!(c.take(&[0x01]), None);
303        assert_eq!(c.take(&[0x02]), None);
304    }
305}