예제 #1
0
 /**
  * Returns the server public host key.
  *
  * Caching this the first time you connect to a server and checking the result on subsequent connections
  * is recommended.  Returns false if the server signature is not signed correctly with the public host key.
  *
  * @return Mixed
  * @access public
  */
 function getServerPublicHostKey()
 {
     if (!($this->bitmap & NET_SSH2_MASK_CONSTRUCTOR)) {
         $this->bitmap |= NET_SSH2_MASK_CONSTRUCTOR;
         if (!$this->_connect()) {
             return false;
         }
     }
     $signature = $this->signature;
     $server_public_host_key = $this->server_public_host_key;
     extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4)));
     $this->_string_shift($server_public_host_key, $length);
     if ($this->signature_validated) {
         return $this->bitmap ? $this->signature_format . ' ' . base64_encode($this->server_public_host_key) : false;
     }
     $this->signature_validated = true;
     switch ($this->signature_format) {
         case 'ssh-dss':
             $zero = new Math_BigInteger();
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $p = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $q = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $g = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $y = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             /* The value for 'dss_signature_blob' is encoded as a string containing
                r, followed by s (which are 160-bit integers, without lengths or
                padding, unsigned, and in network byte order). */
             $temp = unpack('Nlength', $this->_string_shift($signature, 4));
             if ($temp['length'] != 40) {
                 user_error('Invalid signature');
                 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
             }
             $r = new Math_BigInteger($this->_string_shift($signature, 20), 256);
             $s = new Math_BigInteger($this->_string_shift($signature, 20), 256);
             switch (true) {
                 case $r->equals($zero):
                 case $r->compare($q) >= 0:
                 case $s->equals($zero):
                 case $s->compare($q) >= 0:
                     user_error('Invalid signature');
                     return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
             }
             $w = $s->modInverse($q);
             $u1 = $w->multiply(new Math_BigInteger(sha1($this->exchange_hash), 16));
             list(, $u1) = $u1->divide($q);
             $u2 = $w->multiply($r);
             list(, $u2) = $u2->divide($q);
             $g = $g->modPow($u1, $p);
             $y = $y->modPow($u2, $p);
             $v = $g->multiply($y);
             list(, $v) = $v->divide($p);
             list(, $v) = $v->divide($q);
             if (!$v->equals($r)) {
                 user_error('Bad server signature');
                 return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
             }
             break;
         case 'ssh-rsa':
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $e = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $n = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $nLength = $temp['length'];
             /*
             $temp = unpack('Nlength', $this->_string_shift($signature, 4));
             $signature = $this->_string_shift($signature, $temp['length']);
             
             if (!class_exists('Crypt_RSA')) {
                 include_once 'Crypt/RSA.php';
             }
             
             $rsa = new Crypt_RSA();
             $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
             $rsa->loadKey(array('e' => $e, 'n' => $n), CRYPT_RSA_PUBLIC_FORMAT_RAW);
             if (!$rsa->verify($this->exchange_hash, $signature)) {
                 user_error('Bad server signature');
                 return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
             }
             */
             $temp = unpack('Nlength', $this->_string_shift($signature, 4));
             $s = new Math_BigInteger($this->_string_shift($signature, $temp['length']), 256);
             // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the
             // following URL:
             // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
             // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source.
             if ($s->compare(new Math_BigInteger()) < 0 || $s->compare($n->subtract(new Math_BigInteger(1))) > 0) {
                 user_error('Invalid signature');
                 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
             }
             $s = $s->modPow($e, $n);
             $s = $s->toBytes();
             $h = pack('N4H*', 0x302130, 0x906052b, 0xe03021a, 0x5000414, sha1($this->exchange_hash));
             $h = chr(0x1) . str_repeat(chr(0xff), $nLength - 3 - strlen($h)) . $h;
             if ($s != $h) {
                 user_error('Bad server signature');
                 return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
             }
             break;
         default:
             user_error('Unsupported signature format');
             return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
     }
     return $this->signature_format . ' ' . base64_encode($this->server_public_host_key);
 }
예제 #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 optional Integer $bits
  * @param optional Integer $timeout
  * @param optional 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) {
         $rsa = openssl_pkey_new(array('private_key_bits' => $bits, 'config' => dirname(__FILE__) . '/../openssl.cnf'));
         openssl_pkey_export($rsa, $privatekey, NULL, array('config' => dirname(__FILE__) . '/../openssl.cnf'));
         $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();
     $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);
 }
예제 #3
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 optional Integer $bits
  * @param optional Integer $timeout
  * @param optional Math_BigInteger $p
  */
 function createKey($bits = 1024, $timeout = false, $partial = array())
 {
     if (CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL) {
         $rsa = openssl_pkey_new(array('private_key_bits' => $bits));
         openssl_pkey_export($rsa, $privatekey);
         $publickey = openssl_pkey_get_details($rsa);
         $publickey = $publickey['key'];
         if ($this->privateKeyFormat != CRYPT_RSA_PRIVATE_FORMAT_PKCS1) {
             $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)));
         }
         return array('privatekey' => $privatekey, 'publickey' => $publickey, 'partialkey' => false);
     }
     static $e;
     if (!isset($e)) {
         if (!defined('CRYPT_RSA_EXPONENT')) {
             // http://en.wikipedia.org/wiki/65537_%28number%29
             define('CRYPT_RSA_EXPONENT', '65537');
         }
         if (!defined('CRYPT_RSA_COMMENT')) {
             define('CRYPT_RSA_COMMENT', 'phpseclib-generated-key');
         }
         // per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
         // than 256 bits.
         if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
             define('CRYPT_RSA_SMALLEST_PRIME', 4096);
         }
         $e = new Math_BigInteger(CRYPT_RSA_EXPONENT);
     }
     extract($this->_generateMinMax($bits));
     $absoluteMin = $min;
     $temp = $bits >> 1;
     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();
     $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 serialize(array('privatekey' => '', 'publickey' => '', 'partialkey' => 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
                 return array('privatekey' => '', 'publickey' => '', 'partialkey' => empty($primes) ? '' : serialize(array('primes' => array_slice($primes, 0, $i - 1), 'coefficients' => $coefficients, 'lcm' => $lcm, 'exponents' => $exponents)));
             }
             // 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);
 }
예제 #4
0
파일: biginteger.php 프로젝트: thu0ng91/jmc
 /**
  * Calculates modular inverses.
  *
  * Here's a quick 'n dirty example:
  * <code>
  * <?php
  *    include('Math/BigInteger.php');
  *
  *    $a = new Math_BigInteger(30);
  *    $b = new Math_BigInteger(17);
  *
  *    $c = $a->modInverse($b);
  *
  *    echo $c->toString(); // outputs 4
  * ?>
  * </code>
  *
  * @param Math_BigInteger $n
  * @return mixed false, if no modular inverse exists, Math_BigInteger, otherwise.
  * @access public
  * @internal Calculates the modular inverse of $this mod $n 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".  See
  *    {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information.
  */
 function modInverse($n)
 {
     switch (MATH_BIGINTEGER_MODE) {
         case MATH_BIGINTEGER_MODE_GMP:
             $temp = new Math_BigInteger();
             $temp->value = gmp_invert($this->value, $n->value);
             return $temp->value === false ? false : $temp;
         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.
             // if $x is less than 0, the first character of $x is a '-', so we'll remove it.  we can do this because
             // $x mod $n == $x mod -$n.
             $n = bccomp($n->value, '0') < 0 ? substr($n->value, 1) : $n->value;
             if (bccomp($this->value, '0') < 0) {
                 $negated_this = new Math_BigInteger();
                 $negated_this->value = substr($this->value, 1);
                 $temp = $negated_this->modInverse(new Math_BigInteger($n));
                 if ($temp === false) {
                     return false;
                 }
                 $temp->value = bcsub($n, $temp->value);
                 return $temp;
             }
             $u = $this->value;
             $v = $n;
             $a = '1';
             $c = '0';
             while (true) {
                 $q = bcdiv($u, $v);
                 $temp = $u;
                 $u = $v;
                 $v = bcsub($temp, bcmul($v, $q));
                 if (bccomp($v, '0') == 0) {
                     break;
                 }
                 $temp = $a;
                 $a = $c;
                 $c = bcsub($temp, bcmul($c, $q));
             }
             $temp = new Math_BigInteger();
             $temp->value = bccomp($c, '0') < 0 ? bcadd($c, $n) : $c;
             // $u contains the gcd of $this and $n
             return bccomp($u, '1') == 0 ? $temp : false;
     }
     // if $this and $n are even, return false.
     if (!($this->value[0] & 1) && !($n->value[0] & 1)) {
         return false;
     }
     $n = $n->_copy();
     $n->is_negative = false;
     if ($this->compare(new Math_BigInteger()) < 0) {
         // is_negative is currently true.  since we need it to be false, we'll just set it to false, temporarily,
         // and reset it as true, later.
         $this->is_negative = false;
         $temp = $this->modInverse($n);
         if ($temp === false) {
             return false;
         }
         $temp = $n->subtract($temp);
         $this->is_negative = true;
         return $temp;
     }
     $u = $n->_copy();
     $x = $this;
     //list(, $x) = $this->divide($n);
     $v = $x->_copy();
     $a = new Math_BigInteger();
     $b = new Math_BigInteger();
     $c = new Math_BigInteger();
     $d = new Math_BigInteger();
     $a->value = $d->value = array(1);
     while (!empty($u->value)) {
         while (!($u->value[0] & 1)) {
             $u->_rshift(1);
             if ($a->value[0] & 1 || $b->value[0] & 1) {
                 $a = $a->add($x);
                 $b = $b->subtract($n);
             }
             $a->_rshift(1);
             $b->_rshift(1);
         }
         while (!($v->value[0] & 1)) {
             $v->_rshift(1);
             if ($c->value[0] & 1 || $d->value[0] & 1) {
                 $c = $c->add($x);
                 $d = $d->subtract($n);
             }
             $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);
         }
         $u->_normalize();
     }
     // at this point, $v == gcd($this, $n).  if it's not equal to 1, no modular inverse exists.
     if ($v->value != array(1)) {
         return false;
     }
     $d = $d->compare(new Math_BigInteger()) < 0 ? $d->add($n) : $d;
     return $this->is_negative ? $n->subtract($d) : $d;
 }
예제 #5
0
파일: DSA.php 프로젝트: raz0rsdge/horde
 /**
  * DSA verify.
  *
  * @param string $message     Message.
  * @param string $hash_alg    Hash algorithm.
  * @param Math_BigInteger $r  r.
  * @param Math_BigInteger $s  s.
  *
  * @return bool  True if verified.
  */
 public function verify($message, $hash_alg, $r, $s)
 {
     $hash = new Crypt_Hash($hash_alg);
     $hash_m = new Math_BigInteger($hash->hash($message), 256);
     $g = new Math_BigInteger($this->_key->key['g'], 256);
     $p = new Math_BigInteger($this->_key->key['p'], 256);
     $q = new Math_BigInteger($this->_key->key['q'], 256);
     $y = new Math_BigInteger($this->_key->key['y'], 256);
     $w = $s->modInverse($q);
     $hash_m_mul = $hash_m->multiply($w);
     $u1_base = $hash_m_mul->divide($q);
     $u1 = $u1_base[1];
     $r_mul = $r->multiply($w);
     $u2_base = $r_mul->divide($q);
     $u2 = $u2_base[1];
     $g_pow = $g->modPow($u1, $p);
     $y_pow = $y->modPow($u2, $p);
     $g_pow_mul = $g_pow->multiply($y_pow);
     $g_pow_mul_mod_base = $g_pow_mul->divide($p);
     $g_pow_mul_mod = $g_pow_mul_mod_base[1];
     $v_base = $g_pow_mul_mod->divide($q);
     $v = $v_base[1];
     return $v->compare($r) == 0;
 }
예제 #6
0
 /**
  * Key Exchange
  *
  * @param String $kexinit_payload_server
  * @access private
  */
 function _key_exchange($kexinit_payload_server)
 {
     static $kex_algorithms = array('diffie-hellman-group1-sha1', 'diffie-hellman-group14-sha1');
     static $server_host_key_algorithms = array('ssh-rsa', 'ssh-dss');
     static $encryption_algorithms = array('arcfour', 'aes128-cbc', 'aes192-cbc', 'aes256-cbc', '3des-cbc', 'none');
     static $mac_algorithms = array('hmac-sha1-96', 'hmac-sha1', 'hmac-md5-96', 'hmac-md5', 'none');
     static $compression_algorithms = array('none');
     static $str_kex_algorithms, $str_server_host_key_algorithms, $encryption_algorithms_server_to_client, $mac_algorithms_server_to_client, $compression_algorithms_server_to_client, $encryption_algorithms_client_to_server, $mac_algorithms_client_to_server, $compression_algorithms_client_to_server;
     if (empty($str_kex_algorithms)) {
         $str_kex_algorithms = implode(',', $kex_algorithms);
         $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
         $encryption_algorithms_server_to_client = $encryption_algorithms_client_to_server = implode(',', $encryption_algorithms);
         $mac_algorithms_server_to_client = $mac_algorithms_client_to_server = implode(',', $mac_algorithms);
         $compression_algorithms_server_to_client = $compression_algorithms_client_to_server = implode(',', $compression_algorithms);
     }
     $client_cookie = '';
     for ($i = 0; $i < 16; $i++) {
         $client_cookie .= chr(crypt_random(0, 255));
     }
     $response = $kexinit_payload_server;
     $this->_string_shift($response, 1);
     // skip past the message number (it should be SSH_MSG_KEXINIT)
     $server_cookie = $this->_string_shift($response, 16);
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
     extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
     $first_kex_packet_follows = $first_kex_packet_follows != 0;
     // the sending of SSH2_MSG_KEXINIT could go in one of two places.  this is the second place.
     $kexinit_payload_client = pack('Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN', NET_SSH2_MSG_KEXINIT, $client_cookie, strlen($str_kex_algorithms), $str_kex_algorithms, strlen($str_server_host_key_algorithms), $str_server_host_key_algorithms, strlen($encryption_algorithms_client_to_server), $encryption_algorithms_client_to_server, strlen($encryption_algorithms_server_to_client), $encryption_algorithms_server_to_client, strlen($mac_algorithms_client_to_server), $mac_algorithms_client_to_server, strlen($mac_algorithms_server_to_client), $mac_algorithms_server_to_client, strlen($compression_algorithms_client_to_server), $compression_algorithms_client_to_server, strlen($compression_algorithms_server_to_client), $compression_algorithms_server_to_client, 0, '', 0, '', 0, 0);
     if (!$this->_send_binary_packet($kexinit_payload_client)) {
         return false;
     }
     // here ends the second place.
     // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
     for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_server_to_client); $i++) {
     }
     if ($i == count($encryption_algorithms)) {
         user_error('No compatible server to client encryption algorithms found', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
     // diffie-hellman key exchange as fast as possible
     $decrypt = $encryption_algorithms[$i];
     switch ($decrypt) {
         case '3des-cbc':
             $decryptKeyLength = 24;
             // eg. 192 / 8
             break;
         case 'aes256-cbc':
             $decryptKeyLength = 32;
             // eg. 256 / 8
             break;
         case 'aes192-cbc':
             $decryptKeyLength = 24;
             // eg. 192 / 8
             break;
         case 'aes128-cbc':
             $decryptKeyLength = 16;
             // eg. 128 / 8
             break;
         case 'arcfour':
             $decryptKeyLength = 16;
             // eg. 128 / 8
             break;
         case 'none':
             $decryptKeyLength = 0;
     }
     for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_client_to_server); $i++) {
     }
     if ($i == count($encryption_algorithms)) {
         user_error('No compatible client to server encryption algorithms found', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     $encrypt = $encryption_algorithms[$i];
     switch ($encrypt) {
         case '3des-cbc':
             $encryptKeyLength = 24;
             break;
         case 'aes256-cbc':
             $encryptKeyLength = 32;
             break;
         case 'aes192-cbc':
             $encryptKeyLength = 24;
             break;
         case 'aes128-cbc':
             $encryptKeyLength = 16;
             break;
         case 'arcfour':
             $encryptKeyLength = 16;
             break;
         case 'none':
             $encryptKeyLength = 0;
     }
     $keyLength = $decryptKeyLength > $encryptKeyLength ? $decryptKeyLength : $encryptKeyLength;
     // through diffie-hellman key exchange a symmetric key is obtained
     for ($i = 0; $i < count($kex_algorithms) && !in_array($kex_algorithms[$i], $this->kex_algorithms); $i++) {
     }
     if ($i == count($kex_algorithms)) {
         user_error('No compatible key exchange algorithms found', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     switch ($kex_algorithms[$i]) {
         // see http://tools.ietf.org/html/rfc2409#section-6.2 and
         // http://tools.ietf.org/html/rfc2412, appendex E
         case 'diffie-hellman-group1-sha1':
             $p = pack('H256', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF');
             $keyLength = $keyLength < 160 ? $keyLength : 160;
             $hash = 'sha1';
             break;
             // see http://tools.ietf.org/html/rfc3526#section-3
         // see http://tools.ietf.org/html/rfc3526#section-3
         case 'diffie-hellman-group14-sha1':
             $p = pack('H512', 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF');
             $keyLength = $keyLength < 160 ? $keyLength : 160;
             $hash = 'sha1';
     }
     $p = new Math_BigInteger($p, 256);
     //$q = $p->bitwise_rightShift(1);
     /* To increase the speed of the key exchange, both client and server may
                reduce the size of their private exponents.  It should be at least
                twice as long as the key material that is generated from the shared
                secret.  For more details, see the paper by van Oorschot and Wiener
                [VAN-OORSCHOT].
     
                -- http://tools.ietf.org/html/rfc4419#section-6.2 */
     $q = new Math_BigInteger(1);
     $q = $q->bitwise_leftShift(2 * $keyLength);
     $q = $q->subtract(new Math_BigInteger(1));
     $g = new Math_BigInteger(2);
     $x = new Math_BigInteger();
     $x->setRandomGenerator('crypt_random');
     $x = $x->random(new Math_BigInteger(1), $q);
     $e = $g->modPow($x, $p);
     $eBytes = $e->toBytes(true);
     $data = pack('CNa*', NET_SSH2_MSG_KEXDH_INIT, strlen($eBytes), $eBytes);
     if (!$this->_send_binary_packet($data)) {
         user_error('Connection closed by server', E_USER_NOTICE);
         return false;
     }
     $response = $this->_get_binary_packet();
     if ($response === false) {
         user_error('Connection closed by server', E_USER_NOTICE);
         return false;
     }
     extract(unpack('Ctype', $this->_string_shift($response, 1)));
     if ($type != NET_SSH2_MSG_KEXDH_REPLY) {
         user_error('Expected SSH_MSG_KEXDH_REPLY', E_USER_NOTICE);
         return false;
     }
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']);
     $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
     $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']);
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $fBytes = $this->_string_shift($response, $temp['length']);
     $f = new Math_BigInteger($fBytes, -256);
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $signature = $this->_string_shift($response, $temp['length']);
     $temp = unpack('Nlength', $this->_string_shift($signature, 4));
     $signature_format = $this->_string_shift($signature, $temp['length']);
     $key = $f->modPow($x, $p);
     $keyBytes = $key->toBytes(true);
     $source = pack('Na*Na*Na*Na*Na*Na*Na*Na*', strlen($this->identifier), $this->identifier, strlen($this->server_identifier), $this->server_identifier, strlen($kexinit_payload_client), $kexinit_payload_client, strlen($kexinit_payload_server), $kexinit_payload_server, strlen($this->server_public_host_key), $this->server_public_host_key, strlen($eBytes), $eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes);
     $source = pack('H*', $hash($source));
     if ($this->session_id === false) {
         $this->session_id = $source;
     }
     // if you the server's assymetric key matches the one you have on file, then you should be able to decrypt the
     // "signature" and get something that should equal the "exchange hash", as defined in the SSH-2 specs.
     // here, we just check to see if the "signature" is good.  you can verify whether or not the assymetric key is good,
     // later, with the getServerHostKeyAlgorithm() function
     for ($i = 0; $i < count($server_host_key_algorithms) && !in_array($server_host_key_algorithms[$i], $this->server_host_key_algorithms); $i++) {
     }
     if ($i == count($server_host_key_algorithms)) {
         user_error('No compatible server host key algorithms found', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     if ($public_key_format != $server_host_key_algorithms[$i] || $signature_format != $server_host_key_algorithms[$i]) {
         user_error('Sever Host Key Algorithm Mismatch', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     switch ($server_host_key_algorithms[$i]) {
         case 'ssh-dss':
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $p = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $q = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $g = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $y = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             /* The value for 'dss_signature_blob' is encoded as a string containing
                r, followed by s (which are 160-bit integers, without lengths or
                padding, unsigned, and in network byte order). */
             $temp = unpack('Nlength', $this->_string_shift($signature, 4));
             if ($temp['length'] != 40) {
                 user_error('Invalid signature', E_USER_NOTICE);
                 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
             }
             $r = new Math_BigInteger($this->_string_shift($signature, 20), 256);
             $s = new Math_BigInteger($this->_string_shift($signature, 20), 256);
             if ($r->compare($q) >= 0 || $s->compare($q) >= 0) {
                 user_error('Invalid signature', E_USER_NOTICE);
                 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
             }
             $w = $s->modInverse($q);
             $u1 = $w->multiply(new Math_BigInteger(sha1($source), 16));
             list(, $u1) = $u1->divide($q);
             $u2 = $w->multiply($r);
             list(, $u2) = $u2->divide($q);
             $g = $g->modPow($u1, $p);
             $y = $y->modPow($u2, $p);
             $v = $g->multiply($y);
             list(, $v) = $v->divide($p);
             list(, $v) = $v->divide($q);
             if (!$v->equals($r)) {
                 user_error('Invalid signature', E_USER_NOTICE);
                 return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
             }
             break;
         case 'ssh-rsa':
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $e = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $n = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $nLength = $temp['length'];
             /*
             $temp = unpack('Nlength', $this->_string_shift($signature, 4));
             $signature = $this->_string_shift($signature, $temp['length']);
             
             if (!class_exists('Crypt_RSA')) {
                 require_once('Crypt/RSA.php');
             }
             
             $rsa = new Crypt_RSA();
             $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
             $rsa->loadKey(array('e' => $e, 'n' => $n), CRYPT_RSA_PUBLIC_FORMAT_RAW);
             if (!$rsa->verify($source, $signature)) {
                 user_error('Bad server signature', E_USER_NOTICE);
                 return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
             }
             */
             $temp = unpack('Nlength', $this->_string_shift($signature, 4));
             $s = new Math_BigInteger($this->_string_shift($signature, $temp['length']), 256);
             // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the
             // following URL:
             // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
             // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source.
             if ($s->compare(new Math_BigInteger()) < 0 || $s->compare($n->subtract(new Math_BigInteger(1))) > 0) {
                 user_error('Invalid signature', E_USER_NOTICE);
                 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
             }
             $s = $s->modPow($e, $n);
             $s = $s->toBytes();
             $h = pack('N4H*', 0x302130, 0x906052b, 0xe03021a, 0x5000414, sha1($source));
             $h = chr(0x1) . str_repeat(chr(0xff), $nLength - 3 - strlen($h)) . $h;
             if ($s != $h) {
                 user_error('Bad server signature', E_USER_NOTICE);
                 return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
             }
     }
     $packet = pack('C', NET_SSH2_MSG_NEWKEYS);
     if (!$this->_send_binary_packet($packet)) {
         return false;
     }
     $response = $this->_get_binary_packet();
     if ($response === false) {
         user_error('Connection closed by server', E_USER_NOTICE);
         return false;
     }
     extract(unpack('Ctype', $this->_string_shift($response, 1)));
     if ($type != NET_SSH2_MSG_NEWKEYS) {
         user_error('Expected SSH_MSG_NEWKEYS', E_USER_NOTICE);
         return false;
     }
     switch ($encrypt) {
         case '3des-cbc':
             $this->encrypt = new Crypt_TripleDES();
             // $this->encrypt_block_size = 64 / 8 == the default
             break;
         case 'aes256-cbc':
             $this->encrypt = new Crypt_AES();
             $this->encrypt_block_size = 16;
             // eg. 128 / 8
             break;
         case 'aes192-cbc':
             $this->encrypt = new Crypt_AES();
             $this->encrypt_block_size = 16;
             break;
         case 'aes128-cbc':
             $this->encrypt = new Crypt_AES();
             $this->encrypt_block_size = 16;
             break;
         case 'arcfour':
             $this->encrypt = new Crypt_RC4();
             break;
         case 'none':
             //$this->encrypt = new Crypt_Null();
     }
     switch ($decrypt) {
         case '3des-cbc':
             $this->decrypt = new Crypt_TripleDES();
             break;
         case 'aes256-cbc':
             $this->decrypt = new Crypt_AES();
             $this->decrypt_block_size = 16;
             break;
         case 'aes192-cbc':
             $this->decrypt = new Crypt_AES();
             $this->decrypt_block_size = 16;
             break;
         case 'aes128-cbc':
             $this->decrypt = new Crypt_AES();
             $this->decrypt_block_size = 16;
             break;
         case 'arcfour':
             $this->decrypt = new Crypt_RC4();
             break;
         case 'none':
             //$this->decrypt = new Crypt_Null();
     }
     $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
     if ($this->encrypt) {
         $this->encrypt->enableContinuousBuffer();
         $this->encrypt->disablePadding();
         $iv = pack('H*', $hash($keyBytes . $source . 'A' . $this->session_id));
         while ($this->encrypt_block_size > strlen($iv)) {
             $iv .= pack('H*', $hash($keyBytes . $source . $iv));
         }
         $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
         $key = pack('H*', $hash($keyBytes . $source . 'C' . $this->session_id));
         while ($encryptKeyLength > strlen($key)) {
             $key .= pack('H*', $hash($keyBytes . $source . $key));
         }
         $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
     }
     if ($this->decrypt) {
         $this->decrypt->enableContinuousBuffer();
         $this->decrypt->disablePadding();
         $iv = pack('H*', $hash($keyBytes . $source . 'B' . $this->session_id));
         while ($this->decrypt_block_size > strlen($iv)) {
             $iv .= pack('H*', $hash($keyBytes . $source . $iv));
         }
         $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
         $key = pack('H*', $hash($keyBytes . $source . 'D' . $this->session_id));
         while ($decryptKeyLength > strlen($key)) {
             $key .= pack('H*', $hash($keyBytes . $source . $key));
         }
         $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
     }
     for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_client_to_server); $i++) {
     }
     if ($i == count($mac_algorithms)) {
         user_error('No compatible client to server message authentication algorithms found', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     $createKeyLength = 0;
     // ie. $mac_algorithms[$i] == 'none'
     switch ($mac_algorithms[$i]) {
         case 'hmac-sha1':
             $this->hmac_create = new Crypt_Hash('sha1');
             $createKeyLength = 20;
             break;
         case 'hmac-sha1-96':
             $this->hmac_create = new Crypt_Hash('sha1-96');
             $createKeyLength = 20;
             break;
         case 'hmac-md5':
             $this->hmac_create = new Crypt_Hash('md5');
             $createKeyLength = 16;
             break;
         case 'hmac-md5-96':
             $this->hmac_create = new Crypt_Hash('md5-96');
             $createKeyLength = 16;
     }
     for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_server_to_client); $i++) {
     }
     if ($i == count($mac_algorithms)) {
         user_error('No compatible server to client message authentication algorithms found', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     $checkKeyLength = 0;
     $this->hmac_size = 0;
     switch ($mac_algorithms[$i]) {
         case 'hmac-sha1':
             $this->hmac_check = new Crypt_Hash('sha1');
             $checkKeyLength = 20;
             $this->hmac_size = 20;
             break;
         case 'hmac-sha1-96':
             $this->hmac_check = new Crypt_Hash('sha1-96');
             $checkKeyLength = 20;
             $this->hmac_size = 12;
             break;
         case 'hmac-md5':
             $this->hmac_check = new Crypt_Hash('md5');
             $checkKeyLength = 16;
             $this->hmac_size = 16;
             break;
         case 'hmac-md5-96':
             $this->hmac_check = new Crypt_Hash('md5-96');
             $checkKeyLength = 16;
             $this->hmac_size = 12;
     }
     $key = pack('H*', $hash($keyBytes . $source . 'E' . $this->session_id));
     while ($createKeyLength > strlen($key)) {
         $key .= pack('H*', $hash($keyBytes . $source . $key));
     }
     $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
     $key = pack('H*', $hash($keyBytes . $source . 'F' . $this->session_id));
     while ($checkKeyLength > strlen($key)) {
         $key .= pack('H*', $hash($keyBytes . $source . $key));
     }
     $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
     for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_server_to_client); $i++) {
     }
     if ($i == count($compression_algorithms)) {
         user_error('No compatible server to client compression algorithms found', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     $this->decompress = $compression_algorithms[$i] == 'zlib';
     for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_client_to_server); $i++) {
     }
     if ($i == count($compression_algorithms)) {
         user_error('No compatible client to server compression algorithms found', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     $this->compress = $compression_algorithms[$i] == 'zlib';
     return true;
 }
예제 #7
0
 /**
  * Key Exchange
  *
  * @param String $kexinit_payload_server
  * @access private
  */
 function _key_exchange($kexinit_payload_server)
 {
     static $kex_algorithms = array('diffie-hellman-group1-sha1', 'diffie-hellman-group14-sha1');
     static $server_host_key_algorithms = array('ssh-rsa', 'ssh-dss');
     static $encryption_algorithms = array('arcfour', 'aes128-cbc', 'aes192-cbc', 'aes256-cbc', '3des-cbc', 'none');
     static $mac_algorithms = array('hmac-sha1-96', 'hmac-sha1', 'hmac-md5-96', 'hmac-md5', 'none');
     static $compression_algorithms = array('none');
     static $str_kex_algorithms, $str_server_host_key_algorithms, $encryption_algorithms_server_to_client, $mac_algorithms_server_to_client, $compression_algorithms_server_to_client, $encryption_algorithms_client_to_server, $mac_algorithms_client_to_server, $compression_algorithms_client_to_server;
     if (empty($str_kex_algorithms)) {
         $str_kex_algorithms = implode(',', $kex_algorithms);
         $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
         $encryption_algorithms_server_to_client = $encryption_algorithms_client_to_server = implode(',', $encryption_algorithms);
         $mac_algorithms_server_to_client = $mac_algorithms_client_to_server = implode(',', $mac_algorithms);
         $compression_algorithms_server_to_client = $compression_algorithms_client_to_server = implode(',', $compression_algorithms);
     }
     $client_cookie = '';
     for ($i = 0; $i < 16; $i++) {
         $client_cookie .= chr(crypt_random(0, 255));
     }
     $response = $kexinit_payload_server;
     $this->_string_shift($response, 1);
     // skip past the message number (it should be SSH_MSG_KEXINIT)
     list(, $server_cookie) = unpack('a16', $this->_string_shift($response, 16));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
     list(, $first_kex_packet_follows) = unpack('C', $this->_string_shift($response, 1));
     $first_kex_packet_follows = $first_kex_packet_follows != 0;
     // the sending of SSH2_MSG_KEXINIT could go in one of two places.  this is the second place.
     $kexinit_payload_client = pack('Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN', NET_SSH2_MSG_KEXINIT, $client_cookie, strlen($str_kex_algorithms), $str_kex_algorithms, strlen($str_server_host_key_algorithms), $str_server_host_key_algorithms, strlen($encryption_algorithms_client_to_server), $encryption_algorithms_client_to_server, strlen($encryption_algorithms_server_to_client), $encryption_algorithms_server_to_client, strlen($mac_algorithms_client_to_server), $mac_algorithms_client_to_server, strlen($mac_algorithms_server_to_client), $mac_algorithms_server_to_client, strlen($compression_algorithms_client_to_server), $compression_algorithms_client_to_server, strlen($compression_algorithms_server_to_client), $compression_algorithms_server_to_client, 0, '', 0, '', 0, 0);
     if (!$this->_send_binary_packet($kexinit_payload_client)) {
         return false;
     }
     // here ends the second place.
     // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
     for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_server_to_client); $i++) {
     }
     if ($i == count($encryption_algorithms)) {
         user_error('No compatible server to client encryption algorithms found', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
     // diffie-hellman key exchange as fast as possible
     $decrypt = $encryption_algorithms[$i];
     switch ($decrypt) {
         case '3des-cbc':
             $decryptKeyLength = 24;
             // eg. 192 / 8
             break;
         case 'aes256-cbc':
             $decryptKeyLength = 32;
             // eg. 256 / 8
             break;
         case 'aes192-cbc':
             $decryptKeyLength = 24;
             // eg. 192 / 8
             break;
         case 'aes128-cbc':
             $decryptKeyLength = 16;
             // eg. 128 / 8
             break;
         case 'arcfour':
             $decryptKeyLength = 16;
             // eg. 128 / 8
             break;
         case 'none':
             $decryptKeyLength = 0;
     }
     for ($i = 0; $i < count($encryption_algorithms) && !in_array($encryption_algorithms[$i], $this->encryption_algorithms_client_to_server); $i++) {
     }
     if ($i == count($encryption_algorithms)) {
         user_error('No compatible client to server encryption algorithms found', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     $encrypt = $encryption_algorithms[$i];
     switch ($encrypt) {
         case '3des-cbc':
             $encryptKeyLength = 24;
             break;
         case 'aes256-cbc':
             $encryptKeyLength = 32;
             break;
         case 'aes192-cbc':
             $encryptKeyLength = 24;
             break;
         case 'aes128-cbc':
             $encryptKeyLength = 16;
             break;
         case 'arcfour':
             $encryptKeyLength = 16;
             break;
         case 'none':
             $encryptKeyLength = 0;
     }
     $keyLength = $decryptKeyLength > $encryptKeyLength ? $decryptKeyLength : $encryptKeyLength;
     // through diffie-hellman key exchange a symmetric key is obtained
     for ($i = 0; $i < count($kex_algorithms) && !in_array($kex_algorithms[$i], $this->kex_algorithms); $i++) {
     }
     if ($i == count($kex_algorithms)) {
         user_error('No compatible key exchange algorithms found', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     switch ($kex_algorithms[$i]) {
         // see http://tools.ietf.org/html/rfc2409#section-6.2 and
         // http://tools.ietf.org/html/rfc2412, appendex E
         case 'diffie-hellman-group1-sha1':
             $p = pack('N32', 4294967295.0, 4294967295.0, 3373259426.0, 0x2168c234, 3301335691.0, 2161908945.0, 0x29024e08, 2322058356.0, 0x20bbea6, 0x3b139b22, 0x514a0879, 0.0, 0.0, 3443147547.0, 0x302b0a6d, 4066317367.0, 0x4fe1356d, 0x6d51c245, 0.0, 0x625e7ec6, 0.0, 0.0, 0xbff5cb6, 0.0, 0.0, 0x5a899fa5, 0.0, 0x7c4b1fe6, 0x49286651, 0.0, 4294967295.0, 4294967295.0);
             $keyLength = $keyLength < 160 ? $keyLength : 160;
             $hash = 'sha1';
             break;
             // see http://tools.ietf.org/html/rfc3526#section-3
         // see http://tools.ietf.org/html/rfc3526#section-3
         case 'diffie-hellman-group14-sha1':
             $p = pack('N64', 4294967295.0, 4294967295.0, 3373259426.0, 0x2168c234, 3301335691.0, 2161908945.0, 0x29024e08, 2322058356.0, 0x20bbea6, 0x3b139b22, 0x514a0879, 0.0, 0.0, 3443147547.0, 0x302b0a6d, 4066317367.0, 0x4fe1356d, 0x6d51c245, 0.0, 0x625e7ec6, 0.0, 0.0, 0xbff5cb6, 0.0, 0.0, 0x5a899fa5, 0.0, 0x7c4b1fe6, 0x49286651, 0.0, 3254811832.0, 2707668741.0, 2564442166.0, 0x1c55d39a, 0x69163fa8, 4247048031.0, 2204458275.0, 3701714326.0, 0x1c62f356, 0x208552bb, 0.0, 0x7096966d, 0x670c354e, 0x4abc9804, 4050938888.0, 3390579068.0, 0x32905e46, 0x2e36ce3b, 0.0, 0x180e8603, 2603058082.0, 0.0, 3049610736.0, 0x6f4c52c9, 0.0, 2505578264.0, 0x3995497c, 0.0, 0x15d22618, 2566522128.0, 0x15728e5a, 2326571624.0, 4294967295.0, 4294967295.0);
             $keyLength = $keyLength < 160 ? $keyLength : 160;
             $hash = 'sha1';
     }
     $p = new Math_BigInteger($p, 256);
     //$q = $p->bitwise_rightShift(1);
     /* To increase the speed of the key exchange, both client and server may
                reduce the size of their private exponents.  It should be at least
                twice as long as the key material that is generated from the shared
                secret.  For more details, see the paper by van Oorschot and Wiener
                [VAN-OORSCHOT].
     
                -- http://tools.ietf.org/html/rfc4419#section-6.2 */
     $q = new Math_BigInteger(1);
     $q = $q->bitwise_leftShift(2 * $keyLength);
     $q = $q->subtract(new Math_BigInteger(1));
     $g = new Math_BigInteger(2);
     $x = new Math_BigInteger();
     $x = $x->random(new Math_BigInteger(1), $q, 'crypt_random');
     $e = $g->modPow($x, $p);
     $eBytes = $e->toBytes(true);
     $data = pack('CNa*', NET_SSH2_MSG_KEXDH_INIT, strlen($eBytes), $eBytes);
     if (!$this->_send_binary_packet($data)) {
         user_error('Connection closed by server', E_USER_NOTICE);
         return false;
     }
     $response = $this->_get_binary_packet();
     if ($response === false) {
         user_error('Connection closed by server', E_USER_NOTICE);
         return false;
     }
     list(, $type) = unpack('C', $this->_string_shift($response, 1));
     if ($type != NET_SSH2_MSG_KEXDH_REPLY) {
         user_error('Expected SSH_MSG_KEXDH_REPLY', E_USER_NOTICE);
         return false;
     }
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']);
     $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
     $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']);
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $fBytes = $this->_string_shift($response, $temp['length']);
     $f = new Math_BigInteger($fBytes, -256);
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $signature = $this->_string_shift($response, $temp['length']);
     $temp = unpack('Nlength', $this->_string_shift($signature, 4));
     $signature_format = $this->_string_shift($signature, $temp['length']);
     $key = $f->modPow($x, $p);
     $keyBytes = $key->toBytes(true);
     $source = pack('Na*Na*Na*Na*Na*Na*Na*Na*', strlen($this->identifier), $this->identifier, strlen($this->server_identifier), $this->server_identifier, strlen($kexinit_payload_client), $kexinit_payload_client, strlen($kexinit_payload_server), $kexinit_payload_server, strlen($this->server_public_host_key), $this->server_public_host_key, strlen($eBytes), $eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes);
     $source = pack('H*', $hash($source));
     if ($this->session_id === false) {
         $this->session_id = $source;
     }
     // if you the server's assymetric key matches the one you have on file, then you should be able to decrypt the
     // "signature" and get something that should equal the "exchange hash", as defined in the SSH-2 specs.
     // here, we just check to see if the "signature" is good.  you can verify whether or not the assymetric key is good,
     // later, with the getServerHostKeyAlgorithm() function
     for ($i = 0; $i < count($server_host_key_algorithms) && !in_array($server_host_key_algorithms[$i], $this->server_host_key_algorithms); $i++) {
     }
     if ($i == count($server_host_key_algorithms)) {
         user_error('No compatible server host key algorithms found', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     if ($public_key_format != $server_host_key_algorithms[$i] || $signature_format != $server_host_key_algorithms[$i]) {
         user_error('Sever Host Key Algorithm Mismatch', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     switch ($server_host_key_algorithms[$i]) {
         case 'ssh-dss':
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $p = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $q = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $g = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $y = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             /* The value for 'dss_signature_blob' is encoded as a string containing
                r, followed by s (which are 160-bit integers, without lengths or
                padding, unsigned, and in network byte order). */
             $temp = unpack('Nlength', $this->_string_shift($signature, 4));
             if ($temp['length'] != 40) {
                 user_error('Invalid signature', E_USER_NOTICE);
                 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
             }
             $r = new Math_BigInteger($this->_string_shift($signature, 20), 256);
             $s = new Math_BigInteger($this->_string_shift($signature, 20), 256);
             if ($r->compare($q) >= 0 || $s->compare($q) >= 0) {
                 user_error('Invalid signature', E_USER_NOTICE);
                 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
             }
             $w = $s->modInverse($q);
             $u1 = $w->multiply(new Math_BigInteger(sha1($source), 16));
             list(, $u1) = $u1->divide($q);
             $u2 = $w->multiply($r);
             list(, $u2) = $u2->divide($q);
             $g = $g->modPow($u1, $p);
             $y = $y->modPow($u2, $p);
             $v = $g->multiply($y);
             list(, $v) = $v->divide($p);
             list(, $v) = $v->divide($q);
             if ($v->compare($r) != 0) {
                 user_error('Invalid signature', E_USER_NOTICE);
                 return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
             }
             break;
         case 'ssh-rsa':
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $e = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $n = new Math_BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $nLength = $temp['length'];
             $temp = unpack('Nlength', $this->_string_shift($signature, 4));
             $s = new Math_BigInteger($this->_string_shift($signature, $temp['length']), 256);
             // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the
             // following URL:
             // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
             // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source.
             if ($s->compare(new Math_BigInteger()) < 0 || $s->compare($n->subtract(new Math_BigInteger(1))) > 0) {
                 user_error('Invalid signature', E_USER_NOTICE);
                 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
             }
             $s = $s->modPow($e, $n);
             $s = $s->toBytes();
             $h = pack('N4H*', 0x302130, 0x906052b, 0xe03021a, 0x5000414, sha1($source));
             $h = chr(0x1) . str_repeat(chr(0xff), $nLength - 3 - strlen($h)) . $h;
             if ($s != $h) {
                 user_error('Bad server signature', E_USER_NOTICE);
                 return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
             }
     }
     $packet = pack('C', NET_SSH2_MSG_NEWKEYS);
     if (!$this->_send_binary_packet($packet)) {
         return false;
     }
     $response = $this->_get_binary_packet();
     if ($response === false) {
         user_error('Connection closed by server', E_USER_NOTICE);
         return false;
     }
     list(, $type) = unpack('C', $this->_string_shift($response, 1));
     if ($type != NET_SSH2_MSG_NEWKEYS) {
         user_error('Expected SSH_MSG_NEWKEYS', E_USER_NOTICE);
         return false;
     }
     switch ($encrypt) {
         case '3des-cbc':
             $this->encrypt = new Crypt_TripleDES();
             // $this->encrypt_block_size = 64 / 8 == the default
             break;
         case 'aes256-cbc':
             $this->encrypt = new Crypt_AES();
             $this->encrypt_block_size = 16;
             // eg. 128 / 8
             break;
         case 'aes192-cbc':
             $this->encrypt = new Crypt_AES();
             $this->encrypt_block_size = 16;
             break;
         case 'aes128-cbc':
             $this->encrypt = new Crypt_AES();
             $this->encrypt_block_size = 16;
             break;
         case 'arcfour':
             $this->encrypt = new Crypt_RC4();
             break;
         case 'none':
             //$this->encrypt = new Crypt_Null();
     }
     switch ($decrypt) {
         case '3des-cbc':
             $this->decrypt = new Crypt_TripleDES();
             break;
         case 'aes256-cbc':
             $this->decrypt = new Crypt_AES();
             $this->decrypt_block_size = 16;
             break;
         case 'aes192-cbc':
             $this->decrypt = new Crypt_AES();
             $this->decrypt_block_size = 16;
             break;
         case 'aes128-cbc':
             $this->decrypt = new Crypt_AES();
             $this->decrypt_block_size = 16;
             break;
         case 'arcfour':
             $this->decrypt = new Crypt_RC4();
             break;
         case 'none':
             //$this->decrypt = new Crypt_Null();
     }
     $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
     if ($this->encrypt) {
         $this->encrypt->enableContinuousBuffer();
         $this->encrypt->disablePadding();
         $iv = pack('H*', $hash($keyBytes . $source . 'A' . $this->session_id));
         while ($this->encrypt_block_size > strlen($iv)) {
             $iv .= pack('H*', $hash($keyBytes . $source . $iv));
         }
         $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
         $key = pack('H*', $hash($keyBytes . $source . 'C' . $this->session_id));
         while ($encryptKeyLength > strlen($key)) {
             $key .= pack('H*', $hash($keyBytes . $source . $key));
         }
         $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
     }
     if ($this->decrypt) {
         $this->decrypt->enableContinuousBuffer();
         $this->decrypt->disablePadding();
         $iv = pack('H*', $hash($keyBytes . $source . 'B' . $this->session_id));
         while ($this->decrypt_block_size > strlen($iv)) {
             $iv .= pack('H*', $hash($keyBytes . $source . $iv));
         }
         $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
         $key = pack('H*', $hash($keyBytes . $source . 'D' . $this->session_id));
         while ($decryptKeyLength > strlen($key)) {
             $key .= pack('H*', $hash($keyBytes . $source . $key));
         }
         $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
     }
     for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_client_to_server); $i++) {
     }
     if ($i == count($mac_algorithms)) {
         user_error('No compatible client to server message authentication algorithms found', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     $createKeyLength = 0;
     // ie. $mac_algorithms[$i] == 'none'
     switch ($mac_algorithms[$i]) {
         case 'hmac-sha1':
             $this->hmac_create = new Crypt_Hash('sha1');
             $createKeyLength = 20;
             break;
         case 'hmac-sha1-96':
             $this->hmac_create = new Crypt_Hash('sha1-96');
             $createKeyLength = 20;
             break;
         case 'hmac-md5':
             $this->hmac_create = new Crypt_Hash('md5');
             $createKeyLength = 16;
             break;
         case 'hmac-md5-96':
             $this->hmac_create = new Crypt_Hash('md5-96');
             $createKeyLength = 16;
     }
     for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_server_to_client); $i++) {
     }
     if ($i == count($mac_algorithms)) {
         user_error('No compatible server to client message authentication algorithms found', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     $checkKeyLength = 0;
     $this->hmac_size = 0;
     switch ($mac_algorithms[$i]) {
         case 'hmac-sha1':
             $this->hmac_check = new Crypt_Hash('sha1');
             $checkKeyLength = 20;
             $this->hmac_size = 20;
             break;
         case 'hmac-sha1-96':
             $this->hmac_check = new Crypt_Hash('sha1-96');
             $checkKeyLength = 20;
             $this->hmac_size = 12;
             break;
         case 'hmac-md5':
             $this->hmac_check = new Crypt_Hash('md5');
             $checkKeyLength = 16;
             $this->hmac_size = 16;
             break;
         case 'hmac-md5-96':
             $this->hmac_check = new Crypt_Hash('md5-96');
             $checkKeyLength = 16;
             $this->hmac_size = 12;
     }
     $key = pack('H*', $hash($keyBytes . $source . 'E' . $this->session_id));
     while ($createKeyLength > strlen($key)) {
         $key .= pack('H*', $hash($keyBytes . $source . $key));
     }
     $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
     $key = pack('H*', $hash($keyBytes . $source . 'F' . $this->session_id));
     while ($checkKeyLength > strlen($key)) {
         $key .= pack('H*', $hash($keyBytes . $source . $key));
     }
     $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
     for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_server_to_client); $i++) {
     }
     if ($i == count($compression_algorithms)) {
         user_error('No compatible server to client compression algorithms found', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     $this->decompress = $compression_algorithms[$i] == 'zlib';
     for ($i = 0; $i < count($compression_algorithms) && !in_array($compression_algorithms[$i], $this->compression_algorithms_client_to_server); $i++) {
     }
     if ($i == count($compression_algorithms)) {
         user_error('No compatible client to server compression algorithms found', E_USER_NOTICE);
         return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
     }
     $this->compress = $compression_algorithms[$i] == 'zlib';
     return true;
 }
예제 #8
0
    function createKey($bits = 1024, $timeout = false, $partial = array())
    {
        if (!defined('CRYPT_RSA_EXPONENT')) {
                        define('CRYPT_RSA_EXPONENT', '65537');
        }
                                                        if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
            define('CRYPT_RSA_SMALLEST_PRIME', 4096);
        }

                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)));

                        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);
        }

		$_MinMax = $this->_generateMinMax($bits);
		$absoluteMin = $_MinMax['min'];

        $temp = $bits >> 1;         if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
            $num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
            $temp = CRYPT_RSA_SMALLEST_PRIME;
        } else {
            $num_primes = 2;
        }
        $_MinMax = $this->_generateMinMax($temp + $bits % $temp);
        $finalMax = $_MinMax['max'];

        $_MinMax = $this->_generateMinMax($temp + $bits % $temp);
        $min = $_MinMax['min'];
        $max = $_MinMax['max'];

        $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);                     }
                    $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
                } else {
                    $primes[$i] = $generator->randomPrime($min, $max, $timeout);
                }

                if ($primes[$i] === false) {                     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
                    );
                }

                                                if ($i > 2) {
                    $coefficients[$i] = $n->modInverse($primes[$i]);
                }

                $n = $n->multiply($primes[$i]);

                $temp = $primes[$i]->subtract($this->one);

                                                $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]);

                                                                                                        
        return array(
            'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
            'publickey'  => $this->_convertPublicKey($n, $e),
            'partialkey' => false
        );
    }