#![allow(non_camel_case_types)]
#![allow(clippy::unreadable_literal)]
use core::convert::TryFrom;
use num_enum::TryFromPrimitive;
use crate::TlsCipherSuiteID;
#[derive(Debug)]
pub struct CipherSuiteNotFound(());
#[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive)]
#[repr(u8)]
pub enum TlsCipherKx {
Null,
Psk,
Krb5,
Srp,
Rsa,
Dh,
Dhe,
Ecdh,
Ecdhe,
Aecdh,
Eccpwd,
Tls13,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive)]
#[repr(u8)]
pub enum TlsCipherAu {
Null,
Psk,
Krb5,
Srp,
Srp_Dss,
Srp_Rsa,
Dss,
Rsa,
Dhe,
Ecdsa,
Eccpwd,
Tls13,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive)]
#[repr(u8)]
pub enum TlsCipherEnc {
Null,
Des,
TripleDes,
Rc2,
Rc4,
Aria,
Idea,
Seed,
Aes,
Camellia,
Chacha20_Poly1305,
Sm4,
Aegis,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive)]
#[repr(u8)]
pub enum TlsCipherEncMode {
Null,
Cbc,
Ccm,
Gcm,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive)]
#[repr(u8)]
pub enum TlsCipherMac {
Null,
HmacMd5,
HmacSha1,
HmacSha256,
HmacSha384,
HmacSha512,
Aead,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive)]
#[repr(u8)]
pub enum TlsPRF {
Default,
Null,
Md5AndSha1,
Sha1,
Sha256,
Sha384,
Sha512,
Sm3,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TlsCipherSuite {
pub name: &'static str,
pub id: TlsCipherSuiteID,
pub kx: TlsCipherKx,
pub au: TlsCipherAu,
pub enc: TlsCipherEnc,
pub enc_mode: TlsCipherEncMode,
pub enc_size: u16,
pub mac: TlsCipherMac,
pub mac_size: u16,
pub prf: TlsPRF,
}
include!(concat!(env!("OUT_DIR"), "/codegen.rs"));
impl TlsCipherSuite {
pub fn from_id(id: u16) -> Option<&'static TlsCipherSuite> {
CIPHERS.get(&id)
}
pub fn from_name(name: &str) -> Option<&'static TlsCipherSuite> {
CIPHERS.values().find(|&v| v.name == name)
}
pub const fn enc_key_size(&self) -> usize {
(self.enc_size / 8) as usize
}
pub const fn enc_block_size(&self) -> usize {
match self.enc {
TlsCipherEnc::Null => 0,
TlsCipherEnc::Des
| TlsCipherEnc::Idea
| TlsCipherEnc::Rc2
| TlsCipherEnc::TripleDes => 8,
TlsCipherEnc::Aes
| TlsCipherEnc::Aria
| TlsCipherEnc::Camellia
| TlsCipherEnc::Seed
| TlsCipherEnc::Sm4 => 16,
TlsCipherEnc::Chacha20_Poly1305 | TlsCipherEnc::Rc4 | TlsCipherEnc::Aegis => 0,
}
}
pub const fn mac_length(&self) -> usize {
match self.mac {
TlsCipherMac::Null => 0,
TlsCipherMac::Aead => 0,
TlsCipherMac::HmacMd5 => 16,
TlsCipherMac::HmacSha1 => 20,
TlsCipherMac::HmacSha256 => 32,
TlsCipherMac::HmacSha384 => 48,
TlsCipherMac::HmacSha512 => 64,
}
}
}
impl TryFrom<u16> for &'static TlsCipherSuite {
type Error = CipherSuiteNotFound;
fn try_from(value: u16) -> Result<Self, Self::Error> {
CIPHERS.get(&value).ok_or(CipherSuiteNotFound(()))
}
}
impl TryFrom<TlsCipherSuiteID> for &'static TlsCipherSuite {
type Error = CipherSuiteNotFound;
fn try_from(value: TlsCipherSuiteID) -> Result<Self, Self::Error> {
CIPHERS.get(&value.0).ok_or(CipherSuiteNotFound(()))
}
}
impl<'a> TryFrom<&'a str> for &'static TlsCipherSuite {
type Error = CipherSuiteNotFound;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
CIPHERS
.values()
.find(|&v| v.name == value)
.ok_or(CipherSuiteNotFound(()))
}
}
#[cfg(test)]
mod tests {
use crate::tls_ciphers::{TlsCipherKx, TlsCipherSuite, CIPHERS};
use core::convert::TryFrom;
#[test]
fn test_cipher_count() {
println!("loaded: {} cipher suites", CIPHERS.len());
assert!(!CIPHERS.is_empty());
}
#[test]
fn test_cipher_from_id() {
let cipher = <&TlsCipherSuite>::try_from(0xc025).expect("could not get cipher");
println!("Found cipher: {:?}", cipher);
}
#[test]
fn test_cipher_from_name() {
let cipher = <&TlsCipherSuite>::try_from("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384")
.expect("could not get cipher");
println!("Found cipher: {:?}", cipher);
}
#[test]
fn test_cipher_filter() {
let ecdhe_ciphers_count = CIPHERS
.values()
.filter(|c| c.kx == TlsCipherKx::Ecdhe)
.count();
assert!(ecdhe_ciphers_count > 20);
}
}