1use std::fs::File;
2use std::io::BufReader;
3use std::path::PathBuf;
4use std::sync::Arc;
5
6use rustls::{Certificate, PrivateKey};
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>> {
31 let certs = load_certs(&config.cert_path)?;
32 let key = load_key(&config.key_path)?;
33 let versions: Vec<&'static rustls::SupportedProtocolVersion> = match config.min_version {
34 TlsVersion::Tls12 => vec![&rustls::version::TLS13, &rustls::version::TLS12],
35 TlsVersion::Tls13 => vec![&rustls::version::TLS13],
36 };
37 let builder = rustls::ServerConfig::builder()
38 .with_safe_default_cipher_suites()
39 .with_safe_default_kx_groups()
40 .with_protocol_versions(&versions)
41 .map_err(|err| ServerError::InvalidConfig(err.to_string()))?;
42 let mut server_config = if let Some(ca_path) = &config.ca_path {
43 let mut roots = rustls::RootCertStore::empty();
44 let ca_certs = load_certs(ca_path)?;
45 for cert in ca_certs {
46 roots
47 .add(&cert)
48 .map_err(|_| ServerError::InvalidConfig("invalid CA certificate".into()))?;
49 }
50 let verifier = rustls::server::AllowAnyAuthenticatedClient::new(roots);
51 builder
52 .with_client_cert_verifier(Arc::new(verifier))
53 .with_single_cert(certs, key)
54 .map_err(|err| ServerError::InvalidConfig(err.to_string()))?
55 } else {
56 builder
57 .with_no_client_auth()
58 .with_single_cert(certs, key)
59 .map_err(|err| ServerError::InvalidConfig(err.to_string()))?
60 };
61 server_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
62 Ok(Arc::new(server_config))
63}
64
65fn load_certs(path: &PathBuf) -> Result<Vec<Certificate>> {
66 let file = File::open(path).map_err(ServerError::Io)?;
67 let mut reader = BufReader::new(file);
68 let certs = rustls_pemfile::certs(&mut reader)
69 .map_err(|_| ServerError::InvalidConfig("invalid certificate file".into()))?
70 .into_iter()
71 .map(Certificate)
72 .collect();
73 Ok(certs)
74}
75
76fn load_key(path: &PathBuf) -> Result<PrivateKey> {
77 let file = File::open(path).map_err(ServerError::Io)?;
78 let mut reader = BufReader::new(file);
79 let keys = rustls_pemfile::pkcs8_private_keys(&mut reader)
80 .map_err(|_| ServerError::InvalidConfig("invalid private key file".into()))?;
81 if let Some(key) = keys.first() {
82 return Ok(PrivateKey(key.clone()));
83 }
84
85 let file = File::open(path).map_err(ServerError::Io)?;
86 let mut reader = BufReader::new(file);
87 let keys = rustls_pemfile::rsa_private_keys(&mut reader)
88 .map_err(|_| ServerError::InvalidConfig("invalid private key file".into()))?;
89 keys.first()
90 .cloned()
91 .map(PrivateKey)
92 .ok_or_else(|| ServerError::InvalidConfig("private key not found".into()))
93}