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