├── .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 | [](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 |
--------------------------------------------------------------------------------