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