use openssl::ssl::{SslContext, SslMethod, SslVersion};
use serde::{Deserialize, Serialize};
const DEFAULT_CIPHERS: &str = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\
ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\
ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305";
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct ServerTls {
pub min_version: TlsVersion,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub ciphers: Vec<String>,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
pub enum TlsVersion {
#[default]
#[serde(rename = "1.2")]
Tls12,
#[serde(rename = "1.3")]
Tls13,
}
impl TlsVersion {
pub fn ssl_version(self) -> SslVersion {
match self {
Self::Tls12 => SslVersion::TLS1_2,
Self::Tls13 => SslVersion::TLS1_3,
}
}
}
impl ServerTls {
pub fn cipher_list(&self) -> String {
if self.ciphers.is_empty() {
DEFAULT_CIPHERS.to_string()
} else {
self.ciphers.join(":")
}
}
pub fn validate(&self) -> Result<(), String> {
for cipher in &self.ciphers {
let known = SslContext::builder(SslMethod::tls())
.and_then(|mut builder| builder.set_cipher_list(cipher))
.is_ok();
if !known || cipher.contains([':', ',', ' ']) {
return Err(format!(
"ciphers: {cipher:?} is not a TLS 1.2 cipher suite known to OpenSSL"
));
}
}
Ok(())
}
}