Completed
Pull Request — develop (#720)
by Imants
03:58
created

static.php ➔ password_hash()   F

Complexity

Conditions 36
Paths 361

Size

Total Lines 136
Code Lines 98

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 36
c 2
b 1
f 0
dl 0
loc 136
rs 3.3842
eloc 98
nc 361
nop 3

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Contains static functions. Agile Toolkit does not generally use
4
 * static functions, so please do not use any functions here.
5
 */
6
7
// Let's see if static is already loaded. It's a common problem when
8
// We using old-style 'include.php' which includes both agile toolkit and
9
// Composer
10
if (defined('AGILE_TOOLKIT_STATIC_LOADED')) {
11
    trigger_error('Please include either "atk4/loader.php" or "vendor/autoload.php" and only once.');
12
}
13
define('AGILE_TOOLKIT_STATIC_LOADED', true);
14
15
// @codingStandardsIgnoreStart
16
if (!defined('undefined')) {
17
    define('undefined', '_atk4_undefined_value');
18
}
19
// @codingStandardsIgnoreEnd
20
if (!defined('UNDEFINED')) {
21
    define('UNDEFINED', '_atk4_undefined_value');
22
}
23
24
if (!function_exists('error_handler')) {
25
    function error_handler($errno, $errstr, $errfile, $errline)
26
    {
27
        $errorType = array(
28
                E_ERROR => 'Error',             // 1
29
                E_WARNING => 'Warning',           // 2
30
                E_PARSE => 'Parsing Error',     // 4
31
                E_NOTICE => 'Notice',            // 8
32
                E_CORE_ERROR => 'Core Error',        // 16
33
                E_CORE_WARNING => 'Core Warning',      // 32
34
                E_COMPILE_ERROR => 'Compile Error',     // 64
35
                E_COMPILE_WARNING => 'Compile Warning',   // 128
36
                E_USER_ERROR => 'User Error',        // 256
37
                E_USER_WARNING => 'User Warning',      // 512
38
                E_USER_NOTICE => 'User Notice',       // 1024
39
                E_STRICT => 'PHP suggestions',   // 2048
40
                E_RECOVERABLE_ERROR => 'Runtime Notice',    // 4096
41
                E_DEPRECATED => 'Deprecated Notice', // 8192
42
                E_USER_DEPRECATED => 'User Deprecated Notice', // 16384
43
                E_ALL => 'All Errors',        // 32767
44
                );
45
46
        if ((error_reporting() & $errno) != 0) {
47
            $errfile = dirname($errfile).'/<b>'.basename($errfile).'</b>';
48
            $str = "<font style='font-family: verdana;  font-size:10px'><font color=blue>$errfile:$errline</font> ".
49
                "<font color=red>[$errno] <b>$errstr</b></font></font>";
50
51
            switch ($errno) {
52
                case 2: // E_WARNING
53
                    if (strpos($errstr, 'mysql_connect') !== false) {
54
                        break;
55
                    }
56
                    // no-break
57
                case 8: // E_NOTICE
58
                    if (strpos($errstr, 'Undefined offset') === 0) {
59
                        break;
60
                    }
61
                    if (strpos($errstr, 'Undefined index') === 0) {
62
                        break;
63
                    }
64
                    // no-break
65
                case 2048: // E_STRICT
66
                    if (strpos($errstr, 'var: Deprecated') === 0) {
67
                        break;
68
                    }
69
                    if (strpos($errstr, 'Declaration of ') === 0) {
70
                        break;
71
                    }
72
                    if (strpos($errstr, 'Non-static method') === 0) {
73
                        break;
74
                    }
75
                    // no-break
76
                case 8192: // E_DEPRECATED
77
                    if (substr($errstr, -13) == 'is deprecated') {
78
                        break;
79
                    }
80
                    // no-break
81
                default:
82
                    // throw consistently all errors into exceptions
83
                    throw new ErrorException(
84
                        $errorType[$errno].': ['.$errfile.':'.$errline.']'.$errstr,
85
                        0,
86
                        $errno,
87
                        $errfile,
88
                        $errline
89
                    );
90
            }
91
        }
92
    }
93
    set_error_handler('error_handler');
94
};
95
96
// From: https://raw.github.com/ircmaxell/password_compat/master/lib/password.php
97
/*
98
 * A Compatibility library with PHP 5.5's simplified password hashing API.
99
 *
100
 * @author Anthony Ferrara <[email protected]>
101
 * @license http://www.opensource.org/licenses/mit-license.html MIT License
102
 * @copyright 2012 The Authors
103
 */
104
105
if (!defined('PASSWORD_BCRYPT')) {
106
    define('PASSWORD_BCRYPT', 1);
107
    define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
108
109
    /**
110
     * Hash the password using the specified algorithm.
111
     *
112
     * @param string $password The password to hash
113
     * @param int    $algo     The algorithm to use (Defined by PASSWORD_* constants)
114
     * @param array  $options  The options for the algorithm to use
115
     *
116
     * @return string|false The hashed password, or false on error.
117
     */
118
    function password_hash($password, $algo, array $options = array())
119
    {
120
        if (!function_exists('crypt')) {
121
            trigger_error(
122
                'Crypt must be loaded for password_hash to function',
123
                E_USER_WARNING
124
            );
125
            return;
126
        }
127
        if (!is_string($password)) {
128
            trigger_error(
129
                'password_hash(): Password must be a string',
130
                E_USER_WARNING
131
            );
132
            return;
133
        }
134
        if (!is_int($algo)) {
135
            trigger_error(
136
                'password_hash() expects parameter 2 to be long, '.gettype($algo).' given',
137
                E_USER_WARNING
138
            );
139
            return;
140
        }
141
        switch ($algo) {
142
            case PASSWORD_BCRYPT:
143
                // Note that this is a C constant, but not exposed to PHP, so we don't define it here.
144
                $cost = 10;
145
                if (isset($options['cost'])) {
146
                    $cost = $options['cost'];
147
                    if ($cost < 4 || $cost > 31) {
148
                        trigger_error(
149
                            sprintf('password_hash(): Invalid bcrypt cost parameter specified: %d', $cost),
150
                            E_USER_WARNING
151
                        );
152
                        return;
153
                    }
154
                }
155
                $required_salt_len = 22;
156
                $hash_format = sprintf('$2y$%02d$', $cost);
157
                break;
158
            default:
159
                trigger_error(
160
                    sprintf('password_hash(): Unknown password hashing algorithm: %s', $algo),
161
                    E_USER_WARNING
162
                );
163
                return;
164
        }
165
        if (isset($options['salt'])) {
166
            switch (gettype($options['salt'])) {
167
                case 'NULL':
168
                case 'boolean':
169
                case 'integer':
170
                case 'double':
171
                case 'string':
172
                    $salt = (string) $options['salt'];
173
                    break;
174
                case 'object':
175
                    if (method_exists($options['salt'], '__tostring')) {
176
                        $salt = (string) $options['salt'];
177
                        break;
178
                    }
179
                    // no-break
180
                case 'array':
181
                case 'resource':
182
                default:
183
                    trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
184
185
                    return;
186
            }
187
            if (strlen($salt) < $required_salt_len) {
188
                trigger_error(
189
                    sprintf(
190
                        'password_hash(): Provided salt is too short: %d expecting %d',
191
                        strlen($salt),
192
                        $required_salt_len
193
                    ),
194
                    E_USER_WARNING
195
                );
196
                return;
197
            } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
198
                $salt = str_replace('+', '.', base64_encode($salt));
199
            }
200
        } else {
201
            $buffer = '';
202
            $raw_length = (int) ($required_salt_len * 3 / 4 + 1);
203
            $buffer_valid = false;
204
            if (function_exists('mcrypt_create_iv')) {
205
                $buffer = mcrypt_create_iv($raw_length, MCRYPT_DEV_URANDOM);
206
                if ($buffer) {
207
                    $buffer_valid = true;
208
                }
209
            }
210
            if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
211
                $buffer = openssl_random_pseudo_bytes($raw_length);
212
                if ($buffer) {
213
                    $buffer_valid = true;
214
                }
215
            }
216
            if (!$buffer_valid && file_exists('/dev/urandom')) {
217
                $f = @fopen('/dev/urandom', 'r');
218
                if ($f) {
219
                    $read = strlen($buffer);
220
                    while ($read < $raw_length) {
221
                        $buffer .= fread($f, $raw_length - $read);
222
                        $read = strlen($buffer);
223
                    }
224
                    fclose($f);
225
                    if ($read >= $raw_length) {
226
                        $buffer_valid = true;
227
                    }
228
                }
229
            }
230
            if (!$buffer_valid || strlen($buffer) < $raw_length) {
231
                $bl = strlen($buffer);
232
                for ($i = 0; $i < $raw_length; ++$i) {
233
                    if ($i < $bl) {
234
                        $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
235
                    } else {
236
                        $buffer .= chr(mt_rand(0, 255));
237
                    }
238
                }
239
            }
240
            $salt = str_replace('+', '.', base64_encode($buffer));
241
        }
242
        $salt = substr($salt, 0, $required_salt_len);
243
244
        $hash = $hash_format.$salt;
245
246
        $ret = crypt($password, $hash);
247
248
        if (!is_string($ret) || strlen($ret) <= 13) {
249
            return false;
250
        }
251
252
        return $ret;
253
    }
254
255
    /**
256
     * Get information about the password hash. Returns an array of the information
257
     * that was used to generate the password hash.
258
     *
259
     * array(
260
     *    'algo' => 1,
261
     *    'algoName' => 'bcrypt',
262
     *    'options' => array(
263
     *        'cost' => 10,
264
     *    ),
265
     * )
266
     *
267
     * @param string $hash The password hash to extract info from
268
     *
269
     * @return array The array of information about the hash.
270
     */
271
    function password_get_info($hash)
272
    {
273
        $return = array(
274
            'algo' => 0,
275
            'algoName' => 'unknown',
276
            'options' => array(),
277
        );
278
        if (substr($hash, 0, 4) == '$2y$' && strlen($hash) == 60) {
279
            $return['algo'] = PASSWORD_BCRYPT;
280
            $return['algoName'] = 'bcrypt';
281
            list($cost) = sscanf($hash, '$2y$%d$');
282
            $return['options']['cost'] = $cost;
283
        }
284
285
        return $return;
286
    }
287
288
    /**
289
     * Determine if the password hash needs to be rehashed according to the options provided.
290
     *
291
     * If the answer is true, after validating the password using password_verify, rehash it.
292
     *
293
     * @param string $hash    The hash to test
294
     * @param int    $algo    The algorithm used for new password hashes
295
     * @param array  $options The options array passed to password_hash
296
     *
297
     * @return bool True if the password needs to be rehashed.
298
     */
299
    function password_needs_rehash($hash, $algo, array $options = array())
300
    {
301
        $info = password_get_info($hash);
302
        if ($info['algo'] != $algo) {
303
            return true;
304
        }
305
        switch ($algo) {
306
            case PASSWORD_BCRYPT:
307
                $cost = isset($options['cost']) ? $options['cost'] : 10;
308
                if ($cost != $info['options']['cost']) {
309
                    return true;
310
                }
311
                break;
312
        }
313
314
        return false;
315
    }
316
317
    /**
318
     * Verify a password against a hash using a timing attack resistant approach.
319
     *
320
     * @param string $password The password to verify
321
     * @param string $hash     The hash to verify against
322
     *
323
     * @return bool If the password matches the hash
324
     */
325
    function password_verify($password, $hash)
326
    {
327
        if (!function_exists('crypt')) {
328
            trigger_error('Crypt must be loaded for password_verify to function', E_USER_WARNING);
329
330
            return false;
331
        }
332
        $ret = crypt($password, $hash);
333
        if (!is_string($ret) || strlen($ret) != strlen($hash) || strlen($ret) <= 13) {
334
            return false;
335
        }
336
337
        $status = 0;
338
        for ($i = 0, $len = strlen($ret); $i < $len; ++$i) {
339
            $status |= (ord($ret[$i]) ^ ord($hash[$i]));
340
        }
341
342
        return $status === 0;
343
    }
344
}
345