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