A hash_pbkdf2() implementation for PHP versions 5.3 and 5.4.
<?php | |
/** | |
* Generate a PBKDF2 key derivation of a supplied password | |
* | |
* This is a hash_pbkdf2() implementation for PHP versions 5.3 and 5.4. | |
* @link http://www.php.net/manual/en/function.hash-pbkdf2.php | |
* | |
* @param string $algo | |
* @param string $password | |
* @param string $salt | |
* @param int $iterations | |
* @param int $length | |
* @param bool $rawOutput | |
* | |
* @return string | |
*/ | |
function compat_pbkdf2($algo, $password, $salt, $iterations, $length = 0, $rawOutput = false) | |
{ | |
// check for hashing algorithm | |
if (!in_array(strtolower($algo), hash_algos())) { | |
trigger_error(sprintf( | |
'%s(): Unknown hashing algorithm: %s', | |
__FUNCTION__, $algo | |
), E_USER_WARNING); | |
return false; | |
} | |
// check for type of iterations and length | |
foreach (array(4 => $iterations, 5 => $length) as $index => $value) { | |
if (!is_numeric($value)) { | |
trigger_error(sprintf( | |
'%s() expects parameter %d to be long, %s given', | |
__FUNCTION__, $index, gettype($value) | |
), E_USER_WARNING); | |
return null; | |
} | |
} | |
// check iterations | |
$iterations = (int)$iterations; | |
if ($iterations <= 0) { | |
trigger_error(sprintf( | |
'%s(): Iterations must be a positive integer: %d', | |
__FUNCTION__, $iterations | |
), E_USER_WARNING); | |
return false; | |
} | |
// check length | |
$length = (int)$length; | |
if ($length < 0) { | |
trigger_error(sprintf( | |
'%s(): Iterations must be greater than or equal to 0: %d', | |
__FUNCTION__, $length | |
), E_USER_WARNING); | |
return false; | |
} | |
// check salt | |
if (strlen($salt) > PHP_INT_MAX - 4) { | |
trigger_error(sprintf( | |
'%s(): Supplied salt is too long, max of INT_MAX - 4 bytes: %d supplied', | |
__FUNCTION__, strlen($salt) | |
), E_USER_WARNING); | |
return false; | |
} | |
// initialize | |
$derivedKey = ''; | |
$loops = 1; | |
if ($length > 0) { | |
$loops = (int)ceil($length / strlen(hash($algo, '', $rawOutput))); | |
} | |
// hash for each blocks | |
for ($i = 1; $i <= $loops; $i++) { | |
$digest = hash_hmac($algo, $salt . pack('N', $i), $password, true); | |
$block = $digest; | |
for ($j = 1; $j < $iterations; $j++) { | |
$digest = hash_hmac($algo, $digest, $password, true); | |
$block ^= $digest; | |
} | |
$derivedKey .= $block; | |
} | |
if (!$rawOutput) { | |
$derivedKey = bin2hex($derivedKey); | |
} | |
if ($length > 0) { | |
return substr($derivedKey, 0, $length); | |
} | |
return $derivedKey; | |
} | |
// test | |
if (realpath($_SERVER['argv'][0]) === __FILE__ && function_exists('hash_pbkdf2')) { | |
$password = 'password'; | |
$savedPassword = 'sha256,salt,65536,4156f668bb31db3a17f4d1b91424ef0d417ad1f35d055aceaebd8da0f6a44b7e'; | |
list($algo, $salt, $iterations, $hash) = explode(',', $savedPassword); | |
var_dump( | |
$hash, | |
hash_pbkdf2($algo, $password, $salt, $iterations, strlen($hash)), | |
compat_pbkdf2($algo, $password, $salt, $iterations, strlen($hash)) | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
$length was not taking into account the format of the bytes:
https://gist.github.com/dabura667/8655a3c118f8bd56ec34b12a7419ef4b/revisions