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: {:?} is not in the validator set",
134                public_key,
135            )));
136        }
137
138        // Step 2: verify the certificate signature and server name with webpki.
139        verify_self_signed_cert(
140            end_entity,
141            intermediates,
142            webpki::KeyUsage::client_auth(),
143            &self.name,
144            now,
145        )
146        .map(|_| rustls::server::danger::ClientCertVerified::assertion())
147    }
148
149    fn verify_tls12_signature(
150        &self,
151        message: &[u8],
152        cert: &CertificateDer<'_>,
153        dss: &rustls::DigitallySignedStruct,
154    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
155        rustls::crypto::verify_tls12_signature(message, cert, dss, &SUPPORTED_ALGORITHMS)
156    }
157
158    fn verify_tls13_signature(
159        &self,
160        message: &[u8],
161        cert: &CertificateDer<'_>,
162        dss: &rustls::DigitallySignedStruct,
163    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
164        rustls::crypto::verify_tls13_signature(message, cert, dss, &SUPPORTED_ALGORITHMS)
165    }
166
167    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
168        SUPPORTED_ALGORITHMS.supported_schemes()
169    }
170}
171
172/// A `rustls::client::ServerCertVerifier` that ensures the client only connects
173/// with the expected server.
174#[derive(Clone, Debug)]
175pub struct ServerCertVerifier {
176    public_key: Ed25519PublicKey,
177    name: String,
178}
179
180impl ServerCertVerifier {
181    pub fn new(public_key: Ed25519PublicKey, name: String) -> Self {
182        Self { public_key, name }
183    }
184
185    pub fn rustls_client_config(
186        self,
187        certificates: Vec<CertificateDer<'static>>,
188        private_key: PrivateKeyDer<'static>,
189    ) -> Result<rustls::ClientConfig, rustls::Error> {
190        let mut config = rustls::ClientConfig::builder_with_provider(Arc::new(
191            rustls::crypto::ring::default_provider(),
192        ))
193        .with_safe_default_protocol_versions()?
194        .dangerous()
195        .with_custom_certificate_verifier(std::sync::Arc::new(self))
196        .with_client_auth_cert(certificates, private_key)?;
197        config.alpn_protocols = vec![b"h2".to_vec()];
198        Ok(config)
199    }
200}
201
202impl rustls::client::danger::ServerCertVerifier for ServerCertVerifier {
203    fn verify_server_cert(
204        &self,
205        end_entity: &CertificateDer<'_>,
206        intermediates: &[CertificateDer<'_>],
207        _server_name: &ServerName,
208        _ocsp_response: &[u8],
209        now: UnixTime,
210    ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
211        let public_key = public_key_from_certificate(end_entity)?;
212        if public_key != self.public_key {
213            return Err(rustls::Error::General(format!(
214                "invalid certificate: {:?} is not the expected server public key",
215                public_key,
216            )));
217        }
218
219        verify_self_signed_cert(
220            end_entity,
221            intermediates,
222            webpki::KeyUsage::server_auth(),
223            &self.name,
224            now,
225        )
226        .map(|_| rustls::client::danger::ServerCertVerified::assertion())
227    }
228
229    fn verify_tls12_signature(
230        &self,
231        message: &[u8],
232        cert: &CertificateDer<'_>,
233        dss: &rustls::DigitallySignedStruct,
234    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
235        rustls::crypto::verify_tls12_signature(message, cert, dss, &SUPPORTED_ALGORITHMS)
236    }
237
238    fn verify_tls13_signature(
239        &self,
240        message: &[u8],
241        cert: &CertificateDer<'_>,
242        dss: &rustls::DigitallySignedStruct,
243    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
244        rustls::crypto::verify_tls13_signature(message, cert, dss, &SUPPORTED_ALGORITHMS)
245    }
246
247    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
248        SUPPORTED_ALGORITHMS.supported_schemes()
249    }
250}
251
252// Verifies this is a valid ed25519 self-signed certificate
253// 1. we prepare arguments for webpki's certificate verification (following the
254//    rustls implementation) placing the public key at the root of the
255//    certificate chain (as it should be for a self-signed certificate)
256// 2. we call webpki's certificate verification
257fn verify_self_signed_cert(
258    end_entity: &CertificateDer,
259    intermediates: &[CertificateDer],
260    usage: webpki::KeyUsage,
261    name: &str,
262    now: UnixTime,
263) -> Result<(), rustls::Error> {
264    // Check we're receiving correctly signed data with the expected key
265    // Step 1: prepare arguments
266    let (cert, chain, trustroots) = prepare_for_self_signed(end_entity, intermediates)?;
267
268    // Step 2: call verification from webpki
269    let verified_cert = cert
270        .verify_for_usage(
271            SUPPORTED_SIG_ALGS,
272            &trustroots,
273            chain,
274            now,
275            usage,
276            None,
277            None,
278        )
279        .map_err(pki_error)?;
280
281    // Ensure the cert is valid for the network name
282    let subject_name =
283        ServerName::try_from(name).map_err(|_| rustls::Error::UnsupportedNameType)?;
284    verified_cert
285        .end_entity()
286        .verify_is_valid_for_subject_name(&subject_name)
287        .map_err(pki_error)
288}
289
290type CertChainAndRoots<'a> = (
291    webpki::EndEntityCert<'a>,
292    &'a [CertificateDer<'a>],
293    Vec<TrustAnchor<'a>>,
294);
295
296// This prepares arguments for webpki, including a trust anchor which is the end
297// entity of the certificate (which embodies a self-signed certificate by
298// definition)
299fn prepare_for_self_signed<'a>(
300    end_entity: &'a CertificateDer,
301    intermediates: &'a [CertificateDer],
302) -> Result<CertChainAndRoots<'a>, rustls::Error> {
303    // EE cert must appear first.
304    let cert = webpki::EndEntityCert::try_from(end_entity).map_err(pki_error)?;
305
306    // reinterpret the certificate as a root, materializing the self-signed policy
307    let root = webpki::anchor_from_trusted_cert(end_entity).map_err(pki_error)?;
308
309    Ok((cert, intermediates, vec![root]))
310}
311
312fn pki_error(error: webpki::Error) -> rustls::Error {
313    use webpki::Error::*;
314    match error {
315        BadDer | BadDerTime => {
316            rustls::Error::InvalidCertificate(rustls::CertificateError::BadEncoding)
317        }
318        InvalidSignatureForPublicKey
319        | UnsupportedSignatureAlgorithm
320        | UnsupportedSignatureAlgorithmForPublicKey => {
321            rustls::Error::InvalidCertificate(rustls::CertificateError::BadSignature)
322        }
323        CertNotValidForName(_) => {
324            rustls::Error::InvalidCertificate(rustls::CertificateError::NotValidForName)
325        }
326        e => rustls::Error::General(format!("invalid peer certificate: {e}")),
327    }
328}
329
330/// Extracts the public key from a certificate.
331pub fn public_key_from_certificate(
332    certificate: &CertificateDer,
333) -> Result<Ed25519PublicKey, rustls::Error> {
334    use x509_parser::{certificate::X509Certificate, prelude::FromDer};
335
336    let cert = X509Certificate::from_der(certificate.as_ref())
337        .map_err(|e| rustls::Error::General(e.to_string()))?;
338    let spki = cert.1.public_key();
339    let public_key_bytes =
340        <ed25519::pkcs8::PublicKeyBytes as pkcs8::DecodePublicKey>::from_public_key_der(spki.raw)
341            .map_err(|e| rustls::Error::General(format!("invalid ed25519 public key: {e}")))?;
342
343    let public_key = Ed25519PublicKey::from_bytes(public_key_bytes.as_ref())
344        .map_err(|e| rustls::Error::General(format!("invalid ed25519 public key: {e}")))?;
345    Ok(public_key)
346}