[securemail] Upgrade Composer dependencies to fix PHP8 issue

- Upgrading phpseclib/phpseclib (2.0.4 => 2.0.34)
- Upgrading singpolyma/openpgp-php (0.3.0 => 0.5.0)
This commit is contained in:
Hypolite Petovan 2021-11-16 15:36:33 -05:00
parent fb77e3c5ea
commit 0c7fd9a34d
65 changed files with 5407 additions and 3230 deletions

View file

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

View file

@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b3a33a493eae34d9245ca7cc1b4572ea",
"content-hash": "159d6e7134aeceeb05fdf60ef672f3a3",
"packages": [
{
"name": "phpseclib/phpseclib",
"version": "2.0.4",
"version": "2.0.34",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "ab8028c93c03cc8d9c824efa75dc94f1db2369bf"
"reference": "98a6fe587f3481aea319eef7e656d02cfe1675ec"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/ab8028c93c03cc8d9c824efa75dc94f1db2369bf",
"reference": "ab8028c93c03cc8d9c824efa75dc94f1db2369bf",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/98a6fe587f3481aea319eef7e656d02cfe1675ec",
"reference": "98a6fe587f3481aea319eef7e656d02cfe1675ec",
"shasum": ""
},
"require": {
@ -25,8 +25,7 @@
},
"require-dev": {
"phing/phing": "~2.7",
"phpunit/phpunit": "~4.0",
"sami/sami": "~2.0",
"phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4",
"squizlabs/php_codesniffer": "~2.0"
},
"suggest": {
@ -96,27 +95,49 @@
"x.509",
"x509"
],
"time": "2016-10-04T00:57:04+00:00"
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/2.0.34"
},
"funding": [
{
"url": "https://github.com/terrafrost",
"type": "github"
},
{
"url": "https://www.patreon.com/phpseclib",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
"type": "tidelift"
}
],
"time": "2021-10-27T02:46:30+00:00"
},
{
"name": "singpolyma/openpgp-php",
"version": "0.3.0",
"version": "0.5.0",
"source": {
"type": "git",
"url": "https://github.com/singpolyma/openpgp-php.git",
"reference": "6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8"
"reference": "69292f6a46ed7f687083bfb8974b161a41ab213c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/singpolyma/openpgp-php/zipball/6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8",
"reference": "6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8",
"url": "https://api.github.com/repos/singpolyma/openpgp-php/zipball/69292f6a46ed7f687083bfb8974b161a41ab213c",
"reference": "69292f6a46ed7f687083bfb8974b161a41ab213c",
"shasum": ""
},
"require": {
"phpseclib/phpseclib": ">=2.0.0 <=2.0.4"
"php": "^5.6 || ^7.0 || ^8.0",
"phpseclib/phpseclib": "^2.0 !=2.0.8"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
"phpunit/phpunit": "^9.0"
},
"suggest": {
"ext-mcrypt": "required if you use encryption cast5"
},
"type": "library",
"autoload": {
@ -139,7 +160,25 @@
}
],
"description": "Pure-PHP implementation of the OpenPGP Message Format (RFC 4880)",
"time": "2017-04-12T21:23:15+00:00"
"support": {
"issues": "https://github.com/singpolyma/openpgp-php/issues",
"source": "https://github.com/singpolyma/openpgp-php/tree/0.5.0"
},
"funding": [
{
"url": "https://github.com/singpolyma",
"type": "github"
},
{
"url": "https://liberapay.com/singpolyma",
"type": "liberapay"
},
{
"url": "https://www.patreon.com/singpolyma",
"type": "patreon"
}
],
"time": "2021-05-26T00:35:20+00:00"
}
],
"packages-dev": [],
@ -149,5 +188,6 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
"platform-dev": [],
"plugin-api-version": "2.0.0"
}

View file

@ -37,8 +37,8 @@ namespace Composer\Autoload;
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
@ -60,7 +60,7 @@ class ClassLoader
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
@ -279,7 +279,7 @@ class ClassLoader
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**

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

View file

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

View file

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

View file

@ -1,17 +1,18 @@
[
{
"packages": [
{
"name": "phpseclib/phpseclib",
"version": "2.0.4",
"version_normalized": "2.0.4.0",
"version": "2.0.34",
"version_normalized": "2.0.34.0",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "ab8028c93c03cc8d9c824efa75dc94f1db2369bf"
"reference": "98a6fe587f3481aea319eef7e656d02cfe1675ec"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/ab8028c93c03cc8d9c824efa75dc94f1db2369bf",
"reference": "ab8028c93c03cc8d9c824efa75dc94f1db2369bf",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/98a6fe587f3481aea319eef7e656d02cfe1675ec",
"reference": "98a6fe587f3481aea319eef7e656d02cfe1675ec",
"shasum": ""
},
"require": {
@ -19,8 +20,7 @@
},
"require-dev": {
"phing/phing": "~2.7",
"phpunit/phpunit": "~4.0",
"sami/sami": "~2.0",
"phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4",
"squizlabs/php_codesniffer": "~2.0"
},
"suggest": {
@ -29,7 +29,7 @@
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
"ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
},
"time": "2016-10-04T00:57:04+00:00",
"time": "2021-10-27T02:46:30+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -91,30 +91,53 @@
"twofish",
"x.509",
"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",
"version": "0.3.0",
"version_normalized": "0.3.0.0",
"version": "0.5.0",
"version_normalized": "0.5.0.0",
"source": {
"type": "git",
"url": "https://github.com/singpolyma/openpgp-php.git",
"reference": "6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8"
"reference": "69292f6a46ed7f687083bfb8974b161a41ab213c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/singpolyma/openpgp-php/zipball/6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8",
"reference": "6006111bbc4c3b6cb8f0acb7d6c4a7047df366e8",
"url": "https://api.github.com/repos/singpolyma/openpgp-php/zipball/69292f6a46ed7f687083bfb8974b161a41ab213c",
"reference": "69292f6a46ed7f687083bfb8974b161a41ab213c",
"shasum": ""
},
"require": {
"phpseclib/phpseclib": ">=2.0.0 <=2.0.4"
"php": "^5.6 || ^7.0 || ^8.0",
"phpseclib/phpseclib": "^2.0 !=2.0.8"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
"phpunit/phpunit": "^9.0"
},
"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",
"installation-source": "dist",
"autoload": {
@ -136,6 +159,28 @@
"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
http://phpseclib.sourceforge.net/
Copyright (c) 2011-2019 TerraFrost and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View file

@ -1,22 +1,62 @@
# phpseclib - PHP Secure Communications Library
[![Build Status](https://travis-ci.org/phpseclib/phpseclib.svg?branch=2.0)](https://travis-ci.org/phpseclib/phpseclib)
[![Build Status](https://travis-ci.com/phpseclib/phpseclib.svg?branch=2.0)](https://travis-ci.com/phpseclib/phpseclib)
MIT-licensed pure-PHP implementations of an arbitrary-precision integer
arithmetic library, fully PKCS#1 (v2.1) compliant RSA, DES, 3DES, RC4, Rijndael,
AES, Blowfish, Twofish, SSH-1, SSH-2, SFTP, and X.509
## Supporting phpseclib
- [Become a backer or sponsor on Patreon](https://www.patreon.com/phpseclib)
- [One-time donation via PayPal or crypto-currencies](http://sourceforge.net/donate/index.php?group_id=198487)
- [Subscribe to Tidelift](https://tidelift.com/subscription/pkg/packagist-phpseclib-phpseclib?utm_source=packagist-phpseclib-phpseclib&utm_medium=referral&utm_campaign=readme)
## Introduction
MIT-licensed pure-PHP implementations of the following:
SSH-2, SFTP, X.509, an arbitrary-precision integer arithmetic library, Ed25519 / Ed449 / Curve25519 / Curve449, ECDSA / ECDH (with support for 66 curves), RSA (PKCS#1 v2.2 compliant), DSA / DH, DES / 3DES / RC4 / Rijndael / AES / Blowfish / Twofish / Salsa20 / ChaCha20, GCM / Poly1305
* [Download (1.0.4)](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.4.zip/download)
* [Browse Git](https://github.com/phpseclib/phpseclib)
* [Code Coverage Report](http://phpseclib.bantux.org/code_coverage/2.0/latest/)
<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 / Manual](http://phpseclib.sourceforge.net/)
* [API Documentation](http://phpseclib.bantux.org/api/2.0/) (generated by Sami)
* [Documentation / Manual](https://phpseclib.com/)
* [API Documentation](https://api.phpseclib.com/2.0/) (generated by Doctum)
## Branches
### master
* Development Branch
* Unstable API
* Do not use in production
### 3.0
* Long term support (LTS) release
* Major expansion of cryptographic primitives
* Minimum PHP version: 5.6.1
* PSR-4 autoloading with namespace rooted at `\phpseclib3`
* Install via Composer: `composer require phpseclib/phpseclib:~3.0`
### 2.0
* Long term support (LTS) release
* Modernized version of 1.0
* Minimum PHP version: 5.3.3
* PSR-4 autoloading with namespace rooted at `\phpseclib`
* Install via Composer: `composer require phpseclib/phpseclib:~2.0`
### 1.0
* Long term support (LTS) release
* PHP4 compatible
* Composer compatible (PSR-0 autoloading)
* Install using Composer: `composer require phpseclib/phpseclib:~1.0`
* Install using PEAR: See [phpseclib PEAR Channel Documentation](http://phpseclib.sourceforge.net/pear.htm)
* [Download 1.0.19 as ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.19.zip/download)
## Security contact information
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
## Support
@ -26,40 +66,29 @@ Need Support?
* [Create a Support Ticket on GitHub](https://github.com/phpseclib/phpseclib/issues/new)
* [Browse the Support Forum](http://www.frostjedi.com/phpbb/viewforum.php?f=46) (no longer in use)
## Installing Development Dependencies
Dependencies are managed via Composer.
1. Download the [`composer.phar`](https://getcomposer.org/composer.phar) executable as per the
[Composer Download Instructions](https://getcomposer.org/download/), e.g. by running
``` sh
curl -sS https://getcomposer.org/installer | php
```
2. Install Dependencies
``` sh
php composer.phar install
```
## Contributing
1. Fork the Project
2. Install Development Dependencies
2. Ensure you have Composer installed (see [Composer Download Instructions](https://getcomposer.org/download/))
3. Create a Feature Branch
3. Install Development Dependencies
4. (Recommended) Run the Test Suite
``` sh
composer install
```
4. Create a Feature Branch
5. (Recommended) Run the Test Suite
``` sh
vendor/bin/phpunit
```
5. (Recommended) Check whether your code conforms to our Coding Standards by running
6. (Recommended) Check whether your code conforms to our Coding Standards by running
``` sh
vendor/bin/phing -f build/build.xml sniff
```
6. Send us a Pull Request
7. Send us a Pull Request

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": {
"phing/phing": "~2.7",
"phpunit/phpunit": "~4.0",
"sami/sami": "~2.0",
"phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4",
"squizlabs/php_codesniffer": "~2.0"
},
"suggest": {

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -45,6 +45,10 @@ class Random
*/
static function string($length)
{
if (!$length) {
return '';
}
if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
try {
return \random_bytes($length);
@ -62,7 +66,7 @@ class Random
// method 1. prior to PHP 5.3 this would call rand() on windows hence the function_exists('class_alias') call.
// ie. class_alias is a function that was introduced in PHP 5.3
if (extension_loaded('mcrypt') && function_exists('class_alias')) {
return mcrypt_create_iv($length);
return @mcrypt_create_iv($length);
}
// method 2. openssl_random_pseudo_bytes was introduced in PHP 5.3.0 but prior to PHP 5.3.4 there was,
// to quote <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');
}
if ($fp !== true && $fp !== false) { // surprisingly faster than !is_bool() or is_resource()
return fread($fp, $length);
$temp = fread($fp, $length);
if (strlen($temp) == $length) {
return $temp;
}
}
// method 3. pretty much does the same thing as method 2 per the following url:
// https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391
@ -101,7 +108,7 @@ class Random
// not doing. regardless, this'll only be called if this PHP script couldn't open /dev/urandom due to open_basedir
// restrictions or some such
if (extension_loaded('mcrypt')) {
return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
return @mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
}
}
// at this point we have no choice but to use a pure-PHP CSPRNG

View file

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

View file

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

View file

@ -25,6 +25,8 @@ namespace phpseclib\File;
use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger;
use DateTime;
use DateTimeZone;
/**
* Pure-PHP ASN.1 Parser
@ -232,8 +234,11 @@ class ASN1
{
$current = array('start' => $start);
if (!isset($encoded[$encoded_pos])) {
return false;
}
$type = ord($encoded[$encoded_pos++]);
$start++;
$startOffset = 1;
$constructed = ($type >> 5) & 1;
@ -242,14 +247,28 @@ class ASN1
$tag = 0;
// process septets (since the eighth bit is ignored, it's not an octet)
do {
$loop = ord($encoded[0]) >> 7;
if (!isset($encoded[$encoded_pos])) {
return false;
}
$temp = ord($encoded[$encoded_pos++]);
$startOffset++;
$loop = $temp >> 7;
$tag <<= 7;
$tag |= ord($encoded[$encoded_pos++]) & 0x7F;
$start++;
$temp &= 0x7F;
// "bits 7 to 1 of the first subsequent octet shall not all be zero"
if ($startOffset == 2 && $temp == 0) {
return false;
}
$tag |= $temp;
} while ($loop);
}
$start+= $startOffset;
// Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13
if (!isset($encoded[$encoded_pos])) {
return false;
}
$length = ord($encoded[$encoded_pos++]);
$start++;
if ($length == 0x80) { // indefinite length
@ -306,6 +325,9 @@ class ASN1
$remainingLength = $length;
while ($remainingLength > 0) {
$temp = $this->_decode_ber($content, $start, $content_pos);
if ($temp === false) {
break;
}
$length = $temp['length'];
// end-of-content octets - see paragraph 8.1.5
if (substr($content, $content_pos + $length, 2) == "\0\0") {
@ -338,13 +360,16 @@ class ASN1
switch ($tag) {
case self::TYPE_BOOLEAN:
// "The contents octets shall consist of a single octet." -- paragraph 8.2.1
//if (strlen($content) != 1) {
// return false;
//}
if ($constructed || strlen($content) != 1) {
return false;
}
$current['content'] = (bool) ord($content[$content_pos]);
break;
case self::TYPE_INTEGER:
case self::TYPE_ENUMERATED:
if ($constructed) {
return false;
}
$current['content'] = new BigInteger(substr($content, $content_pos), -256);
break;
case self::TYPE_REAL: // not currently supported
@ -357,19 +382,22 @@ class ASN1
$current['content'] = substr($content, $content_pos);
} else {
$temp = $this->_decode_ber($content, $start, $content_pos);
if ($temp === false) {
return false;
}
$length-= (strlen($content) - $content_pos);
$last = count($temp) - 1;
for ($i = 0; $i < $last; $i++) {
// all subtags should be bit strings
//if ($temp[$i]['type'] != self::TYPE_BIT_STRING) {
// return false;
//}
if ($temp[$i]['type'] != self::TYPE_BIT_STRING) {
return false;
}
$current['content'].= substr($temp[$i]['content'], 1);
}
// all subtags should be bit strings
//if ($temp[$last]['type'] != self::TYPE_BIT_STRING) {
// return false;
//}
if ($temp[$last]['type'] != self::TYPE_BIT_STRING) {
return false;
}
$current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1);
}
break;
@ -381,11 +409,14 @@ class ASN1
$length = 0;
while (substr($content, $content_pos, 2) != "\0\0") {
$temp = $this->_decode_ber($content, $length + $start, $content_pos);
if ($temp === false) {
return false;
}
$content_pos += $temp['length'];
// all subtags should be octet strings
//if ($temp['type'] != self::TYPE_OCTET_STRING) {
// return false;
//}
if ($temp['type'] != self::TYPE_OCTET_STRING) {
return false;
}
$current['content'].= $temp['content'];
$length+= $temp['length'];
}
@ -396,12 +427,15 @@ class ASN1
break;
case self::TYPE_NULL:
// "The contents octets shall not contain any octets." -- paragraph 8.8.2
//if (strlen($content)) {
// return false;
//}
if ($constructed || strlen($content)) {
return false;
}
break;
case self::TYPE_SEQUENCE:
case self::TYPE_SET:
if (!$constructed) {
return false;
}
$offset = 0;
$current['content'] = array();
$content_len = strlen($content);
@ -413,30 +447,22 @@ class ASN1
break 2;
}
$temp = $this->_decode_ber($content, $start + $offset, $content_pos);
if ($temp === false) {
return false;
}
$content_pos += $temp['length'];
$current['content'][] = $temp;
$offset+= $temp['length'];
}
break;
case self::TYPE_OBJECT_IDENTIFIER:
$temp = ord($content[$content_pos++]);
$current['content'] = sprintf('%d.%d', floor($temp / 40), $temp % 40);
$valuen = 0;
// process septets
$content_len = strlen($content);
while ($content_pos < $content_len) {
$temp = ord($content[$content_pos++]);
$valuen <<= 7;
$valuen |= $temp & 0x7F;
if (~$temp & 0x80) {
$current['content'].= ".$valuen";
$valuen = 0;
if ($constructed) {
return false;
}
$current['content'] = $this->_decodeOID(substr($content, $content_pos));
if ($current['content'] === false) {
return false;
}
// the eighth bit of the last byte should not be 1
//if ($temp >> 7) {
// return false;
//}
break;
/* Each character string type shall be encoded as if it had been declared:
[UNIVERSAL x] IMPLICIT OCTET STRING
@ -466,12 +492,20 @@ class ASN1
case self::TYPE_UTF8_STRING:
// ????
case self::TYPE_BMP_STRING:
if ($constructed) {
return false;
}
$current['content'] = substr($content, $content_pos);
break;
case self::TYPE_UTC_TIME:
case self::TYPE_GENERALIZED_TIME:
if ($constructed) {
return false;
}
$current['content'] = $this->_decodeTime(substr($content, $content_pos), $tag);
break;
default:
return false;
}
$start+= $length;
@ -495,6 +529,10 @@ class ASN1
*/
function asn1map($decoded, $mapping, $special = array())
{
if (!is_array($decoded)) {
return false;
}
if (isset($mapping['explicit']) && is_array($decoded['content'])) {
$decoded = $decoded['content'][0];
}
@ -580,7 +618,7 @@ class ASN1
$childClass = $tempClass = self::CLASS_UNIVERSAL;
$constant = null;
if (isset($temp['constant'])) {
$tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC;
$tempClass = $temp['type'];
}
if (isset($child['class'])) {
$childClass = $child['class'];
@ -643,7 +681,7 @@ class ASN1
$temp = $decoded['content'][$i];
$tempClass = self::CLASS_UNIVERSAL;
if (isset($temp['constant'])) {
$tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC;
$tempClass = $temp['type'];
}
foreach ($mapping['children'] as $key => $child) {
@ -704,10 +742,17 @@ class ASN1
return isset($this->oids[$decoded['content']]) ? $this->oids[$decoded['content']] : $decoded['content'];
case self::TYPE_UTC_TIME:
case self::TYPE_GENERALIZED_TIME:
if (isset($mapping['implicit'])) {
// for explicitly tagged optional stuff
if (is_array($decoded['content'])) {
$decoded['content'] = $decoded['content'][0]['content'];
}
// for implicitly tagged optional stuff
// in theory, doing isset($mapping['implicit']) would work but malformed certs do exist
// in the wild that OpenSSL decodes without issue so we'll support them as well
if (!is_object($decoded['content'])) {
$decoded['content'] = $this->_decodeTime($decoded['content'], $decoded['type']);
}
return @date($this->format, $decoded['content']);
return $decoded['content'] ? $decoded['content']->format($this->format) : false;
case self::TYPE_BIT_STRING:
if (isset($mapping['mapping'])) {
$offset = ord($decoded['content'][0]);
@ -781,7 +826,7 @@ class ASN1
*
* @param string $source
* @param string $mapping
* @param int $idx
* @param array $special
* @return string
* @access public
*/
@ -797,6 +842,7 @@ class ASN1
* @param string $source
* @param string $mapping
* @param int $idx
* @param array $special
* @return string
* @access private
*/
@ -845,7 +891,7 @@ class ASN1
if ($mapping['type'] == self::TYPE_SET) {
sort($value);
}
$value = implode($value, '');
$value = implode('', $value);
break;
}
@ -956,7 +1002,11 @@ class ASN1
case self::TYPE_GENERALIZED_TIME:
$format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y';
$format.= 'mdHis';
$value = @gmdate($format, strtotime($source)) . 'Z';
// if $source does _not_ include timezone information within it then assume that the timezone is GMT
$date = new DateTime($source, new DateTimeZone('GMT'));
// if $source _does_ include timezone information within it then convert the time to GMT
$date->setTimezone(new DateTimeZone('GMT'));
$value = $date->format($format) . 'Z';
break;
case self::TYPE_BIT_STRING:
if (isset($mapping['mapping'])) {
@ -998,27 +1048,7 @@ class ASN1
$value = base64_decode($source);
break;
case self::TYPE_OBJECT_IDENTIFIER:
$oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids);
if ($oid === false) {
user_error('Invalid OID');
return false;
}
$value = '';
$parts = explode('.', $oid);
$value = chr(40 * $parts[0] + $parts[1]);
for ($i = 2; $i < count($parts); $i++) {
$temp = '';
if (!$parts[$i]) {
$temp = "\0";
} else {
while ($parts[$i]) {
$temp = chr(0x80 | ($parts[$i] & 0x7F)) . $temp;
$parts[$i] >>= 7;
}
$temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F);
}
$value.= $temp;
}
$value = $this->_encodeOID($source);
break;
case self::TYPE_ANY:
$loc = $this->location;
@ -1117,6 +1147,113 @@ class ASN1
return pack('Ca*', 0x80 | strlen($temp), $temp);
}
/**
* BER-decode the OID
*
* Called by _decode_ber()
*
* @access private
* @param string $content
* @return string
*/
function _decodeOID($content)
{
static $eighty;
if (!$eighty) {
$eighty = new BigInteger(80);
}
$oid = array();
$pos = 0;
$len = strlen($content);
if (ord($content[$len - 1]) & 0x80) {
return false;
}
$n = new BigInteger();
while ($pos < $len) {
$temp = ord($content[$pos++]);
$n = $n->bitwise_leftShift(7);
$n = $n->bitwise_or(new BigInteger($temp & 0x7F));
if (~$temp & 0x80) {
$oid[] = $n;
$n = new BigInteger();
}
}
$part1 = array_shift($oid);
$first = floor(ord($content[0]) / 40);
/*
"This packing of the first two object identifier components recognizes that only three values are allocated from the root
node, and at most 39 subsequent values from nodes reached by X = 0 and X = 1."
-- https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=22
*/
if ($first <= 2) { // ie. 0 <= ord($content[0]) < 120 (0x78)
array_unshift($oid, ord($content[0]) % 40);
array_unshift($oid, $first);
} else {
array_unshift($oid, $part1->subtract($eighty));
array_unshift($oid, 2);
}
return implode('.', $oid);
}
/**
* DER-encode the OID
*
* Called by _encode_der()
*
* @access private
* @param string $source
* @return string
*/
function _encodeOID($source)
{
static $mask, $zero, $forty;
if (!$mask) {
$mask = new BigInteger(0x7F);
$zero = new BigInteger();
$forty = new BigInteger(40);
}
$oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids);
if ($oid === false) {
user_error('Invalid OID');
return false;
}
$parts = explode('.', $oid);
$part1 = array_shift($parts);
$part2 = array_shift($parts);
$first = new BigInteger($part1);
$first = $first->multiply($forty);
$first = $first->add(new BigInteger($part2));
array_unshift($parts, $first->toString());
$value = '';
foreach ($parts as $part) {
if (!$part) {
$temp = "\0";
} else {
$temp = '';
$part = new BigInteger($part);
while (!$part->equals($zero)) {
$submask = $part->bitwise_and($mask);
$submask->setPrecision(8);
$temp = (chr(0x80) | $submask->toBytes()) . $temp;
$part = $part->bitwise_rightShift(7);
}
$temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F);
}
$value.= $temp;
}
return $value;
}
/**
* BER-decode the time
*
@ -1137,33 +1274,32 @@ class ASN1
http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2
http://www.obj-sys.com/asn1tutorial/node14.html */
$pattern = $tag == self::TYPE_UTC_TIME ?
'#(..)(..)(..)(..)(..)(..)(.*)#' :
'#(....)(..)(..)(..)(..)(..).*([Z+-].*)$#';
preg_match($pattern, $content, $matches);
list(, $year, $month, $day, $hour, $minute, $second, $timezone) = $matches;
$format = 'YmdHis';
if ($tag == self::TYPE_UTC_TIME) {
$year = $year >= 50 ? "19$year" : "20$year";
// https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=28 says "the seconds
// element shall always be present" but none-the-less I've seen X509 certs where it isn't and if the
// browsers parse it phpseclib ought to too
if (preg_match('#^(\d{10})(Z|[+-]\d{4})$#', $content, $matches)) {
$content = $matches[1] . '00' . $matches[2];
}
$prefix = substr($content, 0, 2) >= 50 ? '19' : '20';
$content = $prefix . $content;
} elseif (strpos($content, '.') !== false) {
$format.= '.u';
}
if ($timezone == 'Z') {
$mktime = 'gmmktime';
$timezone = 0;
} elseif (preg_match('#([+-])(\d\d)(\d\d)#', $timezone, $matches)) {
$mktime = 'gmmktime';
$timezone = 60 * $matches[3] + 3600 * $matches[2];
if ($matches[1] == '-') {
$timezone = -$timezone;
}
} else {
$mktime = 'mktime';
$timezone = 0;
if ($content[strlen($content) - 1] == 'Z') {
$content = substr($content, 0, -1) . '+0000';
}
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\File\ASN1\Element;
use phpseclib\Math\BigInteger;
use DateTime;
use DateTimeZone;
/**
* Pure-PHP X.509 Parser
@ -303,6 +305,22 @@ class X509
*/
var $challenge;
/**
* Recursion Limit
*
* @var int
* @access private
*/
static $recur_limit = 5;
/**
* URL fetch flag
*
* @var bool
* @access private
*/
static $disable_url_fetch = false;
/**
* Default Constructor.
*
@ -945,6 +963,13 @@ class X509
'children' => $AccessDescription
);
$this->SubjectInfoAccessSyntax = array(
'type' => ASN1::TYPE_SEQUENCE,
'min' => 1,
'max' => -1,
'children' => $AccessDescription
);
$this->SubjectAltName = $GeneralNames;
$this->PrivateKeyUsagePeriod = array(
@ -1583,7 +1608,7 @@ class X509
* Map extension values from octet string to extension-specific internal
* format.
*
* @param array ref $root
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
@ -1597,12 +1622,15 @@ class X509
$id = $extensions[$i]['extnId'];
$value = &$extensions[$i]['extnValue'];
$value = base64_decode($value);
$decoded = $asn1->decodeBER($value);
/* [extnValue] contains the DER encoding of an ASN.1 value
corresponding to the extension type identified by extnID */
$map = $this->_getMapping($id);
if (!is_bool($map)) {
$mapped = $asn1->asn1map($decoded[0], $map, array('iPAddress' => array($this, '_decodeIP')));
$decoder = $id == 'id-ce-nameConstraints' ?
array($this, '_decodeNameConstraintIP') :
array($this, '_decodeIP');
$decoded = $asn1->decodeBER($value);
$mapped = $asn1->asn1map($decoded[0], $map, array('iPAddress' => $decoder));
$value = $mapped === false ? $decoded[0] : $mapped;
if ($id == 'id-ce-certificatePolicies') {
@ -1633,7 +1661,7 @@ class X509
* Map extension values from extension-specific internal format to
* octet string.
*
* @param array ref $root
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
@ -1699,7 +1727,7 @@ class X509
* Map attribute values from ANY type to attribute-specific internal
* format.
*
* @param array ref $root
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
@ -1740,7 +1768,7 @@ class X509
* Map attribute values from attribute-specific internal format to
* ANY type.
*
* @param array ref $root
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
@ -1783,7 +1811,7 @@ class X509
* Map DN values from ANY type to DN-specific internal
* format.
*
* @param array ref $root
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
@ -1813,7 +1841,7 @@ class X509
* Map DN values from DN-specific internal format to
* ANY type.
*
* @param array ref $root
* @param array $root (by reference)
* @param string $path
* @param object $asn1
* @access private
@ -1871,6 +1899,8 @@ class X509
return $this->ExtKeyUsageSyntax;
case 'id-pe-authorityInfoAccess':
return $this->AuthorityInfoAccessSyntax;
case 'id-pe-subjectInfoAccess':
return $this->SubjectInfoAccessSyntax;
case 'id-ce-subjectAltName':
return $this->SubjectAltName;
case 'id-ce-subjectDirectoryAttributes':
@ -1907,6 +1937,12 @@ class X509
// "SET Secure Electronic Transaction Specification"
// http://www.maithean.com/docs/set_bk3.pdf
case '2.23.42.7.0': // id-set-hashedRootKey
// "Certificate Transparency"
// https://tools.ietf.org/html/rfc6962
case '1.3.6.1.4.1.11129.2.4.2':
// "Qualified Certificate statements"
// https://tools.ietf.org/html/rfc3739#section-3.2.6
case '1.3.6.1.5.5.7.1.3':
return true;
// CSR attributes
@ -2027,7 +2063,8 @@ class X509
}
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);
switch ($key) {
case 'dNSName':
@ -2053,6 +2090,7 @@ class X509
}
}
}
}
return false;
}
@ -2069,7 +2107,7 @@ class X509
*
* If $date isn't defined it is assumed to be the current date.
*
* @param int $date optional
* @param \DateTime|string $date optional
* @access public
*/
function validateDate($date = null)
@ -2079,7 +2117,7 @@ class X509
}
if (!isset($date)) {
$date = time();
$date = new DateTime(null, new DateTimeZone(@date_default_timezone_get()));
}
$notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore'];
@ -2088,15 +2126,137 @@ class X509
$notAfter = $this->currentCert['tbsCertificate']['validity']['notAfter'];
$notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime'];
if (is_string($date)) {
$date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
}
$notBefore = new DateTime($notBefore, new DateTimeZone(@date_default_timezone_get()));
$notAfter = new DateTime($notAfter, new DateTimeZone(@date_default_timezone_get()));
switch (true) {
case $date < @strtotime($notBefore):
case $date > @strtotime($notAfter):
case $date < $notBefore:
case $date > $notAfter:
return false;
}
return true;
}
/**
* Fetches a URL
*
* @param string $url
* @access private
* @return bool|string
*/
static function _fetchURL($url)
{
if (self::$disable_url_fetch) {
return false;
}
$parts = parse_url($url);
$data = '';
switch ($parts['scheme']) {
case 'http':
$fsock = @fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80);
if (!$fsock) {
return false;
}
fputs($fsock, "GET $parts[path] HTTP/1.0\r\n");
fputs($fsock, "Host: $parts[host]\r\n\r\n");
$line = fgets($fsock, 1024);
if (strlen($line) < 3) {
return false;
}
preg_match('#HTTP/1.\d (\d{3})#', $line, $temp);
if ($temp[1] != '200') {
return false;
}
// skip the rest of the headers in the http response
while (!feof($fsock) && fgets($fsock, 1024) != "\r\n") {
}
while (!feof($fsock)) {
$temp = fread($fsock, 1024);
if ($temp === false) {
return false;
}
$data.= $temp;
}
break;
//case 'ftp':
//case 'ldap':
//default:
}
return $data;
}
/**
* Validates an intermediate cert as identified via authority info access extension
*
* See https://tools.ietf.org/html/rfc4325 for more info
*
* @param bool $caonly
* @param int $count
* @access private
* @return bool
*/
function _testForIntermediate($caonly, $count)
{
$opts = $this->getExtension('id-pe-authorityInfoAccess');
if (!is_array($opts)) {
return false;
}
foreach ($opts as $opt) {
if ($opt['accessMethod'] == 'id-ad-caIssuers') {
// accessLocation is a GeneralName. GeneralName fields support stuff like email addresses, IP addresses, LDAP,
// etc, but we're only supporting URI's. URI's and LDAP are the only thing https://tools.ietf.org/html/rfc4325
// discusses
if (isset($opt['accessLocation']['uniformResourceIdentifier'])) {
$url = $opt['accessLocation']['uniformResourceIdentifier'];
break;
}
}
}
if (!isset($url)) {
return false;
}
$cert = static::_fetchURL($url);
if (!is_string($cert)) {
return false;
}
$parent = new static();
$parent->CAs = $this->CAs;
/*
"Conforming applications that support HTTP or FTP for accessing
certificates MUST be able to accept .cer files and SHOULD be able
to accept .p7c files." -- https://tools.ietf.org/html/rfc4325
A .p7c file is 'a "certs-only" CMS message as specified in RFC 2797"
These are currently unsupported
*/
if (!is_array($parent->loadX509($cert))) {
return false;
}
if (!$parent->_validateSignatureCountable($caonly, ++$count)) {
return false;
}
$this->CAs[] = $parent->currentCert;
//$this->loadCA($cert);
return true;
}
/**
* Validate a signature
*
@ -2113,11 +2273,30 @@ class X509
* @return mixed
*/
function validateSignature($caonly = true)
{
return $this->_validateSignatureCountable($caonly, 0);
}
/**
* Validate a signature
*
* Performs said validation whilst keeping track of how many times validation method is called
*
* @param bool $caonly
* @param int $count
* @access private
* @return mixed
*/
function _validateSignatureCountable($caonly, $count)
{
if (!is_array($this->currentCert) || !isset($this->signatureSubject)) {
return null;
}
if ($count == self::$recur_limit) {
return false;
}
/* TODO:
"emailAddress attribute values are not case-sensitive (e.g., "subscriber@example.com" is the same as "SUBSCRIBER@EXAMPLE.COM")."
-- http://tools.ietf.org/html/rfc5280#section-4.1.2.6
@ -2134,7 +2313,8 @@ class X509
$subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier');
switch (true) {
case !is_array($authorityKey):
case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
case !$subjectKeyID:
case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
$signingCert = $this->currentCert; // working cert
}
}
@ -2151,17 +2331,21 @@ class X509
$subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
switch (true) {
case !is_array($authorityKey):
case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
case !$subjectKeyID:
case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) {
break 2; // serial mismatch - check other ca
}
$signingCert = $ca; // working cert
break 3;
}
}
}
if (count($this->CAs) == $i && $caonly) {
return false;
return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly);
}
} elseif (!isset($signingCert) || $caonly) {
return false;
return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly);
}
return $this->_validateSignature(
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
@ -2197,7 +2381,11 @@ class X509
$subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
switch (true) {
case !is_array($authorityKey):
case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
case !$subjectKeyID:
case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) {
break 2; // serial mismatch - check other ca
}
$signingCert = $ca; // working cert
break 3;
}
@ -2264,6 +2452,41 @@ class X509
return true;
}
/**
* Sets the recursion limit
*
* When validating a signature it may be necessary to download intermediate certs from URI's.
* An intermediate cert that linked to itself would result in an infinite loop so to prevent
* that we set a recursion limit. A negative number means that there is no recursion limit.
*
* @param int $count
* @access public
*/
static function setRecurLimit($count)
{
self::$recur_limit = $count;
}
/**
* Prevents URIs from being automatically retrieved
*
* @access public
*/
static function disableURLFetch()
{
self::$disable_url_fetch = true;
}
/**
* Allows URIs to be automatically retrieved
*
* @access public
*/
static function enableURLFetch()
{
self::$disable_url_fetch = false;
}
/**
* Reformat public keys
*
@ -2304,18 +2527,38 @@ class X509
return inet_ntop(base64_decode($ip));
}
/**
* Decodes an IP address in a name constraints extension
*
* Takes in a base64 encoded "blob" and returns a human readable IP address / mask
*
* @param string $ip
* @access private
* @return array
*/
function _decodeNameConstraintIP($ip)
{
$ip = base64_decode($ip);
$size = strlen($ip) >> 1;
$mask = substr($ip, $size);
$ip = substr($ip, 0, $size);
return array(inet_ntop($ip), inet_ntop($mask));
}
/**
* Encodes an IP address
*
* Takes a human readable IP address into a base64-encoded "blob"
*
* @param string $ip
* @param string|array $ip
* @access private
* @return string
*/
function _encodeIP($ip)
{
return base64_encode(inet_pton($ip));
return is_string($ip) ?
base64_encode(inet_pton($ip)) :
base64_encode(inet_pton($ip[0]) . inet_pton($ip[1]));
}
/**
@ -2469,6 +2712,10 @@ class X509
}
$dn = array_values($dn);
// fix for https://bugs.php.net/75433 affecting PHP 7.2
if (!isset($dn[0])) {
$dn = array_splice($dn, 0, 0);
}
}
/**
@ -2712,12 +2959,14 @@ class X509
$value = array_pop($value); // Always strip data type.
}
} elseif (is_object($value) && $value instanceof Element) {
$callback = create_function('$x', 'return "\x" . bin2hex($x[0]);');
$callback = function ($x) {
return "\x" . bin2hex($x[0]);
};
$value = strtoupper(preg_replace_callback('#[^\x20-\x7E]#', $callback, $value->element));
}
$output.= $desc . '=' . $value;
$result[$desc] = isset($result[$desc]) ?
array_merge((array) $dn[$prop], array($value)) :
array_merge((array) $result[$desc], array($value)) :
$value;
$start = false;
}
@ -2946,7 +3195,8 @@ class X509
/**
* Load a Certificate Signing Request
*
* @param string $csr
* @param string|array $csr
* @param int $mode
* @access public
* @return mixed
*/
@ -3083,7 +3333,7 @@ class X509
*
* https://developer.mozilla.org/en-US/docs/HTML/Element/keygen
*
* @param string $csr
* @param string|array $spkac
* @access public
* @return mixed
*/
@ -3154,7 +3404,7 @@ class X509
/**
* Save a SPKAC CSR request
*
* @param array $csr
* @param string|array $spkac
* @param int $format optional
* @access public
* @return string
@ -3198,6 +3448,7 @@ class X509
* Load a Certificate Revocation List
*
* @param string $crl
* @param int $mode
* @access public
* @return mixed
*/
@ -3335,7 +3586,11 @@ class X509
*/
function _timeField($date)
{
$year = @gmdate("Y", @strtotime($date)); // the same way ASN1.php parses this
if ($date instanceof Element) {
return $date;
}
$dateObj = new DateTime($date, new DateTimeZone('GMT'));
$year = $dateObj->format('Y'); // the same way ASN1.php parses this
if ($year < 2050) {
return array('utcTime' => $date);
} else {
@ -3400,8 +3655,12 @@ class X509
return false;
}
$startDate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O');
$endDate = !empty($this->endDate) ? $this->endDate : @date('D, d M Y H:i:s O', strtotime('+1 year'));
$startDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
$startDate = !empty($this->startDate) ? $this->startDate : $startDate->format('D, d M Y H:i:s O');
$endDate = new DateTime('+1 year', new DateTimeZone(@date_default_timezone_get()));
$endDate = !empty($this->endDate) ? $this->endDate : $endDate->format('D, d M Y H:i:s O');
/* "The serial number MUST be a positive integer"
"Conforming CAs MUST NOT use serialNumber values longer than 20 octets."
-- https://tools.ietf.org/html/rfc5280#section-4.1.2.2
@ -3417,7 +3676,7 @@ class X509
'tbsCertificate' =>
array(
'version' => 'v3',
'serialNumber' => $serialNumber, // $this->setserialNumber()
'serialNumber' => $serialNumber, // $this->setSerialNumber()
'signature' => array('algorithm' => $signatureAlgorithm),
'issuer' => false, // this is going to be overwritten later
'validity' => array(
@ -3463,8 +3722,8 @@ class X509
$altName = array();
if (isset($subject->domains) && count($subject->domains) > 1) {
$altName = array_map(array('X509', '_dnsName'), $subject->domains);
if (isset($subject->domains) && count($subject->domains)) {
$altName = array_map(array('\phpseclib\File\X509', '_dnsName'), $subject->domains);
}
if (isset($subject->ipAddresses) && count($subject->ipAddresses)) {
@ -3669,7 +3928,9 @@ class X509
$currentCert = isset($this->currentCert) ? $this->currentCert : null;
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null;
$thisUpdate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O');
$thisUpdate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
$thisUpdate = !empty($this->startDate) ? $this->startDate : $thisUpdate->format('D, d M Y H:i:s O');
if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) {
$this->currentCert = $crl->currentCert;
@ -3784,8 +4045,7 @@ class X509
/**
* X.509 certificate signing helper function.
*
* @param object $key
* @param \phpseclib\File\X509 $subject
* @param \phpseclib\File\X509 $key
* @param string $signatureAlgorithm
* @access public
* @return mixed
@ -3820,7 +4080,11 @@ class X509
*/
function setStartDate($date)
{
$this->startDate = @date('D, d M Y H:i:s O', @strtotime($date));
if (!is_object($date) || !is_a($date, 'DateTime')) {
$date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
}
$this->startDate = $date->format('D, d M Y H:i:s O');
}
/**
@ -3844,7 +4108,11 @@ class X509
$temp = chr(ASN1::TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp;
$this->endDate = new Element($temp);
} else {
$this->endDate = @date('D, d M Y H:i:s O', @strtotime($date));
if (!is_object($date) || !is_a($date, 'DateTime')) {
$date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
}
$this->endDate = $date->format('D, d M Y H:i:s O');
}
}
@ -3852,7 +4120,7 @@ class X509
* Set Serial Number
*
* @param string $serial
* @param $base optional
* @param int $base optional
* @access public
*/
function setSerialNumber($serial, $base = -256)
@ -4054,6 +4322,10 @@ class X509
}
$extensions = array_values($extensions);
// fix for https://bugs.php.net/75433 affecting PHP 7.2
if (!isset($extensions[0])) {
$extensions = array_splice($extensions, 0, 0);
}
return $result;
}
@ -4511,7 +4783,6 @@ class X509
* Set the IP Addresses's which the cert is to be valid for
*
* @access public
* @param string $ipAddress optional
*/
function setIPAddress()
{
@ -4574,8 +4845,9 @@ class X509
}
$i = count($rclist);
$revocationDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
$rclist[] = array('userCertificate' => $serial,
'revocationDate' => $this->_timeField(@date('D, d M Y H:i:s O')));
'revocationDate' => $this->_timeField($revocationDate->format('D, d M Y H:i:s O')));
return $i;
}
@ -4782,11 +5054,16 @@ class X509
* subject=/O=organization/OU=org unit/CN=common name
* issuer=/O=organization/CN=common name
*/
if (strlen($str) > ini_get('pcre.backtrack_limit')) {
$temp = $str;
} else {
$temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
// remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
$temp = preg_replace('#-+[^-]+-+#', '', $temp);
$temp = preg_replace('#-+END.*[\r\n ]*.*#ms', '', $temp, 1);
}
// remove new lines
$temp = str_replace(array("\r", "\n", ' '), '', $temp);
// remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
$temp = preg_replace('#^-+[^-]+-+|-+[^-]+-+$#', '', $temp);
$temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
return $temp != false ? $temp : $str;
}

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

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

View file

@ -32,6 +32,17 @@ use phpseclib\System\SSH\Agent;
*/
class Identity
{
/**@+
* Signature Flags
*
* See https://tools.ietf.org/html/draft-miller-ssh-agent-00#section-5.3
*
* @access private
*/
const SSH_AGENT_RSA2_256 = 2;
const SSH_AGENT_RSA2_512 = 4;
/**#@-*/
/**
* Key Object
*
@ -59,6 +70,16 @@ class Identity
*/
var $fsock;
/**
* Signature flags
*
* @var int
* @access private
* @see self::sign()
* @see self::setHash()
*/
var $flags = 0;
/**
* Default Constructor.
*
@ -126,6 +147,31 @@ class Identity
{
}
/**
* Set Hash
*
* ssh-agent doesn't support using hashes for RSA other than SHA1
*
* @param string $hash
* @access public
*/
function setHash($hash)
{
$this->flags = 0;
switch ($hash) {
case 'sha1':
break;
case 'sha256':
$this->flags = self::SSH_AGENT_RSA2_256;
break;
case 'sha512':
$this->flags = self::SSH_AGENT_RSA2_512;
break;
default:
user_error('The only supported hashes for RSA are sha1, sha256 and sha512');
}
}
/**
* Create a signature
*
@ -138,21 +184,58 @@ class Identity
function sign($message)
{
// the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE
$packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, 0);
$packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, $this->flags);
$packet = pack('Na*', strlen($packet), $packet);
if (strlen($packet) != fputs($this->fsock, $packet)) {
user_error('Connection closed during signing');
return false;
}
$length = current(unpack('N', fread($this->fsock, 4)));
$temp = fread($this->fsock, 4);
if (strlen($temp) != 4) {
user_error('Connection closed during signing');
return false;
}
$length = current(unpack('N', $temp));
$type = ord(fread($this->fsock, 1));
if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) {
user_error('Unable to retrieve signature');
return false;
}
$signature_blob = fread($this->fsock, $length - 1);
// the only other signature format defined - ssh-dss - is the same length as ssh-rsa
// the + 12 is for the other various SSH added length fields
return substr($signature_blob, strlen('ssh-rsa') + 12);
if (strlen($signature_blob) != $length - 1) {
user_error('Connection closed during signing');
return false;
}
$length = current(unpack('N', $this->_string_shift($signature_blob, 4)));
if ($length != strlen($signature_blob)) {
user_error('Malformed signature blob');
}
$length = current(unpack('N', $this->_string_shift($signature_blob, 4)));
if ($length > strlen($signature_blob) + 4) {
user_error('Malformed signature blob');
}
$type = $this->_string_shift($signature_blob, $length);
$this->_string_shift($signature_blob, 4);
return $signature_blob;
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @return string
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
}

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

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
============================
This is a pure-PHP implementation of the OpenPGP Message Format (RFC 4880).
* <http://github.com/bendiken/openpgp-php>
* <https://github.com/singpolyma/openpgp-php>
### About OpenPGP
About OpenPGP
-------------
OpenPGP is the most widely-used e-mail encryption standard in the world. It
is defined by the OpenPGP Working Group of the Internet Engineering Task
@ -13,8 +16,8 @@ Force (IETF) Proposed Standard RFC 4880. The OpenPGP standard was originally
derived from PGP (Pretty Good Privacy), first created by Phil Zimmermann in
1991.
* <http://tools.ietf.org/html/rfc4880>
* <http://www.openpgp.org/>
* <https://tools.ietf.org/html/rfc4880>
* <https://www.openpgp.org/>
Features
--------
@ -22,15 +25,26 @@ Features
* Encodes and decodes ASCII-armored OpenPGP messages.
* Parses OpenPGP messages into their constituent packets.
* Supports both old-format (PGP 2.6.x) and new-format (RFC 4880) packets.
* Helper class for verifying, signing, encrypting, and decrypting messages using Crypt_RSA from <http://phpseclib.sourceforge.net>
* Helper class for encrypting and decrypting messages and keys using Crypt_AES and Crypt_TripleDES from <http://phpseclib.sourceforge.net>
* Helper class for verifying, signing, encrypting, and decrypting messages <http://phpseclib.sourceforge.net>
* Helper class for encrypting and decrypting messages and keys using <http://phpseclib.sourceforge.net>
* openssl or mcrypt required for CAST5 encryption and decryption
Bugs, Feature Requests, Patches
-------------------------------
This project is primarily maintained by a single volunteer with many other
things vying for their attention, please be patient.
Bugs, feature request, pull requests, patches, and general discussion may
be submitted publicly via email to: dev@singpolyma.net
Github users may alternately submit on the web there.
Users
-----
OpenPGP.php is currently being used in the following projects:
* <https://drupal.org/project/openpgp>
* <https://wordpress.org/plugins/wp-pgp-encrypted-emails/>
Download
@ -38,21 +52,21 @@ Download
To get a local working copy of the development repository, do:
% git clone git://github.com/bendiken/openpgp-php.git
git clone https://github.com/singpolyma/openpgp-php.git
Alternatively, you can download the latest development version as a tarball
as follows:
% wget http://github.com/bendiken/openpgp-php/tarball/master
wget https://github.com/singpolyma/openpgp-php/tarball/master
Authors
-------
* [Arto Bendiken](mailto:arto.bendiken@gmail.com) - <http://ar.to/>
* [Stephen Paul Weber](mailto:singpolyma@singpolyma.net) - <http://singpolyma.net/>
* [Arto Bendiken](mailto:arto.bendiken@gmail.com) (Original author) - <http://ar.to/>
* [Stephen Paul Weber](mailto:singpolyma@singpolyma.net) (Maintainer) - <https://singpolyma.net/>
License
-------
OpenPGP.php is free and unencumbered public domain software. For more
information, see <http://unlicense.org/> or the accompanying UNLICENSE file.
information, see <https://unlicense.org/> or the accompanying UNLICENSE file.

View file

@ -13,10 +13,14 @@
}
],
"require": {
"phpseclib/phpseclib": ">=2.0.0 <=2.0.4"
"php": "^5.6 || ^7.0 || ^8.0",
"phpseclib/phpseclib": "^2.0 !=2.0.8"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
"phpunit/phpunit": "^9.0"
},
"suggest": {
"ext-mcrypt": "required if you use encryption cast5"
},
"autoload": {
"classmap": ["lib/"]

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
@include_once dirname(__FILE__).'/../vendor/autoload.php';
require_once dirname(__FILE__).'/../lib/openpgp.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.
echo preg_replace("/^-/", "- -", $packets[0]->data)."\n";
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
// This will fail if the algo on key or message is not 3DES or AES
@include_once dirname(__FILE__).'/../vendor/autoload.php';
require_once dirname(__FILE__).'/../lib/openpgp.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_rsa.php';
require_once dirname(__FILE__).'/../lib/openpgp_crypt_symmetric.php';

View file

@ -1,5 +1,6 @@
<?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';

View file

@ -1,5 +1,6 @@
<?php
@include_once dirname(__FILE__).'/../vendor/autoload.php';
require_once dirname(__FILE__).'/../lib/openpgp.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
@include_once dirname(__FILE__).'/../vendor/autoload.php';
require_once dirname(__FILE__).'/../lib/openpgp.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 */
echo $m->to_bytes();
?>

View file

@ -1,5 +1,6 @@
<?php
@include_once dirname(__FILE__).'/../vendor/autoload.php';
require_once dirname(__FILE__).'/../lib/openpgp.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 */
var_dump($verify->verify($m));
?>

View file

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

View file

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

View file

@ -1,15 +1,15 @@
<?php
use phpseclib\Crypt\TripleDES as Crypt_TripleDES;
use phpseclib\Crypt\AES as Crypt_AES;
use phpseclib\Crypt\Blowfish as Crypt_Blowfish;
use phpseclib\Crypt\TripleDES as Crypt_TripleDES;
use phpseclib\Crypt\Twofish as Crypt_Twofish;
use phpseclib\Crypt\Random;
define('CRYPT_DES_MODE_CFB', Crypt_TripleDES::MODE_CFB);
define('CRYPT_AES_MODE_CFB', Crypt_AES::MODE_CFB);
require_once dirname(__FILE__).'/openpgp.php';
@include_once dirname(__FILE__).'/openpgp_crypt_rsa.php';
@include_once dirname(__FILE__).'/openpgp_mcrypt_wrapper.php';
@include_once dirname(__FILE__).'/openpgp_openssl_wrapper.php';
class OpenPGP_Crypt_Symmetric {
public static function encrypt($passphrases_and_keys, $message, $symmetric_algorithm=9) {
@ -40,7 +40,7 @@ class OpenPGP_Crypt_Symmetric {
$esk = pack('n', OpenPGP::bitlength($esk)) . $esk;
array_unshift($encrypted, new OpenPGP_AsymmetricSessionKeyPacket($pass->algorithm, $pass->fingerprint(), $esk));
} else if(is_string($pass)) {
$s2k = new OpenPGP_S2K(Random::string(10));
$s2k = new OpenPGP_S2K(Random::string(8));
$cipher->setKey($s2k->make_key($pass, $key_bytes));
$esk = $cipher->encrypt(chr($symmetric_algorithm) . $key);
array_unshift($encrypted, new OpenPGP_SymmetricSessionKeyPacket($s2k, $esk, $symmetric_algorithm));
@ -62,7 +62,7 @@ class OpenPGP_Crypt_Symmetric {
$padAmount = $key_block_bytes - (strlen($p->encrypted_data) % $key_block_bytes);
$data = substr($cipher->decrypt($p->encrypted_data . str_repeat("\0", $padAmount)), 0, strlen($p->encrypted_data));
$decrypted = self::decryptPacket($epacket, ord($data{0}), substr($data, 1));
$decrypted = self::decryptPacket($epacket, ord($data[0]), substr($data, 1));
} else {
list($cipher, $key_bytes, $key_block_bytes) = self::getCipher($p->symmetric_algorithm);
$decrypted = self::decryptPacket($epacket, $p->symmetric_algorithm, $p->s2k->make_key($pass, $key_bytes));
@ -75,6 +75,31 @@ class OpenPGP_Crypt_Symmetric {
return NULL; /* If we get here, we failed */
}
public static function encryptSecretKey($pass, $packet, $symmetric_algorithm=9) {
$packet = clone $packet; // Do not mutate original
$packet->s2k_useage = 254;
$packet->symmetric_algorithm = $symmetric_algorithm;
list($cipher, $key_bytes, $key_block_bytes) = self::getCipher($packet->symmetric_algorithm);
if(!$cipher) throw new Exception("Unsupported cipher");
$material = '';
foreach(OpenPGP_SecretKeyPacket::$secret_key_fields[$packet->algorithm] as $field) {
$f = $packet->key[$field];
$material .= pack('n', OpenPGP::bitlength($f)) . $f;
unset($packet->key[$field]);
}
$material .= hash('sha1', $material, true);
$iv = Random::string($key_block_bytes);
if(!$packet->s2k) $packet->s2k = new OpenPGP_S2K(Random::string(8));
$cipher->setKey($packet->s2k->make_key($pass, $key_bytes));
$cipher->setIV($iv);
$packet->encrypted_data = $iv . $cipher->encrypt($material);
return $packet;
}
public static function decryptSecretKey($pass, $packet) {
$packet = clone $packet; // Do not mutate orinigal
@ -97,6 +122,7 @@ class OpenPGP_Crypt_Symmetric {
if($chk != $mkChk) return NULL;
}
$packet->s2k = NULL;
$packet->s2k_useage = 0;
$packet->symmetric_algorithm = 0;
$packet->encrypted_data = NULL;
@ -146,30 +172,46 @@ class OpenPGP_Crypt_Symmetric {
public static function getCipher($algo) {
$cipher = NULL;
switch($algo) {
case NULL:
case 0:
throw new Exception("Data is already unencrypted");
case 2:
$cipher = new Crypt_TripleDES(CRYPT_DES_MODE_CFB);
$cipher = new Crypt_TripleDES(Crypt_TripleDES::MODE_CFB);
$key_bytes = 24;
$key_block_bytes = 8;
break;
case 3:
if(defined('MCRYPT_CAST_128')) {
if(class_exists('OpenSSLWrapper')) {
$cipher = new OpenSSLWrapper("CAST5-CFB");
} else if(defined('MCRYPT_CAST_128')) {
$cipher = new MCryptWrapper(MCRYPT_CAST_128);
} else {
throw new Exception("Unsupported cipher: you must have mcrypt installed to use CAST5");
}
break;
case 4:
$cipher = new Crypt_Blowfish(Crypt_Blowfish::MODE_CFB);
$key_bytes = 16;
$key_block_bytes = 8;
break;
case 7:
$cipher = new Crypt_AES(CRYPT_AES_MODE_CFB);
$cipher = new Crypt_AES(Crypt_AES::MODE_CFB);
$cipher->setKeyLength(128);
break;
case 8:
$cipher = new Crypt_AES(CRYPT_AES_MODE_CFB);
$cipher = new Crypt_AES(Crypt_AES::MODE_CFB);
$cipher->setKeyLength(192);
break;
case 9:
$cipher = new Crypt_AES(CRYPT_AES_MODE_CFB);
$cipher = new Crypt_AES(Crypt_AES::MODE_CFB);
$cipher->setKeyLength(256);
break;
case 10:
$cipher = new Crypt_Twofish(Crypt_Twofish::MODE_CFB);
if(method_exists($cipher, 'setKeyLength')) {
$cipher->setKeyLength(256);
} else {
$cipher = NULL;
}
break;
}
if(!$cipher) return array(NULL, NULL, NULL); // Unsupported cipher
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) {
$mkChk = 0;
for($i = 0; $i < strlen($s); $i++) {
$mkChk = ($mkChk + ord($s{$i})) % 65536;
$mkChk = ($mkChk + ord($s[$i])) % 65536;
}
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>
</testsuite>
<testsuite name="Signature">
<file>tests/suite.php</file>
</testsuite>
<testsuite name="MessageVerification">
<file>tests/phpseclib_suite.php</file>
</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
use PHPUnit\Framework\TestCase;
/* 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_symmetric.php';
class MessageVerification extends PHPUnit_Framework_TestCase {
class MessageVerification extends TestCase {
public function oneMessageRSA($pkey, $path) {
$pkeyM = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $pkey));
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
@ -51,7 +52,7 @@ class MessageVerification extends PHPUnit_Framework_TestCase {
}
class KeyVerification extends PHPUnit_Framework_TestCase {
class KeyVerification extends TestCase {
public function oneKeyRSA($path) {
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
$verify = new OpenPGP_Crypt_RSA($m);
@ -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) {
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
$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() {
$this->oneSymmetric("hello", "PGP\n", "symmetric-3des.gpg");
}
public function testDecryptCAST5() { // Requires mcrypt
public function testDecryptCAST5() { // Requires mcrypt or openssl
$this->oneSymmetric("hello", "PGP\n", "symmetric-cast5.gpg");
}
public function testDecryptBlowfish() {
$this->oneSymmetric("hello", "PGP\n", "symmetric-blowfish.gpg");
}
public function testDecryptAES() {
$this->oneSymmetric("hello", "PGP\n", "symmetric-aes.gpg");
}
public function testDecryptTwofish() {
if(OpenPGP_Crypt_Symmetric::getCipher(10)[0]) {
$this->oneSymmetric("hello", "PGP\n", "symmetric-twofish.gpg");
}
}
public function testDecryptSessionKey() {
$this->oneSymmetric("hello", "PGP\n", "symmetric-with-session-key.gpg");
}
@ -109,25 +120,89 @@ class Decryption extends PHPUnit_Framework_TestCase {
}
}
public function testDecryptRoundtrip() {
$m = new OpenPGP_Message(array(new OpenPGP_LiteralDataPacket("hello\n")));
$key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg'));
$em = OpenPGP_Crypt_Symmetric::encrypt($key, $m);
foreach($key as $packet) {
if(!($packet instanceof OpenPGP_SecretKeyPacket)) continue;
$decryptor = new OpenPGP_Crypt_RSA($packet);
$m2 = $decryptor->decrypt($em);
foreach($m2 as $p) {
if($p instanceof OpenPGP_LiteralDataPacket) {
$this->assertEquals($p->data, "hello\n");
}
}
}
}
public function testDecryptSecretKey() {
$key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/encryptedSecretKey.gpg'));
$skey = OpenPGP_Crypt_Symmetric::decryptSecretKey("hello", $key[0]);
$this->assertSame(!!$skey, true);
}
public function testEncryptSecretKeyRoundtrip() {
$key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg'));
$enkey = OpenPGP_Crypt_Symmetric::encryptSecretKey("password", $key[0]);
$skey = OpenPGP_Crypt_Symmetric::decryptSecretKey("password", $enkey);
$this->assertEquals($key[0], $skey);
}
class Encryption extends PHPUnit_Framework_TestCase {
public function testEncryptSymmetric() {
public function testAlreadyDecryptedSecretKey() {
$this->expectException(Exception::class);
$this->expectExceptionMessage("Data is already unencrypted");
$key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg'));
OpenPGP_Crypt_Symmetric::decryptSecretKey("hello", $key[0]);
}
}
class Encryption extends TestCase {
public function oneSymmetric($algorithm) {
$data = new OpenPGP_LiteralDataPacket('This is text.', array('format' => 'u', 'filename' => 'stuff.txt'));
$encrypted = OpenPGP_Crypt_Symmetric::encrypt('secret', new OpenPGP_Message(array($data)));
$encrypted = OpenPGP_Crypt_Symmetric::encrypt('secret', new OpenPGP_Message(array($data)), $algorithm);
$encrypted = OpenPGP_Message::parse($encrypted->to_bytes());
$decrypted = OpenPGP_Crypt_Symmetric::decryptSymmetric('secret', $encrypted);
$this->assertEquals($decrypted[0]->data, 'This is text.');
}
public function testEncryptSymmetric3DES() {
$this->oneSymmetric(2);
}
public function testEncryptSymmetricCAST5() {
$this->oneSymmetric(3);
}
public function testEncryptSymmetricBlowfish() {
$this->oneSymmetric(4);
}
public function testEncryptSymmetricAES128() {
$this->oneSymmetric(7);
}
public function testEncryptSymmetricAES192() {
$this->oneSymmetric(8);
}
public function testEncryptSymmetricAES256() {
$this->oneSymmetric(9);
}
public function testEncryptSymmetricTwofish() {
if(OpenPGP_Crypt_Symmetric::getCipher(10)[0]) {
$this->oneSymmetric(10);
}
}
public function testEncryptAsymmetric() {
$key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg'));
$data = new OpenPGP_LiteralDataPacket('This is text.', array('format' => 'u', 'filename' => 'stuff.txt'));
$encrypted = OpenPGP_Crypt_Symmetric::encrypt($key, new OpenPGP_Message(array($data)));
$encrypted = OpenPGP_Message::parse($encrypted->to_bytes());
$decryptor = new OpenPGP_Crypt_RSA($key);
$decrypted = $decryptor->decrypt($encrypted);
$this->assertEquals($decrypted[0]->data, 'This is text.');

View file

@ -1,8 +1,9 @@
<?php
use PHPUnit\Framework\TestCase;
require_once dirname(__FILE__).'/../lib/openpgp.php';
class Serialization extends PHPUnit_Framework_TestCase {
class Serialization extends TestCase {
public function oneSerialization($path) {
$in = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
$mid = $in->to_bytes();
@ -14,7 +15,6 @@ class Serialization extends PHPUnit_Framework_TestCase {
$this->oneSerialization("000001-006.public_key");
}
public function test000002013user_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) {
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
$this->assertEquals($m[0]->fingerprint(), $kf);
@ -397,4 +397,31 @@ class Fingerprint extends PHPUnit_Framework_TestCase {
public function test000035006public_key() {
$this->oneFingerprint("000035-006.public_key", "CB7933459F59C70DF1C3FBEEDEDC3ECF689AF56D");
}
public function test000080006public_key() {
$this->oneFingerprint("000080-006.public_key", "AEDA0C4468AE265E8B7CCA1C3047D4A7B15467AB");
}
public function test000082006public_key() {
$this->oneFingerprint("000082-006.public_key", "589D7E6884A9235BBE821D35BD7BA7BC5547FD09");
}
}
class Signature extends TestCase {
public function oneIssuer($path, $kf) {
$m = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/' . $path));
$this->assertEquals($m[0]->issuer(), $kf);
}
public function test000079002sig() {
$this->oneIssuer("000079-002.sig", "C25059FA8730BC38");
}
public function test000081002sig() {
$this->oneIssuer("000081-002.sig", "6B799484725130FE");
}
public function test000083002sig() {
$this->oneIssuer("000083-002.sig", "BD7BA7BC5547FD09");
}
}