2

A way to prevent timing attacks for hash string comparison is to perform additional HMAC signing in order to randomize the verification process (see https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/february/double-hmac-verification/).

In addition to the second HMAC hashing for each hash, a random salt of random length is added to both in order to make the hashing timing / process even less predictable.

My implementation of this look like this:

function hmac_verify ($hash_original, $message, $key) {

    $hmac_salt = '...'; // was added at the original HMAC signing
    $random_salt = openssl_random_pseudo_bytes (rand(16,96));

    $raw_hash = hash_hmac('sha512', $message . $hmac_salt, $key, true);
    $hash_compare = base64_encode ($raw_hash); // $hash_original is in base64 
    $hash_compare_safe = hash_hmac('sha512', $hash_compare, $random_salt, true);
    $hash_original_safe = hash_hmac('sha512', $hash_original, $random_salt, true);

    if ($hash_compare_safe === $hash_original_safe) return true;
        else return false;

}

The function is called in this way after decrypting the encrypted text in order to verify the decryption result:

if (!hmac_verify ($hmac_hash, $plaintext . $cipher_text, $key . $iv)) return "HASH ERROR";

Will this successfully prevent a timing attack? Am I doing anything unnecessary? Could something be improved?

A second question is whether it is more advisable to perform the HMAC verification on the plaintext, the ciphertext, or both (as in my example), and why.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
az2014
  • 41
  • 3
  • 2
    This question is much better suited for [codereview.se], because this code presumably works (there is no problem to be solved) and CodeReview specifically welcomes questions about security issues. – Artjom B. Oct 25 '15 at 14:45
  • Regarding the security of this, you may be better off on [crypto.se], quoting from their help center: "However, you might like to break your problem down into specifics, such as "under these conditions, does structure X have desired security property Y?" which would be a perfect fit for us." – Vogel612 Oct 25 '15 at 14:53
  • @Vogel612 The problem is that [crypto.se] doesn't usually deal with questions that have code, so it's probably off-topic there. This is also a reason why [security.se] is also probably not the right place to ask this. – Artjom B. Oct 25 '15 at 16:21
  • @ArtjomB. hmm... I have seen code-related questions on [security.se] ([exhibit a](https://security.stackexchange.com/questions/70990/unlock-codes-for-a-mobile-app)) and it seems okay-ish on crypto according to the help-center. Then again I don't actively participate there... – Vogel612 Oct 25 '15 at 16:39
  • 1
    Thanks guys, I posted it here: http://crypto.stackexchange.com/questions/30084/double-randomised-hmac-verification-to-prevent-timing-attack – az2014 Oct 25 '15 at 21:54

1 Answers1

1

I've left some comments inline as I read your function. This isn't an analysis after reading the whole thing, rather this is what I immediately think of as I read it.

function hmac_verify ($hash_original, $message, $key) {
    ##
    # Nitpick: A variable named $hash_original will prime people who read
    # your code to think of simple hash functions rather than HMAC
    ##

    $hmac_salt = '...'; // was added at the original HMAC signing
    ##
    # What is this? $hmac_salt? Looks like a hard coded-salt (a.k.a. pepper).
    # I wouldn't trust this with my life.
    ##

    $random_salt = openssl_random_pseudo_bytes (rand(16,96));
    ##
    # Why are you bothering to randomize this? Just use a static value
    # approximating the output size of the hash function (i.e. 64).
    ##

    $raw_hash = hash_hmac('sha512', $message . $hmac_salt, $key, true);
    $hash_compare = base64_encode ($raw_hash); // $hash_original is in base64 
    $hash_compare_safe = hash_hmac('sha512', $hash_compare, $random_salt, true);
    ##
    # Ah, yeah, don't pepper. HMAC is secure.
    ##
    $hash_original_safe = hash_hmac('sha512', $hash_original, $random_salt, true);

    if ($hash_compare_safe === $hash_original_safe) return true;
        else return false;
    ##
    # Why not just do this?
    # return $hash_compare_safe === $hash_original_safe;
    ## 

}

So, I would highly recommend separating this out into two separate mechanisms: One that calculates MACs and the other that compares strings in constant time (like PHP 5.6's hash_equals() does).

function hmac_verify ($hmac, $message, $key)
{
    $calc = hash_hmac('sha512', $message, $key, true);
    return hmac_equals($hmac, $calc);
}

function hmac_equals($hmac, $calc)
{
    $random = openssl_random_pseudo_bytes(64);
    return (
        hash_hmac('sha512', $hmac, $random)
            ===
        hash_hmac('sha512', $calc, $random)
    );
}
Scott Arciszewski
  • 33,610
  • 16
  • 89
  • 206