1#![allow(missing_docs)]
8
9use rustls::pki_types::{CertificateDer, PrivateKeyDer};
19use std::{sync::Arc, time::Duration};
20use thiserror::Error;
21
22#[derive(Error, Debug)]
24pub enum CertificateError {
25 #[error("Certificate generation failed: {0}")]
26 GenerationFailed(String),
27
28 #[error("Certificate validation failed: {0}")]
29 ValidationFailed(String),
30
31 #[error("Certificate loading failed: {0}")]
32 LoadingFailed(String),
33
34 #[error("Certificate parsing failed: {0}")]
35 ParsingFailed(String),
36
37 #[error("Private key error: {0}")]
38 PrivateKeyError(String),
39
40 #[error("Certificate chain error: {0}")]
41 ChainError(String),
42
43 #[error("Certificate expired or not yet valid")]
44 ValidityError,
45
46 #[error("Unsupported certificate format")]
47 UnsupportedFormat,
48}
49
50#[derive(Debug, Clone)]
52pub struct CertificateConfig {
53 pub common_name: String,
55
56 pub subject_alt_names: Vec<String>,
58
59 pub validity_duration: Duration,
61
62 pub key_algorithm: KeyAlgorithm,
64
65 pub self_signed: bool,
67
68 pub ca_cert_path: Option<String>,
70
71 pub require_chain_validation: bool,
73}
74
75#[derive(Debug, Clone, Copy)]
77pub enum KeyAlgorithm {
78 Rsa(u32),
80 EcdsaP256,
82 EcdsaP384,
84 Ed25519,
86}
87
88#[derive(Debug)]
90pub struct CertificateBundle {
91 pub cert_chain: Vec<CertificateDer<'static>>,
93
94 pub private_key: PrivateKeyDer<'static>,
96
97 pub created_at: std::time::SystemTime,
99
100 pub expires_at: std::time::SystemTime,
102}
103
104pub struct CertificateManager {
106 config: CertificateConfig,
107 ca_certs: Vec<CertificateDer<'static>>,
108}
109
110impl Default for CertificateConfig {
111 fn default() -> Self {
112 Self {
113 common_name: "ant-quic-node".to_string(),
114 subject_alt_names: vec!["localhost".to_string()],
115 validity_duration: Duration::from_secs(365 * 24 * 60 * 60), key_algorithm: KeyAlgorithm::Ed25519,
117 self_signed: true,
118 ca_cert_path: None,
119 require_chain_validation: false,
120 }
121 }
122}
123
124impl CertificateManager {
125 pub fn new(config: CertificateConfig) -> Result<Self, CertificateError> {
127 let ca_certs = if let Some(ca_path) = &config.ca_cert_path {
128 Self::load_ca_certificates(ca_path)?
129 } else {
130 Vec::new()
131 };
132
133 Ok(Self { config, ca_certs })
134 }
135
136 pub fn generate_certificate(&self) -> Result<CertificateBundle, CertificateError> {
138 use rcgen::generate_simple_self_signed;
139
140 let subject_alt_names = vec![self.config.common_name.clone()];
143 let cert = generate_simple_self_signed(subject_alt_names)
144 .map_err(|e| CertificateError::GenerationFailed(e.to_string()))?;
145
146 let cert_der = cert.cert.der();
148 let private_key_der = cert.signing_key.serialize_der();
149
150 let created_at = std::time::SystemTime::now();
151 let expires_at = created_at + self.config.validity_duration;
152
153 Ok(CertificateBundle {
154 cert_chain: vec![cert_der.clone()],
155 private_key: PrivateKeyDer::try_from(private_key_der).map_err(|e| {
156 CertificateError::PrivateKeyError(format!("Key conversion failed: {e:?}"))
157 })?,
158 created_at,
159 expires_at,
160 })
161 }
162
163 pub fn load_certificate_from_pem(
165 cert_path: &str,
166 key_path: &str,
167 ) -> Result<CertificateBundle, CertificateError> {
168 use rustls_pemfile::{certs, private_key};
169
170 let cert_file = std::fs::File::open(cert_path).map_err(|e| {
172 CertificateError::LoadingFailed(format!("Failed to open cert file: {e}"))
173 })?;
174
175 let mut cert_reader = std::io::BufReader::new(cert_file);
176 let cert_chain: Vec<CertificateDer<'static>> = certs(&mut cert_reader)
177 .collect::<Result<Vec<_>, _>>()
178 .map_err(|e| {
179 CertificateError::ParsingFailed(format!("Failed to parse certificates: {e}"))
180 })?;
181
182 if cert_chain.is_empty() {
183 return Err(CertificateError::LoadingFailed(
184 "No certificates found in file".to_string(),
185 ));
186 }
187
188 let key_file = std::fs::File::open(key_path).map_err(|e| {
190 CertificateError::LoadingFailed(format!("Failed to open key file: {e}"))
191 })?;
192
193 let mut key_reader = std::io::BufReader::new(key_file);
194 let private_key = private_key(&mut key_reader)
195 .map_err(|e| {
196 CertificateError::ParsingFailed(format!("Failed to parse private key: {e}"))
197 })?
198 .ok_or_else(|| {
199 CertificateError::LoadingFailed("No private key found in file".to_string())
200 })?;
201
202 let (created_at, expires_at) = Self::extract_validity_from_cert(&cert_chain[0])?;
204
205 Ok(CertificateBundle {
206 cert_chain,
207 private_key,
208 created_at,
209 expires_at,
210 })
211 }
212
213 pub fn validate_certificate(&self, bundle: &CertificateBundle) -> Result<(), CertificateError> {
215 let now = std::time::SystemTime::now();
217 if now > bundle.expires_at {
218 return Err(CertificateError::ValidityError);
219 }
220
221 if self.config.require_chain_validation && !self.ca_certs.is_empty() {
223 self.validate_certificate_chain(&bundle.cert_chain)?;
224 }
225
226 Ok(())
227 }
228
229 pub fn create_server_config(
231 &self,
232 bundle: &CertificateBundle,
233 ) -> Result<Arc<rustls::ServerConfig>, CertificateError> {
234 use rustls::ServerConfig;
235
236 self.validate_certificate(bundle)?;
237
238 let server_config = ServerConfig::builder()
239 .with_no_client_auth()
240 .with_single_cert(bundle.cert_chain.clone(), bundle.private_key.clone_key())
241 .map_err(|e| CertificateError::ValidationFailed(e.to_string()))?;
242
243 Ok(Arc::new(server_config))
244 }
245
246 pub fn create_client_config(&self) -> Result<Arc<rustls::ClientConfig>, CertificateError> {
248 use rustls::ClientConfig;
249
250 let config = if self.ca_certs.is_empty() {
251 ClientConfig::builder()
253 .dangerous()
254 .with_custom_certificate_verifier(Arc::new(NoCertificateVerifier))
255 .with_no_client_auth()
256 } else {
257 let mut root_store = rustls::RootCertStore::empty();
259 for ca_cert in &self.ca_certs {
260 root_store.add(ca_cert.clone()).map_err(|e| {
261 CertificateError::ValidationFailed(format!("Failed to add CA cert: {e}"))
262 })?;
263 }
264
265 ClientConfig::builder()
266 .with_root_certificates(root_store)
267 .with_no_client_auth()
268 };
269
270 Ok(Arc::new(config))
271 }
272
273 fn load_ca_certificates(
275 ca_path: &str,
276 ) -> Result<Vec<CertificateDer<'static>>, CertificateError> {
277 use rustls_pemfile::certs;
278
279 let ca_file = std::fs::File::open(ca_path)
280 .map_err(|e| CertificateError::LoadingFailed(format!("Failed to open CA file: {e}")))?;
281
282 let mut ca_reader = std::io::BufReader::new(ca_file);
283 let ca_certs: Vec<CertificateDer<'static>> = certs(&mut ca_reader)
284 .collect::<Result<Vec<_>, _>>()
285 .map_err(|e| {
286 CertificateError::ParsingFailed(format!("Failed to parse CA certificates: {e}"))
287 })?;
288
289 if ca_certs.is_empty() {
290 return Err(CertificateError::LoadingFailed(
291 "No CA certificates found".to_string(),
292 ));
293 }
294
295 Ok(ca_certs)
296 }
297
298 fn extract_validity_from_cert(
300 _cert: &CertificateDer<'static>,
301 ) -> Result<(std::time::SystemTime, std::time::SystemTime), CertificateError> {
302 let created_at = std::time::SystemTime::now();
305 let expires_at = created_at + Duration::from_secs(365 * 24 * 60 * 60); Ok((created_at, expires_at))
308 }
309
310 fn validate_certificate_chain(
312 &self,
313 cert_chain: &[CertificateDer<'static>],
314 ) -> Result<(), CertificateError> {
315 if cert_chain.is_empty() {
316 return Err(CertificateError::ChainError(
317 "Empty certificate chain".to_string(),
318 ));
319 }
320
321 Ok(())
325 }
326}
327
328#[derive(Debug)]
330struct NoCertificateVerifier;
331
332impl rustls::client::danger::ServerCertVerifier for NoCertificateVerifier {
333 fn verify_server_cert(
334 &self,
335 _end_entity: &CertificateDer<'_>,
336 _intermediates: &[CertificateDer<'_>],
337 _server_name: &rustls::pki_types::ServerName<'_>,
338 _ocsp_response: &[u8],
339 _now: rustls::pki_types::UnixTime,
340 ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
341 Ok(rustls::client::danger::ServerCertVerified::assertion())
342 }
343
344 fn verify_tls12_signature(
345 &self,
346 _message: &[u8],
347 _cert: &CertificateDer<'_>,
348 _dss: &rustls::DigitallySignedStruct,
349 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
350 Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
351 }
352
353 fn verify_tls13_signature(
354 &self,
355 _message: &[u8],
356 _cert: &CertificateDer<'_>,
357 _dss: &rustls::DigitallySignedStruct,
358 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
359 Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
360 }
361
362 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
363 vec![rustls::SignatureScheme::ML_DSA_65]
365 }
366}
367
368impl CertificateBundle {
369 pub fn expires_within(&self, duration: Duration) -> bool {
371 let now = std::time::SystemTime::now();
372 match now.checked_add(duration) {
373 Some(check_time) => check_time >= self.expires_at,
374 None => true, }
376 }
377
378 pub fn remaining_validity(&self) -> Option<Duration> {
380 std::time::SystemTime::now()
381 .duration_since(self.expires_at)
382 .ok()
383 }
384}
385
386#[cfg(test)]
387mod tests {
388 use super::*;
389
390 #[test]
391 fn test_default_certificate_config() {
392 let config = CertificateConfig::default();
393 assert_eq!(config.common_name, "ant-quic-node");
394 assert_eq!(config.subject_alt_names, vec!["localhost"]);
395 assert!(config.self_signed);
396 assert!(!config.require_chain_validation);
397 }
398
399 #[test]
400 fn test_certificate_manager_creation() {
401 let config = CertificateConfig::default();
402 let manager = CertificateManager::new(config);
403 assert!(manager.is_ok());
404 }
405
406 #[test]
407 fn test_certificate_generation() {
408 let config = CertificateConfig::default();
409 let manager = CertificateManager::new(config).unwrap();
410
411 let bundle = manager.generate_certificate();
412 assert!(bundle.is_ok());
413
414 let bundle = bundle.unwrap();
415 assert!(!bundle.cert_chain.is_empty());
416 assert!(bundle.expires_at > bundle.created_at);
417 }
418
419 #[test]
420 fn test_certificate_bundle_expiry_check() {
421 let dummy_key = vec![
424 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
432 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
433 0x1c, 0x1d, 0x1e, 0x1f,
434 ];
435
436 let bundle = CertificateBundle {
437 cert_chain: vec![],
438 private_key: PrivateKeyDer::try_from(dummy_key).unwrap(),
439 created_at: std::time::SystemTime::now(),
440 expires_at: std::time::SystemTime::now() + Duration::from_secs(3600), };
442
443 assert!(!bundle.expires_within(Duration::from_secs(1800))); assert!(bundle.expires_within(Duration::from_secs(7200))); }
446}