Merge pull request #755 from bantu/ssh2-libsodium

[2.0] SSH2: Add support for curve25519-sha256@libssh.org.txt via libsodium-php.

* bantu/ssh2-libsodium:
  SSH2: Add support for curve25519-sha256@libssh.org.txt via libsodium-php.
This commit is contained in:
Andreas Fischer 2015-07-28 00:56:19 +02:00
commit cafaa9f5a6
4 changed files with 147 additions and 104 deletions

View File

@ -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
View File

@ -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": [
{ {

View File

@ -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,14 +1379,23 @@ 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);
} }
// 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) { if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
$dh_group_sizes_packed = pack( $dh_group_sizes_packed = pack(
'NNN', 'NNN',
@ -1439,7 +1468,6 @@ class SSH2
// the generator field element is 2 (decimal) and the hash function is sha1. // the generator field element is 2 (decimal) and the hash function is sha1.
$g = new BigInteger(2); $g = new BigInteger(2);
$prime = new BigInteger($prime, 16); $prime = new BigInteger($prime, 16);
$exchange_hash_rfc4419 = '';
$clientKexInitMessage = NET_SSH2_MSG_KEXDH_INIT; $clientKexInitMessage = NET_SSH2_MSG_KEXDH_INIT;
$serverKexReplyMessage = NET_SSH2_MSG_KEXDH_REPLY; $serverKexReplyMessage = NET_SSH2_MSG_KEXDH_REPLY;
} }
@ -1460,7 +1488,7 @@ class SSH2
-- http://tools.ietf.org/html/rfc4419#section-6.2 */ -- http://tools.ietf.org/html/rfc4419#section-6.2 */
$one = new BigInteger(1); $one = new BigInteger(1);
$keyLength = min($keyLength, $kexHash->getLength()); $keyLength = min($kexHash->getLength(), max($encryptKeyLength, $decryptKeyLength));
$max = $one->bitwise_leftShift(16 * $keyLength); // 2 * 8 * $keyLength $max = $one->bitwise_leftShift(16 * $keyLength); // 2 * 8 * $keyLength
$max = $max->subtract($one); $max = $max->subtract($one);
@ -1468,6 +1496,7 @@ class SSH2
$e = $g->modPow($x, $prime); $e = $g->modPow($x, $prime);
$eBytes = $e->toBytes(true); $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']);
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); $key = $f->modPow($x, $prime);
}
$keyBytes = $key->toBytes(true); $keyBytes = $key->toBytes(true);
$this->exchange_hash = pack( $this->exchange_hash = pack(

View File

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