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    //
61    // On input, in_out must be the ciphertext followed by the tag. When open_in_place() returns Ok(plaintext),
62    // the input ciphertext has been overwritten by the plaintext; plaintext will refer to the plaintext without the tag.
63    ///
64    /// # Errors
65    /// `error::Unspecified` when ciphertext is invalid.
66    #[inline]
67    #[allow(clippy::needless_pass_by_value)]
68    pub fn open_in_place<'in_out, A>(
69        &self,
70        nonce: Nonce,
71        aad: Aad<A>,
72        in_out: &'in_out mut [u8],
73    ) -> Result<&'in_out mut [u8], Unspecified>
74    where
75        A: AsRef<[u8]>,
76    {
77        self.key.open_within(nonce, aad.as_ref(), in_out, 0..)
78    }
79
80    /// Encrypts and signs (“seals”) data in place, appending the tag to the
81    /// resulting ciphertext.
82    ///
83    /// `key.seal_in_place_append_tag(aad, in_out)` is equivalent to:
84    ///
85    /// ```skip
86    /// key.seal_in_place_separate_tag(aad, in_out.as_mut())
87    ///     .map(|tag| in_out.extend(tag.as_ref()))
88    /// ```
89    ///
90    /// The Nonce used for the operation is randomly generated, and returned to the caller.
91    ///
92    /// # Errors
93    /// `error::Unspecified` if encryption operation fails.
94    #[inline]
95    #[allow(clippy::needless_pass_by_value)]
96    pub fn seal_in_place_append_tag<'a, A, InOut>(
97        &self,
98        aad: Aad<A>,
99        in_out: &'a mut InOut,
100    ) -> Result<Nonce, Unspecified>
101    where
102        A: AsRef<[u8]>,
103        InOut: AsMut<[u8]> + for<'in_out> Extend<&'in_out u8>,
104    {
105        let nonce = if self.algorithm == &AES_128_GCM_SIV || self.algorithm == &AES_256_GCM_SIV {
106            let mut nonce = vec![0u8; self.algorithm.nonce_len()];
107            crate::rand::fill(&mut nonce[..])?;
108            Some(Nonce::try_assume_unique_for_key(nonce.as_slice())?)
109        } else {
110            None
111        };
112        self.key
113            .seal_in_place_append_tag(nonce, aad.as_ref(), in_out)
114    }
115
116    /// Encrypts and signs (“seals”) data in place.
117    ///
118    /// `aad` is the additional authenticated data (AAD), if any. This is
119    /// authenticated but not encrypted. The type `A` could be a byte slice
120    /// `&[u8]`, a byte array `[u8; N]` for some constant `N`, `Vec<u8>`, etc.
121    /// If there is no AAD then use `Aad::empty()`.
122    ///
123    /// The plaintext is given as the input value of `in_out`. `seal_in_place()`
124    /// will overwrite the plaintext with the ciphertext and return the tag.
125    /// For most protocols, the caller must append the tag to the ciphertext.
126    /// The tag will be `self.algorithm.tag_len()` bytes long.
127    ///
128    /// The Nonce used for the operation is randomly generated, and returned to the caller.
129    ///
130    /// # Errors
131    /// `error::Unspecified` if encryption operation fails.
132    #[inline]
133    #[allow(clippy::needless_pass_by_value)]
134    pub fn seal_in_place_separate_tag<A>(
135        &self,
136        aad: Aad<A>,
137        in_out: &mut [u8],
138    ) -> Result<(Nonce, Tag), Unspecified>
139    where
140        A: AsRef<[u8]>,
141    {
142        let nonce = if self.algorithm == &AES_128_GCM_SIV || self.algorithm == &AES_256_GCM_SIV {
143            let mut nonce = vec![0u8; self.algorithm.nonce_len()];
144            crate::rand::fill(&mut nonce[..])?;
145            Some(Nonce::try_assume_unique_for_key(nonce.as_slice())?)
146        } else {
147            None
148        };
149        self.key
150            .seal_in_place_separate_tag(nonce, aad.as_ref(), in_out)
151    }
152
153    /// The key's AEAD algorithm.
154    #[inline]
155    #[must_use]
156    pub fn algorithm(&self) -> &'static Algorithm {
157        self.algorithm
158    }
159}
160
161#[allow(clippy::missing_fields_in_debug)]
162impl Debug for RandomizedNonceKey {
163    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
164        f.debug_struct("RandomizedNonceKey")
165            .field("algorithm", &self.algorithm)
166            .finish()
167    }
168}
169
170#[cfg(test)]
171mod tests {
172    use super::{Aad, RandomizedNonceKey};
173    use crate::aead::{
174        AES_128_GCM, AES_128_GCM_SIV, AES_256_GCM, AES_256_GCM_SIV, CHACHA20_POLY1305,
175    };
176    use crate::test::from_hex;
177    use paste::paste;
178
179    const TEST_128_BIT_KEY: &[u8] = &[
180        0xb0, 0x37, 0x9f, 0xf8, 0xfb, 0x8e, 0xa6, 0x31, 0xf4, 0x1c, 0xe6, 0x3e, 0xb5, 0xc5, 0x20,
181        0x7c,
182    ];
183
184    const TEST_256_BIT_KEY: &[u8] = &[
185        0x56, 0xd8, 0x96, 0x68, 0xbd, 0x96, 0xeb, 0xff, 0x5e, 0xa2, 0x0b, 0x34, 0xf2, 0x79, 0x84,
186        0x6e, 0x2b, 0x13, 0x01, 0x3d, 0xab, 0x1d, 0xa4, 0x07, 0x5a, 0x16, 0xd5, 0x0b, 0x53, 0xb0,
187        0xcc, 0x88,
188    ];
189
190    macro_rules! test_randnonce {
191        ($name:ident, $alg:expr, $key:expr) => {
192            paste! {
193                #[test]
194                fn [<test_ $name _randnonce_unsupported>]() {
195                    assert!(RandomizedNonceKey::new($alg, $key).is_err());
196                }
197            }
198        };
199        ($name:ident, $alg:expr, $key:expr, $expect_tag_len:expr, $expect_nonce_len:expr) => {
200            paste! {
201                #[test]
202                fn [<test_ $name _randnonce>]() {
203                    let plaintext = from_hex("00112233445566778899aabbccddeeff").unwrap();
204                    let rand_nonce_key =
205                        RandomizedNonceKey::new($alg, $key).unwrap();
206
207                    assert_eq!($alg, rand_nonce_key.algorithm());
208                    assert_eq!(*$expect_tag_len, $alg.tag_len());
209                    assert_eq!(*$expect_nonce_len, $alg.nonce_len());
210
211                    let mut in_out = Vec::from(plaintext.as_slice());
212
213                    let nonce = rand_nonce_key
214                        .seal_in_place_append_tag(Aad::empty(), &mut in_out)
215                        .unwrap();
216
217                    assert_ne!(plaintext, in_out[..plaintext.len()]);
218
219                    rand_nonce_key
220                        .open_in_place(nonce, Aad::empty(), &mut in_out)
221                        .unwrap();
222
223                    assert_eq!(plaintext, in_out[..plaintext.len()]);
224
225                    let mut in_out = Vec::from(plaintext.as_slice());
226
227                    let (nonce, tag) = rand_nonce_key
228                        .seal_in_place_separate_tag(Aad::empty(), &mut in_out)
229                        .unwrap();
230
231                    assert_ne!(plaintext, in_out[..plaintext.len()]);
232
233                    in_out.extend(tag.as_ref());
234
235                    rand_nonce_key
236                        .open_in_place(nonce, Aad::empty(), &mut in_out)
237                        .unwrap();
238
239                    assert_eq!(plaintext, in_out[..plaintext.len()]);
240                }
241            }
242        };
243    }
244
245    test_randnonce!(aes_128_gcm, &AES_128_GCM, TEST_128_BIT_KEY, &16, &12);
246    test_randnonce!(aes_256_gcm, &AES_256_GCM, TEST_256_BIT_KEY, &16, &12);
247    test_randnonce!(
248        aes_128_gcm_siv,
249        &AES_128_GCM_SIV,
250        TEST_128_BIT_KEY,
251        &16,
252        &12
253    );
254    test_randnonce!(
255        aes_256_gcm_siv,
256        &AES_256_GCM_SIV,
257        TEST_256_BIT_KEY,
258        &16,
259        &12
260    );
261
262    test_randnonce!(chacha20_poly1305, &CHACHA20_POLY1305, TEST_256_BIT_KEY);
263}