tls_parser/
tls_ciphers.rs

1//!
2//! The [CIPHERS](static.CIPHERS.html) static hash map is built during the
3//! compilation of the crate, using `build.rs`. It parses a file derived from
4//! the [IANA TLS Cipher Suite
5//! Registry](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4)
6//! to automatically extract parameters and add all known ciphersuites.
7
8#![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/// Key exchange methods
20#[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/// Authentication methods
38#[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/// Encryption methods
56#[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/// Encryption modes
75#[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive)]
76#[repr(u8)]
77pub enum TlsCipherEncMode {
78    Null,
79    Cbc,
80    Ccm,
81    Gcm,
82}
83
84/// Message Authentication Code (MAC) methods
85#[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/// Pseudo-Random Function (PRF) Function
98#[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/// TLS Ciphersuite
112///
113/// A CipherSuite is a set of algorithm and parameters used to secure
114/// a network connection.
115#[derive(Clone, Debug, PartialEq, Eq)]
116pub struct TlsCipherSuite {
117    /// The IANA name of this ciphersuite
118    pub name: &'static str,
119    /// The 16-bit identifier, provided by IANA, for this ciphersuite
120    pub id: TlsCipherSuiteID,
121    /// The Key Exchange method for this ciphersuite
122    pub kx: TlsCipherKx,
123    /// The Authentication method for this ciphersuite
124    pub au: TlsCipherAu,
125    /// Encryption cipher
126    pub enc: TlsCipherEnc,
127    /// Encryption mode
128    pub enc_mode: TlsCipherEncMode,
129    /// Key size of the encryption, in bits
130    pub enc_size: u16,
131    /// Message Authentication Code (MAC) algorithm
132    pub mac: TlsCipherMac,
133    /// Message Authentication Code (MAC) length
134    pub mac_size: u16,
135    /// Pseudo-Random Function, if specific
136    pub prf: TlsPRF,
137}
138
139include!(concat!(env!("OUT_DIR"), "/codegen.rs"));
140
141impl TlsCipherSuite {
142    /// Attempt to get reference on `TlsCipherSuite` identified by `id`.
143    pub fn from_id(id: u16) -> Option<&'static TlsCipherSuite> {
144        CIPHERS.get(&id)
145    }
146
147    /// Attempt to get reference on `TlsCipherSuite` identified by `name`.
148    pub fn from_name(name: &str) -> Option<&'static TlsCipherSuite> {
149        CIPHERS.values().find(|&v| v.name == name)
150    }
151
152    /// Get the key of this ciphersuite encryption algorithm, in bytes
153    pub const fn enc_key_size(&self) -> usize {
154        (self.enc_size / 8) as usize
155    }
156
157    /// Get the block size of this ciphersuite encryption algorithm, in bytes
158    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            // stream ciphers
171            TlsCipherEnc::Chacha20_Poly1305 | TlsCipherEnc::Rc4 | TlsCipherEnc::Aegis => 0,
172        }
173    }
174
175    /// Get the length of this ciphersuite MAC algorithm, in bytes
176    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}