Skip to main content

oxigdal_security/encryption/
in_transit.rs

1//! TLS/mTLS configuration for data in transit.
2
3use crate::error::{Result, SecurityError};
4use rustls::{ClientConfig, ServerConfig};
5use std::io::BufReader;
6use std::sync::Arc;
7
8/// TLS configuration builder.
9pub struct TlsConfigBuilder {
10    server_name: Option<String>,
11    ca_cert: Option<Vec<u8>>,
12    client_cert: Option<Vec<u8>>,
13    client_key: Option<Vec<u8>>,
14    server_cert: Option<Vec<u8>>,
15    server_key: Option<Vec<u8>>,
16    verify_peer: bool,
17}
18
19impl Default for TlsConfigBuilder {
20    fn default() -> Self {
21        Self::new()
22    }
23}
24
25impl TlsConfigBuilder {
26    /// Create a new TLS configuration builder.
27    pub fn new() -> Self {
28        Self {
29            server_name: None,
30            ca_cert: None,
31            client_cert: None,
32            client_key: None,
33            server_cert: None,
34            server_key: None,
35            verify_peer: true,
36        }
37    }
38
39    /// Set the server name for SNI.
40    pub fn server_name(mut self, name: String) -> Self {
41        self.server_name = Some(name);
42        self
43    }
44
45    /// Set the CA certificate (PEM format).
46    pub fn ca_cert(mut self, cert: Vec<u8>) -> Self {
47        self.ca_cert = Some(cert);
48        self
49    }
50
51    /// Set the client certificate and key (PEM format).
52    pub fn client_cert_and_key(mut self, cert: Vec<u8>, key: Vec<u8>) -> Self {
53        self.client_cert = Some(cert);
54        self.client_key = Some(key);
55        self
56    }
57
58    /// Set the server certificate and key (PEM format).
59    pub fn server_cert_and_key(mut self, cert: Vec<u8>, key: Vec<u8>) -> Self {
60        self.server_cert = Some(cert);
61        self.server_key = Some(key);
62        self
63    }
64
65    /// Set whether to verify the peer certificate.
66    pub fn verify_peer(mut self, verify: bool) -> Self {
67        self.verify_peer = verify;
68        self
69    }
70
71    /// Build a client configuration.
72    pub fn build_client(self) -> Result<Arc<ClientConfig>> {
73        let mut root_store = rustls::RootCertStore::empty();
74
75        if let Some(ca_cert) = self.ca_cert {
76            let mut reader = BufReader::new(ca_cert.as_slice());
77            let certs = rustls_pemfile::certs(&mut reader)
78                .collect::<std::result::Result<Vec<_>, _>>()
79                .map_err(|e| {
80                    SecurityError::certificate(format!("Failed to parse CA cert: {}", e))
81                })?;
82
83            for cert in certs {
84                root_store.add(cert).map_err(|e| {
85                    SecurityError::certificate(format!("Failed to add CA cert: {}", e))
86                })?;
87            }
88        } else {
89            // Use system root certificates
90            root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
91        }
92
93        // Build client config with or without client authentication
94        let config = if let (Some(cert), Some(key)) = (self.client_cert, self.client_key) {
95            let mut cert_reader = BufReader::new(cert.as_slice());
96            let certs = rustls_pemfile::certs(&mut cert_reader)
97                .collect::<std::result::Result<Vec<_>, _>>()
98                .map_err(|e| {
99                    SecurityError::certificate(format!("Failed to parse client cert: {}", e))
100                })?;
101
102            let mut key_reader = BufReader::new(key.as_slice());
103            let key = rustls_pemfile::private_key(&mut key_reader)
104                .map_err(|e| {
105                    SecurityError::certificate(format!("Failed to parse private key: {}", e))
106                })?
107                .ok_or_else(|| SecurityError::certificate("No private key found"))?;
108
109            ClientConfig::builder()
110                .with_root_certificates(root_store)
111                .with_client_auth_cert(certs, key)
112                .map_err(|e| {
113                    SecurityError::certificate(format!("Failed to set client auth: {}", e))
114                })?
115        } else {
116            ClientConfig::builder()
117                .with_root_certificates(root_store)
118                .with_no_client_auth()
119        };
120
121        Ok(Arc::new(config))
122    }
123
124    /// Build a server configuration.
125    pub fn build_server(self) -> Result<Arc<ServerConfig>> {
126        let cert = self
127            .server_cert
128            .ok_or_else(|| SecurityError::certificate("Server certificate required"))?;
129        let key = self
130            .server_key
131            .ok_or_else(|| SecurityError::certificate("Server private key required"))?;
132
133        let mut cert_reader = BufReader::new(cert.as_slice());
134        let certs = rustls_pemfile::certs(&mut cert_reader)
135            .collect::<std::result::Result<Vec<_>, _>>()
136            .map_err(|e| {
137                SecurityError::certificate(format!("Failed to parse server cert: {}", e))
138            })?;
139
140        let mut key_reader = BufReader::new(key.as_slice());
141        let private_key = rustls_pemfile::private_key(&mut key_reader)
142            .map_err(|e| SecurityError::certificate(format!("Failed to parse private key: {}", e)))?
143            .ok_or_else(|| SecurityError::certificate("No private key found"))?;
144
145        let config = if self.verify_peer {
146            // mTLS - require client certificate
147            if let Some(ca_cert) = self.ca_cert {
148                let mut root_store = rustls::RootCertStore::empty();
149                let mut reader = BufReader::new(ca_cert.as_slice());
150                let ca_certs = rustls_pemfile::certs(&mut reader)
151                    .collect::<std::result::Result<Vec<_>, _>>()
152                    .map_err(|e| {
153                        SecurityError::certificate(format!("Failed to parse CA cert: {}", e))
154                    })?;
155
156                for cert in ca_certs {
157                    root_store.add(cert).map_err(|e| {
158                        SecurityError::certificate(format!("Failed to add CA cert: {}", e))
159                    })?;
160                }
161
162                let verifier = rustls::server::WebPkiClientVerifier::builder(Arc::new(root_store))
163                    .build()
164                    .map_err(|e| {
165                        SecurityError::certificate(format!("Failed to build verifier: {}", e))
166                    })?;
167
168                ServerConfig::builder()
169                    .with_client_cert_verifier(verifier)
170                    .with_single_cert(certs, private_key)
171                    .map_err(|e| {
172                        SecurityError::certificate(format!("Failed to build server config: {}", e))
173                    })?
174            } else {
175                return Err(SecurityError::certificate(
176                    "CA certificate required for client verification",
177                ));
178            }
179        } else {
180            // TLS only - no client certificate required
181            ServerConfig::builder()
182                .with_no_client_auth()
183                .with_single_cert(certs, private_key)
184                .map_err(|e| {
185                    SecurityError::certificate(format!("Failed to build server config: {}", e))
186                })?
187        };
188
189        Ok(Arc::new(config))
190    }
191}
192
193/// TLS version.
194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
195pub enum TlsVersion {
196    /// TLS 1.2
197    Tls12,
198    /// TLS 1.3
199    Tls13,
200}
201
202/// Certificate validation result.
203#[derive(Debug, Clone)]
204pub struct CertificateValidation {
205    /// Whether the certificate is valid.
206    pub valid: bool,
207    /// Validation errors if any.
208    pub errors: Vec<String>,
209    /// Certificate subject.
210    pub subject: Option<String>,
211    /// Certificate issuer.
212    pub issuer: Option<String>,
213    /// Certificate expiration date.
214    pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
215}
216
217impl CertificateValidation {
218    /// Create a valid certificate validation result.
219    pub fn valid() -> Self {
220        Self {
221            valid: true,
222            errors: Vec::new(),
223            subject: None,
224            issuer: None,
225            expires_at: None,
226        }
227    }
228
229    /// Create an invalid certificate validation result.
230    pub fn invalid(errors: Vec<String>) -> Self {
231        Self {
232            valid: false,
233            errors,
234            subject: None,
235            issuer: None,
236            expires_at: None,
237        }
238    }
239
240    /// Add error to validation result.
241    pub fn add_error(&mut self, error: String) {
242        self.valid = false;
243        self.errors.push(error);
244    }
245}
246
247#[cfg(test)]
248mod tests {
249    use super::*;
250
251    #[test]
252    fn test_tls_config_builder() {
253        let builder = TlsConfigBuilder::new()
254            .server_name("example.com".to_string())
255            .verify_peer(true);
256
257        // Cannot test without actual certificates
258        assert_eq!(builder.server_name, Some("example.com".to_string()));
259        assert!(builder.verify_peer);
260    }
261
262    #[test]
263    fn test_certificate_validation() {
264        let valid = CertificateValidation::valid();
265        assert!(valid.valid);
266        assert!(valid.errors.is_empty());
267
268        let invalid = CertificateValidation::invalid(vec!["expired".to_string()]);
269        assert!(!invalid.valid);
270        assert_eq!(invalid.errors.len(), 1);
271    }
272}