1use crate::error::Unspecified;
5use core::fmt::Debug;
6
7use super::aead_ctx::AeadCtx;
8use super::{Aad, Algorithm, AlgorithmID, Nonce, Tag, UnboundKey};
9
10pub struct RandomizedNonceKey {
20 key: UnboundKey,
21 algorithm: &'static Algorithm,
22}
23
24impl RandomizedNonceKey {
25 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 #[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 #[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 #[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 #[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}