Bump phpseclib

This commit is contained in:
Michal Sedlák
2026-10-14 21:37:51 +02:00
parent 6f6e2e6c92
commit 51b25f6d96
77 changed files with 3098 additions and 1099 deletions
+10 -10
View File
@@ -125,16 +125,16 @@
},
{
"name": "phpseclib/phpseclib",
"version": "3.0.43",
"version": "3.0.57",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02"
"reference": "d17e0ddaeaf6f22f7e007cbb437d78792fe2a0e4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/709ec107af3cb2f385b9617be72af8cf62441d02",
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/d17e0ddaeaf6f22f7e007cbb437d78792fe2a0e4",
"reference": "d17e0ddaeaf6f22f7e007cbb437d78792fe2a0e4",
"shasum": ""
},
"require": {
@@ -215,7 +215,7 @@
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.43"
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.57"
},
"funding": [
{
@@ -231,16 +231,16 @@
"type": "tidelift"
}
],
"time": "2024-12-14T21:12:59+00:00"
"time": "2026-08-26T12:13:21+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.6.0"
"platform": {},
"platform-dev": {},
"plugin-api-version": "2.9.0"
}
+1 -4
View File
@@ -14,10 +14,7 @@ if (PHP_VERSION_ID < 50600) {
echo $err;
}
}
trigger_error(
$err,
E_USER_ERROR
);
throw new RuntimeException($err);
}
require_once __DIR__ . '/composer/autoload_real.php';
+41 -4
View File
@@ -26,12 +26,23 @@ use Composer\Semver\VersionParser;
*/
class InstalledVersions
{
/**
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
* @internal
*/
private static $selfDir = null;
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
*/
private static $installed;
/**
* @var bool
*/
private static $installedIsLocalDir;
/**
* @var bool|null
*/
@@ -309,6 +320,24 @@ class InstalledVersions
{
self::$installed = $data;
self::$installedByVendor = array();
// when using reload, we disable the duplicate protection to ensure that self::$installed data is
// always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
// so we have to assume it does not, and that may result in duplicate data being returned when listing
// all installed packages for example
self::$installedIsLocalDir = false;
}
/**
* @return string
*/
private static function getSelfDir()
{
if (self::$selfDir === null) {
self::$selfDir = strtr(__DIR__, '\\', '/');
}
return self::$selfDir;
}
/**
@@ -322,19 +351,27 @@ class InstalledVersions
}
$installed = array();
$copiedLocalDir = false;
if (self::$canGetVendors) {
$selfDir = self::getSelfDir();
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
$vendorDir = strtr($vendorDir, '\\', '/');
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require $vendorDir.'/composer/installed.php';
$installed[] = self::$installedByVendor[$vendorDir] = $required;
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
self::$installedByVendor[$vendorDir] = $required;
$installed[] = $required;
if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
self::$installed = $required;
self::$installedIsLocalDir = true;
}
}
if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
$copiedLocalDir = true;
}
}
}
@@ -350,7 +387,7 @@ class InstalledVersions
}
}
if (self::$installed !== array()) {
if (self::$installed !== array() && !$copiedLocalDir) {
$installed[] = self::$installed;
}
+2
View File
@@ -1,3 +1,4 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -17,3 +18,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+7 -7
View File
@@ -125,17 +125,17 @@
},
{
"name": "phpseclib/phpseclib",
"version": "3.0.43",
"version_normalized": "3.0.43.0",
"version": "3.0.57",
"version_normalized": "3.0.57.0",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02"
"reference": "d17e0ddaeaf6f22f7e007cbb437d78792fe2a0e4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/709ec107af3cb2f385b9617be72af8cf62441d02",
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/d17e0ddaeaf6f22f7e007cbb437d78792fe2a0e4",
"reference": "d17e0ddaeaf6f22f7e007cbb437d78792fe2a0e4",
"shasum": ""
},
"require": {
@@ -153,7 +153,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": "2024-12-14T21:12:59+00:00",
"time": "2026-08-26T12:13:21+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -218,7 +218,7 @@
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.43"
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.57"
},
"funding": [
{
+9 -9
View File
@@ -1,9 +1,9 @@
<?php return array(
'root' => array(
'name' => '__root__',
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'reference' => null,
'pretty_version' => 'dev-main',
'version' => 'dev-main',
'reference' => '6f6e2e6c92dba1cabd61ab6e3dd0c13ae52f161a',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -11,9 +11,9 @@
),
'versions' => array(
'__root__' => array(
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'reference' => null,
'pretty_version' => 'dev-main',
'version' => 'dev-main',
'reference' => '6f6e2e6c92dba1cabd61ab6e3dd0c13ae52f161a',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -38,9 +38,9 @@
'dev_requirement' => false,
),
'phpseclib/phpseclib' => array(
'pretty_version' => '3.0.43',
'version' => '3.0.43.0',
'reference' => '709ec107af3cb2f385b9617be72af8cf62441d02',
'pretty_version' => '3.0.57',
'version' => '3.0.57.0',
'reference' => 'd17e0ddaeaf6f22f7e007cbb437d78792fe2a0e4',
'type' => 'library',
'install_path' => __DIR__ . '/../phpseclib/phpseclib',
'aliases' => array(),
+2 -3
View File
@@ -19,8 +19,7 @@ if ($issues) {
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
throw new \RuntimeException(
'Composer detected issues in your platform: ' . implode(' ', $issues)
);
}
+3
View File
@@ -4,6 +4,7 @@ phpseclib ongoing development is made possible by [Tidelift](https://tidelift.co
## Backers
- [HeroDevs](https://www.herodevs.com/sustainability-fund)
- Allan Simon
- [ChargeOver](https://chargeover.com/)
- Raghu Veer Dendukuri
@@ -17,3 +18,5 @@ phpseclib ongoing development is made possible by [Tidelift](https://tidelift.co
- [Anna Filina](https://github.com/afilina)
- [blakemckeeby](https://github.com/blakemckeeby)
- [ssddanbrown](https://github.com/ssddanbrown)
- Stefan Beck
- [Greg Mayes](https://github.com/GregMayes)
+23 -3
View File
@@ -29,6 +29,17 @@ SSH-2, SFTP, X.509, an arbitrary-precision integer arithmetic library, Ed25519 /
* Unstable API
* Do not use in production
### 4.0
* Expected Release Date: September 2026
* Long term support (LTS) release
* X509 split into separate X509, CRL, CSR and SPKAC classes
* PFX and CMS classes added
* All ASN1 classes are lazy loaded by default
* Minimum PHP version: 8.1.0
* PSR-4 autoloading with namespace rooted at `\phpseclib4`
* Install via Composer: `composer require phpseclib/phpseclib:4.0.x-dev`
### 3.0
* Long term support (LTS) release
@@ -51,7 +62,7 @@ SSH-2, SFTP, X.509, an arbitrary-precision integer arithmetic library, Ed25519 /
* PHP4 compatible
* Composer compatible (PSR-0 autoloading)
* Install using Composer: `composer require phpseclib/phpseclib:~1.0`
* [Download 1.0.23 as ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.23.zip/download)
* [Download 1.0.25 as ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.30.zip/download)
## Security contact information
@@ -63,14 +74,23 @@ Need Support?
* [Checkout Questions and Answers on Stack Overflow](http://stackoverflow.com/questions/tagged/phpseclib)
* [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)
## Special Thanks
Special Thanks to our $50+ sponsors!:
<p align="left">
<a target="_blank" href="https://www.sovereign.tech/tech/phpseclib">
<img src="https://phpseclib.com/img/sponsors/sovereign-tech-agency.webp" alt="Sovereign Tech Agency" style="width: 200px">
</a>
</p>
## Additional Thanks
- [HeroDevs](https://www.herodevs.com/sustainability-fund)
- Allan Simon
- [Anna Filina](https://afilina.com/)
- delovelady
- [ChargeOver](https://chargeover.com/)
- <a href="https://jb.gg/OpenSource"><img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg" height="20px"></a>
## Contributing
+13
View File
@@ -0,0 +1,13 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 1.0.x | :white_check_mark: |
| 2.0.x | :white_check_mark: |
| 3.0.x | :white_check_mark: |
## Reporting a Vulnerability
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
@@ -126,7 +126,9 @@ abstract class Strings
// 64-bit floats can be used to get larger numbers then 32-bit signed ints would allow
// for. sure, you're not gonna get the full precision of 64-bit numbers but just because
// you need > 32-bit precision doesn't mean you need the full 64-bit precision
extract(unpack('Nupper/Nlower', self::shift($data, 8)));
$unpacked = unpack('Nupper/Nlower', self::shift($data, 8));
$upper = $unpacked['upper'];
$lower = $unpacked['lower'];
$temp = $upper ? 4294967296 * $upper : 0;
$temp += $lower < 0 ? ($lower & 0x7FFFFFFFF) + 0x80000000 : $lower;
// $temp = hexdec(bin2hex(self::shift($data, 8)));
@@ -354,9 +356,11 @@ abstract class Strings
// from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits
$p1 = ($b * 0x0802) & 0x22110;
$p2 = ($b * 0x8020) & 0x88440;
$r .= chr(
(($p1 | $p2) * 0x10101) >> 16
);
$temp = ($p1 | $p2) * 0x10101;
if (is_float($temp)) {
$temp = (int) fmod($temp, 0x7FFFFFFF);
}
$r .= chr(($temp >> 16) & 0xFF);
}
}
return $r;
+1 -1
View File
@@ -59,7 +59,7 @@ class AES extends Rijndael
*
* Since \phpseclib3\Crypt\AES extends \phpseclib3\Crypt\Rijndael, this function is, technically, available, but it doesn't do anything.
*
* @see \phpseclib3\Crypt\Rijndael::setBlockLength()
* @see Rijndael::setBlockLength()
* @param int $length
* @throws \BadMethodCallException anytime it's called
*/
@@ -129,7 +129,7 @@ class Blowfish extends BlockCipher
/**
* Block Length of the cipher
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::block_size
* @see Common\SymmetricKey::block_size
* @var int
*/
protected $block_size = 8;
@@ -137,7 +137,7 @@ class Blowfish extends BlockCipher
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt
* @see Common\SymmetricKey::cipher_name_mcrypt
* @var string
*/
protected $cipher_name_mcrypt = 'blowfish';
@@ -145,7 +145,7 @@ class Blowfish extends BlockCipher
/**
* Optimizing value while CFB-encrypting
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len
* @see Common\SymmetricKey::cfb_init_len
* @var int
*/
protected $cfb_init_len = 500;
@@ -325,7 +325,7 @@ class Blowfish extends BlockCipher
* derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
* of that, we'll just precompute it once.}
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::setKeyLength()
* @see Common\SymmetricKey::setKeyLength()
* @var int
*/
protected $key_length = 16;
@@ -368,7 +368,7 @@ class Blowfish extends BlockCipher
*
* This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
* @see Common\SymmetricKey::isValidEngine()
* @param int $engine
* @return bool
*/
@@ -394,7 +394,7 @@ class Blowfish extends BlockCipher
/**
* Setup the key (expansion)
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::_setupKey()
* @see Common\SymmetricKey::_setupKey()
*/
protected function setupKey()
{
@@ -448,8 +448,8 @@ class Blowfish extends BlockCipher
protected static function initialize_static_variables()
{
if (is_float(self::$sbox[0x200])) {
self::$sbox = array_map('intval', self::$sbox);
self::$parray = array_map('intval', self::$parray);
self::$sbox = array_map([self::class, 'safe_intval'], self::$sbox);
self::$parray = array_map([self::class, 'safe_intval'], self::$parray);
}
parent::initialize_static_variables();
@@ -755,7 +755,7 @@ class Blowfish extends BlockCipher
/**
* Setup the performance-optimized function for de/encrypt()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::_setupInlineCrypt()
* @see Common\SymmetricKey::_setupInlineCrypt()
*/
protected function setupInlineCrypt()
{
+328 -328
View File
@@ -35,7 +35,7 @@ class ChaCha20 extends Salsa20
*
* This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* @see SymmetricKey::__construct()
* @param int $engine
* @return bool
*/
@@ -73,7 +73,7 @@ class ChaCha20 extends Salsa20
/**
* Encrypts a message.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* @see SymmetricKey::decrypt()
* @see self::crypt()
* @param string $plaintext
* @return string $ciphertext
@@ -95,7 +95,7 @@ class ChaCha20 extends Salsa20
* $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
* At least if the continuous buffer is disabled.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see SymmetricKey::encrypt()
* @see self::crypt()
* @param string $ciphertext
* @return string $plaintext
@@ -279,10 +279,10 @@ class ChaCha20 extends Salsa20
// xor'ing and rotation are all on the same line so i'm keeping it on the same
// line here as well
// @codingStandardsIgnoreStart
$a+= $b; $d = self::leftRotate(intval($d) ^ intval($a), 16);
$c+= $d; $b = self::leftRotate(intval($b) ^ intval($c), 12);
$a+= $b; $d = self::leftRotate(intval($d) ^ intval($a), 8);
$c+= $d; $b = self::leftRotate(intval($b) ^ intval($c), 7);
$a+= $b; $d = self::leftRotate(self::safe_intval($d) ^ self::safe_intval($a), 16);
$c+= $d; $b = self::leftRotate(self::safe_intval($b) ^ self::safe_intval($c), 12);
$a+= $b; $d = self::leftRotate(self::safe_intval($d) ^ self::safe_intval($a), 8);
$c+= $d; $b = self::leftRotate(self::safe_intval($b) ^ self::safe_intval($c), 7);
// @codingStandardsIgnoreEnd
}
@@ -357,424 +357,424 @@ class ChaCha20 extends Salsa20
// @codingStandardsIgnoreStart
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);
// columnRound
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(intval($x12) ^ intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(intval($x4) ^ intval($x8), 7);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 16);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 12);
$x0+= $x4; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x0), 8);
$x8+= $x12; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x8), 7);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(intval($x13) ^ intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(intval($x5) ^ intval($x9), 7);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 16);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 12);
$x1+= $x5; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x1), 8);
$x9+= $x13; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x9), 7);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(intval($x14) ^ intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(intval($x6) ^ intval($x10), 7);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 16);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 12);
$x2+= $x6; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x2), 8);
$x10+= $x14; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x10), 7);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(intval($x15) ^ intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(intval($x7) ^ intval($x11), 7);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 16);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 12);
$x3+= $x7; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x3), 8);
$x11+= $x15; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x11), 7);
// rowRound
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(intval($x15) ^ intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(intval($x5) ^ intval($x10), 7);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 16);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 12);
$x0+= $x5; $x15 = self::leftRotate(self::safe_intval($x15) ^ self::safe_intval($x0), 8);
$x10+= $x15; $x5 = self::leftRotate(self::safe_intval($x5) ^ self::safe_intval($x10), 7);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(intval($x12) ^ intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(intval($x6) ^ intval($x11), 7);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 16);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 12);
$x1+= $x6; $x12 = self::leftRotate(self::safe_intval($x12) ^ self::safe_intval($x1), 8);
$x11+= $x12; $x6 = self::leftRotate(self::safe_intval($x6) ^ self::safe_intval($x11), 7);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(intval($x13) ^ intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(intval($x7) ^ intval($x8), 7);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 16);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 12);
$x2+= $x7; $x13 = self::leftRotate(self::safe_intval($x13) ^ self::safe_intval($x2), 8);
$x8+= $x13; $x7 = self::leftRotate(self::safe_intval($x7) ^ self::safe_intval($x8), 7);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(intval($x14) ^ intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(intval($x4) ^ intval($x9), 7);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 16);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 12);
$x3+= $x4; $x14 = self::leftRotate(self::safe_intval($x14) ^ self::safe_intval($x3), 8);
$x9+= $x14; $x4 = self::leftRotate(self::safe_intval($x4) ^ self::safe_intval($x9), 7);
// @codingStandardsIgnoreEnd
$x0 += $z0;
@@ -794,6 +794,6 @@ class ChaCha20 extends Salsa20
$x14 += $z14;
$x15 += $z15;
return pack('V*', $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15);
return pack('V*', self::safe_intval($x0), self::safe_intval($x1), self::safe_intval($x2), self::safe_intval($x3), self::safe_intval($x4), self::safe_intval($x5), self::safe_intval($x6), self::safe_intval($x7), self::safe_intval($x8), self::safe_intval($x9), self::safe_intval($x10), self::safe_intval($x11), self::safe_intval($x12), self::safe_intval($x13), self::safe_intval($x14), self::safe_intval($x15));
}
}
@@ -78,13 +78,6 @@ abstract class AsymmetricKey
*/
private static $invisiblePlugins = [];
/**
* Available Engines
*
* @var boolean[]
*/
protected static $engines = [];
/**
* Key Comment
*
@@ -92,6 +85,14 @@ abstract class AsymmetricKey
*/
private $comment;
/**
* OpenSSL configuration file name.
*
* @see self::createKey()
* @var ?string
*/
protected static $configFile;
/**
* @param string $type
* @return array|string
@@ -119,6 +120,10 @@ abstract class AsymmetricKey
self::$one = new BigInteger(1);
}
if (!isset(self::$configFile)) {
self::$configFile = dirname(__FILE__) . '/../../openssl.cnf';
}
self::loadPlugins('Keys');
if (static::ALGORITHM != 'RSA' && static::ALGORITHM != 'DH') {
self::loadPlugins('Signature');
@@ -363,6 +368,18 @@ abstract class AsymmetricKey
return self::$plugins[static::ALGORITHM]['Keys'];
}
/**
* Sets the OpenSSL config file path
*
* Set to the empty string to use the default config file
*
* @param string $val
*/
public static function setOpenSSLConfigPath($val)
{
self::$configFile = $val;
}
/**
* Add a fileformat plugin
*
@@ -419,34 +436,28 @@ abstract class AsymmetricKey
}
/**
* Tests engine validity
*
* Force engine (useful for unit testing)
*/
public static function useBestEngine()
public static function forceEngine($engine = null)
{
static::$engines = [
'PHP' => true,
'OpenSSL' => extension_loaded('openssl'),
// this test can be satisfied by either of the following:
// http://php.net/manual/en/book.sodium.php
// https://github.com/paragonie/sodium_compat
'libsodium' => function_exists('sodium_crypto_sign_keypair')
];
return static::$engines;
if (!isset($engine)) {
static::$forcedEngine = null;
return;
}
switch ($engine) {
case 'PHP':
case 'OpenSSL':
case 'libsodium':
static::$forcedEngine = $engine;
break;
default:
throw new \InvalidArgumentException('Valid engines are null, PHP, OpenSSL or libsodium');
}
}
/**
* Flag to use internal engine only (useful for unit testing)
*
*/
public static function useInternalEngine()
public static function getForcedEngine()
{
static::$engines = [
'PHP' => true,
'OpenSSL' => false,
'libsodium' => false
];
return static::$forcedEngine;
}
/**
@@ -50,6 +50,14 @@ abstract class JWK
return $key;
}
if (!is_object($key)) {
throw new \RuntimeException('invalid JWK: not an object');
}
if (!isset($key->keys)) {
throw new \RuntimeException('invalid JWK: object has no property "keys"');
}
if (count($key->keys) != 1) {
throw new \RuntimeException('Although the JWK key format supports multiple keys phpseclib does not');
}
@@ -118,7 +118,7 @@ abstract class OpenSSH
return compact('type', 'publicKey', 'paddedKey');
}
$parts = explode(' ', $key, 3);
$parts = preg_split("#[\t ]+#", $key, 3);
if (!isset($parts[1])) {
$key = base64_decode($parts[0]);
@@ -361,7 +361,9 @@ abstract class PKCS8 extends PKCS
if (!$temp) {
throw new \RuntimeException('Unable to decode BER');
}
extract(ASN1::asn1map($temp[0], Maps\PBEParameter::MAP));
$map = ASN1::asn1map($temp[0], Maps\PBEParameter::MAP);
$salt = $map['salt'];
$iterationCount = $map['iterationCount'];
$iterationCount = (int) $iterationCount->toString();
$cipher->setPassword($password, $kdf, $hash, $salt, $iterationCount);
$key = $cipher->decrypt($decrypted['encryptedData']);
@@ -379,7 +381,8 @@ abstract class PKCS8 extends PKCS
throw new \RuntimeException('Unable to decode BER');
}
$temp = ASN1::asn1map($temp[0], Maps\PBES2params::MAP);
extract($temp);
$keyDerivationFunc = $temp['keyDerivationFunc'];
$encryptionScheme = $temp['encryptionScheme'];
$cipher = self::getPBES2EncryptionObject($encryptionScheme['algorithm']);
$meta['meta']['cipher'] = $encryptionScheme['algorithm'];
@@ -389,7 +392,8 @@ abstract class PKCS8 extends PKCS
throw new \RuntimeException('Unable to decode BER');
}
$temp = ASN1::asn1map($temp[0], Maps\PBES2params::MAP);
extract($temp);
$keyDerivationFunc = $temp['keyDerivationFunc'];
$encryptionScheme = $temp['encryptionScheme'];
if (!$cipher instanceof RC2) {
$cipher->setIV($encryptionScheme['parameters']['octetString']);
@@ -398,7 +402,9 @@ abstract class PKCS8 extends PKCS
if (!$temp) {
throw new \RuntimeException('Unable to decode BER');
}
extract(ASN1::asn1map($temp[0], Maps\RC2CBCParameter::MAP));
$map = ASN1::asn1map($temp[0], Maps\RC2CBCParameter::MAP);
$rc2ParametersVersion = $map['rc2ParametersVersion'];
$iv = $map['iv'];
$effectiveKeyLength = (int) $rc2ParametersVersion->toString();
switch ($effectiveKeyLength) {
case 160:
@@ -423,9 +429,13 @@ abstract class PKCS8 extends PKCS
if (!$temp) {
throw new \RuntimeException('Unable to decode BER');
}
$prf = ['algorithm' => 'id-hmacWithSHA1'];
$params = ASN1::asn1map($temp[0], Maps\PBKDF2params::MAP);
extract($params);
if (empty($params['prf'])) {
$params['prf'] = ['algorithm' => 'id-hmacWithSHA1'];
}
$salt = $params['salt'];
$iterationCount = $params['iterationCount'];
$prf = $params['prf'];
$meta['meta']['prf'] = $prf['algorithm'];
$hash = str_replace('-', '/', substr($prf['algorithm'], 11));
$params = [
@@ -555,7 +565,7 @@ abstract class PKCS8 extends PKCS
],
'privateKey' => $key
];
if ($oid != 'id-Ed25519' && $oid != 'id-Ed448') {
if ($oid != 'id-Ed25519' && $oid != 'id-Ed448' && $oid != 'id-X25519' && $oid != 'id-X448') {
$key['privateKeyAlgorithm']['parameters'] = $params;
}
if (!empty($attr)) {
@@ -672,7 +682,7 @@ abstract class PKCS8 extends PKCS
'publicKey' => "\0" . $key
];
if ($oid != 'id-Ed25519' && $oid != 'id-Ed448') {
if ($oid != 'id-Ed25519' && $oid != 'id-Ed448' && $oid != 'id-X25519' && $oid != 'id-X448') {
$key['publicKeyAlgorithm']['parameters'] = $params;
}
@@ -199,7 +199,7 @@ abstract class PuTTY
$source = Strings::packSSH2('ssss', $type, $encryption, $components['comment'], $public);
extract(unpack('Nlength', Strings::shift($public, 4)));
$length = unpack('Nlength', Strings::shift($public, 4))['length'];
$newtype = Strings::shift($public, $length);
if ($newtype != $type) {
throw new \RuntimeException('The binary type does not match the human readable type field');
@@ -227,7 +227,10 @@ abstract class PuTTY
$parallelism = trim(preg_replace('#Argon2-Parallelism: (\d+)#', '$1', $key[$offset++]));
$salt = Strings::hex2bin(trim(preg_replace('#Argon2-Salt: ([0-9a-f]+)#', '$1', $key[$offset++])));
extract(self::generateV3Key($password, $flavour, $memory, $passes, $salt));
$v3key = self::generateV3Key($password, $flavour, $memory, $passes, $salt);
$symkey = $v3key['symkey'];
$symiv = $v3key['symiv'];
$hashkey = $v3key['hashkey'];
break;
case 2:
@@ -323,7 +326,10 @@ abstract class PuTTY
$key .= "Argon2-Passes: 13\r\n";
$key .= "Argon2-Parallelism: 1\r\n";
$key .= "Argon2-Salt: " . Strings::bin2hex($salt) . "\r\n";
extract(self::generateV3Key($password, 'Argon2id', 8192, 13, $salt));
$v3key = self::generateV3Key($password, 'Argon2id', 8192, 13, $salt);
$symkey = $v3key['symkey'];
$symiv = $v3key['symiv'];
$hashkey = $v3key['hashkey'];
$hash = new Hash('sha256');
$hash->setKey($hashkey);
@@ -26,7 +26,7 @@ abstract class StreamCipher extends SymmetricKey
*
* Stream ciphers do not have a block size
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::block_size
* @see SymmetricKey::block_size
* @var int
*/
protected $block_size = 0;
@@ -34,7 +34,7 @@ abstract class StreamCipher extends SymmetricKey
/**
* Default Constructor.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* @see SymmetricKey::__construct()
* @return StreamCipher
*/
public function __construct()
@@ -60,76 +60,76 @@ abstract class SymmetricKey
* Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* SymmetricKey::encrypt()
* SymmetricKey::decrypt()
*/
const MODE_CTR = -1;
/**
* Encrypt / decrypt using the Electronic Code Book mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* SymmetricKey::encrypt()
* SymmetricKey::decrypt()
*/
const MODE_ECB = 1;
/**
* Encrypt / decrypt using the Code Book Chaining mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* SymmetricKey::encrypt()
* SymmetricKey::decrypt()
*/
const MODE_CBC = 2;
/**
* Encrypt / decrypt using the Cipher Feedback mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* SymmetricKey::encrypt()
* SymmetricKey::decrypt()
*/
const MODE_CFB = 3;
/**
* Encrypt / decrypt using the Cipher Feedback mode (8bit)
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* SymmetricKey::encrypt()
* SymmetricKey::decrypt()
*/
const MODE_CFB8 = 7;
/**
* Encrypt / decrypt using the Output Feedback mode (8bit)
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* SymmetricKey::encrypt()
* SymmetricKey::decrypt()
*/
const MODE_OFB8 = 8;
/**
* Encrypt / decrypt using the Output Feedback mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* SymmetricKey::encrypt()
* SymmetricKey::decrypt()
*/
const MODE_OFB = 4;
/**
* Encrypt / decrypt using Galois/Counter mode.
*
* @link https://en.wikipedia.org/wiki/Galois/Counter_Mode
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* SymmetricKey::encrypt()
* SymmetricKey::decrypt()
*/
const MODE_GCM = 5;
/**
* Encrypt / decrypt using streaming mode.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* SymmetricKey::encrypt()
* SymmetricKey::decrypt()
*/
const MODE_STREAM = 6;
/**
* Mode Map
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* SymmetricKey::__construct()
*/
const MODE_MAP = [
'ctr' => self::MODE_CTR,
@@ -146,44 +146,44 @@ abstract class SymmetricKey
/**
* Base value for the internal implementation $engine switch
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* SymmetricKey::__construct()
*/
const ENGINE_INTERNAL = 1;
/**
* Base value for the eval() implementation $engine switch
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* SymmetricKey::__construct()
*/
const ENGINE_EVAL = 2;
/**
* Base value for the mcrypt implementation $engine switch
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* SymmetricKey::__construct()
*/
const ENGINE_MCRYPT = 3;
/**
* Base value for the openssl implementation $engine switch
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* SymmetricKey::__construct()
*/
const ENGINE_OPENSSL = 4;
/**
* Base value for the libsodium implementation $engine switch
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* SymmetricKey::__construct()
*/
const ENGINE_LIBSODIUM = 5;
/**
* Base value for the openssl / gcm implementation $engine switch
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* SymmetricKey::__construct()
*/
const ENGINE_OPENSSL_GCM = 6;
/**
* Engine Reverse Map
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::getEngine()
* SymmetricKey::getEngine()
*/
const ENGINE_MAP = [
self::ENGINE_INTERNAL => 'PHP',
@@ -666,6 +666,11 @@ abstract class SymmetricKey
{
if (!isset(self::$use_reg_intval)) {
switch (true) {
// PHP 8.5, per https://www.php.net/manual/en/migration85.incompatible.php, now emits a warning
// "when casting floats (or strings that look like floats) to int if they cannot be represented as one"
case PHP_VERSION_ID >= 80500 && PHP_INT_SIZE == 4:
self::$use_reg_intval = false;
break;
// PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster
case (PHP_OS & "\xDF\xDF\xDF") === 'WIN':
case !function_exists('php_uname'):
@@ -1265,7 +1270,7 @@ abstract class SymmetricKey
if ($this->continuousBuffer) {
$this->encryptIV = $iv;
}
break;
return $ciphertext;
case self::MODE_OFB:
return $this->openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer);
}
@@ -2591,7 +2596,7 @@ abstract class SymmetricKey
$length = ord($text[strlen($text) - 1]);
if (!$length || $length > $this->block_size) {
if (!$length | ($length > $this->block_size)) {
throw new BadDecryptionException("The ciphertext has an invalid padding length ($length) compared to the block size ({$this->block_size})");
}
+35 -35
View File
@@ -53,22 +53,22 @@ class DES extends BlockCipher
/**
* Contains $keys[self::ENCRYPT]
*
* @see \phpseclib3\Crypt\DES::setupKey()
* @see \phpseclib3\Crypt\DES::processBlock()
* @see DES::setupKey()
* @see DES::processBlock()
*/
const ENCRYPT = 0;
/**
* Contains $keys[self::DECRYPT]
*
* @see \phpseclib3\Crypt\DES::setupKey()
* @see \phpseclib3\Crypt\DES::processBlock()
* @see DES::setupKey()
* @see DES::processBlock()
*/
const DECRYPT = 1;
/**
* Block Length of the cipher
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::block_size
* @see Common\SymmetricKey::block_size
* @var int
*/
protected $block_size = 8;
@@ -76,7 +76,7 @@ class DES extends BlockCipher
/**
* Key Length (in bytes)
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::setKeyLength()
* @see Common\SymmetricKey::setKeyLength()
* @var int
*/
protected $key_length = 8;
@@ -84,7 +84,7 @@ class DES extends BlockCipher
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt
* @see Common\SymmetricKey::cipher_name_mcrypt
* @var string
*/
protected $cipher_name_mcrypt = 'des';
@@ -92,7 +92,7 @@ class DES extends BlockCipher
/**
* The OpenSSL names of the cipher / modes
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::openssl_mode_names
* @see Common\SymmetricKey::openssl_mode_names
* @var array
*/
protected $openssl_mode_names = [
@@ -106,7 +106,7 @@ class DES extends BlockCipher
/**
* Optimizing value while CFB-encrypting
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len
* @see Common\SymmetricKey::cfb_init_len
* @var int
*/
protected $cfb_init_len = 500;
@@ -586,7 +586,7 @@ class DES extends BlockCipher
*
* This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
* @see Common\SymmetricKey::isValidEngine()
* @param int $engine
* @return bool
*/
@@ -615,7 +615,7 @@ class DES extends BlockCipher
*
* DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::setKey()
* @see Common\SymmetricKey::setKey()
* @param string $key
*/
public function setKey($key)
@@ -631,8 +631,8 @@ class DES extends BlockCipher
/**
* Encrypts a block
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::encryptBlock()
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see Common\SymmetricKey::encryptBlock()
* @see Common\SymmetricKey::encrypt()
* @see self::encrypt()
* @param string $in
* @return string
@@ -645,8 +645,8 @@ class DES extends BlockCipher
/**
* Decrypts a block
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::decryptBlock()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* @see Common\SymmetricKey::decryptBlock()
* @see Common\SymmetricKey::decrypt()
* @see self::decrypt()
* @param string $in
* @return string
@@ -673,14 +673,14 @@ class DES extends BlockCipher
{
static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip;
if (!$sbox1) {
$sbox1 = array_map('intval', self::$sbox1);
$sbox2 = array_map('intval', self::$sbox2);
$sbox3 = array_map('intval', self::$sbox3);
$sbox4 = array_map('intval', self::$sbox4);
$sbox5 = array_map('intval', self::$sbox5);
$sbox6 = array_map('intval', self::$sbox6);
$sbox7 = array_map('intval', self::$sbox7);
$sbox8 = array_map('intval', self::$sbox8);
$sbox1 = array_map([self::class, 'safe_intval'], self::$sbox1);
$sbox2 = array_map([self::class, 'safe_intval'], self::$sbox2);
$sbox3 = array_map([self::class, 'safe_intval'], self::$sbox3);
$sbox4 = array_map([self::class, 'safe_intval'], self::$sbox4);
$sbox5 = array_map([self::class, 'safe_intval'], self::$sbox5);
$sbox6 = array_map([self::class, 'safe_intval'], self::$sbox6);
$sbox7 = array_map([self::class, 'safe_intval'], self::$sbox7);
$sbox8 = array_map([self::class, 'safe_intval'], self::$sbox8);
/* Merge $shuffle with $[inv]ipmap */
for ($i = 0; $i < 256; ++$i) {
$shuffleip[] = self::$shuffle[self::$ipmap[$i]];
@@ -747,7 +747,7 @@ class DES extends BlockCipher
/**
* Creates the key schedule
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::setupKey()
* @see Common\SymmetricKey::setupKey()
*/
protected function setupKey()
{
@@ -1243,9 +1243,9 @@ class DES extends BlockCipher
$pc2mapd3[($d >> 8) & 0xFF] | $pc2mapd4[ $d & 0xFF];
// Reorder: odd bytes/even bytes. Push the result in key schedule.
$val1 = ( $cp & intval(0xFF000000)) | (($cp << 8) & 0x00FF0000) |
$val1 = ( $cp & self::safe_intval(0xFF000000)) | (($cp << 8) & 0x00FF0000) |
(($dp >> 16) & 0x0000FF00) | (($dp >> 8) & 0x000000FF);
$val2 = (($cp << 8) & intval(0xFF000000)) | (($cp << 16) & 0x00FF0000) |
$val2 = (($cp << 8) & self::safe_intval(0xFF000000)) | (($cp << 16) & 0x00FF0000) |
(($dp >> 8) & 0x0000FF00) | ( $dp & 0x000000FF);
$keys[$des_round][self::ENCRYPT][ ] = $val1;
$keys[$des_round][self::DECRYPT][$ki - 1] = $val1;
@@ -1281,7 +1281,7 @@ class DES extends BlockCipher
/**
* Setup the performance-optimized function for de/encrypt()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::setupInlineCrypt()
* @see Common\SymmetricKey::setupInlineCrypt()
*/
protected function setupInlineCrypt()
{
@@ -1292,14 +1292,14 @@ class DES extends BlockCipher
$init_crypt = 'static $sbox1, $sbox2, $sbox3, $sbox4, $sbox5, $sbox6, $sbox7, $sbox8, $shuffleip, $shuffleinvip;
if (!$sbox1) {
$sbox1 = array_map("intval", self::$sbox1);
$sbox2 = array_map("intval", self::$sbox2);
$sbox3 = array_map("intval", self::$sbox3);
$sbox4 = array_map("intval", self::$sbox4);
$sbox5 = array_map("intval", self::$sbox5);
$sbox6 = array_map("intval", self::$sbox6);
$sbox7 = array_map("intval", self::$sbox7);
$sbox8 = array_map("intval", self::$sbox8);'
$sbox1 = array_map(["' . self::class . '", "safe_intval"], self::$sbox1);
$sbox2 = array_map(["' . self::class . '", "safe_intval"], self::$sbox2);
$sbox3 = array_map(["' . self::class . '", "safe_intval"], self::$sbox3);
$sbox4 = array_map(["' . self::class . '", "safe_intval"], self::$sbox4);
$sbox5 = array_map(["' . self::class . '", "safe_intval"], self::$sbox5);
$sbox6 = array_map(["' . self::class . '", "safe_intval"], self::$sbox6);
$sbox7 = array_map(["' . self::class . '", "safe_intval"], self::$sbox7);
$sbox8 = array_map(["' . self::class . '", "safe_intval"], self::$sbox8);'
/* Merge $shuffle with $[inv]ipmap */ . '
for ($i = 0; $i < 256; ++$i) {
$shuffleip[] = self::$shuffle[self::$ipmap[$i]];
+51 -7
View File
@@ -28,6 +28,7 @@ use phpseclib3\Crypt\Common\AsymmetricKey;
use phpseclib3\Crypt\DH\Parameters;
use phpseclib3\Crypt\DH\PrivateKey;
use phpseclib3\Crypt\DH\PublicKey;
use phpseclib3\Exception\BadConfigurationException;
use phpseclib3\Exception\NoKeyLoadedException;
use phpseclib3\Exception\UnsupportedOperationException;
use phpseclib3\Math\BigInteger;
@@ -295,21 +296,64 @@ abstract class DH extends AsymmetricKey
}
if ($private instanceof EC\PrivateKey) {
$privateCurve = $private->getCurve();
switch (true) {
case $public instanceof EC\PublicKey:
if ($privateCurve !== $public->getCurve()) {
throw new \InvalidArgumentException("The public key curve (" . $public->getCurve() . ") and private key curve ($privateCurve) need to match");
}
$orig = $public;
$public = $public->getEncodedCoordinates();
// fall-through
case is_string($public):
$forcedEngine = EC::getForcedEngine();
if ($forcedEngine === 'libsodium' && $privateCurve !== 'Curve25519') {
throw new BadConfigurationException('Engine libsodium is forced but can only used with Curve25519 for ECDH');
}
if (!isset($forcedEngine) || $forcedEngine === 'OpenSSL') {
// PHP 7.3.0 introduced the openssl_pkey_derive() function
// openssl_dh_computee_key() has been around since PHP 5.3.0+ BUT it did not support ECDH
// until PHP 8.1.0 / OpenSSL 3.0.0
if ($forcedEngine === 'OpenSSL' && !function_exists('openssl_pkey_derive')) {
throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for ECDH');
}
if (function_exists('openssl_pkey_derive')) {
$privateStr = (string) $private->withPassword();
$publicStr = (string) (isset($orig) ? $orig : EC::convertPointToPublicKey($private->getCurve(), $public));
$result = openssl_pkey_derive($publicStr, $privateStr);
if ($result) {
return $result;
}
if ($forcedEngine === 'OpenSSL') {
// i suppose we _could_ try openssl_dh_compute_key() at this point
// quoting https://www.php.net/openssl-dh-compute-key "ECDH is only supported as of PHP 8.1.0 and OpenSSL 3.0.0". ie.
// PHP_VERSION_ID >= 80100 && OPENSSL_VERSION_NUMBER >= 0x3000000f
// but i think that's overkill. if openssl_pkey_derive() doesn't work it seems doubtful to me that openssl_dh_compute_key() would
throw new BadConfigurationException('Engine OpenSSL is forced but was unable to perform ECDH because of ' . openssl_error_string());
}
}
}
$curveName = $private->getCurve();
$isMontgomeryCurve = $curveName == 'Curve25519' || $curveName == 'Curve448';
if (!$isMontgomeryCurve) {
$public = EC::convertPointToPublicKey($curveName, $public, false);
}
$point = $private->multiply($public);
switch ($private->getCurve()) {
case 'Curve25519':
case 'Curve448':
$secret = $point;
break;
default:
if ($isMontgomeryCurve) {
/*
"Both MAY check, without leaking extra information about the value of K,
whether K is the all-zero value and abort if so"
-- https://datatracker.ietf.org/doc/html/rfc7748#section-6.1 (and #section-6.2)
*/
$size = $curveName == 'Curve25519' ? 32 : 56;
// throw exception if hash_equals is false, otherwise, return $point
if (hash_equals(str_repeat("\0", $size), $point)) {
throw new \UnexpectedValueException('All-zero shared secret detected (points order is too small)');
}
return $point;
}
// according to https://www.secg.org/sec1-v2.pdf#page=33 only X is returned
$secret = substr($point, 1, (strlen($point) - 1) >> 1);
}
/*
if (($secret[0] & "\x80") === "\x80") {
$secret = "\0$secret";
+44 -28
View File
@@ -33,6 +33,7 @@ use phpseclib3\Crypt\Common\AsymmetricKey;
use phpseclib3\Crypt\DSA\Parameters;
use phpseclib3\Crypt\DSA\PrivateKey;
use phpseclib3\Crypt\DSA\PublicKey;
use phpseclib3\Exception\BadConfigurationException;
use phpseclib3\Exception\InsufficientSetupException;
use phpseclib3\Math\BigInteger;
@@ -87,6 +88,14 @@ abstract class DSA extends AsymmetricKey
*/
protected $sigFormat;
/**
* Forced Engine
*
* @var ?string
* @see parent::forceEngine()
*/
protected static $forcedEngine = null;
/**
* Signature Format (Short)
*
@@ -110,10 +119,6 @@ abstract class DSA extends AsymmetricKey
throw new \RuntimeException('createParameters() should not be called from final classes (' . static::class . ')');
}
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
switch (true) {
case $N == 160:
/*
@@ -185,15 +190,19 @@ abstract class DSA extends AsymmetricKey
{
self::initialize_static_variables();
if (self::$forcedEngine == 'libsodium') {
throw new BadConfigurationException('Engine ' . self::$forcedEngine . ' is forced but unsupported for DSA');
}
if (self::$forcedEngine == 'OpenSSL' && !defined('OPENSSL_KEYTYPE_DSA')) {
throw new BadConfigurationException("Engine OpenSSL is forced but unsupported for DSA");
}
$class = new \ReflectionClass(static::class);
if ($class->isFinal()) {
throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')');
}
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
if (count($args) == 2 && is_int($args[0]) && is_int($args[1])) {
$params = self::createParameters($args[0], $args[1]);
} elseif (count($args) == 1 && $args[0] instanceof Parameters) {
@@ -204,6 +213,33 @@ abstract class DSA extends AsymmetricKey
throw new InsufficientSetupException('Valid parameters are either two integers (L and N), a single DSA object or no parameters at all.');
}
// at this point the only two supported values for self::$forcedEngine are OpenSSL, PHP and null
// if it's either OpenSSL or null we'll use OpenSSL (if it's available)
if (self::$forcedEngine !== 'PHP' && defined('OPENSSL_KEYTYPE_DSA')) {
$config = [];
if (self::$configFile) {
$config['config'] = self::$configFile;
}
$dsa = openssl_pkey_new($config + [
'private_key_type' => OPENSSL_KEYTYPE_DSA,
'p' => $params->p,
'q' => $params->q,
'g' => $params->g,
]);
if ($dsa && openssl_pkey_export($dsa, $privatekeystr, null, $config)) {
// clear the buffer of error strings stemming from a minimalistic openssl.cnf
// https://github.com/php/php-src/issues/11054 talks about other errors this'll pick up
while (openssl_error_string() !== false) {
}
return DSA::load($privatekeystr)
->withHash($params->hash->getHash())
->withSignatureFormat($params->shortFormat);
} elseif (isset(self::$forcedEngine)) {
throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for DSA');
}
}
$private = new PrivateKey();
$private->p = $params->p;
$private->q = $params->q;
@@ -227,10 +263,6 @@ abstract class DSA extends AsymmetricKey
*/
protected static function onLoad(array $components)
{
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
if (!isset($components['x']) && !isset($components['y'])) {
$new = new Parameters();
} elseif (isset($components['x'])) {
@@ -276,22 +308,6 @@ abstract class DSA extends AsymmetricKey
return ['L' => $this->p->getLength(), 'N' => $this->q->getLength()];
}
/**
* Returns the current engine being used
*
* @see self::useInternalEngine()
* @see self::useBestEngine()
* @return string
*/
public function getEngine()
{
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
return self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods()) ?
'OpenSSL' : 'PHP';
}
/**
* Returns the parameters
*
@@ -63,13 +63,28 @@ abstract class PKCS1 extends Progenitor
$key = ASN1::asn1map($decoded[0], Maps\DSAPrivateKey::MAP);
if (is_array($key)) {
if ($key['version']->toString() !== '0') {
throw new \UnexpectedValueException('Version number is not valid');
}
return $key;
}
$key = ASN1::asn1map($decoded[0], Maps\DSAPublicKey::MAP);
if (is_array($key)) {
return $key;
}
// PKCS1 DSA public keys are not supported by phpseclib since they can't be used to do
// anything on their own. in order to verify a signature with DSA you need p, q, g and y.
// a PKCS1 DSA public key only has y. to verify a signature with a PKCS1 DSA public key
// you'd also need to load a PKCS1 DSA parameters file separately. like you'd need to
// load two files instead of just one. there's no other key format that phpseclib supports
// that has that requirement so building it in for PKCS1 DSA public keys seems excessive.
//
// the whole thing would be rather like an RSA public key having the modulo live in
// a separate file than the exponent.
//
// this isn't an issue for PKCS8 DSA public keys because those keys have the parameters
// included. eg. \phpseclib3\File\ASN1\Maps\SubjectPublicKeyInfo has "algorithm" and
// "subjectPublicKey" and "algorithm", in turn, has "algorithm" and "parameters". y
// is saved as "subjectPublicKey" and p, q and g are saved as "parameters".
//$key = ASN1::asn1map($decoded[0], Maps\DSAPublicKey::MAP);
throw new \RuntimeException('Unable to perform ASN1 mapping');
}
@@ -56,7 +56,10 @@ abstract class PuTTY extends Progenitor
if (!isset($components['private'])) {
return $components;
}
extract($components);
$type = $components['type'];
$comment = $components['comment'];
$public = $components['public'];
$private = $components['private'];
unset($components['public'], $components['private']);
list($p, $q, $g, $y) = Strings::unpackSSH2('iiii', $public);
@@ -14,6 +14,7 @@ namespace phpseclib3\Crypt\DSA;
use phpseclib3\Crypt\Common;
use phpseclib3\Crypt\DSA;
use phpseclib3\Crypt\DSA\Formats\Signature\ASN1 as ASN1Signature;
use phpseclib3\Exception\BadConfigurationException;
use phpseclib3\Math\BigInteger;
/**
@@ -79,18 +80,34 @@ final class PrivateKey extends DSA implements Common\PrivateKey
{
$format = $this->sigFormat;
if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
if (self::$forcedEngine === 'libsodium') {
throw new BadConfigurationException('Engine libsodium is forced but unsupported for DSA');
}
if (self::$forcedEngine === 'OpenSSL' && !function_exists('openssl_get_md_methods')) {
throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for DSA');
}
if (function_exists('openssl_get_md_methods') && self::$forcedEngine !== 'PHP') {
if (in_array($this->hash->getHash(), openssl_get_md_methods())) {
$signature = '';
$result = openssl_sign($message, $signature, $this->toString('PKCS8'), $this->hash->getHash());
$result = openssl_sign($message, $signature, $this->withPassword()->toString('PKCS8'), $this->hash->getHash());
if ($result) {
if ($this->shortFormat == 'ASN1') {
return $signature;
}
extract(ASN1Signature::load($signature));
$loaded = ASN1Signature::load($signature);
$r = $loaded['r'];
$s = $loaded['s'];
return $format::save($r, $s);
} elseif (self::$forcedEngine === 'OpenSSL') {
throw new BadConfigurationException('Engine OpenSSL is forced but was unable to create signature because of ' . openssl_error_string());
}
} elseif (self::$forcedEngine === 'OpenSSL') {
throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for DSA / ' . $this->hash->getHash());
}
}
@@ -14,6 +14,7 @@ namespace phpseclib3\Crypt\DSA;
use phpseclib3\Crypt\Common;
use phpseclib3\Crypt\DSA;
use phpseclib3\Crypt\DSA\Formats\Signature\ASN1 as ASN1Signature;
use phpseclib3\Exception\BadConfigurationException;
/**
* DSA Public Key
@@ -34,15 +35,25 @@ final class PublicKey extends DSA implements Common\PublicKey
*/
public function verify($message, $signature)
{
if (self::$forcedEngine === 'libsodium') {
throw new BadConfigurationException('Engine libsodium is forced but unsupported for DSA');
}
if (self::$forcedEngine === 'OpenSSL' && !function_exists('openssl_get_md_methods')) {
throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for DSA');
}
$format = $this->sigFormat;
$params = $format::load($signature);
if ($params === false || count($params) != 2) {
return false;
}
extract($params);
$r = $params['r'];
$s = $params['s'];
if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
if (function_exists('openssl_get_md_methods') && self::$forcedEngine !== 'PHP') {
if (in_array($this->hash->getHash(), openssl_get_md_methods())) {
$sig = $format != 'ASN1' ? ASN1Signature::save($r, $s) : $signature;
$result = openssl_verify($message, $sig, $this->toString('PKCS8'), $this->hash->getHash());
@@ -50,6 +61,9 @@ final class PublicKey extends DSA implements Common\PublicKey
if ($result != -1) {
return (bool) $result;
}
} elseif (self::$forcedEngine === 'OpenSSL') {
throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for DSA / ' . $this->hash->getHash());
}
}
$q_1 = $this->q->subtract(self::$one);
+229 -81
View File
@@ -33,12 +33,15 @@ use phpseclib3\Crypt\Common\AsymmetricKey;
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
use phpseclib3\Crypt\EC\Curves\Curve25519;
use phpseclib3\Crypt\EC\Curves\Curve448;
use phpseclib3\Crypt\EC\Curves\Ed25519;
use phpseclib3\Crypt\EC\Curves\Ed448;
use phpseclib3\Crypt\EC\Formats\Keys\PKCS1;
use phpseclib3\Crypt\EC\Formats\Keys\PKCS8;
use phpseclib3\Crypt\EC\Parameters;
use phpseclib3\Crypt\EC\PrivateKey;
use phpseclib3\Crypt\EC\PublicKey;
use phpseclib3\Exception\BadConfigurationException;
use phpseclib3\Exception\UnsupportedAlgorithmException;
use phpseclib3\Exception\UnsupportedCurveException;
use phpseclib3\Exception\UnsupportedOperationException;
@@ -74,13 +77,6 @@ abstract class EC extends AsymmetricKey
*/
protected $curve;
/**
* Signature Format
*
* @var string
*/
protected $format;
/**
* Signature Format (Short)
*
@@ -130,6 +126,14 @@ abstract class EC extends AsymmetricKey
*/
protected $sigFormat;
/**
* Forced Engine
*
* @var ?string
* @see parent::forceEngine()
*/
protected static $forcedEngine = null;
/**
* Create public / private key pair.
*
@@ -145,31 +149,7 @@ abstract class EC extends AsymmetricKey
throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')');
}
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
$curve = strtolower($curve);
if (self::$engines['libsodium'] && $curve == 'ed25519' && function_exists('sodium_crypto_sign_keypair')) {
$kp = sodium_crypto_sign_keypair();
$privatekey = EC::loadFormat('libsodium', sodium_crypto_sign_secretkey($kp));
//$publickey = EC::loadFormat('libsodium', sodium_crypto_sign_publickey($kp));
$privatekey->curveName = 'Ed25519';
//$publickey->curveName = $curve;
return $privatekey;
}
$privatekey = new PrivateKey();
$curveName = $curve;
if (preg_match('#(?:^curve|^ed)\d+$#', $curveName)) {
$curveName = ucfirst($curveName);
} elseif (substr($curveName, 0, 10) == 'brainpoolp') {
$curveName = 'brainpoolP' . substr($curveName, 10);
}
$curveName = self::getCurveCase($curve);
$curve = '\phpseclib3\Crypt\EC\Curves\\' . $curveName;
if (!class_exists($curve)) {
@@ -180,6 +160,54 @@ abstract class EC extends AsymmetricKey
$curveName = $reflect->isFinal() ?
$reflect->getParentClass()->getShortName() :
$reflect->getShortName();
$curveEngineName = self::getOpenSSLCurveName(strtolower($curveName));
switch ($curveName) {
case 'Ed25519':
$providers = [
'libsodium' => function_exists('sodium_crypto_sign_keypair'),
// OPENSSL_KEYTYPE_ED25519 introduced in PHP 8.4.0
'OpenSSL' => defined('OPENSSL_KEYTYPE_ED25519'),
];
break;
case 'Ed448':
// OPENSSL_KEYTYPE_ED448 introduced in PHP 8.4.0
$providers = ['OpenSSL' => defined('OPENSSL_KEYTYPE_ED448')];
break;
case 'Curve25519':
$providers = [
'libsodium' => function_exists('sodium_crypto_box_publickey_from_secretkey'),
// OPENSSL_KEYTYPE_X25519 introduced in PHP 8.4.0
'OpenSSL' => defined('OPENSSL_KEYTYPE_X25519'),
];
break;
// OPENSSL_KEYTYPE_X448 introduced in PHP 8.4.0
case 'Curve448':
$providers = ['OpenSSL' => defined('OPENSSL_KEYTYPE_X448')];
break;
default:
// openssl_get_curve_names() was introduced in PHP 7.1.0
// exclude curve25519 and curve448 from testing
$providers = ['OpenSSL' => function_exists('openssl_get_curve_names') && substr($curveEngineName, 0, 5) != 'curve' && in_array($curveEngineName, openssl_get_curve_names())];
};
foreach ($providers as $engine => $isSupported) {
// if an engine is being forced and the forced engine doesn't match $engine, skip it
if (isset(self::$forcedEngine) && self::$forcedEngine !== $engine) {
continue;
}
if ($isSupported) {
$result = self::generateWithEngine($engine, $curveEngineName);
if (isset($result)) {
return $result;
}
}
if (self::$forcedEngine === $engine) {
throw new BadConfigurationException("EC::createKey: Engine $engine is forced but unsupported for $curve");
}
}
$privatekey = new PrivateKey();
$curve = new $curve();
if ($curve instanceof TwistedEdwardsCurve) {
@@ -189,21 +217,14 @@ abstract class EC extends AsymmetricKey
} else {
$privatekey->dA = $dA = $curve->createRandomMultiplier();
}
if ($curve instanceof Curve25519 && self::$engines['libsodium']) {
//$r = pack('H*', '0900000000000000000000000000000000000000000000000000000000000000');
//$QA = sodium_crypto_scalarmult($dA->toBytes(), $r);
$QA = sodium_crypto_box_publickey_from_secretkey($dA->toBytes());
$privatekey->QA = [$curve->convertInteger(new BigInteger(strrev($QA), 256))];
} else {
$privatekey->QA = $curve->multiplyPoint($curve->getBasePoint(), $dA);
}
$privatekey->curve = $curve;
$privatekey->curveName = $curveName;
$privatekey->QA = $curve->multiplyPoint($curve->getBasePoint(), $dA);
//$publickey = clone $privatekey;
//unset($publickey->dA);
//unset($publickey->x);
$privatekey->curveName = $curveName;
//$publickey->curveName = $curveName;
if ($privatekey->curve instanceof TwistedEdwardsCurve) {
@@ -213,6 +234,130 @@ abstract class EC extends AsymmetricKey
return $privatekey;
}
/**
* Returns the actual case of the curve
*
* Useful for initializing the curve class
*
* @param string $curveName
* @return string
*/
private static function getCurveCase($curveName)
{
$curveName = strtolower($curveName);
if (preg_match('#(?:^curve|^ed)\d+$#', $curveName)) {
return ucfirst($curveName);
}
if (substr($curveName, 0, 10) == 'brainpoolp') {
return 'brainpoolP' . substr($curveName, 10);
}
return $curveName;
}
/**
* Return the OpenSSL name for a curve
*
* @param string $curve
* @return string
*/
private static function getOpenSSLCurveName($curve)
{
switch ($curve) {
case 'secp256r1':
return 'prime256v1';
case 'secp192r1':
return 'prime192v1';
}
return $curve;
}
/**
* Generate the key for a given curve / engine combo
*
* @param string $engine
* @param string $curve
* @return ?PrivateKey
*/
private static function generateWithEngine($engine, $curve)
{
if ($engine == 'libsodium') {
if ($curve == 'ed25519') {
$kp = sodium_crypto_sign_keypair();
$privatekey = EC::loadFormat('libsodium', sodium_crypto_sign_secretkey($kp));
//$publickey = EC::loadFormat('libsodium', sodium_crypto_sign_publickey($kp));
$privatekey->curveName = 'Ed25519';
//$publickey->curveName = $curve;
return $privatekey;
} else { // $curve == 'curve25519
$privatekey = new PrivateKey();
$privatekey->curve = new Curve25519();
$privatekey->curveName = 'Curve25519';
$privatekey->dA = $privatekey->curve->createRandomMultiplier();
$dA = str_pad($privatekey->dA->toBytes(), 32, "\0", STR_PAD_LEFT);
//$r = pack('H*', '0900000000000000000000000000000000000000000000000000000000000000');
//$QA = sodium_crypto_scalarmult($dA, $r);
$QA = sodium_crypto_box_publickey_from_secretkey($dA);
$privatekey->QA = [$privatekey->curve->convertInteger(new BigInteger(strrev($QA), 256))];
return $privatekey;
}
}
// at this point $engine == 'OpenSSL'
$curveName = self::getCurveCase($curve);
$config = [];
if (self::$configFile) {
$config['config'] = self::$configFile;
}
$params = $config;
switch ($curve) {
case 'ed25519':
$params['private_key_type'] = OPENSSL_KEYTYPE_ED25519;
break;
case 'ed448':
$params['private_key_type'] = OPENSSL_KEYTYPE_ED448;
break;
case 'curve25519':
$params['private_key_type'] = OPENSSL_KEYTYPE_X25519;
break;
case 'curve448':
$params['private_key_type'] = OPENSSL_KEYTYPE_X448;
break;
default:
$params['private_key_type'] = OPENSSL_KEYTYPE_EC;
$params['curve_name'] = $curveName;
}
$key = openssl_pkey_new($params);
if (!$key) {
return null;
}
$privateKeyStr = '';
if (!openssl_pkey_export($key, $privateKeyStr, null, $config)) {
return null;
}
// clear the buffer of error strings
while (openssl_error_string() !== false) {
}
// some versions of OpenSSL / PHP return PKCS1 keys, others return PKCS8 keys
$privatekey = EC::load($privateKeyStr);
switch ($curveName) {
case 'prime256v1':
$privatekey->curveName = 'secp256r1';
break;
case 'prime192v1':
$privatekey->curveName = 'secp192r1';
break;
default:
$privatekey->curveName = $curveName;
}
return $privatekey;
}
/**
* OnLoad Handler
*
@@ -220,10 +365,6 @@ abstract class EC extends AsymmetricKey
*/
protected static function onLoad(array $components)
{
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
if (!isset($components['dA']) && !isset($components['QA'])) {
$new = new Parameters();
$new->curve = $components['curve'];
@@ -320,27 +461,6 @@ abstract class EC extends AsymmetricKey
return $this->curve->getLength();
}
/**
* Returns the current engine being used
*
* @see self::useInternalEngine()
* @see self::useBestEngine()
* @return string
*/
public function getEngine()
{
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
if ($this->curve instanceof TwistedEdwardsCurve) {
return $this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context) ?
'libsodium' : 'PHP';
}
return self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods()) ?
'OpenSSL' : 'PHP';
}
/**
* Returns the public key coordinates as a string
*
@@ -359,6 +479,48 @@ abstract class EC extends AsymmetricKey
return "\4" . $this->QA[0]->toBytes(true) . $this->QA[1]->toBytes(true);
}
/**
* Convert point to public key
*
* For Weierstrass curves, if only the x coordinate is present (as is the case after doing a round of ECDH)
* then we'll guess at the y coordinate. There are only two possible y values and, atleast in-so-far as
* multiplication is concerned, neither value affects the resultant x value
*
* If $toPublicKey is set to false then a string will be returned - a kind of public key precursor
*
* @param string $curveName
* @param string $secret
* @param bool $toPublicKey optional
* @return PublicKey|string
*/
public static function convertPointToPublicKey($curveName, $secret, $toPublicKey = true)
{
$curveName = self::getCurveCase($curveName);
$curve = '\phpseclib3\Crypt\EC\Curves\\' . $curveName;
if (!class_exists($curve)) {
throw new UnsupportedCurveException('Named Curve of ' . $curveName . ' is not supported');
}
$curve = new $curve();
if (!$curve instanceof TwistedEdwardsCurve) {
if ($curve instanceof MontgomeryCurve) {
$secret = strrev($secret);
} elseif ($curve->getLengthInBytes() == strlen($secret)) {
$secret = "\3$secret";
}
if (!$toPublicKey) {
return $secret;
}
$secret = "\0$secret";
} elseif (!$toPublicKey) {
return $secret;
}
$QA = PKCS8::extractPoint($secret, $curve);
$key = PKCS8::savePublicKey($curve, $QA);
return EC::loadFormat('PKCS8', $key);
}
/**
* Returns the parameters
*
@@ -380,7 +542,7 @@ abstract class EC extends AsymmetricKey
/**
* Determines the signature padding mode
*
* Valid values are: ASN1, SSH2, Raw
* Valid values are: ASN1, IEEE, SSH2, Raw
*
* @param string $format
*/
@@ -463,18 +625,4 @@ abstract class EC extends AsymmetricKey
return parent::withHash($hash);
}
/**
* __toString() magic method
*
* @return string
*/
public function __toString()
{
if ($this->curve instanceof MontgomeryCurve) {
return '';
}
return parent::__toString();
}
}
@@ -223,6 +223,9 @@ class Montgomery extends Base
$p2 = $this->convertToInternal($p);
$x = $p[0];
$r = $this->randomInteger();
$p2 = [$p2[0]->multiply($r), $p2[1]->multiply($r)];
$b = $d->toBits();
$b = str_pad($b, 256, '0', STR_PAD_LEFT);
for ($i = 0; $i < strlen($b); $i++) {
@@ -246,7 +249,7 @@ class Montgomery extends Base
*
* x=X/Z
*
* @return \phpseclib3\Math\PrimeField\Integer[]
* @return PrimeInteger[]
*/
public function convertToInternal(array $p)
{
@@ -266,7 +269,7 @@ class Montgomery extends Base
/**
* Returns the affine point
*
* @return \phpseclib3\Math\PrimeField\Integer[]
* @return PrimeInteger[]
*/
public function convertToAffine(array $p)
{
@@ -274,6 +277,9 @@ class Montgomery extends Base
return $p;
}
list($x, $z) = $p;
if ($z->equals($this->zero)) {
return [clone $this->zero];
}
return [$x->divide($z)];
}
}
@@ -751,7 +751,7 @@ class Prime extends Base
* To convert a Jacobian Coordinate to an Affine Point
* you do (x / z^2, y / z^3)
*
* @return \phpseclib3\Math\PrimeField\Integer[]
* @return PrimeInteger[]
*/
public function convertToAffine(array $p)
{
@@ -770,7 +770,7 @@ class Prime extends Base
/**
* Converts an affine point to a jacobian coordinate
*
* @return \phpseclib3\Math\PrimeField\Integer[]
* @return PrimeInteger[]
*/
public function convertToInternal(array $p)
{
@@ -171,7 +171,7 @@ class TwistedEdwards extends Base
/**
* Returns the affine point
*
* @return PrimeField\Integer[]
* @return PrimeInteger[]
*/
public function convertToAffine(array $p)
{
@@ -18,6 +18,8 @@ use phpseclib3\Math\BigInteger;
class Curve25519 extends Montgomery
{
const SIZE = 32;
public function __construct()
{
// 2^255 - 19
@@ -47,10 +49,12 @@ class Curve25519 extends Montgomery
*/
public function multiplyPoint(array $p, BigInteger $d)
{
//$r = strrev(sodium_crypto_scalarmult($d->toBytes(), strrev($p[0]->toBytes())));
$d = $d->toBytes();
$d = str_pad($d, 32, "\0", STR_PAD_LEFT);
//$r = strrev(sodium_crypto_scalarmult($d, strrev($p[0]->toBytes())));
//return [$this->factory->newInteger(new BigInteger($r, 256))];
$d = $d->toBytes();
$d &= "\xF8" . str_repeat("\xFF", 30) . "\x7F";
$d = strrev($d);
$d |= "\x40";
@@ -18,6 +18,8 @@ use phpseclib3\Math\BigInteger;
class Curve448 extends Montgomery
{
const SIZE = 56;
public function __construct()
{
// 2^448 - 2^224 - 1
@@ -58,10 +60,12 @@ class Curve448 extends Montgomery
*/
public function multiplyPoint(array $p, BigInteger $d)
{
//$r = strrev(sodium_crypto_scalarmult($d->toBytes(), strrev($p[0]->toBytes())));
$d = $d->toBytes();
$d = str_pad($d, 56, "\0", STR_PAD_LEFT);
//$r = strrev(sodium_crypto_scalarmult($d, strrev($p[0]->toBytes())));
//return [$this->factory->newInteger(new BigInteger($r, 256))];
$d = $d->toBytes();
$d[0] = $d[0] & "\xFC";
$d = strrev($d);
$d |= "\x80";
@@ -14,10 +14,14 @@
namespace phpseclib3\Crypt\EC\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\EC;
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
use phpseclib3\Crypt\EC\BaseCurves\Binary as BinaryCurve;
use phpseclib3\Crypt\EC\BaseCurves\Montgomery;
use phpseclib3\Crypt\EC\BaseCurves\Prime as PrimeCurve;
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
use phpseclib3\Crypt\EC\Curves\Curve25519;
use phpseclib3\Exception\BadConfigurationException;
use phpseclib3\Exception\UnsupportedCurveException;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps;
@@ -274,6 +278,9 @@ trait Common
*/
public static function extractPoint($str, BaseCurve $curve)
{
if ($curve instanceof Montgomery) {
return [new BigInteger($str, 256)];
}
if ($curve instanceof TwistedEdwardsCurve) {
// first step of point deciding as discussed at the following URL's:
// https://tools.ietf.org/html/rfc8032#section-5.1.3
@@ -524,6 +531,54 @@ trait Common
throw new UnsupportedCurveException('Curve cannot be serialized');
}
private static function deriveMontgomeryPublicKey(array $components)
{
$curve = $components['curve'];
$dA = $components['dA'];
$forcedEngine = EC::getForcedEngine();
$useLibsodium = !isset($forcedEngine) && $curve instanceof Curve25519 && function_exists('sodium_crypto_box_publickey_from_secretkey');
if ($forcedEngine === 'libsodium') {
$useLibsodium = true;
if (!$curve instanceof Curve25519) {
throw new \RuntimeException('Engine libsodium is forced but is not supported for Curve448');
}
if (!function_exists('sodium_crypto_box_publickey_from_secretkey')) {
throw new BadConfigurationException('Engine libsodium is forced but not available');
}
}
if ($useLibsodium) {
//$r = pack('H*', '0900000000000000000000000000000000000000000000000000000000000000');
//$QA = sodium_crypto_scalarmult($dA->toBytes(), $r);
$QA = sodium_crypto_box_publickey_from_secretkey(str_pad($dA->toBytes(), 32, chr(0), STR_PAD_LEFT));
return [$components['curve']->convertInteger(new BigInteger(strrev($QA), 256))];
}
$useOpenSSL = !isset($forcedEngine) && function_exists('openssl_pkey_get_private');
if ($forcedEngine == 'OpenSSL') {
$useOpenSSL = true;
if (!function_exists('openssl_pkey_get_private')) {
throw new BadConfigurationException('Engine OpenSSL is forced but is not available');
}
}
if ($useOpenSSL) {
$pem = PKCS8::savePrivateKey($dA, $curve, []);
$res = openssl_pkey_get_private($pem);
if ($res !== false && ($details = openssl_pkey_get_details($res)) !== false) {
$index = $curve instanceof Curve25519 ? 'x25519' : 'x448';
return isset($details[$index]['pub_key']) ?
[$curve->convertInteger(new BigInteger(strrev($details[$index]['pub_key']), 256))] :
PKCS8::load($details['key'])['QA'];
} elseif ($forcedEngine == 'OpenSSL') {
throw new BadConfigurationException('Engine OpenSSL is forced but was unable to derive the public key because of ' . openssl_error_string());
}
}
return [$components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA'])[0]];
}
/**
* Use Specified Curve
*
@@ -33,6 +33,8 @@ use phpseclib3\Math\BigInteger;
*/
abstract class MontgomeryPrivate
{
use Common;
/**
* Is invisible flag
*
@@ -59,11 +61,11 @@ abstract class MontgomeryPrivate
throw new \LengthException('The only supported lengths are 32 and 56');
}
$components = ['curve' => $curve];
$components['dA'] = new BigInteger($key, 256);
$curve->rangeCheck($components['dA']);
// note that EC::getEncodedCoordinates does some additional "magic" (it does strrev on the result)
$components['QA'] = $components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
$components = [
'curve' => $curve,
'dA' => new BigInteger($key, 256)
];
$components['QA'] = self::deriveMontgomeryPublicKey($components);
return $components;
}
@@ -96,6 +98,6 @@ abstract class MontgomeryPrivate
throw new UnsupportedFormatException('MontgomeryPrivate private keys do not support encryption');
}
return $privateKey->toBytes();
return str_pad($privateKey->toBytes(), $curve::SIZE, "\0", STR_PAD_RIGHT);
}
}
@@ -111,6 +111,9 @@ abstract class OpenSSH extends Progenitor
$reflect = new \ReflectionClass($curve);
$name = $reflect->getShortName();
if (!isset(self::$curveOIDs[$name])) {
throw new UnsupportedCurveException($name . ' is not a curve that the OpenSSH plugin supports');
}
$oid = self::$curveOIDs[$name];
$aliases = array_filter(self::$curveOIDs, function ($v) use ($oid) {
return $v == $oid;
@@ -132,8 +132,8 @@ abstract class PKCS1 extends Progenitor
$components = [];
$components['curve'] = self::loadCurveByParam($key['parameters']);
$components['dA'] = new BigInteger($key['privateKey'], 256);
$components['QA'] = isset($ecPrivate['publicKey']) ?
self::extractPoint($ecPrivate['publicKey'], $components['curve']) :
$components['QA'] = isset($key['publicKey']) ?
self::extractPoint($key['publicKey'], $components['curve']) :
$components['curve']->multiplyPoint($components['curve']->getBasePoint(), $components['dA']);
return $components;
@@ -27,12 +27,14 @@ use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
use phpseclib3\Crypt\EC\BaseCurves\Base as BaseCurve;
use phpseclib3\Crypt\EC\BaseCurves\Montgomery as MontgomeryCurve;
use phpseclib3\Crypt\EC\BaseCurves\TwistedEdwards as TwistedEdwardsCurve;
use phpseclib3\Crypt\EC\Curves\Curve25519;
use phpseclib3\Crypt\EC\Curves\Curve448;
use phpseclib3\Crypt\EC\Curves\Ed25519;
use phpseclib3\Crypt\EC\Curves\Ed448;
use phpseclib3\Exception\UnsupportedCurveException;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps;
use phpseclib3\Math\BigInteger;
use phpseclib3\Math\Common\FiniteField\Integer;
/**
* PKCS#8 Formatted EC Key Handler
@@ -48,14 +50,14 @@ abstract class PKCS8 extends Progenitor
*
* @var array
*/
const OID_NAME = ['id-ecPublicKey', 'id-Ed25519', 'id-Ed448'];
const OID_NAME = ['id-ecPublicKey', 'id-Ed25519', 'id-Ed448', 'id-X25519', 'id-X448'];
/**
* OID Value
*
* @var string
*/
const OID_VALUE = ['1.2.840.10045.2.1', '1.3.101.112', '1.3.101.113'];
const OID_VALUE = ['1.2.840.10045.2.1', '1.3.101.112', '1.3.101.113', '1.3.101.110', '1.3.101.111'];
/**
* Break a public or private key down into its constituent components
@@ -81,6 +83,9 @@ abstract class PKCS8 extends Progenitor
case 'id-Ed25519':
case 'id-Ed448':
return self::loadEdDSA($key);
case 'id-X25519':
case 'id-X448':
return self::loadECDH($key);
}
$decoded = ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element);
@@ -158,20 +163,57 @@ abstract class PKCS8 extends Progenitor
return $components;
}
private static function loadECDH(array $key)
{
$components = [];
if (isset($key['privateKey'])) {
$components['curve'] = $key['privateKeyAlgorithm']['algorithm'] == 'id-X25519' ? new Curve25519() : new Curve448();
$expected = chr(ASN1::TYPE_OCTET_STRING) . ASN1::encodeLength($components['curve']::SIZE);
$privateKey = (string) $key['privateKey'];
if (substr($privateKey, 0, 2) != $expected) {
throw new \RuntimeException(
'The first two bytes of the ' .
$key['privateKeyAlgorithm']['algorithm'] .
' private key field should be 0x' . bin2hex($expected)
);
}
$components['dA'] = new BigInteger(substr($privateKey, 2), 256);
}
if (isset($key['publicKey'])) {
if (!isset($components['curve'])) {
$components['curve'] = $key['publicKeyAlgorithm']['algorithm'] == 'id-X25519' ? new Curve25519() : new Curve448();
}
$components['QA'] = [$components['curve']->convertInteger(new BigInteger(strrev($key['publicKey']), 256))];
}
if (isset($key['privateKey']) && !isset($components['QA'])) {
$components['QA'] = self::deriveMontgomeryPublicKey($components);
}
return $components;
}
/**
* Convert an EC public key to the appropriate format
*
* @param BaseCurve $curve
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
* @param Integer[] $publicKey
* @param array $options optional
* @return string
*/
public static function savePublicKey(BaseCurve $curve, array $publicKey, array $options = [])
{
self::initialize_static_variables();
if ($curve instanceof MontgomeryCurve) {
throw new UnsupportedCurveException('Montgomery Curves are not supported');
return self::wrapPublicKey(
str_pad(strrev($publicKey[0]->toBytes()), $curve::SIZE, "\0", STR_PAD_RIGHT),
null,
$curve instanceof Curve25519 ? 'id-X25519' : 'id-X448',
$options
);
}
if ($curve instanceof TwistedEdwardsCurve) {
@@ -195,7 +237,7 @@ abstract class PKCS8 extends Progenitor
*
* @param BigInteger $privateKey
* @param BaseCurve $curve
* @param \phpseclib3\Math\Common\FiniteField\Integer[] $publicKey
* @param Integer[] $publicKey
* @param string $secret optional
* @param string $password optional
* @param array $options optional
@@ -206,7 +248,13 @@ abstract class PKCS8 extends Progenitor
self::initialize_static_variables();
if ($curve instanceof MontgomeryCurve) {
throw new UnsupportedCurveException('Montgomery Curves are not supported');
return self::wrapPrivateKey(
chr(ASN1::TYPE_OCTET_STRING) . ASN1::encodeLength($curve::SIZE) . str_pad($privateKey->toBytes(), $curve::SIZE, "\0", STR_PAD_LEFT),
[],
null,
$password,
$curve instanceof Curve25519 ? 'id-X25519' : 'id-X448'
);
}
if ($curve instanceof TwistedEdwardsCurve) {
@@ -71,6 +71,10 @@ abstract class XML
$use_errors = libxml_use_internal_errors(true);
if (substr($key, 0, 5) != '<?xml') {
$key = '<xml>' . $key . '</xml>';
}
$temp = self::isolateNamespace($key, 'http://www.w3.org/2009/xmldsig11#');
if ($temp) {
$key = $temp;
@@ -82,9 +86,6 @@ abstract class XML
}
$dom = new \DOMDocument();
if (substr($key, 0, 5) != '<?xml') {
$key = '<xml>' . $key . '</xml>';
}
if (!$dom->loadXML($key)) {
libxml_use_internal_errors($use_errors);
@@ -21,6 +21,7 @@ use phpseclib3\Crypt\EC\Curves\Ed25519;
use phpseclib3\Crypt\EC\Formats\Keys\PKCS1;
use phpseclib3\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature;
use phpseclib3\Crypt\Hash;
use phpseclib3\Exception\BadConfigurationException;
use phpseclib3\Exception\UnsupportedOperationException;
use phpseclib3\Math\BigInteger;
@@ -59,18 +60,40 @@ final class PrivateKey extends EC implements Common\PrivateKey
*/
public function multiply($coordinates)
{
if ($this->curve instanceof MontgomeryCurve) {
if ($this->curve instanceof Curve25519 && self::$engines['libsodium']) {
return sodium_crypto_scalarmult($this->dA->toBytes(), $coordinates);
if (self::$forcedEngine === 'OpenSSL') {
throw new BadConfigurationException('Engine OpenSSL is not supported for the multiplication operation');
}
if (self::$forcedEngine === 'libsodium' && !$this->curve instanceof Curve25519) {
throw new BadConfigurationException('Engine libsodium is only supported for Curve25519');
}
if ($this->curve instanceof Curve25519 && self::$forcedEngine !== 'PHP') {
if (self::$forcedEngine === 'libsodium' && !function_exists('sodium_crypto_scalarmult')) {
throw new BadConfigurationException('Engine libsodium is forced but unsupported for Curve25519');
}
if (function_exists('sodium_crypto_scalarmult')) {
$dA = str_pad($this->dA->toBytes(), 32, "\0", STR_PAD_LEFT);
try {
return sodium_crypto_scalarmult($dA, $coordinates);
} catch (\SodiumException $e) {
if (self::$forcedEngine == 'libsodium') {
throw new BadConfigurationException('Engine libsodium is forced but was unable to perform multiplication because of ' . $e->getMessage());
}
}
}
}
if ($this->curve instanceof MontgomeryCurve) {
$point = [$this->curve->convertInteger(new BigInteger(strrev($coordinates), 256))];
$point = $this->curve->multiplyPoint($point, $this->dA);
return strrev($point[0]->toBytes(true));
}
if (!$this->curve instanceof TwistedEdwardsCurve) {
$coordinates = "\0$coordinates";
}
$point = PKCS1::extractPoint($coordinates, $this->curve);
$point = $this->curve->multiplyPoint($point, $this->dA);
if ($this->curve instanceof TwistedEdwardsCurve) {
@@ -104,11 +127,56 @@ final class PrivateKey extends EC implements Common\PrivateKey
return false;
}
if ($this->curve instanceof TwistedEdwardsCurve) {
if ($this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context)) {
if (self::$forcedEngine === 'libsodium' && !$this->curve instanceof Ed25519) {
throw new BadConfigurationException('Engine libsodium is only supported for Ed25519');
}
// at this point either self::$forcedEngine is NOT libsodium or the curve is Ed25519
if ($this->curve instanceof Ed25519 && self::$forcedEngine !== 'PHP' && self::$forcedEngine !== 'OpenSSL') {
if (self::$forcedEngine === 'libsodium') {
if (!function_exists('sodium_crypto_sign_detached')) {
throw new BadConfigurationException('Engine libsodium is forced but unsupported for Ed25519 / Ed448');
}
if (isset($this->context)) {
throw new BadConfigurationException('Engine libsodium is forced but unsupported for Ed25519ctx (context)');
}
}
if (function_exists('sodium_crypto_sign_detached') && !isset($this->context)) {
$result = sodium_crypto_sign_detached($message, $this->withPassword()->toString('libsodium'));
return $shortFormat == 'SSH2' ? Strings::packSSH2('ss', 'ssh-' . strtolower($this->getCurve()), $result) : $result;
}
}
// at this point self::$forcedEngine CAN'T be libsodium so we won't check for it henceforth
if ($this->curve instanceof TwistedEdwardsCurve) {
if (self::$forcedEngine !== 'PHP') {
$keyTypeConstant = $this->curve instanceof Ed25519 ? 'OPENSSL_KEYTYPE_ED25519' : 'OPENSSL_KEYTYPE_ED448';
if (self::$forcedEngine === 'OpenSSL') {
if (!defined($keyTypeConstant)) {
throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for Ed25519 / Ed448');
}
// OpenSSL supports Ed25519/Ed448 but not Ed25519ctx (context), so skip if context is set
if (isset($this->context)) {
throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for Ed25519 / Ed448 curves with context\'s');
}
}
if (defined($keyTypeConstant) && !isset($this->context)) {
$result = '';
// algorithm 0 is used because EdDSA has a built-in hash
openssl_sign($message, $result, $this->withPassword()->toString('PKCS8'), 0);
if ($result) {
$signature = $shortFormat == 'SSH2'
? Strings::packSSH2('ss', 'ssh-' . strtolower($this->getCurve()), $result)
: $result;
return $signature;
}
if (self::$forcedEngine === 'OpenSSL') {
throw new BadConfigurationException('Engine OpenSSL is forced but was unable to create signature because of ' . openssl_error_string());
}
}
}
// contexts (Ed25519ctx) are supported but prehashing (Ed25519ph) is not.
// quoting https://tools.ietf.org/html/rfc8032#section-8.5 ,
@@ -143,7 +211,14 @@ final class PrivateKey extends EC implements Common\PrivateKey
return $shortFormat == 'SSH2' ? Strings::packSSH2('ss', 'ssh-' . strtolower($this->getCurve()), $R . $S) : $R . $S;
}
if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
if (self::$forcedEngine === 'OpenSSL' && !function_exists('openssl_get_md_methods')) {
throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for ECDSA');
}
// at this point $forcedEngine is either PHP or null. either that OR openssl_get_md_methods() exists
if (self::$forcedEngine !== 'PHP') {
if (in_array($this->hash->getHash(), openssl_get_md_methods())) {
$signature = '';
// altho PHP's OpenSSL bindings only supported EC key creation in PHP 7.1 they've long
// supported signing / verification
@@ -157,9 +232,16 @@ final class PrivateKey extends EC implements Common\PrivateKey
return $signature;
}
extract(ASN1Signature::load($signature));
$loaded = ASN1Signature::load($signature);
$r = $loaded['r'];
$s = $loaded['s'];
return $this->formatSignature($r, $s);
} elseif (self::$forcedEngine === 'OpenSSL') {
throw new BadConfigurationException('Engine OpenSSL is forced but was unable to create signature because of ' . openssl_error_string());
}
} elseif (self::$forcedEngine === 'OpenSSL') {
throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for ECDSA / ' . $this->hash->getHash());
}
}
@@ -20,6 +20,7 @@ use phpseclib3\Crypt\EC\Curves\Ed25519;
use phpseclib3\Crypt\EC\Formats\Keys\PKCS1;
use phpseclib3\Crypt\EC\Formats\Signature\ASN1 as ASN1Signature;
use phpseclib3\Crypt\Hash;
use phpseclib3\Exception\BadConfigurationException;
use phpseclib3\Exception\UnsupportedOperationException;
use phpseclib3\Math\BigInteger;
@@ -52,16 +53,61 @@ final class PublicKey extends EC implements Common\PublicKey
return false;
}
$order = $this->curve->getOrder();
if (self::$forcedEngine === 'libsodium' && !$this->curve instanceof Ed25519) {
throw new BadConfigurationException('Engine libsodium is only supported for Ed25519');
}
// at this point either self::$forcedEngine is NOT libsodium or the curve is Ed25519
if ($this->curve instanceof Ed25519 && self::$forcedEngine !== 'PHP' && self::$forcedEngine !== 'OpenSSL') {
if (self::$forcedEngine === 'libsodium') {
if (!function_exists('sodium_crypto_sign_verify_detached')) {
throw new BadConfigurationException('Engine libsodium is forced but unsupported for Ed25519 / Ed448');
}
if (isset($this->context)) {
throw new BadConfigurationException('Engine libsodium is forced but unsupported for Ed25519ctx (context)');
}
}
if (function_exists('sodium_crypto_sign_verify_detached') && !isset($this->context)) {
if ($shortFormat == 'SSH2') {
list(, $signature) = Strings::unpackSSH2('ss', $signature);
}
return sodium_crypto_sign_verify_detached($signature, $message, $this->toString('libsodium'));
}
}
// at this point self::$forcedEngine CAN'T be libsodium so we won't check for it henceforth
if ($this->curve instanceof TwistedEdwardsCurve) {
if ($shortFormat == 'SSH2') {
list(, $signature) = Strings::unpackSSH2('ss', $signature);
}
if ($this->curve instanceof Ed25519 && self::$engines['libsodium'] && !isset($this->context)) {
return sodium_crypto_sign_verify_detached($signature, $message, $this->toString('libsodium'));
if (self::$forcedEngine !== 'PHP') {
$keyTypeConstant = $this->curve instanceof Ed25519 ? 'OPENSSL_KEYTYPE_ED25519' : 'OPENSSL_KEYTYPE_ED448';
if (self::$forcedEngine === 'OpenSSL') {
if (!defined($keyTypeConstant)) {
throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for Ed25519 / Ed448');
}
// OpenSSL supports Ed25519/Ed448 but not Ed25519ctx (context), so skip if context is set
if (isset($this->context)) {
throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for Ed25519 / Ed448 curves with context\'s');
}
}
if (defined($keyTypeConstant) && !isset($this->context)) {
// algorithm 0 is used because EdDSA has a built-in hash
$result = openssl_verify($message, $signature, $this->toString('PKCS8'), 0) === 1;
if ($result !== -1 && $result !== false) {
return (bool) $result;
}
if (self::$forcedEngine === 'OpenSSL') {
throw new BadConfigurationException('Engine OpenSSL is forced but was unable to create signature because of ' . openssl_error_string());
}
}
}
$order = $this->curve->getOrder();
$curve = $this->curve;
if (strlen($signature) != 2 * $curve::SIZE) {
@@ -115,17 +161,33 @@ final class PublicKey extends EC implements Common\PublicKey
if ($params === false || count($params) != 2) {
return false;
}
extract($params);
$r = $params['r'];
$s = $params['s'];
if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
if (self::$forcedEngine === 'OpenSSL' && !function_exists('openssl_get_md_methods')) {
throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for ECDSA');
}
// at this point $forcedEngine is either PHP or null. either that OR openssl_get_md_methods() exists
if (self::$forcedEngine !== 'PHP') {
if (in_array($this->hash->getHash(), openssl_get_md_methods())) {
$sig = $format != 'ASN1' ? ASN1Signature::save($r, $s) : $signature;
$result = openssl_verify($message, $sig, $this->toString('PKCS8', ['namedCurve' => false]), $this->hash->getHash());
if ($result != -1) {
if ($result !== -1 && $result !== false) {
return (bool) $result;
}
if (self::$forcedEngine === 'OpenSSL') {
throw new BadConfigurationException('Engine OpenSSL is forced but was unable to verify signature because of ' . openssl_error_string());
}
} elseif (self::$forcedEngine === 'OpenSSL') {
throw new BadConfigurationException('Engine OpenSSL is forced but unsupported for ECDSA / ' . $this->hash->getHash());
}
}
$order = $this->curve->getOrder();
$n_1 = $order->subtract(self::$one);
if (!$r->between(self::$one, $n_1) || !$s->between(self::$one, $n_1)) {
+107 -7
View File
@@ -194,6 +194,15 @@ class Hash
private static $maxwordrange128;
/**#@-*/
/**#@+
* AES_CMAC variables
*
* @var string
*/
private $k1;
private $k2;
/**#@-*/
/**
* Default Constructor.
*
@@ -286,7 +295,7 @@ class Hash
public function setHash($hash)
{
$oldHash = $this->hashParam;
$this->hashParam = $hash = strtolower($hash);
$this->hashParam = $hash = strtolower(str_replace('/', '-', $hash));
switch ($hash) {
case 'umac-32':
case 'umac-64':
@@ -299,6 +308,14 @@ class Hash
$this->length = abs(substr($hash, -3)) >> 3;
$this->algo = 'umac';
return;
case 'aes_cmac':
if ($oldHash != $this->hashParam) {
$this->recomputeAESKey = true;
}
$this->blockSize = 128;
$this->length = 16;
$this->algo = 'aes_cmac';
return;
case 'md2-96':
case 'md5-96':
case 'sha1-96':
@@ -306,8 +323,8 @@ class Hash
case 'sha256-96':
case 'sha384-96':
case 'sha512-96':
case 'sha512/224-96':
case 'sha512/256-96':
case 'sha512-224-96':
case 'sha512-256-96':
$hash = substr($hash, 0, -3);
$this->length = 12; // 96 / 8 = 12
break;
@@ -319,7 +336,7 @@ class Hash
$this->length = 20;
break;
case 'sha224':
case 'sha512/224':
case 'sha512-224':
case 'sha3-224':
$this->length = 28;
break;
@@ -327,7 +344,7 @@ class Hash
$this->paddingType = self::PADDING_KECCAK;
// fall-through
case 'sha256':
case 'sha512/256':
case 'sha512-256':
case 'sha3-256':
$this->length = 32;
break;
@@ -407,12 +424,12 @@ class Hash
}
}
if ($hash == 'sha512/224' || $hash == 'sha512/256') {
if ($hash == 'sha512-224' || $hash == 'sha512-256') {
// PHP 7.1.0 introduced sha512/224 and sha512/256 support:
// http://php.net/ChangeLog-7.php#7.1.0
if (version_compare(PHP_VERSION, '7.1.0') < 0) {
// from http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf#page=24
$initial = $hash == 'sha512/256' ?
$initial = $hash == 'sha512-256' ?
[
'22312194FC2BF72C', '9F555FA3C84C64C2', '2393B86B6F53B151', '963877195940EABD',
'96283EE2A88EFFE3', 'BE5E1E2553863992', '2B0199FC2C85B8AA', '0EB72DDC81C52CA2'
@@ -442,6 +459,15 @@ class Hash
$this->opad = str_repeat(chr(0x5C), $b);
}
// PHP's built in hash function does sha3-256 but sha512/256 so we'll update those accordingly
switch ($hash) {
case 'sha512-224':
$hash = 'sha512/224';
break;
case 'sha512-256':
$hash = 'sha512/256';
}
$this->algo = $hash;
$this->computeKey();
@@ -977,6 +1003,69 @@ class Hash
public function hash($text)
{
$algo = $this->algo;
// https://www.rfc-editor.org/rfc/rfc4493.html
// https://en.wikipedia.org/wiki/One-key_MAC
if ($algo == 'aes_cmac') {
$constZero = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
if ($this->recomputeAESKey) {
if (!is_string($this->key)) {
throw new InsufficientSetupException('No key has been set');
}
if (strlen($this->key) != 16) {
throw new \LengthException('Key must be 16 bytes long');
}
// Algorithm Generate_Subkey
$constRb = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x87";
$this->c = new AES('ecb');
$this->c->setKey($this->key);
$this->c->disablePadding();
$l = $this->c->encrypt($constZero);
$msb = ($l & "\x80") == "\x80";
$l = new BigInteger($l, 256);
$l->setPrecision(128);
$l = $l->bitwise_leftShift(1)->toBytes();
// make it constant time
$k1 = $msb ? $l ^ $constRb : $l | $constZero;
$msb = ($k1 & "\x80") == "\x80";
$k2 = new BigInteger($k1, 256);
$k2->setPrecision(128);
$k2 = $k2->bitwise_leftShift(1)->toBytes();
// make it constant time
$k2 = $msb ? $k2 ^ $constRb : $k2 | $constZero;
$this->k1 = $k1;
$this->k2 = $k2;
}
$len = strlen($text);
$const_Bsize = 16;
$M = strlen($text) ? str_split($text, $const_Bsize) : [''];
// Step 2
$n = ceil($len / $const_Bsize);
// Step 3
if ($n == 0) {
$n = 1;
$flag = false;
} else {
$flag = $len % $const_Bsize == 0;
}
// Step 4
$M_last = $flag ?
$M[$n - 1] ^ $k1 :
self::OMAC_padding($M[$n - 1], $const_Bsize) ^ $k2;
// Step 5
$x = $constZero;
// Step 6
$c = &$this->c;
for ($i = 0; $i < $n - 1; $i++) {
$y = $x ^ $M[$i];
$x = $c->encrypt($y);
}
$y = $M_last ^ $x;
return $c->encrypt($y);
}
if ($algo == 'umac') {
if ($this->recomputeAESKey) {
if (!is_string($this->nonce)) {
@@ -1790,6 +1879,17 @@ class Hash
return pack('J*', ...$hash);
}
/**
* OMAC Padding
*
* @link https://www.rfc-editor.org/rfc/rfc4493.html#section-2.4
*/
private static function OMAC_padding($m, $length)
{
$count = $length - strlen($m) - 1;
return "$m\x80" . str_repeat("\0", $count);
}
/**
* __toString() magic method
*/
@@ -32,6 +32,7 @@ abstract class PublicKeyLoader
* @return AsymmetricKey
* @param string|array $key
* @param string $password optional
* @throws NoKeyLoadedException if key is not valid
*/
public static function load($key, $password = false)
{
+14 -14
View File
@@ -45,7 +45,7 @@ class RC2 extends BlockCipher
/**
* Block Length of the cipher
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::block_size
* @see Common\SymmetricKey::block_size
* @var int
*/
protected $block_size = 8;
@@ -53,7 +53,7 @@ class RC2 extends BlockCipher
/**
* The Key
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::key
* @see Common\SymmetricKey::key
* @see self::setKey()
* @var string
*/
@@ -62,7 +62,7 @@ class RC2 extends BlockCipher
/**
* The Original (unpadded) Key
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::key
* @see Common\SymmetricKey::key
* @see self::setKey()
* @see self::encrypt()
* @see self::decrypt()
@@ -73,7 +73,7 @@ class RC2 extends BlockCipher
/**
* Key Length (in bytes)
*
* @see \phpseclib3\Crypt\RC2::setKeyLength()
* @see RC2::setKeyLength()
* @var int
*/
protected $key_length = 16; // = 128 bits
@@ -81,7 +81,7 @@ class RC2 extends BlockCipher
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt
* @see Common\SymmetricKey::cipher_name_mcrypt
* @var string
*/
protected $cipher_name_mcrypt = 'rc2';
@@ -89,7 +89,7 @@ class RC2 extends BlockCipher
/**
* Optimizing value while CFB-encrypting
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len
* @see Common\SymmetricKey::cfb_init_len
* @var int
*/
protected $cfb_init_len = 500;
@@ -261,7 +261,7 @@ class RC2 extends BlockCipher
*
* This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* @see Common\SymmetricKey::__construct()
* @param int $engine
* @return bool
*/
@@ -323,7 +323,7 @@ class RC2 extends BlockCipher
* has more then 128 bytes in it, and set $key to a single null byte if
* it is empty.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::setKey()
* @see Common\SymmetricKey::setKey()
* @param string $key
* @param int|boolean $t1 optional Effective key length in bits.
* @throws \LengthException if the key length isn't supported
@@ -426,8 +426,8 @@ class RC2 extends BlockCipher
/**
* Encrypts a block
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::encryptBlock()
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see Common\SymmetricKey::encryptBlock()
* @see Common\SymmetricKey::encrypt()
* @param string $in
* @return string
*/
@@ -470,8 +470,8 @@ class RC2 extends BlockCipher
/**
* Decrypts a block
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::decryptBlock()
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* @see Common\SymmetricKey::decryptBlock()
* @see Common\SymmetricKey::decrypt()
* @param string $in
* @return string
*/
@@ -514,7 +514,7 @@ class RC2 extends BlockCipher
/**
* Creates the key schedule
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::setupKey()
* @see Common\SymmetricKey::setupKey()
*/
protected function setupKey()
{
@@ -534,7 +534,7 @@ class RC2 extends BlockCipher
/**
* Setup the performance-optimized function for de/encrypt()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::setupInlineCrypt()
* @see Common\SymmetricKey::setupInlineCrypt()
*/
protected function setupInlineCrypt()
{
+8 -8
View File
@@ -52,19 +52,19 @@ use phpseclib3\Crypt\Common\StreamCipher;
class RC4 extends StreamCipher
{
/**
* @see \phpseclib3\Crypt\RC4::_crypt()
* @see RC4::_crypt()
*/
const ENCRYPT = 0;
/**
* @see \phpseclib3\Crypt\RC4::_crypt()
* @see RC4::_crypt()
*/
const DECRYPT = 1;
/**
* Key Length (in bytes)
*
* @see \phpseclib3\Crypt\RC4::setKeyLength()
* @see RC4::setKeyLength()
* @var int
*/
protected $key_length = 128; // = 1024 bits
@@ -72,7 +72,7 @@ class RC4 extends StreamCipher
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt
* @see Common\SymmetricKey::cipher_name_mcrypt
* @var string
*/
protected $cipher_name_mcrypt = 'arcfour';
@@ -98,7 +98,7 @@ class RC4 extends StreamCipher
*
* This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* @see Common\SymmetricKey::__construct()
* @param int $engine
* @return bool
*/
@@ -159,7 +159,7 @@ class RC4 extends StreamCipher
/**
* Encrypts a message.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* @see Common\SymmetricKey::decrypt()
* @see self::crypt()
* @param string $plaintext
* @return string $ciphertext
@@ -178,7 +178,7 @@ class RC4 extends StreamCipher
* $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
* At least if the continuous buffer is disabled.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see Common\SymmetricKey::encrypt()
* @see self::crypt()
* @param string $ciphertext
* @return string $plaintext
@@ -214,7 +214,7 @@ class RC4 extends StreamCipher
/**
* Setup the key (expansion)
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::_setupKey()
* @see Common\SymmetricKey::_setupKey()
*/
protected function setupKey()
{
+309 -66
View File
@@ -57,6 +57,7 @@ use phpseclib3\Crypt\Common\AsymmetricKey;
use phpseclib3\Crypt\RSA\Formats\Keys\PSS;
use phpseclib3\Crypt\RSA\PrivateKey;
use phpseclib3\Crypt\RSA\PublicKey;
use phpseclib3\Exception\BadConfigurationException;
use phpseclib3\Exception\InconsistentSetupException;
use phpseclib3\Exception\UnsupportedAlgorithmException;
use phpseclib3\Math\BigInteger;
@@ -228,12 +229,11 @@ abstract class RSA extends AsymmetricKey
protected static $enableBlinding = true;
/**
* OpenSSL configuration file name.
* Enable automatic salt length determination
*
* @see self::createKey()
* @var ?string
* @var bool
*/
protected static $configFile;
protected static $autoSaltLength = true;
/**
* Smallest Prime
@@ -256,6 +256,14 @@ abstract class RSA extends AsymmetricKey
*/
protected $publicExponent;
/**
* Forced Engine
*
* @var ?string
* @see parent::forceEngine()
*/
protected static $forcedEngine = null;
/**
* Sets the public exponent for key generation
*
@@ -280,18 +288,6 @@ abstract class RSA extends AsymmetricKey
self::$smallestPrime = $val;
}
/**
* Sets the OpenSSL config file path
*
* Set to the empty string to use the default config file
*
* @param string $val
*/
public static function setOpenSSLConfigPath($val)
{
self::$configFile = $val;
}
/**
* Create a private key
*
@@ -309,6 +305,10 @@ abstract class RSA extends AsymmetricKey
throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')');
}
if (self::$forcedEngine == 'libsodium' || (self::$forcedEngine == 'OpenSSL' && !function_exists('openssl_pkey_new'))) {
throw new BadConfigurationException('Engine ' . self::$forcedEngine . ' is forced but unsupported for RSA');
}
$regSize = $bits >> 1; // divide by two to see how many bits P and Q would be
if ($regSize > self::$smallestPrime) {
$num_primes = floor($bits / self::$smallestPrime);
@@ -318,19 +318,20 @@ abstract class RSA extends AsymmetricKey
}
if ($num_primes == 2 && $bits >= 384 && self::$defaultExponent == 65537) {
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
// OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
if (self::$engines['OpenSSL']) {
// at this point the only two supported values for self::$forcedEngine are OpenSSL, PHP and null
// if it's either OpenSSL or null we'll use OpenSSL (if it's available)
if (self::$forcedEngine !== 'PHP' && function_exists('openssl_pkey_new')) {
$config = [];
if (self::$configFile) {
$config['config'] = self::$configFile;
}
// OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
$rsa = openssl_pkey_new(['private_key_bits' => $bits] + $config);
openssl_pkey_export($rsa, $privatekeystr, null, $config);
if (!$rsa || !openssl_pkey_export($rsa, $privatekeystr, null, $config)) {
if (isset(self::$forcedEngine)) {
throw new BadConfigurationException('Engine OpenSSL is forced but produced an error - ' . openssl_error_string());
}
} else {
// clear the buffer of error strings stemming from a minimalistic openssl.cnf
// https://github.com/php/php-src/issues/11054 talks about other errors this'll pick up
while (openssl_error_string() !== false) {
@@ -339,12 +340,14 @@ abstract class RSA extends AsymmetricKey
return RSA::load($privatekeystr);
}
}
}
static $e;
if (!isset($e)) {
$e = new BigInteger(self::$defaultExponent);
}
do {
$n = clone self::$one;
$exponents = $coefficients = $primes = [];
$lcm = [
@@ -352,15 +355,13 @@ abstract class RSA extends AsymmetricKey
'bottom' => false
];
do {
for ($i = 1; $i <= $num_primes; $i++) {
if ($i != $num_primes) {
$primes[$i] = BigInteger::randomPrime($regSize);
} else {
extract(BigInteger::minMaxBits($bits));
/** @var BigInteger $min
* @var BigInteger $max
*/
$minMax = BigInteger::minMaxBits($bits);
$min = $minMax['min'];
$max = $minMax['max'];
list($min) = $min->divide($n);
$min = $min->add(self::$one);
list($max) = $max->divide($n);
@@ -479,18 +480,6 @@ abstract class RSA extends AsymmetricKey
return $key;
}
/**
* Initialize static variables
*/
protected static function initialize_static_variables()
{
if (!isset(self::$configFile)) {
self::$configFile = dirname(__FILE__) . '/../openssl.cnf';
}
parent::initialize_static_variables();
}
/**
* Constructor
*
@@ -582,6 +571,22 @@ abstract class RSA extends AsymmetricKey
break;
case 'sha512/256':
$t = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06\x05\x00\x04\x20";
break;
// the following 3x algorithms are not specified in PKCS1 v2.2, however, some standards none-the-less do use them:
// https://sk-eid.github.io/smart-id-documentation/rp-api/changes.html#_security_enhancements
// the OIDs are from this URL:
// https://csrc.nist.gov/projects/computer-security-objects-register/algorithm-registration#Hash
case 'sha3/224':
$t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x07\x05\x00\x04\x1c";
break;
case 'sha3/256':
$t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x08\x05\x00\x04\x20";
break;
case 'sha3/384':
$t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x09\x05\x00\x04\x30";
break;
case 'sha3/512':
$t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x0A\x05\x00\x04\x40";
}
$t .= $h;
$tLen = strlen($t);
@@ -639,6 +644,22 @@ abstract class RSA extends AsymmetricKey
case 'sha512/256':
$t = "\x30\x2f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06\x04\x20";
break;
// the following 3x algorithms are not specified in PKCS1 v2.2, however, some standards none-the-less do use them:
// https://sk-eid.github.io/smart-id-documentation/rp-api/changes.html#_security_enhancements
// the OIDs are from this URL:
// https://csrc.nist.gov/projects/computer-security-objects-register/algorithm-registration#Hash
case 'sha3/224':
$t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x07\x04\x1c";
break;
case 'sha3/256':
$t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x08\x04\x20";
break;
case 'sha3/384':
$t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x09\x04\x30";
break;
case 'sha3/512':
$t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x0A\x04\x40";
break;
default:
throw new UnsupportedAlgorithmException('md2 and md5 require NULLs');
}
@@ -714,11 +735,15 @@ abstract class RSA extends AsymmetricKey
case 'sha224':
case 'sha512/224':
case 'sha512/256':
case 'sha3/224':
case 'sha3/256':
case 'sha3/384':
case 'sha3/512':
$new->hash = new Hash($hash);
break;
default:
throw new UnsupportedAlgorithmException(
'The only supported hash algorithms are: md2, md5, sha1, sha256, sha384, sha512, sha224, sha512/224, sha512/256'
"The only supported hash algorithms are: md2, md5, sha1, sha256, sha384, sha512, sha224, sha512/224, sha512/256 - $hash provided"
);
}
$new->hLen = $new->hash->getLengthInBytes();
@@ -749,11 +774,15 @@ abstract class RSA extends AsymmetricKey
case 'sha224':
case 'sha512/224':
case 'sha512/256':
case 'sha3/224':
case 'sha3/256':
case 'sha3/384':
case 'sha3/512':
$new->mgfHash = new Hash($hash);
break;
default:
throw new UnsupportedAlgorithmException(
'The only supported hash algorithms are: md2, md5, sha1, sha256, sha384, sha512, sha224, sha512/224, sha512/256'
"The only supported hash algorithms are: md2, md5, sha1, sha256, sha384, sha512, sha224, sha512/224, sha512/256 - $hash provided"
);
}
$new->mgfHLen = $new->mgfHash->getLengthInBytes();
@@ -763,7 +792,6 @@ abstract class RSA extends AsymmetricKey
/**
* Returns the MGF hash algorithm currently being used
*
*/
public function getMGFHash()
{
@@ -892,28 +920,6 @@ abstract class RSA extends AsymmetricKey
return $this->signaturePadding | $this->encryptionPadding;
}
/**
* Returns the current engine being used
*
* OpenSSL is only used in this class (and it's subclasses) for key generation
* Even then it depends on the parameters you're using. It's not used for
* multi-prime RSA nor is it used if the key length is outside of the range
* supported by OpenSSL
*
* @see self::useInternalEngine()
* @see self::useBestEngine()
* @return string
*/
public function getEngine()
{
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
return self::$engines['OpenSSL'] && self::$defaultExponent == 65537 ?
'OpenSSL' :
'PHP';
}
/**
* Enable RSA Blinding
*
@@ -931,4 +937,241 @@ abstract class RSA extends AsymmetricKey
{
static::$enableBlinding = false;
}
public static function enableSaltLengthDiscovery()
{
static::$autoSaltLength = true;
}
public static function disableSaltLengthDiscovery()
{
static::$autoSaltLength = false;
}
/**
* Handles OpenSSL encryption / decryption / signature creation / verification
*
* @param string $func
* @param string $message
* @param ?string $signature
* @return bool|string|null
*/
protected function handleOpenSSL($func, $message, $signature = null)
{
switch ($func) {
case 'openssl_verify':
case 'openssl_sign':
$paddingType = 'signaturePadding';
break;
case 'openssl_public_encrypt':
case 'openssl_private_decrypt':
$paddingType = 'encryptionPadding';
}
if (self::$forcedEngine === 'libsodium') {
throw new BadConfigurationException('Engine libsodium is not supported for RSA');
}
if ((isset(self::$forcedEngine) && self::$forcedEngine !== 'PHP') && $this->$paddingType === self::SIGNATURE_RELAXED_PKCS1) {
throw new BadConfigurationException('Only the PHP engine can be used with relaxed PKCS1 padding');
}
if (self::$forcedEngine !== 'PHP') {
if (self::$forcedEngine === 'OpenSSL' && !function_exists($func)) {
throw new BadConfigurationException('Engine OpenSSL is forced but unavailable for RSA');
}
if ($this->$paddingType === self::SIGNATURE_PSS) {
$create = $func === 'openssl_sign';
switch (true) {
case !defined('OPENSSL_PKCS1_PSS_PADDING'):
$error = 'Engine OpenSSL is forced but PSS encryption requires PHP >= 8.5.0';
break;
case $this->hash->getHash() !== $this->mgfHash->getHash():
$error = 'Engine OpenSSL is forced but can\'t be used because the Hash and MGF Hash do not match';
break;
case !$create && !static::$autoSaltLength:
$error = 'Engine OpenSSL is forced but auto calculation of the salt length is disabled';
break;
case $create && $this->getSaltLength() !== $this->hLen:
$error = 'Engine OpenSSL is forced but can\'t be used because the salt length doesn\'t match the hash length';
break;
case $create && $this->getLength() < 8 * (2 * $this->getSaltLength() + 2):
$error = 'Engine OpenSSL is forced but can\'t be used for PSS signing because the key is too small for OpenSSL to use the configured salt length';
break;
case $create && OPENSSL_VERSION_NUMBER < 0x30100000:
$error = 'Engine OpenSSL is forced but can\'t be used for PSS signing because OpenSSL < 3.1.0 defaults to the maximum salt length instead of the hash length';
break;
}
}
/*
https://datatracker.ietf.org/doc/html/rfc4055#page-6 says the following:
There are two possible encodings for the AlgorithmIdentifier
parameters field associated with these object identifiers. The two
alternatives arise from the loss of the OPTIONAL associated with the
algorithm identifier parameters when the 1988 syntax for
AlgorithmIdentifier was translated into the 1997 syntax. Later the
OPTIONAL was recovered via a defect report, but by then many people
thought that algorithm parameters were mandatory. Because of this
history some implementations encode parameters as a NULL element
while others omit them entirely. The correct encoding is to omit the
parameters field; however, when RSASSA-PSS and RSAES-OAEP were
defined, it was done using the NULL parameters rather than absent
parameters.
All implementations MUST accept both NULL and absent parameters as
legal and equivalent encodings.
OpenSSL does NOT accept both - it REQUIRES NULL be present. phpseclib, however,
DOES accept both. at first, it didn't. at first, not knowing why some small number
of PKCS1 signatures omitted NULL, i added the SIGNATURE_RELAXED_PKCS1 mode on
2015-08-26. https://phpseclib.com/docs/rsa#rsasignature_relaxed_pkcs1 talks more
about that mode. later, on 2021-04-05, there was CVE-2021-30130. consequently,
the SIGNATURE_PKCS1 mode was updated to accept either NULL or non-NULL.
because phpseclib accepts PKCS1 signatures that OpenSSL doesn't, OpenSSL isn't
used for PKCS1. if the OpenSSL extension is installed then it'll be used to perform
unpadded RSA (ie. modular exponentiation), however, the actual PKCS1 construction
takes place in PHP code vs OpenSSL.
see https://security.stackexchange.com/questions/110330/encoding-of-optional-null-in-der
for an additional reference
*/
if ($this->$paddingType === self::SIGNATURE_PKCS1 && $func === 'openssl_verify') {
$error = 'Engine OpenSSL is forced but can\'t be used with PKCS1 signature verification because OpenSSL requires NULL be present whereas phpseclib doesn\'t';
}
if ($this->$paddingType === self::ENCRYPTION_OAEP) {
switch (true) {
case $this->hash->getHash() !== $this->mgfHash->getHash():
$error = 'Engine OpenSSL is forced but can\'t be used because the Hash and MGF Hash do not match';
break;
case $this->hash->getHash() !== 'sha1' && PHP_VERSION_ID < 80500:
$error = 'Engine OpenSSL is forced but non-sha1 hashes are only supported on PHP 8.5.0+';
break;
case strlen($this->label):
$error = 'Engine OpenSSL is forced but can\'t be used because the label is not the empty string';
}
}
if (isset($error)) {
if (self::$forcedEngine === 'OpenSSL') {
throw new BadConfigurationException($error);
}
} elseif ($paddingType === 'signaturePadding') {
switch (true) {
case $this->signaturePadding === self::SIGNATURE_PSS && defined('OPENSSL_PKCS1_PSS_PADDING'):
case $this->signaturePadding !== self::SIGNATURE_PSS && function_exists($func):
$key = $this instanceof PrivateKey ?
$this->withPassword()->toString('PKCS8') :
$this->toString('PKCS8');
if ($func === 'openssl_sign' && strpos($key, 'PUBLIC') !== false) {
if (self::$forcedEngine === 'OpenSSL') {
throw new BadConfigurationException('Engine OpenSSL is forced but cannot be used because the private key does not have the prime components within it');
}
break;
}
$hash = $this->hash->getHash();
// on github actions, php 7.0 and 7.1 on windows emit the following warning:
// openssl_sign(): supplied key param cannot be coerced into a private key
set_error_handler(function ($errno, $errstr) {
throw new BadConfigurationException("Engine OpenSSL is forced but got error: $errstr");
});
try {
$result = $this->signaturePadding === self::SIGNATURE_PSS ?
$func($message, $signature, $key, $hash, OPENSSL_PKCS1_PSS_PADDING) :
$func($message, $signature, $key, $hash);
} catch (BadConfigurationException $e) {
if (self::$forcedEngine === 'OpenSSL') {
throw $e;
}
$result = false;
} finally {
restore_error_handler();
}
if ($func === 'openssl_verify') {
if ($result === -1 || $result === false) {
throw new BadConfigurationException('Engine OpenSSL is forced but was unable to verify signature because of ' . openssl_error_string());
}
return (bool) $result;
}
if ($result) {
return $signature;
}
if (self::$forcedEngine === 'OpenSSL') {
throw new BadConfigurationException('Engine OpenSSL is forced but was unable to create signature because of ' . openssl_error_string());
}
}
} else {
if ($this->encryptionPadding !== self::ENCRYPTION_OAEP || PHP_VERSION_ID >= 80500) {
$key = $this instanceof PrivateKey ?
$this->withPassword()->toString('PKCS8') :
$this->toString('PKCS8');
if ($func === 'openssl_private_decrypt' && strpos($key, 'PUBLIC') !== false) {
if ($this->encryptionPadding === self::ENCRYPTION_OAEP) {
if (self::$forcedEngine === 'OpenSSL') {
throw new BadConfigurationException('Engine OpenSSL is forced but cannot be used because openssl_public_decrypt() doesn\'t have a hash parameter like openssl_private_decrypt() does');
}
return null;
}
$func = 'openssl_public_decrypt';
}
if ($this->encryptionPadding === self::ENCRYPTION_PKCS1 && OPENSSL_VERSION_NUMBER >= 0x30200000) {
// quoting https://docs.openssl.org/3.4/man3/RSA_public_encrypt/#return-values :
//
// "Since version 3.2.0, the default provider in OpenSSL does not return an error when padding checks fail.
// Instead it generates a random message"
//
// the idea is that even a perfect implementation of PKCS1 padding can be used to conduct a Bleichenbacher
// padding oracle attack.
//
// so like if $rsa->decrypt() doesn't throw an exception it's liable to run additional code that'll take
// longer to run than it'd take if an exception would be thrown and in theory, with PKCS1, in particular,
// you can use that fact to guess at successive bits of the private key until you've figured it out. it's
// why you should use OAEP padding vs PKCS1 padding BUT if you need PKCS1 padding for interoperability then
// you're stuck with it.
//
// with the OpenSSL 3.2.0+ behavior they're making it harder to do the attack by making it harder to use PKCS1
// in the real world. this isn't a design philosophy i agree with. like phpseclib lets you DES encryption. you
// shouldn't use DES encryption but if you need to you need to and phpseclib isn't here to judge. that's a big
// difference between phpseclib and stuff like libsodium.
if (self::$forcedEngine === 'OpenSSL') {
throw new BadConfigurationException('Engine OpenSSL is forced but cannot be used to decrypt PKCS1 encrypted strings with OpenSSL 3.2.0+');
}
return null;
}
$hash = $this->hash->getHash();
$output = '';
switch ($this->encryptionPadding) {
case self::ENCRYPTION_NONE:
case self::ENCRYPTION_PKCS1:
$padding = $this->encryptionPadding === self::ENCRYPTION_NONE ? OPENSSL_NO_PADDING : OPENSSL_PKCS1_PADDING;
// on github actions, php 7.0 and 7.1 on windows emit the following warning:
// openssl_private_decrypt(): key parameter is not a valid private key
set_error_handler(function ($errno, $errstr) {
throw new BadConfigurationException("Engine OpenSSL is forced but got error: $errstr");
});
try {
$result = $func($message, $output, $key, $padding);
} catch (BadConfigurationException $e) {
if (self::$forcedEngine === 'OpenSSL') {
throw $e;
}
$result = false;
} finally {
restore_error_handler();
}
break;
//case self::ENCRYPTION_OAEP:
default:
$result = $func($message, $output, $key, OPENSSL_PKCS1_OAEP_PADDING, $hash);
}
if ($result) {
return $output;
}
}
}
return null;
}
}
}
@@ -88,13 +88,11 @@ abstract class MSBLOB
// PUBLICKEYSTRUC publickeystruc
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa387453(v=vs.85).aspx
extract(unpack('atype/aversion/vreserved/Valgo', Strings::shift($key, 8)));
/**
* @var string $type
* @var string $version
* @var integer $reserved
* @var integer $algo
*/
$unpacked = unpack('atype/aversion/vreserved/Valgo', Strings::shift($key, 8));
$type = $unpacked['type'];
$version = $unpacked['version'];
$reserved = $unpacked['reserved'];
$algo = $unpacked['algo'];
switch (ord($type)) {
case self::PUBLICKEYBLOB:
case self::PUBLICKEYBLOBEX:
@@ -121,12 +119,10 @@ abstract class MSBLOB
// RSAPUBKEY rsapubkey
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa387685(v=vs.85).aspx
// could do V for pubexp but that's unsigned 32-bit whereas some PHP installs only do signed 32-bit
extract(unpack('Vmagic/Vbitlen/a4pubexp', Strings::shift($key, 12)));
/**
* @var integer $magic
* @var integer $bitlen
* @var string $pubexp
*/
$unpacked = unpack('Vmagic/Vbitlen/a4pubexp', Strings::shift($key, 12));
$magic = $unpacked['magic'];
$bitlen = $unpacked['bitlen'];
$pubexp = $unpacked['pubexp'];
switch ($magic) {
case self::RSA2:
$components['isPublicKey'] = false;
@@ -65,6 +65,9 @@ abstract class PKCS1 extends Progenitor
$key = ASN1::asn1map($decoded[0], Maps\RSAPrivateKey::MAP);
if (is_array($key)) {
if ($key['version'] === false) {
throw new \UnexpectedValueException('Version number is not valid');
}
$components += [
'modulus' => $key['modulus'],
'publicExponent' => $key['publicExponent'],
@@ -96,6 +99,20 @@ abstract class PKCS1 extends Progenitor
$components['isPublicKey'] = true;
}
$components = $components + $key;
foreach ($components as &$val) {
if ($val instanceof BigInteger) {
$val = self::makePositive($val);
}
if (is_array($val)) {
foreach ($val as &$subval) {
if ($subval instanceof BigInteger) {
$subval = self::makePositive($subval);
}
}
}
}
return $components + $key;
}
@@ -157,4 +174,17 @@ abstract class PKCS1 extends Progenitor
return self::wrapPublicKey($key, 'RSA');
}
/**
* Negative numbers make no sense in RSA so convert them to positive
*
* @param BigInteger $x
* @return string
*/
private static function makePositive(BigInteger $x)
{
return $x->isNegative() ?
new BigInteger($x->toBytes(true), 256) :
$x;
}
}
@@ -141,7 +141,7 @@ abstract class PSS extends Progenitor
$result['hash'] = str_replace('id-', '', $params['hashAlgorithm']['algorithm']);
$result['MGFHash'] = str_replace('id-', '', $params['maskGenAlgorithm']['parameters']['algorithm']);
if (isset($params['saltLength'])) {
$result['saltLength'] = (int) $params['saltLength']->toString();
$result['saltLength'] = (int) "$params[saltLength]";
}
if (isset($key['meta'])) {
@@ -56,7 +56,10 @@ abstract class PuTTY extends Progenitor
if (!isset($components['private'])) {
return $components;
}
extract($components);
$type = $components['type'];
$comment = $components['comment'];
$public = $components['public'];
$private = $components['private'];
unset($components['public'], $components['private']);
$isPublicKey = false;
@@ -216,7 +216,7 @@ final class PrivateKey extends RSA implements Common\PrivateKey
$db = $ps . chr(1) . $salt;
$dbMask = $this->mgf1($h, $emLen - $this->hLen - 1); // ie. stlren($db)
$maskedDB = $db ^ $dbMask;
$maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
$maskedDB[0] = ~chr(256 - (1 << ($emBits & 7))) & $maskedDB[0];
$em = $maskedDB . $h . chr(0xBC);
return $em;
@@ -288,6 +288,11 @@ final class PrivateKey extends RSA implements Common\PrivateKey
*/
public function sign($message)
{
$result = $this->handleOpenSSL('openssl_sign', $message);
if ($result !== null) {
return $result;
}
switch ($this->signaturePadding) {
case self::SIGNATURE_PKCS1:
case self::SIGNATURE_RELAXED_PKCS1:
@@ -434,6 +439,11 @@ final class PrivateKey extends RSA implements Common\PrivateKey
*/
public function decrypt($ciphertext)
{
$result = $this->handleOpenSSL('openssl_private_decrypt', $ciphertext);
if ($result !== null) {
return $result;
}
switch ($this->encryptionPadding) {
case self::ENCRYPTION_NONE:
return $this->raw_encrypt($ciphertext);
@@ -116,7 +116,7 @@ final class PublicKey extends RSA implements Common\PublicKey
}
// Compare
return $r1 || $r2;
return boolval($r1 | $r2);
}
/**
@@ -228,7 +228,11 @@ final class PublicKey extends RSA implements Common\PublicKey
// be output.
$emLen = ($emBits + 7) >> 3; // ie. ceil($emBits / 8);
if (static::$autoSaltLength) {
$sLen = 0;
} else {
$sLen = $this->sLen !== null ? $this->sLen : $this->hLen;
}
$mHash = $this->hash->hash($m);
if ($emLen < $this->hLen + $sLen + 2) {
@@ -241,18 +245,24 @@ final class PublicKey extends RSA implements Common\PublicKey
$maskedDB = substr($em, 0, -$this->hLen - 1);
$h = substr($em, -$this->hLen - 1, $this->hLen);
$temp = chr(0xFF << ($emBits & 7));
$temp = chr(256 - (1 << ($emBits & 7)));
if ((~$maskedDB[0] & $temp) != $temp) {
return false;
}
$dbMask = $this->mgf1($h, $emLen - $this->hLen - 1);
$db = $maskedDB ^ $dbMask;
$db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
$temp = $emLen - $this->hLen - $sLen - 2;
if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
$db[0] = ~chr(256 - (1 << ($emBits & 7))) & $db[0];
// PS is a run of zero bytes terminated by a single 0x01
$psLen = strspn($db, "\0");
if ($psLen == strlen($db) || $db[$psLen] != chr(0x01)) {
return false;
}
$salt = substr($db, $temp + 1); // should be $sLen long
if (!static::$autoSaltLength && $psLen != $emLen - $this->hLen - $sLen - 2) {
return false;
}
$salt = substr($db, $psLen + 1); // should be $sLen long
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
$h2 = $this->hash->hash($m2);
return hash_equals($h, $h2);
@@ -301,6 +311,11 @@ final class PublicKey extends RSA implements Common\PublicKey
*/
public function verify($message, $signature)
{
$result = $this->handleOpenSSL('openssl_verify', $message, $signature);
if ($result !== null) {
return $result;
}
switch ($this->signaturePadding) {
case self::SIGNATURE_RELAXED_PKCS1:
return $this->rsassa_pkcs1_v1_5_relaxed_verify($message, $signature);
@@ -450,6 +465,11 @@ final class PublicKey extends RSA implements Common\PublicKey
*/
public function encrypt($plaintext)
{
$result = $this->handleOpenSSL('openssl_public_encrypt', $plaintext);
if ($result !== null) {
return $result;
}
switch ($this->encryptionPadding) {
case self::ENCRYPTION_NONE:
return $this->raw_encrypt($plaintext);
+29 -19
View File
@@ -74,8 +74,8 @@ class Rijndael extends BlockCipher
* or not for the current $block_size/$key_length.
* In case of, $cipher_name_mcrypt will be set dynamically at run time accordingly.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt
* @see \phpseclib3\Crypt\Common\SymmetricKey::engine
* @see Common\SymmetricKey::cipher_name_mcrypt
* @see Common\SymmetricKey::engine
* @see self::isValidEngine()
* @var string
*/
@@ -262,7 +262,7 @@ class Rijndael extends BlockCipher
*
* This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* Common\SymmetricKey::__construct()
* @param int $engine
* @return bool
*/
@@ -378,7 +378,7 @@ class Rijndael extends BlockCipher
$k = $c[2];
$l = $c[3];
while ($i < $Nb) {
$temp[$i] = ($state[$i] & intval(0xFF000000)) ^
$temp[$i] = ($state[$i] & (PHP_INT_SIZE == 8 ? 0xFF000000 : -16777216)) ^
($state[$j] & 0x00FF0000) ^
($state[$k] & 0x0000FF00) ^
($state[$l] & 0x000000FF) ^
@@ -452,7 +452,7 @@ class Rijndael extends BlockCipher
$l = $Nb - $c[3];
while ($i < $Nb) {
$word = ($state[$i] & intval(0xFF000000)) |
$word = ($state[$i] & (PHP_INT_SIZE == 8 ? 0xFF000000 : -16777216)) |
($state[$j] & 0x00FF0000) |
($state[$k] & 0x0000FF00) |
($state[$l] & 0x000000FF);
@@ -511,7 +511,7 @@ class Rijndael extends BlockCipher
/**
* Setup the key (expansion)
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::setupKey()
* Common\SymmetricKey::setupKey()
*/
protected function setupKey()
{
@@ -528,7 +528,7 @@ class Rijndael extends BlockCipher
0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000
];
$rcon = array_map('intval', $rcon);
$rcon = array_map([self::class, 'safe_intval'], $rcon);
}
if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_length === $this->kl['key_length'] && $this->block_size === $this->kl['block_size']) {
@@ -568,7 +568,9 @@ class Rijndael extends BlockCipher
// on a 32-bit machine, it's 32-bits, and on a 64-bit machine, it's 64-bits. on a 32-bit machine,
// 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and'
// with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is.
$temp = (($temp << 8) & intval(0xFFFFFF00)) | (($temp >> 24) & 0x000000FF); // rotWord
$temp = PHP_INT_SIZE == 8 ? // rotWord
(($temp << 8) & 0xFFFFFF00) | (($temp >> 24) & 0x000000FF) :
($temp << 8) | (($temp >> 24) & 0x000000FF);
$temp = $this->subWord($temp) ^ $rcon[$i / $this->Nk];
} elseif ($this->Nk > 6 && $i % $this->Nk == 4) {
$temp = $this->subWord($temp);
@@ -659,7 +661,7 @@ class Rijndael extends BlockCipher
// according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19> (section 5.2.1),
// precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so
// those are the names we'll use.
$t3 = array_map('intval', [
$t3 = array_map([self::class, 'safe_intval'], [
// with array_map('intval', ...) we ensure we have only int's and not
// some slower floats converted by php automatically on high values
0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
@@ -697,9 +699,9 @@ class Rijndael extends BlockCipher
]);
foreach ($t3 as $t3i) {
$t0[] = (($t3i << 24) & intval(0xFF000000)) | (($t3i >> 8) & 0x00FFFFFF);
$t1[] = (($t3i << 16) & intval(0xFFFF0000)) | (($t3i >> 16) & 0x0000FFFF);
$t2[] = (($t3i << 8) & intval(0xFFFFFF00)) | (($t3i >> 24) & 0x000000FF);
$t0[] = (($t3i << 24) & self::safe_intval(0xFF000000)) | (($t3i >> 8) & 0x00FFFFFF);
$t1[] = (($t3i << 16) & self::safe_intval(0xFFFF0000)) | (($t3i >> 16) & 0x0000FFFF);
$t2[] = (($t3i << 8) & self::safe_intval(0xFFFFFF00)) | (($t3i >> 24) & 0x000000FF);
}
$tables = [
@@ -744,7 +746,7 @@ class Rijndael extends BlockCipher
{
static $tables;
if (empty($tables)) {
$dt3 = array_map('intval', [
$dt3 = array_map([self::class, 'safe_intval'], [
0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B,
0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5,
0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B,
@@ -779,11 +781,19 @@ class Rijndael extends BlockCipher
0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0
]);
if (PHP_INT_SIZE === 8) {
foreach ($dt3 as $dt3i) {
$dt0[] = (($dt3i << 24) & intval(0xFF000000)) | (($dt3i >> 8) & 0x00FFFFFF);
$dt1[] = (($dt3i << 16) & intval(0xFFFF0000)) | (($dt3i >> 16) & 0x0000FFFF);
$dt2[] = (($dt3i << 8) & intval(0xFFFFFF00)) | (($dt3i >> 24) & 0x000000FF);
$dt0[] = (($dt3i << 24) & 0xFF000000) | (($dt3i >> 8) & 0x00FFFFFF);
$dt1[] = (($dt3i << 16) & 0xFFFF0000) | (($dt3i >> 16) & 0x0000FFFF);
$dt2[] = (($dt3i << 8) & 0xFFFFFF00) | (($dt3i >> 24) & 0x000000FF);
};
} else {
foreach ($dt3 as $dt3i) {
$dt0[] = ($dt3i << 24) | (($dt3i >> 8) & 0x00FFFFFF);
$dt1[] = ($dt3i << 16) | (($dt3i >> 16) & 0x0000FFFF);
$dt2[] = ($dt3i << 8) | (($dt3i >> 24) & 0x000000FF);
};
}
$tables = [
// The Precomputed inverse mixColumns tables dt0 - dt3
@@ -818,7 +828,7 @@ class Rijndael extends BlockCipher
/**
* Setup the performance-optimized function for de/encrypt()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::setupInlineCrypt()
* Common\SymmetricKey::setupInlineCrypt()
*/
protected function setupInlineCrypt()
{
@@ -879,7 +889,7 @@ class Rijndael extends BlockCipher
$encrypt_block .= '$in = pack("N*"' . "\n";
for ($i = 0; $i < $Nb; ++$i) {
$encrypt_block .= ',
($' . $e . $i . ' & ' . ((int)0xFF000000) . ') ^
($' . $e . $i . ' & ' . (PHP_INT_SIZE == 8 ? 0xFF000000 : -16777216) . ') ^
($' . $e . (($i + $c[1]) % $Nb) . ' & 0x00FF0000 ) ^
($' . $e . (($i + $c[2]) % $Nb) . ' & 0x0000FF00 ) ^
($' . $e . (($i + $c[3]) % $Nb) . ' & 0x000000FF ) ^
@@ -935,7 +945,7 @@ class Rijndael extends BlockCipher
$decrypt_block .= '$in = pack("N*"' . "\n";
for ($i = 0; $i < $Nb; ++$i) {
$decrypt_block .= ',
($' . $e . $i . ' & ' . ((int)0xFF000000) . ') ^
($' . $e . $i . ' & ' . (PHP_INT_SIZE == 8 ? 0xFF000000 : -16777216) . ') ^
($' . $e . (($Nb + $i - $c[1]) % $Nb) . ' & 0x00FF0000 ) ^
($' . $e . (($Nb + $i - $c[2]) % $Nb) . ' & 0x0000FF00 ) ^
($' . $e . (($Nb + $i - $c[3]) % $Nb) . ' & 0x000000FF ) ^
+6 -6
View File
@@ -47,12 +47,12 @@ class Salsa20 extends StreamCipher
protected $key_length = 32; // = 256 bits
/**
* @see \phpseclib3\Crypt\Salsa20::crypt()
* @see Salsa20::crypt()
*/
const ENCRYPT = 0;
/**
* @see \phpseclib3\Crypt\Salsa20::crypt()
* @see Salsa20::crypt()
*/
const DECRYPT = 1;
@@ -236,7 +236,7 @@ class Salsa20 extends StreamCipher
/**
* Encrypts a message.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* @see SymmetricKey::decrypt()
* @see self::crypt()
* @param string $plaintext
* @return string $ciphertext
@@ -256,7 +256,7 @@ class Salsa20 extends StreamCipher
* $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
* At least if the continuous buffer is disabled.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see SymmetricKey::encrypt()
* @see self::crypt()
* @param string $ciphertext
* @return string $plaintext
@@ -411,7 +411,7 @@ class Salsa20 extends StreamCipher
$r1 &= 0xFFFFFFFF;
$r2 = ($x & 0xFFFFFFFF) >> (32 - $n);
} else {
$x = (int) $x;
$x = self::safe_intval($x);
$r1 = $x << $n;
$r2 = $x >> (32 - $n);
$r2 &= (1 << $n) - 1;
@@ -482,7 +482,7 @@ class Salsa20 extends StreamCipher
}
for ($i = 1; $i <= 16; $i++) {
$x[$i] += $z[$i];
$x[$i] = self::safe_intval($x[$i] + $z[$i]);
}
return pack('V*', ...$x);
+20 -20
View File
@@ -58,7 +58,7 @@ class TripleDES extends DES
/**
* Key Length (in bytes)
*
* @see \phpseclib3\Crypt\TripleDES::setKeyLength()
* @see TripleDES::setKeyLength()
* @var int
*/
protected $key_length = 24;
@@ -66,8 +66,8 @@ class TripleDES extends DES
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib3\Crypt\DES::cipher_name_mcrypt
* @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt
* @see DES::cipher_name_mcrypt
* @see Common\SymmetricKey::cipher_name_mcrypt
* @var string
*/
protected $cipher_name_mcrypt = 'tripledes';
@@ -75,7 +75,7 @@ class TripleDES extends DES
/**
* Optimizing value while CFB-encrypting
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len
* @see Common\SymmetricKey::cfb_init_len
* @var int
*/
protected $cfb_init_len = 750;
@@ -84,7 +84,7 @@ class TripleDES extends DES
* max possible size of $key
*
* @see self::setKey()
* @see \phpseclib3\Crypt\DES::setKey()
* @see DES::setKey()
* @var string
*/
protected $key_length_max = 24;
@@ -126,8 +126,8 @@ class TripleDES extends DES
*
* - cbc3 (same as cbc)
*
* @see \phpseclib3\Crypt\DES::__construct()
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* @see Crypt\DES::__construct()
* @see Common\SymmetricKey::__construct()
* @param string $mode
*/
public function __construct($mode)
@@ -169,7 +169,7 @@ class TripleDES extends DES
*
* This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* @see Common\SymmetricKey::__construct()
* @param int $engine
* @return bool
*/
@@ -189,7 +189,7 @@ class TripleDES extends DES
*
* SetIV is not required when \phpseclib3\Crypt\Common\SymmetricKey::MODE_ECB is being used.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::setIV()
* @see Common\SymmetricKey::setIV()
* @param string $iv
*/
public function setIV($iv)
@@ -209,7 +209,7 @@ class TripleDES extends DES
*
* If you want to use a 64-bit key use DES.php
*
* @see \phpseclib3\Crypt\Common\SymmetricKey:setKeyLength()
* @see Common\SymmetricKey:setKeyLength()
* @throws \LengthException if the key length is invalid
* @param int $length
*/
@@ -233,8 +233,8 @@ class TripleDES extends DES
*
* DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
*
* @see \phpseclib3\Crypt\DES::setKey()
* @see \phpseclib3\Crypt\Common\SymmetricKey::setKey()
* @see DES::setKey()
* @see Common\SymmetricKey::setKey()
* @throws \LengthException if the key length is invalid
* @param string $key
*/
@@ -270,7 +270,7 @@ class TripleDES extends DES
/**
* Encrypts a message.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
* @see Common\SymmetricKey::encrypt()
* @param string $plaintext
* @return string $cipertext
*/
@@ -296,7 +296,7 @@ class TripleDES extends DES
/**
* Decrypts a message.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
* @see Common\SymmetricKey::decrypt()
* @param string $ciphertext
* @return string $plaintext
*/
@@ -351,7 +351,7 @@ class TripleDES extends DES
* continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
* however, they are also less intuitive and more likely to cause you problems.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::enableContinuousBuffer()
* @see Common\SymmetricKey::enableContinuousBuffer()
* @see self::disableContinuousBuffer()
*/
public function enableContinuousBuffer()
@@ -369,7 +369,7 @@ class TripleDES extends DES
*
* The default behavior.
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::disableContinuousBuffer()
* @see Common\SymmetricKey::disableContinuousBuffer()
* @see self::enableContinuousBuffer()
*/
public function disableContinuousBuffer()
@@ -385,8 +385,8 @@ class TripleDES extends DES
/**
* Creates the key schedule
*
* @see \phpseclib3\Crypt\DES::setupKey()
* @see \phpseclib3\Crypt\Common\SymmetricKey::setupKey()
* @see DES::setupKey()
* @see Common\SymmetricKey::setupKey()
*/
protected function setupKey()
{
@@ -419,8 +419,8 @@ class TripleDES extends DES
/**
* Sets the internal crypt engine
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
* @see \phpseclib3\Crypt\Common\SymmetricKey::setPreferredEngine()
* @see Common\SymmetricKey::__construct()
* @see Common\SymmetricKey::setPreferredEngine()
* @param int $engine
*/
public function setPreferredEngine($engine)
+10 -10
View File
@@ -49,7 +49,7 @@ class Twofish extends BlockCipher
/**
* The mcrypt specific name of the cipher
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt
* @see Common\SymmetricKey::cipher_name_mcrypt
* @var string
*/
protected $cipher_name_mcrypt = 'twofish';
@@ -57,7 +57,7 @@ class Twofish extends BlockCipher
/**
* Optimizing value while CFB-encrypting
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len
* @see Common\SymmetricKey::cfb_init_len
* @var int
*/
protected $cfb_init_len = 800;
@@ -373,12 +373,12 @@ class Twofish extends BlockCipher
protected static function initialize_static_variables()
{
if (is_float(self::$m3[0])) {
self::$m0 = array_map('intval', self::$m0);
self::$m1 = array_map('intval', self::$m1);
self::$m2 = array_map('intval', self::$m2);
self::$m3 = array_map('intval', self::$m3);
self::$q0 = array_map('intval', self::$q0);
self::$q1 = array_map('intval', self::$q1);
self::$m0 = array_map([self::class, 'safe_intval'], self::$m0);
self::$m1 = array_map([self::class, 'safe_intval'], self::$m1);
self::$m2 = array_map([self::class, 'safe_intval'], self::$m2);
self::$m3 = array_map([self::class, 'safe_intval'], self::$m3);
self::$q0 = array_map([self::class, 'safe_intval'], self::$q0);
self::$q1 = array_map([self::class, 'safe_intval'], self::$q1);
}
parent::initialize_static_variables();
@@ -431,7 +431,7 @@ class Twofish extends BlockCipher
/**
* Setup the key (expansion)
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::_setupKey()
* @see Common\SymmetricKey::_setupKey()
*/
protected function setupKey()
{
@@ -700,7 +700,7 @@ class Twofish extends BlockCipher
/**
* Setup the performance-optimized function for de/encrypt()
*
* @see \phpseclib3\Crypt\Common\SymmetricKey::_setupInlineCrypt()
* @see Common\SymmetricKey::_setupInlineCrypt()
*/
protected function setupInlineCrypt()
{
+108 -10
View File
@@ -131,6 +131,8 @@ abstract class ASN1
*/
private static $encoded;
private static $use64BitOIDHandling;
/**
* Type mapping table for the ANY type.
*
@@ -190,6 +192,7 @@ abstract class ASN1
*
* @param Element|string $encoded
* @return ?array
* @changed in phpseclib 4.0.0
*/
public static function decodeBER($encoded)
{
@@ -273,8 +276,7 @@ abstract class ASN1
// tags of indefinte length don't really have a header length; this length includes the tag
$current += ['headerlength' => $length + 2];
$start += $length;
extract(unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)));
/** @var integer $length */
$length = unpack('Nlength', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4))['length'];
} else {
$current += ['headerlength' => 2];
}
@@ -515,6 +517,7 @@ abstract class ASN1
* @param array $mapping
* @param array $special
* @return array|bool|Element|string|null
* @changed in phpseclib 4.0.0
*/
public static function asn1map(array $decoded, $mapping, $special = [])
{
@@ -537,7 +540,7 @@ abstract class ASN1
case $mapping['type'] == self::TYPE_CHOICE:
foreach ($mapping['children'] as $key => $option) {
switch (true) {
case isset($option['constant']) && $option['constant'] == $decoded['constant']:
case isset($option['constant']) && isset($decoded['constant']) && $option['constant'] == $decoded['constant']:
case !isset($option['constant']) && $option['type'] == $decoded['type']:
$value = self::asn1map($decoded, $option, $special);
break;
@@ -621,6 +624,13 @@ abstract class ASN1
// Can only match if no constant expected and type matches or is generic.
$maymatch = !isset($child['constant']) && array_search($child['type'], [$temp['type'], self::TYPE_ANY, self::TYPE_CHOICE]) !== false;
}
} elseif (isset($child['constant'])) {
// a CHOICE that is itself tagged is identified by that tag. its alternatives
// can't be used to tell it apart from a sibling with the same CHOICE definition
// (eg. issuerLogo [1] / subjectLogo [2] in RFC 9399's LogotypeExtn).
$maymatch = isset($temp['constant']) &&
$child['constant'] == $temp['constant'] &&
$temp['type'] == self::CLASS_CONTEXT_SPECIFIC;
}
}
@@ -693,6 +703,11 @@ abstract class ASN1
// Can only match if no constant expected and type matches or is generic.
$maymatch = !isset($child['constant']) && array_search($child['type'], [$temp['type'], self::TYPE_ANY, self::TYPE_CHOICE]) !== false;
}
} elseif (isset($child['constant'])) {
// see the comment in the TYPE_SEQUENCE case
$maymatch = isset($temp['constant']) &&
$child['constant'] == $temp['constant'] &&
$tempClass == self::CLASS_CONTEXT_SPECIFIC;
}
if ($maymatch) {
@@ -790,10 +805,17 @@ abstract class ASN1
case self::TYPE_ENUMERATED:
$temp = $decoded['content'];
if (isset($mapping['implicit'])) {
$temp = new BigInteger($decoded['content'], -256);
$temp = new BigInteger($temp, -256);
}
if (!$temp instanceof BigInteger) {
return false;
}
if (isset($mapping['mapping'])) {
$temp = (int) $temp->toString();
$temp = $temp->toString();
if (strlen($temp) > 1) {
return false;
}
$temp = (int) $temp;
return isset($mapping['mapping'][$temp]) ?
$mapping['mapping'][$temp] :
false;
@@ -834,6 +856,7 @@ abstract class ASN1
* @param array $mapping
* @param array $special
* @return string
* @changed in phpseclib 4.0.0
*/
public static function encodeDER($source, $mapping, $special = [])
{
@@ -931,7 +954,19 @@ abstract class ASN1
an untagged "DummyReference" (see ITU-T Rec. X.683 | ISO/IEC 8824-4, 8.3)."
*/
if (isset($child['explicit']) || $child['type'] == self::TYPE_CHOICE) {
if ($child['constant'] <= 30) {
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']);
} else {
$constant = $child['constant'];
$subtag = '';
while ($constant > 0) {
$subtagvalue = $constant & 0x7F;
$subtag = (chr(0x80 | $subtagvalue)) . $subtag;
$constant = $constant >> 7;
}
$subtag[strlen($subtag) - 1] = $subtag[strlen($subtag) - 1] & chr(0x7F);
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | 0x1f) . $subtag;
}
$temp = $subtag . self::encodeLength(strlen($temp)) . $temp;
} else {
$subtag = chr((self::CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']);
@@ -1130,6 +1165,19 @@ abstract class ASN1
return chr($tag) . self::encodeLength(strlen($value)) . $value;
}
public static function enable64BitOIDHandling()
{
if (PHP_INT_SIZE === 4) {
throw new \RuntimeException('64-bit OID handling is unavailable on 32-bit PHP installs');
}
self::$use64BitOIDHandling = true;
}
public static function disable64BitOIDHandling()
{
self::$use64BitOIDHandling = false;
}
/**
* BER-decode the OID
*
@@ -1137,20 +1185,27 @@ abstract class ASN1
*
* @param string $content
* @return string
* @changed in phpseclib 4.0.0
*/
public static function decodeOID($content)
{
// BigInteger's are used because of OIDs like 2.25.329800735698586629295641978511506172918
// https://healthcaresecprivacy.blogspot.com/2011/02/creating-and-using-unique-id-uuid-oid.html elaborates.
static $eighty;
if (!$eighty) {
$eighty = new BigInteger(80);
}
if (!isset(self::$use64BitOIDHandling)) {
self::$use64BitOIDHandling = PHP_INT_SIZE === 8;
}
$oid = [];
$pos = 0;
$len = strlen($content);
// see https://github.com/openjdk/jdk/blob/2deb318c9f047ec5a4b160d66a4b52f93688ec42/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java#L55
if ($len > 4096) {
//throw new \RuntimeException("Object identifier size is limited to 4096 bytes ($len bytes present)");
if ($len > 128) {
//throw new \RuntimeException("Object identifier size is limited to 128 bytes ($len bytes present)");
return false;
}
@@ -1159,15 +1214,52 @@ abstract class ASN1
}
$n = new BigInteger();
$subn = $numBytes = 0;
if (self::$use64BitOIDHandling) {
$prefix = '';
while ($pos < $len) {
$temp = ord($content[$pos++]);
$n = $n->bitwise_leftShift(7);
$n = $n->bitwise_or(new BigInteger($temp & 0x7F));
if (~$temp & 0x80) {
$subn <<= 7;
$subn |= ($temp & 0x7F);
$numBytes++;
$endByte = ~$temp & 0x80;
if ($numBytes === PHP_INT_SIZE) {
$prefix .= substr(pack('J', $subn), 1); // we're basically left shifting by 7 bytes
$subn = $numBytes = 0;
if ($endByte) {
$oid[] = new BigInteger($prefix, 256);
$prefix = '';
}
} elseif ($endByte) {
if (strlen($prefix)) {
$arc = new BigInteger($prefix, 256);
$arc = $arc->bitwise_leftShift($numBytes * 7);
$oid[] = $arc->bitwise_or(new BigInteger($subn));
$prefix = '';
} else {
$oid[] = new BigInteger($subn);
}
$subn = $numBytes = 0;
}
}
} else {
while ($pos < $len) {
$temp = ord($content[$pos++]);
$subn <<= 7;
$subn |= ($temp & 0x7F);
$numBytes++;
$endByte = ~$temp & 0x80;
if ($numBytes === PHP_INT_SIZE || $endByte) {
$n = $n->bitwise_leftShift($numBytes * 7);
$n = $n->bitwise_or(new BigInteger($subn));
$subn = $numBytes = 0;
if ($endByte) {
$oid[] = $n;
$n = new BigInteger();
}
}
}
}
$part1 = array_shift($oid);
$first = floor(ord($content[0]) / 40);
/*
@@ -1252,6 +1344,7 @@ abstract class ASN1
* @param string $content
* @param int $tag
* @return \DateTime|false
* @changed in phpseclib 4.0.0
*/
private static function decodeTime($content, $tag)
{
@@ -1297,6 +1390,7 @@ abstract class ASN1
* Sets the time / date format for asn1map().
*
* @param string $format
* @removed in phpseclib 4.0.0
*/
public static function setTimeFormat($format)
{
@@ -1310,6 +1404,7 @@ abstract class ASN1
* Previously loaded OIDs are retained.
*
* @param array $oids
* @changed in phpseclib 4.0.0
*/
public static function loadOIDs(array $oids)
{
@@ -1324,6 +1419,7 @@ abstract class ASN1
* Previously loaded filters are not retained.
*
* @param array $filters
* @removed in phpseclib 4.0.0
*/
public static function setFilters(array $filters)
{
@@ -1340,6 +1436,7 @@ abstract class ASN1
* @param int $from
* @param int $to
* @return string
* @removed in phpseclib 4.0.0
*/
public static function convert($in, $from = self::TYPE_UTF8_STRING, $to = self::TYPE_UTF8_STRING)
{
@@ -1505,6 +1602,7 @@ abstract class ASN1
*
* @param string $name
* @return string
* @removed in phpseclib 4.0.0
*/
public static function getOID($name)
{
+138 -27
View File
@@ -57,37 +57,37 @@ class X509
/**
* Return internal array representation
*
* @see \phpseclib3\File\X509::getDN()
* @see X509::getDN()
*/
const DN_ARRAY = 0;
/**
* Return string
*
* @see \phpseclib3\File\X509::getDN()
* @see X509::getDN()
*/
const DN_STRING = 1;
/**
* Return ASN.1 name string
*
* @see \phpseclib3\File\X509::getDN()
* @see X509::getDN()
*/
const DN_ASN1 = 2;
/**
* Return OpenSSL compatible array
*
* @see \phpseclib3\File\X509::getDN()
* @see X509::getDN()
*/
const DN_OPENSSL = 3;
/**
* Return canonical ASN.1 RDNs string
*
* @see \phpseclib3\File\X509::getDN()
* @see X509::getDN()
*/
const DN_CANON = 4;
/**
* Return name hash for file indexing
*
* @see \phpseclib3\File\X509::getDN()
* @see X509::getDN()
*/
const DN_HASH = 5;
@@ -96,25 +96,25 @@ class X509
*
* ie. a base64-encoded PEM with a header and a footer
*
* @see \phpseclib3\File\X509::saveX509()
* @see \phpseclib3\File\X509::saveCSR()
* @see \phpseclib3\File\X509::saveCRL()
* @see X509::saveX509()
* @see X509::saveCSR()
* @see X509::saveCRL()
*/
const FORMAT_PEM = 0;
/**
* Save as DER
*
* @see \phpseclib3\File\X509::saveX509()
* @see \phpseclib3\File\X509::saveCSR()
* @see \phpseclib3\File\X509::saveCRL()
* @see X509::saveX509()
* @see X509::saveCSR()
* @see X509::saveCRL()
*/
const FORMAT_DER = 1;
/**
* Save as a SPKAC
*
* @see \phpseclib3\File\X509::saveX509()
* @see \phpseclib3\File\X509::saveCSR()
* @see \phpseclib3\File\X509::saveCRL()
* @see X509::saveX509()
* @see X509::saveCSR()
* @see X509::saveCRL()
*
* Only works on CSRs. Not currently supported.
*/
@@ -124,9 +124,9 @@ class X509
*
* Used only by the load*() functions
*
* @see \phpseclib3\File\X509::saveX509()
* @see \phpseclib3\File\X509::saveCSR()
* @see \phpseclib3\File\X509::saveCRL()
* @see X509::saveX509()
* @see X509::saveCSR()
* @see X509::saveCRL()
*/
const FORMAT_AUTO_DETECT = 3;
@@ -269,10 +269,18 @@ class X509
*/
private $domains = null;
/**
* URL fetch callback
*
* @var string|array|null
*/
private static $urlFetchCallback = null;
/**
* Default Constructor.
*
* @return X509
* @changed in phpseclib 4.0.0
*/
public function __construct()
{
@@ -315,6 +323,7 @@ class X509
'id-at-uniqueIdentifier' => '2.5.4.45',
'id-at-role' => '2.5.4.72',
'id-at-postalAddress' => '2.5.4.16',
'id-at-organizationIdentifier' => '2.5.4.97',
'jurisdictionOfIncorporationCountryName' => '1.3.6.1.4.1.311.60.2.1.3',
'jurisdictionOfIncorporationStateOrProvinceName' => '1.3.6.1.4.1.311.60.2.1.2',
'jurisdictionLocalityName' => '1.3.6.1.4.1.311.60.2.1.1',
@@ -428,6 +437,7 @@ class X509
* @param array|string $cert
* @param int $mode
* @return mixed
* @removed in phpseclib 4.0.0
*/
public function loadX509($cert, $mode = self::FORMAT_AUTO_DETECT)
{
@@ -501,6 +511,7 @@ class X509
* @param array $cert
* @param int $format optional
* @return string
* @removed in phpseclib 4.0.0
*/
public function saveX509(array $cert, $format = self::FORMAT_PEM)
{
@@ -626,7 +637,9 @@ class X509
$extensions = &$this->subArray($root, $path, !empty($this->extensionValues));
foreach ($this->extensionValues as $id => $data) {
extract($data);
$critical = $data['critical'];
$replace = $data['replace'];
$value = $data['value'];
$newext = [
'extnId' => $id,
'extnValue' => $value,
@@ -956,6 +969,7 @@ class X509
*
* @param string $cert
* @return bool
* @removed in phpseclib 4.0.0
*/
public function loadCA($cert)
{
@@ -1082,6 +1096,7 @@ class X509
*
* @param \DateTimeInterface|string $date optional
* @return bool
* @removed in phpseclib 4.0.0
*/
public function validateDate($date = null)
{
@@ -1112,6 +1127,10 @@ class X509
/**
* Fetches a URL
*
* If a fetch callback is set via setURLFetchCallback(), the host is resolved
* once and the connection is pinned to that IP (the callback judges the
* resolved IP, preventing DNS-rebinding bypass).
*
* @param string $url
* @return bool|string
*/
@@ -1122,25 +1141,58 @@ class X509
}
$parts = parse_url($url);
if ($parts === false || !isset($parts['scheme']) || !isset($parts['host'])) {
return false;
}
$host = $parts['host'];
$port = isset($parts['port']) ? $parts['port'] : 80;
if (isset(self::$urlFetchCallback)) {
if (filter_var($host, FILTER_VALIDATE_IP)) {
$ip = $host;
// unwrap IPv4-mapped IPv6 so the callback judges the real v4 address
if (preg_match('/^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i', $ip, $m)) {
$ip = $m[1];
}
} else {
$records = dns_get_record($host, DNS_A | DNS_AAAA);
if (!$records) {
return false;
}
if (isset($records[0]['ip'])) {
$ip = $records[0]['ip'];
} elseif (isset($records[0]['ipv6'])) {
$ip = $records[0]['ipv6'];
} else {
return false;
}
}
if (!call_user_func(self::$urlFetchCallback, $host, $ip, $port, $parts['scheme'])) {
return false;
}
$target = strpos($ip, ':') !== false ? "[$ip]" : $ip;
} else {
$target = $host;
}
$data = '';
switch ($parts['scheme']) {
case 'http':
$fsock = @fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80);
$fsock = @fsockopen($target, $port);
if (!$fsock) {
return false;
}
$path = $parts['path'];
$path = isset($parts['path']) ? $parts['path'] : '/';
if (isset($parts['query'])) {
$path .= '?' . $parts['query'];
}
fputs($fsock, "GET $path HTTP/1.0\r\n");
fputs($fsock, "Host: $parts[host]\r\n\r\n");
fputs($fsock, "Host: $host\r\n\r\n");
$line = fgets($fsock, 1024);
if (strlen($line) < 3) {
if ($line === false || strlen($line) < 3) {
return false;
}
preg_match('#HTTP/1.\d (\d{3})#', $line, $temp);
if ($temp[1] != '200') {
if (!preg_match('#HTTP/1.\d (\d{3})#', $line, $temp) || $temp[1] != '200') {
return false;
}
@@ -1159,7 +1211,8 @@ class X509
break;
//case 'ftp':
//case 'ldap':
//default:
default:
return false;
}
return $data;
@@ -1239,6 +1292,7 @@ class X509
*
* @param bool $caonly optional
* @return mixed
* @changed in phpseclib 4.0.0
*/
public function validateSignature($caonly = true)
{
@@ -1470,6 +1524,7 @@ class X509
/**
* Prevents URIs from being automatically retrieved
*
* @removed in phpseclib 4.0.0
*/
public static function disableURLFetch()
{
@@ -1479,6 +1534,7 @@ class X509
/**
* Allows URIs to be automatically retrieved
*
* @removed in phpseclib 4.0.0
*/
public static function enableURLFetch()
{
@@ -1492,6 +1548,7 @@ class X509
*
* @param string $ip
* @return string
* @removed in phpseclib 4.0.0
*/
public static function decodeIP($ip)
{
@@ -1505,6 +1562,7 @@ class X509
*
* @param string $ip
* @return array
* @removed in phpseclib 4.0.0
*/
public static function decodeNameConstraintIP($ip)
{
@@ -1521,6 +1579,7 @@ class X509
*
* @param string|array $ip
* @return string
* @removed in phpseclib 4.0.0
*/
public static function encodeIP($ip)
{
@@ -1610,6 +1669,9 @@ class X509
case 'organizationalunitname':
case 'ou':
return 'id-at-organizationalUnitName';
case 'id-at-organizationidentifier':
case 'organizationIdentifier':
return 'id-at-organizationIdentifier';
case 'id-at-pseudonym':
case 'pseudonym':
return 'id-at-pseudonym';
@@ -1641,6 +1703,7 @@ class X509
* @param mixed $propValue
* @param string $type optional
* @return bool
* @removed in phpseclib 4.0.0
*/
public function setDNProp($propName, $propValue, $type = 'utf8String')
{
@@ -1671,6 +1734,7 @@ class X509
* Remove Distinguished Name properties
*
* @param string $propName
* @removed in phpseclib 4.0.0
*/
public function removeDNProp($propName)
{
@@ -1704,6 +1768,7 @@ class X509
* @param array $dn optional
* @param bool $withType optional
* @return mixed
* @removed in phpseclib 4.0.0
*/
public function getDNProp($propName, $dn = null, $withType = false)
{
@@ -1768,6 +1833,7 @@ class X509
* @param bool $merge optional
* @param string $type optional
* @return bool
* @changed in phpseclib 4.0.0
*/
public function setDN($dn, $merge = false, $type = 'utf8String')
{
@@ -1809,6 +1875,7 @@ class X509
* @param mixed $format optional
* @param array $dn optional
* @return array|bool|string
* @changed in phpseclib 4.0.0
*/
public function getDN($format = self::DN_ARRAY, $dn = null)
{
@@ -1858,7 +1925,7 @@ class X509
$dn = $this->getDN(self::DN_CANON, $dn);
$hash = new Hash('sha1');
$hash = $hash->hash($dn);
extract(unpack('Vhash', $hash));
$hash = unpack('Vhash', $hash)['hash'];
return strtolower(Strings::bin2hex(pack('N', $hash)));
}
@@ -1950,6 +2017,7 @@ class X509
*
* @param int $format optional
* @return mixed
* @changed in phpseclib 4.0.0
*/
public function getIssuerDN($format = self::DN_ARRAY)
{
@@ -1971,6 +2039,7 @@ class X509
*
* @param int $format optional
* @return mixed
* @changed in phpseclib 4.0.0
*/
public function getSubjectDN($format = self::DN_ARRAY)
{
@@ -1994,6 +2063,7 @@ class X509
* @param string $propName
* @param bool $withType optional
* @return mixed
* @removed in phpseclib 4.0.0
*/
public function getIssuerDNProp($propName, $withType = false)
{
@@ -2015,6 +2085,7 @@ class X509
* @param string $propName
* @param bool $withType optional
* @return mixed
* @removed in phpseclib 4.0.0
*/
public function getSubjectDNProp($propName, $withType = false)
{
@@ -2036,6 +2107,7 @@ class X509
* Get the certificate chain for the current cert
*
* @return mixed
* @removed in phpseclib 4.0.0
*/
public function getChain()
{
@@ -2077,6 +2149,7 @@ class X509
* Returns the current cert
*
* @return array|bool
* @removed in phpseclib 4.0.0
*/
public function &getCurrentCert()
{
@@ -2102,6 +2175,7 @@ class X509
* Key needs to be a \phpseclib3\Crypt\RSA object
*
* @param PrivateKey $key
* @removed in phpseclib 4.0.0
*/
public function setPrivateKey(PrivateKey $key)
{
@@ -2114,6 +2188,7 @@ class X509
* Used for SPKAC CSR's
*
* @param string $challenge
* @removed in phpseclib 4.0.0
*/
public function setChallenge($challenge)
{
@@ -2174,6 +2249,7 @@ class X509
* @param string $csr
* @param int $mode
* @return mixed
* @removed in phpseclib 4.0.0
*/
public function loadCSR($csr, $mode = self::FORMAT_AUTO_DETECT)
{
@@ -2248,6 +2324,7 @@ class X509
* @param array $csr
* @param int $format optional
* @return string
* @removed in phpseclib 4.0.0
*/
public function saveCSR(array $csr, $format = self::FORMAT_PEM)
{
@@ -2293,6 +2370,7 @@ class X509
*
* @param string $spkac
* @return mixed
* @removed in phpseclib 4.0.0
*/
public function loadSPKAC($spkac)
{
@@ -2357,6 +2435,7 @@ class X509
* @param array $spkac
* @param int $format optional
* @return string
* @removed in phpseclib 4.0.0
*/
public function saveSPKAC(array $spkac, $format = self::FORMAT_PEM)
{
@@ -2394,6 +2473,7 @@ class X509
* @param string $crl
* @param int $mode
* @return mixed
* @removed in phpseclib 4.0.0
*/
public function loadCRL($crl, $mode = self::FORMAT_AUTO_DETECT)
{
@@ -2460,6 +2540,7 @@ class X509
* @param array $crl
* @param int $format optional
* @return string
* @removed in phpseclib 4.0.0
*/
public function saveCRL(array $crl, $format = self::FORMAT_PEM)
{
@@ -2540,6 +2621,7 @@ class X509
* a CSR or something with the DN and public key explicitly set.
*
* @return mixed
* @removed in phpseclib 4.0.0
*/
public function sign(X509 $issuer, X509 $subject)
{
@@ -2722,6 +2804,7 @@ class X509
* Sign a CSR
*
* @return mixed
* @removed in phpseclib 4.0.0
*/
public function signCSR()
{
@@ -2777,6 +2860,7 @@ class X509
* Sign a SPKAC
*
* @return mixed
* @removed in phpseclib 4.0.0
*/
public function signSPKAC()
{
@@ -2839,6 +2923,7 @@ class X509
* $issuer's private key needs to be loaded.
*
* @return mixed
* @removed in phpseclib 4.0.0
*/
public function signCRL(X509 $issuer, X509 $crl)
{
@@ -3389,6 +3474,7 @@ class X509
* @param array $cert optional
* @param string $path optional
* @return array
* @removed in phpseclib 4.0.0
*/
public function getExtensions($cert = null, $path = null)
{
@@ -3415,6 +3501,7 @@ class X509
* @param string $id
* @param int $disposition optional
* @return bool
* @removed in phpseclib 4.0.0
*/
public function removeAttribute($id, $disposition = self::ATTR_ALL)
{
@@ -3465,6 +3552,7 @@ class X509
* @param int $disposition optional
* @param array $csr optional
* @return mixed
* @removed in phpseclib 4.0.0
*/
public function getAttribute($id, $disposition = self::ATTR_ALL, $csr = null)
{
@@ -3506,6 +3594,7 @@ class X509
*
* @param array $csr optional
* @return mixed
* @removed in phpseclib 4.0.0
*/
public function getRequestedCertificateExtensions($csr = null)
{
@@ -3526,6 +3615,7 @@ class X509
*
* @param array $csr optional
* @return array
* @removed in phpseclib 4.0.0
*/
public function getAttributes($csr = null)
{
@@ -3552,6 +3642,7 @@ class X509
* @param mixed $value
* @param int $disposition optional
* @return bool
* @removed in phpseclib 4.0.0
*/
public function setAttribute($id, $value, $disposition = self::ATTR_ALL)
{
@@ -3633,6 +3724,7 @@ class X509
* @param mixed $key optional
* @param int $method optional
* @return string binary key identifier
* @removed in phpseclib 4.0.0
*/
public function computeKeyIdentifier($key = null, $method = 1)
{
@@ -3729,6 +3821,7 @@ class X509
*
* @param mixed ...$domains
* @return void
* @removed in phpseclib 4.0.0
*/
public function setDomain(...$domains)
{
@@ -3741,6 +3834,7 @@ class X509
* Set the IP Addresses's which the cert is to be valid for
*
* @param mixed[] ...$ipAddresses
* @removed in phpseclib 4.0.0
*/
public function setIPAddress(...$ipAddresses)
{
@@ -3837,6 +3931,7 @@ class X509
*
* @param string $serial
* @return bool
* @removed in phpseclib 4.0.0
*/
public function unrevoke($serial)
{
@@ -3856,6 +3951,7 @@ class X509
*
* @param string $serial
* @return mixed
* @removed in phpseclib 4.0.0
*/
public function getRevoked($serial)
{
@@ -3873,6 +3969,7 @@ class X509
*
* @param array $crl optional
* @return array|bool
* @removed in phpseclib 4.0.0
*/
public function listRevoked($crl = null)
{
@@ -3901,6 +3998,7 @@ class X509
* @param string $serial
* @param string $id
* @return bool
* @removed in phpseclib 4.0.0
*/
public function removeRevokedCertificateExtension($serial, $id)
{
@@ -3922,6 +4020,7 @@ class X509
* @param string $id
* @param array $crl optional
* @return mixed
* @removed in phpseclib 4.0.0
*/
public function getRevokedCertificateExtension($serial, $id, $crl = null)
{
@@ -3969,6 +4068,7 @@ class X509
* @param bool $critical optional
* @param bool $replace optional
* @return bool
* @removed in phpseclib 4.0.0
*/
public function setRevokedCertificateExtension($serial, $id, $value, $critical = false, $replace = true)
{
@@ -4019,9 +4119,20 @@ class X509
* @param mixed $value
* @param bool $critical
* @param bool $replace
* @removed in phpseclib 4.0.0
*/
public function setExtensionValue($id, $value, $critical = false, $replace = false)
{
$this->extensionValues[$id] = compact('critical', 'replace', 'value');
}
/**
* Returns the OID corresponding to a name
*
* @param ?callable $callback
*/
public static function setURLFetchCallback($callback)
{
self::$urlFetchCallback = $callback;
}
}
+46 -11
View File
@@ -143,6 +143,11 @@ class BigInteger implements \JsonSerializable
['PHP64', ['DefaultEngine']],
['PHP32', ['DefaultEngine']]
];
// per https://phpseclib.com/docs/speed PHP 8.4.0+ _significantly_ sped up BCMath
if (version_compare(PHP_VERSION, '8.4.0') >= 0) {
$engines[1][0] = 'BCMath';
$engines[2][0] = 'PHP64';
}
foreach ($engines as $engine) {
try {
@@ -317,6 +322,7 @@ class BigInteger implements \JsonSerializable
*
* @param BigInteger $n
* @return BigInteger
* @changed in phpseclib 4.0.0
*/
public function modInverse(BigInteger $n)
{
@@ -333,12 +339,10 @@ class BigInteger implements \JsonSerializable
*/
public function extendedGCD(BigInteger $n)
{
extract($this->value->extendedGCD($n->value));
/**
* @var BigInteger $gcd
* @var BigInteger $x
* @var BigInteger $y
*/
$extended = $this->value->extendedGCD($n->value);
$gcd = $extended['gcd'];
$x = $extended['x'];
$y = $extended['y'];
return [
'gcd' => new static($gcd),
'x' => new static($x),
@@ -399,7 +403,7 @@ class BigInteger implements \JsonSerializable
*
* Will be called, automatically, when serialize() is called on a BigInteger object.
*
* __sleep() / __wakeup() have been around since PHP 4.0
* __sleep() / __wakeup() have been around since PHP 4.0 but were deprecated in PHP 8.5
*
* \Serializable was introduced in PHP 5.1 and deprecated in PHP 8.1:
* https://wiki.php.net/rfc/phase_out_serializable
@@ -434,6 +438,38 @@ class BigInteger implements \JsonSerializable
}
}
/**
* __serialize() magic method
*
* @see self::__unserialize()
* @return array
* @access public
*/
public function __serialize()
{
$result = ['hex' => $this->toHex(true)];
if ($this->getPrecision() > 0) {
$result['precision'] = $this->getPrecision();
}
return $result;
}
/**
* __unserialize() magic method
*
* @see self::__serialize()
* @access public
*/
public function __unserialize(array $data)
{
$temp = new static($data['hex'], -16);
$this->value = $temp->value;
if (isset($data['precision']) && $data['precision'] > 0) {
// recalculate $this->bitmask
$this->setPrecision($data['precision']);
}
}
/**
* JSON Serialize
*
@@ -617,10 +653,9 @@ class BigInteger implements \JsonSerializable
self::initialize_static_variables();
$class = self::$mainEngine;
extract($class::minMaxBits($bits));
/** @var BigInteger $min
* @var BigInteger $max
*/
$minMax = $class::minMaxBits($bits);
$min = $minMax['min'];
$max = $minMax['max'];
return [
'min' => new static($min),
'max' => new static($max)
@@ -38,6 +38,11 @@ class BCMath extends Engine
*/
const ENGINE_DIR = 'BCMath';
/**
* Test to see if bcmod() accepts 2 or 3 parameters
*/
const BCMOD_THREE_PARAMS = PHP_VERSION_ID >= 72000;
/**
* Test for engine validity
*
@@ -148,8 +153,8 @@ class BCMath extends Engine
}
while (bccomp($current, '0', 0) > 0) {
$temp = bcmod($current, '16777216');
$value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value;
$temp = self::BCMOD_THREE_PARAMS ? bcmod($current, '16777216', 0) : bcmod($current, '16777216');
$value = chr($temp >> 16) . chr(($temp >> 8) & 0xFF) . chr($temp & 0xFF) . $value;
$current = bcdiv($current, '16777216', 0);
}
@@ -167,7 +172,7 @@ class BCMath extends Engine
public function add(BCMath $y)
{
$temp = new self();
$temp->value = bcadd($this->value, $y->value);
$temp->value = bcadd($this->value, $y->value, 0);
return $this->normalize($temp);
}
@@ -181,7 +186,7 @@ class BCMath extends Engine
public function subtract(BCMath $y)
{
$temp = new self();
$temp->value = bcsub($this->value, $y->value);
$temp->value = bcsub($this->value, $y->value, 0);
return $this->normalize($temp);
}
@@ -195,7 +200,7 @@ class BCMath extends Engine
public function multiply(BCMath $x)
{
$temp = new self();
$temp->value = bcmul($this->value, $x->value);
$temp->value = bcmul($this->value, $x->value, 0);
return $this->normalize($temp);
}
@@ -217,7 +222,7 @@ class BCMath extends Engine
$remainder = new self();
$quotient->value = bcdiv($this->value, $y->value, 0);
$remainder->value = bcmod($this->value, $y->value);
$remainder->value = self::BCMOD_THREE_PARAMS ? bcmod($this->value, $y->value, 0) : bcmod($this->value, $y->value);
if ($remainder->value[0] == '-') {
$remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0);
@@ -297,8 +302,7 @@ class BCMath extends Engine
*/
public function gcd(BCMath $n)
{
extract($this->extendedGCD($n));
/** @var BCMath $gcd */
$gcd = $this->extendedGCD($n)['gcd'];
return $gcd;
}
@@ -475,7 +479,7 @@ class BCMath extends Engine
$result->bitmask = $this->bitmask;
if ($result->bitmask !== false) {
$result->value = bcmod($result->value, $result->bitmask->value);
$result->value = self::BCMOD_THREE_PARAMS ? bcmod($result->value, $result->bitmask->value, 0) : bcmod($result->value, $result->bitmask->value);
}
return $result;
@@ -523,7 +527,7 @@ class BCMath extends Engine
protected function make_odd()
{
if (!$this->isOdd()) {
$this->value = bcadd($this->value, '1');
$this->value = bcadd($this->value, '1', 0);
}
}
@@ -547,7 +551,7 @@ class BCMath extends Engine
$value = $this->value;
foreach (self::PRIMES as $prime) {
$r = bcmod($this->value, $prime);
$r = self::BCMOD_THREE_PARAMS ? bcmod($this->value, $prime, 0) : bcmod($this->value, $prime);
if ($r == '0') {
return $this->value == $prime;
}
@@ -587,7 +591,7 @@ class BCMath extends Engine
public function pow(BCMath $n)
{
$temp = new self();
$temp->value = bcpow($this->value, $n->value);
$temp->value = bcpow($this->value, $n->value, 0);
return $this->normalize($temp);
}
@@ -656,8 +660,9 @@ class BCMath extends Engine
*/
public function testBit($x)
{
$divisor = bcpow('2', $x + 1, 0);
return bccomp(
bcmod($this->value, bcpow('2', $x + 1, 0)),
self::BCMOD_THREE_PARAMS ? bcmod($this->value, $divisor, 0) : bcmod($this->value, $divisor),
bcpow('2', $x, 0),
0
) >= 0;
@@ -91,7 +91,7 @@ abstract class Base extends BCMath
*/
protected static function multiplyReduce($x, $y, $n, $class)
{
return static::reduce(bcmul($x, $y), $n);
return static::reduce(bcmul($x, $y, 0), $n);
}
/**
@@ -105,6 +105,6 @@ abstract class Base extends BCMath
*/
protected static function squareReduce($x, $n, $class)
{
return static::reduce(bcmul($x, $x), $n);
return static::reduce(bcmul($x, $x, 0), $n);
}
}
@@ -33,7 +33,7 @@ abstract class BuiltIn extends BCMath
protected static function powModHelper(BCMath $x, BCMath $e, BCMath $n)
{
$temp = new BCMath();
$temp->value = bcpowmod($x->value, $e->value, $n->value);
$temp->value = bcpowmod($x->value, $e->value, $n->value, 0);
return $x->normalize($temp);
}
@@ -67,7 +67,7 @@ abstract class Barrett extends Base
$m_length = strlen($m);
if (strlen($n) > 2 * $m_length) {
return bcmod($n, $m);
return self::BCMOD_THREE_PARAMS ? bcmod($n, $m, 0) : bcmod($n, $m);
}
// if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced
@@ -89,14 +89,16 @@ abstract class Barrett extends Base
$lhs = '1' . str_repeat('0', $m_length + ($m_length >> 1));
$u = bcdiv($lhs, $m, 0);
$m1 = bcsub($lhs, bcmul($u, $m));
$m1 = bcsub($lhs, bcmul($u, $m, 0), 0);
$cache[self::DATA][] = [
'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1)
'm1' => $m1 // m.length
];
} else {
extract($cache[self::DATA][$key]);
$cacheValues = $cache[self::DATA][$key];
$u = $cacheValues['u'];
$m1 = $cacheValues['m1'];
}
$cutoff = $m_length + ($m_length >> 1);
@@ -104,8 +106,8 @@ abstract class Barrett extends Base
$lsd = substr($n, -$cutoff);
$msd = substr($n, 0, -$cutoff);
$temp = bcmul($msd, $m1); // m.length + (m.length >> 1)
$n = bcadd($lsd, $temp); // m.length + (m.length >> 1) + 1 (so basically we're adding two same length numbers)
$temp = bcmul($msd, $m1, 0); // m.length + (m.length >> 1)
$n = bcadd($lsd, $temp, 0); // m.length + (m.length >> 1) + 1 (so basically we're adding two same length numbers)
//if ($m_length & 1) {
// return self::regularBarrett($n, $m);
//}
@@ -114,31 +116,31 @@ abstract class Barrett extends Base
$temp = substr($n, 0, -$m_length + 1);
// if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2
// if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1
$temp = bcmul($temp, $u);
$temp = bcmul($temp, $u, 0);
// if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1
// if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1)
$temp = substr($temp, 0, -($m_length >> 1) - 1);
// if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1
// if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1)
$temp = bcmul($temp, $m);
$temp = bcmul($temp, $m, 0);
// at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit
// number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop
// following this comment would loop a lot (hence our calling _regularBarrett() in that situation).
$result = bcsub($n, $temp);
$result = bcsub($n, $temp, 0);
//if (bccomp($result, '0') < 0) {
if ($result[0] == '-') {
$temp = '1' . str_repeat('0', $m_length + 1);
$result = bcadd($result, $temp);
$result = bcadd($result, $temp, 0);
}
while (bccomp($result, $m) >= 0) {
$result = bcsub($result, $m);
while (bccomp($result, $m, 0) >= 0) {
$result = bcsub($result, $m, 0);
}
return $correctionNeeded ? substr($result, 0, -1) : $result;
return $correctionNeeded && $result != '0' ? substr($result, 0, -1) : $result;
}
/**
@@ -161,7 +163,7 @@ abstract class Barrett extends Base
$n_length = strlen($n);
if (strlen($x) > 2 * $n_length) {
return bcmod($x, $n);
return self::BCMOD_THREE_PARAMS ? bcmod($x, $n, 0) : bcmod($x, $n);
}
if (($key = array_search($n, $cache[self::VARIABLE])) === false) {
@@ -172,21 +174,21 @@ abstract class Barrett extends Base
}
$temp = substr($x, 0, -$n_length + 1);
$temp = bcmul($temp, $cache[self::DATA][$key]);
$temp = bcmul($temp, $cache[self::DATA][$key], 0);
$temp = substr($temp, 0, -$n_length - 1);
$r1 = substr($x, -$n_length - 1);
$r2 = substr(bcmul($temp, $n), -$n_length - 1);
$r2 = substr(bcmul($temp, $n, 0), -$n_length - 1);
$result = bcsub($r1, $r2);
//if (bccomp($result, '0') < 0) {
if ($result[0] == '-') {
$q = '1' . str_repeat('0', $n_length + 1);
$result = bcadd($result, $q);
$result = bcadd($result, $q, 0);
}
while (bccomp($result, $n) >= 0) {
$result = bcsub($result, $n);
while (bccomp($result, $n, 0) >= 0) {
$result = bcsub($result, $n, 0);
}
return $result;
@@ -58,7 +58,7 @@ abstract class EvalBarrett extends Base
$m_length = strlen($m);
if ($m_length < 5) {
$code = 'return bcmod($x, $n);';
$code = 'return self::BCMOD_THREE_PARAMS ? bcmod($x, $n, 0) : bcmod($x, $n);';
eval('$func = function ($n) { ' . $code . '};');
self::$custom_reduction = $func;
return;
@@ -66,7 +66,7 @@ abstract class EvalBarrett extends Base
$lhs = '1' . str_repeat('0', $m_length + ($m_length >> 1));
$u = bcdiv($lhs, $m, 0);
$m1 = bcsub($lhs, bcmul($u, $m));
$m1 = bcsub($lhs, bcmul($u, $m, 0), 0);
$cutoff = $m_length + ($m_length >> 1);
@@ -78,23 +78,23 @@ abstract class EvalBarrett extends Base
$lsd = substr($n, -' . $cutoff . ');
$msd = substr($n, 0, -' . $cutoff . ');
$temp = bcmul($msd, ' . $m1 . ');
$n = bcadd($lsd, $temp);
$temp = bcmul($msd, ' . $m1 . ', 0);
$n = bcadd($lsd, $temp, 0);
$temp = substr($n, 0, ' . (-$m_length + 1) . ');
$temp = bcmul($temp, ' . $u . ');
$temp = bcmul($temp, ' . $u . ', 0);
$temp = substr($temp, 0, ' . (-($m_length >> 1) - 1) . ');
$temp = bcmul($temp, ' . $m . ');
$temp = bcmul($temp, ' . $m . ', 0);
$result = bcsub($n, $temp);
$result = bcsub($n, $temp, 0);
if ($result[0] == \'-\') {
$temp = \'1' . str_repeat('0', $m_length + 1) . '\';
$result = bcadd($result, $temp);
$result = bcadd($result, $temp, 0);
}
while (bccomp($result, ' . $m . ') >= 0) {
$result = bcsub($result, ' . $m . ');
$result = bcsub($result, ' . $m . ', 0);
}
return $result;';
@@ -316,11 +316,9 @@ abstract class Engine implements \JsonSerializable
return $this->normalize($n->subtract($temp));
}
extract($this->extendedGCD($n));
/**
* @var Engine $gcd
* @var Engine $x
*/
$extended = $this->extendedGCD($n);
$gcd = $extended['gcd'];
$x = $extended['x'];
if (!$gcd->equals(static::$one[static::class])) {
return false;
@@ -366,6 +364,44 @@ abstract class Engine implements \JsonSerializable
}
}
/**
* __serialize() magic method
*
* __sleep / __wakeup were depreciated in PHP 8.5
* Will be called, automatically, when serialize() is called on a Math_BigInteger object.
*
* @see self::__unserialize()
* @access public
*/
public function __serialize()
{
$result = ['hex' => $this->toHex(true)];
if ($this->precision > 0) {
$result['precision'] = $this->precision;
}
return $result;
}
/**
* __unserialize() magic method
*
* __sleep / __wakeup were depreciated in PHP 8.5
* Will be called, automatically, when unserialize() is called on a Math_BigInteger object.
*
* @see self::__serialize()
* @access public
*/
public function __unserialize(array $data)
{
$temp = new static($data['hex'], -16);
$this->value = $temp->value;
$this->is_negative = $temp->is_negative;
if (isset($data['precision']) && $data['precision'] > 0) {
// recalculate $this->bitmask
$this->setPrecision($data['precision']);
}
}
/**
* JSON Serialize
*
@@ -513,8 +549,8 @@ abstract class Engine implements \JsonSerializable
$carry = 0;
for ($i = strlen($x) - 1; $i >= 0; --$i) {
$temp = ord($x[$i]) << $shift | $carry;
$x[$i] = chr($temp);
$temp = (ord($x[$i]) << $shift) | $carry;
$x[$i] = chr($temp & 0xFF);
$carry = $temp >> 8;
}
$carry = ($carry != 0) ? chr($carry) : '';
@@ -644,7 +680,7 @@ abstract class Engine implements \JsonSerializable
return $this->normalize($temp->powModInner($e, $n));
}
if ($this->compare($n) > 0) {
if ($this->compare($n) > 0 || $this->isNegative()) {
list(, $temp) = $this->divide($n);
return $temp->powModInner($e, $n);
}
@@ -740,11 +776,9 @@ abstract class Engine implements \JsonSerializable
*/
public static function random($size)
{
extract(static::minMaxBits($size));
/**
* @var BigInteger $min
* @var BigInteger $max
*/
$minMax = static::minMaxBits($size);
$min = $minMax['min'];
$max = $minMax['max'];
return static::randomRange($min, $max);
}
@@ -758,11 +792,9 @@ abstract class Engine implements \JsonSerializable
*/
public static function randomPrime($size)
{
extract(static::minMaxBits($size));
/**
* @var static $min
* @var static $max
*/
$minMax = static::minMaxBits($size);
$min = $minMax['min'];
$max = $minMax['max'];
return static::randomRangePrime($min, $max);
}
@@ -120,8 +120,7 @@ class GMP extends Engine
public function toBits($twos_compliment = false)
{
$hex = $this->toHex($twos_compliment);
$bits = gmp_strval(gmp_init($hex, 16), 2);
$bits = strlen($hex) ? gmp_strval(gmp_init($hex, 16), 2) : '';
if ($this->precision > 0) {
$bits = substr($bits, -$this->precision);
@@ -296,7 +295,10 @@ class GMP extends Engine
*/
public function extendedGCD(GMP $n)
{
extract(gmp_gcdext($this->value, $n->value));
$extended = gmp_gcdext($this->value, $n->value);
$g = $extended['g'];
$s = $extended['s'];
$t = $extended['t'];
return [
'gcd' => $this->normalize(new self($g)),
@@ -304,7 +304,7 @@ abstract class PHP extends Engine
* @param bool $y_negative
* @return array
*/
public static function subtractHelper(array $x_value, $x_negative, array $y_value, $y_negative)
protected static function subtractHelper(array $x_value, $x_negative, array $y_value, $y_negative)
{
$x_size = count($x_value);
$y_size = count($y_value);
@@ -533,6 +533,13 @@ abstract class PHP extends Engine
$quotient = new static();
$remainder = new static();
$quotient->value = $q;
// The common residue is the first positive modulo, so it is only the
// negative remainders that need the divisor added. A remainder of 0 is
// already the residue; adding the divisor would return the modulus
// itself, which is never a valid residue.
if ($this->is_negative && $r) {
$r = $y->value[0] - $r;
}
$remainder->value = [$r];
$quotient->is_negative = $this->is_negative != $y->is_negative;
return [$this->normalize($quotient), $this->normalize($remainder)];
@@ -664,8 +671,9 @@ abstract class PHP extends Engine
$quotient->is_negative = $x_sign != $y_sign;
// calculate the "common residue", if appropriate
if ($x_sign) {
// calculate the "common residue", if appropriate. A remainder of 0 is
// already the residue -- see divideHelper's single-digit branch.
if ($x_sign && count($x->value)) {
$y->rshift($shift);
$x = $y->subtract($x);
}
@@ -98,7 +98,9 @@ abstract class Barrett extends Base
'm1' => $m1 // m.length
];
} else {
extract($cache[self::DATA][$key]);
$cacheValues = $cache[self::DATA][$key];
$u = $cacheValues['u'];
$m1 = $cacheValues['m1'];
}
$cutoff = $m_length + ($m_length >> 1);
@@ -54,10 +54,31 @@ class Integer extends Base
/**
* Zero
*
* @var BigInteger
* @var BigInteger[]
*/
protected static $zero;
/**
* One
*
* @var BigInteger[]
*/
protected static $one;
/**
* Two
*
* @var BigInteger[]
*/
protected static $two;
/**
* Constant Time Mask
*
* @var BigInteger[]
*/
protected static $mask;
/**
* Default constructor
*
@@ -68,7 +89,7 @@ class Integer extends Base
{
$this->instanceID = $instanceID;
if (!isset($num)) {
$this->value = clone static::$zero[static::class];
$this->value = clone static::$zero[$instanceID];
} else {
$reduce = static::$reduce[$instanceID];
$this->value = $reduce($num);
@@ -84,6 +105,11 @@ class Integer extends Base
public static function setModulo($instanceID, BigInteger $modulo)
{
static::$modulo[$instanceID] = $modulo;
$one = new BigInteger(1);
static::$mask[$instanceID] = [
new BigInteger(0),
$one->bitwise_leftShift($modulo->getLength())->subtract($one)
];
}
/**
@@ -95,8 +121,8 @@ class Integer extends Base
public static function setRecurringModuloFunction($instanceID, callable $function)
{
static::$reduce[$instanceID] = $function;
if (!isset(static::$zero[static::class])) {
static::$zero[static::class] = new BigInteger();
if (!isset(static::$zero[$instanceID])) {
static::$zero[$instanceID] = new BigInteger();
}
}
@@ -107,6 +133,10 @@ class Integer extends Base
{
unset(static::$modulo[$instanceID]);
unset(static::$reduce[$instanceID]);
unset(static::$zero[$instanceID]);
unset(static::$one[$instanceID]);
unset(static::$two[$instanceID]);
unset(static::$mask[$instanceID]);
}
/**
@@ -158,6 +188,25 @@ class Integer extends Base
return $this->value->compare($x->value);
}
/**
* Conditionally add the modulus, without branching on the value.
*
* $diff must be in (-modulo, modulo). Returns $diff + modulo when $diff is
* negative, $diff otherwise.
*
* @return static
*/
private function conditionalAddModulo(BigInteger $diff)
{
$mask = static::$mask[$this->instanceID][(int) $diff->isNegative()];
$temp = new static($this->instanceID);
$temp->value = $diff->add(
static::$modulo[$this->instanceID]->bitwise_and($mask)
);
return $temp;
}
/**
* Adds two PrimeFieldIntegers.
*
@@ -167,13 +216,10 @@ class Integer extends Base
{
static::checkInstance($this, $x);
$temp = new static($this->instanceID);
$temp->value = $this->value->add($x->value);
if ($temp->value->compare(static::$modulo[$this->instanceID]) >= 0) {
$temp->value = $temp->value->subtract(static::$modulo[$this->instanceID]);
}
return $temp;
// $this->value + $x->value is in [0, 2m), so subtracting once lands in [-m, m)
return $this->conditionalAddModulo(
$this->value->add($x->value)->subtract(static::$modulo[$this->instanceID])
);
}
/**
@@ -185,13 +231,8 @@ class Integer extends Base
{
static::checkInstance($this, $x);
$temp = new static($this->instanceID);
$temp->value = $this->value->subtract($x->value);
if ($temp->value->isNegative()) {
$temp->value = $temp->value->add(static::$modulo[$this->instanceID]);
}
return $temp;
// already in (-m, m)
return $this->conditionalAddModulo($this->value->subtract($x->value));
}
/**
@@ -240,32 +281,35 @@ class Integer extends Base
*/
public function squareRoot()
{
static $one, $two;
if (!isset($one)) {
$one = new BigInteger(1);
$two = new BigInteger(2);
if (!isset(static::$one[$this->instanceID])) {
static::$one[$this->instanceID] = new BigInteger(1);
static::$two[$this->instanceID] = new BigInteger(2);
}
$reduce = static::$reduce[$this->instanceID];
$p_1 = static::$modulo[$this->instanceID]->subtract($one);
$one = &static::$one[$this->instanceID];
$two = &static::$two[$this->instanceID];
$modulo = &static::$modulo[$this->instanceID];
$reduce = &static::$reduce[$this->instanceID];
$p_1 = $modulo->subtract($one);
$q = clone $p_1;
$s = BigInteger::scan1divide($q);
list($pow) = $p_1->divide($two);
for ($z = $one; !$z->equals(static::$modulo[$this->instanceID]); $z = $z->add($one)) {
$temp = $z->powMod($pow, static::$modulo[$this->instanceID]);
for ($z = $one; !$z->equals($modulo); $z = $z->add($one)) {
$temp = $z->powMod($pow, $modulo);
if ($temp->equals($p_1)) {
break;
}
}
$m = new BigInteger($s);
$c = $z->powMod($q, static::$modulo[$this->instanceID]);
$t = $this->value->powMod($q, static::$modulo[$this->instanceID]);
$c = $z->powMod($q, $modulo);
$t = $this->value->powMod($q, $modulo);
list($temp) = $q->add($one)->divide($two);
$r = $this->value->powMod($temp, static::$modulo[$this->instanceID]);
$r = $this->value->powMod($temp, $modulo);
while (!$t->equals($one)) {
for ($i = clone $one; $i->compare($m) < 0; $i = $i->add($one)) {
if ($t->powMod($two->pow($i), static::$modulo[$this->instanceID])->equals($one)) {
if ($t->powMod($two->pow($i), $modulo)->equals($one)) {
break;
}
}
@@ -273,7 +317,7 @@ class Integer extends Base
if ($i->compare($m) == 0) {
return false;
}
$b = $c->powMod($two->pow($m->subtract($i)->subtract($one)), static::$modulo[$this->instanceID]);
$b = $c->powMod($two->pow($m->subtract($i)->subtract($one)), $modulo);
$m = $i;
$c = $reduce($b->multiply($b));
$t = $reduce($t->multiply($c));
@@ -356,6 +400,8 @@ class Integer extends Base
{
$w++;
$zero = &static::$zero[$this->instanceID];
$mask = new BigInteger((1 << $w) - 1);
$sub = new BigInteger(1 << $w);
//$sub = new BigInteger(1 << ($w - 1));
@@ -363,7 +409,7 @@ class Integer extends Base
$d_i = [];
$i = 0;
while ($d->compare(static::$zero[static::class]) > 0) {
while ($d->compare($zero) > 0) {
if ($d->isOdd()) {
// start mods
@@ -377,7 +423,7 @@ class Integer extends Base
} else {
$d_i[$i] = 0;
}
$shift = !$d->equals(static::$zero[static::class]) && $d->bitwise_and($mask)->equals(static::$zero[static::class]) ? $w : 1; // $w or $w + 1?
$shift = !$d->equals($zero) && $d->bitwise_and($mask)->equals($zero) ? $w : 1; // $w or $w + 1?
$d = $d->bitwise_rightShift($shift);
while (--$shift > 0) {
$d_i[++$i] = 0;
+302
View File
@@ -0,0 +1,302 @@
<?php
/**
* Pure-PHP implementation of SCP.
*
* PHP version 5
*
* The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $scp = new \phpseclib3\Net\SCP('www.domain.tld');
* if (!$scp->login('username', 'password')) {
* exit('Login Failed');
* }
*
* echo $scp->exec('pwd') . "\r\n";
* $scp->put('filename.ext', 'hello, world!');
* echo $scp->exec('ls -latr');
* ?>
* </code>
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2009 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib3\Net;
use phpseclib3\Exception\FileNotFoundException;
/**
* Pure-PHP implementations of SCP.
*
* @author Jim Wigginton <terrafrost@php.net>
*/
class SCP extends SSH2
{
/**
* Reads data from a local file.
*
* @see SCP::put()
*/
const SOURCE_LOCAL_FILE = 1;
/**
* Reads data from a string.
*
* @see SCP::put()
*/
// this value isn't really used anymore but i'm keeping it reserved for historical reasons
const SOURCE_STRING = 2;
/**
* SCP.php doesn't support SOURCE_CALLBACK because, with that one, we don't know the size, in advance
*/
//const SOURCE_CALLBACK = 16;
/**
* Error information
*
* @see self::getSCPErrors()
* @see self::getLastSCPError()
* @var array
*/
private $scp_errors = [];
/**
* Uploads a file to the SCP server.
*
* By default, \phpseclib\Net\SCP::put() does not read from the local filesystem. $data is dumped directly into $remote_file.
* So, for example, if you set $data to 'filename.ext' and then do \phpseclib\Net\SCP::get(), you will get a file, twelve bytes
* long, containing 'filename.ext' as its contents.
*
* Setting $mode to self::SOURCE_LOCAL_FILE will change the above behavior. With self::SOURCE_LOCAL_FILE, $remote_file will
* contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how
* large $remote_file will be, as well.
*
* Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take
* care of that, yourself.
*
* @param string $remote_file
* @param string $data
* @param int $mode
* @param callable $callback
* @return bool
* @access public
*/
public function put($remote_file, $data, $mode = self::SOURCE_STRING, $callback = null)
{
if (!($this->bitmap & self::MASK_LOGIN)) {
return false;
}
if (empty($remote_file)) {
// remote file cannot be blank
return false;
}
if (!$this->exec('scp -t ' . escapeshellarg($remote_file), false)) { // -t = to
return false;
}
$temp = $this->get_channel_packet(self::CHANNEL_EXEC, true);
if ($temp !== chr(0)) {
$this->close_channel(self::CHANNEL_EXEC, true);
return false;
}
$packet_size = $this->packet_size_client_to_server[self::CHANNEL_EXEC] - 4;
$remote_file = basename($remote_file);
$dataCallback = false;
switch (true) {
case is_resource($data):
$mode = $mode & ~self::SOURCE_LOCAL_FILE;
$info = stream_get_meta_data($data);
if (isset($info['wrapper_type']) && $info['wrapper_type'] == 'PHP' && $info['stream_type'] == 'Input') {
$fp = fopen('php://memory', 'w+');
stream_copy_to_stream($data, $fp);
rewind($fp);
} else {
$fp = $data;
}
break;
case $mode & self::SOURCE_LOCAL_FILE:
if (!is_file($data)) {
throw new FileNotFoundException("$data is not a valid file");
}
$fp = @fopen($data, 'rb');
if (!$fp) {
$this->close_channel(self::CHANNEL_EXEC, true);
return false;
}
}
if (isset($fp)) {
$stat = fstat($fp);
$size = !empty($stat) ? $stat['size'] : 0;
} else {
$size = strlen($data);
}
$sent = 0;
$size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;
$temp = 'C0644 ' . $size . ' ' . $remote_file . "\n";
$this->send_channel_packet(self::CHANNEL_EXEC, $temp);
$temp = $this->get_channel_packet(self::CHANNEL_EXEC, true);
if ($temp !== chr(0)) {
$this->close_channel(self::CHANNEL_EXEC, true);
return false;
}
$sent = 0;
while ($sent < $size) {
$temp = $mode & self::SOURCE_STRING ? substr($data, $sent, $packet_size) : fread($fp, $packet_size);
$this->send_channel_packet(self::CHANNEL_EXEC, $temp);
$sent += strlen($temp);
if (is_callable($callback)) {
call_user_func($callback, $sent);
}
}
$this->close_channel(self::CHANNEL_EXEC, true);
if ($mode != self::SOURCE_STRING) {
fclose($fp);
}
return true;
}
/**
* Downloads a file from the SCP server.
*
* Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if
* the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the
* operation
*
* @param string $remote_file
* @param string $local_file
* @return mixed
* @access public
*/
public function get($remote_file, $local_file = null, $progressCallback = null)
{
if (!($this->bitmap & self::MASK_LOGIN)) {
return false;
}
if (!$this->exec('scp -f ' . escapeshellarg($remote_file), false)) { // -f = from
return false;
}
$this->send_channel_packet(self::CHANNEL_EXEC, chr(0));
$info = $this->get_channel_packet(self::CHANNEL_EXEC, true);
// per https://goteleport.com/blog/scp-familiar-simple-insecure-slow/ non-zero responses mean there are errors
if ($info[0] === chr(1) || $info[0] == chr(2)) {
$type = $info[0] === chr(1) ? 'warning' : 'error';
$this->scp_errors[] = "$type: " . substr($info, 1);
$this->close_channel(self::CHANNEL_EXEC, true);
return false;
}
$this->send_channel_packet(self::CHANNEL_EXEC, chr(0));
if (!preg_match('#(?<perms>[^ ]+) (?<size>\d+) (?<name>.+)#', rtrim($info), $info)) {
$this->close_channel(self::CHANNEL_EXEC, true);
return false;
}
$fclose_check = false;
if (is_resource($local_file)) {
$fp = $local_file;
} elseif (!is_null($local_file)) {
$fp = @fopen($local_file, 'wb');
if (!$fp) {
$this->close_channel(self::CHANNEL_EXEC, true);
return false;
}
$fclose_check = true;
} else {
$content = '';
}
$size = 0;
while (true) {
$data = $this->get_channel_packet(self::CHANNEL_EXEC, true);
// Terminate the loop in case the server repeatedly sends an empty response
if ($data === false) {
$this->close_channel(self::CHANNEL_EXEC, true);
// no data received from server
return false;
}
// SCP usually seems to split stuff out into 16k chunks
$length = strlen($data);
$size += $length;
$end = $size > $info['size'];
if ($end) {
$diff = $size - $info['size'];
$offset = $length - $diff;
if ($data[$offset] === chr(0)) {
$data = substr($data, 0, -$diff);
} else {
$type = $data[$offset] === chr(1) ? 'warning' : 'error';
$this->scp_errors[] = "$type: " . substr($data, 1);
$this->close_channel(self::CHANNEL_EXEC, true);
return false;
}
}
if (is_null($local_file)) {
$content .= $data;
} else {
fputs($fp, $data);
}
if (is_callable($progressCallback)) {
call_user_func($progressCallback, $size);
}
if ($end) {
break;
}
}
$this->close_channel(self::CHANNEL_EXEC, true);
if ($fclose_check) {
fclose($fp);
}
// if $content isn't set that means a file was written to
return isset($content) ? $content : true;
}
/**
* Returns all errors on the SCP layer
*
* @return array
*/
public function getSCPErrors()
{
return $this->scp_errors;
}
/**
* Returns the last error on the SCP layer
*
* @return string
*/
public function getLastSCPError()
{
return count($this->scp_errors) ? $this->scp_errors[count($this->scp_errors) - 1] : '';
}
}
+241 -13
View File
@@ -35,6 +35,7 @@ namespace phpseclib3\Net;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Exception\FileNotFoundException;
use phpseclib3\Exception\TimeoutException;
/**
* Pure-PHP implementations of SFTP.
@@ -48,21 +49,21 @@ class SFTP extends SSH2
*
* \phpseclib3\Net\SSH2::exec() uses 0 and \phpseclib3\Net\SSH2::read() / \phpseclib3\Net\SSH2::write() use 1.
*
* @see \phpseclib3\Net\SSH2::send_channel_packet()
* @see \phpseclib3\Net\SSH2::get_channel_packet()
* @see SSH2::send_channel_packet()
* @see SSH2::get_channel_packet()
*/
const CHANNEL = 0x100;
/**
* Reads data from a local file.
*
* @see \phpseclib3\Net\SFTP::put()
* @see SFTP::put()
*/
const SOURCE_LOCAL_FILE = 1;
/**
* Reads data from a string.
*
* @see \phpseclib3\Net\SFTP::put()
* @see SFTP::put()
*/
// this value isn't really used anymore but i'm keeping it reserved for historical reasons
const SOURCE_STRING = 2;
@@ -70,19 +71,19 @@ class SFTP extends SSH2
* Reads data from callback:
* function callback($length) returns string to proceed, null for EOF
*
* @see \phpseclib3\Net\SFTP::put()
* @see SFTP::put()
*/
const SOURCE_CALLBACK = 16;
/**
* Resumes an upload
*
* @see \phpseclib3\Net\SFTP::put()
* @see SFTP::put()
*/
const RESUME = 4;
/**
* Append a local file to an already existing remote file
*
* @see \phpseclib3\Net\SFTP::put()
* @see SFTP::put()
*/
const RESUME_START = 8;
@@ -598,6 +599,9 @@ class SFTP extends SSH2
$response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_VERSION) {
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_VERSION. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -667,6 +671,9 @@ class SFTP extends SSH2
$this->send_sftp_packet(NET_SFTP_EXTENDED, $packet);
$response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -836,6 +843,8 @@ class SFTP extends SSH2
return false;
}
$path = (string) $path;
if (!$this->canonicalize_paths) {
if ($this->pwd === true) {
return '.';
@@ -881,6 +890,9 @@ class SFTP extends SSH2
$this->logError($response);
return false;
default:
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_NAME or NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -923,6 +935,8 @@ class SFTP extends SSH2
return false;
}
$dir = (string) $dir;
// assume current dir if $dir is empty
if ($dir === '') {
$dir = './';
@@ -932,6 +946,9 @@ class SFTP extends SSH2
}
$dir = $this->realpath($dir);
if ($dir === false) {
return false;
}
// confirm that $dir is, in fact, a valid directory
if ($this->use_stat_cache && is_array($this->query_stat_cache($dir))) {
@@ -956,6 +973,9 @@ class SFTP extends SSH2
$this->logError($response);
return false;
default:
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS' .
'Got packet type: ' . $this->packet_type);
}
@@ -1108,6 +1128,9 @@ class SFTP extends SSH2
$this->logError($response, $status);
return $status;
default:
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -1518,6 +1541,9 @@ class SFTP extends SSH2
return false;
}
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_ATTRS or NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -1585,6 +1611,9 @@ class SFTP extends SSH2
$this->logError($response);
break;
default:
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -1674,6 +1703,7 @@ class SFTP extends SSH2
* @param bool $recursive
* @throws \UnexpectedValueException on receipt of unexpected packets
* @return mixed
* @changed in phpseclib 4.0.0
*/
public function chmod($mode, $filename, $recursive = false)
{
@@ -1708,6 +1738,9 @@ class SFTP extends SSH2
return false;
}
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_ATTRS or NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -1756,6 +1789,9 @@ class SFTP extends SSH2
*/
$response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -1869,6 +1905,9 @@ class SFTP extends SSH2
$this->logError($response);
return false;
default:
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_NAME or NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -1937,6 +1976,9 @@ class SFTP extends SSH2
$response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -1997,6 +2039,9 @@ class SFTP extends SSH2
$response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -2036,6 +2081,9 @@ class SFTP extends SSH2
$response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -2159,6 +2207,9 @@ class SFTP extends SSH2
$this->logError($response);
return false;
default:
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -2301,6 +2352,9 @@ class SFTP extends SSH2
while ($i--) {
$response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -2330,6 +2384,9 @@ class SFTP extends SSH2
// -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3
$response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -2386,6 +2443,9 @@ class SFTP extends SSH2
$this->logError($response);
return false;
default:
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -2478,6 +2538,8 @@ class SFTP extends SSH2
$this->partial_init = false;
$this->init_sftp_connection();
return false;
} elseif ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
} else {
throw new \UnexpectedValueException('Expected NET_SFTP_DATA or NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
@@ -2541,6 +2603,9 @@ class SFTP extends SSH2
$response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -2726,6 +2791,9 @@ class SFTP extends SSH2
case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
return false;
default:
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -2753,6 +2821,9 @@ class SFTP extends SSH2
case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
return false;
default:
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -2993,6 +3064,9 @@ class SFTP extends SSH2
$response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -3355,8 +3429,7 @@ class SFTP extends SSH2
if (strlen($this->packet_buffer) < 4) {
throw new \RuntimeException('Packet is too small');
}
extract(unpack('Nlength', Strings::shift($this->packet_buffer, 4)));
/** @var integer $length */
$length = unpack('Nlength', Strings::shift($this->packet_buffer, 4))['length'];
$tempLength = $length;
$tempLength -= strlen($this->packet_buffer);
@@ -3386,7 +3459,7 @@ class SFTP extends SSH2
$this->packet_type = ord(Strings::shift($this->packet_buffer));
if ($this->use_request_id) {
extract(unpack('Npacket_id', Strings::shift($this->packet_buffer, 4))); // remove the request id
$packet_id = unpack('Npacket_id', Strings::shift($this->packet_buffer, 4))['packet_id']; // remove the request id
$length -= 5; // account for the request id and the packet type
} else {
$length -= 1; // account for the packet type
@@ -3456,11 +3529,11 @@ class SFTP extends SSH2
return $this->packet_type_log;
}
}
/**
* Returns all errors on the SFTP layer
*
* @return array
* @removed in phpseclib 4.0.0
*/
public function getSFTPErrors()
{
@@ -3471,6 +3544,7 @@ class SFTP extends SSH2
* Returns the last error on the SFTP layer
*
* @return string
* @removed in phpseclib 4.0.0
*/
public function getLastSFTPError()
{
@@ -3559,7 +3633,6 @@ class SFTP extends SSH2
/**
* Enable Date Preservation
*
*/
public function enableDatePreservation()
{
@@ -3568,13 +3641,110 @@ class SFTP extends SSH2
/**
* Disable Date Preservation
*
*/
public function disableDatePreservation()
{
$this->preserveTime = false;
}
/**
* Copy
*
* This method (currently) only works if the copy-data extension is available
*
* @param string $oldname
* @param string $newname
* @return bool
*/
public function copy($oldname, $newname)
{
if (!$this->precheck()) {
return false;
}
$oldname = $this->realpath($oldname);
$newname = $this->realpath($newname);
if ($oldname === false || $newname === false) {
return false;
}
if (!isset($this->extensions['copy-data']) || $this->extensions['copy-data'] !== '1') {
throw new \RuntimeException(
"Extension 'copy-data' is not supported by the server. " .
"Call getSupportedVersions() to see a list of supported extension"
);
}
$size = $this->filesize($oldname);
$packet = Strings::packSSH2('s', $oldname);
$packet .= $this->version >= 5 ?
pack('N3', 0, NET_SFTP_OPEN_OPEN_EXISTING, 0) :
pack('N2', NET_SFTP_OPEN_READ, 0);
$this->send_sftp_packet(NET_SFTP_OPEN, $packet);
$response = $this->get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
$oldhandle = substr($response, 4);
break;
case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
$this->logError($response);
return false;
default:
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
if ($this->version >= 5) {
$flags = NET_SFTP_OPEN_OPEN_OR_CREATE;
} else {
$flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE;
}
$packet = Strings::packSSH2('s', $newname);
$packet .= $this->version >= 5 ?
pack('N3', 0, $flags, 0) :
pack('N2', $flags, 0);
$this->send_sftp_packet(NET_SFTP_OPEN, $packet);
$response = $this->get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
$newhandle = substr($response, 4);
break;
case NET_SFTP_STATUS:
$this->logError($response);
return false;
default:
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_HANDLE or NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
$packet = Strings::packSSH2('ssQQsQ', 'copy-data', $oldhandle, 0, $size, $newhandle, 0);
$this->send_sftp_packet(NET_SFTP_EXTENDED, $packet);
$response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
$this->close_handle($oldhandle);
$this->close_handle($newhandle);
return true;
}
/**
* POSIX Rename
*
@@ -3606,6 +3776,9 @@ class SFTP extends SSH2
$packet = Strings::packSSH2('sss', 'posix-rename@openssh.com', $oldname, $newname);
$this->send_sftp_packet(NET_SFTP_EXTENDED, $packet);
} else {
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \RuntimeException(
"Extension 'posix-rename@openssh.com' is not supported by the server. " .
"Call getSupportedVersions() to see a list of supported extension"
@@ -3614,6 +3787,9 @@ class SFTP extends SSH2
$response = $this->get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) {
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
@@ -3666,6 +3842,9 @@ class SFTP extends SSH2
$response = $this->get_sftp_packet();
if ($this->packet_type !== NET_SFTP_EXTENDED_REPLY) {
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException(
'Expected SSH_FXP_EXTENDED_REPLY. '
. 'Got packet type: ' . $this->packet_type
@@ -3694,4 +3873,53 @@ class SFTP extends SSH2
Strings::unpackSSH2('QQQQQQQQQQQ', $response)
);
}
public function hardlink($oldpath, $newpath)
{
if (!$this->precheck()) {
return false;
}
if (!isset($this->extensions['hardlink@openssh.com']) || $this->extensions['hardlink@openssh.com'] !== '1') {
throw new \RuntimeException(
"Extension 'hardlink@openssh.com' is not supported by the server. " .
"Call getSupportedVersions() to see a list of supported extension"
);
}
$oldpath = $this->realpath($oldpath);
$newpath = $this->realpath($newpath);
if ($oldpath === false || $newpath === false) {
return false;
}
$packet = Strings::packSSH2('sss', 'hardlink@openssh.com', $oldpath, $newpath);
$this->send_sftp_packet(NET_SFTP_EXTENDED, $packet);
$response = $this->get_sftp_packet();
if ($this->packet_type !== NET_SFTP_STATUS) {
if ($this->is_timeout) {
throw new TimeoutException('Timed out waiting for SFTP packet response');
}
throw new \UnexpectedValueException('Expected NET_SFTP_STATUS. '
. 'Got packet type: ' . $this->packet_type);
}
// if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
list($status) = Strings::unpackSSH2('N', $response);
if ($status !== NET_SFTP_STATUS_OK) {
$this->logError($response, $status);
return false;
}
// this operation will change the ctime and link-count attributes
// which could be cached depending on sftp version
$this->remove_from_stat_cache($oldpath);
// hardlink creation should fail if $newpath exists;
// removing it from the cache anyway, just to be sure
$this->remove_from_stat_cache($newpath);
return true;
}
}
@@ -139,7 +139,15 @@ class Stream
protected function parse_path($path)
{
$orig = $path;
extract(parse_url($path) + ['port' => 22]);
$url = parse_url($path) + ['port' => 22];
$keys = ['scheme', 'host', 'port', 'user', 'pass', 'path', 'query', 'fragment'];
foreach ($keys as $key) {
if (isset($url[$key])) {
$$key = $url[$key];
}
}
if (isset($query)) {
$path .= '?' . $query;
} elseif (preg_match('/(\?|\?#)$/', $orig)) {
@@ -169,9 +177,9 @@ class Stream
}
$this->sftp = $host;
} else {
if (isset($this->context)) {
$context = stream_context_get_options($this->context);
}
$context = isset($this->context) ?
stream_context_get_options($this->context) :
stream_context_get_options(stream_context_get_default());
if (isset($context[$scheme]['session'])) {
$sftp = $context[$scheme]['session'];
}
+114 -48
View File
@@ -118,10 +118,10 @@ class SSH2
* open request, and 'sender channel' is the channel number allocated by
* the other side.
*
* @see \phpseclib3\Net\SSH2::send_channel_packet()
* @see \phpseclib3\Net\SSH2::get_channel_packet()
* @see SSH2::send_channel_packet()
* @see SSH2::get_channel_packet()
*/
const CHANNEL_EXEC = 1; // PuTTy uses 0x100
const CHANNEL_EXEC = 1; // PuTTY uses 0x100
const CHANNEL_SHELL = 2;
const CHANNEL_SUBSYSTEM = 3;
const CHANNEL_AGENT_FORWARD = 4;
@@ -130,13 +130,13 @@ class SSH2
/**
* Returns the message numbers
*
* @see \phpseclib3\Net\SSH2::getLog()
* @see SSH2::getLog()
*/
const LOG_SIMPLE = 1;
/**
* Returns the message content
*
* @see \phpseclib3\Net\SSH2::getLog()
* @see SSH2::getLog()
*/
const LOG_COMPLEX = 2;
/**
@@ -158,20 +158,20 @@ class SSH2
/**
* Make sure that the log never gets larger than this
*
* @see \phpseclib3\Net\SSH2::getLog()
* @see SSH2::getLog()
*/
const LOG_MAX_SIZE = 1048576; // 1024 * 1024
/**
* Returns when a string matching $expect exactly is found
*
* @see \phpseclib3\Net\SSH2::read()
* @see SSH2::read()
*/
const READ_SIMPLE = 1;
/**
* Returns when a string matching the regular expression $expect is found
*
* @see \phpseclib3\Net\SSH2::read()
* @see SSH2::read()
*/
const READ_REGEX = 2;
/**
@@ -180,7 +180,7 @@ class SSH2
* Some data packets may only contain a single character so it may be necessary
* to call read() multiple times when using this option
*
* @see \phpseclib3\Net\SSH2::read()
* @see SSH2::read()
*/
const READ_NEXT = 3;
@@ -679,7 +679,7 @@ class SSH2
* @see self::send_channel_packet()
* @var array
*/
private $packet_size_client_to_server = [];
protected $packet_size_client_to_server = [];
/**
* Message Number Log
@@ -1477,7 +1477,7 @@ class SSH2
}
if (defined('NET_SSH2_LOGGING')) {
$this->append_log('<- (network: ' . round($totalElapsed, 4) . ')', $line);
$this->append_log('<- (network: ' . round($totalElapsed, 4) . ')', $data);
}
if (feof($this->fsock)) {
@@ -1487,7 +1487,13 @@ class SSH2
$extra = $matches[1];
$this->server_identifier = trim($data, "\r\n");
// earlier the SSH specs were quoted.
// "The server MAY send other lines of data before sending the version string." they said.
// the implication of this is that the lines of data before the server string are *not* a part of it
// getting this right is important because the correct server identifier needs to be fed into the
// exchange hash for the shared keys to be calculated correctly
$data = explode("\r\n", trim($data, "\r\n"));
$this->server_identifier = $data[count($data) - 1];
if (strlen($extra)) {
$this->errors[] = $data;
}
@@ -1677,7 +1683,6 @@ class SSH2
case NET_SSH2_MSG_DISCONNECT:
return $this->handleDisconnect($kexinit_payload_server);
}
$this->kex_buffer[] = $kexinit_payload_server;
}
@@ -2936,6 +2941,9 @@ class SSH2
$this->channel_id_last_interactive = self::CHANNEL_EXEC;
return true;
}
if ($callback === false) {
return true;
}
$output = '';
while (true) {
@@ -2977,7 +2985,7 @@ class SSH2
*/
protected function open_channel($channel, $skip_extended = false)
{
if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_CLOSE) {
if (isset($this->channel_status[$channel])) {
throw new \RuntimeException('Please close the channel (' . $channel . ') before trying to open it again');
}
@@ -3346,6 +3354,27 @@ class SSH2
}
}
/**
* Send EOF on a channel
*
* Sends an EOF to the stream; this is typically used to close standard
* input, while keeping output and error alive.
*
* @param int|null $channel Channel id returned by self::getInteractiveChannelId()
* @return void
*/
public function sendEOF($channel = null)
{
if ($channel === null) {
$channel = $this->get_interactive_channel();
}
$excludeStatuses = [NET_SSH2_MSG_CHANNEL_EOF, NET_SSH2_MSG_CHANNEL_CLOSE];
if (isset($this->channel_status[$channel]) && !in_array($this->channel_status[$channel], $excludeStatuses)) {
$this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$channel]));
}
}
/**
* Is timeout?
*
@@ -3709,12 +3738,12 @@ class SSH2
substr($packet->raw, 0, -$this->hmac_size);
if (($this->hmac_check->getHash() & "\xFF\xFF\xFF\xFF") == 'umac') {
$this->hmac_check->setNonce("\0\0\0\0" . pack('N', $this->get_seq_no));
if ($hmac != $this->hmac_check->hash($reconstructed)) {
if (!hash_equals($hmac, $this->hmac_check->hash($reconstructed))) {
$this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR);
throw new ConnectionClosedException('Invalid UMAC');
}
} else {
if ($hmac != $this->hmac_check->hash(pack('Na*', $this->get_seq_no, $reconstructed))) {
if (!hash_equals($hmac, $this->hmac_check->hash(pack('Na*', $this->get_seq_no, $reconstructed)))) {
$this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR);
throw new ConnectionClosedException('Invalid HMAC');
}
@@ -3722,7 +3751,7 @@ class SSH2
}
$padding_length = 0;
$payload = $packet->plain;
extract(unpack('Cpadding_length', Strings::shift($payload, 1)));
$padding_length = unpack('Cpadding_length', Strings::shift($payload, 1))['padding_length'];
if ($padding_length > 0) {
Strings::pop($payload, $padding_length);
}
@@ -3788,8 +3817,7 @@ class SSH2
$this->key_exchange();
}
// don't filter if we're in the middle of a key exchange (since _filter might send out packets)
return $this->keyExchangeInProgress ? $payload : $this->filter($payload);
return $this->filter($payload);
}
/**
@@ -3809,13 +3837,13 @@ class SSH2
switch ($this->decryptName) {
case 'aes128-gcm@openssh.com':
case 'aes256-gcm@openssh.com':
extract(unpack('Npacket_length', substr($packet->raw, 0, $packet_length_header_size)));
$packet_length = unpack('Npacket_length', substr($packet->raw, 0, $packet_length_header_size))['packet_length'];
$packet->size = $packet_length_header_size + $packet_length + $this->decrypt_block_size; // expect tag
break;
case 'chacha20-poly1305@openssh.com':
$this->lengthDecrypt->setNonce(pack('N2', 0, $this->get_seq_no));
$packet_length_header = $this->lengthDecrypt->decrypt(substr($packet->raw, 0, $packet_length_header_size));
extract(unpack('Npacket_length', $packet_length_header));
$packet_length = unpack('Npacket_length', $packet_length_header)['packet_length'];
$packet->size = $packet_length_header_size + $packet_length + 16; // expect tag
break;
default:
@@ -3824,17 +3852,17 @@ class SSH2
return;
}
$packet->plain = $this->decrypt->decrypt(substr($packet->raw, 0, $this->decrypt_block_size));
extract(unpack('Npacket_length', Strings::shift($packet->plain, $packet_length_header_size)));
$packet_length = unpack('Npacket_length', Strings::shift($packet->plain, $packet_length_header_size))['packet_length'];
$packet->size = $packet_length_header_size + $packet_length;
$added_validation_length = $packet_length_header_size;
} else {
extract(unpack('Npacket_length', substr($packet->raw, 0, $packet_length_header_size)));
$packet_length = unpack('Npacket_length', substr($packet->raw, 0, $packet_length_header_size))['packet_length'];
$packet->size = $packet_length_header_size + $packet_length;
}
break;
}
} else {
extract(unpack('Npacket_length', substr($packet->raw, 0, $packet_length_header_size)));
$packet_length = unpack('Npacket_length', substr($packet->raw, 0, $packet_length_header_size))['packet_length'];
$packet->size = $packet_length_header_size + $packet_length;
$added_validation_length = $packet_length_header_size;
}
@@ -3884,9 +3912,15 @@ class SSH2
*/
private function filter($payload)
{
switch (ord($payload[0])) {
case NET_SSH2_MSG_DISCONNECT:
if (ord($payload[0]) == NET_SSH2_MSG_DISCONNECT) {
return $this->handleDisconnect($payload);
}
if ($this->session_id === false && $this->keyExchangeInProgress) {
return $payload;
}
switch (ord($payload[0])) {
case NET_SSH2_MSG_IGNORE:
$payload = $this->get_binary_packet();
break;
@@ -3900,7 +3934,7 @@ class SSH2
break; // return payload
case NET_SSH2_MSG_KEXINIT:
// this is here for server initiated key re-exchanges after the initial key exchange
if ($this->session_id !== false) {
if (!$this->keyExchangeInProgress && $this->session_id !== false) {
if (!$this->key_exchange($payload)) {
$this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
throw new ConnectionClosedException('Key exchange failed');
@@ -3920,6 +3954,26 @@ class SSH2
$payload = $this->get_binary_packet();
}
/*
Once a party has sent a SSH_MSG_KEXINIT message for key exchange or
re-exchange, until it has sent a SSH_MSG_NEWKEYS message (Section
7.3), it MUST NOT send any messages other than:
o Transport layer generic messages (1 to 19) (but
SSH_MSG_SERVICE_REQUEST and SSH_MSG_SERVICE_ACCEPT MUST NOT be
sent);
o Algorithm negotiation messages (20 to 29) (but further
SSH_MSG_KEXINIT messages MUST NOT be sent);
o Specific key exchange method messages (30 to 49).
-- https://www.rfc-editor.org/rfc/rfc4253#section-7.1
*/
if ($this->keyExchangeInProgress) {
return $payload;
}
// see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in
if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
Strings::shift($payload, 1);
@@ -3932,7 +3986,10 @@ class SSH2
switch (ord($payload[0])) {
case NET_SSH2_MSG_CHANNEL_REQUEST:
if (strlen($payload) == 31) {
extract(unpack('cpacket_type/Nchannel/Nlength', $payload));
$unpacked = unpack('cpacket_type/Nchannel/Nlength', $payload);
$packet_type = $unpacked['packet_type'];
$channel = $unpacked['channel'];
$length = $unpacked['length'];
if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) {
if (ord(substr($payload, 9 + $length))) { // want reply
$this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel]));
@@ -3943,9 +4000,11 @@ class SSH2
break;
case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
Strings::shift($payload, 1);
list($request_name) = Strings::unpackSSH2('s', $payload);
list($request_name, $want_reply) = Strings::unpackSSH2('sb', $payload);
$this->errors[] = "SSH_MSG_GLOBAL_REQUEST: $request_name";
if ($want_reply) {
$this->send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE));
}
$payload = $this->get_binary_packet();
break;
case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
@@ -4096,7 +4155,8 @@ class SSH2
protected function get_channel_packet($client_channel, $skip_extended = false)
{
if (!empty($this->channel_buffers[$client_channel])) {
switch ($this->channel_status[$client_channel]) {
// in phpseclib 4.0 this should be changed to $this->channel_status[$client_channel] ?? null
switch (isset($this->channel_status[$client_channel]) ? $this->channel_status[$client_channel] : null) {
case NET_SSH2_MSG_CHANNEL_REQUEST:
foreach ($this->channel_buffers[$client_channel] as $i => $packet) {
switch (ord($packet[0])) {
@@ -4164,7 +4224,7 @@ class SSH2
continue 2;
case NET_SSH2_MSG_CHANNEL_REQUEST:
if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) {
if (!isset($this->channel_status[$channel])) {
continue 2;
}
list($value) = Strings::unpackSSH2('s', $response);
@@ -4181,12 +4241,14 @@ class SSH2
if (strlen($error_message)) {
$this->errors[count($this->errors) - 1] .= "\r\n$error_message";
}
$this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_CLOSE) {
if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
$this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$channel]));
}
$this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
$this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF;
$this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
}
continue 3;
case 'exit-status':
list(, $this->exit_status) = Strings::unpackSSH2('CN', $response);
@@ -4196,8 +4258,13 @@ class SSH2
continue 3;
default:
// "Some systems may not implement signals, in which case they SHOULD ignore this message."
// -- http://tools.ietf.org/html/rfc4254#section-6.9
list($want_reply) = Strings::unpackSSH2('b', $response);
if ($want_reply) {
// "If the request is not recognized or is not supported for the channel,
// SSH_MSG_CHANNEL_FAILURE is returned."
// -- https://datatracker.ietf.org/doc/html/rfc4254#page-10
$this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_FAILURE, $this->server_channels[$channel]));
}
continue 3;
}
}
@@ -4286,11 +4353,11 @@ class SSH2
$this->close_channel_bitmap($channel);
if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_CLOSE) {
$this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
}
$this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
unset($this->channel_status[$channel]);
$this->channelCount--;
if ($client_channel == $channel) {
@@ -4515,7 +4582,7 @@ class SSH2
protected function append_log_helper($constant, $message_number, $message, array &$message_number_log, array &$message_log, &$log_size, &$realtime_log_file, &$realtime_log_wrap, &$realtime_log_size)
{
// remove the byte identifying the message type from all but the first two messages (ie. the identification strings)
if (strlen($message_number) > 2) {
if (!in_array(substr($message_number, 0, 4), ['<- (', '-> (']) && strlen($message_number) > 2) {
Strings::shift($message);
}
@@ -4655,27 +4722,24 @@ class SSH2
* @param bool $want_reply
* @return void
*/
private function close_channel($client_channel, $want_reply = false)
protected function close_channel($client_channel)
{
// see http://tools.ietf.org/html/rfc4254#section-5.3
if ($this->channel_status[$client_channel] != NET_SSH2_MSG_CHANNEL_EOF) {
$this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
if (!$want_reply) {
$this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
}
$this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
$this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
$this->channelCount--;
$this->curTimeout = 5;
while (!is_bool($this->get_channel_packet($client_channel))) {
}
if ($want_reply) {
$this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
}
unset($this->channel_status[$client_channel]);
$this->close_channel_bitmap($client_channel);
}
@@ -4849,6 +4913,7 @@ class SSH2
* If you are looking for messages from the SFTP layer, please see SFTP::getSFTPErrors()
*
* @return string[]
* @removed in phpseclib 4.0.0
*/
public function getErrors()
{
@@ -4861,6 +4926,7 @@ class SSH2
* If you are looking for messages from the SFTP layer, please see SFTP::getLastSFTPError()
*
* @return string
* @removed in phpseclib 4.0.0
*/
public function getLastError()
{
@@ -35,7 +35,6 @@ namespace phpseclib3\System\SSH;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\PublicKey;
use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Crypt\RSA;
use phpseclib3\Exception\BadConfigurationException;
use phpseclib3\Net\SSH2;
use phpseclib3\System\SSH\Agent\Identity;
+3
View File
@@ -2,5 +2,8 @@
HOME = .
RANDFILE = $ENV::HOME/.rnd
# without this line you'll get this error with openssl_pkey_new():
# Warning: openssl_pkey_new(): Private key length must be at least 384 bits, configured to 0
default_bits = 384
[ v3_ca ]