1use crate::connection_error::ConnectionError;
11use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
12use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
13use rustls::{
14 ClientConfig, DigitallySignedStruct, Error as RustlsError, RootCertStore, SignatureScheme,
15};
16use std::sync::Arc;
17use tokio::net::TcpStream;
18use tokio_rustls::TlsConnector;
19use tracing::{debug, warn};
20
21pub use tokio_rustls::client::TlsStream;
23
24#[derive(Debug, Clone)]
26pub struct TlsConfig {
27 pub use_tls: bool,
29 pub tls_verify_cert: bool,
31 pub tls_cert_path: Option<String>,
33}
34
35impl Default for TlsConfig {
36 fn default() -> Self {
37 Self {
38 use_tls: false,
39 tls_verify_cert: true,
40 tls_cert_path: None,
41 }
42 }
43}
44
45impl TlsConfig {
46 pub fn builder() -> TlsConfigBuilder {
58 TlsConfigBuilder::default()
59 }
60}
61
62#[derive(Debug, Clone)]
99pub struct TlsConfigBuilder {
100 use_tls: bool,
101 tls_verify_cert: bool,
102 tls_cert_path: Option<String>,
103}
104
105impl Default for TlsConfigBuilder {
106 fn default() -> Self {
107 Self {
108 use_tls: false,
109 tls_verify_cert: true, tls_cert_path: None,
111 }
112 }
113}
114
115impl TlsConfigBuilder {
116 pub fn enabled(mut self, use_tls: bool) -> Self {
120 self.use_tls = use_tls;
121 self
122 }
123
124 pub fn verify_cert(mut self, verify: bool) -> Self {
131 self.tls_verify_cert = verify;
132 self
133 }
134
135 pub fn cert_path<S: Into<String>>(mut self, path: S) -> Self {
139 self.tls_cert_path = Some(path.into());
140 self
141 }
142
143 pub fn build(self) -> TlsConfig {
145 TlsConfig {
146 use_tls: self.use_tls,
147 tls_verify_cert: self.tls_verify_cert,
148 tls_cert_path: self.tls_cert_path,
149 }
150 }
151}
152
153mod rustls_backend {
156 use super::*;
157
158 #[derive(Debug)]
160 pub struct CertificateLoadResult {
161 pub root_store: RootCertStore,
162 pub sources: Vec<String>,
163 }
164
165 #[derive(Debug)]
171 pub struct NoVerifier;
172
173 impl ServerCertVerifier for NoVerifier {
174 fn verify_server_cert(
175 &self,
176 _end_entity: &CertificateDer<'_>,
177 _intermediates: &[CertificateDer<'_>],
178 _server_name: &ServerName<'_>,
179 _ocsp_response: &[u8],
180 _now: UnixTime,
181 ) -> Result<ServerCertVerified, RustlsError> {
182 Ok(ServerCertVerified::assertion())
184 }
185
186 fn verify_tls12_signature(
187 &self,
188 _message: &[u8],
189 _cert: &CertificateDer<'_>,
190 _dss: &DigitallySignedStruct,
191 ) -> Result<HandshakeSignatureValid, RustlsError> {
192 Ok(HandshakeSignatureValid::assertion())
194 }
195
196 fn verify_tls13_signature(
197 &self,
198 _message: &[u8],
199 _cert: &CertificateDer<'_>,
200 _dss: &DigitallySignedStruct,
201 ) -> Result<HandshakeSignatureValid, RustlsError> {
202 Ok(HandshakeSignatureValid::assertion())
204 }
205
206 fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
207 vec![
209 SignatureScheme::RSA_PKCS1_SHA1,
210 SignatureScheme::ECDSA_SHA1_Legacy,
211 SignatureScheme::RSA_PKCS1_SHA256,
212 SignatureScheme::ECDSA_NISTP256_SHA256,
213 SignatureScheme::RSA_PKCS1_SHA384,
214 SignatureScheme::ECDSA_NISTP384_SHA384,
215 SignatureScheme::RSA_PKCS1_SHA512,
216 SignatureScheme::ECDSA_NISTP521_SHA512,
217 SignatureScheme::RSA_PSS_SHA256,
218 SignatureScheme::RSA_PSS_SHA384,
219 SignatureScheme::RSA_PSS_SHA512,
220 SignatureScheme::ED25519,
221 SignatureScheme::ED448,
222 ]
223 }
224 }
225}
226
227pub struct TlsManager {
233 config: TlsConfig,
234 cached_connector: Arc<TlsConnector>,
239}
240
241impl std::fmt::Debug for TlsManager {
242 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243 f.debug_struct("TlsManager")
244 .field("config", &self.config)
245 .field("cached_connector", &"<TlsConnector>")
246 .finish()
247 }
248}
249
250impl Clone for TlsManager {
251 fn clone(&self) -> Self {
252 Self {
253 config: self.config.clone(),
254 cached_connector: Arc::clone(&self.cached_connector),
255 }
256 }
257}
258
259impl TlsManager {
260 pub fn new(config: TlsConfig) -> Result<Self, anyhow::Error> {
266 let cert_result = Self::load_certificates_sync(&config)?;
268 let client_config = Self::create_optimized_config_inner(cert_result.root_store, &config)?;
269
270 debug!(
271 "TLS: Initialized with certificate sources: {}",
272 cert_result.sources.join(", ")
273 );
274
275 let cached_connector = Arc::new(TlsConnector::from(Arc::new(client_config)));
276
277 Ok(Self {
278 config,
279 cached_connector,
280 })
281 }
282
283 pub async fn handshake(
285 &self,
286 stream: TcpStream,
287 hostname: &str,
288 backend_name: &str,
289 ) -> Result<TlsStream<TcpStream>, anyhow::Error> {
290 use anyhow::Context;
291
292 debug!("TLS: Connecting to {} with cached config", hostname);
293
294 let domain = rustls_pki_types::ServerName::try_from(hostname)
295 .context("Invalid hostname for TLS")?
296 .to_owned();
297
298 self.cached_connector
299 .connect(domain, stream)
300 .await
301 .map_err(|e| {
302 ConnectionError::TlsHandshake {
303 backend: backend_name.to_string(),
304 source: Box::new(e),
305 }
306 .into()
307 })
308 }
309
310 fn load_certificates_sync(
312 config: &TlsConfig,
313 ) -> Result<rustls_backend::CertificateLoadResult, anyhow::Error> {
314 let mut root_store = RootCertStore::empty();
315 let mut sources = Vec::new();
316
317 if let Some(cert_path) = &config.tls_cert_path {
319 debug!("TLS: Loading custom CA certificate from: {}", cert_path);
320 Self::load_custom_certificate_sync(&mut root_store, cert_path)?;
321 sources.push("custom certificate".to_string());
322 }
323
324 let system_count = Self::load_system_certificates_sync(&mut root_store)?;
326 if system_count > 0 {
327 debug!(
328 "TLS: Loaded {} certificates from system store",
329 system_count
330 );
331 sources.push("system certificates".to_string());
332 }
333
334 if root_store.is_empty() {
336 debug!("TLS: No system certificates available, using Mozilla CA bundle fallback");
337 root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
338 sources.push("Mozilla CA bundle".to_string());
339 }
340
341 Ok(rustls_backend::CertificateLoadResult {
342 root_store,
343 sources,
344 })
345 }
346
347 fn load_custom_certificate_sync(
349 root_store: &mut RootCertStore,
350 cert_path: &str,
351 ) -> Result<(), anyhow::Error> {
352 use anyhow::Context;
353
354 let cert_data = std::fs::read(cert_path)
355 .with_context(|| format!("Failed to read TLS certificate from {}", cert_path))?;
356
357 let certs = rustls_pemfile::certs(&mut cert_data.as_slice())
358 .collect::<Result<Vec<_>, _>>()
359 .context("Failed to parse TLS certificate")?;
360
361 for cert in certs {
362 root_store
363 .add(cert)
364 .context("Failed to add custom certificate to store")?;
365 }
366
367 Ok(())
368 }
369
370 fn load_system_certificates_sync(
372 root_store: &mut RootCertStore,
373 ) -> Result<usize, anyhow::Error> {
374 let cert_result = rustls_native_certs::load_native_certs();
375 let mut added_count = 0;
376
377 for cert in cert_result.certs {
378 if root_store.add(cert).is_ok() {
379 added_count += 1;
380 }
381 }
382
383 for error in cert_result.errors {
385 warn!("TLS: Certificate loading error: {}", error);
386 }
387
388 Ok(added_count)
389 }
390
391 fn create_optimized_config_inner(
393 root_store: RootCertStore,
394 config: &TlsConfig,
395 ) -> Result<ClientConfig, anyhow::Error> {
396 use anyhow::Context;
397 use rustls_backend::NoVerifier;
398
399 let mut client_config = if config.tls_verify_cert {
400 debug!("TLS: Certificate verification enabled with ring crypto provider");
401 ClientConfig::builder_with_provider(Arc::new(rustls::crypto::ring::default_provider()))
402 .with_safe_default_protocol_versions()
403 .context("Failed to create TLS config with ring provider")?
404 .with_root_certificates(root_store)
405 .with_no_client_auth()
406 } else {
407 warn!(
408 "TLS: Certificate verification DISABLED - this is insecure and should only be used for testing!"
409 );
410 ClientConfig::builder_with_provider(Arc::new(rustls::crypto::ring::default_provider()))
412 .with_safe_default_protocol_versions()
413 .context("Failed to create TLS config with ring provider")?
414 .dangerous()
415 .with_custom_certificate_verifier(Arc::new(NoVerifier))
416 .with_no_client_auth()
417 };
418
419 client_config.enable_early_data = true; client_config.resumption = rustls::client::Resumption::default(); Ok(client_config)
430 }
431}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436
437 #[test]
438 fn test_tls_config_default() {
439 let config = TlsConfig::default();
440 assert!(!config.use_tls);
441 assert!(config.tls_verify_cert);
442 assert!(config.tls_cert_path.is_none());
443 }
444
445 #[test]
446 fn test_tls_config_builder_default() {
447 let config = TlsConfig::builder().build();
448 assert!(!config.use_tls);
449 assert!(config.tls_verify_cert); assert!(config.tls_cert_path.is_none());
451 }
452
453 #[test]
454 fn test_tls_config_builder_enabled() {
455 let config = TlsConfig::builder().enabled(true).verify_cert(true).build();
456 assert!(config.use_tls);
457 assert!(config.tls_verify_cert);
458 assert!(config.tls_cert_path.is_none());
459 }
460
461 #[test]
462 fn test_tls_config_builder_with_cert_path() {
463 let config = TlsConfig::builder()
464 .enabled(true)
465 .verify_cert(true)
466 .cert_path("/path/to/cert.pem")
467 .build();
468 assert!(config.use_tls);
469 assert!(config.tls_verify_cert);
470 assert_eq!(config.tls_cert_path, Some("/path/to/cert.pem".to_string()));
471 }
472
473 #[test]
474 fn test_tls_config_builder_insecure() {
475 let config = TlsConfig::builder()
476 .enabled(true)
477 .verify_cert(false)
478 .build();
479 assert!(config.use_tls);
480 assert!(!config.tls_verify_cert);
481 assert!(config.tls_cert_path.is_none());
482 }
483
484 #[test]
485 fn test_tls_config_builder_fluent_api() {
486 let config = TlsConfig::builder()
487 .enabled(true)
488 .verify_cert(true)
489 .cert_path("/custom/ca.pem".to_string())
490 .build();
491 assert!(config.use_tls);
492 assert!(config.tls_verify_cert);
493 assert_eq!(config.tls_cert_path, Some("/custom/ca.pem".to_string()));
494 }
495
496 #[test]
497 fn test_tls_manager_creation() {
498 let config = TlsConfig::default();
499 let manager = TlsManager::new(config).unwrap();
500 assert!(Arc::strong_count(&manager.cached_connector) >= 1);
502 }
503
504 #[test]
505 fn test_certificate_loading() {
506 let config = TlsConfig::default();
507
508 let result = TlsManager::load_certificates_sync(&config).unwrap();
509 assert!(!result.root_store.is_empty());
510 assert!(!result.sources.is_empty());
512 assert!(
514 result
515 .sources
516 .iter()
517 .any(|s| s.contains("Mozilla") || s.contains("system"))
518 );
519 }
520
521 #[test]
522 fn test_tls_config_builder_chaining() {
523 let config = TlsConfig::builder()
524 .enabled(false)
525 .verify_cert(true)
526 .enabled(true) .build();
528
529 assert!(config.use_tls);
530 assert!(config.tls_verify_cert);
531 }
532
533 #[test]
534 fn test_tls_config_clone() {
535 let config1 = TlsConfig::builder()
536 .enabled(true)
537 .cert_path("/test/path")
538 .build();
539
540 let config2 = config1.clone();
541 assert_eq!(config1.use_tls, config2.use_tls);
542 assert_eq!(config1.tls_verify_cert, config2.tls_verify_cert);
543 assert_eq!(config1.tls_cert_path, config2.tls_cert_path);
544 }
545
546 #[test]
547 fn test_tls_manager_clone() {
548 let config = TlsConfig::default();
549 let manager1 = TlsManager::new(config).unwrap();
550 let manager2 = manager1.clone();
551
552 assert!(Arc::ptr_eq(
554 &manager1.cached_connector,
555 &manager2.cached_connector
556 ));
557 }
558
559 #[test]
560 fn test_tls_manager_debug() {
561 let config = TlsConfig::default();
562 let manager = TlsManager::new(config).unwrap();
563 let debug_str = format!("{:?}", manager);
564
565 assert!(debug_str.contains("TlsManager"));
566 assert!(debug_str.contains("<TlsConnector>"));
567 }
568
569 #[test]
570 fn test_tls_config_builder_cert_path_string_types() {
571 let config1 = TlsConfig::builder().cert_path("/path/to/cert.pem").build();
573 assert_eq!(config1.tls_cert_path, Some("/path/to/cert.pem".to_string()));
574
575 let config2 = TlsConfig::builder()
577 .cert_path("/another/path.pem".to_string())
578 .build();
579 assert_eq!(config2.tls_cert_path, Some("/another/path.pem".to_string()));
580 }
581
582 #[test]
583 fn test_no_verifier_supported_schemes() {
584 use rustls_backend::NoVerifier;
585
586 let verifier = NoVerifier;
587 let schemes = verifier.supported_verify_schemes();
588
589 assert!(schemes.contains(&SignatureScheme::RSA_PKCS1_SHA256));
591 assert!(schemes.contains(&SignatureScheme::ECDSA_NISTP256_SHA256));
592 assert!(schemes.contains(&SignatureScheme::ED25519));
593 assert!(schemes.len() >= 10); }
595
596 #[test]
597 fn test_certificate_load_result_sources() {
598 let config = TlsConfig::default();
599 let result = TlsManager::load_certificates_sync(&config).unwrap();
600
601 assert!(!result.sources.is_empty());
603
604 for source in &result.sources {
606 assert!(!source.is_empty());
607 }
608 }
609
610 #[test]
611 fn test_tls_config_builder_defaults_are_secure() {
612 let config = TlsConfig::builder().enabled(true).build();
614
615 assert!(config.use_tls);
616 assert!(config.tls_verify_cert); assert!(config.tls_cert_path.is_none());
618 }
619
620 #[test]
621 fn test_tls_manager_with_verify_disabled() {
622 let config = TlsConfig::builder()
623 .enabled(true)
624 .verify_cert(false)
625 .build();
626 let manager = TlsManager::new(config);
627
628 assert!(manager.is_ok());
630 }
631
632 #[test]
633 fn test_tls_manager_with_verify_enabled() {
634 let config = TlsConfig::builder().enabled(true).verify_cert(true).build();
635 let manager = TlsManager::new(config);
636
637 assert!(manager.is_ok());
639 }
640
641 #[test]
642 fn test_certificate_loading_fallback_to_mozilla_bundle() {
643 let config = TlsConfig::default();
645 let result = TlsManager::load_certificates_sync(&config).unwrap();
646
647 assert!(!result.root_store.is_empty());
649 assert!(!result.sources.is_empty());
650 }
651
652 #[test]
653 fn test_tls_config_debug_format() {
654 let config = TlsConfig::builder()
655 .enabled(true)
656 .verify_cert(false)
657 .cert_path("/test")
658 .build();
659
660 let debug_str = format!("{:?}", config);
661
662 assert!(debug_str.contains("TlsConfig"));
663 assert!(debug_str.contains("use_tls"));
664 assert!(debug_str.contains("tls_verify_cert"));
665 }
666
667 #[test]
668 fn test_tls_config_builder_debug_format() {
669 let builder = TlsConfig::builder().enabled(true).verify_cert(false);
670
671 let debug_str = format!("{:?}", builder);
672
673 assert!(debug_str.contains("TlsConfigBuilder"));
674 }
675
676 #[test]
677 fn test_no_verifier_debug_format() {
678 use rustls_backend::NoVerifier;
679
680 let verifier = NoVerifier;
681 let debug_str = format!("{:?}", verifier);
682
683 assert!(debug_str.contains("NoVerifier"));
684 }
685
686 #[test]
687 fn test_certificate_load_result_debug_format() {
688 let config = TlsConfig::default();
689 let result = TlsManager::load_certificates_sync(&config).unwrap();
690
691 let debug_str = format!("{:?}", result);
692
693 assert!(debug_str.contains("CertificateLoadResult"));
694 assert!(debug_str.contains("root_store"));
695 assert!(debug_str.contains("sources"));
696 }
697
698 #[test]
699 fn test_tls_config_builder_cert_path_empty_string() {
700 let config = TlsConfig::builder().cert_path("").build();
702
703 assert_eq!(config.tls_cert_path, Some(String::new()));
704 }
705
706 #[test]
707 fn test_tls_config_builder_cert_path_with_spaces() {
708 let config = TlsConfig::builder()
709 .cert_path(" /path/with spaces.pem ")
710 .build();
711
712 assert_eq!(
714 config.tls_cert_path,
715 Some(" /path/with spaces.pem ".to_string())
716 );
717 }
718
719 #[test]
720 fn test_multiple_tls_managers_from_same_config() {
721 let config = TlsConfig::builder()
722 .enabled(true)
723 .verify_cert(false)
724 .build();
725
726 let manager1 = TlsManager::new(config.clone()).unwrap();
727 let manager2 = TlsManager::new(config.clone()).unwrap();
728
729 let debug1 = format!("{:?}", manager1);
731 let debug2 = format!("{:?}", manager2);
732
733 assert!(debug1.contains("TlsManager"));
734 assert!(debug2.contains("TlsManager"));
735 }
736
737 #[test]
738 fn test_tls_config_all_combinations() {
739 for use_tls in [true, false] {
741 for verify_cert in [true, false] {
742 let config = TlsConfig::builder()
743 .enabled(use_tls)
744 .verify_cert(verify_cert)
745 .build();
746
747 assert_eq!(config.use_tls, use_tls);
748 assert_eq!(config.tls_verify_cert, verify_cert);
749
750 if use_tls {
752 let manager = TlsManager::new(config);
753 assert!(manager.is_ok());
754 }
755 }
756 }
757 }
758
759 #[test]
760 fn test_tls_config_builder_method_chaining_order() {
761 let config1 = TlsConfig::builder()
763 .enabled(true)
764 .verify_cert(false)
765 .cert_path("/test")
766 .build();
767
768 let config2 = TlsConfig::builder()
769 .cert_path("/test")
770 .verify_cert(false)
771 .enabled(true)
772 .build();
773
774 assert_eq!(config1.use_tls, config2.use_tls);
775 assert_eq!(config1.tls_verify_cert, config2.tls_verify_cert);
776 assert_eq!(config1.tls_cert_path, config2.tls_cert_path);
777 }
778
779 #[test]
780 fn test_tls_config_default_matches_builder_default() {
781 let default_config = TlsConfig::default();
782 let builder_config = TlsConfig::builder().build();
783
784 assert_eq!(default_config.use_tls, builder_config.use_tls);
785 assert_eq!(
786 default_config.tls_verify_cert,
787 builder_config.tls_verify_cert
788 );
789 assert_eq!(default_config.tls_cert_path, builder_config.tls_cert_path);
790 }
791}