Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/3rdparty.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCôme Chilliet <come.chilliet@nextcloud.com>2022-09-05 15:59:43 +0300
committerCôme Chilliet <come.chilliet@nextcloud.com>2022-09-05 15:59:43 +0300
commit1387b8c56a88ef7228bafc520cf3341b0d5f1a7a (patch)
treeeb20af877154d87c3138a7fda34a8d1f6861a32d
parentd6a35b6d5759c08dd268618951f9e5b1c18aa939 (diff)
Bump phpseclib/phpseclib to 2.0.38
Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
-rw-r--r--composer.json2
-rw-r--r--composer.lock17
-rw-r--r--composer/installed.json17
-rw-r--r--composer/installed.php10
-rw-r--r--phpseclib/phpseclib/AUTHORS1
-rw-r--r--phpseclib/phpseclib/phpseclib/Crypt/Base.php214
-rw-r--r--phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php439
-rw-r--r--phpseclib/phpseclib/phpseclib/Crypt/DES.php4
-rw-r--r--phpseclib/phpseclib/phpseclib/Crypt/RC2.php2
-rw-r--r--phpseclib/phpseclib/phpseclib/Crypt/RSA.php123
-rw-r--r--phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php39
-rw-r--r--phpseclib/phpseclib/phpseclib/Crypt/Twofish.php36
-rw-r--r--phpseclib/phpseclib/phpseclib/File/ASN1.php9
-rw-r--r--phpseclib/phpseclib/phpseclib/File/X509.php9
-rw-r--r--phpseclib/phpseclib/phpseclib/Math/BigInteger.php2
-rw-r--r--phpseclib/phpseclib/phpseclib/Net/SFTP.php804
-rw-r--r--phpseclib/phpseclib/phpseclib/Net/SSH2.php370
-rw-r--r--phpseclib/phpseclib/phpseclib/System/SSH/Agent.php3
-rw-r--r--phpseclib/phpseclib/phpseclib/bootstrap.php3
19 files changed, 1806 insertions, 298 deletions
diff --git a/composer.json b/composer.json
index 0cc064d8..1dbdd417 100644
--- a/composer.json
+++ b/composer.json
@@ -41,7 +41,7 @@
"php-ds/php-ds": "^1.3",
"php-http/guzzle7-adapter": "^1.0.0",
"php-opencloud/openstack": "^3.1",
- "phpseclib/phpseclib": "2.0.32",
+ "phpseclib/phpseclib": "^2.0.38",
"pimple/pimple": "^3.5.0",
"psr/container": "^1.1.1",
"psr/event-dispatcher": "^1.0",
diff --git a/composer.lock b/composer.lock
index 9a646905..2be33eee 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "2297310d6ca23ae439e3c5d2642b5331",
+ "content-hash": "682bfa95cb2df85d82d78f313aebd5fd",
"packages": [
{
"name": "aws/aws-sdk-php",
@@ -3016,16 +3016,16 @@
},
{
"name": "phpseclib/phpseclib",
- "version": "2.0.32",
+ "version": "2.0.38",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
- "reference": "f5c4c19880d45d0be3e7d24ae8ac434844a898cd"
+ "reference": "b03536539f43a4f9aa33c4f0b2f3a1c752088fcd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/f5c4c19880d45d0be3e7d24ae8ac434844a898cd",
- "reference": "f5c4c19880d45d0be3e7d24ae8ac434844a898cd",
+ "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/b03536539f43a4f9aa33c4f0b2f3a1c752088fcd",
+ "reference": "b03536539f43a4f9aa33c4f0b2f3a1c752088fcd",
"shasum": ""
},
"require": {
@@ -3040,7 +3040,8 @@
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
"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 few other cryptographic operations.",
- "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
+ "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations.",
+ "ext-xml": "Install the XML extension to load XML formatted public keys."
},
"type": "library",
"autoload": {
@@ -3105,7 +3106,7 @@
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
- "source": "https://github.com/phpseclib/phpseclib/tree/2.0.32"
+ "source": "https://github.com/phpseclib/phpseclib/tree/2.0.38"
},
"funding": [
{
@@ -3121,7 +3122,7 @@
"type": "tidelift"
}
],
- "time": "2021-06-12T12:12:59+00:00"
+ "time": "2022-09-02T17:04:26+00:00"
},
{
"name": "pimple/pimple",
diff --git a/composer/installed.json b/composer/installed.json
index 1cb2e013..1def2305 100644
--- a/composer/installed.json
+++ b/composer/installed.json
@@ -3148,17 +3148,17 @@
},
{
"name": "phpseclib/phpseclib",
- "version": "2.0.32",
- "version_normalized": "2.0.32.0",
+ "version": "2.0.38",
+ "version_normalized": "2.0.38.0",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
- "reference": "f5c4c19880d45d0be3e7d24ae8ac434844a898cd"
+ "reference": "b03536539f43a4f9aa33c4f0b2f3a1c752088fcd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/f5c4c19880d45d0be3e7d24ae8ac434844a898cd",
- "reference": "f5c4c19880d45d0be3e7d24ae8ac434844a898cd",
+ "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/b03536539f43a4f9aa33c4f0b2f3a1c752088fcd",
+ "reference": "b03536539f43a4f9aa33c4f0b2f3a1c752088fcd",
"shasum": ""
},
"require": {
@@ -3173,9 +3173,10 @@
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
"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 few other cryptographic operations.",
- "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
+ "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations.",
+ "ext-xml": "Install the XML extension to load XML formatted public keys."
},
- "time": "2021-06-12T12:12:59+00:00",
+ "time": "2022-09-02T17:04:26+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -3240,7 +3241,7 @@
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
- "source": "https://github.com/phpseclib/phpseclib/tree/2.0.32"
+ "source": "https://github.com/phpseclib/phpseclib/tree/2.0.38"
},
"funding": [
{
diff --git a/composer/installed.php b/composer/installed.php
index 832b77ba..bd2d7725 100644
--- a/composer/installed.php
+++ b/composer/installed.php
@@ -3,7 +3,7 @@
'name' => 'nextcloud/3rdparty',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => '143faae0eba0121682129d1261158dd9db74b2b2',
+ 'reference' => 'd6a35b6d5759c08dd268618951f9e5b1c18aa939',
'type' => 'library',
'install_path' => __DIR__ . '/../',
'aliases' => array(),
@@ -319,7 +319,7 @@
'nextcloud/3rdparty' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => '143faae0eba0121682129d1261158dd9db74b2b2',
+ 'reference' => 'd6a35b6d5759c08dd268618951f9e5b1c18aa939',
'type' => 'library',
'install_path' => __DIR__ . '/../',
'aliases' => array(),
@@ -452,9 +452,9 @@
'dev_requirement' => false,
),
'phpseclib/phpseclib' => array(
- 'pretty_version' => '2.0.32',
- 'version' => '2.0.32.0',
- 'reference' => 'f5c4c19880d45d0be3e7d24ae8ac434844a898cd',
+ 'pretty_version' => '2.0.38',
+ 'version' => '2.0.38.0',
+ 'reference' => 'b03536539f43a4f9aa33c4f0b2f3a1c752088fcd',
'type' => 'library',
'install_path' => __DIR__ . '/../phpseclib/phpseclib',
'aliases' => array(),
diff --git a/phpseclib/phpseclib/AUTHORS b/phpseclib/phpseclib/AUTHORS
index a08b3099..9f10d267 100644
--- a/phpseclib/phpseclib/AUTHORS
+++ b/phpseclib/phpseclib/AUTHORS
@@ -4,3 +4,4 @@ phpseclib Developers: monnerat (Patrick Monnerat)
bantu (Andreas Fischer)
petrich (Hans-Jürgen Petrich)
GrahamCampbell (Graham Campbell)
+ hc-jworman \ No newline at end of file
diff --git a/phpseclib/phpseclib/phpseclib/Crypt/Base.php b/phpseclib/phpseclib/phpseclib/Crypt/Base.php
index 8822b9b8..05ffa636 100644
--- a/phpseclib/phpseclib/phpseclib/Crypt/Base.php
+++ b/phpseclib/phpseclib/phpseclib/Crypt/Base.php
@@ -79,7 +79,11 @@ abstract class Base
/**
* Encrypt / decrypt using the Cipher Feedback mode (8bit)
*/
- const MODE_CFB8 = 38;
+ const MODE_CFB8 = 6;
+ /**
+ * Encrypt / decrypt using the Output Feedback mode (8bit)
+ */
+ const MODE_OFB8 = 7;
/**
* Encrypt / decrypt using the Output Feedback mode.
*
@@ -152,7 +156,7 @@ abstract class Base
* @var string
* @access private
*/
- var $iv;
+ var $iv = '';
/**
* A "sliding" Initialization Vector
@@ -484,6 +488,7 @@ abstract class Base
case self::MODE_CTR:
case self::MODE_CFB:
case self::MODE_CFB8:
+ case self::MODE_OFB8:
case self::MODE_OFB:
case self::MODE_STREAM:
$this->mode = $mode;
@@ -495,6 +500,44 @@ abstract class Base
}
$this->_setEngine();
+
+ // Determining whether inline crypting can be used by the cipher
+ if ($this->use_inline_crypt !== false) {
+ $this->use_inline_crypt = version_compare(PHP_VERSION, '5.3.0') >= 0 || function_exists('create_function');
+ }
+
+ if (!defined('PHP_INT_SIZE')) {
+ define('PHP_INT_SIZE', 4);
+ }
+
+ if (!defined('CRYPT_BASE_USE_REG_INTVAL')) {
+ switch (true) {
+ // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster
+ case (PHP_OS & "\xDF\xDF\xDF") === 'WIN':
+ case (php_uname('m') & "\xDF\xDF\xDF") != 'ARM':
+ case PHP_INT_SIZE == 8:
+ define('CRYPT_BASE_USE_REG_INTVAL', true);
+ break;
+ case (php_uname('m') & "\xDF\xDF\xDF") == 'ARM':
+ switch (true) {
+ /* PHP 7.0.0 introduced a bug that affected 32-bit ARM processors:
+
+ https://github.com/php/php-src/commit/716da71446ebbd40fa6cf2cea8a4b70f504cc3cd
+
+ altho the changelogs make no mention of it, this bug was fixed with this commit:
+
+ https://github.com/php/php-src/commit/c1729272b17a1fe893d1a54e423d3b71470f3ee8
+
+ affected versions of PHP are: 7.0.x, 7.1.0 - 7.1.23 and 7.2.0 - 7.2.11 */
+ case PHP_VERSION_ID >= 70000 && PHP_VERSION_ID <= 70123:
+ case PHP_VERSION_ID >= 70200 && PHP_VERSION_ID <= 70211:
+ define('CRYPT_BASE_USE_REG_INTVAL', false);
+ break;
+ default:
+ define('CRYPT_BASE_USE_REG_INTVAL', true);
+ }
+ }
+ }
}
/**
@@ -588,6 +631,10 @@ abstract class Base
* $hash, $salt, $count, $dkLen
*
* Where $hash (default = sha1) currently supports the following hashes: see: Crypt/Hash.php
+ * {@link https://en.wikipedia.org/wiki/Bcrypt bcypt}:
+ * $salt, $rounds, $keylen
+ *
+ * This is a modified version of bcrypt used by OpenSSH.
*
* @see Crypt/Hash.php
* @param string $password
@@ -601,6 +648,28 @@ abstract class Base
$key = '';
switch ($method) {
+ case 'bcrypt':
+ $func_args = func_get_args();
+
+ if (!isset($func_args[2])) {
+ return false;
+ }
+
+ $salt = $func_args[2];
+
+ $rounds = isset($func_args[3]) ? $func_args[3] : 16;
+ $keylen = isset($func_args[4]) ? $func_args[4] : $this->key_length;
+
+ $bf = new Blowfish();
+ $key = $bf->bcrypt_pbkdf($password, $salt, $keylen + $this->block_size, $rounds);
+ if (!$key) {
+ return false;
+ }
+
+ $this->setKey(substr($key, 0, $keylen));
+ $this->setIV(substr($key, $keylen));
+
+ return true;
default: // 'pbkdf2' or 'pbkdf1'
$func_args = func_get_args();
@@ -773,6 +842,22 @@ abstract class Base
}
}
return $ciphertext;
+ case self::MODE_OFB8:
+ // OpenSSL has built in support for cfb8 but not ofb8
+ $ciphertext = '';
+ $len = strlen($plaintext);
+ $iv = $this->encryptIV;
+
+ for ($i = 0; $i < $len; ++$i) {
+ $xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV);
+ $ciphertext.= $plaintext[$i] ^ $xor;
+ $iv = substr($iv, 1) . $xor[0];
+ }
+
+ if ($this->continuousBuffer) {
+ $this->encryptIV = $iv;
+ }
+ break;
case self::MODE_OFB:
return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer);
}
@@ -896,8 +981,8 @@ abstract class Base
$block = substr($plaintext, $i, $block_size);
if (strlen($block) > strlen($buffer['ciphertext'])) {
$buffer['ciphertext'].= $this->_encryptBlock($xor);
+ $this->_increment_str($xor);
}
- $this->_increment_str($xor);
$key = $this->_string_shift($buffer['ciphertext'], $block_size);
$ciphertext.= $block ^ $key;
}
@@ -959,12 +1044,14 @@ abstract class Base
}
break;
case self::MODE_CFB8:
+ // compared to regular CFB, which encrypts a block at a time,
+ // here, we're encrypting a byte at a time
$ciphertext = '';
$len = strlen($plaintext);
$iv = $this->encryptIV;
for ($i = 0; $i < $len; ++$i) {
- $ciphertext .= ($c = $plaintext[$i] ^ $this->_encryptBlock($iv));
+ $ciphertext.= ($c = $plaintext[$i] ^ $this->_encryptBlock($iv));
$iv = substr($iv, 1) . $c;
}
@@ -976,6 +1063,21 @@ abstract class Base
}
}
break;
+ case self::MODE_OFB8:
+ $ciphertext = '';
+ $len = strlen($plaintext);
+ $iv = $this->encryptIV;
+
+ for ($i = 0; $i < $len; ++$i) {
+ $xor = $this->_encryptBlock($iv);
+ $ciphertext.= $plaintext[$i] ^ $xor;
+ $iv = substr($iv, 1) . $xor[0];
+ }
+
+ if ($this->continuousBuffer) {
+ $this->encryptIV = $iv;
+ }
+ break;
case self::MODE_OFB:
$xor = $this->encryptIV;
if (strlen($buffer['xor'])) {
@@ -1067,7 +1169,7 @@ abstract class Base
$plaintext = '';
if ($this->continuousBuffer) {
$iv = &$this->decryptIV;
- $pos = &$this->buffer['pos'];
+ $pos = &$this->debuffer['pos'];
} else {
$iv = $this->decryptIV;
$pos = 0;
@@ -1116,6 +1218,21 @@ abstract class Base
}
}
break;
+ case self::MODE_OFB8:
+ $plaintext = '';
+ $len = strlen($ciphertext);
+ $iv = $this->decryptIV;
+
+ for ($i = 0; $i < $len; ++$i) {
+ $xor = openssl_encrypt($iv, $this->cipher_name_openssl_ecb, $this->key, $this->openssl_options, $this->decryptIV);
+ $plaintext.= $ciphertext[$i] ^ $xor;
+ $iv = substr($iv, 1) . $xor[0];
+ }
+
+ if ($this->continuousBuffer) {
+ $this->decryptIV = $iv;
+ }
+ break;
case self::MODE_OFB:
$plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer);
}
@@ -1290,7 +1407,7 @@ abstract class Base
$iv = $this->decryptIV;
for ($i = 0; $i < $len; ++$i) {
- $plaintext .= $ciphertext[$i] ^ $this->_encryptBlock($iv);
+ $plaintext.= $ciphertext[$i] ^ $this->_encryptBlock($iv);
$iv = substr($iv, 1) . $ciphertext[$i];
}
@@ -1302,6 +1419,21 @@ abstract class Base
}
}
break;
+ case self::MODE_OFB8:
+ $plaintext = '';
+ $len = strlen($ciphertext);
+ $iv = $this->decryptIV;
+
+ for ($i = 0; $i < $len; ++$i) {
+ $xor = $this->_encryptBlock($iv);
+ $plaintext.= $ciphertext[$i] ^ $xor;
+ $iv = substr($iv, 1) . $xor[0];
+ }
+
+ if ($this->continuousBuffer) {
+ $this->decryptIV = $iv;
+ }
+ break;
case self::MODE_OFB:
$xor = $this->decryptIV;
if (strlen($buffer['xor'])) {
@@ -1864,6 +1996,7 @@ abstract class Base
self::MODE_CFB => 'ncfb',
self::MODE_CFB8 => MCRYPT_MODE_CFB,
self::MODE_OFB => MCRYPT_MODE_NOFB,
+ self::MODE_OFB8 => MCRYPT_MODE_OFB,
self::MODE_STREAM => MCRYPT_MODE_STREAM,
);
@@ -2009,6 +2142,13 @@ abstract class Base
*/
function _increment_str(&$var)
{
+ if (function_exists('sodium_increment')) {
+ $var = strrev($var);
+ sodium_increment($var);
+ $var = strrev($var);
+ return;
+ }
+
for ($i = 4; $i <= strlen($var); $i+= 4) {
$temp = substr($var, -$i, 4);
switch ($temp) {
@@ -2446,7 +2586,7 @@ abstract class Base
for ($_i = 0; $_i < $_len; ++$_i) {
$in = $_iv;
'.$encrypt_block.'
- $_ciphertext .= ($_c = $_text[$_i] ^ $in);
+ $_ciphertext.= ($_c = $_text[$_i] ^ $in);
$_iv = substr($_iv, 1) . $_c;
}
@@ -2468,7 +2608,7 @@ abstract class Base
for ($_i = 0; $_i < $_len; ++$_i) {
$in = $_iv;
'.$encrypt_block.'
- $_plaintext .= $_text[$_i] ^ $in;
+ $_plaintext.= $_text[$_i] ^ $in;
$_iv = substr($_iv, 1) . $_text[$_i];
}
@@ -2483,6 +2623,44 @@ abstract class Base
return $_plaintext;
';
break;
+ case self::MODE_OFB8:
+ $encrypt = $init_encrypt . '
+ $_ciphertext = "";
+ $_len = strlen($_text);
+ $_iv = $self->encryptIV;
+
+ for ($_i = 0; $_i < $_len; ++$_i) {
+ $in = $_iv;
+ '.$encrypt_block.'
+ $_ciphertext.= $_text[$_i] ^ $in;
+ $_iv = substr($_iv, 1) . $in[0];
+ }
+
+ if ($self->continuousBuffer) {
+ $self->encryptIV = $_iv;
+ }
+
+ return $_ciphertext;
+ ';
+ $decrypt = $init_encrypt . '
+ $_plaintext = "";
+ $_len = strlen($_text);
+ $_iv = $self->decryptIV;
+
+ for ($_i = 0; $_i < $_len; ++$_i) {
+ $in = $_iv;
+ '.$encrypt_block.'
+ $_plaintext.= $_text[$_i] ^ $in;
+ $_iv = substr($_iv, 1) . $in[0];
+ }
+
+ if ($self->continuousBuffer) {
+ $self->decryptIV = $_iv;
+ }
+
+ return $_plaintext;
+ ';
+ break;
case self::MODE_OFB:
$encrypt = $init_encrypt . '
$_ciphertext = "";
@@ -2684,11 +2862,8 @@ abstract class Base
*/
function safe_intval($x)
{
- switch (true) {
- case is_int($x):
- // PHP 5.3, per http://php.net/releases/5_3_0.php, introduced "more consistent float rounding"
- case (php_uname('m') & "\xDF\xDF\xDF") != 'ARM':
- return $x;
+ if (is_int($x)) {
+ return $x;
}
return (fmod($x, 0x80000000) & 0x7FFFFFFF) |
((fmod(floor($x / 0x80000000), 2) & 1) << 31);
@@ -2702,15 +2877,12 @@ abstract class Base
*/
function safe_intval_inline()
{
- switch (true) {
- case defined('PHP_INT_SIZE') && PHP_INT_SIZE == 8:
- case (php_uname('m') & "\xDF\xDF\xDF") != 'ARM':
- return '%s';
- break;
- default:
- $safeint = '(is_int($temp = %s) ? $temp : (fmod($temp, 0x80000000) & 0x7FFFFFFF) | ';
- return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))';
+ if (CRYPT_BASE_USE_REG_INTVAL) {
+ return PHP_INT_SIZE == 4 ? 'intval(%s)' : '%s';
}
+
+ $safeint = '(is_int($temp = %s) ? $temp : (fmod($temp, 0x80000000) & 0x7FFFFFFF) | ';
+ return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))';
}
/**
diff --git a/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php b/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php
index 74cc49de..78e6367e 100644
--- a/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php
+++ b/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php
@@ -11,6 +11,87 @@
*
* - {@link http://en.wikipedia.org/wiki/Blowfish_(cipher) Wikipedia description of Blowfish}
*
+ * # An overview of bcrypt vs Blowfish
+ *
+ * OpenSSH private keys use a customized version of bcrypt. Specifically, instead of
+ * encrypting OrpheanBeholderScryDoubt 64 times OpenSSH's bcrypt variant encrypts
+ * OxychromaticBlowfishSwatDynamite 64 times. so we can't use crypt().
+ *
+ * bcrypt is basically Blowfish but instead of performing the key expansion once it performs
+ * the expansion 129 times for each round, with the first key expansion interleaving the salt
+ * and password. This renders OpenSSL unusable and forces us to use a pure-PHP implementation
+ * of blowfish.
+ *
+ * # phpseclib's four different _encryptBlock() implementations
+ *
+ * When using Blowfish as an encryption algorithm, _encryptBlock() is called 9 + 512 +
+ * (the number of blocks in the plaintext) times.
+ *
+ * Each of the first 9 calls to _encryptBlock() modify the P-array. Each of the next 512
+ * calls modify the S-boxes. The remaining _encryptBlock() calls operate on the plaintext to
+ * produce the ciphertext. In the pure-PHP implementation of Blowfish these remaining
+ * _encryptBlock() calls are highly optimized through the use of eval(). Among other things,
+ * P-array lookups are eliminated by hard-coding the key-dependent P-array values, and thus we
+ * have explained 2 of the 4 different _encryptBlock() implementations.
+ *
+ * With bcrypt things are a bit different. _encryptBlock() is called 1,079,296 times,
+ * assuming 16 rounds (which is what OpenSSH's bcrypt defaults to). The eval()-optimized
+ * _encryptBlock() isn't as beneficial because the P-array values are not constant. Well, they
+ * are constant, but only for, at most, 777 _encryptBlock() calls, which is equivalent to ~6KB
+ * of data. The average length of back to back _encryptBlock() calls with a fixed P-array is
+ * 514.12, which is ~4KB of data. Creating an eval()-optimized _encryptBlock() has an upfront
+ * cost, which is CPU dependent and is probably not going to be worth it for just ~4KB of
+ * data. Conseqeuently, bcrypt does not benefit from the eval()-optimized _encryptBlock().
+ *
+ * The regular _encryptBlock() does unpack() and pack() on every call, as well, and that can
+ * begin to add up after one million function calls.
+ *
+ * In theory, one might think that it might be beneficial to rewrite all block ciphers so
+ * that, instead of passing strings to _encryptBlock(), you convert the string to an array of
+ * integers and then pass successive subarrays of that array to _encryptBlock. This, however,
+ * kills PHP's memory use. Like let's say you have a 1MB long string. After doing
+ * $in = str_repeat('a', 1024 * 1024); PHP's memory utilization jumps up by ~1MB. After doing
+ * $blocks = str_split($in, 4); it jumps up by an additional ~16MB. After
+ * $blocks = array_map(fn($x) => unpack('N*', $x), $blocks); it jumps up by an additional
+ * ~90MB, yielding a 106x increase in memory usage. Consequently, it bcrypt calls a different
+ * _encryptBlock() then the regular Blowfish does. That said, the Blowfish _encryptBlock() is
+ * basically just a thin wrapper around the bcrypt _encryptBlock(), so there's that.
+ *
+ * This explains 3 of the 4 _encryptBlock() implementations. the last _encryptBlock()
+ * implementation can best be understood by doing Ctrl + F and searching for where
+ * CRYPT_BASE_USE_REG_INTVAL is defined.
+ *
+ * # phpseclib's three different _setupKey() implementations
+ *
+ * Every bcrypt round is the equivalent of encrypting 512KB of data. Since OpenSSH uses 16
+ * rounds by default that's ~8MB of data that's essentially being encrypted whenever
+ * you use bcrypt. That's a lot of data, however, bcrypt operates within tighter constraints
+ * than regular Blowfish, so we can use that to our advantage. In particular, whereas Blowfish
+ * supports variable length keys, in bcrypt, the initial "key" is the sha512 hash of the
+ * password. sha512 hashes are 512 bits or 64 bytes long and thus the bcrypt keys are of a
+ * fixed length whereas Blowfish keys are not of a fixed length.
+ *
+ * bcrypt actually has two different key expansion steps. The first one (expandstate) is
+ * constantly XOR'ing every _encryptBlock() parameter against the salt prior _encryptBlock()'s
+ * being called. The second one (expand0state) is more similar to Blowfish's _setupKey()
+ * but it can still use the fixed length key optimization discussed above and can do away with
+ * the pack() / unpack() calls.
+ *
+ * I suppose _setupKey() could be made to be a thin wrapper around expandstate() but idk it's
+ * just a lot of work for very marginal benefits as _setupKey() is only called once for
+ * regular Blowfish vs the 128 times it's called --per round-- with bcrypt.
+ *
+ * # blowfish + bcrypt in the same class
+ *
+ * Altho there's a lot of Blowfish code that bcrypt doesn't re-use, bcrypt does re-use the
+ * initial S-boxes, the initial P-array and the int-only _encryptBlock() implementation.
+ *
+ * # Credit
+ *
+ * phpseclib's bcrypt implementation is based losely off of OpenSSH's implementation:
+ *
+ * https://github.com/openssh/openssh-portable/blob/master/openbsd-compat/bcrypt_pbkdf.c
+ *
* Here's a short example of how to use this library:
* <code>
* <?php
@@ -75,6 +156,15 @@ class Blowfish extends Base
var $cfb_init_len = 500;
/**
+ * SHA512 Object
+ *
+ * @see self::bcrypt_pbkdf
+ * @var object
+ * @access private
+ */
+ var $sha512;
+
+ /**
* The fixed subkeys boxes ($sbox0 - $sbox3) with 256 entries each
*
* S-Box 0
@@ -284,6 +374,41 @@ class Blowfish extends Base
var $key_length = 16;
/**
+ * Default Constructor.
+ *
+ * Determines whether or not the mcrypt extension should be used.
+ *
+ * $mode could be:
+ *
+ * - CRYPT_MODE_ECB
+ *
+ * - CRYPT_MODE_CBC
+ *
+ * - CRYPT_MODE_CTR
+ *
+ * - CRYPT_MODE_CFB
+ *
+ * - CRYPT_MODE_OFB
+ *
+ * (or the alias constants of the chosen cipher, for example for AES: CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC ...)
+ *
+ * If not explicitly set, CRYPT_MODE_CBC will be used.
+ *
+ * @param int $mode
+ * @access public
+ */
+ function __construct($mode = self::MODE_CBC)
+ {
+ parent::__construct($mode);
+
+ $this->sbox0 = array_map('intval', $this->sbox0);
+ $this->sbox1 = array_map('intval', $this->sbox1);
+ $this->sbox2 = array_map('intval', $this->sbox2);
+ $this->sbox3 = array_map('intval', $this->sbox3);
+ $this->parray = array_map('intval', $this->parray);
+ }
+
+ /**
* Sets the key length.
*
* Key lengths can be between 32 and 448 bits.
@@ -358,6 +483,7 @@ class Blowfish extends Base
// unpack binary string in unsigned chars
$key = array_values(unpack('C*', $this->key));
$keyl = count($key);
+ // with bcrypt $keyl will always be 16 (because the key is the sha512 of the key you provide)
for ($j = 0, $i = 0; $i < 18; ++$i) {
// xor P1 with the first 32-bits of the key, xor P2 with the second 32-bits ...
for ($data = 0, $k = 0; $k < 4; ++$k) {
@@ -366,7 +492,7 @@ class Blowfish extends Base
$j = 0;
}
}
- $this->bctx['p'][] = $this->parray[$i] ^ $data;
+ $this->bctx['p'][] = $this->parray[$i] ^ intval($data);
}
// encrypt the zero-string, replace P1 and P2 with the encrypted data,
@@ -387,6 +513,230 @@ class Blowfish extends Base
}
/**
+ * bcrypt
+ *
+ * @param string $sha2pass
+ * @param string $sha2salt
+ * @access private
+ * @return string
+ */
+ function _bcrypt_hash($sha2pass, $sha2salt)
+ {
+ $p = $this->parray;
+ $sbox0 = $this->sbox0;
+ $sbox1 = $this->sbox1;
+ $sbox2 = $this->sbox2;
+ $sbox3 = $this->sbox3;
+
+ $cdata = array_values(unpack('N*', 'OxychromaticBlowfishSwatDynamite'));
+ $sha2pass = array_values(unpack('N*', $sha2pass));
+ $sha2salt = array_values(unpack('N*', $sha2salt));
+
+ $this->_expandstate($sha2salt, $sha2pass, $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ for ($i = 0; $i < 64; $i++) {
+ $this->_expand0state($sha2salt, $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ $this->_expand0state($sha2pass, $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ }
+
+ for ($i = 0; $i < 64; $i++) {
+ for ($j = 0; $j < 8; $j+= 2) { // count($cdata) == 8
+ list($cdata[$j], $cdata[$j + 1]) = $this->_encryptBlockHelperFast($cdata[$j], $cdata[$j + 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ }
+ }
+
+ $output = '';
+ for ($i = 0; $i < count($cdata); $i++) {
+ $output.= pack('L*', $cdata[$i]);
+ }
+ return $output;
+ }
+
+ /**
+ * Performs OpenSSH-style bcrypt
+ *
+ * @param string $pass
+ * @param string $salt
+ * @param int $keylen
+ * @param int $rounds
+ * @access public
+ * @return false|string
+ */
+ function bcrypt_pbkdf($pass, $salt, $keylen, $rounds)
+ {
+ if (PHP_INT_SIZE == 4) {
+ user_error('bcrypt is far too slow to be practical on 32-bit versions of PHP');
+ return false;
+ }
+
+ if (!isset($this->sha512)) {
+ $this->sha512 = new Hash('sha512');
+ }
+
+ $sha2pass = $this->sha512->hash($pass);
+ $results = array();
+ $count = 1;
+ while (32 * count($results) < $keylen) {
+ $countsalt = $salt . pack('N', $count++);
+ $sha2salt = $this->sha512->hash($countsalt);
+ $out = $tmpout = $this->_bcrypt_hash($sha2pass, $sha2salt);
+ for ($i = 1; $i < $rounds; $i++) {
+ $sha2salt = $this->sha512->hash($tmpout);
+ $tmpout = $this->_bcrypt_hash($sha2pass, $sha2salt);
+ $out^= $tmpout;
+ }
+ $results[] = $out;
+ }
+ $output = '';
+ for ($i = 0; $i < 32; $i++) {
+ foreach ($results as $result) {
+ $output.= $result[$i];
+ }
+ }
+ return substr($output, 0, $keylen);
+ }
+
+ /**
+ * Key expansion without salt
+ *
+ * @access private
+ * @param int[] $key
+ * @param int[] $sbox0
+ * @param int[] $sbox1
+ * @param int[] $sbox2
+ * @param int[] $sbox3
+ * @param int[] $p
+ * @see self::_bcrypt_hash()
+ */
+ function _expand0state($key, &$sbox0, &$sbox1, &$sbox2, &$sbox3, &$p)
+ {
+ // expand0state is basically the same thing as this:
+ //return $this->_expandstate(array_fill(0, 16, 0), $key);
+ // but this separate function eliminates a bunch of XORs and array lookups
+
+ $p = array(
+ $p[0] ^ $key[0],
+ $p[1] ^ $key[1],
+ $p[2] ^ $key[2],
+ $p[3] ^ $key[3],
+ $p[4] ^ $key[4],
+ $p[5] ^ $key[5],
+ $p[6] ^ $key[6],
+ $p[7] ^ $key[7],
+ $p[8] ^ $key[8],
+ $p[9] ^ $key[9],
+ $p[10] ^ $key[10],
+ $p[11] ^ $key[11],
+ $p[12] ^ $key[12],
+ $p[13] ^ $key[13],
+ $p[14] ^ $key[14],
+ $p[15] ^ $key[15],
+ $p[16] ^ $key[0],
+ $p[17] ^ $key[1]
+ );
+
+ // @codingStandardsIgnoreStart
+ list( $p[0], $p[1]) = $this->_encryptBlockHelperFast( 0, 0, $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list( $p[2], $p[3]) = $this->_encryptBlockHelperFast($p[ 0], $p[ 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list( $p[4], $p[5]) = $this->_encryptBlockHelperFast($p[ 2], $p[ 3], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list( $p[6], $p[7]) = $this->_encryptBlockHelperFast($p[ 4], $p[ 5], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list( $p[8], $p[9]) = $this->_encryptBlockHelperFast($p[ 6], $p[ 7], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list($p[10], $p[11]) = $this->_encryptBlockHelperFast($p[ 8], $p[ 9], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list($p[12], $p[13]) = $this->_encryptBlockHelperFast($p[10], $p[11], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list($p[14], $p[15]) = $this->_encryptBlockHelperFast($p[12], $p[13], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list($p[16], $p[17]) = $this->_encryptBlockHelperFast($p[14], $p[15], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ // @codingStandardsIgnoreEnd
+
+ list($sbox0[0], $sbox0[1]) = $this->_encryptBlockHelperFast($p[16], $p[17], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ for ($i = 2; $i < 256; $i+= 2) {
+ list($sbox0[$i], $sbox0[$i + 1]) = $this->_encryptBlockHelperFast($sbox0[$i - 2], $sbox0[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ }
+
+ list($sbox1[0], $sbox1[1]) = $this->_encryptBlockHelperFast($sbox0[254], $sbox0[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ for ($i = 2; $i < 256; $i+= 2) {
+ list($sbox1[$i], $sbox1[$i + 1]) = $this->_encryptBlockHelperFast($sbox1[$i - 2], $sbox1[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ }
+
+ list($sbox2[0], $sbox2[1]) = $this->_encryptBlockHelperFast($sbox1[254], $sbox1[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ for ($i = 2; $i < 256; $i+= 2) {
+ list($sbox2[$i], $sbox2[$i + 1]) = $this->_encryptBlockHelperFast($sbox2[$i - 2], $sbox2[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ }
+
+ list($sbox3[0], $sbox3[1]) = $this->_encryptBlockHelperFast($sbox2[254], $sbox2[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ for ($i = 2; $i < 256; $i+= 2) {
+ list($sbox3[$i], $sbox3[$i + 1]) = $this->_encryptBlockHelperFast($sbox3[$i - 2], $sbox3[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ }
+ }
+
+ /**
+ * Key expansion with salt
+ *
+ * @access private
+ * @param int[] $data
+ * @param int[] $key
+ * @param int[] $sbox0
+ * @param int[] $sbox1
+ * @param int[] $sbox2
+ * @param int[] $sbox3
+ * @param int[] $p
+ * @see self::_bcrypt_hash()
+ */
+ function _expandstate($data, $key, &$sbox0, &$sbox1, &$sbox2, &$sbox3, &$p)
+ {
+ $p = array(
+ $p[0] ^ $key[0],
+ $p[1] ^ $key[1],
+ $p[2] ^ $key[2],
+ $p[3] ^ $key[3],
+ $p[4] ^ $key[4],
+ $p[5] ^ $key[5],
+ $p[6] ^ $key[6],
+ $p[7] ^ $key[7],
+ $p[8] ^ $key[8],
+ $p[9] ^ $key[9],
+ $p[10] ^ $key[10],
+ $p[11] ^ $key[11],
+ $p[12] ^ $key[12],
+ $p[13] ^ $key[13],
+ $p[14] ^ $key[14],
+ $p[15] ^ $key[15],
+ $p[16] ^ $key[0],
+ $p[17] ^ $key[1]
+ );
+
+ // @codingStandardsIgnoreStart
+ list( $p[0], $p[1]) = $this->_encryptBlockHelperFast($data[ 0] , $data[ 1] , $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list( $p[2], $p[3]) = $this->_encryptBlockHelperFast($data[ 2] ^ $p[ 0], $data[ 3] ^ $p[ 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list( $p[4], $p[5]) = $this->_encryptBlockHelperFast($data[ 4] ^ $p[ 2], $data[ 5] ^ $p[ 3], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list( $p[6], $p[7]) = $this->_encryptBlockHelperFast($data[ 6] ^ $p[ 4], $data[ 7] ^ $p[ 5], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list( $p[8], $p[9]) = $this->_encryptBlockHelperFast($data[ 8] ^ $p[ 6], $data[ 9] ^ $p[ 7], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list($p[10], $p[11]) = $this->_encryptBlockHelperFast($data[10] ^ $p[ 8], $data[11] ^ $p[ 9], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list($p[12], $p[13]) = $this->_encryptBlockHelperFast($data[12] ^ $p[10], $data[13] ^ $p[11], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list($p[14], $p[15]) = $this->_encryptBlockHelperFast($data[14] ^ $p[12], $data[15] ^ $p[13], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ list($p[16], $p[17]) = $this->_encryptBlockHelperFast($data[ 0] ^ $p[14], $data[ 1] ^ $p[15], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ // @codingStandardsIgnoreEnd
+
+ list($sbox0[0], $sbox0[1]) = $this->_encryptBlockHelperFast($data[2] ^ $p[16], $data[3] ^ $p[17], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ for ($i = 2, $j = 4; $i < 256; $i+= 2, $j = ($j + 2) % 16) { // instead of 16 maybe count($data) would be better?
+ list($sbox0[$i], $sbox0[$i + 1]) = $this->_encryptBlockHelperFast($data[$j] ^ $sbox0[$i - 2], $data[$j + 1] ^ $sbox0[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ }
+
+ list($sbox1[0], $sbox1[1]) = $this->_encryptBlockHelperFast($data[2] ^ $sbox0[254], $data[3] ^ $sbox0[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ for ($i = 2, $j = 4; $i < 256; $i+= 2, $j = ($j + 2) % 16) {
+ list($sbox1[$i], $sbox1[$i + 1]) = $this->_encryptBlockHelperFast($data[$j] ^ $sbox1[$i - 2], $data[$j + 1] ^ $sbox1[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ }
+
+ list($sbox2[0], $sbox2[1]) = $this->_encryptBlockHelperFast($data[2] ^ $sbox1[254], $data[3] ^ $sbox1[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ for ($i = 2, $j = 4; $i < 256; $i+= 2, $j = ($j + 2) % 16) {
+ list($sbox2[$i], $sbox2[$i + 1]) = $this->_encryptBlockHelperFast($data[$j] ^ $sbox2[$i - 2], $data[$j + 1] ^ $sbox2[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ }
+
+ list($sbox3[0], $sbox3[1]) = $this->_encryptBlockHelperFast($data[2] ^ $sbox2[254], $data[3] ^ $sbox2[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ for ($i = 2, $j = 4; $i < 256; $i+= 2, $j = ($j + 2) % 16) {
+ list($sbox3[$i], $sbox3[$i + 1]) = $this->_encryptBlockHelperFast($data[$j] ^ $sbox3[$i - 2], $data[$j + 1] ^ $sbox3[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
+ }
+ }
+
+ /**
* Encrypts a block
*
* @access private
@@ -406,18 +756,83 @@ class Blowfish extends Base
$l = $in[1];
$r = $in[2];
- for ($i = 0; $i < 16; $i+= 2) {
- $l^= $p[$i];
- $r^= $this->safe_intval(($this->safe_intval($sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^
- $sb_2[$l >> 8 & 0xff]) +
- $sb_3[$l & 0xff]);
+ list($r, $l) = CRYPT_BASE_USE_REG_INTVAL ?
+ $this->_encryptBlockHelperFast($l, $r, $sb_0, $sb_1, $sb_2, $sb_3, $p) :
+ $this->_encryptBlockHelperSlow($l, $r, $sb_0, $sb_1, $sb_2, $sb_3, $p);
- $r^= $p[$i + 1];
- $l^= $this->safe_intval(($this->safe_intval($sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^
- $sb_2[$r >> 8 & 0xff]) +
- $sb_3[$r & 0xff]);
- }
- return pack("N*", $r ^ $p[17], $l ^ $p[16]);
+ return pack("N*", $r, $l);
+ }
+
+ /**
+ * Fast helper function for block encryption
+ *
+ * @access private
+ * @param int $x0
+ * @param int $x1
+ * @param int[] $sbox0
+ * @param int[] $sbox1
+ * @param int[] $sbox2
+ * @param int[] $sbox3
+ * @param int[] $p
+ * @return int[]
+ */
+ function _encryptBlockHelperFast($x0, $x1, $sbox0, $sbox1, $sbox2, $sbox3, $p)
+ {
+ $x0 ^= $p[0];
+ $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[1];
+ $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[2];
+ $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[3];
+ $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[4];
+ $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[5];
+ $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[6];
+ $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[7];
+ $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[8];
+ $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[9];
+ $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[10];
+ $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[11];
+ $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[12];
+ $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[13];
+ $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[14];
+ $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[15];
+ $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[16];
+
+ return array($x1 & 0xFFFFFFFF ^ $p[17], $x0 & 0xFFFFFFFF);
+ }
+
+ /**
+ * Slow helper function for block encryption
+ *
+ * @access private
+ * @param int $x0
+ * @param int $x1
+ * @param int[] $sbox0
+ * @param int[] $sbox1
+ * @param int[] $sbox2
+ * @param int[] $sbox3
+ * @param int[] $p
+ * @return int[]
+ */
+ function _encryptBlockHelperSlow($x0, $x1, $sbox0, $sbox1, $sbox2, $sbox3, $p)
+ {
+ $x0^= $p[0];
+ $x1^= $this->safe_intval(($this->safe_intval($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[1];
+ $x0^= $this->safe_intval(($this->safe_intval($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[2];
+ $x1^= $this->safe_intval(($this->safe_intval($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[3];
+ $x0^= $this->safe_intval(($this->safe_intval($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[4];
+ $x1^= $this->safe_intval(($this->safe_intval($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[5];
+ $x0^= $this->safe_intval(($this->safe_intval($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[6];
+ $x1^= $this->safe_intval(($this->safe_intval($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[7];
+ $x0^= $this->safe_intval(($this->safe_intval($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[8];
+ $x1^= $this->safe_intval(($this->safe_intval($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[9];
+ $x0^= $this->safe_intval(($this->safe_intval($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[10];
+ $x1^= $this->safe_intval(($this->safe_intval($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[11];
+ $x0^= $this->safe_intval(($this->safe_intval($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[12];
+ $x1^= $this->safe_intval(($this->safe_intval($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[13];
+ $x0^= $this->safe_intval(($this->safe_intval($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[14];
+ $x1^= $this->safe_intval(($this->safe_intval($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[15];
+ $x0^= $this->safe_intval(($this->safe_intval($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[16];
+
+ return array($x1 & 0xFFFFFFFF ^ $p[17], $x0 & 0xFFFFFFFF);
}
/**
diff --git a/phpseclib/phpseclib/phpseclib/Crypt/DES.php b/phpseclib/phpseclib/phpseclib/Crypt/DES.php
index 9a8225fb..a19d1a0b 100644
--- a/phpseclib/phpseclib/phpseclib/Crypt/DES.php
+++ b/phpseclib/phpseclib/phpseclib/Crypt/DES.php
@@ -1246,9 +1246,9 @@ class DES extends Base
$pc2mapd3[($d >> 8) & 0xFF] | $pc2mapd4[ $d & 0xFF];
// Reorder: odd bytes/even bytes. Push the result in key schedule.
- $val1 = ( $cp & 0xFF000000) | (($cp << 8) & 0x00FF0000) |
+ $val1 = ( $cp & intval(0xFF000000)) | (($cp << 8) & 0x00FF0000) |
(($dp >> 16) & 0x0000FF00) | (($dp >> 8) & 0x000000FF);
- $val2 = (($cp << 8) & 0xFF000000) | (($cp << 16) & 0x00FF0000) |
+ $val2 = (($cp << 8) & intval(0xFF000000)) | (($cp << 16) & 0x00FF0000) |
(($dp >> 8) & 0x0000FF00) | ( $dp & 0x000000FF);
$keys[$des_round][self::ENCRYPT][ ] = $val1;
$keys[$des_round][self::DECRYPT][$ki - 1] = $val1;
diff --git a/phpseclib/phpseclib/phpseclib/Crypt/RC2.php b/phpseclib/phpseclib/phpseclib/Crypt/RC2.php
index b2b9d48e..4d27f42d 100644
--- a/phpseclib/phpseclib/phpseclib/Crypt/RC2.php
+++ b/phpseclib/phpseclib/phpseclib/Crypt/RC2.php
@@ -72,7 +72,7 @@ class RC2 extends Base
* @var string
* @access private
*/
- var $orig_key;
+ var $orig_key = '';
/**
* Don't truncate / null pad key
diff --git a/phpseclib/phpseclib/phpseclib/Crypt/RSA.php b/phpseclib/phpseclib/phpseclib/Crypt/RSA.php
index e26fe41d..a8fa2315 100644
--- a/phpseclib/phpseclib/phpseclib/Crypt/RSA.php
+++ b/phpseclib/phpseclib/phpseclib/Crypt/RSA.php
@@ -470,7 +470,7 @@ class RSA
case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'):
define('CRYPT_RSA_MODE', self::MODE_INTERNAL);
break;
- case extension_loaded('openssl') && file_exists($this->configFile):
+ case function_exists('phpinfo') && extension_loaded('openssl') && file_exists($this->configFile):
// some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
$versions = array();
@@ -878,9 +878,9 @@ class RSA
);
$key = "openssh-key-v1\0$key";
- return "-----BEGIN OPENSSH PRIVATE KEY-----\r\n" .
- chunk_split(base64_encode($key), 70) .
- "-----END OPENSSH PRIVATE KEY-----";
+ return "-----BEGIN OPENSSH PRIVATE KEY-----\n" .
+ chunk_split(base64_encode($key), 70, "\n") .
+ "-----END OPENSSH PRIVATE KEY-----\n";
default: // eg. self::PRIVATE_FORMAT_PKCS1
$components = array();
foreach ($raw as $name => $value) {
@@ -1388,6 +1388,10 @@ class RSA
// http://en.wikipedia.org/wiki/XML_Signature
case self::PRIVATE_FORMAT_XML:
case self::PUBLIC_FORMAT_XML:
+ if (!extension_loaded('xml')) {
+ return false;
+ }
+
$this->components = array();
$xml = xml_parser_create('UTF-8');
@@ -1405,11 +1409,18 @@ class RSA
unset($xml);
return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
- // from PuTTY's SSHPUBK.C
+ // see PuTTY's SSHPUBK.C and https://tartarus.org/~simon/putty-snapshots/htmldoc/AppendixC.html
case self::PRIVATE_FORMAT_PUTTY:
$components = array();
$key = preg_split('#\r\n|\r|\n#', $key);
- $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
+ if ($this->_string_shift($key[0], strlen('PuTTY-User-Key-File-')) != 'PuTTY-User-Key-File-') {
+ return false;
+ }
+ $version = (int) $this->_string_shift($key[0], 3); // should be either "2: " or "3: 0" prior to int casting
+ if ($version != 2 && $version != 3) {
+ return false;
+ }
+ $type = rtrim($key[0]);
if ($type != 'ssh-rsa') {
return false;
}
@@ -1424,23 +1435,55 @@ class RSA
extract(unpack('Nlength', $this->_string_shift($public, 4)));
$components['modulus'] = new BigInteger($this->_string_shift($public, $length), -256);
- $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4]));
- $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength))));
-
+ $offset = $publicLength + 4;
switch ($encryption) {
case 'aes256-cbc':
- $symkey = '';
- $sequence = 0;
- while (strlen($symkey) < 32) {
- $temp = pack('Na*', $sequence++, $this->password);
- $symkey.= pack('H*', sha1($temp));
- }
- $symkey = substr($symkey, 0, 32);
$crypto = new AES();
+ switch ($version) {
+ case 3:
+ if (!function_exists('sodium_crypto_pwhash')) {
+ return false;
+ }
+ $flavour = trim(preg_replace('#Key-Derivation: (.*)#', '$1', $key[$offset++]));
+ switch ($flavour) {
+ case 'Argon2i':
+ $flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2I13;
+ break;
+ case 'Argon2id':
+ $flavour = SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13;
+ break;
+ default:
+ return false;
+ }
+ $memory = trim(preg_replace('#Argon2-Memory: (\d+)#', '$1', $key[$offset++]));
+ $passes = trim(preg_replace('#Argon2-Passes: (\d+)#', '$1', $key[$offset++]));
+ $parallelism = trim(preg_replace('#Argon2-Parallelism: (\d+)#', '$1', $key[$offset++]));
+ $salt = pack('H*', trim(preg_replace('#Argon2-Salt: ([0-9a-f]+)#', '$1', $key[$offset++])));
+
+ $length = 80; // keylen + ivlen + mac_keylen
+ $temp = sodium_crypto_pwhash($length, $this->password, $salt, $passes, $memory << 10, $flavour);
+
+ $symkey = substr($temp, 0, 32);
+ $symiv = substr($temp, 32, 16);
+ break;
+ case 2:
+ $symkey = '';
+ $sequence = 0;
+ while (strlen($symkey) < 32) {
+ $temp = pack('Na*', $sequence++, $this->password);
+ $symkey.= pack('H*', sha1($temp));
+ }
+ $symkey = substr($symkey, 0, 32);
+ $symiv = str_repeat("\0", 16);
+ }
}
+ $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$offset++]));
+ $private = base64_decode(implode('', array_map('trim', array_slice($key, $offset, $privateLength))));
+
if ($encryption != 'none') {
$crypto->setKey($symkey);
+ $crypto->setIV($symiv);
$crypto->disablePadding();
$private = $crypto->decrypt($private);
if ($private === false) {
@@ -1483,14 +1526,44 @@ class RSA
if ($magic !== "openssh-key-v1\0") {
return false;
}
- $options = $this->_string_shift($decoded, 24);
- // \0\0\0\4none = ciphername
- // \0\0\0\4none = kdfname
- // \0\0\0\0 = kdfoptions
- // \0\0\0\1 = numkeys
- if ($options != "\0\0\0\4none\0\0\0\4none\0\0\0\0\0\0\0\1") {
+ extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
+ if (strlen($decoded) < $length) {
+ return false;
+ }
+ $ciphername = $this->_string_shift($decoded, $length);
+ extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
+ if (strlen($decoded) < $length) {
+ return false;
+ }
+ $kdfname = $this->_string_shift($decoded, $length);
+ extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
+ if (strlen($decoded) < $length) {
+ return false;
+ }
+ $kdfoptions = $this->_string_shift($decoded, $length);
+ extract(unpack('Nnumkeys', $this->_string_shift($decoded, 4)));
+ if ($numkeys != 1 || ($ciphername != 'none' && $kdfname != 'bcrypt')) {
return false;
}
+ switch ($ciphername) {
+ case 'none':
+ break;
+ case 'aes256-ctr':
+ extract(unpack('Nlength', $this->_string_shift($kdfoptions, 4)));
+ if (strlen($kdfoptions) < $length) {
+ return false;
+ }
+ $salt = $this->_string_shift($kdfoptions, $length);
+ extract(unpack('Nrounds', $this->_string_shift($kdfoptions, 4)));
+ $crypto = new AES(AES::MODE_CTR);
+ $crypto->disablePadding();
+ if (!$crypto->setPassword($this->password, 'bcrypt', $salt, $rounds, 32)) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
if (strlen($decoded) < $length) {
return false;
@@ -1500,12 +1573,16 @@ class RSA
if (strlen($decoded) < $length) {
return false;
}
- $paddedKey = $this->_string_shift($decoded, $length);
if ($this->_string_shift($publicKey, 11) !== "\0\0\0\7ssh-rsa") {
return false;
}
+ $paddedKey = $this->_string_shift($decoded, $length);
+ if (isset($crypto)) {
+ $paddedKey = $crypto->decrypt($paddedKey);
+ }
+
$checkint1 = $this->_string_shift($paddedKey, 4);
$checkint2 = $this->_string_shift($paddedKey, 4);
if (strlen($checkint1) != 4 || $checkint1 !== $checkint2) {
diff --git a/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php b/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php
index 3648a197..7a6be2a6 100644
--- a/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php
+++ b/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php
@@ -340,7 +340,7 @@ class Rijndael extends Base
$k = $c[2];
$l = $c[3];
while ($i < $Nb) {
- $temp[$i] = ($state[$i] & 0xFF000000) ^
+ $temp[$i] = ($state[$i] & intval(0xFF000000)) ^
($state[$j] & 0x00FF0000) ^
($state[$k] & 0x0000FF00) ^
($state[$l] & 0x000000FF) ^
@@ -426,7 +426,7 @@ class Rijndael extends Base
$l = $Nb - $c[3];
while ($i < $Nb) {
- $word = ($state[$i] & 0xFF000000) |
+ $word = ($state[$i] & intval(0xFF000000)) |
($state[$j] & 0x00FF0000) |
($state[$k] & 0x0000FF00) |
($state[$l] & 0x000000FF);
@@ -465,14 +465,19 @@ class Rijndael extends Base
{
// Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field.
// See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse
- static $rcon = array(0,
- 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
- 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
- 0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000,
- 0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000,
- 0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
- 0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000
- );
+ static $rcon;
+
+ if (!isset($rcon)) {
+ $rcon = array(0,
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
+ 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
+ 0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000,
+ 0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000,
+ 0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
+ 0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000
+ );
+ $rcon = array_map('intval', $rcon);
+ }
if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_length === $this->kl['key_length'] && $this->block_size === $this->kl['block_size']) {
// already expanded
@@ -511,7 +516,7 @@ class Rijndael extends Base
// on a 32-bit machine, it's 32-bits, and on a 64-bit machine, it's 64-bits. on a 32-bit machine,
// 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and'
// with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is.
- $temp = (($temp << 8) & 0xFFFFFF00) | (($temp >> 24) & 0x000000FF); // rotWord
+ $temp = (($temp << 8) & intval(0xFFFFFF00)) | (($temp >> 24) & 0x000000FF); // rotWord
$temp = $this->_subWord($temp) ^ $rcon[$i / $this->Nk];
} elseif ($this->Nk > 6 && $i % $this->Nk == 4) {
$temp = $this->_subWord($temp);
@@ -641,9 +646,9 @@ class Rijndael extends Base
));
foreach ($t3 as $t3i) {
- $t0[] = (($t3i << 24) & 0xFF000000) | (($t3i >> 8) & 0x00FFFFFF);
- $t1[] = (($t3i << 16) & 0xFFFF0000) | (($t3i >> 16) & 0x0000FFFF);
- $t2[] = (($t3i << 8) & 0xFFFFFF00) | (($t3i >> 24) & 0x000000FF);
+ $t0[] = (($t3i << 24) & intval(0xFF000000)) | (($t3i >> 8) & 0x00FFFFFF);
+ $t1[] = (($t3i << 16) & intval(0xFFFF0000)) | (($t3i >> 16) & 0x0000FFFF);
+ $t2[] = (($t3i << 8) & intval(0xFFFFFF00)) | (($t3i >> 24) & 0x000000FF);
}
$tables = array(
@@ -725,9 +730,9 @@ class Rijndael extends Base
));
foreach ($dt3 as $dt3i) {
- $dt0[] = (($dt3i << 24) & 0xFF000000) | (($dt3i >> 8) & 0x00FFFFFF);
- $dt1[] = (($dt3i << 16) & 0xFFFF0000) | (($dt3i >> 16) & 0x0000FFFF);
- $dt2[] = (($dt3i << 8) & 0xFFFFFF00) | (($dt3i >> 24) & 0x000000FF);
+ $dt0[] = (($dt3i << 24) & intval(0xFF000000)) | (($dt3i >> 8) & 0x00FFFFFF);
+ $dt1[] = (($dt3i << 16) & intval(0xFFFF0000)) | (($dt3i >> 16) & 0x0000FFFF);
+ $dt2[] = (($dt3i << 8) & intval(0xFFFFFF00)) | (($dt3i >> 24) & 0x000000FF);
};
$tables = array(
diff --git a/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php b/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php
index 70980a2f..1c020481 100644
--- a/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php
+++ b/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php
@@ -369,6 +369,42 @@ class Twofish extends Base
var $key_length = 16;
/**
+ * Default Constructor.
+ *
+ * Determines whether or not the mcrypt extension should be used.
+ *
+ * $mode could be:
+ *
+ * - CRYPT_MODE_ECB
+ *
+ * - CRYPT_MODE_CBC
+ *
+ * - CRYPT_MODE_CTR
+ *
+ * - CRYPT_MODE_CFB
+ *
+ * - CRYPT_MODE_OFB
+ *
+ * (or the alias constants of the chosen cipher, for example for AES: CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC ...)
+ *
+ * If not explicitly set, CRYPT_MODE_CBC will be used.
+ *
+ * @param int $mode
+ * @access public
+ */
+ function __construct($mode = self::MODE_CBC)
+ {
+ parent::__construct($mode);
+
+ $this->m0 = array_map('intval', $this->m0);
+ $this->m1 = array_map('intval', $this->m1);
+ $this->m2 = array_map('intval', $this->m2);
+ $this->m3 = array_map('intval', $this->m3);
+ $this->q0 = array_map('intval', $this->q0);
+ $this->q1 = array_map('intval', $this->q1);
+ }
+
+ /**
* Sets the key length.
*
* Valid key lengths are 128, 192 or 256 bits
diff --git a/phpseclib/phpseclib/phpseclib/File/ASN1.php b/phpseclib/phpseclib/phpseclib/File/ASN1.php
index d1a7719f..fb2d6448 100644
--- a/phpseclib/phpseclib/phpseclib/File/ASN1.php
+++ b/phpseclib/phpseclib/phpseclib/File/ASN1.php
@@ -234,6 +234,9 @@ class ASN1
{
$current = array('start' => $start);
+ if (!isset($encoded[$encoded_pos])) {
+ return false;
+ }
$type = ord($encoded[$encoded_pos++]);
$startOffset = 1;
@@ -244,6 +247,9 @@ class ASN1
$tag = 0;
// process septets (since the eighth bit is ignored, it's not an octet)
do {
+ if (!isset($encoded[$encoded_pos])) {
+ return false;
+ }
$temp = ord($encoded[$encoded_pos++]);
$startOffset++;
$loop = $temp >> 7;
@@ -260,6 +266,9 @@ class ASN1
$start+= $startOffset;
// Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13
+ if (!isset($encoded[$encoded_pos])) {
+ return false;
+ }
$length = ord($encoded[$encoded_pos++]);
$start++;
if ($length == 0x80) { // indefinite length
diff --git a/phpseclib/phpseclib/phpseclib/File/X509.php b/phpseclib/phpseclib/phpseclib/File/X509.php
index fa3c0264..73ecd25d 100644
--- a/phpseclib/phpseclib/phpseclib/File/X509.php
+++ b/phpseclib/phpseclib/phpseclib/File/X509.php
@@ -145,6 +145,7 @@ class X509
var $AuthorityKeyIdentifier;
var $CertificatePolicies;
var $AuthorityInfoAccessSyntax;
+ var $SubjectInfoAccessSyntax;
var $SubjectAltName;
var $SubjectDirectoryAttributes;
var $PrivateKeyUsagePeriod;
@@ -1622,7 +1623,6 @@ class X509
$id = $extensions[$i]['extnId'];
$value = &$extensions[$i]['extnValue'];
$value = base64_decode($value);
- $decoded = $asn1->decodeBER($value);
/* [extnValue] contains the DER encoding of an ASN.1 value
corresponding to the extension type identified by extnID */
$map = $this->_getMapping($id);
@@ -1630,6 +1630,7 @@ class X509
$decoder = $id == 'id-ce-nameConstraints' ?
array($this, '_decodeNameConstraintIP') :
array($this, '_decodeIP');
+ $decoded = $asn1->decodeBER($value);
$mapped = $asn1->asn1map($decoded[0], $map, array('iPAddress' => $decoder));
$value = $mapped === false ? $decoded[0] : $mapped;
@@ -2163,7 +2164,11 @@ class X509
if (!$fsock) {
return false;
}
- fputs($fsock, "GET $parts[path] HTTP/1.0\r\n");
+ $path = $parts['path'];
+ if (isset($parts['query'])) {
+ $path.= '?' . $parts['query'];
+ }
+ fputs($fsock, "GET $path HTTP/1.0\r\n");
fputs($fsock, "Host: $parts[host]\r\n\r\n");
$line = fgets($fsock, 1024);
if (strlen($line) < 3) {
diff --git a/phpseclib/phpseclib/phpseclib/Math/BigInteger.php b/phpseclib/phpseclib/phpseclib/Math/BigInteger.php
index fc24b914..7c0b0a8d 100644
--- a/phpseclib/phpseclib/phpseclib/Math/BigInteger.php
+++ b/phpseclib/phpseclib/phpseclib/Math/BigInteger.php
@@ -268,7 +268,7 @@ class BigInteger
$versions = array();
// avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems)
- if (strpos(ini_get('disable_functions'), 'phpinfo') === false) {
+ if (function_exists('phpinfo')) {
ob_start();
@phpinfo();
$content = ob_get_contents();
diff --git a/phpseclib/phpseclib/phpseclib/Net/SFTP.php b/phpseclib/phpseclib/phpseclib/Net/SFTP.php
index f9bb2235..ade0ea5c 100644
--- a/phpseclib/phpseclib/phpseclib/Net/SFTP.php
+++ b/phpseclib/phpseclib/phpseclib/Net/SFTP.php
@@ -5,9 +5,7 @@
*
* PHP version 5
*
- * Currently only supports SFTPv2 and v3, which, according to wikipedia.org, "is the most widely used version,
- * implemented by the popular OpenSSH SFTP server". If you want SFTPv4/5/6 support, provide me with access
- * to an SFTPv4/5/6 server.
+ * Supports SFTPv2/3/4/5/6. Defaults to v3.
*
* The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
*
@@ -155,6 +153,24 @@ class SFTP extends SSH2
var $version;
/**
+ * Default Server SFTP version
+ *
+ * @var int
+ * @see self::_initChannel()
+ * @access private
+ */
+ var $defaultVersion;
+
+ /**
+ * Preferred SFTP version
+ *
+ * @var int
+ * @see self::_initChannel()
+ * @access private
+ */
+ var $preferredVersion = 3;
+
+ /**
* Current working directory
*
* @var string
@@ -270,6 +286,21 @@ class SFTP extends SSH2
var $preserveTime = false;
/**
+ * Arbitrary Length Packets Flag
+ *
+ * Determines whether or not packets of any length should be allowed,
+ * in cases where the server chooses the packet length (such as
+ * directory listings). By default, packets are only allowed to be
+ * 256 * 1024 bytes (SFTP_MAX_MSG_LENGTH from OpenSSH's sftp-common.h)
+ *
+ * @see self::enableArbitraryLengthPackets()
+ * @see self::_get_sftp_packet()
+ * @var bool
+ * @access private
+ */
+ var $allow_arbitrary_length_packets = false;
+
+ /**
* Was the last packet due to the channels being closed or not?
*
* @see self::get()
@@ -280,6 +311,14 @@ class SFTP extends SSH2
var $channel_close = false;
/**
+ * Has the SFTP channel been partially negotiated?
+ *
+ * @var bool
+ * @access private
+ */
+ var $partial_init = false;
+
+ /**
* Default Constructor.
*
* Connects to an SFTP server
@@ -299,15 +338,13 @@ class SFTP extends SSH2
$this->packet_types = array(
1 => 'NET_SFTP_INIT',
2 => 'NET_SFTP_VERSION',
- /* the format of SSH_FXP_OPEN changed between SFTPv4 and SFTPv5+:
- SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.1
- pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 */
3 => 'NET_SFTP_OPEN',
4 => 'NET_SFTP_CLOSE',
5 => 'NET_SFTP_READ',
6 => 'NET_SFTP_WRITE',
7 => 'NET_SFTP_LSTAT',
9 => 'NET_SFTP_SETSTAT',
+ 10 => 'NET_SFTP_FSETSTAT',
11 => 'NET_SFTP_OPENDIR',
12 => 'NET_SFTP_READDIR',
13 => 'NET_SFTP_REMOVE',
@@ -315,18 +352,13 @@ class SFTP extends SSH2
15 => 'NET_SFTP_RMDIR',
16 => 'NET_SFTP_REALPATH',
17 => 'NET_SFTP_STAT',
- /* the format of SSH_FXP_RENAME changed between SFTPv4 and SFTPv5+:
- SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
- pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.5 */
18 => 'NET_SFTP_RENAME',
19 => 'NET_SFTP_READLINK',
20 => 'NET_SFTP_SYMLINK',
+ 21 => 'NET_SFTP_LINK',
101=> 'NET_SFTP_STATUS',
102=> 'NET_SFTP_HANDLE',
- /* the format of SSH_FXP_NAME changed between SFTPv3 and SFTPv4+:
- SFTPv4+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4
- pre-SFTPv4 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 */
103=> 'NET_SFTP_DATA',
104=> 'NET_SFTP_NAME',
105=> 'NET_SFTP_ATTRS',
@@ -371,25 +403,59 @@ class SFTP extends SSH2
// the order, in this case, matters quite a lot - see \phpseclib\Net\SFTP::_parseAttributes() to understand why
$this->attributes = array(
0x00000001 => 'NET_SFTP_ATTR_SIZE',
- 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+
+ 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+
+ 0x00000080 => 'NET_SFTP_ATTR_OWNERGROUP', // defined in SFTPv4+
0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
+ 0x00000010 => 'NET_SFTP_ATTR_CREATETIME', // SFTPv4+
+ 0x00000020 => 'NET_SFTP_ATTR_MODIFYTIME',
+ 0x00000040 => 'NET_SFTP_ATTR_ACL',
+ 0x00000100 => 'NET_SFTP_ATTR_SUBSECOND_TIMES',
+ 0x00000200 => 'NET_SFTP_ATTR_BITS', // SFTPv5+
+ 0x00000400 => 'NET_SFTP_ATTR_ALLOCATION_SIZE', // SFTPv6+
+ 0x00000800 => 'NET_SFTP_ATTR_TEXT_HINT',
+ 0x00001000 => 'NET_SFTP_ATTR_MIME_TYPE',
+ 0x00002000 => 'NET_SFTP_ATTR_LINK_COUNT',
+ 0x00004000 => 'NET_SFTP_ATTR_UNTRANSLATED_NAME',
+ 0x00008000 => 'NET_SFTP_ATTR_CTIME',
// 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers
// yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in
// two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000.
// that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored.
(-1 << 31) & 0xFFFFFFFF => 'NET_SFTP_ATTR_EXTENDED'
);
- // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3
- // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name
- // the array for that $this->open5_flags and similarly alter the constant names.
$this->open_flags = array(
0x00000001 => 'NET_SFTP_OPEN_READ',
0x00000002 => 'NET_SFTP_OPEN_WRITE',
0x00000004 => 'NET_SFTP_OPEN_APPEND',
0x00000008 => 'NET_SFTP_OPEN_CREATE',
0x00000010 => 'NET_SFTP_OPEN_TRUNCATE',
- 0x00000020 => 'NET_SFTP_OPEN_EXCL'
+ 0x00000020 => 'NET_SFTP_OPEN_EXCL',
+ 0x00000040 => 'NET_SFTP_OPEN_TEXT' // defined in SFTPv4
+ );
+ // SFTPv5+ changed the flags up:
+ // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-8.1.1.3
+ $this->open_flags5 = array(
+ // when SSH_FXF_ACCESS_DISPOSITION is a 3 bit field that controls how the file is opened
+ 0x00000000 => 'NET_SFTP_OPEN_CREATE_NEW',
+ 0x00000001 => 'NET_SFTP_OPEN_CREATE_TRUNCATE',
+ 0x00000002 => 'NET_SFTP_OPEN_OPEN_EXISTING',
+ 0x00000003 => 'NET_SFTP_OPEN_OPEN_OR_CREATE',
+ 0x00000004 => 'NET_SFTP_OPEN_TRUNCATE_EXISTING',
+ // the rest of the flags are not supported
+ 0x00000008 => 'NET_SFTP_OPEN_APPEND_DATA', // "the offset field of SS_FXP_WRITE requests is ignored"
+ 0x00000010 => 'NET_SFTP_OPEN_APPEND_DATA_ATOMIC',
+ 0x00000020 => 'NET_SFTP_OPEN_TEXT_MODE',
+ 0x00000040 => 'NET_SFTP_OPEN_BLOCK_READ',
+ 0x00000080 => 'NET_SFTP_OPEN_BLOCK_WRITE',
+ 0x00000100 => 'NET_SFTP_OPEN_BLOCK_DELETE',
+ 0x00000200 => 'NET_SFTP_OPEN_BLOCK_ADVISORY',
+ 0x00000400 => 'NET_SFTP_OPEN_NOFOLLOW',
+ 0x00000800 => 'NET_SFTP_OPEN_DELETE_ON_CLOSE',
+ 0x00001000 => 'NET_SFTP_OPEN_ACCESS_AUDIT_ALARM_INFO',
+ 0x00002000 => 'NET_SFTP_OPEN_ACCESS_BACKUP',
+ 0x00004000 => 'NET_SFTP_OPEN_BACKUP_STREAM',
+ 0x00008000 => 'NET_SFTP_OPEN_OVERRIDE_OWNER',
);
// http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
// see \phpseclib\Net\SFTP::_parseLongname() for an explanation
@@ -411,6 +477,7 @@ class SFTP extends SSH2
$this->status_codes,
$this->attributes,
$this->open_flags,
+ $this->open_flags5,
$this->file_types
);
@@ -423,28 +490,31 @@ class SFTP extends SSH2
}
/**
- * Login
+ * Check a few things before SFTP functions are called
*
- * @param string $username
* @return bool
* @access public
*/
- function login($username)
+ function _precheck()
{
- if (!call_user_func_array('parent::login', func_get_args())) {
+ if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
- return $this->_init_sftp_connection();
+ if ($this->pwd === false) {
+ return $this->_init_sftp_connection();
+ }
+
+ return true;
}
/**
- * (Re)initializes the SFTP channel
+ * Partially initialize an SFTP connection
*
* @return bool
- * @access private
+ * @access public
*/
- function _init_sftp_connection()
+ function _partial_init_sftp_connection()
{
$this->window_size_server_to_client[self::CHANNEL] = $this->window_size;
@@ -467,6 +537,8 @@ class SFTP extends SSH2
$response = $this->_get_channel_packet(self::CHANNEL, true);
if ($response === false) {
return false;
+ } elseif ($response === true && $this->isTimeout()) {
+ return false;
}
$packet = pack(
@@ -513,6 +585,8 @@ class SFTP extends SSH2
if ($response === false) {
return false;
}
+ } elseif ($response === true && $this->isTimeout()) {
+ return false;
}
$this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA;
@@ -527,11 +601,13 @@ class SFTP extends SSH2
return false;
}
+ $this->use_request_id = true;
+
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nversion', $this->_string_shift($response, 4)));
- $this->version = $version;
+ $this->defaultVersion = $version;
while (!empty($response)) {
if (strlen($response) < 4) {
return false;
@@ -546,21 +622,22 @@ class SFTP extends SSH2
$this->extensions[$key] = $value;
}
- /*
- SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com',
- however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's
- not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for
- one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that
- 'newline@vandyke.com' would.
- */
- /*
- if (isset($this->extensions['newline@vandyke.com'])) {
- $this->extensions['newline'] = $this->extensions['newline@vandyke.com'];
- unset($this->extensions['newline@vandyke.com']);
- }
- */
+ $this->partial_init = true;
- $this->use_request_id = true;
+ return true;
+ }
+
+ /**
+ * (Re)initializes the SFTP channel
+ *
+ * @return bool
+ * @access private
+ */
+ function _init_sftp_connection()
+ {
+ if (!$this->partial_init && !$this->_partial_init_sftp_connection()) {
+ return false;
+ }
/*
A Note on SFTPv4/5/6 support:
@@ -585,15 +662,72 @@ class SFTP extends SSH2
in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what \phpseclib\Net\SFTP would do is close the
channel and reopen it with a new and updated SSH_FXP_INIT packet.
*/
- switch ($this->version) {
- case 2:
- case 3:
- break;
- default:
- return false;
+ $this->version = $this->defaultVersion;
+ if (isset($this->extensions['versions']) && (!$this->preferredVersion || $this->preferredVersion != $this->version)) {
+ $versions = explode(',', $this->extensions['versions']);
+ $supported = array(6, 5, 4);
+ if ($this->preferredVersion) {
+ $supported = array_diff($supported, array($this->preferredVersion));
+ array_unshift($supported, $this->preferredVersion);
+ }
+ foreach ($supported as $ver) {
+ if (in_array($ver, $versions)) {
+ if ($ver === $this->version) {
+ break;
+ }
+ $this->version = (int) $ver;
+ $packet = pack('Na*Na*', strlen('version-select'), 'version-select', strlen($ver), $ver);
+ if (!$this->_send_sftp_packet(NET_SFTP_EXTENDED, $packet)) {
+ return false;
+ }
+ $response = $this->_get_sftp_packet();
+ if ($this->packet_type != NET_SFTP_STATUS) {
+ user_error('Expected SSH_FXP_STATUS');
+ return false;
+ }
+
+ if (strlen($response) < 4) {
+ return false;
+ }
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
+ if ($status != NET_SFTP_STATUS_OK) {
+ $this->_logError($response, $status);
+ return false;
+ }
+
+ break;
+ }
+ }
+ }
+
+ /*
+ SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com',
+ however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's
+ not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for
+ one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that
+ 'newline@vandyke.com' would.
+ */
+ /*
+ if (isset($this->extensions['newline@vandyke.com'])) {
+ $this->extensions['newline'] = $this->extensions['newline@vandyke.com'];
+ unset($this->extensions['newline@vandyke.com']);
}
+ */
+ if ($this->version < 2 || $this->version > 6) {
+ return false;
+ }
+
+ $this->pwd = true;
$this->pwd = $this->_realpath('.');
+ if ($this->pwd === false) {
+ if (!$this->canonicalize_paths) {
+ user_error('Unable to canonicalize current working directory');
+ return false;
+ }
+ $this->canonicalize_paths = false;
+ $this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST);
+ }
$this->_update_stat_cache($this->pwd, array());
@@ -641,7 +775,9 @@ class SFTP extends SSH2
}
/**
- * Enable path canonicalization
+ * Disable path canonicalization
+ *
+ * If this is enabled then $sftp->pwd() will not return the canonicalized absolute path
*
* @access public
*/
@@ -651,6 +787,26 @@ class SFTP extends SSH2
}
/**
+ * Enable arbitrary length packets
+ *
+ * @access public
+ */
+ function enableArbitraryLengthPackets()
+ {
+ $this->allow_arbitrary_length_packets = true;
+ }
+
+ /**
+ * Disable arbitrary length packets
+ *
+ * @access public
+ */
+ function disableArbitraryLengthPackets()
+ {
+ $this->allow_arbitrary_length_packets = false;
+ }
+
+ /**
* Returns the current directory name
*
* @return mixed
@@ -658,6 +814,10 @@ class SFTP extends SSH2
*/
function pwd()
{
+ if (!$this->_precheck()) {
+ return false;
+ }
+
return $this->pwd;
}
@@ -699,6 +859,10 @@ class SFTP extends SSH2
*/
function realpath($path)
{
+ if (!$this->_precheck()) {
+ return false;
+ }
+
return $this->_realpath($path);
}
@@ -719,10 +883,37 @@ class SFTP extends SSH2
function _realpath($path)
{
if (!$this->canonicalize_paths) {
- return $path;
+ if ($this->pwd === true) {
+ return '.';
+ }
+ if (!strlen($path) || $path[0] != '/') {
+ $path = $this->pwd . '/' . $path;
+ }
+
+ $parts = explode('/', $path);
+ $afterPWD = $beforePWD = [];
+ foreach ($parts as $part) {
+ switch ($part) {
+ //case '': // some SFTP servers /require/ double /'s. see https://github.com/phpseclib/phpseclib/pull/1137
+ case '.':
+ break;
+ case '..':
+ if (!empty($afterPWD)) {
+ array_pop($afterPWD);
+ } else {
+ $beforePWD[] = '..';
+ }
+ break;
+ default:
+ $afterPWD[] = $part;
+ }
+ }
+
+ $beforePWD = count($beforePWD) ? implode('/', $beforePWD) : '.';
+ return $beforePWD . '/' . implode('/', $afterPWD);
}
- if ($this->pwd === false) {
+ if ($this->pwd === true) {
// http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9
if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($path), $path))) {
return false;
@@ -744,7 +935,6 @@ class SFTP extends SSH2
$this->_logError($response);
return false;
default:
- user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS');
return false;
}
}
@@ -781,7 +971,7 @@ class SFTP extends SSH2
*/
function chdir($dir)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
@@ -938,7 +1128,7 @@ class SFTP extends SSH2
*/
function _list($dir, $raw = true)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
@@ -993,13 +1183,17 @@ class SFTP extends SSH2
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$shortname = $this->_string_shift($response, $length);
- if (strlen($response) < 4) {
- return false;
+ // SFTPv4 "removed the long filename from the names structure-- it can now be
+ // built from information available in the attrs structure."
+ if ($this->version < 4) {
+ if (strlen($response) < 4) {
+ return false;
+ }
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $longname = $this->_string_shift($response, $length);
}
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
- $longname = $this->_string_shift($response, $length);
$attributes = $this->_parseAttributes($response);
- if (!isset($attributes['type'])) {
+ if (!isset($attributes['type']) && $this->version < 4) {
$fileType = $this->_parseLongname($longname);
if ($fileType) {
$attributes['type'] = $fileType;
@@ -1159,10 +1353,6 @@ class SFTP extends SSH2
*/
function size($filename)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
- return false;
- }
-
$result = $this->stat($filename);
if ($result === false) {
return false;
@@ -1279,7 +1469,7 @@ class SFTP extends SSH2
*/
function stat($filename)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
@@ -1336,7 +1526,7 @@ class SFTP extends SSH2
*/
function lstat($filename)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
@@ -1450,7 +1640,7 @@ class SFTP extends SSH2
*/
function touch($filename, $time = null, $atime = null)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
@@ -1466,9 +1656,25 @@ class SFTP extends SSH2
$atime = $time;
}
- $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL;
- $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $time, $atime);
- $packet = pack('Na*Na*', strlen($filename), $filename, $flags, $attr);
+ if ($this->version < 4) {
+ $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $atime, $time);
+ } else {
+ $attr = pack(
+ 'N5',
+ NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME,
+ $atime / 4294967296,
+ $atime,
+ $time / 4294967296,
+ $time
+ );
+ }
+
+ $packet = pack('Na*', strlen($filename), $filename);
+ $packet.= $this->version >= 5 ?
+ pack('N2', 0, NET_SFTP_OPEN_OPEN_EXISTING) :
+ pack('N', NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL);
+ $packet.= $attr;
+
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}
@@ -1491,19 +1697,47 @@ class SFTP extends SSH2
/**
* Changes file or directory owner
*
+ * $uid should be an int for SFTPv3 and a string for SFTPv4+. Ideally the string
+ * would be of the form "user@dns_domain" but it does not need to be.
+ * `$sftp->getSupportedVersions()['version']` will return the specific version
+ * that's being used.
+ *
* Returns true on success or false on error.
*
* @param string $filename
- * @param int $uid
+ * @param int|string $uid
* @param bool $recursive
* @return bool
* @access public
*/
function chown($filename, $uid, $recursive = false)
{
- // quoting from <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>,
- // "if the owner or group is specified as -1, then that ID is not changed"
- $attr = pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1);
+ /*
+ quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>,
+
+ "To avoid a representation that is tied to a particular underlying
+ implementation at the client or server, the use of UTF-8 strings has
+ been chosen. The string should be of the form "user@dns_domain".
+ This will allow for a client and server that do not use the same
+ local representation the ability to translate to a common syntax that
+ can be interpreted by both. In the case where there is no
+ translation available to the client or server, the attribute value
+ must be constructed without the "@"."
+
+ phpseclib _could_ auto append the dns_domain to $uid BUT what if it shouldn't
+ have one? phpseclib would have no way of knowing so rather than guess phpseclib
+ will just use whatever value the user provided
+ */
+
+ $attr = $this->version < 4 ?
+ // quoting <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>,
+ // "if the owner or group is specified as -1, then that ID is not changed"
+ pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1) :
+ // quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>,
+ // "If either the owner or group field is zero length, the field should be
+ // considered absent, and no change should be made to that specific field
+ // during a modification operation"
+ pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, strlen($uid), $uid, 0, '');
return $this->_setstat($filename, $attr, $recursive);
}
@@ -1511,17 +1745,24 @@ class SFTP extends SSH2
/**
* Changes file or directory group
*
+ * $gid should be an int for SFTPv3 and a string for SFTPv4+. Ideally the string
+ * would be of the form "user@dns_domain" but it does not need to be.
+ * `$sftp->getSupportedVersions()['version']` will return the specific version
+ * that's being used.
+ *
* Returns true on success or false on error.
*
* @param string $filename
- * @param int $gid
+ * @param int|string $gid
* @param bool $recursive
* @return bool
* @access public
*/
function chgrp($filename, $gid, $recursive = false)
{
- $attr = pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid);
+ $attr = $this->version < 4 ?
+ pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid) :
+ pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, 0, '', strlen($gid), $gid);
return $this->_setstat($filename, $attr, $recursive);
}
@@ -1588,7 +1829,7 @@ class SFTP extends SSH2
*/
function _setstat($filename, $attr, $recursive)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
@@ -1606,9 +1847,10 @@ class SFTP extends SSH2
return $result;
}
- // SFTPv4+ has an additional byte field - type - that would need to be sent, as well. setting it to
- // SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT.
- if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($filename), $filename, $attr))) {
+ $packet = $this->version >= 4 ?
+ pack('Na*a*Ca*', strlen($filename), $filename, substr($attr, 0, 4), NET_SFTP_TYPE_UNKNOWN, substr($attr, 4)) :
+ pack('Na*a*', strlen($filename), $filename, $attr);
+ if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) {
return false;
}
@@ -1678,7 +1920,10 @@ class SFTP extends SSH2
return false;
}
} else {
- if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($temp), $temp, $attr))) {
+ $packet = $this->version >= 4 ?
+ pack('Na*Ca*', strlen($temp), $temp, NET_SFTP_TYPE_UNKNOWN, $attr) :
+ pack('Na*a*', strlen($temp), $temp, $attr);
+ if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) {
return false;
}
@@ -1693,7 +1938,10 @@ class SFTP extends SSH2
}
}
- if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($path), $path, $attr))) {
+ $packet = $this->version >= 4 ?
+ pack('Na*Ca*', strlen($temp), $temp, NET_SFTP_TYPE_UNKNOWN, $attr) :
+ pack('Na*a*', strlen($temp), $temp, $attr);
+ if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) {
return false;
}
@@ -1718,7 +1966,7 @@ class SFTP extends SSH2
*/
function readlink($link)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
@@ -1768,15 +2016,44 @@ class SFTP extends SSH2
*/
function symlink($target, $link)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
//$target = $this->_realpath($target);
$link = $this->_realpath($link);
- $packet = pack('Na*Na*', strlen($target), $target, strlen($link), $link);
- if (!$this->_send_sftp_packet(NET_SFTP_SYMLINK, $packet)) {
+ /* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-09#section-12.1 :
+
+ Changed the SYMLINK packet to be LINK and give it the ability to
+ create hard links. Also change it's packet number because many
+ implementation implemented SYMLINK with the arguments reversed.
+ Hopefully the new argument names make it clear which way is which.
+ */
+ if ($this->version == 6) {
+ $type = NET_SFTP_LINK;
+ $packet = pack('Na*Na*C', strlen($link), $link, strlen($target), $target, 1);
+ } else {
+ $type = NET_SFTP_SYMLINK;
+ /* quoting http://bxr.su/OpenBSD/usr.bin/ssh/PROTOCOL#347 :
+
+ 3.1. sftp: Reversal of arguments to SSH_FXP_SYMLINK
+
+ When OpenSSH's sftp-server was implemented, the order of the arguments
+ to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately,
+ the reversal was not noticed until the server was widely deployed. Since
+ fixing this to follow the specification would cause incompatibility, the
+ current order was retained. For correct operation, clients should send
+ SSH_FXP_SYMLINK as follows:
+
+ uint32 id
+ string targetpath
+ string linkpath */
+ $packet = substr($this->server_identifier, 0, 15) == 'SSH-2.0-OpenSSH' ?
+ pack('Na*Na*', strlen($target), $target, strlen($link), $link) :
+ pack('Na*Na*', strlen($link), $link, strlen($target), $target);
+ }
+ if (!$this->_send_sftp_packet($type, $packet)) {
return false;
}
@@ -1809,7 +2086,7 @@ class SFTP extends SSH2
*/
function mkdir($dir, $mode = -1, $recursive = false)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
@@ -1878,7 +2155,7 @@ class SFTP extends SSH2
*/
function rmdir($dir)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
@@ -1927,7 +2204,8 @@ class SFTP extends SSH2
* contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how
* large $remote_file will be, as well.
*
- * Setting $mode to self::SOURCE_CALLBACK will use $data as callback function, which gets only one parameter -- number of bytes to return, and returns a string if there is some data or null if there is no more data
+ * Setting $mode to self::SOURCE_CALLBACK will use $data as callback function, which gets only one parameter -- number
+ * of bytes to return, and returns a string if there is some data or null if there is no more data
*
* If $data is a resource then it'll be used as a resource instead.
*
@@ -1963,7 +2241,7 @@ class SFTP extends SSH2
*/
function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
@@ -1974,10 +2252,14 @@ class SFTP extends SSH2
$this->_remove_from_stat_cache($remote_file);
- $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE;
- // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file."
- // in practice, it doesn't seem to do that.
- //$flags|= ($mode & self::RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE;
+ if ($this->version >= 5) {
+ $flags = NET_SFTP_OPEN_OPEN_OR_CREATE;
+ } else {
+ $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE;
+ // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file."
+ // in practice, it doesn't seem to do that.
+ //$flags|= ($mode & SFTP::RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE;
+ }
if ($start >= 0) {
$offset = $start;
@@ -1987,10 +2269,17 @@ class SFTP extends SSH2
$offset = $size !== false ? $size : 0;
} else {
$offset = 0;
- $flags|= NET_SFTP_OPEN_TRUNCATE;
+ if ($this->version >= 5) {
+ $flags = NET_SFTP_OPEN_CREATE_TRUNCATE;
+ } else {
+ $flags|= NET_SFTP_OPEN_TRUNCATE;
+ }
}
- $packet = pack('Na*N2', strlen($remote_file), $remote_file, $flags, 0);
+ $packet = pack('Na*', strlen($remote_file), $remote_file);
+ $packet.= $this->version >= 5 ?
+ pack('N3', 0, $flags, 0) :
+ pack('N2', $flags, 0);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}
@@ -2021,7 +2310,7 @@ class SFTP extends SSH2
case is_resource($data):
$mode = $mode & ~self::SOURCE_LOCAL_FILE;
$info = stream_get_meta_data($data);
- if ($info['wrapper_type'] == 'PHP' && $info['stream_type'] == 'Input') {
+ if (isset($info['wrapper_type']) && $info['wrapper_type'] == 'PHP' && $info['stream_type'] == 'Input') {
$fp = fopen('php://memory', 'w+');
stream_copy_to_stream($data, $fp);
rewind($fp);
@@ -2099,6 +2388,8 @@ class SFTP extends SSH2
}
}
+ $result = $this->_close_handle($handle);
+
if (!$this->_read_put_responses($i)) {
if ($mode & self::SOURCE_LOCAL_FILE) {
fclose($fp);
@@ -2107,18 +2398,33 @@ class SFTP extends SSH2
return false;
}
- if ($mode & self::SOURCE_LOCAL_FILE) {
- if ($this->preserveTime) {
- $stat = fstat($fp);
- $this->touch($remote_file, $stat['mtime'], $stat['atime']);
- }
-
+ if ($mode & SFTP::SOURCE_LOCAL_FILE) {
if (isset($fp) && is_resource($fp)) {
fclose($fp);
}
+
+ if ($this->preserveTime) {
+ $stat = stat($data);
+ if ($this->version < 4) {
+ $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $stat['atime'], $stat['mtime']);
+ } else {
+ $attr = pack(
+ 'N5',
+ NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME,
+ $stat['atime'] / 4294967296,
+ $stat['atime'],
+ $stat['mtime'] / 4294967296,
+ $stat['mtime']
+ );
+ }
+
+ if (!$this->_setstat($remote_file, $attr, false)) {
+ user_error('Error setting file time');
+ }
+ }
}
- return $this->_close_handle($handle);
+ return $result;
}
/**
@@ -2205,7 +2511,7 @@ class SFTP extends SSH2
*/
function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
@@ -2214,7 +2520,10 @@ class SFTP extends SSH2
return false;
}
- $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0);
+ $packet = pack('Na*', strlen($remote_file), $remote_file);
+ $packet.= $this->version >= 5 ?
+ pack('N3', 0, NET_SFTP_OPEN_OPEN_EXISTING, 0) :
+ pack('N2', NET_SFTP_OPEN_READ, 0);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}
@@ -2316,6 +2625,7 @@ class SFTP extends SSH2
}
// maybe the file was successfully transferred, maybe it wasn't
if ($this->channel_close) {
+ $this->partial_init = false;
$this->_init_sftp_connection();
return false;
} else {
@@ -2365,7 +2675,7 @@ class SFTP extends SSH2
*/
function delete($path, $recursive = true)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
@@ -2436,7 +2746,7 @@ class SFTP extends SSH2
// normally $entries would have at least . and .. but it might not if the directories
// permissions didn't allow reading
if (empty($entries)) {
- return false;
+ $entries = array();
}
unset($entries['.'], $entries['..']);
@@ -2494,6 +2804,10 @@ class SFTP extends SSH2
function file_exists($path)
{
if ($this->use_stat_cache) {
+ if (!$this->_precheck()) {
+ return false;
+ }
+
$path = $this->_realpath($path);
$result = $this->_query_stat_cache($path);
@@ -2564,6 +2878,10 @@ class SFTP extends SSH2
*/
function is_readable($path)
{
+ if (!$this->_precheck()) {
+ return false;
+ }
+
$path = $this->_realpath($path);
$packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_READ, 0);
@@ -2592,6 +2910,10 @@ class SFTP extends SSH2
*/
function is_writable($path)
{
+ if (!$this->_precheck()) {
+ return false;
+ }
+
$path = $this->_realpath($path);
$packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_WRITE, 0);
@@ -2772,6 +3094,10 @@ class SFTP extends SSH2
*/
function _get_xstat_cache_prop($path, $prop, $type)
{
+ if (!$this->_precheck()) {
+ return false;
+ }
+
if ($this->use_stat_cache) {
$path = $this->_realpath($path);
@@ -2792,7 +3118,9 @@ class SFTP extends SSH2
}
/**
- * Renames a file or a directory on the SFTP server
+ * Renames a file or a directory on the SFTP server.
+ *
+ * If the file already exists this will return false
*
* @param string $oldname
* @param string $newname
@@ -2801,7 +3129,7 @@ class SFTP extends SSH2
*/
function rename($oldname, $newname)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
@@ -2813,6 +3141,18 @@ class SFTP extends SSH2
// http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
$packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname);
+ if ($this->version >= 5) {
+ /* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-6.5 ,
+
+ 'flags' is 0 or a combination of:
+
+ SSH_FXP_RENAME_OVERWRITE 0x00000001
+ SSH_FXP_RENAME_ATOMIC 0x00000002
+ SSH_FXP_RENAME_NATIVE 0x00000004
+
+ (none of these are currently supported) */
+ $packet.= "\0\0\0\0";
+ }
if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) {
return false;
}
@@ -2843,6 +3183,31 @@ class SFTP extends SSH2
}
/**
+ * Parse Time
+ *
+ * See '7.7. Times' of draft-ietf-secsh-filexfer-13 for more info.
+ *
+ * @param string $key
+ * @param int $flags
+ * @param string $response
+ * @return array
+ * @access private
+ */
+ function _parseTime($key, $flags, &$response)
+ {
+ if (strlen($response) < 8) {
+ user_error('Malformed file attributes');
+ return array();
+ }
+ $attr = array();
+ $attr[$key] = hexdec(bin2hex($this->_string_shift($response, 8)));
+ if ($flags & NET_SFTP_ATTR_SUBSECOND_TIMES) {
+ $attr+= extract(unpack('N' . $key . '_nseconds', $this->_string_shift($response, 4)));
+ }
+ return $attr;
+ }
+
+ /**
* Parse Attributes
*
* See '7. File Attributes' of draft-ietf-secsh-filexfer-13 for more info.
@@ -2853,16 +3218,56 @@ class SFTP extends SSH2
*/
function _parseAttributes(&$response)
{
+ if ($this->version >= 4) {
+ $length = 5;
+ $format = 'Nflags/Ctype';
+ } else {
+ $length = 4;
+ $format = 'Nflags';
+ }
+
$attr = array();
- if (strlen($response) < 4) {
+ if (strlen($response) < $length) {
user_error('Malformed file attributes');
return array();
}
- extract(unpack('Nflags', $this->_string_shift($response, 4)));
- // SFTPv4+ have a type field (a byte) that follows the above flag field
+ extract(unpack($format, $this->_string_shift($response, $length)));
+ if (isset($type)) {
+ $attr['type'] = $type;
+ }
foreach ($this->attributes as $key => $value) {
switch ($flags & $key) {
- case NET_SFTP_ATTR_SIZE: // 0x00000001
+ case NET_SFTP_ATTR_UIDGID:
+ if ($this->version > 3) {
+ continue 2;
+ }
+ break;
+ case NET_SFTP_ATTR_CREATETIME:
+ case NET_SFTP_ATTR_MODIFYTIME:
+ case NET_SFTP_ATTR_ACL:
+ case NET_SFTP_ATTR_OWNERGROUP:
+ case NET_SFTP_ATTR_SUBSECOND_TIMES:
+ if ($this->version < 4) {
+ continue 2;
+ }
+ break;
+ case NET_SFTP_ATTR_BITS:
+ if ($this->version < 5) {
+ continue 2;
+ }
+ break;
+ case NET_SFTP_ATTR_ALLOCATION_SIZE:
+ case NET_SFTP_ATTR_TEXT_HINT:
+ case NET_SFTP_ATTR_MIME_TYPE:
+ case NET_SFTP_ATTR_LINK_COUNT:
+ case NET_SFTP_ATTR_UNTRANSLATED_NAME:
+ case NET_SFTP_ATTR_CTIME:
+ if ($this->version < 6) {
+ continue 2;
+ }
+ }
+ switch ($flags & $key) {
+ case NET_SFTP_ATTR_SIZE: // 0x00000001
// The size attribute is defined as an unsigned 64-bit integer.
// The following will use floats on 32-bit platforms, if necessary.
// As can be seen in the BigInteger class, floats are generally
@@ -2871,14 +3276,14 @@ class SFTP extends SSH2
// of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB.
$attr['size'] = hexdec(bin2hex($this->_string_shift($response, 8)));
break;
- case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only)
+ case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 or earlier)
if (strlen($response) < 8) {
user_error('Malformed file attributes');
return $attr;
}
$attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8));
break;
- case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
+ case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
if (strlen($response) < 4) {
user_error('Malformed file attributes');
return $attr;
@@ -2892,14 +3297,134 @@ class SFTP extends SSH2
$attr+= array('type' => $fileType);
}
break;
- case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
+ case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
+ if ($this->version >= 4) {
+ $attr+= $this->_parseTime('atime', $flags, $response);
+ break;
+ }
if (strlen($response) < 8) {
user_error('Malformed file attributes');
return $attr;
}
$attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8));
break;
- case NET_SFTP_ATTR_EXTENDED: // 0x80000000
+ case NET_SFTP_ATTR_CREATETIME: // 0x00000010 (SFTPv4+)
+ $attr+= $this->_parseTime('createtime', $flags, $response);
+ break;
+ case NET_SFTP_ATTR_MODIFYTIME: // 0x00000020
+ $attr+= $this->_parseTime('mtime', $flags, $response);
+ break;
+ case NET_SFTP_ATTR_ACL: // 0x00000040
+ // access control list
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-04#section-5.7
+ // currently unsupported
+ if (strlen($response) < 4) {
+ user_error('Malformed file attributes');
+ return $attr;
+ }
+ extract(unpack('Ncount', $this->_string_shift($response, 4)));
+ for ($i = 0; $i < $count; $i++) {
+ if (strlen($response) < 16) {
+ user_error('Malformed file attributes');
+ return $attr;
+ }
+ extract(unpack('Ntype/Nflag/Nmask/Nlength', $this->_string_shift($response, 16)));
+ if (strlen($response) < $length) {
+ user_error('Malformed file attributes');
+ return $attr;
+ }
+ $this->_string_shift($response, $length); // who
+ }
+ break;
+ case NET_SFTP_ATTR_OWNERGROUP: // 0x00000080
+ if (strlen($response) < 4) {
+ user_error('Malformed file attributes');
+ return $attr;
+ }
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ if (strlen($response) < $length) {
+ user_error('Malformed file attributes');
+ return $attr;
+ }
+ $attr['owner'] = $this->_string_shift($response, $length);
+
+ if (strlen($response) < 4) {
+ user_error('Malformed file attributes');
+ return $attr;
+ }
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ if (strlen($response) < $length) {
+ user_error('Malformed file attributes');
+ return $attr;
+ }
+ $attr['group'] = $this->_string_shift($response, $length);
+ break;
+ case NET_SFTP_ATTR_SUBSECOND_TIMES: // 0x00000100
+ break;
+ case NET_SFTP_ATTR_BITS: // 0x00000200 (SFTPv5+)
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-5.8
+ // currently unsupported
+ // tells if you file is:
+ // readonly, system, hidden, case inensitive, archive, encrypted, compressed, sparse
+ // append only, immutable, sync
+ if (strlen($response) < 8) {
+ user_error('Malformed file attributes');
+ return $attr;
+ }
+ extract(unpack('Nattrib-bits/Nattrib-bits-valid', $this->_string_shift($response, 8)));
+ break;
+ case NET_SFTP_ATTR_ALLOCATION_SIZE: // 0x00000400 (SFTPv6+)
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.4
+ // represents the number of bytes htat the file consumes on the disk. will
+ // usually be larger than the 'size' field
+ $attr['allocation-size'] = hexdec(bin2hex($this->_string_shift($response, 8)));
+ break;
+ case NET_SFTP_ATTR_TEXT_HINT: // 0x00000800
+ // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.10
+ // currently unsupported
+ // tells if file is "known text", "guessed text", "known binary", "guessed binary"
+ extract(unpack('Ctext-hint', $this->_string_shift($response)));
+ break;
+ case NET_SFTP_ATTR_MIME_TYPE: // 0x00001000
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.11
+ if (strlen($response) < 4) {
+ user_error('Malformed file attributes');
+ return $attr;
+ }
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ if (strlen($response) < $length) {
+ user_error('Malformed file attributes');
+ return $attr;
+ }
+ $attr['mime-type'] = $this->_string_shift($response, $length);
+ break;
+ case NET_SFTP_ATTR_LINK_COUNT: // 0x00002000
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.12
+ if (strlen($response) < 4) {
+ user_error('Malformed file attributes');
+ return $attr;
+ }
+ $attr+= unpack('Nlink-count', $this->_string_shift($response, 4));
+ break;
+ case NET_SFTP_ATTR_UNTRANSLATED_NAME:// 0x00004000
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.13
+ if (strlen($response) < 4) {
+ user_error('Malformed file attributes');
+ return $attr;
+ }
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ if (strlen($response) < $length) {
+ user_error('Malformed file attributes');
+ return $attr;
+ }
+ $attr['untranslated-name'] = $this->_string_shift($response, $length);
+ break;
+ case NET_SFTP_ATTR_CTIME: // 0x00008000
+ // 'ctime' contains the last time the file attributes were changed. The
+ // exact meaning of this field depends on the server.
+ $attr+= $this->_parseTime('ctime', $flags, $response);
+ break;
+ case NET_SFTP_ATTR_EXTENDED: // 0x80000000
if (strlen($response) < 4) {
user_error('Malformed file attributes');
return $attr;
@@ -3108,6 +3633,9 @@ class SFTP extends SSH2
$this->packet_buffer = '';
return false;
}
+ if ($temp === false) {
+ return false;
+ }
$this->packet_buffer.= $temp;
}
if (strlen($this->packet_buffer) < 4) {
@@ -3117,9 +3645,8 @@ class SFTP extends SSH2
$tempLength = $length;
$tempLength-= strlen($this->packet_buffer);
-
// 256 * 1024 is what SFTP_MAX_MSG_LENGTH is set to in OpenSSH's sftp-common.h
- if ($tempLength > 256 * 1024) {
+ if (!$this->allow_arbitrary_length_packets && !$this->use_request_id && $tempLength > 256 * 1024) {
user_error('Invalid SFTP packet size');
return false;
}
@@ -3128,6 +3655,9 @@ class SFTP extends SSH2
while ($tempLength > 0) {
$temp = $this->_get_channel_packet(self::CHANNEL, true);
if (is_bool($temp)) {
+ if ($temp && $this->channel_status[self::CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) {
+ $this->channel_close = true;
+ }
$this->packet_type = false;
$this->packet_buffer = '';
return false;
@@ -3237,7 +3767,15 @@ class SFTP extends SSH2
*/
function getSupportedVersions()
{
- $temp = array('version' => $this->version);
+ if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ return false;
+ }
+
+ if (!$this->partial_init) {
+ $this->_partial_init_sftp_connection();
+ }
+
+ $temp = array('version' => $this->defaultVersion);
if (isset($this->extensions['versions'])) {
$temp['extensions'] = $this->extensions['versions'];
}
@@ -3245,6 +3783,36 @@ class SFTP extends SSH2
}
/**
+ * Get supported SFTP versions
+ *
+ * @return array
+ * @access public
+ */
+ function getNegotiatedVersion()
+ {
+ if (!$this->_precheck()) {
+ return false;
+ }
+
+ return $this->version;
+ }
+
+ /**
+ * Set preferred version
+ *
+ * If you're preferred version isn't supported then the highest supported
+ * version of SFTP will be utilized. Set to null or false or int(0) to
+ * unset the preferred version
+ *
+ * @param int $version
+ * @access public
+ */
+ function setPreferredVersion($version)
+ {
+ $this->preferredVersion = $version;
+ }
+
+ /**
* Disconnect
*
* @param int $reason
diff --git a/phpseclib/phpseclib/phpseclib/Net/SSH2.php b/phpseclib/phpseclib/phpseclib/Net/SSH2.php
index e449d987..d8e37357 100644
--- a/phpseclib/phpseclib/phpseclib/Net/SSH2.php
+++ b/phpseclib/phpseclib/phpseclib/Net/SSH2.php
@@ -71,6 +71,25 @@ use phpseclib\System\SSH\Agent;
class SSH2
{
/**#@+
+ * Compression Types
+ *
+ * @access private
+ */
+ /**
+ * No compression
+ */
+ const NET_SSH2_COMPRESSION_NONE = 1;
+ /**
+ * zlib compression
+ */
+ const NET_SSH2_COMPRESSION_ZLIB = 2;
+ /**
+ * zlib@openssh.com
+ */
+ const NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH = 3;
+ /**#@-*/
+
+ /**#@+
* Execution Bitmap Masks
*
* @see \phpseclib\Net\SSH2::bitmap
@@ -975,8 +994,65 @@ class SSH2
*
* @see https://tools.ietf.org/html/rfc4252#section-5.1
* @var array|null
+ * @access private
+ */
+ var $auth_methods_to_continue = null;
+
+ /**
+ * Compression method
+ *
+ * @var int
+ * @access private
+ */
+ var $compress = self::NET_SSH2_COMPRESSION_NONE;
+
+ /**
+ * Decompression method
+ *
+ * @var resource|object
+ * @access private
+ */
+ var $decompress = self::NET_SSH2_COMPRESSION_NONE;
+
+ /**
+ * Compression context
+ *
+ * @var int
+ * @access private
+ */
+ var $compress_context;
+
+ /**
+ * Decompression context
+ *
+ * @var resource|object
+ * @access private
+ */
+ var $decompress_context;
+
+ /**
+ * Regenerate Compression Context
+ *
+ * @var bool
+ * @access private
*/
- private $auth_methods_to_continue = null;
+ var $regenerate_compression_context = false;
+
+ /**
+ * Regenerate Decompression Context
+ *
+ * @var bool
+ * @access private
+ */
+ var $regenerate_decompression_context = false;
+
+ /**
+ * Smart multi-factor authentication flag
+ *
+ * @var bool
+ * @access private
+ */
+ var $smartMFA = true;
/**
* Default Constructor.
@@ -1218,8 +1294,8 @@ class SSH2
$read = array($this->fsock);
$write = $except = null;
$start = microtime(true);
- $sec = floor($this->curTimeout);
- $usec = 1000000 * ($this->curTimeout - $sec);
+ $sec = (int) floor($this->curTimeout);
+ $usec = (int) (1000000 * ($this->curTimeout - $sec));
// on windows this returns a "Warning: Invalid CRT parameters detected" error
// the !count() is done as a workaround for <https://bugs.php.net/42682>
if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
@@ -1234,6 +1310,7 @@ class SSH2
if (strlen($temp) == 255) {
continue;
}
+
if ($temp === false) {
return false;
}
@@ -1569,19 +1646,25 @@ class SSH2
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
+ $compression_map = array(
+ 'none' => self::NET_SSH2_COMPRESSION_NONE,
+ 'zlib' => self::NET_SSH2_COMPRESSION_ZLIB,
+ 'zlib@openssh.com' => self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH
+ );
+
$compression_algorithm_out = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server);
if ($compression_algorithm_out === false) {
user_error('No compatible client to server compression algorithms found');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
- //$this->decompress = $compression_algorithm_out == 'zlib';
+ $this->compress = $compression_map[$compression_algorithm_out];
- $compression_algorithm_in = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_client_to_server);
+ $compression_algorithm_in = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client);
if ($compression_algorithm_in === false) {
user_error('No compatible server to client compression algorithms found');
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
}
- //$this->compress = $compression_algorithm_in == 'zlib';
+ $this->decompress = $compression_map[$compression_algorithm_in];
// Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty.
$exchange_hash_rfc4419 = '';
@@ -2014,6 +2097,8 @@ class SSH2
}
$this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
+ $this->regenerate_compression_context = $this->regenerate_decompression_context = true;
+
return true;
}
@@ -2142,7 +2227,7 @@ class SSH2
// try logging with 'none' as an authentication method first since that's what
// PuTTY does
- if (substr($this->server_identifier, 0, 13) != 'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue === null) {
+ if (substr($this->server_identifier, 0, 15) != 'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue === null) {
if ($this->_login($username)) {
return true;
}
@@ -2174,9 +2259,61 @@ class SSH2
return $this->_login_helper($username);
}
- foreach ($args as $arg) {
- if ($this->_login_helper($username, $arg)) {
- return true;
+ while (count($args)) {
+ if (!$this->auth_methods_to_continue || !$this->smartMFA) {
+ $newargs = $args;
+ $args = array();
+ } else {
+ $newargs = array();
+ foreach ($this->auth_methods_to_continue as $method) {
+ switch ($method) {
+ case 'publickey':
+ foreach ($args as $key => $arg) {
+ if (is_object($arg)) {
+ $newargs[] = $arg;
+ unset($args[$key]);
+ break;
+ }
+ }
+ break;
+ case 'keyboard-interactive':
+ $hasArray = $hasString = false;
+ foreach ($args as $arg) {
+ if ($hasArray || is_array($arg)) {
+ $hasArray = true;
+ break;
+ }
+ if ($hasString || is_string($arg)) {
+ $hasString = true;
+ break;
+ }
+ }
+ if ($hasArray && $hasString) {
+ foreach ($args as $key => $arg) {
+ if (is_array($arg)) {
+ $newargs[] = $arg;
+ break 2;
+ }
+ }
+ }
+ case 'password':
+ foreach ($args as $key => $arg) {
+ $newargs[] = $arg;
+ unset($args[$key]);
+ break;
+ }
+ }
+ }
+ }
+
+ if (!count($newargs)) {
+ return false;
+ }
+
+ foreach ($newargs as $arg) {
+ if ($this->_login_helper($username, $arg)) {
+ return true;
+ }
}
}
return false;
@@ -2814,26 +2951,12 @@ class SSH2
return false;
}
- $response = $this->_get_binary_packet();
- if ($response === false) {
- $this->bitmap = 0;
- user_error('Connection closed by server');
- return false;
- }
-
- if (!strlen($response)) {
- return false;
+ $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
+ if (!$this->_get_channel_packet(self::CHANNEL_EXEC)) {
+ user_error('Unable to request pseudo-terminal');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
- list(, $type) = unpack('C', $this->_string_shift($response, 1));
- switch ($type) {
- case NET_SSH2_MSG_CHANNEL_SUCCESS:
- break;
- case NET_SSH2_MSG_CHANNEL_FAILURE:
- default:
- user_error('Unable to request pseudo-terminal');
- return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
- }
$this->in_request_pty_exec = true;
}
@@ -2954,6 +3077,13 @@ class SSH2
return false;
}
+ $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
+
+ if (!$this->_get_channel_packet(self::CHANNEL_SHELL)) {
+ user_error('Unable to request pseudo-terminal');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ }
+
$packet = pack(
'CNNa*C',
NET_SSH2_MSG_CHANNEL_REQUEST,
@@ -2966,7 +3096,12 @@ class SSH2
return false;
}
- $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_IGNORE;
+ $response = $this->_get_channel_packet(self::CHANNEL_SHELL);
+ if ($response === false) {
+ return false;
+ }
+
+ $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
$this->bitmap |= self::MASK_SHELL;
@@ -3369,8 +3504,8 @@ class SSH2
$this->curTimeout-= $elapsed;
}
- $sec = floor($this->curTimeout);
- $usec = 1000000 * ($this->curTimeout - $sec);
+ $sec = (int)floor($this->curTimeout);
+ $usec = (int)(1000000 * ($this->curTimeout - $sec));
// on windows this returns a "Warning: Invalid CRT parameters detected" error
if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
@@ -3384,7 +3519,11 @@ class SSH2
if (!is_resource($this->fsock) || feof($this->fsock)) {
$this->bitmap = 0;
- user_error('Connection closed prematurely');
+ $str = 'Connection closed (by server) prematurely';
+ if (isset($elapsed)) {
+ $str.= ' ' . $elapsed . 's';
+ }
+ user_error($str);
return false;
}
@@ -3392,7 +3531,8 @@ class SSH2
$raw = stream_get_contents($this->fsock, $this->decrypt_block_size);
if (!strlen($raw)) {
- return '';
+ user_error('No data received from server');
+ return false;
}
if ($this->decrypt !== false) {
@@ -3455,9 +3595,41 @@ class SSH2
}
}
- //if ($this->decompress) {
- // $payload = gzinflate(substr($payload, 2));
- //}
+ switch ($this->decompress) {
+ case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH:
+ if (!$this->isAuthenticated()) {
+ break;
+ }
+ case self::NET_SSH2_COMPRESSION_ZLIB:
+ if ($this->regenerate_decompression_context) {
+ $this->regenerate_decompression_context = false;
+
+ $cmf = ord($payload[0]);
+ $cm = $cmf & 0x0F;
+ if ($cm != 8) { // deflate
+ user_error("Only CM = 8 ('deflate') is supported ($cm)");
+ }
+ $cinfo = ($cmf & 0xF0) >> 4;
+ if ($cinfo > 7) {
+ user_error("CINFO above 7 is not allowed ($cinfo)");
+ }
+ $windowSize = 1 << ($cinfo + 8);
+
+ $flg = ord($payload[1]);
+ //$fcheck = $flg && 0x0F;
+ if ((($cmf << 8) | $flg) % 31) {
+ user_error('fcheck failed');
+ }
+ $fdict = boolval($flg & 0x20);
+ $flevel = ($flg & 0xC0) >> 6;
+
+ $this->decompress_context = inflate_init(ZLIB_ENCODING_RAW, array('window' => $cinfo + 8));
+ $payload = substr($payload, 2);
+ }
+ if ($this->decompress_context) {
+ $payload = inflate_add($this->decompress_context, $payload, ZLIB_PARTIAL_FLUSH);
+ }
+ }
$this->get_seq_no++;
@@ -3737,7 +3909,20 @@ class SSH2
function _get_channel_packet($client_channel, $skip_extended = false)
{
if (!empty($this->channel_buffers[$client_channel])) {
- return array_shift($this->channel_buffers[$client_channel]);
+ switch ($this->channel_status[$client_channel]) {
+ case NET_SSH2_MSG_CHANNEL_REQUEST:
+ foreach ($this->channel_buffers[$client_channel] as $i => $packet) {
+ switch (ord($packet[0])) {
+ case NET_SSH2_MSG_CHANNEL_SUCCESS:
+ case NET_SSH2_MSG_CHANNEL_FAILURE:
+ unset($this->channel_buffers[$client_channel][$i]);
+ return substr($packet, 1);
+ }
+ }
+ break;
+ default:
+ return substr(array_shift($this->channel_buffers[$client_channel]), 1);
+ }
}
while (true) {
@@ -3811,10 +3996,7 @@ class SSH2
if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) {
return $data;
}
- if (!isset($this->channel_buffers[$channel])) {
- $this->channel_buffers[$channel] = array();
- }
- $this->channel_buffers[$channel][] = $data;
+ $this->channel_buffers[$channel][] = chr($type) . $data;
continue 2;
case NET_SSH2_MSG_CHANNEL_REQUEST:
@@ -3893,20 +4075,15 @@ class SSH2
$result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
$this->_on_channel_open();
return $result;
- //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
- default:
+ case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
user_error('Unable to open channel');
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
- }
- break;
- case NET_SSH2_MSG_IGNORE:
- switch ($type) {
- case NET_SSH2_MSG_CHANNEL_SUCCESS:
- //$this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_DATA;
- continue 3;
- case NET_SSH2_MSG_CHANNEL_FAILURE:
- user_error('Error opening channel');
- return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ default:
+ if ($client_channel == $channel) {
+ user_error('Unexpected response to open request');
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
+ }
+ return $this->_get_channel_packet($client_channel, $skip_extended);
}
break;
case NET_SSH2_MSG_CHANNEL_REQUEST:
@@ -3915,6 +4092,14 @@ class SSH2
return true;
case NET_SSH2_MSG_CHANNEL_FAILURE:
return false;
+ case NET_SSH2_MSG_CHANNEL_DATA:
+ if (strlen($response) < 4) {
+ return false;
+ }
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
+ $data = $this->_string_shift($response, $length);
+ $this->channel_buffers[$channel][] = chr($type) . $data;
+ return $this->_get_channel_packet($client_channel, $skip_extended);
default:
user_error('Unable to fulfill channel request');
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
@@ -3928,10 +4113,6 @@ class SSH2
switch ($type) {
case NET_SSH2_MSG_CHANNEL_DATA:
- //if ($this->channel_status[$channel] == NET_SSH2_MSG_IGNORE) {
- // $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_DATA;
- //}
-
/*
if ($channel == self::CHANNEL_EXEC) {
// SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server
@@ -3958,10 +4139,7 @@ class SSH2
if ($client_channel == $channel) {
return $data;
}
- if (!isset($this->channel_buffers[$channel])) {
- $this->channel_buffers[$channel] = array();
- }
- $this->channel_buffers[$channel][] = $data;
+ $this->channel_buffers[$channel][] = chr($type) . $data;
break;
case NET_SSH2_MSG_CHANNEL_CLOSE:
$this->curTimeout = 5;
@@ -3980,7 +4158,7 @@ class SSH2
case NET_SSH2_MSG_CHANNEL_EOF:
break;
default:
- user_error('Error reading channel data');
+ user_error("Error reading channel data ($type)");
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
}
@@ -4005,11 +4183,27 @@ class SSH2
return false;
}
- //if ($this->compress) {
- // // the -4 removes the checksum:
- // // http://php.net/function.gzcompress#57710
- // $data = substr(gzcompress($data), 0, -4);
- //}
+ if (!isset($logged)) {
+ $logged = $data;
+ }
+
+ switch ($this->compress) {
+ case self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH:
+ if (!$this->isAuthenticated()) {
+ break;
+ }
+ case self::NET_SSH2_COMPRESSION_ZLIB:
+ if (!$this->regenerate_compression_context) {
+ $header = '';
+ } else {
+ $this->regenerate_compression_context = false;
+ $this->compress_context = deflate_init(ZLIB_ENCODING_RAW, array('window' => 15));
+ $header = "\x78\x9C";
+ }
+ if ($this->compress_context) {
+ $data = $header . deflate_add($this->compress_context, $data, ZLIB_PARTIAL_FLUSH);
+ }
+ }
// 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
$packet_length = strlen($data) + 9;
@@ -4037,10 +4231,10 @@ class SSH2
if (defined('NET_SSH2_LOGGING')) {
$current = microtime(true);
- $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')';
+ $message_number = isset($this->message_numbers[ord($logged[0])]) ? $this->message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')';
$message_number = '-> ' . $message_number .
' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
- $this->_append_log($message_number, isset($logged) ? $logged : $data);
+ $this->_append_log($message_number, $logged);
$this->last_packet = $current;
}
@@ -4735,10 +4929,12 @@ class SSH2
*/
function getSupportedCompressionAlgorithms()
{
- return array(
- 'none' // REQUIRED no compression
- //'zlib' // OPTIONAL ZLIB (LZ77) compression
- );
+ $algos = array('none'); // REQUIRED no compression
+ if (function_exists('deflate_init')) {
+ $algos[] = 'zlib@openssh.com'; // https://datatracker.ietf.org/doc/html/draft-miller-secsh-compression-delayed
+ $algos[] = 'zlib';
+ }
+ return $algos;
}
/**
@@ -4753,18 +4949,24 @@ class SSH2
{
$this->_connect();
+ $compression_map = array(
+ self::NET_SSH2_COMPRESSION_NONE => 'none',
+ self::NET_SSH2_COMPRESSION_ZLIB => 'zlib',
+ self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH => 'zlib@openssh.com'
+ );
+
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',
+ 'comp' => $compression_map[$this->compress],
),
'server_to_client' => array(
'crypt' => $this->decrypt->name,
'mac' => $this->hmac_check->name,
- 'comp' => 'none',
+ 'comp' => $compression_map[$this->decompress],
)
);
}
@@ -5173,8 +5375,24 @@ class SSH2
* @see https://tools.ietf.org/html/rfc4252#section-5.1
* @return array|null
*/
- public function getAuthMethodsToContinue()
+ function getAuthMethodsToContinue()
{
return $this->auth_methods_to_continue;
}
+
+ /**
+ * Enables "smart" multi-factor authentication (MFA)
+ */
+ function enableSmartMFA()
+ {
+ $this->smartMFA = true;
+ }
+
+ /**
+ * Disables "smart" multi-factor authentication (MFA)
+ */
+ function disableSmartMFA()
+ {
+ $this->smartMFA = false;
+ }
}
diff --git a/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php b/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php
index 2b25250b..f65b587c 100644
--- a/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php
+++ b/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php
@@ -234,11 +234,10 @@ class Agent
* Signal that agent forwarding should
* be requested when a channel is opened
*
- * @param Net_SSH2 $ssh
* @return bool
* @access public
*/
- function startSSHForwarding($ssh)
+ function startSSHForwarding()
{
if ($this->forward_status == self::FORWARD_NONE) {
$this->forward_status = self::FORWARD_REQUEST;
diff --git a/phpseclib/phpseclib/phpseclib/bootstrap.php b/phpseclib/phpseclib/phpseclib/bootstrap.php
index 0da0999f..547688f9 100644
--- a/phpseclib/phpseclib/phpseclib/bootstrap.php
+++ b/phpseclib/phpseclib/phpseclib/bootstrap.php
@@ -7,7 +7,8 @@
if (extension_loaded('mbstring')) {
// 2 - MB_OVERLOAD_STRING
- if (ini_get('mbstring.func_overload') & 2) {
+ // mbstring.func_overload is deprecated in php 7.2 and removed in php 8.0.
+ if (version_compare(PHP_VERSION, '8.0.0') < 0 && ini_get('mbstring.func_overload') & 2) {
throw new \UnexpectedValueException(
'Overloading of string functions using mbstring.func_overload ' .
'is not supported by phpseclib.'