| 2020 | if ( !function_exists('wp_random_bytes') ) : |
| 2021 | |
| 2022 | /** |
| 2023 | * Generate a cryptographically secure random string |
| 2024 | * @ref http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/ |
| 2025 | * |
| 2026 | * @param int $bytes - how many bytes do we want? |
| 2027 | */ |
| 2028 | function wp_random_bytes($bytes = 32) { |
| 2029 | $buf = ''; |
| 2030 | /** |
| 2031 | * This is preferable to userspace RNGs (e.g. openssl) |
| 2032 | * because it reads directly from /dev/urandom on most OS's, |
| 2033 | * and uses Windows's Crypto APIs transparently. |
| 2034 | * |
| 2035 | * See PHP bug #55169 for why version 5.3.7 is required |
| 2036 | * |
| 2037 | * Requires the mcrypt extension be installed/enabled. |
| 2038 | */ |
| 2039 | if ( function_exists('mcrypt_create_iv') && version_compare(PHP_VERSION, '5.3.7') >= 0 ) { |
| 2040 | $buf = mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM); |
| 2041 | if ($buf !== FALSE) { |
| 2042 | return $buf; |
| 2043 | } |
| 2044 | } |
| 2045 | /** |
| 2046 | * If /dev/urandom is available, let's prefer it over every other method |
| 2047 | * |
| 2048 | * This should only return false on Windows hosts and in chroot jails |
| 2049 | */ |
| 2050 | if ( is_readable('/dev/urandom') ) { |
| 2051 | $fp = fopen('/dev/urandom', 'rb'); |
| 2052 | // Extra cautious, thanks to feedback from Taylor Hornby (https://defuse.ca) |
| 2053 | $streamset = stream_set_read_buffer($fp, 0); |
| 2054 | if ( $fp !== false && $streamset === 0 ) { |
| 2055 | $buf = ''; |
| 2056 | // How many bytes do we need? |
| 2057 | $remaining = $bytes; |
| 2058 | do { |
| 2059 | $read = fread($fp, $remaining); |
| 2060 | if ( $read === FALSE ) { |
| 2061 | // We cannot safely read from urandom. |
| 2062 | $buf = FALSE; |
| 2063 | break; |
| 2064 | } |
| 2065 | // Decrease the number of bytes returned from remaining |
| 2066 | $remaining -= wp_binsafe_strlen($read); |
| 2067 | |
| 2068 | // We actually read bytes! Let's append them to our buffer |
| 2069 | $buf .= $read; |
| 2070 | |
| 2071 | } while ( $remaining > 0 ); |
| 2072 | // Let's make sure we have enough bytes |
| 2073 | |
| 2074 | fclose($fp); |
| 2075 | if ( $buf !== FALSE ) { |
| 2076 | return $buf; |
| 2077 | } |
| 2078 | } |
| 2079 | } |
| 2080 | |
| 2081 | /** |
| 2082 | * This is available since PHP 5.3.0 on every PHP install. |
| 2083 | * |
| 2084 | * Better than nothing, but not really urandom |
| 2085 | */ |
| 2086 | if ( function_exists('openssl_random_pseudo_bytes') ) { |
| 2087 | $buf = openssl_random_pseudo_bytes($bytes); |
| 2088 | if ( $buf !== false ) { |
| 2089 | return $buf; |
| 2090 | } |
| 2091 | } |
| 2092 | |
| 2093 | /** |
| 2094 | * Windows with PHP < 5.3.0 will not have the function |
| 2095 | * openssl_random_pseudo_bytes() available, so let's use |
| 2096 | * CAPICOM to work around this deficiency. |
| 2097 | */ |
| 2098 | if ( class_exists('COM', false) ) { |
| 2099 | try { |
| 2100 | if ($buf === FALSE) { |
| 2101 | $buf = ''; // Make it a string, not false |
| 2102 | } |
| 2103 | $util = new COM('CAPICOM.Utilities.1'); |
| 2104 | $execs = 0; |
| 2105 | /** |
| 2106 | * Let's not let it loop forever. If we run N times and fail to |
| 2107 | * get N bytes of entropy, then CAPICOM has failed us. |
| 2108 | */ |
| 2109 | do { |
| 2110 | $buf .= base64_decode( $util->GetRandom($bytes, 0) ); |
| 2111 | if ( wp_binsafe_strlen($buf) >= $bytes ) { |
| 2112 | return wp_binsafe_substr($buf, 0, $bytes); |
| 2113 | } |
| 2114 | ++$execs; |
| 2115 | } while ( $execs < $bytes ); |
| 2116 | } catch ( Exception($e) ) { |
| 2117 | unset($e); // Let's not let CAPICOM errors kill our app |
| 2118 | } |
| 2119 | } |
| 2120 | if ($buf === FALSE) { |
| 2121 | $buf = ''; // Make it a string, not false |
| 2122 | } |
| 2123 | /** |
| 2124 | * Okay, at this point, we cannot guarantee a CSPRNG no matter what we |
| 2125 | * do. Our only recourse is to rely on wp_rand() |
| 2126 | */ |
| 2127 | for ($i = 0; $i < $bytes; ++$i) { |
| 2128 | $buf .= chr( wp_rand(0, 255) ); |
| 2129 | } |
| 2130 | return $buf; |
| 2131 | } |
| 2132 | endif; |
| 2133 | |
| 2134 | if ( !function_exists('wp_binsafe_strlen') ): |
| 2135 | /** |
| 2136 | * Binary-safe string length (won't get chewed by multibyte strings) |
| 2137 | * |
| 2138 | * @param string $str The string whose length we are retreiving |
| 2139 | * @return int The string length (binary-safe) |
| 2140 | */ |
| 2141 | function wp_binsafe_strlen($str) { |
| 2142 | if ( function_exists('mb_strlen') ) { |
| 2143 | return mb_strlen($str, '8bit'); |
| 2144 | } |
| 2145 | return strlen($str); |
| 2146 | } |
| 2147 | endif; |
| 2148 | if ( !function_exists('wp_binsafe_substr') ): |
| 2149 | /** |
| 2150 | * Binary-safe substring (won't get chewed by multibyte strings) |
| 2151 | * |
| 2152 | * @param string $str The string whose length we are retreiving |
| 2153 | * @param int $start The starting point in the binary string |
| 2154 | * @param int $length optional - how many bytes to select |
| 2155 | * @return int The string length (binary-safe) |
| 2156 | */ |
| 2157 | function wp_binsafe_substr($str, $start, $length = null) { |
| 2158 | if ( function_exists('mb_substr') ) { |
| 2159 | return mb_substr($str, $start, $length, '8bit'); |
| 2160 | } |
| 2161 | return substr($str, $start, $length); |
| 2162 | } |
| 2163 | endif; |
| 2164 | |
| 2165 | |
| 2166 | if ( !function_exists('wp_random_positive_int') ): |
| 2167 | /** |
| 2168 | * Generate a random positive integer between 0 and PHP_INT_MAX |
| 2169 | * |
| 2170 | * @return int A random positive integer |
| 2171 | */ |
| 2172 | function wp_random_positive_int() { |
| 2173 | $buf = wp_random_bytes(PHP_INT_SIZE); |
| 2174 | if( $buf === false ) { |
| 2175 | trigger_error('Random number failure', E_USER_ERROR); |
| 2176 | } |
| 2177 | |
| 2178 | $val = 0; |
| 2179 | $i = wp_binsafe_strlen($buf); |
| 2180 | |
| 2181 | do { |
| 2182 | $i--; |
| 2183 | $val <<= 8; |
| 2184 | $val |= ord($buf[$i]); |
| 2185 | } while ( $i !== 0 ); |
| 2186 | |
| 2187 | return $val & PHP_INT_MAX; |
| 2188 | } |
| 2189 | endif; |
| 2190 | |
| 2191 | if ( !function_exists('wp_secure_rand') ): |
| 2192 | /** |
| 2193 | * Generates an unbiased, unpredictably random number |
| 2194 | * |
| 2195 | * @since x.x.x |
| 2196 | * |
| 2197 | * @param int $min Lower limit for the generated number |
| 2198 | * @param int $max Upper limit for the generated number |
| 2199 | * @return int|bool A random number between min and max, False on failure |
| 2200 | */ |
| 2201 | function wp_secure_rand($min = 0, $max = 0) { |
| 2202 | if ( !is_int($min) || !is_int($max) ) { |
| 2203 | return (int) $min; |
| 2204 | } |
| 2205 | $range = (int) $max - $min; |
| 2206 | if ($range < 2) { |
| 2207 | return $min; |
| 2208 | } |
| 2209 | |
| 2210 | /** |
| 2211 | * At this stage, we are calculating a bit mask to use |
| 2212 | * to reduce the number of calls to wp_random_positive_int() |
| 2213 | * |
| 2214 | * We calculate the minimum number of bits to store $range |
| 2215 | * ex. 9000 would need to be stored in 14 bits because it is larger than 2^13 but smaller than 2^14 |
| 2216 | * |
| 2217 | * Then we take that even power of 2 and subtract 1 so we get a number that, |
| 2218 | * when represented in binary, is all 1s. |
| 2219 | * |
| 2220 | * Later, when we apply our AND operation with the mask, all bits above 2^14 |
| 2221 | * would become 0 and the rest remain unchanged. |
| 2222 | */ |
| 2223 | $bits = ceil(log($range)/log(2)); |
| 2224 | // ex. 9000 -> 14 |
| 2225 | $mask = ceil(pow(2, $bits)) - 1; |
| 2226 | // ex. 2^14 - 1 == 16383 or 00111111 11111111 |
| 2227 | |
| 2228 | /** |
| 2229 | * 9000 in binary is 00100011 00101000 |
| 2230 | * 16383 in binary is 00111111 11111111 |
| 2231 | * 9000 & 16383 == 00100011 00101000 |
| 2232 | * |
| 2233 | * If this test fails, there is something seriously wrong :( |
| 2234 | */ |
| 2235 | if ( ($range & $mask) !== $range ) { |
| 2236 | // Runtime test failed |
| 2237 | return FALSE; |
| 2238 | } |
| 2239 | |
| 2240 | do { |
| 2241 | // Grab a random integer |
| 2242 | $val = wp_random_positive_int(); |
| 2243 | if ($val === FALSE) { |
| 2244 | // RNG failure |
| 2245 | return FALSE; |
| 2246 | } |
| 2247 | // Apply mask |
| 2248 | $val = $val & $mask; |
| 2249 | // If $val is larger than the maximum acceptable number for |
| 2250 | // $min and $max, we discard and try again. |
| 2251 | } while ($val > $range); |
| 2252 | |
| 2253 | // We now have an unbiased value between 0 and ($max - $min), so let's add it to $min |
| 2254 | return (int) ($min + $val); |
| 2255 | } |
| 2256 | |
| 2257 | endif; |