iroh_net/tls/
verifier.rs

1//! TLS 1.3 certificates and handshakes handling for libp2p
2//!
3//! This module handles a verification of a client/server certificate chain
4//! and signatures allegedly by the given certificates.
5//!
6//! Based on rust-libp2p/transports/tls/src/verifier.rs originally licensed under MIT by Parity
7//! Technologies (UK) Ltd.
8use std::sync::Arc;
9
10use rustls::{
11    client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
12    pki_types::CertificateDer as Certificate,
13    server::danger::{ClientCertVerified, ClientCertVerifier},
14    CertificateError, DigitallySignedStruct, DistinguishedName, OtherError, PeerMisbehaved,
15    SignatureScheme, SupportedProtocolVersion,
16};
17
18use super::certificate;
19use crate::key::PublicKey;
20
21/// The protocol versions supported by this verifier.
22///
23/// The spec says:
24///
25/// > The libp2p handshake uses TLS 1.3 (and higher).
26/// > Endpoints MUST NOT negotiate lower TLS versions.
27pub static PROTOCOL_VERSIONS: &[&SupportedProtocolVersion] = &[&rustls::version::TLS13];
28
29/// Implementation of the `rustls` certificate verification traits for libp2p.
30///
31/// Only TLS 1.3 is supported. TLS 1.2 should be disabled in the configuration of `rustls`.
32#[derive(Debug)]
33pub struct Libp2pCertificateVerifier {
34    /// The peer ID we intend to connect to
35    remote_peer_id: Option<PublicKey>,
36}
37
38/// libp2p requires the following of X.509 server certificate chains:
39///
40/// - Exactly one certificate must be presented.
41/// - The certificate must be self-signed.
42/// - The certificate must have a valid libp2p extension that includes a
43///   signature of its public key.
44impl Libp2pCertificateVerifier {
45    pub fn new() -> Self {
46        Self {
47            remote_peer_id: None,
48        }
49    }
50    pub fn with_remote_peer_id(remote_peer_id: Option<PublicKey>) -> Self {
51        Self { remote_peer_id }
52    }
53
54    /// Return the list of SignatureSchemes that this verifier will handle,
55    /// in `verify_tls12_signature` and `verify_tls13_signature` calls.
56    ///
57    /// This should be in priority order, with the most preferred first.
58    fn verification_schemes() -> Vec<SignatureScheme> {
59        vec![
60            // TODO SignatureScheme::ECDSA_NISTP521_SHA512 is not supported by `ring` yet
61            SignatureScheme::ECDSA_NISTP384_SHA384,
62            SignatureScheme::ECDSA_NISTP256_SHA256,
63            // TODO SignatureScheme::ED448 is not supported by `ring` yet
64            SignatureScheme::ED25519,
65            // In particular, RSA SHOULD NOT be used.
66        ]
67    }
68}
69
70impl ServerCertVerifier for Libp2pCertificateVerifier {
71    fn verify_server_cert(
72        &self,
73        end_entity: &Certificate,
74        intermediates: &[Certificate],
75        _server_name: &rustls::pki_types::ServerName,
76        _ocsp_response: &[u8],
77        _now: rustls::pki_types::UnixTime,
78    ) -> Result<ServerCertVerified, rustls::Error> {
79        let peer_id = verify_presented_certs(end_entity, intermediates)?;
80
81        if let Some(ref remote_peer_id) = self.remote_peer_id {
82            // The public host key allows the peer to calculate the peer ID of the peer
83            // it is connecting to. Clients MUST verify that the peer ID derived from
84            // the certificate matches the peer ID they intended to connect to,
85            // and MUST abort the connection if there is a mismatch.
86            if remote_peer_id != &peer_id {
87                return Err(rustls::Error::PeerMisbehaved(
88                    PeerMisbehaved::BadCertChainExtensions,
89                ));
90            }
91        }
92
93        Ok(ServerCertVerified::assertion())
94    }
95
96    fn verify_tls12_signature(
97        &self,
98        _message: &[u8],
99        _cert: &Certificate,
100        _dss: &DigitallySignedStruct,
101    ) -> Result<HandshakeSignatureValid, rustls::Error> {
102        unreachable!("`PROTOCOL_VERSIONS` only allows TLS 1.3")
103    }
104
105    fn verify_tls13_signature(
106        &self,
107        message: &[u8],
108        cert: &Certificate,
109        dss: &DigitallySignedStruct,
110    ) -> Result<HandshakeSignatureValid, rustls::Error> {
111        verify_tls13_signature(cert, dss.scheme, message, dss.signature())
112    }
113
114    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
115        Self::verification_schemes()
116    }
117}
118
119/// libp2p requires the following of X.509 client certificate chains:
120///
121/// - Exactly one certificate must be presented. In particular, client
122///   authentication is mandatory in libp2p.
123/// - The certificate must be self-signed.
124/// - The certificate must have a valid libp2p extension that includes a
125///   signature of its public key.
126impl ClientCertVerifier for Libp2pCertificateVerifier {
127    fn offer_client_auth(&self) -> bool {
128        true
129    }
130
131    fn verify_client_cert(
132        &self,
133        end_entity: &Certificate,
134        intermediates: &[Certificate],
135        _now: rustls::pki_types::UnixTime,
136    ) -> Result<ClientCertVerified, rustls::Error> {
137        verify_presented_certs(end_entity, intermediates)?;
138
139        Ok(ClientCertVerified::assertion())
140    }
141
142    fn verify_tls12_signature(
143        &self,
144        _message: &[u8],
145        _cert: &Certificate,
146        _dss: &DigitallySignedStruct,
147    ) -> Result<HandshakeSignatureValid, rustls::Error> {
148        unreachable!("`PROTOCOL_VERSIONS` only allows TLS 1.3")
149    }
150
151    fn verify_tls13_signature(
152        &self,
153        message: &[u8],
154        cert: &Certificate,
155        dss: &DigitallySignedStruct,
156    ) -> Result<HandshakeSignatureValid, rustls::Error> {
157        verify_tls13_signature(cert, dss.scheme, message, dss.signature())
158    }
159
160    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
161        Self::verification_schemes()
162    }
163
164    fn root_hint_subjects(&self) -> &[DistinguishedName] {
165        &[][..]
166    }
167}
168
169/// When receiving the certificate chain, an endpoint
170/// MUST check these conditions and abort the connection attempt if
171/// (a) the presented certificate is not yet valid, OR
172/// (b) if it is expired.
173/// Endpoints MUST abort the connection attempt if more than one certificate is received,
174/// or if the certificate’s self-signature is not valid.
175fn verify_presented_certs(
176    end_entity: &Certificate,
177    intermediates: &[Certificate],
178) -> Result<PublicKey, rustls::Error> {
179    if !intermediates.is_empty() {
180        return Err(rustls::Error::General(
181            "libp2p-tls requires exactly one certificate".into(),
182        ));
183    }
184
185    let cert = certificate::parse(end_entity)?;
186
187    Ok(cert.peer_id())
188}
189
190fn verify_tls13_signature(
191    cert: &Certificate,
192    signature_scheme: SignatureScheme,
193    message: &[u8],
194    signature: &[u8],
195) -> Result<HandshakeSignatureValid, rustls::Error> {
196    certificate::parse(cert)?.verify_signature(signature_scheme, message, signature)?;
197
198    Ok(HandshakeSignatureValid::assertion())
199}
200
201impl From<certificate::ParseError> for rustls::Error {
202    fn from(certificate::ParseError(e): certificate::ParseError) -> Self {
203        use webpki::Error::*;
204        match e {
205            BadDer => rustls::Error::InvalidCertificate(CertificateError::BadEncoding),
206            e => {
207                rustls::Error::InvalidCertificate(CertificateError::Other(OtherError(Arc::new(e))))
208            }
209        }
210    }
211}
212impl From<certificate::VerificationError> for rustls::Error {
213    fn from(certificate::VerificationError(e): certificate::VerificationError) -> Self {
214        use webpki::Error::*;
215        match e {
216            InvalidSignatureForPublicKey => {
217                rustls::Error::InvalidCertificate(CertificateError::BadSignature)
218            }
219            UnsupportedSignatureAlgorithm | UnsupportedSignatureAlgorithmForPublicKey => {
220                rustls::Error::InvalidCertificate(CertificateError::BadSignature)
221            }
222            e => {
223                rustls::Error::InvalidCertificate(CertificateError::Other(OtherError(Arc::new(e))))
224            }
225        }
226    }
227}