Ticket #42439: 42439-random-compat.diff
| File 42439-random-compat.diff, 55.3 KB (added by , 9 years ago) |
|---|
-
src/wp-includes/random_compat/byte_safe_strings.php
5 5 * 6 6 * The MIT License (MIT) 7 7 * 8 * Copyright (c) 2015 Paragon Initiative Enterprises8 * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises 9 9 * 10 10 * Permission is hereby granted, free of charge, to any person obtaining a copy 11 11 * of this software and associated documentation files (the "Software"), to deal … … 26 26 * SOFTWARE. 27 27 */ 28 28 29 if (! function_exists('RandomCompat_strlen')) {29 if (!is_callable('RandomCompat_strlen')) { 30 30 if ( 31 31 defined('MB_OVERLOAD_STRING') && 32 32 ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING … … 51 51 ); 52 52 } 53 53 54 return mb_strlen($binary_string, '8bit');54 return (int) mb_strlen($binary_string, '8bit'); 55 55 } 56 56 57 57 } else { … … 73 73 'RandomCompat_strlen() expects a string' 74 74 ); 75 75 } 76 return strlen($binary_string);76 return (int) strlen($binary_string); 77 77 } 78 78 } 79 79 } 80 80 81 if (! function_exists('RandomCompat_substr')) {81 if (!is_callable('RandomCompat_substr')) { 82 82 83 83 if ( 84 84 defined('MB_OVERLOAD_STRING') … … 118 118 * mb_substr($str, 0, NULL, '8bit') returns an empty string on 119 119 * PHP 5.3, so we have to find the length ourselves. 120 120 */ 121 $length = RandomCompat_strlen($ length) - $start;121 $length = RandomCompat_strlen($binary_string) - $start; 122 122 } elseif (!is_int($length)) { 123 123 throw new TypeError( 124 124 'RandomCompat_substr(): Third argument should be an integer, or omitted' 125 125 ); 126 126 } 127 127 128 return mb_substr($binary_string, $start, $length, '8bit'); 128 // Consistency with PHP's behavior 129 if ($start === RandomCompat_strlen($binary_string) && $length === 0) { 130 return ''; 131 } 132 if ($start > RandomCompat_strlen($binary_string)) { 133 return ''; 134 } 135 136 return (string) mb_substr($binary_string, $start, $length, '8bit'); 129 137 } 130 138 131 139 } else { … … 164 172 ); 165 173 } 166 174 167 return substr($binary_string, $start, $length);175 return (string) substr($binary_string, $start, $length); 168 176 } 169 177 170 return substr($binary_string, $start);178 return (string) substr($binary_string, $start); 171 179 } 172 180 } 173 181 } -
src/wp-includes/random_compat/cast_to_int.php
5 5 * 6 6 * The MIT License (MIT) 7 7 * 8 * Copyright (c) 2015 Paragon Initiative Enterprises8 * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises 9 9 * 10 10 * Permission is hereby granted, free of charge, to any person obtaining a copy 11 11 * of this software and associated documentation files (the "Software"), to deal … … 26 26 * SOFTWARE. 27 27 */ 28 28 29 if (! function_exists('RandomCompat_intval')) {29 if (!is_callable('RandomCompat_intval')) { 30 30 31 31 /** 32 32 * Cast to an integer if we can, safely. … … 38 38 * through. 39 39 * 40 40 * @param int|float $number The number we want to convert to an int 41 * @param bool ean$fail_open Set to true to not throw an exception41 * @param bool $fail_open Set to true to not throw an exception 42 42 * 43 * @return int (or float if $fail_open) 43 * @return float|int 44 * @psalm-suppress InvalidReturnType 44 45 * 45 46 * @throws TypeError 46 47 */ 47 48 function RandomCompat_intval($number, $fail_open = false) 48 49 { 49 if (is_numeric($number)) { 50 if (is_int($number) || is_float($number)) { 51 $number += 0; 52 } elseif (is_numeric($number)) { 50 53 $number += 0; 51 54 } 52 55 … … 60 63 $number = (int) $number; 61 64 } 62 65 63 if (is_int($number) || $fail_open) { 64 return $number; 66 if (is_int($number)) { 67 return (int) $number; 68 } elseif (!$fail_open) { 69 throw new TypeError( 70 'Expected an integer.' 71 ); 65 72 } 66 67 throw new TypeError( 68 'Expected an integer.' 69 ); 73 return $number; 70 74 } 71 75 } -
src/wp-includes/random_compat/error_polyfill.php
4 4 * for using the new PHP 7 random_* API in PHP 5 projects 5 5 * 6 6 * The MIT License (MIT) 7 * 8 * Copyright (c) 2015 Paragon Initiative Enterprises7 * 8 * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises 9 9 * 10 10 * Permission is hereby granted, free of charge, to any person obtaining a copy 11 11 * of this software and associated documentation files (the "Software"), to deal … … 35 35 } 36 36 37 37 if (!class_exists('TypeError', false)) { 38 class TypeError extends Error 39 { 40 38 if (is_subclass_of('Error', 'Exception')) { 39 class TypeError extends Error 40 { 41 42 } 43 } else { 44 class TypeError extends Exception 45 { 46 47 } 41 48 } 42 49 } -
src/wp-includes/random_compat/random.php
3 3 * Random_* Compatibility Library 4 4 * for using the new PHP 7 random_* API in PHP 5 projects 5 5 * 6 * @version 1.2.17 * @released 201 6-02-296 * @version 2.0.10 7 * @released 2017-03-13 8 8 * 9 9 * The MIT License (MIT) 10 10 * 11 * Copyright (c) 2015 Paragon Initiative Enterprises11 * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises 12 12 * 13 13 * Permission is hereby granted, free of charge, to any person obtaining a copy 14 14 * of this software and associated documentation files (the "Software"), to deal … … 31 31 32 32 if (!defined('PHP_VERSION_ID')) { 33 33 // This constant was introduced in PHP 5.2.7 34 $RandomCompatversion = explode('.', PHP_VERSION);34 $RandomCompatversion = array_map('intval', explode('.', PHP_VERSION)); 35 35 define( 36 36 'PHP_VERSION_ID', 37 37 $RandomCompatversion[0] * 10000 … … 41 41 $RandomCompatversion = null; 42 42 } 43 43 44 if (PHP_VERSION_ID < 70000) { 45 46 if (!defined('RANDOM_COMPAT_READ_BUFFER')) { 47 define('RANDOM_COMPAT_READ_BUFFER', 8); 48 } 44 /** 45 * PHP 7.0.0 and newer have these functions natively. 46 */ 47 if (PHP_VERSION_ID >= 70000) { 48 return; 49 } 49 50 50 $RandomCompatDIR = dirname(__FILE__); 51 if (!defined('RANDOM_COMPAT_READ_BUFFER')) { 52 define('RANDOM_COMPAT_READ_BUFFER', 8); 53 } 51 54 52 require_once $RandomCompatDIR.'/byte_safe_strings.php'; 53 require_once $RandomCompatDIR.'/cast_to_int.php'; 54 require_once $RandomCompatDIR.'/error_polyfill.php'; 55 $RandomCompatDIR = dirname(__FILE__); 55 56 56 if (!function_exists('random_bytes')) { 57 /** 58 * PHP 5.2.0 - 5.6.x way to implement random_bytes() 59 * 60 * We use conditional statements here to define the function in accordance 61 * to the operating environment. It's a micro-optimization. 62 * 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 */ 72 if (extension_loaded('libsodium')) { 73 // See random_bytes_libsodium.php 74 if (PHP_VERSION_ID >= 50300 && function_exists('\\Sodium\\randombytes_buf')) { 75 require_once $RandomCompatDIR.'/random_bytes_libsodium.php'; 76 } elseif (method_exists('Sodium', 'randombytes_buf')) { 77 require_once $RandomCompatDIR.'/random_bytes_libsodium_legacy.php'; 78 } 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'; 79 82 } 83 } 80 84 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; 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; 121 104 } 122 105 123 /**124 * mcrypt_create_iv()125 */126 106 if ( 127 ! function_exists('random_bytes')107 !is_callable('random_bytes') 128 108 && 129 PHP_VERSION_ID >= 50307109 $RandomCompatUrandom 130 110 && 131 extension_loaded('mcrypt')111 @is_readable('/dev/urandom') 132 112 ) { 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 } 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 } 143 127 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 ); 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; 155 165 156 if (!in_array('com', $RandomCompat_disabled_classes)) { 157 try { 158 $RandomCompatCOMtest = new COM('CAPICOM.Utilities.1'); 159 if (method_exists($RandomCompatCOMtest, 'GetRandom')) { 160 // See random_bytes_com_dotnet.php 161 require_once $RandomCompatDIR.'/random_bytes_com_dotnet.php'; 162 } 163 } catch (com_exception $e) { 164 // Don't try to use it. 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'; 165 188 } 189 } catch (com_exception $e) { 190 // Don't try to use it. 166 191 } 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 or177 (178 DIRECTORY_SEPARATOR === '/'179 &&180 PHP_VERSION_ID >= 50300181 )182 ||183 // Windows with PHP >= 5.4.1184 PHP_VERSION_ID >= 50401185 )186 &&187 !function_exists('random_bytes')188 &&189 extension_loaded('openssl')190 ) {191 // See random_bytes_openssl.php192 require_once $RandomCompatDIR.'/random_bytes_openssl.php';193 192 } 193 $RandomCompat_disabled_classes = null; 194 $RandomCompatCOMtest = null; 195 } 194 196 197 /** 198 * throw new Exception 199 */ 200 if (!is_callable('random_bytes')) { 195 201 /** 196 * throw new Exception 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. 204 * 205 * @param mixed $length 206 * @return void 207 * @throws Exception 197 208 */ 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 function random_bytes($length) 210 { 211 unset($length); // Suppress "variable not used" warnings. 212 throw new Exception( 213 'There is no suitable CSPRNG installed on your system' 214 ); 209 215 } 210 216 } 217 } 211 218 212 if (!function_exists('random_int')) { 213 require_once $RandomCompatDIR.'/random_int.php'; 214 } 215 216 $RandomCompatDIR = null; 219 if (!is_callable('random_int')) { 220 require_once $RandomCompatDIR . '/random_int.php'; 217 221 } 222 223 $RandomCompatDIR = null; -
src/wp-includes/random_compat/random_bytes_com_dotnet.php
5 5 * 6 6 * The MIT License (MIT) 7 7 * 8 * Copyright (c) 2015 Paragon Initiative Enterprises8 * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises 9 9 * 10 10 * Permission is hereby granted, free of charge, to any person obtaining a copy 11 11 * of this software and associated documentation files (the "Software"), to deal … … 26 26 * SOFTWARE. 27 27 */ 28 28 29 /** 30 * Windows with PHP < 5.3.0 will not have the function 31 * openssl_random_pseudo_bytes() available, so let's use 32 * CAPICOM to work around this deficiency. 33 * 34 * @param int $bytes 35 * 36 * @throws Exception 37 * 38 * @return string 39 */ 40 function random_bytes($bytes) 41 { 42 try { 43 $bytes = RandomCompat_intval($bytes); 44 } catch (TypeError $ex) { 45 throw new TypeError( 46 'random_bytes(): $bytes must be an integer' 47 ); 48 } 49 50 if ($bytes < 1) { 51 throw new Error( 52 'Length must be greater than 0' 53 ); 54 } 55 56 $buf = ''; 57 $util = new COM('CAPICOM.Utilities.1'); 58 $execCount = 0; 59 29 if (!is_callable('random_bytes')) { 60 30 /** 61 * Let's not let it loop forever. If we run N times and fail to 62 * get N bytes of random data, then CAPICOM has failed us. 31 * Windows with PHP < 5.3.0 will not have the function 32 * openssl_random_pseudo_bytes() available, so let's use 33 * CAPICOM to work around this deficiency. 34 * 35 * @param int $bytes 36 * 37 * @throws Exception 38 * 39 * @return string 63 40 */ 64 do { 65 $buf .= base64_decode($util->GetRandom($bytes, 0)); 66 if (RandomCompat_strlen($buf) >= $bytes) { 67 /** 68 * Return our random entropy buffer here: 69 */ 70 return RandomCompat_substr($buf, 0, $bytes); 41 function random_bytes($bytes) 42 { 43 try { 44 $bytes = RandomCompat_intval($bytes); 45 } catch (TypeError $ex) { 46 throw new TypeError( 47 'random_bytes(): $bytes must be an integer' 48 ); 71 49 } 72 ++$execCount;73 } while ($execCount < $bytes);74 50 75 /** 76 * If we reach here, PHP has failed us. 77 */ 78 throw new Exception( 79 'Could not gather sufficient random data' 80 ); 81 } 51 if ($bytes < 1) { 52 throw new Error( 53 'Length must be greater than 0' 54 ); 55 } 56 57 $buf = ''; 58 if (!class_exists('COM')) { 59 throw new Error( 60 'COM does not exist' 61 ); 62 } 63 $util = new COM('CAPICOM.Utilities.1'); 64 $execCount = 0; 65 66 /** 67 * Let's not let it loop forever. If we run N times and fail to 68 * get N bytes of random data, then CAPICOM has failed us. 69 */ 70 do { 71 $buf .= base64_decode($util->GetRandom($bytes, 0)); 72 if (RandomCompat_strlen($buf) >= $bytes) { 73 /** 74 * Return our random entropy buffer here: 75 */ 76 return RandomCompat_substr($buf, 0, $bytes); 77 } 78 ++$execCount; 79 } while ($execCount < $bytes); 80 81 /** 82 * If we reach here, PHP has failed us. 83 */ 84 throw new Exception( 85 'Could not gather sufficient random data' 86 ); 87 } 88 } 89 No newline at end of file -
src/wp-includes/random_compat/random_bytes_dev_urandom.php
4 4 * for using the new PHP 7 random_* API in PHP 5 projects 5 5 * 6 6 * The MIT License (MIT) 7 * 8 * Copyright (c) 2015 Paragon Initiative Enterprises7 * 8 * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises 9 9 * 10 10 * Permission is hereby granted, free of charge, to any person obtaining a copy 11 11 * of this software and associated documentation files (the "Software"), to deal … … 30 30 define('RANDOM_COMPAT_READ_BUFFER', 8); 31 31 } 32 32 33 /** 34 * Unless open_basedir is enabled, use /dev/urandom for 35 * random numbers in accordance with best practices 36 * 37 * Why we use /dev/urandom and not /dev/random 38 * @ref http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers 39 * 40 * @param int $bytes 41 * 42 * @throws Exception 43 * 44 * @return string 45 */ 46 function random_bytes($bytes) 47 { 48 static $fp = null; 33 if (!is_callable('random_bytes')) { 49 34 /** 50 * This block should only be run once 35 * Unless open_basedir is enabled, use /dev/urandom for 36 * random numbers in accordance with best practices 37 * 38 * Why we use /dev/urandom and not /dev/random 39 * @ref http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers 40 * 41 * @param int $bytes 42 * 43 * @throws Exception 44 * 45 * @return string 51 46 */ 52 if (empty($fp)) { 47 function random_bytes($bytes) 48 { 49 static $fp = null; 53 50 /** 54 * We use /dev/urandom if it is a char device. 55 * We never fall back to /dev/random 51 * This block should only be run once 56 52 */ 57 $fp = fopen('/dev/urandom', 'rb'); 58 if (!empty($fp)) { 59 $st = fstat($fp); 60 if (($st['mode'] & 0170000) !== 020000) { 61 fclose($fp); 62 $fp = false; 63 } 64 } 65 66 if (!empty($fp)) { 53 if (empty($fp)) { 67 54 /** 68 * stream_set_read_buffer() does not exist in HHVM 69 * 70 * If we don't set the stream's read buffer to 0, PHP will 71 * internally buffer 8192 bytes, which can waste entropy 72 * 73 * stream_set_read_buffer returns 0 on success 55 * We use /dev/urandom if it is a char device. 56 * We never fall back to /dev/random 74 57 */ 75 if (function_exists('stream_set_read_buffer')) { 76 stream_set_read_buffer($fp, RANDOM_COMPAT_READ_BUFFER); 58 $fp = fopen('/dev/urandom', 'rb'); 59 if (!empty($fp)) { 60 $st = fstat($fp); 61 if (($st['mode'] & 0170000) !== 020000) { 62 fclose($fp); 63 $fp = false; 64 } 77 65 } 78 if (function_exists('stream_set_chunk_size')) { 79 stream_set_chunk_size($fp, RANDOM_COMPAT_READ_BUFFER); 66 67 if (!empty($fp)) { 68 /** 69 * stream_set_read_buffer() does not exist in HHVM 70 * 71 * If we don't set the stream's read buffer to 0, PHP will 72 * internally buffer 8192 bytes, which can waste entropy 73 * 74 * stream_set_read_buffer returns 0 on success 75 */ 76 if (is_callable('stream_set_read_buffer')) { 77 stream_set_read_buffer($fp, RANDOM_COMPAT_READ_BUFFER); 78 } 79 if (is_callable('stream_set_chunk_size')) { 80 stream_set_chunk_size($fp, RANDOM_COMPAT_READ_BUFFER); 81 } 80 82 } 81 83 } 82 }83 84 try {85 $bytes = RandomCompat_intval($bytes);86 } catch (TypeError $ex) {87 throw new TypeError(88 'random_bytes(): $bytes must be an integer'89 );90 }91 84 92 if ($bytes < 1) { 93 throw new Error( 94 'Length must be greater than 0' 95 ); 96 } 85 try { 86 $bytes = RandomCompat_intval($bytes); 87 } catch (TypeError $ex) { 88 throw new TypeError( 89 'random_bytes(): $bytes must be an integer' 90 ); 91 } 97 92 98 /** 99 * This if() block only runs if we managed to open a file handle 100 * 101 * It does not belong in an else {} block, because the above 102 * if (empty($fp)) line is logic that should only be run once per 103 * page load. 104 */ 105 if (!empty($fp)) { 106 $remaining = $bytes; 107 $buf = ''; 93 if ($bytes < 1) { 94 throw new Error( 95 'Length must be greater than 0' 96 ); 97 } 108 98 109 99 /** 110 * We use fread() in a loop to protect against partial reads 100 * This if() block only runs if we managed to open a file handle 101 * 102 * It does not belong in an else {} block, because the above 103 * if (empty($fp)) line is logic that should only be run once per 104 * page load. 111 105 */ 112 do { 113 $read = fread($fp, $remaining); 114 if ($read === false) { 115 /** 116 * We cannot safely read from the file. Exit the 117 * do-while loop and trigger the exception condition 118 */ 119 $buf = false; 120 break; 121 } 106 if (!empty($fp)) { 122 107 /** 123 * Decrease the number of bytes returned from remaining108 * @var int 124 109 */ 125 $remaining -= RandomCompat_strlen($read); 126 $buf .= $read; 127 } while ($remaining > 0); 128 129 /** 130 * Is our result valid? 131 */ 132 if ($buf !== false) { 133 if (RandomCompat_strlen($buf) === $bytes) { 110 $remaining = $bytes; 111 112 /** 113 * @var string|bool 114 */ 115 $buf = ''; 116 117 /** 118 * We use fread() in a loop to protect against partial reads 119 */ 120 do { 121 /** 122 * @var string|bool 123 */ 124 $read = fread($fp, $remaining); 125 if (!is_string($read)) { 126 if ($read === false) { 127 /** 128 * We cannot safely read from the file. Exit the 129 * do-while loop and trigger the exception condition 130 * 131 * @var string|bool 132 */ 133 $buf = false; 134 break; 135 } 136 } 134 137 /** 135 * Return our random entropy buffer here:138 * Decrease the number of bytes returned from remaining 136 139 */ 137 return $buf; 140 $remaining -= RandomCompat_strlen($read); 141 /** 142 * @var string|bool 143 */ 144 $buf = $buf . $read; 145 } while ($remaining > 0); 146 147 /** 148 * Is our result valid? 149 */ 150 if (is_string($buf)) { 151 if (RandomCompat_strlen($buf) === $bytes) { 152 /** 153 * Return our random entropy buffer here: 154 */ 155 return $buf; 156 } 138 157 } 139 158 } 140 }141 159 142 /** 143 * If we reach here, PHP has failed us. 144 */ 145 throw new Exception( 146 'Error reading from source device' 147 ); 160 /** 161 * If we reach here, PHP has failed us. 162 */ 163 throw new Exception( 164 'Error reading from source device' 165 ); 166 } 148 167 } -
src/wp-includes/random_compat/random_bytes_libsodium.php
4 4 * for using the new PHP 7 random_* API in PHP 5 projects 5 5 * 6 6 * The MIT License (MIT) 7 * 8 * Copyright (c) 2015 Paragon Initiative Enterprises7 * 8 * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises 9 9 * 10 10 * Permission is hereby granted, free of charge, to any person obtaining a copy 11 11 * of this software and associated documentation files (the "Software"), to deal … … 26 26 * SOFTWARE. 27 27 */ 28 28 29 /** 30 * If the libsodium PHP extension is loaded, we'll use it above any other 31 * solution. 32 * 33 * libsodium-php project: 34 * @ref https://github.com/jedisct1/libsodium-php 35 * 36 * @param int $bytes 37 * 38 * @throws Exception 39 * 40 * @return string 41 */ 42 function random_bytes($bytes) 43 { 44 try { 45 $bytes = RandomCompat_intval($bytes); 46 } catch (TypeError $ex) { 47 throw new TypeError( 48 'random_bytes(): $bytes must be an integer' 49 ); 50 } 51 52 if ($bytes < 1) { 53 throw new Error( 54 'Length must be greater than 0' 55 ); 56 } 57 29 if (!is_callable('random_bytes')) { 58 30 /** 59 * \Sodium\randombytes_buf() doesn't allow more than 2147483647 bytes to be 60 * generated in one invocation. 31 * If the libsodium PHP extension is loaded, we'll use it above any other 32 * solution. 33 * 34 * libsodium-php project: 35 * @ref https://github.com/jedisct1/libsodium-php 36 * 37 * @param int $bytes 38 * 39 * @throws Exception 40 * 41 * @return string 61 42 */ 62 if ($bytes > 2147483647) { 63 $buf = ''; 64 for ($i = 0; $i < $bytes; $i += 1073741824) { 65 $n = ($bytes - $i) > 1073741824 66 ? 1073741824 67 : $bytes - $i; 68 $buf .= \Sodium\randombytes_buf($n); 43 function random_bytes($bytes) 44 { 45 try { 46 $bytes = RandomCompat_intval($bytes); 47 } catch (TypeError $ex) { 48 throw new TypeError( 49 'random_bytes(): $bytes must be an integer' 50 ); 69 51 } 70 } else {71 $buf = \Sodium\randombytes_buf($bytes);72 }73 52 74 if ($buf !== false) { 75 if (RandomCompat_strlen($buf) === $bytes) { 76 return $buf; 53 if ($bytes < 1) { 54 throw new Error( 55 'Length must be greater than 0' 56 ); 77 57 } 78 }79 58 80 /** 81 * If we reach here, PHP has failed us. 82 */ 83 throw new Exception( 84 'Could not gather sufficient random data' 85 ); 59 /** 60 * \Sodium\randombytes_buf() doesn't allow more than 2147483647 bytes to be 61 * generated in one invocation. 62 */ 63 if ($bytes > 2147483647) { 64 $buf = ''; 65 for ($i = 0; $i < $bytes; $i += 1073741824) { 66 $n = ($bytes - $i) > 1073741824 67 ? 1073741824 68 : $bytes - $i; 69 $buf .= \Sodium\randombytes_buf($n); 70 } 71 } else { 72 $buf = \Sodium\randombytes_buf($bytes); 73 } 74 75 if ($buf !== false) { 76 if (RandomCompat_strlen($buf) === $bytes) { 77 return $buf; 78 } 79 } 80 81 /** 82 * If we reach here, PHP has failed us. 83 */ 84 throw new Exception( 85 'Could not gather sufficient random data' 86 ); 87 } 86 88 } -
src/wp-includes/random_compat/random_bytes_libsodium_legacy.php
4 4 * for using the new PHP 7 random_* API in PHP 5 projects 5 5 * 6 6 * The MIT License (MIT) 7 * 8 * Copyright (c) 2015 Paragon Initiative Enterprises7 * 8 * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises 9 9 * 10 10 * Permission is hereby granted, free of charge, to any person obtaining a copy 11 11 * of this software and associated documentation files (the "Software"), to deal … … 26 26 * SOFTWARE. 27 27 */ 28 28 29 /** 30 * If the libsodium PHP extension is loaded, we'll use it above any other 31 * solution. 32 * 33 * libsodium-php project: 34 * @ref https://github.com/jedisct1/libsodium-php 35 * 36 * @param int $bytes 37 * 38 * @throws Exception 39 * 40 * @return string 41 */ 42 function random_bytes($bytes) 43 { 44 try { 45 $bytes = RandomCompat_intval($bytes); 46 } catch (TypeError $ex) { 47 throw new TypeError( 48 'random_bytes(): $bytes must be an integer' 49 ); 50 } 51 52 if ($bytes < 1) { 53 throw new Error( 54 'Length must be greater than 0' 55 ); 56 } 57 29 if (!is_callable('random_bytes')) { 58 30 /** 59 * \Sodium\randombytes_buf() doesn't allow more than 2147483647 bytes to be 60 * generated in one invocation. 31 * If the libsodium PHP extension is loaded, we'll use it above any other 32 * solution. 33 * 34 * libsodium-php project: 35 * @ref https://github.com/jedisct1/libsodium-php 36 * 37 * @param int $bytes 38 * 39 * @throws Exception 40 * 41 * @return string 61 42 */ 62 if ($bytes > 2147483647) { 43 function random_bytes($bytes) 44 { 45 try { 46 $bytes = RandomCompat_intval($bytes); 47 } catch (TypeError $ex) { 48 throw new TypeError( 49 'random_bytes(): $bytes must be an integer' 50 ); 51 } 52 53 if ($bytes < 1) { 54 throw new Error( 55 'Length must be greater than 0' 56 ); 57 } 58 59 /** 60 * @var string 61 */ 63 62 $buf = ''; 64 for ($i = 0; $i < $bytes; $i += 1073741824) { 65 $n = ($bytes - $i) > 1073741824 66 ? 1073741824 67 : $bytes - $i; 68 $buf .= Sodium::randombytes_buf($n); 63 64 /** 65 * \Sodium\randombytes_buf() doesn't allow more than 2147483647 bytes to be 66 * generated in one invocation. 67 */ 68 if ($bytes > 2147483647) { 69 for ($i = 0; $i < $bytes; $i += 1073741824) { 70 $n = ($bytes - $i) > 1073741824 71 ? 1073741824 72 : $bytes - $i; 73 $buf .= Sodium::randombytes_buf((int) $n); 74 } 75 } else { 76 $buf .= Sodium::randombytes_buf((int) $bytes); 69 77 } 70 } else {71 $buf = Sodium::randombytes_buf($bytes);72 }73 78 74 if ($buf !== false) { 75 if (RandomCompat_strlen($buf) === $bytes) { 76 return $buf; 79 if (is_string($buf)) { 80 if (RandomCompat_strlen($buf) === $bytes) { 81 return $buf; 82 } 77 83 } 78 }79 84 80 /** 81 * If we reach here, PHP has failed us. 82 */ 83 throw new Exception( 84 'Could not gather sufficient random data' 85 ); 85 /** 86 * If we reach here, PHP has failed us. 87 */ 88 throw new Exception( 89 'Could not gather sufficient random data' 90 ); 91 } 86 92 } -
src/wp-includes/random_compat/random_bytes_mcrypt.php
4 4 * for using the new PHP 7 random_* API in PHP 5 projects 5 5 * 6 6 * The MIT License (MIT) 7 * 8 * Copyright (c) 2015 Paragon Initiative Enterprises7 * 8 * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises 9 9 * 10 10 * Permission is hereby granted, free of charge, to any person obtaining a copy 11 11 * of this software and associated documentation files (the "Software"), to deal … … 26 26 * SOFTWARE. 27 27 */ 28 28 29 if (!is_callable('random_bytes')) { 30 /** 31 * Powered by ext/mcrypt (and thankfully NOT libmcrypt) 32 * 33 * @ref https://bugs.php.net/bug.php?id=55169 34 * @ref https://github.com/php/php-src/blob/c568ffe5171d942161fc8dda066bce844bdef676/ext/mcrypt/mcrypt.c#L1321-L1386 35 * 36 * @param int $bytes 37 * 38 * @throws Exception 39 * 40 * @return string 41 */ 42 function random_bytes($bytes) 43 { 44 try { 45 $bytes = RandomCompat_intval($bytes); 46 } catch (TypeError $ex) { 47 throw new TypeError( 48 'random_bytes(): $bytes must be an integer' 49 ); 50 } 29 51 30 /** 31 * Powered by ext/mcrypt (and thankfully NOT libmcrypt) 32 * 33 * @ref https://bugs.php.net/bug.php?id=55169 34 * @ref https://github.com/php/php-src/blob/c568ffe5171d942161fc8dda066bce844bdef676/ext/mcrypt/mcrypt.c#L1321-L1386 35 * 36 * @param int $bytes 37 * 38 * @throws Exception 39 * 40 * @return string 41 */ 42 function random_bytes($bytes) 43 { 44 try { 45 $bytes = RandomCompat_intval($bytes); 46 } catch (TypeError $ex) { 47 throw new TypeError( 48 'random_bytes(): $bytes must be an integer' 49 ); 50 } 52 if ($bytes < 1) { 53 throw new Error( 54 'Length must be greater than 0' 55 ); 56 } 51 57 52 if ($bytes < 1) { 53 throw new Error( 54 'Length must be greater than 0' 55 ); 56 } 58 $buf = @mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM); 59 if ( 60 $buf !== false 61 && 62 RandomCompat_strlen($buf) === $bytes 63 ) { 64 /** 65 * Return our random entropy buffer here: 66 */ 67 return $buf; 68 } 57 69 58 $buf = @mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM);59 if (60 $buf !== false61 &&62 RandomCompat_strlen($buf) === $bytes63 ) {64 70 /** 65 * Return our random entropy buffer here:71 * If we reach here, PHP has failed us. 66 72 */ 67 return $buf; 73 throw new Exception( 74 'Could not gather sufficient random data' 75 ); 68 76 } 69 70 /**71 * If we reach here, PHP has failed us.72 */73 throw new Exception(74 'Could not gather sufficient random data'75 );76 77 } -
src/wp-includes/random_compat/random_bytes_openssl.php
1 <?php2 /**3 * Random_* Compatibility Library4 * for using the new PHP 7 random_* API in PHP 5 projects5 *6 * The MIT License (MIT)7 *8 * Copyright (c) 2015 Paragon Initiative Enterprises9 *10 * Permission is hereby granted, free of charge, to any person obtaining a copy11 * of this software and associated documentation files (the "Software"), to deal12 * in the Software without restriction, including without limitation the rights13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell14 * copies of the Software, and to permit persons to whom the Software is15 * furnished to do so, subject to the following conditions:16 *17 * The above copyright notice and this permission notice shall be included in18 * all copies or substantial portions of the Software.19 *20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE26 * SOFTWARE.27 */28 29 /**30 * Since openssl_random_pseudo_bytes() uses openssl's31 * RAND_pseudo_bytes() API, which has been marked as deprecated by the32 * OpenSSL team, this is our last resort before failure.33 *34 * @ref https://www.openssl.org/docs/crypto/RAND_bytes.html35 *36 * @param int $bytes37 *38 * @throws Exception39 *40 * @return string41 */42 function random_bytes($bytes)43 {44 try {45 $bytes = RandomCompat_intval($bytes);46 } catch (TypeError $ex) {47 throw new TypeError(48 'random_bytes(): $bytes must be an integer'49 );50 }51 52 if ($bytes < 1) {53 throw new Error(54 'Length must be greater than 0'55 );56 }57 58 /**59 * $secure is passed by reference. If it's set to false, fail. Note60 * that this will only return false if this function fails to return61 * any data.62 *63 * @ref https://github.com/paragonie/random_compat/issues/6#issuecomment-11956497364 */65 $secure = true;66 $buf = openssl_random_pseudo_bytes($bytes, $secure);67 if (68 $buf !== false69 &&70 $secure71 &&72 RandomCompat_strlen($buf) === $bytes73 ) {74 return $buf;75 }76 77 /**78 * If we reach here, PHP has failed us.79 */80 throw new Exception(81 'Could not gather sufficient random data'82 );83 } -
src/wp-includes/random_compat/random_int.php
1 1 <?php 2 /**3 * Random_* Compatibility Library4 * for using the new PHP 7 random_* API in PHP 5 projects5 *6 * The MIT License (MIT)7 *8 * Copyright (c) 2015 Paragon Initiative Enterprises9 *10 * Permission is hereby granted, free of charge, to any person obtaining a copy11 * of this software and associated documentation files (the "Software"), to deal12 * in the Software without restriction, including without limitation the rights13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell14 * copies of the Software, and to permit persons to whom the Software is15 * furnished to do so, subject to the following conditions:16 *17 * The above copyright notice and this permission notice shall be included in18 * all copies or substantial portions of the Software.19 *20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE26 * SOFTWARE.27 */28 29 /**30 * Fetch a random integer between $min and $max inclusive31 *32 * @param int $min33 * @param int $max34 *35 * @throws Exception36 *37 * @return int38 */39 function random_int($min, $max)40 {41 /**42 * Type and input logic checks43 *44 * If you pass it a float in the range (~PHP_INT_MAX, PHP_INT_MAX)45 * (non-inclusive), it will sanely cast it to an int. If you it's equal to46 * ~PHP_INT_MAX or PHP_INT_MAX, we let it fail as not an integer. Floats47 * lose precision, so the <= and => operators might accidentally let a float48 * through.49 */50 51 try {52 $min = RandomCompat_intval($min);53 } catch (TypeError $ex) {54 throw new TypeError(55 'random_int(): $min must be an integer'56 );57 }58 2 59 try { 60 $max = RandomCompat_intval($max); 61 } catch (TypeError $ex) { 62 throw new TypeError( 63 'random_int(): $max must be an integer' 64 ); 65 } 66 3 if (!is_callable('random_int')) { 67 4 /** 68 * Now that we've verified our weak typing system has given us an integer, 69 * let's validate the logic then we can move forward with generating random 70 * integers along a given range. 5 * Random_* Compatibility Library 6 * for using the new PHP 7 random_* API in PHP 5 projects 7 * 8 * The MIT License (MIT) 9 * 10 * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises 11 * 12 * Permission is hereby granted, free of charge, to any person obtaining a copy 13 * of this software and associated documentation files (the "Software"), to deal 14 * in the Software without restriction, including without limitation the rights 15 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 * copies of the Software, and to permit persons to whom the Software is 17 * furnished to do so, subject to the following conditions: 18 * 19 * The above copyright notice and this permission notice shall be included in 20 * all copies or substantial portions of the Software. 21 * 22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 * SOFTWARE. 71 29 */ 72 if ($min > $max) {73 throw new Error(74 'Minimum value must be less than or equal to the maximum value'75 );76 }77 78 if ($max === $min) {79 return $min;80 }81 30 82 31 /** 83 * Initialize variables to 0 84 * 85 * We want to store: 86 * $bytes => the number of random bytes we need 87 * $mask => an integer bitmask (for use with the &) operator 88 * so we can minimize the number of discards 89 */ 90 $attempts = $bits = $bytes = $mask = $valueShift = 0; 91 92 /** 93 * At this point, $range is a positive number greater than 0. It might 94 * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to 95 * a float and we will lose some precision. 96 */ 97 $range = $max - $min; 98 99 /** 100 * Test for integer overflow: 101 */ 102 if (!is_int($range)) { 103 104 /** 105 * Still safely calculate wider ranges. 106 * Provided by @CodesInChaos, @oittaa 107 * 108 * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435 109 * 110 * We use ~0 as a mask in this case because it generates all 1s 111 * 112 * @ref https://eval.in/400356 (32-bit) 113 * @ref http://3v4l.org/XX9r5 (64-bit) 114 */ 115 $bytes = PHP_INT_SIZE; 116 $mask = ~0; 117 118 } else { 119 120 /** 121 * $bits is effectively ceil(log($range, 2)) without dealing with 122 * type juggling 123 */ 124 while ($range > 0) { 125 if ($bits % 8 === 0) { 126 ++$bytes; 127 } 128 ++$bits; 129 $range >>= 1; 130 $mask = $mask << 1 | 1; 32 * Fetch a random integer between $min and $max inclusive 33 * 34 * @param int $min 35 * @param int $max 36 * 37 * @throws Exception 38 * 39 * @return int 40 */ 41 function random_int($min, $max) 42 { 43 /** 44 * Type and input logic checks 45 * 46 * If you pass it a float in the range (~PHP_INT_MAX, PHP_INT_MAX) 47 * (non-inclusive), it will sanely cast it to an int. If you it's equal to 48 * ~PHP_INT_MAX or PHP_INT_MAX, we let it fail as not an integer. Floats 49 * lose precision, so the <= and => operators might accidentally let a float 50 * through. 51 */ 52 53 try { 54 $min = RandomCompat_intval($min); 55 } catch (TypeError $ex) { 56 throw new TypeError( 57 'random_int(): $min must be an integer' 58 ); 131 59 } 132 $valueShift = $min;133 }134 60 135 /** 136 * Now that we have our parameters set up, let's begin generating 137 * random integers until one falls between $min and $max 138 */ 139 do { 140 /** 141 * The rejection probability is at most 0.5, so this corresponds 142 * to a failure probability of 2^-128 for a working RNG 143 */ 144 if ($attempts > 128) { 145 throw new Exception( 146 'random_int: RNG is broken - too many rejections' 61 try { 62 $max = RandomCompat_intval($max); 63 } catch (TypeError $ex) { 64 throw new TypeError( 65 'random_int(): $max must be an integer' 147 66 ); 148 67 } 149 68 150 69 /** 151 * Let's grab the necessary number of random bytes 152 */ 153 $randomByteString = random_bytes($bytes); 154 if ($randomByteString === false) { 155 throw new Exception( 156 'Random number generator failure' 70 * Now that we've verified our weak typing system has given us an integer, 71 * let's validate the logic then we can move forward with generating random 72 * integers along a given range. 73 */ 74 if ($min > $max) { 75 throw new Error( 76 'Minimum value must be less than or equal to the maximum value' 157 77 ); 158 78 } 159 79 160 /** 161 * Let's turn $randomByteString into an integer 162 * 163 * This uses bitwise operators (<< and |) to build an integer 164 * out of the values extracted from ord() 165 * 166 * Example: [9F] | [6D] | [32] | [0C] => 167 * 159 + 27904 + 3276800 + 201326592 => 168 * 204631455 169 */ 170 $val = 0; 171 for ($i = 0; $i < $bytes; ++$i) { 172 $val |= ord($randomByteString[$i]) << ($i * 8); 80 if ($max === $min) { 81 return (int) $min; 173 82 } 174 83 175 84 /** 176 * Apply mask 177 */ 178 $val &= $mask; 179 $val += $valueShift; 85 * Initialize variables to 0 86 * 87 * We want to store: 88 * $bytes => the number of random bytes we need 89 * $mask => an integer bitmask (for use with the &) operator 90 * so we can minimize the number of discards 91 */ 92 $attempts = $bits = $bytes = $mask = $valueShift = 0; 93 94 /** 95 * At this point, $range is a positive number greater than 0. It might 96 * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to 97 * a float and we will lose some precision. 98 */ 99 $range = $max - $min; 100 101 /** 102 * Test for integer overflow: 103 */ 104 if (!is_int($range)) { 105 106 /** 107 * Still safely calculate wider ranges. 108 * Provided by @CodesInChaos, @oittaa 109 * 110 * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435 111 * 112 * We use ~0 as a mask in this case because it generates all 1s 113 * 114 * @ref https://eval.in/400356 (32-bit) 115 * @ref http://3v4l.org/XX9r5 (64-bit) 116 */ 117 $bytes = PHP_INT_SIZE; 118 $mask = ~0; 119 120 } else { 121 122 /** 123 * $bits is effectively ceil(log($range, 2)) without dealing with 124 * type juggling 125 */ 126 while ($range > 0) { 127 if ($bits % 8 === 0) { 128 ++$bytes; 129 } 130 ++$bits; 131 $range >>= 1; 132 $mask = $mask << 1 | 1; 133 } 134 $valueShift = $min; 135 } 180 136 181 ++$attempts;137 $val = 0; 182 138 /** 183 * If $val overflows to a floating point number, 184 * ... or is larger than $max, 185 * ... or smaller than $min, 186 * then try again. 139 * Now that we have our parameters set up, let's begin generating 140 * random integers until one falls between $min and $max 187 141 */ 188 } while (!is_int($val) || $val > $max || $val < $min); 142 do { 143 /** 144 * The rejection probability is at most 0.5, so this corresponds 145 * to a failure probability of 2^-128 for a working RNG 146 */ 147 if ($attempts > 128) { 148 throw new Exception( 149 'random_int: RNG is broken - too many rejections' 150 ); 151 } 152 153 /** 154 * Let's grab the necessary number of random bytes 155 */ 156 $randomByteString = random_bytes($bytes); 157 158 /** 159 * Let's turn $randomByteString into an integer 160 * 161 * This uses bitwise operators (<< and |) to build an integer 162 * out of the values extracted from ord() 163 * 164 * Example: [9F] | [6D] | [32] | [0C] => 165 * 159 + 27904 + 3276800 + 201326592 => 166 * 204631455 167 */ 168 $val &= 0; 169 for ($i = 0; $i < $bytes; ++$i) { 170 $val |= ord($randomByteString[$i]) << ($i * 8); 171 } 189 172 190 return (int) $val; 173 /** 174 * Apply mask 175 */ 176 $val &= $mask; 177 $val += $valueShift; 178 179 ++$attempts; 180 /** 181 * If $val overflows to a floating point number, 182 * ... or is larger than $max, 183 * ... or smaller than $min, 184 * then try again. 185 */ 186 } while (!is_int($val) || $val > $max || $val < $min); 187 188 return (int) $val; 189 } 191 190 }
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)