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

Source Code

Source Code on GitHub
<?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));
}
?>