/rust/registry/src/index.crates.io-1949cf8c6b5b557f/aws-lc-rs-1.12.4/src/aead/tls.rs
Line | Count | Source |
1 | | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. |
2 | | // SPDX-License-Identifier: Apache-2.0 OR ISC |
3 | | |
4 | | use super::aead_ctx::{self, AeadCtx}; |
5 | | use super::{Aad, Algorithm, AlgorithmID, Nonce, Tag, UnboundKey}; |
6 | | use crate::error::Unspecified; |
7 | | use core::fmt::Debug; |
8 | | use core::ops::RangeFrom; |
9 | | |
10 | | /// The Transport Layer Security (TLS) protocol version. |
11 | | #[allow(clippy::module_name_repetitions)] |
12 | | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
13 | | #[non_exhaustive] |
14 | | pub enum TlsProtocolId { |
15 | | /// TLS 1.2 (RFC 5246) |
16 | | TLS12, |
17 | | |
18 | | /// TLS 1.3 (RFC 8446) |
19 | | TLS13, |
20 | | } |
21 | | |
22 | | /// AEAD Encryption key used for TLS protocol record encryption. |
23 | | /// |
24 | | /// This type encapsulates encryption operations for TLS AEAD algorithms. |
25 | | /// It validates that the provides nonce values are monotonically increasing for each invocation. |
26 | | /// |
27 | | /// The following algorithms are supported: |
28 | | /// * `AES_128_GCM` |
29 | | /// * `AES_256_GCM` |
30 | | /// |
31 | | /// Prefer this type in place of `LessSafeKey`, `OpeningKey`, `SealingKey` for TLS protocol implementations. |
32 | | #[allow(clippy::module_name_repetitions)] |
33 | | pub struct TlsRecordSealingKey { |
34 | | // The TLS specific construction for TLS ciphers in AWS-LC are not thread-safe! |
35 | | // The choice here was either wrap the underlying EVP_AEAD_CTX in a Mutex as done here, |
36 | | // or force this type to !Sync. Since this is an implementation detail of AWS-LC |
37 | | // we have optex to manage this behavior internally. |
38 | | key: UnboundKey, |
39 | | protocol: TlsProtocolId, |
40 | | } |
41 | | |
42 | | impl TlsRecordSealingKey { |
43 | | /// New TLS record sealing key. Only supports `AES_128_GCM` and `AES_256_GCM`. |
44 | | /// |
45 | | /// # Errors |
46 | | /// * `Unspecified`: Returned if the length of `key_bytes` does not match the chosen algorithm, |
47 | | /// or if an unsupported algorithm is provided. |
48 | 0 | pub fn new( |
49 | 0 | algorithm: &'static Algorithm, |
50 | 0 | protocol: TlsProtocolId, |
51 | 0 | key_bytes: &[u8], |
52 | 0 | ) -> Result<Self, Unspecified> { |
53 | 0 | let ctx = match (algorithm.id, protocol) { |
54 | 0 | (AlgorithmID::AES_128_GCM, TlsProtocolId::TLS12) => AeadCtx::aes_128_gcm_tls12( |
55 | 0 | key_bytes, |
56 | 0 | algorithm.tag_len(), |
57 | 0 | aead_ctx::AeadDirection::Seal, |
58 | | ), |
59 | 0 | (AlgorithmID::AES_128_GCM, TlsProtocolId::TLS13) => AeadCtx::aes_128_gcm_tls13( |
60 | 0 | key_bytes, |
61 | 0 | algorithm.tag_len(), |
62 | 0 | aead_ctx::AeadDirection::Seal, |
63 | | ), |
64 | 0 | (AlgorithmID::AES_256_GCM, TlsProtocolId::TLS12) => AeadCtx::aes_256_gcm_tls12( |
65 | 0 | key_bytes, |
66 | 0 | algorithm.tag_len(), |
67 | 0 | aead_ctx::AeadDirection::Seal, |
68 | | ), |
69 | 0 | (AlgorithmID::AES_256_GCM, TlsProtocolId::TLS13) => AeadCtx::aes_256_gcm_tls13( |
70 | 0 | key_bytes, |
71 | 0 | algorithm.tag_len(), |
72 | 0 | aead_ctx::AeadDirection::Seal, |
73 | | ), |
74 | | ( |
75 | | AlgorithmID::AES_128_GCM_SIV |
76 | | | AlgorithmID::AES_192_GCM |
77 | | | AlgorithmID::AES_256_GCM_SIV |
78 | | | AlgorithmID::CHACHA20_POLY1305, |
79 | | _, |
80 | 0 | ) => Err(Unspecified), |
81 | 0 | }?; |
82 | 0 | Ok(Self { |
83 | 0 | key: UnboundKey::from(ctx), |
84 | 0 | protocol, |
85 | 0 | }) |
86 | 0 | } |
87 | | |
88 | | /// Accepts a `Nonce` and `Aad` construction that is unique for this key and |
89 | | /// TLS record sealing operation for the configured TLS protocol version. |
90 | | /// |
91 | | /// `nonce` must be unique and incremented per each sealing operation, |
92 | | /// otherwise an error is returned. |
93 | | /// |
94 | | /// # Errors |
95 | | /// `error::Unspecified` if encryption operation fails. |
96 | | #[inline] |
97 | | #[allow(clippy::needless_pass_by_value)] |
98 | 0 | pub fn seal_in_place_append_tag<A, InOut>( |
99 | 0 | &mut self, |
100 | 0 | nonce: Nonce, |
101 | 0 | aad: Aad<A>, |
102 | 0 | in_out: &mut InOut, |
103 | 0 | ) -> Result<(), Unspecified> |
104 | 0 | where |
105 | 0 | A: AsRef<[u8]>, |
106 | 0 | InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>, |
107 | | { |
108 | 0 | self.key |
109 | 0 | .seal_in_place_append_tag(Some(nonce), aad.as_ref(), in_out) |
110 | 0 | .map(|_| ()) |
111 | 0 | } Unexecuted instantiation: <aws_lc_rs::aead::tls::TlsRecordSealingKey>::seal_in_place_append_tag::<[u8; 5], rustls::msgs::message::outbound::PrefixedPayload> Unexecuted instantiation: <aws_lc_rs::aead::tls::TlsRecordSealingKey>::seal_in_place_append_tag::<_, _> |
112 | | |
113 | | /// Encrypts and signs (“seals”) data in place. |
114 | | /// |
115 | | /// `aad` is the additional authenticated data (AAD), if any. This is |
116 | | /// authenticated but not encrypted. The type `A` could be a byte slice |
117 | | /// `&[u8]`, a byte array `[u8; N]` for some constant `N`, `Vec<u8>`, etc. |
118 | | /// If there is no AAD then use `Aad::empty()`. |
119 | | /// |
120 | | /// The plaintext is given as the input value of `in_out`. `seal_in_place()` |
121 | | /// will overwrite the plaintext with the ciphertext and return the tag. |
122 | | /// For most protocols, the caller must append the tag to the ciphertext. |
123 | | /// The tag will be `self.algorithm.tag_len()` bytes long. |
124 | | /// |
125 | | /// The Nonce used for the operation is randomly generated, and returned to the caller. |
126 | | /// |
127 | | /// # Errors |
128 | | /// `error::Unspecified` if encryption operation fails. |
129 | | #[inline] |
130 | | #[allow(clippy::needless_pass_by_value)] |
131 | 0 | pub fn seal_in_place_separate_tag<A>( |
132 | 0 | &mut self, |
133 | 0 | nonce: Nonce, |
134 | 0 | aad: Aad<A>, |
135 | 0 | in_out: &mut [u8], |
136 | 0 | ) -> Result<Tag, Unspecified> |
137 | 0 | where |
138 | 0 | A: AsRef<[u8]>, |
139 | | { |
140 | 0 | self.key |
141 | 0 | .seal_in_place_separate_tag(Some(nonce), aad.as_ref(), in_out) |
142 | 0 | .map(|(_, tag)| tag) |
143 | 0 | } |
144 | | |
145 | | /// The key's AEAD algorithm. |
146 | | #[inline] |
147 | | #[must_use] |
148 | 0 | pub fn algorithm(&self) -> &'static Algorithm { |
149 | 0 | self.key.algorithm() |
150 | 0 | } Unexecuted instantiation: <aws_lc_rs::aead::tls::TlsRecordSealingKey>::algorithm Unexecuted instantiation: <aws_lc_rs::aead::tls::TlsRecordSealingKey>::algorithm |
151 | | |
152 | | /// The key's associated `TlsProtocolId`. |
153 | | #[must_use] |
154 | 0 | pub fn tls_protocol_id(&self) -> TlsProtocolId { |
155 | 0 | self.protocol |
156 | 0 | } |
157 | | } |
158 | | |
159 | | #[allow(clippy::missing_fields_in_debug)] |
160 | | impl Debug for TlsRecordSealingKey { |
161 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
162 | 0 | f.debug_struct("TlsRecordSealingKey") |
163 | 0 | .field("key", &self.key) |
164 | 0 | .field("protocol", &self.protocol) |
165 | 0 | .finish() |
166 | 0 | } |
167 | | } |
168 | | |
169 | | /// AEAD Encryption key used for TLS protocol record encryption. |
170 | | /// |
171 | | /// This type encapsulates decryption operations for TLS AEAD algorithms. |
172 | | /// |
173 | | /// The following algorithms are supported: |
174 | | /// * `AES_128_GCM` |
175 | | /// * `AES_256_GCM` |
176 | | /// |
177 | | /// Prefer this type in place of `LessSafeKey`, `OpeningKey`, `SealingKey` for TLS protocol implementations. |
178 | | #[allow(clippy::module_name_repetitions)] |
179 | | pub struct TlsRecordOpeningKey { |
180 | | // The TLS specific construction for TLS ciphers in AWS-LC are not thread-safe! |
181 | | // The choice here was either wrap the underlying EVP_AEAD_CTX in a Mutex as done here, |
182 | | // or force this type to !Sync. Since this is an implementation detail of AWS-LC |
183 | | // we have optex to manage this behavior internally. |
184 | | key: UnboundKey, |
185 | | protocol: TlsProtocolId, |
186 | | } |
187 | | |
188 | | impl TlsRecordOpeningKey { |
189 | | /// New TLS record opening key. Only supports `AES_128_GCM` and `AES_256_GCM` Algorithms. |
190 | | /// |
191 | | /// # Errors |
192 | | /// * `Unspecified`: Returned if the length of `key_bytes` does not match the chosen algorithm, |
193 | | /// or if an unsupported algorithm is provided. |
194 | 0 | pub fn new( |
195 | 0 | algorithm: &'static Algorithm, |
196 | 0 | protocol: TlsProtocolId, |
197 | 0 | key_bytes: &[u8], |
198 | 0 | ) -> Result<Self, Unspecified> { |
199 | 0 | let ctx = match (algorithm.id, protocol) { |
200 | 0 | (AlgorithmID::AES_128_GCM, TlsProtocolId::TLS12) => AeadCtx::aes_128_gcm_tls12( |
201 | 0 | key_bytes, |
202 | 0 | algorithm.tag_len(), |
203 | 0 | aead_ctx::AeadDirection::Open, |
204 | | ), |
205 | 0 | (AlgorithmID::AES_128_GCM, TlsProtocolId::TLS13) => AeadCtx::aes_128_gcm_tls13( |
206 | 0 | key_bytes, |
207 | 0 | algorithm.tag_len(), |
208 | 0 | aead_ctx::AeadDirection::Open, |
209 | | ), |
210 | 0 | (AlgorithmID::AES_256_GCM, TlsProtocolId::TLS12) => AeadCtx::aes_256_gcm_tls12( |
211 | 0 | key_bytes, |
212 | 0 | algorithm.tag_len(), |
213 | 0 | aead_ctx::AeadDirection::Open, |
214 | | ), |
215 | 0 | (AlgorithmID::AES_256_GCM, TlsProtocolId::TLS13) => AeadCtx::aes_256_gcm_tls13( |
216 | 0 | key_bytes, |
217 | 0 | algorithm.tag_len(), |
218 | 0 | aead_ctx::AeadDirection::Open, |
219 | | ), |
220 | | ( |
221 | | AlgorithmID::AES_128_GCM_SIV |
222 | | | AlgorithmID::AES_192_GCM |
223 | | | AlgorithmID::AES_256_GCM_SIV |
224 | | | AlgorithmID::CHACHA20_POLY1305, |
225 | | _, |
226 | 0 | ) => Err(Unspecified), |
227 | 0 | }?; |
228 | 0 | Ok(Self { |
229 | 0 | key: UnboundKey::from(ctx), |
230 | 0 | protocol, |
231 | 0 | }) |
232 | 0 | } |
233 | | |
234 | | /// See [`super::OpeningKey::open_in_place()`] for details. |
235 | | /// |
236 | | /// # Errors |
237 | | /// `error::Unspecified` when ciphertext is invalid. |
238 | | #[inline] |
239 | | #[allow(clippy::needless_pass_by_value)] |
240 | 0 | pub fn open_in_place<'in_out, A>( |
241 | 0 | &self, |
242 | 0 | nonce: Nonce, |
243 | 0 | aad: Aad<A>, |
244 | 0 | in_out: &'in_out mut [u8], |
245 | 0 | ) -> Result<&'in_out mut [u8], Unspecified> |
246 | 0 | where |
247 | 0 | A: AsRef<[u8]>, |
248 | | { |
249 | 0 | self.key.open_within(nonce, aad.as_ref(), in_out, 0..) |
250 | 0 | } Unexecuted instantiation: <aws_lc_rs::aead::tls::TlsRecordOpeningKey>::open_in_place::<[u8; 5]> Unexecuted instantiation: <aws_lc_rs::aead::tls::TlsRecordOpeningKey>::open_in_place::<_> |
251 | | |
252 | | /// See [`super::OpeningKey::open_within()`] for details. |
253 | | /// |
254 | | /// # Errors |
255 | | /// `error::Unspecified` when ciphertext is invalid. |
256 | | #[inline] |
257 | | #[allow(clippy::needless_pass_by_value)] |
258 | 0 | pub fn open_within<'in_out, A>( |
259 | 0 | &self, |
260 | 0 | nonce: Nonce, |
261 | 0 | aad: Aad<A>, |
262 | 0 | in_out: &'in_out mut [u8], |
263 | 0 | ciphertext_and_tag: RangeFrom<usize>, |
264 | 0 | ) -> Result<&'in_out mut [u8], Unspecified> |
265 | 0 | where |
266 | 0 | A: AsRef<[u8]>, |
267 | | { |
268 | 0 | self.key |
269 | 0 | .open_within(nonce, aad.as_ref(), in_out, ciphertext_and_tag) |
270 | 0 | } |
271 | | |
272 | | /// The key's AEAD algorithm. |
273 | | #[inline] |
274 | | #[must_use] |
275 | 0 | pub fn algorithm(&self) -> &'static Algorithm { |
276 | 0 | self.key.algorithm() |
277 | 0 | } Unexecuted instantiation: <aws_lc_rs::aead::tls::TlsRecordOpeningKey>::algorithm Unexecuted instantiation: <aws_lc_rs::aead::tls::TlsRecordOpeningKey>::algorithm |
278 | | |
279 | | /// The key's associated `TlsProtocolId`. |
280 | | #[must_use] |
281 | 0 | pub fn tls_protocol_id(&self) -> TlsProtocolId { |
282 | 0 | self.protocol |
283 | 0 | } |
284 | | } |
285 | | |
286 | | #[allow(clippy::missing_fields_in_debug)] |
287 | | impl Debug for TlsRecordOpeningKey { |
288 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
289 | 0 | f.debug_struct("TlsRecordOpeningKey") |
290 | 0 | .field("key", &self.key) |
291 | 0 | .field("protocol", &self.protocol) |
292 | 0 | .finish() |
293 | 0 | } |
294 | | } |
295 | | |
296 | | #[cfg(test)] |
297 | | mod tests { |
298 | | use super::{TlsProtocolId, TlsRecordOpeningKey, TlsRecordSealingKey}; |
299 | | use crate::aead::{Aad, Nonce, AES_128_GCM, AES_256_GCM, CHACHA20_POLY1305}; |
300 | | use crate::test::from_hex; |
301 | | use paste::paste; |
302 | | |
303 | | const TEST_128_BIT_KEY: &[u8] = &[ |
304 | | 0xb0, 0x37, 0x9f, 0xf8, 0xfb, 0x8e, 0xa6, 0x31, 0xf4, 0x1c, 0xe6, 0x3e, 0xb5, 0xc5, 0x20, |
305 | | 0x7c, |
306 | | ]; |
307 | | |
308 | | const TEST_256_BIT_KEY: &[u8] = &[ |
309 | | 0x56, 0xd8, 0x96, 0x68, 0xbd, 0x96, 0xeb, 0xff, 0x5e, 0xa2, 0x0b, 0x34, 0xf2, 0x79, 0x84, |
310 | | 0x6e, 0x2b, 0x13, 0x01, 0x3d, 0xab, 0x1d, 0xa4, 0x07, 0x5a, 0x16, 0xd5, 0x0b, 0x53, 0xb0, |
311 | | 0xcc, 0x88, |
312 | | ]; |
313 | | |
314 | | struct TlsNonceTestCase { |
315 | | nonce: &'static str, |
316 | | expect_err: bool, |
317 | | } |
318 | | |
319 | | const TLS_NONCE_TEST_CASES: &[TlsNonceTestCase] = &[ |
320 | | TlsNonceTestCase { |
321 | | nonce: "9fab40177c900aad9fc28cc3", |
322 | | expect_err: false, |
323 | | }, |
324 | | TlsNonceTestCase { |
325 | | nonce: "9fab40177c900aad9fc28cc4", |
326 | | expect_err: false, |
327 | | }, |
328 | | TlsNonceTestCase { |
329 | | nonce: "9fab40177c900aad9fc28cc2", |
330 | | expect_err: true, |
331 | | }, |
332 | | ]; |
333 | | |
334 | | macro_rules! test_tls_aead { |
335 | | ($name:ident, $alg:expr, $proto:expr, $key:expr) => { |
336 | | paste! { |
337 | | #[test] |
338 | | fn [<test_ $name _tls_aead_unsupported>]() { |
339 | | assert!(TlsRecordSealingKey::new($alg, $proto, $key).is_err()); |
340 | | assert!(TlsRecordOpeningKey::new($alg, $proto, $key).is_err()); |
341 | | } |
342 | | } |
343 | | }; |
344 | | ($name:ident, $alg:expr, $proto:expr, $key:expr, $expect_tag_len:expr, $expect_nonce_len:expr) => { |
345 | | paste! { |
346 | | #[test] |
347 | | fn [<test_ $name>]() { |
348 | | let mut sealing_key = |
349 | | TlsRecordSealingKey::new($alg, $proto, $key).unwrap(); |
350 | | |
351 | | let opening_key = |
352 | | TlsRecordOpeningKey::new($alg, $proto, $key).unwrap(); |
353 | | |
354 | | for case in TLS_NONCE_TEST_CASES { |
355 | | let plaintext = from_hex("00112233445566778899aabbccddeeff").unwrap(); |
356 | | |
357 | | assert_eq!($alg, sealing_key.algorithm()); |
358 | | assert_eq!(*$expect_tag_len, $alg.tag_len()); |
359 | | assert_eq!(*$expect_nonce_len, $alg.nonce_len()); |
360 | | |
361 | | let mut in_out = Vec::from(plaintext.as_slice()); |
362 | | |
363 | | let nonce = from_hex(case.nonce).unwrap(); |
364 | | |
365 | | let nonce_bytes = nonce.as_slice(); |
366 | | |
367 | | let result = sealing_key.seal_in_place_append_tag( |
368 | | Nonce::try_assume_unique_for_key(nonce_bytes).unwrap(), |
369 | | Aad::empty(), |
370 | | &mut in_out, |
371 | | ); |
372 | | |
373 | | match (result, case.expect_err) { |
374 | | (Ok(()), true) => panic!("expected error for seal_in_place_append_tag"), |
375 | | (Ok(()), false) => {} |
376 | | (Err(_), true) => return, |
377 | | (Err(e), false) => panic!("{e}"), |
378 | | } |
379 | | |
380 | | assert_ne!(plaintext, in_out[..plaintext.len()]); |
381 | | |
382 | | // copy ciphertext with prefix, to exercise `open_within` |
383 | | let mut offset_cipher_text = vec![ 1, 2, 3, 4 ]; |
384 | | offset_cipher_text.extend_from_slice(&in_out); |
385 | | |
386 | | opening_key |
387 | | .open_in_place( |
388 | | Nonce::try_assume_unique_for_key(nonce_bytes).unwrap(), |
389 | | Aad::empty(), |
390 | | &mut in_out, |
391 | | ) |
392 | | .unwrap(); |
393 | | |
394 | | assert_eq!(plaintext, in_out[..plaintext.len()]); |
395 | | |
396 | | opening_key |
397 | | .open_within( |
398 | | Nonce::try_assume_unique_for_key(nonce_bytes).unwrap(), |
399 | | Aad::empty(), |
400 | | &mut offset_cipher_text, |
401 | | 4..) |
402 | | .unwrap(); |
403 | | assert_eq!(plaintext, offset_cipher_text[..plaintext.len()]); |
404 | | } |
405 | | } |
406 | | } |
407 | | }; |
408 | | } |
409 | | |
410 | | test_tls_aead!( |
411 | | aes_128_gcm_tls12, |
412 | | &AES_128_GCM, |
413 | | TlsProtocolId::TLS12, |
414 | | TEST_128_BIT_KEY, |
415 | | &16, |
416 | | &12 |
417 | | ); |
418 | | test_tls_aead!( |
419 | | aes_128_gcm_tls13, |
420 | | &AES_128_GCM, |
421 | | TlsProtocolId::TLS13, |
422 | | TEST_128_BIT_KEY, |
423 | | &16, |
424 | | &12 |
425 | | ); |
426 | | test_tls_aead!( |
427 | | aes_256_gcm_tls12, |
428 | | &AES_256_GCM, |
429 | | TlsProtocolId::TLS12, |
430 | | TEST_256_BIT_KEY, |
431 | | &16, |
432 | | &12 |
433 | | ); |
434 | | test_tls_aead!( |
435 | | aes_256_gcm_tls13, |
436 | | &AES_256_GCM, |
437 | | TlsProtocolId::TLS13, |
438 | | TEST_256_BIT_KEY, |
439 | | &16, |
440 | | &12 |
441 | | ); |
442 | | test_tls_aead!( |
443 | | chacha20_poly1305_tls12, |
444 | | &CHACHA20_POLY1305, |
445 | | TlsProtocolId::TLS12, |
446 | | TEST_256_BIT_KEY |
447 | | ); |
448 | | test_tls_aead!( |
449 | | chacha20_poly1305_tls13, |
450 | | &CHACHA20_POLY1305, |
451 | | TlsProtocolId::TLS13, |
452 | | TEST_256_BIT_KEY |
453 | | ); |
454 | | } |