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