Coverage Report

Created: 2026-03-17 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rustls-0.23.35/src/msgs/persist.rs
Line
Count
Source
1
use alloc::vec::Vec;
2
use core::cmp;
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
0
        let age_millis = age_secs as u32 * 1000;
49
0
        age_millis.wrapping_add(self.value.age_add)
50
0
    }
51
}
52
53
impl<T: core::ops::Deref<Target = ClientSessionCommon>> Retrieved<T> {
54
0
    pub(crate) fn has_expired(&self) -> bool {
55
0
        let common = &*self.value;
56
0
        common.lifetime_secs != 0
57
0
            && common
58
0
                .epoch
59
0
                .saturating_add(u64::from(common.lifetime_secs))
60
0
                < self.retrieved_at.as_secs()
61
0
    }
62
}
63
64
impl<T> core::ops::Deref for Retrieved<T> {
65
    type Target = T;
66
67
0
    fn deref(&self) -> &Self::Target {
68
0
        &self.value
69
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
70
}
71
72
#[derive(Debug)]
73
pub struct Tls13ClientSessionValue {
74
    suite: &'static Tls13CipherSuite,
75
    age_add: u32,
76
    max_early_data_size: u32,
77
    pub(crate) common: ClientSessionCommon,
78
    quic_params: PayloadU16,
79
}
80
81
impl Tls13ClientSessionValue {
82
0
    pub(crate) fn new(
83
0
        suite: &'static Tls13CipherSuite,
84
0
        ticket: Arc<PayloadU16>,
85
0
        secret: &[u8],
86
0
        server_cert_chain: CertificateChain<'static>,
87
0
        server_cert_verifier: &Arc<dyn ServerCertVerifier>,
88
0
        client_creds: &Arc<dyn ResolvesClientCert>,
89
0
        time_now: UnixTime,
90
0
        lifetime_secs: u32,
91
0
        age_add: u32,
92
0
        max_early_data_size: u32,
93
0
    ) -> Self {
94
0
        Self {
95
0
            suite,
96
0
            age_add,
97
0
            max_early_data_size,
98
0
            common: ClientSessionCommon::new(
99
0
                ticket,
100
0
                secret,
101
0
                time_now,
102
0
                lifetime_secs,
103
0
                server_cert_chain,
104
0
                server_cert_verifier,
105
0
                client_creds,
106
0
            ),
107
0
            quic_params: PayloadU16::new(Vec::new()),
108
0
        }
109
0
    }
110
111
0
    pub fn max_early_data_size(&self) -> u32 {
112
0
        self.max_early_data_size
113
0
    }
114
115
0
    pub fn suite(&self) -> &'static Tls13CipherSuite {
116
0
        self.suite
117
0
    }
118
119
    #[doc(hidden)]
120
    /// Test only: rewind epoch by `delta` seconds.
121
0
    pub fn rewind_epoch(&mut self, delta: u32) {
122
0
        self.common.epoch -= delta as u64;
123
0
    }
124
125
    #[doc(hidden)]
126
    /// Test only: replace `max_early_data_size` with `new`
127
0
    pub fn _private_set_max_early_data_size(&mut self, new: u32) {
128
0
        self.max_early_data_size = new;
129
0
    }
130
131
0
    pub fn set_quic_params(&mut self, quic_params: &[u8]) {
132
0
        self.quic_params = PayloadU16::new(quic_params.to_vec());
133
0
    }
134
135
0
    pub fn quic_params(&self) -> Vec<u8> {
136
0
        self.quic_params.0.clone()
137
0
    }
138
}
139
140
impl core::ops::Deref for Tls13ClientSessionValue {
141
    type Target = ClientSessionCommon;
142
143
0
    fn deref(&self) -> &Self::Target {
144
0
        &self.common
145
0
    }
146
}
147
148
#[derive(Debug, Clone)]
149
pub struct Tls12ClientSessionValue {
150
    #[cfg(feature = "tls12")]
151
    suite: &'static Tls12CipherSuite,
152
    #[cfg(feature = "tls12")]
153
    pub(crate) session_id: SessionId,
154
    #[cfg(feature = "tls12")]
155
    extended_ms: bool,
156
    #[doc(hidden)]
157
    #[cfg(feature = "tls12")]
158
    pub(crate) common: ClientSessionCommon,
159
}
160
161
#[cfg(feature = "tls12")]
162
impl Tls12ClientSessionValue {
163
0
    pub(crate) fn new(
164
0
        suite: &'static Tls12CipherSuite,
165
0
        session_id: SessionId,
166
0
        ticket: Arc<PayloadU16>,
167
0
        master_secret: &[u8],
168
0
        server_cert_chain: CertificateChain<'static>,
169
0
        server_cert_verifier: &Arc<dyn ServerCertVerifier>,
170
0
        client_creds: &Arc<dyn ResolvesClientCert>,
171
0
        time_now: UnixTime,
172
0
        lifetime_secs: u32,
173
0
        extended_ms: bool,
174
0
    ) -> Self {
175
0
        Self {
176
0
            suite,
177
0
            session_id,
178
0
            extended_ms,
179
0
            common: ClientSessionCommon::new(
180
0
                ticket,
181
0
                master_secret,
182
0
                time_now,
183
0
                lifetime_secs,
184
0
                server_cert_chain,
185
0
                server_cert_verifier,
186
0
                client_creds,
187
0
            ),
188
0
        }
189
0
    }
190
191
0
    pub(crate) fn ticket(&mut self) -> Arc<PayloadU16> {
192
0
        self.common.ticket.clone()
193
0
    }
194
195
0
    pub(crate) fn extended_ms(&self) -> bool {
196
0
        self.extended_ms
197
0
    }
198
199
0
    pub(crate) fn suite(&self) -> &'static Tls12CipherSuite {
200
0
        self.suite
201
0
    }
202
203
    #[doc(hidden)]
204
    /// Test only: rewind epoch by `delta` seconds.
205
0
    pub fn rewind_epoch(&mut self, delta: u32) {
206
0
        self.common.epoch -= delta as u64;
207
0
    }
208
}
209
210
#[cfg(feature = "tls12")]
211
impl core::ops::Deref for Tls12ClientSessionValue {
212
    type Target = ClientSessionCommon;
213
214
0
    fn deref(&self) -> &Self::Target {
215
0
        &self.common
216
0
    }
217
}
218
219
#[derive(Debug, Clone)]
220
pub struct ClientSessionCommon {
221
    ticket: Arc<PayloadU16>,
222
    secret: Zeroizing<PayloadU8>,
223
    epoch: u64,
224
    lifetime_secs: u32,
225
    server_cert_chain: Arc<CertificateChain<'static>>,
226
    server_cert_verifier: Weak<dyn ServerCertVerifier>,
227
    client_creds: Weak<dyn ResolvesClientCert>,
228
}
229
230
impl ClientSessionCommon {
231
0
    fn new(
232
0
        ticket: Arc<PayloadU16>,
233
0
        secret: &[u8],
234
0
        time_now: UnixTime,
235
0
        lifetime_secs: u32,
236
0
        server_cert_chain: CertificateChain<'static>,
237
0
        server_cert_verifier: &Arc<dyn ServerCertVerifier>,
238
0
        client_creds: &Arc<dyn ResolvesClientCert>,
239
0
    ) -> Self {
240
0
        Self {
241
0
            ticket,
242
0
            secret: Zeroizing::new(PayloadU8::new(secret.to_vec())),
243
0
            epoch: time_now.as_secs(),
244
0
            lifetime_secs: cmp::min(lifetime_secs, MAX_TICKET_LIFETIME),
245
0
            server_cert_chain: Arc::new(server_cert_chain),
246
0
            server_cert_verifier: Arc::downgrade(server_cert_verifier),
247
0
            client_creds: Arc::downgrade(client_creds),
248
0
        }
249
0
    }
250
251
0
    pub(crate) fn compatible_config(
252
0
        &self,
253
0
        server_cert_verifier: &Arc<dyn ServerCertVerifier>,
254
0
        client_creds: &Arc<dyn ResolvesClientCert>,
255
0
    ) -> bool {
256
0
        let same_verifier = Weak::ptr_eq(
257
0
            &Arc::downgrade(server_cert_verifier),
258
0
            &self.server_cert_verifier,
259
        );
260
0
        let same_creds = Weak::ptr_eq(&Arc::downgrade(client_creds), &self.client_creds);
261
262
0
        match (same_verifier, same_creds) {
263
0
            (true, true) => true,
264
            (false, _) => {
265
0
                crate::log::trace!("resumption not allowed between different ServerCertVerifiers");
266
0
                false
267
            }
268
            (_, _) => {
269
0
                crate::log::trace!(
270
                    "resumption not allowed between different ResolvesClientCert values"
271
                );
272
0
                false
273
            }
274
        }
275
0
    }
276
277
0
    pub(crate) fn server_cert_chain(&self) -> &CertificateChain<'static> {
278
0
        &self.server_cert_chain
279
0
    }
280
281
0
    pub(crate) fn secret(&self) -> &[u8] {
282
0
        self.secret.0.as_ref()
283
0
    }
284
285
0
    pub(crate) fn ticket(&self) -> &[u8] {
286
0
        self.ticket.0.as_ref()
287
0
    }
288
}
289
290
static MAX_TICKET_LIFETIME: u32 = 7 * 24 * 60 * 60;
291
292
/// This is the maximum allowed skew between server and client clocks, over
293
/// the maximum ticket lifetime period.  This encompasses TCP retransmission
294
/// times in case packet loss occurs when the client sends the ClientHello
295
/// or receives the NewSessionTicket, _and_ actual clock skew over this period.
296
static MAX_FRESHNESS_SKEW_MS: u32 = 60 * 1000;
297
298
// --- Server types ---
299
#[derive(Debug)]
300
pub struct ServerSessionValue {
301
    pub(crate) sni: Option<DnsName<'static>>,
302
    pub(crate) version: ProtocolVersion,
303
    pub(crate) cipher_suite: CipherSuite,
304
    pub(crate) master_secret: Zeroizing<PayloadU8>,
305
    pub(crate) extended_ms: bool,
306
    pub(crate) client_cert_chain: Option<CertificateChain<'static>>,
307
    pub(crate) alpn: Option<PayloadU8>,
308
    pub(crate) application_data: PayloadU16,
309
    pub creation_time_sec: u64,
310
    pub(crate) age_obfuscation_offset: u32,
311
    freshness: Option<bool>,
312
}
313
314
impl Codec<'_> for ServerSessionValue {
315
0
    fn encode(&self, bytes: &mut Vec<u8>) {
316
0
        if let Some(sni) = &self.sni {
317
0
            1u8.encode(bytes);
318
0
            let sni_bytes: &str = sni.as_ref();
319
0
            PayloadU8::<MaybeEmpty>::encode_slice(sni_bytes.as_bytes(), bytes);
320
0
        } else {
321
0
            0u8.encode(bytes);
322
0
        }
323
0
        self.version.encode(bytes);
324
0
        self.cipher_suite.encode(bytes);
325
0
        self.master_secret.encode(bytes);
326
0
        (u8::from(self.extended_ms)).encode(bytes);
327
0
        if let Some(chain) = &self.client_cert_chain {
328
0
            1u8.encode(bytes);
329
0
            chain.encode(bytes);
330
0
        } else {
331
0
            0u8.encode(bytes);
332
0
        }
333
0
        if let Some(alpn) = &self.alpn {
334
0
            1u8.encode(bytes);
335
0
            alpn.encode(bytes);
336
0
        } else {
337
0
            0u8.encode(bytes);
338
0
        }
339
0
        self.application_data.encode(bytes);
340
0
        self.creation_time_sec.encode(bytes);
341
0
        self.age_obfuscation_offset
342
0
            .encode(bytes);
343
0
    }
344
345
0
    fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> {
346
0
        let has_sni = u8::read(r)?;
347
0
        let sni = if has_sni == 1 {
348
0
            let dns_name = PayloadU8::<MaybeEmpty>::read(r)?;
349
0
            let dns_name = match DnsName::try_from(dns_name.0.as_slice()) {
350
0
                Ok(dns_name) => dns_name.to_owned(),
351
0
                Err(_) => return Err(InvalidMessage::InvalidServerName),
352
            };
353
354
0
            Some(dns_name)
355
        } else {
356
0
            None
357
        };
358
359
0
        let v = ProtocolVersion::read(r)?;
360
0
        let cs = CipherSuite::read(r)?;
361
0
        let ms = Zeroizing::new(PayloadU8::read(r)?);
362
0
        let ems = u8::read(r)?;
363
0
        let has_ccert = u8::read(r)? == 1;
364
0
        let ccert = if has_ccert {
365
0
            Some(CertificateChain::read(r)?.into_owned())
366
        } else {
367
0
            None
368
        };
369
0
        let has_alpn = u8::read(r)? == 1;
370
0
        let alpn = if has_alpn {
371
0
            Some(PayloadU8::read(r)?)
372
        } else {
373
0
            None
374
        };
375
0
        let application_data = PayloadU16::read(r)?;
376
0
        let creation_time_sec = u64::read(r)?;
377
0
        let age_obfuscation_offset = u32::read(r)?;
378
379
0
        Ok(Self {
380
0
            sni,
381
0
            version: v,
382
0
            cipher_suite: cs,
383
0
            master_secret: ms,
384
0
            extended_ms: ems == 1u8,
385
0
            client_cert_chain: ccert,
386
0
            alpn,
387
0
            application_data,
388
0
            creation_time_sec,
389
0
            age_obfuscation_offset,
390
0
            freshness: None,
391
0
        })
392
0
    }
393
}
394
395
impl ServerSessionValue {
396
0
    pub(crate) fn new(
397
0
        sni: Option<&DnsName<'_>>,
398
0
        v: ProtocolVersion,
399
0
        cs: CipherSuite,
400
0
        ms: &[u8],
401
0
        client_cert_chain: Option<CertificateChain<'static>>,
402
0
        alpn: Option<ProtocolName>,
403
0
        application_data: Vec<u8>,
404
0
        creation_time: UnixTime,
405
0
        age_obfuscation_offset: u32,
406
0
    ) -> Self {
407
        Self {
408
0
            sni: sni.map(|dns| dns.to_owned()),
409
0
            version: v,
410
0
            cipher_suite: cs,
411
0
            master_secret: Zeroizing::new(PayloadU8::new(ms.to_vec())),
412
            extended_ms: false,
413
0
            client_cert_chain,
414
0
            alpn: alpn.map(|p| PayloadU8::new(p.as_ref().to_vec())),
415
0
            application_data: PayloadU16::new(application_data),
416
0
            creation_time_sec: creation_time.as_secs(),
417
0
            age_obfuscation_offset,
418
0
            freshness: None,
419
        }
420
0
    }
421
422
    #[cfg(feature = "tls12")]
423
0
    pub(crate) fn set_extended_ms_used(&mut self) {
424
0
        self.extended_ms = true;
425
0
    }
426
427
0
    pub(crate) fn set_freshness(
428
0
        mut self,
429
0
        obfuscated_client_age_ms: u32,
430
0
        time_now: UnixTime,
431
0
    ) -> Self {
432
0
        let client_age_ms = obfuscated_client_age_ms.wrapping_sub(self.age_obfuscation_offset);
433
0
        let server_age_ms = (time_now
434
0
            .as_secs()
435
0
            .saturating_sub(self.creation_time_sec) as u32)
436
0
            .saturating_mul(1000);
437
438
0
        let age_difference = server_age_ms.abs_diff(client_age_ms);
439
440
0
        self.freshness = Some(age_difference <= MAX_FRESHNESS_SKEW_MS);
441
0
        self
442
0
    }
443
444
0
    pub(crate) fn is_fresh(&self) -> bool {
445
0
        self.freshness.unwrap_or_default()
446
0
    }
447
}
448
449
#[cfg(test)]
450
mod tests {
451
    use super::*;
452
453
    #[cfg(feature = "std")] // for UnixTime::now
454
    #[test]
455
    fn serversessionvalue_is_debug() {
456
        use std::{println, vec};
457
        let ssv = ServerSessionValue::new(
458
            None,
459
            ProtocolVersion::TLSv1_3,
460
            CipherSuite::TLS13_AES_128_GCM_SHA256,
461
            &[1, 2, 3],
462
            None,
463
            None,
464
            vec![4, 5, 6],
465
            UnixTime::now(),
466
            0x12345678,
467
        );
468
        println!("{ssv:?}");
469
    }
470
471
    #[test]
472
    fn serversessionvalue_no_sni() {
473
        let bytes = [
474
            0x00, 0x03, 0x03, 0xc0, 0x23, 0x03, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
475
            0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0xfe, 0xed, 0xf0, 0x0d,
476
        ];
477
        let mut rd = Reader::init(&bytes);
478
        let ssv = ServerSessionValue::read(&mut rd).unwrap();
479
        assert_eq!(ssv.get_encoding(), bytes);
480
    }
481
482
    #[test]
483
    fn serversessionvalue_with_cert() {
484
        let bytes = [
485
            0x00, 0x03, 0x03, 0xc0, 0x23, 0x03, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
486
            0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0xfe, 0xed, 0xf0, 0x0d,
487
        ];
488
        let mut rd = Reader::init(&bytes);
489
        let ssv = ServerSessionValue::read(&mut rd).unwrap();
490
        assert_eq!(ssv.get_encoding(), bytes);
491
    }
492
}