rustls/client/
builder.rs

1use alloc::vec::Vec;
2use core::marker::PhantomData;
3
4use pki_types::PrivateKeyDer;
5
6use super::client_conn::Resumption;
7use crate::builder::{ConfigBuilder, WantsVerifier};
8use crate::client::{ClientConfig, ClientCredentialResolver, EchMode, handy};
9use crate::crypto::{Credentials, Identity, SingleCredential};
10use crate::error::{ApiMisuse, Error};
11use crate::key_log::NoKeyLog;
12use crate::sync::Arc;
13use crate::webpki::{self, WebPkiServerVerifier};
14use crate::{compress, verify};
15
16impl ConfigBuilder<ClientConfig, WantsVerifier> {
17    /// Enable Encrypted Client Hello (ECH) in the given mode.
18    ///
19    /// This requires TLS 1.3 as the only supported protocol version to meet the requirement
20    /// to support ECH.  At the end, the config building process will return an error if either
21    /// TLS1.3 _is not_ supported by the provider, or TLS1.2 _is_ supported.
22    ///
23    /// The `ClientConfig` that will be produced by this builder will be specific to the provided
24    /// [`crate::client::EchConfig`] and may not be appropriate for all connections made by the program.
25    /// In this case the configuration should only be shared by connections intended for domains
26    /// that offer the provided [`crate::client::EchConfig`] in their DNS zone.
27    pub fn with_ech(mut self, mode: EchMode) -> Self {
28        self.state.client_ech_mode = Some(mode);
29        self
30    }
31}
32
33impl ConfigBuilder<ClientConfig, WantsVerifier> {
34    /// Choose how to verify server certificates.
35    ///
36    /// Using this function does not configure revocation.  If you wish to
37    /// configure revocation, instead use:
38    ///
39    /// ```diff
40    /// - .with_root_certificates(root_store)
41    /// + .with_webpki_verifier(
42    /// +   WebPkiServerVerifier::builder_with_provider(root_store, crypto_provider)
43    /// +   .with_crls(...)
44    /// +   .build()?
45    /// + )
46    /// ```
47    pub fn with_root_certificates(
48        self,
49        root_store: impl Into<Arc<webpki::RootCertStore>>,
50    ) -> ConfigBuilder<ClientConfig, WantsClientCert> {
51        let algorithms = self
52            .provider
53            .signature_verification_algorithms;
54        self.with_webpki_verifier(
55            WebPkiServerVerifier::new_without_revocation(root_store, algorithms).into(),
56        )
57    }
58
59    /// Choose how to verify server certificates using a webpki verifier.
60    ///
61    /// See [`webpki::WebPkiServerVerifier::builder`] and
62    /// [`webpki::WebPkiServerVerifier::builder_with_provider`] for more information.
63    pub fn with_webpki_verifier(
64        self,
65        verifier: Arc<WebPkiServerVerifier>,
66    ) -> ConfigBuilder<ClientConfig, WantsClientCert> {
67        ConfigBuilder {
68            state: WantsClientCert {
69                verifier,
70                client_ech_mode: self.state.client_ech_mode,
71            },
72            provider: self.provider,
73            time_provider: self.time_provider,
74            side: PhantomData,
75        }
76    }
77
78    /// Access configuration options whose use is dangerous and requires
79    /// extra care.
80    pub fn dangerous(self) -> danger::DangerousClientConfigBuilder {
81        danger::DangerousClientConfigBuilder { cfg: self }
82    }
83}
84
85/// Container for unsafe APIs
86pub(super) mod danger {
87    use core::marker::PhantomData;
88
89    use crate::client::WantsClientCert;
90    use crate::sync::Arc;
91    use crate::{ClientConfig, ConfigBuilder, WantsVerifier, verify};
92
93    /// Accessor for dangerous configuration options.
94    #[derive(Debug)]
95    pub struct DangerousClientConfigBuilder {
96        /// The underlying ClientConfigBuilder
97        pub(super) cfg: ConfigBuilder<ClientConfig, WantsVerifier>,
98    }
99
100    impl DangerousClientConfigBuilder {
101        /// Set a custom certificate verifier.
102        pub fn with_custom_certificate_verifier(
103            self,
104            verifier: Arc<dyn verify::ServerVerifier>,
105        ) -> ConfigBuilder<ClientConfig, WantsClientCert> {
106            ConfigBuilder {
107                state: WantsClientCert {
108                    verifier,
109                    client_ech_mode: self.cfg.state.client_ech_mode,
110                },
111                provider: self.cfg.provider,
112                time_provider: self.cfg.time_provider,
113                side: PhantomData,
114            }
115        }
116    }
117}
118
119/// A config builder state where the caller needs to supply whether and how to provide a client
120/// certificate.
121///
122/// For more information, see the [`ConfigBuilder`] documentation.
123#[derive(Clone)]
124pub struct WantsClientCert {
125    verifier: Arc<dyn verify::ServerVerifier>,
126    client_ech_mode: Option<EchMode>,
127}
128
129impl ConfigBuilder<ClientConfig, WantsClientCert> {
130    /// Sets a single certificate chain and matching private key for use
131    /// in client authentication.
132    ///
133    /// `cert_chain` is a vector of DER-encoded certificates.
134    /// `key_der` is a DER-encoded private key as PKCS#1, PKCS#8, or SEC1. The
135    /// `aws-lc-rs` and `ring` [`CryptoProvider`][crate::CryptoProvider]s support
136    /// all three encodings, but other `CryptoProviders` may not.
137    ///
138    /// This function fails if `key_der` is invalid.
139    pub fn with_client_auth_cert(
140        self,
141        identity: Arc<Identity<'static>>,
142        key_der: PrivateKeyDer<'static>,
143    ) -> Result<ClientConfig, Error> {
144        let credentials = Credentials::from_der(identity, key_der, &self.provider)?;
145        self.with_client_credential_resolver(Arc::new(SingleCredential::from(credentials)))
146    }
147
148    /// Do not support client auth.
149    pub fn with_no_client_auth(self) -> Result<ClientConfig, Error> {
150        self.with_client_credential_resolver(Arc::new(handy::FailResolveClientCert {}))
151    }
152
153    /// Sets a custom [`ClientCredentialResolver`].
154    pub fn with_client_credential_resolver(
155        self,
156        client_auth_cert_resolver: Arc<dyn ClientCredentialResolver>,
157    ) -> Result<ClientConfig, Error> {
158        self.provider.consistency_check()?;
159
160        if self.state.client_ech_mode.is_some() {
161            match (
162                self.provider
163                    .tls12_cipher_suites
164                    .is_empty(),
165                self.provider
166                    .tls13_cipher_suites
167                    .is_empty(),
168            ) {
169                (_, true) => return Err(ApiMisuse::EchRequiresTls13Support.into()),
170                (false, _) => return Err(ApiMisuse::EchForbidsTls12Support.into()),
171                (true, false) => {}
172            };
173        }
174
175        Ok(ClientConfig {
176            provider: self.provider,
177            alpn_protocols: Vec::new(),
178            resumption: Resumption::default(),
179            max_fragment_size: None,
180            client_auth_cert_resolver,
181            enable_sni: true,
182            verifier: self.state.verifier,
183            key_log: Arc::new(NoKeyLog {}),
184            enable_secret_extraction: false,
185            enable_early_data: false,
186            require_ems: cfg!(feature = "fips"),
187            time_provider: self.time_provider,
188            cert_compressors: compress::default_cert_compressors().to_vec(),
189            cert_compression_cache: Arc::new(compress::CompressionCache::default()),
190            cert_decompressors: compress::default_cert_decompressors().to_vec(),
191            ech_mode: self.state.client_ech_mode,
192        })
193    }
194}