* @category Horde * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 * @package Crypt_Blowfish */ /** * PBKDF2 (Password-Based Key Derivation Function 2) implementation (RFC * 2898; PKCS #5 v2.0). * * @author Michael Slusarz * @category Horde * @copyright 2015-2017 Horde LLC * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 * @package Crypt_Blowfish * @link https://defuse.ca/php-pbkdf2.htm pbkdf2 code released to the * public domain. */ class Horde_Crypt_Blowfish_Pbkdf2 { /** * Hash algorithm used to create key. * * @var string */ public $hashAlgo; /** * Number of iterations to use. * * @var integer */ public $iterations; /** * Salt. * * @var string */ public $salt; /** * The derived key. * * @var string */ protected $_key; /** * Constructor. * * @param string $pass The password. * @param string $key_length Length of the derived key (in bytes). * @param array $opts Additional options: * - algo: (string) Hash algorithm. * - i_count: (integer) Iteration count. * - salt: (string) The salt to use. */ public function __construct($pass, $key_length, array $opts = array()) { $this->iterations = isset($opts['i_count']) ? $opts['i_count'] : 16384; if (($key_length <= 0) || ($this->iterations <= 0)) { throw new InvalidArgumentException('Invalid arguments'); } $this->hashAlgo = isset($opts['algo']) ? $opts['algo'] : 'SHA256'; /* Nice to have, but salt does not need to be cryptographically * secure random value. */ $this->salt = isset($opts['salt']) ? $opts['salt'] : (function_exists('openssl_random_pseudo_bytes') ? openssl_random_pseudo_bytes($key_length) : substr(hash('sha512', new Horde_Support_Randomid(), true), 0, $key_length)); if (function_exists('hash_pbkdf2')) { $this->_key = hash_pbkdf2( $this->hashAlgo, $pass, $this->salt, $this->iterations, $key_length, true ); return; } $hash_length = strlen(hash($this->hashAlgo, '', true)); $block_count = ceil($key_length / $hash_length); $hash = ''; for ($i = 1; $i <= $block_count; ++$i) { // $i encoded as 4 bytes, big endian. $last = $this->salt . pack('N', $i); for ($j = 0; $j < $this->iterations; $j++) { $last = hash_hmac($this->hashAlgo, $last, $pass, true); if ($j) { $xorsum ^= $last; } else { $xorsum = $last; } } $hash .= $xorsum; } $this->_key = substr($hash, 0, $key_length); } /** */ public function __toString() { return $this->_key; } }