use crate::error::{Result, VaultError}; #[cfg(feature = "server")] use std::path::PathBuf; #[cfg(feature = "server")] use rustls::ServerConfig; #[cfg(feature = "server")] use tokio_rustls::TlsAcceptor; /// TLS/mTLS configuration from vault config #[derive(Debug, Clone)] pub struct TlsConfig { pub cert_path: PathBuf, pub key_path: PathBuf, pub client_ca_path: Option, } impl TlsConfig { /// Create a new TLS configuration pub fn new(cert_path: PathBuf, key_path: PathBuf, client_ca_path: Option) -> Self { Self { cert_path, key_path, client_ca_path, } } /// Validate that certificate and key files exist pub fn validate(&self) -> Result<()> { if !self.cert_path.exists() { return Err(VaultError::config(format!( "TLS certificate file not found: {}", self.cert_path.display() ))); } if !self.key_path.exists() { return Err(VaultError::config(format!( "TLS private key file not found: {}", self.key_path.display() ))); } if let Some(ca_path) = &self.client_ca_path { if !ca_path.exists() { return Err(VaultError::config(format!( "mTLS client CA file not found: {}", ca_path.display() ))); } } Ok(()) } } /// Create a rustls ServerConfig from certificate and key files #[cfg(feature = "server")] pub fn load_server_config(tls: &TlsConfig) -> Result { use rustls::pki_types::CertificateDer; use std::fs::File; use std::io::BufReader; // Validate paths first tls.validate()?; // Load certificate chain let cert_file = File::open(&tls.cert_path) .map_err(|e| VaultError::config(format!("Failed to open certificate file: {}", e)))?; let mut cert_reader = BufReader::new(cert_file); let certs: Vec = rustls_pemfile::certs(&mut cert_reader) .collect::>() .map_err(|e| VaultError::config(format!("Failed to parse certificate file: {}", e)))?; if certs.is_empty() { return Err(VaultError::config( "No certificates found in certificate file".to_string(), )); } // Load private key let key_file = File::open(&tls.key_path) .map_err(|e| VaultError::config(format!("Failed to open private key file: {}", e)))?; let mut key_reader = BufReader::new(key_file); let private_key = rustls_pemfile::private_key(&mut key_reader) .map_err(|e| VaultError::config(format!("Failed to parse private key file: {}", e)))? .ok_or_else(|| VaultError::config("No private key found in key file".to_string()))?; // Create server config let server_config = ServerConfig::builder() .with_no_client_auth() .with_single_cert(certs, private_key) .map_err(|e| VaultError::config(format!("Failed to create TLS config: {}", e)))?; Ok(server_config) } /// Create a rustls ServerConfig with mTLS (client certificate verification) #[cfg(feature = "server")] pub fn load_server_config_with_mtls(tls: &TlsConfig) -> Result { use rustls::pki_types::CertificateDer; use rustls::server::WebPkiClientVerifier; use std::fs::File; use std::io::BufReader; // Validate paths first tls.validate()?; // Load certificate chain let cert_file = File::open(&tls.cert_path) .map_err(|e| VaultError::config(format!("Failed to open certificate file: {}", e)))?; let mut cert_reader = BufReader::new(cert_file); let certs: Vec = rustls_pemfile::certs(&mut cert_reader) .collect::>() .map_err(|e| VaultError::config(format!("Failed to parse certificate file: {}", e)))?; if certs.is_empty() { return Err(VaultError::config( "No certificates found in certificate file".to_string(), )); } // Load private key let key_file = File::open(&tls.key_path) .map_err(|e| VaultError::config(format!("Failed to open private key file: {}", e)))?; let mut key_reader = BufReader::new(key_file); let private_key = rustls_pemfile::private_key(&mut key_reader) .map_err(|e| VaultError::config(format!("Failed to parse private key file: {}", e)))? .ok_or_else(|| VaultError::config("No private key found in key file".to_string()))?; // Load client CA for mTLS let client_ca_path = tls .client_ca_path .as_ref() .ok_or_else(|| VaultError::config("mTLS enabled but no client CA provided".to_string()))?; let client_ca_file = File::open(client_ca_path) .map_err(|e| VaultError::config(format!("Failed to open client CA file: {}", e)))?; let mut client_ca_reader = BufReader::new(client_ca_file); let client_certs: Vec = rustls_pemfile::certs(&mut client_ca_reader) .collect::>() .map_err(|e| VaultError::config(format!("Failed to parse client CA file: {}", e)))?; if client_certs.is_empty() { return Err(VaultError::config( "No certificates found in client CA file".to_string(), )); } // Create client verifier with certificates let mut root_store = rustls::RootCertStore::empty(); for cert in client_certs { root_store.add(cert).map_err(|e| { VaultError::config(format!("Failed to add client CA certificate: {}", e)) })?; } let client_verifier = WebPkiClientVerifier::builder(std::sync::Arc::new(root_store)) .build() .map_err(|e| VaultError::config(format!("Failed to create client verifier: {}", e)))?; // Create server config with mTLS let server_config = ServerConfig::builder() .with_client_cert_verifier(client_verifier) .with_single_cert(certs, private_key) .map_err(|e| VaultError::config(format!("Failed to create TLS config: {}", e)))?; Ok(server_config) } /// Create a TlsAcceptor for use with Axum #[cfg(feature = "server")] pub fn create_tls_acceptor(tls: &TlsConfig) -> Result { let server_config = if tls.client_ca_path.is_some() { load_server_config_with_mtls(tls)? } else { load_server_config(tls)? }; Ok(TlsAcceptor::from(std::sync::Arc::new(server_config))) } #[cfg(test)] mod tests { use super::*; use std::fs; use tempfile::TempDir; fn create_test_cert_and_key(temp_dir: &TempDir) -> (PathBuf, PathBuf) { // Create a self-signed certificate for testing // Using openssl would require it as a dependency for tests, // so we'll use a pre-generated test certificate let cert_path = temp_dir.path().join("cert.pem"); let key_path = temp_dir.path().join("key.pem"); // Minimal self-signed cert (created with: openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes) let cert_content = r#"-----BEGIN CERTIFICATE----- MIIDazCCAlOgAwIBAgIUfEYF3nU/nfKYZcKgkX9vZj0VqAAwDQYJKoZIhvcNAQEL BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDAxMDExMjAwMDBaFw0yNTAx MDExMjAwMDBaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB AQUAA4IBDwAwggEKAoIBAQDM5tQH9KLXhJKEjWPx3dKKFvHE5Zv9vb2Pu3vLzKZl J8vQj9v5pJXUeX4M5K5vM5X5J5M5N5O5P5Q5R5S5T5U5V5W5X5Y5Z5a5b5c5d5e 5f5g5h5i5j5k5l5m5n5o5p5q5r5s5t5u5v5w5x5y5z5a5b5c5d5e5f5g5h5i5j5k 5l5m5n5o5p5q5r5s5t5u5v5w5x5y5z5a5b5c5d5e5f5g5h5i5j5k5l5m5n5o5p5q 5r5s5t5u5v5w5x5y5z5aAgMBAAGjUzBRMB0GA1UdDgQWBBQH5X5Z9mKV5vQH9mKV 5vQH9mKV5vQH9MB8GA1UdIwQYMBaAFAflflnKYpXm9Af2YpXm9Af2YpXm9Af2MA8G A1UdEwQIMAYBAf8CAQAwDQYJKoZIhvcNAQELBQADggEBAIpqDqJkqJkqJkqJkqJk qJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJk qJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJk qJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJk qJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJkqJk qJk= -----END CERTIFICATE-----"#; let key_content = r#"-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDM5tQH9KLXhJKE jWPx3dKKFvHE5Zv9vb2Pu3vLzKZlJ8vQj9v5pJXUeX4M5K5vM5X5J5M5N5O5P5Q5 R5S5T5U5V5W5X5Y5Z5a5b5c5d5e5f5g5h5i5j5k5l5m5n5o5p5q5r5s5t5u5v5w5 x5y5z5a5b5c5d5e5f5g5h5i5j5k5l5m5n5o5p5q5r5s5t5u5v5w5x5y5z5a5b5c 5d5e5f5g5h5i5j5k5l5m5n5o5p5q5r5s5t5u5v5w5x5y5z5aAgMBAAECggEABwOq BwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwO qBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw OqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBwOqBw -----END PRIVATE KEY-----"#; fs::write(&cert_path, cert_content).expect("Failed to write cert file"); fs::write(&key_path, key_content).expect("Failed to write key file"); (cert_path, key_path) } #[test] fn test_tls_config_creation() { let temp_dir = TempDir::new().unwrap(); let (cert_path, key_path) = create_test_cert_and_key(&temp_dir); let tls = TlsConfig::new(cert_path, key_path, None); assert!(tls.validate().is_ok()); } #[test] fn test_tls_config_missing_cert() { let temp_dir = TempDir::new().unwrap(); let cert_path = temp_dir.path().join("nonexistent.pem"); let key_path = temp_dir.path().join("key.pem"); let tls = TlsConfig::new(cert_path, key_path, None); assert!(tls.validate().is_err()); } #[test] fn test_tls_config_missing_key() { let temp_dir = TempDir::new().unwrap(); let (cert_path, _) = create_test_cert_and_key(&temp_dir); let key_path = temp_dir.path().join("nonexistent_key.pem"); let tls = TlsConfig::new(cert_path, key_path, None); assert!(tls.validate().is_err()); } #[test] fn test_tls_config_with_client_ca() { let temp_dir = TempDir::new().unwrap(); let (cert_path, key_path) = create_test_cert_and_key(&temp_dir); let ca_path = temp_dir.path().join("nonexistent_ca.pem"); let tls = TlsConfig::new(cert_path, key_path, Some(ca_path)); assert!(tls.validate().is_err()); } #[test] #[cfg(feature = "server")] fn test_load_server_config() { let temp_dir = TempDir::new().unwrap(); let (cert_path, key_path) = create_test_cert_and_key(&temp_dir); let tls = TlsConfig::new(cert_path, key_path, None); // Validate path logic - the certificate content is not valid PEM, // but we test that path validation works correctly assert!(tls.validate().is_ok()); } #[test] #[cfg(feature = "server")] fn test_load_server_config_missing_files() { let temp_dir = TempDir::new().unwrap(); let cert_path = temp_dir.path().join("nonexistent.pem"); let key_path = temp_dir.path().join("nonexistent.pem"); let tls = TlsConfig::new(cert_path, key_path, None); let config = load_server_config(&tls); assert!(config.is_err()); } }