Skip to main content

rustls/client/
mod.rs

1use alloc::vec::Vec;
2use core::fmt;
3use core::ops::Deref;
4use core::time::Duration;
5
6use pki_types::UnixTime;
7use zeroize::Zeroizing;
8
9use crate::crypto::cipher::Payload;
10use crate::crypto::{CipherSuite, CryptoProvider, Identity, SelectedCredential, SignatureScheme};
11use crate::enums::{ApplicationProtocol, CertificateType};
12use crate::error::{ApiMisuse, Error, InvalidMessage};
13use crate::msgs::{
14    CertificateChain, Codec, ExtensionType, MaybeEmpty, NewSessionTicketPayloadTls13, Reader,
15    SessionId, SizedPayload,
16};
17use crate::sync::Arc;
18use crate::tls13::Tls13ProtocolSuite;
19use crate::tracing::{debug, trace};
20use crate::verify::{DistinguishedName, VerifiedIdentity};
21#[cfg(feature = "webpki")]
22pub use crate::webpki::{
23    ServerVerifierBuilder, VerifierBuilderError, WebPkiServerVerifier,
24    verify_identity_signed_by_trust_anchor, verify_server_name,
25};
26use crate::{Tls12CipherSuite, compress};
27
28mod config;
29pub use config::{
30    ClientConfig, ClientCredentialResolver, ClientSessionKey, ClientSessionStore,
31    CredentialRequest, Resumption, TicketRequest, Tls12Resumption, WantsClientCert,
32};
33
34mod connection;
35pub use connection::{ClientConnectionBuilder, ClientHandshake, ClientSide, WriteEarlyData};
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, ClientState};
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, ServerIdentity, ServerVerifier, SignatureVerificationInput,
57    };
58}
59
60#[cfg(test)]
61mod test;
62
63pub(crate) struct Retrieved<T> {
64    pub(crate) value: T,
65    retrieved_at: UnixTime,
66}
67
68impl<T> Retrieved<T> {
69    pub(crate) fn new(value: T, retrieved_at: UnixTime) -> Self {
70        Self {
71            value,
72            retrieved_at,
73        }
74    }
75
76    pub(crate) fn map<M>(&self, f: impl FnOnce(&T) -> Option<&M>) -> Option<Retrieved<&M>> {
77        Some(Retrieved {
78            value: f(&self.value)?,
79            retrieved_at: self.retrieved_at,
80        })
81    }
82}
83
84impl Retrieved<&Tls13Session> {
85    pub(crate) fn obfuscated_ticket_age(&self) -> u32 {
86        let age_secs = self
87            .retrieved_at
88            .as_secs()
89            .saturating_sub(self.value.common.epoch);
90        // nb. tickets have an upper age limit of ~7 days, well short of the 49 days here
91        let age_millis = u32::try_from(age_secs)
92            .unwrap_or(u32::MAX)
93            .saturating_mul(1000);
94        age_millis.wrapping_add(self.value.age_add)
95    }
96}
97
98impl<T: Deref<Target = ClientSessionCommon>> Retrieved<T> {
99    pub(crate) fn has_expired(&self) -> bool {
100        let common = &*self.value;
101        common.lifetime != Duration::ZERO
102            && common
103                .epoch
104                .saturating_add(common.lifetime.as_secs())
105                < self.retrieved_at.as_secs()
106    }
107}
108
109impl<T> Deref for Retrieved<T> {
110    type Target = T;
111
112    fn deref(&self) -> &Self::Target {
113        &self.value
114    }
115}
116
117/// A stored TLS 1.3 client session value.
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    /// Decode a ticket from the given bytes.
129    #[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    /// Encode this ticket into `buf` for persistence.
177    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    /// Test only: CAS `max_early_data_size` from `expected` to `desired`
191    #[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    /// Test only: rewind epoch by `delta` seconds.
201    #[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
215impl fmt::Debug for Tls13Session {
216    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217        let Self {
218            suite,
219            secret: _,
220            age_add,
221            max_early_data_size,
222            common,
223            quic_params,
224        } = self;
225        f.debug_struct("Tls13Session")
226            .field("suite", suite)
227            .field("age_add", age_add)
228            .field("max_early_data_size", max_early_data_size)
229            .field("common", common)
230            .field("quic_params", quic_params)
231            .finish_non_exhaustive()
232    }
233}
234
235/// A "template" for future TLS1.3 client session values.
236#[derive(Clone)]
237pub(crate) struct Tls13ClientSessionInput {
238    pub(crate) suite: Tls13ProtocolSuite,
239    pub(crate) peer_identity: VerifiedIdentity<'static>,
240    pub(crate) quic_params: Option<SizedPayload<'static, u16, MaybeEmpty>>,
241}
242
243/// A stored TLS 1.2 client session value.
244#[derive(Clone)]
245pub struct Tls12Session {
246    suite: &'static Tls12CipherSuite,
247    pub(crate) session_id: SessionId,
248    master_secret: Zeroizing<[u8; 48]>,
249    extended_ms: bool,
250    #[doc(hidden)]
251    pub(crate) common: ClientSessionCommon,
252}
253
254impl Tls12Session {
255    /// Decode a ticket from the given bytes.
256    pub fn from_slice(bytes: &[u8], provider: &CryptoProvider) -> Result<Self, Error> {
257        Reader::new(bytes).all("Tls12Session", |reader| {
258            let suite = CipherSuite::read(reader)?;
259            let suite = provider
260                .tls12_cipher_suites
261                .iter()
262                .find(|s| s.common.suite == suite)
263                .ok_or(ApiMisuse::ResumingFromUnknownCipherSuite(suite))?;
264
265            Ok(Self {
266                suite: *suite,
267                session_id: SessionId::read(reader)?,
268                master_secret: Zeroizing::new(
269                    reader
270                        .take_array("MasterSecret")
271                        .copied()?,
272                ),
273                extended_ms: matches!(u8::read(reader)?, 1),
274                common: ClientSessionCommon::read(reader)?,
275            })
276        })
277    }
278
279    pub(crate) fn new(
280        suite: &'static Tls12CipherSuite,
281        session_id: SessionId,
282        ticket: Arc<SizedPayload<'static, u16, MaybeEmpty>>,
283        master_secret: &[u8; 48],
284        peer_identity: VerifiedIdentity<'static>,
285        time_now: UnixTime,
286        lifetime: Duration,
287        extended_ms: bool,
288    ) -> Self {
289        Self {
290            suite,
291            session_id,
292            master_secret: Zeroizing::new(*master_secret),
293            extended_ms,
294            common: ClientSessionCommon::new(ticket, time_now, lifetime, peer_identity),
295        }
296    }
297
298    /// Encode this ticket into `buf` for persistence.
299    pub fn encode(&self, buf: &mut Vec<u8>) {
300        self.suite.common.suite.encode(buf);
301        self.session_id.encode(buf);
302        buf.extend_from_slice(&*self.master_secret);
303        buf.push(self.extended_ms as u8);
304        self.common.encode(buf);
305    }
306
307    /// Test only: rewind epoch by `delta` seconds.
308    #[doc(hidden)]
309    pub fn rewind_epoch(&mut self, delta: u32) {
310        self.common.epoch -= delta as u64;
311    }
312}
313
314impl Deref for Tls12Session {
315    type Target = ClientSessionCommon;
316
317    fn deref(&self) -> &Self::Target {
318        &self.common
319    }
320}
321
322impl fmt::Debug for Tls12Session {
323    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
324        let Self {
325            suite,
326            session_id,
327            master_secret: _,
328            extended_ms,
329            common,
330        } = self;
331        f.debug_struct("Tls12Session")
332            .field("suite", suite)
333            .field("session_id", session_id)
334            .field("extended_ms", extended_ms)
335            .field("common", common)
336            .finish_non_exhaustive()
337    }
338}
339
340/// Common data for stored client sessions.
341#[derive(Debug, Clone)]
342pub struct ClientSessionCommon {
343    pub(crate) ticket: Arc<SizedPayload<'static, u16>>,
344    pub(crate) epoch: u64,
345    lifetime: Duration,
346    peer_identity: Arc<VerifiedIdentity<'static>>,
347}
348
349impl ClientSessionCommon {
350    pub(crate) fn new(
351        ticket: Arc<SizedPayload<'static, u16>>,
352        time_now: UnixTime,
353        lifetime: Duration,
354        peer_identity: VerifiedIdentity<'static>,
355    ) -> Self {
356        Self {
357            ticket,
358            epoch: time_now.as_secs(),
359            lifetime: Ord::min(lifetime, MAX_TICKET_LIFETIME),
360            peer_identity: Arc::new(peer_identity),
361        }
362    }
363
364    pub(crate) fn peer_identity(&self) -> &Identity<'static> {
365        &self.peer_identity
366    }
367
368    pub(crate) fn ticket(&self) -> &[u8] {
369        (*self.ticket).bytes()
370    }
371}
372
373impl<'a> Codec<'a> for ClientSessionCommon {
374    fn encode(&self, bytes: &mut Vec<u8>) {
375        self.ticket.encode(bytes);
376        bytes.extend_from_slice(&self.epoch.to_be_bytes());
377        bytes.extend_from_slice(&self.lifetime.as_secs().to_be_bytes());
378        self.peer_identity.encode(bytes);
379    }
380
381    fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> {
382        Ok(Self {
383            ticket: Arc::new(SizedPayload::read(r)?.into_owned()),
384            epoch: u64::read(r)?,
385            lifetime: Duration::from_secs(u64::read(r)?),
386            peer_identity: Arc::new(VerifiedIdentity::assertion(Identity::read(r)?.into_owned())),
387        })
388    }
389}
390
391#[derive(Debug)]
392struct ServerCertDetails {
393    cert_chain: CertificateChain<'static>,
394    ocsp_response: Vec<u8>,
395}
396
397impl ServerCertDetails {
398    fn new(cert_chain: CertificateChain<'static>, ocsp_response: Vec<u8>) -> Self {
399        Self {
400            cert_chain,
401            ocsp_response,
402        }
403    }
404}
405
406struct ClientHelloDetails {
407    alpn_protocols: Vec<ApplicationProtocol<'static>>,
408    sent_extensions: Vec<ExtensionType>,
409    extension_order_seed: u16,
410    offered_cert_compression: bool,
411    offered_cipher_suites: Vec<CipherSuite>,
412}
413
414impl ClientHelloDetails {
415    fn new(alpn_protocols: Vec<ApplicationProtocol<'static>>, extension_order_seed: u16) -> Self {
416        Self {
417            alpn_protocols,
418            sent_extensions: Vec::new(),
419            extension_order_seed,
420            offered_cert_compression: false,
421            offered_cipher_suites: Vec::new(),
422        }
423    }
424
425    fn server_sent_unsolicited_extensions(
426        &self,
427        received_exts: impl Iterator<Item = ExtensionType>,
428        allowed_unsolicited: &[ExtensionType],
429    ) -> bool {
430        for ext_type in received_exts {
431            if !self.sent_extensions.contains(&ext_type) && !allowed_unsolicited.contains(&ext_type)
432            {
433                trace!("Unsolicited extension {ext_type:?}");
434                return true;
435            }
436        }
437
438        false
439    }
440}
441
442enum ClientAuthDetails {
443    /// Send an empty `Certificate` and no `CertificateVerify`.
444    Empty { auth_context_tls13: Option<Vec<u8>> },
445    /// Send a non-empty `Certificate` and a `CertificateVerify`.
446    Verify {
447        credentials: SelectedCredential,
448        auth_context_tls13: Option<Vec<u8>>,
449        compressor: Option<&'static dyn compress::CertCompressor>,
450    },
451}
452
453impl ClientAuthDetails {
454    fn resolve(
455        negotiated_type: CertificateType,
456        resolver: &dyn ClientCredentialResolver,
457        root_hint_subjects: Option<&[DistinguishedName]>,
458        signature_schemes: &[SignatureScheme],
459        auth_context_tls13: Option<Vec<u8>>,
460        compressor: Option<&'static dyn compress::CertCompressor>,
461    ) -> Self {
462        let server_hello = CredentialRequest {
463            negotiated_type,
464            root_hint_subjects: root_hint_subjects.unwrap_or_default(),
465            signature_schemes,
466        };
467
468        if let Some(credentials) = resolver.resolve(&server_hello) {
469            debug!("Attempting client auth");
470            return Self::Verify {
471                credentials,
472                auth_context_tls13,
473                compressor,
474            };
475        }
476
477        debug!("Client auth requested but no cert/sigscheme available");
478        Self::Empty { auth_context_tls13 }
479    }
480}
481
482static MAX_TICKET_LIFETIME: Duration = Duration::from_secs(7 * 24 * 60 * 60);
483
484#[cfg(test)]
485mod tests {
486    use alloc::format;
487
488    use pki_types::SubjectPublicKeyInfoDer;
489
490    use super::*;
491    use crate::crypto::{TEST_PROVIDER, tls12_suite};
492
493    #[test]
494    fn debug_of_session_types() {
495        let tls12 = Tls12Session::new(
496            tls12_suite(CipherSuite(0xff12), &TEST_PROVIDER),
497            SessionId::empty(),
498            Arc::new(SizedPayload::empty()),
499            &[0xa5; 48],
500            VerifiedIdentity::assertion(Identity::RawPublicKey(SubjectPublicKeyInfoDer::from(
501                &b"spki"[..],
502            ))),
503            UnixTime::since_unix_epoch(Duration::from_secs(1)),
504            Duration::from_secs(2),
505            true,
506        );
507        assert_eq!(
508            format!("{tls12:?}"),
509            "Tls12Session { suite: Tls12CipherSuite { suite: 0xff12, .. }, session_id: , extended_ms: true, common: ClientSessionCommon { ticket: , epoch: 1, lifetime: 2s, peer_identity: VerifiedIdentity(RawPublicKey(SubjectPublicKeyInfoDer(0x73706b69))) }, .. }"
510        );
511
512        let tls13 = Tls13Session::new(
513            &NewSessionTicketPayloadTls13::new(
514                Duration::from_secs(2),
515                3,
516                [4u8; 32],
517                Vec::from([5]),
518            ),
519            Tls13ClientSessionInput {
520                suite: Tls13ProtocolSuite::Tcp(TEST_PROVIDER.tls13_cipher_suites[0]),
521                peer_identity: VerifiedIdentity::assertion(Identity::RawPublicKey(
522                    SubjectPublicKeyInfoDer::from(&b"spki"[..]),
523                )),
524                quic_params: None,
525            },
526            &[0xa5; 32],
527            UnixTime::since_unix_epoch(Duration::from_secs(1)),
528        );
529        assert_eq!(
530            format!("{tls13:?}"),
531            "Tls13Session { suite: Tcp(Tls13CipherSuite { suite: 0xff13, .. }), age_add: 3, max_early_data_size: 0, common: ClientSessionCommon { ticket: 05, epoch: 1, lifetime: 2s, peer_identity: VerifiedIdentity(RawPublicKey(SubjectPublicKeyInfoDer(0x73706b69))) }, quic_params: , .. }"
532        );
533    }
534}