mirror of
https://github.com/phpseclib/phpseclib.git
synced 2024-12-28 12:10:59 +00:00
BigInteger: refactor random number generation code somewhat
This commit is contained in:
parent
7b5542cc8a
commit
442922ff0a
@ -3028,6 +3028,37 @@ class Math_BigInteger {
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random BigInteger
|
||||||
|
*
|
||||||
|
* Byte length is equal to $length. Uses crypt_random if it's loaded and mt_rand if it's not.
|
||||||
|
*
|
||||||
|
* @param Integer $length
|
||||||
|
* @return Math_BigInteger
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
function _random_number_helper($size)
|
||||||
|
{
|
||||||
|
$crypt_random = function_exists('crypt_random_string') || (!class_exists('Crypt_Random') && function_exists('crypt_random_string'));
|
||||||
|
if ($crypt_random) {
|
||||||
|
$random = crypt_random_string($size);
|
||||||
|
} else {
|
||||||
|
$random = '';
|
||||||
|
|
||||||
|
if ($size & 1) {
|
||||||
|
$random.= chr(mt_rand(0, 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
$blocks = $size >> 1;
|
||||||
|
for ($i = 0; $i < $blocks; ++$i) {
|
||||||
|
// mt_rand(-2147483648, 0x7FFFFFFF) always produces -2147483648 on some systems
|
||||||
|
$random.= pack('n', mt_rand(0, 0xFFFF));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Math_BigInteger($random, 256);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a random number
|
* Generate a random number
|
||||||
*
|
*
|
||||||
@ -3057,46 +3088,45 @@ class Math_BigInteger {
|
|||||||
$min = $temp;
|
$min = $temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
$max = $max->subtract($min);
|
static $one;
|
||||||
$max = ltrim($max->toBytes(), chr(0));
|
if (!isset($one)) {
|
||||||
$size = strlen($max) - 1;
|
$one = new Math_BigInteger(1);
|
||||||
|
|
||||||
$crypt_random = function_exists('crypt_random_string') || (!class_exists('Crypt_Random') && function_exists('crypt_random_string'));
|
|
||||||
if ($crypt_random) {
|
|
||||||
$random = crypt_random_string($size);
|
|
||||||
} else {
|
|
||||||
$random = '';
|
|
||||||
|
|
||||||
if ($size & 1) {
|
|
||||||
$random.= chr(mt_rand(0, 255));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$blocks = $size >> 1;
|
$max = $max->subtract($min->subtract($one));
|
||||||
for ($i = 0; $i < $blocks; ++$i) {
|
$size = strlen(ltrim($max->toBytes(), chr(0)));
|
||||||
// mt_rand(-2147483648, 0x7FFFFFFF) always produces -2147483648 on some systems
|
|
||||||
$random.= pack('n', mt_rand(0, 0xFFFF));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$fragment = new Math_BigInteger($random, 256);
|
/*
|
||||||
$leading = $fragment->compare(new Math_BigInteger(substr($max, 1), 256)) > 0 ?
|
doing $random % $max doesn't work because some numbers will be more likely to occur than others.
|
||||||
ord($max[0]) - 1 : ord($max[0]);
|
eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145
|
||||||
|
would produce 5 whereas the only value of random that could produce 139 would be 139. ie.
|
||||||
|
not all numbers would be equally likely. some would be more likely than others.
|
||||||
|
|
||||||
if (!$crypt_random) {
|
creating a whole new random number until you find one that is within the range doesn't work
|
||||||
$msb = chr(mt_rand(0, $leading));
|
because, for sufficiently small ranges, the likelihood that you'd get a number within that range
|
||||||
} else {
|
would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability
|
||||||
$cutoff = floor(0xFF / $leading) * $leading;
|
would be pretty high that $random would be greater than $max.
|
||||||
while (true) {
|
|
||||||
$msb = ord(crypt_random_string(1));
|
|
||||||
if ($msb <= $cutoff) {
|
|
||||||
$msb%= $leading;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$msb = chr($msb);
|
|
||||||
}
|
|
||||||
|
|
||||||
$random = new Math_BigInteger($msb . $random, 256);
|
phpseclib works around this using the technique described here:
|
||||||
|
|
||||||
|
http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string
|
||||||
|
*/
|
||||||
|
$random_max = new Math_BigInteger(chr(1) . str_repeat("\0", $size), 256);
|
||||||
|
$random = $this->_random_number_helper($size);
|
||||||
|
|
||||||
|
list($max_multiple) = $random_max->divide($max);
|
||||||
|
$max_multiple = $max_multiple->multiply($max);
|
||||||
|
|
||||||
|
while ($random->compare($max_multiple) >= 0) {
|
||||||
|
$random = $random->subtract($max_multiple);
|
||||||
|
$random_max = $random_max->subtract($max_multiple);
|
||||||
|
$random = $random->bitwise_leftShift(8);
|
||||||
|
$random = $random->add($this->_random_number_helper(1));
|
||||||
|
$random_max = $random_max->bitwise_leftShift(8);
|
||||||
|
list($max_multiple) = $random_max->divide($max);
|
||||||
|
$max_multiple = $max_multiple->multiply($max);
|
||||||
|
}
|
||||||
|
list(, $random) = $random->divide($max);
|
||||||
|
|
||||||
return $this->_normalize($random->add($min));
|
return $this->_normalize($random->add($min));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user