/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 | | } |