12 if (!defined(
'PASSWORD_BCRYPT')) {
19 define(
'PASSWORD_BCRYPT', 1);
20 define(
'PASSWORD_DEFAULT', PASSWORD_BCRYPT);
21 define(
'PASSWORD_BCRYPT_DEFAULT_COST', 10);
24 if (!function_exists(
'password_hash')) {
36 function password_hash($password, $algo, array $options = []) {
37 if (!function_exists(
'crypt')) {
38 trigger_error(
'Crypt must be loaded for password_hash to function', E_USER_WARNING);
41 if (
null === $password || is_int($password)) {
42 $password = (string) $password;
44 if (!is_string($password)) {
45 trigger_error(
'password_hash(): Password must be a string', E_USER_WARNING);
49 trigger_error(
'password_hash() expects parameter 2 to be long, ' . gettype($algo) .
' given', E_USER_WARNING);
55 $cost = PASSWORD_BCRYPT_DEFAULT_COST;
56 if (isset($options[
'cost'])) {
57 $cost = (int) $options[
'cost'];
58 if ($cost < 4 || $cost > 31) {
59 trigger_error(sprintf(
'password_hash(): Invalid bcrypt cost parameter specified: %d', $cost), E_USER_WARNING);
66 $required_salt_len = 22;
67 $hash_format = sprintf(
'$2y$%02d$', $cost);
72 trigger_error(sprintf(
'password_hash(): Unknown password hashing algorithm: %s', $algo), E_USER_WARNING);
75 $salt_req_encoding =
false;
76 if (isset($options[
'salt'])) {
77 switch (gettype($options[
'salt'])) {
83 $salt = (string) $options[
'salt'];
86 if (method_exists($options[
'salt'],
'__tostring')) {
87 $salt = (string) $options[
'salt'];
93 trigger_error(
'password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
96 if (PasswordCompat\binary\_strlen($salt) < $required_salt_len) {
97 trigger_error(sprintf(
'password_hash(): Provided salt is too short: %d expecting %d', PasswordCompat\binary\_strlen($salt), $required_salt_len), E_USER_WARNING);
99 } elseif (0 == preg_match(
'#^[a-zA-Z0-9./]+$#D', $salt)) {
100 $salt_req_encoding =
true;
104 $buffer_valid =
false;
105 if (function_exists(
'mcrypt_create_iv') && !defined(
'PHALANGER')) {
106 $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM);
108 $buffer_valid =
true;
111 if (!$buffer_valid && function_exists(
'openssl_random_pseudo_bytes')) {
113 $buffer = openssl_random_pseudo_bytes($raw_salt_len, $strong);
114 if ($buffer && $strong) {
115 $buffer_valid =
true;
118 if (!$buffer_valid && @is_readable(
'/dev/urandom')) {
119 $file = fopen(
'/dev/urandom',
'r');
122 while ($read < $raw_salt_len) {
123 $local_buffer .= fread($file, $raw_salt_len - $read);
124 $read = PasswordCompat\binary\_strlen($local_buffer);
127 if ($read >= $raw_salt_len) {
128 $buffer_valid =
true;
130 $buffer = str_pad($buffer, $raw_salt_len,
"\0") ^ str_pad($local_buffer, $raw_salt_len,
"\0");
132 if (!$buffer_valid || PasswordCompat\binary\_strlen($buffer) < $raw_salt_len) {
133 $buffer_length = PasswordCompat\binary\_strlen($buffer);
134 for ($i = 0; $i < $raw_salt_len; $i++) {
135 if ($i < $buffer_length) {
136 $buffer[$i] = $buffer[$i] ^ chr(random_int(0, 255));
138 $buffer .= chr(random_int(0, 255));
143 $salt_req_encoding =
true;
145 if ($salt_req_encoding) {
148 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
150 './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
152 $base64_string = base64_encode($salt);
153 $salt = strtr(rtrim($base64_string,
'='), $base64_digits, $bcrypt64_digits);
155 $salt = PasswordCompat\binary\_substr($salt, 0, $required_salt_len);
157 $hash = $hash_format . $salt;
159 $ret = crypt($password, $hash);
161 if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != $resultLength) {
184 function password_get_info($hash) {
187 'algoName' =>
'unknown',
190 if (
'$2y$' == PasswordCompat\binary\_substr($hash, 0, 4) && 60 == PasswordCompat\binary\_strlen($hash)) {
191 $return[
'algo'] = PASSWORD_BCRYPT;
192 $return[
'algoName'] =
'bcrypt';
193 [ $cost ] = sscanf( $hash,
'$2y$%d$' );
194 $return[
'options'][
'cost'] = $cost;
210 function password_needs_rehash($hash, $algo, array $options = []) {
211 $info = password_get_info($hash);
212 if ($info[
'algo'] !== (
int) $algo) {
216 case PASSWORD_BCRYPT:
217 $cost = isset($options[
'cost']) ? (int) $options[
'cost'] : PASSWORD_BCRYPT_DEFAULT_COST;
218 if ($cost !== $info[
'options'][
'cost']) {
234 function password_verify($password, $hash) {
235 if (!function_exists(
'crypt')) {
236 trigger_error(
'Crypt must be loaded for password_verify to function', E_USER_WARNING);
239 $ret = crypt($password, $hash);
240 if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != PasswordCompat\binary\_strlen($hash) || PasswordCompat\binary\_strlen($ret) <= 13) {
245 for ($i = 0; $i < PasswordCompat\binary\_strlen($ret); $i++) {
246 $status |= (ord($ret[$i]) ^ ord($hash[$i]));
249 return 0 === $status;
255namespace PasswordCompat\binary {
257 if (!function_exists(
'PasswordCompat\\binary\\_strlen')) {
271 function _strlen($binary_string) {
272 if (function_exists(
'mb_strlen')) {
273 return mb_strlen($binary_string,
'8bit');
275 return strlen($binary_string);
290 function _substr($binary_string, $start, $length) {
291 if (function_exists(
'mb_substr')) {
292 return mb_substr($binary_string, $start, $length,
'8bit');
294 return substr($binary_string, $start, $length);
305 if (
null === $pass) {
306 if (function_exists(
'crypt')) {
307 $hash =
'$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';
308 $test = crypt(
'password', $hash);
309 $pass = $test == $hash;