Skip to main content

rustls/client/
handy.rs

1use pki_types::ServerName;
2
3use crate::enums::SignatureScheme;
4use crate::msgs::persist;
5use crate::sync::Arc;
6use crate::{NamedGroup, client, sign};
7
8/// An implementer of `ClientSessionStore` which does nothing.
9#[derive(Debug)]
10pub(super) struct NoClientSessionStorage;
11
12impl client::ClientSessionStore for NoClientSessionStorage {
13    fn set_kx_hint(&self, _: ServerName<'static>, _: NamedGroup) {}
14
15    fn kx_hint(&self, _: &ServerName<'_>) -> Option<NamedGroup> {
16        None
17    }
18
19    fn set_tls12_session(&self, _: ServerName<'static>, _: persist::Tls12ClientSessionValue) {}
20
21    fn tls12_session(&self, _: &ServerName<'_>) -> Option<persist::Tls12ClientSessionValue> {
22        None
23    }
24
25    fn remove_tls12_session(&self, _: &ServerName<'_>) {}
26
27    fn insert_tls13_ticket(&self, _: ServerName<'static>, _: persist::Tls13ClientSessionValue) {}
28
29    fn take_tls13_ticket(&self, _: &ServerName<'_>) -> Option<persist::Tls13ClientSessionValue> {
30        None
31    }
32}
33
34#[cfg(any(feature = "std", feature = "hashbrown"))]
35mod cache {
36    use alloc::collections::VecDeque;
37    use core::fmt;
38
39    use pki_types::ServerName;
40
41    use crate::lock::Mutex;
42    use crate::msgs::persist;
43    use crate::{NamedGroup, limited_cache};
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        #[cfg(feature = "tls12")]
52        tls12: Option<persist::Tls12ClientSessionValue>,
53
54        // Up to MAX_TLS13_TICKETS_PER_SERVER TLS1.3 tickets, oldest first.
55        tls13: VecDeque<persist::Tls13ClientSessionValue>,
56    }
57
58    impl Default for ServerData {
59        fn default() -> Self {
60            Self {
61                kx_hint: None,
62                #[cfg(feature = "tls12")]
63                tls12: None,
64                tls13: VecDeque::with_capacity(MAX_TLS13_TICKETS_PER_SERVER),
65            }
66        }
67    }
68
69    /// An implementer of `ClientSessionStore` that stores everything
70    /// in memory.
71    ///
72    /// It enforces a limit on the number of entries to bound memory usage.
73    pub struct ClientSessionMemoryCache {
74        servers: Mutex<limited_cache::LimitedCache<ServerName<'static>, ServerData>>,
75    }
76
77    impl ClientSessionMemoryCache {
78        /// Make a new `ClientSessionMemoryCache`.
79        ///
80        /// `size` determines the maximum number of servers to remember:
81        /// `size` divided by `MAX_TLS13_TICKETS_PER_SERVER` (8), rounded
82        /// up. Each server keeps up to 8 TLS 1.3 tickets. When the maximum
83        /// is reached, the server that was added first is evicted.
84        ///
85        /// For example, `new(100)` remembers up to 13 servers. Any `size`
86        /// from 1 to 8 remembers one server, and a `size` of 0 remembers
87        /// none.
88        #[cfg(feature = "std")]
89        pub fn new(size: usize) -> Self {
90            // Round up so that any non-zero size holds at least one server.
91            let max_servers = size.saturating_add(MAX_TLS13_TICKETS_PER_SERVER - 1)
92                / MAX_TLS13_TICKETS_PER_SERVER;
93            Self {
94                servers: Mutex::new(limited_cache::LimitedCache::new(max_servers)),
95            }
96        }
97
98        /// Make a new `ClientSessionMemoryCache`.
99        ///
100        /// `size` determines the maximum number of servers to remember:
101        /// `size` divided by `MAX_TLS13_TICKETS_PER_SERVER` (8), rounded
102        /// up. Each server keeps up to 8 TLS 1.3 tickets. When the maximum
103        /// is reached, the server that was added first is evicted.
104        ///
105        /// For example, `new(100)` remembers up to 13 servers. Any `size`
106        /// from 1 to 8 remembers one server, and a `size` of 0 remembers
107        /// none.
108        #[cfg(not(feature = "std"))]
109        pub fn new<M: crate::lock::MakeMutex>(size: usize) -> Self {
110            // Round up so that any non-zero size holds at least one server.
111            let max_servers = size.saturating_add(MAX_TLS13_TICKETS_PER_SERVER - 1)
112                / MAX_TLS13_TICKETS_PER_SERVER;
113            Self {
114                servers: Mutex::new::<M>(limited_cache::LimitedCache::new(max_servers)),
115            }
116        }
117    }
118
119    impl super::client::ClientSessionStore for ClientSessionMemoryCache {
120        fn set_kx_hint(&self, server_name: ServerName<'static>, group: NamedGroup) {
121            self.servers
122                .lock()
123                .unwrap()
124                .get_or_insert_default_and_edit(server_name, |data| data.kx_hint = Some(group));
125        }
126
127        fn kx_hint(&self, server_name: &ServerName<'_>) -> Option<NamedGroup> {
128            self.servers
129                .lock()
130                .unwrap()
131                .get(server_name)
132                .and_then(|sd| sd.kx_hint)
133        }
134
135        fn set_tls12_session(
136            &self,
137            _server_name: ServerName<'static>,
138            _value: persist::Tls12ClientSessionValue,
139        ) {
140            #[cfg(feature = "tls12")]
141            self.servers
142                .lock()
143                .unwrap()
144                .get_or_insert_default_and_edit(_server_name.clone(), |data| {
145                    data.tls12 = Some(_value)
146                });
147        }
148
149        fn tls12_session(
150            &self,
151            _server_name: &ServerName<'_>,
152        ) -> Option<persist::Tls12ClientSessionValue> {
153            #[cfg(not(feature = "tls12"))]
154            return None;
155
156            #[cfg(feature = "tls12")]
157            self.servers
158                .lock()
159                .unwrap()
160                .get(_server_name)
161                .and_then(|sd| sd.tls12.as_ref().cloned())
162        }
163
164        fn remove_tls12_session(&self, _server_name: &ServerName<'static>) {
165            #[cfg(feature = "tls12")]
166            self.servers
167                .lock()
168                .unwrap()
169                .get_mut(_server_name)
170                .and_then(|data| data.tls12.take());
171        }
172
173        fn insert_tls13_ticket(
174            &self,
175            server_name: ServerName<'static>,
176            value: persist::Tls13ClientSessionValue,
177        ) {
178            self.servers
179                .lock()
180                .unwrap()
181                .get_or_insert_default_and_edit(server_name.clone(), |data| {
182                    if data.tls13.len() == data.tls13.capacity() {
183                        data.tls13.pop_front();
184                    }
185                    data.tls13.push_back(value);
186                });
187        }
188
189        fn take_tls13_ticket(
190            &self,
191            server_name: &ServerName<'static>,
192        ) -> Option<persist::Tls13ClientSessionValue> {
193            self.servers
194                .lock()
195                .unwrap()
196                .get_mut(server_name)
197                .and_then(|data| data.tls13.pop_back())
198        }
199    }
200
201    impl fmt::Debug for ClientSessionMemoryCache {
202        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203            // Note: we omit self.servers as it may contain sensitive data.
204            f.debug_struct("ClientSessionMemoryCache")
205                .finish()
206        }
207    }
208}
209
210#[cfg(any(feature = "std", feature = "hashbrown"))]
211pub use cache::ClientSessionMemoryCache;
212
213#[derive(Debug)]
214pub(super) struct FailResolveClientCert {}
215
216impl client::ResolvesClientCert for FailResolveClientCert {
217    fn resolve(
218        &self,
219        _root_hint_subjects: &[&[u8]],
220        _sigschemes: &[SignatureScheme],
221    ) -> Option<Arc<sign::CertifiedKey>> {
222        None
223    }
224
225    fn has_certs(&self) -> bool {
226        false
227    }
228}
229
230/// An exemplar `ResolvesClientCert` implementation that always resolves to a single
231/// [RFC 7250] raw public key.
232///
233/// [RFC 7250]: https://tools.ietf.org/html/rfc7250
234#[derive(Clone, Debug)]
235pub struct AlwaysResolvesClientRawPublicKeys(Arc<sign::CertifiedKey>);
236impl AlwaysResolvesClientRawPublicKeys {
237    /// Create a new `AlwaysResolvesClientRawPublicKeys` instance.
238    pub fn new(certified_key: Arc<sign::CertifiedKey>) -> Self {
239        Self(certified_key)
240    }
241}
242
243impl client::ResolvesClientCert for AlwaysResolvesClientRawPublicKeys {
244    fn resolve(
245        &self,
246        _root_hint_subjects: &[&[u8]],
247        _sigschemes: &[SignatureScheme],
248    ) -> Option<Arc<sign::CertifiedKey>> {
249        Some(self.0.clone())
250    }
251
252    fn only_raw_public_keys(&self) -> bool {
253        true
254    }
255
256    /// Returns true if the resolver is ready to present an identity.
257    ///
258    /// Even though the function is called `has_certs`, it returns true
259    /// although only an RPK (Raw Public Key) is available, not an actual certificate.
260    fn has_certs(&self) -> bool {
261        true
262    }
263}
264
265#[cfg(test)]
266#[macro_rules_attribute::apply(test_for_each_provider)]
267mod tests {
268    use core::time::Duration;
269    use std::prelude::v1::*;
270    use std::{format, vec};
271
272    use pki_types::{ServerName, UnixTime};
273
274    #[cfg(feature = "std")]
275    use super::ClientSessionMemoryCache;
276    use super::NoClientSessionStorage;
277    use super::provider::cipher_suite;
278    use crate::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
279    use crate::client::{ClientSessionStore, ResolvesClientCert};
280    use crate::msgs::base::PayloadU16;
281    use crate::msgs::enums::NamedGroup;
282    use crate::msgs::handshake::CertificateChain;
283    #[cfg(feature = "tls12")]
284    use crate::msgs::handshake::SessionId;
285    use crate::msgs::persist::Tls13ClientSessionValue;
286    use crate::pki_types::CertificateDer;
287    use crate::suites::SupportedCipherSuite;
288    use crate::sync::Arc;
289    use crate::{DigitallySignedStruct, Error, SignatureScheme, sign};
290
291    #[test]
292    fn test_noclientsessionstorage_does_nothing() {
293        let c = NoClientSessionStorage {};
294        let name = ServerName::try_from("example.com").unwrap();
295        let now = UnixTime::now();
296        let server_cert_verifier: Arc<dyn ServerCertVerifier> = Arc::new(DummyServerCertVerifier);
297        let resolves_client_cert: Arc<dyn ResolvesClientCert> = Arc::new(DummyResolvesClientCert);
298
299        c.set_kx_hint(name.clone(), NamedGroup::X25519);
300        assert_eq!(None, c.kx_hint(&name));
301
302        #[cfg(feature = "tls12")]
303        {
304            use crate::msgs::persist::Tls12ClientSessionValue;
305            let SupportedCipherSuite::Tls12(tls12_suite) =
306                cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
307            else {
308                unreachable!()
309            };
310
311            c.set_tls12_session(
312                name.clone(),
313                Tls12ClientSessionValue::new(
314                    tls12_suite,
315                    SessionId::empty(),
316                    Arc::new(PayloadU16::empty()),
317                    &[],
318                    CertificateChain::default(),
319                    &server_cert_verifier,
320                    &resolves_client_cert,
321                    now,
322                    0,
323                    true,
324                ),
325            );
326            assert!(c.tls12_session(&name).is_none());
327            c.remove_tls12_session(&name);
328        }
329
330        let SupportedCipherSuite::Tls13(tls13_suite) = cipher_suite::TLS13_AES_256_GCM_SHA384
331        else {
332            unreachable!();
333        };
334        c.insert_tls13_ticket(
335            name.clone(),
336            Tls13ClientSessionValue::new(
337                tls13_suite,
338                Arc::new(PayloadU16::empty()),
339                &[],
340                CertificateChain::default(),
341                &server_cert_verifier,
342                &resolves_client_cert,
343                now,
344                0,
345                0,
346                0,
347            ),
348        );
349        assert!(c.take_tls13_ticket(&name).is_none());
350    }
351
352    #[test]
353    fn debug_of_session_types() {
354        let server_cert_verifier: Arc<dyn ServerCertVerifier> = Arc::new(DummyServerCertVerifier);
355        let resolves_client_cert: Arc<dyn ResolvesClientCert> = Arc::new(DummyResolvesClientCert);
356
357        #[cfg(feature = "tls12")]
358        {
359            use crate::msgs::persist::Tls12ClientSessionValue;
360            let SupportedCipherSuite::Tls12(tls12_suite) =
361                cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
362            else {
363                unreachable!()
364            };
365
366            let tls12 = Tls12ClientSessionValue::new(
367                tls12_suite,
368                SessionId::empty(),
369                Arc::new(PayloadU16::new(vec![5])),
370                &[0xa5; 48],
371                CertificateChain::default(),
372                &server_cert_verifier,
373                &resolves_client_cert,
374                UnixTime::since_unix_epoch(Duration::from_secs(1)),
375                2,
376                true,
377            );
378            let debug = format!("{tls12:?}");
379            assert!(
380                debug.contains("ClientSessionCommon { ticket: 05, epoch: 1, lifetime_secs: 2,")
381            );
382            assert!(!debug.contains("a5a5"), "{debug}");
383        }
384
385        let SupportedCipherSuite::Tls13(tls13_suite) = cipher_suite::TLS13_AES_256_GCM_SHA384
386        else {
387            unreachable!();
388        };
389        let tls13 = Tls13ClientSessionValue::new(
390            tls13_suite,
391            Arc::new(PayloadU16::new(vec![5])),
392            &[0xa5; 32],
393            CertificateChain::default(),
394            &server_cert_verifier,
395            &resolves_client_cert,
396            UnixTime::since_unix_epoch(Duration::from_secs(1)),
397            2,
398            3,
399            0,
400        );
401        let debug = format!("{tls13:?}");
402        assert!(debug.contains("ClientSessionCommon { ticket: 05, epoch: 1, lifetime_secs: 2,"));
403        assert!(!debug.contains("a5a5"), "{debug}");
404    }
405
406    /// For each `size` from 1 to 8, the cache keeps one server's key
407    /// exchange hint and TLS 1.3 ticket.
408    #[cfg(feature = "std")]
409    #[test]
410    fn test_clientsessionmemorycache_small_sizes_retain_server() {
411        for size in 1..=8 {
412            let c = ClientSessionMemoryCache::new(size);
413            let name = ServerName::try_from("example.com").unwrap();
414
415            c.set_kx_hint(name.clone(), NamedGroup::X25519);
416            assert_eq!(c.kx_hint(&name), Some(NamedGroup::X25519), "size {size}");
417
418            c.insert_tls13_ticket(name.clone(), tls13_session(UnixTime::now()));
419            assert!(c.take_tls13_ticket(&name).is_some(), "size {size}");
420        }
421    }
422
423    /// A cache of size 9 remembers two servers: after three servers are
424    /// added, the first is evicted and the other two remain.
425    #[cfg(feature = "std")]
426    #[test]
427    fn test_clientsessionmemorycache_evicts_oldest_server() {
428        let c = ClientSessionMemoryCache::new(9);
429        let first = ServerName::try_from("first.example.com").unwrap();
430        let second = ServerName::try_from("second.example.com").unwrap();
431        let third = ServerName::try_from("third.example.com").unwrap();
432
433        c.set_kx_hint(first.clone(), NamedGroup::X25519);
434        c.set_kx_hint(second.clone(), NamedGroup::X25519);
435        c.set_kx_hint(third.clone(), NamedGroup::X25519);
436
437        assert_eq!(c.kx_hint(&first), None);
438        assert_eq!(c.kx_hint(&second), Some(NamedGroup::X25519));
439        assert_eq!(c.kx_hint(&third), Some(NamedGroup::X25519));
440    }
441
442    /// Test helper: builds a placeholder TLS 1.3 ticket. Its contents are
443    /// empty or dummy values; the tests only check whether the cache
444    /// keeps it.
445    #[cfg(feature = "std")]
446    fn tls13_session(now: UnixTime) -> Tls13ClientSessionValue {
447        let SupportedCipherSuite::Tls13(tls13_suite) = cipher_suite::TLS13_AES_256_GCM_SHA384
448        else {
449            unreachable!();
450        };
451        let server_cert_verifier: Arc<dyn ServerCertVerifier> = Arc::new(DummyServerCertVerifier);
452        let resolves_client_cert: Arc<dyn ResolvesClientCert> = Arc::new(DummyResolvesClientCert);
453        Tls13ClientSessionValue::new(
454            tls13_suite,
455            Arc::new(PayloadU16::empty()),
456            &[],
457            CertificateChain::default(),
458            &server_cert_verifier,
459            &resolves_client_cert,
460            now,
461            0,
462            0,
463            0,
464        )
465    }
466
467    #[derive(Debug)]
468    struct DummyServerCertVerifier;
469
470    impl ServerCertVerifier for DummyServerCertVerifier {
471        #[cfg_attr(coverage_nightly, coverage(off))]
472        fn verify_server_cert(
473            &self,
474            _end_entity: &CertificateDer<'_>,
475            _intermediates: &[CertificateDer<'_>],
476            _server_name: &ServerName<'_>,
477            _ocsp_response: &[u8],
478            _now: UnixTime,
479        ) -> Result<ServerCertVerified, Error> {
480            unreachable!()
481        }
482
483        #[cfg_attr(coverage_nightly, coverage(off))]
484        fn verify_tls12_signature(
485            &self,
486            _message: &[u8],
487            _cert: &CertificateDer<'_>,
488            _dss: &DigitallySignedStruct,
489        ) -> Result<HandshakeSignatureValid, Error> {
490            unreachable!()
491        }
492
493        #[cfg_attr(coverage_nightly, coverage(off))]
494        fn verify_tls13_signature(
495            &self,
496            _message: &[u8],
497            _cert: &CertificateDer<'_>,
498            _dss: &DigitallySignedStruct,
499        ) -> Result<HandshakeSignatureValid, Error> {
500            unreachable!()
501        }
502
503        #[cfg_attr(coverage_nightly, coverage(off))]
504        fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
505            unreachable!()
506        }
507    }
508
509    #[derive(Debug)]
510    struct DummyResolvesClientCert;
511
512    impl ResolvesClientCert for DummyResolvesClientCert {
513        #[cfg_attr(coverage_nightly, coverage(off))]
514        fn resolve(
515            &self,
516            _root_hint_subjects: &[&[u8]],
517            _sigschemes: &[SignatureScheme],
518        ) -> Option<Arc<sign::CertifiedKey>> {
519            unreachable!()
520        }
521
522        #[cfg_attr(coverage_nightly, coverage(off))]
523        fn has_certs(&self) -> bool {
524            unreachable!()
525        }
526    }
527}