1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use rustls::crypto::CryptoProvider;
use std::sync::Arc;

#[cfg(all(
    any(unix, target_arch = "wasm32"),
    not(target_os = "android"),
    not(target_os = "macos"),
    not(target_os = "ios"),
    not(target_os = "tvos")
))]
mod others;

#[cfg(all(
    any(unix, target_arch = "wasm32"),
    not(target_os = "android"),
    not(target_os = "macos"),
    not(target_os = "ios"),
    not(target_os = "tvos")
))]
pub use others::Verifier;

#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))]
mod apple;

#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))]
pub use apple::Verifier;

#[cfg(target_os = "android")]
pub(crate) mod android;

#[cfg(target_os = "android")]
pub use android::Verifier;

#[cfg(windows)]
mod windows;

#[cfg(windows)]
pub use windows::Verifier;

/// An EKU was invalid for the use case of verifying a server certificate.
///
/// This error is used primarily for tests.
#[derive(Debug, PartialEq)]
pub(crate) struct EkuError;

impl std::fmt::Display for EkuError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("certificate had invalid extensions")
    }
}

impl std::error::Error for EkuError {}

// Log the certificate we are verifying so that we can try and find what may be wrong with it
// if we need to debug a user's situation.
fn log_server_cert(_end_entity: &rustls::pki_types::CertificateDer<'_>) {
    #[cfg(feature = "cert-logging")]
    {
        use base64::Engine;
        log::debug!(
            "verifying certificate: {}",
            base64::engine::general_purpose::STANDARD.encode(_end_entity.as_ref())
        );
    }
}

// Unknown certificate error shorthand. Used when we need to construct an "Other" certificate
// error with a platform specific error message.
#[cfg(any(windows, target_os = "macos", target_os = "ios", target_os = "tvos"))]
fn invalid_certificate(reason: impl Into<String>) -> rustls::Error {
    rustls::Error::InvalidCertificate(rustls::CertificateError::Other(rustls::OtherError(
        Arc::from(Box::from(reason.into())),
    )))
}

#[cfg(any(windows, target_os = "android"))]
/// List of EKUs that one or more of that *must* be in the end-entity certificate.
///
/// Legacy server-gated crypto OIDs are assumed to no longer be in use.
///
/// Currently supported:
/// - id-kp-serverAuth
// TODO: Chromium also allows for `OID_ANY_EKU` on Android.
pub const ALLOWED_EKUS: &[&str] = &["1.3.6.1.5.5.7.3.1"];

impl Verifier {
    /// Chainable setter to configure the [`CryptoProvider`] for this `Verifier`.
    ///
    /// This will be used instead of the rustls processs-default `CryptoProvider`, even if one has
    /// been installed.
    pub fn with_provider(mut self, crypto_provider: Arc<CryptoProvider>) -> Self {
        self.set_provider(crypto_provider);
        self
    }

    /// Configures the [`CryptoProvider`] for this `Verifier`.
    ///
    /// This will be used instead of the rustls processs-default `CryptoProvider`, even if one has
    /// been installed.
    pub fn set_provider(&mut self, crypto_provider: Arc<CryptoProvider>) {
        self.crypto_provider = crypto_provider.into();
    }

    fn get_provider(&self) -> &Arc<CryptoProvider> {
        self.crypto_provider.get_or_init(|| {
            CryptoProvider::get_default()
                .expect("rustls default CryptoProvider not set")
                .clone()
        })
    }
}