1use std::fs;
8use std::io::BufReader;
9use std::sync::Arc;
10
11use rustls::pki_types::{CertificateDer, PrivateKeyDer};
12
13use crate::error::HttpError;
14
15pub fn server_config(
24 cert_file: &str,
25 key_file: &str,
26) -> Result<Arc<rustls::ServerConfig>, HttpError> {
27 let certs = load_certs(cert_file)?;
28 let key = load_private_key(key_file)?;
29
30 let cfg = rustls::ServerConfig::builder()
31 .with_no_client_auth()
32 .with_single_cert(certs, key)
33 .map_err(|e| HttpError::Tls(e.to_string()))?;
34
35 Ok(Arc::new(cfg))
36}
37
38pub fn default_client_config() -> Arc<rustls::ClientConfig> {
48 use std::sync::OnceLock;
49 static CFG: OnceLock<Arc<rustls::ClientConfig>> = OnceLock::new();
50 Arc::clone(CFG.get_or_init(|| {
51 let mut roots = rustls::RootCertStore::empty();
52 roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
53 Arc::new(
54 rustls::ClientConfig::builder()
55 .with_root_certificates(roots)
56 .with_no_client_auth(),
57 )
58 }))
59}
60
61pub fn client_config_with_ca(ca_file: &str) -> Result<Arc<rustls::ClientConfig>, HttpError> {
64 let mut roots = rustls::RootCertStore::empty();
65 roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
66
67 let ca_certs = load_certs(ca_file)?;
68 for cert in ca_certs {
69 roots.add(cert).map_err(|e| HttpError::Tls(e.to_string()))?;
70 }
71
72 Ok(Arc::new(
73 rustls::ClientConfig::builder()
74 .with_root_certificates(roots)
75 .with_no_client_auth(),
76 ))
77}
78
79fn load_certs(path: &str) -> Result<Vec<CertificateDer<'static>>, HttpError> {
84 let f = fs::File::open(path).map_err(|e| HttpError::Io(e))?;
85 let mut r = BufReader::new(f);
86 rustls_pemfile::certs(&mut r)
87 .collect::<Result<Vec<_>, _>>()
88 .map_err(|e| HttpError::Tls(e.to_string()))
89}
90
91fn load_private_key(path: &str) -> Result<PrivateKeyDer<'static>, HttpError> {
92 let f = fs::File::open(path).map_err(HttpError::Io)?;
93 let mut r = BufReader::new(f);
94 rustls_pemfile::private_key(&mut r)
95 .map_err(|e| HttpError::Tls(e.to_string()))?
96 .ok_or_else(|| HttpError::Tls(format!("no private key found in {path}")))
97}