CMAC or HMAC: Which is Better?
In cybersecurity, there are so many acronyms, and to be an expert, you really need to dig underneath the methods and understand how they work. One weak area of the industry is in the usage of MACs (Message Authentication Codes).
With the public-key signing, we use a public key and a private key, where the private key will digitally sign a hash of the message, and where the public key is verified the signature. With a MAC, we use a shared symmetric key, and where Bob and Alice will share the same secret key (Figure 1).
HMAC
HMAC is a message authentication code (MAC) that can be used to verify the integrity and authentication of a message. It involves hashing a message with a secret key and thus differs from standard hashing, which is purely a one-way function. As with any MAC, it can be used with a standard hash function, such as MD5 or SHA-1, which results in methods such as HMAC-MD5 or HMAC-SHA-1. Also, as with any hashing function, the strength depends on the quality of the hashing function and the resulting number of hash code bits. Along with this, the number of bits in the secret key is a factor in the strength of the hash.
The figure below outlines the operation, where the message to be sent is converted with a secret key and the hashing function to an HMAC code. This is then sent with the message. On receipt, the receiver recalculates the HMAC code from the same secret key and the message and checks it against the received version. If they match, it validates both the sender and the message.
With OpenSSL, we get a wide range of MAC methods [here]:
CMAC
CMACs (Cipher-based message authentication codes) create a message authentication code (MAC) using a block cipher and a secret key. They differ from HMACs in that they use a symmetric key method for the MACs rather than a hashing method. When the recipient receives the CMAC field, they will also compute the same CMAC with the message and the secret key. If they are the same, the recipient knows that the message has not been changed and signed with the secret key.
Coding
The following is the Golang code using Google Tink [here]:
package main
import (
"fmt"
"github.com/google/tink/go/mac"
"github.com/google/tink/go/keyset"
"github.com/google/tink/go/insecurecleartextkeyset"
"os"
)
func main() {
msg:="This is a test"
htype:="HMACSHA256Tag256Key"
key,_ := keyset.NewHandle(mac.HMACSHA256Tag256KeyTemplate())
argCount := len(os.Args[1:])
if (argCount>0) { msg= (os.Args[1])}
if (argCount>1) { htype= (os.Args[2])}
if htype=="HMACSHA256Tag256Key" { key,_ = keyset.NewHandle(mac.HMACSHA256Tag256KeyTemplate())
} else if htype=="AESCMACTag128Key" { key,_ = keyset.NewHandle(mac.AESCMACTag128KeyTemplate())
} else if htype=="HMACSHA256Tag128Key" {key,_ = keyset.NewHandle(mac.HMACSHA256Tag128KeyTemplate() )
} else if htype=="HMACSHA512Tag256Key" {key,_ = keyset.NewHandle(mac.HMACSHA512Tag256KeyTemplate() )
} else if htype=="HMACSHA512Tag512Key" {key,_ = keyset.NewHandle(mac.HMACSHA512Tag512KeyTemplate()) }
m, _ := mac.New(key)
mac, _ := m.ComputeMAC([]byte(msg))
res:=m.VerifyMAC(mac, []byte(msg))
if (res!=nil) {
fmt.Printf("MAC failed!")
} else {
fmt.Printf("Message: %s\n\n", msg)
fmt.Printf("MAC type: %s\n",htype)
fmt.Printf("MAC: %x\n", mac)
fmt.Printf("\nMAC success!\n\n")
exportedPriv := &keyset.MemReaderWriter{}
insecurecleartextkeyset.Write(key, exportedPriv)
fmt.Printf("Key: %s\n\n", exportedPriv)
}
}
A sample run for HMAC with SHA-256 and a 256-bit key is [here]:
Message: qwerty123
MAC type: HMACSHA256Tag256Key
MAC: 0122589d948fb8a93f1ab0b0de355d736a328648cefde8c38b2e84e9d447875508581dea20
MAC success!
Key: .{primary_key_id:576232852 key:{key_data:{type_url:"type.googleapis.com.google.crypto.tink.HmacKey" value:".x12.x04.x08.x03.x10 .x1a .xf9h.x96.xc1.xd1c.x06N|.x85.x81Ø–.x7f.xa2.xd1.xf8.x14.xe0].xb12*AW.xba.xcc.xf1 .x1b.x9b.xca" key_material_type:SYMMETRIC} status:ENABLED key_id:576232852 output_prefix_type:TINK} .nil.}
For CMAC, we get [here]:
Message: qwerty123
MAC type: AESCMACTag128Key
MAC: 0139bae9f2b77bdc6104d08be0ec8e77484402e275
MAC success!
Key: .{primary_key_id:968550898 key:{key_data:{type_url:"type.googleapis.com.google.crypto.tink.AesCmacKey" value:".x12 ZSƃ .xb1.x01Sx..xd2:p.x1c.xbe.xa0.xc4;}.x9b.xb0 tÇ.x93.x81.xa7e.xb3,.xbc.x1a.x02.x08.x10" key_material_type:SYMMETRIC} status:ENABLED key_id:968550898 output_prefix_type:TINK} .nil.}
The main difference between an HMAC and a CMAC is thus that HMAC uses a hashing method for its MAC, whereas CMAC uses a symmetric key method. In the following, we can use a range of symmetric key methods to create the CMAC code [here]:
Some Bouncy Castle code for MAC codes is here:
MACs with stream ciphers
There are many MACs around, including HMAC, GMAC, CMAC, Blake2bmac, Blake2smac, KMAC128 and KMAC256. One of the core applications of a CMAC is in authenticating a stream cipher, such as with AES GCM. If we did not use a CMAC, an adversary can flip the bits in the cipher, and change the decrypted output.
The following is an example of a GMAC (using a stream mode, such as with GCM) [here]:
openssl mac -cipher AES-128-GCM -macopt hexkey:77A77FAF290C1FA30C683DF16BA7A77B -in 1.txt GMAC
740DDAFDB2008F4E3C7D10B9141A84DF
The following is an example of a CMAC (used with block mode ciphers):
openssl mac -cipher AES-128-CBC -macopt hexkey:77A77FAF290C1FA30C683DF16BA7A77B -in 1.txt CMAC
8CDBB6E42ED45C8A98328ADC739D3E41
For a 256-bit key with AES-256-CBC:
openssl mac -cipher AES-256-CBC -macopt hexkey:77A77FAF290C1FA30C683DF16BA7A77B77A77FAF290C1FA30C683DF16BA7A77B -in 1.txt CMAC
12F029BB51AC78D457DAF414A25A57EE
OpenSSL supports the following CMAC codes [here]:
Conclusions
And, so, the answer is that HMAC uses a hashing method, and CMAC uses a symmetric key method. Overall, HMAC is simpler and faster than CMAC. It is normally the best choice for varying lengths of messages and for different key sizes. CMAC, though, is possibly better at complying with standards and its integration into a range of protocols.