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: &CipherSuite in client_suites {
134 if let Some(selected: &SupportedCipherSuite) = server_suitesIter<'_, SupportedCipherSuite>
135 .iter()
136 .find(|x: &&SupportedCipherSuite| *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: &SupportedCipherSuite) = server_suitesIter<'_, SupportedCipherSuite>
150 .iter()
151 .find(|x: &&SupportedCipherSuite| 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 allimpl Iterator.iter()
166 .filter(|&&suite: SupportedCipherSuite| suite.usable_for_signature_algorithm(_sig_alg: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 allimpl Iterator.iter()
179 .filter(|&&suite: SupportedCipherSuite| 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: SignatureAlgorithm = sigscheme.sign();
190 common_suitesIter<'_, SupportedCipherSuite>
191 .iter()
192 .any(|&suite: SupportedCipherSuite| suite.usable_for_signature_algorithm(_sig_alg: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, feature = "ring"))]
250#[cfg(all(test, any(feature = "ring", feature = "aws_lc_rs")))]
251mod tests {
252 use super::*;
253 use crate::enums::CipherSuite;
254 use crate::test_provider::tls13::*;
255
256 #[test]
257 fn test_client_pref() {
258 let client = vec![
259 CipherSuite::TLS13_AES_128_GCM_SHA256,
260 CipherSuite::TLS13_AES_256_GCM_SHA384,
261 ];
262 let server = vec![TLS13_AES_256_GCM_SHA384, TLS13_AES_128_GCM_SHA256];
263 let chosen = choose_ciphersuite_preferring_client(&client, &server);
264 assert!(chosen.is_some());
265 assert_eq!(chosen.unwrap(), TLS13_AES_128_GCM_SHA256);
266 }
267
268 #[test]
269 fn test_server_pref() {
270 let client = vec![
271 CipherSuite::TLS13_AES_128_GCM_SHA256,
272 CipherSuite::TLS13_AES_256_GCM_SHA384,
273 ];
274 let server = vec![TLS13_AES_256_GCM_SHA384, TLS13_AES_128_GCM_SHA256];
275 let chosen = choose_ciphersuite_preferring_server(&client, &server);
276 assert!(chosen.is_some());
277 assert_eq!(chosen.unwrap(), TLS13_AES_256_GCM_SHA384);
278 }
279
280 #[test]
281 fn test_pref_fails() {
282 assert!(choose_ciphersuite_preferring_client(
283 &[CipherSuite::TLS_NULL_WITH_NULL_NULL],
284 crypto::ring::ALL_CIPHER_SUITES
285 )
286 .is_none());
287 assert!(choose_ciphersuite_preferring_server(
288 &[CipherSuite::TLS_NULL_WITH_NULL_NULL],
289 crypto::ring::ALL_CIPHER_SUITES
290 )
291 .is_none());
292 }
293
294 #[test]
295 fn test_scs_is_debug() {
296 println!("{:?}", crypto::ring::ALL_CIPHER_SUITES);
297 }
298
299 #[test]
300 fn test_can_resume_to() {
301 assert!(TLS13_AES_128_GCM_SHA256
302 .tls13()
303 .unwrap()
304 .can_resume_from(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL)
305 .is_some());
306 assert!(TLS13_AES_256_GCM_SHA384
307 .tls13()
308 .unwrap()
309 .can_resume_from(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL)
310 .is_none());
311 }
312}
313