4 * This is a compatibility file for password_hash and password_verify for
5 * php systems prior to 5.5 that do not have password hashing built-in.
6 * This will export these two functions to the global namespace
9 defined('PASSWORD_BCRYPT') or define('PASSWORD_BCRYPT', 1);
11 defined('PASSWORD_DEFAULT') or define('PASSWORD_DEFAULT', PASSWORD_BCRYPT
);
13 if (! function_exists('password_hash')) {
15 * Hash the password using the specified algorithm
17 * @param string $password
18 * The password to hash
20 * The algorithm to use (Defined by PASSWORD_* constants)
21 * @param array $options
22 * The options for the algorithm to use
24 * @return s string|false The hashed password, or false on error.
26 function password_hash($password, $algo, $options = array())
28 if (! function_exists('crypt')) {
29 trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING
);
33 if (! is_string($password)) {
34 trigger_error("password_hash(): Password must be a string", E_USER_WARNING
);
38 if (! is_int($algo)) {
39 trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING
);
45 // Note that this is a C constant, but not exposed to PHP, so we don't define it here.
47 if (isset($options ['cost'])) {
48 $cost = $options ['cost'];
49 if ($cost < 4 ||
$cost > 31) {
50 trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING
);
55 $required_salt_len = 22;
56 $hash_format = sprintf("$2y$%02d$", $cost);
59 trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING
);
63 if (isset($options ['salt'])) {
64 switch (gettype($options ['salt'])) {
70 $salt = (string) $options ['salt'];
73 if (method_exists($options ['salt'], '__tostring')) {
74 $salt = (string) $options ['salt'];
78 //NOTE FALL-THROUGH CASE HERE. POSSIBLE BUG.
82 trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING
);
86 if (strlen($salt) < $required_salt_len) {
87 trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", strlen($salt), $required_salt_len), E_USER_WARNING
);
89 } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
90 $salt = str_replace('+', '.', base64_encode($salt));
93 $salt = __password_make_salt($required_salt_len);
96 $salt = substr($salt, 0, $required_salt_len);
98 $hash = $hash_format . $salt;
100 $ret = crypt($password, $hash);
102 if (! is_string($ret) ||
strlen($ret) < 13) {
110 if (! function_exists('password_get_info')) {
112 * Get information about the password hash.
113 * Returns an array of the information
114 * that was used to generate the password hash.
118 * 'algoName' => 'bcrypt',
119 * 'options' => array(
124 * @param string $hash
125 * The password hash to extract info from
127 * @return array The array of information about the hash.
129 function password_get_info($hash)
133 'algoName' => 'unknown',
134 'options' => array ()
136 if (substr($hash, 0, 4) == '$2y$' && strlen($hash) == 60) {
137 $return ['algo'] = PASSWORD_BCRYPT
;
138 $return ['algoName'] = 'bcrypt';
139 list ( $cost ) = sscanf($hash, "$2y$%d$");
140 $return ['options'] ['cost'] = $cost;
147 if (! function_exists('password_needs_rehash')) {
149 * Determine if the password hash needs to be rehashed according to the options provided
151 * If the answer is true, after validating the password using password_verify, rehash it.
153 * @param string $hash
156 * The algorithm used for new password hashes
157 * @param array $options
158 * The options array passed to password_hash
160 * @return boolean True if the password needs to be rehashed.
162 function password_needs_rehash($hash, $algo, array $options = array())
164 $info = password_get_info($hash);
165 if ($info ['algo'] != $algo) {
170 case PASSWORD_BCRYPT
:
171 $cost = isset($options ['cost']) ?
$options ['cost'] : 10;
172 if ($cost != $info ['options'] ['cost']) {
182 if (! function_exists('password_verify')) {
184 * Verify a password against a hash using a timing attack resistant approach
186 * @param string $password
187 * The password to verify
188 * @param string $hash
189 * The hash to verify against
191 * @return boolean If the password matches the hash
193 function password_verify($password, $hash)
195 if (! function_exists('crypt')) {
196 trigger_error("Crypt must be loaded for password_create to function", E_USER_WARNING
);
200 $ret = crypt($password, $hash);
201 if (! is_string($ret) ||
strlen($ret) != strlen($hash)) {
206 for ($i = 0; $i < strlen($ret); $i++
) {
207 $status |
= (ord($ret [$i]) ^
ord($hash [$i]));
210 return $status === 0;
215 * Function to make a salt
217 * DO NOT USE THIS FUNCTION DIRECTLY
222 function __password_make_salt($length)
225 trigger_error(sprintf("Length cannot be less than or equal zero: %d", $length), E_USER_WARNING
);
230 $raw_length = (int) ($length * 3 / 4 +
1);
231 $buffer_valid = false;
232 if (function_exists('mcrypt_create_iv')) {
233 $buffer = mcrypt_create_iv($raw_length, MCRYPT_DEV_URANDOM
);
235 $buffer_valid = true;
239 if (! $buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
240 $buffer = openssl_random_pseudo_bytes($raw_length);
242 $buffer_valid = true;
246 if (! $buffer_valid && file_exists('/dev/urandom')) {
247 $f = @fopen
('/dev/urandom', 'r');
249 $read = strlen($buffer);
250 while ($read < $raw_length) {
251 $buffer .= fread($f, $raw_length - $read);
252 $read = strlen($buffer);
256 if ($read >= $raw_length) {
257 $buffer_valid = true;
262 if (! $buffer_valid) {
263 for ($i = 0; $i < $raw_length; $i++
) {
264 $buffer .= chr(mt_rand(0, 255));
268 $buffer = str_replace('+', '.', base64_encode($buffer));
269 return substr($buffer, 0, $length);