1use std::fs::File;
2use std::io::BufReader;
3use std::path::PathBuf;
4use std::sync::Arc;
5
6use rustls::pki_types::{CertificateDer, PrivateKeyDer};
7use serde::Deserialize;
8
9use crate::error::{Result, ServerError};
10
11#[derive(Clone, Debug, Deserialize)]
13pub struct TlsConfig {
14 pub cert_path: PathBuf,
15 pub key_path: PathBuf,
16 pub ca_path: Option<PathBuf>,
17 #[serde(default)]
18 pub min_version: TlsVersion,
19}
20
21#[derive(Clone, Copy, Debug, Default, Deserialize)]
22#[serde(rename_all = "snake_case")]
23pub enum TlsVersion {
24 #[default]
25 Tls12,
26 Tls13,
27}
28
29pub fn build_rustls_config(config: &TlsConfig) -> Result<Arc<rustls::ServerConfig>> {
45 let certs = load_certs(&config.cert_path)?;
46 let key = load_key(&config.key_path)?;
47 let versions: Vec<&'static rustls::SupportedProtocolVersion> = match config.min_version {
48 TlsVersion::Tls12 => vec![&rustls::version::TLS13, &rustls::version::TLS12],
49 TlsVersion::Tls13 => vec![&rustls::version::TLS13],
50 };
51 let provider = Arc::new(rustls::crypto::ring::default_provider());
52 let builder = rustls::ServerConfig::builder_with_provider(provider.clone())
53 .with_protocol_versions(&versions)
54 .map_err(|err| ServerError::InvalidConfig(err.to_string()))?;
55 let mut server_config = if let Some(ca_path) = &config.ca_path {
56 let mut roots = rustls::RootCertStore::empty();
57 let ca_certs = load_certs(ca_path)?;
58 for cert in ca_certs {
59 roots
60 .add(cert)
61 .map_err(|_| ServerError::InvalidConfig("invalid CA certificate".into()))?;
62 }
63 let verifier =
64 rustls::server::WebPkiClientVerifier::builder_with_provider(Arc::new(roots), provider)
65 .build()
66 .map_err(|err| ServerError::InvalidConfig(err.to_string()))?;
67 builder
68 .with_client_cert_verifier(verifier)
69 .with_single_cert(certs, key)
70 .map_err(|err| ServerError::InvalidConfig(err.to_string()))?
71 } else {
72 builder
73 .with_no_client_auth()
74 .with_single_cert(certs, key)
75 .map_err(|err| ServerError::InvalidConfig(err.to_string()))?
76 };
77 server_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
78 Ok(Arc::new(server_config))
79}
80
81fn load_certs(path: &PathBuf) -> Result<Vec<CertificateDer<'static>>> {
82 let file = File::open(path).map_err(ServerError::Io)?;
83 let mut reader = BufReader::new(file);
84 rustls_pemfile::certs(&mut reader)
85 .collect::<std::result::Result<Vec<_>, _>>()
86 .map_err(|_| ServerError::InvalidConfig("invalid certificate file".into()))
87}
88
89fn load_key(path: &PathBuf) -> Result<PrivateKeyDer<'static>> {
90 let file = File::open(path).map_err(ServerError::Io)?;
91 let mut reader = BufReader::new(file);
92 let keys: Vec<_> = rustls_pemfile::pkcs8_private_keys(&mut reader)
93 .collect::<std::result::Result<Vec<_>, _>>()
94 .map_err(|_| ServerError::InvalidConfig("invalid private key file".into()))?;
95 if let Some(key) = keys.into_iter().next() {
96 return Ok(PrivateKeyDer::Pkcs8(key));
97 }
98
99 let file = File::open(path).map_err(ServerError::Io)?;
100 let mut reader = BufReader::new(file);
101 let keys: Vec<_> = rustls_pemfile::rsa_private_keys(&mut reader)
102 .collect::<std::result::Result<Vec<_>, _>>()
103 .map_err(|_| ServerError::InvalidConfig("invalid private key file".into()))?;
104 keys.into_iter()
105 .next()
106 .map(PrivateKeyDer::Pkcs1)
107 .ok_or_else(|| ServerError::InvalidConfig("private key not found".into()))
108}