kutil_http/tls/
container.rs

1use super::{error::*, pem::*, resolver::*};
2
3use {
4    bytestring::*,
5    kutil_std::collections::*,
6    rustls::{crypto::*, server::*, sign::*},
7    std::sync::*,
8};
9
10//
11// TlsContainer
12//
13
14/// TLS container based on Rustls.
15#[derive(Clone, Debug, Default)]
16pub struct TlsContainer {
17    targets: FastHashMap<ByteString, SniResolverTarget>,
18}
19
20impl TlsContainer {
21    /// True if empty.
22    pub fn is_empty(&self) -> bool {
23        self.targets.is_empty()
24    }
25
26    /// Add key.
27    pub fn add_key(&mut self, sni: ByteString, certified_key: Arc<CertifiedKey>) -> Result<(), TlsContainerError> {
28        if self.targets.contains_key(&sni) {
29            return Err(format!("already has a target for: {}", sni).into());
30        }
31
32        self.targets.insert(sni, SniResolverTarget::Key(certified_key));
33        Ok(())
34    }
35
36    /// Add delegate.
37    pub fn add_delegate(
38        &mut self,
39        sni: ByteString,
40        resolver: Arc<dyn ResolvesServerCert>,
41    ) -> Result<(), TlsContainerError> {
42        if self.targets.contains_key(&sni) {
43            return Err(format!("already has a target for: {}", sni).into());
44        }
45
46        self.targets.insert(sni, SniResolverTarget::Delegate(resolver));
47        Ok(())
48    }
49
50    /// Add key from PEM (Privacy-Enhanced Mail) files.
51    pub fn add_key_from_pem(
52        &mut self,
53        sni: ByteString,
54        certificates_pem: &[u8],
55        private_key_pem: &[u8],
56    ) -> Result<(), TlsContainerError> {
57        if self.targets.contains_key(&sni) {
58            return Err(format!("already has a target for: {}", sni).into());
59        }
60
61        self.targets.insert(
62            sni,
63            SniResolverTarget::Key(Arc::new(
64                certified_key_from_pem(certificates_pem, private_key_pem).map_err(TlsContainerError::new_from)?,
65            )),
66        );
67        Ok(())
68    }
69
70    /// Creates a [ServerConfig] for HTTP, specifically for the "h2" and "http/1.1" ALPN
71    /// (Application-Layer Protocol Negotiation) protocols.
72    ///
73    /// Will return an error if we have no keys.
74    ///
75    /// Otherwise, if we have more than one key then the configuration will use the SNI sent by the
76    /// client in its TLS hello message to select the appropriate key in the store.
77    pub fn http_server_config(&self) -> Result<ServerConfig, TlsContainerError> {
78        let provider = aws_lc_rs::default_provider();
79
80        let mut server_config = ServerConfig::builder_with_provider(provider.into())
81            .with_safe_default_protocol_versions()
82            .expect("builder")
83            .with_no_client_auth()
84            .with_cert_resolver(Arc::new(self.resolver()?));
85
86        server_config.alpn_protocols = vec!["h2".into(), "http/1.1".into()];
87
88        Ok(server_config)
89    }
90
91    /// Creates a [SniResolver].
92    pub fn resolver(&self) -> Result<SniResolver, TlsContainerError> {
93        if self.targets.is_empty() {
94            Err("no targets".into())
95        } else {
96            Ok(if self.targets.len() == 1 {
97                SniResolver::Single(self.targets.values().next().expect("not empty").clone())
98            } else {
99                SniResolver::BySNI(self.targets.clone())
100            })
101        }
102    }
103}