Highway to PSR2
[openemr.git] / portal / patient / fwk / libs / util / password.php
blob83cd0b47303c5fde304457f95e1e97de242688d6
1 <?php
2 /**
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
6 */
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')) {
12 /**
13 * Hash the password using the specified algorithm
15 * @param string $password
16 * The password to hash
17 * @param int $algo
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())
26 if (! function_exists('crypt')) {
27 trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING);
28 return null;
31 if (! is_string($password)) {
32 trigger_error("password_hash(): Password must be a string", E_USER_WARNING);
33 return null;
36 if (! is_int($algo)) {
37 trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING);
38 return null;
41 switch ($algo) {
42 case PASSWORD_BCRYPT:
43 // Note that this is a C constant, but not exposed to PHP, so we don't define it here.
44 $cost = 10;
45 if (isset($options ['cost'])) {
46 $cost = $options ['cost'];
47 if ($cost < 4 || $cost > 31) {
48 trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING);
49 return null;
53 $required_salt_len = 22;
54 $hash_format = sprintf("$2y$%02d$", $cost);
55 break;
56 default:
57 trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);
58 return null;
61 if (isset($options ['salt'])) {
62 switch (gettype($options ['salt'])) {
63 case 'NULL':
64 case 'boolean':
65 case 'integer':
66 case 'double':
67 case 'string':
68 $salt = ( string ) $options ['salt'];
69 break;
70 case 'object':
71 if (method_exists($options ['salt'], '__tostring')) {
72 $salt = ( string ) $options ['salt'];
73 break;
76 //NOTE FALL-THROUGH CASE HERE. POSSIBLE BUG.
77 case 'array':
78 case 'resource':
79 default:
80 trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
81 return null;
84 if (strlen($salt) < $required_salt_len) {
85 trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", strlen($salt), $required_salt_len), E_USER_WARNING);
86 return null;
87 } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
88 $salt = str_replace('+', '.', base64_encode($salt));
90 } else {
91 $salt = __password_make_salt($required_salt_len);
94 $salt = substr($salt, 0, $required_salt_len);
96 $hash = $hash_format . $salt;
98 $ret = crypt($password, $hash);
100 if (! is_string($ret) || strlen($ret) < 13) {
101 return false;
104 return $ret;
108 if (! function_exists('password_get_info')) {
110 * Get information about the password hash.
111 * Returns an array of the information
112 * that was used to generate the password hash.
114 * array(
115 * 'algo' => 1,
116 * 'algoName' => 'bcrypt',
117 * 'options' => array(
118 * 'cost' => 10,
119 * ),
122 * @param string $hash
123 * The password hash to extract info from
125 * @return array The array of information about the hash.
127 function password_get_info($hash)
129 $return = array (
130 'algo' => 0,
131 'algoName' => 'unknown',
132 'options' => array ()
134 if (substr($hash, 0, 4) == '$2y$' && strlen($hash) == 60) {
135 $return ['algo'] = PASSWORD_BCRYPT;
136 $return ['algoName'] = 'bcrypt';
137 list ( $cost ) = sscanf($hash, "$2y$%d$");
138 $return ['options'] ['cost'] = $cost;
141 return $return;
145 if (! function_exists('password_needs_rehash')) {
147 * Determine if the password hash needs to be rehashed according to the options provided
149 * If the answer is true, after validating the password using password_verify, rehash it.
151 * @param string $hash
152 * The hash to test
153 * @param int $algo
154 * The algorithm used for new password hashes
155 * @param array $options
156 * The options array passed to password_hash
158 * @return boolean True if the password needs to be rehashed.
160 function password_needs_rehash($hash, $algo, array $options = array())
162 $info = password_get_info($hash);
163 if ($info ['algo'] != $algo) {
164 return true;
167 switch ($algo) {
168 case PASSWORD_BCRYPT:
169 $cost = isset($options ['cost']) ? $options ['cost'] : 10;
170 if ($cost != $info ['options'] ['cost']) {
171 return true;
173 break;
176 return false;
180 if (! function_exists('password_verify')) {
182 * Verify a password against a hash using a timing attack resistant approach
184 * @param string $password
185 * The password to verify
186 * @param string $hash
187 * The hash to verify against
189 * @return boolean If the password matches the hash
191 function password_verify($password, $hash)
193 if (! function_exists('crypt')) {
194 trigger_error("Crypt must be loaded for password_create to function", E_USER_WARNING);
195 return false;
198 $ret = crypt($password, $hash);
199 if (! is_string($ret) || strlen($ret) != strlen($hash)) {
200 return false;
203 $status = 0;
204 for ($i = 0; $i < strlen($ret); $i ++) {
205 $status |= (ord($ret [$i]) ^ ord($hash [$i]));
208 return $status === 0;
213 * Function to make a salt
215 * DO NOT USE THIS FUNCTION DIRECTLY
217 * @internal
220 function __password_make_salt($length)
222 if ($length <= 0) {
223 trigger_error(sprintf("Length cannot be less than or equal zero: %d", $length), E_USER_WARNING);
224 return false;
227 $buffer = '';
228 $raw_length = ( int ) ($length * 3 / 4 + 1);
229 $buffer_valid = false;
230 if (function_exists('mcrypt_create_iv')) {
231 $buffer = mcrypt_create_iv($raw_length, MCRYPT_DEV_URANDOM);
232 if ($buffer) {
233 $buffer_valid = true;
237 if (! $buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
238 $buffer = openssl_random_pseudo_bytes($raw_length);
239 if ($buffer) {
240 $buffer_valid = true;
244 if (! $buffer_valid && file_exists('/dev/urandom')) {
245 $f = @fopen('/dev/urandom', 'r');
246 if ($f) {
247 $read = strlen($buffer);
248 while ($read < $raw_length) {
249 $buffer .= fread($f, $raw_length - $read);
250 $read = strlen($buffer);
253 fclose($f);
254 if ($read >= $raw_length) {
255 $buffer_valid = true;
260 if (! $buffer_valid) {
261 for ($i = 0; $i < $raw_length; $i ++) {
262 $buffer .= chr(mt_rand(0, 255));
266 $buffer = str_replace('+', '.', base64_encode($buffer));
267 return substr($buffer, 0, $length);