1#[cfg(feature = "alloc")]
64use alloc::vec::Vec;
65
66use crate::classic::crypto_onetimeauth::{
67 OnetimeauthState, crypto_onetimeauth, crypto_onetimeauth_final, crypto_onetimeauth_init,
68 crypto_onetimeauth_update, crypto_onetimeauth_verify,
69};
70use crate::constants::{CRYPTO_ONETIMEAUTH_BYTES, CRYPTO_ONETIMEAUTH_KEYBYTES};
71use crate::error::Error;
72use crate::types::*;
73use crate::utils::verify_ct;
74
75pub type Key = StackByteArray<CRYPTO_ONETIMEAUTH_KEYBYTES>;
77pub type Mac = StackByteArray<CRYPTO_ONETIMEAUTH_BYTES>;
79
80#[cfg(any(
81 all(feature = "protected", any(unix, windows)),
82 all(doc, not(doctest), feature = "std")
83))]
84#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
85pub mod protected {
86 use super::*;
104 pub use crate::protected::*;
105
106 pub type Key = HeapByteArray<CRYPTO_ONETIMEAUTH_KEYBYTES>;
109 pub type Mac = HeapByteArray<CRYPTO_ONETIMEAUTH_BYTES>;
112}
113
114pub struct OnetimeAuth {
117 state: OnetimeauthState,
118}
119
120impl OnetimeAuth {
121 #[must_use]
126 pub fn compute<
127 Output: NewByteArray<CRYPTO_ONETIMEAUTH_BYTES>,
128 Key: ByteArray<CRYPTO_ONETIMEAUTH_KEYBYTES>,
129 Input: Bytes + ?Sized,
130 >(
131 key: &Key,
132 input: &Input,
133 ) -> Output {
134 let mut output = Output::new_byte_array();
135 crypto_onetimeauth(output.as_mut_array(), input.as_slice(), key.as_array());
136 output
137 }
138
139 #[cfg(feature = "alloc")]
143 #[must_use]
144 pub fn compute_to_vec<Key: ByteArray<CRYPTO_ONETIMEAUTH_KEYBYTES>, Input: Bytes + ?Sized>(
145 key: &Key,
146 input: &Input,
147 ) -> Vec<u8> {
148 Self::compute::<Mac, _, _>(key, input).to_vec()
149 }
150
151 pub fn compute_and_verify<
158 OtherMac: ByteArray<CRYPTO_ONETIMEAUTH_BYTES>,
159 Key: ByteArray<CRYPTO_ONETIMEAUTH_KEYBYTES>,
160 Input: Bytes + ?Sized,
161 >(
162 other_mac: &OtherMac,
163 key: &Key,
164 input: &Input,
165 ) -> Result<(), Error> {
166 crypto_onetimeauth_verify(other_mac.as_array(), input.as_slice(), key.as_array())
167 }
168
169 #[must_use]
174 pub fn new<Key: ByteArray<CRYPTO_ONETIMEAUTH_KEYBYTES>>(key: &Key) -> Self {
175 Self {
176 state: crypto_onetimeauth_init(key.as_array()),
177 }
178 }
179
180 pub fn update<Input: Bytes + ?Sized>(&mut self, input: &Input) {
182 crypto_onetimeauth_update(&mut self.state, input.as_slice())
183 }
184
185 #[must_use]
188 pub fn finalize<Output: NewByteArray<CRYPTO_ONETIMEAUTH_BYTES>>(self) -> Output {
189 let mut output = Output::new_byte_array();
190 crypto_onetimeauth_final(self.state, output.as_mut_array());
191 output
192 }
193
194 #[cfg(feature = "alloc")]
198 #[must_use]
199 pub fn finalize_to_vec(self) -> Vec<u8> {
200 self.finalize::<Mac>().to_vec()
201 }
202
203 pub fn verify<OtherMac: ByteArray<CRYPTO_ONETIMEAUTH_BYTES>>(
211 self,
212 other_mac: &OtherMac,
213 ) -> Result<(), Error> {
214 let computed_mac: Mac = self.finalize();
215
216 verify_ct(other_mac.as_array(), computed_mac.as_array())
217 }
218}
219
220#[cfg(all(test, feature = "alloc"))]
221mod tests {
222 use super::*;
223
224 const KEY: &str = "85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b";
226 const MESSAGE: &[u8] = b"Cryptographic Forum Research Group";
227 const TAG: &str = "a8061dc1305136c6c22b8baf0c0127a9";
228
229 fn vector() -> (Key, Vec<u8>) {
230 (
231 Key::try_from(hex::decode(KEY).expect("hex").as_slice()).expect("key"),
232 hex::decode(TAG).expect("hex"),
233 )
234 }
235
236 #[test]
237 fn rfc8439_vector_through_single_and_multi_part_interfaces() {
238 let (key, expected) = vector();
239
240 assert_eq!(OnetimeAuth::compute_to_vec(&key, &MESSAGE), expected);
241 let fixed: Mac = OnetimeAuth::compute(&key, &MESSAGE);
242 assert_eq!(fixed.as_slice(), expected.as_slice());
243 OnetimeAuth::compute_and_verify(&fixed, &key, &MESSAGE).expect("verify failed");
244
245 for parts in [
248 vec![MESSAGE],
249 vec![&MESSAGE[..16], &MESSAGE[16..]],
250 vec![&MESSAGE[..15], &MESSAGE[15..17], &MESSAGE[17..]],
251 vec![
252 &[][..],
253 &MESSAGE[..1],
254 &[][..],
255 &MESSAGE[1..33],
256 &MESSAGE[33..],
257 &[][..],
258 ],
259 ] {
260 let mut auth = OnetimeAuth::new(&key);
261 for part in &parts {
262 auth.update(part);
263 }
264 assert_eq!(auth.finalize_to_vec(), expected);
265
266 let mut verifier = OnetimeAuth::new(&key);
267 for part in &parts {
268 verifier.update(part);
269 }
270 verifier.verify(&fixed).expect("incremental verify failed");
271 }
272
273 for index in [0, CRYPTO_ONETIMEAUTH_BYTES - 1] {
274 let mut flipped = fixed.clone();
275 flipped[index] ^= 1;
276 assert!(matches!(
277 OnetimeAuth::compute_and_verify(&flipped, &key, &MESSAGE),
278 Err(Error::AuthenticationFailed)
279 ));
280 let mut verifier = OnetimeAuth::new(&key);
281 verifier.update(&MESSAGE);
282 assert!(matches!(
283 verifier.verify(&flipped),
284 Err(Error::AuthenticationFailed)
285 ));
286 }
287
288 let mut wrong_key = key.clone();
289 wrong_key[CRYPTO_ONETIMEAUTH_KEYBYTES - 1] ^= 1;
290 assert!(matches!(
291 OnetimeAuth::compute_and_verify(&fixed, &wrong_key, &MESSAGE),
292 Err(Error::AuthenticationFailed)
293 ));
294 let mut verifier = OnetimeAuth::new(&key);
295 verifier.update(&MESSAGE[..MESSAGE.len() - 1]);
296 assert!(matches!(
297 verifier.verify(&fixed),
298 Err(Error::AuthenticationFailed)
299 ));
300 }
301
302 #[test]
303 fn rustaceous_and_classic_macs_verify_each_other() {
304 let (key, _) = vector();
305 for len in [0, 1, 15, 16, 17, 31, 32, MESSAGE.len()] {
306 let message = &MESSAGE[..len];
307 let mac = OnetimeAuth::compute_to_vec(&key, &message);
308 crypto_onetimeauth_verify(
309 mac.as_slice().try_into().expect("MAC length"),
310 message,
311 key.as_array(),
312 )
313 .expect("classic verify");
314
315 let mut classic = [0u8; CRYPTO_ONETIMEAUTH_BYTES];
316 crypto_onetimeauth(&mut classic, message, key.as_array());
317 OnetimeAuth::compute_and_verify(&classic, &key, &message).expect("rustaceous verify");
318 let mut verifier = OnetimeAuth::new(&key);
319 verifier.update(&message);
320 verifier.verify(&classic).expect("incremental verify");
321 }
322 }
323
324 #[cfg(all(feature = "protected", any(unix, windows)))]
325 #[test]
326 fn locked_key_and_input_produce_the_rfc8439_tag() {
327 use crate::onetimeauth::protected::*;
328
329 let (key, expected) = vector();
330 let key =
331 protected::Key::from_slice_into_readonly_locked(key.as_slice()).expect("lock key");
332 let input = HeapBytes::from_slice_into_readonly_locked(MESSAGE).expect("lock input");
333
334 let mac: Locked<protected::Mac> = OnetimeAuth::compute(&key, &input);
335 assert_eq!(mac.as_slice(), expected.as_slice());
336 OnetimeAuth::compute_and_verify(&mac, &key, &input).expect("verify failed");
337 let mut verifier = OnetimeAuth::new(&key);
338 verifier.update(&input);
339 verifier.verify(&mac).expect("incremental verify failed");
340 }
341
342 #[cfg(dryoc_native_tests)]
343 #[test]
344 fn rfc8439_vector_matches_libsodium() {
345 use crate::native_test_util::onetimeauth_poly1305;
346
347 let (key, expected) = vector();
348 let so_tag = onetimeauth_poly1305(MESSAGE, key.as_slice());
349 assert_eq!(so_tag.as_slice(), expected.as_slice());
350 OnetimeAuth::compute_and_verify(&so_tag, &key, &MESSAGE).expect("verify sodium tag");
351 }
352}