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