/** * @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); }
/** * 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); }