Skip to main content

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