3 * This is a compatibility file for password_hash and password_verify for
4 * php systems prior to 5.5 that do not have password hashing built-in.
5 * This will export these two functions to the global namespace
7 defined ( 'PASSWORD_BCRYPT' ) or define ( 'PASSWORD_BCRYPT', 1 );
9 defined ( 'PASSWORD_DEFAULT' ) or define ( 'PASSWORD_DEFAULT', PASSWORD_BCRYPT
);
11 if (! function_exists ( 'password_hash' )) {
13 * Hash the password using the specified algorithm
15 * @param string $password
16 * The password to hash
18 * The algorithm to use (Defined by PASSWORD_* constants)
19 * @param array $options
20 * The options for the algorithm to use
22 * @return s string|false The hashed password, or false on error.
24 function password_hash($password, $algo, $options = array()) {
25 if (! function_exists ( 'crypt' )) {
26 trigger_error ( "Crypt must be loaded for password_hash to function", E_USER_WARNING
);
29 if (! is_string ( $password )) {
30 trigger_error ( "password_hash(): Password must be a string", E_USER_WARNING
);
33 if (! is_int ( $algo )) {
34 trigger_error ( "password_hash() expects parameter 2 to be long, " . gettype ( $algo ) . " given", E_USER_WARNING
);
38 case PASSWORD_BCRYPT
:
39 // Note that this is a C constant, but not exposed to PHP, so we don't define it here.
41 if (isset ( $options ['cost'] )) {
42 $cost = $options ['cost'];
43 if ($cost < 4 ||
$cost > 31) {
44 trigger_error ( sprintf ( "password_hash(): Invalid bcrypt cost parameter specified: %d", $cost ), E_USER_WARNING
);
48 $required_salt_len = 22;
49 $hash_format = sprintf ( "$2y$%02d$", $cost );
52 trigger_error ( sprintf ( "password_hash(): Unknown password hashing algorithm: %s", $algo ), E_USER_WARNING
);
55 if (isset ( $options ['salt'] )) {
56 switch (gettype ( $options ['salt'] )) {
62 $salt = ( string ) $options ['salt'];
65 if (method_exists ( $options ['salt'], '__tostring' )) {
66 $salt = ( string ) $options ['salt'];
72 trigger_error ( 'password_hash(): Non-string salt parameter supplied', E_USER_WARNING
);
75 if (strlen ( $salt ) < $required_salt_len) {
76 trigger_error ( sprintf ( "password_hash(): Provided salt is too short: %d expecting %d", strlen ( $salt ), $required_salt_len ), E_USER_WARNING
);
78 } elseif (0 == preg_match ( '#^[a-zA-Z0-9./]+$#D', $salt )) {
79 $salt = str_replace ( '+', '.', base64_encode ( $salt ) );
82 $salt = __password_make_salt ( $required_salt_len );
84 $salt = substr ( $salt, 0, $required_salt_len );
86 $hash = $hash_format . $salt;
88 $ret = crypt ( $password, $hash );
90 if (! is_string ( $ret ) ||
strlen ( $ret ) < 13) {
98 if (! function_exists ( 'password_get_info' )) {
100 * Get information about the password hash.
101 * Returns an array of the information
102 * that was used to generate the password hash.
106 * 'algoName' => 'bcrypt',
107 * 'options' => array(
112 * @param string $hash
113 * The password hash to extract info from
115 * @return array The array of information about the hash.
117 function password_get_info($hash) {
120 'algoName' => 'unknown',
121 'options' => array ()
123 if (substr ( $hash, 0, 4 ) == '$2y$' && strlen ( $hash ) == 60) {
124 $return ['algo'] = PASSWORD_BCRYPT
;
125 $return ['algoName'] = 'bcrypt';
126 list ( $cost ) = sscanf ( $hash, "$2y$%d$" );
127 $return ['options'] ['cost'] = $cost;
133 if (! function_exists ( 'password_needs_rehash' )) {
135 * Determine if the password hash needs to be rehashed according to the options provided
137 * If the answer is true, after validating the password using password_verify, rehash it.
139 * @param string $hash
142 * The algorithm used for new password hashes
143 * @param array $options
144 * The options array passed to password_hash
146 * @return boolean True if the password needs to be rehashed.
148 function password_needs_rehash($hash, $algo, array $options = array()) {
149 $info = password_get_info ( $hash );
150 if ($info ['algo'] != $algo) {
154 case PASSWORD_BCRYPT
:
155 $cost = isset ( $options ['cost'] ) ?
$options ['cost'] : 10;
156 if ($cost != $info ['options'] ['cost']) {
165 if (! function_exists ( 'password_verify' )) {
167 * Verify a password against a hash using a timing attack resistant approach
169 * @param string $password
170 * The password to verify
171 * @param string $hash
172 * The hash to verify against
174 * @return boolean If the password matches the hash
176 function password_verify($password, $hash) {
177 if (! function_exists ( 'crypt' )) {
178 trigger_error ( "Crypt must be loaded for password_create to function", E_USER_WARNING
);
181 $ret = crypt ( $password, $hash );
182 if (! is_string ( $ret ) ||
strlen ( $ret ) != strlen ( $hash )) {
187 for($i = 0; $i < strlen ( $ret ); $i ++
) {
188 $status |
= (ord ( $ret [$i] ) ^
ord ( $hash [$i] ));
191 return $status === 0;
196 * Function to make a salt
198 * DO NOT USE THIS FUNCTION DIRECTLY
203 function __password_make_salt($length) {
205 trigger_error ( sprintf ( "Length cannot be less than or equal zero: %d", $length ), E_USER_WARNING
);
209 $raw_length = ( int ) ($length * 3 / 4 +
1);
210 $buffer_valid = false;
211 if (function_exists ( 'mcrypt_create_iv' )) {
212 $buffer = mcrypt_create_iv ( $raw_length, MCRYPT_DEV_URANDOM
);
214 $buffer_valid = true;
217 if (! $buffer_valid && function_exists ( 'openssl_random_pseudo_bytes' )) {
218 $buffer = openssl_random_pseudo_bytes ( $raw_length );
220 $buffer_valid = true;
223 if (! $buffer_valid && file_exists ( '/dev/urandom' )) {
224 $f = @fopen
( '/dev/urandom', 'r' );
226 $read = strlen ( $buffer );
227 while ( $read < $raw_length ) {
228 $buffer .= fread ( $f, $raw_length - $read );
229 $read = strlen ( $buffer );
232 if ($read >= $raw_length) {
233 $buffer_valid = true;
237 if (! $buffer_valid) {
238 for($i = 0; $i < $raw_length; $i ++
) {
239 $buffer .= chr ( mt_rand ( 0, 255 ) );
242 $buffer = str_replace ( '+', '.', base64_encode ( $buffer ) );
243 return substr ( $buffer, 0, $length );