use super::material::Material;
use rustls::pki_types::pem::PemObject;
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use rustls::{ClientConfig, RootCertStore, ServerConfig};
use std::path::Path;
use std::sync::Arc;
pub fn server_config(m: &Material) -> Result<Arc<ServerConfig>, String> {
let provider = Arc::new(rustls::crypto::ring::default_provider());
let verifier = rustls::server::WebPkiClientVerifier::builder_with_provider(
Arc::new(anchors(&m.anchors)?),
Arc::clone(&provider),
)
.build()
.map_err(|e| format!("{}: client verifier: {e}", m.anchors.display()))?;
let (chain, key) = identity(&m.chain, &m.key)?;
let config = ServerConfig::builder_with_provider(provider)
.with_safe_default_protocol_versions()
.map_err(|e| format!("tls versions: {e}"))?
.with_client_cert_verifier(verifier)
.with_single_cert(chain, key)
.map_err(|e| format!("{}: server identity: {e}", m.chain.display()))?;
Ok(Arc::new(config))
}
pub fn client_config(m: &Material) -> Result<Arc<ClientConfig>, String> {
let provider = Arc::new(rustls::crypto::ring::default_provider());
let (chain, key) = identity(&m.chain, &m.key)?;
let config = ClientConfig::builder_with_provider(provider)
.with_safe_default_protocol_versions()
.map_err(|e| format!("tls versions: {e}"))?
.with_root_certificates(anchors(&m.anchors)?)
.with_client_auth_cert(chain, key)
.map_err(|e| format!("{}: client identity: {e}", m.chain.display()))?;
Ok(Arc::new(config))
}
fn anchors(path: &Path) -> Result<RootCertStore, String> {
let mut store = RootCertStore::empty();
for anchor in
CertificateDer::pem_file_iter(path).map_err(|e| format!("{}: {e}", path.display()))?
{
let anchor = anchor.map_err(|e| format!("{}: {e}", path.display()))?;
store
.add(anchor)
.map_err(|e| format!("{}: {e}", path.display()))?;
}
if store.is_empty() {
return Err(format!("{}: no certificate in it", path.display()));
}
Ok(store)
}
fn identity(
chain: &Path,
key: &Path,
) -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>), String> {
let certs: Vec<CertificateDer<'static>> = CertificateDer::pem_file_iter(chain)
.map_err(|e| format!("{}: {e}", chain.display()))?
.collect::<Result<_, _>>()
.map_err(|e| format!("{}: {e}", chain.display()))?;
if certs.is_empty() {
return Err(format!("{}: no certificate in it", chain.display()));
}
let private =
PrivateKeyDer::from_pem_file(key).map_err(|e| format!("{}: {e}", key.display()))?;
Ok((certs, private))
}
#[cfg(test)]
mod tests;