Merge pull request #1208 from MrPetovan/task/10839-securemail-php8-compat

[securemail] Upgrade Composer dependencies to fix PHP8 issue
This commit is contained in:
Michael Vogel 2021-11-16 22:04:54 +01:00 committed by GitHub
commit a309d2bf1b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
65 changed files with 5407 additions and 3230 deletions

View file

@ -11,7 +11,7 @@
], ],
"require": { "require": {
"phpseclib/phpseclib": "^2.0", "phpseclib/phpseclib": "^2.0",
"singpolyma/openpgp-php": "^0.3.0" "singpolyma/openpgp-php": "^0.5.0"
}, },
"license": "AGPL-3.0+", "license": "AGPL-3.0+",
"minimum-stability": "stable", "minimum-stability": "stable",

View file

@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "b3a33a493eae34d9245ca7cc1b4572ea", "content-hash": "159d6e7134aeceeb05fdf60ef672f3a3",
"packages": [ "packages": [
{ {
"name": "phpseclib/phpseclib", "name": "phpseclib/phpseclib",
"version": "2.0.4", "version": "2.0.34",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpseclib/phpseclib.git", "url": "https://github.com/phpseclib/phpseclib.git",
"reference": "ab8028c93c03cc8d9c824efa75dc94f1db2369bf" "reference": "98a6fe587f3481aea319eef7e656d02cfe1675ec"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/ab8028c93c03cc8d9c824efa75dc94f1db2369bf", "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/98a6fe587f3481aea319eef7e656d02cfe1675ec",
"reference": "ab8028c93c03cc8d9c824efa75dc94f1db2369bf", "reference": "98a6fe587f3481aea319eef7e656d02cfe1675ec",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -25,8 +25,7 @@
}, },
"require-dev": { "require-dev": {
"phing/phing": "~2.7", "phing/phing": "~2.7",
"phpunit/phpunit": "~4.0", "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4",
"sami/sami": "~2.0",
"squizlabs/php_codesniffer": "~2.0" "squizlabs/php_codesniffer": "~2.0"
}, },
"suggest": { "suggest": {
@ -96,27 +95,49 @@
"x.509", "x.509",
"x509" "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", "name": "singpolyma/openpgp-php",
"version": "0.3.0", "version": "0.5.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/singpolyma/openpgp-php.git", "url": "https://github.com/singpolyma/openpgp-php.git",
"reference": "6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8" "reference": "69292f6a46ed7f687083bfb8974b161a41ab213c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/singpolyma/openpgp-php/zipball/6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8", "url": "https://api.github.com/repos/singpolyma/openpgp-php/zipball/69292f6a46ed7f687083bfb8974b161a41ab213c",
"reference": "6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8", "reference": "69292f6a46ed7f687083bfb8974b161a41ab213c",
"shasum": "" "shasum": ""
}, },
"require": { "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": { "require-dev": {
"phpunit/phpunit": "~4.0" "phpunit/phpunit": "^9.0"
},
"suggest": {
"ext-mcrypt": "required if you use encryption cast5"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
@ -139,7 +160,25 @@
} }
], ],
"description": "Pure-PHP implementation of the OpenPGP Message Format (RFC 4880)", "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": [], "packages-dev": [],
@ -149,5 +188,6 @@
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
"platform": [], "platform": [],
"platform-dev": [] "platform-dev": [],
"plugin-api-version": "2.0.0"
} }

View file

@ -37,8 +37,8 @@ namespace Composer\Autoload;
* *
* @author Fabien Potencier <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/ * @see https://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/ * @see https://www.php-fig.org/psr/psr-4/
*/ */
class ClassLoader class ClassLoader
{ {
@ -60,7 +60,7 @@ class ClassLoader
public function getPrefixes() public function getPrefixes()
{ {
if (!empty($this->prefixesPsr0)) { 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(); return array();
@ -279,7 +279,7 @@ class ClassLoader
*/ */
public function setApcuPrefix($apcuPrefix) 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;
} }
/** /**

View file

@ -0,0 +1,227 @@
<?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;
}
}

View file

@ -6,6 +6,7 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'MCryptWrapper' => $vendorDir . '/singpolyma/openpgp-php/lib/openpgp_mcrypt_wrapper.php', 'MCryptWrapper' => $vendorDir . '/singpolyma/openpgp-php/lib/openpgp_mcrypt_wrapper.php',
'OpenPGP' => $vendorDir . '/singpolyma/openpgp-php/lib/openpgp.php', 'OpenPGP' => $vendorDir . '/singpolyma/openpgp-php/lib/openpgp.php',
'OpenPGP_AsymmetricSessionKeyPacket' => $vendorDir . '/singpolyma/openpgp-php/lib/openpgp.php', 'OpenPGP_AsymmetricSessionKeyPacket' => $vendorDir . '/singpolyma/openpgp-php/lib/openpgp.php',
@ -55,4 +56,5 @@ return array(
'OpenPGP_TrustPacket' => $vendorDir . '/singpolyma/openpgp-php/lib/openpgp.php', 'OpenPGP_TrustPacket' => $vendorDir . '/singpolyma/openpgp-php/lib/openpgp.php',
'OpenPGP_UserAttributePacket' => $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', 'OpenPGP_UserIDPacket' => $vendorDir . '/singpolyma/openpgp-php/lib/openpgp.php',
'OpenSSLWrapper' => $vendorDir . '/singpolyma/openpgp-php/lib/openpgp_openssl_wrapper.php',
); );

View file

@ -13,19 +13,24 @@ class ComposerAutoloaderInitSecuremailAddon
} }
} }
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader() public static function getLoader()
{ {
if (null !== self::$loader) { if (null !== self::$loader) {
return self::$loader; return self::$loader;
} }
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInitSecuremailAddon', 'loadClassLoader'), true, true); spl_autoload_register(array('ComposerAutoloaderInitSecuremailAddon', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(); self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInitSecuremailAddon', 'loadClassLoader')); spl_autoload_unregister(array('ComposerAutoloaderInitSecuremailAddon', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) { if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php'; require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitSecuremailAddon::getInitializer($loader)); call_user_func(\Composer\Autoload\ComposerStaticInitSecuremailAddon::getInitializer($loader));
} else { } else {

View file

@ -25,6 +25,7 @@ class ComposerStaticInitSecuremailAddon
); );
public static $classMap = array ( public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'MCryptWrapper' => __DIR__ . '/..' . '/singpolyma/openpgp-php/lib/openpgp_mcrypt_wrapper.php', 'MCryptWrapper' => __DIR__ . '/..' . '/singpolyma/openpgp-php/lib/openpgp_mcrypt_wrapper.php',
'OpenPGP' => __DIR__ . '/..' . '/singpolyma/openpgp-php/lib/openpgp.php', 'OpenPGP' => __DIR__ . '/..' . '/singpolyma/openpgp-php/lib/openpgp.php',
'OpenPGP_AsymmetricSessionKeyPacket' => __DIR__ . '/..' . '/singpolyma/openpgp-php/lib/openpgp.php', 'OpenPGP_AsymmetricSessionKeyPacket' => __DIR__ . '/..' . '/singpolyma/openpgp-php/lib/openpgp.php',
@ -74,6 +75,7 @@ class ComposerStaticInitSecuremailAddon
'OpenPGP_TrustPacket' => __DIR__ . '/..' . '/singpolyma/openpgp-php/lib/openpgp.php', 'OpenPGP_TrustPacket' => __DIR__ . '/..' . '/singpolyma/openpgp-php/lib/openpgp.php',
'OpenPGP_UserAttributePacket' => __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', '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) public static function getInitializer(ClassLoader $loader)

View file

@ -1,17 +1,18 @@
[ {
"packages": [
{ {
"name": "phpseclib/phpseclib", "name": "phpseclib/phpseclib",
"version": "2.0.4", "version": "2.0.34",
"version_normalized": "2.0.4.0", "version_normalized": "2.0.34.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpseclib/phpseclib.git", "url": "https://github.com/phpseclib/phpseclib.git",
"reference": "ab8028c93c03cc8d9c824efa75dc94f1db2369bf" "reference": "98a6fe587f3481aea319eef7e656d02cfe1675ec"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/ab8028c93c03cc8d9c824efa75dc94f1db2369bf", "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/98a6fe587f3481aea319eef7e656d02cfe1675ec",
"reference": "ab8028c93c03cc8d9c824efa75dc94f1db2369bf", "reference": "98a6fe587f3481aea319eef7e656d02cfe1675ec",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -19,8 +20,7 @@
}, },
"require-dev": { "require-dev": {
"phing/phing": "~2.7", "phing/phing": "~2.7",
"phpunit/phpunit": "~4.0", "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4",
"sami/sami": "~2.0",
"squizlabs/php_codesniffer": "~2.0" "squizlabs/php_codesniffer": "~2.0"
}, },
"suggest": { "suggest": {
@ -29,7 +29,7 @@
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
"ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
}, },
"time": "2016-10-04T00:57:04+00:00", "time": "2021-10-27T02:46:30+00:00",
"type": "library", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
@ -91,30 +91,53 @@
"twofish", "twofish",
"x.509", "x.509",
"x509" "x509"
] ],
"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"
}, },
{ {
"name": "singpolyma/openpgp-php", "name": "singpolyma/openpgp-php",
"version": "0.3.0", "version": "0.5.0",
"version_normalized": "0.3.0.0", "version_normalized": "0.5.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/singpolyma/openpgp-php.git", "url": "https://github.com/singpolyma/openpgp-php.git",
"reference": "6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8" "reference": "69292f6a46ed7f687083bfb8974b161a41ab213c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/singpolyma/openpgp-php/zipball/6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8", "url": "https://api.github.com/repos/singpolyma/openpgp-php/zipball/69292f6a46ed7f687083bfb8974b161a41ab213c",
"reference": "6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8", "reference": "69292f6a46ed7f687083bfb8974b161a41ab213c",
"shasum": "" "shasum": ""
}, },
"require": { "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": { "require-dev": {
"phpunit/phpunit": "~4.0" "phpunit/phpunit": "^9.0"
}, },
"time": "2017-04-12T21:23:15+00:00", "suggest": {
"ext-mcrypt": "required if you use encryption cast5"
},
"time": "2021-05-26T00:35:20+00:00",
"type": "library", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
@ -136,6 +159,28 @@
"email": "singpolyma@singpolyma.net" "email": "singpolyma@singpolyma.net"
} }
], ],
"description": "Pure-PHP implementation of the OpenPGP Message Format (RFC 4880)" "description": "Pure-PHP implementation of the OpenPGP Message Format (RFC 4880)",
"support": {
"issues": "https://github.com/singpolyma/openpgp-php/issues",
"source": "https://github.com/singpolyma/openpgp-php/tree/0.5.0"
},
"funding": [
{
"url": "https://github.com/singpolyma",
"type": "github"
},
{
"url": "https://liberapay.com/singpolyma",
"type": "liberapay"
},
{
"url": "https://www.patreon.com/singpolyma",
"type": "patreon"
}
],
"install-path": "../singpolyma/openpgp-php"
}
],
"dev": true,
"dev-package-names": []
} }
]

View file

@ -0,0 +1,42 @@
<?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',
),
),
);

View file

@ -0,0 +1,26 @@
<?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
);
}

View file

@ -0,0 +1,8 @@
# Backers
phpseclib ongoing development is made possible by [Tidelift](https://tidelift.com/subscription/pkg/packagist-phpseclib-phpseclib?utm_source=packagist-phpseclib-phpseclib&utm_medium=referral&utm_campaign=readme) and by contributions by users like you. Thank you.
## Backers
- Zane Hooper
- [Setasign](https://www.setasign.com/)

View file

@ -1,5 +1,4 @@
Copyright 2007-2016 TerraFrost and other contributors Copyright (c) 2011-2019 TerraFrost and other contributors
http://phpseclib.sourceforge.net/
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the

View file

@ -1,22 +1,62 @@
# phpseclib - PHP Secure Communications Library # 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 ## Supporting phpseclib
arithmetic library, fully PKCS#1 (v2.1) compliant RSA, DES, 3DES, RC4, Rijndael,
AES, Blowfish, Twofish, SSH-1, SSH-2, SFTP, and X.509 - [Become a backer or sponsor on Patreon](https://www.patreon.com/phpseclib)
- [One-time donation via PayPal or crypto-currencies](http://sourceforge.net/donate/index.php?group_id=198487)
- [Subscribe to Tidelift](https://tidelift.com/subscription/pkg/packagist-phpseclib-phpseclib?utm_source=packagist-phpseclib-phpseclib&utm_medium=referral&utm_campaign=readme)
## Introduction
MIT-licensed pure-PHP implementations of the following:
SSH-2, SFTP, X.509, an arbitrary-precision integer arithmetic library, Ed25519 / Ed449 / Curve25519 / Curve449, ECDSA / ECDH (with support for 66 curves), RSA (PKCS#1 v2.2 compliant), DSA / DH, DES / 3DES / RC4 / Rijndael / AES / Blowfish / Twofish / Salsa20 / ChaCha20, GCM / Poly1305
* [Download (1.0.4)](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.4.zip/download)
* [Browse Git](https://github.com/phpseclib/phpseclib) * [Browse Git](https://github.com/phpseclib/phpseclib)
* [Code Coverage Report](http://phpseclib.bantux.org/code_coverage/2.0/latest/)
<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)
## Documentation ## Documentation
* [Documentation / Manual](http://phpseclib.sourceforge.net/) * [Documentation / Manual](https://phpseclib.com/)
* [API Documentation](http://phpseclib.bantux.org/api/2.0/) (generated by Sami) * [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 ## Support
@ -26,40 +66,29 @@ Need Support?
* [Create a Support Ticket on GitHub](https://github.com/phpseclib/phpseclib/issues/new) * [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) * [Browse the Support Forum](http://www.frostjedi.com/phpbb/viewforum.php?f=46) (no longer in use)
## Installing Development Dependencies
Dependencies are managed via Composer.
1. Download the [`composer.phar`](https://getcomposer.org/composer.phar) executable as per the
[Composer Download Instructions](https://getcomposer.org/download/), e.g. by running
``` sh
curl -sS https://getcomposer.org/installer | php
```
2. Install Dependencies
``` sh
php composer.phar install
```
## Contributing ## Contributing
1. Fork the Project 1. Fork the Project
2. Install Development Dependencies 2. Ensure you have Composer installed (see [Composer Download Instructions](https://getcomposer.org/download/))
3. Create a Feature Branch 3. Install Development Dependencies
4. (Recommended) Run the Test Suite ``` sh
composer install
```
4. Create a Feature Branch
5. (Recommended) Run the Test Suite
``` sh ``` sh
vendor/bin/phpunit 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 ``` sh
vendor/bin/phing -f build/build.xml sniff vendor/bin/phing -f build/build.xml sniff
``` ```
6. Send us a Pull Request 7. Send us a Pull Request

View file

@ -0,0 +1,27 @@
build: false
shallow_clone: false
platform:
- x86
- x64
clone_folder: C:\projects\phpseclib
install:
- cinst -y OpenSSL.Light
- SET PATH=C:\Program Files\OpenSSL;%PATH%
- sc config wuauserv start= auto
- net start wuauserv
- cinst -y php --version 5.6.30
- cd c:\tools\php56
- copy php.ini-production php.ini
- echo date.timezone="UTC" >> php.ini
- echo extension_dir=ext >> php.ini
- echo extension=php_openssl.dll >> php.ini
- echo extension=php_gmp.dll >> php.ini
- cd C:\projects\phpseclib
- SET PATH=C:\tools\php56;%PATH%
- php.exe -r "readfile('http://getcomposer.org/installer');" | php.exe
- php.exe composer.phar install --prefer-source --no-interaction
test_script:
- cd C:\projects\phpseclib
- vendor\bin\phpunit.bat tests/Windows32Test.php

View file

@ -55,8 +55,7 @@
}, },
"require-dev": { "require-dev": {
"phing/phing": "~2.7", "phing/phing": "~2.7",
"phpunit/phpunit": "~4.0", "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4",
"sami/sami": "~2.0",
"squizlabs/php_codesniffer": "~2.0" "squizlabs/php_codesniffer": "~2.0"
}, },
"suggest": { "suggest": {

File diff suppressed because it is too large Load diff

View file

@ -76,6 +76,10 @@ abstract class Base
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
*/ */
const MODE_CFB = 3; const MODE_CFB = 3;
/**
* Encrypt / decrypt using the Cipher Feedback mode (8bit)
*/
const MODE_CFB8 = 38;
/** /**
* Encrypt / decrypt using the Output Feedback mode. * Encrypt / decrypt using the Output Feedback mode.
* *
@ -409,7 +413,7 @@ abstract class Base
* @var mixed * @var mixed
* @access private * @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 * If OpenSSL can be used in ECB but not in CTR we can emulate CTR
@ -479,6 +483,7 @@ abstract class Base
break; break;
case self::MODE_CTR: case self::MODE_CTR:
case self::MODE_CFB: case self::MODE_CFB:
case self::MODE_CFB8:
case self::MODE_OFB: case self::MODE_OFB:
case self::MODE_STREAM: case self::MODE_STREAM:
$this->mode = $mode; $this->mode = $mode;
@ -490,11 +495,6 @@ abstract class Base
} }
$this->_setEngine(); $this->_setEngine();
// Determining whether inline crypting can be used by the cipher
if ($this->use_inline_crypt !== false && function_exists('create_function')) {
$this->use_inline_crypt = true;
}
} }
/** /**
@ -644,10 +644,10 @@ abstract class Base
case !function_exists('hash_algos'): case !function_exists('hash_algos'):
case !in_array($hash, hash_algos()): case !in_array($hash, hash_algos()):
$i = 1; $i = 1;
while (strlen($key) < $dkLen) {
$hmac = new Hash(); $hmac = new Hash();
$hmac->setHash($hash); $hmac->setHash($hash);
$hmac->setKey($password); $hmac->setKey($password);
while (strlen($key) < $dkLen) {
$f = $u = $hmac->hash($salt . pack('N', $i++)); $f = $u = $hmac->hash($salt . pack('N', $i++));
for ($j = 2; $j <= $count; ++$j) { for ($j = 2; $j <= $count; ++$j) {
$u = $hmac->hash($u); $u = $hmac->hash($u);
@ -702,7 +702,7 @@ abstract class Base
case self::MODE_STREAM: case self::MODE_STREAM:
return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
case self::MODE_ECB: 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; return !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result;
case self::MODE_CBC: case self::MODE_CBC:
$result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV); $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV);
@ -762,6 +762,16 @@ abstract class Base
$iv = substr($ciphertext, -$this->block_size); $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; return $ciphertext;
case self::MODE_OFB: case self::MODE_OFB:
return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer); return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer);
@ -769,12 +779,14 @@ abstract class Base
} }
if ($this->engine === self::ENGINE_MCRYPT) { if ($this->engine === self::ENGINE_MCRYPT) {
set_error_handler(array($this, 'do_nothing'));
if ($this->changed) { if ($this->changed) {
$this->_setupMcrypt(); $this->_setupMcrypt();
$this->changed = false; $this->changed = false;
} }
if ($this->enchanged) { if ($this->enchanged) {
@mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
$this->enchanged = false; $this->enchanged = false;
} }
@ -807,15 +819,15 @@ abstract class Base
if ($len >= $block_size) { if ($len >= $block_size) {
if ($this->enbuffer['enmcrypt_init'] === false || $len > $this->cfb_init_len) { if ($this->enbuffer['enmcrypt_init'] === false || $len > $this->cfb_init_len) {
if ($this->enbuffer['enmcrypt_init'] === true) { 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; $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); $iv = substr($ciphertext, -$block_size);
$len%= $block_size; $len%= $block_size;
} else { } else {
while ($len >= $block_size) { 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; $ciphertext.= $iv;
$len-= $block_size; $len-= $block_size;
$i+= $block_size; $i+= $block_size;
@ -824,22 +836,26 @@ abstract class Base
} }
if ($len) { if ($len) {
$iv = @mcrypt_generic($this->ecb, $iv); $iv = mcrypt_generic($this->ecb, $iv);
$block = $iv ^ substr($plaintext, -$len); $block = $iv ^ substr($plaintext, -$len);
$iv = substr_replace($iv, $block, 0, $len); $iv = substr_replace($iv, $block, 0, $len);
$ciphertext.= $block; $ciphertext.= $block;
$pos = $len; $pos = $len;
} }
restore_error_handler();
return $ciphertext; return $ciphertext;
} }
$ciphertext = @mcrypt_generic($this->enmcrypt, $plaintext); $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
if (!$this->continuousBuffer) { 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; return $ciphertext;
} }
@ -942,6 +958,24 @@ abstract class Base
$pos = $len; $pos = $len;
} }
break; 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: case self::MODE_OFB:
$xor = $this->encryptIV; $xor = $this->encryptIV;
if (strlen($buffer['xor'])) { if (strlen($buffer['xor'])) {
@ -1007,14 +1041,14 @@ abstract class Base
break; break;
case self::MODE_ECB: case self::MODE_ECB:
if (!defined('OPENSSL_RAW_DATA')) { 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); $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
break; break;
case self::MODE_CBC: case self::MODE_CBC:
if (!defined('OPENSSL_RAW_DATA')) { if (!defined('OPENSSL_RAW_DATA')) {
$padding = str_repeat(chr($this->block_size), $this->block_size) ^ substr($ciphertext, -$this->block_size); $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; $offset = 2 * $this->block_size;
} else { } else {
$offset = $this->block_size; $offset = $this->block_size;
@ -1072,6 +1106,16 @@ abstract class Base
$iv = substr($ciphertext, -$this->block_size); $iv = substr($ciphertext, -$this->block_size);
} }
break; 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: case self::MODE_OFB:
$plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer); $plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer);
} }
@ -1080,13 +1124,14 @@ abstract class Base
} }
if ($this->engine === self::ENGINE_MCRYPT) { if ($this->engine === self::ENGINE_MCRYPT) {
set_error_handler(array($this, 'do_nothing'));
$block_size = $this->block_size; $block_size = $this->block_size;
if ($this->changed) { if ($this->changed) {
$this->_setupMcrypt(); $this->_setupMcrypt();
$this->changed = false; $this->changed = false;
} }
if ($this->dechanged) { if ($this->dechanged) {
@mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
$this->dechanged = false; $this->dechanged = false;
} }
@ -1114,26 +1159,30 @@ abstract class Base
} }
if ($len >= $block_size) { if ($len >= $block_size) {
$cb = substr($ciphertext, $i, $len - $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); $iv = substr($cb, -$block_size);
$len%= $block_size; $len%= $block_size;
} }
if ($len) { if ($len) {
$iv = @mcrypt_generic($this->ecb, $iv); $iv = mcrypt_generic($this->ecb, $iv);
$plaintext.= $iv ^ substr($ciphertext, -$len); $plaintext.= $iv ^ substr($ciphertext, -$len);
$iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len); $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len);
$pos = $len; $pos = $len;
} }
restore_error_handler();
return $plaintext; return $plaintext;
} }
$plaintext = @mdecrypt_generic($this->demcrypt, $ciphertext); $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
if (!$this->continuousBuffer) { 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; return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
} }
@ -1235,6 +1284,24 @@ abstract class Base
$pos = $len; $pos = $len;
} }
break; 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: case self::MODE_OFB:
$xor = $this->decryptIV; $xor = $this->decryptIV;
if (strlen($buffer['xor'])) { if (strlen($buffer['xor'])) {
@ -1297,7 +1364,7 @@ abstract class Base
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size); $block = substr($plaintext, $i, $block_size);
if (strlen($block) > strlen($buffer['ciphertext'])) { 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; $result = !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result;
$buffer['ciphertext'].= $result; $buffer['ciphertext'].= $result;
} }
@ -1308,7 +1375,7 @@ abstract class Base
} else { } else {
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($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; $otp = !defined('OPENSSL_RAW_DATA') ? substr($otp, 0, -$this->block_size) : $otp;
$this->_increment_str($xor); $this->_increment_str($xor);
$ciphertext.= $block ^ $otp; $ciphertext.= $block ^ $otp;
@ -1352,7 +1419,7 @@ abstract class Base
} }
if ($this->continuousBuffer) { if ($this->continuousBuffer) {
if (!defined('OPENSSL_RAW_DATA')) { 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); $encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
if ($overflow) { if ($overflow) {
@ -1435,6 +1502,8 @@ abstract class Base
return 'ctr'; return 'ctr';
case self::MODE_CFB: case self::MODE_CFB:
return 'cfb'; return 'cfb';
case self::MODE_CFB8:
return 'cfb8';
case self::MODE_OFB: case self::MODE_OFB:
return 'ofb'; return 'ofb';
} }
@ -1591,9 +1660,12 @@ abstract class Base
} }
return false; return false;
case self::ENGINE_MCRYPT: case self::ENGINE_MCRYPT:
return $this->cipher_name_mcrypt && set_error_handler(array($this, 'do_nothing'));
$result = $this->cipher_name_mcrypt &&
extension_loaded('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: case self::ENGINE_INTERNAL:
return true; return true;
} }
@ -1670,17 +1742,19 @@ abstract class Base
} }
if ($this->engine != self::ENGINE_MCRYPT && $this->enmcrypt) { 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, // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed,
// (re)open them with the module named in $this->cipher_name_mcrypt // (re)open them with the module named in $this->cipher_name_mcrypt
@mcrypt_module_close($this->enmcrypt); mcrypt_module_close($this->enmcrypt);
@mcrypt_module_close($this->demcrypt); mcrypt_module_close($this->demcrypt);
$this->enmcrypt = null; $this->enmcrypt = null;
$this->demcrypt = null; $this->demcrypt = null;
if ($this->ecb) { if ($this->ecb) {
@mcrypt_module_close($this->ecb); mcrypt_module_close($this->ecb);
$this->ecb = null; $this->ecb = null;
} }
restore_error_handler();
} }
$this->changed = true; $this->changed = true;
@ -1788,23 +1862,24 @@ abstract class Base
self::MODE_ECB => MCRYPT_MODE_ECB, self::MODE_ECB => MCRYPT_MODE_ECB,
self::MODE_CBC => MCRYPT_MODE_CBC, self::MODE_CBC => MCRYPT_MODE_CBC,
self::MODE_CFB => 'ncfb', self::MODE_CFB => 'ncfb',
self::MODE_CFB8 => MCRYPT_MODE_CFB,
self::MODE_OFB => MCRYPT_MODE_NOFB, self::MODE_OFB => MCRYPT_MODE_NOFB,
self::MODE_STREAM => MCRYPT_MODE_STREAM, self::MODE_STREAM => MCRYPT_MODE_STREAM,
); );
$this->demcrypt = @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], ''); $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() // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer()
// to workaround mcrypt's broken ncfb implementation in buffered mode // to workaround mcrypt's broken ncfb implementation in buffered mode
// see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps}
if ($this->mode == self::MODE_CFB) { 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? } // else should mcrypt_generic_deinit be called?
if ($this->mode == self::MODE_CFB) { if ($this->mode == self::MODE_CFB) {
@mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size)); mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size));
} }
} }
@ -2359,6 +2434,52 @@ abstract class Base
$_pos = $_len; $_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; return $_plaintext;
'; ';
break; break;
@ -2492,7 +2613,8 @@ abstract class Base
} }
// Create the $inline function and return its name as string. Ready to run! // Create the $inline function and return its name as string. Ready to run!
return create_function('$_action, &$self, $_text', $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' }'); eval('$func = function ($_action, &$self, $_text) { ' . $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' } };');
return $func;
} }
/** /**
@ -2521,7 +2643,7 @@ abstract class Base
* *
* @see self::_setupInlineCrypt() * @see self::_setupInlineCrypt()
* @access private * @access private
* @param $bytes * @param string $bytes
* @return string * @return string
*/ */
function _hashInlineCryptFunction($bytes) function _hashInlineCryptFunction($bytes)
@ -2550,4 +2672,53 @@ abstract class Base
return $result . pack('H*', sha1($hash)); 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()
{
}
} }

View file

@ -294,7 +294,7 @@ class Blowfish extends Base
function setKeyLength($length) function setKeyLength($length)
{ {
if ($length < 32) { if ($length < 32) {
$this->key_length = 7; $this->key_length = 4;
} elseif ($length > 448) { } elseif ($length > 448) {
$this->key_length = 56; $this->key_length = 56;
} else { } else {
@ -317,7 +317,10 @@ class Blowfish extends Base
function isValidEngine($engine) function isValidEngine($engine)
{ {
if ($engine == self::ENGINE_OPENSSL) { 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; return false;
} }
$this->cipher_name_openssl_ecb = 'bf-ecb'; $this->cipher_name_openssl_ecb = 'bf-ecb';
@ -405,16 +408,14 @@ class Blowfish extends Base
for ($i = 0; $i < 16; $i+= 2) { for ($i = 0; $i < 16; $i+= 2) {
$l^= $p[$i]; $l^= $p[$i];
$r^= ($sb_0[$l >> 24 & 0xff] + $r^= $this->safe_intval(($this->safe_intval($sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^
$sb_1[$l >> 16 & 0xff] ^
$sb_2[$l >> 8 & 0xff]) + $sb_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff]; $sb_3[$l & 0xff]);
$r^= $p[$i + 1]; $r^= $p[$i + 1];
$l^= ($sb_0[$r >> 24 & 0xff] + $l^= $this->safe_intval(($this->safe_intval($sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^
$sb_1[$r >> 16 & 0xff] ^
$sb_2[$r >> 8 & 0xff]) + $sb_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff]; $sb_3[$r & 0xff]);
} }
return pack("N*", $r ^ $p[17], $l ^ $p[16]); return pack("N*", $r ^ $p[17], $l ^ $p[16]);
} }
@ -440,16 +441,14 @@ class Blowfish extends Base
for ($i = 17; $i > 2; $i-= 2) { for ($i = 17; $i > 2; $i-= 2) {
$l^= $p[$i]; $l^= $p[$i];
$r^= ($sb_0[$l >> 24 & 0xff] + $r^= $this->safe_intval(($this->safe_intval($sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^
$sb_1[$l >> 16 & 0xff] ^
$sb_2[$l >> 8 & 0xff]) + $sb_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff]; $sb_3[$l & 0xff]);
$r^= $p[$i - 1]; $r^= $p[$i - 1];
$l^= ($sb_0[$r >> 24 & 0xff] + $l^= $this->safe_intval(($this->safe_intval($sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^
$sb_1[$r >> 16 & 0xff] ^
$sb_2[$r >> 8 & 0xff]) + $sb_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff]; $sb_3[$r & 0xff]);
} }
return pack("N*", $r ^ $p[0], $l ^ $p[1]); return pack("N*", $r ^ $p[0], $l ^ $p[1]);
} }
@ -475,6 +474,8 @@ class Blowfish extends Base
$code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
} }
$safeint = $this->safe_intval_inline();
if (!isset($lambda_functions[$code_hash])) { if (!isset($lambda_functions[$code_hash])) {
switch (true) { switch (true) {
case $gen_hi_opt_code: case $gen_hi_opt_code:
@ -510,16 +511,14 @@ class Blowfish extends Base
for ($i = 0; $i < 16; $i+= 2) { for ($i = 0; $i < 16; $i+= 2) {
$encrypt_block.= ' $encrypt_block.= '
$l^= ' . $p[$i] . '; $l^= ' . $p[$i] . ';
$r^= ($sb_0[$l >> 24 & 0xff] + $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^
$sb_1[$l >> 16 & 0xff] ^
$sb_2[$l >> 8 & 0xff]) + $sb_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff]; $sb_3[$l & 0xff]') . ';
$r^= ' . $p[$i + 1] . '; $r^= ' . $p[$i + 1] . ';
$l^= ($sb_0[$r >> 24 & 0xff] + $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^
$sb_1[$r >> 16 & 0xff] ^
$sb_2[$r >> 8 & 0xff]) + $sb_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff]; $sb_3[$r & 0xff]') . ';
'; ';
} }
$encrypt_block.= ' $encrypt_block.= '
@ -539,16 +538,14 @@ class Blowfish extends Base
for ($i = 17; $i > 2; $i-= 2) { for ($i = 17; $i > 2; $i-= 2) {
$decrypt_block.= ' $decrypt_block.= '
$l^= ' . $p[$i] . '; $l^= ' . $p[$i] . ';
$r^= ($sb_0[$l >> 24 & 0xff] + $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^
$sb_1[$l >> 16 & 0xff] ^
$sb_2[$l >> 8 & 0xff]) + $sb_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff]; $sb_3[$l & 0xff]') . ';
$r^= ' . $p[$i - 1] . '; $r^= ' . $p[$i - 1] . ';
$l^= ($sb_0[$r >> 24 & 0xff] + $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^
$sb_1[$r >> 16 & 0xff] ^
$sb_2[$r >> 8 & 0xff]) + $sb_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff]; $sb_3[$r & 0xff]') . ';
'; ';
} }

View file

@ -1357,8 +1357,8 @@ class DES extends Base
$k[self::ENCRYPT][$i] = '$ke[' . $i . ']'; $k[self::ENCRYPT][$i] = '$ke[' . $i . ']';
$k[self::DECRYPT][$i] = '$kd[' . $i . ']'; $k[self::DECRYPT][$i] = '$kd[' . $i . ']';
} }
$init_encrypt = '$ke = $self->keys[self::ENCRYPT];'; $init_encrypt = '$ke = $self->keys[$self::ENCRYPT];';
$init_decrypt = '$kd = $self->keys[self::DECRYPT];'; $init_decrypt = '$kd = $self->keys[$self::DECRYPT];';
break; break;
} }

View file

@ -112,6 +112,15 @@ class Hash
*/ */
var $key = false; var $key = false;
/**
* Computed Key
*
* @see self::_computeKey()
* @var string
* @access private
*/
var $computedKey = false;
/** /**
* Outer XOR (Internal HMAC) * Outer XOR (Internal HMAC)
* *
@ -130,6 +139,15 @@ class Hash
*/ */
var $ipad; var $ipad;
/**
* Engine
*
* @see self::setHash()
* @var string
* @access private
*/
var $engine;
/** /**
* Default Constructor. * Default Constructor.
* *
@ -166,6 +184,43 @@ class Hash
function setKey($key = false) function setKey($key = false)
{ {
$this->key = $key; $this->key = $key;
$this->_computeKey();
}
/**
* Pre-compute the key used by the HMAC
*
* Quoting http://tools.ietf.org/html/rfc2104#section-2, "Applications that use keys longer than B bytes
* will first hash the key using H and then use the resultant L byte string as the actual key to HMAC."
*
* As documented in https://www.reddit.com/r/PHP/comments/9nct2l/symfonypolyfill_hash_pbkdf2_correct_fix_for/
* when doing an HMAC multiple times it's faster to compute the hash once instead of computing it during
* every call
*
* @access private
*/
function _computeKey()
{
if ($this->key === false) {
$this->computedKey = false;
return;
}
if (strlen($this->key) <= $this->b) {
$this->computedKey = $this->key;
return;
}
switch ($this->engine) {
case self::MODE_MHASH:
$this->computedKey = mhash($this->hash, $this->key);
break;
case self::MODE_HASH:
$this->computedKey = hash($this->hash, $this->key, true);
break;
case self::MODE_INTERNAL:
$this->computedKey = call_user_func($this->hash, $this->key);
}
} }
/** /**
@ -216,19 +271,38 @@ class Hash
} }
switch ($hash) { switch ($hash) {
case 'md2-96':
case 'md2': case 'md2':
$mode = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ? $this->b = 16;
case 'md5-96':
case 'sha1-96':
case 'sha224-96':
case 'sha256-96':
case 'md2':
case 'md5':
case 'sha1':
case 'sha224':
case 'sha256':
$this->b = 64;
break;
default:
$this->b = 128;
}
switch ($hash) {
case 'md2':
$this->engine = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ?
self::MODE_HASH : self::MODE_INTERNAL; self::MODE_HASH : self::MODE_INTERNAL;
break; break;
case 'sha384': case 'sha384':
case 'sha512': 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; break;
default: default:
$mode = CRYPT_HASH_MODE; $this->engine = CRYPT_HASH_MODE;
} }
switch ($mode) { switch ($this->engine) {
case self::MODE_MHASH: case self::MODE_MHASH:
switch ($hash) { switch ($hash) {
case 'md5': case 'md5':
@ -241,6 +315,7 @@ class Hash
default: default:
$this->hash = MHASH_SHA1; $this->hash = MHASH_SHA1;
} }
$this->_computeKey(self::MODE_MHASH);
return; return;
case self::MODE_HASH: case self::MODE_HASH:
switch ($hash) { switch ($hash) {
@ -257,35 +332,33 @@ class Hash
default: default:
$this->hash = 'sha1'; $this->hash = 'sha1';
} }
$this->_computeKey(self::MODE_HASH);
return; return;
} }
switch ($hash) { switch ($hash) {
case 'md2': case 'md2':
$this->b = 16;
$this->hash = array($this, '_md2'); $this->hash = array($this, '_md2');
break; break;
case 'md5': case 'md5':
$this->b = 64;
$this->hash = array($this, '_md5'); $this->hash = array($this, '_md5');
break; break;
case 'sha256': case 'sha256':
$this->b = 64;
$this->hash = array($this, '_sha256'); $this->hash = array($this, '_sha256');
break; break;
case 'sha384': case 'sha384':
case 'sha512': case 'sha512':
$this->b = 128;
$this->hash = array($this, '_sha512'); $this->hash = array($this, '_sha512');
break; break;
case 'sha1': case 'sha1':
default: default:
$this->b = 64;
$this->hash = array($this, '_sha1'); $this->hash = array($this, '_sha1');
} }
$this->ipad = str_repeat(chr(0x36), $this->b); $this->ipad = str_repeat(chr(0x36), $this->b);
$this->opad = str_repeat(chr(0x5C), $this->b); $this->opad = str_repeat(chr(0x5C), $this->b);
$this->_computeKey(self::MODE_INTERNAL);
} }
/** /**
@ -297,24 +370,16 @@ class Hash
*/ */
function hash($text) function hash($text)
{ {
$mode = is_array($this->hash) ? self::MODE_INTERNAL : CRYPT_HASH_MODE;
if (!empty($this->key) || is_string($this->key)) { if (!empty($this->key) || is_string($this->key)) {
switch ($mode) { switch ($this->engine) {
case self::MODE_MHASH: case self::MODE_MHASH:
$output = mhash($this->hash, $text, $this->key); $output = mhash($this->hash, $text, $this->computedKey);
break; break;
case self::MODE_HASH: case self::MODE_HASH:
$output = hash_hmac($this->hash, $text, $this->key, true); $output = hash_hmac($this->hash, $text, $this->computedKey, true);
break; break;
case self::MODE_INTERNAL: case self::MODE_INTERNAL:
/* "Applications that use keys longer than B bytes will first hash the key using H and then use the $key = str_pad($this->computedKey, $this->b, chr(0)); // step 1
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 = $this->ipad ^ $key; // step 2
$temp .= $text; // step 3 $temp .= $text; // step 3
$temp = call_user_func($this->hash, $temp); // step 4 $temp = call_user_func($this->hash, $temp); // step 4
@ -323,7 +388,7 @@ class Hash
$output = call_user_func($this->hash, $output); // step 7 $output = call_user_func($this->hash, $output); // step 7
} }
} else { } else {
switch ($mode) { switch ($this->engine) {
case self::MODE_MHASH: case self::MODE_MHASH:
$output = mhash($this->hash, $text); $output = mhash($this->hash, $text);
break; break;
@ -784,7 +849,6 @@ class Hash
* _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the * _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. * possibility of overflow exists, care has to be taken. BigInteger could be used but this should be faster.
* *
* @param int $...
* @return int * @return int
* @see self::_sha256() * @see self::_sha256()
* @access private * @access private
@ -802,9 +866,14 @@ class Hash
$result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument; $result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument;
} }
if ((php_uname('m') & "\xDF\xDF\xDF") != 'ARM') {
return fmod($result, $mod); return fmod($result, $mod);
} }
return (fmod($result, 0x80000000) & 0x7FFFFFFF) |
((fmod(floor($result / 0x80000000), 2) & 1) << 31);
}
/** /**
* String Shift * String Shift
* *

View file

@ -296,7 +296,7 @@ class RC2 extends Base
function setKeyLength($length) function setKeyLength($length)
{ {
if ($length < 8) { if ($length < 8) {
$this->default_key_length = 8; $this->default_key_length = 1;
} elseif ($length > 1024) { } elseif ($length > 1024) {
$this->default_key_length = 128; $this->default_key_length = 128;
} else { } else {

View file

@ -107,7 +107,7 @@ class RC4 extends Base
* @var string * @var string
* @access private * @access private
*/ */
var $key = "\0"; var $key;
/** /**
* The Key Stream for decryption and encryption * The Key Stream for decryption and encryption
@ -144,8 +144,10 @@ class RC4 extends Base
*/ */
function isValidEngine($engine) function isValidEngine($engine)
{ {
switch ($engine) { if ($engine == Base::ENGINE_OPENSSL) {
case Base::ENGINE_OPENSSL: if (version_compare(PHP_VERSION, '5.3.7') >= 0) {
$this->cipher_name_openssl = 'rc4-40';
} else {
switch (strlen($this->key)) { switch (strlen($this->key)) {
case 5: case 5:
$this->cipher_name_openssl = 'rc4-40'; $this->cipher_name_openssl = 'rc4-40';
@ -160,6 +162,7 @@ class RC4 extends Base
return false; return false;
} }
} }
}
return parent::isValidEngine($engine); return parent::isValidEngine($engine);
} }

View file

@ -182,6 +182,10 @@ class RSA
* PKCS#8 formatted private key * PKCS#8 formatted private key
*/ */
const PRIVATE_FORMAT_PKCS8 = 8; const PRIVATE_FORMAT_PKCS8 = 8;
/**
* OpenSSH formatted private key
*/
const PRIVATE_FORMAT_OPENSSH = 9;
/**#@-*/ /**#@-*/
/**#@+ /**#@+
@ -468,6 +472,10 @@ class RSA
break; break;
case extension_loaded('openssl') && file_exists($this->configFile): case extension_loaded('openssl') && file_exists($this->configFile):
// some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
$versions = array();
// avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems)
if (strpos(ini_get('disable_functions'), 'phpinfo') === false) {
ob_start(); ob_start();
@phpinfo(); @phpinfo();
$content = ob_get_contents(); $content = ob_get_contents();
@ -475,7 +483,6 @@ class RSA
preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
$versions = array();
if (!empty($matches[1])) { if (!empty($matches[1])) {
for ($i = 0; $i < count($matches[1]); $i++) { for ($i = 0; $i < count($matches[1]); $i++) {
$fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
@ -488,6 +495,7 @@ class RSA
} }
} }
} }
}
// it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+ // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
switch (true) { switch (true) {
@ -529,7 +537,7 @@ class RSA
* @access public * @access public
* @param int $bits * @param int $bits
* @param int $timeout * @param int $timeout
* @param array $p * @param array $partial
*/ */
function createKey($bits = 1024, $timeout = false, $partial = array()) function createKey($bits = 1024, $timeout = false, $partial = array())
{ {
@ -708,7 +716,12 @@ class RSA
* *
* @access private * @access private
* @see self::setPrivateKeyFormat() * @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 * @return string
*/ */
function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients) function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
@ -816,6 +829,58 @@ class RSA
$key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n"; $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n";
return $key; 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 default: // eg. self::PRIVATE_FORMAT_PKCS1
$components = array(); $components = array();
foreach ($raw as $name => $value) { foreach ($raw as $name => $value) {
@ -937,8 +1002,9 @@ class RSA
* *
* @access private * @access private
* @see self::setPublicKeyFormat() * @see self::setPublicKeyFormat()
* @param string $RSAPrivateKey * @param Math_BigInteger $n
* @return string * @param Math_BigInteger $e
* @return string|array<string,Math_BigInteger>
*/ */
function _convertPublicKey($n, $e) function _convertPublicKey($n, $e)
{ {
@ -1016,9 +1082,9 @@ class RSA
* @access private * @access private
* @see self::_convertPublicKey() * @see self::_convertPublicKey()
* @see self::_convertPrivateKey() * @see self::_convertPrivateKey()
* @param string $key * @param string|array $key
* @param int $type * @param int $type
* @return array * @return array|bool
*/ */
function _parseKey($key, $type) function _parseKey($key, $type)
{ {
@ -1153,6 +1219,7 @@ class RSA
$length = $this->_decodeLength($temp); $length = $this->_decodeLength($temp);
switch ($this->_string_shift($temp, $length)) { 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\x01": // rsaEncryption
case "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0A": // rsaPSS
break; break;
case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
/* /*
@ -1329,9 +1396,14 @@ class RSA
xml_set_character_data_handler($xml, '_data_handler'); xml_set_character_data_handler($xml, '_data_handler');
// add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added // add <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
if (!xml_parse($xml, '<xml>' . $key . '</xml>')) { if (!xml_parse($xml, '<xml>' . $key . '</xml>')) {
xml_parser_free($xml);
unset($xml);
return false; return false;
} }
xml_parser_free($xml);
unset($xml);
return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false; return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
// from PuTTY's SSHPUBK.C // from PuTTY's SSHPUBK.C
case self::PRIVATE_FORMAT_PUTTY: case self::PRIVATE_FORMAT_PUTTY:
@ -1404,7 +1476,78 @@ class RSA
$components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256)); $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256));
return $components; return $components;
case self::PRIVATE_FORMAT_OPENSSH:
$components = array();
$decoded = $this->_extractBER($key);
$magic = $this->_string_shift($decoded, 15);
if ($magic !== "openssh-key-v1\0") {
return false;
} }
$options = $this->_string_shift($decoded, 24);
// \0\0\0\4none = ciphername
// \0\0\0\4none = kdfname
// \0\0\0\0 = kdfoptions
// \0\0\0\1 = numkeys
if ($options != "\0\0\0\4none\0\0\0\4none\0\0\0\0\0\0\0\1") {
return false;
}
extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
if (strlen($decoded) < $length) {
return false;
}
$publicKey = $this->_string_shift($decoded, $length);
extract(unpack('Nlength', $this->_string_shift($decoded, 4)));
if (strlen($decoded) < $length) {
return false;
}
$paddedKey = $this->_string_shift($decoded, $length);
if ($this->_string_shift($publicKey, 11) !== "\0\0\0\7ssh-rsa") {
return false;
}
$checkint1 = $this->_string_shift($paddedKey, 4);
$checkint2 = $this->_string_shift($paddedKey, 4);
if (strlen($checkint1) != 4 || $checkint1 !== $checkint2) {
return false;
}
if ($this->_string_shift($paddedKey, 11) !== "\0\0\0\7ssh-rsa") {
return false;
}
$values = array(
&$components['modulus'],
&$components['publicExponent'],
&$components['privateExponent'],
&$components['coefficients'][2],
&$components['primes'][1],
&$components['primes'][2]
);
foreach ($values as &$value) {
extract(unpack('Nlength', $this->_string_shift($paddedKey, 4)));
if (strlen($paddedKey) < $length) {
return false;
}
$value = new BigInteger($this->_string_shift($paddedKey, $length), -256);
}
extract(unpack('Nlength', $this->_string_shift($paddedKey, 4)));
if (strlen($paddedKey) < $length) {
return false;
}
$components['comment'] = $this->_string_shift($decoded, $length);
$temp = $components['primes'][1]->subtract($this->one);
$components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
$temp = $components['primes'][2]->subtract($this->one);
$components['exponents'][] = $components['publicExponent']->modInverse($temp);
return $components;
}
return false;
} }
/** /**
@ -1501,8 +1644,9 @@ class RSA
* Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed) * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
* *
* @access public * @access public
* @param string $key * @param string|RSA|array $key
* @param int $type optional * @param bool|int $type optional
* @return bool
*/ */
function loadKey($key, $type = false) function loadKey($key, $type = false)
{ {
@ -1559,7 +1703,8 @@ class RSA
self::PRIVATE_FORMAT_PKCS1, self::PRIVATE_FORMAT_PKCS1,
self::PRIVATE_FORMAT_XML, self::PRIVATE_FORMAT_XML,
self::PRIVATE_FORMAT_PUTTY, self::PRIVATE_FORMAT_PUTTY,
self::PUBLIC_FORMAT_OPENSSH self::PUBLIC_FORMAT_OPENSSH,
self::PRIVATE_FORMAT_OPENSSH
); );
foreach ($types as $type) { foreach ($types as $type) {
$components = $this->_parseKey($key, $type); $components = $this->_parseKey($key, $type);
@ -1572,6 +1717,15 @@ class RSA
} }
if ($components === false) { 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; return false;
} }
@ -1733,7 +1887,6 @@ class RSA
* *
* @see self::getPublicKey() * @see self::getPublicKey()
* @access public * @access public
* @param string $key
* @param int $type optional * @param int $type optional
*/ */
function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8) function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8)
@ -1791,7 +1944,6 @@ class RSA
* *
* @see self::getPublicKey() * @see self::getPublicKey()
* @access public * @access public
* @param string $key
* @param int $type optional * @param int $type optional
* @return mixed * @return mixed
*/ */
@ -1816,8 +1968,7 @@ class RSA
* *
* @see self::getPrivateKey() * @see self::getPrivateKey()
* @access private * @access private
* @param string $key * @param int $mode optional
* @param int $type optional
*/ */
function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8) function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8)
{ {
@ -2034,7 +2185,7 @@ class RSA
* of the hash function Hash) and 0. * of the hash function Hash) and 0.
* *
* @access public * @access public
* @param int $format * @param int $sLen
*/ */
function setSaltLength($sLen) function setSaltLength($sLen)
{ {
@ -2067,7 +2218,7 @@ class RSA
* See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}. * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
* *
* @access private * @access private
* @param string $x * @param int|string|resource $x
* @return \phpseclib\Math\BigInteger * @return \phpseclib\Math\BigInteger
*/ */
function _os2ip($x) function _os2ip($x)
@ -2198,16 +2349,21 @@ class RSA
*/ */
function _equals($x, $y) function _equals($x, $y)
{ {
if (function_exists('hash_equals')) {
return hash_equals($x, $y);
}
if (strlen($x) != strlen($y)) { if (strlen($x) != strlen($y)) {
return false; return false;
} }
$result = 0; $result = "\0";
$x^= $y;
for ($i = 0; $i < strlen($x); $i++) { for ($i = 0; $i < strlen($x); $i++) {
$result |= ord($x[$i]) ^ ord($y[$i]); $result|= $x[$i];
} }
return $result == 0; return $result === "\0";
} }
/** /**
@ -2289,7 +2445,7 @@ class RSA
* *
* @access private * @access private
* @param string $mgfSeed * @param string $mgfSeed
* @param int $mgfLen * @param int $maskLen
* @return string * @return string
*/ */
function _mgf1($mgfSeed, $maskLen) function _mgf1($mgfSeed, $maskLen)
@ -2414,19 +2570,26 @@ class RSA
$db = $maskedDB ^ $dbMask; $db = $maskedDB ^ $dbMask;
$lHash2 = substr($db, 0, $this->hLen); $lHash2 = substr($db, 0, $this->hLen);
$m = substr($db, $this->hLen); $m = substr($db, $this->hLen);
if ($lHash != $lHash2) { $hashesMatch = $this->_equals($lHash, $lHash2);
user_error('Decryption error'); $leadingZeros = 1;
return false; $patternMatch = 0;
$offset = 0;
for ($i = 0; $i < strlen($m); $i++) {
$patternMatch|= $leadingZeros & ($m[$i] === "\1");
$leadingZeros&= $m[$i] === "\0";
$offset+= $patternMatch ? 0 : 1;
} }
$m = ltrim($m, chr(0));
if (ord($m[0]) != 1) { // we do | instead of || to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation
// to protect against timing attacks
if (!$hashesMatch | !$patternMatch) {
user_error('Decryption error'); user_error('Decryption error');
return false; return false;
} }
// Output the message M // Output the message M
return substr($m, 1); return substr($m, $offset + 1);
} }
/** /**
@ -2605,7 +2768,7 @@ class RSA
// if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
// be output. // 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; $sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
$mHash = $this->hash->hash($m); $mHash = $this->hash->hash($m);
@ -2683,7 +2846,7 @@ class RSA
// RSA verification // RSA verification
$modBits = 8 * $this->k; $modBits = strlen($this->modulus->toBits());
$s2 = $this->_os2ip($s); $s2 = $this->_os2ip($s);
$m2 = $this->_rsavp1($s2); $m2 = $this->_rsavp1($s2);
@ -2691,7 +2854,7 @@ class RSA
user_error('Invalid signature'); user_error('Invalid signature');
return false; return false;
} }
$em = $this->_i2osp($m2, $modBits >> 3); $em = $this->_i2osp($m2, $this->k);
if ($em === false) { if ($em === false) {
user_error('Invalid signature'); user_error('Invalid signature');
return false; return false;
@ -2754,6 +2917,59 @@ class RSA
return $em; 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 * RSASSA-PKCS1-V1_5-SIGN
* *
@ -2791,6 +3007,7 @@ class RSA
* *
* @access private * @access private
* @param string $m * @param string $m
* @param string $s
* @return string * @return string
*/ */
function _rsassa_pkcs1_v1_5_verify($m, $s) function _rsassa_pkcs1_v1_5_verify($m, $s)
@ -2819,13 +3036,17 @@ class RSA
// EMSA-PKCS1-v1_5 encoding // EMSA-PKCS1-v1_5 encoding
$em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); $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'); user_error('RSA modulus too short');
return false; return false;
} }
// Compare // Compare
return $this->_equals($em, $em2);
return ($em2 !== false && $this->_equals($em, $em2)) ||
($em3 !== false && $this->_equals($em, $em3));
} }
/** /**
@ -2931,7 +3152,7 @@ class RSA
* *
* @see self::encrypt() * @see self::encrypt()
* @access public * @access public
* @param string $plaintext * @param string $ciphertext
* @return string * @return string
*/ */
function decrypt($ciphertext) function decrypt($ciphertext)

View file

@ -45,6 +45,10 @@ class Random
*/ */
static function string($length) static function string($length)
{ {
if (!$length) {
return '';
}
if (version_compare(PHP_VERSION, '7.0.0', '>=')) { if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
try { try {
return \random_bytes($length); return \random_bytes($length);
@ -62,7 +66,7 @@ class Random
// method 1. prior to PHP 5.3 this would call rand() on windows hence the function_exists('class_alias') call. // 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 // ie. class_alias is a function that was introduced in PHP 5.3
if (extension_loaded('mcrypt') && function_exists('class_alias')) { 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, // 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 // to quote <http://php.net/ChangeLog-5.php#5.3.4>, "possible blocking behavior". as of 5.3.4
@ -93,7 +97,10 @@ class Random
$fp = @fopen('/dev/urandom', 'rb'); $fp = @fopen('/dev/urandom', 'rb');
} }
if ($fp !== true && $fp !== false) { // surprisingly faster than !is_bool() or is_resource() 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: // 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 // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391
@ -101,7 +108,7 @@ class Random
// not doing. regardless, this'll only be called if this PHP script couldn't open /dev/urandom due to open_basedir // 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 // restrictions or some such
if (extension_loaded('mcrypt')) { 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 // at this point we have no choice but to use a pure-PHP CSPRNG

View file

@ -432,8 +432,10 @@ class Twofish extends Base
$m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^ $m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^
$m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]]; $m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff); $B = ($B << 8) | ($B >> 24 & 0xff);
$K[] = $A+= $B; $A = $this->safe_intval($A + $B);
$K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); $K[] = $A;
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
} }
for ($i = 0; $i < 256; ++$i) { for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0]; $S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0];
@ -456,8 +458,10 @@ class Twofish extends Base
$m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ $m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^
$m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]]; $m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff); $B = ($B << 8) | ($B >> 24 & 0xff);
$K[] = $A+= $B; $A = $this->safe_intval($A + $B);
$K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); $K[] = $A;
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
} }
for ($i = 0; $i < 256; ++$i) { for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0]; $S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0];
@ -481,8 +485,10 @@ class Twofish extends Base
$m2[$q1[$q0[$q0[$q0[$j] ^ $key[31]] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ $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]]; $m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^ $key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff); $B = ($B << 8) | ($B >> 24 & 0xff);
$K[] = $A+= $B; $A = $this->safe_intval($A + $B);
$K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); $K[] = $A;
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
} }
for ($i = 0; $i < 256; ++$i) { for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4] ^ $s0]; $S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4] ^ $s0];
@ -578,9 +584,9 @@ class Twofish extends Base
$S1[ $R1 & 0xff] ^ $S1[ $R1 & 0xff] ^
$S2[($R1 >> 8) & 0xff] ^ $S2[($R1 >> 8) & 0xff] ^
$S3[($R1 >> 16) & 0xff]; $S3[($R1 >> 16) & 0xff];
$R2^= $t0 + $t1 + $K[++$ki]; $R2^= $this->safe_intval($t0 + $t1 + $K[++$ki]);
$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); $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] ^ $t0 = $S0[ $R2 & 0xff] ^
$S1[($R2 >> 8) & 0xff] ^ $S1[($R2 >> 8) & 0xff] ^
@ -590,9 +596,9 @@ class Twofish extends Base
$S1[ $R3 & 0xff] ^ $S1[ $R3 & 0xff] ^
$S2[($R3 >> 8) & 0xff] ^ $S2[($R3 >> 8) & 0xff] ^
$S3[($R3 >> 16) & 0xff]; $S3[($R3 >> 16) & 0xff];
$R0^= ($t0 + $t1 + $K[++$ki]); $R0^= $this->safe_intval($t0 + $t1 + $K[++$ki]);
$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); $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 // @codingStandardsIgnoreStart
@ -634,9 +640,9 @@ class Twofish extends Base
$S1[$R1 & 0xff] ^ $S1[$R1 & 0xff] ^
$S2[$R1 >> 8 & 0xff] ^ $S2[$R1 >> 8 & 0xff] ^
$S3[$R1 >> 16 & 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; $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] ^ $t0 = $S0[$R2 & 0xff] ^
$S1[$R2 >> 8 & 0xff] ^ $S1[$R2 >> 8 & 0xff] ^
@ -646,9 +652,9 @@ class Twofish extends Base
$S1[$R3 & 0xff] ^ $S1[$R3 & 0xff] ^
$S2[$R3 >> 8 & 0xff] ^ $S2[$R3 >> 8 & 0xff] ^
$S3[$R3 >> 16 & 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; $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 // @codingStandardsIgnoreStart
@ -679,6 +685,8 @@ class Twofish extends Base
$code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
} }
$safeint = $this->safe_intval_inline();
if (!isset($lambda_functions[$code_hash])) { if (!isset($lambda_functions[$code_hash])) {
switch (true) { switch (true) {
case $gen_hi_opt_code: case $gen_hi_opt_code:
@ -727,9 +735,9 @@ class Twofish extends Base
$S1[ $R1 & 0xff] ^ $S1[ $R1 & 0xff] ^
$S2[($R1 >> 8) & 0xff] ^ $S2[($R1 >> 8) & 0xff] ^
$S3[($R1 >> 16) & 0xff]; $S3[($R1 >> 16) & 0xff];
$R2^= ($t0 + $t1 + '.$K[++$ki].'); $R2^= ' . sprintf($safeint, '$t0 + $t1 + ' . $K[++$ki]) . ';
$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); $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] ^ $t0 = $S0[ $R2 & 0xff] ^
$S1[($R2 >> 8) & 0xff] ^ $S1[($R2 >> 8) & 0xff] ^
@ -739,9 +747,9 @@ class Twofish extends Base
$S1[ $R3 & 0xff] ^ $S1[ $R3 & 0xff] ^
$S2[($R3 >> 8) & 0xff] ^ $S2[($R3 >> 8) & 0xff] ^
$S3[($R3 >> 16) & 0xff]; $S3[($R3 >> 16) & 0xff];
$R0^= ($t0 + $t1 + '.$K[++$ki].'); $R0^= ' . sprintf($safeint, '($t0 + $t1 + ' . $K[++$ki] . ')') . ';
$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); $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.= ' $encrypt_block.= '
@ -769,9 +777,9 @@ class Twofish extends Base
$S1[$R1 & 0xff] ^ $S1[$R1 & 0xff] ^
$S2[$R1 >> 8 & 0xff] ^ $S2[$R1 >> 8 & 0xff] ^
$S3[$R1 >> 16 & 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; $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] ^ $t0 = $S0[$R2 & 0xff] ^
$S1[$R2 >> 8 & 0xff] ^ $S1[$R2 >> 8 & 0xff] ^
@ -781,9 +789,9 @@ class Twofish extends Base
$S1[$R3 & 0xff] ^ $S1[$R3 & 0xff] ^
$S2[$R3 >> 8 & 0xff] ^ $S2[$R3 >> 8 & 0xff] ^
$S3[$R3 >> 16 & 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; $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.= ' $decrypt_block.= '

View file

@ -203,8 +203,7 @@ class ANSI
/** /**
* Set the number of lines that should be logged past the terminal height * Set the number of lines that should be logged past the terminal height
* *
* @param int $x * @param int $history
* @param int $y
* @access public * @access public
*/ */
function setHistory($history) function setHistory($history)
@ -272,7 +271,7 @@ class ANSI
case "\x1B[K": // Clear screen from cursor right case "\x1B[K": // Clear screen from cursor right
$this->screen[$this->y] = substr($this->screen[$this->y], 0, $this->x); $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; break;
case "\x1B[2K": // Clear entire line case "\x1B[2K": // Clear entire line
$this->screen[$this->y] = str_repeat(' ', $this->x); $this->screen[$this->y] = str_repeat(' ', $this->x);
@ -305,6 +304,9 @@ class ANSI
case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): // Move cursor left n lines case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): // Move cursor left n lines
$this->old_x = $this->x; $this->old_x = $this->x;
$this->x-= $match[1]; $this->x-= $match[1];
if ($this->x < 0) {
$this->x = 0;
}
break; break;
case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window
break; break;
@ -313,19 +315,20 @@ class ANSI
$mods = explode(';', $match[1]); $mods = explode(';', $match[1]);
foreach ($mods as $mod) { foreach ($mods as $mod) {
switch ($mod) { switch ($mod) {
case 0: // Turn off character attributes case '':
case '0': // Turn off character attributes
$attr_cell = clone $this->base_attr_cell; $attr_cell = clone $this->base_attr_cell;
break; break;
case 1: // Turn bold mode on case '1': // Turn bold mode on
$attr_cell->bold = true; $attr_cell->bold = true;
break; break;
case 4: // Turn underline mode on case '4': // Turn underline mode on
$attr_cell->underline = true; $attr_cell->underline = true;
break; break;
case 5: // Turn blinking mode on case '5': // Turn blinking mode on
$attr_cell->blink = true; $attr_cell->blink = true;
break; break;
case 7: // Turn reverse video on case '7': // Turn reverse video on
$attr_cell->reverse = !$attr_cell->reverse; $attr_cell->reverse = !$attr_cell->reverse;
$temp = $attr_cell->background; $temp = $attr_cell->background;
$attr_cell->background = $attr_cell->foreground; $attr_cell->background = $attr_cell->foreground;
@ -338,23 +341,23 @@ class ANSI
$back = &$attr_cell->{ $attr_cell->reverse ? 'foreground' : 'background' }; $back = &$attr_cell->{ $attr_cell->reverse ? 'foreground' : 'background' };
switch ($mod) { switch ($mod) {
// @codingStandardsIgnoreStart // @codingStandardsIgnoreStart
case 30: $front = 'black'; break; case '30': $front = 'black'; break;
case 31: $front = 'red'; break; case '31': $front = 'red'; break;
case 32: $front = 'green'; break; case '32': $front = 'green'; break;
case 33: $front = 'yellow'; break; case '33': $front = 'yellow'; break;
case 34: $front = 'blue'; break; case '34': $front = 'blue'; break;
case 35: $front = 'magenta'; break; case '35': $front = 'magenta'; break;
case 36: $front = 'cyan'; break; case '36': $front = 'cyan'; break;
case 37: $front = 'white'; break; case '37': $front = 'white'; break;
case 40: $back = 'black'; break; case '40': $back = 'black'; break;
case 41: $back = 'red'; break; case '41': $back = 'red'; break;
case 42: $back = 'green'; break; case '42': $back = 'green'; break;
case 43: $back = 'yellow'; break; case '43': $back = 'yellow'; break;
case 44: $back = 'blue'; break; case '44': $back = 'blue'; break;
case 45: $back = 'magenta'; break; case '45': $back = 'magenta'; break;
case 46: $back = 'cyan'; break; case '46': $back = 'cyan'; break;
case 47: $back = 'white'; break; case '47': $back = 'white'; break;
// @codingStandardsIgnoreEnd // @codingStandardsIgnoreEnd
default: default:
@ -416,7 +419,7 @@ class ANSI
if ($this->x > $this->max_x) { if ($this->x > $this->max_x) {
$this->x = 0; $this->x = 0;
$this->y++; $this->_newLine();
} else { } else {
$this->x++; $this->x++;
} }

View file

@ -25,6 +25,8 @@ namespace phpseclib\File;
use phpseclib\File\ASN1\Element; use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger; use phpseclib\Math\BigInteger;
use DateTime;
use DateTimeZone;
/** /**
* Pure-PHP ASN.1 Parser * Pure-PHP ASN.1 Parser
@ -232,8 +234,11 @@ class ASN1
{ {
$current = array('start' => $start); $current = array('start' => $start);
if (!isset($encoded[$encoded_pos])) {
return false;
}
$type = ord($encoded[$encoded_pos++]); $type = ord($encoded[$encoded_pos++]);
$start++; $startOffset = 1;
$constructed = ($type >> 5) & 1; $constructed = ($type >> 5) & 1;
@ -242,14 +247,28 @@ class ASN1
$tag = 0; $tag = 0;
// process septets (since the eighth bit is ignored, it's not an octet) // process septets (since the eighth bit is ignored, it's not an octet)
do { 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 <<= 7;
$tag |= ord($encoded[$encoded_pos++]) & 0x7F; $temp &= 0x7F;
$start++; // "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); } while ($loop);
} }
$start+= $startOffset;
// Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13 // 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++]); $length = ord($encoded[$encoded_pos++]);
$start++; $start++;
if ($length == 0x80) { // indefinite length if ($length == 0x80) { // indefinite length
@ -306,6 +325,9 @@ class ASN1
$remainingLength = $length; $remainingLength = $length;
while ($remainingLength > 0) { while ($remainingLength > 0) {
$temp = $this->_decode_ber($content, $start, $content_pos); $temp = $this->_decode_ber($content, $start, $content_pos);
if ($temp === false) {
break;
}
$length = $temp['length']; $length = $temp['length'];
// end-of-content octets - see paragraph 8.1.5 // end-of-content octets - see paragraph 8.1.5
if (substr($content, $content_pos + $length, 2) == "\0\0") { if (substr($content, $content_pos + $length, 2) == "\0\0") {
@ -338,13 +360,16 @@ class ASN1
switch ($tag) { switch ($tag) {
case self::TYPE_BOOLEAN: case self::TYPE_BOOLEAN:
// "The contents octets shall consist of a single octet." -- paragraph 8.2.1 // "The contents octets shall consist of a single octet." -- paragraph 8.2.1
//if (strlen($content) != 1) { if ($constructed || strlen($content) != 1) {
// return false; return false;
//} }
$current['content'] = (bool) ord($content[$content_pos]); $current['content'] = (bool) ord($content[$content_pos]);
break; break;
case self::TYPE_INTEGER: case self::TYPE_INTEGER:
case self::TYPE_ENUMERATED: case self::TYPE_ENUMERATED:
if ($constructed) {
return false;
}
$current['content'] = new BigInteger(substr($content, $content_pos), -256); $current['content'] = new BigInteger(substr($content, $content_pos), -256);
break; break;
case self::TYPE_REAL: // not currently supported case self::TYPE_REAL: // not currently supported
@ -357,19 +382,22 @@ class ASN1
$current['content'] = substr($content, $content_pos); $current['content'] = substr($content, $content_pos);
} else { } else {
$temp = $this->_decode_ber($content, $start, $content_pos); $temp = $this->_decode_ber($content, $start, $content_pos);
if ($temp === false) {
return false;
}
$length-= (strlen($content) - $content_pos); $length-= (strlen($content) - $content_pos);
$last = count($temp) - 1; $last = count($temp) - 1;
for ($i = 0; $i < $last; $i++) { for ($i = 0; $i < $last; $i++) {
// all subtags should be bit strings // all subtags should be bit strings
//if ($temp[$i]['type'] != self::TYPE_BIT_STRING) { if ($temp[$i]['type'] != self::TYPE_BIT_STRING) {
// return false; return false;
//} }
$current['content'].= substr($temp[$i]['content'], 1); $current['content'].= substr($temp[$i]['content'], 1);
} }
// all subtags should be bit strings // all subtags should be bit strings
//if ($temp[$last]['type'] != self::TYPE_BIT_STRING) { if ($temp[$last]['type'] != self::TYPE_BIT_STRING) {
// return false; return false;
//} }
$current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1); $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1);
} }
break; break;
@ -381,11 +409,14 @@ class ASN1
$length = 0; $length = 0;
while (substr($content, $content_pos, 2) != "\0\0") { while (substr($content, $content_pos, 2) != "\0\0") {
$temp = $this->_decode_ber($content, $length + $start, $content_pos); $temp = $this->_decode_ber($content, $length + $start, $content_pos);
if ($temp === false) {
return false;
}
$content_pos += $temp['length']; $content_pos += $temp['length'];
// all subtags should be octet strings // all subtags should be octet strings
//if ($temp['type'] != self::TYPE_OCTET_STRING) { if ($temp['type'] != self::TYPE_OCTET_STRING) {
// return false; return false;
//} }
$current['content'].= $temp['content']; $current['content'].= $temp['content'];
$length+= $temp['length']; $length+= $temp['length'];
} }
@ -396,12 +427,15 @@ class ASN1
break; break;
case self::TYPE_NULL: case self::TYPE_NULL:
// "The contents octets shall not contain any octets." -- paragraph 8.8.2 // "The contents octets shall not contain any octets." -- paragraph 8.8.2
//if (strlen($content)) { if ($constructed || strlen($content)) {
// return false; return false;
//} }
break; break;
case self::TYPE_SEQUENCE: case self::TYPE_SEQUENCE:
case self::TYPE_SET: case self::TYPE_SET:
if (!$constructed) {
return false;
}
$offset = 0; $offset = 0;
$current['content'] = array(); $current['content'] = array();
$content_len = strlen($content); $content_len = strlen($content);
@ -413,30 +447,22 @@ class ASN1
break 2; break 2;
} }
$temp = $this->_decode_ber($content, $start + $offset, $content_pos); $temp = $this->_decode_ber($content, $start + $offset, $content_pos);
if ($temp === false) {
return false;
}
$content_pos += $temp['length']; $content_pos += $temp['length'];
$current['content'][] = $temp; $current['content'][] = $temp;
$offset+= $temp['length']; $offset+= $temp['length'];
} }
break; break;
case self::TYPE_OBJECT_IDENTIFIER: case self::TYPE_OBJECT_IDENTIFIER:
$temp = ord($content[$content_pos++]); if ($constructed) {
$current['content'] = sprintf('%d.%d', floor($temp / 40), $temp % 40); return false;
$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;
} }
$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; break;
/* Each character string type shall be encoded as if it had been declared: /* Each character string type shall be encoded as if it had been declared:
[UNIVERSAL x] IMPLICIT OCTET STRING [UNIVERSAL x] IMPLICIT OCTET STRING
@ -466,12 +492,20 @@ class ASN1
case self::TYPE_UTF8_STRING: case self::TYPE_UTF8_STRING:
// ???? // ????
case self::TYPE_BMP_STRING: case self::TYPE_BMP_STRING:
if ($constructed) {
return false;
}
$current['content'] = substr($content, $content_pos); $current['content'] = substr($content, $content_pos);
break; break;
case self::TYPE_UTC_TIME: case self::TYPE_UTC_TIME:
case self::TYPE_GENERALIZED_TIME: case self::TYPE_GENERALIZED_TIME:
if ($constructed) {
return false;
}
$current['content'] = $this->_decodeTime(substr($content, $content_pos), $tag); $current['content'] = $this->_decodeTime(substr($content, $content_pos), $tag);
break;
default: default:
return false;
} }
$start+= $length; $start+= $length;
@ -495,6 +529,10 @@ class ASN1
*/ */
function asn1map($decoded, $mapping, $special = array()) function asn1map($decoded, $mapping, $special = array())
{ {
if (!is_array($decoded)) {
return false;
}
if (isset($mapping['explicit']) && is_array($decoded['content'])) { if (isset($mapping['explicit']) && is_array($decoded['content'])) {
$decoded = $decoded['content'][0]; $decoded = $decoded['content'][0];
} }
@ -580,7 +618,7 @@ class ASN1
$childClass = $tempClass = self::CLASS_UNIVERSAL; $childClass = $tempClass = self::CLASS_UNIVERSAL;
$constant = null; $constant = null;
if (isset($temp['constant'])) { if (isset($temp['constant'])) {
$tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC; $tempClass = $temp['type'];
} }
if (isset($child['class'])) { if (isset($child['class'])) {
$childClass = $child['class']; $childClass = $child['class'];
@ -643,7 +681,7 @@ class ASN1
$temp = $decoded['content'][$i]; $temp = $decoded['content'][$i];
$tempClass = self::CLASS_UNIVERSAL; $tempClass = self::CLASS_UNIVERSAL;
if (isset($temp['constant'])) { if (isset($temp['constant'])) {
$tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC; $tempClass = $temp['type'];
} }
foreach ($mapping['children'] as $key => $child) { foreach ($mapping['children'] as $key => $child) {
@ -704,10 +742,17 @@ class ASN1
return isset($this->oids[$decoded['content']]) ? $this->oids[$decoded['content']] : $decoded['content']; return isset($this->oids[$decoded['content']]) ? $this->oids[$decoded['content']] : $decoded['content'];
case self::TYPE_UTC_TIME: case self::TYPE_UTC_TIME:
case self::TYPE_GENERALIZED_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']); $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: case self::TYPE_BIT_STRING:
if (isset($mapping['mapping'])) { if (isset($mapping['mapping'])) {
$offset = ord($decoded['content'][0]); $offset = ord($decoded['content'][0]);
@ -781,7 +826,7 @@ class ASN1
* *
* @param string $source * @param string $source
* @param string $mapping * @param string $mapping
* @param int $idx * @param array $special
* @return string * @return string
* @access public * @access public
*/ */
@ -797,6 +842,7 @@ class ASN1
* @param string $source * @param string $source
* @param string $mapping * @param string $mapping
* @param int $idx * @param int $idx
* @param array $special
* @return string * @return string
* @access private * @access private
*/ */
@ -845,7 +891,7 @@ class ASN1
if ($mapping['type'] == self::TYPE_SET) { if ($mapping['type'] == self::TYPE_SET) {
sort($value); sort($value);
} }
$value = implode($value, ''); $value = implode('', $value);
break; break;
} }
@ -956,7 +1002,11 @@ class ASN1
case self::TYPE_GENERALIZED_TIME: case self::TYPE_GENERALIZED_TIME:
$format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y'; $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y';
$format.= 'mdHis'; $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; break;
case self::TYPE_BIT_STRING: case self::TYPE_BIT_STRING:
if (isset($mapping['mapping'])) { if (isset($mapping['mapping'])) {
@ -998,27 +1048,7 @@ class ASN1
$value = base64_decode($source); $value = base64_decode($source);
break; break;
case self::TYPE_OBJECT_IDENTIFIER: case self::TYPE_OBJECT_IDENTIFIER:
$oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids); $value = $this->_encodeOID($source);
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;
}
break; break;
case self::TYPE_ANY: case self::TYPE_ANY:
$loc = $this->location; $loc = $this->location;
@ -1117,6 +1147,113 @@ class ASN1
return pack('Ca*', 0x80 | strlen($temp), $temp); 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 * BER-decode the time
* *
@ -1137,33 +1274,32 @@ class ASN1
http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2 http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2
http://www.obj-sys.com/asn1tutorial/node14.html */ http://www.obj-sys.com/asn1tutorial/node14.html */
$pattern = $tag == self::TYPE_UTC_TIME ? $format = 'YmdHis';
'#(..)(..)(..)(..)(..)(..)(.*)#' :
'#(....)(..)(..)(..)(..)(..).*([Z+-].*)$#';
preg_match($pattern, $content, $matches);
list(, $year, $month, $day, $hour, $minute, $second, $timezone) = $matches;
if ($tag == self::TYPE_UTC_TIME) { 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') { if ($content[strlen($content) - 1] == 'Z') {
$mktime = 'gmmktime'; $content = substr($content, 0, -1) . '+0000';
$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;
} }
return @$mktime($hour, $minute, $second, $month, $day, $year) + $timezone; if (strpos($content, '-') !== false || strpos($content, '+') !== false) {
$format.= 'O';
}
// error supression isn't necessary as of PHP 7.0:
// http://php.net/manual/en/migration70.other-changes.php
return @DateTime::createFromFormat($format, $content);
} }
/** /**

View file

@ -31,6 +31,8 @@ use phpseclib\Crypt\Random;
use phpseclib\Crypt\RSA; use phpseclib\Crypt\RSA;
use phpseclib\File\ASN1\Element; use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger; use phpseclib\Math\BigInteger;
use DateTime;
use DateTimeZone;
/** /**
* Pure-PHP X.509 Parser * Pure-PHP X.509 Parser
@ -303,6 +305,22 @@ class X509
*/ */
var $challenge; 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. * Default Constructor.
* *
@ -945,6 +963,13 @@ class X509
'children' => $AccessDescription 'children' => $AccessDescription
); );
$this->SubjectInfoAccessSyntax = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $AccessDescription
);
$this->SubjectAltName = $GeneralNames; $this->SubjectAltName = $GeneralNames;
$this->PrivateKeyUsagePeriod = array( $this->PrivateKeyUsagePeriod = array(
@ -1583,7 +1608,7 @@ class X509
* Map extension values from octet string to extension-specific internal * Map extension values from octet string to extension-specific internal
* format. * format.
* *
* @param array ref $root * @param array $root (by reference)
* @param string $path * @param string $path
* @param object $asn1 * @param object $asn1
* @access private * @access private
@ -1597,12 +1622,15 @@ class X509
$id = $extensions[$i]['extnId']; $id = $extensions[$i]['extnId'];
$value = &$extensions[$i]['extnValue']; $value = &$extensions[$i]['extnValue'];
$value = base64_decode($value); $value = base64_decode($value);
$decoded = $asn1->decodeBER($value);
/* [extnValue] contains the DER encoding of an ASN.1 value /* [extnValue] contains the DER encoding of an ASN.1 value
corresponding to the extension type identified by extnID */ corresponding to the extension type identified by extnID */
$map = $this->_getMapping($id); $map = $this->_getMapping($id);
if (!is_bool($map)) { 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; $value = $mapped === false ? $decoded[0] : $mapped;
if ($id == 'id-ce-certificatePolicies') { if ($id == 'id-ce-certificatePolicies') {
@ -1633,7 +1661,7 @@ class X509
* Map extension values from extension-specific internal format to * Map extension values from extension-specific internal format to
* octet string. * octet string.
* *
* @param array ref $root * @param array $root (by reference)
* @param string $path * @param string $path
* @param object $asn1 * @param object $asn1
* @access private * @access private
@ -1699,7 +1727,7 @@ class X509
* Map attribute values from ANY type to attribute-specific internal * Map attribute values from ANY type to attribute-specific internal
* format. * format.
* *
* @param array ref $root * @param array $root (by reference)
* @param string $path * @param string $path
* @param object $asn1 * @param object $asn1
* @access private * @access private
@ -1740,7 +1768,7 @@ class X509
* Map attribute values from attribute-specific internal format to * Map attribute values from attribute-specific internal format to
* ANY type. * ANY type.
* *
* @param array ref $root * @param array $root (by reference)
* @param string $path * @param string $path
* @param object $asn1 * @param object $asn1
* @access private * @access private
@ -1783,7 +1811,7 @@ class X509
* Map DN values from ANY type to DN-specific internal * Map DN values from ANY type to DN-specific internal
* format. * format.
* *
* @param array ref $root * @param array $root (by reference)
* @param string $path * @param string $path
* @param object $asn1 * @param object $asn1
* @access private * @access private
@ -1813,7 +1841,7 @@ class X509
* Map DN values from DN-specific internal format to * Map DN values from DN-specific internal format to
* ANY type. * ANY type.
* *
* @param array ref $root * @param array $root (by reference)
* @param string $path * @param string $path
* @param object $asn1 * @param object $asn1
* @access private * @access private
@ -1871,6 +1899,8 @@ class X509
return $this->ExtKeyUsageSyntax; return $this->ExtKeyUsageSyntax;
case 'id-pe-authorityInfoAccess': case 'id-pe-authorityInfoAccess':
return $this->AuthorityInfoAccessSyntax; return $this->AuthorityInfoAccessSyntax;
case 'id-pe-subjectInfoAccess':
return $this->SubjectInfoAccessSyntax;
case 'id-ce-subjectAltName': case 'id-ce-subjectAltName':
return $this->SubjectAltName; return $this->SubjectAltName;
case 'id-ce-subjectDirectoryAttributes': case 'id-ce-subjectDirectoryAttributes':
@ -1907,6 +1937,12 @@ class X509
// "SET Secure Electronic Transaction Specification" // "SET Secure Electronic Transaction Specification"
// http://www.maithean.com/docs/set_bk3.pdf // http://www.maithean.com/docs/set_bk3.pdf
case '2.23.42.7.0': // id-set-hashedRootKey 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; return true;
// CSR attributes // CSR attributes
@ -2027,7 +2063,8 @@ class X509
} }
if ($names = $this->getExtension('id-ce-subjectAltName')) { if ($names = $this->getExtension('id-ce-subjectAltName')) {
foreach ($names as $key => $value) { foreach ($names as $name) {
foreach ($name as $key => $value) {
$value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value); $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value);
switch ($key) { switch ($key) {
case 'dNSName': case 'dNSName':
@ -2053,6 +2090,7 @@ class X509
} }
} }
} }
}
return false; return false;
} }
@ -2069,7 +2107,7 @@ class X509
* *
* If $date isn't defined it is assumed to be the current date. * If $date isn't defined it is assumed to be the current date.
* *
* @param int $date optional * @param \DateTime|string $date optional
* @access public * @access public
*/ */
function validateDate($date = null) function validateDate($date = null)
@ -2079,7 +2117,7 @@ class X509
} }
if (!isset($date)) { if (!isset($date)) {
$date = time(); $date = new DateTime(null, new DateTimeZone(@date_default_timezone_get()));
} }
$notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore']; $notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore'];
@ -2088,15 +2126,137 @@ class X509
$notAfter = $this->currentCert['tbsCertificate']['validity']['notAfter']; $notAfter = $this->currentCert['tbsCertificate']['validity']['notAfter'];
$notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime']; $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) { switch (true) {
case $date < @strtotime($notBefore): case $date < $notBefore:
case $date > @strtotime($notAfter): case $date > $notAfter:
return false; return false;
} }
return true; 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 * Validate a signature
* *
@ -2113,11 +2273,30 @@ class X509
* @return mixed * @return mixed
*/ */
function validateSignature($caonly = true) 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)) { if (!is_array($this->currentCert) || !isset($this->signatureSubject)) {
return null; return null;
} }
if ($count == self::$recur_limit) {
return false;
}
/* TODO: /* TODO:
"emailAddress attribute values are not case-sensitive (e.g., "subscriber@example.com" is the same as "SUBSCRIBER@EXAMPLE.COM")." "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 -- http://tools.ietf.org/html/rfc5280#section-4.1.2.6
@ -2134,7 +2313,8 @@ class X509
$subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier'); $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier');
switch (true) { switch (true) {
case !is_array($authorityKey): 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 $signingCert = $this->currentCert; // working cert
} }
} }
@ -2151,17 +2331,21 @@ class X509
$subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
switch (true) { switch (true) {
case !is_array($authorityKey): 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 $signingCert = $ca; // working cert
break 3; break 3;
} }
} }
} }
if (count($this->CAs) == $i && $caonly) { if (count($this->CAs) == $i && $caonly) {
return false; return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly);
} }
} elseif (!isset($signingCert) || $caonly) { } elseif (!isset($signingCert) || $caonly) {
return false; return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly);
} }
return $this->_validateSignature( return $this->_validateSignature(
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
@ -2197,7 +2381,11 @@ class X509
$subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
switch (true) { switch (true) {
case !is_array($authorityKey): 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 $signingCert = $ca; // working cert
break 3; break 3;
} }
@ -2264,6 +2452,41 @@ class X509
return true; 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 * Reformat public keys
* *
@ -2304,18 +2527,38 @@ class X509
return inet_ntop(base64_decode($ip)); 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 * Encodes an IP address
* *
* Takes a human readable IP address into a base64-encoded "blob" * Takes a human readable IP address into a base64-encoded "blob"
* *
* @param string $ip * @param string|array $ip
* @access private * @access private
* @return string * @return string
*/ */
function _encodeIP($ip) function _encodeIP($ip)
{ {
return base64_encode(inet_pton($ip)); return is_string($ip) ?
base64_encode(inet_pton($ip)) :
base64_encode(inet_pton($ip[0]) . inet_pton($ip[1]));
} }
/** /**
@ -2469,6 +2712,10 @@ class X509
} }
$dn = array_values($dn); $dn = array_values($dn);
// fix for https://bugs.php.net/75433 affecting PHP 7.2
if (!isset($dn[0])) {
$dn = array_splice($dn, 0, 0);
}
} }
/** /**
@ -2712,12 +2959,14 @@ class X509
$value = array_pop($value); // Always strip data type. $value = array_pop($value); // Always strip data type.
} }
} elseif (is_object($value) && $value instanceof Element) { } 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)); $value = strtoupper(preg_replace_callback('#[^\x20-\x7E]#', $callback, $value->element));
} }
$output.= $desc . '=' . $value; $output.= $desc . '=' . $value;
$result[$desc] = isset($result[$desc]) ? $result[$desc] = isset($result[$desc]) ?
array_merge((array) $dn[$prop], array($value)) : array_merge((array) $result[$desc], array($value)) :
$value; $value;
$start = false; $start = false;
} }
@ -2946,7 +3195,8 @@ class X509
/** /**
* Load a Certificate Signing Request * Load a Certificate Signing Request
* *
* @param string $csr * @param string|array $csr
* @param int $mode
* @access public * @access public
* @return mixed * @return mixed
*/ */
@ -3083,7 +3333,7 @@ class X509
* *
* https://developer.mozilla.org/en-US/docs/HTML/Element/keygen * https://developer.mozilla.org/en-US/docs/HTML/Element/keygen
* *
* @param string $csr * @param string|array $spkac
* @access public * @access public
* @return mixed * @return mixed
*/ */
@ -3154,7 +3404,7 @@ class X509
/** /**
* Save a SPKAC CSR request * Save a SPKAC CSR request
* *
* @param array $csr * @param string|array $spkac
* @param int $format optional * @param int $format optional
* @access public * @access public
* @return string * @return string
@ -3198,6 +3448,7 @@ class X509
* Load a Certificate Revocation List * Load a Certificate Revocation List
* *
* @param string $crl * @param string $crl
* @param int $mode
* @access public * @access public
* @return mixed * @return mixed
*/ */
@ -3335,7 +3586,11 @@ class X509
*/ */
function _timeField($date) 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) { if ($year < 2050) {
return array('utcTime' => $date); return array('utcTime' => $date);
} else { } else {
@ -3400,8 +3655,12 @@ class X509
return false; return false;
} }
$startDate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O'); $startDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
$endDate = !empty($this->endDate) ? $this->endDate : @date('D, d M Y H:i:s O', strtotime('+1 year')); $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" /* "The serial number MUST be a positive integer"
"Conforming CAs MUST NOT use serialNumber values longer than 20 octets." "Conforming CAs MUST NOT use serialNumber values longer than 20 octets."
-- https://tools.ietf.org/html/rfc5280#section-4.1.2.2 -- https://tools.ietf.org/html/rfc5280#section-4.1.2.2
@ -3417,7 +3676,7 @@ class X509
'tbsCertificate' => 'tbsCertificate' =>
array( array(
'version' => 'v3', 'version' => 'v3',
'serialNumber' => $serialNumber, // $this->setserialNumber() 'serialNumber' => $serialNumber, // $this->setSerialNumber()
'signature' => array('algorithm' => $signatureAlgorithm), 'signature' => array('algorithm' => $signatureAlgorithm),
'issuer' => false, // this is going to be overwritten later 'issuer' => false, // this is going to be overwritten later
'validity' => array( 'validity' => array(
@ -3463,8 +3722,8 @@ class X509
$altName = array(); $altName = array();
if (isset($subject->domains) && count($subject->domains) > 1) { if (isset($subject->domains) && count($subject->domains)) {
$altName = array_map(array('X509', '_dnsName'), $subject->domains); $altName = array_map(array('\phpseclib\File\X509', '_dnsName'), $subject->domains);
} }
if (isset($subject->ipAddresses) && count($subject->ipAddresses)) { if (isset($subject->ipAddresses) && count($subject->ipAddresses)) {
@ -3669,7 +3928,9 @@ class X509
$currentCert = isset($this->currentCert) ? $this->currentCert : null; $currentCert = isset($this->currentCert) ? $this->currentCert : null;
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : 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'])) { if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) {
$this->currentCert = $crl->currentCert; $this->currentCert = $crl->currentCert;
@ -3784,8 +4045,7 @@ class X509
/** /**
* X.509 certificate signing helper function. * X.509 certificate signing helper function.
* *
* @param object $key * @param \phpseclib\File\X509 $key
* @param \phpseclib\File\X509 $subject
* @param string $signatureAlgorithm * @param string $signatureAlgorithm
* @access public * @access public
* @return mixed * @return mixed
@ -3820,7 +4080,11 @@ class X509
*/ */
function setStartDate($date) function setStartDate($date)
{ {
$this->startDate = @date('D, d M Y H:i:s O', @strtotime($date)); if (!is_object($date) || !is_a($date, 'DateTime')) {
$date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
}
$this->startDate = $date->format('D, d M Y H:i:s O');
} }
/** /**
@ -3844,7 +4108,11 @@ class X509
$temp = chr(ASN1::TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp; $temp = chr(ASN1::TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp;
$this->endDate = new Element($temp); $this->endDate = new Element($temp);
} else { } else {
$this->endDate = @date('D, d M Y H:i:s O', @strtotime($date)); if (!is_object($date) || !is_a($date, 'DateTime')) {
$date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
}
$this->endDate = $date->format('D, d M Y H:i:s O');
} }
} }
@ -3852,7 +4120,7 @@ class X509
* Set Serial Number * Set Serial Number
* *
* @param string $serial * @param string $serial
* @param $base optional * @param int $base optional
* @access public * @access public
*/ */
function setSerialNumber($serial, $base = -256) function setSerialNumber($serial, $base = -256)
@ -4054,6 +4322,10 @@ class X509
} }
$extensions = array_values($extensions); $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; return $result;
} }
@ -4511,7 +4783,6 @@ class X509
* Set the IP Addresses's which the cert is to be valid for * Set the IP Addresses's which the cert is to be valid for
* *
* @access public * @access public
* @param string $ipAddress optional
*/ */
function setIPAddress() function setIPAddress()
{ {
@ -4574,8 +4845,9 @@ class X509
} }
$i = count($rclist); $i = count($rclist);
$revocationDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
$rclist[] = array('userCertificate' => $serial, $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; return $i;
} }
@ -4782,11 +5054,16 @@ class X509
* subject=/O=organization/OU=org unit/CN=common name * subject=/O=organization/OU=org unit/CN=common name
* issuer=/O=organization/CN=common name * issuer=/O=organization/CN=common name
*/ */
if (strlen($str) > ini_get('pcre.backtrack_limit')) {
$temp = $str;
} else {
$temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
// remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff $temp = preg_replace('#-+END.*[\r\n ]*.*#ms', '', $temp, 1);
$temp = preg_replace('#-+[^-]+-+#', '', $temp); }
// remove new lines // remove new lines
$temp = str_replace(array("\r", "\n", ' '), '', $temp); $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; $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
return $temp != false ? $temp : $str; return $temp != false ? $temp : $str;
} }

View file

@ -45,7 +45,6 @@
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @copyright 2006 Jim Wigginton * @copyright 2006 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://pear.php.net/package/Math_BigInteger
*/ */
namespace phpseclib\Math; namespace phpseclib\Math;
@ -244,7 +243,7 @@ class BigInteger
* ?> * ?>
* </code> * </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 * @param int $base
* @return \phpseclib\Math\BigInteger * @return \phpseclib\Math\BigInteger
* @access public * @access public
@ -266,6 +265,10 @@ class BigInteger
if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { 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 // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
$versions = array();
// avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems)
if (strpos(ini_get('disable_functions'), 'phpinfo') === false) {
ob_start(); ob_start();
@phpinfo(); @phpinfo();
$content = ob_get_contents(); $content = ob_get_contents();
@ -273,7 +276,6 @@ class BigInteger
preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches);
$versions = array();
if (!empty($matches[1])) { if (!empty($matches[1])) {
for ($i = 0; $i < count($matches[1]); $i++) { for ($i = 0; $i < count($matches[1]); $i++) {
$fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i])));
@ -286,6 +288,7 @@ class BigInteger
} }
} }
} }
}
// it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+ // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+
switch (true) { switch (true) {
@ -360,8 +363,12 @@ class BigInteger
case 256: case 256:
switch (MATH_BIGINTEGER_MODE) { switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP: case self::MODE_GMP:
$sign = $this->is_negative ? '-' : ''; $this->value = function_exists('gmp_import') ?
$this->value = gmp_init($sign . '0x' . bin2hex($x)); gmp_import($x) :
gmp_init('0x' . bin2hex($x));
if ($this->is_negative) {
$this->value = gmp_neg($this->value);
}
break; break;
case self::MODE_BCMATH: case self::MODE_BCMATH:
// round $len to the nearest 4 (thanks, DavidMJ!) // round $len to the nearest 4 (thanks, DavidMJ!)
@ -438,6 +445,9 @@ class BigInteger
// (?<=^|-)0*: find any 0's that are preceded by the start of the string or by a - (ie. octals) // (?<=^|-)0*: 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 // [^-0-9].*: find any non-numeric characters and then any characters that follow that
$x = preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#', '', $x); $x = preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#', '', $x);
if (!strlen($x) || $x == '-') {
$x = '0';
}
switch (MATH_BIGINTEGER_MODE) { switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP: case self::MODE_GMP:
@ -531,11 +541,11 @@ class BigInteger
$temp = $comparison < 0 ? $this->add(new static(1)) : $this->copy(); $temp = $comparison < 0 ? $this->add(new static(1)) : $this->copy();
$bytes = $temp->toBytes(); $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); $bytes = chr(0);
} }
if (ord($bytes[0]) & 0x80) { if ($this->precision <= 0 && (ord($bytes[0]) & 0x80)) {
$bytes = chr(0) . $bytes; $bytes = chr(0) . $bytes;
} }
@ -548,9 +558,13 @@ class BigInteger
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
} }
if (function_exists('gmp_export')) {
$temp = gmp_export($this->value);
} else {
$temp = gmp_strval(gmp_abs($this->value), 16); $temp = gmp_strval(gmp_abs($this->value), 16);
$temp = (strlen($temp) & 1) ? '0' . $temp : $temp; $temp = (strlen($temp) & 1) ? '0' . $temp : $temp;
$temp = pack('H*', $temp); $temp = pack('H*', $temp);
}
return $this->precision > 0 ? return $this->precision > 0 ?
substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
@ -644,11 +658,11 @@ class BigInteger
{ {
$hex = $this->toHex($twos_compliment); $hex = $this->toHex($twos_compliment);
$bits = ''; $bits = '';
for ($i = strlen($hex) - 8, $start = strlen($hex) & 7; $i >= $start; $i-=8) { for ($i = strlen($hex) - 6, $start = strlen($hex) % 6; $i >= $start; $i-=6) {
$bits = str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT) . $bits; $bits = str_pad(decbin(hexdec(substr($hex, $i, 6))), 24, '0', STR_PAD_LEFT) . $bits;
} }
if ($start) { // hexdec('') == 0 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'); $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');
@ -693,6 +707,7 @@ class BigInteger
} }
$temp = $this->copy(); $temp = $this->copy();
$temp->bitmask = false;
$temp->is_negative = false; $temp->is_negative = false;
$divisor = new static(); $divisor = new static();
@ -829,7 +844,7 @@ class BigInteger
$opts[] = 'OpenSSL'; $opts[] = 'OpenSSL';
} }
if (!empty($opts)) { if (!empty($opts)) {
$engine.= ' (' . implode($opts, ', ') . ')'; $engine.= ' (' . implode('.', $opts) . ')';
} }
return array( return array(
'value' => '0x' . $this->toHex(true), 'value' => '0x' . $this->toHex(true),
@ -1547,7 +1562,9 @@ class BigInteger
$temp_value = array($quotient_value[$q_index]); $temp_value = array($quotient_value[$q_index]);
$temp = $temp->multiply($y); $temp = $temp->multiply($y);
$temp_value = &$temp->value; $temp_value = &$temp->value;
if (count($temp_value)) {
$temp_value = array_merge($adjust, $temp_value); $temp_value = array_merge($adjust, $temp_value);
}
$x = $x->subtract($temp); $x = $x->subtract($temp);
@ -1977,7 +1994,7 @@ class BigInteger
* *
* @see self::_slidingWindow() * @see self::_slidingWindow()
* @access private * @access private
* @param \phpseclib\Math\BigInteger * @param \phpseclib\Math\BigInteger $n
* @return \phpseclib\Math\BigInteger * @return \phpseclib\Math\BigInteger
*/ */
function _mod2($n) function _mod2($n)
@ -2671,7 +2688,7 @@ class BigInteger
* Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y). * Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y).
* *
* @param \phpseclib\Math\BigInteger $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 * @access public
* @see self::equals() * @see self::equals()
* @internal Could return $this->subtract($x), but that's not as fast as what we do do. * @internal Could return $this->subtract($x), but that's not as fast as what we do do.
@ -2680,7 +2697,14 @@ class BigInteger
{ {
switch (MATH_BIGINTEGER_MODE) { switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP: 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: case self::MODE_BCMATH:
return bccomp($this->value, $y->value, 0); return bccomp($this->value, $y->value, 0);
} }
@ -2860,8 +2884,7 @@ class BigInteger
switch (MATH_BIGINTEGER_MODE) { switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP: case self::MODE_GMP:
$temp = new static(); $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); return $this->_normalize($temp);
case self::MODE_BCMATH: case self::MODE_BCMATH:
$left = $this->toBytes(); $left = $this->toBytes();
@ -2877,6 +2900,7 @@ class BigInteger
$length = max(count($this->value), count($x->value)); $length = max(count($this->value), count($x->value));
$result = $this->copy(); $result = $this->copy();
$result->is_negative = false;
$result->value = array_pad($result->value, $length, 0); $result->value = array_pad($result->value, $length, 0);
$x->value = array_pad($x->value, $length, 0); $x->value = array_pad($x->value, $length, 0);
@ -2900,7 +2924,7 @@ class BigInteger
// (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0) // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0)
$temp = $this->toBytes(); $temp = $this->toBytes();
if ($temp == '') { if ($temp == '') {
return ''; return $this->_normalize(new static());
} }
$pre_msb = decbin(ord($temp[0])); $pre_msb = decbin(ord($temp[0]));
$temp = ~$temp; $temp = ~$temp;
@ -3066,7 +3090,7 @@ class BigInteger
* *
* Byte length is equal to $length. Uses \phpseclib\Crypt\Random if it's loaded and mt_rand if it's not. * 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 * @return \phpseclib\Math\BigInteger
* @access private * @access private
*/ */
@ -3435,7 +3459,7 @@ class BigInteger
break; break;
} }
} }
$s = 26 * $i + $j - 1; $s = 26 * $i + $j;
$r->_rshift($s); $r->_rshift($s);
} }
@ -3533,7 +3557,7 @@ class BigInteger
* *
* Removes leading zeros and truncates (if necessary) to maintain the appropriate precision * 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 * @return \phpseclib\Math\BigInteger
* @see self::_trim() * @see self::_trim()
* @access private * @access private
@ -3546,7 +3570,14 @@ class BigInteger
switch (MATH_BIGINTEGER_MODE) { switch (MATH_BIGINTEGER_MODE) {
case self::MODE_GMP: case self::MODE_GMP:
if ($this->bitmask !== false) { 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); $result->value = gmp_and($result->value, $result->bitmask->value);
if ($flip) {
$result->value = gmp_neg($result->value);
}
} }
return $result; return $result;
@ -3561,6 +3592,7 @@ class BigInteger
$value = &$result->value; $value = &$result->value;
if (!count($value)) { if (!count($value)) {
$result->is_negative = false;
return $result; return $result;
} }
@ -3602,8 +3634,8 @@ class BigInteger
/** /**
* Array Repeat * Array Repeat
* *
* @param $input Array * @param array $input
* @param $multiplier mixed * @param mixed $multiplier
* @return array * @return array
* @access private * @access private
*/ */
@ -3617,8 +3649,8 @@ class BigInteger
* *
* Shifts binary strings $shift bits, essentially multiplying by 2**$shift. * Shifts binary strings $shift bits, essentially multiplying by 2**$shift.
* *
* @param $x String * @param string $x (by reference)
* @param $shift Integer * @param int $shift
* @return string * @return string
* @access private * @access private
*/ */
@ -3646,8 +3678,8 @@ class BigInteger
* *
* Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder. * Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder.
* *
* @param $x String * @param string $x (by referenc)
* @param $shift Integer * @param int $shift
* @return string * @return string
* @access private * @access private
*/ */

View file

@ -99,7 +99,7 @@ class SCP
* *
* Connects to an SSH server * 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 * @return \phpseclib\Net\SCP
* @access public * @access public
*/ */
@ -144,6 +144,11 @@ class SCP
return false; 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 if (!$this->ssh->exec('scp -t ' . escapeshellarg($remote_file), false)) { // -t = to
return false; return false;
} }
@ -299,6 +304,9 @@ class SCP
$response = $this->ssh->_get_binary_packet(); $response = $this->ssh->_get_binary_packet();
switch ($response[SSH1::RESPONSE_TYPE]) { switch ($response[SSH1::RESPONSE_TYPE]) {
case NET_SSH1_SMSG_STDOUT_DATA: case NET_SSH1_SMSG_STDOUT_DATA:
if (strlen($response[SSH1::RESPONSE_DATA]) < 4) {
return false;
}
extract(unpack('Nlength', $response[SSH1::RESPONSE_DATA])); extract(unpack('Nlength', $response[SSH1::RESPONSE_DATA]));
return $this->ssh->_string_shift($response[SSH1::RESPONSE_DATA], $length); return $this->ssh->_string_shift($response[SSH1::RESPONSE_DATA], $length);
case NET_SSH1_SMSG_STDERR_DATA: case NET_SSH1_SMSG_STDERR_DATA:

File diff suppressed because it is too large Load diff

View file

@ -179,7 +179,7 @@ class Stream
if ($host[0] == '$') { if ($host[0] == '$') {
$host = substr($host, 1); $host = substr($host, 1);
global $$host; global ${$host};
if (($$host instanceof SFTP) === false) { if (($$host instanceof SFTP) === false) {
return false; return false;
} }
@ -410,7 +410,7 @@ class Stream
{ {
switch ($whence) { switch ($whence) {
case SEEK_SET: case SEEK_SET:
if ($offset >= $this->size || $offset < 0) { if ($offset < 0) {
return false; return false;
} }
break; break;
@ -447,7 +447,9 @@ class Stream
// and https://github.com/php/php-src/blob/master/main/php_streams.h#L592 // and https://github.com/php/php-src/blob/master/main/php_streams.h#L592
switch ($option) { switch ($option) {
case 1: // PHP_STREAM_META_TOUCH 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 2: // PHP_STREAM_OWNER_NAME
case 3: // PHP_STREAM_GROUP_NAME case 3: // PHP_STREAM_GROUP_NAME
return false; return false;
@ -626,7 +628,6 @@ class Stream
* $options. What does 8 correspond to? * $options. What does 8 correspond to?
* *
* @param string $path * @param string $path
* @param int $mode
* @param int $options * @param int $options
* @return bool * @return bool
* @access public * @access public
@ -768,8 +769,8 @@ class Stream
* If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not * 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. * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method.
* *
* @param string * @param string $name
* @param array * @param array $arguments
* @return mixed * @return mixed
* @access public * @access public
*/ */

View file

@ -575,28 +575,46 @@ class SSH1
$this->_string_shift($response[self::RESPONSE_DATA], 4); $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)); $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); $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; $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)); $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); $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->server_key_public_modulus = $server_key_public_modulus;
$this->_string_shift($response[self::RESPONSE_DATA], 4); $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)); $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); $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; $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)); $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); $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->host_key_public_modulus = $host_key_public_modulus;
$this->_string_shift($response[self::RESPONSE_DATA], 4); $this->_string_shift($response[self::RESPONSE_DATA], 4);
// get a list of the supported ciphers // 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))); extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4)));
foreach ($this->supported_ciphers as $mask => $name) { foreach ($this->supported_ciphers as $mask => $name) {
if (($supported_ciphers_mask & (1 << $mask)) == 0) { if (($supported_ciphers_mask & (1 << $mask)) == 0) {
unset($this->supported_ciphers[$mask]); unset($this->supported_ciphers[$mask]);
@ -604,6 +622,9 @@ class SSH1
} }
// get a list of the supported authentications // 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))); extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4)));
foreach ($this->supported_authentications as $mask => $name) { foreach ($this->supported_authentications as $mask => $name) {
if (($supported_authentications_mask & (1 << $mask)) == 0) { if (($supported_authentications_mask & (1 << $mask)) == 0) {
@ -791,6 +812,7 @@ class SSH1
* @see self::interactiveRead() * @see self::interactiveRead()
* @see self::interactiveWrite() * @see self::interactiveWrite()
* @param string $cmd * @param string $cmd
* @param bool $block
* @return mixed * @return mixed
* @access public * @access public
*/ */
@ -895,7 +917,7 @@ class SSH1
/** /**
* Returns the output of an interactive shell when there's a match for $expect * 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. * a regular expression.
* *
* @see self::write() * @see self::write()
@ -904,7 +926,7 @@ class SSH1
* @return bool * @return bool
* @access public * @access public
*/ */
function read($expect, $mode = self::READ__SIMPLE) function read($expect, $mode = self::READ_SIMPLE)
{ {
if (!($this->bitmap & self::MASK_LOGIN)) { if (!($this->bitmap & self::MASK_LOGIN)) {
user_error('Operation disallowed prior to login()'); user_error('Operation disallowed prior to login()');
@ -918,7 +940,7 @@ class SSH1
$match = $expect; $match = $expect;
while (true) { while (true) {
if ($mode == self::READ__REGEX) { if ($mode == self::READ_REGEX) {
preg_match($expect, $this->interactiveBuffer, $matches); preg_match($expect, $this->interactiveBuffer, $matches);
$match = isset($matches[0]) ? $matches[0] : ''; $match = isset($matches[0]) ? $matches[0] : '';
} }
@ -1091,7 +1113,11 @@ class SSH1
} }
$start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 $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); $padding_length = 8 - ($temp['length'] & 7);
$length = $temp['length'] + $padding_length; $length = $temp['length'] + $padding_length;
@ -1099,6 +1125,9 @@ class SSH1
while ($length > 0) { while ($length > 0) {
$temp = fread($this->fsock, $length); $temp = fread($this->fsock, $length);
if (strlen($temp) != $length) {
return false;
}
$raw.= $temp; $raw.= $temp;
$length-= strlen($temp); $length-= strlen($temp);
} }
@ -1112,6 +1141,9 @@ class SSH1
$type = $raw[$padding_length]; $type = $raw[$padding_length];
$data = substr($raw, $padding_length + 1, -4); $data = substr($raw, $padding_length + 1, -4);
if (strlen($raw) < 4) {
return false;
}
$temp = unpack('Ncrc', substr($raw, -4)); $temp = unpack('Ncrc', substr($raw, -4));
//if ( $temp['crc'] != $this->_crc($padding . $type . $data) ) { //if ( $temp['crc'] != $this->_crc($padding . $type . $data) ) {
@ -1354,7 +1386,6 @@ class SSH1
* named constants from it, using the value as the name of the constant and the index as the value of the constant. * 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. * If any of the constants that would be defined already exists, none of the constants will be defined.
* *
* @param array $array
* @access private * @access private
*/ */
function _define_array() function _define_array()
@ -1553,7 +1584,8 @@ class SSH1
* *
* Makes sure that only the last 1MB worth of packets will be logged * 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 * @access private
*/ */
function _append_log($protocol_flags, $message) function _append_log($protocol_flags, $message)

File diff suppressed because it is too large Load diff

View file

@ -43,7 +43,7 @@ use phpseclib\System\SSH\Agent\Identity;
* *
* @package SSH\Agent * @package SSH\Agent
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @access internal * @access public
*/ */
class Agent class Agent
{ {
@ -117,8 +117,9 @@ class Agent
* @return \phpseclib\System\SSH\Agent * @return \phpseclib\System\SSH\Agent
* @access public * @access public
*/ */
function __construct() function __construct($address = null)
{ {
if (!$address) {
switch (true) { switch (true) {
case isset($_SERVER['SSH_AUTH_SOCK']): case isset($_SERVER['SSH_AUTH_SOCK']):
$address = $_SERVER['SSH_AUTH_SOCK']; $address = $_SERVER['SSH_AUTH_SOCK'];
@ -130,6 +131,7 @@ class Agent
user_error('SSH_AUTH_SOCK not found'); user_error('SSH_AUTH_SOCK not found');
return false; return false;
} }
}
$this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr); $this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr);
if (!$this->fsock) { if (!$this->fsock) {
@ -155,23 +157,54 @@ class Agent
$packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES); $packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES);
if (strlen($packet) != fputs($this->fsock, $packet)) { if (strlen($packet) != fputs($this->fsock, $packet)) {
user_error('Connection closed while requesting identities'); 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)); $type = ord(fread($this->fsock, 1));
if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) { if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) {
user_error('Unable to request identities'); user_error('Unable to request identities');
return array();
} }
$identities = 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++) { 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); $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); $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) { 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))); $length = current(unpack('N', substr($key_blob, 0, 4)));
$key_type = substr($key_blob, 4, $length); $key_type = substr($key_blob, 4, $length);
@ -293,14 +326,24 @@ class Agent
if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) { if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) {
user_error('Connection closed attempting to forward data to SSH agent'); user_error('Connection closed attempting to forward data to SSH agent');
return false;
} }
$this->socket_buffer = ''; $this->socket_buffer = '';
$this->expected_bytes = 0; $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); $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)); $agent_reply_data = current(unpack('a*', $agent_reply_data));
return pack('Na*', $agent_reply_bytes, $agent_reply_data); return pack('Na*', $agent_reply_bytes, $agent_reply_data);

View file

@ -32,6 +32,17 @@ use phpseclib\System\SSH\Agent;
*/ */
class Identity 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 * Key Object
* *
@ -59,6 +70,16 @@ class Identity
*/ */
var $fsock; var $fsock;
/**
* Signature flags
*
* @var int
* @access private
* @see self::sign()
* @see self::setHash()
*/
var $flags = 0;
/** /**
* Default Constructor. * Default Constructor.
* *
@ -126,6 +147,31 @@ class Identity
{ {
} }
/**
* Set Hash
*
* ssh-agent doesn't support using hashes for RSA other than SHA1
*
* @param string $hash
* @access public
*/
function setHash($hash)
{
$this->flags = 0;
switch ($hash) {
case 'sha1':
break;
case 'sha256':
$this->flags = self::SSH_AGENT_RSA2_256;
break;
case 'sha512':
$this->flags = self::SSH_AGENT_RSA2_512;
break;
default:
user_error('The only supported hashes for RSA are sha1, sha256 and sha512');
}
}
/** /**
* Create a signature * Create a signature
* *
@ -138,21 +184,58 @@ class Identity
function sign($message) 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 // 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); $packet = pack('Na*', strlen($packet), $packet);
if (strlen($packet) != fputs($this->fsock, $packet)) { if (strlen($packet) != fputs($this->fsock, $packet)) {
user_error('Connection closed during signing'); 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)); $type = ord(fread($this->fsock, 1));
if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) { if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) {
user_error('Unable to retrieve signature'); user_error('Unable to retrieve signature');
return false;
} }
$signature_blob = fread($this->fsock, $length - 1); $signature_blob = fread($this->fsock, $length - 1);
// the only other signature format defined - ssh-dss - is the same length as ssh-rsa if (strlen($signature_blob) != $length - 1) {
// the + 12 is for the other various SSH added length fields user_error('Connection closed during signing');
return substr($signature_blob, strlen('ssh-rsa') + 12); 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;
} }
} }

View file

@ -0,0 +1,3 @@
github: singpolyma
liberapay: singpolyma
patreon: singpolyma

View file

@ -0,0 +1,30 @@
let Prelude = https://prelude.dhall-lang.org/v17.0.0/package.dhall
let phpseclib = \(max: Natural) -> \(filter: (Natural -> Bool)) ->
Prelude.List.map Natural Text
(\(m: Natural) -> "PHPSECLIB='2.0.${Prelude.Natural.show m}'")
(Prelude.List.filter Natural filter (Prelude.Natural.enumerate max))
let Exclusion = { php: Text, env: Text }
in
{
language = "php",
php = [
"7.3",
"7.4",
"8.0"
],
dist = "xenial",
env = [
"PHPSECLIB='^2.0 !=2.0.8'"
] # (phpseclib 28 (\(m: Natural) -> Prelude.Bool.not (Prelude.Natural.equal m 8))
),
matrix = {
exclude = Prelude.List.concatMap Text Exclusion (\(php: Text) ->
Prelude.List.map Text Exclusion (\(env: Text) ->
{ php = php, env = env }
) (phpseclib 7 (\(_: Natural) -> True))
) ["7.3", "7.4", "8.0"],
fast_finish = True
},
before_script = ''
sed -i "s/\"phpseclib\/phpseclib\": \"[^\"]*/\"phpseclib\/phpseclib\": \"$PHPSECLIB/" composer.json && composer install --prefer-source''
}

View file

@ -1,18 +1,82 @@
language: php # Code generated by dhall-to-yaml. DO NOT EDIT.
php: before_script: "sed -i \"s/\\\"phpseclib\\/phpseclib\\\": \\\"[^\\\"]*/\\\"phpseclib\\/phpseclib\\\": \\\"$PHPSECLIB/\" composer.json && composer install --prefer-source"
- 5.3 dist: xenial
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
# - nightly
env: env:
- PHPSECLIB="2.0.0" - "PHPSECLIB='^2.0 !=2.0.8'"
- PHPSECLIB="2.0.1" - "PHPSECLIB='2.0.0'"
- PHPSECLIB="2.0.2" - "PHPSECLIB='2.0.1'"
- PHPSECLIB="2.0.3" - "PHPSECLIB='2.0.2'"
- PHPSECLIB="2.0.4" - "PHPSECLIB='2.0.3'"
- "PHPSECLIB='2.0.4'"
before_script: 'sed -i "s/\"phpseclib\/phpseclib\": \"[^\"]*/\"phpseclib\/phpseclib\": \"$PHPSECLIB/" composer.json && composer install --prefer-source --dev' - "PHPSECLIB='2.0.5'"
- "PHPSECLIB='2.0.6'"
- "PHPSECLIB='2.0.7'"
- "PHPSECLIB='2.0.9'"
- "PHPSECLIB='2.0.10'"
- "PHPSECLIB='2.0.11'"
- "PHPSECLIB='2.0.12'"
- "PHPSECLIB='2.0.13'"
- "PHPSECLIB='2.0.14'"
- "PHPSECLIB='2.0.15'"
- "PHPSECLIB='2.0.16'"
- "PHPSECLIB='2.0.17'"
- "PHPSECLIB='2.0.18'"
- "PHPSECLIB='2.0.19'"
- "PHPSECLIB='2.0.20'"
- "PHPSECLIB='2.0.21'"
- "PHPSECLIB='2.0.22'"
- "PHPSECLIB='2.0.23'"
- "PHPSECLIB='2.0.24'"
- "PHPSECLIB='2.0.25'"
- "PHPSECLIB='2.0.26'"
- "PHPSECLIB='2.0.27'"
language: php
matrix:
exclude:
- env: "PHPSECLIB='2.0.0'"
php: '7.3'
- env: "PHPSECLIB='2.0.1'"
php: '7.3'
- env: "PHPSECLIB='2.0.2'"
php: '7.3'
- env: "PHPSECLIB='2.0.3'"
php: '7.3'
- env: "PHPSECLIB='2.0.4'"
php: '7.3'
- env: "PHPSECLIB='2.0.5'"
php: '7.3'
- env: "PHPSECLIB='2.0.6'"
php: '7.3'
- env: "PHPSECLIB='2.0.0'"
php: '7.4'
- env: "PHPSECLIB='2.0.1'"
php: '7.4'
- env: "PHPSECLIB='2.0.2'"
php: '7.4'
- env: "PHPSECLIB='2.0.3'"
php: '7.4'
- env: "PHPSECLIB='2.0.4'"
php: '7.4'
- env: "PHPSECLIB='2.0.5'"
php: '7.4'
- env: "PHPSECLIB='2.0.6'"
php: '7.4'
- env: "PHPSECLIB='2.0.0'"
php: '8.0'
- env: "PHPSECLIB='2.0.1'"
php: '8.0'
- env: "PHPSECLIB='2.0.2'"
php: '8.0'
- env: "PHPSECLIB='2.0.3'"
php: '8.0'
- env: "PHPSECLIB='2.0.4'"
php: '8.0'
- env: "PHPSECLIB='2.0.5'"
php: '8.0'
- env: "PHPSECLIB='2.0.6'"
php: '8.0'
fast_finish: true
php:
- '7.3'
- '7.4'
- '8.0'

View file

@ -1,11 +1,14 @@
[![Build Status](https://travis-ci.org/singpolyma/openpgp-php.svg?branch=master)](https://travis-ci.org/singpolyma/openpgp-php)
OpenPGP.php: OpenPGP for PHP OpenPGP.php: OpenPGP for PHP
============================ ============================
This is a pure-PHP implementation of the OpenPGP Message Format (RFC 4880). 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 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 is defined by the OpenPGP Working Group of the Internet Engineering Task
@ -13,8 +16,8 @@ Force (IETF) Proposed Standard RFC 4880. The OpenPGP standard was originally
derived from PGP (Pretty Good Privacy), first created by Phil Zimmermann in derived from PGP (Pretty Good Privacy), first created by Phil Zimmermann in
1991. 1991.
* <http://tools.ietf.org/html/rfc4880> * <https://tools.ietf.org/html/rfc4880>
* <http://www.openpgp.org/> * <https://www.openpgp.org/>
Features Features
-------- --------
@ -22,15 +25,26 @@ Features
* Encodes and decodes ASCII-armored OpenPGP messages. * Encodes and decodes ASCII-armored OpenPGP messages.
* Parses OpenPGP messages into their constituent packets. * Parses OpenPGP messages into their constituent packets.
* Supports both old-format (PGP 2.6.x) and new-format (RFC 4880) 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 verifying, signing, encrypting, and decrypting messages <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 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 Users
----- -----
OpenPGP.php is currently being used in the following projects: OpenPGP.php is currently being used in the following projects:
* <https://drupal.org/project/openpgp>
* <https://wordpress.org/plugins/wp-pgp-encrypted-emails/> * <https://wordpress.org/plugins/wp-pgp-encrypted-emails/>
Download Download
@ -38,21 +52,21 @@ Download
To get a local working copy of the development repository, do: 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 Alternatively, you can download the latest development version as a tarball
as follows: as follows:
% wget http://github.com/bendiken/openpgp-php/tarball/master wget https://github.com/singpolyma/openpgp-php/tarball/master
Authors Authors
------- -------
* [Arto Bendiken](mailto:arto.bendiken@gmail.com) - <http://ar.to/> * [Arto Bendiken](mailto:arto.bendiken@gmail.com) (Original author) - <http://ar.to/>
* [Stephen Paul Weber](mailto:singpolyma@singpolyma.net) - <http://singpolyma.net/> * [Stephen Paul Weber](mailto:singpolyma@singpolyma.net) (Maintainer) - <https://singpolyma.net/>
License License
------- -------
OpenPGP.php is free and unencumbered public domain software. For more 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.

View file

@ -13,10 +13,14 @@
} }
], ],
"require": { "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": { "require-dev": {
"phpunit/phpunit": "~4.0" "phpunit/phpunit": "^9.0"
},
"suggest": {
"ext-mcrypt": "required if you use encryption cast5"
}, },
"autoload": { "autoload": {
"classmap": ["lib/"] "classmap": ["lib/"]

View file

@ -0,0 +1,22 @@
OpenPGP.php Examples
====================
The scripts in this folder show how to use this library to perform various tasks
such as [generating a new key](keygen.php), [signing a message](sign.php), and
[verifying a message](verify.php) that has been signed.
To use these examples, make sure [`phpseclib`](http://phpseclib.sourceforge.net/) is available. You can install it
using [Composer](https://getcomposer.org/):
```sh
git clone https://github.com/singpolyma/openpgp-php.git # Clone the repository.
cd openpgp-php
composer install # Use Composer to install the requirements.
```
Once Composer has installed the requirements, run the examples using PHP:
```sh
# Generate a new OpenPGP key; see the `keygen.php` file for parameters.
php ./examples/keygen.php > mykey.gpg
```

View file

@ -0,0 +1,21 @@
<?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');

View file

@ -1,5 +1,6 @@
<?php <?php
@include_once dirname(__FILE__).'/../vendor/autoload.php';
require_once dirname(__FILE__).'/../lib/openpgp.php'; require_once dirname(__FILE__).'/../lib/openpgp.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php'; require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php';
@ -27,5 +28,3 @@ echo "-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA256\n\n";
// trailing whitespace to lines. // trailing whitespace to lines.
echo preg_replace("/^-/", "- -", $packets[0]->data)."\n"; echo preg_replace("/^-/", "- -", $packets[0]->data)."\n";
echo OpenPGP::enarmor($packets[1][0]->to_bytes(), "PGP SIGNATURE"); echo OpenPGP::enarmor($packets[1][0]->to_bytes(), "PGP SIGNATURE");
?>

View file

@ -3,6 +3,7 @@
// USAGE: php examples/deASCIIdeCrypt.php secretkey.asc password message.asc // 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 // 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.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php'; require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_symmetric.php'; require_once dirname(__FILE__).'/../lib/openpgp_crypt_symmetric.php';

View file

@ -1,5 +1,6 @@
<?php <?php
@include_once dirname(__FILE__).'/../vendor/autoload.php';
require_once dirname(__FILE__).'/../lib/openpgp.php'; require_once dirname(__FILE__).'/../lib/openpgp.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php'; require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_symmetric.php'; require_once dirname(__FILE__).'/../lib/openpgp_crypt_symmetric.php';

View file

@ -1,5 +1,6 @@
<?php <?php
@include_once dirname(__FILE__).'/../vendor/autoload.php';
require_once dirname(__FILE__).'/../lib/openpgp.php'; require_once dirname(__FILE__).'/../lib/openpgp.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php'; require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php';

View file

@ -0,0 +1,28 @@
<?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();

View file

@ -0,0 +1,116 @@
<?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

View file

@ -1,5 +1,6 @@
<?php <?php
@include_once dirname(__FILE__).'/../vendor/autoload.php';
require_once dirname(__FILE__).'/../lib/openpgp.php'; require_once dirname(__FILE__).'/../lib/openpgp.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php'; require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php';
@ -18,5 +19,3 @@ $m = $sign->sign($data);
/* Output the raw message bytes to STDOUT */ /* Output the raw message bytes to STDOUT */
echo $m->to_bytes(); echo $m->to_bytes();
?>

View file

@ -1,5 +1,6 @@
<?php <?php
@include_once dirname(__FILE__).'/../vendor/autoload.php';
require_once dirname(__FILE__).'/../lib/openpgp.php'; require_once dirname(__FILE__).'/../lib/openpgp.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php'; require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php';
@ -14,5 +15,3 @@ $verify = new OpenPGP_Crypt_RSA($wkey);
/* Dump verification information to STDOUT */ /* Dump verification information to STDOUT */
var_dump($verify->verify($m)); var_dump($verify->verify($m));
?>

View file

@ -5,7 +5,7 @@
* (RFC 4880). * (RFC 4880).
* *
* @package OpenPGP * @package OpenPGP
* @version 0.3.0 * @version 0.5.0
* @author Arto Bendiken <arto.bendiken@gmail.com> * @author Arto Bendiken <arto.bendiken@gmail.com>
* @author Stephen Paul Weber <singpolyma@singpolyma.net> * @author Stephen Paul Weber <singpolyma@singpolyma.net>
* @see http://github.com/bendiken/openpgp-php * @see http://github.com/bendiken/openpgp-php
@ -18,6 +18,8 @@
* @see http://tools.ietf.org/html/rfc4880 * @see http://tools.ietf.org/html/rfc4880
*/ */
class OpenPGP { 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
* @see http://tools.ietf.org/html/rfc4880#section-6.2 * @see http://tools.ietf.org/html/rfc4880#section-6.2
@ -28,7 +30,7 @@ class OpenPGP {
foreach ($headers as $key => $value) { foreach ($headers as $key => $value) {
$text .= $key . ': ' . (string)$value . "\n"; $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 .= "\n".'=' . base64_encode(substr(pack('N', self::crc24($data)), 1)) . "\n";
$text .= self::footer($marker) . "\n"; $text .= self::footer($marker) . "\n";
return $text; return $text;
@ -42,8 +44,13 @@ class OpenPGP {
$header = self::header($header); $header = self::header($header);
$text = str_replace(array("\r\n", "\r"), array("\n", ''), $text); $text = str_replace(array("\r\n", "\r"), array("\n", ''), $text);
if (($pos1 = strpos($text, $header)) !== FALSE && if (($pos1 = strpos($text, $header)) !== FALSE &&
($pos1 = strpos($text, "\n\n", $pos1 += strlen($header))) !== FALSE && ($pos1 = strpos($text, "\n\n", $pos1 += strlen($header))) !== FALSE) {
($pos2 = strpos($text, "\n=", $pos1 += 2)) !== 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)); return base64_decode($text = substr($text, $pos1, $pos2 - $pos1));
} }
} }
@ -122,20 +129,20 @@ class OpenPGP_S2K {
static function parse(&$input) { static function parse(&$input) {
$s2k = new OpenPGP_S2k(); $s2k = new OpenPGP_S2k();
switch($s2k->type = ord($input{0})) { switch($s2k->type = ord($input[0])) {
case 0: case 0:
$s2k->hash_algorithm = ord($input{1}); $s2k->hash_algorithm = ord($input[1]);
$input = substr($input, 2); $input = substr($input, 2);
break; break;
case 1: case 1:
$s2k->hash_algorithm = ord($input{1}); $s2k->hash_algorithm = ord($input[1]);
$s2k->salt = substr($input, 2, 8); $s2k->salt = substr($input, 2, 8);
$input = substr($input, 10); $input = substr($input, 10);
break; break;
case 3: case 3:
$s2k->hash_algorithm = ord($input{1}); $s2k->hash_algorithm = ord($input[1]);
$s2k->salt = substr($input, 2, 8); $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); $input = substr($input, 11);
break; break;
} }
@ -150,10 +157,12 @@ class OpenPGP_S2K {
$bytes .= chr($this->hash_algorithm); $bytes .= chr($this->hash_algorithm);
break; break;
case 1: case 1:
if(strlen($this->salt) != 8) throw new Exception("Invalid salt length");
$bytes .= chr($this->hash_algorithm); $bytes .= chr($this->hash_algorithm);
$bytes .= $this->salt; $bytes .= $this->salt;
break; break;
case 3: case 3:
if(strlen($this->salt) != 8) throw new Exception("Invalid salt length");
$bytes .= chr($this->hash_algorithm); $bytes .= chr($this->hash_algorithm);
$bytes .= $this->salt; $bytes .= $this->salt;
$bytes .= chr(OpenPGP::encode_s2k_count($this->count)); $bytes .= chr(OpenPGP::encode_s2k_count($this->count));
@ -553,7 +562,7 @@ class OpenPGP_Packet {
} }
function read_byte() { 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) { function read_bytes($count = 1) {
@ -609,7 +618,7 @@ class OpenPGP_AsymmetricSessionKeyPacket extends OpenPGP_Packet {
$rawkeyid = $this->read_bytes(8); $rawkeyid = $this->read_bytes(8);
$this->keyid = ''; $this->keyid = '';
for($i = 0; $i < strlen($rawkeyid); $i++) { // Store KeyID in Hex 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()); $this->key_algorithm = ord($this->read_byte());
@ -625,7 +634,7 @@ class OpenPGP_AsymmetricSessionKeyPacket extends OpenPGP_Packet {
$bytes = chr($this->version); $bytes = chr($this->version);
for($i = 0; $i < strlen($this->keyid); $i += 2) { 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); $bytes .= chr($this->key_algorithm);
@ -685,13 +694,15 @@ class OpenPGP_SignaturePacket extends OpenPGP_Packet {
switch($this->version = ord($this->read_byte())) { switch($this->version = ord($this->read_byte())) {
case 2: case 2:
case 3: 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()); $this->signature_type = ord($this->read_byte());
$creation_time = $this->read_timestamp(); $creation_time = $this->read_timestamp();
$keyid = $this->read_bytes(8); $keyid = $this->read_bytes(8);
$keyidHex = ''; $keyidHex = '';
for($i = 0; $i < strlen($keyid); $i++) { // Store KeyID in Hex 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(); $this->hashed_subpackets = array();
@ -768,7 +779,7 @@ class OpenPGP_SignaturePacket extends OpenPGP_Packet {
foreach((array)$this->unhashed_subpackets as $p) { foreach((array)$this->unhashed_subpackets as $p) {
if($p instanceof OpenPGP_SignaturePacket_IssuerPacket) { if($p instanceof OpenPGP_SignaturePacket_IssuerPacket) {
for($i = 0; $i < strlen($p->data); $i += 2) { 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; break;
} }
@ -975,8 +986,8 @@ class OpenPGP_SignaturePacket_ExportableCertificationPacket extends OpenPGP_Sign
class OpenPGP_SignaturePacket_TrustSignaturePacket extends OpenPGP_SignaturePacket_Subpacket { class OpenPGP_SignaturePacket_TrustSignaturePacket extends OpenPGP_SignaturePacket_Subpacket {
function read() { function read() {
$this->depth = ord($this->input{0}); $this->depth = ord($this->input[0]);
$this->trust = ord($this->input{1}); $this->trust = ord($this->input[1]);
} }
function body() { function body() {
@ -1052,7 +1063,7 @@ class OpenPGP_SignaturePacket_RevocationKeyPacket extends OpenPGP_SignaturePacke
$bytes .= chr($this->key_algorithm); $bytes .= chr($this->key_algorithm);
for($i = 0; $i < strlen($this->fingerprint); $i += 2) { 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; return $bytes;
@ -1072,7 +1083,7 @@ class OpenPGP_SignaturePacket_IssuerPacket extends OpenPGP_SignaturePacket_Subpa
function body() { function body() {
$bytes = ''; $bytes = '';
for($i = 0; $i < strlen($this->data); $i += 2) { 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; return $bytes;
} }
@ -1305,7 +1316,7 @@ class OpenPGP_OnePassSignaturePacket extends OpenPGP_Packet {
function body() { function body() {
$body = chr($this->version).chr($this->signature_type).chr($this->hash_algorithm).chr($this->key_algorithm); $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) { 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); $body .= chr((int)$this->nested);
return $body; return $body;
@ -1594,6 +1605,13 @@ class OpenPGP_CompressedDataPacket extends OpenPGP_Packet implements IteratorAgg
public $algorithm; public $algorithm;
/* see http://tools.ietf.org/html/rfc4880#section-9.3 */ /* see http://tools.ietf.org/html/rfc4880#section-9.3 */
static $algorithms = array(0 => 'Uncompressed', 1 => 'ZIP', 2 => 'ZLIB', 3 => 'BZip2'); 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() { function read() {
$this->algorithm = ord($this->read_byte()); $this->algorithm = ord($this->read_byte());
$this->data = $this->read_bytes($this->length); $this->data = $this->read_bytes($this->length);

View file

@ -182,8 +182,10 @@ class OpenPGP_Crypt_RSA {
$keys = new self($keys); $keys = new self($keys);
} }
$session_key = NULL;
foreach($message as $p) { foreach($message as $p) {
if($p instanceof OpenPGP_AsymmetricSessionKeyPacket) { if($p instanceof OpenPGP_AsymmetricSessionKeyPacket) {
$session_key = $p;
if($keys instanceof Crypt_RSA) { if($keys instanceof Crypt_RSA) {
$sk = self::try_decrypt_session($keys, substr($p->encrypted_data, 2)); $sk = self::try_decrypt_session($keys, substr($p->encrypted_data, 2));
} else if(strlen(str_replace('0', '', $p->keyid)) < 1) { } else if(strlen(str_replace('0', '', $p->keyid)) < 1) {
@ -203,23 +205,26 @@ class OpenPGP_Crypt_RSA {
} }
} }
if (!$session_key) throw new Exception("Not an asymmetrically encrypted message");
return NULL; /* Failed */ return NULL; /* Failed */
} }
static function try_decrypt_session($key, $edata) { static function try_decrypt_session($key, $edata) {
$key->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); $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); $sk = substr($data, 1, strlen($data)-3);
$chk = unpack('n', substr($data, -2)); $chk = unpack('n', substr($data, -2));
$chk = reset($chk); $chk = reset($chk);
$sk_chk = 0; $sk_chk = 0;
for($i = 0; $i < strlen($sk); $i++) { 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; 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') { static function crypt_rsa_key($mod, $exp, $hash='SHA256') {

View file

@ -1,15 +1,15 @@
<?php <?php
use phpseclib\Crypt\TripleDES as Crypt_TripleDES;
use phpseclib\Crypt\AES as Crypt_AES; 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; 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'; require_once dirname(__FILE__).'/openpgp.php';
@include_once dirname(__FILE__).'/openpgp_crypt_rsa.php'; @include_once dirname(__FILE__).'/openpgp_crypt_rsa.php';
@include_once dirname(__FILE__).'/openpgp_mcrypt_wrapper.php'; @include_once dirname(__FILE__).'/openpgp_mcrypt_wrapper.php';
@include_once dirname(__FILE__).'/openpgp_openssl_wrapper.php';
class OpenPGP_Crypt_Symmetric { class OpenPGP_Crypt_Symmetric {
public static function encrypt($passphrases_and_keys, $message, $symmetric_algorithm=9) { public static function encrypt($passphrases_and_keys, $message, $symmetric_algorithm=9) {
@ -40,7 +40,7 @@ class OpenPGP_Crypt_Symmetric {
$esk = pack('n', OpenPGP::bitlength($esk)) . $esk; $esk = pack('n', OpenPGP::bitlength($esk)) . $esk;
array_unshift($encrypted, new OpenPGP_AsymmetricSessionKeyPacket($pass->algorithm, $pass->fingerprint(), $esk)); array_unshift($encrypted, new OpenPGP_AsymmetricSessionKeyPacket($pass->algorithm, $pass->fingerprint(), $esk));
} else if(is_string($pass)) { } 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)); $cipher->setKey($s2k->make_key($pass, $key_bytes));
$esk = $cipher->encrypt(chr($symmetric_algorithm) . $key); $esk = $cipher->encrypt(chr($symmetric_algorithm) . $key);
array_unshift($encrypted, new OpenPGP_SymmetricSessionKeyPacket($s2k, $esk, $symmetric_algorithm)); array_unshift($encrypted, new OpenPGP_SymmetricSessionKeyPacket($s2k, $esk, $symmetric_algorithm));
@ -62,7 +62,7 @@ class OpenPGP_Crypt_Symmetric {
$padAmount = $key_block_bytes - (strlen($p->encrypted_data) % $key_block_bytes); $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)); $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 { } else {
list($cipher, $key_bytes, $key_block_bytes) = self::getCipher($p->symmetric_algorithm); 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)); $decrypted = self::decryptPacket($epacket, $p->symmetric_algorithm, $p->s2k->make_key($pass, $key_bytes));
@ -75,6 +75,31 @@ class OpenPGP_Crypt_Symmetric {
return NULL; /* If we get here, we failed */ 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) { public static function decryptSecretKey($pass, $packet) {
$packet = clone $packet; // Do not mutate orinigal $packet = clone $packet; // Do not mutate orinigal
@ -97,6 +122,7 @@ class OpenPGP_Crypt_Symmetric {
if($chk != $mkChk) return NULL; if($chk != $mkChk) return NULL;
} }
$packet->s2k = NULL;
$packet->s2k_useage = 0; $packet->s2k_useage = 0;
$packet->symmetric_algorithm = 0; $packet->symmetric_algorithm = 0;
$packet->encrypted_data = NULL; $packet->encrypted_data = NULL;
@ -146,30 +172,46 @@ class OpenPGP_Crypt_Symmetric {
public static function getCipher($algo) { public static function getCipher($algo) {
$cipher = NULL; $cipher = NULL;
switch($algo) { switch($algo) {
case NULL:
case 0:
throw new Exception("Data is already unencrypted");
case 2: case 2:
$cipher = new Crypt_TripleDES(CRYPT_DES_MODE_CFB); $cipher = new Crypt_TripleDES(Crypt_TripleDES::MODE_CFB);
$key_bytes = 24; $key_bytes = 24;
$key_block_bytes = 8; $key_block_bytes = 8;
break; break;
case 3: 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); $cipher = new MCryptWrapper(MCRYPT_CAST_128);
} else {
throw new Exception("Unsupported cipher: you must have mcrypt installed to use CAST5");
} }
break; break;
case 4:
$cipher = new Crypt_Blowfish(Crypt_Blowfish::MODE_CFB);
$key_bytes = 16;
$key_block_bytes = 8;
break;
case 7: case 7:
$cipher = new Crypt_AES(CRYPT_AES_MODE_CFB); $cipher = new Crypt_AES(Crypt_AES::MODE_CFB);
$cipher->setKeyLength(128); $cipher->setKeyLength(128);
break; break;
case 8: case 8:
$cipher = new Crypt_AES(CRYPT_AES_MODE_CFB); $cipher = new Crypt_AES(Crypt_AES::MODE_CFB);
$cipher->setKeyLength(192); $cipher->setKeyLength(192);
break; break;
case 9: case 9:
$cipher = new Crypt_AES(CRYPT_AES_MODE_CFB); $cipher = new Crypt_AES(Crypt_AES::MODE_CFB);
$cipher->setKeyLength(256); $cipher->setKeyLength(256);
break; 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 if(!$cipher) return array(NULL, NULL, NULL); // Unsupported cipher
if(!isset($key_bytes)) $key_bytes = isset($cipher->key_size)?$cipher->key_size:$cipher->key_length; if(!isset($key_bytes)) $key_bytes = isset($cipher->key_size)?$cipher->key_size:$cipher->key_length;
@ -187,7 +229,7 @@ class OpenPGP_Crypt_Symmetric {
public static function checksum($s) { public static function checksum($s) {
$mkChk = 0; $mkChk = 0;
for($i = 0; $i < strlen($s); $i++) { for($i = 0; $i < strlen($s); $i++) {
$mkChk = ($mkChk + ord($s{$i})) % 65536; $mkChk = ($mkChk + ord($s[$i])) % 65536;
} }
return $mkChk; return $mkChk;
} }

View file

@ -0,0 +1,33 @@
<?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);
}
}
}

View file

@ -8,6 +8,10 @@
<file>tests/suite.php</file> <file>tests/suite.php</file>
</testsuite> </testsuite>
<testsuite name="Signature">
<file>tests/suite.php</file>
</testsuite>
<testsuite name="MessageVerification"> <testsuite name="MessageVerification">
<file>tests/phpseclib_suite.php</file> <file>tests/phpseclib_suite.php</file>
</testsuite> </testsuite>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1 @@
<EFBFBD> σθΆh<CE86>±Ο³ΦΙfuάhθύsΕώ®Ωψ°Όώ_VF•4Σ

View file

@ -0,0 +1,3 @@
Œ 
cýІ èÑÔÖÒ9=õ­Çâ]¼TföA ¼c«vìåeøkº€Èʲõ¡©n}%.<16>lòëuÛ?\êåI
ð[øõblÊ

View file

@ -1,4 +1,5 @@
<?php <?php
use PHPUnit\Framework\TestCase;
/* The tests which require phpseclib */ /* The tests which require phpseclib */
@ -6,7 +7,7 @@ require_once dirname(__FILE__).'/../lib/openpgp.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php'; require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_symmetric.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) { public function oneMessageRSA($pkey, $path) {
$pkeyM = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $pkey)); $pkeyM = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $pkey));
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path)); $m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
@ -51,7 +52,7 @@ class MessageVerification extends PHPUnit_Framework_TestCase {
} }
class KeyVerification extends PHPUnit_Framework_TestCase { class KeyVerification extends TestCase {
public function oneKeyRSA($path) { public function oneKeyRSA($path) {
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path)); $m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
$verify = new OpenPGP_Crypt_RSA($m); $verify = new OpenPGP_Crypt_RSA($m);
@ -64,7 +65,7 @@ class KeyVerification extends PHPUnit_Framework_TestCase {
} }
class Decryption extends PHPUnit_Framework_TestCase { class Decryption extends TestCase {
public function oneSymmetric($pass, $cnt, $path) { public function oneSymmetric($pass, $cnt, $path) {
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path)); $m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
$m2 = OpenPGP_Crypt_Symmetric::decryptSymmetric($pass, $m); $m2 = OpenPGP_Crypt_Symmetric::decryptSymmetric($pass, $m);
@ -76,18 +77,28 @@ class Decryption extends PHPUnit_Framework_TestCase {
} }
} }
public function testDecryptAES() {
$this->oneSymmetric("hello", "PGP\n", "symmetric-aes.gpg");
}
public function testDecrypt3DES() { public function testDecrypt3DES() {
$this->oneSymmetric("hello", "PGP\n", "symmetric-3des.gpg"); $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"); $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() { public function testDecryptSessionKey() {
$this->oneSymmetric("hello", "PGP\n", "symmetric-with-session-key.gpg"); $this->oneSymmetric("hello", "PGP\n", "symmetric-with-session-key.gpg");
} }
@ -109,25 +120,89 @@ class Decryption extends PHPUnit_Framework_TestCase {
} }
} }
public function testDecryptRoundtrip() {
$m = new OpenPGP_Message(array(new OpenPGP_LiteralDataPacket("hello\n")));
$key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg'));
$em = OpenPGP_Crypt_Symmetric::encrypt($key, $m);
foreach($key as $packet) {
if(!($packet instanceof OpenPGP_SecretKeyPacket)) continue;
$decryptor = new OpenPGP_Crypt_RSA($packet);
$m2 = $decryptor->decrypt($em);
foreach($m2 as $p) {
if($p instanceof OpenPGP_LiteralDataPacket) {
$this->assertEquals($p->data, "hello\n");
}
}
}
}
public function testDecryptSecretKey() { public function testDecryptSecretKey() {
$key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/encryptedSecretKey.gpg')); $key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/encryptedSecretKey.gpg'));
$skey = OpenPGP_Crypt_Symmetric::decryptSecretKey("hello", $key[0]); $skey = OpenPGP_Crypt_Symmetric::decryptSecretKey("hello", $key[0]);
$this->assertSame(!!$skey, true); $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);
} }
class Encryption extends PHPUnit_Framework_TestCase { public function testAlreadyDecryptedSecretKey() {
public function testEncryptSymmetric() { $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 TestCase {
public function oneSymmetric($algorithm) {
$data = new OpenPGP_LiteralDataPacket('This is text.', array('format' => 'u', 'filename' => 'stuff.txt')); $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); $decrypted = OpenPGP_Crypt_Symmetric::decryptSymmetric('secret', $encrypted);
$this->assertEquals($decrypted[0]->data, 'This is text.'); $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() { public function testEncryptAsymmetric() {
$key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg')); $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')); $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_Crypt_Symmetric::encrypt($key, new OpenPGP_Message(array($data)));
$encrypted = OpenPGP_Message::parse($encrypted->to_bytes());
$decryptor = new OpenPGP_Crypt_RSA($key); $decryptor = new OpenPGP_Crypt_RSA($key);
$decrypted = $decryptor->decrypt($encrypted); $decrypted = $decryptor->decrypt($encrypted);
$this->assertEquals($decrypted[0]->data, 'This is text.'); $this->assertEquals($decrypted[0]->data, 'This is text.');

View file

@ -1,8 +1,9 @@
<?php <?php
use PHPUnit\Framework\TestCase;
require_once dirname(__FILE__).'/../lib/openpgp.php'; require_once dirname(__FILE__).'/../lib/openpgp.php';
class Serialization extends PHPUnit_Framework_TestCase { class Serialization extends TestCase {
public function oneSerialization($path) { public function oneSerialization($path) {
$in = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path)); $in = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
$mid = $in->to_bytes(); $mid = $in->to_bytes();
@ -14,7 +15,6 @@ class Serialization extends PHPUnit_Framework_TestCase {
$this->oneSerialization("000001-006.public_key"); $this->oneSerialization("000001-006.public_key");
} }
public function test000002013user_id() { public function test000002013user_id() {
$this->oneSerialization("000002-013.user_id"); $this->oneSerialization("000002-013.user_id");
} }
@ -376,7 +376,7 @@ class Serialization extends PHPUnit_Framework_TestCase {
} }
} }
class Fingerprint extends PHPUnit_Framework_TestCase { class Fingerprint extends TestCase {
public function oneFingerprint($path, $kf) { public function oneFingerprint($path, $kf) {
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path)); $m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
$this->assertEquals($m[0]->fingerprint(), $kf); $this->assertEquals($m[0]->fingerprint(), $kf);
@ -397,4 +397,31 @@ class Fingerprint extends PHPUnit_Framework_TestCase {
public function test000035006public_key() { public function test000035006public_key() {
$this->oneFingerprint("000035-006.public_key", "CB7933459F59C70DF1C3FBEEDEDC3ECF689AF56D"); $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");
}
} }