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 Wikimedia\suppressWarnings();
98 $stat = stat( $file );
99 Wikimedia\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 && function_exists(
'mcrypt_create_iv' ) ) {
269 $rem = $bytes - strlen(
$buffer );
270 $iv = mcrypt_create_iv( $rem, MCRYPT_DEV_URANDOM );
271 if ( $iv ===
false ) {
272 $this->logger->debug(
"mcrypt_create_iv returned false." );
275 $this->logger->debug(
"mcrypt_create_iv generated " . strlen( $iv ) .
276 " bytes of randomness." );
280 if ( strlen(
$buffer ) < $bytes && function_exists(
'openssl_random_pseudo_bytes' ) ) {
281 $rem = $bytes - strlen(
$buffer );
282 $openssl_strong =
false;
283 $openssl_bytes = openssl_random_pseudo_bytes( $rem, $openssl_strong );
284 if ( $openssl_bytes ===
false ) {
285 $this->logger->debug(
"openssl_random_pseudo_bytes returned false." );
288 $this->logger->debug(
"openssl_random_pseudo_bytes generated " .
289 strlen( $openssl_bytes ) .
" bytes of " .
290 ( $openssl_strong ?
"strong" :
"weak" ) .
" randomness." );
292 if ( strlen(
$buffer ) >= $bytes ) {
295 $this->strong = !!$openssl_strong;
300 if ( strlen(
$buffer ) < $bytes &&
301 ( function_exists(
'stream_set_read_buffer' ) || $forceStrong )
303 $rem = $bytes - strlen(
$buffer );
304 if ( !function_exists(
'stream_set_read_buffer' ) && $forceStrong ) {
305 $this->logger->debug(
"Was forced to read from /dev/urandom " .
306 "without control over the buffer size." );
310 Wikimedia\suppressWarnings();
311 $urandom = fopen(
"/dev/urandom",
"rb" );
312 Wikimedia\restoreWarnings();
322 $chunk_size = 1024 * 8;
323 if ( function_exists(
'stream_set_read_buffer' ) ) {
325 stream_set_read_buffer( $urandom, $rem );
328 $random_bytes = fread( $urandom, max( $chunk_size, $rem ) );
331 $this->logger->debug(
"/dev/urandom generated " . strlen( $random_bytes ) .
332 " bytes of randomness." );
334 if ( strlen(
$buffer ) >= $bytes ) {
336 $this->strong =
true;
339 $this->logger->debug(
"/dev/urandom could not be opened." );
349 if ( strlen(
$buffer ) < $bytes ) {
350 $this->logger->debug( __METHOD__ .
351 ": Falling back to using a pseudo random state to generate randomness." );
353 while ( strlen(
$buffer ) < $bytes ) {
357 $this->strong =
false;
363 $generated = substr(
$buffer, 0, $bytes );
366 $this->logger->debug( strlen(
$buffer ) .
367 " bytes of randomness leftover in the buffer." );
388 $bytes = ceil( $chars / 2 );
390 $hex = bin2hex( $this->
generate( $bytes, $forceStrong ) );
399 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 ...