Skip to main content

rtc_dtls/cipher_suite/
mod.rs

1/// Shared implementation for the AES-128-CCM suites.
2pub mod cipher_suite_aes_128_ccm;
3/// Shared implementation for the AES-128-GCM-SHA256 suites.
4pub mod cipher_suite_aes_128_gcm_sha256;
5/// Shared implementation for the AES-256-CBC-SHA suites.
6pub mod cipher_suite_aes_256_cbc_sha;
7/// Shared implementation for the ChaCha20-Poly1305-SHA256 suites.
8pub mod cipher_suite_chacha20_poly1305_sha256;
9/// An ECDHE-ECDSA suite with AES-128, the pairing WebRTC normally negotiates.
10/// An ECDHE-ECDSA suite with AES-128-CCM and a truncated 8-byte tag.
11pub mod cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm;
12/// An ECDHE-ECDSA suite with AES-128-CCM and a full 16-byte tag.
13pub mod cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm8;
14/// `TLS_PSK_WITH_AES_128_CCM`, for pre-shared-key handshakes.
15pub mod cipher_suite_tls_psk_with_aes_128_ccm;
16/// `TLS_PSK_WITH_AES_128_CCM_8`, with a truncated 8-byte tag.
17pub mod cipher_suite_tls_psk_with_aes_128_ccm8;
18/// `TLS_PSK_WITH_AES_128_GCM_SHA256`, for pre-shared-key handshakes.
19pub mod cipher_suite_tls_psk_with_aes_128_gcm_sha256;
20
21use std::fmt;
22
23use super::client_certificate_type::*;
24use super::record_layer::record_layer_header::*;
25use shared::error::*;
26
27use cipher_suite_aes_128_gcm_sha256::*;
28use cipher_suite_aes_256_cbc_sha::*;
29use cipher_suite_chacha20_poly1305_sha256::*;
30use cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm::*;
31use cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm8::*;
32use cipher_suite_tls_psk_with_aes_128_ccm::*;
33use cipher_suite_tls_psk_with_aes_128_ccm8::*;
34use cipher_suite_tls_psk_with_aes_128_gcm_sha256::*;
35
36// CipherSuiteID is an ID for our supported CipherSuites
37// Supported Cipher Suites
38#[allow(non_camel_case_types)]
39#[derive(Copy, Clone, Debug, PartialEq, Eq)]
40/// The cipher suites this crate can negotiate, by their IANA code points.
41pub enum CipherSuiteId {
42    // AES-128-CCM
43    /// `TLS_ECDHE_ECDSA_WITH_AES_128_CCM` (`0xc0ac`).
44    Tls_Ecdhe_Ecdsa_With_Aes_128_Ccm = 0xc0ac,
45    /// `TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8` (`0xc0ae`).
46    Tls_Ecdhe_Ecdsa_With_Aes_128_Ccm_8 = 0xc0ae,
47
48    // AES-128-GCM-SHA256
49    /// `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256` (`0xc02b`).
50    Tls_Ecdhe_Ecdsa_With_Aes_128_Gcm_Sha256 = 0xc02b,
51    /// `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256` (`0xc02f`).
52    Tls_Ecdhe_Rsa_With_Aes_128_Gcm_Sha256 = 0xc02f,
53
54    // AES-256-CBC-SHA
55    /// `TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA` (`0xc00a`).
56    Tls_Ecdhe_Ecdsa_With_Aes_256_Cbc_Sha = 0xc00a,
57    /// `TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA` (`0xc014`).
58    Tls_Ecdhe_Rsa_With_Aes_256_Cbc_Sha = 0xc014,
59
60    /// `TLS_PSK_WITH_AES_128_CCM` (`0xc0a4`).
61    Tls_Psk_With_Aes_128_Ccm = 0xc0a4,
62    /// `TLS_PSK_WITH_AES_128_CCM_8` (`0xc0a8`).
63    Tls_Psk_With_Aes_128_Ccm_8 = 0xc0a8,
64    /// `TLS_PSK_WITH_AES_128_GCM_SHA256` (`0x00a8`).
65    Tls_Psk_With_Aes_128_Gcm_Sha256 = 0x00a8,
66
67    // CHACHA20_POLY1305_SHA256
68    /// `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256` (`0xcca8`).
69    Tls_Ecdhe_Rsa_With_ChaCha20_Poly1305_Sha256 = 0xcca8,
70    /// `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256` (`0xcca9`).
71    Tls_Ecdhe_Ecdsa_With_ChaCha20_Poly1305_Sha256 = 0xcca9,
72
73    /// A code point this crate does not implement.
74    Unsupported,
75}
76
77impl fmt::Display for CipherSuiteId {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        match *self {
80            CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_128_Ccm => {
81                write!(f, "TLS_ECDHE_ECDSA_WITH_AES_128_CCM")
82            }
83            CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_128_Ccm_8 => {
84                write!(f, "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8")
85            }
86            CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_128_Gcm_Sha256 => {
87                write!(f, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256")
88            }
89            CipherSuiteId::Tls_Ecdhe_Rsa_With_Aes_128_Gcm_Sha256 => {
90                write!(f, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")
91            }
92            CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_256_Cbc_Sha => {
93                write!(f, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA")
94            }
95            CipherSuiteId::Tls_Ecdhe_Rsa_With_Aes_256_Cbc_Sha => {
96                write!(f, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA")
97            }
98            CipherSuiteId::Tls_Psk_With_Aes_128_Ccm => write!(f, "TLS_PSK_WITH_AES_128_CCM"),
99            CipherSuiteId::Tls_Psk_With_Aes_128_Ccm_8 => write!(f, "TLS_PSK_WITH_AES_128_CCM_8"),
100            CipherSuiteId::Tls_Psk_With_Aes_128_Gcm_Sha256 => {
101                write!(f, "TLS_PSK_WITH_AES_128_GCM_SHA256")
102            }
103            CipherSuiteId::Tls_Ecdhe_Rsa_With_ChaCha20_Poly1305_Sha256 => {
104                write!(f, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256")
105            }
106            CipherSuiteId::Tls_Ecdhe_Ecdsa_With_ChaCha20_Poly1305_Sha256 => {
107                write!(f, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256")
108            }
109
110            _ => write!(f, "Unsupported CipherSuiteID"),
111        }
112    }
113}
114
115impl From<u16> for CipherSuiteId {
116    fn from(val: u16) -> Self {
117        match val {
118            // AES-128-CCM
119            0xc0ac => CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_128_Ccm,
120            0xc0ae => CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_128_Ccm_8,
121
122            // AES-128-GCM-SHA256
123            0xc02b => CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_128_Gcm_Sha256,
124            0xc02f => CipherSuiteId::Tls_Ecdhe_Rsa_With_Aes_128_Gcm_Sha256,
125
126            // AES-256-CBC-SHA
127            0xc00a => CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_256_Cbc_Sha,
128            0xc014 => CipherSuiteId::Tls_Ecdhe_Rsa_With_Aes_256_Cbc_Sha,
129
130            0xc0a4 => CipherSuiteId::Tls_Psk_With_Aes_128_Ccm,
131            0xc0a8 => CipherSuiteId::Tls_Psk_With_Aes_128_Ccm_8,
132            0x00a8 => CipherSuiteId::Tls_Psk_With_Aes_128_Gcm_Sha256,
133
134            // CHACHA20_POLY1305_SHA256
135            0xcca8 => CipherSuiteId::Tls_Ecdhe_Rsa_With_ChaCha20_Poly1305_Sha256,
136            0xcca9 => CipherSuiteId::Tls_Ecdhe_Ecdsa_With_ChaCha20_Poly1305_Sha256,
137
138            _ => CipherSuiteId::Unsupported,
139        }
140    }
141}
142
143impl From<&str> for CipherSuiteId {
144    fn from(val: &str) -> Self {
145        match val {
146            "TLS_ECDHE_ECDSA_WITH_AES_128_CCM" => CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_128_Ccm,
147            "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8" => {
148                CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_128_Ccm_8
149            }
150            "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" => {
151                CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_128_Gcm_Sha256
152            }
153            "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" => {
154                CipherSuiteId::Tls_Ecdhe_Rsa_With_Aes_128_Gcm_Sha256
155            }
156            "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA" => {
157                CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_256_Cbc_Sha
158            }
159            "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" => {
160                CipherSuiteId::Tls_Ecdhe_Rsa_With_Aes_256_Cbc_Sha
161            }
162            "TLS_PSK_WITH_AES_128_CCM" => CipherSuiteId::Tls_Psk_With_Aes_128_Ccm,
163            "TLS_PSK_WITH_AES_128_CCM_8" => CipherSuiteId::Tls_Psk_With_Aes_128_Ccm_8,
164            "TLS_PSK_WITH_AES_128_GCM_SHA256" => CipherSuiteId::Tls_Psk_With_Aes_128_Gcm_Sha256,
165            "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" => {
166                CipherSuiteId::Tls_Ecdhe_Rsa_With_ChaCha20_Poly1305_Sha256
167            }
168            "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" => {
169                CipherSuiteId::Tls_Ecdhe_Ecdsa_With_ChaCha20_Poly1305_Sha256
170            }
171            _ => CipherSuiteId::Unsupported,
172        }
173    }
174}
175
176#[derive(Copy, Clone, Debug)]
177/// The hash a suite uses in its PRF and `Finished` computation.
178pub enum CipherSuiteHash {
179    /// SHA-256.
180    Sha256,
181}
182
183impl CipherSuiteHash {
184    pub(crate) fn size(&self) -> usize {
185        match *self {
186            CipherSuiteHash::Sha256 => 32,
187        }
188    }
189}
190
191/// A negotiated cipher suite: its identity, and the record encryption it performs once keys
192/// are installed.
193pub trait CipherSuite: Send + Sync {
194    /// The suite's IANA name.
195    fn to_string(&self) -> String;
196    /// The suite's code point.
197    fn id(&self) -> CipherSuiteId;
198    /// The certificate type this suite requires of a peer.
199    fn certificate_type(&self) -> ClientCertificateType;
200    /// The hash used for the PRF and `Finished`.
201    fn hash_func(&self) -> CipherSuiteHash;
202    /// Whether this suite authenticates with a pre-shared key rather than certificates.
203    fn is_psk(&self) -> bool;
204    /// Whether keys have been installed, so records can be protected.
205    fn is_initialized(&self) -> bool;
206
207    // Generate the internal encryption state
208    /// Installs the keying material derived from the handshake.
209    ///
210    /// # Errors
211    ///
212    /// Fails if the key or salt lengths do not match what this suite expects.
213    fn init(
214        &mut self,
215        master_secret: &[u8],
216        client_random: &[u8],
217        server_random: &[u8],
218        is_client: bool,
219    ) -> Result<()>;
220
221    /// Protects one record, returning the encrypted record including its header.
222    ///
223    /// # Errors
224    ///
225    /// Fails if keys are not installed, or the cipher rejects the input.
226    fn encrypt(&self, pkt_rlh: &RecordLayerHeader, raw: &[u8]) -> Result<Vec<u8>>;
227    /// Unprotects one record.
228    ///
229    /// # Errors
230    ///
231    /// Fails if authentication fails, or the record is malformed.
232    fn decrypt(&self, input: &[u8]) -> Result<Vec<u8>>;
233}
234
235// Taken from https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
236// A cipher_suite is a specific combination of key agreement, cipher and MAC
237// function.
238/// Builds the [`CipherSuite`] implementation for `id`.
239///
240/// # Errors
241///
242/// Fails if the id is not one this crate implements.
243pub fn cipher_suite_for_id(id: CipherSuiteId) -> Result<Box<dyn CipherSuite>> {
244    match id {
245        CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_128_Ccm => {
246            Ok(Box::new(new_cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm()))
247        }
248        CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_128_Ccm_8 => Ok(Box::new(
249            new_cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm8(),
250        )),
251        CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_128_Gcm_Sha256 => {
252            Ok(Box::new(CipherSuiteAes128GcmSha256::new(false)))
253        }
254        CipherSuiteId::Tls_Ecdhe_Rsa_With_Aes_128_Gcm_Sha256 => {
255            Ok(Box::new(CipherSuiteAes128GcmSha256::new(true)))
256        }
257        CipherSuiteId::Tls_Ecdhe_Rsa_With_Aes_256_Cbc_Sha => {
258            Ok(Box::new(CipherSuiteAes256CbcSha::new(true)))
259        }
260        CipherSuiteId::Tls_Ecdhe_Ecdsa_With_Aes_256_Cbc_Sha => {
261            Ok(Box::new(CipherSuiteAes256CbcSha::new(false)))
262        }
263        CipherSuiteId::Tls_Psk_With_Aes_128_Ccm => {
264            Ok(Box::new(new_cipher_suite_tls_psk_with_aes_128_ccm()))
265        }
266        CipherSuiteId::Tls_Psk_With_Aes_128_Ccm_8 => {
267            Ok(Box::new(new_cipher_suite_tls_psk_with_aes_128_ccm8()))
268        }
269        CipherSuiteId::Tls_Psk_With_Aes_128_Gcm_Sha256 => {
270            Ok(Box::<CipherSuiteTlsPskWithAes128GcmSha256>::default())
271        }
272        CipherSuiteId::Tls_Ecdhe_Rsa_With_ChaCha20_Poly1305_Sha256 => {
273            Ok(Box::new(CipherSuiteChaCha20Poly1305Sha256::new(true)))
274        }
275        CipherSuiteId::Tls_Ecdhe_Ecdsa_With_ChaCha20_Poly1305_Sha256 => {
276            Ok(Box::new(CipherSuiteChaCha20Poly1305Sha256::new(false)))
277        }
278
279        _ => Err(Error::ErrInvalidCipherSuite),
280    }
281}
282
283// CipherSuites we support in order of preference
284pub(crate) fn default_cipher_suites() -> Vec<Box<dyn CipherSuite>> {
285    vec![
286        Box::new(CipherSuiteAes128GcmSha256::new(false)),
287        Box::new(CipherSuiteAes256CbcSha::new(false)),
288        Box::new(CipherSuiteAes128GcmSha256::new(true)),
289        Box::new(CipherSuiteAes256CbcSha::new(true)),
290        Box::new(CipherSuiteChaCha20Poly1305Sha256::new(false)),
291    ]
292}
293
294fn all_cipher_suites() -> Vec<Box<dyn CipherSuite>> {
295    vec![
296        Box::new(new_cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm()),
297        Box::new(new_cipher_suite_tls_ecdhe_ecdsa_with_aes_128_ccm8()),
298        Box::new(CipherSuiteAes128GcmSha256::new(false)),
299        Box::new(CipherSuiteAes128GcmSha256::new(true)),
300        Box::new(CipherSuiteAes256CbcSha::new(false)),
301        Box::new(CipherSuiteAes256CbcSha::new(true)),
302        Box::new(new_cipher_suite_tls_psk_with_aes_128_ccm()),
303        Box::new(new_cipher_suite_tls_psk_with_aes_128_ccm8()),
304        Box::<CipherSuiteTlsPskWithAes128GcmSha256>::default(),
305        Box::new(CipherSuiteChaCha20Poly1305Sha256::new(false)),
306        Box::new(CipherSuiteChaCha20Poly1305Sha256::new(true)),
307    ]
308}
309
310fn cipher_suites_for_ids(ids: &[CipherSuiteId]) -> Result<Vec<Box<dyn CipherSuite>>> {
311    let mut cipher_suites = vec![];
312    for id in ids {
313        cipher_suites.push(cipher_suite_for_id(*id)?);
314    }
315    Ok(cipher_suites)
316}
317
318pub(crate) fn parse_cipher_suites(
319    user_selected_suites: &[CipherSuiteId],
320    exclude_psk: bool,
321    exclude_non_psk: bool,
322) -> Result<Vec<Box<dyn CipherSuite>>> {
323    let cipher_suites = if !user_selected_suites.is_empty() {
324        cipher_suites_for_ids(user_selected_suites)?
325    } else {
326        default_cipher_suites()
327    };
328
329    let filtered_cipher_suites: Vec<Box<dyn CipherSuite>> = cipher_suites
330        .into_iter()
331        .filter(|c| !((exclude_psk && c.is_psk()) || (exclude_non_psk && !c.is_psk())))
332        .collect();
333
334    if filtered_cipher_suites.is_empty() {
335        Err(Error::ErrNoAvailableCipherSuites)
336    } else {
337        Ok(filtered_cipher_suites)
338    }
339}