From fcd5846c5ab6d15bfa760c13f0f4f74ae010be2b Mon Sep 17 00:00:00 2001 From: Llewellyn van der Merwe Date: Wed, 17 Jul 2019 01:53:34 +0200 Subject: [PATCH] Update the phpseclib via composer. --- README.md | 2 +- admin/README.txt | 2 +- admin/models/forms/admin_view.xml | 2 +- admin/models/server.php | 16 +- componentbuilder.xml | 2 +- .../vdm_io/vendor/composer/ClassLoader.php | 4 +- .../vendor/composer/autoload_classmap.php | 24 -- .../vendor/composer/autoload_static.php | 28 --- .../vdm_io/vendor/composer/installed.json | 12 +- .../vdm_io/vendor/phpseclib/phpseclib/LICENSE | 5 +- .../vendor/phpseclib/phpseclib/README.md | 19 +- .../vendor/phpseclib/phpseclib/appveyor.yml | 27 ++ .../phpseclib/phpseclib/Crypt/Base.php | 128 +++++++++- .../phpseclib/phpseclib/Crypt/Hash.php | 121 ++++++--- .../phpseclib/phpseclib/Crypt/RSA.php | 206 +++++++++++++--- .../phpseclib/phpseclib/File/ASN1.php | 164 +++++++++---- .../phpseclib/phpseclib/File/X509.php | 207 +++++++++++++++- .../phpseclib/phpseclib/Math/BigInteger.php | 54 ++-- .../phpseclib/phpseclib/phpseclib/Net/SCP.php | 5 + .../phpseclib/phpseclib/Net/SFTP.php | 80 ++++-- .../phpseclib/phpseclib/Net/SSH2.php | 232 +++++++++++++++--- .../phpseclib/phpseclib/System/SSH/Agent.php | 28 ++- .../phpseclib/System/SSH/Agent/Identity.php | 80 +++++- 23 files changed, 1174 insertions(+), 274 deletions(-) create mode 100644 libraries/vdm_io/vendor/phpseclib/phpseclib/appveyor.yml diff --git a/README.md b/README.md index ef5584734..cc9ec7981 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ TODO + *Author*: [Llewellyn van der Merwe](mailto:llewellyn@joomlacomponentbuilder.com) + *Name*: [Component Builder](https://github.com/vdm-io/Joomla-Component-Builder) + *First Build*: 30th April, 2015 -+ *Last Build*: 16th July, 2019 ++ *Last Build*: 17th July, 2019 + *Version*: 2.9.32 + *Copyright*: Copyright (C) 2015 - 2019 Vast Development Method. All rights reserved. + *License*: GNU General Public License version 2 or later; see LICENSE.txt diff --git a/admin/README.txt b/admin/README.txt index ef5584734..cc9ec7981 100644 --- a/admin/README.txt +++ b/admin/README.txt @@ -146,7 +146,7 @@ TODO + *Author*: [Llewellyn van der Merwe](mailto:llewellyn@joomlacomponentbuilder.com) + *Name*: [Component Builder](https://github.com/vdm-io/Joomla-Component-Builder) + *First Build*: 30th April, 2015 -+ *Last Build*: 16th July, 2019 ++ *Last Build*: 17th July, 2019 + *Version*: 2.9.32 + *Copyright*: Copyright (C) 2015 - 2019 Vast Development Method. All rights reserved. + *License*: GNU General Public License version 2 or later; see LICENSE.txt diff --git a/admin/models/forms/admin_view.xml b/admin/models/forms/admin_view.xml index 4b9621508..41b154bc2 100644 --- a/admin/models/forms/admin_view.xml +++ b/admin/models/forms/admin_view.xml @@ -1094,7 +1094,7 @@ readonly="false" disabled="false" required="true" - filter="WORD" + filter="STRING" message="COM_COMPONENTBUILDER_ADMIN_VIEW_KEY_MESSAGE" hint="COM_COMPONENTBUILDER_ADMIN_VIEW_KEY_HINT" /> diff --git a/admin/models/server.php b/admin/models/server.php index fe78c987f..4438ef7bc 100644 --- a/admin/models/server.php +++ b/admin/models/server.php @@ -179,7 +179,7 @@ class ComponentbuilderModelServer extends JModelAdmin $item->tags->getTagIds($item->id, 'com_componentbuilder.server'); } } - $this->sales_serverorupdate_servervvvw = $item->id; + $this->sales_serverupdate_servervvvw = $item->id; return $item; } @@ -203,19 +203,19 @@ class ComponentbuilderModelServer extends JModelAdmin // From the componentbuilder_joomla_component table $query->from($db->quoteName('#__componentbuilder_joomla_component', 'a')); - // Filter by sales_serverorupdate_servervvvw global. - $sales_serverorupdate_servervvvw = $this->sales_serverorupdate_servervvvw; - if (is_numeric($sales_serverorupdate_servervvvw )) + // Filter by sales_serverupdate_servervvvw global. + $sales_serverupdate_servervvvw = $this->sales_serverupdate_servervvvw; + if (is_numeric($sales_serverupdate_servervvvw )) { - $query->where('a.sales_serverORupdate_server = ' . (int) $sales_serverorupdate_servervvvw ); + $query->where('a.sales_server = ' . (int) $sales_serverupdate_servervvvw . ' OR a.update_server = ' . (int) $sales_serverupdate_servervvvw, ' OR'); } - elseif (is_string($sales_serverorupdate_servervvvw)) + elseif (is_string($sales_serverupdate_servervvvw)) { - $query->where('a.sales_serverORupdate_server = ' . $db->quote($sales_serverorupdate_servervvvw)); + $query->where('a.sales_server = ' . $db->quote($sales_serverupdate_servervvvw) . ' OR a.update_server = ' . $db->quote($sales_serverupdate_servervvvw), ' OR'); } else { - $query->where('a.sales_serverORupdate_server = -5'); + $query->where('a.update_server = -5'); } // Join over the asset groups. diff --git a/componentbuilder.xml b/componentbuilder.xml index 8cbec8119..e36b8ef4c 100644 --- a/componentbuilder.xml +++ b/componentbuilder.xml @@ -1,7 +1,7 @@ COM_COMPONENTBUILDER - 16th July, 2019 + 17th July, 2019 Llewellyn van der Merwe llewellyn@joomlacomponentbuilder.com http://www.joomlacomponentbuilder.com diff --git a/libraries/vdm_io/vendor/composer/ClassLoader.php b/libraries/vdm_io/vendor/composer/ClassLoader.php index dc02dfb11..fce8549f0 100644 --- a/libraries/vdm_io/vendor/composer/ClassLoader.php +++ b/libraries/vdm_io/vendor/composer/ClassLoader.php @@ -279,7 +279,7 @@ class ClassLoader */ public function setApcuPrefix($apcuPrefix) { - $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; } /** @@ -377,7 +377,7 @@ class ClassLoader $subPath = $class; while (false !== $lastPos = strrpos($subPath, '\\')) { $subPath = substr($subPath, 0, $lastPos); - $search = $subPath.'\\'; + $search = $subPath . '\\'; if (isset($this->prefixDirsPsr4[$search])) { $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); foreach ($this->prefixDirsPsr4[$search] as $dir) { diff --git a/libraries/vdm_io/vendor/composer/autoload_classmap.php b/libraries/vdm_io/vendor/composer/autoload_classmap.php index 2f4700ebd..7a91153b0 100644 --- a/libraries/vdm_io/vendor/composer/autoload_classmap.php +++ b/libraries/vdm_io/vendor/composer/autoload_classmap.php @@ -6,28 +6,4 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( - 'phpseclib\\Crypt\\AES' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/AES.php', - 'phpseclib\\Crypt\\Base' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Base.php', - 'phpseclib\\Crypt\\Blowfish' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php', - 'phpseclib\\Crypt\\DES' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/DES.php', - 'phpseclib\\Crypt\\Hash' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Hash.php', - '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\\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\\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', - 'phpseclib\\File\\X509' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/X509.php', - 'phpseclib\\Math\\BigInteger' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Math/BigInteger.php', - 'phpseclib\\Net\\SCP' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SCP.php', - 'phpseclib\\Net\\SFTP' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SFTP.php', - 'phpseclib\\Net\\SFTP\\Stream' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php', - 'phpseclib\\Net\\SSH1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SSH1.php', - 'phpseclib\\Net\\SSH2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Net/SSH2.php', - 'phpseclib\\System\\SSH\\Agent' => $vendorDir . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php', - 'phpseclib\\System\\SSH\\Agent\\Identity' => $vendorDir . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php', ); diff --git a/libraries/vdm_io/vendor/composer/autoload_static.php b/libraries/vdm_io/vendor/composer/autoload_static.php index ea6b0d7d3..09b862a0c 100644 --- a/libraries/vdm_io/vendor/composer/autoload_static.php +++ b/libraries/vdm_io/vendor/composer/autoload_static.php @@ -24,39 +24,11 @@ class ComposerStaticInit10d22a526bd476954b93748a871e7ad4 ), ); - public static $classMap = array ( - 'phpseclib\\Crypt\\AES' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/AES.php', - 'phpseclib\\Crypt\\Base' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Base.php', - 'phpseclib\\Crypt\\Blowfish' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php', - 'phpseclib\\Crypt\\DES' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/DES.php', - 'phpseclib\\Crypt\\Hash' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Hash.php', - '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\\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\\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', - 'phpseclib\\File\\X509' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/X509.php', - 'phpseclib\\Math\\BigInteger' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Math/BigInteger.php', - 'phpseclib\\Net\\SCP' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SCP.php', - 'phpseclib\\Net\\SFTP' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SFTP.php', - 'phpseclib\\Net\\SFTP\\Stream' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php', - 'phpseclib\\Net\\SSH1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SSH1.php', - 'phpseclib\\Net\\SSH2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Net/SSH2.php', - 'phpseclib\\System\\SSH\\Agent' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php', - 'phpseclib\\System\\SSH\\Agent\\Identity' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php', - ); - public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { $loader->prefixLengthsPsr4 = ComposerStaticInit10d22a526bd476954b93748a871e7ad4::$prefixLengthsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInit10d22a526bd476954b93748a871e7ad4::$prefixDirsPsr4; - $loader->classMap = ComposerStaticInit10d22a526bd476954b93748a871e7ad4::$classMap; }, null, ClassLoader::class); } diff --git a/libraries/vdm_io/vendor/composer/installed.json b/libraries/vdm_io/vendor/composer/installed.json index 4ce92974f..d7857e90e 100644 --- a/libraries/vdm_io/vendor/composer/installed.json +++ b/libraries/vdm_io/vendor/composer/installed.json @@ -1,17 +1,17 @@ [ { "name": "phpseclib/phpseclib", - "version": "2.0.10", - "version_normalized": "2.0.10.0", + "version": "2.0.21", + "version_normalized": "2.0.21.0", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "d305b780829ea4252ed9400b3f5937c2c99b51d4" + "reference": "9f1287e68b3f283339a9f98f67515dd619e5bf9d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/d305b780829ea4252ed9400b3f5937c2c99b51d4", - "reference": "d305b780829ea4252ed9400b3f5937c2c99b51d4", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/9f1287e68b3f283339a9f98f67515dd619e5bf9d", + "reference": "9f1287e68b3f283339a9f98f67515dd619e5bf9d", "shasum": "" }, "require": { @@ -29,7 +29,7 @@ "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." }, - "time": "2018-02-19T04:29:13+00:00", + "time": "2019-07-12T12:53:49+00:00", "type": "library", "installation-source": "dist", "autoload": { diff --git a/libraries/vdm_io/vendor/phpseclib/phpseclib/LICENSE b/libraries/vdm_io/vendor/phpseclib/phpseclib/LICENSE index a8ec8ebd4..e7214ebbe 100644 --- a/libraries/vdm_io/vendor/phpseclib/phpseclib/LICENSE +++ b/libraries/vdm_io/vendor/phpseclib/phpseclib/LICENSE @@ -1,5 +1,4 @@ -Copyright 2007-2016 TerraFrost and other contributors -http://phpseclib.sourceforge.net/ +Copyright (c) 2011-2019 TerraFrost and other contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -18,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/libraries/vdm_io/vendor/phpseclib/phpseclib/README.md b/libraries/vdm_io/vendor/phpseclib/phpseclib/README.md index 94402bee0..a9e7ebdab 100644 --- a/libraries/vdm_io/vendor/phpseclib/phpseclib/README.md +++ b/libraries/vdm_io/vendor/phpseclib/phpseclib/README.md @@ -2,6 +2,14 @@ [![Build Status](https://travis-ci.org/phpseclib/phpseclib.svg?branch=2.0)](https://travis-ci.org/phpseclib/phpseclib) +## Supporting phpseclib + +- [Become a backer or sponsor on Patreon](https://www.patreon.com/phpseclib) +- [One-time donation via PayPal or crypto-currencies](http://sourceforge.net/donate/index.php?group_id=198487) +- [Subscribe to Tidelift](https://tidelift.com/subscription/pkg/packagist-phpseclib-phpseclib?utm_source=packagist-phpseclib-phpseclib&utm_medium=referral&utm_campaign=readme) + +## Introduction + MIT-licensed pure-PHP implementations of 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 @@ -24,19 +32,24 @@ AES, Blowfish, Twofish, SSH-1, SSH-2, SFTP, and X.509 ### 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` +* 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 Composer: `composer require phpseclib/phpseclib:~1.0` * Install using PEAR: See [phpseclib PEAR Channel Documentation](http://phpseclib.sourceforge.net/pear.htm) -* [Download 1.0.10 as ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.10.zip/download) +* [Download 1.0.16 as ZIP](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.16.zip/download) + +## Security contact information + +To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. ## Support diff --git a/libraries/vdm_io/vendor/phpseclib/phpseclib/appveyor.yml b/libraries/vdm_io/vendor/phpseclib/phpseclib/appveyor.yml new file mode 100644 index 000000000..210a90347 --- /dev/null +++ b/libraries/vdm_io/vendor/phpseclib/phpseclib/appveyor.yml @@ -0,0 +1,27 @@ +build: false +shallow_clone: false +platform: + - x86 + - x64 +clone_folder: C:\projects\phpseclib + +install: + - cinst -y OpenSSL.Light + - SET PATH=C:\Program Files\OpenSSL;%PATH% + - sc config wuauserv start= auto + - net start wuauserv + - cinst -y php --version 5.6.30 + - cd c:\tools\php56 + - copy php.ini-production php.ini + - echo date.timezone="UTC" >> php.ini + - echo extension_dir=ext >> php.ini + - echo extension=php_openssl.dll >> php.ini + - echo extension=php_gmp.dll >> php.ini + - cd C:\projects\phpseclib + - SET PATH=C:\tools\php56;%PATH% + - php.exe -r "readfile('http://getcomposer.org/installer');" | php.exe + - php.exe composer.phar install --prefer-source --no-interaction + +test_script: + - cd C:\projects\phpseclib + - vendor\bin\phpunit.bat tests/Windows32Test.php \ No newline at end of file diff --git a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php index 54a8997ed..03b176e7b 100644 --- a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php +++ b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php @@ -76,6 +76,10 @@ abstract class Base * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 */ const MODE_CFB = 3; + /** + * Encrypt / decrypt using the Cipher Feedback mode (8bit) + */ + const MODE_CFB8 = 38; /** * Encrypt / decrypt using the Output Feedback mode. * @@ -479,6 +483,7 @@ abstract class Base break; case self::MODE_CTR: case self::MODE_CFB: + case self::MODE_CFB8: case self::MODE_OFB: case self::MODE_STREAM: $this->mode = $mode; @@ -644,10 +649,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); @@ -702,7 +707,7 @@ abstract class Base case self::MODE_STREAM: return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); case self::MODE_ECB: - $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); + $result = @openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); return !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result; case self::MODE_CBC: $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV); @@ -762,6 +767,16 @@ abstract class Base $iv = substr($ciphertext, -$this->block_size); } + return $ciphertext; + case self::MODE_CFB8: + $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV); + if ($this->continuousBuffer) { + if (($len = strlen($ciphertext)) >= $this->block_size) { + $this->encryptIV = substr($ciphertext, -$this->block_size); + } else { + $this->encryptIV = substr($this->encryptIV, $len - $this->block_size) . substr($ciphertext, -$len); + } + } return $ciphertext; case self::MODE_OFB: return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer); @@ -942,6 +957,24 @@ abstract class Base $pos = $len; } break; + case self::MODE_CFB8: + $ciphertext = ''; + $len = strlen($plaintext); + $iv = $this->encryptIV; + + for ($i = 0; $i < $len; ++$i) { + $ciphertext .= ($c = $plaintext[$i] ^ $this->_encryptBlock($iv)); + $iv = substr($iv, 1) . $c; + } + + if ($this->continuousBuffer) { + if ($len >= $block_size) { + $this->encryptIV = substr($ciphertext, -$block_size); + } else { + $this->encryptIV = substr($this->encryptIV, $len - $block_size) . substr($ciphertext, -$len); + } + } + break; case self::MODE_OFB: $xor = $this->encryptIV; if (strlen($buffer['xor'])) { @@ -1007,14 +1040,14 @@ abstract class Base break; case self::MODE_ECB: if (!defined('OPENSSL_RAW_DATA')) { - $ciphertext.= openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true); + $ciphertext.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true); } $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options); break; case self::MODE_CBC: if (!defined('OPENSSL_RAW_DATA')) { $padding = str_repeat(chr($this->block_size), $this->block_size) ^ substr($ciphertext, -$this->block_size); - $ciphertext.= substr(openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size); + $ciphertext.= substr(@openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size); $offset = 2 * $this->block_size; } else { $offset = $this->block_size; @@ -1072,6 +1105,16 @@ abstract class Base $iv = substr($ciphertext, -$this->block_size); } break; + case self::MODE_CFB8: + $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->decryptIV); + if ($this->continuousBuffer) { + if (($len = strlen($ciphertext)) >= $this->block_size) { + $this->decryptIV = substr($ciphertext, -$this->block_size); + } else { + $this->decryptIV = substr($this->decryptIV, $len - $this->block_size) . substr($ciphertext, -$len); + } + } + break; case self::MODE_OFB: $plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer); } @@ -1235,6 +1278,24 @@ abstract class Base $pos = $len; } break; + case self::MODE_CFB8: + $plaintext = ''; + $len = strlen($ciphertext); + $iv = $this->decryptIV; + + for ($i = 0; $i < $len; ++$i) { + $plaintext .= $ciphertext[$i] ^ $this->_encryptBlock($iv); + $iv = substr($iv, 1) . $ciphertext[$i]; + } + + if ($this->continuousBuffer) { + if ($len >= $block_size) { + $this->decryptIV = substr($ciphertext, -$block_size); + } else { + $this->decryptIV = substr($this->decryptIV, $len - $block_size) . substr($ciphertext, -$len); + } + } + break; case self::MODE_OFB: $xor = $this->decryptIV; if (strlen($buffer['xor'])) { @@ -1297,7 +1358,7 @@ abstract class Base for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); if (strlen($block) > strlen($buffer['ciphertext'])) { - $result = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); + $result = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); $result = !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result; $buffer['ciphertext'].= $result; } @@ -1308,7 +1369,7 @@ abstract class Base } else { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { $block = substr($plaintext, $i, $block_size); - $otp = openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); + $otp = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); $otp = !defined('OPENSSL_RAW_DATA') ? substr($otp, 0, -$this->block_size) : $otp; $this->_increment_str($xor); $ciphertext.= $block ^ $otp; @@ -1352,7 +1413,7 @@ abstract class Base } if ($this->continuousBuffer) { if (!defined('OPENSSL_RAW_DATA')) { - $encryptIV.= openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options); + $encryptIV.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options); } $encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, $this->openssl_options); if ($overflow) { @@ -1435,6 +1496,8 @@ abstract class Base return 'ctr'; case self::MODE_CFB: return 'cfb'; + case self::MODE_CFB8: + return 'cfb8'; case self::MODE_OFB: return 'ofb'; } @@ -1788,6 +1851,7 @@ 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, ); @@ -2359,6 +2423,52 @@ abstract class Base $_pos = $_len; } + return $_plaintext; + '; + break; + case self::MODE_CFB8: + $encrypt = $init_encrypt . ' + $_ciphertext = ""; + $_len = strlen($_text); + $_iv = $self->encryptIV; + + for ($_i = 0; $_i < $_len; ++$_i) { + $in = $_iv; + '.$encrypt_block.' + $_ciphertext .= ($_c = $_text[$_i] ^ $in); + $_iv = substr($_iv, 1) . $_c; + } + + if ($self->continuousBuffer) { + if ($_len >= '.$block_size.') { + $self->encryptIV = substr($_ciphertext, -'.$block_size.'); + } else { + $self->encryptIV = substr($self->encryptIV, $_len - '.$block_size.') . substr($_ciphertext, -$_len); + } + } + + return $_ciphertext; + '; + $decrypt = $init_encrypt . ' + $_plaintext = ""; + $_len = strlen($_text); + $_iv = $self->decryptIV; + + for ($_i = 0; $_i < $_len; ++$_i) { + $in = $_iv; + '.$encrypt_block.' + $_plaintext .= $_text[$_i] ^ $in; + $_iv = substr($_iv, 1) . $_text[$_i]; + } + + if ($self->continuousBuffer) { + if ($_len >= '.$block_size.') { + $self->decryptIV = substr($_text, -'.$block_size.'); + } else { + $self->decryptIV = substr($self->decryptIV, $_len - '.$block_size.') . substr($_text, -$_len); + } + } + return $_plaintext; '; break; diff --git a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php index 6ae01329e..a61668209 100644 --- a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php +++ b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php @@ -112,6 +112,15 @@ class Hash */ var $key = false; + /** + * Computed Key + * + * @see self::_computeKey() + * @var string + * @access private + */ + var $computedKey = false; + /** * Outer XOR (Internal HMAC) * @@ -130,6 +139,15 @@ class Hash */ var $ipad; + /** + * Engine + * + * @see self::setHash() + * @var string + * @access private + */ + var $engine; + /** * Default Constructor. * @@ -166,6 +184,43 @@ class Hash function setKey($key = false) { $this->key = $key; + $this->_computeKey(); + } + + /** + * Pre-compute the key used by the HMAC + * + * Quoting http://tools.ietf.org/html/rfc2104#section-2, "Applications that use keys longer than B bytes + * will first hash the key using H and then use the resultant L byte string as the actual key to HMAC." + * + * As documented in https://www.reddit.com/r/PHP/comments/9nct2l/symfonypolyfill_hash_pbkdf2_correct_fix_for/ + * when doing an HMAC multiple times it's faster to compute the hash once instead of computing it during + * every call + * + * @access private + */ + function _computeKey() + { + if ($this->key === false) { + $this->computedKey = false; + return; + } + + if (strlen($this->key) <= $this->b) { + $this->computedKey = $this->key; + return; + } + + switch ($this->engine) { + case self::MODE_MHASH: + $this->computedKey = mhash($this->hash, $this->key); + break; + case self::MODE_HASH: + $this->computedKey = hash($this->hash, $this->key, true); + break; + case self::MODE_INTERNAL: + $this->computedKey = call_user_func($this->hash, $this->key); + } } /** @@ -216,19 +271,38 @@ class Hash } switch ($hash) { + case 'md2-96': case 'md2': - $mode = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ? + $this->b = 16; + case 'md5-96': + case 'sha1-96': + case 'sha224-96': + case 'sha256-96': + case 'md2': + case 'md5': + case 'sha1': + case 'sha224': + case 'sha256': + $this->b = 64; + break; + default: + $this->b = 128; + } + + switch ($hash) { + case 'md2': + $this->engine = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ? self::MODE_HASH : self::MODE_INTERNAL; break; case 'sha384': case 'sha512': - $mode = CRYPT_HASH_MODE == self::MODE_MHASH ? self::MODE_INTERNAL : CRYPT_HASH_MODE; + $this->engine = CRYPT_HASH_MODE == self::MODE_MHASH ? self::MODE_INTERNAL : CRYPT_HASH_MODE; break; default: - $mode = CRYPT_HASH_MODE; + $this->engine = CRYPT_HASH_MODE; } - switch ($mode) { + switch ($this->engine) { case self::MODE_MHASH: switch ($hash) { case 'md5': @@ -241,6 +315,7 @@ class Hash default: $this->hash = MHASH_SHA1; } + $this->_computeKey(self::MODE_MHASH); return; case self::MODE_HASH: switch ($hash) { @@ -257,35 +332,33 @@ class Hash default: $this->hash = 'sha1'; } + $this->_computeKey(self::MODE_HASH); return; } switch ($hash) { case 'md2': - $this->b = 16; $this->hash = array($this, '_md2'); break; case 'md5': - $this->b = 64; $this->hash = array($this, '_md5'); break; case 'sha256': - $this->b = 64; $this->hash = array($this, '_sha256'); break; case 'sha384': case 'sha512': - $this->b = 128; $this->hash = array($this, '_sha512'); break; case 'sha1': default: - $this->b = 64; $this->hash = array($this, '_sha1'); } $this->ipad = str_repeat(chr(0x36), $this->b); $this->opad = str_repeat(chr(0x5C), $this->b); + + $this->_computeKey(self::MODE_INTERNAL); } /** @@ -297,33 +370,25 @@ class Hash */ function hash($text) { - $mode = is_array($this->hash) ? self::MODE_INTERNAL : CRYPT_HASH_MODE; - if (!empty($this->key) || is_string($this->key)) { - switch ($mode) { + switch ($this->engine) { case self::MODE_MHASH: - $output = mhash($this->hash, $text, $this->key); + $output = mhash($this->hash, $text, $this->computedKey); break; case self::MODE_HASH: - $output = hash_hmac($this->hash, $text, $this->key, true); + $output = hash_hmac($this->hash, $text, $this->computedKey, true); break; case self::MODE_INTERNAL: - /* "Applications that use keys longer than B bytes will first hash the key using H and then use the - resultant L byte string as the actual key to HMAC." - - -- http://tools.ietf.org/html/rfc2104#section-2 */ - $key = strlen($this->key) > $this->b ? call_user_func($this->hash, $this->key) : $this->key; - - $key = str_pad($key, $this->b, chr(0)); // step 1 - $temp = $this->ipad ^ $key; // step 2 - $temp .= $text; // step 3 - $temp = call_user_func($this->hash, $temp); // step 4 - $output = $this->opad ^ $key; // step 5 - $output.= $temp; // step 6 - $output = call_user_func($this->hash, $output); // step 7 + $key = str_pad($this->computedKey, $this->b, chr(0)); // step 1 + $temp = $this->ipad ^ $key; // step 2 + $temp .= $text; // step 3 + $temp = call_user_func($this->hash, $temp); // step 4 + $output = $this->opad ^ $key; // step 5 + $output.= $temp; // step 6 + $output = call_user_func($this->hash, $output); // step 7 } } else { - switch ($mode) { + switch ($this->engine) { case self::MODE_MHASH: $output = mhash($this->hash, $text); break; diff --git a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php index cd116b56d..d2c6c7cbe 100644 --- a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php +++ b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php @@ -182,6 +182,10 @@ class RSA * PKCS#8 formatted private key */ const PRIVATE_FORMAT_PKCS8 = 8; + /** + * OpenSSH formatted private key + */ + const PRIVATE_FORMAT_OPENSSH = 9; /**#@-*/ /**#@+ @@ -468,23 +472,27 @@ class RSA break; case extension_loaded('openssl') && file_exists($this->configFile): // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work - ob_start(); - @phpinfo(); - $content = ob_get_contents(); - ob_end_clean(); - - preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); - $versions = array(); - if (!empty($matches[1])) { - for ($i = 0; $i < count($matches[1]); $i++) { - $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); - // Remove letter part in OpenSSL version - if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) { - $versions[$matches[1][$i]] = $fullVersion; - } else { - $versions[$matches[1][$i]] = $m[0]; + // avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems) + if (strpos(ini_get('disable_functions'), 'phpinfo') === false) { + ob_start(); + @phpinfo(); + $content = ob_get_contents(); + ob_end_clean(); + + preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); + + if (!empty($matches[1])) { + for ($i = 0; $i < count($matches[1]); $i++) { + $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); + + // Remove letter part in OpenSSL version + if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) { + $versions[$matches[1][$i]] = $fullVersion; + } else { + $versions[$matches[1][$i]] = $m[0]; + } } } } @@ -816,6 +824,58 @@ class RSA $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n"; return $key; + case self::PRIVATE_FORMAT_OPENSSH: + if ($num_primes != 2) { + return false; + } + $publicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']); + $privateKey = pack( + 'Na*Na*Na*Na*Na*Na*Na*', + strlen('ssh-rsa'), + 'ssh-rsa', + strlen($raw['modulus']), + $raw['modulus'], + strlen($raw['publicExponent']), + $raw['publicExponent'], + strlen($raw['privateExponent']), + $raw['privateExponent'], + strlen($raw['coefficient']), + $raw['coefficient'], + strlen($raw['prime1']), + $raw['prime1'], + strlen($raw['prime2']), + $raw['prime2'] + ); + $checkint = Random::string(4); + $paddedKey = pack( + 'a*Na*', + $checkint . $checkint . $privateKey, + strlen($this->comment), + $this->comment + ); + $paddingLength = (7 * strlen($paddedKey)) % 8; + for ($i = 1; $i <= $paddingLength; $i++) { + $paddedKey.= chr($i); + } + $key = pack( + 'Na*Na*Na*NNa*Na*', + strlen('none'), + 'none', + strlen('none'), + 'none', + 0, + '', + 1, + strlen($publicKey), + $publicKey, + strlen($paddedKey), + $paddedKey + ); + $key = "openssh-key-v1\0$key"; + + return "-----BEGIN OPENSSH PRIVATE KEY-----\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) { @@ -1016,9 +1076,9 @@ class RSA * @access private * @see self::_convertPublicKey() * @see self::_convertPrivateKey() - * @param string $key + * @param string|array $key * @param int $type - * @return array + * @return array|bool */ function _parseKey($key, $type) { @@ -1329,9 +1389,14 @@ class RSA 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: @@ -1403,6 +1468,75 @@ class RSA } $components['coefficients'] = array(2 => new BigInteger($this->_string_shift($private, $length), -256)); + return $components; + case self::PRIVATE_FORMAT_OPENSSH: + $components = array(); + $decoded = $this->_extractBER($key); + $magic = $this->_string_shift($decoded, 15); + if ($magic !== "openssh-key-v1\0") { + return false; + } + $options = $this->_string_shift($decoded, 24); + // \0\0\0\4none = ciphername + // \0\0\0\4none = kdfname + // \0\0\0\0 = kdfoptions + // \0\0\0\1 = numkeys + if ($options != "\0\0\0\4none\0\0\0\4none\0\0\0\0\0\0\0\1") { + return false; + } + extract(unpack('Nlength', $this->_string_shift($decoded, 4))); + if (strlen($decoded) < $length) { + return false; + } + $publicKey = $this->_string_shift($decoded, $length); + extract(unpack('Nlength', $this->_string_shift($decoded, 4))); + if (strlen($decoded) < $length) { + return false; + } + $paddedKey = $this->_string_shift($decoded, $length); + + if ($this->_string_shift($publicKey, 11) !== "\0\0\0\7ssh-rsa") { + return false; + } + + $checkint1 = $this->_string_shift($paddedKey, 4); + $checkint2 = $this->_string_shift($paddedKey, 4); + if (strlen($checkint1) != 4 || $checkint1 !== $checkint2) { + return false; + } + + if ($this->_string_shift($paddedKey, 11) !== "\0\0\0\7ssh-rsa") { + return false; + } + + $values = array( + &$components['modulus'], + &$components['publicExponent'], + &$components['privateExponent'], + &$components['coefficients'][2], + &$components['primes'][1], + &$components['primes'][2] + ); + + foreach ($values as &$value) { + extract(unpack('Nlength', $this->_string_shift($paddedKey, 4))); + if (strlen($paddedKey) < $length) { + return false; + } + $value = new BigInteger($this->_string_shift($paddedKey, $length), -256); + } + + extract(unpack('Nlength', $this->_string_shift($paddedKey, 4))); + if (strlen($paddedKey) < $length) { + return false; + } + $components['comment'] = $this->_string_shift($decoded, $length); + + $temp = $components['primes'][1]->subtract($this->one); + $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp)); + $temp = $components['primes'][2]->subtract($this->one); + $components['exponents'][] = $components['publicExponent']->modInverse($temp); + return $components; } } @@ -1501,8 +1635,9 @@ class RSA * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed) * * @access public - * @param string $key - * @param int $type optional + * @param string|RSA|array $key + * @param bool|int $type optional + * @return bool */ function loadKey($key, $type = false) { @@ -1559,7 +1694,8 @@ class RSA self::PRIVATE_FORMAT_PKCS1, self::PRIVATE_FORMAT_XML, self::PRIVATE_FORMAT_PUTTY, - self::PUBLIC_FORMAT_OPENSSH + self::PUBLIC_FORMAT_OPENSSH, + self::PRIVATE_FORMAT_OPENSSH ); foreach ($types as $type) { $components = $this->_parseKey($key, $type); @@ -2207,16 +2343,21 @@ class RSA */ function _equals($x, $y) { + if (function_exists('hash_equals')) { + return hash_equals($x, $y); + } + if (strlen($x) != strlen($y)) { return false; } - $result = 0; + $result = "\0"; + $x^= $y; for ($i = 0; $i < strlen($x); $i++) { - $result |= ord($x[$i]) ^ ord($y[$i]); + $result|= $x[$i]; } - return $result == 0; + return $result === "\0"; } /** @@ -2423,19 +2564,26 @@ class RSA $db = $maskedDB ^ $dbMask; $lHash2 = substr($db, 0, $this->hLen); $m = substr($db, $this->hLen); - if (!$this->_equals($lHash, $lHash2)) { - user_error('Decryption error'); - return false; + $hashesMatch = $this->_equals($lHash, $lHash2); + $leadingZeros = 1; + $patternMatch = 0; + $offset = 0; + for ($i = 0; $i < strlen($m); $i++) { + $patternMatch|= $leadingZeros & ($m[$i] === "\1"); + $leadingZeros&= $m[$i] === "\0"; + $offset+= $patternMatch ? 0 : 1; } - $m = ltrim($m, chr(0)); - if (ord($m[0]) != 1) { + + // we do & instead of && to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation + // to protect against timing attacks + if (!$hashesMatch & !$patternMatch) { user_error('Decryption error'); return false; } // Output the message M - return substr($m, 1); + return substr($m, $offset + 1); } /** diff --git a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php index 1da046e82..3aaa30900 100644 --- a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php +++ b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php @@ -244,9 +244,10 @@ class ASN1 $tag = 0; // process septets (since the eighth bit is ignored, it's not an octet) do { - $loop = ord($encoded[0]) >> 7; + $temp = ord($encoded[$encoded_pos++]); + $loop = $temp >> 7; $tag <<= 7; - $tag |= ord($encoded[$encoded_pos++]) & 0x7F; + $tag |= $temp & 0x7F; $start++; } while ($loop); } @@ -308,6 +309,9 @@ class ASN1 $remainingLength = $length; while ($remainingLength > 0) { $temp = $this->_decode_ber($content, $start, $content_pos); + if ($temp === false) { + break; + } $length = $temp['length']; // end-of-content octets - see paragraph 8.1.5 if (substr($content, $content_pos + $length, 2) == "\0\0") { @@ -359,6 +363,9 @@ class ASN1 $current['content'] = substr($content, $content_pos); } else { $temp = $this->_decode_ber($content, $start, $content_pos); + if ($temp === false) { + return false; + } $length-= (strlen($content) - $content_pos); $last = count($temp) - 1; for ($i = 0; $i < $last; $i++) { @@ -383,6 +390,9 @@ class ASN1 $length = 0; while (substr($content, $content_pos, 2) != "\0\0") { $temp = $this->_decode_ber($content, $length + $start, $content_pos); + if ($temp === false) { + return false; + } $content_pos += $temp['length']; // all subtags should be octet strings //if ($temp['type'] != self::TYPE_OCTET_STRING) { @@ -415,30 +425,16 @@ class ASN1 break 2; } $temp = $this->_decode_ber($content, $start + $offset, $content_pos); + if ($temp === false) { + return false; + } $content_pos += $temp['length']; $current['content'][] = $temp; $offset+= $temp['length']; } break; case self::TYPE_OBJECT_IDENTIFIER: - $temp = ord($content[$content_pos++]); - $current['content'] = sprintf('%d.%d', floor($temp / 40), $temp % 40); - $valuen = 0; - // process septets - $content_len = strlen($content); - while ($content_pos < $content_len) { - $temp = ord($content[$content_pos++]); - $valuen <<= 7; - $valuen |= $temp & 0x7F; - if (~$temp & 0x80) { - $current['content'].= ".$valuen"; - $valuen = 0; - } - } - // the eighth bit of the last byte should not be 1 - //if ($temp >> 7) { - // return false; - //} + $current['content'] = $this->_decodeOID(substr($content, $content_pos)); break; /* Each character string type shall be encoded as if it had been declared: [UNIVERSAL x] IMPLICIT OCTET STRING @@ -582,7 +578,7 @@ class ASN1 $childClass = $tempClass = self::CLASS_UNIVERSAL; $constant = null; if (isset($temp['constant'])) { - $tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC; + $tempClass = $temp['type']; } if (isset($child['class'])) { $childClass = $child['class']; @@ -645,7 +641,7 @@ class ASN1 $temp = $decoded['content'][$i]; $tempClass = self::CLASS_UNIVERSAL; if (isset($temp['constant'])) { - $tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC; + $tempClass = $temp['type']; } foreach ($mapping['children'] as $key => $child) { @@ -1001,27 +997,7 @@ class ASN1 $value = base64_decode($source); break; case self::TYPE_OBJECT_IDENTIFIER: - $oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids); - if ($oid === false) { - user_error('Invalid OID'); - return false; - } - $value = ''; - $parts = explode('.', $oid); - $value = chr(40 * $parts[0] + $parts[1]); - for ($i = 2; $i < count($parts); $i++) { - $temp = ''; - if (!$parts[$i]) { - $temp = "\0"; - } else { - while ($parts[$i]) { - $temp = chr(0x80 | ($parts[$i] & 0x7F)) . $temp; - $parts[$i] >>= 7; - } - $temp[strlen($temp) - 1] = $temp[strlen($temp) - 1] & chr(0x7F); - } - $value.= $temp; - } + $value = $this->_encodeOID($source); break; case self::TYPE_ANY: $loc = $this->location; @@ -1120,6 +1096,108 @@ 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 * diff --git a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/File/X509.php b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/File/X509.php index 67317db23..4ebafa171 100644 --- a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/File/X509.php +++ b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/File/X509.php @@ -305,6 +305,22 @@ class X509 */ var $challenge; + /** + * Recursion Limit + * + * @var int + * @access private + */ + static $recur_limit = 5; + + /** + * URL fetch flag + * + * @var bool + * @access private + */ + static $disable_url_fetch = false; + /** * Default Constructor. * @@ -1912,6 +1928,9 @@ class X509 // "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 @@ -2076,7 +2095,7 @@ class X509 * * If $date isn't defined it is assumed to be the current date. * - * @param int $date optional + * @param \DateTime|string $date optional * @access public */ function validateDate($date = null) @@ -2086,7 +2105,7 @@ class X509 } if (!isset($date)) { - $date = new DateTime($date, new DateTimeZone(@date_default_timezone_get())); + $date = new DateTime(null, new DateTimeZone(@date_default_timezone_get())); } $notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore']; @@ -2095,15 +2114,133 @@ 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 < new DateTime($notBefore, new DateTimeZone(@date_default_timezone_get())): - case $date > new DateTime($notAfter, new DateTimeZone(@date_default_timezone_get())): + case $date < $notBefore: + case $date > $notAfter: return false; } return true; } + /** + * Fetches a URL + * + * @param string $url + * @access private + * @return bool|string + */ + static function _fetchURL($url) + { + if (self::$disable_url_fetch) { + return false; + } + + $parts = parse_url($url); + $data = ''; + switch ($parts['scheme']) { + case 'http': + $fsock = @fsockopen($parts['host'], isset($parts['port']) ? $parts['port'] : 80); + if (!$fsock) { + return false; + } + fputs($fsock, "GET $parts[path] HTTP/1.0\r\n"); + fputs($fsock, "Host: $parts[host]\r\n\r\n"); + $line = fgets($fsock, 1024); + if (strlen($line) < 3) { + return false; + } + preg_match('#HTTP/1.\d (\d{3})#', $line, $temp); + if ($temp[1] != '200') { + return false; + } + + // skip the rest of the headers in the http response + while (!feof($fsock) && fgets($fsock, 1024) != "\r\n") { + } + + while (!feof($fsock)) { + $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 * @@ -2120,11 +2257,30 @@ class X509 * @return mixed */ function validateSignature($caonly = true) + { + return $this->_validateSignatureCountable($caonly, 0); + } + + /** + * Validate a signature + * + * Performs said validation whilst keeping track of how many times validation method is called + * + * @param bool $caonly + * @param int $count + * @access private + * @return mixed + */ + function _validateSignatureCountable($caonly, $count) { if (!is_array($this->currentCert) || !isset($this->signatureSubject)) { return null; } + if ($count == self::$recur_limit) { + return false; + } + /* TODO: "emailAddress attribute values are not case-sensitive (e.g., "subscriber@example.com" is the same as "SUBSCRIBER@EXAMPLE.COM")." -- http://tools.ietf.org/html/rfc5280#section-4.1.2.6 @@ -2170,10 +2326,10 @@ class X509 } } if (count($this->CAs) == $i && $caonly) { - return false; + return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly); } } elseif (!isset($signingCert) || $caonly) { - return false; + return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly); } return $this->_validateSignature( $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], @@ -2280,6 +2436,41 @@ class X509 return true; } + /** + * Sets the recursion limit + * + * When validating a signature it may be necessary to download intermediate certs from URI's. + * An intermediate cert that linked to itself would result in an infinite loop so to prevent + * that we set a recursion limit. A negative number means that there is no recursion limit. + * + * @param int $count + * @access public + */ + static function setRecurLimit($count) + { + self::$recur_limit = $count; + } + + /** + * Prevents URIs from being automatically retrieved + * + * @access public + */ + static function disableURLFetch() + { + self::$disable_url_fetch = true; + } + + /** + * Allows URIs to be automatically retrieved + * + * @access public + */ + static function enableURLFetch() + { + self::$disable_url_fetch = false; + } + /** * Reformat public keys * @@ -2739,7 +2930,7 @@ class X509 } $output.= $desc . '=' . $value; $result[$desc] = isset($result[$desc]) ? - array_merge((array) $dn[$prop], array($value)) : + array_merge((array) $result[$desc], array($value)) : $value; $start = false; } @@ -3447,7 +3638,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( diff --git a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php index 2aa39a50a..fe1f43c58 100644 --- a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php +++ b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php @@ -45,7 +45,6 @@ * @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; @@ -266,23 +265,27 @@ 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 - ob_start(); - @phpinfo(); - $content = ob_get_contents(); - ob_end_clean(); - - preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); - $versions = array(); - if (!empty($matches[1])) { - for ($i = 0; $i < count($matches[1]); $i++) { - $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); - // Remove letter part in OpenSSL version - if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) { - $versions[$matches[1][$i]] = $fullVersion; - } else { - $versions[$matches[1][$i]] = $m[0]; + // avoid generating errors (even with suppression) when phpinfo() is disabled (common in production systems) + if (strpos(ini_get('disable_functions'), 'phpinfo') === false) { + ob_start(); + @phpinfo(); + $content = ob_get_contents(); + ob_end_clean(); + + preg_match_all('#OpenSSL (Header|Library) Version(.*)#im', $content, $matches); + + if (!empty($matches[1])) { + for ($i = 0; $i < count($matches[1]); $i++) { + $fullVersion = trim(str_replace('=>', '', strip_tags($matches[2][$i]))); + + // Remove letter part in OpenSSL version + if (!preg_match('/(\d+\.\d+\.\d+)/i', $fullVersion, $m)) { + $versions[$matches[1][$i]] = $fullVersion; + } else { + $versions[$matches[1][$i]] = $m[0]; + } } } } @@ -442,6 +445,9 @@ class BigInteger // (?<=^|-)0*: find any 0's that are preceded by the start of the string or by a - (ie. octals) // [^-0-9].*: find any non-numeric characters and then any characters that follow that $x = preg_replace('#(?add(new static(1)) : $this->copy(); $bytes = $temp->toBytes(); - if (empty($bytes)) { // eg. if the number we're trying to convert is -1 + if (!strlen($bytes)) { // eg. if the number we're trying to convert is -1 $bytes = chr(0); } @@ -1555,7 +1561,9 @@ class BigInteger $temp_value = array($quotient_value[$q_index]); $temp = $temp->multiply($y); $temp_value = &$temp->value; - $temp_value = array_merge($adjust, $temp_value); + if (count($temp_value)) { + $temp_value = array_merge($adjust, $temp_value); + } $x = $x->subtract($temp); @@ -2688,7 +2696,14 @@ class BigInteger { switch (MATH_BIGINTEGER_MODE) { case self::MODE_GMP: - return gmp_cmp($this->value, $y->value); + $r = gmp_cmp($this->value, $y->value); + if ($r < -1) { + $r = -1; + } + if ($r > 1) { + $r = 1; + } + return $r; case self::MODE_BCMATH: return bccomp($this->value, $y->value, 0); } @@ -3569,6 +3584,7 @@ class BigInteger $value = &$result->value; if (!count($value)) { + $result->is_negative = false; return $result; } diff --git a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php index f95bce6df..cf13496cd 100644 --- a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php +++ b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php @@ -144,6 +144,11 @@ class SCP return false; } + if (empty($remote_file)) { + user_error('remote_file cannot be blank', E_USER_NOTICE); + return false; + } + if (!$this->ssh->exec('scp -t ' . escapeshellarg($remote_file), false)) { // -t = to return false; } diff --git a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php index 8825f30cf..a248c2581 100644 --- a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php +++ b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php @@ -109,11 +109,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 int + * @var boolean * @see self::_send_sftp_packet() * @access private */ - var $request_id = false; + var $use_request_id = false; /** * The Packet Type @@ -250,6 +250,15 @@ class SFTP extends SSH2 */ var $canonicalize_paths = true; + /** + * Request Buffers + * + * @see self::_get_sftp_packet() + * @var array + * @access private + */ + var $requestBuffer = array(); + /** * Default Constructor. * @@ -519,7 +528,7 @@ class SFTP extends SSH2 } */ - $this->request_id = 1; + $this->use_request_id = true; /* A Note on SFTPv4/5/6 support: @@ -833,6 +842,7 @@ 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; @@ -864,7 +874,17 @@ class SFTP extends SSH2 unset($files[$key]); continue; } - if ($key != '.' && $key != '..' && is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key)))) { + $is_directory = false; + if ($key != '.' && $key != '..') { + if ($this->use_stat_cache) { + $is_directory = is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key))); + } else { + $stat = $this->lstat($dir . '/' . $key); + $is_directory = $stat && $stat['type'] === NET_SFTP_TYPE_DIRECTORY; + } + } + + if ($is_directory) { $depth++; $files[$key] = $this->rawlist($dir . '/' . $key, true); $depth--; @@ -2128,10 +2148,11 @@ class SFTP extends SSH2 * @param string $local_file * @param int $offset * @param int $length + * @param callable|null $progressCallback * @return mixed * @access public */ - function get($remote_file, $local_file = false, $offset = 0, $length = -1) + function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null) { if (!($this->bitmap & SSH2::MASK_LOGIN)) { return false; @@ -2189,7 +2210,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)) { + if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet, $i)) { if ($fclose_check) { fclose($fp); } @@ -2197,6 +2218,9 @@ class SFTP extends SSH2 } $packet = null; $read+= $packet_size; + if (is_callable($progressCallback)) { + call_user_func($progressCallback, $read); + } $i++; } @@ -2204,15 +2228,17 @@ class SFTP extends SSH2 break; } + $packets_sent = $i - 1; + $clear_responses = false; while ($i > 0) { $i--; if ($clear_responses) { - $this->_get_sftp_packet(); + $this->_get_sftp_packet($packets_sent - $i); continue; } else { - $response = $this->_get_sftp_packet(); + $response = $this->_get_sftp_packet($packets_sent - $i); } switch ($this->packet_type) { @@ -2921,10 +2947,10 @@ class SFTP extends SSH2 * @return bool * @access private */ - function _send_sftp_packet($type, $data) + function _send_sftp_packet($type, $data, $request_id = 1) { - $packet = $this->request_id !== false ? - pack('NCNa*', strlen($data) + 5, $type, $this->request_id, $data) : + $packet = $this->use_request_id ? + pack('NCNa*', strlen($data) + 5, $type, $request_id, $data) : pack('NCa*', strlen($data) + 1, $type, $data); $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 @@ -2962,9 +2988,18 @@ class SFTP extends SSH2 * @return string * @access private */ - function _get_sftp_packet() + function _get_sftp_packet($request_id = null) { - $this->curTimeout = false; + if (isset($request_id) && isset($this->requestBuffer[$request_id])) { + $this->packet_type = $this->requestBuffer[$request_id]['packet_type']; + $temp = $this->requestBuffer[$request_id]['packet']; + unset($this->requestBuffer[$request_id]); + return $temp; + } + + // in SSH2.php the timeout is cumulative per function call. eg. exec() will + // timeout after 10s. but for SFTP.php it's cumulative per packet + $this->curTimeout = $this->timeout; $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 @@ -2985,6 +3020,13 @@ class SFTP extends SSH2 $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); @@ -3001,8 +3043,8 @@ class SFTP extends SSH2 $this->packet_type = ord($this->_string_shift($this->packet_buffer)); - if ($this->request_id !== false) { - $this->_string_shift($this->packet_buffer, 4); // remove the request id + if ($this->use_request_id) { + extract(unpack('Npacket_id', $this->_string_shift($this->packet_buffer, 4))); // remove the request id $length-= 5; // account for the request id and the packet type } else { $length-= 1; // account for the packet type @@ -3025,6 +3067,14 @@ 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; } diff --git a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php index cdb7cb39e..817fa165e 100644 --- a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php +++ b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php @@ -104,6 +104,7 @@ class SSH2 const CHANNEL_SHELL = 2; const CHANNEL_SUBSYSTEM = 3; const CHANNEL_AGENT_FORWARD = 4; + const CHANNEL_KEEP_ALIVE = 5; /**#@-*/ /**#@+ @@ -145,7 +146,10 @@ class SSH2 */ const READ_REGEX = 2; /** - * Returns when a string matching the regular expression $expect is found + * Returns whenever a data packet is received. + * + * Some data packets may only contain a single character so it may be necessary + * to call read() multiple times when using this option */ const READ_NEXT = 3; /**#@-*/ @@ -918,6 +922,22 @@ class SSH2 */ 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(); + /** * Default Constructor. * @@ -1124,11 +1144,12 @@ class SSH2 } $elapsed = microtime(true) - $start; - $this->curTimeout-= $elapsed; - - if ($this->curTimeout <= 0) { - $this->is_timeout = true; - return false; + if ($this->curTimeout) { + $this->curTimeout-= $elapsed; + if ($this->curTimeout < 0) { + $this->is_timeout = true; + return false; + } } } @@ -1193,6 +1214,7 @@ class SSH2 } if (feof($this->fsock)) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } @@ -1206,7 +1228,7 @@ class SSH2 $this->server_identifier = trim($temp, "\r\n"); if (strlen($extra)) { - $this->errors[] = utf8_decode($data); + $this->errors[] = $data; } if (version_compare($matches[3], '1.99', '<')) { @@ -1221,6 +1243,7 @@ class SSH2 if (!$this->send_kex_first) { $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } @@ -1309,6 +1332,8 @@ 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 ); @@ -1459,6 +1484,7 @@ class SSH2 $kexinit_payload_server = $this->_get_binary_packet(); if ($kexinit_payload_server === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } @@ -1595,6 +1621,7 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } @@ -1687,12 +1714,14 @@ 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; } $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } @@ -1782,9 +1811,25 @@ class SSH2 return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } - if ($public_key_format != $server_host_key_algorithm || $this->signature_format != $server_host_key_algorithm) { - user_error('Server Host Key Algorithm Mismatch'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + 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); + } } $packet = pack( @@ -1799,6 +1844,7 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } @@ -1820,7 +1866,7 @@ class SSH2 $this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt); if ($this->encrypt) { if ($this->crypto_engine) { - $this->encrypt->setEngine($this->crypto_engine); + $this->encrypt->setPreferredEngine($this->crypto_engine); } if ($this->encrypt->block_size) { $this->encrypt_block_size = $this->encrypt->block_size; @@ -1844,7 +1890,7 @@ class SSH2 $this->decrypt = $this->_encryption_algorithm_to_crypt_instance($decrypt); if ($this->decrypt) { if ($this->crypto_engine) { - $this->decrypt->setEngine($this->crypto_engine); + $this->decrypt->setPreferredEngine($this->crypto_engine); } if ($this->decrypt->block_size) { $this->decrypt_block_size = $this->decrypt->block_size; @@ -2095,6 +2141,7 @@ class SSH2 function login($username) { $args = func_get_args(); + $this->auth[] = $args; return call_user_func_array(array(&$this, '_login'), $args); } @@ -2166,6 +2213,7 @@ class SSH2 } return $this->_login_helper($username, $password); } + $this->bitmap = 0; user_error('Connection closed by server'); return false; } @@ -2218,6 +2266,7 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } @@ -2276,6 +2325,7 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } @@ -2294,7 +2344,7 @@ class SSH2 return false; } extract(unpack('Nlength', $this->_string_shift($response, 4))); - $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length)); + $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $this->_string_shift($response, $length); return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); case NET_SSH2_MSG_USERAUTH_FAILURE: // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees @@ -2376,6 +2426,7 @@ class SSH2 } else { $orig = $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } @@ -2545,6 +2596,21 @@ 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, @@ -2555,7 +2621,7 @@ class SSH2 strlen('publickey'), 'publickey' ); - $part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey); + $part2 = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($publickey), $publickey); $packet = $part1 . chr(0) . $part2; if (!$this->_send_binary_packet($packet)) { @@ -2564,6 +2630,7 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } @@ -2595,8 +2662,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('ssh-rsa'), 'ssh-rsa', strlen($signature), $signature); + $signature = pack('Na*Na*', strlen($signatureType), $signatureType, strlen($signature), $signature); $packet.= pack('Na*', strlen($signature), $signature); if (!$this->_send_binary_packet($packet)) { @@ -2605,6 +2673,7 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } @@ -2731,6 +2800,7 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } @@ -2870,6 +2940,7 @@ class SSH2 $response = $this->_get_binary_packet(); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } @@ -3184,6 +3255,66 @@ 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 * @@ -3214,8 +3345,8 @@ class SSH2 function _get_binary_packet($skip_channel_filter = false) { if (!is_resource($this->fsock) || feof($this->fsock)) { - user_error('Connection closed prematurely'); $this->bitmap = 0; + user_error('Connection closed prematurely'); return false; } @@ -3258,8 +3389,8 @@ class SSH2 while ($remaining_length > 0) { $temp = stream_get_contents($this->fsock, $remaining_length); if ($temp === false || feof($this->fsock)) { - user_error('Error reading from socket'); $this->bitmap = 0; + user_error('Error reading from socket'); return false; } $buffer.= $temp; @@ -3277,8 +3408,8 @@ class SSH2 if ($this->hmac_check !== false) { $hmac = stream_get_contents($this->fsock, $this->hmac_size); if ($hmac === false || strlen($hmac) != $this->hmac_size) { - user_error('Error reading socket'); $this->bitmap = 0; + user_error('Error reading socket'); return false; } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) { user_error('Invalid HMAC'); @@ -3322,7 +3453,7 @@ class SSH2 return false; } extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8))); - $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length)); + $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . $this->_string_shift($payload, $length); $this->bitmap = 0; return false; case NET_SSH2_MSG_IGNORE: @@ -3334,7 +3465,7 @@ class SSH2 return false; } extract(unpack('Nlength', $this->_string_shift($payload, 4))); - $this->errors[] = 'SSH_MSG_DEBUG: ' . utf8_decode($this->_string_shift($payload, $length)); + $this->errors[] = 'SSH_MSG_DEBUG: ' . $this->_string_shift($payload, $length); $payload = $this->_get_binary_packet($skip_channel_filter); break; case NET_SSH2_MSG_UNIMPLEMENTED: @@ -3357,7 +3488,7 @@ class SSH2 return false; } extract(unpack('Nlength', $this->_string_shift($payload, 4))); - $this->banner_message = utf8_decode($this->_string_shift($payload, $length)); + $this->banner_message = $this->_string_shift($payload, $length); $payload = $this->_get_binary_packet(); } @@ -3561,7 +3692,12 @@ class SSH2 $response = $this->binary_packet_buffer; $this->binary_packet_buffer = false; } else { - if ($this->curTimeout) { + $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; @@ -3576,6 +3712,9 @@ class SSH2 // 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; + if ($client_channel == self::CHANNEL_EXEC && !$this->request_pty) { + $this->_close_channel($client_channel); + } return true; } $elapsed = microtime(true) - $start; @@ -3584,6 +3723,7 @@ class SSH2 $response = $this->_get_binary_packet(true); if ($response === false) { + $this->bitmap = 0; user_error('Connection closed by server'); return false; } @@ -3592,10 +3732,6 @@ class SSH2 if ($client_channel == -1 && $response === true) { return true; } - if (!strlen($response)) { - return ''; - } - if (!strlen($response)) { return false; } @@ -3626,7 +3762,7 @@ class SSH2 switch ($type) { case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: /* - if ($client_channel == NET_SSH2_CHANNEL_EXEC) { + if ($client_channel == self::CHANNEL_EXEC) { $this->_send_channel_packet($client_channel, chr(0)); } */ @@ -3818,8 +3954,8 @@ class SSH2 function _send_binary_packet($data, $logged = null) { if (!is_resource($this->fsock) || feof($this->fsock)) { - user_error('Connection closed prematurely'); $this->bitmap = 0; + user_error('Connection closed prematurely'); return false; } @@ -4480,6 +4616,8 @@ class SSH2 break; case 'ssh-rsa': + case 'rsa-sha2-256': + case 'rsa-sha2-512': if (strlen($server_public_host_key) < 4) { return false; } @@ -4502,8 +4640,21 @@ class SSH2 $signature = $this->_string_shift($signature, $temp['length']); $rsa = new RSA(); + switch ($this->signature_format) { + case 'rsa-sha2-512': + $hash = 'sha512'; + break; + case 'rsa-sha2-256': + $hash = 'sha256'; + break; + //case 'ssh-rsa': + default: + $hash = 'sha1'; + } + $rsa->setHash($hash); $rsa->setSignatureMode(RSA::SIGNATURE_PKCS1); $rsa->loadKey(array('e' => $e, 'n' => $n), RSA::PUBLIC_FORMAT_RAW); + if (!$rsa->verify($this->exchange_hash, $signature)) { user_error('Bad server signature'); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); @@ -4530,7 +4681,30 @@ class SSH2 $s = $s->modPow($e, $n); $s = $s->toBytes(); - $h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($this->exchange_hash)); + switch ($this->signature_format) { + case 'rsa-sha2-512': + $hash = 'sha512'; + break; + case 'rsa-sha2-256': + $hash = 'sha256'; + break; + //case 'ssh-rsa': + default: + $hash = 'sha1'; + } + $hashObj = new Hash($hash); + switch ($this->signature_format) { + case 'rsa-sha2-512': + $h = pack('N5a*', 0x00305130, 0x0D060960, 0x86480165, 0x03040203, 0x05000440, $hashObj->hash($this->exchange_hash)); + break; + case 'rsa-sha2-256': + $h = pack('N5a*', 0x00303130, 0x0D060960, 0x86480165, 0x03040201, 0x05000420, $hashObj->hash($this->exchange_hash)); + break; + //case 'ssh-rsa': + default: + $hash = 'sha1'; + $h = pack('N4a*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, $hashObj->hash($this->exchange_hash)); + } $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 2 - strlen($h)) . $h; if ($s != $h) { diff --git a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php index a4ff0549d..99dcecfe6 100644 --- a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php +++ b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php @@ -43,7 +43,7 @@ use phpseclib\System\SSH\Agent\Identity; * * @package SSH\Agent * @author Jim Wigginton - * @access internal + * @access public */ class Agent { @@ -117,18 +117,20 @@ class Agent * @return \phpseclib\System\SSH\Agent * @access public */ - function __construct() + function __construct($address = null) { - switch (true) { - case isset($_SERVER['SSH_AUTH_SOCK']): - $address = $_SERVER['SSH_AUTH_SOCK']; - break; - case isset($_ENV['SSH_AUTH_SOCK']): - $address = $_ENV['SSH_AUTH_SOCK']; - break; - default: - user_error('SSH_AUTH_SOCK not found'); - return false; + if (!$address) { + switch (true) { + case isset($_SERVER['SSH_AUTH_SOCK']): + $address = $_SERVER['SSH_AUTH_SOCK']; + break; + case isset($_ENV['SSH_AUTH_SOCK']): + $address = $_ENV['SSH_AUTH_SOCK']; + break; + default: + user_error('SSH_AUTH_SOCK not found'); + return false; + } } $this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr); @@ -155,12 +157,14 @@ class Agent $packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES); if (strlen($packet) != fputs($this->fsock, $packet)) { user_error('Connection closed while requesting identities'); + return array(); } $length = current(unpack('N', fread($this->fsock, 4))); $type = ord(fread($this->fsock, 1)); if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) { user_error('Unable to request identities'); + return array(); } $identities = array(); diff --git a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php index b8cc6cded..b4649046a 100644 --- a/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php +++ b/libraries/vdm_io/vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php @@ -32,6 +32,17 @@ use phpseclib\System\SSH\Agent; */ class Identity { + /**@+ + * Signature Flags + * + * See https://tools.ietf.org/html/draft-miller-ssh-agent-00#section-5.3 + * + * @access private + */ + const SSH_AGENT_RSA2_256 = 2; + const SSH_AGENT_RSA2_512 = 4; + /**#@-*/ + /** * Key Object * @@ -59,6 +70,16 @@ class Identity */ var $fsock; + /** + * Signature flags + * + * @var int + * @access private + * @see self::sign() + * @see self::setHash() + */ + var $flags = 0; + /** * Default Constructor. * @@ -126,6 +147,31 @@ class Identity { } + /** + * Set Hash + * + * ssh-agent doesn't support using hashes for RSA other than SHA1 + * + * @param string $hash + * @access public + */ + function setHash($hash) + { + $this->flags = 0; + switch ($hash) { + case 'sha1': + break; + case 'sha256': + $this->flags = self::SSH_AGENT_RSA2_256; + break; + case 'sha512': + $this->flags = self::SSH_AGENT_RSA2_512; + break; + default: + user_error('The only supported hashes for RSA are sha1, sha256 and sha512'); + } + } + /** * Create a signature * @@ -138,7 +184,7 @@ class Identity function sign($message) { // the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE - $packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, 0); + $packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, $this->flags); $packet = pack('Na*', strlen($packet), $packet); if (strlen($packet) != fputs($this->fsock, $packet)) { user_error('Connection closed during signing'); @@ -151,8 +197,34 @@ class Identity } $signature_blob = fread($this->fsock, $length - 1); - // the only other signature format defined - ssh-dss - is the same length as ssh-rsa - // the + 12 is for the other various SSH added length fields - return substr($signature_blob, strlen('ssh-rsa') + 12); + $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; } }