use std::path::Path;
use std::sync::Arc;
use rustls::pki_types::pem::PemObject;
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use rustls::{ClientConfig, RootCertStore};
use super::material::Material;
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)?;
if let Some(said) = chain
.first()
.and_then(|leaf| super::leaf::refusal(leaf, &m.chain))
{
return Err(said);
}
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))
}
pub(crate) 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)
}
pub(crate) 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;