Exemplo n.º 1
0
/**
 * If the libsodium PHP extension is loaded, we'll use it above any other
 * solution.
 *
 * libsodium-php project:
 * @ref https://github.com/jedisct1/libsodium-php
 *
 * @param int $bytes
 *
 * @throws Exception
 *
 * @return string
 */
function random_bytes($bytes)
{
    try {
        $bytes = RandomCompat_intval($bytes);
    } catch (TypeError $ex) {
        throw new TypeError('random_bytes(): $bytes must be an integer');
    }
    if ($bytes < 1) {
        throw new Error('Length must be greater than 0');
    }
    /**
     * \Sodium\randombytes_buf() doesn't allow more than 2147483647 bytes to be
     * generated in one invocation.
     */
    if ($bytes > 2147483647) {
        $buf = '';
        for ($i = 0; $i < $bytes; $i += 1073741824) {
            $n = $bytes - $i > 1073741824 ? 1073741824 : $bytes - $i;
            $buf .= \Sodium\randombytes_buf($n);
        }
    } else {
        $buf = \Sodium\randombytes_buf($bytes);
    }
    if ($buf !== false) {
        if (RandomCompat_strlen($buf) === $bytes) {
            return $buf;
        }
    }
    /**
     * If we reach here, PHP has failed us.
     */
    throw new Exception('Could not gather sufficient random data');
}
Exemplo n.º 2
0
 public function testStrlen()
 {
     if (!function_exists('RandomCompat_strlen')) {
         return $this->markTestSkipped('We don\' need to test this in PHP 7.');
     }
     $this->assertEquals(RandomCompat_strlen("𝒳"), 4);
 }
Exemplo n.º 3
0
/**
 * Windows with PHP < 5.3.0 will not have the function
 * openssl_random_pseudo_bytes() available, so let's use
 * CAPICOM to work around this deficiency.
 * 
 * @param int $bytes
 * 
 * @throws Exception
 * 
 * @return string
 */
function random_bytes($bytes)
{
    if (!is_int($bytes)) {
        throw new TypeError('Length must be an integer');
    }
    if ($bytes < 1) {
        throw new Error('Length must be greater than 0');
    }
    $buf = '';
    $util = new COM('CAPICOM.Utilities.1');
    $execCount = 0;
    /**
     * Let's not let it loop forever. If we run N times and fail to
     * get N bytes of random data, then CAPICOM has failed us.
     */
    do {
        $buf .= base64_decode($util->GetRandom($bytes, 0));
        if (RandomCompat_strlen($buf) >= $bytes) {
            /**
             * Return our random entropy buffer here:
             */
            return RandomCompat_substr($buf, 0, $bytes);
        }
        ++$execCount;
    } while ($execCount < $bytes);
    /**
     * If we reach here, PHP has failed us.
     */
    throw new Exception('PHP failed to generate random data.');
}
Exemplo n.º 4
0
/**
 * Since openssl_random_pseudo_bytes() uses openssl's
 * RAND_pseudo_bytes() API, which has been marked as deprecated by the
 * OpenSSL team, this is our last resort before failure.
 *
 * @ref https://www.openssl.org/docs/crypto/RAND_bytes.html
 *
 * @param int $bytes
 *
 * @throws Exception
 *
 * @return string
 */
function random_bytes($bytes)
{
    try {
        $bytes = RandomCompat_intval($bytes);
    } catch (TypeError $ex) {
        throw new TypeError('random_bytes(): $bytes must be an integer');
    }
    if ($bytes < 1) {
        throw new Error('Length must be greater than 0');
    }
    $secure = true;
    /**
     * $secure is passed by reference. If it's set to false, fail. Note
     * that this will only return false if this function fails to return
     * any data.
     *
     * @ref https://github.com/paragonie/random_compat/issues/6#issuecomment-119564973
     */
    $buf = openssl_random_pseudo_bytes($bytes, $secure);
    if ($buf !== false && $secure) {
        if (RandomCompat_strlen($buf) === $bytes) {
            return $buf;
        }
    }
    /**
     * If we reach here, PHP has failed us.
     */
    throw new Exception('Could not gather sufficient random data');
}
Exemplo n.º 5
0
/**
 * Powered by ext/mcrypt (and thankfully NOT libmcrypt)
 * 
 * @ref https://bugs.php.net/bug.php?id=55169
 * @ref https://github.com/php/php-src/blob/c568ffe5171d942161fc8dda066bce844bdef676/ext/mcrypt/mcrypt.c#L1321-L1386
 * 
 * @param int $bytes
 * 
 * @throws Exception
 * 
 * @return string
 */
function random_bytes($bytes)
{
    if (!is_int($bytes)) {
        throw new TypeError('Length must be an integer');
    }
    if ($bytes < 1) {
        throw new Error('Length must be greater than 0');
    }
    $buf = mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM);
    if ($buf !== false) {
        if (RandomCompat_strlen($buf) === $bytes) {
            /**
             * Return our random entropy buffer here:
             */
            return $buf;
        }
    }
    /**
     * If we reach here, PHP has failed us.
     */
    throw new Exception('PHP failed to generate random data.');
}
 /**
  * Powered by ext/mcrypt (and thankfully NOT libmcrypt)
  *
  * @ref https://bugs.php.net/bug.php?id=55169
  * @ref https://github.com/php/php-src/blob/c568ffe5171d942161fc8dda066bce844bdef676/ext/mcrypt/mcrypt.c#L1321-L1386
  *
  * @param int $bytes
  *
  * @throws Exception
  *
  * @return string
  */
 function random_bytes($bytes)
 {
     try {
         $bytes = RandomCompat_intval($bytes);
     } catch (TypeError $ex) {
         throw new TypeError('random_bytes(): $bytes must be an integer');
     }
     if ($bytes < 1) {
         throw new Error('Length must be greater than 0');
     }
     $buf = @mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM);
     if ($buf !== false && RandomCompat_strlen($buf) === $bytes) {
         /**
          * Return our random entropy buffer here:
          */
         return $buf;
     }
     /**
      * If we reach here, PHP has failed us.
      */
     throw new Exception('Could not gather sufficient random data');
 }
Exemplo n.º 7
0
 /**
  * Since openssl_random_pseudo_bytes() uses openssl's 
  * RAND_pseudo_bytes() API, which has been marked as deprecated by the
  * OpenSSL team, this is our last resort before failure.
  * 
  * @ref https://www.openssl.org/docs/crypto/RAND_bytes.html
  * 
  * @param int $bytes
  * @return string
  */
 function random_bytes($bytes)
 {
     $secure = true;
     $buf = openssl_random_pseudo_bytes($bytes, $secure);
     if ($buf !== false && $secure) {
         if (RandomCompat_strlen($buf) === $bytes) {
             return $buf;
         }
     }
 }
/**
 * Unless open_basedir is enabled, use /dev/urandom for
 * random numbers in accordance with best practices
 * 
 * Why we use /dev/urandom and not /dev/random
 * @ref http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers
 * 
 * @param int $bytes
 * 
 * @throws Exception
 * 
 * @return string
 */
function random_bytes($bytes)
{
    static $fp = null;
    /**
     * This block should only be run once
     */
    if (empty($fp)) {
        /**
         * We use /dev/urandom if it is a char device.
         * We never fall back to /dev/random
         */
        $fp = fopen('/dev/urandom', 'rb');
        if (!empty($fp)) {
            $st = fstat($fp);
            if (($st['mode'] & 0170000) !== 020000) {
                fclose($fp);
                $fp = false;
            }
        }
        if (!empty($fp)) {
            /**
             * stream_set_read_buffer() does not exist in HHVM
             * 
             * If we don't set the stream's read buffer to 0, PHP will
             * internally buffer 8192 bytes, which can waste entropy
             * 
             * stream_set_read_buffer returns 0 on success
             */
            if (function_exists('stream_set_read_buffer')) {
                stream_set_read_buffer($fp, RANDOM_COMPAT_READ_BUFFER);
            }
            if (function_exists('stream_set_chunk_size')) {
                stream_set_chunk_size($fp, RANDOM_COMPAT_READ_BUFFER);
            }
        }
    }
    try {
        $bytes = RandomCompat_intval($bytes);
    } catch (TypeError $ex) {
        throw new TypeError('random_bytes(): $bytes must be an integer');
    }
    if ($bytes < 1) {
        throw new Error('Length must be greater than 0');
    }
    /**
     * This if() block only runs if we managed to open a file handle
     * 
     * It does not belong in an else {} block, because the above 
     * if (empty($fp)) line is logic that should only be run once per
     * page load.
     */
    if (!empty($fp)) {
        $remaining = $bytes;
        $buf = '';
        /**
         * We use fread() in a loop to protect against partial reads
         */
        do {
            $read = fread($fp, $remaining);
            if ($read === false) {
                /**
                 * We cannot safely read from the file. Exit the
                 * do-while loop and trigger the exception condition
                 */
                $buf = false;
                break;
            }
            /**
             * Decrease the number of bytes returned from remaining
             */
            $remaining -= RandomCompat_strlen($read);
            $buf .= $read;
        } while ($remaining > 0);
        /**
         * Is our result valid?
         */
        if ($buf !== false) {
            if (RandomCompat_strlen($buf) === $bytes) {
                /**
                 * Return our random entropy buffer here:
                 */
                return $buf;
            }
        }
    }
    /**
     * If we reach here, PHP has failed us.
     */
    throw new Exception('Error reading from source device');
}
 /**
  * substr() implementation that isn't brittle to mbstring.func_overload
  *
  * This version uses mb_substr() in '8bit' mode to treat strings as raw
  * binary rather than UTF-8, ISO-8859-1, etc
  *
  * @param string $binary_string
  * @param int $start
  * @param int $length (optional)
  *
  * @throws TypeError
  *
  * @return string
  */
 function RandomCompat_substr($binary_string, $start, $length = null)
 {
     if (!is_string($binary_string)) {
         throw new TypeError('RandomCompat_substr(): First argument should be a string');
     }
     if (!is_int($start)) {
         throw new TypeError('RandomCompat_substr(): Second argument should be an integer');
     }
     if ($length === null) {
         /**
          * mb_substr($str, 0, NULL, '8bit') returns an empty string on
          * PHP 5.3, so we have to find the length ourselves.
          */
         $length = RandomCompat_strlen($length) - $start;
     } elseif (!is_int($length)) {
         throw new TypeError('RandomCompat_substr(): Third argument should be an integer, or omitted');
     }
     // Consistency with PHP's behavior
     if ($start === RandomCompat_strlen($binary_string) && $length === 0) {
         return '';
     }
     if ($start > RandomCompat_strlen($binary_string)) {
         return false;
     }
     return mb_substr($binary_string, $start, $length, '8bit');
 }
Exemplo n.º 10
0
 /**
  * substr() implementation that isn't brittle to mbstring.func_overload
  * 
  * This version uses mb_substr() in '8bit' mode to treat strings as raw
  * binary rather than UTF-8, ISO-8859-1, etc
  * 
  * @param string $binary_string
  * @param int $start
  * @param int $length (optional)
  * 
  * @throws InvalidArgumentException
  * 
  * @return string
  */
 function RandomCompat_substr($binary_string, $start, $length = null)
 {
     if (!is_string($binary_string)) {
         throw new InvalidArgumentException('RandomCompat_substr(): First argument should be a string');
     }
     if (!is_int($start)) {
         throw new InvalidArgumentException('RandomCompat_substr(): Second argument should be an integer');
     }
     if ($length === null) {
         /**
          * mb_substr($str, 0, NULL, '8bit') returns an empty string on
          * PHP 5.3, so we have to find the length ourselves.
          */
         $length = RandomCompat_strlen($length) - $start;
     } elseif (!is_int($length)) {
         throw new InvalidArgumentException('RandomCompat_substr(): Third argument should be an integer, or omitted');
     }
     return mb_substr($binary_string, $start, $length, '8bit');
 }