Subversion Repositories oidplus

Rev

Rev 846 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /**
  4.  * GMP BigInteger Engine
  5.  *
  6.  * PHP version 5 and 7
  7.  *
  8.  * @category  Math
  9.  * @package   BigInteger
  10.  * @author    Jim Wigginton <terrafrost@php.net>
  11.  * @copyright 2017 Jim Wigginton
  12.  * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  13.  * @link      http://pear.php.net/package/Math_BigInteger
  14.  */
  15.  
  16. namespace phpseclib3\Math\BigInteger\Engines;
  17.  
  18. use phpseclib3\Exception\BadConfigurationException;
  19.  
  20. /**
  21.  * GMP Engine.
  22.  *
  23.  * @package GMP
  24.  * @author  Jim Wigginton <terrafrost@php.net>
  25.  * @access  public
  26.  */
  27. class GMP extends Engine
  28. {
  29.     /**
  30.      * Can Bitwise operations be done fast?
  31.      *
  32.      * @see parent::bitwise_leftRotate()
  33.      * @see parent::bitwise_rightRotate()
  34.      * @access protected
  35.      */
  36.     const FAST_BITWISE = true;
  37.  
  38.     /**
  39.      * Engine Directory
  40.      *
  41.      * @see parent::setModExpEngine
  42.      * @access protected
  43.      */
  44.     const ENGINE_DIR = 'GMP';
  45.  
  46.     /**
  47.      * Test for engine validity
  48.      *
  49.      * @return bool
  50.      * @see parent::__construct()
  51.      */
  52.     public static function isValidEngine()
  53.     {
  54.         return extension_loaded('gmp');
  55.     }
  56.  
  57.     /**
  58.      * Default constructor
  59.      *
  60.      * @param mixed $x integer Base-10 number or base-$base number if $base set.
  61.      * @param int $base
  62.      * @see parent::__construct()
  63.      */
  64.     public function __construct($x = 0, $base = 10)
  65.     {
  66.         if (!isset(static::$isValidEngine[static::class])) {
  67.             static::$isValidEngine[static::class] = self::isValidEngine();
  68.         }
  69.         if (!static::$isValidEngine[static::class]) {
  70.             throw new BadConfigurationException('GMP is not setup correctly on this system');
  71.         }
  72.  
  73.         if ($x instanceof \GMP) {
  74.             $this->value = $x;
  75.             return;
  76.         }
  77.  
  78.         $this->value = gmp_init(0);
  79.  
  80.         parent::__construct($x, $base);
  81.     }
  82.  
  83.     /**
  84.      * Initialize a GMP BigInteger Engine instance
  85.      *
  86.      * @param int $base
  87.      * @see parent::__construct()
  88.      */
  89.     protected function initialize($base)
  90.     {
  91.         switch (abs($base)) {
  92.             case 256:
  93.                 $this->value = gmp_import($this->value);
  94.                 if ($this->is_negative) {
  95.                     $this->value = -$this->value;
  96.                 }
  97.                 break;
  98.             case 16:
  99.                 $temp = $this->is_negative ? '-0x' . $this->value : '0x' . $this->value;
  100.                 $this->value = gmp_init($temp);
  101.                 break;
  102.             case 10:
  103.                 $this->value = gmp_init(isset($this->value) ? $this->value : '0');
  104.         }
  105.     }
  106.  
  107.     /**
  108.      * Converts a BigInteger to a base-10 number.
  109.      *
  110.      * @return string
  111.      */
  112.     public function toString()
  113.     {
  114.         return (string)$this->value;
  115.     }
  116.  
  117.     /**
  118.      * Converts a BigInteger to a bit string (eg. base-2).
  119.      *
  120.      * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
  121.      * saved as two's compliment.
  122.      *
  123.      * @param bool $twos_compliment
  124.      * @return string
  125.      */
  126.     public function toBits($twos_compliment = false)
  127.     {
  128.         $hex = $this->toHex($twos_compliment);
  129.  
  130.         $bits = gmp_strval(gmp_init($hex, 16), 2);
  131.  
  132.         if ($this->precision > 0) {
  133.             $bits = substr($bits, -$this->precision);
  134.         }
  135.  
  136.         if ($twos_compliment && $this->compare(new static()) > 0 && $this->precision <= 0) {
  137.             return '0' . $bits;
  138.         }
  139.  
  140.         return $bits;
  141.     }
  142.  
  143.     /**
  144.      * Converts a BigInteger to a byte string (eg. base-256).
  145.      *
  146.      * @param bool $twos_compliment
  147.      * @return string
  148.      */
  149.     public function toBytes($twos_compliment = false)
  150.     {
  151.         if ($twos_compliment) {
  152.             return $this->toBytesHelper();
  153.         }
  154.  
  155.         if (gmp_cmp($this->value, gmp_init(0)) == 0) {
  156.             return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
  157.         }
  158.  
  159.         $temp = gmp_export($this->value);
  160.  
  161.         return $this->precision > 0 ?
  162.             substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
  163.             ltrim($temp, chr(0));
  164.     }
  165.  
  166.     /**
  167.      * Adds two BigIntegers.
  168.      *
  169.      * @param GMP $y
  170.      * @return GMP
  171.      */
  172.     public function add(GMP $y)
  173.     {
  174.         $temp = new self();
  175.         $temp->value = $this->value + $y->value;
  176.  
  177.         return $this->normalize($temp);
  178.     }
  179.  
  180.     /**
  181.      * Subtracts two BigIntegers.
  182.      *
  183.      * @param GMP $y
  184.      * @return GMP
  185.      */
  186.     public function subtract(GMP $y)
  187.     {
  188.         $temp = new self();
  189.         $temp->value = $this->value - $y->value;
  190.  
  191.         return $this->normalize($temp);
  192.     }
  193.  
  194.     /**
  195.      * Multiplies two BigIntegers.
  196.      *
  197.      * @param GMP $x
  198.      * @return GMP
  199.      */
  200.     public function multiply(GMP $x)
  201.     {
  202.         $temp = new self();
  203.         $temp->value = $this->value * $x->value;
  204.  
  205.         return $this->normalize($temp);
  206.     }
  207.  
  208.     /**
  209.      * Divides two BigIntegers.
  210.      *
  211.      * Returns an array whose first element contains the quotient and whose second element contains the
  212.      * "common residue".  If the remainder would be positive, the "common residue" and the remainder are the
  213.      * same.  If the remainder would be negative, the "common residue" is equal to the sum of the remainder
  214.      * and the divisor (basically, the "common residue" is the first positive modulo).
  215.      *
  216.      * @param GMP $y
  217.      * @return array{GMP, GMP}
  218.      */
  219.     public function divide(GMP $y)
  220.     {
  221.         $quotient = new self();
  222.         $remainder = new self();
  223.  
  224.         list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value);
  225.  
  226.         if (gmp_sign($remainder->value) < 0) {
  227.             $remainder->value = $remainder->value + gmp_abs($y->value);
  228.         }
  229.  
  230.         return [$this->normalize($quotient), $this->normalize($remainder)];
  231.     }
  232.  
  233.     /**
  234.      * Compares two numbers.
  235.      *
  236.      * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite.  The reason for this
  237.      * is demonstrated thusly:
  238.      *
  239.      * $x  > $y: $x->compare($y)  > 0
  240.      * $x  < $y: $x->compare($y)  < 0
  241.      * $x == $y: $x->compare($y) == 0
  242.      *
  243.      * Note how the same comparison operator is used.  If you want to test for equality, use $x->equals($y).
  244.      *
  245.      * {@internal Could return $this->subtract($x), but that's not as fast as what we do do.}
  246.      *
  247.      * @param GMP $y
  248.      * @return int in case < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
  249.      * @access public
  250.      * @see self::equals()
  251.      */
  252.     public function compare(GMP $y)
  253.     {
  254.         $r = gmp_cmp($this->value, $y->value);
  255.         if ($r < -1) {
  256.             $r = -1;
  257.         }
  258.         if ($r > 1) {
  259.             $r = 1;
  260.         }
  261.         return $r;
  262.     }
  263.  
  264.     /**
  265.      * Tests the equality of two numbers.
  266.      *
  267.      * If you need to see if one number is greater than or less than another number, use BigInteger::compare()
  268.      *
  269.      * @param GMP $x
  270.      * @return bool
  271.      */
  272.     public function equals(GMP $x)
  273.     {
  274.         return $this->value == $x->value;
  275.     }
  276.  
  277.     /**
  278.      * Calculates modular inverses.
  279.      *
  280.      * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
  281.      *
  282.      * @param GMP $n
  283.      * @return false|GMP
  284.      */
  285.     public function modInverse(GMP $n)
  286.     {
  287.         $temp = new self();
  288.         $temp->value = gmp_invert($this->value, $n->value);
  289.  
  290.         return $temp->value === false ? false : $this->normalize($temp);
  291.     }
  292.  
  293.     /**
  294.      * Calculates the greatest common divisor and Bezout's identity.
  295.      *
  296.      * Say you have 693 and 609.  The GCD is 21.  Bezout's identity states that there exist integers x and y such that
  297.      * 693*x + 609*y == 21.  In point of fact, there are actually an infinite number of x and y combinations and which
  298.      * combination is returned is dependent upon which mode is in use.  See
  299.      * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information.
  300.      *
  301.      * @param GMP $n
  302.      * @return GMP[]
  303.      */
  304.     public function extendedGCD(GMP $n)
  305.     {
  306.         extract(gmp_gcdext($this->value, $n->value));
  307.  
  308.         return [
  309.             'gcd' => $this->normalize(new self($g)),
  310.             'x' => $this->normalize(new self($s)),
  311.             'y' => $this->normalize(new self($t))
  312.         ];
  313.     }
  314.  
  315.     /**
  316.      * Calculates the greatest common divisor
  317.      *
  318.      * Say you have 693 and 609.  The GCD is 21.
  319.      *
  320.      * @param GMP $n
  321.      * @return GMP
  322.      */
  323.     public function gcd(GMP $n)
  324.     {
  325.         $r = gmp_gcd($this->value, $n->value);
  326.         return $this->normalize(new self($r));
  327.     }
  328.  
  329.     /**
  330.      * Absolute value.
  331.      *
  332.      * @return GMP
  333.      * @access public
  334.      */
  335.     public function abs()
  336.     {
  337.         $temp = new self();
  338.         $temp->value = gmp_abs($this->value);
  339.  
  340.         return $temp;
  341.     }
  342.  
  343.     /**
  344.      * Logical And
  345.      *
  346.      * @param GMP $x
  347.      * @return GMP
  348.      */
  349.     public function bitwise_and(GMP $x)
  350.     {
  351.         $temp = new self();
  352.         $temp->value = $this->value & $x->value;
  353.  
  354.         return $this->normalize($temp);
  355.     }
  356.  
  357.     /**
  358.      * Logical Or
  359.      *
  360.      * @param GMP $x
  361.      * @return GMP
  362.      */
  363.     public function bitwise_or(GMP $x)
  364.     {
  365.         $temp = new self();
  366.         $temp->value = $this->value | $x->value;
  367.  
  368.         return $this->normalize($temp);
  369.     }
  370.  
  371.     /**
  372.      * Logical Exclusive Or
  373.      *
  374.      * @param GMP $x
  375.      * @return GMP
  376.      */
  377.     public function bitwise_xor(GMP $x)
  378.     {
  379.         $temp = new self();
  380.         $temp->value = $this->value ^ $x->value;
  381.  
  382.         return $this->normalize($temp);
  383.     }
  384.  
  385.     /**
  386.      * Logical Right Shift
  387.      *
  388.      * Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
  389.      *
  390.      * @param int $shift
  391.      * @return GMP
  392.      */
  393.     public function bitwise_rightShift($shift)
  394.     {
  395.         // 0xFFFFFFFF >> 2 == -1 (on 32-bit systems)
  396.         // gmp_init('0xFFFFFFFF') >> 2 == gmp_init('0x3FFFFFFF')
  397.  
  398.         $temp = new self();
  399.         $temp->value = $this->value >> $shift;
  400.  
  401.         return $this->normalize($temp);
  402.     }
  403.  
  404.     /**
  405.      * Logical Left Shift
  406.      *
  407.      * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
  408.      *
  409.      * @param int $shift
  410.      * @return GMP
  411.      */
  412.     public function bitwise_leftShift($shift)
  413.     {
  414.         $temp = new self();
  415.         $temp->value = $this->value << $shift;
  416.  
  417.         return $this->normalize($temp);
  418.     }
  419.  
  420.     /**
  421.      * Performs modular exponentiation.
  422.      *
  423.      * @param GMP $e
  424.      * @param GMP $n
  425.      * @return GMP
  426.      */
  427.     public function modPow(GMP $e, GMP $n)
  428.     {
  429.         return $this->powModOuter($e, $n);
  430.     }
  431.  
  432.     /**
  433.      * Performs modular exponentiation.
  434.      *
  435.      * Alias for modPow().
  436.      *
  437.      * @param GMP $e
  438.      * @param GMP $n
  439.      * @return GMP
  440.      */
  441.     public function powMod(GMP $e, GMP $n)
  442.     {
  443.         return $this->powModOuter($e, $n);
  444.     }
  445.  
  446.     /**
  447.      * Performs modular exponentiation.
  448.      *
  449.      * @param GMP $e
  450.      * @param GMP $n
  451.      * @return GMP
  452.      */
  453.     protected function powModInner(GMP $e, GMP $n)
  454.     {
  455.         $class = static::$modexpEngine[static::class];
  456.         return $class::powModHelper($this, $e, $n);
  457.     }
  458.  
  459.     /**
  460.      * Normalize
  461.      *
  462.      * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
  463.      *
  464.      * @param GMP $result
  465.      * @return GMP
  466.      */
  467.     protected function normalize(GMP $result)
  468.     {
  469.         $result->precision = $this->precision;
  470.         $result->bitmask = $this->bitmask;
  471.  
  472.         if ($result->bitmask !== false) {
  473.             $flip = $result->value < 0;
  474.             if ($flip) {
  475.                 $result->value = -$result->value;
  476.             }
  477.             $result->value = $result->value & $result->bitmask->value;
  478.             if ($flip) {
  479.                 $result->value = -$result->value;
  480.             }
  481.         }
  482.  
  483.         return $result;
  484.     }
  485.  
  486.     /**
  487.      * Performs some post-processing for randomRangePrime
  488.      *
  489.      * @param Engine $x
  490.      * @param Engine $min
  491.      * @param Engine $max
  492.      * @return GMP
  493.      */
  494.     protected static function randomRangePrimeInner(Engine $x, Engine $min, Engine $max)
  495.     {
  496.         $p = gmp_nextprime($x->value);
  497.  
  498.         if ($p <= $max->value) {
  499.             return new self($p);
  500.         }
  501.  
  502.         if ($min->value != $x->value) {
  503.             $x = new self($x->value - 1);
  504.         }
  505.  
  506.         return self::randomRangePrime($min, $x);
  507.     }
  508.  
  509.     /**
  510.      * Generate a random prime number between a range
  511.      *
  512.      * If there's not a prime within the given range, false will be returned.
  513.      *
  514.      * @param GMP $min
  515.      * @param GMP $max
  516.      * @return false|GMP
  517.      */
  518.     public static function randomRangePrime(GMP $min, GMP $max)
  519.     {
  520.         return self::randomRangePrimeOuter($min, $max);
  521.     }
  522.  
  523.     /**
  524.      * Generate a random number between a range
  525.      *
  526.      * Returns a random number between $min and $max where $min and $max
  527.      * can be defined using one of the two methods:
  528.      *
  529.      * BigInteger::randomRange($min, $max)
  530.      * BigInteger::randomRange($max, $min)
  531.      *
  532.      * @param GMP $min
  533.      * @param GMP $max
  534.      * @return GMP
  535.      */
  536.     public static function randomRange(GMP $min, GMP $max)
  537.     {
  538.         return self::randomRangeHelper($min, $max);
  539.     }
  540.  
  541.     /**
  542.      * Make the current number odd
  543.      *
  544.      * If the current number is odd it'll be unchanged.  If it's even, one will be added to it.
  545.      *
  546.      * @see self::randomPrime()
  547.      */
  548.     protected function make_odd()
  549.     {
  550.         gmp_setbit($this->value, 0);
  551.     }
  552.  
  553.     /**
  554.      * Tests Primality
  555.      *
  556.      * @param int $t
  557.      * @return bool
  558.      */
  559.     protected function testPrimality($t)
  560.     {
  561.         return gmp_prob_prime($this->value, $t) != 0;
  562.     }
  563.  
  564.     /**
  565.      * Calculates the nth root of a biginteger.
  566.      *
  567.      * Returns the nth root of a positive biginteger, where n defaults to 2
  568.      *
  569.      * @param int $n
  570.      * @return GMP
  571.      */
  572.     protected function rootInner($n)
  573.     {
  574.         $root = new self();
  575.         $root->value = gmp_root($this->value, $n);
  576.         return $this->normalize($root);
  577.     }
  578.  
  579.     /**
  580.      * Performs exponentiation.
  581.      *
  582.      * @param GMP $n
  583.      * @return GMP
  584.      */
  585.     public function pow(GMP $n)
  586.     {
  587.         $temp = new self();
  588.         $temp->value = $this->value ** $n->value;
  589.  
  590.         return $this->normalize($temp);
  591.     }
  592.  
  593.     /**
  594.      * Return the minimum BigInteger between an arbitrary number of BigIntegers.
  595.      *
  596.      * @param GMP ...$nums
  597.      * @return GMP
  598.      */
  599.     public static function min(GMP ...$nums)
  600.     {
  601.         return self::minHelper($nums);
  602.     }
  603.  
  604.     /**
  605.      * Return the maximum BigInteger between an arbitrary number of BigIntegers.
  606.      *
  607.      * @param GMP ...$nums
  608.      * @return GMP
  609.      */
  610.     public static function max(GMP ...$nums)
  611.     {
  612.         return self::maxHelper($nums);
  613.     }
  614.  
  615.     /**
  616.      * Tests BigInteger to see if it is between two integers, inclusive
  617.      *
  618.      * @param GMP $min
  619.      * @param GMP $max
  620.      * @return bool
  621.      */
  622.     public function between(GMP $min, GMP $max)
  623.     {
  624.         return $this->compare($min) >= 0 && $this->compare($max) <= 0;
  625.     }
  626.  
  627.     /**
  628.      * Create Recurring Modulo Function
  629.      *
  630.      * Sometimes it may be desirable to do repeated modulos with the same number outside of
  631.      * modular exponentiation
  632.      *
  633.      * @return callable
  634.      */
  635.     public function createRecurringModuloFunction()
  636.     {
  637.         $temp = $this->value;
  638.         return function (GMP $x) use ($temp) {
  639.             return new GMP($x->value % $temp);
  640.         };
  641.     }
  642.  
  643.     /**
  644.      * Scan for 1 and right shift by that amount
  645.      *
  646.      * ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
  647.      *
  648.      * @param GMP $r
  649.      * @return int
  650.      */
  651.     public static function scan1divide(GMP $r)
  652.     {
  653.         $s = gmp_scan1($r->value, 0);
  654.         $r->value >>= $s;
  655.         return $s;
  656.     }
  657.  
  658.     /**
  659.      * Is Odd?
  660.      *
  661.      * @return bool
  662.      */
  663.     public function isOdd()
  664.     {
  665.         return gmp_testbit($this->value, 0);
  666.     }
  667.  
  668.     /**
  669.      * Tests if a bit is set
  670.      *
  671.      * @return bool
  672.      */
  673.     public function testBit($x)
  674.     {
  675.         return gmp_testbit($this->value, $x);
  676.     }
  677.  
  678.     /**
  679.      * Is Negative?
  680.      *
  681.      * @return bool
  682.      */
  683.     public function isNegative()
  684.     {
  685.         return gmp_sign($this->value) == -1;
  686.     }
  687.  
  688.     /**
  689.      * Negate
  690.      *
  691.      * Given $k, returns -$k
  692.      *
  693.      * @return GMP
  694.      */
  695.     public function negate()
  696.     {
  697.         $temp = clone $this;
  698.         $temp->value = -$this->value;
  699.  
  700.         return $temp;
  701.     }
  702. }
  703.