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::{PayloadU8, PayloadU16}; |
11 | use crate::msgs::codec::{Codec, Reader}; |
12 | use crate::msgs::handshake::CertificateChain; |
13 | #[cfg (feature = "tls12" )] |
14 | use crate::msgs::handshake::SessionId; |
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 | 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 | |
42 | impl Retrieved<&Tls13ClientSessionValue> { |
43 | pub(crate) fn obfuscated_ticket_age(&self) -> u32 { |
44 | let age_secs: u64 = self |
45 | .retrieved_at |
46 | .as_secs() |
47 | .saturating_sub(self.value.common.epoch); |
48 | let age_millis: u32 = age_secs as u32 * 1000; |
49 | age_millis.wrapping_add(self.value.age_add) |
50 | } |
51 | } |
52 | |
53 | impl<T: core::ops::Deref<Target = ClientSessionCommon>> Retrieved<T> { |
54 | pub(crate) fn has_expired(&self) -> bool { |
55 | let common: &ClientSessionCommon = &*self.value; |
56 | common.lifetime_secs != 0 |
57 | && commonu64 |
58 | .epoch |
59 | .saturating_add(u64::from(common.lifetime_secs)) |
60 | < self.retrieved_at.as_secs() |
61 | } |
62 | } |
63 | |
64 | impl<T> core::ops::Deref for Retrieved<T> { |
65 | type Target = T; |
66 | |
67 | fn deref(&self) -> &Self::Target { |
68 | &self.value |
69 | } |
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 | pub(crate) fn new( |
83 | suite: &'static Tls13CipherSuite, |
84 | ticket: Arc<PayloadU16>, |
85 | secret: &[u8], |
86 | server_cert_chain: CertificateChain<'static>, |
87 | server_cert_verifier: &Arc<dyn ServerCertVerifier>, |
88 | client_creds: &Arc<dyn ResolvesClientCert>, |
89 | time_now: UnixTime, |
90 | lifetime_secs: u32, |
91 | age_add: u32, |
92 | max_early_data_size: u32, |
93 | ) -> Self { |
94 | Self { |
95 | suite, |
96 | age_add, |
97 | max_early_data_size, |
98 | common: ClientSessionCommon::new( |
99 | ticket, |
100 | secret, |
101 | time_now, |
102 | lifetime_secs, |
103 | server_cert_chain, |
104 | server_cert_verifier, |
105 | client_creds, |
106 | ), |
107 | quic_params: PayloadU16(Vec::new()), |
108 | } |
109 | } |
110 | |
111 | pub fn max_early_data_size(&self) -> u32 { |
112 | self.max_early_data_size |
113 | } |
114 | |
115 | pub fn suite(&self) -> &'static Tls13CipherSuite { |
116 | self.suite |
117 | } |
118 | |
119 | #[doc (hidden)] |
120 | /// Test only: rewind epoch by `delta` seconds. |
121 | pub fn rewind_epoch(&mut self, delta: u32) { |
122 | self.common.epoch -= delta as u64; |
123 | } |
124 | |
125 | #[doc (hidden)] |
126 | /// Test only: replace `max_early_data_size` with `new` |
127 | pub fn _private_set_max_early_data_size(&mut self, new: u32) { |
128 | self.max_early_data_size = new; |
129 | } |
130 | |
131 | pub fn set_quic_params(&mut self, quic_params: &[u8]) { |
132 | self.quic_params = PayloadU16(quic_params.to_vec()); |
133 | } |
134 | |
135 | pub fn quic_params(&self) -> Vec<u8> { |
136 | self.quic_params.0.clone() |
137 | } |
138 | } |
139 | |
140 | impl core::ops::Deref for Tls13ClientSessionValue { |
141 | type Target = ClientSessionCommon; |
142 | |
143 | fn deref(&self) -> &Self::Target { |
144 | &self.common |
145 | } |
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 | pub(crate) fn new( |
164 | suite: &'static Tls12CipherSuite, |
165 | session_id: SessionId, |
166 | ticket: Arc<PayloadU16>, |
167 | master_secret: &[u8], |
168 | server_cert_chain: CertificateChain<'static>, |
169 | server_cert_verifier: &Arc<dyn ServerCertVerifier>, |
170 | client_creds: &Arc<dyn ResolvesClientCert>, |
171 | time_now: UnixTime, |
172 | lifetime_secs: u32, |
173 | extended_ms: bool, |
174 | ) -> Self { |
175 | Self { |
176 | suite, |
177 | session_id, |
178 | extended_ms, |
179 | common: ClientSessionCommon::new( |
180 | ticket, |
181 | master_secret, |
182 | time_now, |
183 | lifetime_secs, |
184 | server_cert_chain, |
185 | server_cert_verifier, |
186 | client_creds, |
187 | ), |
188 | } |
189 | } |
190 | |
191 | pub(crate) fn ticket(&mut self) -> Arc<PayloadU16> { |
192 | Arc::clone(&self.common.ticket) |
193 | } |
194 | |
195 | pub(crate) fn extended_ms(&self) -> bool { |
196 | self.extended_ms |
197 | } |
198 | |
199 | pub(crate) fn suite(&self) -> &'static Tls12CipherSuite { |
200 | self.suite |
201 | } |
202 | |
203 | #[doc (hidden)] |
204 | /// Test only: rewind epoch by `delta` seconds. |
205 | pub fn rewind_epoch(&mut self, delta: u32) { |
206 | self.common.epoch -= delta as u64; |
207 | } |
208 | } |
209 | |
210 | #[cfg (feature = "tls12" )] |
211 | impl core::ops::Deref for Tls12ClientSessionValue { |
212 | type Target = ClientSessionCommon; |
213 | |
214 | fn deref(&self) -> &Self::Target { |
215 | &self.common |
216 | } |
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 | fn new( |
232 | ticket: Arc<PayloadU16>, |
233 | secret: &[u8], |
234 | time_now: UnixTime, |
235 | lifetime_secs: u32, |
236 | server_cert_chain: CertificateChain<'static>, |
237 | server_cert_verifier: &Arc<dyn ServerCertVerifier>, |
238 | client_creds: &Arc<dyn ResolvesClientCert>, |
239 | ) -> Self { |
240 | Self { |
241 | ticket, |
242 | secret: Zeroizing::new(PayloadU8(secret.to_vec())), |
243 | epoch: time_now.as_secs(), |
244 | lifetime_secs: cmp::min(lifetime_secs, MAX_TICKET_LIFETIME), |
245 | server_cert_chain: Arc::new(server_cert_chain), |
246 | server_cert_verifier: Arc::downgrade(server_cert_verifier), |
247 | client_creds: Arc::downgrade(client_creds), |
248 | } |
249 | } |
250 | |
251 | pub(crate) fn compatible_config( |
252 | &self, |
253 | server_cert_verifier: &Arc<dyn ServerCertVerifier>, |
254 | client_creds: &Arc<dyn ResolvesClientCert>, |
255 | ) -> bool { |
256 | let same_verifier = Weak::ptr_eq( |
257 | &Arc::downgrade(server_cert_verifier), |
258 | &self.server_cert_verifier, |
259 | ); |
260 | let same_creds = Weak::ptr_eq(&Arc::downgrade(client_creds), &self.client_creds); |
261 | |
262 | match (same_verifier, same_creds) { |
263 | (true, true) => true, |
264 | (false, _) => { |
265 | crate::log::trace!("resumption not allowed between different ServerCertVerifiers" ); |
266 | false |
267 | } |
268 | (_, _) => { |
269 | crate::log::trace!( |
270 | "resumption not allowed between different ResolvesClientCert values" |
271 | ); |
272 | false |
273 | } |
274 | } |
275 | } |
276 | |
277 | pub(crate) fn server_cert_chain(&self) -> &CertificateChain<'static> { |
278 | &self.server_cert_chain |
279 | } |
280 | |
281 | pub(crate) fn secret(&self) -> &[u8] { |
282 | self.secret.0.as_ref() |
283 | } |
284 | |
285 | pub(crate) fn ticket(&self) -> &[u8] { |
286 | self.ticket.0.as_ref() |
287 | } |
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 | fn encode(&self, bytes: &mut Vec<u8>) { |
316 | if let Some(sni) = &self.sni { |
317 | 1u8.encode(bytes); |
318 | let sni_bytes: &str = sni.as_ref(); |
319 | PayloadU8::new(Vec::from(sni_bytes)).encode(bytes); |
320 | } else { |
321 | 0u8.encode(bytes); |
322 | } |
323 | self.version.encode(bytes); |
324 | self.cipher_suite.encode(bytes); |
325 | self.master_secret.encode(bytes); |
326 | (u8::from(self.extended_ms)).encode(bytes); |
327 | if let Some(chain) = &self.client_cert_chain { |
328 | 1u8.encode(bytes); |
329 | chain.encode(bytes); |
330 | } else { |
331 | 0u8.encode(bytes); |
332 | } |
333 | if let Some(alpn) = &self.alpn { |
334 | 1u8.encode(bytes); |
335 | alpn.encode(bytes); |
336 | } else { |
337 | 0u8.encode(bytes); |
338 | } |
339 | self.application_data.encode(bytes); |
340 | self.creation_time_sec.encode(bytes); |
341 | self.age_obfuscation_offset |
342 | .encode(bytes); |
343 | } |
344 | |
345 | fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { |
346 | let has_sni = u8::read(r)?; |
347 | let sni = if has_sni == 1 { |
348 | let dns_name = PayloadU8::read(r)?; |
349 | let dns_name = match DnsName::try_from(dns_name.0.as_slice()) { |
350 | Ok(dns_name) => dns_name.to_owned(), |
351 | Err(_) => return Err(InvalidMessage::InvalidServerName), |
352 | }; |
353 | |
354 | Some(dns_name) |
355 | } else { |
356 | None |
357 | }; |
358 | |
359 | let v = ProtocolVersion::read(r)?; |
360 | let cs = CipherSuite::read(r)?; |
361 | let ms = Zeroizing::new(PayloadU8::read(r)?); |
362 | let ems = u8::read(r)?; |
363 | let has_ccert = u8::read(r)? == 1; |
364 | let ccert = if has_ccert { |
365 | Some(CertificateChain::read(r)?.into_owned()) |
366 | } else { |
367 | None |
368 | }; |
369 | let has_alpn = u8::read(r)? == 1; |
370 | let alpn = if has_alpn { |
371 | Some(PayloadU8::read(r)?) |
372 | } else { |
373 | None |
374 | }; |
375 | let application_data = PayloadU16::read(r)?; |
376 | let creation_time_sec = u64::read(r)?; |
377 | let age_obfuscation_offset = u32::read(r)?; |
378 | |
379 | Ok(Self { |
380 | sni, |
381 | version: v, |
382 | cipher_suite: cs, |
383 | master_secret: ms, |
384 | extended_ms: ems == 1u8, |
385 | client_cert_chain: ccert, |
386 | alpn, |
387 | application_data, |
388 | creation_time_sec, |
389 | age_obfuscation_offset, |
390 | freshness: None, |
391 | }) |
392 | } |
393 | } |
394 | |
395 | impl ServerSessionValue { |
396 | pub(crate) fn new( |
397 | sni: Option<&DnsName<'_>>, |
398 | v: ProtocolVersion, |
399 | cs: CipherSuite, |
400 | ms: &[u8], |
401 | client_cert_chain: Option<CertificateChain<'static>>, |
402 | alpn: Option<Vec<u8>>, |
403 | application_data: Vec<u8>, |
404 | creation_time: UnixTime, |
405 | age_obfuscation_offset: u32, |
406 | ) -> Self { |
407 | Self { |
408 | sni: sni.map(|dns| dns.to_owned()), |
409 | version: v, |
410 | cipher_suite: cs, |
411 | master_secret: Zeroizing::new(PayloadU8::new(ms.to_vec())), |
412 | extended_ms: false, |
413 | client_cert_chain, |
414 | alpn: alpn.map(PayloadU8::new), |
415 | application_data: PayloadU16::new(application_data), |
416 | creation_time_sec: creation_time.as_secs(), |
417 | age_obfuscation_offset, |
418 | freshness: None, |
419 | } |
420 | } |
421 | |
422 | #[cfg (feature = "tls12" )] |
423 | pub(crate) fn set_extended_ms_used(&mut self) { |
424 | self.extended_ms = true; |
425 | } |
426 | |
427 | pub(crate) fn set_freshness( |
428 | mut self, |
429 | obfuscated_client_age_ms: u32, |
430 | time_now: UnixTime, |
431 | ) -> Self { |
432 | let client_age_ms = obfuscated_client_age_ms.wrapping_sub(self.age_obfuscation_offset); |
433 | let server_age_ms = (time_now |
434 | .as_secs() |
435 | .saturating_sub(self.creation_time_sec) as u32) |
436 | .saturating_mul(1000); |
437 | |
438 | let age_difference = if client_age_ms < server_age_ms { |
439 | server_age_ms - client_age_ms |
440 | } else { |
441 | client_age_ms - server_age_ms |
442 | }; |
443 | |
444 | self.freshness = Some(age_difference <= MAX_FRESHNESS_SKEW_MS); |
445 | self |
446 | } |
447 | |
448 | pub(crate) fn is_fresh(&self) -> bool { |
449 | self.freshness.unwrap_or_default() |
450 | } |
451 | } |
452 | |
453 | #[cfg (test)] |
454 | mod tests { |
455 | use super::*; |
456 | |
457 | #[cfg (feature = "std" )] // for UnixTime::now |
458 | #[test ] |
459 | fn serversessionvalue_is_debug() { |
460 | use std::{println, vec}; |
461 | let ssv = ServerSessionValue::new( |
462 | None, |
463 | ProtocolVersion::TLSv1_3, |
464 | CipherSuite::TLS13_AES_128_GCM_SHA256, |
465 | &[1, 2, 3], |
466 | None, |
467 | None, |
468 | vec![4, 5, 6], |
469 | UnixTime::now(), |
470 | 0x12345678, |
471 | ); |
472 | println!("{:?}" , ssv); |
473 | } |
474 | |
475 | #[test ] |
476 | fn serversessionvalue_no_sni() { |
477 | let bytes = [ |
478 | 0x00, 0x03, 0x03, 0xc0, 0x23, 0x03, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, |
479 | 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0xfe, 0xed, 0xf0, 0x0d, |
480 | ]; |
481 | let mut rd = Reader::init(&bytes); |
482 | let ssv = ServerSessionValue::read(&mut rd).unwrap(); |
483 | assert_eq!(ssv.get_encoding(), bytes); |
484 | } |
485 | |
486 | #[test ] |
487 | fn serversessionvalue_with_cert() { |
488 | let bytes = [ |
489 | 0x00, 0x03, 0x03, 0xc0, 0x23, 0x03, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, |
490 | 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0xfe, 0xed, 0xf0, 0x0d, |
491 | ]; |
492 | let mut rd = Reader::init(&bytes); |
493 | let ssv = ServerSessionValue::read(&mut rd).unwrap(); |
494 | assert_eq!(ssv.get_encoding(), bytes); |
495 | } |
496 | } |
497 | |