Viel neues
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Barrett Modular Exponentiation Engine
|
||||
*
|
||||
* PHP version 5 and 7
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2017 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://pear.php.net/package/Math_BigInteger
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
|
||||
|
||||
use phpseclib3\Math\BigInteger\Engines\PHP;
|
||||
use phpseclib3\Math\BigInteger\Engines\PHP\Base;
|
||||
|
||||
/**
|
||||
* PHP Barrett Modular Exponentiation Engine
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
*/
|
||||
abstract class Barrett extends Base
|
||||
{
|
||||
/**
|
||||
* Barrett Modular Reduction
|
||||
*
|
||||
* See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} /
|
||||
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly,
|
||||
* so as not to require negative numbers (initially, this script didn't support negative numbers).
|
||||
*
|
||||
* Employs "folding", as described at
|
||||
* {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from
|
||||
* it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x."
|
||||
*
|
||||
* Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that
|
||||
* usable on account of (1) its not using reasonable radix points as discussed in
|
||||
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable
|
||||
* radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that
|
||||
* (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line
|
||||
* comments for details.
|
||||
*
|
||||
* @param class-string<PHP> $class
|
||||
*/
|
||||
protected static function reduce(array $n, array $m, string $class): array
|
||||
{
|
||||
static $cache = [
|
||||
self::VARIABLE => [],
|
||||
self::DATA => [],
|
||||
];
|
||||
|
||||
$m_length = count($m);
|
||||
|
||||
// if (self::compareHelper($n, $static::square($m)) >= 0) {
|
||||
if (count($n) >= 2 * $m_length) {
|
||||
$lhs = new $class();
|
||||
$rhs = new $class();
|
||||
$lhs->value = $n;
|
||||
$rhs->value = $m;
|
||||
[, $temp] = $lhs->divide($rhs);
|
||||
return $temp->value;
|
||||
}
|
||||
|
||||
// if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced
|
||||
if ($m_length < 5) {
|
||||
return self::regularBarrett($n, $m, $class);
|
||||
}
|
||||
// n = 2 * m.length
|
||||
|
||||
if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
|
||||
$key = count($cache[self::VARIABLE]);
|
||||
$cache[self::VARIABLE][] = $m;
|
||||
|
||||
$lhs = new $class();
|
||||
$lhs_value = &$lhs->value;
|
||||
$lhs_value = self::array_repeat(0, $m_length + ($m_length >> 1));
|
||||
$lhs_value[] = 1;
|
||||
$rhs = new $class();
|
||||
$rhs->value = $m;
|
||||
|
||||
[$u, $m1] = $lhs->divide($rhs);
|
||||
$u = $u->value;
|
||||
$m1 = $m1->value;
|
||||
|
||||
$cache[self::DATA][] = [
|
||||
'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1)
|
||||
'm1' => $m1, // m.length
|
||||
];
|
||||
} else {
|
||||
extract($cache[self::DATA][$key]);
|
||||
}
|
||||
|
||||
$cutoff = $m_length + ($m_length >> 1);
|
||||
$lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1)
|
||||
$msd = array_slice($n, $cutoff); // m.length >> 1
|
||||
|
||||
$lsd = self::trim($lsd);
|
||||
$temp = $class::multiplyHelper($msd, false, $m1, false); // m.length + (m.length >> 1)
|
||||
$n = $class::addHelper($lsd, false, $temp[self::VALUE], false); // m.length + (m.length >> 1) + 1 (so basically we're adding two same length numbers)
|
||||
//if ($m_length & 1) {
|
||||
// return self::regularBarrett($n[self::VALUE], $m, $class);
|
||||
//}
|
||||
|
||||
// (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2
|
||||
$temp = array_slice($n[self::VALUE], $m_length - 1);
|
||||
// if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2
|
||||
// if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1
|
||||
$temp = $class::multiplyHelper($temp, false, $u, false);
|
||||
// if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1
|
||||
// if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1)
|
||||
$temp = array_slice($temp[self::VALUE], ($m_length >> 1) + 1);
|
||||
// if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1
|
||||
// if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1)
|
||||
$temp = $class::multiplyHelper($temp, false, $m, false);
|
||||
|
||||
// at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit
|
||||
// number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop
|
||||
// following this comment would loop a lot (hence our calling _regularBarrett() in that situation).
|
||||
|
||||
$result = $class::subtractHelper($n[self::VALUE], false, $temp[self::VALUE], false);
|
||||
|
||||
while (self::compareHelper($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) {
|
||||
$result = $class::subtractHelper($result[self::VALUE], $result[self::SIGN], $m, false);
|
||||
}
|
||||
|
||||
return $result[self::VALUE];
|
||||
}
|
||||
|
||||
/**
|
||||
* (Regular) Barrett Modular Reduction
|
||||
*
|
||||
* For numbers with more than four digits BigInteger::_barrett() is faster. The difference between that and this
|
||||
* is that this function does not fold the denominator into a smaller form.
|
||||
*/
|
||||
private static function regularBarrett(array $x, array $n, string $class): array
|
||||
{
|
||||
static $cache = [
|
||||
self::VARIABLE => [],
|
||||
self::DATA => [],
|
||||
];
|
||||
|
||||
$n_length = count($n);
|
||||
|
||||
if (count($x) > 2 * $n_length) {
|
||||
$lhs = new $class();
|
||||
$rhs = new $class();
|
||||
$lhs->value = $x;
|
||||
$rhs->value = $n;
|
||||
[, $temp] = $lhs->divide($rhs);
|
||||
return $temp->value;
|
||||
}
|
||||
|
||||
if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
|
||||
$key = count($cache[self::VARIABLE]);
|
||||
$cache[self::VARIABLE][] = $n;
|
||||
$lhs = new $class();
|
||||
$lhs_value = &$lhs->value;
|
||||
$lhs_value = self::array_repeat(0, 2 * $n_length);
|
||||
$lhs_value[] = 1;
|
||||
$rhs = new $class();
|
||||
$rhs->value = $n;
|
||||
[$temp, ] = $lhs->divide($rhs); // m.length
|
||||
$cache[self::DATA][] = $temp->value;
|
||||
}
|
||||
|
||||
// 2 * m.length - (m.length - 1) = m.length + 1
|
||||
$temp = array_slice($x, $n_length - 1);
|
||||
// (m.length + 1) + m.length = 2 * m.length + 1
|
||||
$temp = $class::multiplyHelper($temp, false, $cache[self::DATA][$key], false);
|
||||
// (2 * m.length + 1) - (m.length - 1) = m.length + 2
|
||||
$temp = array_slice($temp[self::VALUE], $n_length + 1);
|
||||
|
||||
// m.length + 1
|
||||
$result = array_slice($x, 0, $n_length + 1);
|
||||
// m.length + 1
|
||||
$temp = self::multiplyLower($temp, false, $n, false, $n_length + 1, $class);
|
||||
// $temp == array_slice($class::regularMultiply($temp, false, $n, false)->value, 0, $n_length + 1)
|
||||
|
||||
if (self::compareHelper($result, false, $temp[self::VALUE], $temp[self::SIGN]) < 0) {
|
||||
$corrector_value = self::array_repeat(0, $n_length + 1);
|
||||
$corrector_value[count($corrector_value)] = 1;
|
||||
$result = $class::addHelper($result, false, $corrector_value, false);
|
||||
$result = $result[self::VALUE];
|
||||
}
|
||||
|
||||
// at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits
|
||||
$result = $class::subtractHelper($result, false, $temp[self::VALUE], $temp[self::SIGN]);
|
||||
while (self::compareHelper($result[self::VALUE], $result[self::SIGN], $n, false) > 0) {
|
||||
$result = $class::subtractHelper($result[self::VALUE], $result[self::SIGN], $n, false);
|
||||
}
|
||||
|
||||
return $result[self::VALUE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs long multiplication up to $stop digits
|
||||
*
|
||||
* If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved.
|
||||
*
|
||||
* @see self::regularBarrett()
|
||||
*/
|
||||
private static function multiplyLower(array $x_value, bool $x_negative, array $y_value, bool $y_negative, int $stop, string $class): array
|
||||
{
|
||||
$x_length = count($x_value);
|
||||
$y_length = count($y_value);
|
||||
|
||||
if (!$x_length || !$y_length) { // a 0 is being multiplied
|
||||
return [
|
||||
self::VALUE => [],
|
||||
self::SIGN => false,
|
||||
];
|
||||
}
|
||||
|
||||
if ($x_length < $y_length) {
|
||||
$temp = $x_value;
|
||||
$x_value = $y_value;
|
||||
$y_value = $temp;
|
||||
|
||||
$x_length = count($x_value);
|
||||
$y_length = count($y_value);
|
||||
}
|
||||
|
||||
$product_value = self::array_repeat(0, $x_length + $y_length);
|
||||
|
||||
// the following for loop could be removed if the for loop following it
|
||||
// (the one with nested for loops) initially set $i to 0, but
|
||||
// doing so would also make the result in one set of unnecessary adds,
|
||||
// since on the outermost loops first pass, $product->value[$k] is going
|
||||
// to always be 0
|
||||
|
||||
$carry = 0;
|
||||
|
||||
for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i
|
||||
$temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
|
||||
$carry = $class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
|
||||
$product_value[$j] = (int) ($temp - $class::BASE_FULL * $carry);
|
||||
}
|
||||
|
||||
if ($j < $stop) {
|
||||
$product_value[$j] = $carry;
|
||||
}
|
||||
|
||||
// the above for loop is what the previous comment was talking about. the
|
||||
// following for loop is the "one with nested for loops"
|
||||
|
||||
for ($i = 1; $i < $y_length; ++$i) {
|
||||
$carry = 0;
|
||||
|
||||
for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) {
|
||||
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
|
||||
$carry = $class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
|
||||
$product_value[$k] = (int) ($temp - $class::BASE_FULL * $carry);
|
||||
}
|
||||
|
||||
if ($k < $stop) {
|
||||
$product_value[$k] = $carry;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
self::VALUE => self::trim($product_value),
|
||||
self::SIGN => $x_negative != $y_negative,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Classic Modular Exponentiation Engine
|
||||
*
|
||||
* PHP version 5 and 7
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2017 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://pear.php.net/package/Math_BigInteger
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
|
||||
|
||||
use phpseclib3\Math\BigInteger\Engines\PHP\Base;
|
||||
|
||||
/**
|
||||
* PHP Classic Modular Exponentiation Engine
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
*/
|
||||
abstract class Classic extends Base
|
||||
{
|
||||
/**
|
||||
* Regular Division
|
||||
*/
|
||||
protected static function reduce(array $x, array $n, string $class): array
|
||||
{
|
||||
$lhs = new $class();
|
||||
$lhs->value = $x;
|
||||
$rhs = new $class();
|
||||
$rhs->value = $n;
|
||||
[, $temp] = $lhs->divide($rhs);
|
||||
return $temp->value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Dynamic Barrett Modular Exponentiation Engine
|
||||
*
|
||||
* PHP version 5 and 7
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2017 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://pear.php.net/package/Math_BigInteger
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
|
||||
|
||||
use phpseclib3\Math\BigInteger\Engines\PHP;
|
||||
use phpseclib3\Math\BigInteger\Engines\PHP\Base;
|
||||
|
||||
/**
|
||||
* PHP Dynamic Barrett Modular Exponentiation Engine
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
*/
|
||||
abstract class EvalBarrett extends Base
|
||||
{
|
||||
/**
|
||||
* Custom Reduction Function
|
||||
*
|
||||
* @see self::generateCustomReduction
|
||||
*/
|
||||
private static $custom_reduction;
|
||||
|
||||
/**
|
||||
* Barrett Modular Reduction
|
||||
*
|
||||
* This calls a dynamically generated loop unrolled function that's specific to a given modulo.
|
||||
* Array lookups are avoided as are if statements testing for how many bits the host OS supports, etc.
|
||||
*/
|
||||
protected static function reduce(array $n, array $m, string $class): array
|
||||
{
|
||||
$inline = self::$custom_reduction;
|
||||
return $inline($n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Custom Reduction
|
||||
*/
|
||||
protected static function generateCustomReduction(PHP $m, string $class): callable
|
||||
{
|
||||
$m_length = count($m->value);
|
||||
|
||||
if ($m_length < 5) {
|
||||
$code = '
|
||||
$lhs = new ' . $class . '();
|
||||
$lhs->value = $x;
|
||||
$rhs = new ' . $class . '();
|
||||
$rhs->value = [' .
|
||||
implode(',', array_map(self::class . '::float2string', $m->value)) . '];
|
||||
list(, $temp) = $lhs->divide($rhs);
|
||||
return $temp->value;
|
||||
';
|
||||
eval('$func = function ($x) { ' . $code . '};');
|
||||
self::$custom_reduction = $func;
|
||||
//self::$custom_reduction = \Closure::bind($func, $m, $class);
|
||||
return $func;
|
||||
}
|
||||
|
||||
$lhs = new $class();
|
||||
$lhs_value = &$lhs->value;
|
||||
|
||||
$lhs_value = self::array_repeat(0, $m_length + ($m_length >> 1));
|
||||
$lhs_value[] = 1;
|
||||
$rhs = new $class();
|
||||
|
||||
[$u, $m1] = $lhs->divide($m);
|
||||
|
||||
if ($class::BASE != 26) {
|
||||
$u = $u->value;
|
||||
} else {
|
||||
$lhs_value = self::array_repeat(0, 2 * $m_length);
|
||||
$lhs_value[] = 1;
|
||||
$rhs = new $class();
|
||||
|
||||
[$u] = $lhs->divide($m);
|
||||
$u = $u->value;
|
||||
}
|
||||
|
||||
$m = $m->value;
|
||||
$m1 = $m1->value;
|
||||
|
||||
$cutoff = count($m) + (count($m) >> 1);
|
||||
|
||||
$code = '
|
||||
if (count($n) >= ' . (2 * count($m)) . ') {
|
||||
$lhs = new ' . $class . '();
|
||||
$rhs = new ' . $class . '();
|
||||
$lhs->value = $n;
|
||||
$rhs->value = [' .
|
||||
implode(',', array_map(self::class . '::float2string', $m)) . '];
|
||||
list(, $temp) = $lhs->divide($rhs);
|
||||
return $temp->value;
|
||||
}
|
||||
|
||||
$lsd = array_slice($n, 0, ' . $cutoff . ');
|
||||
$msd = array_slice($n, ' . $cutoff . ');';
|
||||
|
||||
$code .= self::generateInlineTrim('msd');
|
||||
$code .= self::generateInlineMultiply('msd', $m1, 'temp', $class);
|
||||
$code .= self::generateInlineAdd('lsd', 'temp', 'n', $class);
|
||||
|
||||
$code .= '$temp = array_slice($n, ' . (count($m) - 1) . ');';
|
||||
$code .= self::generateInlineMultiply('temp', $u, 'temp2', $class);
|
||||
$code .= self::generateInlineTrim('temp2');
|
||||
|
||||
$code .= $class::BASE == 26 ?
|
||||
'$temp = array_slice($temp2, ' . (count($m) + 1) . ');' :
|
||||
'$temp = array_slice($temp2, ' . ((count($m) >> 1) + 1) . ');';
|
||||
$code .= self::generateInlineMultiply('temp', $m, 'temp2', $class);
|
||||
$code .= self::generateInlineTrim('temp2');
|
||||
|
||||
/*
|
||||
if ($class::BASE == 26) {
|
||||
$code.= '$n = array_slice($n, 0, ' . (count($m) + 1) . ');
|
||||
$temp2 = array_slice($temp2, 0, ' . (count($m) + 1) . ');';
|
||||
}
|
||||
*/
|
||||
|
||||
$code .= self::generateInlineSubtract2('n', 'temp2', 'temp', $class);
|
||||
|
||||
$subcode = self::generateInlineSubtract1('temp', $m, 'temp2', $class);
|
||||
$subcode .= '$temp = $temp2;';
|
||||
|
||||
$code .= self::generateInlineCompare($m, 'temp', $subcode);
|
||||
|
||||
$code .= 'return $temp;';
|
||||
|
||||
eval('$func = function ($n) { ' . $code . '};');
|
||||
|
||||
self::$custom_reduction = $func;
|
||||
|
||||
return $func;
|
||||
|
||||
//self::$custom_reduction = \Closure::bind($func, $m, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline Trim
|
||||
*
|
||||
* Removes leading zeros
|
||||
*/
|
||||
private static function generateInlineTrim(string $name): string
|
||||
{
|
||||
return '
|
||||
for ($i = count($' . $name . ') - 1; $i >= 0; --$i) {
|
||||
if ($' . $name . '[$i]) {
|
||||
break;
|
||||
}
|
||||
unset($' . $name . '[$i]);
|
||||
}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline Multiply (unknown, known)
|
||||
*/
|
||||
private static function generateInlineMultiply(string $input, array $arr, string $output, string $class): string
|
||||
{
|
||||
if (!count($arr)) {
|
||||
return 'return [];';
|
||||
}
|
||||
|
||||
$regular = '
|
||||
$length = count($' . $input . ');
|
||||
if (!$length) {
|
||||
$' . $output . ' = [];
|
||||
}else{
|
||||
$' . $output . ' = array_fill(0, $length + ' . count($arr) . ', 0);
|
||||
$carry = 0;';
|
||||
|
||||
for ($i = 0; $i < count($arr); $i++) {
|
||||
$regular .= '
|
||||
$subtemp = $' . $input . '[0] * ' . $arr[$i];
|
||||
$regular .= $i ? ' + $carry;' : ';';
|
||||
|
||||
$regular .= '$carry = ';
|
||||
$regular .= $class::BASE === 26 ?
|
||||
'intval($subtemp / 0x4000000);' :
|
||||
'$subtemp >> 31;';
|
||||
$regular .=
|
||||
'$' . $output . '[' . $i . '] = ';
|
||||
if ($class::BASE === 26) {
|
||||
$regular .= '(int) (';
|
||||
}
|
||||
$regular .= '$subtemp - ' . $class::BASE_FULL . ' * $carry';
|
||||
$regular .= $class::BASE === 26 ? ');' : ';';
|
||||
}
|
||||
|
||||
$regular .= '$' . $output . '[' . count($arr) . '] = $carry;';
|
||||
|
||||
$regular .= '
|
||||
for ($i = 1; $i < $length; ++$i) {';
|
||||
|
||||
for ($j = 0; $j < count($arr); $j++) {
|
||||
$regular .= $j ? '$k++;' : '$k = $i;';
|
||||
$regular .= '
|
||||
$subtemp = $' . $output . '[$k] + $' . $input . '[$i] * ' . $arr[$j];
|
||||
$regular .= $j ? ' + $carry;' : ';';
|
||||
|
||||
$regular .= '$carry = ';
|
||||
$regular .= $class::BASE === 26 ?
|
||||
'intval($subtemp / 0x4000000);' :
|
||||
'$subtemp >> 31;';
|
||||
$regular .=
|
||||
'$' . $output . '[$k] = ';
|
||||
if ($class::BASE === 26) {
|
||||
$regular .= '(int) (';
|
||||
}
|
||||
$regular .= '$subtemp - ' . $class::BASE_FULL . ' * $carry';
|
||||
$regular .= $class::BASE === 26 ? ');' : ';';
|
||||
}
|
||||
|
||||
$regular .= '$' . $output . '[++$k] = $carry; $carry = 0;';
|
||||
|
||||
$regular .= '}}';
|
||||
|
||||
//if (count($arr) < 2 * self::KARATSUBA_CUTOFF) {
|
||||
//}
|
||||
|
||||
return $regular;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline Addition
|
||||
*/
|
||||
private static function generateInlineAdd(string $x, string $y, string $result, string $class): string
|
||||
{
|
||||
$code = '
|
||||
$length = max(count($' . $x . '), count($' . $y . '));
|
||||
$' . $result . ' = array_pad($' . $x . ', $length + 1, 0);
|
||||
$_' . $y . ' = array_pad($' . $y . ', $length, 0);
|
||||
$carry = 0;
|
||||
for ($i = 0, $j = 1; $j < $length; $i+=2, $j+=2) {
|
||||
$sum = ($' . $result . '[$j] + $_' . $y . '[$j]) * ' . $class::BASE_FULL . '
|
||||
+ $' . $result . '[$i] + $_' . $y . '[$i] +
|
||||
$carry;
|
||||
$carry = $sum >= ' . self::float2string($class::MAX_DIGIT2) . ';
|
||||
$sum = $carry ? $sum - ' . self::float2string($class::MAX_DIGIT2) . ' : $sum;';
|
||||
|
||||
$code .= $class::BASE === 26 ?
|
||||
'$upper = intval($sum / 0x4000000); $' . $result . '[$i] = (int) ($sum - ' . $class::BASE_FULL . ' * $upper);' :
|
||||
'$upper = $sum >> 31; $' . $result . '[$i] = $sum - ' . $class::BASE_FULL . ' * $upper;';
|
||||
$code .= '
|
||||
$' . $result . '[$j] = $upper;
|
||||
}
|
||||
if ($j == $length) {
|
||||
$sum = $' . $result . '[$i] + $_' . $y . '[$i] + $carry;
|
||||
$carry = $sum >= ' . self::float2string($class::BASE_FULL) . ';
|
||||
$' . $result . '[$i] = $carry ? $sum - ' . self::float2string($class::BASE_FULL) . ' : $sum;
|
||||
++$i;
|
||||
}
|
||||
if ($carry) {
|
||||
for (; $' . $result . '[$i] == ' . $class::MAX_DIGIT . '; ++$i) {
|
||||
$' . $result . '[$i] = 0;
|
||||
}
|
||||
++$' . $result . '[$i];
|
||||
}';
|
||||
$code .= self::generateInlineTrim($result);
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline Subtraction 2
|
||||
*
|
||||
* For when $known is more digits than $unknown. This is the harder use case to optimize for.
|
||||
*/
|
||||
private static function generateInlineSubtract2(string $known, string $unknown, string $result, string $class): string
|
||||
{
|
||||
$code = '
|
||||
$' . $result . ' = $' . $known . ';
|
||||
$carry = 0;
|
||||
$size = count($' . $unknown . ');
|
||||
for ($i = 0, $j = 1; $j < $size; $i+= 2, $j+= 2) {
|
||||
$sum = ($' . $known . '[$j] - $' . $unknown . '[$j]) * ' . $class::BASE_FULL . ' + $' . $known . '[$i]
|
||||
- $' . $unknown . '[$i]
|
||||
- $carry;
|
||||
$carry = $sum < 0;
|
||||
if ($carry) {
|
||||
$sum+= ' . self::float2string($class::MAX_DIGIT2) . ';
|
||||
}
|
||||
$subtemp = ';
|
||||
$code .= $class::BASE === 26 ?
|
||||
'intval($sum / 0x4000000);' :
|
||||
'$sum >> 31;';
|
||||
$code .= '$' . $result . '[$i] = ';
|
||||
if ($class::BASE === 26) {
|
||||
$code .= '(int) (';
|
||||
}
|
||||
$code .= '$sum - ' . $class::BASE_FULL . ' * $subtemp';
|
||||
if ($class::BASE === 26) {
|
||||
$code .= ')';
|
||||
}
|
||||
$code .= ';
|
||||
$' . $result . '[$j] = $subtemp;
|
||||
}
|
||||
if ($j == $size) {
|
||||
$sum = $' . $known . '[$i] - $' . $unknown . '[$i] - $carry;
|
||||
$carry = $sum < 0;
|
||||
$' . $result . '[$i] = $carry ? $sum + ' . $class::BASE_FULL . ' : $sum;
|
||||
++$i;
|
||||
}
|
||||
|
||||
if ($carry) {
|
||||
for (; !$' . $result . '[$i]; ++$i) {
|
||||
$' . $result . '[$i] = ' . $class::MAX_DIGIT . ';
|
||||
}
|
||||
--$' . $result . '[$i];
|
||||
}';
|
||||
|
||||
$code .= self::generateInlineTrim($result);
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline Subtraction 1
|
||||
*
|
||||
* For when $unknown is more digits than $known. This is the easier use case to optimize for.
|
||||
*/
|
||||
private static function generateInlineSubtract1(string $unknown, array $known, string $result, string $class): string
|
||||
{
|
||||
$code = '$' . $result . ' = $' . $unknown . ';';
|
||||
for ($i = 0, $j = 1; $j < count($known); $i += 2, $j += 2) {
|
||||
$code .= '$sum = $' . $unknown . '[' . $j . '] * ' . $class::BASE_FULL . ' + $' . $unknown . '[' . $i . '] - ';
|
||||
$code .= self::float2string($known[$j] * $class::BASE_FULL + $known[$i]);
|
||||
if ($i != 0) {
|
||||
$code .= ' - $carry';
|
||||
}
|
||||
|
||||
$code .= ';
|
||||
if ($carry = $sum < 0) {
|
||||
$sum+= ' . self::float2string($class::MAX_DIGIT2) . ';
|
||||
}
|
||||
$subtemp = ';
|
||||
$code .= $class::BASE === 26 ?
|
||||
'intval($sum / 0x4000000);' :
|
||||
'$sum >> 31;';
|
||||
$code .= '
|
||||
$' . $result . '[' . $i . '] = ';
|
||||
if ($class::BASE === 26) {
|
||||
$code .= ' (int) (';
|
||||
}
|
||||
$code .= '$sum - ' . $class::BASE_FULL . ' * $subtemp';
|
||||
if ($class::BASE === 26) {
|
||||
$code .= ')';
|
||||
}
|
||||
$code .= ';
|
||||
$' . $result . '[' . $j . '] = $subtemp;';
|
||||
}
|
||||
|
||||
$code .= '$i = ' . $i . ';';
|
||||
|
||||
if ($j == count($known)) {
|
||||
$code .= '
|
||||
$sum = $' . $unknown . '[' . $i . '] - ' . $known[$i] . ' - $carry;
|
||||
$carry = $sum < 0;
|
||||
$' . $result . '[' . $i . '] = $carry ? $sum + ' . $class::BASE_FULL . ' : $sum;
|
||||
++$i;';
|
||||
}
|
||||
|
||||
$code .= '
|
||||
if ($carry) {
|
||||
for (; !$' . $result . '[$i]; ++$i) {
|
||||
$' . $result . '[$i] = ' . $class::MAX_DIGIT . ';
|
||||
}
|
||||
--$' . $result . '[$i];
|
||||
}';
|
||||
$code .= self::generateInlineTrim($result);
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline Comparison
|
||||
*
|
||||
* If $unknown >= $known then loop
|
||||
*/
|
||||
private static function generateInlineCompare(array $known, string $unknown, string $subcode): string
|
||||
{
|
||||
$uniqid = uniqid();
|
||||
$code = 'loop_' . $uniqid . ':
|
||||
$clength = count($' . $unknown . ');
|
||||
switch (true) {
|
||||
case $clength < ' . count($known) . ':
|
||||
goto end_' . $uniqid . ';
|
||||
case $clength > ' . count($known) . ':';
|
||||
for ($i = count($known) - 1; $i >= 0; $i--) {
|
||||
$code .= '
|
||||
case $' . $unknown . '[' . $i . '] > ' . $known[$i] . ':
|
||||
goto subcode_' . $uniqid . ';
|
||||
case $' . $unknown . '[' . $i . '] < ' . $known[$i] . ':
|
||||
goto end_' . $uniqid . ';';
|
||||
}
|
||||
$code .= '
|
||||
default:
|
||||
// do subcode
|
||||
}
|
||||
|
||||
subcode_' . $uniqid . ':' . $subcode . '
|
||||
goto loop_' . $uniqid . ';
|
||||
|
||||
end_' . $uniqid . ':';
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a float to a string
|
||||
*
|
||||
* If you do echo floatval(pow(2, 52)) you'll get 4.6116860184274E+18. It /can/ be displayed without a loss of
|
||||
* precision but displayed in this way there will be precision loss, hence the need for this method.
|
||||
*
|
||||
* @param int|float $num
|
||||
*/
|
||||
private static function float2string($num): string
|
||||
{
|
||||
if (!is_float($num)) {
|
||||
return (string) $num;
|
||||
}
|
||||
|
||||
if ($num < 0) {
|
||||
return '-' . self::float2string(abs($num));
|
||||
}
|
||||
|
||||
$temp = '';
|
||||
while ($num) {
|
||||
$temp = fmod($num, 10) . $temp;
|
||||
$num = floor($num / 10);
|
||||
}
|
||||
|
||||
return $temp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Montgomery Modular Exponentiation Engine
|
||||
*
|
||||
* PHP version 5 and 7
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2017 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://pear.php.net/package/Math_BigInteger
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
|
||||
|
||||
use phpseclib3\Math\BigInteger\Engines\PHP\Montgomery as Progenitor;
|
||||
|
||||
/**
|
||||
* PHP Montgomery Modular Exponentiation Engine
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
*/
|
||||
abstract class Montgomery extends Progenitor
|
||||
{
|
||||
/**
|
||||
* Prepare a number for use in Montgomery Modular Reductions
|
||||
*/
|
||||
protected static function prepareReduce(array $x, array $n, string $class): array
|
||||
{
|
||||
$lhs = new $class();
|
||||
$lhs->value = array_merge(self::array_repeat(0, count($n)), $x);
|
||||
$rhs = new $class();
|
||||
$rhs->value = $n;
|
||||
|
||||
[, $temp] = $lhs->divide($rhs);
|
||||
return $temp->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Montgomery Multiply
|
||||
*
|
||||
* Interleaves the montgomery reduction and long multiplication algorithms together as described in
|
||||
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
|
||||
*/
|
||||
protected static function reduce(array $x, array $n, string $class): array
|
||||
{
|
||||
static $cache = [
|
||||
self::VARIABLE => [],
|
||||
self::DATA => [],
|
||||
];
|
||||
|
||||
if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
|
||||
$key = count($cache[self::VARIABLE]);
|
||||
$cache[self::VARIABLE][] = $x;
|
||||
$cache[self::DATA][] = self::modInverse67108864($n, $class);
|
||||
}
|
||||
|
||||
$k = count($n);
|
||||
|
||||
$result = [self::VALUE => $x];
|
||||
|
||||
for ($i = 0; $i < $k; ++$i) {
|
||||
$temp = $result[self::VALUE][$i] * $cache[self::DATA][$key];
|
||||
$temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
|
||||
$temp = $class::regularMultiply([$temp], $n);
|
||||
$temp = array_merge(self::array_repeat(0, $i), $temp);
|
||||
$result = $class::addHelper($result[self::VALUE], false, $temp, false);
|
||||
}
|
||||
|
||||
$result[self::VALUE] = array_slice($result[self::VALUE], $k);
|
||||
|
||||
if (self::compareHelper($result, false, $n, false) >= 0) {
|
||||
$result = $class::subtractHelper($result[self::VALUE], false, $n, false);
|
||||
}
|
||||
|
||||
return $result[self::VALUE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Modular Inverse of a number mod 2**26 (eg. 67108864)
|
||||
*
|
||||
* Based off of the bnpInvDigit function implemented and justified in the following URL:
|
||||
*
|
||||
* {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js}
|
||||
*
|
||||
* The following URL provides more info:
|
||||
*
|
||||
* {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85}
|
||||
*
|
||||
* As for why we do all the bitmasking... strange things can happen when converting from floats to ints. For
|
||||
* instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields
|
||||
* int(-2147483648). To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't
|
||||
* auto-converted to floats. The outermost bitmask is present because without it, there's no guarantee that
|
||||
* the "residue" returned would be the so-called "common residue". We use fmod, in the last step, because the
|
||||
* maximum possible $x is 26 bits and the maximum $result is 16 bits. Thus, we have to be able to handle up to
|
||||
* 40 bits, which only 64-bit floating points will support.
|
||||
*
|
||||
* Thanks to Pedro Gimeno Fortea for input!
|
||||
*/
|
||||
protected static function modInverse67108864(array $x, string $class): int // 2**26 == 67,108,864
|
||||
{
|
||||
$x = -$x[0];
|
||||
$result = $x & 0x3; // x**-1 mod 2**2
|
||||
$result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4
|
||||
$result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8
|
||||
$result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16
|
||||
$result = $class::BASE == 26 ?
|
||||
fmod($result * (2 - fmod($x * $result, $class::BASE_FULL)), $class::BASE_FULL) : // x**-1 mod 2**26
|
||||
($result * (2 - ($x * $result) % $class::BASE_FULL)) % $class::BASE_FULL;
|
||||
return $result & $class::MAX_DIGIT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Montgomery Modular Exponentiation Engine with interleaved multiplication
|
||||
*
|
||||
* PHP version 5 and 7
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2017 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://pear.php.net/package/Math_BigInteger
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
|
||||
|
||||
use phpseclib3\Math\BigInteger\Engines\PHP;
|
||||
|
||||
/**
|
||||
* PHP Montgomery Modular Exponentiation Engine with interleaved multiplication
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
*/
|
||||
abstract class MontgomeryMult extends Montgomery
|
||||
{
|
||||
/**
|
||||
* Montgomery Multiply
|
||||
*
|
||||
* Interleaves the montgomery reduction and long multiplication algorithms together as described in
|
||||
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
|
||||
*
|
||||
* @param class-string<PHP> $class
|
||||
* @see self::_prepMontgomery()
|
||||
* @see self::_montgomery()
|
||||
*/
|
||||
public static function multiplyReduce(array $x, array $y, array $m, string $class): array
|
||||
{
|
||||
// the following code, although not callable, can be run independently of the above code
|
||||
// although the above code performed better in my benchmarks the following could might
|
||||
// perform better under different circumstances. in lieu of deleting it it's just been
|
||||
// made uncallable
|
||||
|
||||
static $cache = [
|
||||
self::VARIABLE => [],
|
||||
self::DATA => [],
|
||||
];
|
||||
|
||||
if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
|
||||
$key = count($cache[self::VARIABLE]);
|
||||
$cache[self::VARIABLE][] = $m;
|
||||
$cache[self::DATA][] = self::modInverse67108864($m, $class);
|
||||
}
|
||||
|
||||
$n = max(count($x), count($y), count($m));
|
||||
$x = array_pad($x, $n, 0);
|
||||
$y = array_pad($y, $n, 0);
|
||||
$m = array_pad($m, $n, 0);
|
||||
$a = [self::VALUE => self::array_repeat(0, $n + 1)];
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
$temp = $a[self::VALUE][0] + $x[$i] * $y[0];
|
||||
$temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
|
||||
$temp = $temp * $cache[self::DATA][$key];
|
||||
$temp = $temp - $class::BASE_FULL * ($class::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
|
||||
$temp = $class::addHelper($class::regularMultiply([$x[$i]], $y), false, $class::regularMultiply([$temp], $m), false);
|
||||
$a = $class::addHelper($a[self::VALUE], false, $temp[self::VALUE], false);
|
||||
$a[self::VALUE] = array_slice($a[self::VALUE], 1);
|
||||
}
|
||||
if (self::compareHelper($a[self::VALUE], false, $m, false) >= 0) {
|
||||
$a = $class::subtractHelper($a[self::VALUE], false, $m, false);
|
||||
}
|
||||
return $a[self::VALUE];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PHP Power of Two Modular Exponentiation Engine
|
||||
*
|
||||
* PHP version 5 and 7
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
* @copyright 2017 Jim Wigginton
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link http://pear.php.net/package/Math_BigInteger
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpseclib3\Math\BigInteger\Engines\PHP\Reductions;
|
||||
|
||||
use phpseclib3\Math\BigInteger\Engines\PHP\Base;
|
||||
|
||||
/**
|
||||
* PHP Power Of Two Modular Exponentiation Engine
|
||||
*
|
||||
* @author Jim Wigginton <terrafrost@php.net>
|
||||
*/
|
||||
abstract class PowerOfTwo extends Base
|
||||
{
|
||||
/**
|
||||
* Prepare a number for use in Montgomery Modular Reductions
|
||||
*/
|
||||
protected static function prepareReduce(array $x, array $n, string $class): array
|
||||
{
|
||||
return self::reduce($x, $n, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Power Of Two Reduction
|
||||
*/
|
||||
protected static function reduce(array $x, array $n, string $class): array
|
||||
{
|
||||
$lhs = new $class();
|
||||
$lhs->value = $x;
|
||||
$rhs = new $class();
|
||||
$rhs->value = $n;
|
||||
|
||||
$temp = new $class();
|
||||
$temp->value = [1];
|
||||
|
||||
$result = $lhs->bitwise_and($rhs->subtract($temp));
|
||||
return $result->value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user