Skip to main content

aws_lc_rs/aead/
rand_nonce.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0 OR ISC
3
4use crate::error::Unspecified;
5use core::fmt::Debug;
6
7use super::aead_ctx::AeadCtx;
8use super::{
9    Aad, Algorithm, AlgorithmID, Nonce, Tag, UnboundKey, AES_128_GCM_SIV, AES_256_GCM_SIV,
10};
11
12/// AEAD Cipher key using a randomized nonce.
13///
14/// `RandomizedNonceKey` handles generation random nonce values.
15///
16/// The following algorithms are supported:
17/// * `AES_128_GCM`
18/// * `AES_256_GCM`
19/// * `AES_128_GCM_SIV`
20/// * `AES_256_GCM_SIV`
21///
22/// Prefer this type in place of `LessSafeKey`, `OpeningKey`, `SealingKey`.
23pub struct RandomizedNonceKey {
24    key: UnboundKey,
25    algorithm: &'static Algorithm,
26}
27
28impl RandomizedNonceKey {
29    /// New Random Nonce Sequence
30    /// # Errors
31    pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self, Unspecified> {
32        let ctx = match algorithm.id {
33            AlgorithmID::AES_128_GCM => AeadCtx::aes_128_gcm_randnonce(
34                key_bytes,
35                algorithm.tag_len(),
36                algorithm.nonce_len(),
37            ),
38            AlgorithmID::AES_256_GCM => AeadCtx::aes_256_gcm_randnonce(
39                key_bytes,
40                algorithm.tag_len(),
41                algorithm.nonce_len(),
42            ),
43            AlgorithmID::AES_128_GCM_SIV => {
44                AeadCtx::aes_128_gcm_siv(key_bytes, algorithm.tag_len())
45            }
46            AlgorithmID::AES_256_GCM_SIV => {
47                AeadCtx::aes_256_gcm_siv(key_bytes, algorithm.tag_len())
48            }
49            AlgorithmID::AES_192_GCM | AlgorithmID::CHACHA20_POLY1305 => return Err(Unspecified),
50        }?;
51        Ok(Self {
52            key: UnboundKey::from(ctx),
53            algorithm,
54        })
55    }
56
57    /// Authenticates and decrypts (“opens”) data in place.
58    ///
59    /// `aad` is the additional authenticated data (AAD), if any.
60    /// `nonce` must be the nonce used by the corresponding sealing operation.
61    ///
62    /// `in_out` must contain the ciphertext followed by the tag. On success, it is
63    /// overwritten with the plaintext, which is returned without the tag.
64    ///
65    /// # Errors
66    /// Returns `error::Unspecified` if the ciphertext is invalid. In this case,
67    /// `in_out` may have been overwritten in an unspecified way.
68    #[inline]
69    #[allow(clippy::needless_pass_by_value)]
70    pub fn open_in_place<'in_out, A>(
71        &self,
72        nonce: Nonce,
73        aad: Aad<A>,
74        in_out: &'in_out mut [u8],
75    ) -> Result<&'in_out mut [u8], Unspecified>
76    where
77        A: AsRef<[u8]>,
78    {
79        self.key.open_within(nonce, aad.as_ref(), in_out, 0..)
80    }
81
82    /// Encrypts and signs (“seals”) data in place, appending the tag to the
83    /// resulting ciphertext.
84    ///
85    /// `key.seal_in_place_append_tag(aad, in_out)` is equivalent to:
86    ///
87    /// ```skip
88    /// key.seal_in_place_separate_tag(aad, in_out.as_mut())
89    ///     .map(|tag| in_out.extend(tag.as_ref()))
90    /// ```
91    ///
92    /// The Nonce used for the operation is randomly generated, and returned to the caller.
93    ///
94    /// # Errors
95    /// `error::Unspecified` if encryption operation fails.
96    #[inline]
97    #[allow(clippy::needless_pass_by_value)]
98    pub fn seal_in_place_append_tag<'a, A, InOut>(
99        &self,
100        aad: Aad<A>,
101        in_out: &'a mut InOut,
102    ) -> Result<Nonce, Unspecified>
103    where
104        A: AsRef<[u8]>,
105        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
106    {
107        let nonce = if self.algorithm == &AES_128_GCM_SIV || self.algorithm == &AES_256_GCM_SIV {
108            let mut nonce = vec![0u8; self.algorithm.nonce_len()];
109            crate::rand::fill(&mut nonce[..])?;
110            Some(Nonce::try_assume_unique_for_key(nonce.as_slice())?)
111        } else {
112            None
113        };
114        self.key
115            .seal_in_place_append_tag(nonce, aad.as_ref(), in_out)
116    }
117
118    /// Encrypts and signs (“seals”) data in place.
119    ///
120    /// `aad` is the additional authenticated data (AAD), if any. This is
121    /// authenticated but not encrypted. The type `A` could be a byte slice
122    /// `&[u8]`, a byte array `[u8; N]` for some constant `N`, `Vec<u8>`, etc.
123    /// If there is no AAD then use `Aad::empty()`.
124    ///
125    /// The plaintext is given as the input value of `in_out`. `seal_in_place()`
126    /// will overwrite the plaintext with the ciphertext and return the tag.
127    /// For most protocols, the caller must append the tag to the ciphertext.
128    /// The tag will be `self.algorithm.tag_len()` bytes long.
129    ///
130    /// The Nonce used for the operation is randomly generated, and returned to the caller.
131    ///
132    /// # Errors
133    /// `error::Unspecified` if encryption operation fails.
134    #[inline]
135    #[allow(clippy::needless_pass_by_value)]
136    pub fn seal_in_place_separate_tag<A>(
137        &self,
138        aad: Aad<A>,
139        in_out: &mut [u8],
140    ) -> Result<(Nonce, Tag), Unspecified>
141    where
142        A: AsRef<[u8]>,
143    {
144        let nonce = if self.algorithm == &AES_128_GCM_SIV || self.algorithm == &AES_256_GCM_SIV {
145            let mut nonce = vec![0u8; self.algorithm.nonce_len()];
146            crate::rand::fill(&mut nonce[..])?;
147            Some(Nonce::try_assume_unique_for_key(nonce.as_slice())?)
148        } else {
149            None
150        };
151        self.key
152            .seal_in_place_separate_tag(nonce, aad.as_ref(), in_out)
153    }
154
155    /// The key's AEAD algorithm.
156    #[inline]
157    #[must_use]
158    pub fn algorithm(&self) -> &'static Algorithm {
159        self.algorithm
160    }
161}
162
163#[allow(clippy::missing_fields_in_debug)]
164impl Debug for RandomizedNonceKey {
165    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
166        f.debug_struct("RandomizedNonceKey")
167            .field("algorithm", &self.algorithm)
168            .finish()
169    }
170}
171
172#[cfg(test)]
173mod tests {
174    use super::{Aad, RandomizedNonceKey};
175    use crate::aead::{
176        AES_128_GCM, AES_128_GCM_SIV, AES_256_GCM, AES_256_GCM_SIV, CHACHA20_POLY1305,
177    };
178    use crate::test::from_hex;
179    use paste::paste;
180
181    const TEST_128_BIT_KEY: &[u8] = &[
182        0xb0, 0x37, 0x9f, 0xf8, 0xfb, 0x8e, 0xa6, 0x31, 0xf4, 0x1c, 0xe6, 0x3e, 0xb5, 0xc5, 0x20,
183        0x7c,
184    ];
185
186    const TEST_256_BIT_KEY: &[u8] = &[
187        0x56, 0xd8, 0x96, 0x68, 0xbd, 0x96, 0xeb, 0xff, 0x5e, 0xa2, 0x0b, 0x34, 0xf2, 0x79, 0x84,
188        0x6e, 0x2b, 0x13, 0x01, 0x3d, 0xab, 0x1d, 0xa4, 0x07, 0x5a, 0x16, 0xd5, 0x0b, 0x53, 0xb0,
189        0xcc, 0x88,
190    ];
191
192    macro_rules! test_randnonce {
193        ($name:ident, $alg:expr, $key:expr) => {
194            paste! {
195                #[test]
196                fn [<test_ $name _randnonce_unsupported>]() {
197                    assert!(RandomizedNonceKey::new($alg, $key).is_err());
198                }
199            }
200        };
201        ($name:ident, $alg:expr, $key:expr, $expect_tag_len:expr, $expect_nonce_len:expr) => {
202            paste! {
203                #[test]
204                fn [<test_ $name _randnonce>]() {
205                    let plaintext = from_hex("00112233445566778899aabbccddeeff").unwrap();
206                    let rand_nonce_key =
207                        RandomizedNonceKey::new($alg, $key).unwrap();
208
209                    assert_eq!($alg, rand_nonce_key.algorithm());
210                    assert_eq!(*$expect_tag_len, $alg.tag_len());
211                    assert_eq!(*$expect_nonce_len, $alg.nonce_len());
212
213                    let mut in_out = Vec::from(plaintext.as_slice());
214
215                    let nonce = rand_nonce_key
216                        .seal_in_place_append_tag(Aad::empty(), &mut in_out)
217                        .unwrap();
218
219                    assert_ne!(plaintext, in_out[..plaintext.len()]);
220
221                    rand_nonce_key
222                        .open_in_place(nonce, Aad::empty(), &mut in_out)
223                        .unwrap();
224
225                    assert_eq!(plaintext, in_out[..plaintext.len()]);
226
227                    let mut in_out = Vec::from(plaintext.as_slice());
228
229                    let (nonce, tag) = rand_nonce_key
230                        .seal_in_place_separate_tag(Aad::empty(), &mut in_out)
231                        .unwrap();
232
233                    assert_ne!(plaintext, in_out[..plaintext.len()]);
234
235                    in_out.extend(tag.as_ref());
236
237                    rand_nonce_key
238                        .open_in_place(nonce, Aad::empty(), &mut in_out)
239                        .unwrap();
240
241                    assert_eq!(plaintext, in_out[..plaintext.len()]);
242                }
243            }
244        };
245    }
246
247    test_randnonce!(aes_128_gcm, &AES_128_GCM, TEST_128_BIT_KEY, &16, &12);
248    test_randnonce!(aes_256_gcm, &AES_256_GCM, TEST_256_BIT_KEY, &16, &12);
249    test_randnonce!(
250        aes_128_gcm_siv,
251        &AES_128_GCM_SIV,
252        TEST_128_BIT_KEY,
253        &16,
254        &12
255    );
256    test_randnonce!(
257        aes_256_gcm_siv,
258        &AES_256_GCM_SIV,
259        TEST_256_BIT_KEY,
260        &16,
261        &12
262    );
263
264    test_randnonce!(chacha20_poly1305, &CHACHA20_POLY1305, TEST_256_BIT_KEY);
265}