Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit of random support

  • Loading branch information...
commit ac878d6afcd3974b18790755087263ebc63e868d 0 parents
@ircmaxell authored
Showing with 2,345 additions and 0 deletions.
  1. +1 −0  .gitignore
  2. +6 −0 README.md
  3. +29 −0 composer.json
  4. +54 −0 composer.lock
  5. +115 −0 lib/RandomLib/AbstractMixer.php
  6. +221 −0 lib/RandomLib/Factory.php
  7. +214 −0 lib/RandomLib/Generator.php
  8. +56 −0 lib/RandomLib/Mixer.php
  9. +105 −0 lib/RandomLib/Mixer/Hash.php
  10. +50 −0 lib/RandomLib/Source.php
  11. +65 −0 lib/RandomLib/Source/CAPICOM.php
  12. +68 −0 lib/RandomLib/Source/MTRand.php
  13. +91 −0 lib/RandomLib/Source/MicroTime.php
  14. +64 −0 lib/RandomLib/Source/OpenSSL.php
  15. +68 −0 lib/RandomLib/Source/Rand.php
  16. +49 −0 lib/RandomLib/Source/Random.php
  17. +72 −0 lib/RandomLib/Source/URandom.php
  18. +61 −0 lib/RandomLib/Source/UniqID.php
  19. +29 −0 phpunit.xml.dist
  20. +41 −0 test/Mocks/AbstractMock.php
  21. +61 −0 test/Mocks/Random/Generator.php
  22. +71 −0 test/Mocks/Random/Mixer.php
  23. +64 −0 test/Mocks/Random/Source.php
  24. +45 −0 test/Unit/RandomLib/FactoryTest.php
  25. +162 −0 test/Unit/RandomLib/GeneratorTest.php
  26. +51 −0 test/Unit/RandomLib/Mixer/HashTest.php
  27. +36 −0 test/Unit/RandomLib/Source/CAPICOMTest.php
  28. +42 −0 test/Unit/RandomLib/Source/MTRandTest.php
  29. +38 −0 test/Unit/RandomLib/Source/MicroTimeTest.php
  30. +40 −0 test/Unit/RandomLib/Source/RandTest.php
  31. +43 −0 test/Unit/RandomLib/Source/URandomTest.php
  32. +36 −0 test/Unit/RandomLib/Source/UniqIDTest.php
  33. +148 −0 test/Vectors/Random/GeneratorTest.php
  34. +49 −0 test/bootstrap.php
1  .gitignore
@@ -0,0 +1 @@
+vendor
6 README.md
@@ -0,0 +1,6 @@
+RandomLib
+=========
+
+A library for generating random numbers and strings of various strengths.
+
+This library is useful in security contexts.
29 composer.json
@@ -0,0 +1,29 @@
+{
+ "name": "ircmaxell/random-lib",
+ "type": "library",
+ "version": "0.0.1-beta1",
+ "description": "A Library For Generating Secure Random Numbers",
+ "keywords": ["random", "random-numbers", "random-strings", "cryptography"],
+ "homepage": "https://github.com/ircmaxell/PHP-RandomLib",
+ "license": "MIT",
+ "minimum-stability": "dev",
+ "authors": [
+ {
+ "name": "Anthony Ferrara",
+ "email": "ircmaxell@ircmaxell.com",
+ "homepage": "http://blog.ircmaxell.com"
+ }
+ ],
+ "require-dev": {
+ "mikey179/vfsStream": "1.1.*"
+ },
+ "require": {
+ "ircmaxell/security-lib": "dev-master",
+ "php": ">=5.3.2"
+ },
+ "autoload": {
+ "psr-0": {
+ "RandomLib": "lib"
+ }
+ }
+}
54 composer.lock
@@ -0,0 +1,54 @@
+{
+ "hash": "fc0152cc930dfe3d647e17cd31b0d3a9",
+ "packages": [
+ {
+ "name": "ircmaxell/security-lib",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ircmaxell/SecurityLib",
+ "reference": "b8ce85731e14940d92ff72274e8d19696b69a018"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ircmaxell/SecurityLib/zipball/b8ce85731e14940d92ff72274e8d19696b69a018",
+ "reference": "b8ce85731e14940d92ff72274e8d19696b69a018",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.2"
+ },
+ "require-dev": {
+ "mikey179/vfsstream": "1.1.*"
+ },
+ "time": "2013-02-13 17:23:59",
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "SecurityLib": "lib"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Anthony Ferrara",
+ "email": "ircmaxell@ircmaxell.com",
+ "homepage": "http://blog.ircmaxell.com"
+ }
+ ],
+ "description": "A Base Security Library",
+ "homepage": "https://github.com/ircmaxell/PHP-SecurityLib"
+ }
+ ],
+ "packages-dev": null,
+ "aliases": [
+
+ ],
+ "minimum-stability": "dev",
+ "stability-flags": {
+ "ircmaxell/security-lib": 20
+ }
+}
115 lib/RandomLib/AbstractMixer.php
@@ -0,0 +1,115 @@
+<?php
+/**
+ * An abstract mixer to implement a common mixing strategy
+ *
+ * PHP version 5.3
+ *
+ * @category PHPSecurityLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright 2011 The Authors
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version Build @@version@@
+ */
+
+namespace RandomLib;
+
+/**
+ * An abstract mixer to implement a common mixing strategy
+ *
+ * @see http://tools.ietf.org/html/rfc4086#section-5.2
+ * @category PHPSecurityLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ */
+abstract class AbstractMixer implements \RandomLib\Mixer {
+
+ /**
+ * Get the block size (the size of the individual blocks used for the mixing)
+ *
+ * @return int The block size
+ */
+ abstract protected function getPartSize();
+
+ /**
+ * Mix 2 parts together using one method
+ *
+ * @param string $part1 The first part to mix
+ * @param string $part2 The second part to mix
+ *
+ * @return string The mixed data
+ */
+ abstract protected function mixParts1($part1, $part2);
+
+ /**
+ * Mix 2 parts together using another different method
+ *
+ * @param string $part1 The first part to mix
+ * @param string $part2 The second part to mix
+ *
+ * @return string The mixed data
+ */
+ abstract protected function mixParts2($part1, $part2);
+
+ /**
+ * Mix the provided array of strings into a single output of the same size
+ *
+ * All elements of the array should be the same size.
+ *
+ * @param array $parts The parts to be mixed
+ *
+ * @return string The mixed result
+ */
+ public function mix(array $parts) {
+ if (empty($parts)) {
+ return '';
+ }
+ $len = strlen($parts[0]);
+ $parts = $this->normalizeParts($parts);
+ $stringSize = count($parts[0]);
+ $partsSize = count($parts);
+ $result = '';
+ $offset = 0;
+ for ($i = 0; $i < $stringSize; $i++) {
+ $stub = $parts[$offset][$i];
+ for ($j = 1; $j < $partsSize; $j++) {
+ $newKey = $parts[($j + $offset) % $partsSize][$i];
+ //Alternately mix the output for each source
+ if ($j % 2 == 1) {
+ $stub ^= $this->mixParts1($stub, $newKey);
+ } else {
+ $stub ^= $this->mixParts2($stub, $newKey);
+ }
+ }
+ $result .= $stub;
+ $offset = ($offset + 1) % $partsSize;
+ }
+ return substr($result, 0, $len);
+ }
+
+ /**
+ * Normalize the part array and split it block part size.
+ *
+ * This will make all parts the same length and a multiple
+ * of the part size
+ *
+ * @param array $parts The parts to normalize
+ *
+ * @return array The normalized and split parts
+ */
+ protected function normalizeParts(array $parts) {
+ $blockSize = $this->getPartSize();
+ $callback = function($value) {
+ return strlen($value);
+ };
+ $maxSize = max(array_map($callback, $parts));
+ if ($maxSize % $blockSize != 0) {
+ $maxSize += $blockSize - ($maxSize % $blockSize);
+ }
+ foreach ($parts as &$part) {
+ $part = str_pad($part, $maxSize, chr(0));
+ $part = str_split($part, $blockSize);
+ }
+ return $parts;
+ }
+}
221 lib/RandomLib/Factory.php
@@ -0,0 +1,221 @@
+<?php
+/**
+ * The Random Factory
+ *
+ * Use this factory to instantiate random number generators, sources and mixers.
+ *
+ * PHP version 5.3
+ *
+ * @category PHPPasswordLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright 2011 The Authors
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version Build @@version@@
+ */
+
+namespace RandomLib;
+
+use SecurityLib\Strength;
+
+/**
+ * The Random Factory
+ *
+ * Use this factory to instantiate random number generators, sources and mixers.
+ *
+ * @category PHPPasswordLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ */
+class Factory extends \SecurityLib\AbstractFactory {
+
+ /**
+ * @var array A list of available random number mixing strategies
+ */
+ protected $mixers = array();
+
+ /**
+ * @var array A list of available random number sources
+ */
+ protected $sources = array();
+
+ /**
+ * Build a new instance of the factory, loading core mixers and sources
+ *
+ * @return void
+ */
+ public function __construct() {
+ $this->loadMixers();
+ $this->loadSources();
+ }
+
+ /**
+ * Get a generator for the requested strength
+ *
+ * @param Strength $strength The requested strength of the random number
+ *
+ * @return Generator The instantiated generator
+ * @throws RuntimeException If an appropriate mixing strategy isn't found
+ */
+ public function getGenerator(\SecurityLib\Strength $strength) {
+ $sources = $this->getSources();
+ $newSources = array();
+ foreach ($sources as $source) {
+ if ($strength->compare($source::getStrength()) <= 0) {
+ $newSources[] = new $source;
+ }
+ }
+ $mixer = $this->findMixer($strength);
+ return new Generator($newSources, $mixer);
+ }
+
+ /**
+ * Get a high strength random number generator
+ *
+ * High Strength keys should ONLY be used for generating extremely strong
+ * cryptographic keys. Generating them is very resource intensive and may
+ * take several minutes or more depending on the requested size.
+ *
+ * @return Generator The instantiated generator
+ */
+ public function getHighStrengthGenerator() {
+ return $this->getGenerator(new Strength(Strength::HIGH));
+ }
+
+ /**
+ * Get a low strength random number generator
+ *
+ * Low Strength should be used anywhere that random strings are needed in a
+ * non-cryptographical setting. They are not strong enough to be used as
+ * keys or salts. They are however useful for one-time use tokens.
+ *
+ * @return Generator The instantiated generator
+ */
+ public function getLowStrengthGenerator() {
+ return $this->getGenerator(new Strength(Strength::LOW));
+ }
+
+ /**
+ * Get a medium strength random number generator
+ *
+ * Medium Strength should be used for most needs of a cryptographic nature.
+ * They are strong enough to be used as keys and salts. However, they do
+ * take some time and resources to generate, so they should not be over-used
+ *
+ * @return Generator The instantiated generator
+ */
+ public function getMediumStrengthGenerator() {
+ return $this->getGenerator(new Strength(Strength::MEDIUM));
+ }
+
+ /**
+ * Get all loaded mixing strategies
+ *
+ * @return array An array of mixers
+ */
+ public function getMixers() {
+ return $this->mixers;
+ }
+
+ /**
+ * Get all loaded random number sources
+ *
+ * @return array An array of sources
+ */
+ public function getSources() {
+ return $this->sources;
+ }
+
+ /**
+ * Register a mixing strategy for this factory instance
+ *
+ * @param string $name The name of the stategy
+ * @param string $class The class name of the implementation
+ *
+ * @return Factory $this The current factory instance
+ */
+ public function registerMixer($name, $class) {
+ $this->registerType(
+ 'mixers',
+ __NAMESPACE__ . '\\Mixer',
+ $name,
+ $class
+ );
+ return $this;
+ }
+
+ /**
+ * Register a random number source for this factory instance
+ *
+ * Note that this class must implement the Source interface
+ *
+ * @param string $name The name of the stategy
+ * @param string $class The class name of the implementation
+ *
+ * @return Factory $this The current factory instance
+ */
+ public function registerSource($name, $class) {
+ $this->registerType(
+ 'sources',
+ __NAMESPACE__ . '\\Source',
+ $name,
+ $class
+ );
+ return $this;
+ }
+
+ /**
+ * Find a mixer based upon the requested strength
+ *
+ * @param Strength $strength The strength mixer to find
+ *
+ * @return Mixer The found mixer
+ * @throws RuntimeException if a valid mixer cannot be found
+ */
+ protected function findMixer(\SecurityLib\Strength $strength) {
+ $newMixer = null;
+ $fallback = null;
+ foreach ($this->getMixers() as $mixer) {
+ if ($strength->compare($mixer::getStrength()) == 0) {
+ $newMixer = new $mixer;
+ } elseif ($strength->compare($mixer::getStrength()) == 1) {
+ $fallback = new $mixer;
+ }
+ }
+ if (is_null($newMixer)) {
+ if (is_null($fallback)) {
+ throw new \RuntimeException('Could not find mixer');
+ }
+ return $fallback;
+ }
+ return $newMixer;
+ }
+
+ /**
+ * Load all core mixing strategies
+ *
+ * @return void
+ */
+ protected function loadMixers() {
+ $this->loadFiles(
+ __DIR__ . '/Mixer',
+ __NAMESPACE__ . '\\Mixer\\',
+ array($this, 'registerMixer')
+ );
+ }
+
+ /**
+ * Load all core random number sources
+ *
+ * @return void
+ */
+ protected function loadSources() {
+ $this->loadFiles(
+ __DIR__ . '/Source',
+ __NAMESPACE__ . '\\Source\\',
+ array($this, 'registerSource')
+ );
+ }
+
+}
+
214 lib/RandomLib/Generator.php
@@ -0,0 +1,214 @@
+<?php
+/**
+ * The Random Number Generator Class
+ *
+ * Use this factory to generate cryptographic quality random numbers (strings)
+ *
+ * PHP version 5.3
+ *
+ * @category PHPPasswordLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @author Timo Hamina
+ * @copyright 2011 The Authors
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version Build @@version@@
+ */
+
+namespace RandomLib;
+
+use SecurityLib\BaseConverter;
+
+/**
+ * The Random Number Generator Class
+ *
+ * Use this factory to generate cryptographic quality random numbers (strings)
+ *
+ * @category PHPPasswordLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @author Timo Hamina
+ */
+class Generator {
+
+ /**
+ * @var Mixer The mixing strategy to use for this generator instance
+ */
+ protected $mixer = null;
+
+ /**
+ * @var array An array of random number sources to use for this generator
+ */
+ protected $sources = array();
+
+ /**
+ * Build a new instance of the generator
+ *
+ * @param array $sources An array of random data sources to use
+ * @param Mixer $mixer The mixing strategy to use for this generator
+ */
+ public function __construct(array $sources, Mixer $mixer) {
+ foreach ($sources as $source) {
+ $this->addSource($source);
+ }
+ $this->mixer = $mixer;
+ }
+
+ /**
+ * Add a random number source to the generator
+ *
+ * @param Source $source The random number source to add
+ *
+ * @return Generator $this The current generator instance
+ */
+ public function addSource(Source $source) {
+ $this->sources[] = $source;
+ return $this;
+ }
+
+ /**
+ * Generate a random number (string) of the requested size
+ *
+ * @param int $size The size of the requested random number
+ *
+ * @return string The generated random number (string)
+ */
+ public function generate($size) {
+ $seeds = array();
+ foreach ($this->sources as $source) {
+ $seeds[] = $source->generate($size);
+ }
+ return $this->mixer->mix($seeds);
+ }
+
+ /**
+ * Generate a random integer with the given range
+ *
+ * @param int $min The lower bound of the range to generate
+ * @param int $max The upper bound of the range to generate
+ *
+ * @return int The generated random number within the range
+ */
+ public function generateInt($min = 0, $max = PHP_INT_MAX) {
+ $tmp = (int) max($max, $min);
+ $min = (int) min($max, $min);
+ $max = $tmp;
+ $range = $max - $min;
+ if ($range == 0) {
+ return $max;
+ } elseif ($range > PHP_INT_MAX || is_float($range)) {
+ /**
+ * This works, because PHP will auto-convert it to a float at this point,
+ * But on 64 bit systems, the float won't have enough precision to
+ * actually store the difference, so we need to check if it's a float
+ * and hence auto-converted...
+ */
+ throw new \RangeException(
+ 'The supplied range is too great to generate'
+ );
+ }
+
+ $bits = $this->countBits($range) + 1;
+ $bytes = (int) max(ceil($bits / 8), 1);
+ $mask = (int) (pow(2, $bits) - 1);
+ /**
+ * The mask is a better way of dropping unused bits. Basically what it does
+ * is to set all the bits in the mask to 1 that we may need. Since the max
+ * range is PHP_INT_MAX, we will never need negative numbers (which would
+ * have the MSB set on the max int possible to generate). Therefore we
+ * can just mask that away. Since pow returns a float, we need to cast
+ * it back to an int so the mask will work.
+ *
+ * On a 64 bit platform, that means that PHP_INT_MAX is 2^63 - 1. Which
+ * is also the mask if 63 bits are needed (by the log(range, 2) call).
+ * So if the computed result is negative (meaning the 64th bit is set), the
+ * mask will correct that.
+ *
+ * This turns out to be slightly better than the shift as we don't need to
+ * worry about "fixing" negative values.
+ */
+ do {
+ $test = $this->generate($bytes);
+ $result = hexdec(bin2hex($test)) & $mask;
+ } while ($result > $range);
+ return $result + $min;
+ }
+
+ /**
+ * Generate a random string of specified length.
+ *
+ * This uses the supplied character list for generating the new result
+ * string.
+ *
+ * @param int $length The length of the generated string
+ * @param string $characters An optional list of characters to use
+ *
+ * @return string The generated random string
+ */
+ public function generateString($length, $characters = '') {
+ if ($length == 0 || strlen($characters) == 1) {
+ return '';
+ } elseif (empty($characters)) {
+ // Default to base 64
+ $characters = '0123456789abcdefghijklmnopqrstuvwxyz' .
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ./';
+ }
+ // determine how many bytes to generate
+ // This is basically doing floor(log(strlen($characters)))
+ // But it's fixed to work properly for all numbers
+ $len = strlen($characters);
+ $bytes = ceil($length * ($this->countBits($len) + 1) / 8);
+
+ // determine mask for valid characters
+ $mask = 255 - (255 % $len);
+ $result = '';
+ do {
+ $rand = $this->generate($bytes);
+ for ($i = 0; $i < $bytes; $i++) {
+ if (ord($rand[$i]) > $mask) {
+ continue;
+ }
+ $result .= $characters[ord($rand[$i]) % $len];
+ }
+ } while (strlen($result) < $length);
+ // We may over-generate, since we always use the entire buffer
+ return substr($result, 0, $length);
+ }
+
+ /**
+ * Get the Mixer used for this instance
+ *
+ * @return Mixer the current mixer
+ */
+ public function getMixer() {
+ return $this->mixer;
+ }
+
+ /**
+ * Get the Sources used for this instance
+ *
+ * @return Source[] the current mixer
+ */
+ public function getSources() {
+ return $this->sources;
+ }
+
+ /**
+ * Count the minimum number of bits to represent the provided number
+ *
+ * This is basically floor(log($number, 2))
+ * But avoids float precision issues
+ *
+ * @param int $number The number to count
+ *
+ * @return int The number of bits
+ */
+ protected function countBits($number) {
+ $log2 = 0;
+ while ($number >>= 1) {
+ $log2++;
+ }
+ return $log2;
+ }
+
+}
56 lib/RandomLib/Mixer.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * The Mixer strategy interface.
+ *
+ * All mixing strategies must implement this interface
+ *
+ * PHP version 5.3
+ *
+ * @category PHPPasswordLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright 2011 The Authors
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version Build @@version@@
+ */
+
+namespace RandomLib;
+
+/**
+ * The Mixer strategy interface.
+ *
+ * All mixing strategies must implement this interface
+ *
+ * @category PHPPasswordLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @codeCoverageIgnore
+ */
+interface Mixer {
+
+ /**
+ * Return an instance of Strength indicating the strength of the mixer
+ *
+ * @return Strength An instance of one of the strength classes
+ */
+ public static function getStrength();
+
+ /**
+ * Test to see if the mixer is available
+ *
+ * @return boolean If the mixer is available on the system
+ */
+ public static function test();
+
+ /**
+ * Mix the provided array of strings into a single output of the same size
+ *
+ * All elements of the array should be the same size.
+ *
+ * @param array $parts The parts to be mixed
+ *
+ * @return string The mixed result
+ */
+ public function mix(array $parts);
+
+}
105 lib/RandomLib/Mixer/Hash.php
@@ -0,0 +1,105 @@
+<?php
+/**
+ * The Hash medium strength mixer class
+ *
+ * This class implements a mixer based upon the recommendations in RFC 4086
+ * section 5.2
+ *
+ * PHP version 5.3
+ *
+ * @see http://tools.ietf.org/html/rfc4086#section-5.2
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Mixer
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright 2011 The Authors
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version Build @@version@@
+ */
+
+namespace RandomLib\Mixer;
+
+use \SecurityLib\Strength;
+
+/**
+ * The Hash medium strength mixer class
+ *
+ * This class implements a mixer based upon the recommendations in RFC 4086
+ * section 5.2
+ *
+ * @see http://tools.ietf.org/html/rfc4086#section-5.2
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Mixer
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ */
+class Hash extends \RandomLib\AbstractMixer {
+
+ /**
+ * @var string The hash instance to use
+ */
+ protected $hash = null;
+
+ /**
+ * Build the hash mixer
+ *
+ * @param string $hash The hash instance to use (defaults to sha512)
+ *
+ * @return void
+ */
+ public function __construct($hash = 'sha512') {
+ $this->hash = $hash;
+ }
+
+ /**
+ * Return an instance of Strength indicating the strength of the source
+ *
+ * @return Strength An instance of one of the strength classes
+ */
+ public static function getStrength() {
+ return new Strength(Strength::MEDIUM);
+ }
+
+ /**
+ * Test to see if the mixer is available
+ *
+ * @return boolean If the mixer is available on the system
+ */
+ public static function test() {
+ return true;
+ }
+
+ /**
+ * Get the block size (the size of the individual blocks used for the mixing)
+ *
+ * @return int The block size
+ */
+ protected function getPartSize() {
+ return strlen(hash($this->hash, '', true));
+ }
+
+ /**
+ * Mix 2 parts together using one method
+ *
+ * @param string $part1 The first part to mix
+ * @param string $part2 The second part to mix
+ *
+ * @return string The mixed data
+ */
+ protected function mixParts1($part1, $part2) {
+ return hash_hmac($this->hash, $part1, $part2, true);
+ }
+
+ /**
+ * Mix 2 parts together using another different method
+ *
+ * @param string $part1 The first part to mix
+ * @param string $part2 The second part to mix
+ *
+ * @return string The mixed data
+ */
+ protected function mixParts2($part1, $part2) {
+ return hash_hmac($this->hash, $part2, $part1, true);
+ }
+
+}
50 lib/RandomLib/Source.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * The Random Number Source interface.
+ *
+ * All random number sources must implement this interface
+ *
+ * PHP version 5.3
+ *
+ * @category PHPPasswordLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright 2011 The Authors
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version Build @@version@@
+ */
+
+namespace RandomLib;
+
+/**
+ * The Random Number Source interface.
+ *
+ * All random number sources must implement this interface
+ *
+ * @category PHPPasswordLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @codeCoverageIgnore
+ */
+interface Source {
+
+ /**
+ * Return an instance of Strength indicating the strength of the source
+ *
+ * @return Strength An instance of one of the strength classes
+ */
+ public static function getStrength();
+
+ /**
+ * Generate a random string of the specified size
+ *
+ * Note: If the source fails to generate enough data, the result must be
+ * padded to the requested length.
+ *
+ * @param int $size The size of the requested random string
+ *
+ * @return string A string of the requested size
+ */
+ public function generate($size);
+
+}
65 lib/RandomLib/Source/CAPICOM.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * The Capicom Random Number Source
+ *
+ * This uses the Windows CapiCom Com object to generate random numbers
+ *
+ * PHP version 5.3
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Source
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright 2011 The Authors
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version Build @@version@@
+ */
+
+namespace RandomLib\Source;
+
+use SecurityLib\Strength;
+
+/**
+ * The Capicom Random Number Source
+ *
+ * This uses the Windows CapiCom Com object to generate random numbers
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Source
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @codeCoverageIgnore
+ */
+class CAPICOM implements \RandomLib\Source {
+
+ /**
+ * Return an instance of Strength indicating the strength of the source
+ *
+ * @return Strength An instance of one of the strength classes
+ */
+ public static function getStrength() {
+ return new Strength(Strength::MEDIUM);
+ }
+
+ /**
+ * Generate a random string of the specified size
+ *
+ * @param int $size The size of the requested random string
+ *
+ * @return string A string of the requested size
+ */
+ public function generate($size) {
+ if (!class_exists('\\COM', false)) {
+ return str_repeat(chr(0), $size);
+ }
+ try {
+ $util = new \COM('CAPICOM.Utilities.1');
+ $data = base64_decode($util->GetRandom($size, 0));
+ return str_pad($data, $size, chr(0));
+ } catch (\Exception $e) {
+ unset($e);
+ return str_repeat(chr(0), $size);
+ }
+ }
+
+}
68 lib/RandomLib/Source/MTRand.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * The MTRand Random Number Source
+ *
+ * This source generates low strength random numbers by using the internal
+ * mt_rand() function. By itself it is quite weak. However when combined with
+ * other sources it does provide significant benefit.
+ *
+ * PHP version 5.3
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Source
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright 2011 The Authors
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version Build @@version@@
+ */
+
+namespace RandomLib\Source;
+
+use SecurityLib\Strength;
+
+/**
+ * The MTRand Random Number Source
+ *
+ * This source generates low strength random numbers by using the internal
+ * mt_rand() function. By itself it is quite weak. However when combined with
+ * other sources it does provide significant benefit.
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Source
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @codeCoverageIgnore
+ */
+class MTRand implements \RandomLib\Source {
+
+ /**
+ * Return an instance of Strength indicating the strength of the source
+ *
+ * @return Strength An instance of one of the strength classes
+ */
+ public static function getStrength() {
+ // Detect if Suhosin Hardened PHP patch is applied
+ if (defined('S_ALL')) {
+ return new Strength(Strength::MEDIUM);
+ } else {
+ return new Strength(Strength::LOW);
+ }
+ }
+
+ /**
+ * Generate a random string of the specified size
+ *
+ * @param int $size The size of the requested random string
+ *
+ * @return string A string of the requested size
+ */
+ public function generate($size) {
+ $result = '';
+ for ($i = 0; $i < $size; $i++) {
+ $result .= chr((mt_rand() ^ mt_rand()) % 256);
+ }
+ return $result;
+ }
+
+}
91 lib/RandomLib/Source/MicroTime.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * The Microtime Random Number Source
+ *
+ * This uses the current micro-second (looped several times) for a **very** weak
+ * random number source. This is only useful when combined with several other
+ * stronger sources
+ *
+ * PHP version 5.3
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Source
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright 2011 The Authors
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version Build @@version@@
+ */
+
+namespace RandomLib\Source;
+
+use SecurityLib\Strength;
+
+/**
+ * The Microtime Random Number Source
+ *
+ * This uses the current micro-second (looped several times) for a **very** weak
+ * random number source. This is only useful when combined with several other
+ * stronger sources
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Source
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @codeCoverageIgnore
+ */
+class MicroTime implements \RandomLib\Source {
+
+ private $state = null;
+
+ /**
+ * Return an instance of Strength indicating the strength of the source
+ *
+ * @return Strength An instance of one of the strength classes
+ */
+ public static function getStrength() {
+ return new Strength(Strength::VERYLOW);
+ }
+
+ public function __construct() {
+ $state = '';
+ if (function_exists('posix_times')) {
+ $state .= serialize(posix_times());
+ }
+ $state .= getmypid() . memory_get_usage();
+ $state .= serialize($_ENV);
+ $this->state = hash('sha512', $state, true);
+ }
+
+ /**
+ * Generate a random string of the specified size
+ *
+ * @param int $size The size of the requested random string
+ *
+ * @return string A string of the requested size
+ */
+ public function generate($size) {
+ $result = '';
+ $seed = microtime() . memory_get_usage();
+ $this->state = hash('sha512', $this->state . $seed, true);
+ /**
+ * Make the generated randomness a bit better by forcing a GC run which
+ * should complete in a indeterminate amount of time, hence improving
+ * the strength of the randomness a bit. It's still not crypto-safe,
+ * but at least it's more difficult to predict.
+ */
+ gc_collect_cycles();
+ for ($i = 0; $i < $size; $i += 8) {
+ $seed = $this->state . microtime() . pack('N', $i);
+ $this->state = hash('sha512', $seed, true);
+ /**
+ * We only use the first 8 bytes here to prevent exposing the state
+ * in its entirety, which could potentially expose other random
+ * generations in the future (in the same process)...
+ */
+ $result .= substr($this->state, 0, 8);
+ }
+ return substr($result, 0, $size);
+ }
+
+}
64 lib/RandomLib/Source/OpenSSL.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * The OpenSSL Random Number Source
+ *
+ * This uses the OS's secure generator to generate high strength numbers
+ *
+ * PHP version 5.3
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Source
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright 2011 The Authors
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version Build @@version@@
+ */
+
+namespace RandomLib\Source;
+
+use SecurityLib\Strength;
+
+/**
+ * The OpenSSL Random Number Source
+ *
+ * This uses the OS's secure generator to generate high strength numbers
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Source
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @codeCoverageIgnore
+ */
+class OpenSSL implements \RandomLib\Source {
+
+ /**
+ * Return an instance of Strength indicating the strength of the source
+ *
+ * @return Strength An instance of one of the strength classes
+ */
+ public static function getStrength() {
+ return new Strength(Strength::HIGH);
+ }
+
+ /**
+ * Generate a random string of the specified size
+ *
+ * @param int $size The size of the requested random string
+ *
+ * @return string A string of the requested size
+ */
+ public function generate($size) {
+ if (!function_exists('openssl_random_pseudo_bytes') || $size < 1) {
+ return str_repeat(chr(0), $size);
+ }
+ /**
+ * Note, normally we would check the return of of $crypto_strong to
+ * ensure that we generated a good random string. However, since we're
+ * using this as one part of many sources a low strength random number
+ * shouldn't be much of an issue.
+ */
+ return openssl_random_pseudo_bytes($size);
+ }
+
+}
68 lib/RandomLib/Source/Rand.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * The Rand Random Number Source
+ *
+ * This source generates low strength random numbers by using the internal
+ * rand() function. By itself it is quite weak. However when combined with
+ * other sources it does provide significant benefit.
+ *
+ * PHP version 5.3
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Source
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright 2011 The Authors
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version Build @@version@@
+ */
+
+namespace RandomLib\Source;
+
+use SecurityLib\Strength;
+
+/**
+ * The Rand Random Number Source
+ *
+ * This source generates low strength random numbers by using the internal
+ * rand() function. By itself it is quite weak. However when combined with
+ * other sources it does provide significant benefit.
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Source
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @codeCoverageIgnore
+ */
+class Rand implements \RandomLib\Source {
+
+ /**
+ * Return an instance of Strength indicating the strength of the source
+ *
+ * @return Strength An instance of one of the strength classes
+ */
+ public static function getStrength() {
+ // Detect if Suhosin Hardened PHP patch is applied
+ if (defined('S_ALL')) {
+ return new Strength(Strength::LOW);
+ } else {
+ return new Strength(Strength::VERYLOW);
+ }
+ }
+
+ /**
+ * Generate a random string of the specified size
+ *
+ * @param int $size The size of the requested random string
+ *
+ * @return string A string of the requested size
+ */
+ public function generate($size) {
+ $result = '';
+ for ($i = 0; $i < $size; $i++) {
+ $result .= chr((rand() ^ rand()) % 256);
+ }
+ return $result;
+ }
+
+}
49 lib/RandomLib/Source/Random.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * The Random Random Number Source
+ *
+ * This uses the *nix /dev/random device to generate high strength numbers
+ *
+ * PHP version 5.3
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Source
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright 2011 The Authors
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version Build @@version@@
+ */
+
+namespace RandomLib\Source;
+
+use SecurityLib\Strength;
+
+/**
+ * The Random Random Number Source
+ *
+ * This uses the *nix /dev/random device to generate high strength numbers
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Source
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @codeCoverageIgnore
+ */
+class Random extends URandom {
+
+ /**
+ * @var string The file to read from
+ */
+ protected $file = '/dev/random';
+
+ /**
+ * Return an instance of Strength indicating the strength of the source
+ *
+ * @return Strength An instance of one of the strength classes
+ */
+ public static function getStrength() {
+ return new Strength(Strength::HIGH);
+ }
+
+}
72 lib/RandomLib/Source/URandom.php
@@ -0,0 +1,72 @@
+<?php
+/**
+ * The URandom Random Number Source
+ *
+ * This uses the *nix /dev/urandom device to generate medium strength numbers
+ *
+ * PHP version 5.3
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Source
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright 2011 The Authors
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version Build @@version@@
+ */
+
+namespace RandomLib\Source;
+
+use SecurityLib\Strength;
+
+/**
+ * The URandom Random Number Source
+ *
+ * This uses the *nix /dev/urandom device to generate medium strength numbers
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Source
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @codeCoverageIgnore
+ */
+class URandom implements \RandomLib\Source {
+
+ /**
+ * @var string The file to read from
+ */
+ protected $file = '/dev/urandom';
+
+ /**
+ * Return an instance of Strength indicating the strength of the source
+ *
+ * @return Strength An instance of one of the strength classes
+ */
+ public static function getStrength() {
+ return new Strength(Strength::MEDIUM);
+ }
+
+ /**
+ * Generate a random string of the specified size
+ *
+ * @param int $size The size of the requested random string
+ *
+ * @return string A string of the requested size
+ */
+ public function generate($size) {
+ if ($size == 0 || !file_exists($this->file)) {
+ return str_repeat(chr(0), $size);
+ }
+ $file = fopen($this->file, 'rb');
+ if (!$file) {
+ return str_repeat(chr(0), $size);
+ }
+ if (function_exists('stream_set_read_buffer')) {
+ stream_set_read_buffer($file, 0);
+ }
+ $result = fread($file, $size);
+ fclose($file);
+ return $result;
+ }
+
+}
61 lib/RandomLib/Source/UniqID.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * The UniqID Random Number Source
+ *
+ * This uses the internal `uniqid()` function to generate low strength random
+ * numbers.
+ *
+ * PHP version 5.3
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Source
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright 2011 The Authors
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @version Build @@version@@
+ */
+
+namespace RandomLib\Source;
+
+use SecurityLib\Strength;
+
+/**
+ * The UniqID Random Number Source
+ *
+ * This uses the internal `uniqid()` function to generate low strength random
+ * numbers.
+ *
+ * @category PHPCryptLib
+ * @package Random
+ * @subpackage Source
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @codeCoverageIgnore
+ */
+class UniqID implements \RandomLib\Source {
+
+ /**
+ * Return an instance of Strength indicating the strength of the source
+ *
+ * @return Strength An instance of one of the strength classes
+ */
+ public static function getStrength() {
+ return new Strength(Strength::LOW);
+ }
+
+ /**
+ * Generate a random string of the specified size
+ *
+ * @param int $size The size of the requested random string
+ *
+ * @return string A string of the requested size
+ */
+ public function generate($size) {
+ $result = '';
+ while (strlen($result) < $size) {
+ $result = uniqid($result, true);
+ }
+ return substr($result, 0, $size);
+ }
+
+}
29 phpunit.xml.dist
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit backupGlobals="true"
+ backupStaticAttributes="false"
+ bootstrap="test/bootstrap.php"
+ colors="false"
+ convertErrorsToExceptions="true"
+ convertNoticesToExceptions="true"
+ convertWarningsToExceptions="true"
+ forceCoversAnnotation="false"
+ mapTestClassNameToCoveredClassName="false"
+ processIsolation="false"
+ stopOnError="false"
+ stopOnFailure="false"
+ stopOnIncomplete="false"
+ stopOnSkipped="false"
+ testSuiteLoaderClass="PHPUnit_Runner_StandardTestSuiteLoader"
+ strict="false"
+ verbose="false">
+ <testsuites>
+ <testsuite name="Unit">
+ <directory>test/Unit</directory>
+ </testsuite>
+ </testsuites>
+ <filter>
+ <whitelist>
+ <directory suffix=".php">lib/</directory>
+ </whitelist>
+ </filter>
+</phpunit>
41 test/Mocks/AbstractMock.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * The interface that all hash implementations must implement
+ *
+ * PHP version 5.3
+ *
+ * @category PHPPasswordLib
+ * @package Hash
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright 2011 The Authors
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ * @license http://www.gnu.org/licenses/lgpl-2.1.html LGPL v 2.1
+ */
+
+namespace RandomLibTest\Mocks;
+
+/**
+ * The interface that all hash implementations must implement
+ *
+ * @category PHPPasswordLib
+ * @package Hash
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ */
+class AbstractMock {
+
+ protected $callbacks = array();
+
+ public static function init() {}
+
+ public function __construct(array $callbacks = array()) {
+ $this->callbacks = $callbacks;
+ }
+
+ public function __call($name, array $args = array()) {
+ if (isset($this->callbacks[$name])) {
+ return call_user_func_array($this->callbacks[$name], $args);
+ }
+ return null;
+ }
+
+}
61 test/Mocks/Random/Generator.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * The Mixer strategy interface.
+ *
+ * All mixing strategies must implement this interface
+ *
+ * PHP version 5.3
+ *
+ * @category PHPPasswordLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright 2011 The Authors
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ * @license http://www.gnu.org/licenses/lgpl-2.1.html LGPL v 2.1
+ */
+
+namespace RandomLibTest\Mocks\Random;
+
+
+/**
+ * The Mixer strategy interface.
+ *
+ * All mixing strategies must implement this interface
+ *
+ * @category PHPPasswordLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ */
+class Generator extends \RandomLib\Generator {
+ protected $callbacks = array();
+
+ public static function init() {}
+
+ public function __construct(array $callbacks = array()) {
+ $this->callbacks = $callbacks;
+ }
+
+ public function __call($name, array $args = array()) {
+ if (isset($this->callbacks[$name])) {
+ return call_user_func_array($this->callbacks[$name], $args);
+ }
+ return null;
+ }
+
+ public function addSource(\PasswordLib\Random\Source $source) {
+ return $this->__call('addSource', array($source));
+ }
+
+ public function generate($size) {
+ return $this->__call('generate', array($size));
+ }
+
+ public function generateInt($min = 0, $max = \PHP_INT_MAX) {
+ return $this->__call('generateInt', array($min, $max));
+ }
+
+ public function generateString($length, $chars = '') {
+ return $this->__call('generateString', array($length, $chars));
+ }
+
+}
71 test/Mocks/Random/Mixer.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * The Mixer strategy interface.
+ *
+ * All mixing strategies must implement this interface
+ *
+ * PHP version 5.3
+ *
+ * @category PHPPasswordLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright 2011 The Authors
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ * @license http://www.gnu.org/licenses/lgpl-2.1.html LGPL v 2.1
+ */
+
+namespace RandomLibTest\Mocks\Random;
+
+use SecurityLib\Strength;
+/**
+ * The Mixer strategy interface.
+ *
+ * All mixing strategies must implement this interface
+ *
+ * @category PHPPasswordLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ */
+class Mixer extends \RandomLibTest\Mocks\AbstractMock implements \RandomLib\Mixer {
+
+ public static $strength = null;
+
+ public static $test = true;
+
+ public static function init() {
+ static::$strength = new Strength(Strength::HIGH);
+ static::$test = true;
+ }
+
+ /**
+ * Return an instance of Strength indicating the strength of the mixer
+ *
+ * @return Strength An instance of one of the strength classes
+ */
+ public static function getStrength() {
+ return static::$strength;
+ }
+
+ /**
+ * Test to see if the mixer is available
+ *
+ * @return boolean If the mixer is available on the system
+ */
+ public static function test() {
+ return static::$test;
+ }
+
+ /**
+ * Mix the provided array of strings into a single output of the same size
+ *
+ * All elements of the array should be the same size.
+ *
+ * @param array $parts The parts to be mixed
+ *
+ * @return string The mixed result
+ */
+ public function mix(array $parts) {
+ return $this->__call('mix', array($parts));
+ }
+
+}
64 test/Mocks/Random/Source.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * The Random Number Source interface.
+ *
+ * All random number sources must implement this interface
+ *
+ * PHP version 5.3
+ *
+ * @category PHPPasswordLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ * @copyright 2011 The Authors
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ * @license http://www.gnu.org/licenses/lgpl-2.1.html LGPL v 2.1
+ */
+
+namespace RandomLibTest\Mocks\Random;
+
+use SecurityLib\Strength;
+
+/**
+ * The Random Number Source interface.
+ *
+ * All random number sources must implement this interface
+ *
+ * @category PHPPasswordLib
+ * @package Random
+ * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
+ */
+class Source extends \RandomLibTest\Mocks\AbstractMock implements \RandomLib\Source {
+
+ public static $strength = null;
+
+ public static function init() {
+ static::$strength = new Strength(Strength::VERYLOW);
+ }
+
+ /**
+ * Return an instance of Strength indicating the strength of the source
+ *
+ * @return Strength An instance of one of the strength classes
+ */
+ public static function getStrength() {
+ return static::$strength;
+ }
+
+
+ /**
+ * Generate a random string of the specified size
+ *
+ * Note: If the source fails to generate enough data, the result must be
+ * padded to the requested length.
+ *
+ * @param int $size The size of the requested random string
+ *
+ * @return string A string of the requested size
+ */
+ public function generate($size) {
+ return $this->__call('generate', array($size));
+ }
+
+}
+
+
45 test/Unit/RandomLib/FactoryTest.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace RandomLib;
+
+use SecurityLib\Strength;
+
+class FactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testConstruct() {
+ $factory = new Factory;
+ $this->assertTrue($factory instanceof Factory);
+ }
+
+ public function testGetGeneratorFallback() {
+ $factory = new Factory;
+ $generator = $factory->getGenerator(new Strength(Strength::VERYLOW));
+ $mixer = call_user_func(array(
+ get_class($generator->getMixer()),
+ 'getStrength'
+ ));
+ $this->assertTrue($mixer->compare(new Strength(Strength::LOW)) <= 0);
+ }
+
+ /**
+ * @covers RandomLib\Factory::getMediumStrengthGenerator
+ * @covers RandomLib\Factory::getGenerator
+ * @covers RandomLib\Factory::findMixer
+ */
+ public function testGetMediumStrengthGenerator() {
+ $factory = new Factory;
+ $generator = $factory->getMediumStrengthGenerator();
+ $this->assertTrue($generator instanceof Generator);
+ $mixer = call_user_func(array(
+ get_class($generator->getMixer()),
+ 'getStrength'
+ ));
+ $this->assertTrue($mixer->compare(new Strength(Strength::MEDIUM)) <= 0);
+ foreach ($generator->getSources() as $source) {
+ $strength = call_user_func(array(get_class($source), 'getStrength'));
+ $this->assertTrue($strength->compare(new Strength(Strength::MEDIUM)) >= 0);
+ }
+ }
+
+
+}
162 test/Unit/RandomLib/GeneratorTest.php
@@ -0,0 +1,162 @@
+<?php
+namespace RandomLib;
+
+class GeneratorTest extends \PHPUnit_Framework_TestCase {
+
+ protected $generator = null;
+ protected $mixer = null;
+ protected $sources = array();
+
+ public static function provideGenerate() {
+ return array(
+ array(0, ''),
+ array(1, chr(0)),
+ array(2, chr(1) . chr(1)),
+ array(3, chr(2) . chr(0) . chr(2)),
+ array(4, chr(3) . chr(3) . chr(3) . chr(3)),
+ );
+ }
+
+ public static function provideGenerateInt() {
+ return array(
+ array(1, 1, 1),
+ array(0, 1, 0),
+ array(0, 255, 0),
+ array(400, 655, 400),
+ array(0, 65535, 257),
+ array(65535, 131070, 65792),
+ array(0, 16777215, (2<<16) + 2),
+ array(-10, 0, -10),
+ array(-655, -400, -655),
+ array(-131070, -65535, -130813),
+ );
+ }
+
+ public static function provideGenerateIntRangeTest() {
+ return array(
+ array(0, 0),
+ array(0, 1),
+ array(1, 10000),
+ array(100000, \PHP_INT_MAX),
+ );
+ }
+
+ public static function provideGenerateStringTest() {
+ return array(
+ array(0, 'ab', ''),
+ array(1, 'ab', 'a'),
+ array(1, 'a', ''),
+ array(2, 'ab', 'aa'),
+ array(3, 'abc', 'aaa'),
+ array(8, '0123456789abcdef', '42024420'),
+ array(16, '0123456789abcdef', '9955115599995511'),
+ array(16, '', 'dd99dd11dd99dddd'),
+ );
+ }
+
+ public function setUp() {
+ $source1 = $this->getMock('RandomLib\Source');
+ $source1->expects($this->any())
+ ->method('generate')
+ ->will($this->returnCallback(function ($size) {
+ $r = '';
+ for ($i = 0; $i < $size; $i++) {
+ $r .= chr($i);
+ }
+ return $r;
+ }
+ ));
+ $source2 = $this->getMock('RandomLib\Source');
+ $source2->expects($this->any())
+ ->method('generate')
+ ->will($this->returnCallback(function ($size) {
+ $r = '';
+ for ($i = $size - 1; $i >= 0; $i--) {
+ $r .= chr($i);
+ }
+ return $r;
+ }
+ ));
+
+ $this->mixer = $this->getMock('RandomLib\Mixer');
+ $this->mixer->expects($this->any())
+ ->method('mix')
+ ->will($this->returnCallback(function(array $sources) {
+ if (empty($sources)) return '';
+ $start = array_pop($sources);
+ return array_reduce(
+ $sources,
+ function($el1, $el2) {
+ return $el1 ^ $el2;
+ },
+ $start
+ );
+ }));
+
+ $this->sources = array($source1, $source2);
+ $this->generator = new Generator($this->sources, $this->mixer);
+ }
+
+ public function testConstruct() {
+ $this->assertTrue($this->generator instanceof Generator);
+ }
+
+ public function testGetMixer() {
+ $this->assertSame($this->mixer, $this->generator->getMixer());
+ }
+
+ public function testGetSources() {
+ $this->assertSame($this->sources, $this->generator->getSources());
+ }
+
+ /**
+ * @dataProvider provideGenerate
+ */
+ public function testGenerate($size, $expect) {
+ $this->assertEquals($expect, $this->generator->generate($size));
+ }
+
+ /**
+ * @dataProvider provideGenerateInt
+ */
+ public function testGenerateInt($min, $max, $expect) {
+ $this->assertEquals($expect, $this->generator->generateInt($min, $max));
+ }
+
+ /**
+ * @dataProvider provideGenerateIntRangeTest
+ */
+ public function testGenerateIntRange($min, $max) {
+ $n = $this->generator->generateInt($min, $max);
+ $this->assertTrue($min <= $n);
+ $this->assertTrue($max >= $n);
+ }
+
+ /**
+ * @expectedException RangeException
+ */
+ public function testGenerateIntFail() {
+ $n = $this->generator->generateInt(-1, PHP_INT_MAX);
+ }
+
+ /**
+ */
+ public function testGenerateIntLargeTest() {
+ $bits = 30;
+ $expected = 50529027;
+ if (PHP_INT_MAX > 4000000000) {
+ $bits = 55;
+ $expected = 1693273676973062;
+ }
+ $n = $this->generator->generateInt(0, (int) pow(2, $bits));
+ $this->assertEquals($expected, $n);
+ }
+
+ /**
+ * @dataProvider provideGenerateStringTest
+ */
+ public function testGenerateString($length, $chars, $expected) {
+ $n = $this->generator->generateString($length, $chars);
+ $this->assertEquals($expected, $n);
+ }
+}
51 test/Unit/RandomLib/Mixer/HashTest.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace RandomLib\Mixer;
+
+use SecurityLib\Strength;
+
+class HashTest extends \PHPUnit_Framework_TestCase {
+
+ public static function provideMix() {
+ $data = array(
+ array(array(), ''),
+ array(array('', ''), ''),
+ array(array('a'), '61'),
+ // This expects 'b' because of how the mock hmac function works
+ array(array('a', 'b'), '9a'),
+ array(array('aa', 'ba'), '6e84'),
+ array(array('ab', 'bb'), 'b0cb'),
+ array(array('aa', 'bb'), 'ae8d'),
+ array(array('aa', 'bb', 'cc'), 'a14c'),
+ array(array('aabbcc', 'bbccdd', 'ccddee'), 'a8aff3939934'),
+ );
+ return $data;
+ }
+
+ public function testConstructWithoutArgument() {
+ $hash = new Hash;
+ $this->assertTrue($hash instanceof \RandomLib\Mixer);
+ }
+
+ public function testGetStrength() {
+ $strength = new Strength(Strength::MEDIUM);
+ $actual = Hash::getStrength();
+ $this->assertEquals($actual, $strength);
+ }
+
+ public function testTest() {
+ $actual = Hash::test();
+ $this->assertTrue($actual);
+ }
+
+ /**
+ * @dataProvider provideMix
+ */
+ public function testMix($parts, $result) {
+ $mixer = new Hash('md5');
+ $actual = $mixer->mix($parts);
+ $this->assertEquals($result, bin2hex($actual));
+ }
+
+
+}
36 test/Unit/RandomLib/Source/CAPICOMTest.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace RandomLib\Source;
+
+use SecurityLib\Strength;
+
+class CAPICOMTest extends \PHPUnit_Framework_TestCase {
+
+ public static function provideGenerate() {
+ $data = array();
+ for ($i = 0; $i < 100; $i += 25) {
+ $not = $i > 0 ? str_repeat(chr(0), $i) : chr(0);
+ $data[] = array($i, $not);
+ }
+ return $data;
+ }
+
+ /**
+ */
+ public function testGetStrength() {
+ $strength = new Strength(Strength::MEDIUM);
+ $actual = CAPICOM::getStrength();
+ $this->assertEquals($actual, $strength);
+ }
+
+ /**
+ * @dataProvider provideGenerate
+ * @group slow
+ */
+ public function testGenerate($length, $not) {
+ $rand = new CAPICOM;
+ $stub = $rand->generate($length);
+ $this->assertEquals($length, strlen($stub));
+ }
+
+}
42 test/Unit/RandomLib/Source/MTRandTest.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace RandomLib\Source;
+
+use SecurityLib\Strength;
+
+
+
+class MTRandTest extends \PHPUnit_Framework_TestCase {
+
+ public static function provideGenerate() {
+ $data = array();
+ for ($i = 0; $i < 100; $i += 5) {
+ $not = $i > 0 ? str_repeat(chr(0), $i) : chr(0);
+ $data[] = array($i, $not);
+ }
+ return $data;
+ }
+
+ /**
+ */
+ public function testGetStrength() {
+ if (defined('S_ALL')) {
+ $strength = new Strength(Strength::MEDIUM);
+ } else {
+ $strength = new Strength(Strength::LOW);
+ }
+ $actual = MTRand::getStrength();
+ $this->assertEquals($actual, $strength);
+ }
+
+ /**
+ * @dataProvider provideGenerate
+ */
+ public function testGenerate($length, $not) {
+ $rand = new MTRand;
+ $stub = $rand->generate($length);
+ $this->assertEquals($length, strlen($stub));
+ $this->assertNotEquals($not, $stub);
+ }
+
+}
38 test/Unit/RandomLib/Source/MicroTimeTest.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace RandomLib\Source;
+
+use SecurityLib\Strength;
+
+
+
+class MicroTimeTest extends \PHPUnit_Framework_TestCase {
+
+ public static function provideGenerate() {
+ $data = array();
+ for ($i = 0; $i < 100; $i += 5) {
+ $not = $i > 0 ? str_repeat(chr(0), $i) : chr(0);
+ $data[] = array($i, $not);
+ }
+ return $data;
+ }
+
+ /**
+ */
+ public function testGetStrength() {
+ $strength = new Strength(Strength::VERYLOW);
+ $actual = MicroTime::getStrength();
+ $this->assertEquals($actual, $strength);
+ }
+
+ /**
+ * @dataProvider provideGenerate
+ */
+ public function testGenerate($length, $not) {
+ $rand = new MicroTime;
+ $stub = $rand->generate($length);
+ $this->assertEquals($length, strlen($stub));
+ $this->assertNotEquals($not, $stub);
+ }
+
+}
40 test/Unit/RandomLib/Source/RandTest.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace RandomLib\Source;
+
+use SecurityLib\Strength;
+
+class RandTest extends \PHPUnit_Framework_TestCase {
+
+ public static function provideGenerate() {
+ $data = array();
+ for ($i = 0; $i < 100; $i += 5) {
+ $not = $i > 0 ? str_repeat(chr(0), $i) : chr(0);
+ $data[] = array($i, $not);
+ }
+ return $data;
+ }
+
+ /**
+ */
+ public function testGetStrength() {
+ if (defined('S_ALL')) {
+ $strength = new Strength(Strength::LOW);
+ } else {
+ $strength = new Strength(Strength::VERYLOW);
+ }
+ $actual = Rand::getStrength();
+ $this->assertEquals($actual, $strength);
+ }
+
+ /**
+ * @dataProvider provideGenerate
+ */
+ public function testGenerate($length, $not) {
+ $rand = new Rand;
+ $stub = $rand->generate($length);
+ $this->assertEquals($length, strlen($stub));
+ $this->assertNotEquals($not, $stub);
+ }
+
+}
43 test/Unit/RandomLib/Source/URandomTest.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace RandomLib\Source;
+
+use SecurityLib\Strength;
+
+
+
+class URandomTest extends \PHPUnit_Framework_TestCase {
+
+ public static function provideGenerate() {
+ $data = array();
+ for ($i = 0; $i < 10; $i += 5) {
+ $not = $i > 0 ? str_repeat(chr(0), $i) : chr(0);
+ $data[] = array($i, $not);
+ }
+ return $data;
+ }
+
+ /**
+ */
+ public function testGetStrength() {
+ $strength = new Strength(Strength::MEDIUM);
+ $actual = URandom::getStrength();
+ $this->assertEquals($actual, $strength);
+ }
+
+ /**
+ * @dataProvider provideGenerate
+ * @group slow
+ */
+ public function testGenerate($length, $not) {
+ $rand = new URandom;
+ $stub = $rand->generate($length);
+ $this->assertEquals($length, strlen($stub));
+ if (file_exists('/dev/urandom')) {
+ $this->assertNotEquals($not, $stub);
+ } else {
+ $this->assertEquals(str_repeat(chr(0), $length), $stub);
+ }
+ }
+
+}
36 test/Unit/RandomLib/Source/UniqIDTest.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace RandomLib\Source;
+
+use SecurityLib\Strength;
+
+class UniqIDTest extends \PHPUnit_Framework_TestCase {
+
+ public static function provideGenerate() {
+ $data = array();
+ for ($i = 0; $i < 100; $i += 5) {
+ $not = $i > 0 ? str_repeat(chr(0), $i) : chr(0);
+ $data[] = array($i, $not);
+ }
+ return $data;
+ }
+
+ /**
+ */
+ public function testGetStrength() {
+ $strength = new Strength(Strength::LOW);
+ $actual = UniqID::getStrength();
+ $this->assertEquals($actual, $strength);
+ }
+
+ /**
+ * @dataProvider provideGenerate
+ */
+ public function testGenerate($length, $not) {
+ $rand = new UniqID;
+ $stub = $rand->generate($length);
+ $this->assertEquals($length, strlen($stub));
+ $this->assertNotEquals($not, $stub);
+ }
+
+}
148 test/Vectors/Random/GeneratorTest.php
@@ -0,0 +1,148 @@
+<?php
+
+use RandomLibTest\Mocks\Random\Mixer;
+use RandomLibTest\Mocks\Random\Source;
+
+use RandomLib\Generator;
+
+class Vectors_Random_GeneratorTest extends PHPUnit_Framework_TestCase {
+
+ public static function provideGenerateInt() {
+ return array(
+