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::{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
52/// Dangerous configuration that should be audited and used with extreme care.
53pub 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/// A stored TLS 1.3 client session value.
116#[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    /// Decode a ticket from the given bytes.
128    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    /// Encode this ticket into `buf` for persistence.
169    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    /// Test only: replace `max_early_data_size` with `new`
179    #[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    /// Test only: rewind epoch by `delta` seconds.
189    #[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/// A "template" for future TLS1.3 client session values.
204#[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/// A stored TLS 1.2 client session value.
212#[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    /// Decode a ticket from the given bytes.
224    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    /// Encode this ticket into `buf` for persistence.
266    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    /// Test only: rewind epoch by `delta` seconds.
275    #[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/// Common data for stored client sessions.
290#[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    /// Send an empty `Certificate` and no `CertificateVerify`.
398    Empty { auth_context_tls13: Option<Vec<u8>> },
399    /// Send a non-empty `Certificate` and a `CertificateVerify`.
400    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);