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 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 pub fn rewind_epoch(&mut self, delta: u32) {
124 self.common.epoch -= delta as u64;
125 }
126
127 #[doc(hidden)]
128 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 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
336static MAX_FRESHNESS_SKEW_MS: u32 = 60 * 1000;
341
342pub 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")] #[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}