diff --git a/securemail/composer.json b/securemail/composer.json index 14a2ca9a..435fc34b 100644 --- a/securemail/composer.json +++ b/securemail/composer.json @@ -11,7 +11,7 @@ ], "require": { "phpseclib/phpseclib": "^2.0", - "singpolyma/openpgp-php": "^0.3.0" + "singpolyma/openpgp-php": "^0.5.0" }, "license": "AGPL-3.0+", "minimum-stability": "stable", diff --git a/securemail/composer.lock b/securemail/composer.lock index 08de3972..f72f5be9 100644 --- a/securemail/composer.lock +++ b/securemail/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b3a33a493eae34d9245ca7cc1b4572ea", + "content-hash": "159d6e7134aeceeb05fdf60ef672f3a3", "packages": [ { "name": "phpseclib/phpseclib", - "version": "2.0.4", + "version": "2.0.34", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "ab8028c93c03cc8d9c824efa75dc94f1db2369bf" + "reference": "98a6fe587f3481aea319eef7e656d02cfe1675ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/ab8028c93c03cc8d9c824efa75dc94f1db2369bf", - "reference": "ab8028c93c03cc8d9c824efa75dc94f1db2369bf", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/98a6fe587f3481aea319eef7e656d02cfe1675ec", + "reference": "98a6fe587f3481aea319eef7e656d02cfe1675ec", "shasum": "" }, "require": { @@ -25,8 +25,7 @@ }, "require-dev": { "phing/phing": "~2.7", - "phpunit/phpunit": "~4.0", - "sami/sami": "~2.0", + "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4", "squizlabs/php_codesniffer": "~2.0" }, "suggest": { @@ -96,27 +95,49 @@ "x.509", "x509" ], - "time": "2016-10-04T00:57:04+00:00" + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/2.0.34" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2021-10-27T02:46:30+00:00" }, { "name": "singpolyma/openpgp-php", - "version": "0.3.0", + "version": "0.5.0", "source": { "type": "git", "url": "https://github.com/singpolyma/openpgp-php.git", - "reference": "6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8" + "reference": "69292f6a46ed7f687083bfb8974b161a41ab213c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/singpolyma/openpgp-php/zipball/6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8", - "reference": "6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8", + "url": "https://api.github.com/repos/singpolyma/openpgp-php/zipball/69292f6a46ed7f687083bfb8974b161a41ab213c", + "reference": "69292f6a46ed7f687083bfb8974b161a41ab213c", "shasum": "" }, "require": { - "phpseclib/phpseclib": ">=2.0.0 <=2.0.4" + "php": "^5.6 || ^7.0 || ^8.0", + "phpseclib/phpseclib": "^2.0 !=2.0.8" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "^9.0" + }, + "suggest": { + "ext-mcrypt": "required if you use encryption cast5" }, "type": "library", "autoload": { @@ -139,7 +160,25 @@ } ], "description": "Pure-PHP implementation of the OpenPGP Message Format (RFC 4880)", - "time": "2017-04-12T21:23:15+00:00" + "support": { + "issues": "https://github.com/singpolyma/openpgp-php/issues", + "source": "https://github.com/singpolyma/openpgp-php/tree/0.5.0" + }, + "funding": [ + { + "url": "https://github.com/singpolyma", + "type": "github" + }, + { + "url": "https://liberapay.com/singpolyma", + "type": "liberapay" + }, + { + "url": "https://www.patreon.com/singpolyma", + "type": "patreon" + } + ], + "time": "2021-05-26T00:35:20+00:00" } ], "packages-dev": [], @@ -149,5 +188,6 @@ "prefer-stable": false, "prefer-lowest": false, "platform": [], - "platform-dev": [] + "platform-dev": [], + "plugin-api-version": "2.0.0" } diff --git a/securemail/vendor/composer/ClassLoader.php b/securemail/vendor/composer/ClassLoader.php index 95f7e097..1a58957d 100644 --- a/securemail/vendor/composer/ClassLoader.php +++ b/securemail/vendor/composer/ClassLoader.php @@ -37,8 +37,8 @@ namespace Composer\Autoload; * * @author Fabien Potencier * @author Jordi Boggiano - * @see http://www.php-fig.org/psr/psr-0/ - * @see http://www.php-fig.org/psr/psr-4/ + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ */ class ClassLoader { @@ -60,7 +60,7 @@ class ClassLoader public function getPrefixes() { if (!empty($this->prefixesPsr0)) { - return call_user_func_array('array_merge', $this->prefixesPsr0); + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); } return array(); @@ -279,7 +279,7 @@ class ClassLoader */ public function setApcuPrefix($apcuPrefix) { - $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; } /** diff --git a/securemail/vendor/composer/InstalledVersions.php b/securemail/vendor/composer/InstalledVersions.php new file mode 100644 index 00000000..dc02cae0 --- /dev/null +++ b/securemail/vendor/composer/InstalledVersions.php @@ -0,0 +1,227 @@ + + array ( + 'pretty_version' => 'dev-develop', + 'version' => 'dev-develop', + 'aliases' => + array ( + ), + 'reference' => 'fb77e3c5ea0bcc6497dd6f24960b3d9ff1a159bd', + 'name' => 'friendica-addons/securemail', + ), + 'versions' => + array ( + 'friendica-addons/securemail' => + array ( + 'pretty_version' => 'dev-develop', + 'version' => 'dev-develop', + 'aliases' => + array ( + ), + 'reference' => 'fb77e3c5ea0bcc6497dd6f24960b3d9ff1a159bd', + ), + 'phpseclib/phpseclib' => + array ( + 'pretty_version' => '2.0.34', + 'version' => '2.0.34.0', + 'aliases' => + array ( + ), + 'reference' => '98a6fe587f3481aea319eef7e656d02cfe1675ec', + ), + 'singpolyma/openpgp-php' => + array ( + 'pretty_version' => '0.5.0', + 'version' => '0.5.0.0', + 'aliases' => + array ( + ), + 'reference' => '69292f6a46ed7f687083bfb8974b161a41ab213c', + ), + ), +); + + + + + + + +public static function getInstalledPackages() +{ +return array_keys(self::$installed['versions']); +} + + + + + + + + + +public static function isInstalled($packageName) +{ +return isset(self::$installed['versions'][$packageName]); +} + + + + + + + + + + + + + + +public static function satisfies(VersionParser $parser, $packageName, $constraint) +{ +$constraint = $parser->parseConstraints($constraint); +$provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + +return $provided->matches($constraint); +} + + + + + + + + + + +public static function getVersionRanges($packageName) +{ +if (!isset(self::$installed['versions'][$packageName])) { +throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); +} + +$ranges = array(); +if (isset(self::$installed['versions'][$packageName]['pretty_version'])) { +$ranges[] = self::$installed['versions'][$packageName]['pretty_version']; +} +if (array_key_exists('aliases', self::$installed['versions'][$packageName])) { +$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['aliases']); +} +if (array_key_exists('replaced', self::$installed['versions'][$packageName])) { +$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['replaced']); +} +if (array_key_exists('provided', self::$installed['versions'][$packageName])) { +$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['provided']); +} + +return implode(' || ', $ranges); +} + + + + + +public static function getVersion($packageName) +{ +if (!isset(self::$installed['versions'][$packageName])) { +throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); +} + +if (!isset(self::$installed['versions'][$packageName]['version'])) { +return null; +} + +return self::$installed['versions'][$packageName]['version']; +} + + + + + +public static function getPrettyVersion($packageName) +{ +if (!isset(self::$installed['versions'][$packageName])) { +throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); +} + +if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) { +return null; +} + +return self::$installed['versions'][$packageName]['pretty_version']; +} + + + + + +public static function getReference($packageName) +{ +if (!isset(self::$installed['versions'][$packageName])) { +throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); +} + +if (!isset(self::$installed['versions'][$packageName]['reference'])) { +return null; +} + +return self::$installed['versions'][$packageName]['reference']; +} + + + + + +public static function getRootPackage() +{ +return self::$installed['root']; +} + + + + + + + +public static function getRawData() +{ +return self::$installed; +} + + + + + + + + + + + + + + + + + + + +public static function reload($data) +{ +self::$installed = $data; +} +} diff --git a/securemail/vendor/composer/autoload_classmap.php b/securemail/vendor/composer/autoload_classmap.php index 65b2f24d..f832f412 100644 --- a/securemail/vendor/composer/autoload_classmap.php +++ b/securemail/vendor/composer/autoload_classmap.php @@ -6,6 +6,7 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'MCryptWrapper' => $vendorDir . '/singpolyma/openpgp-php/lib/openpgp_mcrypt_wrapper.php', 'OpenPGP' => $vendorDir . '/singpolyma/openpgp-php/lib/openpgp.php', 'OpenPGP_AsymmetricSessionKeyPacket' => $vendorDir . '/singpolyma/openpgp-php/lib/openpgp.php', @@ -55,4 +56,5 @@ return array( 'OpenPGP_TrustPacket' => $vendorDir . '/singpolyma/openpgp-php/lib/openpgp.php', 'OpenPGP_UserAttributePacket' => $vendorDir . '/singpolyma/openpgp-php/lib/openpgp.php', 'OpenPGP_UserIDPacket' => $vendorDir . '/singpolyma/openpgp-php/lib/openpgp.php', + 'OpenSSLWrapper' => $vendorDir . '/singpolyma/openpgp-php/lib/openpgp_openssl_wrapper.php', ); diff --git a/securemail/vendor/composer/autoload_real.php b/securemail/vendor/composer/autoload_real.php index 5b57bd5a..2db90803 100644 --- a/securemail/vendor/composer/autoload_real.php +++ b/securemail/vendor/composer/autoload_real.php @@ -13,19 +13,24 @@ class ComposerAutoloaderInitSecuremailAddon } } + /** + * @return \Composer\Autoload\ClassLoader + */ public static function getLoader() { if (null !== self::$loader) { return self::$loader; } + require __DIR__ . '/platform_check.php'; + spl_autoload_register(array('ComposerAutoloaderInitSecuremailAddon', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitSecuremailAddon', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { - require_once __DIR__ . '/autoload_static.php'; + require __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInitSecuremailAddon::getInitializer($loader)); } else { diff --git a/securemail/vendor/composer/autoload_static.php b/securemail/vendor/composer/autoload_static.php index fa77ac92..cdb7c788 100644 --- a/securemail/vendor/composer/autoload_static.php +++ b/securemail/vendor/composer/autoload_static.php @@ -25,6 +25,7 @@ class ComposerStaticInitSecuremailAddon ); public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 'MCryptWrapper' => __DIR__ . '/..' . '/singpolyma/openpgp-php/lib/openpgp_mcrypt_wrapper.php', 'OpenPGP' => __DIR__ . '/..' . '/singpolyma/openpgp-php/lib/openpgp.php', 'OpenPGP_AsymmetricSessionKeyPacket' => __DIR__ . '/..' . '/singpolyma/openpgp-php/lib/openpgp.php', @@ -74,6 +75,7 @@ class ComposerStaticInitSecuremailAddon 'OpenPGP_TrustPacket' => __DIR__ . '/..' . '/singpolyma/openpgp-php/lib/openpgp.php', 'OpenPGP_UserAttributePacket' => __DIR__ . '/..' . '/singpolyma/openpgp-php/lib/openpgp.php', 'OpenPGP_UserIDPacket' => __DIR__ . '/..' . '/singpolyma/openpgp-php/lib/openpgp.php', + 'OpenSSLWrapper' => __DIR__ . '/..' . '/singpolyma/openpgp-php/lib/openpgp_openssl_wrapper.php', ); public static function getInitializer(ClassLoader $loader) diff --git a/securemail/vendor/composer/installed.json b/securemail/vendor/composer/installed.json index 8181f155..a1feceae 100644 --- a/securemail/vendor/composer/installed.json +++ b/securemail/vendor/composer/installed.json @@ -1,141 +1,186 @@ -[ - { - "name": "phpseclib/phpseclib", - "version": "2.0.4", - "version_normalized": "2.0.4.0", - "source": { - "type": "git", - "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "ab8028c93c03cc8d9c824efa75dc94f1db2369bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/ab8028c93c03cc8d9c824efa75dc94f1db2369bf", - "reference": "ab8028c93c03cc8d9c824efa75dc94f1db2369bf", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phing/phing": "~2.7", - "phpunit/phpunit": "~4.0", - "sami/sami": "~2.0", - "squizlabs/php_codesniffer": "~2.0" - }, - "suggest": { - "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." - }, - "time": "2016-10-04T00:57:04+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "files": [ - "phpseclib/bootstrap.php" +{ + "packages": [ + { + "name": "phpseclib/phpseclib", + "version": "2.0.34", + "version_normalized": "2.0.34.0", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "98a6fe587f3481aea319eef7e656d02cfe1675ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/98a6fe587f3481aea319eef7e656d02cfe1675ec", + "reference": "98a6fe587f3481aea319eef7e656d02cfe1675ec", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phing/phing": "~2.7", + "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4", + "squizlabs/php_codesniffer": "~2.0" + }, + "suggest": { + "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." + }, + "time": "2021-10-27T02:46:30+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" ], - "psr-4": { - "phpseclib\\": "phpseclib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jim Wigginton", - "email": "terrafrost@php.net", - "role": "Lead Developer" + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/2.0.34" }, - { - "name": "Patrick Monnerat", - "email": "pm@datasphere.ch", - "role": "Developer" + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "install-path": "../phpseclib/phpseclib" + }, + { + "name": "singpolyma/openpgp-php", + "version": "0.5.0", + "version_normalized": "0.5.0.0", + "source": { + "type": "git", + "url": "https://github.com/singpolyma/openpgp-php.git", + "reference": "69292f6a46ed7f687083bfb8974b161a41ab213c" }, - { - "name": "Andreas Fischer", - "email": "bantu@phpbb.com", - "role": "Developer" + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/singpolyma/openpgp-php/zipball/69292f6a46ed7f687083bfb8974b161a41ab213c", + "reference": "69292f6a46ed7f687083bfb8974b161a41ab213c", + "shasum": "" }, - { - "name": "Hans-Jürgen Petrich", - "email": "petrich@tronic-media.com", - "role": "Developer" + "require": { + "php": "^5.6 || ^7.0 || ^8.0", + "phpseclib/phpseclib": "^2.0 !=2.0.8" }, - { - "name": "Graham Campbell", - "email": "graham@alt-three.com", - "role": "Developer" - } - ], - "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", - "homepage": "http://phpseclib.sourceforge.net", - "keywords": [ - "BigInteger", - "aes", - "asn.1", - "asn1", - "blowfish", - "crypto", - "cryptography", - "encryption", - "rsa", - "security", - "sftp", - "signature", - "signing", - "ssh", - "twofish", - "x.509", - "x509" - ] - }, - { - "name": "singpolyma/openpgp-php", - "version": "0.3.0", - "version_normalized": "0.3.0.0", - "source": { - "type": "git", - "url": "https://github.com/singpolyma/openpgp-php.git", - "reference": "6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/singpolyma/openpgp-php/zipball/6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8", - "reference": "6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8", - "shasum": "" - }, - "require": { - "phpseclib/phpseclib": ">=2.0.0 <=2.0.4" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "time": "2017-04-12T21:23:15+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "classmap": [ - "lib/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Unlicense" - ], - "authors": [ - { - "name": "Arto Bendiken", - "email": "arto.bendiken@gmail.com" + "require-dev": { + "phpunit/phpunit": "^9.0" }, - { - "name": "Stephen Paul Weber", - "email": "singpolyma@singpolyma.net" - } - ], - "description": "Pure-PHP implementation of the OpenPGP Message Format (RFC 4880)" - } -] + "suggest": { + "ext-mcrypt": "required if you use encryption cast5" + }, + "time": "2021-05-26T00:35:20+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Unlicense" + ], + "authors": [ + { + "name": "Arto Bendiken", + "email": "arto.bendiken@gmail.com" + }, + { + "name": "Stephen Paul Weber", + "email": "singpolyma@singpolyma.net" + } + ], + "description": "Pure-PHP implementation of the OpenPGP Message Format (RFC 4880)", + "support": { + "issues": "https://github.com/singpolyma/openpgp-php/issues", + "source": "https://github.com/singpolyma/openpgp-php/tree/0.5.0" + }, + "funding": [ + { + "url": "https://github.com/singpolyma", + "type": "github" + }, + { + "url": "https://liberapay.com/singpolyma", + "type": "liberapay" + }, + { + "url": "https://www.patreon.com/singpolyma", + "type": "patreon" + } + ], + "install-path": "../singpolyma/openpgp-php" + } + ], + "dev": true, + "dev-package-names": [] +} diff --git a/securemail/vendor/composer/installed.php b/securemail/vendor/composer/installed.php new file mode 100644 index 00000000..796db969 --- /dev/null +++ b/securemail/vendor/composer/installed.php @@ -0,0 +1,42 @@ + + array ( + 'pretty_version' => 'dev-develop', + 'version' => 'dev-develop', + 'aliases' => + array ( + ), + 'reference' => 'fb77e3c5ea0bcc6497dd6f24960b3d9ff1a159bd', + 'name' => 'friendica-addons/securemail', + ), + 'versions' => + array ( + 'friendica-addons/securemail' => + array ( + 'pretty_version' => 'dev-develop', + 'version' => 'dev-develop', + 'aliases' => + array ( + ), + 'reference' => 'fb77e3c5ea0bcc6497dd6f24960b3d9ff1a159bd', + ), + 'phpseclib/phpseclib' => + array ( + 'pretty_version' => '2.0.34', + 'version' => '2.0.34.0', + 'aliases' => + array ( + ), + 'reference' => '98a6fe587f3481aea319eef7e656d02cfe1675ec', + ), + 'singpolyma/openpgp-php' => + array ( + 'pretty_version' => '0.5.0', + 'version' => '0.5.0.0', + 'aliases' => + array ( + ), + 'reference' => '69292f6a46ed7f687083bfb8974b161a41ab213c', + ), + ), +); diff --git a/securemail/vendor/composer/platform_check.php b/securemail/vendor/composer/platform_check.php new file mode 100644 index 00000000..8b379f44 --- /dev/null +++ b/securemail/vendor/composer/platform_check.php @@ -0,0 +1,26 @@ += 50600)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 5.6.0". You are running ' . PHP_VERSION . '.'; +} + +if ($issues) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); + } elseif (!headers_sent()) { + echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; + } + } + trigger_error( + 'Composer detected issues in your platform: ' . implode(' ', $issues), + E_USER_ERROR + ); +} diff --git a/securemail/vendor/phpseclib/phpseclib/BACKERS.md b/securemail/vendor/phpseclib/phpseclib/BACKERS.md new file mode 100644 index 00000000..e03152ca --- /dev/null +++ b/securemail/vendor/phpseclib/phpseclib/BACKERS.md @@ -0,0 +1,8 @@ +# Backers + +phpseclib ongoing development is made possible by [Tidelift](https://tidelift.com/subscription/pkg/packagist-phpseclib-phpseclib?utm_source=packagist-phpseclib-phpseclib&utm_medium=referral&utm_campaign=readme) and by contributions by users like you. Thank you. + +## Backers + +- Zane Hooper +- [Setasign](https://www.setasign.com/) \ No newline at end of file diff --git a/securemail/vendor/phpseclib/phpseclib/LICENSE b/securemail/vendor/phpseclib/phpseclib/LICENSE index a8ec8ebd..e7214ebb 100644 --- a/securemail/vendor/phpseclib/phpseclib/LICENSE +++ b/securemail/vendor/phpseclib/phpseclib/LICENSE @@ -1,5 +1,4 @@ -Copyright 2007-2016 TerraFrost and other contributors -http://phpseclib.sourceforge.net/ +Copyright (c) 2011-2019 TerraFrost and other contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -18,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/securemail/vendor/phpseclib/phpseclib/README.md b/securemail/vendor/phpseclib/phpseclib/README.md index a6bde111..099486dc 100644 --- a/securemail/vendor/phpseclib/phpseclib/README.md +++ b/securemail/vendor/phpseclib/phpseclib/README.md @@ -1,22 +1,62 @@ # phpseclib - PHP Secure Communications Library -[![Build Status](https://travis-ci.org/phpseclib/phpseclib.svg?branch=2.0)](https://travis-ci.org/phpseclib/phpseclib) +[![Build Status](https://travis-ci.com/phpseclib/phpseclib.svg?branch=2.0)](https://travis-ci.com/phpseclib/phpseclib) -MIT-licensed pure-PHP implementations of an arbitrary-precision integer -arithmetic library, fully PKCS#1 (v2.1) compliant RSA, DES, 3DES, RC4, Rijndael, -AES, Blowfish, Twofish, SSH-1, SSH-2, SFTP, and X.509 +## Supporting phpseclib + +- [Become a backer or sponsor on Patreon](https://www.patreon.com/phpseclib) +- [One-time donation via PayPal or crypto-currencies](http://sourceforge.net/donate/index.php?group_id=198487) +- [Subscribe to Tidelift](https://tidelift.com/subscription/pkg/packagist-phpseclib-phpseclib?utm_source=packagist-phpseclib-phpseclib&utm_medium=referral&utm_campaign=readme) + +## Introduction + +MIT-licensed pure-PHP implementations of the following: + +SSH-2, SFTP, X.509, an arbitrary-precision integer arithmetic library, Ed25519 / Ed449 / Curve25519 / Curve449, ECDSA / ECDH (with support for 66 curves), RSA (PKCS#1 v2.2 compliant), DSA / DH, DES / 3DES / RC4 / Rijndael / AES / Blowfish / Twofish / Salsa20 / ChaCha20, GCM / Poly1305 -* [Download (1.0.4)](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.4.zip/download) * [Browse Git](https://github.com/phpseclib/phpseclib) -* [Code Coverage Report](http://phpseclib.bantux.org/code_coverage/2.0/latest/) - -PEAR Channel -PEAR Channel: [phpseclib.sourceforge.net](http://phpseclib.sourceforge.net/pear.htm) ## Documentation -* [Documentation / Manual](http://phpseclib.sourceforge.net/) -* [API Documentation](http://phpseclib.bantux.org/api/2.0/) (generated by Sami) +* [Documentation / Manual](https://phpseclib.com/) +* [API Documentation](https://api.phpseclib.com/2.0/) (generated by Doctum) + +## Branches + +### master + +* Development Branch +* Unstable API +* Do not use in production + +### 3.0 + +* Long term support (LTS) release +* Major expansion of cryptographic primitives +* Minimum PHP version: 5.6.1 +* PSR-4 autoloading with namespace rooted at `\phpseclib3` +* Install via Composer: `composer require phpseclib/phpseclib:~3.0` + +### 2.0 + +* Long term support (LTS) release +* Modernized version of 1.0 +* Minimum PHP version: 5.3.3 +* PSR-4 autoloading with namespace rooted at `\phpseclib` +* Install via Composer: `composer require phpseclib/phpseclib:~2.0` + +### 1.0 + +* Long term support (LTS) release +* PHP4 compatible +* Composer compatible (PSR-0 autoloading) +* Install using Composer: `composer require phpseclib/phpseclib:~1.0` +* Install using PEAR: See [phpseclib PEAR Channel Documentation](http://phpseclib.sourceforge.net/pear.htm) +* [Download 1.0.19 as ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.19.zip/download) + +## Security contact information + +To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. ## Support @@ -26,40 +66,29 @@ Need Support? * [Create a Support Ticket on GitHub](https://github.com/phpseclib/phpseclib/issues/new) * [Browse the Support Forum](http://www.frostjedi.com/phpbb/viewforum.php?f=46) (no longer in use) -## Installing Development Dependencies - -Dependencies are managed via Composer. - -1. Download the [`composer.phar`](https://getcomposer.org/composer.phar) executable as per the - [Composer Download Instructions](https://getcomposer.org/download/), e.g. by running - - ``` sh - curl -sS https://getcomposer.org/installer | php - ``` - -2. Install Dependencies - - ``` sh - php composer.phar install - ``` - ## Contributing 1. Fork the Project -2. Install Development Dependencies +2. Ensure you have Composer installed (see [Composer Download Instructions](https://getcomposer.org/download/)) -3. Create a Feature Branch +3. Install Development Dependencies -4. (Recommended) Run the Test Suite + ``` sh + composer install + ``` + +4. Create a Feature Branch + +5. (Recommended) Run the Test Suite ``` sh vendor/bin/phpunit ``` -5. (Recommended) Check whether your code conforms to our Coding Standards by running +6. (Recommended) Check whether your code conforms to our Coding Standards by running ``` sh vendor/bin/phing -f build/build.xml sniff ``` -6. Send us a Pull Request +7. Send us a Pull Request diff --git a/securemail/vendor/phpseclib/phpseclib/appveyor.yml b/securemail/vendor/phpseclib/phpseclib/appveyor.yml new file mode 100644 index 00000000..210a9034 --- /dev/null +++ b/securemail/vendor/phpseclib/phpseclib/appveyor.yml @@ -0,0 +1,27 @@ +build: false +shallow_clone: false +platform: + - x86 + - x64 +clone_folder: C:\projects\phpseclib + +install: + - cinst -y OpenSSL.Light + - SET PATH=C:\Program Files\OpenSSL;%PATH% + - sc config wuauserv start= auto + - net start wuauserv + - cinst -y php --version 5.6.30 + - cd c:\tools\php56 + - copy php.ini-production php.ini + - echo date.timezone="UTC" >> php.ini + - echo extension_dir=ext >> php.ini + - echo extension=php_openssl.dll >> php.ini + - echo extension=php_gmp.dll >> php.ini + - cd C:\projects\phpseclib + - SET PATH=C:\tools\php56;%PATH% + - php.exe -r "readfile('http://getcomposer.org/installer');" | php.exe + - php.exe composer.phar install --prefer-source --no-interaction + +test_script: + - cd C:\projects\phpseclib + - vendor\bin\phpunit.bat tests/Windows32Test.php \ No newline at end of file diff --git a/securemail/vendor/phpseclib/phpseclib/composer.json b/securemail/vendor/phpseclib/phpseclib/composer.json index 4b84b110..08b9c7c9 100644 --- a/securemail/vendor/phpseclib/phpseclib/composer.json +++ b/securemail/vendor/phpseclib/phpseclib/composer.json @@ -55,8 +55,7 @@ }, "require-dev": { "phing/phing": "~2.7", - "phpunit/phpunit": "~4.0", - "sami/sami": "~2.0", + "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4", "squizlabs/php_codesniffer": "~2.0" }, "suggest": { diff --git a/securemail/vendor/phpseclib/phpseclib/composer.lock b/securemail/vendor/phpseclib/phpseclib/composer.lock deleted file mode 100644 index beda2d64..00000000 --- a/securemail/vendor/phpseclib/phpseclib/composer.lock +++ /dev/null @@ -1,1819 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", - "This file is @generated automatically" - ], - "hash": "8599992bf6058a9da82372eb8bcae2c2", - "content-hash": "fde47c84178c55c06de858a2128e3d07", - "packages": [], - "packages-dev": [ - { - "name": "doctrine/instantiator", - "version": "1.0.5", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", - "shasum": "" - }, - "require": { - "php": ">=5.3,<8.0-DEV" - }, - "require-dev": { - "athletic/athletic": "~0.1.8", - "ext-pdo": "*", - "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", - "keywords": [ - "constructor", - "instantiate" - ], - "time": "2015-06-14 21:17:01" - }, - { - "name": "michelf/php-markdown", - "version": "1.6.0", - "source": { - "type": "git", - "url": "https://github.com/michelf/php-markdown.git", - "reference": "156e56ee036505ec637d761ee62dc425d807183c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/michelf/php-markdown/zipball/156e56ee036505ec637d761ee62dc425d807183c", - "reference": "156e56ee036505ec637d761ee62dc425d807183c", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-lib": "1.4.x-dev" - } - }, - "autoload": { - "psr-0": { - "Michelf": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Michel Fortin", - "email": "michel.fortin@michelf.ca", - "homepage": "https://michelf.ca/", - "role": "Developer" - }, - { - "name": "John Gruber", - "homepage": "https://daringfireball.net/" - } - ], - "description": "PHP Markdown", - "homepage": "https://michelf.ca/projects/php-markdown/", - "keywords": [ - "markdown" - ], - "time": "2015-12-24 01:37:31" - }, - { - "name": "nikic/php-parser", - "version": "v0.9.5", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "ef70767475434bdb3615b43c327e2cae17ef12eb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ef70767475434bdb3615b43c327e2cae17ef12eb", - "reference": "ef70767475434bdb3615b43c327e2cae17ef12eb", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.9-dev" - } - }, - "autoload": { - "psr-0": { - "PHPParser": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "time": "2014-07-23 18:24:17" - }, - { - "name": "phing/phing", - "version": "2.14.0", - "source": { - "type": "git", - "url": "https://github.com/phingofficial/phing.git", - "reference": "7dd73c83c377623def54b58121f46b4dcb35dd61" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phingofficial/phing/zipball/7dd73c83c377623def54b58121f46b4dcb35dd61", - "reference": "7dd73c83c377623def54b58121f46b4dcb35dd61", - "shasum": "" - }, - "require": { - "php": ">=5.2.0" - }, - "require-dev": { - "ext-pdo_sqlite": "*", - "lastcraft/simpletest": "@dev", - "mikey179/vfsstream": "^1.6", - "pdepend/pdepend": "2.x", - "pear/archive_tar": "1.4.x", - "pear/http_request2": "dev-trunk", - "pear/net_growl": "dev-trunk", - "pear/pear-core-minimal": "1.10.1", - "pear/versioncontrol_git": "@dev", - "pear/versioncontrol_svn": "~0.5", - "phpdocumentor/phpdocumentor": "2.x", - "phploc/phploc": "~2.0.6", - "phpmd/phpmd": "~2.2", - "phpunit/phpunit": ">=3.7", - "sebastian/git": "~1.0", - "sebastian/phpcpd": "2.x", - "squizlabs/php_codesniffer": "~2.2", - "symfony/yaml": "~2.7" - }, - "suggest": { - "pdepend/pdepend": "PHP version of JDepend", - "pear/archive_tar": "Tar file management class", - "pear/versioncontrol_git": "A library that provides OO interface to handle Git repository", - "pear/versioncontrol_svn": "A simple OO-style interface for Subversion, the free/open-source version control system", - "phpdocumentor/phpdocumentor": "Documentation Generator for PHP", - "phploc/phploc": "A tool for quickly measuring the size of a PHP project", - "phpmd/phpmd": "PHP version of PMD tool", - "phpunit/php-code-coverage": "Library that provides collection, processing, and rendering functionality for PHP code coverage information", - "phpunit/phpunit": "The PHP Unit Testing Framework", - "sebastian/phpcpd": "Copy/Paste Detector (CPD) for PHP code", - "tedivm/jshrink": "Javascript Minifier built in PHP" - }, - "bin": [ - "bin/phing" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.14.x-dev" - } - }, - "autoload": { - "classmap": [ - "classes/phing/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "classes" - ], - "license": [ - "LGPL-3.0" - ], - "authors": [ - { - "name": "Michiel Rook", - "email": "mrook@php.net" - }, - { - "name": "Phing Community", - "homepage": "https://www.phing.info/trac/wiki/Development/Contributors" - } - ], - "description": "PHing Is Not GNU make; it's a PHP project build system or build tool based on Apache Ant.", - "homepage": "https://www.phing.info/", - "keywords": [ - "build", - "phing", - "task", - "tool" - ], - "time": "2016-03-10 21:39:23" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "1.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", - "shasum": "" - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "time": "2015-12-27 11:43:31" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "9270140b940ff02e58ec577c237274e92cd40cdd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9270140b940ff02e58ec577c237274e92cd40cdd", - "reference": "9270140b940ff02e58ec577c237274e92cd40cdd", - "shasum": "" - }, - "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^1.0@dev", - "phpdocumentor/type-resolver": "^0.2.0", - "webmozart/assert": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^4.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2016-06-10 09:48:41" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "0.2", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b39c7a5b194f9ed7bd0dd345c751007a41862443", - "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443", - "shasum": "" - }, - "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "time": "2016-06-10 07:14:17" - }, - { - "name": "phpspec/prophecy", - "version": "v1.6.1", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "58a8137754bc24b25740d4281399a4a3596058e0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/58a8137754bc24b25740d4281399a4a3596058e0", - "reference": "58a8137754bc24b25740d4281399a4a3596058e0", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", - "sebastian/comparator": "^1.1", - "sebastian/recursion-context": "^1.0" - }, - "require-dev": { - "phpspec/phpspec": "^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6.x-dev" - } - }, - "autoload": { - "psr-0": { - "Prophecy\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "time": "2016-06-07 08:13:47" - }, - { - "name": "phpunit/php-code-coverage", - "version": "2.2.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", - "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "phpunit/php-file-iterator": "~1.3", - "phpunit/php-text-template": "~1.2", - "phpunit/php-token-stream": "~1.3", - "sebastian/environment": "^1.3.2", - "sebastian/version": "~1.0" - }, - "require-dev": { - "ext-xdebug": ">=2.1.4", - "phpunit/phpunit": "~4" - }, - "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.2.1", - "ext-xmlwriter": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.2.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "time": "2015-10-06 15:47:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "1.4.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", - "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "time": "2015-06-21 13:08:43" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2015-06-21 13:50:34" - }, - { - "name": "phpunit/php-timer", - "version": "1.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", - "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4|~5" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "time": "2016-05-12 18:03:57" - }, - { - "name": "phpunit/php-token-stream", - "version": "1.4.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", - "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "time": "2015-09-15 10:49:45" - }, - { - "name": "phpunit/phpunit", - "version": "4.8.26", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "fc1d8cd5b5de11625979125c5639347896ac2c74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fc1d8cd5b5de11625979125c5639347896ac2c74", - "reference": "fc1d8cd5b5de11625979125c5639347896ac2c74", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=5.3.3", - "phpspec/prophecy": "^1.3.1", - "phpunit/php-code-coverage": "~2.1", - "phpunit/php-file-iterator": "~1.4", - "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": "^1.0.6", - "phpunit/phpunit-mock-objects": "~2.3", - "sebastian/comparator": "~1.1", - "sebastian/diff": "~1.2", - "sebastian/environment": "~1.3", - "sebastian/exporter": "~1.2", - "sebastian/global-state": "~1.0", - "sebastian/version": "~1.0", - "symfony/yaml": "~2.1|~3.0" - }, - "suggest": { - "phpunit/php-invoker": "~1.1" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.8.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "time": "2016-05-17 03:09:28" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "2.3.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", - "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": ">=5.3.3", - "phpunit/php-text-template": "~1.2", - "sebastian/exporter": "~1.2" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "time": "2015-10-02 06:51:40" - }, - { - "name": "pimple/pimple", - "version": "v2.1.1", - "source": { - "type": "git", - "url": "https://github.com/silexphp/Pimple.git", - "reference": "ea22fb2880faf7b7b0e17c9809c6fe25b071fd76" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/silexphp/Pimple/zipball/ea22fb2880faf7b7b0e17c9809c6fe25b071fd76", - "reference": "ea22fb2880faf7b7b0e17c9809c6fe25b071fd76", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1.x-dev" - } - }, - "autoload": { - "psr-0": { - "Pimple": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", - "homepage": "http://pimple.sensiolabs.org", - "keywords": [ - "container", - "dependency injection" - ], - "time": "2014-07-24 07:10:08" - }, - { - "name": "sami/sami", - "version": "v2.0.0", - "source": { - "type": "git", - "url": "https://github.com/FriendsOfPHP/Sami.git", - "reference": "fa58b324f41aa2aefe21dac4f22d8c98965fc012" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/Sami/zipball/fa58b324f41aa2aefe21dac4f22d8c98965fc012", - "reference": "fa58b324f41aa2aefe21dac4f22d8c98965fc012", - "shasum": "" - }, - "require": { - "michelf/php-markdown": "~1.3", - "nikic/php-parser": "0.9.*", - "php": ">=5.3.0", - "pimple/pimple": "2.*", - "symfony/console": "~2.1", - "symfony/filesystem": "~2.1", - "symfony/finder": "~2.1", - "symfony/process": "~2.1", - "symfony/yaml": "~2.1", - "twig/twig": "1.*" - }, - "bin": [ - "sami.php" - ], - "type": "application", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "psr-0": { - "Sami": "." - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Sami, an API documentation generator", - "homepage": "http://sami.sensiolabs.org", - "keywords": [ - "phpdoc" - ], - "time": "2014-06-25 12:05:18" - }, - { - "name": "sebastian/comparator", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", - "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "http://www.github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "time": "2015-07-26 15:48:44" - }, - { - "name": "sebastian/diff", - "version": "1.4.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", - "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff" - ], - "time": "2015-12-08 07:14:41" - }, - { - "name": "sebastian/environment", - "version": "1.3.7", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/4e8f0da10ac5802913afc151413bc8c53b6c2716", - "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "time": "2016-05-17 03:18:57" - }, - { - "name": "sebastian/exporter", - "version": "1.2.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", - "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "sebastian/recursion-context": "~1.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "time": "2016-06-17 09:04:28" - }, - { - "name": "sebastian/global-state", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "time": "2015-10-12 03:26:01" - }, - { - "name": "sebastian/recursion-context", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "913401df809e99e4f47b27cdd781f4a258d58791" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791", - "reference": "913401df809e99e4f47b27cdd781f4a258d58791", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2015-11-11 19:50:13" - }, - { - "name": "sebastian/version", - "version": "1.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", - "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", - "shasum": "" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2015-06-21 13:59:46" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "2.6.1", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "fb72ed32f8418db5e7770be1653e62e0d6f5dd3d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/fb72ed32f8418db5e7770be1653e62e0d6f5dd3d", - "reference": "fb72ed32f8418db5e7770be1653e62e0d6f5dd3d", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.1.2" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "bin": [ - "scripts/phpcs", - "scripts/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "classmap": [ - "CodeSniffer.php", - "CodeSniffer/CLI.php", - "CodeSniffer/Exception.php", - "CodeSniffer/File.php", - "CodeSniffer/Fixer.php", - "CodeSniffer/Report.php", - "CodeSniffer/Reporting.php", - "CodeSniffer/Sniff.php", - "CodeSniffer/Tokens.php", - "CodeSniffer/Reports/", - "CodeSniffer/Tokenizers/", - "CodeSniffer/DocGenerators/", - "CodeSniffer/Standards/AbstractPatternSniff.php", - "CodeSniffer/Standards/AbstractScopeSniff.php", - "CodeSniffer/Standards/AbstractVariableSniff.php", - "CodeSniffer/Standards/IncorrectPatternException.php", - "CodeSniffer/Standards/Generic/Sniffs/", - "CodeSniffer/Standards/MySource/Sniffs/", - "CodeSniffer/Standards/PEAR/Sniffs/", - "CodeSniffer/Standards/PSR1/Sniffs/", - "CodeSniffer/Standards/PSR2/Sniffs/", - "CodeSniffer/Standards/Squiz/Sniffs/", - "CodeSniffer/Standards/Zend/Sniffs/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "http://www.squizlabs.com/php-codesniffer", - "keywords": [ - "phpcs", - "standards" - ], - "time": "2016-05-30 22:24:32" - }, - { - "name": "symfony/console", - "version": "v2.8.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3", - "reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3", - "shasum": "" - }, - "require": { - "php": ">=5.3.9", - "symfony/polyfill-mbstring": "~1.0" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/event-dispatcher": "~2.1|~3.0.0", - "symfony/process": "~2.1|~3.0.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com", - "time": "2016-06-06 15:06:25" - }, - { - "name": "symfony/filesystem", - "version": "v2.8.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "dee379131dceed90a429e951546b33edfe7dccbb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/dee379131dceed90a429e951546b33edfe7dccbb", - "reference": "dee379131dceed90a429e951546b33edfe7dccbb", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "https://symfony.com", - "time": "2016-04-12 18:01:21" - }, - { - "name": "symfony/finder", - "version": "v2.8.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "3ec095fab1800222732ca522a95dce8fa124007b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/3ec095fab1800222732ca522a95dce8fa124007b", - "reference": "3ec095fab1800222732ca522a95dce8fa124007b", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Finder Component", - "homepage": "https://symfony.com", - "time": "2016-06-06 11:11:27" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.2.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "dff51f72b0706335131b00a7f49606168c582594" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/dff51f72b0706335131b00a7f49606168c582594", - "reference": "dff51f72b0706335131b00a7f49606168c582594", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "time": "2016-05-18 14:26:46" - }, - { - "name": "symfony/process", - "version": "v2.8.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "115347d00c342198cdc52a7bd8bc15b5ab43500c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/115347d00c342198cdc52a7bd8bc15b5ab43500c", - "reference": "115347d00c342198cdc52a7bd8bc15b5ab43500c", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Process Component", - "homepage": "https://symfony.com", - "time": "2016-06-06 11:11:27" - }, - { - "name": "symfony/yaml", - "version": "v2.8.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "815fabf3f48c7d1df345a69d1ad1a88f59757b34" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/815fabf3f48c7d1df345a69d1ad1a88f59757b34", - "reference": "815fabf3f48c7d1df345a69d1ad1a88f59757b34", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", - "time": "2016-06-06 11:11:27" - }, - { - "name": "twig/twig", - "version": "v1.24.1", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "3566d311a92aae4deec6e48682dc5a4528c4a512" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/3566d311a92aae4deec6e48682dc5a4528c4a512", - "reference": "3566d311a92aae4deec6e48682dc5a4528c4a512", - "shasum": "" - }, - "require": { - "php": ">=5.2.7" - }, - "require-dev": { - "symfony/debug": "~2.7", - "symfony/phpunit-bridge": "~2.7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.24-dev" - } - }, - "autoload": { - "psr-0": { - "Twig_": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com", - "role": "Project Founder" - }, - { - "name": "Twig Team", - "homepage": "http://twig.sensiolabs.org/contributors", - "role": "Contributors" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "http://twig.sensiolabs.org", - "keywords": [ - "templating" - ], - "time": "2016-05-30 09:11:59" - }, - { - "name": "webmozart/assert", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde", - "reference": "30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "time": "2015-08-24 13:29:44" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=5.3.3" - }, - "platform-dev": [] -} diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php index fd99c44f..8822b9b8 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php @@ -76,6 +76,10 @@ abstract class Base * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 */ const MODE_CFB = 3; + /** + * Encrypt / decrypt using the Cipher Feedback mode (8bit) + */ + const MODE_CFB8 = 38; /** * Encrypt / decrypt using the Output Feedback mode. * @@ -409,7 +413,7 @@ abstract class Base * @var mixed * @access private */ - var $use_inline_crypt; + var $use_inline_crypt = true; /** * If OpenSSL can be used in ECB but not in CTR we can emulate CTR @@ -479,6 +483,7 @@ abstract class Base break; case self::MODE_CTR: case self::MODE_CFB: + case self::MODE_CFB8: case self::MODE_OFB: case self::MODE_STREAM: $this->mode = $mode; @@ -490,11 +495,6 @@ abstract class Base } $this->_setEngine(); - - // Determining whether inline crypting can be used by the cipher - if ($this->use_inline_crypt !== false && function_exists('create_function')) { - $this->use_inline_crypt = true; - } } /** @@ -644,10 +644,10 @@ abstract class Base case !function_exists('hash_algos'): case !in_array($hash, hash_algos()): $i = 1; + $hmac = new Hash(); + $hmac->setHash($hash); + $hmac->setKey($password); while (strlen($key) < $dkLen) { - $hmac = new Hash(); - $hmac->setHash($hash); - $hmac->setKey($password); $f = $u = $hmac->hash($salt . pack('N', $i++)); for ($j = 2; $j <= $count; ++$j) { $u = $hmac->hash($u); @@ -702,7 +702,7 @@ abstract class Base case self::MODE_STREAM: return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); case self::MODE_ECB: - $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); + $result = @openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); return !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result; case self::MODE_CBC: $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV); @@ -762,6 +762,16 @@ abstract class Base $iv = substr($ciphertext, -$this->block_size); } + return $ciphertext; + case self::MODE_CFB8: + $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV); + if ($this->continuousBuffer) { + if (($len = strlen($ciphertext)) >= $this->block_size) { + $this->encryptIV = substr($ciphertext, -$this->block_size); + } else { + $this->encryptIV = substr($this->encryptIV, $len - $this->block_size) . substr($ciphertext, -$len); + } + } return $ciphertext; case self::MODE_OFB: return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer); @@ -769,12 +779,14 @@ abstract class Base } if ($this->engine === self::ENGINE_MCRYPT) { + set_error_handler(array($this, 'do_nothing')); + if ($this->changed) { $this->_setupMcrypt(); $this->changed = false; } if ($this->enchanged) { - @mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); $this->enchanged = false; } @@ -807,15 +819,15 @@ abstract class Base if ($len >= $block_size) { if ($this->enbuffer['enmcrypt_init'] === false || $len > $this->cfb_init_len) { if ($this->enbuffer['enmcrypt_init'] === true) { - @mcrypt_generic_init($this->enmcrypt, $this->key, $iv); + mcrypt_generic_init($this->enmcrypt, $this->key, $iv); $this->enbuffer['enmcrypt_init'] = false; } - $ciphertext.= @mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size)); + $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size)); $iv = substr($ciphertext, -$block_size); $len%= $block_size; } else { while ($len >= $block_size) { - $iv = @mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size); + $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size); $ciphertext.= $iv; $len-= $block_size; $i+= $block_size; @@ -824,22 +836,26 @@ abstract class Base } if ($len) { - $iv = @mcrypt_generic($this->ecb, $iv); + $iv = mcrypt_generic($this->ecb, $iv); $block = $iv ^ substr($plaintext, -$len); $iv = substr_replace($iv, $block, 0, $len); $ciphertext.= $block; $pos = $len; } + restore_error_handler(); + return $ciphertext; } - $ciphertext = @mcrypt_generic($this->enmcrypt, $plaintext); + $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); if (!$this->continuousBuffer) { - @mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); } + restore_error_handler(); + return $ciphertext; } @@ -942,6 +958,24 @@ abstract class Base $pos = $len; } break; + case self::MODE_CFB8: + $ciphertext = ''; + $len = strlen($plaintext); + $iv = $this->encryptIV; + + for ($i = 0; $i < $len; ++$i) { + $ciphertext .= ($c = $plaintext[$i] ^ $this->_encryptBlock($iv)); + $iv = substr($iv, 1) . $c; + } + + if ($this->continuousBuffer) { + if ($len >= $block_size) { + $this->encryptIV = substr($ciphertext, -$block_size); + } else { + $this->encryptIV = substr($this->encryptIV, $len - $block_size) . substr($ciphertext, -$len); + } + } + break; case self::MODE_OFB: $xor = $this->encryptIV; if (strlen($buffer['xor'])) { @@ -1007,14 +1041,14 @@ abstract class Base break; case self::MODE_ECB: if (!defined('OPENSSL_RAW_DATA')) { - $ciphertext.= openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true); + $ciphertext.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true); } $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options); break; case self::MODE_CBC: if (!defined('OPENSSL_RAW_DATA')) { $padding = str_repeat(chr($this->block_size), $this->block_size) ^ substr($ciphertext, -$this->block_size); - $ciphertext.= substr(openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size); + $ciphertext.= substr(@openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size); $offset = 2 * $this->block_size; } else { $offset = $this->block_size; @@ -1072,6 +1106,16 @@ abstract class Base $iv = substr($ciphertext, -$this->block_size); } break; + case self::MODE_CFB8: + $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->decryptIV); + if ($this->continuousBuffer) { + if (($len = strlen($ciphertext)) >= $this->block_size) { + $this->decryptIV = substr($ciphertext, -$this->block_size); + } else { + $this->decryptIV = substr($this->decryptIV, $len - $this->block_size) . substr($ciphertext, -$len); + } + } + break; case self::MODE_OFB: $plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer); } @@ -1080,13 +1124,14 @@ abstract class Base } if ($this->engine === self::ENGINE_MCRYPT) { + set_error_handler(array($this, 'do_nothing')); $block_size = $this->block_size; if ($this->changed) { $this->_setupMcrypt(); $this->changed = false; } if ($this->dechanged) { - @mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); $this->dechanged = false; } @@ -1114,26 +1159,30 @@ abstract class Base } if ($len >= $block_size) { $cb = substr($ciphertext, $i, $len - $len % $block_size); - $plaintext.= @mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; + $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; $iv = substr($cb, -$block_size); $len%= $block_size; } if ($len) { - $iv = @mcrypt_generic($this->ecb, $iv); + $iv = mcrypt_generic($this->ecb, $iv); $plaintext.= $iv ^ substr($ciphertext, -$len); $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len); $pos = $len; } + restore_error_handler(); + return $plaintext; } - $plaintext = @mdecrypt_generic($this->demcrypt, $ciphertext); + $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); if (!$this->continuousBuffer) { - @mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); } + restore_error_handler(); + return $this->paddable ? $this->_unpad($plaintext) : $plaintext; } @@ -1235,6 +1284,24 @@ abstract class Base $pos = $len; } break; + case self::MODE_CFB8: + $plaintext = ''; + $len = strlen($ciphertext); + $iv = $this->decryptIV; + + for ($i = 0; $i < $len; ++$i) { + $plaintext .= $ciphertext[$i] ^ $this->_encryptBlock($iv); + $iv = substr($iv, 1) . $ciphertext[$i]; + } + + if ($this->continuousBuffer) { + if ($len >= $block_size) { + $this->decryptIV = substr($ciphertext, -$block_size); + } else { + $this->decryptIV = substr($this->decryptIV, $len - $block_size) . substr($ciphertext, -$len); + } + } + break; case self::MODE_OFB: $xor = $this->decryptIV; if (strlen($buffer['xor'])) { @@ -1297,7 +1364,7 @@ abstract class Base for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); if (strlen($block) > strlen($buffer['ciphertext'])) { - $result = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); + $result = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); $result = !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result; $buffer['ciphertext'].= $result; } @@ -1308,7 +1375,7 @@ abstract class Base } else { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); - $otp = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); + $otp = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); $otp = !defined('OPENSSL_RAW_DATA') ? substr($otp, 0, -$this->block_size) : $otp; $this->_increment_str($xor); $ciphertext.= $block ^ $otp; @@ -1352,7 +1419,7 @@ abstract class Base } if ($this->continuousBuffer) { if (!defined('OPENSSL_RAW_DATA')) { - $encryptIV.= openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options); + $encryptIV.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options); } $encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); if ($overflow) { @@ -1435,6 +1502,8 @@ abstract class Base return 'ctr'; case self::MODE_CFB: return 'cfb'; + case self::MODE_CFB8: + return 'cfb8'; case self::MODE_OFB: return 'ofb'; } @@ -1591,9 +1660,12 @@ abstract class Base } return false; case self::ENGINE_MCRYPT: - return $this->cipher_name_mcrypt && + set_error_handler(array($this, 'do_nothing')); + $result = $this->cipher_name_mcrypt && extension_loaded('mcrypt') && - in_array($this->cipher_name_mcrypt, @mcrypt_list_algorithms()); + in_array($this->cipher_name_mcrypt, mcrypt_list_algorithms()); + restore_error_handler(); + return $result; case self::ENGINE_INTERNAL: return true; } @@ -1670,17 +1742,19 @@ abstract class Base } if ($this->engine != self::ENGINE_MCRYPT && $this->enmcrypt) { + set_error_handler(array($this, 'do_nothing')); // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed, // (re)open them with the module named in $this->cipher_name_mcrypt - @mcrypt_module_close($this->enmcrypt); - @mcrypt_module_close($this->demcrypt); + mcrypt_module_close($this->enmcrypt); + mcrypt_module_close($this->demcrypt); $this->enmcrypt = null; $this->demcrypt = null; if ($this->ecb) { - @mcrypt_module_close($this->ecb); + mcrypt_module_close($this->ecb); $this->ecb = null; } + restore_error_handler(); } $this->changed = true; @@ -1788,23 +1862,24 @@ abstract class Base self::MODE_ECB => MCRYPT_MODE_ECB, self::MODE_CBC => MCRYPT_MODE_CBC, self::MODE_CFB => 'ncfb', + self::MODE_CFB8 => MCRYPT_MODE_CFB, self::MODE_OFB => MCRYPT_MODE_NOFB, self::MODE_STREAM => MCRYPT_MODE_STREAM, ); - $this->demcrypt = @mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); - $this->enmcrypt = @mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); + $this->demcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); + $this->enmcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer() // to workaround mcrypt's broken ncfb implementation in buffered mode // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} if ($this->mode == self::MODE_CFB) { - $this->ecb = @mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, ''); + $this->ecb = mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, ''); } } // else should mcrypt_generic_deinit be called? if ($this->mode == self::MODE_CFB) { - @mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size)); + mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size)); } } @@ -2359,6 +2434,52 @@ abstract class Base $_pos = $_len; } + return $_plaintext; + '; + break; + case self::MODE_CFB8: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_len = strlen($_text); + $_iv = $self->encryptIV; + + for ($_i = 0; $_i < $_len; ++$_i) { + $in = $_iv; + '.$encrypt_block.' + $_ciphertext .= ($_c = $_text[$_i] ^ $in); + $_iv = substr($_iv, 1) . $_c; + } + + if ($self->continuousBuffer) { + if ($_len >= '.$block_size.') { + $self->encryptIV = substr($_ciphertext, -'.$block_size.'); + } else { + $self->encryptIV = substr($self->encryptIV, $_len - '.$block_size.') . substr($_ciphertext, -$_len); + } + } + + 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) . $_text[$_i]; + } + + if ($self->continuousBuffer) { + if ($_len >= '.$block_size.') { + $self->decryptIV = substr($_text, -'.$block_size.'); + } else { + $self->decryptIV = substr($self->decryptIV, $_len - '.$block_size.') . substr($_text, -$_len); + } + } + return $_plaintext; '; break; @@ -2492,7 +2613,8 @@ abstract class Base } // Create the $inline function and return its name as string. Ready to run! - return create_function('$_action, &$self, $_text', $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' }'); + eval('$func = function ($_action, &$self, $_text) { ' . $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' } };'); + return $func; } /** @@ -2521,7 +2643,7 @@ abstract class Base * * @see self::_setupInlineCrypt() * @access private - * @param $bytes + * @param string $bytes * @return string */ function _hashInlineCryptFunction($bytes) @@ -2550,4 +2672,53 @@ abstract class Base return $result . pack('H*', sha1($hash)); } } + + /** + * Convert float to int + * + * On ARM CPUs converting floats to ints doesn't always work + * + * @access private + * @param string $x + * @return int + */ + 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; + } + return (fmod($x, 0x80000000) & 0x7FFFFFFF) | + ((fmod(floor($x / 0x80000000), 2) & 1) << 31); + } + + /** + * eval()'able string for in-line float to int + * + * @access private + * @return string + */ + 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))'; + } + } + + /** + * Dummy error handler to suppress mcrypt errors + * + * @access private + */ + function do_nothing() + { + } } diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php index 3949e812..74cc49de 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php @@ -294,7 +294,7 @@ class Blowfish extends Base function setKeyLength($length) { if ($length < 32) { - $this->key_length = 7; + $this->key_length = 4; } elseif ($length > 448) { $this->key_length = 56; } else { @@ -317,7 +317,10 @@ class Blowfish extends Base function isValidEngine($engine) { if ($engine == self::ENGINE_OPENSSL) { - if ($this->key_length != 16) { + if (version_compare(PHP_VERSION, '5.3.7') < 0 && $this->key_length != 16) { + return false; + } + if ($this->key_length < 16) { return false; } $this->cipher_name_openssl_ecb = 'bf-ecb'; @@ -405,16 +408,14 @@ class Blowfish extends Base for ($i = 0; $i < 16; $i+= 2) { $l^= $p[$i]; - $r^= ($sb_0[$l >> 24 & 0xff] + - $sb_1[$l >> 16 & 0xff] ^ + $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]; + $sb_3[$l & 0xff]); $r^= $p[$i + 1]; - $l^= ($sb_0[$r >> 24 & 0xff] + - $sb_1[$r >> 16 & 0xff] ^ + $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]; + $sb_3[$r & 0xff]); } return pack("N*", $r ^ $p[17], $l ^ $p[16]); } @@ -440,16 +441,14 @@ class Blowfish extends Base for ($i = 17; $i > 2; $i-= 2) { $l^= $p[$i]; - $r^= ($sb_0[$l >> 24 & 0xff] + - $sb_1[$l >> 16 & 0xff] ^ + $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]; + $sb_3[$l & 0xff]); $r^= $p[$i - 1]; - $l^= ($sb_0[$r >> 24 & 0xff] + - $sb_1[$r >> 16 & 0xff] ^ + $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]; + $sb_3[$r & 0xff]); } return pack("N*", $r ^ $p[0], $l ^ $p[1]); } @@ -475,6 +474,8 @@ class Blowfish extends Base $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); } + $safeint = $this->safe_intval_inline(); + if (!isset($lambda_functions[$code_hash])) { switch (true) { case $gen_hi_opt_code: @@ -510,16 +511,14 @@ class Blowfish extends Base for ($i = 0; $i < 16; $i+= 2) { $encrypt_block.= ' $l^= ' . $p[$i] . '; - $r^= ($sb_0[$l >> 24 & 0xff] + - $sb_1[$l >> 16 & 0xff] ^ + $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^ $sb_2[$l >> 8 & 0xff]) + - $sb_3[$l & 0xff]; + $sb_3[$l & 0xff]') . '; $r^= ' . $p[$i + 1] . '; - $l^= ($sb_0[$r >> 24 & 0xff] + - $sb_1[$r >> 16 & 0xff] ^ + $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^ $sb_2[$r >> 8 & 0xff]) + - $sb_3[$r & 0xff]; + $sb_3[$r & 0xff]') . '; '; } $encrypt_block.= ' @@ -539,16 +538,14 @@ class Blowfish extends Base for ($i = 17; $i > 2; $i-= 2) { $decrypt_block.= ' $l^= ' . $p[$i] . '; - $r^= ($sb_0[$l >> 24 & 0xff] + - $sb_1[$l >> 16 & 0xff] ^ + $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^ $sb_2[$l >> 8 & 0xff]) + - $sb_3[$l & 0xff]; + $sb_3[$l & 0xff]') . '; $r^= ' . $p[$i - 1] . '; - $l^= ($sb_0[$r >> 24 & 0xff] + - $sb_1[$r >> 16 & 0xff] ^ + $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^ $sb_2[$r >> 8 & 0xff]) + - $sb_3[$r & 0xff]; + $sb_3[$r & 0xff]') . '; '; } diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php index 51220469..9a8225fb 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php @@ -1357,8 +1357,8 @@ class DES extends Base $k[self::ENCRYPT][$i] = '$ke[' . $i . ']'; $k[self::DECRYPT][$i] = '$kd[' . $i . ']'; } - $init_encrypt = '$ke = $self->keys[self::ENCRYPT];'; - $init_decrypt = '$kd = $self->keys[self::DECRYPT];'; + $init_encrypt = '$ke = $self->keys[$self::ENCRYPT];'; + $init_decrypt = '$kd = $self->keys[$self::DECRYPT];'; break; } diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php index 07665a16..248b65ef 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php @@ -112,6 +112,15 @@ class Hash */ var $key = false; + /** + * Computed Key + * + * @see self::_computeKey() + * @var string + * @access private + */ + var $computedKey = false; + /** * Outer XOR (Internal HMAC) * @@ -130,6 +139,15 @@ class Hash */ var $ipad; + /** + * Engine + * + * @see self::setHash() + * @var string + * @access private + */ + var $engine; + /** * Default Constructor. * @@ -166,6 +184,43 @@ class Hash function setKey($key = false) { $this->key = $key; + $this->_computeKey(); + } + + /** + * Pre-compute the key used by the HMAC + * + * Quoting http://tools.ietf.org/html/rfc2104#section-2, "Applications that use keys longer than B bytes + * will first hash the key using H and then use the resultant L byte string as the actual key to HMAC." + * + * As documented in https://www.reddit.com/r/PHP/comments/9nct2l/symfonypolyfill_hash_pbkdf2_correct_fix_for/ + * when doing an HMAC multiple times it's faster to compute the hash once instead of computing it during + * every call + * + * @access private + */ + function _computeKey() + { + if ($this->key === false) { + $this->computedKey = false; + return; + } + + if (strlen($this->key) <= $this->b) { + $this->computedKey = $this->key; + return; + } + + switch ($this->engine) { + case self::MODE_MHASH: + $this->computedKey = mhash($this->hash, $this->key); + break; + case self::MODE_HASH: + $this->computedKey = hash($this->hash, $this->key, true); + break; + case self::MODE_INTERNAL: + $this->computedKey = call_user_func($this->hash, $this->key); + } } /** @@ -216,19 +271,38 @@ class Hash } switch ($hash) { + case 'md2-96': case 'md2': - $mode = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ? + $this->b = 16; + case 'md5-96': + case 'sha1-96': + case 'sha224-96': + case 'sha256-96': + case 'md2': + case 'md5': + case 'sha1': + case 'sha224': + case 'sha256': + $this->b = 64; + break; + default: + $this->b = 128; + } + + switch ($hash) { + case 'md2': + $this->engine = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ? self::MODE_HASH : self::MODE_INTERNAL; break; case 'sha384': case 'sha512': - $mode = CRYPT_HASH_MODE == self::MODE_MHASH ? self::MODE_INTERNAL : CRYPT_HASH_MODE; + $this->engine = CRYPT_HASH_MODE == self::MODE_MHASH ? self::MODE_INTERNAL : CRYPT_HASH_MODE; break; default: - $mode = CRYPT_HASH_MODE; + $this->engine = CRYPT_HASH_MODE; } - switch ($mode) { + switch ($this->engine) { case self::MODE_MHASH: switch ($hash) { case 'md5': @@ -241,6 +315,7 @@ class Hash default: $this->hash = MHASH_SHA1; } + $this->_computeKey(self::MODE_MHASH); return; case self::MODE_HASH: switch ($hash) { @@ -257,35 +332,33 @@ class Hash default: $this->hash = 'sha1'; } + $this->_computeKey(self::MODE_HASH); return; } switch ($hash) { case 'md2': - $this->b = 16; $this->hash = array($this, '_md2'); break; case 'md5': - $this->b = 64; $this->hash = array($this, '_md5'); break; case 'sha256': - $this->b = 64; $this->hash = array($this, '_sha256'); break; case 'sha384': case 'sha512': - $this->b = 128; $this->hash = array($this, '_sha512'); break; case 'sha1': default: - $this->b = 64; $this->hash = array($this, '_sha1'); } $this->ipad = str_repeat(chr(0x36), $this->b); $this->opad = str_repeat(chr(0x5C), $this->b); + + $this->_computeKey(self::MODE_INTERNAL); } /** @@ -297,33 +370,25 @@ class Hash */ function hash($text) { - $mode = is_array($this->hash) ? self::MODE_INTERNAL : CRYPT_HASH_MODE; - if (!empty($this->key) || is_string($this->key)) { - switch ($mode) { + switch ($this->engine) { case self::MODE_MHASH: - $output = mhash($this->hash, $text, $this->key); + $output = mhash($this->hash, $text, $this->computedKey); break; case self::MODE_HASH: - $output = hash_hmac($this->hash, $text, $this->key, true); + $output = hash_hmac($this->hash, $text, $this->computedKey, true); break; case self::MODE_INTERNAL: - /* "Applications that use keys longer than B bytes will first hash the key using H and then use the - resultant L byte string as the actual key to HMAC." - - -- http://tools.ietf.org/html/rfc2104#section-2 */ - $key = strlen($this->key) > $this->b ? call_user_func($this->hash, $this->key) : $this->key; - - $key = str_pad($key, $this->b, chr(0)); // step 1 - $temp = $this->ipad ^ $key; // step 2 - $temp .= $text; // step 3 - $temp = call_user_func($this->hash, $temp); // step 4 - $output = $this->opad ^ $key; // step 5 - $output.= $temp; // step 6 - $output = call_user_func($this->hash, $output); // step 7 + $key = str_pad($this->computedKey, $this->b, chr(0)); // step 1 + $temp = $this->ipad ^ $key; // step 2 + $temp .= $text; // step 3 + $temp = call_user_func($this->hash, $temp); // step 4 + $output = $this->opad ^ $key; // step 5 + $output.= $temp; // step 6 + $output = call_user_func($this->hash, $output); // step 7 } } else { - switch ($mode) { + switch ($this->engine) { case self::MODE_MHASH: $output = mhash($this->hash, $text); break; @@ -784,7 +849,6 @@ class Hash * _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the * possibility of overflow exists, care has to be taken. BigInteger could be used but this should be faster. * - * @param int $... * @return int * @see self::_sha256() * @access private @@ -802,7 +866,12 @@ class Hash $result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument; } - return fmod($result, $mod); + if ((php_uname('m') & "\xDF\xDF\xDF") != 'ARM') { + return fmod($result, $mod); + } + + return (fmod($result, 0x80000000) & 0x7FFFFFFF) | + ((fmod(floor($result / 0x80000000), 2) & 1) << 31); } /** diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php index e9cfa3f8..b2b9d48e 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php @@ -296,7 +296,7 @@ class RC2 extends Base function setKeyLength($length) { if ($length < 8) { - $this->default_key_length = 8; + $this->default_key_length = 1; } elseif ($length > 1024) { $this->default_key_length = 128; } else { diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php index 1e768d7d..25e4ff85 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php @@ -107,7 +107,7 @@ class RC4 extends Base * @var string * @access private */ - var $key = "\0"; + var $key; /** * The Key Stream for decryption and encryption @@ -144,8 +144,10 @@ class RC4 extends Base */ function isValidEngine($engine) { - switch ($engine) { - case Base::ENGINE_OPENSSL: + if ($engine == Base::ENGINE_OPENSSL) { + if (version_compare(PHP_VERSION, '5.3.7') >= 0) { + $this->cipher_name_openssl = 'rc4-40'; + } else { switch (strlen($this->key)) { case 5: $this->cipher_name_openssl = 'rc4-40'; @@ -159,6 +161,7 @@ class RC4 extends Base default: return false; } + } } return parent::isValidEngine($engine); diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php index ef508a43..22387be3 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php @@ -182,6 +182,10 @@ class RSA * PKCS#8 formatted private key */ const PRIVATE_FORMAT_PKCS8 = 8; + /** + * OpenSSH formatted private key + */ + const PRIVATE_FORMAT_OPENSSH = 9; /**#@-*/ /**#@+ @@ -468,23 +472,27 @@ class RSA break; case extension_loaded('openssl') && file_exists($this->configFile): // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work - ob_start(); - @phpinfo(); - $content = ob_get_contents(); - ob_end_clean(); - - preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); - $versions = array(); - if (!empty($matches[1])) { - for ($i = 0; $i < count($matches[1]); $i++) { - $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); - // Remove letter part in OpenSSL version - if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) { - $versions[$matches[1][$i]] = $fullVersion; - } else { - $versions[$matches[1][$i]] = $m[0]; + // avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems) + if (strpos(ini_get('disable_functions'), 'phpinfo') === false) { + ob_start(); + @phpinfo(); + $content = ob_get_contents(); + ob_end_clean(); + + preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); + + if (!empty($matches[1])) { + for ($i = 0; $i < count($matches[1]); $i++) { + $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); + + // Remove letter part in OpenSSL version + if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) { + $versions[$matches[1][$i]] = $fullVersion; + } else { + $versions[$matches[1][$i]] = $m[0]; + } } } } @@ -529,7 +537,7 @@ class RSA * @access public * @param int $bits * @param int $timeout - * @param array $p + * @param array $partial */ function createKey($bits = 1024, $timeout = false, $partial = array()) { @@ -708,7 +716,12 @@ class RSA * * @access private * @see self::setPrivateKeyFormat() - * @param string $RSAPrivateKey + * @param Math_BigInteger $n + * @param Math_BigInteger $e + * @param Math_BigInteger $d + * @param array $primes + * @param array $exponents + * @param array $coefficients * @return string */ function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients) @@ -816,6 +829,58 @@ class RSA $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n"; return $key; + case self::PRIVATE_FORMAT_OPENSSH: + if ($num_primes != 2) { + return false; + } + $publicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']); + $privateKey = pack( + 'Na*Na*Na*Na*Na*Na*Na*', + strlen('ssh-rsa'), + 'ssh-rsa', + strlen($raw['modulus']), + $raw['modulus'], + strlen($raw['publicExponent']), + $raw['publicExponent'], + strlen($raw['privateExponent']), + $raw['privateExponent'], + strlen($raw['coefficient']), + $raw['coefficient'], + strlen($raw['prime1']), + $raw['prime1'], + strlen($raw['prime2']), + $raw['prime2'] + ); + $checkint = Random::string(4); + $paddedKey = pack( + 'a*Na*', + $checkint . $checkint . $privateKey, + strlen($this->comment), + $this->comment + ); + $paddingLength = (7 * strlen($paddedKey)) % 8; + for ($i = 1; $i <= $paddingLength; $i++) { + $paddedKey.= chr($i); + } + $key = pack( + 'Na*Na*Na*NNa*Na*', + strlen('none'), + 'none', + strlen('none'), + 'none', + 0, + '', + 1, + strlen($publicKey), + $publicKey, + strlen($paddedKey), + $paddedKey + ); + $key = "openssh-key-v1\0$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) { @@ -937,8 +1002,9 @@ class RSA * * @access private * @see self::setPublicKeyFormat() - * @param string $RSAPrivateKey - * @return string + * @param Math_BigInteger $n + * @param Math_BigInteger $e + * @return string|array */ function _convertPublicKey($n, $e) { @@ -1016,9 +1082,9 @@ class RSA * @access private * @see self::_convertPublicKey() * @see self::_convertPrivateKey() - * @param string $key + * @param string|array $key * @param int $type - * @return array + * @return array|bool */ function _parseKey($key, $type) { @@ -1153,6 +1219,7 @@ class RSA $length = $this->_decodeLength($temp); switch ($this->_string_shift($temp, $length)) { case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption + case "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0A": // rsaPSS break; case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC /* @@ -1329,9 +1396,14 @@ class RSA xml_set_character_data_handler($xml, '_data_handler'); // add to account for "dangling" tags like ... that are sometimes added if (!xml_parse($xml, '' . $key . '')) { + xml_parser_free($xml); + unset($xml); return false; } + xml_parser_free($xml); + unset($xml); + return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false; // from PuTTY's SSHPUBK.C case self::PRIVATE_FORMAT_PUTTY: @@ -1403,8 +1475,79 @@ class RSA } $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256)); + return $components; + case self::PRIVATE_FORMAT_OPENSSH: + $components = array(); + $decoded = $this->_extractBER($key); + $magic = $this->_string_shift($decoded, 15); + 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") { + return false; + } + extract(unpack('Nlength', $this->_string_shift($decoded, 4))); + if (strlen($decoded) < $length) { + return false; + } + $publicKey = $this->_string_shift($decoded, $length); + extract(unpack('Nlength', $this->_string_shift($decoded, 4))); + 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; + } + + $checkint1 = $this->_string_shift($paddedKey, 4); + $checkint2 = $this->_string_shift($paddedKey, 4); + if (strlen($checkint1) != 4 || $checkint1 !== $checkint2) { + return false; + } + + if ($this->_string_shift($paddedKey, 11) !== "\0\0\0\7ssh-rsa") { + return false; + } + + $values = array( + &$components['modulus'], + &$components['publicExponent'], + &$components['privateExponent'], + &$components['coefficients'][2], + &$components['primes'][1], + &$components['primes'][2] + ); + + foreach ($values as &$value) { + extract(unpack('Nlength', $this->_string_shift($paddedKey, 4))); + if (strlen($paddedKey) < $length) { + return false; + } + $value = new BigInteger($this->_string_shift($paddedKey, $length), -256); + } + + extract(unpack('Nlength', $this->_string_shift($paddedKey, 4))); + if (strlen($paddedKey) < $length) { + return false; + } + $components['comment'] = $this->_string_shift($decoded, $length); + + $temp = $components['primes'][1]->subtract($this->one); + $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp)); + $temp = $components['primes'][2]->subtract($this->one); + $components['exponents'][] = $components['publicExponent']->modInverse($temp); + return $components; } + + return false; } /** @@ -1501,8 +1644,9 @@ class RSA * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed) * * @access public - * @param string $key - * @param int $type optional + * @param string|RSA|array $key + * @param bool|int $type optional + * @return bool */ function loadKey($key, $type = false) { @@ -1559,7 +1703,8 @@ class RSA self::PRIVATE_FORMAT_PKCS1, self::PRIVATE_FORMAT_XML, self::PRIVATE_FORMAT_PUTTY, - self::PUBLIC_FORMAT_OPENSSH + self::PUBLIC_FORMAT_OPENSSH, + self::PRIVATE_FORMAT_OPENSSH ); foreach ($types as $type) { $components = $this->_parseKey($key, $type); @@ -1572,6 +1717,15 @@ class RSA } if ($components === false) { + $this->comment = null; + $this->modulus = null; + $this->k = null; + $this->exponent = null; + $this->primes = null; + $this->exponents = null; + $this->coefficients = null; + $this->publicExponent = null; + return false; } @@ -1733,7 +1887,6 @@ class RSA * * @see self::getPublicKey() * @access public - * @param string $key * @param int $type optional */ function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8) @@ -1791,7 +1944,6 @@ class RSA * * @see self::getPublicKey() * @access public - * @param string $key * @param int $type optional * @return mixed */ @@ -1816,8 +1968,7 @@ class RSA * * @see self::getPrivateKey() * @access private - * @param string $key - * @param int $type optional + * @param int $mode optional */ function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8) { @@ -2034,7 +2185,7 @@ class RSA * of the hash function Hash) and 0. * * @access public - * @param int $format + * @param int $sLen */ function setSaltLength($sLen) { @@ -2067,7 +2218,7 @@ class RSA * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}. * * @access private - * @param string $x + * @param int|string|resource $x * @return \phpseclib\Math\BigInteger */ function _os2ip($x) @@ -2198,16 +2349,21 @@ class RSA */ function _equals($x, $y) { + if (function_exists('hash_equals')) { + return hash_equals($x, $y); + } + if (strlen($x) != strlen($y)) { return false; } - $result = 0; + $result = "\0"; + $x^= $y; for ($i = 0; $i < strlen($x); $i++) { - $result |= ord($x[$i]) ^ ord($y[$i]); + $result|= $x[$i]; } - return $result == 0; + return $result === "\0"; } /** @@ -2289,7 +2445,7 @@ class RSA * * @access private * @param string $mgfSeed - * @param int $mgfLen + * @param int $maskLen * @return string */ function _mgf1($mgfSeed, $maskLen) @@ -2414,19 +2570,26 @@ class RSA $db = $maskedDB ^ $dbMask; $lHash2 = substr($db, 0, $this->hLen); $m = substr($db, $this->hLen); - if ($lHash != $lHash2) { - user_error('Decryption error'); - return false; + $hashesMatch = $this->_equals($lHash, $lHash2); + $leadingZeros = 1; + $patternMatch = 0; + $offset = 0; + for ($i = 0; $i < strlen($m); $i++) { + $patternMatch|= $leadingZeros & ($m[$i] === "\1"); + $leadingZeros&= $m[$i] === "\0"; + $offset+= $patternMatch ? 0 : 1; } - $m = ltrim($m, chr(0)); - if (ord($m[0]) != 1) { + + // we do | instead of || to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation + // to protect against timing attacks + if (!$hashesMatch | !$patternMatch) { user_error('Decryption error'); return false; } // Output the message M - return substr($m, 1); + return substr($m, $offset + 1); } /** @@ -2605,7 +2768,7 @@ class RSA // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error // be output. - $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8); + $emLen = ($emBits + 7) >> 3; // ie. ceil($emBits / 8); $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; $mHash = $this->hash->hash($m); @@ -2683,7 +2846,7 @@ class RSA // RSA verification - $modBits = 8 * $this->k; + $modBits = strlen($this->modulus->toBits()); $s2 = $this->_os2ip($s); $m2 = $this->_rsavp1($s2); @@ -2691,7 +2854,7 @@ class RSA user_error('Invalid signature'); return false; } - $em = $this->_i2osp($m2, $modBits >> 3); + $em = $this->_i2osp($m2, $this->k); if ($em === false) { user_error('Invalid signature'); return false; @@ -2754,6 +2917,59 @@ class RSA return $em; } + /** + * EMSA-PKCS1-V1_5-ENCODE (without NULL) + * + * Quoting https://tools.ietf.org/html/rfc8017#page-65, + * + * "The parameters field associated with id-sha1, id-sha224, id-sha256, + * id-sha384, id-sha512, id-sha512/224, and id-sha512/256 should + * generally be omitted, but if present, it shall have a value of type + * NULL" + * + * @access private + * @param string $m + * @param int $emLen + * @return string + */ + function _emsa_pkcs1_v1_5_encode_without_null($m, $emLen) + { + $h = $this->hash->hash($m); + if ($h === false) { + return false; + } + + switch ($this->hashName) { + case 'sha1': + $t = pack('H*', '301f300706052b0e03021a0414'); + break; + case 'sha256': + $t = pack('H*', '302f300b06096086480165030402010420'); + break; + case 'sha384': + $t = pack('H*', '303f300b06096086480165030402020430'); + break; + case 'sha512': + $t = pack('H*', '304f300b06096086480165030402030440'); + break; + default: + return false; + } + $t.= $h; + $tLen = strlen($t); + + if ($emLen < $tLen + 11) { + user_error('Intended encoded message length too short'); + return false; + } + + $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); + + $em = "\0\1$ps\0$t"; + + return $em; + } + /** * RSASSA-PKCS1-V1_5-SIGN * @@ -2791,6 +3007,7 @@ class RSA * * @access private * @param string $m + * @param string $s * @return string */ function _rsassa_pkcs1_v1_5_verify($m, $s) @@ -2819,13 +3036,17 @@ class RSA // EMSA-PKCS1-v1_5 encoding $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); - if ($em2 === false) { + $em3 = $this->_emsa_pkcs1_v1_5_encode_without_null($m, $this->k); + + if ($em2 === false && $em3 === false) { user_error('RSA modulus too short'); return false; } // Compare - return $this->_equals($em, $em2); + + return ($em2 !== false && $this->_equals($em, $em2)) || + ($em3 !== false && $this->_equals($em, $em3)); } /** @@ -2931,7 +3152,7 @@ class RSA * * @see self::encrypt() * @access public - * @param string $plaintext + * @param string $ciphertext * @return string */ function decrypt($ciphertext) diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php index 170d1c37..8f53eb31 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php @@ -45,6 +45,10 @@ class Random */ static function string($length) { + if (!$length) { + return ''; + } + if (version_compare(PHP_VERSION, '7.0.0', '>=')) { try { return \random_bytes($length); @@ -62,7 +66,7 @@ class Random // method 1. prior to PHP 5.3 this would call rand() on windows hence the function_exists('class_alias') call. // ie. class_alias is a function that was introduced in PHP 5.3 if (extension_loaded('mcrypt') && function_exists('class_alias')) { - return mcrypt_create_iv($length); + return @mcrypt_create_iv($length); } // method 2. openssl_random_pseudo_bytes was introduced in PHP 5.3.0 but prior to PHP 5.3.4 there was, // to quote , "possible blocking behavior". as of 5.3.4 @@ -93,7 +97,10 @@ class Random $fp = @fopen('/dev/urandom', 'rb'); } if ($fp !== true && $fp !== false) { // surprisingly faster than !is_bool() or is_resource() - return fread($fp, $length); + $temp = fread($fp, $length); + if (strlen($temp) == $length) { + return $temp; + } } // method 3. pretty much does the same thing as method 2 per the following url: // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391 @@ -101,7 +108,7 @@ class Random // not doing. regardless, this'll only be called if this PHP script couldn't open /dev/urandom due to open_basedir // restrictions or some such if (extension_loaded('mcrypt')) { - return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); + return @mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); } } // at this point we have no choice but to use a pure-PHP CSPRNG diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php index 3dd4ea1d..70980a2f 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php @@ -432,8 +432,10 @@ class Twofish extends Base $m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^ $m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]]; $B = ($B << 8) | ($B >> 24 & 0xff); - $K[] = $A+= $B; - $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); + $A = $this->safe_intval($A + $B); + $K[] = $A; + $A = $this->safe_intval($A + $B); + $K[] = ($A << 9 | $A >> 23 & 0x1ff); } for ($i = 0; $i < 256; ++$i) { $S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0]; @@ -456,8 +458,10 @@ class Twofish extends Base $m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ $m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]]; $B = ($B << 8) | ($B >> 24 & 0xff); - $K[] = $A+= $B; - $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); + $A = $this->safe_intval($A + $B); + $K[] = $A; + $A = $this->safe_intval($A + $B); + $K[] = ($A << 9 | $A >> 23 & 0x1ff); } for ($i = 0; $i < 256; ++$i) { $S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0]; @@ -481,8 +485,10 @@ class Twofish extends Base $m2[$q1[$q0[$q0[$q0[$j] ^ $key[31]] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ $m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^ $key[16]] ^ $key[8]]; $B = ($B << 8) | ($B >> 24 & 0xff); - $K[] = $A+= $B; - $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); + $A = $this->safe_intval($A + $B); + $K[] = $A; + $A = $this->safe_intval($A + $B); + $K[] = ($A << 9 | $A >> 23 & 0x1ff); } for ($i = 0; $i < 256; ++$i) { $S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4] ^ $s0]; @@ -578,9 +584,9 @@ class Twofish extends Base $S1[ $R1 & 0xff] ^ $S2[($R1 >> 8) & 0xff] ^ $S3[($R1 >> 16) & 0xff]; - $R2^= $t0 + $t1 + $K[++$ki]; + $R2^= $this->safe_intval($t0 + $t1 + $K[++$ki]); $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); - $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ($t0 + ($t1 << 1) + $K[++$ki]); + $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ $this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]); $t0 = $S0[ $R2 & 0xff] ^ $S1[($R2 >> 8) & 0xff] ^ @@ -590,9 +596,9 @@ class Twofish extends Base $S1[ $R3 & 0xff] ^ $S2[($R3 >> 8) & 0xff] ^ $S3[($R3 >> 16) & 0xff]; - $R0^= ($t0 + $t1 + $K[++$ki]); + $R0^= $this->safe_intval($t0 + $t1 + $K[++$ki]); $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); - $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ($t0 + ($t1 << 1) + $K[++$ki]); + $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ $this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]); } // @codingStandardsIgnoreStart @@ -634,9 +640,9 @@ class Twofish extends Base $S1[$R1 & 0xff] ^ $S2[$R1 >> 8 & 0xff] ^ $S3[$R1 >> 16 & 0xff]; - $R3^= $t0 + ($t1 << 1) + $K[--$ki]; + $R3^= $this->safe_intval($t0 + ($t1 << 1) + $K[--$ki]); $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; - $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ($t0 + $t1 + $K[--$ki]); + $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ $this->safe_intval($t0 + $t1 + $K[--$ki]); $t0 = $S0[$R2 & 0xff] ^ $S1[$R2 >> 8 & 0xff] ^ @@ -646,9 +652,9 @@ class Twofish extends Base $S1[$R3 & 0xff] ^ $S2[$R3 >> 8 & 0xff] ^ $S3[$R3 >> 16 & 0xff]; - $R1^= $t0 + ($t1 << 1) + $K[--$ki]; + $R1^= $this->safe_intval($t0 + ($t1 << 1) + $K[--$ki]); $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; - $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ($t0 + $t1 + $K[--$ki]); + $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ $this->safe_intval($t0 + $t1 + $K[--$ki]); } // @codingStandardsIgnoreStart @@ -679,6 +685,8 @@ class Twofish extends Base $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); } + $safeint = $this->safe_intval_inline(); + if (!isset($lambda_functions[$code_hash])) { switch (true) { case $gen_hi_opt_code: @@ -727,9 +735,9 @@ class Twofish extends Base $S1[ $R1 & 0xff] ^ $S2[($R1 >> 8) & 0xff] ^ $S3[($R1 >> 16) & 0xff]; - $R2^= ($t0 + $t1 + '.$K[++$ki].'); + $R2^= ' . sprintf($safeint, '$t0 + $t1 + ' . $K[++$ki]) . '; $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); - $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ($t0 + ($t1 << 1) + '.$K[++$ki].'); + $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . '; $t0 = $S0[ $R2 & 0xff] ^ $S1[($R2 >> 8) & 0xff] ^ @@ -739,16 +747,16 @@ class Twofish extends Base $S1[ $R3 & 0xff] ^ $S2[($R3 >> 8) & 0xff] ^ $S3[($R3 >> 16) & 0xff]; - $R0^= ($t0 + $t1 + '.$K[++$ki].'); + $R0^= ' . sprintf($safeint, '($t0 + $t1 + ' . $K[++$ki] . ')') . '; $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); - $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ($t0 + ($t1 << 1) + '.$K[++$ki].'); + $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . '; '; } $encrypt_block.= ' - $in = pack("V4", '.$K[4].' ^ $R2, - '.$K[5].' ^ $R3, - '.$K[6].' ^ $R0, - '.$K[7].' ^ $R1); + $in = pack("V4", ' . $K[4] . ' ^ $R2, + ' . $K[5] . ' ^ $R3, + ' . $K[6] . ' ^ $R0, + ' . $K[7] . ' ^ $R1); '; // Generating decrypt code: @@ -769,9 +777,9 @@ class Twofish extends Base $S1[$R1 & 0xff] ^ $S2[$R1 >> 8 & 0xff] ^ $S3[$R1 >> 16 & 0xff]; - $R3^= $t0 + ($t1 << 1) + '.$K[--$ki].'; + $R3^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . '; $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; - $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ($t0 + $t1 + '.$K[--$ki].'); + $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] . ')') . '; $t0 = $S0[$R2 & 0xff] ^ $S1[$R2 >> 8 & 0xff] ^ @@ -781,16 +789,16 @@ class Twofish extends Base $S1[$R3 & 0xff] ^ $S2[$R3 >> 8 & 0xff] ^ $S3[$R3 >> 16 & 0xff]; - $R1^= $t0 + ($t1 << 1) + '.$K[--$ki].'; + $R1^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . '; $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; - $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ($t0 + $t1 + '.$K[--$ki].'); + $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] . ')') . '; '; } $decrypt_block.= ' - $in = pack("V4", '.$K[0].' ^ $R2, - '.$K[1].' ^ $R3, - '.$K[2].' ^ $R0, - '.$K[3].' ^ $R1); + $in = pack("V4", ' . $K[0] . ' ^ $R2, + ' . $K[1] . ' ^ $R3, + ' . $K[2] . ' ^ $R0, + ' . $K[3] . ' ^ $R1); '; $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php index 1f3eecb3..b6874d35 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php @@ -203,8 +203,7 @@ class ANSI /** * Set the number of lines that should be logged past the terminal height * - * @param int $x - * @param int $y + * @param int $history * @access public */ function setHistory($history) @@ -272,7 +271,7 @@ class ANSI case "\x1B[K": // Clear screen from cursor right $this->screen[$this->y] = substr($this->screen[$this->y], 0, $this->x); - array_splice($this->attrs[$this->y], $this->x + 1, $this->max_x - $this->x, array_fill($this->x, $this->max_x - $this->x - 1, $this->base_attr_cell)); + array_splice($this->attrs[$this->y], $this->x + 1, $this->max_x - $this->x, array_fill($this->x, $this->max_x - ($this->x - 1), $this->base_attr_cell)); break; case "\x1B[2K": // Clear entire line $this->screen[$this->y] = str_repeat(' ', $this->x); @@ -305,6 +304,9 @@ class ANSI case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): // Move cursor left n lines $this->old_x = $this->x; $this->x-= $match[1]; + if ($this->x < 0) { + $this->x = 0; + } break; case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window break; @@ -313,19 +315,20 @@ class ANSI $mods = explode(';', $match[1]); foreach ($mods as $mod) { switch ($mod) { - case 0: // Turn off character attributes + case '': + case '0': // Turn off character attributes $attr_cell = clone $this->base_attr_cell; break; - case 1: // Turn bold mode on + case '1': // Turn bold mode on $attr_cell->bold = true; break; - case 4: // Turn underline mode on + case '4': // Turn underline mode on $attr_cell->underline = true; break; - case 5: // Turn blinking mode on + case '5': // Turn blinking mode on $attr_cell->blink = true; break; - case 7: // Turn reverse video on + case '7': // Turn reverse video on $attr_cell->reverse = !$attr_cell->reverse; $temp = $attr_cell->background; $attr_cell->background = $attr_cell->foreground; @@ -338,23 +341,23 @@ class ANSI $back = &$attr_cell->{ $attr_cell->reverse ? 'foreground' : 'background' }; switch ($mod) { // @codingStandardsIgnoreStart - case 30: $front = 'black'; break; - case 31: $front = 'red'; break; - case 32: $front = 'green'; break; - case 33: $front = 'yellow'; break; - case 34: $front = 'blue'; break; - case 35: $front = 'magenta'; break; - case 36: $front = 'cyan'; break; - case 37: $front = 'white'; break; + case '30': $front = 'black'; break; + case '31': $front = 'red'; break; + case '32': $front = 'green'; break; + case '33': $front = 'yellow'; break; + case '34': $front = 'blue'; break; + case '35': $front = 'magenta'; break; + case '36': $front = 'cyan'; break; + case '37': $front = 'white'; break; - case 40: $back = 'black'; break; - case 41: $back = 'red'; break; - case 42: $back = 'green'; break; - case 43: $back = 'yellow'; break; - case 44: $back = 'blue'; break; - case 45: $back = 'magenta'; break; - case 46: $back = 'cyan'; break; - case 47: $back = 'white'; break; + case '40': $back = 'black'; break; + case '41': $back = 'red'; break; + case '42': $back = 'green'; break; + case '43': $back = 'yellow'; break; + case '44': $back = 'blue'; break; + case '45': $back = 'magenta'; break; + case '46': $back = 'cyan'; break; + case '47': $back = 'white'; break; // @codingStandardsIgnoreEnd default: @@ -416,7 +419,7 @@ class ANSI if ($this->x > $this->max_x) { $this->x = 0; - $this->y++; + $this->_newLine(); } else { $this->x++; } diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php index 8308645b..fb2d6448 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php @@ -25,6 +25,8 @@ namespace phpseclib\File; use phpseclib\File\ASN1\Element; use phpseclib\Math\BigInteger; +use DateTime; +use DateTimeZone; /** * Pure-PHP ASN.1 Parser @@ -232,8 +234,11 @@ class ASN1 { $current = array('start' => $start); + if (!isset($encoded[$encoded_pos])) { + return false; + } $type = ord($encoded[$encoded_pos++]); - $start++; + $startOffset = 1; $constructed = ($type >> 5) & 1; @@ -242,14 +247,28 @@ class ASN1 $tag = 0; // process septets (since the eighth bit is ignored, it's not an octet) do { - $loop = ord($encoded[0]) >> 7; + if (!isset($encoded[$encoded_pos])) { + return false; + } + $temp = ord($encoded[$encoded_pos++]); + $startOffset++; + $loop = $temp >> 7; $tag <<= 7; - $tag |= ord($encoded[$encoded_pos++]) & 0x7F; - $start++; + $temp &= 0x7F; + // "bits 7 to 1 of the first subsequent octet shall not all be zero" + if ($startOffset == 2 && $temp == 0) { + return false; + } + $tag |= $temp; } while ($loop); } + $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 @@ -306,6 +325,9 @@ class ASN1 $remainingLength = $length; while ($remainingLength > 0) { $temp = $this->_decode_ber($content, $start, $content_pos); + if ($temp === false) { + break; + } $length = $temp['length']; // end-of-content octets - see paragraph 8.1.5 if (substr($content, $content_pos + $length, 2) == "\0\0") { @@ -338,13 +360,16 @@ class ASN1 switch ($tag) { case self::TYPE_BOOLEAN: // "The contents octets shall consist of a single octet." -- paragraph 8.2.1 - //if (strlen($content) != 1) { - // return false; - //} + if ($constructed || strlen($content) != 1) { + return false; + } $current['content'] = (bool) ord($content[$content_pos]); break; case self::TYPE_INTEGER: case self::TYPE_ENUMERATED: + if ($constructed) { + return false; + } $current['content'] = new BigInteger(substr($content, $content_pos), -256); break; case self::TYPE_REAL: // not currently supported @@ -357,19 +382,22 @@ class ASN1 $current['content'] = substr($content, $content_pos); } else { $temp = $this->_decode_ber($content, $start, $content_pos); + if ($temp === false) { + return false; + } $length-= (strlen($content) - $content_pos); $last = count($temp) - 1; for ($i = 0; $i < $last; $i++) { // all subtags should be bit strings - //if ($temp[$i]['type'] != self::TYPE_BIT_STRING) { - // return false; - //} + if ($temp[$i]['type'] != self::TYPE_BIT_STRING) { + return false; + } $current['content'].= substr($temp[$i]['content'], 1); } // all subtags should be bit strings - //if ($temp[$last]['type'] != self::TYPE_BIT_STRING) { - // return false; - //} + if ($temp[$last]['type'] != self::TYPE_BIT_STRING) { + return false; + } $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1); } break; @@ -381,11 +409,14 @@ class ASN1 $length = 0; while (substr($content, $content_pos, 2) != "\0\0") { $temp = $this->_decode_ber($content, $length + $start, $content_pos); + if ($temp === false) { + return false; + } $content_pos += $temp['length']; // all subtags should be octet strings - //if ($temp['type'] != self::TYPE_OCTET_STRING) { - // return false; - //} + if ($temp['type'] != self::TYPE_OCTET_STRING) { + return false; + } $current['content'].= $temp['content']; $length+= $temp['length']; } @@ -396,12 +427,15 @@ class ASN1 break; case self::TYPE_NULL: // "The contents octets shall not contain any octets." -- paragraph 8.8.2 - //if (strlen($content)) { - // return false; - //} + if ($constructed || strlen($content)) { + return false; + } break; case self::TYPE_SEQUENCE: case self::TYPE_SET: + if (!$constructed) { + return false; + } $offset = 0; $current['content'] = array(); $content_len = strlen($content); @@ -413,30 +447,22 @@ class ASN1 break 2; } $temp = $this->_decode_ber($content, $start + $offset, $content_pos); + if ($temp === false) { + return false; + } $content_pos += $temp['length']; $current['content'][] = $temp; $offset+= $temp['length']; } break; case self::TYPE_OBJECT_IDENTIFIER: - $temp = ord($content[$content_pos++]); - $current['content'] = sprintf('%d.%d', floor($temp / 40), $temp % 40); - $valuen = 0; - // process septets - $content_len = strlen($content); - while ($content_pos < $content_len) { - $temp = ord($content[$content_pos++]); - $valuen <<= 7; - $valuen |= $temp & 0x7F; - if (~$temp & 0x80) { - $current['content'].= ".$valuen"; - $valuen = 0; - } + if ($constructed) { + return false; + } + $current['content'] = $this->_decodeOID(substr($content, $content_pos)); + if ($current['content'] === false) { + return false; } - // the eighth bit of the last byte should not be 1 - //if ($temp >> 7) { - // return false; - //} break; /* Each character string type shall be encoded as if it had been declared: [UNIVERSAL x] IMPLICIT OCTET STRING @@ -466,12 +492,20 @@ class ASN1 case self::TYPE_UTF8_STRING: // ???? case self::TYPE_BMP_STRING: + if ($constructed) { + return false; + } $current['content'] = substr($content, $content_pos); break; case self::TYPE_UTC_TIME: case self::TYPE_GENERALIZED_TIME: + if ($constructed) { + return false; + } $current['content'] = $this->_decodeTime(substr($content, $content_pos), $tag); + break; default: + return false; } $start+= $length; @@ -495,6 +529,10 @@ class ASN1 */ function asn1map($decoded, $mapping, $special = array()) { + if (!is_array($decoded)) { + return false; + } + if (isset($mapping['explicit']) && is_array($decoded['content'])) { $decoded = $decoded['content'][0]; } @@ -580,7 +618,7 @@ class ASN1 $childClass = $tempClass = self::CLASS_UNIVERSAL; $constant = null; if (isset($temp['constant'])) { - $tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC; + $tempClass = $temp['type']; } if (isset($child['class'])) { $childClass = $child['class']; @@ -643,7 +681,7 @@ class ASN1 $temp = $decoded['content'][$i]; $tempClass = self::CLASS_UNIVERSAL; if (isset($temp['constant'])) { - $tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC; + $tempClass = $temp['type']; } foreach ($mapping['children'] as $key => $child) { @@ -704,10 +742,17 @@ class ASN1 return isset($this->oids[$decoded['content']]) ? $this->oids[$decoded['content']] : $decoded['content']; case self::TYPE_UTC_TIME: case self::TYPE_GENERALIZED_TIME: - if (isset($mapping['implicit'])) { + // for explicitly tagged optional stuff + if (is_array($decoded['content'])) { + $decoded['content'] = $decoded['content'][0]['content']; + } + // for implicitly tagged optional stuff + // in theory, doing isset($mapping['implicit']) would work but malformed certs do exist + // in the wild that OpenSSL decodes without issue so we'll support them as well + if (!is_object($decoded['content'])) { $decoded['content'] = $this->_decodeTime($decoded['content'], $decoded['type']); } - return @date($this->format, $decoded['content']); + return $decoded['content'] ? $decoded['content']->format($this->format) : false; case self::TYPE_BIT_STRING: if (isset($mapping['mapping'])) { $offset = ord($decoded['content'][0]); @@ -781,7 +826,7 @@ class ASN1 * * @param string $source * @param string $mapping - * @param int $idx + * @param array $special * @return string * @access public */ @@ -797,6 +842,7 @@ class ASN1 * @param string $source * @param string $mapping * @param int $idx + * @param array $special * @return string * @access private */ @@ -845,7 +891,7 @@ class ASN1 if ($mapping['type'] == self::TYPE_SET) { sort($value); } - $value = implode($value, ''); + $value = implode('', $value); break; } @@ -956,7 +1002,11 @@ class ASN1 case self::TYPE_GENERALIZED_TIME: $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y'; $format.= 'mdHis'; - $value = @gmdate($format, strtotime($source)) . 'Z'; + // if $source does _not_ include timezone information within it then assume that the timezone is GMT + $date = new DateTime($source, new DateTimeZone('GMT')); + // if $source _does_ include timezone information within it then convert the time to GMT + $date->setTimezone(new DateTimeZone('GMT')); + $value = $date->format($format) . 'Z'; break; case self::TYPE_BIT_STRING: if (isset($mapping['mapping'])) { @@ -998,27 +1048,7 @@ class ASN1 $value = base64_decode($source); break; case self::TYPE_OBJECT_IDENTIFIER: - $oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids); - if ($oid === false) { - user_error('Invalid OID'); - return false; - } - $value = ''; - $parts = explode('.', $oid); - $value = chr(40 * $parts[0] + $parts[1]); - for ($i = 2; $i < count($parts); $i++) { - $temp = ''; - if (!$parts[$i]) { - $temp = "\0"; - } else { - while ($parts[$i]) { - $temp = chr(0x80 | ($parts[$i] & 0x7F)) . $temp; - $parts[$i] >>= 7; - } - $temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F); - } - $value.= $temp; - } + $value = $this->_encodeOID($source); break; case self::TYPE_ANY: $loc = $this->location; @@ -1117,6 +1147,113 @@ class ASN1 return pack('Ca*', 0x80 | strlen($temp), $temp); } + /** + * BER-decode the OID + * + * Called by _decode_ber() + * + * @access private + * @param string $content + * @return string + */ + function _decodeOID($content) + { + static $eighty; + if (!$eighty) { + $eighty = new BigInteger(80); + } + + $oid = array(); + $pos = 0; + $len = strlen($content); + + if (ord($content[$len - 1]) & 0x80) { + return false; + } + + $n = new BigInteger(); + while ($pos < $len) { + $temp = ord($content[$pos++]); + $n = $n->bitwise_leftShift(7); + $n = $n->bitwise_or(new BigInteger($temp & 0x7F)); + if (~$temp & 0x80) { + $oid[] = $n; + $n = new BigInteger(); + } + } + $part1 = array_shift($oid); + $first = floor(ord($content[0]) / 40); + /* + "This packing of the first two object identifier components recognizes that only three values are allocated from the root + node, and at most 39 subsequent values from nodes reached by X = 0 and X = 1." + + -- https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=22 + */ + if ($first <= 2) { // ie. 0 <= ord($content[0]) < 120 (0x78) + array_unshift($oid, ord($content[0]) % 40); + array_unshift($oid, $first); + } else { + array_unshift($oid, $part1->subtract($eighty)); + array_unshift($oid, 2); + } + + return implode('.', $oid); + } + + /** + * DER-encode the OID + * + * Called by _encode_der() + * + * @access private + * @param string $source + * @return string + */ + function _encodeOID($source) + { + static $mask, $zero, $forty; + if (!$mask) { + $mask = new BigInteger(0x7F); + $zero = new BigInteger(); + $forty = new BigInteger(40); + } + + $oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids); + if ($oid === false) { + user_error('Invalid OID'); + return false; + } + $parts = explode('.', $oid); + $part1 = array_shift($parts); + $part2 = array_shift($parts); + + $first = new BigInteger($part1); + $first = $first->multiply($forty); + $first = $first->add(new BigInteger($part2)); + + array_unshift($parts, $first->toString()); + + $value = ''; + foreach ($parts as $part) { + if (!$part) { + $temp = "\0"; + } else { + $temp = ''; + $part = new BigInteger($part); + while (!$part->equals($zero)) { + $submask = $part->bitwise_and($mask); + $submask->setPrecision(8); + $temp = (chr(0x80) | $submask->toBytes()) . $temp; + $part = $part->bitwise_rightShift(7); + } + $temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F); + } + $value.= $temp; + } + + return $value; + } + /** * BER-decode the time * @@ -1137,33 +1274,32 @@ class ASN1 http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2 http://www.obj-sys.com/asn1tutorial/node14.html */ - $pattern = $tag == self::TYPE_UTC_TIME ? - '#(..)(..)(..)(..)(..)(..)(.*)#' : - '#(....)(..)(..)(..)(..)(..).*([Z+-].*)$#'; - - preg_match($pattern, $content, $matches); - - list(, $year, $month, $day, $hour, $minute, $second, $timezone) = $matches; + $format = 'YmdHis'; if ($tag == self::TYPE_UTC_TIME) { - $year = $year >= 50 ? "19$year" : "20$year"; - } - - if ($timezone == 'Z') { - $mktime = 'gmmktime'; - $timezone = 0; - } elseif (preg_match('#([+-])(\d\d)(\d\d)#', $timezone, $matches)) { - $mktime = 'gmmktime'; - $timezone = 60 * $matches[3] + 3600 * $matches[2]; - if ($matches[1] == '-') { - $timezone = -$timezone; + // https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=28 says "the seconds + // element shall always be present" but none-the-less I've seen X509 certs where it isn't and if the + // browsers parse it phpseclib ought to too + if (preg_match('#^(\d{10})(Z|[+-]\d{4})$#', $content, $matches)) { + $content = $matches[1] . '00' . $matches[2]; } - } else { - $mktime = 'mktime'; - $timezone = 0; + $prefix = substr($content, 0, 2) >= 50 ? '19' : '20'; + $content = $prefix . $content; + } elseif (strpos($content, '.') !== false) { + $format.= '.u'; } - return @$mktime($hour, $minute, $second, $month, $day, $year) + $timezone; + if ($content[strlen($content) - 1] == 'Z') { + $content = substr($content, 0, -1) . '+0000'; + } + + if (strpos($content, '-') !== false || strpos($content, '+') !== false) { + $format.= 'O'; + } + + // error supression isn't necessary as of PHP 7.0: + // http://php.net/manual/en/migration70.other-changes.php + return @DateTime::createFromFormat($format, $content); } /** diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/File/X509.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/File/X509.php index 863d9e99..0da0b83c 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/File/X509.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/File/X509.php @@ -31,6 +31,8 @@ use phpseclib\Crypt\Random; use phpseclib\Crypt\RSA; use phpseclib\File\ASN1\Element; use phpseclib\Math\BigInteger; +use DateTime; +use DateTimeZone; /** * Pure-PHP X.509 Parser @@ -303,6 +305,22 @@ class X509 */ var $challenge; + /** + * Recursion Limit + * + * @var int + * @access private + */ + static $recur_limit = 5; + + /** + * URL fetch flag + * + * @var bool + * @access private + */ + static $disable_url_fetch = false; + /** * Default Constructor. * @@ -945,6 +963,13 @@ class X509 'children' => $AccessDescription ); + $this->SubjectInfoAccessSyntax = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'min' => 1, + 'max' => -1, + 'children' => $AccessDescription + ); + $this->SubjectAltName = $GeneralNames; $this->PrivateKeyUsagePeriod = array( @@ -1583,7 +1608,7 @@ class X509 * Map extension values from octet string to extension-specific internal * format. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -1597,12 +1622,15 @@ 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); if (!is_bool($map)) { - $mapped = $asn1->asn1map($decoded[0], $map, array('iPAddress' => array($this, '_decodeIP'))); + $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; if ($id == 'id-ce-certificatePolicies') { @@ -1633,7 +1661,7 @@ class X509 * Map extension values from extension-specific internal format to * octet string. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -1699,7 +1727,7 @@ class X509 * Map attribute values from ANY type to attribute-specific internal * format. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -1740,7 +1768,7 @@ class X509 * Map attribute values from attribute-specific internal format to * ANY type. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -1783,7 +1811,7 @@ class X509 * Map DN values from ANY type to DN-specific internal * format. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -1813,7 +1841,7 @@ class X509 * Map DN values from DN-specific internal format to * ANY type. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -1871,6 +1899,8 @@ class X509 return $this->ExtKeyUsageSyntax; case 'id-pe-authorityInfoAccess': return $this->AuthorityInfoAccessSyntax; + case 'id-pe-subjectInfoAccess': + return $this->SubjectInfoAccessSyntax; case 'id-ce-subjectAltName': return $this->SubjectAltName; case 'id-ce-subjectDirectoryAttributes': @@ -1907,6 +1937,12 @@ class X509 // "SET Secure Electronic Transaction Specification" // http://www.maithean.com/docs/set_bk3.pdf case '2.23.42.7.0': // id-set-hashedRootKey + // "Certificate Transparency" + // https://tools.ietf.org/html/rfc6962 + case '1.3.6.1.4.1.11129.2.4.2': + // "Qualified Certificate statements" + // https://tools.ietf.org/html/rfc3739#section-3.2.6 + case '1.3.6.1.5.5.7.1.3': return true; // CSR attributes @@ -2027,30 +2063,32 @@ class X509 } if ($names = $this->getExtension('id-ce-subjectAltName')) { - foreach ($names as $key => $value) { - $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value); - switch ($key) { - case 'dNSName': - /* From RFC2818 "HTTP over TLS": + foreach ($names as $name) { + foreach ($name as $key => $value) { + $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value); + switch ($key) { + case 'dNSName': + /* From RFC2818 "HTTP over TLS": - If a subjectAltName extension of type dNSName is present, that MUST - be used as the identity. Otherwise, the (most specific) Common Name - field in the Subject field of the certificate MUST be used. Although - the use of the Common Name is existing practice, it is deprecated and - Certification Authorities are encouraged to use the dNSName instead. */ - if (preg_match('#^' . $value . '$#', $components['host'])) { - return true; - } - break; - case 'iPAddress': - /* From RFC2818 "HTTP over TLS": + If a subjectAltName extension of type dNSName is present, that MUST + be used as the identity. Otherwise, the (most specific) Common Name + field in the Subject field of the certificate MUST be used. Although + the use of the Common Name is existing practice, it is deprecated and + Certification Authorities are encouraged to use the dNSName instead. */ + if (preg_match('#^' . $value . '$#', $components['host'])) { + return true; + } + break; + case 'iPAddress': + /* From RFC2818 "HTTP over TLS": - In some cases, the URI is specified as an IP address rather than a - hostname. In this case, the iPAddress subjectAltName must be present - in the certificate and must exactly match the IP in the URI. */ - if (preg_match('#(?:\d{1-3}\.){4}#', $components['host'] . '.') && preg_match('#^' . $value . '$#', $components['host'])) { - return true; - } + In some cases, the URI is specified as an IP address rather than a + hostname. In this case, the iPAddress subjectAltName must be present + in the certificate and must exactly match the IP in the URI. */ + if (preg_match('#(?:\d{1-3}\.){4}#', $components['host'] . '.') && preg_match('#^' . $value . '$#', $components['host'])) { + return true; + } + } } } return false; @@ -2069,7 +2107,7 @@ class X509 * * If $date isn't defined it is assumed to be the current date. * - * @param int $date optional + * @param \DateTime|string $date optional * @access public */ function validateDate($date = null) @@ -2079,7 +2117,7 @@ class X509 } if (!isset($date)) { - $date = time(); + $date = new DateTime(null, new DateTimeZone(@date_default_timezone_get())); } $notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore']; @@ -2088,15 +2126,137 @@ class X509 $notAfter = $this->currentCert['tbsCertificate']['validity']['notAfter']; $notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime']; + if (is_string($date)) { + $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get())); + } + + $notBefore = new DateTime($notBefore, new DateTimeZone(@date_default_timezone_get())); + $notAfter = new DateTime($notAfter, new DateTimeZone(@date_default_timezone_get())); + switch (true) { - case $date < @strtotime($notBefore): - case $date > @strtotime($notAfter): + case $date < $notBefore: + case $date > $notAfter: return false; } return true; } + /** + * Fetches a URL + * + * @param string $url + * @access private + * @return bool|string + */ + static function _fetchURL($url) + { + if (self::$disable_url_fetch) { + return false; + } + + $parts = parse_url($url); + $data = ''; + switch ($parts['scheme']) { + case 'http': + $fsock = @fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80); + if (!$fsock) { + return false; + } + fputs($fsock, "GET $parts[path] HTTP/1.0\r\n"); + fputs($fsock, "Host: $parts[host]\r\n\r\n"); + $line = fgets($fsock, 1024); + if (strlen($line) < 3) { + return false; + } + preg_match('#HTTP/1.\d (\d{3})#', $line, $temp); + if ($temp[1] != '200') { + return false; + } + + // skip the rest of the headers in the http response + while (!feof($fsock) && fgets($fsock, 1024) != "\r\n") { + } + + while (!feof($fsock)) { + $temp = fread($fsock, 1024); + if ($temp === false) { + return false; + } + $data.= $temp; + } + + break; + //case 'ftp': + //case 'ldap': + //default: + } + + return $data; + } + + /** + * Validates an intermediate cert as identified via authority info access extension + * + * See https://tools.ietf.org/html/rfc4325 for more info + * + * @param bool $caonly + * @param int $count + * @access private + * @return bool + */ + function _testForIntermediate($caonly, $count) + { + $opts = $this->getExtension('id-pe-authorityInfoAccess'); + if (!is_array($opts)) { + return false; + } + foreach ($opts as $opt) { + if ($opt['accessMethod'] == 'id-ad-caIssuers') { + // accessLocation is a GeneralName. GeneralName fields support stuff like email addresses, IP addresses, LDAP, + // etc, but we're only supporting URI's. URI's and LDAP are the only thing https://tools.ietf.org/html/rfc4325 + // discusses + if (isset($opt['accessLocation']['uniformResourceIdentifier'])) { + $url = $opt['accessLocation']['uniformResourceIdentifier']; + break; + } + } + } + + if (!isset($url)) { + return false; + } + + $cert = static::_fetchURL($url); + if (!is_string($cert)) { + return false; + } + + $parent = new static(); + $parent->CAs = $this->CAs; + /* + "Conforming applications that support HTTP or FTP for accessing + certificates MUST be able to accept .cer files and SHOULD be able + to accept .p7c files." -- https://tools.ietf.org/html/rfc4325 + + A .p7c file is 'a "certs-only" CMS message as specified in RFC 2797" + + These are currently unsupported + */ + if (!is_array($parent->loadX509($cert))) { + return false; + } + + if (!$parent->_validateSignatureCountable($caonly, ++$count)) { + return false; + } + + $this->CAs[] = $parent->currentCert; + //$this->loadCA($cert); + + return true; + } + /** * Validate a signature * @@ -2113,11 +2273,30 @@ class X509 * @return mixed */ function validateSignature($caonly = true) + { + return $this->_validateSignatureCountable($caonly, 0); + } + + /** + * Validate a signature + * + * Performs said validation whilst keeping track of how many times validation method is called + * + * @param bool $caonly + * @param int $count + * @access private + * @return mixed + */ + function _validateSignatureCountable($caonly, $count) { if (!is_array($this->currentCert) || !isset($this->signatureSubject)) { return null; } + if ($count == self::$recur_limit) { + return false; + } + /* TODO: "emailAddress attribute values are not case-sensitive (e.g., "subscriber@example.com" is the same as "SUBSCRIBER@EXAMPLE.COM")." -- http://tools.ietf.org/html/rfc5280#section-4.1.2.6 @@ -2134,7 +2313,8 @@ class X509 $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier'); switch (true) { case !is_array($authorityKey): - case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + case !$subjectKeyID: + case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: $signingCert = $this->currentCert; // working cert } } @@ -2151,17 +2331,21 @@ class X509 $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); switch (true) { case !is_array($authorityKey): - case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + case !$subjectKeyID: + case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) { + break 2; // serial mismatch - check other ca + } $signingCert = $ca; // working cert break 3; } } } if (count($this->CAs) == $i && $caonly) { - return false; + return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly); } } elseif (!isset($signingCert) || $caonly) { - return false; + return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly); } return $this->_validateSignature( $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], @@ -2197,7 +2381,11 @@ class X509 $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); switch (true) { case !is_array($authorityKey): - case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + case !$subjectKeyID: + case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) { + break 2; // serial mismatch - check other ca + } $signingCert = $ca; // working cert break 3; } @@ -2264,6 +2452,41 @@ class X509 return true; } + /** + * Sets the recursion limit + * + * When validating a signature it may be necessary to download intermediate certs from URI's. + * An intermediate cert that linked to itself would result in an infinite loop so to prevent + * that we set a recursion limit. A negative number means that there is no recursion limit. + * + * @param int $count + * @access public + */ + static function setRecurLimit($count) + { + self::$recur_limit = $count; + } + + /** + * Prevents URIs from being automatically retrieved + * + * @access public + */ + static function disableURLFetch() + { + self::$disable_url_fetch = true; + } + + /** + * Allows URIs to be automatically retrieved + * + * @access public + */ + static function enableURLFetch() + { + self::$disable_url_fetch = false; + } + /** * Reformat public keys * @@ -2304,18 +2527,38 @@ class X509 return inet_ntop(base64_decode($ip)); } + /** + * Decodes an IP address in a name constraints extension + * + * Takes in a base64 encoded "blob" and returns a human readable IP address / mask + * + * @param string $ip + * @access private + * @return array + */ + function _decodeNameConstraintIP($ip) + { + $ip = base64_decode($ip); + $size = strlen($ip) >> 1; + $mask = substr($ip, $size); + $ip = substr($ip, 0, $size); + return array(inet_ntop($ip), inet_ntop($mask)); + } + /** * Encodes an IP address * * Takes a human readable IP address into a base64-encoded "blob" * - * @param string $ip + * @param string|array $ip * @access private * @return string */ function _encodeIP($ip) { - return base64_encode(inet_pton($ip)); + return is_string($ip) ? + base64_encode(inet_pton($ip)) : + base64_encode(inet_pton($ip[0]) . inet_pton($ip[1])); } /** @@ -2469,6 +2712,10 @@ class X509 } $dn = array_values($dn); + // fix for https://bugs.php.net/75433 affecting PHP 7.2 + if (!isset($dn[0])) { + $dn = array_splice($dn, 0, 0); + } } /** @@ -2712,12 +2959,14 @@ class X509 $value = array_pop($value); // Always strip data type. } } elseif (is_object($value) && $value instanceof Element) { - $callback = create_function('$x', 'return "\x" . bin2hex($x[0]);'); + $callback = function ($x) { + return "\x" . bin2hex($x[0]); + }; $value = strtoupper(preg_replace_callback('#[^\x20-\x7E]#', $callback, $value->element)); } $output.= $desc . '=' . $value; $result[$desc] = isset($result[$desc]) ? - array_merge((array) $dn[$prop], array($value)) : + array_merge((array) $result[$desc], array($value)) : $value; $start = false; } @@ -2946,7 +3195,8 @@ class X509 /** * Load a Certificate Signing Request * - * @param string $csr + * @param string|array $csr + * @param int $mode * @access public * @return mixed */ @@ -3083,7 +3333,7 @@ class X509 * * https://developer.mozilla.org/en-US/docs/HTML/Element/keygen * - * @param string $csr + * @param string|array $spkac * @access public * @return mixed */ @@ -3154,7 +3404,7 @@ class X509 /** * Save a SPKAC CSR request * - * @param array $csr + * @param string|array $spkac * @param int $format optional * @access public * @return string @@ -3198,6 +3448,7 @@ class X509 * Load a Certificate Revocation List * * @param string $crl + * @param int $mode * @access public * @return mixed */ @@ -3335,7 +3586,11 @@ class X509 */ function _timeField($date) { - $year = @gmdate("Y", @strtotime($date)); // the same way ASN1.php parses this + if ($date instanceof Element) { + return $date; + } + $dateObj = new DateTime($date, new DateTimeZone('GMT')); + $year = $dateObj->format('Y'); // the same way ASN1.php parses this if ($year < 2050) { return array('utcTime' => $date); } else { @@ -3400,8 +3655,12 @@ class X509 return false; } - $startDate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O'); - $endDate = !empty($this->endDate) ? $this->endDate : @date('D, d M Y H:i:s O', strtotime('+1 year')); + $startDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get())); + $startDate = !empty($this->startDate) ? $this->startDate : $startDate->format('D, d M Y H:i:s O'); + + $endDate = new DateTime('+1 year', new DateTimeZone(@date_default_timezone_get())); + $endDate = !empty($this->endDate) ? $this->endDate : $endDate->format('D, d M Y H:i:s O'); + /* "The serial number MUST be a positive integer" "Conforming CAs MUST NOT use serialNumber values longer than 20 octets." -- https://tools.ietf.org/html/rfc5280#section-4.1.2.2 @@ -3417,7 +3676,7 @@ class X509 'tbsCertificate' => array( 'version' => 'v3', - 'serialNumber' => $serialNumber, // $this->setserialNumber() + 'serialNumber' => $serialNumber, // $this->setSerialNumber() 'signature' => array('algorithm' => $signatureAlgorithm), 'issuer' => false, // this is going to be overwritten later 'validity' => array( @@ -3463,8 +3722,8 @@ class X509 $altName = array(); - if (isset($subject->domains) && count($subject->domains) > 1) { - $altName = array_map(array('X509', '_dnsName'), $subject->domains); + if (isset($subject->domains) && count($subject->domains)) { + $altName = array_map(array('\phpseclib\File\X509', '_dnsName'), $subject->domains); } if (isset($subject->ipAddresses) && count($subject->ipAddresses)) { @@ -3669,7 +3928,9 @@ class X509 $currentCert = isset($this->currentCert) ? $this->currentCert : null; $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null; - $thisUpdate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O'); + + $thisUpdate = new DateTime('now', new DateTimeZone(@date_default_timezone_get())); + $thisUpdate = !empty($this->startDate) ? $this->startDate : $thisUpdate->format('D, d M Y H:i:s O'); if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) { $this->currentCert = $crl->currentCert; @@ -3784,8 +4045,7 @@ class X509 /** * X.509 certificate signing helper function. * - * @param object $key - * @param \phpseclib\File\X509 $subject + * @param \phpseclib\File\X509 $key * @param string $signatureAlgorithm * @access public * @return mixed @@ -3820,7 +4080,11 @@ class X509 */ function setStartDate($date) { - $this->startDate = @date('D, d M Y H:i:s O', @strtotime($date)); + if (!is_object($date) || !is_a($date, 'DateTime')) { + $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get())); + } + + $this->startDate = $date->format('D, d M Y H:i:s O'); } /** @@ -3844,7 +4108,11 @@ class X509 $temp = chr(ASN1::TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp; $this->endDate = new Element($temp); } else { - $this->endDate = @date('D, d M Y H:i:s O', @strtotime($date)); + if (!is_object($date) || !is_a($date, 'DateTime')) { + $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get())); + } + + $this->endDate = $date->format('D, d M Y H:i:s O'); } } @@ -3852,7 +4120,7 @@ class X509 * Set Serial Number * * @param string $serial - * @param $base optional + * @param int $base optional * @access public */ function setSerialNumber($serial, $base = -256) @@ -4054,6 +4322,10 @@ class X509 } $extensions = array_values($extensions); + // fix for https://bugs.php.net/75433 affecting PHP 7.2 + if (!isset($extensions[0])) { + $extensions = array_splice($extensions, 0, 0); + } return $result; } @@ -4511,7 +4783,6 @@ class X509 * Set the IP Addresses's which the cert is to be valid for * * @access public - * @param string $ipAddress optional */ function setIPAddress() { @@ -4574,8 +4845,9 @@ class X509 } $i = count($rclist); + $revocationDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get())); $rclist[] = array('userCertificate' => $serial, - 'revocationDate' => $this->_timeField(@date('D, d M Y H:i:s O'))); + 'revocationDate' => $this->_timeField($revocationDate->format('D, d M Y H:i:s O'))); return $i; } @@ -4782,11 +5054,16 @@ class X509 * subject=/O=organization/OU=org unit/CN=common name * issuer=/O=organization/CN=common name */ - $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); - // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff - $temp = preg_replace('#-+[^-]+-+#', '', $temp); + if (strlen($str) > ini_get('pcre.backtrack_limit')) { + $temp = $str; + } else { + $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); + $temp = preg_replace('#-+END.*[\r\n ]*.*#ms', '', $temp, 1); + } // remove new lines $temp = str_replace(array("\r", "\n", ' '), '', $temp); + // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff + $temp = preg_replace('#^-+[^-]+-+|-+[^-]+-+$#', '', $temp); $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; return $temp != false ? $temp : $str; } diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php index 4b13d7c6..fc24b914 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php @@ -45,7 +45,6 @@ * @author Jim Wigginton * @copyright 2006 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib\Math; @@ -244,7 +243,7 @@ class BigInteger * ?> * * - * @param $x base-10 number or base-$base number if $base set. + * @param int|string|resource $x base-10 number or base-$base number if $base set. * @param int $base * @return \phpseclib\Math\BigInteger * @access public @@ -266,23 +265,27 @@ class BigInteger if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work - ob_start(); - @phpinfo(); - $content = ob_get_contents(); - ob_end_clean(); - - preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); - $versions = array(); - if (!empty($matches[1])) { - for ($i = 0; $i < count($matches[1]); $i++) { - $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); - // Remove letter part in OpenSSL version - if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) { - $versions[$matches[1][$i]] = $fullVersion; - } else { - $versions[$matches[1][$i]] = $m[0]; + // avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems) + if (strpos(ini_get('disable_functions'), 'phpinfo') === false) { + ob_start(); + @phpinfo(); + $content = ob_get_contents(); + ob_end_clean(); + + preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); + + if (!empty($matches[1])) { + for ($i = 0; $i < count($matches[1]); $i++) { + $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); + + // Remove letter part in OpenSSL version + if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) { + $versions[$matches[1][$i]] = $fullVersion; + } else { + $versions[$matches[1][$i]] = $m[0]; + } } } } @@ -360,8 +363,12 @@ class BigInteger case 256: switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: - $sign = $this->is_negative ? '-' : ''; - $this->value = gmp_init($sign . '0x' . bin2hex($x)); + $this->value = function_exists('gmp_import') ? + gmp_import($x) : + gmp_init('0x' . bin2hex($x)); + if ($this->is_negative) { + $this->value = gmp_neg($this->value); + } break; case self::MODE_BCMATH: // round $len to the nearest 4 (thanks, DavidMJ!) @@ -438,6 +445,9 @@ class BigInteger // (?<=^|-)0*: find any 0's that are preceded by the start of the string or by a - (ie. octals) // [^-0-9].*: find any non-numeric characters and then any characters that follow that $x = preg_replace('#(?add(new static(1)) : $this->copy(); $bytes = $temp->toBytes(); - if (empty($bytes)) { // eg. if the number we're trying to convert is -1 + if (!strlen($bytes)) { // eg. if the number we're trying to convert is -1 $bytes = chr(0); } - if (ord($bytes[0]) & 0x80) { + if ($this->precision <= 0 && (ord($bytes[0]) & 0x80)) { $bytes = chr(0) . $bytes; } @@ -548,9 +558,13 @@ class BigInteger return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } - $temp = gmp_strval(gmp_abs($this->value), 16); - $temp = (strlen($temp) & 1) ? '0' . $temp : $temp; - $temp = pack('H*', $temp); + if (function_exists('gmp_export')) { + $temp = gmp_export($this->value); + } else { + $temp = gmp_strval(gmp_abs($this->value), 16); + $temp = (strlen($temp) & 1) ? '0' . $temp : $temp; + $temp = pack('H*', $temp); + } return $this->precision > 0 ? substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : @@ -644,11 +658,11 @@ class BigInteger { $hex = $this->toHex($twos_compliment); $bits = ''; - for ($i = strlen($hex) - 8, $start = strlen($hex) & 7; $i >= $start; $i-=8) { - $bits = str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT) . $bits; + for ($i = strlen($hex) - 6, $start = strlen($hex) % 6; $i >= $start; $i-=6) { + $bits = str_pad(decbin(hexdec(substr($hex, $i, 6))), 24, '0', STR_PAD_LEFT) . $bits; } if ($start) { // hexdec('') == 0 - $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8, '0', STR_PAD_LEFT) . $bits; + $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8 * $start, '0', STR_PAD_LEFT) . $bits; } $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0'); @@ -693,6 +707,7 @@ class BigInteger } $temp = $this->copy(); + $temp->bitmask = false; $temp->is_negative = false; $divisor = new static(); @@ -829,7 +844,7 @@ class BigInteger $opts[] = 'OpenSSL'; } if (!empty($opts)) { - $engine.= ' (' . implode($opts, ', ') . ')'; + $engine.= ' (' . implode('.', $opts) . ')'; } return array( 'value' => '0x' . $this->toHex(true), @@ -1547,7 +1562,9 @@ class BigInteger $temp_value = array($quotient_value[$q_index]); $temp = $temp->multiply($y); $temp_value = &$temp->value; - $temp_value = array_merge($adjust, $temp_value); + if (count($temp_value)) { + $temp_value = array_merge($adjust, $temp_value); + } $x = $x->subtract($temp); @@ -1977,7 +1994,7 @@ class BigInteger * * @see self::_slidingWindow() * @access private - * @param \phpseclib\Math\BigInteger + * @param \phpseclib\Math\BigInteger $n * @return \phpseclib\Math\BigInteger */ function _mod2($n) @@ -2671,7 +2688,7 @@ class BigInteger * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). * * @param \phpseclib\Math\BigInteger $y - * @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. + * @return int that is < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal. * @access public * @see self::equals() * @internal Could return $this->subtract($x), but that's not as fast as what we do do. @@ -2680,7 +2697,14 @@ class BigInteger { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: - return gmp_cmp($this->value, $y->value); + $r = gmp_cmp($this->value, $y->value); + if ($r < -1) { + $r = -1; + } + if ($r > 1) { + $r = 1; + } + return $r; case self::MODE_BCMATH: return bccomp($this->value, $y->value, 0); } @@ -2860,8 +2884,7 @@ class BigInteger switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: $temp = new static(); - $temp->value = gmp_xor($this->value, $x->value); - + $temp->value = gmp_xor(gmp_abs($this->value), gmp_abs($x->value)); return $this->_normalize($temp); case self::MODE_BCMATH: $left = $this->toBytes(); @@ -2877,6 +2900,7 @@ class BigInteger $length = max(count($this->value), count($x->value)); $result = $this->copy(); + $result->is_negative = false; $result->value = array_pad($result->value, $length, 0); $x->value = array_pad($x->value, $length, 0); @@ -2900,7 +2924,7 @@ class BigInteger // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0) $temp = $this->toBytes(); if ($temp == '') { - return ''; + return $this->_normalize(new static()); } $pre_msb = decbin(ord($temp[0])); $temp = ~$temp; @@ -3066,7 +3090,7 @@ class BigInteger * * Byte length is equal to $length. Uses \phpseclib\Crypt\Random if it's loaded and mt_rand if it's not. * - * @param int $length + * @param int $size * @return \phpseclib\Math\BigInteger * @access private */ @@ -3435,7 +3459,7 @@ class BigInteger break; } } - $s = 26 * $i + $j - 1; + $s = 26 * $i + $j; $r->_rshift($s); } @@ -3533,7 +3557,7 @@ class BigInteger * * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision * - * @param \phpseclib\Math\BigInteger + * @param \phpseclib\Math\BigInteger $result * @return \phpseclib\Math\BigInteger * @see self::_trim() * @access private @@ -3546,7 +3570,14 @@ class BigInteger switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: if ($this->bitmask !== false) { + $flip = gmp_cmp($result->value, gmp_init(0)) < 0; + if ($flip) { + $result->value = gmp_neg($result->value); + } $result->value = gmp_and($result->value, $result->bitmask->value); + if ($flip) { + $result->value = gmp_neg($result->value); + } } return $result; @@ -3561,6 +3592,7 @@ class BigInteger $value = &$result->value; if (!count($value)) { + $result->is_negative = false; return $result; } @@ -3602,8 +3634,8 @@ class BigInteger /** * Array Repeat * - * @param $input Array - * @param $multiplier mixed + * @param array $input + * @param mixed $multiplier * @return array * @access private */ @@ -3617,8 +3649,8 @@ class BigInteger * * Shifts binary strings $shift bits, essentially multiplying by 2**$shift. * - * @param $x String - * @param $shift Integer + * @param string $x (by reference) + * @param int $shift * @return string * @access private */ @@ -3646,8 +3678,8 @@ class BigInteger * * Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder. * - * @param $x String - * @param $shift Integer + * @param string $x (by referenc) + * @param int $shift * @return string * @access private */ diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php index 8784b543..cf13496c 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php @@ -99,7 +99,7 @@ class SCP * * Connects to an SSH server * - * @param \phpseclib\Net\SSH1|\phpseclin\Net\SSH2 $ssh + * @param \phpseclib\Net\SSH1|\phpseclib\Net\SSH2 $ssh * @return \phpseclib\Net\SCP * @access public */ @@ -144,6 +144,11 @@ class SCP return false; } + if (empty($remote_file)) { + user_error('remote_file cannot be blank', E_USER_NOTICE); + return false; + } + if (!$this->ssh->exec('scp -t ' . escapeshellarg($remote_file), false)) { // -t = to return false; } @@ -299,6 +304,9 @@ class SCP $response = $this->ssh->_get_binary_packet(); switch ($response[SSH1::RESPONSE_TYPE]) { case NET_SSH1_SMSG_STDOUT_DATA: + if (strlen($response[SSH1::RESPONSE_DATA]) < 4) { + return false; + } extract(unpack('Nlength', $response[SSH1::RESPONSE_DATA])); return $this->ssh->_string_shift($response[SSH1::RESPONSE_DATA], $length); case NET_SSH1_SMSG_STDERR_DATA: diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php index 053ad3b7..35a33e48 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php +++ b/securemail/vendor/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}. * @@ -109,11 +107,11 @@ class SFTP extends SSH2 * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support * concurrent actions, so it's somewhat academic, here. * - * @var int + * @var boolean * @see self::_send_sftp_packet() * @access private */ - var $request_id = false; + var $use_request_id = false; /** * The Packet Type @@ -154,11 +152,29 @@ 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 - * @see self::_realpath() + * @see self::realpath() * @see self::chdir() * @access private */ @@ -187,7 +203,7 @@ class SFTP extends SSH2 * * @see self::getSFTPErrors() * @see self::getLastSFTPError() - * @var string + * @var array * @access private */ var $sftp_errors = array(); @@ -236,6 +252,72 @@ class SFTP extends SSH2 */ var $sortOptions = array(); + /** + * Canonicalization Flag + * + * Determines whether or not paths should be canonicalized before being + * passed on to the remote server. + * + * @see self::enablePathCanonicalization() + * @see self::disablePathCanonicalization() + * @see self::realpath() + * @var bool + * @access private + */ + var $canonicalize_paths = true; + + /** + * Request Buffers + * + * @see self::_get_sftp_packet() + * @var array + * @access private + */ + var $requestBuffer = array(); + + /** + * Preserve timestamps on file downloads / uploads + * + * @see self::get() + * @see self::put() + * @var bool + * @access private + */ + 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() + * @see self::get_sftp_packet() + * @var bool + * @access private + */ + var $channel_close = false; + + /** + * Has the SFTP channel been partially negotiated? + * + * @var bool + * @access private + */ + var $partial_init = false; + /** * Default Constructor. * @@ -256,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', @@ -272,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', @@ -328,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 => 'NET_SFTP_ATTR_EXTENDED' + (-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 @@ -368,29 +477,45 @@ class SFTP extends SSH2 $this->status_codes, $this->attributes, $this->open_flags, + $this->open_flags5, $this->file_types ); if (!defined('NET_SFTP_QUEUE_SIZE')) { - define('NET_SFTP_QUEUE_SIZE', 50); + define('NET_SFTP_QUEUE_SIZE', 32); + } + if (!defined('NET_SFTP_UPLOAD_QUEUE_SIZE')) { + define('NET_SFTP_UPLOAD_QUEUE_SIZE', 1024); } } /** - * Login + * Check a few things before SFTP functions are called * - * @param string $username - * @param string $password * @return bool * @access public */ - function login($username) + function _precheck() { - $args = func_get_args(); - if (!call_user_func_array(array(&$this, '_login'), $args)) { + if (!($this->bitmap & SSH2::MASK_LOGIN)) { return false; } + if ($this->pwd === false) { + return $this->_init_sftp_connection(); + } + + return true; + } + + /** + * Partially initialize an SFTP connection + * + * @return bool + * @access public + */ + function _partial_init_sftp_connection() + { $this->window_size_server_to_client[self::CHANNEL] = $this->window_size; $packet = pack( @@ -409,9 +534,11 @@ class SFTP extends SSH2 $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN; - $response = $this->_get_channel_packet(self::CHANNEL); + $response = $this->_get_channel_packet(self::CHANNEL, true); if ($response === false) { return false; + } elseif ($response === true && $this->isTimeout()) { + return false; } $packet = pack( @@ -430,7 +557,7 @@ class SFTP extends SSH2 $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; - $response = $this->_get_channel_packet(self::CHANNEL); + $response = $this->_get_channel_packet(self::CHANNEL, true); if ($response === false) { // from PuTTY's psftp.exe $command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" . @@ -454,10 +581,12 @@ class SFTP extends SSH2 $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; - $response = $this->_get_channel_packet(self::CHANNEL); + $response = $this->_get_channel_packet(self::CHANNEL, true); if ($response === false) { return false; } + } elseif ($response === true && $this->isTimeout()) { + return false; } $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA; @@ -472,31 +601,43 @@ 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; + } extract(unpack('Nlength', $this->_string_shift($response, 4))); $key = $this->_string_shift($response, $length); + if (strlen($response) < 4) { + return false; + } extract(unpack('Nlength', $this->_string_shift($response, 4))); $value = $this->_string_shift($response, $length); $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->request_id = 1; + 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: @@ -521,12 +662,60 @@ 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 = $this->_realpath('.'); @@ -566,6 +755,46 @@ class SFTP extends SSH2 $this->stat_cache = array(); } + /** + * Enable path canonicalization + * + * @access public + */ + function enablePathCanonicalization() + { + $this->canonicalize_paths = true; + } + + /** + * Enable path canonicalization + * + * @access public + */ + function disablePathCanonicalization() + { + $this->canonicalize_paths = false; + } + + /** + * 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 * @@ -574,6 +803,10 @@ class SFTP extends SSH2 */ function pwd() { + if (!$this->_precheck()) { + return false; + } + return $this->pwd; } @@ -587,12 +820,15 @@ class SFTP extends SSH2 function _logError($response, $status = -1) { if ($status == -1) { + if (strlen($response) < 4) { + return; + } extract(unpack('Nstatus', $this->_string_shift($response, 4))); } $error = $this->status_codes[$status]; - if ($this->version > 2) { + if ($this->version > 2 || strlen($response) < 4) { extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->sftp_errors[] = $error . ': ' . $this->_string_shift($response, $length); } else { @@ -612,6 +848,10 @@ class SFTP extends SSH2 */ function realpath($path) { + if (!$this->_precheck()) { + return false; + } + return $this->_realpath($path); } @@ -621,13 +861,20 @@ class SFTP extends SSH2 * SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it. Returns * the absolute (canonicalized) path. * + * If canonicalize_paths has been disabled using disablePathCanonicalization(), $path is returned as-is. + * * @see self::chdir() + * @see self::disablePathCanonicalization() * @param string $path * @return mixed * @access private */ function _realpath($path) { + if (!$this->canonicalize_paths) { + return $path; + } + if ($this->pwd === false) { // 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))) { @@ -641,6 +888,9 @@ class SFTP extends SSH2 // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks // at is the first part and that part is defined the same in SFTP versions 3 through 6. $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway + if (strlen($response) < 4) { + return false; + } extract(unpack('Nlength', $this->_string_shift($response, 4))); return $this->_string_shift($response, $length); case NET_SFTP_STATUS: @@ -652,7 +902,7 @@ class SFTP extends SSH2 } } - if ($path[0] != '/') { + if (!strlen($path) || $path[0] != '/') { $path = $this->pwd . '/' . $path; } @@ -684,7 +934,7 @@ class SFTP extends SSH2 */ function chdir($dir) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -777,6 +1027,7 @@ class SFTP extends SSH2 } if (is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $value)))) { $temp = $this->_nlist_helper($dir . '/' . $value, true, $relativeDir . $value . '/'); + $temp = is_array($temp) ? $temp : array(); $result = array_merge($result, $temp); } else { $result[] = $relativeDir . $value; @@ -808,7 +1059,17 @@ class SFTP extends SSH2 unset($files[$key]); continue; } - if ($key != '.' && $key != '..' && is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key)))) { + $is_directory = false; + if ($key != '.' && $key != '..') { + if ($this->use_stat_cache) { + $is_directory = is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key))); + } else { + $stat = $this->lstat($dir . '/' . $key); + $is_directory = $stat && $stat['type'] === NET_SFTP_TYPE_DIRECTORY; + } + } + + if ($is_directory) { $depth++; $files[$key] = $this->rawlist($dir . '/' . $key, true); $depth--; @@ -830,7 +1091,7 @@ class SFTP extends SSH2 */ function _list($dir, $raw = true) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -875,14 +1136,27 @@ class SFTP extends SSH2 $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_NAME: + if (strlen($response) < 4) { + return false; + } extract(unpack('Ncount', $this->_string_shift($response, 4))); for ($i = 0; $i < $count; $i++) { + if (strlen($response) < 4) { + return false; + } extract(unpack('Nlength', $this->_string_shift($response, 4))); $shortname = $this->_string_shift($response, $length); - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $longname = $this->_string_shift($response, $length); + // 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); + } $attributes = $this->_parseAttributes($response); - if (!isset($attributes['type'])) { + if (!isset($attributes['type']) && $this->version < 4) { $fileType = $this->_parseLongname($longname); if ($fileType) { $attributes['type'] = $fileType; @@ -905,6 +1179,9 @@ class SFTP extends SSH2 } break; case NET_SFTP_STATUS: + if (strlen($response) < 4) { + return false; + } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_EOF) { $this->_logError($response, $status); @@ -925,7 +1202,7 @@ class SFTP extends SSH2 uasort($contents, array(&$this, '_comparator')); } - return $raw ? $contents : array_keys($contents); + return $raw ? $contents : array_map('strval', array_keys($contents)); } /** @@ -1039,10 +1316,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; @@ -1079,7 +1352,7 @@ class SFTP extends SSH2 $temp[$dir] = array(); } if ($i === $max) { - if (is_object($temp[$dir])) { + if (is_object($temp[$dir]) && is_object($value)) { if (!isset($value->stat) && isset($temp[$dir]->stat)) { $value->stat = $temp[$dir]->stat; } @@ -1108,6 +1381,9 @@ class SFTP extends SSH2 $temp = &$this->stat_cache; $max = count($dirs) - 1; foreach ($dirs as $i => $dir) { + if (!is_array($temp)) { + return false; + } if ($i === $max) { unset($temp[$dir]); return true; @@ -1124,7 +1400,7 @@ class SFTP extends SSH2 * * Mainly used by file_exists * - * @param string $dir + * @param string $path * @return mixed * @access private */ @@ -1134,6 +1410,9 @@ class SFTP extends SSH2 $temp = &$this->stat_cache; foreach ($dirs as $dir) { + if (!is_array($temp)) { + return null; + } if (!isset($temp[$dir])) { return null; } @@ -1153,7 +1432,7 @@ class SFTP extends SSH2 */ function stat($filename) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -1210,7 +1489,7 @@ class SFTP extends SSH2 */ function lstat($filename) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -1267,7 +1546,7 @@ class SFTP extends SSH2 /** * Returns general information about a file or symbolic link * - * Determines information without calling \phpseclib\Net\SFTP::_realpath(). + * Determines information without calling \phpseclib\Net\SFTP::realpath(). * The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT. * * @param string $filename @@ -1324,7 +1603,7 @@ class SFTP extends SSH2 */ function touch($filename, $time = null, $atime = null) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -1340,9 +1619,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; } @@ -1365,19 +1660,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 , - // "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 , + + "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 , + // "if the owner or group is specified as -1, then that ID is not changed" + pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1) : + // quoting , + // "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); } @@ -1385,17 +1708,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, $gid, -1) : + pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, 0, '', strlen($gid), $gid); return $this->_setstat($filename, $attr, $recursive); } @@ -1428,7 +1758,7 @@ class SFTP extends SSH2 return true; } - $filename = $this->_realPath($filename); + $filename = $this->realpath($filename); // rather than return what the permissions *should* be, we'll return what they actually are. this will also // tell us if the file actually exists. // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following: @@ -1462,7 +1792,7 @@ class SFTP extends SSH2 */ function _setstat($filename, $attr, $recursive) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -1480,9 +1810,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; } @@ -1499,6 +1830,9 @@ class SFTP extends SSH2 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); @@ -1549,7 +1883,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; } @@ -1564,7 +1901,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; } @@ -1589,7 +1929,7 @@ class SFTP extends SSH2 */ function readlink($link) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -1611,12 +1951,18 @@ class SFTP extends SSH2 return false; } + if (strlen($response) < 4) { + return false; + } extract(unpack('Ncount', $this->_string_shift($response, 4))); // the file isn't a symlink if (!$count) { return false; } + if (strlen($response) < 4) { + return false; + } extract(unpack('Nlength', $this->_string_shift($response, 4))); return $this->_string_shift($response, $length); } @@ -1633,15 +1979,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; } @@ -1651,6 +2026,9 @@ class SFTP extends SSH2 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); @@ -1664,19 +2042,18 @@ class SFTP extends SSH2 * Creates a directory. * * @param string $dir + * @param int $mode + * @param bool $recursive * @return bool * @access public */ function mkdir($dir, $mode = -1, $recursive = false) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } $dir = $this->_realpath($dir); - // by not providing any permissions, hopefully the server will use the logged in users umask - their - // default permissions. - $attr = $mode == -1 ? "\0\0\0\0" : pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); if ($recursive) { $dirs = explode('/', preg_replace('#/(?=/)|/$#', '', $dir)); @@ -1687,24 +2064,26 @@ class SFTP extends SSH2 for ($i = 0; $i < count($dirs); $i++) { $temp = array_slice($dirs, 0, $i + 1); $temp = implode('/', $temp); - $result = $this->_mkdir_helper($temp, $attr); + $result = $this->_mkdir_helper($temp, $mode); } return $result; } - return $this->_mkdir_helper($dir, $attr); + return $this->_mkdir_helper($dir, $mode); } /** * Helper function for directory creation * * @param string $dir + * @param int $mode * @return bool * @access private */ - function _mkdir_helper($dir, $attr) + function _mkdir_helper($dir, $mode) { - if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*a*', strlen($dir), $dir, $attr))) { + // send SSH_FXP_MKDIR without any attributes (that's what the \0\0\0\0 is doing) + if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*a*', strlen($dir), $dir, "\0\0\0\0"))) { return false; } @@ -1714,12 +2093,19 @@ class SFTP extends SSH2 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; } + if ($mode !== -1) { + $this->chmod($mode, $dir); + } + return true; } @@ -1732,7 +2118,7 @@ class SFTP extends SSH2 */ function rmdir($dir) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -1751,6 +2137,9 @@ class SFTP extends SSH2 return false; } + if (strlen($response) < 4) { + return false; + } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED? @@ -1778,7 +2167,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. * @@ -1814,7 +2204,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; } @@ -1825,10 +2215,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; @@ -1838,10 +2232,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; } @@ -1871,7 +2272,14 @@ class SFTP extends SSH2 break; case is_resource($data): $mode = $mode & ~self::SOURCE_LOCAL_FILE; - $fp = $data; + $info = stream_get_meta_data($data); + if ($info['wrapper_type'] == 'PHP' && $info['stream_type'] == 'Input') { + $fp = fopen('php://memory', 'w+'); + stream_copy_to_stream($data, $fp); + rewind($fp); + } else { + $fp = $data; + } break; case $mode & self::SOURCE_LOCAL_FILE: if (!is_file($data)) { @@ -1886,7 +2294,7 @@ class SFTP extends SSH2 if (isset($fp)) { $stat = fstat($fp); - $size = $stat['size']; + $size = !empty($stat) ? $stat['size'] : 0; if ($local_start >= 0) { fseek($fp, $local_start); @@ -1901,10 +2309,10 @@ class SFTP extends SSH2 $sent = 0; $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size; - $sftp_packet_size = 4096; // PuTTY uses 4096 - // make the SFTP packet be exactly 4096 bytes by including the bytes in the NET_SFTP_WRITE packets "header" + $sftp_packet_size = $this->max_sftp_packet; + // make the SFTP packet be exactly the SFTP packet size by including the bytes in the NET_SFTP_WRITE packets "header" $sftp_packet_size-= strlen($handle) + 25; - $i = 0; + $i = $j = 0; while ($dataCallback || ($size === 0 || $sent < $size)) { if ($dataCallback) { $temp = call_user_func($dataCallback, $sftp_packet_size); @@ -1920,7 +2328,7 @@ class SFTP extends SSH2 $subtemp = $offset + $sent; $packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp); - if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet)) { + if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet, $j)) { if ($mode & self::SOURCE_LOCAL_FILE) { fclose($fp); } @@ -1932,8 +2340,9 @@ class SFTP extends SSH2 } $i++; + $j++; - if ($i == NET_SFTP_QUEUE_SIZE) { + if ($i == NET_SFTP_UPLOAD_QUEUE_SIZE) { if (!$this->_read_put_responses($i)) { $i = 0; break; @@ -1942,6 +2351,8 @@ class SFTP extends SSH2 } } + $result = $this->_close_handle($handle); + if (!$this->_read_put_responses($i)) { if ($mode & self::SOURCE_LOCAL_FILE) { fclose($fp); @@ -1950,11 +2361,33 @@ class SFTP extends SSH2 return false; } - if ($mode & self::SOURCE_LOCAL_FILE) { - fclose($fp); + 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; } /** @@ -1976,6 +2409,9 @@ class SFTP extends SSH2 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); @@ -2007,6 +2443,9 @@ class SFTP extends SSH2 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); @@ -2029,12 +2468,13 @@ class SFTP extends SSH2 * @param string $local_file * @param int $offset * @param int $length + * @param callable|null $progressCallback * @return mixed * @access public */ - function get($remote_file, $local_file = false, $offset = 0, $length = -1) + function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -2043,7 +2483,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; } @@ -2067,7 +2510,7 @@ class SFTP extends SSH2 $res_offset = $stat['size']; } else { $res_offset = 0; - if ($local_file !== false) { + if ($local_file !== false && !is_callable($local_file)) { $fp = fopen($local_file, 'wb'); if (!$fp) { return false; @@ -2077,7 +2520,7 @@ class SFTP extends SSH2 } } - $fclose_check = $local_file !== false && !is_resource($local_file); + $fclose_check = $local_file !== false && !is_callable($local_file) && !is_resource($local_file); $start = $offset; $read = 0; @@ -2090,7 +2533,7 @@ class SFTP extends SSH2 $packet_size = $length > 0 ? min($this->max_sftp_packet, $length - $read) : $this->max_sftp_packet; $packet = pack('Na*N3', strlen($handle), $handle, $tempoffset / 4294967296, $tempoffset, $packet_size); - if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) { + if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet, $i)) { if ($fclose_check) { fclose($fp); } @@ -2105,15 +2548,17 @@ class SFTP extends SSH2 break; } + $packets_sent = $i - 1; + $clear_responses = false; while ($i > 0) { $i--; if ($clear_responses) { - $this->_get_sftp_packet(); + $this->_get_sftp_packet($packets_sent - $i); continue; } else { - $response = $this->_get_sftp_packet(); + $response = $this->_get_sftp_packet($packets_sent - $i); } switch ($this->packet_type) { @@ -2122,9 +2567,14 @@ class SFTP extends SSH2 $offset+= strlen($temp); if ($local_file === false) { $content.= $temp; + } elseif (is_callable($local_file)) { + $local_file($temp); } else { fputs($fp, $temp); } + if (is_callable($progressCallback)) { + call_user_func($progressCallback, $offset); + } $temp = null; break; case NET_SFTP_STATUS: @@ -2136,7 +2586,14 @@ class SFTP extends SSH2 if ($fclose_check) { fclose($fp); } - user_error('Expected SSH_FX_DATA or SSH_FXP_STATUS'); + // 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 { + user_error('Expected SSH_FX_DATA or SSH_FXP_STATUS'); + } } $response = null; } @@ -2156,6 +2613,11 @@ class SFTP extends SSH2 if ($fclose_check) { fclose($fp); + + if ($this->preserveTime) { + $stat = $this->stat($remote_file); + touch($local_file, $stat['mtime'], $stat['atime']); + } } if (!$this->_close_handle($handle)) { @@ -2176,7 +2638,16 @@ class SFTP extends SSH2 */ function delete($path, $recursive = true) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { + return false; + } + + if (is_object($path)) { + // It's an object. Cast it as string before we check anything else. + $path = (string) $path; + } + + if (!is_string($path) || $path == '') { return false; } @@ -2197,6 +2668,9 @@ class SFTP extends SSH2 } // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + if (strlen($response) < 4) { + return false; + } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); @@ -2293,6 +2767,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); @@ -2363,6 +2841,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); @@ -2391,6 +2873,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); @@ -2565,11 +3051,16 @@ class SFTP extends SSH2 * * @param string $path * @param string $prop + * @param mixed $type * @return mixed * @access private */ function _get_xstat_cache_prop($path, $prop, $type) { + if (!$this->_precheck()) { + return false; + } + if ($this->use_stat_cache) { $path = $this->_realpath($path); @@ -2590,7 +3081,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 @@ -2599,7 +3092,7 @@ class SFTP extends SSH2 */ function rename($oldname, $newname) { - if (!($this->bitmap & SSH2::MASK_LOGIN)) { + if (!$this->_precheck()) { return false; } @@ -2611,6 +3104,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; } @@ -2622,6 +3127,9 @@ class SFTP extends SSH2 } // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED + if (strlen($response) < 4) { + return false; + } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); @@ -2637,6 +3145,31 @@ class SFTP extends SSH2 return true; } + /** + * 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 * @@ -2648,12 +3181,26 @@ class SFTP extends SSH2 */ function _parseAttributes(&$response) { + if ($this->version >= 4) { + $length = 5; + $format = 'Nflags/Ctype'; + } else { + $length = 4; + $format = 'Nflags'; + } + $attr = array(); - extract(unpack('Nflags', $this->_string_shift($response, 4))); - // SFTPv4+ have a type field (a byte) that follows the above flag field + if (strlen($response) < $length) { + user_error('Malformed file attributes'); + return array(); + } + 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_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 @@ -2662,10 +3209,18 @@ 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; + } $attr+= unpack('Npermissions', $this->_string_shift($response, 4)); // mode == permissions; permissions was the original array key and is retained for bc purposes. // mode was added because that's the more industry standard terminology @@ -2675,14 +3230,150 @@ 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; + } + extract(unpack('Ncount', $this->_string_shift($response, 4))); + for ($i = 0; $i < $count; $i++) { + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } extract(unpack('Nlength', $this->_string_shift($response, 4))); $key = $this->_string_shift($response, $length); + if (strlen($response) < 4) { + user_error('Malformed file attributes'); + return $attr; + } extract(unpack('Nlength', $this->_string_shift($response, 4))); $attr[$key] = $this->_string_shift($response, $length); } @@ -2774,15 +3465,20 @@ class SFTP extends SSH2 * * @param int $type * @param string $data + * @param int $request_id * @see self::_get_sftp_packet() * @see self::_send_channel_packet() * @return bool * @access private */ - function _send_sftp_packet($type, $data) + function _send_sftp_packet($type, $data, $request_id = 1) { - $packet = $this->request_id !== false ? - pack('NCNa*', strlen($data) + 5, $type, $this->request_id, $data) : + // in SSH2.php the timeout is cumulative per function call. eg. exec() will + // timeout after 10s. but for SFTP.php it's cumulative per packet + $this->curTimeout = $this->timeout; + + $packet = $this->use_request_id ? + pack('NCNa*', strlen($data) + 5, $type, $request_id, $data) : pack('NCa*', strlen($data) + 1, $type, $data); $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 @@ -2792,13 +3488,21 @@ class SFTP extends SSH2 if (defined('NET_SFTP_LOGGING')) { $packet_type = '-> ' . $this->packet_types[$type] . ' (' . round($stop - $start, 4) . 's)'; - if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) { - echo "
\r\n" . $this->_format_log(array($data), array($packet_type)) . "\r\n
\r\n"; - flush(); - ob_flush(); + if (NET_SFTP_LOGGING == self::LOG_REALTIME) { + switch (PHP_SAPI) { + case 'cli': + $start = $stop = "\r\n"; + break; + default: + $start = '
';
+                        $stop = '
'; + } + echo $start . $this->_format_log(array($data), array($packet_type)) . $stop; + @flush(); + @ob_flush(); } else { $this->packet_type_log[] = $packet_type; - if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) { + if (NET_SFTP_LOGGING == self::LOG_COMPLEX) { $this->packet_log[] = $data; } } @@ -2807,6 +3511,20 @@ class SFTP extends SSH2 return $result; } + /** + * Resets a connection for re-use + * + * @param int $reason + * @access private + */ + function _reset_connection($reason) + { + parent::_reset_connection($reason); + $this->use_request_id = false; + $this->pwd = false; + $this->requestBuffer = array(); + } + /** * Receives SFTP Packets * @@ -2820,29 +3538,55 @@ class SFTP extends SSH2 * @return string * @access private */ - function _get_sftp_packet() + function _get_sftp_packet($request_id = null) { - $this->curTimeout = false; + $this->channel_close = false; + + if (isset($request_id) && isset($this->requestBuffer[$request_id])) { + $this->packet_type = $this->requestBuffer[$request_id]['packet_type']; + $temp = $this->requestBuffer[$request_id]['packet']; + unset($this->requestBuffer[$request_id]); + return $temp; + } + + // in SSH2.php the timeout is cumulative per function call. eg. exec() will + // timeout after 10s. but for SFTP.php it's cumulative per packet + $this->curTimeout = $this->timeout; $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 // SFTP packet length while (strlen($this->packet_buffer) < 4) { - $temp = $this->_get_channel_packet(self::CHANNEL); - if (is_bool($temp)) { + $temp = $this->_get_channel_packet(self::CHANNEL, true); + if ($temp === true) { + if ($this->channel_status[self::CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) { + $this->channel_close = true; + } $this->packet_type = false; $this->packet_buffer = ''; return false; } + if ($temp === false) { + return false; + } $this->packet_buffer.= $temp; } + if (strlen($this->packet_buffer) < 4) { + return false; + } extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4))); $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 (!$this->allow_arbitrary_length_packets && !$this->use_request_id && $tempLength > 256 * 1024) { + user_error('Invalid SFTP packet size'); + return false; + } + // SFTP packet type and data payload while ($tempLength > 0) { - $temp = $this->_get_channel_packet(self::CHANNEL); + $temp = $this->_get_channel_packet(self::CHANNEL, true); if (is_bool($temp)) { $this->packet_type = false; $this->packet_buffer = ''; @@ -2856,8 +3600,8 @@ class SFTP extends SSH2 $this->packet_type = ord($this->_string_shift($this->packet_buffer)); - if ($this->request_id !== false) { - $this->_string_shift($this->packet_buffer, 4); // remove the request id + if ($this->use_request_id) { + extract(unpack('Npacket_id', $this->_string_shift($this->packet_buffer, 4))); // remove the request id $length-= 5; // account for the request id and the packet type } else { $length-= 1; // account for the packet type @@ -2868,18 +3612,34 @@ class SFTP extends SSH2 if (defined('NET_SFTP_LOGGING')) { $packet_type = '<- ' . $this->packet_types[$this->packet_type] . ' (' . round($stop - $start, 4) . 's)'; - if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) { - echo "
\r\n" . $this->_format_log(array($packet), array($packet_type)) . "\r\n
\r\n"; - flush(); - ob_flush(); + if (NET_SFTP_LOGGING == self::LOG_REALTIME) { + switch (PHP_SAPI) { + case 'cli': + $start = $stop = "\r\n"; + break; + default: + $start = '
';
+                        $stop = '
'; + } + echo $start . $this->_format_log(array($packet), array($packet_type)) . $stop; + @flush(); + @ob_flush(); } else { $this->packet_type_log[] = $packet_type; - if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) { + if (NET_SFTP_LOGGING == self::LOG_COMPLEX) { $this->packet_log[] = $packet; } } } + if (isset($request_id) && $this->use_request_id && $packet_id != $request_id) { + $this->requestBuffer[$packet_id] = array( + 'packet_type' => $this->packet_type, + 'packet' => $packet + ); + return $this->_get_sftp_packet($request_id); + } + return $packet; } @@ -2898,10 +3658,10 @@ class SFTP extends SSH2 } switch (NET_SFTP_LOGGING) { - case NET_SFTP_LOG_COMPLEX: + case self::LOG_COMPLEX: return $this->_format_log($this->packet_log, $this->packet_type_log); break; - //case NET_SFTP_LOG_SIMPLE: + //case self::LOG_SIMPLE: default: return $this->packet_type_log; } @@ -2910,7 +3670,7 @@ class SFTP extends SSH2 /** * Returns all errors * - * @return string + * @return array * @access public */ function getSFTPErrors() @@ -2937,13 +3697,51 @@ class SFTP extends SSH2 */ function getSupportedVersions() { - $temp = array('version' => $this->version); + if (!($this->bitmap & NET_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']; } return $temp; } + /** + * 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 * @@ -2956,4 +3754,24 @@ class SFTP extends SSH2 $this->pwd = false; parent::_disconnect($reason); } + + /** + * Enable Date Preservation + * + * @access public + */ + function enableDatePreservation() + { + $this->preserveTime = true; + } + + /** + * Disable Date Preservation + * + * @access public + */ + function disableDatePreservation() + { + $this->preserveTime = false; + } } diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php index 08d726ca..ec9e5841 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php @@ -179,7 +179,7 @@ class Stream if ($host[0] == '$') { $host = substr($host, 1); - global $$host; + global ${$host}; if (($$host instanceof SFTP) === false) { return false; } @@ -410,7 +410,7 @@ class Stream { switch ($whence) { case SEEK_SET: - if ($offset >= $this->size || $offset < 0) { + if ($offset < 0) { return false; } break; @@ -447,7 +447,9 @@ class Stream // and https://github.com/php/php-src/blob/master/main/php_streams.h#L592 switch ($option) { case 1: // PHP_STREAM_META_TOUCH - return $this->sftp->touch($path, $var[0], $var[1]); + $time = isset($var[0]) ? $var[0] : null; + $atime = isset($var[1]) ? $var[1] : null; + return $this->sftp->touch($path, $time, $atime); case 2: // PHP_STREAM_OWNER_NAME case 3: // PHP_STREAM_GROUP_NAME return false; @@ -626,7 +628,6 @@ class Stream * $options. What does 8 correspond to? * * @param string $path - * @param int $mode * @param int $options * @return bool * @access public @@ -768,8 +769,8 @@ class Stream * If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method. * - * @param string - * @param array + * @param string $name + * @param array $arguments * @return mixed * @access public */ diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php index cc108a94..e372b8b9 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php @@ -575,28 +575,46 @@ class SSH1 $this->_string_shift($response[self::RESPONSE_DATA], 4); + if (strlen($response[self::RESPONSE_DATA]) < 2) { + return false; + } $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); $server_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); $this->server_key_public_exponent = $server_key_public_exponent; + if (strlen($response[self::RESPONSE_DATA]) < 2) { + return false; + } $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); $server_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); + $this->server_key_public_modulus = $server_key_public_modulus; $this->_string_shift($response[self::RESPONSE_DATA], 4); + if (strlen($response[self::RESPONSE_DATA]) < 2) { + return false; + } $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); $host_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); $this->host_key_public_exponent = $host_key_public_exponent; + if (strlen($response[self::RESPONSE_DATA]) < 2) { + return false; + } $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); $host_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); + $this->host_key_public_modulus = $host_key_public_modulus; $this->_string_shift($response[self::RESPONSE_DATA], 4); // get a list of the supported ciphers + if (strlen($response[self::RESPONSE_DATA]) < 4) { + return false; + } extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4))); + foreach ($this->supported_ciphers as $mask => $name) { if (($supported_ciphers_mask & (1 << $mask)) == 0) { unset($this->supported_ciphers[$mask]); @@ -604,6 +622,9 @@ class SSH1 } // get a list of the supported authentications + if (strlen($response[self::RESPONSE_DATA]) < 4) { + return false; + } extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4))); foreach ($this->supported_authentications as $mask => $name) { if (($supported_authentications_mask & (1 << $mask)) == 0) { @@ -791,6 +812,7 @@ class SSH1 * @see self::interactiveRead() * @see self::interactiveWrite() * @param string $cmd + * @param bool $block * @return mixed * @access public */ @@ -895,7 +917,7 @@ class SSH1 /** * Returns the output of an interactive shell when there's a match for $expect * - * $expect can take the form of a string literal or, if $mode == self::READ__REGEX, + * $expect can take the form of a string literal or, if $mode == self::READ_REGEX, * a regular expression. * * @see self::write() @@ -904,7 +926,7 @@ class SSH1 * @return bool * @access public */ - function read($expect, $mode = self::READ__SIMPLE) + function read($expect, $mode = self::READ_SIMPLE) { if (!($this->bitmap & self::MASK_LOGIN)) { user_error('Operation disallowed prior to login()'); @@ -918,7 +940,7 @@ class SSH1 $match = $expect; while (true) { - if ($mode == self::READ__REGEX) { + if ($mode == self::READ_REGEX) { preg_match($expect, $this->interactiveBuffer, $matches); $match = isset($matches[0]) ? $matches[0] : ''; } @@ -1091,7 +1113,11 @@ class SSH1 } $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 - $temp = unpack('Nlength', fread($this->fsock, 4)); + $data = fread($this->fsock, 4); + if (strlen($data) < 4) { + return false; + } + $temp = unpack('Nlength', $data); $padding_length = 8 - ($temp['length'] & 7); $length = $temp['length'] + $padding_length; @@ -1099,6 +1125,9 @@ class SSH1 while ($length > 0) { $temp = fread($this->fsock, $length); + if (strlen($temp) != $length) { + return false; + } $raw.= $temp; $length-= strlen($temp); } @@ -1112,6 +1141,9 @@ class SSH1 $type = $raw[$padding_length]; $data = substr($raw, $padding_length + 1, -4); + if (strlen($raw) < 4) { + return false; + } $temp = unpack('Ncrc', substr($raw, -4)); //if ( $temp['crc'] != $this->_crc($padding . $type . $data) ) { @@ -1354,7 +1386,6 @@ class SSH1 * named constants from it, using the value as the name of the constant and the index as the value of the constant. * If any of the constants that would be defined already exists, none of the constants will be defined. * - * @param array $array * @access private */ function _define_array() @@ -1553,7 +1584,8 @@ class SSH1 * * Makes sure that only the last 1MB worth of packets will be logged * - * @param string $data + * @param int $protocol_flags + * @param string $message * @access private */ function _append_log($protocol_flags, $message) diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php index 11580755..3377e2b8 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php @@ -61,6 +61,23 @@ use phpseclib\Crypt\Twofish; use phpseclib\Math\BigInteger; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification. use phpseclib\System\SSH\Agent; +/**#@+ + * @access private + */ +/** + * No compression + */ +define('NET_SSH2_COMPRESSION_NONE', 1); +/** + * zlib compression + */ +define('NET_SSH2_COMPRESSION_ZLIB', 2); +/** + * zlib@openssh.com + */ +define('NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH', 3); +/**#@-*/ + /** * Pure-PHP implementation of SSHv2. * @@ -100,10 +117,11 @@ class SSH2 * @see \phpseclib\Net\SSH2::_get_channel_packet() * @access private */ - const CHANNEL_EXEC = 0; // PuTTy uses 0x100 - const CHANNEL_SHELL = 1; - const CHANNEL_SUBSYSTEM = 2; - const CHANNEL_AGENT_FORWARD = 3; + const CHANNEL_EXEC = 1; // PuTTy uses 0x100 + const CHANNEL_SHELL = 2; + const CHANNEL_SUBSYSTEM = 3; + const CHANNEL_AGENT_FORWARD = 4; + const CHANNEL_KEEP_ALIVE = 5; /**#@-*/ /**#@+ @@ -126,6 +144,10 @@ class SSH2 * Dumps the content real-time to a file */ const LOG_REALTIME_FILE = 4; + /** + * Make sure that the log never gets larger than this + */ + const LOG_MAX_SIZE = 1048576; // 1024 * 1024 /**#@-*/ /**#@+ @@ -141,9 +163,12 @@ class SSH2 */ const READ_REGEX = 2; /** - * Make sure that the log never gets larger than this + * Returns whenever a data packet is received. + * + * Some data packets may only contain a single character so it may be necessary + * to call read() multiple times when using this option */ - const LOG_MAX_SIZE = 1048576; // 1024 * 1024 + const READ_NEXT = 3; /**#@-*/ /** @@ -201,6 +226,15 @@ class SSH2 */ var $kex_algorithms = false; + /** + * Key Exchange Algorithm + * + * @see self::getMethodsNegotiated() + * @var string|false + * @access private + */ + var $kex_algorithm = false; + /** * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods * @@ -309,6 +343,15 @@ class SSH2 */ var $languages_client_to_server = false; + /** + * Preferred Algorithms + * + * @see self::setPreferredAlgorithms() + * @var array + * @access private + */ + var $preferred = array(); + /** * Block Size for Server to Client Encryption * @@ -566,6 +609,20 @@ class SSH2 */ var $window_size = 0x7FFFFFFF; + /** + * What we resize the window to + * + * When PuTTY resizes the window it doesn't add an additional 0x7FFFFFFF bytes - it adds 0x40000000 bytes. + * Some SFTP clients (GoAnywhere) don't support adding 0x7FFFFFFF to the window size after the fact so + * we'll just do what PuTTY does + * + * @var int + * @see self::_send_channel_packet() + * @see self::exec() + * @access private + */ + var $window_resize = 0x40000000; + /** * Window size, server to client * @@ -647,6 +704,14 @@ class SSH2 */ var $curTimeout; + /** + * Keep Alive Interval + * + * @see self::setKeepAlive() + * @access private + */ + var $keepAlive; + /** * Real-time log file pointer * @@ -866,6 +931,119 @@ class SSH2 */ var $agent; + /** + * Send the identification string first? + * + * @var bool + * @access private + */ + var $send_id_string_first = true; + + /** + * Send the key exchange initiation packet first? + * + * @var bool + * @access private + */ + var $send_kex_first = true; + + /** + * Some versions of OpenSSH incorrectly calculate the key size + * + * @var bool + * @access private + */ + var $bad_key_size_fix = false; + + /** + * Should we try to re-connect to re-establish keys? + * + * @var bool + * @access private + */ + var $retry_connect = false; + + /** + * Binary Packet Buffer + * + * @var string|false + * @access private + */ + var $binary_packet_buffer = false; + + /** + * Preferred Signature Format + * + * @var string|false + * @access private + */ + var $preferred_signature_format = false; + + /** + * Authentication Credentials + * + * @var array + * @access private + */ + var $auth = array(); + + /** + * The authentication methods that may productively continue authentication. + * + * @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 = NET_SSH2_COMPRESSION_NONE; + + /** + * Decompression method + * + * @var resource|object + * @access private + */ + var $decompress = 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 + */ + var $regenerate_compression_context = false; + + /** + * Regenerate Decompression Context + * + * @var bool + * @access private + */ + var $regenerate_decompression_context = false; + /** * Default Constructor. * @@ -978,13 +1156,69 @@ class SSH2 * CRYPT_MODE_INTERNAL, CRYPT_MODE_MCRYPT * * @param int $engine - * @access private + * @access public */ function setCryptoEngine($engine) { $this->crypto_engine = $engine; } + /** + * Send Identification String First + * + * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established, + * both sides MUST send an identification string". It does not say which side sends it first. In + * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy + * + * @access public + */ + function sendIdentificationStringFirst() + { + $this->send_id_string_first = true; + } + + /** + * Send Identification String Last + * + * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established, + * both sides MUST send an identification string". It does not say which side sends it first. In + * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy + * + * @access public + */ + function sendIdentificationStringLast() + { + $this->send_id_string_first = false; + } + + /** + * Send SSH_MSG_KEXINIT First + * + * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending + * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory + * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy + * + * @access public + */ + function sendKEXINITFirst() + { + $this->send_kex_first = true; + } + + /** + * Send SSH_MSG_KEXINIT Last + * + * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending + * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory + * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy + * + * @access public + */ + function sendKEXINITLast() + { + $this->send_kex_first = false; + } + /** * Connect to an SSHv2 server * @@ -1005,7 +1239,10 @@ class SSH2 if (!is_resource($this->fsock)) { $start = microtime(true); - $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout); + // with stream_select a timeout of 0 means that no timeout takes place; + // with fsockopen a timeout of 0 means that you instantly timeout + // to resolve this incompatibility a timeout of 100,000 will be used for fsockopen if timeout is 0 + $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout == 0 ? 100000 : $this->curTimeout); if (!$this->fsock) { $host = $this->host . ':' . $this->port; user_error(rtrim("Cannot connect to $host. Error $errno. $errstr")); @@ -1013,14 +1250,21 @@ class SSH2 } $elapsed = microtime(true) - $start; - $this->curTimeout-= $elapsed; - - if ($this->curTimeout <= 0) { - $this->is_timeout = true; - return false; + if ($this->curTimeout) { + $this->curTimeout-= $elapsed; + if ($this->curTimeout < 0) { + $this->is_timeout = true; + return false; + } } } + $this->identifier = $this->_generate_identifier(); + + if ($this->send_id_string_first) { + fputs($this->fsock, $this->identifier . "\r\n"); + } + /* According to the SSH2 specs, "The server MAY send other lines of data before sending the version @@ -1056,6 +1300,9 @@ class SSH2 if (strlen($temp) == 255) { continue; } + if ($temp === false) { + return false; + } $line.= "$temp\n"; @@ -1076,14 +1323,13 @@ class SSH2 } if (feof($this->fsock)) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } $extra = $matches[1]; - $this->identifier = $this->_generate_identifier(); - if (defined('NET_SSH2_LOGGING')) { $this->_append_log('<-', $matches[0]); $this->_append_log('->', $this->identifier . "\r\n"); @@ -1091,28 +1337,37 @@ class SSH2 $this->server_identifier = trim($temp, "\r\n"); if (strlen($extra)) { - $this->errors[] = utf8_decode($data); + $this->errors[] = $data; } - if ($matches[3] != '1.99' && $matches[3] != '2.0') { + if (version_compare($matches[3], '1.99', '<')) { user_error("Cannot connect to SSH $matches[3] servers"); return false; } - fputs($this->fsock, $this->identifier . "\r\n"); - - $response = $this->_get_binary_packet(); - if ($response === false) { - user_error('Connection closed by server'); - return false; + if (!$this->send_id_string_first) { + fputs($this->fsock, $this->identifier . "\r\n"); } - if (ord($response[0]) != NET_SSH2_MSG_KEXINIT) { - user_error('Expected SSH_MSG_KEXINIT'); - return false; + if (!$this->send_kex_first) { + $response = $this->_get_binary_packet(); + if ($response === false) { + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; + } + + if (!strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) { + user_error('Expected SSH_MSG_KEXINIT'); + return false; + } + + if (!$this->_key_exchange($response)) { + return false; + } } - if (!$this->_key_exchange($response)) { + if ($this->send_kex_first && !$this->_key_exchange()) { return false; } @@ -1134,7 +1389,7 @@ class SSH2 $identifier = 'SSH-2.0-phpseclib_2.0'; $ext = array(); - if (extension_loaded('libsodium')) { + if (function_exists('sodium_crypto_box_publickey_from_secretkey')) { $ext[] = 'libsodium'; } @@ -1160,184 +1415,68 @@ class SSH2 /** * Key Exchange * - * @param string $kexinit_payload_server + * @param string $kexinit_payload_server optional * @access private */ - function _key_exchange($kexinit_payload_server) + function _key_exchange($kexinit_payload_server = false) { - $kex_algorithms = array( - // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using - // Curve25519. See doc/curve25519-sha256@libssh.org.txt in the - // libssh repository for more information. - 'curve25519-sha256@libssh.org', + $preferred = $this->preferred; + $send_kex = true; - // Diffie-Hellman Key Agreement (DH) using integer modulo prime - // groups. - 'diffie-hellman-group1-sha1', // REQUIRED - 'diffie-hellman-group14-sha1', // REQUIRED - 'diffie-hellman-group-exchange-sha1', // RFC 4419 - 'diffie-hellman-group-exchange-sha256', // RFC 4419 - ); - if (!function_exists('\\Sodium\\library_version_major')) { - $kex_algorithms = array_diff( - $kex_algorithms, - array('curve25519-sha256@libssh.org') - ); - } - - $server_host_key_algorithms = array( - 'ssh-rsa', // RECOMMENDED sign Raw RSA Key - 'ssh-dss' // REQUIRED sign Raw DSS Key - ); - - $encryption_algorithms = array( - // from : - 'arcfour256', - 'arcfour128', - - //'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key - - // CTR modes from : - 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key - 'aes192-ctr', // RECOMMENDED AES with 192-bit key - 'aes256-ctr', // RECOMMENDED AES with 256-bit key - - 'twofish128-ctr', // OPTIONAL Twofish in SDCTR mode, with 128-bit key - 'twofish192-ctr', // OPTIONAL Twofish with 192-bit key - 'twofish256-ctr', // OPTIONAL Twofish with 256-bit key - - 'aes128-cbc', // RECOMMENDED AES with a 128-bit key - 'aes192-cbc', // OPTIONAL AES with a 192-bit key - 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key - - 'twofish128-cbc', // OPTIONAL Twofish with a 128-bit key - 'twofish192-cbc', // OPTIONAL Twofish with a 192-bit key - 'twofish256-cbc', - 'twofish-cbc', // OPTIONAL alias for "twofish256-cbc" - // (this is being retained for historical reasons) - - 'blowfish-ctr', // OPTIONAL Blowfish in SDCTR mode - - 'blowfish-cbc', // OPTIONAL Blowfish in CBC mode - - '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode - - '3des-cbc', // REQUIRED three-key 3DES in CBC mode - //'none' // OPTIONAL no encryption; NOT RECOMMENDED - ); - - if (extension_loaded('openssl') && !extension_loaded('mcrypt')) { - // OpenSSL does not support arcfour256 in any capacity and arcfour128 / arcfour support is limited to - // instances that do not use continuous buffers - $encryption_algorithms = array_diff( - $encryption_algorithms, - array('arcfour256', 'arcfour128', 'arcfour') - ); - } - - if (class_exists('\phpseclib\Crypt\RC4') === false) { - $encryption_algorithms = array_diff( - $encryption_algorithms, - array('arcfour256', 'arcfour128', 'arcfour') - ); - } - if (class_exists('\phpseclib\Crypt\Rijndael') === false) { - $encryption_algorithms = array_diff( - $encryption_algorithms, - array('aes128-ctr', 'aes192-ctr', 'aes256-ctr', 'aes128-cbc', 'aes192-cbc', 'aes256-cbc') - ); - } - if (class_exists('\phpseclib\Crypt\Twofish') === false) { - $encryption_algorithms = array_diff( - $encryption_algorithms, - array('twofish128-ctr', 'twofish192-ctr', 'twofish256-ctr', 'twofish128-cbc', 'twofish192-cbc', 'twofish256-cbc', 'twofish-cbc') - ); - } - if (class_exists('\phpseclib\Crypt\Blowfish') === false) { - $encryption_algorithms = array_diff( - $encryption_algorithms, - array('blowfish-ctr', 'blowfish-cbc') - ); - } - if (class_exists('\phpseclib\Crypt\TripleDES') === false) { - $encryption_algorithms = array_diff( - $encryption_algorithms, - array('3des-ctr', '3des-cbc') - ); - } - $encryption_algorithms = array_values($encryption_algorithms); - - $mac_algorithms = array( - // from : - 'hmac-sha2-256',// RECOMMENDED HMAC-SHA256 (digest length = key length = 32) - - 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20) - 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20) - 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16) - 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16) - //'none' // OPTIONAL no MAC; NOT RECOMMENDED - ); - - $compression_algorithms = array( - 'none' // REQUIRED no compression - //'zlib' // OPTIONAL ZLIB (LZ77) compression - ); + $kex_algorithms = isset($preferred['kex']) ? + $preferred['kex'] : + $this->getSupportedKEXAlgorithms(); + $server_host_key_algorithms = isset($preferred['hostkey']) ? + $preferred['hostkey'] : + $this->getSupportedHostKeyAlgorithms(); + $s2c_encryption_algorithms = isset($preferred['server_to_client']['crypt']) ? + $preferred['server_to_client']['crypt'] : + $this->getSupportedEncryptionAlgorithms(); + $c2s_encryption_algorithms = isset($preferred['client_to_server']['crypt']) ? + $preferred['client_to_server']['crypt'] : + $this->getSupportedEncryptionAlgorithms(); + $s2c_mac_algorithms = isset($preferred['server_to_client']['mac']) ? + $preferred['server_to_client']['mac'] : + $this->getSupportedMACAlgorithms(); + $c2s_mac_algorithms = isset($preferred['client_to_server']['mac']) ? + $preferred['client_to_server']['mac'] : + $this->getSupportedMACAlgorithms(); + $s2c_compression_algorithms = isset($preferred['server_to_client']['comp']) ? + $preferred['server_to_client']['comp'] : + $this->getSupportedCompressionAlgorithms(); + $c2s_compression_algorithms = isset($preferred['client_to_server']['comp']) ? + $preferred['client_to_server']['comp'] : + $this->getSupportedCompressionAlgorithms(); // some SSH servers have buggy implementations of some of the above algorithms - switch ($this->server_identifier) { - case 'SSH-2.0-SSHD': - $mac_algorithms = array_values(array_diff( - $mac_algorithms, - array('hmac-sha1-96', 'hmac-md5-96') - )); + switch (true) { + case $this->server_identifier == 'SSH-2.0-SSHD': + case substr($this->server_identifier, 0, 13) == 'SSH-2.0-DLINK': + if (!isset($preferred['server_to_client']['mac'])) { + $s2c_mac_algorithms = array_values(array_diff( + $s2c_mac_algorithms, + array('hmac-sha1-96', 'hmac-md5-96') + )); + } + if (!isset($preferred['client_to_server']['mac'])) { + $c2s_mac_algorithms = array_values(array_diff( + $c2s_mac_algorithms, + array('hmac-sha1-96', 'hmac-md5-96') + )); + } } $str_kex_algorithms = implode(',', $kex_algorithms); $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms); - $encryption_algorithms_server_to_client = $encryption_algorithms_client_to_server = implode(',', $encryption_algorithms); - $mac_algorithms_server_to_client = $mac_algorithms_client_to_server = implode(',', $mac_algorithms); - $compression_algorithms_server_to_client = $compression_algorithms_client_to_server = implode(',', $compression_algorithms); + $encryption_algorithms_server_to_client = implode(',', $s2c_encryption_algorithms); + $encryption_algorithms_client_to_server = implode(',', $c2s_encryption_algorithms); + $mac_algorithms_server_to_client = implode(',', $s2c_mac_algorithms); + $mac_algorithms_client_to_server = implode(',', $c2s_mac_algorithms); + $compression_algorithms_server_to_client = implode(',', $s2c_compression_algorithms); + $compression_algorithms_client_to_server = implode(',', $c2s_compression_algorithms); $client_cookie = Random::string(16); - $response = $kexinit_payload_server; - $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT) - $server_cookie = $this->_string_shift($response, 16); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); - - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); - - extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1))); - $first_kex_packet_follows = $first_kex_packet_follows != 0; - - // the sending of SSH2_MSG_KEXINIT could go in one of two places. this is the second place. $kexinit_payload_client = pack( 'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN', NET_SSH2_MSG_KEXINIT, @@ -1366,22 +1505,111 @@ class SSH2 0 ); - if (!$this->_send_binary_packet($kexinit_payload_client)) { + if ($kexinit_payload_server === false) { + if (!$this->_send_binary_packet($kexinit_payload_client)) { + return false; + } + + $kexinit_payload_server = $this->_get_binary_packet(); + if ($kexinit_payload_server === false) { + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; + } + + if (!strlen($kexinit_payload_server) || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT) { + user_error('Expected SSH_MSG_KEXINIT'); + return false; + } + + $send_kex = false; + } + + $response = $kexinit_payload_server; + $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT) + $server_cookie = $this->_string_shift($response, 16); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + if (strlen($response) < 4) { + return false; + } + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + if (!strlen($response)) { + return false; + } + extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1))); + $first_kex_packet_follows = $first_kex_packet_follows != 0; + + if ($send_kex && !$this->_send_binary_packet($kexinit_payload_client)) { return false; } - // here ends the second place. // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the // diffie-hellman key exchange as fast as possible - $decrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_server_to_client); + $decrypt = $this->_array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client); $decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt); if ($decryptKeyLength === null) { user_error('No compatible server to client encryption algorithms found'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } - $encrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_client_to_server); + $encrypt = $this->_array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server); $encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt); if ($encryptKeyLength === null) { user_error('No compatible client to server encryption algorithms found'); @@ -1389,20 +1617,52 @@ class SSH2 } // through diffie-hellman key exchange a symmetric key is obtained - $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms); + $this->kex_algorithm = $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms); if ($kex_algorithm === false) { user_error('No compatible key exchange algorithms found'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } + $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms); + if ($server_host_key_algorithm === false) { + user_error('No compatible server host key algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $mac_algorithm_in = $this->_array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client); + if ($mac_algorithm_in === false) { + user_error('No compatible server to client message authentication algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $compression_map = array( + 'none' => NET_SSH2_COMPRESSION_NONE, + 'zlib' => NET_SSH2_COMPRESSION_ZLIB, + 'zlib@openssh.com' => 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->compress = $compression_map[$compression_algorithm_out]; + + $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->decompress = $compression_map[$compression_algorithm_in]; + // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty. $exchange_hash_rfc4419 = ''; if ($kex_algorithm === 'curve25519-sha256@libssh.org') { $x = Random::string(32); - $eBytes = \Sodium\crypto_box_publickey_from_secretkey($x); - $clientKexInitMessage = NET_SSH2_MSG_KEX_ECDH_INIT; - $serverKexReplyMessage = NET_SSH2_MSG_KEX_ECDH_REPLY; + $eBytes = sodium_crypto_box_publickey_from_secretkey($x); + $clientKexInitMessage = 'NET_SSH2_MSG_KEX_ECDH_INIT'; + $serverKexReplyMessage = 'NET_SSH2_MSG_KEX_ECDH_REPLY'; $kexHash = new Hash('sha256'); } else { if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) { @@ -1420,9 +1680,11 @@ class SSH2 if (!$this->_send_binary_packet($packet)) { return false; } + $this->_updateLogHistory('UNKNOWN (34)', 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'); $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } @@ -1431,11 +1693,18 @@ class SSH2 user_error('Expected SSH_MSG_KEX_DH_GEX_GROUP'); return false; } + $this->_updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEXDH_GEX_GROUP'); + if (strlen($response) < 4) { + return false; + } extract(unpack('NprimeLength', $this->_string_shift($response, 4))); $primeBytes = $this->_string_shift($response, $primeLength); $prime = new BigInteger($primeBytes, -256); + if (strlen($response) < 4) { + return false; + } extract(unpack('NgLength', $this->_string_shift($response, 4))); $gBytes = $this->_string_shift($response, $gLength); $g = new BigInteger($gBytes, -256); @@ -1449,8 +1718,8 @@ class SSH2 $gBytes ); - $clientKexInitMessage = NET_SSH2_MSG_KEXDH_GEX_INIT; - $serverKexReplyMessage = NET_SSH2_MSG_KEXDH_GEX_REPLY; + $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_GEX_INIT'; + $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_GEX_REPLY'; } else { switch ($kex_algorithm) { // see http://tools.ietf.org/html/rfc2409#section-6.2 and @@ -1477,8 +1746,8 @@ class SSH2 // the generator field element is 2 (decimal) and the hash function is sha1. $g = new BigInteger(2); $prime = new BigInteger($prime, 16); - $clientKexInitMessage = NET_SSH2_MSG_KEXDH_INIT; - $serverKexReplyMessage = NET_SSH2_MSG_KEXDH_REPLY; + $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_INIT'; + $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_REPLY'; } switch ($kex_algorithm) { @@ -1506,37 +1775,71 @@ class SSH2 $eBytes = $e->toBytes(true); } - $data = pack('CNa*', $clientKexInitMessage, strlen($eBytes), $eBytes); + $data = pack('CNa*', constant($clientKexInitMessage), strlen($eBytes), $eBytes); if (!$this->_send_binary_packet($data)) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } + switch ($clientKexInitMessage) { + case 'NET_SSH2_MSG_KEX_ECDH_INIT': + $this->_updateLogHistory('NET_SSH2_MSG_KEXDH_INIT', 'NET_SSH2_MSG_KEX_ECDH_INIT'); + break; + case 'NET_SSH2_MSG_KEXDH_GEX_INIT': + $this->_updateLogHistory('UNKNOWN (32)', 'NET_SSH2_MSG_KEXDH_GEX_INIT'); + } $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } + if (!strlen($response)) { + return false; + } extract(unpack('Ctype', $this->_string_shift($response, 1))); - if ($type != $serverKexReplyMessage) { - user_error('Expected SSH_MSG_KEXDH_REPLY'); + if ($type != constant($serverKexReplyMessage)) { + user_error("Expected $serverKexReplyMessage"); return false; } + switch ($serverKexReplyMessage) { + case 'NET_SSH2_MSG_KEX_ECDH_REPLY': + $this->_updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEX_ECDH_REPLY'); + break; + case 'NET_SSH2_MSG_KEXDH_GEX_REPLY': + $this->_updateLogHistory('UNKNOWN (33)', 'NET_SSH2_MSG_KEXDH_GEX_REPLY'); + } + if (strlen($response) < 4) { + return false; + } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']); + if (strlen($server_public_host_key) < 4) { + return false; + } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']); + if (strlen($response) < 4) { + return false; + } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $fBytes = $this->_string_shift($response, $temp['length']); + if (strlen($response) < 4) { + return false; + } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->signature = $this->_string_shift($response, $temp['length']); + if (strlen($this->signature) < 4) { + return false; + } $temp = unpack('Nlength', $this->_string_shift($this->signature, 4)); $this->signature_format = $this->_string_shift($this->signature, $temp['length']); @@ -1545,8 +1848,15 @@ class SSH2 user_error('Received curve25519 public key of invalid length.'); return false; } - $key = new BigInteger(\Sodium\crypto_scalarmult($x, $fBytes), 256); - \Sodium\memzero($x); + $key = new BigInteger(sodium_crypto_scalarmult($x, $fBytes), 256); + // sodium_compat doesn't emulate sodium_memzero + // also, with v1 of libsodium API the extension identifies itself as + // libsodium whereas v2 of the libsodium API (what PHP 7.2+ includes) + // identifies itself as sodium. sodium_compat uses the v1 API to + // emulate the v2 API if it's the v1 API that's available + if (extension_loaded('sodium') || extension_loaded('libsodium')) { + sodium_memzero($x); + } } else { $f = new BigInteger($fBytes, -256); $key = $f->modPow($x, $prime); @@ -1580,15 +1890,25 @@ class SSH2 $this->session_id = $this->exchange_hash; } - $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms); - if ($server_host_key_algorithm === false) { - user_error('No compatible server host key algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + switch ($server_host_key_algorithm) { + case 'ssh-dss': + $expected_key_format = 'ssh-dss'; + break; + //case 'rsa-sha2-256': + //case 'rsa-sha2-512': + //case 'ssh-rsa': + default: + $expected_key_format = 'ssh-rsa'; } - if ($public_key_format != $server_host_key_algorithm || $this->signature_format != $server_host_key_algorithm) { - user_error('Server Host Key Algorithm Mismatch'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) { + switch (true) { + case $this->signature_format == $server_host_key_algorithm: + case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512': + case $this->signature_format != 'ssh-rsa': + user_error('Server Host Key Algorithm Mismatch'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } } $packet = pack( @@ -1603,10 +1923,14 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } + if (!strlen($response)) { + return false; + } extract(unpack('Ctype', $this->_string_shift($response, 1))); if ($type != NET_SSH2_MSG_NEWKEYS) { @@ -1619,7 +1943,7 @@ class SSH2 $this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt); if ($this->encrypt) { if ($this->crypto_engine) { - $this->encrypt->setEngine($this->crypto_engine); + $this->encrypt->setPreferredEngine($this->crypto_engine); } if ($this->encrypt->block_size) { $this->encrypt_block_size = $this->encrypt->block_size; @@ -1627,6 +1951,10 @@ class SSH2 $this->encrypt->enableContinuousBuffer(); $this->encrypt->disablePadding(); + if ($this->encrypt->getBlockLength()) { + $this->encrypt_block_size = $this->encrypt->getBlockLength() >> 3; + } + $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id); while ($this->encrypt_block_size > strlen($iv)) { $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); @@ -1638,12 +1966,14 @@ class SSH2 $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->encrypt->setKey(substr($key, 0, $encryptKeyLength)); + + $this->encrypt->name = $decrypt; } $this->decrypt = $this->_encryption_algorithm_to_crypt_instance($decrypt); if ($this->decrypt) { if ($this->crypto_engine) { - $this->decrypt->setEngine($this->crypto_engine); + $this->decrypt->setPreferredEngine($this->crypto_engine); } if ($this->decrypt->block_size) { $this->decrypt_block_size = $this->decrypt->block_size; @@ -1651,6 +1981,10 @@ class SSH2 $this->decrypt->enableContinuousBuffer(); $this->decrypt->disablePadding(); + if ($this->decrypt->getBlockLength()) { + $this->decrypt_block_size = $this->decrypt->getBlockLength() >> 3; + } + $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id); while ($this->decrypt_block_size > strlen($iv)) { $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); @@ -1662,6 +1996,8 @@ class SSH2 $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key); } $this->decrypt->setKey(substr($key, 0, $decryptKeyLength)); + + $this->decrypt->name = $decrypt; } /* The "arcfour128" algorithm is the RC4 cipher, as described in @@ -1678,14 +2014,14 @@ class SSH2 $this->decrypt->decrypt(str_repeat("\0", 1536)); } - $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_client_to_server); - if ($mac_algorithm === false) { + $mac_algorithm_out = $this->_array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server); + if ($mac_algorithm_out === false) { user_error('No compatible client to server message authentication algorithms found'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } $createKeyLength = 0; // ie. $mac_algorithm == 'none' - switch ($mac_algorithm) { + switch ($mac_algorithm_out) { case 'hmac-sha2-256': $this->hmac_create = new Hash('sha256'); $createKeyLength = 32; @@ -1706,16 +2042,11 @@ class SSH2 $this->hmac_create = new Hash('md5-96'); $createKeyLength = 16; } - - $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_server_to_client); - if ($mac_algorithm === false) { - user_error('No compatible server to client message authentication algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - } + $this->hmac_create->name = $mac_algorithm_out; $checkKeyLength = 0; $this->hmac_size = 0; - switch ($mac_algorithm) { + switch ($mac_algorithm_in) { case 'hmac-sha2-256': $this->hmac_check = new Hash('sha256'); $checkKeyLength = 32; @@ -1741,6 +2072,7 @@ class SSH2 $checkKeyLength = 16; $this->hmac_size = 12; } + $this->hmac_check->name = $mac_algorithm_in; $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id); while ($createKeyLength > strlen($key)) { @@ -1754,19 +2086,7 @@ class SSH2 } $this->hmac_check->setKey(substr($key, 0, $checkKeyLength)); - $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_server_to_client); - if ($compression_algorithm === false) { - user_error('No compatible server to client compression algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - } - $this->decompress = $compression_algorithm == 'zlib'; - - $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_client_to_server); - if ($compression_algorithm === false) { - user_error('No compatible client to server compression algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - } - $this->compress = $compression_algorithm == 'zlib'; + $this->regenerate_compression_context = $this->regenerate_decompression_context = true; return true; } @@ -1780,6 +2100,10 @@ class SSH2 */ function _encryption_algorithm_to_key_size($algorithm) { + if ($this->bad_key_size_fix && $this->_bad_algorithm_candidate($algorithm)) { + return 16; + } + switch ($algorithm) { case 'none': return 0; @@ -1854,14 +2178,33 @@ class SSH2 return null; } + /** + * Tests whether or not proposed algorithm has a potential for issues + * + * @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html + * @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291 + * @param string $algorithm Name of the encryption algorithm + * @return bool + * @access private + */ + function _bad_algorithm_candidate($algorithm) + { + switch ($algorithm) { + case 'arcfour256': + case 'aes192-ctr': + case 'aes256-ctr': + return true; + } + + return false; + } + /** * Login * * The $password parameter can be a plaintext password, a \phpseclib\Crypt\RSA object or an array * * @param string $username - * @param mixed $password - * @param mixed $... * @return bool * @see self::_login() * @access public @@ -1869,6 +2212,18 @@ class SSH2 function login($username) { $args = func_get_args(); + $this->auth[] = $args; + + // try logging with 'none' as an authentication method first since that's what + // PuTTY does + if (substr($this->server_identifier, 0, 15) != 'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue === null) { + if ($this->_login($username)) { + return true; + } + if (count($args) == 1) { + return false; + } + } return call_user_func_array(array(&$this, '_login'), $args); } @@ -1876,8 +2231,6 @@ class SSH2 * Login Helper * * @param string $username - * @param mixed $password - * @param mixed $... * @return bool * @see self::_login_helper() * @access private @@ -1933,10 +2286,21 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { + if ($this->retry_connect) { + $this->retry_connect = false; + if (!$this->_connect()) { + return false; + } + return $this->_login_helper($username, $password); + } + $this->bitmap = 0; user_error('Connection closed by server'); return false; } + if (strlen($response) < 4) { + return false; + } extract(unpack('Ctype', $this->_string_shift($response, 1))); if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) { @@ -1982,17 +2346,23 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } + if (!strlen($response)) { + return false; + } extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_USERAUTH_SUCCESS: $this->bitmap |= self::MASK_LOGIN; return true; - //case NET_SSH2_MSG_USERAUTH_FAILURE: + case NET_SSH2_MSG_USERAUTH_FAILURE: + extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); + $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); default: return false; } @@ -2037,25 +2407,37 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } + if (!strlen($response)) { + return false; + } extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed - if (defined('NET_SSH2_LOGGING')) { - $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'; + $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'); + if (strlen($response) < 4) { + return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); - $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length)); + $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $this->_string_shift($response, $length); return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); case NET_SSH2_MSG_USERAUTH_FAILURE: // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees // multi-factor authentication + if (strlen($response) < 4) { + return false; + } extract(unpack('Nlength', $this->_string_shift($response, 4))); $auth_methods = explode(',', $this->_string_shift($response, $length)); + $this->auth_methods_to_continue = $auth_methods; + if (!strlen($response)) { + return false; + } extract(unpack('Cpartial_success', $this->_string_shift($response, 1))); $partial_success = $partial_success != 0; @@ -2112,7 +2494,6 @@ class SSH2 /** * Handle the keyboard-interactive requests / responses. * - * @param string $responses... * @return bool * @access private */ @@ -2125,21 +2506,37 @@ class SSH2 } else { $orig = $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } } + if (!strlen($response)) { + return false; + } extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_USERAUTH_INFO_REQUEST: + if (strlen($response) < 4) { + return false; + } extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->_string_shift($response, $length); // name; may be empty + if (strlen($response) < 4) { + return false; + } extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->_string_shift($response, $length); // instruction; may be empty + if (strlen($response) < 4) { + return false; + } extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->_string_shift($response, $length); // language tag; may be empty + if (strlen($response) < 4) { + return false; + } extract(unpack('Nnum_prompts', $this->_string_shift($response, 4))); for ($i = 0; $i < count($responses); $i++) { @@ -2154,6 +2551,9 @@ class SSH2 if (isset($this->keyboard_requests_responses)) { for ($i = 0; $i < $num_prompts; $i++) { + if (strlen($response) < 4) { + return false; + } extract(unpack('Nlength', $this->_string_shift($response, 4))); // prompt - ie. "Password: "; must not be empty $prompt = $this->_string_shift($response, $length); @@ -2170,12 +2570,8 @@ class SSH2 // see http://tools.ietf.org/html/rfc4256#section-3.2 if (strlen($this->last_interactive_response)) { $this->last_interactive_response = ''; - } elseif (defined('NET_SSH2_LOGGING')) { - $this->message_number_log[count($this->message_number_log) - 1] = str_replace( - 'UNKNOWN', - 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', - $this->message_number_log[count($this->message_number_log) - 1] - ); + } else { + $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST'); } if (!count($responses) && $num_prompts) { @@ -2198,13 +2594,7 @@ class SSH2 return false; } - if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) { - $this->message_number_log[count($this->message_number_log) - 1] = str_replace( - 'UNKNOWN', - 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE', - $this->message_number_log[count($this->message_number_log) - 1] - ); - } + $this->_updateLogHistory('UNKNOWN (61)', 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'); /* After receiving the response, the server MUST send either an @@ -2217,6 +2607,8 @@ class SSH2 case NET_SSH2_MSG_USERAUTH_SUCCESS: return true; case NET_SSH2_MSG_USERAUTH_FAILURE: + extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); + $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); return false; } @@ -2248,7 +2640,7 @@ class SSH2 * Login with an RSA private key * * @param string $username - * @param \phpseclib\Crypt\RSA $password + * @param \phpseclib\Crypt\RSA $privatekey * @return bool * @access private * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} @@ -2276,6 +2668,21 @@ class SSH2 $publickey['n'] ); + switch ($this->signature_format) { + case 'rsa-sha2-512': + $hash = 'sha512'; + $signatureType = 'rsa-sha2-512'; + break; + case 'rsa-sha2-256': + $hash = 'sha256'; + $signatureType = 'rsa-sha2-256'; + break; + //case 'ssh-rsa': + default: + $hash = 'sha1'; + $signatureType = 'ssh-rsa'; + } + $part1 = pack( 'CNa*Na*Na*', NET_SSH2_MSG_USERAUTH_REQUEST, @@ -2286,7 +2693,7 @@ class SSH2 strlen('publickey'), 'publickey' ); - $part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey); + $part2 = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($publickey), $publickey); $packet = $part1 . chr(0) . $part2; if (!$this->_send_binary_packet($packet)) { @@ -2295,33 +2702,43 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } + if (!strlen($response)) { + return false; + } extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_USERAUTH_FAILURE: - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length); + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); + $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); + $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE'; return false; case NET_SSH2_MSG_USERAUTH_PK_OK: // we'll just take it on faith that the public key blob and the public key algorithm name are as // they should be - if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) { - $this->message_number_log[count($this->message_number_log) - 1] = str_replace( - 'UNKNOWN', - 'NET_SSH2_MSG_USERAUTH_PK_OK', - $this->message_number_log[count($this->message_number_log) - 1] - ); - } + $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PK_OK'); + break; + case NET_SSH2_MSG_USERAUTH_SUCCESS: + $this->bitmap |= self::MASK_LOGIN; + return true; + default: + user_error('Unexpected response to publickey authentication pt 1'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } $packet = $part1 . chr(1) . $part2; $privatekey->setSignatureMode(RSA::SIGNATURE_PKCS1); + $privatekey->setHash($hash); $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet)); - $signature = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($signature), $signature); + $signature = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($signature), $signature); $packet.= pack('Na*', strlen($signature), $signature); if (!$this->_send_binary_packet($packet)) { @@ -2330,22 +2747,29 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } + if (!strlen($response)) { + return false; + } extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_USERAUTH_FAILURE: // either the login is bad or the server employs multi-factor authentication + extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); + $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); return false; case NET_SSH2_MSG_USERAUTH_SUCCESS: $this->bitmap |= self::MASK_LOGIN; return true; } - return false; + user_error('Unexpected response to publickey authentication pt 2'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } /** @@ -2362,6 +2786,19 @@ class SSH2 $this->timeout = $this->curTimeout = $timeout; } + /** + * Set Keep Alive + * + * Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive non-zero number. + * + * @param int $interval + * @access public + */ + function setKeepAlive($interval) + { + $this->keepAlive = $interval; + } + /** * Get the output from stdError * @@ -2389,7 +2826,12 @@ class SSH2 $this->is_timeout = false; $this->stdErrorLog = ''; - if (!($this->bitmap & self::MASK_LOGIN)) { + if (!$this->isAuthenticated()) { + return false; + } + + if ($this->in_request_pty_exec) { + user_error('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.'); return false; } @@ -2446,22 +2888,12 @@ class SSH2 return false; } - $response = $this->_get_binary_packet(); - if ($response === false) { - user_error('Connection closed by server'); - 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; } @@ -2582,22 +3014,11 @@ class SSH2 return false; } - $response = $this->_get_binary_packet(); - if ($response === false) { - user_error('Connection closed by server'); - return false; - } + $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST; - list(, $type) = unpack('C', $this->_string_shift($response, 1)); - - switch ($type) { - case NET_SSH2_MSG_CHANNEL_SUCCESS: - // if a pty can't be opened maybe commands can still be executed - case NET_SSH2_MSG_CHANNEL_FAILURE: - break; - default: - user_error('Unable to request pseudo-terminal'); - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + 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( @@ -2612,8 +3033,6 @@ class SSH2 return false; } - $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST; - $response = $this->_get_channel_packet(self::CHANNEL_SHELL); if ($response === false) { return false; @@ -2673,7 +3092,7 @@ class SSH2 * @see self::write() * @param string $expect * @param int $mode - * @return string + * @return string|bool * @access public */ function read($expect = '', $mode = self::READ_SIMPLE) @@ -2681,7 +3100,7 @@ class SSH2 $this->curTimeout = $this->timeout; $this->is_timeout = false; - if (!($this->bitmap & self::MASK_LOGIN)) { + if (!$this->isAuthenticated()) { user_error('Operation disallowed prior to login()'); return false; } @@ -2693,6 +3112,10 @@ class SSH2 $channel = $this->_get_interactive_channel(); + if ($mode == self::READ_NEXT) { + return $this->_get_channel_packet($channel); + } + $match = $expect; while (true) { if ($mode == self::READ_REGEX) { @@ -2723,7 +3146,7 @@ class SSH2 */ function write($cmd) { - if (!($this->bitmap & self::MASK_LOGIN)) { + if (!$this->isAuthenticated()) { user_error('Operation disallowed prior to login()'); return false; } @@ -2891,6 +3314,87 @@ class SSH2 return (bool) ($this->bitmap & self::MASK_LOGIN); } + /** + * Pings a server connection, or tries to reconnect if the connection has gone down + * + * Inspired by http://php.net/manual/en/mysqli.ping.php + * + * @return bool + * @access public + */ + function ping() + { + if (!$this->isAuthenticated()) { + if (!empty($this->auth)) { + return $this->_reconnect(); + } + return false; + } + + $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size; + $packet_size = 0x4000; + $packet = pack( + 'CNa*N3', + NET_SSH2_MSG_CHANNEL_OPEN, + strlen('session'), + 'session', + self::CHANNEL_KEEP_ALIVE, + $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE], + $packet_size + ); + + if (!@$this->_send_binary_packet($packet)) { + return $this->_reconnect(); + } + + $this->channel_status[self::CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN; + + $response = @$this->_get_channel_packet(self::CHANNEL_KEEP_ALIVE); + if ($response !== false) { + $this->_close_channel(self::CHANNEL_KEEP_ALIVE); + return true; + } + + return $this->_reconnect(); + } + + /** + * In situ reconnect method + * + * @return boolean + * @access private + */ + function _reconnect() + { + $this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST); + $this->retry_connect = true; + if (!$this->_connect()) { + return false; + } + foreach ($this->auth as $auth) { + $result = call_user_func_array(array(&$this, 'login'), $auth); + } + return $result; + } + + /** + * Resets a connection for re-use + * + * @param int $reason + * @access private + */ + function _reset_connection($reason) + { + $this->_disconnect($reason); + $this->decrypt = $this->encrypt = false; + $this->decrypt_block_size = $this->encrypt_block_size = 8; + $this->hmac_check = $this->hmac_create = false; + $this->hmac_size = false; + $this->session_id = false; + $this->retry_connect = true; + $this->get_seq_no = $this->send_seq_no = 0; + } + /** * Gets Binary Packets * @@ -2900,11 +3404,59 @@ class SSH2 * @return string * @access private */ - function _get_binary_packet() + function _get_binary_packet($skip_channel_filter = false) { + if ($skip_channel_filter) { + $read = array($this->fsock); + $write = $except = null; + + if (!$this->curTimeout) { + if ($this->keepAlive <= 0) { + @stream_select($read, $write, $except, null); + } else { + if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); + return $this->_get_binary_packet(true); + } + } + } else { + if ($this->curTimeout < 0) { + $this->is_timeout = true; + return true; + } + + $read = array($this->fsock); + $write = $except = null; + + $start = microtime(true); + + if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) { + if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); + $elapsed = microtime(true) - $start; + $this->curTimeout-= $elapsed; + return $this->_get_binary_packet(true); + } + $elapsed = microtime(true) - $start; + $this->curTimeout-= $elapsed; + } + + $sec = floor($this->curTimeout); + $usec = 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)) { + $this->is_timeout = true; + return true; + } + $elapsed = microtime(true) - $start; + $this->curTimeout-= $elapsed; + } + } + if (!is_resource($this->fsock) || feof($this->fsock)) { - user_error('Connection closed prematurely'); $this->bitmap = 0; + user_error('Connection closed (by server) prematurely ' . $elapsed . 's'); return false; } @@ -2923,6 +3475,9 @@ class SSH2 return false; } + if (strlen($raw) < 5) { + return false; + } extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5))); $remaining_length = $packet_length + 4 - $this->decrypt_block_size; @@ -2931,6 +3486,11 @@ class SSH2 // "implementations SHOULD check that the packet length is reasonable" // PuTTY uses 0x9000 as the actual max packet size and so to shall we if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) { + if (!$this->bad_key_size_fix && $this->_bad_algorithm_candidate($this->decrypt->name) && !($this->bitmap & SSH2::MASK_LOGIN)) { + $this->bad_key_size_fix = true; + $this->_reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + return false; + } user_error('Invalid size'); return false; } @@ -2939,13 +3499,14 @@ class SSH2 while ($remaining_length > 0) { $temp = stream_get_contents($this->fsock, $remaining_length); if ($temp === false || feof($this->fsock)) { - user_error('Error reading from socket'); $this->bitmap = 0; + user_error('Error reading from socket'); return false; } $buffer.= $temp; $remaining_length-= strlen($temp); } + $stop = microtime(true); if (strlen($buffer)) { $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer; @@ -2957,8 +3518,8 @@ class SSH2 if ($this->hmac_check !== false) { $hmac = stream_get_contents($this->fsock, $this->hmac_size); if ($hmac === false || strlen($hmac) != $this->hmac_size) { - user_error('Error reading socket'); $this->bitmap = 0; + user_error('Error reading socket'); return false; } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) { user_error('Invalid HMAC'); @@ -2966,9 +3527,41 @@ class SSH2 } } - //if ($this->decompress) { - // $payload = gzinflate(substr($payload, 2)); - //} + switch ($this->decompress) { + case NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH: + if (!$this->isAuthenticated()) { + break; + } + case 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, ['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++; @@ -2981,7 +3574,7 @@ class SSH2 $this->last_packet = $current; } - return $this->_filter($payload); + return $this->_filter($payload, $skip_channel_filter); } /** @@ -2993,48 +3586,86 @@ class SSH2 * @return string * @access private */ - function _filter($payload) + function _filter($payload, $skip_channel_filter) { switch (ord($payload[0])) { case NET_SSH2_MSG_DISCONNECT: $this->_string_shift($payload, 1); + if (strlen($payload) < 8) { + return false; + } extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8))); - $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length)); + $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . $this->_string_shift($payload, $length); $this->bitmap = 0; return false; case NET_SSH2_MSG_IGNORE: - $payload = $this->_get_binary_packet(); + $payload = $this->_get_binary_packet($skip_channel_filter); break; case NET_SSH2_MSG_DEBUG: $this->_string_shift($payload, 2); + if (strlen($payload) < 4) { + return false; + } extract(unpack('Nlength', $this->_string_shift($payload, 4))); - $this->errors[] = 'SSH_MSG_DEBUG: ' . utf8_decode($this->_string_shift($payload, $length)); - $payload = $this->_get_binary_packet(); + $this->errors[] = 'SSH_MSG_DEBUG: ' . $this->_string_shift($payload, $length); + $payload = $this->_get_binary_packet($skip_channel_filter); break; case NET_SSH2_MSG_UNIMPLEMENTED: return false; case NET_SSH2_MSG_KEXINIT: if ($this->session_id !== false) { + $this->send_kex_first = false; if (!$this->_key_exchange($payload)) { $this->bitmap = 0; return false; } - $payload = $this->_get_binary_packet(); + $payload = $this->_get_binary_packet($skip_channel_filter); } } // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in - if (($this->bitmap & self::MASK_CONNECTED) && !($this->bitmap & self::MASK_LOGIN) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) { + if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) { $this->_string_shift($payload, 1); + if (strlen($payload) < 4) { + return false; + } extract(unpack('Nlength', $this->_string_shift($payload, 4))); - $this->banner_message = utf8_decode($this->_string_shift($payload, $length)); + $this->banner_message = $this->_string_shift($payload, $length); $payload = $this->_get_binary_packet(); } // only called when we've already logged in - if (($this->bitmap & self::MASK_CONNECTED) && ($this->bitmap & self::MASK_LOGIN)) { + if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) { + if (is_bool($payload)) { + return $payload; + } + switch (ord($payload[0])) { + case NET_SSH2_MSG_CHANNEL_REQUEST: + if (strlen($payload) == 31) { + extract(unpack('cpacket_type/Nchannel/Nlength', $payload)); + if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) { + if (ord(substr($payload, 9 + $length))) { // want reply + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel])); + } + $payload = $this->_get_binary_packet($skip_channel_filter); + } + } + break; + case NET_SSH2_MSG_CHANNEL_DATA: + case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: + case NET_SSH2_MSG_CHANNEL_CLOSE: + case NET_SSH2_MSG_CHANNEL_EOF: + if (!$skip_channel_filter && !empty($this->server_channels)) { + $this->binary_packet_buffer = $payload; + $this->_get_channel_packet(true); + $payload = $this->_get_binary_packet(); + } + break; case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4 + if (strlen($payload) < 4) { + return false; + } extract(unpack('Nlength', $this->_string_shift($payload, 4))); $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . $this->_string_shift($payload, $length); @@ -3042,12 +3673,18 @@ class SSH2 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } - $payload = $this->_get_binary_packet(); + $payload = $this->_get_binary_packet($skip_channel_filter); break; case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1 $this->_string_shift($payload, 1); + if (strlen($payload) < 4) { + return false; + } extract(unpack('Nlength', $this->_string_shift($payload, 4))); $data = $this->_string_shift($payload, $length); + if (strlen($payload) < 4) { + return false; + } extract(unpack('Nserver_channel', $this->_string_shift($payload, 4))); switch ($data) { case 'auth-agent': @@ -3055,6 +3692,9 @@ class SSH2 if (isset($this->agent)) { $new_channel = self::CHANNEL_AGENT_FORWARD; + if (strlen($payload) < 8) { + return false; + } extract(unpack('Nremote_window_size', $this->_string_shift($payload, 4))); extract(unpack('Nremote_maximum_packet_size', $this->_string_shift($payload, 4))); @@ -3096,15 +3736,18 @@ class SSH2 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } } - $payload = $this->_get_binary_packet(); + $payload = $this->_get_binary_packet($skip_channel_filter); break; case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST: $this->_string_shift($payload, 1); + if (strlen($payload) < 8) { + return false; + } extract(unpack('Nchannel', $this->_string_shift($payload, 4))); extract(unpack('Nwindow_size', $this->_string_shift($payload, 4))); $this->window_size_client_to_server[$channel]+= $window_size; - $payload = ($this->bitmap & self::MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet(); + $payload = ($this->bitmap & self::MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet($skip_channel_filter); } } @@ -3165,6 +3808,10 @@ class SSH2 */ function disablePTY() { + if ($this->in_request_pty_exec) { + $this->_close_channel(self::CHANNEL_EXEC); + $this->in_request_pty_exec = false; + } $this->request_pty = false; } @@ -3186,52 +3833,60 @@ class SSH2 * * Returns the data as a string if it's available and false if not. * - * @param $client_channel - * @return mixed + * @param int $client_channel + * @param bool $skip_extended + * @return mixed|bool * @access private */ 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) { - if ($this->curTimeout) { - if ($this->curTimeout < 0) { - $this->is_timeout = true; + if ($this->binary_packet_buffer !== false) { + $response = $this->binary_packet_buffer; + $this->binary_packet_buffer = false; + } else { + $response = $this->_get_binary_packet(true); + if ($response === true && $this->is_timeout) { + if ($client_channel == self::CHANNEL_EXEC && !$this->request_pty) { + $this->_close_channel($client_channel); + } return true; } - - $read = array($this->fsock); - $write = $except = null; - - $start = microtime(true); - $sec = floor($this->curTimeout); - $usec = 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)) { - $this->is_timeout = true; - return true; + if ($response === false) { + $this->bitmap = 0; + user_error('Connection closed by server'); + return false; } - $elapsed = microtime(true) - $start; - $this->curTimeout-= $elapsed; } - $response = $this->_get_binary_packet(); - if ($response === false) { - user_error('Connection closed by server'); - return false; - } if ($client_channel == -1 && $response === true) { return true; } if (!strlen($response)) { - return ''; + return false; } - extract(unpack('Ctype', $this->_string_shift($response, 1))); + if (strlen($response) < 4) { + return false; + } if ($type == NET_SSH2_MSG_CHANNEL_OPEN) { extract(unpack('Nlength', $this->_string_shift($response, 4))); } else { @@ -3244,34 +3899,123 @@ class SSH2 // resize the window, if appropriate if ($this->window_size_server_to_client[$channel] < 0) { - $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_size); + // PuTTY does something more analogous to the following: + //if ($this->window_size_server_to_client[$channel] < 0x3FFFFFFF) { + $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_resize); if (!$this->_send_binary_packet($packet)) { return false; } - $this->window_size_server_to_client[$channel]+= $this->window_size; + $this->window_size_server_to_client[$channel]+= $this->window_resize; + } + + switch ($type) { + case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: + /* + if ($client_channel == self::CHANNEL_EXEC) { + $this->_send_channel_packet($client_channel, chr(0)); + } + */ + // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR + if (strlen($response) < 8) { + return false; + } + extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8))); + $data = $this->_string_shift($response, $length); + $this->stdErrorLog.= $data; + if ($skip_extended || $this->quiet_mode) { + continue 2; + } + if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) { + return $data; + } + $this->channel_buffers[$channel][] = chr($type) . $data; + + continue 2; + case NET_SSH2_MSG_CHANNEL_REQUEST: + if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) { + continue 2; + } + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $value = $this->_string_shift($response, $length); + switch ($value) { + case 'exit-signal': + $this->_string_shift($response, 1); + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length); + $this->_string_shift($response, 1); + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + if ($length) { + $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length); + } + + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); + + $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF; + + continue 3; + case 'exit-status': + if (strlen($response) < 5) { + return false; + } + extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5))); + $this->exit_status = $exit_status; + + // "The client MAY ignore these messages." + // -- http://tools.ietf.org/html/rfc4254#section-6.10 + + continue 3; + default: + // "Some systems may not implement signals, in which case they SHOULD ignore this message." + // -- http://tools.ietf.org/html/rfc4254#section-6.9 + continue 3; + } } switch ($this->channel_status[$channel]) { case NET_SSH2_MSG_CHANNEL_OPEN: switch ($type) { case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: + if (strlen($response) < 4) { + return false; + } extract(unpack('Nserver_channel', $this->_string_shift($response, 4))); $this->server_channels[$channel] = $server_channel; + if (strlen($response) < 4) { + return false; + } extract(unpack('Nwindow_size', $this->_string_shift($response, 4))); if ($window_size < 0) { $window_size&= 0x7FFFFFFF; $window_size+= 0x80000000; } $this->window_size_client_to_server[$channel] = $window_size; + if (strlen($response) < 4) { + return false; + } $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4)); $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server']; $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); + 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: @@ -3280,6 +4024,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); @@ -3302,6 +4054,9 @@ class SSH2 $this->_send_channel_packet($channel, chr(0)); } */ + if (strlen($response) < 4) { + return false; + } extract(unpack('Nlength', $this->_string_shift($response, 4))); $data = $this->_string_shift($response, $length); @@ -3316,68 +4071,10 @@ class SSH2 if ($client_channel == $channel) { return $data; } - if (!isset($this->channel_buffers[$channel])) { - $this->channel_buffers[$channel] = array(); - } - $this->channel_buffers[$channel][] = $data; - break; - case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: - /* - if ($client_channel == self::CHANNEL_EXEC) { - $this->_send_channel_packet($client_channel, chr(0)); - } - */ - // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR - extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8))); - $data = $this->_string_shift($response, $length); - $this->stdErrorLog.= $data; - if ($skip_extended || $this->quiet_mode) { - break; - } - if ($client_channel == $channel) { - return $data; - } - if (!isset($this->channel_buffers[$channel])) { - $this->channel_buffers[$channel] = array(); - } - $this->channel_buffers[$channel][] = $data; - break; - case NET_SSH2_MSG_CHANNEL_REQUEST: - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $value = $this->_string_shift($response, $length); - switch ($value) { - case 'exit-signal': - $this->_string_shift($response, 1); - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length); - $this->_string_shift($response, 1); - extract(unpack('Nlength', $this->_string_shift($response, 4))); - if ($length) { - $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length); - } - - $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); - $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); - - $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF; - - break; - case 'exit-status': - extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5))); - $this->exit_status = $exit_status; - - // "The client MAY ignore these messages." - // -- http://tools.ietf.org/html/rfc4254#section-6.10 - - break; - default: - // "Some systems may not implement signals, in which case they SHOULD ignore this message." - // -- http://tools.ietf.org/html/rfc4254#section-6.9 - break; - } + $this->channel_buffers[$channel][] = chr($type) . $data; break; case NET_SSH2_MSG_CHANNEL_CLOSE: - $this->curTimeout = 0; + $this->curTimeout = 5; if ($this->bitmap & self::MASK_SHELL) { $this->bitmap&= ~self::MASK_SHELL; @@ -3393,7 +4090,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); } } @@ -3413,16 +4110,32 @@ class SSH2 function _send_binary_packet($data, $logged = null) { if (!is_resource($this->fsock) || feof($this->fsock)) { - user_error('Connection closed prematurely'); $this->bitmap = 0; + user_error('Connection closed prematurely'); 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 NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH: + if (!$this->isAuthenticated()) { + break; + } + case NET_SSH2_COMPRESSION_ZLIB: + if (!$this->regenerate_compression_context) { + $header = ''; + } else { + $this->regenerate_compression_context = false; + $this->compress_context = deflate_init(ZLIB_ENCODING_RAW, ['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; @@ -3445,15 +4158,15 @@ class SSH2 $packet.= $hmac; $start = microtime(true); - $result = strlen($packet) == fputs($this->fsock, $packet); + $result = strlen($packet) == @fputs($this->fsock, $packet); $stop = microtime(true); 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; } @@ -3465,7 +4178,8 @@ class SSH2 * * Makes sure that only the last 1MB worth of packets will be logged * - * @param string $data + * @param string $message_number + * @param string $message * @access private */ function _append_log($message_number, $message) @@ -3606,11 +4320,15 @@ class SSH2 $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE; - $this->curTimeout = 0; + $this->curTimeout = 5; while (!is_bool($this->_get_channel_packet($client_channel))) { } + if ($this->is_timeout) { + $this->disconnect(); + } + if ($want_reply) { $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel])); } @@ -3632,10 +4350,14 @@ class SSH2 if ($this->bitmap & self::MASK_CONNECTED) { $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, ''); $this->_send_binary_packet($data); - $this->bitmap = 0; - fclose($this->fsock); - return false; } + + $this->bitmap = 0; + if (is_resource($this->fsock) && get_resource_type($this->fsock) == 'stream') { + fclose($this->fsock); + } + + return false; } /** @@ -3662,7 +4384,6 @@ class SSH2 * named constants from it, using the value as the name of the constant and the index as the value of the constant. * If any of the constants that would be defined already exists, none of the constants will be defined. * - * @param array $array * @access private */ function _define_array() @@ -3696,10 +4417,9 @@ class SSH2 switch (NET_SSH2_LOGGING) { case self::LOG_SIMPLE: return $this->message_number_log; - break; case self::LOG_COMPLEX: - return $this->_format_log($this->message_log, $this->message_number_log); - break; + $log = $this->_format_log($this->message_log, $this->message_number_log); + return PHP_SAPI == 'cli' ? $log : '
' . $log . '
'; default: return false; } @@ -3957,6 +4677,318 @@ class SSH2 return $this->languages_client_to_server; } + /** + * Returns a list of algorithms the server supports + * + * @return array + * @access public + */ + function getServerAlgorithms() + { + $this->_connect(); + + return array( + 'kex' => $this->kex_algorithms, + 'hostkey' => $this->server_host_key_algorithms, + 'client_to_server' => array( + 'crypt' => $this->encryption_algorithms_client_to_server, + 'mac' => $this->mac_algorithms_client_to_server, + 'comp' => $this->compression_algorithms_client_to_server, + 'lang' => $this->languages_client_to_server + ), + 'server_to_client' => array( + 'crypt' => $this->encryption_algorithms_server_to_client, + 'mac' => $this->mac_algorithms_server_to_client, + 'comp' => $this->compression_algorithms_server_to_client, + 'lang' => $this->languages_server_to_client + ) + ); + } + + /** + * Returns a list of KEX algorithms that phpseclib supports + * + * @return array + * @access public + */ + function getSupportedKEXAlgorithms() + { + $kex_algorithms = array( + // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using + // Curve25519. See doc/curve25519-sha256@libssh.org.txt in the + // libssh repository for more information. + 'curve25519-sha256@libssh.org', + + 'diffie-hellman-group-exchange-sha256',// RFC 4419 + 'diffie-hellman-group-exchange-sha1', // RFC 4419 + + // Diffie-Hellman Key Agreement (DH) using integer modulo prime + // groups. + 'diffie-hellman-group14-sha1', // REQUIRED + 'diffie-hellman-group1-sha1', // REQUIRED + ); + + if (!function_exists('sodium_crypto_box_publickey_from_secretkey')) { + $kex_algorithms = array_diff( + $kex_algorithms, + array('curve25519-sha256@libssh.org') + ); + } + + return $kex_algorithms; + } + + /** + * Returns a list of host key algorithms that phpseclib supports + * + * @return array + * @access public + */ + function getSupportedHostKeyAlgorithms() + { + return array( + 'rsa-sha2-256', // RFC 8332 + 'rsa-sha2-512', // RFC 8332 + 'ssh-rsa', // RECOMMENDED sign Raw RSA Key + 'ssh-dss' // REQUIRED sign Raw DSS Key + ); + } + + /** + * Returns a list of symmetric key algorithms that phpseclib supports + * + * @return array + * @access public + */ + function getSupportedEncryptionAlgorithms() + { + $algos = array( + // from : + 'arcfour256', + 'arcfour128', + + //'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key + + // CTR modes from : + 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key + 'aes192-ctr', // RECOMMENDED AES with 192-bit key + 'aes256-ctr', // RECOMMENDED AES with 256-bit key + + 'twofish128-ctr', // OPTIONAL Twofish in SDCTR mode, with 128-bit key + 'twofish192-ctr', // OPTIONAL Twofish with 192-bit key + 'twofish256-ctr', // OPTIONAL Twofish with 256-bit key + + 'aes128-cbc', // RECOMMENDED AES with a 128-bit key + 'aes192-cbc', // OPTIONAL AES with a 192-bit key + 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key + + 'twofish128-cbc', // OPTIONAL Twofish with a 128-bit key + 'twofish192-cbc', // OPTIONAL Twofish with a 192-bit key + 'twofish256-cbc', + 'twofish-cbc', // OPTIONAL alias for "twofish256-cbc" + // (this is being retained for historical reasons) + + 'blowfish-ctr', // OPTIONAL Blowfish in SDCTR mode + + 'blowfish-cbc', // OPTIONAL Blowfish in CBC mode + + '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode + + '3des-cbc', // REQUIRED three-key 3DES in CBC mode + + //'none' // OPTIONAL no encryption; NOT RECOMMENDED + ); + + if ($this->crypto_engine) { + $engines = array($this->crypto_engine); + } else { + $engines = array( + Base::ENGINE_OPENSSL, + Base::ENGINE_MCRYPT, + Base::ENGINE_INTERNAL + ); + } + + $ciphers = array(); + foreach ($engines as $engine) { + foreach ($algos as $algo) { + $obj = $this->_encryption_algorithm_to_crypt_instance($algo); + if ($obj instanceof Rijndael) { + $obj->setKeyLength(preg_replace('#[^\d]#', '', $algo)); + } + switch ($algo) { + case 'arcfour128': + case 'arcfour256': + if ($engine != Base::ENGINE_INTERNAL) { + continue 2; + } + } + if ($obj->isValidEngine($engine)) { + $algos = array_diff($algos, array($algo)); + $ciphers[] = $algo; + } + } + } + + return $ciphers; + } + + /** + * Returns a list of MAC algorithms that phpseclib supports + * + * @return array + * @access public + */ + function getSupportedMACAlgorithms() + { + return array( + // from : + 'hmac-sha2-256',// RECOMMENDED HMAC-SHA256 (digest length = key length = 32) + + 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20) + 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20) + 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16) + 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16) + //'none' // OPTIONAL no MAC; NOT RECOMMENDED + ); + } + + /** + * Returns a list of compression algorithms that phpseclib supports + * + * @return array + * @access public + */ + function getSupportedCompressionAlgorithms() + { + $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; + } + + /** + * Return list of negotiated algorithms + * + * Uses the same format as https://www.php.net/ssh2-methods-negotiated + * + * @return array + * @access public + */ + function getAlgorithmsNegotiated() + { + $this->_connect(); + + $compression_map = array( + NET_SSH2_COMPRESSION_NONE => 'none', + NET_SSH2_COMPRESSION_ZLIB => 'zlib', + 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' => $compression_map[$this->compress], + ), + 'server_to_client' => array( + 'crypt' => $this->decrypt->name, + 'mac' => $this->hmac_check->name, + 'comp' => $compression_map[$this->decompress], + ) + ); + } + + /** + * Accepts an associative array with up to four parameters as described at + * + * + * @param array $methods + * @access public + */ + function setPreferredAlgorithms($methods) + { + $preferred = $methods; + + if (isset($preferred['kex'])) { + $preferred['kex'] = array_intersect( + $preferred['kex'], + $this->getSupportedKEXAlgorithms() + ); + } + + if (isset($preferred['hostkey'])) { + $preferred['hostkey'] = array_intersect( + $preferred['hostkey'], + $this->getSupportedHostKeyAlgorithms() + ); + } + + $keys = array('client_to_server', 'server_to_client'); + foreach ($keys as $key) { + if (isset($preferred[$key])) { + $a = &$preferred[$key]; + if (isset($a['crypt'])) { + $a['crypt'] = array_intersect( + $a['crypt'], + $this->getSupportedEncryptionAlgorithms() + ); + } + if (isset($a['comp'])) { + $a['comp'] = array_intersect( + $a['comp'], + $this->getSupportedCompressionAlgorithms() + ); + } + if (isset($a['mac'])) { + $a['mac'] = array_intersect( + $a['mac'], + $this->getSupportedMACAlgorithms() + ); + } + } + } + + $keys = array( + 'kex', + 'hostkey', + 'client_to_server/crypt', + 'client_to_server/comp', + 'client_to_server/mac', + 'server_to_client/crypt', + 'server_to_client/comp', + 'server_to_client/mac', + ); + foreach ($keys as $key) { + $p = $preferred; + $m = $methods; + + $subkeys = explode('/', $key); + foreach ($subkeys as $subkey) { + if (!isset($p[$subkey])) { + continue 2; + } + $p = $p[$subkey]; + $m = $m[$subkey]; + } + + if (count($p) != count($m)) { + $diff = array_diff($m, $p); + $msg = count($diff) == 1 ? + ' is not a supported algorithm' : + ' are not supported algorithms'; + user_error(implode(', ', $diff) . $msg); + return false; + } + } + + $this->preferred = $preferred; + } + /** * Returns the banner message. * @@ -3991,6 +5023,9 @@ class SSH2 $signature = $this->signature; $server_public_host_key = $this->server_public_host_key; + if (strlen($server_public_host_key) < 4) { + return false; + } extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4))); $this->_string_shift($server_public_host_key, $length); @@ -4006,15 +5041,27 @@ class SSH2 case 'ssh-dss': $zero = new BigInteger(); + if (strlen($server_public_host_key) < 4) { + return false; + } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $p = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); + if (strlen($server_public_host_key) < 4) { + return false; + } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $q = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); + if (strlen($server_public_host_key) < 4) { + return false; + } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $g = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); + if (strlen($server_public_host_key) < 4) { + return false; + } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $y = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); @@ -4061,27 +5108,54 @@ class SSH2 break; case 'ssh-rsa': + case 'rsa-sha2-256': + case 'rsa-sha2-512': + if (strlen($server_public_host_key) < 4) { + return false; + } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $e = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); + if (strlen($server_public_host_key) < 4) { + return false; + } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $rawN = $this->_string_shift($server_public_host_key, $temp['length']); $n = new BigInteger($rawN, -256); $nLength = strlen(ltrim($rawN, "\0")); /* + if (strlen($signature) < 4) { + return false; + } $temp = unpack('Nlength', $this->_string_shift($signature, 4)); $signature = $this->_string_shift($signature, $temp['length']); $rsa = new RSA(); + switch ($this->signature_format) { + case 'rsa-sha2-512': + $hash = 'sha512'; + break; + case 'rsa-sha2-256': + $hash = 'sha256'; + break; + //case 'ssh-rsa': + default: + $hash = 'sha1'; + } + $rsa->setHash($hash); $rsa->setSignatureMode(RSA::SIGNATURE_PKCS1); $rsa->loadKey(array('e' => $e, 'n' => $n), RSA::PUBLIC_FORMAT_RAW); + if (!$rsa->verify($this->exchange_hash, $signature)) { user_error('Bad server signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); } */ + if (strlen($signature) < 4) { + return false; + } $temp = unpack('Nlength', $this->_string_shift($signature, 4)); $s = new BigInteger($this->_string_shift($signature, $temp['length']), 256); @@ -4099,7 +5173,30 @@ class SSH2 $s = $s->modPow($e, $n); $s = $s->toBytes(); - $h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($this->exchange_hash)); + switch ($this->signature_format) { + case 'rsa-sha2-512': + $hash = 'sha512'; + break; + case 'rsa-sha2-256': + $hash = 'sha256'; + break; + //case 'ssh-rsa': + default: + $hash = 'sha1'; + } + $hashObj = new Hash($hash); + switch ($this->signature_format) { + case 'rsa-sha2-512': + $h = pack('N5a*', 0x00305130, 0x0D060960, 0x86480165, 0x03040203, 0x05000440, $hashObj->hash($this->exchange_hash)); + break; + case 'rsa-sha2-256': + $h = pack('N5a*', 0x00303130, 0x0D060960, 0x86480165, 0x03040201, 0x05000420, $hashObj->hash($this->exchange_hash)); + break; + //case 'ssh-rsa': + default: + $hash = 'sha1'; + $h = pack('N4a*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, $hashObj->hash($this->exchange_hash)); + } $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 2 - strlen($h)) . $h; if ($s != $h) { @@ -4185,4 +5282,33 @@ class SSH2 $this->windowColumns = $columns; $this->windowRows = $rows; } + + /** + * Update packet types in log history + * + * @param string $old + * @param string $new + * @access private + */ + function _updateLogHistory($old, $new) + { + if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) { + $this->message_number_log[count($this->message_number_log) - 1] = str_replace( + $old, + $new, + $this->message_number_log[count($this->message_number_log) - 1] + ); + } + } + + /** + * Return the list of authentication methods that may productively continue authentication. + * + * @see https://tools.ietf.org/html/rfc4252#section-5.1 + * @return array|null + */ + function getAuthMethodsToContinue() + { + return $this->auth_methods_to_continue; + } } diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php index a4ff0549..2b25250b 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php @@ -43,7 +43,7 @@ use phpseclib\System\SSH\Agent\Identity; * * @package SSH\Agent * @author Jim Wigginton - * @access internal + * @access public */ class Agent { @@ -117,18 +117,20 @@ class Agent * @return \phpseclib\System\SSH\Agent * @access public */ - function __construct() + function __construct($address = null) { - switch (true) { - case isset($_SERVER['SSH_AUTH_SOCK']): - $address = $_SERVER['SSH_AUTH_SOCK']; - break; - case isset($_ENV['SSH_AUTH_SOCK']): - $address = $_ENV['SSH_AUTH_SOCK']; - break; - default: - user_error('SSH_AUTH_SOCK not found'); - return false; + if (!$address) { + switch (true) { + case isset($_SERVER['SSH_AUTH_SOCK']): + $address = $_SERVER['SSH_AUTH_SOCK']; + break; + case isset($_ENV['SSH_AUTH_SOCK']): + $address = $_ENV['SSH_AUTH_SOCK']; + break; + default: + user_error('SSH_AUTH_SOCK not found'); + return false; + } } $this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr); @@ -155,23 +157,54 @@ class Agent $packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES); if (strlen($packet) != fputs($this->fsock, $packet)) { user_error('Connection closed while requesting identities'); + return array(); } - $length = current(unpack('N', fread($this->fsock, 4))); + $temp = fread($this->fsock, 4); + if (strlen($temp) != 4) { + user_error('Connection closed while requesting identities'); + return array(); + } + $length = current(unpack('N', $temp)); $type = ord(fread($this->fsock, 1)); if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) { user_error('Unable to request identities'); + return array(); } $identities = array(); - $keyCount = current(unpack('N', fread($this->fsock, 4))); + $temp = fread($this->fsock, 4); + if (strlen($temp) != 4) { + user_error('Connection closed while requesting identities'); + return array(); + } + $keyCount = current(unpack('N', $temp)); for ($i = 0; $i < $keyCount; $i++) { - $length = current(unpack('N', fread($this->fsock, 4))); + $temp = fread($this->fsock, 4); + if (strlen($temp) != 4) { + user_error('Connection closed while requesting identities'); + return array(); + } + $length = current(unpack('N', $temp)); $key_blob = fread($this->fsock, $length); + if (strlen($key_blob) != $length) { + user_error('Connection closed while requesting identities'); + return array(); + } $key_str = 'ssh-rsa ' . base64_encode($key_blob); - $length = current(unpack('N', fread($this->fsock, 4))); + $temp = fread($this->fsock, 4); + if (strlen($temp) != 4) { + user_error('Connection closed while requesting identities'); + return array(); + } + $length = current(unpack('N', $temp)); if ($length) { - $key_str.= ' ' . fread($this->fsock, $length); + $temp = fread($this->fsock, $length); + if (strlen($temp) != $length) { + user_error('Connection closed while requesting identities'); + return array(); + } + $key_str.= ' ' . $temp; } $length = current(unpack('N', substr($key_blob, 0, 4))); $key_type = substr($key_blob, 4, $length); @@ -293,14 +326,24 @@ class Agent if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) { user_error('Connection closed attempting to forward data to SSH agent'); + return false; } $this->socket_buffer = ''; $this->expected_bytes = 0; - $agent_reply_bytes = current(unpack('N', fread($this->fsock, 4))); + $temp = fread($this->fsock, 4); + if (strlen($temp) != 4) { + user_error('Connection closed while reading data response'); + return false; + } + $agent_reply_bytes = current(unpack('N', $temp)); $agent_reply_data = fread($this->fsock, $agent_reply_bytes); + if (strlen($agent_reply_data) != $agent_reply_bytes) { + user_error('Connection closed while reading data response'); + return false; + } $agent_reply_data = current(unpack('a*', $agent_reply_data)); return pack('Na*', $agent_reply_bytes, $agent_reply_data); diff --git a/securemail/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php b/securemail/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php index b8cc6cde..68b6bfdf 100644 --- a/securemail/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php +++ b/securemail/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php @@ -32,6 +32,17 @@ use phpseclib\System\SSH\Agent; */ class Identity { + /**@+ + * Signature Flags + * + * See https://tools.ietf.org/html/draft-miller-ssh-agent-00#section-5.3 + * + * @access private + */ + const SSH_AGENT_RSA2_256 = 2; + const SSH_AGENT_RSA2_512 = 4; + /**#@-*/ + /** * Key Object * @@ -59,6 +70,16 @@ class Identity */ var $fsock; + /** + * Signature flags + * + * @var int + * @access private + * @see self::sign() + * @see self::setHash() + */ + var $flags = 0; + /** * Default Constructor. * @@ -126,6 +147,31 @@ class Identity { } + /** + * Set Hash + * + * ssh-agent doesn't support using hashes for RSA other than SHA1 + * + * @param string $hash + * @access public + */ + function setHash($hash) + { + $this->flags = 0; + switch ($hash) { + case 'sha1': + break; + case 'sha256': + $this->flags = self::SSH_AGENT_RSA2_256; + break; + case 'sha512': + $this->flags = self::SSH_AGENT_RSA2_512; + break; + default: + user_error('The only supported hashes for RSA are sha1, sha256 and sha512'); + } + } + /** * Create a signature * @@ -138,21 +184,58 @@ class Identity function sign($message) { // the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE - $packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, 0); + $packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, $this->flags); $packet = pack('Na*', strlen($packet), $packet); if (strlen($packet) != fputs($this->fsock, $packet)) { user_error('Connection closed during signing'); + return false; } - $length = current(unpack('N', fread($this->fsock, 4))); + $temp = fread($this->fsock, 4); + if (strlen($temp) != 4) { + user_error('Connection closed during signing'); + return false; + } + $length = current(unpack('N', $temp)); $type = ord(fread($this->fsock, 1)); if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) { user_error('Unable to retrieve signature'); + return false; } $signature_blob = fread($this->fsock, $length - 1); - // the only other signature format defined - ssh-dss - is the same length as ssh-rsa - // the + 12 is for the other various SSH added length fields - return substr($signature_blob, strlen('ssh-rsa') + 12); + if (strlen($signature_blob) != $length - 1) { + user_error('Connection closed during signing'); + return false; + } + $length = current(unpack('N', $this->_string_shift($signature_blob, 4))); + if ($length != strlen($signature_blob)) { + user_error('Malformed signature blob'); + } + $length = current(unpack('N', $this->_string_shift($signature_blob, 4))); + if ($length > strlen($signature_blob) + 4) { + user_error('Malformed signature blob'); + } + $type = $this->_string_shift($signature_blob, $length); + $this->_string_shift($signature_blob, 4); + + return $signature_blob; + } + + /** + * String Shift + * + * Inspired by array_shift + * + * @param string $string + * @param int $index + * @return string + * @access private + */ + function _string_shift(&$string, $index = 1) + { + $substr = substr($string, 0, $index); + $string = substr($string, $index); + return $substr; } } diff --git a/securemail/vendor/singpolyma/openpgp-php/.github/FUNDING.yml b/securemail/vendor/singpolyma/openpgp-php/.github/FUNDING.yml new file mode 100644 index 00000000..ba2e0ffe --- /dev/null +++ b/securemail/vendor/singpolyma/openpgp-php/.github/FUNDING.yml @@ -0,0 +1,3 @@ +github: singpolyma +liberapay: singpolyma +patreon: singpolyma diff --git a/securemail/vendor/singpolyma/openpgp-php/.travis.dhall b/securemail/vendor/singpolyma/openpgp-php/.travis.dhall new file mode 100644 index 00000000..06ce0354 --- /dev/null +++ b/securemail/vendor/singpolyma/openpgp-php/.travis.dhall @@ -0,0 +1,30 @@ +let Prelude = https://prelude.dhall-lang.org/v17.0.0/package.dhall +let phpseclib = \(max: Natural) -> \(filter: (Natural -> Bool)) -> + Prelude.List.map Natural Text + (\(m: Natural) -> "PHPSECLIB='2.0.${Prelude.Natural.show m}'") + (Prelude.List.filter Natural filter (Prelude.Natural.enumerate max)) +let Exclusion = { php: Text, env: Text } +in +{ + language = "php", + php = [ + "7.3", + "7.4", + "8.0" + ], + dist = "xenial", + env = [ + "PHPSECLIB='^2.0 !=2.0.8'" + ] # (phpseclib 28 (\(m: Natural) -> Prelude.Bool.not (Prelude.Natural.equal m 8)) + ), + matrix = { + exclude = Prelude.List.concatMap Text Exclusion (\(php: Text) -> + Prelude.List.map Text Exclusion (\(env: Text) -> + { php = php, env = env } + ) (phpseclib 7 (\(_: Natural) -> True)) + ) ["7.3", "7.4", "8.0"], + fast_finish = True + }, + before_script = '' + sed -i "s/\"phpseclib\/phpseclib\": \"[^\"]*/\"phpseclib\/phpseclib\": \"$PHPSECLIB/" composer.json && composer install --prefer-source'' +} diff --git a/securemail/vendor/singpolyma/openpgp-php/.travis.yml b/securemail/vendor/singpolyma/openpgp-php/.travis.yml index 3829874e..00fc4eb0 100644 --- a/securemail/vendor/singpolyma/openpgp-php/.travis.yml +++ b/securemail/vendor/singpolyma/openpgp-php/.travis.yml @@ -1,18 +1,82 @@ -language: php -php: - - 5.3 - - 5.4 - - 5.5 - - 5.6 - - 7.0 - - hhvm -# - nightly - +# Code generated by dhall-to-yaml. DO NOT EDIT. +before_script: "sed -i \"s/\\\"phpseclib\\/phpseclib\\\": \\\"[^\\\"]*/\\\"phpseclib\\/phpseclib\\\": \\\"$PHPSECLIB/\" composer.json && composer install --prefer-source" +dist: xenial env: - - PHPSECLIB="2.0.0" - - PHPSECLIB="2.0.1" - - PHPSECLIB="2.0.2" - - PHPSECLIB="2.0.3" - - PHPSECLIB="2.0.4" - -before_script: 'sed -i "s/\"phpseclib\/phpseclib\": \"[^\"]*/\"phpseclib\/phpseclib\": \"$PHPSECLIB/" composer.json && composer install --prefer-source --dev' +- "PHPSECLIB='^2.0 !=2.0.8'" +- "PHPSECLIB='2.0.0'" +- "PHPSECLIB='2.0.1'" +- "PHPSECLIB='2.0.2'" +- "PHPSECLIB='2.0.3'" +- "PHPSECLIB='2.0.4'" +- "PHPSECLIB='2.0.5'" +- "PHPSECLIB='2.0.6'" +- "PHPSECLIB='2.0.7'" +- "PHPSECLIB='2.0.9'" +- "PHPSECLIB='2.0.10'" +- "PHPSECLIB='2.0.11'" +- "PHPSECLIB='2.0.12'" +- "PHPSECLIB='2.0.13'" +- "PHPSECLIB='2.0.14'" +- "PHPSECLIB='2.0.15'" +- "PHPSECLIB='2.0.16'" +- "PHPSECLIB='2.0.17'" +- "PHPSECLIB='2.0.18'" +- "PHPSECLIB='2.0.19'" +- "PHPSECLIB='2.0.20'" +- "PHPSECLIB='2.0.21'" +- "PHPSECLIB='2.0.22'" +- "PHPSECLIB='2.0.23'" +- "PHPSECLIB='2.0.24'" +- "PHPSECLIB='2.0.25'" +- "PHPSECLIB='2.0.26'" +- "PHPSECLIB='2.0.27'" +language: php +matrix: + exclude: + - env: "PHPSECLIB='2.0.0'" + php: '7.3' + - env: "PHPSECLIB='2.0.1'" + php: '7.3' + - env: "PHPSECLIB='2.0.2'" + php: '7.3' + - env: "PHPSECLIB='2.0.3'" + php: '7.3' + - env: "PHPSECLIB='2.0.4'" + php: '7.3' + - env: "PHPSECLIB='2.0.5'" + php: '7.3' + - env: "PHPSECLIB='2.0.6'" + php: '7.3' + - env: "PHPSECLIB='2.0.0'" + php: '7.4' + - env: "PHPSECLIB='2.0.1'" + php: '7.4' + - env: "PHPSECLIB='2.0.2'" + php: '7.4' + - env: "PHPSECLIB='2.0.3'" + php: '7.4' + - env: "PHPSECLIB='2.0.4'" + php: '7.4' + - env: "PHPSECLIB='2.0.5'" + php: '7.4' + - env: "PHPSECLIB='2.0.6'" + php: '7.4' + - env: "PHPSECLIB='2.0.0'" + php: '8.0' + - env: "PHPSECLIB='2.0.1'" + php: '8.0' + - env: "PHPSECLIB='2.0.2'" + php: '8.0' + - env: "PHPSECLIB='2.0.3'" + php: '8.0' + - env: "PHPSECLIB='2.0.4'" + php: '8.0' + - env: "PHPSECLIB='2.0.5'" + php: '8.0' + - env: "PHPSECLIB='2.0.6'" + php: '8.0' + fast_finish: true +php: +- '7.3' +- '7.4' +- '8.0' diff --git a/securemail/vendor/singpolyma/openpgp-php/README.md b/securemail/vendor/singpolyma/openpgp-php/README.md index 099f520a..d371a2d2 100644 --- a/securemail/vendor/singpolyma/openpgp-php/README.md +++ b/securemail/vendor/singpolyma/openpgp-php/README.md @@ -1,11 +1,14 @@ +[![Build Status](https://travis-ci.org/singpolyma/openpgp-php.svg?branch=master)](https://travis-ci.org/singpolyma/openpgp-php) + OpenPGP.php: OpenPGP for PHP ============================ This is a pure-PHP implementation of the OpenPGP Message Format (RFC 4880). -* +* -### About OpenPGP +About OpenPGP +------------- OpenPGP is the most widely-used e-mail encryption standard in the world. It is defined by the OpenPGP Working Group of the Internet Engineering Task @@ -13,8 +16,8 @@ Force (IETF) Proposed Standard RFC 4880. The OpenPGP standard was originally derived from PGP (Pretty Good Privacy), first created by Phil Zimmermann in 1991. -* -* +* +* Features -------- @@ -22,15 +25,26 @@ Features * Encodes and decodes ASCII-armored OpenPGP messages. * Parses OpenPGP messages into their constituent packets. * Supports both old-format (PGP 2.6.x) and new-format (RFC 4880) packets. -* Helper class for verifying, signing, encrypting, and decrypting messages using Crypt_RSA from -* Helper class for encrypting and decrypting messages and keys using Crypt_AES and Crypt_TripleDES from +* Helper class for verifying, signing, encrypting, and decrypting messages +* Helper class for encrypting and decrypting messages and keys using + * openssl or mcrypt required for CAST5 encryption and decryption + +Bugs, Feature Requests, Patches +------------------------------- + +This project is primarily maintained by a single volunteer with many other +things vying for their attention, please be patient. + +Bugs, feature request, pull requests, patches, and general discussion may +be submitted publicly via email to: dev@singpolyma.net + +Github users may alternately submit on the web there. Users ----- OpenPGP.php is currently being used in the following projects: -* * Download @@ -38,21 +52,21 @@ Download To get a local working copy of the development repository, do: - % git clone git://github.com/bendiken/openpgp-php.git + git clone https://github.com/singpolyma/openpgp-php.git Alternatively, you can download the latest development version as a tarball as follows: - % wget http://github.com/bendiken/openpgp-php/tarball/master + wget https://github.com/singpolyma/openpgp-php/tarball/master Authors ------- -* [Arto Bendiken](mailto:arto.bendiken@gmail.com) - -* [Stephen Paul Weber](mailto:singpolyma@singpolyma.net) - +* [Arto Bendiken](mailto:arto.bendiken@gmail.com) (Original author) - +* [Stephen Paul Weber](mailto:singpolyma@singpolyma.net) (Maintainer) - License ------- OpenPGP.php is free and unencumbered public domain software. For more -information, see or the accompanying UNLICENSE file. +information, see or the accompanying UNLICENSE file. diff --git a/securemail/vendor/singpolyma/openpgp-php/composer.json b/securemail/vendor/singpolyma/openpgp-php/composer.json index 6acc8c46..a5968799 100644 --- a/securemail/vendor/singpolyma/openpgp-php/composer.json +++ b/securemail/vendor/singpolyma/openpgp-php/composer.json @@ -13,10 +13,14 @@ } ], "require": { - "phpseclib/phpseclib": ">=2.0.0 <=2.0.4" + "php": "^5.6 || ^7.0 || ^8.0", + "phpseclib/phpseclib": "^2.0 !=2.0.8" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "^9.0" + }, + "suggest": { + "ext-mcrypt": "required if you use encryption cast5" }, "autoload": { "classmap": ["lib/"] diff --git a/securemail/vendor/singpolyma/openpgp-php/examples/README.md b/securemail/vendor/singpolyma/openpgp-php/examples/README.md new file mode 100644 index 00000000..3f3c3b39 --- /dev/null +++ b/securemail/vendor/singpolyma/openpgp-php/examples/README.md @@ -0,0 +1,22 @@ +OpenPGP.php Examples +==================== + +The scripts in this folder show how to use this library to perform various tasks +such as [generating a new key](keygen.php), [signing a message](sign.php), and +[verifying a message](verify.php) that has been signed. + +To use these examples, make sure [`phpseclib`](http://phpseclib.sourceforge.net/) is available. You can install it +using [Composer](https://getcomposer.org/): + +```sh +git clone https://github.com/singpolyma/openpgp-php.git # Clone the repository. +cd openpgp-php +composer install # Use Composer to install the requirements. +``` + +Once Composer has installed the requirements, run the examples using PHP: + +```sh +# Generate a new OpenPGP key; see the `keygen.php` file for parameters. +php ./examples/keygen.php > mykey.gpg +``` diff --git a/securemail/vendor/singpolyma/openpgp-php/examples/armorEncryptSignCompress.php b/securemail/vendor/singpolyma/openpgp-php/examples/armorEncryptSignCompress.php new file mode 100644 index 00000000..59d5af40 --- /dev/null +++ b/securemail/vendor/singpolyma/openpgp-php/examples/armorEncryptSignCompress.php @@ -0,0 +1,21 @@ + 'u']); +$signed = $signer->sign($data); + +$compressed = new OpenPGP_CompressedDataPacket($signed); +$encrypted = OpenPGP_Crypt_Symmetric::encrypt([$recipientPublicKey, $key], new OpenPGP_Message([$compressed])); + +echo OpenPGP::enarmor($encrypted->to_bytes(), 'PGP MESSAGE'); + + diff --git a/securemail/vendor/singpolyma/openpgp-php/examples/clearsign.php b/securemail/vendor/singpolyma/openpgp-php/examples/clearsign.php index 686af02f..1445fa7a 100644 --- a/securemail/vendor/singpolyma/openpgp-php/examples/clearsign.php +++ b/securemail/vendor/singpolyma/openpgp-php/examples/clearsign.php @@ -1,5 +1,6 @@ data)."\n"; echo OpenPGP::enarmor($packets[1][0]->to_bytes(), "PGP SIGNATURE"); - -?> diff --git a/securemail/vendor/singpolyma/openpgp-php/examples/deASCIIdeCrypt.php b/securemail/vendor/singpolyma/openpgp-php/examples/deASCIIdeCrypt.php index 9ab6cdd2..0326ba6c 100644 --- a/securemail/vendor/singpolyma/openpgp-php/examples/deASCIIdeCrypt.php +++ b/securemail/vendor/singpolyma/openpgp-php/examples/deASCIIdeCrypt.php @@ -3,6 +3,7 @@ // USAGE: php examples/deASCIIdeCrypt.php secretkey.asc password message.asc // This will fail if the algo on key or message is not 3DES or AES +@include_once dirname(__FILE__).'/../vendor/autoload.php'; require_once dirname(__FILE__).'/../lib/openpgp.php'; require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php'; require_once dirname(__FILE__).'/../lib/openpgp_crypt_symmetric.php'; diff --git a/securemail/vendor/singpolyma/openpgp-php/examples/encryptDecrypt.php b/securemail/vendor/singpolyma/openpgp-php/examples/encryptDecrypt.php index 7804967f..b9c1a520 100644 --- a/securemail/vendor/singpolyma/openpgp-php/examples/encryptDecrypt.php +++ b/securemail/vendor/singpolyma/openpgp-php/examples/encryptDecrypt.php @@ -1,5 +1,6 @@ createKey(512); +$rsa->loadKey($k['privatekey']); + +$nkey = new OpenPGP_SecretKeyPacket(array( + 'n' => $rsa->modulus->toBytes(), + 'e' => $rsa->publicExponent->toBytes(), + 'd' => $rsa->exponent->toBytes(), + 'p' => $rsa->primes[2]->toBytes(), + 'q' => $rsa->primes[1]->toBytes(), + 'u' => $rsa->coefficients[2]->toBytes() +)); + +$uid = new OpenPGP_UserIDPacket('Test '); + +$wkey = new OpenPGP_Crypt_RSA($nkey); +$m = $wkey->sign_key_userid(array($nkey, $uid)); +$m[0] = OpenPGP_Crypt_Symmetric::encryptSecretKey("password", $nkey); + +// Serialize encrypted private key +print $m->to_bytes(); diff --git a/securemail/vendor/singpolyma/openpgp-php/examples/keygenSubkeys.php b/securemail/vendor/singpolyma/openpgp-php/examples/keygenSubkeys.php new file mode 100644 index 00000000..2cb12a9c --- /dev/null +++ b/securemail/vendor/singpolyma/openpgp-php/examples/keygenSubkeys.php @@ -0,0 +1,116 @@ +createKey($key_length); +$rsa->loadKey($k['privatekey']); + +$nkey = new OpenPGP_SecretKeyPacket(array( + 'n' => $rsa->modulus->toBytes(), + 'e' => $rsa->publicExponent->toBytes(), + 'd' => $rsa->exponent->toBytes(), + 'p' => $rsa->primes[2]->toBytes(), + 'q' => $rsa->primes[1]->toBytes(), + 'u' => $rsa->coefficients[2]->toBytes() +)); + +// Start assembling packets for our eventual OpenPGP_Message +$packets = array($nkey); + +$wkey = new OpenPGP_Crypt_RSA($nkey); +$fingerprint = $wkey->key()->fingerprint; +$key = $wkey->private_key(); +$key->setHash('sha256'); +$keyid = substr($fingerprint, -16); + +// Add multiple UID packets and signatures + +$uids = array( + new OpenPGP_UserIDPacket('Support', '', 'support@example.com'), + new OpenPGP_UserIDPacket('Security', '', 'security@example.com'), +); + +foreach($uids as $uid) { + // Append the UID packet + $packets[] = $uid; + + $sig = new OpenPGP_SignaturePacket(new OpenPGP_Message(array($nkey, $uid)), 'RSA', 'SHA256'); + $sig->signature_type = 0x13; + $sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_KeyFlagsPacket(array(0x01 | 0x02)); // Certify + sign bits + $sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_IssuerPacket($keyid); + $m = $wkey->sign_key_userid(array($nkey, $uid, $sig)); + + // Append the UID signature from the master key + $packets[] = $m->packets[2]; +} + +// Generate an encryption subkey + +$rsa_subkey = new \phpseclib\Crypt\RSA(); +$sub_k = $rsa_subkey->createKey($key_length); +$rsa_subkey->loadKey($sub_k['privatekey']); + +$subkey = new OpenPGP_SecretSubkeyPacket(array( + 'n' => $rsa_subkey->modulus->toBytes(), + 'e' => $rsa_subkey->publicExponent->toBytes(), + 'd' => $rsa_subkey->exponent->toBytes(), + 'p' => $rsa_subkey->primes[2]->toBytes(), + 'q' => $rsa_subkey->primes[1]->toBytes(), + 'u' => $rsa_subkey->coefficients[2]->toBytes() +)); + +// Append the encryption subkey +$packets[] = $subkey; + +$sub_wkey = new OpenPGP_Crypt_RSA($subkey); + +/* + * Sign the encryption subkey with the master key + * + * OpenPGP_SignaturePacket assumes any message starting with an + * OpenPGP_PublicKeyPacket is followed by a OpenPGP_UserIDPacket. We need + * to pass `null` in the constructor and generate the `->data` ourselves. + */ +$sub_sig = new OpenPGP_SignaturePacket(null, 'RSA', 'SHA256'); +$sub_sig->signature_type = 0x18; +$sub_sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_SignatureCreationTimePacket(time()); +$sub_sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_KeyFlagsPacket(array(0x0C)); // Encrypt bits +$sub_sig->hashed_subpackets[] = new OpenPGP_SignaturePacket_IssuerPacket($keyid); +$sub_sig->data = implode('', $nkey->fingerprint_material()) . implode('', $subkey->fingerprint_material()); +$sub_sig->sign_data(array('RSA' => array('SHA256' => function($data) use($key) {return array($key->sign($data));}))); + +// Append the subkey signature +$packets[] = $sub_sig; + +// Build the OpenPGP_Message for the secret key from our packets +$m = new OpenPGP_Message($packets); + +// Serialize the private key +print $m->to_bytes(); + +// Clone a public key message from the secret key +$pubm = clone($m); + +// Convert the private key packets to public so we only export public data +// (n+e in RSA) +foreach($pubm as $idx => $p) { + if($p instanceof OpenPGP_SecretSubkeyPacket) { + $pubm[$idx] = new OpenPGP_PublicSubkeyPacket($p); + } else if($p instanceof OpenPGP_SecretKeyPacket) { + $pubm[$idx] = new OpenPGP_PublicKeyPacket($p); + } +} + +// Serialize the public key +$public_bytes = $pubm->to_bytes(); + +// Note: If using PHP 7.4 CLI, disable deprecated warnings: +// php -d error_reporting="E_ALL & ~E_DEPRECATED" examples/keygenSubkeys.php > mykey.gpg \ No newline at end of file diff --git a/securemail/vendor/singpolyma/openpgp-php/examples/sign.php b/securemail/vendor/singpolyma/openpgp-php/examples/sign.php index 5e68d642..fcffc597 100644 --- a/securemail/vendor/singpolyma/openpgp-php/examples/sign.php +++ b/securemail/vendor/singpolyma/openpgp-php/examples/sign.php @@ -1,5 +1,6 @@ sign($data); /* Output the raw message bytes to STDOUT */ echo $m->to_bytes(); - -?> diff --git a/securemail/vendor/singpolyma/openpgp-php/examples/verify.php b/securemail/vendor/singpolyma/openpgp-php/examples/verify.php index 86501996..b5cf5b4e 100644 --- a/securemail/vendor/singpolyma/openpgp-php/examples/verify.php +++ b/securemail/vendor/singpolyma/openpgp-php/examples/verify.php @@ -1,5 +1,6 @@ verify($m)); - -?> diff --git a/securemail/vendor/singpolyma/openpgp-php/lib/openpgp.php b/securemail/vendor/singpolyma/openpgp-php/lib/openpgp.php index dc659d79..068f68f4 100644 --- a/securemail/vendor/singpolyma/openpgp-php/lib/openpgp.php +++ b/securemail/vendor/singpolyma/openpgp-php/lib/openpgp.php @@ -5,7 +5,7 @@ * (RFC 4880). * * @package OpenPGP - * @version 0.3.0 + * @version 0.5.0 * @author Arto Bendiken * @author Stephen Paul Weber * @see http://github.com/bendiken/openpgp-php @@ -18,6 +18,8 @@ * @see http://tools.ietf.org/html/rfc4880 */ class OpenPGP { + const VERSION = array(0, 5, 0); + /** * @see http://tools.ietf.org/html/rfc4880#section-6 * @see http://tools.ietf.org/html/rfc4880#section-6.2 @@ -28,7 +30,7 @@ class OpenPGP { foreach ($headers as $key => $value) { $text .= $key . ': ' . (string)$value . "\n"; } - $text .= "\n" . base64_encode($data); + $text .= "\n" . wordwrap(base64_encode($data), 76, "\n", true); $text .= "\n".'=' . base64_encode(substr(pack('N', self::crc24($data)), 1)) . "\n"; $text .= self::footer($marker) . "\n"; return $text; @@ -42,8 +44,13 @@ class OpenPGP { $header = self::header($header); $text = str_replace(array("\r\n", "\r"), array("\n", ''), $text); if (($pos1 = strpos($text, $header)) !== FALSE && - ($pos1 = strpos($text, "\n\n", $pos1 += strlen($header))) !== FALSE && - ($pos2 = strpos($text, "\n=", $pos1 += 2)) !== FALSE) { + ($pos1 = strpos($text, "\n\n", $pos1 += strlen($header))) !== FALSE) { + $pos2 = strpos($text, "\n=", $pos1 += 2); + if ($pos2 === FALSE) { + trigger_error("Invalid ASCII armor, missing CRC"); + $pos2 = strpos($text, "-----END"); + if ($pos2 === FALSE) return NULL; + } return base64_decode($text = substr($text, $pos1, $pos2 - $pos1)); } } @@ -122,20 +129,20 @@ class OpenPGP_S2K { static function parse(&$input) { $s2k = new OpenPGP_S2k(); - switch($s2k->type = ord($input{0})) { + switch($s2k->type = ord($input[0])) { case 0: - $s2k->hash_algorithm = ord($input{1}); + $s2k->hash_algorithm = ord($input[1]); $input = substr($input, 2); break; case 1: - $s2k->hash_algorithm = ord($input{1}); + $s2k->hash_algorithm = ord($input[1]); $s2k->salt = substr($input, 2, 8); $input = substr($input, 10); break; case 3: - $s2k->hash_algorithm = ord($input{1}); + $s2k->hash_algorithm = ord($input[1]); $s2k->salt = substr($input, 2, 8); - $s2k->count = OpenPGP::decode_s2k_count(ord($input{10})); + $s2k->count = OpenPGP::decode_s2k_count(ord($input[10])); $input = substr($input, 11); break; } @@ -150,10 +157,12 @@ class OpenPGP_S2K { $bytes .= chr($this->hash_algorithm); break; case 1: + if(strlen($this->salt) != 8) throw new Exception("Invalid salt length"); $bytes .= chr($this->hash_algorithm); $bytes .= $this->salt; break; case 3: + if(strlen($this->salt) != 8) throw new Exception("Invalid salt length"); $bytes .= chr($this->hash_algorithm); $bytes .= $this->salt; $bytes .= chr(OpenPGP::encode_s2k_count($this->count)); @@ -553,7 +562,7 @@ class OpenPGP_Packet { } function read_byte() { - return ($bytes = $this->read_bytes()) ? $bytes[0] : NULL; + return !is_null($bytes = $this->read_bytes()) ? $bytes[0] : NULL; } function read_bytes($count = 1) { @@ -609,7 +618,7 @@ class OpenPGP_AsymmetricSessionKeyPacket extends OpenPGP_Packet { $rawkeyid = $this->read_bytes(8); $this->keyid = ''; for($i = 0; $i < strlen($rawkeyid); $i++) { // Store KeyID in Hex - $this->keyid .= sprintf('%02X',ord($rawkeyid{$i})); + $this->keyid .= sprintf('%02X',ord($rawkeyid[$i])); } $this->key_algorithm = ord($this->read_byte()); @@ -625,7 +634,7 @@ class OpenPGP_AsymmetricSessionKeyPacket extends OpenPGP_Packet { $bytes = chr($this->version); for($i = 0; $i < strlen($this->keyid); $i += 2) { - $bytes .= chr(hexdec($this->keyid{$i}.$this->keyid{$i+1})); + $bytes .= chr(hexdec($this->keyid[$i].$this->keyid[$i+1])); } $bytes .= chr($this->key_algorithm); @@ -685,13 +694,15 @@ class OpenPGP_SignaturePacket extends OpenPGP_Packet { switch($this->version = ord($this->read_byte())) { case 2: case 3: - assert(ord($this->read_byte()) == 5); + if(ord($this->read_byte()) != 5) { + throw new Exception("Invalid version 2 or 3 SignaturePacket"); + } $this->signature_type = ord($this->read_byte()); $creation_time = $this->read_timestamp(); $keyid = $this->read_bytes(8); $keyidHex = ''; for($i = 0; $i < strlen($keyid); $i++) { // Store KeyID in Hex - $keyidHex .= sprintf('%02X',ord($keyid{$i})); + $keyidHex .= sprintf('%02X',ord($keyid[$i])); } $this->hashed_subpackets = array(); @@ -768,7 +779,7 @@ class OpenPGP_SignaturePacket extends OpenPGP_Packet { foreach((array)$this->unhashed_subpackets as $p) { if($p instanceof OpenPGP_SignaturePacket_IssuerPacket) { for($i = 0; $i < strlen($p->data); $i += 2) { - $body .= chr(hexdec($p->data{$i}.$p->data{$i+1})); + $body .= chr(hexdec($p->data[$i].$p->data[$i+1])); } break; } @@ -975,8 +986,8 @@ class OpenPGP_SignaturePacket_ExportableCertificationPacket extends OpenPGP_Sign class OpenPGP_SignaturePacket_TrustSignaturePacket extends OpenPGP_SignaturePacket_Subpacket { function read() { - $this->depth = ord($this->input{0}); - $this->trust = ord($this->input{1}); + $this->depth = ord($this->input[0]); + $this->trust = ord($this->input[1]); } function body() { @@ -1052,7 +1063,7 @@ class OpenPGP_SignaturePacket_RevocationKeyPacket extends OpenPGP_SignaturePacke $bytes .= chr($this->key_algorithm); for($i = 0; $i < strlen($this->fingerprint); $i += 2) { - $bytes .= chr(hexdec($this->fingerprint{$i}.$this->fingerprint{$i+1})); + $bytes .= chr(hexdec($this->fingerprint[$i].$this->fingerprint[$i+1])); } return $bytes; @@ -1072,7 +1083,7 @@ class OpenPGP_SignaturePacket_IssuerPacket extends OpenPGP_SignaturePacket_Subpa function body() { $bytes = ''; for($i = 0; $i < strlen($this->data); $i += 2) { - $bytes .= chr(hexdec($this->data{$i}.$this->data{$i+1})); + $bytes .= chr(hexdec($this->data[$i].$this->data[$i+1])); } return $bytes; } @@ -1305,7 +1316,7 @@ class OpenPGP_OnePassSignaturePacket extends OpenPGP_Packet { function body() { $body = chr($this->version).chr($this->signature_type).chr($this->hash_algorithm).chr($this->key_algorithm); for($i = 0; $i < strlen($this->key_id); $i += 2) { - $body .= chr(hexdec($this->key_id{$i}.$this->key_id{$i+1})); + $body .= chr(hexdec($this->key_id[$i].$this->key_id[$i+1])); } $body .= chr((int)$this->nested); return $body; @@ -1594,6 +1605,13 @@ class OpenPGP_CompressedDataPacket extends OpenPGP_Packet implements IteratorAgg public $algorithm; /* see http://tools.ietf.org/html/rfc4880#section-9.3 */ static $algorithms = array(0 => 'Uncompressed', 1 => 'ZIP', 2 => 'ZLIB', 3 => 'BZip2'); + + function __construct($m=NULL, $algorithm=1) { + parent::__construct(); + $this->algorithm = $algorithm; + $this->data = $m ? $m : new OpenPGP_Message(); + } + function read() { $this->algorithm = ord($this->read_byte()); $this->data = $this->read_bytes($this->length); diff --git a/securemail/vendor/singpolyma/openpgp-php/lib/openpgp_crypt_rsa.php b/securemail/vendor/singpolyma/openpgp-php/lib/openpgp_crypt_rsa.php index bce11e18..f5c5d382 100644 --- a/securemail/vendor/singpolyma/openpgp-php/lib/openpgp_crypt_rsa.php +++ b/securemail/vendor/singpolyma/openpgp-php/lib/openpgp_crypt_rsa.php @@ -182,8 +182,10 @@ class OpenPGP_Crypt_RSA { $keys = new self($keys); } + $session_key = NULL; foreach($message as $p) { if($p instanceof OpenPGP_AsymmetricSessionKeyPacket) { + $session_key = $p; if($keys instanceof Crypt_RSA) { $sk = self::try_decrypt_session($keys, substr($p->encrypted_data, 2)); } else if(strlen(str_replace('0', '', $p->keyid)) < 1) { @@ -203,23 +205,26 @@ class OpenPGP_Crypt_RSA { } } + if (!$session_key) throw new Exception("Not an asymmetrically encrypted message"); + return NULL; /* Failed */ } static function try_decrypt_session($key, $edata) { $key->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); - $data = $key->decrypt($edata); + $data = @$key->decrypt($edata); + if(!$data) return NULL; $sk = substr($data, 1, strlen($data)-3); $chk = unpack('n', substr($data, -2)); $chk = reset($chk); $sk_chk = 0; for($i = 0; $i < strlen($sk); $i++) { - $sk_chk = ($sk_chk + ord($sk{$i})) % 65536; + $sk_chk = ($sk_chk + ord($sk[$i])) % 65536; } if($sk_chk != $chk) return NULL; - return array(ord($data{0}), $sk); + return array(ord($data[0]), $sk); } static function crypt_rsa_key($mod, $exp, $hash='SHA256') { diff --git a/securemail/vendor/singpolyma/openpgp-php/lib/openpgp_crypt_symmetric.php b/securemail/vendor/singpolyma/openpgp-php/lib/openpgp_crypt_symmetric.php index 844c53b2..4d6ef999 100644 --- a/securemail/vendor/singpolyma/openpgp-php/lib/openpgp_crypt_symmetric.php +++ b/securemail/vendor/singpolyma/openpgp-php/lib/openpgp_crypt_symmetric.php @@ -1,15 +1,15 @@ algorithm, $pass->fingerprint(), $esk)); } else if(is_string($pass)) { - $s2k = new OpenPGP_S2K(Random::string(10)); + $s2k = new OpenPGP_S2K(Random::string(8)); $cipher->setKey($s2k->make_key($pass, $key_bytes)); $esk = $cipher->encrypt(chr($symmetric_algorithm) . $key); array_unshift($encrypted, new OpenPGP_SymmetricSessionKeyPacket($s2k, $esk, $symmetric_algorithm)); @@ -62,7 +62,7 @@ class OpenPGP_Crypt_Symmetric { $padAmount = $key_block_bytes - (strlen($p->encrypted_data) % $key_block_bytes); $data = substr($cipher->decrypt($p->encrypted_data . str_repeat("\0", $padAmount)), 0, strlen($p->encrypted_data)); - $decrypted = self::decryptPacket($epacket, ord($data{0}), substr($data, 1)); + $decrypted = self::decryptPacket($epacket, ord($data[0]), substr($data, 1)); } else { list($cipher, $key_bytes, $key_block_bytes) = self::getCipher($p->symmetric_algorithm); $decrypted = self::decryptPacket($epacket, $p->symmetric_algorithm, $p->s2k->make_key($pass, $key_bytes)); @@ -75,6 +75,31 @@ class OpenPGP_Crypt_Symmetric { return NULL; /* If we get here, we failed */ } + public static function encryptSecretKey($pass, $packet, $symmetric_algorithm=9) { + $packet = clone $packet; // Do not mutate original + $packet->s2k_useage = 254; + $packet->symmetric_algorithm = $symmetric_algorithm; + + list($cipher, $key_bytes, $key_block_bytes) = self::getCipher($packet->symmetric_algorithm); + if(!$cipher) throw new Exception("Unsupported cipher"); + + $material = ''; + foreach(OpenPGP_SecretKeyPacket::$secret_key_fields[$packet->algorithm] as $field) { + $f = $packet->key[$field]; + $material .= pack('n', OpenPGP::bitlength($f)) . $f; + unset($packet->key[$field]); + } + $material .= hash('sha1', $material, true); + + $iv = Random::string($key_block_bytes); + if(!$packet->s2k) $packet->s2k = new OpenPGP_S2K(Random::string(8)); + $cipher->setKey($packet->s2k->make_key($pass, $key_bytes)); + $cipher->setIV($iv); + $packet->encrypted_data = $iv . $cipher->encrypt($material); + + return $packet; + } + public static function decryptSecretKey($pass, $packet) { $packet = clone $packet; // Do not mutate orinigal @@ -97,6 +122,7 @@ class OpenPGP_Crypt_Symmetric { if($chk != $mkChk) return NULL; } + $packet->s2k = NULL; $packet->s2k_useage = 0; $packet->symmetric_algorithm = 0; $packet->encrypted_data = NULL; @@ -146,29 +172,45 @@ class OpenPGP_Crypt_Symmetric { public static function getCipher($algo) { $cipher = NULL; switch($algo) { + case NULL: + case 0: + throw new Exception("Data is already unencrypted"); case 2: - $cipher = new Crypt_TripleDES(CRYPT_DES_MODE_CFB); - $key_bytes = 24; - $key_block_bytes = 8; + $cipher = new Crypt_TripleDES(Crypt_TripleDES::MODE_CFB); + $key_bytes = 24; + $key_block_bytes = 8; break; case 3: - if(defined('MCRYPT_CAST_128')) { + if(class_exists('OpenSSLWrapper')) { + $cipher = new OpenSSLWrapper("CAST5-CFB"); + } else if(defined('MCRYPT_CAST_128')) { $cipher = new MCryptWrapper(MCRYPT_CAST_128); - } else { - throw new Exception("Unsupported cipher: you must have mcrypt installed to use CAST5"); } break; + case 4: + $cipher = new Crypt_Blowfish(Crypt_Blowfish::MODE_CFB); + $key_bytes = 16; + $key_block_bytes = 8; + break; case 7: - $cipher = new Crypt_AES(CRYPT_AES_MODE_CFB); - $cipher->setKeyLength(128); + $cipher = new Crypt_AES(Crypt_AES::MODE_CFB); + $cipher->setKeyLength(128); break; case 8: - $cipher = new Crypt_AES(CRYPT_AES_MODE_CFB); - $cipher->setKeyLength(192); + $cipher = new Crypt_AES(Crypt_AES::MODE_CFB); + $cipher->setKeyLength(192); break; case 9: - $cipher = new Crypt_AES(CRYPT_AES_MODE_CFB); + $cipher = new Crypt_AES(Crypt_AES::MODE_CFB); + $cipher->setKeyLength(256); + break; + case 10: + $cipher = new Crypt_Twofish(Crypt_Twofish::MODE_CFB); + if(method_exists($cipher, 'setKeyLength')) { $cipher->setKeyLength(256); + } else { + $cipher = NULL; + } break; } if(!$cipher) return array(NULL, NULL, NULL); // Unsupported cipher @@ -187,7 +229,7 @@ class OpenPGP_Crypt_Symmetric { public static function checksum($s) { $mkChk = 0; for($i = 0; $i < strlen($s); $i++) { - $mkChk = ($mkChk + ord($s{$i})) % 65536; + $mkChk = ($mkChk + ord($s[$i])) % 65536; } return $mkChk; } diff --git a/securemail/vendor/singpolyma/openpgp-php/lib/openpgp_openssl_wrapper.php b/securemail/vendor/singpolyma/openpgp-php/lib/openpgp_openssl_wrapper.php new file mode 100644 index 00000000..83d5ad65 --- /dev/null +++ b/securemail/vendor/singpolyma/openpgp-php/lib/openpgp_openssl_wrapper.php @@ -0,0 +1,33 @@ +cipher = $cipher; + $this->key_size = 16; + $this->block_size = 8; + $this->iv = str_repeat("\0", 8); + } + + function setKey($key) { + $this->key = $key; + } + + function setIV($iv) { + $this->iv = $iv; + } + + function encrypt($data) { + return openssl_encrypt($data, $this->cipher, $this->key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $this->iv); + } + + function decrypt($data) { + return openssl_decrypt($data, $this->cipher, $this->key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $this->iv); + } + } +} diff --git a/securemail/vendor/singpolyma/openpgp-php/phpunit.xml b/securemail/vendor/singpolyma/openpgp-php/phpunit.xml index a38403e1..9a2ad2c6 100644 --- a/securemail/vendor/singpolyma/openpgp-php/phpunit.xml +++ b/securemail/vendor/singpolyma/openpgp-php/phpunit.xml @@ -8,6 +8,10 @@ tests/suite.php + + tests/suite.php + + tests/phpseclib_suite.php diff --git a/securemail/vendor/singpolyma/openpgp-php/tests/data/000079-002.sig b/securemail/vendor/singpolyma/openpgp-php/tests/data/000079-002.sig new file mode 100644 index 00000000..d5a51b9c Binary files /dev/null and b/securemail/vendor/singpolyma/openpgp-php/tests/data/000079-002.sig differ diff --git a/securemail/vendor/singpolyma/openpgp-php/tests/data/000080-006.public_key b/securemail/vendor/singpolyma/openpgp-php/tests/data/000080-006.public_key new file mode 100644 index 00000000..dce135cb Binary files /dev/null and b/securemail/vendor/singpolyma/openpgp-php/tests/data/000080-006.public_key differ diff --git a/securemail/vendor/singpolyma/openpgp-php/tests/data/000081-002.sig b/securemail/vendor/singpolyma/openpgp-php/tests/data/000081-002.sig new file mode 100644 index 00000000..e428a87c Binary files /dev/null and b/securemail/vendor/singpolyma/openpgp-php/tests/data/000081-002.sig differ diff --git a/securemail/vendor/singpolyma/openpgp-php/tests/data/000082-006.public_key b/securemail/vendor/singpolyma/openpgp-php/tests/data/000082-006.public_key new file mode 100644 index 00000000..f521febd Binary files /dev/null and b/securemail/vendor/singpolyma/openpgp-php/tests/data/000082-006.public_key differ diff --git a/securemail/vendor/singpolyma/openpgp-php/tests/data/000083-002.sig b/securemail/vendor/singpolyma/openpgp-php/tests/data/000083-002.sig new file mode 100644 index 00000000..ecff87f8 Binary files /dev/null and b/securemail/vendor/singpolyma/openpgp-php/tests/data/000083-002.sig differ diff --git a/securemail/vendor/singpolyma/openpgp-php/tests/data/symmetric-blowfish.gpg b/securemail/vendor/singpolyma/openpgp-php/tests/data/symmetric-blowfish.gpg new file mode 100644 index 00000000..0dda30c9 --- /dev/null +++ b/securemail/vendor/singpolyma/openpgp-php/tests/data/symmetric-blowfish.gpg @@ -0,0 +1 @@ + hϳfuhs_VF4 \ No newline at end of file diff --git a/securemail/vendor/singpolyma/openpgp-php/tests/data/symmetric-twofish.gpg b/securemail/vendor/singpolyma/openpgp-php/tests/data/symmetric-twofish.gpg new file mode 100644 index 00000000..14255d8d --- /dev/null +++ b/securemail/vendor/singpolyma/openpgp-php/tests/data/symmetric-twofish.gpg @@ -0,0 +1,3 @@ +  +cІ 9=]TfA cvekʲn}%.lu?\I +[bl \ No newline at end of file diff --git a/securemail/vendor/singpolyma/openpgp-php/tests/phpseclib_suite.php b/securemail/vendor/singpolyma/openpgp-php/tests/phpseclib_suite.php index 0f6ae30f..b54a6d23 100644 --- a/securemail/vendor/singpolyma/openpgp-php/tests/phpseclib_suite.php +++ b/securemail/vendor/singpolyma/openpgp-php/tests/phpseclib_suite.php @@ -1,4 +1,5 @@ oneSymmetric("hello", "PGP\n", "symmetric-aes.gpg"); - } - public function testDecrypt3DES() { $this->oneSymmetric("hello", "PGP\n", "symmetric-3des.gpg"); } - public function testDecryptCAST5() { // Requires mcrypt + public function testDecryptCAST5() { // Requires mcrypt or openssl $this->oneSymmetric("hello", "PGP\n", "symmetric-cast5.gpg"); } + public function testDecryptBlowfish() { + $this->oneSymmetric("hello", "PGP\n", "symmetric-blowfish.gpg"); + } + + public function testDecryptAES() { + $this->oneSymmetric("hello", "PGP\n", "symmetric-aes.gpg"); + } + + public function testDecryptTwofish() { + if(OpenPGP_Crypt_Symmetric::getCipher(10)[0]) { + $this->oneSymmetric("hello", "PGP\n", "symmetric-twofish.gpg"); + } + } + public function testDecryptSessionKey() { $this->oneSymmetric("hello", "PGP\n", "symmetric-with-session-key.gpg"); } @@ -109,25 +120,89 @@ class Decryption extends PHPUnit_Framework_TestCase { } } + public function testDecryptRoundtrip() { + $m = new OpenPGP_Message(array(new OpenPGP_LiteralDataPacket("hello\n"))); + $key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg')); + $em = OpenPGP_Crypt_Symmetric::encrypt($key, $m); + + foreach($key as $packet) { + if(!($packet instanceof OpenPGP_SecretKeyPacket)) continue; + $decryptor = new OpenPGP_Crypt_RSA($packet); + $m2 = $decryptor->decrypt($em); + + foreach($m2 as $p) { + if($p instanceof OpenPGP_LiteralDataPacket) { + $this->assertEquals($p->data, "hello\n"); + } + } + } + } + public function testDecryptSecretKey() { $key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/encryptedSecretKey.gpg')); $skey = OpenPGP_Crypt_Symmetric::decryptSecretKey("hello", $key[0]); $this->assertSame(!!$skey, true); } + + public function testEncryptSecretKeyRoundtrip() { + $key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg')); + $enkey = OpenPGP_Crypt_Symmetric::encryptSecretKey("password", $key[0]); + $skey = OpenPGP_Crypt_Symmetric::decryptSecretKey("password", $enkey); + $this->assertEquals($key[0], $skey); + } + + public function testAlreadyDecryptedSecretKey() { + $this->expectException(Exception::class); + $this->expectExceptionMessage("Data is already unencrypted"); + $key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg')); + OpenPGP_Crypt_Symmetric::decryptSecretKey("hello", $key[0]); + } } -class Encryption extends PHPUnit_Framework_TestCase { - public function testEncryptSymmetric() { +class Encryption extends TestCase { + public function oneSymmetric($algorithm) { $data = new OpenPGP_LiteralDataPacket('This is text.', array('format' => 'u', 'filename' => 'stuff.txt')); - $encrypted = OpenPGP_Crypt_Symmetric::encrypt('secret', new OpenPGP_Message(array($data))); + $encrypted = OpenPGP_Crypt_Symmetric::encrypt('secret', new OpenPGP_Message(array($data)), $algorithm); + $encrypted = OpenPGP_Message::parse($encrypted->to_bytes()); $decrypted = OpenPGP_Crypt_Symmetric::decryptSymmetric('secret', $encrypted); $this->assertEquals($decrypted[0]->data, 'This is text.'); } + public function testEncryptSymmetric3DES() { + $this->oneSymmetric(2); + } + + public function testEncryptSymmetricCAST5() { + $this->oneSymmetric(3); + } + + public function testEncryptSymmetricBlowfish() { + $this->oneSymmetric(4); + } + + public function testEncryptSymmetricAES128() { + $this->oneSymmetric(7); + } + + public function testEncryptSymmetricAES192() { + $this->oneSymmetric(8); + } + + public function testEncryptSymmetricAES256() { + $this->oneSymmetric(9); + } + + public function testEncryptSymmetricTwofish() { + if(OpenPGP_Crypt_Symmetric::getCipher(10)[0]) { + $this->oneSymmetric(10); + } + } + public function testEncryptAsymmetric() { $key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg')); $data = new OpenPGP_LiteralDataPacket('This is text.', array('format' => 'u', 'filename' => 'stuff.txt')); $encrypted = OpenPGP_Crypt_Symmetric::encrypt($key, new OpenPGP_Message(array($data))); + $encrypted = OpenPGP_Message::parse($encrypted->to_bytes()); $decryptor = new OpenPGP_Crypt_RSA($key); $decrypted = $decryptor->decrypt($encrypted); $this->assertEquals($decrypted[0]->data, 'This is text.'); diff --git a/securemail/vendor/singpolyma/openpgp-php/tests/suite.php b/securemail/vendor/singpolyma/openpgp-php/tests/suite.php index 1752d0b6..57f7ad70 100644 --- a/securemail/vendor/singpolyma/openpgp-php/tests/suite.php +++ b/securemail/vendor/singpolyma/openpgp-php/tests/suite.php @@ -1,8 +1,9 @@ to_bytes(); @@ -14,355 +15,354 @@ class Serialization extends PHPUnit_Framework_TestCase { $this->oneSerialization("000001-006.public_key"); } - public function test000002013user_id() { $this->oneSerialization("000002-013.user_id"); } - + public function test000003002sig() { $this->oneSerialization("000003-002.sig"); } - + public function test000004012ring_trust() { $this->oneSerialization("000004-012.ring_trust"); } - + public function test000005002sig() { $this->oneSerialization("000005-002.sig"); } - + public function test000006012ring_trust() { $this->oneSerialization("000006-012.ring_trust"); } - + public function test000007002sig() { $this->oneSerialization("000007-002.sig"); } - + public function test000008012ring_trust() { $this->oneSerialization("000008-012.ring_trust"); } - + public function test000009002sig() { $this->oneSerialization("000009-002.sig"); } - + public function test000010012ring_trust() { $this->oneSerialization("000010-012.ring_trust"); } - + public function test000011002sig() { $this->oneSerialization("000011-002.sig"); } - + public function test000012012ring_trust() { $this->oneSerialization("000012-012.ring_trust"); } - + public function test000013014public_subkey() { $this->oneSerialization("000013-014.public_subkey"); } - + public function test000014002sig() { $this->oneSerialization("000014-002.sig"); } - + public function test000015012ring_trust() { $this->oneSerialization("000015-012.ring_trust"); } - + public function test000016006public_key() { $this->oneSerialization("000016-006.public_key"); } - + public function test000017002sig() { $this->oneSerialization("000017-002.sig"); } - + public function test000018012ring_trust() { $this->oneSerialization("000018-012.ring_trust"); } - + public function test000019013user_id() { $this->oneSerialization("000019-013.user_id"); } - + public function test000020002sig() { $this->oneSerialization("000020-002.sig"); } - + public function test000021012ring_trust() { $this->oneSerialization("000021-012.ring_trust"); } - + public function test000022002sig() { $this->oneSerialization("000022-002.sig"); } - + public function test000023012ring_trust() { $this->oneSerialization("000023-012.ring_trust"); } - + public function test000024014public_subkey() { $this->oneSerialization("000024-014.public_subkey"); } - + public function test000025002sig() { $this->oneSerialization("000025-002.sig"); } - + public function test000026012ring_trust() { $this->oneSerialization("000026-012.ring_trust"); } - + public function test000027006public_key() { $this->oneSerialization("000027-006.public_key"); } - + public function test000028002sig() { $this->oneSerialization("000028-002.sig"); } - + public function test000029012ring_trust() { $this->oneSerialization("000029-012.ring_trust"); } - + public function test000030013user_id() { $this->oneSerialization("000030-013.user_id"); } - + public function test000031002sig() { $this->oneSerialization("000031-002.sig"); } - + public function test000032012ring_trust() { $this->oneSerialization("000032-012.ring_trust"); } - + public function test000033002sig() { $this->oneSerialization("000033-002.sig"); } - + public function test000034012ring_trust() { $this->oneSerialization("000034-012.ring_trust"); } - + public function test000035006public_key() { $this->oneSerialization("000035-006.public_key"); } - + public function test000036013user_id() { $this->oneSerialization("000036-013.user_id"); } - + public function test000037002sig() { $this->oneSerialization("000037-002.sig"); } - + public function test000038012ring_trust() { $this->oneSerialization("000038-012.ring_trust"); } - + public function test000039002sig() { $this->oneSerialization("000039-002.sig"); } - + public function test000040012ring_trust() { $this->oneSerialization("000040-012.ring_trust"); } - + public function test000041017attribute() { $this->oneSerialization("000041-017.attribute"); } - + public function test000042002sig() { $this->oneSerialization("000042-002.sig"); } - + public function test000043012ring_trust() { $this->oneSerialization("000043-012.ring_trust"); } - + public function test000044014public_subkey() { $this->oneSerialization("000044-014.public_subkey"); } - + public function test000045002sig() { $this->oneSerialization("000045-002.sig"); } - + public function test000046012ring_trust() { $this->oneSerialization("000046-012.ring_trust"); } - + public function test000047005secret_key() { $this->oneSerialization("000047-005.secret_key"); } - + public function test000048013user_id() { $this->oneSerialization("000048-013.user_id"); } - + public function test000049002sig() { $this->oneSerialization("000049-002.sig"); } - + public function test000050012ring_trust() { $this->oneSerialization("000050-012.ring_trust"); } - + public function test000051007secret_subkey() { $this->oneSerialization("000051-007.secret_subkey"); } - + public function test000052002sig() { $this->oneSerialization("000052-002.sig"); } - + public function test000053012ring_trust() { $this->oneSerialization("000053-012.ring_trust"); } - + public function test000054005secret_key() { $this->oneSerialization("000054-005.secret_key"); } - + public function test000055002sig() { $this->oneSerialization("000055-002.sig"); } - + public function test000056012ring_trust() { $this->oneSerialization("000056-012.ring_trust"); } - + public function test000057013user_id() { $this->oneSerialization("000057-013.user_id"); } - + public function test000058002sig() { $this->oneSerialization("000058-002.sig"); } - + public function test000059012ring_trust() { $this->oneSerialization("000059-012.ring_trust"); } - + public function test000060007secret_subkey() { $this->oneSerialization("000060-007.secret_subkey"); } - + public function test000061002sig() { $this->oneSerialization("000061-002.sig"); } - + public function test000062012ring_trust() { $this->oneSerialization("000062-012.ring_trust"); } - + public function test000063005secret_key() { $this->oneSerialization("000063-005.secret_key"); } - + public function test000064002sig() { $this->oneSerialization("000064-002.sig"); } - + public function test000065012ring_trust() { $this->oneSerialization("000065-012.ring_trust"); } - + public function test000066013user_id() { $this->oneSerialization("000066-013.user_id"); } - + public function test000067002sig() { $this->oneSerialization("000067-002.sig"); } - + public function test000068012ring_trust() { $this->oneSerialization("000068-012.ring_trust"); } - + public function test000069005secret_key() { $this->oneSerialization("000069-005.secret_key"); } - + public function test000070013user_id() { $this->oneSerialization("000070-013.user_id"); } - + public function test000071002sig() { $this->oneSerialization("000071-002.sig"); } - + public function test000072012ring_trust() { $this->oneSerialization("000072-012.ring_trust"); } - + public function test000073017attribute() { $this->oneSerialization("000073-017.attribute"); } - + public function test000074002sig() { $this->oneSerialization("000074-002.sig"); } - + public function test000075012ring_trust() { $this->oneSerialization("000075-012.ring_trust"); } - + public function test000076007secret_subkey() { $this->oneSerialization("000076-007.secret_subkey"); } - + public function test000077002sig() { $this->oneSerialization("000077-002.sig"); } - + public function test000078012ring_trust() { $this->oneSerialization("000078-012.ring_trust"); } - + public function test002182002sig() { $this->oneSerialization("002182-002.sig"); } - + public function testpubringgpg() { $this->oneSerialization("pubring.gpg"); } - + public function testsecringgpg() { $this->oneSerialization("secring.gpg"); } - + public function testcompressedsiggpg() { $this->oneSerialization("compressedsig.gpg"); } - + public function testcompressedsigzlibgpg() { $this->oneSerialization("compressedsig-zlib.gpg"); } - + public function testcompressedsigbzip2gpg() { $this->oneSerialization("compressedsig-bzip2.gpg"); } - + public function testonepass_sig() { $this->oneSerialization("onepass_sig"); } - + public function testsymmetrically_encrypted() { $this->oneSerialization("symmetrically_encrypted"); } - + public function testuncompressedopsdsagpg() { $this->oneSerialization("uncompressed-ops-dsa.gpg"); } - + public function testuncompressedopsdsasha384txtgpg() { $this->oneSerialization("uncompressed-ops-dsa-sha384.txt.gpg"); } - + public function testuncompressedopsrsagpg() { $this->oneSerialization("uncompressed-ops-rsa.gpg"); } @@ -376,7 +376,7 @@ class Serialization extends PHPUnit_Framework_TestCase { } } -class Fingerprint extends PHPUnit_Framework_TestCase { +class Fingerprint extends TestCase { public function oneFingerprint($path, $kf) { $m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path)); $this->assertEquals($m[0]->fingerprint(), $kf); @@ -397,4 +397,31 @@ class Fingerprint extends PHPUnit_Framework_TestCase { public function test000035006public_key() { $this->oneFingerprint("000035-006.public_key", "CB7933459F59C70DF1C3FBEEDEDC3ECF689AF56D"); } + + public function test000080006public_key() { + $this->oneFingerprint("000080-006.public_key", "AEDA0C4468AE265E8B7CCA1C3047D4A7B15467AB"); + } + + public function test000082006public_key() { + $this->oneFingerprint("000082-006.public_key", "589D7E6884A9235BBE821D35BD7BA7BC5547FD09"); + } +} + +class Signature extends TestCase { + public function oneIssuer($path, $kf) { + $m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path)); + $this->assertEquals($m[0]->issuer(), $kf); + } + + public function test000079002sig() { + $this->oneIssuer("000079-002.sig", "C25059FA8730BC38"); + } + + public function test000081002sig() { + $this->oneIssuer("000081-002.sig", "6B799484725130FE"); + } + + public function test000083002sig() { + $this->oneIssuer("000083-002.sig", "BD7BA7BC5547FD09"); + } }