What is HMAC and how does it work? 

HMAC stands for Hash-based Message Authentication Code or Keyed-hash Message Authentication Code. We use it to verify the authenticity and integrity of data transmitted. Here, authenticity is ensuring that the data was indeed sent by the person who claims to have sent the data, and integrity is ensuring that the data was received as it was sent—in other words, the data was not modified after being sent and was received intact.  

Hashing

To that end, the HMAC algorithm makes use of a cryptographic key and hashing. Hashing turns data into a hash of a constant size. Hashing is also deterministic, meaning that the algorithm produces the same hash for the same input. But the real killer feature of hashing is that there is no way one can obtain the original data from the hash.  

This feature of hashing makes it ideal for ensuring data integrity. When data is sent across a communication channel, the data is hashed and the hash is also sent. A recipient can ensure the integrity of the data they received by hashing the received data and checking if the hash is equal to the hash received. MD5 and SHA-256 are two of the popular hash algorithms.  

HMAC can prove authenticity too

While ascertaining data integrity is important, verifying authenticity is also important. Here is where HMAC becomes handy since it can both verify the authenticity and integrity of data. Let’s see how it accomplishes this. 

Simply put, HMAC combines the data to be sent with a cryptographic key and creates a hash. This hash is once again combined with the cryptographic key and another hash is created. It is this second hash which we send along with the data. We shall now see how this algorithm works at length.  

How the HMAC algorithm works

First, we choose a hashing algorithm. Depending on the algorithm, the data would be hashed in blocks of a certain size B and a hash of size L is produced. It is relevant to note here that HMAC uses hashing algorithms that are block ciphers. Block ciphers encrypt data in blocks. For example, if we have a stream of 160 bits, then a block cipher might encrypt (or hash) the data in a block of 8 bits. In contrast, a stream cipher encrypts the data bit by bit.  

Next, we generate a cryptographic key randomly. This key is supposed to be shared with the recipient securely. Besides, the size of the key should be between the block size B and the size of the hash L. If the size of the key is bigger than B, then we hash the key using the chosen hashing algorithm to produce a hash of size L. 

Inner and Outer keys

Now, we need to derive two keys—the inner key and the outer key—from the cryptographic key. The inner key is generated by appending zeroes to the end of the key to make it of size B, and then XORing the key with the ipad—which is 0x36 repeated B times. For example, if the block size is 64 bytes (512 bits) and the key size is 20 bytes (160bits), then we append 352 bits of zeroes to the key to make the key 512 bits in size. Then, this key is XORed with the byte 0x36 repeated 64 times. The resultant key is the inner key.  

Then, the outer key is derived by appending zeroes to the original key to make it of size B and then XORing it with the opad—which is 0x5C repeated B times. Now, the fun part begins.  

First, we append the data to the inner key and hash them, producing a hash of size L. Then this hash is appended to the outer key and hashed once again producing another hash of size L, which will be the hash that will be used to confirm the integrity and authenticity of the data.  

Now, the recipient of the data can use the data that they receive and the key they have, and execute the above algorithm and see if the produced hash matches the code they have received. If they match, then they prove two things.  

  1. The input data and the key are both the same since the recipient obtained the same output
  2. Since the key is the same and since we expect the key to be a secret between the sender and the recipient, it was the sender who actually sent the data.  

Summary

Thus, we can summarize the HMAC algorithm as follows: 

H()—hashing function 

K—Cryptographic key (zero padded if needed) 

H(K XOR opad, H(K XOR ipad, data)) 

To get a better understanding of the algorithm, let’s consider an example. 

An example

I want to send the data “Hello World!” to Bob. I choose the SHA1 hashing algorithm to hash the data. The SHA-1 algorithm hashes data in blocks of 64 bytes. So, the block size B is 64 bytes. The hash produced by this algorithm is 20 bytes. So, L is 20 bytes.  

Now, I need to generate a cryptographic key. Since L is 20 bytes, I need to ensure that the key is bigger than L in size. Even though RFC 2104 recommends that the size of the key should not be more than B, i.e., 64 bytes, if the key is longer than L, then we can hash it to produce a key of size L.  

Generating a secret key

Let me produce a 1024-bit (128-byte) key using All Keys Generator 

0x2B4B6250655368566B5970337336763979244226452948404D635166546A576E5A7134743777217A25432A462D4A614E645267556B58703273357538782F413F4428472B4B6250655368566D5971337436773979244226452948404D635166546A576E5A7234753778214125432A462D4A614E645267556B5870327335763879 

This is in the hexadecimal format with each character representing 4 bits. Here, there are 256 characters producing 256x4=1024 bits. 

Hashing the secret key

Now, since my key is bigger than the block size of 64 bytes, I need to hash it. I am going to use this implementation of jsSHA for hashing. Hashing the above key gives me the hash below: 

0x5833f34f47af76685334d71be258e5548520593f 

Zero-padding the secret key

This hash is also in the hexadecimal format. There are 40 characters producing 40×4, 160 bits (20 bytes). Now, we need to append zeroes to make the size of this key 64 bytes (512bits). We will have to append 512-160=352 bits of zeroes, which is 352/4=88 zeroes in hexadecimal, to the key to make it 64 bytes in size.   

0x5833f34f47af76685334d71be258e5548520593f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 

Creating the ipad

Now, we need to create the ipad. We derive the ipad by repeating 0x36 B number of times. Since the size of the block B is 64 bytes, we need to repeat 0x36 64 times to produce the ipad of size 64 bytes.  

0x36363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636 

Deriving the inner key

Next, let’s produce the inner key by XORing the ipad with the key bitwise.  We can do this using xor.pw. The resultant inner key is this: 

0x6e05c5797199405e6502e12dd46ed362b3166f093636363636363636363636363636363636363636363636363636363636363636363636363636363636363636 

Creating the first hash

Now, let’s append our hexadecimal-encoded data to this key and create a hash using SHA-1.  

Our data in hexadecimal (use this site to convert string into hexadecimal): 

0x48656c6c6f20576f726c6421 

Once appended to the inner key:

0x6e05c5797199405e6502e12dd46ed362b3166f09363636363636363636363636363636363636363636363636363636363636363636363636363636363636363648656c6c6f20576f726c6421 

The created hash:  

0xde33ba343e2e8cccbfd1935f7f8ce96b1eeb8ae2 

Creating the opad

Next, let’s create the outer key. To create the outer key, we first need to create the opad. We obtain the opad by repeating 0x5C B number of times. So, the opad is: 

0x5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c 

Deriving the outer key

Now, let’s XOR it with our zero-padded original key to produce the outer key.  

0x46faf131bf32a340f688b47be04b908d97c05635c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c 

Producing the HMAC code

Finally, let’s append the hash we obtained by hashing the inner key and the data to the outer key, and hash it.  

0x46faf131bf32a340f688b47be04b908d97c05635c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5cde33ba343e2e8cccbfd1935f7f8ce96b1eeb8ae2

The final hash: 

0xbfc72c78a8ee233f27b658838990d226d26f5b8a 

Now, to test this, you can use the HMAC Demo section of this site. Enter the data as a string, and the hexadecimal key and see if we get the above hash as the output.   

1 Comment

  1. […] To produce the exact hash, the same input should be used. So, the recipient has to use the same key and the same data to get the exact same hash. If the hash obtained thus is the same, then that means that the data received has not been modified during transmission. Since the cryptographic key is supposed to be a secret between the sender and the recipient, then it also means that it is indeed the sender who has sent the data. A more detailed explanation of the HMAC algorithm can be found here.  […]

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.