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