Coverage Report

Created: 2026-10-28 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rustls-0.23.46/src/msgs/persist.rs
Line
Count
Source
1
use alloc::vec::Vec;
2
use core::{cmp, fmt};
3
4
use pki_types::{DnsName, UnixTime};
5
use zeroize::Zeroizing;
6
7
use crate::client::ResolvesClientCert;
8
use crate::enums::{CipherSuite, ProtocolVersion};
9
use crate::error::InvalidMessage;
10
use crate::msgs::base::{MaybeEmpty, PayloadU8, PayloadU16};
11
use crate::msgs::codec::{Codec, Reader};
12
#[cfg(feature = "tls12")]
13
use crate::msgs::handshake::SessionId;
14
use crate::msgs::handshake::{CertificateChain, ProtocolName};
15
use crate::sync::{Arc, Weak};
16
#[cfg(feature = "tls12")]
17
use crate::tls12::Tls12CipherSuite;
18
use crate::tls13::Tls13CipherSuite;
19
use crate::verify::ServerCertVerifier;
20
21
pub(crate) struct Retrieved<T> {
22
    pub(crate) value: T,
23
    retrieved_at: UnixTime,
24
}
25
26
impl<T> Retrieved<T> {
27
0
    pub(crate) fn new(value: T, retrieved_at: UnixTime) -> Self {
28
0
        Self {
29
0
            value,
30
0
            retrieved_at,
31
0
        }
32
0
    }
33
34
0
    pub(crate) fn map<M>(&self, f: impl FnOnce(&T) -> Option<&M>) -> Option<Retrieved<&M>> {
35
        Some(Retrieved {
36
0
            value: f(&self.value)?,
37
0
            retrieved_at: self.retrieved_at,
38
        })
39
0
    }
40
}
41
42
impl Retrieved<&Tls13ClientSessionValue> {
43
0
    pub(crate) fn obfuscated_ticket_age(&self) -> u32 {
44
0
        let age_secs = self
45
0
            .retrieved_at
46
0
            .as_secs()
47
0
            .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
0
        let age_millis = u32::try_from(age_secs)
50
0
            .unwrap_or(u32::MAX)
51
0
            .saturating_mul(1000);
52
0
        age_millis.wrapping_add(self.value.age_add)
53
0
    }
54
}
55
56
impl<T: core::ops::Deref<Target = ClientSessionCommon>> Retrieved<T> {
57
0
    pub(crate) fn has_expired(&self) -> bool {
58
0
        let common = &*self.value;
59
0
        common.lifetime_secs != 0
60
0
            && common
61
0
                .epoch
62
0
                .saturating_add(u64::from(common.lifetime_secs))
63
0
                < self.retrieved_at.as_secs()
64
0
    }
65
}
66
67
impl<T> core::ops::Deref for Retrieved<T> {
68
    type Target = T;
69
70
0
    fn deref(&self) -> &Self::Target {
71
0
        &self.value
72
0
    }
Unexecuted instantiation: <rustls::msgs::persist::Retrieved<rustls::client::hs::ClientSessionValue> as core::ops::deref::Deref>::deref
Unexecuted instantiation: <rustls::msgs::persist::Retrieved<&rustls::msgs::persist::Tls13ClientSessionValue> as core::ops::deref::Deref>::deref
73
}
74
75
pub 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
83
impl Tls13ClientSessionValue {
84
0
    pub(crate) fn new(
85
0
        suite: &'static Tls13CipherSuite,
86
0
        ticket: Arc<PayloadU16>,
87
0
        secret: &[u8],
88
0
        server_cert_chain: CertificateChain<'static>,
89
0
        server_cert_verifier: &Arc<dyn ServerCertVerifier>,
90
0
        client_creds: &Arc<dyn ResolvesClientCert>,
91
0
        time_now: UnixTime,
92
0
        lifetime_secs: u32,
93
0
        age_add: u32,
94
0
        max_early_data_size: u32,
95
0
    ) -> Self {
96
0
        Self {
97
0
            suite,
98
0
            age_add,
99
0
            max_early_data_size,
100
0
            common: ClientSessionCommon::new(
101
0
                ticket,
102
0
                secret,
103
0
                time_now,
104
0
                lifetime_secs,
105
0
                server_cert_chain,
106
0
                server_cert_verifier,
107
0
                client_creds,
108
0
            ),
109
0
            quic_params: PayloadU16::new(Vec::new()),
110
0
        }
111
0
    }
112
113
0
    pub fn max_early_data_size(&self) -> u32 {
114
0
        self.max_early_data_size
115
0
    }
116
117
0
    pub fn suite(&self) -> &'static Tls13CipherSuite {
118
0
        self.suite
119
0
    }
120
121
    #[doc(hidden)]
122
    /// Test only: rewind epoch by `delta` seconds.
123
0
    pub fn rewind_epoch(&mut self, delta: u32) {
124
0
        self.common.epoch -= delta as u64;
125
0
    }
126
127
    #[doc(hidden)]
128
    /// Test only: replace `max_early_data_size` with `new`
129
0
    pub fn _private_set_max_early_data_size(&mut self, new: u32) {
130
0
        self.max_early_data_size = new;
131
0
    }
132
133
0
    pub fn set_quic_params(&mut self, quic_params: &[u8]) {
134
0
        self.quic_params = PayloadU16::new(quic_params.to_vec());
135
0
    }
136
137
0
    pub fn quic_params(&self) -> Vec<u8> {
138
0
        self.quic_params.0.clone()
139
0
    }
140
}
141
142
impl core::ops::Deref for Tls13ClientSessionValue {
143
    type Target = ClientSessionCommon;
144
145
0
    fn deref(&self) -> &Self::Target {
146
0
        &self.common
147
0
    }
148
}
149
150
impl fmt::Debug for Tls13ClientSessionValue {
151
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152
        let Self {
153
0
            suite,
154
0
            age_add,
155
0
            max_early_data_size,
156
0
            common,
157
0
            quic_params,
158
0
        } = self;
159
160
0
        f.debug_struct("Tls13ClientSessionValue")
161
0
            .field("suite", suite)
162
0
            .field("age_add", age_add)
163
0
            .field("max_early_data_size", max_early_data_size)
164
0
            .field("common", common)
165
0
            .field("quic_params", quic_params)
166
0
            .finish()
167
0
    }
168
}
169
170
#[derive(Debug, Clone)]
171
pub 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")]
184
impl Tls12ClientSessionValue {
185
0
    pub(crate) fn new(
186
0
        suite: &'static Tls12CipherSuite,
187
0
        session_id: SessionId,
188
0
        ticket: Arc<PayloadU16>,
189
0
        master_secret: &[u8],
190
0
        server_cert_chain: CertificateChain<'static>,
191
0
        server_cert_verifier: &Arc<dyn ServerCertVerifier>,
192
0
        client_creds: &Arc<dyn ResolvesClientCert>,
193
0
        time_now: UnixTime,
194
0
        lifetime_secs: u32,
195
0
        extended_ms: bool,
196
0
    ) -> Self {
197
0
        Self {
198
0
            suite,
199
0
            session_id,
200
0
            extended_ms,
201
0
            common: ClientSessionCommon::new(
202
0
                ticket,
203
0
                master_secret,
204
0
                time_now,
205
0
                lifetime_secs,
206
0
                server_cert_chain,
207
0
                server_cert_verifier,
208
0
                client_creds,
209
0
            ),
210
0
        }
211
0
    }
212
213
0
    pub(crate) fn ticket(&mut self) -> Arc<PayloadU16> {
214
0
        self.common.ticket.clone()
215
0
    }
216
217
0
    pub(crate) fn extended_ms(&self) -> bool {
218
0
        self.extended_ms
219
0
    }
220
221
0
    pub(crate) fn suite(&self) -> &'static Tls12CipherSuite {
222
0
        self.suite
223
0
    }
224
225
    #[doc(hidden)]
226
    /// Test only: rewind epoch by `delta` seconds.
227
0
    pub fn rewind_epoch(&mut self, delta: u32) {
228
0
        self.common.epoch -= delta as u64;
229
0
    }
230
}
231
232
#[cfg(feature = "tls12")]
233
impl core::ops::Deref for Tls12ClientSessionValue {
234
    type Target = ClientSessionCommon;
235
236
0
    fn deref(&self) -> &Self::Target {
237
0
        &self.common
238
0
    }
239
}
240
241
#[derive(Clone)]
242
pub 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
252
impl ClientSessionCommon {
253
0
    fn new(
254
0
        ticket: Arc<PayloadU16>,
255
0
        secret: &[u8],
256
0
        time_now: UnixTime,
257
0
        lifetime_secs: u32,
258
0
        server_cert_chain: CertificateChain<'static>,
259
0
        server_cert_verifier: &Arc<dyn ServerCertVerifier>,
260
0
        client_creds: &Arc<dyn ResolvesClientCert>,
261
0
    ) -> Self {
262
0
        Self {
263
0
            ticket,
264
0
            secret: Zeroizing::new(PayloadU8::new(secret.to_vec())),
265
0
            epoch: time_now.as_secs(),
266
0
            lifetime_secs: cmp::min(lifetime_secs, MAX_TICKET_LIFETIME),
267
0
            server_cert_chain: Arc::new(server_cert_chain),
268
0
            server_cert_verifier: Arc::downgrade(server_cert_verifier),
269
0
            client_creds: Arc::downgrade(client_creds),
270
0
        }
271
0
    }
272
273
0
    pub(crate) fn compatible_config(
274
0
        &self,
275
0
        server_cert_verifier: &Arc<dyn ServerCertVerifier>,
276
0
        client_creds: &Arc<dyn ResolvesClientCert>,
277
0
    ) -> bool {
278
0
        let same_verifier = Weak::ptr_eq(
279
0
            &Arc::downgrade(server_cert_verifier),
280
0
            &self.server_cert_verifier,
281
        );
282
0
        let same_creds = Weak::ptr_eq(&Arc::downgrade(client_creds), &self.client_creds);
283
284
0
        match (same_verifier, same_creds) {
285
0
            (true, true) => true,
286
            (false, _) => {
287
0
                crate::log::trace!("resumption not allowed between different ServerCertVerifiers");
288
0
                false
289
            }
290
            (_, _) => {
291
0
                crate::log::trace!(
292
0
                    "resumption not allowed between different ResolvesClientCert values"
293
                );
294
0
                false
295
            }
296
        }
297
0
    }
298
299
0
    pub(crate) fn server_cert_chain(&self) -> &CertificateChain<'static> {
300
0
        &self.server_cert_chain
301
0
    }
302
303
0
    pub(crate) fn secret(&self) -> &[u8] {
304
0
        self.secret.0.as_ref()
305
0
    }
306
307
0
    pub(crate) fn ticket(&self) -> &[u8] {
308
0
        self.ticket.0.as_ref()
309
0
    }
310
}
311
312
impl fmt::Debug for ClientSessionCommon {
313
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314
        let Self {
315
0
            ticket,
316
            secret: _,
317
0
            epoch,
318
0
            lifetime_secs,
319
0
            server_cert_chain,
320
0
            server_cert_verifier,
321
0
            client_creds,
322
0
        } = self;
323
0
        f.debug_struct("ClientSessionCommon")
324
0
            .field("ticket", ticket)
325
0
            .field("epoch", epoch)
326
0
            .field("lifetime_secs", lifetime_secs)
327
0
            .field("server_cert_chain", server_cert_chain)
328
0
            .field("server_cert_verifier", server_cert_verifier)
329
0
            .field("client_creds", client_creds)
330
0
            .finish_non_exhaustive()
331
0
    }
332
}
333
334
static 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.
340
static MAX_FRESHNESS_SKEW_MS: u32 = 60 * 1000;
341
342
// --- Server types ---
343
pub 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
357
impl fmt::Debug for ServerSessionValue {
358
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
359
        let Self {
360
0
            sni,
361
0
            version,
362
0
            cipher_suite,
363
            master_secret: _,
364
0
            extended_ms,
365
0
            client_cert_chain,
366
0
            alpn,
367
0
            application_data,
368
0
            creation_time_sec,
369
0
            age_obfuscation_offset,
370
0
            freshness,
371
0
        } = self;
372
0
        f.debug_struct("ServerSessionValue")
373
0
            .field("sni", sni)
374
0
            .field("version", version)
375
0
            .field("cipher_suite", cipher_suite)
376
0
            .field("extended_ms", extended_ms)
377
0
            .field("client_cert_chain", client_cert_chain)
378
0
            .field("alpn", alpn)
379
0
            .field("application_data", application_data)
380
0
            .field("creation_time_sec", creation_time_sec)
381
0
            .field("age_obfuscation_offset", age_obfuscation_offset)
382
0
            .field("freshness", freshness)
383
0
            .finish_non_exhaustive()
384
0
    }
385
}
386
387
impl Codec<'_> for ServerSessionValue {
388
0
    fn encode(&self, bytes: &mut Vec<u8>) {
389
0
        if let Some(sni) = &self.sni {
390
0
            1u8.encode(bytes);
391
0
            let sni_bytes: &str = sni.as_ref();
392
0
            PayloadU8::<MaybeEmpty>::encode_slice(sni_bytes.as_bytes(), bytes);
393
0
        } else {
394
0
            0u8.encode(bytes);
395
0
        }
396
0
        self.version.encode(bytes);
397
0
        self.cipher_suite.encode(bytes);
398
0
        self.master_secret.encode(bytes);
399
0
        (u8::from(self.extended_ms)).encode(bytes);
400
0
        if let Some(chain) = &self.client_cert_chain {
401
0
            1u8.encode(bytes);
402
0
            chain.encode(bytes);
403
0
        } else {
404
0
            0u8.encode(bytes);
405
0
        }
406
0
        if let Some(alpn) = &self.alpn {
407
0
            1u8.encode(bytes);
408
0
            alpn.encode(bytes);
409
0
        } else {
410
0
            0u8.encode(bytes);
411
0
        }
412
0
        self.application_data.encode(bytes);
413
0
        self.creation_time_sec.encode(bytes);
414
0
        self.age_obfuscation_offset
415
0
            .encode(bytes);
416
0
    }
417
418
0
    fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> {
419
0
        let has_sni = u8::read(r)?;
420
0
        let sni = if has_sni == 1 {
421
0
            let dns_name = PayloadU8::<MaybeEmpty>::read(r)?;
422
0
            let dns_name = match DnsName::try_from(dns_name.0.as_slice()) {
423
0
                Ok(dns_name) => dns_name.to_owned(),
424
0
                Err(_) => return Err(InvalidMessage::InvalidServerName),
425
            };
426
427
0
            Some(dns_name)
428
        } else {
429
0
            None
430
        };
431
432
0
        let v = ProtocolVersion::read(r)?;
433
0
        let cs = CipherSuite::read(r)?;
434
0
        let ms = Zeroizing::new(PayloadU8::read(r)?);
435
0
        let ems = u8::read(r)?;
436
0
        let has_ccert = u8::read(r)? == 1;
437
0
        let ccert = if has_ccert {
438
0
            Some(CertificateChain::read(r)?.into_owned())
439
        } else {
440
0
            None
441
        };
442
0
        let has_alpn = u8::read(r)? == 1;
443
0
        let alpn = if has_alpn {
444
0
            Some(PayloadU8::read(r)?)
445
        } else {
446
0
            None
447
        };
448
0
        let application_data = PayloadU16::read(r)?;
449
0
        let creation_time_sec = u64::read(r)?;
450
0
        let age_obfuscation_offset = u32::read(r)?;
451
452
0
        Ok(Self {
453
0
            sni,
454
0
            version: v,
455
0
            cipher_suite: cs,
456
0
            master_secret: ms,
457
0
            extended_ms: ems == 1u8,
458
0
            client_cert_chain: ccert,
459
0
            alpn,
460
0
            application_data,
461
0
            creation_time_sec,
462
0
            age_obfuscation_offset,
463
0
            freshness: None,
464
0
        })
465
0
    }
466
}
467
468
impl ServerSessionValue {
469
0
    pub(crate) fn new(
470
0
        sni: Option<&DnsName<'_>>,
471
0
        v: ProtocolVersion,
472
0
        cs: CipherSuite,
473
0
        ms: &[u8],
474
0
        client_cert_chain: Option<CertificateChain<'static>>,
475
0
        alpn: Option<ProtocolName>,
476
0
        application_data: Vec<u8>,
477
0
        creation_time: UnixTime,
478
0
        age_obfuscation_offset: u32,
479
0
    ) -> Self {
480
        Self {
481
0
            sni: sni.map(|dns| dns.to_owned()),
482
0
            version: v,
483
0
            cipher_suite: cs,
484
0
            master_secret: Zeroizing::new(PayloadU8::new(ms.to_vec())),
485
            extended_ms: false,
486
0
            client_cert_chain,
487
0
            alpn: alpn.map(|p| PayloadU8::new(p.as_ref().to_vec())),
488
0
            application_data: PayloadU16::new(application_data),
489
0
            creation_time_sec: creation_time.as_secs(),
490
0
            age_obfuscation_offset,
491
0
            freshness: None,
492
        }
493
0
    }
494
495
    #[cfg(feature = "tls12")]
496
0
    pub(crate) fn set_extended_ms_used(&mut self) {
497
0
        self.extended_ms = true;
498
0
    }
499
500
0
    pub(crate) fn set_freshness(
501
0
        mut self,
502
0
        obfuscated_client_age_ms: u32,
503
0
        time_now: UnixTime,
504
0
    ) -> Self {
505
0
        let client_age_ms = obfuscated_client_age_ms.wrapping_sub(self.age_obfuscation_offset);
506
0
        let server_age_ms = (time_now
507
0
            .as_secs()
508
0
            .saturating_sub(self.creation_time_sec) as u32)
509
0
            .saturating_mul(1000);
510
511
0
        let age_difference = server_age_ms.abs_diff(client_age_ms);
512
513
0
        self.freshness = Some(age_difference <= MAX_FRESHNESS_SKEW_MS);
514
0
        self
515
0
    }
516
517
0
    pub(crate) fn is_fresh(&self) -> bool {
518
0
        self.freshness.unwrap_or_default()
519
0
    }
520
}
521
522
#[cfg(test)]
523
mod 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
}