Skip to main content

bouncycastle_hmac/
lib.rs

1//! This crate contains an implementation of the Hash-Based Message Authentication Code (HMAC)
2//! as specified in RFC2104, taking into account NIST Implementation Guidance in FIPS 140-2 IG A.8
3//! and NIST SP 800-107-r1.
4//!
5//! # Usage
6//!
7//! The HMAC object (and the [`MAC`] trait in general) is designed in three phases:
8//!
9//! * The initialization phase where you specify the underlying hash function and the key material.
10//! * The update phase where you feed in the content being MAC'd, either in one-shot or in chunks.
11//! * The finalization phase where you either obtain the MAC value or verify an existing MAC value.
12//!
13//! The initialization phase is primarily performed via the [`MAC::new`] function which performs
14//! checks on the provided key to ensure that it is of the correct type [`KeyType::MACKey`] and tagged
15//! at the correct security level for the chosen hash function. In cases where you need to use HMAC
16//! with an intentially week key (such as an all-zero salt), the alternative constructor
17//! [`MAC::new_allow_weak_key`] can be used.
18//!
19//! The update phase supports streaming of the content via the repeated calls to the [`MAC::do_update`] function.
20//! One-shot APIs are provided that combine the update and finalization phases into a single function call.
21//!
22//!
23//! # Examples
24//!
25//! Instantiation of an HMAC object is straightforward:
26//!
27//! ```
28//! use bouncycastle_hmac::HMAC_SHA256;
29//! use bouncycastle_core::traits::MAC;
30//! use bouncycastle_core::key_material::{KeyMaterial256};
31//!
32//! let key: KeyMaterial256 = HMAC_SHA256::keygen().expect("Will only fail if the system RNG can't start up.");
33//!
34//! let hmac = HMAC_SHA256::new(&key).expect(
35//!         "Should succeed because key is long enough and tagged KeyType::MACKey");
36//! ```
37//!
38//! Alternatively, if you have key material from somewhere else, you can create the key manually, like so:
39//! ```
40//! use bouncycastle_hmac::HMAC_SHA256;
41//! use bouncycastle_core::traits::MAC;
42//! use bouncycastle_core::key_material::{KeyMaterial256, KeyType};
43//!
44//! let key = KeyMaterial256::from_bytes_as_type(
45//!             b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
46//!             KeyType::MACKey).unwrap();
47//!
48//! let hmac = HMAC_SHA256::new(&key).expect(
49//!         "Should succeed because key is long enough and tagged KeyType::MACKey");
50//! ```
51//!
52//! ## Computing a MAC
53//! MAC functionality is accessed via the [`MAC`] trait.
54//!
55//! The simplest usage is via the one-shot functions.
56//! ```
57//! use bouncycastle_hmac::HMAC_SHA256;
58//! use bouncycastle_core::traits::MAC;
59//! use bouncycastle_core::key_material::{KeyMaterial256, KeyType};
60//!
61//! let key: KeyMaterial256 = HMAC_SHA256::keygen().expect("Will only fail if the system RNG can't start up.");
62//!
63//! let data: &[u8] = b"Hello, world!";
64//! let hmac = HMAC_SHA256::new(&key).expect("Should succeed because key is long enough and tagged KeyType::MACKey");
65//! let output: Vec<u8> = hmac.mac(data);
66//! ```
67//!
68//! More advanced usage will require creating an HMAC object to hold state between successive calls,
69//! for example if input is received in chunks and not all available at the same time:
70//!
71//! ```
72//! use bouncycastle_core::traits::MAC;
73//! use bouncycastle_hmac::HMAC_SHA256;
74//! use bouncycastle_core::key_material::{KeyMaterial256, KeyType};
75//!
76//! let key: KeyMaterial256 = HMAC_SHA256::keygen().expect("Will only fail if the system RNG can't start up.");
77//!
78//! let mut hmac = HMAC_SHA256::new(&key).expect("Should succeed because key is long enough and tagged KeyType::MACKey");
79//! hmac.do_update(b"Hello,");
80//! hmac.do_update(b" world!");
81//! let output: Vec<u8> = hmac.do_final();
82//! ```
83//!
84//! ## Verifying a MAC
85//! MAC functionality is accessed via the [`MAC`] trait which provides functions for MAC verification.
86//! The built-in verification functions use constant-time comparisons and so are *strongly recommended*
87//! rather than re-computing the MAC value and comparing it yourself.
88//!
89//! The simplest usage is via the one-shot functions.
90//! ```
91//! use bouncycastle_core::key_material::{KeyMaterial256, KeyType};
92//! use bouncycastle_core::traits::MAC;
93//!
94//! // For this example to work, we are hard-coding both the key and the MAC value that it generates
95//! // for this data.
96//! let key = KeyMaterial256::from_bytes_as_type(
97//!             b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
98//!             KeyType::MACKey).unwrap();
99//!
100//! let data: &[u8] = b"Hello, world!";
101//!
102//! // .verify() returns a bool: true if the MAC is valid, false otherwise.
103//! if bouncycastle_hmac::HMAC_SHA256::new(&key).unwrap()
104//!                 .verify(data,
105//!                         b"\xa2\xd1\x2e\xcf\xfc\x41\xba\xf1\x23\xd6\x3e\x44\xfc\x27\x88\x90
106//!                            \x47\xcd\x08\xe7\x05\xd7\x0f\xa3\xb8\xaa\x8a\x5c\x18\x7c\x6c\xa9"
107//!                         )
108//! {
109//!     println!("MAC is valid!");
110//! } else {
111//!     println!("MAC is invalid!");
112//! }
113//! ```
114//!
115//! Similarly, a streaming version is available, which is identical to the streaming interface for
116//! computing a mac value, but calls [`MAC::do_verify_final`] instead of [`MAC::do_final`].
117//!
118//! ```
119//! use bouncycastle_core::key_material::{KeyMaterial256, KeyType};
120//! use bouncycastle_core::traits::MAC;
121//! use bouncycastle_hmac::HMAC_SHA256;
122//!
123//! // For this example to work, we are hard-coding both the key and the MAC value that it generates
124//! // for this data.
125//! let key = KeyMaterial256::from_bytes_as_type(
126//!             b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
127//!             KeyType::MACKey).unwrap();
128//! let mut hmac = HMAC_SHA256::new(&key).unwrap();
129//! hmac.do_update(b"Hello,");
130//! hmac.do_update(b" world!");
131//! if hmac.do_verify_final(b"\xa2\xd1\x2e\xcf\xfc\x41\xba\xf1\x23\xd6\x3e\x44\xfc\x27\x88\x90\x47\xcd\x08\xe7\x05\xd7\x0f\xa3\xb8\xaa\x8a\x5c\x18\x7c\x6c\xa9"
132//!                     )
133//! {
134//!     println!("MAC is valid!");
135//! } else {
136//!     println!("MAC is invalid!");
137//! }
138//! ```
139//!
140//! # Suspending and resuming execution
141//!
142//! When MAC'ing a large message, it can be advantageous to be able to suspend the operation
143//! to a cache and resume it later; for example if waiting for the message to stream over a slow network
144//! connection. For this reason, all HMAC algorithms impl [`SuspendableKeyed`].
145//!
146//! Note that since HMAC is a keyed
147//! algorithm and we do not want to serialize the private key into the state, the trait structure forces you to
148//! re-provide the same key when you resume the operation. Securely storing this key in the interim
149//! is the responsibility of the caller. Note also that if you resume the HMAC with the wrong key,
150//! `from_serialized_state` has no way to detect this, so the end result will be a broken MAC value
151//! computed with different keys in the inner and outer pad. So make sure you resume with the same key!
152//!
153//!```rust
154//! use bouncycastle_hmac::HMAC_SHA256;
155//! use bouncycastle_core::key_material::KeyMaterial256;
156//! use bouncycastle_core::traits::{MAC, SuspendableKeyed};
157//! use bouncycastle_core::key_material::KeyType;
158//!
159//! let msg_part1 = b"The quick brown fox";
160//! let msg_part2 = b" jumped over the lazy dog";
161//!
162//! let key = KeyMaterial256::from_bytes_as_type(
163//!             b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
164//!             KeyType::MACKey).unwrap();
165//!
166//! let mut hmac = HMAC_SHA256::new(&key).unwrap();
167//! hmac.do_update(msg_part1);
168//!
169//! // suspend the in-progress mac (the key is NOT included in the serialized state)
170//! let serialized_state = hmac.suspend();
171//!
172//! // ...
173//! // do other things in the meantime
174//! // ...
175//!
176//! // ... later, possibly on another host: resume from the serialized state by re-supplying
177//! // the same salt (make sure you store it securely!).
178//! let mut hmac_resumed = HMAC_SHA256::from_suspended(serialized_state, &key).unwrap();
179//! hmac_resumed.do_update(msg_part2);
180//! let h: Vec<u8> = hmac_resumed.do_final();
181//! ```
182
183#![forbid(unsafe_code)]
184#![forbid(missing_docs)]
185
186use bouncycastle_core::errors::{KeyMaterialError, MACError, RNGError, SuspendableError};
187use bouncycastle_core::key_material::{KeyMaterial, KeyMaterialTrait, KeyType};
188use bouncycastle_core::traits::{
189    Algorithm, AlgorithmOID, Hash, MAC, RNG, SecurityStrength, Suspendable, SuspendableKeyed,
190};
191use bouncycastle_rng::{HashDRBG_SHA256, HashDRBG_SHA512};
192use bouncycastle_sha2::{
193    SHA224, SHA256, SHA384, SHA512, SUSPENDED_SHA256_STATE_LEN, SUSPENDED_SHA512_STATE_LEN,
194};
195use bouncycastle_sha3::{SHA3_224, SHA3_256, SHA3_384, SHA3_512, SUSPENDED_SHA3_STATE_LEN};
196use bouncycastle_utils::{ct, secret::Secret};
197use core::fmt::{Debug, Display, Formatter};
198
199/*** String constants ***/
200///
201pub const HMAC_SHA224_NAME: &str = "HMAC-SHA224";
202///
203pub const HMAC_SHA256_NAME: &str = "HMAC-SHA256";
204///
205pub const HMAC_SHA384_NAME: &str = "HMAC-SHA384";
206///
207pub const HMAC_SHA512_NAME: &str = "HMAC-SHA512";
208///
209pub const HMAC_SHA3_224_NAME: &str = "HMAC-SHA3-224";
210///
211pub const HMAC_SHA3_256_NAME: &str = "HMAC-SHA3-256";
212///
213pub const HMAC_SHA3_384_NAME: &str = "HMAC-SHA3-384";
214///
215pub const HMAC_SHA3_512_NAME: &str = "HMAC-SHA3-512";
216
217/*** Type aliases ***/
218/// Public type for HMAC using SHA224.
219#[allow(non_camel_case_types)]
220pub type HMAC_SHA224 = HMAC<SHA224, 64>;
221impl Algorithm for HMAC_SHA224 {
222    const ALG_NAME: &'static str = HMAC_SHA224_NAME;
223    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_112bit;
224}
225/// Defined in RFC 4231: id-hmacWithSHA224 { digestAlgorithm 8 }
226impl AlgorithmOID for HMAC_SHA224 {
227    const OID: &'static [u32] = &[1, 2, 840, 113549, 2, 8];
228    const OID_DER: &'static [u8] = &[0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x08];
229}
230
231/// Public type for HKDF using SHA256.
232#[allow(non_camel_case_types)]
233pub type HMAC_SHA256 = HMAC<SHA256, 64>;
234impl Algorithm for HMAC_SHA256 {
235    const ALG_NAME: &'static str = HMAC_SHA256_NAME;
236    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_128bit;
237}
238/// Defined in RFC 4231: id-hmacWithSHA256 { digestAlgorithm 9 }
239impl AlgorithmOID for HMAC_SHA256 {
240    const OID: &'static [u32] = &[1, 2, 840, 113549, 2, 9];
241    const OID_DER: &'static [u8] = &[0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x09];
242}
243
244/// Public type for HKDF using SHA384.
245#[allow(non_camel_case_types)]
246pub type HMAC_SHA384 = HMAC<SHA384, 128>;
247impl Algorithm for HMAC_SHA384 {
248    const ALG_NAME: &'static str = HMAC_SHA384_NAME;
249    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_192bit;
250}
251/// Defined in RFC 4231: id-hmacWithSHA384 { digestAlgorithm 10 }
252impl AlgorithmOID for HMAC_SHA384 {
253    const OID: &'static [u32] = &[1, 2, 840, 113549, 2, 10];
254    const OID_DER: &'static [u8] = &[0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x0a];
255}
256
257/// Public type for HKDF using SHA512.
258#[allow(non_camel_case_types)]
259pub type HMAC_SHA512 = HMAC<SHA512, 128>;
260impl Algorithm for HMAC_SHA512 {
261    const ALG_NAME: &'static str = HMAC_SHA512_NAME;
262    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit;
263}
264/// Defined in RFC 4231: id-hmacWithSHA512 { digestAlgorithm 11 }
265impl AlgorithmOID for HMAC_SHA512 {
266    const OID: &'static [u32] = &[1, 2, 840, 113549, 2, 11];
267    const OID_DER: &'static [u8] = &[0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x0b];
268}
269
270/// Public type for HKDF using SHA3_224.
271#[allow(non_camel_case_types)]
272pub type HMAC_SHA3_224 = HMAC<SHA3_224, 144>;
273impl Algorithm for HMAC_SHA3_224 {
274    const ALG_NAME: &'static str = HMAC_SHA3_224_NAME;
275    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_112bit;
276}
277/// Assigned by NIST in the Computer Security Objects Register: id-hmacWithSHA3-224 { hashAlgs 13 }
278impl AlgorithmOID for HMAC_SHA3_224 {
279    const OID: &'static [u32] = &[2, 16, 840, 1, 101, 3, 4, 2, 13];
280    const OID_DER: &'static [u8] =
281        &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0d];
282}
283
284/// Public type for HKDF using SHA3_256.
285#[allow(non_camel_case_types)]
286pub type HMAC_SHA3_256 = HMAC<SHA3_256, 136>;
287impl Algorithm for HMAC_SHA3_256 {
288    const ALG_NAME: &'static str = HMAC_SHA3_256_NAME;
289    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_128bit;
290}
291/// Assigned by NIST in the Computer Security Objects Register: id-hmacWithSHA3-256 { hashAlgs 14 }
292impl AlgorithmOID for HMAC_SHA3_256 {
293    const OID: &'static [u32] = &[2, 16, 840, 1, 101, 3, 4, 2, 14];
294    const OID_DER: &'static [u8] =
295        &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0e];
296}
297
298/// Public type for HKDF using SHA3_384.
299#[allow(non_camel_case_types)]
300pub type HMAC_SHA3_384 = HMAC<SHA3_384, 104>;
301impl Algorithm for HMAC_SHA3_384 {
302    const ALG_NAME: &'static str = HMAC_SHA3_384_NAME;
303    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_192bit;
304}
305/// Assigned by NIST in the Computer Security Objects Register: id-hmacWithSHA3-384 { hashAlgs 15 }
306impl AlgorithmOID for HMAC_SHA3_384 {
307    const OID: &'static [u32] = &[2, 16, 840, 1, 101, 3, 4, 2, 15];
308    const OID_DER: &'static [u8] =
309        &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0f];
310}
311
312/// Public type for HKDF using SHA3_512.
313#[allow(non_camel_case_types)]
314pub type HMAC_SHA3_512 = HMAC<SHA3_512, 72>;
315impl Algorithm for HMAC_SHA3_512 {
316    const ALG_NAME: &'static str = HMAC_SHA3_512_NAME;
317    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit;
318}
319/// Assigned by NIST in the Computer Security Objects Register: id-hmacWithSHA3-512 { hashAlgs 16 }
320impl AlgorithmOID for HMAC_SHA3_512 {
321    const OID: &'static [u32] = &[2, 16, 840, 1, 101, 3, 4, 2, 16];
322    const OID_DER: &'static [u8] =
323        &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x10];
324}
325
326// The internal key buffer must be able to hold a key up to the *block length* of the underlying hash:
327// per RFC 2104, a key no longer than the block is used verbatim (only longer keys are pre-hashed down
328// to the output length). So the buffer size is a const parameter of the struct, set per hash to its
329// block length by the type aliases below. Block lengths (bytes): SHA-224/256 = 64, SHA-384/512 = 128,
330// SHA3-224 = 144, SHA3-256 = 136, SHA3-384 = 104, SHA3-512 = 72.
331//
332// The default is used only when `HMAC<HASH>` is written without an explicit buffer size; it is the
333// largest block length across all supported hashes, so it is always large enough.
334const LARGEST_HASHER_BLOCK_LEN: usize = 144;
335
336/// Internal struct for HKDF.
337/// HMAC implements RFC 2104.
338/// Can, in theory, be instantiated with hash functions other than the ones provided by this crate (even custom ones).
339#[derive(Clone)]
340pub struct HMAC<HASH: Hash + Default, const KEY_BUF_LEN: usize = LARGEST_HASHER_BLOCK_LEN> {
341    hasher: HASH,
342    // todo: once rust stable merges generic_const_exprs, we can remove this hack and delete the KEY_BUF_LEN param.
343    // key: [u8; HASH::OUTPUT_LEN];
344    key: Secret<[u8; KEY_BUF_LEN]>,
345    key_len: Secret<usize>, // Doing it this way to avoid needing a vec, so that this can be made no_std friendly.
346}
347
348impl<HASH: Hash + Default, const KEY_BUF_LEN: usize> Debug for HMAC<HASH, KEY_BUF_LEN> {
349    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
350        write!(f, "HMAC-{} instance", HASH::ALG_NAME,)
351    }
352}
353
354impl<HASH: Hash + Default, const KEY_BUF_LEN: usize> Display for HMAC<HASH, KEY_BUF_LEN> {
355    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
356        write!(f, "HMAC-{} instance", HASH::ALG_NAME,)
357    }
358}
359
360// See definitions in RFC 2104 Section 2.
361const IPAD_BYTE: u8 = 0x36;
362const OPAD_BYTE: u8 = 0x5C;
363
364/// Per FIPS 140-2 IG A.8 Use of a truncated HMAC (matching NIST SP 800-107-r1
365/// Section 5.3.3. Truncation of HMAC), says that the minimum truncation of a
366/// HMAC for tagging should be 32 bits; this exceeds the lower bound set by
367/// IETF RFC 2104 Section 5 Truncated output, which sets the lower bound to be
368/// half of the hash's length and no fewer than 80 bits.
369///
370/// However, as we feel there should be a minimum limit (and have an author
371/// work around this via explicit truncation manually afterwards), but not
372/// be too strict about it,
373/// = 32 bits / 8 = 4 bytes;
374pub const MIN_FIPS_DIGEST_LEN: usize = 4;
375
376impl<HASH: Hash + Default, const KEY_BUF_LEN: usize> HMAC<HASH, KEY_BUF_LEN> {
377    fn pad_key_into_hasher(&mut self, padding: u8) {
378        // TODO: it would be nice to be able to statically extract the length of HASH and not need a Vec or over-sized array here.
379        // TODO: make this no_std-friendly
380        let mut padded = vec![0u8; self.hasher.block_bitlen() / 8];
381
382        padded[..*self.key_len].copy_from_slice(&self.key[..*self.key_len]);
383
384        // XXX: easier way to xor over Vec?
385        for entry in &mut padded {
386            *entry ^= padding;
387        }
388
389        // Per RFC 2104 Section 2, write the padded key into the stream prior
390        // to any other data.
391        self.hasher.do_update(&padded)
392    }
393
394    /// Per RFC 2104 Section 2, if the application key exceeds the block
395    /// length of the underlying hashes algorithm, we apply a hash invocation
396    /// over the key first.
397    /// This does NOT absorb the key into the hasher; that is done separately via [`HMAC::pad_key_into_hasher`].
398    fn load_key_material(&mut self, key_bytes: &[u8]) {
399        if key_bytes.len() > self.hasher.block_bitlen() / 8 {
400            // then we have to pre-hash it -- use a new instance of the hasher rather than the internal one
401            HASH::default().hash_out(key_bytes, &mut self.key[..self.hasher.output_len()]);
402            *self.key_len = self.hasher.output_len();
403        } else {
404            self.key[..key_bytes.len()].copy_from_slice(key_bytes);
405            *self.key_len = key_bytes.len();
406        }
407
408        // Just as a sanity-check.
409        assert!(
410            *self.key_len <= KEY_BUF_LEN,
411            "Fatal error: Key length exceeds HMAC internal buffer length"
412        );
413    }
414
415    /// Private init so that users are forced to go through one of the public new methods and thus we
416    /// don't need to track state errors.
417    fn init(&mut self, key: &impl KeyMaterialTrait, allow_weak_keys: bool) -> Result<(), MACError> {
418        // check that the key is of type KeyMaterial::MACKey
419        // Make an exception for all-zero keys, which is allowed (which can be zero-length or non-zero-length,
420        // because it's just a nuisance to force users to set KeyType::MACKey for an all-zero key.
421        if !(key.key_type() == KeyType::Zeroized || key.key_type() == KeyType::MACKey) {
422            return Err(MACError::KeyMaterialError(KeyMaterialError::InvalidKeyType(
423                "Key type must be a MAC key.",
424            )));
425        }
426
427        // import the key material as bytes.
428        // Per RFC 2104 Section 2, if the application key exceeds the block
429        // length of the underlying hashes algorithm, we apply a hash invocation
430        // over the key first.
431
432        self.load_key_material(key.ref_to_bytes());
433
434        self.pad_key_into_hasher(IPAD_BYTE);
435
436        // check that the key had enough security level
437        if !allow_weak_keys && key.security_strength() < HASH::default().max_security_strength() {
438            Err(KeyMaterialError::SecurityStrength(
439                "HMAC::init(): provided key has a lower security strength than the instantiated HMAC",
440            ))?
441        } else {
442            Ok(())
443        }
444    }
445
446    /// the out buffer can be oversized, but not less than the MIN_FIPS_DIGEST_LENGTH
447    /// Returns the number of bytes written.
448    fn do_final_internal_out(mut self, out: &mut [u8]) -> Result<usize, MACError> {
449        if out.len() < MIN_FIPS_DIGEST_LEN {
450            return Err(MACError::InvalidLength(
451                "HMAC truncation too short for FIPS 140-2 guidelines",
452            ));
453        }
454
455        out.fill(0);
456
457        // Per RFC 2104 Section 2, save our inner digest to calculate our
458        // outer digest. Note that we can't (necessarily) reuse out as a
459        // scratch pad here: if we're truncating the output but not
460        // truncating the underlying hashes, we'd lose bytes and compute an
461        // invalid outer hashes.
462        // TODO: rework this to be no_std friendly (ie no vec!)
463        let mut ihash = vec![0u8; self.hasher.output_len()];
464        // `HMAC` implements `Drop` (required by `Secret`), so we cannot move `self.hasher` out
465        // directly. Swap in a fresh default and consume the taken-out hasher instead.
466        core::mem::take(&mut self.hasher).do_final_out(&mut ihash);
467
468        // ohash
469        self.hasher = HASH::default();
470        self.pad_key_into_hasher(OPAD_BYTE);
471        self.hasher.do_update(&ihash);
472        Ok(core::mem::take(&mut self.hasher).do_final_out(out))
473    }
474}
475
476// TODO: potential feature: add an interface that pre-computes the intermediate values (K XOR ipad) and (K XOR opad)
477// TODO for a given key as described in RFC2104 section 4.
478// TODO: This is essentially a "batch mode" where you want to perform many MACs or Verifications with the same key
479// TODO: against different data.
480
481impl<HASH: Hash + Default, const KEY_BUF_LEN: usize> MAC for HMAC<HASH, KEY_BUF_LEN> {
482    fn new(key: &impl KeyMaterialTrait) -> Result<Self, MACError> {
483        let mut hmac = Self { hasher: HASH::default(), key: Secret::new(), key_len: Secret::new() };
484        hmac.init(key, false)?;
485        Ok(hmac)
486    }
487
488    fn new_allow_weak_key(key: &impl KeyMaterialTrait) -> Result<Self, MACError> {
489        let mut hmac = Self { hasher: HASH::default(), key: Secret::new(), key_len: Secret::new() };
490        hmac.init(key, true)?;
491        Ok(hmac)
492    }
493
494    fn output_len(&self) -> usize {
495        self.hasher.output_len()
496    }
497
498    fn mac(self, data: &[u8]) -> Vec<u8> {
499        let mut out = vec![0_u8; self.hasher.output_len()];
500        let bytes_written = self.mac_out(data, &mut out).expect("HMAC::mac(): should not have failed because we gave it a sufficiently large output buffer to meet FIPS rules.");
501        out[..bytes_written].to_vec()
502    }
503
504    fn mac_out(mut self, data: &[u8], mut out: &mut [u8]) -> Result<usize, MACError> {
505        out.fill(0);
506
507        self.do_update(data);
508        self.do_final_out(&mut out)
509    }
510
511    fn verify(mut self, data: &[u8], mac: &[u8]) -> bool {
512        self.do_update(data);
513        self.do_verify_final(mac)
514    }
515
516    fn do_update(&mut self, data: &[u8]) {
517        self.hasher.do_update(data)
518    }
519
520    fn do_final(self) -> Vec<u8> {
521        let mut out = vec![0_u8; self.hasher.output_len()];
522        self.do_final_internal_out(&mut out).expect("HMAC::do_final(): should not have failed because we gave it a sufficiently large output buffer to meet FIPS rules.");
523        out
524    }
525
526    fn do_final_out(self, mut out: &mut [u8]) -> Result<usize, MACError> {
527        out.fill(0);
528
529        self.do_final_internal_out(&mut out)
530    }
531
532    fn do_verify_final(self, mac: &[u8]) -> bool {
533        let mut out = vec![0_u8; HASH::default().output_len()];
534        let output_len = self.do_final_internal_out(&mut out).expect("HMAC::do_final(): should not have failed because we gave it a sufficiently large output buffer to meet FIPS rules.");
535        if mac.len() != output_len {
536            return false;
537        }
538        ct::ct_eq_bytes(mac, &out[..output_len])
539    }
540
541    fn max_security_strength(&self) -> SecurityStrength {
542        HASH::default().max_security_strength()
543    }
544}
545
546/* SerializedState */
547
548/*** Serialized-state length constants ***/
549/// Length in bytes of the serialized state of [`HMAC_SHA224`].
550pub const SUSPENDED_HMAC_SHA224_STATE_LEN: usize = SUSPENDED_SHA256_STATE_LEN;
551/// Length in bytes of the serialized state of [`HMAC_SHA256`].
552pub const SUSPENDED_HMAC_SHA256_STATE_LEN: usize = SUSPENDED_SHA256_STATE_LEN;
553/// Length in bytes of the serialized state of [`HMAC_SHA384`].
554pub const SUSPENDED_HMAC_SHA384_STATE_LEN: usize = SUSPENDED_SHA512_STATE_LEN;
555/// Length in bytes of the serialized state of [`HMAC_SHA512`].
556pub const SUSPENDED_HMAC_SHA512_STATE_LEN: usize = SUSPENDED_SHA512_STATE_LEN;
557/// Length in bytes of the serialized state of [`HMAC_SHA3_224`].
558pub const SUSPENDED_HMAC_SHA3_224_STATE_LEN: usize = SUSPENDED_SHA3_STATE_LEN;
559/// Length in bytes of the serialized state of [`HMAC_SHA3_256`].
560pub const SUSPENDED_HMAC_SHA3_256_STATE_LEN: usize = SUSPENDED_SHA3_STATE_LEN;
561/// Length in bytes of the serialized state of [`HMAC_SHA3_384`].
562pub const SUSPENDED_HMAC_SHA3_384_STATE_LEN: usize = SUSPENDED_SHA3_STATE_LEN;
563/// Length in bytes of the serialized state of [`HMAC_SHA3_512`].
564pub const SUSPENDED_HMAC_SHA3_512_STATE_LEN: usize = SUSPENDED_SHA3_STATE_LEN;
565
566/// HMAC is a keyed algorithm, so it implements [`SuspendableKeyed`] (rather than
567/// [`Suspendable`]) for suspending and resuming in-progress operations.
568/// The key is deliberately NOT written into the serialized
569/// bytes and must be re-supplied at deserialization.
570///
571/// The serialized state is exactly the inner hasher's state (which has already absorbed `K ⊕ ipad`
572/// and any message chunks provided so far) — so this is a straight passthrough to the underlying hash's
573/// [`Suspendable`] impl. The re-supplied key is needed to reconstruct the material for the outer
574/// (`K ⊕ opad`) step at finalization.
575///
576/// There is no way to detect a mismatched key on
577/// resume: the caller MUST supply the same key the HMAC was created with, otherwise the resumed
578/// operation will silently produce an incorrect MAC.
579impl<
580    const HASH_STATE_LEN: usize,
581    const KEY_BUF_LEN: usize,
582    HASH: Hash + Default + Suspendable<HASH_STATE_LEN>,
583> SuspendableKeyed<HASH_STATE_LEN> for HMAC<HASH, KEY_BUF_LEN>
584{
585    // HMAC accepts any key material, so the key type is the trait object `dyn KeyMaterialTrait`
586    // rather than a single concrete key type. The key is only used (by reference) to reload the key
587    // bytes at from_serialized_state, so dynamic dispatch here is negligible.
588    type Key = dyn KeyMaterialTrait;
589
590    fn suspend(mut self) -> [u8; HASH_STATE_LEN] {
591        // The key is intentionally excluded; the resumable state is just the inner hasher, which
592        // already carries the library version header from the hash's own SerializableState impl.
593        // `HMAC` implements `Drop` (required by `Secret`), so move the hasher out via `mem::take`
594        // rather than a direct partial move.
595        core::mem::take(&mut self.hasher).suspend()
596    }
597
598    fn from_suspended(
599        state: [u8; HASH_STATE_LEN],
600        key: &Self::Key,
601    ) -> Result<Self, SuspendableError> {
602        // Rebuild the inner hasher (version-compatibility is validated by the hash's impl).
603        let hasher = HASH::from_suspended(state)?;
604
605        // Re-load the key material exactly as `new()` did (pre-hashing an over-length key), but do
606        // NOT re-absorb `K ⊕ ipad` — the deserialized hasher already contains it. The key is only
607        // needed for the outer `K ⊕ opad` step at finalization.
608        let mut hmac = HMAC { hasher, key: Secret::new(), key_len: Secret::new() };
609        hmac.load_key_material(key.ref_to_bytes());
610
611        Ok(hmac)
612    }
613}
614
615/* KeyGen functions */
616
617// These need to be separate intrinsic impl's because there isn't a way to statically-type the
618// KeyMaterial<N> based on the HASH type.
619// Using a macro to cut down on code duplication.
620// todo: once rust supports const generics, we can remove the need for this macro and impl this directly
621//      on HMAC as `-> KeyMetarial<H::OUTPUT_LEN>`
622macro_rules! impl_hmac_keygen {
623    ($hash:ty, $block_len:literal, $n:literal, $drbg:ty) => {
624        impl HMAC<$hash, $block_len> {
625            /// Generate a key of the appropriate length for the given HMAC
626            pub fn keygen() -> Result<KeyMaterial<$n>, RNGError> {
627                let mut key = KeyMaterial::<$n>::new();
628                let mut os_rng = <$drbg>::new_from_os();
629                os_rng.fill_keymaterial_out(&mut key)?;
630                key.set_key_type(KeyType::MACKey)?;
631                Ok(key)
632            }
633        }
634    };
635}
636
637impl_hmac_keygen!(SHA224, 64, 28, HashDRBG_SHA256);
638impl_hmac_keygen!(SHA256, 64, 32, HashDRBG_SHA256);
639impl_hmac_keygen!(SHA384, 128, 48, HashDRBG_SHA512);
640impl_hmac_keygen!(SHA512, 128, 64, HashDRBG_SHA512);
641impl_hmac_keygen!(SHA3_224, 144, 28, HashDRBG_SHA256);
642impl_hmac_keygen!(SHA3_256, 136, 32, HashDRBG_SHA256);
643impl_hmac_keygen!(SHA3_384, 104, 48, HashDRBG_SHA512);
644impl_hmac_keygen!(SHA3_512, 72, 64, HashDRBG_SHA512);