Beispiel #1
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 dependant 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:
  * <code>
  * <?php
  *    include 'Math/BigInteger.php';
  *
  *    $a = new Math_BigInteger(693);
  *    $b = new Math_BigInteger(609);
  *
  *    extract($a->extendedGCD($b));
  *
  *    echo $gcd->toString() . "\r\n"; // outputs 21
  *    echo $a->toString() * $x->toString() + $b->toString() * $y->toString(); // outputs 21
  * ?>
  * </code>
  *
  * @param Math_BigInteger $n
  * @return 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".
  */
 function extendedGCD($n)
 {
     switch (MATH_BIGINTEGER_MODE) {
         case MATH_BIGINTEGER_MODE_GMP:
             extract(gmp_gcdext($this->value, $n->value));
             return array('gcd' => $this->_normalize(new Math_BigInteger($g)), 'x' => $this->_normalize(new Math_BigInteger($s)), 'y' => $this->_normalize(new Math_BigInteger($t)));
         case MATH_BIGINTEGER_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 array('gcd' => $this->_normalize(new Math_BigInteger($u)), 'x' => $this->_normalize(new Math_BigInteger($a)), 'y' => $this->_normalize(new Math_BigInteger($b)));
     }
     $y = $n->copy();
     $x = $this->copy();
     $g = new Math_BigInteger();
     $g->value = array(1);
     while (!($x->value[0] & 1 || $y->value[0] & 1)) {
         $x->_rshift(1);
         $y->_rshift(1);
         $g->_lshift(1);
     }
     $u = $x->copy();
     $v = $y->copy();
     $a = new Math_BigInteger();
     $b = new Math_BigInteger();
     $c = new Math_BigInteger();
     $d = new Math_BigInteger();
     $a->value = $d->value = $g->value = array(1);
     $b->value = $c->value = array();
     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 array('gcd' => $this->_normalize($g->multiply($v)), 'x' => $this->_normalize($c), 'y' => $this->_normalize($d));
 }
Beispiel #2
0
 /**
  * Create public / private key pair
  *
  * Returns an array with the following three elements:
  *  - 'privatekey': The private key.
  *  - 'publickey':  The public key.
  *  - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
  *                  Will need to be passed back to Crypt_RSA::createKey() as the third parameter for further processing.
  *
  * @access public
  * @param int $bits
  * @param int $timeout
  * @param Math_BigInteger $p
  */
 function createKey($bits = 1024, $timeout = false, $partial = array())
 {
     if (!defined('CRYPT_RSA_EXPONENT')) {
         // http://en.wikipedia.org/wiki/65537_%28number%29
         define('CRYPT_RSA_EXPONENT', '65537');
     }
     // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
     // than 256 bits. as a consequence if the key you're trying to create is 1024 bits and you've set CRYPT_RSA_SMALLEST_PRIME
     // to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). at least if
     // CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_INTERNAL. if CRYPT_RSA_MODE is set to CRYPT_RSA_MODE_OPENSSL then
     // CRYPT_RSA_SMALLEST_PRIME is ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key
     // generation when there's a chance neither gmp nor OpenSSL are installed)
     if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
         define('CRYPT_RSA_SMALLEST_PRIME', 4096);
     }
     // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
     if (CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) {
         $config = array();
         if (isset($this->configFile)) {
             $config['config'] = $this->configFile;
         }
         $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config);
         openssl_pkey_export($rsa, $privatekey, null, $config);
         $publickey = openssl_pkey_get_details($rsa);
         $publickey = $publickey['key'];
         $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, CRYPT_RSA_PRIVATE_FORMAT_PKCS1)));
         $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, CRYPT_RSA_PUBLIC_FORMAT_PKCS1)));
         // clear the buffer of error strings stemming from a minimalistic openssl.cnf
         while (openssl_error_string() !== false) {
         }
         return array('privatekey' => $privatekey, 'publickey' => $publickey, 'partialkey' => false);
     }
     static $e;
     if (!isset($e)) {
         $e = new Math_BigInteger(CRYPT_RSA_EXPONENT);
     }
     extract($this->_generateMinMax($bits));
     $absoluteMin = $min;
     $temp = $bits >> 1;
     // divide by two to see how many bits P and Q would be
     if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
         $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
         $temp = CRYPT_RSA_SMALLEST_PRIME;
     } else {
         $num_primes = 2;
     }
     extract($this->_generateMinMax($temp + $bits % $temp));
     $finalMax = $max;
     extract($this->_generateMinMax($temp));
     $generator = new Math_BigInteger();
     $n = $this->one->copy();
     if (!empty($partial)) {
         extract(unserialize($partial));
     } else {
         $exponents = $coefficients = $primes = array();
         $lcm = array('top' => $this->one->copy(), 'bottom' => false);
     }
     $start = time();
     $i0 = count($primes) + 1;
     do {
         for ($i = $i0; $i <= $num_primes; $i++) {
             if ($timeout !== false) {
                 $timeout -= time() - $start;
                 $start = time();
                 if ($timeout <= 0) {
                     return array('privatekey' => '', 'publickey' => '', 'partialkey' => serialize(array('primes' => $primes, 'coefficients' => $coefficients, 'lcm' => $lcm, 'exponents' => $exponents)));
                 }
             }
             if ($i == $num_primes) {
                 list($min, $temp) = $absoluteMin->divide($n);
                 if (!$temp->equals($this->zero)) {
                     $min = $min->add($this->one);
                     // ie. ceil()
                 }
                 $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
             } else {
                 $primes[$i] = $generator->randomPrime($min, $max, $timeout);
             }
             if ($primes[$i] === false) {
                 // if we've reached the timeout
                 if (count($primes) > 1) {
                     $partialkey = '';
                 } else {
                     array_pop($primes);
                     $partialkey = serialize(array('primes' => $primes, 'coefficients' => $coefficients, 'lcm' => $lcm, 'exponents' => $exponents));
                 }
                 return array('privatekey' => '', 'publickey' => '', 'partialkey' => $partialkey);
             }
             // the first coefficient is calculated differently from the rest
             // ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
             if ($i > 2) {
                 $coefficients[$i] = $n->modInverse($primes[$i]);
             }
             $n = $n->multiply($primes[$i]);
             $temp = $primes[$i]->subtract($this->one);
             // textbook RSA implementations use Euler's totient function instead of the least common multiple.
             // see http://en.wikipedia.org/wiki/Euler%27s_totient_function
             $lcm['top'] = $lcm['top']->multiply($temp);
             $lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
             $exponents[$i] = $e->modInverse($temp);
         }
         list($temp) = $lcm['top']->divide($lcm['bottom']);
         $gcd = $temp->gcd($e);
         $i0 = 1;
     } while (!$gcd->equals($this->one));
     $d = $e->modInverse($temp);
     $coefficients[2] = $primes[2]->modInverse($primes[1]);
     // from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
     // RSAPrivateKey ::= SEQUENCE {
     //     version           Version,
     //     modulus           INTEGER,  -- n
     //     publicExponent    INTEGER,  -- e
     //     privateExponent   INTEGER,  -- d
     //     prime1            INTEGER,  -- p
     //     prime2            INTEGER,  -- q
     //     exponent1         INTEGER,  -- d mod (p-1)
     //     exponent2         INTEGER,  -- d mod (q-1)
     //     coefficient       INTEGER,  -- (inverse of q) mod p
     //     otherPrimeInfos   OtherPrimeInfos OPTIONAL
     // }
     return array('privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), 'publickey' => $this->_convertPublicKey($n, $e), 'partialkey' => false);
 }