diff --git a/composer.json b/composer.json index 581f05e334..3c4eb61eb7 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "michelf/php-markdown": "^1.8.0", "openid/php-openid": "^2.3", "paragonie/constant_time_encoding": "^1.0.4", - "phpseclib/phpseclib": "^2.0.19", + "phpseclib/phpseclib": "dev-master#f815e43077da67d3dd5b4d18a45753f5b79c1ab9", "stomp-php/stomp-php": "^4.5.1" }, "require-dev": { diff --git a/composer.lock b/composer.lock index f1105b4724..670b7973a9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1511cdda74eee145816ad9fe85138588", + "content-hash": "ac49a57ede587e949b9bad4b3c080a1d", "packages": [ { "name": "apereo/phpcas", @@ -461,21 +461,72 @@ "time": "2018-04-30T17:57:16+00:00" }, { - "name": "phpseclib/phpseclib", - "version": "2.0.19", + "name": "paragonie/random_compat", + "version": "v2.0.18", "source": { "type": "git", - "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "d2085db7b7394baa071a69c8f9159723c250f2ba" + "url": "https://github.com/paragonie/random_compat.git", + "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/d2085db7b7394baa071a69c8f9159723c250f2ba", - "reference": "d2085db7b7394baa071a69c8f9159723c250f2ba", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", + "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "time": "2019-01-03T20:59:08+00:00" + }, + { + "name": "phpseclib/phpseclib", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "f815e43077da67d3dd5b4d18a45753f5b79c1ab9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/f815e43077da67d3dd5b4d18a45753f5b79c1ab9", + "reference": "f815e43077da67d3dd5b4d18a45753f5b79c1ab9", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1", + "paragonie/random_compat": "^1.4|^2.0", + "php": ">=5.6.1" }, "require-dev": { "phing/phing": "~2.7", @@ -550,7 +601,7 @@ "x.509", "x509" ], - "time": "2019-06-20T03:34:11+00:00" + "time": "2019-06-23T16:33:59+00:00" }, { "name": "stomp-php/stomp-php", @@ -5822,7 +5873,9 @@ ], "aliases": [], "minimum-stability": "dev", - "stability-flags": [], + "stability-flags": { + "phpseclib/phpseclib": 20 + }, "prefer-stable": true, "prefer-lowest": false, "platform": { diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index b646a79831..8afc77cf8e 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -474,10 +474,22 @@ return array( 'phpseclib\\Crypt\\RC2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RC2.php', 'phpseclib\\Crypt\\RC4' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RC4.php', 'phpseclib\\Crypt\\RSA' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA.php', + 'phpseclib\\Crypt\\RSA\\MSBLOB' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/MSBLOB.php', + 'phpseclib\\Crypt\\RSA\\OpenSSH' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/OpenSSH.php', + 'phpseclib\\Crypt\\RSA\\PKCS' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/PKCS.php', + 'phpseclib\\Crypt\\RSA\\PKCS1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/PKCS1.php', + 'phpseclib\\Crypt\\RSA\\PKCS8' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/PKCS8.php', + 'phpseclib\\Crypt\\RSA\\PuTTY' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/PuTTY.php', + 'phpseclib\\Crypt\\RSA\\Raw' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Raw.php', + 'phpseclib\\Crypt\\RSA\\XML' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/XML.php', 'phpseclib\\Crypt\\Random' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php', 'phpseclib\\Crypt\\Rijndael' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php', 'phpseclib\\Crypt\\TripleDES' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php', 'phpseclib\\Crypt\\Twofish' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php', + 'phpseclib\\Exception\\BadConfigurationException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/BadConfigurationException.php', + 'phpseclib\\Exception\\FileNotFoundException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/FileNotFoundException.php', + 'phpseclib\\Exception\\NoSupportedAlgorithmsException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/NoSupportedAlgorithmsException.php', + 'phpseclib\\Exception\\UnsupportedAlgorithmException' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Exception/UnsupportedAlgorithmException.php', 'phpseclib\\File\\ANSI' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ANSI.php', 'phpseclib\\File\\ASN1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1.php', 'phpseclib\\File\\ASN1\\Element' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php', diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index 18d5e4a3c7..0698628c17 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -6,6 +6,7 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + '5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php', '2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', '757772e28a0943a9afe83def8db95bdf' => $vendorDir . '/mf2/mf2/Mf2/Parser.php', 'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php', diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 8c6336129e..d560ecb13b 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -7,6 +7,7 @@ namespace Composer\Autoload; class ComposerStaticInit444c3f31864f68a3f466e2c19837e185 { public static $files = array ( + '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php', '2cffec82183ee1cea088009cef9a6fc3' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', '757772e28a0943a9afe83def8db95bdf' => __DIR__ . '/..' . '/mf2/mf2/Mf2/Parser.php', 'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php', @@ -542,10 +543,22 @@ class ComposerStaticInit444c3f31864f68a3f466e2c19837e185 'phpseclib\\Crypt\\RC2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RC2.php', 'phpseclib\\Crypt\\RC4' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RC4.php', 'phpseclib\\Crypt\\RSA' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA.php', + 'phpseclib\\Crypt\\RSA\\MSBLOB' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/MSBLOB.php', + 'phpseclib\\Crypt\\RSA\\OpenSSH' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/OpenSSH.php', + 'phpseclib\\Crypt\\RSA\\PKCS' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/PKCS.php', + 'phpseclib\\Crypt\\RSA\\PKCS1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/PKCS1.php', + 'phpseclib\\Crypt\\RSA\\PKCS8' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/PKCS8.php', + 'phpseclib\\Crypt\\RSA\\PuTTY' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/PuTTY.php', + 'phpseclib\\Crypt\\RSA\\Raw' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/Raw.php', + 'phpseclib\\Crypt\\RSA\\XML' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA/XML.php', 'phpseclib\\Crypt\\Random' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php', 'phpseclib\\Crypt\\Rijndael' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php', 'phpseclib\\Crypt\\TripleDES' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php', 'phpseclib\\Crypt\\Twofish' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php', + 'phpseclib\\Exception\\BadConfigurationException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/BadConfigurationException.php', + 'phpseclib\\Exception\\FileNotFoundException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/FileNotFoundException.php', + 'phpseclib\\Exception\\NoSupportedAlgorithmsException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/NoSupportedAlgorithmsException.php', + 'phpseclib\\Exception\\UnsupportedAlgorithmException' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Exception/UnsupportedAlgorithmException.php', 'phpseclib\\File\\ANSI' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ANSI.php', 'phpseclib\\File\\ASN1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1.php', 'phpseclib\\File\\ASN1\\Element' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 20a413de43..34b0a044c4 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -470,22 +470,75 @@ ] }, { - "name": "phpseclib/phpseclib", - "version": "2.0.19", - "version_normalized": "2.0.19.0", + "name": "paragonie/random_compat", + "version": "v2.0.18", + "version_normalized": "2.0.18.0", "source": { "type": "git", - "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "d2085db7b7394baa071a69c8f9159723c250f2ba" + "url": "https://github.com/paragonie/random_compat.git", + "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/d2085db7b7394baa071a69c8f9159723c250f2ba", - "reference": "d2085db7b7394baa071a69c8f9159723c250f2ba", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", + "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "time": "2019-01-03T20:59:08+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ] + }, + { + "name": "phpseclib/phpseclib", + "version": "dev-master", + "version_normalized": "9999999-dev", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "f815e43077da67d3dd5b4d18a45753f5b79c1ab9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/f815e43077da67d3dd5b4d18a45753f5b79c1ab9", + "reference": "f815e43077da67d3dd5b4d18a45753f5b79c1ab9", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1", + "paragonie/random_compat": "^1.4|^2.0", + "php": ">=5.6.1" }, "require-dev": { "phing/phing": "~2.7", @@ -499,7 +552,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": "2019-06-20T03:34:11+00:00", + "time": "2019-06-23T16:33:59+00:00", "type": "library", "installation-source": "dist", "autoload": { diff --git a/vendor/phpseclib/phpseclib/README.md b/vendor/phpseclib/phpseclib/README.md index 4d8f8dd79a..4c9af456fc 100644 --- a/vendor/phpseclib/phpseclib/README.md +++ b/vendor/phpseclib/phpseclib/README.md @@ -1,43 +1,22 @@ # phpseclib - PHP Secure Communications Library -[![Build Status](https://travis-ci.org/phpseclib/phpseclib.svg?branch=2.0)](https://travis-ci.org/phpseclib/phpseclib) +[![Build Status](https://travis-ci.org/phpseclib/phpseclib.svg?branch=master)](https://travis-ci.org/phpseclib/phpseclib) MIT-licensed pure-PHP implementations of an arbitrary-precision integer arithmetic library, fully PKCS#1 (v2.1) compliant RSA, DES, 3DES, RC4, Rijndael, AES, Blowfish, Twofish, SSH-1, SSH-2, SFTP, and X.509 +* [Download (1.0.2)](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.2.zip/download) * [Browse Git](https://github.com/phpseclib/phpseclib) -* [Code Coverage Report](https://coverage.phpseclib.org/2.0/latest/) +* [Code Coverage Report](http://phpseclib.bantux.org/code_coverage/master/latest/) + +PEAR Channel +PEAR Channel: [phpseclib.sourceforge.net](http://phpseclib.sourceforge.net/pear.htm) ## Documentation * [Documentation / Manual](http://phpseclib.sourceforge.net/) -* [API Documentation](https://api.phpseclib.org/2.0/) (generated by Sami) - -## Branches - -### master - -* Development Branch -* Unstable API -* Do not use in production - -### 2.0 - -* Long term support (LTS) release -* Modernized version of 1.0 -* Minimum PHP version: 5.3.3 -* PSR-4 autoloading with namespace rooted at `\phpseclib` -* Install via Composer: `composer require phpseclib/phpseclib ~2.0` - -### 1.0 - -* Long term support (LTS) release -* PHP4 compatible -* Composer compatible (PSR-0 autoloading) -* Install using Composer: `composer require phpseclib/phpseclib ~1.0` -* Install using PEAR: See [phpseclib PEAR Channel Documentation](http://phpseclib.sourceforge.net/pear.htm) -* [Download 1.0.16 as ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.16.zip/download) +* [API Documentation](http://phpseclib.bantux.org/api/master/) (generated by Sami) ## Support @@ -47,29 +26,40 @@ Need Support? * [Create a Support Ticket on GitHub](https://github.com/phpseclib/phpseclib/issues/new) * [Browse the Support Forum](http://www.frostjedi.com/phpbb/viewforum.php?f=46) (no longer in use) +## Installing Development Dependencies + +Dependencies are managed via Composer. + +1. Download the [`composer.phar`](https://getcomposer.org/composer.phar) executable as per the + [Composer Download Instructions](https://getcomposer.org/download/), e.g. by running + + ``` sh + curl -sS https://getcomposer.org/installer | php + ``` + +2. Install Dependencies + + ``` sh + php composer.phar install + ``` + ## Contributing 1. Fork the Project -2. Ensure you have Composer installed (see [Composer Download Instructions](https://getcomposer.org/download/)) +2. Install Development Dependencies -3. Install Development Dependencies +3. Create a Feature Branch - ``` sh - composer install - ``` - -4. Create a Feature Branch - -5. (Recommended) Run the Test Suite +4. (Recommended) Run the Test Suite ``` sh vendor/bin/phpunit ``` -6. (Recommended) Check whether your code conforms to our Coding Standards by running +5. (Recommended) Check whether your code conforms to our Coding Standards by running ``` sh vendor/bin/phing -f build/build.xml sniff ``` -7. Send us a Pull Request +6. Send us a Pull Request diff --git a/vendor/phpseclib/phpseclib/appveyor.yml b/vendor/phpseclib/phpseclib/appveyor.yml deleted file mode 100644 index 210a903470..0000000000 --- a/vendor/phpseclib/phpseclib/appveyor.yml +++ /dev/null @@ -1,27 +0,0 @@ -build: false -shallow_clone: false -platform: - - x86 - - x64 -clone_folder: C:\projects\phpseclib - -install: - - cinst -y OpenSSL.Light - - SET PATH=C:\Program Files\OpenSSL;%PATH% - - sc config wuauserv start= auto - - net start wuauserv - - cinst -y php --version 5.6.30 - - cd c:\tools\php56 - - copy php.ini-production php.ini - - echo date.timezone="UTC" >> php.ini - - echo extension_dir=ext >> php.ini - - echo extension=php_openssl.dll >> php.ini - - echo extension=php_gmp.dll >> php.ini - - cd C:\projects\phpseclib - - SET PATH=C:\tools\php56;%PATH% - - php.exe -r "readfile('http://getcomposer.org/installer');" | php.exe - - php.exe composer.phar install --prefer-source --no-interaction - -test_script: - - cd C:\projects\phpseclib - - vendor\bin\phpunit.bat tests/Windows32Test.php \ No newline at end of file diff --git a/vendor/phpseclib/phpseclib/composer.json b/vendor/phpseclib/phpseclib/composer.json index b4e8a1c9c9..9f728103ac 100644 --- a/vendor/phpseclib/phpseclib/composer.json +++ b/vendor/phpseclib/phpseclib/composer.json @@ -51,11 +51,13 @@ } ], "require": { + "paragonie/constant_time_encoding": "^1|^2", + "paragonie/random_compat": "^1.4|^2.0", "php": ">=5.3.3" }, "require-dev": { "phing/phing": "~2.7", - "phpunit/phpunit": "^4.8.35|^5.7|^6.0", + "phpunit/phpunit": "~4.0", "sami/sami": "~2.0", "squizlabs/php_codesniffer": "~2.0" }, diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php index 7d8cb8b034..8521eb5e5c 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php @@ -66,30 +66,32 @@ class AES extends Rijndael * @see \phpseclib\Crypt\Rijndael::setBlockLength() * @access public * @param int $length + * @throws \BadMethodCallException anytime it's called */ function setBlockLength($length) { - return; + throw new \BadMethodCallException('The block length cannot be set for AES.'); } /** * Sets the key length * - * Valid key lengths are 128, 192, and 256. If the length is less than 128, it will be rounded up to - * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount. + * Valid key lengths are 128, 192, and 256. Set the link to bool(false) to disable a fixed key length * * @see \phpseclib\Crypt\Rijndael:setKeyLength() * @access public * @param int $length + * @throws \LengthException if the key length isn't supported */ function setKeyLength($length) { switch ($length) { - case 160: - $length = 192; + case 128: + case 192: + case 256: break; - case 224: - $length = 256; + default: + throw new \LengthException('Key of size ' . $length . ' not supported by this algorithm. Only keys of sizes 128, 192 or 256 supported'); } parent::setKeyLength($length); } @@ -103,24 +105,19 @@ class AES extends Rijndael * @see setKeyLength() * @access public * @param string $key + * @throws \LengthException if the key length isn't supported */ function setKey($key) { - parent::setKey($key); - - if (!$this->explicit_key_length) { - $length = strlen($key); - switch (true) { - case $length <= 16: - $this->key_length = 16; - break; - case $length <= 24: - $this->key_length = 24; - break; - default: - $this->key_length = 32; - } - $this->_setEngine(); + switch (strlen($key)) { + case 16: + case 24: + case 32: + break; + default: + throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported'); } + + parent::setKey($key); } } diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php index 03b176e7bf..e3cc7b87bb 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php @@ -76,10 +76,6 @@ abstract class Base * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 */ const MODE_CFB = 3; - /** - * Encrypt / decrypt using the Cipher Feedback mode (8bit) - */ - const MODE_CFB8 = 38; /** * Encrypt / decrypt using the Output Feedback mode. * @@ -143,7 +139,7 @@ abstract class Base * @var string * @access private */ - var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + var $key = false; /** * The Initialization Vector @@ -152,7 +148,7 @@ abstract class Base * @var string * @access private */ - var $iv; + var $iv = false; /** * A "sliding" Initialization Vector @@ -433,15 +429,6 @@ abstract class Base */ var $openssl_options; - /** - * Has the key length explicitly been set or should it be derived from the key, itself? - * - * @see self::setKeyLength() - * @var bool - * @access private - */ - var $explicit_key_length = false; - /** * Don't truncate / null pad key * @@ -452,9 +439,16 @@ abstract class Base var $skip_key_adjustment = false; /** - * Default Constructor. + * Has the key length explicitly been set or should it be derived from the key, itself? * - * Determines whether or not the mcrypt extension should be used. + * @see self::setKeyLength() + * @var bool + * @access private + */ + var $explicit_key_length = false; + + /** + * Default Constructor. * * $mode could be: * @@ -468,54 +462,59 @@ abstract class Base * * - self::MODE_OFB * - * If not explicitly set, self::MODE_CBC will be used. - * * @param int $mode * @access public + * @throws \InvalidArgumentException if an invalid / unsupported mode is provided */ - function __construct($mode = self::MODE_CBC) + function __construct($mode) { // $mode dependent settings switch ($mode) { case self::MODE_ECB: + case self::MODE_CBC: $this->paddable = true; - $this->mode = self::MODE_ECB; break; case self::MODE_CTR: case self::MODE_CFB: - case self::MODE_CFB8: case self::MODE_OFB: case self::MODE_STREAM: - $this->mode = $mode; + $this->paddable = false; break; - case self::MODE_CBC: default: - $this->paddable = true; - $this->mode = self::MODE_CBC; + throw new \InvalidArgumentException('No valid mode has been specified'); } - $this->_setEngine(); + $this->mode = $mode; // Determining whether inline crypting can be used by the cipher - if ($this->use_inline_crypt !== false) { - $this->use_inline_crypt = version_compare(PHP_VERSION, '5.3.0') >= 0 || function_exists('create_function'); + if ($this->use_inline_crypt !== false && function_exists('create_function')) { + $this->use_inline_crypt = true; } } /** - * Sets the initialization vector. (optional) + * Sets the initialization vector. * - * SetIV is not required when self::MODE_ECB (or ie for AES: \phpseclib\Crypt\AES::MODE_ECB) is being used. If not explicitly set, it'll be assumed - * to be all zero's. + * setIV() is not required when self::MODE_ECB (or ie for AES: \phpseclib\Crypt\AES::MODE_ECB) is being used. * * @access public * @param string $iv + * @throws \LengthException if the IV length isn't equal to the block size + * @throws \InvalidArgumentException if an IV is provided when one shouldn't be * @internal Can be overwritten by a sub class, but does not have to be */ function setIV($iv) { if ($this->mode == self::MODE_ECB) { - return; + throw new \InvalidArgumentException('This mode does not require an IV.'); + } + + if ($this->mode == self::MODE_STREAM && $this->usesIV()) { + throw new \InvalidArgumentException('This algorithm does not use an IV.'); + } + + if (strlen($iv) != $this->block_size) { + throw new \LengthException('Received initialization vector of size ' . strlen($iv) . ', but size ' . $this->block_size . ' is required'); } $this->iv = $iv; @@ -523,18 +522,14 @@ abstract class Base } /** - * Sets the key length. - * - * Keys with explicitly set lengths need to be treated accordingly + * Returns whether or not the algorithm uses an IV * * @access public - * @param int $length + * @return bool */ - function setKeyLength($length) + function usesIV() { - $this->explicit_key_length = true; - $this->changed = true; - $this->_setEngine(); + return true; } /** @@ -559,6 +554,24 @@ abstract class Base return $this->block_size << 3; } + /** + * Sets the key length. + * + * Keys with explicitly set lengths need to be treated accordingly + * + * @access public + * @param int $length + */ + function setKeyLength($length) + { + $this->explicit_key_length = $length >> 3; + + if (is_string($this->key) && strlen($this->key) != $this->explicit_key_length) { + $this->key = false; + throw new \LengthException('Key has already been set and is not ' .$this->explicit_key_length . ' bytes long'); + } + } + /** * Sets the key. * @@ -575,12 +588,12 @@ abstract class Base */ function setKey($key) { - if (!$this->explicit_key_length) { - $this->setKeyLength(strlen($key) << 3); - $this->explicit_key_length = false; + if ($this->explicit_key_length !== false && strlen($key) != $this->explicit_key_length) { + throw new \LengthException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . strlen($key) . ' bytes'); } $this->key = $key; + $this->key_length = strlen($key); $this->changed = true; $this->_setEngine(); } @@ -597,6 +610,7 @@ abstract class Base * @see Crypt/Hash.php * @param string $password * @param string $method + * @throws \LengthException if pbkdf1 is being used and the derived key length exceeds the hash length * @return bool * @access public * @internal Could, but not must, extend by the child Crypt_* class @@ -623,7 +637,8 @@ abstract class Base if (isset($func_args[5])) { $dkLen = $func_args[5]; } else { - $dkLen = $method == 'pbkdf1' ? 2 * $this->key_length : $this->key_length; + $key_length = $this->explicit_key_length !== false ? $this->explicit_key_length : $this->key_length; + $dkLen = $method == 'pbkdf1' ? 2 * $key_length : $key_length; } switch (true) { @@ -631,8 +646,7 @@ abstract class Base $hashObj = new Hash(); $hashObj->setHash($hash); if ($dkLen > $hashObj->getLength()) { - user_error('Derived key too long'); - return false; + throw new \LengthException('Derived key length cannot be longer than the hash length'); } $t = $password . $salt; for ($i = 0; $i < $count; ++$i) { @@ -649,10 +663,10 @@ abstract class Base case !function_exists('hash_algos'): case !in_array($hash, hash_algos()): $i = 1; - $hmac = new Hash(); - $hmac->setHash($hash); - $hmac->setKey($password); while (strlen($key) < $dkLen) { + $hmac = new Hash(); + $hmac->setHash($hash); + $hmac->setKey($password); $f = $u = $hmac->hash($salt . pack('N', $i++)); for ($j = 2; $j <= $count; ++$j) { $u = $hmac->hash($u); @@ -707,7 +721,7 @@ abstract class Base case self::MODE_STREAM: return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); case self::MODE_ECB: - $result = @openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); + $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); return !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result; case self::MODE_CBC: $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV); @@ -767,16 +781,6 @@ abstract class Base $iv = substr($ciphertext, -$this->block_size); } - return $ciphertext; - case self::MODE_CFB8: - $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV); - if ($this->continuousBuffer) { - if (($len = strlen($ciphertext)) >= $this->block_size) { - $this->encryptIV = substr($ciphertext, -$this->block_size); - } else { - $this->encryptIV = substr($this->encryptIV, $len - $this->block_size) . substr($ciphertext, -$len); - } - } return $ciphertext; case self::MODE_OFB: return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer); @@ -789,7 +793,7 @@ abstract class Base $this->changed = false; } if ($this->enchanged) { - @mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + mcrypt_generic_init($this->enmcrypt, $this->key, $this->_getIV($this->encryptIV)); $this->enchanged = false; } @@ -822,15 +826,15 @@ abstract class Base if ($len >= $block_size) { if ($this->enbuffer['enmcrypt_init'] === false || $len > $this->cfb_init_len) { if ($this->enbuffer['enmcrypt_init'] === true) { - @mcrypt_generic_init($this->enmcrypt, $this->key, $iv); + mcrypt_generic_init($this->enmcrypt, $this->key, $iv); $this->enbuffer['enmcrypt_init'] = false; } - $ciphertext.= @mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size)); + $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size)); $iv = substr($ciphertext, -$block_size); $len%= $block_size; } else { while ($len >= $block_size) { - $iv = @mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size); + $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size); $ciphertext.= $iv; $len-= $block_size; $i+= $block_size; @@ -839,7 +843,7 @@ abstract class Base } if ($len) { - $iv = @mcrypt_generic($this->ecb, $iv); + $iv = mcrypt_generic($this->ecb, $iv); $block = $iv ^ substr($plaintext, -$len); $iv = substr_replace($iv, $block, 0, $len); $ciphertext.= $block; @@ -849,10 +853,10 @@ abstract class Base return $ciphertext; } - $ciphertext = @mcrypt_generic($this->enmcrypt, $plaintext); + $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); if (!$this->continuousBuffer) { - @mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + mcrypt_generic_init($this->enmcrypt, $this->key, $this->_getIV($this->encryptIV)); } return $ciphertext; @@ -957,24 +961,6 @@ abstract class Base $pos = $len; } break; - case self::MODE_CFB8: - $ciphertext = ''; - $len = strlen($plaintext); - $iv = $this->encryptIV; - - for ($i = 0; $i < $len; ++$i) { - $ciphertext .= ($c = $plaintext[$i] ^ $this->_encryptBlock($iv)); - $iv = substr($iv, 1) . $c; - } - - if ($this->continuousBuffer) { - if ($len >= $block_size) { - $this->encryptIV = substr($ciphertext, -$block_size); - } else { - $this->encryptIV = substr($this->encryptIV, $len - $block_size) . substr($ciphertext, -$len); - } - } - break; case self::MODE_OFB: $xor = $this->encryptIV; if (strlen($buffer['xor'])) { @@ -1019,14 +1005,13 @@ abstract class Base * @access public * @param string $ciphertext * @return string $plaintext + * @throws \LengthException if we're inside a block cipher and the ciphertext length is not a multiple of the block size * @internal Could, but not must, extend by the child Crypt_* class */ function decrypt($ciphertext) { - if ($this->paddable) { - // we pad with chr(0) since that's what mcrypt_generic does. to quote from {@link http://www.php.net/function.mcrypt-generic}: - // "The data is padded with "\0" to make sure the length of the data is n * blocksize." - $ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($this->block_size - strlen($ciphertext) % $this->block_size) % $this->block_size, chr(0)); + if ($this->paddable && strlen($ciphertext) % $this->block_size) { + throw new \LengthException('The ciphertext length (' . strlen($ciphertext) . ') needs to be a multiple of the block size (' . $this->block_size . ')'); } if ($this->engine === self::ENGINE_OPENSSL) { @@ -1040,14 +1025,14 @@ abstract class Base break; case self::MODE_ECB: if (!defined('OPENSSL_RAW_DATA')) { - $ciphertext.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true); + $ciphertext.= openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true); } $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options); break; case self::MODE_CBC: if (!defined('OPENSSL_RAW_DATA')) { $padding = str_repeat(chr($this->block_size), $this->block_size) ^ substr($ciphertext, -$this->block_size); - $ciphertext.= substr(@openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size); + $ciphertext.= substr(openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size); $offset = 2 * $this->block_size; } else { $offset = $this->block_size; @@ -1105,16 +1090,6 @@ abstract class Base $iv = substr($ciphertext, -$this->block_size); } break; - case self::MODE_CFB8: - $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->decryptIV); - if ($this->continuousBuffer) { - if (($len = strlen($ciphertext)) >= $this->block_size) { - $this->decryptIV = substr($ciphertext, -$this->block_size); - } else { - $this->decryptIV = substr($this->decryptIV, $len - $this->block_size) . substr($ciphertext, -$len); - } - } - break; case self::MODE_OFB: $plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer); } @@ -1129,7 +1104,7 @@ abstract class Base $this->changed = false; } if ($this->dechanged) { - @mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + mcrypt_generic_init($this->demcrypt, $this->key, $this->_getIV($this->decryptIV)); $this->dechanged = false; } @@ -1157,12 +1132,12 @@ abstract class Base } if ($len >= $block_size) { $cb = substr($ciphertext, $i, $len - $len % $block_size); - $plaintext.= @mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; + $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; $iv = substr($cb, -$block_size); $len%= $block_size; } if ($len) { - $iv = @mcrypt_generic($this->ecb, $iv); + $iv = mcrypt_generic($this->ecb, $iv); $plaintext.= $iv ^ substr($ciphertext, -$len); $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len); $pos = $len; @@ -1171,10 +1146,10 @@ abstract class Base return $plaintext; } - $plaintext = @mdecrypt_generic($this->demcrypt, $ciphertext); + $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); if (!$this->continuousBuffer) { - @mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + mcrypt_generic_init($this->demcrypt, $this->key, $this->_getIV($this->decryptIV)); } return $this->paddable ? $this->_unpad($plaintext) : $plaintext; @@ -1278,24 +1253,6 @@ abstract class Base $pos = $len; } break; - case self::MODE_CFB8: - $plaintext = ''; - $len = strlen($ciphertext); - $iv = $this->decryptIV; - - for ($i = 0; $i < $len; ++$i) { - $plaintext .= $ciphertext[$i] ^ $this->_encryptBlock($iv); - $iv = substr($iv, 1) . $ciphertext[$i]; - } - - if ($this->continuousBuffer) { - if ($len >= $block_size) { - $this->decryptIV = substr($ciphertext, -$block_size); - } else { - $this->decryptIV = substr($this->decryptIV, $len - $block_size) . substr($ciphertext, -$len); - } - } - break; case self::MODE_OFB: $xor = $this->decryptIV; if (strlen($buffer['xor'])) { @@ -1329,13 +1286,29 @@ abstract class Base return $this->paddable ? $this->_unpad($plaintext) : $plaintext; } + /** + * Get the IV + * + * mcrypt requires an IV even if ECB is used + * + * @see self::encrypt() + * @see self::decrypt() + * @param string $iv + * @return string + * @access private + */ + function _getIV($iv) + { + return $this->mode == self::MODE_ECB ? str_repeat("\0", $this->block_size) : $iv; + } + /** * OpenSSL CTR Processor * * PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream * for CTR is the same for both encrypting and decrypting this function is re-used by both Base::encrypt() * and Base::decrypt(). Also, OpenSSL doesn't implement CTR for all of it's symmetric ciphers so this - * function will emulate CTR with ECB when necessary. + * function will emulate CTR with ECB when necesary. * * @see self::encrypt() * @see self::decrypt() @@ -1358,7 +1331,7 @@ abstract class Base for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); if (strlen($block) > strlen($buffer['ciphertext'])) { - $result = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); + $result = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); $result = !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result; $buffer['ciphertext'].= $result; } @@ -1369,7 +1342,7 @@ abstract class Base } else { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); - $otp = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); + $otp = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); $otp = !defined('OPENSSL_RAW_DATA') ? substr($otp, 0, -$this->block_size) : $otp; $this->_increment_str($xor); $ciphertext.= $block ^ $otp; @@ -1413,7 +1386,7 @@ abstract class Base } if ($this->continuousBuffer) { if (!defined('OPENSSL_RAW_DATA')) { - $encryptIV.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options); + $encryptIV.= openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options); } $encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); if ($overflow) { @@ -1496,8 +1469,6 @@ abstract class Base return 'ctr'; case self::MODE_CFB: return 'cfb'; - case self::MODE_CFB8: - return 'cfb8'; case self::MODE_OFB: return 'ofb'; } @@ -1656,7 +1627,7 @@ abstract class Base case self::ENGINE_MCRYPT: return $this->cipher_name_mcrypt && extension_loaded('mcrypt') && - in_array($this->cipher_name_mcrypt, @mcrypt_list_algorithms()); + in_array($this->cipher_name_mcrypt, mcrypt_list_algorithms()); case self::ENGINE_INTERNAL: return true; } @@ -1735,13 +1706,13 @@ abstract class Base if ($this->engine != self::ENGINE_MCRYPT && $this->enmcrypt) { // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed, // (re)open them with the module named in $this->cipher_name_mcrypt - @mcrypt_module_close($this->enmcrypt); - @mcrypt_module_close($this->demcrypt); + mcrypt_module_close($this->enmcrypt); + mcrypt_module_close($this->demcrypt); $this->enmcrypt = null; $this->demcrypt = null; if ($this->ecb) { - @mcrypt_module_close($this->ecb); + mcrypt_module_close($this->ecb); $this->ecb = null; } } @@ -1851,24 +1822,23 @@ abstract class Base self::MODE_ECB => MCRYPT_MODE_ECB, self::MODE_CBC => MCRYPT_MODE_CBC, self::MODE_CFB => 'ncfb', - self::MODE_CFB8 => MCRYPT_MODE_CFB, self::MODE_OFB => MCRYPT_MODE_NOFB, self::MODE_STREAM => MCRYPT_MODE_STREAM, ); - $this->demcrypt = @mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); - $this->enmcrypt = @mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); + $this->demcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); + $this->enmcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer() // to workaround mcrypt's broken ncfb implementation in buffered mode // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} if ($this->mode == self::MODE_CFB) { - $this->ecb = @mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, ''); + $this->ecb = mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, ''); } } // else should mcrypt_generic_deinit be called? if ($this->mode == self::MODE_CFB) { - @mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size)); + mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size)); } } @@ -1884,6 +1854,7 @@ abstract class Base * * @see self::_unpad() * @param string $text + * @throws \LengthException if padding is disabled and the plaintext's length is not a multiple of the block size * @access private * @return string */ @@ -1895,8 +1866,7 @@ abstract class Base if ($length % $this->block_size == 0) { return $text; } else { - user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})"); - $this->padding = true; + throw new \LengthException("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size}). Try enabling padding."); } } @@ -1913,6 +1883,7 @@ abstract class Base * * @see self::_pad() * @param string $text + * @throws \LengthException if the ciphertext's length is not a multiple of the block size * @access private * @return string */ @@ -1925,7 +1896,7 @@ abstract class Base $length = ord($text[strlen($text) - 1]); if (!$length || $length > $this->block_size) { - return false; + throw new \LengthException("The ciphertext has an invalid padding length ($length) compared to the block size ({$this->block_size})"); } return substr($text, 0, -$length); @@ -1938,20 +1909,19 @@ abstract class Base * after disableContinuousBuffer() or on cipher $engine (re)init * ie after setKey() or setIV() * - * @access public + * @access private * @internal Could, but not must, extend by the child Crypt_* class + * @throws \UnexpectedValueException when an IV is required but not defined */ function _clearBuffers() { $this->enbuffer = $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); - // mcrypt's handling of invalid's $iv: - // $this->encryptIV = $this->decryptIV = strlen($this->iv) == $this->block_size ? $this->iv : str_repeat("\0", $this->block_size); - $this->encryptIV = $this->decryptIV = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, "\0"); - - if (!$this->skip_key_adjustment) { - $this->key = str_pad(substr($this->key, 0, $this->key_length), $this->key_length, "\0"); + if ($this->iv === false && !in_array($this->mode, array(self::MODE_STREAM, self::MODE_ECB))) { + throw new \UnexpectedValueException('No IV has been defined'); } + + $this->encryptIV = $this->decryptIV = $this->iv; } /** @@ -2423,52 +2393,6 @@ abstract class Base $_pos = $_len; } - return $_plaintext; - '; - break; - case self::MODE_CFB8: - $encrypt = $init_encrypt . ' - $_ciphertext = ""; - $_len = strlen($_text); - $_iv = $self->encryptIV; - - for ($_i = 0; $_i < $_len; ++$_i) { - $in = $_iv; - '.$encrypt_block.' - $_ciphertext .= ($_c = $_text[$_i] ^ $in); - $_iv = substr($_iv, 1) . $_c; - } - - if ($self->continuousBuffer) { - if ($_len >= '.$block_size.') { - $self->encryptIV = substr($_ciphertext, -'.$block_size.'); - } else { - $self->encryptIV = substr($self->encryptIV, $_len - '.$block_size.') . substr($_ciphertext, -$_len); - } - } - - return $_ciphertext; - '; - $decrypt = $init_encrypt . ' - $_plaintext = ""; - $_len = strlen($_text); - $_iv = $self->decryptIV; - - for ($_i = 0; $_i < $_len; ++$_i) { - $in = $_iv; - '.$encrypt_block.' - $_plaintext .= $_text[$_i] ^ $in; - $_iv = substr($_iv, 1) . $_text[$_i]; - } - - if ($self->continuousBuffer) { - if ($_len >= '.$block_size.') { - $self->decryptIV = substr($_text, -'.$block_size.'); - } else { - $self->decryptIV = substr($self->decryptIV, $_len - '.$block_size.') . substr($_text, -$_len); - } - } - return $_plaintext; '; break; @@ -2602,11 +2526,6 @@ abstract class Base } // Create the $inline function and return its name as string. Ready to run! - if (version_compare(PHP_VERSION, '5.3.0') >= 0) { - eval('$func = function ($_action, &$self, $_text) { ' . $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' } };'); - return $func; - } - return create_function('$_action, &$self, $_text', $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' }'); } @@ -2618,7 +2537,7 @@ abstract class Base * is stored, classwide (!), here for reusing. * * The string-based index of $function is a classwide - * unique value representing, at least, the $mode of + * uniqe value representing, at least, the $mode of * operation (or more... depends of the optimizing level) * for which $mode the lambda function was created. * @@ -2659,50 +2578,10 @@ abstract class Base $len = strlen($bytes); for ($i = 0; $i < $len; $i+=20) { $t = substr($bytes, $i, 20); - $hash = pack('H*', sha1($hash)); + $hash = sha1($hash, true); $result .= $t ^ $hash; } - return $result . pack('H*', sha1($hash)); - } - } - - /** - * Convert float to int - * - * On ARM CPUs converting floats to ints doesn't always work - * - * @access private - * @param string $x - * @return int - */ - function safe_intval($x) - { - switch (true) { - case is_int($x): - // PHP 5.3, per http://php.net/releases/5_3_0.php, introduced "more consistent float rounding" - case (php_uname('m') & "\xDF\xDF\xDF") != 'ARM': - return $x; - } - return (fmod($x, 0x80000000) & 0x7FFFFFFF) | - ((fmod(floor($x / 0x80000000), 2) & 1) << 31); - } - - /** - * eval()'able string for in-line float to int - * - * @access private - * @return string - */ - function safe_intval_inline() - { - switch (true) { - case defined('PHP_INT_SIZE') && PHP_INT_SIZE == 8: - case (php_uname('m') & "\xDF\xDF\xDF") != 'ARM': - return '%s'; - break; - default: - $safeint = '(is_int($temp = %s) ? $temp : (fmod($temp, 0x80000000) & 0x7FFFFFFF) | '; - return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))'; + return $result . sha1($hash, true); } } } diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php index 74cc49de8a..3500df595b 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php @@ -283,6 +283,22 @@ class Blowfish extends Base */ var $key_length = 16; + /** + * Default Constructor. + * + * @param int $mode + * @access public + * @throws \InvalidArgumentException if an invalid / unsupported mode is provided + */ + function __construct($mode) + { + if ($mode == self::MODE_STREAM) { + throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode'); + } + + parent::__construct($mode); + } + /** * Sets the key length. * @@ -293,14 +309,12 @@ class Blowfish extends Base */ function setKeyLength($length) { - if ($length < 32) { - $this->key_length = 4; - } elseif ($length > 448) { - $this->key_length = 56; - } else { - $this->key_length = $length >> 3; + if ($length < 32 || $length > 448) { + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes between 32 and 448 bits are supported'); } + $this->key_length = $length >> 3; + parent::setKeyLength($length); } @@ -317,10 +331,7 @@ class Blowfish extends Base function isValidEngine($engine) { if ($engine == self::ENGINE_OPENSSL) { - if (version_compare(PHP_VERSION, '5.3.7') < 0 && $this->key_length != 16) { - return false; - } - if ($this->key_length < 16) { + if ($this->key_length != 16) { return false; } $this->cipher_name_openssl_ecb = 'bf-ecb'; @@ -408,14 +419,16 @@ class Blowfish extends Base for ($i = 0; $i < 16; $i+= 2) { $l^= $p[$i]; - $r^= $this->safe_intval(($this->safe_intval($sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^ + $r^= ($sb_0[$l >> 24 & 0xff] + + $sb_1[$l >> 16 & 0xff] ^ $sb_2[$l >> 8 & 0xff]) + - $sb_3[$l & 0xff]); + $sb_3[$l & 0xff]; $r^= $p[$i + 1]; - $l^= $this->safe_intval(($this->safe_intval($sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^ + $l^= ($sb_0[$r >> 24 & 0xff] + + $sb_1[$r >> 16 & 0xff] ^ $sb_2[$r >> 8 & 0xff]) + - $sb_3[$r & 0xff]); + $sb_3[$r & 0xff]; } return pack("N*", $r ^ $p[17], $l ^ $p[16]); } @@ -441,14 +454,16 @@ class Blowfish extends Base for ($i = 17; $i > 2; $i-= 2) { $l^= $p[$i]; - $r^= $this->safe_intval(($this->safe_intval($sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^ + $r^= ($sb_0[$l >> 24 & 0xff] + + $sb_1[$l >> 16 & 0xff] ^ $sb_2[$l >> 8 & 0xff]) + - $sb_3[$l & 0xff]); + $sb_3[$l & 0xff]; $r^= $p[$i - 1]; - $l^= $this->safe_intval(($this->safe_intval($sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^ + $l^= ($sb_0[$r >> 24 & 0xff] + + $sb_1[$r >> 16 & 0xff] ^ $sb_2[$r >> 8 & 0xff]) + - $sb_3[$r & 0xff]); + $sb_3[$r & 0xff]; } return pack("N*", $r ^ $p[0], $l ^ $p[1]); } @@ -474,8 +489,6 @@ class Blowfish extends Base $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); } - $safeint = $this->safe_intval_inline(); - if (!isset($lambda_functions[$code_hash])) { switch (true) { case $gen_hi_opt_code: @@ -511,14 +524,16 @@ class Blowfish extends Base for ($i = 0; $i < 16; $i+= 2) { $encrypt_block.= ' $l^= ' . $p[$i] . '; - $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^ + $r^= ($sb_0[$l >> 24 & 0xff] + + $sb_1[$l >> 16 & 0xff] ^ $sb_2[$l >> 8 & 0xff]) + - $sb_3[$l & 0xff]') . '; + $sb_3[$l & 0xff]; $r^= ' . $p[$i + 1] . '; - $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^ + $l^= ($sb_0[$r >> 24 & 0xff] + + $sb_1[$r >> 16 & 0xff] ^ $sb_2[$r >> 8 & 0xff]) + - $sb_3[$r & 0xff]') . '; + $sb_3[$r & 0xff]; '; } $encrypt_block.= ' @@ -538,14 +553,16 @@ class Blowfish extends Base for ($i = 17; $i > 2; $i-= 2) { $decrypt_block.= ' $l^= ' . $p[$i] . '; - $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^ + $r^= ($sb_0[$l >> 24 & 0xff] + + $sb_1[$l >> 16 & 0xff] ^ $sb_2[$l >> 8 & 0xff]) + - $sb_3[$l & 0xff]') . '; + $sb_3[$l & 0xff]; $r^= ' . $p[$i - 1] . '; - $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^ + $l^= ($sb_0[$r >> 24 & 0xff] + + $sb_1[$r >> 16 & 0xff] ^ $sb_2[$r >> 8 & 0xff]) + - $sb_3[$r & 0xff]') . '; + $sb_3[$r & 0xff]; '; } diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php index 9a8225fb5d..14273d2889 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php @@ -578,6 +578,22 @@ class DES extends Base 0x00000820, 0x00020020, 0x08000000, 0x08020800 ); + /** + * Default Constructor. + * + * @param int $mode + * @access public + * @throws \InvalidArgumentException if an invalid / unsupported mode is provided + */ + function __construct($mode) + { + if ($mode == self::MODE_STREAM) { + throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode'); + } + + parent::__construct($mode); + } + /** * Test for engine validity * @@ -603,24 +619,18 @@ class DES extends Base /** * Sets the key. * - * Keys can be of any length. DES, itself, uses 64-bit keys (eg. strlen($key) == 8), however, we - * only use the first eight, if $key has more then eight characters in it, and pad $key with the - * null byte if it is less then eight characters long. + * Keys must be 64-bits long or 8 bytes long. * * DES also requires that every eighth bit be a parity bit, however, we'll ignore that. * - * If the key is not explicitly set, it'll be assumed to be all zero's. - * * @see \phpseclib\Crypt\Base::setKey() * @access public * @param string $key */ function setKey($key) { - // We check/cut here only up to max length of the key. - // Key padding to the proper length will be done in _setupKey() - if (strlen($key) > $this->key_length_max) { - $key = substr($key, 0, $this->key_length_max); + if (!($this instanceof TripleDES) && strlen($key) != 8) { + throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of size 8 are supported'); } // Sets the key @@ -1302,7 +1312,7 @@ class DES extends Base // After that, we'll still create very fast optimized code but not the hi-ultimative code, for each $mode one $gen_hi_opt_code = (bool)( count($lambda_functions) < 10 ); - // Generation of a unique hash for our generated code + // Generation of a uniqe hash for our generated code $code_hash = "Crypt_DES, $des_rounds, {$this->mode}"; if ($gen_hi_opt_code) { // For hi-optimized code, we create for each combination of @@ -1357,8 +1367,8 @@ class DES extends Base $k[self::ENCRYPT][$i] = '$ke[' . $i . ']'; $k[self::DECRYPT][$i] = '$kd[' . $i . ']'; } - $init_encrypt = '$ke = $self->keys[$self::ENCRYPT];'; - $init_decrypt = '$kd = $self->keys[$self::DECRYPT];'; + $init_encrypt = '$ke = $self->keys[self::ENCRYPT];'; + $init_decrypt = '$kd = $self->keys[self::DECRYPT];'; break; } diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php index a61668209a..080e8cd210 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php @@ -1,26 +1,19 @@ * setKey('abcdefg'); * @@ -31,7 +24,9 @@ * @category Crypt * @package Hash * @author Jim Wigginton - * @copyright 2007 Jim Wigginton + * @copyright 2015 Jim Wigginton + * @author Andreas Fischer + * @copyright 2015 Andreas Fischer * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ @@ -39,34 +34,16 @@ namespace phpseclib\Crypt; use phpseclib\Math\BigInteger; +use phpseclib\Exception\UnsupportedAlgorithmException; /** - * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions. - * * @package Hash * @author Jim Wigginton + * @author Andreas Fischer * @access public */ class Hash { - /**#@+ - * @access private - * @see \phpseclib\Crypt\Hash::__construct() - */ - /** - * Toggles the internal implementation - */ - const MODE_INTERNAL = 1; - /** - * Toggles the mhash() implementation, which has been deprecated on PHP 5.3.0+. - */ - const MODE_MHASH = 2; - /** - * Toggles the hash() implementation, which works on PHP 5.1.2+. - */ - const MODE_HASH = 3; - /**#@-*/ - /** * Hash Parameter * @@ -76,15 +53,6 @@ class Hash */ var $hashParam; - /** - * Byte-length of compression blocks / key (Internal HMAC) - * - * @see self::setAlgorithm() - * @var int - * @access private - */ - var $b; - /** * Byte-length of hash output (Internal HMAC) * @@ -92,7 +60,7 @@ class Hash * @var int * @access private */ - var $l = false; + var $length; /** * Hash Algorithm @@ -113,18 +81,22 @@ class Hash var $key = false; /** - * Computed Key + * Initial Hash * - * @see self::_computeKey() - * @var string + * Used only for sha512/* + * + * @see self::_sha512() + * @var array * @access private */ - var $computedKey = false; + var $initial = false; /** * Outer XOR (Internal HMAC) * - * @see self::setKey() + * Used only for sha512/* + * + * @see self::hash() * @var string * @access private */ @@ -133,44 +105,26 @@ class Hash /** * Inner XOR (Internal HMAC) * - * @see self::setKey() + * Used only for sha512/* + * + * @see self::hash() * @var string * @access private */ var $ipad; - /** - * Engine - * - * @see self::setHash() - * @var string - * @access private - */ - var $engine; - /** * Default Constructor. * * @param string $hash - * @return \phpseclib\Crypt\Hash * @access public */ - function __construct($hash = 'sha1') + function __construct($hash = 'sha256') { - if (!defined('CRYPT_HASH_MODE')) { - switch (true) { - case extension_loaded('hash'): - define('CRYPT_HASH_MODE', self::MODE_HASH); - break; - case extension_loaded('mhash'): - define('CRYPT_HASH_MODE', self::MODE_MHASH); - break; - default: - define('CRYPT_HASH_MODE', self::MODE_INTERNAL); - } - } - $this->setHash($hash); + + $this->ipad = str_repeat(chr(0x36), 128); + $this->opad = str_repeat(chr(0x5C), 128); } /** @@ -184,43 +138,6 @@ class Hash function setKey($key = false) { $this->key = $key; - $this->_computeKey(); - } - - /** - * Pre-compute the key used by the HMAC - * - * Quoting http://tools.ietf.org/html/rfc2104#section-2, "Applications that use keys longer than B bytes - * will first hash the key using H and then use the resultant L byte string as the actual key to HMAC." - * - * As documented in https://www.reddit.com/r/PHP/comments/9nct2l/symfonypolyfill_hash_pbkdf2_correct_fix_for/ - * when doing an HMAC multiple times it's faster to compute the hash once instead of computing it during - * every call - * - * @access private - */ - function _computeKey() - { - if ($this->key === false) { - $this->computedKey = false; - return; - } - - if (strlen($this->key) <= $this->b) { - $this->computedKey = $this->key; - return; - } - - switch ($this->engine) { - case self::MODE_MHASH: - $this->computedKey = mhash($this->hash, $this->key); - break; - case self::MODE_HASH: - $this->computedKey = hash($this->hash, $this->key, true); - break; - case self::MODE_INTERNAL: - $this->computedKey = call_user_func($this->hash, $this->key); - } } /** @@ -246,119 +163,77 @@ class Hash { $this->hashParam = $hash = strtolower($hash); switch ($hash) { + case 'md2-96': case 'md5-96': case 'sha1-96': case 'sha256-96': case 'sha512-96': + case 'sha512/224-96': + case 'sha512/256-96': $hash = substr($hash, 0, -3); - $this->l = 12; // 96 / 8 = 12 + $this->length = 12; // 96 / 8 = 12 break; case 'md2': case 'md5': - $this->l = 16; + $this->length = 16; break; case 'sha1': - $this->l = 20; + $this->length = 20; break; - case 'sha256': - $this->l = 32; - break; - case 'sha384': - $this->l = 48; - break; - case 'sha512': - $this->l = 64; - } - - switch ($hash) { - case 'md2-96': - case 'md2': - $this->b = 16; - case 'md5-96': - case 'sha1-96': - case 'sha224-96': - case 'sha256-96': - case 'md2': - case 'md5': - case 'sha1': case 'sha224': - case 'sha256': - $this->b = 64; - break; - default: - $this->b = 128; - } - - switch ($hash) { - case 'md2': - $this->engine = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ? - self::MODE_HASH : self::MODE_INTERNAL; - break; - case 'sha384': - case 'sha512': - $this->engine = CRYPT_HASH_MODE == self::MODE_MHASH ? self::MODE_INTERNAL : CRYPT_HASH_MODE; - break; - default: - $this->engine = CRYPT_HASH_MODE; - } - - switch ($this->engine) { - case self::MODE_MHASH: - switch ($hash) { - case 'md5': - $this->hash = MHASH_MD5; - break; - case 'sha256': - $this->hash = MHASH_SHA256; - break; - case 'sha1': - default: - $this->hash = MHASH_SHA1; - } - $this->_computeKey(self::MODE_MHASH); - return; - case self::MODE_HASH: - switch ($hash) { - case 'md5': - $this->hash = 'md5'; - return; - case 'md2': - case 'sha256': - case 'sha384': - case 'sha512': - $this->hash = $hash; - return; - case 'sha1': - default: - $this->hash = 'sha1'; - } - $this->_computeKey(self::MODE_HASH); - return; - } - - switch ($hash) { - case 'md2': - $this->hash = array($this, '_md2'); - break; - case 'md5': - $this->hash = array($this, '_md5'); + case 'sha512/224': + $this->length = 28; break; case 'sha256': - $this->hash = array($this, '_sha256'); + case 'sha512/256': + $this->length = 32; break; case 'sha384': - case 'sha512': - $this->hash = array($this, '_sha512'); + $this->length = 48; + break; + case 'sha512': + $this->length = 64; break; - case 'sha1': default: - $this->hash = array($this, '_sha1'); + // see if the hash isn't "officially" supported see if it can + // be "unofficially" supported and calculate the length + // accordingly. + if (in_array($hash, hash_algos())) { + $this->length = strlen(hash($hash, '', true)); + break; + } + // if the hash algorithm doens't exist maybe it's a truncated + // hash, e.g. whirlpool-12 or some such. + if (preg_match('#(-\d+)$#', $hash, $matches)) { + $hash = substr($hash, 0, -strlen($matches[1])); + if (in_array($hash, hash_algos())) { + $this->length = abs($matches[1]) >> 3; + break; + } + } + throw new UnsupportedAlgorithmException( + "$hash is not a supported algorithm" + ); } - $this->ipad = str_repeat(chr(0x36), $this->b); - $this->opad = str_repeat(chr(0x5C), $this->b); + if ($hash == 'sha512/224' || $hash == 'sha512/256') { + // from http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf#page=24 + $this->initial = $hash == 'sha512/256' ? + array( + '22312194FC2BF72C', '9F555FA3C84C64C2', '2393B86B6F53B151', '963877195940EABD', + '96283EE2A88EFFE3', 'BE5E1E2553863992', '2B0199FC2C85B8AA', '0EB72DDC81C52CA2' + ) : + array( + '8C3D37C819544DA2', '73E1996689DCD4D6', '1DFAB7AE32FF9C82', '679DD514582F9FCF', + '0F6D2B697BD44DA8', '77E36F7304C48942', '3F9D85A86A1D36C8', '1112E6AD91D692A1' + ); + for ($i = 0; $i < 8; $i++) { + $this->initial[$i] = new BigInteger($this->initial[$i], 16); + $this->initial[$i]->setPrecision(64); + } + } - $this->_computeKey(self::MODE_INTERNAL); + $this->hash = $hash; } /** @@ -370,37 +245,35 @@ class Hash */ function hash($text) { - if (!empty($this->key) || is_string($this->key)) { - switch ($this->engine) { - case self::MODE_MHASH: - $output = mhash($this->hash, $text, $this->computedKey); - break; - case self::MODE_HASH: - $output = hash_hmac($this->hash, $text, $this->computedKey, true); - break; - case self::MODE_INTERNAL: - $key = str_pad($this->computedKey, $this->b, chr(0)); // step 1 - $temp = $this->ipad ^ $key; // step 2 - $temp .= $text; // step 3 - $temp = call_user_func($this->hash, $temp); // step 4 - $output = $this->opad ^ $key; // step 5 - $output.= $temp; // step 6 - $output = call_user_func($this->hash, $output); // step 7 - } - } else { - switch ($this->engine) { - case self::MODE_MHASH: - $output = mhash($this->hash, $text); - break; - case self::MODE_HASH: - $output = hash($this->hash, $text, true); - break; - case self::MODE_INTERNAL: - $output = call_user_func($this->hash, $text); - } - } + switch ($this->hash) { + case 'sha512/224': + case 'sha512/256': + if (empty($this->key) || !is_string($this->key)) { + return substr(self::_sha512($text, $this->initial), 0, $this->length); + } + /* "Applications that use keys longer than B bytes will first hash the key using H and then use the + resultant L byte string as the actual key to HMAC." - return substr($output, 0, $this->l); + -- http://tools.ietf.org/html/rfc2104#section-2 */ + $key = strlen($this->key) > $this->b ? self::_sha512($this->key, $this->initial) : $this->key; + + $key = str_pad($this->key, 128, chr(0)); // step 1 + $temp = $this->ipad ^ $this->key; // step 2 + $temp .= $text; // step 3 + $temp = self::_sha512($temp, $this->initial); // step 4 + $output = $this->opad ^ $this->key; // step 5 + $output.= $temp; // step 6 + $output = self::_sha512($output, $this->initial); // step 7 + + return substr($output, 0, $this->length); + } + $output = !empty($this->key) || is_string($this->key) ? + hash_hmac($this->hash, $text, $this->key, true) : + hash($this->hash, $text, true); + + return strlen($output) > $this->length + ? substr($output, 0, $this->length) + : $output; } /** @@ -411,243 +284,20 @@ class Hash */ function getLength() { - return $this->l; + return $this->length; } /** - * Wrapper for MD5 + * Pure-PHP implementation of SHA512 * * @access private * @param string $m */ - function _md5($m) + static function _sha512($m, $hash) { - return pack('H*', md5($m)); - } - - /** - * Wrapper for SHA1 - * - * @access private - * @param string $m - */ - function _sha1($m) - { - return pack('H*', sha1($m)); - } - - /** - * Pure-PHP implementation of MD2 - * - * See {@link http://tools.ietf.org/html/rfc1319 RFC1319}. - * - * @access private - * @param string $m - */ - function _md2($m) - { - static $s = array( - 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, - 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, - 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, - 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, - 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, - 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, - 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, - 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, - 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, - 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, - 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, - 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197, - 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65, - 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123, - 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233, - 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228, - 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237, - 31, 26, 219, 153, 141, 51, 159, 17, 131, 20 - ); - - // Step 1. Append Padding Bytes - $pad = 16 - (strlen($m) & 0xF); - $m.= str_repeat(chr($pad), $pad); - - $length = strlen($m); - - // Step 2. Append Checksum - $c = str_repeat(chr(0), 16); - $l = chr(0); - for ($i = 0; $i < $length; $i+= 16) { - for ($j = 0; $j < 16; $j++) { - // RFC1319 incorrectly states that C[j] should be set to S[c xor L] - //$c[$j] = chr($s[ord($m[$i + $j] ^ $l)]); - // per , however, C[j] should be set to S[c xor L] xor C[j] - $c[$j] = chr($s[ord($m[$i + $j] ^ $l)] ^ ord($c[$j])); - $l = $c[$j]; - } - } - $m.= $c; - - $length+= 16; - - // Step 3. Initialize MD Buffer - $x = str_repeat(chr(0), 48); - - // Step 4. Process Message in 16-Byte Blocks - for ($i = 0; $i < $length; $i+= 16) { - for ($j = 0; $j < 16; $j++) { - $x[$j + 16] = $m[$i + $j]; - $x[$j + 32] = $x[$j + 16] ^ $x[$j]; - } - $t = chr(0); - for ($j = 0; $j < 18; $j++) { - for ($k = 0; $k < 48; $k++) { - $x[$k] = $t = $x[$k] ^ chr($s[ord($t)]); - //$t = $x[$k] = $x[$k] ^ chr($s[ord($t)]); - } - $t = chr(ord($t) + $j); - } - } - - // Step 5. Output - return substr($x, 0, 16); - } - - /** - * Pure-PHP implementation of SHA256 - * - * See {@link http://en.wikipedia.org/wiki/SHA_hash_functions#SHA-256_.28a_SHA-2_variant.29_pseudocode SHA-256 (a SHA-2 variant) pseudocode - Wikipedia}. - * - * @access private - * @param string $m - */ - function _sha256($m) - { - if (extension_loaded('suhosin')) { - return pack('H*', sha256($m)); - } - - // Initialize variables - $hash = array( - 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 - ); - // Initialize table of round constants - // (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311) - static $k = array( - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 - ); - - // Pre-processing - $length = strlen($m); - // to round to nearest 56 mod 64, we'll add 64 - (length + (64 - 56)) % 64 - $m.= str_repeat(chr(0), 64 - (($length + 8) & 0x3F)); - $m[$length] = chr(0x80); - // we don't support hashing strings 512MB long - $m.= pack('N2', 0, $length << 3); - - // Process the message in successive 512-bit chunks - $chunks = str_split($m, 64); - foreach ($chunks as $chunk) { - $w = array(); - for ($i = 0; $i < 16; $i++) { - extract(unpack('Ntemp', $this->_string_shift($chunk, 4))); - $w[] = $temp; - } - - // Extend the sixteen 32-bit words into sixty-four 32-bit words - for ($i = 16; $i < 64; $i++) { - // @codingStandardsIgnoreStart - $s0 = $this->_rightRotate($w[$i - 15], 7) ^ - $this->_rightRotate($w[$i - 15], 18) ^ - $this->_rightShift( $w[$i - 15], 3); - $s1 = $this->_rightRotate($w[$i - 2], 17) ^ - $this->_rightRotate($w[$i - 2], 19) ^ - $this->_rightShift( $w[$i - 2], 10); - // @codingStandardsIgnoreEnd - $w[$i] = $this->_add($w[$i - 16], $s0, $w[$i - 7], $s1); - } - - // Initialize hash value for this chunk - list($a, $b, $c, $d, $e, $f, $g, $h) = $hash; - - // Main loop - for ($i = 0; $i < 64; $i++) { - $s0 = $this->_rightRotate($a, 2) ^ - $this->_rightRotate($a, 13) ^ - $this->_rightRotate($a, 22); - $maj = ($a & $b) ^ - ($a & $c) ^ - ($b & $c); - $t2 = $this->_add($s0, $maj); - - $s1 = $this->_rightRotate($e, 6) ^ - $this->_rightRotate($e, 11) ^ - $this->_rightRotate($e, 25); - $ch = ($e & $f) ^ - ($this->_not($e) & $g); - $t1 = $this->_add($h, $s1, $ch, $k[$i], $w[$i]); - - $h = $g; - $g = $f; - $f = $e; - $e = $this->_add($d, $t1); - $d = $c; - $c = $b; - $b = $a; - $a = $this->_add($t1, $t2); - } - - // Add this chunk's hash to result so far - $hash = array( - $this->_add($hash[0], $a), - $this->_add($hash[1], $b), - $this->_add($hash[2], $c), - $this->_add($hash[3], $d), - $this->_add($hash[4], $e), - $this->_add($hash[5], $f), - $this->_add($hash[6], $g), - $this->_add($hash[7], $h) - ); - } - - // Produce the final hash value (big-endian) - return pack('N8', $hash[0], $hash[1], $hash[2], $hash[3], $hash[4], $hash[5], $hash[6], $hash[7]); - } - - /** - * Pure-PHP implementation of SHA384 and SHA512 - * - * @access private - * @param string $m - */ - function _sha512($m) - { - static $init384, $init512, $k; + static $k; if (!isset($k)) { - // Initialize variables - $init384 = array( // initial values for SHA384 - 'cbbb9d5dc1059ed8', '629a292a367cd507', '9159015a3070dd17', '152fecd8f70e5939', - '67332667ffc00b31', '8eb44a8768581511', 'db0c2e0d64f98fa7', '47b5481dbefa4fa4' - ); - $init512 = array( // initial values for SHA512 - '6a09e667f3bcc908', 'bb67ae8584caa73b', '3c6ef372fe94f82b', 'a54ff53a5f1d36f1', - '510e527fade682d1', '9b05688c2b3e6c1f', '1f83d9abfb41bd6b', '5be0cd19137e2179' - ); - - for ($i = 0; $i < 8; $i++) { - $init384[$i] = new BigInteger($init384[$i], 16); - $init384[$i]->setPrecision(64); - $init512[$i] = new BigInteger($init512[$i], 16); - $init512[$i]->setPrecision(64); - } - // Initialize table of round constants // (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409) $k = array( @@ -678,8 +328,6 @@ class Hash } } - $hash = $this->l == 48 ? $init384 : $init512; - // Pre-processing $length = strlen($m); // to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128 @@ -693,7 +341,7 @@ class Hash foreach ($chunks as $chunk) { $w = array(); for ($i = 0; $i < 16; $i++) { - $temp = new BigInteger($this->_string_shift($chunk, 8), 256); + $temp = new BigInteger(self::_string_shift($chunk, 8), 256); $temp->setPrecision(64); $w[] = $temp; } @@ -714,21 +362,21 @@ class Hash ); $s1 = $temp[0]->bitwise_xor($temp[1]); $s1 = $s1->bitwise_xor($temp[2]); - $w[$i] = $w[$i - 16]->copy(); + $w[$i] = clone $w[$i - 16]; $w[$i] = $w[$i]->add($s0); $w[$i] = $w[$i]->add($w[$i - 7]); $w[$i] = $w[$i]->add($s1); } // Initialize hash value for this chunk - $a = $hash[0]->copy(); - $b = $hash[1]->copy(); - $c = $hash[2]->copy(); - $d = $hash[3]->copy(); - $e = $hash[4]->copy(); - $f = $hash[5]->copy(); - $g = $hash[6]->copy(); - $h = $hash[7]->copy(); + $a = clone $hash[0]; + $b = clone $hash[1]; + $c = clone $hash[2]; + $d = clone $hash[3]; + $e = clone $hash[4]; + $f = clone $hash[5]; + $g = clone $hash[6]; + $h = clone $hash[7]; // Main loop for ($i = 0; $i < 80; $i++) { @@ -765,13 +413,13 @@ class Hash $t1 = $t1->add($k[$i]); $t1 = $t1->add($w[$i]); - $h = $g->copy(); - $g = $f->copy(); - $f = $e->copy(); + $h = clone $g; + $g = clone $f; + $f = clone $e; $e = $d->add($t1); - $d = $c->copy(); - $c = $b->copy(); - $b = $a->copy(); + $d = clone $c; + $c = clone $b; + $b = clone $a; $a = $t1->add($t2); } @@ -791,90 +439,11 @@ class Hash // Produce the final hash value (big-endian) // (\phpseclib\Crypt\Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here) $temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() . - $hash[4]->toBytes() . $hash[5]->toBytes(); - if ($this->l != 48) { - $temp.= $hash[6]->toBytes() . $hash[7]->toBytes(); - } + $hash[4]->toBytes() . $hash[5]->toBytes() . $hash[6]->toBytes() . $hash[7]->toBytes(); return $temp; } - /** - * Right Rotate - * - * @access private - * @param int $int - * @param int $amt - * @see self::_sha256() - * @return int - */ - function _rightRotate($int, $amt) - { - $invamt = 32 - $amt; - $mask = (1 << $invamt) - 1; - return (($int << $invamt) & 0xFFFFFFFF) | (($int >> $amt) & $mask); - } - - /** - * Right Shift - * - * @access private - * @param int $int - * @param int $amt - * @see self::_sha256() - * @return int - */ - function _rightShift($int, $amt) - { - $mask = (1 << (32 - $amt)) - 1; - return ($int >> $amt) & $mask; - } - - /** - * Not - * - * @access private - * @param int $int - * @see self::_sha256() - * @return int - */ - function _not($int) - { - return ~$int & 0xFFFFFFFF; - } - - /** - * Add - * - * _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the - * possibility of overflow exists, care has to be taken. BigInteger could be used but this should be faster. - * - * @param int $... - * @return int - * @see self::_sha256() - * @access private - */ - function _add() - { - static $mod; - if (!isset($mod)) { - $mod = pow(2, 32); - } - - $result = 0; - $arguments = func_get_args(); - foreach ($arguments as $argument) { - $result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument; - } - - if ((php_uname('m') & "\xDF\xDF\xDF") != 'ARM') { - return fmod($result, $mod); - } - - return (fmod($result, 0x80000000) & 0x7FFFFFFF) | - ((fmod(floor($result / 0x80000000), 2) & 1) << 31); - } - /** * String Shift * @@ -885,7 +454,7 @@ class Hash * @return string * @access private */ - function _string_shift(&$string, $index = 1) + static function _string_shift(&$string, $index = 1) { $substr = substr($string, 0, $index); $string = substr($string, $index); diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php index b2b9d48eaa..648cf96a7c 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php @@ -259,6 +259,22 @@ class RC2 extends Base 0x70, 0x02, 0xC2, 0x1E, 0xB8, 0x0A, 0xFC, 0xE6 ); + /** + * Default Constructor. + * + * @param int $mode + * @access public + * @throws \InvalidArgumentException if an invalid / unsupported mode is provided + */ + function __construct($mode) + { + if ($mode == self::MODE_STREAM) { + throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode'); + } + + parent::__construct($mode); + } + /** * Test for engine validity * @@ -292,19 +308,15 @@ class RC2 extends Base * * @access public * @param int $length in bits + * @throws \LengthException if the key length isn't supported */ function setKeyLength($length) { - if ($length < 8) { - $this->default_key_length = 1; - } elseif ($length > 1024) { - $this->default_key_length = 128; - } else { - $this->default_key_length = $length; + if ($length < 8 || $length > 1024) { + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported'); } - $this->current_key_length = $this->default_key_length; - parent::setKeyLength($length); + $this->default_key_length = $this->current_key_length = $length; } /** @@ -333,16 +345,20 @@ class RC2 extends Base * @access public * @param string $key * @param int $t1 optional Effective key length in bits. + * @throws \LengthException if the key length isn't supported */ - function setKey($key, $t1 = 0) + function setKey($key, $t1 = false) { $this->orig_key = $key; - if ($t1 <= 0) { + if ($t1 === false) { $t1 = $this->default_key_length; - } elseif ($t1 > 1024) { - $t1 = 1024; } + + if ($t1 < 1 || $t1 > 1024) { + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported'); + } + $this->current_key_length = $t1; // Key byte count should be 1..128. $key = strlen($key) ? substr($key, 0, 128) : "\x00"; @@ -565,7 +581,7 @@ class RC2 extends Base // (Currently, for Crypt_RC2, one generated $lambda_function cost on php5.5@32bit ~60kb unfreeable mem and ~100kb on php5.5@64bit) $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); - // Generation of a unique hash for our generated code + // Generation of a uniqe hash for our generated code $code_hash = "Crypt_RC2, {$this->mode}"; if ($gen_hi_opt_code) { $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php index 25e4ff854d..3da70b6ea0 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php @@ -107,7 +107,7 @@ class RC4 extends Base * @var string * @access private */ - var $key; + var $key = "\0"; /** * The Key Stream for decryption and encryption @@ -121,8 +121,6 @@ class RC4 extends Base /** * Default Constructor. * - * Determines whether or not the mcrypt extension should be used. - * * @see \phpseclib\Crypt\Base::__construct() * @return \phpseclib\Crypt\RC4 * @access public @@ -144,10 +142,8 @@ class RC4 extends Base */ function isValidEngine($engine) { - if ($engine == Base::ENGINE_OPENSSL) { - if (version_compare(PHP_VERSION, '5.3.7') >= 0) { - $this->cipher_name_openssl = 'rc4-40'; - } else { + switch ($engine) { + case Base::ENGINE_OPENSSL: switch (strlen($this->key)) { case 5: $this->cipher_name_openssl = 'rc4-40'; @@ -161,33 +157,20 @@ class RC4 extends Base default: return false; } - } } return parent::isValidEngine($engine); } /** - * Dummy function. + * RC4 does not use an IV * - * Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1]. - * If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before - * calling setKey(). - * - * [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way. Since, in that protocol, - * the IV's are relatively easy to predict, an attack described by - * {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir} - * can be used to quickly guess at the rest of the key. The following links elaborate: - * - * {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009} - * {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack} - * - * @param string $iv - * @see self::setKey() * @access public + * @return bool */ - function setIV($iv) + function usesIV() { + return false; } /** @@ -197,20 +180,38 @@ class RC4 extends Base * * @access public * @param int $length + * @throws \LengthException if the key length is invalid */ function setKeyLength($length) { - if ($length < 8) { - $this->key_length = 1; - } elseif ($length > 2048) { - $this->key_length = 256; - } else { - $this->key_length = $length >> 3; + if ($length < 8 || $length > 2048) { + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 256 bytes are supported'); } + $this->key_length = $length >> 3; + parent::setKeyLength($length); } + /** + * Sets the key length + * + * Keys can be between 1 and 256 bytes long. + * + * @access public + * @param int $length + * @throws \LengthException if the key length is invalid + */ + function setKey($key) + { + $length = strlen($key); + if ($length < 1 || $length > 256) { + throw new \LengthException('Key size of ' . $length . ' bytes is not supported by RC4. Keys must be between 1 and 256 bytes long'); + } + + parent::setKey($key); + } + /** * Encrypts a message. * diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php index d2c6c7cbef..38c2d390f3 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php @@ -8,36 +8,30 @@ * Here's an example of how to encrypt and decrypt text with this library: * * createKey()); + * extract(\phpseclib\Crypt\RSA::createKey()); * - * $plaintext = 'terrafrost'; + * $plaintext = 'terrafrost'; * - * $rsa->loadKey($privatekey); - * $ciphertext = $rsa->encrypt($plaintext); + * $ciphertext = $publickey->encrypt($plaintext); * - * $rsa->loadKey($publickey); - * echo $rsa->decrypt($ciphertext); + * echo $privatekey->decrypt($ciphertext); * ?> * * * Here's an example of how to create signatures and verify signatures with this library: * * createKey()); + * extract(\phpseclib\Crypt\RSA::createKey()); * - * $plaintext = 'terrafrost'; + * $plaintext = 'terrafrost'; * - * $rsa->loadKey($privatekey); - * $signature = $rsa->sign($plaintext); + * $signature = $privatekey->sign($plaintext); * - * $rsa->loadKey($publickey); - * echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified'; + * echo $publickey->verify($plaintext, $signature) ? 'verified' : 'unverified'; * ?> * * @@ -51,6 +45,8 @@ namespace phpseclib\Crypt; +use ParagonIE\ConstantTime\Base64; +use phpseclib\File\ASN1; use phpseclib\Math\BigInteger; /** @@ -71,26 +67,32 @@ class RSA * Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding} * (OAEP) for encryption / decryption. * - * Uses sha1 by default. + * Uses sha256 by default * * @see self::setHash() * @see self::setMGFHash() */ - const ENCRYPTION_OAEP = 1; + const PADDING_OAEP = 1; /** * Use PKCS#1 padding. * - * Although self::ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards + * Although self::PADDING_OAEP / self::PADDING_PSS offers more security, including PKCS#1 padding is necessary for purposes of backwards * compatibility with protocols (like SSH-1) written before OAEP's introduction. */ - const ENCRYPTION_PKCS1 = 2; + const PADDING_PKCS1 = 2; /** * Do not use any padding * * Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy * stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc. */ - const ENCRYPTION_NONE = 3; + const PADDING_NONE = 3; + /** + * Use PKCS#1 padding with PKCS1 v1.5 compatability + * + * A PKCS1 v2.1 encrypted message may not successfully decrypt with a PKCS1 v1.5 implementation (such as OpenSSL). + */ + const PADDING_PKCS15_COMPAT = 6; /**#@-*/ /**#@+ @@ -98,29 +100,27 @@ class RSA * @see self::sign() * @see self::verify() * @see self::setHash() - */ + */ /** * Use the Probabilistic Signature Scheme for signing * - * Uses sha1 by default. + * Uses sha256 and 0 as the salt length * * @see self::setSaltLength() * @see self::setMGFHash() + * @see self::setHash() */ - const SIGNATURE_PSS = 1; + const PADDING_PSS = 4; /** - * Use the PKCS#1 scheme by default. - * - * Although self::SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards - * compatibility with protocols (like SSH-2) written before PSS's introduction. + * Use a relaxed version of PKCS#1 padding for signature verification */ - const SIGNATURE_PKCS1 = 2; + const PADDING_RELAXED_PKCS1 = 5; /**#@-*/ /**#@+ * @access private - * @see \phpseclib\Crypt\RSA::createKey() - */ + * @see self::createKey() + */ /** * ASN1 Integer */ @@ -145,8 +145,8 @@ class RSA /**#@+ * @access private - * @see \phpseclib\Crypt\RSA::__construct() - */ + * @see self::__construct() + */ /** * To use the pure-PHP implementation */ @@ -159,124 +159,37 @@ class RSA const MODE_OPENSSL = 2; /**#@-*/ - /**#@+ - * @access public - * @see \phpseclib\Crypt\RSA::createKey() - * @see \phpseclib\Crypt\RSA::setPrivateKeyFormat() - */ - /** - * PKCS#1 formatted private key - * - * Used by OpenSSH - */ - const PRIVATE_FORMAT_PKCS1 = 0; - /** - * PuTTY formatted private key - */ - const PRIVATE_FORMAT_PUTTY = 1; - /** - * XML formatted private key - */ - const PRIVATE_FORMAT_XML = 2; - /** - * PKCS#8 formatted private key - */ - const PRIVATE_FORMAT_PKCS8 = 8; - /** - * OpenSSH formatted private key - */ - const PRIVATE_FORMAT_OPENSSH = 9; - /**#@-*/ - - /**#@+ - * @access public - * @see \phpseclib\Crypt\RSA::createKey() - * @see \phpseclib\Crypt\RSA::setPublicKeyFormat() - */ - /** - * Raw public key - * - * An array containing two \phpseclib\Math\BigInteger objects. - * - * The exponent can be indexed with any of the following: - * - * 0, e, exponent, publicExponent - * - * The modulus can be indexed with any of the following: - * - * 1, n, modulo, modulus - */ - const PUBLIC_FORMAT_RAW = 3; - /** - * PKCS#1 formatted public key (raw) - * - * Used by File/X509.php - * - * Has the following header: - * - * -----BEGIN RSA PUBLIC KEY----- - * - * Analogous to ssh-keygen's pem format (as specified by -m) - */ - const PUBLIC_FORMAT_PKCS1 = 4; - const PUBLIC_FORMAT_PKCS1_RAW = 4; - /** - * XML formatted public key - */ - const PUBLIC_FORMAT_XML = 5; - /** - * OpenSSH formatted public key - * - * Place in $HOME/.ssh/authorized_keys - */ - const PUBLIC_FORMAT_OPENSSH = 6; - /** - * PKCS#1 formatted public key (encapsulated) - * - * Used by PHP's openssl_public_encrypt() and openssl's rsautl (when -pubin is set) - * - * Has the following header: - * - * -----BEGIN PUBLIC KEY----- - * - * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8 - * is specific to private keys it's basically creating a DER-encoded wrapper - * for keys. This just extends that same concept to public keys (much like ssh-keygen) - */ - const PUBLIC_FORMAT_PKCS8 = 7; - /**#@-*/ - /** * Precomputed Zero * - * @var \phpseclib\Math\BigInteger + * @var array * @access private */ - var $zero; + static $zero; /** * Precomputed One * - * @var \phpseclib\Math\BigInteger + * @var array * @access private */ - var $one; + static $one; /** * Private Key Format * - * @var int + * @var string * @access private */ - var $privateKeyFormat = self::PRIVATE_FORMAT_PKCS1; + var $privateKeyFormat = 'PKCS1'; /** * Public Key Format * - * @var int - * @access public + * @var string + * @access private */ - var $publicKeyFormat = self::PUBLIC_FORMAT_PKCS8; + var $publicKeyFormat = 'PKCS8'; /** * Modulus (ie. n) @@ -374,22 +287,6 @@ class RSA */ var $mgfHLen; - /** - * Encryption mode - * - * @var int - * @access private - */ - var $encryptionMode = self::ENCRYPTION_OAEP; - - /** - * Signature mode - * - * @var int - * @access private - */ - var $signatureMode = self::SIGNATURE_PSS; - /** * Public Exponent * @@ -407,46 +304,69 @@ class RSA var $password = false; /** - * Components + * Loaded File Format * - * For use with parsing XML formatted keys. PHP's XML Parser functions use utilized - instead of PHP's DOM functions - - * because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't. - * - * @see self::_start_element_handler() - * @var array + * @var string * @access private */ - var $components = array(); - - /** - * Current String - * - * For use with parsing XML formatted keys. - * - * @see self::_character_handler() - * @see self::_stop_element_handler() - * @var mixed - * @access private - */ - var $current; + var $format = false; /** * OpenSSL configuration file name. * * Set to null to use system configuration file. + * * @see self::createKey() * @var mixed - * @Access public + * @access public */ - var $configFile; + static $configFile; /** - * Public key comment field. + * Supported file formats (lower case) * - * @var string + * @see self::_initialize_static_variables() + * @var array * @access private */ - var $comment = 'phpseclib-generated-key'; + static $fileFormats = false; + + /** + * Supported file formats (original case) + * + * @see self::_initialize_static_variables() + * @var array + * @access private + */ + static $origFileFormats = false; + + + /** + * Initialize static variables + * + * @access private + */ + static function _initialize_static_variables() + { + if (!isset(self::$zero)) { + self::$zero= new BigInteger(0); + self::$one = new BigInteger(1); + self::$configFile = __DIR__ . '/../openssl.cnf'; + + if (self::$fileFormats === false) { + self::$fileFormats = array(); + foreach (glob(__DIR__ . '/RSA/*.php') as $file) { + $name = pathinfo($file, PATHINFO_FILENAME); + $type = 'phpseclib\Crypt\RSA\\' . $name; + $meta = new \ReflectionClass($type); + if (!$meta->isAbstract()) { + self::$fileFormats[strtolower($name)] = $type; + self::$origFileFormats[] = $name; + } + } + } + } + } /** * The constructor @@ -460,68 +380,12 @@ class RSA */ function __construct() { - $this->configFile = dirname(__FILE__) . '/../openssl.cnf'; + self::_initialize_static_variables(); - if (!defined('CRYPT_RSA_MODE')) { - switch (true) { - // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular, - // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger - // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either. - case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'): - define('CRYPT_RSA_MODE', self::MODE_INTERNAL); - break; - case extension_loaded('openssl') && file_exists($this->configFile): - // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work - $versions = array(); - - // avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems) - if (strpos(ini_get('disable_functions'), 'phpinfo') === false) { - ob_start(); - @phpinfo(); - $content = ob_get_contents(); - ob_end_clean(); - - preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); - - if (!empty($matches[1])) { - for ($i = 0; $i < count($matches[1]); $i++) { - $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); - - // Remove letter part in OpenSSL version - if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) { - $versions[$matches[1][$i]] = $fullVersion; - } else { - $versions[$matches[1][$i]] = $m[0]; - } - } - } - } - - // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+ - switch (true) { - case !isset($versions['Header']): - case !isset($versions['Library']): - case $versions['Header'] == $versions['Library']: - case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0: - define('CRYPT_RSA_MODE', self::MODE_OPENSSL); - break; - default: - define('CRYPT_RSA_MODE', self::MODE_INTERNAL); - define('MATH_BIGINTEGER_OPENSSL_DISABLE', true); - } - break; - default: - define('CRYPT_RSA_MODE', self::MODE_INTERNAL); - } - } - - $this->zero = new BigInteger(); - $this->one = new BigInteger(1); - - $this->hash = new Hash('sha1'); + $this->hash = new Hash('sha256'); $this->hLen = $this->hash->getLength(); - $this->hashName = 'sha1'; - $this->mgfHash = new Hash('sha1'); + $this->hashName = 'sha256'; + $this->mgfHash = new Hash('sha256'); $this->mgfHLen = $this->mgfHash->getLength(); } @@ -539,8 +403,26 @@ class RSA * @param int $timeout * @param array $p */ - function createKey($bits = 1024, $timeout = false, $partial = array()) + static function createKey($bits = 2048, $timeout = false, $partial = array()) { + self::_initialize_static_variables(); + + if (!defined('CRYPT_RSA_MODE')) { + switch (true) { + // Math/BigInteger's openssl requirements are a little less stringent than Crypt/RSA's. in particular, + // Math/BigInteger doesn't require an openssl.cfg file whereas Crypt/RSA does. so if Math/BigInteger + // can't use OpenSSL it can be pretty trivially assumed, then, that Crypt/RSA can't either. + case defined('MATH_BIGINTEGER_OPENSSL_DISABLE'): + define('CRYPT_RSA_MODE', self::MODE_INTERNAL); + break; + case extension_loaded('openssl') && file_exists(self::$configFile): + define('CRYPT_RSA_MODE', self::MODE_OPENSSL); + break; + default: + define('CRYPT_RSA_MODE', self::MODE_INTERNAL); + } + } + if (!defined('CRYPT_RSA_EXPONENT')) { // http://en.wikipedia.org/wiki/65537_%28number%29 define('CRYPT_RSA_EXPONENT', '65537'); @@ -558,16 +440,17 @@ class RSA // OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum if (CRYPT_RSA_MODE == self::MODE_OPENSSL && $bits >= 384 && CRYPT_RSA_EXPONENT == 65537) { $config = array(); - if (isset($this->configFile)) { - $config['config'] = $this->configFile; + if (isset(self::$configFile)) { + $config['config'] = self::$configFile; } $rsa = openssl_pkey_new(array('private_key_bits' => $bits) + $config); - openssl_pkey_export($rsa, $privatekey, null, $config); - $publickey = openssl_pkey_get_details($rsa); - $publickey = $publickey['key']; + openssl_pkey_export($rsa, $privatekeystr, null, $config); + $privatekey = new RSA(); + $privatekey->load($privatekeystr); - $privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, self::PRIVATE_FORMAT_PKCS1))); - $publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, self::PUBLIC_FORMAT_PKCS1))); + $publickeyarr = openssl_pkey_get_details($rsa); + $publickey = new RSA(); + $publickey->load($publickeyarr['key']); // clear the buffer of error strings stemming from a minimalistic openssl.cnf while (openssl_error_string() !== false) { @@ -585,7 +468,7 @@ class RSA $e = new BigInteger(CRYPT_RSA_EXPONENT); } - extract($this->_generateMinMax($bits)); + extract(self::_generateMinMax($bits)); $absoluteMin = $min; $temp = $bits >> 1; // divide by two to see how many bits P and Q would be if ($temp > CRYPT_RSA_SMALLEST_PRIME) { @@ -594,19 +477,17 @@ class RSA } else { $num_primes = 2; } - extract($this->_generateMinMax($temp + $bits % $temp)); + extract(self::_generateMinMax($temp + $bits % $temp)); $finalMax = $max; - extract($this->_generateMinMax($temp)); + extract(self::_generateMinMax($temp)); - $generator = new BigInteger(); - - $n = $this->one->copy(); + $n = clone self::$one; if (!empty($partial)) { extract(unserialize($partial)); } else { $exponents = $coefficients = $primes = array(); $lcm = array( - 'top' => $this->one->copy(), + 'top' => clone self::$one, 'bottom' => false ); } @@ -635,12 +516,12 @@ class RSA if ($i == $num_primes) { list($min, $temp) = $absoluteMin->divide($n); - if (!$temp->equals($this->zero)) { - $min = $min->add($this->one); // ie. ceil() + if (!$temp->equals(self::$zero)) { + $min = $min->add(self::$one); // ie. ceil() } - $primes[$i] = $generator->randomPrime($min, $finalMax, $timeout); + $primes[$i] = BigInteger::randomPrime($min, $finalMax, $timeout); } else { - $primes[$i] = $generator->randomPrime($min, $max, $timeout); + $primes[$i] = BigInteger::randomPrime($min, $max, $timeout); } if ($primes[$i] === false) { // if we've reached the timeout @@ -657,8 +538,8 @@ class RSA } return array( - 'privatekey' => '', - 'publickey' => '', + 'privatekey' => false, + 'publickey' => false, 'partialkey' => $partialkey ); } @@ -671,7 +552,7 @@ class RSA $n = $n->multiply($primes[$i]); - $temp = $primes[$i]->subtract($this->one); + $temp = $primes[$i]->subtract(self::$one); // textbook RSA implementations use Euler's totient function instead of the least common multiple. // see http://en.wikipedia.org/wiki/Euler%27s_totient_function @@ -684,7 +565,7 @@ class RSA list($temp) = $lcm['top']->divide($lcm['bottom']); $gcd = $temp->gcd($e); $i0 = 1; - } while (!$gcd->equals($this->one)); + } while (!$gcd->equals(self::$one)); $d = $e->modInverse($temp); @@ -703,842 +584,221 @@ class RSA // coefficient INTEGER, -- (inverse of q) mod p // otherPrimeInfos OtherPrimeInfos OPTIONAL // } + $privatekey = new RSA(); + $privatekey->modulus = $n; + $privatekey->k = $bits >> 3; + $privatekey->publicExponent = $e; + $privatekey->exponent = $d; + $privatekey->privateExponent = $e; + $privatekey->primes = $primes; + $privatekey->exponents = $exponents; + $privatekey->coefficients = $coefficients; + + $publickey = new RSA(); + $publickey->modulus = $n; + $publickey->k = $bits >> 3; + $publickey->exponent = $e; return array( - 'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), - 'publickey' => $this->_convertPublicKey($n, $e), + 'privatekey' => $privatekey, + 'publickey' => $publickey, 'partialkey' => false ); } /** - * Convert a private key to the appropriate format. + * Add a fileformat plugin * - * @access private - * @see self::setPrivateKeyFormat() - * @param string $RSAPrivateKey - * @return string + * The plugin needs to either already be loaded or be auto-loadable. + * Loading a plugin whose shortname overwrite an existing shortname will overwrite the old plugin. + * + * @see self::load() + * @param string $fullname + * @access public + * @return bool */ - function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients) + static function addFileFormat($fullname) { - $signed = $this->privateKeyFormat != self::PRIVATE_FORMAT_XML; - $num_primes = count($primes); - $raw = array( - 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi - 'modulus' => $n->toBytes($signed), - 'publicExponent' => $e->toBytes($signed), - 'privateExponent' => $d->toBytes($signed), - 'prime1' => $primes[1]->toBytes($signed), - 'prime2' => $primes[2]->toBytes($signed), - 'exponent1' => $exponents[1]->toBytes($signed), - 'exponent2' => $exponents[2]->toBytes($signed), - 'coefficient' => $coefficients[2]->toBytes($signed) - ); + self::_initialize_static_variables(); - // if the format in question does not support multi-prime rsa and multi-prime rsa was used, - // call _convertPublicKey() instead. - switch ($this->privateKeyFormat) { - case self::PRIVATE_FORMAT_XML: - if ($num_primes != 2) { - return false; - } - return "\r\n" . - ' ' . base64_encode($raw['modulus']) . "\r\n" . - ' ' . base64_encode($raw['publicExponent']) . "\r\n" . - '

' . base64_encode($raw['prime1']) . "

\r\n" . - ' ' . base64_encode($raw['prime2']) . "\r\n" . - ' ' . base64_encode($raw['exponent1']) . "\r\n" . - ' ' . base64_encode($raw['exponent2']) . "\r\n" . - ' ' . base64_encode($raw['coefficient']) . "\r\n" . - ' ' . base64_encode($raw['privateExponent']) . "\r\n" . - '
'; - break; - case self::PRIVATE_FORMAT_PUTTY: - if ($num_primes != 2) { - return false; - } - $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: "; - $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none'; - $key.= $encryption; - $key.= "\r\nComment: " . $this->comment . "\r\n"; - $public = pack( - 'Na*Na*Na*', - strlen('ssh-rsa'), - 'ssh-rsa', - strlen($raw['publicExponent']), - $raw['publicExponent'], - strlen($raw['modulus']), - $raw['modulus'] - ); - $source = pack( - 'Na*Na*Na*Na*', - strlen('ssh-rsa'), - 'ssh-rsa', - strlen($encryption), - $encryption, - strlen($this->comment), - $this->comment, - strlen($public), - $public - ); - $public = base64_encode($public); - $key.= "Public-Lines: " . ((strlen($public) + 63) >> 6) . "\r\n"; - $key.= chunk_split($public, 64); - $private = pack( - 'Na*Na*Na*Na*', - strlen($raw['privateExponent']), - $raw['privateExponent'], - strlen($raw['prime1']), - $raw['prime1'], - strlen($raw['prime2']), - $raw['prime2'], - strlen($raw['coefficient']), - $raw['coefficient'] - ); - if (empty($this->password) && !is_string($this->password)) { - $source.= pack('Na*', strlen($private), $private); - $hashkey = 'putty-private-key-file-mac-key'; - } else { - $private.= Random::string(16 - (strlen($private) & 15)); - $source.= pack('Na*', strlen($private), $private); - $sequence = 0; - $symkey = ''; - while (strlen($symkey) < 32) { - $temp = pack('Na*', $sequence++, $this->password); - $symkey.= pack('H*', sha1($temp)); - } - $symkey = substr($symkey, 0, 32); - $crypto = new AES(); - - $crypto->setKey($symkey); - $crypto->disablePadding(); - $private = $crypto->encrypt($private); - $hashkey = 'putty-private-key-file-mac-key' . $this->password; - } - - $private = base64_encode($private); - $key.= 'Private-Lines: ' . ((strlen($private) + 63) >> 6) . "\r\n"; - $key.= chunk_split($private, 64); - $hash = new Hash('sha1'); - $hash->setKey(pack('H*', sha1($hashkey))); - $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n"; - - return $key; - case self::PRIVATE_FORMAT_OPENSSH: - if ($num_primes != 2) { - return false; - } - $publicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']); - $privateKey = pack( - 'Na*Na*Na*Na*Na*Na*Na*', - strlen('ssh-rsa'), - 'ssh-rsa', - strlen($raw['modulus']), - $raw['modulus'], - strlen($raw['publicExponent']), - $raw['publicExponent'], - strlen($raw['privateExponent']), - $raw['privateExponent'], - strlen($raw['coefficient']), - $raw['coefficient'], - strlen($raw['prime1']), - $raw['prime1'], - strlen($raw['prime2']), - $raw['prime2'] - ); - $checkint = Random::string(4); - $paddedKey = pack( - 'a*Na*', - $checkint . $checkint . $privateKey, - strlen($this->comment), - $this->comment - ); - $paddingLength = (7 * strlen($paddedKey)) % 8; - for ($i = 1; $i <= $paddingLength; $i++) { - $paddedKey.= chr($i); - } - $key = pack( - 'Na*Na*Na*NNa*Na*', - strlen('none'), - 'none', - strlen('none'), - 'none', - 0, - '', - 1, - strlen($publicKey), - $publicKey, - strlen($paddedKey), - $paddedKey - ); - $key = "openssh-key-v1\0$key"; - - return "-----BEGIN OPENSSH PRIVATE KEY-----\r\n" . - chunk_split(base64_encode($key), 70) . - "-----END OPENSSH PRIVATE KEY-----"; - default: // eg. self::PRIVATE_FORMAT_PKCS1 - $components = array(); - foreach ($raw as $name => $value) { - $components[$name] = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value); - } - - $RSAPrivateKey = implode('', $components); - - if ($num_primes > 2) { - $OtherPrimeInfos = ''; - for ($i = 3; $i <= $num_primes; $i++) { - // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo - // - // OtherPrimeInfo ::= SEQUENCE { - // prime INTEGER, -- ri - // exponent INTEGER, -- di - // coefficient INTEGER -- ti - // } - $OtherPrimeInfo = pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true)); - $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true)); - $OtherPrimeInfo.= pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true)); - $OtherPrimeInfos.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo); - } - $RSAPrivateKey.= pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos); - } - - $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); - - if ($this->privateKeyFormat == self::PRIVATE_FORMAT_PKCS8) { - $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA - $RSAPrivateKey = pack( - 'Ca*a*Ca*a*', - self::ASN1_INTEGER, - "\01\00", - $rsaOID, - 4, - $this->_encodeLength(strlen($RSAPrivateKey)), - $RSAPrivateKey - ); - $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); - if (!empty($this->password) || is_string($this->password)) { - $salt = Random::string(8); - $iterationCount = 2048; - - $crypto = new DES(); - $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount); - $RSAPrivateKey = $crypto->encrypt($RSAPrivateKey); - - $parameters = pack( - 'Ca*a*Ca*N', - self::ASN1_OCTETSTRING, - $this->_encodeLength(strlen($salt)), - $salt, - self::ASN1_INTEGER, - $this->_encodeLength(4), - $iterationCount - ); - $pbeWithMD5AndDES_CBC = "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03"; - - $encryptionAlgorithm = pack( - 'Ca*a*Ca*a*', - self::ASN1_OBJECT, - $this->_encodeLength(strlen($pbeWithMD5AndDES_CBC)), - $pbeWithMD5AndDES_CBC, - self::ASN1_SEQUENCE, - $this->_encodeLength(strlen($parameters)), - $parameters - ); - - $RSAPrivateKey = pack( - 'Ca*a*Ca*a*', - self::ASN1_SEQUENCE, - $this->_encodeLength(strlen($encryptionAlgorithm)), - $encryptionAlgorithm, - self::ASN1_OCTETSTRING, - $this->_encodeLength(strlen($RSAPrivateKey)), - $RSAPrivateKey - ); - - $RSAPrivateKey = pack('Ca*a*', self::ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey); - - $RSAPrivateKey = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n" . - chunk_split(base64_encode($RSAPrivateKey), 64) . - '-----END ENCRYPTED PRIVATE KEY-----'; - } else { - $RSAPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" . - chunk_split(base64_encode($RSAPrivateKey), 64) . - '-----END PRIVATE KEY-----'; - } - return $RSAPrivateKey; - } - - if (!empty($this->password) || is_string($this->password)) { - $iv = Random::string(8); - $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key - $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8); - $des = new TripleDES(); - $des->setKey($symkey); - $des->setIV($iv); - $iv = strtoupper(bin2hex($iv)); - $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . - "Proc-Type: 4,ENCRYPTED\r\n" . - "DEK-Info: DES-EDE3-CBC,$iv\r\n" . - "\r\n" . - chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) . - '-----END RSA PRIVATE KEY-----'; - } else { - $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" . - chunk_split(base64_encode($RSAPrivateKey), 64) . - '-----END RSA PRIVATE KEY-----'; - } - - return $RSAPrivateKey; + if (class_exists($fullname)) { + $meta = new \ReflectionClass($path); + $shortname = $meta->getShortName(); + self::$fileFormats[strtolower($shortname)] = $fullname; + self::$origFileFormats[] = $shortname; } } /** - * Convert a public key to the appropriate format + * Returns a list of supported formats. * - * @access private - * @see self::setPublicKeyFormat() - * @param string $RSAPrivateKey - * @return string + * @access public + * @return array */ - function _convertPublicKey($n, $e) + static function getSupportedFormats() { - $signed = $this->publicKeyFormat != self::PUBLIC_FORMAT_XML; + self::_initialize_static_variables(); - $modulus = $n->toBytes($signed); - $publicExponent = $e->toBytes($signed); - - switch ($this->publicKeyFormat) { - case self::PUBLIC_FORMAT_RAW: - return array('e' => $e->copy(), 'n' => $n->copy()); - case self::PUBLIC_FORMAT_XML: - return "\r\n" . - ' ' . base64_encode($modulus) . "\r\n" . - ' ' . base64_encode($publicExponent) . "\r\n" . - ''; - break; - case self::PUBLIC_FORMAT_OPENSSH: - // from : - // string "ssh-rsa" - // mpint e - // mpint n - $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus); - $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment; - - return $RSAPublicKey; - default: // eg. self::PUBLIC_FORMAT_PKCS1_RAW or self::PUBLIC_FORMAT_PKCS1 - // from : - // RSAPublicKey ::= SEQUENCE { - // modulus INTEGER, -- n - // publicExponent INTEGER -- e - // } - $components = array( - 'modulus' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus), - 'publicExponent' => pack('Ca*a*', self::ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent) - ); - - $RSAPublicKey = pack( - 'Ca*a*a*', - self::ASN1_SEQUENCE, - $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), - $components['modulus'], - $components['publicExponent'] - ); - - if ($this->publicKeyFormat == self::PUBLIC_FORMAT_PKCS1_RAW) { - $RSAPublicKey = "-----BEGIN RSA PUBLIC KEY-----\r\n" . - chunk_split(base64_encode($RSAPublicKey), 64) . - '-----END RSA PUBLIC KEY-----'; - } else { - // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. - $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA - $RSAPublicKey = chr(0) . $RSAPublicKey; - $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey; - - $RSAPublicKey = pack( - 'Ca*a*', - self::ASN1_SEQUENCE, - $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)), - $rsaOID . $RSAPublicKey - ); - - $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . - chunk_split(base64_encode($RSAPublicKey), 64) . - '-----END PUBLIC KEY-----'; - } - - return $RSAPublicKey; - } + return self::$origFileFormats; } /** - * Break a public or private key down into its constituant components + * Loads a public or private key * - * @access private - * @see self::_convertPublicKey() - * @see self::_convertPrivateKey() - * @param string|array $key - * @param int $type - * @return array|bool + * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed) + * + * @access public + * @param string $key + * @param int $type optional */ - function _parseKey($key, $type) + function load($key, $type = false) { - if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) { + if ($key instanceof RSA) { + $this->privateKeyFormat = $key->privateKeyFormat; + $this->publicKeyFormat = $key->publicKeyFormat; + $this->k = $key->k; + $this->hLen = $key->hLen; + $this->sLen = $key->sLen; + $this->mgfHLen = $key->mgfHLen; + $this->password = $key->password; + + if (is_object($key->hash)) { + $this->hash = new Hash($key->hash->getHash()); + } + if (is_object($key->mgfHash)) { + $this->mgfHash = new Hash($key->mgfHash->getHash()); + } + + if (is_object($key->modulus)) { + $this->modulus = clone $key->modulus; + } + if (is_object($key->exponent)) { + $this->exponent = clone $key->exponent; + } + if (is_object($key->publicExponent)) { + $this->publicExponent = clone $key->publicExponent; + } + + $this->primes = array(); + $this->exponents = array(); + $this->coefficients = array(); + + foreach ($this->primes as $prime) { + $this->primes[] = clone $prime; + } + foreach ($this->exponents as $exponent) { + $this->exponents[] = clone $exponent; + } + foreach ($this->coefficients as $coefficient) { + $this->coefficients[] = clone $coefficient; + } + + return true; + } + + $components = false; + if ($type === false) { + foreach (self::$fileFormats as $format) { + try { + $components = $format::load($key, $this->password); + } catch (\Exception $e) { + $components = false; + } + if ($components !== false) { + break; + } + } + } else { + $format = strtolower($type); + if (isset(self::$fileFormats[$format])) { + $format = self::$fileFormats[$format]; + try { + $components = $format::load($key, $this->password); + } catch (\Exception $e) { + $components = false; + } + } + } + + if ($components === false) { + $this->format = false; return false; } - switch ($type) { - case self::PUBLIC_FORMAT_RAW: - if (!is_array($key)) { - return false; - } - $components = array(); - switch (true) { - case isset($key['e']): - $components['publicExponent'] = $key['e']->copy(); - break; - case isset($key['exponent']): - $components['publicExponent'] = $key['exponent']->copy(); - break; - case isset($key['publicExponent']): - $components['publicExponent'] = $key['publicExponent']->copy(); - break; - case isset($key[0]): - $components['publicExponent'] = $key[0]->copy(); - } - switch (true) { - case isset($key['n']): - $components['modulus'] = $key['n']->copy(); - break; - case isset($key['modulo']): - $components['modulus'] = $key['modulo']->copy(); - break; - case isset($key['modulus']): - $components['modulus'] = $key['modulus']->copy(); - break; - case isset($key[1]): - $components['modulus'] = $key[1]->copy(); - } - return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false; - case self::PRIVATE_FORMAT_PKCS1: - case self::PRIVATE_FORMAT_PKCS8: - case self::PUBLIC_FORMAT_PKCS1: - /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is - "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to - protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding - two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here: + $this->format = $format; - http://tools.ietf.org/html/rfc1421#section-4.6.1.1 - http://tools.ietf.org/html/rfc1421#section-4.6.1.3 - - DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell. - DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation - function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's - own implementation. ie. the implementation *is* the standard and any bugs that may exist in that - implementation are part of the standard, as well. - - * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */ - if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { - $iv = pack('H*', trim($matches[2])); - $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key - $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8))); - // remove the Proc-Type / DEK-Info sections as they're no longer needed - $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key); - $ciphertext = $this->_extractBER($key); - if ($ciphertext === false) { - $ciphertext = $key; - } - switch ($matches[1]) { - case 'AES-256-CBC': - $crypto = new AES(); - break; - case 'AES-128-CBC': - $symkey = substr($symkey, 0, 16); - $crypto = new AES(); - break; - case 'DES-EDE3-CFB': - $crypto = new TripleDES(Base::MODE_CFB); - break; - case 'DES-EDE3-CBC': - $symkey = substr($symkey, 0, 24); - $crypto = new TripleDES(); - break; - case 'DES-CBC': - $crypto = new DES(); - break; - default: - return false; - } - $crypto->setKey($symkey); - $crypto->setIV($iv); - $decoded = $crypto->decrypt($ciphertext); - } else { - $decoded = $this->_extractBER($key); - } - - if ($decoded !== false) { - $key = $decoded; - } - - $components = array(); - - if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { - return false; - } - if ($this->_decodeLength($key) != strlen($key)) { - return false; - } - - $tag = ord($this->_string_shift($key)); - /* intended for keys for which OpenSSL's asn1parse returns the following: - - 0:d=0 hl=4 l= 631 cons: SEQUENCE - 4:d=1 hl=2 l= 1 prim: INTEGER :00 - 7:d=1 hl=2 l= 13 cons: SEQUENCE - 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption - 20:d=2 hl=2 l= 0 prim: NULL - 22:d=1 hl=4 l= 609 prim: OCTET STRING - - ie. PKCS8 keys*/ - - if ($tag == self::ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") { - $this->_string_shift($key, 3); - $tag = self::ASN1_SEQUENCE; - } - - if ($tag == self::ASN1_SEQUENCE) { - $temp = $this->_string_shift($key, $this->_decodeLength($key)); - if (ord($this->_string_shift($temp)) != self::ASN1_OBJECT) { - return false; - } - $length = $this->_decodeLength($temp); - switch ($this->_string_shift($temp, $length)) { - case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption - break; - case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC - /* - PBEParameter ::= SEQUENCE { - salt OCTET STRING (SIZE(8)), - iterationCount INTEGER } - */ - if (ord($this->_string_shift($temp)) != self::ASN1_SEQUENCE) { - return false; - } - if ($this->_decodeLength($temp) != strlen($temp)) { - return false; - } - $this->_string_shift($temp); // assume it's an octet string - $salt = $this->_string_shift($temp, $this->_decodeLength($temp)); - if (ord($this->_string_shift($temp)) != self::ASN1_INTEGER) { - return false; - } - $this->_decodeLength($temp); - list(, $iterationCount) = unpack('N', str_pad($temp, 4, chr(0), STR_PAD_LEFT)); - $this->_string_shift($key); // assume it's an octet string - $length = $this->_decodeLength($key); - if (strlen($key) != $length) { - return false; - } - - $crypto = new DES(); - $crypto->setPassword($this->password, 'pbkdf1', 'md5', $salt, $iterationCount); - $key = $crypto->decrypt($key); - if ($key === false) { - return false; - } - return $this->_parseKey($key, self::PRIVATE_FORMAT_PKCS1); - default: - return false; - } - /* intended for keys for which OpenSSL's asn1parse returns the following: - - 0:d=0 hl=4 l= 290 cons: SEQUENCE - 4:d=1 hl=2 l= 13 cons: SEQUENCE - 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption - 17:d=2 hl=2 l= 0 prim: NULL - 19:d=1 hl=4 l= 271 prim: BIT STRING */ - $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag - $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length - // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of - // unused bits in the final subsequent octet. The number shall be in the range zero to seven." - // -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2) - if ($tag == self::ASN1_BITSTRING) { - $this->_string_shift($key); - } - if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { - return false; - } - if ($this->_decodeLength($key) != strlen($key)) { - return false; - } - $tag = ord($this->_string_shift($key)); - } - if ($tag != self::ASN1_INTEGER) { - return false; - } - - $length = $this->_decodeLength($key); - $temp = $this->_string_shift($key, $length); - if (strlen($temp) != 1 || ord($temp) > 2) { - $components['modulus'] = new BigInteger($temp, 256); - $this->_string_shift($key); // skip over self::ASN1_INTEGER - $length = $this->_decodeLength($key); - $components[$type == self::PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256); - - return $components; - } - if (ord($this->_string_shift($key)) != self::ASN1_INTEGER) { - return false; - } - $length = $this->_decodeLength($key); - $components['modulus'] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['publicExponent'] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['privateExponent'] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['primes'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256)); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['exponents'] = array(1 => new BigInteger($this->_string_shift($key, $length), 256)); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($key, $length), 256)); - - if (!empty($key)) { - if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { - return false; - } - $this->_decodeLength($key); - while (!empty($key)) { - if (ord($this->_string_shift($key)) != self::ASN1_SEQUENCE) { - return false; - } - $this->_decodeLength($key); - $key = substr($key, 1); - $length = $this->_decodeLength($key); - $components['primes'][] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['exponents'][] = new BigInteger($this->_string_shift($key, $length), 256); - $this->_string_shift($key); - $length = $this->_decodeLength($key); - $components['coefficients'][] = new BigInteger($this->_string_shift($key, $length), 256); - } - } - - return $components; - case self::PUBLIC_FORMAT_OPENSSH: - $parts = explode(' ', $key, 3); - - $key = isset($parts[1]) ? base64_decode($parts[1]) : false; - if ($key === false) { - return false; - } - - $comment = isset($parts[2]) ? $parts[2] : false; - - $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa"; - - if (strlen($key) <= 4) { - return false; - } - extract(unpack('Nlength', $this->_string_shift($key, 4))); - $publicExponent = new BigInteger($this->_string_shift($key, $length), -256); - if (strlen($key) <= 4) { - return false; - } - extract(unpack('Nlength', $this->_string_shift($key, 4))); - $modulus = new BigInteger($this->_string_shift($key, $length), -256); - - if ($cleanup && strlen($key)) { - if (strlen($key) <= 4) { - return false; - } - extract(unpack('Nlength', $this->_string_shift($key, 4))); - $realModulus = new BigInteger($this->_string_shift($key, $length), -256); - return strlen($key) ? false : array( - 'modulus' => $realModulus, - 'publicExponent' => $modulus, - 'comment' => $comment - ); - } else { - return strlen($key) ? false : array( - 'modulus' => $modulus, - 'publicExponent' => $publicExponent, - 'comment' => $comment - ); - } - // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue - // http://en.wikipedia.org/wiki/XML_Signature - case self::PRIVATE_FORMAT_XML: - case self::PUBLIC_FORMAT_XML: - $this->components = array(); - - $xml = xml_parser_create('UTF-8'); - xml_set_object($xml, $this); - xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler'); - xml_set_character_data_handler($xml, '_data_handler'); - // add to account for "dangling" tags like ... that are sometimes added - if (!xml_parse($xml, '' . $key . '')) { - xml_parser_free($xml); - unset($xml); - return false; - } - - xml_parser_free($xml); - unset($xml); - - return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false; - // from PuTTY's SSHPUBK.C - case self::PRIVATE_FORMAT_PUTTY: - $components = array(); - $key = preg_split('#\r\n|\r|\n#', $key); - $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0])); - if ($type != 'ssh-rsa') { - return false; - } - $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1])); - $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2])); - - $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3])); - $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength)))); - $public = substr($public, 11); - extract(unpack('Nlength', $this->_string_shift($public, 4))); - $components['publicExponent'] = new BigInteger($this->_string_shift($public, $length), -256); - extract(unpack('Nlength', $this->_string_shift($public, 4))); - $components['modulus'] = new BigInteger($this->_string_shift($public, $length), -256); - - $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4])); - $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength)))); - - switch ($encryption) { - case 'aes256-cbc': - $symkey = ''; - $sequence = 0; - while (strlen($symkey) < 32) { - $temp = pack('Na*', $sequence++, $this->password); - $symkey.= pack('H*', sha1($temp)); - } - $symkey = substr($symkey, 0, 32); - $crypto = new AES(); - } - - if ($encryption != 'none') { - $crypto->setKey($symkey); - $crypto->disablePadding(); - $private = $crypto->decrypt($private); - if ($private === false) { - return false; - } - } - - extract(unpack('Nlength', $this->_string_shift($private, 4))); - if (strlen($private) < $length) { - return false; - } - $components['privateExponent'] = new BigInteger($this->_string_shift($private, $length), -256); - extract(unpack('Nlength', $this->_string_shift($private, 4))); - if (strlen($private) < $length) { - return false; - } - $components['primes'] = array(1 => new BigInteger($this->_string_shift($private, $length), -256)); - extract(unpack('Nlength', $this->_string_shift($private, 4))); - if (strlen($private) < $length) { - return false; - } - $components['primes'][] = new BigInteger($this->_string_shift($private, $length), -256); - - $temp = $components['primes'][1]->subtract($this->one); - $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp)); - $temp = $components['primes'][2]->subtract($this->one); - $components['exponents'][] = $components['publicExponent']->modInverse($temp); - - extract(unpack('Nlength', $this->_string_shift($private, 4))); - if (strlen($private) < $length) { - return false; - } - $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256)); - - return $components; - case self::PRIVATE_FORMAT_OPENSSH: - $components = array(); - $decoded = $this->_extractBER($key); - $magic = $this->_string_shift($decoded, 15); - if ($magic !== "openssh-key-v1\0") { - return false; - } - $options = $this->_string_shift($decoded, 24); - // \0\0\0\4none = ciphername - // \0\0\0\4none = kdfname - // \0\0\0\0 = kdfoptions - // \0\0\0\1 = numkeys - if ($options != "\0\0\0\4none\0\0\0\4none\0\0\0\0\0\0\0\1") { - return false; - } - extract(unpack('Nlength', $this->_string_shift($decoded, 4))); - if (strlen($decoded) < $length) { - return false; - } - $publicKey = $this->_string_shift($decoded, $length); - extract(unpack('Nlength', $this->_string_shift($decoded, 4))); - if (strlen($decoded) < $length) { - return false; - } - $paddedKey = $this->_string_shift($decoded, $length); - - if ($this->_string_shift($publicKey, 11) !== "\0\0\0\7ssh-rsa") { - return false; - } - - $checkint1 = $this->_string_shift($paddedKey, 4); - $checkint2 = $this->_string_shift($paddedKey, 4); - if (strlen($checkint1) != 4 || $checkint1 !== $checkint2) { - return false; - } - - if ($this->_string_shift($paddedKey, 11) !== "\0\0\0\7ssh-rsa") { - return false; - } - - $values = array( - &$components['modulus'], - &$components['publicExponent'], - &$components['privateExponent'], - &$components['coefficients'][2], - &$components['primes'][1], - &$components['primes'][2] - ); - - foreach ($values as &$value) { - extract(unpack('Nlength', $this->_string_shift($paddedKey, 4))); - if (strlen($paddedKey) < $length) { - return false; - } - $value = new BigInteger($this->_string_shift($paddedKey, $length), -256); - } - - extract(unpack('Nlength', $this->_string_shift($paddedKey, 4))); - if (strlen($paddedKey) < $length) { - return false; - } - $components['comment'] = $this->_string_shift($decoded, $length); - - $temp = $components['primes'][1]->subtract($this->one); - $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp)); - $temp = $components['primes'][2]->subtract($this->one); - $components['exponents'][] = $components['publicExponent']->modInverse($temp); - - return $components; + $this->modulus = $components['modulus']; + $this->k = strlen($this->modulus->toBytes()); + $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent']; + if (isset($components['primes'])) { + $this->primes = $components['primes']; + $this->exponents = $components['exponents']; + $this->coefficients = $components['coefficients']; + $this->publicExponent = $components['publicExponent']; + } else { + $this->primes = array(); + $this->exponents = array(); + $this->coefficients = array(); + $this->publicExponent = false; } + + if ($components['isPublicKey']) { + $this->setPublicKey(); + } + + return true; + } + + /** + * Returns the format of the loaded key. + * + * If the key that was loaded wasn't in a valid or if the key was auto-generated + * with RSA::createKey() then this will return false. + * + * @see self::load() + * @access public + * @return mixed + */ + function getLoadedFormat() + { + if ($this->format === false) { + return false; + } + + $meta = new \ReflectionClass($this->format); + return $meta->getShortName(); + } + + /** + * Returns the private key + * + * The private key is only returned if the currently loaded key contains the constituent prime numbers. + * + * @see self::getPublicKey() + * @access public + * @param string $type optional + * @return mixed + */ + function getPrivateKey($type = 'PKCS1') + { + $type = strtolower($type); + if (!isset(self::$fileFormats[$type])) { + return false; + } + $type = self::$fileFormats[$type]; + if (!method_exists($type, 'savePrivateKey')) { + return false; + } + + if (empty($this->primes)) { + return false; + } + + $oldFormat = $this->privateKeyFormat; + $this->privateKeyFormat = $type; + $temp = $type::savePrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients, $this->password); + $this->privateKeyFormat = $oldFormat; + return $temp; } /** @@ -1554,206 +814,6 @@ class RSA return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits()); } - /** - * Start Element Handler - * - * Called by xml_set_element_handler() - * - * @access private - * @param resource $parser - * @param string $name - * @param array $attribs - */ - function _start_element_handler($parser, $name, $attribs) - { - //$name = strtoupper($name); - switch ($name) { - case 'MODULUS': - $this->current = &$this->components['modulus']; - break; - case 'EXPONENT': - $this->current = &$this->components['publicExponent']; - break; - case 'P': - $this->current = &$this->components['primes'][1]; - break; - case 'Q': - $this->current = &$this->components['primes'][2]; - break; - case 'DP': - $this->current = &$this->components['exponents'][1]; - break; - case 'DQ': - $this->current = &$this->components['exponents'][2]; - break; - case 'INVERSEQ': - $this->current = &$this->components['coefficients'][2]; - break; - case 'D': - $this->current = &$this->components['privateExponent']; - } - $this->current = ''; - } - - /** - * Stop Element Handler - * - * Called by xml_set_element_handler() - * - * @access private - * @param resource $parser - * @param string $name - */ - function _stop_element_handler($parser, $name) - { - if (isset($this->current)) { - $this->current = new BigInteger(base64_decode($this->current), 256); - unset($this->current); - } - } - - /** - * Data Handler - * - * Called by xml_set_character_data_handler() - * - * @access private - * @param resource $parser - * @param string $data - */ - function _data_handler($parser, $data) - { - if (!isset($this->current) || is_object($this->current)) { - return; - } - $this->current.= trim($data); - } - - /** - * Loads a public or private key - * - * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed) - * - * @access public - * @param string|RSA|array $key - * @param bool|int $type optional - * @return bool - */ - function loadKey($key, $type = false) - { - if ($key instanceof RSA) { - $this->privateKeyFormat = $key->privateKeyFormat; - $this->publicKeyFormat = $key->publicKeyFormat; - $this->k = $key->k; - $this->hLen = $key->hLen; - $this->sLen = $key->sLen; - $this->mgfHLen = $key->mgfHLen; - $this->encryptionMode = $key->encryptionMode; - $this->signatureMode = $key->signatureMode; - $this->password = $key->password; - $this->configFile = $key->configFile; - $this->comment = $key->comment; - - if (is_object($key->hash)) { - $this->hash = new Hash($key->hash->getHash()); - } - if (is_object($key->mgfHash)) { - $this->mgfHash = new Hash($key->mgfHash->getHash()); - } - - if (is_object($key->modulus)) { - $this->modulus = $key->modulus->copy(); - } - if (is_object($key->exponent)) { - $this->exponent = $key->exponent->copy(); - } - if (is_object($key->publicExponent)) { - $this->publicExponent = $key->publicExponent->copy(); - } - - $this->primes = array(); - $this->exponents = array(); - $this->coefficients = array(); - - foreach ($this->primes as $prime) { - $this->primes[] = $prime->copy(); - } - foreach ($this->exponents as $exponent) { - $this->exponents[] = $exponent->copy(); - } - foreach ($this->coefficients as $coefficient) { - $this->coefficients[] = $coefficient->copy(); - } - - return true; - } - - if ($type === false) { - $types = array( - self::PUBLIC_FORMAT_RAW, - self::PRIVATE_FORMAT_PKCS1, - self::PRIVATE_FORMAT_XML, - self::PRIVATE_FORMAT_PUTTY, - self::PUBLIC_FORMAT_OPENSSH, - self::PRIVATE_FORMAT_OPENSSH - ); - foreach ($types as $type) { - $components = $this->_parseKey($key, $type); - if ($components !== false) { - break; - } - } - } else { - $components = $this->_parseKey($key, $type); - } - - if ($components === false) { - $this->comment = null; - $this->modulus = null; - $this->k = null; - $this->exponent = null; - $this->primes = null; - $this->exponents = null; - $this->coefficients = null; - $this->publicExponent = null; - - return false; - } - - if (isset($components['comment']) && $components['comment'] !== false) { - $this->comment = $components['comment']; - } - $this->modulus = $components['modulus']; - $this->k = strlen($this->modulus->toBytes()); - $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent']; - if (isset($components['primes'])) { - $this->primes = $components['primes']; - $this->exponents = $components['exponents']; - $this->coefficients = $components['coefficients']; - $this->publicExponent = $components['publicExponent']; - } else { - $this->primes = array(); - $this->exponents = array(); - $this->coefficients = array(); - $this->publicExponent = false; - } - - switch ($type) { - case self::PUBLIC_FORMAT_OPENSSH: - case self::PUBLIC_FORMAT_RAW: - $this->setPublicKey(); - break; - case self::PRIVATE_FORMAT_PKCS1: - switch (true) { - case strpos($key, '-BEGIN PUBLIC KEY-') !== false: - case strpos($key, '-BEGIN RSA PUBLIC KEY-') !== false: - $this->setPublicKey(); - } - } - - return true; - } - /** * Sets the password * @@ -1761,7 +821,7 @@ class RSA * Or rather, pass in $password such that empty($password) && !is_string($password) is true. * * @see self::createKey() - * @see self::loadKey() + * @see self::load() * @access public * @param string $password */ @@ -1803,27 +863,40 @@ class RSA return true; } + $components = false; if ($type === false) { - $types = array( - self::PUBLIC_FORMAT_RAW, - self::PUBLIC_FORMAT_PKCS1, - self::PUBLIC_FORMAT_XML, - self::PUBLIC_FORMAT_OPENSSH - ); - foreach ($types as $type) { - $components = $this->_parseKey($key, $type); + foreach (self::$fileFormats as $format) { + if (!method_exists($format, 'savePublicKey')) { + continue; + } + try { + $components = $format::load($key, $this->password); + } catch (\Exception $e) { + $components = false; + } if ($components !== false) { break; } } } else { - $components = $this->_parseKey($key, $type); + $format = strtolower($type); + if (isset(self::$fileFormats[$format])) { + $format = self::$fileFormats[$format]; + try { + $components = $format::load($key, $this->password); + } catch (\Exception $e) { + $components = false; + } + } } if ($components === false) { + $this->format = false; return false; } + $this->format = $format; + if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) { $this->modulus = $components['modulus']; $this->exponent = $this->publicExponent = $components['publicExponent']; @@ -1859,13 +932,13 @@ class RSA } $rsa = new RSA(); - if (!$rsa->loadKey($key, $type)) { + if (!$rsa->load($key, $type)) { return false; } $rsa->publicExponent = false; // don't overwrite the old key if the new key is invalid - $this->loadKey($rsa); + $this->load($rsa); return true; } @@ -1876,20 +949,29 @@ class RSA * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this * function won't return it since this library, for the most part, doesn't distinguish between public and private keys. * - * @see self::getPublicKey() + * @see self::getPrivateKey() * @access public - * @param string $key - * @param int $type optional + * @param string $type optional + * @return mixed */ - function getPublicKey($type = self::PUBLIC_FORMAT_PKCS8) + function getPublicKey($type = 'PKCS8') { + $type = strtolower($type); + if (!isset(self::$fileFormats[$type])) { + return false; + } + $type = self::$fileFormats[$type]; + if (!method_exists($type, 'savePublicKey')) { + return false; + } + if (empty($this->modulus) || empty($this->publicExponent)) { return false; } $oldFormat = $this->publicKeyFormat; $this->publicKeyFormat = $type; - $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent); + $temp = $type::savePublicKey($this->modulus, $this->publicExponent); $this->publicKeyFormat = $oldFormat; return $temp; } @@ -1920,7 +1002,7 @@ class RSA switch ($algorithm) { case 'sha256': $hash = new Hash('sha256'); - $base = base64_encode($hash->hash($RSAPublicKey)); + $base = Base64::encode($hash->hash($RSAPublicKey)); return substr($base, 0, strlen($base) - 1); case 'md5': return substr(chunk_split(md5($RSAPublicKey), 2, ':'), 0, -1); @@ -1929,30 +1011,6 @@ class RSA } } - /** - * Returns the private key - * - * The private key is only returned if the currently loaded key contains the constituent prime numbers. - * - * @see self::getPublicKey() - * @access public - * @param string $key - * @param int $type optional - * @return mixed - */ - function getPrivateKey($type = self::PUBLIC_FORMAT_PKCS1) - { - if (empty($this->primes)) { - return false; - } - - $oldFormat = $this->privateKeyFormat; - $this->privateKeyFormat = $type; - $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients); - $this->privateKeyFormat = $oldFormat; - return $temp; - } - /** * Returns a minimalistic private key * @@ -1961,24 +1019,34 @@ class RSA * * @see self::getPrivateKey() * @access private - * @param string $key - * @param int $type optional + * @param string $type optional + * @return mixed */ - function _getPrivatePublicKey($mode = self::PUBLIC_FORMAT_PKCS8) + function _getPrivatePublicKey($type = 'PKCS8') { + $type = strtolower($type); + if (!isset(self::$fileFormats[$type])) { + return false; + } + $type = self::$fileFormats[$type]; + if (!method_exists($type, 'savePublicKey')) { + return false; + } + if (empty($this->modulus) || empty($this->exponent)) { return false; } $oldFormat = $this->publicKeyFormat; - $this->publicKeyFormat = $mode; - $temp = $this->_convertPublicKey($this->modulus, $this->exponent); + $this->publicKeyFormat = $type; + $temp = $type::savePublicKey($this->modulus, $this->exponent); $this->publicKeyFormat = $oldFormat; return $temp; } + /** - * __toString() magic method + * __toString() magic method * * @access public * @return string @@ -1986,23 +1054,23 @@ class RSA function __toString() { $key = $this->getPrivateKey($this->privateKeyFormat); - if ($key !== false) { + if (is_string($key)) { return $key; } $key = $this->_getPrivatePublicKey($this->publicKeyFormat); - return $key !== false ? $key : ''; + return is_string($key) ? $key : ''; } /** - * __clone() magic method + * __clone() magic method * * @access public - * @return Crypt_RSA + * @return \phpseclib\Crypt\RSA */ function __clone() { $key = new RSA(); - $key->loadKey($this); + $key->load($this); return $key; } @@ -2013,7 +1081,7 @@ class RSA * @param int $bits * @return array */ - function _generateMinMax($bits) + static function _generateMinMax($bits) { $bytes = $bits >> 3; $min = str_repeat(chr(0), $bytes); @@ -2117,8 +1185,8 @@ class RSA /** * Determines which hashing function should be used * - * Used with signature production / verification and (if the encryption mode is self::ENCRYPTION_OAEP) encryption and - * decryption. If $hash isn't supported, sha1 is used. + * Used with signature production / verification and (if the encryption mode is self::PADDING_OAEP) encryption and + * decryption. If $hash isn't supported, sha256 is used. * * @access public * @param string $hash @@ -2133,12 +1201,15 @@ class RSA case 'sha256': case 'sha384': case 'sha512': + case 'sha224': + case 'sha512/224': + case 'sha512/256': $this->hash = new Hash($hash); $this->hashName = $hash; break; default: - $this->hash = new Hash('sha1'); - $this->hashName = 'sha1'; + $this->hash = new Hash('sha256'); + $this->hashName = 'sha256'; } $this->hLen = $this->hash->getLength(); } @@ -2146,7 +1217,7 @@ class RSA /** * Determines which hashing function should be used for the mask generation function * - * The mask generation function is used by self::ENCRYPTION_OAEP and self::SIGNATURE_PSS and although it's + * The mask generation function is used by self::PADDING_OAEP and self::PADDING_PSS and although it's * best if Hash and MGFHash are set to the same thing this is not a requirement. * * @access public @@ -2162,10 +1233,13 @@ class RSA case 'sha256': case 'sha384': case 'sha512': + case 'sha224': + case 'sha512/224': + case 'sha512/256': $this->mgfHash = new Hash($hash); break; default: - $this->mgfHash = new Hash('sha1'); + $this->mgfHash = new Hash('sha256'); } $this->mgfHLen = $this->mgfHash->getLength(); } @@ -2192,15 +1266,17 @@ class RSA * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}. * * @access private - * @param \phpseclib\Math\BigInteger $x + * @param bool|\phpseclib\Math\BigInteger $x * @param int $xLen - * @return string + * @return bool|string */ function _i2osp($x, $xLen) { + if ($x === false) { + return false; + } $x = $x->toBytes(); if (strlen($x) > $xLen) { - user_error('Integer too large'); return false; } return str_pad($x, $xLen, chr(0), STR_PAD_LEFT); @@ -2233,11 +1309,11 @@ class RSA { switch (true) { case empty($this->primes): - case $this->primes[1]->equals($this->zero): + case $this->primes[1]->equals(self::$zero): case empty($this->coefficients): - case $this->coefficients[2]->equals($this->zero): + case $this->coefficients[2]->equals(self::$zero): case empty($this->exponents): - case $this->exponents[1]->equals($this->zero): + case $this->exponents[1]->equals(self::$zero): return $x->modPow($this->exponent, $this->modulus); } @@ -2273,9 +1349,7 @@ class RSA } } - $one = new BigInteger(1); - - $r = $one->random($one, $smallest->subtract($one)); + $r = BigInteger::random(self::$one, $smallest->subtract(self::$one)); $m_i = array( 1 => $this->_blind($x, $r, 1), @@ -2343,21 +1417,16 @@ class RSA */ function _equals($x, $y) { - if (function_exists('hash_equals')) { - return hash_equals($x, $y); - } - if (strlen($x) != strlen($y)) { return false; } - $result = "\0"; - $x^= $y; + $result = 0; for ($i = 0; $i < strlen($x); $i++) { - $result|= $x[$i]; + $result |= ord($x[$i]) ^ ord($y[$i]); } - return $result === "\0"; + return $result == 0; } /** @@ -2367,12 +1436,11 @@ class RSA * * @access private * @param \phpseclib\Math\BigInteger $m - * @return \phpseclib\Math\BigInteger + * @return bool|\phpseclib\Math\BigInteger */ function _rsaep($m) { - if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { - user_error('Message representative out of range'); + if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) { return false; } return $this->_exponentiate($m); @@ -2385,12 +1453,11 @@ class RSA * * @access private * @param \phpseclib\Math\BigInteger $c - * @return \phpseclib\Math\BigInteger + * @return bool|\phpseclib\Math\BigInteger */ function _rsadp($c) { - if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) { - user_error('Ciphertext representative out of range'); + if ($c->compare(self::$zero) < 0 || $c->compare($this->modulus) > 0) { return false; } return $this->_exponentiate($c); @@ -2403,12 +1470,11 @@ class RSA * * @access private * @param \phpseclib\Math\BigInteger $m - * @return \phpseclib\Math\BigInteger + * @return bool|\phpseclib\Math\BigInteger */ function _rsasp1($m) { - if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) { - user_error('Message representative out of range'); + if ($m->compare(self::$zero) < 0 || $m->compare($this->modulus) > 0) { return false; } return $this->_exponentiate($m); @@ -2421,12 +1487,11 @@ class RSA * * @access private * @param \phpseclib\Math\BigInteger $s - * @return \phpseclib\Math\BigInteger + * @return bool|\phpseclib\Math\BigInteger */ function _rsavp1($s) { - if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) { - user_error('Signature representative out of range'); + if ($s->compare(self::$zero) < 0 || $s->compare($this->modulus) > 0) { return false; } return $this->_exponentiate($s); @@ -2465,6 +1530,7 @@ class RSA * @access private * @param string $m * @param string $l + * @throws \OutOfBoundsException if strlen($m) > $this->k - 2 * $this->hLen - 2 * @return string */ function _rsaes_oaep_encrypt($m, $l = '') @@ -2477,8 +1543,7 @@ class RSA // be output. if ($mLen > $this->k - 2 * $this->hLen - 2) { - user_error('Message too long'); - return false; + throw new \OutOfBoundsException('Message too long'); } // EME-OAEP encoding @@ -2528,7 +1593,7 @@ class RSA * @access private * @param string $c * @param string $l - * @return string + * @return bool|string */ function _rsaes_oaep_decrypt($c, $l = '') { @@ -2538,7 +1603,6 @@ class RSA // be output. if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) { - user_error('Decryption error'); return false; } @@ -2546,11 +1610,10 @@ class RSA $c = $this->_os2ip($c); $m = $this->_rsadp($c); - if ($m === false) { - user_error('Decryption error'); + $em = $this->_i2osp($m, $this->k); + if ($em === false) { return false; } - $em = $this->_i2osp($m, $this->k); // EME-OAEP decoding @@ -2564,26 +1627,17 @@ class RSA $db = $maskedDB ^ $dbMask; $lHash2 = substr($db, 0, $this->hLen); $m = substr($db, $this->hLen); - $hashesMatch = $this->_equals($lHash, $lHash2); - $leadingZeros = 1; - $patternMatch = 0; - $offset = 0; - for ($i = 0; $i < strlen($m); $i++) { - $patternMatch|= $leadingZeros & ($m[$i] === "\1"); - $leadingZeros&= $m[$i] === "\0"; - $offset+= $patternMatch ? 0 : 1; + if ($lHash != $lHash2) { + return false; } - - // we do & instead of && to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation - // to protect against timing attacks - if (!$hashesMatch & !$patternMatch) { - user_error('Decryption error'); + $m = ltrim($m, chr(0)); + if (ord($m[0]) != 1) { return false; } // Output the message M - return substr($m, $offset + 1); + return substr($m, 1); } /** @@ -2593,10 +1647,15 @@ class RSA * * @access private * @param string $m - * @return string + * @return bool|string + * @throws \OutOfBoundsException if strlen($m) > $this->k */ function _raw_encrypt($m) { + if (strlen($m) > $this->k) { + throw new \OutOfBoundsException('Message too long'); + } + $temp = $this->_os2ip($m); $temp = $this->_rsaep($temp); return $this->_i2osp($temp, $this->k); @@ -2609,17 +1668,18 @@ class RSA * * @access private * @param string $m - * @return string + * @param bool $pkcs15_compat optional + * @throws \OutOfBoundsException if strlen($m) > $this->k - 11 + * @return bool|string */ - function _rsaes_pkcs1_v1_5_encrypt($m) + function _rsaes_pkcs1_v1_5_encrypt($m, $pkcs15_compat = false) { $mLen = strlen($m); // Length checking if ($mLen > $this->k - 11) { - user_error('Message too long'); - return false; + throw new \OutOfBoundsException('Message too long'); } // EME-PKCS1-v1_5 encoding @@ -2633,7 +1693,7 @@ class RSA } $type = 2; // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done - if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) { + if ($pkcs15_compat && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) { $type = 1; // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF" $ps = str_repeat("\xFF", $psLen); @@ -2668,14 +1728,13 @@ class RSA * * @access private * @param string $c - * @return string + * @return bool|string */ function _rsaes_pkcs1_v1_5_decrypt($c) { // Length checking if (strlen($c) != $this->k) { // or if k < 11 - user_error('Decryption error'); return false; } @@ -2683,17 +1742,14 @@ class RSA $c = $this->_os2ip($c); $m = $this->_rsadp($c); - - if ($m === false) { - user_error('Decryption error'); + $em = $this->_i2osp($m, $this->k); + if ($em === false) { return false; } - $em = $this->_i2osp($m, $this->k); // EME-PKCS1-v1_5 decoding if (ord($em[0]) != 0 || ord($em[1]) > 2) { - user_error('Decryption error'); return false; } @@ -2701,7 +1757,6 @@ class RSA $m = substr($em, strlen($ps) + 3); if (strlen($ps) < 8) { - user_error('Decryption error'); return false; } @@ -2717,6 +1772,7 @@ class RSA * * @access private * @param string $m + * @throws \RuntimeException on encoding error * @param int $emBits */ function _emsa_pss_encode($m, $emBits) @@ -2725,11 +1781,10 @@ class RSA // be output. $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8) - $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; + $sLen = $this->sLen ? $this->sLen : $this->hLen; $mHash = $this->hash->hash($m); if ($emLen < $this->hLen + $sLen + 2) { - user_error('Encoding error'); return false; } @@ -2763,7 +1818,7 @@ class RSA // be output. $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8); - $sLen = $this->sLen !== null ? $this->sLen : $this->hLen; + $sLen = $this->sLen ? $this->sLen : $this->hLen; $mHash = $this->hash->hash($m); if ($emLen < $this->hLen + $sLen + 2) { @@ -2800,7 +1855,7 @@ class RSA * * @access private * @param string $m - * @return string + * @return bool|string */ function _rsassa_pss_sign($m) { @@ -2827,14 +1882,13 @@ class RSA * @access private * @param string $m * @param string $s - * @return string + * @return bool|string */ function _rsassa_pss_verify($m, $s) { // Length checking if (strlen($s) != $this->k) { - user_error('Invalid signature'); return false; } @@ -2844,13 +1898,8 @@ class RSA $s2 = $this->_os2ip($s); $m2 = $this->_rsavp1($s2); - if ($m2 === false) { - user_error('Invalid signature'); - return false; - } $em = $this->_i2osp($m2, $modBits >> 3); if ($em === false) { - user_error('Invalid signature'); return false; } @@ -2867,41 +1916,48 @@ class RSA * @access private * @param string $m * @param int $emLen + * @throws \LengthException if the intended encoded message length is too short * @return string */ function _emsa_pkcs1_v1_5_encode($m, $emLen) { $h = $this->hash->hash($m); - if ($h === false) { - return false; - } // see http://tools.ietf.org/html/rfc3447#page-43 switch ($this->hashName) { case 'md2': - $t = pack('H*', '3020300c06082a864886f70d020205000410'); + $t = "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10"; break; case 'md5': - $t = pack('H*', '3020300c06082a864886f70d020505000410'); + $t = "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10"; break; case 'sha1': - $t = pack('H*', '3021300906052b0e03021a05000414'); + $t = "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14"; break; case 'sha256': - $t = pack('H*', '3031300d060960864801650304020105000420'); + $t = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20"; break; case 'sha384': - $t = pack('H*', '3041300d060960864801650304020205000430'); + $t = "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30"; break; case 'sha512': - $t = pack('H*', '3051300d060960864801650304020305000440'); + $t = "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40"; + break; + // from https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf#page=40 + case 'sha224': + $t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c"; + break; + case 'sha512/224': + $t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x05\x05\x00\x04\x1c"; + break; + case 'sha512/256': + $t = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06\x05\x00\x04\x20"; } $t.= $h; $tLen = strlen($t); if ($emLen < $tLen + 11) { - user_error('Intended encoded message length too short'); - return false; + throw new \LengthException('Intended encoded message length too short'); } $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); @@ -2918,16 +1974,19 @@ class RSA * * @access private * @param string $m - * @return string + * @throws \LengthException if the RSA modulus is too short + * @return bool|string */ function _rsassa_pkcs1_v1_5_sign($m) { // EMSA-PKCS1-v1_5 encoding - $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); - if ($em === false) { - user_error('RSA modulus too short'); - return false; + // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus + // too short" and stop. + try { + $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); + } catch (\LengthException $e) { + throw new \LengthException('RSA modulus too short'); } // RSA signature @@ -2948,14 +2007,64 @@ class RSA * * @access private * @param string $m - * @return string + * @param string $s + * @throws \LengthException if the RSA modulus is too short + * @return bool */ function _rsassa_pkcs1_v1_5_verify($m, $s) { // Length checking if (strlen($s) != $this->k) { - user_error('Invalid signature'); + return false; + } + + // RSA verification + + $s = $this->_os2ip($s); + $m2 = $this->_rsavp1($s); + $em = $this->_i2osp($m2, $this->k); + if ($em === false) { + return false; + } + + // EMSA-PKCS1-v1_5 encoding + + // If the encoding operation outputs "intended encoded message length too short," output "RSA modulus + // too short" and stop. + try { + $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); + } catch (\LengthException $e) { + throw new \LengthException('RSA modulus too short'); + } + + // Compare + return $this->_equals($em, $em2); + } + + /** + * RSASSA-PKCS1-V1_5-VERIFY (relaxed matching) + * + * Per {@link http://tools.ietf.org/html/rfc3447#page-43 RFC3447#page-43} PKCS1 v1.5 + * specified the use BER encoding rather than DER encoding that PKCS1 v2.0 specified. + * This means that under rare conditions you can have a perfectly valid v1.5 signature + * that fails to validate with _rsassa_pkcs1_v1_5_verify(). PKCS1 v2.1 also recommends + * that if you're going to validate these types of signatures you "should indicate + * whether the underlying BER encoding is a DER encoding and hence whether the signature + * is valid with respect to the specification given in [PKCS1 v2.0+]". so if you do + * $rsa->getLastPadding() and get RSA::PADDING_RELAXED_PKCS1 back instead of + * RSA::PADDING_PKCS1... that means BER encoding was used. + * + * @access private + * @param string $m + * @param string $s + * @return bool + */ + function _rsassa_pkcs1_v1_5_relaxed_verify($m, $s) + { + // Length checking + + if (strlen($s) != $this->k) { return false; } @@ -2964,122 +2073,104 @@ class RSA $s = $this->_os2ip($s); $m2 = $this->_rsavp1($s); if ($m2 === false) { - user_error('Invalid signature'); return false; } $em = $this->_i2osp($m2, $this->k); if ($em === false) { - user_error('Invalid signature'); return false; } - // EMSA-PKCS1-v1_5 encoding - - $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); - if ($em2 === false) { - user_error('RSA modulus too short'); + if ($this->_string_shift($em, 2) != "\0\1") { return false; } - // Compare + $em = ltrim($em, "\xFF"); + if ($this->_string_shift($em) != "\0") { + return false; + } + + $asn1 = new ASN1(); + $decoded = $asn1->decodeBER($em); + if (!is_array($decoded) || empty($decoded[0]) || strlen($em) > $decoded[0]['length']) { + return false; + } + + $AlgorithmIdentifier = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'algorithm' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), + 'parameters' => array( + 'type' => ASN1::TYPE_ANY, + 'optional' => true + ) + ) + ); + + $DigestInfo = array( + 'type' => ASN1::TYPE_SEQUENCE, + 'children' => array( + 'digestAlgorithm' => $AlgorithmIdentifier, + 'digest' => array('type' => ASN1::TYPE_OCTET_STRING) + ) + ); + + $oids = array( + '1.2.840.113549.2.2' => 'md2', + '1.2.840.113549.2.4' => 'md4', // from PKCS1 v1.5 + '1.2.840.113549.2.5' => 'md5', + '1.3.14.3.2.26' => 'sha1', + '2.16.840.1.101.3.4.2.1' => 'sha256', + '2.16.840.1.101.3.4.2.2' => 'sha384', + '2.16.840.1.101.3.4.2.3' => 'sha512', + // from PKCS1 v2.2 + '2.16.840.1.101.3.4.2.4' => 'sha224', + '2.16.840.1.101.3.4.2.5' => 'sha512/224', + '2.16.840.1.101.3.4.2.6' => 'sha512/256', + ); + + $asn1->loadOIDs($oids); + + $decoded = $asn1->asn1map($decoded[0], $DigestInfo); + if (!isset($decoded) || $decoded === false) { + return false; + } + + if (!in_array($decoded['digestAlgorithm']['algorithm'], $oids)) { + return false; + } + + $hash = new Hash($decoded['digestAlgorithm']['algorithm']); + $em = $hash->hash($m); + $em2 = Base64::decode($decoded['digest']); + return $this->_equals($em, $em2); } - /** - * Set Encryption Mode - * - * Valid values include self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1. - * - * @access public - * @param int $mode - */ - function setEncryptionMode($mode) - { - $this->encryptionMode = $mode; - } - - /** - * Set Signature Mode - * - * Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1 - * - * @access public - * @param int $mode - */ - function setSignatureMode($mode) - { - $this->signatureMode = $mode; - } - - /** - * Set public key comment. - * - * @access public - * @param string $comment - */ - function setComment($comment) - { - $this->comment = $comment; - } - - /** - * Get public key comment. - * - * @access public - * @return string - */ - function getComment() - { - return $this->comment; - } - /** * Encryption * - * Both self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1 both place limits on how long $plaintext can be. + * Both self::PADDING_OAEP and self::PADDING_PKCS1 both place limits on how long $plaintext can be. * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will * be concatenated together. * * @see self::decrypt() * @access public * @param string $plaintext - * @return string + * @param int $padding optional + * @return bool|string + * @throws \LengthException if the RSA modulus is too short */ - function encrypt($plaintext) + function encrypt($plaintext, $padding = self::PADDING_OAEP) { - switch ($this->encryptionMode) { - case self::ENCRYPTION_NONE: - $plaintext = str_split($plaintext, $this->k); - $ciphertext = ''; - foreach ($plaintext as $m) { - $ciphertext.= $this->_raw_encrypt($m); - } - return $ciphertext; - case self::ENCRYPTION_PKCS1: - $length = $this->k - 11; - if ($length <= 0) { - return false; - } - - $plaintext = str_split($plaintext, $length); - $ciphertext = ''; - foreach ($plaintext as $m) { - $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m); - } - return $ciphertext; - //case self::ENCRYPTION_OAEP: + switch ($padding) { + case self::PADDING_NONE: + return $this->_raw_encrypt($plaintext); + case self::PADDING_PKCS15_COMPAT: + case self::PADDING_PKCS1: + return $this->_rsaes_pkcs1_v1_5_encrypt($plaintext, $padding == self::PADDING_PKCS15_COMPAT); + //case self::PADDING_OAEP: default: - $length = $this->k - 2 * $this->hLen - 2; - if ($length <= 0) { - return false; - } - - $plaintext = str_split($plaintext, $length); - $ciphertext = ''; - foreach ($plaintext as $m) { - $ciphertext.= $this->_rsaes_oaep_encrypt($m); - } - return $ciphertext; + return $this->_rsaes_oaep_encrypt($plaintext); } } @@ -3089,40 +2180,20 @@ class RSA * @see self::encrypt() * @access public * @param string $plaintext - * @return string + * @param int $padding optional + * @return bool|string */ - function decrypt($ciphertext) + function decrypt($ciphertext, $padding = self::PADDING_OAEP) { - if ($this->k <= 0) { - return false; - } - - $ciphertext = str_split($ciphertext, $this->k); - $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT); - - $plaintext = ''; - - switch ($this->encryptionMode) { - case self::ENCRYPTION_NONE: - $decrypt = '_raw_encrypt'; - break; - case self::ENCRYPTION_PKCS1: - $decrypt = '_rsaes_pkcs1_v1_5_decrypt'; - break; - //case self::ENCRYPTION_OAEP: + switch ($padding) { + case self::PADDING_NONE: + return $this->_raw_encrypt($ciphertext); + case self::PADDING_PKCS1: + return $this->_rsaes_pkcs1_v1_5_decrypt($ciphertext); + //case self::PADDING_OAEP: default: - $decrypt = '_rsaes_oaep_decrypt'; + return $this->_rsaes_oaep_decrypt($ciphertext); } - - foreach ($ciphertext as $c) { - $temp = $this->$decrypt($c); - if ($temp === false) { - return false; - } - $plaintext.= $temp; - } - - return $plaintext; } /** @@ -3131,18 +2202,20 @@ class RSA * @see self::verify() * @access public * @param string $message + * @param int $padding optional * @return string */ - function sign($message) + function sign($message, $padding = self::PADDING_PSS) { if (empty($this->modulus) || empty($this->exponent)) { return false; } - switch ($this->signatureMode) { - case self::SIGNATURE_PKCS1: + switch ($padding) { + case self::PADDING_PKCS1: + case self::PADDING_RELAXED_PKCS1: return $this->_rsassa_pkcs1_v1_5_sign($message); - //case self::SIGNATURE_PSS: + //case self::PADDING_PSS: default: return $this->_rsassa_pss_sign($message); } @@ -3155,47 +2228,23 @@ class RSA * @access public * @param string $message * @param string $signature + * @param int $padding optional * @return bool */ - function verify($message, $signature) + function verify($message, $signature, $padding = self::PADDING_PSS) { if (empty($this->modulus) || empty($this->exponent)) { return false; } - switch ($this->signatureMode) { - case self::SIGNATURE_PKCS1: + switch ($padding) { + case self::PADDING_RELAXED_PKCS1: + return $this->_rsassa_pkcs1_v1_5_relaxed_verify($message, $signature); + case self::PADDING_PKCS1: return $this->_rsassa_pkcs1_v1_5_verify($message, $signature); - //case self::SIGNATURE_PSS: + //case self::PADDING_PSS: default: return $this->_rsassa_pss_verify($message, $signature); } } - - /** - * Extract raw BER from Base64 encoding - * - * @access private - * @param string $str - * @return string - */ - function _extractBER($str) - { - /* X.509 certs are assumed to be base64 encoded but sometimes they'll have additional things in them - * above and beyond the ceritificate. - * ie. some may have the following preceding the -----BEGIN CERTIFICATE----- line: - * - * Bag Attributes - * localKeyID: 01 00 00 00 - * subject=/O=organization/OU=org unit/CN=common name - * issuer=/O=organization/CN=common name - */ - $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); - // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff - $temp = preg_replace('#-+[^-]+-+#', '', $temp); - // remove new lines - $temp = str_replace(array("\r", "\n", ' '), '', $temp); - $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; - return $temp != false ? $temp : $str; - } } diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php index 01e34cc367..5e412e8fb8 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php @@ -41,72 +41,22 @@ class Random * eg. for RSA key generation. * * @param int $length + * @throws \RuntimeException if a symmetric cipher is needed but not loaded * @return string */ static function string($length) { - if (!$length) { - return ''; - } - - if (version_compare(PHP_VERSION, '7.0.0', '>=')) { - try { - return \random_bytes($length); - } catch (\Throwable $e) { - // If a sufficient source of randomness is unavailable, random_bytes() will throw an - // object that implements the Throwable interface (Exception, TypeError, Error). - // We don't actually need to do anything here. The string() method should just continue - // as normal. Note, however, that if we don't have a sufficient source of randomness for - // random_bytes(), most of the other calls here will fail too, so we'll end up using - // the PHP implementation. - } - } - - if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { - // method 1. prior to PHP 5.3 this would call rand() on windows hence the function_exists('class_alias') call. - // ie. class_alias is a function that was introduced in PHP 5.3 - if (extension_loaded('mcrypt') && function_exists('class_alias')) { - return @mcrypt_create_iv($length); - } - // method 2. openssl_random_pseudo_bytes was introduced in PHP 5.3.0 but prior to PHP 5.3.4 there was, - // to quote , "possible blocking behavior". as of 5.3.4 - // openssl_random_pseudo_bytes and mcrypt_create_iv do the exact same thing on Windows. ie. they both - // call php_win32_get_random_bytes(): - // - // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/openssl/openssl.c#L5008 - // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1392 - // - // php_win32_get_random_bytes() is defined thusly: - // - // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/win32/winutil.c#L80 - // - // we're calling it, all the same, in the off chance that the mcrypt extension is not available - if (extension_loaded('openssl') && version_compare(PHP_VERSION, '5.3.4', '>=')) { - return openssl_random_pseudo_bytes($length); - } - } else { - // method 1. the fastest - if (extension_loaded('openssl')) { - return openssl_random_pseudo_bytes($length); - } - // method 2 - static $fp = true; - if ($fp === true) { - // warning's will be output unles the error suppression operator is used. errors such as - // "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc. - $fp = @fopen('/dev/urandom', 'rb'); - } - if ($fp !== true && $fp !== false) { // surprisingly faster than !is_bool() or is_resource() - return fread($fp, $length); - } - // method 3. pretty much does the same thing as method 2 per the following url: - // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391 - // surprisingly slower than method 2. maybe that's because mcrypt_create_iv does a bunch of error checking that we're - // not doing. regardless, this'll only be called if this PHP script couldn't open /dev/urandom due to open_basedir - // restrictions or some such - if (extension_loaded('mcrypt')) { - return @mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); - } + try { + return \random_bytes($length); + } catch (\Exception $e) { + // random_compat will throw an Exception, which in PHP 5 does not implement Throwable + } catch (\Throwable $e) { + // If a sufficient source of randomness is unavailable, random_bytes() will throw an + // object that implements the Throwable interface (Exception, TypeError, Error). + // We don't actually need to do anything here. The string() method should just continue + // as normal. Note, however, that if we don't have a sufficient source of randomness for + // random_bytes(), most of the other calls here will fail too, so we'll end up using + // the PHP implementation. } // at this point we have no choice but to use a pure-PHP CSPRNG @@ -143,15 +93,14 @@ class Random session_cache_limiter(''); session_start(); - $v = $seed = $_SESSION['seed'] = pack('H*', sha1( - (isset($_SERVER) ? phpseclib_safe_serialize($_SERVER) : '') . - (isset($_POST) ? phpseclib_safe_serialize($_POST) : '') . - (isset($_GET) ? phpseclib_safe_serialize($_GET) : '') . - (isset($_COOKIE) ? phpseclib_safe_serialize($_COOKIE) : '') . - phpseclib_safe_serialize($GLOBALS) . - phpseclib_safe_serialize($_SESSION) . - phpseclib_safe_serialize($_OLD_SESSION) - )); + $v = (isset($_SERVER) ? self::safe_serialize($_SERVER) : '') . + (isset($_POST) ? self::safe_serialize($_POST) : '') . + (isset($_GET) ? self::safe_serialize($_GET) : '') . + (isset($_COOKIE) ? self::safe_serialize($_COOKIE) : '') . + self::safe_serialize($GLOBALS) . + self::safe_serialize($_SESSION) . + self::safe_serialize($_OLD_SESSION); + $v = $seed = $_SESSION['seed'] = sha1($v, true); if (!isset($_SESSION['count'])) { $_SESSION['count'] = 0; } @@ -182,8 +131,8 @@ class Random // http://tools.ietf.org/html/rfc4253#section-7.2 // // see the is_string($crypto) part for an example of how to expand the keys - $key = pack('H*', sha1($seed . 'A')); - $iv = pack('H*', sha1($seed . 'C')); + $key = sha1($seed . 'A', true); + $iv = sha1($seed . 'C', true); // ciphers are used as per the nist.gov link below. also, see this link: // @@ -208,8 +157,7 @@ class Random $crypto = new RC4(); break; default: - user_error(__CLASS__ . ' requires at least one symmetric cipher be loaded'); - return false; + throw new \RuntimeException(__CLASS__ . ' requires at least one symmetric cipher be loaded'); } $crypto->setKey($key); @@ -236,19 +184,16 @@ class Random } return substr($result, 0, $length); } -} -if (!function_exists('phpseclib_safe_serialize')) { /** * Safely serialize variables * - * If a class has a private __sleep() method it'll give a fatal error on PHP 5.2 and earlier. - * PHP 5.3 will emit a warning. + * If a class has a private __sleep() it'll emit a warning * * @param mixed $arr * @access public */ - function phpseclib_safe_serialize(&$arr) + function safe_serialize(&$arr) { if (is_object($arr)) { return ''; @@ -265,7 +210,7 @@ if (!function_exists('phpseclib_safe_serialize')) { foreach (array_keys($arr) as $key) { // do not recurse on the '__phpseclib_marker' key itself, for smaller memory usage if ($key !== '__phpseclib_marker') { - $safearr[$key] = phpseclib_safe_serialize($arr[$key]); + $safearr[$key] = self::safe_serialize($arr[$key]); } } unset($arr['__phpseclib_marker']); diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php index 3648a1972f..c98f02e252 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php @@ -168,11 +168,26 @@ class Rijndael extends Base */ var $kl; + /** + * Default Constructor. + * + * @param int $mode + * @access public + * @throws \InvalidArgumentException if an invalid / unsupported mode is provided + */ + function __construct($mode) + { + if ($mode == self::MODE_STREAM) { + throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode'); + } + + parent::__construct($mode); + } + /** * Sets the key length. * - * Valid key lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to - * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount. + * Valid key lengths are 128, 160, 192, 224, and 256. * * Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined * and the most (if not all) implementations are not able using 160/224-bit keys but round/pad them up to @@ -186,49 +201,75 @@ class Rijndael extends Base * This results then in slower encryption. * * @access public + * @throws \LengthException if the key length is invalid * @param int $length */ function setKeyLength($length) { - switch (true) { - case $length <= 128: - $this->key_length = 16; - break; - case $length <= 160: - $this->key_length = 20; - break; - case $length <= 192: - $this->key_length = 24; - break; - case $length <= 224: - $this->key_length = 28; + switch ($length) { + case 128: + case 160: + case 192: + case 224: + case 256: + $this->key_length = $length >> 3; break; default: - $this->key_length = 32; + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported'); } parent::setKeyLength($length); } + /** + * Sets the key. + * + * Rijndael supports five different key lengths + * + * @see setKeyLength() + * @access public + * @param string $key + * @throws \LengthException if the key length isn't supported + */ + function setKey($key) + { + switch (strlen($key)) { + case 16: + case 20: + case 24: + case 28: + case 32: + break; + default: + throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 20, 24, 28 or 32 are supported'); + } + + parent::setKey($key); + } + /** * Sets the block length * - * Valid block lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to - * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount. + * Valid block lengths are 128, 160, 192, 224, and 256. * * @access public * @param int $length */ function setBlockLength($length) { - $length >>= 5; - if ($length > 8) { - $length = 8; - } elseif ($length < 4) { - $length = 4; + switch ($length) { + case 128: + case 160: + case 192: + case 224: + case 256: + break; + default: + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported'); } - $this->Nb = $length; - $this->block_size = $length << 2; + + $this->Nb = $length >> 5; + $this->block_size = $length >> 3; $this->changed = true; $this->_setEngine(); } diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php index a2c41668ae..29c6eb9d0d 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php @@ -128,7 +128,7 @@ class TripleDES extends DES /** * Default Constructor. * - * Determines whether or not the mcrypt extension should be used. + * Determines whether or not the mcrypt or OpenSSL extensions should be used. * * $mode could be: * @@ -142,16 +142,14 @@ class TripleDES extends DES * * - \phpseclib\Crypt\Base::MODE_OFB * - * - \phpseclib\Crypt\TripleDES::MODE_3CBC - * - * If not explicitly set, \phpseclib\Crypt\Base::MODE_CBC will be used. + * - \phpseclib\Crypt\TripleDES::MODE_3CB * * @see \phpseclib\Crypt\DES::__construct() * @see \phpseclib\Crypt\Base::__construct() * @param int $mode * @access public */ - function __construct($mode = Base::MODE_CBC) + function __construct($mode) { switch ($mode) { // In case of self::MODE_3CBC, we init as CRYPT_DES_MODE_CBC @@ -200,10 +198,9 @@ class TripleDES extends DES } /** - * Sets the initialization vector. (optional) + * Sets the initialization vector. * - * SetIV is not required when \phpseclib\Crypt\Base::MODE_ECB is being used. If not explicitly set, it'll be assumed - * to be all zero's. + * SetIV is not required when \phpseclib\Crypt\Base::MODE_ECB is being used. * * @see \phpseclib\Crypt\Base::setIV() * @access public @@ -222,24 +219,23 @@ class TripleDES extends DES /** * Sets the key length. * - * Valid key lengths are 64, 128 and 192 + * Valid key lengths are 128 and 192 bits. + * + * If you want to use a 64-bit key use DES.php * * @see \phpseclib\Crypt\Base:setKeyLength() * @access public + * @throws \LengthException if the key length is invalid * @param int $length */ function setKeyLength($length) { - $length >>= 3; - switch (true) { - case $length <= 8: - $this->key_length = 8; - break; - case $length <= 16: - $this->key_length = 16; + switch ($length) { + case 128: + case 192: break; default: - $this->key_length = 24; + throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128 or 192 bits are supported'); } parent::setKeyLength($length); @@ -248,36 +244,38 @@ class TripleDES extends DES /** * Sets the key. * - * Keys can be of any length. Triple DES, itself, can use 128-bit (eg. strlen($key) == 16) or - * 192-bit (eg. strlen($key) == 24) keys. This function pads and truncates $key as appropriate. + * Triple DES can use 128-bit (eg. strlen($key) == 16) or 192-bit (eg. strlen($key) == 24) keys. * * DES also requires that every eighth bit be a parity bit, however, we'll ignore that. * - * If the key is not explicitly set, it'll be assumed to be all null bytes. - * * @access public * @see \phpseclib\Crypt\DES::setKey() * @see \phpseclib\Crypt\Base::setKey() + * @throws \LengthException if the key length is invalid * @param string $key */ function setKey($key) { - $length = $this->explicit_key_length ? $this->key_length : strlen($key); - if ($length > 8) { - $key = str_pad(substr($key, 0, 24), 24, chr(0)); - // if $key is between 64 and 128-bits, use the first 64-bits as the last, per this: - // http://php.net/function.mcrypt-encrypt#47973 - $key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24); - } else { - $key = str_pad($key, 8, chr(0)); + if ($this->explicit_key_length !== false && strlen($key) != $this->explicit_key_length) { + throw new \LengthException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . strlen($key) . ' bytes'); } - parent::setKey($key); - // And in case of self::MODE_3CBC: - // if key <= 64bits we not need the 3 $des to work, - // because we will then act as regular DES-CBC with just a <= 64bit key. - // So only if the key > 64bits (> 8 bytes) we will call setKey() for the 3 $des. - if ($this->mode_3cbc && $length > 8) { + switch (strlen($key)) { + case 16: + $key.= substr($key, 0, 8); + case 24: + break; + default: + throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16 or 24 are supported'); + } + + // copied from Base::setKey() + $this->key = $key; + $this->key_length = strlen($key); + $this->changed = true; + $this->_setEngine(); + + if ($this->mode_3cbc) { $this->des[0]->setKey(substr($key, 0, 8)); $this->des[1]->setKey(substr($key, 8, 8)); $this->des[2]->setKey(substr($key, 16, 8)); diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php index 70980a2ff1..e4d910db94 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php @@ -368,6 +368,22 @@ class Twofish extends Base */ var $key_length = 16; + /** + * Default Constructor. + * + * @param int $mode + * @access public + * @throws \InvalidArgumentException if an invalid / unsupported mode is provided + */ + function __construct($mode) + { + if ($mode == self::MODE_STREAM) { + throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode'); + } + + parent::__construct($mode); + } + /** * Sets the key length. * @@ -378,20 +394,42 @@ class Twofish extends Base */ function setKeyLength($length) { - switch (true) { - case $length <= 128: - $this->key_length = 16; - break; - case $length <= 192: - $this->key_length = 24; + switch ($length) { + case 128: + case 192: + case 256: break; default: - $this->key_length = 32; + throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported'); } parent::setKeyLength($length); } + /** + * Sets the key. + * + * Rijndael supports five different key lengths + * + * @see setKeyLength() + * @access public + * @param string $key + * @throws \LengthException if the key length isn't supported + */ + function setKey($key) + { + switch (strlen($key)) { + case 16: + case 24: + case 32: + break; + default: + throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported'); + } + + parent::setKey($key); + } + /** * Setup the key (expansion) * @@ -432,10 +470,8 @@ class Twofish extends Base $m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^ $m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]]; $B = ($B << 8) | ($B >> 24 & 0xff); - $A = $this->safe_intval($A + $B); - $K[] = $A; - $A = $this->safe_intval($A + $B); - $K[] = ($A << 9 | $A >> 23 & 0x1ff); + $K[] = $A+= $B; + $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); } for ($i = 0; $i < 256; ++$i) { $S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0]; @@ -458,10 +494,8 @@ class Twofish extends Base $m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ $m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]]; $B = ($B << 8) | ($B >> 24 & 0xff); - $A = $this->safe_intval($A + $B); - $K[] = $A; - $A = $this->safe_intval($A + $B); - $K[] = ($A << 9 | $A >> 23 & 0x1ff); + $K[] = $A+= $B; + $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); } for ($i = 0; $i < 256; ++$i) { $S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0]; @@ -485,10 +519,8 @@ class Twofish extends Base $m2[$q1[$q0[$q0[$q0[$j] ^ $key[31]] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^ $m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^ $key[16]] ^ $key[8]]; $B = ($B << 8) | ($B >> 24 & 0xff); - $A = $this->safe_intval($A + $B); - $K[] = $A; - $A = $this->safe_intval($A + $B); - $K[] = ($A << 9 | $A >> 23 & 0x1ff); + $K[] = $A+= $B; + $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff); } for ($i = 0; $i < 256; ++$i) { $S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4] ^ $s0]; @@ -584,9 +616,9 @@ class Twofish extends Base $S1[ $R1 & 0xff] ^ $S2[($R1 >> 8) & 0xff] ^ $S3[($R1 >> 16) & 0xff]; - $R2^= $this->safe_intval($t0 + $t1 + $K[++$ki]); + $R2^= $t0 + $t1 + $K[++$ki]; $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); - $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ $this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]); + $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ($t0 + ($t1 << 1) + $K[++$ki]); $t0 = $S0[ $R2 & 0xff] ^ $S1[($R2 >> 8) & 0xff] ^ @@ -596,9 +628,9 @@ class Twofish extends Base $S1[ $R3 & 0xff] ^ $S2[($R3 >> 8) & 0xff] ^ $S3[($R3 >> 16) & 0xff]; - $R0^= $this->safe_intval($t0 + $t1 + $K[++$ki]); + $R0^= ($t0 + $t1 + $K[++$ki]); $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); - $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ $this->safe_intval($t0 + ($t1 << 1) + $K[++$ki]); + $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ($t0 + ($t1 << 1) + $K[++$ki]); } // @codingStandardsIgnoreStart @@ -640,9 +672,9 @@ class Twofish extends Base $S1[$R1 & 0xff] ^ $S2[$R1 >> 8 & 0xff] ^ $S3[$R1 >> 16 & 0xff]; - $R3^= $this->safe_intval($t0 + ($t1 << 1) + $K[--$ki]); + $R3^= $t0 + ($t1 << 1) + $K[--$ki]; $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; - $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ $this->safe_intval($t0 + $t1 + $K[--$ki]); + $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ($t0 + $t1 + $K[--$ki]); $t0 = $S0[$R2 & 0xff] ^ $S1[$R2 >> 8 & 0xff] ^ @@ -652,9 +684,9 @@ class Twofish extends Base $S1[$R3 & 0xff] ^ $S2[$R3 >> 8 & 0xff] ^ $S3[$R3 >> 16 & 0xff]; - $R1^= $this->safe_intval($t0 + ($t1 << 1) + $K[--$ki]); + $R1^= $t0 + ($t1 << 1) + $K[--$ki]; $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; - $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ $this->safe_intval($t0 + $t1 + $K[--$ki]); + $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ($t0 + $t1 + $K[--$ki]); } // @codingStandardsIgnoreStart @@ -679,14 +711,12 @@ class Twofish extends Base // (Currently, for Crypt_Twofish, one generated $lambda_function cost on php5.5@32bit ~140kb unfreeable mem and ~240kb on php5.5@64bit) $gen_hi_opt_code = (bool)(count($lambda_functions) < 10); - // Generation of a unique hash for our generated code + // Generation of a uniqe hash for our generated code $code_hash = "Crypt_Twofish, {$this->mode}"; if ($gen_hi_opt_code) { $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); } - $safeint = $this->safe_intval_inline(); - if (!isset($lambda_functions[$code_hash])) { switch (true) { case $gen_hi_opt_code: @@ -735,9 +765,9 @@ class Twofish extends Base $S1[ $R1 & 0xff] ^ $S2[($R1 >> 8) & 0xff] ^ $S3[($R1 >> 16) & 0xff]; - $R2^= ' . sprintf($safeint, '$t0 + $t1 + ' . $K[++$ki]) . '; + $R2^= ($t0 + $t1 + '.$K[++$ki].'); $R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); - $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . '; + $R3 = ((($R3 >> 31) & 1) | ($R3 << 1)) ^ ($t0 + ($t1 << 1) + '.$K[++$ki].'); $t0 = $S0[ $R2 & 0xff] ^ $S1[($R2 >> 8) & 0xff] ^ @@ -747,16 +777,16 @@ class Twofish extends Base $S1[ $R3 & 0xff] ^ $S2[($R3 >> 8) & 0xff] ^ $S3[($R3 >> 16) & 0xff]; - $R0^= ' . sprintf($safeint, '($t0 + $t1 + ' . $K[++$ki] . ')') . '; + $R0^= ($t0 + $t1 + '.$K[++$ki].'); $R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); - $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ' . sprintf($safeint, '($t0 + ($t1 << 1) + ' . $K[++$ki] . ')') . '; + $R1 = ((($R1 >> 31) & 1) | ($R1 << 1)) ^ ($t0 + ($t1 << 1) + '.$K[++$ki].'); '; } $encrypt_block.= ' - $in = pack("V4", ' . $K[4] . ' ^ $R2, - ' . $K[5] . ' ^ $R3, - ' . $K[6] . ' ^ $R0, - ' . $K[7] . ' ^ $R1); + $in = pack("V4", '.$K[4].' ^ $R2, + '.$K[5].' ^ $R3, + '.$K[6].' ^ $R0, + '.$K[7].' ^ $R1); '; // Generating decrypt code: @@ -777,9 +807,9 @@ class Twofish extends Base $S1[$R1 & 0xff] ^ $S2[$R1 >> 8 & 0xff] ^ $S3[$R1 >> 16 & 0xff]; - $R3^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . '; + $R3^= $t0 + ($t1 << 1) + '.$K[--$ki].'; $R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31; - $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] . ')') . '; + $R2 = ($R2 >> 31 & 0x1 | $R2 << 1) ^ ($t0 + $t1 + '.$K[--$ki].'); $t0 = $S0[$R2 & 0xff] ^ $S1[$R2 >> 8 & 0xff] ^ @@ -789,16 +819,16 @@ class Twofish extends Base $S1[$R3 & 0xff] ^ $S2[$R3 >> 8 & 0xff] ^ $S3[$R3 >> 16 & 0xff]; - $R1^= ' . sprintf($safeint, '$t0 + ($t1 << 1) + ' . $K[--$ki]) . '; + $R1^= $t0 + ($t1 << 1) + '.$K[--$ki].'; $R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31; - $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ' . sprintf($safeint, '($t0 + $t1 + '.$K[--$ki] . ')') . '; + $R0 = ($R0 >> 31 & 0x1 | $R0 << 1) ^ ($t0 + $t1 + '.$K[--$ki].'); '; } $decrypt_block.= ' - $in = pack("V4", ' . $K[0] . ' ^ $R2, - ' . $K[1] . ' ^ $R3, - ' . $K[2] . ' ^ $R0, - ' . $K[3] . ' ^ $R1); + $in = pack("V4", '.$K[0].' ^ $R2, + '.$K[1].' ^ $R3, + '.$K[2].' ^ $R0, + '.$K[3].' ^ $R1); '; $lambda_functions[$code_hash] = $this->_createInlineCryptFunction( diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php b/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php index 5ff1f2ea1a..1f3eecb30a 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php +++ b/vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php @@ -305,9 +305,6 @@ class ANSI case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): // Move cursor left n lines $this->old_x = $this->x; $this->x-= $match[1]; - if ($this->x < 0) { - $this->x = 0; - } break; case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window break; @@ -419,7 +416,7 @@ class ANSI if ($this->x > $this->max_x) { $this->x = 0; - $this->_newLine(); + $this->y++; } else { $this->x++; } diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php index 3aaa309003..ad59f69bdb 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php +++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php @@ -23,10 +23,9 @@ namespace phpseclib\File; +use ParagonIE\ConstantTime\Base64; use phpseclib\File\ASN1\Element; use phpseclib\Math\BigInteger; -use DateTime; -use DateTimeZone; /** * Pure-PHP ASN.1 Parser @@ -226,15 +225,14 @@ class ASN1 * * @param string $encoded * @param int $start - * @param int $encoded_pos * @return array * @access private */ - function _decode_ber($encoded, $start = 0, $encoded_pos = 0) + function _decode_ber($encoded, $start = 0) { $current = array('start' => $start); - $type = ord($encoded[$encoded_pos++]); + $type = ord($this->_string_shift($encoded)); $start++; $constructed = ($type >> 5) & 1; @@ -244,27 +242,25 @@ class ASN1 $tag = 0; // process septets (since the eighth bit is ignored, it's not an octet) do { - $temp = ord($encoded[$encoded_pos++]); - $loop = $temp >> 7; + $loop = ord($encoded[0]) >> 7; $tag <<= 7; - $tag |= $temp & 0x7F; + $tag |= ord($this->_string_shift($encoded)) & 0x7F; $start++; } while ($loop); } // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13 - $length = ord($encoded[$encoded_pos++]); + $length = ord($this->_string_shift($encoded)); $start++; if ($length == 0x80) { // indefinite length // "[A sender shall] use the indefinite form (see 8.1.3.6) if the encoding is constructed and is not all // immediately available." -- paragraph 8.1.3.2.c - $length = strlen($encoded) - $encoded_pos; + $length = strlen($encoded); } elseif ($length & 0x80) { // definite length, long form // technically, the long form of the length can be represented by up to 126 octets (bytes), but we'll only // support it up to four. $length&= 0x7F; - $temp = substr($encoded, $encoded_pos, $length); - $encoded_pos += $length; + $temp = $this->_string_shift($encoded, $length); // tags of indefinte length don't really have a header length; this length includes the tag $current+= array('headerlength' => $length + 2); $start+= $length; @@ -273,12 +269,11 @@ class ASN1 $current+= array('headerlength' => 2); } - if ($length > (strlen($encoded) - $encoded_pos)) { + if ($length > strlen($encoded)) { return false; } - $content = substr($encoded, $encoded_pos, $length); - $content_pos = 0; + $content = $this->_string_shift($encoded, $length); // at this point $length can be overwritten. it's only accurate for definite length things as is @@ -308,13 +303,10 @@ class ASN1 $newcontent = array(); $remainingLength = $length; while ($remainingLength > 0) { - $temp = $this->_decode_ber($content, $start, $content_pos); - if ($temp === false) { - break; - } + $temp = $this->_decode_ber($content, $start); $length = $temp['length']; // end-of-content octets - see paragraph 8.1.5 - if (substr($content, $content_pos + $length, 2) == "\0\0") { + if (substr($content, $length, 2) == "\0\0") { $length+= 2; $start+= $length; $newcontent[] = $temp; @@ -323,7 +315,7 @@ class ASN1 $start+= $length; $remainingLength-= $length; $newcontent[] = $temp; - $content_pos += $length; + $this->_string_shift($content, $length); } return array( @@ -347,11 +339,11 @@ class ASN1 //if (strlen($content) != 1) { // return false; //} - $current['content'] = (bool) ord($content[$content_pos]); + $current['content'] = (bool) ord($content[0]); break; case self::TYPE_INTEGER: case self::TYPE_ENUMERATED: - $current['content'] = new BigInteger(substr($content, $content_pos), -256); + $current['content'] = new BigInteger($content, -256); break; case self::TYPE_REAL: // not currently supported return false; @@ -360,13 +352,10 @@ class ASN1 // the number of unused bits in the final subsequent octet. The number shall be in the range zero to // seven. if (!$constructed) { - $current['content'] = substr($content, $content_pos); + $current['content'] = $content; } else { - $temp = $this->_decode_ber($content, $start, $content_pos); - if ($temp === false) { - return false; - } - $length-= (strlen($content) - $content_pos); + $temp = $this->_decode_ber($content, $start); + $length-= strlen($content); $last = count($temp) - 1; for ($i = 0; $i < $last; $i++) { // all subtags should be bit strings @@ -384,16 +373,13 @@ class ASN1 break; case self::TYPE_OCTET_STRING: if (!$constructed) { - $current['content'] = substr($content, $content_pos); + $current['content'] = $content; } else { $current['content'] = ''; $length = 0; - while (substr($content, $content_pos, 2) != "\0\0") { - $temp = $this->_decode_ber($content, $length + $start, $content_pos); - if ($temp === false) { - return false; - } - $content_pos += $temp['length']; + while (substr($content, 0, 2) != "\0\0") { + $temp = $this->_decode_ber($content, $length + $start); + $this->_string_shift($content, $temp['length']); // all subtags should be octet strings //if ($temp['type'] != self::TYPE_OCTET_STRING) { // return false; @@ -401,7 +387,7 @@ class ASN1 $current['content'].= $temp['content']; $length+= $temp['length']; } - if (substr($content, $content_pos, 2) == "\0\0") { + if (substr($content, 0, 2) == "\0\0") { $length+= 2; // +2 for the EOC } } @@ -416,25 +402,37 @@ class ASN1 case self::TYPE_SET: $offset = 0; $current['content'] = array(); - $content_len = strlen($content); - while ($content_pos < $content_len) { + while (strlen($content)) { // if indefinite length construction was used and we have an end-of-content string next // see paragraphs 8.1.1.3, 8.1.3.2, 8.1.3.6, 8.1.5, and (for an example) 8.6.4.2 - if (!isset($current['headerlength']) && substr($content, $content_pos, 2) == "\0\0") { + if (!isset($current['headerlength']) && substr($content, 0, 2) == "\0\0") { $length = $offset + 2; // +2 for the EOC break 2; } - $temp = $this->_decode_ber($content, $start + $offset, $content_pos); - if ($temp === false) { - return false; - } - $content_pos += $temp['length']; + $temp = $this->_decode_ber($content, $start + $offset); + $this->_string_shift($content, $temp['length']); $current['content'][] = $temp; $offset+= $temp['length']; } break; case self::TYPE_OBJECT_IDENTIFIER: - $current['content'] = $this->_decodeOID(substr($content, $content_pos)); + $temp = ord($this->_string_shift($content)); + $current['content'] = sprintf('%d.%d', floor($temp / 40), $temp % 40); + $valuen = 0; + // process septets + while (strlen($content)) { + $temp = ord($this->_string_shift($content)); + $valuen <<= 7; + $valuen |= $temp & 0x7F; + if (~$temp & 0x80) { + $current['content'].= ".$valuen"; + $valuen = 0; + } + } + // the eighth bit of the last byte should not be 1 + //if ($temp >> 7) { + // return false; + //} break; /* Each character string type shall be encoded as if it had been declared: [UNIVERSAL x] IMPLICIT OCTET STRING @@ -464,11 +462,11 @@ class ASN1 case self::TYPE_UTF8_STRING: // ???? case self::TYPE_BMP_STRING: - $current['content'] = substr($content, $content_pos); + $current['content'] = $content; break; case self::TYPE_UTC_TIME: case self::TYPE_GENERALIZED_TIME: - $current['content'] = $this->_decodeTime(substr($content, $content_pos), $tag); + $current['content'] = $this->_decodeTime($content, $tag); default: } @@ -500,7 +498,7 @@ class ASN1 switch (true) { case $mapping['type'] == self::TYPE_ANY: $intype = $decoded['type']; - if (isset($decoded['constant']) || !isset($this->ANYmap[$intype]) || (ord($this->encoded[$decoded['start']]) & 0x20)) { + if (isset($decoded['constant']) || !isset($this->ANYmap[$intype]) || ($this->encoded[$decoded['start']] & 0x20)) { return new Element(substr($this->encoded, $decoded['start'], $decoded['length'])); } $inmap = $this->ANYmap[$intype]; @@ -578,7 +576,7 @@ class ASN1 $childClass = $tempClass = self::CLASS_UNIVERSAL; $constant = null; if (isset($temp['constant'])) { - $tempClass = $temp['type']; + $tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC; } if (isset($child['class'])) { $childClass = $child['class']; @@ -641,7 +639,7 @@ class ASN1 $temp = $decoded['content'][$i]; $tempClass = self::CLASS_UNIVERSAL; if (isset($temp['constant'])) { - $tempClass = $temp['type']; + $tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC; } foreach ($mapping['children'] as $key => $child) { @@ -705,7 +703,7 @@ class ASN1 if (isset($mapping['implicit'])) { $decoded['content'] = $this->_decodeTime($decoded['content'], $decoded['type']); } - return $decoded['content'] ? $decoded['content']->format($this->format) : false; + return @date($this->format, $decoded['content']); case self::TYPE_BIT_STRING: if (isset($mapping['mapping'])) { $offset = ord($decoded['content'][0]); @@ -736,7 +734,7 @@ class ASN1 return $values; } case self::TYPE_OCTET_STRING: - return base64_encode($decoded['content']); + return Base64::encode($decoded['content']); case self::TYPE_NULL: return ''; case self::TYPE_BOOLEAN: @@ -796,6 +794,7 @@ class ASN1 * @param string $mapping * @param int $idx * @return string + * @throws \RuntimeException if the input has an error in it * @access private */ function _encode_der($source, $mapping, $idx = null, $special = array()) @@ -822,10 +821,10 @@ class ASN1 case self::TYPE_SET: // Children order is not important, thus process in sequence. case self::TYPE_SEQUENCE: $tag|= 0x20; // set the constructed bit + $value = ''; // ignore the min and max if (isset($mapping['min']) && isset($mapping['max'])) { - $value = array(); $child = $mapping['children']; foreach ($source as $content) { @@ -833,21 +832,11 @@ class ASN1 if ($temp === false) { return false; } - $value[]= $temp; + $value.= $temp; } - /* "The encodings of the component values of a set-of value shall appear in ascending order, the encodings being compared - as octet strings with the shorter components being padded at their trailing end with 0-octets. - NOTE - The padding octets are for comparison purposes only and do not appear in the encodings." - - -- sec 11.6 of http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf */ - if ($mapping['type'] == self::TYPE_SET) { - sort($value); - } - $value = implode($value, ''); break; } - $value = ''; foreach ($mapping['children'] as $key => $child) { if (!array_key_exists($key, $source)) { if (!isset($child['optional'])) { @@ -954,8 +943,7 @@ class ASN1 case self::TYPE_GENERALIZED_TIME: $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y'; $format.= 'mdHis'; - $date = new DateTime($source, new DateTimeZone('GMT')); - $value = $date->format($format) . 'Z'; + $value = @gmdate($format, strtotime($source)) . 'Z'; break; case self::TYPE_BIT_STRING: if (isset($mapping['mapping'])) { @@ -994,10 +982,30 @@ class ASN1 the number of unused bits in the final subsequent octet. The number shall be in the range zero to seven. -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=16 */ - $value = base64_decode($source); + $value = Base64::decode($source); break; case self::TYPE_OBJECT_IDENTIFIER: - $value = $this->_encodeOID($source); + $oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids); + if ($oid === false) { + throw new \RuntimeException('Invalid OID'); + return false; + } + $value = ''; + $parts = explode('.', $oid); + $value = chr(40 * $parts[0] + $parts[1]); + for ($i = 2; $i < count($parts); $i++) { + $temp = ''; + if (!$parts[$i]) { + $temp = "\0"; + } else { + while ($parts[$i]) { + $temp = chr(0x80 | ($parts[$i] & 0x7F)) . $temp; + $parts[$i] >>= 7; + } + $temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F); + } + $value.= $temp; + } break; case self::TYPE_ANY: $loc = $this->location; @@ -1032,7 +1040,7 @@ class ASN1 $filters = $filters[$part]; } if ($filters === false) { - user_error('No filters defined for ' . implode('/', $loc)); + throw new \RuntimeException('No filters defined for ' . implode('/', $loc)); return false; } return $this->_encode_der($source, $filters + $mapping, null, $special); @@ -1056,7 +1064,7 @@ class ASN1 $value = $source ? "\xFF" : "\x00"; break; default: - user_error('Mapping provides no type definition for ' . implode('/', $this->location)); + throw new \RuntimeException('Mapping provides no type definition for ' . implode('/', $this->location)); return false; } @@ -1096,108 +1104,6 @@ class ASN1 return pack('Ca*', 0x80 | strlen($temp), $temp); } - /** - * BER-decode the OID - * - * Called by _decode_ber() - * - * @access private - * @param string $content - * @return string - */ - function _decodeOID($content) - { - static $eighty; - if (!$eighty) { - $eighty = new BigInteger(80); - } - - $oid = array(); - $pos = 0; - $len = strlen($content); - $n = new BigInteger(); - while ($pos < $len) { - $temp = ord($content[$pos++]); - $n = $n->bitwise_leftShift(7); - $n = $n->bitwise_or(new BigInteger($temp & 0x7F)); - if (~$temp & 0x80) { - $oid[] = $n; - $n = new BigInteger(); - } - } - $part1 = array_shift($oid); - $first = floor(ord($content[0]) / 40); - /* - "This packing of the first two object identifier components recognizes that only three values are allocated from the root - node, and at most 39 subsequent values from nodes reached by X = 0 and X = 1." - - -- https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=22 - */ - if ($first <= 2) { // ie. 0 <= ord($content[0]) < 120 (0x78) - array_unshift($oid, ord($content[0]) % 40); - array_unshift($oid, $first); - } else { - array_unshift($oid, $part1->subtract($eighty)); - array_unshift($oid, 2); - } - - return implode('.', $oid); - } - - /** - * DER-encode the OID - * - * Called by _encode_der() - * - * @access private - * @param string $content - * @return string - */ - function _encodeOID($source) - { - static $mask, $zero, $forty; - if (!$mask) { - $mask = new BigInteger(0x7F); - $zero = new BigInteger(); - $forty = new BigInteger(40); - } - - $oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids); - if ($oid === false) { - user_error('Invalid OID'); - return false; - } - $parts = explode('.', $oid); - $part1 = array_shift($parts); - $part2 = array_shift($parts); - - $first = new BigInteger($part1); - $first = $first->multiply($forty); - $first = $first->add(new BigInteger($part2)); - - array_unshift($parts, $first->toString()); - - $value = ''; - foreach ($parts as $part) { - if (!$part) { - $temp = "\0"; - } else { - $temp = ''; - $part = new BigInteger($part); - while (!$part->equals($zero)) { - $submask = $part->bitwise_and($mask); - $submask->setPrecision(8); - $temp = (chr(0x80) | $submask->toBytes()) . $temp; - $part = $part->bitwise_rightShift(7); - } - $temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F); - } - $value.= $temp; - } - - return $value; - } - /** * BER-decode the time * @@ -1218,32 +1124,33 @@ class ASN1 http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2 http://www.obj-sys.com/asn1tutorial/node14.html */ - $format = 'YmdHis'; + $pattern = $tag == self::TYPE_UTC_TIME ? + '#(..)(..)(..)(..)(..)(..)(.*)#' : + '#(....)(..)(..)(..)(..)(..).*([Z+-].*)$#'; + + preg_match($pattern, $content, $matches); + + list(, $year, $month, $day, $hour, $minute, $second, $timezone) = $matches; if ($tag == self::TYPE_UTC_TIME) { - // https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=28 says "the seconds - // element shall always be present" but none-the-less I've seen X509 certs where it isn't and if the - // browsers parse it phpseclib ought to too - if (preg_match('#^(\d{10})(Z|[+-]\d{4})$#', $content, $matches)) { - $content = $matches[1] . '00' . $matches[2]; + $year = $year >= 50 ? "19$year" : "20$year"; + } + + if ($timezone == 'Z') { + $mktime = 'gmmktime'; + $timezone = 0; + } elseif (preg_match('#([+-])(\d\d)(\d\d)#', $timezone, $matches)) { + $mktime = 'gmmktime'; + $timezone = 60 * $matches[3] + 3600 * $matches[2]; + if ($matches[1] == '-') { + $timezone = -$timezone; } - $prefix = substr($content, 0, 2) >= 50 ? '19' : '20'; - $content = $prefix . $content; - } elseif (strpos($content, '.') !== false) { - $format.= '.u'; + } else { + $mktime = 'mktime'; + $timezone = 0; } - if ($content[strlen($content) - 1] == 'Z') { - $content = substr($content, 0, -1) . '+0000'; - } - - if (strpos($content, '-') !== false || strpos($content, '+') !== false) { - $format.= 'O'; - } - - // error supression isn't necessary as of PHP 7.0: - // http://php.net/manual/en/migration70.other-changes.php - return @DateTime::createFromFormat($format, $content); + return @$mktime($hour, $minute, $second, $month, $day, $year) + $timezone; } /** diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/X509.php b/vendor/phpseclib/phpseclib/phpseclib/File/X509.php index 4ebafa1710..984d1e678b 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/File/X509.php +++ b/vendor/phpseclib/phpseclib/phpseclib/File/X509.php @@ -26,13 +26,14 @@ namespace phpseclib\File; +use ParagonIE\ConstantTime\Base64; +use ParagonIE\ConstantTime\Hex; use phpseclib\Crypt\Hash; use phpseclib\Crypt\Random; use phpseclib\Crypt\RSA; +use phpseclib\Exception\UnsupportedAlgorithmException; use phpseclib\File\ASN1\Element; use phpseclib\Math\BigInteger; -use DateTime; -use DateTimeZone; /** * Pure-PHP X.509 Parser @@ -246,7 +247,7 @@ class X509 /** * The signature subject * - * There's no guarantee \phpseclib\File\X509 is going to re-encode an X.509 cert in the same way it was originally + * There's no guarantee \phpseclib\File\X509 is going to reencode an X.509 cert in the same way it was originally * encoded so we take save the portion of the original cert that the signature would have made for. * * @var string @@ -305,22 +306,6 @@ class X509 */ var $challenge; - /** - * Recursion Limit - * - * @var int - * @access private - */ - static $recur_limit = 5; - - /** - * URL fetch flag - * - * @var bool - * @access private - */ - static $disable_url_fetch = false; - /** * Default Constructor. * @@ -1502,9 +1487,7 @@ class X509 $this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); - if ($this->_isSubArrayValid($x509, 'tbsCertificate/extensions')) { - $this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1); - } + $this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1); $this->_mapInDNs($x509, 'tbsCertificate/issuer/rdnSequence', $asn1); $this->_mapInDNs($x509, 'tbsCertificate/subject/rdnSequence', $asn1); @@ -1543,7 +1526,7 @@ class X509 switch ($algorithm) { case 'rsaEncryption': $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'] - = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']))); + = Base64::encode("\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']))); /* "[For RSA keys] the parameters field MUST have ASN.1 type NULL for this algorithm identifier." -- https://tools.ietf.org/html/rfc3279#section-2.3.1 @@ -1593,7 +1576,7 @@ class X509 return $cert; // case self::FORMAT_PEM: default: - return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(base64_encode($cert), 64) . '-----END CERTIFICATE-----'; + return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(Base64::encode($cert), 64) . '-----END CERTIFICATE-----'; } } @@ -1608,13 +1591,13 @@ class X509 */ function _mapInExtensions(&$root, $path, $asn1) { - $extensions = &$this->_subArrayUnchecked($root, $path); + $extensions = &$this->_subArray($root, $path); - if ($extensions) { + if (is_array($extensions)) { for ($i = 0; $i < count($extensions); $i++) { $id = $extensions[$i]['extnId']; $value = &$extensions[$i]['extnValue']; - $value = base64_decode($value); + $value = Base64::decode($value); $decoded = $asn1->decodeBER($value); /* [extnValue] contains the DER encoding of an ASN.1 value corresponding to the extension type identified by extnID */ @@ -1641,7 +1624,7 @@ class X509 } } } else { - $value = base64_encode($value); + $value = Base64::encode($value); } } } @@ -1702,12 +1685,12 @@ class X509 $map = $this->_getMapping($id); if (is_bool($map)) { if (!$map) { - user_error($id . ' is not a currently supported extension'); + //user_error($id . ' is not a currently supported extension'); unset($extensions[$i]); } } else { $temp = $asn1->encodeDER($value, $map, array('iPAddress' => array($this, '_encodeIP'))); - $value = base64_encode($temp); + $value = Base64::encode($temp); } } } @@ -1742,11 +1725,11 @@ class X509 if ($mapped !== false) { $values[$j] = $mapped; } - if ($id == 'pkcs-9-at-extensionRequest' && $this->_isSubArrayValid($values, $j)) { + if ($id == 'pkcs-9-at-extensionRequest') { $this->_mapInExtensions($values, $j, $asn1); } } elseif ($map) { - $values[$j] = base64_encode($value); + $values[$j] = Base64::encode($value); } } } @@ -1775,7 +1758,7 @@ class X509 $id = $attributes[$i]['type']; $map = $this->_getMapping($id); if ($map === false) { - user_error($id . ' is not a currently supported attribute', E_USER_NOTICE); + //user_error($id . ' is not a currently supported attribute', E_USER_NOTICE); unset($attributes[$i]); } elseif (is_array($attributes[$i]['value'])) { $values = &$attributes[$i]['value']; @@ -1925,12 +1908,6 @@ class X509 // "SET Secure Electronic Transaction Specification" // http://www.maithean.com/docs/set_bk3.pdf case '2.23.42.7.0': // id-set-hashedRootKey - // "Certificate Transparency" - // https://tools.ietf.org/html/rfc6962 - case '1.3.6.1.4.1.11129.2.4.2': - // "Qualified Certificate statements" - // https://tools.ietf.org/html/rfc3739#section-3.2.6 - case '1.3.6.1.5.5.7.1.3': return true; // CSR attributes @@ -2051,32 +2028,30 @@ class X509 } if ($names = $this->getExtension('id-ce-subjectAltName')) { - foreach ($names as $name) { - foreach ($name as $key => $value) { - $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value); - switch ($key) { - case 'dNSName': - /* From RFC2818 "HTTP over TLS": + foreach ($names as $key => $value) { + $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value); + switch ($key) { + case 'dNSName': + /* From RFC2818 "HTTP over TLS": - If a subjectAltName extension of type dNSName is present, that MUST - be used as the identity. Otherwise, the (most specific) Common Name - field in the Subject field of the certificate MUST be used. Although - the use of the Common Name is existing practice, it is deprecated and - Certification Authorities are encouraged to use the dNSName instead. */ - if (preg_match('#^' . $value . '$#', $components['host'])) { - return true; - } - break; - case 'iPAddress': - /* From RFC2818 "HTTP over TLS": + If a subjectAltName extension of type dNSName is present, that MUST + be used as the identity. Otherwise, the (most specific) Common Name + field in the Subject field of the certificate MUST be used. Although + the use of the Common Name is existing practice, it is deprecated and + Certification Authorities are encouraged to use the dNSName instead. */ + if (preg_match('#^' . $value . '$#', $components['host'])) { + return true; + } + break; + case 'iPAddress': + /* From RFC2818 "HTTP over TLS": - In some cases, the URI is specified as an IP address rather than a - hostname. In this case, the iPAddress subjectAltName must be present - in the certificate and must exactly match the IP in the URI. */ - if (preg_match('#(?:\d{1-3}\.){4}#', $components['host'] . '.') && preg_match('#^' . $value . '$#', $components['host'])) { - return true; - } - } + In some cases, the URI is specified as an IP address rather than a + hostname. In this case, the iPAddress subjectAltName must be present + in the certificate and must exactly match the IP in the URI. */ + if (preg_match('#(?:\d{1-3}\.){4}#', $components['host'] . '.') && preg_match('#^' . $value . '$#', $components['host'])) { + return true; + } } } return false; @@ -2095,7 +2070,7 @@ class X509 * * If $date isn't defined it is assumed to be the current date. * - * @param \DateTime|string $date optional + * @param int $date optional * @access public */ function validateDate($date = null) @@ -2105,7 +2080,7 @@ class X509 } if (!isset($date)) { - $date = new DateTime(null, new DateTimeZone(@date_default_timezone_get())); + $date = time(); } $notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore']; @@ -2114,133 +2089,15 @@ class X509 $notAfter = $this->currentCert['tbsCertificate']['validity']['notAfter']; $notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime']; - if (is_string($date)) { - $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get())); - } - - $notBefore = new DateTime($notBefore, new DateTimeZone(@date_default_timezone_get())); - $notAfter = new DateTime($notAfter, new DateTimeZone(@date_default_timezone_get())); - switch (true) { - case $date < $notBefore: - case $date > $notAfter: + case $date < @strtotime($notBefore): + case $date > @strtotime($notAfter): return false; } return true; } - /** - * Fetches a URL - * - * @param string $url - * @access private - * @return bool|string - */ - static function _fetchURL($url) - { - if (self::$disable_url_fetch) { - return false; - } - - $parts = parse_url($url); - $data = ''; - switch ($parts['scheme']) { - case 'http': - $fsock = @fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80); - if (!$fsock) { - return false; - } - fputs($fsock, "GET $parts[path] HTTP/1.0\r\n"); - fputs($fsock, "Host: $parts[host]\r\n\r\n"); - $line = fgets($fsock, 1024); - if (strlen($line) < 3) { - return false; - } - preg_match('#HTTP/1.\d (\d{3})#', $line, $temp); - if ($temp[1] != '200') { - return false; - } - - // skip the rest of the headers in the http response - while (!feof($fsock) && fgets($fsock, 1024) != "\r\n") { - } - - while (!feof($fsock)) { - $data.= fread($fsock, 1024); - } - - break; - //case 'ftp': - //case 'ldap': - //default: - } - - return $data; - } - - /** - * Validates an intermediate cert as identified via authority info access extension - * - * See https://tools.ietf.org/html/rfc4325 for more info - * - * @param bool $caonly - * @param int $count - * @access private - * @return bool - */ - function _testForIntermediate($caonly, $count) - { - $opts = $this->getExtension('id-pe-authorityInfoAccess'); - if (!is_array($opts)) { - return false; - } - foreach ($opts as $opt) { - if ($opt['accessMethod'] == 'id-ad-caIssuers') { - // accessLocation is a GeneralName. GeneralName fields support stuff like email addresses, IP addresses, LDAP, - // etc, but we're only supporting URI's. URI's and LDAP are the only thing https://tools.ietf.org/html/rfc4325 - // discusses - if (isset($opt['accessLocation']['uniformResourceIdentifier'])) { - $url = $opt['accessLocation']['uniformResourceIdentifier']; - break; - } - } - } - - if (!isset($url)) { - return false; - } - - $cert = static::_fetchURL($url); - if (!is_string($cert)) { - return false; - } - - $parent = new static(); - $parent->CAs = $this->CAs; - /* - "Conforming applications that support HTTP or FTP for accessing - certificates MUST be able to accept .cer files and SHOULD be able - to accept .p7c files." -- https://tools.ietf.org/html/rfc4325 - - A .p7c file is 'a "certs-only" CMS message as specified in RFC 2797" - - These are currently unsupported - */ - if (!is_array($parent->loadX509($cert))) { - return false; - } - - if (!$parent->_validateSignatureCountable($caonly, ++$count)) { - return false; - } - - $this->CAs[] = $parent->currentCert; - //$this->loadCA($cert); - - return true; - } - /** * Validate a signature * @@ -2257,30 +2114,11 @@ class X509 * @return mixed */ function validateSignature($caonly = true) - { - return $this->_validateSignatureCountable($caonly, 0); - } - - /** - * Validate a signature - * - * Performs said validation whilst keeping track of how many times validation method is called - * - * @param bool $caonly - * @param int $count - * @access private - * @return mixed - */ - function _validateSignatureCountable($caonly, $count) { if (!is_array($this->currentCert) || !isset($this->signatureSubject)) { return null; } - if ($count == self::$recur_limit) { - return false; - } - /* TODO: "emailAddress attribute values are not case-sensitive (e.g., "subscriber@example.com" is the same as "SUBSCRIBER@EXAMPLE.COM")." -- http://tools.ietf.org/html/rfc5280#section-4.1.2.6 @@ -2297,8 +2135,7 @@ class X509 $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier'); switch (true) { case !is_array($authorityKey): - case !$subjectKeyID: - case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: $signingCert = $this->currentCert; // working cert } } @@ -2315,27 +2152,23 @@ class X509 $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); switch (true) { case !is_array($authorityKey): - case !$subjectKeyID: - case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: - if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) { - break 2; // serial mismatch - check other ca - } + case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: $signingCert = $ca; // working cert break 3; } } } if (count($this->CAs) == $i && $caonly) { - return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly); + return false; } } elseif (!isset($signingCert) || $caonly) { - return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly); + return false; } return $this->_validateSignature( $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], - substr(base64_decode($this->currentCert['signature']), 1), + substr(Base64::decode($this->currentCert['signature']), 1), $this->signatureSubject ); case isset($this->currentCert['certificationRequestInfo']): @@ -2343,7 +2176,7 @@ class X509 $this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'], $this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], - substr(base64_decode($this->currentCert['signature']), 1), + substr(Base64::decode($this->currentCert['signature']), 1), $this->signatureSubject ); case isset($this->currentCert['publicKeyAndChallenge']): @@ -2351,7 +2184,7 @@ class X509 $this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'], $this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], - substr(base64_decode($this->currentCert['signature']), 1), + substr(Base64::decode($this->currentCert['signature']), 1), $this->signatureSubject ); case isset($this->currentCert['tbsCertList']): @@ -2365,11 +2198,7 @@ class X509 $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); switch (true) { case !is_array($authorityKey): - case !$subjectKeyID: - case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: - if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) { - break 2; // serial mismatch - check other ca - } + case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: $signingCert = $ca; // working cert break 3; } @@ -2383,7 +2212,7 @@ class X509 $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $this->currentCert['signatureAlgorithm']['algorithm'], - substr(base64_decode($this->currentCert['signature']), 1), + substr(Base64::decode($this->currentCert['signature']), 1), $this->signatureSubject ); default: @@ -2394,7 +2223,8 @@ class X509 /** * Validates a signature * - * Returns true if the signature is verified, false if it is not correct or null on error + * Returns true if the signature is verified and false if it is not correct. + * If the algorithms are unsupposed an exception is thrown. * * @param string $publicKeyAlgorithm * @param string $publicKey @@ -2402,14 +2232,15 @@ class X509 * @param string $signature * @param string $signatureSubject * @access private - * @return int + * @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported + * @return bool */ function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject) { switch ($publicKeyAlgorithm) { case 'rsaEncryption': $rsa = new RSA(); - $rsa->loadKey($publicKey); + $rsa->load($publicKey); switch ($signatureAlgorithm) { case 'md2WithRSAEncryption': @@ -2420,57 +2251,21 @@ class X509 case 'sha384WithRSAEncryption': case 'sha512WithRSAEncryption': $rsa->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)); - $rsa->setSignatureMode(RSA::SIGNATURE_PKCS1); - if (!@$rsa->verify($signatureSubject, $signature)) { + if (!@$rsa->verify($signatureSubject, $signature, RSA::PADDING_PKCS1)) { return false; } break; default: - return null; + throw new UnsupportedAlgorithmException('Signature algorithm unsupported'); } break; default: - return null; + throw new UnsupportedAlgorithmException('Public key algorithm unsupported'); } return true; } - /** - * Sets the recursion limit - * - * When validating a signature it may be necessary to download intermediate certs from URI's. - * An intermediate cert that linked to itself would result in an infinite loop so to prevent - * that we set a recursion limit. A negative number means that there is no recursion limit. - * - * @param int $count - * @access public - */ - static function setRecurLimit($count) - { - self::$recur_limit = $count; - } - - /** - * Prevents URIs from being automatically retrieved - * - * @access public - */ - static function disableURLFetch() - { - self::$disable_url_fetch = true; - } - - /** - * Allows URIs to be automatically retrieved - * - * @access public - */ - static function enableURLFetch() - { - self::$disable_url_fetch = false; - } - /** * Reformat public keys * @@ -2490,7 +2285,7 @@ class X509 // subjectPublicKey is stored as a bit string in X.509 certs. the first byte of a bit string represents how many bits // in the last byte should be ignored. the following only supports non-zero stuff but as none of the X.509 certs Firefox // uses as a cert authority actually use a non-zero bit I think it's safe to assume that none do. - chunk_split(base64_encode(substr(base64_decode($key), 1)), 64) . + chunk_split(Base64::encode(substr(Base64::decode($key), 1)), 64) . '-----END RSA PUBLIC KEY-----'; default: return $key; @@ -2508,7 +2303,7 @@ class X509 */ function _decodeIP($ip) { - return inet_ntop(base64_decode($ip)); + return inet_ntop(Base64::decode($ip)); } /** @@ -2522,7 +2317,7 @@ class X509 */ function _encodeIP($ip) { - return base64_encode(inet_pton($ip)); + return Base64::encode(inet_pton($ip)); } /** @@ -2676,10 +2471,6 @@ class X509 } $dn = array_values($dn); - // fix for https://bugs.php.net/75433 affecting PHP 7.2 - if (!isset($dn[0])) { - $dn = array_splice($dn, 0, 0); - } } /** @@ -2850,7 +2641,7 @@ class X509 $hash = new Hash('sha1'); $hash = $hash->hash($dn); extract(unpack('Vhash', $hash)); - return strtolower(bin2hex(pack('N', $hash))); + return strtolower(Hex::encode(pack('N', $hash))); } // Default is to return a string. @@ -2923,14 +2714,12 @@ class X509 $value = array_pop($value); // Always strip data type. } } elseif (is_object($value) && $value instanceof Element) { - $callback = function ($x) { - return "\x" . bin2hex($x[0]); - }; + $callback = create_function('$x', 'return "\x" . bin2hex($x[0]);'); $value = strtoupper(preg_replace_callback('#[^\x20-\x7E]#', $callback, $value->element)); } $output.= $desc . '=' . $value; $result[$desc] = isset($result[$desc]) ? - array_merge((array) $result[$desc], array($value)) : + array_merge((array) $dn[$prop], array($value)) : $value; $start = false; } @@ -3146,7 +2935,7 @@ class X509 switch ($keyinfo['algorithm']['algorithm']) { case 'rsaEncryption': $publicKey = new RSA(); - $publicKey->loadKey($key); + $publicKey->load($key); $publicKey->setPublicKey(); break; default: @@ -3224,7 +3013,7 @@ class X509 switch ($algorithm) { case 'rsaEncryption': $this->publicKey = new RSA(); - $this->publicKey->loadKey($key); + $this->publicKey->load($key); $this->publicKey->setPublicKey(); break; default: @@ -3259,10 +3048,7 @@ class X509 switch ($algorithm) { case 'rsaEncryption': $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'] - = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']))); - $csr['certificationRequestInfo']['subjectPKInfo']['algorithm']['parameters'] = null; - $csr['signatureAlgorithm']['parameters'] = null; - $csr['certificationRequestInfo']['signature']['parameters'] = null; + = Base64::encode("\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']))); } } @@ -3285,7 +3071,7 @@ class X509 return $csr; // case self::FORMAT_PEM: default: - return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(base64_encode($csr), 64) . '-----END CERTIFICATE REQUEST-----'; + return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(Base64::encode($csr), 64) . '-----END CERTIFICATE REQUEST-----'; } } @@ -3314,9 +3100,9 @@ class X509 $asn1 = new ASN1(); - // OpenSSL produces SPKAC's that are preceded by the string SPKAC= + // OpenSSL produces SPKAC's that are preceeded by the string SPKAC= $temp = preg_replace('#(?:SPKAC=)|[ \r\n\\\]#', '', $spkac); - $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Base64::decode($temp) : false; if ($temp != false) { $spkac = $temp; } @@ -3351,7 +3137,7 @@ class X509 switch ($algorithm) { case 'rsaEncryption': $this->publicKey = new RSA(); - $this->publicKey->loadKey($key); + $this->publicKey->load($key); $this->publicKey->setPublicKey(); break; default: @@ -3387,7 +3173,7 @@ class X509 switch ($algorithm) { case 'rsaEncryption': $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'] - = base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']))); + = Base64::encode("\0" . Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $spkac['publicKeyAndChallenge']['spki']['subjectPublicKey']))); } } @@ -3401,9 +3187,9 @@ class X509 return $spkac; // case self::FORMAT_PEM: default: - // OpenSSL's implementation of SPKAC requires the SPKAC be preceded by SPKAC= and since there are pretty much + // OpenSSL's implementation of SPKAC requires the SPKAC be preceeded by SPKAC= and since there are pretty much // no other SPKAC decoders phpseclib will use that same format - return 'SPKAC=' . base64_encode($spkac); + return 'SPKAC=' . Base64::encode($spkac); } } @@ -3455,18 +3241,11 @@ class X509 $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); $this->_mapInDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1); - if ($this->_isSubArrayValid($crl, 'tbsCertList/crlExtensions')) { - $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1); - } - if ($this->_isSubArrayValid($crl, 'tbsCertList/revokedCertificates')) { - $rclist_ref = &$this->_subArrayUnchecked($crl, 'tbsCertList/revokedCertificates'); - if ($rclist_ref) { - $rclist = $crl['tbsCertList']['revokedCertificates']; - foreach ($rclist as $i => $extension) { - if ($this->_isSubArrayValid($rclist, "$i/crlEntryExtensions", $asn1)) { - $this->_mapInExtensions($rclist_ref, "$i/crlEntryExtensions", $asn1); - } - } + $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1); + $rclist = &$this->_subArray($crl, 'tbsCertList/revokedCertificates'); + if (is_array($rclist)) { + foreach ($rclist as $i => $extension) { + $this->_mapInExtensions($rclist, "$i/crlEntryExtensions", $asn1); } } @@ -3530,7 +3309,7 @@ class X509 return $crl; // case self::FORMAT_PEM: default: - return "-----BEGIN X509 CRL-----\r\n" . chunk_split(base64_encode($crl), 64) . '-----END X509 CRL-----'; + return "-----BEGIN X509 CRL-----\r\n" . chunk_split(Base64::encode($crl), 64) . '-----END X509 CRL-----'; } } @@ -3548,11 +3327,7 @@ class X509 */ function _timeField($date) { - if ($date instanceof Element) { - return $date; - } - $dateObj = new DateTime($date, new DateTimeZone('GMT')); - $year = $dateObj->format('Y'); // the same way ASN1.php parses this + $year = @gmdate("Y", @strtotime($date)); // the same way ASN1.php parses this if ($year < 2050) { return array('utcTime' => $date); } else { @@ -3573,7 +3348,7 @@ class X509 * @access public * @return mixed */ - function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption') + function sign($issuer, $subject, $signatureAlgorithm = 'sha256WithRSAEncryption') { if (!is_object($issuer->privateKey) || empty($issuer->dn)) { return false; @@ -3617,12 +3392,8 @@ class X509 return false; } - $startDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get())); - $startDate = !empty($this->startDate) ? $this->startDate : $startDate->format('D, d M Y H:i:s O'); - - $endDate = new DateTime('+1 year', new DateTimeZone(@date_default_timezone_get())); - $endDate = !empty($this->endDate) ? $this->endDate : $endDate->format('D, d M Y H:i:s O'); - + $startDate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O'); + $endDate = !empty($this->endDate) ? $this->endDate : @date('D, d M Y H:i:s O', strtotime('+1 year')); /* "The serial number MUST be a positive integer" "Conforming CAs MUST NOT use serialNumber values longer than 20 octets." -- https://tools.ietf.org/html/rfc5280#section-4.1.2.2 @@ -3638,7 +3409,7 @@ class X509 'tbsCertificate' => array( 'version' => 'v3', - 'serialNumber' => $serialNumber, // $this->setSerialNumber() + 'serialNumber' => $serialNumber, // $this->setserialNumber() 'signature' => array('algorithm' => $signatureAlgorithm), 'issuer' => false, // this is going to be overwritten later 'validity' => array( @@ -3684,8 +3455,8 @@ class X509 $altName = array(); - if (isset($subject->domains) && count($subject->domains)) { - $altName = array_map(array('\phpseclib\File\X509', '_dnsName'), $subject->domains); + if (isset($subject->domains) && count($subject->domains) > 1) { + $altName = array_map(array('X509', '_dnsName'), $subject->domains); } if (isset($subject->ipAddresses) && count($subject->ipAddresses)) { @@ -3730,7 +3501,7 @@ class X509 ); if (!isset($subject->currentKeyIdentifier)) { - $this->setExtension('id-ce-subjectKeyIdentifier', base64_encode($this->computeKeyIdentifier($this->currentCert)), false, false); + $this->setExtension('id-ce-subjectKeyIdentifier', Base64::encode($this->computeKeyIdentifier($this->currentCert)), false, false); } } @@ -3763,7 +3534,7 @@ class X509 $origPublicKey = $this->publicKey; $class = get_class($this->privateKey); $this->publicKey = new $class(); - $this->publicKey->loadKey($this->privateKey->getPublicKey()); + $this->publicKey->load($this->privateKey->getPublicKey()); $this->publicKey->setPublicKey(); if (!($publicKey = $this->_formatSubjectPublicKey())) { return false; @@ -3821,7 +3592,7 @@ class X509 $origPublicKey = $this->publicKey; $class = get_class($this->privateKey); $this->publicKey = new $class(); - $this->publicKey->loadKey($this->privateKey->getPublicKey()); + $this->publicKey->load($this->privateKey->getPublicKey()); $this->publicKey->setPublicKey(); $publicKey = $this->_formatSubjectPublicKey(); if (!$publicKey) { @@ -3890,9 +3661,7 @@ class X509 $currentCert = isset($this->currentCert) ? $this->currentCert : null; $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null; - - $thisUpdate = new DateTime('now', new DateTimeZone(@date_default_timezone_get())); - $thisUpdate = !empty($this->startDate) ? $this->startDate : $thisUpdate->format('D, d M Y H:i:s O'); + $thisUpdate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O'); if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) { $this->currentCert = $crl->currentCert; @@ -4011,6 +3780,7 @@ class X509 * @param \phpseclib\File\X509 $subject * @param string $signatureAlgorithm * @access public + * @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported * @return mixed */ function _sign($key, $signatureAlgorithm) @@ -4025,14 +3795,15 @@ class X509 case 'sha384WithRSAEncryption': case 'sha512WithRSAEncryption': $key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)); - $key->setSignatureMode(RSA::SIGNATURE_PKCS1); - $this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject)); + $this->currentCert['signature'] = Base64::encode("\0" . $key->sign($this->signatureSubject, RSA::PADDING_PKCS1)); return $this->currentCert; + default: + throw new UnsupportedAlgorithmException('Signature algorithm unsupported'); } } - return false; + throw new UnsupportedAlgorithmException('Unsupported public key algorithm'); } /** @@ -4043,11 +3814,7 @@ class X509 */ function setStartDate($date) { - if (!is_object($date) || !is_a($date, 'DateTime')) { - $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get())); - } - - $this->startDate = $date->format('D, d M Y H:i:s O'); + $this->startDate = @date('D, d M Y H:i:s O', @strtotime($date)); } /** @@ -4071,11 +3838,7 @@ class X509 $temp = chr(ASN1::TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp; $this->endDate = new Element($temp); } else { - if (!is_object($date) || !is_a($date, 'DateTime')) { - $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get())); - } - - $this->endDate = $date->format('D, d M Y H:i:s O'); + $this->endDate = @date('D, d M Y H:i:s O', @strtotime($date)); } } @@ -4101,74 +3864,6 @@ class X509 $this->caFlag = true; } - /** - * Check for validity of subarray - * - * This is intended for use in conjunction with _subArrayUnchecked(), - * implementing the checks included in _subArray() but without copying - * a potentially large array by passing its reference by-value to is_array(). - * - * @param array $root - * @param string $path - * @return boolean - * @access private - */ - function _isSubArrayValid($root, $path) - { - if (!is_array($root)) { - return false; - } - - foreach (explode('/', $path) as $i) { - if (!is_array($root)) { - return false; - } - - if (!isset($root[$i])) { - return true; - } - - $root = $root[$i]; - } - - return true; - } - - /** - * Get a reference to a subarray - * - * This variant of _subArray() does no is_array() checking, - * so $root should be checked with _isSubArrayValid() first. - * - * This is here for performance reasons: - * Passing a reference (i.e. $root) by-value (i.e. to is_array()) - * creates a copy. If $root is an especially large array, this is expensive. - * - * @param array $root - * @param string $path absolute path with / as component separator - * @param bool $create optional - * @access private - * @return array|false - */ - function &_subArrayUnchecked(&$root, $path, $create = false) - { - $false = false; - - foreach (explode('/', $path) as $i) { - if (!isset($root[$i])) { - if (!$create) { - return $false; - } - - $root[$i] = array(); - } - - $root = &$root[$i]; - } - - return $root; - } - /** * Get a reference to a subarray * @@ -4285,10 +3980,6 @@ class X509 } $extensions = array_values($extensions); - // fix for https://bugs.php.net/75433 affecting PHP 7.2 - if (!isset($extensions[0])) { - $extensions = array_splice($extensions, 0, 0); - } return $result; } @@ -4618,7 +4309,7 @@ class X509 if (empty($value)) { unset($this->currentKeyIdentifier); } else { - $this->currentKeyIdentifier = base64_encode($value); + $this->currentKeyIdentifier = Base64::encode($value); } } @@ -4666,10 +4357,10 @@ class X509 if (empty($raw)) { return false; } - $raw = base64_decode($raw); + $raw = Base64::decode($raw); // If the key is private, compute identifier from its corresponding public key. $key = new RSA(); - if (!$key->loadKey($raw)) { + if (!$key->load($raw)) { return false; // Not an unencrypted RSA key. } if ($key->getPrivateKey() !== false) { // If private. @@ -4689,7 +4380,7 @@ class X509 } return false; default: // Should be a key object (i.e.: \phpseclib\Crypt\RSA). - $key = $key->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1); + $key = $key->getPublicKey('PKCS1'); break; } @@ -4719,10 +4410,10 @@ class X509 if ($this->publicKey instanceof RSA) { // the following two return statements do the same thing. i dunno.. i just prefer the later for some reason. // the former is a good example of how to do fuzzing on the public key - //return new Element(base64_decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey->getPublicKey()))); + //return new Element(Base64::decode(preg_replace('#-.+-|[\r\n]#', '', $this->publicKey->getPublicKey()))); return array( 'algorithm' => array('algorithm' => 'rsaEncryption'), - 'subjectPublicKey' => $this->publicKey->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1) + 'subjectPublicKey' => $this->publicKey->getPublicKey('PKCS1') ); } @@ -4809,9 +4500,8 @@ class X509 } $i = count($rclist); - $revocationDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get())); $rclist[] = array('userCertificate' => $serial, - 'revocationDate' => $this->_timeField($revocationDate->format('D, d M Y H:i:s O'))); + 'revocationDate' => $this->_timeField(@date('D, d M Y H:i:s O'))); return $i; } @@ -5023,7 +4713,7 @@ class X509 $temp = preg_replace('#-+[^-]+-+#', '', $temp); // remove new lines $temp = str_replace(array("\r", "\n", ' '), '', $temp); - $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; + $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? Base64::decode($temp) : false; return $temp != false ? $temp : $str; } diff --git a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php index 57e14d2f90..66fb48e426 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php @@ -45,10 +45,13 @@ * @author Jim Wigginton * @copyright 2006 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @link http://pear.php.net/package/Math_BigInteger */ namespace phpseclib\Math; +use ParagonIE\ConstantTime\Base64; +use ParagonIE\ConstantTime\Hex; use phpseclib\Crypt\Random; /** @@ -264,43 +267,7 @@ class BigInteger } if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { - // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work - $versions = array(); - - // avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems) - if (strpos(ini_get('disable_functions'), 'phpinfo') === false) { - ob_start(); - @phpinfo(); - $content = ob_get_contents(); - ob_end_clean(); - - preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); - - if (!empty($matches[1])) { - for ($i = 0; $i < count($matches[1]); $i++) { - $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); - - // Remove letter part in OpenSSL version - if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) { - $versions[$matches[1][$i]] = $fullVersion; - } else { - $versions[$matches[1][$i]] = $m[0]; - } - } - } - } - - // it doesn't appear that OpenSSL versions were reported upon until PHP 5.3+ - switch (true) { - case !isset($versions['Header']): - case !isset($versions['Library']): - case $versions['Header'] == $versions['Library']: - case version_compare($versions['Header'], '1.0.0') >= 0 && version_compare($versions['Library'], '1.0.0') >= 0: - define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); - break; - default: - define('MATH_BIGINTEGER_OPENSSL_DISABLE', true); - } + define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); } if (!defined('PHP_INT_SIZE')) { @@ -363,12 +330,8 @@ class BigInteger case 256: switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: - $this->value = function_exists('gmp_import') ? - gmp_import($x) : - gmp_init('0x' . bin2hex($x)); - if ($this->is_negative) { - $this->value = gmp_neg($this->value); - } + $sign = $this->is_negative ? '-' : ''; + $this->value = gmp_init($sign . '0x' . Hex::encode($x)); break; case self::MODE_BCMATH: // round $len to the nearest 4 (thanks, DavidMJ!) @@ -413,7 +376,7 @@ class BigInteger $is_negative = false; if ($base < 0 && hexdec($x[0]) >= 8) { $this->is_negative = $is_negative = true; - $x = bin2hex(~pack('H*', $x)); + $x = Hex::encode(~Hex::decode($x)); } switch (MATH_BIGINTEGER_MODE) { @@ -424,13 +387,13 @@ class BigInteger break; case self::MODE_BCMATH: $x = (strlen($x) & 1) ? '0' . $x : $x; - $temp = new static(pack('H*', $x), 256); + $temp = new static(Hex::decode($x), 256); $this->value = $this->is_negative ? '-' . $temp->value : $temp->value; $this->is_negative = false; break; default: $x = (strlen($x) & 1) ? '0' . $x : $x; - $temp = new static(pack('H*', $x), 256); + $temp = new static(Hex::decode($x), 256); $this->value = $temp->value; } @@ -445,9 +408,6 @@ class BigInteger // (?<=^|-)0*: find any 0's that are preceded by the start of the string or by a - (ie. octals) // [^-0-9].*: find any non-numeric characters and then any characters that follow that $x = preg_replace('#(?precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } - $temp = $comparison < 0 ? $this->add(new static(1)) : $this->copy(); + $temp = $comparison < 0 ? $this->add(new static(1)) : $this; $bytes = $temp->toBytes(); - if (!strlen($bytes)) { // eg. if the number we're trying to convert is -1 + if (empty($bytes)) { // eg. if the number we're trying to convert is -1 $bytes = chr(0); } @@ -558,13 +518,9 @@ class BigInteger return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } - if (function_exists('gmp_export')) { - $temp = gmp_export($this->value); - } else { - $temp = gmp_strval(gmp_abs($this->value), 16); - $temp = (strlen($temp) & 1) ? '0' . $temp : $temp; - $temp = pack('H*', $temp); - } + $temp = gmp_strval(gmp_abs($this->value), 16); + $temp = (strlen($temp) & 1) ? '0' . $temp : $temp; + $temp = Hex::decode($temp); return $this->precision > 0 ? substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) : @@ -595,13 +551,11 @@ class BigInteger if (!count($this->value)) { return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : ''; } - $result = $this->_int2bytes($this->value[count($this->value) - 1]); + $result = self::_int2bytes($this->value[count($this->value) - 1]); - $temp = $this->copy(); - - for ($i = count($temp->value) - 2; $i >= 0; --$i) { - $temp->_base256_lshift($result, self::$base); - $result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT); + for ($i = count($this->value) - 2; $i >= 0; --$i) { + self::_base256_lshift($result, self::$base); + $result = $result | str_pad(self::_int2bytes($this->value[$i]), strlen($result), chr(0), STR_PAD_LEFT); } return $this->precision > 0 ? @@ -631,7 +585,7 @@ class BigInteger */ function toHex($twos_compliment = false) { - return bin2hex($this->toBytes($twos_compliment)); + return Hex::encode($this->toBytes($twos_compliment)); } /** @@ -706,7 +660,7 @@ class BigInteger return '0'; } - $temp = $this->copy(); + $temp = clone $this; $temp->is_negative = false; $divisor = new static(); @@ -728,28 +682,6 @@ class BigInteger return $result; } - /** - * Copy an object - * - * PHP5 passes objects by reference while PHP4 passes by value. As such, we need a function to guarantee - * that all objects are passed by value, when appropriate. More information can be found here: - * - * {@link http://php.net/language.oop5.basic#51624} - * - * @access public - * @see self::__clone() - * @return \phpseclib\Math\BigInteger - */ - function copy() - { - $temp = new static(); - $temp->value = $this->value; - $temp->is_negative = $this->is_negative; - $temp->precision = $this->precision; - $temp->bitmask = $this->bitmask; - return $temp; - } - /** * __toString() magic method * @@ -764,23 +696,6 @@ class BigInteger return $this->toString(); } - /** - * __clone() magic method - * - * Although you can call BigInteger::__toString() directly in PHP5, you cannot call BigInteger::__clone() directly - * in PHP5. You can in PHP4 since it's not a magic method, but in PHP5, you have to call it by using the PHP5 - * only syntax of $y = clone $x. As such, if you're trying to write an application that works on both PHP4 and - * PHP5, call BigInteger::copy(), instead. - * - * @access public - * @see self::copy() - * @return \phpseclib\Math\BigInteger - */ - function __clone() - { - return $this->copy(); - } - /** * __sleep() magic method * @@ -871,7 +786,7 @@ class BigInteger * @access public * @internal Performs base-2**52 addition */ - function add($y) + function add(BigInteger $y) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: @@ -886,7 +801,7 @@ class BigInteger return $this->_normalize($temp); } - $temp = $this->_add($this->value, $this->is_negative, $y->value, $y->is_negative); + $temp = self::_add($this->value, $this->is_negative, $y->value, $y->is_negative); $result = new static(); $result->value = $temp[self::VALUE]; @@ -905,7 +820,7 @@ class BigInteger * @return array * @access private */ - function _add($x_value, $x_negative, $y_value, $y_negative) + static function _add($x_value, $x_negative, $y_value, $y_negative) { $x_size = count($x_value); $y_size = count($y_value); @@ -931,8 +846,8 @@ class BigInteger ); } - $temp = $this->_subtract($x_value, false, $y_value, false); - $temp[self::SIGN] = $this->_compare($x_value, false, $y_value, false) > 0 ? + $temp = self::_subtract($x_value, false, $y_value, false); + $temp[self::SIGN] = self::_compare($x_value, false, $y_value, false) > 0 ? $x_negative : $y_negative; return $temp; @@ -975,7 +890,7 @@ class BigInteger } return array( - self::VALUE => $this->_trim($value), + self::VALUE => self::_trim($value), self::SIGN => $x_negative ); } @@ -1000,7 +915,7 @@ class BigInteger * @access public * @internal Performs base-2**52 subtraction */ - function subtract($y) + function subtract(BigInteger $y) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: @@ -1015,7 +930,7 @@ class BigInteger return $this->_normalize($temp); } - $temp = $this->_subtract($this->value, $this->is_negative, $y->value, $y->is_negative); + $temp = self::_subtract($this->value, $this->is_negative, $y->value, $y->is_negative); $result = new static(); $result->value = $temp[self::VALUE]; @@ -1034,7 +949,7 @@ class BigInteger * @return array * @access private */ - function _subtract($x_value, $x_negative, $y_value, $y_negative) + static function _subtract($x_value, $x_negative, $y_value, $y_negative) { $x_size = count($x_value); $y_size = count($y_value); @@ -1053,13 +968,13 @@ class BigInteger // add, if appropriate (ie. -$x - +$y or +$x - -$y) if ($x_negative != $y_negative) { - $temp = $this->_add($x_value, false, $y_value, false); + $temp = self::_add($x_value, false, $y_value, false); $temp[self::SIGN] = $x_negative; return $temp; } - $diff = $this->_compare($x_value, $x_negative, $y_value, $y_negative); + $diff = self::_compare($x_value, $x_negative, $y_value, $y_negative); if (!$diff) { return array( @@ -1109,7 +1024,7 @@ class BigInteger } return array( - self::VALUE => $this->_trim($x_value), + self::VALUE => self::_trim($x_value), self::SIGN => $x_negative ); } @@ -1133,7 +1048,7 @@ class BigInteger * @return \phpseclib\Math\BigInteger * @access public */ - function multiply($x) + function multiply(BigInteger $x) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: @@ -1148,7 +1063,7 @@ class BigInteger return $this->_normalize($temp); } - $temp = $this->_multiply($this->value, $this->is_negative, $x->value, $x->is_negative); + $temp = self::_multiply($this->value, $this->is_negative, $x->value, $x->is_negative); $product = new static(); $product->value = $temp[self::VALUE]; @@ -1167,7 +1082,7 @@ class BigInteger * @return array * @access private */ - function _multiply($x_value, $x_negative, $y_value, $y_negative) + static function _multiply($x_value, $x_negative, $y_value, $y_negative) { //if ( $x_value == $y_value ) { // return array( @@ -1188,8 +1103,8 @@ class BigInteger return array( self::VALUE => min($x_length, $y_length) < 2 * self::KARATSUBA_CUTOFF ? - $this->_trim($this->_regularMultiply($x_value, $y_value)) : - $this->_trim($this->_karatsuba($x_value, $y_value)), + self::_trim(self::_regularMultiply($x_value, $y_value)) : + self::_trim(self::_karatsuba($x_value, $y_value)), self::SIGN => $x_negative != $y_negative ); } @@ -1204,7 +1119,7 @@ class BigInteger * @return array * @access private */ - function _regularMultiply($x_value, $y_value) + static function _regularMultiply($x_value, $y_value) { $x_length = count($x_value); $y_length = count($y_value); @@ -1222,7 +1137,7 @@ class BigInteger $y_length = count($y_value); } - $product_value = $this->_array_repeat(0, $x_length + $y_length); + $product_value = self::_array_repeat(0, $x_length + $y_length); // the following for loop could be removed if the for loop following it // (the one with nested for loops) initially set $i to 0, but @@ -1268,12 +1183,12 @@ class BigInteger * @return array * @access private */ - function _karatsuba($x_value, $y_value) + static function _karatsuba($x_value, $y_value) { $m = min(count($x_value) >> 1, count($y_value) >> 1); if ($m < self::KARATSUBA_CUTOFF) { - return $this->_regularMultiply($x_value, $y_value); + return self::_regularMultiply($x_value, $y_value); } $x1 = array_slice($x_value, $m); @@ -1281,20 +1196,20 @@ class BigInteger $y1 = array_slice($y_value, $m); $y0 = array_slice($y_value, 0, $m); - $z2 = $this->_karatsuba($x1, $y1); - $z0 = $this->_karatsuba($x0, $y0); + $z2 = self::_karatsuba($x1, $y1); + $z0 = self::_karatsuba($x0, $y0); - $z1 = $this->_add($x1, false, $x0, false); - $temp = $this->_add($y1, false, $y0, false); - $z1 = $this->_karatsuba($z1[self::VALUE], $temp[self::VALUE]); - $temp = $this->_add($z2, false, $z0, false); - $z1 = $this->_subtract($z1, false, $temp[self::VALUE], false); + $z1 = self::_add($x1, false, $x0, false); + $temp = self::_add($y1, false, $y0, false); + $z1 = self::_karatsuba($z1[self::VALUE], $temp[self::VALUE]); + $temp = self::_add($z2, false, $z0, false); + $z1 = self::_subtract($z1, false, $temp[self::VALUE], false); $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); - $xy = $this->_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]); - $xy = $this->_add($xy[self::VALUE], $xy[self::SIGN], $z0, false); + $xy = self::_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]); + $xy = self::_add($xy[self::VALUE], $xy[self::SIGN], $z0, false); return $xy[self::VALUE]; } @@ -1306,11 +1221,11 @@ class BigInteger * @return array * @access private */ - function _square($x = false) + static function _square($x = false) { return count($x) < 2 * self::KARATSUBA_CUTOFF ? - $this->_trim($this->_baseSquare($x)) : - $this->_trim($this->_karatsubaSquare($x)); + self::_trim(self::_baseSquare($x)) : + self::_trim(self::_karatsubaSquare($x)); } /** @@ -1324,12 +1239,12 @@ class BigInteger * @return array * @access private */ - function _baseSquare($value) + static function _baseSquare($value) { if (empty($value)) { return array(); } - $square_value = $this->_array_repeat(0, 2 * count($value)); + $square_value = self::_array_repeat(0, 2 * count($value)); for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) { $i2 = $i << 1; @@ -1363,30 +1278,30 @@ class BigInteger * @return array * @access private */ - function _karatsubaSquare($value) + static function _karatsubaSquare($value) { $m = count($value) >> 1; if ($m < self::KARATSUBA_CUTOFF) { - return $this->_baseSquare($value); + return self::_baseSquare($value); } $x1 = array_slice($value, $m); $x0 = array_slice($value, 0, $m); - $z2 = $this->_karatsubaSquare($x1); - $z0 = $this->_karatsubaSquare($x0); + $z2 = self::_karatsubaSquare($x1); + $z0 = self::_karatsubaSquare($x0); - $z1 = $this->_add($x1, false, $x0, false); - $z1 = $this->_karatsubaSquare($z1[self::VALUE]); - $temp = $this->_add($z2, false, $z0, false); - $z1 = $this->_subtract($z1, false, $temp[self::VALUE], false); + $z1 = self::_add($x1, false, $x0, false); + $z1 = self::_karatsubaSquare($z1[self::VALUE]); + $temp = self::_add($z2, false, $z0, false); + $z1 = self::_subtract($z1, false, $temp[self::VALUE], false); $z2 = array_merge(array_fill(0, 2 * $m, 0), $z2); $z1[self::VALUE] = array_merge(array_fill(0, $m, 0), $z1[self::VALUE]); - $xx = $this->_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]); - $xx = $this->_add($xx[self::VALUE], $xx[self::SIGN], $z0, false); + $xx = self::_add($z2, false, $z1[self::VALUE], $z1[self::SIGN]); + $xx = self::_add($xx[self::VALUE], $xx[self::SIGN], $z0, false); return $xx[self::VALUE]; } @@ -1418,7 +1333,7 @@ class BigInteger * @access public * @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}. */ - function divide($y) + function divide(BigInteger $y) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: @@ -1461,8 +1376,8 @@ class BigInteger $zero = new static(); } - $x = $this->copy(); - $y = $y->copy(); + $x = clone $this; + $y = clone $y; $x_sign = $x->is_negative; $y_sign = $y->is_negative; @@ -1561,9 +1476,7 @@ class BigInteger $temp_value = array($quotient_value[$q_index]); $temp = $temp->multiply($y); $temp_value = &$temp->value; - if ($temp_value !== []) { - $temp_value = array_merge($adjust, $temp_value); - } + $temp_value = array_merge($adjust, $temp_value); $x = $x->subtract($temp); @@ -1601,14 +1514,14 @@ class BigInteger * @return array * @access private */ - function _divide_digit($dividend, $divisor) + static function _divide_digit($dividend, $divisor) { $carry = 0; $result = array(); for ($i = count($dividend) - 1; $i >= 0; --$i) { $temp = self::$baseFull * $carry + $dividend[$i]; - $result[$i] = $this->_safe_divide($temp, $divisor); + $result[$i] = self::_safe_divide($temp, $divisor); $carry = (int) ($temp - $divisor * $result[$i]); } @@ -1655,7 +1568,7 @@ class BigInteger * the other, a power of two - and recombine them, later. This is the method that this modPow function uses. * {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates. */ - function modPow($e, $n) + function modPow(BigInteger $e, BigInteger $n) { $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs(); @@ -1689,31 +1602,31 @@ class BigInteger ); $components = array( - 'modulus' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['modulus'])), $components['modulus']), - 'publicExponent' => pack('Ca*a*', 2, $this->_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent']) + 'modulus' => pack('Ca*a*', 2, self::_encodeASN1Length(strlen($components['modulus'])), $components['modulus']), + 'publicExponent' => pack('Ca*a*', 2, self::_encodeASN1Length(strlen($components['publicExponent'])), $components['publicExponent']) ); $RSAPublicKey = pack( 'Ca*a*a*', 48, - $this->_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])), + self::_encodeASN1Length(strlen($components['modulus']) + strlen($components['publicExponent'])), $components['modulus'], $components['publicExponent'] ); - $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA + $rsaOID = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"; // hex version of MA0GCSqGSIb3DQEBAQUA $RSAPublicKey = chr(0) . $RSAPublicKey; - $RSAPublicKey = chr(3) . $this->_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey; + $RSAPublicKey = chr(3) . self::_encodeASN1Length(strlen($RSAPublicKey)) . $RSAPublicKey; $encapsulated = pack( 'Ca*a*', 48, - $this->_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)), + self::_encodeASN1Length(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey ); $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . - chunk_split(base64_encode($encapsulated)) . + chunk_split(Base64::encode($encapsulated)) . '-----END PUBLIC KEY-----'; $plaintext = str_pad($this->toBytes(), strlen($n->toBytes(true)) - 1, "\0", STR_PAD_LEFT); @@ -1743,7 +1656,7 @@ class BigInteger if ($e->value == array(2)) { $temp = new static(); - $temp->value = $this->_square($this->value); + $temp->value = self::_square($this->value); list(, $temp) = $temp->divide($n); return $this->_normalize($temp); } @@ -1772,7 +1685,7 @@ class BigInteger } // at this point, 2^$j * $n/(2^$j) == $n - $mod1 = $n->copy(); + $mod1 = clone $n; $mod1->_rshift($j); $mod2 = new static(); $mod2->value = array(1); @@ -1806,7 +1719,7 @@ class BigInteger * @return \phpseclib\Math\BigInteger * @access public */ - function powMod($e, $n) + function powMod(BigInteger $e, BigInteger $n) { return $this->modPow($e, $n); } @@ -1848,23 +1761,23 @@ class BigInteger // precompute $this^0 through $this^$window_size $powers = array(); - $powers[1] = $this->_prepareReduce($this->value, $n_value, $mode); - $powers[2] = $this->_squareReduce($powers[1], $n_value, $mode); + $powers[1] = self::_prepareReduce($this->value, $n_value, $mode); + $powers[2] = self::_squareReduce($powers[1], $n_value, $mode); // we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end // in a 1. ie. it's supposed to be odd. $temp = 1 << ($window_size - 1); for ($i = 1; $i < $temp; ++$i) { $i2 = $i << 1; - $powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode); + $powers[$i2 + 1] = self::_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode); } $result = array(1); - $result = $this->_prepareReduce($result, $n_value, $mode); + $result = self::_prepareReduce($result, $n_value, $mode); for ($i = 0; $i < $e_length;) { if (!$e_bits[$i]) { - $result = $this->_squareReduce($result, $n_value, $mode); + $result = self::_squareReduce($result, $n_value, $mode); ++$i; } else { for ($j = $window_size - 1; $j > 0; --$j) { @@ -1875,17 +1788,17 @@ class BigInteger // eg. the length of substr($e_bits, $i, $j + 1) for ($k = 0; $k <= $j; ++$k) { - $result = $this->_squareReduce($result, $n_value, $mode); + $result = self::_squareReduce($result, $n_value, $mode); } - $result = $this->_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode); + $result = self::_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode); $i += $j + 1; } } $temp = new static(); - $temp->value = $this->_reduce($result, $n_value, $mode); + $temp->value = self::_reduce($result, $n_value, $mode); return $temp; } @@ -1902,13 +1815,13 @@ class BigInteger * @param int $mode * @return array */ - function _reduce($x, $n, $mode) + static function _reduce($x, $n, $mode) { switch ($mode) { case self::MONTGOMERY: - return $this->_montgomery($x, $n); + return self::_montgomery($x, $n); case self::BARRETT: - return $this->_barrett($x, $n); + return self::_barrett($x, $n); case self::POWEROF2: $lhs = new static(); $lhs->value = $x; @@ -1939,12 +1852,12 @@ class BigInteger * @param int $mode * @return array */ - function _prepareReduce($x, $n, $mode) + static function _prepareReduce($x, $n, $mode) { if ($mode == self::MONTGOMERY) { - return $this->_prepMontgomery($x, $n); + return self::_prepMontgomery($x, $n); } - return $this->_reduce($x, $n, $mode); + return self::_reduce($x, $n, $mode); } /** @@ -1958,13 +1871,13 @@ class BigInteger * @param int $mode * @return array */ - function _multiplyReduce($x, $y, $n, $mode) + static function _multiplyReduce($x, $y, $n, $mode) { if ($mode == self::MONTGOMERY) { - return $this->_montgomeryMultiply($x, $y, $n); + return self::_montgomeryMultiply($x, $y, $n); } - $temp = $this->_multiply($x, false, $y, false); - return $this->_reduce($temp[self::VALUE], $n, $mode); + $temp = self::_multiply($x, false, $y, false); + return self::_reduce($temp[self::VALUE], $n, $mode); } /** @@ -1977,12 +1890,12 @@ class BigInteger * @param int $mode * @return array */ - function _squareReduce($x, $n, $mode) + static function _squareReduce($x, $n, $mode) { if ($mode == self::MONTGOMERY) { - return $this->_montgomeryMultiply($x, $x, $n); + return self::_montgomeryMultiply($x, $x, $n); } - return $this->_reduce($this->_square($x), $n, $mode); + return self::_reduce(self::_square($x), $n, $mode); } /** @@ -2027,7 +1940,7 @@ class BigInteger * @param array $m * @return array */ - function _barrett($n, $m) + static function _barrett($n, $m) { static $cache = array( self::VARIABLE => array(), @@ -2036,7 +1949,7 @@ class BigInteger $m_length = count($m); - // if ($this->_compare($n, $this->_square($m)) >= 0) { + // if (self::_compare($n, self::_square($m)) >= 0) { if (count($n) > 2 * $m_length) { $lhs = new static(); $rhs = new static(); @@ -2048,7 +1961,7 @@ class BigInteger // if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced if ($m_length < 5) { - return $this->_regularBarrett($n, $m); + return self::_regularBarrett($n, $m); } // n = 2 * m.length @@ -2059,7 +1972,7 @@ class BigInteger $lhs = new static(); $lhs_value = &$lhs->value; - $lhs_value = $this->_array_repeat(0, $m_length + ($m_length >> 1)); + $lhs_value = self::_array_repeat(0, $m_length + ($m_length >> 1)); $lhs_value[] = 1; $rhs = new static(); $rhs->value = $m; @@ -2079,34 +1992,34 @@ class BigInteger $cutoff = $m_length + ($m_length >> 1); $lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1) $msd = array_slice($n, $cutoff); // m.length >> 1 - $lsd = $this->_trim($lsd); - $temp = $this->_multiply($msd, false, $m1, false); - $n = $this->_add($lsd, false, $temp[self::VALUE], false); // m.length + (m.length >> 1) + 1 + $lsd = self::_trim($lsd); + $temp = self::_multiply($msd, false, $m1, false); + $n = self::_add($lsd, false, $temp[self::VALUE], false); // m.length + (m.length >> 1) + 1 if ($m_length & 1) { - return $this->_regularBarrett($n[self::VALUE], $m); + return self::_regularBarrett($n[self::VALUE], $m); } // (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2 $temp = array_slice($n[self::VALUE], $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 = $this->_multiply($temp, false, $u, false); + $temp = self::_multiply($temp, false, $u, false); // 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 = array_slice($temp[self::VALUE], ($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 = $this->_multiply($temp, false, $m, false); + $temp = self::_multiply($temp, false, $m, false); // 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 = $this->_subtract($n[self::VALUE], false, $temp[self::VALUE], false); + $result = self::_subtract($n[self::VALUE], false, $temp[self::VALUE], false); - while ($this->_compare($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) { - $result = $this->_subtract($result[self::VALUE], $result[self::SIGN], $m, false); + while (self::_compare($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) { + $result = self::_subtract($result[self::VALUE], $result[self::SIGN], $m, false); } return $result[self::VALUE]; @@ -2124,7 +2037,7 @@ class BigInteger * @param array $n * @return array */ - function _regularBarrett($x, $n) + static function _regularBarrett($x, $n) { static $cache = array( self::VARIABLE => array(), @@ -2147,7 +2060,7 @@ class BigInteger $cache[self::VARIABLE][] = $n; $lhs = new static(); $lhs_value = &$lhs->value; - $lhs_value = $this->_array_repeat(0, 2 * $n_length); + $lhs_value = self::_array_repeat(0, 2 * $n_length); $lhs_value[] = 1; $rhs = new static(); $rhs->value = $n; @@ -2158,27 +2071,27 @@ class BigInteger // 2 * m.length - (m.length - 1) = m.length + 1 $temp = array_slice($x, $n_length - 1); // (m.length + 1) + m.length = 2 * m.length + 1 - $temp = $this->_multiply($temp, false, $cache[self::DATA][$key], false); + $temp = self::_multiply($temp, false, $cache[self::DATA][$key], false); // (2 * m.length + 1) - (m.length - 1) = m.length + 2 $temp = array_slice($temp[self::VALUE], $n_length + 1); // m.length + 1 $result = array_slice($x, 0, $n_length + 1); // m.length + 1 - $temp = $this->_multiplyLower($temp, false, $n, false, $n_length + 1); - // $temp == array_slice($temp->_multiply($temp, false, $n, false)->value, 0, $n_length + 1) + $temp = self::_multiplyLower($temp, false, $n, false, $n_length + 1); + // $temp == array_slice(self::_multiply($temp, false, $n, false)->value, 0, $n_length + 1) - if ($this->_compare($result, false, $temp[self::VALUE], $temp[self::SIGN]) < 0) { - $corrector_value = $this->_array_repeat(0, $n_length + 1); + if (self::_compare($result, false, $temp[self::VALUE], $temp[self::SIGN]) < 0) { + $corrector_value = self::_array_repeat(0, $n_length + 1); $corrector_value[count($corrector_value)] = 1; - $result = $this->_add($result, false, $corrector_value, false); + $result = self::_add($result, false, $corrector_value, false); $result = $result[self::VALUE]; } // at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits - $result = $this->_subtract($result, false, $temp[self::VALUE], $temp[self::SIGN]); - while ($this->_compare($result[self::VALUE], $result[self::SIGN], $n, false) > 0) { - $result = $this->_subtract($result[self::VALUE], $result[self::SIGN], $n, false); + $result = self::_subtract($result, false, $temp[self::VALUE], $temp[self::SIGN]); + while (self::_compare($result[self::VALUE], $result[self::SIGN], $n, false) > 0) { + $result = self::_subtract($result[self::VALUE], $result[self::SIGN], $n, false); } return $result[self::VALUE]; @@ -2198,7 +2111,7 @@ class BigInteger * @return array * @access private */ - function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop) + static function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop) { $x_length = count($x_value); $y_length = count($y_value); @@ -2219,7 +2132,7 @@ class BigInteger $y_length = count($y_value); } - $product_value = $this->_array_repeat(0, $x_length + $y_length); + $product_value = self::_array_repeat(0, $x_length + $y_length); // the following for loop could be removed if the for loop following it // (the one with nested for loops) initially set $i to 0, but @@ -2257,7 +2170,7 @@ class BigInteger } return array( - self::VALUE => $this->_trim($product_value), + self::VALUE => self::_trim($product_value), self::SIGN => $x_negative != $y_negative ); } @@ -2277,7 +2190,7 @@ class BigInteger * @param array $n * @return array */ - function _montgomery($x, $n) + static function _montgomery($x, $n) { static $cache = array( self::VARIABLE => array(), @@ -2287,7 +2200,7 @@ class BigInteger if (($key = array_search($n, $cache[self::VARIABLE])) === false) { $key = count($cache[self::VARIABLE]); $cache[self::VARIABLE][] = $x; - $cache[self::DATA][] = $this->_modInverse67108864($n); + $cache[self::DATA][] = self::_modInverse67108864($n); } $k = count($n); @@ -2297,15 +2210,15 @@ class BigInteger for ($i = 0; $i < $k; ++$i) { $temp = $result[self::VALUE][$i] * $cache[self::DATA][$key]; $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); - $temp = $this->_regularMultiply(array($temp), $n); + $temp = self::_regularMultiply(array($temp), $n); $temp = array_merge($this->_array_repeat(0, $i), $temp); - $result = $this->_add($result[self::VALUE], false, $temp, false); + $result = self::_add($result[self::VALUE], false, $temp, false); } $result[self::VALUE] = array_slice($result[self::VALUE], $k); - if ($this->_compare($result, false, $n, false) >= 0) { - $result = $this->_subtract($result[self::VALUE], false, $n, false); + if (self::_compare($result, false, $n, false) >= 0) { + $result = self::_subtract($result[self::VALUE], false, $n, false); } return $result[self::VALUE]; @@ -2325,10 +2238,10 @@ class BigInteger * @param array $m * @return array */ - function _montgomeryMultiply($x, $y, $m) + static function _montgomeryMultiply($x, $y, $m) { - $temp = $this->_multiply($x, false, $y, false); - return $this->_montgomery($temp[self::VALUE], $m); + $temp = self::_multiply($x, false, $y, false); + return self::_montgomery($temp[self::VALUE], $m); // the following code, although not callable, can be run independently of the above code // although the above code performed better in my benchmarks the following could might @@ -2343,25 +2256,25 @@ class BigInteger if (($key = array_search($m, $cache[self::VARIABLE])) === false) { $key = count($cache[self::VARIABLE]); $cache[self::VARIABLE][] = $m; - $cache[self::DATA][] = $this->_modInverse67108864($m); + $cache[self::DATA][] = self::_modInverse67108864($m); } $n = max(count($x), count($y), count($m)); $x = array_pad($x, $n, 0); $y = array_pad($y, $n, 0); $m = array_pad($m, $n, 0); - $a = array(self::VALUE => $this->_array_repeat(0, $n + 1)); + $a = array(self::VALUE => self::_array_repeat(0, $n + 1)); for ($i = 0; $i < $n; ++$i) { $temp = $a[self::VALUE][0] + $x[$i] * $y[0]; $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); $temp = $temp * $cache[self::DATA][$key]; $temp = $temp - self::$baseFull * (self::$base === 26 ? intval($temp / 0x4000000) : ($temp >> 31)); - $temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false); - $a = $this->_add($a[self::VALUE], false, $temp[self::VALUE], false); + $temp = self::_add(self::_regularMultiply(array($x[$i]), $y), false, self::_regularMultiply(array($temp), $m), false); + $a = self::_add($a[self::VALUE], false, $temp[self::VALUE], false); $a[self::VALUE] = array_slice($a[self::VALUE], 1); } - if ($this->_compare($a[self::VALUE], false, $m, false) >= 0) { - $a = $this->_subtract($a[self::VALUE], false, $m, false); + if (self::_compare($a[self::VALUE], false, $m, false) >= 0) { + $a = self::_subtract($a[self::VALUE], false, $m, false); } return $a[self::VALUE]; } @@ -2376,10 +2289,10 @@ class BigInteger * @param array $n * @return array */ - function _prepMontgomery($x, $n) + static function _prepMontgomery($x, $n) { $lhs = new static(); - $lhs->value = array_merge($this->_array_repeat(0, count($n)), $x); + $lhs->value = array_merge(self::_array_repeat(0, count($n)), $x); $rhs = new static(); $rhs->value = $n; @@ -2451,7 +2364,7 @@ class BigInteger * @access public * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information. */ - function modInverse($n) + function modInverse(BigInteger $n) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: @@ -2492,7 +2405,7 @@ class BigInteger * * Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which - * combination is returned is dependent upon which mode is in use. See + * combination is returned is dependant upon which mode is in use. See * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information. * * Here's an example: @@ -2515,7 +2428,7 @@ class BigInteger * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes, * the more traditional algorithim requires "relatively costly multiple-precision divisions". */ - function extendedGCD($n) + function extendedGCD(BigInteger $n) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: @@ -2562,8 +2475,8 @@ class BigInteger ); } - $y = $n->copy(); - $x = $this->copy(); + $y = clone $n; + $x = clone $this; $g = new static(); $g->value = array(1); @@ -2573,8 +2486,8 @@ class BigInteger $g->_lshift(1); } - $u = $x->copy(); - $v = $y->copy(); + $u = clone $x; + $v = clone $y; $a = new static(); $b = new static(); @@ -2644,7 +2557,7 @@ class BigInteger * @return \phpseclib\Math\BigInteger * @access public */ - function gcd($n) + function gcd(BigInteger $n) { extract($this->extendedGCD($n)); return $gcd; @@ -2692,23 +2605,16 @@ class BigInteger * @see self::equals() * @internal Could return $this->subtract($x), but that's not as fast as what we do do. */ - function compare($y) + function compare(BigInteger $y) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: - $r = gmp_cmp($this->value, $y->value); - if ($r < -1) { - $r = -1; - } - if ($r > 1) { - $r = 1; - } - return $r; + return gmp_cmp($this->value, $y->value); case self::MODE_BCMATH: return bccomp($this->value, $y->value, 0); } - return $this->_compare($this->value, $this->is_negative, $y->value, $y->is_negative); + return self::_compare($this->value, $this->is_negative, $y->value, $y->is_negative); } /** @@ -2722,7 +2628,7 @@ class BigInteger * @see self::compare() * @access private */ - function _compare($x_value, $x_negative, $y_value, $y_negative) + static function _compare($x_value, $x_negative, $y_value, $y_negative) { if ($x_negative != $y_negative) { return (!$x_negative && $y_negative) ? 1 : -1; @@ -2757,7 +2663,7 @@ class BigInteger * @access public * @see self::compare() */ - function equals($x) + function equals(BigInteger $x) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: @@ -2778,6 +2684,12 @@ class BigInteger */ function setPrecision($bits) { + if ($bits < 1) { + $this->precision = -1; + $this->bitmask = false; + + return; + } $this->precision = $bits; if (MATH_BIGINTEGER_MODE != self::MODE_BCMATH) { $this->bitmask = new static(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256); @@ -2789,6 +2701,18 @@ class BigInteger $this->value = $temp->value; } + /** + * Get Precision + * + * @return int + * @see self::setPrecision() + * @access public + */ + function getPrecision() + { + return $this->precision; + } + /** * Logical And * @@ -2797,7 +2721,7 @@ class BigInteger * @internal Implemented per a request by Lluis Pamies i Juarez * @return \phpseclib\Math\BigInteger */ - function bitwise_and($x) + function bitwise_and(BigInteger $x) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: @@ -2817,7 +2741,7 @@ class BigInteger return $this->_normalize(new static($left & $right, 256)); } - $result = $this->copy(); + $result = clone $this; $length = min(count($x->value), count($this->value)); @@ -2838,7 +2762,7 @@ class BigInteger * @internal Implemented per a request by Lluis Pamies i Juarez * @return \phpseclib\Math\BigInteger */ - function bitwise_or($x) + function bitwise_or(BigInteger $x) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: @@ -2859,7 +2783,7 @@ class BigInteger } $length = max(count($this->value), count($x->value)); - $result = $this->copy(); + $result = clone $this; $result->value = array_pad($result->value, $length, 0); $x->value = array_pad($x->value, $length, 0); @@ -2878,12 +2802,13 @@ class BigInteger * @internal Implemented per a request by Lluis Pamies i Juarez * @return \phpseclib\Math\BigInteger */ - function bitwise_xor($x) + function bitwise_xor(BigInteger $x) { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: $temp = new static(); - $temp->value = gmp_xor(gmp_abs($this->value), gmp_abs($x->value)); + $temp->value = gmp_xor($this->value, $x->value); + return $this->_normalize($temp); case self::MODE_BCMATH: $left = $this->toBytes(); @@ -2898,8 +2823,7 @@ class BigInteger } $length = max(count($this->value), count($x->value)); - $result = $this->copy(); - $result->is_negative = false; + $result = clone $this; $result->value = array_pad($result->value, $length, 0); $x->value = array_pad($x->value, $length, 0); @@ -2923,7 +2847,7 @@ class BigInteger // (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0) $temp = $this->toBytes(); if ($temp == '') { - return $this->_normalize(new static()); + return ''; } $pre_msb = decbin(ord($temp[0])); $temp = ~$temp; @@ -2942,7 +2866,8 @@ class BigInteger // generate as many leading 1's as we need to. $leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3); - $this->_base256_lshift($leading_ones, $current_bits); + + self::_base256_lshift($leading_ones, $current_bits); $temp = str_pad($temp, strlen($leading_ones), chr(0), STR_PAD_LEFT); @@ -3060,7 +2985,7 @@ class BigInteger $shift%= $precision; if (!$shift) { - return $this->copy(); + return clone $this; } $left = $this->bitwise_leftShift($shift); @@ -3093,7 +3018,7 @@ class BigInteger * @return \phpseclib\Math\BigInteger * @access private */ - function _random_number_helper($size) + static function _random_number_helper($size) { if (class_exists('\phpseclib\Crypt\Random')) { $random = Random::string($size); @@ -3120,30 +3045,16 @@ class BigInteger * Returns a random number between $min and $max where $min and $max * can be defined using one of the two methods: * - * $min->random($max) - * $max->random($min) + * BigInteger::random($min, $max) + * BigInteger::random($max, $min) * * @param \phpseclib\Math\BigInteger $arg1 * @param \phpseclib\Math\BigInteger $arg2 * @return \phpseclib\Math\BigInteger * @access public - * @internal The API for creating random numbers used to be $a->random($min, $max), where $a was a BigInteger object. - * That method is still supported for BC purposes. */ - function random($arg1, $arg2 = false) + static function random(BigInteger $min, BigInteger $max) { - if ($arg1 === false) { - return false; - } - - if ($arg2 === false) { - $max = $arg1; - $min = $this; - } else { - $min = $arg1; - $max = $arg2; - } - $compare = $max->compare($min); if (!$compare) { @@ -3179,7 +3090,7 @@ class BigInteger http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string */ $random_max = new static(chr(1) . str_repeat("\0", $size), 256); - $random = $this->_random_number_helper($size); + $random = static::_random_number_helper($size); list($max_multiple) = $random_max->divide($max); $max_multiple = $max_multiple->multiply($max); @@ -3188,14 +3099,14 @@ class BigInteger $random = $random->subtract($max_multiple); $random_max = $random_max->subtract($max_multiple); $random = $random->bitwise_leftShift(8); - $random = $random->add($this->_random_number_helper(1)); + $random = $random->add(self::_random_number_helper(1)); $random_max = $random_max->bitwise_leftShift(8); list($max_multiple) = $random_max->divide($max); $max_multiple = $max_multiple->multiply($max); } list(, $random) = $random->divide($max); - return $this->_normalize($random->add($min)); + return $random->add($min); } /** @@ -3204,27 +3115,15 @@ class BigInteger * If there's not a prime within the given range, false will be returned. * If more than $timeout seconds have elapsed, give up and return false. * - * @param \phpseclib\Math\BigInteger $arg1 - * @param \phpseclib\Math\BigInteger $arg2 + * @param \phpseclib\Math\BigInteger $min + * @param \phpseclib\Math\BigInteger $max * @param int $timeout * @return Math_BigInteger|false * @access public * @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}. */ - function randomPrime($arg1, $arg2 = false, $timeout = false) + static function randomPrime(BigInteger $min, BigInteger $max, $timeout = false) { - if ($arg1 === false) { - return false; - } - - if ($arg2 === false) { - $max = $arg1; - $min = $this; - } else { - $min = $arg1; - $max = $arg2; - } - $compare = $max->compare($min); if (!$compare) { @@ -3244,7 +3143,7 @@ class BigInteger $start = time(); - $x = $this->random($min, $max); + $x = self::random($min, $max); // gmp_nextprime() requires PHP 5 >= 5.2.0 per . if (MATH_BIGINTEGER_MODE == self::MODE_GMP && extension_loaded('gmp')) { @@ -3259,7 +3158,7 @@ class BigInteger $x = $x->subtract($one); } - return $x->randomPrime($min, $x); + return self::randomPrime($min, $x); } if ($x->equals($two)) { @@ -3272,11 +3171,11 @@ class BigInteger if ($min->equals($max)) { return false; } - $x = $min->copy(); + $x = clone $min; $x->_make_odd(); } - $initial_x = $x->copy(); + $initial_x = clone $x; while (true) { if ($timeout !== false && time() - $start > $timeout) { @@ -3290,7 +3189,7 @@ class BigInteger $x = $x->add($two); if ($x->compare($max) > 0) { - $x = $min->copy(); + $x = clone $min; if ($x->equals($two)) { return $x; } @@ -3428,18 +3327,18 @@ class BigInteger } else { $value = $this->value; foreach ($primes as $prime) { - list(, $r) = $this->_divide_digit($value, $prime); + list(, $r) = self::_divide_digit($value, $prime); if (!$r) { return count($value) == 1 && $value[0] == $prime; } } } - $n = $this->copy(); + $n = clone $this; $n_1 = $n->subtract($one); $n_2 = $n->subtract($two); - $r = $n_1->copy(); + $r = clone $n_1; $r_value = $r->value; // ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s)); if (MATH_BIGINTEGER_MODE == self::MODE_BCMATH) { @@ -3458,12 +3357,12 @@ class BigInteger break; } } - $s = 26 * $i + $j; + $s = 26 * $i + $j - 1; $r->_rshift($s); } for ($i = 0; $i < $t; ++$i) { - $a = $this->random($two, $n_2); + $a = self::random($two, $n_2); $y = $a->modPow($r, $n); if (!$y->equals($one) && !$y->equals($n_1)) { @@ -3584,7 +3483,6 @@ class BigInteger $value = &$result->value; if (!count($value)) { - $result->is_negative = false; return $result; } @@ -3611,7 +3509,7 @@ class BigInteger * @return \phpseclib\Math\BigInteger * @access private */ - function _trim($value) + static function _trim($value) { for ($i = count($value) - 1; $i >= 0; --$i) { if ($value[$i]) { @@ -3631,7 +3529,7 @@ class BigInteger * @return array * @access private */ - function _array_repeat($input, $multiplier) + static function _array_repeat($input, $multiplier) { return ($multiplier) ? array_fill(0, $multiplier, $input) : array(); } @@ -3646,7 +3544,7 @@ class BigInteger * @return string * @access private */ - function _base256_lshift(&$x, $shift) + static function _base256_lshift(&$x, $shift) { if ($shift == 0) { return; @@ -3675,7 +3573,7 @@ class BigInteger * @return string * @access private */ - function _base256_rshift(&$x, $shift) + static function _base256_rshift(&$x, $shift) { if ($shift == 0) { $x = ltrim($x, chr(0)); @@ -3716,7 +3614,7 @@ class BigInteger * @return string * @access private */ - function _int2bytes($x) + static function _int2bytes($x) { return ltrim(pack('N', $x), chr(0)); } @@ -3728,7 +3626,7 @@ class BigInteger * @return int * @access private */ - function _bytes2int($x) + static function _bytes2int($x) { $temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT)); return $temp['int']; @@ -3744,7 +3642,7 @@ class BigInteger * @param int $length * @return string */ - function _encodeASN1Length($length) + static function _encodeASN1Length($length) { if ($length <= 0x7F) { return chr($length); @@ -3767,7 +3665,7 @@ class BigInteger * @param int $y * @return int */ - function _safe_divide($x, $y) + static function _safe_divide($x, $y) { if (self::$base === 26) { return (int) ($x / $y); diff --git a/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php b/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php index cf13496cd4..4c28d8b0ef 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php @@ -32,6 +32,8 @@ namespace phpseclib\Net; +use phpseclib\Exception\FileNotFoundException; + /** * Pure-PHP implementations of SCP. * @@ -99,7 +101,9 @@ class SCP * * Connects to an SSH server * - * @param \phpseclib\Net\SSH1|\phpseclib\Net\SSH2 $ssh + * @param string $host + * @param int $port + * @param int $timeout * @return \phpseclib\Net\SCP * @access public */ @@ -135,6 +139,7 @@ class SCP * @param string $data * @param int $mode * @param callable $callback + * @throws \phpseclib\Exception\FileNotFoundException if you're uploading via a file and the file doesn't exist * @return bool * @access public */ @@ -144,11 +149,6 @@ class SCP return false; } - if (empty($remote_file)) { - user_error('remote_file cannot be blank', E_USER_NOTICE); - return false; - } - if (!$this->ssh->exec('scp -t ' . escapeshellarg($remote_file), false)) { // -t = to return false; } @@ -168,8 +168,7 @@ class SCP $size = strlen($data); } else { if (!is_file($data)) { - user_error("$data is not a valid file", E_USER_NOTICE); - return false; + throw new FileNotFoundException("$data is not a valid file"); } $fp = @fopen($data, 'rb'); @@ -289,6 +288,7 @@ class SCP * Receives a packet from an SSH server * * @return string + * @throws \UnexpectedValueException on receipt of an unexpected packet * @access private */ function _receive() @@ -304,9 +304,6 @@ class SCP $response = $this->ssh->_get_binary_packet(); switch ($response[SSH1::RESPONSE_TYPE]) { case NET_SSH1_SMSG_STDOUT_DATA: - if (strlen($response[SSH1::RESPONSE_DATA]) < 4) { - return false; - } extract(unpack('Nlength', $response[SSH1::RESPONSE_DATA])); return $this->ssh->_string_shift($response[SSH1::RESPONSE_DATA], $length); case NET_SSH1_SMSG_STDERR_DATA: @@ -317,8 +314,7 @@ class SCP $this->ssh->bitmap = 0; return false; default: - user_error('Unknown packet received', E_USER_NOTICE); - return false; + throw new \UnexpectedValueException('Unknown packet received'); } } } diff --git a/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php b/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php index a248c25819..a4d8c9e42e 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php @@ -37,6 +37,9 @@ namespace phpseclib\Net; +use ParagonIE\ConstantTime\Hex; +use phpseclib\Exception\FileNotFoundException; + /** * Pure-PHP implementations of SFTP. * @@ -109,11 +112,11 @@ class SFTP extends SSH2 * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support * concurrent actions, so it's somewhat academic, here. * - * @var boolean + * @var int * @see self::_send_sftp_packet() * @access private */ - var $use_request_id = false; + var $request_id = false; /** * The Packet Type @@ -158,7 +161,7 @@ class SFTP extends SSH2 * Current working directory * * @var string - * @see self::realpath() + * @see self::_realpath() * @see self::chdir() * @access private */ @@ -187,7 +190,7 @@ class SFTP extends SSH2 * * @see self::getSFTPErrors() * @see self::getLastSFTPError() - * @var array + * @var string * @access private */ var $sftp_errors = array(); @@ -236,29 +239,6 @@ class SFTP extends SSH2 */ var $sortOptions = array(); - /** - * Canonicalization Flag - * - * Determines whether or not paths should be canonicalized before being - * passed on to the remote server. - * - * @see self::enablePathCanonicalization() - * @see self::disablePathCanonicalization() - * @see self::realpath() - * @var bool - * @access private - */ - var $canonicalize_paths = true; - - /** - * Request Buffers - * - * @see self::_get_sftp_packet() - * @var array - * @access private - */ - var $requestBuffer = array(); - /** * Default Constructor. * @@ -358,11 +338,11 @@ class SFTP extends SSH2 // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000. // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored. - (-1 << 31) & 0xFFFFFFFF => 'NET_SFTP_ATTR_EXTENDED' + -1 << 31 => 'NET_SFTP_ATTR_EXTENDED' ); // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name - // the array for that $this->open5_flags and similarly alter the constant names. + // the array for that $this->open5_flags and similarily alter the constant names. $this->open_flags = array( 0x00000001 => 'NET_SFTP_OPEN_READ', 0x00000002 => 'NET_SFTP_OPEN_WRITE', @@ -395,7 +375,7 @@ class SFTP extends SSH2 ); if (!defined('NET_SFTP_QUEUE_SIZE')) { - define('NET_SFTP_QUEUE_SIZE', 32); + define('NET_SFTP_QUEUE_SIZE', 50); } } @@ -404,6 +384,7 @@ class SFTP extends SSH2 * * @param string $username * @param string $password + * @throws \UnexpectedValueException on receipt of unexpected packets * @return bool * @access public */ @@ -432,7 +413,7 @@ class SFTP extends SSH2 $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN; - $response = $this->_get_channel_packet(self::CHANNEL, true); + $response = $this->_get_channel_packet(self::CHANNEL); if ($response === false) { return false; } @@ -453,7 +434,7 @@ class SFTP extends SSH2 $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; - $response = $this->_get_channel_packet(self::CHANNEL, true); + $response = $this->_get_channel_packet(self::CHANNEL); if ($response === false) { // from PuTTY's psftp.exe $command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" . @@ -477,7 +458,7 @@ class SFTP extends SSH2 $this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; - $response = $this->_get_channel_packet(self::CHANNEL, true); + $response = $this->_get_channel_packet(self::CHANNEL); if ($response === false) { return false; } @@ -491,24 +472,14 @@ class SFTP extends SSH2 $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_VERSION) { - user_error('Expected SSH_FXP_VERSION'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_VERSION'); } - if (strlen($response) < 4) { - return false; - } extract(unpack('Nversion', $this->_string_shift($response, 4))); $this->version = $version; while (!empty($response)) { - if (strlen($response) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($response, 4))); $key = $this->_string_shift($response, $length); - if (strlen($response) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($response, 4))); $value = $this->_string_shift($response, $length); $this->extensions[$key] = $value; @@ -528,7 +499,7 @@ class SFTP extends SSH2 } */ - $this->use_request_id = true; + $this->request_id = 1; /* A Note on SFTPv4/5/6 support: @@ -598,26 +569,6 @@ class SFTP extends SSH2 $this->stat_cache = array(); } - /** - * Enable path canonicalization - * - * @access public - */ - function enablePathCanonicalization() - { - $this->canonicalize_paths = true; - } - - /** - * Enable path canonicalization - * - * @access public - */ - function disablePathCanonicalization() - { - $this->canonicalize_paths = false; - } - /** * Returns the current directory name * @@ -639,15 +590,12 @@ class SFTP extends SSH2 function _logError($response, $status = -1) { if ($status == -1) { - if (strlen($response) < 4) { - return; - } extract(unpack('Nstatus', $this->_string_shift($response, 4))); } $error = $this->status_codes[$status]; - if ($this->version > 2 || strlen($response) < 4) { + if ($this->version > 2) { extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->sftp_errors[] = $error . ': ' . $this->_string_shift($response, $length); } else { @@ -655,41 +603,20 @@ class SFTP extends SSH2 } } - /** - * Returns canonicalized absolute pathname - * - * realpath() expands all symbolic links and resolves references to '/./', '/../' and extra '/' characters in the input - * path and returns the canonicalized absolute pathname. - * - * @param string $path - * @return mixed - * @access public - */ - function realpath($path) - { - return $this->_realpath($path); - } - /** * Canonicalize the Server-Side Path Name * * SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it. Returns * the absolute (canonicalized) path. * - * If canonicalize_paths has been disabled using disablePathCanonicalization(), $path is returned as-is. - * * @see self::chdir() - * @see self::disablePathCanonicalization() * @param string $path + * @throws \UnexpectedValueException on receipt of unexpected packets * @return mixed * @access private */ function _realpath($path) { - if (!$this->canonicalize_paths) { - return $path; - } - if ($this->pwd === false) { // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9 if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($path), $path))) { @@ -703,17 +630,13 @@ class SFTP extends SSH2 // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks // at is the first part and that part is defined the same in SFTP versions 3 through 6. $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway - if (strlen($response) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($response, 4))); return $this->_string_shift($response, $length); case NET_SFTP_STATUS: $this->_logError($response); return false; default: - user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); } } @@ -744,6 +667,7 @@ class SFTP extends SSH2 * Changes the current directory * * @param string $dir + * @throws \UnexpectedValueException on receipt of unexpected packets * @return bool * @access public */ @@ -788,8 +712,7 @@ class SFTP extends SSH2 $this->_logError($response); return false; default: - user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); } if (!$this->_close_handle($handle)) { @@ -842,7 +765,6 @@ class SFTP extends SSH2 } if (is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $value)))) { $temp = $this->_nlist_helper($dir . '/' . $value, true, $relativeDir . $value . '/'); - $temp = is_array($temp) ? $temp : array(); $result = array_merge($result, $temp); } else { $result[] = $relativeDir . $value; @@ -874,17 +796,7 @@ class SFTP extends SSH2 unset($files[$key]); continue; } - $is_directory = false; - if ($key != '.' && $key != '..') { - if ($this->use_stat_cache) { - $is_directory = is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key))); - } else { - $stat = $this->lstat($dir . '/' . $key); - $is_directory = $stat && $stat['type'] === NET_SFTP_TYPE_DIRECTORY; - } - } - - if ($is_directory) { + if ($key != '.' && $key != '..' && is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key)))) { $depth++; $files[$key] = $this->rawlist($dir . '/' . $key, true); $depth--; @@ -902,6 +814,7 @@ class SFTP extends SSH2 * @param string $dir * @param bool $raw * @return mixed + * @throws \UnexpectedValueException on receipt of unexpected packets * @access private */ function _list($dir, $raw = true) @@ -933,8 +846,7 @@ class SFTP extends SSH2 $this->_logError($response); return false; default: - user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); } $this->_update_stat_cache($dir, array()); @@ -951,19 +863,10 @@ class SFTP extends SSH2 $response = $this->_get_sftp_packet(); switch ($this->packet_type) { case NET_SFTP_NAME: - if (strlen($response) < 4) { - return false; - } extract(unpack('Ncount', $this->_string_shift($response, 4))); for ($i = 0; $i < $count; $i++) { - if (strlen($response) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($response, 4))); $shortname = $this->_string_shift($response, $length); - if (strlen($response) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($response, 4))); $longname = $this->_string_shift($response, $length); $attributes = $this->_parseAttributes($response); @@ -990,9 +893,6 @@ class SFTP extends SSH2 } break; case NET_SFTP_STATUS: - if (strlen($response) < 4) { - return false; - } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_EOF) { $this->_logError($response, $status); @@ -1000,8 +900,7 @@ class SFTP extends SSH2 } break 2; default: - user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); } } @@ -1167,7 +1066,7 @@ class SFTP extends SSH2 $temp[$dir] = array(); } if ($i === $max) { - if (is_object($temp[$dir]) && is_object($value)) { + if (is_object($temp[$dir])) { if (!isset($value->stat) && isset($temp[$dir]->stat)) { $value->stat = $temp[$dir]->stat; } @@ -1355,11 +1254,12 @@ class SFTP extends SSH2 /** * Returns general information about a file or symbolic link * - * Determines information without calling \phpseclib\Net\SFTP::realpath(). + * Determines information without calling \phpseclib\Net\SFTP::_realpath(). * The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT. * * @param string $filename * @param int $type + * @throws \UnexpectedValueException on receipt of unexpected packets * @return mixed * @access private */ @@ -1380,8 +1280,7 @@ class SFTP extends SSH2 return false; } - user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); } /** @@ -1407,6 +1306,7 @@ class SFTP extends SSH2 * @param string $filename * @param int $time * @param int $atime + * @throws \UnexpectedValueException on receipt of unexpected packets * @return bool * @access public */ @@ -1443,8 +1343,7 @@ class SFTP extends SSH2 $this->_logError($response); break; default: - user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); } return $this->_setstat($filename, $attr, false); @@ -1497,6 +1396,7 @@ class SFTP extends SSH2 * @param int $mode * @param string $filename * @param bool $recursive + * @throws \UnexpectedValueException on receipt of unexpected packets * @return mixed * @access public */ @@ -1516,7 +1416,7 @@ class SFTP extends SSH2 return true; } - $filename = $this->realpath($filename); + $filename = $this->_realPath($filename); // rather than return what the permissions *should* be, we'll return what they actually are. this will also // tell us if the file actually exists. // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following: @@ -1535,8 +1435,7 @@ class SFTP extends SSH2 return false; } - user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); } /** @@ -1545,6 +1444,7 @@ class SFTP extends SSH2 * @param string $filename * @param string $attr * @param bool $recursive + * @throws \UnexpectedValueException on receipt of unexpected packets * @return bool * @access private */ @@ -1583,13 +1483,9 @@ class SFTP extends SSH2 */ $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); } - if (strlen($response) < 4) { - return false; - } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); @@ -1675,6 +1571,7 @@ class SFTP extends SSH2 * Return the target of a symbolic link * * @param string $link + * @throws \UnexpectedValueException on receipt of unexpected packets * @return mixed * @access public */ @@ -1698,22 +1595,15 @@ class SFTP extends SSH2 $this->_logError($response); return false; default: - user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); } - if (strlen($response) < 4) { - return false; - } extract(unpack('Ncount', $this->_string_shift($response, 4))); // the file isn't a symlink if (!$count) { return false; } - if (strlen($response) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($response, 4))); return $this->_string_shift($response, $length); } @@ -1725,6 +1615,7 @@ class SFTP extends SSH2 * * @param string $target * @param string $link + * @throws \UnexpectedValueException on receipt of unexpected packets * @return bool * @access public */ @@ -1734,7 +1625,7 @@ class SFTP extends SSH2 return false; } - //$target = $this->_realpath($target); + $target = $this->_realpath($target); $link = $this->_realpath($link); $packet = pack('Na*Na*', strlen($target), $target, strlen($link), $link); @@ -1744,13 +1635,9 @@ class SFTP extends SSH2 $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); } - if (strlen($response) < 4) { - return false; - } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); @@ -1800,6 +1687,7 @@ class SFTP extends SSH2 * * @param string $dir * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets * @access private */ function _mkdir_helper($dir, $attr) @@ -1810,13 +1698,9 @@ class SFTP extends SSH2 $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); } - if (strlen($response) < 4) { - return false; - } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); @@ -1830,6 +1714,7 @@ class SFTP extends SSH2 * Removes a directory. * * @param string $dir + * @throws \UnexpectedValueException on receipt of unexpected packets * @return bool * @access public */ @@ -1850,13 +1735,9 @@ class SFTP extends SSH2 $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); } - if (strlen($response) < 4) { - return false; - } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED? @@ -1914,6 +1795,9 @@ class SFTP extends SSH2 * @param int $start * @param int $local_start * @param callable|null $progressCallback + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \BadFunctionCallException if you're uploading via a callback and the callback function is invalid + * @throws \phpseclib\Exception\FileNotFoundException if you're uploading via a file and the file doesn't exist * @return bool * @access public * @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - \phpseclib\Net\SFTP::setMode(). @@ -1961,8 +1845,7 @@ class SFTP extends SSH2 $this->_logError($response); return false; default: - user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); } // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3 @@ -1970,26 +1853,18 @@ class SFTP extends SSH2 switch (true) { case $mode & self::SOURCE_CALLBACK: if (!is_callable($data)) { - user_error("\$data should be is_callable() if you specify SOURCE_CALLBACK flag"); + throw new \BadFunctionCallException("\$data should be is_callable() if you specify SOURCE_CALLBACK flag"); } $dataCallback = $data; // do nothing break; case is_resource($data): $mode = $mode & ~self::SOURCE_LOCAL_FILE; - $info = stream_get_meta_data($data); - if ($info['wrapper_type'] == 'PHP' && $info['stream_type'] == 'Input') { - $fp = fopen('php://memory', 'w+'); - stream_copy_to_stream($data, $fp); - rewind($fp); - } else { - $fp = $data; - } + $fp = $data; break; case $mode & self::SOURCE_LOCAL_FILE: if (!is_file($data)) { - user_error("$data is not a valid file"); - return false; + throw new FileNotFoundException("$data is not a valid file"); } $fp = @fopen($data, 'rb'); if (!$fp) { @@ -1999,7 +1874,7 @@ class SFTP extends SSH2 if (isset($fp)) { $stat = fstat($fp); - $size = !empty($stat) ? $stat['size'] : 0; + $size = $stat['size']; if ($local_start >= 0) { fseek($fp, $local_start); @@ -2018,7 +1893,7 @@ class SFTP extends SSH2 // make the SFTP packet be exactly 4096 bytes by including the bytes in the NET_SFTP_WRITE packets "header" $sftp_packet_size-= strlen($handle) + 25; $i = 0; - while ($dataCallback || ($size === 0 || $sent < $size)) { + while ($dataCallback || $sent < $size) { if ($dataCallback) { $temp = call_user_func($dataCallback, $sftp_packet_size); if (is_null($temp)) { @@ -2026,11 +1901,7 @@ class SFTP extends SSH2 } } else { $temp = isset($fp) ? fread($fp, $sftp_packet_size) : substr($data, $sent, $sftp_packet_size); - if ($temp === false || $temp === '') { - break; - } } - $subtemp = $offset + $sent; $packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp); if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet)) { @@ -2078,6 +1949,7 @@ class SFTP extends SSH2 * * @param int $i * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets * @access private */ function _read_put_responses($i) @@ -2085,13 +1957,9 @@ class SFTP extends SSH2 while ($i--) { $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); } - if (strlen($response) < 4) { - return false; - } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); @@ -2107,6 +1975,7 @@ class SFTP extends SSH2 * * @param string $handle * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets * @access private */ function _close_handle($handle) @@ -2119,13 +1988,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) { - user_error('Expected SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); } - if (strlen($response) < 4) { - return false; - } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); @@ -2148,11 +2013,11 @@ class SFTP extends SSH2 * @param string $local_file * @param int $offset * @param int $length - * @param callable|null $progressCallback + * @throws \UnexpectedValueException on receipt of unexpected packets * @return mixed * @access public */ - function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null) + function get($remote_file, $local_file = false, $offset = 0, $length = -1) { if (!($this->bitmap & SSH2::MASK_LOGIN)) { return false; @@ -2177,8 +2042,7 @@ class SFTP extends SSH2 $this->_logError($response); return false; default: - user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); } if (is_resource($local_file)) { @@ -2210,7 +2074,7 @@ class SFTP extends SSH2 $packet_size = $length > 0 ? min($this->max_sftp_packet, $length - $read) : $this->max_sftp_packet; $packet = pack('Na*N3', strlen($handle), $handle, $tempoffset / 4294967296, $tempoffset, $packet_size); - if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet, $i)) { + if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) { if ($fclose_check) { fclose($fp); } @@ -2218,9 +2082,6 @@ class SFTP extends SSH2 } $packet = null; $read+= $packet_size; - if (is_callable($progressCallback)) { - call_user_func($progressCallback, $read); - } $i++; } @@ -2228,17 +2089,15 @@ class SFTP extends SSH2 break; } - $packets_sent = $i - 1; - $clear_responses = false; while ($i > 0) { $i--; if ($clear_responses) { - $this->_get_sftp_packet($packets_sent - $i); + $this->_get_sftp_packet(); continue; } else { - $response = $this->_get_sftp_packet($packets_sent - $i); + $response = $this->_get_sftp_packet(); } switch ($this->packet_type) { @@ -2261,7 +2120,7 @@ class SFTP extends SSH2 if ($fclose_check) { fclose($fp); } - user_error('Expected SSH_FX_DATA or SSH_FXP_STATUS'); + throw new \UnexpectedValueException('Expected SSH_FXP_DATA or SSH_FXP_STATUS'); } $response = null; } @@ -2297,6 +2156,7 @@ class SFTP extends SSH2 * @param string $path * @param bool $recursive * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets * @access public */ function delete($path, $recursive = true) @@ -2305,15 +2165,6 @@ class SFTP extends SSH2 return false; } - if (is_object($path)) { - // It's an object. Cast it as string before we check anything else. - $path = (string) $path; - } - - if (!is_string($path) || $path == '') { - return false; - } - $path = $this->_realpath($path); if ($path === false) { return false; @@ -2326,14 +2177,10 @@ class SFTP extends SSH2 $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); } // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED - if (strlen($response) < 4) { - return false; - } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); @@ -2732,6 +2579,7 @@ class SFTP extends SSH2 * @param string $oldname * @param string $newname * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets * @access public */ function rename($oldname, $newname) @@ -2754,14 +2602,10 @@ class SFTP extends SSH2 $response = $this->_get_sftp_packet(); if ($this->packet_type != NET_SFTP_STATUS) { - user_error('Expected SSH_FXP_STATUS'); - return false; + throw new \UnexpectedValueException('Expected SSH_FXP_STATUS'); } // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED - if (strlen($response) < 4) { - return false; - } extract(unpack('Nstatus', $this->_string_shift($response, 4))); if ($status != NET_SFTP_STATUS_OK) { $this->_logError($response, $status); @@ -2789,10 +2633,6 @@ class SFTP extends SSH2 function _parseAttributes(&$response) { $attr = array(); - if (strlen($response) < 4) { - user_error('Malformed file attributes'); - return array(); - } extract(unpack('Nflags', $this->_string_shift($response, 4))); // SFTPv4+ have a type field (a byte) that follows the above flag field foreach ($this->attributes as $key => $value) { @@ -2804,20 +2644,12 @@ class SFTP extends SSH2 // IEEE 754 binary64 "double precision" on such platforms and // as such can represent integers of at least 2^50 without loss // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB. - $attr['size'] = hexdec(bin2hex($this->_string_shift($response, 8))); + $attr['size'] = hexdec(Hex::encode($this->_string_shift($response, 8))); break; case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only) - if (strlen($response) < 8) { - user_error('Malformed file attributes'); - return $attr; - } $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8)); break; case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004 - if (strlen($response) < 4) { - user_error('Malformed file attributes'); - return $attr; - } $attr+= unpack('Npermissions', $this->_string_shift($response, 4)); // mode == permissions; permissions was the original array key and is retained for bc purposes. // mode was added because that's the more industry standard terminology @@ -2828,29 +2660,13 @@ class SFTP extends SSH2 } break; case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008 - if (strlen($response) < 8) { - user_error('Malformed file attributes'); - return $attr; - } $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8)); break; case NET_SFTP_ATTR_EXTENDED: // 0x80000000 - if (strlen($response) < 4) { - user_error('Malformed file attributes'); - return $attr; - } extract(unpack('Ncount', $this->_string_shift($response, 4))); for ($i = 0; $i < $count; $i++) { - if (strlen($response) < 4) { - user_error('Malformed file attributes'); - return $attr; - } extract(unpack('Nlength', $this->_string_shift($response, 4))); $key = $this->_string_shift($response, $length); - if (strlen($response) < 4) { - user_error('Malformed file attributes'); - return $attr; - } extract(unpack('Nlength', $this->_string_shift($response, 4))); $attr[$key] = $this->_string_shift($response, $length); } @@ -2947,10 +2763,10 @@ class SFTP extends SSH2 * @return bool * @access private */ - function _send_sftp_packet($type, $data, $request_id = 1) + function _send_sftp_packet($type, $data) { - $packet = $this->use_request_id ? - pack('NCNa*', strlen($data) + 5, $type, $request_id, $data) : + $packet = $this->request_id !== false ? + pack('NCNa*', strlen($data) + 5, $type, $this->request_id, $data) : pack('NCa*', strlen($data) + 1, $type, $data); $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 @@ -2988,24 +2804,15 @@ class SFTP extends SSH2 * @return string * @access private */ - function _get_sftp_packet($request_id = null) + function _get_sftp_packet() { - if (isset($request_id) && isset($this->requestBuffer[$request_id])) { - $this->packet_type = $this->requestBuffer[$request_id]['packet_type']; - $temp = $this->requestBuffer[$request_id]['packet']; - unset($this->requestBuffer[$request_id]); - return $temp; - } - - // in SSH2.php the timeout is cumulative per function call. eg. exec() will - // timeout after 10s. but for SFTP.php it's cumulative per packet - $this->curTimeout = $this->timeout; + $this->curTimeout = false; $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 // SFTP packet length while (strlen($this->packet_buffer) < 4) { - $temp = $this->_get_channel_packet(self::CHANNEL, true); + $temp = $this->_get_channel_packet(self::CHANNEL); if (is_bool($temp)) { $this->packet_type = false; $this->packet_buffer = ''; @@ -3013,23 +2820,13 @@ class SFTP extends SSH2 } $this->packet_buffer.= $temp; } - if (strlen($this->packet_buffer) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4))); $tempLength = $length; $tempLength-= strlen($this->packet_buffer); - - // 256 * 1024 is what SFTP_MAX_MSG_LENGTH is set to in OpenSSH's sftp-common.h - if ($tempLength > 256 * 1024) { - user_error('Invalid SFTP packet size'); - return false; - } - // SFTP packet type and data payload while ($tempLength > 0) { - $temp = $this->_get_channel_packet(self::CHANNEL, true); + $temp = $this->_get_channel_packet(self::CHANNEL); if (is_bool($temp)) { $this->packet_type = false; $this->packet_buffer = ''; @@ -3043,8 +2840,8 @@ class SFTP extends SSH2 $this->packet_type = ord($this->_string_shift($this->packet_buffer)); - if ($this->use_request_id) { - extract(unpack('Npacket_id', $this->_string_shift($this->packet_buffer, 4))); // remove the request id + if ($this->request_id !== false) { + $this->_string_shift($this->packet_buffer, 4); // remove the request id $length-= 5; // account for the request id and the packet type } else { $length-= 1; // account for the packet type @@ -3067,21 +2864,13 @@ class SFTP extends SSH2 } } - if (isset($request_id) && $this->use_request_id && $packet_id != $request_id) { - $this->requestBuffer[$packet_id] = array( - 'packet_type' => $this->packet_type, - 'packet' => $packet - ); - return $this->_get_sftp_packet($request_id); - } - return $packet; } /** * Returns a log of the packets that have been sent and received. * - * Returns a string if NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX, an array if NET_SFTP_LOGGING == NET_SFTP_LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING') + * Returns a string if NET_SFTP_LOGGING == self::LOG_COMPLEX, an array if NET_SFTP_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING') * * @access public * @return string or Array @@ -3105,7 +2894,7 @@ class SFTP extends SSH2 /** * Returns all errors * - * @return array + * @return string * @access public */ function getSFTPErrors() diff --git a/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php b/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php index d2c4425dea..d19d08b837 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php @@ -19,6 +19,7 @@ namespace phpseclib\Net\SFTP; use phpseclib\Crypt\RSA; use phpseclib\Net\SFTP; +use phpseclib\Net\SSH2; /** * SFTP Stream Wrapper @@ -177,13 +178,12 @@ class Stream } } - if ($host[0] == '$') { - $host = substr($host, 1); - global ${$host}; - if (($$host instanceof SFTP) === false) { + if (preg_match('/^{[a-z0-9]+}$/i', $host)) { + $host = SSH2::getConnectionByResourceId($host); + if ($host === false) { return false; } - $this->sftp = $$host; + $this->sftp = $host; } else { if (isset($this->context)) { $context = stream_context_get_options($this->context); diff --git a/vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php b/vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php index 514b20a2d9..2ed4a002a0 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php @@ -48,6 +48,7 @@ namespace phpseclib\Net; +use ParagonIE\ConstantTime\Hex; use phpseclib\Crypt\DES; use phpseclib\Crypt\Random; use phpseclib\Crypt\TripleDES; @@ -537,14 +538,15 @@ class SSH1 * Connect to an SSHv1 server * * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors * @access private */ function _connect() { $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->connectionTimeout); if (!$this->fsock) { - user_error(rtrim("Cannot connect to {$this->host}:{$this->port}. Error $errno. $errstr")); - return false; + throw new \RuntimeException(rtrim("Cannot connect to $host. Error $errno. $errstr")); } $this->server_identification = $init_line = fgets($this->fsock, 255); @@ -555,66 +557,45 @@ class SSH1 } if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) { - user_error('Can only connect to SSH servers'); - return false; + throw new \RuntimeException('Can only connect to SSH servers'); } if ($parts[1][0] != 1) { - user_error("Cannot connect to SSH $parts[1] servers"); - return false; + throw new \RuntimeException("Cannot connect to $parts[1] servers"); } fputs($this->fsock, $this->identifier."\r\n"); $response = $this->_get_binary_packet(); if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) { - user_error('Expected SSH_SMSG_PUBLIC_KEY'); - return false; + throw new \UnexpectedValueException('Expected SSH_SMSG_PUBLIC_KEY'); } $anti_spoofing_cookie = $this->_string_shift($response[self::RESPONSE_DATA], 8); $this->_string_shift($response[self::RESPONSE_DATA], 4); - if (strlen($response[self::RESPONSE_DATA]) < 2) { - return false; - } $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); $server_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); $this->server_key_public_exponent = $server_key_public_exponent; - if (strlen($response[self::RESPONSE_DATA]) < 2) { - return false; - } $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); $server_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); - $this->server_key_public_modulus = $server_key_public_modulus; $this->_string_shift($response[self::RESPONSE_DATA], 4); - if (strlen($response[self::RESPONSE_DATA]) < 2) { - return false; - } $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); $host_key_public_exponent = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); $this->host_key_public_exponent = $host_key_public_exponent; - if (strlen($response[self::RESPONSE_DATA]) < 2) { - return false; - } $temp = unpack('nlen', $this->_string_shift($response[self::RESPONSE_DATA], 2)); $host_key_public_modulus = new BigInteger($this->_string_shift($response[self::RESPONSE_DATA], ceil($temp['len'] / 8)), 256); - $this->host_key_public_modulus = $host_key_public_modulus; $this->_string_shift($response[self::RESPONSE_DATA], 4); // get a list of the supported ciphers - if (strlen($response[self::RESPONSE_DATA]) < 4) { - return false; - } extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4))); - foreach ($this->supported_ciphers as $mask => $name) { if (($supported_ciphers_mask & (1 << $mask)) == 0) { unset($this->supported_ciphers[$mask]); @@ -622,9 +603,6 @@ class SSH1 } // get a list of the supported authentications - if (strlen($response[self::RESPONSE_DATA]) < 4) { - return false; - } extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4))); foreach ($this->supported_authentications as $mask => $name) { if (($supported_authentications_mask & (1 << $mask)) == 0) { @@ -632,7 +610,7 @@ class SSH1 } } - $session_id = pack('H*', md5($host_key_public_modulus->toBytes() . $server_key_public_modulus->toBytes() . $anti_spoofing_cookie)); + $session_id = md5($host_key_public_modulus->toBytes() . $server_key_public_modulus->toBytes() . $anti_spoofing_cookie, true); $session_key = Random::string(32); $double_encrypted_session_key = $session_key ^ str_pad($session_id, 32, chr(0)); @@ -673,8 +651,7 @@ class SSH1 $data = pack('C2a*na*N', NET_SSH1_CMSG_SESSION_KEY, $cipher, $anti_spoofing_cookie, 8 * strlen($double_encrypted_session_key), $double_encrypted_session_key, 0); if (!$this->_send_binary_packet($data)) { - user_error('Error sending SSH_CMSG_SESSION_KEY'); - return false; + throw new \RuntimeException('Error sending SSH_CMSG_SESSION_KEY'); } switch ($cipher) { @@ -682,16 +659,20 @@ class SSH1 // $this->crypto = new \phpseclib\Crypt\Null(); // break; case self::CIPHER_DES: - $this->crypto = new DES(); + $this->crypto = new DES(DES::MODE_CBC); $this->crypto->disablePadding(); $this->crypto->enableContinuousBuffer(); $this->crypto->setKey(substr($session_key, 0, 8)); + // "The iv (initialization vector) is initialized to all zeroes." + $this->crypto->setIV(str_repeat("\0", 8)); break; case self::CIPHER_3DES: $this->crypto = new TripleDES(TripleDES::MODE_3CBC); $this->crypto->disablePadding(); $this->crypto->enableContinuousBuffer(); $this->crypto->setKey(substr($session_key, 0, 24)); + // "All three initialization vectors are initialized to zero." + $this->crypto->setIV(str_repeat("\0", 8)); break; //case self::CIPHER_RC4: // $this->crypto = new RC4(); @@ -703,8 +684,7 @@ class SSH1 $response = $this->_get_binary_packet(); if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) { - user_error('Expected SSH_SMSG_SUCCESS'); - return false; + throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS'); } $this->bitmap = self::MASK_CONNECTED; @@ -718,6 +698,8 @@ class SSH1 * @param string $username * @param string $password * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors * @access public */ function login($username, $password = '') @@ -736,8 +718,7 @@ class SSH1 $data = pack('CNa*', NET_SSH1_CMSG_USER, strlen($username), $username); if (!$this->_send_binary_packet($data)) { - user_error('Error sending SSH_CMSG_USER'); - return false; + throw new \RuntimeException('Error sending SSH_CMSG_USER'); } $response = $this->_get_binary_packet(); @@ -749,15 +730,13 @@ class SSH1 $this->bitmap |= self::MASK_LOGIN; return true; } elseif ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE) { - user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); - return false; + throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); } $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen($password), $password); if (!$this->_send_binary_packet($data)) { - user_error('Error sending SSH_CMSG_AUTH_PASSWORD'); - return false; + throw new \RuntimeException('Error sending SSH_CMSG_AUTH_PASSWORD'); } // remove the username and password from the last logged packet @@ -777,8 +756,7 @@ class SSH1 } elseif ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE) { return false; } else { - user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); - return false; + throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); } } @@ -813,20 +791,19 @@ class SSH1 * @see self::interactiveWrite() * @param string $cmd * @return mixed + * @throws \RuntimeException on error sending command * @access public */ function exec($cmd, $block = true) { if (!($this->bitmap & self::MASK_LOGIN)) { - user_error('Operation disallowed prior to login()'); - return false; + throw new \RuntimeException('Operation disallowed prior to login()'); } $data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD, strlen($cmd), $cmd); if (!$this->_send_binary_packet($data)) { - user_error('Error sending SSH_CMSG_EXEC_CMD'); - return false; + throw new \RuntimeException('Error sending SSH_CMSG_EXEC_CMD'); } if (!$block) { @@ -862,6 +839,8 @@ class SSH1 * @see self::interactiveRead() * @see self::interactiveWrite() * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors * @access private */ function _initShell() @@ -872,8 +851,7 @@ class SSH1 $data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, self::TTY_OP_END); if (!$this->_send_binary_packet($data)) { - user_error('Error sending SSH_CMSG_REQUEST_PTY'); - return false; + throw new \RuntimeException('Error sending SSH_CMSG_REQUEST_PTY'); } $response = $this->_get_binary_packet(); @@ -882,15 +860,13 @@ class SSH1 return false; } if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) { - user_error('Expected SSH_SMSG_SUCCESS'); - return false; + throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS'); } $data = pack('C', NET_SSH1_CMSG_EXEC_SHELL); if (!$this->_send_binary_packet($data)) { - user_error('Error sending SSH_CMSG_EXEC_SHELL'); - return false; + throw new \RuntimeException('Error sending SSH_CMSG_EXEC_SHELL'); } $this->bitmap |= self::MASK_SHELL; @@ -916,30 +892,29 @@ class SSH1 /** * Returns the output of an interactive shell when there's a match for $expect * - * $expect can take the form of a string literal or, if $mode == self::READ_REGEX, + * $expect can take the form of a string literal or, if $mode == self::READ__REGEX, * a regular expression. * * @see self::write() * @param string $expect * @param int $mode * @return bool + * @throws \RuntimeException on connection error * @access public */ - function read($expect, $mode = self::READ_SIMPLE) + function read($expect, $mode = self::READ__SIMPLE) { if (!($this->bitmap & self::MASK_LOGIN)) { - user_error('Operation disallowed prior to login()'); - return false; + throw new \RuntimeException('Operation disallowed prior to login()'); } if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { - user_error('Unable to initiate an interactive shell session'); - return false; + throw new \RuntimeException('Unable to initiate an interactive shell session'); } $match = $expect; while (true) { - if ($mode == self::READ_REGEX) { + if ($mode == self::READ__REGEX) { preg_match($expect, $this->interactiveBuffer, $matches); $match = isset($matches[0]) ? $matches[0] : ''; } @@ -962,25 +937,23 @@ class SSH1 * @see self::interactiveRead() * @param string $cmd * @return bool + * @throws \RuntimeException on connection error * @access public */ function interactiveWrite($cmd) { if (!($this->bitmap & self::MASK_LOGIN)) { - user_error('Operation disallowed prior to login()'); - return false; + throw new \RuntimeException('Operation disallowed prior to login()'); } if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { - user_error('Unable to initiate an interactive shell session'); - return false; + throw new \RuntimeException('Unable to initiate an interactive shell session'); } $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($cmd), $cmd); if (!$this->_send_binary_packet($data)) { - user_error('Error sending SSH_CMSG_STDIN'); - return false; + throw new \RuntimeException('Error sending SSH_CMSG_STDIN'); } return true; @@ -997,18 +970,17 @@ class SSH1 * * @see self::interactiveRead() * @return string + * @throws \RuntimeException on connection error * @access public */ function interactiveRead() { if (!($this->bitmap & self::MASK_LOGIN)) { - user_error('Operation disallowed prior to login()'); - return false; + throw new \RuntimeException('Operation disallowed prior to login()'); } if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { - user_error('Unable to initiate an interactive shell session'); - return false; + throw new \RuntimeException('Unable to initiate an interactive shell session'); } $read = array($this->fsock); @@ -1112,11 +1084,7 @@ class SSH1 } $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 - $data = fread($this->fsock, 4); - if (strlen($data) < 4) { - return false; - } - $temp = unpack('Nlength', $data); + $temp = unpack('Nlength', fread($this->fsock, 4)); $padding_length = 8 - ($temp['length'] & 7); $length = $temp['length'] + $padding_length; @@ -1137,9 +1105,6 @@ class SSH1 $type = $raw[$padding_length]; $data = substr($raw, $padding_length + 1, -4); - if (strlen($raw) < 4) { - return false; - } $temp = unpack('Ncrc', substr($raw, -4)); //if ( $temp['crc'] != $this->_crc($padding . $type . $data) ) { @@ -1341,9 +1306,9 @@ class SSH1 { /* $rsa = new RSA(); - $rsa->loadKey($key, RSA::PUBLIC_FORMAT_RAW); - $rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1); - return $rsa->encrypt($m); + $rsa->load($key, 'raw'); + $rsa->setHash('sha1'); + return $rsa->encrypt($m, RSA::PADDING_PKCS1); */ // To quote from protocol-1.5.txt: diff --git a/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php b/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php index dd7698d5e3..97a37a0bf2 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php @@ -26,7 +26,7 @@ * * $key = new \phpseclib\Crypt\RSA(); * //$key->setPassword('whatever'); - * $key->loadKey(file_get_contents('privatekey')); + * $key->load(file_get_contents('privatekey')); * * $ssh = new \phpseclib\Net\SSH2('www.domain.tld'); * if (!$ssh->login('username', $key)) { @@ -49,6 +49,7 @@ namespace phpseclib\Net; +use ParagonIE\ConstantTime\Base64; use phpseclib\Crypt\Base; use phpseclib\Crypt\Blowfish; use phpseclib\Crypt\Hash; @@ -60,6 +61,7 @@ use phpseclib\Crypt\TripleDES; use phpseclib\Crypt\Twofish; use phpseclib\Math\BigInteger; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification. use phpseclib\System\SSH\Agent; +use phpseclib\Exception\NoSupportedAlgorithmsException; /** * Pure-PHP implementation of SSHv2. @@ -100,11 +102,10 @@ class SSH2 * @see \phpseclib\Net\SSH2::_get_channel_packet() * @access private */ - const CHANNEL_EXEC = 1; // PuTTy uses 0x100 - const CHANNEL_SHELL = 2; - const CHANNEL_SUBSYSTEM = 3; - const CHANNEL_AGENT_FORWARD = 4; - const CHANNEL_KEEP_ALIVE = 5; + const CHANNEL_EXEC = 0; // PuTTy uses 0x100 + const CHANNEL_SHELL = 1; + const CHANNEL_SUBSYSTEM = 2; + const CHANNEL_AGENT_FORWARD = 3; /**#@-*/ /**#@+ @@ -127,10 +128,6 @@ class SSH2 * Dumps the content real-time to a file */ const LOG_REALTIME_FILE = 4; - /** - * Make sure that the log never gets larger than this - */ - const LOG_MAX_SIZE = 1048576; // 1024 * 1024 /**#@-*/ /**#@+ @@ -146,12 +143,9 @@ class SSH2 */ const READ_REGEX = 2; /** - * Returns whenever a data packet is received. - * - * Some data packets may only contain a single character so it may be necessary - * to call read() multiple times when using this option + * Make sure that the log never gets larger than this */ - const READ_NEXT = 3; + const LOG_MAX_SIZE = 1048576; // 1024 * 1024 /**#@-*/ /** @@ -403,7 +397,7 @@ class SSH2 var $server_public_host_key; /** - * Session identifier + * Session identifer * * "The exchange hash H from the first key exchange is additionally * used as the session identifier, which is a unique identifier for @@ -875,68 +869,12 @@ class SSH2 var $agent; /** - * Send the identification string first? + * Connection storage to replicates ssh2 extension functionality: + * {@link http://php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-examples} * - * @var bool - * @access private + * @var SSH2[] */ - var $send_id_string_first = true; - - /** - * Send the key exchange initiation packet first? - * - * @var bool - * @access private - */ - var $send_kex_first = true; - - /** - * Some versions of OpenSSH incorrectly calculate the key size - * - * @var bool - * @access private - */ - var $bad_key_size_fix = false; - - /** - * The selected decryption algorithm - * - * @var string - * @access private - */ - var $decrypt_algorithm = ''; - - /** - * Should we try to re-connect to re-establish keys? - * - * @var bool - * @access private - */ - var $retry_connect = false; - - /** - * Binary Packet Buffer - * - * @var string|false - * @access private - */ - var $binary_packet_buffer = false; - - /** - * Preferred Signature Format - * - * @var string|false - * @access private - */ - var $preferred_signature_format = false; - - /** - * Authentication Credentials - * - * @var array - * @access private - */ - var $auth = array(); + static $connections; /** * Default Constructor. @@ -1031,6 +969,8 @@ class SSH2 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY') ); + self::$connections[$this->getResourceId()] = $this; + if (is_resource($host)) { $this->fsock = $host; return; @@ -1050,73 +990,19 @@ class SSH2 * CRYPT_MODE_INTERNAL, CRYPT_MODE_MCRYPT * * @param int $engine - * @access public + * @access private */ function setCryptoEngine($engine) { $this->crypto_engine = $engine; } - /** - * Send Identification String First - * - * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established, - * both sides MUST send an identification string". It does not say which side sends it first. In - * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy - * - * @access public - */ - function sendIdentificationStringFirst() - { - $this->send_id_string_first = true; - } - - /** - * Send Identification String Last - * - * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established, - * both sides MUST send an identification string". It does not say which side sends it first. In - * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy - * - * @access public - */ - function sendIdentificationStringLast() - { - $this->send_id_string_first = false; - } - - /** - * Send SSH_MSG_KEXINIT First - * - * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending - * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory - * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy - * - * @access public - */ - function sendKEXINITFirst() - { - $this->send_kex_first = true; - } - - /** - * Send SSH_MSG_KEXINIT Last - * - * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending - * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory - * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy - * - * @access public - */ - function sendKEXINITLast() - { - $this->send_kex_first = false; - } - /** * Connect to an SSHv2 server * * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors * @access private */ function _connect() @@ -1133,32 +1019,21 @@ class SSH2 if (!is_resource($this->fsock)) { $start = microtime(true); - // with stream_select a timeout of 0 means that no timeout takes place; - // with fsockopen a timeout of 0 means that you instantly timeout - // to resolve this incompatibility a timeout of 100,000 will be used for fsockopen if timeout is 0 - $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout == 0 ? 100000 : $this->curTimeout); + $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout); if (!$this->fsock) { $host = $this->host . ':' . $this->port; - user_error(rtrim("Cannot connect to $host. Error $errno. $errstr")); - return false; + throw new \RuntimeException(rtrim("Cannot connect to $host. Error $errno. $errstr")); } $elapsed = microtime(true) - $start; - if ($this->curTimeout) { - $this->curTimeout-= $elapsed; - if ($this->curTimeout < 0) { - $this->is_timeout = true; - return false; - } + $this->curTimeout-= $elapsed; + + if ($this->curTimeout <= 0) { + $this->is_timeout = true; + return false; } } - $this->identifier = $this->_generate_identifier(); - - if ($this->send_id_string_first) { - fputs($this->fsock, $this->identifier . "\r\n"); - } - /* According to the SSH2 specs, "The server MAY send other lines of data before sending the version @@ -1196,31 +1071,21 @@ class SSH2 } $line.= "$temp\n"; - - // quoting RFC4253, "Implementers who wish to maintain - // compatibility with older, undocumented versions of this protocol may - // want to process the identification string without expecting the - // presence of the carriage return character for reasons described in - // Section 5 of this document." - - //if (substr($line, -2) == "\r\n") { - // break; - //} - - break; + if (substr($line, -2) == "\r\n") { + break; + } } - $data.= $line; } if (feof($this->fsock)) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } $extra = $matches[1]; + $this->identifier = $this->_generate_identifier(); + if (defined('NET_SSH2_LOGGING')) { $this->_append_log('<-', $matches[0]); $this->_append_log('->', $this->identifier . "\r\n"); @@ -1228,37 +1093,25 @@ class SSH2 $this->server_identifier = trim($temp, "\r\n"); if (strlen($extra)) { - $this->errors[] = $data; + $this->errors[] = utf8_decode($data); } - if (version_compare($matches[3], '1.99', '<')) { - user_error("Cannot connect to SSH $matches[3] servers"); - return false; + if ($matches[3] != '1.99' && $matches[3] != '2.0') { + throw new \RuntimeException("Cannot connect to SSH $matches[1] servers"); } - if (!$this->send_id_string_first) { - fputs($this->fsock, $this->identifier . "\r\n"); + fputs($this->fsock, $this->identifier . "\r\n"); + + $response = $this->_get_binary_packet(); + if ($response === false) { + throw new \RuntimeException('Connection closed by server'); } - if (!$this->send_kex_first) { - $response = $this->_get_binary_packet(); - if ($response === false) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; - } - - if (!strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) { - user_error('Expected SSH_MSG_KEXINIT'); - return false; - } - - if (!$this->_key_exchange($response)) { - return false; - } + if (ord($response[0]) != NET_SSH2_MSG_KEXINIT) { + throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT'); } - if ($this->send_kex_first && !$this->_key_exchange()) { + if (!$this->_key_exchange($response)) { return false; } @@ -1280,7 +1133,7 @@ class SSH2 $identifier = 'SSH-2.0-phpseclib_2.0'; $ext = array(); - if (function_exists('\\Sodium\\library_version_major')) { + if (extension_loaded('libsodium')) { $ext[] = 'libsodium'; } @@ -1306,10 +1159,13 @@ class SSH2 /** * Key Exchange * - * @param string $kexinit_payload_server optional + * @param string $kexinit_payload_server + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors + * @throws \phpseclib\Exception\NoSupportedAlgorithmsException when none of the algorithms phpseclib has loaded are compatible * @access private */ - function _key_exchange($kexinit_payload_server = false) + function _key_exchange($kexinit_payload_server) { $kex_algorithms = array( // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using @@ -1332,8 +1188,6 @@ class SSH2 } $server_host_key_algorithms = array( - 'rsa-sha2-256', // RFC 8332 - 'rsa-sha2-512', // RFC 8332 'ssh-rsa', // RECOMMENDED sign Raw RSA Key 'ssh-dss' // REQUIRED sign Raw DSS Key ); @@ -1432,9 +1286,8 @@ class SSH2 ); // some SSH servers have buggy implementations of some of the above algorithms - switch (true) { - case $this->server_identifier == 'SSH-2.0-SSHD': - case substr($this->server_identifier, 0, 13) == 'SSH-2.0-DLINK': + switch ($this->server_identifier) { + case 'SSH-2.0-SSHD': $mac_algorithms = array_values(array_diff( $mac_algorithms, array('hmac-sha1-96', 'hmac-md5-96') @@ -1449,6 +1302,44 @@ class SSH2 $client_cookie = Random::string(16); + $response = $kexinit_payload_server; + $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT) + $server_cookie = $this->_string_shift($response, 16); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); + + $temp = unpack('Nlength', $this->_string_shift($response, 4)); + $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); + + extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1))); + $first_kex_packet_follows = $first_kex_packet_follows != 0; + + // the sending of SSH2_MSG_KEXINIT could go in one of two places. this is the second place. $kexinit_payload_client = pack( 'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN', NET_SSH2_MSG_KEXINIT, @@ -1477,120 +1368,34 @@ class SSH2 0 ); - if ($this->send_kex_first) { - if (!$this->_send_binary_packet($kexinit_payload_client)) { - return false; - } - - $kexinit_payload_server = $this->_get_binary_packet(); - if ($kexinit_payload_server === false) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; - } - - if (!strlen($kexinit_payload_server) || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT) { - user_error('Expected SSH_MSG_KEXINIT'); - return false; - } - } - - $response = $kexinit_payload_server; - $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT) - $server_cookie = $this->_string_shift($response, 16); - - if (strlen($response) < 4) { - return false; - } - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); - - if (strlen($response) < 4) { - return false; - } - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length'])); - - if (strlen($response) < 4) { - return false; - } - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); - - if (strlen($response) < 4) { - return false; - } - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); - - if (strlen($response) < 4) { - return false; - } - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); - - if (strlen($response) < 4) { - return false; - } - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); - - if (strlen($response) < 4) { - return false; - } - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); - - if (strlen($response) < 4) { - return false; - } - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); - - if (strlen($response) < 4) { - return false; - } - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length'])); - - if (strlen($response) < 4) { - return false; - } - $temp = unpack('Nlength', $this->_string_shift($response, 4)); - $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length'])); - - if (!strlen($response)) { - return false; - } - extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1))); - $first_kex_packet_follows = $first_kex_packet_follows != 0; - - if (!$this->send_kex_first && !$this->_send_binary_packet($kexinit_payload_client)) { + if (!$this->_send_binary_packet($kexinit_payload_client)) { return false; } + // here ends the second place. // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange + // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the // diffie-hellman key exchange as fast as possible $decrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_server_to_client); $decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt); if ($decryptKeyLength === null) { - user_error('No compatible server to client encryption algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible server to client encryption algorithms found'); } $encrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_client_to_server); $encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt); if ($encryptKeyLength === null) { - user_error('No compatible client to server encryption algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible client to server encryption algorithms found'); } // through diffie-hellman key exchange a symmetric key is obtained $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms); if ($kex_algorithm === false) { - user_error('No compatible key exchange algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible key exchange algorithms found'); } // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty. @@ -1621,7 +1426,6 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - $this->bitmap = 0; user_error('Connection closed by server'); return false; } @@ -1631,16 +1435,10 @@ class SSH2 return false; } - if (strlen($response) < 4) { - return false; - } extract(unpack('NprimeLength', $this->_string_shift($response, 4))); $primeBytes = $this->_string_shift($response, $primeLength); $prime = new BigInteger($primeBytes, -256); - if (strlen($response) < 4) { - return false; - } extract(unpack('NgLength', $this->_string_shift($response, 4))); $gBytes = $this->_string_shift($response, $gLength); $g = new BigInteger($gBytes, -256); @@ -1706,7 +1504,7 @@ class SSH2 $max = $one->bitwise_leftShift(16 * $keyLength); // 2 * 8 * $keyLength $max = $max->subtract($one); - $x = $one->random($one, $max); + $x = BigInteger::random($one, $max); $e = $g->modPow($x, $prime); $eBytes = $e->toBytes(true); @@ -1714,54 +1512,31 @@ class SSH2 $data = pack('CNa*', $clientKexInitMessage, strlen($eBytes), $eBytes); if (!$this->_send_binary_packet($data)) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } $response = $this->_get_binary_packet(); if ($response === false) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; - } - if (!strlen($response)) { - return false; + throw new \RuntimeException('Connection closed by server'); } extract(unpack('Ctype', $this->_string_shift($response, 1))); if ($type != $serverKexReplyMessage) { - user_error('Expected SSH_MSG_KEXDH_REPLY'); - return false; + throw new \UnexpectedValueException('Expected SSH_MSG_KEXDH_REPLY'); } - if (strlen($response) < 4) { - return false; - } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']); - if (strlen($server_public_host_key) < 4) { - return false; - } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']); - if (strlen($response) < 4) { - return false; - } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $fBytes = $this->_string_shift($response, $temp['length']); - if (strlen($response) < 4) { - return false; - } $temp = unpack('Nlength', $this->_string_shift($response, 4)); $this->signature = $this->_string_shift($response, $temp['length']); - if (strlen($this->signature) < 4) { - return false; - } $temp = unpack('Nlength', $this->_string_shift($this->signature, 4)); $this->signature_format = $this->_string_shift($this->signature, $temp['length']); @@ -1807,29 +1582,13 @@ class SSH2 $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms); if ($server_host_key_algorithm === false) { - user_error('No compatible server host key algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible server host key algorithms found'); } - switch ($server_host_key_algorithm) { - case 'ssh-dss': - $expected_key_format = 'ssh-dss'; - break; - //case 'rsa-sha2-256': - //case 'rsa-sha2-512': - //case 'ssh-rsa': - default: - $expected_key_format = 'ssh-rsa'; - } - - if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) { - switch (true) { - case $this->signature_format == $server_host_key_algorithm: - case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512': - case $this->signature_format != 'ssh-rsa': - user_error('Server Host Key Algorithm Mismatch'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - } + if ($public_key_format != $server_host_key_algorithm || $this->signature_format != $server_host_key_algorithm) { + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \RuntimeException('Server Host Key Algorithm Mismatch'); } $packet = pack( @@ -1844,29 +1603,21 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } - if (!strlen($response)) { - return false; - } extract(unpack('Ctype', $this->_string_shift($response, 1))); if ($type != NET_SSH2_MSG_NEWKEYS) { - user_error('Expected SSH_MSG_NEWKEYS'); - return false; + throw new \UnexpectedValueException('Expected SSH_MSG_NEWKEYS'); } - $this->decrypt_algorithm = $decrypt; - $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes); $this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt); if ($this->encrypt) { if ($this->crypto_engine) { - $this->encrypt->setPreferredEngine($this->crypto_engine); + $this->encrypt->setEngine($this->crypto_engine); } if ($this->encrypt->block_size) { $this->encrypt_block_size = $this->encrypt->block_size; @@ -1874,11 +1625,13 @@ class SSH2 $this->encrypt->enableContinuousBuffer(); $this->encrypt->disablePadding(); - $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id); - while ($this->encrypt_block_size > strlen($iv)) { - $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); + if ($this->encrypt->usesIV()) { + $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id); + while ($this->encrypt_block_size > strlen($iv)) { + $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); + } + $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size)); } - $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size)); $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id); while ($encryptKeyLength > strlen($key)) { @@ -1890,7 +1643,7 @@ class SSH2 $this->decrypt = $this->_encryption_algorithm_to_crypt_instance($decrypt); if ($this->decrypt) { if ($this->crypto_engine) { - $this->decrypt->setPreferredEngine($this->crypto_engine); + $this->decrypt->setEngine($this->crypto_engine); } if ($this->decrypt->block_size) { $this->decrypt_block_size = $this->decrypt->block_size; @@ -1898,11 +1651,13 @@ class SSH2 $this->decrypt->enableContinuousBuffer(); $this->decrypt->disablePadding(); - $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id); - while ($this->decrypt_block_size > strlen($iv)) { - $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); + if ($this->decrypt->usesIV()) { + $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id); + while ($this->decrypt_block_size > strlen($iv)) { + $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv); + } + $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size)); } - $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size)); $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id); while ($decryptKeyLength > strlen($key)) { @@ -1927,8 +1682,8 @@ class SSH2 $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_client_to_server); if ($mac_algorithm === false) { - user_error('No compatible client to server message authentication algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible client to server message authentication algorithms found'); } $createKeyLength = 0; // ie. $mac_algorithm == 'none' @@ -1956,8 +1711,8 @@ class SSH2 $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_server_to_client); if ($mac_algorithm === false) { - user_error('No compatible server to client message authentication algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible server to client message authentication algorithms found'); } $checkKeyLength = 0; @@ -2003,15 +1758,15 @@ class SSH2 $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_server_to_client); if ($compression_algorithm === false) { - user_error('No compatible server to client compression algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible server to client compression algorithms found'); } $this->decompress = $compression_algorithm == 'zlib'; $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_client_to_server); if ($compression_algorithm === false) { - user_error('No compatible client to server compression algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new NoSupportedAlgorithmsException('No compatible client to server compression algorithms found'); } $this->compress = $compression_algorithm == 'zlib'; @@ -2027,10 +1782,6 @@ class SSH2 */ function _encryption_algorithm_to_key_size($algorithm) { - if ($this->bad_key_size_fix && $this->_bad_algorithm_candidate($algorithm)) { - return 16; - } - switch ($algorithm) { case 'none': return 0; @@ -2073,26 +1824,26 @@ class SSH2 { switch ($algorithm) { case '3des-cbc': - return new TripleDES(); + return new TripleDES(Base::MODE_CBC); case '3des-ctr': return new TripleDES(Base::MODE_CTR); case 'aes256-cbc': case 'aes192-cbc': case 'aes128-cbc': - return new Rijndael(); + return new Rijndael(Base::MODE_CBC); case 'aes256-ctr': case 'aes192-ctr': case 'aes128-ctr': return new Rijndael(Base::MODE_CTR); case 'blowfish-cbc': - return new Blowfish(); + return new Blowfish(Base::MODE_CBC); case 'blowfish-ctr': return new Blowfish(Base::MODE_CTR); case 'twofish128-cbc': case 'twofish192-cbc': case 'twofish256-cbc': case 'twofish-cbc': - return new Twofish(); + return new Twofish(Base::MODE_CBC); case 'twofish128-ctr': case 'twofish192-ctr': case 'twofish256-ctr': @@ -2105,27 +1856,6 @@ class SSH2 return null; } - /* - * Tests whether or not proposed algorithm has a potential for issues - * - * @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html - * @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291 - * @param string $algorithm Name of the encryption algorithm - * @return bool - * @access private - */ - function _bad_algorithm_candidate($algorithm) - { - switch ($algorithm) { - case 'arcfour256': - case 'aes192-ctr': - case 'aes256-ctr': - return true; - } - - return false; - } - /** * Login * @@ -2141,7 +1871,6 @@ class SSH2 function login($username) { $args = func_get_args(); - $this->auth[] = $args; return call_user_func_array(array(&$this, '_login'), $args); } @@ -2182,6 +1911,8 @@ class SSH2 * @param string $username * @param string $password * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors * @access private * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} * by sending dummy SSH_MSG_IGNORE messages. @@ -2206,26 +1937,13 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - if ($this->retry_connect) { - $this->retry_connect = false; - if (!$this->_connect()) { - return false; - } - return $this->_login_helper($username, $password); - } - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } - if (strlen($response) < 4) { - return false; - } extract(unpack('Ctype', $this->_string_shift($response, 1))); if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) { - user_error('Expected SSH_MSG_SERVICE_ACCEPT'); - return false; + throw new \UnexpectedValueException('Expected SSH_MSG_SERVICE_ACCEPT'); } $this->bitmap |= self::MASK_LOGIN_REQ; } @@ -2266,14 +1984,9 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } - if (!strlen($response)) { - return false; - } extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { @@ -2325,14 +2038,9 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } - if (!strlen($response)) { - return false; - } extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { @@ -2340,23 +2048,14 @@ class SSH2 if (defined('NET_SSH2_LOGGING')) { $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'; } - if (strlen($response) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($response, 4))); - $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $this->_string_shift($response, $length); + $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length)); return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); case NET_SSH2_MSG_USERAUTH_FAILURE: // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees // multi-factor authentication - if (strlen($response) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($response, 4))); $auth_methods = explode(',', $this->_string_shift($response, $length)); - if (!strlen($response)) { - return false; - } extract(unpack('Cpartial_success', $this->_string_shift($response, 1))); $partial_success = $partial_success != 0; @@ -2415,6 +2114,7 @@ class SSH2 * * @param string $responses... * @return bool + * @throws \RuntimeException on connection error * @access private */ function _keyboard_interactive_process() @@ -2426,37 +2126,20 @@ class SSH2 } else { $orig = $response = $this->_get_binary_packet(); if ($response === false) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } } - if (!strlen($response)) { - return false; - } extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_USERAUTH_INFO_REQUEST: - if (strlen($response) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->_string_shift($response, $length); // name; may be empty - if (strlen($response) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->_string_shift($response, $length); // instruction; may be empty - if (strlen($response) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->_string_shift($response, $length); // language tag; may be empty - if (strlen($response) < 4) { - return false; - } extract(unpack('Nnum_prompts', $this->_string_shift($response, 4))); for ($i = 0; $i < count($responses); $i++) { @@ -2471,9 +2154,6 @@ class SSH2 if (isset($this->keyboard_requests_responses)) { for ($i = 0; $i < $num_prompts; $i++) { - if (strlen($response) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($response, 4))); // prompt - ie. "Password: "; must not be empty $prompt = $this->_string_shift($response, $length); @@ -2570,6 +2250,7 @@ class SSH2 * @param string $username * @param \phpseclib\Crypt\RSA $password * @return bool + * @throws \RuntimeException on connection error * @access private * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} * by sending dummy SSH_MSG_IGNORE messages. @@ -2577,7 +2258,7 @@ class SSH2 function _privatekey_login($username, $privatekey) { // see http://tools.ietf.org/html/rfc4253#page-15 - $publickey = $privatekey->getPublicKey(RSA::PUBLIC_FORMAT_RAW); + $publickey = $privatekey->getPublicKey('Raw'); if ($publickey === false) { return false; } @@ -2596,21 +2277,6 @@ class SSH2 $publickey['n'] ); - switch ($this->signature_format) { - case 'rsa-sha2-512': - $hash = 'sha512'; - $signatureType = 'rsa-sha2-512'; - break; - case 'rsa-sha2-256': - $hash = 'sha256'; - $signatureType = 'rsa-sha2-256'; - break; - //case 'ssh-rsa': - default: - $hash = 'sha1'; - $signatureType = 'ssh-rsa'; - } - $part1 = pack( 'CNa*Na*Na*', NET_SSH2_MSG_USERAUTH_REQUEST, @@ -2621,7 +2287,7 @@ class SSH2 strlen('publickey'), 'publickey' ); - $part2 = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($publickey), $publickey); + $part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey); $packet = $part1 . chr(0) . $part2; if (!$this->_send_binary_packet($packet)) { @@ -2630,21 +2296,13 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } - if (!strlen($response)) { - return false; - } extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { case NET_SSH2_MSG_USERAUTH_FAILURE: - if (strlen($response) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($response, 4))); $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length); return false; @@ -2661,10 +2319,9 @@ class SSH2 } $packet = $part1 . chr(1) . $part2; - $privatekey->setSignatureMode(RSA::SIGNATURE_PKCS1); - $privatekey->setHash($hash); - $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet)); - $signature = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($signature), $signature); + $privatekey->setHash('sha1'); + $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet), RSA::PADDING_PKCS1); + $signature = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($signature), $signature); $packet.= pack('Na*', strlen($signature), $signature); if (!$this->_send_binary_packet($packet)) { @@ -2673,14 +2330,9 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } - if (!strlen($response)) { - return false; - } extract(unpack('Ctype', $this->_string_shift($response, 1))); switch ($type) { @@ -2728,6 +2380,7 @@ class SSH2 * @param string $command * @param Callback $callback * @return string + * @throws \RuntimeException on connection error * @access public */ function exec($command, $callback = null) @@ -2736,18 +2389,13 @@ class SSH2 $this->is_timeout = false; $this->stdErrorLog = ''; - if (!$this->isAuthenticated()) { - return false; - } - - if ($this->in_request_pty_exec) { - user_error('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.'); + if (!($this->bitmap & self::MASK_LOGIN)) { return false; } // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to // be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but, - // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway. + // honestly, if you're transfering more than 2GB, you probably shouldn't be using phpseclib, anyway. // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info $this->window_size_server_to_client[self::CHANNEL_EXEC] = $this->window_size; // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy @@ -2800,14 +2448,9 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } - if (!strlen($response)) { - return false; - } list(, $type) = unpack('C', $this->_string_shift($response, 1)); switch ($type) { @@ -2815,8 +2458,8 @@ class SSH2 break; case NET_SSH2_MSG_CHANNEL_FAILURE: default: - user_error('Unable to request pseudo-terminal'); - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \RuntimeException('Unable to request pseudo-terminal'); } $this->in_request_pty_exec = true; } @@ -2884,6 +2527,8 @@ class SSH2 * @see self::read() * @see self::write() * @return bool + * @throws \UnexpectedValueException on receipt of unexpected packets + * @throws \RuntimeException on other errors * @access private */ function _initShell() @@ -2940,14 +2585,9 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; + throw new \RuntimeException('Connection closed by server'); } - if (!strlen($response)) { - return false; - } list(, $type) = unpack('C', $this->_string_shift($response, 1)); switch ($type) { @@ -2956,8 +2596,8 @@ class SSH2 case NET_SSH2_MSG_CHANNEL_FAILURE: break; default: - user_error('Unable to request pseudo-terminal'); - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \UnexpectedValueException('Unable to request pseudo-terminal'); } $packet = pack( @@ -3034,6 +2674,7 @@ class SSH2 * @param string $expect * @param int $mode * @return string + * @throws \RuntimeException on connection error * @access public */ function read($expect = '', $mode = self::READ_SIMPLE) @@ -3041,22 +2682,16 @@ class SSH2 $this->curTimeout = $this->timeout; $this->is_timeout = false; - if (!$this->isAuthenticated()) { - user_error('Operation disallowed prior to login()'); - return false; + if (!($this->bitmap & self::MASK_LOGIN)) { + throw new \RuntimeException('Operation disallowed prior to login()'); } if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { - user_error('Unable to initiate an interactive shell session'); - return false; + throw new \RuntimeException('Unable to initiate an interactive shell session'); } $channel = $this->_get_interactive_channel(); - if ($mode == self::READ_NEXT) { - return $this->_get_channel_packet($channel); - } - $match = $expect; while (true) { if ($mode == self::READ_REGEX) { @@ -3083,18 +2718,17 @@ class SSH2 * @see self::read() * @param string $cmd * @return bool + * @throws \RuntimeException on connection error * @access public */ function write($cmd) { - if (!$this->isAuthenticated()) { - user_error('Operation disallowed prior to login()'); - return false; + if (!($this->bitmap & self::MASK_LOGIN)) { + throw new \RuntimeException('Operation disallowed prior to login()'); } if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { - user_error('Unable to initiate an interactive shell session'); - return false; + throw new \RuntimeException('Unable to initiate an interactive shell session'); } return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd); @@ -3218,6 +2852,7 @@ class SSH2 if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) { fclose($this->realtime_log_file); } + unset(self::$connections[$this->getResourceId()]); } /** @@ -3255,84 +2890,6 @@ class SSH2 return (bool) ($this->bitmap & self::MASK_LOGIN); } - /** - * Pings a server connection, or tries to reconnect if the connection has gone down - * - * Inspired by http://php.net/manual/en/mysqli.ping.php - * - * @return bool - * @access public - */ - function ping() - { - if (!$this->isAuthenticated()) { - return false; - } - - $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size; - $packet_size = 0x4000; - $packet = pack( - 'CNa*N3', - NET_SSH2_MSG_CHANNEL_OPEN, - strlen('session'), - 'session', - self::CHANNEL_KEEP_ALIVE, - $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE], - $packet_size - ); - - if (!@$this->_send_binary_packet($packet)) { - return $this->_reconnect(); - } - - $this->channel_status[self::CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN; - - $response = @$this->_get_channel_packet(self::CHANNEL_KEEP_ALIVE); - if ($response !== false) { - $this->_close_channel(self::CHANNEL_KEEP_ALIVE); - return true; - } - - return $this->_reconnect(); - } - - /** - * In situ reconnect method - * - * @return boolean - * @access private - */ - function _reconnect() - { - $this->_reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST); - $this->retry_connect = true; - if (!$this->_connect()) { - return false; - } - foreach ($this->auth as $auth) { - $result = call_user_func_array(array(&$this, 'login'), $auth); - } - return $result; - } - - /** - * Resets a connection for re-use - * - * @param int $reason - * @access private - */ - function _reset_connection($reason) - { - $this->_disconnect($reason); - $this->decrypt = $this->encrypt = false; - $this->decrypt_block_size = $this->encrypt_block_size = 8; - $this->hmac_check = $this->hmac_create = false; - $this->hmac_size = false; - $this->session_id = false; - $this->retry_connect = true; - $this->get_seq_no = $this->send_seq_no = 0; - } - /** * Gets Binary Packets * @@ -3340,14 +2897,14 @@ class SSH2 * * @see self::_send_binary_packet() * @return string + * @throws \RuntimeException on connection errors * @access private */ - function _get_binary_packet($skip_channel_filter = false) + function _get_binary_packet() { if (!is_resource($this->fsock) || feof($this->fsock)) { $this->bitmap = 0; - user_error('Connection closed prematurely'); - return false; + throw new \RuntimeException('Connection closed prematurely'); } $start = microtime(true); @@ -3361,13 +2918,9 @@ class SSH2 $raw = $this->decrypt->decrypt($raw); } if ($raw === false) { - user_error('Unable to decrypt content'); - return false; + throw new \RuntimeException('Unable to decrypt content'); } - if (strlen($raw) < 5) { - return false; - } extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5))); $remaining_length = $packet_length + 4 - $this->decrypt_block_size; @@ -3376,13 +2929,7 @@ class SSH2 // "implementations SHOULD check that the packet length is reasonable" // PuTTY uses 0x9000 as the actual max packet size and so to shall we if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) { - if (!$this->bad_key_size_fix && $this->_bad_algorithm_candidate($this->decrypt_algorithm) && !($this->bitmap & SSH2::MASK_LOGIN)) { - $this->bad_key_size_fix = true; - $this->_reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - return false; - } - user_error('Invalid size'); - return false; + throw new \RuntimeException('Invalid size'); } $buffer = ''; @@ -3390,13 +2937,11 @@ class SSH2 $temp = stream_get_contents($this->fsock, $remaining_length); if ($temp === false || feof($this->fsock)) { $this->bitmap = 0; - user_error('Error reading from socket'); - return false; + throw new \RuntimeException('Error reading from socket'); } $buffer.= $temp; $remaining_length-= strlen($temp); } - $stop = microtime(true); if (strlen($buffer)) { $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer; @@ -3409,11 +2954,9 @@ class SSH2 $hmac = stream_get_contents($this->fsock, $this->hmac_size); if ($hmac === false || strlen($hmac) != $this->hmac_size) { $this->bitmap = 0; - user_error('Error reading socket'); - return false; + throw new \RuntimeException('Error reading socket'); } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) { - user_error('Invalid HMAC'); - return false; + throw new \RuntimeException('Invalid HMAC'); } } @@ -3432,7 +2975,7 @@ class SSH2 $this->last_packet = $current; } - return $this->_filter($payload, $skip_channel_filter); + return $this->_filter($payload); } /** @@ -3444,72 +2987,48 @@ class SSH2 * @return string * @access private */ - function _filter($payload, $skip_channel_filter) + function _filter($payload) { switch (ord($payload[0])) { case NET_SSH2_MSG_DISCONNECT: $this->_string_shift($payload, 1); - if (strlen($payload) < 8) { - return false; - } extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8))); - $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . $this->_string_shift($payload, $length); + $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length)); $this->bitmap = 0; return false; case NET_SSH2_MSG_IGNORE: - $payload = $this->_get_binary_packet($skip_channel_filter); + $payload = $this->_get_binary_packet(); break; case NET_SSH2_MSG_DEBUG: $this->_string_shift($payload, 2); - if (strlen($payload) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($payload, 4))); - $this->errors[] = 'SSH_MSG_DEBUG: ' . $this->_string_shift($payload, $length); - $payload = $this->_get_binary_packet($skip_channel_filter); + $this->errors[] = 'SSH_MSG_DEBUG: ' . utf8_decode($this->_string_shift($payload, $length)); + $payload = $this->_get_binary_packet(); break; case NET_SSH2_MSG_UNIMPLEMENTED: return false; case NET_SSH2_MSG_KEXINIT: if ($this->session_id !== false) { - $this->send_kex_first = false; if (!$this->_key_exchange($payload)) { $this->bitmap = 0; return false; } - $payload = $this->_get_binary_packet($skip_channel_filter); + $payload = $this->_get_binary_packet(); } } // 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) { + if (($this->bitmap & self::MASK_CONNECTED) && !($this->bitmap & self::MASK_LOGIN) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) { $this->_string_shift($payload, 1); - if (strlen($payload) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($payload, 4))); - $this->banner_message = $this->_string_shift($payload, $length); + $this->banner_message = utf8_decode($this->_string_shift($payload, $length)); $payload = $this->_get_binary_packet(); } // only called when we've already logged in - if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) { + if (($this->bitmap & self::MASK_CONNECTED) && ($this->bitmap & self::MASK_LOGIN)) { switch (ord($payload[0])) { - case NET_SSH2_MSG_CHANNEL_DATA: - case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: - case NET_SSH2_MSG_CHANNEL_REQUEST: - case NET_SSH2_MSG_CHANNEL_CLOSE: - case NET_SSH2_MSG_CHANNEL_EOF: - if (!$skip_channel_filter && !empty($this->server_channels)) { - $this->binary_packet_buffer = $payload; - $this->_get_channel_packet(true); - $payload = $this->_get_binary_packet(); - } - break; case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4 - if (strlen($payload) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($payload, 4))); $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . $this->_string_shift($payload, $length); @@ -3517,18 +3036,12 @@ class SSH2 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } - $payload = $this->_get_binary_packet($skip_channel_filter); + $payload = $this->_get_binary_packet(); break; case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1 $this->_string_shift($payload, 1); - if (strlen($payload) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($payload, 4))); $data = $this->_string_shift($payload, $length); - if (strlen($payload) < 4) { - return false; - } extract(unpack('Nserver_channel', $this->_string_shift($payload, 4))); switch ($data) { case 'auth-agent': @@ -3536,9 +3049,6 @@ class SSH2 if (isset($this->agent)) { $new_channel = self::CHANNEL_AGENT_FORWARD; - if (strlen($payload) < 8) { - return false; - } extract(unpack('Nremote_window_size', $this->_string_shift($payload, 4))); extract(unpack('Nremote_maximum_packet_size', $this->_string_shift($payload, 4))); @@ -3580,18 +3090,15 @@ class SSH2 return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } } - $payload = $this->_get_binary_packet($skip_channel_filter); + $payload = $this->_get_binary_packet(); break; case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST: $this->_string_shift($payload, 1); - if (strlen($payload) < 8) { - return false; - } extract(unpack('Nchannel', $this->_string_shift($payload, 4))); extract(unpack('Nwindow_size', $this->_string_shift($payload, 4))); $this->window_size_client_to_server[$channel]+= $window_size; - $payload = ($this->bitmap & self::MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet($skip_channel_filter); + $payload = ($this->bitmap & self::MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet(); } } @@ -3652,10 +3159,6 @@ class SSH2 */ function disablePTY() { - if ($this->in_request_pty_exec) { - $this->_close_channel(self::CHANNEL_EXEC); - $this->in_request_pty_exec = false; - } $this->request_pty = false; } @@ -3679,6 +3182,7 @@ class SSH2 * * @param $client_channel * @return mixed + * @throws \RuntimeException on connection error * @access private */ function _get_channel_packet($client_channel, $skip_extended = false) @@ -3688,56 +3192,40 @@ class SSH2 } while (true) { - if ($this->binary_packet_buffer !== false) { - $response = $this->binary_packet_buffer; - $this->binary_packet_buffer = false; - } else { + if ($this->curTimeout) { + if ($this->curTimeout < 0) { + $this->is_timeout = true; + return true; + } + $read = array($this->fsock); $write = $except = null; - if (!$this->curTimeout) { - @stream_select($read, $write, $except, null); - } else { - if ($this->curTimeout < 0) { - $this->is_timeout = true; - return true; - } - - $read = array($this->fsock); - $write = $except = null; - - $start = microtime(true); - $sec = floor($this->curTimeout); - $usec = 1000000 * ($this->curTimeout - $sec); - // on windows this returns a "Warning: Invalid CRT parameters detected" error - if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { - $this->is_timeout = true; - $this->_close_channel($client_channel); - return true; - } - $elapsed = microtime(true) - $start; - $this->curTimeout-= $elapsed; - } - - $response = $this->_get_binary_packet(true); - if ($response === false) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; + $start = microtime(true); + $sec = floor($this->curTimeout); + $usec = 1000000 * ($this->curTimeout - $sec); + // on windows this returns a "Warning: Invalid CRT parameters detected" error + if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { + $this->is_timeout = true; + return true; } + $elapsed = microtime(true) - $start; + $this->curTimeout-= $elapsed; } + $response = $this->_get_binary_packet(); + if ($response === false) { + throw new \RuntimeException('Connection closed by server'); + } if ($client_channel == -1 && $response === true) { return true; } if (!strlen($response)) { - return false; + return ''; } + extract(unpack('Ctype', $this->_string_shift($response, 1))); - if (strlen($response) < 4) { - return false; - } if ($type == NET_SSH2_MSG_CHANNEL_OPEN) { extract(unpack('Nlength', $this->_string_shift($response, 4))); } else { @@ -3757,103 +3245,18 @@ class SSH2 $this->window_size_server_to_client[$channel]+= $this->window_size; } - switch ($type) { - case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: - /* - if ($client_channel == NET_SSH2_CHANNEL_EXEC) { - $this->_send_channel_packet($client_channel, chr(0)); - } - */ - // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR - if (strlen($response) < 8) { - return false; - } - extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8))); - $data = $this->_string_shift($response, $length); - $this->stdErrorLog.= $data; - if ($skip_extended || $this->quiet_mode) { - continue 2; - } - if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) { - return $data; - } - if (!isset($this->channel_buffers[$channel])) { - $this->channel_buffers[$channel] = array(); - } - $this->channel_buffers[$channel][] = $data; - - continue 2; - case NET_SSH2_MSG_CHANNEL_REQUEST: - if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) { - continue 2; - } - if (strlen($response) < 4) { - return false; - } - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $value = $this->_string_shift($response, $length); - switch ($value) { - case 'exit-signal': - $this->_string_shift($response, 1); - if (strlen($response) < 4) { - return false; - } - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length); - $this->_string_shift($response, 1); - if (strlen($response) < 4) { - return false; - } - extract(unpack('Nlength', $this->_string_shift($response, 4))); - if ($length) { - $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length); - } - - $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); - $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); - - $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF; - - continue 3; - case 'exit-status': - if (strlen($response) < 5) { - return false; - } - extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5))); - $this->exit_status = $exit_status; - - // "The client MAY ignore these messages." - // -- http://tools.ietf.org/html/rfc4254#section-6.10 - - continue 3; - default: - // "Some systems may not implement signals, in which case they SHOULD ignore this message." - // -- http://tools.ietf.org/html/rfc4254#section-6.9 - continue 3; - } - } - switch ($this->channel_status[$channel]) { case NET_SSH2_MSG_CHANNEL_OPEN: switch ($type) { case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: - if (strlen($response) < 4) { - return false; - } extract(unpack('Nserver_channel', $this->_string_shift($response, 4))); $this->server_channels[$channel] = $server_channel; - if (strlen($response) < 4) { - return false; - } extract(unpack('Nwindow_size', $this->_string_shift($response, 4))); if ($window_size < 0) { $window_size&= 0x7FFFFFFF; $window_size+= 0x80000000; } $this->window_size_client_to_server[$channel] = $window_size; - if (strlen($response) < 4) { - return false; - } $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4)); $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server']; $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended); @@ -3861,8 +3264,8 @@ class SSH2 return $result; //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: default: - user_error('Unable to open channel'); - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \RuntimeException('Unable to open channel'); } break; case NET_SSH2_MSG_CHANNEL_REQUEST: @@ -3872,8 +3275,8 @@ class SSH2 case NET_SSH2_MSG_CHANNEL_FAILURE: return false; default: - user_error('Unable to fulfill channel request'); - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \RuntimeException('Unable to fulfill channel request'); } case NET_SSH2_MSG_CHANNEL_CLOSE: return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended); @@ -3893,9 +3296,6 @@ class SSH2 $this->_send_channel_packet($channel, chr(0)); } */ - if (strlen($response) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($response, 4))); $data = $this->_string_shift($response, $length); @@ -3915,6 +3315,61 @@ class SSH2 } $this->channel_buffers[$channel][] = $data; break; + case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: + /* + if ($client_channel == self::CHANNEL_EXEC) { + $this->_send_channel_packet($client_channel, chr(0)); + } + */ + // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR + extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8))); + $data = $this->_string_shift($response, $length); + $this->stdErrorLog.= $data; + if ($skip_extended || $this->quiet_mode) { + break; + } + if ($client_channel == $channel) { + return $data; + } + if (!isset($this->channel_buffers[$channel])) { + $this->channel_buffers[$channel] = array(); + } + $this->channel_buffers[$channel][] = $data; + break; + case NET_SSH2_MSG_CHANNEL_REQUEST: + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $value = $this->_string_shift($response, $length); + switch ($value) { + case 'exit-signal': + $this->_string_shift($response, 1); + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length); + $this->_string_shift($response, 1); + extract(unpack('Nlength', $this->_string_shift($response, 4))); + if ($length) { + $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length); + } + + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); + + $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF; + + break; + case 'exit-status': + extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5))); + $this->exit_status = $exit_status; + + // "The client MAY ignore these messages." + // -- http://tools.ietf.org/html/rfc4254#section-6.10 + + break; + default: + // "Some systems may not implement signals, in which case they SHOULD ignore this message." + // -- http://tools.ietf.org/html/rfc4254#section-6.9 + break; + } + break; case NET_SSH2_MSG_CHANNEL_CLOSE: $this->curTimeout = 0; @@ -3926,14 +3381,12 @@ class SSH2 } $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE; - if ($client_channel == $channel) { - return true; - } + return true; case NET_SSH2_MSG_CHANNEL_EOF: break; default: - user_error('Error reading channel data'); - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + throw new \RuntimeException('Error reading channel data'); } } } @@ -3953,8 +3406,7 @@ class SSH2 { if (!is_resource($this->fsock) || feof($this->fsock)) { $this->bitmap = 0; - user_error('Connection closed prematurely'); - return false; + throw new \RuntimeException('Connection closed prematurely'); } //if ($this->compress) { @@ -4045,14 +3497,14 @@ class SSH2 @flush(); @ob_flush(); break; - // basically the same thing as self::LOG_REALTIME with the caveat that self::LOG_REALTIME_FILE + // basically the same thing as self::LOG_REALTIME with the caveat that NET_SFTP_LOG_REALTIME_FILENAME // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE. // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily // at the beginning of the file case self::LOG_REALTIME_FILE: if (!isset($this->realtime_log_file)) { // PHP doesn't seem to like using constants in fopen() - $filename = self::LOG_REALTIME_FILENAME; + $filename = NET_SSH2_LOG_REALTIME_FILENAME; $fp = fopen($filename, 'w'); $this->realtime_log_file = $fp; } @@ -4235,9 +3687,10 @@ class SSH2 switch (NET_SSH2_LOGGING) { case self::LOG_SIMPLE: return $this->message_number_log; + break; case self::LOG_COMPLEX: - $log = $this->_format_log($this->message_log, $this->message_number_log); - return PHP_SAPI == 'cli' ? $log : '
' . $log . '
'; + return $this->_format_log($this->message_log, $this->message_number_log); + break; default: return false; } @@ -4516,6 +3969,8 @@ class SSH2 * is recommended. Returns false if the server signature is not signed correctly with the public host key. * * @return mixed + * @throws \RuntimeException on badly formatted keys + * @throws \phpseclib\Exception\NoSupportedAlgorithmsException when the key isn't in a supported format * @access public */ function getServerPublicHostKey() @@ -4529,15 +3984,12 @@ class SSH2 $signature = $this->signature; $server_public_host_key = $this->server_public_host_key; - if (strlen($server_public_host_key) < 4) { - return false; - } extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4))); $this->_string_shift($server_public_host_key, $length); if ($this->signature_validated) { return $this->bitmap ? - $this->signature_format . ' ' . base64_encode($this->server_public_host_key) : + $this->signature_format . ' ' . Base64::encode($this->server_public_host_key) : false; } @@ -4547,27 +3999,15 @@ class SSH2 case 'ssh-dss': $zero = new BigInteger(); - if (strlen($server_public_host_key) < 4) { - return false; - } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $p = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); - if (strlen($server_public_host_key) < 4) { - return false; - } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $q = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); - if (strlen($server_public_host_key) < 4) { - return false; - } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $g = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); - if (strlen($server_public_host_key) < 4) { - return false; - } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $y = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); @@ -4576,8 +4016,8 @@ class SSH2 padding, unsigned, and in network byte order). */ $temp = unpack('Nlength', $this->_string_shift($signature, 4)); if ($temp['length'] != 40) { - user_error('Invalid signature'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \RuntimeException('Invalid signature'); } $r = new BigInteger($this->_string_shift($signature, 20), 256); @@ -4588,8 +4028,8 @@ class SSH2 case $r->compare($q) >= 0: case $s->equals($zero): case $s->compare($q) >= 0: - user_error('Invalid signature'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \RuntimeException('Invalid signature'); } $w = $s->modInverse($q); @@ -4608,60 +4048,33 @@ class SSH2 list(, $v) = $v->divide($q); if (!$v->equals($r)) { - user_error('Bad server signature'); + //user_error('Bad server signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); } break; case 'ssh-rsa': - case 'rsa-sha2-256': - case 'rsa-sha2-512': - if (strlen($server_public_host_key) < 4) { - return false; - } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $e = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256); - if (strlen($server_public_host_key) < 4) { - return false; - } $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4)); $rawN = $this->_string_shift($server_public_host_key, $temp['length']); $n = new BigInteger($rawN, -256); $nLength = strlen(ltrim($rawN, "\0")); /* - if (strlen($signature) < 4) { - return false; - } $temp = unpack('Nlength', $this->_string_shift($signature, 4)); $signature = $this->_string_shift($signature, $temp['length']); $rsa = new RSA(); - switch ($this->signature_format) { - case 'rsa-sha2-512': - $hash = 'sha512'; - break; - case 'rsa-sha2-256': - $hash = 'sha256'; - break; - //case 'ssh-rsa': - default: - $hash = 'sha1'; - } - $rsa->setHash($hash); - $rsa->setSignatureMode(RSA::SIGNATURE_PKCS1); - $rsa->loadKey(array('e' => $e, 'n' => $n), RSA::PUBLIC_FORMAT_RAW); - - if (!$rsa->verify($this->exchange_hash, $signature)) { - user_error('Bad server signature'); + $rsa->load(array('e' => $e, 'n' => $n), 'raw'); + $rsa->setHash('sha1'); + if (!$rsa->verify($this->exchange_hash, $signature, RSA::PADDING_PKCS1)) { + //user_error('Bad server signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); } */ - if (strlen($signature) < 4) { - return false; - } $temp = unpack('Nlength', $this->_string_shift($signature, 4)); $s = new BigInteger($this->_string_shift($signature, $temp['length']), 256); @@ -4672,50 +4085,27 @@ class SSH2 // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source. if ($s->compare(new BigInteger()) < 0 || $s->compare($n->subtract(new BigInteger(1))) > 0) { - user_error('Invalid signature'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + throw new \RuntimeException('Invalid signature'); } $s = $s->modPow($e, $n); $s = $s->toBytes(); - switch ($this->signature_format) { - case 'rsa-sha2-512': - $hash = 'sha512'; - break; - case 'rsa-sha2-256': - $hash = 'sha256'; - break; - //case 'ssh-rsa': - default: - $hash = 'sha1'; - } - $hashObj = new Hash($hash); - switch ($this->signature_format) { - case 'rsa-sha2-512': - $h = pack('N5a*', 0x00305130, 0x0D060960, 0x86480165, 0x03040203, 0x05000440, $hashObj->hash($this->exchange_hash)); - break; - case 'rsa-sha2-256': - $h = pack('N5a*', 0x00303130, 0x0D060960, 0x86480165, 0x03040201, 0x05000420, $hashObj->hash($this->exchange_hash)); - break; - //case 'ssh-rsa': - default: - $hash = 'sha1'; - $h = pack('N4a*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, $hashObj->hash($this->exchange_hash)); - } + $h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($this->exchange_hash)); $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 2 - strlen($h)) . $h; if ($s != $h) { - user_error('Bad server signature'); + //user_error('Bad server signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); } break; default: - user_error('Unsupported signature format'); - return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); + $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); + throw new NoSupportedAlgorithmsException('Unsupported signature format'); } - return $this->signature_format . ' ' . base64_encode($this->server_public_host_key); + return $this->signature_format . ' ' . Base64::encode($this->server_public_host_key); } /** @@ -4788,4 +4178,47 @@ class SSH2 $this->windowColumns = $columns; $this->windowRows = $rows; } + + /** + * @return string + */ + function __toString() + { + return $this->getResourceId(); + } + + /** + * We use {} because that symbols should not be in URL according to + * {@link http://tools.ietf.org/html/rfc3986#section-2 RFC}. + * It will safe us from any conflicts, because otherwise regexp will + * match all alphanumeric domains. + * + * @return string + */ + function getResourceId() + { + return '{' . spl_object_hash($this) . '}'; + } + + /** + * Return existing connection + * + * @param string $id + * + * @return bool|SSH2 will return false if no such connection + */ + static function getConnectionByResourceId($id) + { + return isset(self::$connections[$id]) ? self::$connections[$id] : false; + } + + /** + * Return all excising connections + * + * @return SSH2[] + */ + static function getConnections() + { + return self::$connections; + } } diff --git a/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php b/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php index 99dcecfe62..23bf027a42 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php +++ b/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php @@ -33,7 +33,9 @@ namespace phpseclib\System\SSH; +use ParagonIE\ConstantTime\Base64; use phpseclib\Crypt\RSA; +use phpseclib\Exception\BadConfigurationException; use phpseclib\System\SSH\Agent\Identity; /** @@ -43,7 +45,7 @@ use phpseclib\System\SSH\Agent\Identity; * * @package SSH\Agent * @author Jim Wigginton - * @access public + * @access internal */ class Agent { @@ -115,27 +117,26 @@ class Agent * Default Constructor * * @return \phpseclib\System\SSH\Agent + * @throws \phpseclib\Exception\BadConfigurationException if SSH_AUTH_SOCK cannot be found + * @throws \RuntimeException on connection errors * @access public */ - function __construct($address = null) + function __construct() { - if (!$address) { - switch (true) { - case isset($_SERVER['SSH_AUTH_SOCK']): - $address = $_SERVER['SSH_AUTH_SOCK']; - break; - case isset($_ENV['SSH_AUTH_SOCK']): - $address = $_ENV['SSH_AUTH_SOCK']; - break; - default: - user_error('SSH_AUTH_SOCK not found'); - return false; - } + switch (true) { + case isset($_SERVER['SSH_AUTH_SOCK']): + $address = $_SERVER['SSH_AUTH_SOCK']; + break; + case isset($_ENV['SSH_AUTH_SOCK']): + $address = $_ENV['SSH_AUTH_SOCK']; + break; + default: + throw new BadConfigurationException('SSH_AUTH_SOCK not found'); } $this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr); if (!$this->fsock) { - user_error("Unable to connect to ssh-agent (Error $errno: $errstr)"); + throw new \RuntimeException("Unable to connect to ssh-agent (Error $errno: $errstr)"); } } @@ -146,6 +147,7 @@ class Agent * Returns an array containing zero or more \phpseclib\System\SSH\Agent\Identity objects * * @return array + * @throws \RuntimeException on receipt of unexpected packets * @access public */ function requestIdentities() @@ -156,15 +158,13 @@ class Agent $packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES); if (strlen($packet) != fputs($this->fsock, $packet)) { - user_error('Connection closed while requesting identities'); - return array(); + throw new \RuntimeException('Connection closed while requesting identities'); } $length = current(unpack('N', fread($this->fsock, 4))); $type = ord(fread($this->fsock, 1)); if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) { - user_error('Unable to request identities'); - return array(); + throw new \RuntimeException('Unable to request identities'); } $identities = array(); @@ -172,7 +172,7 @@ class Agent for ($i = 0; $i < $keyCount; $i++) { $length = current(unpack('N', fread($this->fsock, 4))); $key_blob = fread($this->fsock, $length); - $key_str = 'ssh-rsa ' . base64_encode($key_blob); + $key_str = 'ssh-rsa ' . Base64::encode($key_blob); $length = current(unpack('N', fread($this->fsock, 4))); if ($length) { $key_str.= ' ' . fread($this->fsock, $length); @@ -182,7 +182,7 @@ class Agent switch ($key_type) { case 'ssh-rsa': $key = new RSA(); - $key->loadKey($key_str); + $key->load($key_str); break; case 'ssh-dss': // not currently supported @@ -278,6 +278,7 @@ class Agent * * @param string $data * @return data from SSH Agent + * @throws \RuntimeException on connection errors * @access private */ function _forward_data($data) @@ -296,7 +297,7 @@ class Agent } if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) { - user_error('Connection closed attempting to forward data to SSH agent'); + throw new \RuntimeException('Connection closed attempting to forward data to SSH agent'); } $this->socket_buffer = ''; diff --git a/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php b/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php index b4649046a0..612c414e90 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php +++ b/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php @@ -15,6 +15,8 @@ namespace phpseclib\System\SSH\Agent; +use phpseclib\Crypt\RSA; +use phpseclib\Exception\UnsupportedAlgorithmException; use phpseclib\System\SSH\Agent; /** @@ -32,17 +34,6 @@ use phpseclib\System\SSH\Agent; */ class Identity { - /**@+ - * Signature Flags - * - * See https://tools.ietf.org/html/draft-miller-ssh-agent-00#section-5.3 - * - * @access private - */ - const SSH_AGENT_RSA2_256 = 2; - const SSH_AGENT_RSA2_512 = 4; - /**#@-*/ - /** * Key Object * @@ -70,16 +61,6 @@ class Identity */ var $fsock; - /** - * Signature flags - * - * @var int - * @access private - * @see self::sign() - * @see self::setHash() - */ - var $flags = 0; - /** * Default Constructor. * @@ -125,50 +106,28 @@ class Identity * * Wrapper for $this->key->getPublicKey() * - * @param int $format optional + * @param int $type optional * @return mixed * @access public */ - function getPublicKey($format = null) + function getPublicKey($type = 'PKCS8') { - return !isset($format) ? $this->key->getPublicKey() : $this->key->getPublicKey($format); + return $this->key->getPublicKey($type); } /** - * Set Signature Mode + * Sets the hash * - * Doesn't do anything as ssh-agent doesn't let you pick and choose the signature mode. ie. - * ssh-agent's only supported mode is \phpseclib\Crypt\RSA::SIGNATURE_PKCS1 + * ssh-agent only supports signatures with sha1 hashes but to maintain BC with RSA.php this function exists * - * @param int $mode + * @param string $hash optional + * @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported * @access public */ - function setSignatureMode($mode) + function setHash($hash = 'sha1') { - } - - /** - * Set Hash - * - * ssh-agent doesn't support using hashes for RSA other than SHA1 - * - * @param string $hash - * @access public - */ - function setHash($hash) - { - $this->flags = 0; - switch ($hash) { - case 'sha1': - break; - case 'sha256': - $this->flags = self::SSH_AGENT_RSA2_256; - break; - case 'sha512': - $this->flags = self::SSH_AGENT_RSA2_512; - break; - default: - user_error('The only supported hashes for RSA are sha1, sha256 and sha512'); + if ($hash != 'sha1') { + throw new UnsupportedAlgorithmException('ssh-agent can only be used with the sha1 hash'); } } @@ -178,53 +137,34 @@ class Identity * See "2.6.2 Protocol 2 private key signature request" * * @param string $message + * @param int $padding optional * @return string + * @throws \RuntimeException on connection errors + * @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported * @access public */ - function sign($message) + function sign($message, $padding = RSA::PADDING_PKCS1) { + if ($padding != RSA::PADDING_PKCS1 && $padding != RSA::PADDING_RELAXED_PKCS1) { + throw new UnsupportedAlgorithmException('ssh-agent can only create PKCS1 signatures'); + } + // the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE - $packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, $this->flags); + $packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, 0); $packet = pack('Na*', strlen($packet), $packet); if (strlen($packet) != fputs($this->fsock, $packet)) { - user_error('Connection closed during signing'); + throw new \RuntimeException('Connection closed during signing'); } $length = current(unpack('N', fread($this->fsock, 4))); $type = ord(fread($this->fsock, 1)); if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) { - user_error('Unable to retrieve signature'); + throw new \RuntimeException('Unable to retreive signature'); } $signature_blob = fread($this->fsock, $length - 1); - $length = current(unpack('N', $this->_string_shift($signature_blob, 4))); - if ($length != strlen($signature_blob)) { - user_error('Malformed signature blob'); - } - $length = current(unpack('N', $this->_string_shift($signature_blob, 4))); - if ($length > strlen($signature_blob) + 4) { - user_error('Malformed signature blob'); - } - $type = $this->_string_shift($signature_blob, $length); - $this->_string_shift($signature_blob, 4); - - return $signature_blob; - } - - /** - * String Shift - * - * Inspired by array_shift - * - * @param string $string - * @param int $index - * @return string - * @access private - */ - function _string_shift(&$string, $index = 1) - { - $substr = substr($string, 0, $index); - $string = substr($string, $index); - return $substr; + // the only other signature format defined - ssh-dss - is the same length as ssh-rsa + // the + 12 is for the other various SSH added length fields + return substr($signature_blob, strlen('ssh-rsa') + 12); } } diff --git a/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php b/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php index 0da0999fd2..bd4ba0b5a5 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php +++ b/vendor/phpseclib/phpseclib/phpseclib/bootstrap.php @@ -1,14 +1,18 @@