= $bytes ) { // urandom is always strong, set to true if all our data was generated using it self::$strong = true; } } } if ( strlen( $buffer ) < $bytes ) { // If available and we failed to read enough data out of urandom make use // of openssl's random_pesudo_bytes method to attempt to generate randomness. // However don't do this on Windows with PHP < 5.3.4 due to a bug: // http://stackoverflow.com/questions/1940168/openssl-random-pseudo-bytes-is-slow-php if ( ( $bytes - strlen( $buffer ) > 0 ) && function_exists( 'openssl_random_pseudo_bytes' ) && ( !wfIsWindows() || version_compare( PHP_VERSION, '5.3.4', '>=' ) ) ) { $buffer .= openssl_random_pseudo_bytes( $bytes - strlen( $buffer ), $openssl_strong ); if ( strlen( $buffer ) >= $bytes ) { // openssl tells us if the random source was strong, if some of our data was generated // using it use it's say on whether the randomness is strong self::$strong = !!$openssl_strong; } } } // If we cannot use or generate enough data from /dev/urandom or openssl // use this loop to generate a good set of pesudo random data. // This works by initializing a random state using a pile of unstable data // and continually shoving it through a hash along with a variable salt. // We hash the random state with more salt to avoid the state from leaking // out and being used to predict the /randomness/ that follows. while ( strlen( $buffer ) < $bytes ) { $buffer .= self::hmac( self::randomState(), mt_rand() ); // This code is never really cryptographically strong, if we use it // at all, then set strong to false. self::$strong = false; } // Once the buffer has been filled up with enough random data to fulfill // the request shift off enough data to handle the request and leave the // unused portion left inside the buffer for the next request for random data $generated = substr( $buffer, 0, $bytes ); $buffer = substr( $buffer, $bytes ); return $generated; } /** * Generate a run of (ideally) cryptographically random data and return * it in hexadecimal string format. * You can use MWCryptRand::wasStrong() if you wish to know if the source used * was cryptographically strong. * * @param $chars int the number of hex chars of random data to generate * @return String Hexadecimal random data */ public static function generateHex( $chars ) { // hex strings are 2x the length of raw binary so we divide the length in half // odd numbers will result in a .5 that leads the generate() being 1 character // short, so we use ceil() to ensure that we always have enough bytes $bytes = ceil( $chars / 2 ); // Generate the data and then convert it to a hex string $hex = bin2hex( self::generate( $bytes ) ); // A bit of paranoia here, the caller asked for a specific length of string // here, and it's possible (eg when given an odd number) that we may actually // have at least 1 char more than they asked for. Just in case they made this // call intending to insert it into a database that does truncation we don't // want to give them too much and end up with their database and their live // code having two different values because part of what we gave them is truncated // hence, we strip out any run of characters longer than what we were asked for. return substr( $hex, 0, $chars ); } }