3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
10 namespace Zend\Crypt\Password
;
14 use Zend\Stdlib\ArrayUtils
;
17 * Bcrypt algorithm using crypt() function of PHP
19 class Bcrypt
implements PasswordInterface
21 const MIN_SALT_SIZE
= 16;
26 protected $cost = '14';
36 protected $backwardCompatibility = false;
41 * @param array|Traversable $options
42 * @throws Exception\InvalidArgumentException
44 public function __construct($options = array())
46 if (!empty($options)) {
47 if ($options instanceof Traversable
) {
48 $options = ArrayUtils
::iteratorToArray($options);
49 } elseif (!is_array($options)) {
50 throw new Exception\
InvalidArgumentException(
51 'The options parameter must be an array or a Traversable'
54 foreach ($options as $key => $value) {
55 switch (strtolower($key)) {
57 $this->setSalt($value);
60 $this->setCost($value);
70 * @param string $password
71 * @throws Exception\RuntimeException
74 public function create($password)
76 if (empty($this->salt
)) {
77 $salt = Rand
::getBytes(self
::MIN_SALT_SIZE
);
81 $salt64 = substr(str_replace('+', '.', base64_encode($salt)), 0, 22);
83 * Check for security flaw in the bcrypt implementation used by crypt()
84 * @see http://php.net/security/crypt_blowfish.php
86 if ((version_compare(PHP_VERSION
, '5.3.7') >= 0) && !$this->backwardCompatibility
) {
90 // check if the password contains 8-bit character
91 if (preg_match('/[\x80-\xFF]/', $password)) {
92 throw new Exception\
RuntimeException(
93 'The bcrypt implementation used by PHP can contain a security flaw ' .
94 'using password with 8-bit character. ' .
95 'We suggest to upgrade to PHP 5.3.7+ or use passwords with only 7-bit characters'
99 $hash = crypt($password, $prefix . $this->cost
. '$' . $salt64);
100 if (strlen($hash) < 13) {
101 throw new Exception\
RuntimeException('Error during the bcrypt generation');
107 * Verify if a password is correct against an hash value
109 * @param string $password
110 * @param string $hash
111 * @throws Exception\RuntimeException when the hash is unable to be processed
114 public function verify($password, $hash)
116 $result = crypt($password, $hash);
117 if ($result === $hash) {
120 if (strlen($result) <= 13) {
121 /* This should only happen if the algorithm that generated hash is
122 * either unsupported by this version of crypt(), or is invalid.
124 * An example of when this can happen, is if you generate
125 * non-backwards-compatible hashes on 5.3.7+, and then try to verify
128 * This is needed, because version comparisons are not possible due
129 * to back-ported functionality by some distributions.
131 throw new Exception\
RuntimeException(
132 'The supplied password hash could not be verified. Please check ' .
133 'backwards compatibility settings.'
140 * Set the cost parameter
142 * @param int|string $cost
143 * @throws Exception\InvalidArgumentException
146 public function setCost($cost)
150 if ($cost < 4 ||
$cost > 31) {
151 throw new Exception\
InvalidArgumentException(
152 'The cost parameter of bcrypt must be in range 04-31'
155 $this->cost
= sprintf('%1$02d', $cost);
161 * Get the cost parameter
165 public function getCost()
173 * @param string $salt
174 * @throws Exception\InvalidArgumentException
177 public function setSalt($salt)
179 if (strlen($salt) < self
::MIN_SALT_SIZE
) {
180 throw new Exception\
InvalidArgumentException(
181 'The length of the salt must be at least ' . self
::MIN_SALT_SIZE
. ' bytes'
193 public function getSalt()
199 * Set the backward compatibility $2a$ instead of $2y$ for PHP 5.3.7+
204 public function setBackwardCompatibility($value)
206 $this->backwardCompatibility
= (bool) $value;
211 * Get the backward compatibility
215 public function getBackwardCompatibility()
217 return $this->backwardCompatibility
;