You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
131 lines
4.2 KiB
131 lines
4.2 KiB
<?php
|
|
/**
|
|
* li₃: the most RAD framework for PHP (http://li3.me)
|
|
*
|
|
* Copyright 2015, Union of RAD. All rights reserved. This source
|
|
* code is distributed under the terms of the BSD 3-Clause License.
|
|
* The full license text can be found in the LICENSE.txt file.
|
|
*/
|
|
|
|
namespace lithium\security;
|
|
|
|
use COM;
|
|
use LogicException;
|
|
|
|
/**
|
|
* A cryptographically-strong random number generator, which allows to generate arbritrary
|
|
* length strings of bytes, usable for i.e. password salts, UUIDs, keys or initialization
|
|
* vectors (IVs). Random byte strings can be encoded using the base64-encoder for use with
|
|
* DES and XDES.
|
|
*/
|
|
class Random {
|
|
|
|
/**
|
|
* Option flag for the encoder.
|
|
*
|
|
* @see lithium\security\Random::generate()
|
|
*/
|
|
const ENCODE_BASE_64 = 1;
|
|
|
|
/**
|
|
* A callable which, given a number of bytes, returns that
|
|
* amount of random bytes.
|
|
*
|
|
* @see lithium\security\Random::_source()
|
|
* @var callable
|
|
*/
|
|
protected static $_source;
|
|
|
|
/**
|
|
* Generates random bytes for use in UUIDs and password salts, using
|
|
* a cryptographically strong random number generator source.
|
|
*
|
|
* ```
|
|
* $bits = Random::generate(8); // 64 bits
|
|
* $hex = bin2hex($bits); // [0-9a-f]+
|
|
* ```
|
|
*
|
|
* Optionally base64-encodes the resulting random string per the following. The
|
|
* alphabet used by `base64_encode()` is different than the one we should be using.
|
|
* When considering the meaty part of the resulting string, however, a bijection
|
|
* allows to go the from one to another. Given that we're working on random bytes, we
|
|
* can use safely use `base64_encode()` without losing any entropy.
|
|
*
|
|
* @param integer $bytes The number of random bytes to generate.
|
|
* @param array $options The options used when generating random bytes:
|
|
* - `'encode'` _integer_: If specified, and set to `Random::ENCODE_BASE_64`, the
|
|
* resulting value will be base64-encoded, per the note above.
|
|
* @return string Returns (an encoded) string of random bytes.
|
|
*/
|
|
public static function generate($bytes, array $options = []) {
|
|
$defaults = ['encode' => null];
|
|
$options += $defaults;
|
|
|
|
$source = static::$_source ?: (static::$_source = static::_source());
|
|
$result = $source($bytes);
|
|
|
|
if ($options['encode'] !== static::ENCODE_BASE_64) {
|
|
return $result;
|
|
}
|
|
return strtr(rtrim(base64_encode($result), '='), '+', '.');
|
|
}
|
|
|
|
/**
|
|
* Returns the best available random number generator source.
|
|
*
|
|
* The source of randomness used are as follows:
|
|
*
|
|
* 1. `random_bytes()`, available in PHP >=7.0
|
|
* 2. `random_bytes()`, available if the openssl extension is installed
|
|
* 3. `mcrypt_create_iv()`, available if the mcrypt extensions is installed
|
|
* 4. `/dev/urandom`, available on *nix
|
|
* 5. `GetRandom()` through COM, available on Windows
|
|
*
|
|
* Note: Users restricting path access through the `open_basedir` INI setting,
|
|
* will need to include `/dev/urandom` into the list of allowed paths, as this
|
|
* method might read from it.
|
|
*
|
|
* @link http://php.net/random_bytes
|
|
* @link http://php.net/mcrypt_create_iv
|
|
* @link http://msdn.microsoft.com/en-us/library/aa388182%28VS.85%29.aspx?ppud=4
|
|
* @link http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/
|
|
* @see lithium\util\Random::$_source
|
|
* @return callable Returns a closure containing a random number generator.
|
|
*/
|
|
protected static function _source() {
|
|
if (function_exists('random_bytes')) {
|
|
return function($bytes) {
|
|
return random_bytes($bytes);
|
|
};
|
|
}
|
|
if (function_exists('openssl_random_pseudo_bytes')) {
|
|
return function($bytes) {
|
|
return openssl_random_pseudo_bytes($bytes);
|
|
};
|
|
}
|
|
if (function_exists('mcrypt_create_iv')) {
|
|
return function($bytes) {
|
|
return mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM);
|
|
};
|
|
}
|
|
if (is_readable('/dev/urandom')) {
|
|
return function($bytes) {
|
|
$stream = fopen('/dev/urandom', 'rb');
|
|
$result = fread($stream, $bytes);
|
|
fclose($stream);
|
|
|
|
return $result;
|
|
};
|
|
}
|
|
if (class_exists('COM', false)) {
|
|
$com = new COM('CAPICOM.Utilities.1');
|
|
|
|
return function($bytes) use ($com) {
|
|
return base64_decode($com->GetRandom($bytes, 0));
|
|
};
|
|
}
|
|
throw new LogicException('No suitable strong random number generator source found.');
|
|
}
|
|
}
|
|
|
|
?>
|