title: PBKDF2 Password Hashing for PHP
body: <p>Thanks to some great advice on a recent post I'm implementing PBKDF2 from
  <a href="https://defuse.ca/php-pbkdf2.htm" rel="nofollow">https://defuse.ca/php-pbkdf2.htm</a> into a small PHP image gallery I'm building to teach myself some PHP.</p>
  
  <p>I understand that you store the salt and the hash in the database and then reconstruct them to match when the user enters their password. What I don't understand is how the validate_password function on the above website works when it generates a different, unique salt for the same password.</p>
  
  <p>For example, I created a test repeatedly generating a password of just my name (andrew). Each time the final hash was different, presumably due to the salt being unique? However, it did not matter which generated hash I compared the password too, it validated it. (It obviously didn't validate a totally different password like Andrew56 or ndrew1).</p>
  
  <p>Can anyone explain to my simple self why this is the case? It seems like I don't even need to store the salt? I hope this isn't off topic.</p>
  
  <pre><code>&lt;?php
  /*
   * Password hashing with PBKDF2.
   * Author: havoc AT defuse.ca
   * www: https://defuse.ca/php-pbkdf2.htm
   */
  
  // These constants may be changed without breaking existing hashes.
  define("PBKDF2_HASH_ALGORITHM", "sha256");
  define("PBKDF2_ITERATIONS", 1000);
  define("PBKDF2_SALT_BYTES", 24);
  define("PBKDF2_HASH_BYTES", 24);
  
  define("HASH_SECTIONS", 4);
  define("HASH_ALGORITHM_INDEX", 0);
  define("HASH_ITERATION_INDEX", 1);
  define("HASH_SALT_INDEX", 2);
  define("HASH_PBKDF2_INDEX", 3);
  
  function create_hash($password)
  {
  // format: algorithm:iterations:salt:hash
  $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
  return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" .  $salt . ":" . 
      base64_encode(pbkdf2(
          PBKDF2_HASH_ALGORITHM,
          $password,
          $salt,
          PBKDF2_ITERATIONS,
          PBKDF2_HASH_BYTES,
          true
      ));
  }
  
  function validate_password($password, $good_hash)
  {
  $params = explode(":", $good_hash);
  if(count($params) &lt; HASH_SECTIONS)
     return false; 
  $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
  return slow_equals(
      $pbkdf2,
      pbkdf2(
          $params[HASH_ALGORITHM_INDEX],
          $password,
          $params[HASH_SALT_INDEX],
          (int)$params[HASH_ITERATION_INDEX],
          strlen($pbkdf2),
          true
      )
  );
  }
  
  // Compares two strings $a and $b in length-constant time.
  function slow_equals($a, $b)
  {
  $diff = strlen($a) ^ strlen($b);
  for($i = 0; $i &lt; strlen($a) &amp;&amp; $i &lt; strlen($b); $i++)
  {
      $diff |= ord($a[$i]) ^ ord($b[$i]);
  }
  return $diff === 0; 
  }
  
  /*
   * 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))
      die('PBKDF2 ERROR: Invalid hash algorithm.');
  if($count &lt;= 0 || $key_length &lt;= 0)
      die('PBKDF2 ERROR: Invalid parameters.');
  
  $hash_length = strlen(hash($algorithm, "", true));
  $block_count = ceil($key_length / $hash_length);
  
  $output = "";
  for($i = 1; $i &lt;= $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 &lt; $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));
  }
  ?&gt;
  </code></pre>