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