Demonstrating HMAC SHA256 keysize limits with openssl

2021-12-22

This is something i never thought i’d need to know or care to find out: the maximum size of an HMAC SHA256 key is capped at 64bytes.

Anything larger than that key is hashed back to 64 bytes; anything smaller is padded.

This is actually just documentation right here in the wiki page for HMAC and you can see it in language bindings like

	if len(key) > blocksize {
		// If key is too big, hash it.
		hm.outer.Write(key)
		key = hm.outer.Sum(nil)
	}

or in dotnet

The secret key for HMACSHA256 encryption. The key can be any length. However, the recommended size is 64 bytes. If the key is more than 64 bytes long, it is hashed (using SHA-256) to derive a 64-byte key. If it is less than 64 bytes long, it is padded to 64 bytes.

Can you show me this is the case?

sure, for academic reasons i’ll just use openssl and a bit of inference.

64bytes

Lets pick some sample text and generate a key of exactly 64bytes and calculate its mac

echo -n "foo" > data.txt

# 64 bytes (hex)
# export RAW_KEY=`openssl rand -hex 32`
export RAW_KEY=e2c6c78e079ca23ac0d37fbbc0ae36a2d5c0f0c7186e70fbd6a964e60444a0de

openssl dgst -sha256 -mac hmac -macopt key:$RAW_KEY data.txt
   HMAC-SHA256(data.txt)= 941b8178bd275e41f98a372679ef066220ff5e0ed470633390038f9ef49d1233

Now just hash that and recalculate the mac….you’ll see its different…thats totally reasonable: the secret key is different

HASHED_HEX_RAW_KEY=`echo -n $RAW_KEY | openssl dgst -sha256 |  awk '{print $2}'` 

openssl dgst -sha256 -mac hmac -macopt hexkey:$HASHED_HEX_RAW_KEY data.txt
   HMAC-SHA256(data.txt)= 388d4c0d8d87da9aa332f0dd2f25d40239df692d150855db65d5b4e77f4c5d6a

ok, what does that prove? nothing but see what happens when try 65bytes

65bytes

Create a key of 65bytes (well…66) and calculate mac

# 65 bytes
# export RAW_KEY=`openssl rand -hex 33`
export RAW_KEY=892982ea016645e04b0436ce49221e4e16e7ff250ce1c93210bf9d5c504da845ae

openssl dgst -sha256 -mac hmac -macopt key:$RAW_KEY data.txt
   HMAC-SHA256(data.txt)= 3fd1fc902c63fd3017d113b9e0c9d7918439a30bc9409d8605caad7b2b3a9c7f

Now hash the key and look at the calculated mac

HASHED_HEX_RAW_KEY=`echo -n $RAW_KEY | openssl dgst -sha256 |  awk '{print $2}'` 

openssl dgst -sha256 -mac hmac -macopt hexkey:$HASHED_HEX_RAW_KEY data.txt
   HMAC-SHA256(data.txt)= 3fd1fc902c63fd3017d113b9e0c9d7918439a30bc9409d8605caad7b2b3a9c7f

Wow!..they’re the same…openssl must be using the hashed key internally for keysize>64bytes

this isn’t precisely Q.E.D but its sufficient for me…(yes, thats the first time i got to use that since boarding school math class)

Why do i care?

Now…why do you even care to know this…

Well, i found out that Google Cloud KMS now support HMAC key import.

What i wanted to do is to import an AWS Secret Access Key into Cloud KMS.

Once imported, i could securely use GCP KMS to access AWS Resources.

How? well, its a trick i used to to make AWS HMAC signing work with Trusted Platform Modules and PKCS-11 devices.

Essentially, you’re saving the aws secret into hardware and making the hardware help create the AWS signed request. IMO, it think thats neat if i have to say so!!

For more info see

Coming back to the limits, it took me a long to notice that the HMAC key import limit for Google KMS is…32bytes. period.

Symmetric keys for signing (MAC keys) must have length equal to the output length of the cryptographic hash function being used (e.g. HMAC-SHA256 keys must have a length of 32 bytes), and must not be encoded. If your key is hex-encoded or base-64 encoded, you must decode it before attempting to import it.

and my AWS key was…44bytes which is still pefectly valid.

  • AWS SECRET with HMAC Prefix:
44 bytes (AWS4HMrL6cNwJCNQzRX8R33mto/jiKM/6AO123456789) 

meaning…i’m stuck now due to a support in GCP KMS :(.

My usecase is academic (i just wanted to see this work for security reasons)…if you (as a reader, maybe customer) need this, please contact your google support teams!



ref:

Just a sample in go for hmac-sha256 to compare with the openssl commands above

  • main.go
package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"fmt"
	"io"
)

func main() {
	h := hmac.New(sha256.New, []byte("892982ea016645e04b0436ce49221e4e16e7ff250ce1c93210bf9d5c504da845ae"))
	io.WriteString(h, "foo")
	fmt.Printf("%x", h.Sum(nil))
}

This site supports webmentions. Send me a mention via this form.