Skip to main content

deboa_compio/client/tls/
rustls.rs

1//! TLS implementation using rustls
2
3use crate::cert::{DeboaCertificate, DeboaIdentity};
4use deboa::{
5    errors::{ConnectionError, DeboaError},
6    Result,
7};
8use rustls::{
9    crypto::CryptoProvider,
10    pki_types::{CertificateDer, PrivateKeyDer},
11    ClientConfig,
12};
13
14pub(crate) fn default_provider() -> CryptoProvider {
15    #[cfg(feature = "__rustls_aws_lc_rs")]
16    return rustls::crypto::aws_lc_rs::default_provider();
17    #[cfg(feature = "__rustls_ring")]
18    return rustls::crypto::ring::default_provider();
19}
20
21#[inline]
22pub(crate) fn alpn() -> Vec<Vec<u8>> {
23    vec![
24        #[cfg(feature = "http3")]
25        b"h3".to_vec(),
26        #[cfg(feature = "http2")]
27        b"h2".to_vec(),
28        #[cfg(feature = "http1")]
29        b"http/1.1".to_vec(),
30    ]
31}
32
33/// Builder for TLS connections using rustls
34pub struct TlsConnectionBuilder<'a> {
35    identity: Option<&'a DeboaIdentity>,
36    certificate: Option<&'a DeboaCertificate>,
37    skip_server_verification: bool,
38    alpn: Vec<Vec<u8>>,
39    provider: CryptoProvider,
40}
41
42impl Default for TlsConnectionBuilder<'_> {
43    fn default() -> Self {
44        Self {
45            identity: None,
46            certificate: None,
47            skip_server_verification: false,
48            alpn: alpn(),
49            provider: default_provider(),
50        }
51    }
52}
53
54impl<'a> TlsConnectionBuilder<'a> {
55    /// Set the identity to use for the connection
56    pub fn identity(mut self, identity: Option<&'a DeboaIdentity>) -> Self {
57        self.identity = identity;
58        self
59    }
60
61    /// Set the certificate to use for the connection
62    pub fn certificate(mut self, certificate: Option<&'a DeboaCertificate>) -> Self {
63        self.certificate = certificate;
64        self
65    }
66
67    /// Skip server verification
68    pub fn skip_server_verification(mut self, skip_server_verification: bool) -> Self {
69        self.skip_server_verification = skip_server_verification;
70        self
71    }
72
73    /// Set the ALPN protocols to use for the connection
74    pub fn alpn(mut self, alpn: Vec<Vec<u8>>) -> Self {
75        self.alpn = alpn;
76        self
77    }
78
79    /// Build the TLS client configuration
80    pub fn build_config(self) -> Result<ClientConfig> {
81        let client_config = {
82            if self.skip_server_verification {
83                ClientConfig::builder()
84                    .dangerous()
85                    .with_custom_certificate_verifier(verify::SkipServerVerification::new(
86                        self.provider,
87                    ))
88                    .with_no_client_auth()
89            } else {
90                #[cfg(feature = "__webpki_rustls_verifier")]
91                let config = {
92                    let config = ClientConfig::builder_with_provider(self.provider.into())
93                        .with_protocol_versions(rustls::ALL_VERSIONS)
94                        .map_err(|e| {
95                            DeboaError::Connection(ConnectionError::Tls {
96                                message: format!("Failed to set TLS version: {}", e),
97                            })
98                        })?;
99
100                    let mut root_store =
101                        rustls::RootCertStore { roots: webpki_roots::TLS_SERVER_ROOTS.to_vec() };
102                    let config = if let Some(ca) = self.certificate {
103                        let cert = ca
104                            .try_into()
105                            .map_err(|e| {
106                                DeboaError::Connection(ConnectionError::Tls {
107                                    message: format!("Invalid CA certificate: {}", e),
108                                })
109                            })?;
110
111                        root_store
112                            .add(cert)
113                            .map_err(|e| {
114                                DeboaError::Connection(ConnectionError::Tls {
115                                    message: format!(
116                                        "Could not add CA certificate to the store: {}",
117                                        e
118                                    ),
119                                })
120                            })?;
121
122                        config.with_root_certificates(root_store)
123                    } else {
124                        config.with_root_certificates(root_store)
125                    };
126
127                    config
128                };
129
130                #[cfg(feature = "__platform_rustls_verifier")]
131                let config = {
132                    use rustls_platform_verifier::BuilderVerifierExt;
133                    rustls::ClientConfig::builder_with_provider(default_provider())
134                        .with_protocol_versions(rustls::ALL_VERSIONS)
135                        .map_err(|e| {
136                            DeboaError::Connection(ConnectionError::Tls {
137                                message: format!("Failed to set TLS version: {}", e),
138                            })
139                        })?
140                        .with_platform_verifier()
141                };
142
143                let mut config = if let Some(id) = self.identity {
144                    let pair: (CertificateDer<'_>, PrivateKeyDer<'_>) = id
145                        .try_into()
146                        .map_err(|e| {
147                            DeboaError::Connection(ConnectionError::Tls {
148                                message: format!("Invalid client identity: {}", e),
149                            })
150                        })?;
151
152                    config
153                        .with_client_auth_cert(vec![pair.0], pair.1)
154                        .map_err(|e| {
155                            DeboaError::Connection(ConnectionError::Tls {
156                                message: format!("Failed to set client identity: {}", e),
157                            })
158                        })?
159                } else {
160                    config.with_no_client_auth()
161                };
162
163                config.enable_early_data = true;
164
165                config.alpn_protocols = self.alpn;
166
167                config
168            }
169        };
170
171        Ok(client_config)
172    }
173}
174
175#[cfg(any(feature = "http1", feature = "http2"))]
176/// TCP connection module for TLS
177pub mod tcp {
178    use compio::net::TcpStream;
179    use compio_tls::{TlsConnector, TlsStream};
180    use deboa::{
181        errors::{ConnectionError, DeboaError},
182        Result,
183    };
184    use rustls::ClientConfig;
185    use std::sync::Arc;
186
187    /// Establish a TLS connection over TCP
188    pub async fn connect(
189        config: ClientConfig,
190        inner_stream: TcpStream,
191        host: &str,
192    ) -> Result<TlsStream<TcpStream>> {
193        let connector = TlsConnector::from(Arc::new(config));
194
195        connector
196            .connect(host, inner_stream)
197            .await
198            .map_err(|e| {
199                DeboaError::Connection(ConnectionError::Tls {
200                    message: format!("Could not connect to server: {}", e),
201                })
202            })
203    }
204}
205
206#[cfg(feature = "http3")]
207/// UDP connection module for TLS
208pub mod udp {
209    use compio_quic::{Connection, Endpoint};
210    use deboa::{
211        errors::{ConnectionError, DeboaError},
212        Result,
213    };
214    use rustls::ClientConfig;
215    use std::{net::SocketAddr, sync::Arc};
216
217    /// Establish a TLS connection over UDP
218    pub async fn connect(
219        config: ClientConfig,
220        endpoint: &mut Endpoint,
221        socket_addr: SocketAddr,
222        host: &str,
223    ) -> Result<Connection> {
224        let quic_config =
225            compio_quic::crypto::rustls::QuicClientConfig::try_from(config).map_err(|e| {
226                DeboaError::Connection(ConnectionError::Tls {
227                    message: format!("Could not create QUIC client config: {}", e),
228                })
229            })?;
230
231        let client_config = compio_quic::ClientConfig::new(Arc::new(quic_config));
232
233        let conn = endpoint
234            .connect(socket_addr, host, Some(client_config))
235            .map_err(|e| {
236                DeboaError::Connection(ConnectionError::Udp {
237                    message: format!("Could not connect to server: {}", e),
238                })
239            })?;
240
241        let conn = conn
242            .await
243            .map_err(|e| {
244                DeboaError::Connection(ConnectionError::Udp {
245                    message: format!("Could not connect to server: {}", e),
246                })
247            })?;
248
249        Ok(conn)
250    }
251}
252
253pub(crate) mod verify {
254    use rustls::{
255        client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
256        crypto::CryptoProvider,
257        pki_types::{CertificateDer, ServerName, UnixTime},
258    };
259    use std::sync::Arc;
260
261    #[derive(Debug)]
262    pub(crate) struct SkipServerVerification(CryptoProvider);
263
264    impl SkipServerVerification {
265        pub(crate) fn new(provider: CryptoProvider) -> Arc<Self> {
266            Arc::new(Self(provider))
267        }
268    }
269
270    impl ServerCertVerifier for SkipServerVerification {
271        fn verify_server_cert(
272            &self,
273            _end_entity: &CertificateDer<'_>,
274            _intermediates: &[CertificateDer<'_>],
275            _server_name: &ServerName<'_>,
276            _ocsp: &[u8],
277            _now: UnixTime,
278        ) -> std::result::Result<ServerCertVerified, rustls::Error> {
279            Ok(ServerCertVerified::assertion())
280        }
281
282        fn verify_tls12_signature(
283            &self,
284            message: &[u8],
285            cert: &CertificateDer<'_>,
286            dss: &rustls::DigitallySignedStruct,
287        ) -> std::result::Result<HandshakeSignatureValid, rustls::Error> {
288            rustls::crypto::verify_tls12_signature(
289                message,
290                cert,
291                dss,
292                &self
293                    .0
294                    .signature_verification_algorithms,
295            )
296        }
297
298        fn verify_tls13_signature(
299            &self,
300            message: &[u8],
301            cert: &CertificateDer<'_>,
302            dss: &rustls::DigitallySignedStruct,
303        ) -> std::result::Result<HandshakeSignatureValid, rustls::Error> {
304            rustls::crypto::verify_tls13_signature(
305                message,
306                cert,
307                dss,
308                &self
309                    .0
310                    .signature_verification_algorithms,
311            )
312        }
313
314        fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
315            self.0
316                .signature_verification_algorithms
317                .supported_schemes()
318        }
319    }
320}