1use alloc::vec::Vec;
2use core::fmt;
3use core::ops::Deref;
4use core::time::Duration;
5
6use pki_types::UnixTime;
7use zeroize::Zeroizing;
8
9use crate::compress;
10use crate::crypto::cipher::Payload;
11use crate::crypto::kx::NamedGroup;
12use crate::crypto::{CipherSuite, CryptoProvider, Identity, SelectedCredential, SignatureScheme};
13use crate::enums::{ApplicationProtocol, CertificateType};
14use crate::error::{ApiMisuse, Error, InvalidMessage};
15use crate::msgs::{
16 CertificateChain, Codec, ExtensionType, MaybeEmpty, NewSessionTicketPayloadTls13, Reader,
17 SessionId, SizedPayload,
18};
19use crate::sync::Arc;
20use crate::tls12::Tls12CipherSuite;
21use crate::tls13::Tls13ProtocolSuite;
22use crate::tracing::{debug, trace};
23use crate::verify::{DistinguishedName, VerifiedIdentity};
24#[cfg(feature = "webpki")]
25pub use crate::webpki::{
26 ServerVerifierBuilder, VerifierBuilderError, WebPkiServerVerifier,
27 verify_identity_signed_by_trust_anchor, verify_server_name,
28};
29
30mod config;
31pub use config::{
32 ClientConfig, ClientCredentialResolver, ClientSessionKey, ClientSessionStore,
33 CredentialRequest, Resumption, TicketRequest, Tls12Resumption, WantsClientCert,
34};
35
36mod connection;
37pub use connection::{ClientConnectionBuilder, ClientHandshake, ClientSide, WriteEarlyData};
38
39mod ech;
40pub use ech::{EchConfig, EchGreaseConfig, EchMode, EchStatus};
41
42mod handy;
43#[cfg(feature = "std")]
44pub use handy::ClientSessionMemoryCache;
45
46mod hs;
47pub(crate) use hs::{ClientHandler, ClientState};
48
49mod tls12;
50pub(crate) use tls12::TLS12_HANDLER;
51
52mod tls13;
53pub(crate) use tls13::TLS13_HANDLER;
54
55pub mod danger {
57 pub use super::config::danger::{DangerousClientConfig, DangerousClientConfigBuilder};
58 pub use crate::verify::{
59 HandshakeSignatureValid, ServerIdentity, ServerVerifier, SignatureVerificationInput,
60 };
61}
62
63#[cfg(test)]
64mod test;
65
66pub(crate) struct Retrieved<T> {
67 pub(crate) value: T,
68 retrieved_at: UnixTime,
69}
70
71impl<T> Retrieved<T> {
72 pub(crate) fn new(value: T, retrieved_at: UnixTime) -> Self {
73 Self {
74 value,
75 retrieved_at,
76 }
77 }
78
79 pub(crate) fn map<M>(&self, f: impl FnOnce(&T) -> Option<&M>) -> Option<Retrieved<&M>> {
80 Some(Retrieved {
81 value: f(&self.value)?,
82 retrieved_at: self.retrieved_at,
83 })
84 }
85}
86
87impl Retrieved<&Tls13Session> {
88 pub(crate) fn obfuscated_ticket_age(&self) -> u32 {
89 let age_secs = self
90 .retrieved_at
91 .as_secs()
92 .saturating_sub(self.value.common.epoch);
93 let age_millis = u32::try_from(age_secs)
95 .unwrap_or(u32::MAX)
96 .saturating_mul(1000);
97 age_millis.wrapping_add(self.value.age_add)
98 }
99}
100
101impl<T: Deref<Target = ClientSessionCommon>> Retrieved<T> {
102 pub(crate) fn has_expired(&self) -> bool {
103 let common = &*self.value;
104 common.lifetime != Duration::ZERO
105 && common
106 .epoch
107 .saturating_add(common.lifetime.as_secs())
108 < self.retrieved_at.as_secs()
109 }
110}
111
112impl<T> Deref for Retrieved<T> {
113 type Target = T;
114
115 fn deref(&self) -> &Self::Target {
116 &self.value
117 }
118}
119
120pub struct Tls13Session {
122 suite: Tls13ProtocolSuite,
123 secret: Zeroizing<SizedPayload<'static, u8>>,
124 pub(crate) age_add: u32,
125 max_early_data_size: u32,
126 pub(crate) common: ClientSessionCommon,
127 quic_params: SizedPayload<'static, u16, MaybeEmpty>,
128 alpn: Option<ApplicationProtocol<'static>>,
129}
130
131impl Tls13Session {
132 #[cfg(test)]
134 pub fn from_slice(bytes: &[u8], provider: &CryptoProvider) -> Result<Self, Error> {
135 Reader::new(bytes).all("Tls13Session", |reader| {
136 let suite = CipherSuite::read(reader)?;
137 let suite = provider
138 .tls13_cipher_suites
139 .iter()
140 .find(|s| s.common.suite == suite)
141 .ok_or(ApiMisuse::ResumingFromUnknownCipherSuite(suite))?;
142
143 Ok(Self {
144 suite: Tls13ProtocolSuite::Tcp(suite),
145 secret: Zeroizing::new(SizedPayload::<u8>::read(reader)?.into_owned()),
146 age_add: u32::read(reader)?,
147 max_early_data_size: u32::read(reader)?,
148 common: ClientSessionCommon::read(reader)?,
149 quic_params: SizedPayload::<u16, MaybeEmpty>::read(reader)?.into_owned(),
150 alpn: match u8::read(reader)? {
151 1 => Some(ApplicationProtocol::read(reader)?.to_owned()),
152 _ => None,
153 },
154 })
155 })
156 }
157
158 pub(crate) fn new(
159 ticket: &NewSessionTicketPayloadTls13,
160 input: Tls13ClientSessionInput,
161 secret: &[u8],
162 time_now: UnixTime,
163 ) -> Self {
164 Self {
165 suite: input.suite,
166 secret: Zeroizing::new(secret.to_vec().into()),
167 age_add: ticket.age_add,
168 max_early_data_size: ticket
169 .extensions
170 .max_early_data_size
171 .unwrap_or_default(),
172 common: ClientSessionCommon::new(
173 ticket.ticket.clone(),
174 time_now,
175 ticket.lifetime,
176 input.peer_identity,
177 ),
178 quic_params: input
179 .quic_params
180 .unwrap_or_else(|| SizedPayload::from(Payload::new(Vec::new()))),
181 alpn: input.alpn,
182 }
183 }
184
185 pub fn encode(&self, buf: &mut Vec<u8>) {
187 self.suite
188 .suite()
189 .common
190 .suite
191 .encode(buf);
192 self.secret.encode(buf);
193 buf.extend_from_slice(&self.age_add.to_be_bytes());
194 buf.extend_from_slice(&self.max_early_data_size.to_be_bytes());
195 self.common.encode(buf);
196 self.quic_params.encode(buf);
197 if let Some(alpn) = &self.alpn {
198 1u8.encode(buf);
199 alpn.encode(buf);
200 } else {
201 0u8.encode(buf);
202 }
203 }
204
205 #[doc(hidden)]
207 pub fn _reset_max_early_data_size(&mut self, expected: u32, desired: u32) {
208 assert_eq!(
209 self.max_early_data_size, expected,
210 "max_early_data_size was not expected value"
211 );
212 self.max_early_data_size = desired;
213 }
214
215 #[doc(hidden)]
217 pub fn rewind_epoch(&mut self, delta: u32) {
218 self.common.epoch -= delta as u64;
219 }
220}
221
222impl Deref for Tls13Session {
223 type Target = ClientSessionCommon;
224
225 fn deref(&self) -> &Self::Target {
226 &self.common
227 }
228}
229
230impl fmt::Debug for Tls13Session {
231 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
232 let Self {
233 suite,
234 secret: _,
235 age_add,
236 max_early_data_size,
237 common,
238 quic_params,
239 alpn,
240 } = self;
241 f.debug_struct("Tls13Session")
242 .field("suite", suite)
243 .field("age_add", age_add)
244 .field("max_early_data_size", max_early_data_size)
245 .field("common", common)
246 .field("quic_params", quic_params)
247 .field("alpn", alpn)
248 .finish_non_exhaustive()
249 }
250}
251
252#[derive(Clone)]
254pub(crate) struct Tls13ClientSessionInput {
255 pub(crate) suite: Tls13ProtocolSuite,
256 pub(crate) peer_identity: VerifiedIdentity<'static>,
257 pub(crate) quic_params: Option<SizedPayload<'static, u16, MaybeEmpty>>,
258 pub(crate) alpn: Option<ApplicationProtocol<'static>>,
259}
260
261#[derive(Clone)]
263pub struct Tls12Session {
264 suite: &'static Tls12CipherSuite,
265 pub(crate) session_id: SessionId,
266 master_secret: Zeroizing<[u8; 48]>,
267 extended_ms: bool,
268 #[doc(hidden)]
269 pub(crate) common: ClientSessionCommon,
270}
271
272impl Tls12Session {
273 pub fn from_slice(bytes: &[u8], provider: &CryptoProvider) -> Result<Self, Error> {
275 Reader::new(bytes).all("Tls12Session", |reader| {
276 let suite = CipherSuite::read(reader)?;
277 let suite = provider
278 .tls12_cipher_suites
279 .iter()
280 .find(|s| s.common.suite == suite)
281 .ok_or(ApiMisuse::ResumingFromUnknownCipherSuite(suite))?;
282
283 Ok(Self {
284 suite: *suite,
285 session_id: SessionId::read(reader)?,
286 master_secret: Zeroizing::new(
287 reader
288 .take_array("MasterSecret")
289 .copied()?,
290 ),
291 extended_ms: matches!(u8::read(reader)?, 1),
292 common: ClientSessionCommon::read(reader)?,
293 })
294 })
295 }
296
297 pub(crate) fn new(
298 suite: &'static Tls12CipherSuite,
299 session_id: SessionId,
300 ticket: Arc<SizedPayload<'static, u16, MaybeEmpty>>,
301 master_secret: &[u8; 48],
302 peer_identity: VerifiedIdentity<'static>,
303 time_now: UnixTime,
304 lifetime: Duration,
305 extended_ms: bool,
306 ) -> Self {
307 Self {
308 suite,
309 session_id,
310 master_secret: Zeroizing::new(*master_secret),
311 extended_ms,
312 common: ClientSessionCommon::new(ticket, time_now, lifetime, peer_identity),
313 }
314 }
315
316 pub fn encode(&self, buf: &mut Vec<u8>) {
318 self.suite.common.suite.encode(buf);
319 self.session_id.encode(buf);
320 buf.extend_from_slice(&*self.master_secret);
321 buf.push(self.extended_ms as u8);
322 self.common.encode(buf);
323 }
324
325 #[doc(hidden)]
327 pub fn rewind_epoch(&mut self, delta: u32) {
328 self.common.epoch -= delta as u64;
329 }
330}
331
332impl Deref for Tls12Session {
333 type Target = ClientSessionCommon;
334
335 fn deref(&self) -> &Self::Target {
336 &self.common
337 }
338}
339
340impl fmt::Debug for Tls12Session {
341 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342 let Self {
343 suite,
344 session_id,
345 master_secret: _,
346 extended_ms,
347 common,
348 } = self;
349 f.debug_struct("Tls12Session")
350 .field("suite", suite)
351 .field("session_id", session_id)
352 .field("extended_ms", extended_ms)
353 .field("common", common)
354 .finish_non_exhaustive()
355 }
356}
357
358#[derive(Debug, Clone)]
360pub struct ClientSessionCommon {
361 pub(crate) ticket: Arc<SizedPayload<'static, u16>>,
362 pub(crate) epoch: u64,
363 lifetime: Duration,
364 peer_identity: Arc<VerifiedIdentity<'static>>,
365}
366
367impl ClientSessionCommon {
368 pub(crate) fn new(
369 ticket: Arc<SizedPayload<'static, u16>>,
370 time_now: UnixTime,
371 lifetime: Duration,
372 peer_identity: VerifiedIdentity<'static>,
373 ) -> Self {
374 Self {
375 ticket,
376 epoch: time_now.as_secs(),
377 lifetime: Ord::min(lifetime, MAX_TICKET_LIFETIME),
378 peer_identity: Arc::new(peer_identity),
379 }
380 }
381
382 pub(crate) fn peer_identity(&self) -> &Identity<'static> {
383 &self.peer_identity
384 }
385
386 pub(crate) fn ticket(&self) -> &[u8] {
387 (*self.ticket).bytes()
388 }
389}
390
391impl<'a> Codec<'a> for ClientSessionCommon {
392 fn encode(&self, bytes: &mut Vec<u8>) {
393 self.ticket.encode(bytes);
394 bytes.extend_from_slice(&self.epoch.to_be_bytes());
395 bytes.extend_from_slice(&self.lifetime.as_secs().to_be_bytes());
396 self.peer_identity.encode(bytes);
397 }
398
399 fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> {
400 Ok(Self {
401 ticket: Arc::new(SizedPayload::read(r)?.into_owned()),
402 epoch: u64::read(r)?,
403 lifetime: Duration::from_secs(u64::read(r)?),
404 peer_identity: Arc::new(VerifiedIdentity::assertion(Identity::read(r)?.into_owned())),
405 })
406 }
407}
408
409#[derive(Debug)]
410struct ServerCertDetails {
411 cert_chain: CertificateChain<'static>,
412 ocsp_response: Vec<u8>,
413}
414
415impl ServerCertDetails {
416 fn new(cert_chain: CertificateChain<'static>, ocsp_response: Vec<u8>) -> Self {
417 Self {
418 cert_chain,
419 ocsp_response,
420 }
421 }
422}
423
424struct ClientHelloDetails {
425 alpn_protocols: Vec<ApplicationProtocol<'static>>,
426 sent_extensions: Vec<ExtensionType>,
427 extension_order_seed: u16,
428 offered_cert_compression: bool,
429 offered_cipher_suites: Vec<CipherSuite>,
430 offered_key_shares: Vec<NamedGroup>,
431}
432
433impl ClientHelloDetails {
434 fn new(alpn_protocols: Vec<ApplicationProtocol<'static>>, extension_order_seed: u16) -> Self {
435 Self {
436 alpn_protocols,
437 sent_extensions: Vec::new(),
438 extension_order_seed,
439 offered_cert_compression: false,
440 offered_cipher_suites: Vec::new(),
441 offered_key_shares: Vec::new(),
442 }
443 }
444
445 fn server_sent_unsolicited_extensions(
446 &self,
447 received_exts: impl Iterator<Item = ExtensionType>,
448 allowed_unsolicited: &[ExtensionType],
449 ) -> bool {
450 for ext_type in received_exts {
451 if !self.sent_extensions.contains(&ext_type) && !allowed_unsolicited.contains(&ext_type)
452 {
453 trace!("Unsolicited extension {ext_type:?}");
454 return true;
455 }
456 }
457
458 false
459 }
460}
461
462enum ClientAuthDetails {
463 Empty { auth_context_tls13: Option<Vec<u8>> },
465 Verify {
467 credentials: SelectedCredential,
468 auth_context_tls13: Option<Vec<u8>>,
469 compressor: Option<&'static dyn compress::CertCompressor>,
470 },
471}
472
473impl ClientAuthDetails {
474 fn resolve(
475 negotiated_type: CertificateType,
476 resolver: &dyn ClientCredentialResolver,
477 root_hint_subjects: Option<&[DistinguishedName]>,
478 signature_schemes: &[SignatureScheme],
479 auth_context_tls13: Option<Vec<u8>>,
480 compressor: Option<&'static dyn compress::CertCompressor>,
481 ) -> Self {
482 let server_hello = CredentialRequest {
483 negotiated_type,
484 root_hint_subjects: root_hint_subjects.unwrap_or_default(),
485 signature_schemes,
486 };
487
488 if let Some(credentials) = resolver.resolve(&server_hello) {
489 debug!("Attempting client auth");
490 return Self::Verify {
491 credentials,
492 auth_context_tls13,
493 compressor,
494 };
495 }
496
497 debug!("Client auth requested but no cert/sigscheme available");
498 Self::Empty { auth_context_tls13 }
499 }
500}
501
502static MAX_TICKET_LIFETIME: Duration = Duration::from_secs(7 * 24 * 60 * 60);
503
504#[cfg(test)]
505mod tests {
506 use alloc::format;
507
508 use pki_types::SubjectPublicKeyInfoDer;
509
510 use super::*;
511 use crate::crypto::{TEST_PROVIDER, tls12_suite};
512
513 #[test]
514 fn debug_of_session_types() {
515 let tls12 = Tls12Session::new(
516 tls12_suite(CipherSuite(0xff12), &TEST_PROVIDER),
517 SessionId::empty(),
518 Arc::new(SizedPayload::empty()),
519 &[0xa5; 48],
520 VerifiedIdentity::assertion(Identity::RawPublicKey(SubjectPublicKeyInfoDer::from(
521 &b"spki"[..],
522 ))),
523 UnixTime::since_unix_epoch(Duration::from_secs(1)),
524 Duration::from_secs(2),
525 true,
526 );
527 assert_eq!(
528 format!("{tls12:?}"),
529 "Tls12Session { suite: Tls12CipherSuite { suite: 0xff12, .. }, session_id: , extended_ms: true, common: ClientSessionCommon { ticket: , epoch: 1, lifetime: 2s, peer_identity: VerifiedIdentity(RawPublicKey(SubjectPublicKeyInfoDer(0x73706b69))) }, .. }"
530 );
531
532 let tls13 = Tls13Session::new(
533 &NewSessionTicketPayloadTls13::new(
534 Duration::from_secs(2),
535 3,
536 [4u8; 32],
537 Vec::from([5]),
538 ),
539 Tls13ClientSessionInput {
540 suite: Tls13ProtocolSuite::Tcp(TEST_PROVIDER.tls13_cipher_suites[0]),
541 peer_identity: VerifiedIdentity::assertion(Identity::RawPublicKey(
542 SubjectPublicKeyInfoDer::from(&b"spki"[..]),
543 )),
544 quic_params: None,
545 alpn: None,
546 },
547 &[0xa5; 32],
548 UnixTime::since_unix_epoch(Duration::from_secs(1)),
549 );
550 assert_eq!(
551 format!("{tls13:?}"),
552 "Tls13Session { suite: Tcp(Tls13CipherSuite { suite: 0xff13, .. }), age_add: 3, max_early_data_size: 0, common: ClientSessionCommon { ticket: 05, epoch: 1, lifetime: 2s, peer_identity: VerifiedIdentity(RawPublicKey(SubjectPublicKeyInfoDer(0x73706b69))) }, quic_params: , alpn: None, .. }"
553 );
554 }
555}