1#![allow(non_camel_case_types)]
9#![allow(clippy::unreadable_literal)]
10
11use core::convert::TryFrom;
12use num_enum::TryFromPrimitive;
13
14use crate::TlsCipherSuiteID;
15
16#[derive(Debug)]
17pub struct CipherSuiteNotFound(());
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive)]
21#[repr(u8)]
22pub enum TlsCipherKx {
23 Null,
24 Psk,
25 Krb5,
26 Srp,
27 Rsa,
28 Dh,
29 Dhe,
30 Ecdh,
31 Ecdhe,
32 Aecdh,
33 Eccpwd,
34 Tls13,
35}
36
37#[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive)]
39#[repr(u8)]
40pub enum TlsCipherAu {
41 Null,
42 Psk,
43 Krb5,
44 Srp,
45 Srp_Dss,
46 Srp_Rsa,
47 Dss,
48 Rsa,
49 Dhe,
50 Ecdsa,
51 Eccpwd,
52 Tls13,
53}
54
55#[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive)]
57#[repr(u8)]
58pub enum TlsCipherEnc {
59 Null,
60 Des,
61 TripleDes,
62 Rc2,
63 Rc4,
64 Aria,
65 Idea,
66 Seed,
67 Aes,
68 Camellia,
69 Chacha20_Poly1305,
70 Sm4,
71 Aegis,
72}
73
74#[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive)]
76#[repr(u8)]
77pub enum TlsCipherEncMode {
78 Null,
79 Cbc,
80 Ccm,
81 Gcm,
82}
83
84#[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive)]
86#[repr(u8)]
87pub enum TlsCipherMac {
88 Null,
89 HmacMd5,
90 HmacSha1,
91 HmacSha256,
92 HmacSha384,
93 HmacSha512,
94 Aead,
95}
96
97#[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive)]
99#[repr(u8)]
100pub enum TlsPRF {
101 Default,
102 Null,
103 Md5AndSha1,
104 Sha1,
105 Sha256,
106 Sha384,
107 Sha512,
108 Sm3,
109}
110
111#[derive(Clone, Debug, PartialEq, Eq)]
116pub struct TlsCipherSuite {
117 pub name: &'static str,
119 pub id: TlsCipherSuiteID,
121 pub kx: TlsCipherKx,
123 pub au: TlsCipherAu,
125 pub enc: TlsCipherEnc,
127 pub enc_mode: TlsCipherEncMode,
129 pub enc_size: u16,
131 pub mac: TlsCipherMac,
133 pub mac_size: u16,
135 pub prf: TlsPRF,
137}
138
139include!(concat!(env!("OUT_DIR"), "/codegen.rs"));
140
141impl TlsCipherSuite {
142 pub fn from_id(id: u16) -> Option<&'static TlsCipherSuite> {
144 CIPHERS.get(&id)
145 }
146
147 pub fn from_name(name: &str) -> Option<&'static TlsCipherSuite> {
149 CIPHERS.values().find(|&v| v.name == name)
150 }
151
152 pub const fn enc_key_size(&self) -> usize {
154 (self.enc_size / 8) as usize
155 }
156
157 pub const fn enc_block_size(&self) -> usize {
159 match self.enc {
160 TlsCipherEnc::Null => 0,
161 TlsCipherEnc::Des
162 | TlsCipherEnc::Idea
163 | TlsCipherEnc::Rc2
164 | TlsCipherEnc::TripleDes => 8,
165 TlsCipherEnc::Aes
166 | TlsCipherEnc::Aria
167 | TlsCipherEnc::Camellia
168 | TlsCipherEnc::Seed
169 | TlsCipherEnc::Sm4 => 16,
170 TlsCipherEnc::Chacha20_Poly1305 | TlsCipherEnc::Rc4 | TlsCipherEnc::Aegis => 0,
172 }
173 }
174
175 pub const fn mac_length(&self) -> usize {
177 match self.mac {
178 TlsCipherMac::Null => 0,
179 TlsCipherMac::Aead => 0,
180 TlsCipherMac::HmacMd5 => 16,
181 TlsCipherMac::HmacSha1 => 20,
182 TlsCipherMac::HmacSha256 => 32,
183 TlsCipherMac::HmacSha384 => 48,
184 TlsCipherMac::HmacSha512 => 64,
185 }
186 }
187}
188
189impl TryFrom<u16> for &'static TlsCipherSuite {
190 type Error = CipherSuiteNotFound;
191
192 fn try_from(value: u16) -> Result<Self, Self::Error> {
193 CIPHERS.get(&value).ok_or(CipherSuiteNotFound(()))
194 }
195}
196
197impl TryFrom<TlsCipherSuiteID> for &'static TlsCipherSuite {
198 type Error = CipherSuiteNotFound;
199
200 fn try_from(value: TlsCipherSuiteID) -> Result<Self, Self::Error> {
201 CIPHERS.get(&value.0).ok_or(CipherSuiteNotFound(()))
202 }
203}
204
205impl<'a> TryFrom<&'a str> for &'static TlsCipherSuite {
206 type Error = CipherSuiteNotFound;
207
208 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
209 CIPHERS
210 .values()
211 .find(|&v| v.name == value)
212 .ok_or(CipherSuiteNotFound(()))
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use crate::tls_ciphers::{TlsCipherKx, TlsCipherSuite, CIPHERS};
219 use core::convert::TryFrom;
220
221 #[test]
222 fn test_cipher_count() {
223 println!("loaded: {} cipher suites", CIPHERS.len());
224 assert!(!CIPHERS.is_empty());
225 }
226
227 #[test]
228 fn test_cipher_from_id() {
229 let cipher = <&TlsCipherSuite>::try_from(0xc025).expect("could not get cipher");
230 println!("Found cipher: {:?}", cipher);
231 }
232
233 #[test]
234 fn test_cipher_from_name() {
235 let cipher = <&TlsCipherSuite>::try_from("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384")
236 .expect("could not get cipher");
237 println!("Found cipher: {:?}", cipher);
238 }
239
240 #[test]
241 fn test_cipher_filter() {
242 let ecdhe_ciphers_count = CIPHERS
243 .values()
244 .filter(|c| c.kx == TlsCipherKx::Ecdhe)
245 .count();
246 assert!(ecdhe_ciphers_count > 20);
247 }
248}