iota_tls/
verifier.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{collections::BTreeSet, sync::Arc};
6
7use arc_swap::ArcSwap;
8use fastcrypto::{ed25519::Ed25519PublicKey, traits::ToFromBytes};
9use rustls::{
10    crypto::WebPkiSupportedAlgorithms,
11    pki_types::{
12        CertificateDer, PrivateKeyDer, ServerName, SignatureVerificationAlgorithm, TrustAnchor,
13        UnixTime,
14    },
15};
16
17static SUPPORTED_SIG_ALGS: &[&dyn SignatureVerificationAlgorithm] = &[webpki::ring::ED25519];
18
19static SUPPORTED_ALGORITHMS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms {
20    all: SUPPORTED_SIG_ALGS,
21    mapping: &[(rustls::SignatureScheme::ED25519, SUPPORTED_SIG_ALGS)],
22};
23
24/// The Allower trait provides an interface for callers to inject decisions
25/// whether to allow a cert to be verified or not.  This does not prform actual
26/// cert validation it only acts as a gatekeeper to decide if we should even
27/// try.  For example, we may want to filter our actions to well known public
28/// keys.
29pub trait Allower: std::fmt::Debug + Send + Sync {
30    // TODO: change allower interface to use raw key bytes.
31    fn allowed(&self, key: &Ed25519PublicKey) -> bool;
32}
33
34/// AllowAll will allow all public certificates to be validated, it fails open
35#[derive(Debug, Clone, Default)]
36pub struct AllowAll;
37
38impl Allower for AllowAll {
39    fn allowed(&self, _: &Ed25519PublicKey) -> bool {
40        true
41    }
42}
43
44/// AllowPublicKeys restricts keys to those that are found in the member set.
45/// non-members will not be allowed.
46#[derive(Debug, Clone, Default)]
47pub struct AllowPublicKeys {
48    inner: Arc<ArcSwap<BTreeSet<Ed25519PublicKey>>>,
49}
50
51impl AllowPublicKeys {
52    pub fn new(allowed: BTreeSet<Ed25519PublicKey>) -> Self {
53        Self {
54            inner: Arc::new(ArcSwap::from_pointee(allowed)),
55        }
56    }
57
58    pub fn update(&self, new_allowed: BTreeSet<Ed25519PublicKey>) {
59        self.inner.store(Arc::new(new_allowed));
60    }
61}
62
63impl Allower for AllowPublicKeys {
64    fn allowed(&self, key: &Ed25519PublicKey) -> bool {
65        self.inner.load().contains(key)
66    }
67}
68
69/// A `rustls::server::ClientCertVerifier` that will ensure that every client
70/// provides a valid, expected certificate and that the client's public key is
71/// in the validator set.
72#[derive(Clone, Debug)]
73pub struct ClientCertVerifier<A> {
74    allower: A,
75    name: String,
76}
77
78impl<A> ClientCertVerifier<A> {
79    pub fn new(allower: A, name: String) -> Self {
80        Self { allower, name }
81    }
82}
83
84impl<A: Allower + 'static> ClientCertVerifier<A> {
85    pub fn rustls_server_config(
86        self,
87        certificates: Vec<CertificateDer<'static>>,
88        private_key: PrivateKeyDer<'static>,
89    ) -> Result<rustls::ServerConfig, rustls::Error> {
90        let mut config = rustls::ServerConfig::builder_with_provider(Arc::new(
91            rustls::crypto::ring::default_provider(),
92        ))
93        .with_safe_default_protocol_versions()?
94        .with_client_cert_verifier(std::sync::Arc::new(self))
95        .with_single_cert(certificates, private_key)?;
96        config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
97
98        Ok(config)
99    }
100}
101
102impl<A: Allower> rustls::server::danger::ClientCertVerifier for ClientCertVerifier<A> {
103    fn offer_client_auth(&self) -> bool {
104        true
105    }
106
107    fn client_auth_mandatory(&self) -> bool {
108        true
109    }
110
111    fn root_hint_subjects(&self) -> &[rustls::DistinguishedName] {
112        // Since we're relying on self-signed certificates and not on CAs, continue the
113        // handshake without passing a list of CA DNs
114        &[]
115    }
116
117    // Verifies this is a valid ed25519 self-signed certificate
118    // 1. we prepare arguments for webpki's certificate verification (following the
119    //    rustls implementation) placing the public key at the root of the
120    //    certificate chain (as it should be for a self-signed certificate)
121    // 2. we call webpki's certificate verification
122    fn verify_client_cert(
123        &self,
124        end_entity: &CertificateDer,
125        intermediates: &[CertificateDer],
126        now: UnixTime,
127    ) -> Result<rustls::server::danger::ClientCertVerified, rustls::Error> {
128        // Step 1: Check this matches the key we expect
129        let public_key = public_key_from_certificate(end_entity)?;
130
131        if !self.allower.allowed(&public_key) {
132            return Err(rustls::Error::General(format!(
133                "invalid certificate: {public_key:?} is not in the validator set",
134            )));
135        }
136
137        // Step 2: verify the certificate signature and server name with webpki.
138        verify_self_signed_cert(
139            end_entity,
140            intermediates,
141            webpki::KeyUsage::client_auth(),
142            &self.name,
143            now,
144        )
145        .map(|_| rustls::server::danger::ClientCertVerified::assertion())
146    }
147
148    fn verify_tls12_signature(
149        &self,
150        message: &[u8],
151        cert: &CertificateDer<'_>,
152        dss: &rustls::DigitallySignedStruct,
153    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
154        rustls::crypto::verify_tls12_signature(message, cert, dss, &SUPPORTED_ALGORITHMS)
155    }
156
157    fn verify_tls13_signature(
158        &self,
159        message: &[u8],
160        cert: &CertificateDer<'_>,
161        dss: &rustls::DigitallySignedStruct,
162    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
163        rustls::crypto::verify_tls13_signature(message, cert, dss, &SUPPORTED_ALGORITHMS)
164    }
165
166    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
167        SUPPORTED_ALGORITHMS.supported_schemes()
168    }
169}
170
171/// A `rustls::client::ServerCertVerifier` that ensures the client only connects
172/// with the expected server.
173#[derive(Clone, Debug)]
174pub struct ServerCertVerifier {
175    public_key: Ed25519PublicKey,
176    name: String,
177}
178
179impl ServerCertVerifier {
180    pub fn new(public_key: Ed25519PublicKey, name: String) -> Self {
181        Self { public_key, name }
182    }
183
184    pub fn rustls_client_config_with_client_auth(
185        self,
186        certificates: Vec<CertificateDer<'static>>,
187        private_key: PrivateKeyDer<'static>,
188    ) -> Result<rustls::ClientConfig, rustls::Error> {
189        rustls::ClientConfig::builder_with_provider(Arc::new(
190            rustls::crypto::ring::default_provider(),
191        ))
192        .with_safe_default_protocol_versions()?
193        .dangerous()
194        .with_custom_certificate_verifier(std::sync::Arc::new(self))
195        .with_client_auth_cert(certificates, private_key)
196    }
197
198    pub fn rustls_client_config_with_no_client_auth(
199        self,
200    ) -> Result<rustls::ClientConfig, rustls::Error> {
201        Ok(rustls::ClientConfig::builder_with_provider(Arc::new(
202            rustls::crypto::ring::default_provider(),
203        ))
204        .with_safe_default_protocol_versions()?
205        .dangerous()
206        .with_custom_certificate_verifier(std::sync::Arc::new(self))
207        .with_no_client_auth())
208    }
209}
210
211impl rustls::client::danger::ServerCertVerifier for ServerCertVerifier {
212    fn verify_server_cert(
213        &self,
214        end_entity: &CertificateDer<'_>,
215        intermediates: &[CertificateDer<'_>],
216        _server_name: &ServerName,
217        _ocsp_response: &[u8],
218        now: UnixTime,
219    ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
220        let public_key = public_key_from_certificate(end_entity)?;
221        if public_key != self.public_key {
222            return Err(rustls::Error::General(format!(
223                "invalid certificate: {public_key:?} is not the expected server public key",
224            )));
225        }
226
227        verify_self_signed_cert(
228            end_entity,
229            intermediates,
230            webpki::KeyUsage::server_auth(),
231            &self.name,
232            now,
233        )
234        .map(|_| rustls::client::danger::ServerCertVerified::assertion())
235    }
236
237    fn verify_tls12_signature(
238        &self,
239        message: &[u8],
240        cert: &CertificateDer<'_>,
241        dss: &rustls::DigitallySignedStruct,
242    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
243        rustls::crypto::verify_tls12_signature(message, cert, dss, &SUPPORTED_ALGORITHMS)
244    }
245
246    fn verify_tls13_signature(
247        &self,
248        message: &[u8],
249        cert: &CertificateDer<'_>,
250        dss: &rustls::DigitallySignedStruct,
251    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
252        rustls::crypto::verify_tls13_signature(message, cert, dss, &SUPPORTED_ALGORITHMS)
253    }
254
255    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
256        SUPPORTED_ALGORITHMS.supported_schemes()
257    }
258}
259
260// Verifies this is a valid ed25519 self-signed certificate
261// 1. we prepare arguments for webpki's certificate verification (following the
262//    rustls implementation) placing the public key at the root of the
263//    certificate chain (as it should be for a self-signed certificate)
264// 2. we call webpki's certificate verification
265fn verify_self_signed_cert(
266    end_entity: &CertificateDer,
267    intermediates: &[CertificateDer],
268    usage: webpki::KeyUsage,
269    name: &str,
270    now: UnixTime,
271) -> Result<(), rustls::Error> {
272    // Check we're receiving correctly signed data with the expected key
273    // Step 1: prepare arguments
274    let (cert, chain, trustroots) = prepare_for_self_signed(end_entity, intermediates)?;
275
276    // Step 2: call verification from webpki
277    let verified_cert = cert
278        .verify_for_usage(
279            SUPPORTED_SIG_ALGS,
280            &trustroots,
281            chain,
282            now,
283            usage,
284            None,
285            None,
286        )
287        .map_err(pki_error)?;
288
289    // Ensure the cert is valid for the network name
290    let subject_name =
291        ServerName::try_from(name).map_err(|_| rustls::Error::UnsupportedNameType)?;
292    verified_cert
293        .end_entity()
294        .verify_is_valid_for_subject_name(&subject_name)
295        .map_err(pki_error)
296}
297
298type CertChainAndRoots<'a> = (
299    webpki::EndEntityCert<'a>,
300    &'a [CertificateDer<'a>],
301    Vec<TrustAnchor<'a>>,
302);
303
304// This prepares arguments for webpki, including a trust anchor which is the end
305// entity of the certificate (which embodies a self-signed certificate by
306// definition)
307fn prepare_for_self_signed<'a>(
308    end_entity: &'a CertificateDer,
309    intermediates: &'a [CertificateDer],
310) -> Result<CertChainAndRoots<'a>, rustls::Error> {
311    // EE cert must appear first.
312    let cert = webpki::EndEntityCert::try_from(end_entity).map_err(pki_error)?;
313
314    // reinterpret the certificate as a root, materializing the self-signed policy
315    let root = webpki::anchor_from_trusted_cert(end_entity).map_err(pki_error)?;
316
317    Ok((cert, intermediates, vec![root]))
318}
319
320fn pki_error(error: webpki::Error) -> rustls::Error {
321    use webpki::Error::*;
322    match error {
323        BadDer | BadDerTime => {
324            rustls::Error::InvalidCertificate(rustls::CertificateError::BadEncoding)
325        }
326        InvalidSignatureForPublicKey
327        | UnsupportedSignatureAlgorithm
328        | UnsupportedSignatureAlgorithmForPublicKey => {
329            rustls::Error::InvalidCertificate(rustls::CertificateError::BadSignature)
330        }
331        CertNotValidForName(_) => {
332            rustls::Error::InvalidCertificate(rustls::CertificateError::NotValidForName)
333        }
334        e => rustls::Error::General(format!("invalid peer certificate: {e}")),
335    }
336}
337
338/// Extracts the public key from a certificate.
339pub fn public_key_from_certificate(
340    certificate: &CertificateDer,
341) -> Result<Ed25519PublicKey, rustls::Error> {
342    use x509_parser::{certificate::X509Certificate, prelude::FromDer};
343
344    let cert = X509Certificate::from_der(certificate.as_ref())
345        .map_err(|e| rustls::Error::General(e.to_string()))?;
346    let spki = cert.1.public_key();
347    let public_key_bytes =
348        <ed25519::pkcs8::PublicKeyBytes as pkcs8::DecodePublicKey>::from_public_key_der(spki.raw)
349            .map_err(|e| rustls::Error::General(format!("invalid ed25519 public key: {e}")))?;
350
351    let public_key = Ed25519PublicKey::from_bytes(public_key_bytes.as_ref())
352        .map_err(|e| rustls::Error::General(format!("invalid ed25519 public key: {e}")))?;
353    Ok(public_key)
354}