Skip to main content

rustls/client/
config.rs

1use alloc::vec::Vec;
2use core::any::Any;
3use core::fmt;
4use core::hash::{Hash, Hasher};
5use core::marker::PhantomData;
6
7#[cfg(feature = "webpki")]
8use pki_types::PrivateKeyDer;
9use pki_types::{FipsStatus, ServerName, UnixTime};
10
11use super::ech::EchMode;
12#[cfg(feature = "std")]
13use super::handy::ClientSessionMemoryCache;
14use super::handy::{FailResolveClientCert, NoClientSessionStorage};
15use super::{Tls12ClientSessionValue, Tls13ClientSessionValue};
16use crate::builder::{ConfigBuilder, WantsVerifier};
17#[cfg(feature = "std")]
18use crate::client::connection::ClientConnectionBuilder;
19#[cfg(doc)]
20use crate::crypto;
21use crate::crypto::kx::NamedGroup;
22use crate::crypto::{CipherSuite, CryptoProvider, SelectedCredential, SignatureScheme, hash};
23#[cfg(feature = "webpki")]
24use crate::crypto::{Credentials, Identity, SingleCredential};
25use crate::enums::{ApplicationProtocol, CertificateType, ProtocolVersion};
26use crate::error::{ApiMisuse, Error};
27use crate::key_log::NoKeyLog;
28use crate::suites::SupportedCipherSuite;
29use crate::sync::Arc;
30#[cfg(feature = "std")]
31use crate::time_provider::DefaultTimeProvider;
32use crate::time_provider::TimeProvider;
33#[cfg(feature = "webpki")]
34use crate::webpki::{self, WebPkiServerVerifier};
35use crate::{DistinguishedName, DynHasher, KeyLog, compress, verify};
36
37/// Common configuration for (typically) all connections made by a program.
38///
39/// Making one of these is cheap, though one of the inputs may be expensive: gathering trust roots
40/// from the operating system to add to the [`RootCertStore`] passed to `with_root_certificates()`
41/// (the rustls-native-certs crate is often used for this) may take on the order of a few hundred
42/// milliseconds.
43///
44/// These must be created via the [`ClientConfig::builder()`] or [`ClientConfig::builder()`]
45/// function.
46///
47/// Note that using [`ConfigBuilder<ClientConfig, WantsVersions>::with_ech()`] will produce a common
48/// configuration specific to the provided [`crate::client::EchConfig`] that may not be appropriate
49/// for all connections made by the program. In this case the configuration should only be shared
50/// by connections intended for domains that offer the provided [`crate::client::EchConfig`] in
51/// their DNS zone.
52///
53/// # Defaults
54///
55/// * [`ClientConfig::max_fragment_size`]: the default is `None` (meaning 16kB).
56/// * [`ClientConfig::resumption`]: supports resumption with up to 256 server names, using session
57///   ids or tickets, with a max of eight tickets per server.
58/// * [`ClientConfig::alpn_protocols`]: the default is empty -- no ALPN protocol is negotiated.
59/// * [`ClientConfig::key_log`]: key material is not logged.
60/// * [`ClientConfig::cert_decompressors`]: depends on the crate features, see [`compress::default_cert_decompressors()`].
61/// * [`ClientConfig::cert_compressors`]: depends on the crate features, see [`compress::default_cert_compressors()`].
62/// * [`ClientConfig::cert_compression_cache`]: caches the most recently used 4 compressions
63///
64/// [`RootCertStore`]: crate::RootCertStore
65#[derive(Clone, Debug)]
66pub struct ClientConfig {
67    /// Which ALPN protocols we include in our client hello.
68    /// If empty, no ALPN extension is sent.
69    pub alpn_protocols: Vec<ApplicationProtocol<'static>>,
70
71    /// How and when the client can resume a previous session.
72    ///
73    /// # Sharing `resumption` between `ClientConfig`s
74    /// In a program using many `ClientConfig`s it may improve resumption rates
75    /// (which has a significant impact on connection performance) if those
76    /// configs share a single `Resumption`.
77    ///
78    /// However, resumption is only allowed between two `ClientConfig`s if their
79    /// `client_auth_cert_resolver` (ie, potential client authentication credentials)
80    /// and `verifier` (ie, server certificate verification settings):
81    ///
82    /// - are the same type (determined by hashing their `TypeId`), and
83    /// - input the same data into [`ServerVerifier::hash_config()`] and
84    ///   [`ClientCredentialResolver::hash_config()`].
85    ///
86    /// To illustrate, imagine two `ClientConfig`s `A` and `B`.  `A` fully validates
87    /// the server certificate, `B` does not.  If `A` and `B` shared a resumption store,
88    /// it would be possible for a session originated by `B` to be inserted into the
89    /// store, and then resumed by `A`.  This would give a false impression to the user
90    /// of `A` that the server certificate is fully validated.
91    ///
92    /// [`ServerVerifier::hash_config()`]: verify::ServerVerifier::hash_config()
93    pub resumption: Resumption,
94
95    /// The maximum size of plaintext input to be emitted in a single TLS record.
96    /// A value of None is equivalent to the [TLS maximum] of 16 kB.
97    ///
98    /// rustls enforces an arbitrary minimum of 32 bytes for this field.
99    /// Out of range values are reported as errors when initializing a connection.
100    ///
101    /// Setting this value to a little less than the TCP MSS may improve latency
102    /// for stream-y workloads.
103    ///
104    /// [TLS maximum]: https://datatracker.ietf.org/doc/html/rfc8446#section-5.1
105    pub max_fragment_size: Option<usize>,
106
107    /// Whether to send the Server Name Indication (SNI) extension
108    /// during the client handshake.
109    ///
110    /// The default is true.
111    pub enable_sni: bool,
112
113    /// How to output key material for debugging.  The default
114    /// does nothing.
115    pub key_log: Arc<dyn KeyLog>,
116
117    /// Allows traffic secrets to be extracted after the handshake,
118    /// e.g. for kTLS setup.
119    pub enable_secret_extraction: bool,
120
121    /// Whether to send data on the first flight ("early data") in
122    /// TLS 1.3 handshakes.
123    ///
124    /// The default is false.
125    pub enable_early_data: bool,
126
127    /// If set to `true`, requires the server to support the extended
128    /// master secret extraction method defined in [RFC 7627].
129    ///
130    /// The default is `true` if the configured [`CryptoProvider`] is FIPS-compliant,
131    /// false otherwise.
132    ///
133    /// It must be set to `true` to meet FIPS requirement mentioned in section
134    /// **D.Q Transition of the TLS 1.2 KDF to Support the Extended Master
135    /// Secret** from [FIPS 140-3 IG.pdf].
136    ///
137    /// [RFC 7627]: https://datatracker.ietf.org/doc/html/rfc7627
138    /// [FIPS 140-3 IG.pdf]: https://csrc.nist.gov/csrc/media/Projects/cryptographic-module-validation-program/documents/fips%20140-3/FIPS%20140-3%20IG.pdf
139    pub require_ems: bool,
140
141    /// Items that affect the fundamental security properties of a connection.
142    pub(super) domain: SecurityDomain,
143
144    /// How to decompress the server's certificate chain.
145    ///
146    /// If this is non-empty, the [RFC8779] certificate compression
147    /// extension is offered, and any compressed certificates are
148    /// transparently decompressed during the handshake.
149    ///
150    /// This only applies to TLS1.3 connections.  It is ignored for
151    /// TLS1.2 connections.
152    ///
153    /// [RFC8779]: https://datatracker.ietf.org/doc/rfc8879/
154    pub cert_decompressors: Vec<&'static dyn compress::CertDecompressor>,
155
156    /// How to compress the client's certificate chain.
157    ///
158    /// If a server supports this extension, and advertises support
159    /// for one of the compression algorithms included here, the
160    /// client certificate will be compressed according to [RFC8779].
161    ///
162    /// This only applies to TLS1.3 connections.  It is ignored for
163    /// TLS1.2 connections.
164    ///
165    /// [RFC8779]: https://datatracker.ietf.org/doc/rfc8879/
166    pub cert_compressors: Vec<&'static dyn compress::CertCompressor>,
167
168    /// Caching for compressed certificates.
169    ///
170    /// This is optional: [`compress::CompressionCache::Disabled`] gives
171    /// a cache that does no caching.
172    pub cert_compression_cache: Arc<compress::CompressionCache>,
173
174    /// How to offer Encrypted Client Hello (ECH). The default is to not offer ECH.
175    pub(super) ech_mode: Option<EchMode>,
176}
177
178impl ClientConfig {
179    /// Create a builder for a client configuration with a specific [`CryptoProvider`].
180    ///
181    /// This will use the provider's configured ciphersuites.
182    ///
183    /// For more information, see the [`ConfigBuilder`] documentation.
184    #[cfg(feature = "std")]
185    pub fn builder(provider: Arc<CryptoProvider>) -> ConfigBuilder<Self, WantsVerifier> {
186        Self::builder_with_details(provider, Arc::new(DefaultTimeProvider))
187    }
188
189    /// Create a builder for a client configuration with no default implementation details.
190    ///
191    /// This API must be used by `no_std` users.
192    ///
193    /// You must provide a specific [`TimeProvider`].
194    ///
195    /// You must provide a specific [`CryptoProvider`].
196    ///
197    /// For more information, see the [`ConfigBuilder`] documentation.
198    pub fn builder_with_details(
199        provider: Arc<CryptoProvider>,
200        time_provider: Arc<dyn TimeProvider>,
201    ) -> ConfigBuilder<Self, WantsVerifier> {
202        ConfigBuilder {
203            state: WantsVerifier {
204                client_ech_mode: None,
205            },
206            provider,
207            time_provider,
208            side: PhantomData,
209        }
210    }
211
212    /// Create a new client connection builder for the given server name.
213    ///
214    /// The `ClientConfig` controls how the client behaves;
215    /// `name` is the name of server we want to talk to.
216    #[cfg(feature = "std")]
217    pub fn connect(self: &Arc<Self>, server_name: ServerName<'static>) -> ClientConnectionBuilder {
218        ClientConnectionBuilder {
219            config: self.clone(),
220            name: server_name,
221            alpn_protocols: None,
222        }
223    }
224
225    /// Access configuration options whose use is dangerous and requires
226    /// extra care.
227    pub fn dangerous(&mut self) -> danger::DangerousClientConfig<'_> {
228        danger::DangerousClientConfig { cfg: self }
229    }
230
231    /// Return the FIPS validation status for connections made with this configuration.
232    ///
233    /// This is different from [`CryptoProvider::fips()`]: [`CryptoProvider::fips()`]
234    /// is concerned only with cryptography, whereas this _also_ covers TLS-level
235    /// configuration that NIST recommends, as well as ECH HPKE suites if applicable.
236    pub fn fips(&self) -> FipsStatus {
237        if !self.require_ems {
238            return FipsStatus::Unvalidated;
239        }
240
241        let status = self.domain.provider.fips();
242        match &self.ech_mode {
243            Some(ech) => Ord::min(status, ech.fips()),
244            None => status,
245        }
246    }
247
248    /// Return the crypto provider used to construct this client configuration.
249    pub fn provider(&self) -> &Arc<CryptoProvider> {
250        &self.domain.provider
251    }
252
253    /// Return the resolver for this client configuration.
254    ///
255    /// This is the object that determines which credentials to use for client
256    /// authentication.
257    pub fn resolver(&self) -> &Arc<dyn ClientCredentialResolver> {
258        &self.domain.client_auth_cert_resolver
259    }
260
261    /// Return the resolver for this client configuration.
262    ///
263    /// This is the object that determines which credentials to use for client
264    /// authentication.
265    pub fn verifier(&self) -> &Arc<dyn verify::ServerVerifier> {
266        &self.domain.verifier
267    }
268
269    pub(crate) fn supports_version(&self, v: ProtocolVersion) -> bool {
270        self.domain.provider.supports_version(v)
271    }
272
273    pub(super) fn find_cipher_suite(&self, suite: CipherSuite) -> Option<SupportedCipherSuite> {
274        self.domain
275            .provider
276            .iter_cipher_suites()
277            .find(|&scs| scs.suite() == suite)
278    }
279
280    pub(super) fn current_time(&self) -> Result<UnixTime, Error> {
281        self.domain
282            .time_provider
283            .current_time()
284            .ok_or(Error::FailedToGetCurrentTime)
285    }
286
287    /// A hash which partitions this config's use of the [`Self::resumption`] store.
288    pub(super) fn config_hash(&self) -> [u8; 32] {
289        self.domain.config_hash
290    }
291}
292
293struct HashAdapter<'a>(&'a mut dyn hash::Context);
294
295impl Hasher for HashAdapter<'_> {
296    fn finish(&self) -> u64 {
297        // SAFETY: this is private to `SecurityDomain::new`, which guarantees `hash::Output`
298        // is at least 32 bytes.
299        u64::from_be_bytes(
300            self.0.fork_finish().as_ref()[..8]
301                .try_into()
302                .unwrap(),
303        )
304    }
305
306    fn write(&mut self, bytes: &[u8]) {
307        self.0.update(bytes)
308    }
309}
310
311/// A trait for the ability to store client session data, so that sessions
312/// can be resumed in future connections.
313///
314/// Generally all data in this interface should be treated as
315/// **highly sensitive**, containing enough key material to break all security
316/// of the corresponding session.
317///
318/// `set_`, `insert_`, `remove_` and `take_` operations are mutating; this isn't
319/// expressed in the type system to allow implementations freedom in
320/// how to achieve interior mutability.  `Mutex` is a common choice.
321pub trait ClientSessionStore: fmt::Debug + Send + Sync {
322    /// Remember what `NamedGroup` the given server chose.
323    fn set_kx_hint(&self, key: ClientSessionKey<'static>, group: NamedGroup);
324
325    /// This should return the value most recently passed to `set_kx_hint`
326    /// for the given `key`.
327    ///
328    /// If `None` is returned, the caller chooses the first configured group,
329    /// and an extra round trip might happen if that choice is unsatisfactory
330    /// to the server.
331    fn kx_hint(&self, key: &ClientSessionKey<'_>) -> Option<NamedGroup>;
332
333    /// Remember a TLS1.2 session.
334    ///
335    /// At most one of these can be remembered at a time, per `server_name`.
336    fn set_tls12_session(&self, key: ClientSessionKey<'static>, value: Tls12ClientSessionValue);
337
338    /// Get the most recently saved TLS1.2 session for `server_name` provided to `set_tls12_session`.
339    fn tls12_session(&self, key: &ClientSessionKey<'_>) -> Option<Tls12ClientSessionValue>;
340
341    /// Remove and forget any saved TLS1.2 session for `server_name`.
342    fn remove_tls12_session(&self, key: &ClientSessionKey<'static>);
343
344    /// Remember a TLS1.3 ticket that might be retrieved later from `take_tls13_ticket`, allowing
345    /// resumption of this session.
346    ///
347    /// This can be called multiple times for a given session, allowing multiple independent tickets
348    /// to be valid at once.  The number of times this is called is controlled by the server, so
349    /// implementations of this trait should apply a reasonable bound of how many items are stored
350    /// simultaneously.
351    fn insert_tls13_ticket(&self, key: ClientSessionKey<'static>, value: Tls13ClientSessionValue);
352
353    /// Return a TLS1.3 ticket previously provided to `add_tls13_ticket`.
354    ///
355    /// Implementations of this trait must return each value provided to `add_tls13_ticket` _at most once_.
356    fn take_tls13_ticket(&self, key: &ClientSessionKey<'static>)
357    -> Option<Tls13ClientSessionValue>;
358}
359
360/// Identifies a security context and server in the [`ClientSessionStore`] interface.
361#[derive(Clone, Debug, Eq, Hash, PartialEq)]
362#[non_exhaustive]
363pub struct ClientSessionKey<'a> {
364    /// A hash to partition the client storage between different security domains.
365    pub config_hash: [u8; 32],
366
367    /// Transport-level identity of the server.
368    pub server_name: ServerName<'a>,
369}
370
371impl ClientSessionKey<'_> {
372    /// Copy the value to own its contents.
373    pub fn to_owned(&self) -> ClientSessionKey<'static> {
374        let Self {
375            config_hash,
376            server_name,
377        } = self;
378        ClientSessionKey {
379            config_hash: *config_hash,
380            server_name: server_name.to_owned(),
381        }
382    }
383}
384
385/// A trait for the ability to choose a certificate chain and
386/// private key for the purposes of client authentication.
387pub trait ClientCredentialResolver: fmt::Debug + Send + Sync {
388    /// Resolve a client certificate chain/private key to use as the client's identity.
389    ///
390    /// The `SelectedCredential` returned from this method contains an identity and a
391    /// one-time-use [`Signer`] wrapping the private key. This is usually obtained via a
392    /// [`Credentials`], on which an implementation can call [`Credentials::signer()`].
393    /// An implementation can either store long-lived [`Credentials`] values, or instantiate
394    /// them as needed using one of its constructors.
395    ///
396    /// Return `None` to continue the handshake without any client
397    /// authentication.  The server may reject the handshake later
398    /// if it requires authentication.
399    ///
400    /// [RFC 5280 A.1]: https://www.rfc-editor.org/rfc/rfc5280#appendix-A.1
401    ///
402    /// [`Credentials`]: crate::crypto::Credentials
403    /// [`Credentials::signer()`]: crate::crypto::Credentials::signer
404    /// [`Signer`]: crate::crypto::Signer
405    fn resolve(&self, request: &CredentialRequest<'_>) -> Option<SelectedCredential>;
406
407    /// Returns which [`CertificateType`]s this resolver supports.
408    ///
409    /// Should return the empty slice if the resolver does not have any credentials to send.
410    /// Implementations should return the same value every time.
411    ///
412    /// See [RFC 7250](https://tools.ietf.org/html/rfc7250) for more information.
413    fn supported_certificate_types(&self) -> &'static [CertificateType];
414
415    /// Instance configuration should be input to `h`.
416    fn hash_config(&self, h: &mut dyn Hasher);
417}
418
419/// Context from the server to inform client credential selection.
420pub struct CredentialRequest<'a> {
421    pub(super) negotiated_type: CertificateType,
422    pub(super) root_hint_subjects: &'a [DistinguishedName],
423    pub(super) signature_schemes: &'a [SignatureScheme],
424}
425
426impl CredentialRequest<'_> {
427    /// List of certificate authority subject distinguished names provided by the server.
428    ///
429    /// If the list is empty, the client should send whatever certificate it has. The hints
430    /// are expected to be DER-encoded X.500 distinguished names, per [RFC 5280 A.1]. Note that
431    /// the encoding comes from the server and has not been validated by rustls.
432    ///
433    /// See [`DistinguishedName`] for more information on decoding with external crates like
434    /// `x509-parser`.
435    ///
436    /// [`DistinguishedName`]: crate::DistinguishedName
437    pub fn root_hint_subjects(&self) -> &[DistinguishedName] {
438        self.root_hint_subjects
439    }
440
441    /// Get the compatible signature schemes.
442    pub fn signature_schemes(&self) -> &[SignatureScheme] {
443        self.signature_schemes
444    }
445
446    /// The negotiated certificate type.
447    ///
448    /// If the server does not support [RFC 7250], this will be `CertificateType::X509`.
449    ///
450    /// [RFC 7250]: https://tools.ietf.org/html/rfc7250
451    pub fn negotiated_type(&self) -> CertificateType {
452        self.negotiated_type
453    }
454}
455
456/// Items that affect the fundamental security properties of a connection.
457///
458/// This is its own type because `config_hash` depends on the other fields:
459/// fields therefore should not be mutated, but an entire object created
460/// through [`Self::new`] for any edits.
461#[derive(Clone, Debug)]
462pub(super) struct SecurityDomain {
463    /// Provides the current system time
464    time_provider: Arc<dyn TimeProvider>,
465
466    /// Source of randomness and other crypto.
467    provider: Arc<CryptoProvider>,
468
469    /// How to verify the server certificate chain.
470    verifier: Arc<dyn verify::ServerVerifier>,
471
472    /// How to decide what client auth certificate/keys to use.
473    client_auth_cert_resolver: Arc<dyn ClientCredentialResolver>,
474
475    config_hash: [u8; 32],
476}
477
478impl SecurityDomain {
479    pub(crate) fn new(
480        provider: Arc<CryptoProvider>,
481        client_auth_cert_resolver: Arc<dyn ClientCredentialResolver + 'static>,
482        verifier: Arc<dyn verify::ServerVerifier + 'static>,
483        time_provider: Arc<dyn TimeProvider + 'static>,
484    ) -> Self {
485        // Use a hash function that outputs at least 32 bytes.
486        let hash = provider
487            .iter_cipher_suites()
488            .map(|cs| cs.hash_provider())
489            .find(|h| h.output_len() >= 32)
490            .expect("no suitable cipher suite available (with |H| >= 32)"); // this is -- in practice -- all cipher suites
491
492        let mut h = hash.start();
493        let mut adapter = HashAdapter(h.as_mut());
494
495        // Include TypeId of impl, so two different types with different non-configured
496        // behavior do not collide even if their `hash_config()`s are the same.
497        client_auth_cert_resolver
498            .type_id()
499            .hash(&mut DynHasher(&mut adapter));
500        client_auth_cert_resolver.hash_config(&mut adapter);
501
502        verifier
503            .type_id()
504            .hash(&mut DynHasher(&mut adapter));
505        verifier.hash_config(&mut adapter);
506
507        time_provider
508            .type_id()
509            .hash(&mut DynHasher(&mut adapter));
510
511        let config_hash = h.finish().as_ref()[..32]
512            .try_into()
513            .unwrap();
514
515        Self {
516            time_provider,
517            provider,
518            verifier,
519            client_auth_cert_resolver,
520            config_hash,
521        }
522    }
523
524    fn with_verifier(&self, verifier: Arc<dyn verify::ServerVerifier + 'static>) -> Self {
525        let Self {
526            time_provider,
527            provider,
528            verifier: _,
529            client_auth_cert_resolver,
530            config_hash: _,
531        } = self;
532        Self::new(
533            provider.clone(),
534            client_auth_cert_resolver.clone(),
535            verifier,
536            time_provider.clone(),
537        )
538    }
539}
540
541/// Configuration for how/when a client is allowed to resume a previous session.
542#[derive(Clone, Debug)]
543pub struct Resumption {
544    /// How we store session data or tickets. The default is to use an in-memory
545    /// [super::handy::ClientSessionMemoryCache].
546    pub(super) store: Arc<dyn ClientSessionStore>,
547
548    /// What mechanism is used for resuming a TLS 1.2 session.
549    pub(super) tls12_resumption: Tls12Resumption,
550}
551
552impl Resumption {
553    /// Create a new `Resumption` that stores data for the given number of sessions in memory.
554    ///
555    /// This is the default `Resumption` choice, and enables resuming a TLS 1.2 session with
556    /// a session id or RFC 5077 ticket.
557    #[cfg(feature = "std")]
558    pub fn in_memory_sessions(num: usize) -> Self {
559        Self {
560            store: Arc::new(ClientSessionMemoryCache::new(num)),
561            tls12_resumption: Tls12Resumption::SessionIdOrTickets,
562        }
563    }
564
565    /// Use a custom [`ClientSessionStore`] implementation to store sessions.
566    ///
567    /// By default, enables resuming a TLS 1.2 session with a session id or RFC 5077 ticket.
568    pub fn store(store: Arc<dyn ClientSessionStore>) -> Self {
569        Self {
570            store,
571            tls12_resumption: Tls12Resumption::SessionIdOrTickets,
572        }
573    }
574
575    /// Disable all use of session resumption.
576    pub fn disabled() -> Self {
577        Self {
578            store: Arc::new(NoClientSessionStorage),
579            tls12_resumption: Tls12Resumption::Disabled,
580        }
581    }
582
583    /// Configure whether TLS 1.2 sessions may be resumed, and by what mechanism.
584    ///
585    /// This is meaningless if you've disabled resumption entirely, which is the case in `no-std`
586    /// contexts.
587    pub fn tls12_resumption(mut self, tls12: Tls12Resumption) -> Self {
588        self.tls12_resumption = tls12;
589        self
590    }
591}
592
593impl Default for Resumption {
594    /// Create an in-memory session store resumption with up to 256 server names, allowing
595    /// a TLS 1.2 session to resume with a session id or RFC 5077 ticket.
596    fn default() -> Self {
597        #[cfg(feature = "std")]
598        let ret = Self::in_memory_sessions(256);
599
600        #[cfg(not(feature = "std"))]
601        let ret = Self::disabled();
602
603        ret
604    }
605}
606
607/// What mechanisms to support for resuming a TLS 1.2 session.
608#[non_exhaustive]
609#[derive(Clone, Copy, Debug, PartialEq)]
610pub enum Tls12Resumption {
611    /// Disable 1.2 resumption.
612    Disabled,
613    /// Support 1.2 resumption using session ids only.
614    SessionIdOnly,
615    /// Support 1.2 resumption using session ids or RFC 5077 tickets.
616    ///
617    /// See[^1] for why you might like to disable RFC 5077 by instead choosing the `SessionIdOnly`
618    /// option. Note that TLS 1.3 tickets do not have those issues.
619    ///
620    /// [^1]: <https://words.filippo.io/we-need-to-talk-about-session-tickets/>
621    SessionIdOrTickets,
622}
623
624impl ConfigBuilder<ClientConfig, WantsVerifier> {
625    /// Choose how to verify server certificates.
626    ///
627    /// Using this function does not configure revocation.  If you wish to
628    /// configure revocation, instead use:
629    ///
630    /// ```diff
631    /// - .with_root_certificates(root_store)
632    /// + .with_webpki_verifier(
633    /// +   WebPkiServerVerifier::builder(root_store, crypto_provider)
634    /// +   .with_crls(...)
635    /// +   .build()?
636    /// + )
637    /// ```
638    #[cfg(feature = "webpki")]
639    pub fn with_root_certificates(
640        self,
641        root_store: impl Into<Arc<webpki::RootCertStore>>,
642    ) -> ConfigBuilder<ClientConfig, WantsClientCert> {
643        let algorithms = self
644            .provider
645            .signature_verification_algorithms;
646        self.with_webpki_verifier(
647            WebPkiServerVerifier::new_without_revocation(root_store, algorithms).into(),
648        )
649    }
650
651    /// Choose how to verify server certificates using a webpki verifier.
652    ///
653    /// See [`webpki::WebPkiServerVerifier::builder`] and
654    /// [`webpki::WebPkiServerVerifier::builder`] for more information.
655    #[cfg(feature = "webpki")]
656    pub fn with_webpki_verifier(
657        self,
658        verifier: Arc<WebPkiServerVerifier>,
659    ) -> ConfigBuilder<ClientConfig, WantsClientCert> {
660        ConfigBuilder {
661            state: WantsClientCert {
662                verifier,
663                client_ech_mode: self.state.client_ech_mode,
664            },
665            provider: self.provider,
666            time_provider: self.time_provider,
667            side: PhantomData,
668        }
669    }
670
671    /// Enable Encrypted Client Hello (ECH) in the given mode.
672    ///
673    /// This requires TLS 1.3 as the only supported protocol version to meet the requirement
674    /// to support ECH.  At the end, the config building process will return an error if either
675    /// TLS1.3 _is not_ supported by the provider, or TLS1.2 _is_ supported.
676    ///
677    /// The `ClientConfig` that will be produced by this builder will be specific to the provided
678    /// [`crate::client::EchConfig`] and may not be appropriate for all connections made by the program.
679    /// In this case the configuration should only be shared by connections intended for domains
680    /// that offer the provided [`crate::client::EchConfig`] in their DNS zone.
681    pub fn with_ech(mut self, mode: EchMode) -> Self {
682        self.state.client_ech_mode = Some(mode);
683        self
684    }
685
686    /// Access configuration options whose use is dangerous and requires
687    /// extra care.
688    pub fn dangerous(self) -> danger::DangerousClientConfigBuilder {
689        danger::DangerousClientConfigBuilder { cfg: self }
690    }
691}
692
693/// A config builder state where the caller needs to supply whether and how to provide a client
694/// certificate.
695///
696/// For more information, see the [`ConfigBuilder`] documentation.
697#[derive(Clone)]
698pub struct WantsClientCert {
699    verifier: Arc<dyn verify::ServerVerifier>,
700    client_ech_mode: Option<EchMode>,
701}
702
703impl ConfigBuilder<ClientConfig, WantsClientCert> {
704    /// Sets a single certificate chain and matching private key for use
705    /// in client authentication.
706    ///
707    /// `cert_chain` is a vector of DER-encoded certificates.
708    /// `key_der` is a DER-encoded private key as PKCS#1, PKCS#8, or SEC1. The
709    /// `aws-lc-rs` and `ring` [`CryptoProvider`]s support
710    /// all three encodings, but other `CryptoProviders` may not.
711    ///
712    /// This function fails if `key_der` is invalid.
713    #[cfg(feature = "webpki")]
714    pub fn with_client_auth_cert(
715        self,
716        identity: Arc<Identity<'static>>,
717        key_der: PrivateKeyDer<'static>,
718    ) -> Result<ClientConfig, Error> {
719        let credentials = Credentials::from_der(identity, key_der, &self.provider)?;
720        self.with_client_credential_resolver(Arc::new(SingleCredential::from(credentials)))
721    }
722
723    /// Do not support client auth.
724    pub fn with_no_client_auth(self) -> Result<ClientConfig, Error> {
725        self.with_client_credential_resolver(Arc::new(FailResolveClientCert {}))
726    }
727
728    /// Sets a custom [`ClientCredentialResolver`].
729    pub fn with_client_credential_resolver(
730        self,
731        client_auth_cert_resolver: Arc<dyn ClientCredentialResolver>,
732    ) -> Result<ClientConfig, Error> {
733        self.provider.consistency_check()?;
734
735        if self.state.client_ech_mode.is_some() {
736            match (
737                self.provider
738                    .tls12_cipher_suites
739                    .is_empty(),
740                self.provider
741                    .tls13_cipher_suites
742                    .is_empty(),
743            ) {
744                (_, true) => return Err(ApiMisuse::EchRequiresTls13Support.into()),
745                (false, _) => return Err(ApiMisuse::EchForbidsTls12Support.into()),
746                (true, false) => {}
747            };
748        }
749
750        let require_ems = !matches!(self.provider.fips(), FipsStatus::Unvalidated);
751        Ok(ClientConfig {
752            alpn_protocols: Vec::new(),
753            resumption: Resumption::default(),
754            max_fragment_size: None,
755            enable_sni: true,
756            key_log: Arc::new(NoKeyLog {}),
757            enable_secret_extraction: false,
758            enable_early_data: false,
759            require_ems,
760            domain: SecurityDomain::new(
761                self.provider,
762                client_auth_cert_resolver,
763                self.state.verifier,
764                self.time_provider,
765            ),
766            cert_decompressors: compress::default_cert_decompressors().to_vec(),
767            cert_compressors: compress::default_cert_compressors().to_vec(),
768            cert_compression_cache: Arc::new(compress::CompressionCache::default()),
769            ech_mode: self.state.client_ech_mode,
770        })
771    }
772}
773
774/// Container for unsafe APIs
775pub(super) mod danger {
776    use core::marker::PhantomData;
777
778    use crate::client::WantsClientCert;
779    use crate::client::config::ClientConfig;
780    use crate::sync::Arc;
781    use crate::verify::ServerVerifier;
782    use crate::{ConfigBuilder, WantsVerifier};
783
784    /// Accessor for dangerous configuration options.
785    #[derive(Debug)]
786    pub struct DangerousClientConfig<'a> {
787        /// The underlying ClientConfig
788        pub(super) cfg: &'a mut ClientConfig,
789    }
790
791    impl DangerousClientConfig<'_> {
792        /// Overrides the default `ServerVerifier` with something else.
793        pub fn set_certificate_verifier(&mut self, verifier: Arc<dyn ServerVerifier>) {
794            self.cfg.domain = self.cfg.domain.with_verifier(verifier);
795        }
796    }
797
798    /// Accessor for dangerous configuration options.
799    #[derive(Debug)]
800    pub struct DangerousClientConfigBuilder {
801        /// The underlying ClientConfigBuilder
802        pub(super) cfg: ConfigBuilder<ClientConfig, WantsVerifier>,
803    }
804
805    impl DangerousClientConfigBuilder {
806        /// Set a custom certificate verifier.
807        pub fn with_custom_certificate_verifier(
808            self,
809            verifier: Arc<dyn ServerVerifier>,
810        ) -> ConfigBuilder<ClientConfig, WantsClientCert> {
811            ConfigBuilder {
812                state: WantsClientCert {
813                    verifier,
814                    client_ech_mode: self.cfg.state.client_ech_mode,
815                },
816                provider: self.cfg.provider,
817                time_provider: self.cfg.time_provider,
818                side: PhantomData,
819            }
820        }
821    }
822}