[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger/Engines/ -> Engine.php (source)

   1  <?php
   2  
   3  /**
   4   * Base BigInteger Engine
   5   *
   6   * PHP version 5 and 7
   7   *
   8   * @author    Jim Wigginton <terrafrost@php.net>
   9   * @copyright 2017 Jim Wigginton
  10   * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  11   * @link      http://pear.php.net/package/Math_BigInteger
  12   */
  13  
  14  namespace phpseclib3\Math\BigInteger\Engines;
  15  
  16  use phpseclib3\Common\Functions\Strings;
  17  use phpseclib3\Crypt\Random;
  18  use phpseclib3\Exception\BadConfigurationException;
  19  use phpseclib3\Math\BigInteger;
  20  
  21  /**
  22   * Base Engine.
  23   *
  24   * @author  Jim Wigginton <terrafrost@php.net>
  25   */
  26  abstract class Engine implements \JsonSerializable
  27  {
  28      /* final protected */ const PRIMES = [
  29          3,   5,   7,   11,  13,  17,  19,  23,  29,  31,  37,  41,  43,  47,  53,  59,
  30          61,  67,  71,  73,  79,  83,  89,  97,  101, 103, 107, 109, 113, 127, 131, 137,
  31          139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
  32          229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
  33          317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
  34          421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
  35          521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617,
  36          619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727,
  37          733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829,
  38          839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947,
  39          953, 967, 971, 977, 983, 991, 997,
  40      ];
  41  
  42      /**
  43       * BigInteger(0)
  44       *
  45       * @var array<class-string<static>, static>
  46       */
  47      protected static $zero = [];
  48  
  49      /**
  50       * BigInteger(1)
  51       *
  52       * @var array<class-string<static>, static>
  53       */
  54      protected static $one  = [];
  55  
  56      /**
  57       * BigInteger(2)
  58       *
  59       * @var array<class-string<static>, static>
  60       */
  61      protected static $two = [];
  62  
  63      /**
  64       * Modular Exponentiation Engine
  65       *
  66       * @var array<class-string<static>, class-string<static>>
  67       */
  68      protected static $modexpEngine;
  69  
  70      /**
  71       * Engine Validity Flag
  72       *
  73       * @var array<class-string<static>, bool>
  74       */
  75      protected static $isValidEngine;
  76  
  77      /**
  78       * Holds the BigInteger's value
  79       *
  80       * @var \GMP|string|array|int
  81       */
  82      protected $value;
  83  
  84      /**
  85       * Holds the BigInteger's sign
  86       *
  87       * @var bool
  88       */
  89      protected $is_negative;
  90  
  91      /**
  92       * Precision
  93       *
  94       * @see static::setPrecision()
  95       * @var int
  96       */
  97      protected $precision = -1;
  98  
  99      /**
 100       * Precision Bitmask
 101       *
 102       * @see static::setPrecision()
 103       * @var static|false
 104       */
 105      protected $bitmask = false;
 106  
 107      /**
 108       * Recurring Modulo Function
 109       *
 110       * @var callable
 111       */
 112      protected $reduce;
 113  
 114      /**
 115       * Mode independent value used for serialization.
 116       *
 117       * @see self::__sleep()
 118       * @see self::__wakeup()
 119       * @var string
 120       */
 121      protected $hex;
 122  
 123      /**
 124       * Default constructor
 125       *
 126       * @param int|numeric-string $x integer Base-10 number or base-$base number if $base set.
 127       * @param int $base
 128       */
 129      public function __construct($x = 0, $base = 10)
 130      {
 131          if (!array_key_exists(static::class, static::$zero)) {
 132              static::$zero[static::class] = null; // Placeholder to prevent infinite loop.
 133              static::$zero[static::class] = new static(0);
 134              static::$one[static::class] = new static(1);
 135              static::$two[static::class] = new static(2);
 136          }
 137  
 138          // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48
 139          // '0' is the only value like this per http://php.net/empty
 140          if (empty($x) && (abs($base) != 256 || $x !== '0')) {
 141              return;
 142          }
 143  
 144          switch ($base) {
 145              case -256:
 146              case 256:
 147                  if ($base == -256 && (ord($x[0]) & 0x80)) {
 148                      $this->value = ~$x;
 149                      $this->is_negative = true;
 150                  } else {
 151                      $this->value = $x;
 152                      $this->is_negative = false;
 153                  }
 154  
 155                  $this->initialize($base);
 156  
 157                  if ($this->is_negative) {
 158                      $temp = $this->add(new static('-1'));
 159                      $this->value = $temp->value;
 160                  }
 161                  break;
 162              case -16:
 163              case 16:
 164                  if ($base > 0 && $x[0] == '-') {
 165                      $this->is_negative = true;
 166                      $x = substr($x, 1);
 167                  }
 168  
 169                  $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#s', '$1', $x);
 170  
 171                  $is_negative = false;
 172                  if ($base < 0 && hexdec($x[0]) >= 8) {
 173                      $this->is_negative = $is_negative = true;
 174                      $x = Strings::bin2hex(~Strings::hex2bin($x));
 175                  }
 176  
 177                  $this->value = $x;
 178                  $this->initialize($base);
 179  
 180                  if ($is_negative) {
 181                      $temp = $this->add(new static('-1'));
 182                      $this->value = $temp->value;
 183                  }
 184                  break;
 185              case -10:
 186              case 10:
 187                  // (?<!^)(?:-).*: find any -'s that aren't at the beginning and then any characters that follow that
 188                  // (?<=^|-)0*: find any 0's that are preceded by the start of the string or by a - (ie. octals)
 189                  // [^-0-9].*: find any non-numeric characters and then any characters that follow that
 190                  $this->value = preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#s', '', $x);
 191                  if (!strlen($this->value) || $this->value == '-') {
 192                      $this->value = '0';
 193                  }
 194                  $this->initialize($base);
 195                  break;
 196              case -2:
 197              case 2:
 198                  if ($base > 0 && $x[0] == '-') {
 199                      $this->is_negative = true;
 200                      $x = substr($x, 1);
 201                  }
 202  
 203                  $x = preg_replace('#^([01]*).*#s', '$1', $x);
 204  
 205                  $temp = new static(Strings::bits2bin($x), 128 * $base); // ie. either -16 or +16
 206                  $this->value = $temp->value;
 207                  if ($temp->is_negative) {
 208                      $this->is_negative = true;
 209                  }
 210  
 211                  break;
 212              default:
 213                  // base not supported, so we'll let $this == 0
 214          }
 215      }
 216  
 217      /**
 218       * Sets engine type.
 219       *
 220       * Throws an exception if the type is invalid
 221       *
 222       * @param class-string<Engine> $engine
 223       */
 224      public static function setModExpEngine($engine)
 225      {
 226          $fqengine = '\\phpseclib3\\Math\\BigInteger\\Engines\\' . static::ENGINE_DIR . '\\' . $engine;
 227          if (!class_exists($fqengine) || !method_exists($fqengine, 'isValidEngine')) {
 228              throw new \InvalidArgumentException("$engine is not a valid engine");
 229          }
 230          if (!$fqengine::isValidEngine()) {
 231              throw new BadConfigurationException("$engine is not setup correctly on this system");
 232          }
 233          static::$modexpEngine[static::class] = $fqengine;
 234      }
 235  
 236      /**
 237       * Converts a BigInteger to a byte string (eg. base-256).
 238       *
 239       * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
 240       * saved as two's compliment.
 241       * @return string
 242       */
 243      protected function toBytesHelper()
 244      {
 245          $comparison = $this->compare(new static());
 246          if ($comparison == 0) {
 247              return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
 248          }
 249  
 250          $temp = $comparison < 0 ? $this->add(new static(1)) : $this;
 251          $bytes = $temp->toBytes();
 252  
 253          if (!strlen($bytes)) { // eg. if the number we're trying to convert is -1
 254              $bytes = chr(0);
 255          }
 256  
 257          if (ord($bytes[0]) & 0x80) {
 258              $bytes = chr(0) . $bytes;
 259          }
 260  
 261          return $comparison < 0 ? ~$bytes : $bytes;
 262      }
 263  
 264      /**
 265       * Converts a BigInteger to a hex string (eg. base-16).
 266       *
 267       * @param bool $twos_compliment
 268       * @return string
 269       */
 270      public function toHex($twos_compliment = false)
 271      {
 272          return Strings::bin2hex($this->toBytes($twos_compliment));
 273      }
 274  
 275      /**
 276       * Converts a BigInteger to a bit string (eg. base-2).
 277       *
 278       * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
 279       * saved as two's compliment.
 280       *
 281       * @param bool $twos_compliment
 282       * @return string
 283       */
 284      public function toBits($twos_compliment = false)
 285      {
 286          $hex = $this->toBytes($twos_compliment);
 287          $bits = Strings::bin2bits($hex);
 288  
 289          $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');
 290  
 291          if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) {
 292              return '0' . $result;
 293          }
 294  
 295          return $result;
 296      }
 297  
 298      /**
 299       * Calculates modular inverses.
 300       *
 301       * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
 302       *
 303       * {@internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information.}
 304       *
 305       * @param Engine $n
 306       * @return static|false
 307       */
 308      protected function modInverseHelper(Engine $n)
 309      {
 310          // $x mod -$n == $x mod $n.
 311          $n = $n->abs();
 312  
 313          if ($this->compare(static::$zero[static::class]) < 0) {
 314              $temp = $this->abs();
 315              $temp = $temp->modInverse($n);
 316              return $this->normalize($n->subtract($temp));
 317          }
 318  
 319          extract($this->extendedGCD($n));
 320          /**
 321           * @var Engine $gcd
 322           * @var Engine $x
 323           */
 324  
 325          if (!$gcd->equals(static::$one[static::class])) {
 326              return false;
 327          }
 328  
 329          $x = $x->compare(static::$zero[static::class]) < 0 ? $x->add($n) : $x;
 330  
 331          return $this->compare(static::$zero[static::class]) < 0 ? $this->normalize($n->subtract($x)) : $this->normalize($x);
 332      }
 333  
 334      /**
 335       * Serialize
 336       *
 337       * Will be called, automatically, when serialize() is called on a BigInteger object.
 338       *
 339       * @return array
 340       */
 341      public function __sleep()
 342      {
 343          $this->hex = $this->toHex(true);
 344          $vars = ['hex'];
 345          if ($this->precision > 0) {
 346              $vars[] = 'precision';
 347          }
 348          return $vars;
 349      }
 350  
 351      /**
 352       * Serialize
 353       *
 354       * Will be called, automatically, when unserialize() is called on a BigInteger object.
 355       *
 356       * @return void
 357       */
 358      public function __wakeup()
 359      {
 360          $temp = new static($this->hex, -16);
 361          $this->value = $temp->value;
 362          $this->is_negative = $temp->is_negative;
 363          if ($this->precision > 0) {
 364              // recalculate $this->bitmask
 365              $this->setPrecision($this->precision);
 366          }
 367      }
 368  
 369      /**
 370       * JSON Serialize
 371       *
 372       * Will be called, automatically, when json_encode() is called on a BigInteger object.
 373       *
 374       * @return array{hex: string, precision?: int]
 375       */
 376      #[\ReturnTypeWillChange]
 377      public function jsonSerialize()
 378      {
 379          $result = ['hex' => $this->toHex(true)];
 380          if ($this->precision > 0) {
 381              $result['precision'] = $this->precision;
 382          }
 383          return $result;
 384      }
 385  
 386      /**
 387       * Converts a BigInteger to a base-10 number.
 388       *
 389       * @return string
 390       */
 391      public function __toString()
 392      {
 393          return $this->toString();
 394      }
 395  
 396      /**
 397       *  __debugInfo() magic method
 398       *
 399       * Will be called, automatically, when print_r() or var_dump() are called
 400       *
 401       * @return array
 402       */
 403      public function __debugInfo()
 404      {
 405          $result = [
 406              'value' => '0x' . $this->toHex(true),
 407              'engine' => basename(static::class)
 408          ];
 409          return $this->precision > 0 ? $result + ['precision' => $this->precision] : $result;
 410      }
 411  
 412      /**
 413       * Set Precision
 414       *
 415       * Some bitwise operations give different results depending on the precision being used.  Examples include left
 416       * shift, not, and rotates.
 417       *
 418       * @param int $bits
 419       */
 420      public function setPrecision($bits)
 421      {
 422          if ($bits < 1) {
 423              $this->precision = -1;
 424              $this->bitmask = false;
 425  
 426              return;
 427          }
 428          $this->precision = $bits;
 429          $this->bitmask = static::setBitmask($bits);
 430  
 431          $temp = $this->normalize($this);
 432          $this->value = $temp->value;
 433      }
 434  
 435      /**
 436       * Get Precision
 437       *
 438       * Returns the precision if it exists, -1 if it doesn't
 439       *
 440       * @return int
 441       */
 442      public function getPrecision()
 443      {
 444          return $this->precision;
 445      }
 446  
 447      /**
 448       * Set Bitmask
 449       * @return static
 450       * @param int $bits
 451       * @see self::setPrecision()
 452       */
 453      protected static function setBitmask($bits)
 454      {
 455          return new static(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256);
 456      }
 457  
 458      /**
 459       * Logical Not
 460       *
 461       * @return Engine|string
 462       */
 463      public function bitwise_not()
 464      {
 465          // calculuate "not" without regard to $this->precision
 466          // (will always result in a smaller number.  ie. ~1 isn't 1111 1110 - it's 0)
 467          $temp = $this->toBytes();
 468          if ($temp == '') {
 469              return $this->normalize(static::$zero[static::class]);
 470          }
 471          $pre_msb = decbin(ord($temp[0]));
 472          $temp = ~$temp;
 473          $msb = decbin(ord($temp[0]));
 474          if (strlen($msb) == 8) {
 475              $msb = substr($msb, strpos($msb, '0'));
 476          }
 477          $temp[0] = chr(bindec($msb));
 478  
 479          // see if we need to add extra leading 1's
 480          $current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8;
 481          $new_bits = $this->precision - $current_bits;
 482          if ($new_bits <= 0) {
 483              return $this->normalize(new static($temp, 256));
 484          }
 485  
 486          // generate as many leading 1's as we need to.
 487          $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3);
 488  
 489          self::base256_lshift($leading_ones, $current_bits);
 490  
 491          $temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT);
 492  
 493          return $this->normalize(new static($leading_ones | $temp, 256));
 494      }
 495  
 496      /**
 497       * Logical Left Shift
 498       *
 499       * Shifts binary strings $shift bits, essentially multiplying by 2**$shift.
 500       *
 501       * @param string $x
 502       * @param int $shift
 503       * @return void
 504       */
 505      protected static function base256_lshift(&$x, $shift)
 506      {
 507          if ($shift == 0) {
 508              return;
 509          }
 510  
 511          $num_bytes = $shift >> 3; // eg. floor($shift/8)
 512          $shift &= 7; // eg. $shift % 8
 513  
 514          $carry = 0;
 515          for ($i = strlen($x) - 1; $i >= 0; --$i) {
 516              $temp = ord($x[$i]) << $shift | $carry;
 517              $x[$i] = chr($temp);
 518              $carry = $temp >> 8;
 519          }
 520          $carry = ($carry != 0) ? chr($carry) : '';
 521          $x = $carry . $x . str_repeat(chr(0), $num_bytes);
 522      }
 523  
 524      /**
 525       * Logical Left Rotate
 526       *
 527       * Instead of the top x bits being dropped they're appended to the shifted bit string.
 528       *
 529       * @param int $shift
 530       * @return Engine
 531       */
 532      public function bitwise_leftRotate($shift)
 533      {
 534          $bits = $this->toBytes();
 535  
 536          if ($this->precision > 0) {
 537              $precision = $this->precision;
 538              if (static::FAST_BITWISE) {
 539                  $mask = $this->bitmask->toBytes();
 540              } else {
 541                  $mask = $this->bitmask->subtract(new static(1));
 542                  $mask = $mask->toBytes();
 543              }
 544          } else {
 545              $temp = ord($bits[0]);
 546              for ($i = 0; $temp >> $i; ++$i) {
 547              }
 548              $precision = 8 * strlen($bits) - 8 + $i;
 549              $mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3);
 550          }
 551  
 552          if ($shift < 0) {
 553              $shift += $precision;
 554          }
 555          $shift %= $precision;
 556  
 557          if (!$shift) {
 558              return clone $this;
 559          }
 560  
 561          $left = $this->bitwise_leftShift($shift);
 562          $left = $left->bitwise_and(new static($mask, 256));
 563          $right = $this->bitwise_rightShift($precision - $shift);
 564          $result = static::FAST_BITWISE ? $left->bitwise_or($right) : $left->add($right);
 565          return $this->normalize($result);
 566      }
 567  
 568      /**
 569       * Logical Right Rotate
 570       *
 571       * Instead of the bottom x bits being dropped they're prepended to the shifted bit string.
 572       *
 573       * @param int $shift
 574       * @return Engine
 575       */
 576      public function bitwise_rightRotate($shift)
 577      {
 578          return $this->bitwise_leftRotate(-$shift);
 579      }
 580  
 581      /**
 582       * Returns the smallest and largest n-bit number
 583       *
 584       * @param int $bits
 585       * @return array{min: static, max: static}
 586       */
 587      public static function minMaxBits($bits)
 588      {
 589          $bytes = $bits >> 3;
 590          $min = str_repeat(chr(0), $bytes);
 591          $max = str_repeat(chr(0xFF), $bytes);
 592          $msb = $bits & 7;
 593          if ($msb) {
 594              $min = chr(1 << ($msb - 1)) . $min;
 595              $max = chr((1 << $msb) - 1) . $max;
 596          } else {
 597              $min[0] = chr(0x80);
 598          }
 599          return [
 600              'min' => new static($min, 256),
 601              'max' => new static($max, 256)
 602          ];
 603      }
 604  
 605      /**
 606       * Return the size of a BigInteger in bits
 607       *
 608       * @return int
 609       */
 610      public function getLength()
 611      {
 612          return strlen($this->toBits());
 613      }
 614  
 615      /**
 616       * Return the size of a BigInteger in bytes
 617       *
 618       * @return int
 619       */
 620      public function getLengthInBytes()
 621      {
 622          return (int) ceil($this->getLength() / 8);
 623      }
 624  
 625      /**
 626       * Performs some pre-processing for powMod
 627       *
 628       * @param Engine $e
 629       * @param Engine $n
 630       * @return static|false
 631       */
 632      protected function powModOuter(Engine $e, Engine $n)
 633      {
 634          $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs();
 635  
 636          if ($e->compare(new static()) < 0) {
 637              $e = $e->abs();
 638  
 639              $temp = $this->modInverse($n);
 640              if ($temp === false) {
 641                  return false;
 642              }
 643  
 644              return $this->normalize($temp->powModInner($e, $n));
 645          }
 646  
 647          if ($this->compare($n) > 0) {
 648              list(, $temp) = $this->divide($n);
 649              return $temp->powModInner($e, $n);
 650          }
 651  
 652          return $this->powModInner($e, $n);
 653      }
 654  
 655      /**
 656       * Sliding Window k-ary Modular Exponentiation
 657       *
 658       * Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} /
 659       * {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}.  In a departure from those algorithims,
 660       * however, this function performs a modular reduction after every multiplication and squaring operation.
 661       * As such, this function has the same preconditions that the reductions being used do.
 662       *
 663       * @template T of Engine
 664       * @param Engine $x
 665       * @param Engine $e
 666       * @param Engine $n
 667       * @param class-string<T> $class
 668       * @return T
 669       */
 670      protected static function slidingWindow(Engine $x, Engine $e, Engine $n, $class)
 671      {
 672          static $window_ranges = [7, 25, 81, 241, 673, 1793]; // from BigInteger.java's oddModPow function
 673          //static $window_ranges = [0, 7, 36, 140, 450, 1303, 3529]; // from MPM 7.3.1
 674  
 675          $e_bits = $e->toBits();
 676          $e_length = strlen($e_bits);
 677  
 678          // calculate the appropriate window size.
 679          // $window_size == 3 if $window_ranges is between 25 and 81, for example.
 680          for ($i = 0, $window_size = 1; $i < count($window_ranges) && $e_length > $window_ranges[$i]; ++$window_size, ++$i) {
 681          }
 682  
 683          $n_value = $n->value;
 684  
 685          if (method_exists(static::class, 'generateCustomReduction')) {
 686              static::generateCustomReduction($n, $class);
 687          }
 688  
 689          // precompute $this^0 through $this^$window_size
 690          $powers = [];
 691          $powers[1] = static::prepareReduce($x->value, $n_value, $class);
 692          $powers[2] = static::squareReduce($powers[1], $n_value, $class);
 693  
 694          // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end
 695          // in a 1.  ie. it's supposed to be odd.
 696          $temp = 1 << ($window_size - 1);
 697          for ($i = 1; $i < $temp; ++$i) {
 698              $i2 = $i << 1;
 699              $powers[$i2 + 1] = static::multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $class);
 700          }
 701  
 702          $result = new $class(1);
 703          $result = static::prepareReduce($result->value, $n_value, $class);
 704  
 705          for ($i = 0; $i < $e_length;) {
 706              if (!$e_bits[$i]) {
 707                  $result = static::squareReduce($result, $n_value, $class);
 708                  ++$i;
 709              } else {
 710                  for ($j = $window_size - 1; $j > 0; --$j) {
 711                      if (!empty($e_bits[$i + $j])) {
 712                          break;
 713                      }
 714                  }
 715  
 716                  // eg. the length of substr($e_bits, $i, $j + 1)
 717                  for ($k = 0; $k <= $j; ++$k) {
 718                      $result = static::squareReduce($result, $n_value, $class);
 719                  }
 720  
 721                  $result = static::multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $class);
 722  
 723                  $i += $j + 1;
 724              }
 725          }
 726  
 727          $temp = new $class();
 728          $temp->value = static::reduce($result, $n_value, $class);
 729  
 730          return $temp;
 731      }
 732  
 733      /**
 734       * Generates a random number of a certain size
 735       *
 736       * Bit length is equal to $size
 737       *
 738       * @param int $size
 739       * @return Engine
 740       */
 741      public static function random($size)
 742      {
 743          extract(static::minMaxBits($size));
 744          /**
 745           * @var BigInteger $min
 746           * @var BigInteger $max
 747           */
 748          return static::randomRange($min, $max);
 749      }
 750  
 751      /**
 752       * Generates a random prime number of a certain size
 753       *
 754       * Bit length is equal to $size
 755       *
 756       * @param int $size
 757       * @return Engine
 758       */
 759      public static function randomPrime($size)
 760      {
 761          extract(static::minMaxBits($size));
 762          /**
 763           * @var static $min
 764           * @var static $max
 765           */
 766          return static::randomRangePrime($min, $max);
 767      }
 768  
 769      /**
 770       * Performs some pre-processing for randomRangePrime
 771       *
 772       * @param Engine $min
 773       * @param Engine $max
 774       * @return static|false
 775       */
 776      protected static function randomRangePrimeOuter(Engine $min, Engine $max)
 777      {
 778          $compare = $max->compare($min);
 779  
 780          if (!$compare) {
 781              return $min->isPrime() ? $min : false;
 782          } elseif ($compare < 0) {
 783              // if $min is bigger then $max, swap $min and $max
 784              $temp = $max;
 785              $max = $min;
 786              $min = $temp;
 787          }
 788  
 789          $length = $max->getLength();
 790          if ($length > 8196) {
 791              throw new \RuntimeException("Generation of random prime numbers larger than 8196 has been disabled ($length)");
 792          }
 793  
 794          $x = static::randomRange($min, $max);
 795  
 796          return static::randomRangePrimeInner($x, $min, $max);
 797      }
 798  
 799      /**
 800       * Generate a random number between a range
 801       *
 802       * Returns a random number between $min and $max where $min and $max
 803       * can be defined using one of the two methods:
 804       *
 805       * BigInteger::randomRange($min, $max)
 806       * BigInteger::randomRange($max, $min)
 807       *
 808       * @param Engine $min
 809       * @param Engine $max
 810       * @return Engine
 811       */
 812      protected static function randomRangeHelper(Engine $min, Engine $max)
 813      {
 814          $compare = $max->compare($min);
 815  
 816          if (!$compare) {
 817              return $min;
 818          } elseif ($compare < 0) {
 819              // if $min is bigger then $max, swap $min and $max
 820              $temp = $max;
 821              $max = $min;
 822              $min = $temp;
 823          }
 824  
 825          if (!isset(static::$one[static::class])) {
 826              static::$one[static::class] = new static(1);
 827          }
 828  
 829          $max = $max->subtract($min->subtract(static::$one[static::class]));
 830  
 831          $size = strlen(ltrim($max->toBytes(), chr(0)));
 832  
 833          /*
 834              doing $random % $max doesn't work because some numbers will be more likely to occur than others.
 835              eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145
 836              would produce 5 whereas the only value of random that could produce 139 would be 139. ie.
 837              not all numbers would be equally likely. some would be more likely than others.
 838  
 839              creating a whole new random number until you find one that is within the range doesn't work
 840              because, for sufficiently small ranges, the likelihood that you'd get a number within that range
 841              would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability
 842              would be pretty high that $random would be greater than $max.
 843  
 844              phpseclib works around this using the technique described here:
 845  
 846              http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string
 847          */
 848          $random_max = new static(chr(1) . str_repeat("\0", $size), 256);
 849          $random = new static(Random::string($size), 256);
 850  
 851          list($max_multiple) = $random_max->divide($max);
 852          $max_multiple = $max_multiple->multiply($max);
 853  
 854          while ($random->compare($max_multiple) >= 0) {
 855              $random = $random->subtract($max_multiple);
 856              $random_max = $random_max->subtract($max_multiple);
 857              $random = $random->bitwise_leftShift(8);
 858              $random = $random->add(new static(Random::string(1), 256));
 859              $random_max = $random_max->bitwise_leftShift(8);
 860              list($max_multiple) = $random_max->divide($max);
 861              $max_multiple = $max_multiple->multiply($max);
 862          }
 863          list(, $random) = $random->divide($max);
 864  
 865          return $random->add($min);
 866      }
 867  
 868      /**
 869       * Performs some post-processing for randomRangePrime
 870       *
 871       * @param Engine $x
 872       * @param Engine $min
 873       * @param Engine $max
 874       * @return static|false
 875       */
 876      protected static function randomRangePrimeInner(Engine $x, Engine $min, Engine $max)
 877      {
 878          if (!isset(static::$two[static::class])) {
 879              static::$two[static::class] = new static('2');
 880          }
 881  
 882          $x->make_odd();
 883          if ($x->compare($max) > 0) {
 884              // if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range
 885              if ($min->equals($max)) {
 886                  return false;
 887              }
 888              $x = clone $min;
 889              $x->make_odd();
 890          }
 891  
 892          $initial_x = clone $x;
 893  
 894          while (true) {
 895              if ($x->isPrime()) {
 896                  return $x;
 897              }
 898  
 899              $x = $x->add(static::$two[static::class]);
 900  
 901              if ($x->compare($max) > 0) {
 902                  $x = clone $min;
 903                  if ($x->equals(static::$two[static::class])) {
 904                      return $x;
 905                  }
 906                  $x->make_odd();
 907              }
 908  
 909              if ($x->equals($initial_x)) {
 910                  return false;
 911              }
 912          }
 913      }
 914  
 915      /**
 916       * Sets the $t parameter for primality testing
 917       *
 918       * @return int
 919       */
 920      protected function setupIsPrime()
 921      {
 922          $length = $this->getLengthInBytes();
 923  
 924          // see HAC 4.49 "Note (controlling the error probability)"
 925          // @codingStandardsIgnoreStart
 926               if ($length >= 163) { $t =  2; } // floor(1300 / 8)
 927          else if ($length >= 106) { $t =  3; } // floor( 850 / 8)
 928          else if ($length >= 81 ) { $t =  4; } // floor( 650 / 8)
 929          else if ($length >= 68 ) { $t =  5; } // floor( 550 / 8)
 930          else if ($length >= 56 ) { $t =  6; } // floor( 450 / 8)
 931          else if ($length >= 50 ) { $t =  7; } // floor( 400 / 8)
 932          else if ($length >= 43 ) { $t =  8; } // floor( 350 / 8)
 933          else if ($length >= 37 ) { $t =  9; } // floor( 300 / 8)
 934          else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8)
 935          else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8)
 936          else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8)
 937          else                     { $t = 27; }
 938          // @codingStandardsIgnoreEnd
 939  
 940          return $t;
 941      }
 942  
 943      /**
 944       * Tests Primality
 945       *
 946       * Uses the {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}.
 947       * See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24} for more info.
 948       *
 949       * @param int $t
 950       * @return bool
 951       */
 952      protected function testPrimality($t)
 953      {
 954          if (!$this->testSmallPrimes()) {
 955              return false;
 956          }
 957  
 958          $n   = clone $this;
 959          $n_1 = $n->subtract(static::$one[static::class]);
 960          $n_2 = $n->subtract(static::$two[static::class]);
 961  
 962          $r = clone $n_1;
 963          $s = static::scan1divide($r);
 964  
 965          for ($i = 0; $i < $t; ++$i) {
 966              $a = static::randomRange(static::$two[static::class], $n_2);
 967              $y = $a->modPow($r, $n);
 968  
 969              if (!$y->equals(static::$one[static::class]) && !$y->equals($n_1)) {
 970                  for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) {
 971                      $y = $y->modPow(static::$two[static::class], $n);
 972                      if ($y->equals(static::$one[static::class])) {
 973                          return false;
 974                      }
 975                  }
 976  
 977                  if (!$y->equals($n_1)) {
 978                      return false;
 979                  }
 980              }
 981          }
 982  
 983          return true;
 984      }
 985  
 986      /**
 987       * Checks a numer to see if it's prime
 988       *
 989       * Assuming the $t parameter is not set, this function has an error rate of 2**-80.  The main motivation for the
 990       * $t parameter is distributability.  BigInteger::randomPrime() can be distributed across multiple pageloads
 991       * on a website instead of just one.
 992       *
 993       * @param int|bool $t
 994       * @return bool
 995       */
 996      public function isPrime($t = false)
 997      {
 998          // OpenSSL limits RSA keys to 16384 bits. The length of an RSA key is equal to the length of the modulo, which is
 999          // produced by multiplying the primes p and q by one another. The largest number two 8196 bit primes can produce is
1000          // a 16384 bit number so, basically, 8196 bit primes are the largest OpenSSL will generate and if that's the largest
1001          // that it'll generate it also stands to reason that that's the largest you'll be able to test primality on
1002          $length = $this->getLength();
1003          if ($length > 8196) {
1004              throw new \RuntimeException("Primality testing is not supported for numbers larger than 8196 bits ($length)");
1005          }
1006  
1007          if (!$t) {
1008              $t = $this->setupIsPrime();
1009          }
1010          return $this->testPrimality($t);
1011      }
1012  
1013      /**
1014       * Performs a few preliminary checks on root
1015       *
1016       * @param int $n
1017       * @return Engine
1018       */
1019      protected function rootHelper($n)
1020      {
1021          if ($n < 1) {
1022              return clone static::$zero[static::class];
1023          } // we want positive exponents
1024          if ($this->compare(static::$one[static::class]) < 0) {
1025              return clone static::$zero[static::class];
1026          } // we want positive numbers
1027          if ($this->compare(static::$two[static::class]) < 0) {
1028              return clone static::$one[static::class];
1029          } // n-th root of 1 or 2 is 1
1030  
1031          return $this->rootInner($n);
1032      }
1033  
1034      /**
1035       * Calculates the nth root of a biginteger.
1036       *
1037       * Returns the nth root of a positive biginteger, where n defaults to 2
1038       *
1039       * {@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}.}
1040       *
1041       * @param int $n
1042       * @return Engine
1043       */
1044      protected function rootInner($n)
1045      {
1046          $n = new static($n);
1047  
1048          // g is our guess number
1049          $g = static::$two[static::class];
1050          // while (g^n < num) g=g*2
1051          while ($g->pow($n)->compare($this) < 0) {
1052              $g = $g->multiply(static::$two[static::class]);
1053          }
1054          // if (g^n==num) num is a power of 2, we're lucky, end of job
1055          // == 0 bccomp(bcpow($g, $n), $n->value)==0
1056          if ($g->pow($n)->equals($this) > 0) {
1057              $root = $g;
1058              return $this->normalize($root);
1059          }
1060  
1061          // if we're here num wasn't a power of 2 :(
1062          $og = $g; // og means original guess and here is our upper bound
1063          $g = $g->divide(static::$two[static::class])[0]; // g is set to be our lower bound
1064          $step = $og->subtract($g)->divide(static::$two[static::class])[0]; // step is the half of upper bound - lower bound
1065          $g = $g->add($step); // we start at lower bound + step , basically in the middle of our interval
1066  
1067          // while step>1
1068  
1069          while ($step->compare(static::$one[static::class]) == 1) {
1070              $guess = $g->pow($n);
1071              $step = $step->divide(static::$two[static::class])[0];
1072              $comp = $guess->compare($this); // compare our guess with real number
1073              switch ($comp) {
1074                  case -1: // if guess is lower we add the new step
1075                      $g = $g->add($step);
1076                      break;
1077                  case 1: // if guess is higher we sub the new step
1078                      $g = $g->subtract($step);
1079                      break;
1080                  case 0: // if guess is exactly the num we're done, we return the value
1081                      $root = $g;
1082                      break 2;
1083              }
1084          }
1085  
1086          if ($comp == 1) {
1087              $g = $g->subtract($step);
1088          }
1089  
1090          // whatever happened, g is the closest guess we can make so return it
1091          $root = $g;
1092  
1093          return $this->normalize($root);
1094      }
1095  
1096      /**
1097       * Calculates the nth root of a biginteger.
1098       *
1099       * @param int $n
1100       * @return Engine
1101       */
1102      public function root($n = 2)
1103      {
1104          return $this->rootHelper($n);
1105      }
1106  
1107      /**
1108       * Return the minimum BigInteger between an arbitrary number of BigIntegers.
1109       *
1110       * @param array $nums
1111       * @return Engine
1112       */
1113      protected static function minHelper(array $nums)
1114      {
1115          if (count($nums) == 1) {
1116              return $nums[0];
1117          }
1118          $min = $nums[0];
1119          for ($i = 1; $i < count($nums); $i++) {
1120              $min = $min->compare($nums[$i]) > 0 ? $nums[$i] : $min;
1121          }
1122          return $min;
1123      }
1124  
1125      /**
1126       * Return the minimum BigInteger between an arbitrary number of BigIntegers.
1127       *
1128       * @param array $nums
1129       * @return Engine
1130       */
1131      protected static function maxHelper(array $nums)
1132      {
1133          if (count($nums) == 1) {
1134              return $nums[0];
1135          }
1136          $max = $nums[0];
1137          for ($i = 1; $i < count($nums); $i++) {
1138              $max = $max->compare($nums[$i]) < 0 ? $nums[$i] : $max;
1139          }
1140          return $max;
1141      }
1142  
1143      /**
1144       * Create Recurring Modulo Function
1145       *
1146       * Sometimes it may be desirable to do repeated modulos with the same number outside of
1147       * modular exponentiation
1148       *
1149       * @return callable
1150       */
1151      public function createRecurringModuloFunction()
1152      {
1153          $class = static::class;
1154  
1155          $fqengine = !method_exists(static::$modexpEngine[static::class], 'reduce') ?
1156              '\\phpseclib3\\Math\\BigInteger\\Engines\\' . static::ENGINE_DIR . '\\DefaultEngine' :
1157              static::$modexpEngine[static::class];
1158          if (method_exists($fqengine, 'generateCustomReduction')) {
1159              $func = $fqengine::generateCustomReduction($this, static::class);
1160              return eval('return function(' . static::class . ' $x) use ($func, $class) {
1161                  $r = new $class();
1162                  $r->value = $func($x->value);
1163                  return $r;
1164              };');
1165          }
1166          $n = $this->value;
1167          return eval('return function(' . static::class . ' $x) use ($n, $fqengine, $class) {
1168              $r = new $class();
1169              $r->value = $fqengine::reduce($x->value, $n, $class);
1170              return $r;
1171          };');
1172      }
1173  
1174      /**
1175       * Calculates the greatest common divisor and Bezout's identity.
1176       *
1177       * @param Engine $n
1178       * @return array{gcd: Engine, x: Engine, y: Engine}
1179       */
1180      protected function extendedGCDHelper(Engine $n)
1181      {
1182          $u = clone $this;
1183          $v = clone $n;
1184  
1185          $one = new static(1);
1186          $zero = new static();
1187  
1188          $a = clone $one;
1189          $b = clone $zero;
1190          $c = clone $zero;
1191          $d = clone $one;
1192  
1193          while (!$v->equals($zero)) {
1194              list($q) = $u->divide($v);
1195  
1196              $temp = $u;
1197              $u = $v;
1198              $v = $temp->subtract($v->multiply($q));
1199  
1200              $temp = $a;
1201              $a = $c;
1202              $c = $temp->subtract($a->multiply($q));
1203  
1204              $temp = $b;
1205              $b = $d;
1206              $d = $temp->subtract($b->multiply($q));
1207          }
1208  
1209          return [
1210              'gcd' => $u,
1211              'x' => $a,
1212              'y' => $b
1213          ];
1214      }
1215  
1216      /**
1217       * Bitwise Split
1218       *
1219       * Splits BigInteger's into chunks of $split bits
1220       *
1221       * @param int $split
1222       * @return Engine[]
1223       */
1224      public function bitwise_split($split)
1225      {
1226          if ($split < 1) {
1227              throw new \RuntimeException('Offset must be greater than 1');
1228          }
1229  
1230          $mask = static::$one[static::class]->bitwise_leftShift($split)->subtract(static::$one[static::class]);
1231  
1232          $num = clone $this;
1233  
1234          $vals = [];
1235          while (!$num->equals(static::$zero[static::class])) {
1236              $vals[] = $num->bitwise_and($mask);
1237              $num = $num->bitwise_rightShift($split);
1238          }
1239  
1240          return array_reverse($vals);
1241      }
1242  
1243      /**
1244       * Logical And
1245       *
1246       * @param Engine $x
1247       * @return Engine
1248       */
1249      protected function bitwiseAndHelper(Engine $x)
1250      {
1251          $left = $this->toBytes(true);
1252          $right = $x->toBytes(true);
1253  
1254          $length = max(strlen($left), strlen($right));
1255  
1256          $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
1257          $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
1258  
1259          return $this->normalize(new static($left & $right, -256));
1260      }
1261  
1262      /**
1263       * Logical Or
1264       *
1265       * @param Engine $x
1266       * @return Engine
1267       */
1268      protected function bitwiseOrHelper(Engine $x)
1269      {
1270          $left = $this->toBytes(true);
1271          $right = $x->toBytes(true);
1272  
1273          $length = max(strlen($left), strlen($right));
1274  
1275          $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
1276          $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
1277  
1278          return $this->normalize(new static($left | $right, -256));
1279      }
1280  
1281      /**
1282       * Logical Exclusive Or
1283       *
1284       * @param Engine $x
1285       * @return Engine
1286       */
1287      protected function bitwiseXorHelper(Engine $x)
1288      {
1289          $left = $this->toBytes(true);
1290          $right = $x->toBytes(true);
1291  
1292          $length = max(strlen($left), strlen($right));
1293  
1294  
1295          $left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
1296          $right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
1297          return $this->normalize(new static($left ^ $right, -256));
1298      }
1299  }