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