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}