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