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]
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 #[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 #[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 #[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}