use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum TlsVersion {
Ssl30,
Tls10,
Tls11,
Tls12,
Tls13,
Other(u16),
}
impl TlsVersion {
#[must_use]
pub const fn from_u16(v: u16) -> Self {
match v {
0x0300 => Self::Ssl30,
0x0301 => Self::Tls10,
0x0302 => Self::Tls11,
0x0303 => Self::Tls12,
0x0304 => Self::Tls13,
other => Self::Other(other),
}
}
}
impl std::fmt::Display for TlsVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TlsVersion::Ssl30 => f.write_str("SSL 3.0"),
TlsVersion::Tls10 => f.write_str("TLS 1.0"),
TlsVersion::Tls11 => f.write_str("TLS 1.1"),
TlsVersion::Tls12 => f.write_str("TLS 1.2"),
TlsVersion::Tls13 => f.write_str("TLS 1.3"),
TlsVersion::Other(v) => write!(f, "Unknown(0x{v:04x})"),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum CipherSuite {
Aes128GcmSha256,
Aes256GcmSha384,
Chacha20Poly1305Sha256,
Aes128CcmSha256,
Aes128Ccm8Sha256,
EcdheEcdsaAes128GcmSha256,
EcdheEcdsaAes256GcmSha384,
EcdheRsaAes128GcmSha256,
EcdheRsaAes256GcmSha384,
EcdheEcdsaChacha20Poly1305Sha256,
EcdheRsaChacha20Poly1305Sha256,
RsaAes128GcmSha256,
RsaAes256GcmSha384,
Other(u16),
}
impl CipherSuite {
#[must_use]
pub const fn from_u16(v: u16) -> Self {
match v {
0x1301 => Self::Aes128GcmSha256,
0x1302 => Self::Aes256GcmSha384,
0x1303 => Self::Chacha20Poly1305Sha256,
0x1304 => Self::Aes128CcmSha256,
0x1305 => Self::Aes128Ccm8Sha256,
0xc02b => Self::EcdheEcdsaAes128GcmSha256,
0xc02c => Self::EcdheEcdsaAes256GcmSha384,
0xc02f => Self::EcdheRsaAes128GcmSha256,
0xc030 => Self::EcdheRsaAes256GcmSha384,
0xcca9 => Self::EcdheEcdsaChacha20Poly1305Sha256,
0xcca8 => Self::EcdheRsaChacha20Poly1305Sha256,
0x009c => Self::RsaAes128GcmSha256,
0x009d => Self::RsaAes256GcmSha384,
other => Self::Other(other),
}
}
}
impl std::fmt::Display for CipherSuite {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CipherSuite::Aes128GcmSha256 => f.write_str("TLS_AES_128_GCM_SHA256"),
CipherSuite::Aes256GcmSha384 => f.write_str("TLS_AES_256_GCM_SHA384"),
CipherSuite::Chacha20Poly1305Sha256 => f.write_str("TLS_CHACHA20_POLY1305_SHA256"),
CipherSuite::Aes128CcmSha256 => f.write_str("TLS_AES_128_CCM_SHA256"),
CipherSuite::Aes128Ccm8Sha256 => f.write_str("TLS_AES_128_CCM_8_SHA256"),
CipherSuite::EcdheEcdsaAes128GcmSha256 => {
f.write_str("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256")
}
CipherSuite::EcdheEcdsaAes256GcmSha384 => {
f.write_str("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384")
}
CipherSuite::EcdheRsaAes128GcmSha256 => {
f.write_str("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")
}
CipherSuite::EcdheRsaAes256GcmSha384 => {
f.write_str("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384")
}
CipherSuite::EcdheEcdsaChacha20Poly1305Sha256 => {
f.write_str("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256")
}
CipherSuite::EcdheRsaChacha20Poly1305Sha256 => {
f.write_str("TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256")
}
CipherSuite::RsaAes128GcmSha256 => f.write_str("TLS_RSA_WITH_AES_128_GCM_SHA256"),
CipherSuite::RsaAes256GcmSha384 => f.write_str("TLS_RSA_WITH_AES_256_GCM_SHA384"),
CipherSuite::Other(v) => write!(f, "Unknown(0x{v:04x})"),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum NamedGroup {
Secp256r1,
Secp384r1,
Secp521r1,
X25519,
X448,
Ffdhe2048,
Ffdhe3072,
Ffdhe4096,
X25519Kyber768Draft00,
X25519MlKem768,
SecP256r1MlKem768,
Other(u16),
}
impl NamedGroup {
#[must_use]
pub const fn from_u16(v: u16) -> Self {
match v {
0x0017 => Self::Secp256r1,
0x0018 => Self::Secp384r1,
0x0019 => Self::Secp521r1,
0x001d => Self::X25519,
0x001e => Self::X448,
0x0100 => Self::Ffdhe2048,
0x0101 => Self::Ffdhe3072,
0x0102 => Self::Ffdhe4096,
0x6399 => Self::X25519Kyber768Draft00,
0x11ec => Self::X25519MlKem768,
0x11eb => Self::SecP256r1MlKem768,
other => Self::Other(other),
}
}
}
impl std::fmt::Display for NamedGroup {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NamedGroup::Secp256r1 => f.write_str("secp256r1"),
NamedGroup::Secp384r1 => f.write_str("secp384r1"),
NamedGroup::Secp521r1 => f.write_str("secp521r1"),
NamedGroup::X25519 => f.write_str("x25519"),
NamedGroup::X448 => f.write_str("x448"),
NamedGroup::Ffdhe2048 => f.write_str("ffdhe2048"),
NamedGroup::Ffdhe3072 => f.write_str("ffdhe3072"),
NamedGroup::Ffdhe4096 => f.write_str("ffdhe4096"),
NamedGroup::X25519Kyber768Draft00 => f.write_str("x25519_kyber768_draft00"),
NamedGroup::X25519MlKem768 => f.write_str("X25519MLKEM768"),
NamedGroup::SecP256r1MlKem768 => f.write_str("SecP256r1MLKEM768"),
NamedGroup::Other(v) => write!(f, "Unknown(0x{v:04x})"),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct AlpnProtocol(pub String);
impl AlpnProtocol {
#[must_use]
pub fn from_bytes(bytes: &[u8]) -> Self {
Self(String::from_utf8_lossy(bytes).into_owned())
}
}
impl std::fmt::Display for AlpnProtocol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tls_version_round_trips_named_values() {
assert_eq!(TlsVersion::from_u16(0x0304), TlsVersion::Tls13);
assert_eq!(TlsVersion::Tls13.to_string(), "TLS 1.3");
}
#[test]
fn tls_version_preserves_unknown() {
assert_eq!(TlsVersion::from_u16(0x7f1c), TlsVersion::Other(0x7f1c));
assert_eq!(TlsVersion::from_u16(0x7f1c).to_string(), "Unknown(0x7f1c)");
}
#[test]
fn cipher_suite_names_tls13_mtis() {
assert_eq!(CipherSuite::from_u16(0x1301), CipherSuite::Aes128GcmSha256);
assert_eq!(
CipherSuite::Chacha20Poly1305Sha256.to_string(),
"TLS_CHACHA20_POLY1305_SHA256"
);
}
#[test]
fn named_group_recognizes_x25519() {
assert_eq!(NamedGroup::from_u16(0x001d), NamedGroup::X25519);
}
#[test]
fn alpn_from_bytes_lossy() {
assert_eq!(AlpnProtocol::from_bytes(b"h2").to_string(), "h2");
}
}