Skip to main content

dryoc/
hmac.rs

1//! # HMAC authentication
2//!
3//! [`HmacSha256`], [`HmacSha512`], and [`HmacSha512256`] provide Rustaceous
4//! wrappers for libsodium's direct HMAC authentication variants.
5//!
6//! HMAC computes a fixed-size authentication tag for a message using a shared
7//! secret key. Anyone with the same key can recompute the tag and verify that
8//! the message was produced by someone who knew the key and that the message
9//! was not changed. HMAC does not encrypt the message.
10//!
11//! Use these types when:
12//!
13//! * you need one of libsodium's direct `crypto_auth_hmacsha*` variants
14//! * two parties already share the same secret key
15//! * the message can be public, but tampering must be detected
16//!
17//! [`HmacSha512256`] matches libsodium's default [`crypto_auth`](crate::auth)
18//! construction. [`HmacSha256`] and [`HmacSha512`] are available for protocol
19//! compatibility when those exact algorithms are required.
20//!
21//! # Rustaceous API example
22//!
23//! ```
24//! use dryoc::hmac::{HmacSha256, HmacSha256Key, HmacSha256Mac};
25//! use dryoc::types::*;
26//!
27//! let key = HmacSha256Key::generate();
28//! let message = b"Uneasy lies the head that wears a crown.";
29//!
30//! let mac: HmacSha256Mac = HmacSha256::compute(&key, message);
31//! HmacSha256::compute_and_verify(&mac, &key, message).expect("verify failed");
32//! ```
33//!
34//! The concrete authenticators are type aliases over [`Hmac`] and can also be
35//! used through [`HmacVariant`] in generic code.
36//!
37//! # Incremental interface
38//!
39//! ```
40//! use dryoc::hmac::{HmacSha512256, HmacSha512256Key, HmacSha512256Mac};
41//! use dryoc::types::*;
42//!
43//! let key = HmacSha512256Key::generate();
44//! let mut auth = HmacSha512256::new(&key);
45//! auth.update(b"Though she be but little, ");
46//! auth.update(b"she is fierce.");
47//! let mac: HmacSha512256Mac = auth.finalize();
48//!
49//! let mut verifier = HmacSha512256::new(&key);
50//! verifier.update(b"Though she be but little, ");
51//! verifier.update(b"she is fierce.");
52//! verifier.verify(&mac).expect("verify failed");
53//! ```
54//!
55//! # Generic HMAC variants
56//!
57//! ```
58//! # #[cfg(feature = "alloc")]
59//! # {
60//! use dryoc::constants::{CRYPTO_AUTH_HMACSHA256_BYTES, CRYPTO_AUTH_HMACSHA256_KEYBYTES};
61//! use dryoc::hmac::{Hmac, HmacSha256, HmacSha256Key, HmacSha256Variant, HmacVariant};
62//! use dryoc::types::*;
63//!
64//! fn authenticate<Variant, const KEY_LENGTH: usize, const MAC_LENGTH: usize>(
65//!     key: &StackByteArray<KEY_LENGTH>,
66//!     input: &[u8],
67//! ) -> Vec<u8>
68//! where
69//!     Variant: HmacVariant<KEY_LENGTH, MAC_LENGTH>,
70//! {
71//!     Hmac::<Variant, KEY_LENGTH, MAC_LENGTH>::compute_to_vec(key, input)
72//! }
73//!
74//! let key = HmacSha256Key::generate();
75//! let message = b"The quality of mercy is not strained.";
76//! let generic_mac = authenticate::<
77//!     HmacSha256Variant,
78//!     CRYPTO_AUTH_HMACSHA256_KEYBYTES,
79//!     CRYPTO_AUTH_HMACSHA256_BYTES,
80//! >(&key, message);
81//! let concrete_mac = HmacSha256::compute_to_vec(&key, message);
82//! assert_eq!(generic_mac, concrete_mac);
83//! # }
84//! ```
85
86#[cfg(feature = "alloc")]
87use alloc::vec::Vec;
88use core::marker::PhantomData;
89
90use subtle::ConstantTimeEq;
91use zeroize::Zeroize;
92
93use crate::classic::crypto_auth_hmacsha256::{
94    HmacSha256State, crypto_auth_hmacsha256, crypto_auth_hmacsha256_final,
95    crypto_auth_hmacsha256_init, crypto_auth_hmacsha256_update, crypto_auth_hmacsha256_verify,
96};
97use crate::classic::crypto_auth_hmacsha512::{
98    HmacSha512State, crypto_auth_hmacsha512, crypto_auth_hmacsha512_final,
99    crypto_auth_hmacsha512_init, crypto_auth_hmacsha512_update, crypto_auth_hmacsha512_verify,
100};
101use crate::classic::crypto_auth_hmacsha512256::{
102    HmacSha512256State, crypto_auth_hmacsha512256, crypto_auth_hmacsha512256_final,
103    crypto_auth_hmacsha512256_init, crypto_auth_hmacsha512256_update,
104    crypto_auth_hmacsha512256_verify,
105};
106use crate::constants::{
107    CRYPTO_AUTH_HMACSHA256_BYTES, CRYPTO_AUTH_HMACSHA256_KEYBYTES, CRYPTO_AUTH_HMACSHA512_BYTES,
108    CRYPTO_AUTH_HMACSHA512_KEYBYTES, CRYPTO_AUTH_HMACSHA512256_BYTES,
109    CRYPTO_AUTH_HMACSHA512256_KEYBYTES,
110};
111use crate::error::Error;
112use crate::types::*;
113
114/// Stack-allocated key for HMAC-SHA-256.
115pub type HmacSha256Key = StackByteArray<CRYPTO_AUTH_HMACSHA256_KEYBYTES>;
116/// Stack-allocated message authentication code for HMAC-SHA-256.
117pub type HmacSha256Mac = StackByteArray<CRYPTO_AUTH_HMACSHA256_BYTES>;
118/// Stack-allocated key for HMAC-SHA-512.
119pub type HmacSha512Key = StackByteArray<CRYPTO_AUTH_HMACSHA512_KEYBYTES>;
120/// Stack-allocated message authentication code for HMAC-SHA-512.
121pub type HmacSha512Mac = StackByteArray<CRYPTO_AUTH_HMACSHA512_BYTES>;
122/// Stack-allocated key for HMAC-SHA-512-256.
123pub type HmacSha512256Key = StackByteArray<CRYPTO_AUTH_HMACSHA512256_KEYBYTES>;
124/// Stack-allocated message authentication code for HMAC-SHA-512-256.
125pub type HmacSha512256Mac = StackByteArray<CRYPTO_AUTH_HMACSHA512256_BYTES>;
126
127#[cfg(any(
128    all(feature = "protected", any(unix, windows)),
129    all(doc, not(doctest), feature = "std")
130))]
131#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
132pub mod protected {
133    //! # Protected memory type aliases for HMAC
134    //!
135    //! This mod provides protected-memory aliases for HMAC keys and MACs. Use
136    //! these aliases when key material or authentication tags should live in
137    //! locked memory.
138    //!
139    //! ```
140    //! use dryoc::hmac::HmacSha256;
141    //! use dryoc::hmac::protected::*;
142    //!
143    //! let key = HmacSha256Key::generate_readonly_locked().expect("key failed");
144    //! let input = HeapBytes::from_slice_into_readonly_locked(b"More matter, with less art.")
145    //!     .expect("input failed");
146    //! let mac: Locked<HmacSha256Mac> = HmacSha256::compute(&key, &input);
147    //! ```
148    use super::*;
149    pub use crate::protected::*;
150
151    /// Heap-allocated, page-aligned key for HMAC-SHA-256.
152    pub type HmacSha256Key = HeapByteArray<CRYPTO_AUTH_HMACSHA256_KEYBYTES>;
153    /// Heap-allocated, page-aligned MAC for HMAC-SHA-256.
154    pub type HmacSha256Mac = HeapByteArray<CRYPTO_AUTH_HMACSHA256_BYTES>;
155    /// Heap-allocated, page-aligned key for HMAC-SHA-512.
156    pub type HmacSha512Key = HeapByteArray<CRYPTO_AUTH_HMACSHA512_KEYBYTES>;
157    /// Heap-allocated, page-aligned MAC for HMAC-SHA-512.
158    pub type HmacSha512Mac = HeapByteArray<CRYPTO_AUTH_HMACSHA512_BYTES>;
159    /// Heap-allocated, page-aligned key for HMAC-SHA-512-256.
160    pub type HmacSha512256Key = HeapByteArray<CRYPTO_AUTH_HMACSHA512256_KEYBYTES>;
161    /// Heap-allocated, page-aligned MAC for HMAC-SHA-512-256.
162    pub type HmacSha512256Mac = HeapByteArray<CRYPTO_AUTH_HMACSHA512256_BYTES>;
163}
164
165mod sealed {
166    use crate::error::Error;
167    use crate::types::NewByteArray;
168
169    /// The primitive operations behind an [`HmacVariant`](super::HmacVariant),
170    /// private to dryoc.
171    pub trait Sealed<const KEY_LENGTH: usize, const MAC_LENGTH: usize> {
172        /// Incremental state for this HMAC variant.
173        type State;
174        /// Default stack-allocated MAC type used by verification.
175        type Mac: NewByteArray<MAC_LENGTH> + zeroize::Zeroize;
176
177        /// Computes a MAC in one shot.
178        fn compute(mac: &mut [u8; MAC_LENGTH], input: &[u8], key: &[u8; KEY_LENGTH]);
179        /// Verifies a MAC in one shot.
180        fn verify(
181            mac: &[u8; MAC_LENGTH],
182            input: &[u8],
183            key: &[u8; KEY_LENGTH],
184        ) -> Result<(), Error>;
185        /// Initializes incremental authentication.
186        fn init(key: &[u8; KEY_LENGTH]) -> Self::State;
187        /// Updates incremental authentication.
188        fn update(state: &mut Self::State, input: &[u8]);
189        /// Finalizes incremental authentication.
190        fn finalize(state: Self::State, mac: &mut [u8; MAC_LENGTH]);
191    }
192}
193
194/// HMAC algorithm variant used by [`Hmac`]: [`HmacSha256Variant`],
195/// [`HmacSha512Variant`] or [`HmacSha512256Variant`].
196///
197/// This trait is sealed so applications cannot plug in custom cryptographic
198/// algorithms; use it to write code that is generic over the provided
199/// variants.
200pub trait HmacVariant<const KEY_LENGTH: usize, const MAC_LENGTH: usize>:
201    sealed::Sealed<KEY_LENGTH, MAC_LENGTH>
202{
203}
204
205/// Rustaceous HMAC authenticator for a specific [`HmacVariant`].
206pub struct Hmac<Variant, const KEY_LENGTH: usize, const MAC_LENGTH: usize>
207where
208    Variant: HmacVariant<KEY_LENGTH, MAC_LENGTH>,
209{
210    state: Variant::State,
211    _variant: PhantomData<Variant>,
212}
213
214/// HMAC-SHA-256 algorithm marker.
215#[derive(Clone, Copy, Debug, Default)]
216pub struct HmacSha256Variant;
217/// HMAC-SHA-512 algorithm marker.
218#[derive(Clone, Copy, Debug, Default)]
219pub struct HmacSha512Variant;
220/// HMAC-SHA-512-256 algorithm marker.
221#[derive(Clone, Copy, Debug, Default)]
222pub struct HmacSha512256Variant;
223
224/// Rustaceous HMAC-SHA-256 authenticator.
225pub type HmacSha256 =
226    Hmac<HmacSha256Variant, CRYPTO_AUTH_HMACSHA256_KEYBYTES, CRYPTO_AUTH_HMACSHA256_BYTES>;
227/// Rustaceous HMAC-SHA-512 authenticator.
228pub type HmacSha512 =
229    Hmac<HmacSha512Variant, CRYPTO_AUTH_HMACSHA512_KEYBYTES, CRYPTO_AUTH_HMACSHA512_BYTES>;
230/// Rustaceous HMAC-SHA-512-256 authenticator.
231pub type HmacSha512256 =
232    Hmac<HmacSha512256Variant, CRYPTO_AUTH_HMACSHA512256_KEYBYTES, CRYPTO_AUTH_HMACSHA512256_BYTES>;
233
234macro_rules! impl_hmac_variant {
235    (
236        $variant:ty,
237        $key_len:expr,
238        $mac_len:expr,
239        $state:ty,
240        $mac:ty,
241        $compute:path,
242        $verify:path,
243        $init:path,
244        $update:path,
245        $finalize:path
246    ) => {
247        impl HmacVariant<$key_len, $mac_len> for $variant {}
248
249        impl sealed::Sealed<$key_len, $mac_len> for $variant {
250            type Mac = $mac;
251            type State = $state;
252
253            fn compute(mac: &mut [u8; $mac_len], input: &[u8], key: &[u8; $key_len]) {
254                $compute(mac, input, key);
255            }
256
257            fn verify(
258                mac: &[u8; $mac_len],
259                input: &[u8],
260                key: &[u8; $key_len],
261            ) -> Result<(), Error> {
262                $verify(mac, input, key)
263            }
264
265            fn init(key: &[u8; $key_len]) -> Self::State {
266                $init(key)
267            }
268
269            fn update(state: &mut Self::State, input: &[u8]) {
270                $update(state, input);
271            }
272
273            fn finalize(state: Self::State, mac: &mut [u8; $mac_len]) {
274                $finalize(state, mac);
275            }
276        }
277    };
278}
279
280impl_hmac_variant!(
281    HmacSha256Variant,
282    CRYPTO_AUTH_HMACSHA256_KEYBYTES,
283    CRYPTO_AUTH_HMACSHA256_BYTES,
284    HmacSha256State,
285    HmacSha256Mac,
286    crypto_auth_hmacsha256,
287    crypto_auth_hmacsha256_verify,
288    crypto_auth_hmacsha256_init,
289    crypto_auth_hmacsha256_update,
290    crypto_auth_hmacsha256_final
291);
292
293impl_hmac_variant!(
294    HmacSha512Variant,
295    CRYPTO_AUTH_HMACSHA512_KEYBYTES,
296    CRYPTO_AUTH_HMACSHA512_BYTES,
297    HmacSha512State,
298    HmacSha512Mac,
299    crypto_auth_hmacsha512,
300    crypto_auth_hmacsha512_verify,
301    crypto_auth_hmacsha512_init,
302    crypto_auth_hmacsha512_update,
303    crypto_auth_hmacsha512_final
304);
305
306impl_hmac_variant!(
307    HmacSha512256Variant,
308    CRYPTO_AUTH_HMACSHA512256_KEYBYTES,
309    CRYPTO_AUTH_HMACSHA512256_BYTES,
310    HmacSha512256State,
311    HmacSha512256Mac,
312    crypto_auth_hmacsha512256,
313    crypto_auth_hmacsha512256_verify,
314    crypto_auth_hmacsha512256_init,
315    crypto_auth_hmacsha512256_update,
316    crypto_auth_hmacsha512256_final
317);
318
319impl<Variant, const KEY_LENGTH: usize, const MAC_LENGTH: usize>
320    Hmac<Variant, KEY_LENGTH, MAC_LENGTH>
321where
322    Variant: HmacVariant<KEY_LENGTH, MAC_LENGTH>,
323{
324    /// Computes and returns the message authentication code for `input` using
325    /// `key`.
326    #[must_use]
327    pub fn compute<
328        Output: NewByteArray<MAC_LENGTH>,
329        Key: ByteArray<KEY_LENGTH>,
330        Input: Bytes + ?Sized,
331    >(
332        key: &Key,
333        input: &Input,
334    ) -> Output {
335        let mut output = Output::new_byte_array();
336        Variant::compute(output.as_mut_array(), input.as_slice(), key.as_array());
337        output
338    }
339
340    /// Convenience wrapper around [`Self::compute`] that returns a [`Vec`].
341    #[cfg(feature = "alloc")]
342    #[must_use]
343    pub fn compute_to_vec<Key: ByteArray<KEY_LENGTH>, Input: Bytes + ?Sized>(
344        key: &Key,
345        input: &Input,
346    ) -> Vec<u8> {
347        Self::compute::<StackByteArray<MAC_LENGTH>, _, _>(key, input).to_vec()
348    }
349
350    /// Verifies `other_mac` against `input` using `key`.
351    ///
352    /// # Errors
353    ///
354    /// Returns an error if `other_mac` does not authenticate `input` under
355    /// `key`.
356    pub fn compute_and_verify<
357        OtherMac: ByteArray<MAC_LENGTH>,
358        Key: ByteArray<KEY_LENGTH>,
359        Input: Bytes + ?Sized,
360    >(
361        other_mac: &OtherMac,
362        key: &Key,
363        input: &Input,
364    ) -> Result<(), Error> {
365        Variant::verify(other_mac.as_array(), input.as_slice(), key.as_array())
366    }
367
368    /// Returns a new incremental authenticator for `key`.
369    #[must_use]
370    pub fn new<Key: ByteArray<KEY_LENGTH>>(key: &Key) -> Self {
371        Self {
372            state: Variant::init(key.as_array()),
373            _variant: PhantomData,
374        }
375    }
376
377    /// Updates the authenticator with `input`.
378    pub fn update<Input: Bytes + ?Sized>(&mut self, input: &Input) {
379        Variant::update(&mut self.state, input.as_slice())
380    }
381
382    /// Finalizes this authenticator, returning the message authentication code.
383    #[must_use]
384    pub fn finalize<Output: NewByteArray<MAC_LENGTH>>(self) -> Output {
385        let mut output = Output::new_byte_array();
386        Variant::finalize(self.state, output.as_mut_array());
387        output
388    }
389
390    /// Finalizes this authenticator, returning the message authentication code
391    /// as a [`Vec`].
392    #[cfg(feature = "alloc")]
393    #[must_use]
394    pub fn finalize_to_vec(self) -> Vec<u8> {
395        self.finalize::<StackByteArray<MAC_LENGTH>>().to_vec()
396    }
397
398    /// Finalizes this authenticator and verifies that the computed code matches
399    /// `other_mac` using a constant-time comparison.
400    ///
401    /// # Errors
402    ///
403    /// Returns an error if `other_mac` does not match the authentication code
404    /// computed from the data passed to [`Hmac::update`].
405    pub fn verify<OtherMac: ByteArray<MAC_LENGTH>>(
406        self,
407        other_mac: &OtherMac,
408    ) -> Result<(), Error> {
409        let mut computed_mac = Variant::Mac::new_byte_array();
410        Variant::finalize(self.state, computed_mac.as_mut_array());
411        let valid = other_mac
412            .as_array()
413            .ct_eq(computed_mac.as_array())
414            .unwrap_u8();
415        computed_mac.as_mut_slice().zeroize();
416
417        if valid == 1 {
418            Ok(())
419        } else {
420            Err(Error::AuthenticationFailed)
421        }
422    }
423}
424
425#[cfg(all(test, feature = "alloc"))]
426mod tests {
427    use super::*;
428    // RFC 4231 test cases 1-4. libsodium's `crypto_auth_hmacsha*` take
429    // fixed 32-byte keys, so the shorter RFC keys are zero-padded, which HMAC
430    // defines to produce the same tag. HMAC-SHA-512-256 is the 32-byte
431    // truncation of HMAC-SHA-512.
432    use crate::classic::crypto_auth_hmac_impl::test_util::RFC4231_PADDABLE_KEYS as CASES;
433
434    fn padded_key<const KEY_LENGTH: usize>(key: &[u8]) -> StackByteArray<KEY_LENGTH> {
435        let mut padded = StackByteArray::<KEY_LENGTH>::default();
436        padded[..key.len()].copy_from_slice(key);
437        padded
438    }
439
440    /// Exercises every entry point of one variant against `expected`.
441    fn assert_variant<Variant, const KEY_LENGTH: usize, const MAC_LENGTH: usize>(
442        key: StackByteArray<KEY_LENGTH>,
443        message: &[u8],
444        expected: &[u8],
445    ) where
446        Variant: HmacVariant<KEY_LENGTH, MAC_LENGTH>,
447    {
448        type H<V, const K: usize, const M: usize> = Hmac<V, K, M>;
449
450        assert_eq!(
451            H::<Variant, KEY_LENGTH, MAC_LENGTH>::compute_to_vec(&key, message),
452            expected
453        );
454        let fixed: StackByteArray<MAC_LENGTH> =
455            H::<Variant, KEY_LENGTH, MAC_LENGTH>::compute(&key, message);
456        assert_eq!(fixed.as_slice(), expected);
457        H::<Variant, KEY_LENGTH, MAC_LENGTH>::compute_and_verify(&fixed, &key, message)
458            .expect("one-shot verify failed");
459
460        let split = message.len() / 3;
461        for parts in [
462            vec![message],
463            vec![&message[..split], &message[split..]],
464            vec![
465                &[][..],
466                &message[..1],
467                &message[1..split],
468                &message[split..],
469                &[][..],
470            ],
471        ] {
472            let mut auth = H::<Variant, KEY_LENGTH, MAC_LENGTH>::new(&key);
473            for part in &parts {
474                auth.update(*part);
475            }
476            assert_eq!(auth.finalize_to_vec(), expected);
477
478            let mut verifier = H::<Variant, KEY_LENGTH, MAC_LENGTH>::new(&key);
479            for part in &parts {
480                verifier.update(*part);
481            }
482            verifier.verify(&fixed).expect("incremental verify failed");
483        }
484
485        for index in [0, MAC_LENGTH / 2, MAC_LENGTH - 1] {
486            let mut flipped = fixed.clone();
487            flipped[index] ^= 1;
488            assert!(matches!(
489                H::<Variant, KEY_LENGTH, MAC_LENGTH>::compute_and_verify(&flipped, &key, message),
490                Err(Error::AuthenticationFailed)
491            ));
492            let mut verifier = H::<Variant, KEY_LENGTH, MAC_LENGTH>::new(&key);
493            verifier.update(message);
494            assert!(matches!(
495                verifier.verify(&flipped),
496                Err(Error::AuthenticationFailed)
497            ));
498        }
499
500        let mut wrong_key = key.clone();
501        wrong_key[KEY_LENGTH - 1] ^= 1;
502        assert!(matches!(
503            H::<Variant, KEY_LENGTH, MAC_LENGTH>::compute_and_verify(&fixed, &wrong_key, message),
504            Err(Error::AuthenticationFailed)
505        ));
506        let mut verifier = H::<Variant, KEY_LENGTH, MAC_LENGTH>::new(&key);
507        verifier.update(&message[..message.len() - 1]);
508        assert!(matches!(
509            verifier.verify(&fixed),
510            Err(Error::AuthenticationFailed)
511        ));
512    }
513
514    #[test]
515    fn rfc4231_vectors_through_all_three_variants() {
516        for case in CASES {
517            let sha256 = case.sha256();
518            let sha512 = case.sha512();
519            assert_variant::<
520                HmacSha256Variant,
521                CRYPTO_AUTH_HMACSHA256_KEYBYTES,
522                CRYPTO_AUTH_HMACSHA256_BYTES,
523            >(padded_key(case.key), case.data, &sha256);
524            assert_variant::<
525                HmacSha512Variant,
526                CRYPTO_AUTH_HMACSHA512_KEYBYTES,
527                CRYPTO_AUTH_HMACSHA512_BYTES,
528            >(padded_key(case.key), case.data, &sha512);
529            assert_variant::<
530                HmacSha512256Variant,
531                CRYPTO_AUTH_HMACSHA512256_KEYBYTES,
532                CRYPTO_AUTH_HMACSHA512256_BYTES,
533            >(
534                padded_key(case.key),
535                case.data,
536                &sha512[..CRYPTO_AUTH_HMACSHA512256_BYTES],
537            );
538        }
539    }
540
541    #[test]
542    fn rustaceous_and_classic_macs_verify_each_other() {
543        for case in CASES {
544            let key256: HmacSha256Key = padded_key(case.key);
545            let mac = HmacSha256::compute_to_vec(&key256, case.data);
546            crypto_auth_hmacsha256_verify(
547                mac.as_slice().try_into().expect("MAC length"),
548                case.data,
549                key256.as_array(),
550            )
551            .expect("classic verify");
552            let mut classic = [0u8; CRYPTO_AUTH_HMACSHA256_BYTES];
553            crypto_auth_hmacsha256(&mut classic, case.data, key256.as_array());
554            HmacSha256::compute_and_verify(&classic, &key256, case.data)
555                .expect("rustaceous verify");
556            let mut verifier = HmacSha256::new(&key256);
557            verifier.update(case.data);
558            verifier.verify(&classic).expect("incremental verify");
559
560            let key512: HmacSha512Key = padded_key(case.key);
561            let mac = HmacSha512::compute_to_vec(&key512, case.data);
562            crypto_auth_hmacsha512_verify(
563                mac.as_slice().try_into().expect("MAC length"),
564                case.data,
565                key512.as_array(),
566            )
567            .expect("classic verify");
568            let mut classic = [0u8; CRYPTO_AUTH_HMACSHA512_BYTES];
569            crypto_auth_hmacsha512(&mut classic, case.data, key512.as_array());
570            HmacSha512::compute_and_verify(&classic, &key512, case.data)
571                .expect("rustaceous verify");
572            let mut verifier = HmacSha512::new(&key512);
573            verifier.update(case.data);
574            verifier.verify(&classic).expect("incremental verify");
575
576            let key512256: HmacSha512256Key = padded_key(case.key);
577            let mac = HmacSha512256::compute_to_vec(&key512256, case.data);
578            crypto_auth_hmacsha512256_verify(
579                mac.as_slice().try_into().expect("MAC length"),
580                case.data,
581                key512256.as_array(),
582            )
583            .expect("classic verify");
584            let mut classic = [0u8; CRYPTO_AUTH_HMACSHA512256_BYTES];
585            crypto_auth_hmacsha512256(&mut classic, case.data, key512256.as_array());
586            HmacSha512256::compute_and_verify(&classic, &key512256, case.data)
587                .expect("rustaceous verify");
588            let mut verifier = HmacSha512256::new(&key512256);
589            verifier.update(case.data);
590            verifier.verify(&classic).expect("incremental verify");
591        }
592    }
593
594    #[test]
595    fn variants_are_distinct_and_the_generic_api_matches_the_aliases() {
596        fn compute_with_variant<Variant, const KEY_LENGTH: usize, const MAC_LENGTH: usize>(
597            key: StackByteArray<KEY_LENGTH>,
598            input: &[u8],
599        ) -> Vec<u8>
600        where
601            Variant: HmacVariant<KEY_LENGTH, MAC_LENGTH>,
602        {
603            Hmac::<Variant, KEY_LENGTH, MAC_LENGTH>::compute_to_vec(&key, input)
604        }
605
606        let case = CASES[0];
607        let sha256 = case.sha256();
608        let sha512 = case.sha512();
609        assert_eq!(
610            compute_with_variant::<
611                HmacSha256Variant,
612                CRYPTO_AUTH_HMACSHA256_KEYBYTES,
613                CRYPTO_AUTH_HMACSHA256_BYTES,
614            >(padded_key(case.key), case.data),
615            sha256
616        );
617        assert_eq!(
618            compute_with_variant::<
619                HmacSha512Variant,
620                CRYPTO_AUTH_HMACSHA512_KEYBYTES,
621                CRYPTO_AUTH_HMACSHA512_BYTES,
622            >(padded_key(case.key), case.data),
623            sha512
624        );
625        let truncated = compute_with_variant::<
626            HmacSha512256Variant,
627            CRYPTO_AUTH_HMACSHA512256_KEYBYTES,
628            CRYPTO_AUTH_HMACSHA512256_BYTES,
629        >(padded_key(case.key), case.data);
630        assert_eq!(truncated, &sha512[..CRYPTO_AUTH_HMACSHA512256_BYTES]);
631        // Same key length and MAC length, different hash: the aliases must not
632        // collapse onto one another.
633        assert_ne!(truncated, sha256);
634    }
635
636    #[cfg(dryoc_native_tests)]
637    #[test]
638    fn rfc4231_keys_match_libsodium() {
639        use crate::native_test_util::{auth_hmacsha256, auth_hmacsha512, auth_hmacsha512256};
640
641        for case in CASES {
642            let key: HmacSha256Key = padded_key(case.key);
643            let so_tag = auth_hmacsha256(case.data, key.as_slice());
644            assert_eq!(HmacSha256::compute_to_vec(&key, case.data), so_tag);
645
646            let key: HmacSha512Key = padded_key(case.key);
647            let so_tag = auth_hmacsha512(case.data, key.as_slice());
648            assert_eq!(HmacSha512::compute_to_vec(&key, case.data), so_tag);
649
650            let key: HmacSha512256Key = padded_key(case.key);
651            let so_tag = auth_hmacsha512256(case.data, key.as_slice());
652            assert_eq!(HmacSha512256::compute_to_vec(&key, case.data), so_tag);
653        }
654    }
655}