anytls_rs/util/
tls.rs

1//! TLS utilities for certificate generation and configuration
2
3use crate::util::{AnyTlsError, Result};
4use rustls::pki_types::{CertificateDer, PrivateKeyDer};
5use rustls::server::ServerConfig;
6use rustls::{ClientConfig, RootCertStore};
7use std::sync::Arc;
8use std::{fs::File, io::BufReader, path::Path};
9
10impl From<rustls::Error> for AnyTlsError {
11    fn from(err: rustls::Error) -> Self {
12        AnyTlsError::Tls(format!("rustls error: {}", err))
13    }
14}
15
16impl From<rcgen::Error> for AnyTlsError {
17    fn from(err: rcgen::Error) -> Self {
18        AnyTlsError::Tls(format!("rcgen error: {}", err))
19    }
20}
21
22/// Generate a self-signed certificate for testing
23///
24/// This generates a certificate similar to the Go version:
25/// - ECDSA P-256 key (default for rcgen, better performance than RSA 2048)
26/// - Valid for reasonable duration (rcgen default, typically 1 year)
27/// - Server authentication usage
28/// - Supports localhost and custom server names
29pub fn generate_key_pair() -> Result<(CertificateDer<'static>, PrivateKeyDer<'static>)> {
30    generate_key_pair_with_name(None)
31}
32
33/// Generate a self-signed certificate with a specific server name
34pub fn generate_key_pair_with_name(
35    server_name: Option<&str>,
36) -> Result<(CertificateDer<'static>, PrivateKeyDer<'static>)> {
37    // Determine server name to use
38    let name = server_name.unwrap_or("localhost");
39
40    // Use rcgen's simple API to generate self-signed certificate
41    // This is the recommended approach for basic use cases
42    let subject_alt_names = vec![name.to_string(), "localhost".to_string()];
43    let certified_key = rcgen::generate_simple_self_signed(subject_alt_names)?;
44
45    // Serialize to DER format
46    let cert_der = certified_key.cert.der();
47    let key_der = certified_key.signing_key.serialize_der();
48
49    // Convert to rustls types
50    // CertificateDer implements From<&[u8]>, but we need 'static lifetime
51    // So we clone into a Vec<u8> and use it
52    let cert_der_vec: Vec<u8> = cert_der.to_vec();
53    let cert_der: CertificateDer<'static> = cert_der_vec.into();
54    let key_der: PrivateKeyDer<'static> = PrivateKeyDer::Pkcs8(key_der.into());
55
56    Ok((cert_der, key_der))
57}
58
59/// Create a server TLS config with a generated certificate
60pub fn create_server_config() -> Result<Arc<ServerConfig>> {
61    let (cert, key) = generate_key_pair()?;
62
63    let config = ServerConfig::builder()
64        .with_no_client_auth()
65        .with_single_cert(vec![cert], key)?;
66
67    Ok(Arc::new(config))
68}
69
70/// Create a server TLS config by loading certificate/private key from disk (PEM).
71pub fn create_server_config_from_files<P: AsRef<Path>>(
72    cert_path: P,
73    key_path: P,
74) -> Result<Arc<ServerConfig>> {
75    let cert_file = File::open(&cert_path).map_err(AnyTlsError::Io)?;
76    let mut cert_reader = BufReader::new(cert_file);
77    let certs = rustls_pemfile::certs(&mut cert_reader)
78        .collect::<std::result::Result<Vec<_>, _>>()
79        .map_err(|e| AnyTlsError::Tls(format!("failed to parse certificate: {e}")))?;
80    if certs.is_empty() {
81        return Err(AnyTlsError::Tls(format!(
82            "no certificates found in {:?}",
83            cert_path.as_ref()
84        )));
85    }
86
87    let key_file = File::open(&key_path).map_err(AnyTlsError::Io)?;
88    let mut key_reader = BufReader::new(key_file);
89    let key = rustls_pemfile::private_key(&mut key_reader)
90        .map_err(|e| AnyTlsError::Tls(format!("failed to parse private key: {e}")))?
91        .ok_or_else(|| {
92            AnyTlsError::Tls(format!("no private key found in {:?}", key_path.as_ref()))
93        })?;
94
95    let config = ServerConfig::builder()
96        .with_no_client_auth()
97        .with_single_cert(certs, key)?;
98
99    Ok(Arc::new(config))
100}
101
102/// Certificate verifier that accepts all certificates (for testing only)
103/// Similar to Go's InsecureSkipVerify: true
104#[derive(Debug)]
105struct NoCertificateVerification;
106
107impl rustls::client::danger::ServerCertVerifier for NoCertificateVerification {
108    fn verify_server_cert(
109        &self,
110        _end_entity: &rustls::pki_types::CertificateDer<'_>,
111        _intermediates: &[rustls::pki_types::CertificateDer<'_>],
112        _server_name: &rustls::pki_types::ServerName<'_>,
113        _ocsp_response: &[u8],
114        _now: rustls::pki_types::UnixTime,
115    ) -> std::result::Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
116        // Accept all certificates (for testing only)
117        // This is similar to Go's InsecureSkipVerify: true
118        Ok(rustls::client::danger::ServerCertVerified::assertion())
119    }
120
121    fn verify_tls12_signature(
122        &self,
123        _message: &[u8],
124        _cert: &rustls::pki_types::CertificateDer<'_>,
125        _dss: &rustls::DigitallySignedStruct,
126    ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
127        Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
128    }
129
130    fn verify_tls13_signature(
131        &self,
132        _message: &[u8],
133        _cert: &rustls::pki_types::CertificateDer<'_>,
134        _dss: &rustls::DigitallySignedStruct,
135    ) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
136        Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
137    }
138
139    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
140        // Support common signature schemes
141        vec![
142            rustls::SignatureScheme::RSA_PKCS1_SHA256,
143            rustls::SignatureScheme::RSA_PKCS1_SHA384,
144            rustls::SignatureScheme::RSA_PKCS1_SHA512,
145            rustls::SignatureScheme::ECDSA_NISTP256_SHA256,
146            rustls::SignatureScheme::ECDSA_NISTP384_SHA384,
147            rustls::SignatureScheme::ECDSA_NISTP521_SHA512,
148            rustls::SignatureScheme::RSA_PSS_SHA256,
149            rustls::SignatureScheme::RSA_PSS_SHA384,
150            rustls::SignatureScheme::RSA_PSS_SHA512,
151            rustls::SignatureScheme::ED25519,
152            rustls::SignatureScheme::ED448,
153        ]
154    }
155}
156
157/// Create a client TLS config with insecure verification (for testing)
158/// This accepts self-signed certificates, similar to Go's InsecureSkipVerify: true
159pub fn create_client_config() -> Result<Arc<ClientConfig>> {
160    let root_store = RootCertStore::empty();
161
162    // For testing, we'll accept any certificate
163    // In production, you should load proper root certificates
164
165    let mut config = ClientConfig::builder()
166        .with_root_certificates(root_store)
167        .with_no_client_auth();
168
169    // Set custom certificate verifier to accept self-signed certificates
170    config
171        .dangerous()
172        .set_certificate_verifier(Arc::new(NoCertificateVerification));
173
174    Ok(Arc::new(config))
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180
181    #[test]
182    fn test_create_client_config() {
183        let config = create_client_config().unwrap();
184        // Config should be created successfully
185        assert!(Arc::strong_count(&config) >= 1);
186    }
187
188    #[test]
189    fn test_generate_key_pair() {
190        // Should now succeed
191        let (cert, key) = generate_key_pair().unwrap();
192        assert!(!cert.as_ref().is_empty());
193        match &key {
194            PrivateKeyDer::Pkcs8(data) => assert!(!data.secret_pkcs8_der().is_empty()),
195            PrivateKeyDer::Pkcs1(data) => assert!(!data.secret_pkcs1_der().is_empty()),
196            PrivateKeyDer::Sec1(data) => assert!(!data.secret_sec1_der().is_empty()),
197            _ => panic!("Unexpected key type"),
198        }
199    }
200
201    #[test]
202    fn test_generate_key_pair_with_name() {
203        let (cert, key) = generate_key_pair_with_name(Some("example.com")).unwrap();
204        assert!(!cert.as_ref().is_empty());
205        match &key {
206            PrivateKeyDer::Pkcs8(data) => assert!(!data.secret_pkcs8_der().is_empty()),
207            PrivateKeyDer::Pkcs1(data) => assert!(!data.secret_pkcs1_der().is_empty()),
208            PrivateKeyDer::Sec1(data) => assert!(!data.secret_sec1_der().is_empty()),
209            _ => panic!("Unexpected key type"),
210        }
211    }
212
213    #[test]
214    fn test_create_server_config() {
215        // Should now succeed
216        let config = create_server_config().unwrap();
217        assert!(Arc::strong_count(&config) >= 1);
218    }
219}