Skip to content

Commit

Permalink
Begin PIE's fork of RandomLib.
Browse files Browse the repository at this point in the history
Goals of this fork:

- No PHP 7.1 warnings because of mcrypt.
- Better classification of source/mixer strength.
- Type-safety (verified by Psalm).
- Use libsodium.
- No more /dev/random, only /dev/urandom.
  • Loading branch information
paragonie-security committed Oct 5, 2017
1 parent e9e0204 commit eef39f5
Show file tree
Hide file tree
Showing 27 changed files with 451 additions and 154 deletions.
5 changes: 5 additions & 0 deletions .travis.yml
Expand Up @@ -4,6 +4,10 @@ sudo: required

matrix:
include:
- php: 7.2
env: LIBSODIUM=0
- php: 7.1
env: LIBSODIUM=1
- php: 7.0
env: LIBSODIUM=1
- php: 5.6
Expand All @@ -30,3 +34,4 @@ before_script:
script:
- make lint
- make test
- make typecheck
5 changes: 5 additions & 0 deletions Makefile
Expand Up @@ -23,3 +23,8 @@ cs:
.PHONY: test
test:
vendor/bin/phpunit


.PHONY: typecheck
typecheck:
vendor/bin/psalm
12 changes: 10 additions & 2 deletions composer.json
@@ -1,11 +1,16 @@
{
"name": "ircmaxell/random-lib",
"name": "paragonie/random-lib",
"type": "library",
"description": "A Library For Generating Secure Random Numbers",
"keywords": ["random", "random-numbers", "random-strings", "cryptography"],
"homepage": "https://github.com/ircmaxell/RandomLib",
"license": "MIT",
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
},
{
"name": "Anthony Ferrara",
"email": "ircmaxell@ircmaxell.com",
Expand All @@ -15,10 +20,13 @@
"require-dev": {
"mikey179/vfsStream": "^1.6",
"friendsofphp/php-cs-fixer": "^1.11",
"phpunit/phpunit": "^4.8|^5.0"
"phpunit/phpunit": "^4.8 || >=5.0.0 <5.4",
"vimeo/psalm": "^0|^1"
},
"require": {
"ircmaxell/security-lib": "^1.1",
"paragonie/random_compat": "^2",
"paragonie/sodium_compat": "^1.3",
"php": ">=5.3.2"
},
"autoload": {
Expand Down
21 changes: 21 additions & 0 deletions lib/RandomLib/AbstractMcryptMixer.php
Expand Up @@ -19,6 +19,7 @@
* @subpackage Mixer
*
* @author Anthony Ferrara <ircmaxell@ircmaxell.com>
* @author Paragon Initiative Enterprises <security@paragonie.com>
* @copyright 2013 The Authors
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*
Expand All @@ -35,6 +36,7 @@
*
* @author Anthony Ferrara <ircmaxell@ircmaxell.com>
* @author Chris Smith <chris@cs278.org>
* @author Paragon Initiative Enterprises <security@paragonie.com>
*/
abstract class AbstractMcryptMixer extends AbstractMixer
{
Expand Down Expand Up @@ -66,6 +68,13 @@ public static function test()
{
return extension_loaded('mcrypt');
}
/**
* @return bool
*/
public static function advisable()
{
return static::test() && PHP_VERSION_ID < 70100;
}

/**
* Construct mcrypt mixer
Expand Down Expand Up @@ -107,6 +116,12 @@ protected function getPartSize()
*/
protected function mixParts1($part1, $part2)
{
if (!\is_string($part1)) {
throw new \InvalidArgumentException('Expected a string');
}
if (!\is_string($part2)) {
throw new \InvalidArgumentException('Expected a string');
}
return $this->encryptBlock($part1, $part2);
}

Expand All @@ -115,6 +130,12 @@ protected function mixParts1($part1, $part2)
*/
protected function mixParts2($part1, $part2)
{
if (!\is_string($part1)) {
throw new \InvalidArgumentException('Expected a string');
}
if (!\is_string($part2)) {
throw new \InvalidArgumentException('Expected a string');
}
return $this->decryptBlock($part2, $part1);
}

Expand Down
68 changes: 46 additions & 22 deletions lib/RandomLib/AbstractMixer.php
Expand Up @@ -18,6 +18,7 @@
* @package Random
*
* @author Anthony Ferrara <ircmaxell@ircmaxell.com>
* @author Paragon Initiative Enterprises <security@paragonie.com>
* @copyright 2011 The Authors
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*
Expand All @@ -36,6 +37,7 @@
* @package Random
*
* @author Anthony Ferrara <ircmaxell@ircmaxell.com>
* @author Paragon Initiative Enterprises <security@paragonie.com>
*/
abstract class AbstractMixer implements \RandomLib\Mixer
{
Expand Down Expand Up @@ -67,29 +69,44 @@ abstract protected function mixParts1($part1, $part2);
*/
abstract protected function mixParts2($part1, $part2);

/**
* @return bool
*/
public static function advisable()
{
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
* @param array<int, string> $parts The parts to be mixed
*
* @return string The mixed result
* @psalm-suppress MixedArgument
*/
public function mix(array $parts)
{
if (empty($parts)) {
return '';
}
/** @var int $len */
$len = Util::safeStrlen($parts[0]);
/** @var array<int, array<int, string>> $parts */
$parts = $this->normalizeParts($parts);
$stringSize = count($parts[0]);
$partsSize = count($parts);
$stringSize = \count($parts[0]);
$partsSize = \count($parts);
/** @var string $result */
$result = '';
/** @var int $offset */
$offset = 0;
for ($i = 0; $i < $stringSize; $i++) {
$stub = $parts[$offset][$i];
for ($j = 1; $j < $partsSize; $j++) {
for ($i = 0; $i < $stringSize; ++$i) {
/** @var string $stub */
$stub = (string) $parts[$offset][$i];
for ($j = 1; $j < $partsSize; ++$j) {
/** @var string $newKey */
$newKey = $parts[($j + $offset) % $partsSize][$i];
//Alternately mix the output for each source
if ($j % 2 == 1) {
Expand All @@ -102,7 +119,9 @@ public function mix(array $parts)
$offset = ($offset + 1) % $partsSize;
}

return Util::safeSubstr($result, 0, $len);
/** @var string $final */
$final = Util::safeSubstr($result, 0, $len);
return $final;
}

/**
Expand All @@ -111,16 +130,27 @@ public function mix(array $parts)
* This will make all parts the same length and a multiple
* of the part size
*
* @param array $parts The parts to normalize
* @param array<int, string> $parts The parts to normalize
*
* @return array The normalized and split parts
* @psalm-suppress MissingClosureReturnType
* @psalm-suppress UntypedParam
* @psalm-suppress MissingArgument
*/
protected function normalizeParts(array $parts)
{
$blockSize = $this->getPartSize();
$callback = function ($value) {
return Util::safeStrlen($value);
$callback =
/**
* @var callable $callback
* @param string $value
* @return int
*/
function ($value) {
return (int) Util::safeStrlen($value);
};

/** @var int $maxSize */
$maxSize = max(array_map($callback, $parts));
if ($maxSize % $blockSize != 0) {
$maxSize += $blockSize - ($maxSize % $blockSize);
Expand All @@ -133,6 +163,12 @@ protected function normalizeParts(array $parts)
return $parts;
}

/**
* @param string $string
* @param int $size
* @param string $character
* @return string
*/
private function str_pad($string, $size, $character)
{
$start = Util::safeStrlen($string);
Expand All @@ -143,16 +179,4 @@ private function str_pad($string, $size, $character)

return Util::safeSubstr($string, 0, $size);
}

private function str_split($string, $size)
{
$blocks = array();
$length = Util::safeStrlen($string);
$parts = ceil($length / $size);
for ($i = 0; $i < $parts; $i++) {
$blocks[] = Util::safeSubstr($string, $i * $length, $length);
}

return $blocks;
}
}
3 changes: 2 additions & 1 deletion lib/RandomLib/AbstractSource.php
Expand Up @@ -16,6 +16,7 @@
* @package Random
*
* @author Anthony Ferrara <ircmaxell@ircmaxell.com>
* @author Paragon Initiative Enterprises <security@paragonie.com>
* @copyright 2011 The Authors
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*
Expand Down Expand Up @@ -64,6 +65,6 @@ public static function isSupported()
*/
protected static function emptyValue($size)
{
return str_repeat(chr(0), $size);
return (string) \str_repeat(\chr(0), $size);
}
}
39 changes: 27 additions & 12 deletions lib/RandomLib/Factory.php
Expand Up @@ -20,6 +20,7 @@
* @package Random
*
* @author Anthony Ferrara <ircmaxell@ircmaxell.com>
* @author Paragon Initiative Enterprises <security@paragonie.com>
* @copyright 2011 The Authors
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*
Expand All @@ -38,17 +39,18 @@
* @package Random
*
* @author Anthony Ferrara <ircmaxell@ircmaxell.com>
* @author Paragon Initiative Enterprises <security@paragonie.com>
*/
class Factory extends \SecurityLib\AbstractFactory
{

/**
* @var array A list of available random number mixing strategies
* @var array<int, Mixer> A list of available random number mixing strategies
*/
protected $mixers = array();

/**
* @var array A list of available random number sources
* @var array<int, Source> A list of available random number sources
*/
protected $sources = array();

Expand All @@ -68,7 +70,7 @@ public function __construct()
*
* @param Strength $strength The requested strength of the random number
*
* @throws RuntimeException If an appropriate mixing strategy isn't found
* @throws \RuntimeException If an appropriate mixing strategy isn't found
*
* @return Generator The instantiated generator
*/
Expand Down Expand Up @@ -125,7 +127,7 @@ public function getMediumStrengthGenerator()
/**
* Get all loaded mixing strategies
*
* @return array An array of mixers
* @return array<int, Mixer> An array of mixers
*/
public function getMixers()
{
Expand All @@ -135,7 +137,7 @@ public function getMixers()
/**
* Get all loaded random number sources
*
* @return array An array of sources
* @return array<int, Source> An array of sources
*/
public function getSources()
{
Expand Down Expand Up @@ -189,16 +191,21 @@ public function registerSource($name, $class)
*
* @param Strength $strength The strength mixer to find
*
* @throws RuntimeException if a valid source cannot be found
* @throws \RuntimeException if a valid source cannot be found
*
* @return Source The found source
* @return array<int, Source> The found source
*/
protected function findSources(\SecurityLib\Strength $strength)
{
/** @var array<int, Source> $sources */
$sources = array();
foreach ($this->getSources() as $source) {
if ($strength->compare($source::getStrength()) <= 0 && $source::isSupported()) {
$sources[] = new $source();
/** @var Source $obj */
$obj = new $source();
if ($obj instanceof Source) {
$sources[] = $obj;
}
}
}

Expand All @@ -214,30 +221,38 @@ protected function findSources(\SecurityLib\Strength $strength)
*
* @param Strength $strength The strength mixer to find
*
* @throws RuntimeException if a valid mixer cannot be found
* @throws \RuntimeException if a valid mixer cannot be found
*
* @return Mixer The found mixer
*/
protected function findMixer(\SecurityLib\Strength $strength)
{
/** @var Mixer|null $newMixer */
$newMixer = null;
/** @var Mixer|null $fallback */
$fallback = null;
foreach ($this->getMixers() as $mixer) {
if (!$mixer::test()) {
if (!$mixer::test() || !$mixer::advisable()) {
continue;
}
if ($strength->compare($mixer::getStrength()) == 0) {
/** @var Mixer $newMixer */
$newMixer = new $mixer();
} elseif ($strength->compare($mixer::getStrength()) == 1) {
/** @var Mixer $fallback */
$fallback = new $mixer();
}
}
if (is_null($newMixer)) {
if (is_null($fallback)) {
if (\is_null($newMixer)) {
if (\is_null($fallback)) {
throw new \RuntimeException('Could not find mixer');
} elseif (!($fallback instanceof Mixer)) {
throw new \RuntimeException('Invalid Mixer');
}

return $fallback;
} elseif (!($newMixer instanceof Mixer)) {
throw new \RuntimeException('Invalid Mixer');
}

return $newMixer;
Expand Down

0 comments on commit eef39f5

Please sign in to comment.