]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
[CORE] Downgrade phpseclib to a working state
authorDiogo Cordeiro <diogo@fc.up.pt>
Mon, 24 Jun 2019 16:45:19 +0000 (17:45 +0100)
committerDiogo Cordeiro <diogo@fc.up.pt>
Sat, 3 Aug 2019 16:47:26 +0000 (17:47 +0100)
33 files changed:
composer.json
composer.lock
vendor/composer/autoload_classmap.php
vendor/composer/autoload_files.php
vendor/composer/autoload_static.php
vendor/composer/installed.json
vendor/phpseclib/phpseclib/README.md
vendor/phpseclib/phpseclib/appveyor.yml [deleted file]
vendor/phpseclib/phpseclib/composer.json
vendor/phpseclib/phpseclib/phpseclib/Crypt/AES.php
vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php
vendor/phpseclib/phpseclib/phpseclib/Crypt/Blowfish.php
vendor/phpseclib/phpseclib/phpseclib/Crypt/DES.php
vendor/phpseclib/phpseclib/phpseclib/Crypt/Hash.php
vendor/phpseclib/phpseclib/phpseclib/Crypt/RC2.php
vendor/phpseclib/phpseclib/phpseclib/Crypt/RC4.php
vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php
vendor/phpseclib/phpseclib/phpseclib/Crypt/Random.php
vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php
vendor/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php
vendor/phpseclib/phpseclib/phpseclib/Crypt/Twofish.php
vendor/phpseclib/phpseclib/phpseclib/File/ANSI.php
vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php
vendor/phpseclib/phpseclib/phpseclib/File/X509.php
vendor/phpseclib/phpseclib/phpseclib/Math/BigInteger.php
vendor/phpseclib/phpseclib/phpseclib/Net/SCP.php
vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php
vendor/phpseclib/phpseclib/phpseclib/Net/SFTP/Stream.php
vendor/phpseclib/phpseclib/phpseclib/Net/SSH1.php
vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php
vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent.php
vendor/phpseclib/phpseclib/phpseclib/System/SSH/Agent/Identity.php
vendor/phpseclib/phpseclib/phpseclib/bootstrap.php

index 581f05e33467ac31b53a9448a3e402c08d5a2c0d..3c4eb61eb78a10a7e1626a31e2a5657386a2bd9f 100644 (file)
@@ -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": {
index f1105b4724786cd408a9f6496ecad184279bd8f4..670b7973a962a91987f289e14884303c4ab999f7 100644 (file)
@@ -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",
             ],
             "time": "2018-04-30T17:57:16+00:00"
         },
+        {
+            "name": "paragonie/random_compat",
+            "version": "v2.0.18",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/paragonie/random_compat.git",
+                "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0a58ef6e3146256cc3dc7cc393927bcc7d1b72db",
+                "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db",
+                "shasum": ""
+            },
+            "require": {
+                "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": "2.0.19",
+            "version": "dev-master",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpseclib/phpseclib.git",
-                "reference": "d2085db7b7394baa071a69c8f9159723c250f2ba"
+                "reference": "f815e43077da67d3dd5b4d18a45753f5b79c1ab9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/d2085db7b7394baa071a69c8f9159723c250f2ba",
-                "reference": "d2085db7b7394baa071a69c8f9159723c250f2ba",
+                "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/f815e43077da67d3dd5b4d18a45753f5b79c1ab9",
+                "reference": "f815e43077da67d3dd5b4d18a45753f5b79c1ab9",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.3"
+                "paragonie/constant_time_encoding": "^1",
+                "paragonie/random_compat": "^1.4|^2.0",
+                "php": ">=5.6.1"
             },
             "require-dev": {
                 "phing/phing": "~2.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",
     ],
     "aliases": [],
     "minimum-stability": "dev",
-    "stability-flags": [],
+    "stability-flags": {
+        "phpseclib/phpseclib": 20
+    },
     "prefer-stable": true,
     "prefer-lowest": false,
     "platform": {
index b646a79831c25b0f16420931817fb5e83a937a99..8afc77cf8e0ea6aed9d005a53ad0292b3ea1f1ee 100644 (file)
@@ -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',
index 18d5e4a3c7e00f1449b53cf850dee892ae01a4a2..0698628c178e4e0b501bd5a65a276b92259cac63 100644 (file)
@@ -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',
index 8c6336129e5bdbabbf62d9f410ea9739268375d6..d560ecb13be5217264e63a0236cb82d353167d61 100644 (file)
@@ -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',
index 20a413de434c43bd6242e3cb34a7aa4229f2e67e..34b0a044c4f3d95ff856f8a2670f25124e51856c 100644 (file)
             "rfc4648"
         ]
     },
+    {
+        "name": "paragonie/random_compat",
+        "version": "v2.0.18",
+        "version_normalized": "2.0.18.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/paragonie/random_compat.git",
+            "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0a58ef6e3146256cc3dc7cc393927bcc7d1b72db",
+            "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db",
+            "shasum": ""
+        },
+        "require": {
+            "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": "2.0.19",
-        "version_normalized": "2.0.19.0",
+        "version": "dev-master",
+        "version_normalized": "9999999-dev",
         "source": {
             "type": "git",
             "url": "https://github.com/phpseclib/phpseclib.git",
-            "reference": "d2085db7b7394baa071a69c8f9159723c250f2ba"
+            "reference": "f815e43077da67d3dd5b4d18a45753f5b79c1ab9"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/d2085db7b7394baa071a69c8f9159723c250f2ba",
-            "reference": "d2085db7b7394baa071a69c8f9159723c250f2ba",
+            "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/f815e43077da67d3dd5b4d18a45753f5b79c1ab9",
+            "reference": "f815e43077da67d3dd5b4d18a45753f5b79c1ab9",
             "shasum": ""
         },
         "require": {
-            "php": ">=5.3.3"
+            "paragonie/constant_time_encoding": "^1",
+            "paragonie/random_compat": "^1.4|^2.0",
+            "php": ">=5.6.1"
         },
         "require-dev": {
             "phing/phing": "~2.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": {
index 4d8f8dd79af5ff364d49327e523446fc0c0de612..4c9af456fc683e5c4fad361d91e08a70728dcd3f 100644 (file)
@@ -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/)
+
+<img src="http://phpseclib.sourceforge.net/pear-icon.png" alt="PEAR Channel" width="16" height="16">
+PEAR Channel: [phpseclib.sourceforge.net](http://phpseclib.sourceforge.net/pear.htm)
 
 ## Documentation
 
 * [Documentation / Manual](http://phpseclib.sourceforge.net/)
-* [API Documentation](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)
 
-## Contributing
+## Installing Development Dependencies
 
-1. Fork the Project
+Dependencies are managed via Composer.
 
-2. Ensure you have Composer installed (see [Composer Download Instructions](https://getcomposer.org/download/))
+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
 
-3. Install Development Dependencies
+    ``` sh
+    curl -sS https://getcomposer.org/installer | php
+    ```
+
+2. Install Dependencies
 
     ``` sh
-    composer install
+    php composer.phar install
     ```
 
-4. Create a Feature Branch
+## Contributing
+
+1. Fork the Project
+
+2. Install Development Dependencies
+
+3. 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 (file)
index 210a903..0000000
+++ /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
index b4e8a1c9c9213efd79ec15cfcc7f8d8150e5a7ac..9f728103ac99cc3319b1ca7c846dc3240bc244fb 100644 (file)
         }
     ],
     "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"
     },
index 7d8cb8b034199ec014dc9e3b5e0374a89c6f7194..8521eb5e5cfff65adcae437d0432bd63b8190509 100644 (file)
@@ -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);
     }
 }
index 03b176e7bfc0928d9ef30c45e7fa2d9b800aedc1..e3cc7b87bb62d33d77662dba2e81f1b122454595 100644 (file)
@@ -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
@@ -434,28 +430,26 @@ abstract class Base
     var $openssl_options;
 
     /**
-     * Has the key length explicitly been set or should it be derived from the key, itself?
+     * Don't truncate / null pad key
      *
-     * @see self::setKeyLength()
+     * @see self::_clearBuffers()
      * @var bool
      * @access private
      */
-    var $explicit_key_length = false;
+    var $skip_key_adjustment = false;
 
     /**
-     * Don't truncate / null pad key
+     * Has the key length explicitly been set or should it be derived from the key, itself?
      *
-     * @see self::_clearBuffers()
+     * @see self::setKeyLength()
      * @var bool
      * @access private
      */
-    var $skip_key_adjustment = false;
+    var $explicit_key_length = false;
 
     /**
      * Default Constructor.
      *
-     * Determines whether or not the mcrypt extension should be used.
-     *
      * $mode could be:
      *
      * - self::MODE_ECB
@@ -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);
         }
     }
 }
index 74cc49de8abac9a0c3c87065db73addd9bb47352..3500df595b66d9f63065646cb9ce77f9b5f5f92e 100644 (file)
@@ -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];
                 ';
             }
 
index 9a8225fb5d243721a5332da431fdabda63fbafff..14273d288978d1c1110e80e096329a59d7002d5e 100644 (file)
@@ -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;
             }
 
index a61668209ae9a7f18ae2f5b4022a0d022ce2d175..080e8cd2106c73793331d23f022b3e5aa685a203 100644 (file)
@@ -1,26 +1,19 @@
 <?php
 
 /**
- * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
+ * Wrapper around hash() and hash_hmac() functions supporting truncated hashes
+ * such as sha256-96.  Any hash algorithm returned by hash_algos() (and
+ * truncated versions thereof) are supported.
  *
- * Uses hash() or mhash() if available and an internal implementation, otherwise.  Currently supports the following:
- *
- * md2, md5, md5-96, sha1, sha1-96, sha256, sha256-96, sha384, and sha512, sha512-96
- *
- * If {@link self::setKey() setKey()} is called, {@link self::hash() hash()} will return the HMAC as opposed to
- * the hash.  If no valid algorithm is provided, sha1 will be used.
- *
- * PHP version 5
- *
- * {@internal The variable names are the same as those in
- * {@link http://tools.ietf.org/html/rfc2104#section-2 RFC2104}.}}
+ * If {@link self::setKey() setKey()} is called, {@link self::hash() hash()} will
+ * return the HMAC as opposed to the hash.
  *
  * Here's a short example of how to use this library:
  * <code>
  * <?php
  *    include 'vendor/autoload.php';
  *
- *    $hash = new \phpseclib\Crypt\Hash('sha1');
+ *    $hash = new \phpseclib\Crypt\Hash('sha512');
  *
  *    $hash->setKey('abcdefg');
  *
@@ -31,7 +24,9 @@
  * @category  Crypt
  * @package   Hash
  * @author    Jim Wigginton <terrafrost@php.net>
- * @copyright 2007 Jim Wigginton
+ * @copyright 2015 Jim Wigginton
+ * @author    Andreas Fischer <bantu@phpbb.com>
+ * @copyright 2015 Andreas Fischer
  * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  * @link      http://phpseclib.sourceforge.net
  */
 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 <terrafrost@php.net>
+ * @author  Andreas Fischer <bantu@phpbb.com>
  * @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()
-     * @var string
-     * @access private
-     */
-    var $ipad;
-
-    /**
-     * Engine
+     * Used only for sha512/*
      *
-     * @see self::setHash()
+     * @see self::hash()
      * @var string
      * @access private
      */
-    var $engine;
+    var $ipad;
 
     /**
      * 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;
+            case 'sha512/224':
+                $this->length = 28;
                 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;
+            case 'sha256':
+            case 'sha512/256':
+                $this->length = 32;
                 break;
             case 'sha384':
+                $this->length = 48;
+                break;
             case 'sha512':
-                $this->engine = CRYPT_HASH_MODE == self::MODE_MHASH ? self::MODE_INTERNAL : CRYPT_HASH_MODE;
+                $this->length = 64;
                 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;
+                // 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;
                 }
-                $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';
+                // 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;
+                    }
                 }
-                $this->_computeKey(self::MODE_HASH);
-                return;
+                throw new UnsupportedAlgorithmException(
+                    "$hash is not a supported algorithm"
+                );
         }
 
-        switch ($hash) {
-            case 'md2':
-                $this->hash = array($this, '_md2');
-                break;
-            case 'md5':
-                $this->hash = array($this, '_md5');
-                break;
-            case 'sha256':
-                $this->hash = array($this, '_sha256');
-                break;
-            case 'sha384':
-            case 'sha512':
-                $this->hash = array($this, '_sha512');
-                break;
-            case 'sha1':
-            default:
-                $this->hash = array($this, '_sha1');
+        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->ipad = str_repeat(chr(0x36), $this->b);
-        $this->opad = str_repeat(chr(0x5C), $this->b);
-
-        $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."
+
+                    -- 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 substr($output, 0, $this->l);
+        return strlen($output) > $this->length
+            ? substr($output, 0, $this->length)
+            : $output;
     }
 
     /**
@@ -411,243 +284,20 @@ class Hash
      */
     function getLength()
     {
-        return $this->l;
-    }
-
-    /**
-     * Wrapper for MD5
-     *
-     * @access private
-     * @param string $m
-     */
-    function _md5($m)
-    {
-        return pack('H*', md5($m));
+        return $this->length;
     }
 
     /**
-     * Wrapper for SHA1
+     * Pure-PHP implementation of SHA512
      *
      * @access private
      * @param string $m
      */
-    function _sha1($m)
+    static function _sha512($m, $hash)
     {
-        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 <http://www.rfc-editor.org/errata_search.php?rfc=1319>, 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);
index b2b9d48eaa15ca8123065a6f3ae08f4d618399aa..648cf96a7c96a2f90810da344dc8f3fb8b96cb92 100644 (file)
@@ -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);
index 25e4ff854d55bfc7251e459ffaba1e5d8d799a25..3da70b6ea0fd918808a9c258da86fc5cf04ebb76 100644 (file)
@@ -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.
-     *
-     * 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:
+     * RC4 does not use an IV
      *
-     * {@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.
      *
index d2c6c7cbef31e7706218a92a706991e70f743420..38c2d390f337c37a1858b891d4f07852af9ef3a7 100644 (file)
@@ -8,36 +8,30 @@
  * Here's an example of how to encrypt and decrypt text with this library:
  * <code>
  * <?php
- *    include 'vendor/autoload.php';
+ * include 'vendor/autoload.php';
  *
- *    $rsa = new \phpseclib\Crypt\RSA();
- *    extract($rsa->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);
  * ?>
  * </code>
  *
  * Here's an example of how to create signatures and verify signatures with this library:
  * <code>
  * <?php
- *    include 'vendor/autoload.php';
+ * include 'vendor/autoload.php';
  *
- *    $rsa = new \phpseclib\Crypt\RSA();
- *    extract($rsa->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';
  * ?>
  * </code>
  *
@@ -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();
+    var $format = false;
 
     /**
-     * Current String
+     * OpenSSL configuration file name.
      *
-     * For use with parsing XML formatted keys.
+     * Set to null to use system configuration file.
      *
-     * @see self::_character_handler()
-     * @see self::_stop_element_handler()
+     * @see self::createKey()
      * @var mixed
+     * @access public
+     */
+    static $configFile;
+
+    /**
+     * Supported file formats (lower case)
+     *
+     * @see self::_initialize_static_variables()
+     * @var array
      * @access private
      */
-    var $current;
+    static $fileFormats = false;
 
     /**
-     * OpenSSL configuration file name.
+     * Supported file formats (original case)
      *
-     * Set to null to use system configuration file.
-     * @see self::createKey()
-     * @var mixed
-     * @Access public
+     * @see self::_initialize_static_variables()
+     * @var array
+     * @access private
      */
-    var $configFile;
+    static $origFileFormats = false;
+
 
     /**
-     * Public key comment field.
+     * Initialize static variables
      *
-     * @var string
      * @access private
      */
-    var $comment = 'phpseclib-generated-key';
+    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';
-
-        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);
-            }
-        }
+        self::_initialize_static_variables();
 
-        $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));
-
-        $generator = new BigInteger();
+        extract(self::_generateMinMax($temp));
 
-        $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,930 +584,62 @@ 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.
-     *
-     * @access private
-     * @see self::setPrivateKeyFormat()
-     * @param string $RSAPrivateKey
-     * @return string
-     */
-    function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
-    {
-        $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)
-        );
-
-        // 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 "<RSAKeyValue>\r\n" .
-                       '  <Modulus>' . base64_encode($raw['modulus']) . "</Modulus>\r\n" .
-                       '  <Exponent>' . base64_encode($raw['publicExponent']) . "</Exponent>\r\n" .
-                       '  <P>' . base64_encode($raw['prime1']) . "</P>\r\n" .
-                       '  <Q>' . base64_encode($raw['prime2']) . "</Q>\r\n" .
-                       '  <DP>' . base64_encode($raw['exponent1']) . "</DP>\r\n" .
-                       '  <DQ>' . base64_encode($raw['exponent2']) . "</DQ>\r\n" .
-                       '  <InverseQ>' . base64_encode($raw['coefficient']) . "</InverseQ>\r\n" .
-                       '  <D>' . base64_encode($raw['privateExponent']) . "</D>\r\n" .
-                       '</RSAKeyValue>';
-                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;
-        }
-    }
-
-    /**
-     * Convert a public key to the appropriate format
+     * Add a fileformat plugin
      *
-     * @access private
-     * @see self::setPublicKeyFormat()
-     * @param string $RSAPrivateKey
-     * @return string
-     */
-    function _convertPublicKey($n, $e)
-    {
-        $signed = $this->publicKeyFormat != self::PUBLIC_FORMAT_XML;
-
-        $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 "<RSAKeyValue>\r\n" .
-                       '  <Modulus>' . base64_encode($modulus) . "</Modulus>\r\n" .
-                       '  <Exponent>' . base64_encode($publicExponent) . "</Exponent>\r\n" .
-                       '</RSAKeyValue>';
-                break;
-            case self::PUBLIC_FORMAT_OPENSSH:
-                // from <http://tools.ietf.org/html/rfc4253#page-15>:
-                // 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 <http://tools.ietf.org/html/rfc3447#appendix-A.1.1>:
-                // 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;
-        }
-    }
-
-    /**
-     * Break a public or private key down into its constituant components
-     *
-     * @access private
-     * @see self::_convertPublicKey()
-     * @see self::_convertPrivateKey()
-     * @param string|array $key
-     * @param int $type
-     * @return array|bool
-     */
-    function _parseKey($key, $type)
-    {
-        if ($type != self::PUBLIC_FORMAT_RAW && !is_string($key)) {
-            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:
-
-                   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 <xml></xml> to account for "dangling" tags like <BitStrength>...</BitStrength> that are sometimes added
-                if (!xml_parse($xml, '<xml>' . $key . '</xml>')) {
-                    xml_parser_free($xml);
-                    unset($xml);
-                    return false;
-                }
-
-                xml_parser_free($xml);
-                unset($xml);
-
-                return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
-            // from PuTTY's SSHPUBK.C
-            case self::PRIVATE_FORMAT_PUTTY:
-                $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;
-        }
-    }
-
-    /**
-     * Returns the key size
-     *
-     * More specifically, this returns the size of the modulo in bits.
+     * 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 int
-     */
-    function getSize()
-    {
-        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
+     * @return bool
      */
-    function _start_element_handler($parser, $name, $attribs)
+    static function addFileFormat($fullname)
     {
-        //$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 = '';
-    }
+        self::_initialize_static_variables();
 
-    /**
-     * 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);
+        if (class_exists($fullname)) {
+            $meta = new \ReflectionClass($path);
+            $shortname = $meta->getShortName();
+            self::$fileFormats[strtolower($shortname)] = $fullname;
+            self::$origFileFormats[] = $shortname;
         }
     }
 
     /**
-     * Data Handler
-     *
-     * Called by xml_set_character_data_handler()
+     * Returns a list of supported formats.
      *
-     * @access private
-     * @param resource $parser
-     * @param string $data
+     * @access public
+     * @return array
      */
-    function _data_handler($parser, $data)
+    static function getSupportedFormats()
     {
-        if (!isset($this->current) || is_object($this->current)) {
-            return;
-        }
-        $this->current.= trim($data);
+        self::_initialize_static_variables();
+
+        return self::$origFileFormats;
     }
 
     /**
@@ -1635,11 +648,10 @@ 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|RSA|array $key
-     * @param bool|int $type optional
-     * @return bool
+     * @param string $key
+     * @param int $type optional
      */
-    function loadKey($key, $type = false)
+    function load($key, $type = false)
     {
         if ($key instanceof RSA) {
             $this->privateKeyFormat = $key->privateKeyFormat;
@@ -1648,11 +660,7 @@ class RSA
             $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());
@@ -1662,13 +670,13 @@ class RSA
             }
 
             if (is_object($key->modulus)) {
-                $this->modulus = $key->modulus->copy();
+                $this->modulus = clone $key->modulus;
             }
             if (is_object($key->exponent)) {
-                $this->exponent = $key->exponent->copy();
+                $this->exponent = clone $key->exponent;
             }
             if (is_object($key->publicExponent)) {
-                $this->publicExponent = $key->publicExponent->copy();
+                $this->publicExponent = clone $key->publicExponent;
             }
 
             $this->primes = array();
@@ -1676,53 +684,49 @@ class RSA
             $this->coefficients = array();
 
             foreach ($this->primes as $prime) {
-                $this->primes[] = $prime->copy();
+                $this->primes[] = clone $prime;
             }
             foreach ($this->exponents as $exponent) {
-                $this->exponents[] = $exponent->copy();
+                $this->exponents[] = clone $exponent;
             }
             foreach ($this->coefficients as $coefficient) {
-                $this->coefficients[] = $coefficient->copy();
+                $this->coefficients[] = clone $coefficient;
             }
 
             return true;
         }
 
+        $components = false;
         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);
+            foreach (self::$fileFormats as $format) {
+                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->comment = null;
-            $this->modulus = null;
-            $this->k = null;
-            $this->exponent = null;
-            $this->primes = null;
-            $this->exponents = null;
-            $this->coefficients = null;
-            $this->publicExponent = null;
-
+            $this->format = false;
             return false;
         }
 
-        if (isset($components['comment']) && $components['comment'] !== false) {
-            $this->comment = $components['comment'];
-        }
+        $this->format = $format;
+
         $this->modulus = $components['modulus'];
         $this->k = strlen($this->modulus->toBytes());
         $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
@@ -1738,22 +742,78 @@ class RSA
             $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();
-                }
+        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;
+    }
+
+    /**
+     * Returns the key size
+     *
+     * More specifically, this returns the size of the modulo in bits.
+     *
+     * @access public
+     * @return int
+     */
+    function getSize()
+    {
+        return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits());
+    }
+
     /**
      * 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;
-        }
-
-        // 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');
+        if ($lHash != $lHash2) {
+            return false;
+        }
+        $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,15 @@ 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;
         }
 
@@ -2963,22 +2023,19 @@ 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');
-            return false;
+        // 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
@@ -2986,100 +2043,134 @@ class RSA
     }
 
     /**
-     * Set Encryption Mode
+     * RSASSA-PKCS1-V1_5-VERIFY (relaxed matching)
      *
-     * Valid values include self::ENCRYPTION_OAEP and self::ENCRYPTION_PKCS1.
+     * 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 public
-     * @param int $mode
+     * @access private
+     * @param string $m
+     * @param string $s
+     * @return bool
      */
-    function setEncryptionMode($mode)
+    function _rsassa_pkcs1_v1_5_relaxed_verify($m, $s)
     {
-        $this->encryptionMode = $mode;
-    }
+        // Length checking
 
-    /**
-     * Set Signature Mode
-     *
-     * Valid values include self::SIGNATURE_PSS and self::SIGNATURE_PKCS1
-     *
-     * @access public
-     * @param int $mode
-     */
-    function setSignatureMode($mode)
-    {
-        $this->signatureMode = $mode;
-    }
+        if (strlen($s) != $this->k) {
+            return false;
+        }
 
-    /**
-     * Set public key comment.
-     *
-     * @access public
-     * @param string $comment
-     */
-    function setComment($comment)
-    {
-        $this->comment = $comment;
-    }
+        // RSA verification
 
-    /**
-     * Get public key comment.
-     *
-     * @access public
-     * @return string
-     */
-    function getComment()
-    {
-        return $this->comment;
+        $s = $this->_os2ip($s);
+        $m2 = $this->_rsavp1($s);
+        if ($m2 === false) {
+            return false;
+        }
+        $em = $this->_i2osp($m2, $this->k);
+        if ($em === false) {
+            return false;
+        }
+
+        if ($this->_string_shift($em, 2) != "\0\1") {
+            return false;
+        }
+
+        $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);
     }
 
     /**
      * 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';
-        }
-
-        foreach ($ciphertext as $c) {
-            $temp = $this->$decrypt($c);
-            if ($temp === false) {
-                return false;
-            }
-            $plaintext.= $temp;
+                return $this->_rsaes_oaep_decrypt($ciphertext);
         }
-
-        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;
-    }
 }
index 01e34cc367dddefe8502a4c2decb0182e165bdca..5e412e8fb8b441310dbeeafeb7b4f27b5480d8f6 100644 (file)
@@ -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 <http://php.net/ChangeLog-5.php#5.3.4>, "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']);
index 3648a1972f556c4ad23db693952f59c2500cd0d7..c98f02e252572560ee6c185628548f4b8c94d8b6 100644 (file)
@@ -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();
     }
index a2c41668ae4b1f597fb352a10edf9d1979add7ee..29c6eb9d0d4e5fe3787d97077bfeb27f0d01c7fa 100644 (file)
@@ -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));
index 70980a2ff1bff2ae67eefc81b6f4f7411963057a..e4d910db9427393a48df74cea836ffaa84b76794 100644 (file)
@@ -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(
index 5ff1f2ea1ab98a735724e42e9aa499aafaa79552..1f3eecb30a1b44a8df89ad29251523a4b8ab196a 100644 (file)
@@ -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++;
                     }
index 3aaa309003699b0622c330454298d03128a5df84..ad59f69bdbdfdc91e38880e8cae3163c49ffecbe 100644 (file)
 
 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;
-                    }
-                    /* "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.= $temp;
                     }
-                    $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+-].*)$#';
 
-        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];
-            }
-            $prefix = substr($content, 0, 2) >= 50 ? '19' : '20';
-            $content = $prefix . $content;
-        } elseif (strpos($content, '.') !== false) {
-            $format.= '.u';
-        }
+        preg_match($pattern, $content, $matches);
 
-        if ($content[strlen($content) - 1] == 'Z') {
-            $content = substr($content, 0, -1) . '+0000';
+        list(, $year, $month, $day, $hour, $minute, $second, $timezone) = $matches;
+
+        if ($tag == self::TYPE_UTC_TIME) {
+            $year = $year >= 50 ? "19$year" : "20$year";
         }
 
-        if (strpos($content, '-') !== false || strpos($content, '+') !== false) {
-            $format.= 'O';
+        if ($timezone == 'Z') {
+            $mktime = 'gmmktime';
+            $timezone = 0;
+        } elseif (preg_match('#([+-])(\d\d)(\d\d)#', $timezone, $matches)) {
+            $mktime = 'gmmktime';
+            $timezone = 60 * $matches[3] + 3600 * $matches[2];
+            if ($matches[1] == '-') {
+                $timezone = -$timezone;
+            }
+        } else {
+            $mktime = 'mktime';
+            $timezone = 0;
         }
 
-        // 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;
     }
 
     /**
index 4ebafa1710dfc6b6605539db58a8a172874316d4..984d1e678b16c3a723cf1a62c456c3cbdec068c9 100644 (file)
 
 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":
-
-                               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;
-                            }
-                    }
+            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":
+
+                           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;
     }
 
index 57e14d2f90f3de80982a98132640b011a2e10836..66fb48e4268ed0e50fab45aef7a9f4b24fa464c1 100644 (file)
  * @author    Jim Wigginton <terrafrost@php.net>
  * @copyright 2006 Jim Wigginton
  * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
+ * @link      http://pear.php.net/package/Math_BigInteger
  */
 
 namespace phpseclib\Math;
 
+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('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#', '', $x);
-                if (!strlen($x) || $x == '-') {
-                    $x = '0';
-                }
 
                 switch (MATH_BIGINTEGER_MODE) {
                     case self::MODE_GMP:
@@ -538,10 +498,10 @@ class BigInteger
                 return $this->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 <lluis _a_ pamies.cat>
      * @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 <lluis _a_ pamies.cat>
      * @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 <lluis _a_ pamies.cat>
      * @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 <http://php.net/gmp-nextprime>.
         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);
index cf13496cd43e048fc8308ac6392c84ae3cb1cf6e..4c28d8b0efc699fca0ef1128d9ef4911593bd126 100644 (file)
@@ -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');
                     }
                 }
         }
index a248c25819ca53e5914390016b11ec5cd9ffcb6c..a4d8c9e42eee33ea72ef835769aceb996d3350b7 100644 (file)
@@ -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()
index d2c4425deae3a0859a89725041ba4a706838028a..d19d08b83709db596aadc23921f6786bc6070815 100644 (file)
@@ -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);
index 514b20a2d97c2c1ab0590f4433a4e4fe60c4efde..2ed4a002a0fc95f8eb37ab76b907d6b3e9794247 100644 (file)
@@ -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:
index dd7698d5e310beed0a6c55dfd5c804aae9f2e6a5..97a37a0bf25b900cf0c12528dc3e86ced8ce8cda 100644 (file)
@@ -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?
-     *
-     * @var bool
-     * @access private
-     */
-    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
+     * Connection storage to replicates ssh2 extension functionality:
+     * {@link http://php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-examples}
      *
-     * @var array
-     * @access private
+     * @var SSH2[]
      */
-    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,30 +1019,19 @@ 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->identifier = $this->_generate_identifier();
+            $this->curTimeout-= $elapsed;
 
-        if ($this->send_id_string_first) {
-            fputs($this->fsock, $this->identifier . "\r\n");
+            if ($this->curTimeout <= 0) {
+                $this->is_timeout = true;
+                return false;
+            }
         }
 
         /* According to the SSH2 specs,
@@ -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 (!$this->send_id_string_first) {
-            fputs($this->fsock, $this->identifier . "\r\n");
+        if ($matches[3] != '1.99' && $matches[3] != '2.0') {
+            throw new \RuntimeException("Cannot connect to SSH $matches[1] servers");
         }
 
-        if (!$this->send_kex_first) {
-            $response = $this->_get_binary_packet();
-            if ($response === false) {
-                $this->bitmap = 0;
-                user_error('Connection closed by server');
-                return false;
-            }
+        fputs($this->fsock, $this->identifier . "\r\n");
 
-            if (!strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
-                user_error('Expected SSH_MSG_KEXINIT');
-                return false;
-            }
+        $response = $this->_get_binary_packet();
+        if ($response === false) {
+            throw new \RuntimeException('Connection closed by server');
+        }
 
-            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,148 +1302,100 @@ class SSH2
 
         $client_cookie = Random::string(16);
 
-        $kexinit_payload_client = pack(
-            'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
-            NET_SSH2_MSG_KEXINIT,
-            $client_cookie,
-            strlen($str_kex_algorithms),
-            $str_kex_algorithms,
-            strlen($str_server_host_key_algorithms),
-            $str_server_host_key_algorithms,
-            strlen($encryption_algorithms_client_to_server),
-            $encryption_algorithms_client_to_server,
-            strlen($encryption_algorithms_server_to_client),
-            $encryption_algorithms_server_to_client,
-            strlen($mac_algorithms_client_to_server),
-            $mac_algorithms_client_to_server,
-            strlen($mac_algorithms_server_to_client),
-            $mac_algorithms_server_to_client,
-            strlen($compression_algorithms_client_to_server),
-            $compression_algorithms_client_to_server,
-            strlen($compression_algorithms_server_to_client),
-            $compression_algorithms_server_to_client,
-            0,
-            '',
-            0,
-            '',
-            0,
-            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)) {
+        // 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,
+            $client_cookie,
+            strlen($str_kex_algorithms),
+            $str_kex_algorithms,
+            strlen($str_server_host_key_algorithms),
+            $str_server_host_key_algorithms,
+            strlen($encryption_algorithms_client_to_server),
+            $encryption_algorithms_client_to_server,
+            strlen($encryption_algorithms_server_to_client),
+            $encryption_algorithms_server_to_client,
+            strlen($mac_algorithms_client_to_server),
+            $mac_algorithms_client_to_server,
+            strlen($mac_algorithms_server_to_client),
+            $mac_algorithms_server_to_client,
+            strlen($compression_algorithms_client_to_server),
+            $compression_algorithms_client_to_server,
+            strlen($compression_algorithms_server_to_client),
+            $compression_algorithms_server_to_client,
+            0,
+            '',
+            0,
+            '',
+            0,
+            0
+        );
+
+        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);
-        }
-
-        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';
+            $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
+            throw new NoSupportedAlgorithmsException('No compatible server host key algorithms found');
         }
 
-        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,29 +1856,8 @@ 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
+    /**
+     * Login
      *
      * The $password parameter can be a plaintext password, a \phpseclib\Crypt\RSA object or an array
      *
@@ -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 : '<pre>' . $log . '</pre>';
+                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;
+    }
 }
index 99dcecfe6298948560a5b6d18937a37726bd54fe..23bf027a429acd8bbb361be2d39cd532badc584c 100644 (file)
@@ -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 <terrafrost@php.net>
- * @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 = '';
index b4649046a038e74afe3f114f0dd5459104e9a9a9..612c414e9050cc62f7db328207d17223b9a81b32 100644 (file)
@@ -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);
     }
 }
index 0da0999fd210da027eeb1bc49ba975a7ff6e11ae..bd4ba0b5a50986006e5e13c79f4a5c0e725991e3 100644 (file)
@@ -1,14 +1,18 @@
 <?php
+
 /**
  * Bootstrapping File for phpseclib
  *
+ * composer isn't a requirement for phpseclib 2.0 but this file isn't really required
+ * either. it's a bonus for those using composer but if you're not phpseclib will
+ * still work
+ *
  * @license http://www.opensource.org/licenses/mit-license.html MIT License
  */
-
 if (extension_loaded('mbstring')) {
     // 2 - MB_OVERLOAD_STRING
     if (ini_get('mbstring.func_overload') & 2) {
-        throw new \UnexpectedValueException(
+        throw new UnexpectedValueException(
             'Overloading of string functions using mbstring.func_overload ' .
             'is not supported by phpseclib.'
         );