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
400    /// authentication.  The server may reject the handshake later
401    /// if it requires authentication.
402    ///
403    /// [RFC 5280 A.1]: https://www.rfc-editor.org/rfc/rfc5280#appendix-A.1
404    ///
405    /// [`Credentials`]: crate::crypto::Credentials
406    /// [`Credentials::signer()`]: crate::crypto::Credentials::signer
407    /// [`Signer`]: crate::crypto::Signer
408    fn resolve(&self, request: &CredentialRequest<'_>) -> Option<SelectedCredential>;
409
410    /// Returns which [`CertificateType`]s this resolver supports.
411    ///
412    /// Should return the empty slice if the resolver does not have any credentials to send.
413    /// Implementations should return the same value every time.
414    ///
415    /// See [RFC 7250](https://tools.ietf.org/html/rfc7250) for more information.
416    fn supported_certificate_types(&self) -> &'static [CertificateType];
417
418    /// Instance configuration should be input to `h`.
419    fn hash_config(&self, h: &mut dyn Hasher);
420}
421
422/// Context from the server to inform client credential selection.
423pub struct CredentialRequest<'a> {
424    pub(super) negotiated_type: CertificateType,
425    pub(super) root_hint_subjects: &'a [DistinguishedName],
426    pub(super) signature_schemes: &'a [SignatureScheme],
427}
428
429impl CredentialRequest<'_> {
430    /// List of certificate authority subject distinguished names provided by the server.
431    ///
432    /// If the list is empty, the client should send whatever certificate it has. The hints
433    /// are expected to be DER-encoded X.500 distinguished names, per [RFC 5280 A.1]. Note that
434    /// the encoding comes from the server and has not been validated by rustls.
435    ///
436    /// See [`DistinguishedName`] for more information on decoding with external crates like
437    /// `x509-parser`.
438    ///
439    /// [`DistinguishedName`]: crate::DistinguishedName
440    pub fn root_hint_subjects(&self) -> &[DistinguishedName] {
441        self.root_hint_subjects
442    }
443
444    /// Get the compatible signature schemes.
445    pub fn signature_schemes(&self) -> &[SignatureScheme] {
446        self.signature_schemes
447    }
448
449    /// The negotiated certificate type.
450    ///
451    /// If the server does not support [RFC 7250], this will be `CertificateType::X509`.
452    ///
453    /// [RFC 7250]: https://tools.ietf.org/html/rfc7250
454    pub fn negotiated_type(&self) -> CertificateType {
455        self.negotiated_type
456    }
457}
458
459/// Items that affect the fundamental security properties of a connection.
460///
461/// This is its own type because `config_hash` depends on the other fields:
462/// fields therefore should not be mutated, but an entire object created
463/// through [`Self::new`] for any edits.
464#[derive(Clone, Debug)]
465pub(super) struct SecurityDomain {
466    /// Provides the current system time
467    time_provider: Arc<dyn TimeProvider>,
468
469    /// Source of randomness and other crypto.
470    provider: Arc<CryptoProvider>,
471
472    /// How to verify the server certificate chain.
473    verifier: Arc<dyn ServerVerifier>,
474
475    /// How to decide what client auth certificate/keys to use.
476    client_auth_cert_resolver: Arc<dyn ClientCredentialResolver>,
477
478    config_hash: [u8; 32],
479}
480
481impl SecurityDomain {
482    pub(crate) fn new(
483        provider: Arc<CryptoProvider>,
484        client_auth_cert_resolver: Arc<dyn ClientCredentialResolver + 'static>,
485        verifier: Arc<dyn ServerVerifier + 'static>,
486        time_provider: Arc<dyn TimeProvider + 'static>,
487    ) -> Self {
488        // Use a hash function that outputs at least 32 bytes.
489        let hash = provider
490            .iter_cipher_suites()
491            .map(|cs| cs.hash_provider())
492            .find(|h| h.output_len() >= 32)
493            .expect("no suitable cipher suite available (with |H| >= 32)"); // this is -- in practice -- all cipher suites
494
495        let mut h = hash.start();
496        let mut adapter = HashAdapter(h.as_mut());
497
498        // Include TypeId of impl, so two different types with different non-configured
499        // behavior do not collide even if their `hash_config()`s are the same.
500        client_auth_cert_resolver
501            .type_id()
502            .hash(&mut DynHasher(&mut adapter));
503        client_auth_cert_resolver.hash_config(&mut adapter);
504
505        verifier
506            .type_id()
507            .hash(&mut DynHasher(&mut adapter));
508        verifier.hash_config(&mut adapter);
509
510        time_provider
511            .type_id()
512            .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    pub fn in_memory_sessions(num: usize) -> Self {
561        Self {
562            store: Arc::new(ClientSessionMemoryCache::new(num)),
563            tls12_resumption: Tls12Resumption::SessionIdOrTickets,
564        }
565    }
566
567    /// Use a custom [`ClientSessionStore`] implementation to store sessions.
568    ///
569    /// By default, enables resuming a TLS 1.2 session with a session id or RFC 5077 ticket.
570    pub fn store(store: Arc<dyn ClientSessionStore>) -> Self {
571        Self {
572            store,
573            tls12_resumption: Tls12Resumption::SessionIdOrTickets,
574        }
575    }
576
577    /// Disable all use of session resumption.
578    pub fn disabled() -> Self {
579        Self {
580            store: Arc::new(NoClientSessionStorage),
581            tls12_resumption: Tls12Resumption::Disabled,
582        }
583    }
584
585    /// Configure whether TLS 1.2 sessions may be resumed, and by what mechanism.
586    ///
587    /// This is meaningless if you've disabled resumption entirely, which is the case in `no-std`
588    /// contexts.
589    pub fn tls12_resumption(mut self, tls12: Tls12Resumption) -> Self {
590        self.tls12_resumption = tls12;
591        self
592    }
593}
594
595impl Default for Resumption {
596    /// Create an in-memory session store resumption with up to 256 server names, allowing
597    /// a TLS 1.2 session to resume with a session id or RFC 5077 ticket.
598    fn default() -> Self {
599        Self::in_memory_sessions(256)
600    }
601}
602
603/// What mechanisms to support for resuming a TLS 1.2 session.
604#[non_exhaustive]
605#[derive(Clone, Copy, Debug, PartialEq)]
606pub enum Tls12Resumption {
607    /// Disable 1.2 resumption.
608    Disabled,
609    /// Support 1.2 resumption using session ids only.
610    SessionIdOnly,
611    /// Support 1.2 resumption using session ids or RFC 5077 tickets.
612    ///
613    /// See[^1] for why you might like to disable RFC 5077 by instead choosing the `SessionIdOnly`
614    /// option. Note that TLS 1.3 tickets do not have those issues.
615    ///
616    /// [^1]: <https://words.filippo.io/we-need-to-talk-about-session-tickets/>
617    SessionIdOrTickets,
618}
619
620/// Number of TLS 1.3 session tickets to request via the [RFC 9149]
621/// `ticket_request` extension.
622///
623/// [RFC 9149]: https://datatracker.ietf.org/doc/html/rfc9149
624#[expect(clippy::exhaustive_structs)]
625#[derive(Clone, Copy, Debug, PartialEq)]
626pub struct TicketRequest {
627    /// Tickets desired when the server negotiates a new connection.
628    ///
629    /// RFC 9149 recommends setting this to the desired number of tickets
630    /// and `resumption_count` to 0 for initial connections.
631    pub new_session_count: u8,
632
633    /// Tickets desired when the server resumes using a presented ticket.
634    ///
635    /// A value of 1 is a good default for primed caches. Clients racing
636    /// multiple connections may want a higher value.
637    pub resumption_count: u8,
638}
639
640impl ConfigBuilder<ClientConfig, WantsVerifier> {
641    /// Choose how to verify server certificates.
642    ///
643    /// Using this function does not configure revocation.  If you wish to
644    /// configure revocation, instead use:
645    ///
646    /// ```diff
647    /// - .with_root_certificates(root_store)
648    /// + .with_webpki_verifier(
649    /// +   WebPkiServerVerifier::builder(root_store, crypto_provider)
650    /// +   .with_crls(...)
651    /// +   .build()?
652    /// + )
653    /// ```
654    #[cfg(feature = "webpki")]
655    pub fn with_root_certificates(
656        self,
657        root_store: impl Into<Arc<webpki::RootCertStore>>,
658    ) -> ConfigBuilder<ClientConfig, WantsClientCert> {
659        let algorithms = self
660            .provider
661            .signature_verification_algorithms;
662        self.with_webpki_verifier(
663            WebPkiServerVerifier::new_without_revocation(root_store, algorithms).into(),
664        )
665    }
666
667    /// Choose how to verify server certificates using a webpki verifier.
668    ///
669    /// See [`webpki::WebPkiServerVerifier::builder`] for more information.
670    #[cfg(feature = "webpki")]
671    pub fn with_webpki_verifier(
672        self,
673        verifier: Arc<WebPkiServerVerifier>,
674    ) -> ConfigBuilder<ClientConfig, WantsClientCert> {
675        ConfigBuilder {
676            state: WantsClientCert {
677                verifier,
678                client_ech_mode: self.state.client_ech_mode,
679            },
680            provider: self.provider,
681            time_provider: self.time_provider,
682            side: PhantomData,
683        }
684    }
685
686    /// Enable Encrypted Client Hello (ECH) in the given mode.
687    ///
688    /// This requires TLS 1.3 as the only supported protocol version to meet the requirement
689    /// to support ECH.  At the end, the config building process will return an error if either
690    /// TLS1.3 _is not_ supported by the provider, or TLS1.2 _is_ supported.
691    ///
692    /// The `ClientConfig` that will be produced by this builder will be specific to the provided
693    /// [`crate::client::EchConfig`] and may not be appropriate for all connections made by the program.
694    /// In this case the configuration should only be shared by connections intended for domains
695    /// that offer the provided [`crate::client::EchConfig`] in their DNS zone.
696    pub fn with_ech(mut self, mode: EchMode) -> Self {
697        self.state.client_ech_mode = Some(mode);
698        self
699    }
700
701    /// Access configuration options whose use is dangerous and requires
702    /// extra care.
703    pub fn dangerous(self) -> danger::DangerousClientConfigBuilder {
704        danger::DangerousClientConfigBuilder { cfg: self }
705    }
706}
707
708/// A config builder state where the caller needs to supply whether and how to provide a client
709/// certificate.
710///
711/// For more information, see the [`ConfigBuilder`] documentation.
712#[derive(Clone)]
713pub struct WantsClientCert {
714    verifier: Arc<dyn ServerVerifier>,
715    client_ech_mode: Option<EchMode>,
716}
717
718impl ConfigBuilder<ClientConfig, WantsClientCert> {
719    /// Sets a single certificate chain and matching private key for use
720    /// in client authentication.
721    ///
722    /// `cert_chain` is a vector of DER-encoded certificates.
723    /// `key_der` is a DER-encoded private key as PKCS#1, PKCS#8, or SEC1. The
724    /// `aws-lc-rs` and `ring` [`CryptoProvider`]s support
725    /// all three encodings, but other `CryptoProviders` may not.
726    ///
727    /// This function fails if `key_der` is invalid.
728    #[cfg(feature = "webpki")]
729    pub fn with_client_auth_cert(
730        self,
731        identity: Arc<Identity<'static>>,
732        key_der: PrivateKeyDer<'static>,
733    ) -> Result<ClientConfig, Error> {
734        let credentials = Credentials::from_der(identity, key_der, &self.provider)?;
735        self.with_client_credential_resolver(Arc::new(SingleCredential::from(credentials)))
736    }
737
738    /// Do not support client auth.
739    pub fn with_no_client_auth(self) -> Result<ClientConfig, Error> {
740        self.with_client_credential_resolver(Arc::new(FailResolveClientCert {}))
741    }
742
743    /// Sets a custom [`ClientCredentialResolver`].
744    pub fn with_client_credential_resolver(
745        self,
746        client_auth_cert_resolver: Arc<dyn ClientCredentialResolver>,
747    ) -> Result<ClientConfig, Error> {
748        self.provider.consistency_check()?;
749
750        if self.state.client_ech_mode.is_some() {
751            match (
752                self.provider
753                    .tls12_cipher_suites
754                    .is_empty(),
755                self.provider
756                    .tls13_cipher_suites
757                    .is_empty(),
758            ) {
759                (_, true) => return Err(ApiMisuse::EchRequiresTls13Support.into()),
760                (false, _) => return Err(ApiMisuse::EchForbidsTls12Support.into()),
761                (true, false) => {}
762            };
763        }
764
765        let require_ems = !matches!(self.provider.fips(), FipsStatus::Unvalidated);
766        Ok(ClientConfig {
767            alpn_protocols: Vec::new(),
768            check_selected_alpn: true,
769            resumption: Resumption::default(),
770            max_fragment_size: None,
771            enable_sni: true,
772            key_log: Arc::new(NoKeyLog {}),
773            enable_secret_extraction: false,
774            enable_early_data: false,
775            require_ems,
776            send_ticket_request: None,
777            domain: SecurityDomain::new(
778                self.provider,
779                client_auth_cert_resolver,
780                self.state.verifier,
781                self.time_provider,
782            ),
783            cert_decompressors: compress::default_cert_decompressors().to_vec(),
784            cert_compressors: compress::default_cert_compressors().to_vec(),
785            cert_compression_cache: Arc::new(compress::CompressionCache::default()),
786            ech_mode: self.state.client_ech_mode,
787        })
788    }
789}
790
791/// Container for unsafe APIs
792pub(super) mod danger {
793    use core::marker::PhantomData;
794
795    use crate::client::WantsClientCert;
796    use crate::client::config::ClientConfig;
797    use crate::sync::Arc;
798    use crate::verify::ServerVerifier;
799    use crate::{ConfigBuilder, WantsVerifier};
800
801    /// Accessor for dangerous configuration options.
802    #[derive(Debug)]
803    pub struct DangerousClientConfig<'a> {
804        /// The underlying ClientConfig
805        pub(super) cfg: &'a mut ClientConfig,
806    }
807
808    impl DangerousClientConfig<'_> {
809        /// Overrides the default `ServerVerifier` with something else.
810        pub fn set_certificate_verifier(&mut self, verifier: Arc<dyn ServerVerifier>) {
811            self.cfg.domain = self.cfg.domain.with_verifier(verifier);
812        }
813    }
814
815    /// Accessor for dangerous configuration options.
816    #[derive(Debug)]
817    pub struct DangerousClientConfigBuilder {
818        /// The underlying ClientConfigBuilder
819        pub(super) cfg: ConfigBuilder<ClientConfig, WantsVerifier>,
820    }
821
822    impl DangerousClientConfigBuilder {
823        /// Set a custom certificate verifier.
824        pub fn with_custom_certificate_verifier(
825            self,
826            verifier: Arc<dyn ServerVerifier>,
827        ) -> ConfigBuilder<ClientConfig, WantsClientCert> {
828            ConfigBuilder {
829                state: WantsClientCert {
830                    verifier,
831                    client_ech_mode: self.cfg.state.client_ech_mode,
832                },
833                provider: self.cfg.provider,
834                time_provider: self.cfg.time_provider,
835                side: PhantomData,
836            }
837        }
838    }
839}