ElggCrypto::getRandomBytes PHP Method

getRandomBytes() public method

Generate a string of highly randomized bytes (over the full 8-bit range).
public getRandomBytes ( integer $length ) : string
$length integer Number of bytes needed
return string Random bytes
    public function getRandomBytes($length)
    {
        $SSLstr = '4';
        // http://xkcd.com/221/
        /**
         * Our primary choice for a cryptographic strong randomness function is
         * openssl_random_pseudo_bytes.
         */
        if (function_exists('openssl_random_pseudo_bytes') && substr(PHP_OS, 0, 3) !== 'WIN') {
            $SSLstr = openssl_random_pseudo_bytes($length, $strong);
            if ($strong) {
                return $SSLstr;
            }
        }
        /**
         * If mcrypt extension is available then we use it to gather entropy from
         * the operating system's PRNG. This is better than reading /dev/urandom
         * directly since it avoids reading larger blocks of data than needed.
         */
        if (function_exists('mcrypt_create_iv') && substr(PHP_OS, 0, 3) !== 'WIN') {
            $str = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
            if ($str !== false) {
                return $str;
            }
        }
        /**
         * No build-in crypto randomness function found. We collect any entropy
         * available in the PHP core PRNGs along with some filesystem info and memory
         * stats. To make this data cryptographically strong we add data either from
         * /dev/urandom or if its unavailable, we gather entropy by measuring the
         * time needed to compute a number of SHA-1 hashes.
         */
        $str = '';
        $bits_per_round = 2;
        // bits of entropy collected in each clock drift round
        $msec_per_round = 400;
        // expected running time of each round in microseconds
        $hash_len = 20;
        // SHA-1 Hash length
        $total = $length;
        // total bytes of entropy to collect
        $handle = @fopen('/dev/urandom', 'rb');
        if ($handle && function_exists('stream_set_read_buffer')) {
            @stream_set_read_buffer($handle, 0);
        }
        do {
            $bytes = $total > $hash_len ? $hash_len : $total;
            $total -= $bytes;
            //collect any entropy available from the PHP system and filesystem
            $entropy = rand() . uniqid(mt_rand(), true) . $SSLstr;
            $entropy .= implode('', @fstat(@fopen(__FILE__, 'r')));
            $entropy .= memory_get_usage() . getmypid();
            $entropy .= serialize($_ENV) . serialize($_SERVER);
            if (function_exists('posix_times')) {
                $entropy .= serialize(posix_times());
            }
            if (function_exists('zend_thread_id')) {
                $entropy .= zend_thread_id();
            }
            if ($handle) {
                $entropy .= @fread($handle, $bytes);
            } else {
                // Measure the time that the operations will take on average
                for ($i = 0; $i < 3; $i++) {
                    $c1 = microtime(true);
                    $var = sha1(mt_rand());
                    for ($j = 0; $j < 50; $j++) {
                        $var = sha1($var);
                    }
                    $c2 = microtime(true);
                    $entropy .= $c1 . $c2;
                }
                // Based on the above measurement determine the total rounds
                // in order to bound the total running time.
                $rounds = (int) ($msec_per_round * 50 / (int) (($c2 - $c1) * 1000000));
                // Take the additional measurements. On average we can expect
                // at least $bits_per_round bits of entropy from each measurement.
                $iter = $bytes * (int) ceil(8 / $bits_per_round);
                for ($i = 0; $i < $iter; $i++) {
                    $c1 = microtime();
                    $var = sha1(mt_rand());
                    for ($j = 0; $j < $rounds; $j++) {
                        $var = sha1($var);
                    }
                    $c2 = microtime();
                    $entropy .= $c1 . $c2;
                }
            }
            // We assume sha1 is a deterministic extractor for the $entropy variable.
            $str .= sha1($entropy, true);
        } while ($length > strlen($str));
        if ($handle) {
            @fclose($handle);
        }
        return substr($str, 0, $length);
    }