[php-src] Issue #9604: hash_pbkdf2() wastes time by repeatedly hashing the HMAC key blocks

From: Date: Fri, 23 Sep 2022 17:19:13 +0000
Subject: [php-src] Issue #9604: hash_pbkdf2() wastes time by repeatedly hashing the HMAC key blocks
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-242458@lists.php.net to get a copy of this message
Issue: https://github.com/php/php-src/issues/9604 Author: plstand ### Description As mentioned in a 2015 blog post titled "[PBKDF2: performance matters](https://jbp.io/2015/08/11/pbkdf2-performance-matters.html)", hash_pbkdf2() is missing an important optimization that relates specifically to the use of HMAC for the underlying pseudorandom function. This means it "[...] is at least two times slower than it otherwise could be." The author of the blog post linked to a pull request he had made against PHP 5.6 (#1387), which unfortunately did not get a review before it was closed as stale in 2017. I have attempted to update his patch for PHP 8.0 (the lowest supported version at the time I am writing this report), and I am creating a new pull request for it. I hope that this time the PHP developers will take a look. Below is a test script that demonstrates that the optimization is missing. ---- The following [code](https://3v4l.org/3vKTn#v8.1.10): ```php <?php function test_native() { $st = hrtime(true); $h = hash_pbkdf2('sha256', 'password', 'salt', 100000); $et = hrtime(true); return [$h, $et - $st]; } function test_emulated() { $st = hrtime(true); $K = str_pad('password', 64, "\0"); $K ^= str_repeat("\x36", 64); $ictx = hash_init('sha256'); hash_update($ictx, $K); $K ^= str_repeat("\x6a", 64); $octx = hash_init('sha256'); hash_update($octx, $K); $DK = str_repeat("\0", 32); $prev = "salt\0\0\0\1"; for ($j = 0; $j < 100000; ++$j) { $ctx = hash_copy($ictx); hash_update($ctx, $prev); $temp = hash_final($ctx, true); $ctx = hash_copy($octx); hash_update($ctx, $temp); $prev = hash_final($ctx, true); $DK ^= $prev; } $h = bin2hex($DK); $et = hrtime(true); return [$h, $et - $st]; } $nr = test_native(); $er = test_emulated(); var_dump($nr[0]); var_dump($er[0]); var_dump($nr[1] <= $er[1]); ``` Resulted in this output: ``` string(64) "0394a2ede332c9a13eb82e9b24631604c31df978b4e2f0fbd2c549944f9d79a5" string(64) "0394a2ede332c9a13eb82e9b24631604c31df978b4e2f0fbd2c549944f9d79a5" bool(false) ``` But I expected this output instead: ``` string(64) "0394a2ede332c9a13eb82e9b24631604c31df978b4e2f0fbd2c549944f9d79a5" string(64) "0394a2ede332c9a13eb82e9b24631604c31df978b4e2f0fbd2c549944f9d79a5" bool(true) ``` ### PHP Version PHP 8.1.10 ### Operating System _No response_

« previous php.bugs (#242458) next »