1 <?php
2 3 4 5 6 7 8
9
10 namespace {
11
12 if (!defined('PASSWORD_BCRYPT')) {
13 14 15 16 17 18
19 define('PASSWORD_BCRYPT', 1);
20 define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
21 define('PASSWORD_BCRYPT_DEFAULT_COST', 10);
22 }
23
24 if (!function_exists('password_hash')) {
25
26 27 28 29 30 31 32 33 34
35 function password_hash($password, $algo, array $options = array()) {
36 if (!function_exists('crypt')) {
37 trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING);
38 return null;
39 }
40 if (is_null($password) || is_int($password)) {
41 $password = (string) $password;
42 }
43 if (!is_string($password)) {
44 trigger_error("password_hash(): Password must be a string", E_USER_WARNING);
45 return null;
46 }
47 if (!is_int($algo)) {
48 trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING);
49 return null;
50 }
51 $resultLength = 0;
52 switch ($algo) {
53 case PASSWORD_BCRYPT:
54 $cost = PASSWORD_BCRYPT_DEFAULT_COST;
55 if (isset($options['cost'])) {
56 $cost = $options['cost'];
57 if ($cost < 4 || $cost > 31) {
58 trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING);
59 return null;
60 }
61 }
62
63 $raw_salt_len = 16;
64
65 $required_salt_len = 22;
66 $hash_format = sprintf("$2y$%02d$", $cost);
67
68 $resultLength = 60;
69 break;
70 default:
71 trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);
72 return null;
73 }
74 $salt_requires_encoding = false;
75 if (isset($options['salt'])) {
76 switch (gettype($options['salt'])) {
77 case 'NULL':
78 case 'boolean':
79 case 'integer':
80 case 'double':
81 case 'string':
82 $salt = (string) $options['salt'];
83 break;
84 case 'object':
85 if (method_exists($options['salt'], '__tostring')) {
86 $salt = (string) $options['salt'];
87 break;
88 }
89 case 'array':
90 case 'resource':
91 default:
92 trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
93 return null;
94 }
95 if (PasswordCompat\binary\_strlen($salt) < $required_salt_len) {
96 trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", PasswordCompat\binary\_strlen($salt), $required_salt_len), E_USER_WARNING);
97 return null;
98 } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
99 $salt_requires_encoding = true;
100 }
101 } else {
102 $buffer = '';
103 $buffer_valid = false;
104 if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
105 $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM);
106 if ($buffer) {
107 $buffer_valid = true;
108 }
109 }
110 if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
111 $buffer = openssl_random_pseudo_bytes($raw_salt_len);
112 if ($buffer) {
113 $buffer_valid = true;
114 }
115 }
116 if (!$buffer_valid && @is_readable('/dev/urandom')) {
117 $f = fopen('/dev/urandom', 'r');
118 $read = PasswordCompat\binary\_strlen($buffer);
119 while ($read < $raw_salt_len) {
120 $buffer .= fread($f, $raw_salt_len - $read);
121 $read = PasswordCompat\binary\_strlen($buffer);
122 }
123 fclose($f);
124 if ($read >= $raw_salt_len) {
125 $buffer_valid = true;
126 }
127 }
128 if (!$buffer_valid || PasswordCompat\binary\_strlen($buffer) < $raw_salt_len) {
129 $bl = PasswordCompat\binary\_strlen($buffer);
130 for ($i = 0; $i < $raw_salt_len; $i++) {
131 if ($i < $bl) {
132 $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
133 } else {
134 $buffer .= chr(mt_rand(0, 255));
135 }
136 }
137 }
138 $salt = $buffer;
139 $salt_requires_encoding = true;
140 }
141 if ($salt_requires_encoding) {
142
143 $base64_digits =
144 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
145 $bcrypt64_digits =
146 './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
147
148 $base64_string = base64_encode($salt);
149 $salt = strtr(rtrim($base64_string, '='), $base64_digits, $bcrypt64_digits);
150 }
151 $salt = PasswordCompat\binary\_substr($salt, 0, $required_salt_len);
152
153 $hash = $hash_format . $salt;
154
155 $ret = crypt($password, $hash);
156
157 if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != $resultLength) {
158 return false;
159 }
160
161 return $ret;
162 }
163
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
180 function password_get_info($hash) {
181 $return = array(
182 'algo' => 0,
183 'algoName' => 'unknown',
184 'options' => array(),
185 );
186 if (PasswordCompat\binary\_substr($hash, 0, 4) == '$2y$' && PasswordCompat\binary\_strlen($hash) == 60) {
187 $return['algo'] = PASSWORD_BCRYPT;
188 $return['algoName'] = 'bcrypt';
189 list($cost) = sscanf($hash, "$2y$%d$");
190 $return['options']['cost'] = $cost;
191 }
192 return $return;
193 }
194
195 196 197 198 199 200 201 202 203 204 205
206 function password_needs_rehash($hash, $algo, array $options = array()) {
207 $info = password_get_info($hash);
208 if ($info['algo'] != $algo) {
209 return true;
210 }
211 switch ($algo) {
212 case PASSWORD_BCRYPT:
213 $cost = isset($options['cost']) ? $options['cost'] : PASSWORD_BCRYPT_DEFAULT_COST;
214 if ($cost != $info['options']['cost']) {
215 return true;
216 }
217 break;
218 }
219 return false;
220 }
221
222 223 224 225 226 227 228 229
230 function password_verify($password, $hash) {
231 if (!function_exists('crypt')) {
232 trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING);
233 return false;
234 }
235 $ret = crypt($password, $hash);
236 if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != PasswordCompat\binary\_strlen($hash) || PasswordCompat\binary\_strlen($ret) <= 13) {
237 return false;
238 }
239
240 $status = 0;
241 for ($i = 0; $i < PasswordCompat\binary\_strlen($ret); $i++) {
242 $status |= (ord($ret[$i]) ^ ord($hash[$i]));
243 }
244
245 return $status === 0;
246 }
247 }
248
249 }
250
251 namespace PasswordCompat\binary {
252
253 if (!function_exists('PasswordCompat\\binary\\_strlen')) {
254
255 256 257 258 259 260 261 262 263 264 265 266
267 function _strlen($binary_string) {
268 if (function_exists('mb_strlen')) {
269 return mb_strlen($binary_string, '8bit');
270 }
271 return strlen($binary_string);
272 }
273
274 275 276 277 278 279 280 281 282 283 284 285
286 function _substr($binary_string, $start, $length) {
287 if (function_exists('mb_substr')) {
288 return mb_substr($binary_string, $start, $length, '8bit');
289 }
290 return substr($binary_string, $start, $length);
291 }
292
293 294 295 296 297
298 function check() {
299 static $pass = NULL;
300
301 if (is_null($pass)) {
302 if (function_exists('crypt')) {
303 $hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';
304 $test = crypt("password", $hash);
305 $pass = $test == $hash;
306 } else {
307 $pass = false;
308 }
309 }
310 return $pass;
311 }
312
313 }
314 }