diff --git a/phpseclib/Math/BigInteger.php b/phpseclib/Math/BigInteger.php
index 324e788d..fb19731e 100644
--- a/phpseclib/Math/BigInteger.php
+++ b/phpseclib/Math/BigInteger.php
@@ -6,27 +6,7 @@
* Supports base-2, base-10, base-16, and base-256 numbers. Uses the GMP or BCMath extensions, if available,
* and an internal implementation, otherwise.
*
- * PHP version 5
- *
- * {@internal (all DocBlock comments regarding implementation - such as the one that follows - refer to the
- * {@link self::MODE_INTERNAL self::MODE_INTERNAL} mode)
- *
- * BigInteger uses base-2**26 to perform operations such as multiplication and division and
- * base-2**52 (ie. two base 2**26 digits) to perform addition and subtraction. Because the largest possible
- * value when multiplying two base-2**26 numbers together is a base-2**52 number, double precision floating
- * point numbers - numbers that should be supported on most hardware and whose significand is 53 bits - are
- * used. As a consequence, bitwise operators such as >> and << cannot be used, nor can the modulo operator %,
- * which only supports integers. Although this fact will slow this library down, the fact that such a high
- * base is being used should more than compensate.
- *
- * Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness little endian} format. ie.
- * (new \phpseclib\Math\BigInteger(pow(2, 26)))->value = [0, 1]
- *
- * Useful resources are as follows:
- *
- * - {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf Handbook of Applied Cryptography (HAC)}
- * - {@link http://math.libtomcrypt.com/files/tommath.pdf Multi-Precision Math (MPM)}
- * - Java's BigInteger classes. See /j2se/src/share/classes/java/math in jdk-1_5_0-src-jrl.zip
+ * PHP version 5 and 7
*
* Here's an example of how to use this library:
*
@@ -43,17 +23,14 @@
* @category Math
* @package BigInteger
* @author Jim Wigginton
- * @copyright 2006 Jim Wigginton
+ * @copyright 2017 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://pear.php.net/package/Math_BigInteger
*/
namespace phpseclib\Math;
-use ParagonIE\ConstantTime\Base64;
-use ParagonIE\ConstantTime\Hex;
-use phpseclib\Crypt\Random;
-use phpseclib\File\ASN1;
+use phpseclib\Exception\BadConfigurationException;
/**
* Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256
@@ -63,174 +40,79 @@ use phpseclib\File\ASN1;
* @author Jim Wigginton
* @access public
*/
-class BigInteger
+class BigInteger implements \Serializable
{
- /**#@+
- * Reduction constants
+ /**
+ * Main Engine
*
- * @access private
- * @see BigInteger::_reduce()
+ * @var string
*/
- /**
- * @see BigInteger::_montgomery()
- * @see BigInteger::_prepMontgomery()
- */
- const MONTGOMERY = 0;
- /**
- * @see BigInteger::_barrett()
- */
- const BARRETT = 1;
- /**
- * @see BigInteger::_mod2()
- */
- const POWEROF2 = 2;
- /**
- * @see BigInteger::_remainder()
- */
- const CLASSIC = 3;
- /**
- * @see BigInteger::__clone()
- */
- const NONE = 4;
- /**#@-*/
-
- /**#@+
- * Array constants
- *
- * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and
- * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them.
- *
- * @access private
- */
- /**
- * $result[self::VALUE] contains the value.
- */
- const VALUE = 0;
- /**
- * $result[self::SIGN] contains the sign.
- */
- const SIGN = 1;
- /**#@-*/
-
- /**#@+
- * @access private
- * @see BigInteger::_montgomery()
- * @see BigInteger::_barrett()
- */
- /**
- * Cache constants
- *
- * $cache[self::VARIABLE] tells us whether or not the cached data is still valid.
- */
- const VARIABLE = 0;
- /**
- * $cache[self::DATA] contains the cached data.
- */
- const DATA = 1;
- /**#@-*/
-
- /**#@+
- * Mode constants.
- *
- * @access private
- * @see BigInteger::__construct()
- */
- /**
- * To use the pure-PHP implementation
- */
- const MODE_INTERNAL = 1;
- /**
- * To use the BCMath library
- *
- * (if enabled; otherwise, the internal implementation will be used)
- */
- const MODE_BCMATH = 2;
- /**
- * To use the GMP library
- *
- * (if present; otherwise, either the BCMath or the internal implementation will be used)
- */
- const MODE_GMP = 3;
- /**#@-*/
+ private static $mainEngine;
/**
- * Karatsuba Cutoff
+ * Modular Exponentiation Engine
*
- * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication?
- *
- * @access private
+ * @var string
*/
- const KARATSUBA_CUTOFF = 25;
-
- /**#@+
- * Static properties used by the pure-PHP implementation.
- *
- * @see __construct()
- */
- private static $base;
- private static $baseFull;
- private static $maxDigit;
- private static $msb;
+ private static $modexpEngine;
/**
- * $max10 in greatest $max10Len satisfying
- * $max10 = 10**$max10Len <= 2**$base.
- */
- private static $max10;
-
- /**
- * $max10Len in greatest $max10Len satisfying
- * $max10 = 10**$max10Len <= 2**$base.
- */
- private static $max10Len;
- private static $maxDigit2;
- /**#@-*/
-
- /**
- * Holds the BigInteger's value.
+ * Selected Engines
*
* @var array
- * @access private
+ */
+ private static $engines;
+
+ /**
+ * The actual BigInteger object
+ *
+ * @var object
*/
private $value;
/**
- * Holds the BigInteger's magnitude.
+ * Sets engine type.
*
- * @var bool
- * @access private
+ * Throws an exception if the type is invalid
+ *
+ * @param string $main
+ * @param array $modexp optional
*/
- private $is_negative = false;
+ public static function setEngine($main, $modexps = ['DefaultEngine'])
+ {
+ self::$engines = [];
- /**
- * Precision
- *
- * @see self::setPrecision()
- * @access private
- */
- private $precision = -1;
+ $fqmain = 'phpseclib\\Math\\BigInteger\\Engines\\' . $main;
+ if (!class_exists($fqmain) || !method_exists($fqmain, 'isValidEngine')) {
+ throw new \InvalidArgumentException("$main is not a valid engine");
+ }
+ if (!$fqmain::isValidEngine()) {
+ throw new BadConfigurationException("$main is not setup correctly on this system");
+ }
+ self::$mainEngine = $fqmain;
- /**
- * Precision Bitmask
- *
- * @see self::setPrecision()
- * @access private
- */
- private $bitmask = false;
+ if (!in_array('Default', $modexps)) {
+ $modexps[] = 'DefaultEngine';
+ }
- /**
- * Mode independent value used for serialization.
- *
- * If the bcmath or gmp extensions are installed $this->value will be a non-serializable resource, hence the need for
- * a variable that'll be serializable regardless of whether or not extensions are being used. Unlike $this->value,
- * however, $this->hex is only calculated when $this->__sleep() is called.
- *
- * @see self::__sleep()
- * @see self::__wakeup()
- * @var string
- * @access private
- */
- private $hex;
+ $found = false;
+ foreach ($modexps as $modexp) {
+ try {
+ $fqmain::setModExpEngine($modexp);
+ $found = true;
+ break;
+ } catch (\Exception $e) {
+ }
+ }
+
+ if (!$found) {
+ throw new BadConfigurationException("No valid modular exponentiation engine found for $main");
+ }
+
+ self::$modexpEngine = $modexp;
+
+ self::$engines = [$main, $modexp];
+ }
/**
* Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers.
@@ -238,355 +120,86 @@ class BigInteger
* If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using
* two's compliment. The sole exception to this is -10, which is treated the same as 10 is.
*
- * Here's an example:
- *
- * toString(); // outputs 50
- * ?>
- *
- *
* @param $x base-10 number or base-$base number if $base set.
* @param int $base
* @return \phpseclib\Math\BigInteger
- * @access public
*/
public function __construct($x = 0, $base = 10)
{
- if (!defined('MATH_BIGINTEGER_MODE')) {
- switch (true) {
- case extension_loaded('gmp'):
- define('MATH_BIGINTEGER_MODE', self::MODE_GMP);
+ if (!isset(self::$mainEngine)) {
+ $engines = [
+ ['GMP'],
+ ['PHP64', ['OpenSSL']],
+ ['BCMath', ['OpenSSL']],
+ ['PHP32', ['OpenSSL']]
+ ];
+ foreach ($engines as $engine) {
+ try {
+ self::setEngine($engine[0], isset($engine[1]) ? $engine[1] : []);
break;
- case extension_loaded('bcmath'):
- define('MATH_BIGINTEGER_MODE', self::MODE_BCMATH);
- break;
- default:
- define('MATH_BIGINTEGER_MODE', self::MODE_INTERNAL);
+ } catch (\InvalidArgumentException $e) {
+ }
}
}
- if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
- define('MATH_BIGINTEGER_OPENSSL_ENABLED', true);
+ if ($x instanceof self::$mainEngine) {
+ $this->value = clone $x;
+ } elseif ($x instanceof BigInteger\Engines\Engine) {
+ $this->value = new static("$x");
+ $this->value->setPrecision($x->getPrecision());
+ } else {
+ $this->value = new self::$mainEngine($x, $base);
}
+ }
- if (!defined('PHP_INT_SIZE')) {
- define('PHP_INT_SIZE', 4);
- }
+ /**
+ * Converts a BigInteger to a base-10 number.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return $this->value->toString();
+ }
- if (empty(self::$base) && MATH_BIGINTEGER_MODE == self::MODE_INTERNAL) {
- switch (PHP_INT_SIZE) {
- case 8: // use 64-bit integers if int size is 8 bytes
- self::$base = 31;
- self::$baseFull = 0x80000000;
- self::$maxDigit = 0x7FFFFFFF;
- self::$msb = 0x40000000;
- self::$max10 = 1000000000;
- self::$max10Len = 9;
- self::$maxDigit2 = pow(2, 62);
- break;
- //case 4: // use 64-bit floats if int size is 4 bytes
- default:
- self::$base = 26;
- self::$baseFull = 0x4000000;
- self::$maxDigit = 0x3FFFFFF;
- self::$msb = 0x2000000;
- self::$max10 = 10000000;
- self::$max10Len = 7;
- self::$maxDigit2 = pow(2, 52); // pow() prevents truncation
- }
- }
+ /**
+ * __toString() magic method
+ */
+ public function __toString()
+ {
+ return (string) $this->value;
+ }
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- switch (true) {
- case is_resource($x) && get_resource_type($x) == 'GMP integer':
- // PHP 5.6 switched GMP from using resources to objects
- case $x instanceof \GMP:
- $this->value = $x;
- return;
- }
- $this->value = gmp_init(0);
- break;
- case self::MODE_BCMATH:
- $this->value = '0';
- break;
- default:
- $this->value = [];
- }
-
- // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48
- // '0' is the only value like this per http://php.net/empty
- if (empty($x) && (abs($base) != 256 || $x !== '0')) {
- return;
- }
-
- switch ($base) {
- case -256:
- if (ord($x[0]) & 0x80) {
- $x = ~$x;
- $this->is_negative = true;
- }
- case 256:
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $sign = $this->is_negative ? '-' : '';
- $this->value = gmp_init($sign . '0x' . Hex::encode($x));
- break;
- case self::MODE_BCMATH:
- // round $len to the nearest 4 (thanks, DavidMJ!)
- $len = (strlen($x) + 3) & 0xFFFFFFFC;
-
- $x = str_pad($x, $len, chr(0), STR_PAD_LEFT);
-
- for ($i = 0; $i < $len; $i+= 4) {
- $this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32
- $this->value = bcadd($this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2]) << 8) | ord($x[$i + 3])), 0);
- }
-
- if ($this->is_negative) {
- $this->value = '-' . $this->value;
- }
-
- break;
- // converts a base-2**8 (big endian / msb) number to base-2**26 (little endian / lsb)
- default:
- while (strlen($x)) {
- $this->value[] = $this->_bytes2int($this->_base256_rshift($x, self::$base));
- }
- }
-
- if ($this->is_negative) {
- if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
- $this->is_negative = false;
- }
- $temp = $this->add(new static('-1'));
- $this->value = $temp->value;
- }
- break;
- case 16:
- case -16:
- if ($base > 0 && $x[0] == '-') {
- $this->is_negative = true;
- $x = substr($x, 1);
- }
-
- $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#', '$1', $x);
-
- $is_negative = false;
- if ($base < 0 && hexdec($x[0]) >= 8) {
- $this->is_negative = $is_negative = true;
- $x = Hex::encode(~Hex::decode($x));
- }
-
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp = $this->is_negative ? '-0x' . $x : '0x' . $x;
- $this->value = gmp_init($temp);
- $this->is_negative = false;
- break;
- case self::MODE_BCMATH:
- $x = (strlen($x) & 1) ? '0' . $x : $x;
- $temp = new static(Hex::decode($x), 256);
- $this->value = $this->is_negative ? '-' . $temp->value : $temp->value;
- $this->is_negative = false;
- break;
- default:
- $x = (strlen($x) & 1) ? '0' . $x : $x;
- $temp = new static(Hex::decode($x), 256);
- $this->value = $temp->value;
- }
-
- if ($is_negative) {
- $temp = $this->add(new static('-1'));
- $this->value = $temp->value;
- }
- break;
- case 10:
- case -10:
- // (?value = gmp_init($x);
- break;
- case self::MODE_BCMATH:
- // explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different
- // results then doing it on '-1' does (modInverse does $x[0])
- $this->value = $x === '-' ? '0' : (string) $x;
- break;
- default:
- $temp = new static();
-
- $multiplier = new static();
- $multiplier->value = [self::$max10];
-
- if ($x[0] == '-') {
- $this->is_negative = true;
- $x = substr($x, 1);
- }
-
- $x = str_pad($x, strlen($x) + ((self::$max10Len - 1) * strlen($x)) % self::$max10Len, 0, STR_PAD_LEFT);
- while (strlen($x)) {
- $temp = $temp->multiply($multiplier);
- $temp = $temp->add(new static($this->_int2bytes(substr($x, 0, self::$max10Len)), 256));
- $x = substr($x, self::$max10Len);
- }
-
- $this->value = $temp->value;
- }
- break;
- case 2: // base-2 support originally implemented by Lluis Pamies - thanks!
- case -2:
- if ($base > 0 && $x[0] == '-') {
- $this->is_negative = true;
- $x = substr($x, 1);
- }
-
- $x = preg_replace('#^([01]*).*#', '$1', $x);
- $x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT);
-
- $str = '0x';
- while (strlen($x)) {
- $part = substr($x, 0, 4);
- $str.= dechex(bindec($part));
- $x = substr($x, 4);
- }
-
- if ($this->is_negative) {
- $str = '-' . $str;
- }
-
- $temp = new static($str, 8 * $base); // ie. either -16 or +16
- $this->value = $temp->value;
- $this->is_negative = $temp->is_negative;
-
- break;
- default:
- // base not supported, so we'll let $this == 0
- }
+ /**
+ * __debugInfo() magic method
+ *
+ * Will be called, automatically, when print_r() or var_dump() are called
+ */
+ public function __debugInfo()
+ {
+ return $this->value->__debugInfo();
}
/**
* Converts a BigInteger to a byte string (eg. base-256).
*
- * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
- * saved as two's compliment.
- *
- * Here's an example:
- *
- * toBytes(); // outputs chr(65)
- * ?>
- *
- *
* @param bool $twos_compliment
* @return string
- * @access public
- * @internal Converts a base-2**26 number to base-2**8
*/
public function toBytes($twos_compliment = false)
{
- if ($twos_compliment) {
- $comparison = $this->compare(new static());
- if ($comparison == 0) {
- return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
- }
-
- $temp = $comparison < 0 ? $this->add(new static(1)) : $this;
- $bytes = $temp->toBytes();
-
- if (empty($bytes)) { // eg. if the number we're trying to convert is -1
- $bytes = chr(0);
- }
-
- if (ord($bytes[0]) & 0x80) {
- $bytes = chr(0) . $bytes;
- }
-
- return $comparison < 0 ? ~$bytes : $bytes;
- }
-
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- if (gmp_cmp($this->value, gmp_init(0)) == 0) {
- return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
- }
-
- $temp = gmp_strval(gmp_abs($this->value), 16);
- $temp = (strlen($temp) & 1) ? '0' . $temp : $temp;
- $temp = Hex::decode($temp);
-
- return $this->precision > 0 ?
- substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
- ltrim($temp, chr(0));
- case self::MODE_BCMATH:
- if ($this->value === '0') {
- return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
- }
-
- $value = '';
- $current = $this->value;
-
- if ($current[0] == '-') {
- $current = substr($current, 1);
- }
-
- while (bccomp($current, '0', 0) > 0) {
- $temp = bcmod($current, '16777216');
- $value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value;
- $current = bcdiv($current, '16777216', 0);
- }
-
- return $this->precision > 0 ?
- substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
- ltrim($value, chr(0));
- }
-
- if (!count($this->value)) {
- return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
- }
- $result = self::_int2bytes($this->value[count($this->value) - 1]);
-
- for ($i = count($this->value) - 2; $i >= 0; --$i) {
- self::_base256_lshift($result, self::$base);
- $result = $result | str_pad(self::_int2bytes($this->value[$i]), strlen($result), chr(0), STR_PAD_LEFT);
- }
-
- return $this->precision > 0 ?
- str_pad(substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) :
- $result;
+ return $this->value->toBytes($twos_compliment);
}
/**
- * Converts a BigInteger to a hex string (eg. base-16)).
- *
- * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
- * saved as two's compliment.
- *
- * Here's an example:
- *
- * toHex(); // outputs '41'
- * ?>
- *
+ * Converts a BigInteger to a hex string (eg. base-16).
*
* @param bool $twos_compliment
* @return string
- * @access public
- * @internal Converts a base-2**26 number to base-2**8
*/
public function toHex($twos_compliment = false)
{
- return Hex::encode($this->toBytes($twos_compliment));
+ return $this->value->toHex($twos_compliment);
}
/**
@@ -595,716 +208,43 @@ class BigInteger
* Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
* saved as two's compliment.
*
- * Here's an example:
- *
- * toBits(); // outputs '1000001'
- * ?>
- *
- *
* @param bool $twos_compliment
* @return string
- * @access public
- * @internal Converts a base-2**26 number to base-2**2
*/
- public function toBits($twos_compliment = false)
+ function toBits($twos_compliment = false)
{
- $hex = $this->toHex($twos_compliment);
- $bits = '';
- for ($i = strlen($hex) - 8, $start = strlen($hex) & 7; $i >= $start; $i-=8) {
- $bits = str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT) . $bits;
- }
- if ($start) { // hexdec('') == 0
- $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8, '0', STR_PAD_LEFT) . $bits;
- }
- $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');
-
- if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) {
- return '0' . $result;
- }
-
- return $result;
- }
-
- /**
- * Converts a BigInteger to a base-10 number.
- *
- * Here's an example:
- *
- * toString(); // outputs 50
- * ?>
- *
- *
- * @return string
- * @access public
- * @internal Converts a base-2**26 number to base-10**7 (which is pretty much base-10)
- */
- public function toString()
- {
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- return gmp_strval($this->value);
- case self::MODE_BCMATH:
- if ($this->value === '0') {
- return '0';
- }
-
- return ltrim($this->value, '0');
- }
-
- if (!count($this->value)) {
- return '0';
- }
-
- $temp = clone $this;
- $temp->is_negative = false;
-
- $divisor = new static();
- $divisor->value = [self::$max10];
- $result = '';
- while (count($temp->value)) {
- list($temp, $mod) = $temp->divide($divisor);
- $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', self::$max10Len, '0', STR_PAD_LEFT) . $result;
- }
- $result = ltrim($result, '0');
- if (empty($result)) {
- $result = '0';
- }
-
- if ($this->is_negative) {
- $result = '-' . $result;
- }
-
- return $result;
- }
-
- /**
- * __toString() magic method
- *
- * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
- * toString().
- *
- * @access public
- * @internal Implemented per a suggestion by Techie-Michael - thanks!
- */
- public function __toString()
- {
- return $this->toString();
- }
-
- /**
- * __sleep() magic method
- *
- * Will be called, automatically, when serialize() is called on a BigInteger object.
- *
- * @see self::__wakeup()
- * @access public
- */
- public function __sleep()
- {
- $this->hex = $this->toHex(true);
- $vars = ['hex'];
- if ($this->precision > 0) {
- $vars[] = 'precision';
- }
- return $vars;
- }
-
- /**
- * __wakeup() magic method
- *
- * Will be called, automatically, when unserialize() is called on a BigInteger object.
- *
- * @see self::__sleep()
- * @access public
- */
- public function __wakeup()
- {
- $temp = new static($this->hex, -16);
- $this->value = $temp->value;
- $this->is_negative = $temp->is_negative;
- if ($this->precision > 0) {
- // recalculate $this->bitmask
- $this->setPrecision($this->precision);
- }
- }
-
- /**
- * __debugInfo() magic method
- *
- * Will be called, automatically, when print_r() or var_dump() are called
- *
- * @access public
- */
- public function __debugInfo()
- {
- $opts = [];
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $engine = 'gmp';
- break;
- case self::MODE_BCMATH:
- $engine = 'bcmath';
- break;
- case self::MODE_INTERNAL:
- $engine = 'internal';
- $opts[] = PHP_INT_SIZE == 8 ? '64-bit' : '32-bit';
- }
- if (MATH_BIGINTEGER_MODE != self::MODE_GMP && defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
- $opts[] = 'OpenSSL';
- }
- if (!empty($opts)) {
- $engine.= ' (' . implode($opts, ', ') . ')';
- }
- return [
- 'value' => '0x' . $this->toHex(true),
- 'engine' => $engine
- ];
+ return $this->value->toBits();
}
/**
* Adds two BigIntegers.
*
- * Here's an example:
- *
- * add($b);
- *
- * echo $c->toString(); // outputs 30
- * ?>
- *
- *
- * @param \phpseclib\Math\BigInteger $y
* @return \phpseclib\Math\BigInteger
- * @access public
- * @internal Performs base-2**52 addition
*/
public function add(BigInteger $y)
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp = new static();
- $temp->value = gmp_add($this->value, $y->value);
-
- return $this->_normalize($temp);
- case self::MODE_BCMATH:
- $temp = new static();
- $temp->value = bcadd($this->value, $y->value, 0);
-
- return $this->_normalize($temp);
- }
-
- $temp = self::_add($this->value, $this->is_negative, $y->value, $y->is_negative);
-
- $result = new static();
- $result->value = $temp[self::VALUE];
- $result->is_negative = $temp[self::SIGN];
-
- return $this->_normalize($result);
- }
-
- /**
- * Performs addition.
- *
- * @param array $x_value
- * @param bool $x_negative
- * @param array $y_value
- * @param bool $y_negative
- * @return array
- * @access private
- */
- private static function _add($x_value, $x_negative, $y_value, $y_negative)
- {
- $x_size = count($x_value);
- $y_size = count($y_value);
-
- if ($x_size == 0) {
- return [
- self::VALUE => $y_value,
- self::SIGN => $y_negative
- ];
- } elseif ($y_size == 0) {
- return [
- self::VALUE => $x_value,
- self::SIGN => $x_negative
- ];
- }
-
- // subtract, if appropriate
- if ($x_negative != $y_negative) {
- if ($x_value == $y_value) {
- return [
- self::VALUE => array(),
- self::SIGN => false
- ];
- }
-
- $temp = self::_subtract($x_value, false, $y_value, false);
- $temp[self::SIGN] = self::_compare($x_value, false, $y_value, false) > 0 ?
- $x_negative : $y_negative;
-
- return $temp;
- }
-
- if ($x_size < $y_size) {
- $size = $x_size;
- $value = $y_value;
- } else {
- $size = $y_size;
- $value = $x_value;
- }
-
- $value[count($value)] = 0; // just in case the carry adds an extra digit
-
- $carry = 0;
- for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) {
- $sum = $x_value[$j] * self::$baseFull + $x_value[$i] + $y_value[$j] * self::$baseFull + $y_value[$i] + $carry;
- $carry = $sum >= self::$maxDigit2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
- $sum = $carry ? $sum - self::$maxDigit2 : $sum;
-
- $temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31);
-
- $value[$i] = (int) ($sum - self::$baseFull * $temp); // eg. a faster alternative to fmod($sum, 0x4000000)
- $value[$j] = $temp;
- }
-
- if ($j == $size) { // ie. if $y_size is odd
- $sum = $x_value[$i] + $y_value[$i] + $carry;
- $carry = $sum >= self::$baseFull;
- $value[$i] = $carry ? $sum - self::$baseFull : $sum;
- ++$i; // ie. let $i = $j since we've just done $value[$i]
- }
-
- if ($carry) {
- for (; $value[$i] == self::$maxDigit; ++$i) {
- $value[$i] = 0;
- }
- ++$value[$i];
- }
-
- return [
- self::VALUE => self::_trim($value),
- self::SIGN => $x_negative
- ];
+ return new static($this->value->add($y->value));
}
/**
* Subtracts two BigIntegers.
*
- * Here's an example:
- *
- * subtract($b);
- *
- * echo $c->toString(); // outputs -10
- * ?>
- *
- *
- * @param \phpseclib\Math\BigInteger $y
* @return \phpseclib\Math\BigInteger
- * @access public
- * @internal Performs base-2**52 subtraction
*/
- public function subtract(BigInteger $y)
+ function subtract(BigInteger $y)
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp = new static();
- $temp->value = gmp_sub($this->value, $y->value);
-
- return $this->_normalize($temp);
- case self::MODE_BCMATH:
- $temp = new static();
- $temp->value = bcsub($this->value, $y->value, 0);
-
- return $this->_normalize($temp);
- }
-
- $temp = self::_subtract($this->value, $this->is_negative, $y->value, $y->is_negative);
-
- $result = new static();
- $result->value = $temp[self::VALUE];
- $result->is_negative = $temp[self::SIGN];
-
- return $this->_normalize($result);
- }
-
- /**
- * Performs subtraction.
- *
- * @param array $x_value
- * @param bool $x_negative
- * @param array $y_value
- * @param bool $y_negative
- * @return array
- * @access private
- */
- private static function _subtract($x_value, $x_negative, $y_value, $y_negative)
- {
- $x_size = count($x_value);
- $y_size = count($y_value);
-
- if ($x_size == 0) {
- return [
- self::VALUE => $y_value,
- self::SIGN => !$y_negative
- ];
- } elseif ($y_size == 0) {
- return [
- self::VALUE => $x_value,
- self::SIGN => $x_negative
- ];
- }
-
- // add, if appropriate (ie. -$x - +$y or +$x - -$y)
- if ($x_negative != $y_negative) {
- $temp = self::_add($x_value, false, $y_value, false);
- $temp[self::SIGN] = $x_negative;
-
- return $temp;
- }
-
- $diff = self::_compare($x_value, $x_negative, $y_value, $y_negative);
-
- if (!$diff) {
- return [
- self::VALUE => [],
- self::SIGN => false
- ];
- }
-
- // switch $x and $y around, if appropriate.
- if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) {
- $temp = $x_value;
- $x_value = $y_value;
- $y_value = $temp;
-
- $x_negative = !$x_negative;
-
- $x_size = count($x_value);
- $y_size = count($y_value);
- }
-
- // at this point, $x_value should be at least as big as - if not bigger than - $y_value
-
- $carry = 0;
- for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) {
- $sum = $x_value[$j] * self::$baseFull + $x_value[$i] - $y_value[$j] * self::$baseFull - $y_value[$i] - $carry;
- $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
- $sum = $carry ? $sum + self::$maxDigit2 : $sum;
-
- $temp = self::$base === 26 ? intval($sum / 0x4000000) : ($sum >> 31);
-
- $x_value[$i] = (int) ($sum - self::$baseFull * $temp);
- $x_value[$j] = $temp;
- }
-
- if ($j == $y_size) { // ie. if $y_size is odd
- $sum = $x_value[$i] - $y_value[$i] - $carry;
- $carry = $sum < 0;
- $x_value[$i] = $carry ? $sum + self::$baseFull : $sum;
- ++$i;
- }
-
- if ($carry) {
- for (; !$x_value[$i]; ++$i) {
- $x_value[$i] = self::$maxDigit;
- }
- --$x_value[$i];
- }
-
- return [
- self::VALUE => self::_trim($x_value),
- self::SIGN => $x_negative
- ];
+ return new static($this->value->subtract($y->value));
}
/**
* Multiplies two BigIntegers
*
- * Here's an example:
- *
- * multiply($b);
- *
- * echo $c->toString(); // outputs 200
- * ?>
- *
- *
* @param \phpseclib\Math\BigInteger $x
* @return \phpseclib\Math\BigInteger
- * @access public
*/
public function multiply(BigInteger $x)
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp = new static();
- $temp->value = gmp_mul($this->value, $x->value);
-
- return $this->_normalize($temp);
- case self::MODE_BCMATH:
- $temp = new static();
- $temp->value = bcmul($this->value, $x->value, 0);
-
- return $this->_normalize($temp);
- }
-
- $temp = self::_multiply($this->value, $this->is_negative, $x->value, $x->is_negative);
-
- $product = new static();
- $product->value = $temp[self::VALUE];
- $product->is_negative = $temp[self::SIGN];
-
- return $this->_normalize($product);
- }
-
- /**
- * Performs multiplication.
- *
- * @param array $x_value
- * @param bool $x_negative
- * @param array $y_value
- * @param bool $y_negative
- * @return array
- * @access private
- */
- private static function _multiply($x_value, $x_negative, $y_value, $y_negative)
- {
- //if ( $x_value == $y_value ) {
- // return [
- // self::VALUE => $this->_square($x_value),
- // self::SIGN => $x_sign != $y_value
- // ];
- //}
-
- $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
- ];
- }
-
- return [
- self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ?
- self::_trim(self::_regularMultiply($x_value, $y_value)) :
- self::_trim(self::_karatsuba($x_value, $y_value)),
- self::SIGN => $x_negative != $y_negative
- ];
- }
-
- /**
- * Performs long multiplication on two BigIntegers
- *
- * Modeled after 'multiply' in MutableBigInteger.java.
- *
- * @param array $x_value
- * @param array $y_value
- * @return array
- * @access private
- */
- private static function _regularMultiply($x_value, $y_value)
- {
- $x_length = count($x_value);
- $y_length = count($y_value);
-
- if (!$x_length || !$y_length) { // a 0 is being multiplied
- return [];
- }
-
- 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
- $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
- $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
- $product_value[$j] = (int) ($temp - self::$baseFull * $carry);
- }
-
- $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; ++$j, ++$k) {
- $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
- $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
- $product_value[$k] = (int) ($temp - self::$baseFull * $carry);
- }
-
- $product_value[$k] = $carry;
- }
-
- return $product_value;
- }
-
- /**
- * Performs Karatsuba multiplication on two BigIntegers
- *
- * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
- * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}.
- *
- * @param array $x_value
- * @param array $y_value
- * @return array
- * @access private
- */
- private static function _karatsuba($x_value, $y_value)
- {
- $m = min(count($x_value) >> 1, count($y_value) >> 1);
-
- if ($m < self::KARATSUBA_CUTOFF) {
- return self::_regularMultiply($x_value, $y_value);
- }
-
- $x1 = array_slice($x_value, $m);
- $x0 = array_slice($x_value, 0, $m);
- $y1 = array_slice($y_value, $m);
- $y0 = array_slice($y_value, 0, $m);
-
- $z2 = self::_karatsuba($x1, $y1);
- $z0 = self::_karatsuba($x0, $y0);
-
- $z1 = self::_add($x1, false, $x0, false);
- $temp = self::_add($y1, false, $y0, false);
- $z1 = self::_karatsuba($z1[self::VALUE], $temp[self::VALUE]);
- $temp = self::_add($z2, false, $z0, false);
- $z1 = self::_subtract($z1, false, $temp[self::VALUE], false);
-
- $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
- $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);
-
- $xy = self::_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
- $xy = self::_add($xy[self::VALUE], $xy[self::SIGN], $z0, false);
-
- return $xy[self::VALUE];
- }
-
- /**
- * Performs squaring
- *
- * @param array $x
- * @return array
- * @access private
- */
- private static function _square($x = false)
- {
- return count($x) < 2 * self::KARATSUBA_CUTOFF ?
- self::_trim(self::_baseSquare($x)) :
- self::_trim(self::_karatsubaSquare($x));
- }
-
- /**
- * Performs traditional squaring on two BigIntegers
- *
- * Squaring can be done faster than multiplying a number by itself can be. See
- * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} /
- * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information.
- *
- * @param array $value
- * @return array
- * @access private
- */
- private static function _baseSquare($value)
- {
- if (empty($value)) {
- return [];
- }
- $square_value = self::_array_repeat(0, 2 * count($value));
-
- for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) {
- $i2 = $i << 1;
-
- $temp = $square_value[$i2] + $value[$i] * $value[$i];
- $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
- $square_value[$i2] = (int) ($temp - self::$baseFull * $carry);
-
- // note how we start from $i+1 instead of 0 as we do in multiplication.
- for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) {
- $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry;
- $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
- $square_value[$k] = (int) ($temp - self::$baseFull * $carry);
- }
-
- // the following line can yield values larger 2**15. at this point, PHP should switch
- // over to floats.
- $square_value[$i + $max_index + 1] = $carry;
- }
-
- return $square_value;
- }
-
- /**
- * Performs Karatsuba "squaring" on two BigIntegers
- *
- * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
- * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}.
- *
- * @param array $value
- * @return array
- * @access private
- */
- private static function _karatsubaSquare($value)
- {
- $m = count($value) >> 1;
-
- if ($m < self::KARATSUBA_CUTOFF) {
- return self::_baseSquare($value);
- }
-
- $x1 = array_slice($value, $m);
- $x0 = array_slice($value, 0, $m);
-
- $z2 = self::_karatsubaSquare($x1);
- $z0 = self::_karatsubaSquare($x0);
-
- $z1 = self::_add($x1, false, $x0, false);
- $z1 = self::_karatsubaSquare($z1[self::VALUE]);
- $temp = self::_add($z2, false, $z0, false);
- $z1 = self::_subtract($z1, false, $temp[self::VALUE], false);
-
- $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
- $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);
-
- $xx = self::_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
- $xx = self::_add($xx[self::VALUE], $xx[self::SIGN], $z0, false);
-
- return $xx[self::VALUE];
+ return new static($this->value->multiply($x->value));
}
/**
@@ -1330,1210 +270,43 @@ class BigInteger
*
*
* @param \phpseclib\Math\BigInteger $y
- * @return array
- * @access public
- * @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}.
+ * @return \phpseclib\Math\BigInteger
*/
public function divide(BigInteger $y)
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $quotient = new static();
- $remainder = new static();
-
- list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value);
-
- if (gmp_sign($remainder->value) < 0) {
- $remainder->value = gmp_add($remainder->value, gmp_abs($y->value));
- }
-
- return [$this->_normalize($quotient), $this->_normalize($remainder)];
- case self::MODE_BCMATH:
- $quotient = new static();
- $remainder = new static();
-
- $quotient->value = bcdiv($this->value, $y->value, 0);
- $remainder->value = bcmod($this->value, $y->value);
-
- if ($remainder->value[0] == '-') {
- $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0);
- }
-
- return [$this->_normalize($quotient), $this->_normalize($remainder)];
- }
-
- if (count($y->value) == 1) {
- list($q, $r) = $this->_divide_digit($this->value, $y->value[0]);
- $quotient = new static();
- $remainder = new static();
- $quotient->value = $q;
- $remainder->value = [$r];
- $quotient->is_negative = $this->is_negative != $y->is_negative;
- return [$this->_normalize($quotient), $this->_normalize($remainder)];
- }
-
- static $zero;
- if (!isset($zero)) {
- $zero = new static();
- }
-
- $x = clone $this;
- $y = clone $y;
-
- $x_sign = $x->is_negative;
- $y_sign = $y->is_negative;
-
- $x->is_negative = $y->is_negative = false;
-
- $diff = $x->compare($y);
-
- if (!$diff) {
- $temp = new static();
- $temp->value = [1];
- $temp->is_negative = $x_sign != $y_sign;
- return [$this->_normalize($temp), $this->_normalize(new static())];
- }
-
- if ($diff < 0) {
- // if $x is negative, "add" $y.
- if ($x_sign) {
- $x = $y->subtract($x);
- }
- return [$this->_normalize(new static()), $this->_normalize($x)];
- }
-
- // normalize $x and $y as described in HAC 14.23 / 14.24
- $msb = $y->value[count($y->value) - 1];
- for ($shift = 0; !($msb & self::$msb); ++$shift) {
- $msb <<= 1;
- }
- $x->_lshift($shift);
- $y->_lshift($shift);
- $y_value = &$y->value;
-
- $x_max = count($x->value) - 1;
- $y_max = count($y->value) - 1;
-
- $quotient = new static();
- $quotient_value = &$quotient->value;
- $quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1);
-
- static $temp, $lhs, $rhs;
- if (!isset($temp)) {
- $temp = new static();
- $lhs = new static();
- $rhs = new static();
- }
- $temp_value = &$temp->value;
- $rhs_value = &$rhs->value;
-
- // $temp = $y << ($x_max - $y_max-1) in base 2**26
- $temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value);
-
- while ($x->compare($temp) >= 0) {
- // calculate the "common residue"
- ++$quotient_value[$x_max - $y_max];
- $x = $x->subtract($temp);
- $x_max = count($x->value) - 1;
- }
-
- for ($i = $x_max; $i >= $y_max + 1; --$i) {
- $x_value = &$x->value;
- $x_window = [
- isset($x_value[$i]) ? $x_value[$i] : 0,
- isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0,
- isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0
- ];
- $y_window = [
- $y_value[$y_max],
- ($y_max > 0) ? $y_value[$y_max - 1] : 0
- ];
-
- $q_index = $i - $y_max - 1;
- if ($x_window[0] == $y_window[0]) {
- $quotient_value[$q_index] = self::$maxDigit;
- } else {
- $quotient_value[$q_index] = $this->_safe_divide(
- $x_window[0] * self::$baseFull + $x_window[1],
- $y_window[0]
- );
- }
-
- $temp_value = [$y_window[1], $y_window[0]];
-
- $lhs->value = [$quotient_value[$q_index]];
- $lhs = $lhs->multiply($temp);
-
- $rhs_value = [$x_window[2], $x_window[1], $x_window[0]];
-
- while ($lhs->compare($rhs) > 0) {
- --$quotient_value[$q_index];
-
- $lhs->value = [$quotient_value[$q_index]];
- $lhs = $lhs->multiply($temp);
- }
-
- $adjust = $this->_array_repeat(0, $q_index);
- $temp_value = [$quotient_value[$q_index]];
- $temp = $temp->multiply($y);
- $temp_value = &$temp->value;
- $temp_value = array_merge($adjust, $temp_value);
-
- $x = $x->subtract($temp);
-
- if ($x->compare($zero) < 0) {
- $temp_value = array_merge($adjust, $y_value);
- $x = $x->add($temp);
-
- --$quotient_value[$q_index];
- }
-
- $x_max = count($x_value) - 1;
- }
-
- // unnormalize the remainder
- $x->_rshift($shift);
-
- $quotient->is_negative = $x_sign != $y_sign;
-
- // calculate the "common residue", if appropriate
- if ($x_sign) {
- $y->_rshift($shift);
- $x = $y->subtract($x);
- }
-
- return [$this->_normalize($quotient), $this->_normalize($x)];
- }
-
- /**
- * Divides a BigInteger by a regular integer
- *
- * abc / x = a00 / x + b0 / x + c / x
- *
- * @param array $dividend
- * @param array $divisor
- * @return array
- * @access private
- */
- private static function _divide_digit($dividend, $divisor)
- {
- $carry = 0;
- $result = [];
-
- for ($i = count($dividend) - 1; $i >= 0; --$i) {
- $temp = self::$baseFull * $carry + $dividend[$i];
- $result[$i] = self::_safe_divide($temp, $divisor);
- $carry = (int) ($temp - $divisor * $result[$i]);
- }
-
- return [$result, $carry];
- }
-
- /**
- * Performs modular exponentiation.
- *
- * Here's an example:
- *
- * modPow($b, $c);
- *
- * echo $c->toString(); // outputs 10
- * ?>
- *
- *
- * @param \phpseclib\Math\BigInteger $e
- * @param \phpseclib\Math\BigInteger $n
- * @return \phpseclib\Math\BigInteger
- * @access public
- * @internal The most naive approach to modular exponentiation has very unreasonable requirements, and
- * and although the approach involving repeated squaring does vastly better, it, too, is impractical
- * for our purposes. The reason being that division - by far the most complicated and time-consuming
- * of the basic operations (eg. +,-,*,/) - occurs multiple times within it.
- *
- * Modular reductions resolve this issue. Although an individual modular reduction takes more time
- * then an individual division, when performed in succession (with the same modulo), they're a lot faster.
- *
- * The two most commonly used modular reductions are Barrett and Montgomery reduction. Montgomery reduction,
- * although faster, only works when the gcd of the modulo and of the base being used is 1. In RSA, when the
- * base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because
- * the product of two odd numbers is odd), but what about when RSA isn't used?
- *
- * In contrast, Barrett reduction has no such constraint. As such, some bigint implementations perform a
- * Barrett reduction after every operation in the modpow function. Others perform Barrett reductions when the
- * modulo is even and Montgomery reductions when the modulo is odd. BigInteger.java's modPow method, however,
- * uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and
- * the other, a power of two - and recombine them, later. This is the method that this modPow function uses.
- * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates.
- */
- public function modPow(BigInteger $e, BigInteger $n)
- {
- $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs();
-
- if ($e->compare(new static()) < 0) {
- $e = $e->abs();
-
- $temp = $this->modInverse($n);
- if ($temp === false) {
- return false;
- }
-
- return $this->_normalize($temp->modPow($e, $n));
- }
-
- if (MATH_BIGINTEGER_MODE == self::MODE_GMP) {
- $temp = new static();
- $temp->value = gmp_powm($this->value, $e->value, $n->value);
-
- return $this->_normalize($temp);
- }
-
- if ($this->compare(new static()) < 0 || $this->compare($n) > 0) {
- list(, $temp) = $this->divide($n);
- return $temp->modPow($e, $n);
- }
-
- if (defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
- $components = [
- 'modulus' => $n->toBytes(true),
- 'publicExponent' => $e->toBytes(true)
- ];
-
- $components = [
- 'modulus' => pack('Ca*a*', 2, ASN1::encodeLength(strlen($components['modulus'])), $components['modulus']),
- 'publicExponent' => pack('Ca*a*', 2, ASN1::encodeLength(strlen($components['publicExponent'])), $components['publicExponent'])
- ];
-
- $RSAPublicKey = pack(
- 'Ca*a*a*',
- 48,
- ASN1::encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
- $components['modulus'],
- $components['publicExponent']
- );
-
- $rsaOID = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"; // hex version of MA0GCSqGSIb3DQEBAQUA
- $RSAPublicKey = chr(0) . $RSAPublicKey;
- $RSAPublicKey = chr(3) . ASN1::encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
-
- $encapsulated = pack(
- 'Ca*a*',
- 48,
- ASN1::encodeLength(strlen($rsaOID . $RSAPublicKey)),
- $rsaOID . $RSAPublicKey
- );
-
- $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
- chunk_split(Base64::encode($encapsulated)) .
- '-----END PUBLIC KEY-----';
-
- $plaintext = str_pad($this->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT);
-
- if (openssl_public_encrypt($plaintext, $result, $RSAPublicKey, OPENSSL_NO_PADDING)) {
- return new static($result, 256);
- }
- }
-
- if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
- $temp = new static();
- $temp->value = bcpowmod($this->value, $e->value, $n->value, 0);
-
- return $this->_normalize($temp);
- }
-
- if (empty($e->value)) {
- $temp = new static();
- $temp->value = [1];
- return $this->_normalize($temp);
- }
-
- if ($e->value == [1]) {
- list(, $temp) = $this->divide($n);
- return $this->_normalize($temp);
- }
-
- if ($e->value == [2]) {
- $temp = new static();
- $temp->value = self::_square($this->value);
- list(, $temp) = $temp->divide($n);
- return $this->_normalize($temp);
- }
-
- return $this->_normalize($this->_slidingWindow($e, $n, self::BARRETT));
-
- // 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
-
- // is the modulo odd?
- if ($n->value[0] & 1) {
- return $this->_normalize($this->_slidingWindow($e, $n, self::MONTGOMERY));
- }
- // if it's not, it's even
-
- // find the lowest set bit (eg. the max pow of 2 that divides $n)
- for ($i = 0; $i < count($n->value); ++$i) {
- if ($n->value[$i]) {
- $temp = decbin($n->value[$i]);
- $j = strlen($temp) - strrpos($temp, '1') - 1;
- $j+= 26 * $i;
- break;
- }
- }
- // at this point, 2^$j * $n/(2^$j) == $n
-
- $mod1 = clone $n;
- $mod1->_rshift($j);
- $mod2 = new static();
- $mod2->value = [1];
- $mod2->_lshift($j);
-
- $part1 = ($mod1->value != [1]) ? $this->_slidingWindow($e, $mod1, self::MONTGOMERY) : new static();
- $part2 = $this->_slidingWindow($e, $mod2, self::POWEROF2);
-
- $y1 = $mod2->modInverse($mod1);
- $y2 = $mod1->modInverse($mod2);
-
- $result = $part1->multiply($mod2);
- $result = $result->multiply($y1);
-
- $temp = $part2->multiply($mod1);
- $temp = $temp->multiply($y2);
-
- $result = $result->add($temp);
- list(, $result) = $result->divide($n);
-
- return $this->_normalize($result);
- }
-
- /**
- * Performs modular exponentiation.
- *
- * Alias for modPow().
- *
- * @param \phpseclib\Math\BigInteger $e
- * @param \phpseclib\Math\BigInteger $n
- * @return \phpseclib\Math\BigInteger
- * @access public
- */
- public function powMod(BigInteger $e, BigInteger $n)
- {
- return $this->modPow($e, $n);
- }
-
- /**
- * Sliding Window k-ary Modular Exponentiation
- *
- * Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} /
- * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}. In a departure from those algorithims,
- * however, this function performs a modular reduction after every multiplication and squaring operation.
- * As such, this function has the same preconditions that the reductions being used do.
- *
- * @param \phpseclib\Math\BigInteger $e
- * @param \phpseclib\Math\BigInteger $n
- * @param int $mode
- * @return \phpseclib\Math\BigInteger
- * @access private
- */
- private function _slidingWindow($e, $n, $mode)
- {
- static $window_ranges = [7, 25, 81, 241, 673, 1793]; // from BigInteger.java's oddModPow function
- //static $window_ranges = [0, 7, 36, 140, 450, 1303, 3529]; // from MPM 7.3.1
-
- $e_value = $e->value;
- $e_length = count($e_value) - 1;
- $e_bits = decbin($e_value[$e_length]);
- for ($i = $e_length - 1; $i >= 0; --$i) {
- $e_bits.= str_pad(decbin($e_value[$i]), self::$base, '0', STR_PAD_LEFT);
- }
-
- $e_length = strlen($e_bits);
-
- // calculate the appropriate window size.
- // $window_size == 3 if $window_ranges is between 25 and 81, for example.
- for ($i = 0, $window_size = 1; $i < count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) {
- }
-
- $n_value = $n->value;
-
- // precompute $this^0 through $this^$window_size
- $powers = [];
- $powers[1] = self::_prepareReduce($this->value, $n_value, $mode);
- $powers[2] = self::_squareReduce($powers[1], $n_value, $mode);
-
- // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end
- // in a 1. ie. it's supposed to be odd.
- $temp = 1 << ($window_size - 1);
- for ($i = 1; $i < $temp; ++$i) {
- $i2 = $i << 1;
- $powers[$i2 + 1] = self::_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode);
- }
-
- $result = [1];
- $result = self::_prepareReduce($result, $n_value, $mode);
-
- for ($i = 0; $i < $e_length;) {
- if (!$e_bits[$i]) {
- $result = self::_squareReduce($result, $n_value, $mode);
- ++$i;
- } else {
- for ($j = $window_size - 1; $j > 0; --$j) {
- if (!empty($e_bits[$i + $j])) {
- break;
- }
- }
-
- // eg. the length of substr($e_bits, $i, $j + 1)
- for ($k = 0; $k <= $j; ++$k) {
- $result = self::_squareReduce($result, $n_value, $mode);
- }
-
- $result = self::_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode);
-
- $i += $j + 1;
- }
- }
-
- $temp = new static();
- $temp->value = self::_reduce($result, $n_value, $mode);
-
- return $temp;
- }
-
- /**
- * Modular reduction
- *
- * For most $modes this will return the remainder.
- *
- * @see self::_slidingWindow()
- * @access private
- * @param array $x
- * @param array $n
- * @param int $mode
- * @return array
- */
- private static function _reduce($x, $n, $mode)
- {
- switch ($mode) {
- case self::MONTGOMERY:
- return self::_montgomery($x, $n);
- case self::BARRETT:
- return self::_barrett($x, $n);
- case self::POWEROF2:
- $lhs = new static();
- $lhs->value = $x;
- $rhs = new static();
- $rhs->value = $n;
- return $x->_mod2($n);
- case self::CLASSIC:
- $lhs = new static();
- $lhs->value = $x;
- $rhs = new static();
- $rhs->value = $n;
- list(, $temp) = $lhs->divide($rhs);
- return $temp->value;
- case self::NONE:
- return $x;
- default:
- // an invalid $mode was provided
- }
- }
-
- /**
- * Modular reduction preperation
- *
- * @see self::_slidingWindow()
- * @access private
- * @param array $x
- * @param array $n
- * @param int $mode
- * @return array
- */
- private static function _prepareReduce($x, $n, $mode)
- {
- if ($mode == self::MONTGOMERY) {
- return self::_prepMontgomery($x, $n);
- }
- return self::_reduce($x, $n, $mode);
- }
-
- /**
- * Modular multiply
- *
- * @see self::_slidingWindow()
- * @access private
- * @param array $x
- * @param array $y
- * @param array $n
- * @param int $mode
- * @return array
- */
- private static function _multiplyReduce($x, $y, $n, $mode)
- {
- if ($mode == self::MONTGOMERY) {
- return self::_montgomeryMultiply($x, $y, $n);
- }
- $temp = self::_multiply($x, false, $y, false);
- return self::_reduce($temp[self::VALUE], $n, $mode);
- }
-
- /**
- * Modular square
- *
- * @see self::_slidingWindow()
- * @access private
- * @param array $x
- * @param array $n
- * @param int $mode
- * @return array
- */
- private static function _squareReduce($x, $n, $mode)
- {
- if ($mode == self::MONTGOMERY) {
- return self::_montgomeryMultiply($x, $x, $n);
- }
- return self::_reduce(self::_square($x), $n, $mode);
- }
-
- /**
- * Modulos for Powers of Two
- *
- * Calculates $x%$n, where $n = 2**$e, for some $e. Since this is basically the same as doing $x & ($n-1),
- * we'll just use this function as a wrapper for doing that.
- *
- * @see self::_slidingWindow()
- * @access private
- * @param \phpseclib\Math\BigInteger
- * @return \phpseclib\Math\BigInteger
- */
- private function _mod2($n)
- {
- $temp = new static();
- $temp->value = [1];
- return $this->bitwise_and($n->subtract($temp));
- }
-
- /**
- * 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.
- *
- * @see self::_slidingWindow()
- * @access private
- * @param array $n
- * @param array $m
- * @return array
- */
- private static function _barrett($n, $m)
- {
- static $cache = [
- self::VARIABLE => [],
- self::DATA => []
- ];
-
- $m_length = count($m);
-
- // if (self::_compare($n, self::_square($m)) >= 0) {
- if (count($n) > 2 * $m_length) {
- $lhs = new static();
- $rhs = new static();
- $lhs->value = $n;
- $rhs->value = $m;
- list(, $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);
- }
-
- // n = 2 * m.length
-
- if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
- $key = count($cache[self::VARIABLE]);
- $cache[self::VARIABLE][] = $m;
-
- $lhs = new static();
- $lhs_value = &$lhs->value;
- $lhs_value = self::_array_repeat(0, $m_length + ($m_length >> 1));
- $lhs_value[] = 1;
- $rhs = new static();
- $rhs->value = $m;
-
- list($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 = self::_multiply($msd, false, $m1, false);
- $n = self::_add($lsd, false, $temp[self::VALUE], false); // m.length + (m.length >> 1) + 1
-
- if ($m_length & 1) {
- return self::_regularBarrett($n[self::VALUE], $m);
- }
-
- // (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 = self::_multiply($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 = self::_multiply($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 = self::_subtract($n[self::VALUE], false, $temp[self::VALUE], false);
-
- while (self::_compare($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) {
- $result = self::_subtract($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.
- *
- * @see self::_slidingWindow()
- * @access private
- * @param array $x
- * @param array $n
- * @return array
- */
- private static function _regularBarrett($x, $n)
- {
- static $cache = [
- self::VARIABLE => [],
- self::DATA => []
- ];
-
- $n_length = count($n);
-
- if (count($x) > 2 * $n_length) {
- $lhs = new static();
- $rhs = new static();
- $lhs->value = $x;
- $rhs->value = $n;
- list(, $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 static();
- $lhs_value = &$lhs->value;
- $lhs_value = self::_array_repeat(0, 2 * $n_length);
- $lhs_value[] = 1;
- $rhs = new static();
- $rhs->value = $n;
- list($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 = self::_multiply($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);
- // $temp == array_slice(self::_multiply($temp, false, $n, false)->value, 0, $n_length + 1)
-
- if (self::_compare($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 = self::_add($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 = self::_subtract($result, false, $temp[self::VALUE], $temp[self::SIGN]);
- while (self::_compare($result[self::VALUE], $result[self::SIGN], $n, false) > 0) {
- $result = self::_subtract($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()
- * @param array $x_value
- * @param bool $x_negative
- * @param array $y_value
- * @param bool $y_negative
- * @param int $stop
- * @return array
- * @access private
- */
- private static function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop)
- {
- $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 = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
- $product_value[$j] = (int) ($temp - self::$baseFull * $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 = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
- $product_value[$k] = (int) ($temp - self::$baseFull * $carry);
- }
-
- if ($k < $stop) {
- $product_value[$k] = $carry;
- }
- }
-
+ list($q, $r) = $this->value->divide($y->value);
return [
- self::VALUE => self::_trim($product_value),
- self::SIGN => $x_negative != $y_negative
+ new static($q),
+ new static($r)
];
}
- /**
- * Montgomery Modular Reduction
- *
- * ($x->_prepMontgomery($n))->_montgomery($n) yields $x % $n.
- * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=170 MPM 6.3} provides insights on how this can be
- * improved upon (basically, by using the comba method). gcd($n, 2) must be equal to one for this function
- * to work correctly.
- *
- * @see self::_prepMontgomery()
- * @see self::_slidingWindow()
- * @access private
- * @param array $x
- * @param array $n
- * @return array
- */
- private static function _montgomery($x, $n)
- {
- 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);
- }
-
- $k = count($n);
-
- $result = [self::VALUE => $x];
-
- for ($i = 0; $i < $k; ++$i) {
- $temp = $result[self::VALUE][$i] * $cache[self::DATA][$key];
- $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
- $temp = self::_regularMultiply([$temp], $n);
- $temp = array_merge($this->_array_repeat(0, $i), $temp);
- $result = self::_add($result[self::VALUE], false, $temp, false);
- }
-
- $result[self::VALUE] = array_slice($result[self::VALUE], $k);
-
- if (self::_compare($result, false, $n, false) >= 0) {
- $result = self::_subtract($result[self::VALUE], false, $n, false);
- }
-
- return $result[self::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}
- *
- * @see self::_prepMontgomery()
- * @see self::_montgomery()
- * @access private
- * @param array $x
- * @param array $y
- * @param array $m
- * @return array
- */
- private static function _montgomeryMultiply($x, $y, $m)
- {
- $temp = self::_multiply($x, false, $y, false);
- return self::_montgomery($temp[self::VALUE], $m);
-
- // 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);
- }
-
- $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 - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
- $temp = $temp * $cache[self::DATA][$key];
- $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31));
- $temp = self::_add(self::_regularMultiply([$x[$i]], $y), false, self::_regularMultiply([$temp], $m), false);
- $a = self::_add($a[self::VALUE], false, $temp[self::VALUE], false);
- $a[self::VALUE] = array_slice($a[self::VALUE], 1);
- }
- if (self::_compare($a[self::VALUE], false, $m, false) >= 0) {
- $a = self::_subtract($a[self::VALUE], false, $m, false);
- }
- return $a[self::VALUE];
- }
-
- /**
- * Prepare a number for use in Montgomery Modular Reductions
- *
- * @see self::_montgomery()
- * @see self::_slidingWindow()
- * @access private
- * @param array $x
- * @param array $n
- * @return array
- */
- private static function _prepMontgomery($x, $n)
- {
- $lhs = new static();
- $lhs->value = array_merge(self::_array_repeat(0, count($n)), $x);
- $rhs = new static();
- $rhs->value = $n;
-
- list(, $temp) = $lhs->divide($rhs);
- return $temp->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!
- *
- * @see self::_montgomery()
- * @access private
- * @param array $x
- * @return int
- */
- private function _modInverse67108864($x) // 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 = fmod($result * (2 - fmod($x * $result, self::$baseFull)), self::$baseFull); // x**-1 mod 2**26
- return $result & self::$maxDigit;
- }
-
/**
* Calculates modular inverses.
*
* Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
*
- * Here's an example:
- *
- * modInverse($b);
- * echo $c->toString(); // outputs 4
- *
- * echo "\r\n";
- *
- * $d = $a->multiply($c);
- * list(, $d) = $d->divide($b);
- * echo $d; // outputs 1 (as per the definition of modular inverse)
- * ?>
- *
- *
* @param \phpseclib\Math\BigInteger $n
- * @return \phpseclib\Math\BigInteger|false
- * @access public
- * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information.
*/
public function modInverse(BigInteger $n)
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp = new static();
- $temp->value = gmp_invert($this->value, $n->value);
-
- return ($temp->value === false) ? false : $this->_normalize($temp);
- }
-
- static $zero, $one;
- if (!isset($zero)) {
- $zero = new static();
- $one = new static(1);
- }
-
- // $x mod -$n == $x mod $n.
- $n = $n->abs();
-
- if ($this->compare($zero) < 0) {
- $temp = $this->abs();
- $temp = $temp->modInverse($n);
- return $this->_normalize($n->subtract($temp));
- }
-
- extract($this->extendedGCD($n));
-
- if (!$gcd->equals($one)) {
- return false;
- }
-
- $x = $x->compare($zero) < 0 ? $x->add($n) : $x;
-
- return $this->compare($zero) < 0 ? $this->_normalize($n->subtract($x)) : $this->_normalize($x);
+ return new static($this->value->modInverse($n->value));
}
/**
- * Calculates the greatest common divisor and Bezout's identity.
+ * Calculates modular inverses.
*
- * Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that
- * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which
- * combination is returned is dependent upon which mode is in use. See
- * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information.
- *
- * Here's an example:
- *
- * extendedGCD($b));
- *
- * echo $gcd->toString() . "\r\n"; // outputs 21
- * echo $a->toString() * $x->toString() + $b->toString() * $y->toString(); // outputs 21
- * ?>
- *
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
*
* @param \phpseclib\Math\BigInteger $n
- * @return \phpseclib\Math\BigInteger
- * @access public
- * @internal Calculates the GCD using the binary xGCD algorithim described in
- * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes,
- * the more traditional algorithim requires "relatively costly multiple-precision divisions".
*/
public function extendedGCD(BigInteger $n)
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- extract(gmp_gcdext($this->value, $n->value));
-
- return [
- 'gcd' => $this->_normalize(new static($g)),
- 'x' => $this->_normalize(new static($s)),
- 'y' => $this->_normalize(new static($t))
- ];
- case self::MODE_BCMATH:
- // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works
- // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is,
- // the basic extended euclidean algorithim is what we're using.
-
- $u = $this->value;
- $v = $n->value;
-
- $a = '1';
- $b = '0';
- $c = '0';
- $d = '1';
-
- while (bccomp($v, '0', 0) != 0) {
- $q = bcdiv($u, $v, 0);
-
- $temp = $u;
- $u = $v;
- $v = bcsub($temp, bcmul($v, $q, 0), 0);
-
- $temp = $a;
- $a = $c;
- $c = bcsub($temp, bcmul($a, $q, 0), 0);
-
- $temp = $b;
- $b = $d;
- $d = bcsub($temp, bcmul($b, $q, 0), 0);
- }
-
- return [
- 'gcd' => $this->_normalize(new static($u)),
- 'x' => $this->_normalize(new static($a)),
- 'y' => $this->_normalize(new static($b))
- ];
- }
-
- $y = clone $n;
- $x = clone $this;
- $g = new static();
- $g->value = [1];
-
- while (!(($x->value[0] & 1)|| ($y->value[0] & 1))) {
- $x->_rshift(1);
- $y->_rshift(1);
- $g->_lshift(1);
- }
-
- $u = clone $x;
- $v = clone $y;
-
- $a = new static();
- $b = new static();
- $c = new static();
- $d = new static();
-
- $a->value = $d->value = $g->value = [1];
- $b->value = $c->value = [];
-
- while (!empty($u->value)) {
- while (!($u->value[0] & 1)) {
- $u->_rshift(1);
- if ((!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1))) {
- $a = $a->add($y);
- $b = $b->subtract($x);
- }
- $a->_rshift(1);
- $b->_rshift(1);
- }
-
- while (!($v->value[0] & 1)) {
- $v->_rshift(1);
- if ((!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1))) {
- $c = $c->add($y);
- $d = $d->subtract($x);
- }
- $c->_rshift(1);
- $d->_rshift(1);
- }
-
- if ($u->compare($v) >= 0) {
- $u = $u->subtract($v);
- $a = $a->subtract($c);
- $b = $b->subtract($d);
- } else {
- $v = $v->subtract($u);
- $c = $c->subtract($a);
- $d = $d->subtract($b);
- }
- }
-
+ extract($this->value->extendedGCD($n->value));
return [
- 'gcd' => $this->_normalize($g->multiply($v)),
- 'x' => $this->_normalize($c),
- 'y' => $this->_normalize($d)
+ 'gcd' => new static($gcd),
+ 'x' => new static($x),
+ 'y' => new static($y)
];
}
@@ -2542,26 +315,11 @@ class BigInteger
*
* Say you have 693 and 609. The GCD is 21.
*
- * Here's an example:
- *
- * extendedGCD($b);
- *
- * echo $gcd->toString() . "\r\n"; // outputs 21
- * ?>
- *
- *
- * @param \phpseclib\Math\BigInteger $n
* @return \phpseclib\Math\BigInteger
- * @access public
*/
public function gcd(BigInteger $n)
{
- extract($this->extendedGCD($n));
- return $gcd;
+ return new static($this->value->gcd($n->value));
}
/**
@@ -2572,20 +330,93 @@ class BigInteger
*/
public function abs()
{
- $temp = new static();
+ return new static($this->value->abs());
+ }
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp->value = gmp_abs($this->value);
- break;
- case self::MODE_BCMATH:
- $temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value;
- break;
- default:
- $temp->value = $this->value;
+ /**
+ * Set Precision
+ *
+ * Some bitwise operations give different results depending on the precision being used. Examples include left
+ * shift, not, and rotates.
+ *
+ * @param int $bits
+ */
+ public function setPrecision($bits)
+ {
+ $this->value->setPrecision($bits);
+ }
+
+ /**
+ * Get Precision
+ *
+ * Returns the precision if it exists, false if it doesn't
+ *
+ * @return int|bool
+ */
+ public function getPrecision()
+ {
+ return $this->value->getPrecision();
+ }
+
+ /**
+ * Serialize
+ *
+ * Will be called, automatically, when serialize() is called on a BigInteger object.
+ *
+ * phpseclib 1.0 serialized strings look like this:
+ * O:15:"Math_BigInteger":1:{s:3:"hex";s:18:"00ab54a98ceb1f0ad2";}
+ *
+ * phpseclib 3.0 serialized strings look like this:
+ * C:25:"phpseclib\Math\BigInteger":42:{a:1:{s:3:"hex";s:18:"00ab54a98ceb1f0ad2";}}
+ *
+ * @return string
+ */
+ public function serialize()
+ {
+ $val = ['hex' => $this->toHex(true)];
+ $precision = $this->value->getPrecision();
+ if ($precision > 0) {
+ $val['precision'] = $precision;
}
+ return serialize($val);
+ }
- return $temp;
+ /**
+ * Serialize
+ *
+ * Will be called, automatically, when unserialize() is called on a BigInteger object.
+ *
+ * @param string $serialized
+ */
+ public function unserialize($serialized)
+ {
+ $r = unserialize($serialized);
+ $temp = new static($r['hex'], -16);
+ $this->value = $temp->value;
+ if (isset($r['precision'])) {
+ // recalculate $this->bitmask
+ $this->setPrecision($r['precision']);
+ }
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @return \phpseclib\Math\BigInteger
+ */
+ public function powMod(BigInteger $e, BigInteger $n)
+ {
+ return new static($this->value->powMod($e->value, $n->value));
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @return \phpseclib\Math\BigInteger
+ */
+ public function modPow(BigInteger $e, BigInteger $n)
+ {
+ return new static($this->value->modPow($e->value, $n->value));
}
/**
@@ -2608,50 +439,7 @@ class BigInteger
*/
public function compare(BigInteger $y)
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- return gmp_cmp($this->value, $y->value);
- case self::MODE_BCMATH:
- return bccomp($this->value, $y->value, 0);
- }
-
- return self::_compare($this->value, $this->is_negative, $y->value, $y->is_negative);
- }
-
- /**
- * Compares two numbers.
- *
- * @param array $x_value
- * @param bool $x_negative
- * @param array $y_value
- * @param bool $y_negative
- * @return int
- * @see self::compare()
- * @access private
- */
- private static function _compare($x_value, $x_negative, $y_value, $y_negative)
- {
- if ($x_negative != $y_negative) {
- return (!$x_negative && $y_negative) ? 1 : -1;
- }
-
- $result = $x_negative ? -1 : 1;
-
- if (count($x_value) != count($y_value)) {
- return (count($x_value) > count($y_value)) ? $result : -$result;
- }
- $size = max(count($x_value), count($y_value));
-
- $x_value = array_pad($x_value, $size, 0);
- $y_value = array_pad($y_value, $size, 0);
-
- for ($i = count($x_value) - 1; $i >= 0; --$i) {
- if ($x_value[$i] != $y_value[$i]) {
- return ($x_value[$i] > $y_value[$i]) ? $result : -$result;
- }
- }
-
- return 0;
+ return $this->value->compare($y->value);
}
/**
@@ -2659,220 +447,51 @@ class BigInteger
*
* If you need to see if one number is greater than or less than another number, use BigInteger::compare()
*
- * @param \phpseclib\Math\BigInteger $x
* @return bool
- * @access public
- * @see self::compare()
*/
public function equals(BigInteger $x)
{
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- return gmp_cmp($this->value, $x->value) == 0;
- default:
- return $this->value === $x->value && $this->is_negative == $x->is_negative;
- }
- }
-
- /**
- * Set Precision
- *
- * Some bitwise operations give different results depending on the precision being used. Examples include left
- * shift, not, and rotates.
- *
- * @param int $bits
- * @access public
- */
- public function setPrecision($bits)
- {
- if ($bits < 1) {
- $this->precision = -1;
- $this->bitmask = false;
-
- return;
- }
- $this->precision = $bits;
- if (MATH_BIGINTEGER_MODE != self::MODE_BCMATH) {
- $this->bitmask = new static(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256);
- } else {
- $this->bitmask = new static(bcpow('2', $bits, 0));
- }
-
- $temp = $this->_normalize($this);
- $this->value = $temp->value;
- }
-
- /**
- * Get Precision
- *
- * @return int
- * @see self::setPrecision()
- * @access public
- */
- public function getPrecision()
- {
- return $this->precision;
- }
-
- /**
- * Logical And
- *
- * @param \phpseclib\Math\BigInteger $x
- * @access public
- * @internal Implemented per a request by Lluis Pamies i Juarez
- * @return \phpseclib\Math\BigInteger
- */
- public function bitwise_and(BigInteger $x)
- {
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp = new static();
- $temp->value = gmp_and($this->value, $x->value);
-
- return $this->_normalize($temp);
- case self::MODE_BCMATH:
- $left = $this->toBytes();
- $right = $x->toBytes();
-
- $length = max(strlen($left), strlen($right));
-
- $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
- $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
-
- return $this->_normalize(new static($left & $right, 256));
- }
-
- $result = clone $this;
-
- $length = min(count($x->value), count($this->value));
-
- $result->value = array_slice($result->value, 0, $length);
-
- for ($i = 0; $i < $length; ++$i) {
- $result->value[$i]&= $x->value[$i];
- }
-
- return $this->_normalize($result);
- }
-
- /**
- * Logical Or
- *
- * @param \phpseclib\Math\BigInteger $x
- * @access public
- * @internal Implemented per a request by Lluis Pamies i Juarez
- * @return \phpseclib\Math\BigInteger
- */
- public function bitwise_or(BigInteger $x)
- {
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp = new static();
- $temp->value = gmp_or($this->value, $x->value);
-
- return $this->_normalize($temp);
- case self::MODE_BCMATH:
- $left = $this->toBytes();
- $right = $x->toBytes();
-
- $length = max(strlen($left), strlen($right));
-
- $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
- $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
-
- return $this->_normalize(new static($left | $right, 256));
- }
-
- $length = max(count($this->value), count($x->value));
- $result = clone $this;
- $result->value = array_pad($result->value, $length, 0);
- $x->value = array_pad($x->value, $length, 0);
-
- for ($i = 0; $i < $length; ++$i) {
- $result->value[$i]|= $x->value[$i];
- }
-
- return $this->_normalize($result);
- }
-
- /**
- * Logical Exclusive-Or
- *
- * @param \phpseclib\Math\BigInteger $x
- * @access public
- * @internal Implemented per a request by Lluis Pamies i Juarez
- * @return \phpseclib\Math\BigInteger
- */
- public function bitwise_xor(BigInteger $x)
- {
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $temp = new static();
- $temp->value = gmp_xor($this->value, $x->value);
-
- return $this->_normalize($temp);
- case self::MODE_BCMATH:
- $left = $this->toBytes();
- $right = $x->toBytes();
-
- $length = max(strlen($left), strlen($right));
-
- $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
- $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
-
- return $this->_normalize(new static($left ^ $right, 256));
- }
-
- $length = max(count($this->value), count($x->value));
- $result = clone $this;
- $result->value = array_pad($result->value, $length, 0);
- $x->value = array_pad($x->value, $length, 0);
-
- for ($i = 0; $i < $length; ++$i) {
- $result->value[$i]^= $x->value[$i];
- }
-
- return $this->_normalize($result);
+ return $this->value->equals($x->value);
}
/**
* Logical Not
*
- * @access public
- * @internal Implemented per a request by Lluis Pamies i Juarez
* @return \phpseclib\Math\BigInteger
*/
public function bitwise_not()
{
- // calculuate "not" without regard to $this->precision
- // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0)
- $temp = $this->toBytes();
- if ($temp == '') {
- return '';
- }
- $pre_msb = decbin(ord($temp[0]));
- $temp = ~$temp;
- $msb = decbin(ord($temp[0]));
- if (strlen($msb) == 8) {
- $msb = substr($msb, strpos($msb, '0'));
- }
- $temp[0] = chr(bindec($msb));
+ return new static($this->value->bitwise_not());
+ }
- // see if we need to add extra leading 1's
- $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8;
- $new_bits = $this->precision - $current_bits;
- if ($new_bits <= 0) {
- return $this->_normalize(new static($temp, 256));
- }
+ /**
+ * Logical And
+ *
+ * @return \phpseclib\Math\BigInteger
+ */
+ public function bitwise_and(BigInteger $x)
+ {
+ return new static($this->value->bitwise_and($x->value));
+ }
- // generate as many leading 1's as we need to.
- $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3);
+ /**
+ * Logical Or
+ *
+ * @return \phpseclib\Math\BigInteger
+ */
+ public function bitwise_or(BigInteger $x)
+ {
+ return new static($this->value->bitwise_or($x->value));
+ }
- self::_base256_lshift($leading_ones, $current_bits);
-
- $temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT);
-
- return $this->_normalize(new static($leading_ones | $temp, 256));
+ /**
+ * Logical Exlusive Or
+ *
+ * @return \phpseclib\Math\BigInteger
+ */
+ public function bitwise_xor(BigInteger $x)
+ {
+ return new static($this->value->bitwise_xor($x->value));
}
/**
@@ -2882,35 +501,10 @@ class BigInteger
*
* @param int $shift
* @return \phpseclib\Math\BigInteger
- * @access public
- * @internal The only version that yields any speed increases is the internal version.
*/
public function bitwise_rightShift($shift)
{
- $temp = new static();
-
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- static $two;
-
- if (!isset($two)) {
- $two = gmp_init('2');
- }
-
- $temp->value = gmp_div_q($this->value, gmp_pow($two, $shift));
-
- break;
- case self::MODE_BCMATH:
- $temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0);
-
- break;
- default: // could just replace _lshift with this, but then all _lshift() calls would need to be rewritten
- // and I don't want to do that...
- $temp->value = $this->value;
- $temp->_rshift($shift);
- }
-
- return $this->_normalize($temp);
+ return new static($this->value->bitwise_rightShift($shift));
}
/**
@@ -2920,35 +514,10 @@ class BigInteger
*
* @param int $shift
* @return \phpseclib\Math\BigInteger
- * @access public
- * @internal The only version that yields any speed increases is the internal version.
*/
public function bitwise_leftShift($shift)
{
- $temp = new static();
-
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- static $two;
-
- if (!isset($two)) {
- $two = gmp_init('2');
- }
-
- $temp->value = gmp_mul($this->value, gmp_pow($two, $shift));
-
- break;
- case self::MODE_BCMATH:
- $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0);
-
- break;
- default: // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten
- // and I don't want to do that...
- $temp->value = $this->value;
- $temp->_lshift($shift);
- }
-
- return $this->_normalize($temp);
+ return new static($this->value->bitwise_leftShift($shift));
}
/**
@@ -2958,42 +527,10 @@ class BigInteger
*
* @param int $shift
* @return \phpseclib\Math\BigInteger
- * @access public
*/
public function bitwise_leftRotate($shift)
{
- $bits = $this->toBytes();
-
- if ($this->precision > 0) {
- $precision = $this->precision;
- if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
- $mask = $this->bitmask->subtract(new static(1));
- $mask = $mask->toBytes();
- } else {
- $mask = $this->bitmask->toBytes();
- }
- } else {
- $temp = ord($bits[0]);
- for ($i = 0; $temp >> $i; ++$i) {
- }
- $precision = 8 * strlen($bits) - 8 + $i;
- $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3);
- }
-
- if ($shift < 0) {
- $shift+= $precision;
- }
- $shift%= $precision;
-
- if (!$shift) {
- return clone $this;
- }
-
- $left = $this->bitwise_leftShift($shift);
- $left = $left->bitwise_and(new static($mask, 256));
- $right = $this->bitwise_rightShift($precision - $shift);
- $result = MATH_BIGINTEGER_MODE != self::MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right);
- return $this->_normalize($result);
+ return new static($this->value->bitwise_leftRotate($shift));
}
/**
@@ -3003,51 +540,87 @@ class BigInteger
*
* @param int $shift
* @return \phpseclib\Math\BigInteger
- * @access public
*/
public function bitwise_rightRotate($shift)
{
- return $this->bitwise_leftRotate(-$shift);
+ return new static($this->value->bitwise_rightRotate($shift));
}
/**
* Returns the smallest and largest n-bit number
*
* @param int $bits
- * @return \phpseclib\Math\BigInteger
- * @access public
+ * @return array
*/
public static function minMaxBits($bits)
{
- $bytes = $bits >> 3;
- $min = str_repeat(chr(0), $bytes);
- $max = str_repeat(chr(0xFF), $bytes);
- $msb = $bits & 7;
- if ($msb) {
- $min = chr(1 << ($msb - 1)) . $min;
- $max = chr((1 << $msb) - 1) . $max;
- } else {
- $min[0] = chr(0x80);
- }
+ $class = self::$mainEngine;
+ extract($class::minMaxBits($bits));
return [
- 'min' => new static($min, 256),
- 'max' => new static($max, 256)
+ 'min' => new static($min),
+ 'max' => new static($max)
];
}
+ /**
+ * Return the size of a BigInteger in bits
+ *
+ * @return int
+ */
+ public function getLength()
+ {
+ return $this->value->getLength();
+ }
+
+ /**
+ * Return the size of a BigInteger in bytes
+ *
+ * @return int
+ */
+ public function getLengthInBytes()
+ {
+ return $this->value->getLengthInBytes();
+ }
+
/**
* Generates a random number of a certain size
*
- * Bit length is equal to $size.
+ * Bit length is equal to $size
*
* @param int $size
* @return \phpseclib\Math\BigInteger
- * @access public
*/
public static function random($size)
{
- extract(self::minMaxBits($size));
- return self::randomRange($min, $max);
+ $class = self::$mainEngine;
+ return new static($class::random($size));
+ }
+
+ /**
+ * Generates a random prime number of a certain size
+ *
+ * Bit length is equal to $size
+ *
+ * @param int $size
+ * @return \phpseclib\Math\BigInteger
+ */
+ public static function randomPrime($size)
+ {
+ $class = self::$mainEngine;
+ return new static($class::randomPrime($size));
+ }
+
+ /**
+ * Generate a random prime number between a range
+ *
+ * If there's not a prime within the given range, false will be returned.
+ *
+ * @return \phpseclib\Math\BigInteger|false
+ */
+ public static function randomRangePrime(BigInteger $min, BigInteger $max)
+ {
+ $class = self::$mainEngine;
+ return new static($class::randomRangePrime($min->value, $max->value));
}
/**
@@ -3059,190 +632,12 @@ class BigInteger
* BigInteger::randomRange($min, $max)
* BigInteger::randomRange($max, $min)
*
- * @param \phpseclib\Math\BigInteger $arg1
- * @param \phpseclib\Math\BigInteger $arg2
* @return \phpseclib\Math\BigInteger
- * @access public
*/
public static function randomRange(BigInteger $min, BigInteger $max)
{
- $compare = $max->compare($min);
-
- if (!$compare) {
- return $min;
- } elseif ($compare < 0) {
- // if $min is bigger then $max, swap $min and $max
- $temp = $max;
- $max = $min;
- $min = $temp;
- }
-
- static $one;
- if (!isset($one)) {
- $one = new static(1);
- }
-
- $max = $max->subtract($min->subtract($one));
-
- $size = strlen(ltrim($max->toBytes(), chr(0)));
-
- /*
- doing $random % $max doesn't work because some numbers will be more likely to occur than others.
- 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.
-
- creating a whole new random number until you find one that is within the range doesn't work
- because, for sufficiently small ranges, the likelihood that you'd get a number within that range
- would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability
- would be pretty high that $random would be greater than $max.
-
- 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 static(chr(1) . str_repeat("\0", $size), 256);
- $random = new static(Random::string($size), 256);
-
- 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(new static(Random::string(1), 256));
- $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 $random->add($min);
- }
-
- /**
- * Generates a random prime number of a certain size
- *
- * Bit length is equal to $size
- *
- * @param int $size
- * @return \phpseclib\Math\BigInteger
- * @access public
- */
- public static function randomPrime($size)
- {
- extract(self::minMaxBits($size));
- return self::randomRangePrime($min, $max);
- }
-
- /**
- * Generate a random prime number between a range
- *
- * If there's not a prime within the given range, false will be returned.
- *
- * @param \phpseclib\Math\BigInteger $min
- * @param \phpseclib\Math\BigInteger $max
- * @return Math_BigInteger|false
- * @access public
- * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}.
- */
- public static function randomRangePrime(BigInteger $min, BigInteger $max)
- {
- $compare = $max->compare($min);
-
- if (!$compare) {
- return $min->isPrime() ? $min : false;
- } elseif ($compare < 0) {
- // if $min is bigger then $max, swap $min and $max
- $temp = $max;
- $max = $min;
- $min = $temp;
- }
-
- static $one, $two;
- if (!isset($one)) {
- $one = new static(1);
- $two = new static(2);
- }
-
- $x = self::randomRange($min, $max);
-
- // gmp_nextprime() requires PHP 5 >= 5.2.0 per .
- if (MATH_BIGINTEGER_MODE == self::MODE_GMP && extension_loaded('gmp')) {
- $p = new static();
- $p->value = gmp_nextprime($x->value);
-
- if ($p->compare($max) <= 0) {
- return $p;
- }
-
- if (!$min->equals($x)) {
- $x = $x->subtract($one);
- }
-
- return self::randomRangePrime($min, $x);
- }
-
- if ($x->equals($two)) {
- return $x;
- }
-
- $x->_make_odd();
- if ($x->compare($max) > 0) {
- // if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range
- if ($min->equals($max)) {
- return false;
- }
- $x = clone $min;
- $x->_make_odd();
- }
-
- $initial_x = clone $x;
-
- while (true) {
- if ($x->isPrime()) {
- return $x;
- }
-
- $x = $x->add($two);
-
- if ($x->compare($max) > 0) {
- $x = clone $min;
- if ($x->equals($two)) {
- return $x;
- }
- $x->_make_odd();
- }
-
- if ($x->equals($initial_x)) {
- return false;
- }
- }
- }
-
- /**
- * Make the current number odd
- *
- * If the current number is odd it'll be unchanged. If it's even, one will be added to it.
- *
- * @see self::randomPrime()
- * @access private
- */
- private function _make_odd()
- {
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- gmp_setbit($this->value, 0);
- break;
- case self::MODE_BCMATH:
- if ($this->value[strlen($this->value) - 1] % 2 == 0) {
- $this->value = bcadd($this->value, '1');
- }
- break;
- default:
- $this->value[0] |= 1;
- }
+ $class = self::$mainEngine;
+ return new static($class::randomRange($min->value, $max->value));
}
/**
@@ -3252,426 +647,12 @@ class BigInteger
* $t parameter is distributability. BigInteger::randomPrime() can be distributed across multiple pageloads
* on a website instead of just one.
*
- * @param \phpseclib\Math\BigInteger $t
+ * @param int $t
* @return bool
- * @access public
- * @internal Uses the
- * {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}. See
- * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24}.
*/
public function isPrime($t = false)
{
- $length = strlen($this->toBytes());
-
- if (!$t) {
- // see HAC 4.49 "Note (controlling the error probability)"
- // @codingStandardsIgnoreStart
- if ($length >= 163) { $t = 2; } // floor(1300 / 8)
- else if ($length >= 106) { $t = 3; } // floor( 850 / 8)
- else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8)
- else if ($length >= 68 ) { $t = 5; } // floor( 550 / 8)
- else if ($length >= 56 ) { $t = 6; } // floor( 450 / 8)
- else if ($length >= 50 ) { $t = 7; } // floor( 400 / 8)
- else if ($length >= 43 ) { $t = 8; } // floor( 350 / 8)
- else if ($length >= 37 ) { $t = 9; } // floor( 300 / 8)
- else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8)
- else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8)
- else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8)
- else { $t = 27; }
- // @codingStandardsIgnoreEnd
- }
-
- // ie. gmp_testbit($this, 0)
- // ie. isEven() or !isOdd()
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- return gmp_prob_prime($this->value, $t) != 0;
- case self::MODE_BCMATH:
- if ($this->value === '2') {
- return true;
- }
- if ($this->value[strlen($this->value) - 1] % 2 == 0) {
- return false;
- }
- break;
- default:
- if ($this->value == [2]) {
- return true;
- }
- if (~$this->value[0] & 1) {
- return false;
- }
- }
-
- static $primes, $zero, $one, $two;
-
- if (!isset($primes)) {
- $primes = [
- 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,
- 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137,
- 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
- 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
- 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
- 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
- 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617,
- 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727,
- 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829,
- 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947,
- 953, 967, 971, 977, 983, 991, 997
- ];
-
- if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
- for ($i = 0; $i < count($primes); ++$i) {
- $primes[$i] = new static($primes[$i]);
- }
- }
-
- $zero = new static();
- $one = new static(1);
- $two = new static(2);
- }
-
- if ($this->equals($one)) {
- return false;
- }
-
- // see HAC 4.4.1 "Random search for probable primes"
- if (MATH_BIGINTEGER_MODE != self::MODE_INTERNAL) {
- foreach ($primes as $prime) {
- list(, $r) = $this->divide($prime);
- if ($r->equals($zero)) {
- return $this->equals($prime);
- }
- }
- } else {
- $value = $this->value;
- foreach ($primes as $prime) {
- list(, $r) = self::_divide_digit($value, $prime);
- if (!$r) {
- return count($value) == 1 && $value[0] == $prime;
- }
- }
- }
-
- $n = clone $this;
- $n_1 = $n->subtract($one);
- $n_2 = $n->subtract($two);
-
- $r = clone $n_1;
- $r_value = $r->value;
- // ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
- if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) {
- $s = 0;
- // if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals($one) check earlier
- while ($r->value[strlen($r->value) - 1] % 2 == 0) {
- $r->value = bcdiv($r->value, '2', 0);
- ++$s;
- }
- } else {
- for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) {
- $temp = ~$r_value[$i] & 0xFFFFFF;
- for ($j = 1; ($temp >> $j) & 1; ++$j) {
- }
- if ($j != 25) {
- break;
- }
- }
- $s = 26 * $i + $j - 1;
- $r->_rshift($s);
- }
-
- for ($i = 0; $i < $t; ++$i) {
- $a = self::randomRange($two, $n_2);
- $y = $a->modPow($r, $n);
-
- if (!$y->equals($one) && !$y->equals($n_1)) {
- for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) {
- $y = $y->modPow($two, $n);
- if ($y->equals($one)) {
- return false;
- }
- }
-
- if (!$y->equals($n_1)) {
- return false;
- }
- }
- }
- return true;
- }
-
- /**
- * Logical Left Shift
- *
- * Shifts BigInteger's by $shift bits.
- *
- * @param int $shift
- * @access private
- */
- private function _lshift($shift)
- {
- if ($shift == 0) {
- return;
- }
-
- $num_digits = (int) ($shift / self::$base);
- $shift %= self::$base;
- $shift = 1 << $shift;
-
- $carry = 0;
-
- for ($i = 0; $i < count($this->value); ++$i) {
- $temp = $this->value[$i] * $shift + $carry;
- $carry = self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
- $this->value[$i] = (int) ($temp - $carry * self::$baseFull);
- }
-
- if ($carry) {
- $this->value[count($this->value)] = $carry;
- }
-
- while ($num_digits--) {
- array_unshift($this->value, 0);
- }
- }
-
- /**
- * Logical Right Shift
- *
- * Shifts BigInteger's by $shift bits.
- *
- * @param int $shift
- * @access private
- */
- private function _rshift($shift)
- {
- if ($shift == 0) {
- return;
- }
-
- $num_digits = (int) ($shift / self::$base);
- $shift %= self::$base;
- $carry_shift = self::$base - $shift;
- $carry_mask = (1 << $shift) - 1;
-
- if ($num_digits) {
- $this->value = array_slice($this->value, $num_digits);
- }
-
- $carry = 0;
-
- for ($i = count($this->value) - 1; $i >= 0; --$i) {
- $temp = $this->value[$i] >> $shift | $carry;
- $carry = ($this->value[$i] & $carry_mask) << $carry_shift;
- $this->value[$i] = $temp;
- }
-
- $this->value = $this->_trim($this->value);
- }
-
- /**
- * Normalize
- *
- * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
- *
- * @param \phpseclib\Math\BigInteger
- * @return \phpseclib\Math\BigInteger
- * @see self::_trim()
- * @access private
- */
- private function _normalize($result)
- {
- $result->precision = $this->precision;
- $result->bitmask = $this->bitmask;
-
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- if ($this->bitmask !== false) {
- $result->value = gmp_and($result->value, $result->bitmask->value);
- }
-
- return $result;
- case self::MODE_BCMATH:
- if (!empty($result->bitmask->value)) {
- $result->value = bcmod($result->value, $result->bitmask->value);
- }
-
- return $result;
- }
-
- $value = &$result->value;
-
- if (!count($value)) {
- return $result;
- }
-
- $value = $this->_trim($value);
-
- if (!empty($result->bitmask->value)) {
- $length = min(count($value), count($this->bitmask->value));
- $value = array_slice($value, 0, $length);
-
- for ($i = 0; $i < $length; ++$i) {
- $value[$i] = $value[$i] & $this->bitmask->value[$i];
- }
- }
-
- return $result;
- }
-
- /**
- * Trim
- *
- * Removes leading zeros
- *
- * @param array $value
- * @return \phpseclib\Math\BigInteger
- * @access private
- */
- private static function _trim($value)
- {
- for ($i = count($value) - 1; $i >= 0; --$i) {
- if ($value[$i]) {
- break;
- }
- unset($value[$i]);
- }
-
- return $value;
- }
-
- /**
- * Array Repeat
- *
- * @param $input Array
- * @param $multiplier mixed
- * @return array
- * @access private
- */
- private static function _array_repeat($input, $multiplier)
- {
- return ($multiplier) ? array_fill(0, $multiplier, $input) : [];
- }
-
- /**
- * Logical Left Shift
- *
- * Shifts binary strings $shift bits, essentially multiplying by 2**$shift.
- *
- * @param $x String
- * @param $shift Integer
- * @return string
- * @access private
- */
- private static function _base256_lshift(&$x, $shift)
- {
- if ($shift == 0) {
- return;
- }
-
- $num_bytes = $shift >> 3; // eg. floor($shift/8)
- $shift &= 7; // eg. $shift % 8
-
- $carry = 0;
- for ($i = strlen($x) - 1; $i >= 0; --$i) {
- $temp = ord($x[$i]) << $shift | $carry;
- $x[$i] = chr($temp);
- $carry = $temp >> 8;
- }
- $carry = ($carry != 0) ? chr($carry) : '';
- $x = $carry . $x . str_repeat(chr(0), $num_bytes);
- }
-
- /**
- * Logical Right Shift
- *
- * Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder.
- *
- * @param $x String
- * @param $shift Integer
- * @return string
- * @access private
- */
- private static function _base256_rshift(&$x, $shift)
- {
- if ($shift == 0) {
- $x = ltrim($x, chr(0));
- return '';
- }
-
- $num_bytes = $shift >> 3; // eg. floor($shift/8)
- $shift &= 7; // eg. $shift % 8
-
- $remainder = '';
- if ($num_bytes) {
- $start = $num_bytes > strlen($x) ? -strlen($x) : -$num_bytes;
- $remainder = substr($x, $start);
- $x = substr($x, 0, -$num_bytes);
- }
-
- $carry = 0;
- $carry_shift = 8 - $shift;
- for ($i = 0; $i < strlen($x); ++$i) {
- $temp = (ord($x[$i]) >> $shift) | $carry;
- $carry = (ord($x[$i]) << $carry_shift) & 0xFF;
- $x[$i] = chr($temp);
- }
- $x = ltrim($x, chr(0));
-
- $remainder = chr($carry >> $carry_shift) . $remainder;
-
- return ltrim($remainder, chr(0));
- }
-
- // one quirk about how the following functions are implemented is that PHP defines N to be an unsigned long
- // at 32-bits, while java's longs are 64-bits.
-
- /**
- * Converts 32-bit integers to bytes.
- *
- * @param int $x
- * @return string
- * @access private
- */
- private static function _int2bytes($x)
- {
- return ltrim(pack('N', $x), chr(0));
- }
-
- /**
- * Converts bytes to 32-bit integers
- *
- * @param string $x
- * @return int
- * @access private
- */
- private static function _bytes2int($x)
- {
- $temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT));
- return $temp['int'];
- }
-
- /**
- * Single digit division
- *
- * Even if int64 is being used the division operator will return a float64 value
- * if the dividend is not evenly divisible by the divisor. Since a float64 doesn't
- * have the precision of int64 this is a problem so, when int64 is being used,
- * we'll guarantee that the dividend is divisible by first subtracting the remainder.
- *
- * @access private
- * @param int $x
- * @param int $y
- * @return int
- */
- private static function _safe_divide($x, $y)
- {
- if (self::$base === 26) {
- return (int) ($x / $y);
- }
-
- // self::$base === 31
- return ($x - ($x % $y)) / $y;
+ return $this->value->isPrime($t);
}
/**
@@ -3679,205 +660,63 @@ class BigInteger
*
* Returns the nth root of a positive biginteger, where n defaults to 2
*
- * Here's an example:
- *
- * root();
- *
- * echo $root->toString(); // outputs 25
- * ?>
- *
- *
- * @param \phpseclib\Math\BigInteger $n
- * @access public
+ * @param int $n optional
* @return \phpseclib\Math\BigInteger
- * @internal This function is based off of {@link http://mathforum.org/library/drmath/view/52605.html this page} and {@link http://stackoverflow.com/questions/11242920/calculating-nth-root-with-bcmath-in-php this stackoverflow question}.
*/
- public function root($n = null)
+ public function root($n = 2)
{
- static $zero, $one, $two;
- if (!isset($one)) {
- $zero = new static(0);
- $one = new static(1);
- $two = new static(2);
- }
- if ($n === null) {
- $n = $two;
- }
- if ($n->compare($one) == -1) {
- return $zero;
- } // we want positive exponents
- if ($this->compare($one) == -1) {
- return new static(0);
- } // we want positive numbers
- if ($this->compare($two) == -1) {
- return $one;
- } // n-th root of 1 or 2 is 1
-
- $root = new static();
- if (MATH_BIGINTEGER_MODE == self::MODE_GMP && function_exists('gmp_root')) {
- $root->value = gmp_root($this->value, gmp_intval($n->value));
- return $this->_normalize($root);
- }
-
- // g is our guess number
- $g = $two;
- // while (g^n < num) g=g*2
- while ($g->pow($n)->compare($this) == -1) {
- $g = $g->multiply($two);
- }
- // if (g^n==num) num is a power of 2, we're lucky, end of job
- // == 0 bccomp(bcpow($g,$n), $n->value)==0
- if ($g->pow($n)->equals($this)) {
- $root = $g;
- return $this->_normalize($root);
- }
-
- // if we're here num wasn't a power of 2 :(
- $og = $g; // og means original guess and here is our upper bound
- $g = $g->divide($two)[0]; // g is set to be our lower bound
- $step = $og->subtract($g)->divide($two)[0]; // step is the half of upper bound - lower bound
- $g = $g->add($step); // we start at lower bound + step , basically in the middle of our interval
-
- // while step>1
-
- while ($step->compare($one) == 1) {
- $guess = $g->pow($n);
- $step = $step->divide($two)[0];
- $comp = $guess->compare($this); // compare our guess with real number
- switch ($comp) {
- case -1: // if guess is lower we add the new step
- $g = $g->add($step);
- break;
- case 1: // if guess is higher we sub the new step
- $g = $g->subtract($step);
- break;
- case 0: // if guess is exactly the num we're done, we return the value
- $root = $g;
- break 2;
- }
- }
-
- if ($comp == 1) {
- $g = $g->subtract($step);
- }
-
- // whatever happened, g is the closest guess we can make so return it
- $root = $g;
-
- return $this->_normalize($root);
+ return new static($this->value->root($n));
}
/**
* Performs exponentiation.
*
- * @param \phpseclib\Math\BigInteger $n
- * @access public
* @return \phpseclib\Math\BigInteger
*/
- public function pow($n)
+ public function pow(BigInteger $n)
{
- $zero = new static(0);
- if ($n->compare($zero) == 0) {
- return new static(1);
- } // n^0 = 1
-
- $res = new static();
- switch (MATH_BIGINTEGER_MODE) {
- case self::MODE_GMP:
- $res->value = gmp_pow($this->value, gmp_intval($n->value));
-
- return $this->_normalize($res);
- case self::MODE_BCMATH:
- $res->value = bcpow($this->value, $n->value);
-
- return $this->_normalize($res);
- default:
- $one = new static(1);
- $res = $this;
- while (!$n->equals($one)) {
- $res = $res->multiply($this);
- $n = $n->subtract($one);
- }
-
- return $res;
- }
+ return new static($this->value->pow($n->value));
}
/**
* Return the minimum BigInteger between an arbitrary number of BigIntegers.
*
- * @param \phpseclib\Math\BigInteger ...$param
- * @access public
* @return \phpseclib\Math\BigInteger
*/
- public static function min()
+ public static function min(BigInteger ...$nums)
{
- $args = func_get_args();
- if (count($args) == 1) {
- return $args[0];
- }
- $min = $args[0];
- for ($i = 1; $i < count($args); $i++) {
- $min = $min->compare($args[$i]) > 0 ? $args[$i] : $min;
- }
- return $min;
+ $class = self::$mainEngine;
+ $nums = array_map(function($num) { return $num->value; }, $nums);
+ return new static($class::min(...$nums));
}
/**
* Return the maximum BigInteger between an arbitrary number of BigIntegers.
*
- * @param \phpseclib\Math\BigInteger ...$param
- * @access public
* @return \phpseclib\Math\BigInteger
*/
- public static function max()
+ public static function max(BigInteger ...$nums)
{
- $args = func_get_args();
- if (count($args) == 1) {
- return $args[0];
- }
- $max = $args[0];
- for ($i = 1; $i < count($args); $i++) {
- $max = $max->compare($args[$i]) < 0 ? $args[$i] : $max;
- }
- return $max;
- }
-
- /**
- * Return the size of a BigInteger in bits
- *
- * @access public
- * @return int
- */
- public function getLength()
- {
- return strlen($this->toBits());
- }
-
- /**
- * Return the size of a BigInteger in bytes
- *
- * @access public
- * @return int
- */
- public function getLengthInBytes()
- {
- return strlen($this->toBytes());
+ $class = self::$mainEngine;
+ $nums = array_map(function($num) { return $num->value; }, $nums);
+ return new static($class::max(...$nums));
}
/**
* Tests BigInteger to see if it is between two integers, inclusive
*
- * @param \phpseclib\Math\BigInteger $min
- * @param \phpseclib\Math\BigInteger $max
- * @access public
* @return boolean
*/
- function between(BigInteger $min, BigInteger $max)
+ public function between(BigInteger $min, BigInteger $max)
{
- return $this->compare($min) >= 0 && $this->compare($max) <= 0;
+ return $this->value->between($min->value, $max->value);
}
-}
+
+ /**
+ * Clone
+ */
+ public function __clone()
+ {
+ $this->value = clone $this->value;
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/BCMath.php b/phpseclib/Math/BigInteger/Engines/BCMath.php
new file mode 100644
index 00000000..fee7b212
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/BCMath.php
@@ -0,0 +1,677 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines;
+
+use ParagonIE\ConstantTime\Hex;
+use phpseclib\Exception\BadConfigurationException;
+
+/**
+ * BCMath Engine.
+ *
+ * @package BCMath
+ * @author Jim Wigginton
+ * @access public
+ */
+class BCMath extends Engine
+{
+ /**
+ * Can Bitwise operations be done fast?
+ *
+ * @see parent::bitwise_leftRotate()
+ * @see parent::bitwise_rightRotate()
+ * @access protected
+ */
+ const FAST_BITWISE = false;
+
+ /**
+ * Engine Directory
+ *
+ * @see parent::setModExpEngine
+ * @access protected
+ */
+ const ENGINE_DIR = 'BCMath';
+
+ /**
+ * Modular Exponentiation Engine
+ *
+ * @var string
+ */
+ protected static $modexpEngine;
+
+ /**
+ * Engine Validity Flag
+ *
+ * @var bool
+ */
+ protected static $isValidEngine;
+
+ /**
+ * BigInteger(0)
+ *
+ * @var \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ protected static $zero;
+
+ /**
+ * BigInteger(1)
+ *
+ * @var \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ protected static $one;
+
+ /**
+ * BigInteger(2)
+ *
+ * @var \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ protected static $two;
+
+ /**
+ * Primes > 2 and < 1000
+ *
+ * @var array
+ */
+ protected static $primes;
+
+ /**
+ * Test for engine validity
+ *
+ * @see parent::__construct()
+ * @return bool
+ */
+ public static function isValidEngine()
+ {
+ return extension_loaded('bcmath');
+ }
+
+ /**
+ * Default constructor
+ *
+ * @param $x base-10 number or base-$base number if $base set.
+ * @param int $base
+ * @see parent::__construct()
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public function __construct($x = 0, $base = 10)
+ {
+ if (!isset(self::$isValidEngine)) {
+ self::$isValidEngine = self::isValidEngine();
+ }
+ if (!self::$isValidEngine) {
+ throw new BadConfigurationException('BCMath is not setup correctly on this system');
+ }
+
+ $this->value = '0';
+
+ parent::__construct($x, $base);
+ }
+
+ /**
+ * Initialize a BCMath BigInteger Engine instance
+ *
+ * @param int $base
+ * @see parent::__construct()
+ */
+ protected function initialize($base)
+ {
+ switch (abs($base)) {
+ case 256:
+ // round $len to the nearest 4
+ $len = (strlen($this->value) + 3) & 0xFFFFFFFC;
+
+ $x = str_pad($this->value, $len, chr(0), STR_PAD_LEFT);
+
+ $this->value = '0';
+ for ($i = 0; $i < $len; $i+= 4) {
+ $this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32
+ $this->value = bcadd($this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2]) << 8) | ord($x[$i + 3])), 0);
+ }
+
+ if ($this->is_negative) {
+ $this->value = '-' . $this->value;
+ }
+ break;
+ case 16:
+ $x = (strlen($this->value) & 1) ? '0' . $this->value : $this->value;
+ $temp = new self(Hex::decode($x), 256);
+ $this->value = $this->is_negative ? '-' . $temp->value : $temp->value;
+ $this->is_negative = false;
+ break;
+ case 10:
+ // explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different
+ // results then doing it on '-1' does (modInverse does $x[0])
+ $this->value = $this->value === '-' ? '0' : (string) $this->value;
+ }
+ }
+
+ /**
+ * Converts a BigInteger to a base-10 number.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ if ($this->value === '0') {
+ return '0';
+ }
+
+ return ltrim($this->value, '0');
+ }
+
+ /**
+ * Converts a BigInteger to a byte string (eg. base-256).
+ *
+ * @param bool $twos_compliment
+ * @return string
+ */
+ function toBytes($twos_compliment = false)
+ {
+ if ($twos_compliment) {
+ return $this->toBytesHelper();
+ }
+
+ $value = '';
+ $current = $this->value;
+
+ if ($current[0] == '-') {
+ $current = substr($current, 1);
+ }
+
+ while (bccomp($current, '0', 0) > 0) {
+ $temp = bcmod($current, '16777216');
+ $value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value;
+ $current = bcdiv($current, '16777216', 0);
+ }
+
+ return $this->precision > 0 ?
+ substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
+ ltrim($value, chr(0));
+ }
+
+ /**
+ * Adds two BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public function add(BCMath $y)
+ {
+ $temp = new self();
+ $temp->value = bcadd($this->value, $y->value);
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Subtracts two BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public function subtract(BCMath $y)
+ {
+ $temp = new self();
+ $temp->value = bcsub($this->value, $y->value);
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Multiplies two BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public function multiply(BCMath $x)
+ {
+ $temp = new self();
+ $temp->value = bcmul($this->value, $x->value);
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Divides two BigIntegers.
+ *
+ * Returns an array whose first element contains the quotient and whose second element contains the
+ * "common residue". If the remainder would be positive, the "common residue" and the remainder are the
+ * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder
+ * and the divisor (basically, the "common residue" is the first positive modulo).
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public function divide(BCMath $y)
+ {
+ $quotient = new self();
+ $remainder = new self();
+
+ $quotient->value = bcdiv($this->value, $y->value, 0);
+ $remainder->value = bcmod($this->value, $y->value);
+
+ if ($remainder->value[0] == '-') {
+ $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0);
+ }
+
+ return [$this->normalize($quotient), $this->normalize($remainder)];
+ }
+
+ /**
+ * Calculates modular inverses.
+ *
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
+ *
+ * @param \phpseclib\Math\BigInteger\Engines\BCMath $n
+ */
+ public function modInverse(BCMath $n)
+ {
+ return $this->modInverseHelper($n);
+ }
+
+ /**
+ * Calculates the greatest common divisor and Bezout's identity.
+ *
+ * Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that
+ * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which
+ * combination is returned is dependent upon which mode is in use. See
+ * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public function extendedGCD(BCMath $n)
+ {
+ // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works
+ // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is,
+ // the basic extended euclidean algorithim is what we're using.
+
+ $u = $this->value;
+ $v = $n->value;
+
+ $a = '1';
+ $b = '0';
+ $c = '0';
+ $d = '1';
+
+ while (bccomp($v, '0', 0) != 0) {
+ $q = bcdiv($u, $v, 0);
+
+ $temp = $u;
+ $u = $v;
+ $v = bcsub($temp, bcmul($v, $q, 0), 0);
+
+ $temp = $a;
+ $a = $c;
+ $c = bcsub($temp, bcmul($a, $q, 0), 0);
+
+ $temp = $b;
+ $b = $d;
+ $d = bcsub($temp, bcmul($b, $q, 0), 0);
+ }
+
+ return [
+ 'gcd' => $this->normalize(new static($u)),
+ 'x' => $this->normalize(new static($a)),
+ 'y' => $this->normalize(new static($b))
+ ];
+ }
+
+ /**
+ * Calculates the greatest common divisor
+ *
+ * Say you have 693 and 609. The GCD is 21.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public function gcd(BCMath $n)
+ {
+ extract($this->extendedGCD($n));
+ return $gcd;
+ }
+
+ /**
+ * Absolute value.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public function abs()
+ {
+ $temp = new static();
+ $temp->value = bccomp($this->value, '0', 0) < 0 ?
+ substr($this->value, 1) :
+ $this->value;
+
+ return $temp;
+ }
+
+ /**
+ * Logical And
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public function bitwise_and(BCMath $x)
+ {
+ $left = $this->toBytes();
+ $right = $x->toBytes();
+
+ $length = max(strlen($left), strlen($right));
+
+ $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
+ $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
+
+ return $this->normalize(new static($left & $right, 256));
+ }
+
+ /**
+ * Logical Or
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public function bitwise_or(BCMath $x)
+ {
+ $left = $this->toBytes();
+ $right = $x->toBytes();
+
+ $length = max(strlen($left), strlen($right));
+
+ $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
+ $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
+
+ return $this->normalize(new static($left | $right, 256));
+ }
+
+ /**
+ * Logical Exlusive Or
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public function bitwise_xor(BCMath $x)
+ {
+ $left = $this->toBytes();
+ $right = $x->toBytes();
+
+ $length = max(strlen($left), strlen($right));
+
+ $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
+ $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
+
+ return $this->normalize(new static($left ^ $right, 256));
+ }
+
+ /**
+ * Logical Right Shift
+ *
+ * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
+ *
+ * @param int $shift
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public function bitwise_rightShift($shift)
+ {
+ $temp = new static();
+ $temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0);
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Logical Left Shift
+ *
+ * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
+ *
+ * @param int $shift
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public function bitwise_leftShift($shift)
+ {
+ $temp = new static();
+ $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0);
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Compares two numbers.
+ *
+ * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is
+ * demonstrated thusly:
+ *
+ * $x > $y: $x->compare($y) > 0
+ * $x < $y: $x->compare($y) < 0
+ * $x == $y: $x->compare($y) == 0
+ *
+ * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y).
+ *
+ * @param \phpseclib\Math\BigInteger $y
+ * @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
+ * @see self::equals()
+ * @internal Could return $this->subtract($x), but that's not as fast as what we do do.
+ */
+ public function compare(BCMath $y)
+ {
+ return bccomp($this->value, $y->value, 0);
+ }
+
+ /**
+ * Tests the equality of two numbers.
+ *
+ * If you need to see if one number is greater than or less than another number, use BigInteger::compare()
+ *
+ * @return bool
+ */
+ public function equals(BCMath $x)
+ {
+ return $this->value == $x->value;
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public function modPow(BCMath $e, BCMath $n)
+ {
+ return $this->powModOuter($e, $n);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * Alias for modPow().
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public function powMod(BCMath $e, BCMath $n)
+ {
+ return $this->powModOuter($e, $n);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ protected function powModInner(BCMath $e, BCMath $n)
+ {
+ try {
+ $class = self::$modexpEngine;
+ return $class::powModHelper($this, $e, $n, static::class);
+ } catch (\Exception $err) {
+ return BCMath\DefaultEngine::powModHelper($this, $e, $n, static::class);
+ }
+ }
+
+ /**
+ * Normalize
+ *
+ * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ protected function normalize(BCMath $result)
+ {
+ $result->precision = $this->precision;
+ $result->bitmask = $this->bitmask;
+
+ if ($result->bitmask !== false) {
+ $result->value = bcmod($result->value, $result->bitmask->value);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Generate a random prime number between a range
+ *
+ * If there's not a prime within the given range, false will be returned.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath|false
+ */
+ public static function randomRangePrime(BCMath $min, BCMath $max)
+ {
+ return self::randomRangePrimeOuter($min, $max);
+ }
+
+ /**
+ * Generate a random number between a range
+ *
+ * Returns a random number between $min and $max where $min and $max
+ * can be defined using one of the two methods:
+ *
+ * BigInteger::randomRange($min, $max)
+ * BigInteger::randomRange($max, $min)
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\Engine\BCMath
+ */
+ public static function randomRange(BCMath $min, BCMath $max)
+ {
+ return self::randomRangeHelper($min, $max);
+ }
+
+ /**
+ * Make the current number odd
+ *
+ * If the current number is odd it'll be unchanged. If it's even, one will be added to it.
+ *
+ * @see self::randomPrime()
+ */
+ protected function make_odd()
+ {
+ if ($this->value[strlen($this->value) - 1] % 2 == 0) {
+ $this->value = bcadd($this->value, '1');
+ }
+ }
+
+ /**
+ * Test the number against small primes.
+ *
+ * @see self::isPrime()
+ */
+ protected function testSmallPrimes()
+ {
+ if ($this->value === '1') {
+ return false;
+ }
+ if ($this->value === '2') {
+ return true;
+ }
+ if ($this->value[strlen($this->value) - 1] % 2 == 0) {
+ return false;
+ }
+
+ $value = $this->value;
+
+ foreach (self::$primes as $prime) {
+ $r = bcmod($this->value, $prime);
+ if ($r == '0') {
+ return $this->value == $prime;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Scan for 1 and right shift by that amount
+ *
+ * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
+ *
+ * @see self::isPrime()
+ * @return int
+ */
+ protected static function scan1divide($r)
+ {
+ $r_value = &$r->value;
+ $s = 0;
+ // if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals(static::$one) check earlier
+ while ($r_value[strlen($r_value) - 1] % 2 == 0) {
+ $r_value = bcdiv($r_value, '2', 0);
+ ++$s;
+ }
+
+ return $s;
+ }
+
+ /**
+ * Performs exponentiation.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public function pow(BCMath $n)
+ {
+ $temp = new self();
+ $temp->value = bcpow($this->value, $n->value);
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Return the minimum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public static function min(BCMath ...$nums)
+ {
+ return self::minHelper($nums);
+ }
+
+ /**
+ * Return the maximum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ public static function max(BCMath ...$nums)
+ {
+ return self::maxHelper($nums);
+ }
+
+ /**
+ * Tests BigInteger to see if it is between two integers, inclusive
+ *
+ * @return boolean
+ */
+ public function between(BCMath $min, BCMath $max)
+ {
+ return $this->compare($min) >= 0 && $this->compare($max) <= 0;
+ }
+
+ /**
+ * Set Bitmask
+ *
+ * @param int $bits
+ * @see self::setPrecision()
+ */
+ protected static function setBitmask($bits)
+ {
+ $temp = parent::setBitmask($bits);
+ return $temp->add(static::$one);
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/BCMath/Base.php b/phpseclib/Math/BigInteger/Engines/BCMath/Base.php
new file mode 100644
index 00000000..026cd86f
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/BCMath/Base.php
@@ -0,0 +1,121 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines\BCMath;
+
+use phpseclib\Math\BigInteger\Engines\BCMath;
+
+/**
+ * Sliding Window Exponentiation Engine
+ *
+ * @package PHP
+ * @author Jim Wigginton
+ * @access public
+ */
+abstract class Base extends BCMath
+{
+ /**#@+
+ * @access private
+ */
+ /**
+ * Cache constants
+ *
+ * $cache[self::VARIABLE] tells us whether or not the cached data is still valid.
+ */
+ const VARIABLE = 0;
+ /**
+ * $cache[self::DATA] contains the cached data.
+ */
+ const DATA = 1;
+ /**#@-*/
+
+ /**
+ * Test for engine validity
+ *
+ * @return bool
+ */
+ public static function isValidEngine()
+ {
+ return static::class != __CLASS__;
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @param \phpseclib\Math\BigInteger\Engines\BCMath $x
+ * @param \phpseclib\Math\BigInteger\Engines\BCMath $e
+ * @param \phpseclib\Math\BigInteger\Engines\BCMath $n
+ * @param string $class
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ protected static function powModHelper(BCMath $x, BCMath $e, BCMath $n, $class)
+ {
+ if (empty($e->value)) {
+ $temp = new $class();
+ $temp->value = '1';
+ return $x->normalize($temp);
+ }
+
+ if ($e->value == '1') {
+ $temp = bcdiv($x, $n);
+ return $x->normalize($temp);
+ }
+
+ return $x->normalize(static::slidingWindow($x, $e, $n, $class));
+ }
+
+ /**
+ * Modular reduction preperation
+ *
+ * @param string $x
+ * @param string $n
+ * @param string $class
+ * @see self::slidingWindow()
+ * @return string
+ */
+ protected static function prepareReduce($x, $n, $class)
+ {
+ return static::reduce($x, $n);
+ }
+
+ /**
+ * Modular multiply
+ *
+ * @param string $x
+ * @param string $y
+ * @param string $n
+ * @param string $class
+ * @see self::slidingWindow()
+ * @return string
+ */
+ protected static function multiplyReduce($x, $y, $n, $class)
+ {
+ return static::reduce(bcmul($x, $y), $n);
+ }
+
+ /**
+ * Modular square
+ *
+ * @param string $x
+ * @param string $n
+ * @param string $class
+ * @see self::slidingWindow()
+ * @return string
+ */
+ protected static function squareReduce($x, $n, $class)
+ {
+ return static::reduce(bcmul($x, $x), $n);
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/BCMath/BuiltIn.php b/phpseclib/Math/BigInteger/Engines/BCMath/BuiltIn.php
new file mode 100644
index 00000000..8cf0de23
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/BCMath/BuiltIn.php
@@ -0,0 +1,41 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines\BCMath;
+
+use phpseclib\Math\BigInteger\Engines\BCMath;
+
+/**
+ * Built-In BCMath Modular Exponentiation Engine
+ *
+ * @package BCMath
+ * @author Jim Wigginton
+ * @access public
+ */
+abstract class BuiltIn extends BCMath
+{
+ /**
+ * Performs modular exponentiation.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\BCMath
+ */
+ protected static function powModHelper(BCMath $x, BCMath $e, BCMath $n)
+ {
+ $temp = new BCMath();
+ $temp->value = bcpowmod($x->value, $e->value, $n->value);
+
+ return $x->normalize($temp);
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/BCMath/DefaultEngine.php b/phpseclib/Math/BigInteger/Engines/BCMath/DefaultEngine.php
new file mode 100644
index 00000000..6a3b1331
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/BCMath/DefaultEngine.php
@@ -0,0 +1,29 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines\BCMath;
+
+use phpseclib\Math\BigInteger\Engines\BCMath\Reductions\Barrett;
+
+/**
+ * PHP Default Modular Exponentiation Engine
+ *
+ * @package PHP
+ * @author Jim Wigginton
+ * @access public
+ */
+abstract class DefaultEngine extends Barrett
+{
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/BCMath/OpenSSL.php b/phpseclib/Math/BigInteger/Engines/BCMath/OpenSSL.php
new file mode 100644
index 00000000..1187240f
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/BCMath/OpenSSL.php
@@ -0,0 +1,29 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines\BCMath;
+
+use phpseclib\Math\BigInteger\Engines\OpenSSL as Progenitor;
+
+/**
+ * OpenSSL Modular Exponentiation Engine
+ *
+ * @package BCMath
+ * @author Jim Wigginton
+ * @access public
+ */
+abstract class OpenSSL extends Progenitor
+{
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/Barrett.php b/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/Barrett.php
new file mode 100644
index 00000000..7237c5ac
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/Barrett.php
@@ -0,0 +1,193 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines\BCMath\Reductions;
+
+use phpseclib\Math\BigInteger\Engines\BCMath\Base;
+
+/**
+ * PHP Barrett Modular Exponentiation Engine
+ *
+ * @package PHP
+ * @author Jim Wigginton
+ * @access public
+ */
+abstract class Barrett extends Base
+{
+ /**#@+
+ * @access private
+ */
+ /**
+ * Cache constants
+ *
+ * $cache[self::VARIABLE] tells us whether or not the cached data is still valid.
+ */
+ const VARIABLE = 0;
+ /**
+ * $cache[self::DATA] contains the cached data.
+ */
+ const DATA = 1;
+ /**#@-*/
+
+ /**
+ * 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 string $n
+ * @param string $m
+ * @return array
+ */
+ protected static function reduce($n, $m)
+ {
+ static $cache = [
+ self::VARIABLE => [],
+ self::DATA => []
+ ];
+
+ $m_length = strlen($m);
+
+ if (strlen($n) > 2 * $m_length) {
+ return bcmod($n, $m);
+ }
+
+ // 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);
+ }
+ // n = 2 * m.length
+
+ if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
+ $key = count($cache[self::VARIABLE]);
+ $cache[self::VARIABLE][] = $m;
+
+ $lhs = '1' . str_repeat('0', $m_length + ($m_length >> 1));
+ $u = bcdiv($lhs, $m, 0);
+ $m1 = bcsub($lhs, bcmul($u, $m));
+
+ $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 = substr($n, -$cutoff);
+ $msd = substr($n, 0, -$cutoff);
+
+ $temp = bcmul($msd, $m1); // m.length + (m.length >> 1)
+ $n = bcadd($lsd, $temp); // m.length + (m.length >> 1) + 1 (so basically we're adding two same length numbers)
+ //if ($m_length & 1) {
+ // return self::regularBarrett($n, $m);
+ //}
+
+ // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2
+ $temp = substr($n, 0, -$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 = bcmul($temp, $u);
+ // 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 = substr($temp, 0, -($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 = bcmul($temp, $m);
+
+ // 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 = bcsub($n, $temp);
+
+ //if (bccomp($result, '0') < 0) {
+ if ($result[0] == '-') {
+ $temp = '1' . str_repeat('0', $m_length + 1);
+ $result = bcadd($result, $temp);
+ }
+
+ while (bccomp($result, $m) >= 0) {
+ $result = bcsub($result, $m);
+ }
+
+ return $result;
+ }
+
+ /**
+ * (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.
+ *
+ * @param string $x
+ * @param string $n
+ * @return string
+ */
+ private static function regularBarrett($x, $n)
+ {
+ static $cache = [
+ self::VARIABLE => [],
+ self::DATA => []
+ ];
+
+ $n_length = strlen($n);
+
+ if (strlen($x) > 2 * $n_length) {
+ return bcmod($x, $n);
+ }
+
+ if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
+ $key = count($cache[self::VARIABLE]);
+ $cache[self::VARIABLE][] = $n;
+ $lhs = '1' . str_repeat('0', 2 * $n_length);
+ $cache[self::DATA][] = bcdiv($lhs, $n, 0);
+ }
+
+ $temp = substr($x, 0, -$n_length + 1);
+ $temp = bcmul($temp, $cache[self::DATA][$key]);
+ $temp = substr($temp, 0, -$n_length - 1);
+
+ $r1 = substr($x, -$n_length - 1);
+ $r2 = substr(bcmul($temp, $n), -$n_length - 1);
+ $result = bcsub($r1, $r2);
+
+ //if (bccomp($result, '0') < 0) {
+ if ($result[0] == '-') {
+ $q = '1' . str_repeat('0', $n_length + 1);
+ $result = bcadd($result, $q);
+ }
+
+ while (bccomp($result, $n) >= 0) {
+ $result = bcsub($result, $n);
+ }
+
+ return $result;
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php b/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php
new file mode 100644
index 00000000..2ab676e6
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/BCMath/Reductions/EvalBarrett.php
@@ -0,0 +1,110 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines\BCMath\Reductions;
+
+use phpseclib\Math\BigInteger\Engines\BCMath\Base;
+use phpseclib\Math\BigInteger\Engines\BCMath;
+
+/**
+ * PHP Barrett Modular Exponentiation Engine
+ *
+ * @package PHP
+ * @author Jim Wigginton
+ * @access public
+ */
+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.
+ *
+ * @param string $n
+ * @param string $m
+ * @return string
+ */
+ protected static function reduce($n, $m)
+ {
+ $inline = self::$custom_reduction;
+ return $inline($n);
+ }
+
+ /**
+ * Generate Custom Reduction
+ *
+ * @param array $m
+ * @param string $class
+ * @return callable
+ */
+ protected static function generateCustomReduction(BCMath $m, $class)
+ {
+ $m_length = strlen($m);
+
+ if ($m_length < 5) {
+ $code = 'return bcmod($x, $n);';
+ eval('$func = function ($n) { ' . $code . '};');
+ self::$custom_reduction = $func;
+ return;
+ }
+
+ $lhs = '1' . str_repeat('0', $m_length + ($m_length >> 1));
+ $u = bcdiv($lhs, $m, 0);
+ $m1 = bcsub($lhs, bcmul($u, $m));
+
+ $cutoff = $m_length + ($m_length >> 1);
+
+ $m = "'$m'";
+ $u = "'$u'";
+ $m1= "'$m1'";
+
+ $code.= '
+ $lsd = substr($n, -' . $cutoff . ');
+ $msd = substr($n, 0, -' . $cutoff . ');
+
+ $temp = bcmul($msd, ' . $m1 . ');
+ $n = bcadd($lsd, $temp);
+
+ $temp = substr($n, 0, ' . (-$m_length + 1) . ');
+ $temp = bcmul($temp, ' . $u . ');
+ $temp = substr($temp, 0, ' . (-($m_length >> 1) - 1) . ');
+ $temp = bcmul($temp, ' . $m . ');
+
+ $result = bcsub($n, $temp);
+
+ if ($result[0] == \'-\') {
+ $temp = \'1' . str_repeat('0', $m_length + 1) . '\';
+ $result = bcadd($result, $temp);
+ }
+
+ while (bccomp($result, ' . $m . ') >= 0) {
+ $result = bcsub($result, ' . $m . ');
+ }
+
+ return $result;';
+
+ eval('$func = function ($n) { ' . $code . '};');
+
+ self::$custom_reduction = $func;
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/Engine.php b/phpseclib/Math/BigInteger/Engines/Engine.php
new file mode 100644
index 00000000..89787416
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/Engine.php
@@ -0,0 +1,1039 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines;
+
+use ParagonIE\ConstantTime\Hex;
+use phpseclib\Exception\BadConfigurationException;
+use phpseclib\Crypt\Random;
+
+/**
+ * Base Engine.
+ *
+ * @package Engine
+ * @author Jim Wigginton
+ * @access public
+ */
+abstract class Engine implements \Serializable
+{
+ /**
+ * Holds the BigInteger's value
+ *
+ * @var mixed
+ */
+ protected $value;
+
+ /**
+ * Holds the BigInteger's sign
+ *
+ * @var bool
+ */
+ protected $is_negative;
+
+ /**
+ * Precision
+ *
+ * @see static::setPrecision()
+ */
+ protected $precision = -1;
+
+ /**
+ * Precision Bitmask
+ *
+ * @see static::setPrecision()
+ */
+ protected $bitmask = false;
+
+ /**
+ * Default constructor
+ *
+ * @param $x base-10 number or base-$base number if $base set.
+ * @param int $base
+ * @return \phpseclib\Math\BigInteger\Engines\Engine
+ */
+ public function __construct($x, $base)
+ {
+ if (!isset(static::$primes)) {
+ static::$primes = [
+ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,
+ 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137,
+ 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
+ 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
+ 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
+ 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
+ 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617,
+ 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727,
+ 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829,
+ 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947,
+ 953, 967, 971, 977, 983, 991, 997
+ ];
+ static::$zero = new static(0);
+ static::$one = new static(1);
+ static::$two = new static(2);
+ }
+
+ // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48
+ // '0' is the only value like this per http://php.net/empty
+ if (empty($x) && (abs($base) != 256 || $x !== '0')) {
+ return;
+ }
+
+ switch ($base) {
+ case -256:
+ case 256:
+ if ($base == -256 && (ord($x[0]) & 0x80)) {
+ $this->value = ~$x;
+ $this->is_negative = true;
+ } else {
+ $this->value = $x;
+ $this->is_negative = false;
+ }
+
+ static::initialize($base);
+
+ if ($this->is_negative) {
+ $temp = $this->add(new static('-1'));
+ $this->value = $temp->value;
+ }
+ break;
+ case -16:
+ case 16:
+ if ($base > 0 && $x[0] == '-') {
+ $this->is_negative = true;
+ $x = substr($x, 1);
+ }
+
+ $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#', '$1', $x);
+
+ $is_negative = false;
+ if ($base < 0 && hexdec($x[0]) >= 8) {
+ $this->is_negative = $is_negative = true;
+ $x = Hex::encode(~Hex::decode($x));
+ }
+
+ $this->value = $x;
+ static::initialize($base);
+
+ if ($is_negative) {
+ $temp = $this->add(new static('-1'));
+ $this->value = $temp->value;
+ }
+ break;
+ case -10:
+ case 10:
+ // (?value = preg_replace('#(? 0 && $x[0] == '-') {
+ $this->is_negative = true;
+ $x = substr($x, 1);
+ }
+
+ $x = preg_replace('#^([01]*).*#', '$1', $x);
+ $x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT);
+
+ $str = '0x';
+ while (strlen($x)) {
+ $part = substr($x, 0, 4);
+ $str.= dechex(bindec($part));
+ $x = substr($x, 4);
+ }
+
+ if ($this->is_negative) {
+ $str = '-' . $str;
+ }
+
+ $temp = new static($str, 8 * $base); // ie. either -16 or +16
+ $this->value = $temp->value;
+ $this->is_negative = $temp->is_negative;
+
+ break;
+ default:
+ // base not supported, so we'll let $this == 0
+ }
+ }
+
+ /**
+ * Sets engine type.
+ *
+ * Throws an exception if the type is invalid
+ *
+ * @param string $engine
+ */
+ public static function setModExpEngine($engine)
+ {
+ $fqengine = '\\phpseclib\\Math\\BigInteger\\Engines\\' . static::ENGINE_DIR . '\\' . $engine;
+ if (!class_exists($fqengine) || !method_exists($fqengine, 'isValidEngine')) {
+ throw new \InvalidArgumentException("$engine is not a valid engine");
+ }
+ if (!$fqengine::isValidEngine()) {
+ throw new BadConfigurationException("$engine is not setup correctly on this system");
+ }
+ static::$modexpEngine = $fqengine;
+ }
+
+ /**
+ * Converts a BigInteger to a byte string (eg. base-256).
+ *
+ * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
+ * saved as two's compliment.
+ *
+ * @param bool $twos_compliment
+ * @return string
+ */
+ protected function toBytesHelper()
+ {
+ $comparison = $this->compare(new static());
+ if ($comparison == 0) {
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
+ }
+
+ $temp = $comparison < 0 ? $this->add(new static(1)) : $this;
+ $bytes = $temp->toBytes();
+
+ if (empty($bytes)) { // eg. if the number we're trying to convert is -1
+ $bytes = chr(0);
+ }
+
+ if (ord($bytes[0]) & 0x80) {
+ $bytes = chr(0) . $bytes;
+ }
+
+ return $comparison < 0 ? ~$bytes : $bytes;
+ }
+
+ /**
+ * Converts a BigInteger to a hex string (eg. base-16).
+ *
+ * @param bool $twos_compliment
+ * @return string
+ */
+ public function toHex($twos_compliment = false)
+ {
+ return Hex::encode($this->toBytes($twos_compliment));
+ }
+
+ /**
+ * Converts a BigInteger to a bit string (eg. base-2).
+ *
+ * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
+ * saved as two's compliment.
+ *
+ * @param bool $twos_compliment
+ * @return string
+ */
+ public function toBits($twos_compliment = false)
+ {
+ $hex = $this->toHex($twos_compliment);
+ $bits = '';
+ for ($i = strlen($hex) - 8, $start = strlen($hex) & 7; $i >= $start; $i-=8) {
+ $bits = str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT) . $bits;
+ }
+ if ($start) { // hexdec('') == 0
+ $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8, '0', STR_PAD_LEFT) . $bits;
+ }
+ $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');
+
+ if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) {
+ return '0' . $result;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Calculates modular inverses.
+ *
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
+ *
+ * @param \phpseclib\Math\BigInteger\Engines\Engine $n
+ * @return \phpseclib\Math\BigInteger\Engines\Engine|false
+ * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information.
+ */
+ protected function modInverseHelper(Engine $n)
+ {
+ // $x mod -$n == $x mod $n.
+ $n = $n->abs();
+
+ if ($this->compare(static::$zero) < 0) {
+ $temp = $this->abs();
+ $temp = $temp->modInverse($n);
+ return $this->normalize($n->subtract($temp));
+ }
+
+ extract($this->extendedGCD($n));
+
+ if (!$gcd->equals(static::$one)) {
+ return false;
+ }
+
+ $x = $x->compare(static::$zero) < 0 ? $x->add($n) : $x;
+
+ return $this->compare(static::$zero) < 0 ? $this->normalize($n->subtract($x)) : $this->normalize($x);
+ }
+
+ /**
+ * Serialize
+ *
+ * Will be called, automatically, when serialize() is called on a BigInteger object.
+ *
+ * @return string
+ */
+ public function serialize()
+ {
+ $val = ['hex' => $this->toHex(true)];
+ if ($this->precision > 0) {
+ $val['precision'] = $this->precision;
+ }
+ return serialize($val);
+ }
+
+ /**
+ * Serialize
+ *
+ * Will be called, automatically, when unserialize() is called on a BigInteger object.
+ *
+ * @param string $serialized
+ */
+ public function unserialize($serialized)
+ {
+ $r = unserialize($serialized);
+ $temp = new static($r['hex'], -16);
+ $this->value = $temp->value;
+ $this->is_negative = $temp->is_negative;
+ if (isset($r['precision'])) {
+ // recalculate $this->bitmask
+ $this->setPrecision($r['precision']);
+ }
+ }
+
+ /**
+ * Converts a BigInteger to a base-10 number.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toString();
+ }
+
+ /**
+ * __debugInfo() magic method
+ *
+ * Will be called, automatically, when print_r() or var_dump() are called
+ */
+ public function __debugInfo()
+ {
+ return [
+ 'value' => '0x' . $this->toHex(true),
+ 'engine' => basename(static::class)
+ ];
+ }
+
+ /**
+ * Set Precision
+ *
+ * Some bitwise operations give different results depending on the precision being used. Examples include left
+ * shift, not, and rotates.
+ *
+ * @param int $bits
+ */
+ public function setPrecision($bits)
+ {
+ if ($bits < 1) {
+ $this->precision = -1;
+ $this->bitmask = false;
+
+ return;
+ }
+ $this->precision = $bits;
+ $this->bitmask = static::setBitmask($bits);
+
+ $temp = $this->normalize($this);
+ $this->value = $temp->value;
+ }
+
+ /**
+ * Get Precision
+ *
+ * Returns the precision if it exists, -1 if it doesn't
+ *
+ * @return int
+ */
+ public function getPrecision()
+ {
+ return $this->precision;
+ }
+
+ /**
+ * Set Bitmask
+ *
+ * @param int $bits
+ * @see self::setPrecision()
+ */
+ protected static function setBitmask($bits)
+ {
+ return new static(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256);
+ }
+
+ /**
+ * Logical Not
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\Engine
+ */
+ public function bitwise_not()
+ {
+ // calculuate "not" without regard to $this->precision
+ // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0)
+ $temp = $this->toBytes();
+ if ($temp == '') {
+ return '';
+ }
+ $pre_msb = decbin(ord($temp[0]));
+ $temp = ~$temp;
+ $msb = decbin(ord($temp[0]));
+ if (strlen($msb) == 8) {
+ $msb = substr($msb, strpos($msb, '0'));
+ }
+ $temp[0] = chr(bindec($msb));
+
+ // see if we need to add extra leading 1's
+ $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8;
+ $new_bits = $this->precision - $current_bits;
+ if ($new_bits <= 0) {
+ return $this->normalize(new static($temp, 256));
+ }
+
+ // generate as many leading 1's as we need to.
+ $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3);
+
+ self::base256_lshift($leading_ones, $current_bits);
+
+ $temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT);
+
+ return $this->normalize(new static($leading_ones | $temp, 256));
+ }
+
+ /**
+ * Logical Left Shift
+ *
+ * Shifts binary strings $shift bits, essentially multiplying by 2**$shift.
+ *
+ * @param $x String
+ * @param $shift Integer
+ * @return string
+ */
+ protected static function base256_lshift(&$x, $shift)
+ {
+ if ($shift == 0) {
+ return;
+ }
+
+ $num_bytes = $shift >> 3; // eg. floor($shift/8)
+ $shift &= 7; // eg. $shift % 8
+
+ $carry = 0;
+ for ($i = strlen($x) - 1; $i >= 0; --$i) {
+ $temp = ord($x[$i]) << $shift | $carry;
+ $x[$i] = chr($temp);
+ $carry = $temp >> 8;
+ }
+ $carry = ($carry != 0) ? chr($carry) : '';
+ $x = $carry . $x . str_repeat(chr(0), $num_bytes);
+ }
+
+ /**
+ * Logical Left Rotate
+ *
+ * Instead of the top x bits being dropped they're appended to the shifted bit string.
+ *
+ * @param int $shift
+ * @return \phpseclib\Math\BigInteger\Engine\Engines
+ */
+ public function bitwise_leftRotate($shift)
+ {
+ $bits = $this->toBytes();
+
+ if ($this->precision > 0) {
+ $precision = $this->precision;
+ if (static::FAST_BITWISE) {
+ $mask = $this->bitmask->toBytes();
+ } else {
+ $mask = $this->bitmask->subtract(new static(1));
+ $mask = $mask->toBytes();
+ }
+ } else {
+ $temp = ord($bits[0]);
+ for ($i = 0; $temp >> $i; ++$i) {
+ }
+ $precision = 8 * strlen($bits) - 8 + $i;
+ $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3);
+ }
+
+ if ($shift < 0) {
+ $shift+= $precision;
+ }
+ $shift%= $precision;
+
+ if (!$shift) {
+ return clone $this;
+ }
+
+ $left = $this->bitwise_leftShift($shift);
+ $left = $left->bitwise_and(new static($mask, 256));
+ $right = $this->bitwise_rightShift($precision - $shift);
+ $result = static::FAST_BITWISE ? $left->bitwise_or($right) : $left->add($right);
+ return $this->normalize($result);
+ }
+
+ /**
+ * Logical Right Rotate
+ *
+ * Instead of the bottom x bits being dropped they're prepended to the shifted bit string.
+ *
+ * @param int $shift
+ * @return \phpseclib\Math\BigInteger\Engines\Engine
+ */
+ public function bitwise_rightRotate($shift)
+ {
+ return $this->bitwise_leftRotate(-$shift);
+ }
+
+ /**
+ * Returns the smallest and largest n-bit number
+ *
+ * @param int $bits
+ * @return \phpseclib\Math\BigInteger\Engines\Engine
+ */
+ public static function minMaxBits($bits)
+ {
+ $bytes = $bits >> 3;
+ $min = str_repeat(chr(0), $bytes);
+ $max = str_repeat(chr(0xFF), $bytes);
+ $msb = $bits & 7;
+ if ($msb) {
+ $min = chr(1 << ($msb - 1)) . $min;
+ $max = chr((1 << $msb) - 1) . $max;
+ } else {
+ $min[0] = chr(0x80);
+ }
+ return [
+ 'min' => new static($min, 256),
+ 'max' => new static($max, 256)
+ ];
+ }
+
+ /**
+ * Return the size of a BigInteger in bits
+ *
+ * @return int
+ */
+ public function getLength()
+ {
+ return strlen($this->toBits());
+ }
+
+ /**
+ * Return the size of a BigInteger in bytes
+ *
+ * @return int
+ */
+ public function getLengthInBytes()
+ {
+ return strlen($this->toBytes());
+ }
+
+ /**
+ * Performs some pre-processing for powMod
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\Engine
+ */
+ protected function powModOuter(Engine $e, Engine $n)
+ {
+ $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs();
+
+ if ($e->compare(new static()) < 0) {
+ $e = $e->abs();
+
+ $temp = $this->modInverse($n);
+ if ($temp === false) {
+ return false;
+ }
+
+ return $this->normalize($temp->powModInner($e, $n));
+ }
+
+ return $this->powModInner($e, $n);
+ }
+
+ /**
+ * Sliding Window k-ary Modular Exponentiation
+ *
+ * Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} /
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}. In a departure from those algorithims,
+ * however, this function performs a modular reduction after every multiplication and squaring operation.
+ * As such, this function has the same preconditions that the reductions being used do.
+ *
+ * @param \phpseclib\Math\BigInteger\Engine $x
+ * @param \phpseclib\Math\BigInteger\Engine $e
+ * @param \phpseclib\Math\BigInteger\Engine $n
+ * @param string $class
+ * @return \phpseclib\Math\BigInteger\Engine
+ */
+ protected static function slidingWindow(Engine $x, Engine $e, Engine $n, $class)
+ {
+ static $window_ranges = [7, 25, 81, 241, 673, 1793]; // from BigInteger.java's oddModPow function
+ //static $window_ranges = [0, 7, 36, 140, 450, 1303, 3529]; // from MPM 7.3.1
+
+ $e_bits = $e->toBits();
+ $e_length = strlen($e_bits);
+
+ // calculate the appropriate window size.
+ // $window_size == 3 if $window_ranges is between 25 and 81, for example.
+ for ($i = 0, $window_size = 1; $i < count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) {
+ }
+
+ $n_value = $n->value;
+
+ if (method_exists(static::class, 'generateCustomReduction')) {
+ static::generateCustomReduction($n, $class);
+ }
+
+ // precompute $this^0 through $this^$window_size
+ $powers = [];
+ $powers[1] = static::prepareReduce($x->value, $n_value, $class);
+ $powers[2] = static::squareReduce($powers[1], $n_value, $class);
+
+ // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end
+ // in a 1. ie. it's supposed to be odd.
+ $temp = 1 << ($window_size - 1);
+ for ($i = 1; $i < $temp; ++$i) {
+ $i2 = $i << 1;
+ $powers[$i2 + 1] = static::multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $class);
+ }
+
+ $result = new $class(1);
+ $result = static::prepareReduce($result->value, $n_value, $class);
+
+ for ($i = 0; $i < $e_length;) {
+ if (!$e_bits[$i]) {
+ $result = static::squareReduce($result, $n_value, $class);
+ ++$i;
+ } else {
+ for ($j = $window_size - 1; $j > 0; --$j) {
+ if (!empty($e_bits[$i + $j])) {
+ break;
+ }
+ }
+
+ // eg. the length of substr($e_bits, $i, $j + 1)
+ for ($k = 0; $k <= $j; ++$k) {
+ $result = static::squareReduce($result, $n_value, $class);
+ }
+
+ $result = static::multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $class);
+
+ $i += $j + 1;
+ }
+ }
+
+ $temp = new $class();
+ $temp->value = static::reduce($result, $n_value, $class);
+
+ return $temp;
+ }
+
+ /**
+ * Generates a random number of a certain size
+ *
+ * Bit length is equal to $size
+ *
+ * @param int $size
+ * @return \phpseclib\Math\BigInteger\Engines\Engine
+ */
+ public static function random($size)
+ {
+ extract(static::minMaxBits($size));
+ return static::randomRange($min, $max);
+ }
+
+ /**
+ * Generates a random prime number of a certain size
+ *
+ * Bit length is equal to $size
+ *
+ * @param int $size
+ * @return \phpseclib\Math\BigInteger\Engines\Engine
+ */
+ public static function randomPrime($size)
+ {
+ extract(static::minMaxBits($size));
+ return static::randomRangePrime($min, $max);
+ }
+
+ /**
+ * Performs some pre-processing for randomRangePrime
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\Engine
+ */
+ protected static function randomRangePrimeOuter(Engine $min, Engine $max)
+ {
+ $compare = $max->compare($min);
+
+ if (!$compare) {
+ return $min->isPrime() ? $min : false;
+ } elseif ($compare < 0) {
+ // if $min is bigger then $max, swap $min and $max
+ $temp = $max;
+ $max = $min;
+ $min = $temp;
+ }
+
+ $x = static::randomRange($min, $max);
+
+ return static::randomRangePrimeInner($x, $min, $max);
+ }
+
+ /**
+ * Generate a random number between a range
+ *
+ * Returns a random number between $min and $max where $min and $max
+ * can be defined using one of the two methods:
+ *
+ * BigInteger::randomRange($min, $max)
+ * BigInteger::randomRange($max, $min)
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\Engine
+ */
+ protected static function randomRangeHelper(Engine $min, Engine $max)
+ {
+ $compare = $max->compare($min);
+
+ if (!$compare) {
+ return $min;
+ } elseif ($compare < 0) {
+ // if $min is bigger then $max, swap $min and $max
+ $temp = $max;
+ $max = $min;
+ $min = $temp;
+ }
+
+ if (!isset(static::$one)) {
+ static::$one = new static(1);
+ }
+
+ $max = $max->subtract($min->subtract(static::$one));
+
+ $size = strlen(ltrim($max->toBytes(), chr(0)));
+
+ /*
+ doing $random % $max doesn't work because some numbers will be more likely to occur than others.
+ 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.
+
+ creating a whole new random number until you find one that is within the range doesn't work
+ because, for sufficiently small ranges, the likelihood that you'd get a number within that range
+ would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability
+ would be pretty high that $random would be greater than $max.
+
+ 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 static(chr(1) . str_repeat("\0", $size), 256);
+ $random = new static(Random::string($size), 256);
+
+ 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(new static(Random::string(1), 256));
+ $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 $random->add($min);
+ }
+
+ /**
+ * Performs some post-processing for randomRangePrime
+ *
+ * @return \phpseclib\Math\BigInteger\Engine
+ */
+ protected static function randomRangePrimeInner(Engine $x, Engine $min, Engine $max)
+ {
+ if (!isset(static::$two)) {
+ static::$two = new static('2');
+ }
+
+ $x->make_odd();
+ if ($x->compare($max) > 0) {
+ // if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range
+ if ($min->equals($max)) {
+ return false;
+ }
+ $x = clone $min;
+ $x->make_odd();
+ }
+
+ $initial_x = clone $x;
+
+ while (true) {
+ if ($x->isPrime()) {
+ return $x;
+ }
+
+ $x = $x->add(static::$two);
+
+ if ($x->compare($max) > 0) {
+ $x = clone $min;
+ if ($x->equals(static::$two)) {
+ return $x;
+ }
+ $x->make_odd();
+ }
+
+ if ($x->equals($initial_x)) {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Sets the $t parameter for primality testing
+ *
+ * @return int
+ */
+ protected function setupIsPrime()
+ {
+ $length = $this->getLengthInBytes();
+
+ // see HAC 4.49 "Note (controlling the error probability)"
+ // @codingStandardsIgnoreStart
+ if ($length >= 163) { $t = 2; } // floor(1300 / 8)
+ else if ($length >= 106) { $t = 3; } // floor( 850 / 8)
+ else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8)
+ else if ($length >= 68 ) { $t = 5; } // floor( 550 / 8)
+ else if ($length >= 56 ) { $t = 6; } // floor( 450 / 8)
+ else if ($length >= 50 ) { $t = 7; } // floor( 400 / 8)
+ else if ($length >= 43 ) { $t = 8; } // floor( 350 / 8)
+ else if ($length >= 37 ) { $t = 9; } // floor( 300 / 8)
+ else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8)
+ else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8)
+ else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8)
+ else { $t = 27; }
+ // @codingStandardsIgnoreEnd
+
+ return $t;
+ }
+
+ /**
+ * Tests Primality
+ *
+ * Uses the {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}.
+ * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24} for more info.
+ *
+ * @param int $t
+ * @return bool
+ */
+ protected function testPrimality($t)
+ {
+ if (!$this->testSmallPrimes()) {
+ return false;
+ }
+
+ $n = clone $this;
+ $n_1 = $n->subtract(static::$one);
+ $n_2 = $n->subtract(static::$two);
+
+ $r = clone $n_1;
+ $s = static::scan1divide($r);
+
+ for ($i = 0; $i < $t; ++$i) {
+ $a = static::randomRange(static::$two, $n_2);
+ $y = $a->modPow($r, $n);
+
+ if (!$y->equals(static::$one) && !$y->equals($n_1)) {
+ for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) {
+ $y = $y->modPow(static::$two, $n);
+ if ($y->equals(static::$one)) {
+ return false;
+ }
+ }
+
+ if (!$y->equals($n_1)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks a numer to see if it's prime
+ *
+ * Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the
+ * $t parameter is distributability. BigInteger::randomPrime() can be distributed across multiple pageloads
+ * on a website instead of just one.
+ *
+ * @param int $t
+ * @return bool
+ */
+ public function isPrime($t = false)
+ {
+ if (!$t) {
+ $t = $this->setupIsPrime();
+ }
+ return $this->testPrimality($t);
+ }
+
+ /**
+ * Performs a few preliminary checks on root
+ *
+ * @param int $n
+ * @return \phpseclib\Math\BigInteger\Engines\Engine
+ */
+ protected function rootHelper($n)
+ {
+ if ($n < 1) {
+ return clone static::$zero;
+ } // we want positive exponents
+ if ($this->compare(static::$one) < 0) {
+ return clone static::$zero;
+ } // we want positive numbers
+ if ($this->compare(static::$two) < 0) {
+ return clone static::$one;
+ } // n-th root of 1 or 2 is 1
+
+ return $this->rootInner($n);
+ }
+
+ /**
+ * Calculates the nth root of a biginteger.
+ *
+ * Returns the nth root of a positive biginteger, where n defaults to 2
+ *
+ * @param int $n
+ * @return \phpseclib\Math\BigInteger\Engines\Engine
+ * @internal This function is based off of {@link http://mathforum.org/library/drmath/view/52605.html this page} and {@link http://stackoverflow.com/questions/11242920/calculating-nth-root-with-bcmath-in-php this stackoverflow question}.
+ */
+ protected function rootInner($n)
+ {
+ $n = new static($n);
+
+ // g is our guess number
+ $g = static::$two;
+ // while (g^n < num) g=g*2
+ while ($g->pow($n)->compare($this) < 0) {
+ $g = $g->multiply(static::$two);
+ }
+ // if (g^n==num) num is a power of 2, we're lucky, end of job
+ // == 0 bccomp(bcpow($g, $n), $n->value)==0
+ if ($g->pow($n)->equals($this) > 0) {
+ $root = $g;
+ return $this->normalize($root);
+ }
+
+ // if we're here num wasn't a power of 2 :(
+ $og = $g; // og means original guess and here is our upper bound
+ $g = $g->divide(static::$two)[0]; // g is set to be our lower bound
+ $step = $og->subtract($g)->divide(static::$two)[0]; // step is the half of upper bound - lower bound
+ $g = $g->add($step); // we start at lower bound + step , basically in the middle of our interval
+
+ // while step>1
+
+ while ($step->compare(static::$one) == 1) {
+ $guess = $g->pow($n);
+ $step = $step->divide(static::$two)[0];
+ $comp = $guess->compare($this); // compare our guess with real number
+ switch ($comp) {
+ case -1: // if guess is lower we add the new step
+ $g = $g->add($step);
+ break;
+ case 1: // if guess is higher we sub the new step
+ $g = $g->subtract($step);
+ break;
+ case 0: // if guess is exactly the num we're done, we return the value
+ $root = $g;
+ break 2;
+ }
+ }
+
+ if ($comp == 1) {
+ $g = $g->subtract($step);
+ }
+
+ // whatever happened, g is the closest guess we can make so return it
+ $root = $g;
+
+ return $this->normalize($root);
+ }
+
+ /**
+ * Calculates the nth root of a biginteger.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\Engine
+ */
+ public function root($n = 2)
+ {
+ return $this->rootHelper($n);
+ }
+
+ /**
+ * Return the minimum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\Engine
+ */
+ protected static function minHelper(array $nums)
+ {
+ if (count($nums) == 1) {
+ return $nums[0];
+ }
+ $min = $nums[0];
+ for ($i = 1; $i < count($nums); $i++) {
+ $min = $min->compare($nums[$i]) > 0 ? $nums[$i] : $min;
+ }
+ return $min;
+ }
+
+ /**
+ * Return the minimum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\Engine
+ */
+ protected static function maxHelper(array $nums)
+ {
+ if (count($nums) == 1) {
+ return $nums[0];
+ }
+ $max = $nums[0];
+ for ($i = 1; $i < count($nums); $i++) {
+ $max = $max->compare($nums[$i]) < 0 ? $nums[$i] : $max;
+ }
+ return $max;
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/GMP.php b/phpseclib/Math/BigInteger/Engines/GMP.php
new file mode 100644
index 00000000..5e5a0197
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/GMP.php
@@ -0,0 +1,600 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines;
+
+use ParagonIE\ConstantTime\Hex;
+use phpseclib\Exception\BadConfigurationException;
+
+/**
+ * GMP Engine.
+ *
+ * @package GMP
+ * @author Jim Wigginton
+ * @access public
+ */
+class GMP extends Engine
+{
+ /**
+ * Can Bitwise operations be done fast?
+ *
+ * @see parent::bitwise_leftRotate()
+ * @see parent::bitwise_rightRotate()
+ * @access protected
+ */
+ const FAST_BITWISE = true;
+
+ /**
+ * Engine Directory
+ *
+ * @see parent::setModExpEngine
+ * @access protected
+ */
+ const ENGINE_DIR = 'GMP';
+
+ /**
+ * Modular Exponentiation Engine
+ *
+ * @var string
+ */
+ protected static $modexpEngine;
+
+ /**
+ * Engine Validity Flag
+ *
+ * @var bool
+ */
+ protected static $isValidEngine;
+
+ /**
+ * BigInteger(0)
+ *
+ * @var \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ protected static $zero;
+
+ /**
+ * BigInteger(1)
+ *
+ * @var \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ protected static $one;
+
+ /**
+ * BigInteger(2)
+ *
+ * @var \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ protected static $two;
+
+ /**
+ * Primes > 2 and < 1000
+ *
+ * Unused for GMP Engine
+ *
+ * @var mixed
+ */
+ protected static $primes;
+
+ /**
+ * Test for engine validity
+ *
+ * @see parent::__construct()
+ * @return bool
+ */
+ public static function isValidEngine()
+ {
+ return extension_loaded('gmp');
+ }
+
+ /**
+ * Default constructor
+ *
+ * @param $x base-10 number or base-$base number if $base set.
+ * @param int $base
+ * @see parent::__construct()
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ public function __construct($x = 0, $base = 10)
+ {
+ if (!isset(self::$isValidEngine)) {
+ self::$isValidEngine = self::isValidEngine();
+ }
+ if (!self::$isValidEngine) {
+ throw new BadConfigurationException('GMP is not setup correctly on this system');
+ }
+
+ if ($x instanceof \GMP) {
+ $this->value = $x;
+ return;
+ }
+
+ $this->value = gmp_init(0);
+
+ parent::__construct($x, $base);
+ }
+
+ /**
+ * Initialize a GMP BigInteger Engine instance
+ *
+ * @param int $base
+ * @see parent::__construct()
+ */
+ protected function initialize($base)
+ {
+ switch (abs($base)) {
+ case 256:
+ $sign = $this->is_negative ? '-' : '';
+ $this->value = gmp_init($sign . '0x' . Hex::encode($this->value));
+ break;
+ case 16:
+ $temp = $this->is_negative ? '-0x' . $this->value : '0x' . $this->value;
+ $this->value = gmp_init($temp);
+ break;
+ case 10:
+ $this->value = gmp_init(isset($this->value) ? $this->value : '0');
+ }
+ }
+
+ /**
+ * Converts a BigInteger to a base-10 number.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return (string) $this->value;
+ }
+
+ /**
+ * Converts a BigInteger to a byte string (eg. base-256).
+ *
+ * @param bool $twos_compliment
+ * @return string
+ */
+ function toBytes($twos_compliment = false)
+ {
+ if ($twos_compliment) {
+ return $this->toBytesHelper();
+ }
+
+ if (gmp_cmp($this->value, gmp_init(0)) == 0) {
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
+ }
+
+ $temp = gmp_strval(gmp_abs($this->value), 16);
+ $temp = (strlen($temp) & 1) ? '0' . $temp : $temp;
+ $temp = Hex::decode($temp);
+
+ return $this->precision > 0 ?
+ substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
+ ltrim($temp, chr(0));
+ }
+
+ /**
+ * Adds two BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ public function add(GMP $y)
+ {
+ $temp = new self();
+ $temp->value = $this->value + $y->value;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Subtracts two BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ public function subtract(GMP $y)
+ {
+ $temp = new self();
+ $temp->value = $this->value - $y->value;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Multiplies two BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ public function multiply(GMP $x)
+ {
+ $temp = new self();
+ $temp->value = $this->value * $x->value;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Divides two BigIntegers.
+ *
+ * Returns an array whose first element contains the quotient and whose second element contains the
+ * "common residue". If the remainder would be positive, the "common residue" and the remainder are the
+ * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder
+ * and the divisor (basically, the "common residue" is the first positive modulo).
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ public function divide(GMP $y)
+ {
+ $quotient = new self();
+ $remainder = new self();
+
+ list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value);
+
+ if (gmp_sign($remainder->value) < 0) {
+ $remainder->value = $remainder->value + gmp_abs($y->value);
+ }
+
+ return [$this->normalize($quotient), $this->normalize($remainder)];
+ }
+
+ /**
+ * Compares two numbers.
+ *
+ * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is
+ * demonstrated thusly:
+ *
+ * $x > $y: $x->compare($y) > 0
+ * $x < $y: $x->compare($y) < 0
+ * $x == $y: $x->compare($y) == 0
+ *
+ * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y).
+ *
+ * @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
+ * @access public
+ * @see self::equals()
+ * @internal Could return $this->subtract($x), but that's not as fast as what we do do.
+ */
+ public function compare(GMP $y)
+ {
+ return gmp_cmp($this->value, $y->value);
+ }
+
+ /**
+ * Tests the equality of two numbers.
+ *
+ * If you need to see if one number is greater than or less than another number, use BigInteger::compare()
+ *
+ * @return bool
+ */
+ public function equals(GMP $x)
+ {
+ return $this->value == $x->value;
+ }
+
+ /**
+ * Calculates modular inverses.
+ *
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP|false
+ */
+ public function modInverse(GMP $n)
+ {
+ $temp = new self();
+ $temp->value = gmp_invert($this->value, $n->value);
+
+ return $temp->value === false ? false : $this->normalize($temp);
+ }
+
+ /**
+ * Calculates the greatest common divisor and Bezout's identity.
+ *
+ * Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that
+ * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which
+ * combination is returned is dependent upon which mode is in use. See
+ * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information.
+ *
+ * @param \phpseclib\Math\BigInteger\Engines\GMP $n
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ public function extendedGCD(GMP $n)
+ {
+ extract(gmp_gcdext($this->value, $n->value));
+
+ return [
+ 'gcd' => $this->normalize(new self($g)),
+ 'x' => $this->normalize(new self($s)),
+ 'y' => $this->normalize(new self($t))
+ ];
+ }
+
+ /**
+ * Calculates the greatest common divisor
+ *
+ * Say you have 693 and 609. The GCD is 21.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ public function gcd(GMP $n)
+ {
+ $r = gmp_gcd($this->value, $n->value);
+ return $this->normalize(new self($r));
+ }
+
+ /**
+ * Absolute value.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ * @access public
+ */
+ public function abs()
+ {
+ $temp = new self();
+ $temp->value = gmp_abs($this->value);
+
+ return $temp;
+ }
+
+ /**
+ * Logical And
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ public function bitwise_and(GMP $x)
+ {
+ $temp = new self();
+ $temp->value = $this->value & $x->value;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Logical Or
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ public function bitwise_or(GMP $x)
+ {
+ $temp = new self();
+ $temp->value = $this->value | $x->value;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Logical Exclusive Or
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ public function bitwise_xor(GMP $x)
+ {
+ $temp = new self();
+ $temp->value = $this->value ^ $x->value;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Logical Right Shift
+ *
+ * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
+ *
+ * @param int $shift
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ public function bitwise_rightShift($shift)
+ {
+ // 0xFFFFFFFF >> 2 == -1 (on 32-bit systems)
+ // gmp_init('0xFFFFFFFF') >> 2 == gmp_init('0x3FFFFFFF')
+
+ $temp = new self();
+ $temp->value = $this->value >> $shift;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Logical Left Shift
+ *
+ * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
+ *
+ * @param int $shift
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ public function bitwise_leftShift($shift)
+ {
+ $temp = new self();
+ $temp->value = $this->value << $shift;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ public function modPow(GMP $e, GMP $n)
+ {
+ return $this->powModOuter($e, $n);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * Alias for modPow().
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ public function powMod(GMP $e, GMP $n)
+ {
+ return $this->powModOuter($e, $n);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ protected function powModInner(GMP $e, GMP $n)
+ {
+ $class = self::$modexpEngine;
+ return $class::powModHelper($this, $e, $n);
+ }
+
+ /**
+ * Normalize
+ *
+ * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ protected function normalize(GMP $result)
+ {
+ $result->precision = $this->precision;
+ $result->bitmask = $this->bitmask;
+
+ if ($result->bitmask !== false) {
+ $result->value = $result->value & $result->bitmask->value;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Performs some post-processing for randomRangePrime
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ protected static function randomRangePrimeInner(Engine $x, Engine $min, Engine $max)
+ {
+ $p = gmp_nextprime($x->value);
+
+ if ($p <= $max->value) {
+ return new self($p);
+ }
+
+ if ($min->value != $x->value) {
+ $x = new self($x - 1);
+ }
+
+ return self::randomRangePrime($min, $x);
+ }
+
+ /**
+ * Generate a random prime number between a range
+ *
+ * If there's not a prime within the given range, false will be returned.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP|false
+ */
+ public static function randomRangePrime(GMP $min, GMP $max)
+ {
+ return self::randomRangePrimeOuter($min, $max);
+ }
+
+ /**
+ * Generate a random number between a range
+ *
+ * Returns a random number between $min and $max where $min and $max
+ * can be defined using one of the two methods:
+ *
+ * BigInteger::randomRange($min, $max)
+ * BigInteger::randomRange($max, $min)
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\Engine\GMP
+ */
+ public static function randomRange(GMP $min, GMP $max)
+ {
+ return self::randomRangeHelper($min, $max);
+ }
+
+ /**
+ * Make the current number odd
+ *
+ * If the current number is odd it'll be unchanged. If it's even, one will be added to it.
+ *
+ * @see self::randomPrime()
+ */
+ protected function make_odd()
+ {
+ gmp_setbit($this->value, 0);
+ }
+
+ /**
+ * Tests Primality
+ *
+ * @param int $t
+ * @return bool
+ */
+ protected function testPrimality($t)
+ {
+ return gmp_prob_prime($this->value, $t) != 0;
+ }
+
+ /**
+ * Calculates the nth root of a biginteger.
+ *
+ * Returns the nth root of a positive biginteger, where n defaults to 2
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\Engine\GMP
+ */
+ protected function rootInner($n)
+ {
+ $root = new self();
+ $root->value = gmp_root($this->value, $n);
+ return $this->normalize($root);
+ }
+
+ /**
+ * Performs exponentiation.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ public function pow(GMP $n)
+ {
+ $temp = new self();
+ $temp->value = $this->value ** $n->value;
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Return the minimum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ public static function min(GMP ...$nums)
+ {
+ return self::minHelper($nums);
+ }
+
+ /**
+ * Return the maximum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ public static function max(GMP ...$nums)
+ {
+ return self::maxHelper($nums);
+ }
+
+ /**
+ * Tests BigInteger to see if it is between two integers, inclusive
+ *
+ * @return boolean
+ */
+ public function between(GMP $min, GMP $max)
+ {
+ return $this->compare($min) >= 0 && $this->compare($max) <= 0;
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/GMP/DefaultEngine.php b/phpseclib/Math/BigInteger/Engines/GMP/DefaultEngine.php
new file mode 100644
index 00000000..c1b0e594
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/GMP/DefaultEngine.php
@@ -0,0 +1,41 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines\GMP;
+
+use phpseclib\Math\BigInteger\Engines\GMP;
+
+/**
+ * GMP Modular Exponentiation Engine
+ *
+ * @package GMP
+ * @author Jim Wigginton
+ * @access public
+ */
+abstract class DefaultEngine extends GMP
+{
+ /**
+ * Performs modular exponentiation.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\GMP
+ */
+ protected static function powModHelper(GMP $x, GMP $e, GMP $n)
+ {
+ $temp = new GMP();
+ $temp->value = gmp_powm($x->value, $e->value, $n->value);
+
+ return $x->normalize($temp);
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/OpenSSL.php b/phpseclib/Math/BigInteger/Engines/OpenSSL.php
new file mode 100644
index 00000000..93d216aa
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/OpenSSL.php
@@ -0,0 +1,71 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines;
+
+use phpseclib\Crypt\RSA;
+use phpseclib\Math\BigInteger;
+
+/**
+ * OpenSSL Modular Exponentiation Engine
+ *
+ * @package Engines
+ * @author Jim Wigginton
+ * @access public
+ */
+abstract class OpenSSL
+{
+ /**
+ * Test for engine validity
+ *
+ * @return bool
+ */
+ public static function isValidEngine()
+ {
+ return extension_loaded('openssl') && static::class != __CLASS__;
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\Engine
+ */
+ public static function powModHelper(Engine $x, Engine $e, Engine $n)
+ {
+ if ($n->getLengthInBytes() < 31 || $n->getLengthInBytes() > 16384) {
+ throw new \OutOfRangeException('Only modulo between 31 and 16384 bits are accepted');
+ }
+
+ $rsa = new RSA();
+ $rsa->load([
+ 'e' => new BigInteger($e),
+ 'n' => new BigInteger($n)
+ ]);
+ //$rsa->setPublicKeyFormat('PKCS1');
+
+ $plaintext = str_pad($x->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT);
+
+ // this is easily prone to failure. if the modulo is a multiple of 2 or 3 or whatever it
+ // won't work and you'll get a "failure: error:0906D06C:PEM routines:PEM_read_bio:no start line"
+ // error. i suppose, for even numbers, we could do what PHP\Montgomery.php does, but then what
+ // about odd numbers divisible by 3, by 5, etc?
+ if (!openssl_public_encrypt($plaintext, $result, "$rsa", OPENSSL_NO_PADDING)) {
+ throw new \UnexpectedValueException(openssl_error_string());
+ }
+
+ $class = get_class($x);
+ return new $class($result, 256);
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/PHP.php b/phpseclib/Math/BigInteger/Engines/PHP.php
new file mode 100644
index 00000000..8e448cc0
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/PHP.php
@@ -0,0 +1,1329 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines;
+
+use ParagonIE\ConstantTime\Hex;
+use phpseclib\Exception\BadConfigurationException;
+
+/**
+ * pure-PHP Engine.
+ *
+ * @package PHP
+ * @author Jim Wigginton
+ * @access public
+ */
+abstract class PHP extends Engine
+{
+ /**#@+
+ * Array constants
+ *
+ * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and
+ * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them.
+ *
+ * @access protected
+ */
+ /**
+ * $result[self::VALUE] contains the value.
+ */
+ const VALUE = 0;
+ /**
+ * $result[self::SIGN] contains the sign.
+ */
+ const SIGN = 1;
+ /**#@-*/
+
+ /**
+ * Karatsuba Cutoff
+ *
+ * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication?
+ *
+ * @access private
+ */
+ const KARATSUBA_CUTOFF = 25;
+
+ /**
+ * Can Bitwise operations be done fast?
+ *
+ * @see parent::bitwise_leftRotate()
+ * @see parent::bitwise_rightRotate()
+ * @access protected
+ */
+ const FAST_BITWISE = true;
+
+ /**
+ * Engine Directory
+ *
+ * @see parent::setModExpEngine
+ * @access protected
+ */
+ const ENGINE_DIR = 'PHP';
+
+ /**
+ * Default constructor
+ *
+ * @param $x base-10 number or base-$base number if $base set.
+ * @param int $base
+ * @see parent::__construct()
+ * @return \phpseclib\Math\BigInteger\Engines\PHP
+ */
+ public function __construct($x = 0, $base = 10)
+ {
+ if (!isset(static::$isValidEngine)) {
+ static::$isValidEngine = static::isValidEngine();
+ }
+ if (!static::$isValidEngine) {
+ throw new BadConfigurationException(static::class . ' is not setup correctly on this system');
+ }
+
+ $this->value = [];
+ parent::__construct($x, $base);
+ }
+
+ /**
+ * Initialize a PHP BigInteger Engine instance
+ *
+ * @param int $base
+ * @see parent::__construct()
+ */
+ protected function initialize($base)
+ {
+ switch (abs($base)) {
+ case 256:
+ $x = $this->value;
+ $this->value = [];
+
+ while (strlen($x)) {
+ $this->value[] = self::bytes2int(self::base256_rshift($x, static::BASE));
+ }
+ break;
+ case 16:
+ $x = (strlen($this->value) & 1) ? '0' . $this->value : $this->value;
+ $temp = new static(Hex::decode($x), 256);
+ $this->value = $temp->value;
+ break;
+ case 10:
+ $temp = new static();
+
+ $multiplier = new static();
+ $multiplier->value = [static::MAX10];
+
+ $x = $this->value;
+
+ if ($x[0] == '-') {
+ $this->is_negative = true;
+ $x = substr($x, 1);
+ }
+
+ $x = str_pad($x, strlen($x) + ((static::MAX10LEN - 1) * strlen($x)) % static::MAX10LEN, 0, STR_PAD_LEFT);
+ while (strlen($x)) {
+ $temp = $temp->multiply($multiplier);
+ $temp = $temp->add(new static($this->int2bytes(substr($x, 0, static::MAX10LEN)), 256));
+ $x = substr($x, static::MAX10LEN);
+ }
+
+ $this->value = $temp->value;
+ }
+ }
+
+ /**
+ * Pads strings so that unpack may be used on them
+ *
+ * @return string
+ */
+ protected function pad($str)
+ {
+ $length = strlen($str);
+
+ $pad = 4 - (strlen($str) % 4);
+
+ return str_pad($str, $length + $pad, "\0", STR_PAD_LEFT);
+ }
+
+ /**
+ * Converts a BigInteger to a base-10 number.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ if (!count($this->value)) {
+ return '0';
+ }
+
+ $temp = clone $this;
+ $temp->is_negative = false;
+
+ $divisor = new static();
+ $divisor->value = [static::MAX10];
+ $result = '';
+ while (count($temp->value)) {
+ list($temp, $mod) = $temp->divide($divisor);
+ $result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', static::MAX10LEN, '0', STR_PAD_LEFT) . $result;
+ }
+ $result = ltrim($result, '0');
+ if (empty($result)) {
+ $result = '0';
+ }
+
+ if ($this->is_negative) {
+ $result = '-' . $result;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Converts a BigInteger to a byte string (eg. base-256).
+ *
+ * @param bool $twos_compliment
+ * @return string
+ */
+ public function toBytes($twos_compliment = false)
+ {
+ if ($twos_compliment) {
+ return $this->toBytesHelper();
+ }
+
+ if (!count($this->value)) {
+ return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
+ }
+ $result = self::int2bytes($this->value[count($this->value) - 1]);
+
+ for ($i = count($this->value) - 2; $i >= 0; --$i) {
+ self::base256_lshift($result, static::BASE);
+ $result = $result | str_pad(self::int2bytes($this->value[$i]), strlen($result), chr(0), STR_PAD_LEFT);
+ }
+
+ return $this->precision > 0 ?
+ str_pad(substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) :
+ $result;
+ }
+
+ /**
+ * Performs addition.
+ *
+ * @param array $x_value
+ * @param bool $x_negative
+ * @param array $y_value
+ * @param bool $y_negative
+ * @return array
+ */
+ protected static function addHelper(array $x_value, $x_negative, array $y_value, $y_negative)
+ {
+ $x_size = count($x_value);
+ $y_size = count($y_value);
+
+ if ($x_size == 0) {
+ return [
+ self::VALUE => $y_value,
+ self::SIGN => $y_negative
+ ];
+ } elseif ($y_size == 0) {
+ return [
+ self::VALUE => $x_value,
+ self::SIGN => $x_negative
+ ];
+ }
+
+ // subtract, if appropriate
+ if ($x_negative != $y_negative) {
+ if ($x_value == $y_value) {
+ return [
+ self::VALUE => array(),
+ self::SIGN => false
+ ];
+ }
+
+ $temp = self::subtractHelper($x_value, false, $y_value, false);
+ $temp[self::SIGN] = self::compareHelper($x_value, false, $y_value, false) > 0 ?
+ $x_negative : $y_negative;
+
+ return $temp;
+ }
+
+ if ($x_size < $y_size) {
+ $size = $x_size;
+ $value = $y_value;
+ } else {
+ $size = $y_size;
+ $value = $x_value;
+ }
+
+ $value[count($value)] = 0; // just in case the carry adds an extra digit
+
+ $carry = 0;
+ for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) {
+ //$sum = $x_value[$j] * static::BASE_FULL + $x_value[$i] + $y_value[$j] * static::BASE_FULL + $y_value[$i] + $carry;
+ $sum = ($x_value[$j] + $y_value[$j]) * static::BASE_FULL + $x_value[$i] + $y_value[$i] + $carry;
+ $carry = $sum >= static::MAX_DIGIT2; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
+ $sum = $carry ? $sum - static::MAX_DIGIT2 : $sum;
+
+ $temp = static::BASE === 26 ? intval($sum / 0x4000000) : ($sum >> 31);
+
+ $value[$i] = (int) ($sum - static::BASE_FULL * $temp); // eg. a faster alternative to fmod($sum, 0x4000000)
+ $value[$j] = $temp;
+ }
+
+ if ($j == $size) { // ie. if $y_size is odd
+ $sum = $x_value[$i] + $y_value[$i] + $carry;
+ $carry = $sum >= static::BASE_FULL;
+ $value[$i] = $carry ? $sum - static::BASE_FULL : $sum;
+ ++$i; // ie. let $i = $j since we've just done $value[$i]
+ }
+
+ if ($carry) {
+ for (; $value[$i] == static::MAX_DIGIT; ++$i) {
+ $value[$i] = 0;
+ }
+ ++$value[$i];
+ }
+
+ return [
+ self::VALUE => self::trim($value),
+ self::SIGN => $x_negative
+ ];
+ }
+
+ /**
+ * Performs subtraction.
+ *
+ * @param array $x_value
+ * @param bool $x_negative
+ * @param array $y_value
+ * @param bool $y_negative
+ * @return array
+ */
+ static function subtractHelper(array $x_value, $x_negative, array $y_value, $y_negative)
+ {
+ $x_size = count($x_value);
+ $y_size = count($y_value);
+
+ if ($x_size == 0) {
+ return [
+ self::VALUE => $y_value,
+ self::SIGN => !$y_negative
+ ];
+ } elseif ($y_size == 0) {
+ return [
+ self::VALUE => $x_value,
+ self::SIGN => $x_negative
+ ];
+ }
+
+ // add, if appropriate (ie. -$x - +$y or +$x - -$y)
+ if ($x_negative != $y_negative) {
+ $temp = self::addHelper($x_value, false, $y_value, false);
+ $temp[self::SIGN] = $x_negative;
+
+ return $temp;
+ }
+
+ $diff = self::compareHelper($x_value, $x_negative, $y_value, $y_negative);
+
+ if (!$diff) {
+ return [
+ self::VALUE => [],
+ self::SIGN => false
+ ];
+ }
+
+ // switch $x and $y around, if appropriate.
+ if ((!$x_negative && $diff < 0) || ($x_negative && $diff > 0)) {
+ $temp = $x_value;
+ $x_value = $y_value;
+ $y_value = $temp;
+
+ $x_negative = !$x_negative;
+
+ $x_size = count($x_value);
+ $y_size = count($y_value);
+ }
+
+ // at this point, $x_value should be at least as big as - if not bigger than - $y_value
+
+ $carry = 0;
+ for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) {
+ $sum = ($x_value[$j] - $y_value[$j]) * static::BASE_FULL + $x_value[$i] - $y_value[$i] - $carry;
+
+ $carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
+ $sum = $carry ? $sum + static::MAX_DIGIT2 : $sum;
+
+ $temp = static::BASE === 26 ? intval($sum / 0x4000000) : ($sum >> 31);
+
+ $x_value[$i] = (int) ($sum - static::BASE_FULL * $temp);
+ $x_value[$j] = $temp;
+ }
+
+ if ($j == $y_size) { // ie. if $y_size is odd
+ $sum = $x_value[$i] - $y_value[$i] - $carry;
+ $carry = $sum < 0;
+ $x_value[$i] = $carry ? $sum + static::BASE_FULL : $sum;
+ ++$i;
+ }
+
+ if ($carry) {
+ for (; !$x_value[$i]; ++$i) {
+ $x_value[$i] = static::MAX_DIGIT;
+ }
+ --$x_value[$i];
+ }
+
+ return [
+ self::VALUE => self::trim($x_value),
+ self::SIGN => $x_negative
+ ];
+ }
+
+ /**
+ * Performs multiplication.
+ *
+ * @param array $x_value
+ * @param bool $x_negative
+ * @param array $y_value
+ * @param bool $y_negative
+ * @return array
+ */
+ protected static function multiplyHelper(array $x_value, $x_negative, array $y_value, $y_negative)
+ {
+ //if ( $x_value == $y_value ) {
+ // return [
+ // self::VALUE => $this->_square($x_value),
+ // self::SIGN => $x_sign != $y_value
+ // ];
+ //}
+
+ $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
+ ];
+ }
+
+ return [
+ self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ?
+ self::trim(self::regularMultiply($x_value, $y_value)) :
+ self::trim(self::karatsuba($x_value, $y_value)),
+ self::SIGN => $x_negative != $y_negative
+ ];
+ }
+
+ /**
+ * Performs Karatsuba multiplication on two BigIntegers
+ *
+ * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}.
+ *
+ * @param array $x_value
+ * @param array $y_value
+ * @return array
+ */
+ private static function karatsuba(array $x_value, array $y_value)
+ {
+ $m = min(count($x_value) >> 1, count($y_value) >> 1);
+
+ if ($m < self::KARATSUBA_CUTOFF) {
+ return self::regularMultiply($x_value, $y_value);
+ }
+
+ $x1 = array_slice($x_value, $m);
+ $x0 = array_slice($x_value, 0, $m);
+ $y1 = array_slice($y_value, $m);
+ $y0 = array_slice($y_value, 0, $m);
+
+ $z2 = self::karatsuba($x1, $y1);
+ $z0 = self::karatsuba($x0, $y0);
+
+ $z1 = self::addHelper($x1, false, $x0, false);
+ $temp = self::addHelper($y1, false, $y0, false);
+ $z1 = self::karatsuba($z1[self::VALUE], $temp[self::VALUE]);
+ $temp = self::addHelper($z2, false, $z0, false);
+ $z1 = self::subtractHelper($z1, false, $temp[self::VALUE], false);
+
+ $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
+ $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);
+
+ $xy = self::addHelper($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
+ $xy = self::addHelper($xy[self::VALUE], $xy[self::SIGN], $z0, false);
+
+ return $xy[self::VALUE];
+ }
+
+ /**
+ * Performs long multiplication on two BigIntegers
+ *
+ * Modeled after 'multiply' in MutableBigInteger.java.
+ *
+ * @param array $x_value
+ * @param array $y_value
+ * @return array
+ */
+ protected static function regularMultiply(array $x_value, array $y_value)
+ {
+ $x_length = count($x_value);
+ $y_length = count($y_value);
+
+ if (!$x_length || !$y_length) { // a 0 is being multiplied
+ return [];
+ }
+
+ $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
+ $temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
+ $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
+ $product_value[$j] = (int) ($temp - static::BASE_FULL * $carry);
+ }
+
+ $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; ++$j, ++$k) {
+ $temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
+ $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
+ $product_value[$k] = (int) ($temp - static::BASE_FULL * $carry);
+ }
+
+ $product_value[$k] = $carry;
+ }
+
+ return $product_value;
+ }
+
+ /**
+ * Divides two BigIntegers.
+ *
+ * Returns an array whose first element contains the quotient and whose second element contains the
+ * "common residue". If the remainder would be positive, the "common residue" and the remainder are the
+ * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder
+ * and the divisor (basically, the "common residue" is the first positive modulo).
+ *
+ * @param \phpseclib\Math\BigInteger\PHP $y
+ * @return array
+ * @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}.
+ */
+ protected function divideHelper(PHP $y)
+ {
+ if (count($y->value) == 1) {
+ list($q, $r) = $this->divide_digit($this->value, $y->value[0]);
+ $quotient = new static();
+ $remainder = new static();
+ $quotient->value = $q;
+ $remainder->value = [$r];
+ $quotient->is_negative = $this->is_negative != $y->is_negative;
+ return [$this->normalize($quotient), $this->normalize($remainder)];
+ }
+
+ $x = clone $this;
+ $y = clone $y;
+
+ $x_sign = $x->is_negative;
+ $y_sign = $y->is_negative;
+
+ $x->is_negative = $y->is_negative = false;
+
+ $diff = $x->compare($y);
+
+ if (!$diff) {
+ $temp = new static();
+ $temp->value = [1];
+ $temp->is_negative = $x_sign != $y_sign;
+ return [$this->normalize($temp), $this->normalize(static::$zero)];
+ }
+
+ if ($diff < 0) {
+ // if $x is negative, "add" $y.
+ if ($x_sign) {
+ $x = $y->subtract($x);
+ }
+ return [$this->normalize(static::$zero), $this->normalize($x)];
+ }
+
+ // normalize $x and $y as described in HAC 14.23 / 14.24
+ $msb = $y->value[count($y->value) - 1];
+ for ($shift = 0; !($msb & static::MSB); ++$shift) {
+ $msb <<= 1;
+ }
+ $x->lshift($shift);
+ $y->lshift($shift);
+ $y_value = &$y->value;
+
+ $x_max = count($x->value) - 1;
+ $y_max = count($y->value) - 1;
+
+ $quotient = new static();
+ $quotient_value = &$quotient->value;
+ $quotient_value = self::array_repeat(0, $x_max - $y_max + 1);
+
+ static $temp, $lhs, $rhs;
+ if (!isset($temp)) {
+ $temp = new static();
+ $lhs = new static();
+ $rhs = new static();
+ }
+ $temp_value = &$temp->value;
+ $rhs_value = &$rhs->value;
+
+ // $temp = $y << ($x_max - $y_max-1) in base 2**26
+ $temp_value = array_merge(self::array_repeat(0, $x_max - $y_max), $y_value);
+
+ while ($x->compare($temp) >= 0) {
+ // calculate the "common residue"
+ ++$quotient_value[$x_max - $y_max];
+ $x = $x->subtract($temp);
+ $x_max = count($x->value) - 1;
+ }
+
+ for ($i = $x_max; $i >= $y_max + 1; --$i) {
+ $x_value = &$x->value;
+ $x_window = [
+ isset($x_value[$i]) ? $x_value[$i] : 0,
+ isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0,
+ isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0
+ ];
+ $y_window = [
+ $y_value[$y_max],
+ ($y_max > 0) ? $y_value[$y_max - 1] : 0
+ ];
+
+ $q_index = $i - $y_max - 1;
+ if ($x_window[0] == $y_window[0]) {
+ $quotient_value[$q_index] = static::MAX_DIGIT;
+ } else {
+ $quotient_value[$q_index] = self::safe_divide(
+ $x_window[0] * static::BASE_FULL + $x_window[1],
+ $y_window[0]
+ );
+ }
+
+ $temp_value = [$y_window[1], $y_window[0]];
+
+ $lhs->value = [$quotient_value[$q_index]];
+ $lhs = $lhs->multiply($temp);
+
+ $rhs_value = [$x_window[2], $x_window[1], $x_window[0]];
+
+ while ($lhs->compare($rhs) > 0) {
+ --$quotient_value[$q_index];
+
+ $lhs->value = [$quotient_value[$q_index]];
+ $lhs = $lhs->multiply($temp);
+ }
+
+ $adjust = self::array_repeat(0, $q_index);
+ $temp_value = [$quotient_value[$q_index]];
+ $temp = $temp->multiply($y);
+ $temp_value = &$temp->value;
+ $temp_value = array_merge($adjust, $temp_value);
+
+ $x = $x->subtract($temp);
+
+ if ($x->compare(static::$zero) < 0) {
+ $temp_value = array_merge($adjust, $y_value);
+ $x = $x->add($temp);
+
+ --$quotient_value[$q_index];
+ }
+
+ $x_max = count($x_value) - 1;
+ }
+
+ // unnormalize the remainder
+ $x->rshift($shift);
+
+ $quotient->is_negative = $x_sign != $y_sign;
+
+ // calculate the "common residue", if appropriate
+ if ($x_sign) {
+ $y->rshift($shift);
+ $x = $y->subtract($x);
+ }
+
+ return [$this->normalize($quotient), $this->normalize($x)];
+ }
+
+ /**
+ * Divides a BigInteger by a regular integer
+ *
+ * abc / x = a00 / x + b0 / x + c / x
+ *
+ * @param int $divisor
+ * @return array
+ */
+ private static function divide_digit(array $dividend, $divisor)
+ {
+ $carry = 0;
+ $result = [];
+
+ for ($i = count($dividend) - 1; $i >= 0; --$i) {
+ $temp = static::BASE_FULL * $carry + $dividend[$i];
+ $result[$i] = self::safe_divide($temp, $divisor);
+ $carry = (int) ($temp - $divisor * $result[$i]);
+ }
+
+ return [$result, $carry];
+ }
+
+ /**
+ * Single digit division
+ *
+ * Even if int64 is being used the division operator will return a float64 value
+ * if the dividend is not evenly divisible by the divisor. Since a float64 doesn't
+ * have the precision of int64 this is a problem so, when int64 is being used,
+ * we'll guarantee that the dividend is divisible by first subtracting the remainder.
+ *
+ * @param int $x
+ * @param int $y
+ * @return int
+ */
+ private static function safe_divide($x, $y)
+ {
+ if (static::BASE === 26) {
+ return (int) ($x / $y);
+ }
+
+ // static::BASE === 31
+ return ($x - ($x % $y)) / $y;
+ }
+
+ /*
+ * Convert an array / boolean to a PHP BigInteger object
+ *
+ * @param array $arr
+ * @return \phpseclib\Math\BigInteger\Engines\PHP
+ */
+ protected function convertToObj(array $arr)
+ {
+ $result = new static();
+ $result->value = $arr[self::VALUE];
+ $result->is_negative = $arr[self::SIGN];
+
+ return $this->normalize($result);
+ }
+
+ /**
+ * Normalize
+ *
+ * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP
+ */
+ protected function normalize($result)
+ {
+ $result->precision = $this->precision;
+ $result->bitmask = $this->bitmask;
+
+ $value = &$result->value;
+
+ if (!count($value)) {
+ return $result;
+ }
+
+ $value = $this->trim($value);
+
+ if (!empty($result->bitmask->value)) {
+ $length = min(count($value), count($result->bitmask->value));
+ $value = array_slice($value, 0, $length);
+
+ for ($i = 0; $i < $length; ++$i) {
+ $value[$i] = $value[$i] & $result->bitmask->value[$i];
+ }
+ }
+
+ return $result;
+ }
+
+ /*
+ * Compares two numbers.
+ *
+ * @param array $x_value
+ * @param bool $x_negative
+ * @param array $y_value
+ * @param bool $y_negative
+ * @return int
+ * @see static::compare()
+ */
+ protected static function compareHelper(array $x_value, $x_negative, array $y_value, $y_negative)
+ {
+ if ($x_negative != $y_negative) {
+ return (!$x_negative && $y_negative) ? 1 : -1;
+ }
+
+ $result = $x_negative ? -1 : 1;
+
+ if (count($x_value) != count($y_value)) {
+ return (count($x_value) > count($y_value)) ? $result : -$result;
+ }
+ $size = max(count($x_value), count($y_value));
+
+ $x_value = array_pad($x_value, $size, 0);
+ $y_value = array_pad($y_value, $size, 0);
+
+ for ($i = count($x_value) - 1; $i >= 0; --$i) {
+ if ($x_value[$i] != $y_value[$i]) {
+ return ($x_value[$i] > $y_value[$i]) ? $result : -$result;
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Calculates the greatest common divisor and Bezout's identity.
+ *
+ * Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that
+ * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which
+ * combination is returned is dependent upon which mode is in use. See
+ * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP
+ * @internal Calculates the GCD using the binary xGCD algorithim described in
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes,
+ * the more traditional algorithim requires "relatively costly multiple-precision divisions".
+ */
+ protected function extendedGCDHelper(PHP $n)
+ {
+ $y = clone $n;
+ $x = clone $this;
+ $g = new static();
+ $g->value = [1];
+
+ while (!(($x->value[0] & 1)|| ($y->value[0] & 1))) {
+ $x->rshift(1);
+ $y->rshift(1);
+ $g->lshift(1);
+ }
+
+ $u = clone $x;
+ $v = clone $y;
+
+ $a = new static();
+ $b = new static();
+ $c = new static();
+ $d = new static();
+
+ $a->value = $d->value = $g->value = [1];
+ $b->value = $c->value = [];
+
+ while (!empty($u->value)) {
+ while (!($u->value[0] & 1)) {
+ $u->rshift(1);
+ if ((!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1))) {
+ $a = $a->add($y);
+ $b = $b->subtract($x);
+ }
+ $a->rshift(1);
+ $b->rshift(1);
+ }
+
+ while (!($v->value[0] & 1)) {
+ $v->rshift(1);
+ if ((!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1))) {
+ $c = $c->add($y);
+ $d = $d->subtract($x);
+ }
+ $c->rshift(1);
+ $d->rshift(1);
+ }
+
+ if ($u->compare($v) >= 0) {
+ $u = $u->subtract($v);
+ $a = $a->subtract($c);
+ $b = $b->subtract($d);
+ } else {
+ $v = $v->subtract($u);
+ $c = $c->subtract($a);
+ $d = $d->subtract($b);
+ }
+ }
+
+ return [
+ 'gcd' => $this->normalize($g->multiply($v)),
+ 'x' => $this->normalize($c),
+ 'y' => $this->normalize($d)
+ ];
+ }
+
+ /**
+ * Absolute value.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP
+ */
+ public function abs()
+ {
+ $temp = new static();
+ $temp->value = $this->value;
+
+ return $temp;
+ }
+
+ /**
+ * Logical And
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP
+ */
+ protected function bitwiseAndHelper(PHP $x)
+ {
+ $result = clone $this;
+
+ $length = min(count($x->value), count($this->value));
+
+ $result->value = array_slice($result->value, 0, $length);
+
+ for ($i = 0; $i < $length; ++$i) {
+ $result->value[$i]&= $x->value[$i];
+ }
+
+ return $this->normalize($result);
+ }
+
+ /**
+ * Logical Or
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP
+ */
+ protected function bitwiseOrHelper(PHP $x)
+ {
+ $length = max(count($this->value), count($x->value));
+ $result = clone $this;
+ $result->value = array_pad($result->value, $length, 0);
+ $x->value = array_pad($x->value, $length, 0);
+
+ for ($i = 0; $i < $length; ++$i) {
+ $result->value[$i]|= $x->value[$i];
+ }
+
+ return $this->normalize($result);
+ }
+
+ /**
+ * Logical Exlusive Or
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP
+ */
+ protected function bitwiseXorHelper(PHP $x)
+ {
+ $length = max(count($this->value), count($x->value));
+ $result = clone $this;
+ $result->value = array_pad($result->value, $length, 0);
+ $x->value = array_pad($x->value, $length, 0);
+
+ for ($i = 0; $i < $length; ++$i) {
+ $result->value[$i]^= $x->value[$i];
+ }
+
+ return $this->normalize($result);
+ }
+
+ /**
+ * Trim
+ *
+ * Removes leading zeros
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP
+ */
+ protected static function trim(array $value)
+ {
+ for ($i = count($value) - 1; $i >= 0; --$i) {
+ if ($value[$i]) {
+ break;
+ }
+ unset($value[$i]);
+ }
+
+ return $value;
+ }
+
+ /**
+ * Logical Right Shift
+ *
+ * Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder.
+ *
+ * @param $x String
+ * @param $shift Integer
+ * @return string
+ */
+ private static function base256_rshift(&$x, $shift)
+ {
+ if ($shift == 0) {
+ $x = ltrim($x, chr(0));
+ return '';
+ }
+
+ $num_bytes = $shift >> 3; // eg. floor($shift/8)
+ $shift &= 7; // eg. $shift % 8
+
+ $remainder = '';
+ if ($num_bytes) {
+ $start = $num_bytes > strlen($x) ? -strlen($x) : -$num_bytes;
+ $remainder = substr($x, $start);
+ $x = substr($x, 0, -$num_bytes);
+ }
+
+ $carry = 0;
+ $carry_shift = 8 - $shift;
+ for ($i = 0; $i < strlen($x); ++$i) {
+ $temp = (ord($x[$i]) >> $shift) | $carry;
+ $carry = (ord($x[$i]) << $carry_shift) & 0xFF;
+ $x[$i] = chr($temp);
+ }
+ $x = ltrim($x, chr(0));
+
+ $remainder = chr($carry >> $carry_shift) . $remainder;
+
+ return ltrim($remainder, chr(0));
+ }
+
+ /**
+ * Logical Right Shift
+ *
+ * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
+ *
+ * @param int $shift
+ * @return \phpseclib\Math\BigInteger\Engines\PHP
+ */
+ public function bitwise_rightShift($shift)
+ {
+ $temp = new static();
+
+ // could just replace lshift with this, but then all lshift() calls would need to be rewritten
+ // and I don't want to do that...
+ $temp->value = $this->value;
+ $temp->rshift($shift);
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Logical Left Shift
+ *
+ * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
+ *
+ * @param int $shift
+ * @return \phpseclib\Math\BigInteger\Engines\PHP
+ */
+ public function bitwise_leftShift($shift)
+ {
+ $temp = new static();
+ // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten
+ // and I don't want to do that...
+ $temp->value = $this->value;
+ $temp->lshift($shift);
+
+ return $this->normalize($temp);
+ }
+
+ /**
+ * Converts 32-bit integers to bytes.
+ *
+ * @param int $x
+ * @return string
+ */
+ private static function int2bytes($x)
+ {
+ return ltrim(pack('N', $x), chr(0));
+ }
+
+ /**
+ * Converts bytes to 32-bit integers
+ *
+ * @param string $x
+ * @return int
+ */
+ private static function bytes2int($x)
+ {
+ $temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT));
+ return $temp['int'];
+ }
+
+ /**
+ * Array Repeat
+ *
+ * @param int $input
+ * @param int $multiplier
+ * @return array
+ */
+ protected static function array_repeat($input, $multiplier)
+ {
+ return $multiplier ? array_fill(0, $multiplier, $input) : [];
+ }
+
+ /**
+ * Logical Left Shift
+ *
+ * Shifts BigInteger's by $shift bits.
+ *
+ * @param int $shift
+ */
+ protected function lshift($shift)
+ {
+ if ($shift == 0) {
+ return;
+ }
+
+ $num_digits = (int) ($shift / static::BASE);
+ $shift %= static::BASE;
+ $shift = 1 << $shift;
+
+ $carry = 0;
+
+ for ($i = 0; $i < count($this->value); ++$i) {
+ $temp = $this->value[$i] * $shift + $carry;
+ $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
+ $this->value[$i] = (int) ($temp - $carry * static::BASE_FULL);
+ }
+
+ if ($carry) {
+ $this->value[count($this->value)] = $carry;
+ }
+
+ while ($num_digits--) {
+ array_unshift($this->value, 0);
+ }
+ }
+
+ /**
+ * Logical Right Shift
+ *
+ * Shifts BigInteger's by $shift bits.
+ *
+ * @param int $shift
+ */
+ protected function rshift($shift)
+ {
+ if ($shift == 0) {
+ return;
+ }
+
+ $num_digits = (int) ($shift / static::BASE);
+ $shift %= static::BASE;
+ $carry_shift = static::BASE - $shift;
+ $carry_mask = (1 << $shift) - 1;
+
+ if ($num_digits) {
+ $this->value = array_slice($this->value, $num_digits);
+ }
+
+ $carry = 0;
+
+ for ($i = count($this->value) - 1; $i >= 0; --$i) {
+ $temp = $this->value[$i] >> $shift | $carry;
+ $carry = ($this->value[$i] & $carry_mask) << $carry_shift;
+ $this->value[$i] = $temp;
+ }
+
+ $this->value = $this->trim($this->value);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP
+ */
+ protected function powModInner(PHP $e, PHP $n)
+ {
+ try {
+ $class = static::$modexpEngine;
+ return $class::powModHelper($this, $e, $n, static::class);
+ } catch (\Exception $err) {
+ return PHP\DefaultEngine::powModHelper($this, $e, $n, static::class);
+ }
+ }
+
+ /**
+ * Performs squaring
+ *
+ * @return array
+ */
+ protected static function square(array $x)
+ {
+ return count($x) < 2 * self::KARATSUBA_CUTOFF ?
+ self::trim(self::baseSquare($x)) :
+ self::trim(self::karatsubaSquare($x));
+ }
+
+ /**
+ * Performs traditional squaring on two BigIntegers
+ *
+ * Squaring can be done faster than multiplying a number by itself can be. See
+ * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} /
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information.
+ *
+ * @return array
+ */
+ protected static function baseSquare(array $value)
+ {
+ if (empty($value)) {
+ return [];
+ }
+ $square_value = self::array_repeat(0, 2 * count($value));
+
+ for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) {
+ $i2 = $i << 1;
+
+ $temp = $square_value[$i2] + $value[$i] * $value[$i];
+ $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
+ $square_value[$i2] = (int) ($temp - static::BASE_FULL * $carry);
+
+ // note how we start from $i+1 instead of 0 as we do in multiplication.
+ for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) {
+ $temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry;
+ $carry = static::BASE === 26 ? intval($temp / 0x4000000) : ($temp >> 31);
+ $square_value[$k] = (int) ($temp - static::BASE_FULL * $carry);
+ }
+
+ // the following line can yield values larger 2**15. at this point, PHP should switch
+ // over to floats.
+ $square_value[$i + $max_index + 1] = $carry;
+ }
+
+ return $square_value;
+ }
+
+ /**
+ * Performs Karatsuba "squaring" on two BigIntegers
+ *
+ * See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
+ * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}.
+ *
+ * @return array
+ */
+ protected static function karatsubaSquare(array $value)
+ {
+ $m = count($value) >> 1;
+
+ if ($m < self::KARATSUBA_CUTOFF) {
+ return self::baseSquare($value);
+ }
+
+ $x1 = array_slice($value, $m);
+ $x0 = array_slice($value, 0, $m);
+
+ $z2 = self::karatsubaSquare($x1);
+ $z0 = self::karatsubaSquare($x0);
+
+ $z1 = self::addHelper($x1, false, $x0, false);
+ $z1 = self::karatsubaSquare($z1[self::VALUE]);
+ $temp = self::addHelper($z2, false, $z0, false);
+ $z1 = self::subtractHelper($z1, false, $temp[self::VALUE], false);
+
+ $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
+ $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]);
+
+ $xx = self::addHelper($z2, false, $z1[self::VALUE], $z1[self::SIGN]);
+ $xx = self::addHelper($xx[self::VALUE], $xx[self::SIGN], $z0, false);
+
+ return $xx[self::VALUE];
+ }
+
+ /**
+ * Make the current number odd
+ *
+ * If the current number is odd it'll be unchanged. If it's even, one will be added to it.
+ *
+ * @see self::randomPrime()
+ */
+ protected function make_odd()
+ {
+ $this->value[0] |= 1;
+ }
+
+ /**
+ * Test the number against small primes.
+ *
+ * @see self::isPrime()
+ */
+ protected function testSmallPrimes()
+ {
+ if ($this->value == [1]) {
+ return false;
+ }
+ if ($this->value == [2]) {
+ return true;
+ }
+ if (~$this->value[0] & 1) {
+ return false;
+ }
+
+ $value = $this->value;
+ foreach (self::$primes as $prime) {
+ list(, $r) = self::divide_digit($value, $prime);
+ if (!$r) {
+ return count($value) == 1 && $value[0] == $prime;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Scan for 1 and right shift by that amount
+ *
+ * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
+ *
+ * @see self::isPrime()
+ * @return int
+ */
+ protected static function scan1divide($r)
+ {
+ $r_value = &$r->value;
+ for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) {
+ $temp = ~$r_value[$i] & static::MAX_DIGIT;
+ for ($j = 1; ($temp >> $j) & 1; ++$j) {
+ }
+ if ($j <= static::BASE) {
+ break;
+ }
+ }
+ $s = static::BASE * $i + $j - 1;
+ $r->rshift($s);
+ return $s;
+ }
+
+ /**
+ * Performs exponentiation.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP
+ */
+ protected function powHelper(PHP $n)
+ {
+ if ($n->compare(static::$zero) == 0) {
+ return new self(1);
+ } // n^0 = 1
+
+
+ $temp = clone $this;
+ while (!$n->equals(static::$one)) {
+ $temp = $temp->multiply($this);
+ $n = $n->subtract(static::$one);
+ }
+
+ return $temp;
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/PHP/Base.php b/phpseclib/Math/BigInteger/Engines/PHP/Base.php
new file mode 100644
index 00000000..640d17c6
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/PHP/Base.php
@@ -0,0 +1,149 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines\PHP;
+
+use phpseclib\Math\BigInteger\Engines\PHP;
+
+/**
+ * PHP Modular Exponentiation Engine
+ *
+ * @package PHP
+ * @author Jim Wigginton
+ * @access public
+ */
+abstract class Base extends PHP
+{
+ /**#@+
+ * @access private
+ */
+ /**
+ * Cache constants
+ *
+ * $cache[self::VARIABLE] tells us whether or not the cached data is still valid.
+ */
+ const VARIABLE = 0;
+ /**
+ * $cache[self::DATA] contains the cached data.
+ */
+ const DATA = 1;
+ /**#@-*/
+
+ /**
+ * Test for engine validity
+ *
+ * @return bool
+ */
+ public static function isValidEngine()
+ {
+ return static::class != __CLASS__;
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * The most naive approach to modular exponentiation has very unreasonable requirements, and
+ * and although the approach involving repeated squaring does vastly better, it, too, is impractical
+ * for our purposes. The reason being that division - by far the most complicated and time-consuming
+ * of the basic operations (eg. +,-,*,/) - occurs multiple times within it.
+ *
+ * Modular reductions resolve this issue. Although an individual modular reduction takes more time
+ * then an individual division, when performed in succession (with the same modulo), they're a lot faster.
+ *
+ * The two most commonly used modular reductions are Barrett and Montgomery reduction. Montgomery reduction,
+ * although faster, only works when the gcd of the modulo and of the base being used is 1. In RSA, when the
+ * base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because
+ * the product of two odd numbers is odd), but what about when RSA isn't used?
+ *
+ * In contrast, Barrett reduction has no such constraint. As such, some bigint implementations perform a
+ * Barrett reduction after every operation in the modpow function. Others perform Barrett reductions when the
+ * modulo is even and Montgomery reductions when the modulo is odd. BigInteger.java's modPow method, however,
+ * uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and
+ * the other, a power of two - and recombine them, later. This is the method that this modPow function uses.
+ * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates.
+ *
+ * @param \phpseclib\Math\BigInteger\Engines\PHP $x
+ * @param \phpseclib\Math\BigInteger\Engines\PHP $e
+ * @param \phpseclib\Math\BigInteger\Engines\PHP $n
+ * @param string $class
+ * @return \phpseclib\Math\BigInteger\Engines\PHP
+ */
+ protected static function powModHelper(PHP $x, PHP $e, PHP $n, $class)
+ {
+ if (empty($e->value)) {
+ $temp = new $class();
+ $temp->value = [1];
+ return $x->normalize($temp);
+ }
+
+ if ($e->value == [1]) {
+ list(, $temp) = $x->divide($n);
+ return $x->normalize($temp);
+ }
+
+ if ($e->value == [2]) {
+ $temp = new $class;
+ $temp->value = $class::square($x->value);
+ list(, $temp) = $temp->divide($n);
+ return $x->normalize($temp);
+ }
+
+ return $x->normalize(static::slidingWindow($x, $e, $n, $class));
+ }
+
+ /**
+ * Modular reduction preperation
+ *
+ * @param array $x
+ * @param array $n
+ * @param string $class
+ * @see self::slidingWindow()
+ * @return array
+ */
+ protected static function prepareReduce(array $x, array $n, $class)
+ {
+ return static::reduce($x, $n, $class);
+ }
+
+ /**
+ * Modular multiply
+ *
+ * @param array $x
+ * @param array $y
+ * @param array $n
+ * @param string $class
+ * @see self::slidingWindow()
+ * @return array
+ */
+ protected static function multiplyReduce(array $x, array $y, array $n, $class)
+ {
+ $temp = $class::multiplyHelper($x, false, $y, false);
+ return static::reduce($temp[self::VALUE], $n, $class);
+ }
+
+ /**
+ * Modular square
+ *
+ * @param array $x
+ * @param array $n
+ * @param string $class
+ * @see self::slidingWindow()
+ * @return array
+ */
+ protected static function squareReduce(array $x, array $n, $class)
+ {
+ return static::reduce($class::square($x), $n, $class);
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/PHP/DefaultEngine.php b/phpseclib/Math/BigInteger/Engines/PHP/DefaultEngine.php
new file mode 100644
index 00000000..ad316610
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/PHP/DefaultEngine.php
@@ -0,0 +1,29 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines\PHP;
+
+use phpseclib\Math\BigInteger\Engines\PHP\Reductions\EvalBarrett;
+
+/**
+ * PHP Default Modular Exponentiation Engine
+ *
+ * @package PHP
+ * @author Jim Wigginton
+ * @access public
+ */
+abstract class DefaultEngine extends EvalBarrett
+{
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/PHP/Montgomery.php b/phpseclib/Math/BigInteger/Engines/PHP/Montgomery.php
new file mode 100644
index 00000000..8bffb1ca
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/PHP/Montgomery.php
@@ -0,0 +1,93 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines\PHP;
+
+use phpseclib\Math\BigInteger\Engines\PHP\Reductions\PowerOfTwo;
+use phpseclib\Math\BigInteger\Engines\PHP;
+use phpseclib\Math\BigInteger\Engines\PHP\Base;
+use phpseclib\Math\BigInteger\Engines\Engine;
+
+/**
+ * PHP Montgomery Modular Exponentiation Engine
+ *
+ * @package PHP
+ * @author Jim Wigginton
+ * @access public
+ */
+abstract class Montgomery extends Base
+{
+ /**
+ * Test for engine validity
+ *
+ * @return bool
+ */
+ public static function isValidEngine()
+ {
+ return static::class != __CLASS__;
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @param \phpseclib\Math\BigInteger\Engine $x
+ * @param \phpseclib\Math\BigInteger\Engine $e
+ * @param \phpseclib\Math\BigInteger\Engine $n
+ * @param string $class
+ * @return \phpseclib\Math\BigInteger\Engine
+ */
+ protected static function slidingWindow(Engine $x, Engine $e, Engine $n, $class)
+ {
+ // is the modulo odd?
+ if ($n->value[0] & 1) {
+ return parent::slidingWindow($x, $e, $n, $class);
+ }
+ // if it's not, it's even
+
+ // find the lowest set bit (eg. the max pow of 2 that divides $n)
+ for ($i = 0; $i < count($n->value); ++$i) {
+ if ($n->value[$i]) {
+ $temp = decbin($n->value[$i]);
+ $j = strlen($temp) - strrpos($temp, '1') - 1;
+ $j+= $class::BASE * $i;
+ break;
+ }
+ }
+ // at this point, 2^$j * $n/(2^$j) == $n
+
+ $mod1 = clone $n;
+ $mod1->rshift($j);
+ $mod2 = new $class();
+ $mod2->value = [1];
+ $mod2->lshift($j);
+
+ $part1 = $mod1->value != [1] ? parent::slidingWindow($x, $e, $mod1, $class) : new $class();
+ $part2 = PowerOfTwo::slidingWindow($x, $e, $mod2, $class);
+
+ $y1 = $mod2->modInverse($mod1);
+ $y2 = $mod1->modInverse($mod2);
+
+ $result = $part1->multiply($mod2);
+ $result = $result->multiply($y1);
+
+ $temp = $part2->multiply($mod1);
+ $temp = $temp->multiply($y2);
+
+ $result = $result->add($temp);
+ list(, $result) = $result->divide($n);
+
+ return $result;
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/PHP/OpenSSL.php b/phpseclib/Math/BigInteger/Engines/PHP/OpenSSL.php
new file mode 100644
index 00000000..bb5481fe
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/PHP/OpenSSL.php
@@ -0,0 +1,29 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines\PHP;
+
+use phpseclib\Math\BigInteger\Engines\OpenSSL as Progenitor;
+
+/**
+ * OpenSSL Modular Exponentiation Engine
+ *
+ * @package PHP
+ * @author Jim Wigginton
+ * @access public
+ */
+abstract class OpenSSL extends Progenitor
+{
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Barrett.php b/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Barrett.php
new file mode 100644
index 00000000..a8326c0c
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Barrett.php
@@ -0,0 +1,284 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines\PHP\Reductions;
+
+use phpseclib\Math\BigInteger\Engines\PHP\Base;
+
+/**
+ * PHP Barrett Modular Exponentiation Engine
+ *
+ * @package PHP
+ * @author Jim Wigginton
+ * @access public
+ */
+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 array $n
+ * @param array $m
+ * @param string $class
+ * @return array
+ */
+ protected static function reduce(array $n, array $m, $class)
+ {
+ 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;
+ list(, $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;
+
+ list($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.
+ *
+ * @param array $x
+ * @param array $n
+ * @param string $class
+ * @return array
+ */
+ private static function regularBarrett(array $x, array $n, $class)
+ {
+ 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;
+ list(, $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;
+ list($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);
+ // $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()
+ * @param array $x_value
+ * @param bool $x_negative
+ * @param array $y_value
+ * @param bool $y_negative
+ * @param int $stop
+ * @param string $class
+ * @return array
+ */
+ private static function multiplyLower(array $x_value, $x_negative, array $y_value, $y_negative, $stop, $class)
+ {
+ $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
+ ];
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Classic.php b/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Classic.php
new file mode 100644
index 00000000..67273f80
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Classic.php
@@ -0,0 +1,46 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines\PHP\Reductions;
+
+use phpseclib\Math\BigInteger\Engines\PHP\Base;
+
+/**
+ * PHP Classic Modular Exponentiation Engine
+ *
+ * @package PHP
+ * @author Jim Wigginton
+ * @access public
+ */
+abstract class Classic extends Base
+{
+ /**
+ * Regular Division
+ *
+ * @param array $x
+ * @param array $n
+ * @param string $class
+ * @return array
+ */
+ protected static function reduce(array $x, array $n, $class)
+ {
+ $lhs = new $class();
+ $lhs->value = $x;
+ $rhs = new $class();
+ $rhs->value = $n;
+ list(, $temp) = $lhs->divide($rhs);
+ return $temp->value;
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php b/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php
new file mode 100644
index 00000000..f34018f3
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/PHP/Reductions/EvalBarrett.php
@@ -0,0 +1,471 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines\PHP\Reductions;
+
+use phpseclib\Math\BigInteger\Engines\PHP\Base;
+use phpseclib\Math\BigInteger\Engines\PHP;
+
+/**
+ * PHP Dynamic Barrett Modular Exponentiation Engine
+ *
+ * @package PHP
+ * @author Jim Wigginton
+ * @access public
+ */
+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.
+ *
+ * @param array $n
+ * @param array $m
+ * @param string $class
+ * @return array
+ */
+ protected static function reduce(array $n, array $m, $class)
+ {
+ $inline = self::$custom_reduction;
+ return $inline($n);
+ }
+
+ /**
+ * Generate Custom Reduction
+ *
+ * @param PHP $m
+ * @param string $class
+ * @return callable
+ */
+ protected static function generateCustomReduction(PHP $m, $class)
+ {
+ $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::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;
+ }
+
+ $lhs = new $class();
+ $lhs_value = &$lhs->value;
+ $lhs_value = self::array_repeat(0, $m_length + ($m_length >> 1));
+ $lhs_value[] = 1;
+ $rhs = new $class();
+
+ list($u, $m1) = $lhs->divide($m);
+
+ $m = $m->value;
+ $u = $u->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::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.= '
+ $temp = array_slice($temp2, ' . ((count($m) >> 1) + 1) . ');';
+ $code.= self::generateInlineMultiply('temp', $m, 'temp2', $class);
+ $code.= self::generateInlineTrim('temp2');
+
+ $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;
+
+ //self::$custom_reduction = \Closure::bind($func, $m, $class);
+ }
+
+ /**
+ * Inline Trim
+ *
+ * Removes leading zeros
+ *
+ * @param string $name
+ * @return string
+ */
+ private static function generateInlineTrim($name)
+ {
+ return '
+ for ($i = count($' . $name . ') - 1; $i >= 0; --$i) {
+ if ($' . $name . '[$i]) {
+ break;
+ }
+ unset($' . $name . '[$i]);
+ }';
+ }
+
+ /**
+ * Inline Multiply (unknown, known)
+ *
+ * @param string $input
+ * @param array $arr
+ * @param string $output
+ * @param string $class
+ * @return string
+ */
+ private static function generateInlineMultiply($input, array $arr, $output, $class)
+ {
+ if (!count($arr)) {
+ return 'return [];';
+ }
+
+ $label = 'label_' . uniqid();
+
+ $regular = '
+ $length = count($' . $input . ');
+ if (!$length) {
+ $' . $output . ' = [];
+ goto ' . $label . ';
+ }
+ $' . $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.= '}';
+
+ $regular.= $label . ':';
+
+ //if (count($arr) < 2 * self::KARATSUBA_CUTOFF) {
+ //}
+
+ return $regular;
+ }
+
+ /**
+ * Inline Addition
+ *
+ * @param string $x
+ * @param string $y
+ * @param string $result
+ * @param string $class
+ * @return string
+ */
+ private static function generateInlineAdd($x, $y, $result, $class)
+ {
+ $code = '
+ $length = max(count($' . $x . '), count($' . $y . '));
+ $' . $result . ' = array_pad($' . $x . ', $length, 0);
+ $_' . $y . ' = array_pad($' . $y . ', $length, 0);
+ $carry = 0;
+ for ($i = 0, $j = 1; $j < $length; $i+=2, $j+=2) {
+ $sum = $' . $result . '[$j] * ' . $class::BASE_FULL . ' + $' . $result . '[$i] +
+ $_' . $y . '[$j] * ' . $class::BASE_FULL . ' + $_' . $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::MAX_DIGIT2) . ';
+ $' . $result . '[$i] = $carry ? $sum - ' . self::float2string($class::MAX_DIGIT2) . ' : $sum;
+ }
+ if ($carry) {
+ $' . $result . '[] = $carry;
+ }';
+ $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.
+ *
+ * @param string $known
+ * @param string $unknown
+ * @param string $result
+ * @param string $class
+ * @return string
+ */
+ private static function generateInlineSubtract2($known, $unknown, $result, $class)
+ {
+ $code = '
+ $' . $result .' = $' . $known . ';
+ $carry = 0;
+ $size = count($' . $unknown . ');
+ for ($i = 0, $j = 1; $j < $size; $i+= 2, $j+= 2) {
+ $sum = $' . $known . '[$j] * ' . $class::BASE_FULL . ' + $' . $known . '[$i]
+ - $' . $unknown . '[$j] * ' . $class::BASE_FULL . ' - $' . $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) {
+ $' . $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.
+ *
+ * @param string $unknown
+ * @param array $known
+ * @param string $result
+ * @param string $class
+ * @return string
+ */
+ private static function generateInlineSubtract1($unknown, array $known, $result, $class)
+ {
+ $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 $uknown >= $known then loop
+ *
+ * @param array $known
+ * @param string $unknown
+ * @param string $subcode
+ * @return string
+ */
+ private static function generateInlineCompare(array $known, $unknown, $subcode)
+ {
+ $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 string $x
+ * @param string $y
+ * @param string $result
+ * @param string $class
+ * @return string
+ */
+ private static function float2string($num)
+ {
+ if (!is_float($num)) {
+ return $num;
+ }
+
+ if ($num < 0) {
+ return '-' . self::float2string(abs($num));
+ }
+
+ $temp = '';
+ while ($num) {
+ $temp = fmod($num, 10) . $temp;
+ $num = floor($num / 10);
+ }
+
+ return $temp;
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Montgomery.php b/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Montgomery.php
new file mode 100644
index 00000000..2fe8ccca
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/PHP/Reductions/Montgomery.php
@@ -0,0 +1,130 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines\PHP\Reductions;
+
+use phpseclib\Math\BigInteger\Engines\PHP\Montgomery as Progenitor;
+
+/**
+ * PHP Montgomery Modular Exponentiation Engine
+ *
+ * @package PHP
+ * @author Jim Wigginton
+ * @access public
+ */
+abstract class Montgomery extends Progenitor
+{
+ /**
+ * Prepare a number for use in Montgomery Modular Reductions
+ *
+ * @param array $x
+ * @param array $n
+ * @param string $class
+ * @return array
+ */
+ protected static function prepareReduce(array $x, array $n, $class)
+ {
+ $lhs = new $class();
+ $lhs->value = array_merge(self::array_repeat(0, count($n)), $x);
+ $rhs = new $class();
+ $rhs->value = $n;
+
+ list(, $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}
+ *
+ * @param array $x
+ * @param array $n
+ * @param string $class
+ * @return array
+ */
+ protected static function reduce(array $x, array $n, $class)
+ {
+ 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!
+ *
+ * @param array $x
+ * @param string $class
+ * @return int
+ */
+ protected static function modInverse67108864(array $x, $class) // 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;
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/PHP/Reductions/MontgomeryMult.php b/phpseclib/Math/BigInteger/Engines/PHP/Reductions/MontgomeryMult.php
new file mode 100644
index 00000000..45604c2b
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/PHP/Reductions/MontgomeryMult.php
@@ -0,0 +1,81 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines\PHP\Reductions;
+
+use phpseclib\Math\BigInteger\Engines\PHP\Base;
+
+/**
+ * PHP Montgomery Modular Exponentiation Engine with interleaved multiplication
+ *
+ * @package PHP
+ * @author Jim Wigginton
+ * @access public
+ */
+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}
+ *
+ * @see self::_prepMontgomery()
+ * @see self::_montgomery()
+ * @access private
+ * @param array $x
+ * @param array $y
+ * @param array $m
+ * @param string $class
+ * @return array
+ */
+ public static function multiplyReduce(array $x, array $y, array $m, $class)
+ {
+ // 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];
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/PHP/Reductions/PowerOfTwo.php b/phpseclib/Math/BigInteger/Engines/PHP/Reductions/PowerOfTwo.php
new file mode 100644
index 00000000..3c5a1d1c
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/PHP/Reductions/PowerOfTwo.php
@@ -0,0 +1,63 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines\PHP\Reductions;
+
+use phpseclib\Math\BigInteger\Engines\PHP\Base;
+
+/**
+ * PHP Power Of Two Modular Exponentiation Engine
+ *
+ * @package PHP
+ * @author Jim Wigginton
+ * @access public
+ */
+abstract class PowerOfTwo extends Base
+{
+ /**
+ * Prepare a number for use in Montgomery Modular Reductions
+ *
+ * @param array $x
+ * @param array $n
+ * @param string $class
+ * @return array
+ */
+ protected static function prepareReduce(array $x, array $n, $class)
+ {
+ return self::reduce($x, $n, $class);
+ }
+
+ /**
+ * Power Of Two Reduction
+ *
+ * @param array $x
+ * @param array $n
+ * @param string $class
+ * @return array
+ */
+ protected static function reduce(array $x, array $n, $class)
+ {
+ $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;
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/PHP32.php b/phpseclib/Math/BigInteger/Engines/PHP32.php
new file mode 100644
index 00000000..b205a9c1
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/PHP32.php
@@ -0,0 +1,343 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines;
+
+use ParagonIE\ConstantTime\Hex;
+
+/**
+ * Pure-PHP 32-bit Engine.
+ *
+ * Uses 64-bit floats if int size is 4 bits
+ *
+ * @package PHP32
+ * @author Jim Wigginton
+ * @access public
+ */
+class PHP32 extends PHP
+{
+ /**#@+
+ * Constants used by PHP.php
+ */
+ const BASE = 26;
+ const BASE_FULL = 0x4000000;
+ const MAX_DIGIT = 0x3FFFFFF;
+ const MSB = 0x2000000;
+
+ /**
+ * MAX10 in greatest MAX10LEN satisfying
+ * MAX10 = 10**MAX10LEN <= 2**BASE.
+ */
+ const MAX10 = 10000000;
+
+ /**
+ * MAX10LEN in greatest MAX10LEN satisfying
+ * MAX10 = 10**MAX10LEN <= 2**BASE.
+ */
+ const MAX10LEN = 7;
+ const MAX_DIGIT2 = 4503599627370496;
+ /**#@-*/
+
+ /**
+ * Modular Exponentiation Engine
+ *
+ * @var string
+ */
+ protected static $modexpEngine;
+
+ /**
+ * Engine Validity Flag
+ *
+ * @var bool
+ */
+ protected static $isValidEngine;
+
+ /**
+ * Primes > 2 and < 1000
+ *
+ * @var array
+ */
+ protected static $primes;
+
+ /**
+ * BigInteger(0)
+ *
+ * @var \phpseclib\Math\BigInteger\Engines\PHP32
+ */
+ protected static $zero;
+
+ /**
+ * BigInteger(1)
+ *
+ * @var \phpseclib\Math\BigInteger\Engines\PHP32
+ */
+ protected static $one;
+
+ /**
+ * BigInteger(2)
+ *
+ * @var \phpseclib\Math\BigInteger\Engines\PHP32
+ */
+ protected static $two;
+
+ /**
+ * Test for engine validity
+ *
+ * @see parent::__construct()
+ * @return bool
+ */
+ public static function isValidEngine()
+ {
+ return PHP_INT_SIZE >= 4;
+ }
+
+ /**
+ * Adds two BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP32
+ */
+ public function add(PHP32 $y)
+ {
+ $temp = self::addHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
+
+ return $this->convertToObj($temp);
+ }
+
+ /**
+ * Subtracts two BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP32
+ */
+ public function subtract(PHP32 $y)
+ {
+ $temp = self::subtractHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
+
+ return $this->convertToObj($temp);
+ }
+
+ /**
+ * Multiplies two BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP32
+ */
+ public function multiply(PHP32 $y)
+ {
+ $temp = self::multiplyHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
+
+ return $this->convertToObj($temp);
+ }
+
+ /**
+ * Divides two BigIntegers.
+ *
+ * Returns an array whose first element contains the quotient and whose second element contains the
+ * "common residue". If the remainder would be positive, the "common residue" and the remainder are the
+ * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder
+ * and the divisor (basically, the "common residue" is the first positive modulo).
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP32
+ */
+ public function divide(PHP32 $y)
+ {
+ return $this->divideHelper($y);
+ }
+
+ /**
+ * Calculates modular inverses.
+ *
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
+ */
+ public function modInverse(PHP32 $n)
+ {
+ return $this->modInverseHelper($n);
+ }
+
+ /**
+ * Calculates modular inverses.
+ *
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
+ */
+ public function extendedGCD(PHP32 $n)
+ {
+ return $this->extendedGCDHelper($n);
+ }
+
+ /**
+ * Calculates the greatest common divisor
+ *
+ * Say you have 693 and 609. The GCD is 21.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP32
+ */
+ public function gcd(PHP32 $n)
+ {
+ extract($this->extendedGCD($n));
+ return $gcd;
+ }
+
+ /**
+ * Logical And
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP32
+ */
+ public function bitwise_and(PHP32 $x)
+ {
+ return $this->bitwiseAndHelper($x);
+ }
+
+ /**
+ * Logical Or
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP32
+ */
+ public function bitwise_or(PHP32 $x)
+ {
+ return $this->bitwiseOrHelper($x);
+ }
+
+ /**
+ * Logical Exlusive Or
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP32
+ */
+ public function bitwise_xor(PHP32 $x)
+ {
+ return $this->bitwiseXorHelper($x);
+ }
+
+ /**
+ * Compares two numbers.
+ *
+ * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is
+ * demonstrated thusly:
+ *
+ * $x > $y: $x->compare($y) > 0
+ * $x < $y: $x->compare($y) < 0
+ * $x == $y: $x->compare($y) == 0
+ *
+ * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y).
+ *
+ * @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
+ * @access public
+ * @see self::equals()
+ * @internal Could return $this->subtract($x), but that's not as fast as what we do do.
+ */
+ public function compare(PHP32 $y)
+ {
+ return $this->compareHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
+ }
+
+ /**
+ * Tests the equality of two numbers.
+ *
+ * If you need to see if one number is greater than or less than another number, use BigInteger::compare()
+ *
+ * @return bool
+ */
+ public function equals(PHP32 $x)
+ {
+ return $this->value === $x->value && $this->is_negative == $x->is_negative;
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP32
+ */
+ public function modPow(PHP32 $e, PHP32 $n)
+ {
+ return $this->powModOuter($e, $n);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * Alias for modPow().
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP32
+ */
+ public function powMod(PHP32 $e, PHP32 $n)
+ {
+ return $this->powModOuter($e, $n);
+ }
+
+ /**
+ * Generate a random prime number between a range
+ *
+ * If there's not a prime within the given range, false will be returned.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP32|false
+ */
+ public static function randomRangePrime(PHP32 $min, PHP32 $max)
+ {
+ return self::randomRangePrimeOuter($min, $max);
+ }
+
+ /**
+ * Generate a random number between a range
+ *
+ * Returns a random number between $min and $max where $min and $max
+ * can be defined using one of the two methods:
+ *
+ * BigInteger::randomRange($min, $max)
+ * BigInteger::randomRange($max, $min)
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\Engine\PHP32
+ */
+ public static function randomRange(PHP32 $min, PHP32 $max)
+ {
+ return self::randomRangeHelper($min, $max);
+ }
+
+ /**
+ * Performs exponentiation.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP64
+ */
+ public function pow(PHP32 $n)
+ {
+ return $this->powHelper($n);
+ }
+
+ /**
+ * Return the minimum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP32
+ */
+ public static function min(PHP32 ...$nums)
+ {
+ return self::minHelper($nums);
+ }
+
+ /**
+ * Return the maximum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP32
+ */
+ public static function max(PHP32 ...$nums)
+ {
+ return self::maxHelper($nums);
+ }
+
+ /**
+ * Tests BigInteger to see if it is between two integers, inclusive
+ *
+ * @return boolean
+ */
+ public function between(PHP32 $min, PHP32 $max)
+ {
+ return $this->compare($min) >= 0 && $this->compare($max) <= 0;
+ }
+}
\ No newline at end of file
diff --git a/phpseclib/Math/BigInteger/Engines/PHP64.php b/phpseclib/Math/BigInteger/Engines/PHP64.php
new file mode 100644
index 00000000..c3f7e84d
--- /dev/null
+++ b/phpseclib/Math/BigInteger/Engines/PHP64.php
@@ -0,0 +1,343 @@
+
+ * @copyright 2017 Jim Wigginton
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ * @link http://pear.php.net/package/Math_BigInteger
+ */
+
+namespace phpseclib\Math\BigInteger\Engines;
+
+use ParagonIE\ConstantTime\Hex;
+
+/**
+ * Pure-PHP 64-bit Engine.
+ *
+ * Uses 64-bit integers if int size is 8 bits
+ *
+ * @package PHP
+ * @author Jim Wigginton
+ * @access public
+ */
+class PHP64 extends PHP
+{
+ /**#@+
+ * Constants used by PHP.php
+ */
+ const BASE = 31;
+ const BASE_FULL = 0x80000000;
+ const MAX_DIGIT = 0x7FFFFFFF;
+ const MSB = 0x40000000;
+
+ /**
+ * MAX10 in greatest MAX10LEN satisfying
+ * MAX10 = 10**MAX10LEN <= 2**BASE.
+ */
+ const MAX10 = 1000000000;
+
+ /**
+ * MAX10LEN in greatest MAX10LEN satisfying
+ * MAX10 = 10**MAX10LEN <= 2**BASE.
+ */
+ const MAX10LEN = 9;
+ const MAX_DIGIT2 = 4611686018427387904;
+ /**#@-*/
+
+ /**
+ * Modular Exponentiation Engine
+ *
+ * @var string
+ */
+ protected static $modexpEngine;
+
+ /**
+ * Engine Validity Flag
+ *
+ * @var bool
+ */
+ protected static $isValidEngine;
+
+ /**
+ * Primes > 2 and < 1000
+ *
+ * @var array
+ */
+ protected static $primes;
+
+ /**
+ * BigInteger(0)
+ *
+ * @var \phpseclib\Math\BigInteger\Engines\PHP64
+ */
+ protected static $zero;
+
+ /**
+ * BigInteger(1)
+ *
+ * @var \phpseclib\Math\BigInteger\Engines\PHP64
+ */
+ protected static $one;
+
+ /**
+ * BigInteger(2)
+ *
+ * @var \phpseclib\Math\BigInteger\Engines\PHP64
+ */
+ protected static $two;
+
+ /**
+ * Test for engine validity
+ *
+ * @see parent::__construct()
+ * @return bool
+ */
+ public static function isValidEngine()
+ {
+ return PHP_INT_SIZE >= 8;
+ }
+
+ /**
+ * Adds two BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP64
+ */
+ public function add(PHP64 $y)
+ {
+ $temp = self::addHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
+
+ return $this->convertToObj($temp);
+ }
+
+ /**
+ * Subtracts two BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP64
+ */
+ public function subtract(PHP64 $y)
+ {
+ $temp = self::subtractHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
+
+ return $this->convertToObj($temp);
+ }
+
+ /**
+ * Multiplies two BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP64
+ */
+ public function multiply(PHP64 $y)
+ {
+ $temp = self::multiplyHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
+
+ return $this->convertToObj($temp);
+ }
+
+ /**
+ * Divides two BigIntegers.
+ *
+ * Returns an array whose first element contains the quotient and whose second element contains the
+ * "common residue". If the remainder would be positive, the "common residue" and the remainder are the
+ * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder
+ * and the divisor (basically, the "common residue" is the first positive modulo).
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP64
+ */
+ public function divide(PHP64 $y)
+ {
+ return $this->divideHelper($y);
+ }
+
+ /**
+ * Calculates modular inverses.
+ *
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
+ */
+ public function modInverse(PHP64 $n)
+ {
+ return $this->modInverseHelper($n);
+ }
+
+ /**
+ * Calculates modular inverses.
+ *
+ * Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
+ */
+ public function extendedGCD(PHP64 $n)
+ {
+ return $this->extendedGCDHelper($n);
+ }
+
+ /**
+ * Calculates the greatest common divisor
+ *
+ * Say you have 693 and 609. The GCD is 21.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP64
+ */
+ public function gcd(PHP64 $n)
+ {
+ extract($this->extendedGCD($n));
+ return $gcd;
+ }
+
+ /**
+ * Logical And
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP64
+ */
+ public function bitwise_and(PHP64 $x)
+ {
+ return $this->bitwiseAndHelper($x);
+ }
+
+ /**
+ * Logical Or
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP64
+ */
+ public function bitwise_or(PHP64 $x)
+ {
+ return $this->bitwiseOrHelper($x);
+ }
+
+ /**
+ * Logical Exlusive Or
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP64
+ */
+ public function bitwise_xor(PHP64 $x)
+ {
+ return $this->bitwiseXorHelper($x);
+ }
+
+ /**
+ * Compares two numbers.
+ *
+ * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is
+ * demonstrated thusly:
+ *
+ * $x > $y: $x->compare($y) > 0
+ * $x < $y: $x->compare($y) < 0
+ * $x == $y: $x->compare($y) == 0
+ *
+ * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y).
+ *
+ * @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
+ * @access public
+ * @see self::equals()
+ * @internal Could return $this->subtract($x), but that's not as fast as what we do do.
+ */
+ public function compare(PHP64 $y)
+ {
+ return parent::compareHelper($this->value, $this->is_negative, $y->value, $y->is_negative);
+ }
+
+ /**
+ * Tests the equality of two numbers.
+ *
+ * If you need to see if one number is greater than or less than another number, use BigInteger::compare()
+ *
+ * @return bool
+ */
+ public function equals(PHP64 $x)
+ {
+ return $this->value === $x->value && $this->is_negative == $x->is_negative;
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP64
+ */
+ public function modPow(PHP64 $e, PHP64 $n)
+ {
+ return $this->powModOuter($e, $n);
+ }
+
+ /**
+ * Performs modular exponentiation.
+ *
+ * Alias for modPow().
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP64
+ */
+ public function powMod(PHP64 $e, PHP64 $n)
+ {
+ return $this->powModOuter($e, $n);
+ }
+
+ /**
+ * Generate a random prime number between a range
+ *
+ * If there's not a prime within the given range, false will be returned.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP64|false
+ */
+ public static function randomRangePrime(PHP64 $min, PHP64 $max)
+ {
+ return self::randomRangePrimeOuter($min, $max);
+ }
+
+ /**
+ * Generate a random number between a range
+ *
+ * Returns a random number between $min and $max where $min and $max
+ * can be defined using one of the two methods:
+ *
+ * BigInteger::randomRange($min, $max)
+ * BigInteger::randomRange($max, $min)
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\Engine\PHP64
+ */
+ public static function randomRange(PHP64 $min, PHP64 $max)
+ {
+ return self::randomRangeHelper($min, $max);
+ }
+
+ /**
+ * Performs exponentiation.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP64
+ */
+ public function pow(PHP64 $n)
+ {
+ return $this->powHelper($n);
+ }
+
+ /**
+ * Return the minimum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP64
+ */
+ public static function min(PHP64 ...$nums)
+ {
+ return self::minHelper($nums);
+ }
+
+ /**
+ * Return the maximum BigInteger between an arbitrary number of BigIntegers.
+ *
+ * @return \phpseclib\Math\BigInteger\Engines\PHP64
+ */
+ public static function max(PHP64 ...$nums)
+ {
+ return self::maxHelper($nums);
+ }
+
+ /**
+ * Tests BigInteger to see if it is between two integers, inclusive
+ *
+ * @return boolean
+ */
+ public function between(PHP64 $min, PHP64 $max)
+ {
+ return $this->compare($min) >= 0 && $this->compare($max) <= 0;
+ }
+}
\ No newline at end of file
diff --git a/tests/PhpseclibFunctionalTestCase.php b/tests/PhpseclibFunctionalTestCase.php
index ba646edb..5e784833 100644
--- a/tests/PhpseclibFunctionalTestCase.php
+++ b/tests/PhpseclibFunctionalTestCase.php
@@ -10,29 +10,6 @@ use phpseclib\Math\BigInteger;
abstract class PhpseclibFunctionalTestCase extends PhpseclibTestCase
{
- public static function setUpBeforeClass()
- {
- if (extension_loaded('runkit')) {
- if (extension_loaded('gmp')) {
- self::ensureConstant(
- 'MATH_BIGINTEGER_MODE',
- BigInteger::MODE_GMP
- );
- } elseif (extension_loaded('bcmath')) {
- self::ensureConstant(
- 'MATH_BIGINTEGER_MODE',
- BigInteger::MODE_BCMATH
- );
- } else {
- self::markTestSkipped(
- 'Should have gmp or bcmath extension for functional test.'
- );
- }
- self::reRequireFile('Math/BigInteger.php');
- }
- parent::setUpBeforeClass();
- }
-
/**
* @param string $variable
* @param string|null $message
diff --git a/tests/Unit/Math/BigInteger/BCMathTest.php b/tests/Unit/Math/BigInteger/BCMathTest.php
index 691d4031..4e5edba3 100644
--- a/tests/Unit/Math/BigInteger/BCMathTest.php
+++ b/tests/Unit/Math/BigInteger/BCMathTest.php
@@ -5,16 +5,25 @@
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
+use \phpseclib\Math\BigInteger\Engines\BCMath;
+
class Unit_Math_BigInteger_BCMathTest extends Unit_Math_BigInteger_TestCase
{
public static function setUpBeforeClass()
{
- if (!extension_loaded('bcmath')) {
+ if (!BCMath::isValidEngine()) {
self::markTestSkipped('BCMath extension is not available.');
}
+ BCMath::setModExpEngine('DefaultEngine');
+ }
- parent::setUpBeforeClass();
+ public function getInstance($x = 0, $base = 10)
+ {
+ return new BCMath($x, $base);
+ }
- self::ensureConstant('MATH_BIGINTEGER_MODE', \phpseclib\Math\BigInteger::MODE_BCMATH);
+ public static function getStaticClass()
+ {
+ return 'phpseclib\Math\BigInteger\Engines\BCMath';
}
}
diff --git a/tests/Unit/Math/BigInteger/DefaultTest.php b/tests/Unit/Math/BigInteger/DefaultTest.php
new file mode 100644
index 00000000..63e2c06f
--- /dev/null
+++ b/tests/Unit/Math/BigInteger/DefaultTest.php
@@ -0,0 +1,21 @@
+
+ * @copyright 2013 Andreas Fischer
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ */
+
+use \phpseclib\Math\BigInteger;
+
+class Unit_Math_BigInteger_DefaultTest extends Unit_Math_BigInteger_TestCase
+{
+ public function getInstance($x = 0, $base = 10)
+ {
+ return new BigInteger($x, $base);
+ }
+
+ public static function getStaticClass()
+ {
+ return 'phpseclib\Math\BigInteger';
+ }
+}
diff --git a/tests/Unit/Math/BigInteger/GMPTest.php b/tests/Unit/Math/BigInteger/GMPTest.php
index f344a4e8..f4b902af 100644
--- a/tests/Unit/Math/BigInteger/GMPTest.php
+++ b/tests/Unit/Math/BigInteger/GMPTest.php
@@ -5,16 +5,25 @@
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
+use \phpseclib\Math\BigInteger\Engines\GMP;
+
class Unit_Math_BigInteger_GMPTest extends Unit_Math_BigInteger_TestCase
{
public static function setUpBeforeClass()
{
- if (!extension_loaded('gmp')) {
+ if (!GMP::isValidEngine()) {
self::markTestSkipped('GNU Multiple Precision (GMP) extension is not available.');
}
+ GMP::setModExpEngine('DefaultEngine');
+ }
- parent::setUpBeforeClass();
+ public function getInstance($x = 0, $base = 10)
+ {
+ return new GMP($x, $base);
+ }
- self::ensureConstant('MATH_BIGINTEGER_MODE', \phpseclib\Math\BigInteger::MODE_GMP);
+ public static function getStaticClass()
+ {
+ return 'phpseclib\Math\BigInteger\Engines\GMP';
}
}
diff --git a/tests/Unit/Math/BigInteger/InternalTest.php b/tests/Unit/Math/BigInteger/InternalTest.php
deleted file mode 100644
index fbc5fa77..00000000
--- a/tests/Unit/Math/BigInteger/InternalTest.php
+++ /dev/null
@@ -1,25 +0,0 @@
-
- * @copyright 2013 Andreas Fischer
- * @license http://www.opensource.org/licenses/mit-license.html MIT License
- */
-
-class Unit_Math_BigInteger_InternalTest extends Unit_Math_BigInteger_TestCase
-{
- public static function setUpBeforeClass()
- {
- parent::setUpBeforeClass();
-
- self::ensureConstant('MATH_BIGINTEGER_MODE', \phpseclib\Math\BigInteger::MODE_INTERNAL);
- self::ensureConstant('MATH_BIGINTEGER_OPENSSL_DISABLE', true);
- }
-
- public function testInternalRepresentation()
- {
- $x = new \phpseclib\Math\BigInteger('FFFFFFFFFFFFFFFFC90FDA', 16);
- $y = new \phpseclib\Math\BigInteger("$x");
-
- $this->assertSame(self::getVar($x, 'value'), self::getVar($y, 'value'));
- }
-}
diff --git a/tests/Unit/Math/BigInteger/PHP32Test.php b/tests/Unit/Math/BigInteger/PHP32Test.php
new file mode 100644
index 00000000..205d2b8b
--- /dev/null
+++ b/tests/Unit/Math/BigInteger/PHP32Test.php
@@ -0,0 +1,37 @@
+
+ * @copyright 2013 Andreas Fischer
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ */
+
+use \phpseclib\Math\BigInteger\Engines\PHP32;
+
+class Unit_Math_BigInteger_PHP32Test extends Unit_Math_BigInteger_TestCase
+{
+ public static function setUpBeforeClass()
+ {
+ if (version_compare(PHP_VERSION, '7.0.0') < 0) {
+ self::markTestSkipped('32-bit integers slow things down too much on PHP 5.6');
+ }
+ PHP32::setModExpEngine('DefaultEngine');
+ }
+
+ public function getInstance($x = 0, $base = 10)
+ {
+ return new PHP32($x, $base);
+ }
+
+ public function testInternalRepresentation()
+ {
+ $x = new PHP32('FFFFFFFFFFFFFFFFC90FDA', 16);
+ $y = new PHP32("$x");
+
+ $this->assertSame(self::getVar($x, 'value'), self::getVar($y, 'value'));
+ }
+
+ public static function getStaticClass()
+ {
+ return 'phpseclib\Math\BigInteger\Engines\PHP32';
+ }
+}
diff --git a/tests/Unit/Math/BigInteger/InternalOpenSSLTest.php b/tests/Unit/Math/BigInteger/PHP64OpenSSLTest.php
similarity index 61%
rename from tests/Unit/Math/BigInteger/InternalOpenSSLTest.php
rename to tests/Unit/Math/BigInteger/PHP64OpenSSLTest.php
index d5056b87..8c87bd49 100644
--- a/tests/Unit/Math/BigInteger/InternalOpenSSLTest.php
+++ b/tests/Unit/Math/BigInteger/PHP64OpenSSLTest.php
@@ -5,16 +5,18 @@
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
-class Unit_Math_BigInteger_InternalOpenSSLTest extends Unit_Math_BigInteger_TestCase
+use \phpseclib\Math\BigInteger\Engines\PHP64;
+
+class Unit_Math_BigInteger_PHP64OpenSSLTest extends Unit_Math_BigInteger_PHP64Test
{
public static function setUpBeforeClass()
{
- if (!extension_loaded('openssl')) {
- self::markTestSkipped('openssl_public_encrypt() function is not available.');
- }
-
parent::setUpBeforeClass();
- self::ensureConstant('MATH_BIGINTEGER_MODE', \phpseclib\Math\BigInteger::MODE_INTERNAL);
+ try {
+ PHP64::setModExpEngine('OpenSSL');
+ } catch (BadConfigurationException $e) {
+ self::markTestSkipped('openssl_public_encrypt() function is not available.');
+ }
}
}
diff --git a/tests/Unit/Math/BigInteger/PHP64Test.php b/tests/Unit/Math/BigInteger/PHP64Test.php
new file mode 100644
index 00000000..007111db
--- /dev/null
+++ b/tests/Unit/Math/BigInteger/PHP64Test.php
@@ -0,0 +1,37 @@
+
+ * @copyright 2013 Andreas Fischer
+ * @license http://www.opensource.org/licenses/mit-license.html MIT License
+ */
+
+use \phpseclib\Math\BigInteger\Engines\PHP64;
+
+class Unit_Math_BigInteger_PHP64Test extends Unit_Math_BigInteger_TestCase
+{
+ public static function setUpBeforeClass()
+ {
+ if (!PHP64::isValidEngine()) {
+ self::markTestSkipped('64-bit integers are not available.');
+ }
+ PHP64::setModExpEngine('DefaultEngine');
+ }
+
+ public function getInstance($x = 0, $base = 10)
+ {
+ return new PHP64($x, $base);
+ }
+
+ public function testInternalRepresentation()
+ {
+ $x = new PHP64('FFFFFFFFFFFFFFFFC90FDA', 16);
+ $y = new PHP64("$x");
+
+ $this->assertSame(self::getVar($x, 'value'), self::getVar($y, 'value'));
+ }
+
+ public static function getStaticClass()
+ {
+ return 'phpseclib\Math\BigInteger\Engines\PHP64';
+ }
+}
diff --git a/tests/Unit/Math/BigInteger/TestCase.php b/tests/Unit/Math/BigInteger/TestCase.php
index a966f143..c0ac5464 100644
--- a/tests/Unit/Math/BigInteger/TestCase.php
+++ b/tests/Unit/Math/BigInteger/TestCase.php
@@ -7,17 +7,6 @@
abstract class Unit_Math_BigInteger_TestCase extends PhpseclibTestCase
{
- public static function setUpBeforeClass()
- {
- parent::setUpBeforeClass();
- self::reRequireFile('Math/BigInteger.php');
- }
-
- public function getInstance($x = 0, $base = 10)
- {
- return new \phpseclib\Math\BigInteger($x, $base);
- }
-
public function testConstructorBase2()
{
// 2**65 = 36893488147419103232
@@ -273,7 +262,8 @@ abstract class Unit_Math_BigInteger_TestCase extends PhpseclibTestCase
$min = $this->getInstance(0);
$max = $this->getInstance('18446744073709551616');
- $rand1 = \phpseclib\Math\BigInteger::randomRange($min, $max);
+ $class = static::getStaticClass();
+ $rand1 = $class::randomRange($min, $max);
// technically $rand1 can equal $min but with the $min and $max we've
// chosen it's just not that likely
$this->assertTrue($rand1->compare($min) > 0);
@@ -285,14 +275,6 @@ abstract class Unit_Math_BigInteger_TestCase extends PhpseclibTestCase
*/
public function testDiffieHellmanKeyAgreement()
{
- if (getenv('TRAVIS') && PHP_VERSION === '5.3.3'
- && MATH_BIGINTEGER_MODE === \phpseclib\Math\BigInteger::MODE_INTERNAL
- ) {
- $this->markTestIncomplete(
- 'This test hangs on PHP 5.3.3 using internal mode.'
- );
- }
-
// "Oakley Group 14" 2048-bit modular exponentiation group as used in
// SSH2 diffie-hellman-group14-sha1
$prime = $this->getInstance(
@@ -313,10 +295,11 @@ abstract class Unit_Math_BigInteger_TestCase extends PhpseclibTestCase
/*
Code for generation of $alicePrivate and $bobPrivate.
+ $class = static::getStaticClass();
$one = $this->getInstance(1);
$max = $one->bitwise_leftShift(512)->subtract($one);
- $alicePrivate = \phpseclib\Math\BigInteger::randomRange($one, $max);
- $bobPrivate = \phpseclib\Math\BigInteger::randomRange($one, $max);
+ $alicePrivate = $static::randomRange($one, $max);
+ $bobPrivate = $static::randomRange($one, $max);
var_dump($alicePrivate->toHex(), $bobPrivate->toHex());
*/
@@ -382,19 +365,18 @@ abstract class Unit_Math_BigInteger_TestCase extends PhpseclibTestCase
}
public function testRoot()
{
- $bigInteger = new \phpseclib\Math\BigInteger('64000000'); // (20^2)^3
- $three = new \phpseclib\Math\BigInteger('3');
+ $bigInteger = $this->getInstance('64000000'); // (20^2)^3
$bigInteger = $bigInteger->root();
$this->assertSame('8000', (string) $bigInteger);
- $bigInteger = $bigInteger->root($three);
+ $bigInteger = $bigInteger->root(3);
$this->assertSame('20', (string) $bigInteger);
}
public function testPow()
{
- $bigInteger = new \phpseclib\Math\BigInteger('20');
- $two = new \phpseclib\Math\BigInteger('2');
- $three = new \phpseclib\Math\BigInteger('3');
+ $bigInteger = $this->getInstance('20');
+ $two = $this->getInstance('2');
+ $three = $this->getInstance('3');
$bigInteger = $bigInteger->pow($two);
$this->assertSame('400', (string) $bigInteger);
$bigInteger = $bigInteger->pow($three);
@@ -403,17 +385,19 @@ abstract class Unit_Math_BigInteger_TestCase extends PhpseclibTestCase
public function testMax()
{
- $min = new \phpseclib\Math\BigInteger('20');
- $max = new \phpseclib\Math\BigInteger('20000');
- $this->assertSame((string) $max, (string) \phpseclib\Math\BigInteger::max($min, $max));
- $this->assertSame((string) $max, (string) \phpseclib\Math\BigInteger::max($max, $min));
+ $class = static::getStaticClass();
+ $min = $this->getInstance('20');
+ $max = $this->getInstance('20000');
+ $this->assertSame((string) $max, (string) $class::max($min, $max));
+ $this->assertSame((string) $max, (string) $class::max($max, $min));
}
public function testMin()
{
- $min = new \phpseclib\Math\BigInteger('20');
- $max = new \phpseclib\Math\BigInteger('20000');
- $this->assertSame((string) $min, (string) \phpseclib\Math\BigInteger::min($min, $max));
- $this->assertSame((string) $min, (string) \phpseclib\Math\BigInteger::min($max, $min));
+ $class = static::getStaticClass();
+ $min = $this->getInstance('20');
+ $max = $this->getInstance('20000');
+ $this->assertSame((string) $min, (string) $class::min($min, $max));
+ $this->assertSame((string) $min, (string) $class::min($max, $min));
}
}