Completed
Push — develop ( 5cb106...80f130 )
by Dmytro
13:36
created

PasswordHash::crypt_private()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
nc 8
nop 2
dl 0
loc 42
rs 8.0035
c 0
b 0
f 0
1
<?php namespace EvolutionCMS\Legacy;
2
3
use EvolutionCMS\Interfaces\PasswordHashInterface;
4
//
5
// Portable PHP password hashing framework.
6
//
7
// Version 0.3 / genuine.
8
//
9
// Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
10
// the public domain.  Revised in subsequent years, still public domain.
11
//
12
// There's absolutely no warranty.
13
//
14
// The homepage URL for this framework is:
15
//
16
//    http://www.openwall.com/phpass/
17
//
18
// Please be sure to update the Version line if you edit this file in any way.
19
// It is suggested that you leave the main version number intact, but indicate
20
// your project name (after the slash) and add your own revision information.
21
//
22
// Please do not change the "private" password hashing method implemented in
23
// here, thereby making your hashes incompatible.  However, if you must, please
24
// change the hash type identifier (the "$P$") to something different.
25
//
26
// Obviously, since this code is in the public domain, the above are not
27
// requirements (there can be none), but merely suggestions.
28
//
29
class PasswordHash implements PasswordHashInterface
0 ignored issues
show
Coding Style introduced by
The property $iteration_count_log2 is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style introduced by
The property $portable_hashes is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style introduced by
The property $random_state is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
30
{
31
    public $itoa64;
32
    public $iteration_count_log2;
33
    public $portable_hashes;
34
    public $random_state;
35
36
    /**
37
     * PasswordHash constructor.
38
     *
39
     * @param int $iteration_count_log2
40
     * @param bool $portable_hashes
41
     */
42
    public function __construct($iteration_count_log2 = 8, $portable_hashes = true)
0 ignored issues
show
Coding Style Naming introduced by
The parameter $iteration_count_log2 is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style Naming introduced by
The parameter $portable_hashes is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
43
    {
44
        $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
45
46
        if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) {
47
            $iteration_count_log2 = 8;
48
        }
49
        $this->iteration_count_log2 = $iteration_count_log2;
50
51
        $this->portable_hashes = $portable_hashes;
52
53
        $this->random_state = microtime() . uniqid(mt_rand(), true);
54
    }
55
56
    /**
57
     * @param string $count
58
     * @return bool|string
59
     */
60
    public function get_random_bytes($count)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
61
    {
62
        $output = '';
63
        if (@is_readable('/dev/urandom') &&
64
            ($fh = @fopen('/dev/urandom', 'rb'))) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $fh. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
65
            $output = fread($fh, $count);
66
            fclose($fh);
67
        }
68
69
        if (strlen($output) < $count) {
70
            $output = '';
71
            for ($i = 0; $i < $count; $i += 16) {
72
                $this->random_state =
73
                    md5(microtime() . $this->random_state);
74
                $output .=
75
                    pack('H*', md5($this->random_state));
76
            }
77
            $output = substr($output, 0, $count);
78
        }
79
80
        return $output;
81
    }
82
83
    /**
84
     * @param string $input
85
     * @param int $count
86
     * @return string
87
     */
88
    public function encode64($input, $count)
89
    {
90
        $output = '';
91
        $i = 0;
92
        do {
93
            $value = ord($input[$i++]);
94
            $output .= $this->itoa64[$value & 0x3f];
95
            if ($i < $count) {
96
                $value |= ord($input[$i]) << 8;
97
            }
98
            $output .= $this->itoa64[($value >> 6) & 0x3f];
99
            if ($i++ >= $count) {
100
                break;
101
            }
102
            if ($i < $count) {
103
                $value |= ord($input[$i]) << 16;
104
            }
105
            $output .= $this->itoa64[($value >> 12) & 0x3f];
106
            if ($i++ >= $count) {
107
                break;
108
            }
109
            $output .= $this->itoa64[($value >> 18) & 0x3f];
110
        } while ($i < $count);
111
112
        return $output;
113
    }
114
115
    /**
116
     * @param string $input
117
     * @return string
118
     */
119
    public function gensalt_private($input)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
120
    {
121
        $output = '$P$';
122
        $output .= $this->itoa64[min($this->iteration_count_log2 + 5, 30)];
123
        $output .= $this->encode64($input, 6);
124
125
        return $output;
126
    }
127
128
    /**
129
     * @param string $password
130
     * @param string $setting
131
     * @return string
132
     */
133
    public function crypt_private($password, $setting)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
134
    {
135
        $output = '*0';
136
        if (substr($setting, 0, 2) == $output) {
137
            $output = '*1';
138
        }
139
140
        $id = substr($setting, 0, 3);
141
        // We use "$P$", phpBB3 uses "$H$" for the same thing
142
        if ($id != '$P$' && $id != '$H$') {
143
            return $output;
144
        }
145
146
        $count_log2 = strpos($this->itoa64, $setting[3]);
147
        if ($count_log2 < 7 || $count_log2 > 30) {
148
            return $output;
149
        }
150
151
        $count = 1 << $count_log2;
152
153
        $salt = substr($setting, 4, 8);
154
        if (strlen($salt) != 8) {
155
            return $output;
156
        }
157
158
        // We're kind of forced to use MD5 here since it's the only
159
        // cryptographic primitive available in all versions of PHP
160
        // currently in use.  To implement our own low-level crypto
161
        // in PHP would result in much worse performance and
162
        // consequently in lower iteration counts and hashes that are
163
        // quicker to crack (by non-PHP code).
164
165
        $hash = md5($salt . $password, true);
166
        do {
167
            $hash = md5($hash . $password, true);
168
        } while (--$count);
169
170
        $output = substr($setting, 0, 12);
171
        $output .= $this->encode64($hash, 16);
172
173
        return $output;
174
    }
175
176
    /**
177
     * @param string $input
178
     * @return string
179
     */
180
    public function gensalt_extended($input)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
181
    {
182
        $count_log2 = min($this->iteration_count_log2 + 8, 24);
183
        // This should be odd to not reveal weak DES keys, and the
184
        // maximum valid value is (2**24 - 1) which is odd anyway.
185
        $count = (1 << $count_log2) - 1;
186
187
        $output = '_';
188
        $output .= $this->itoa64[$count & 0x3f];
189
        $output .= $this->itoa64[($count >> 6) & 0x3f];
190
        $output .= $this->itoa64[($count >> 12) & 0x3f];
191
        $output .= $this->itoa64[($count >> 18) & 0x3f];
192
193
        $output .= $this->encode64($input, 3);
194
195
        return $output;
196
    }
197
198
    /**
199
     * @param string $input
200
     * @return string
201
     */
202
    public function gensalt_blowfish($input)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
203
    {
204
        // This one needs to use a different order of characters and a
205
        // different encoding scheme from the one in encode64() above.
206
        // We care because the last character in our encoded string will
207
        // only represent 2 bits.  While two known implementations of
208
        // bcrypt will happily accept and correct a salt string which
209
        // has the 4 unused bits set to non-zero, we do not want to take
210
        // chances and we also do not want to waste an additional byte
211
        // of entropy.
212
        $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
213
214
        $output = '$2a$';
215
        $output .= chr(ord('0') + $this->iteration_count_log2 / 10);
216
        $output .= chr(ord('0') + $this->iteration_count_log2 % 10);
217
        $output .= '$';
218
219
        $i = 0;
220
        do {
221
            $c1 = ord($input[$i++]);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $c1. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
222
            $output .= $itoa64[$c1 >> 2];
223
            $c1 = ($c1 & 0x03) << 4;
224
            if ($i >= 16) {
225
                $output .= $itoa64[$c1];
226
                break;
227
            }
228
229
            $c2 = ord($input[$i++]);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $c2. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
230
            $c1 |= $c2 >> 4;
231
            $output .= $itoa64[$c1];
232
            $c1 = ($c2 & 0x0f) << 2;
233
234
            $c2 = ord($input[$i++]);
235
            $c1 |= $c2 >> 6;
236
            $output .= $itoa64[$c1];
237
            $output .= $itoa64[$c2 & 0x3f];
238
        } while (1);
239
240
        return $output;
241
    }
242
243
    /**
244
     * @param string $password
245
     * @return string
246
     */
247
    public function HashPassword($password)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
248
    {
249
        if (strlen($password) > 4096) {
250
            return '*';
251
        }
252
253
        $random = '';
254
255 View Code Duplication
        if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
256
            $random = $this->get_random_bytes(16);
257
            $hash =
258
                crypt($password, $this->gensalt_blowfish($random));
0 ignored issues
show
Bug introduced by
It seems like $random defined by $this->get_random_bytes(16) on line 256 can also be of type boolean; however, EvolutionCMS\Legacy\Pass...ash::gensalt_blowfish() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
259
            if (strlen($hash) == 60) {
260
                return $hash;
261
            }
262
        }
263
264 View Code Duplication
        if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) {
265
            if (strlen($random) < 3) {
266
                $random = $this->get_random_bytes(3);
267
            }
268
            $hash =
269
                crypt($password, $this->gensalt_extended($random));
0 ignored issues
show
Bug introduced by
It seems like $random can also be of type boolean; however, EvolutionCMS\Legacy\Pass...ash::gensalt_extended() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
270
            if (strlen($hash) == 20) {
271
                return $hash;
272
            }
273
        }
274
275
        if (strlen($random) < 6) {
276
            $random = $this->get_random_bytes(6);
277
        }
278
        $hash =
279
            $this->crypt_private($password,
280
                $this->gensalt_private($random));
0 ignored issues
show
Bug introduced by
It seems like $random can also be of type boolean; however, EvolutionCMS\Legacy\Pass...Hash::gensalt_private() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
281
        if (strlen($hash) == 34) {
282
            return $hash;
283
        }
284
285
        // Returning '*' on error is safe here, but would _not_ be safe
286
        // in a crypt(3)-like function used _both_ for generating new
287
        // hashes and for validating passwords against existing hashes.
288
        return '*';
289
    }
290
291
    /**
292
     * @param string $password
293
     * @param string $stored_hash
294
     * @return bool
295
     */
296
    public function CheckPassword($password, $stored_hash)
0 ignored issues
show
Coding Style Naming introduced by
The parameter $stored_hash is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
297
    {
298
        if (strlen($password) > 4096) {
299
            return false;
300
        }
301
302
        $hash = $this->crypt_private($password, $stored_hash);
303
        if (substr($hash, 0, 1) === '*') {
304
            $hash = crypt($password, $stored_hash);
305
        }
306
307
        return ($hash === $stored_hash) ? true : false;
308
    }
309
}
310