26use Psr\Log\LoggerInterface;
53 protected $randomFuncs = [];
60 protected $randomFiles = [];
67 public function __construct( array $randomFuncs, array $randomFiles, LoggerInterface $logger ) {
68 $this->randomFuncs = $randomFuncs;
69 $this->randomFiles = $randomFiles;
70 $this->logger = $logger;
84 $state .= rand() . uniqid( mt_rand(),
true );
87 $files = $this->randomFiles;
94 $files[] = dirname( __DIR__ );
96 foreach ( $files as $file ) {
97 MediaWiki\suppressWarnings();
98 $stat = stat( $file );
99 MediaWiki\restoreWarnings();
102 foreach ( $stat as $k => $v ) {
103 if ( is_numeric( $k ) ) {
108 $path = realpath( $file );
109 if ( $path !==
false ) {
114 $state .= implode(
'', $stat );
124 if ( function_exists(
'getmypid' ) ) {
125 $state .= getmypid();
130 if ( function_exists(
'memory_get_usage' ) ) {
131 $state .= memory_get_usage(
true );
134 foreach ( $this->randomFuncs as $randomFunc ) {
135 $state .= call_user_func( $randomFunc );
151 $minIterations = self::MIN_ITERATIONS;
155 $bufLength = 10000000;
156 $buffer = str_repeat(
' ', $bufLength );
161 $startTime = microtime(
true );
162 $currentTime = $startTime;
163 while ( $iterations < $minIterations || $currentTime - $startTime < $duration ) {
166 $bufPos = ( $bufPos + 13 ) % $bufLength;
169 $nextTime = microtime(
true );
170 $delta = (int)( ( $nextTime - $currentTime ) * 1000000 );
173 if ( $iterations % 100 === 0 ) {
174 $data = sha1( $data );
176 $currentTime = $nextTime;
179 $timeTaken = $currentTime - $startTime;
182 $this->logger->debug(
"Clock drift calculation " .
183 "(time-taken=" . ( $timeTaken * 1000 ) .
"ms, " .
184 "iterations=$iterations, " .
185 "time-per-iteration=" . ( $timeTaken / $iterations * 1e6 ) .
"us)" );
195 static $state =
null;
196 if ( is_null( $state ) ) {
217 if ( is_null( $this->strong ) ) {
218 throw new RuntimeException( __METHOD__ .
' called before generation of random data' );
221 return $this->strong;
236 public function generate( $bytes, $forceStrong =
false ) {
237 $bytes = floor( $bytes );
239 if ( is_null( $this->strong ) ) {
241 $this->strong =
true;
244 if ( strlen(
$buffer ) < $bytes ) {
251 if ( PHP_VERSION_ID >= 70000
252 || ( defined(
'HHVM_VERSION_ID' ) && HHVM_VERSION_ID >= 31101 )
254 $rem = $bytes - strlen(
$buffer );
255 $buffer .= random_bytes( $rem );
257 if ( strlen(
$buffer ) >= $bytes ) {
258 $this->strong =
true;
262 if ( strlen(
$buffer ) < $bytes ) {
269 if ( function_exists(
'mcrypt_create_iv' ) ) {
270 $rem = $bytes - strlen(
$buffer );
271 $iv = mcrypt_create_iv( $rem, MCRYPT_DEV_URANDOM );
272 if ( $iv ===
false ) {
273 $this->logger->debug(
"mcrypt_create_iv returned false." );
276 $this->logger->debug(
"mcrypt_create_iv generated " . strlen( $iv ) .
277 " bytes of randomness." );
282 if ( strlen(
$buffer ) < $bytes ) {
283 if ( function_exists(
'openssl_random_pseudo_bytes' ) ) {
284 $rem = $bytes - strlen(
$buffer );
285 $openssl_bytes = openssl_random_pseudo_bytes( $rem, $openssl_strong );
286 if ( $openssl_bytes ===
false ) {
287 $this->logger->debug(
"openssl_random_pseudo_bytes returned false." );
290 $this->logger->debug(
"openssl_random_pseudo_bytes generated " .
291 strlen( $openssl_bytes ) .
" bytes of " .
292 ( $openssl_strong ?
"strong" :
"weak" ) .
" randomness." );
294 if ( strlen(
$buffer ) >= $bytes ) {
297 $this->strong = !!$openssl_strong;
303 if ( strlen(
$buffer ) < $bytes &&
304 ( function_exists(
'stream_set_read_buffer' ) || $forceStrong )
306 $rem = $bytes - strlen(
$buffer );
307 if ( !function_exists(
'stream_set_read_buffer' ) && $forceStrong ) {
308 $this->logger->debug(
"Was forced to read from /dev/urandom " .
309 "without control over the buffer size." );
313 MediaWiki\suppressWarnings();
314 $urandom = fopen(
"/dev/urandom",
"rb" );
315 MediaWiki\restoreWarnings();
325 $chunk_size = 1024 * 8;
326 if ( function_exists(
'stream_set_read_buffer' ) ) {
328 stream_set_read_buffer( $urandom, $rem );
331 $random_bytes = fread( $urandom, max( $chunk_size, $rem ) );
334 $this->logger->debug(
"/dev/urandom generated " . strlen( $random_bytes ) .
335 " bytes of randomness." );
337 if ( strlen(
$buffer ) >= $bytes ) {
339 $this->strong =
true;
342 $this->logger->debug(
"/dev/urandom could not be opened." );
352 if ( strlen(
$buffer ) < $bytes ) {
353 $this->logger->debug( __METHOD__ .
354 ": Falling back to using a pseudo random state to generate randomness." );
356 while ( strlen(
$buffer ) < $bytes ) {
360 $this->strong =
false;
366 $generated = substr(
$buffer, 0, $bytes );
369 $this->logger->debug( strlen(
$buffer ) .
370 " bytes of randomness leftover in the buffer." );
391 $bytes = ceil( $chars / 2 );
393 $hex = bin2hex( $this->
generate( $bytes, $forceStrong ) );
402 return substr( $hex, 0, $chars );
wasStrong()
Return a boolean indicating whether or not the source used for cryptographic random bytes generation ...
driftHash( $data)
Randomly hash data while mixing in clock drift data for randomness.
string[] $randomFiles
List of files to generate some random state from.
generateHex( $chars, $forceStrong=false)
Generate a run of (ideally) cryptographically random data and return it in hexadecimal string format.
__construct(array $randomFuncs, array $randomFiles, LoggerInterface $logger)
randomState()
Return a rolling random state initially build using data from unstable sources.
const MSEC_PER_BYTE
Number of milliseconds we want to spend generating each separate byte of the final generated bytes.
$strong
A boolean indicating whether the previous random generation was done using cryptographically strong r...
generate( $bytes, $forceStrong=false)
Generate a run of (ideally) cryptographically random data and return it in raw binary form.
callable[] $randomFuncs
List of functions to call to generate some random state.
initialRandomState()
Initialize an initial random state based off of whatever we can find.
const MIN_ITERATIONS
Minimum number of iterations we want to make in our drift calculations.
static hashLength( $raw=true)
Return the byte-length output of the hash algorithm we are using in self::hash and self::hmac.
static hmac( $data, $key, $raw=true)
Generate an acceptably unstable one-way-hmac of some text making use of the best hash algorithm that ...
static hash( $data, $raw=true)
Generate an acceptably unstable one-way-hash of some text making use of the best hash algorithm that ...