rustls/
suites.rs

1use crate::common_state::Protocol;
2use crate::crypto;
3use crate::crypto::cipher::{AeadKey, Iv};
4use crate::enums::{CipherSuite, ProtocolVersion, SignatureAlgorithm, SignatureScheme};
5#[cfg(feature = "tls12")]
6use crate::tls12::Tls12CipherSuite;
7use crate::tls13::Tls13CipherSuite;
8#[cfg(feature = "tls12")]
9use crate::versions::TLS12;
10use crate::versions::{SupportedProtocolVersion, TLS13};
11
12use alloc::vec::Vec;
13use core::fmt;
14
15/// Common state for cipher suites (both for TLS 1.2 and TLS 1.3)
16pub struct CipherSuiteCommon {
17    /// The TLS enumeration naming this cipher suite.
18    pub suite: CipherSuite,
19
20    /// Which hash function the suite uses.
21    pub hash_provider: &'static dyn crypto::hash::Hash,
22
23    /// Number of messages that can be safely encrypted with a single key of this type
24    ///
25    /// Once a `MessageEncrypter` produced for this suite has encrypted more than
26    /// `confidentiality_limit` messages, an attacker gains an advantage in distinguishing it
27    /// from an ideal pseudorandom permutation (PRP).
28    ///
29    /// This is to be set on the assumption that messages are maximally sized --
30    /// at least 2 ** 14 bytes for TCP-TLS and 2 ** 16 for QUIC.
31    pub confidentiality_limit: u64,
32
33    /// Number of messages that can be safely decrypted with a single key of this type
34    ///
35    /// Once a `MessageDecrypter` produced for this suite has failed to decrypt `integrity_limit`
36    /// messages, an attacker gains an advantage in forging messages.
37    ///
38    /// This is not relevant for TLS over TCP (which is implemented in this crate)
39    /// because a single failed decryption is fatal to the connection.  However,
40    /// this quantity is used by QUIC.
41    pub integrity_limit: u64,
42}
43
44/// A cipher suite supported by rustls.
45///
46/// This type carries both configuration and implementation. Compare with
47/// [`CipherSuite`], which carries solely a cipher suite identifier.
48#[derive(Clone, Copy, PartialEq)]
49pub enum SupportedCipherSuite {
50    /// A TLS 1.2 cipher suite
51    #[cfg(feature = "tls12")]
52    Tls12(&'static Tls12CipherSuite),
53    /// A TLS 1.3 cipher suite
54    Tls13(&'static Tls13CipherSuite),
55}
56
57impl SupportedCipherSuite {
58    /// The cipher suite's identifier
59    pub fn suite(&self) -> CipherSuite {
60        self.common().suite
61    }
62
63    /// The hash function the ciphersuite uses.
64    pub(crate) fn hash_provider(&self) -> &'static dyn crypto::hash::Hash {
65        self.common().hash_provider
66    }
67
68    pub(crate) fn common(&self) -> &CipherSuiteCommon {
69        match self {
70            #[cfg(feature = "tls12")]
71            Self::Tls12(inner) => &inner.common,
72            Self::Tls13(inner) => &inner.common,
73        }
74    }
75
76    /// Return the inner `Tls13CipherSuite` for this suite, if it is a TLS1.3 suite.
77    pub fn tls13(&self) -> Option<&'static Tls13CipherSuite> {
78        match self {
79            #[cfg(feature = "tls12")]
80            Self::Tls12(_) => None,
81            Self::Tls13(inner) => Some(inner),
82        }
83    }
84
85    /// Return supported protocol version for the cipher suite.
86    pub fn version(&self) -> &'static SupportedProtocolVersion {
87        match self {
88            #[cfg(feature = "tls12")]
89            Self::Tls12(_) => &TLS12,
90            Self::Tls13(_) => &TLS13,
91        }
92    }
93
94    /// Return true if this suite is usable for a key only offering `sig_alg`
95    /// signatures.  This resolves to true for all TLS1.3 suites.
96    pub fn usable_for_signature_algorithm(&self, _sig_alg: SignatureAlgorithm) -> bool {
97        match self {
98            Self::Tls13(_) => true, // no constraint expressed by ciphersuite (e.g., TLS1.3)
99            #[cfg(feature = "tls12")]
100            Self::Tls12(inner) => inner
101                .sign
102                .iter()
103                .any(|scheme| scheme.sign() == _sig_alg),
104        }
105    }
106
107    /// Return true if this suite is usable for the given [`Protocol`].
108    ///
109    /// All cipher suites are usable for TCP-TLS.  Only TLS1.3 suites
110    /// with `Tls13CipherSuite::quic` provided are usable for QUIC.
111    pub(crate) fn usable_for_protocol(&self, proto: Protocol) -> bool {
112        match proto {
113            Protocol::Tcp => true,
114            Protocol::Quic => self
115                .tls13()
116                .and_then(|cs| cs.quic)
117                .is_some(),
118        }
119    }
120}
121
122impl fmt::Debug for SupportedCipherSuite {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        self.suite().fmt(f)
125    }
126}
127
128// These both O(N^2)!
129pub(crate) fn choose_ciphersuite_preferring_client(
130    client_suites: &[CipherSuite],
131    server_suites: &[SupportedCipherSuite],
132) -> Option<SupportedCipherSuite> {
133    for client_suite in client_suites {
134        if let Some(selected) = server_suites
135            .iter()
136            .find(|x| *client_suite == x.suite())
137        {
138            return Some(*selected);
139        }
140    }
141
142    None
143}
144
145pub(crate) fn choose_ciphersuite_preferring_server(
146    client_suites: &[CipherSuite],
147    server_suites: &[SupportedCipherSuite],
148) -> Option<SupportedCipherSuite> {
149    if let Some(selected) = server_suites
150        .iter()
151        .find(|x| client_suites.contains(&x.suite()))
152    {
153        return Some(*selected);
154    }
155
156    None
157}
158
159/// Return a list of the ciphersuites in `all` with the suites
160/// incompatible with `SignatureAlgorithm` `sigalg` removed.
161pub(crate) fn reduce_given_sigalg(
162    all: &[SupportedCipherSuite],
163    sigalg: SignatureAlgorithm,
164) -> Vec<SupportedCipherSuite> {
165    all.iter()
166        .filter(|&&suite| suite.usable_for_signature_algorithm(sigalg))
167        .copied()
168        .collect()
169}
170
171/// Return a list of the ciphersuites in `all` with the suites
172/// incompatible with the chosen `version` removed.
173pub(crate) fn reduce_given_version_and_protocol(
174    all: &[SupportedCipherSuite],
175    version: ProtocolVersion,
176    proto: Protocol,
177) -> Vec<SupportedCipherSuite> {
178    all.iter()
179        .filter(|&&suite| suite.version().version == version && suite.usable_for_protocol(proto))
180        .copied()
181        .collect()
182}
183
184/// Return true if `sigscheme` is usable by any of the given suites.
185pub(crate) fn compatible_sigscheme_for_suites(
186    sigscheme: SignatureScheme,
187    common_suites: &[SupportedCipherSuite],
188) -> bool {
189    let sigalg = sigscheme.sign();
190    common_suites
191        .iter()
192        .any(|&suite| suite.usable_for_signature_algorithm(sigalg))
193}
194
195/// Secrets for transmitting/receiving data over a TLS session.
196///
197/// After performing a handshake with rustls, these secrets can be extracted
198/// to configure kTLS for a socket, and have the kernel take over encryption
199/// and/or decryption.
200pub struct ExtractedSecrets {
201    /// sequence number and secrets for the "tx" (transmit) direction
202    pub tx: (u64, ConnectionTrafficSecrets),
203
204    /// sequence number and secrets for the "rx" (receive) direction
205    pub rx: (u64, ConnectionTrafficSecrets),
206}
207
208/// [ExtractedSecrets] minus the sequence numbers
209pub(crate) struct PartiallyExtractedSecrets {
210    /// secrets for the "tx" (transmit) direction
211    pub(crate) tx: ConnectionTrafficSecrets,
212
213    /// secrets for the "rx" (receive) direction
214    pub(crate) rx: ConnectionTrafficSecrets,
215}
216
217/// Secrets used to encrypt/decrypt data in a TLS session.
218///
219/// These can be used to configure kTLS for a socket in one direction.
220/// The only other piece of information needed is the sequence number,
221/// which is in [ExtractedSecrets].
222#[non_exhaustive]
223pub enum ConnectionTrafficSecrets {
224    /// Secrets for the AES_128_GCM AEAD algorithm
225    Aes128Gcm {
226        /// AEAD Key
227        key: AeadKey,
228        /// Initialization vector
229        iv: Iv,
230    },
231
232    /// Secrets for the AES_256_GCM AEAD algorithm
233    Aes256Gcm {
234        /// AEAD Key
235        key: AeadKey,
236        /// Initialization vector
237        iv: Iv,
238    },
239
240    /// Secrets for the CHACHA20_POLY1305 AEAD algorithm
241    Chacha20Poly1305 {
242        /// AEAD Key
243        key: AeadKey,
244        /// Initialization vector
245        iv: Iv,
246    },
247}
248
249#[cfg(all(test, any(feature = "ring", feature = "aws_lc_rs")))]
250mod tests {
251    use super::*;
252    use crate::test_provider::tls13::*;
253    use std::{println, vec};
254
255    #[test]
256    fn test_client_pref() {
257        let client = vec![
258            CipherSuite::TLS13_AES_128_GCM_SHA256,
259            CipherSuite::TLS13_AES_256_GCM_SHA384,
260        ];
261        let server = vec![TLS13_AES_256_GCM_SHA384, TLS13_AES_128_GCM_SHA256];
262        let chosen = choose_ciphersuite_preferring_client(&client, &server);
263        assert!(chosen.is_some());
264        assert_eq!(chosen.unwrap(), TLS13_AES_128_GCM_SHA256);
265    }
266
267    #[test]
268    fn test_server_pref() {
269        let client = vec![
270            CipherSuite::TLS13_AES_128_GCM_SHA256,
271            CipherSuite::TLS13_AES_256_GCM_SHA384,
272        ];
273        let server = vec![TLS13_AES_256_GCM_SHA384, TLS13_AES_128_GCM_SHA256];
274        let chosen = choose_ciphersuite_preferring_server(&client, &server);
275        assert!(chosen.is_some());
276        assert_eq!(chosen.unwrap(), TLS13_AES_256_GCM_SHA384);
277    }
278
279    #[test]
280    fn test_pref_fails() {
281        assert!(choose_ciphersuite_preferring_client(
282            &[CipherSuite::TLS_NULL_WITH_NULL_NULL],
283            crate::test_provider::ALL_CIPHER_SUITES
284        )
285        .is_none());
286        assert!(choose_ciphersuite_preferring_server(
287            &[CipherSuite::TLS_NULL_WITH_NULL_NULL],
288            crate::test_provider::ALL_CIPHER_SUITES
289        )
290        .is_none());
291    }
292
293    #[test]
294    fn test_scs_is_debug() {
295        println!("{:?}", crate::test_provider::ALL_CIPHER_SUITES);
296    }
297
298    #[test]
299    fn test_can_resume_to() {
300        assert!(TLS13_AES_128_GCM_SHA256
301            .tls13()
302            .unwrap()
303            .can_resume_from(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL)
304            .is_some());
305        assert!(TLS13_AES_256_GCM_SHA384
306            .tls13()
307            .unwrap()
308            .can_resume_from(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL)
309            .is_none());
310    }
311}