Coverage Report

Created: 2025-10-29 07:05

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