sui_tls/
verifier.rs

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