pavex/tls/client/
rustls_0_23.rs

1use crate::tls::client::config::{
2    AllowedTlsVersionsConfig, CertificateVerificationConfig, CryptoProviderConfig, RootCertificate,
3    RootCertificateFileEncoding,
4};
5
6use super::TlsClientPolicyConfig;
7use anyhow::{Context, bail, ensure};
8use rustls::{
9    ClientConfig, Error as TlsError, RootCertStore, SupportedProtocolVersion,
10    client::{
11        WebPkiServerVerifier,
12        danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
13    },
14    crypto::CryptoProvider,
15    pki_types::{
16        CertificateDer, ServerName,
17        pem::{PemObject, SectionKind},
18    },
19    version::{TLS12, TLS13},
20};
21use std::sync::Arc;
22
23impl TryFrom<&TlsClientPolicyConfig> for ClientConfig {
24    type Error = Rustls023ConfigError;
25
26    fn try_from(value: &TlsClientPolicyConfig) -> Result<Self, Self::Error> {
27        value.rustls_0_23_config()
28    }
29}
30
31#[derive(Debug, thiserror::Error)]
32#[error(transparent)]
33/// The error that can occur in [`TlsClientPolicyConfig::rustls_0_23_config`] when building a
34/// [`rustls::ClientConfig`](https://docs.rs/rustls/0.23/rustls/client/struct.ClientConfig.html).
35pub struct Rustls023ConfigError(anyhow::Error);
36
37impl TlsClientPolicyConfig {
38    /// Build a [`rustls::ClientConfig`](https://docs.rs/rustls/0.23/rustls/client/struct.ClientConfig.html) according to the specified configuration.
39    pub fn rustls_0_23_config(&self) -> Result<ClientConfig, Rustls023ConfigError> {
40        fn _config(policy: &TlsClientPolicyConfig) -> Result<ClientConfig, anyhow::Error> {
41            let provider = Arc::new(crypto_provider(&policy.crypto_provider)?);
42            let builder = ClientConfig::builder_with_provider(provider.clone())
43                .with_protocol_versions(supported_versions(policy.allowed_versions)?)?;
44
45            let config = if policy.insecure.skip_verification {
46                builder
47                    .dangerous()
48                    .with_custom_certificate_verifier(Arc::new(NoVerify { provider }))
49            } else {
50                let certificate_verifier =
51                    certificate_verifier(provider, &policy.certificate_verification)?;
52                builder
53                    .dangerous()
54                    .with_custom_certificate_verifier(certificate_verifier)
55            }
56            .with_no_client_auth();
57
58            Ok(config)
59        }
60
61        _config(self).map_err(Rustls023ConfigError)
62    }
63}
64
65fn crypto_provider(p: &CryptoProviderConfig) -> Result<CryptoProvider, anyhow::Error> {
66    match p {
67        #[cfg(not(feature = "tls_crypto_provider_aws_lc_rs"))]
68        CryptoProviderConfig::AwsLcRs => {
69            bail!(
70                "Your TLS client configuration wants to use `aws_lc_rs` as its cryptography stack, but the corresponding `cargo` feature is not enabled.\n\
71                 Add `tls_crypto_provider_aws_lc_rs` to the `features` array for `pavex` in your Cargo.toml manifest."
72            );
73        }
74        #[cfg(feature = "tls_crypto_provider_aws_lc_rs")]
75        CryptoProviderConfig::AwsLcRs => Ok(rustls::crypto::aws_lc_rs::default_provider()),
76        #[cfg(feature = "tls_crypto_provider_aws_lc_rs")]
77        CryptoProviderConfig::AwsLcRsFips => {
78            let prov = rustls::crypto::aws_lc_rs::default_provider();
79
80            if !prov.fips() {
81                bail!(
82                    "FIPS requested but the `fips` feature is not enabled. Add `fips` to the `features` array for `pavex` in your Cargo.toml manifest."
83                );
84            }
85            Ok(prov)
86        }
87        #[cfg(not(feature = "tls_crypto_provider_aws_lc_rs"))]
88        CryptoProviderConfig::AwsLcRsFips => {
89            bail!(
90                "Your TLS client configuration wants to use `aws_lc_rs_fips` as its cryptography stack, but the corresponding `cargo` feature is not enabled.\n\
91                 Add `tls_crypto_provider_aws_lc_rs` and `fips` to the `features` array for `pavex` in your Cargo.toml manifest."
92            );
93        }
94        #[cfg(feature = "tls_crypto_provider_ring")]
95        CryptoProviderConfig::Ring => Ok(rustls::crypto::ring::default_provider()),
96        #[cfg(not(feature = "tls_crypto_provider_ring"))]
97        CryptoProviderConfig::Ring => bail!(
98            "Your TLS client configuration wants to use `ring` as its cryptography stack, but the corresponding `cargo` feature is not enabled.\n\
99             Add `tls_crypto_provider_ring` to the `features` array for `pavex` in your Cargo.toml manifest."
100        ),
101    }
102}
103
104fn certificate_verifier(
105    crypto_provider: Arc<CryptoProvider>,
106    config: &CertificateVerificationConfig,
107) -> Result<Arc<dyn ServerCertVerifier>, anyhow::Error> {
108    if !config.use_os_verifier && config.additional_roots.is_empty() {
109        anyhow::bail!(
110            "You disabled OS server certificate verification without providing a list of additional root certificates to trust.\n\
111           This configuration is invalid: it would cause **all** TLS connections to fail.\n\
112           Please enable OS certificate verification or provide a list of root certificates to trust. Check out the documentation \
113           of `pavex::tls::client::CertificateVerificationConfig` for more information."
114        )
115    }
116    let additional_roots = additional_roots(&config.additional_roots)?;
117    if config.use_os_verifier {
118        let verifier = rustls_platform_verifier::Verifier::new_with_extra_roots(
119            additional_roots,
120            crypto_provider,
121        )
122        .context("Failed to initialize the server certificate verifier")?;
123        Ok(Arc::new(verifier))
124    } else {
125        let mut root_cert_store = RootCertStore::empty();
126        for root in additional_roots {
127            root_cert_store
128                .add(root)
129                .context("One of your additional root certificates is invalid")?;
130        }
131        let verifier =
132            WebPkiServerVerifier::builder_with_provider(Arc::new(root_cert_store), crypto_provider)
133                .build()
134                .context("Failed to initialize the server certificate verifier")?;
135        Ok(verifier)
136    }
137}
138
139fn supported_versions(
140    config: AllowedTlsVersionsConfig,
141) -> Result<&'static [&'static SupportedProtocolVersion], anyhow::Error> {
142    static ALL: [&SupportedProtocolVersion; 2] = [&TLS12, &TLS13];
143    static ONLY_TLS12: [&SupportedProtocolVersion; 1] = [&TLS12];
144    static ONLY_TLS13: [&SupportedProtocolVersion; 1] = [&TLS13];
145
146    match (config.v1_2, config.v1_3) {
147        (true, true) => Ok(&ALL),
148        (true, false) => Ok(&ONLY_TLS12),
149        (false, true) => Ok(&ONLY_TLS13),
150        (false, false) => {
151            bail!("You disabled both TLS 1.2 and TLS 1.3. At least one of them must be enabled.");
152        }
153    }
154}
155
156fn additional_roots(
157    root_sources: &[RootCertificate],
158) -> Result<Vec<CertificateDer<'static>>, anyhow::Error> {
159    let mut roots = Vec::with_capacity(root_sources.len());
160    for (i, source) in root_sources.iter().enumerate() {
161        parse_additional_root(&mut roots, source).with_context(|| {
162            format!(
163                "Failed to parse the root certificate at index {i} in your list of `additional_roots`",
164            )
165        })?;
166    }
167    Ok(roots)
168}
169
170fn parse_additional_root(
171    roots: &mut Vec<CertificateDer<'static>>,
172    source: &RootCertificate,
173) -> Result<(), anyhow::Error> {
174    match source {
175        RootCertificate::File { encoding, path } => {
176            let contents = fs_err::read(path).context("Failed to read root certificate file")?;
177            match encoding {
178                RootCertificateFileEncoding::Der => {
179                    roots.push(CertificateDer::from(contents));
180                }
181                RootCertificateFileEncoding::Pem => {
182                    roots.extend(parse_certificates_from_pem_bytes(&contents)?);
183                }
184            }
185        }
186        RootCertificate::Inline { data } => {
187            roots.extend(parse_certificates_from_pem_bytes(data.as_bytes())?);
188        }
189    }
190    Ok(())
191}
192
193fn parse_certificates_from_pem_bytes(
194    data: &[u8],
195) -> Result<Vec<CertificateDer<'static>>, anyhow::Error> {
196    let mut certs = Vec::new();
197    for outcome in <(SectionKind, Vec<u8>) as PemObject>::pem_slice_iter(data) {
198        let (section_kind, section_data) =
199            outcome.context("Failed to parse a section of your PEM-encoded root certificate")?;
200        if section_kind != SectionKind::Certificate {
201            anyhow::bail!(
202                "Expected a PEM-encoded root certificate, but found a {} section",
203                kind2str(section_kind)
204            )
205        }
206        certs.push(CertificateDer::from(section_data));
207    }
208    ensure!(
209        !certs.is_empty(),
210        "Your PEM bundle doesn't contain any root certificate. There should be at least one `BEGIN CERTIFICATE` block"
211    );
212    Ok(certs)
213}
214
215fn kind2str(kind: SectionKind) -> &'static str {
216    match kind {
217        SectionKind::Certificate => "CERTIFICATE",
218        SectionKind::PublicKey => "PUBLIC KEY",
219        SectionKind::RsaPrivateKey => "RSA PRIVATE KEY",
220        SectionKind::PrivateKey => "PRIVATE KEY",
221        SectionKind::EcPrivateKey => "EC PRIVATE KEY",
222        SectionKind::Crl => "X509 CRL",
223        SectionKind::Csr => "CERTIFICATE REQUEST",
224        SectionKind::EchConfigList => "ECHCONFIG",
225        _ => "unknown",
226    }
227}
228
229#[derive(Debug, Clone)]
230/// A custom verifier that doesn't actually verify server certificates.
231struct NoVerify {
232    provider: Arc<CryptoProvider>,
233}
234
235impl ServerCertVerifier for NoVerify {
236    fn verify_tls12_signature(
237        &self,
238        _message: &[u8],
239        _cert: &CertificateDer<'_>,
240        _dss: &rustls::DigitallySignedStruct,
241    ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, TlsError> {
242        Ok(HandshakeSignatureValid::assertion())
243    }
244
245    fn verify_tls13_signature(
246        &self,
247        _message: &[u8],
248        _cert: &CertificateDer<'_>,
249        _dss: &rustls::DigitallySignedStruct,
250    ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, TlsError> {
251        Ok(HandshakeSignatureValid::assertion())
252    }
253
254    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
255        self.provider
256            .signature_verification_algorithms
257            .supported_schemes()
258    }
259
260    fn verify_server_cert(
261        &self,
262        _end_entity: &CertificateDer<'_>,
263        _intermediates: &[CertificateDer<'_>],
264        _server_name: &ServerName<'_>,
265        _ocsp_response: &[u8],
266        _now: rustls::pki_types::UnixTime,
267    ) -> std::result::Result<ServerCertVerified, TlsError> {
268        Ok(ServerCertVerified::assertion())
269    }
270}