Skip to content

Instantly share code, notes, and snippets.

@spaze
Last active October 12, 2018 20:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save spaze/7392b0e40d8f568db0b4c8f3cff5c86e to your computer and use it in GitHub Desktop.
Save spaze/7392b0e40d8f568db0b4c8f3cff5c86e to your computer and use it in GitHub Desktop.
Symfony's PBKDF2 polyfill benchmark (TL;DR it's slow, DO NOT USE, use hash_pbkdf2 available in PHP 5.5+ if you must use PBKDF2 but just use password_hash) for the thread here https://twitter.com/spazef0rze/status/1050436425559302147
<?php
function hashPbkdf2($algorithm, $password, $salt, $iterations, $length = 0)
{
// Number of blocks needed to create the derived key
$blocks = ceil($length / strlen(hash($algorithm, null, true)));
$digest = '';
for ($i = 1; $i <= $blocks; $i++) {
$ib = $block = hash_hmac($algorithm, $salt . pack('N', $i), $password, true);
// Iterations
for ($j = 1; $j < $iterations; $j++) {
$ib ^= ($block = hash_hmac($algorithm, $block, $password, true));
}
$digest .= $ib;
}
return substr($digest, 0, 40);
}
function bench($p, $s, $i)
{
echo 'pw length: ' . strlen($p) . "\n";
echo 'iterations: ' . $i . "\n";
$start = microtime(true);
$h1 = bin2hex(hashPbkdf2('sha256', $p, $s, $i, 32));
echo 'polyfill: ' . (microtime(true) - $start) . "s\n";
$start = microtime(true);
$h2 = hash_pbkdf2('sha256', $p, $s, $i);
echo 'native: ' . (microtime(true) - $start) . "s\n";
echo 'h1 === h2: ' . ($h1 === $h2 ? 'true' : 'false') . "\n\n";
}
$iter = 1e4;
bench(str_repeat('*', 1e2), 'somerandombytes', $iter);
bench(str_repeat('*', 1e3), 'somerandombytes', $iter);
bench(str_repeat('*', 1e4), 'somerandombytes', $iter);
bench(str_repeat('*', 1e5), 'somerandombytes', $iter);
pw length: 100
iterations: 10000
polyfill: 0.19428706169128s
native: 0.10092806816101s
h1 === h2: true
pw length: 1000
iterations: 10000
polyfill: 0.4820568561554s
native: 0.09175705909729s
h1 === h2: true
pw length: 10000
iterations: 10000
polyfill: 3.5844631195068s
native: 0.1085410118103s
h1 === h2: true
pw length: 100000
iterations: 10000
polyfill: 24.373319149017s
native: 0.054205179214478s
h1 === h2: true
@Sc00bz
Copy link

Sc00bz commented Oct 12, 2018

function hashPbkdf2($algorithm, $password, $salt, $iterations, $length = 0)

The $length = 0 suggests this does something better than returning an empty string.

+    $hashLength = strlen(hash($algorithm, null, true));
+    if ($length <= 0) $length = $hashLength;
-    $blocks = ceil($length / strlen(hash($algorithm, null, true)));
+    $blocks = ceil($length / $hashLength);

Also $length is ignored also 40 seems like it should be 20 because SHA1 the confusion between hex and bytes:

-    return substr($digest, 0, 40);
+    return substr($digest, 0, $length);

@spaze
Copy link
Author

spaze commented Oct 12, 2018

FYI, hashPbkdf2 is a polyfill taken right from Symfony and except hardcoding 40 in the return statement I didn't and didn't plan to change anything.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment