$value) { switch (strtolower($key)) { case 'salt': $this->setSalt($value); break; case 'cost': $this->setCost($value); break; } } } } /** * Bcrypt * * @param string $password * @throws Exception\RuntimeException * @return string */ public function create($password) { if (empty($this->salt)) { $salt = Rand::getBytes(self::MIN_SALT_SIZE); } else { $salt = $this->salt; } $salt64 = substr(str_replace('+', '.', base64_encode($salt)), 0, 22); /** * Check for security flaw in the bcrypt implementation used by crypt() * @see http://php.net/security/crypt_blowfish.php */ if ((version_compare(PHP_VERSION, '5.3.7') >= 0) && !$this->backwardCompatibility) { $prefix = '$2y$'; } else { $prefix = '$2a$'; // check if the password contains 8-bit character if (preg_match('/[\x80-\xFF]/', $password)) { throw new Exception\RuntimeException( 'The bcrypt implementation used by PHP can contain a security flaw ' . 'using password with 8-bit character. ' . 'We suggest to upgrade to PHP 5.3.7+ or use passwords with only 7-bit characters' ); } } $hash = crypt($password, $prefix . $this->cost . '$' . $salt64); if (strlen($hash) < 13) { throw new Exception\RuntimeException('Error during the bcrypt generation'); } return $hash; } /** * Verify if a password is correct against an hash value * * @param string $password * @param string $hash * @throws Exception\RuntimeException when the hash is unable to be processed * @return bool */ public function verify($password, $hash) { $result = crypt($password, $hash); if ($result === $hash) { return true; } if (strlen($result) <= 13) { /* This should only happen if the algorithm that generated hash is * either unsupported by this version of crypt(), or is invalid. * * An example of when this can happen, is if you generate * non-backwards-compatible hashes on 5.3.7+, and then try to verify * them on < 5.3.7. * * This is needed, because version comparisons are not possible due * to back-ported functionality by some distributions. */ throw new Exception\RuntimeException( 'The supplied password hash could not be verified. Please check ' . 'backwards compatibility settings.' ); } return false; } /** * Set the cost parameter * * @param int|string $cost * @throws Exception\InvalidArgumentException * @return Bcrypt */ public function setCost($cost) { if (!empty($cost)) { $cost = (int) $cost; if ($cost < 4 || $cost > 31) { throw new Exception\InvalidArgumentException( 'The cost parameter of bcrypt must be in range 04-31' ); } $this->cost = sprintf('%1$02d', $cost); } return $this; } /** * Get the cost parameter * * @return string */ public function getCost() { return $this->cost; } /** * Set the salt value * * @param string $salt * @throws Exception\InvalidArgumentException * @return Bcrypt */ public function setSalt($salt) { if (strlen($salt) < self::MIN_SALT_SIZE) { throw new Exception\InvalidArgumentException( 'The length of the salt must be at least ' . self::MIN_SALT_SIZE . ' bytes' ); } $this->salt = $salt; return $this; } /** * Get the salt value * * @return string */ public function getSalt() { return $this->salt; } /** * Set the backward compatibility $2a$ instead of $2y$ for PHP 5.3.7+ * * @param bool $value * @return Bcrypt */ public function setBackwardCompatibility($value) { $this->backwardCompatibility = (bool) $value; return $this; } /** * Get the backward compatibility * * @return bool */ public function getBackwardCompatibility() { return $this->backwardCompatibility; } }