SSH2: backport setPreferredAlgorithms() / getAlgorithmsNegotiated()

This commit is contained in:
terrafrost 2019-09-07 19:08:00 -05:00
parent 0b93c5bbd7
commit 3ad44cb3d6

View File

@ -214,6 +214,15 @@ class Net_SSH2
*/
var $kex_algorithms = false;
/**
* Key Exchange Algorithm
*
* @see self::getMethodsNegotiated()
* @var string|false
* @access private
*/
var $kex_algorithm = false;
/**
* Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods
*
@ -322,6 +331,15 @@ class Net_SSH2
*/
var $languages_client_to_server = false;
/**
* Preferred Algorithms
*
* @see self::setPreferredAlgorithms()
* @var array
* @access private
*/
var $preferred = array();
/**
* Block Size for Server to Client Encryption
*
@ -903,14 +921,6 @@ class Net_SSH2
*/
var $bad_key_size_fix = false;
/**
* The selected decryption algorithm
*
* @var string
* @access private
*/
var $decrypt_algorithm = '';
/**
* Should we try to re-connect to re-establish keys?
*
@ -1324,137 +1334,59 @@ class Net_SSH2
*/
function _key_exchange($kexinit_payload_server = false)
{
static $kex_algorithms = array(
'diffie-hellman-group1-sha1', // REQUIRED
'diffie-hellman-group14-sha1', // REQUIRED
'diffie-hellman-group-exchange-sha1', // RFC 4419
'diffie-hellman-group-exchange-sha256', // RFC 4419
);
$preferred = $this->preferred;
static $server_host_key_algorithms = array(
'rsa-sha2-256', // RFC 8332
'rsa-sha2-512', // RFC 8332
'ssh-rsa', // RECOMMENDED sign Raw RSA Key
'ssh-dss' // REQUIRED sign Raw DSS Key
);
static $encryption_algorithms = false;
if ($encryption_algorithms === false) {
$encryption_algorithms = array(
// from <http://tools.ietf.org/html/rfc4345#section-4>:
'arcfour256',
'arcfour128',
//'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key
// CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>:
'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key
'aes192-ctr', // RECOMMENDED AES with 192-bit key
'aes256-ctr', // RECOMMENDED AES with 256-bit key
'twofish128-ctr', // OPTIONAL Twofish in SDCTR mode, with 128-bit key
'twofish192-ctr', // OPTIONAL Twofish with 192-bit key
'twofish256-ctr', // OPTIONAL Twofish with 256-bit key
'aes128-cbc', // RECOMMENDED AES with a 128-bit key
'aes192-cbc', // OPTIONAL AES with a 192-bit key
'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key
'twofish128-cbc', // OPTIONAL Twofish with a 128-bit key
'twofish192-cbc', // OPTIONAL Twofish with a 192-bit key
'twofish256-cbc',
'twofish-cbc', // OPTIONAL alias for "twofish256-cbc"
// (this is being retained for historical reasons)
'blowfish-ctr', // OPTIONAL Blowfish in SDCTR mode
'blowfish-cbc', // OPTIONAL Blowfish in CBC mode
'3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode
'3des-cbc', // REQUIRED three-key 3DES in CBC mode
//'none' // OPTIONAL no encryption; NOT RECOMMENDED
);
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 (phpseclib_resolve_include_path('Crypt/RC4.php') === false) {
$encryption_algorithms = array_diff(
$encryption_algorithms,
array('arcfour256', 'arcfour128', 'arcfour')
);
}
if (phpseclib_resolve_include_path('Crypt/Rijndael.php') === false) {
$encryption_algorithms = array_diff(
$encryption_algorithms,
array('aes128-ctr', 'aes192-ctr', 'aes256-ctr', 'aes128-cbc', 'aes192-cbc', 'aes256-cbc')
);
}
if (phpseclib_resolve_include_path('Crypt/Twofish.php') === false) {
$encryption_algorithms = array_diff(
$encryption_algorithms,
array('twofish128-ctr', 'twofish192-ctr', 'twofish256-ctr', 'twofish128-cbc', 'twofish192-cbc', 'twofish256-cbc', 'twofish-cbc')
);
}
if (phpseclib_resolve_include_path('Crypt/Blowfish.php') === false) {
$encryption_algorithms = array_diff(
$encryption_algorithms,
array('blowfish-ctr', 'blowfish-cbc')
);
}
if (phpseclib_resolve_include_path('Crypt/TripleDES.php') === false) {
$encryption_algorithms = array_diff(
$encryption_algorithms,
array('3des-ctr', '3des-cbc')
);
}
$encryption_algorithms = array_values($encryption_algorithms);
}
$mac_algorithms = array(
// from <http://www.ietf.org/rfc/rfc6668.txt>:
'hmac-sha2-256',// RECOMMENDED HMAC-SHA256 (digest length = key length = 32)
'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20)
'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16)
//'none' // OPTIONAL no MAC; NOT RECOMMENDED
);
static $compression_algorithms = array(
'none' // REQUIRED no compression
//'zlib' // OPTIONAL ZLIB (LZ77) compression
);
$kex_algorithms = isset($preferred['kex']) ?
$preferred['kex'] :
$this->getSupportedKEXAlgorithms();
$server_host_key_algorithms = isset($preferred['hostkey']) ?
$preferred['hostkey'] :
$this->getSupportedHostKeyAlgorithms();
$s2c_encryption_algorithms = isset($preferred['server_to_client']['crypt']) ?
$preferred['server_to_client']['crypt'] :
$this->getSupportedEncryptionAlgorithms();
$c2s_encryption_algorithms = isset($preferred['client_to_server']['crypt']) ?
$preferred['client_to_server']['crypt'] :
$this->getSupportedEncryptionAlgorithms();
$s2c_mac_algorithms = isset($preferred['server_to_client']['mac']) ?
$preferred['server_to_client']['mac'] :
$this->getSupportedMACAlgorithms();
$c2s_mac_algorithms = isset($preferred['client_to_server']['mac']) ?
$preferred['client_to_server']['mac'] :
$this->getSupportedMACAlgorithms();
$s2c_compression_algorithms = isset($preferred['server_to_client']['comp']) ?
$preferred['server_to_client']['comp'] :
$this->getSupportedCompressionAlgorithms();
$c2s_compression_algorithms = isset($preferred['client_to_server']['comp']) ?
$preferred['client_to_server']['comp'] :
$this->getSupportedCompressionAlgorithms();
// some SSH servers have buggy implementations of some of the above algorithms
switch (true) {
case $this->server_identifier == 'SSH-2.0-SSHD':
case substr($this->server_identifier, 0, 13) == 'SSH-2.0-DLINK':
$mac_algorithms = array_values(array_diff(
$mac_algorithms,
array('hmac-sha1-96', 'hmac-md5-96')
));
if (!isset($preferred['server_to_client']['mac'])) {
$s2c_mac_algorithms = array_values(array_diff(
$s2c_mac_algorithms,
array('hmac-sha1-96', 'hmac-md5-96')
));
}
if (!isset($preferred['client_to_server']['mac'])) {
$c2s_mac_algorithms = array_values(array_diff(
$c2s_mac_algorithms,
array('hmac-sha1-96', 'hmac-md5-96')
));
}
}
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);
}
$str_kex_algorithms = implode(',', $kex_algorithms);
$str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
$encryption_algorithms_server_to_client = implode(',', $s2c_encryption_algorithms);
$encryption_algorithms_client_to_server = implode(',', $c2s_encryption_algorithms);
$mac_algorithms_server_to_client = implode(',', $s2c_mac_algorithms);
$mac_algorithms_client_to_server = implode(',', $c2s_mac_algorithms);
$compression_algorithms_server_to_client = implode(',', $s2c_compression_algorithms);
$compression_algorithms_client_to_server = implode(',', $c2s_compression_algorithms);
$client_cookie = crypt_random_string(16);
@ -1581,14 +1513,14 @@ class Net_SSH2
// 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);
$decrypt = $this->_array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client);
$decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt);
if ($decryptKeyLength === null) {
user_error('No compatible server to client encryption algorithms found');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$encrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_client_to_server);
$encrypt = $this->_array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server);
$encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt);
if ($encryptKeyLength === null) {
user_error('No compatible client to server encryption algorithms found');
@ -1598,7 +1530,7 @@ class Net_SSH2
$keyLength = $decryptKeyLength > $encryptKeyLength ? $decryptKeyLength : $encryptKeyLength;
// through diffie-hellman key exchange a symmetric key is obtained
$kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms);
$this->kex_algorithm = $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms);
if ($kex_algorithm === false) {
user_error('No compatible key exchange algorithms found');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
@ -1853,161 +1785,8 @@ class Net_SSH2
return false;
}
switch ($encrypt) {
case '3des-cbc':
if (!class_exists('Crypt_TripleDES')) {
include_once 'Crypt/TripleDES.php';
}
$this->encrypt = new Crypt_TripleDES();
// $this->encrypt_block_size = 64 / 8 == the default
break;
case '3des-ctr':
if (!class_exists('Crypt_TripleDES')) {
include_once 'Crypt/TripleDES.php';
}
$this->encrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
// $this->encrypt_block_size = 64 / 8 == the default
break;
case 'aes256-cbc':
case 'aes192-cbc':
case 'aes128-cbc':
if (!class_exists('Crypt_Rijndael')) {
include_once 'Crypt/Rijndael.php';
}
$this->encrypt = new Crypt_Rijndael();
$this->encrypt_block_size = 16; // eg. 128 / 8
break;
case 'aes256-ctr':
case 'aes192-ctr':
case 'aes128-ctr':
if (!class_exists('Crypt_Rijndael')) {
include_once 'Crypt/Rijndael.php';
}
$this->encrypt = new Crypt_Rijndael(CRYPT_RIJNDAEL_MODE_CTR);
$this->encrypt_block_size = 16; // eg. 128 / 8
break;
case 'blowfish-cbc':
if (!class_exists('Crypt_Blowfish')) {
include_once 'Crypt/Blowfish.php';
}
$this->encrypt = new Crypt_Blowfish();
$this->encrypt_block_size = 8;
break;
case 'blowfish-ctr':
if (!class_exists('Crypt_Blowfish')) {
include_once 'Crypt/Blowfish.php';
}
$this->encrypt = new Crypt_Blowfish(CRYPT_BLOWFISH_MODE_CTR);
$this->encrypt_block_size = 8;
break;
case 'twofish128-cbc':
case 'twofish192-cbc':
case 'twofish256-cbc':
case 'twofish-cbc':
if (!class_exists('Crypt_Twofish')) {
include_once 'Crypt/Twofish.php';
}
$this->encrypt = new Crypt_Twofish();
$this->encrypt_block_size = 16;
break;
case 'twofish128-ctr':
case 'twofish192-ctr':
case 'twofish256-ctr':
if (!class_exists('Crypt_Twofish')) {
include_once 'Crypt/Twofish.php';
}
$this->encrypt = new Crypt_Twofish(CRYPT_TWOFISH_MODE_CTR);
$this->encrypt_block_size = 16;
break;
case 'arcfour':
case 'arcfour128':
case 'arcfour256':
if (!class_exists('Crypt_RC4')) {
include_once 'Crypt/RC4.php';
}
$this->encrypt = new Crypt_RC4();
break;
case 'none':
//$this->encrypt = new Crypt_Null();
}
switch ($decrypt) {
case '3des-cbc':
if (!class_exists('Crypt_TripleDES')) {
include_once 'Crypt/TripleDES.php';
}
$this->decrypt = new Crypt_TripleDES();
break;
case '3des-ctr':
if (!class_exists('Crypt_TripleDES')) {
include_once 'Crypt/TripleDES.php';
}
$this->decrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
break;
case 'aes256-cbc':
case 'aes192-cbc':
case 'aes128-cbc':
if (!class_exists('Crypt_Rijndael')) {
include_once 'Crypt/Rijndael.php';
}
$this->decrypt = new Crypt_Rijndael();
$this->decrypt_block_size = 16;
break;
case 'aes256-ctr':
case 'aes192-ctr':
case 'aes128-ctr':
if (!class_exists('Crypt_Rijndael')) {
include_once 'Crypt/Rijndael.php';
}
$this->decrypt = new Crypt_Rijndael(CRYPT_RIJNDAEL_MODE_CTR);
$this->decrypt_block_size = 16;
break;
case 'blowfish-cbc':
if (!class_exists('Crypt_Blowfish')) {
include_once 'Crypt/Blowfish.php';
}
$this->decrypt = new Crypt_Blowfish();
$this->decrypt_block_size = 8;
break;
case 'blowfish-ctr':
if (!class_exists('Crypt_Blowfish')) {
include_once 'Crypt/Blowfish.php';
}
$this->decrypt = new Crypt_Blowfish(CRYPT_BLOWFISH_MODE_CTR);
$this->decrypt_block_size = 8;
break;
case 'twofish128-cbc':
case 'twofish192-cbc':
case 'twofish256-cbc':
case 'twofish-cbc':
if (!class_exists('Crypt_Twofish')) {
include_once 'Crypt/Twofish.php';
}
$this->decrypt = new Crypt_Twofish();
$this->decrypt_block_size = 16;
break;
case 'twofish128-ctr':
case 'twofish192-ctr':
case 'twofish256-ctr':
if (!class_exists('Crypt_Twofish')) {
include_once 'Crypt/Twofish.php';
}
$this->decrypt = new Crypt_Twofish(CRYPT_TWOFISH_MODE_CTR);
$this->decrypt_block_size = 16;
break;
case 'arcfour':
case 'arcfour128':
case 'arcfour256':
if (!class_exists('Crypt_RC4')) {
include_once 'Crypt/RC4.php';
}
$this->decrypt = new Crypt_RC4();
break;
case 'none':
//$this->decrypt = new Crypt_Null();
}
$this->decrypt_algorithm = $decrypt;
$this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt);
$this->decrypt = $this->_encryption_algorithm_to_crypt_instance($decrypt);
$keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
@ -2018,6 +1797,10 @@ class Net_SSH2
$this->encrypt->enableContinuousBuffer();
$this->encrypt->disablePadding();
if ($this->encrypt->getBlockLength()) {
$this->encrypt_block_size = $this->encrypt->getBlockLength() >> 3;
}
$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);
@ -2029,6 +1812,8 @@ class Net_SSH2
$key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
}
$this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
$this->encrypt->name = $decrypt;
}
if ($this->decrypt) {
@ -2038,6 +1823,10 @@ class Net_SSH2
$this->decrypt->enableContinuousBuffer();
$this->decrypt->disablePadding();
if ($this->decrypt->getBlockLength()) {
$this->decrypt_block_size = $this->decrypt->getBlockLength() >> 3;
}
$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);
@ -2049,6 +1838,8 @@ class Net_SSH2
$key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
}
$this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
$this->decrypt->name = $decrypt;
}
/* The "arcfour128" algorithm is the RC4 cipher, as described in
@ -2065,7 +1856,7 @@ class Net_SSH2
$this->decrypt->decrypt(str_repeat("\0", 1536));
}
$mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_client_to_server);
$mac_algorithm = $this->_array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server);
if ($mac_algorithm === false) {
user_error('No compatible client to server message authentication algorithms found');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
@ -2093,8 +1884,9 @@ class Net_SSH2
$this->hmac_create = new Crypt_Hash('md5-96');
$createKeyLength = 16;
}
$this->hmac_create->name = $mac_algorithm;
$mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_server_to_client);
$mac_algorithm = $this->_array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client);
if ($mac_algorithm === false) {
user_error('No compatible server to client message authentication algorithms found');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
@ -2128,6 +1920,7 @@ class Net_SSH2
$checkKeyLength = 16;
$this->hmac_size = 12;
}
$this->hmac_check->name = $mac_algorithm;
$key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id);
while ($createKeyLength > strlen($key)) {
@ -2141,19 +1934,19 @@ class Net_SSH2
}
$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) {
user_error('No compatible server to client compression algorithms found');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$this->decompress = $compression_algorithm == 'zlib';
$compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_client_to_server);
$compression_algorithm = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server);
if ($compression_algorithm === false) {
user_error('No compatible client to server compression algorithms found');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
$this->compress = $compression_algorithm == 'zlib';
//$this->decompress = $compression_algorithm == 'zlib';
$compression_algorithm = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_client_to_server);
if ($compression_algorithm === false) {
user_error('No compatible server to client compression algorithms found');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
//$this->compress = $compression_algorithm == 'zlib';
return true;
}
@ -2201,6 +1994,89 @@ class Net_SSH2
return null;
}
/**
* Maps an encryption algorithm name to an instance of a subclass of
* \phpseclib\Crypt\Base.
*
* @param string $algorithm Name of the encryption algorithm
* @return mixed Instance of \phpseclib\Crypt\Base or null for unknown
* @access private
*/
function _encryption_algorithm_to_crypt_instance($algorithm)
{
switch ($algorithm) {
case '3des-cbc':
if (!class_exists('Crypt_TripleDES')) {
include_once 'Crypt/TripleDES.php';
}
return new Crypt_TripleDES();
break;
case '3des-ctr':
if (!class_exists('Crypt_TripleDES')) {
include_once 'Crypt/TripleDES.php';
}
return new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
break;
case 'aes256-cbc':
case 'aes192-cbc':
case 'aes128-cbc':
if (!class_exists('Crypt_Rijndael')) {
include_once 'Crypt/Rijndael.php';
}
return new Crypt_Rijndael();
break;
case 'aes256-ctr':
case 'aes192-ctr':
case 'aes128-ctr':
if (!class_exists('Crypt_Rijndael')) {
include_once 'Crypt/Rijndael.php';
}
return new Crypt_Rijndael(CRYPT_RIJNDAEL_MODE_CTR);
$this->encrypt_block_size = 16; // eg. 128 / 8
break;
case 'blowfish-cbc':
if (!class_exists('Crypt_Blowfish')) {
include_once 'Crypt/Blowfish.php';
}
return new Crypt_Blowfish();
break;
case 'blowfish-ctr':
if (!class_exists('Crypt_Blowfish')) {
include_once 'Crypt/Blowfish.php';
}
return new Crypt_Blowfish(CRYPT_BLOWFISH_MODE_CTR);
break;
case 'twofish128-cbc':
case 'twofish192-cbc':
case 'twofish256-cbc':
case 'twofish-cbc':
if (!class_exists('Crypt_Twofish')) {
include_once 'Crypt/Twofish.php';
}
return new Crypt_Twofish();
break;
case 'twofish128-ctr':
case 'twofish192-ctr':
case 'twofish256-ctr':
if (!class_exists('Crypt_Twofish')) {
include_once 'Crypt/Twofish.php';
}
return new Crypt_Twofish(CRYPT_TWOFISH_MODE_CTR);
break;
case 'arcfour':
case 'arcfour128':
case 'arcfour256':
if (!class_exists('Crypt_RC4')) {
include_once 'Crypt/RC4.php';
}
return new Crypt_RC4();
break;
case 'none':
//return new Crypt_Null();
}
return null;
}
/**
* Tests whether or not proposed algorithm has a potential for issues
*
@ -3477,7 +3353,7 @@ class Net_SSH2
// "implementations SHOULD check that the packet length is reasonable"
// PuTTY uses 0x9000 as the actual max packet size and so to shall we
if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) {
if (!$this->bad_key_size_fix && $this->_bad_algorithm_candidate($this->decrypt_algorithm) && !($this->bitmap & NET_SSH2_MASK_LOGIN)) {
if (!$this->bad_key_size_fix && $this->_bad_algorithm_candidate($this->decrypt->name) && !($this->bitmap & NET_SSH2_MASK_LOGIN)) {
$this->bad_key_size_fix = true;
$this->_reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
return false;
@ -4600,6 +4476,297 @@ class Net_SSH2
return $this->languages_client_to_server;
}
/**
* Returns a list of algorithms the server supports
*
* @return array
* @access public
*/
public function getServerAlgorithms()
{
$this->_connect();
return array(
'kex' => $this->kex_algorithms,
'hostkey' => $this->server_host_key_algorithms,
'client_to_server' => array(
'crypt' => $this->encryption_algorithms_client_to_server,
'mac' => $this->mac_algorithms_client_to_server,
'comp' => $this->compression_algorithms_client_to_server,
'lang' => $this->languages_client_to_server
),
'server_to_client' => array(
'crypt' => $this->encryption_algorithms_server_to_client,
'mac' => $this->mac_algorithms_server_to_client,
'comp' => $this->compression_algorithms_server_to_client,
'lang' => $this->languages_server_to_client
)
);
}
/**
* Returns a list of KEX algorithms that phpseclib supports
*
* @return array
* @access public
*/
function getSupportedKEXAlgorithms()
{
$kex_algorithms = array(
'diffie-hellman-group-exchange-sha256',// RFC 4419
'diffie-hellman-group-exchange-sha1', // RFC 4419
// Diffie-Hellman Key Agreement (DH) using integer modulo prime
// groups.
'diffie-hellman-group14-sha1', // REQUIRED
'diffie-hellman-group1-sha1', // REQUIRED
);
return $kex_algorithms;
}
/**
* Returns a list of host key algorithms that phpseclib supports
*
* @return array
* @access public
*/
function getSupportedHostKeyAlgorithms()
{
return array(
'rsa-sha2-256', // RFC 8332
'rsa-sha2-512', // RFC 8332
'ssh-rsa', // RECOMMENDED sign Raw RSA Key
'ssh-dss' // REQUIRED sign Raw DSS Key
);
}
/**
* Returns a list of symmetric key algorithms that phpseclib supports
*
* @return array
* @access public
*/
function getSupportedEncryptionAlgorithms()
{
$algos = array(
// from <http://tools.ietf.org/html/rfc4345#section-4>:
'arcfour256',
'arcfour128',
//'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key
// CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>:
'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key
'aes192-ctr', // RECOMMENDED AES with 192-bit key
'aes256-ctr', // RECOMMENDED AES with 256-bit key
'twofish128-ctr', // OPTIONAL Twofish in SDCTR mode, with 128-bit key
'twofish192-ctr', // OPTIONAL Twofish with 192-bit key
'twofish256-ctr', // OPTIONAL Twofish with 256-bit key
'aes128-cbc', // RECOMMENDED AES with a 128-bit key
'aes192-cbc', // OPTIONAL AES with a 192-bit key
'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key
'twofish128-cbc', // OPTIONAL Twofish with a 128-bit key
'twofish192-cbc', // OPTIONAL Twofish with a 192-bit key
'twofish256-cbc',
'twofish-cbc', // OPTIONAL alias for "twofish256-cbc"
// (this is being retained for historical reasons)
'blowfish-ctr', // OPTIONAL Blowfish in SDCTR mode
'blowfish-cbc', // OPTIONAL Blowfish in CBC mode
'3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode
'3des-cbc', // REQUIRED three-key 3DES in CBC mode
//'none' // OPTIONAL no encryption; NOT RECOMMENDED
);
$engines = array(
CRYPT_ENGINE_OPENSSL,
CRYPT_ENGINE_MCRYPT,
CRYPT_ENGINE_INTERNAL
);
$ciphers = array();
foreach ($engines as $engine) {
foreach ($algos as $algo) {
$obj = $this->_encryption_algorithm_to_crypt_instance($algo);
if (strtolower(get_class($obj)) == 'crypt_rijndael') {
$obj->setKeyLength(preg_replace('#[^\d]#', '', $algo));
}
switch ($algo) {
case 'arcfour128':
case 'arcfour256':
if ($engine == CRYPT_ENGINE_INTERNAL) {
$algos = array_diff($algos, array($algo));
$ciphers[] = $algo;
} else {
continue 2;
}
}
if ($obj->isValidEngine($engine)) {
$algos = array_diff($algos, array($algo));
$ciphers[] = $algo;
}
}
}
return $ciphers;
}
/**
* Returns a list of MAC algorithms that phpseclib supports
*
* @return array
* @access public
*/
function getSupportedMACAlgorithms()
{
return array(
// from <http://www.ietf.org/rfc/rfc6668.txt>:
'hmac-sha2-256',// RECOMMENDED HMAC-SHA256 (digest length = key length = 32)
'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20)
'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16)
//'none' // OPTIONAL no MAC; NOT RECOMMENDED
);
}
/**
* Returns a list of compression algorithms that phpseclib supports
*
* @return array
* @access public
*/
function getSupportedCompressionAlgorithms()
{
return array(
'none' // REQUIRED no compression
//'zlib' // OPTIONAL ZLIB (LZ77) compression
);
}
/**
* Return list of negotiated algorithms
*
* Uses the same format as https://www.php.net/ssh2-methods-negotiated
*
* @return array
* @access public
*/
function getAlgorithmsNegotiated()
{
$this->_connect();
return array(
'kex' => $this->kex_algorithm,
'hostkey' => $this->signature_format,
'client_to_server' => array(
'crypt' => $this->encrypt->name,
'mac' => $this->hmac_create->name,
'comp' => 'none',
),
'server_to_client' => array(
'crypt' => $this->decrypt->name,
'mac' => $this->hmac_check->name,
'comp' => 'none',
)
);
}
/**
* Accepts an associative array with up to four parameters as described at
* <https://www.php.net/manual/en/function.ssh2-connect.php>
*
* @param array $methods
* @access public
*/
function setPreferredAlgorithms($methods)
{
$preferred = $methods;
if (isset($preferred['kex'])) {
$preferred['kex'] = array_intersect(
$preferred['kex'],
$this->getSupportedKEXAlgorithms()
);
}
if (isset($preferred['hostkey'])) {
$preferred['hostkey'] = array_intersect(
$preferred['hostkey'],
$this->getSupportedHostKeyAlgorithms()
);
}
$keys = array('client_to_server', 'server_to_client');
foreach ($keys as $key) {
if (isset($preferred[$key])) {
$a = &$preferred[$key];
if (isset($a['crypt'])) {
$a['crypt'] = array_intersect(
$a['crypt'],
$this->getSupportedEncryptionAlgorithms()
);
}
if (isset($a['comp'])) {
$a['comp'] = array_intersect(
$a['comp'],
$this->getSupportedCompressionAlgorithms()
);
}
if (isset($a['mac'])) {
$a['mac'] = array_intersect(
$a['mac'],
$this->getSupportedMACAlgorithms()
);
}
}
}
$keys = array(
'kex',
'hostkey',
'client_to_server/crypt',
'client_to_server/comp',
'client_to_server/mac',
'server_to_client/crypt',
'server_to_client/comp',
'server_to_client/mac',
);
foreach ($keys as $key) {
$p = $preferred;
$m = $methods;
$subkeys = explode('/', $key);
foreach ($subkeys as $subkey) {
if (!isset($p[$subkey])) {
continue 2;
}
$p = $p[$subkey];
$m = $m[$subkey];
}
if (count($p) != count($m)) {
$diff = array_diff($m, $p);
$msg = count($diff) == 1 ?
' is not a supported algorithm' :
' are not supported algorithms';
user_error(implode(', ', $diff) . $msg);
return false;
}
}
$this->preferred = $preferred;
}
/**
* Returns the banner message.
*