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);