1use anyhow::Context;
2use rustls_pki_types::pem::PemObject;
3use std::{
4 path::{Path, PathBuf},
5 sync::Arc,
6};
7use tokio_rustls::{rustls, TlsAcceptor};
8
9#[derive(Clone)]
13pub struct TlsConfig {
14 pub cert_path: PathBuf,
16 pub key_path: PathBuf,
18}
19
20impl TlsConfig {
21 pub(super) fn server_config(&self) -> anyhow::Result<TlsAcceptor> {
23 let certs = load_certs(&self.cert_path)?;
24 let private_key = load_key(&self.key_path)?;
25
26 let cfg = rustls::ServerConfig::builder()
27 .with_no_client_auth()
28 .with_single_cert(certs, private_key)
29 .map_err(|e| anyhow::anyhow!("{}", e))?;
30
31 Ok(Arc::new(cfg).into())
32 }
33}
34
35fn load_certs(
37 path: impl AsRef<Path>,
38) -> anyhow::Result<Vec<rustls_pki_types::CertificateDer<'static>>> {
39 rustls_pki_types::CertificateDer::pem_file_iter(&path)
40 .and_then(Iterator::collect)
41 .with_context(|| {
42 format!(
43 "failed to load certificate(s) from '{}'",
44 path.as_ref().display()
45 )
46 })
47}
48
49fn load_key(path: impl AsRef<Path>) -> anyhow::Result<rustls_pki_types::PrivateKeyDer<'static>> {
51 rustls_pki_types::PrivateKeyDer::from_pem_file(&path).with_context(|| {
52 format!(
53 "failed to load private key from '{}'",
54 path.as_ref().display()
55 )
56 })
57}
58
59#[cfg(test)]
60mod tests {
61 use rustls_pki_types::pem;
62
63 use super::*;
64
65 const TESTDATA_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/testdata");
66
67 #[test]
68 fn test_read_non_existing_cert() {
69 let path = Path::new(TESTDATA_DIR).join("non-existing-file.pem");
70 match load_certs(path).unwrap_err().downcast().unwrap() {
71 pem::Error::Io(err) => assert_eq!(err.kind(), std::io::ErrorKind::NotFound),
72 other => panic!("expected Error::Io error got {other:?}"),
73 }
74 }
75
76 #[test]
77 fn test_read_invalid_cert() {
78 let path = Path::new(TESTDATA_DIR).join("invalid-cert.pem");
79 match load_certs(path).unwrap_err().downcast().unwrap() {
80 pem::Error::MissingSectionEnd { .. } => (),
81 other => panic!("expected Error::MissingSectionEnd got {other:?}"),
82 }
83 }
84
85 #[test]
86 fn test_read_valid_cert() {
87 let path = Path::new(TESTDATA_DIR).join("valid-cert.pem");
88 let certs = load_certs(path).unwrap();
89 assert_eq!(certs.len(), 2);
90 }
91
92 #[test]
93 fn test_read_non_existing_private_key() {
94 let path = Path::new(TESTDATA_DIR).join("non-existing-file.pem");
95 match load_key(path).unwrap_err().downcast().unwrap() {
96 pem::Error::Io(err) => assert_eq!(err.kind(), std::io::ErrorKind::NotFound),
97 other => panic!("expected Error::Io error got {other:?}"),
98 }
99 }
100
101 #[test]
102 fn test_read_invalid_private_key() {
103 let path = Path::new(TESTDATA_DIR).join("invalid-private-key.pem");
104 match load_key(path).unwrap_err().downcast().unwrap() {
105 pem::Error::MissingSectionEnd { .. } => (),
106 other => panic!("expected Error::MissingSectionEnd got {other:?}"),
107 }
108 }
109
110 #[test]
111 fn test_read_valid_private_key() {
112 let path = Path::new(TESTDATA_DIR).join("valid-private-key.pem");
113 load_key(path).unwrap();
114 }
115}