portable_rustls/
suites.rs

1use core::fmt;
2
3use crate::common_state::Protocol;
4use crate::crypto::cipher::{AeadKey, Iv};
5use crate::crypto::{self, KeyExchangeAlgorithm};
6use crate::enums::{CipherSuite, SignatureAlgorithm, SignatureScheme};
7use crate::msgs::handshake::ALL_KEY_EXCHANGE_ALGORITHMS;
8#[cfg(feature = "tls12")]
9use crate::tls12::Tls12CipherSuite;
10use crate::tls13::Tls13CipherSuite;
11#[cfg(feature = "tls12")]
12use crate::versions::TLS12;
13use crate::versions::{SupportedProtocolVersion, TLS13};
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 TCP-TLS 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    /// each is 2<sup>14</sup> bytes. It **does not** consider confidentiality limits for
31    /// QUIC connections - see the [`quic::KeyBuilder.confidentiality_limit`] field for
32    /// this context.
33    ///
34    /// For AES-GCM implementations, this should be set to 2<sup>24</sup> to limit attack
35    /// probability to one in 2<sup>60</sup>.  See [AEBounds] (Table 1) and [draft-irtf-aead-limits-08]:
36    ///
37    /// ```python
38    /// >>> p = 2 ** -60
39    /// >>> L = (2 ** 14 // 16) + 1
40    /// >>> qlim = (math.sqrt(p) * (2 ** (129 // 2)) - 1) / (L + 1)
41    /// >>> print(int(qlim).bit_length())
42    /// 24
43    /// ```
44    /// [AEBounds]: https://eprint.iacr.org/2024/051.pdf
45    /// [draft-irtf-aead-limits-08]: https://www.ietf.org/archive/id/draft-irtf-cfrg-aead-limits-08.html#section-5.1.1
46    ///
47    /// For chacha20-poly1305 implementations, this should be set to `u64::MAX`:
48    /// see <https://www.ietf.org/archive/id/draft-irtf-cfrg-aead-limits-08.html#section-5.2.1>
49    pub confidentiality_limit: u64,
50}
51
52impl CipherSuiteCommon {
53    /// Return `true` if this is backed by a FIPS-approved implementation.
54    ///
55    /// This means all the constituent parts that do cryptography return `true` for `fips()`.
56    #[cfg(unstable_api_not_supported)] // [FIPS REMOVED FROM THIS FORK]
57    pub fn fips(&self) -> bool {
58        self.hash_provider.fips()
59    }
60}
61
62/// A cipher suite supported by rustls.
63///
64/// This type carries both configuration and implementation. Compare with
65/// [`CipherSuite`], which carries solely a cipher suite identifier.
66#[derive(Clone, Copy, PartialEq)]
67pub enum SupportedCipherSuite {
68    /// A TLS 1.2 cipher suite
69    #[cfg(feature = "tls12")]
70    Tls12(&'static Tls12CipherSuite),
71    /// A TLS 1.3 cipher suite
72    Tls13(&'static Tls13CipherSuite),
73}
74
75impl SupportedCipherSuite {
76    /// The cipher suite's identifier
77    pub fn suite(&self) -> CipherSuite {
78        self.common().suite
79    }
80
81    /// The hash function the ciphersuite uses.
82    pub(crate) fn hash_provider(&self) -> &'static dyn crypto::hash::Hash {
83        self.common().hash_provider
84    }
85
86    pub(crate) fn common(&self) -> &CipherSuiteCommon {
87        match self {
88            #[cfg(feature = "tls12")]
89            Self::Tls12(inner) => &inner.common,
90            Self::Tls13(inner) => &inner.common,
91        }
92    }
93
94    /// Return the inner `Tls13CipherSuite` for this suite, if it is a TLS1.3 suite.
95    pub fn tls13(&self) -> Option<&'static Tls13CipherSuite> {
96        match self {
97            #[cfg(feature = "tls12")]
98            Self::Tls12(_) => None,
99            Self::Tls13(inner) => Some(inner),
100        }
101    }
102
103    /// Return supported protocol version for the cipher suite.
104    pub fn version(&self) -> &'static SupportedProtocolVersion {
105        match self {
106            #[cfg(feature = "tls12")]
107            Self::Tls12(_) => &TLS12,
108            Self::Tls13(_) => &TLS13,
109        }
110    }
111
112    /// Return true if this suite is usable for a key only offering `sig_alg`
113    /// signatures.  This resolves to true for all TLS1.3 suites.
114    pub fn usable_for_signature_algorithm(&self, _sig_alg: SignatureAlgorithm) -> bool {
115        match self {
116            Self::Tls13(_) => true, // no constraint expressed by ciphersuite (e.g., TLS1.3)
117            #[cfg(feature = "tls12")]
118            Self::Tls12(inner) => inner
119                .sign
120                .iter()
121                .any(|scheme| scheme.algorithm() == _sig_alg),
122        }
123    }
124
125    /// Return true if this suite is usable for the given [`Protocol`].
126    ///
127    /// All cipher suites are usable for TCP-TLS.  Only TLS1.3 suites
128    /// with `Tls13CipherSuite::quic` provided are usable for QUIC.
129    pub(crate) fn usable_for_protocol(&self, proto: Protocol) -> bool {
130        match proto {
131            Protocol::Tcp => true,
132            Protocol::Quic => self
133                .tls13()
134                .and_then(|cs| cs.quic)
135                .is_some(),
136        }
137    }
138
139    /// Return `true` if this is backed by a FIPS-approved implementation.
140    #[cfg(unstable_api_not_supported)] // [FIPS REMOVED FROM THIS FORK]
141    pub fn fips(&self) -> bool {
142        match self {
143            #[cfg(feature = "tls12")]
144            Self::Tls12(cs) => cs.fips(),
145            Self::Tls13(cs) => cs.fips(),
146        }
147    }
148
149    /// Return the list of `KeyExchangeAlgorithm`s supported by this cipher suite.
150    ///
151    /// TLS 1.3 cipher suites support both ECDHE and DHE key exchange, but TLS 1.2 suites
152    /// support one or the other.
153    pub(crate) fn key_exchange_algorithms(&self) -> &[KeyExchangeAlgorithm] {
154        match self {
155            #[cfg(feature = "tls12")]
156            Self::Tls12(tls12) => core::slice::from_ref(&tls12.kx),
157            Self::Tls13(_) => ALL_KEY_EXCHANGE_ALGORITHMS,
158        }
159    }
160
161    /// Say if the given `KeyExchangeAlgorithm` is supported by this cipher suite.
162    ///
163    /// TLS 1.3 cipher suites support all key exchange types, but TLS 1.2 suites
164    /// support only one.
165    pub(crate) fn usable_for_kx_algorithm(&self, _kxa: KeyExchangeAlgorithm) -> bool {
166        match self {
167            #[cfg(feature = "tls12")]
168            Self::Tls12(tls12) => tls12.kx == _kxa,
169            Self::Tls13(_) => true,
170        }
171    }
172}
173
174impl fmt::Debug for SupportedCipherSuite {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        self.suite().fmt(f)
177    }
178}
179
180/// Return true if `sigscheme` is usable by any of the given suites.
181pub(crate) fn compatible_sigscheme_for_suites(
182    sigscheme: SignatureScheme,
183    common_suites: &[SupportedCipherSuite],
184) -> bool {
185    let sigalg = sigscheme.algorithm();
186    common_suites
187        .iter()
188        .any(|&suite| suite.usable_for_signature_algorithm(sigalg))
189}
190
191/// Secrets for transmitting/receiving data over a TLS session.
192///
193/// After performing a handshake with rustls, these secrets can be extracted
194/// to configure kTLS for a socket, and have the kernel take over encryption
195/// and/or decryption.
196pub struct ExtractedSecrets {
197    /// sequence number and secrets for the "tx" (transmit) direction
198    pub tx: (u64, ConnectionTrafficSecrets),
199
200    /// sequence number and secrets for the "rx" (receive) direction
201    pub rx: (u64, ConnectionTrafficSecrets),
202}
203
204/// [ExtractedSecrets] minus the sequence numbers
205pub(crate) struct PartiallyExtractedSecrets {
206    /// secrets for the "tx" (transmit) direction
207    pub(crate) tx: ConnectionTrafficSecrets,
208
209    /// secrets for the "rx" (receive) direction
210    pub(crate) rx: ConnectionTrafficSecrets,
211}
212
213/// Secrets used to encrypt/decrypt data in a TLS session.
214///
215/// These can be used to configure kTLS for a socket in one direction.
216/// The only other piece of information needed is the sequence number,
217/// which is in [ExtractedSecrets].
218#[non_exhaustive]
219pub enum ConnectionTrafficSecrets {
220    /// Secrets for the AES_128_GCM AEAD algorithm
221    Aes128Gcm {
222        /// AEAD Key
223        key: AeadKey,
224        /// Initialization vector
225        iv: Iv,
226    },
227
228    /// Secrets for the AES_256_GCM AEAD algorithm
229    Aes256Gcm {
230        /// AEAD Key
231        key: AeadKey,
232        /// Initialization vector
233        iv: Iv,
234    },
235
236    /// Secrets for the CHACHA20_POLY1305 AEAD algorithm
237    Chacha20Poly1305 {
238        /// AEAD Key
239        key: AeadKey,
240        /// Initialization vector
241        iv: Iv,
242    },
243}
244
245#[cfg(test)]
246#[macro_rules_attribute::apply(test_for_each_provider)]
247mod tests {
248    use std::println;
249
250    use super::provider::tls13::*;
251
252    #[test]
253    fn test_scs_is_debug() {
254        println!("{:?}", super::provider::ALL_CIPHER_SUITES);
255    }
256
257    #[test]
258    fn test_can_resume_to() {
259        assert!(TLS13_AES_128_GCM_SHA256
260            .tls13()
261            .unwrap()
262            .can_resume_from(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL)
263            .is_some());
264        assert!(TLS13_AES_256_GCM_SHA384
265            .tls13()
266            .unwrap()
267            .can_resume_from(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL)
268            .is_none());
269    }
270}