rustls/
suites.rs

1use std::fmt;
2
3use crate::enums::{CipherSuite, ProtocolVersion, SignatureAlgorithm, SignatureScheme};
4#[cfg(feature = "tls12")]
5use crate::tls12::Tls12CipherSuite;
6#[cfg(feature = "tls12")]
7use crate::tls12::{
8    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
9    // TLS1.2 suites
10    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
11    TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
12    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
13    TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
14    TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
15};
16use crate::tls13::Tls13CipherSuite;
17use crate::tls13::{
18    TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256,
19};
20#[cfg(feature = "tls12")]
21use crate::versions::TLS12;
22use crate::versions::{SupportedProtocolVersion, TLS13};
23
24/// Bulk symmetric encryption scheme used by a cipher suite.
25#[allow(non_camel_case_types)]
26#[derive(Debug, Eq, PartialEq)]
27pub enum BulkAlgorithm {
28    /// AES with 128-bit keys in Galois counter mode.
29    Aes128Gcm,
30
31    /// AES with 256-bit keys in Galois counter mode.
32    Aes256Gcm,
33
34    /// Chacha20 for confidentiality with poly1305 for authenticity.
35    Chacha20Poly1305,
36}
37
38/// Common state for cipher suites (both for TLS 1.2 and TLS 1.3)
39#[derive(Debug)]
40pub struct CipherSuiteCommon {
41    /// The TLS enumeration naming this cipher suite.
42    pub suite: CipherSuite,
43
44    /// How to do bulk encryption.
45    pub bulk: BulkAlgorithm,
46
47    pub(crate) aead_algorithm: &'static ring::aead::Algorithm,
48}
49
50/// A cipher suite supported by rustls.
51///
52/// All possible instances of this type are provided by the library in
53/// the [`ALL_CIPHER_SUITES`] array.
54#[derive(Clone, Copy, PartialEq)]
55pub enum SupportedCipherSuite {
56    /// A TLS 1.2 cipher suite
57    #[cfg(feature = "tls12")]
58    Tls12(&'static Tls12CipherSuite),
59    /// A TLS 1.3 cipher suite
60    Tls13(&'static Tls13CipherSuite),
61}
62
63impl fmt::Debug for SupportedCipherSuite {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        self.suite().fmt(f)
66    }
67}
68
69impl SupportedCipherSuite {
70    /// Which hash function to use with this suite.
71    pub fn hash_algorithm(&self) -> &'static ring::digest::Algorithm {
72        match self {
73            #[cfg(feature = "tls12")]
74            Self::Tls12(inner) => inner.hash_algorithm(),
75            Self::Tls13(inner) => inner.hash_algorithm(),
76        }
77    }
78
79    /// The cipher suite's identifier
80    pub fn suite(&self) -> CipherSuite {
81        self.common().suite
82    }
83
84    pub(crate) fn common(&self) -> &CipherSuiteCommon {
85        match self {
86            #[cfg(feature = "tls12")]
87            Self::Tls12(inner) => &inner.common,
88            Self::Tls13(inner) => &inner.common,
89        }
90    }
91
92    #[cfg(any(test, feature = "quic"))]
93    pub(crate) fn tls13(&self) -> Option<&'static Tls13CipherSuite> {
94        match self {
95            #[cfg(feature = "tls12")]
96            Self::Tls12(_) => None,
97            Self::Tls13(inner) => Some(inner),
98        }
99    }
100
101    /// Return supported protocol version for the cipher suite.
102    pub fn version(&self) -> &'static SupportedProtocolVersion {
103        match self {
104            #[cfg(feature = "tls12")]
105            Self::Tls12(_) => &TLS12,
106            Self::Tls13(_) => &TLS13,
107        }
108    }
109
110    /// Return true if this suite is usable for a key only offering `sig_alg`
111    /// signatures.  This resolves to true for all TLS1.3 suites.
112    pub fn usable_for_signature_algorithm(&self, _sig_alg: SignatureAlgorithm) -> bool {
113        match self {
114            Self::Tls13(_) => true, // no constraint expressed by ciphersuite (e.g., TLS1.3)
115            #[cfg(feature = "tls12")]
116            Self::Tls12(inner) => inner
117                .sign
118                .iter()
119                .any(|scheme| scheme.sign() == _sig_alg),
120        }
121    }
122}
123
124/// A list of all the cipher suites supported by rustls.
125pub static ALL_CIPHER_SUITES: &[SupportedCipherSuite] = &[
126    // TLS1.3 suites
127    TLS13_AES_256_GCM_SHA384,
128    TLS13_AES_128_GCM_SHA256,
129    TLS13_CHACHA20_POLY1305_SHA256,
130    // TLS1.2 suites
131    #[cfg(feature = "tls12")]
132    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
133    #[cfg(feature = "tls12")]
134    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
135    #[cfg(feature = "tls12")]
136    TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
137    #[cfg(feature = "tls12")]
138    TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
139    #[cfg(feature = "tls12")]
140    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
141    #[cfg(feature = "tls12")]
142    TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
143];
144
145/// The cipher suite configuration that an application should use by default.
146///
147/// This will be [`ALL_CIPHER_SUITES`] sans any supported cipher suites that
148/// shouldn't be enabled by most applications.
149pub static DEFAULT_CIPHER_SUITES: &[SupportedCipherSuite] = ALL_CIPHER_SUITES;
150
151// These both O(N^2)!
152pub(crate) fn choose_ciphersuite_preferring_client(
153    client_suites: &[CipherSuite],
154    server_suites: &[SupportedCipherSuite],
155) -> Option<SupportedCipherSuite> {
156    for client_suite in client_suites {
157        if let Some(selected) = server_suites
158            .iter()
159            .find(|x| *client_suite == x.suite())
160        {
161            return Some(*selected);
162        }
163    }
164
165    None
166}
167
168pub(crate) fn choose_ciphersuite_preferring_server(
169    client_suites: &[CipherSuite],
170    server_suites: &[SupportedCipherSuite],
171) -> Option<SupportedCipherSuite> {
172    if let Some(selected) = server_suites
173        .iter()
174        .find(|x| client_suites.contains(&x.suite()))
175    {
176        return Some(*selected);
177    }
178
179    None
180}
181
182/// Return a list of the ciphersuites in `all` with the suites
183/// incompatible with `SignatureAlgorithm` `sigalg` removed.
184pub(crate) fn reduce_given_sigalg(
185    all: &[SupportedCipherSuite],
186    sigalg: SignatureAlgorithm,
187) -> Vec<SupportedCipherSuite> {
188    all.iter()
189        .filter(|&&suite| suite.usable_for_signature_algorithm(sigalg))
190        .copied()
191        .collect()
192}
193
194/// Return a list of the ciphersuites in `all` with the suites
195/// incompatible with the chosen `version` removed.
196pub(crate) fn reduce_given_version(
197    all: &[SupportedCipherSuite],
198    version: ProtocolVersion,
199) -> Vec<SupportedCipherSuite> {
200    all.iter()
201        .filter(|&&suite| suite.version().version == version)
202        .copied()
203        .collect()
204}
205
206/// Return true if `sigscheme` is usable by any of the given suites.
207pub(crate) fn compatible_sigscheme_for_suites(
208    sigscheme: SignatureScheme,
209    common_suites: &[SupportedCipherSuite],
210) -> bool {
211    let sigalg = sigscheme.sign();
212    common_suites
213        .iter()
214        .any(|&suite| suite.usable_for_signature_algorithm(sigalg))
215}
216
217/// Secrets for transmitting/receiving data over a TLS session.
218///
219/// After performing a handshake with rustls, these secrets can be extracted
220/// to configure kTLS for a socket, and have the kernel take over encryption
221/// and/or decryption.
222#[cfg(feature = "secret_extraction")]
223#[cfg_attr(docsrs, doc(cfg(feature = "secret_extraction")))]
224pub struct ExtractedSecrets {
225    /// sequence number and secrets for the "tx" (transmit) direction
226    pub tx: (u64, ConnectionTrafficSecrets),
227
228    /// sequence number and secrets for the "rx" (receive) direction
229    pub rx: (u64, ConnectionTrafficSecrets),
230}
231
232/// [ExtractedSecrets] minus the sequence numbers
233#[cfg(feature = "secret_extraction")]
234pub(crate) struct PartiallyExtractedSecrets {
235    /// secrets for the "tx" (transmit) direction
236    pub(crate) tx: ConnectionTrafficSecrets,
237
238    /// secrets for the "rx" (receive) direction
239    pub(crate) rx: ConnectionTrafficSecrets,
240}
241
242/// Secrets used to encrypt/decrypt data in a TLS session.
243///
244/// These can be used to configure kTLS for a socket in one direction.
245/// The only other piece of information needed is the sequence number,
246/// which is in [ExtractedSecrets].
247#[cfg(feature = "secret_extraction")]
248#[cfg_attr(docsrs, doc(cfg(feature = "secret_extraction")))]
249#[non_exhaustive]
250pub enum ConnectionTrafficSecrets {
251    /// Secrets for the AES_128_GCM AEAD algorithm
252    Aes128Gcm {
253        /// key (16 bytes)
254        key: [u8; 16],
255        /// salt (4 bytes)
256        salt: [u8; 4],
257        /// initialization vector (8 bytes, chopped from key block)
258        iv: [u8; 8],
259    },
260
261    /// Secrets for the AES_256_GCM AEAD algorithm
262    Aes256Gcm {
263        /// key (32 bytes)
264        key: [u8; 32],
265        /// salt (4 bytes)
266        salt: [u8; 4],
267        /// initialization vector (8 bytes, chopped from key block)
268        iv: [u8; 8],
269    },
270
271    /// Secrets for the CHACHA20_POLY1305 AEAD algorithm
272    Chacha20Poly1305 {
273        /// key (32 bytes)
274        key: [u8; 32],
275        /// initialization vector (12 bytes)
276        iv: [u8; 12],
277    },
278}
279
280#[cfg(test)]
281mod test {
282    use super::*;
283
284    #[test]
285    fn test_client_pref() {
286        let client = vec![
287            CipherSuite::TLS13_AES_128_GCM_SHA256,
288            CipherSuite::TLS13_AES_256_GCM_SHA384,
289        ];
290        let server = vec![TLS13_AES_256_GCM_SHA384, TLS13_AES_128_GCM_SHA256];
291        let chosen = choose_ciphersuite_preferring_client(&client, &server);
292        assert!(chosen.is_some());
293        assert_eq!(chosen.unwrap(), TLS13_AES_128_GCM_SHA256);
294    }
295
296    #[test]
297    fn test_server_pref() {
298        let client = vec![
299            CipherSuite::TLS13_AES_128_GCM_SHA256,
300            CipherSuite::TLS13_AES_256_GCM_SHA384,
301        ];
302        let server = vec![TLS13_AES_256_GCM_SHA384, TLS13_AES_128_GCM_SHA256];
303        let chosen = choose_ciphersuite_preferring_server(&client, &server);
304        assert!(chosen.is_some());
305        assert_eq!(chosen.unwrap(), TLS13_AES_256_GCM_SHA384);
306    }
307
308    #[test]
309    fn test_pref_fails() {
310        assert!(choose_ciphersuite_preferring_client(
311            &[CipherSuite::TLS_NULL_WITH_NULL_NULL],
312            ALL_CIPHER_SUITES
313        )
314        .is_none());
315        assert!(choose_ciphersuite_preferring_server(
316            &[CipherSuite::TLS_NULL_WITH_NULL_NULL],
317            ALL_CIPHER_SUITES
318        )
319        .is_none());
320    }
321
322    #[test]
323    fn test_scs_is_debug() {
324        println!("{:?}", ALL_CIPHER_SUITES);
325    }
326
327    #[test]
328    fn test_can_resume_to() {
329        assert!(TLS13_AES_128_GCM_SHA256
330            .tls13()
331            .unwrap()
332            .can_resume_from(crate::tls13::TLS13_CHACHA20_POLY1305_SHA256_INTERNAL)
333            .is_some());
334        assert!(TLS13_AES_256_GCM_SHA384
335            .tls13()
336            .unwrap()
337            .can_resume_from(crate::tls13::TLS13_CHACHA20_POLY1305_SHA256_INTERNAL)
338            .is_none());
339    }
340}