Example #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.
  *
  * @access public
  * @return Mixed
  */
 public function getServerPublicHostKey()
 {
     if (!($this->bitmap & self::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 BigInteger();
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $p = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $q = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $g = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $y = new 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 BigInteger($this->_string_shift($signature, 20), 256);
             $s = new 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 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 BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
             $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
             $rawN = $this->_string_shift($server_public_host_key, $temp['length']);
             $n = new BigInteger($rawN, -256);
             $nLength = strlen(ltrim($rawN, ""));
             /*
                         $temp = unpack('Nlength', $this->_string_shift($signature, 4));
                         $signature = $this->_string_shift($signature, $temp['length']);
             
                         $rsa = new RSA();
                         $rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
                         $rsa->loadKey(array('e' => $e, 'n' => $n), 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 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 BigInteger()) < 0 || $s->compare($n->subtract(new 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 - 2 - 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);
 }