Is HMAC a suitable substitute for HKDF?

My PHP version does not include the hash_hkdf() function, and upgrading versions is not worth the trouble for my project (which is for personal pleasure, not for securing important private data). In light of the absence of this function, is it suitable to substitute the hash_hkdf() function with hash_hmac(), like so:

$k = hash_pbkdf2($ha, $pw,$s, $it,$kl, true);
$ek = hash_hmac($ha, "e", $k, true);$ak = hash_hmac($ha, "a",$k, true);
$c = openssl_encrypt($pt, $cm,$ek, OPENSSL_RAW_DATA, $iv);$h = hash_hmac($ha,$iv.$s.$c, $ak, true); return base64_encode($iv.$s.$h.$c);  • Since – in practice – there is no significant difference between HKDF and single-iteration PBKDF2, why don’t you simply use the available hash_pbkdf2 (as you do in line 1) with an according single itteration and be done with it? With your DIY code, you’re making something very complicated while it’s actually pretty simple. – e-sushi Apr 17 '18 at 11:51 2 Answers HKDF is itself built from HMAC, so your question strikes me as a false dilemma. If you have HMAC, HKDF Is just a handful of HMAC calls; you could just implement your own. Yes, I know that "don't roll your own" is top cryptographic advice, but compared to the things you're already proposing to do that you illustrate in your question, implementing your HKDF on your own is only a marginal increase in risk. Reading the RFC should help you convince yourself of that. Or you could consider this third-party library that implements HKDF for you already. Not that there's anything obviously wrong with the way you're using HMAC, but I just don't think that the lack of HKDF in some versions of PHP is big enough of an obstacle to to ground that decision. A more interesting observation is that what you're doing to compute $ek and $ak is almost identical to a single-block application of HKDF-expand (the second half of HKDF, which is in fact two functions—read the RFC). What you are doing differs from that only because HKDF-expand appends a block counter byte to its info parameter. If you change your subkey derivation lines to append the counter byte: $ek = hash_hmac($ha, "e\x01",$k, true);
$ak = hash_hmac($ha, "a\x01", $k, true);  ...then that is by definition the same as this (pseudocode because PHP doesn't seem to have an actual hkdf_expand function): $ek = hkdf_expand($ha,$k, $ha_output_size, "e");$ak = hkdf_expand($ha,$k, $ha_output_size, "a");  ...where $ha_output_size is the output size of the hash function that \$ha` refers to.

The other, earlier half of HKDF is the HKDF-extract function, whose purpose is to take random inputs that are not directly suitable for use directly as cryptographic keys because they are biased, and output a master pseudorandom key ("PRK" in the RFC's terminology) that is suitable for direct use or further derivation. But PBKDF2 is already intended to produce output that is suitable for use as a pseudorandom key, so arguably you don't need the HKDF-extract step.

So what you're doing is, overall, already almost the same as what you might do with HKDF. That's an argument that could be used to either keep doing it your way (the lack of the counter byte isn't going to break your app compared to HKDF-expand) or to actually stick in that extra byte so you actually are using HKDF.

As long as you always pass a distinct input for each key and purpose, then yes, this use of HMAC is fine. HKDF is just a more convenient way to produce arbitrary-length outputs for a structured set of purposes. For reasonable choices of the underlying hash function, both are conjectured to be PRFs, and internally, HKDF is built out of HMAC with a distinct encoding of its structured parameters.