1use std::path::PathBuf;
16
17#[derive(Clone, Debug)]
20pub struct ServerTls {
21 pub cert_path: PathBuf,
23 pub key_path: PathBuf,
25}
26
27#[derive(Clone, Debug, Default)]
30pub struct ClientTls {
31 pub server_name: Option<String>,
34 pub ca_path: Option<PathBuf>,
37}
38
39#[cfg(feature = "tls")]
40mod imp {
41 use std::fs::File;
42 use std::io::BufReader;
43 use std::sync::Arc;
44
45 use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName};
46 use tokio_rustls::rustls::{self, ClientConfig, RootCertStore, ServerConfig};
47 use tokio_rustls::{TlsAcceptor, TlsConnector};
48
49 use super::{ClientTls, ServerTls};
50
51 fn provider() -> Arc<rustls::crypto::CryptoProvider> {
52 Arc::new(rustls::crypto::ring::default_provider())
53 }
54
55 fn load_certs(path: &std::path::Path) -> Result<Vec<CertificateDer<'static>>, String> {
56 let mut reader = BufReader::new(
57 File::open(path).map_err(|e| format!("open cert {}: {e}", path.display()))?,
58 );
59 rustls_pemfile::certs(&mut reader)
60 .collect::<Result<Vec<_>, _>>()
61 .map_err(|e| format!("read certs {}: {e}", path.display()))
62 }
63
64 fn load_key(path: &std::path::Path) -> Result<PrivateKeyDer<'static>, String> {
65 let mut reader = BufReader::new(
66 File::open(path).map_err(|e| format!("open key {}: {e}", path.display()))?,
67 );
68 rustls_pemfile::private_key(&mut reader)
69 .map_err(|e| format!("read key {}: {e}", path.display()))?
70 .ok_or_else(|| format!("no private key in {}", path.display()))
71 }
72
73 pub fn build_acceptor(cfg: &ServerTls) -> Result<TlsAcceptor, String> {
76 let certs = load_certs(&cfg.cert_path)?;
77 let key = load_key(&cfg.key_path)?;
78 let config = ServerConfig::builder_with_provider(provider())
79 .with_safe_default_protocol_versions()
80 .map_err(|e| format!("rustls protocol versions: {e}"))?
81 .with_no_client_auth()
82 .with_single_cert(certs, key)
83 .map_err(|e| format!("server cert/key: {e}"))?;
84 Ok(TlsAcceptor::from(Arc::new(config)))
85 }
86
87 pub fn build_connector(cfg: &ClientTls) -> Result<TlsConnector, String> {
90 let mut roots = RootCertStore::empty();
91 match &cfg.ca_path {
92 Some(path) => {
93 for cert in load_certs(path)? {
94 roots
95 .add(cert)
96 .map_err(|e| format!("add CA from {}: {e}", path.display()))?;
97 }
98 }
99 None => {
100 let loaded = rustls_native_certs::load_native_certs();
101 if roots.is_empty() && loaded.certs.is_empty() {
102 return Err(format!(
103 "no native root certificates available ({} load error(s))",
104 loaded.errors.len()
105 ));
106 }
107 for cert in loaded.certs {
108 let _ = roots.add(cert);
109 }
110 }
111 }
112 let config = ClientConfig::builder_with_provider(provider())
113 .with_safe_default_protocol_versions()
114 .map_err(|e| format!("rustls protocol versions: {e}"))?
115 .with_root_certificates(roots)
116 .with_no_client_auth();
117 Ok(TlsConnector::from(Arc::new(config)))
118 }
119
120 pub fn server_name(cfg: &ClientTls, host: &str) -> Result<ServerName<'static>, String> {
122 let name = cfg.server_name.clone().unwrap_or_else(|| host.to_owned());
123 ServerName::try_from(name).map_err(|e| format!("invalid TLS server name: {e}"))
124 }
125}
126
127#[cfg(feature = "tls")]
128pub use imp::{build_acceptor, build_connector, server_name};