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