mirror of
https://github.com/phpseclib/phpseclib.git
synced 2024-12-30 12:50:38 +00:00
Merge branch '2.0'
* 2.0: SSH2: Add support for curve25519-sha256@libssh.org.txt via libsodium-php.
This commit is contained in:
commit
7e25340359
@ -55,6 +55,7 @@
|
|||||||
"squizlabs/php_codesniffer": "~2.0"
|
"squizlabs/php_codesniffer": "~2.0"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
|
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
|
||||||
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a wide variety of cryptographic operations.",
|
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a wide variety of cryptographic operations.",
|
||||||
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations."
|
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations."
|
||||||
},
|
},
|
||||||
|
2
composer.lock
generated
2
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"hash": "a8adabfa04c250bbec41c87f79a59b31",
|
"hash": "b24ab20be15b6312e532ee2ffa18d5fa",
|
||||||
"packages": [],
|
"packages": [],
|
||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
{
|
{
|
||||||
|
@ -953,7 +953,10 @@ class SSH2
|
|||||||
31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP',
|
31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP',
|
||||||
32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT',
|
32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT',
|
||||||
33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY',
|
33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY',
|
||||||
34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST')
|
34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'),
|
||||||
|
// RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org)
|
||||||
|
array(30 => 'NET_SSH2_MSG_KEX_ECDH_INIT',
|
||||||
|
31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY')
|
||||||
);
|
);
|
||||||
|
|
||||||
if (is_resource($host)) {
|
if (is_resource($host)) {
|
||||||
@ -1110,9 +1113,13 @@ class SSH2
|
|||||||
*/
|
*/
|
||||||
function _generate_identifier()
|
function _generate_identifier()
|
||||||
{
|
{
|
||||||
$identifier = 'SSH-2.0-phpseclib_0.3';
|
$identifier = 'SSH-2.0-phpseclib_2.0';
|
||||||
|
|
||||||
$ext = array();
|
$ext = array();
|
||||||
|
if (extension_loaded('libsodium')) {
|
||||||
|
$ext[] = 'libsodium';
|
||||||
|
}
|
||||||
|
|
||||||
if (extension_loaded('openssl')) {
|
if (extension_loaded('openssl')) {
|
||||||
$ext[] = 'openssl';
|
$ext[] = 'openssl';
|
||||||
} elseif (extension_loaded('mcrypt')) {
|
} elseif (extension_loaded('mcrypt')) {
|
||||||
@ -1141,11 +1148,24 @@ class SSH2
|
|||||||
function _key_exchange($kexinit_payload_server)
|
function _key_exchange($kexinit_payload_server)
|
||||||
{
|
{
|
||||||
static $kex_algorithms = array(
|
static $kex_algorithms = array(
|
||||||
|
// Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using
|
||||||
|
// Curve25519. See doc/curve25519-sha256@libssh.org.txt in the
|
||||||
|
// libssh repository for more information.
|
||||||
|
'curve25519-sha256@libssh.org',
|
||||||
|
|
||||||
|
// Diffie-Hellman Key Agreement (DH) using integer modulo prime
|
||||||
|
// groups.
|
||||||
'diffie-hellman-group1-sha1', // REQUIRED
|
'diffie-hellman-group1-sha1', // REQUIRED
|
||||||
'diffie-hellman-group14-sha1', // REQUIRED
|
'diffie-hellman-group14-sha1', // REQUIRED
|
||||||
'diffie-hellman-group-exchange-sha1', // RFC 4419
|
'diffie-hellman-group-exchange-sha1', // RFC 4419
|
||||||
'diffie-hellman-group-exchange-sha256', // RFC 4419
|
'diffie-hellman-group-exchange-sha256', // RFC 4419
|
||||||
);
|
);
|
||||||
|
if (!class_exists('\Sodium')) {
|
||||||
|
$kex_algorithms = array_diff(
|
||||||
|
$kex_algorithms,
|
||||||
|
array('curve25519-sha256@libssh.org')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static $server_host_key_algorithms = array(
|
static $server_host_key_algorithms = array(
|
||||||
'ssh-rsa', // RECOMMENDED sign Raw RSA Key
|
'ssh-rsa', // RECOMMENDED sign Raw RSA Key
|
||||||
@ -1359,115 +1379,124 @@ class SSH2
|
|||||||
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
|
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
$keyLength = $decryptKeyLength > $encryptKeyLength ? $decryptKeyLength : $encryptKeyLength;
|
|
||||||
|
|
||||||
// through diffie-hellman key exchange a symmetric key is obtained
|
// through diffie-hellman key exchange a symmetric key is obtained
|
||||||
$kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms);
|
$kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms);
|
||||||
if ($kex_algorithm === false) {
|
if ($kex_algorithm === false) {
|
||||||
user_error('No compatible key exchange algorithms found');
|
user_error('No compatible key exchange algorithms found');
|
||||||
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
|
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
|
||||||
}
|
}
|
||||||
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();
|
// Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty.
|
||||||
if ($response === false) {
|
$exchange_hash_rfc4419 = '';
|
||||||
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)));
|
if ($kex_algorithm === 'curve25519-sha256@libssh.org') {
|
||||||
$primeBytes = $this->_string_shift($response, $primeLength);
|
$x = Random::string(32);
|
||||||
$prime = new BigInteger($primeBytes, -256);
|
$eBytes = \Sodium::crypto_box_publickey_from_secretkey($x);
|
||||||
|
$clientKexInitMessage = NET_SSH2_MSG_KEX_ECDH_INIT;
|
||||||
extract(unpack('NgLength', $this->_string_shift($response, 4)));
|
$serverKexReplyMessage = NET_SSH2_MSG_KEX_ECDH_REPLY;
|
||||||
$gBytes = $this->_string_shift($response, $gLength);
|
$kexHash = new Hash('sha256');
|
||||||
$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 {
|
} else {
|
||||||
switch ($kex_algorithm) {
|
if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
|
||||||
// see http://tools.ietf.org/html/rfc2409#section-6.2 and
|
$dh_group_sizes_packed = pack(
|
||||||
// http://tools.ietf.org/html/rfc2412, appendex E
|
'NNN',
|
||||||
case 'diffie-hellman-group1-sha1':
|
$this->kex_dh_group_size_min,
|
||||||
$prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
|
$this->kex_dh_group_size_preferred,
|
||||||
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
|
$this->kex_dh_group_size_max
|
||||||
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
|
);
|
||||||
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
|
$packet = pack(
|
||||||
break;
|
'Ca*',
|
||||||
// see http://tools.ietf.org/html/rfc3526#section-3
|
NET_SSH2_MSG_KEXDH_GEX_REQUEST,
|
||||||
case 'diffie-hellman-group14-sha1':
|
$dh_group_sizes_packed
|
||||||
$prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
|
);
|
||||||
'020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
|
if (!$this->_send_binary_packet($packet)) {
|
||||||
'4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
|
return false;
|
||||||
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
|
}
|
||||||
'98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
|
|
||||||
'9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
|
$response = $this->_get_binary_packet();
|
||||||
'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
|
if ($response === false) {
|
||||||
'3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
|
user_error('Connection closed by server');
|
||||||
break;
|
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
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
// For both diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1
|
|
||||||
// the generator field element is 2 (decimal) and the hash function is sha1.
|
switch ($kex_algorithm) {
|
||||||
$g = new BigInteger(2);
|
case 'diffie-hellman-group-exchange-sha256':
|
||||||
$prime = new BigInteger($prime, 16);
|
$kexHash = new Hash('sha256');
|
||||||
$exchange_hash_rfc4419 = '';
|
break;
|
||||||
$clientKexInitMessage = NET_SSH2_MSG_KEXDH_INIT;
|
default:
|
||||||
$serverKexReplyMessage = NET_SSH2_MSG_KEXDH_REPLY;
|
$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);
|
||||||
}
|
}
|
||||||
|
|
||||||
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($keyLength, $kexHash->getLength());
|
|
||||||
$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);
|
$data = pack('CNa*', $clientKexInitMessage, strlen($eBytes), $eBytes);
|
||||||
|
|
||||||
if (!$this->_send_binary_packet($data)) {
|
if (!$this->_send_binary_packet($data)) {
|
||||||
@ -1495,7 +1524,6 @@ class SSH2
|
|||||||
|
|
||||||
$temp = unpack('Nlength', $this->_string_shift($response, 4));
|
$temp = unpack('Nlength', $this->_string_shift($response, 4));
|
||||||
$fBytes = $this->_string_shift($response, $temp['length']);
|
$fBytes = $this->_string_shift($response, $temp['length']);
|
||||||
$f = new BigInteger($fBytes, -256);
|
|
||||||
|
|
||||||
$temp = unpack('Nlength', $this->_string_shift($response, 4));
|
$temp = unpack('Nlength', $this->_string_shift($response, 4));
|
||||||
$this->signature = $this->_string_shift($response, $temp['length']);
|
$this->signature = $this->_string_shift($response, $temp['length']);
|
||||||
@ -1503,7 +1531,17 @@ class SSH2
|
|||||||
$temp = unpack('Nlength', $this->_string_shift($this->signature, 4));
|
$temp = unpack('Nlength', $this->_string_shift($this->signature, 4));
|
||||||
$this->signature_format = $this->_string_shift($this->signature, $temp['length']);
|
$this->signature_format = $this->_string_shift($this->signature, $temp['length']);
|
||||||
|
|
||||||
$key = $f->modPow($x, $prime);
|
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);
|
$keyBytes = $key->toBytes(true);
|
||||||
|
|
||||||
$this->exchange_hash = pack(
|
$this->exchange_hash = pack(
|
||||||
|
@ -39,7 +39,11 @@ class Unit_Net_SSH2Test extends PhpseclibTestCase
|
|||||||
public function testGenerateIdentifier()
|
public function testGenerateIdentifier()
|
||||||
{
|
{
|
||||||
$identifier = $this->createSSHMock()->_generate_identifier();
|
$identifier = $this->createSSHMock()->_generate_identifier();
|
||||||
$this->assertStringStartsWith('SSH-2.0-phpseclib_0.3', $identifier);
|
$this->assertStringStartsWith('SSH-2.0-phpseclib_2.0', $identifier);
|
||||||
|
|
||||||
|
if (extension_loaded('libsodium')) {
|
||||||
|
$this->assertContains('libsodium', $identifier);
|
||||||
|
}
|
||||||
|
|
||||||
if (extension_loaded('openssl')) {
|
if (extension_loaded('openssl')) {
|
||||||
$this->assertContains('openssl', $identifier);
|
$this->assertContains('openssl', $identifier);
|
||||||
|
Loading…
Reference in New Issue
Block a user