Skip to main content

rustls/msgs/
persist.rs

1use alloc::vec::Vec;
2use core::{cmp, fmt};
3
4use pki_types::{DnsName, UnixTime};
5use zeroize::Zeroizing;
6
7use crate::client::ResolvesClientCert;
8use crate::enums::{CipherSuite, ProtocolVersion};
9use crate::error::InvalidMessage;
10use crate::msgs::base::{MaybeEmpty, PayloadU8, PayloadU16};
11use crate::msgs::codec::{Codec, Reader};
12#[cfg(feature = "tls12")]
13use crate::msgs::handshake::SessionId;
14use crate::msgs::handshake::{CertificateChain, ProtocolName};
15use crate::sync::{Arc, Weak};
16#[cfg(feature = "tls12")]
17use crate::tls12::Tls12CipherSuite;
18use crate::tls13::Tls13CipherSuite;
19use crate::verify::ServerCertVerifier;
20
21pub(crate) struct Retrieved<T> {
22    pub(crate) value: T,
23    retrieved_at: UnixTime,
24}
25
26impl<T> Retrieved<T> {
27    pub(crate) fn new(value: T, retrieved_at: UnixTime) -> Self {
28        Self {
29            value,
30            retrieved_at,
31        }
32    }
33
34    pub(crate) fn map<M>(&self, f: impl FnOnce(&T) -> Option<&M>) -> Option<Retrieved<&M>> {
35        Some(Retrieved {
36            value: f(&self.value)?,
37            retrieved_at: self.retrieved_at,
38        })
39    }
40}
41
42impl Retrieved<&Tls13ClientSessionValue> {
43    pub(crate) fn obfuscated_ticket_age(&self) -> u32 {
44        let age_secs = self
45            .retrieved_at
46            .as_secs()
47            .saturating_sub(self.value.common.epoch);
48        // nb. tickets have an upper age limit of ~7 days, well short of the 49 days here
49        let age_millis = u32::try_from(age_secs)
50            .unwrap_or(u32::MAX)
51            .saturating_mul(1000);
52        age_millis.wrapping_add(self.value.age_add)
53    }
54}
55
56impl<T: core::ops::Deref<Target = ClientSessionCommon>> Retrieved<T> {
57    pub(crate) fn has_expired(&self) -> bool {
58        let common = &*self.value;
59        common.lifetime_secs != 0
60            && common
61                .epoch
62                .saturating_add(u64::from(common.lifetime_secs))
63                < self.retrieved_at.as_secs()
64    }
65}
66
67impl<T> core::ops::Deref for Retrieved<T> {
68    type Target = T;
69
70    fn deref(&self) -> &Self::Target {
71        &self.value
72    }
73}
74
75pub struct Tls13ClientSessionValue {
76    suite: &'static Tls13CipherSuite,
77    age_add: u32,
78    max_early_data_size: u32,
79    pub(crate) common: ClientSessionCommon,
80    quic_params: PayloadU16,
81}
82
83impl Tls13ClientSessionValue {
84    pub(crate) fn new(
85        suite: &'static Tls13CipherSuite,
86        ticket: Arc<PayloadU16>,
87        secret: &[u8],
88        server_cert_chain: CertificateChain<'static>,
89        server_cert_verifier: &Arc<dyn ServerCertVerifier>,
90        client_creds: &Arc<dyn ResolvesClientCert>,
91        time_now: UnixTime,
92        lifetime_secs: u32,
93        age_add: u32,
94        max_early_data_size: u32,
95    ) -> Self {
96        Self {
97            suite,
98            age_add,
99            max_early_data_size,
100            common: ClientSessionCommon::new(
101                ticket,
102                secret,
103                time_now,
104                lifetime_secs,
105                server_cert_chain,
106                server_cert_verifier,
107                client_creds,
108            ),
109            quic_params: PayloadU16::new(Vec::new()),
110        }
111    }
112
113    pub fn max_early_data_size(&self) -> u32 {
114        self.max_early_data_size
115    }
116
117    pub fn suite(&self) -> &'static Tls13CipherSuite {
118        self.suite
119    }
120
121    #[doc(hidden)]
122    /// Test only: rewind epoch by `delta` seconds.
123    pub fn rewind_epoch(&mut self, delta: u32) {
124        self.common.epoch -= delta as u64;
125    }
126
127    #[doc(hidden)]
128    /// Test only: replace `max_early_data_size` with `new`
129    pub fn _private_set_max_early_data_size(&mut self, new: u32) {
130        self.max_early_data_size = new;
131    }
132
133    pub fn set_quic_params(&mut self, quic_params: &[u8]) {
134        self.quic_params = PayloadU16::new(quic_params.to_vec());
135    }
136
137    pub fn quic_params(&self) -> Vec<u8> {
138        self.quic_params.0.clone()
139    }
140}
141
142impl core::ops::Deref for Tls13ClientSessionValue {
143    type Target = ClientSessionCommon;
144
145    fn deref(&self) -> &Self::Target {
146        &self.common
147    }
148}
149
150impl fmt::Debug for Tls13ClientSessionValue {
151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152        let Self {
153            suite,
154            age_add,
155            max_early_data_size,
156            common,
157            quic_params,
158        } = self;
159
160        f.debug_struct("Tls13ClientSessionValue")
161            .field("suite", suite)
162            .field("age_add", age_add)
163            .field("max_early_data_size", max_early_data_size)
164            .field("common", common)
165            .field("quic_params", quic_params)
166            .finish()
167    }
168}
169
170#[derive(Debug, Clone)]
171pub struct Tls12ClientSessionValue {
172    #[cfg(feature = "tls12")]
173    suite: &'static Tls12CipherSuite,
174    #[cfg(feature = "tls12")]
175    pub(crate) session_id: SessionId,
176    #[cfg(feature = "tls12")]
177    extended_ms: bool,
178    #[doc(hidden)]
179    #[cfg(feature = "tls12")]
180    pub(crate) common: ClientSessionCommon,
181}
182
183#[cfg(feature = "tls12")]
184impl Tls12ClientSessionValue {
185    pub(crate) fn new(
186        suite: &'static Tls12CipherSuite,
187        session_id: SessionId,
188        ticket: Arc<PayloadU16>,
189        master_secret: &[u8],
190        server_cert_chain: CertificateChain<'static>,
191        server_cert_verifier: &Arc<dyn ServerCertVerifier>,
192        client_creds: &Arc<dyn ResolvesClientCert>,
193        time_now: UnixTime,
194        lifetime_secs: u32,
195        extended_ms: bool,
196    ) -> Self {
197        Self {
198            suite,
199            session_id,
200            extended_ms,
201            common: ClientSessionCommon::new(
202                ticket,
203                master_secret,
204                time_now,
205                lifetime_secs,
206                server_cert_chain,
207                server_cert_verifier,
208                client_creds,
209            ),
210        }
211    }
212
213    pub(crate) fn ticket(&mut self) -> Arc<PayloadU16> {
214        self.common.ticket.clone()
215    }
216
217    pub(crate) fn extended_ms(&self) -> bool {
218        self.extended_ms
219    }
220
221    pub(crate) fn suite(&self) -> &'static Tls12CipherSuite {
222        self.suite
223    }
224
225    #[doc(hidden)]
226    /// Test only: rewind epoch by `delta` seconds.
227    pub fn rewind_epoch(&mut self, delta: u32) {
228        self.common.epoch -= delta as u64;
229    }
230}
231
232#[cfg(feature = "tls12")]
233impl core::ops::Deref for Tls12ClientSessionValue {
234    type Target = ClientSessionCommon;
235
236    fn deref(&self) -> &Self::Target {
237        &self.common
238    }
239}
240
241#[derive(Clone)]
242pub struct ClientSessionCommon {
243    ticket: Arc<PayloadU16>,
244    secret: Zeroizing<PayloadU8>,
245    epoch: u64,
246    lifetime_secs: u32,
247    server_cert_chain: Arc<CertificateChain<'static>>,
248    server_cert_verifier: Weak<dyn ServerCertVerifier>,
249    client_creds: Weak<dyn ResolvesClientCert>,
250}
251
252impl ClientSessionCommon {
253    fn new(
254        ticket: Arc<PayloadU16>,
255        secret: &[u8],
256        time_now: UnixTime,
257        lifetime_secs: u32,
258        server_cert_chain: CertificateChain<'static>,
259        server_cert_verifier: &Arc<dyn ServerCertVerifier>,
260        client_creds: &Arc<dyn ResolvesClientCert>,
261    ) -> Self {
262        Self {
263            ticket,
264            secret: Zeroizing::new(PayloadU8::new(secret.to_vec())),
265            epoch: time_now.as_secs(),
266            lifetime_secs: cmp::min(lifetime_secs, MAX_TICKET_LIFETIME),
267            server_cert_chain: Arc::new(server_cert_chain),
268            server_cert_verifier: Arc::downgrade(server_cert_verifier),
269            client_creds: Arc::downgrade(client_creds),
270        }
271    }
272
273    pub(crate) fn compatible_config(
274        &self,
275        server_cert_verifier: &Arc<dyn ServerCertVerifier>,
276        client_creds: &Arc<dyn ResolvesClientCert>,
277    ) -> bool {
278        let same_verifier = Weak::ptr_eq(
279            &Arc::downgrade(server_cert_verifier),
280            &self.server_cert_verifier,
281        );
282        let same_creds = Weak::ptr_eq(&Arc::downgrade(client_creds), &self.client_creds);
283
284        match (same_verifier, same_creds) {
285            (true, true) => true,
286            (false, _) => {
287                crate::log::trace!("resumption not allowed between different ServerCertVerifiers");
288                false
289            }
290            (_, _) => {
291                crate::log::trace!(
292                    "resumption not allowed between different ResolvesClientCert values"
293                );
294                false
295            }
296        }
297    }
298
299    pub(crate) fn server_cert_chain(&self) -> &CertificateChain<'static> {
300        &self.server_cert_chain
301    }
302
303    pub(crate) fn secret(&self) -> &[u8] {
304        self.secret.0.as_ref()
305    }
306
307    pub(crate) fn ticket(&self) -> &[u8] {
308        self.ticket.0.as_ref()
309    }
310}
311
312impl fmt::Debug for ClientSessionCommon {
313    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314        let Self {
315            ticket,
316            secret: _,
317            epoch,
318            lifetime_secs,
319            server_cert_chain,
320            server_cert_verifier,
321            client_creds,
322        } = self;
323        f.debug_struct("ClientSessionCommon")
324            .field("ticket", ticket)
325            .field("epoch", epoch)
326            .field("lifetime_secs", lifetime_secs)
327            .field("server_cert_chain", server_cert_chain)
328            .field("server_cert_verifier", server_cert_verifier)
329            .field("client_creds", client_creds)
330            .finish_non_exhaustive()
331    }
332}
333
334static MAX_TICKET_LIFETIME: u32 = 7 * 24 * 60 * 60;
335
336/// This is the maximum allowed skew between server and client clocks, over
337/// the maximum ticket lifetime period.  This encompasses TCP retransmission
338/// times in case packet loss occurs when the client sends the ClientHello
339/// or receives the NewSessionTicket, _and_ actual clock skew over this period.
340static MAX_FRESHNESS_SKEW_MS: u32 = 60 * 1000;
341
342// --- Server types ---
343pub struct ServerSessionValue {
344    pub(crate) sni: Option<DnsName<'static>>,
345    pub(crate) version: ProtocolVersion,
346    pub(crate) cipher_suite: CipherSuite,
347    pub(crate) master_secret: Zeroizing<PayloadU8>,
348    pub(crate) extended_ms: bool,
349    pub(crate) client_cert_chain: Option<CertificateChain<'static>>,
350    pub(crate) alpn: Option<PayloadU8>,
351    pub(crate) application_data: PayloadU16,
352    pub creation_time_sec: u64,
353    pub(crate) age_obfuscation_offset: u32,
354    freshness: Option<bool>,
355}
356
357impl fmt::Debug for ServerSessionValue {
358    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
359        let Self {
360            sni,
361            version,
362            cipher_suite,
363            master_secret: _,
364            extended_ms,
365            client_cert_chain,
366            alpn,
367            application_data,
368            creation_time_sec,
369            age_obfuscation_offset,
370            freshness,
371        } = self;
372        f.debug_struct("ServerSessionValue")
373            .field("sni", sni)
374            .field("version", version)
375            .field("cipher_suite", cipher_suite)
376            .field("extended_ms", extended_ms)
377            .field("client_cert_chain", client_cert_chain)
378            .field("alpn", alpn)
379            .field("application_data", application_data)
380            .field("creation_time_sec", creation_time_sec)
381            .field("age_obfuscation_offset", age_obfuscation_offset)
382            .field("freshness", freshness)
383            .finish_non_exhaustive()
384    }
385}
386
387impl Codec<'_> for ServerSessionValue {
388    fn encode(&self, bytes: &mut Vec<u8>) {
389        if let Some(sni) = &self.sni {
390            1u8.encode(bytes);
391            let sni_bytes: &str = sni.as_ref();
392            PayloadU8::<MaybeEmpty>::encode_slice(sni_bytes.as_bytes(), bytes);
393        } else {
394            0u8.encode(bytes);
395        }
396        self.version.encode(bytes);
397        self.cipher_suite.encode(bytes);
398        self.master_secret.encode(bytes);
399        (u8::from(self.extended_ms)).encode(bytes);
400        if let Some(chain) = &self.client_cert_chain {
401            1u8.encode(bytes);
402            chain.encode(bytes);
403        } else {
404            0u8.encode(bytes);
405        }
406        if let Some(alpn) = &self.alpn {
407            1u8.encode(bytes);
408            alpn.encode(bytes);
409        } else {
410            0u8.encode(bytes);
411        }
412        self.application_data.encode(bytes);
413        self.creation_time_sec.encode(bytes);
414        self.age_obfuscation_offset
415            .encode(bytes);
416    }
417
418    fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> {
419        let has_sni = u8::read(r)?;
420        let sni = if has_sni == 1 {
421            let dns_name = PayloadU8::<MaybeEmpty>::read(r)?;
422            let dns_name = match DnsName::try_from(dns_name.0.as_slice()) {
423                Ok(dns_name) => dns_name.to_owned(),
424                Err(_) => return Err(InvalidMessage::InvalidServerName),
425            };
426
427            Some(dns_name)
428        } else {
429            None
430        };
431
432        let v = ProtocolVersion::read(r)?;
433        let cs = CipherSuite::read(r)?;
434        let ms = Zeroizing::new(PayloadU8::read(r)?);
435        let ems = u8::read(r)?;
436        let has_ccert = u8::read(r)? == 1;
437        let ccert = if has_ccert {
438            Some(CertificateChain::read(r)?.into_owned())
439        } else {
440            None
441        };
442        let has_alpn = u8::read(r)? == 1;
443        let alpn = if has_alpn {
444            Some(PayloadU8::read(r)?)
445        } else {
446            None
447        };
448        let application_data = PayloadU16::read(r)?;
449        let creation_time_sec = u64::read(r)?;
450        let age_obfuscation_offset = u32::read(r)?;
451
452        Ok(Self {
453            sni,
454            version: v,
455            cipher_suite: cs,
456            master_secret: ms,
457            extended_ms: ems == 1u8,
458            client_cert_chain: ccert,
459            alpn,
460            application_data,
461            creation_time_sec,
462            age_obfuscation_offset,
463            freshness: None,
464        })
465    }
466}
467
468impl ServerSessionValue {
469    pub(crate) fn new(
470        sni: Option<&DnsName<'_>>,
471        v: ProtocolVersion,
472        cs: CipherSuite,
473        ms: &[u8],
474        client_cert_chain: Option<CertificateChain<'static>>,
475        alpn: Option<ProtocolName>,
476        application_data: Vec<u8>,
477        creation_time: UnixTime,
478        age_obfuscation_offset: u32,
479    ) -> Self {
480        Self {
481            sni: sni.map(|dns| dns.to_owned()),
482            version: v,
483            cipher_suite: cs,
484            master_secret: Zeroizing::new(PayloadU8::new(ms.to_vec())),
485            extended_ms: false,
486            client_cert_chain,
487            alpn: alpn.map(|p| PayloadU8::new(p.as_ref().to_vec())),
488            application_data: PayloadU16::new(application_data),
489            creation_time_sec: creation_time.as_secs(),
490            age_obfuscation_offset,
491            freshness: None,
492        }
493    }
494
495    #[cfg(feature = "tls12")]
496    pub(crate) fn set_extended_ms_used(&mut self) {
497        self.extended_ms = true;
498    }
499
500    pub(crate) fn set_freshness(
501        mut self,
502        obfuscated_client_age_ms: u32,
503        time_now: UnixTime,
504    ) -> Self {
505        let client_age_ms = obfuscated_client_age_ms.wrapping_sub(self.age_obfuscation_offset);
506        let server_age_ms = (time_now
507            .as_secs()
508            .saturating_sub(self.creation_time_sec) as u32)
509            .saturating_mul(1000);
510
511        let age_difference = server_age_ms.abs_diff(client_age_ms);
512
513        self.freshness = Some(age_difference <= MAX_FRESHNESS_SKEW_MS);
514        self
515    }
516
517    pub(crate) fn is_fresh(&self) -> bool {
518        self.freshness.unwrap_or_default()
519    }
520}
521
522#[cfg(test)]
523mod tests {
524    use super::*;
525
526    #[cfg(feature = "std")] // for UnixTime::now
527    #[test]
528    fn serversessionvalue_is_debug() {
529        use std::{format, println, vec};
530        let ssv = ServerSessionValue::new(
531            None,
532            ProtocolVersion::TLSv1_3,
533            CipherSuite::TLS13_AES_128_GCM_SHA256,
534            &[0xa5; 48],
535            None,
536            None,
537            vec![4, 5, 6],
538            UnixTime::now(),
539            0x12345678,
540        );
541        let debug = format!("{ssv:?}");
542        println!("{debug}");
543        assert!(debug.contains("application_data: 040506"));
544        assert!(!debug.contains("a5a5"), "{debug}");
545    }
546
547    #[test]
548    fn serversessionvalue_no_sni() {
549        let bytes = [
550            0x00, 0x03, 0x03, 0xc0, 0x23, 0x03, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
551            0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0xfe, 0xed, 0xf0, 0x0d,
552        ];
553        let mut rd = Reader::init(&bytes);
554        let ssv = ServerSessionValue::read(&mut rd).unwrap();
555        assert_eq!(ssv.get_encoding(), bytes);
556    }
557
558    #[test]
559    fn serversessionvalue_with_cert() {
560        let bytes = [
561            0x00, 0x03, 0x03, 0xc0, 0x23, 0x03, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
562            0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0xfe, 0xed, 0xf0, 0x0d,
563        ];
564        let mut rd = Reader::init(&bytes);
565        let ssv = ServerSessionValue::read(&mut rd).unwrap();
566        assert_eq!(ssv.get_encoding(), bytes);
567    }
568}