1use 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
12pub struct RandomizedNonceKey {
24 key: UnboundKey,
25 algorithm: &'static Algorithm,
26}
27
28impl RandomizedNonceKey {
29 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 #[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 #[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 #[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 #[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}