I Know HMAC, But What’s CMAC?

With public-key encryption, we can create a signature where we sign with the private key, and then prove with the public key. Typical…

Photo by Nerene Grobler on Unsplash

I Know HMAC, But What’s CMAC?

With public-key encryption, we can create a signature where we sign with the private key, and then prove with the public key. Typical methods used are ECDSA and EdDSA. But this is rather heavyweight for signing messages that go back and forth between users. So one method is to use HMAC, and where the users have a shared secret key. This might be pre-arranged and long-term, or could be a short-term session key that they use for just one session. Using this shared key will then protect against a session hi-jack. So let’s look at the most common method (HMAC) and at an alternative (CMAC).

HMAC (hash-based message authentication code)

HMAC (hash-based message authentication code) supports the usage of a key to hash data. This key is kept secret between Bob and Alice, and can be used to authentication both the data and that the sender still knows the secret. Overall HMAC can be used with a range of different hashing methods, such as MD5, SHA-1, SHA-256 (SHA-2) and SHA-3:

One of the most useful library for crypto primitives in Python is called cryptography. It contain both “secure” primitives and a “hazmat” layer. Overall the “hazmat” layer should be treated with care, as it could contain security vulnerabilities. In this case we will implement a number of HMAC primitives with the “hazmat” layer.

from cryptography.hazmat.primitives import hashes, hmac
import binascii
import sys
st = "The quick brown fox jumps over the lazy dog"
hex=False
showhex="No"
k="key"
def show_hmac(name,type,data,key):
digest = hmac.HMAC(key, type)
digest.update(data)
res=digest.finalize()
hex=binascii.b2a_hex(res).decode()
b64=binascii.b2a_base64(res).decode()
print (f"HMAC-{name}: {hex} {b64}")
if (len(sys.argv)>1):
st=str(sys.argv[1])
if (len(sys.argv)>2):
showhex=str(sys.argv[2])
if (len(sys.argv)>3):
k=str(sys.argv[3])
if (showhex=="yes"): hex=True
try:
if (hex==True): data = binascii.a2b_hex(st)
else: data=st.encode()
	if (hex==True): key = binascii.a2b_hex(k)
else: key=k.encode()
	print ("Data: ",st)
print (" Hex: ",binascii.b2a_hex(data).decode())
print ("Key: ",k)
print (" Hex: ",binascii.b2a_hex(key).decode())
print()
	show_hmac("Blake2p (64 bytes)",hashes.BLAKE2b(64),data,key)
show_hmac("Blake2s (32 bytes)",hashes.BLAKE2s(32),data,key)
show_hmac("MD5",hashes.MD5(),data,key)
show_hmac("SHA1",hashes.SHA1(),data,key)
show_hmac("SHA224",hashes.SHA224(),data,key)
show_hmac("SHA256",hashes.SHA256(),data,key)
show_hmac("SHA384",hashes.SHA384(),data,key)
show_hmac("SHA3_224",hashes.SHA3_224(),data,key)
show_hmac("SHA3_256",hashes.SHA3_256(),data,key)
show_hmac("SHA3_384",hashes.SHA3_384(),data,key)
show_hmac("SHA3_512",hashes.SHA3_512(),data,key)
show_hmac("SHA512",hashes.SHA512(),data,key)
show_hmac("SHA512_224",hashes.SHA512_224(),data,key)
show_hmac("SHA512_256",hashes.SHA512_256(),data,key)

except Exception as e:
print(e)

A sample run:

Data:  The quick brown fox jumps over the lazy dog
Hex: 54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67
Key: key
Hex: 6b6579
HMAC-Blake2p (64 bytes): 92294f92c0dfb9b00ec9ae8bd94d7e7d8a036b885a499f149dfe2fd2199394aaaf6b8894a1730cccb2cd050f9bcf5062a38b51b0dab33207f8ef35ae2c9df51b kilPksDfubAOya6L2U1+fYoDa4haSZ8Unf4v0hmTlKqva4iUoXMMzLLNBQ+bz1Bio4tRsNqzMgf47zWuLJ31Gw==
HMAC-Blake2s (32 bytes): f93215bb90d4af4c3061cd932fb169fb8bb8a91d0b4022baea1271e1323cd9a0 +TIVu5DUr0wwYc2TL7Fp+4u4qR0LQCK66hJx4TI82aA=
HMAC-MD5: 80070713463e7749b90c2dc24911e275 gAcHE0Y+d0m5DC3CSRHidQ==
HMAC-SHA1: de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9 3nybhbi3iqa8ino29wqQcBydtNk=
HMAC-SHA224: 88ff8b54675d39b8f72322e65ff945c52d96379988ada25639747e69 iP+LVGddObj3IyLmX/lFxS2WN5mIraJWOXR+aQ==
HMAC-SHA256: f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8 97yD9DBThCSxMpjmqm+xQ+9NWaFJRhdZl0edvC0aPNg=
HMAC-SHA384: d7f4727e2c0b39ae0f1e40cc96f60242d5b7801841cea6fc592c5d3e1ae50700582a96cf35e1e554995fe4e03381c237 1/RyfiwLOa4PHkDMlvYCQtW3gBhBzqb8WSxdPhrlBwBYKpbPNeHlVJlf5OAzgcI3
HMAC-SHA3_224: ff6fa8447ce10fb1efdccfe62caf8b640fe46c4fb1007912bf85100f /2+oRHzhD7Hv3M/mLK+LZA/kbE+xAHkSv4UQDw==
HMAC-SHA3_256: 8c6e0683409427f8931711b10ca92a506eb1fafa48fadd66d76126f47ac2c333 jG4Gg0CUJ/iTFxGxDKkqUG6x+vpI+t1m12Em9HrCwzM=
HMAC-SHA3_384: aa739ad9fcdf9be4a04f06680ade7a1bd1e01a0af64accb04366234cf9f6934a0f8589772f857681fcde8acc256091a2 qnOa2fzfm+SgTwZoCt56G9HgGgr2SsywQ2YjTPn2k0oPhYl3L4V2gfzeiswlYJGi
HMAC-SHA3_512: 237a35049c40b3ef5ddd960b3dc893d8284953b9a4756611b1b61bffcf53edd979f93547db714b06ef0a692062c609b70208ab8d4a280ceee40ed8100f293063 I3o1BJxAs+9d3ZYLPciT2ChJU7mkdWYRsbYb/89T7dl5+TVH23FLBu8KaSBixgm3AgirjUooDO7kDtgQDykwYw==
HMAC-SHA512: b42af09057bac1e2d41708e48a902e09b5ff7f12ab428a4fe86653c73dd248fb82f948a549f7b791a5b41915ee4d1ec3935357e4e2317250d0372afa2ebeeb3a tCrwkFe6weLUFwjkipAuCbX/fxKrQopP6GZTxz3SSPuC+UilSfe3kaW0GRXuTR7Dk1NX5OIxclDQNyr6Lr7rOg==
HMAC-SHA512_224: a1afb4f708cb63570639195121785ada3dc615989cc3c73f38e306a3 oa+09wjLY1cGORlRIXha2j3GFZicw8c/OOMGow==
HMAC-SHA512_256: 7fb65e03577da9151a1016e9c2e514d4d48842857f13927f348588173dca6d89 f7ZeA1d9qRUaEBbpwuUU1NSIQoV/E5J/NIWIFz3KbYk=

CMACs (Cipher-based message authentication codes)

But, what is CMAC? Well, CMACs (Cipher-based message authentication codes) create message authentication codes (MACs) using a block cipher and a secret key. They differ from HMACs in that they use a block symmetric key method for the MACs rather than a hashing method. The code to implement this is [here]:

import os
import sys
import binascii

from cryptography.hazmat.primitives import cmac

from cryptography.hazmat.primitives.ciphers import algorithms

message = "message"
ctype="TripleDES"

if (len(sys.argv)>1):
message=str(sys.argv[1])
if (len(sys.argv)>2):
ctype=str(sys.argv[2])

message=message.encode()
key = os.urandom(32)

c = cmac.CMAC(algorithms.AES(key))

if (ctype=="AES"):
c = cmac.CMAC(algorithms.AES(key))
elif (ctype=="Camellia"):
c = cmac.CMAC(algorithms.Camellia(key))
elif (ctype=="TripleDES"):
key = os.urandom(16)
c = cmac.CMAC(algorithms.TripleDES(key))
elif (ctype=="Blowfish"):
c = cmac.CMAC(algorithms.Blowfish(key))
elif (ctype=="CAST5"):
key = os.urandom(16)
c = cmac.CMAC(algorithms.CAST5(key))
elif (ctype=="SEED"):
key = os.urandom(16)
c = cmac.CMAC(algorithms.SEED(key))


c.update(message)

c_copy = c.copy()

signature = c.finalize()

print (f"Message: {message.decode()}" )
print (f"Type: {ctype}" )
print (f"Key: {binascii.b2a_hex(key).decode()}")

print (f"CMAC signature: {binascii.b2a_hex(signature).decode()}")

try:
rtn=c_copy.verify(signature)
print ("Signature matches")
except:
print ("Signature does not match")

A sample run is [here]:

Message: message
Type: AES
Key: 5b2997c9aca4bd4ac5081e6551d1faabcae25d2ed9ac8b58b2acc643d963f425
CMAC signature: 6c8de15306d8ed7d8e0badf945d38561
Signature matches

Here is the running code:

Generally CMAC will be slower than HMAC, as hashing methods are generally faster than block cipher methods. In most cases HMAC will work best, but CMAC may work better where there is embedded hardware which has hardware accelleration for block ciphers. For this, CMAC would likely run faster than HMAC.

If you want to learn more about cryptography, there is no better place to start than to investigate the Cryptography Hazmat library in Python:

https://asecuritysite.com/hazmat

Go learn the future!