Issues (365)

src/Library/libraries/phpseclib/Crypt/Random.php (2 issues)

1
<?php
2
3
/**
4
 * Random Number Generator
5
 *
6
 * PHP version 5
7
 *
8
 * Here's a short example of how to use this library:
9
 * <code>
10
 * <?php
11
 *    include 'vendor/autoload.php';
12
 *
13
 *    echo bin2hex(\phpseclib\Crypt\Random::string(8));
14
 * ?>
15
 * </code>
16
 *
17
 * @category  Crypt
18
 * @package   Random
19
 * @author    Jim Wigginton <[email protected]>
20
 * @copyright 2007 Jim Wigginton
21
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
22
 * @link      http://phpseclib.sourceforge.net
23
 */
24
25
namespace phpseclib\Crypt;
26
27
/**
28
 * Pure-PHP Random Number Generator
29
 *
30
 * @package Random
31
 * @author  Jim Wigginton <[email protected]>
32
 * @access  public
33
 */
34
class Random
35
{
36
    /**
37
     * Generate a random string.
38
     *
39
     * Although microoptimizations are generally discouraged as they impair readability this function is ripe with
40
     * microoptimizations because this function has the potential of being called a huge number of times.
41
     * eg. for RSA key generation.
42
     *
43
     * @param int $length
44
     * @return string
45
     */
46
    public static function string($length)
47
    {
48
        if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
49
            try {
50
                return \random_bytes($length);
51
            } catch (\Throwable $e) {
52
                // If a sufficient source of randomness is unavailable, random_bytes() will throw an
53
                // object that implements the Throwable interface (Exception, TypeError, Error).
54
                // We don't actually need to do anything here. The string() method should just continue
55
                // as normal. Note, however, that if we don't have a sufficient source of randomness for
56
                // random_bytes(), most of the other calls here will fail too, so we'll end up using
57
                // the PHP implementation.
58
            }
59
        }
60
61
        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
62
            // method 1. prior to PHP 5.3 this would call rand() on windows hence the function_exists('class_alias') call.
63
            // ie. class_alias is a function that was introduced in PHP 5.3
64
            if (extension_loaded('mcrypt') && function_exists('class_alias')) {
65
                return mcrypt_create_iv($length);
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

65
                return /** @scrutinizer ignore-deprecated */ mcrypt_create_iv($length);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
66
            }
67
            // method 2. openssl_random_pseudo_bytes was introduced in PHP 5.3.0 but prior to PHP 5.3.4 there was,
68
            // to quote <http://php.net/ChangeLog-5.php#5.3.4>, "possible blocking behavior". as of 5.3.4
69
            // openssl_random_pseudo_bytes and mcrypt_create_iv do the exact same thing on Windows. ie. they both
70
            // call php_win32_get_random_bytes():
71
            //
72
            // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/openssl/openssl.c#L5008
73
            // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1392
74
            //
75
            // php_win32_get_random_bytes() is defined thusly:
76
            //
77
            // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/win32/winutil.c#L80
78
            //
79
            // we're calling it, all the same, in the off chance that the mcrypt extension is not available
80
            if (extension_loaded('openssl') && version_compare(PHP_VERSION, '5.3.4', '>=')) {
81
                return openssl_random_pseudo_bytes($length);
82
            }
83
        } else {
84
            // method 1. the fastest
85
            if (extension_loaded('openssl')) {
86
                return openssl_random_pseudo_bytes($length);
87
            }
88
            // method 2
89
            static $fp = true;
90
            if ($fp === true) {
91
                // warning's will be output unles the error suppression operator is used. errors such as
92
                // "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc.
93
                $fp = @fopen('/dev/urandom', 'rb');
94
            }
95
            if ($fp !== true && $fp !== false) { // surprisingly faster than !is_bool() or is_resource()
96
                return fread($fp, $length);
97
            }
98
            // method 3. pretty much does the same thing as method 2 per the following url:
99
            // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391
100
            // surprisingly slower than method 2. maybe that's because mcrypt_create_iv does a bunch of error checking that we're
101
            // not doing. regardless, this'll only be called if this PHP script couldn't open /dev/urandom due to open_basedir
102
            // restrictions or some such
103
            if (extension_loaded('mcrypt')) {
104
                return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

104
                return /** @scrutinizer ignore-deprecated */ mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
105
            }
106
        }
107
        // at this point we have no choice but to use a pure-PHP CSPRNG
108
109
        // cascade entropy across multiple PHP instances by fixing the session and collecting all
110
        // environmental variables, including the previous session data and the current session
111
        // data.
112
        //
113
        // mt_rand seeds itself by looking at the PID and the time, both of which are (relatively)
114
        // easy to guess at. linux uses mouse clicks, keyboard timings, etc, as entropy sources, but
115
        // PHP isn't low level to be able to use those as sources and on a web server there's not likely
116
        // going to be a ton of keyboard or mouse action. web servers do have one thing that we can use
117
        // however, a ton of people visiting the website. obviously you don't want to base your seeding
118
        // soley on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled
119
        // by the user and (2) this isn't just looking at the data sent by the current user - it's based
120
        // on the data sent by all users. one user requests the page and a hash of their info is saved.
121
        // another user visits the page and the serialization of their data is utilized along with the
122
        // server envirnment stuff and a hash of the previous http request data (which itself utilizes
123
        // a hash of the session data before that). certainly an attacker should be assumed to have
124
        // full control over his own http requests. he, however, is not going to have control over
125
        // everyone's http requests.
126
        static $crypto = false, $v;
127
        if ($crypto === false) {
128
            // save old session data
129
            $old_session_id = session_id();
130
            $old_use_cookies = ini_get('session.use_cookies');
131
            $old_session_cache_limiter = session_cache_limiter();
132
            $_OLD_SESSION = isset($_SESSION) ? $_SESSION : false;
133
            if ($old_session_id != '') {
134
                session_write_close();
135
            }
136
137
            session_id(1);
138
            ini_set('session.use_cookies', 0);
139
            session_cache_limiter('');
140
            session_start();
141
142
            $v = $seed = $_SESSION['seed'] = pack('H*', hash(
143
                'sha256',
144
                (isset($_SERVER) ? phpseclib_safe_serialize($_SERVER) : '') .
145
                (isset($_POST) ? phpseclib_safe_serialize($_POST) : '') .
146
                (isset($_GET) ? phpseclib_safe_serialize($_GET) : '') .
147
                phpseclib_safe_serialize($GLOBALS) .
148
                phpseclib_safe_serialize($_SESSION) .
149
                phpseclib_safe_serialize($_OLD_SESSION)
150
            ));
151
            if (!isset($_SESSION['count'])) {
152
                $_SESSION['count'] = 0;
153
            }
154
            $_SESSION['count']++;
155
156
            session_write_close();
157
158
            // restore old session data
159
            if ($old_session_id != '') {
160
                session_id($old_session_id);
161
                session_start();
162
                ini_set('session.use_cookies', $old_use_cookies);
163
                session_cache_limiter($old_session_cache_limiter);
164
            } else {
165
                if ($_OLD_SESSION !== false) {
166
                    $_SESSION = $_OLD_SESSION;
167
                    unset($_OLD_SESSION);
168
                } else {
169
                    unset($_SESSION);
170
                }
171
            }
172
173
            // in SSH2 a shared secret and an exchange hash are generated through the key exchange process.
174
            // the IV client to server is the hash of that "nonce" with the letter A and for the encryption key it's the letter C.
175
            // if the hash doesn't produce enough a key or an IV that's long enough concat successive hashes of the
176
            // original hash and the current hash. we'll be emulating that. for more info see the following URL:
177
            //
178
            // http://tools.ietf.org/html/rfc4253#section-7.2
179
            //
180
            // see the is_string($crypto) part for an example of how to expand the keys
181
            $key = pack('H*', hash('sha256', $seed . 'A'));
182
            $iv = pack('H*', hash('sha256', $seed . 'C'));
183
184
            // ciphers are used as per the nist.gov link below. also, see this link:
185
            //
186
            // http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives
187
            switch (true) {
188
                case class_exists('\phpseclib\Crypt\AES'):
189
                    $crypto = new AES(Base::MODE_CTR);
190
                    break;
191
                case class_exists('\phpseclib\Crypt\Twofish'):
192
                    $crypto = new Twofish(Base::MODE_CTR);
193
                    break;
194
                case class_exists('\phpseclib\Crypt\Blowfish'):
195
                    $crypto = new Blowfish(Base::MODE_CTR);
196
                    break;
197
                case class_exists('\phpseclib\Crypt\TripleDES'):
198
                    $crypto = new TripleDES(Base::MODE_CTR);
199
                    break;
200
                case class_exists('\phpseclib\Crypt\DES'):
201
                    $crypto = new DES(Base::MODE_CTR);
202
                    break;
203
                case class_exists('\phpseclib\Crypt\RC4'):
204
                    $crypto = new RC4();
205
                    break;
206
                default:
207
                    user_error(__CLASS__ . ' requires at least one symmetric cipher be loaded');
208
                    return false;
209
            }
210
211
            $crypto->setKey($key);
212
            $crypto->setIV($iv);
213
            $crypto->enableContinuousBuffer();
214
        }
215
216
        //return $crypto->encrypt(str_repeat("\0", $length));
217
218
        // the following is based off of ANSI X9.31:
219
        //
220
        // http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf
221
        //
222
        // OpenSSL uses that same standard for it's random numbers:
223
        //
224
        // http://www.opensource.apple.com/source/OpenSSL/OpenSSL-38/openssl/fips-1.0/rand/fips_rand.c
225
        // (do a search for "ANS X9.31 A.2.4")
226
        $result = '';
227
        while (strlen($result) < $length) {
228
            $i = $crypto->encrypt(microtime()); // strlen(microtime()) == 21
229
            $r = $crypto->encrypt($i ^ $v); // strlen($v) == 20
230
            $v = $crypto->encrypt($r ^ $i); // strlen($r) == 20
231
            $result.= $r;
232
        }
233
        return substr($result, 0, $length);
234
    }
235
}
236
237
if (!function_exists('phpseclib_safe_serialize')) {
238
    /**
239
     * Safely serialize variables
240
     *
241
     * If a class has a private __sleep() method it'll give a fatal error on PHP 5.2 and earlier.
242
     * PHP 5.3 will emit a warning.
243
     *
244
     * @param mixed $arr
245
     * @access public
246
     */
247
    function phpseclib_safe_serialize(&$arr)
248
    {
249
        if (is_object($arr)) {
250
            return '';
251
        }
252
        if (!is_array($arr)) {
253
            return serialize($arr);
254
        }
255
        // prevent circular array recursion
256
        if (isset($arr['__phpseclib_marker'])) {
257
            return '';
258
        }
259
        $safearr = array();
260
        $arr['__phpseclib_marker'] = true;
261
        foreach (array_keys($arr) as $key) {
262
            // do not recurse on the '__phpseclib_marker' key itself, for smaller memory usage
263
            if ($key !== '__phpseclib_marker') {
264
                $safearr[$key] = phpseclib_safe_serialize($arr[$key]);
265
            }
266
        }
267
        unset($arr['__phpseclib_marker']);
268
        return serialize($safearr);
269
    }
270
}
271