deltachat/net/tls/
danger.rs

1//! Custom TLS verification.
2//!
3//! We want to accept expired certificates.
4
5use rustls::RootCertStore;
6use rustls::client::{verify_server_cert_signed_by_trust_anchor, verify_server_name};
7use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
8use rustls::server::ParsedCertificate;
9use tokio_rustls::rustls;
10
11use crate::net::tls::spki::spki_hash;
12
13#[derive(Debug)]
14pub(super) struct CustomCertificateVerifier {
15    /// Root certificates.
16    root_cert_store: RootCertStore,
17
18    /// Expected SPKI hash as a base64 of SHA-256.
19    spki_hash: Option<String>,
20}
21
22impl CustomCertificateVerifier {
23    pub(super) fn new(spki_hash: Option<String>) -> Self {
24        let root_cert_store =
25            RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
26        Self {
27            root_cert_store,
28            spki_hash,
29        }
30    }
31}
32
33impl rustls::client::danger::ServerCertVerifier for CustomCertificateVerifier {
34    fn verify_server_cert(
35        &self,
36        end_entity: &CertificateDer<'_>,
37        intermediates: &[CertificateDer<'_>],
38        server_name: &ServerName<'_>,
39        // OCSP is a certificate revocation mechanism that is intentionally ignored.
40        // It is practically not used and is essentially deprecated
41        // in favor of Certificate Revocation Lists.
42        // Let's Encrypt has disabled OCSP responders in 2025:
43        // <https://letsencrypt.org/2025/08/06/ocsp-service-has-reached-end-of-life>.
44        // Theoretically checking of stapled OCSP responses could be implemented,
45        // but it is not interesting to implement it because it is not used
46        // by the servers: <https://github.com/rustls/webpki/issues/217>.
47        _ocsp_response: &[u8],
48        now: UnixTime,
49    ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
50        let parsed_certificate = ParsedCertificate::try_from(end_entity)?;
51
52        let spki = parsed_certificate.subject_public_key_info();
53
54        let provider = rustls::crypto::ring::default_provider();
55
56        if let ServerName::DnsName(dns_name) = server_name
57            && dns_name.as_ref().starts_with("_")
58        {
59            // Do not verify certificates for hostnames starting with `_`.
60            // They are used for servers with self-signed certificates, e.g. for local testing.
61            // Hostnames starting with `_` can have only self-signed TLS certificates or wildcard certificates.
62            // It is not possible to get valid non-wildcard TLS certificates because CA/Browser Forum requirements
63            // explicitly state that domains should start with a letter, digit or hyphen:
64            // https://github.com/cabforum/servercert/blob/24f38fd4765e019db8bb1a8c56bf63c7115ce0b0/docs/BR.md
65        } else if let Some(hash) = &self.spki_hash
66            && spki_hash(&spki) == *hash
67        {
68            // Last time we successfully connected to this hostname with TLS checks,
69            // SPKI had this hash.
70            // It does not matter if certificate has now expired.
71        } else {
72            // verify_server_cert_signed_by_trust_anchor does no revocation checking:
73            // <https://docs.rs/rustls/0.23.37/rustls/client/fn.verify_server_cert_signed_by_trust_anchor.html>
74            // We don't do it either.
75            verify_server_cert_signed_by_trust_anchor(
76                &parsed_certificate,
77                &self.root_cert_store,
78                intermediates,
79                now,
80                provider.signature_verification_algorithms.all,
81            )?;
82        }
83
84        // Verify server name unconditionally.
85        //
86        // We do this even for self-signed certificates when hostname starts with `_`
87        // so we don't try to connect to captive portals
88        // and fail on MITM certificates if they are generated once
89        // and reused for all hostnames.
90        verify_server_name(&parsed_certificate, server_name)?;
91        Ok(rustls::client::danger::ServerCertVerified::assertion())
92    }
93
94    fn verify_tls12_signature(
95        &self,
96        message: &[u8],
97        cert: &CertificateDer<'_>,
98        dss: &rustls::DigitallySignedStruct,
99    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
100        let provider = rustls::crypto::ring::default_provider();
101        let supported_schemes = &provider.signature_verification_algorithms;
102        rustls::crypto::verify_tls12_signature(message, cert, dss, supported_schemes)
103    }
104
105    fn verify_tls13_signature(
106        &self,
107        message: &[u8],
108        cert: &CertificateDer<'_>,
109        dss: &rustls::DigitallySignedStruct,
110    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
111        let provider = rustls::crypto::ring::default_provider();
112        let supported_schemes = &provider.signature_verification_algorithms;
113        rustls::crypto::verify_tls13_signature(message, cert, dss, supported_schemes)
114    }
115
116    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
117        let provider = rustls::crypto::ring::default_provider();
118        provider
119            .signature_verification_algorithms
120            .supported_schemes()
121    }
122}