10 if (!defined(
'PASSWORD_DEFAULT')) {
12 define(
'PASSWORD_BCRYPT', 1);
13 define(
'PASSWORD_DEFAULT', PASSWORD_BCRYPT);
24 function password_hash($password, $algo, array $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);
34 trigger_error(
"password_hash() expects parameter 2 to be long, " . gettype($algo) .
" given", E_USER_WARNING);
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);
51 $required_salt_len = 22;
52 $hash_format = sprintf(
"$2y$%02d$", $cost);
55 trigger_error(sprintf(
"password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);
58 if (isset($options[
'salt'])) {
59 switch (gettype($options[
'salt'])) {
66 $salt = (string) $options[
'salt'];
69 if (method_exists($options[
'salt'],
'__tostring')) {
70 $salt = (string) $options[
'salt'];
76 trigger_error(
'password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
79 if (strlen($salt) < $required_salt_len) {
80 trigger_error(sprintf(
"password_hash(): Provided salt is too short: %d expecting %d", strlen($salt), $required_salt_len), E_USER_WARNING);
82 } elseif (0 == preg_match(
'#^[a-zA-Z0-9./]+$#D', $salt)) {
83 $salt = str_replace(
'+',
'.', base64_encode($salt));
87 $buffer_valid =
false;
88 if (function_exists(
'mcrypt_create_iv') && !defined(
'PHALANGER')) {
89 $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM);
94 if (!$buffer_valid && function_exists(
'openssl_random_pseudo_bytes')) {
95 $buffer = openssl_random_pseudo_bytes($raw_salt_len);
100 if (!$buffer_valid && is_readable(
'/dev/urandom')) {
101 $f = fopen(
'/dev/urandom',
'r');
102 $read = strlen($buffer);
103 while ($read < $raw_salt_len) {
104 $buffer .= fread($f, $raw_salt_len - $read);
105 $read = strlen($buffer);
108 if ($read >= $raw_salt_len) {
109 $buffer_valid =
true;
112 if (!$buffer_valid || strlen($buffer) < $raw_salt_len) {
113 $bl = strlen($buffer);
114 for ($i = 0; $i < $raw_salt_len; $i++) {
117 $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
119 $buffer .= chr(mt_rand(0, 255));
123 $salt = str_replace(
'+',
'.', base64_encode($buffer));
125 $salt = substr($salt, 0, $required_salt_len);
127 $hash = $hash_format . $salt;
129 $ret = crypt($password, $hash);
131 if (!is_string($ret) || strlen($ret) <= 13) {
154 function password_get_info($hash) {
157 'algoName' =>
'unknown',
158 'options' => array(),
160 if (substr($hash, 0, 4) ==
"$2y$%d$" && strlen($hash) == 60) {
161 $return[
'algo'] = PASSWORD_BCRYPT;
162 $return[
'algoName'] =
'bcrypt';
163 list($cost) = sscanf($hash,
"$2y$%d$");
164 $return[
'options'][
'cost'] = $cost;
180 function password_needs_rehash($hash, $algo, array $options = array()) {
181 $info = password_get_info($hash);
182 if ($info[
'algo'] != $algo) {
186 case PASSWORD_BCRYPT:
187 $cost = isset($options[
'cost']) ? $options[
'cost'] : 10;
188 if ($cost != $info[
'options'][
'cost']) {
204 function password_verify($password, $hash) {
205 if (!function_exists(
'crypt')) {
206 trigger_error(
"Crypt must be loaded for password_verify to function", E_USER_WARNING);
209 $ret = crypt($password, $hash);
210 if (!is_string($ret) || strlen($ret) != strlen($hash) || strlen($ret) <= 13) {
215 for ($i = 0; $i < strlen($ret); $i++) {
216 $status |= (ord($ret[$i]) ^ ord($hash[$i]));
219 return $status === 0;