@@ -205,69 +205,76 @@ pub const HMAC_SHA3_512_NAME: &str = "HMAC-SHA3-512";
205205
206206/*** Type aliases ***/
207207#[ allow( non_camel_case_types) ]
208- pub type HMAC_SHA224 = HMAC < SHA224 > ;
208+ pub type HMAC_SHA224 = HMAC < SHA224 , 64 > ;
209209impl Algorithm for HMAC_SHA224 {
210210 const ALG_NAME : & ' static str = HMAC_SHA224_NAME ;
211211 const MAX_SECURITY_STRENGTH : SecurityStrength = SecurityStrength :: _112bit;
212212}
213213
214214#[ allow( non_camel_case_types) ]
215- pub type HMAC_SHA256 = HMAC < SHA256 > ;
215+ pub type HMAC_SHA256 = HMAC < SHA256 , 64 > ;
216216impl Algorithm for HMAC_SHA256 {
217217 const ALG_NAME : & ' static str = HMAC_SHA256_NAME ;
218218 const MAX_SECURITY_STRENGTH : SecurityStrength = SecurityStrength :: _128bit;
219219}
220220
221221#[ allow( non_camel_case_types) ]
222- pub type HMAC_SHA384 = HMAC < SHA384 > ;
222+ pub type HMAC_SHA384 = HMAC < SHA384 , 128 > ;
223223impl Algorithm for HMAC_SHA384 {
224224 const ALG_NAME : & ' static str = HMAC_SHA384_NAME ;
225225 const MAX_SECURITY_STRENGTH : SecurityStrength = SecurityStrength :: _192bit;
226226}
227227
228228#[ allow( non_camel_case_types) ]
229- pub type HMAC_SHA512 = HMAC < SHA512 > ;
229+ pub type HMAC_SHA512 = HMAC < SHA512 , 128 > ;
230230impl Algorithm for HMAC_SHA512 {
231231 const ALG_NAME : & ' static str = HMAC_SHA512_NAME ;
232232 const MAX_SECURITY_STRENGTH : SecurityStrength = SecurityStrength :: _256bit;
233233}
234234
235235#[ allow( non_camel_case_types) ]
236- pub type HMAC_SHA3_224 = HMAC < SHA3_224 > ;
236+ pub type HMAC_SHA3_224 = HMAC < SHA3_224 , 144 > ;
237237impl Algorithm for HMAC_SHA3_224 {
238238 const ALG_NAME : & ' static str = HMAC_SHA3_224_NAME ;
239239 const MAX_SECURITY_STRENGTH : SecurityStrength = SecurityStrength :: _112bit;
240240}
241241
242242#[ allow( non_camel_case_types) ]
243- pub type HMAC_SHA3_256 = HMAC < SHA3_256 > ;
243+ pub type HMAC_SHA3_256 = HMAC < SHA3_256 , 136 > ;
244244impl Algorithm for HMAC_SHA3_256 {
245245 const ALG_NAME : & ' static str = HMAC_SHA3_256_NAME ;
246246 const MAX_SECURITY_STRENGTH : SecurityStrength = SecurityStrength :: _128bit;
247247}
248248
249249#[ allow( non_camel_case_types) ]
250- pub type HMAC_SHA3_384 = HMAC < SHA3_384 > ;
250+ pub type HMAC_SHA3_384 = HMAC < SHA3_384 , 104 > ;
251251impl Algorithm for HMAC_SHA3_384 {
252252 const ALG_NAME : & ' static str = HMAC_SHA3_384_NAME ;
253253 const MAX_SECURITY_STRENGTH : SecurityStrength = SecurityStrength :: _192bit;
254254}
255255
256256#[ allow( non_camel_case_types) ]
257- pub type HMAC_SHA3_512 = HMAC < SHA3_512 > ;
257+ pub type HMAC_SHA3_512 = HMAC < SHA3_512 , 72 > ;
258258impl Algorithm for HMAC_SHA3_512 {
259259 const ALG_NAME : & ' static str = HMAC_SHA3_512_NAME ;
260260 const MAX_SECURITY_STRENGTH : SecurityStrength = SecurityStrength :: _256bit;
261261}
262262
263- // TODO: is there a rustacious way to extract this from HASH?
264- const LARGEST_HASHER_OUTPUT_LEN : usize = 64 ;
263+ // The internal key buffer must be able to hold a key up to the *block length* of the underlying hash:
264+ // per RFC 2104, a key no longer than the block is used verbatim (only longer keys are pre-hashed down
265+ // to the output length). So the buffer size is a const parameter of the struct, set per hash to its
266+ // block length by the type aliases below. Block lengths (bytes): SHA-224/256 = 64, SHA-384/512 = 128,
267+ // SHA3-224 = 144, SHA3-256 = 136, SHA3-384 = 104, SHA3-512 = 72.
268+ //
269+ // The default is used only when `HMAC<HASH>` is written without an explicit buffer size; it is the
270+ // largest block length across all supported hashes, so it is always large enough.
271+ const LARGEST_HASHER_BLOCK_LEN : usize = 144 ;
265272
266273// HMAC implements RFC 2104.
267274#[ derive( Clone ) ]
268- pub struct HMAC < HASH : Hash + Default > {
275+ pub struct HMAC < HASH : Hash + Default , const KEY_BUF_LEN : usize = LARGEST_HASHER_BLOCK_LEN > {
269276 hasher : HASH ,
270- key : [ u8 ; LARGEST_HASHER_OUTPUT_LEN ] ,
277+ key : [ u8 ; KEY_BUF_LEN ] ,
271278 key_len : usize , // Doing it this way to avoid needing a vec, so that this can be made no_std friendly.
272279}
273280
@@ -286,21 +293,13 @@ const OPAD_BYTE: u8 = 0x5C;
286293// be too strict about it,
287294pub const MIN_FIPS_DIGEST_LEN : usize = 4 ; // 32 / 8;
288295
289- impl < HASH : Hash + Default > HMAC < HASH > {
296+ impl < HASH : Hash + Default , const KEY_BUF_LEN : usize > HMAC < HASH , KEY_BUF_LEN > {
290297 fn pad_key_into_hasher ( & mut self , padding : u8 ) {
291298 // 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.
292299 // TODO: make this no_std-friendly
293300 let mut padded = vec ! [ 0u8 ; self . hasher. block_bitlen( ) / 8 ] ;
294-
295- // Per RFC 2104 Section 2, if the application key exceeds the block
296- // length of the underlying hashes algorithm, we apply a hash invocation
297- // over the key first.
298- // if self.key_len > self.hasher.block_bitlen() / 8 {
299- // HASH::default().hash_out(&self.key[..self.key_len], &mut padded[..self.hasher.output_len()])?;
300- // } else {
301- // TODO: does this need a guard for a key_len longer than the block length?
301+
302302 padded[ ..self . key_len ] . copy_from_slice ( & self . key [ ..self . key_len ] ) ;
303- // }
304303
305304 // XXX: easier way to xor over Vec?
306305 for entry in & mut padded {
@@ -312,9 +311,10 @@ impl<HASH: Hash + Default> HMAC<HASH> {
312311 self . hasher . do_update ( & padded)
313312 }
314313
315- /// Loads the raw key bytes into `self.key` / `self.key_len`, pre-hashing them first if they
316- /// exceed the underlying hash's block length (per RFC 2104 Section 2). This does NOT absorb the
317- /// key into the hasher; that is done separately via [HMAC::pad_key_into_hasher].
314+ /// Per RFC 2104 Section 2, if the application key exceeds the block
315+ /// length of the underlying hashes algorithm, we apply a hash invocation
316+ /// over the key first.
317+ /// This does NOT absorb the key into the hasher; that is done separately via [HMAC::pad_key_into_hasher].
318318 fn load_key_material ( & mut self , key_bytes : & [ u8 ] ) {
319319 if key_bytes. len ( ) > self . hasher . block_bitlen ( ) / 8 {
320320 // then we have to pre-hash it -- use a new instance of the hasher rather than the internal one
@@ -324,6 +324,12 @@ impl<HASH: Hash + Default> HMAC<HASH> {
324324 self . key [ ..key_bytes. len ( ) ] . copy_from_slice ( key_bytes) ;
325325 self . key_len = key_bytes. len ( ) ;
326326 }
327+
328+ // Just as a sanity-check.
329+ assert ! (
330+ self . key_len <= KEY_BUF_LEN ,
331+ "Fatal error: Key length exceeds HMAC internal buffer length"
332+ ) ;
327333 }
328334
329335 /// Private init so that users are forced to go through one of the public new methods and thus we
@@ -390,17 +396,15 @@ impl<HASH: Hash + Default> HMAC<HASH> {
390396// TODO: This is essentially a "batch mode" where you want to perform many MACs or Verifications with the same key
391397// TODO: against different data.
392398
393- impl < HASH : Hash + Default > MAC for HMAC < HASH > {
399+ impl < HASH : Hash + Default , const KEY_BUF_LEN : usize > MAC for HMAC < HASH , KEY_BUF_LEN > {
394400 fn new ( key : & impl KeyMaterialTrait ) -> Result < Self , MACError > {
395- let mut hmac =
396- Self { hasher : HASH :: default ( ) , key : [ 0u8 ; LARGEST_HASHER_OUTPUT_LEN ] , key_len : 0 } ;
401+ let mut hmac = Self { hasher : HASH :: default ( ) , key : [ 0u8 ; KEY_BUF_LEN ] , key_len : 0 } ;
397402 hmac. init ( key, false ) ?;
398403 Ok ( hmac)
399404 }
400405
401406 fn new_allow_weak_key ( key : & impl KeyMaterialTrait ) -> Result < Self , MACError > {
402- let mut hmac =
403- Self { hasher : HASH :: default ( ) , key : [ 0u8 ; LARGEST_HASHER_OUTPUT_LEN ] , key_len : 0 } ;
407+ let mut hmac = Self { hasher : HASH :: default ( ) , key : [ 0u8 ; KEY_BUF_LEN ] , key_len : 0 } ;
404408 hmac. init ( key, true ) ?;
405409 Ok ( hmac)
406410 }
@@ -470,8 +474,11 @@ impl<HASH: Hash + Default> MAC for HMAC<HASH> {
470474/// There is no way to detect a mismatched key on
471475/// resume: the caller MUST supply the same key the HMAC was created with, otherwise the resumed
472476/// operation will silently produce an incorrect MAC.
473- impl < const HASH_STATE_LEN : usize , HASH : Hash + Default + SerializableState < HASH_STATE_LEN > >
474- SerializableKeyedState < HASH_STATE_LEN > for HMAC < HASH >
477+ impl <
478+ const HASH_STATE_LEN : usize ,
479+ const KEY_BUF_LEN : usize ,
480+ HASH : Hash + Default + SerializableState < HASH_STATE_LEN > ,
481+ > SerializableKeyedState < HASH_STATE_LEN > for HMAC < HASH , KEY_BUF_LEN >
475482{
476483 // HMAC accepts any key material, so the key type is the trait object `dyn KeyMaterialTrait`
477484 // rather than a single concrete key type. The key is only used (by reference) to reload the key
@@ -494,7 +501,7 @@ impl<const HASH_STATE_LEN: usize, HASH: Hash + Default + SerializableState<HASH_
494501 // Re-load the key material exactly as `new()` did (pre-hashing an over-length key), but do
495502 // NOT re-absorb `K ⊕ ipad` — the deserialized hasher already contains it. The key is only
496503 // needed for the outer `K ⊕ opad` step at finalization.
497- let mut hmac = HMAC { hasher, key : [ 0u8 ; LARGEST_HASHER_OUTPUT_LEN ] , key_len : 0 } ;
504+ let mut hmac = HMAC { hasher, key : [ 0u8 ; KEY_BUF_LEN ] , key_len : 0 } ;
498505 hmac. load_key_material ( key. ref_to_bytes ( ) ) ;
499506
500507 Ok ( hmac)
0 commit comments