PageRenderTime 7ms CodeModel.GetById 1ms app.highlight 3ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Phpass/Hash/Adapter/Pbkdf2.php

http://github.com/rchouinard/phpass
PHP | 159 lines | 57 code | 21 blank | 81 comment | 12 complexity | f518519fc537e956e3138fa15fff9e4c MD5 | raw file
  1<?php
  2/**
  3 * PHP Password Library
  4 *
  5 * @package PHPass\Hashes
  6 * @category Cryptography
  7 * @author Ryan Chouinard <rchouinard at gmail.com>
  8 * @license http://www.opensource.org/licenses/mit-license.html MIT License
  9 * @link https://github.com/rchouinard/phpass Project at GitHub
 10 */
 11
 12/**
 13 * @namespace
 14 */
 15namespace Phpass\Hash\Adapter;
 16
 17/**
 18 * PBKDF2 hash adapter
 19 *
 20 * @package PHPass\Hashes
 21 * @category Cryptography
 22 * @author Ryan Chouinard <rchouinard at gmail.com>
 23 * @license http://www.opensource.org/licenses/mit-license.html MIT License
 24 * @link https://github.com/rchouinard/phpass Project at GitHub
 25 */
 26class Pbkdf2 extends Base
 27{
 28
 29    /**
 30     * Hashing algorithm used by the PBKDF2 implementation.
 31     *
 32     * @var string
 33     */
 34    protected $_algo = 'sha256';
 35
 36    /**
 37     * Return a hashed string.
 38     *
 39     * @param string $password
 40     *   The string to be hashed.
 41     * @param string $salt
 42     *   An optional salt string to base the hashing on. If not provided, the
 43     *   adapter will generate a new secure salt value.
 44     * @return string
 45     *   Returns the hashed string.
 46     * @see Adapter::crypt()
 47     */
 48    public function crypt($password, $salt = null)
 49    {
 50        $setting = $salt;
 51        if (!$setting) {
 52            $setting = $this->genSalt();
 53        }
 54
 55        // Return blowfish error string *0 or *1 on failure
 56        // Portable adapter does this, so we do it here to remain consistent
 57        $output = '*0';
 58        if (substr($setting, 0, 2) == $output) {
 59            $output = '*1';
 60        }
 61
 62        if (substr($setting, 0, 6) != '$p5v2$') {
 63            return $output;
 64        }
 65
 66        $countLog2 = $countLog2 = strpos($this->_itoa64, $setting[6]);
 67        if ($countLog2 < 0 || $countLog2 > 30) {
 68            return $output;
 69        }
 70        $count = 1 << $countLog2;
 71
 72        $salt = substr($setting, 7, 8);
 73        if (strlen($salt) != 8) {
 74            return $output;
 75        }
 76
 77        $hash = $this->_pbkdf2($password, $salt, $count, 24, $this->_algo);
 78
 79        $output = substr($setting, 0, 16);
 80        $output .= $this->_encode64($hash, 24);
 81
 82        return $output;
 83    }
 84
 85    /**
 86     * Generate a salt string suitable for the crypt() method.
 87     *
 88     * Pbkdf2::genSalt() generates a 16-character salt string which can be
 89     * passed to crypt(). The salt consists of a string beginning with a
 90     * compatible hash identifier, one byte of iteration count, and an
 91     * 8-byte encoded salt followed by "$".
 92     *
 93     * @param string $input
 94     *   Optional random data to be used when generating the salt. Must contain
 95     *   at least 6 bytes of data.
 96     * @return string
 97     *   Returns the generated salt string.
 98     * @see Adapter::genSalt()
 99     */
100    public function genSalt($input = null)
101    {
102        if (!$input) {
103            $input = $this->_getRandomBytes(6);
104        }
105
106        // PKCS #5, version 2
107        // Python implementation uses $p5k2$, but we're not using a compatible
108        // string. https://www.dlitz.net/software/python-pbkdf2/
109        $output = '$p5v2$';
110
111        // Iteration count between 1 and 1,073,741,824
112        $output .= $this->_itoa64[min(max($this->_iterationCountLog2, 0), 30)];
113
114        // 8-byte (64-bit) salt value, as recommended by the standard
115        $output .= $this->_encode64($input, 6);
116
117        // $p5v2$CSSSSSSSS$
118        return $output . '$';
119    }
120
121    /**
122     * Internal implementation of PKCS #5 v2.0.
123     *
124     * This implementation passes tests using vectors given in RFC 6070 s.2,
125     * PBKDF2 HMAC-SHA1 Test Vectors. Vectors given for PBKDF2 HMAC-SHA2 at
126     * http://stackoverflow.com/questions/5130513 also pass.
127     *
128     * @param string $password
129     *   The string to be hashed.
130     * @param string $salt
131     *   Salt value used by the HMAC function.
132     * @param integer $iterationCount
133     *   Number of iterations for key stretching.
134     * @param integer $keyLength
135     *   Length of derived key.
136     * @param string $algo
137     *   Algorithm to use when generating HMAC digest.
138     * @return string
139     *   Returns the raw hash string.
140     */
141    protected function _pbkdf2($password, $salt, $iterationCount = 1000, $keyLength = 20, $algo = 'sha1')
142    {
143        $hashLength = strlen(hash($algo, null, true));
144        $keyBlocks = ceil($keyLength / $hashLength);
145        $derivedKey = '';
146
147        for ($block = 1; $block <= $keyBlocks; ++$block) {
148            $iteratedBlock = $currentBlock = hash_hmac($algo, $salt . pack('N', $block), $password, true);
149            for ($iteration = 1; $iteration < $iterationCount; ++$iteration) {
150                $iteratedBlock ^= $currentBlock = hash_hmac($algo, $currentBlock, $password, true);
151            }
152
153            $derivedKey .= $iteratedBlock;
154        }
155
156        return substr($derivedKey, 0, $keyLength);
157    }
158
159}