pkarr/extra/
tls.rs

1//! Implementation of [Tls](https://pkarr.org/tls) spec.
2//!
3
4use std::{fmt::Debug, sync::Arc};
5
6use ed25519_dalek::pkcs8::{Document, EncodePrivateKey, EncodePublicKey};
7use futures_lite::{pin, stream::block_on};
8use rustls::{
9    client::danger::{DangerousClientConfigBuilder, ServerCertVerified, ServerCertVerifier},
10    crypto::ring::sign::any_eddsa_type,
11    crypto::{verify_tls13_signature_with_raw_key, WebPkiSupportedAlgorithms},
12    pki_types::CertificateDer,
13    pki_types::SubjectPublicKeyInfoDer,
14    server::AlwaysResolvesServerRawPublicKeys,
15    sign::CertifiedKey,
16    CertificateError, ServerConfig, SignatureScheme,
17};
18
19use crate::{Client, Keypair, PublicKey};
20
21/// A custom certificate verifier for Pkarr public keys.
22///
23/// This verifier checks if the server's certificate matches the public key of a Pkarr endpoint.
24#[derive(Debug)]
25pub struct CertVerifier(Client);
26
27static SUPPORTED_ALGORITHMS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms {
28    all: &[webpki::ring::ED25519],
29    mapping: &[(SignatureScheme::ED25519, &[webpki::ring::ED25519])],
30};
31
32impl ServerCertVerifier for CertVerifier {
33    /// Verify Pkarr public keys.
34    ///
35    /// This method checks if the `endpoint_certificate` matches the public key of a Pkarr endpoint
36    /// resolvalbe from the `host_name`.
37    fn verify_server_cert(
38        &self,
39        endpoint_certificate: &rustls::pki_types::CertificateDer<'_>,
40        intermediates: &[rustls::pki_types::CertificateDer<'_>],
41        host_name: &rustls::pki_types::ServerName<'_>,
42        _ocsp_response: &[u8],
43        _now: rustls::pki_types::UnixTime,
44    ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
45        if !intermediates.is_empty() {
46            return Err(rustls::Error::InvalidCertificate(
47                CertificateError::UnknownIssuer,
48            ));
49        }
50
51        let end_entity_as_spki = SubjectPublicKeyInfoDer::from(endpoint_certificate.as_ref());
52        let expected_spki = end_entity_as_spki.as_ref();
53
54        let qname = host_name.to_str();
55
56        // Resolve HTTPS endpoints and hope that the cached SignedPackets didn't change
57        // since the last time we resolved endpoints to establish the connection in the
58        // first place.
59        //
60        // This won't be necessary if Reqwest enabled us to create a rustls configuration
61        // per connection.
62        //
63        // TODO: update after Reqwest enables this.
64        let stream = self.0.resolve_https_endpoints(&qname);
65        pin!(stream);
66        for endpoint in block_on(stream) {
67            if endpoint.public_key().to_public_key_der().as_bytes() == expected_spki {
68                return Ok(ServerCertVerified::assertion());
69            }
70        }
71
72        // Repeat for SVCB endpoints
73        let stream = self.0.resolve_svcb_endpoints(&qname);
74        pin!(stream);
75        for endpoint in block_on(stream) {
76            if endpoint.public_key().to_public_key_der().as_bytes() == expected_spki {
77                return Ok(ServerCertVerified::assertion());
78            }
79        }
80
81        Err(rustls::Error::InvalidCertificate(
82            CertificateError::UnknownIssuer,
83        ))
84    }
85
86    /// Verify a message signature using a raw public key and the first TLS 1.3 compatible
87    /// supported scheme.
88    fn verify_tls12_signature(
89        &self,
90        message: &[u8],
91        cert: &rustls::pki_types::CertificateDer<'_>,
92        dss: &rustls::DigitallySignedStruct,
93    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
94        verify_tls13_signature_with_raw_key(
95            message,
96            &SubjectPublicKeyInfoDer::from(cert.as_ref()),
97            dss,
98            &SUPPORTED_ALGORITHMS,
99        )
100    }
101
102    /// Verify a message signature using a raw public key and the first TLS 1.3 compatible
103    /// supported scheme.
104    fn verify_tls13_signature(
105        &self,
106        message: &[u8],
107        cert: &rustls::pki_types::CertificateDer<'_>,
108        dss: &rustls::DigitallySignedStruct,
109    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
110        verify_tls13_signature_with_raw_key(
111            message,
112            &SubjectPublicKeyInfoDer::from(cert.as_ref()),
113            dss,
114            &SUPPORTED_ALGORITHMS,
115        )
116    }
117
118    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
119        vec![SignatureScheme::ED25519]
120    }
121
122    fn requires_raw_public_keys(&self) -> bool {
123        true
124    }
125}
126
127impl CertVerifier {
128    pub(crate) fn new(pkarr_client: Client) -> Self {
129        CertVerifier(pkarr_client)
130    }
131}
132
133impl From<Client> for CertVerifier {
134    fn from(pkarr_client: Client) -> Self {
135        CertVerifier::new(pkarr_client)
136    }
137}
138
139impl From<Client> for rustls::ClientConfig {
140    /// Creates a [rustls::ClientConfig] that uses [rustls::crypto::ring::default_provider()]
141    /// and no client auth and follows the [tls for pkarr domains](https://pkarr.org/tls) spec.
142    ///
143    /// If you want more control, create a [CertVerifier] from this [Client] to use as a [custom certificate verifier][DangerousClientConfigBuilder::with_custom_certificate_verifier].
144    fn from(client: Client) -> Self {
145        let verifier: CertVerifier = client.into();
146
147        create_client_config_with_ring()
148            .with_custom_certificate_verifier(Arc::new(verifier))
149            .with_no_client_auth()
150    }
151}
152
153fn create_client_config_with_ring() -> DangerousClientConfigBuilder {
154    rustls::ClientConfig::builder_with_provider(rustls::crypto::ring::default_provider().into())
155        .with_safe_default_protocol_versions()
156        .expect("version supported by ring")
157        .dangerous()
158}
159
160impl Keypair {
161    /// Return a RawPublicKey certified key according to [RFC 7250](https://tools.ietf.org/html/rfc7250)
162    /// useful to use with [rustls::ConfigBuilder::with_cert_resolver] and [rustls::server::AlwaysResolvesServerRawPublicKeys]
163    pub fn to_rpk_certified_key(&self) -> CertifiedKey {
164        let client_private_key = any_eddsa_type(
165            &self
166                .0
167                .to_pkcs8_der()
168                .expect("Keypair::to_rpk_certificate: convert secret key to pkcs8 der")
169                .as_bytes()
170                .into(),
171        )
172        .expect("Keypair::to_rpk_certificate: convert KeyPair to rustls SigningKey");
173
174        let client_public_key = client_private_key
175            .public_key()
176            .expect("Keypair::to_rpk_certificate: load SPKI");
177        let client_public_key_as_cert = CertificateDer::from(client_public_key.to_vec());
178
179        CertifiedKey::new(vec![client_public_key_as_cert], client_private_key)
180    }
181
182    #[cfg(all(not(target_family = "wasm"), feature = "tls"))]
183    /// Create a [rustls::ServerConfig] using this keypair as a RawPublicKey certificate according to [RFC 7250](https://tools.ietf.org/html/rfc7250)
184    pub fn to_rpk_rustls_server_config(&self) -> ServerConfig {
185        let cert_resolver =
186            AlwaysResolvesServerRawPublicKeys::new(self.to_rpk_certified_key().into());
187
188        ServerConfig::builder_with_provider(rustls::crypto::ring::default_provider().into())
189            .with_safe_default_protocol_versions()
190            .expect("version supported by ring")
191            .with_no_client_auth()
192            .with_cert_resolver(std::sync::Arc::new(cert_resolver))
193    }
194}
195
196impl From<Keypair> for ServerConfig {
197    fn from(keypair: Keypair) -> Self {
198        keypair.to_rpk_rustls_server_config()
199    }
200}
201
202impl From<&Keypair> for ServerConfig {
203    fn from(keypair: &Keypair) -> Self {
204        keypair.to_rpk_rustls_server_config()
205    }
206}
207
208impl PublicKey {
209    /// Converts the public key to a DER-encoded document.
210    pub fn to_public_key_der(&self) -> Document {
211        self.0.to_public_key_der().expect("to_public_key_der")
212    }
213}