3 * The Blowfish password hashing implementation
5 * Use this class to generate and validate Blowfish password hashes.
9 * @category PHPCryptLib
11 * @subpackage Implementation
12 * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
13 * @copyright 2011 The Authors
14 * @license http://www.opensource.org/licenses/mit-license.html MIT License
15 * @version Build @@version@@
18 namespace CryptLib\Password\Implementation
;
20 use CryptLib\Random\Factory
as RandomFactory
;
23 * The Blowfish password hashing implementation
25 * Use this class to generate and validate Blowfish password hashes.
27 * @category PHPCryptLib
29 * @subpackage Implementation
30 * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
32 class Blowfish
implements \CryptLib\Password\Password
{
35 * @var Generator The random generator to use for seeds
37 protected $generator = null;
40 * @var int The number of iterations to perform (base 2)
42 protected $iterations = 10;
45 * Determine if the hash was made with this method
47 * @param string $hash The hashed data to check
49 * @return boolean Was the hash created by this method
51 public static function detect($hash) {
52 static $regex = '/^\$2[ay]\$(0[4-9]|[1-2][0-9]|3[0-1])\$[a-zA-Z0-9.\/]{53}/';
53 return 1 == preg_match($regex, $hash);
57 * Return the prefix used by this hashing method
59 * @return string The prefix used
61 public static function getPrefix() {
62 if (version_compare(PHP_VERSION
, '5.3.7') >= 0) {
70 * Load an instance of the class based upon the supplied hash
72 * @param string $hash The hash to load from
74 * @return Password the created instance
75 * @throws InvalidArgumentException if the hash wasn't created here
77 public static function loadFromHash($hash) {
78 if (!static::detect($hash)) {
79 throw new \
InvalidArgumentException('Hash Not Created Here');
81 list(, , $iterations) = explode('$', $hash, 4);
82 return new static((int) $iterations);
86 * Build a new instance
88 * @param int $iterations The number of times to iterate the hash
89 * @param Generator $generator The random generator to use for seeds
93 public function __construct(
95 \CryptLib\Random\Generator
$generator = null
97 if ($iterations > 31 ||
$iterations < 4) {
98 throw new \
InvalidArgumentException('Invalid Iteration Count Supplied');
100 $this->iterations
= $iterations;
101 if (is_null($generator)) {
102 $random = new RandomFactory();
103 $generator = $random->getMediumStrengthGenerator();
105 $this->generator
= $generator;
109 * Create a password hash for a given plain text password
111 * @param string $password The password to hash
113 * @return string The formatted password hash
115 public function create($password) {
117 * Check for security flaw in the bcrypt implementation used by crypt()
118 * @see http://php.net/security/crypt_blowfish.php
120 $match = preg_match('/[\x80-\xFF]/', $password);
121 if (version_compare(PHP_VERSION
, '5.3.7', '<') && $match) {
122 throw new \
RuntimeException(
123 'The bcrypt implementation used by PHP contains a security flaw ' .
124 'for password with 8-bit character. We suggest to upgrade to ' .
125 'PHP 5.3.7+ or use passwords with only 7-bit characters'
128 $salt = $this->to64($this->generator
->generate(16));
129 $prefix = static::getPrefix();
130 $prefix .= str_pad($this->iterations
, 2, '0', STR_PAD_LEFT
);
131 $saltstring = $prefix . '$' . $salt;
132 $result = crypt($password, $saltstring);
133 if ($result[0] == '*') {
134 //@codeCoverageIgnoreStart
135 throw new \
RuntimeException('Password Could Not Be Created');
136 //@codeCoverageIgnoreEnd
142 * Verify a password hash against a given plain text password
144 * @param string $password The password to hash
145 * @param string $hash The supplied ahsh to validate
147 * @return boolean Does the password validate against the hash
149 public function verify($password, $hash) {
150 if (!static::detect($hash)) {
151 throw new \
InvalidArgumentException(
152 'The hash was not created here, we cannot verify it'
155 $test = crypt($password, $hash);
156 return $test == $hash;
160 * Convert the input number to a base64 number of the specified size
162 * @param int $input The number to convert
164 * @return string The converted representation
166 protected function to64($input) {
169 $itoa = './ABCDEFGHIJKLMNOPQRSTUVWXYZ'
170 . 'abcdefghijklmnopqrstuvwxyz0123456789';
173 $size = strlen($input);
176 $cval1 = ord($input[$ictr++
]);
177 $output .= $itoa[$cval1 >> 2];
178 $cval1 = ($cval1 & 0x03) << 4;
179 if ($ictr >= $size) {
180 $output .= $itoa[$cval1];
183 $cval2 = ord($input[$ictr++
]);
184 $cval1 |
= $cval2 >> 4;
185 $output .= $itoa[$cval1];
186 $cval1 = ($cval2 & 0x0f) << 2;
187 $cval2 = ord($input[$ictr++
]);
188 $cval1 |
= $cval2 >> 6;
189 $output .= $itoa[$cval1];
190 $output .= $itoa[$cval2 & 0x3f];