/**
* Returns a string of highly randomized bytes (over the full 8-bit range).
*
- * @copyright Drupal CMS
- * @license GNU General Public License version 2
- * @param integer $count Number of characters (bytes) to return
+ * Note: Returned values are not guaranteed to be crypto-safe,
+ * most likely they are not, depending on the used retrieval method.
+ *
+ * @param integer $bytesToReturn Number of characters (bytes) to return
* @return string Random Bytes
- */
- public static function generateRandomBytes($count) {
- $output = '';
- // /dev/urandom is available on many *nix systems and is considered
- // the best commonly available pseudo-random source.
- if (TYPO3_OS != 'WIN' && ($fh = @fopen('/dev/urandom', 'rb'))) {
- $output = fread($fh, $count);
- fclose($fh);
- } elseif (TYPO3_OS == 'WIN') {
- if (class_exists('COM')) {
- try {
- $com = new COM('CAPICOM.Utilities.1');
- $output = base64_decode($com->GetRandom($count, 0));
- } catch (Exception $e) {
- // CAPICOM not installed
+ * @see http://bugs.php.net/bug.php?id=52523
+ * @see http://www.php-security.org/2010/05/09/mops-submission-04-generating-unpredictable-session-ids-and-hashes/index.html
+ */
+ public static function generateRandomBytes($bytesToReturn) {
+ // Cache 4k of the generated bytestream.
+ static $bytes = '';
+ $bytesToGenerate = max(4096, $bytesToReturn);
+
+ // if we have not enough random bytes cached, we generate new ones
+ if (!isset($bytes{$bytesToReturn - 1})) {
+ if (TYPO3_OS === 'WIN') {
+ // Openssl seems to be deadly slow on Windows, so try to use mcrypt
+ // Windows PHP versions have a bug when using urandom source (see #24410)
+ $bytes .= self::generateRandomBytesMcrypt($bytesToGenerate, MCRYPT_RAND);
+ } else {
+ // Try to use native PHP functions first, precedence has openssl
+ $bytes .= self::generateRandomBytesOpenSsl($bytesToGenerate);
+
+ if (!isset($bytes{$bytesToReturn - 1})) {
+ $bytes .= self::generateRandomBytesMcrypt($bytesToGenerate, MCRYPT_DEV_URANDOM);
}
- }
- if ($output === '') {
- if (function_exists('mcrypt_create_iv')) {
- $output = mcrypt_create_iv($count, MCRYPT_DEV_URANDOM);
- } elseif (function_exists('openssl_random_pseudo_bytes')) {
- $isStrong = NULL;
- $output = openssl_random_pseudo_bytes($count, $isStrong);
- // skip ssl since it wasn't using the strong algo
- if ($isStrong !== TRUE) {
- $output = '';
- }
+
+ // If openssl and mcrypt failed, try /dev/urandom
+ if (!isset($bytes{$bytesToReturn - 1})) {
+ $bytes .= self::generateRandomBytesUrandom($bytesToGenerate);
}
}
- }
- // fallback if other random byte generation failed until now
- if (!isset($output{$count - 1})) {
- // We initialize with the somewhat random.
- $randomState = $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']
- . base_convert(memory_get_usage() % pow(10, 6), 10, 2)
- . microtime() . uniqid('') . getmypid();
- while (!isset($output{$count - 1})) {
- $randomState = sha1(microtime() . mt_rand() . $randomState);
- $output .= sha1(mt_rand() . $randomState, TRUE);
+ // Fall back if other random byte generation failed until now
+ if (!isset($bytes{$bytesToReturn - 1})) {
+ $bytes .= self::generateRandomBytesFallback($bytesToReturn);
}
- $output = substr($output, strlen($output) - $count, $count);
}
+
+ // get first $bytesToReturn and remove it from the byte cache
+ $output = substr($bytes, 0, $bytesToReturn);
+ $bytes = substr($bytes, $bytesToReturn);
+
return $output;
}
+ /**
+ * Generate random bytes using openssl if available
+ *
+ * @param string $bytesToGenerate
+ * @return string
+ */
+ protected static function generateRandomBytesOpenSsl($bytesToGenerate) {
+ if (!function_exists('openssl_random_pseudo_bytes')) {
+ return '';
+ }
+ $isStrong = NULL;
+ return (string) openssl_random_pseudo_bytes($bytesToGenerate, $isStrong);
+ }
+
+ /**
+ * Generate random bytes using mcrypt if available
+ *
+ * @param $bytesToGenerate
+ * @param $randomSource
+ * @return string
+ */
+ protected static function generateRandomBytesMcrypt($bytesToGenerate, $randomSource) {
+ if (!function_exists('mcrypt_create_iv')) {
+ return '';
+ }
+ return (string) @mcrypt_create_iv($bytesToGenerate, $randomSource);
+ }
+
+ /**
+ * Read random bytes from /dev/urandom if it is accessible
+ *
+ * @param $bytesToGenerate
+ * @return string
+ */
+ protected static function generateRandomBytesUrandom($bytesToGenerate) {
+ $bytes = '';
+ $fh = @fopen('/dev/urandom', 'rb');
+ if ($fh) {
+ // PHP only performs buffered reads, so in reality it will always read
+ // at least 4096 bytes. Thus, it costs nothing extra to read and store
+ // that much so as to speed any additional invocations.
+ $bytes = fread($fh, $bytesToGenerate);
+ fclose($fh);
+ }
+
+ return $bytes;
+ }
+
+ /**
+ * Generate pseudo random bytes as last resort
+ *
+ * @param $bytesToReturn
+ * @return string
+ */
+ protected static function generateRandomBytesFallback($bytesToReturn) {
+ $bytes = '';
+ // We initialize with somewhat random.
+ $randomState = $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] . base_convert(memory_get_usage() % pow(10, 6), 10, 2) . microtime() . uniqid('') . getmypid();
+ while (!isset($bytes{$bytesToReturn - 1})) {
+ $randomState = sha1(microtime() . mt_rand() . $randomState);
+ $bytes .= sha1(mt_rand() . $randomState, TRUE);
+ }
+ return $bytes;
+ }
+
/**
* Returns a hex representation of a random byte string.
*
}
}
-?>
\ No newline at end of file
+?>