1296
PBKDF2 For PHP
PBKDF2 (Password-Based Key Derivation Function) is a key stretching algorithm. It can be used to hash passwords in a computationally intensive manner, so that dictionary and brute-force attacks are less effective. See CrackStation's Hashing Security Article for instructions on implementing salted password hashing.
The following code is a PBKDF2 implementation in PHP. It complies with the PBKDF2 test vectors in RFC 6070. Performance improvements to the original code were provided by variations-of-shadow.com.
Benchmarks
The following benchmarks demonstrate the running time for various iteration counts, using the SHA256 hash function. The benchmarks were run on an AMD Phenom 9600 2.3GHz CPU.
1 iteration : 0.000149 seconds
2 iterations: 0.000036 seconds
4 iterations: 0.000038 seconds
8 iterations: 0.000052 seconds
16 iterations: 0.000090 seconds
32 iterations: 0.000157 seconds
64 iterations: 0.000297 seconds
128 iterations: 0.000623 seconds
256 iterations: 0.001141 seconds
512 iterations: 0.002258 seconds
1024 iterations: 0.004594 seconds
2048 iterations: 0.009575 seconds
4096 iterations: 0.018386 seconds
8192 iterations: 0.036070 seconds
16384 iterations: 0.073297 seconds
32768 iterations: 0.145324 seconds
65536 iterations: 0.294785 seconds
131072 iterations: 0.577492 seconds
262144 iterations: 1.173854 seconds
524288 iterations: 2.334627 seconds
1048576 iterations: 4.688382 seconds
2097152 iterations: 9.249891 seconds
4194304 iterations: 18.69492 seconds
8388608 iterations: 36.90171 seconds
16777216 iterations: 75.31797 seconds
2 iterations: 0.000036 seconds
4 iterations: 0.000038 seconds
8 iterations: 0.000052 seconds
16 iterations: 0.000090 seconds
32 iterations: 0.000157 seconds
64 iterations: 0.000297 seconds
128 iterations: 0.000623 seconds
256 iterations: 0.001141 seconds
512 iterations: 0.002258 seconds
1024 iterations: 0.004594 seconds
2048 iterations: 0.009575 seconds
4096 iterations: 0.018386 seconds
8192 iterations: 0.036070 seconds
16384 iterations: 0.073297 seconds
32768 iterations: 0.145324 seconds
65536 iterations: 0.294785 seconds
131072 iterations: 0.577492 seconds
262144 iterations: 1.173854 seconds
524288 iterations: 2.334627 seconds
1048576 iterations: 4.688382 seconds
2097152 iterations: 9.249891 seconds
4194304 iterations: 18.69492 seconds
8388608 iterations: 36.90171 seconds
16777216 iterations: 75.31797 seconds
Source Code
<?php
/*
* PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
* $algorithm - The hash algorithm to use. Recommended: SHA256
* $password - The password.
* $salt - A salt that is unique to the password.
* $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
* $key_length - The length of the derived key in bytes.
* $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
* Returns: A $key_length-byte key derived from the password and salt.
*
* Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
*
* This implementation of PBKDF2 was originally created by https://defuse.ca
* With improvements by http://www.variations-of-shadow.com
*/
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
$algorithm = strtolower($algorithm);
if(!in_array($algorithm, hash_algos(), true))
trigger_error('PBKDF2 ERROR: Invalid hash algorithm.', E_USER_ERROR);
if($count <= 0 || $key_length <= 0)
trigger_error('PBKDF2 ERROR: Invalid parameters.', E_USER_ERROR);
if (function_exists("hash_pbkdf2")) {
// The output length is in NIBBLES (4-bits) if $raw_output is false!
if (!$raw_output) {
$key_length = $key_length * 2;
}
return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
}
$hash_length = strlen(hash($algorithm, "", true));
$block_count = ceil($key_length / $hash_length);
$output = "";
for($i = 1; $i <= $block_count; $i++) {
// $i encoded as 4 bytes, big endian.
$last = $salt . pack("N", $i);
// first iteration
$last = $xorsum = hash_hmac($algorithm, $last, $password, true);
// perform the other $count - 1 iterations
for ($j = 1; $j < $count; $j++) {
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}
if($raw_output)
return substr($output, 0, $key_length);
else
return bin2hex(substr($output, 0, $key_length));
}
?>