You want to base what on my hmac?

Published: Mar 09 2020

A guide to shrinking an elephant hashed and encoded in PHP.

I was recently working on a new bundle for Symfony, and while reviewing a block of code I had written earlier I asked my self, why do the big guys (Symfony and Laravel) encode the binary output of hash_hmac() using base64_encode()?

After some digging around, I discovered the answer. But first lets take a look at how hash_hmac() works. More specifically, what hash_hmac() returns...

TL;DR

When overhead is a concern, using base64_encode(hash_hmac(..., true)) gives you a hash with a string length of 44 characters vs the 64 characters that hash_hmac(..., false) provides.

What is this hash hmac and why do I care about it?

From the manual - "hash_hmac — Generate a keyed hash value using the HMAC method"

Translation, this method allows us to take a string of data, and potentially create a cryptographically secure hash that can be used to verify the data integrity. One thing to remember about hash_hmac, it's selfish. hash_hmac takes the data and gives you a string (hash), but it will never give you the data used to create that hash.

This is sometimes referred to as a one-way hash. Meaning the only way we can verify the integrity and authenticity of the data, is to create a new hash and then compare the original hashed value against our newly created hash. To do this we use the hash_equals() method.

To put it another way, lets play the game telephone. You remember that as a kid right? A large group gets in a circle and then the first kid says to his buddy on the left, "elephants are gray". Then his buddy does the same to the kid to his left, and so on and so on. The point of that game is to see if the phrase is still the same by the time it circles back around to the first kid.

Only in our version of the game, I'm going to start the game and use hash_hmac to prove to everyone at the end, I'm not fibbing...

$elephantHash = hash_hmac('sha256', 'elephants are gray', 'secret_key'); 
var_dump($elephantHash);
// string(64) d53c1002ddf2159b926ae4eaf775fb0242282e826070bb79a899bbf1af45642f

So the phrase is "elephants are gray", go tell a friend what the phrase is and have them repeat it to me. Hurry, I'm waiting...

While I'm waiting to hear back from your friend, I'll show you how I'm going to verify the original phrase. First, there are 3 required arguments hash_hmac() needs. The first is the algorithm, algo, that will be used to create the hash. sha256 is a good place to start, but be sure to follow best practices when picking which algorithm best fits your use case.

The second argument is the data string. This string is what will actually be hashed. In our case, we are hashing 'elephants are gray'. Lastly, we have to provide the third, key, argument that is used to generate the hash. The key, similar to a salt, must be kept secret. If you're using a framework like Symfony, APP_SECRET could be used as your key. So long as it's never exposed.

There is a 4th optional argument to hash_hmac(), raw_output, but I'll get to that later. Meanwhile, let's see what happens when we create a second hash using 'elephants are gray' as the data string again and compare it to the first.

// verifyElephantColor()
$secondGrayElephant = hash_hmac('sha256', 'elephants are gray', 'secret_key');

function verifyElephantColor($knownElephant, $suspiciousElephant): string
{
    if (hash_equals($knownElephant, $suspiciousElephant) {
        return 'We have 2 gray elephants';
    }

    return 'Ooops, one of the elephants changed color';
}

The verifyElephantColor() method above would return We have 2 gray elephants. This is because our, $secondGrayElephant, is using the same algorithm, data, and key as $elephantHash.

That was quick! Your friend just said to me 'elephants are pink'. Hmm, something seems fishy about this. I don't remember telling you the PHP elephant is pink... Let's find out for sure by calling the verifyElephantColor() method again.

$pinkElephant = hash_hmac('sha256', 'elephants are pink', 'secret_key');
echo verifyElephantColor($elephantHash, $pinkElephant);

// Ooops, one of the elephants changed color

AH HA! See, I never said elephants are pink! Since the data supplied to hash_hmac() differs from the original data used to create $elephantHash, we are g