HMAC#

ToDo:

  • Add illustration for HMAC - Similar to this.

  • Add relevant resources at the end.


Checksums provides control for data integrity but not for authentication, that is why they are vulnerable to man in the middle attacks. A similar method that provides both features is called message authentication code (MAC), not to be confused with media access control address (MAC address) used in communications and networking. In this material MAC will always refer to the former.

MACs provides both integrity and authentication using a key. There are different ways to implement this mechanism:

This chapter will cover the usage of HMAC. Block ciphers, in particular AES, will be covered in following chapter.

As with hashes, HMACs are non-reversible and only with the secret key can one produce the same hash for a given message.

Since HMAC is a procedure, it does not rely on any specific hash function. Therefore different hash functions can be used, to specify exactly which, the name of the hash function is usually stated. For instance, if using SHA256 the HMAC will be called HMAC-SHA256 or HSHA256.

HMAC != Hashes#

Hashes were shown to be a method to hide information such as passwords in a secure way, on the contrary, when using HMACs, the message is completely visible / public. The objective of HMACs is not to hide a message but to provide a way to the receipient to verify if the message has been sent by the intended party.

It should be noted that anyone with the secret key can produce messages so the secret key should be exchange by a secure channel beforehand. If someone receives the message and does not have the secret key, they will be able to see the message but they will not be able to verify if its coming for the intended source.

HMAC != Hashes + Salt (+ Pepper)#

HMAC is a method that combines a secret key and the original message in such a way that, if changing the key, the message will also change. At first, it may seem that this key is analogous to a salt or a pepper when hashing but the main difference is that the key is not simply appended/preppended to the message but rather mixed in a cryptographically secure way.

Moreover salts are public whereas HMAC keys are not and pepper could be re-discovered whereas HMAC should not.

HMAC != Checksums#

While both checksums and HMACs provides integrity, the main difference is that checksums has no password/key protection and hence a given input will always have the same output, whereas in HMACs the output depends on both the input and the password/key.

Generating HMAC#

Python has the hmac module as part of the standard library and can be combined with the hash functions used in previous chapters.

As well as with the hashlib it is possible to create HMACs with a single function call or with the builder pattern.

import hmac
import hashlib

Using single Function#

The single function approach will always return a bytes object, if the hex representation is desired, use any of the methods available (see Bytes Appendix).

secret_key = b"my secret"
message = b"Hello World!"

message_hmac_bytes = hmac.digest(secret_key, message, digest=hashlib.sha256)
message_hmac = message_hmac_bytes.hex()
message_hmac
'3262c371784a36377154bdeb0bfbfc6ebf88591a7e564dbb0c4c7ee16c273440'

Using Builder Pattern#

One change in this implementation is that the parameter is called digestmod instead of digest. For the bytes output one can use the digest method and for the hex output the hexdigest method.

secret_key = b"my secret"
message = b"Hello World!"

keyed_hasher = hmac.new(secret_key, digestmod=hashlib.sha256)
keyed_hasher.update(message)
message_hmac = keyed_hasher.hexdigest()
message_hmac
'3262c371784a36377154bdeb0bfbfc6ebf88591a7e564dbb0c4c7ee16c273440'

Verifying a Message#

Note: the hmac.compare_digest is exactly the same as secrets.compare_digest. The latter is an alias of the former.

def verify(received_message, received_hmac, key):
    guess_hmac = hmac.digest(key, received_message, digest=hashlib.sha256).hex()
    
    if not hmac.compare_digest(received_hmac, guess_hmac):
        return "Mismatch, either the message has been modified or key is incorrect"
    
    return "Match, message and the key are consistent"    

Altered Message#

received_message = b"Hello Wrold!"
received_hmac = '3262c371784a36377154bdeb0bfbfc6ebf88591a7e564dbb0c4c7ee16c273440'
guess_key = b"my secret"

print(verify(received_message, received_hmac, guess_key))
Mismatch, either the message has been modified or key is incorrect

Wrong Key#

received_message = b"Hello World!"
received_hmac = '3262c371784a36377154bdeb0bfbfc6ebf88591a7e564dbb0c4c7ee16c273440'
guess_key = b"password"

print(verify(received_message, received_hmac, guess_key))
Mismatch, either the message has been modified or key is incorrect

Correct Message and Key#

received_message = b"Hello World!"
received_hmac = '3262c371784a36377154bdeb0bfbfc6ebf88591a7e564dbb0c4c7ee16c273440'
guess_key = b"my secret"

print(verify(received_message, received_hmac, guess_key))
Match, message and the key are consistent

Conclusion#

HMACs are a sequence of bytes that can only be produced with a given input and a password/key, the input message is generally public and the HMAC serves as means of integrity, without requiring a secure channel. The drawback is that the passwrod/key itself should be delivered by a secure channel to begin with.