1use std::fmt;
12use std::path::{Path, PathBuf};
13use std::sync::Arc;
14
15use tokio_rustls::rustls::crypto::ring;
16use tokio_rustls::rustls::pki_types::pem::PemObject;
17use tokio_rustls::rustls::pki_types::CertificateDer;
18use tokio_rustls::rustls::server::danger::ClientCertVerifier;
19use tokio_rustls::rustls::server::WebPkiClientVerifier;
20use tokio_rustls::rustls::RootCertStore;
21
22#[derive(Debug)]
25pub enum MtlsError {
26 Io { path: PathBuf, source: std::io::Error },
27 NoRoots(PathBuf),
28 Verifier(tokio_rustls::rustls::server::VerifierBuilderError),
29}
30
31impl fmt::Display for MtlsError {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 match self {
34 Self::Io { path, source } =>
35 write!(f, "cannot read client-CA file {}: {source}", path.display()),
36 Self::NoRoots(p) =>
37 write!(f, "no usable CA certificates in {}", p.display()),
38 Self::Verifier(e) => write!(f, "client-cert verifier build error: {e}"),
39 }
40 }
41}
42
43impl std::error::Error for MtlsError {}
44
45pub fn verifier_from_ca_file(ca_path: &Path) -> Result<Arc<dyn ClientCertVerifier>, MtlsError> {
49 let data = std::fs::read(ca_path)
50 .map_err(|source| MtlsError::Io { path: ca_path.to_path_buf(), source })?;
51
52 let mut roots = RootCertStore::empty();
53 for cert in CertificateDer::pem_slice_iter(&data) {
54 let cert = cert.map_err(|e| MtlsError::Io {
55 path: ca_path.to_path_buf(),
56 source: std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()),
57 })?;
58 let _ = roots.add(cert);
61 }
62 if roots.is_empty() {
63 return Err(MtlsError::NoRoots(ca_path.to_path_buf()));
64 }
65
66 WebPkiClientVerifier::builder_with_provider(Arc::new(roots), Arc::new(ring::default_provider()))
67 .build()
68 .map_err(MtlsError::Verifier)
69}