29 * SOFTWARE. |
29 * SOFTWARE. |
30 */ |
30 */ |
31 |
31 |
32 if (!defined('PHP_VERSION_ID')) { |
32 if (!defined('PHP_VERSION_ID')) { |
33 // This constant was introduced in PHP 5.2.7 |
33 // This constant was introduced in PHP 5.2.7 |
34 $RandomCompatversion = explode('.', PHP_VERSION); |
34 $RandomCompatversion = array_map('intval', explode('.', PHP_VERSION)); |
35 define( |
35 define( |
36 'PHP_VERSION_ID', |
36 'PHP_VERSION_ID', |
37 $RandomCompatversion[0] * 10000 |
37 $RandomCompatversion[0] * 10000 |
38 + $RandomCompatversion[1] * 100 |
38 + $RandomCompatversion[1] * 100 |
39 + $RandomCompatversion[2] |
39 + $RandomCompatversion[2] |
40 ); |
40 ); |
41 $RandomCompatversion = null; |
41 $RandomCompatversion = null; |
42 } |
42 } |
43 |
43 |
44 if (PHP_VERSION_ID < 70000) { |
44 /** |
45 |
45 * PHP 7.0.0 and newer have these functions natively. |
46 if (!defined('RANDOM_COMPAT_READ_BUFFER')) { |
46 */ |
47 define('RANDOM_COMPAT_READ_BUFFER', 8); |
47 if (PHP_VERSION_ID >= 70000) { |
48 } |
48 return; |
49 |
49 } |
50 $RandomCompatDIR = dirname(__FILE__); |
50 |
51 |
51 if (!defined('RANDOM_COMPAT_READ_BUFFER')) { |
52 require_once $RandomCompatDIR.'/byte_safe_strings.php'; |
52 define('RANDOM_COMPAT_READ_BUFFER', 8); |
53 require_once $RandomCompatDIR.'/cast_to_int.php'; |
53 } |
54 require_once $RandomCompatDIR.'/error_polyfill.php'; |
54 |
55 |
55 $RandomCompatDIR = dirname(__FILE__); |
56 if (!function_exists('random_bytes')) { |
56 |
|
57 require_once $RandomCompatDIR . '/byte_safe_strings.php'; |
|
58 require_once $RandomCompatDIR . '/cast_to_int.php'; |
|
59 require_once $RandomCompatDIR . '/error_polyfill.php'; |
|
60 |
|
61 if (!is_callable('random_bytes')) { |
|
62 /** |
|
63 * PHP 5.2.0 - 5.6.x way to implement random_bytes() |
|
64 * |
|
65 * We use conditional statements here to define the function in accordance |
|
66 * to the operating environment. It's a micro-optimization. |
|
67 * |
|
68 * In order of preference: |
|
69 * 1. Use libsodium if available. |
|
70 * 2. fread() /dev/urandom if available (never on Windows) |
|
71 * 3. mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM) |
|
72 * 4. COM('CAPICOM.Utilities.1')->GetRandom() |
|
73 * |
|
74 * See RATIONALE.md for our reasoning behind this particular order |
|
75 */ |
|
76 if (extension_loaded('libsodium')) { |
|
77 // See random_bytes_libsodium.php |
|
78 if (PHP_VERSION_ID >= 50300 && is_callable('\\Sodium\\randombytes_buf')) { |
|
79 require_once $RandomCompatDIR . '/random_bytes_libsodium.php'; |
|
80 } elseif (method_exists('Sodium', 'randombytes_buf')) { |
|
81 require_once $RandomCompatDIR . '/random_bytes_libsodium_legacy.php'; |
|
82 } |
|
83 } |
|
84 |
|
85 /** |
|
86 * Reading directly from /dev/urandom: |
|
87 */ |
|
88 if (DIRECTORY_SEPARATOR === '/') { |
|
89 // DIRECTORY_SEPARATOR === '/' on Unix-like OSes -- this is a fast |
|
90 // way to exclude Windows. |
|
91 $RandomCompatUrandom = true; |
|
92 $RandomCompat_basedir = ini_get('open_basedir'); |
|
93 |
|
94 if (!empty($RandomCompat_basedir)) { |
|
95 $RandomCompat_open_basedir = explode( |
|
96 PATH_SEPARATOR, |
|
97 strtolower($RandomCompat_basedir) |
|
98 ); |
|
99 $RandomCompatUrandom = (array() !== array_intersect( |
|
100 array('/dev', '/dev/', '/dev/urandom'), |
|
101 $RandomCompat_open_basedir |
|
102 )); |
|
103 $RandomCompat_open_basedir = null; |
|
104 } |
|
105 |
|
106 if ( |
|
107 !is_callable('random_bytes') |
|
108 && |
|
109 $RandomCompatUrandom |
|
110 && |
|
111 @is_readable('/dev/urandom') |
|
112 ) { |
|
113 // Error suppression on is_readable() in case of an open_basedir |
|
114 // or safe_mode failure. All we care about is whether or not we |
|
115 // can read it at this point. If the PHP environment is going to |
|
116 // panic over trying to see if the file can be read in the first |
|
117 // place, that is not helpful to us here. |
|
118 |
|
119 // See random_bytes_dev_urandom.php |
|
120 require_once $RandomCompatDIR . '/random_bytes_dev_urandom.php'; |
|
121 } |
|
122 // Unset variables after use |
|
123 $RandomCompat_basedir = null; |
|
124 } else { |
|
125 $RandomCompatUrandom = false; |
|
126 } |
|
127 |
|
128 /** |
|
129 * mcrypt_create_iv() |
|
130 * |
|
131 * We only want to use mcypt_create_iv() if: |
|
132 * |
|
133 * - random_bytes() hasn't already been defined |
|
134 * - the mcrypt extensions is loaded |
|
135 * - One of these two conditions is true: |
|
136 * - We're on Windows (DIRECTORY_SEPARATOR !== '/') |
|
137 * - We're not on Windows and /dev/urandom is readabale |
|
138 * (i.e. we're not in a chroot jail) |
|
139 * - Special case: |
|
140 * - If we're not on Windows, but the PHP version is between |
|
141 * 5.6.10 and 5.6.12, we don't want to use mcrypt. It will |
|
142 * hang indefinitely. This is bad. |
|
143 * - If we're on Windows, we want to use PHP >= 5.3.7 or else |
|
144 * we get insufficient entropy errors. |
|
145 */ |
|
146 if ( |
|
147 !is_callable('random_bytes') |
|
148 && |
|
149 // Windows on PHP < 5.3.7 is broken, but non-Windows is not known to be. |
|
150 (DIRECTORY_SEPARATOR === '/' || PHP_VERSION_ID >= 50307) |
|
151 && |
|
152 // Prevent this code from hanging indefinitely on non-Windows; |
|
153 // see https://bugs.php.net/bug.php?id=69833 |
|
154 ( |
|
155 DIRECTORY_SEPARATOR !== '/' || |
|
156 (PHP_VERSION_ID <= 50609 || PHP_VERSION_ID >= 50613) |
|
157 ) |
|
158 && |
|
159 extension_loaded('mcrypt') |
|
160 ) { |
|
161 // See random_bytes_mcrypt.php |
|
162 require_once $RandomCompatDIR . '/random_bytes_mcrypt.php'; |
|
163 } |
|
164 $RandomCompatUrandom = null; |
|
165 |
|
166 /** |
|
167 * This is a Windows-specific fallback, for when the mcrypt extension |
|
168 * isn't loaded. |
|
169 */ |
|
170 if ( |
|
171 !is_callable('random_bytes') |
|
172 && |
|
173 extension_loaded('com_dotnet') |
|
174 && |
|
175 class_exists('COM') |
|
176 ) { |
|
177 $RandomCompat_disabled_classes = preg_split( |
|
178 '#\s*,\s*#', |
|
179 strtolower(ini_get('disable_classes')) |
|
180 ); |
|
181 |
|
182 if (!in_array('com', $RandomCompat_disabled_classes)) { |
|
183 try { |
|
184 $RandomCompatCOMtest = new COM('CAPICOM.Utilities.1'); |
|
185 if (method_exists($RandomCompatCOMtest, 'GetRandom')) { |
|
186 // See random_bytes_com_dotnet.php |
|
187 require_once $RandomCompatDIR . '/random_bytes_com_dotnet.php'; |
|
188 } |
|
189 } catch (com_exception $e) { |
|
190 // Don't try to use it. |
|
191 } |
|
192 } |
|
193 $RandomCompat_disabled_classes = null; |
|
194 $RandomCompatCOMtest = null; |
|
195 } |
|
196 |
|
197 /** |
|
198 * throw new Exception |
|
199 */ |
|
200 if (!is_callable('random_bytes')) { |
57 /** |
201 /** |
58 * PHP 5.2.0 - 5.6.x way to implement random_bytes() |
202 * We don't have any more options, so let's throw an exception right now |
|
203 * and hope the developer won't let it fail silently. |
59 * |
204 * |
60 * We use conditional statements here to define the function in accordance |
205 * @param mixed $length |
61 * to the operating environment. It's a micro-optimization. |
206 * @return void |
62 * |
207 * @throws Exception |
63 * In order of preference: |
|
64 * 1. Use libsodium if available. |
|
65 * 2. fread() /dev/urandom if available (never on Windows) |
|
66 * 3. mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM) |
|
67 * 4. COM('CAPICOM.Utilities.1')->GetRandom() |
|
68 * 5. openssl_random_pseudo_bytes() (absolute last resort) |
|
69 * |
|
70 * See ERRATA.md for our reasoning behind this particular order |
|
71 */ |
208 */ |
72 if (extension_loaded('libsodium')) { |
209 function random_bytes($length) |
73 // See random_bytes_libsodium.php |
210 { |
74 if (PHP_VERSION_ID >= 50300 && function_exists('\\Sodium\\randombytes_buf')) { |
211 unset($length); // Suppress "variable not used" warnings. |
75 require_once $RandomCompatDIR.'/random_bytes_libsodium.php'; |
212 throw new Exception( |
76 } elseif (method_exists('Sodium', 'randombytes_buf')) { |
213 'There is no suitable CSPRNG installed on your system' |
77 require_once $RandomCompatDIR.'/random_bytes_libsodium_legacy.php'; |
|
78 } |
|
79 } |
|
80 |
|
81 /** |
|
82 * Reading directly from /dev/urandom: |
|
83 */ |
|
84 if (DIRECTORY_SEPARATOR === '/') { |
|
85 // DIRECTORY_SEPARATOR === '/' on Unix-like OSes -- this is a fast |
|
86 // way to exclude Windows. |
|
87 $RandomCompatUrandom = true; |
|
88 $RandomCompat_basedir = ini_get('open_basedir'); |
|
89 |
|
90 if (!empty($RandomCompat_basedir)) { |
|
91 $RandomCompat_open_basedir = explode( |
|
92 PATH_SEPARATOR, |
|
93 strtolower($RandomCompat_basedir) |
|
94 ); |
|
95 $RandomCompatUrandom = in_array( |
|
96 '/dev', |
|
97 $RandomCompat_open_basedir |
|
98 ); |
|
99 $RandomCompat_open_basedir = null; |
|
100 } |
|
101 |
|
102 if ( |
|
103 !function_exists('random_bytes') |
|
104 && |
|
105 $RandomCompatUrandom |
|
106 && |
|
107 @is_readable('/dev/urandom') |
|
108 ) { |
|
109 // Error suppression on is_readable() in case of an open_basedir |
|
110 // or safe_mode failure. All we care about is whether or not we |
|
111 // can read it at this point. If the PHP environment is going to |
|
112 // panic over trying to see if the file can be read in the first |
|
113 // place, that is not helpful to us here. |
|
114 |
|
115 // See random_bytes_dev_urandom.php |
|
116 require_once $RandomCompatDIR.'/random_bytes_dev_urandom.php'; |
|
117 } |
|
118 // Unset variables after use |
|
119 $RandomCompat_basedir = null; |
|
120 $RandomCompatUrandom = null; |
|
121 } |
|
122 |
|
123 /** |
|
124 * mcrypt_create_iv() |
|
125 */ |
|
126 if ( |
|
127 !function_exists('random_bytes') |
|
128 && |
|
129 PHP_VERSION_ID >= 50307 |
|
130 && |
|
131 extension_loaded('mcrypt') |
|
132 ) { |
|
133 // Prevent this code from hanging indefinitely on non-Windows; |
|
134 // see https://bugs.php.net/bug.php?id=69833 |
|
135 if ( |
|
136 DIRECTORY_SEPARATOR !== '/' || |
|
137 (PHP_VERSION_ID <= 50609 || PHP_VERSION_ID >= 50613) |
|
138 ) { |
|
139 // See random_bytes_mcrypt.php |
|
140 require_once $RandomCompatDIR.'/random_bytes_mcrypt.php'; |
|
141 } |
|
142 } |
|
143 |
|
144 if ( |
|
145 !function_exists('random_bytes') |
|
146 && |
|
147 extension_loaded('com_dotnet') |
|
148 && |
|
149 class_exists('COM') |
|
150 ) { |
|
151 $RandomCompat_disabled_classes = preg_split( |
|
152 '#\s*,\s*#', |
|
153 strtolower(ini_get('disable_classes')) |
|
154 ); |
214 ); |
155 |
215 } |
156 if (!in_array('com', $RandomCompat_disabled_classes)) { |
216 } |
157 try { |
217 } |
158 $RandomCompatCOMtest = new COM('CAPICOM.Utilities.1'); |
218 |
159 if (method_exists($RandomCompatCOMtest, 'GetRandom')) { |
219 if (!is_callable('random_int')) { |
160 // See random_bytes_com_dotnet.php |
220 require_once $RandomCompatDIR . '/random_int.php'; |
161 require_once $RandomCompatDIR.'/random_bytes_com_dotnet.php'; |
221 } |
162 } |
222 |
163 } catch (com_exception $e) { |
223 $RandomCompatDIR = null; |
164 // Don't try to use it. |
|
165 } |
|
166 } |
|
167 $RandomCompat_disabled_classes = null; |
|
168 $RandomCompatCOMtest = null; |
|
169 } |
|
170 |
|
171 /** |
|
172 * openssl_random_pseudo_bytes() |
|
173 */ |
|
174 if ( |
|
175 ( |
|
176 // Unix-like with PHP >= 5.3.0 or |
|
177 ( |
|
178 DIRECTORY_SEPARATOR === '/' |
|
179 && |
|
180 PHP_VERSION_ID >= 50300 |
|
181 ) |
|
182 || |
|
183 // Windows with PHP >= 5.4.1 |
|
184 PHP_VERSION_ID >= 50401 |
|
185 ) |
|
186 && |
|
187 !function_exists('random_bytes') |
|
188 && |
|
189 extension_loaded('openssl') |
|
190 ) { |
|
191 // See random_bytes_openssl.php |
|
192 require_once $RandomCompatDIR.'/random_bytes_openssl.php'; |
|
193 } |
|
194 |
|
195 /** |
|
196 * throw new Exception |
|
197 */ |
|
198 if (!function_exists('random_bytes')) { |
|
199 /** |
|
200 * We don't have any more options, so let's throw an exception right now |
|
201 * and hope the developer won't let it fail silently. |
|
202 */ |
|
203 function random_bytes($length) |
|
204 { |
|
205 throw new Exception( |
|
206 'There is no suitable CSPRNG installed on your system' |
|
207 ); |
|
208 } |
|
209 } |
|
210 } |
|
211 |
|
212 if (!function_exists('random_int')) { |
|
213 require_once $RandomCompatDIR.'/random_int.php'; |
|
214 } |
|
215 |
|
216 $RandomCompatDIR = null; |
|
217 } |
|