use core::fmt;
use serde::{Deserialize, Serialize};
use zerocopy::{Immutable, IntoBytes, KnownLayout};
#[repr(u16)]
#[derive(
Copy,
Clone,
Debug,
Eq,
PartialEq,
Hash,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
Deserialize,
)]
#[non_exhaustive]
pub(crate) enum Version {
Tls13 = u16::to_be(0x0304),
}
#[repr(u16)]
#[derive(
Copy,
Clone,
Debug,
Eq,
PartialEq,
Hash,
Immutable,
IntoBytes,
KnownLayout,
Serialize,
Deserialize,
)]
#[non_exhaustive]
pub enum CipherSuiteId {
#[serde(rename = "TLS_AES_128_GCM_SHA256")]
TlsAes128GcmSha256 = u16::to_be(0x1301),
#[serde(rename = "TLS_AES_256_GCM_SHA384")]
TlsAes256GcmSha384 = u16::to_be(0x1302),
#[serde(rename = "TLS_CHACHA20_POLY1305_SHA256")]
TlsChaCha20Poly1305Sha256 = u16::to_be(0x1303),
#[serde(rename = "TLS_AES_128_CCM_SHA256")]
TlsAes128CcmSha256 = u16::to_be(0x1304),
#[serde(rename = "TLS_AES_128_CCM_8_SHA256")]
TlsAes128Ccm8Sha256 = u16::to_be(0x1305),
}
impl CipherSuiteId {
pub const fn all() -> &'static [Self] {
use CipherSuiteId::*;
&[
TlsAes128GcmSha256,
TlsAes256GcmSha384,
TlsChaCha20Poly1305Sha256,
TlsAes128CcmSha256,
TlsAes128Ccm8Sha256,
]
}
pub const fn to_bytes(self) -> [u8; 2] {
zerocopy::transmute!(self)
}
pub const fn try_from_bytes(bytes: [u8; 2]) -> Option<Self> {
use CipherSuiteId::*;
let id = match u16::from_be_bytes(bytes) {
0x1301 => TlsAes128GcmSha256,
0x1302 => TlsAes256GcmSha384,
0x1303 => TlsChaCha20Poly1305Sha256,
0x1304 => TlsAes128CcmSha256,
0x1305 => TlsAes128Ccm8Sha256,
_ => return None,
};
Some(id)
}
}
impl fmt::Display for CipherSuiteId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use CipherSuiteId::*;
let name = match self {
TlsAes128GcmSha256 => "TLS_AES_128_GCM_SHA256",
TlsAes256GcmSha384 => "TLS_AES_256_GCM_SHA384",
TlsChaCha20Poly1305Sha256 => "TLS_CHACHA20_POLY1305_SHA256",
TlsAes128CcmSha256 => "TLS_AES_128_CCM_SHA256",
TlsAes128Ccm8Sha256 => "TLS_AES_128_CCM_8_SHA256",
};
name.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cipher_suite_id() {
use CipherSuiteId::*;
let tests = [
([0x13, 0x01], Some(TlsAes128GcmSha256)),
([0x13, 0x02], Some(TlsAes256GcmSha384)),
([0x13, 0x03], Some(TlsChaCha20Poly1305Sha256)),
([0x13, 0x04], Some(TlsAes128CcmSha256)),
([0x13, 0x05], Some(TlsAes128Ccm8Sha256)),
([0x13, 0x00], None),
([0x13, 0x06], None),
];
for (idx, (bytes, suite)) in tests.into_iter().enumerate() {
let got = CipherSuiteId::try_from_bytes(bytes);
assert_eq!(got, suite, "#{idx}");
let Some(suite) = suite else {
continue;
};
assert_eq!(suite.to_bytes(), bytes, "#{idx}");
let got = suite.as_bytes();
let want = suite.to_bytes();
assert_eq!(got, want, "#{idx}");
}
}
#[test]
fn test_cipher_suite_round_trip() {
for &suite in CipherSuiteId::all() {
let got = CipherSuiteId::try_from_bytes(suite.to_bytes());
assert_eq!(got, Some(suite), "{suite}");
}
}
}