1 | use crate::common_state::Protocol; |
2 | use crate::crypto; |
3 | use crate::crypto::cipher::{AeadKey, Iv}; |
4 | use crate::enums::{CipherSuite, ProtocolVersion, SignatureAlgorithm, SignatureScheme}; |
5 | #[cfg (feature = "tls12" )] |
6 | use crate::tls12::Tls12CipherSuite; |
7 | use crate::tls13::Tls13CipherSuite; |
8 | #[cfg (feature = "tls12" )] |
9 | use crate::versions::TLS12; |
10 | use crate::versions::{SupportedProtocolVersion, TLS13}; |
11 | |
12 | use alloc::vec::Vec; |
13 | use core::fmt; |
14 | |
15 | /// Common state for cipher suites (both for TLS 1.2 and TLS 1.3) |
16 | pub 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)] |
49 | pub 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 | |
57 | impl 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 | |
122 | impl 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)! |
129 | pub(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 | |
145 | pub(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. |
161 | pub(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. |
173 | pub(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. |
185 | pub(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. |
200 | pub 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 |
209 | pub(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 ] |
223 | pub 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" )))] |
251 | mod 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 | |