From 3040055fc5976dcd58a05e47b5184337ffd635e5 Mon Sep 17 00:00:00 2001 From: Daniel Friesen Date: Mon, 20 Feb 2012 21:22:52 +0000 Subject: [PATCH] Commit a new cryptographic random generator class for use in MediaWiki. Waiting for it to be reviewed before actually making use of it inside code and adding a RELEASE-NOTES entry. --- includes/AutoLoader.php | 1 + includes/CryptRand.php | 252 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 includes/CryptRand.php diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 4540c7958f..74165806a6 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -49,6 +49,7 @@ $wgAutoloadLocalClasses = array( 'ConfEditorToken' => 'includes/ConfEditor.php', 'Cookie' => 'includes/Cookie.php', 'CookieJar' => 'includes/Cookie.php', + 'MWCryptRand' => 'includes/CryptRand.php', 'CurlHttpRequest' => 'includes/HttpFunctions.php', // 'DBDataObject' => 'includes/DBDataObject.php', // 'DBTable' => 'includes/DBTable.php', diff --git a/includes/CryptRand.php b/includes/CryptRand.php new file mode 100644 index 0000000000..86332129df --- /dev/null +++ b/includes/CryptRand.php @@ -0,0 +1,252 @@ += $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 ); + } + +} -- 2.20.1