Skip to main content

rustls/client/
mod.rs

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::{Identity, SelectedCredential, SignatureScheme};
10use crate::enums::{ApplicationProtocol, CertificateType};
11use crate::log::{debug, trace};
12use crate::msgs::{
13    CertificateChain, ExtensionType, MaybeEmpty, ServerExtensions, SessionId, SizedPayload,
14};
15use crate::sync::Arc;
16use crate::verify::DistinguishedName;
17#[cfg(feature = "webpki")]
18pub use crate::webpki::{
19    ServerVerifierBuilder, VerifierBuilderError, WebPkiServerVerifier,
20    verify_identity_signed_by_trust_anchor, verify_server_name,
21};
22use crate::{Tls12CipherSuite, Tls13CipherSuite, compress};
23
24mod config;
25pub use config::{
26    ClientConfig, ClientCredentialResolver, ClientSessionKey, ClientSessionStore,
27    CredentialRequest, Resumption, Tls12Resumption, WantsClientCert,
28};
29
30mod connection;
31#[cfg(feature = "std")]
32pub use connection::{ClientConnection, ClientConnectionBuilder, WriteEarlyData};
33pub use connection::{
34    ClientConnectionData, EarlyDataError, MayEncryptEarlyData, UnbufferedClientConnection,
35};
36
37mod ech;
38pub use ech::{EchConfig, EchGreaseConfig, EchMode, EchStatus};
39
40mod handy;
41#[cfg(any(feature = "std", feature = "hashbrown"))]
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
53/// Dangerous configuration that should be audited and used with extreme care.
54pub mod danger {
55    pub use super::config::danger::{DangerousClientConfig, DangerousClientConfigBuilder};
56    pub use crate::verify::{
57        HandshakeSignatureValid, PeerVerified, ServerIdentity, ServerVerifier,
58        SignatureVerificationInput,
59    };
60}
61
62#[cfg(test)]
63mod test;
64
65pub(crate) struct Retrieved<T> {
66    pub(crate) value: T,
67    retrieved_at: UnixTime,
68}
69
70impl<T> Retrieved<T> {
71    pub(crate) fn new(value: T, retrieved_at: UnixTime) -> Self {
72        Self {
73            value,
74            retrieved_at,
75        }
76    }
77
78    pub(crate) fn map<M>(&self, f: impl FnOnce(&T) -> Option<&M>) -> Option<Retrieved<&M>> {
79        Some(Retrieved {
80            value: f(&self.value)?,
81            retrieved_at: self.retrieved_at,
82        })
83    }
84}
85
86impl Retrieved<&Tls13ClientSessionValue> {
87    pub(crate) fn obfuscated_ticket_age(&self) -> u32 {
88        let age_secs = self
89            .retrieved_at
90            .as_secs()
91            .saturating_sub(self.value.common.epoch);
92        let age_millis = age_secs as u32 * 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/// A stored TLS 1.3 client session value.
117#[derive(Debug)]
118pub struct Tls13ClientSessionValue {
119    suite: &'static Tls13CipherSuite,
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 Tls13ClientSessionValue {
128    pub(crate) fn new(
129        input: Tls13ClientSessionInput,
130        ticket: Arc<SizedPayload<'static, u16, MaybeEmpty>>,
131        secret: &[u8],
132        time_now: UnixTime,
133        lifetime: Duration,
134        age_add: u32,
135        max_early_data_size: u32,
136    ) -> Self {
137        Self {
138            suite: input.suite,
139            secret: Zeroizing::new(secret.to_vec().into()),
140            age_add,
141            max_early_data_size,
142            common: ClientSessionCommon::new(ticket, time_now, lifetime, input.peer_identity),
143            quic_params: input
144                .quic_params
145                .unwrap_or_else(|| SizedPayload::from(Payload::new(Vec::new()))),
146        }
147    }
148
149    pub(crate) fn secret(&self) -> &[u8] {
150        self.secret.bytes()
151    }
152
153    /// Maximum early data size supported by the server.
154    pub fn max_early_data_size(&self) -> u32 {
155        self.max_early_data_size
156    }
157
158    /// The TLS 1.3 cipher suite used in this session.
159    pub fn suite(&self) -> &'static Tls13CipherSuite {
160        self.suite
161    }
162
163    /// Test only: rewind epoch by `delta` seconds.
164    #[doc(hidden)]
165    pub fn rewind_epoch(&mut self, delta: u32) {
166        self.common.epoch -= delta as u64;
167    }
168
169    /// Test only: replace `max_early_data_size` with `new`
170    #[doc(hidden)]
171    pub fn _private_set_max_early_data_size(&mut self, new: u32) {
172        self.max_early_data_size = new;
173    }
174
175    /// QUIC transport parameters provided by the server.
176    pub fn quic_params(&self) -> Vec<u8> {
177        self.quic_params.to_vec()
178    }
179}
180
181impl Deref for Tls13ClientSessionValue {
182    type Target = ClientSessionCommon;
183
184    fn deref(&self) -> &Self::Target {
185        &self.common
186    }
187}
188
189/// A "template" for future TLS1.3 client session values.
190#[derive(Clone)]
191pub(crate) struct Tls13ClientSessionInput {
192    pub(crate) suite: &'static Tls13CipherSuite,
193    pub(crate) peer_identity: Identity<'static>,
194    pub(crate) quic_params: Option<SizedPayload<'static, u16, MaybeEmpty>>,
195}
196
197/// A stored TLS 1.2 client session value.
198#[derive(Debug, Clone)]
199pub struct Tls12ClientSessionValue {
200    suite: &'static Tls12CipherSuite,
201    pub(crate) session_id: SessionId,
202    master_secret: Zeroizing<[u8; 48]>,
203    extended_ms: bool,
204    #[doc(hidden)]
205    pub(crate) common: ClientSessionCommon,
206}
207
208impl Tls12ClientSessionValue {
209    pub(crate) fn new(
210        suite: &'static Tls12CipherSuite,
211        session_id: SessionId,
212        ticket: Arc<SizedPayload<'static, u16, MaybeEmpty>>,
213        master_secret: &[u8; 48],
214        peer_identity: Identity<'static>,
215        time_now: UnixTime,
216        lifetime: Duration,
217        extended_ms: bool,
218    ) -> Self {
219        Self {
220            suite,
221            session_id,
222            master_secret: Zeroizing::new(*master_secret),
223            extended_ms,
224            common: ClientSessionCommon::new(ticket, time_now, lifetime, peer_identity),
225        }
226    }
227
228    pub(crate) fn master_secret(&self) -> &[u8; 48] {
229        &self.master_secret
230    }
231
232    pub(crate) fn ticket(&self) -> Arc<SizedPayload<'static, u16, MaybeEmpty>> {
233        self.common.ticket.clone()
234    }
235
236    pub(crate) fn extended_ms(&self) -> bool {
237        self.extended_ms
238    }
239
240    pub(crate) fn suite(&self) -> &'static Tls12CipherSuite {
241        self.suite
242    }
243
244    /// Test only: rewind epoch by `delta` seconds.
245    #[doc(hidden)]
246    pub fn rewind_epoch(&mut self, delta: u32) {
247        self.common.epoch -= delta as u64;
248    }
249}
250
251impl Deref for Tls12ClientSessionValue {
252    type Target = ClientSessionCommon;
253
254    fn deref(&self) -> &Self::Target {
255        &self.common
256    }
257}
258
259/// Common data for stored client sessions.
260#[derive(Debug, Clone)]
261pub struct ClientSessionCommon {
262    pub(crate) ticket: Arc<SizedPayload<'static, u16>>,
263    pub(crate) epoch: u64,
264    lifetime: Duration,
265    peer_identity: Arc<Identity<'static>>,
266}
267
268impl ClientSessionCommon {
269    pub(crate) fn new(
270        ticket: Arc<SizedPayload<'static, u16>>,
271        time_now: UnixTime,
272        lifetime: Duration,
273        peer_identity: Identity<'static>,
274    ) -> Self {
275        Self {
276            ticket,
277            epoch: time_now.as_secs(),
278            lifetime: Ord::min(lifetime, MAX_TICKET_LIFETIME),
279            peer_identity: Arc::new(peer_identity),
280        }
281    }
282
283    pub(crate) fn peer_identity(&self) -> &Identity<'static> {
284        &self.peer_identity
285    }
286
287    pub(crate) fn ticket(&self) -> &[u8] {
288        (*self.ticket).bytes()
289    }
290}
291
292#[derive(Debug)]
293struct ServerCertDetails {
294    cert_chain: CertificateChain<'static>,
295    ocsp_response: Vec<u8>,
296}
297
298impl ServerCertDetails {
299    fn new(cert_chain: CertificateChain<'static>, ocsp_response: Vec<u8>) -> Self {
300        Self {
301            cert_chain,
302            ocsp_response,
303        }
304    }
305}
306
307struct ClientHelloDetails {
308    alpn_protocols: Vec<ApplicationProtocol<'static>>,
309    sent_extensions: Vec<ExtensionType>,
310    extension_order_seed: u16,
311    offered_cert_compression: bool,
312}
313
314impl ClientHelloDetails {
315    fn new(alpn_protocols: Vec<ApplicationProtocol<'static>>, extension_order_seed: u16) -> Self {
316        Self {
317            alpn_protocols,
318            sent_extensions: Vec::new(),
319            extension_order_seed,
320            offered_cert_compression: false,
321        }
322    }
323
324    fn server_sent_unsolicited_extensions(
325        &self,
326        received_exts: &ServerExtensions<'_>,
327        allowed_unsolicited: &[ExtensionType],
328    ) -> bool {
329        let mut extensions = received_exts.collect_used();
330        extensions.extend(
331            received_exts
332                .unknown_extensions
333                .iter()
334                .map(|ext| ExtensionType::from(*ext)),
335        );
336        for ext_type in extensions {
337            if !self.sent_extensions.contains(&ext_type) && !allowed_unsolicited.contains(&ext_type)
338            {
339                trace!("Unsolicited extension {ext_type:?}");
340                return true;
341            }
342        }
343
344        false
345    }
346}
347
348enum ClientAuthDetails {
349    /// Send an empty `Certificate` and no `CertificateVerify`.
350    Empty { auth_context_tls13: Option<Vec<u8>> },
351    /// Send a non-empty `Certificate` and a `CertificateVerify`.
352    Verify {
353        credentials: SelectedCredential,
354        auth_context_tls13: Option<Vec<u8>>,
355        compressor: Option<&'static dyn compress::CertCompressor>,
356    },
357}
358
359impl ClientAuthDetails {
360    fn resolve(
361        negotiated_type: CertificateType,
362        resolver: &dyn ClientCredentialResolver,
363        root_hint_subjects: Option<&[DistinguishedName]>,
364        signature_schemes: &[SignatureScheme],
365        auth_context_tls13: Option<Vec<u8>>,
366        compressor: Option<&'static dyn compress::CertCompressor>,
367    ) -> Self {
368        let server_hello = CredentialRequest {
369            negotiated_type,
370            root_hint_subjects: root_hint_subjects.unwrap_or_default(),
371            signature_schemes,
372        };
373
374        if let Some(credentials) = resolver.resolve(&server_hello) {
375            debug!("Attempting client auth");
376            return Self::Verify {
377                credentials,
378                auth_context_tls13,
379                compressor,
380            };
381        }
382
383        debug!("Client auth requested but no cert/sigscheme available");
384        Self::Empty { auth_context_tls13 }
385    }
386}
387
388static MAX_TICKET_LIFETIME: Duration = Duration::from_secs(7 * 24 * 60 * 60);