1use std::path::{Path, PathBuf};
10use std::sync::Arc;
11
12use rustls::crypto::ring as ring_provider;
13use rustls::pki_types::{CertificateDer, PrivateKeyDer};
14use serde::{Deserialize, Serialize};
15use thiserror::Error;
16use tracing::{debug, info, warn};
17
18#[derive(Debug, Error)]
20pub enum TlsError {
21 #[error("failed to read PEM file {path}: {source}")]
23 PemRead {
24 path: String,
25 source: std::io::Error,
26 },
27
28 #[error("no certificates found in {path}")]
30 NoCertificates { path: String },
31
32 #[error("no private key found in {path}")]
34 NoPrivateKey { path: String },
35
36 #[error("PKCS#11 URI detected: {uri} (use kipuka-hsm crate)")]
38 Pkcs11Uri { uri: String },
39
40 #[error("TLS configuration error: {0}")]
42 Config(String),
43
44 #[error("I/O error: {0}")]
46 Io(#[from] std::io::Error),
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct TlsConfig {
52 pub cert_chain_path: PathBuf,
54 pub private_key_path: PathBuf,
56 pub client_ca_path: Option<PathBuf>,
58 pub require_client_cert: bool,
60 pub min_version: Option<String>,
62}
63
64pub struct TlsConfigBuilder {
71 cert_chain: Vec<CertificateDer<'static>>,
72 private_key: Option<PrivateKeyDer<'static>>,
73 client_verifier: Option<Arc<dyn rustls::server::danger::ClientCertVerifier>>,
74}
75
76impl TlsConfigBuilder {
77 pub fn new() -> Self {
79 Self {
80 cert_chain: Vec::new(),
81 private_key: None,
82 client_verifier: None,
83 }
84 }
85
86 pub fn with_cert_chain(mut self, path: &Path) -> Result<Self, TlsError> {
88 let pem_data = std::fs::read(path).map_err(|e| TlsError::PemRead {
89 path: path.display().to_string(),
90 source: e,
91 })?;
92
93 let certs: Vec<CertificateDer<'static>> = rustls_pemfile::certs(&mut &pem_data[..])
94 .filter_map(|r| r.ok())
95 .collect();
96
97 if certs.is_empty() {
98 return Err(TlsError::NoCertificates {
99 path: path.display().to_string(),
100 });
101 }
102
103 info!(
104 path = %path.display(),
105 count = certs.len(),
106 "loaded certificate chain"
107 );
108
109 self.cert_chain = certs;
110 Ok(self)
111 }
112
113 pub fn with_private_key(mut self, path: &Path) -> Result<Self, TlsError> {
118 let pem_data = std::fs::read(path).map_err(|e| TlsError::PemRead {
119 path: path.display().to_string(),
120 source: e,
121 })?;
122
123 if let Ok(text) = std::str::from_utf8(&pem_data) {
125 let trimmed = text.trim();
126 if trimmed.starts_with("pkcs11:") {
127 return Err(TlsError::Pkcs11Uri {
128 uri: trimmed.to_owned(),
129 });
130 }
131 }
132
133 let key = rustls_pemfile::private_key(&mut &pem_data[..])
134 .map_err(|e| TlsError::Config(format!("key parse error: {e}")))?
135 .ok_or_else(|| TlsError::NoPrivateKey {
136 path: path.display().to_string(),
137 })?;
138
139 debug!(path = %path.display(), "loaded private key");
140
141 self.private_key = Some(key);
142 Ok(self)
143 }
144
145 pub fn with_client_auth(mut self, ca_path: &Path, required: bool) -> Result<Self, TlsError> {
147 let pem_data = std::fs::read(ca_path).map_err(|e| TlsError::PemRead {
148 path: ca_path.display().to_string(),
149 source: e,
150 })?;
151
152 let mut root_store = rustls::RootCertStore::empty();
153 let ca_certs: Vec<CertificateDer<'static>> = rustls_pemfile::certs(&mut &pem_data[..])
154 .filter_map(|r| r.ok())
155 .collect();
156
157 if ca_certs.is_empty() {
158 return Err(TlsError::NoCertificates {
159 path: ca_path.display().to_string(),
160 });
161 }
162
163 for cert in &ca_certs {
164 root_store.add(cert.clone()).map_err(|e| {
165 TlsError::Config(format!("failed to add CA cert to trust store: {e}"))
166 })?;
167 }
168
169 let verifier = if required {
170 rustls::server::WebPkiClientVerifier::builder(Arc::new(root_store))
171 .build()
172 .map_err(|e| TlsError::Config(format!("client verifier build error: {e}")))?
173 } else {
174 rustls::server::WebPkiClientVerifier::builder(Arc::new(root_store))
175 .allow_unauthenticated()
176 .build()
177 .map_err(|e| TlsError::Config(format!("client verifier build error: {e}")))?
178 };
179
180 info!(
181 ca_path = %ca_path.display(),
182 ca_count = ca_certs.len(),
183 required,
184 "configured client certificate verification"
185 );
186
187 self.client_verifier = Some(verifier);
188 Ok(self)
189 }
190
191 pub fn build(self) -> Result<rustls::ServerConfig, TlsError> {
197 let key = self
198 .private_key
199 .ok_or_else(|| TlsError::Config("no private key loaded".into()))?;
200
201 if self.cert_chain.is_empty() {
202 return Err(TlsError::Config("no certificate chain loaded".into()));
203 }
204
205 let provider = Arc::new(ring_provider::default_provider());
206
207 let mut config = if let Some(verifier) = self.client_verifier {
208 rustls::ServerConfig::builder_with_provider(provider)
209 .with_protocol_versions(&[&rustls::version::TLS12, &rustls::version::TLS13])
210 .map_err(|e| TlsError::Config(format!("protocol version error: {e}")))?
211 .with_client_cert_verifier(verifier)
212 .with_single_cert(self.cert_chain, key)
213 .map_err(|e| TlsError::Config(format!("server config error: {e}")))?
214 } else {
215 rustls::ServerConfig::builder_with_provider(provider)
216 .with_protocol_versions(&[&rustls::version::TLS12, &rustls::version::TLS13])
217 .map_err(|e| TlsError::Config(format!("protocol version error: {e}")))?
218 .with_no_client_auth()
219 .with_single_cert(self.cert_chain, key)
220 .map_err(|e| TlsError::Config(format!("server config error: {e}")))?
221 };
222
223 config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
224
225 info!("TLS server config built (TLS 1.2+, NIAP CA PP compliant)");
226
227 Ok(config)
228 }
229
230 pub fn from_config(config: &TlsConfig) -> Result<rustls::ServerConfig, TlsError> {
232 let mut builder = Self::new()
233 .with_cert_chain(&config.cert_chain_path)?
234 .with_private_key(&config.private_key_path)?;
235
236 if let Some(ref ca_path) = config.client_ca_path {
237 builder = builder.with_client_auth(ca_path, config.require_client_cert)?;
238 } else if config.require_client_cert {
239 warn!("require_client_cert is true but no client_ca_path configured");
240 }
241
242 builder.build()
243 }
244}
245
246impl Default for TlsConfigBuilder {
247 fn default() -> Self {
248 Self::new()
249 }
250}