Example #1
0
 /**
  * Key Exchange
  *
  * @param string $kexinit_payload_server
  * @throws \UnexpectedValueException on receipt of unexpected packets
  * @throws \RuntimeException on other errors
  * @throws \phpseclib\Exception\NoSupportedAlgorithmsException when none of the algorithms phpseclib has loaded are compatible
  * @access private
  */
 function _key_exchange($kexinit_payload_server)
 {
     $kex_algorithms = array('curve25519-sha256@libssh.org', 'diffie-hellman-group1-sha1', 'diffie-hellman-group14-sha1', 'diffie-hellman-group-exchange-sha1', 'diffie-hellman-group-exchange-sha256');
     if (!class_exists('\\Sodium')) {
         $kex_algorithms = array_diff($kex_algorithms, array('curve25519-sha256@libssh.org'));
     }
     $server_host_key_algorithms = array('ssh-rsa', 'ssh-dss');
     $encryption_algorithms = array('arcfour256', 'arcfour128', 'aes128-ctr', 'aes192-ctr', 'aes256-ctr', 'twofish128-ctr', 'twofish192-ctr', 'twofish256-ctr', 'aes128-cbc', 'aes192-cbc', 'aes256-cbc', 'twofish128-cbc', 'twofish192-cbc', 'twofish256-cbc', 'twofish-cbc', 'blowfish-ctr', 'blowfish-cbc', '3des-ctr', '3des-cbc');
     if (extension_loaded('openssl') && !extension_loaded('mcrypt')) {
         // OpenSSL does not support arcfour256 in any capacity and arcfour128 / arcfour support is limited to
         // instances that do not use continuous buffers
         $encryption_algorithms = array_diff($encryption_algorithms, array('arcfour256', 'arcfour128', 'arcfour'));
     }
     if (class_exists('\\phpseclib\\Crypt\\RC4') === false) {
         $encryption_algorithms = array_diff($encryption_algorithms, array('arcfour256', 'arcfour128', 'arcfour'));
     }
     if (class_exists('\\phpseclib\\Crypt\\Rijndael') === false) {
         $encryption_algorithms = array_diff($encryption_algorithms, array('aes128-ctr', 'aes192-ctr', 'aes256-ctr', 'aes128-cbc', 'aes192-cbc', 'aes256-cbc'));
     }
     if (class_exists('\\phpseclib\\Crypt\\Twofish') === false) {
         $encryption_algorithms = array_diff($encryption_algorithms, array('twofish128-ctr', 'twofish192-ctr', 'twofish256-ctr', 'twofish128-cbc', 'twofish192-cbc', 'twofish256-cbc', 'twofish-cbc'));
     }
     if (class_exists('\\phpseclib\\Crypt\\Blowfish') === false) {
         $encryption_algorithms = array_diff($encryption_algorithms, array('blowfish-ctr', 'blowfish-cbc'));
     }
     if (class_exists('\\phpseclib\\Crypt\\TripleDES') === false) {
         $encryption_algorithms = array_diff($encryption_algorithms, array('3des-ctr', '3des-cbc'));
     }
     $encryption_algorithms = array_values($encryption_algorithms);
     $mac_algorithms = array('hmac-sha2-256', 'hmac-sha1-96', 'hmac-sha1', 'hmac-md5-96', 'hmac-md5');
     $compression_algorithms = array('none');
     // some SSH servers have buggy implementations of some of the above algorithms
     switch ($this->server_identifier) {
         case 'SSH-2.0-SSHD':
             $mac_algorithms = array_values(array_diff($mac_algorithms, array('hmac-sha1-96', 'hmac-md5-96')));
     }
     $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 = Random::string(16);
     $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
     // 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 = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_server_to_client);
     $decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt);
     if ($decryptKeyLength === null) {
         $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
         throw new NoSupportedAlgorithmsException('No compatible server to client encryption algorithms found');
     }
     $encrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_client_to_server);
     $encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt);
     if ($encryptKeyLength === null) {
         $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
         throw new NoSupportedAlgorithmsException('No compatible client to server encryption algorithms found');
     }
     // through diffie-hellman key exchange a symmetric key is obtained
     $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms);
     if ($kex_algorithm === false) {
         $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
         throw new NoSupportedAlgorithmsException('No compatible key exchange algorithms found');
     }
     // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty.
     $exchange_hash_rfc4419 = '';
     if ($kex_algorithm === 'curve25519-sha256@libssh.org') {
         $x = Random::string(32);
         $eBytes = \Sodium::crypto_box_publickey_from_secretkey($x);
         $clientKexInitMessage = NET_SSH2_MSG_KEX_ECDH_INIT;
         $serverKexReplyMessage = NET_SSH2_MSG_KEX_ECDH_REPLY;
         $kexHash = new Hash('sha256');
     } else {
         if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
             $dh_group_sizes_packed = pack('NNN', $this->kex_dh_group_size_min, $this->kex_dh_group_size_preferred, $this->kex_dh_group_size_max);
             $packet = pack('Ca*', NET_SSH2_MSG_KEXDH_GEX_REQUEST, $dh_group_sizes_packed);
             if (!$this->_send_binary_packet($packet)) {
                 return false;
             }
             $response = $this->_get_binary_packet();
             if ($response === false) {
                 user_error('Connection closed by server');
                 return false;
             }
             extract(unpack('Ctype', $this->_string_shift($response, 1)));
             if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) {
                 user_error('Expected SSH_MSG_KEX_DH_GEX_GROUP');
                 return false;
             }
             extract(unpack('NprimeLength', $this->_string_shift($response, 4)));
             $primeBytes = $this->_string_shift($response, $primeLength);
             $prime = new BigInteger($primeBytes, -256);
             extract(unpack('NgLength', $this->_string_shift($response, 4)));
             $gBytes = $this->_string_shift($response, $gLength);
             $g = new BigInteger($gBytes, -256);
             $exchange_hash_rfc4419 = pack('a*Na*Na*', $dh_group_sizes_packed, $primeLength, $primeBytes, $gLength, $gBytes);
             $clientKexInitMessage = NET_SSH2_MSG_KEXDH_GEX_INIT;
             $serverKexReplyMessage = NET_SSH2_MSG_KEXDH_GEX_REPLY;
         } else {
             switch ($kex_algorithm) {
                 // see http://tools.ietf.org/html/rfc2409#section-6.2 and
                 // http://tools.ietf.org/html/rfc2412, appendex E
                 case 'diffie-hellman-group1-sha1':
                     $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
                     break;
                     // see http://tools.ietf.org/html/rfc3526#section-3
                 // see http://tools.ietf.org/html/rfc3526#section-3
                 case 'diffie-hellman-group14-sha1':
                     $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' . '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' . '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' . 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' . '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' . '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' . 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' . '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
                     break;
             }
             // For both diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1
             // the generator field element is 2 (decimal) and the hash function is sha1.
             $g = new BigInteger(2);
             $prime = new BigInteger($prime, 16);
             $clientKexInitMessage = NET_SSH2_MSG_KEXDH_INIT;
             $serverKexReplyMessage = NET_SSH2_MSG_KEXDH_REPLY;
         }
         switch ($kex_algorithm) {
             case 'diffie-hellman-group-exchange-sha256':
                 $kexHash = new Hash('sha256');
                 break;
             default:
                 $kexHash = new Hash('sha1');
         }
         /* 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 */
         $one = new BigInteger(1);
         $keyLength = min($kexHash->getLength(), max($encryptKeyLength, $decryptKeyLength));
         $max = $one->bitwise_leftShift(16 * $keyLength);
         // 2 * 8 * $keyLength
         $max = $max->subtract($one);
         $x = $one->random($one, $max);
         $e = $g->modPow($x, $prime);
         $eBytes = $e->toBytes(true);
     }
     $data = pack('CNa*', $clientKexInitMessage, strlen($eBytes), $eBytes);
     if (!$this->_send_binary_packet($data)) {
         throw new \RuntimeException('Connection closed by server');
     }
     $response = $this->_get_binary_packet();
     if ($response === false) {
         throw new \RuntimeException('Connection closed by server');
     }
     extract(unpack('Ctype', $this->_string_shift($response, 1)));
     if ($type != $serverKexReplyMessage) {
         throw new \UnexpectedValueException('Expected SSH_MSG_KEXDH_REPLY');
     }
     $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']);
     $temp = unpack('Nlength', $this->_string_shift($response, 4));
     $this->signature = $this->_string_shift($response, $temp['length']);
     $temp = unpack('Nlength', $this->_string_shift($this->signature, 4));
     $this->signature_format = $this->_string_shift($this->signature, $temp['length']);
     if ($kex_algorithm === 'curve25519-sha256@libssh.org') {
         if (strlen($fBytes) !== 32) {
             user_error('Received curve25519 public key of invalid length.');
             return false;
         }
         $key = new BigInteger(\Sodium::crypto_scalarmult($x, $fBytes), 256);
         \Sodium::sodium_memzero($x);
     } else {
         $f = new BigInteger($fBytes, -256);
         $key = $f->modPow($x, $prime);
     }
     $keyBytes = $key->toBytes(true);
     $this->exchange_hash = pack('Na*Na*Na*Na*Na*a*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, $exchange_hash_rfc4419, strlen($eBytes), $eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes);
     $this->exchange_hash = $kexHash->hash($this->exchange_hash);
     if ($this->session_id === false) {
         $this->session_id = $this->exchange_hash;
     }
     $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms);
     if ($server_host_key_algorithm === false) {
         $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
         throw new NoSupportedAlgorithmsException('No compatible server host key algorithms found');
     }
     if ($public_key_format != $server_host_key_algorithm || $this->signature_format != $server_host_key_algorithm) {
         $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
         throw new \RuntimeException('Server Host Key Algorithm Mismatch');
     }
     $packet = pack('C', NET_SSH2_MSG_NEWKEYS);
     if (!$this->_send_binary_packet($packet)) {
         return false;
     }
     $response = $this->_get_binary_packet();
     if ($response === false) {
         throw new \RuntimeException('Connection closed by server');
     }
     extract(unpack('Ctype', $this->_string_shift($response, 1)));
     if ($type != NET_SSH2_MSG_NEWKEYS) {
         throw new \UnexpectedValueException('Expected SSH_MSG_NEWKEYS');
     }
     $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
     $this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt);
     if ($this->encrypt) {
         if ($this->crypto_engine) {
             $this->encrypt->setEngine($this->crypto_engine);
         }
         if ($this->encrypt->block_size) {
             $this->encrypt_block_size = $this->encrypt->block_size;
         }
         $this->encrypt->enableContinuousBuffer();
         $this->encrypt->disablePadding();
         $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id);
         while ($this->encrypt_block_size > strlen($iv)) {
             $iv .= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
         }
         $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
         $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id);
         while ($encryptKeyLength > strlen($key)) {
             $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
         }
         $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
     }
     $this->decrypt = $this->_encryption_algorithm_to_crypt_instance($decrypt);
     if ($this->decrypt) {
         if ($this->crypto_engine) {
             $this->decrypt->setEngine($this->crypto_engine);
         }
         if ($this->decrypt->block_size) {
             $this->decrypt_block_size = $this->decrypt->block_size;
         }
         $this->decrypt->enableContinuousBuffer();
         $this->decrypt->disablePadding();
         $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id);
         while ($this->decrypt_block_size > strlen($iv)) {
             $iv .= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
         }
         $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
         $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id);
         while ($decryptKeyLength > strlen($key)) {
             $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
         }
         $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
     }
     /* The "arcfour128" algorithm is the RC4 cipher, as described in
                [SCHNEIER], using a 128-bit key.  The first 1536 bytes of keystream
                generated by the cipher MUST be discarded, and the first byte of the
                first encrypted packet MUST be encrypted using the 1537th byte of
                keystream.
     
                -- http://tools.ietf.org/html/rfc4345#section-4 */
     if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') {
         $this->encrypt->encrypt(str_repeat("", 1536));
     }
     if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') {
         $this->decrypt->decrypt(str_repeat("", 1536));
     }
     $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_client_to_server);
     if ($mac_algorithm === false) {
         $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
         throw new NoSupportedAlgorithmsException('No compatible client to server message authentication algorithms found');
     }
     $createKeyLength = 0;
     // ie. $mac_algorithm == 'none'
     switch ($mac_algorithm) {
         case 'hmac-sha2-256':
             $this->hmac_create = new Hash('sha256');
             $createKeyLength = 32;
             break;
         case 'hmac-sha1':
             $this->hmac_create = new Hash('sha1');
             $createKeyLength = 20;
             break;
         case 'hmac-sha1-96':
             $this->hmac_create = new Hash('sha1-96');
             $createKeyLength = 20;
             break;
         case 'hmac-md5':
             $this->hmac_create = new Hash('md5');
             $createKeyLength = 16;
             break;
         case 'hmac-md5-96':
             $this->hmac_create = new Hash('md5-96');
             $createKeyLength = 16;
     }
     $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_server_to_client);
     if ($mac_algorithm === false) {
         $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
         throw new NoSupportedAlgorithmsException('No compatible server to client message authentication algorithms found');
     }
     $checkKeyLength = 0;
     $this->hmac_size = 0;
     switch ($mac_algorithm) {
         case 'hmac-sha2-256':
             $this->hmac_check = new Hash('sha256');
             $checkKeyLength = 32;
             $this->hmac_size = 32;
             break;
         case 'hmac-sha1':
             $this->hmac_check = new Hash('sha1');
             $checkKeyLength = 20;
             $this->hmac_size = 20;
             break;
         case 'hmac-sha1-96':
             $this->hmac_check = new Hash('sha1-96');
             $checkKeyLength = 20;
             $this->hmac_size = 12;
             break;
         case 'hmac-md5':
             $this->hmac_check = new Hash('md5');
             $checkKeyLength = 16;
             $this->hmac_size = 16;
             break;
         case 'hmac-md5-96':
             $this->hmac_check = new Hash('md5-96');
             $checkKeyLength = 16;
             $this->hmac_size = 12;
     }
     $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id);
     while ($createKeyLength > strlen($key)) {
         $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
     }
     $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
     $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id);
     while ($checkKeyLength > strlen($key)) {
         $key .= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
     }
     $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
     $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_server_to_client);
     if ($compression_algorithm === false) {
         $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
         throw new NoSupportedAlgorithmsException('No compatible server to client compression algorithms found');
     }
     $this->decompress = $compression_algorithm == 'zlib';
     $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_client_to_server);
     if ($compression_algorithm === false) {
         $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
         throw new NoSupportedAlgorithmsException('No compatible client to server compression algorithms found');
     }
     $this->compress = $compression_algorithm == 'zlib';
     return true;
 }
Example #2
0
 /**
  * Connect to an SSHv1 server
  *
  * @return bool
  * @access private
  */
 function _connect()
 {
     $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->connectionTimeout);
     if (!$this->fsock) {
         user_error(rtrim("Cannot connect to {$this->host}:{$this->port}. Error {$errno}. {$errstr}"));
         return false;
     }
     $this->server_identification = $init_line = fgets($this->fsock, 255);
     if (defined('NET_SSH1_LOGGING')) {
         $this->_append_log('<-', $this->server_identification);
         $this->_append_log('->', $this->identifier . "\r\n");
     }
     if (!preg_match('#SSH-([0-9\\.]+)-(.+)#', $init_line, $parts)) {
         user_error('Can only connect to SSH servers');
         return false;
     }
     if ($parts[1][0] != 1) {
         user_error("Cannot connect to SSH {$parts['1']} servers");
         return false;
     }
     fputs($this->fsock, $this->identifier . "\r\n");
     $response = $this->_get_binary_packet();
     if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) {
         user_error('Expected SSH_SMSG_PUBLIC_KEY');
         return false;
     }
     $anti_spoofing_cookie = $this->_string_shift($response[self::RESPONSE_DATA], 8);
     $this->_string_shift($response[self::RESPONSE_DATA], 4);
     $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
     $server_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
     $this->server_key_public_exponent = $server_key_public_exponent;
     $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
     $server_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
     $this->server_key_public_modulus = $server_key_public_modulus;
     $this->_string_shift($response[self::RESPONSE_DATA], 4);
     $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
     $host_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
     $this->host_key_public_exponent = $host_key_public_exponent;
     $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2));
     $host_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256);
     $this->host_key_public_modulus = $host_key_public_modulus;
     $this->_string_shift($response[self::RESPONSE_DATA], 4);
     // get a list of the supported ciphers
     extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4)));
     foreach ($this->supported_ciphers as $mask => $name) {
         if (($supported_ciphers_mask & 1 << $mask) == 0) {
             unset($this->supported_ciphers[$mask]);
         }
     }
     // get a list of the supported authentications
     extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4)));
     foreach ($this->supported_authentications as $mask => $name) {
         if (($supported_authentications_mask & 1 << $mask) == 0) {
             unset($this->supported_authentications[$mask]);
         }
     }
     $session_id = pack('H*', md5($host_key_public_modulus->toBytes() . $server_key_public_modulus->toBytes() . $anti_spoofing_cookie));
     $session_key = Random::string(32);
     $double_encrypted_session_key = $session_key ^ str_pad($session_id, 32, chr(0));
     if ($server_key_public_modulus->compare($host_key_public_modulus) < 0) {
         $double_encrypted_session_key = $this->_rsa_crypt($double_encrypted_session_key, array($server_key_public_exponent, $server_key_public_modulus));
         $double_encrypted_session_key = $this->_rsa_crypt($double_encrypted_session_key, array($host_key_public_exponent, $host_key_public_modulus));
     } else {
         $double_encrypted_session_key = $this->_rsa_crypt($double_encrypted_session_key, array($host_key_public_exponent, $host_key_public_modulus));
         $double_encrypted_session_key = $this->_rsa_crypt($double_encrypted_session_key, array($server_key_public_exponent, $server_key_public_modulus));
     }
     $cipher = isset($this->supported_ciphers[$this->cipher]) ? $this->cipher : self::CIPHER_3DES;
     $data = pack('C2a*na*N', NET_SSH1_CMSG_SESSION_KEY, $cipher, $anti_spoofing_cookie, 8 * strlen($double_encrypted_session_key), $double_encrypted_session_key, 0);
     if (!$this->_send_binary_packet($data)) {
         user_error('Error sending SSH_CMSG_SESSION_KEY');
         return false;
     }
     switch ($cipher) {
         //case self::CIPHER_NONE:
         //    $this->crypto = new \phpseclib\Crypt\Null();
         //    break;
         case self::CIPHER_DES:
             $this->crypto = new DES();
             $this->crypto->disablePadding();
             $this->crypto->enableContinuousBuffer();
             $this->crypto->setKey(substr($session_key, 0, 8));
             break;
         case self::CIPHER_3DES:
             $this->crypto = new TripleDES(TripleDES::MODE_3CBC);
             $this->crypto->disablePadding();
             $this->crypto->enableContinuousBuffer();
             $this->crypto->setKey(substr($session_key, 0, 24));
             break;
             //case self::CIPHER_RC4:
             //    $this->crypto = new RC4();
             //    $this->crypto->enableContinuousBuffer();
             //    $this->crypto->setKey(substr($session_key, 0,  16));
             //    break;
     }
     $response = $this->_get_binary_packet();
     if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
         user_error('Expected SSH_SMSG_SUCCESS');
         return false;
     }
     $this->bitmap = self::MASK_CONNECTED;
     return true;
 }