use crate::error::Error;
use tokio_rustls::rustls::{
server::{AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, NoClientAuth},
Certificate, PrivateKey, RootCertStore, ServerConfig,
};
#[derive(Debug)]
pub enum ClientAuth {
Off,
Optional(Vec<u8>),
Required(Vec<u8>),
}
#[derive(Debug)]
pub struct TlsConfig {
pub cert: Vec<u8>,
pub key: Vec<u8>,
pub ocsp: Vec<u8>,
pub client_auth: ClientAuth,
}
impl Default for TlsConfig {
fn default() -> Self {
Self::new()
}
}
impl TlsConfig {
#[must_use]
pub fn new() -> Self {
Self {
cert: Vec::new(),
key: Vec::new(),
client_auth: ClientAuth::Off,
ocsp: Vec::new(),
}
}
#[must_use]
pub fn cert(mut self, cert: Vec<u8>) -> Self {
self.cert = cert;
self
}
#[must_use]
pub fn key(mut self, key: Vec<u8>) -> Self {
self.key = key;
self
}
#[must_use]
pub fn client_auth_optional(mut self, trust_anchor: Vec<u8>) -> Self {
self.client_auth = ClientAuth::Optional(trust_anchor);
self
}
#[must_use]
pub fn client_auth_required(mut self, trust_anchor: Vec<u8>) -> Self {
self.client_auth = ClientAuth::Required(trust_anchor);
self
}
#[must_use]
pub fn ocsp(mut self, ocsp: Vec<u8>) -> Self {
self.ocsp = ocsp;
self
}
pub(crate) fn build(self) -> Result<ServerConfig, Error> {
fn read_trust_anchor(trust_anchor: &Certificate) -> Result<RootCertStore, Error> {
let mut store = RootCertStore::empty();
store
.add(trust_anchor)
.map_err(|e| Error::Custom(Box::new(e)))?;
Ok(store)
}
let certs = rustls_pemfile::certs(&mut self.cert.as_slice())
.map(|mut certs| certs.drain(..).map(Certificate).collect())
.map_err(|e| Error::Custom(Box::new(e)))?;
let keys = {
let mut pkcs8: Vec<PrivateKey> =
rustls_pemfile::pkcs8_private_keys(&mut self.key.as_slice())
.map(|mut keys| keys.drain(..).map(PrivateKey).collect())
.map_err(|e| Error::Custom(Box::new(e)))?;
if pkcs8.is_empty() {
let mut rsa: Vec<PrivateKey> =
rustls_pemfile::rsa_private_keys(&mut self.key.as_slice())
.map(|mut keys| keys.drain(..).map(PrivateKey).collect())
.map_err(|e| Error::Custom(Box::new(e)))?;
if rsa.is_empty() {
return Err(Error::Custom(Box::new(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"failed to parse tls private keys",
))));
}
rsa.remove(0)
} else {
pkcs8.remove(0)
}
};
let client_auth = match self.client_auth {
ClientAuth::Off => NoClientAuth::boxed(),
ClientAuth::Optional(trust_anchor) => AllowAnyAnonymousOrAuthenticatedClient::new(
read_trust_anchor(&Certificate(trust_anchor))?,
)
.boxed(),
ClientAuth::Required(trust_anchor) => {
AllowAnyAuthenticatedClient::new(read_trust_anchor(&Certificate(trust_anchor))?)
.boxed()
}
};
ServerConfig::builder()
.with_safe_defaults()
.with_client_cert_verifier(client_auth)
.with_single_cert_with_ocsp_and_sct(certs, keys, self.ocsp, Vec::new())
.map_err(|e| Error::Custom(Box::new(e)))
}
}