Skip to main content

reqwest/
tls.rs

1//! TLS configuration and types
2//!
3//! A `Client` will use transport layer security (TLS) by default to connect to
4//! HTTPS destinations.
5//!
6//! # Backends
7//!
8//! reqwest supports several TLS backends, enabled with Cargo features.
9//!
10//! ## default-tls
11//!
12//! reqwest will pick a TLS backend by default. This is true when the
13//! `default-tls` feature is enabled.
14//!
15//! While it currently uses `rustls`, the feature set is designed to only
16//! enable configuration that is shared among available backends. This allows
17//! reqwest to change the default to `native-tls` (or another) by configuration.
18//!
19//! <div class="warning">This feature is enabled by default, and takes
20//! precedence if any other crate enables it. This is true even if you declare
21//! `features = []`. You must set `default-features = false` instead.</div>
22//!
23//! Since Cargo features are additive, other crates in your dependency tree can
24//! cause the default backend to be enabled. If you wish to ensure your
25//! `Client` uses a specific backend, call the appropriate builder methods
26//! (such as [`tls_backend_rustls()`][]).
27//!
28//! [`tls_backend_rustls()`]: crate::ClientBuilder::tls_backend_rustls()
29//!
30//! ## native-tls
31//!
32//! This backend uses the [native-tls][] crate. That will try to use the system
33//! TLS on Windows and Mac, and OpenSSL on Linux targets.
34//!
35//! Enabling the feature explicitly allows for `native-tls`-specific
36//! configuration options.
37//!
38//! [native-tls]: https://crates.io/crates/native-tls
39//!
40//! ## rustls
41//!
42//! This backend uses the [rustls][] crate, a TLS library written in Rust.
43//!
44//! [rustls]: https://crates.io/crates/rustls
45
46#[cfg(feature = "__rustls")]
47use rustls::{
48    client::danger::HandshakeSignatureValid, client::danger::ServerCertVerified,
49    client::danger::ServerCertVerifier, crypto::WebPkiSupportedAlgorithms,
50    server::ParsedCertificate, DigitallySignedStruct, Error as TLSError, RootCertStore,
51    SignatureScheme,
52};
53use rustls_pki_types::pem::PemObject;
54#[cfg(feature = "__rustls")]
55use rustls_pki_types::{ServerName, UnixTime};
56use std::{
57    fmt,
58    io::{BufRead, BufReader},
59    sync::Arc,
60};
61
62/// Represents a X509 certificate revocation list.
63#[cfg(feature = "__rustls")]
64pub struct CertificateRevocationList {
65    #[cfg(feature = "__rustls")]
66    inner: rustls_pki_types::CertificateRevocationListDer<'static>,
67}
68
69/// Represents a server X509 certificate.
70#[derive(Clone)]
71pub struct Certificate {
72    #[cfg(feature = "__native-tls")]
73    native: native_tls_crate::Certificate,
74    #[cfg(feature = "__rustls")]
75    original: Cert,
76}
77
78#[cfg(feature = "__rustls")]
79#[derive(Clone)]
80enum Cert {
81    Der(Vec<u8>),
82    Pem(Vec<u8>),
83}
84
85/// Represents a private key and X509 cert as a client certificate.
86#[derive(Clone)]
87pub struct Identity {
88    #[cfg_attr(
89        not(any(feature = "__native-tls", feature = "__rustls")),
90        allow(unused)
91    )]
92    inner: ClientCert,
93}
94
95enum ClientCert {
96    #[cfg(feature = "__native-tls")]
97    Pkcs12(native_tls_crate::Identity),
98    #[cfg(feature = "__native-tls")]
99    Pkcs8(native_tls_crate::Identity),
100    #[cfg(feature = "__rustls")]
101    Pem {
102        key: rustls_pki_types::PrivateKeyDer<'static>,
103        certs: Vec<rustls_pki_types::CertificateDer<'static>>,
104    },
105}
106
107impl Clone for ClientCert {
108    fn clone(&self) -> Self {
109        match self {
110            #[cfg(feature = "__native-tls")]
111            Self::Pkcs8(i) => Self::Pkcs8(i.clone()),
112            #[cfg(feature = "__native-tls")]
113            Self::Pkcs12(i) => Self::Pkcs12(i.clone()),
114            #[cfg(feature = "__rustls")]
115            ClientCert::Pem { key, certs } => ClientCert::Pem {
116                key: key.clone_key(),
117                certs: certs.clone(),
118            },
119            #[cfg_attr(
120                any(feature = "__native-tls", feature = "__rustls"),
121                allow(unreachable_patterns)
122            )]
123            _ => unreachable!(),
124        }
125    }
126}
127
128impl Certificate {
129    /// Create a `Certificate` from a binary DER encoded certificate
130    ///
131    /// # Examples
132    ///
133    /// ```
134    /// # use std::fs::File;
135    /// # use std::io::Read;
136    /// # fn cert() -> Result<(), Box<dyn std::error::Error>> {
137    /// let mut buf = Vec::new();
138    /// File::open("my_cert.der")?
139    ///     .read_to_end(&mut buf)?;
140    /// let cert = reqwest::Certificate::from_der(&buf)?;
141    /// # drop(cert);
142    /// # Ok(())
143    /// # }
144    /// ```
145    pub fn from_der(der: &[u8]) -> crate::Result<Certificate> {
146        Ok(Certificate {
147            #[cfg(feature = "__native-tls")]
148            native: native_tls_crate::Certificate::from_der(der).map_err(crate::error::builder)?,
149            #[cfg(feature = "__rustls")]
150            original: Cert::Der(der.to_owned()),
151        })
152    }
153
154    /// Create a `Certificate` from a PEM encoded certificate
155    ///
156    /// # Examples
157    ///
158    /// ```
159    /// # use std::fs::File;
160    /// # use std::io::Read;
161    /// # fn cert() -> Result<(), Box<dyn std::error::Error>> {
162    /// let mut buf = Vec::new();
163    /// File::open("my_cert.pem")?
164    ///     .read_to_end(&mut buf)?;
165    /// let cert = reqwest::Certificate::from_pem(&buf)?;
166    /// # drop(cert);
167    /// # Ok(())
168    /// # }
169    /// ```
170    pub fn from_pem(pem: &[u8]) -> crate::Result<Certificate> {
171        Ok(Certificate {
172            #[cfg(feature = "__native-tls")]
173            native: native_tls_crate::Certificate::from_pem(pem).map_err(crate::error::builder)?,
174            #[cfg(feature = "__rustls")]
175            original: Cert::Pem(pem.to_owned()),
176        })
177    }
178
179    /// Create a collection of `Certificate`s from a PEM encoded certificate bundle.
180    /// Example byte sources may be `.crt`, `.cer` or `.pem` files.
181    ///
182    /// # Examples
183    ///
184    /// ```
185    /// # use std::fs::File;
186    /// # use std::io::Read;
187    /// # fn cert() -> Result<(), Box<dyn std::error::Error>> {
188    /// let mut buf = Vec::new();
189    /// File::open("ca-bundle.crt")?
190    ///     .read_to_end(&mut buf)?;
191    /// let certs = reqwest::Certificate::from_pem_bundle(&buf)?;
192    /// # drop(certs);
193    /// # Ok(())
194    /// # }
195    /// ```
196    pub fn from_pem_bundle(pem_bundle: &[u8]) -> crate::Result<Vec<Certificate>> {
197        let mut reader = BufReader::new(pem_bundle);
198
199        Self::read_pem_certs(&mut reader)?
200            .iter()
201            .map(|cert_vec| Certificate::from_der(cert_vec))
202            .collect::<crate::Result<Vec<Certificate>>>()
203    }
204
205    /*
206    #[cfg(feature = "rustls")]
207    pub fn from_trust_anchor() -> Self {
208
209    }
210    */
211
212    #[cfg(feature = "__native-tls")]
213    pub(crate) fn add_to_native_tls(self, tls: &mut native_tls_crate::TlsConnectorBuilder) {
214        tls.add_root_certificate(self.native);
215    }
216
217    #[cfg(feature = "__rustls")]
218    pub(crate) fn add_to_rustls(
219        self,
220        root_cert_store: &mut rustls::RootCertStore,
221    ) -> crate::Result<()> {
222        use std::io::Cursor;
223
224        match self.original {
225            Cert::Der(buf) => root_cert_store
226                .add(buf.into())
227                .map_err(crate::error::builder)?,
228            Cert::Pem(buf) => {
229                let mut reader = Cursor::new(buf);
230                let certs = Self::read_pem_certs(&mut reader)?;
231                for c in certs {
232                    root_cert_store
233                        .add(c.into())
234                        .map_err(crate::error::builder)?;
235                }
236            }
237        }
238        Ok(())
239    }
240
241    fn read_pem_certs(reader: &mut impl BufRead) -> crate::Result<Vec<Vec<u8>>> {
242        rustls_pki_types::CertificateDer::pem_reader_iter(reader)
243            .map(|result| match result {
244                Ok(cert) => Ok(cert.as_ref().to_vec()),
245                Err(_) => Err(crate::error::builder("invalid certificate encoding")),
246            })
247            .collect()
248    }
249}
250
251impl Identity {
252    /// Parses a DER-formatted PKCS #12 archive, using the specified password to decrypt the key.
253    ///
254    /// The archive should contain a leaf certificate and its private key, as well any intermediate
255    /// certificates that allow clients to build a chain to a trusted root.
256    /// The chain certificates should be in order from the leaf certificate towards the root.
257    ///
258    /// PKCS #12 archives typically have the file extension `.p12` or `.pfx`, and can be created
259    /// with the OpenSSL `pkcs12` tool:
260    ///
261    /// ```bash
262    /// openssl pkcs12 -export -out identity.pfx -inkey key.pem -in cert.pem -certfile chain_certs.pem
263    /// ```
264    ///
265    /// # Examples
266    ///
267    /// ```
268    /// # use std::fs::File;
269    /// # use std::io::Read;
270    /// # fn pkcs12() -> Result<(), Box<dyn std::error::Error>> {
271    /// let mut buf = Vec::new();
272    /// File::open("my-ident.pfx")?
273    ///     .read_to_end(&mut buf)?;
274    /// let pkcs12 = reqwest::Identity::from_pkcs12_der(&buf, "my-privkey-password")?;
275    /// # drop(pkcs12);
276    /// # Ok(())
277    /// # }
278    /// ```
279    ///
280    /// # Optional
281    ///
282    /// This requires the `native-tls` Cargo feature enabled.
283    #[cfg(feature = "__native-tls")]
284    pub fn from_pkcs12_der(der: &[u8], password: &str) -> crate::Result<Identity> {
285        Ok(Identity {
286            inner: ClientCert::Pkcs12(
287                native_tls_crate::Identity::from_pkcs12(der, password)
288                    .map_err(crate::error::builder)?,
289            ),
290        })
291    }
292
293    /// Parses a chain of PEM encoded X509 certificates, with the leaf certificate first.
294    /// `key` is a PEM encoded PKCS #8 formatted private key for the leaf certificate.
295    ///
296    /// The certificate chain should contain any intermediate certificates that should be sent to
297    /// clients to allow them to build a chain to a trusted root.
298    ///
299    /// A certificate chain here means a series of PEM encoded certificates concatenated together.
300    ///
301    /// # Examples
302    ///
303    /// ```
304    /// # use std::fs;
305    /// # fn pkcs8() -> Result<(), Box<dyn std::error::Error>> {
306    /// let cert = fs::read("client.pem")?;
307    /// let key = fs::read("key.pem")?;
308    /// let pkcs8 = reqwest::Identity::from_pkcs8_pem(&cert, &key)?;
309    /// # drop(pkcs8);
310    /// # Ok(())
311    /// # }
312    /// ```
313    ///
314    /// # Optional
315    ///
316    /// This requires the `native-tls` Cargo feature enabled.
317    #[cfg(feature = "__native-tls")]
318    pub fn from_pkcs8_pem(pem: &[u8], key: &[u8]) -> crate::Result<Identity> {
319        Ok(Identity {
320            inner: ClientCert::Pkcs8(
321                native_tls_crate::Identity::from_pkcs8(pem, key).map_err(crate::error::builder)?,
322            ),
323        })
324    }
325
326    /// Parses PEM encoded private key and certificate.
327    ///
328    /// The input should contain a PEM encoded private key
329    /// and at least one PEM encoded certificate.
330    ///
331    /// Note: The private key must be in RSA, SEC1 Elliptic Curve or PKCS#8 format.
332    ///
333    /// # Examples
334    ///
335    /// ```
336    /// # use std::fs::File;
337    /// # use std::io::Read;
338    /// # fn pem() -> Result<(), Box<dyn std::error::Error>> {
339    /// let mut buf = Vec::new();
340    /// File::open("my-ident.pem")?
341    ///     .read_to_end(&mut buf)?;
342    /// let id = reqwest::Identity::from_pem(&buf)?;
343    /// # drop(id);
344    /// # Ok(())
345    /// # }
346    /// ```
347    ///
348    /// # Optional
349    ///
350    /// This requires the `rustls(-...)` Cargo feature enabled.
351    #[cfg(feature = "__rustls")]
352    pub fn from_pem(buf: &[u8]) -> crate::Result<Identity> {
353        use rustls_pki_types::{pem::SectionKind, PrivateKeyDer};
354        use std::io::Cursor;
355
356        let (key, certs) = {
357            let mut pem = Cursor::new(buf);
358            let mut sk = Vec::<rustls_pki_types::PrivateKeyDer>::new();
359            let mut certs = Vec::<rustls_pki_types::CertificateDer>::new();
360
361            while let Some((kind, data)) =
362                rustls_pki_types::pem::from_buf(&mut pem).map_err(|_| {
363                    crate::error::builder(TLSError::General(String::from(
364                        "Invalid identity PEM file",
365                    )))
366                })?
367            {
368                match kind {
369                    SectionKind::Certificate => certs.push(data.into()),
370                    SectionKind::PrivateKey => sk.push(PrivateKeyDer::Pkcs8(data.into())),
371                    SectionKind::RsaPrivateKey => sk.push(PrivateKeyDer::Pkcs1(data.into())),
372                    SectionKind::EcPrivateKey => sk.push(PrivateKeyDer::Sec1(data.into())),
373                    _ => {
374                        return Err(crate::error::builder(TLSError::General(String::from(
375                            "No valid certificate was found",
376                        ))))
377                    }
378                }
379            }
380
381            if let (Some(sk), false) = (sk.pop(), certs.is_empty()) {
382                (sk, certs)
383            } else {
384                return Err(crate::error::builder(TLSError::General(String::from(
385                    "private key or certificate not found",
386                ))));
387            }
388        };
389
390        Ok(Identity {
391            inner: ClientCert::Pem { key, certs },
392        })
393    }
394
395    #[cfg(feature = "__native-tls")]
396    pub(crate) fn add_to_native_tls(
397        self,
398        tls: &mut native_tls_crate::TlsConnectorBuilder,
399    ) -> crate::Result<()> {
400        match self.inner {
401            ClientCert::Pkcs12(id) | ClientCert::Pkcs8(id) => {
402                tls.identity(id);
403                Ok(())
404            }
405            #[cfg(feature = "__rustls")]
406            ClientCert::Pem { .. } => Err(crate::error::builder("incompatible TLS identity type")),
407        }
408    }
409
410    #[cfg(feature = "__rustls")]
411    pub(crate) fn add_to_rustls(
412        self,
413        config_builder: rustls::ConfigBuilder<
414            rustls::ClientConfig,
415            // Not sure here
416            rustls::client::WantsClientCert,
417        >,
418    ) -> crate::Result<rustls::ClientConfig> {
419        match self.inner {
420            ClientCert::Pem { key, certs } => config_builder
421                .with_client_auth_cert(certs, key)
422                .map_err(crate::error::builder),
423            #[cfg(feature = "__native-tls")]
424            ClientCert::Pkcs12(..) | ClientCert::Pkcs8(..) => {
425                Err(crate::error::builder("incompatible TLS identity type"))
426            }
427        }
428    }
429}
430
431#[cfg(feature = "__rustls")]
432impl CertificateRevocationList {
433    /// Parses a PEM encoded CRL.
434    ///
435    /// # Examples
436    ///
437    /// ```
438    /// # use std::fs::File;
439    /// # use std::io::Read;
440    /// # fn crl() -> Result<(), Box<dyn std::error::Error>> {
441    /// let mut buf = Vec::new();
442    /// File::open("my_crl.pem")?
443    ///     .read_to_end(&mut buf)?;
444    /// let crl = reqwest::tls::CertificateRevocationList::from_pem(&buf)?;
445    /// # drop(crl);
446    /// # Ok(())
447    /// # }
448    /// ```
449    ///
450    /// # Optional
451    ///
452    /// This requires the `rustls(-...)` Cargo feature enabled.
453    #[cfg(feature = "__rustls")]
454    pub fn from_pem(pem: &[u8]) -> crate::Result<CertificateRevocationList> {
455        Ok(CertificateRevocationList {
456            #[cfg(feature = "__rustls")]
457            inner: rustls_pki_types::CertificateRevocationListDer::from_pem_slice(pem)
458                .map_err(|_| crate::error::builder("invalid crl encoding"))?,
459        })
460    }
461
462    /// Creates a collection of `CertificateRevocationList`s from a PEM encoded CRL bundle.
463    /// Example byte sources may be `.crl` or `.pem` files.
464    ///
465    /// # Examples
466    ///
467    /// ```
468    /// # use std::fs::File;
469    /// # use std::io::Read;
470    /// # fn crls() -> Result<(), Box<dyn std::error::Error>> {
471    /// let mut buf = Vec::new();
472    /// File::open("crl-bundle.crl")?
473    ///     .read_to_end(&mut buf)?;
474    /// let crls = reqwest::tls::CertificateRevocationList::from_pem_bundle(&buf)?;
475    /// # drop(crls);
476    /// # Ok(())
477    /// # }
478    /// ```
479    ///
480    /// # Optional
481    ///
482    /// This requires the `rustls(-...)` Cargo feature enabled.
483    #[cfg(feature = "__rustls")]
484    pub fn from_pem_bundle(pem_bundle: &[u8]) -> crate::Result<Vec<CertificateRevocationList>> {
485        rustls_pki_types::CertificateRevocationListDer::pem_slice_iter(pem_bundle)
486            .map(|result| match result {
487                Ok(crl) => Ok(CertificateRevocationList { inner: crl }),
488                Err(_) => Err(crate::error::builder("invalid crl encoding")),
489            })
490            .collect::<crate::Result<Vec<CertificateRevocationList>>>()
491    }
492
493    #[cfg(feature = "__rustls")]
494    pub(crate) fn as_rustls_crl<'a>(&self) -> rustls_pki_types::CertificateRevocationListDer<'a> {
495        self.inner.clone()
496    }
497}
498
499impl fmt::Debug for Certificate {
500    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
501        f.debug_struct("Certificate").finish()
502    }
503}
504
505impl fmt::Debug for Identity {
506    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
507        f.debug_struct("Identity").finish()
508    }
509}
510
511#[cfg(feature = "__rustls")]
512impl fmt::Debug for CertificateRevocationList {
513    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
514        f.debug_struct("CertificateRevocationList").finish()
515    }
516}
517
518/// A TLS protocol version.
519#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
520pub struct Version(InnerVersion);
521
522#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
523#[non_exhaustive]
524enum InnerVersion {
525    Tls1_0,
526    Tls1_1,
527    Tls1_2,
528    Tls1_3,
529}
530
531// These could perhaps be From/TryFrom implementations, but those would be
532// part of the public API so let's be careful
533impl Version {
534    /// Version 1.0 of the TLS protocol.
535    pub const TLS_1_0: Version = Version(InnerVersion::Tls1_0);
536    /// Version 1.1 of the TLS protocol.
537    pub const TLS_1_1: Version = Version(InnerVersion::Tls1_1);
538    /// Version 1.2 of the TLS protocol.
539    pub const TLS_1_2: Version = Version(InnerVersion::Tls1_2);
540    /// Version 1.3 of the TLS protocol.
541    pub const TLS_1_3: Version = Version(InnerVersion::Tls1_3);
542
543    #[cfg(feature = "__native-tls")]
544    pub(crate) fn to_native_tls(self) -> Option<native_tls_crate::Protocol> {
545        match self.0 {
546            InnerVersion::Tls1_0 => Some(native_tls_crate::Protocol::Tlsv10),
547            InnerVersion::Tls1_1 => Some(native_tls_crate::Protocol::Tlsv11),
548            InnerVersion::Tls1_2 => Some(native_tls_crate::Protocol::Tlsv12),
549            InnerVersion::Tls1_3 => None,
550        }
551    }
552
553    #[cfg(feature = "__rustls")]
554    pub(crate) fn from_rustls(version: rustls::ProtocolVersion) -> Option<Self> {
555        match version {
556            rustls::ProtocolVersion::SSLv2 => None,
557            rustls::ProtocolVersion::SSLv3 => None,
558            rustls::ProtocolVersion::TLSv1_0 => Some(Self(InnerVersion::Tls1_0)),
559            rustls::ProtocolVersion::TLSv1_1 => Some(Self(InnerVersion::Tls1_1)),
560            rustls::ProtocolVersion::TLSv1_2 => Some(Self(InnerVersion::Tls1_2)),
561            rustls::ProtocolVersion::TLSv1_3 => Some(Self(InnerVersion::Tls1_3)),
562            _ => None,
563        }
564    }
565}
566
567pub(crate) enum TlsBackend {
568    // This is the default and HTTP/3 feature does not use it so suppress it.
569    #[allow(dead_code)]
570    #[cfg(feature = "__native-tls")]
571    NativeTls,
572    #[cfg(feature = "__native-tls")]
573    BuiltNativeTls(native_tls_crate::TlsConnector),
574    #[cfg(feature = "__rustls")]
575    Rustls,
576    #[cfg(feature = "__rustls")]
577    BuiltRustls(Box<rustls::ClientConfig>),
578    #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
579    UnknownPreconfigured,
580}
581
582impl fmt::Debug for TlsBackend {
583    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
584        match self {
585            #[cfg(feature = "__native-tls")]
586            TlsBackend::NativeTls => write!(f, "NativeTls"),
587            #[cfg(feature = "__native-tls")]
588            TlsBackend::BuiltNativeTls(_) => write!(f, "BuiltNativeTls"),
589            #[cfg(feature = "__rustls")]
590            TlsBackend::Rustls => write!(f, "Rustls"),
591            #[cfg(feature = "__rustls")]
592            TlsBackend::BuiltRustls(_) => write!(f, "BuiltRustls"),
593            #[cfg(any(feature = "__native-tls", feature = "__rustls",))]
594            TlsBackend::UnknownPreconfigured => write!(f, "UnknownPreconfigured"),
595        }
596    }
597}
598
599#[allow(clippy::derivable_impls)]
600impl Default for TlsBackend {
601    fn default() -> TlsBackend {
602        #[cfg(any(
603            all(feature = "__rustls", not(feature = "__native-tls")),
604            feature = "http3"
605        ))]
606        {
607            TlsBackend::Rustls
608        }
609
610        #[cfg(all(feature = "__native-tls", not(feature = "http3")))]
611        {
612            TlsBackend::NativeTls
613        }
614    }
615}
616
617#[cfg(feature = "__rustls")]
618pub(crate) fn rustls_store(certs: Vec<Certificate>) -> crate::Result<RootCertStore> {
619    let mut root_cert_store = rustls::RootCertStore::empty();
620    for cert in certs {
621        cert.add_to_rustls(&mut root_cert_store)?;
622    }
623    Ok(root_cert_store)
624}
625
626/// Returns a cached default root certificate store with webpki roots and native OS root CAs.
627#[cfg(feature = "__rustls")]
628pub fn default_root_store() -> &'static rustls::RootCertStore {
629    static DEFAULT_ROOTS: std::sync::OnceLock<rustls::RootCertStore> = std::sync::OnceLock::new();
630    DEFAULT_ROOTS.get_or_init(|| {
631        let mut roots = rustls::RootCertStore::empty();
632        roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
633        let native = rustls_native_certs::load_native_certs();
634        for err in &native.errors {
635            log::warn!("failed to load native root certificate: {err}");
636        }
637        if !native.certs.is_empty() {
638            roots.add_parsable_certificates(native.certs);
639        }
640        roots
641    })
642}
643
644/// Returns a cached `Arc<RootCertStore>`, avoiding deep clones on every call.
645///
646/// The underlying store is initialized once (loading webpki + native roots),
647/// and subsequent calls only increment the `Arc` reference count.
648#[cfg(feature = "__rustls")]
649pub fn default_root_store_arc() -> Arc<rustls::RootCertStore> {
650    static DEFAULT_ROOTS_ARC: std::sync::OnceLock<Arc<rustls::RootCertStore>> =
651        std::sync::OnceLock::new();
652    DEFAULT_ROOTS_ARC
653        .get_or_init(|| Arc::new(default_root_store().clone()))
654        .clone()
655}
656
657/// Creates a root certificate store from the cached default store plus user-provided certs.
658#[cfg(feature = "__rustls")]
659pub fn merged_root_store(certs: Vec<Certificate>) -> crate::Result<RootCertStore> {
660    let mut store = default_root_store().clone();
661    for cert in certs {
662        cert.add_to_rustls(&mut store)?;
663    }
664    Ok(store)
665}
666
667#[cfg(feature = "__rustls")]
668#[derive(Debug)]
669pub(crate) struct NoVerifier;
670
671#[cfg(feature = "__rustls")]
672impl ServerCertVerifier for NoVerifier {
673    fn verify_server_cert(
674        &self,
675        _end_entity: &rustls_pki_types::CertificateDer,
676        _intermediates: &[rustls_pki_types::CertificateDer],
677        _server_name: &ServerName,
678        _ocsp_response: &[u8],
679        _now: UnixTime,
680    ) -> Result<ServerCertVerified, TLSError> {
681        Ok(ServerCertVerified::assertion())
682    }
683
684    fn verify_tls12_signature(
685        &self,
686        _message: &[u8],
687        _cert: &rustls_pki_types::CertificateDer,
688        _dss: &DigitallySignedStruct,
689    ) -> Result<HandshakeSignatureValid, TLSError> {
690        Ok(HandshakeSignatureValid::assertion())
691    }
692
693    fn verify_tls13_signature(
694        &self,
695        _message: &[u8],
696        _cert: &rustls_pki_types::CertificateDer,
697        _dss: &DigitallySignedStruct,
698    ) -> Result<HandshakeSignatureValid, TLSError> {
699        Ok(HandshakeSignatureValid::assertion())
700    }
701
702    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
703        vec![
704            SignatureScheme::RSA_PKCS1_SHA1,
705            SignatureScheme::ECDSA_SHA1_Legacy,
706            SignatureScheme::RSA_PKCS1_SHA256,
707            SignatureScheme::ECDSA_NISTP256_SHA256,
708            SignatureScheme::RSA_PKCS1_SHA384,
709            SignatureScheme::ECDSA_NISTP384_SHA384,
710            SignatureScheme::RSA_PKCS1_SHA512,
711            SignatureScheme::ECDSA_NISTP521_SHA512,
712            SignatureScheme::RSA_PSS_SHA256,
713            SignatureScheme::RSA_PSS_SHA384,
714            SignatureScheme::RSA_PSS_SHA512,
715            SignatureScheme::ED25519,
716            SignatureScheme::ED448,
717        ]
718    }
719}
720
721#[cfg(feature = "__rustls")]
722#[derive(Debug)]
723pub(crate) struct IgnoreHostname {
724    roots: RootCertStore,
725    signature_algorithms: WebPkiSupportedAlgorithms,
726}
727
728#[cfg(feature = "__rustls")]
729impl IgnoreHostname {
730    pub(crate) fn new(
731        roots: RootCertStore,
732        signature_algorithms: WebPkiSupportedAlgorithms,
733    ) -> Self {
734        Self {
735            roots,
736            signature_algorithms,
737        }
738    }
739}
740
741#[cfg(feature = "__rustls")]
742impl ServerCertVerifier for IgnoreHostname {
743    fn verify_server_cert(
744        &self,
745        end_entity: &rustls_pki_types::CertificateDer<'_>,
746        intermediates: &[rustls_pki_types::CertificateDer<'_>],
747        _server_name: &ServerName<'_>,
748        _ocsp_response: &[u8],
749        now: UnixTime,
750    ) -> Result<ServerCertVerified, TLSError> {
751        let cert = ParsedCertificate::try_from(end_entity)?;
752
753        rustls::client::verify_server_cert_signed_by_trust_anchor(
754            &cert,
755            &self.roots,
756            intermediates,
757            now,
758            self.signature_algorithms.all,
759        )?;
760        Ok(ServerCertVerified::assertion())
761    }
762
763    fn verify_tls12_signature(
764        &self,
765        message: &[u8],
766        cert: &rustls_pki_types::CertificateDer<'_>,
767        dss: &DigitallySignedStruct,
768    ) -> Result<HandshakeSignatureValid, TLSError> {
769        rustls::crypto::verify_tls12_signature(message, cert, dss, &self.signature_algorithms)
770    }
771
772    fn verify_tls13_signature(
773        &self,
774        message: &[u8],
775        cert: &rustls_pki_types::CertificateDer<'_>,
776        dss: &DigitallySignedStruct,
777    ) -> Result<HandshakeSignatureValid, TLSError> {
778        rustls::crypto::verify_tls13_signature(message, cert, dss, &self.signature_algorithms)
779    }
780
781    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
782        self.signature_algorithms.supported_schemes()
783    }
784}
785
786/// Hyper extension carrying extra TLS layer information.
787/// Made available to clients on responses when `tls_info` is set.
788#[derive(Clone)]
789pub struct TlsInfo {
790    pub(crate) peer_certificate: Option<Vec<u8>>,
791}
792
793impl TlsInfo {
794    /// Get the DER encoded leaf certificate of the peer.
795    pub fn peer_certificate(&self) -> Option<&[u8]> {
796        self.peer_certificate.as_ref().map(|der| &der[..])
797    }
798}
799
800impl std::fmt::Debug for TlsInfo {
801    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
802        f.debug_struct("TlsInfo").finish()
803    }
804}
805
806#[cfg(test)]
807mod tests {
808    use super::*;
809
810    #[cfg(feature = "__native-tls")]
811    #[test]
812    fn certificate_from_der_invalid() {
813        Certificate::from_der(b"not der").unwrap_err();
814    }
815
816    #[cfg(feature = "__native-tls")]
817    #[test]
818    fn certificate_from_pem_invalid() {
819        Certificate::from_pem(b"not pem").unwrap_err();
820    }
821
822    #[cfg(feature = "__native-tls")]
823    #[test]
824    fn identity_from_pkcs12_der_invalid() {
825        Identity::from_pkcs12_der(b"not der", "nope").unwrap_err();
826    }
827
828    #[cfg(feature = "__native-tls")]
829    #[test]
830    fn identity_from_pkcs8_pem_invalid() {
831        Identity::from_pkcs8_pem(b"not pem", b"not key").unwrap_err();
832    }
833
834    #[cfg(feature = "__rustls")]
835    #[test]
836    fn identity_from_pem_invalid() {
837        Identity::from_pem(b"not pem").unwrap_err();
838    }
839
840    #[cfg(feature = "__rustls")]
841    #[test]
842    fn identity_from_pem_pkcs1_key() {
843        let pem = b"-----BEGIN CERTIFICATE-----\n\
844            -----END CERTIFICATE-----\n\
845            -----BEGIN RSA PRIVATE KEY-----\n\
846            -----END RSA PRIVATE KEY-----\n";
847
848        Identity::from_pem(pem).unwrap();
849    }
850
851    #[test]
852    fn certificates_from_pem_bundle() {
853        const PEM_BUNDLE: &[u8] = b"
854            -----BEGIN CERTIFICATE-----
855            MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5
856            MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
857            Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
858            A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
859            Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl
860            ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j
861            QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr
862            ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr
863            BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM
864            YyRIHN8wfdVoOw==
865            -----END CERTIFICATE-----
866
867            -----BEGIN CERTIFICATE-----
868            MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5
869            MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g
870            Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG
871            A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg
872            Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi
873            9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk
874            M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB
875            /zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB
876            MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw
877            CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW
878            1KyLa2tJElMzrdfkviT8tQp21KW8EA==
879            -----END CERTIFICATE-----
880        ";
881
882        assert!(Certificate::from_pem_bundle(PEM_BUNDLE).is_ok())
883    }
884
885    #[cfg(feature = "__rustls")]
886    #[test]
887    fn crl_from_pem() {
888        let pem = b"-----BEGIN X509 CRL-----\n-----END X509 CRL-----\n";
889
890        CertificateRevocationList::from_pem(pem).unwrap();
891    }
892
893    #[cfg(feature = "__rustls")]
894    #[test]
895    fn crl_from_pem_bundle() {
896        let pem_bundle = std::fs::read("tests/support/crl.pem").unwrap();
897
898        let result = CertificateRevocationList::from_pem_bundle(&pem_bundle);
899
900        assert!(result.is_ok());
901        let result = result.unwrap();
902        assert_eq!(result.len(), 1);
903    }
904}