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