Issue #2550519 by klausi, nullkernel, cweagans, alexpott, pwolanin, neclimdul: Crypt::randomBytes() doesn't actually return cryptographically secure random bytes
parent
87986e3151
commit
1aa1fbe2ae
|
@ -912,6 +912,54 @@
|
|||
],
|
||||
"time": "2015-06-07 08:43:18"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/random_compat",
|
||||
"version": "1.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/random_compat.git",
|
||||
"reference": "a208865a5aeffc2dbbef2a5b3409887272d93f32"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/a208865a5aeffc2dbbef2a5b3409887272d93f32",
|
||||
"reference": "a208865a5aeffc2dbbef2a5b3409887272d93f32",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "4.*|5.*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"lib/random.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Paragon Initiative Enterprises",
|
||||
"email": "security@paragonie.com",
|
||||
"homepage": "https://paragonie.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
|
||||
"keywords": [
|
||||
"csprng",
|
||||
"pseudorandom",
|
||||
"random"
|
||||
],
|
||||
"time": "2015-12-01 02:52:15"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "1.0",
|
||||
|
|
|
@ -29,7 +29,8 @@
|
|||
"masterminds/html5": "~2.1",
|
||||
"symfony/psr-http-message-bridge": "v0.2",
|
||||
"zendframework/zend-diactoros": "~1.1",
|
||||
"composer/semver": "~1.0"
|
||||
"composer/semver": "~1.0",
|
||||
"paragonie/random_compat": "~1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"behat/mink": "~1.6",
|
||||
|
|
|
@ -22,6 +22,10 @@ class Crypt {
|
|||
* bytes normally from mt_rand()) and uses the best available pseudo-random
|
||||
* source.
|
||||
*
|
||||
* In PHP 7 and up, this uses the built-in PHP function random_bytes().
|
||||
* In older PHP versions, this uses the random_bytes() function provided by
|
||||
* the random_compat library.
|
||||
*
|
||||
* @param int $count
|
||||
* The number of characters (bytes) to return in the string.
|
||||
*
|
||||
|
@ -29,65 +33,7 @@ class Crypt {
|
|||
* A randomly generated string.
|
||||
*/
|
||||
public static function randomBytes($count) {
|
||||
// $random_state does not use drupal_static as it stores random bytes.
|
||||
static $random_state, $bytes;
|
||||
|
||||
$missing_bytes = $count - strlen($bytes);
|
||||
|
||||
if ($missing_bytes > 0) {
|
||||
// openssl_random_pseudo_bytes() will find entropy in a system-dependent
|
||||
// way.
|
||||
if (function_exists('openssl_random_pseudo_bytes')) {
|
||||
$bytes .= openssl_random_pseudo_bytes($missing_bytes);
|
||||
}
|
||||
|
||||
// If OpenSSL is not available, we can use mcrypt. On Windows, this will
|
||||
// transparently pull from CryptGenRandom. On Unix-based systems, it will
|
||||
// read from /dev/urandom as expected.
|
||||
elseif (function_exists(('mcrypt_create_iv')) && defined('MCRYPT_DEV_URANDOM')) {
|
||||
$bytes .= mcrypt_create_iv($count, MCRYPT_DEV_URANDOM);
|
||||
}
|
||||
|
||||
// Else, read directly from /dev/urandom, which is available on many *nix
|
||||
// systems and is considered cryptographically secure.
|
||||
elseif ($fh = @fopen('/dev/urandom', 'rb')) {
|
||||
// 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, max(4096, $missing_bytes));
|
||||
fclose($fh);
|
||||
}
|
||||
|
||||
// If we couldn't get enough entropy, this simple hash-based PRNG will
|
||||
// generate a good set of pseudo-random bytes on any system.
|
||||
// Note that it may be important that our $random_state is passed
|
||||
// through hash() prior to being rolled into $output, that the two hash()
|
||||
// invocations are different, and that the extra input into the first one -
|
||||
// the microtime() - is prepended rather than appended. This is to avoid
|
||||
// directly leaking $random_state via the $output stream, which could
|
||||
// allow for trivial prediction of further "random" numbers.
|
||||
if (strlen($bytes) < $count) {
|
||||
// Initialize on the first call. The contents of $_SERVER includes a mix
|
||||
// of user-specific and system information that varies a little with
|
||||
// each page.
|
||||
if (!isset($random_state)) {
|
||||
$random_state = print_r($_SERVER, TRUE);
|
||||
if (function_exists('getmypid')) {
|
||||
// Further initialize with the somewhat random PHP process ID.
|
||||
$random_state .= getmypid();
|
||||
}
|
||||
$bytes = '';
|
||||
}
|
||||
|
||||
do {
|
||||
$random_state = hash('sha256', microtime() . mt_rand() . $random_state);
|
||||
$bytes .= hash('sha256', mt_rand() . $random_state, TRUE);
|
||||
} while (strlen($bytes) < $count);
|
||||
}
|
||||
}
|
||||
$output = substr($bytes, 0, $count);
|
||||
$bytes = substr($bytes, $count);
|
||||
return $output;
|
||||
return random_bytes($count);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -178,7 +124,7 @@ class Crypt {
|
|||
/**
|
||||
* Returns a URL-safe, base64 encoded string of highly randomized bytes.
|
||||
*
|
||||
* @param $byte_count
|
||||
* @param $count
|
||||
* The number of random bytes to fetch and base64 encode.
|
||||
*
|
||||
* @return string
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
"homepage": "https://www.drupal.org/project/drupal",
|
||||
"license": "GPL-2.0+",
|
||||
"require": {
|
||||
"php": ">=5.5.9"
|
||||
"php": ">=5.5.9",
|
||||
"paragonie/random_compat": "~1.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
|
|
|
@ -36,6 +36,7 @@ class Composer {
|
|||
'jcalderonzumba/mink-phantomjs-driver' => ['tests'],
|
||||
'masterminds/html5' => ['test'],
|
||||
'mikey179/vfsStream' => ['src/test'],
|
||||
'paragonie/random_compat' => ['tests'],
|
||||
'phpdocumentor/reflection-docblock' => ['tests'],
|
||||
'phpunit/php-code-coverage' => ['tests'],
|
||||
'phpunit/php-timer' => ['tests'],
|
||||
|
|
|
@ -440,11 +440,6 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
|||
// Initialize the container.
|
||||
$this->initializeContainer();
|
||||
|
||||
// Ensure mt_rand() is reseeded to prevent random values from one page load
|
||||
// being exploited to predict random values in subsequent page loads.
|
||||
$seed = unpack("L", Crypt::randomBytes(4));
|
||||
mt_srand($seed[1]);
|
||||
|
||||
$this->booted = TRUE;
|
||||
|
||||
return $this;
|
||||
|
|
|
@ -9,5 +9,6 @@ return array(
|
|||
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
|
||||
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
|
||||
'454414594637b9dd94a19af83f56f1a2' => $baseDir . '/core/lib/Drupal.php',
|
||||
);
|
||||
|
|
|
@ -3857,5 +3857,55 @@
|
|||
"phantomjs",
|
||||
"testing"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "paragonie/random_compat",
|
||||
"version": "1.1.1",
|
||||
"version_normalized": "1.1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/random_compat.git",
|
||||
"reference": "a208865a5aeffc2dbbef2a5b3409887272d93f32"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/a208865a5aeffc2dbbef2a5b3409887272d93f32",
|
||||
"reference": "a208865a5aeffc2dbbef2a5b3409887272d93f32",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "4.*|5.*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
|
||||
},
|
||||
"time": "2015-12-01 02:52:15",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"lib/random.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Paragon Initiative Enterprises",
|
||||
"email": "security@paragonie.com",
|
||||
"homepage": "https://paragonie.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
|
||||
"keywords": [
|
||||
"csprng",
|
||||
"pseudorandom",
|
||||
"random"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
checks:
|
||||
php:
|
||||
code_rating: true
|
||||
duplication: false
|
|
@ -0,0 +1,26 @@
|
|||
language: php
|
||||
php:
|
||||
|
||||
- "7.0"
|
||||
- "5.6"
|
||||
- "5.5"
|
||||
- "5.4"
|
||||
- "5.3"
|
||||
- "hhvm"
|
||||
|
||||
|
||||
sudo: false
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- php: "hhvm"
|
||||
|
||||
install:
|
||||
|
||||
- composer install
|
||||
- composer self-update
|
||||
- composer update
|
||||
- chmod +x ./phpunit.sh
|
||||
|
||||
script: ./phpunit.sh travis
|
|
@ -0,0 +1,40 @@
|
|||
## Errata (Design Decisions)
|
||||
|
||||
### Reasoning Behind the Order of Preferred Random Data Sources
|
||||
|
||||
The order is:
|
||||
|
||||
1. `libsodium if available`
|
||||
2. `fread() /dev/urandom if available`
|
||||
3. `mcrypt_create_iv($bytes, MCRYPT_CREATE_IV)`
|
||||
4. `COM('CAPICOM.Utilities.1')->GetRandom()`
|
||||
5. `openssl_random_pseudo_bytes()`
|
||||
|
||||
If libsodium is available, we get random data from it. This is the preferred
|
||||
method on all OSes, but libsodium is not very widely installed, so other
|
||||
fallbacks are available.
|
||||
|
||||
Next, we read `/dev/urandom` (if it exists). This is the preferred file to read
|
||||
for random data for cryptographic purposes for BSD and Linux.
|
||||
|
||||
Despite [strongly urging people not to use mcrypt in their projects](https://paragonie.com/blog/2015/05/if-you-re-typing-word-mcrypt-into-your-code-you-re-doing-it-wrong),
|
||||
because libmcrypt is abandonware and the API puts too much responsibility on the
|
||||
implementor, we prioritize `mcrypt_create_iv()` with `MCRYPT_DEV_URANDOM` above
|
||||
the remaining implementations.
|
||||
|
||||
The reason is simple: `mcrypt_create_iv()` is part of PHP's `ext/mcrypt` code,
|
||||
and is not part `libmcrypt`. It actually does the right thing:
|
||||
|
||||
* On Unix-based operating systems, it reads from `/dev/urandom`, which is the
|
||||
sane and correct thing to do.
|
||||
* On Windows, it reads from `CryptGenRandom`, which is an exclusively Windows
|
||||
way to get random bytes.
|
||||
|
||||
If we're on Windows and don't have access to `mcrypt`, we use `CAPICOM.Utilities.1`.
|
||||
|
||||
Finally, we use `openssl_random_pseudo_bytes()` **as a last resort**, due to
|
||||
[PHP bug #70014](https://bugs.php.net/bug.php?id=70014). Internally, this
|
||||
function calls `RAND_pseudo_bytes()`, which has been [deprecated](https://github.com/paragonie/random_compat/issues/5)
|
||||
by the OpenSSL team. Furthermore, [it might silently return weak random data](https://github.com/paragonie/random_compat/issues/6#issuecomment-119564973)
|
||||
if it is called before OpenSSL's **userspace** CSPRNG is seeded. Also,
|
||||
[you want the OS CSPRNG, not a userspace CSPRNG](http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/).
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Paragon Initiative Enterprises
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
# random_compat
|
||||
|
||||
[![Build Status](https://travis-ci.org/paragonie/random_compat.svg?branch=master)](https://travis-ci.org/paragonie/random_compat)
|
||||
[![Scrutinizer](https://scrutinizer-ci.com/g/paragonie/random_compat/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/paragonie/random_compat)
|
||||
|
||||
PHP 5.x polyfill for `random_bytes()` and `random_int()` created and maintained
|
||||
by [Paragon Initiative Enterprises](https://paragonie.com).
|
||||
|
||||
Although this library *should* function in earlier versions of PHP, we will only
|
||||
consider issues relevant to [supported PHP versions](https://secure.php.net/supported-versions.php).
|
||||
**If you are using an unsupported version of PHP, please upgrade as soon as possible.**
|
||||
|
||||
## Important
|
||||
|
||||
Although this library has been examined by some security experts in the PHP
|
||||
community, there will always be a chance that we overlooked something. Please
|
||||
ask your favorite trusted hackers to hammer it for implementation errors and
|
||||
bugs before even thinking about deploying it in production.
|
||||
|
||||
**Do not use the master branch, use a [stable release](https://github.com/paragonie/random_compat/releases/latest).**
|
||||
|
||||
For the background of this library, please refer to our blog post on
|
||||
[Generating Random Integers and Strings in PHP](https://paragonie.com/blog/2015/07/how-safely-generate-random-strings-and-integers-in-php).
|
||||
|
||||
### Usability Notice
|
||||
|
||||
If PHP cannot safely generate random data, this library will throw an `Exception`.
|
||||
It will never fall back to insecure random data. If this keeps happening, upgrade
|
||||
to a newer version of PHP immediately.
|
||||
|
||||
## Usage
|
||||
|
||||
This library exposes the [CSPRNG functions added in PHP 7](https://secure.php.net/manual/en/ref.csprng.php)
|
||||
for use in PHP 5 projects. Their behavior should be identical.
|
||||
|
||||
### Generate a string of random bytes
|
||||
|
||||
```php
|
||||
try {
|
||||
$string = random_bytes(32);
|
||||
} catch (TypeError $e) {
|
||||
// Well, it's an integer, so this IS unexpected.
|
||||
die("An unexpected error has occurred");
|
||||
} catch (Error $e) {
|
||||
// This is also unexpected because 32 is a reasonable integer.
|
||||
die("An unexpected error has occurred");
|
||||
} catch (Exception $e) {
|
||||
// If you get this message, the CSPRNG failed hard.
|
||||
die("Could not generate a random string. Is our OS secure?");
|
||||
}
|
||||
|
||||
var_dump(bin2hex($string));
|
||||
// string(64) "5787c41ae124b3b9363b7825104f8bc8cf27c4c3036573e5f0d4a91ad2eeac6f"
|
||||
```
|
||||
|
||||
### Generate a random integer between two given integers (inclusive)
|
||||
|
||||
```php
|
||||
try {
|
||||
$int = random_int(0,255);
|
||||
|
||||
} catch (TypeError $e) {
|
||||
// Well, it's an integer, so this IS unexpected.
|
||||
die("An unexpected error has occurred");
|
||||
} catch (Error $e) {
|
||||
// This is also unexpected because 0 and 255 are both reasonable integers.
|
||||
die("An unexpected error has occurred");
|
||||
} catch (Exception $e) {
|
||||
// If you get this message, the CSPRNG failed hard.
|
||||
die("Could not generate a random string. Is our OS secure?");
|
||||
}
|
||||
|
||||
var_dump($int);
|
||||
// int(47)
|
||||
```
|
||||
|
||||
### Exception handling
|
||||
|
||||
When handling exceptions and errors you must account for differences between
|
||||
PHP 5 and PHP7.
|
||||
|
||||
The differences:
|
||||
|
||||
* Catching `Error` works, so long as it is caught before `Exception`.
|
||||
* Catching `Exception` has different behavior, without previously catching `Error`.
|
||||
* There is *no* portable way to catch all errors/exceptions.
|
||||
|
||||
#### Our recommendation
|
||||
|
||||
**Always** catch `Error` before `Exception`.
|
||||
|
||||
#### Example
|
||||
|
||||
```php
|
||||
try {
|
||||
return random_int(1, $userInput);
|
||||
} catch (TypeError $e) {
|
||||
// This is okay, so long as `Error` is caught before `Exception`.
|
||||
throw new Exception('Please enter a number!');
|
||||
} catch (Error $e) {
|
||||
// This is required, if you do not need to do anything just rethrow.
|
||||
throw $e;
|
||||
} catch (Exception $e) {
|
||||
// This is optional and maybe omitted if you do not want to handle errors
|
||||
// during generation.
|
||||
throw new InternalServerErrorException(
|
||||
'Oops, our server is bust and cannot generate any random data.',
|
||||
500,
|
||||
$e
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Contributors
|
||||
|
||||
This project would not be anywhere near as excellent as it is today if it
|
||||
weren't for the contributions of the following individuals:
|
||||
|
||||
* [@AndrewCarterUK (Andrew Carter)](https://github.com/AndrewCarterUK)
|
||||
* [@asgrim (James Titcumb)](https://github.com/asgrim)
|
||||
* [@CodesInChaos (Christian Winnerlein)](https://github.com/CodesInChaos)
|
||||
* [@chriscct7 (Chris Christoff)](https://github.com/chriscct7)
|
||||
* [@cs278 (Chris Smith)](https://github.com/cs278)
|
||||
* [@cweagans (Cameron Eagans)](https://github.com/cweagans)
|
||||
* [@dd32 (Dion Hulse)](https://github.com/dd32)
|
||||
* [@geggleto (Glenn Eggleton)](https://github.com/geggleto)
|
||||
* [@ircmaxell (Anthony Ferrara)](https://github.com/ircmaxell)
|
||||
* [@jedisct1 (Frank Denis)](https://github.com/jedisct1)
|
||||
* [@juliangut (Julián Gutiérrez)](https://github.com/juliangut)
|
||||
* [@kelunik (Niklas Keller)](https://github.com/kelunik)
|
||||
* [@lt (Leigh)](https://github.com/lt)
|
||||
* [@MasonM (Mason Malone)](https://github.com/MasonM)
|
||||
* [@mmeyer2k (Michael M)](https://github.com/mmeyer2k)
|
||||
* [@narfbg (Andrey Andreev)](https://github.com/narfbg)
|
||||
* [@nicolas-grekas (Nicolas Grekas)](https://github.com/nicolas-grekas)
|
||||
* [@oittaa](https://github.com/oittaa)
|
||||
* [@redragonx (Stephen Chavez)](https://github.com/redragonx)
|
||||
* [@rchouinard (Ryan Chouinard)](https://github.com/rchouinard)
|
||||
* [@SammyK (Sammy Kaye Powers)](https://github.com/SammyK)
|
||||
* [@scottchiefbaker (Scott Baker)](https://github.com/scottchiefbaker)
|
||||
* [@skyosev (Stoyan Kyosev)](https://github.com/skyosev)
|
||||
* [@stof (Christophe Coevoet)](https://github.com/stof)
|
||||
* [@teohhanhui (Teoh Han Hui)](https://github.com/teohhanhui)
|
||||
* [@tom-- (Tom Worster)](https://github.com/tom--)
|
||||
* [@tsyr2ko](https://github.com/tsyr2ko)
|
||||
* [@trowski (Aaron Piotrowski)](https://github.com/trowski)
|
||||
* [@twistor (Chris Lepannen)](https://github.com/twistor)
|
||||
* [@voku (Lars Moelleken)](https://github.com/voku)
|
||||
* [@xabbuh (Christian Flothmann)](https://github.com/xabbuh)
|
|
@ -0,0 +1,108 @@
|
|||
# An Invitation to Security Researchers
|
||||
|
||||
Every company says they take security "very seriously." Rather than bore anyone
|
||||
with banal boilerplate, here are some quick answers followed by detailed
|
||||
elaboration. If you have any questions about our policies, please email them to
|
||||
`scott@paragonie.com`.
|
||||
|
||||
## Quick Answers
|
||||
|
||||
* There is no compulsion to disclose vulnerabilities privately, but we
|
||||
appreciate a head's up.
|
||||
* `security@paragonie.com` will get your reports to the right person. Our GPG
|
||||
fingerprint, should you decide to encrypt your report, is
|
||||
`7F52 D5C6 1D12 55C7 3136 2E82 6B97 A1C2 8264 04DA`.
|
||||
|
||||
* **YES**, we will reward security researchers who disclose vulnerabilities in
|
||||
our software.
|
||||
* In most cases, **No Proof-of-Concept Required.**
|
||||
|
||||
## How to Report a Security Bug to Paragon Initiative Enterprises
|
||||
|
||||
### There is no compulsion to disclose privately.
|
||||
|
||||
We believe vulnerability disclosure style is a personal choice and enjoy working
|
||||
with a diverse community. We understand and appreciate the importance of Full
|
||||
Disclosure in the history and practice of security research.
|
||||
|
||||
We would *like* to know about high-severity bugs before they become public
|
||||
knowledge, so we can fix them in a timely manner, but **we do not believe in
|
||||
threatening researchers or trying to enforce vulnerability embargoes**.
|
||||
|
||||
Ultimately, if you discover a security-affecting vulnerability, what you do with
|
||||
it is your choice. We would like to work with people, and to celebrate and
|
||||
reward their skill, experience, and dedication. We appreciate being informed of
|
||||
our mistakes so we can learn from them and build a better product. Our goal is
|
||||
to empower the community.
|
||||
|
||||
### Where to Send Security Vulnerabilities
|
||||
|
||||
Our security email address is `security@paragonie.com`. Also feel free to open a
|
||||
new issue on Github if you want to disclose publicly.
|
||||
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG
|
||||
|
||||
mQENBFUgwRUBCADcIpqNwyYc5UmY/tpx1sF/rQ3knR1YNXYZThzFV+Gmqhp1fDH5
|
||||
qBs9foh1xwI6O7knWmQngnf/nBumI3x6xj7PuOdEZUh2FwCG/VWnglW8rKmoHzHA
|
||||
ivjiu9SLnPIPAgHSHeh2XD7q3Ndm3nenbjAiRFNl2iXcwA2cTQp9Mmfw9vVcw0G0
|
||||
z1o0G3s8cC8ZS6flFySIervvfSRWj7A1acI5eE3+AH/qXJRdEJ+9J8OB65p1JMfk
|
||||
6+fWgOB1XZxMpz70S0rW6IX38WDSRhEK2fXyZJAJjyt+YGuzjZySNSoQR/V6vNYn
|
||||
syrNPCJ2i5CgZQxAkyBBcr7koV9RIhPRzct/ABEBAAG0IVNlY3VyaXR5IDxzZWN1
|
||||
cml0eUBwYXJhZ29uaWUuY29tPokBOQQTAQIAIwUCVSDBFQIbAwcLCQgHAwIBBhUI
|
||||
AgkKCwQWAgMBAh4BAheAAAoJEGuXocKCZATat2YIAIoejNFEQ2c1iaOEtSuB7Pn/
|
||||
WLbsDsHNLDKOV+UnfaCjv/vL7D+5NMChFCi2frde/NQb2TsjqmIH+V+XbnJtlrXD
|
||||
Vj7yvMVal+Jqjwj7v4eOEWcKVcFZk+9cfUgh7t92T2BMX58RpgZF0IQZ6Z1R3FfC
|
||||
9Ub4X6ykW+te1q0/4CoRycniwmlQi6iGSr99LQ5pfJq2Qlmz/luTZ0UX0h575T7d
|
||||
cp2T1sX/zFRk/fHeANWSksipdDBjAXR7NMnYZgw2HghEdFk/xRDY7K1NRWNZBf05
|
||||
WrMHmh6AIVJiWZvI175URxEe268hh+wThBhXQHMhFNJM1qPIuzb4WogxM3UUD7m5
|
||||
AQ0EVSDBFQEIALNkpzSuJsHAHh79sc0AYWztdUe2MzyofQbbOnOCpWZebYsC3EXU
|
||||
335fIg59k0m6f+O7GmEZzzIv5v0i99GS1R8CJm6FvhGqtH8ZqmOGbc71WdJSiNVE
|
||||
0kpQoJlVzRbig6ZyyjzrggbM1eh5OXOk5pw4+23FFEdw7JWU0HJS2o71r1hwp05Z
|
||||
vy21kcUEobz/WWQQyGS0Neo7PJn+9KS6wOxXul/UE0jct/5f7KLMdWMJ1VgniQmm
|
||||
hjvkHLPSICteqCI04RfcmMseW9gueHQXeUu1SNIvsWa2MhxjeBej3pDnrZWszKwy
|
||||
gF45GO9/v4tkIXNMy5J1AtOyRgQ3IUMqp8EAEQEAAYkBHwQYAQIACQUCVSDBFQIb
|
||||
DAAKCRBrl6HCgmQE2jnIB/4/xFz8InpM7eybnBOAir3uGcYfs3DOmaKn7qWVtGzv
|
||||
rKpQPYnVtlU2i6Z5UO4c4jDLT/8Xm1UDz3Lxvqt4xCaDwJvBZexU5BMK8l5DvOzH
|
||||
6o6P2L1UDu6BvmPXpVZz7/qUhOnyf8VQg/dAtYF4/ax19giNUpI5j5o5mX5w80Rx
|
||||
qSXV9NdSL4fdjeG1g/xXv2luhoV53T1bsycI3wjk/x5tV+M2KVhZBvvuOm/zhJje
|
||||
oLWp0saaESkGXIXqurj6gZoujJvSvzl0n9F9VwqMEizDUfrXgtD1siQGhP0sVC6q
|
||||
ha+F/SAEJ0jEquM4TfKWWU2S5V5vgPPpIQSYRnhQW4b1
|
||||
=xJPW
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
```
|
||||
|
||||
### We Will Reward Security Researchers
|
||||
|
||||
**This process has not been formalized; nor have dollar amounts been
|
||||
discussed.**
|
||||
|
||||
However, if you report a valid security-affecting bug, we will compensate you
|
||||
for the time spent finding the vulnerability and reward you for being a good
|
||||
neighbor.
|
||||
|
||||
#### What does a "valid" bug mean?
|
||||
|
||||
There are two sides to this:
|
||||
|
||||
1. Some have spammed projects with invalid bug reports hoping to collect
|
||||
bounties for pressing a button and running an automated analysis tool. This
|
||||
is not cool.
|
||||
2. There is a potential for the developers of a project to declare all security
|
||||
bug reports as invalid to save money.
|
||||
|
||||
Our team members have an established history of reporting vulnerabilities to
|
||||
large open source projects. **We aren't in the business of ripping people off.**
|
||||
When in doubt, our policy is to err on the side of generosity.
|
||||
|
||||
### No Proof-of-Concept Required
|
||||
|
||||
We might ask for one if we feel we do not understand some of the details
|
||||
pertaining to a specific vulnerability. We certainly appreciate them if you
|
||||
include them in your report, but we believe **the burden lies with the developer
|
||||
to prove their software *is* secure** rather than with the researcher to prove
|
||||
that it isn't.
|
||||
|
||||
In our experience, most bugs are simpler to fix than they are to exploit.
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"name": "paragonie/random_compat",
|
||||
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
|
||||
"keywords": [
|
||||
"csprng",
|
||||
"random",
|
||||
"pseudorandom"
|
||||
],
|
||||
"license": "MIT",
|
||||
"type": "library",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Paragon Initiative Enterprises",
|
||||
"email": "security@paragonie.com",
|
||||
"homepage": "https://paragonie.com"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/paragonie/random_compat/issues",
|
||||
"email": "info@paragonie.com",
|
||||
"source": "https://github.com/paragonie/random_compat"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "4.*|5.*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
|
||||
},
|
||||
"autoload": {
|
||||
"files": ["lib/random.php"]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Paragon Initiative Enterprises
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
if (!function_exists('RandomCompat_strlen')) {
|
||||
if (
|
||||
defined('MB_OVERLOAD_STRING') &&
|
||||
ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING
|
||||
) {
|
||||
/**
|
||||
* strlen() implementation that isn't brittle to mbstring.func_overload
|
||||
*
|
||||
* This version uses mb_strlen() in '8bit' mode to treat strings as raw
|
||||
* binary rather than UTF-8, ISO-8859-1, etc
|
||||
*
|
||||
* @param string $binary_string
|
||||
*
|
||||
* @throws TypeError
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function RandomCompat_strlen($binary_string)
|
||||
{
|
||||
if (!is_string($binary_string)) {
|
||||
throw new TypeError(
|
||||
'RandomCompat_strlen() expects a string'
|
||||
);
|
||||
}
|
||||
return mb_strlen($binary_string, '8bit');
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* strlen() implementation that isn't brittle to mbstring.func_overload
|
||||
*
|
||||
* This version just used the default strlen()
|
||||
*
|
||||
* @param string $binary_string
|
||||
*
|
||||
* @throws TypeError
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function RandomCompat_strlen($binary_string)
|
||||
{
|
||||
if (!is_string($binary_string)) {
|
||||
throw new TypeError(
|
||||
'RandomCompat_strlen() expects a string'
|
||||
);
|
||||
}
|
||||
return strlen($binary_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('RandomCompat_substr')) {
|
||||
if (
|
||||
defined('MB_OVERLOAD_STRING') &&
|
||||
ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING
|
||||
) {
|
||||
/**
|
||||
* 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'
|
||||
);
|
||||
}
|
||||
return mb_substr($binary_string, $start, $length, '8bit');
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* substr() implementation that isn't brittle to mbstring.func_overload
|
||||
*
|
||||
* This version just uses the default substr()
|
||||
*
|
||||
* @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) {
|
||||
if (!is_int($length)) {
|
||||
throw new TypeError(
|
||||
'RandomCompat_substr(): Third argument should be an integer, or omitted'
|
||||
);
|
||||
}
|
||||
return substr($binary_string, $start, $length);
|
||||
}
|
||||
return substr($binary_string, $start);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Paragon Initiative Enterprises
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
if (!function_exists('RandomCompat_intval')) {
|
||||
|
||||
/**
|
||||
* Cast to an integer if we can, safely.
|
||||
*
|
||||
* If you pass it a float in the range (~PHP_INT_MAX, PHP_INT_MAX)
|
||||
* (non-inclusive), it will sanely cast it to an int. If you it's equal to
|
||||
* ~PHP_INT_MAX or PHP_INT_MAX, we let it fail as not an integer. Floats
|
||||
* lose precision, so the <= and => operators might accidentally let a float
|
||||
* through.
|
||||
*
|
||||
* @param numeric $number The number we want to convert to an int
|
||||
* @param boolean $fail_open Set to true to not throw an exception
|
||||
*
|
||||
* @return int (or float if $fail_open)
|
||||
*/
|
||||
function RandomCompat_intval($number, $fail_open = false)
|
||||
{
|
||||
if (is_numeric($number)) {
|
||||
$number += 0;
|
||||
}
|
||||
if (
|
||||
is_float($number) &&
|
||||
$number > ~PHP_INT_MAX &&
|
||||
$number < PHP_INT_MAX
|
||||
) {
|
||||
$number = (int) $number;
|
||||
}
|
||||
if (is_int($number) || $fail_open) {
|
||||
return $number;
|
||||
}
|
||||
throw new TypeError(
|
||||
'Expected an integer.'
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Paragon Initiative Enterprises
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
if (!class_exists('Error', false)) {
|
||||
// We can't really avoid making this extend Exception in PHP 5.
|
||||
class Error extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists('TypeError', false)) {
|
||||
class TypeError extends Error
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Paragon Initiative Enterprises
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
if (!defined('PHP_VERSION_ID')) {
|
||||
// This constant was introduced in PHP 5.2.7
|
||||
$RandomCompatversion = explode('.', PHP_VERSION);
|
||||
define('PHP_VERSION_ID', ($RandomCompatversion[0] * 10000 + $RandomCompatversion[1] * 100 + $RandomCompatversion[2]));
|
||||
$RandomCompatversion = null;
|
||||
}
|
||||
if (PHP_VERSION_ID < 70000) {
|
||||
if (!defined('RANDOM_COMPAT_READ_BUFFER')) {
|
||||
define('RANDOM_COMPAT_READ_BUFFER', 8);
|
||||
}
|
||||
$RandomCompatDIR = dirname(__FILE__);
|
||||
require_once $RandomCompatDIR.'/byte_safe_strings.php';
|
||||
require_once $RandomCompatDIR.'/cast_to_int.php';
|
||||
require_once $RandomCompatDIR.'/error_polyfill.php';
|
||||
if (!function_exists('random_bytes')) {
|
||||
/**
|
||||
* PHP 5.2.0 - 5.6.x way to implement random_bytes()
|
||||
*
|
||||
* We use conditional statements here to define the function in accordance
|
||||
* to the operating environment. It's a micro-optimization.
|
||||
*
|
||||
* In order of preference:
|
||||
* 1. Use libsodium if available.
|
||||
* 2. fread() /dev/urandom if available (never on Windows)
|
||||
* 3. mcrypt_create_iv($bytes, MCRYPT_CREATE_IV)
|
||||
* 4. COM('CAPICOM.Utilities.1')->GetRandom()
|
||||
* 5. openssl_random_pseudo_bytes() (absolute last resort)
|
||||
*
|
||||
* See ERRATA.md for our reasoning behind this particular order
|
||||
*/
|
||||
if (extension_loaded('libsodium')) {
|
||||
// See random_bytes_libsodium.php
|
||||
require_once $RandomCompatDIR.'/random_bytes_libsodium.php';
|
||||
}
|
||||
if (
|
||||
!function_exists('random_bytes') &&
|
||||
DIRECTORY_SEPARATOR === '/' &&
|
||||
@is_readable('/dev/urandom')
|
||||
) {
|
||||
// DIRECTORY_SEPARATOR === '/' on Unix-like OSes -- this is a fast
|
||||
// way to exclude Windows.
|
||||
//
|
||||
// Error suppression on is_readable() in case of an open_basedir or
|
||||
// safe_mode failure. All we care about is whether or not we can
|
||||
// read it at this point. If the PHP environment is going to panic
|
||||
// over trying to see if the file can be read in the first place,
|
||||
// that is not helpful to us here.
|
||||
|
||||
// See random_bytes_dev_urandom.php
|
||||
require_once $RandomCompatDIR.'/random_bytes_dev_urandom.php';
|
||||
}
|
||||
if (
|
||||
!function_exists('random_bytes') &&
|
||||
PHP_VERSION_ID >= 50307 &&
|
||||
extension_loaded('mcrypt')
|
||||
) {
|
||||
// See random_bytes_mcrypt.php
|
||||
require_once $RandomCompatDIR.'/random_bytes_mcrypt.php';
|
||||
}
|
||||
if (
|
||||
!function_exists('random_bytes') &&
|
||||
extension_loaded('com_dotnet') &&
|
||||
class_exists('COM')
|
||||
) {
|
||||
try {
|
||||
$RandomCompatCOMtest = new COM('CAPICOM.Utilities.1');
|
||||
if (method_exists($RandomCompatCOMtest, 'GetRandom')) {
|
||||
// See random_bytes_com_dotnet.php
|
||||
require_once $RandomCompatDIR.'/random_bytes_com_dotnet.php';
|
||||
}
|
||||
} catch (com_exception $e) {
|
||||
// Don't try to use it.
|
||||
}
|
||||
$RandomCompatCOMtest = null;
|
||||
}
|
||||
if (
|
||||
!function_exists('random_bytes') &&
|
||||
extension_loaded('openssl') &&
|
||||
(
|
||||
// Unix-like with PHP >= 5.3.0 or
|
||||
(
|
||||
DIRECTORY_SEPARATOR === '/' &&
|
||||
PHP_VERSION_ID >= 50300
|
||||
) ||
|
||||
// Windows with PHP >= 5.4.1
|
||||
PHP_VERSION_ID >= 50401
|
||||
)
|
||||
) {
|
||||
// See random_bytes_openssl.php
|
||||
require_once $RandomCompatDIR.'/random_bytes_openssl.php';
|
||||
}
|
||||
if (!function_exists('random_bytes')) {
|
||||
/**
|
||||
* We don't have any more options, so let's throw an exception right now
|
||||
* and hope the developer won't let it fail silently.
|
||||
*/
|
||||
function random_bytes()
|
||||
{
|
||||
throw new Exception(
|
||||
'There is no suitable CSPRNG installed on your system'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!function_exists('random_int')) {
|
||||
require_once $RandomCompatDIR.'/random_int.php';
|
||||
}
|
||||
$RandomCompatDIR = null;
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Paragon Initiative Enterprises
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
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 = '';
|
||||
$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(
|
||||
'Could not gather sufficient random data'
|
||||
);
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Paragon Initiative Enterprises
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
if (!defined('RANDOM_COMPAT_READ_BUFFER')) {
|
||||
define('RANDOM_COMPAT_READ_BUFFER', 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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'
|
||||
);
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Paragon Initiative Enterprises
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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'
|
||||
);
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Paragon Initiative Enterprises
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (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'
|
||||
);
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Paragon Initiative Enterprises
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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'
|
||||
);
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
/**
|
||||
* Random_* Compatibility Library
|
||||
* for using the new PHP 7 random_* API in PHP 5 projects
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Paragon Initiative Enterprises
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fetch a random integer between $min and $max inclusive
|
||||
*
|
||||
* @param int $min
|
||||
* @param int $max
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function random_int($min, $max)
|
||||
{
|
||||
/**
|
||||
* Type and input logic checks
|
||||
*
|
||||
* If you pass it a float in the range (~PHP_INT_MAX, PHP_INT_MAX)
|
||||
* (non-inclusive), it will sanely cast it to an int. If you it's equal to
|
||||
* ~PHP_INT_MAX or PHP_INT_MAX, we let it fail as not an integer. Floats
|
||||
* lose precision, so the <= and => operators might accidentally let a float
|
||||
* through.
|
||||
*/
|
||||
|
||||
try {
|
||||
$min = RandomCompat_intval($min);
|
||||
} catch (TypeError $ex) {
|
||||
throw new TypeError(
|
||||
'random_int(): $min must be an integer'
|
||||
);
|
||||
}
|
||||
try {
|
||||
$max = RandomCompat_intval($max);
|
||||
} catch (TypeError $ex) {
|
||||
throw new TypeError(
|
||||
'random_int(): $max must be an integer'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Now that we've verified our weak typing system has given us an integer,
|
||||
* let's validate the logic then we can move forward with generating random
|
||||
* integers along a given range.
|
||||
*/
|
||||
if ($min > $max) {
|
||||
throw new Error(
|
||||
'Minimum value must be less than or equal to the maximum value'
|
||||
);
|
||||
}
|
||||
if ($max === $min) {
|
||||
return $min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize variables to 0
|
||||
*
|
||||
* We want to store:
|
||||
* $bytes => the number of random bytes we need
|
||||
* $mask => an integer bitmask (for use with the &) operator
|
||||
* so we can minimize the number of discards
|
||||
*/
|
||||
$attempts = $bits = $bytes = $mask = $valueShift = 0;
|
||||
|
||||
/**
|
||||
* At this point, $range is a positive number greater than 0. It might
|
||||
* overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to
|
||||
* a float and we will lose some precision.
|
||||
*/
|
||||
$range = $max - $min;
|
||||
|
||||
/**
|
||||
* Test for integer overflow:
|
||||
*/
|
||||
if (!is_int($range)) {
|
||||
/**
|
||||
* Still safely calculate wider ranges.
|
||||
* Provided by @CodesInChaos, @oittaa
|
||||
*
|
||||
* @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435
|
||||
*
|
||||
* We use ~0 as a mask in this case because it generates all 1s
|
||||
*
|
||||
* @ref https://eval.in/400356 (32-bit)
|
||||
* @ref http://3v4l.org/XX9r5 (64-bit)
|
||||
*/
|
||||
$bytes = PHP_INT_SIZE;
|
||||
$mask = ~0;
|
||||
} else {
|
||||
/**
|
||||
* $bits is effectively ceil(log($range, 2)) without dealing with
|
||||
* type juggling
|
||||
*/
|
||||
while ($range > 0) {
|
||||
if ($bits % 8 === 0) {
|
||||
++$bytes;
|
||||
}
|
||||
++$bits;
|
||||
$range >>= 1;
|
||||
$mask = $mask << 1 | 1;
|
||||
}
|
||||
$valueShift = $min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Now that we have our parameters set up, let's begin generating
|
||||
* random integers until one falls between $min and $max
|
||||
*/
|
||||
do {
|
||||
/**
|
||||
* The rejection probability is at most 0.5, so this corresponds
|
||||
* to a failure probability of 2^-128 for a working RNG
|
||||
*/
|
||||
if ($attempts > 128) {
|
||||
throw new Exception(
|
||||
'random_int: RNG is broken - too many rejections'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Let's grab the necessary number of random bytes
|
||||
*/
|
||||
$randomByteString = random_bytes($bytes);
|
||||
if ($randomByteString === false) {
|
||||
throw new Exception(
|
||||
'Random number generator failure'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Let's turn $randomByteString into an integer
|
||||
*
|
||||
* This uses bitwise operators (<< and |) to build an integer
|
||||
* out of the values extracted from ord()
|
||||
*
|
||||
* Example: [9F] | [6D] | [32] | [0C] =>
|
||||
* 159 + 27904 + 3276800 + 201326592 =>
|
||||
* 204631455
|
||||
*/
|
||||
$val = 0;
|
||||
for ($i = 0; $i < $bytes; ++$i) {
|
||||
$val |= ord($randomByteString[$i]) << ($i * 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply mask
|
||||
*/
|
||||
$val &= $mask;
|
||||
$val += $valueShift;
|
||||
|
||||
++$attempts;
|
||||
/**
|
||||
* If $val overflows to a floating point number,
|
||||
* ... or is larger than $max,
|
||||
* ... or smaller than $min,
|
||||
* then try again.
|
||||
*/
|
||||
} while (!is_int($val) || $val > $max || $val < $min);
|
||||
return (int) $val;
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if [ "$1" == 'full' ]; then
|
||||
fulltest=1
|
||||
elif [ "$1" == 'each' ]; then
|
||||
testeach=1
|
||||
else
|
||||
fulltest=0
|
||||
fi
|
||||
|
||||
PHP_VERSION=$(php -r "echo PHP_VERSION_ID;")
|
||||
|
||||
echo
|
||||
echo -e "\033[33mBegin Unit Testing\033[0m"
|
||||
# Run the testing suite
|
||||
echo "Basic test suite:"
|
||||
php vendor/bin/phpunit tests/unit
|
||||
if [ $? -ne 0 ]; then
|
||||
# Test failure
|
||||
exit 1
|
||||
fi
|
||||
echo "With open_basedir enabled:"
|
||||
php -d open_basedir=`pwd` vendor/bin/phpunit tests/unit
|
||||
if [ $? -ne 0 ]; then
|
||||
# Test failure
|
||||
exit 1
|
||||
fi
|
||||
echo "With open_basedir enabled, allowing /dev:"
|
||||
php -d open_basedir=`pwd`:/dev vendor/bin/phpunit tests/unit
|
||||
if [ $? -ne 0 ]; then
|
||||
# Test failure
|
||||
exit 1
|
||||
fi
|
||||
echo "With mbstring.func_overload enabled:"
|
||||
php -d mbstring.func_overload=7 vendor/bin/phpunit tests/unit
|
||||
if [ $? -ne 0 ]; then
|
||||
# Test failure
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$testeach" == "1" ]]; then
|
||||
echo " CAPICOM:"
|
||||
php vendor/bin/phpunit --bootstrap tests/specific/capicom.php tests/unit
|
||||
echo " /dev/urandom:"
|
||||
php vendor/bin/phpunit --bootstrap tests/specific/dev_urandom.php tests/unit
|
||||
echo " libsodium:"
|
||||
php vendor/bin/phpunit --bootstrap tests/specific/libsodium.php tests/unit
|
||||
echo " mcrypt:"
|
||||
php vendor/bin/phpunit --bootstrap tests/specific/mcrypt.php tests/unit
|
||||
echo " openssl:"
|
||||
php vendor/bin/phpunit --bootstrap tests/specific/openssl.php tests/unit
|
||||
fi
|
||||
|
||||
# Should we perform full statistical analyses?
|
||||
if [[ "$fulltest" == "1" ]]; then
|
||||
php vendor/bin/phpunit tests/full
|
||||
if [ $? -ne 0 ]; then
|
||||
# Test failure
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="true"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="lib/random.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Unit">
|
||||
<directory>tests/unit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<testsuites>
|
||||
<testsuite name="Random Compat Test Suite">
|
||||
<directory suffix="Test.php">./tests/unit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">./lib</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
Loading…
Reference in New Issue