phpsecPw

  1. phpsec
    1. dev
    2. b-0.1
    3. b-0.2
    4. b-0.3

Provides methods for hashing and salting of passwords. Note: This class is deprecated. Use phpsecHash instead.

See also

phpsecHash

Hierarchy

Properties

NameDescription
phpsecPw::$_method
phpsecPw::$_pbkdf2_cIteration count that PBKDF2 will use. 8192 is just a little slower than a work load of 11 when using bcrypt. Oh, slow is good.
phpsecPw::$_pbkdf2_dkLen
phpsecPw::$_pbkdf2_prf

Functions & methods

NameDescription
phpsecPw::checkValidate a user-supplied password against a stored password generated using the phpsecPw::hash() method.
phpsecPw::hashCreate a hashed version of a password, safe for storage in a database. This function return a json encoded array that can be stored directly into a database. The array has the following layout: array( 'hash' => The hash created from…
phpsecPw::injectInject a salt into a password to create the string to be hashed. What we really do is to create an hash from the password, and retrieve the first character from this hash. This is then converted to its decimal value. We now have a number between 0 and…

Constants

NameDescription
phpsecPw::phpsecPw_BCRYPT
phpsecPw::phpsecPw_PBKDF2
phpsecPw::phpsecPw_SHA256
phpsecPw::phpsecPw_SHA512

phpsec/phpsec.pw.php, line 16

View source
<?php
class phpsecPw {
  const phpsecPw_PBKDF2 = 'pbkdf2';
  const phpsecPw_BCRYPT = 'bcrypt';
  const phpsecPw_SHA256 = 'sha256';
  const phpsecPw_SHA512 = 'sha512';

  public static $_method       = self::phpsecPw_PBKDF2;

  /**
   * Iteration count that PBKDF2 will use.
   * 8192 is just a little slower than a work load of 11 when using bcrypt.
   * Oh, slow is good.
   */
  public static $_pbkdf2_c     = 8192;
  public static $_pbkdf2_dkLen = 256;
  public static $_pbkdf2_prf   = self::phpsecPw_SHA512;

  /**
   * Create a hashed version of a password, safe for storage in a database.
   * This function return a json encoded array that can be stored directly
   * into a database. The array has the following layout:
   * array(
   *   'hash'      => The hash created from the password and a salt.
   *   'salt'      => The salt that was used along with the password to create the hash.
   *   'algo'      => The hashing algorithm used.
   * )
   *
   * @param string $password
   *   The password to hash.
   *
   * @return string
   *   Returns a json encoded array containing the password hash, salt and
   *   some meta data.
   */
  public static function hash($password) {
    $salt     = phpsecRand::bytes(64);
    switch (self::$_method) {
      case self::phpsecPw_PBKDF2:
        $hash = phpsecCrypt::pbkdf2($password, $salt, self::$_pbkdf2_c, self::$_pbkdf2_dkLen, self::$_pbkdf2_prf);
        /* phpsecCrypt::pbkdf2() returns a binary string. So let's base64 encode it.*/
        $hash = base64_encode($hash);
        /* We append the iteration count, derived key length and PRF as we need this later. */
        $algo = 'pbkdf2:' . self::$_pbkdf2_c . ':' . self::$_pbkdf2_dkLen . ':' . self::$_pbkdf2_prf;
        break;
      default:
        $injected = self::inject($password, $salt);
        $hash     = hash(self::$_method, $injected);
        $algo     = self::$_method;
    }


    $return = array(
      'hash' => $hash, 
      'salt' => base64_encode($salt), 
      'algo' => $algo,
    );
    return json_encode($return);
  }

  /**
   * Validate a user-supplied password against a stored password generated
   * using the phpsecPw::hash() method.
   *
   * @param string $password
   *   The password supplied by the user in the login form.
   *
   * @param string $dbPassword
   *   The json string fetched from the database, in the exact format
   *   as created by phpsecPw::hash().
   *
   * @return boolean
   *   True on password match, false otherwise.
   */
  public static function check($password, $dbPassword) {
    /**
     * Unserialize registered password array and validate it to ensure
     * we got a valid array.
     */
    $data = json_decode($dbPassword, true);

    $dataStructure = array(
      'hash' => true, 
      'salt' => true, 
      'algo' => true,
    );

    /* Check structure of array. */
    if ($data !== null && phpsec::arrayCheck($data, $dataStructure)) {

      /* Try to Base64 decode the salt.  base64_decode() will return false
       * if the string passed is not Base64 encoded. This way we can separate
       * binary salts from the old type of salts. */
      $decodedSalt = base64_decode($data['salt'], true);
      if ($decodedSalt !== false) {
        /* The salt was Base64 encoded. Use the decoded version. */
        $data['salt'] = $decodedSalt;
      }

      /**
       * We do a switch on the 6 first characters on the used hashing method.
       * This way we are able to catch when pbkdf2 is used, since this has
       * it's iteration count, derived key length and PRF attached to it:
       * "pbkdf2:iteration count:derived key length:PRF"
       */
      switch (substr($data['algo'], 0, 6)) {
        case self::phpsecPw_PBKDF2:
          /* As described above, we need to seperate out the iteration count
           * and derived key length. */
          list($method, $iterationCount, $dkLen, $prf) = explode(':', $data['algo']);
          /* Just to make sure anything fishy isn't going on. */
          if (!is_numeric($iterationCount) || !is_numeric($dkLen)) {
            return false;
          }

          /* Create a new derived key, with the iteration count and derived key length
           * that were used when generating the original dk. */
          $dk = phpsecCrypt::pbkdf2($password, $data['salt'], $iterationCount, $dkLen, $prf);

          /* Check the new dk against the old base64 encoded dk. */
          if ($dk === base64_decode($data['hash'])) {
            return true;
          }
          break;

        default:
          /* If not pbkdf2, we assume normal hash. */
          $pwInjected = self::inject($password, $data['salt']);
          /* Create a hash and see if it matches. */
          if (hash($data['algo'], $pwInjected) == $data['hash']) {
            return true;
          }
      }
    }
    else {
      /* Invalid array supplied. */
      phpsec::error('Invalid data supplied. Expected serialized array as returned by pwHash()');
    }
    return false;
  }

  /**
   * Inject a salt into a password to create the string to be hashed.
   * What we really do is to create an hash from the password, and retrieve
   * the first character from this hash. This is then converted to its decimal value.
   * We now have a number between 0 and 15. We use this to calculate where to place the salt.
   * We take the length of the password and dividing it by 16, and then we multiply this by the
   * number we got from the hash.
   *
   * @param string $password
   *   Plain-text password.
   *
   * @param string $salt
   *   Well, the salt to inject into the password.
   *
   * @return string
   *   Returns the salted password, ready to be hashed.
   *
   */
  private static function inject($password, $salt) {
    $hex = hexdec(substr(hash(phpsec::HASH_TYPE, $password), 0, 1));
    $len = strlen($password);
    $pos = floor($hex * ($len / 16));

    return substr($password, 0, $pos) . $salt . substr($password, $pos);
  }
}
?>

Copyright (c) 2011, 2012 Audun Larsen.

Drupal theme by Kiwi Themes.