├── .gitignore ├── Makefile ├── test ├── Unit │ └── RandomLib │ │ ├── Source │ │ ├── UniqIDTest.php │ │ ├── CAPICOMTest.php │ │ ├── URandomTest.php │ │ ├── RandTest.php │ │ ├── MTRandTest.php │ │ ├── MicroTimeTest.php │ │ ├── AbstractSourceTest.php │ │ └── SodiumTest.php │ │ ├── Mixer │ │ ├── HashTest.php │ │ └── McryptRijndael128Test.php │ │ ├── FactoryTest.php │ │ ├── GeneratorStringTest.php │ │ └── GeneratorTest.php ├── Mocks │ ├── AbstractMock.php │ └── Random │ │ ├── Generator.php │ │ ├── Mixer.php │ │ └── Source.php ├── bootstrap.php └── Vectors │ └── Random │ └── GeneratorTest.php ├── .travis.yml ├── composer.json ├── phpunit.xml.dist ├── LICENSE ├── lib └── RandomLib │ ├── Mixer │ ├── McryptRijndael128.php │ ├── XorMixer.php │ └── Hash.php │ ├── Source │ ├── Random.php │ ├── UniqID.php │ ├── RandomBytes.php │ ├── CAPICOM.php │ ├── Rand.php │ ├── MTRand.php │ ├── URandom.php │ ├── Sodium.php │ ├── OpenSSL.php │ └── MicroTime.php │ ├── Mixer.php │ ├── AbstractSource.php │ ├── Source.php │ ├── AbstractMcryptMixer.php │ ├── AbstractMixer.php │ ├── Factory.php │ └── Generator.php ├── .php_cs ├── .scrutinizer.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.lock 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | .PHONY: all 4 | all: build 5 | 6 | .PHONY: build 7 | build: lint cs test 8 | 9 | lintfiles := $(shell find lib test -type f -iname '*.php') 10 | 11 | .PHONY: ${lintfiles} 12 | ${lintfiles}: 13 | php -l $@ 14 | 15 | .PHONY: lint 16 | lint: $(lintfiles) 17 | 18 | .PHONY: cs 19 | cs: 20 | vendor/bin/php-cs-fixer --quiet --no-interaction fix; true 21 | 22 | 23 | .PHONY: test 24 | test: 25 | vendor/bin/phpunit 26 | -------------------------------------------------------------------------------- /test/Unit/RandomLib/Source/UniqIDTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | namespace RandomLib\Source; 12 | 13 | use SecurityLib\Strength; 14 | 15 | class UniqIDTest extends AbstractSourceTest 16 | { 17 | protected static function getExpectedStrength() 18 | { 19 | return new Strength(Strength::LOW); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/Unit/RandomLib/Source/CAPICOMTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | namespace RandomLib\Source; 12 | 13 | use SecurityLib\Strength; 14 | 15 | class CAPICOMTest extends AbstractSourceTest 16 | { 17 | protected static function getExpectedStrength() 18 | { 19 | return new Strength(Strength::MEDIUM); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/Unit/RandomLib/Source/URandomTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | namespace RandomLib\Source; 12 | 13 | use SecurityLib\Strength; 14 | 15 | class URandomTest extends AbstractSourceTest 16 | { 17 | protected static function getExpectedStrength() 18 | { 19 | return new Strength(Strength::MEDIUM); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/Unit/RandomLib/Source/RandTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | namespace RandomLib\Source; 12 | 13 | use SecurityLib\Strength; 14 | 15 | class RandTest extends AbstractSourceTest 16 | { 17 | protected static function getExpectedStrength() 18 | { 19 | if (defined('S_ALL')) { 20 | return new Strength(Strength::LOW); 21 | } else { 22 | return new Strength(Strength::VERYLOW); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/Unit/RandomLib/Source/MTRandTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | namespace RandomLib\Source; 12 | 13 | use SecurityLib\Strength; 14 | 15 | class MTRandTest extends AbstractSourceTest 16 | { 17 | protected static function getExpectedStrength() 18 | { 19 | if (defined('S_ALL')) { 20 | return new Strength(Strength::MEDIUM); 21 | } else { 22 | return new Strength(Strength::LOW); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | sudo: required 4 | 5 | matrix: 6 | include: 7 | - php: 7.0 8 | env: LIBSODIUM=1 9 | - php: 5.6 10 | env: LIBSODIUM=1 11 | - php: 5.5 12 | env: LIBSODIUM=1 13 | - php: 5.4 14 | env: LIBSODIUM=1 15 | - php: 5.3 16 | env: LIBSODIUM=0 17 | - php: hhvm 18 | env: LIBSODIUM=0 # libsodium extension is not supported by HHVM 19 | 20 | before_install: 21 | - if [[ "$LIBSODIUM" == "1" ]]; then sudo add-apt-repository -y ppa:chris-lea/libsodium; fi 22 | - if [[ "$LIBSODIUM" == "1" ]]; then sudo apt-get -q update; fi 23 | - if [[ "$LIBSODIUM" == "1" ]]; then sudo apt-get -y install libsodium-dev; fi 24 | - if [[ "$LIBSODIUM" == "1" ]]; then pecl install libsodium; fi 25 | 26 | before_script: 27 | - travis_retry composer self-update 28 | - travis_retry composer install --prefer-source 29 | 30 | script: 31 | - make lint 32 | - make test 33 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ircmaxell/random-lib", 3 | "type": "library", 4 | "description": "A Library For Generating Secure Random Numbers", 5 | "keywords": ["random", "random-numbers", "random-strings", "cryptography"], 6 | "homepage": "https://github.com/ircmaxell/RandomLib", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Anthony Ferrara", 11 | "email": "ircmaxell@ircmaxell.com", 12 | "homepage": "http://blog.ircmaxell.com" 13 | } 14 | ], 15 | "require-dev": { 16 | "mikey179/vfsStream": "^1.6", 17 | "friendsofphp/php-cs-fixer": "^1.11", 18 | "phpunit/phpunit": "^4.8|^5.0" 19 | }, 20 | "require": { 21 | "ircmaxell/security-lib": "^1.1", 22 | "php": ">=5.3.2" 23 | }, 24 | "autoload": { 25 | "psr-0": { 26 | "RandomLib": "lib" 27 | } 28 | }, 29 | "extra": { 30 | "branch-alias": { 31 | "dev-master": "1.1.x-dev" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | test/Unit 22 | 23 | 24 | 25 | 26 | lib/ 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /test/Unit/RandomLib/Source/MicroTimeTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | namespace RandomLib\Source; 12 | 13 | use SecurityLib\Strength; 14 | 15 | class MicroTimeTest extends AbstractSourceTest 16 | { 17 | protected static function getExpectedStrength() 18 | { 19 | return new Strength(Strength::VERYLOW); 20 | } 21 | 22 | /** 23 | * Test the initialization of the static counter (!== 0) 24 | */ 25 | public function testCounterNotNull() 26 | { 27 | $class = static::getTestedClass(); 28 | $rand = new $class(); 29 | $reflection_class = new \ReflectionClass($class); 30 | $static = $reflection_class->getStaticProperties(); 31 | $this->assertTrue($static['counter'] !== 0); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 The Authors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /test/Mocks/AbstractMock.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The interface that all hash implementations must implement 14 | * 15 | * PHP version 5.3 16 | * 17 | * @category PHPPasswordLib 18 | * @package Hash 19 | * 20 | * @author Anthony Ferrara 21 | * @copyright 2011 The Authors 22 | * @license http://opensource.org/licenses/bsd-license.php New BSD License 23 | * @license http://www.gnu.org/licenses/lgpl-2.1.html LGPL v 2.1 24 | */ 25 | namespace RandomLibtest\Mocks; 26 | 27 | /** 28 | * The interface that all hash implementations must implement 29 | * 30 | * @category PHPPasswordLib 31 | * @package Hash 32 | * 33 | * @author Anthony Ferrara 34 | */ 35 | class AbstractMock 36 | { 37 | protected $callbacks = array(); 38 | 39 | public static function init() 40 | { 41 | } 42 | 43 | public function __construct(array $callbacks = array()) 44 | { 45 | $this->callbacks = $callbacks; 46 | } 47 | 48 | public function __call($name, array $args = array()) 49 | { 50 | if (isset($this->callbacks[$name])) { 51 | return call_user_func_array($this->callbacks[$name], $args); 52 | } 53 | 54 | return null; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/RandomLib/Mixer/McryptRijndael128.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * mcrypt mixer using the Rijndael cipher with 128 bit block size 14 | * 15 | * PHP version 5.3 16 | * 17 | * @category PHPCryptLib 18 | * @package Random 19 | * @subpackage Mixer 20 | * 21 | * @author Anthony Ferrara 22 | * @copyright 2013 The Authors 23 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 24 | * 25 | * @version Build @@version@@ 26 | */ 27 | namespace RandomLib\Mixer; 28 | 29 | use RandomLib\AbstractMcryptMixer; 30 | use SecurityLib\Strength; 31 | 32 | /** 33 | * mcrypt mixer using the Rijndael cipher with 128 bit block size 34 | * 35 | * @category PHPCryptLib 36 | * @package Random 37 | * @subpackage Mixer 38 | * 39 | * @author Anthony Ferrara 40 | * @author Chris Smith 41 | */ 42 | class McryptRijndael128 extends AbstractMcryptMixer 43 | { 44 | /** 45 | * {@inheritdoc} 46 | */ 47 | public static function getStrength() 48 | { 49 | return new Strength(Strength::HIGH); 50 | } 51 | 52 | /** 53 | * {@inheritdoc} 54 | */ 55 | protected function getCipher() 56 | { 57 | return 'rijndael-128'; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/RandomLib/Source/Random.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The Random Random Number Source 14 | * 15 | * This uses the *nix /dev/random device to generate high strength numbers 16 | * 17 | * PHP version 5.3 18 | * 19 | * @category PHPCryptLib 20 | * @package Random 21 | * @subpackage Source 22 | * 23 | * @author Anthony Ferrara 24 | * @copyright 2011 The Authors 25 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 26 | * 27 | * @version Build @@version@@ 28 | */ 29 | namespace RandomLib\Source; 30 | 31 | use SecurityLib\Strength; 32 | 33 | /** 34 | * The Random Random Number Source 35 | * 36 | * This uses the *nix /dev/random device to generate high strength numbers 37 | * 38 | * @category PHPCryptLib 39 | * @package Random 40 | * @subpackage Source 41 | * 42 | * @author Anthony Ferrara 43 | * @codeCoverageIgnore 44 | */ 45 | class Random extends URandom 46 | { 47 | 48 | /** 49 | * @var string The file to read from 50 | */ 51 | protected static $file = '/dev/random'; 52 | 53 | /** 54 | * Return an instance of Strength indicating the strength of the source 55 | * 56 | * @return \SecurityLib\Strength An instance of one of the strength classes 57 | */ 58 | public static function getStrength() 59 | { 60 | return new Strength(Strength::HIGH); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test/bootstrap.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * Bootstrap the library. This registers a simple autoloader for autoloading 14 | * classes 15 | * 16 | * If you are using this library inside of another that uses a similar 17 | * autoloading system, you can use that autoloader instead of this file. 18 | * 19 | * PHP version 5.3 20 | * 21 | * @category PHPPasswordLib 22 | * @package test 23 | * 24 | * @author Anthony Ferrara 25 | * @copyright 2011 The Authors 26 | * @license http://opensource.org/licenses/bsd-license.php New BSD License 27 | * @license http://www.gnu.org/licenses/lgpl-2.1.html LGPL v 2.1 28 | */ 29 | namespace RandomLibTest; 30 | 31 | ini_set('memory_limit', '1G'); 32 | 33 | /** 34 | * The simple autoloader for the PasswordLibTest libraries. 35 | * 36 | * This does not use the PRS-0 standards due to the namespace prefix and directory 37 | * structure 38 | * 39 | * @param string $class The class name to load 40 | * 41 | * @return void 42 | */ 43 | spl_autoload_register(function ($class) { 44 | $nslen = strlen(__NAMESPACE__); 45 | if (substr($class, 0, $nslen) != __NAMESPACE__) { 46 | //Only autoload libraries from this package 47 | return; 48 | } 49 | $path = substr(str_replace('\\', '/', $class), $nslen); 50 | $path = __DIR__ . $path . '.php'; 51 | if (file_exists($path)) { 52 | require $path; 53 | } 54 | }); 55 | 56 | define('PATH_ROOT', dirname(__DIR__)); 57 | 58 | require_once dirname(__DIR__) . '/vendor/autoload.php'; 59 | -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | 7 | @copyright 2011 The Authors 8 | @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | @version Build @@version@@ 10 | EOF; 11 | 12 | Symfony\CS\Fixer\Contrib\HeaderCommentFixer::setHeader($header); 13 | 14 | return Symfony\CS\Config\Config::create() 15 | ->level(Symfony\CS\FixerInterface::PSR2_LEVEL) 16 | ->fixers([ 17 | 'align_double_arrow', 18 | 'array_element_no_space_before_comma', 19 | 'array_element_white_space_after_comma', 20 | 'declare_equal_normalize', 21 | 'extra_empty_lines', 22 | 'header_comment', 23 | 'list_commas', 24 | 'multiline_array_trailing_comma', 25 | 'new_with_braces', 26 | 'no_blank_lines_before_namespace', 27 | 'no_empty_comment', 28 | 'no_empty_lines_after_phpdocs', 29 | 'no_empty_phpdoc', 30 | 'no_empty_statement', 31 | 'object_operator', 32 | 'ordered_use', 33 | 'php_unit_dedicate_assert', 34 | 'phpdoc_indent', 35 | 'phpdoc_order', 36 | 'phpdoc_params', 37 | 'phpdoc_scalar', 38 | 'phpdoc_separation', 39 | 'remove_leading_slash_use', 40 | 'remove_lines_between_uses', 41 | 'return', 42 | 'self_accessor', 43 | 'short_bool_cast', 44 | 'short_scalar_cast', 45 | 'single_blank_line_before_namespace', 46 | 'spaces_before_semicolon', 47 | 'ternary_spaces', 48 | 'trim_array_spaces', 49 | 'unneeded_control_parentheses', 50 | 'unused_use', 51 | 'whitespacey_lines', 52 | ]) 53 | ->finder( 54 | Symfony\CS\Finder\DefaultFinder::create() 55 | ->in(__DIR__ . "/lib") 56 | ->in(__DIR__ . "/test") 57 | ) 58 | ; -------------------------------------------------------------------------------- /lib/RandomLib/Mixer.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The Mixer strategy interface. 14 | * 15 | * All mixing strategies must implement this interface 16 | * 17 | * PHP version 5.3 18 | * 19 | * @category PHPPasswordLib 20 | * @package Random 21 | * 22 | * @author Anthony Ferrara 23 | * @copyright 2011 The Authors 24 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 25 | * 26 | * @version Build @@version@@ 27 | */ 28 | namespace RandomLib; 29 | 30 | /** 31 | * The Mixer strategy interface. 32 | * 33 | * All mixing strategies must implement this interface 34 | * 35 | * @category PHPPasswordLib 36 | * @package Random 37 | * 38 | * @author Anthony Ferrara 39 | * @codeCoverageIgnore 40 | */ 41 | interface Mixer 42 | { 43 | 44 | /** 45 | * Return an instance of Strength indicating the strength of the mixer 46 | * 47 | * @return \SecurityLib\Strength An instance of one of the strength classes 48 | */ 49 | public static function getStrength(); 50 | 51 | /** 52 | * Test to see if the mixer is available 53 | * 54 | * @return bool If the mixer is available on the system 55 | */ 56 | public static function test(); 57 | 58 | /** 59 | * Mix the provided array of strings into a single output of the same size 60 | * 61 | * All elements of the array should be the same size. 62 | * 63 | * @param array $parts The parts to be mixed 64 | * 65 | * @return string The mixed result 66 | */ 67 | public function mix(array $parts); 68 | } 69 | -------------------------------------------------------------------------------- /lib/RandomLib/AbstractSource.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * PHP version 5.3 14 | * 15 | * @category PHPSecurityLib 16 | * @package Random 17 | * 18 | * @author Anthony Ferrara 19 | * @copyright 2011 The Authors 20 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 21 | * 22 | * @version Build @@version@@ 23 | */ 24 | namespace RandomLib; 25 | 26 | use SecurityLib\Strength; 27 | 28 | /** 29 | * An abstract mixer to implement a common mixing strategy 30 | * 31 | * @category PHPSecurityLib 32 | * @package Random 33 | */ 34 | abstract class AbstractSource implements \RandomLib\Source 35 | { 36 | 37 | /** 38 | * Return an instance of Strength indicating the strength of the source 39 | * 40 | * @return \SecurityLib\Strength An instance of one of the strength classes 41 | */ 42 | public static function getStrength() 43 | { 44 | return new Strength(Strength::VERYLOW); 45 | } 46 | 47 | /** 48 | * If the source is currently available. 49 | * Reasons might be because the library is not installed 50 | * 51 | * @return bool 52 | */ 53 | public static function isSupported() 54 | { 55 | return true; 56 | } 57 | 58 | /** 59 | * Returns a string of zeroes, useful when no entropy is available. 60 | * 61 | * @param int $size The size of the requested random string 62 | * 63 | * @return string A string of the requested size 64 | */ 65 | protected static function emptyValue($size) 66 | { 67 | return str_repeat(chr(0), $size); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /test/Unit/RandomLib/Mixer/HashTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | namespace RandomLib\Mixer; 12 | 13 | use SecurityLib\Strength; 14 | 15 | class HashTest extends \PHPUnit_Framework_TestCase 16 | { 17 | public static function provideMix() 18 | { 19 | $data = array( 20 | array(array(), ''), 21 | array(array('1', '1'), '0d'), 22 | array(array('a'), '61'), 23 | // This expects 'b' because of how the mock hmac function works 24 | array(array('a', 'b'), '9a'), 25 | array(array('aa', 'ba'), '6e84'), 26 | array(array('ab', 'bb'), 'b0cb'), 27 | array(array('aa', 'bb'), 'ae8d'), 28 | array(array('aa', 'bb', 'cc'), 'a14c'), 29 | array(array('aabbcc', 'bbccdd', 'ccddee'), 'a8aff3939934'), 30 | ); 31 | 32 | return $data; 33 | } 34 | 35 | public function testConstructWithoutArgument() 36 | { 37 | $hash = new Hash(); 38 | $this->assertTrue($hash instanceof \RandomLib\Mixer); 39 | } 40 | 41 | public function testGetStrength() 42 | { 43 | $strength = new Strength(Strength::MEDIUM); 44 | $actual = Hash::getStrength(); 45 | $this->assertEquals($actual, $strength); 46 | } 47 | 48 | public function testTest() 49 | { 50 | $actual = Hash::test(); 51 | $this->assertTrue($actual); 52 | } 53 | 54 | /** 55 | * @dataProvider provideMix 56 | */ 57 | public function testMix($parts, $result) 58 | { 59 | $mixer = new Hash('md5'); 60 | $actual = $mixer->mix($parts); 61 | $this->assertEquals($result, bin2hex($actual)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/Unit/RandomLib/Source/AbstractSourceTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | namespace RandomLib\Source; 12 | 13 | use SecurityLib\Strength; 14 | 15 | abstract class AbstractSourceTest extends \PHPUnit_Framework_TestCase 16 | { 17 | public function setUp() 18 | { 19 | $class = static::getTestedClass(); 20 | 21 | if (!$class::isSupported()) { 22 | $this->markTestSkipped(); 23 | } 24 | } 25 | 26 | protected static function getTestedClass() 27 | { 28 | return preg_replace('/Test$/', '', get_called_class()); 29 | } 30 | 31 | protected static function getExpectedStrength() 32 | { 33 | return new Strength(Strength::VERYLOW); 34 | } 35 | 36 | public static function provideGenerate() 37 | { 38 | $data = array(); 39 | for ($i = 0; $i < 100; $i += 5) { 40 | $not = $i > 0 ? str_repeat(chr(0), $i) : chr(0); 41 | $data[] = array($i, $not); 42 | } 43 | 44 | return $data; 45 | } 46 | 47 | public function testGetStrength() 48 | { 49 | $class = static::getTestedClass(); 50 | $strength = static::getExpectedStrength(); 51 | $actual = $class::getStrength(); 52 | $this->assertEquals($actual, $strength); 53 | } 54 | 55 | /** 56 | * @dataProvider provideGenerate 57 | * @group slow 58 | */ 59 | public function testGenerate($length, $not) 60 | { 61 | $class = static::getTestedClass(); 62 | $rand = new $class(); 63 | $stub = $rand->generate($length); 64 | $this->assertEquals($length, strlen($stub)); 65 | $this->assertNotEquals($not, $stub); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/RandomLib/Source.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The Random Number Source interface. 14 | * 15 | * All random number sources must implement this interface 16 | * 17 | * PHP version 5.3 18 | * 19 | * @category PHPPasswordLib 20 | * @package Random 21 | * 22 | * @author Anthony Ferrara 23 | * @copyright 2011 The Authors 24 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 25 | * 26 | * @version Build @@version@@ 27 | */ 28 | namespace RandomLib; 29 | 30 | /** 31 | * The Random Number Source interface. 32 | * 33 | * All random number sources must implement this interface 34 | * 35 | * @category PHPPasswordLib 36 | * @package Random 37 | * 38 | * @author Anthony Ferrara 39 | * @codeCoverageIgnore 40 | */ 41 | interface Source 42 | { 43 | 44 | /** 45 | * Return an instance of Strength indicating the strength of the source 46 | * 47 | * @return \SecurityLib\Strength An instance of one of the strength classes 48 | */ 49 | public static function getStrength(); 50 | 51 | /** 52 | * If the source is currently available. 53 | * Reasons might be because the library is not installed 54 | * 55 | * @return bool 56 | */ 57 | public static function isSupported(); 58 | 59 | /** 60 | * Generate a random string of the specified size 61 | * 62 | * Note: If the source fails to generate enough data, the result must be 63 | * padded to the requested length. 64 | * 65 | * @param int $size The size of the requested random string 66 | * 67 | * @return string A string of the requested size 68 | */ 69 | public function generate($size); 70 | } 71 | -------------------------------------------------------------------------------- /test/Unit/RandomLib/Mixer/McryptRijndael128Test.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | namespace RandomLib\Mixer; 12 | 13 | use SecurityLib\Strength; 14 | 15 | class McryptRijndael128Test extends \PHPUnit_Framework_TestCase 16 | { 17 | public static function provideMix() 18 | { 19 | $data = array( 20 | array(array(), ''), 21 | array(array('', ''), ''), 22 | array(array('a'), '61'), 23 | array(array('a', 'b'), '6a'), 24 | array(array('aa', 'ba'), '688d'), 25 | array(array('ab', 'bb'), 'f8bc'), 26 | array(array('aa', 'bb'), 'a0f3'), 27 | array(array('aa', 'bb', 'cc'), '87c3'), 28 | array(array('aabbcc', 'bbccdd', 'ccddee'), '7cf2273e46c7'), 29 | ); 30 | 31 | return $data; 32 | } 33 | 34 | protected function setUp() 35 | { 36 | if (!extension_loaded('mcrypt')) { 37 | $this->markTestSkipped('mcrypt extension is not available'); 38 | } 39 | } 40 | 41 | public function testConstructWithoutArgument() 42 | { 43 | $hash = new McryptRijndael128(); 44 | $this->assertTrue($hash instanceof \RandomLib\Mixer); 45 | } 46 | 47 | public function testGetStrength() 48 | { 49 | $strength = new Strength(Strength::HIGH); 50 | $actual = McryptRijndael128::getStrength(); 51 | $this->assertEquals($actual, $strength); 52 | } 53 | 54 | public function testTest() 55 | { 56 | $actual = McryptRijndael128::test(); 57 | $this->assertTrue($actual); 58 | } 59 | 60 | /** 61 | * @dataProvider provideMix 62 | */ 63 | public function testMix($parts, $result) 64 | { 65 | $mixer = new McryptRijndael128(); 66 | $actual = $mixer->mix($parts); 67 | $this->assertEquals($result, bin2hex($actual)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/RandomLib/Source/UniqID.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The UniqID Random Number Source 14 | * 15 | * This uses the internal `uniqid()` function to generate low strength random 16 | * numbers. 17 | * 18 | * PHP version 5.3 19 | * 20 | * @category PHPCryptLib 21 | * @package Random 22 | * @subpackage Source 23 | * 24 | * @author Anthony Ferrara 25 | * @copyright 2011 The Authors 26 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 27 | * 28 | * @version Build @@version@@ 29 | */ 30 | namespace RandomLib\Source; 31 | 32 | use SecurityLib\Strength; 33 | use SecurityLib\Util; 34 | 35 | /** 36 | * The UniqID Random Number Source 37 | * 38 | * This uses the internal `uniqid()` function to generate low strength random 39 | * numbers. 40 | * 41 | * @category PHPCryptLib 42 | * @package Random 43 | * @subpackage Source 44 | * 45 | * @author Anthony Ferrara 46 | * @codeCoverageIgnore 47 | */ 48 | class UniqID extends \RandomLib\AbstractSource 49 | { 50 | 51 | /** 52 | * Return an instance of Strength indicating the strength of the source 53 | * 54 | * @return \SecurityLib\Strength An instance of one of the strength classes 55 | */ 56 | public static function getStrength() 57 | { 58 | return new Strength(Strength::LOW); 59 | } 60 | 61 | /** 62 | * Generate a random string of the specified size 63 | * 64 | * @param int $size The size of the requested random string 65 | * 66 | * @return string A string of the requested size 67 | */ 68 | public function generate($size) 69 | { 70 | $result = ''; 71 | while (Util::safeStrlen($result) < $size) { 72 | $result = uniqid($result, true); 73 | } 74 | 75 | return Util::safeSubstr($result, 0, $size); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /test/Mocks/Random/Generator.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The Mixer strategy interface. 14 | * 15 | * All mixing strategies must implement this interface 16 | * 17 | * PHP version 5.3 18 | * 19 | * @category PHPPasswordLib 20 | * @package Random 21 | * 22 | * @author Anthony Ferrara 23 | * @copyright 2011 The Authors 24 | * @license http://opensource.org/licenses/bsd-license.php New BSD License 25 | * @license http://www.gnu.org/licenses/lgpl-2.1.html LGPL v 2.1 26 | */ 27 | namespace RandomLibtest\Mocks\Random; 28 | 29 | /** 30 | * The Mixer strategy interface. 31 | * 32 | * All mixing strategies must implement this interface 33 | * 34 | * @category PHPPasswordLib 35 | * @package Random 36 | * 37 | * @author Anthony Ferrara 38 | */ 39 | class Generator extends \RandomLib\Generator 40 | { 41 | protected $callbacks = array(); 42 | 43 | public static function init() 44 | { 45 | } 46 | 47 | public function __construct(array $callbacks = array()) 48 | { 49 | $this->callbacks = $callbacks; 50 | } 51 | 52 | public function __call($name, array $args = array()) 53 | { 54 | if (isset($this->callbacks[$name])) { 55 | return call_user_func_array($this->callbacks[$name], $args); 56 | } 57 | 58 | return null; 59 | } 60 | 61 | public function addSource(\PasswordLib\Random\Source $source) 62 | { 63 | return $this->__call('addSource', array($source)); 64 | } 65 | 66 | public function generate($size) 67 | { 68 | return $this->__call('generate', array($size)); 69 | } 70 | 71 | public function generateInt($min = 0, $max = \PHP_INT_MAX) 72 | { 73 | return $this->__call('generateInt', array($min, $max)); 74 | } 75 | 76 | public function generateString($length, $chars = '') 77 | { 78 | return $this->__call('generateString', array($length, $chars)); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/RandomLib/Source/RandomBytes.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The PHP7 Random Number Source 14 | * 15 | * This uses the inbuilt PHP7 Random Bytes function 16 | * 17 | * PHP version 5.3 18 | * 19 | * @category PHPCryptLib 20 | * @package Random 21 | * @subpackage Source 22 | * 23 | * @author Anthony Ferrara 24 | * @copyright 2011 The Authors 25 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 26 | * 27 | * @version Build @@version@@ 28 | */ 29 | namespace RandomLib\Source; 30 | 31 | use SecurityLib\Strength; 32 | 33 | /** 34 | * The PHP7 Random Number Source 35 | * 36 | * This uses the php7 secure generator to generate high strength numbers 37 | * 38 | * @category PHPCryptLib 39 | * @package Random 40 | * @subpackage Source 41 | * 42 | * @author Anthony Ferrara 43 | */ 44 | class RandomBytes extends \RandomLib\AbstractSource 45 | { 46 | 47 | /** 48 | * If the source is currently available. 49 | * Reasons might be because the library is not installed 50 | * 51 | * @return bool 52 | */ 53 | public static function isSupported() 54 | { 55 | return function_exists('random_bytes'); 56 | } 57 | 58 | /** 59 | * Return an instance of Strength indicating the strength of the source 60 | * 61 | * @return Strength An instance of one of the strength classes 62 | */ 63 | public static function getStrength() 64 | { 65 | return new Strength(Strength::HIGH); 66 | } 67 | 68 | /** 69 | * Generate a random string of the specified size 70 | * 71 | * @param int $size The size of the requested random string 72 | * 73 | * @return string A string of the requested size 74 | */ 75 | public function generate($size) 76 | { 77 | if (!self::isSupported()) { 78 | return str_repeat(chr(0), $size); 79 | } 80 | 81 | return \random_bytes($size); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /test/Mocks/Random/Mixer.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The Mixer strategy interface. 14 | * 15 | * All mixing strategies must implement this interface 16 | * 17 | * PHP version 5.3 18 | * 19 | * @category PHPPasswordLib 20 | * @package Random 21 | * 22 | * @author Anthony Ferrara 23 | * @copyright 2011 The Authors 24 | * @license http://opensource.org/licenses/bsd-license.php New BSD License 25 | * @license http://www.gnu.org/licenses/lgpl-2.1.html LGPL v 2.1 26 | */ 27 | namespace RandomLibtest\Mocks\Random; 28 | 29 | use SecurityLib\Strength; 30 | 31 | /** 32 | * The Mixer strategy interface. 33 | * 34 | * All mixing strategies must implement this interface 35 | * 36 | * @category PHPPasswordLib 37 | * @package Random 38 | * 39 | * @author Anthony Ferrara 40 | */ 41 | class Mixer extends \RandomLibTest\Mocks\AbstractMock implements \RandomLib\Mixer 42 | { 43 | public static $strength = null; 44 | 45 | public static $test = true; 46 | 47 | public static function init() 48 | { 49 | static::$strength = new Strength(Strength::HIGH); 50 | static::$test = true; 51 | } 52 | 53 | /** 54 | * Return an instance of Strength indicating the strength of the mixer 55 | * 56 | * @return \SecurityLib\Strength An instance of one of the strength classes 57 | */ 58 | public static function getStrength() 59 | { 60 | return static::$strength; 61 | } 62 | 63 | /** 64 | * Test to see if the mixer is available 65 | * 66 | * @return bool If the mixer is available on the system 67 | */ 68 | public static function test() 69 | { 70 | return static::$test; 71 | } 72 | 73 | /** 74 | * Mix the provided array of strings into a single output of the same size 75 | * 76 | * All elements of the array should be the same size. 77 | * 78 | * @param array $parts The parts to be mixed 79 | * 80 | * @return string The mixed result 81 | */ 82 | public function mix(array $parts) 83 | { 84 | return $this->__call('mix', array($parts)); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /test/Unit/RandomLib/FactoryTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | namespace RandomLib; 12 | 13 | use SecurityLib\Strength; 14 | 15 | class FactoryTest extends \PHPUnit_Framework_TestCase 16 | { 17 | public function testConstruct() 18 | { 19 | $factory = new Factory(); 20 | $this->assertTrue($factory instanceof Factory); 21 | } 22 | 23 | public function testGetGeneratorFallback() 24 | { 25 | $factory = new Factory(); 26 | $generator = $factory->getGenerator(new Strength(Strength::VERYLOW)); 27 | $mixer = call_user_func(array( 28 | get_class($generator->getMixer()), 29 | 'getStrength', 30 | )); 31 | $this->assertTrue($mixer->compare(new Strength(Strength::VERYLOW)) <= 0); 32 | } 33 | 34 | /** 35 | * @covers RandomLib\Factory::getMediumStrengthGenerator 36 | * @covers RandomLib\Factory::getGenerator 37 | * @covers RandomLib\Factory::findMixer 38 | * @covers RandomLib\Factory::findSources 39 | */ 40 | public function testGetMediumStrengthGenerator() 41 | { 42 | $factory = new Factory(); 43 | $generator = $factory->getMediumStrengthGenerator(); 44 | $this->assertTrue($generator instanceof Generator); 45 | $mixer = call_user_func(array( 46 | get_class($generator->getMixer()), 47 | 'getStrength', 48 | )); 49 | $this->assertTrue($mixer->compare(new Strength(Strength::MEDIUM)) <= 0); 50 | foreach ($generator->getSources() as $source) { 51 | $strength = call_user_func(array(get_class($source), 'getStrength')); 52 | $this->assertTrue($strength->compare(new Strength(Strength::MEDIUM)) >= 0); 53 | } 54 | } 55 | 56 | /** 57 | * @expectedException RuntimeException 58 | * @expectedExceptionMessage Could not find sources 59 | */ 60 | public function testNoAvailableSource() 61 | { 62 | $factory = new Factory(); 63 | $sources = new \ReflectionProperty($factory, 'sources'); 64 | $sources->setAccessible(true); 65 | $sources->setValue($factory, array()); 66 | $factory->getMediumStrengthGenerator(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /test/Unit/RandomLib/Source/SodiumTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | namespace RandomLib\Source; 12 | 13 | use SecurityLib\Strength; 14 | 15 | class SodiumTest extends \PHPUnit_Framework_TestCase 16 | { 17 | public static function provideGenerate() 18 | { 19 | $data = array(); 20 | for ($i = 1; $i < 100; $i += 5) { 21 | $not = str_repeat(chr(0), $i); 22 | $data[] = array($i, $not); 23 | } 24 | 25 | return $data; 26 | } 27 | 28 | 29 | public function testGetStrength() 30 | { 31 | $strength = new Strength(Strength::HIGH); 32 | $actual = Sodium::getStrength(); 33 | $this->assertEquals($actual, $strength); 34 | } 35 | 36 | /** 37 | * @dataProvider provideGenerate 38 | */ 39 | public function testGenerate($length, $not) 40 | { 41 | if (!extension_loaded('libsodium')) { 42 | $this->markTestSkipped('The libsodium extension is not loaded'); 43 | } 44 | 45 | $rand = new Sodium(); 46 | $stub = $rand->generate($length); 47 | $this->assertEquals($length, strlen($stub)); 48 | $this->assertNotEquals($not, $stub); 49 | } 50 | 51 | /** 52 | * @dataProvider provideGenerate 53 | */ 54 | public function testGenerateWithoutLibsodium($length, $not) 55 | { 56 | $rand = new Sodium(false); 57 | $stub = $rand->generate($length); 58 | $this->assertEquals($length, strlen($stub)); 59 | $this->assertEquals($not, $stub); 60 | } 61 | 62 | public function testGenerateWithZeroLength() 63 | { 64 | if (!extension_loaded('libsodium')) { 65 | $this->markTestSkipped('The libsodium extension is not loaded'); 66 | } 67 | 68 | $rand = new Sodium(); 69 | $stub = $rand->generate(0); 70 | $this->assertEquals(0, strlen($stub)); 71 | $this->assertEquals('', $stub); 72 | } 73 | 74 | public function testGenerateWithZeroLengthWithoutLibsodium() 75 | { 76 | $rand = new Sodium(false); 77 | $stub = $rand->generate(0); 78 | $this->assertEquals(0, strlen($stub)); 79 | $this->assertEquals('', $stub); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /test/Mocks/Random/Source.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The Random Number Source interface. 14 | * 15 | * All random number sources must implement this interface 16 | * 17 | * PHP version 5.3 18 | * 19 | * @category PHPPasswordLib 20 | * @package Random 21 | * 22 | * @author Anthony Ferrara 23 | * @copyright 2011 The Authors 24 | * @license http://opensource.org/licenses/bsd-license.php New BSD License 25 | * @license http://www.gnu.org/licenses/lgpl-2.1.html LGPL v 2.1 26 | */ 27 | namespace RandomLibtest\Mocks\Random; 28 | 29 | use SecurityLib\Strength; 30 | 31 | /** 32 | * The Random Number Source interface. 33 | * 34 | * All random number sources must implement this interface 35 | * 36 | * @category PHPPasswordLib 37 | * @package Random 38 | * 39 | * @author Anthony Ferrara 40 | */ 41 | class Source extends \RandomLibTest\Mocks\AbstractMock implements \RandomLib\Source 42 | { 43 | public static $strength = null; 44 | 45 | public static function init() 46 | { 47 | static::$strength = new Strength(Strength::VERYLOW); 48 | } 49 | 50 | /** 51 | * Return an instance of Strength indicating the strength of the source 52 | * 53 | * @return \SecurityLib\Strength An instance of one of the strength classes 54 | */ 55 | public static function getStrength() 56 | { 57 | return static::$strength; 58 | } 59 | 60 | /** 61 | * If the source is currently available. 62 | * Reasons might be because the library is not installed 63 | * 64 | * @return bool 65 | */ 66 | public static function isSupported() 67 | { 68 | return true; 69 | } 70 | 71 | /** 72 | * Generate a random string of the specified size 73 | * 74 | * Note: If the source fails to generate enough data, the result must be 75 | * padded to the requested length. 76 | * 77 | * @param int $size The size of the requested random string 78 | * 79 | * @return string A string of the requested size 80 | */ 81 | public function generate($size) 82 | { 83 | return $this->__call('generate', array($size)); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/RandomLib/Source/CAPICOM.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The Capicom Random Number Source 14 | * 15 | * This uses the Windows CapiCom Com object to generate random numbers 16 | * 17 | * PHP version 5.3 18 | * 19 | * @category PHPCryptLib 20 | * @package Random 21 | * @subpackage Source 22 | * 23 | * @author Anthony Ferrara 24 | * @copyright 2011 The Authors 25 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 26 | * 27 | * @version Build @@version@@ 28 | */ 29 | namespace RandomLib\Source; 30 | 31 | use SecurityLib\Strength; 32 | 33 | /** 34 | * The Capicom Random Number Source 35 | * 36 | * This uses the Windows CapiCom Com object to generate random numbers 37 | * 38 | * @category PHPCryptLib 39 | * @package Random 40 | * @subpackage Source 41 | * 42 | * @author Anthony Ferrara 43 | * @codeCoverageIgnore 44 | */ 45 | class CAPICOM extends \RandomLib\AbstractSource 46 | { 47 | 48 | /** 49 | * Return an instance of Strength indicating the strength of the source 50 | * 51 | * @return \SecurityLib\Strength An instance of one of the strength classes 52 | */ 53 | public static function getStrength() 54 | { 55 | return new Strength(Strength::MEDIUM); 56 | } 57 | 58 | /** 59 | * If the source is currently available. 60 | * Reasons might be because the library is not installed 61 | * 62 | * @return bool 63 | */ 64 | public static function isSupported() 65 | { 66 | return class_exists('\\COM', false); 67 | } 68 | 69 | /** 70 | * Generate a random string of the specified size 71 | * 72 | * @param int $size The size of the requested random string 73 | * 74 | * @return string A string of the requested size 75 | */ 76 | public function generate($size) 77 | { 78 | try { 79 | $util = new \COM('CAPICOM.Utilities.1'); 80 | $data = base64_decode($util->GetRandom($size, 0)); 81 | 82 | return str_pad($data, $size, chr(0)); 83 | } catch (\Exception $e) { 84 | unset($e); 85 | 86 | return static::emptyValue($size); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/RandomLib/Source/Rand.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The Rand Random Number Source 14 | * 15 | * This source generates low strength random numbers by using the internal 16 | * rand() function. By itself it is quite weak. However when combined with 17 | * other sources it does provide significant benefit. 18 | * 19 | * PHP version 5.3 20 | * 21 | * @category PHPCryptLib 22 | * @package Random 23 | * @subpackage Source 24 | * 25 | * @author Anthony Ferrara 26 | * @copyright 2011 The Authors 27 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 28 | * 29 | * @version Build @@version@@ 30 | */ 31 | namespace RandomLib\Source; 32 | 33 | use SecurityLib\Strength; 34 | 35 | /** 36 | * The Rand Random Number Source 37 | * 38 | * This source generates low strength random numbers by using the internal 39 | * rand() function. By itself it is quite weak. However when combined with 40 | * other sources it does provide significant benefit. 41 | * 42 | * @category PHPCryptLib 43 | * @package Random 44 | * @subpackage Source 45 | * 46 | * @author Anthony Ferrara 47 | * @codeCoverageIgnore 48 | */ 49 | class Rand extends \RandomLib\AbstractSource 50 | { 51 | 52 | /** 53 | * Return an instance of Strength indicating the strength of the source 54 | * 55 | * @return \SecurityLib\Strength An instance of one of the strength classes 56 | */ 57 | public static function getStrength() 58 | { 59 | // Detect if Suhosin Hardened PHP patch is applied 60 | if (defined('S_ALL')) { 61 | return new Strength(Strength::LOW); 62 | } else { 63 | return new Strength(Strength::VERYLOW); 64 | } 65 | } 66 | 67 | /** 68 | * Generate a random string of the specified size 69 | * 70 | * @param int $size The size of the requested random string 71 | * 72 | * @return string A string of the requested size 73 | */ 74 | public function generate($size) 75 | { 76 | $result = ''; 77 | for ($i = 0; $i < $size; $i++) { 78 | $result .= chr((rand() ^ rand()) % 256); 79 | } 80 | 81 | return $result; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/RandomLib/Source/MTRand.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The MTRand Random Number Source 14 | * 15 | * This source generates low strength random numbers by using the internal 16 | * mt_rand() function. By itself it is quite weak. However when combined with 17 | * other sources it does provide significant benefit. 18 | * 19 | * PHP version 5.3 20 | * 21 | * @category PHPCryptLib 22 | * @package Random 23 | * @subpackage Source 24 | * 25 | * @author Anthony Ferrara 26 | * @copyright 2011 The Authors 27 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 28 | * 29 | * @version Build @@version@@ 30 | */ 31 | namespace RandomLib\Source; 32 | 33 | use SecurityLib\Strength; 34 | 35 | /** 36 | * The MTRand Random Number Source 37 | * 38 | * This source generates low strength random numbers by using the internal 39 | * mt_rand() function. By itself it is quite weak. However when combined with 40 | * other sources it does provide significant benefit. 41 | * 42 | * @category PHPCryptLib 43 | * @package Random 44 | * @subpackage Source 45 | * 46 | * @author Anthony Ferrara 47 | * @codeCoverageIgnore 48 | */ 49 | class MTRand extends \RandomLib\AbstractSource 50 | { 51 | 52 | /** 53 | * Return an instance of Strength indicating the strength of the source 54 | * 55 | * @return \SecurityLib\Strength An instance of one of the strength classes 56 | */ 57 | public static function getStrength() 58 | { 59 | // Detect if Suhosin Hardened PHP patch is applied 60 | if (defined('S_ALL')) { 61 | return new Strength(Strength::MEDIUM); 62 | } else { 63 | return new Strength(Strength::LOW); 64 | } 65 | } 66 | 67 | /** 68 | * Generate a random string of the specified size 69 | * 70 | * @param int $size The size of the requested random string 71 | * 72 | * @return string A string of the requested size 73 | */ 74 | public function generate($size) 75 | { 76 | $result = ''; 77 | for ($i = 0; $i < $size; $i++) { 78 | $result .= chr((mt_rand() ^ mt_rand()) % 256); 79 | } 80 | 81 | return $result; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/RandomLib/Source/URandom.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The URandom Random Number Source 14 | * 15 | * This uses the *nix /dev/urandom device to generate medium strength numbers 16 | * 17 | * PHP version 5.3 18 | * 19 | * @category PHPCryptLib 20 | * @package Random 21 | * @subpackage Source 22 | * 23 | * @author Anthony Ferrara 24 | * @copyright 2011 The Authors 25 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 26 | * 27 | * @version Build @@version@@ 28 | */ 29 | namespace RandomLib\Source; 30 | 31 | use SecurityLib\Strength; 32 | 33 | /** 34 | * The URandom Random Number Source 35 | * 36 | * This uses the *nix /dev/urandom device to generate medium strength numbers 37 | * 38 | * @category PHPCryptLib 39 | * @package Random 40 | * @subpackage Source 41 | * 42 | * @author Anthony Ferrara 43 | * @codeCoverageIgnore 44 | */ 45 | class URandom extends \RandomLib\AbstractSource 46 | { 47 | 48 | /** 49 | * @var string The file to read from 50 | */ 51 | protected static $file = '/dev/urandom'; 52 | 53 | /** 54 | * Return an instance of Strength indicating the strength of the source 55 | * 56 | * @return \SecurityLib\Strength An instance of one of the strength classes 57 | */ 58 | public static function getStrength() 59 | { 60 | return new Strength(Strength::MEDIUM); 61 | } 62 | 63 | /** 64 | * If the source is currently available. 65 | * Reasons might be because the library is not installed 66 | * 67 | * @return bool 68 | */ 69 | public static function isSupported() 70 | { 71 | return @file_exists(static::$file); 72 | } 73 | 74 | /** 75 | * Generate a random string of the specified size 76 | * 77 | * @param int $size The size of the requested random string 78 | * 79 | * @return string A string of the requested size 80 | */ 81 | public function generate($size) 82 | { 83 | if ($size == 0) { 84 | return static::emptyValue($size); 85 | } 86 | $file = fopen(static::$file, 'rb'); 87 | if (!$file) { 88 | return static::emptyValue($size); 89 | } 90 | if (function_exists('stream_set_read_buffer')) { 91 | stream_set_read_buffer($file, 0); 92 | } 93 | $result = fread($file, $size); 94 | fclose($file); 95 | 96 | return $result; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/RandomLib/Mixer/XorMixer.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The Hash medium strength mixer class 14 | * 15 | * This class implements a mixer based upon the recommendations in RFC 4086 16 | * section 5.2 17 | * 18 | * PHP version 5.3 19 | * 20 | * @see http://tools.ietf.org/html/rfc4086#section-5.2 21 | * 22 | * @category PHPCryptLib 23 | * @package Random 24 | * @subpackage Mixer 25 | * 26 | * @author Anthony Ferrara 27 | * @copyright 2011 The Authors 28 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 29 | * 30 | * @version Build @@version@@ 31 | */ 32 | namespace RandomLib\Mixer; 33 | 34 | use SecurityLib\Strength; 35 | 36 | /** 37 | * The Hash medium strength mixer class 38 | * 39 | * This class implements a mixer based upon the recommendations in RFC 4086 40 | * section 5.2 41 | * 42 | * @see http://tools.ietf.org/html/rfc4086#section-5.2 43 | * 44 | * @category PHPCryptLib 45 | * @package Random 46 | * @subpackage Mixer 47 | * 48 | * @author Anthony Ferrara 49 | */ 50 | class XorMixer extends \RandomLib\AbstractMixer 51 | { 52 | 53 | /** 54 | * Return an instance of Strength indicating the strength of the source 55 | * 56 | * @return \SecurityLib\Strength An instance of one of the strength classes 57 | */ 58 | public static function getStrength() 59 | { 60 | return new Strength(Strength::VERYLOW); 61 | } 62 | 63 | /** 64 | * Test to see if the mixer is available 65 | * 66 | * @return bool If the mixer is available on the system 67 | */ 68 | public static function test() 69 | { 70 | return true; 71 | } 72 | 73 | /** 74 | * Get the block size (the size of the individual blocks used for the mixing) 75 | * 76 | * @return int The block size 77 | */ 78 | protected function getPartSize() 79 | { 80 | return 64; 81 | } 82 | 83 | /** 84 | * Mix 2 parts together using one method 85 | * 86 | * @param string $part1 The first part to mix 87 | * @param string $part2 The second part to mix 88 | * 89 | * @return string The mixed data 90 | */ 91 | protected function mixParts1($part1, $part2) 92 | { 93 | return $part1 ^ $part2; 94 | } 95 | 96 | /** 97 | * Mix 2 parts together using another different method 98 | * 99 | * @param string $part1 The first part to mix 100 | * @param string $part2 The second part to mix 101 | * 102 | * @return string The mixed data 103 | */ 104 | protected function mixParts2($part1, $part2) 105 | { 106 | // Both mixers are identical, this is for speed, not security 107 | return $part1 ^ $part2; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | filter: 2 | paths: 3 | - lib/* 4 | checks: 5 | php: 6 | code_rating: true 7 | duplication: true 8 | variable_existence: true 9 | useless_calls: true 10 | use_statement_alias_conflict: true 11 | unused_variables: true 12 | unused_properties: true 13 | unused_parameters: true 14 | unused_methods: true 15 | unreachable_code: true 16 | sql_injection_vulnerabilities: true 17 | security_vulnerabilities: true 18 | precedence_mistakes: true 19 | precedence_in_conditions: true 20 | parameter_non_unique: true 21 | no_property_on_interface: true 22 | no_non_implemented_abstract_methods: true 23 | deprecated_code_usage: true 24 | closure_use_not_conflicting: true 25 | closure_use_modifiable: true 26 | avoid_useless_overridden_methods: true 27 | avoid_conflicting_incrementers: true 28 | assignment_of_null_return: true 29 | verify_access_scope_valid: true 30 | verify_argument_usable_as_reference: true 31 | verify_property_names: true 32 | use_self_instead_of_fqcn: true 33 | uppercase_constants: true 34 | too_many_arguments: true 35 | spacing_of_function_arguments: true 36 | spacing_around_non_conditional_operators: true 37 | spacing_around_conditional_operators: true 38 | space_after_cast: true 39 | single_namespace_per_use: true 40 | scope_indentation: 41 | spaces_per_level: '4' 42 | return_doc_comments: true 43 | return_doc_comment_if_not_inferrable: true 44 | require_scope_for_properties: true 45 | require_scope_for_methods: true 46 | require_php_tag_first: true 47 | property_assignments: true 48 | properties_in_camelcaps: true 49 | php5_style_constructor: true 50 | parameters_in_camelcaps: true 51 | parameter_doc_comments: true 52 | param_doc_comment_if_not_inferrable: true 53 | optional_parameters_at_the_end: true 54 | one_class_per_file: true 55 | no_unnecessary_function_call_in_for_loop: true 56 | no_unnecessary_final_modifier: true 57 | no_trailing_whitespace: true 58 | no_space_inside_cast_operator: true 59 | no_space_before_semicolon: true 60 | no_short_open_tag: true 61 | no_commented_out_code: true 62 | newline_at_end_of_file: true 63 | missing_arguments: true 64 | lowercase_php_keywords: true 65 | lowercase_basic_constants: true 66 | function_in_camel_caps: true 67 | function_body_start_on_same_line: true 68 | ensure_lower_case_builtin_functions: true 69 | classes_in_camel_caps: true 70 | blank_line_after_namespace_declaration: true 71 | avoid_usage_of_logical_operators: true 72 | avoid_todo_comments: true 73 | avoid_tab_indentation: true 74 | avoid_superglobals: true 75 | avoid_fixme_comments: true 76 | avoid_corrupting_byteorder_marks: true 77 | avoid_duplicate_types: true 78 | avoid_closing_tag: true 79 | avoid_aliased_php_functions: true 80 | argument_type_checks: true 81 | -------------------------------------------------------------------------------- /lib/RandomLib/Source/Sodium.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The libsodium Random Number Source 14 | * 15 | * This uses the libsodium secure generator to generate high strength numbers 16 | * 17 | * PHP version 5.3 18 | * 19 | * @category PHPCryptLib 20 | * @package Random 21 | * @subpackage Source 22 | * 23 | * @author Anthony Ferrara 24 | * @author Ben Ramsey 25 | * @copyright 2011 The Authors 26 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 27 | * 28 | * @version Build @@version@@ 29 | * 30 | * @link https://paragonie.com/book/pecl-libsodium 31 | * @link http://pecl.php.net/package/libsodium 32 | */ 33 | namespace RandomLib\Source; 34 | 35 | use SecurityLib\Strength; 36 | 37 | /** 38 | * The libsodium Random Number Source 39 | * 40 | * This uses the libsodium secure generator to generate high strength numbers 41 | * 42 | * @category PHPCryptLib 43 | * @package Random 44 | * @subpackage Source 45 | * 46 | * @author Anthony Ferrara 47 | * @author Ben Ramsey 48 | */ 49 | class Sodium extends \RandomLib\AbstractSource 50 | { 51 | 52 | /** 53 | * A property that may be forcibly set to `false` in the constructor, for 54 | * the purpose of testing this source 55 | * 56 | * @var bool 57 | */ 58 | private $hasLibsodium = false; 59 | 60 | /** 61 | * Constructs a libsodium Random Number Source 62 | * 63 | * @param bool $useLibsodium May be set to `false` to disable libsodium for 64 | * testing purposes 65 | */ 66 | public function __construct($useLibsodium = true) 67 | { 68 | if ($useLibsodium && extension_loaded('libsodium')) { 69 | $this->hasLibsodium = true; 70 | } 71 | } 72 | 73 | /** 74 | * If the source is currently available. 75 | * Reasons might be because the library is not installed 76 | * 77 | * @return bool 78 | */ 79 | public static function isSupported() 80 | { 81 | return function_exists('Sodium\\randombytes_buf'); 82 | } 83 | 84 | /** 85 | * Return an instance of Strength indicating the strength of the source 86 | * 87 | * @return Strength An instance of one of the strength classes 88 | */ 89 | public static function getStrength() 90 | { 91 | return new Strength(Strength::HIGH); 92 | } 93 | 94 | /** 95 | * Generate a random string of the specified size 96 | * 97 | * @param int $size The size of the requested random string 98 | * 99 | * @return string A string of the requested size 100 | */ 101 | public function generate($size) 102 | { 103 | if (!$this->hasLibsodium || $size < 1) { 104 | return str_repeat(chr(0), $size); 105 | } 106 | 107 | return \Sodium\randombytes_buf($size); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /lib/RandomLib/Mixer/Hash.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The Hash medium strength mixer class 14 | * 15 | * This class implements a mixer based upon the recommendations in RFC 4086 16 | * section 5.2 17 | * 18 | * PHP version 5.3 19 | * 20 | * @see http://tools.ietf.org/html/rfc4086#section-5.2 21 | * 22 | * @category PHPCryptLib 23 | * @package Random 24 | * @subpackage Mixer 25 | * 26 | * @author Anthony Ferrara 27 | * @copyright 2011 The Authors 28 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 29 | * 30 | * @version Build @@version@@ 31 | */ 32 | namespace RandomLib\Mixer; 33 | 34 | use SecurityLib\Strength; 35 | use SecurityLib\Util; 36 | 37 | /** 38 | * The Hash medium strength mixer class 39 | * 40 | * This class implements a mixer based upon the recommendations in RFC 4086 41 | * section 5.2 42 | * 43 | * @see http://tools.ietf.org/html/rfc4086#section-5.2 44 | * 45 | * @category PHPCryptLib 46 | * @package Random 47 | * @subpackage Mixer 48 | * 49 | * @author Anthony Ferrara 50 | */ 51 | class Hash extends \RandomLib\AbstractMixer 52 | { 53 | 54 | /** 55 | * @var string The hash instance to use 56 | */ 57 | protected $hash = null; 58 | 59 | /** 60 | * Build the hash mixer 61 | * 62 | * @param string $hash The hash instance to use (defaults to sha512) 63 | * 64 | * @return void 65 | */ 66 | public function __construct($hash = 'sha512') 67 | { 68 | $this->hash = $hash; 69 | } 70 | 71 | /** 72 | * Return an instance of Strength indicating the strength of the source 73 | * 74 | * @return \SecurityLib\Strength An instance of one of the strength classes 75 | */ 76 | public static function getStrength() 77 | { 78 | return new Strength(Strength::MEDIUM); 79 | } 80 | 81 | /** 82 | * Test to see if the mixer is available 83 | * 84 | * @return bool If the mixer is available on the system 85 | */ 86 | public static function test() 87 | { 88 | return true; 89 | } 90 | 91 | /** 92 | * Get the block size (the size of the individual blocks used for the mixing) 93 | * 94 | * @return int The block size 95 | */ 96 | protected function getPartSize() 97 | { 98 | return Util::safeStrlen(hash($this->hash, '', true)); 99 | } 100 | 101 | /** 102 | * Mix 2 parts together using one method 103 | * 104 | * @param string $part1 The first part to mix 105 | * @param string $part2 The second part to mix 106 | * 107 | * @return string The mixed data 108 | */ 109 | protected function mixParts1($part1, $part2) 110 | { 111 | return hash_hmac($this->hash, $part1, $part2, true); 112 | } 113 | 114 | /** 115 | * Mix 2 parts together using another different method 116 | * 117 | * @param string $part1 The first part to mix 118 | * @param string $part2 The second part to mix 119 | * 120 | * @return string The mixed data 121 | */ 122 | protected function mixParts2($part1, $part2) 123 | { 124 | return hash_hmac($this->hash, $part2, $part1, true); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /lib/RandomLib/Source/OpenSSL.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The OpenSSL Random Number Source 14 | * 15 | * This uses the OS's secure generator to generate high strength numbers 16 | * 17 | * PHP version 5.3 18 | * 19 | * @category PHPCryptLib 20 | * @package Random 21 | * @subpackage Source 22 | * 23 | * @author Anthony Ferrara 24 | * @copyright 2011 The Authors 25 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 26 | * 27 | * @version Build @@version@@ 28 | */ 29 | namespace RandomLib\Source; 30 | 31 | use SecurityLib\Strength; 32 | 33 | /** 34 | * The OpenSSL Random Number Source 35 | * 36 | * This uses the OS's secure generator to generate high strength numbers 37 | * 38 | * @category PHPCryptLib 39 | * @package Random 40 | * @subpackage Source 41 | * 42 | * @author Anthony Ferrara 43 | * @codeCoverageIgnore 44 | */ 45 | class OpenSSL extends \RandomLib\AbstractSource 46 | { 47 | 48 | /** 49 | * Return an instance of Strength indicating the strength of the source 50 | * 51 | * @return \SecurityLib\Strength An instance of one of the strength classes 52 | */ 53 | public static function getStrength() 54 | { 55 | /** 56 | * Prior to PHP 5.6.12 (see https://bugs.php.net/bug.php?id=70014) the "openssl_random_pseudo_bytes" 57 | * was using "RAND_pseudo_bytes" (predictable) instead of "RAND_bytes" (unpredictable). 58 | * Release notes: http://php.net/ChangeLog-5.php#5.6.12 59 | */ 60 | if (PHP_VERSION_ID >= 50612) { 61 | return new Strength(Strength::HIGH); 62 | } 63 | 64 | /** 65 | * Prior to PHP 5.5.28 (see https://bugs.php.net/bug.php?id=70014) the "openssl_random_pseudo_bytes" 66 | * was using "RAND_pseudo_bytes" (predictable) instead of "RAND_bytes" (unpredictable). 67 | * Release notes: http://php.net/ChangeLog-5.php#5.5.28 68 | */ 69 | if (PHP_VERSION_ID >= 50528 && PHP_VERSION_ID < 50600) { 70 | return new Strength(Strength::HIGH); 71 | } 72 | 73 | /** 74 | * Prior to PHP 5.4.44 (see https://bugs.php.net/bug.php?id=70014) the "openssl_random_pseudo_bytes" 75 | * was using "RAND_pseudo_bytes" (predictable) instead of "RAND_bytes" (unpredictable). 76 | * Release notes: http://php.net/ChangeLog-5.php#5.4.44 77 | */ 78 | if (PHP_VERSION_ID >= 50444 && PHP_VERSION_ID < 50500) { 79 | return new Strength(Strength::HIGH); 80 | } 81 | 82 | return new Strength(Strength::MEDIUM); 83 | } 84 | 85 | /** 86 | * If the source is currently available. 87 | * Reasons might be because the library is not installed 88 | * 89 | * @return bool 90 | */ 91 | public static function isSupported() 92 | { 93 | return function_exists('openssl_random_pseudo_bytes'); 94 | } 95 | 96 | /** 97 | * Generate a random string of the specified size 98 | * 99 | * @param int $size The size of the requested random string 100 | * 101 | * @return string A string of the requested size 102 | */ 103 | public function generate($size) 104 | { 105 | if ($size < 1) { 106 | return str_repeat(chr(0), $size); 107 | } 108 | /** 109 | * Note, normally we would check the return of of $crypto_strong to 110 | * ensure that we generated a good random string. However, since we're 111 | * using this as one part of many sources a low strength random number 112 | * shouldn't be much of an issue. 113 | */ 114 | return openssl_random_pseudo_bytes($size); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /test/Unit/RandomLib/GeneratorStringTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | namespace RandomLib; 12 | 13 | class GeneratorStringTest extends \PHPUnit_Framework_TestCase 14 | { 15 | protected $generator = null; 16 | protected $mixer = null; 17 | protected $sources = array(); 18 | 19 | public static function provideCharCombinations() 20 | { 21 | return array( 22 | array("CHAR_LOWER", implode("", range("a", "z"))), 23 | array("CHAR_UPPER", implode("", range("A", "Z"))), 24 | array("CHAR_DIGITS", implode("", range(0, 9))), 25 | array("CHAR_UPPER_HEX", "0123456789ABCDEF"), 26 | array("CHAR_LOWER_HEX", "0123456789abcdef"), 27 | array("CHAR_BASE64", "+/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), 28 | array("EASY_TO_READ", "3479ACEFHJKLMNPRTUVWXYabcdefghijkmnopqrstuvwxyz"), 29 | array("CHAR_BRACKETS", "()<>[]{}"), 30 | array("CHAR_SYMBOLS", " !\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"), 31 | array("CHAR_PUNCT", ",.:;"), 32 | array("CHAR_ALPHA", implode("", array_merge(range("A", "Z"), range("a", "z")))), 33 | array("CHAR_ALNUM", implode("", array_merge(range(0, 9), range("A", "Z"), range("a", "z")))), 34 | array("CHAR_ALPHA | PUNCT", ",.:;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", Generator::CHAR_ALPHA | Generator::CHAR_PUNCT), 35 | array("CHAR_LOWER | EASY_TO_READ", "abcdefghijkmnopqrstuvwxyz", Generator::CHAR_LOWER | Generator::EASY_TO_READ), 36 | array("CHAR_DIGITS | EASY_TO_READ", "3479", Generator::CHAR_DIGITS | Generator::EASY_TO_READ), 37 | ); 38 | } 39 | 40 | public function setUp() 41 | { 42 | $source1 = $this->getMock('RandomLib\Source'); 43 | $source1->expects($this->any()) 44 | ->method('generate') 45 | ->will($this->returnCallback(function ($size) { 46 | $r = ''; 47 | for ($i = 0; $i < $size; $i++) { 48 | $r .= chr($i % 256); 49 | } 50 | 51 | return $r; 52 | } 53 | )); 54 | $source2 = $this->getMock('RandomLib\Source'); 55 | $source2->expects($this->any()) 56 | ->method('generate') 57 | ->will($this->returnCallback(function ($size) { 58 | $r = ''; 59 | for ($i = 0; $i < $size; $i++) { 60 | $r .= chr(0); 61 | } 62 | 63 | return $r; 64 | } 65 | )); 66 | 67 | $this->mixer = $this->getMock('RandomLib\Mixer'); 68 | $this->mixer->expects($this->any()) 69 | ->method('mix') 70 | ->will($this->returnCallback(function (array $sources) { 71 | if (empty($sources)) { 72 | return ''; 73 | } 74 | $start = array_pop($sources); 75 | 76 | return array_reduce( 77 | $sources, 78 | function ($el1, $el2) { 79 | return $el1 ^ $el2; 80 | }, 81 | $start 82 | ); 83 | })); 84 | 85 | $this->sources = array($source1, $source2); 86 | $this->generator = new Generator($this->sources, $this->mixer); 87 | } 88 | 89 | /** 90 | * @dataProvider provideCharCombinations 91 | */ 92 | public function testScheme($schemeName, $expected, $scheme = 0) 93 | { 94 | // test for overspecification by doubling the expected amount 95 | if (!$scheme) { 96 | $scheme = constant("RandomLib\Generator::$schemeName"); 97 | } 98 | $chars = $this->generator->generateString(strlen($expected) * 2, $scheme); 99 | $this->assertEquals($expected . $expected, $chars, sprintf("Testing Generator::%s failed", $schemeName)); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RandomLib 2 | ========= 3 | 4 | [![Build Status](https://travis-ci.org/ircmaxell/RandomLib.svg?branch=master)](https://travis-ci.org/ircmaxell/RandomLib) 5 | 6 | A library for generating random numbers and strings of various strengths. 7 | 8 | This library is useful in security contexts. 9 | 10 | Install 11 | ------- 12 | 13 | Via Composer 14 | 15 | ```sh 16 | $ composer require ircmaxell/random-lib 17 | ``` 18 | 19 | Usage 20 | ----- 21 | 22 | ### Factory 23 | 24 | A factory is used to get generators of varying strength: 25 | 26 | ```php 27 | $factory = new RandomLib\Factory; 28 | $generator = $factory->getGenerator(new SecurityLib\Strength(SecurityLib\Strength::MEDIUM)); 29 | ``` 30 | 31 | A factory can be configured with additional mixers and sources but can be 32 | used out of the box to create both medium and low strength generators. 33 | 34 | Convenience methods are provided for creating high, medium, and low 35 | strength generators. Example: 36 | 37 | ```php 38 | $generator = $factory->getMediumStrengthGenerator(); 39 | ``` 40 | 41 | #### $factory->getLowStrengthGenerator() 42 | 43 | Convenience method to get a low strength random number generator. 44 | 45 | Low Strength should be used anywhere that random strings are needed in a 46 | non-cryptographical setting. They are not strong enough to be used as 47 | keys or salts. They are however useful for one-time use tokens. 48 | 49 | 50 | #### $factory->getMediumStrengthGenerator() 51 | 52 | Convenience method to get a medium strength random number generator. 53 | 54 | Medium Strength should be used for most needs of a cryptographic nature. 55 | They are strong enough to be used as keys and salts. However, they do 56 | take some time and resources to generate, so they should not be over-used 57 | 58 | 59 | #### $factory->getHighStrengthGenerator() 60 | 61 | Convenience method to get a high strength random number generator. 62 | 63 | High Strength keys should ONLY be used for generating extremely strong 64 | cryptographic keys. Generating them is very resource intensive and may 65 | take several minutes or more depending on the requested size. 66 | 67 | **There are currently no mixers shipped with this package that are 68 | capable of creating a high space generator. This will not work out of 69 | the box!** 70 | 71 | 72 | ### Generator 73 | 74 | A generator is used to generate random numbers and strings. 75 | 76 | Example: 77 | 78 | ```php 79 | // Generate a random string that is 32 bytes in length. 80 | $bytes = $generator->generate(32); 81 | 82 | // Generate a whole number between 5 and 15. 83 | $randomInt = $generator->generateInt(5, 15); 84 | 85 | // Generate a 32 character string that only contains the letters 86 | // 'a', 'b', 'c', 'd', 'e', and 'f'. 87 | $randomString = $generator->generateString(32, 'abcdef'); 88 | ``` 89 | 90 | #### $generator->generate($size) 91 | 92 | Generate a random byte string of the requested size. 93 | 94 | 95 | #### $generator->generateInt($min = 0, $max = PHP_INT_MAX) 96 | 97 | Generate a random integer with the given range. If range (`$max - $min`) 98 | is zero, `$max` will be used. 99 | 100 | 101 | #### $generator->generateString($length, $characters = '') 102 | 103 | Generate a random string of specified length. 104 | 105 | This uses the supplied character list for generating the new result 106 | string. The list of characters should be specified as a string containing 107 | each allowed character. 108 | 109 | If no character list is specified, the following list of characters is used: 110 | 111 | 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/ 112 | 113 | **Examples:** 114 | 115 | ```php 116 | // Give the character list 'abcdef': 117 | print $generator->generateString(32, 'abcdef')."\n"; 118 | 119 | // One would expect to receive output that only contained those 120 | // characters: 121 | // 122 | // adaeabecfbddcdaeedaedfbbcdccccfe 123 | // adfbfdbfddadbfcbbefebcacbefafffa 124 | // ceeadbcabecbccacdcaabbdccfadbafe 125 | // abadcffabdcacdbcbafcaecabafcdbbf 126 | // dbdbddacdeaceabfaefcbfafebcacdca 127 | ``` 128 | 129 | License 130 | ------- 131 | 132 | MIT, see LICENSE. 133 | 134 | 135 | Community 136 | --------- 137 | 138 | If you have questions or want to help out, join us in the **#php.security** 139 | channel on **irc.freenode.net**. 140 | 141 | Security Vulnerabilities 142 | ======================== 143 | 144 | If you have found a security issue, please contact the author directly at [me@ircmaxell.com](mailto:me@ircmaxell.com). 145 | -------------------------------------------------------------------------------- /lib/RandomLib/AbstractMcryptMixer.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The Mcrypt abstract mixer class 14 | * 15 | * PHP version 5.3 16 | * 17 | * @category PHPCryptLib 18 | * @package Random 19 | * @subpackage Mixer 20 | * 21 | * @author Anthony Ferrara 22 | * @copyright 2013 The Authors 23 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 24 | * 25 | * @version Build @@version@@ 26 | */ 27 | namespace RandomLib; 28 | 29 | /** 30 | * The mcrypt abstract mixer class 31 | * 32 | * @category PHPCryptLib 33 | * @package Random 34 | * @subpackage Mixer 35 | * 36 | * @author Anthony Ferrara 37 | * @author Chris Smith 38 | */ 39 | abstract class AbstractMcryptMixer extends AbstractMixer 40 | { 41 | /** 42 | * mcrypt module resource 43 | * 44 | * @var resource 45 | */ 46 | private $mcrypt; 47 | 48 | /** 49 | * Block size of cipher 50 | * 51 | * @var int 52 | */ 53 | private $blockSize; 54 | 55 | /** 56 | * Cipher initialization vector 57 | * 58 | * @var string 59 | */ 60 | private $initv; 61 | 62 | /** 63 | * {@inheritdoc} 64 | */ 65 | public static function test() 66 | { 67 | return extension_loaded('mcrypt'); 68 | } 69 | 70 | /** 71 | * Construct mcrypt mixer 72 | */ 73 | public function __construct() 74 | { 75 | $this->mcrypt = mcrypt_module_open($this->getCipher(), '', MCRYPT_MODE_ECB, ''); 76 | $this->blockSize = mcrypt_enc_get_block_size($this->mcrypt); 77 | $this->initv = str_repeat(chr(0), mcrypt_enc_get_iv_size($this->mcrypt)); 78 | } 79 | 80 | /** 81 | * Performs cleanup 82 | */ 83 | public function __destruct() 84 | { 85 | if ($this->mcrypt) { 86 | mcrypt_module_close($this->mcrypt); 87 | } 88 | } 89 | 90 | /** 91 | * Fetch the cipher for mcrypt. 92 | * 93 | * @return string 94 | */ 95 | abstract protected function getCipher(); 96 | 97 | /** 98 | * {@inheritdoc} 99 | */ 100 | protected function getPartSize() 101 | { 102 | return $this->blockSize; 103 | } 104 | 105 | /** 106 | * {@inheritdoc} 107 | */ 108 | protected function mixParts1($part1, $part2) 109 | { 110 | return $this->encryptBlock($part1, $part2); 111 | } 112 | 113 | /** 114 | * {@inheritdoc} 115 | */ 116 | protected function mixParts2($part1, $part2) 117 | { 118 | return $this->decryptBlock($part2, $part1); 119 | } 120 | 121 | /** 122 | * Encrypts a block using the suppied key 123 | * 124 | * @param string $input Plaintext to encrypt 125 | * @param string $key Encryption key 126 | * 127 | * @return string Resulting ciphertext 128 | */ 129 | private function encryptBlock($input, $key) 130 | { 131 | if (!$input && !$key) { 132 | return ''; 133 | } 134 | 135 | $this->prepareCipher($key); 136 | $result = mcrypt_generic($this->mcrypt, $input); 137 | mcrypt_generic_deinit($this->mcrypt); 138 | 139 | return $result; 140 | } 141 | 142 | /** 143 | * Derypts a block using the suppied key 144 | * 145 | * @param string $input Ciphertext to decrypt 146 | * @param string $key Encryption key 147 | * 148 | * @return string Resulting plaintext 149 | */ 150 | private function decryptBlock($input, $key) 151 | { 152 | if (!$input && !$key) { 153 | return ''; 154 | } 155 | 156 | $this->prepareCipher($key); 157 | $result = mdecrypt_generic($this->mcrypt, $input); 158 | mcrypt_generic_deinit($this->mcrypt); 159 | 160 | return $result; 161 | } 162 | 163 | /** 164 | * Sets up the mcrypt module 165 | * 166 | * @param string $key 167 | * 168 | * @return void 169 | */ 170 | private function prepareCipher($key) 171 | { 172 | if (0 !== mcrypt_generic_init($this->mcrypt, $key, $this->initv)) { 173 | throw new \RuntimeException('Failed to prepare mcrypt module'); 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /lib/RandomLib/Source/MicroTime.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The Microtime Random Number Source 14 | * 15 | * This uses the current micro-second (looped several times) for a **very** weak 16 | * random number source. This is only useful when combined with several other 17 | * stronger sources 18 | * 19 | * PHP version 5.3 20 | * 21 | * @category PHPCryptLib 22 | * @package Random 23 | * @subpackage Source 24 | * 25 | * @author Anthony Ferrara 26 | * @copyright 2011 The Authors 27 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 28 | * 29 | * @version Build @@version@@ 30 | */ 31 | namespace RandomLib\Source; 32 | 33 | use SecurityLib\Strength; 34 | use SecurityLib\Util; 35 | 36 | /** 37 | * The Microtime Random Number Source 38 | * 39 | * This uses the current micro-second (looped several times) for a **very** weak 40 | * random number source. This is only useful when combined with several other 41 | * stronger sources 42 | * 43 | * @category PHPCryptLib 44 | * @package Random 45 | * @subpackage Source 46 | * 47 | * @author Anthony Ferrara 48 | * @codeCoverageIgnore 49 | */ 50 | final class MicroTime extends \RandomLib\AbstractSource 51 | { 52 | 53 | /** 54 | * A static counter to ensure unique hashes and prevent state collisions 55 | * 56 | * @var int A counter 57 | */ 58 | private static $counter = null; 59 | 60 | /** 61 | * The current state of the random number generator. 62 | * 63 | * @var string The state of the PRNG 64 | */ 65 | private static $state = ''; 66 | 67 | public function __construct() 68 | { 69 | $state = self::$state; 70 | if (function_exists('posix_times')) { 71 | $state .= serialize(posix_times()); 72 | } 73 | if (!defined('HHVM_VERSION') && function_exists('zend_thread_id')) { 74 | $state .= zend_thread_id(); 75 | } 76 | if (function_exists('hphp_get_thread_id')) { 77 | $state .= hphp_get_thread_id(); 78 | } 79 | $state .= getmypid() . memory_get_usage(); 80 | $state .= serialize($_ENV); 81 | $state .= serialize($_SERVER); 82 | $state .= count(debug_backtrace(false)); 83 | self::$state = hash('sha512', $state, true); 84 | if (is_null(self::$counter)) { 85 | list(, self::$counter) = unpack("i", Util::safeSubstr(self::$state, 0, 4)); 86 | $seed = $this->generate(Util::safeStrlen(dechex(PHP_INT_MAX))); 87 | list(, self::$counter) = unpack("i", $seed); 88 | } 89 | } 90 | 91 | /** 92 | * Generate a random string of the specified size 93 | * 94 | * @param int $size The size of the requested random string 95 | * 96 | * @return string A string of the requested size 97 | */ 98 | public function generate($size) 99 | { 100 | $result = ''; 101 | $seed = microtime() . memory_get_usage(); 102 | self::$state = hash('sha512', self::$state . $seed, true); 103 | /** 104 | * Make the generated randomness a bit better by forcing a GC run which 105 | * should complete in a indeterminate amount of time, hence improving 106 | * the strength of the randomness a bit. It's still not crypto-safe, 107 | * but at least it's more difficult to predict. 108 | */ 109 | gc_collect_cycles(); 110 | for ($i = 0; $i < $size; $i += 8) { 111 | $seed = self::$state . 112 | microtime() . 113 | pack('Ni', $i, self::counter()); 114 | self::$state = hash('sha512', $seed, true); 115 | /** 116 | * We only use the first 8 bytes here to prevent exposing the state 117 | * in its entirety, which could potentially expose other random 118 | * generations in the future (in the same process)... 119 | */ 120 | $result .= Util::safeSubstr(self::$state, 0, 8); 121 | } 122 | 123 | return Util::safeSubstr($result, 0, $size); 124 | } 125 | 126 | private static function counter() 127 | { 128 | if (self::$counter >= PHP_INT_MAX) { 129 | self::$counter = -1 * PHP_INT_MAX - 1; 130 | } else { 131 | self::$counter++; 132 | } 133 | 134 | return self::$counter; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /lib/RandomLib/AbstractMixer.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * An abstract mixer to implement a common mixing strategy 14 | * 15 | * PHP version 5.3 16 | * 17 | * @category PHPSecurityLib 18 | * @package Random 19 | * 20 | * @author Anthony Ferrara 21 | * @copyright 2011 The Authors 22 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 23 | * 24 | * @version Build @@version@@ 25 | */ 26 | namespace RandomLib; 27 | 28 | use SecurityLib\Util; 29 | 30 | /** 31 | * An abstract mixer to implement a common mixing strategy 32 | * 33 | * @see http://tools.ietf.org/html/rfc4086#section-5.2 34 | * 35 | * @category PHPSecurityLib 36 | * @package Random 37 | * 38 | * @author Anthony Ferrara 39 | */ 40 | abstract class AbstractMixer implements \RandomLib\Mixer 41 | { 42 | 43 | /** 44 | * Get the block size (the size of the individual blocks used for the mixing) 45 | * 46 | * @return int The block size 47 | */ 48 | abstract protected function getPartSize(); 49 | 50 | /** 51 | * Mix 2 parts together using one method 52 | * 53 | * @param string $part1 The first part to mix 54 | * @param string $part2 The second part to mix 55 | * 56 | * @return string The mixed data 57 | */ 58 | abstract protected function mixParts1($part1, $part2); 59 | 60 | /** 61 | * Mix 2 parts together using another different method 62 | * 63 | * @param string $part1 The first part to mix 64 | * @param string $part2 The second part to mix 65 | * 66 | * @return string The mixed data 67 | */ 68 | abstract protected function mixParts2($part1, $part2); 69 | 70 | /** 71 | * Mix the provided array of strings into a single output of the same size 72 | * 73 | * All elements of the array should be the same size. 74 | * 75 | * @param array $parts The parts to be mixed 76 | * 77 | * @return string The mixed result 78 | */ 79 | public function mix(array $parts) 80 | { 81 | if (empty($parts)) { 82 | return ''; 83 | } 84 | $len = Util::safeStrlen($parts[0]); 85 | $parts = $this->normalizeParts($parts); 86 | $stringSize = count($parts[0]); 87 | $partsSize = count($parts); 88 | $result = ''; 89 | $offset = 0; 90 | for ($i = 0; $i < $stringSize; $i++) { 91 | $stub = $parts[$offset][$i]; 92 | for ($j = 1; $j < $partsSize; $j++) { 93 | $newKey = $parts[($j + $offset) % $partsSize][$i]; 94 | //Alternately mix the output for each source 95 | if ($j % 2 == 1) { 96 | $stub ^= $this->mixParts1($stub, $newKey); 97 | } else { 98 | $stub ^= $this->mixParts2($stub, $newKey); 99 | } 100 | } 101 | $result .= $stub; 102 | $offset = ($offset + 1) % $partsSize; 103 | } 104 | 105 | return Util::safeSubstr($result, 0, $len); 106 | } 107 | 108 | /** 109 | * Normalize the part array and split it block part size. 110 | * 111 | * This will make all parts the same length and a multiple 112 | * of the part size 113 | * 114 | * @param array $parts The parts to normalize 115 | * 116 | * @return array The normalized and split parts 117 | */ 118 | protected function normalizeParts(array $parts) 119 | { 120 | $blockSize = $this->getPartSize(); 121 | $callback = function ($value) { 122 | return Util::safeStrlen($value); 123 | }; 124 | $maxSize = max(array_map($callback, $parts)); 125 | if ($maxSize % $blockSize != 0) { 126 | $maxSize += $blockSize - ($maxSize % $blockSize); 127 | } 128 | foreach ($parts as &$part) { 129 | $part = $this->str_pad($part, $maxSize, chr(0)); 130 | $part = str_split($part, $blockSize); 131 | } 132 | 133 | return $parts; 134 | } 135 | 136 | private function str_pad($string, $size, $character) 137 | { 138 | $start = Util::safeStrlen($string); 139 | $inc = Util::safeStrlen($character); 140 | for ($i = $start; $i < $size; $i+= $inc) { 141 | $string = $string . $character; 142 | } 143 | 144 | return Util::safeSubstr($string, 0, $size); 145 | } 146 | 147 | private function str_split($string, $size) 148 | { 149 | $blocks = array(); 150 | $length = Util::safeStrlen($string); 151 | $parts = ceil($length / $size); 152 | for ($i = 0; $i < $parts; $i++) { 153 | $blocks[] = Util::safeSubstr($string, $i * $length, $length); 154 | } 155 | 156 | return $blocks; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /test/Unit/RandomLib/GeneratorTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | namespace RandomLib; 12 | 13 | class GeneratorTest extends \PHPUnit_Framework_TestCase 14 | { 15 | protected $generator = null; 16 | protected $mixer = null; 17 | protected $sources = array(); 18 | 19 | public static function provideGenerate() 20 | { 21 | return array( 22 | array(0, ''), 23 | array(1, chr(0)), 24 | array(2, chr(1) . chr(1)), 25 | array(3, chr(2) . chr(0) . chr(2)), 26 | array(4, chr(3) . chr(3) . chr(3) . chr(3)), 27 | ); 28 | } 29 | 30 | public static function provideGenerateInt() 31 | { 32 | return array( 33 | array(1, 1, 1), 34 | array(0, 1, 0), 35 | array(0, 255, 0), 36 | array(400, 655, 400), 37 | array(0, 65535, 257), 38 | array(65535, 131070, 65792), 39 | array(0, 16777215, (2<<16) + 2), 40 | array(-10, 0, -10), 41 | array(-655, -400, -655), 42 | array(-131070, -65535, -130813), 43 | ); 44 | } 45 | 46 | public static function provideGenerateIntRangeTest() 47 | { 48 | return array( 49 | array(0, 0), 50 | array(0, 1), 51 | array(1, 10000), 52 | array(100000, \PHP_INT_MAX), 53 | ); 54 | } 55 | 56 | public static function provideGenerateStringTest() 57 | { 58 | return array( 59 | array(0, 'ab', ''), 60 | array(1, 'ab', 'a'), 61 | array(1, 'a', ''), 62 | array(2, 'ab', 'bb'), 63 | array(3, 'abc', 'cac'), 64 | array(8, '0123456789abcdef', '77777777'), 65 | array(16, '0123456789abcdef', 'ffffffffffffffff'), 66 | array(16, '', 'DDDDDDDDDDDDDDDD'), 67 | ); 68 | } 69 | 70 | public function setUp() 71 | { 72 | $source1 = $this->getMock('RandomLib\Source'); 73 | $source1->expects($this->any()) 74 | ->method('generate') 75 | ->will($this->returnCallback(function ($size) { 76 | $r = ''; 77 | for ($i = 0; $i < $size; $i++) { 78 | $r .= chr($i); 79 | } 80 | 81 | return $r; 82 | } 83 | )); 84 | $source2 = $this->getMock('RandomLib\Source'); 85 | $source2->expects($this->any()) 86 | ->method('generate') 87 | ->will($this->returnCallback(function ($size) { 88 | $r = ''; 89 | for ($i = $size - 1; $i >= 0; $i--) { 90 | $r .= chr($i); 91 | } 92 | 93 | return $r; 94 | } 95 | )); 96 | 97 | $this->mixer = $this->getMock('RandomLib\Mixer'); 98 | $this->mixer->expects($this->any()) 99 | ->method('mix') 100 | ->will($this->returnCallback(function (array $sources) { 101 | if (empty($sources)) { 102 | return ''; 103 | } 104 | $start = array_pop($sources); 105 | 106 | return array_reduce( 107 | $sources, 108 | function ($el1, $el2) { 109 | return $el1 ^ $el2; 110 | }, 111 | $start 112 | ); 113 | })); 114 | 115 | $this->sources = array($source1, $source2); 116 | $this->generator = new Generator($this->sources, $this->mixer); 117 | } 118 | 119 | public function testConstruct() 120 | { 121 | $this->assertTrue($this->generator instanceof Generator); 122 | } 123 | 124 | public function testGetMixer() 125 | { 126 | $this->assertSame($this->mixer, $this->generator->getMixer()); 127 | } 128 | 129 | public function testGetSources() 130 | { 131 | $this->assertSame($this->sources, $this->generator->getSources()); 132 | } 133 | 134 | /** 135 | * @dataProvider provideGenerate 136 | */ 137 | public function testGenerate($size, $expect) 138 | { 139 | $this->assertEquals($expect, $this->generator->generate($size)); 140 | } 141 | 142 | /** 143 | * @dataProvider provideGenerateInt 144 | */ 145 | public function testGenerateInt($min, $max, $expect) 146 | { 147 | $this->assertEquals($expect, $this->generator->generateInt($min, $max)); 148 | } 149 | 150 | /** 151 | * @dataProvider provideGenerateIntRangeTest 152 | */ 153 | public function testGenerateIntRange($min, $max) 154 | { 155 | $n = $this->generator->generateInt($min, $max); 156 | $this->assertTrue($min <= $n); 157 | $this->assertTrue($max >= $n); 158 | } 159 | 160 | /** 161 | * @expectedException RangeException 162 | */ 163 | public function testGenerateIntFail() 164 | { 165 | $n = $this->generator->generateInt(-1, PHP_INT_MAX); 166 | } 167 | 168 | 169 | public function testGenerateIntLargeTest() 170 | { 171 | $bits = 30; 172 | $expected = 50529027; 173 | if (PHP_INT_MAX > 4000000000) { 174 | $bits = 55; 175 | $expected = 1693273676973062; 176 | } 177 | $n = $this->generator->generateInt(0, (int) pow(2, $bits)); 178 | $this->assertEquals($expected, $n); 179 | } 180 | 181 | /** 182 | * @dataProvider provideGenerateStringTest 183 | */ 184 | public function testGenerateString($length, $chars, $expected) 185 | { 186 | $n = $this->generator->generateString($length, $chars); 187 | $this->assertEquals($expected, $n); 188 | } 189 | 190 | /** 191 | * This test checks for issue #22: 192 | * 193 | * @see https://github.com/ircmaxell/RandomLib/issues/22 194 | */ 195 | public function testGenerateLargeRange() 196 | { 197 | if (PHP_INT_MAX < pow(2, 32)) { 198 | $this->markTestSkipped("Only test on 64 bit platforms"); 199 | } 200 | $this->assertEquals(506381209866536711, $this->generator->generateInt(0, PHP_INT_MAX)); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /test/Vectors/Random/GeneratorTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | use RandomLib\Generator; 13 | use RandomLibTest\Mocks\Random\Mixer; 14 | use RandomLibTest\Mocks\Random\Source; 15 | 16 | class Vectors_Random_GeneratorTest extends PHPUnit_Framework_TestCase 17 | { 18 | public static function provideGenerateInt() 19 | { 20 | return array( 21 | // First, lets test each offset based range 22 | array(0, 7), 23 | array(0, 15), 24 | array(0, 31), 25 | array(0, 63), 26 | array(0, 127), 27 | array(0, 255), 28 | array(0, 511), 29 | array(0, 1023), 30 | // Let's try a range not starting at 0 31 | array(8, 15), 32 | // Let's try a range with a negative number 33 | array(-18, -11), 34 | // Let's try a non-power-of-2 range 35 | array(10, 100), 36 | // Finally, let's try two large numbers 37 | array(100000, 100007), 38 | array(100000000, 100002047), 39 | // Now, let's force a few loops by setting a valid offset 40 | array(0, 5, 2), 41 | array(0, 9, 5), 42 | array(0, 27, 4), 43 | ); 44 | } 45 | 46 | public static function provideGenerators() 47 | { 48 | $factory = new \RandomLib\Factory(); 49 | $generator = $factory->getLowStrengthGenerator(); 50 | $sources = $generator->getSources(); 51 | $ret = array(); 52 | 53 | $ret[] = array(new Generator($sources, new \RandomLib\Mixer\Hash()), 10000, 'hash'); 54 | 55 | return $ret; 56 | } 57 | 58 | /** 59 | * This test asserts that the algorithm that generates the integers does not 60 | * actually introduce any bias into the generated numbers. If this test 61 | * passes, the generated integers from the generator will be as unbiased as 62 | * the sources that provide the data. 63 | * 64 | * @dataProvider provideGenerateInt 65 | */ 66 | public function testGenerateInt($min, $max, $offset = 0) 67 | { 68 | $generator = $this->getGenerator($max - $min + $offset); 69 | for ($i = $max; $i >= $min; $i--) { 70 | $this->assertEquals($i, $generator->generateInt($min, $max)); 71 | } 72 | } 73 | 74 | /** 75 | * This generator generates two bytes at a time, and uses each 8 bit segment of 76 | * the generated byte as a coordinate on a grid (so 01011010 would be the 77 | * coordinate (0101, 1010) or (5, 10). These are used as inputs to a MonteCarlo 78 | * algorithm for the integral of y=x over a 15x15 grid. The expected answer is 79 | * 1/2 * 15 * 15 (or 1/2 * base * height, since the result is a triangle). 80 | * Therefore, if we get an answer close to that, we know the generator is good. 81 | * 82 | * Now, since the area under the line should be equal to the area above the line. 83 | * Therefore, the ratio of the two areas should be equal. This way, we can avoid 84 | * computing total to figure out the areas. 85 | * 86 | * I have set the bounds on the test to be 80% and 120%. Meaning that I will 87 | * consider the test valid and unbiased if the number of random elements that 88 | * fall under (inside) of the line and the number that fall outside of the line 89 | * are at most 20% apart. 90 | * 91 | * Since testing randomness is not reliable or repeatable, I will only fail the 92 | * test in two different scenarios. The first is if after the iterations the 93 | * outside or the inside is 0. The chances of that happening are so low that 94 | * if it happens, it's relatively safe to assume that something bad happened. The 95 | * second scenario happens when the ratio is outside of the 20% tolerance. If 96 | * that happens, I will re-run the entire test. If that test is outside of the 20% 97 | * tolerance, then the test will fail 98 | * 99 | * 100 | * @dataProvider provideGenerators 101 | */ 102 | public function testGenerate(\RandomLib\Generator $generator, $times) 103 | { 104 | $ratio = $this->doTestGenerate($generator, $times); 105 | if ($ratio < 0.8 || $ratio > 1.2) { 106 | $ratio2 = $this->doTestGenerate($generator, $times); 107 | if ($ratio2 > 1.2 || $ratio2 < 0.8) { 108 | $this->fail( 109 | sprintf( 110 | 'The test failed multiple runs with final ratios %f and %f', 111 | $ratio, 112 | $ratio2 113 | ) 114 | ); 115 | } 116 | } 117 | } 118 | 119 | protected function doTestGenerate(\RandomLib\Generator $generator, $times) 120 | { 121 | $inside = 0; 122 | $outside = 0; 123 | $on = 0; 124 | for ($i = 0; $i < $times; $i++) { 125 | $byte = $generator->generate(2); 126 | $byte = unpack('n', $byte); 127 | $byte = array_shift($byte); 128 | $xCoord = ($byte >> 8); 129 | $yCoord = ($byte & 0xFF); 130 | if ($xCoord < $yCoord) { 131 | $outside++; 132 | } elseif ($xCoord == $yCoord) { 133 | $on++; 134 | } else { 135 | $inside++; 136 | } 137 | } 138 | $this->assertGreaterThan(0, $outside, 'Outside Is 0'); 139 | $this->assertGreaterThan(0, $inside, 'Inside Is 0'); 140 | $ratio = $inside / $outside; 141 | 142 | return $ratio; 143 | } 144 | 145 | public function getGenerator($random) 146 | { 147 | $source1 = new Source(array( 148 | 'generate' => function ($size) use (&$random) { 149 | $ret = pack('N', $random); 150 | $random--; 151 | 152 | return substr($ret, -1 * $size); 153 | }, 154 | )); 155 | $sources = array($source1); 156 | $mixer = new Mixer(array( 157 | 'mix'=> function (array $sources) { 158 | if (empty($sources)) { 159 | return ''; 160 | } 161 | 162 | return array_pop($sources); 163 | }, 164 | )); 165 | 166 | return new Generator($sources, $mixer); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /lib/RandomLib/Factory.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The Random Factory 14 | * 15 | * Use this factory to instantiate random number generators, sources and mixers. 16 | * 17 | * PHP version 5.3 18 | * 19 | * @category PHPPasswordLib 20 | * @package Random 21 | * 22 | * @author Anthony Ferrara 23 | * @copyright 2011 The Authors 24 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 25 | * 26 | * @version Build @@version@@ 27 | */ 28 | namespace RandomLib; 29 | 30 | use SecurityLib\Strength; 31 | 32 | /** 33 | * The Random Factory 34 | * 35 | * Use this factory to instantiate random number generators, sources and mixers. 36 | * 37 | * @category PHPPasswordLib 38 | * @package Random 39 | * 40 | * @author Anthony Ferrara 41 | */ 42 | class Factory extends \SecurityLib\AbstractFactory 43 | { 44 | 45 | /** 46 | * @var array A list of available random number mixing strategies 47 | */ 48 | protected $mixers = array(); 49 | 50 | /** 51 | * @var array A list of available random number sources 52 | */ 53 | protected $sources = array(); 54 | 55 | /** 56 | * Build a new instance of the factory, loading core mixers and sources 57 | * 58 | * @return void 59 | */ 60 | public function __construct() 61 | { 62 | $this->loadMixers(); 63 | $this->loadSources(); 64 | } 65 | 66 | /** 67 | * Get a generator for the requested strength 68 | * 69 | * @param Strength $strength The requested strength of the random number 70 | * 71 | * @throws RuntimeException If an appropriate mixing strategy isn't found 72 | * 73 | * @return Generator The instantiated generator 74 | */ 75 | public function getGenerator(\SecurityLib\Strength $strength) 76 | { 77 | $sources = $this->findSources($strength); 78 | $mixer = $this->findMixer($strength); 79 | 80 | return new Generator($sources, $mixer); 81 | } 82 | 83 | /** 84 | * Get a high strength random number generator 85 | * 86 | * High Strength keys should ONLY be used for generating extremely strong 87 | * cryptographic keys. Generating them is very resource intensive and may 88 | * take several minutes or more depending on the requested size. 89 | * 90 | * @return Generator The instantiated generator 91 | */ 92 | public function getHighStrengthGenerator() 93 | { 94 | return $this->getGenerator(new Strength(Strength::HIGH)); 95 | } 96 | 97 | /** 98 | * Get a low strength random number generator 99 | * 100 | * Low Strength should be used anywhere that random strings are needed in a 101 | * non-cryptographical setting. They are not strong enough to be used as 102 | * keys or salts. They are however useful for one-time use tokens. 103 | * 104 | * @return Generator The instantiated generator 105 | */ 106 | public function getLowStrengthGenerator() 107 | { 108 | return $this->getGenerator(new Strength(Strength::LOW)); 109 | } 110 | 111 | /** 112 | * Get a medium strength random number generator 113 | * 114 | * Medium Strength should be used for most needs of a cryptographic nature. 115 | * They are strong enough to be used as keys and salts. However, they do 116 | * take some time and resources to generate, so they should not be over-used 117 | * 118 | * @return Generator The instantiated generator 119 | */ 120 | public function getMediumStrengthGenerator() 121 | { 122 | return $this->getGenerator(new Strength(Strength::MEDIUM)); 123 | } 124 | 125 | /** 126 | * Get all loaded mixing strategies 127 | * 128 | * @return array An array of mixers 129 | */ 130 | public function getMixers() 131 | { 132 | return $this->mixers; 133 | } 134 | 135 | /** 136 | * Get all loaded random number sources 137 | * 138 | * @return array An array of sources 139 | */ 140 | public function getSources() 141 | { 142 | return $this->sources; 143 | } 144 | 145 | /** 146 | * Register a mixing strategy for this factory instance 147 | * 148 | * @param string $name The name of the stategy 149 | * @param string $class The class name of the implementation 150 | * 151 | * @return Factory $this The current factory instance 152 | */ 153 | public function registerMixer($name, $class) 154 | { 155 | $this->registerType( 156 | 'mixers', 157 | __NAMESPACE__ . '\\Mixer', 158 | $name, 159 | $class 160 | ); 161 | 162 | return $this; 163 | } 164 | 165 | /** 166 | * Register a random number source for this factory instance 167 | * 168 | * Note that this class must implement the Source interface 169 | * 170 | * @param string $name The name of the stategy 171 | * @param string $class The class name of the implementation 172 | * 173 | * @return Factory $this The current factory instance 174 | */ 175 | public function registerSource($name, $class) 176 | { 177 | $this->registerType( 178 | 'sources', 179 | __NAMESPACE__ . '\\Source', 180 | $name, 181 | $class 182 | ); 183 | 184 | return $this; 185 | } 186 | 187 | /** 188 | * Find a sources based upon the requested strength 189 | * 190 | * @param Strength $strength The strength mixer to find 191 | * 192 | * @throws RuntimeException if a valid source cannot be found 193 | * 194 | * @return Source The found source 195 | */ 196 | protected function findSources(\SecurityLib\Strength $strength) 197 | { 198 | $sources = array(); 199 | foreach ($this->getSources() as $source) { 200 | if ($strength->compare($source::getStrength()) <= 0 && $source::isSupported()) { 201 | $sources[] = new $source(); 202 | } 203 | } 204 | 205 | if (0 === count($sources)) { 206 | throw new \RuntimeException('Could not find sources'); 207 | } 208 | 209 | return $sources; 210 | } 211 | 212 | /** 213 | * Find a mixer based upon the requested strength 214 | * 215 | * @param Strength $strength The strength mixer to find 216 | * 217 | * @throws RuntimeException if a valid mixer cannot be found 218 | * 219 | * @return Mixer The found mixer 220 | */ 221 | protected function findMixer(\SecurityLib\Strength $strength) 222 | { 223 | $newMixer = null; 224 | $fallback = null; 225 | foreach ($this->getMixers() as $mixer) { 226 | if (!$mixer::test()) { 227 | continue; 228 | } 229 | if ($strength->compare($mixer::getStrength()) == 0) { 230 | $newMixer = new $mixer(); 231 | } elseif ($strength->compare($mixer::getStrength()) == 1) { 232 | $fallback = new $mixer(); 233 | } 234 | } 235 | if (is_null($newMixer)) { 236 | if (is_null($fallback)) { 237 | throw new \RuntimeException('Could not find mixer'); 238 | } 239 | 240 | return $fallback; 241 | } 242 | 243 | return $newMixer; 244 | } 245 | 246 | /** 247 | * Load all core mixing strategies 248 | * 249 | * @return void 250 | */ 251 | protected function loadMixers() 252 | { 253 | $this->loadFiles( 254 | __DIR__ . '/Mixer', 255 | __NAMESPACE__ . '\\Mixer\\', 256 | array($this, 'registerMixer') 257 | ); 258 | } 259 | 260 | /** 261 | * Load all core random number sources 262 | * 263 | * @return void 264 | */ 265 | protected function loadSources() 266 | { 267 | $this->loadFiles( 268 | __DIR__ . '/Source', 269 | __NAMESPACE__ . '\\Source\\', 270 | array($this, 'registerSource') 271 | ); 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /lib/RandomLib/Generator.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2011 The Authors 8 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 9 | * @version Build @@version@@ 10 | */ 11 | 12 | /** 13 | * The Random Number Generator Class 14 | * 15 | * Use this factory to generate cryptographic quality random numbers (strings) 16 | * 17 | * PHP version 5.3 18 | * 19 | * @category PHPPasswordLib 20 | * @package Random 21 | * 22 | * @author Anthony Ferrara 23 | * @author Timo Hamina 24 | * @copyright 2011 The Authors 25 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 26 | * 27 | * @version Build @@version@@ 28 | */ 29 | namespace RandomLib; 30 | 31 | /** 32 | * The Random Number Generator Class 33 | * 34 | * Use this factory to generate cryptographic quality random numbers (strings) 35 | * 36 | * @category PHPPasswordLib 37 | * @package Random 38 | * 39 | * @author Anthony Ferrara 40 | * @author Timo Hamina 41 | */ 42 | class Generator 43 | { 44 | 45 | /** 46 | * @const Flag for uppercase letters 47 | */ 48 | const CHAR_UPPER = 1; 49 | 50 | /** 51 | * @const Flag for lowercase letters 52 | */ 53 | const CHAR_LOWER = 2; 54 | 55 | /** 56 | * @const Flag for alpha characters (combines UPPER + LOWER) 57 | */ 58 | const CHAR_ALPHA = 3; // CHAR_UPPER | CHAR_LOWER 59 | 60 | /** 61 | * @const Flag for digits 62 | */ 63 | const CHAR_DIGITS = 4; 64 | 65 | /** 66 | * @const Flag for alpha numeric characters 67 | */ 68 | const CHAR_ALNUM = 7; // CHAR_ALPHA | CHAR_DIGITS 69 | 70 | /** 71 | * @const Flag for uppercase hexadecimal symbols 72 | */ 73 | const CHAR_UPPER_HEX = 12; // 8 | CHAR_DIGITS 74 | 75 | /** 76 | * @const Flag for lowercase hexidecimal symbols 77 | */ 78 | const CHAR_LOWER_HEX = 20; // 16 | CHAR_DIGITS 79 | 80 | /** 81 | * @const Flag for base64 symbols 82 | */ 83 | const CHAR_BASE64 = 39; // 32 | CHAR_ALNUM 84 | 85 | /** 86 | * @const Flag for additional symbols accessible via the keyboard 87 | */ 88 | const CHAR_SYMBOLS = 64; 89 | 90 | /** 91 | * @const Flag for brackets 92 | */ 93 | const CHAR_BRACKETS = 128; 94 | 95 | /** 96 | * @const Flag for punctuation marks 97 | */ 98 | const CHAR_PUNCT = 256; 99 | 100 | /** 101 | * @const Flag for upper/lower-case and digits but without "B8G6I1l|0OQDS5Z2" 102 | */ 103 | const EASY_TO_READ = 512; 104 | 105 | /** 106 | * @var Mixer The mixing strategy to use for this generator instance 107 | */ 108 | protected $mixer = null; 109 | 110 | /** 111 | * @var array An array of random number sources to use for this generator 112 | */ 113 | protected $sources = array(); 114 | 115 | /** 116 | * @var array The different characters, by Flag 117 | */ 118 | protected $charArrays = array( 119 | self::CHAR_UPPER => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 120 | self::CHAR_LOWER => 'abcdefghijklmnopqrstuvwxyz', 121 | self::CHAR_DIGITS => '0123456789', 122 | self::CHAR_UPPER_HEX => 'ABCDEF', 123 | self::CHAR_LOWER_HEX => 'abcdef', 124 | self::CHAR_BASE64 => '+/', 125 | self::CHAR_SYMBOLS => '!"#$%&\'()* +,-./:;<=>?@[\]^_`{|}~', 126 | self::CHAR_BRACKETS => '()[]{}<>', 127 | self::CHAR_PUNCT => ',.;:', 128 | ); 129 | 130 | /** 131 | * @internal 132 | * @private 133 | * @const string Ambiguous characters for "Easy To Read" sets 134 | */ 135 | const AMBIGUOUS_CHARS = 'B8G6I1l|0OQDS5Z2()[]{}:;,.'; 136 | 137 | /** 138 | * Build a new instance of the generator 139 | * 140 | * @param array $sources An array of random data sources to use 141 | * @param Mixer $mixer The mixing strategy to use for this generator 142 | */ 143 | public function __construct(array $sources, Mixer $mixer) 144 | { 145 | foreach ($sources as $source) { 146 | $this->addSource($source); 147 | } 148 | $this->mixer = $mixer; 149 | } 150 | 151 | /** 152 | * Add a random number source to the generator 153 | * 154 | * @param Source $source The random number source to add 155 | * 156 | * @return Generator $this The current generator instance 157 | */ 158 | public function addSource(Source $source) 159 | { 160 | $this->sources[] = $source; 161 | 162 | return $this; 163 | } 164 | 165 | /** 166 | * Generate a random number (string) of the requested size 167 | * 168 | * @param int $size The size of the requested random number 169 | * 170 | * @return string The generated random number (string) 171 | */ 172 | public function generate($size) 173 | { 174 | $seeds = array(); 175 | foreach ($this->sources as $source) { 176 | $seeds[] = $source->generate($size); 177 | } 178 | 179 | return $this->mixer->mix($seeds); 180 | } 181 | 182 | /** 183 | * Generate a random integer with the given range 184 | * 185 | * @param int $min The lower bound of the range to generate 186 | * @param int $max The upper bound of the range to generate 187 | * 188 | * @return int The generated random number within the range 189 | */ 190 | public function generateInt($min = 0, $max = PHP_INT_MAX) 191 | { 192 | $tmp = (int) max($max, $min); 193 | $min = (int) min($max, $min); 194 | $max = $tmp; 195 | $range = $max - $min; 196 | if ($range == 0) { 197 | return $max; 198 | } elseif ($range > PHP_INT_MAX || is_float($range) || $range < 0) { 199 | /** 200 | * This works, because PHP will auto-convert it to a float at this point, 201 | * But on 64 bit systems, the float won't have enough precision to 202 | * actually store the difference, so we need to check if it's a float 203 | * and hence auto-converted... 204 | */ 205 | throw new \RangeException( 206 | 'The supplied range is too great to generate' 207 | ); 208 | } 209 | 210 | $bits = $this->countBits($range) + 1; 211 | $bytes = (int) max(ceil($bits / 8), 1); 212 | if ($bits == 63) { 213 | /** 214 | * Fixes issue #22 215 | * 216 | * @see https://github.com/ircmaxell/RandomLib/issues/22 217 | */ 218 | $mask = 0x7fffffffffffffff; 219 | } else { 220 | $mask = (int) (pow(2, $bits) - 1); 221 | } 222 | 223 | /** 224 | * The mask is a better way of dropping unused bits. Basically what it does 225 | * is to set all the bits in the mask to 1 that we may need. Since the max 226 | * range is PHP_INT_MAX, we will never need negative numbers (which would 227 | * have the MSB set on the max int possible to generate). Therefore we 228 | * can just mask that away. Since pow returns a float, we need to cast 229 | * it back to an int so the mask will work. 230 | * 231 | * On a 64 bit platform, that means that PHP_INT_MAX is 2^63 - 1. Which 232 | * is also the mask if 63 bits are needed (by the log(range, 2) call). 233 | * So if the computed result is negative (meaning the 64th bit is set), the 234 | * mask will correct that. 235 | * 236 | * This turns out to be slightly better than the shift as we don't need to 237 | * worry about "fixing" negative values. 238 | */ 239 | do { 240 | $test = $this->generate($bytes); 241 | $result = hexdec(bin2hex($test)) & $mask; 242 | } while ($result > $range); 243 | 244 | return $result + $min; 245 | } 246 | 247 | /** 248 | * Generate a random string of specified length. 249 | * 250 | * This uses the supplied character list for generating the new result 251 | * string. 252 | * 253 | * @param int $length The length of the generated string 254 | * @param mixed $characters String: An optional list of characters to use 255 | * Integer: Character flags 256 | * 257 | * @return string The generated random string 258 | */ 259 | public function generateString($length, $characters = '') 260 | { 261 | if (is_int($characters)) { 262 | // Combine character sets 263 | $characters = $this->expandCharacterSets($characters); 264 | } 265 | if ($length == 0 || strlen($characters) == 1) { 266 | return ''; 267 | } elseif (empty($characters)) { 268 | // Default to base 64 269 | $characters = $this->expandCharacterSets(self::CHAR_BASE64); 270 | } 271 | 272 | // determine how many bytes to generate 273 | // This is basically doing floor(log(strlen($characters))) 274 | // But it's fixed to work properly for all numbers 275 | $len = strlen($characters); 276 | 277 | // The max call here fixes an issue where we under-generate in cases 278 | // where less than 8 bits are needed to represent $len 279 | $bytes = $length * ceil(($this->countBits($len)) / 8); 280 | 281 | // determine mask for valid characters 282 | $mask = 256 - (256 % $len); 283 | 284 | $result = ''; 285 | do { 286 | $rand = $this->generate($bytes); 287 | for ($i = 0; $i < $bytes; $i++) { 288 | if (ord($rand[$i]) >= $mask) { 289 | continue; 290 | } 291 | $result .= $characters[ord($rand[$i]) % $len]; 292 | } 293 | } while (strlen($result) < $length); 294 | // We may over-generate, since we always use the entire buffer 295 | return substr($result, 0, $length); 296 | } 297 | 298 | /** 299 | * Get the Mixer used for this instance 300 | * 301 | * @return Mixer the current mixer 302 | */ 303 | public function getMixer() 304 | { 305 | return $this->mixer; 306 | } 307 | 308 | /** 309 | * Get the Sources used for this instance 310 | * 311 | * @return Source[] the current mixer 312 | */ 313 | public function getSources() 314 | { 315 | return $this->sources; 316 | } 317 | 318 | /** 319 | * Count the minimum number of bits to represent the provided number 320 | * 321 | * This is basically floor(log($number, 2)) 322 | * But avoids float precision issues 323 | * 324 | * @param int $number The number to count 325 | * 326 | * @return int The number of bits 327 | */ 328 | protected function countBits($number) 329 | { 330 | $log2 = 0; 331 | while ($number >>= 1) { 332 | $log2++; 333 | } 334 | 335 | return $log2; 336 | } 337 | 338 | /** 339 | * Expand a character set bitwise spec into a string character set 340 | * 341 | * This will also replace EASY_TO_READ characters if the flag is set 342 | * 343 | * @param int $spec The spec to expand (bitwise combination of flags) 344 | * 345 | * @return string The expanded string 346 | */ 347 | protected function expandCharacterSets($spec) 348 | { 349 | $combined = ''; 350 | if ($spec == self::EASY_TO_READ) { 351 | $spec |= self::CHAR_ALNUM; 352 | } 353 | foreach ($this->charArrays as $flag => $chars) { 354 | if ($flag == self::EASY_TO_READ) { 355 | // handle this later 356 | continue; 357 | } 358 | if (($spec & $flag) === $flag) { 359 | $combined .= $chars; 360 | } 361 | } 362 | if ($spec & self::EASY_TO_READ) { 363 | // remove ambiguous characters 364 | $combined = str_replace(str_split(self::AMBIGUOUS_CHARS), '', $combined); 365 | } 366 | 367 | return count_chars($combined, 3); 368 | } 369 | } 370 | --------------------------------------------------------------------------------