Skip to content

Commit

Permalink
Replace Zend\Math\Rand usage of mt_rand() with RandomLib generator an…
Browse files Browse the repository at this point in the history
…d SHA1 hash timing source for CPU drift entropy mining
  • Loading branch information
padraic authored and weierophinney committed Mar 13, 2013
1 parent 977f491 commit 97b98e7
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 23 deletions.
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
],
"homepage": "http://framework.zend.com/",
"license": "BSD-3-Clause",
"minimum-stability": "dev",
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"doctrine/common": ">=2.1",
"phpunit/PHPUnit": "3.7.*"
"phpunit/PHPUnit": "3.7.*",
"ircmaxell/random-lib": "dev-master"
},
"suggest": {
"doctrine/common": "Doctrine\\Common >=2.1 for annotation features",
Expand Down
70 changes: 48 additions & 22 deletions library/Zend/Math/Rand.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@
*/
abstract class Rand
{

/**
* Alternative random byte generator using RandomLib
*
* @var \RandomLib\Generator
*/
protected static $generator = null;

/**
* Generate random bytes using OpenSSL or Mcrypt and mt_rand() as fallback
*
Expand All @@ -27,34 +35,52 @@ public static function getBytes($length, $strong = false)
if ($length <= 0) {
return false;
}
if (extension_loaded('openssl')) {
$rand = openssl_random_pseudo_bytes($length, $secure);
if ($secure === true) {
return $rand;
$bytes = '';
if (function_exists('openssl_random_pseudo_bytes')
&& (version_compare(PHP_VERSION, '5.3.4') >= 0
|| strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) {
$bytes = openssl_random_pseudo_bytes($length, $usable);
if (true === $usable) {
return $bytes;
}
}
if (extension_loaded('mcrypt')) {
// PHP bug #55169
// @see https://bugs.php.net/bug.php?id=55169
if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' ||
version_compare(PHP_VERSION, '5.3.7') >= 0) {
$rand = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
if ($rand !== false && strlen($rand) === $length) {
return $rand;
}
} elseif (function_exists('mcrypt_create_iv')
&& (version_compare(PHP_VERSION, '5.3.7') >= 0
|| strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) {
$bytes = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
if ($bytes !== false && strlen($bytes) === $length) {
return $bytes;
}
}
if ($strong) {
throw new Exception\RuntimeException(
'This PHP environment doesn\'t support secure random number generation. ' .
'Please consider to install the OpenSSL and/or Mcrypt extensions'
$checkAlternatives = (file_exists('/dev/urandom') && is_readable('/dev/urandom'))
|| class_exists('\\COM', false);
if (true === $strong && false === $checkAlternatives) {
throw new Exception\RuntimeException (
'Unable to generate sufficiently strong random bytes'
);
}
$rand = '';
for ($i = 0; $i < $length; $i++) {
$rand .= chr(mt_rand(0, 255));
$generator = self::getAlternativeGenerator();
return $generator->generate($length);
}

public static function getAlternativeGenerator()
{
if (!is_null(self::$generator)) {
return self::$generator;
}
if (!class_exists('\\RandomLib\\Factory')) {
throw new Exception\RuntimeException(
'The RandomLib fallback pseudorandom number generator (PRNG) '
. ' must be installed in the absence of the OpenSSL and '
. 'Mcrypt extensions'
);
}
return $rand;
$factory = new \RandomLib\Factory;
$factory->registerSource(
'HashTiming',
'\Zend\Math\Source\HashTiming'
);
self::$generator = $factory->getMediumStrengthGenerator();
return self::$generator;
}

/**
Expand Down
115 changes: 115 additions & 0 deletions library/Zend/Math/Source/HashTiming.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Math\Source;

use RandomLib;
use SecurityLib\Strength;

/*
* Author:
* George Argyros <[email protected]>
*
* Copyright (c) 2012, George Argyros
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL GEORGE ARGYROS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
*
* The function is providing, at least at the systems tested :),
* $len bytes of entropy under any PHP installation or operating system.
* The execution time should be at most 10-20 ms in any system.
*
* Modified by Padraic Brady as part of Zend Framework to use 25% of the
* original version's iterations.
*/
class HashTiming 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::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 = '';
$entropy = '';
$msec_per_round = 400;
$bits_per_round = 2;
$total = $size;
$bytes = 0;
$hash_length = 20;
$rounds = 0;
while (strlen($result) < $size) {
$bytes = ($total > $hash_length)? $hash_length : $total;
$total -= $bytes;
for ($i=1; $i < 3; $i++) {
$t1 = microtime(true);
$seed = mt_rand();
for ($j=1; $j < 50; $j++) {
$seed = sha1($seed);
}
$t2 = microtime(true);
$entropy .= $t1 . $t2;
}
$div = (int) (($t2 - $t1) * 1000000);
if ($div <= 0) {
$div = 400;
}
$rounds = (int) ($msec_per_round * 50 / $div);
$iter = $bytes * (int) (ceil(8 / $bits_per_round));
for ($i = 0; $i < $iter; $i ++)
{
$t1 = microtime();
$seed = sha1(mt_rand());
for ($j = 0; $j < $rounds; $j++)
{
$seed = sha1($seed);
}
$t2 = microtime();
$entropy .= $t1 . $t2;
}
$result .= sha1($entropy, true);
}
return substr($result, 0, $size);
}

}
4 changes: 4 additions & 0 deletions library/Zend/Math/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "zendframework/zend-math",
"description": " ",
"license": "BSD-3-Clause",
"minimum-stability": "dev",
"keywords": [
"zf2",
"math"
Expand All @@ -15,6 +16,9 @@
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ircmaxell/random-lib": "Fallback random byte generator for Zend\\Math\\Rand if OpenSSL/Mcrypt extensions are unavailable"
}
"extra": {
"branch-alias": {
"dev-master": "2.1-dev",
Expand Down
25 changes: 25 additions & 0 deletions tests/ZendTest/Math/RandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@

namespace ZendTest\Math;

use Zend\Math;
use Zend\Math\Rand;
use RandomLib;

/**
* @category Zend
Expand Down Expand Up @@ -127,4 +129,27 @@ public function testGetStringBase64()
$this->assertTrue(preg_match('#^[0-9a-zA-Z+/]+$#', $rand) === 1);
}
}

public function testHashTimingSourceStrengthIsVeryLow()
{
$this->assertEquals(1, (string) Math\Source\HashTiming::getStrength());
}

public function testHashTimingSourceStrengthIsRandomWithCorrectLength()
{
$source = new Math\Source\HashTiming;
$rand = $source->generate(32);
$this->assertTrue(32 === strlen($rand));
$rand2 = $source->generate(32);
$this->assertNotEquals($rand, $rand2);
}

public function testAltGeneratorIsRandomWithCorrectLength()
{
$source = Math\Rand::getAlternativeGenerator();
$rand = $source->generate(32);
$this->assertTrue(32 === strlen($rand));
$rand2 = $source->generate(32);
$this->assertNotEquals($rand, $rand2);
}
}

0 comments on commit 97b98e7

Please sign in to comment.