Skip to main content

webtrans_quinn/tls/
mod.rs

1//! TLS helpers for managing certificates and private keys.
2
3pub mod cert;
4pub mod key;
5
6use webtrans_proto::{Error, Result};
7
8use cert::SkipServerVerification;
9use rustls::client::{ClientConfig, WebPkiServerVerifier};
10use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
11use rustls::server::ServerConfig;
12use std::path::Path;
13use std::sync::Arc;
14
15/// Alias of [`rustls::server::ServerConfig`].
16pub type TlsServerConfig = rustls::server::ServerConfig;
17
18/// Alias of [`rustls::client::ClientConfig`].
19pub type TlsClientConfig = rustls::client::ClientConfig;
20
21/// Generate a self-signed certificate and private key (DER).
22pub fn generate_self_signed_pair_der(
23    subject_alt_names: Vec<String>,
24) -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
25    let cert = rcgen::generate_simple_self_signed(subject_alt_names)
26        .map_err(|e| Error::Tls(e.to_string()))?;
27
28    let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(cert.signing_key.serialize_der()));
29    let cert_chain = vec![CertificateDer::from(cert.cert)];
30    Ok((cert_chain, key))
31}
32
33/// Generate a self-signed certificate and private key (PEM).
34pub fn generate_self_signed_pair_pem(
35    subject_alt_names: Vec<String>,
36) -> Result<(Vec<String>, String)> {
37    let cert = rcgen::generate_simple_self_signed(subject_alt_names)
38        .map_err(|e| Error::Tls(e.to_string()))?;
39
40    let key = cert.signing_key.serialize_pem();
41    let cert_chain = vec![cert.cert.pem()];
42    Ok((cert_chain, key))
43}
44
45/// Load a certificate chain and private key from files.
46pub fn load_cert(
47    cert_path: &Path,
48    key_path: &Path,
49) -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
50    let cert_chain = cert::load_certs(cert_path)?;
51    let key = key::load_key(key_path)?;
52    Ok((cert_chain, key))
53}
54
55/// TLS configuration containing both client and server configurations.
56#[derive(Debug, Clone)]
57pub struct TlsConfig {
58    /// TLS client configuration used by outbound WebTransport connections.
59    pub client_config: ClientConfig,
60    /// TLS server configuration used by inbound WebTransport listeners.
61    pub server_config: ServerConfig,
62}
63
64impl TlsConfig {
65    /// Create a new TLS configuration with the specified certificate and private key.
66    pub fn with_cert(cert_path: &Path, key_path: &Path) -> Result<Self> {
67        let client_config = TlsClientConfigBuilder::new_with_native_certs()?
68            .with_alpn_protocols(vec![b"h3".to_vec()])
69            .build();
70
71        let server_config = TlsServerConfigBuilder::new_with_cert(cert_path, key_path)?
72            .with_alpn_protocols(vec![b"h3".to_vec()])
73            .build();
74
75        Ok(Self {
76            client_config,
77            server_config,
78        })
79    }
80
81    /// Create a new TLS configuration with self-signed certificates (localhost).
82    pub fn with_self_signed_certs() -> Result<Self> {
83        let client_config = TlsClientConfigBuilder::new_with_native_certs()?
84            .with_alpn_protocols(vec![b"h3".to_vec()])
85            .build();
86
87        let server_config =
88            TlsServerConfigBuilder::new_with_self_signed_certs(vec!["localhost".into()])?
89                .with_alpn_protocols(vec![b"h3".to_vec()])
90                .build();
91
92        Ok(Self {
93            client_config,
94            server_config,
95        })
96    }
97
98    /// Create a new TLS configuration with system certificates (server side uses self-signed localhost).
99    pub fn new_native_config() -> Result<Self> {
100        let client_config = TlsClientConfigBuilder::new_with_native_certs()?
101            .with_alpn_protocols(vec![b"h3".to_vec()])
102            .build();
103
104        let server_config =
105            TlsServerConfigBuilder::new_with_self_signed_certs(vec!["localhost".into()])?
106                .with_alpn_protocols(vec![b"h3".to_vec()])
107                .build();
108
109        Ok(Self {
110            client_config,
111            server_config,
112        })
113    }
114
115    /// Create a new TLS configuration with no certificate verification (testing only).
116    pub fn new_insecure_config() -> Result<Self> {
117        let client_config = TlsClientConfigBuilder::new_insecure()?
118            .with_alpn_protocols(vec![b"h3".to_vec()])
119            .build();
120
121        let server_config =
122            TlsServerConfigBuilder::new_with_self_signed_certs(vec!["localhost".into()])?
123                .with_alpn_protocols(vec![b"h3".to_vec()])
124                .build();
125
126        Ok(Self {
127            client_config,
128            server_config,
129        })
130    }
131}
132
133/// Server config builder (owned builder).
134#[derive(Debug, Clone)]
135pub struct TlsServerConfigBuilder {
136    inner: TlsServerConfig,
137}
138
139impl TlsServerConfigBuilder {
140    /// Build a server TLS config using an ephemeral self-signed certificate.
141    pub fn new_insecure(subject_alt_names: Vec<String>) -> Result<Self> {
142        let (certs, key) = generate_self_signed_pair_der(subject_alt_names)?;
143        let inner = ServerConfig::builder()
144            .with_no_client_auth()
145            .with_single_cert(certs, key)
146            .map_err(|e| Error::Tls(e.to_string()))?;
147        Ok(Self { inner })
148    }
149
150    /// Build a server TLS config from certificate and private key files.
151    pub fn new_with_cert(cert_path: &Path, key_path: &Path) -> Result<Self> {
152        let (certs, key) = load_cert(cert_path, key_path)?;
153        let inner = ServerConfig::builder()
154            .with_no_client_auth()
155            .with_single_cert(certs, key)
156            .map_err(|e| Error::Tls(e.to_string()))?;
157        Ok(Self { inner })
158    }
159
160    /// Build a server TLS config using generated self-signed certificates.
161    pub fn new_with_self_signed_certs(subject_alt_names: Vec<String>) -> Result<Self> {
162        Self::new_insecure(subject_alt_names)
163    }
164
165    /// Override ALPN protocol list.
166    pub fn with_alpn_protocols(mut self, protocols: Vec<Vec<u8>>) -> Self {
167        self.inner.alpn_protocols = protocols;
168        self
169    }
170
171    /// Finalize and return the server TLS config.
172    pub fn build(self) -> TlsServerConfig {
173        self.inner
174    }
175}
176
177/// Client config builder (owned builder).
178#[derive(Debug, Clone)]
179pub struct TlsClientConfigBuilder {
180    inner: TlsClientConfig,
181}
182
183impl TlsClientConfigBuilder {
184    /// Build a client TLS config that disables certificate verification.
185    pub fn new_insecure() -> Result<Self> {
186        let inner = ClientConfig::builder()
187            .dangerous()
188            .with_custom_certificate_verifier(SkipServerVerification::new())
189            .with_no_client_auth();
190        Ok(Self { inner })
191    }
192
193    /// Build a client TLS config from native system trust roots.
194    pub fn new_with_native_certs() -> Result<Self> {
195        let native_certs = cert::get_native_certs()?;
196        let inner = ClientConfig::builder()
197            .with_root_certificates(native_certs)
198            .with_no_client_auth();
199        Ok(Self { inner })
200    }
201
202    /// Build a client TLS config with a custom WebPKI verifier.
203    pub fn new_with_webpki_verifier(verifier: Arc<WebPkiServerVerifier>) -> Result<Self> {
204        let inner = ClientConfig::builder()
205            .with_webpki_verifier(verifier)
206            .with_no_client_auth();
207        Ok(Self { inner })
208    }
209
210    /// Override ALPN protocol list.
211    pub fn with_alpn_protocols(mut self, protocols: Vec<Vec<u8>>) -> Self {
212        self.inner.alpn_protocols = protocols;
213        self
214    }
215
216    /// Finalize and return the client TLS config.
217    pub fn build(self) -> TlsClientConfig {
218        self.inner
219    }
220}
221
222#[cfg(test)]
223mod tests {
224    use super::*;
225
226    #[test]
227    fn test_generate_self_signed_pair_der() {
228        let (cert_chain, key) = generate_self_signed_pair_der(vec!["localhost".into()]).unwrap();
229        let rustls_server_config = ServerConfig::builder()
230            .with_no_client_auth()
231            .with_single_cert(cert_chain, key);
232
233        if let Err(e) = rustls_server_config {
234            panic!("Failed to create ServerConfig: {e}");
235        }
236    }
237
238    #[test]
239    fn test_generate_self_signed_pair_pem() {
240        let (cert_chain, key) = generate_self_signed_pair_pem(vec!["localhost".into()]).unwrap();
241
242        let cert_path = Path::new("cert.pem");
243        let key_path = Path::new("key.pem");
244        std::fs::write(cert_path, cert_chain.join("\n")).unwrap();
245        std::fs::write(key_path, key).unwrap();
246
247        let (cert_chain, key) = load_cert(cert_path, key_path).unwrap();
248        let rustls_server_config = ServerConfig::builder()
249            .with_no_client_auth()
250            .with_single_cert(cert_chain, key);
251
252        if let Err(e) = rustls_server_config {
253            panic!("Failed to create ServerConfig: {e}");
254        }
255
256        std::fs::remove_file(cert_path).unwrap();
257        std::fs::remove_file(key_path).unwrap();
258    }
259}