1#[cfg(feature = "alloc")]
62use alloc::vec::Vec;
63
64use crate::classic::crypto_auth::{
65 AuthState, crypto_auth, crypto_auth_final, crypto_auth_init, crypto_auth_update,
66 crypto_auth_verify,
67};
68use crate::constants::{CRYPTO_AUTH_BYTES, CRYPTO_AUTH_KEYBYTES};
69use crate::error::Error;
70use crate::types::*;
71use crate::utils::verify_ct;
72
73pub type Key = StackByteArray<CRYPTO_AUTH_KEYBYTES>;
75pub type Mac = StackByteArray<CRYPTO_AUTH_BYTES>;
77
78#[cfg(any(
79 all(feature = "protected", any(unix, windows)),
80 all(doc, not(doctest), feature = "std")
81))]
82#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
83pub mod protected {
84 use super::*;
102 pub use crate::protected::*;
103
104 pub type Key = HeapByteArray<CRYPTO_AUTH_KEYBYTES>;
107 pub type Mac = HeapByteArray<CRYPTO_AUTH_BYTES>;
110}
111
112pub struct Auth {
115 state: AuthState,
116}
117
118impl Auth {
119 #[must_use]
121 pub fn compute<
122 Output: NewByteArray<CRYPTO_AUTH_BYTES>,
123 Key: ByteArray<CRYPTO_AUTH_KEYBYTES>,
124 Input: Bytes + ?Sized,
125 >(
126 key: &Key,
127 input: &Input,
128 ) -> Output {
129 let mut output = Output::new_byte_array();
130 crypto_auth(output.as_mut_array(), input.as_slice(), key.as_array());
131 output
132 }
133
134 #[cfg(feature = "alloc")]
138 #[must_use]
139 pub fn compute_to_vec<Key: ByteArray<CRYPTO_AUTH_KEYBYTES>, Input: Bytes + ?Sized>(
140 key: &Key,
141 input: &Input,
142 ) -> Vec<u8> {
143 Self::compute::<Mac, _, _>(key, input).to_vec()
144 }
145
146 pub fn compute_and_verify<
153 OtherMac: ByteArray<CRYPTO_AUTH_BYTES>,
154 Key: ByteArray<CRYPTO_AUTH_KEYBYTES>,
155 Input: Bytes + ?Sized,
156 >(
157 other_mac: &OtherMac,
158 key: &Key,
159 input: &Input,
160 ) -> Result<(), Error> {
161 crypto_auth_verify(other_mac.as_array(), input.as_slice(), key.as_array())
162 }
163
164 #[must_use]
166 pub fn new<Key: ByteArray<CRYPTO_AUTH_KEYBYTES>>(key: &Key) -> Self {
167 Self {
168 state: crypto_auth_init(key.as_array()),
169 }
170 }
171
172 pub fn update<Input: Bytes + ?Sized>(&mut self, input: &Input) {
174 crypto_auth_update(&mut self.state, input.as_slice())
175 }
176
177 #[must_use]
180 pub fn finalize<Output: NewByteArray<CRYPTO_AUTH_BYTES>>(self) -> Output {
181 let mut output = Output::new_byte_array();
182 crypto_auth_final(self.state, output.as_mut_array());
183 output
184 }
185
186 #[cfg(feature = "alloc")]
190 #[must_use]
191 pub fn finalize_to_vec(self) -> Vec<u8> {
192 self.finalize::<Mac>().to_vec()
193 }
194
195 pub fn verify<OtherMac: ByteArray<CRYPTO_AUTH_BYTES>>(
203 self,
204 other_mac: &OtherMac,
205 ) -> Result<(), Error> {
206 let computed_mac: Mac = self.finalize();
207
208 verify_ct(other_mac.as_array(), computed_mac.as_array())
209 }
210}
211
212#[cfg(all(test, feature = "alloc"))]
213mod tests {
214 use super::*;
215 use crate::classic::crypto_auth_hmac_impl::test_util::RFC4231_PADDABLE_KEYS as CASES;
219
220 fn padded_key(key: &[u8]) -> Key {
221 let mut padded = Key::default();
222 padded[..key.len()].copy_from_slice(key);
223 padded
224 }
225
226 #[test]
227 fn rfc4231_vectors_through_single_and_multi_part_interfaces() {
228 for case in CASES {
229 let (key, message, expected) = (padded_key(case.key), case.data, case.sha512256());
230
231 assert_eq!(Auth::compute_to_vec(&key, &message), expected);
232 let fixed: Mac = Auth::compute(&key, &message);
233 assert_eq!(fixed.as_slice(), expected.as_slice());
234 Auth::compute_and_verify(&fixed, &key, &message).expect("verify failed");
235
236 let split = message.len() / 2;
237 let mut auth = Auth::new(&key);
238 auth.update(&message[..split]);
239 auth.update(&[][..]);
240 auth.update(&message[split..]);
241 assert_eq!(auth.finalize_to_vec(), expected);
242
243 let mut verifier = Auth::new(&key);
244 verifier.update(&message);
245 verifier.verify(&fixed).expect("incremental verify failed");
246
247 for index in [0, CRYPTO_AUTH_BYTES - 1] {
248 let mut flipped = fixed.clone();
249 flipped[index] ^= 1;
250 assert!(matches!(
251 Auth::compute_and_verify(&flipped, &key, &message),
252 Err(Error::AuthenticationFailed)
253 ));
254 let mut verifier = Auth::new(&key);
255 verifier.update(&message);
256 assert!(matches!(
257 verifier.verify(&flipped),
258 Err(Error::AuthenticationFailed)
259 ));
260 }
261
262 let mut wrong_key = key.clone();
263 wrong_key[CRYPTO_AUTH_KEYBYTES - 1] ^= 1;
264 assert!(matches!(
265 Auth::compute_and_verify(&fixed, &wrong_key, &message),
266 Err(Error::AuthenticationFailed)
267 ));
268 let mut verifier = Auth::new(&key);
269 verifier.update(&message[..message.len() - 1]);
270 assert!(matches!(
271 verifier.verify(&fixed),
272 Err(Error::AuthenticationFailed)
273 ));
274 }
275 }
276
277 #[test]
278 fn rustaceous_and_classic_macs_verify_each_other() {
279 for case in CASES {
280 let (key, message) = (padded_key(case.key), case.data);
281 let mac = Auth::compute_to_vec(&key, &message);
282 crypto_auth_verify(
283 mac.as_slice().try_into().expect("MAC length"),
284 message,
285 key.as_array(),
286 )
287 .expect("classic verify");
288
289 let mut classic = [0u8; CRYPTO_AUTH_BYTES];
290 crypto_auth(&mut classic, message, key.as_array());
291 Auth::compute_and_verify(&classic, &key, &message).expect("rustaceous verify");
292 let mut verifier = Auth::new(&key);
293 verifier.update(&message);
294 verifier.verify(&classic).expect("incremental verify");
295 }
296 }
297
298 #[cfg(all(feature = "protected", any(unix, windows)))]
299 #[test]
300 fn locked_key_and_input_produce_the_same_mac() {
301 use crate::auth::protected::*;
302
303 for case in CASES {
304 let (key, message, expected) = (case.key, case.data, case.sha512256());
305 let key = protected::Key::from_slice_into_readonly_locked(padded_key(key).as_slice())
306 .expect("lock key");
307 let input = HeapBytes::from_slice_into_readonly_locked(message).expect("lock input");
308
309 let mac: Locked<protected::Mac> = Auth::compute(&key, &input);
310 assert_eq!(mac.as_slice(), expected.as_slice());
311 Auth::compute_and_verify(&mac, &key, &input).expect("verify failed");
312 let mut verifier = Auth::new(&key);
313 verifier.update(&input);
314 verifier.verify(&mac).expect("incremental verify failed");
315 }
316 }
317
318 #[cfg(dryoc_native_tests)]
319 #[test]
320 fn rfc4231_keys_match_libsodium() {
321 use crate::native_test_util::auth_hmacsha512256;
322
323 for case in CASES {
324 let (key, message) = (padded_key(case.key), case.data);
325 let so_tag = auth_hmacsha512256(message, key.as_slice());
326 assert_eq!(Auth::compute_to_vec(&key, &message), so_tag);
327 Auth::compute_and_verify(&so_tag, &key, &message).expect("verify sodium tag");
328 }
329 }
330}