],
"require": {
"phpseclib/phpseclib": "^2.0",
- "singpolyma/openpgp-php": "^0.3.0"
+ "singpolyma/openpgp-php": "^0.5.0"
},
"license": "AGPL-3.0+",
"minimum-stability": "stable",
"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": {
},
"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": {
"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": {
}
],
"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": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
- "platform-dev": []
+ "platform-dev": [],
+ "plugin-api-version": "2.0.0"
}
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
- * @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
{
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();
*/
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;
}
/**
--- /dev/null
+<?php
+
+namespace Composer;
+
+use Composer\Semver\VersionParser;
+
+
+
+
+
+
+class InstalledVersions
+{
+private static $installed = array (
+ 'root' =>
+ 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;
+}
+}
$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',
'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',
);
}
}
+ /**
+ * @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 {
);
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',
'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)
-[
- {
- "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"
+ ],
+ "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"
],
- "psr-4": {
- "phpseclib\\": "phpseclib/"
- }
+ "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"
+ }
+ ],
+ "install-path": "../phpseclib/phpseclib"
},
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Jim Wigginton",
- "email": "terrafrost@php.net",
- "role": "Lead Developer"
+ {
+ "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": "Patrick Monnerat",
- "email": "pm@datasphere.ch",
- "role": "Developer"
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/singpolyma/openpgp-php/zipball/69292f6a46ed7f687083bfb8974b161a41ab213c",
+ "reference": "69292f6a46ed7f687083bfb8974b161a41ab213c",
+ "shasum": ""
},
- {
- "name": "Andreas Fischer",
- "email": "bantu@phpbb.com",
- "role": "Developer"
+ "require": {
+ "php": "^5.6 || ^7.0 || ^8.0",
+ "phpseclib/phpseclib": "^2.0 !=2.0.8"
},
- {
- "name": "Hans-Jürgen Petrich",
- "email": "petrich@tronic-media.com",
- "role": "Developer"
+ "require-dev": {
+ "phpunit/phpunit": "^9.0"
},
- {
- "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"
+ "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"
},
- {
- "name": "Stephen Paul Weber",
- "email": "singpolyma@singpolyma.net"
- }
- ],
- "description": "Pure-PHP implementation of the OpenPGP Message Format (RFC 4880)"
- }
-]
+ "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": []
+}
--- /dev/null
+<?php return array (
+ 'root' =>
+ 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',
+ ),
+ ),
+);
--- /dev/null
+<?php
+
+// platform_check.php @generated by Composer
+
+$issues = array();
+
+if (!(PHP_VERSION_ID >= 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
+ );
+}
--- /dev/null
+# 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
-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
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
# 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
-* [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/)
+- [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
-<img src="http://phpseclib.sourceforge.net/pear-icon.png" alt="PEAR Channel" width="16" height="16">
-PEAR Channel: [phpseclib.sourceforge.net](http://phpseclib.sourceforge.net/pear.htm)
+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
+
+* [Browse Git](https://github.com/phpseclib/phpseclib)
## 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
* [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
+## Contributing
-Dependencies are managed via Composer.
+1. Fork the Project
-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
+2. Ensure you have Composer installed (see [Composer Download Instructions](https://getcomposer.org/download/))
- ``` sh
- curl -sS https://getcomposer.org/installer | php
- ```
-
-2. Install Dependencies
+3. Install Development Dependencies
``` sh
- php composer.phar install
+ composer install
```
-## Contributing
-
-1. Fork the Project
-
-2. Install Development Dependencies
-
-3. Create a Feature Branch
+4. Create a Feature Branch
-4. (Recommended) Run the Test Suite
+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
--- /dev/null
+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
},
"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": {
+++ /dev/null
-{
- "_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": []
-}
* @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.
*
* @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
break;
case self::MODE_CTR:
case self::MODE_CFB:
+ case self::MODE_CFB8:
case self::MODE_OFB:
case self::MODE_STREAM:
$this->mode = $mode;
}
$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;
- }
}
/**
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);
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);
$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);
}
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;
}
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;
}
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;
}
$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'])) {
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;
$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);
}
}
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;
}
}
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;
}
$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'])) {
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;
}
} 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;
}
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) {
return 'ctr';
case self::MODE_CFB:
return 'cfb';
+ case self::MODE_CFB8:
+ return 'cfb8';
case self::MODE_OFB:
return 'ofb';
}
}
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;
}
}
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;
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));
}
}
$_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;
}
// 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;
}
/**
*
* @see self::_setupInlineCrypt()
* @access private
- * @param $bytes
+ * @param string $bytes
* @return string
*/
function _hashInlineCryptFunction($bytes)
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()
+ {
+ }
}
function setKeyLength($length)
{
if ($length < 32) {
- $this->key_length = 7;
+ $this->key_length = 4;
} elseif ($length > 448) {
$this->key_length = 56;
} else {
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';
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]);
}
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]);
}
$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:
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.= '
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]') . ';
';
}
$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;
}
*/
var $key = false;
+ /**
+ * Computed Key
+ *
+ * @see self::_computeKey()
+ * @var string
+ * @access private
+ */
+ var $computedKey = false;
+
/**
* Outer XOR (Internal HMAC)
*
*/
var $ipad;
+ /**
+ * Engine
+ *
+ * @see self::setHash()
+ * @var string
+ * @access private
+ */
+ var $engine;
+
/**
* Default Constructor.
*
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);
+ }
}
/**
$this->l = 64;
}
+ switch ($hash) {
+ case 'md2-96':
+ case 'md2':
+ $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':
- $mode = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ?
+ $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':
default:
$this->hash = MHASH_SHA1;
}
+ $this->_computeKey(self::MODE_MHASH);
return;
case self::MODE_HASH:
switch ($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);
}
/**
*/
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;
* _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
$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);
}
/**
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 {
* @var string
* @access private
*/
- var $key = "\0";
+ var $key;
/**
* The Key Stream for decryption and encryption
*/
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';
default:
return false;
}
+ }
}
return parent::isValidEngine($engine);
* PKCS#8 formatted private key
*/
const PRIVATE_FORMAT_PKCS8 = 8;
+ /**
+ * OpenSSH formatted private key
+ */
+ const PRIVATE_FORMAT_OPENSSH = 9;
/**#@-*/
/**#@+
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];
+ }
}
}
}
* @access public
* @param int $bits
* @param int $timeout
- * @param array $p
+ * @param array $partial
*/
function createKey($bits = 1024, $timeout = false, $partial = array())
{
*
* @access private
* @see self::setPrivateKeyFormat()
- * @param string $RSAPrivateKey
+ * @param Math_BigInteger $n
+ * @param Math_BigInteger $e
+ * @param Math_BigInteger $d
+ * @param array<int,Math_BigInteger> $primes
+ * @param array<int,Math_BigInteger> $exponents
+ * @param array<int,Math_BigInteger> $coefficients
* @return string
*/
function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
$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) {
*
* @access private
* @see self::setPublicKeyFormat()
- * @param string $RSAPrivateKey
- * @return string
+ * @param Math_BigInteger $n
+ * @param Math_BigInteger $e
+ * @return string|array<string,Math_BigInteger>
*/
function _convertPublicKey($n, $e)
{
* @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)
{
$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
/*
xml_set_character_data_handler($xml, '_data_handler');
// add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
if (!xml_parse($xml, '<xml>' . $key . '</xml>')) {
+ 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:
}
$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;
}
/**
* 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)
{
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);
}
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;
}
*
* @see self::getPublicKey()
* @access public
- * @param string $key
* @param int $type optional
*/
function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8)
*
* @see self::getPublicKey()
* @access public
- * @param string $key
* @param int $type optional
* @return mixed
*/
*
* @see self::getPrivateKey()
* @access private
- * @param string $key
- * @param int $type optional
+ * @param int $mode optional
*/
function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8)
{
* of the hash function Hash) and 0.
*
* @access public
- * @param int $format
+ * @param int $sLen
*/
function setSaltLength($sLen)
{
* 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)
*/
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";
}
/**
*
* @access private
* @param string $mgfSeed
- * @param int $mgfLen
+ * @param int $maskLen
* @return string
*/
function _mgf1($mgfSeed, $maskLen)
$db = $maskedDB ^ $dbMask;
$lHash2 = substr($db, 0, $this->hLen);
$m = substr($db, $this->hLen);
- if ($lHash != $lHash2) {
- user_error('Decryption error');
- return false;
- }
- $m = ltrim($m, chr(0));
- if (ord($m[0]) != 1) {
+ $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;
+ }
+
+ // 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);
}
/**
// 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);
// RSA verification
- $modBits = 8 * $this->k;
+ $modBits = strlen($this->modulus->toBits());
$s2 = $this->_os2ip($s);
$m2 = $this->_rsavp1($s2);
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;
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
*
*
* @access private
* @param string $m
+ * @param string $s
* @return string
*/
function _rsassa_pkcs1_v1_5_verify($m, $s)
// 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));
}
/**
*
* @see self::encrypt()
* @access public
- * @param string $plaintext
+ * @param string $ciphertext
* @return string
*/
function decrypt($ciphertext)
*/
static function string($length)
{
+ if (!$length) {
+ return '';
+ }
+
if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
try {
return \random_bytes($length);
// 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 <http://php.net/ChangeLog-5.php#5.3.4>, "possible blocking behavior". as of 5.3.4
$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
// 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
$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];
$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];
$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];
$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] ^
$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
$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] ^
$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
$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:
$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] ^
$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:
$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] ^
$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(
/**
* 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)
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);
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;
$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;
$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 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 '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;
// @codingStandardsIgnoreEnd
default:
if ($this->x > $this->max_x) {
$this->x = 0;
- $this->y++;
+ $this->_newLine();
} else {
$this->x++;
}
use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger;
+use DateTime;
+use DateTimeZone;
/**
* Pure-PHP ASN.1 Parser
{
$current = array('start' => $start);
+ if (!isset($encoded[$encoded_pos])) {
+ return false;
+ }
$type = ord($encoded[$encoded_pos++]);
- $start++;
+ $startOffset = 1;
$constructed = ($type >> 5) & 1;
$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
$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") {
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
$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;
$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'];
}
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);
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
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;
*/
function asn1map($decoded, $mapping, $special = array())
{
+ if (!is_array($decoded)) {
+ return false;
+ }
+
if (isset($mapping['explicit']) && is_array($decoded['content'])) {
$decoded = $decoded['content'][0];
}
$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'];
$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) {
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]);
*
* @param string $source
* @param string $mapping
- * @param int $idx
+ * @param array $special
* @return string
* @access public
*/
* @param string $source
* @param string $mapping
* @param int $idx
+ * @param array $special
* @return string
* @access private
*/
if ($mapping['type'] == self::TYPE_SET) {
sort($value);
}
- $value = implode($value, '');
+ $value = implode('', $value);
break;
}
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'])) {
$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;
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
*
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";
+ // 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];
+ }
+ $prefix = substr($content, 0, 2) >= 50 ? '19' : '20';
+ $content = $prefix . $content;
+ } elseif (strpos($content, '.') !== false) {
+ $format.= '.u';
}
- 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;
- }
- } else {
- $mktime = 'mktime';
- $timezone = 0;
+ if ($content[strlen($content) - 1] == 'Z') {
+ $content = substr($content, 0, -1) . '+0000';
+ }
+
+ if (strpos($content, '-') !== false || strpos($content, '+') !== false) {
+ $format.= 'O';
}
- return @$mktime($hour, $minute, $second, $month, $day, $year) + $timezone;
+ // error supression isn't necessary as of PHP 7.0:
+ // http://php.net/manual/en/migration70.other-changes.php
+ return @DateTime::createFromFormat($format, $content);
}
/**
use phpseclib\Crypt\RSA;
use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger;
+use DateTime;
+use DateTimeZone;
/**
* Pure-PHP X.509 Parser
*/
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.
*
'children' => $AccessDescription
);
+ $this->SubjectInfoAccessSyntax = array(
+ 'type' => ASN1::TYPE_SEQUENCE,
+ 'min' => 1,
+ 'max' => -1,
+ 'children' => $AccessDescription
+ );
+
$this->SubjectAltName = $GeneralNames;
$this->PrivateKeyUsagePeriod = array(
* 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
$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') {
* 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
* 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
* 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
* 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
* 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
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':
// "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
}
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":
-
- 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;
- }
+ 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":
+
+ 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;
*
* 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)
}
if (!isset($date)) {
- $date = time();
+ $date = new DateTime(null, new DateTimeZone(@date_default_timezone_get()));
}
$notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore'];
$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
*
* @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
$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
}
}
$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'],
$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;
}
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
*
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]));
}
/**
}
$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);
+ }
}
/**
$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;
}
/**
* Load a Certificate Signing Request
*
- * @param string $csr
+ * @param string|array $csr
+ * @param int $mode
* @access public
* @return mixed
*/
*
* https://developer.mozilla.org/en-US/docs/HTML/Element/keygen
*
- * @param string $csr
+ * @param string|array $spkac
* @access public
* @return mixed
*/
/**
* Save a SPKAC CSR request
*
- * @param array $csr
+ * @param string|array $spkac
* @param int $format optional
* @access public
* @return string
* Load a Certificate Revocation List
*
* @param string $crl
+ * @param int $mode
* @access public
* @return mixed
*/
*/
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 {
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
'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(
$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)) {
$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;
/**
* 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
*/
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');
}
/**
$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');
}
}
* Set Serial Number
*
* @param string $serial
- * @param $base optional
+ * @param int $base optional
* @access public
*/
function setSerialNumber($serial, $base = -256)
}
$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;
}
* Set the IP Addresses's which the cert is to be valid for
*
* @access public
- * @param string $ipAddress optional
*/
function setIPAddress()
{
}
$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;
}
* 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;
}
* @author Jim Wigginton <terrafrost@php.net>
* @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;
* ?>
* </code>
*
- * @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
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();
+ $versions = array();
- preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
+ // 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();
- $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];
+ 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];
+ }
}
}
}
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!)
// (?<=^|-)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('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#', '', $x);
+ if (!strlen($x) || $x == '-') {
+ $x = '0';
+ }
switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP:
$temp = $comparison < 0 ? $this->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;
}
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)) :
{
$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');
}
$temp = $this->copy();
+ $temp->bitmask = false;
$temp->is_negative = false;
$divisor = new static();
$opts[] = 'OpenSSL';
}
if (!empty($opts)) {
- $engine.= ' (' . implode($opts, ', ') . ')';
+ $engine.= ' (' . implode('.', $opts) . ')';
}
return array(
'value' => '0x' . $this->toHex(true),
$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);
*
* @see self::_slidingWindow()
* @access private
- * @param \phpseclib\Math\BigInteger
+ * @param \phpseclib\Math\BigInteger $n
* @return \phpseclib\Math\BigInteger
*/
function _mod2($n)
* 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.
{
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);
}
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();
$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);
// (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;
*
* 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
*/
break;
}
}
- $s = 26 * $i + $j - 1;
+ $s = 26 * $i + $j;
$r->_rshift($s);
}
*
* 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
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;
$value = &$result->value;
if (!count($value)) {
+ $result->is_negative = false;
return $result;
}
/**
* Array Repeat
*
- * @param $input Array
- * @param $multiplier mixed
+ * @param array $input
+ * @param mixed $multiplier
* @return array
* @access private
*/
*
* 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
*/
*
* 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
*/
*
* 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
*/
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;
}
$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:
*
* 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}.
*
* 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
*/
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
*/
*
* @see self::getSFTPErrors()
* @see self::getLastSFTPError()
- * @var string
+ * @var array
* @access private
*/
var $sftp_errors = array();
*/
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.
*
$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',
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',
// 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
$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(
$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(
$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" .
$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;
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;
+
+ return true;
+ }
- $this->request_id = 1;
+ /**
+ * (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:
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('.');
$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
*
*/
function pwd()
{
+ if (!$this->_precheck()) {
+ return false;
+ }
+
return $this->pwd;
}
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 {
*/
function realpath($path)
{
+ if (!$this->_precheck()) {
+ return false;
+ }
+
return $this->_realpath($path);
}
* 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))) {
// 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:
}
}
- if ($path[0] != '/') {
+ if (!strlen($path) || $path[0] != '/') {
$path = $this->pwd . '/' . $path;
}
*/
function chdir($dir)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
}
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;
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--;
*/
function _list($dir, $raw = true)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
$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;
}
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);
uasort($contents, array(&$this, '_comparator'));
}
- return $raw ? $contents : array_keys($contents);
+ return $raw ? $contents : array_map('strval', array_keys($contents));
}
/**
*/
function size($filename)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
- return false;
- }
-
$result = $this->stat($filename);
if ($result === false) {
return false;
$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;
}
$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;
*
* Mainly used by file_exists
*
- * @param string $dir
+ * @param string $path
* @return mixed
* @access private
*/
$temp = &$this->stat_cache;
foreach ($dirs as $dir) {
+ if (!is_array($temp)) {
+ return null;
+ }
if (!isset($temp[$dir])) {
return null;
}
*/
function stat($filename)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
*/
function lstat($filename)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
/**
* 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
*/
function touch($filename, $time = null, $atime = null)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
$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;
}
/**
* Changes file or directory owner
*
+ * $uid should be an int for SFTPv3 and a string for SFTPv4+. Ideally the string
+ * would be of the form "user@dns_domain" but it does not need to be.
+ * `$sftp->getSupportedVersions()['version']` will return the specific version
+ * that's being used.
+ *
* Returns true on success or false on error.
*
* @param string $filename
- * @param int $uid
+ * @param int|string $uid
* @param bool $recursive
* @return bool
* @access public
*/
function chown($filename, $uid, $recursive = false)
{
- // quoting from <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>,
- // "if the owner or group is specified as -1, then that ID is not changed"
- $attr = pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1);
+ /*
+ quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>,
+
+ "To avoid a representation that is tied to a particular underlying
+ implementation at the client or server, the use of UTF-8 strings has
+ been chosen. The string should be of the form "user@dns_domain".
+ This will allow for a client and server that do not use the same
+ local representation the ability to translate to a common syntax that
+ can be interpreted by both. In the case where there is no
+ translation available to the client or server, the attribute value
+ must be constructed without the "@"."
+
+ phpseclib _could_ auto append the dns_domain to $uid BUT what if it shouldn't
+ have one? phpseclib would have no way of knowing so rather than guess phpseclib
+ will just use whatever value the user provided
+ */
+
+ $attr = $this->version < 4 ?
+ // quoting <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>,
+ // "if the owner or group is specified as -1, then that ID is not changed"
+ pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1) :
+ // quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>,
+ // "If either the owner or group field is zero length, the field should be
+ // considered absent, and no change should be made to that specific field
+ // during a modification operation"
+ pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, strlen($uid), $uid, 0, '');
return $this->_setstat($filename, $attr, $recursive);
}
/**
* 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);
}
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:
*/
function _setstat($filename, $attr, $recursive)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
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;
}
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;
}
} 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;
}
}
}
- 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;
}
*/
function readlink($link)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
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);
}
*/
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;
}
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);
* 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));
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;
}
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;
}
*/
function rmdir($dir)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
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?
* 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.
*
*/
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;
}
$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;
$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;
}
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)) {
if (isset($fp)) {
$stat = fstat($fp);
- $size = $stat['size'];
+ $size = !empty($stat) ? $stat['size'] : 0;
if ($local_start >= 0) {
fseek($fp, $local_start);
$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);
$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);
}
}
$i++;
+ $j++;
- if ($i == NET_SFTP_QUEUE_SIZE) {
+ if ($i == NET_SFTP_UPLOAD_QUEUE_SIZE) {
if (!$this->_read_put_responses($i)) {
$i = 0;
break;
}
}
+ $result = $this->_close_handle($handle);
+
if (!$this->_read_put_responses($i)) {
if ($mode & self::SOURCE_LOCAL_FILE) {
fclose($fp);
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;
}
/**
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 (strlen($response) < 4) {
+ return false;
+ }
extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status);
* @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;
}
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;
}
$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;
}
}
- $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;
$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);
}
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) {
$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:
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;
}
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)) {
*/
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;
}
}
// 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);
function file_exists($path)
{
if ($this->use_stat_cache) {
+ if (!$this->_precheck()) {
+ return false;
+ }
+
$path = $this->_realpath($path);
$result = $this->_query_stat_cache($path);
*/
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);
*/
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);
*
* @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);
}
/**
- * 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
*/
function rename($oldname, $newname)
{
- if (!($this->bitmap & SSH2::MASK_LOGIN)) {
+ if (!$this->_precheck()) {
return false;
}
// 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;
}
}
// 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);
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
*
*/
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
// 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
$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);
}
*
* @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
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 "<pre>\r\n" . $this->_format_log(array($data), array($packet_type)) . "\r\n</pre>\r\n";
- flush();
- ob_flush();
+ if (NET_SFTP_LOGGING == self::LOG_REALTIME) {
+ switch (PHP_SAPI) {
+ case 'cli':
+ $start = $stop = "\r\n";
+ break;
+ default:
+ $start = '<pre>';
+ $stop = '</pre>';
+ }
+ 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;
}
}
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
*
* @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 = '';
$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
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 "<pre>\r\n" . $this->_format_log(array($packet), array($packet_type)) . "\r\n</pre>\r\n";
- flush();
- ob_flush();
+ if (NET_SFTP_LOGGING == self::LOG_REALTIME) {
+ switch (PHP_SAPI) {
+ case 'cli':
+ $start = $stop = "\r\n";
+ break;
+ default:
+ $start = '<pre>';
+ $stop = '</pre>';
+ }
+ 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;
}
}
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;
}
/**
* Returns all errors
*
- * @return string
+ * @return array
* @access public
*/
function getSFTPErrors()
*/
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
*
$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;
+ }
}
if ($host[0] == '$') {
$host = substr($host, 1);
- global $$host;
+ global ${$host};
if (($$host instanceof SFTP) === false) {
return false;
}
{
switch ($whence) {
case SEEK_SET:
- if ($offset >= $this->size || $offset < 0) {
+ if ($offset < 0) {
return false;
}
break;
// 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;
* $options. What does 8 correspond to?
*
* @param string $path
- * @param int $mode
* @param int $options
* @return bool
* @access public
* 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
*/
$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]);
}
// 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) {
* @see self::interactiveRead()
* @see self::interactiveWrite()
* @param string $cmd
+ * @param bool $block
* @return mixed
* @access public
*/
/**
* 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()
* @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()');
$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] : '';
}
}
$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;
while ($length > 0) {
$temp = fread($this->fsock, $length);
+ if (strlen($temp) != $length) {
+ return false;
+ }
$raw.= $temp;
$length-= strlen($temp);
}
$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) ) {
* 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()
*
* 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)
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.
*
* @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;
/**#@-*/
/**#@+
* 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
/**#@-*/
/**#@+
*/
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;
/**#@-*/
/**
*/
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
*
*/
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
*
*/
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
*
*/
var $curTimeout;
+ /**
+ * Keep Alive Interval
+ *
+ * @see self::setKeepAlive()
+ * @access private
+ */
+ var $keepAlive;
+
/**
* Real-time log file pointer
*
*/
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.
*
* 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
*
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"));
}
$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
if (strlen($temp) == 255) {
continue;
}
+ if ($temp === false) {
+ return false;
+ }
$line.= "$temp\n";
}
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");
$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;
}
$identifier = 'SSH-2.0-phpseclib_2.0';
$ext = array();
- if (extension_loaded('libsodium')) {
+ if (function_exists('sodium_crypto_box_publickey_from_secretkey')) {
$ext[] = 'libsodium';
}
/**
* 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;
+
+ $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();
- // 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')
- );
+ // some SSH servers have buggy implementations of some of the above algorithms
+ switch (true) {
+ case $this->server_identifier == 'SSH-2.0-SSHD':
+ case substr($this->server_identifier, 0, 13) == 'SSH-2.0-DLINK':
+ 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')
+ ));
+ }
}
- $server_host_key_algorithms = array(
- 'ssh-rsa', // RECOMMENDED sign Raw RSA Key
- 'ssh-dss' // REQUIRED sign Raw DSS Key
- );
+ $str_kex_algorithms = implode(',', $kex_algorithms);
+ $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
+ $encryption_algorithms_server_to_client = implode(',', $s2c_encryption_algorithms);
+ $encryption_algorithms_client_to_server = implode(',', $c2s_encryption_algorithms);
+ $mac_algorithms_server_to_client = implode(',', $s2c_mac_algorithms);
+ $mac_algorithms_client_to_server = implode(',', $c2s_mac_algorithms);
+ $compression_algorithms_server_to_client = implode(',', $s2c_compression_algorithms);
+ $compression_algorithms_client_to_server = implode(',', $c2s_compression_algorithms);
- $encryption_algorithms = array(
- // from <http://tools.ietf.org/html/rfc4345#section-4>:
- 'arcfour256',
- 'arcfour128',
+ $client_cookie = Random::string(16);
- //'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key
+ $kexinit_payload_client = pack(
+ 'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
+ NET_SSH2_MSG_KEXINIT,
+ $client_cookie,
+ strlen($str_kex_algorithms),
+ $str_kex_algorithms,
+ strlen($str_server_host_key_algorithms),
+ $str_server_host_key_algorithms,
+ strlen($encryption_algorithms_client_to_server),
+ $encryption_algorithms_client_to_server,
+ strlen($encryption_algorithms_server_to_client),
+ $encryption_algorithms_server_to_client,
+ strlen($mac_algorithms_client_to_server),
+ $mac_algorithms_client_to_server,
+ strlen($mac_algorithms_server_to_client),
+ $mac_algorithms_server_to_client,
+ strlen($compression_algorithms_client_to_server),
+ $compression_algorithms_client_to_server,
+ strlen($compression_algorithms_server_to_client),
+ $compression_algorithms_server_to_client,
+ 0,
+ '',
+ 0,
+ '',
+ 0,
+ 0
+ );
- // CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>:
- 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key
- 'aes192-ctr', // RECOMMENDED AES with 192-bit key
- 'aes256-ctr', // RECOMMENDED AES with 256-bit key
+ if ($kexinit_payload_server === false) {
+ if (!$this->_send_binary_packet($kexinit_payload_client)) {
+ return false;
+ }
- '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
+ $kexinit_payload_server = $this->_get_binary_packet();
+ if ($kexinit_payload_server === false) {
+ $this->bitmap = 0;
+ user_error('Connection closed by server');
+ return false;
+ }
- '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 <http://www.ietf.org/rfc/rfc6668.txt>:
- 'hmac-sha2-256',// RECOMMENDED HMAC-SHA256 (digest length = key length = 32)
-
- 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
- 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20)
- 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
- 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16)
- //'none' // OPTIONAL no MAC; NOT RECOMMENDED
- );
-
- $compression_algorithms = array(
- 'none' // REQUIRED no compression
- //'zlib' // OPTIONAL ZLIB (LZ77) compression
- );
+ if (!strlen($kexinit_payload_server) || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT) {
+ user_error('Expected SSH_MSG_KEXINIT');
+ return false;
+ }
- // 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')
- ));
+ $send_kex = false;
}
- $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);
-
- $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);
+ 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;
- // 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,
- $client_cookie,
- strlen($str_kex_algorithms),
- $str_kex_algorithms,
- strlen($str_server_host_key_algorithms),
- $str_server_host_key_algorithms,
- strlen($encryption_algorithms_client_to_server),
- $encryption_algorithms_client_to_server,
- strlen($encryption_algorithms_server_to_client),
- $encryption_algorithms_server_to_client,
- strlen($mac_algorithms_client_to_server),
- $mac_algorithms_client_to_server,
- strlen($mac_algorithms_server_to_client),
- $mac_algorithms_server_to_client,
- strlen($compression_algorithms_client_to_server),
- $compression_algorithms_client_to_server,
- strlen($compression_algorithms_server_to_client),
- $compression_algorithms_server_to_client,
- 0,
- '',
- 0,
- '',
- 0,
- 0
- );
-
- if (!$this->_send_binary_packet($kexinit_payload_client)) {
+ 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');
}
// 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) {
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;
}
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);
$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
// 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) {
$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']);
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);
$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(
$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) {
$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;
$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);
$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;
$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);
$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
$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;
$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;
$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)) {
}
$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;
}
*/
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;
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
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);
}
* Login Helper
*
* @param string $username
- * @param mixed $password
- * @param mixed $...
* @return bool
* @see self::_login_helper()
* @access private
$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) {
$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;
}
$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;
/**
* Handle the keyboard-interactive requests / responses.
*
- * @param string $responses...
* @return bool
* @access private
*/
} 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++) {
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);
// 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) {
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
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;
}
* 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}
$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,
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)) {
$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)) {
$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);
}
/**
$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
*
$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;
}
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;
}
return false;
}
- $response = $this->_get_binary_packet();
- if ($response === false) {
- user_error('Connection closed by server');
- return false;
- }
-
- list(, $type) = unpack('C', $this->_string_shift($response, 1));
+ $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
- 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(
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;
* @see self::write()
* @param string $expect
* @param int $mode
- * @return string
+ * @return string|bool
* @access public
*/
function read($expect = '', $mode = self::READ_SIMPLE)
$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;
}
$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) {
*/
function write($cmd)
{
- if (!($this->bitmap & self::MASK_LOGIN)) {
+ if (!$this->isAuthenticated()) {
user_error('Operation disallowed prior to login()');
return false;
}
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
*
* @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;
}
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;
// "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;
}
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;
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');
}
}
- //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++;
$this->last_packet = $current;
}
- return $this->_filter($payload);
+ return $this->_filter($payload, $skip_channel_filter);
}
/**
* @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);
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':
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)));
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);
}
}
*/
function disablePTY()
{
+ if ($this->in_request_pty_exec) {
+ $this->_close_channel(self::CHANNEL_EXEC);
+ $this->in_request_pty_exec = false;
+ }
$this->request_pty = false;
}
*
* 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 {
// 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:
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);
$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);
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;
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);
}
}
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;
$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;
}
*
* 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)
$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]));
}
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;
+ }
+
+ $this->bitmap = 0;
+ if (is_resource($this->fsock) && get_resource_type($this->fsock) == 'stream') {
fclose($this->fsock);
- return false;
}
+
+ return false;
}
/**
* 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()
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 : '<pre>' . $log . '</pre>';
default:
return false;
}
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 <http://tools.ietf.org/html/rfc4345#section-4>:
+ 'arcfour256',
+ 'arcfour128',
+
+ //'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key
+
+ // CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>:
+ 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key
+ 'aes192-ctr', // RECOMMENDED AES with 192-bit key
+ 'aes256-ctr', // RECOMMENDED AES with 256-bit key
+
+ 'twofish128-ctr', // OPTIONAL Twofish in SDCTR mode, with 128-bit key
+ 'twofish192-ctr', // OPTIONAL Twofish with 192-bit key
+ 'twofish256-ctr', // OPTIONAL Twofish with 256-bit key
+
+ 'aes128-cbc', // RECOMMENDED AES with a 128-bit key
+ 'aes192-cbc', // OPTIONAL AES with a 192-bit key
+ 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key
+
+ 'twofish128-cbc', // OPTIONAL Twofish with a 128-bit key
+ 'twofish192-cbc', // OPTIONAL Twofish with a 192-bit key
+ 'twofish256-cbc',
+ 'twofish-cbc', // OPTIONAL alias for "twofish256-cbc"
+ // (this is being retained for historical reasons)
+
+ 'blowfish-ctr', // OPTIONAL Blowfish in SDCTR mode
+
+ 'blowfish-cbc', // OPTIONAL Blowfish in CBC mode
+
+ '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode
+
+ '3des-cbc', // REQUIRED three-key 3DES in CBC mode
+
+ //'none' // OPTIONAL no encryption; NOT RECOMMENDED
+ );
+
+ if ($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 <http://www.ietf.org/rfc/rfc6668.txt>:
+ 'hmac-sha2-256',// RECOMMENDED HMAC-SHA256 (digest length = key length = 32)
+
+ 'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
+ 'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20)
+ 'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
+ 'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16)
+ //'none' // OPTIONAL no MAC; NOT RECOMMENDED
+ );
+ }
+
+ /**
+ * Returns a list of compression algorithms that phpseclib supports
+ *
+ * @return array
+ * @access public
+ */
+ function getSupportedCompressionAlgorithms()
+ {
+ $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
+ * <https://www.php.net/manual/en/function.ssh2-connect.php>
+ *
+ * @param array $methods
+ * @access public
+ */
+ function setPreferredAlgorithms($methods)
+ {
+ $preferred = $methods;
+
+ if (isset($preferred['kex'])) {
+ $preferred['kex'] = array_intersect(
+ $preferred['kex'],
+ $this->getSupportedKEXAlgorithms()
+ );
+ }
+
+ if (isset($preferred['hostkey'])) {
+ $preferred['hostkey'] = array_intersect(
+ $preferred['hostkey'],
+ $this->getSupportedHostKeyAlgorithms()
+ );
+ }
+
+ $keys = array('client_to_server', 'server_to_client');
+ foreach ($keys as $key) {
+ if (isset($preferred[$key])) {
+ $a = &$preferred[$key];
+ if (isset($a['crypt'])) {
+ $a['crypt'] = array_intersect(
+ $a['crypt'],
+ $this->getSupportedEncryptionAlgorithms()
+ );
+ }
+ if (isset($a['comp'])) {
+ $a['comp'] = array_intersect(
+ $a['comp'],
+ $this->getSupportedCompressionAlgorithms()
+ );
+ }
+ if (isset($a['mac'])) {
+ $a['mac'] = array_intersect(
+ $a['mac'],
+ $this->getSupportedMACAlgorithms()
+ );
+ }
+ }
+ }
+
+ $keys = array(
+ 'kex',
+ 'hostkey',
+ 'client_to_server/crypt',
+ 'client_to_server/comp',
+ 'client_to_server/mac',
+ 'server_to_client/crypt',
+ 'server_to_client/comp',
+ 'server_to_client/mac',
+ );
+ foreach ($keys as $key) {
+ $p = $preferred;
+ $m = $methods;
+
+ $subkeys = explode('/', $key);
+ foreach ($subkeys as $subkey) {
+ if (!isset($p[$subkey])) {
+ continue 2;
+ }
+ $p = $p[$subkey];
+ $m = $m[$subkey];
+ }
+
+ if (count($p) != count($m)) {
+ $diff = array_diff($m, $p);
+ $msg = count($diff) == 1 ?
+ ' is not a supported algorithm' :
+ ' are not supported algorithms';
+ user_error(implode(', ', $diff) . $msg);
+ return false;
+ }
+ }
+
+ $this->preferred = $preferred;
+ }
+
/**
* Returns the banner message.
*
$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);
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);
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);
$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) {
$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;
+ }
}
*
* @package SSH\Agent
* @author Jim Wigginton <terrafrost@php.net>
- * @access internal
+ * @access public
*/
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);
$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);
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);
*/
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
*
*/
var $fsock;
+ /**
+ * Signature flags
+ *
+ * @var int
+ * @access private
+ * @see self::sign()
+ * @see self::setHash()
+ */
+ var $flags = 0;
+
/**
* Default Constructor.
*
{
}
+ /**
+ * 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
*
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;
}
}
--- /dev/null
+github: singpolyma
+liberapay: singpolyma
+patreon: singpolyma
--- /dev/null
+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''
+}
+# 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 !=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:
- - 5.3
- - 5.4
- - 5.5
- - 5.6
- - 7.0
- - hhvm
-# - nightly
-
-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'
+- '7.3'
+- '7.4'
+- '8.0'
+[![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).
-* <http://github.com/bendiken/openpgp-php>
+* <https://github.com/singpolyma/openpgp-php>
-### 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
derived from PGP (Pretty Good Privacy), first created by Phil Zimmermann in
1991.
-* <http://tools.ietf.org/html/rfc4880>
-* <http://www.openpgp.org/>
+* <https://tools.ietf.org/html/rfc4880>
+* <https://www.openpgp.org/>
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 <http://phpseclib.sourceforge.net>
-* Helper class for encrypting and decrypting messages and keys using Crypt_AES and Crypt_TripleDES from <http://phpseclib.sourceforge.net>
+* Helper class for verifying, signing, encrypting, and decrypting messages <http://phpseclib.sourceforge.net>
+* Helper class for encrypting and decrypting messages and keys using <http://phpseclib.sourceforge.net>
+ * 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:
-* <https://drupal.org/project/openpgp>
* <https://wordpress.org/plugins/wp-pgp-encrypted-emails/>
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) - <http://ar.to/>
-* [Stephen Paul Weber](mailto:singpolyma@singpolyma.net) - <http://singpolyma.net/>
+* [Arto Bendiken](mailto:arto.bendiken@gmail.com) (Original author) - <http://ar.to/>
+* [Stephen Paul Weber](mailto:singpolyma@singpolyma.net) (Maintainer) - <https://singpolyma.net/>
License
-------
OpenPGP.php is free and unencumbered public domain software. For more
-information, see <http://unlicense.org/> or the accompanying UNLICENSE file.
+information, see <https://unlicense.org/> or the accompanying UNLICENSE file.
}
],
"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/"]
--- /dev/null
+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
+```
--- /dev/null
+<?php
+@include_once dirname(__FILE__).'/../vendor/autoload.php';
+require_once dirname(__FILE__).'/../lib/openpgp.php';
+require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php';
+
+$recipientPublicKey = OpenPGP_Message::parse(OpenPGP::unarmor(file_get_contents('public.asc'), 'PGP PUBLIC KEY BLOCK'));
+
+$encryptedPrivateKey = OpenPGP_Message::parse(OpenPGP::unarmor(file_get_contents('sekret.asc'), 'PGP PRIVATE KEY BLOCK'));
+$privateKeyPassphrase = 'test';
+$key = OpenPGP_Crypt_Symmetric::decryptSecretKey($privateKeyPassphrase, $encryptedPrivateKey[0]);
+
+$signer = new OpenPGP_Crypt_RSA($key);
+$data = new OpenPGP_LiteralDataPacket("some text\n", ['format' => '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');
+
+
<?php
+@include_once dirname(__FILE__).'/../vendor/autoload.php';
require_once dirname(__FILE__).'/../lib/openpgp.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php';
// trailing whitespace to lines.
echo preg_replace("/^-/", "- -", $packets[0]->data)."\n";
echo OpenPGP::enarmor($packets[1][0]->to_bytes(), "PGP SIGNATURE");
-
-?>
// 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';
<?php
+@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';
<?php
+@include_once dirname(__FILE__).'/../vendor/autoload.php';
require_once dirname(__FILE__).'/../lib/openpgp.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php';
--- /dev/null
+<?php
+
+@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';
+
+$rsa = new \phpseclib\Crypt\RSA();
+$k = $rsa->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 <test@example.com>');
+
+$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();
--- /dev/null
+<?php
+
+@include_once dirname(__FILE__).'/../vendor/autoload.php';
+require_once dirname(__FILE__).'/../lib/openpgp.php';
+require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php';
+
+// Key length: 512, 1024, 2048, 3072, 4096
+$key_length = 512;
+
+// Generate a master signing key
+
+$rsa = new \phpseclib\Crypt\RSA();
+$k = $rsa->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
<?php
+@include_once dirname(__FILE__).'/../vendor/autoload.php';
require_once dirname(__FILE__).'/../lib/openpgp.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php';
/* Output the raw message bytes to STDOUT */
echo $m->to_bytes();
-
-?>
<?php
+@include_once dirname(__FILE__).'/../vendor/autoload.php';
require_once dirname(__FILE__).'/../lib/openpgp.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php';
/* Dump verification information to STDOUT */
var_dump($verify->verify($m));
-
-?>
* (RFC 4880).
*
* @package OpenPGP
- * @version 0.3.0
+ * @version 0.5.0
* @author Arto Bendiken <arto.bendiken@gmail.com>
* @author Stephen Paul Weber <singpolyma@singpolyma.net>
* @see http://github.com/bendiken/openpgp-php
* @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
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;
$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));
}
}
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;
}
$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));
}
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) {
$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());
$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);
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();
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;
}
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() {
$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;
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;
}
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;
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);
$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) {
}
}
+ 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') {
<?php
-use phpseclib\Crypt\TripleDES as Crypt_TripleDES;
use phpseclib\Crypt\AES as Crypt_AES;
+use phpseclib\Crypt\Blowfish as Crypt_Blowfish;
+use phpseclib\Crypt\TripleDES as Crypt_TripleDES;
+use phpseclib\Crypt\Twofish as Crypt_Twofish;
use phpseclib\Crypt\Random;
-define('CRYPT_DES_MODE_CFB', Crypt_TripleDES::MODE_CFB);
-define('CRYPT_AES_MODE_CFB', Crypt_AES::MODE_CFB);
-
require_once dirname(__FILE__).'/openpgp.php';
@include_once dirname(__FILE__).'/openpgp_crypt_rsa.php';
@include_once dirname(__FILE__).'/openpgp_mcrypt_wrapper.php';
+@include_once dirname(__FILE__).'/openpgp_openssl_wrapper.php';
class OpenPGP_Crypt_Symmetric {
public static function encrypt($passphrases_and_keys, $message, $symmetric_algorithm=9) {
$esk = pack('n', OpenPGP::bitlength($esk)) . $esk;
array_unshift($encrypted, new OpenPGP_AsymmetricSessionKeyPacket($pass->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));
$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));
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
if($chk != $mkChk) return NULL;
}
+ $packet->s2k = NULL;
$packet->s2k_useage = 0;
$packet->symmetric_algorithm = 0;
$packet->encrypted_data = NULL;
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
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;
}
--- /dev/null
+<?php
+
+if(function_exists('openssl_encrypt')) {
+ class OpenSSLWrapper {
+ public $cipher, $key, $iv, $key_size, $block_size;
+
+
+ function __construct($cipher) {
+ if($cipher != "CAST5-CFB") throw Exception("OpenSSLWrapper is only used for CAST5 right now");
+
+ $this->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);
+ }
+ }
+}
<file>tests/suite.php</file>
</testsuite>
+ <testsuite name="Signature">
+ <file>tests/suite.php</file>
+ </testsuite>
+
<testsuite name="MessageVerification">
<file>tests/phpseclib_suite.php</file>
</testsuite>
--- /dev/null
+\8c\r\ 4\ 4\ 3\ 2óè¢h\9c±Ï³ÖÉ\1afuÜhè\82ý\92sÅ\17\7fþ®Ùø\ f°¼þ_VF\954Ó
\ No newline at end of file
--- /dev/null
+\8c\r\ 4
+\ 3\ 2cýÐ\86\vèÑÔÖÒ9\ 1=õÇâ]¼TföA\v¼c«vìåeøkº\80Èʲõ¡©n}%.\16\90l\18\16òë\19uÛ?\êåI
+ð[øõblÊ
\ No newline at end of file
<?php
+use PHPUnit\Framework\TestCase;
/* The tests which require phpseclib */
require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_symmetric.php';
-class MessageVerification extends PHPUnit_Framework_TestCase {
+class MessageVerification extends TestCase {
public function oneMessageRSA($pkey, $path) {
$pkeyM = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $pkey));
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
}
-class KeyVerification extends PHPUnit_Framework_TestCase {
+class KeyVerification extends TestCase {
public function oneKeyRSA($path) {
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
$verify = new OpenPGP_Crypt_RSA($m);
}
-class Decryption extends PHPUnit_Framework_TestCase {
+class Decryption extends TestCase {
public function oneSymmetric($pass, $cnt, $path) {
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
$m2 = OpenPGP_Crypt_Symmetric::decryptSymmetric($pass, $m);
}
}
- public function testDecryptAES() {
- $this->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");
}
}
}
+ 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.');
<?php
+use PHPUnit\Framework\TestCase;
require_once dirname(__FILE__).'/../lib/openpgp.php';
-class Serialization extends PHPUnit_Framework_TestCase {
+class Serialization extends TestCase {
public function oneSerialization($path) {
$in = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
$mid = $in->to_bytes();
$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");
}
}
}
-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);
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");
+ }
}