/**
  * @see AbstractPublicKey::verify($message, $signature)
  */
 public function verify($message, $signature)
 {
     $params = DSAKeyPair::$KEYSIZES[$this->keysize];
     $hash_alg = $params["hashAlg"];
     $hexlength = $params["q_bitlength"] / 4;
     // we pre-pad with 0s because encoding may have gotten rid of some
     $signature = Utils::hex_lpad(bin2hex($signature), $hexlength * 2);
     // now this should only happen if the signature was longer
     if (strlen($signature) != $hexlength * 2) {
         throw new \Exception("problem with r/s combo: " . sizeof($signature) . "/" . $hexlength . " - " . $signature);
     }
     $r = new MathBigInteger(substr($signature, 0, $hexlength), 16);
     $s = new MathBigInteger(substr($signature, $hexlength, $hexlength), 16);
     // check rangeconstraints
     if ($r->compare(DSAKeyPair::$zero) < 0 || $r->compare($this->key_q) > 0) {
         throw new \Exception("problem with r: " . $r->toString());
     }
     if ($s->compare(DSAKeyPair::$zero) < 0 || $s->compare($this->key_q) > 0) {
         throw new \Exception("problem with s: " . $r->toString());
     }
     return CryptDSA::verify($message, $hash_alg, $r, $s, $this->key_p, $this->key_q, $this->key_g, $this->key_y);
 }
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 CryptRSA::createKey() as the third parameter for further processing.
  *
  * @access public
  * @param optional Integer $bits
  * @param optional Integer $timeout
  * @param optional MathBigInteger $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) {
         $inst = Configuration::getInstance();
         $cnfPath = Utils::path_concat($inst->get('base_path'), $inst->get('var_path'), 'openssl.cnf');
         $rsa = openssl_pkey_new(array('private_key_bits' => $bits, 'config' => $cnfPath));
         openssl_pkey_export($rsa, $privatekey, NULL, array('config' => $cnfPath));
         $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 MathBigInteger(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 MathBigInteger();
     $generator->setRandomGenerator('crypt_random');
     $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($lcm) = $lcm['top']->divide($lcm['bottom']);
         $gcd = $lcm->gcd($e);
         $i0 = 1;
     } while (!$gcd->equals($this->one));
     $d = $e->modInverse($lcm);
     $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);
 }
 /**
  * @see AbstractSecretKey::sign($message)
  */
 public function sign($message)
 {
     $params = DSAKeyPair::$KEYSIZES[$this->keysize];
     $hash_alg = $params["hashAlg"];
     $hexlength = $params["q_bitlength"] / 4;
     $keys = CryptDSA::sign($message, $hash_alg, $this->key_p, $this->key_q, $this->key_g, $this->key_x);
     $signature = Utils::hex_lpad($keys["r"]->toHex(), $hexlength) . Utils::hex_lpad($keys["s"]->toHex(), $hexlength);
     return pack("H*", $signature);
 }