[CORE] Downgrade phpseclib to a working state

This commit is contained in:
Diogo Cordeiro 2019-06-24 17:45:19 +01:00
parent c1c2a9f1a1
commit 411e8ed79d
33 changed files with 2365 additions and 5096 deletions

View File

@ -24,7 +24,7 @@
"michelf/php-markdown": "^1.8.0", "michelf/php-markdown": "^1.8.0",
"openid/php-openid": "^2.3", "openid/php-openid": "^2.3",
"paragonie/constant_time_encoding": "^1.0.4", "paragonie/constant_time_encoding": "^1.0.4",
"phpseclib/phpseclib": "^2.0.19", "phpseclib/phpseclib": "dev-master#f815e43077da67d3dd5b4d18a45753f5b79c1ab9",
"stomp-php/stomp-php": "^4.5.1" "stomp-php/stomp-php": "^4.5.1"
}, },
"require-dev": { "require-dev": {

73
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "1511cdda74eee145816ad9fe85138588", "content-hash": "ac49a57ede587e949b9bad4b3c080a1d",
"packages": [ "packages": [
{ {
"name": "apereo/phpcas", "name": "apereo/phpcas",
@ -461,21 +461,72 @@
"time": "2018-04-30T17:57:16+00:00" "time": "2018-04-30T17:57:16+00:00"
}, },
{ {
"name": "phpseclib/phpseclib", "name": "paragonie/random_compat",
"version": "2.0.19", "version": "v2.0.18",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpseclib/phpseclib.git", "url": "https://github.com/paragonie/random_compat.git",
"reference": "d2085db7b7394baa071a69c8f9159723c250f2ba" "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/d2085db7b7394baa071a69c8f9159723c250f2ba", "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0a58ef6e3146256cc3dc7cc393927bcc7d1b72db",
"reference": "d2085db7b7394baa071a69c8f9159723c250f2ba", "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.3" "php": ">=5.2.0"
},
"require-dev": {
"phpunit/phpunit": "4.*|5.*"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
},
"type": "library",
"autoload": {
"files": [
"lib/random.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [
"csprng",
"polyfill",
"pseudorandom",
"random"
],
"time": "2019-01-03T20:59:08+00:00"
},
{
"name": "phpseclib/phpseclib",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "f815e43077da67d3dd5b4d18a45753f5b79c1ab9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/f815e43077da67d3dd5b4d18a45753f5b79c1ab9",
"reference": "f815e43077da67d3dd5b4d18a45753f5b79c1ab9",
"shasum": ""
},
"require": {
"paragonie/constant_time_encoding": "^1",
"paragonie/random_compat": "^1.4|^2.0",
"php": ">=5.6.1"
}, },
"require-dev": { "require-dev": {
"phing/phing": "~2.7", "phing/phing": "~2.7",
@ -550,7 +601,7 @@
"x.509", "x.509",
"x509" "x509"
], ],
"time": "2019-06-20T03:34:11+00:00" "time": "2019-06-23T16:33:59+00:00"
}, },
{ {
"name": "stomp-php/stomp-php", "name": "stomp-php/stomp-php",
@ -5822,7 +5873,9 @@
], ],
"aliases": [], "aliases": [],
"minimum-stability": "dev", "minimum-stability": "dev",
"stability-flags": [], "stability-flags": {
"phpseclib/phpseclib": 20
},
"prefer-stable": true, "prefer-stable": true,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {

View File

@ -474,10 +474,22 @@ return array(
'phpseclib\\Crypt\\RC2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RC2.php', 'phpseclib\\Crypt\\RC2' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RC2.php',
'phpseclib\\Crypt\\RC4' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RC4.php', 'phpseclib\\Crypt\\RC4' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RC4.php',
'phpseclib\\Crypt\\RSA' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/RSA.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\\Random' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php',
'phpseclib\\Crypt\\Rijndael' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php', 'phpseclib\\Crypt\\Rijndael' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php',
'phpseclib\\Crypt\\TripleDES' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php', 'phpseclib\\Crypt\\TripleDES' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php',
'phpseclib\\Crypt\\Twofish' => $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Twofish.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\\ANSI' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ANSI.php',
'phpseclib\\File\\ASN1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1.php', 'phpseclib\\File\\ASN1' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1.php',
'phpseclib\\File\\ASN1\\Element' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php', 'phpseclib\\File\\ASN1\\Element' => $vendorDir . '/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php',

View File

@ -6,6 +6,7 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
'2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', '2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
'757772e28a0943a9afe83def8db95bdf' => $vendorDir . '/mf2/mf2/Mf2/Parser.php', '757772e28a0943a9afe83def8db95bdf' => $vendorDir . '/mf2/mf2/Mf2/Parser.php',
'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php', 'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php',

View File

@ -7,6 +7,7 @@ namespace Composer\Autoload;
class ComposerStaticInit444c3f31864f68a3f466e2c19837e185 class ComposerStaticInit444c3f31864f68a3f466e2c19837e185
{ {
public static $files = array ( public static $files = array (
'5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
'2cffec82183ee1cea088009cef9a6fc3' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', '2cffec82183ee1cea088009cef9a6fc3' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
'757772e28a0943a9afe83def8db95bdf' => __DIR__ . '/..' . '/mf2/mf2/Mf2/Parser.php', '757772e28a0943a9afe83def8db95bdf' => __DIR__ . '/..' . '/mf2/mf2/Mf2/Parser.php',
'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.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\\RC2' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RC2.php',
'phpseclib\\Crypt\\RC4' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RC4.php', 'phpseclib\\Crypt\\RC4' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RC4.php',
'phpseclib\\Crypt\\RSA' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/RSA.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\\Random' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php',
'phpseclib\\Crypt\\Rijndael' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php', 'phpseclib\\Crypt\\Rijndael' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php',
'phpseclib\\Crypt\\TripleDES' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php', 'phpseclib\\Crypt\\TripleDES' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/TripleDES.php',
'phpseclib\\Crypt\\Twofish' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/Crypt/Twofish.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\\ANSI' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ANSI.php',
'phpseclib\\File\\ASN1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1.php', 'phpseclib\\File\\ASN1' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1.php',
'phpseclib\\File\\ASN1\\Element' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php', 'phpseclib\\File\\ASN1\\Element' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/File/ASN1/Element.php',

View File

@ -470,22 +470,75 @@
] ]
}, },
{ {
"name": "phpseclib/phpseclib", "name": "paragonie/random_compat",
"version": "2.0.19", "version": "v2.0.18",
"version_normalized": "2.0.19.0", "version_normalized": "2.0.18.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpseclib/phpseclib.git", "url": "https://github.com/paragonie/random_compat.git",
"reference": "d2085db7b7394baa071a69c8f9159723c250f2ba" "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/d2085db7b7394baa071a69c8f9159723c250f2ba", "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0a58ef6e3146256cc3dc7cc393927bcc7d1b72db",
"reference": "d2085db7b7394baa071a69c8f9159723c250f2ba", "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.3" "php": ">=5.2.0"
},
"require-dev": {
"phpunit/phpunit": "4.*|5.*"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
},
"time": "2019-01-03T20:59:08+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"files": [
"lib/random.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [
"csprng",
"polyfill",
"pseudorandom",
"random"
]
},
{
"name": "phpseclib/phpseclib",
"version": "dev-master",
"version_normalized": "9999999-dev",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "f815e43077da67d3dd5b4d18a45753f5b79c1ab9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/f815e43077da67d3dd5b4d18a45753f5b79c1ab9",
"reference": "f815e43077da67d3dd5b4d18a45753f5b79c1ab9",
"shasum": ""
},
"require": {
"paragonie/constant_time_encoding": "^1",
"paragonie/random_compat": "^1.4|^2.0",
"php": ">=5.6.1"
}, },
"require-dev": { "require-dev": {
"phing/phing": "~2.7", "phing/phing": "~2.7",
@ -499,7 +552,7 @@
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", "ext-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." "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", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {

View File

@ -1,43 +1,22 @@
# phpseclib - PHP Secure Communications Library # 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 MIT-licensed pure-PHP implementations of an arbitrary-precision integer
arithmetic library, fully PKCS#1 (v2.1) compliant RSA, DES, 3DES, RC4, Rijndael, arithmetic library, fully PKCS#1 (v2.1) compliant RSA, DES, 3DES, RC4, Rijndael,
AES, Blowfish, Twofish, SSH-1, SSH-2, SFTP, and X.509 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) * [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
* [Documentation / Manual](http://phpseclib.sourceforge.net/) * [Documentation / Manual](http://phpseclib.sourceforge.net/)
* [API Documentation](https://api.phpseclib.org/2.0/) (generated by Sami) * [API Documentation](http://phpseclib.bantux.org/api/master/) (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)
## Support ## Support
@ -47,29 +26,40 @@ Need Support?
* [Create a Support Ticket on GitHub](https://github.com/phpseclib/phpseclib/issues/new) * [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) * [Browse the Support Forum](http://www.frostjedi.com/phpbb/viewforum.php?f=46) (no longer in use)
## Installing Development Dependencies
Dependencies are managed via Composer.
1. Download the [`composer.phar`](https://getcomposer.org/composer.phar) executable as per the
[Composer Download Instructions](https://getcomposer.org/download/), e.g. by running
``` sh
curl -sS https://getcomposer.org/installer | php
```
2. Install Dependencies
``` sh
php composer.phar install
```
## Contributing ## Contributing
1. Fork the Project 1. Fork the Project
2. Ensure you have Composer installed (see [Composer Download Instructions](https://getcomposer.org/download/)) 2. Install Development Dependencies
3. Install Development Dependencies 3. Create a Feature Branch
``` sh 4. (Recommended) Run the Test Suite
composer install
```
4. Create a Feature Branch
5. (Recommended) Run the Test Suite
``` sh ``` sh
vendor/bin/phpunit 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 ``` sh
vendor/bin/phing -f build/build.xml sniff vendor/bin/phing -f build/build.xml sniff
``` ```
7. Send us a Pull Request 6. Send us a Pull Request

View File

@ -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

View File

@ -51,11 +51,13 @@
} }
], ],
"require": { "require": {
"paragonie/constant_time_encoding": "^1|^2",
"paragonie/random_compat": "^1.4|^2.0",
"php": ">=5.3.3" "php": ">=5.3.3"
}, },
"require-dev": { "require-dev": {
"phing/phing": "~2.7", "phing/phing": "~2.7",
"phpunit/phpunit": "^4.8.35|^5.7|^6.0", "phpunit/phpunit": "~4.0",
"sami/sami": "~2.0", "sami/sami": "~2.0",
"squizlabs/php_codesniffer": "~2.0" "squizlabs/php_codesniffer": "~2.0"
}, },

View File

@ -66,30 +66,32 @@ class AES extends Rijndael
* @see \phpseclib\Crypt\Rijndael::setBlockLength() * @see \phpseclib\Crypt\Rijndael::setBlockLength()
* @access public * @access public
* @param int $length * @param int $length
* @throws \BadMethodCallException anytime it's called
*/ */
function setBlockLength($length) function setBlockLength($length)
{ {
return; throw new \BadMethodCallException('The block length cannot be set for AES.');
} }
/** /**
* Sets the key length * 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 * Valid key lengths are 128, 192, and 256. Set the link to bool(false) to disable a fixed key length
* 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
* *
* @see \phpseclib\Crypt\Rijndael:setKeyLength() * @see \phpseclib\Crypt\Rijndael:setKeyLength()
* @access public * @access public
* @param int $length * @param int $length
* @throws \LengthException if the key length isn't supported
*/ */
function setKeyLength($length) function setKeyLength($length)
{ {
switch ($length) { switch ($length) {
case 160: case 128:
$length = 192; case 192:
case 256:
break; break;
case 224: default:
$length = 256; throw new \LengthException('Key of size ' . $length . ' not supported by this algorithm. Only keys of sizes 128, 192 or 256 supported');
} }
parent::setKeyLength($length); parent::setKeyLength($length);
} }
@ -103,24 +105,19 @@ class AES extends Rijndael
* @see setKeyLength() * @see setKeyLength()
* @access public * @access public
* @param string $key * @param string $key
* @throws \LengthException if the key length isn't supported
*/ */
function setKey($key) function setKey($key)
{ {
parent::setKey($key); switch (strlen($key)) {
case 16:
if (!$this->explicit_key_length) { case 24:
$length = strlen($key); case 32:
switch (true) {
case $length <= 16:
$this->key_length = 16;
break;
case $length <= 24:
$this->key_length = 24;
break; break;
default: 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');
}
$this->_setEngine();
} }
parent::setKey($key);
} }
} }

View File

@ -76,10 +76,6 @@ abstract class Base
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
*/ */
const MODE_CFB = 3; const MODE_CFB = 3;
/**
* Encrypt / decrypt using the Cipher Feedback mode (8bit)
*/
const MODE_CFB8 = 38;
/** /**
* Encrypt / decrypt using the Output Feedback mode. * Encrypt / decrypt using the Output Feedback mode.
* *
@ -143,7 +139,7 @@ abstract class Base
* @var string * @var string
* @access private * @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 * The Initialization Vector
@ -152,7 +148,7 @@ abstract class Base
* @var string * @var string
* @access private * @access private
*/ */
var $iv; var $iv = false;
/** /**
* A "sliding" Initialization Vector * A "sliding" Initialization Vector
@ -433,15 +429,6 @@ abstract class Base
*/ */
var $openssl_options; var $openssl_options;
/**
* Has the key length explicitly been set or should it be derived from the key, itself?
*
* @see self::setKeyLength()
* @var bool
* @access private
*/
var $explicit_key_length = false;
/** /**
* Don't truncate / null pad key * Don't truncate / null pad key
* *
@ -452,9 +439,16 @@ abstract class Base
var $skip_key_adjustment = false; var $skip_key_adjustment = false;
/** /**
* Default Constructor. * Has the key length explicitly been set or should it be derived from the key, itself?
* *
* Determines whether or not the mcrypt extension should be used. * @see self::setKeyLength()
* @var bool
* @access private
*/
var $explicit_key_length = false;
/**
* Default Constructor.
* *
* $mode could be: * $mode could be:
* *
@ -468,54 +462,59 @@ abstract class Base
* *
* - self::MODE_OFB * - self::MODE_OFB
* *
* If not explicitly set, self::MODE_CBC will be used.
*
* @param int $mode * @param int $mode
* @access public * @access public
* @throws \InvalidArgumentException if an invalid / unsupported mode is provided
*/ */
function __construct($mode = self::MODE_CBC) function __construct($mode)
{ {
// $mode dependent settings // $mode dependent settings
switch ($mode) { switch ($mode) {
case self::MODE_ECB: case self::MODE_ECB:
case self::MODE_CBC:
$this->paddable = true; $this->paddable = true;
$this->mode = self::MODE_ECB;
break; break;
case self::MODE_CTR: case self::MODE_CTR:
case self::MODE_CFB: case self::MODE_CFB:
case self::MODE_CFB8:
case self::MODE_OFB: case self::MODE_OFB:
case self::MODE_STREAM: case self::MODE_STREAM:
$this->mode = $mode; $this->paddable = false;
break; break;
case self::MODE_CBC:
default: default:
$this->paddable = true; throw new \InvalidArgumentException('No valid mode has been specified');
$this->mode = self::MODE_CBC;
} }
$this->_setEngine(); $this->mode = $mode;
// Determining whether inline crypting can be used by the cipher // Determining whether inline crypting can be used by the cipher
if ($this->use_inline_crypt !== false) { if ($this->use_inline_crypt !== false && function_exists('create_function')) {
$this->use_inline_crypt = version_compare(PHP_VERSION, '5.3.0') >= 0 || 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 * setIV() is not required when self::MODE_ECB (or ie for AES: \phpseclib\Crypt\AES::MODE_ECB) is being used.
* to be all zero's.
* *
* @access public * @access public
* @param string $iv * @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 * @internal Can be overwritten by a sub class, but does not have to be
*/ */
function setIV($iv) function setIV($iv)
{ {
if ($this->mode == self::MODE_ECB) { 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; $this->iv = $iv;
@ -523,18 +522,14 @@ abstract class Base
} }
/** /**
* Sets the key length. * Returns whether or not the algorithm uses an IV
*
* Keys with explicitly set lengths need to be treated accordingly
* *
* @access public * @access public
* @param int $length * @return bool
*/ */
function setKeyLength($length) function usesIV()
{ {
$this->explicit_key_length = true; return true;
$this->changed = true;
$this->_setEngine();
} }
/** /**
@ -559,6 +554,24 @@ abstract class Base
return $this->block_size << 3; 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. * Sets the key.
* *
@ -575,12 +588,12 @@ abstract class Base
*/ */
function setKey($key) function setKey($key)
{ {
if (!$this->explicit_key_length) { if ($this->explicit_key_length !== false && strlen($key) != $this->explicit_key_length) {
$this->setKeyLength(strlen($key) << 3); throw new \LengthException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . strlen($key) . ' bytes');
$this->explicit_key_length = false;
} }
$this->key = $key; $this->key = $key;
$this->key_length = strlen($key);
$this->changed = true; $this->changed = true;
$this->_setEngine(); $this->_setEngine();
} }
@ -597,6 +610,7 @@ abstract class Base
* @see Crypt/Hash.php * @see Crypt/Hash.php
* @param string $password * @param string $password
* @param string $method * @param string $method
* @throws \LengthException if pbkdf1 is being used and the derived key length exceeds the hash length
* @return bool * @return bool
* @access public * @access public
* @internal Could, but not must, extend by the child Crypt_* class * @internal Could, but not must, extend by the child Crypt_* class
@ -623,7 +637,8 @@ abstract class Base
if (isset($func_args[5])) { if (isset($func_args[5])) {
$dkLen = $func_args[5]; $dkLen = $func_args[5];
} else { } 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) { switch (true) {
@ -631,8 +646,7 @@ abstract class Base
$hashObj = new Hash(); $hashObj = new Hash();
$hashObj->setHash($hash); $hashObj->setHash($hash);
if ($dkLen > $hashObj->getLength()) { if ($dkLen > $hashObj->getLength()) {
user_error('Derived key too long'); throw new \LengthException('Derived key length cannot be longer than the hash length');
return false;
} }
$t = $password . $salt; $t = $password . $salt;
for ($i = 0; $i < $count; ++$i) { for ($i = 0; $i < $count; ++$i) {
@ -649,10 +663,10 @@ abstract class Base
case !function_exists('hash_algos'): case !function_exists('hash_algos'):
case !in_array($hash, hash_algos()): case !in_array($hash, hash_algos()):
$i = 1; $i = 1;
while (strlen($key) < $dkLen) {
$hmac = new Hash(); $hmac = new Hash();
$hmac->setHash($hash); $hmac->setHash($hash);
$hmac->setKey($password); $hmac->setKey($password);
while (strlen($key) < $dkLen) {
$f = $u = $hmac->hash($salt . pack('N', $i++)); $f = $u = $hmac->hash($salt . pack('N', $i++));
for ($j = 2; $j <= $count; ++$j) { for ($j = 2; $j <= $count; ++$j) {
$u = $hmac->hash($u); $u = $hmac->hash($u);
@ -707,7 +721,7 @@ abstract class Base
case self::MODE_STREAM: case self::MODE_STREAM:
return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options); return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
case self::MODE_ECB: 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; return !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result;
case self::MODE_CBC: case self::MODE_CBC:
$result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV); $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); $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; return $ciphertext;
case self::MODE_OFB: case self::MODE_OFB:
return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer); return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer);
@ -789,7 +793,7 @@ abstract class Base
$this->changed = false; $this->changed = false;
} }
if ($this->enchanged) { 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; $this->enchanged = false;
} }
@ -822,15 +826,15 @@ abstract class Base
if ($len >= $block_size) { if ($len >= $block_size) {
if ($this->enbuffer['enmcrypt_init'] === false || $len > $this->cfb_init_len) { if ($this->enbuffer['enmcrypt_init'] === false || $len > $this->cfb_init_len) {
if ($this->enbuffer['enmcrypt_init'] === true) { 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; $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); $iv = substr($ciphertext, -$block_size);
$len%= $block_size; $len%= $block_size;
} else { } else {
while ($len >= $block_size) { 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; $ciphertext.= $iv;
$len-= $block_size; $len-= $block_size;
$i+= $block_size; $i+= $block_size;
@ -839,7 +843,7 @@ abstract class Base
} }
if ($len) { if ($len) {
$iv = @mcrypt_generic($this->ecb, $iv); $iv = mcrypt_generic($this->ecb, $iv);
$block = $iv ^ substr($plaintext, -$len); $block = $iv ^ substr($plaintext, -$len);
$iv = substr_replace($iv, $block, 0, $len); $iv = substr_replace($iv, $block, 0, $len);
$ciphertext.= $block; $ciphertext.= $block;
@ -849,10 +853,10 @@ abstract class Base
return $ciphertext; return $ciphertext;
} }
$ciphertext = @mcrypt_generic($this->enmcrypt, $plaintext); $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
if (!$this->continuousBuffer) { 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; return $ciphertext;
@ -957,24 +961,6 @@ abstract class Base
$pos = $len; $pos = $len;
} }
break; 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: case self::MODE_OFB:
$xor = $this->encryptIV; $xor = $this->encryptIV;
if (strlen($buffer['xor'])) { if (strlen($buffer['xor'])) {
@ -1019,14 +1005,13 @@ abstract class Base
* @access public * @access public
* @param string $ciphertext * @param string $ciphertext
* @return string $plaintext * @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 * @internal Could, but not must, extend by the child Crypt_* class
*/ */
function decrypt($ciphertext) function decrypt($ciphertext)
{ {
if ($this->paddable) { if ($this->paddable && strlen($ciphertext) % $this->block_size) {
// we pad with chr(0) since that's what mcrypt_generic does. to quote from {@link http://www.php.net/function.mcrypt-generic}: throw new \LengthException('The ciphertext length (' . strlen($ciphertext) . ') needs to be a multiple of the block size (' . $this->block_size . ')');
// "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->engine === self::ENGINE_OPENSSL) { if ($this->engine === self::ENGINE_OPENSSL) {
@ -1040,14 +1025,14 @@ abstract class Base
break; break;
case self::MODE_ECB: case self::MODE_ECB:
if (!defined('OPENSSL_RAW_DATA')) { 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); $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
break; break;
case self::MODE_CBC: case self::MODE_CBC:
if (!defined('OPENSSL_RAW_DATA')) { if (!defined('OPENSSL_RAW_DATA')) {
$padding = str_repeat(chr($this->block_size), $this->block_size) ^ substr($ciphertext, -$this->block_size); $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; $offset = 2 * $this->block_size;
} else { } else {
$offset = $this->block_size; $offset = $this->block_size;
@ -1105,16 +1090,6 @@ abstract class Base
$iv = substr($ciphertext, -$this->block_size); $iv = substr($ciphertext, -$this->block_size);
} }
break; 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: case self::MODE_OFB:
$plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer); $plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer);
} }
@ -1129,7 +1104,7 @@ abstract class Base
$this->changed = false; $this->changed = false;
} }
if ($this->dechanged) { 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; $this->dechanged = false;
} }
@ -1157,12 +1132,12 @@ abstract class Base
} }
if ($len >= $block_size) { if ($len >= $block_size) {
$cb = substr($ciphertext, $i, $len - $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); $iv = substr($cb, -$block_size);
$len%= $block_size; $len%= $block_size;
} }
if ($len) { if ($len) {
$iv = @mcrypt_generic($this->ecb, $iv); $iv = mcrypt_generic($this->ecb, $iv);
$plaintext.= $iv ^ substr($ciphertext, -$len); $plaintext.= $iv ^ substr($ciphertext, -$len);
$iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len); $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len);
$pos = $len; $pos = $len;
@ -1171,10 +1146,10 @@ abstract class Base
return $plaintext; return $plaintext;
} }
$plaintext = @mdecrypt_generic($this->demcrypt, $ciphertext); $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
if (!$this->continuousBuffer) { 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; return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
@ -1278,24 +1253,6 @@ abstract class Base
$pos = $len; $pos = $len;
} }
break; 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: case self::MODE_OFB:
$xor = $this->decryptIV; $xor = $this->decryptIV;
if (strlen($buffer['xor'])) { if (strlen($buffer['xor'])) {
@ -1329,13 +1286,29 @@ abstract class Base
return $this->paddable ? $this->_unpad($plaintext) : $plaintext; 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 * OpenSSL CTR Processor
* *
* PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream * 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() * 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 * 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::encrypt()
* @see self::decrypt() * @see self::decrypt()
@ -1358,7 +1331,7 @@ abstract class Base
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size); $block = substr($plaintext, $i, $block_size);
if (strlen($block) > strlen($buffer['ciphertext'])) { 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; $result = !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result;
$buffer['ciphertext'].= $result; $buffer['ciphertext'].= $result;
} }
@ -1369,7 +1342,7 @@ abstract class Base
} else { } else {
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($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; $otp = !defined('OPENSSL_RAW_DATA') ? substr($otp, 0, -$this->block_size) : $otp;
$this->_increment_str($xor); $this->_increment_str($xor);
$ciphertext.= $block ^ $otp; $ciphertext.= $block ^ $otp;
@ -1413,7 +1386,7 @@ abstract class Base
} }
if ($this->continuousBuffer) { if ($this->continuousBuffer) {
if (!defined('OPENSSL_RAW_DATA')) { 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); $encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
if ($overflow) { if ($overflow) {
@ -1496,8 +1469,6 @@ abstract class Base
return 'ctr'; return 'ctr';
case self::MODE_CFB: case self::MODE_CFB:
return 'cfb'; return 'cfb';
case self::MODE_CFB8:
return 'cfb8';
case self::MODE_OFB: case self::MODE_OFB:
return 'ofb'; return 'ofb';
} }
@ -1656,7 +1627,7 @@ abstract class Base
case self::ENGINE_MCRYPT: case self::ENGINE_MCRYPT:
return $this->cipher_name_mcrypt && return $this->cipher_name_mcrypt &&
extension_loaded('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: case self::ENGINE_INTERNAL:
return true; return true;
} }
@ -1735,13 +1706,13 @@ abstract class Base
if ($this->engine != self::ENGINE_MCRYPT && $this->enmcrypt) { if ($this->engine != self::ENGINE_MCRYPT && $this->enmcrypt) {
// Closing the current mcrypt resource(s). _mcryptSetup() will, if needed, // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed,
// (re)open them with the module named in $this->cipher_name_mcrypt // (re)open them with the module named in $this->cipher_name_mcrypt
@mcrypt_module_close($this->enmcrypt); mcrypt_module_close($this->enmcrypt);
@mcrypt_module_close($this->demcrypt); mcrypt_module_close($this->demcrypt);
$this->enmcrypt = null; $this->enmcrypt = null;
$this->demcrypt = null; $this->demcrypt = null;
if ($this->ecb) { if ($this->ecb) {
@mcrypt_module_close($this->ecb); mcrypt_module_close($this->ecb);
$this->ecb = null; $this->ecb = null;
} }
} }
@ -1851,24 +1822,23 @@ abstract class Base
self::MODE_ECB => MCRYPT_MODE_ECB, self::MODE_ECB => MCRYPT_MODE_ECB,
self::MODE_CBC => MCRYPT_MODE_CBC, self::MODE_CBC => MCRYPT_MODE_CBC,
self::MODE_CFB => 'ncfb', self::MODE_CFB => 'ncfb',
self::MODE_CFB8 => MCRYPT_MODE_CFB,
self::MODE_OFB => MCRYPT_MODE_NOFB, self::MODE_OFB => MCRYPT_MODE_NOFB,
self::MODE_STREAM => MCRYPT_MODE_STREAM, self::MODE_STREAM => MCRYPT_MODE_STREAM,
); );
$this->demcrypt = @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], ''); $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() // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer()
// to workaround mcrypt's broken ncfb implementation in buffered mode // to workaround mcrypt's broken ncfb implementation in buffered mode
// see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps}
if ($this->mode == self::MODE_CFB) { 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? } // else should mcrypt_generic_deinit be called?
if ($this->mode == self::MODE_CFB) { 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() * @see self::_unpad()
* @param string $text * @param string $text
* @throws \LengthException if padding is disabled and the plaintext's length is not a multiple of the block size
* @access private * @access private
* @return string * @return string
*/ */
@ -1895,8 +1866,7 @@ abstract class Base
if ($length % $this->block_size == 0) { if ($length % $this->block_size == 0) {
return $text; return $text;
} else { } else {
user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})"); throw new \LengthException("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size}). Try enabling padding.");
$this->padding = true;
} }
} }
@ -1913,6 +1883,7 @@ abstract class Base
* *
* @see self::_pad() * @see self::_pad()
* @param string $text * @param string $text
* @throws \LengthException if the ciphertext's length is not a multiple of the block size
* @access private * @access private
* @return string * @return string
*/ */
@ -1925,7 +1896,7 @@ abstract class Base
$length = ord($text[strlen($text) - 1]); $length = ord($text[strlen($text) - 1]);
if (!$length || $length > $this->block_size) { 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); return substr($text, 0, -$length);
@ -1938,20 +1909,19 @@ abstract class Base
* after disableContinuousBuffer() or on cipher $engine (re)init * after disableContinuousBuffer() or on cipher $engine (re)init
* ie after setKey() or setIV() * ie after setKey() or setIV()
* *
* @access public * @access private
* @internal Could, but not must, extend by the child Crypt_* class * @internal Could, but not must, extend by the child Crypt_* class
* @throws \UnexpectedValueException when an IV is required but not defined
*/ */
function _clearBuffers() function _clearBuffers()
{ {
$this->enbuffer = $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); $this->enbuffer = $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true);
// mcrypt's handling of invalid's $iv: if ($this->iv === false && !in_array($this->mode, array(self::MODE_STREAM, self::MODE_ECB))) {
// $this->encryptIV = $this->decryptIV = strlen($this->iv) == $this->block_size ? $this->iv : str_repeat("\0", $this->block_size); throw new \UnexpectedValueException('No IV has been defined');
$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");
} }
$this->encryptIV = $this->decryptIV = $this->iv;
} }
/** /**
@ -2423,52 +2393,6 @@ abstract class Base
$_pos = $_len; $_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; return $_plaintext;
'; ';
break; break;
@ -2602,11 +2526,6 @@ abstract class Base
} }
// Create the $inline function and return its name as string. Ready to run! // 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 . ' }'); 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. * is stored, classwide (!), here for reusing.
* *
* The string-based index of $function is a classwide * 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) * operation (or more... depends of the optimizing level)
* for which $mode the lambda function was created. * for which $mode the lambda function was created.
* *
@ -2659,50 +2578,10 @@ abstract class Base
$len = strlen($bytes); $len = strlen($bytes);
for ($i = 0; $i < $len; $i+=20) { for ($i = 0; $i < $len; $i+=20) {
$t = substr($bytes, $i, 20); $t = substr($bytes, $i, 20);
$hash = pack('H*', sha1($hash)); $hash = sha1($hash, true);
$result .= $t ^ $hash; $result .= $t ^ $hash;
} }
return $result . pack('H*', sha1($hash)); return $result . sha1($hash, true);
}
}
/**
* 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))';
} }
} }
} }

View File

@ -283,6 +283,22 @@ class Blowfish extends Base
*/ */
var $key_length = 16; 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. * Sets the key length.
* *
@ -293,14 +309,12 @@ class Blowfish extends Base
*/ */
function setKeyLength($length) function setKeyLength($length)
{ {
if ($length < 32) { if ($length < 32 || $length > 448) {
$this->key_length = 4; 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');
} elseif ($length > 448) {
$this->key_length = 56;
} else {
$this->key_length = $length >> 3;
} }
$this->key_length = $length >> 3;
parent::setKeyLength($length); parent::setKeyLength($length);
} }
@ -317,10 +331,7 @@ class Blowfish extends Base
function isValidEngine($engine) function isValidEngine($engine)
{ {
if ($engine == self::ENGINE_OPENSSL) { if ($engine == self::ENGINE_OPENSSL) {
if (version_compare(PHP_VERSION, '5.3.7') < 0 && $this->key_length != 16) { if ($this->key_length != 16) {
return false;
}
if ($this->key_length < 16) {
return false; return false;
} }
$this->cipher_name_openssl_ecb = 'bf-ecb'; $this->cipher_name_openssl_ecb = 'bf-ecb';
@ -408,14 +419,16 @@ class Blowfish extends Base
for ($i = 0; $i < 16; $i+= 2) { for ($i = 0; $i < 16; $i+= 2) {
$l^= $p[$i]; $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_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff]); $sb_3[$l & 0xff];
$r^= $p[$i + 1]; $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_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff]); $sb_3[$r & 0xff];
} }
return pack("N*", $r ^ $p[17], $l ^ $p[16]); return pack("N*", $r ^ $p[17], $l ^ $p[16]);
} }
@ -441,14 +454,16 @@ class Blowfish extends Base
for ($i = 17; $i > 2; $i-= 2) { for ($i = 17; $i > 2; $i-= 2) {
$l^= $p[$i]; $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_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff]); $sb_3[$l & 0xff];
$r^= $p[$i - 1]; $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_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff]); $sb_3[$r & 0xff];
} }
return pack("N*", $r ^ $p[0], $l ^ $p[1]); 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); $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
} }
$safeint = $this->safe_intval_inline();
if (!isset($lambda_functions[$code_hash])) { if (!isset($lambda_functions[$code_hash])) {
switch (true) { switch (true) {
case $gen_hi_opt_code: case $gen_hi_opt_code:
@ -511,14 +524,16 @@ class Blowfish extends Base
for ($i = 0; $i < 16; $i+= 2) { for ($i = 0; $i < 16; $i+= 2) {
$encrypt_block.= ' $encrypt_block.= '
$l^= ' . $p[$i] . '; $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_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff]') . '; $sb_3[$l & 0xff];
$r^= ' . $p[$i + 1] . '; $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_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff]') . '; $sb_3[$r & 0xff];
'; ';
} }
$encrypt_block.= ' $encrypt_block.= '
@ -538,14 +553,16 @@ class Blowfish extends Base
for ($i = 17; $i > 2; $i-= 2) { for ($i = 17; $i > 2; $i-= 2) {
$decrypt_block.= ' $decrypt_block.= '
$l^= ' . $p[$i] . '; $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_2[$l >> 8 & 0xff]) +
$sb_3[$l & 0xff]') . '; $sb_3[$l & 0xff];
$r^= ' . $p[$i - 1] . '; $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_2[$r >> 8 & 0xff]) +
$sb_3[$r & 0xff]') . '; $sb_3[$r & 0xff];
'; ';
} }

View File

@ -578,6 +578,22 @@ class DES extends Base
0x00000820, 0x00020020, 0x08000000, 0x08020800 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 * Test for engine validity
* *
@ -603,24 +619,18 @@ class DES extends Base
/** /**
* Sets the key. * Sets the key.
* *
* Keys can be of any length. DES, itself, uses 64-bit keys (eg. strlen($key) == 8), however, we * Keys must be 64-bits long or 8 bytes long.
* 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.
* *
* DES also requires that every eighth bit be a parity bit, however, we'll ignore that. * 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() * @see \phpseclib\Crypt\Base::setKey()
* @access public * @access public
* @param string $key * @param string $key
*/ */
function setKey($key) function setKey($key)
{ {
// We check/cut here only up to max length of the key. if (!($this instanceof TripleDES) && strlen($key) != 8) {
// Key padding to the proper length will be done in _setupKey() throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of size 8 are supported');
if (strlen($key) > $this->key_length_max) {
$key = substr($key, 0, $this->key_length_max);
} }
// Sets the key // 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 // 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 ); $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}"; $code_hash = "Crypt_DES, $des_rounds, {$this->mode}";
if ($gen_hi_opt_code) { if ($gen_hi_opt_code) {
// For hi-optimized code, we create for each combination of // 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::ENCRYPT][$i] = '$ke[' . $i . ']';
$k[self::DECRYPT][$i] = '$kd[' . $i . ']'; $k[self::DECRYPT][$i] = '$kd[' . $i . ']';
} }
$init_encrypt = '$ke = $self->keys[$self::ENCRYPT];'; $init_encrypt = '$ke = $self->keys[self::ENCRYPT];';
$init_decrypt = '$kd = $self->keys[$self::DECRYPT];'; $init_decrypt = '$kd = $self->keys[self::DECRYPT];';
break; break;
} }

View File

@ -1,26 +1,19 @@
<?php <?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: * If {@link self::setKey() setKey()} is called, {@link self::hash() hash()} will
* * return the HMAC as opposed to the hash.
* 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}.}}
* *
* Here's a short example of how to use this library: * Here's a short example of how to use this library:
* <code> * <code>
* <?php * <?php
* include 'vendor/autoload.php'; * include 'vendor/autoload.php';
* *
* $hash = new \phpseclib\Crypt\Hash('sha1'); * $hash = new \phpseclib\Crypt\Hash('sha512');
* *
* $hash->setKey('abcdefg'); * $hash->setKey('abcdefg');
* *
@ -31,7 +24,9 @@
* @category Crypt * @category Crypt
* @package Hash * @package Hash
* @author Jim Wigginton <terrafrost@php.net> * @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 * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -39,34 +34,16 @@
namespace phpseclib\Crypt; namespace phpseclib\Crypt;
use phpseclib\Math\BigInteger; 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 * @package Hash
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @author Andreas Fischer <bantu@phpbb.com>
* @access public * @access public
*/ */
class Hash 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 * Hash Parameter
* *
@ -76,15 +53,6 @@ class Hash
*/ */
var $hashParam; 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) * Byte-length of hash output (Internal HMAC)
* *
@ -92,7 +60,7 @@ class Hash
* @var int * @var int
* @access private * @access private
*/ */
var $l = false; var $length;
/** /**
* Hash Algorithm * Hash Algorithm
@ -113,18 +81,22 @@ class Hash
var $key = false; var $key = false;
/** /**
* Computed Key * Initial Hash
* *
* @see self::_computeKey() * Used only for sha512/*
* @var string *
* @see self::_sha512()
* @var array
* @access private * @access private
*/ */
var $computedKey = false; var $initial = false;
/** /**
* Outer XOR (Internal HMAC) * Outer XOR (Internal HMAC)
* *
* @see self::setKey() * Used only for sha512/*
*
* @see self::hash()
* @var string * @var string
* @access private * @access private
*/ */
@ -133,44 +105,26 @@ class Hash
/** /**
* Inner XOR (Internal HMAC) * Inner XOR (Internal HMAC)
* *
* @see self::setKey() * Used only for sha512/*
*
* @see self::hash()
* @var string * @var string
* @access private * @access private
*/ */
var $ipad; var $ipad;
/**
* Engine
*
* @see self::setHash()
* @var string
* @access private
*/
var $engine;
/** /**
* Default Constructor. * Default Constructor.
* *
* @param string $hash * @param string $hash
* @return \phpseclib\Crypt\Hash
* @access public * @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->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) function setKey($key = false)
{ {
$this->key = $key; $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); $this->hashParam = $hash = strtolower($hash);
switch ($hash) { switch ($hash) {
case 'md2-96':
case 'md5-96': case 'md5-96':
case 'sha1-96': case 'sha1-96':
case 'sha256-96': case 'sha256-96':
case 'sha512-96': case 'sha512-96':
case 'sha512/224-96':
case 'sha512/256-96':
$hash = substr($hash, 0, -3); $hash = substr($hash, 0, -3);
$this->l = 12; // 96 / 8 = 12 $this->length = 12; // 96 / 8 = 12
break; break;
case 'md2': case 'md2':
case 'md5': case 'md5':
$this->l = 16; $this->length = 16;
break; break;
case 'sha1': case 'sha1':
$this->l = 20; $this->length = 20;
break; 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 'sha224':
case 'sha256': case 'sha512/224':
$this->b = 64; $this->length = 28;
break; break;
default: case 'sha256':
$this->b = 128; case 'sha512/256':
} $this->length = 32;
switch ($hash) {
case 'md2':
$this->engine = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ?
self::MODE_HASH : self::MODE_INTERNAL;
break; break;
case 'sha384': case 'sha384':
$this->length = 48;
break;
case 'sha512': case 'sha512':
$this->engine = CRYPT_HASH_MODE == self::MODE_MHASH ? self::MODE_INTERNAL : CRYPT_HASH_MODE; $this->length = 64;
break; break;
default: default:
$this->engine = CRYPT_HASH_MODE; // see if the hash isn't "officially" supported see if it can
// be "unofficially" supported and calculate the length
// accordingly.
if (in_array($hash, hash_algos())) {
$this->length = strlen(hash($hash, '', true));
break;
}
// if the hash algorithm doens't exist maybe it's a truncated
// hash, e.g. whirlpool-12 or some such.
if (preg_match('#(-\d+)$#', $hash, $matches)) {
$hash = substr($hash, 0, -strlen($matches[1]));
if (in_array($hash, hash_algos())) {
$this->length = abs($matches[1]) >> 3;
break;
}
}
throw new UnsupportedAlgorithmException(
"$hash is not a supported algorithm"
);
} }
switch ($this->engine) { if ($hash == 'sha512/224' || $hash == 'sha512/256') {
case self::MODE_MHASH: // from http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf#page=24
switch ($hash) { $this->initial = $hash == 'sha512/256' ?
case 'md5': array(
$this->hash = MHASH_MD5; '22312194FC2BF72C', '9F555FA3C84C64C2', '2393B86B6F53B151', '963877195940EABD',
break; '96283EE2A88EFFE3', 'BE5E1E2553863992', '2B0199FC2C85B8AA', '0EB72DDC81C52CA2'
case 'sha256': ) :
$this->hash = MHASH_SHA256; array(
break; '8C3D37C819544DA2', '73E1996689DCD4D6', '1DFAB7AE32FF9C82', '679DD514582F9FCF',
case 'sha1': '0F6D2B697BD44DA8', '77E36F7304C48942', '3F9D85A86A1D36C8', '1112E6AD91D692A1'
default: );
$this->hash = MHASH_SHA1; for ($i = 0; $i < 8; $i++) {
$this->initial[$i] = new BigInteger($this->initial[$i], 16);
$this->initial[$i]->setPrecision(64);
} }
$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; $this->hash = $hash;
return;
case 'sha1':
default:
$this->hash = 'sha1';
}
$this->_computeKey(self::MODE_HASH);
return;
}
switch ($hash) {
case 'md2':
$this->hash = array($this, '_md2');
break;
case 'md5':
$this->hash = array($this, '_md5');
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');
}
$this->ipad = str_repeat(chr(0x36), $this->b);
$this->opad = str_repeat(chr(0x5C), $this->b);
$this->_computeKey(self::MODE_INTERNAL);
} }
/** /**
@ -370,37 +245,35 @@ class Hash
*/ */
function hash($text) function hash($text)
{ {
if (!empty($this->key) || is_string($this->key)) { switch ($this->hash) {
switch ($this->engine) { case 'sha512/224':
case self::MODE_MHASH: case 'sha512/256':
$output = mhash($this->hash, $text, $this->computedKey); if (empty($this->key) || !is_string($this->key)) {
break; return substr(self::_sha512($text, $this->initial), 0, $this->length);
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);
}
} }
/* "Applications that use keys longer than B bytes will first hash the key using H and then use the
resultant L byte string as the actual key to HMAC."
return substr($output, 0, $this->l); -- http://tools.ietf.org/html/rfc2104#section-2 */
$key = strlen($this->key) > $this->b ? self::_sha512($this->key, $this->initial) : $this->key;
$key = str_pad($this->key, 128, chr(0)); // step 1
$temp = $this->ipad ^ $this->key; // step 2
$temp .= $text; // step 3
$temp = self::_sha512($temp, $this->initial); // step 4
$output = $this->opad ^ $this->key; // step 5
$output.= $temp; // step 6
$output = self::_sha512($output, $this->initial); // step 7
return substr($output, 0, $this->length);
}
$output = !empty($this->key) || is_string($this->key) ?
hash_hmac($this->hash, $text, $this->key, true) :
hash($this->hash, $text, true);
return strlen($output) > $this->length
? substr($output, 0, $this->length)
: $output;
} }
/** /**
@ -411,243 +284,20 @@ class Hash
*/ */
function getLength() function getLength()
{ {
return $this->l; return $this->length;
} }
/** /**
* Wrapper for MD5 * Pure-PHP implementation of SHA512
* *
* @access private * @access private
* @param string $m * @param string $m
*/ */
function _md5($m) static function _sha512($m, $hash)
{ {
return pack('H*', md5($m)); static $k;
}
/**
* Wrapper for SHA1
*
* @access private
* @param string $m
*/
function _sha1($m)
{
return pack('H*', sha1($m));
}
/**
* Pure-PHP implementation of MD2
*
* See {@link http://tools.ietf.org/html/rfc1319 RFC1319}.
*
* @access private
* @param string $m
*/
function _md2($m)
{
static $s = array(
41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188,
76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24,
138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251,
245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63,
148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50,
39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165,
181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210,
150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157,
112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27,
96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15,
85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197,
234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65,
129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123,
8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233,
203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228,
166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237,
31, 26, 219, 153, 141, 51, 159, 17, 131, 20
);
// Step 1. Append Padding Bytes
$pad = 16 - (strlen($m) & 0xF);
$m.= str_repeat(chr($pad), $pad);
$length = strlen($m);
// Step 2. Append Checksum
$c = str_repeat(chr(0), 16);
$l = chr(0);
for ($i = 0; $i < $length; $i+= 16) {
for ($j = 0; $j < 16; $j++) {
// RFC1319 incorrectly states that C[j] should be set to S[c xor L]
//$c[$j] = chr($s[ord($m[$i + $j] ^ $l)]);
// per <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;
if (!isset($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 // Initialize table of round constants
// (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409) // (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409)
$k = array( $k = array(
@ -678,8 +328,6 @@ class Hash
} }
} }
$hash = $this->l == 48 ? $init384 : $init512;
// Pre-processing // Pre-processing
$length = strlen($m); $length = strlen($m);
// to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128 // 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) { foreach ($chunks as $chunk) {
$w = array(); $w = array();
for ($i = 0; $i < 16; $i++) { 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); $temp->setPrecision(64);
$w[] = $temp; $w[] = $temp;
} }
@ -714,21 +362,21 @@ class Hash
); );
$s1 = $temp[0]->bitwise_xor($temp[1]); $s1 = $temp[0]->bitwise_xor($temp[1]);
$s1 = $s1->bitwise_xor($temp[2]); $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($s0);
$w[$i] = $w[$i]->add($w[$i - 7]); $w[$i] = $w[$i]->add($w[$i - 7]);
$w[$i] = $w[$i]->add($s1); $w[$i] = $w[$i]->add($s1);
} }
// Initialize hash value for this chunk // Initialize hash value for this chunk
$a = $hash[0]->copy(); $a = clone $hash[0];
$b = $hash[1]->copy(); $b = clone $hash[1];
$c = $hash[2]->copy(); $c = clone $hash[2];
$d = $hash[3]->copy(); $d = clone $hash[3];
$e = $hash[4]->copy(); $e = clone $hash[4];
$f = $hash[5]->copy(); $f = clone $hash[5];
$g = $hash[6]->copy(); $g = clone $hash[6];
$h = $hash[7]->copy(); $h = clone $hash[7];
// Main loop // Main loop
for ($i = 0; $i < 80; $i++) { for ($i = 0; $i < 80; $i++) {
@ -765,13 +413,13 @@ class Hash
$t1 = $t1->add($k[$i]); $t1 = $t1->add($k[$i]);
$t1 = $t1->add($w[$i]); $t1 = $t1->add($w[$i]);
$h = $g->copy(); $h = clone $g;
$g = $f->copy(); $g = clone $f;
$f = $e->copy(); $f = clone $e;
$e = $d->add($t1); $e = $d->add($t1);
$d = $c->copy(); $d = clone $c;
$c = $b->copy(); $c = clone $b;
$b = $a->copy(); $b = clone $a;
$a = $t1->add($t2); $a = $t1->add($t2);
} }
@ -791,90 +439,11 @@ class Hash
// Produce the final hash value (big-endian) // 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) // (\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() . $temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() .
$hash[4]->toBytes() . $hash[5]->toBytes(); $hash[4]->toBytes() . $hash[5]->toBytes() . $hash[6]->toBytes() . $hash[7]->toBytes();
if ($this->l != 48) {
$temp.= $hash[6]->toBytes() . $hash[7]->toBytes();
}
return $temp; 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 * String Shift
* *
@ -885,7 +454,7 @@ class Hash
* @return string * @return string
* @access private * @access private
*/ */
function _string_shift(&$string, $index = 1) static function _string_shift(&$string, $index = 1)
{ {
$substr = substr($string, 0, $index); $substr = substr($string, 0, $index);
$string = substr($string, $index); $string = substr($string, $index);

View File

@ -259,6 +259,22 @@ class RC2 extends Base
0x70, 0x02, 0xC2, 0x1E, 0xB8, 0x0A, 0xFC, 0xE6 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 * Test for engine validity
* *
@ -292,19 +308,15 @@ class RC2 extends Base
* *
* @access public * @access public
* @param int $length in bits * @param int $length in bits
* @throws \LengthException if the key length isn't supported
*/ */
function setKeyLength($length) function setKeyLength($length)
{ {
if ($length < 8) { if ($length < 8 || $length > 1024) {
$this->default_key_length = 1; throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported');
} elseif ($length > 1024) {
$this->default_key_length = 128;
} else {
$this->default_key_length = $length;
} }
$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 * @access public
* @param string $key * @param string $key
* @param int $t1 optional Effective key length in bits. * @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; $this->orig_key = $key;
if ($t1 <= 0) { if ($t1 === false) {
$t1 = $this->default_key_length; $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; $this->current_key_length = $t1;
// Key byte count should be 1..128. // Key byte count should be 1..128.
$key = strlen($key) ? substr($key, 0, 128) : "\x00"; $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) // (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); $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}"; $code_hash = "Crypt_RC2, {$this->mode}";
if ($gen_hi_opt_code) { if ($gen_hi_opt_code) {
$code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);

View File

@ -107,7 +107,7 @@ class RC4 extends Base
* @var string * @var string
* @access private * @access private
*/ */
var $key; var $key = "\0";
/** /**
* The Key Stream for decryption and encryption * The Key Stream for decryption and encryption
@ -121,8 +121,6 @@ class RC4 extends Base
/** /**
* Default Constructor. * Default Constructor.
* *
* Determines whether or not the mcrypt extension should be used.
*
* @see \phpseclib\Crypt\Base::__construct() * @see \phpseclib\Crypt\Base::__construct()
* @return \phpseclib\Crypt\RC4 * @return \phpseclib\Crypt\RC4
* @access public * @access public
@ -144,10 +142,8 @@ class RC4 extends Base
*/ */
function isValidEngine($engine) function isValidEngine($engine)
{ {
if ($engine == Base::ENGINE_OPENSSL) { switch ($engine) {
if (version_compare(PHP_VERSION, '5.3.7') >= 0) { case Base::ENGINE_OPENSSL:
$this->cipher_name_openssl = 'rc4-40';
} else {
switch (strlen($this->key)) { switch (strlen($this->key)) {
case 5: case 5:
$this->cipher_name_openssl = 'rc4-40'; $this->cipher_name_openssl = 'rc4-40';
@ -162,32 +158,19 @@ class RC4 extends Base
return false; return false;
} }
} }
}
return parent::isValidEngine($engine); return parent::isValidEngine($engine);
} }
/** /**
* Dummy function. * RC4 does not use an IV
* *
* Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1].
* If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before
* calling setKey().
*
* [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way. Since, in that protocol,
* the IV's are relatively easy to predict, an attack described by
* {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir}
* can be used to quickly guess at the rest of the key. The following links elaborate:
*
* {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009}
* {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack}
*
* @param string $iv
* @see self::setKey()
* @access public * @access public
* @return bool
*/ */
function setIV($iv) function usesIV()
{ {
return false;
} }
/** /**
@ -197,20 +180,38 @@ class RC4 extends Base
* *
* @access public * @access public
* @param int $length * @param int $length
* @throws \LengthException if the key length is invalid
*/ */
function setKeyLength($length) function setKeyLength($length)
{ {
if ($length < 8) { if ($length < 8 || $length > 2048) {
$this->key_length = 1; throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 256 bytes are supported');
} elseif ($length > 2048) {
$this->key_length = 256;
} else {
$this->key_length = $length >> 3;
} }
$this->key_length = $length >> 3;
parent::setKeyLength($length); 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. * Encrypts a message.
* *

File diff suppressed because it is too large Load Diff

View File

@ -41,17 +41,15 @@ class Random
* eg. for RSA key generation. * eg. for RSA key generation.
* *
* @param int $length * @param int $length
* @throws \RuntimeException if a symmetric cipher is needed but not loaded
* @return string * @return string
*/ */
static function string($length) static function string($length)
{ {
if (!$length) {
return '';
}
if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
try { try {
return \random_bytes($length); return \random_bytes($length);
} catch (\Exception $e) {
// random_compat will throw an Exception, which in PHP 5 does not implement Throwable
} catch (\Throwable $e) { } catch (\Throwable $e) {
// If a sufficient source of randomness is unavailable, random_bytes() will throw an // If a sufficient source of randomness is unavailable, random_bytes() will throw an
// object that implements the Throwable interface (Exception, TypeError, Error). // object that implements the Throwable interface (Exception, TypeError, Error).
@ -60,54 +58,6 @@ class Random
// random_bytes(), most of the other calls here will fail too, so we'll end up using // random_bytes(), most of the other calls here will fail too, so we'll end up using
// the PHP implementation. // 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);
}
}
// at this point we have no choice but to use a pure-PHP CSPRNG // at this point we have no choice but to use a pure-PHP CSPRNG
// cascade entropy across multiple PHP instances by fixing the session and collecting all // cascade entropy across multiple PHP instances by fixing the session and collecting all
@ -143,15 +93,14 @@ class Random
session_cache_limiter(''); session_cache_limiter('');
session_start(); session_start();
$v = $seed = $_SESSION['seed'] = pack('H*', sha1( $v = (isset($_SERVER) ? self::safe_serialize($_SERVER) : '') .
(isset($_SERVER) ? phpseclib_safe_serialize($_SERVER) : '') . (isset($_POST) ? self::safe_serialize($_POST) : '') .
(isset($_POST) ? phpseclib_safe_serialize($_POST) : '') . (isset($_GET) ? self::safe_serialize($_GET) : '') .
(isset($_GET) ? phpseclib_safe_serialize($_GET) : '') . (isset($_COOKIE) ? self::safe_serialize($_COOKIE) : '') .
(isset($_COOKIE) ? phpseclib_safe_serialize($_COOKIE) : '') . self::safe_serialize($GLOBALS) .
phpseclib_safe_serialize($GLOBALS) . self::safe_serialize($_SESSION) .
phpseclib_safe_serialize($_SESSION) . self::safe_serialize($_OLD_SESSION);
phpseclib_safe_serialize($_OLD_SESSION) $v = $seed = $_SESSION['seed'] = sha1($v, true);
));
if (!isset($_SESSION['count'])) { if (!isset($_SESSION['count'])) {
$_SESSION['count'] = 0; $_SESSION['count'] = 0;
} }
@ -182,8 +131,8 @@ class Random
// http://tools.ietf.org/html/rfc4253#section-7.2 // http://tools.ietf.org/html/rfc4253#section-7.2
// //
// see the is_string($crypto) part for an example of how to expand the keys // see the is_string($crypto) part for an example of how to expand the keys
$key = pack('H*', sha1($seed . 'A')); $key = sha1($seed . 'A', true);
$iv = pack('H*', sha1($seed . 'C')); $iv = sha1($seed . 'C', true);
// ciphers are used as per the nist.gov link below. also, see this link: // ciphers are used as per the nist.gov link below. also, see this link:
// //
@ -208,8 +157,7 @@ class Random
$crypto = new RC4(); $crypto = new RC4();
break; break;
default: default:
user_error(__CLASS__ . ' requires at least one symmetric cipher be loaded'); throw new \RuntimeException(__CLASS__ . ' requires at least one symmetric cipher be loaded');
return false;
} }
$crypto->setKey($key); $crypto->setKey($key);
@ -236,19 +184,16 @@ class Random
} }
return substr($result, 0, $length); return substr($result, 0, $length);
} }
}
if (!function_exists('phpseclib_safe_serialize')) {
/** /**
* Safely serialize variables * Safely serialize variables
* *
* If a class has a private __sleep() method it'll give a fatal error on PHP 5.2 and earlier. * If a class has a private __sleep() it'll emit a warning
* PHP 5.3 will emit a warning.
* *
* @param mixed $arr * @param mixed $arr
* @access public * @access public
*/ */
function phpseclib_safe_serialize(&$arr) function safe_serialize(&$arr)
{ {
if (is_object($arr)) { if (is_object($arr)) {
return ''; return '';
@ -265,7 +210,7 @@ if (!function_exists('phpseclib_safe_serialize')) {
foreach (array_keys($arr) as $key) { foreach (array_keys($arr) as $key) {
// do not recurse on the '__phpseclib_marker' key itself, for smaller memory usage // do not recurse on the '__phpseclib_marker' key itself, for smaller memory usage
if ($key !== '__phpseclib_marker') { if ($key !== '__phpseclib_marker') {
$safearr[$key] = phpseclib_safe_serialize($arr[$key]); $safearr[$key] = self::safe_serialize($arr[$key]);
} }
} }
unset($arr['__phpseclib_marker']); unset($arr['__phpseclib_marker']);

View File

@ -168,11 +168,26 @@ class Rijndael extends Base
*/ */
var $kl; 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. * 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 * Valid key lengths are 128, 160, 192, 224, and 256.
* 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
* *
* Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined * 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 * 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. * This results then in slower encryption.
* *
* @access public * @access public
* @throws \LengthException if the key length is invalid
* @param int $length * @param int $length
*/ */
function setKeyLength($length) function setKeyLength($length)
{ {
switch (true) { switch ($length) {
case $length <= 128: case 128:
$this->key_length = 16; case 160:
break; case 192:
case $length <= 160: case 224:
$this->key_length = 20; case 256:
break; $this->key_length = $length >> 3;
case $length <= 192:
$this->key_length = 24;
break;
case $length <= 224:
$this->key_length = 28;
break; break;
default: 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); 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 * 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 * Valid block lengths are 128, 160, 192, 224, and 256.
* 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
* *
* @access public * @access public
* @param int $length * @param int $length
*/ */
function setBlockLength($length) function setBlockLength($length)
{ {
$length >>= 5; switch ($length) {
if ($length > 8) { case 128:
$length = 8; case 160:
} elseif ($length < 4) { case 192:
$length = 4; 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->changed = true;
$this->_setEngine(); $this->_setEngine();
} }

View File

@ -128,7 +128,7 @@ class TripleDES extends DES
/** /**
* Default Constructor. * 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: * $mode could be:
* *
@ -142,16 +142,14 @@ class TripleDES extends DES
* *
* - \phpseclib\Crypt\Base::MODE_OFB * - \phpseclib\Crypt\Base::MODE_OFB
* *
* - \phpseclib\Crypt\TripleDES::MODE_3CBC * - \phpseclib\Crypt\TripleDES::MODE_3CB
*
* If not explicitly set, \phpseclib\Crypt\Base::MODE_CBC will be used.
* *
* @see \phpseclib\Crypt\DES::__construct() * @see \phpseclib\Crypt\DES::__construct()
* @see \phpseclib\Crypt\Base::__construct() * @see \phpseclib\Crypt\Base::__construct()
* @param int $mode * @param int $mode
* @access public * @access public
*/ */
function __construct($mode = Base::MODE_CBC) function __construct($mode)
{ {
switch ($mode) { switch ($mode) {
// In case of self::MODE_3CBC, we init as CRYPT_DES_MODE_CBC // 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 * SetIV is not required when \phpseclib\Crypt\Base::MODE_ECB is being used.
* to be all zero's.
* *
* @see \phpseclib\Crypt\Base::setIV() * @see \phpseclib\Crypt\Base::setIV()
* @access public * @access public
@ -222,24 +219,23 @@ class TripleDES extends DES
/** /**
* Sets the key length. * 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() * @see \phpseclib\Crypt\Base:setKeyLength()
* @access public * @access public
* @throws \LengthException if the key length is invalid
* @param int $length * @param int $length
*/ */
function setKeyLength($length) function setKeyLength($length)
{ {
$length >>= 3; switch ($length) {
switch (true) { case 128:
case $length <= 8: case 192:
$this->key_length = 8;
break;
case $length <= 16:
$this->key_length = 16;
break; break;
default: 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); parent::setKeyLength($length);
@ -248,36 +244,38 @@ class TripleDES extends DES
/** /**
* Sets the key. * Sets the key.
* *
* Keys can be of any length. Triple DES, itself, can use 128-bit (eg. strlen($key) == 16) or * Triple DES can use 128-bit (eg. strlen($key) == 16) or 192-bit (eg. strlen($key) == 24) keys.
* 192-bit (eg. strlen($key) == 24) keys. This function pads and truncates $key as appropriate.
* *
* DES also requires that every eighth bit be a parity bit, however, we'll ignore that. * 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 * @access public
* @see \phpseclib\Crypt\DES::setKey() * @see \phpseclib\Crypt\DES::setKey()
* @see \phpseclib\Crypt\Base::setKey() * @see \phpseclib\Crypt\Base::setKey()
* @throws \LengthException if the key length is invalid
* @param string $key * @param string $key
*/ */
function setKey($key) function setKey($key)
{ {
$length = $this->explicit_key_length ? $this->key_length : strlen($key); if ($this->explicit_key_length !== false && strlen($key) != $this->explicit_key_length) {
if ($length > 8) { throw new \LengthException('Key length has already been set to ' . $this->explicit_key_length . ' bytes and this key is ' . strlen($key) . ' bytes');
$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));
} }
parent::setKey($key);
// And in case of self::MODE_3CBC: switch (strlen($key)) {
// if key <= 64bits we not need the 3 $des to work, case 16:
// because we will then act as regular DES-CBC with just a <= 64bit key. $key.= substr($key, 0, 8);
// So only if the key > 64bits (> 8 bytes) we will call setKey() for the 3 $des. case 24:
if ($this->mode_3cbc && $length > 8) { 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[0]->setKey(substr($key, 0, 8));
$this->des[1]->setKey(substr($key, 8, 8)); $this->des[1]->setKey(substr($key, 8, 8));
$this->des[2]->setKey(substr($key, 16, 8)); $this->des[2]->setKey(substr($key, 16, 8));

View File

@ -368,6 +368,22 @@ class Twofish extends Base
*/ */
var $key_length = 16; 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. * Sets the key length.
* *
@ -378,20 +394,42 @@ class Twofish extends Base
*/ */
function setKeyLength($length) function setKeyLength($length)
{ {
switch (true) { switch ($length) {
case $length <= 128: case 128:
$this->key_length = 16; case 192:
break; case 256:
case $length <= 192:
$this->key_length = 24;
break; break;
default: 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); 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) * Setup the key (expansion)
* *
@ -432,10 +470,8 @@ class Twofish extends Base
$m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^ $m2[$q1[$q0[$j] ^ $key[15]] ^ $key[7]] ^
$m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]]; $m3[$q1[$q1[$j] ^ $key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff); $B = ($B << 8) | ($B >> 24 & 0xff);
$A = $this->safe_intval($A + $B); $K[] = $A+= $B;
$K[] = $A; $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff);
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
} }
for ($i = 0; $i < 256; ++$i) { for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$i] ^ $s4] ^ $s0]; $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]] ^ $m2[$q1[$q0[$q0[$j] ^ $key[23]] ^ $key[15]] ^ $key[7]] ^
$m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]]; $m3[$q1[$q1[$q0[$j] ^ $key[24]] ^ $key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff); $B = ($B << 8) | ($B >> 24 & 0xff);
$A = $this->safe_intval($A + $B); $K[] = $A+= $B;
$K[] = $A; $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff);
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
} }
for ($i = 0; $i < 256; ++$i) { for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$q1[$i] ^ $s8] ^ $s4] ^ $s0]; $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]] ^ $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]]; $m3[$q1[$q1[$q0[$q1[$j] ^ $key[32]] ^ $key[24]] ^ $key[16]] ^ $key[8]];
$B = ($B << 8) | ($B >> 24 & 0xff); $B = ($B << 8) | ($B >> 24 & 0xff);
$A = $this->safe_intval($A + $B); $K[] = $A+= $B;
$K[] = $A; $K[] = (($A+= $B) << 9 | $A >> 23 & 0x1ff);
$A = $this->safe_intval($A + $B);
$K[] = ($A << 9 | $A >> 23 & 0x1ff);
} }
for ($i = 0; $i < 256; ++$i) { for ($i = 0; $i < 256; ++$i) {
$S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4] ^ $s0]; $S0[$i] = $m0[$q0[$q0[$q1[$q1[$i] ^ $sc] ^ $s8] ^ $s4] ^ $s0];
@ -584,9 +616,9 @@ class Twofish extends Base
$S1[ $R1 & 0xff] ^ $S1[ $R1 & 0xff] ^
$S2[($R1 >> 8) & 0xff] ^ $S2[($R1 >> 8) & 0xff] ^
$S3[($R1 >> 16) & 0xff]; $S3[($R1 >> 16) & 0xff];
$R2^= $this->safe_intval($t0 + $t1 + $K[++$ki]); $R2^= $t0 + $t1 + $K[++$ki];
$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); $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] ^ $t0 = $S0[ $R2 & 0xff] ^
$S1[($R2 >> 8) & 0xff] ^ $S1[($R2 >> 8) & 0xff] ^
@ -596,9 +628,9 @@ class Twofish extends Base
$S1[ $R3 & 0xff] ^ $S1[ $R3 & 0xff] ^
$S2[($R3 >> 8) & 0xff] ^ $S2[($R3 >> 8) & 0xff] ^
$S3[($R3 >> 16) & 0xff]; $S3[($R3 >> 16) & 0xff];
$R0^= $this->safe_intval($t0 + $t1 + $K[++$ki]); $R0^= ($t0 + $t1 + $K[++$ki]);
$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); $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 // @codingStandardsIgnoreStart
@ -640,9 +672,9 @@ class Twofish extends Base
$S1[$R1 & 0xff] ^ $S1[$R1 & 0xff] ^
$S2[$R1 >> 8 & 0xff] ^ $S2[$R1 >> 8 & 0xff] ^
$S3[$R1 >> 16 & 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; $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] ^ $t0 = $S0[$R2 & 0xff] ^
$S1[$R2 >> 8 & 0xff] ^ $S1[$R2 >> 8 & 0xff] ^
@ -652,9 +684,9 @@ class Twofish extends Base
$S1[$R3 & 0xff] ^ $S1[$R3 & 0xff] ^
$S2[$R3 >> 8 & 0xff] ^ $S2[$R3 >> 8 & 0xff] ^
$S3[$R3 >> 16 & 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; $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 // @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) // (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); $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}"; $code_hash = "Crypt_Twofish, {$this->mode}";
if ($gen_hi_opt_code) { if ($gen_hi_opt_code) {
$code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key); $code_hash = str_pad($code_hash, 32) . $this->_hashInlineCryptFunction($this->key);
} }
$safeint = $this->safe_intval_inline();
if (!isset($lambda_functions[$code_hash])) { if (!isset($lambda_functions[$code_hash])) {
switch (true) { switch (true) {
case $gen_hi_opt_code: case $gen_hi_opt_code:
@ -735,9 +765,9 @@ class Twofish extends Base
$S1[ $R1 & 0xff] ^ $S1[ $R1 & 0xff] ^
$S2[($R1 >> 8) & 0xff] ^ $S2[($R1 >> 8) & 0xff] ^
$S3[($R1 >> 16) & 0xff]; $S3[($R1 >> 16) & 0xff];
$R2^= ' . sprintf($safeint, '$t0 + $t1 + ' . $K[++$ki]) . '; $R2^= ($t0 + $t1 + '.$K[++$ki].');
$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31); $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] ^ $t0 = $S0[ $R2 & 0xff] ^
$S1[($R2 >> 8) & 0xff] ^ $S1[($R2 >> 8) & 0xff] ^
@ -747,16 +777,16 @@ class Twofish extends Base
$S1[ $R3 & 0xff] ^ $S1[ $R3 & 0xff] ^
$S2[($R3 >> 8) & 0xff] ^ $S2[($R3 >> 8) & 0xff] ^
$S3[($R3 >> 16) & 0xff]; $S3[($R3 >> 16) & 0xff];
$R0^= ' . sprintf($safeint, '($t0 + $t1 + ' . $K[++$ki] . ')') . '; $R0^= ($t0 + $t1 + '.$K[++$ki].');
$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31); $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.= ' $encrypt_block.= '
$in = pack("V4", ' . $K[4] . ' ^ $R2, $in = pack("V4", '.$K[4].' ^ $R2,
' . $K[5] . ' ^ $R3, '.$K[5].' ^ $R3,
' . $K[6] . ' ^ $R0, '.$K[6].' ^ $R0,
' . $K[7] . ' ^ $R1); '.$K[7].' ^ $R1);
'; ';
// Generating decrypt code: // Generating decrypt code:
@ -777,9 +807,9 @@ class Twofish extends Base
$S1[$R1 & 0xff] ^ $S1[$R1 & 0xff] ^
$S2[$R1 >> 8 & 0xff] ^ $S2[$R1 >> 8 & 0xff] ^
$S3[$R1 >> 16 & 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; $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] ^ $t0 = $S0[$R2 & 0xff] ^
$S1[$R2 >> 8 & 0xff] ^ $S1[$R2 >> 8 & 0xff] ^
@ -789,16 +819,16 @@ class Twofish extends Base
$S1[$R3 & 0xff] ^ $S1[$R3 & 0xff] ^
$S2[$R3 >> 8 & 0xff] ^ $S2[$R3 >> 8 & 0xff] ^
$S3[$R3 >> 16 & 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; $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.= ' $decrypt_block.= '
$in = pack("V4", ' . $K[0] . ' ^ $R2, $in = pack("V4", '.$K[0].' ^ $R2,
' . $K[1] . ' ^ $R3, '.$K[1].' ^ $R3,
' . $K[2] . ' ^ $R0, '.$K[2].' ^ $R0,
' . $K[3] . ' ^ $R1); '.$K[3].' ^ $R1);
'; ';
$lambda_functions[$code_hash] = $this->_createInlineCryptFunction( $lambda_functions[$code_hash] = $this->_createInlineCryptFunction(

View File

@ -305,9 +305,6 @@ class ANSI
case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): // Move cursor left n lines case preg_match('#\x1B\[(\d+)D#', $this->ansi, $match): // Move cursor left n lines
$this->old_x = $this->x; $this->old_x = $this->x;
$this->x-= $match[1]; $this->x-= $match[1];
if ($this->x < 0) {
$this->x = 0;
}
break; break;
case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window case preg_match('#\x1B\[(\d+);(\d+)r#', $this->ansi, $match): // Set top and bottom lines of a window
break; break;
@ -419,7 +416,7 @@ class ANSI
if ($this->x > $this->max_x) { if ($this->x > $this->max_x) {
$this->x = 0; $this->x = 0;
$this->_newLine(); $this->y++;
} else { } else {
$this->x++; $this->x++;
} }

View File

@ -23,10 +23,9 @@
namespace phpseclib\File; namespace phpseclib\File;
use ParagonIE\ConstantTime\Base64;
use phpseclib\File\ASN1\Element; use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger; use phpseclib\Math\BigInteger;
use DateTime;
use DateTimeZone;
/** /**
* Pure-PHP ASN.1 Parser * Pure-PHP ASN.1 Parser
@ -226,15 +225,14 @@ class ASN1
* *
* @param string $encoded * @param string $encoded
* @param int $start * @param int $start
* @param int $encoded_pos
* @return array * @return array
* @access private * @access private
*/ */
function _decode_ber($encoded, $start = 0, $encoded_pos = 0) function _decode_ber($encoded, $start = 0)
{ {
$current = array('start' => $start); $current = array('start' => $start);
$type = ord($encoded[$encoded_pos++]); $type = ord($this->_string_shift($encoded));
$start++; $start++;
$constructed = ($type >> 5) & 1; $constructed = ($type >> 5) & 1;
@ -244,27 +242,25 @@ class ASN1
$tag = 0; $tag = 0;
// process septets (since the eighth bit is ignored, it's not an octet) // process septets (since the eighth bit is ignored, it's not an octet)
do { do {
$temp = ord($encoded[$encoded_pos++]); $loop = ord($encoded[0]) >> 7;
$loop = $temp >> 7;
$tag <<= 7; $tag <<= 7;
$tag |= $temp & 0x7F; $tag |= ord($this->_string_shift($encoded)) & 0x7F;
$start++; $start++;
} while ($loop); } while ($loop);
} }
// Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13 // 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++; $start++;
if ($length == 0x80) { // indefinite length 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 // "[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 // immediately available." -- paragraph 8.1.3.2.c
$length = strlen($encoded) - $encoded_pos; $length = strlen($encoded);
} elseif ($length & 0x80) { // definite length, long form } 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 // 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. // support it up to four.
$length&= 0x7F; $length&= 0x7F;
$temp = substr($encoded, $encoded_pos, $length); $temp = $this->_string_shift($encoded, $length);
$encoded_pos += $length;
// tags of indefinte length don't really have a header length; this length includes the tag // tags of indefinte length don't really have a header length; this length includes the tag
$current+= array('headerlength' => $length + 2); $current+= array('headerlength' => $length + 2);
$start+= $length; $start+= $length;
@ -273,12 +269,11 @@ class ASN1
$current+= array('headerlength' => 2); $current+= array('headerlength' => 2);
} }
if ($length > (strlen($encoded) - $encoded_pos)) { if ($length > strlen($encoded)) {
return false; return false;
} }
$content = substr($encoded, $encoded_pos, $length); $content = $this->_string_shift($encoded, $length);
$content_pos = 0;
// at this point $length can be overwritten. it's only accurate for definite length things as is // 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(); $newcontent = array();
$remainingLength = $length; $remainingLength = $length;
while ($remainingLength > 0) { while ($remainingLength > 0) {
$temp = $this->_decode_ber($content, $start, $content_pos); $temp = $this->_decode_ber($content, $start);
if ($temp === false) {
break;
}
$length = $temp['length']; $length = $temp['length'];
// end-of-content octets - see paragraph 8.1.5 // 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; $length+= 2;
$start+= $length; $start+= $length;
$newcontent[] = $temp; $newcontent[] = $temp;
@ -323,7 +315,7 @@ class ASN1
$start+= $length; $start+= $length;
$remainingLength-= $length; $remainingLength-= $length;
$newcontent[] = $temp; $newcontent[] = $temp;
$content_pos += $length; $this->_string_shift($content, $length);
} }
return array( return array(
@ -347,11 +339,11 @@ class ASN1
//if (strlen($content) != 1) { //if (strlen($content) != 1) {
// return false; // return false;
//} //}
$current['content'] = (bool) ord($content[$content_pos]); $current['content'] = (bool) ord($content[0]);
break; break;
case self::TYPE_INTEGER: case self::TYPE_INTEGER:
case self::TYPE_ENUMERATED: case self::TYPE_ENUMERATED:
$current['content'] = new BigInteger(substr($content, $content_pos), -256); $current['content'] = new BigInteger($content, -256);
break; break;
case self::TYPE_REAL: // not currently supported case self::TYPE_REAL: // not currently supported
return false; 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 // the number of unused bits in the final subsequent octet. The number shall be in the range zero to
// seven. // seven.
if (!$constructed) { if (!$constructed) {
$current['content'] = substr($content, $content_pos); $current['content'] = $content;
} else { } else {
$temp = $this->_decode_ber($content, $start, $content_pos); $temp = $this->_decode_ber($content, $start);
if ($temp === false) { $length-= strlen($content);
return false;
}
$length-= (strlen($content) - $content_pos);
$last = count($temp) - 1; $last = count($temp) - 1;
for ($i = 0; $i < $last; $i++) { for ($i = 0; $i < $last; $i++) {
// all subtags should be bit strings // all subtags should be bit strings
@ -384,16 +373,13 @@ class ASN1
break; break;
case self::TYPE_OCTET_STRING: case self::TYPE_OCTET_STRING:
if (!$constructed) { if (!$constructed) {
$current['content'] = substr($content, $content_pos); $current['content'] = $content;
} else { } else {
$current['content'] = ''; $current['content'] = '';
$length = 0; $length = 0;
while (substr($content, $content_pos, 2) != "\0\0") { while (substr($content, 0, 2) != "\0\0") {
$temp = $this->_decode_ber($content, $length + $start, $content_pos); $temp = $this->_decode_ber($content, $length + $start);
if ($temp === false) { $this->_string_shift($content, $temp['length']);
return false;
}
$content_pos += $temp['length'];
// all subtags should be octet strings // all subtags should be octet strings
//if ($temp['type'] != self::TYPE_OCTET_STRING) { //if ($temp['type'] != self::TYPE_OCTET_STRING) {
// return false; // return false;
@ -401,7 +387,7 @@ class ASN1
$current['content'].= $temp['content']; $current['content'].= $temp['content'];
$length+= $temp['length']; $length+= $temp['length'];
} }
if (substr($content, $content_pos, 2) == "\0\0") { if (substr($content, 0, 2) == "\0\0") {
$length+= 2; // +2 for the EOC $length+= 2; // +2 for the EOC
} }
} }
@ -416,25 +402,37 @@ class ASN1
case self::TYPE_SET: case self::TYPE_SET:
$offset = 0; $offset = 0;
$current['content'] = array(); $current['content'] = array();
$content_len = strlen($content); while (strlen($content)) {
while ($content_pos < $content_len) {
// if indefinite length construction was used and we have an end-of-content string next // 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 // 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 $length = $offset + 2; // +2 for the EOC
break 2; break 2;
} }
$temp = $this->_decode_ber($content, $start + $offset, $content_pos); $temp = $this->_decode_ber($content, $start + $offset);
if ($temp === false) { $this->_string_shift($content, $temp['length']);
return false;
}
$content_pos += $temp['length'];
$current['content'][] = $temp; $current['content'][] = $temp;
$offset+= $temp['length']; $offset+= $temp['length'];
} }
break; break;
case self::TYPE_OBJECT_IDENTIFIER: 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; break;
/* Each character string type shall be encoded as if it had been declared: /* Each character string type shall be encoded as if it had been declared:
[UNIVERSAL x] IMPLICIT OCTET STRING [UNIVERSAL x] IMPLICIT OCTET STRING
@ -464,11 +462,11 @@ class ASN1
case self::TYPE_UTF8_STRING: case self::TYPE_UTF8_STRING:
// ???? // ????
case self::TYPE_BMP_STRING: case self::TYPE_BMP_STRING:
$current['content'] = substr($content, $content_pos); $current['content'] = $content;
break; break;
case self::TYPE_UTC_TIME: case self::TYPE_UTC_TIME:
case self::TYPE_GENERALIZED_TIME: case self::TYPE_GENERALIZED_TIME:
$current['content'] = $this->_decodeTime(substr($content, $content_pos), $tag); $current['content'] = $this->_decodeTime($content, $tag);
default: default:
} }
@ -500,7 +498,7 @@ class ASN1
switch (true) { switch (true) {
case $mapping['type'] == self::TYPE_ANY: case $mapping['type'] == self::TYPE_ANY:
$intype = $decoded['type']; $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'])); return new Element(substr($this->encoded, $decoded['start'], $decoded['length']));
} }
$inmap = $this->ANYmap[$intype]; $inmap = $this->ANYmap[$intype];
@ -578,7 +576,7 @@ class ASN1
$childClass = $tempClass = self::CLASS_UNIVERSAL; $childClass = $tempClass = self::CLASS_UNIVERSAL;
$constant = null; $constant = null;
if (isset($temp['constant'])) { if (isset($temp['constant'])) {
$tempClass = $temp['type']; $tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC;
} }
if (isset($child['class'])) { if (isset($child['class'])) {
$childClass = $child['class']; $childClass = $child['class'];
@ -641,7 +639,7 @@ class ASN1
$temp = $decoded['content'][$i]; $temp = $decoded['content'][$i];
$tempClass = self::CLASS_UNIVERSAL; $tempClass = self::CLASS_UNIVERSAL;
if (isset($temp['constant'])) { if (isset($temp['constant'])) {
$tempClass = $temp['type']; $tempClass = isset($temp['class']) ? $temp['class'] : self::CLASS_CONTEXT_SPECIFIC;
} }
foreach ($mapping['children'] as $key => $child) { foreach ($mapping['children'] as $key => $child) {
@ -705,7 +703,7 @@ class ASN1
if (isset($mapping['implicit'])) { if (isset($mapping['implicit'])) {
$decoded['content'] = $this->_decodeTime($decoded['content'], $decoded['type']); $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: case self::TYPE_BIT_STRING:
if (isset($mapping['mapping'])) { if (isset($mapping['mapping'])) {
$offset = ord($decoded['content'][0]); $offset = ord($decoded['content'][0]);
@ -736,7 +734,7 @@ class ASN1
return $values; return $values;
} }
case self::TYPE_OCTET_STRING: case self::TYPE_OCTET_STRING:
return base64_encode($decoded['content']); return Base64::encode($decoded['content']);
case self::TYPE_NULL: case self::TYPE_NULL:
return ''; return '';
case self::TYPE_BOOLEAN: case self::TYPE_BOOLEAN:
@ -796,6 +794,7 @@ class ASN1
* @param string $mapping * @param string $mapping
* @param int $idx * @param int $idx
* @return string * @return string
* @throws \RuntimeException if the input has an error in it
* @access private * @access private
*/ */
function _encode_der($source, $mapping, $idx = null, $special = array()) 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_SET: // Children order is not important, thus process in sequence.
case self::TYPE_SEQUENCE: case self::TYPE_SEQUENCE:
$tag|= 0x20; // set the constructed bit $tag|= 0x20; // set the constructed bit
$value = '';
// ignore the min and max // ignore the min and max
if (isset($mapping['min']) && isset($mapping['max'])) { if (isset($mapping['min']) && isset($mapping['max'])) {
$value = array();
$child = $mapping['children']; $child = $mapping['children'];
foreach ($source as $content) { foreach ($source as $content) {
@ -833,21 +832,11 @@ class ASN1
if ($temp === false) { if ($temp === false) {
return false; return false;
} }
$value[]= $temp; $value.= $temp;
} }
/* "The encodings of the component values of a set-of value shall appear in ascending order, the encodings being compared
as octet strings with the shorter components being padded at their trailing end with 0-octets.
NOTE - The padding octets are for comparison purposes only and do not appear in the encodings."
-- sec 11.6 of http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf */
if ($mapping['type'] == self::TYPE_SET) {
sort($value);
}
$value = implode($value, '');
break; break;
} }
$value = '';
foreach ($mapping['children'] as $key => $child) { foreach ($mapping['children'] as $key => $child) {
if (!array_key_exists($key, $source)) { if (!array_key_exists($key, $source)) {
if (!isset($child['optional'])) { if (!isset($child['optional'])) {
@ -954,8 +943,7 @@ class ASN1
case self::TYPE_GENERALIZED_TIME: case self::TYPE_GENERALIZED_TIME:
$format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y'; $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y';
$format.= 'mdHis'; $format.= 'mdHis';
$date = new DateTime($source, new DateTimeZone('GMT')); $value = @gmdate($format, strtotime($source)) . 'Z';
$value = $date->format($format) . 'Z';
break; break;
case self::TYPE_BIT_STRING: case self::TYPE_BIT_STRING:
if (isset($mapping['mapping'])) { 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. 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 */ -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=16 */
$value = base64_decode($source); $value = Base64::decode($source);
break; break;
case self::TYPE_OBJECT_IDENTIFIER: 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; break;
case self::TYPE_ANY: case self::TYPE_ANY:
$loc = $this->location; $loc = $this->location;
@ -1032,7 +1040,7 @@ class ASN1
$filters = $filters[$part]; $filters = $filters[$part];
} }
if ($filters === false) { if ($filters === false) {
user_error('No filters defined for ' . implode('/', $loc)); throw new \RuntimeException('No filters defined for ' . implode('/', $loc));
return false; return false;
} }
return $this->_encode_der($source, $filters + $mapping, null, $special); return $this->_encode_der($source, $filters + $mapping, null, $special);
@ -1056,7 +1064,7 @@ class ASN1
$value = $source ? "\xFF" : "\x00"; $value = $source ? "\xFF" : "\x00";
break; break;
default: 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; return false;
} }
@ -1096,108 +1104,6 @@ class ASN1
return pack('Ca*', 0x80 | strlen($temp), $temp); 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 * BER-decode the time
* *
@ -1218,32 +1124,33 @@ class ASN1
http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2 http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2
http://www.obj-sys.com/asn1tutorial/node14.html */ http://www.obj-sys.com/asn1tutorial/node14.html */
$format = 'YmdHis'; $pattern = $tag == self::TYPE_UTC_TIME ?
'#(..)(..)(..)(..)(..)(..)(.*)#' :
'#(....)(..)(..)(..)(..)(..).*([Z+-].*)$#';
preg_match($pattern, $content, $matches);
list(, $year, $month, $day, $hour, $minute, $second, $timezone) = $matches;
if ($tag == self::TYPE_UTC_TIME) { if ($tag == self::TYPE_UTC_TIME) {
// https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=28 says "the seconds $year = $year >= 50 ? "19$year" : "20$year";
// element shall always be present" but none-the-less I've seen X509 certs where it isn't and if the
// browsers parse it phpseclib ought to too
if (preg_match('#^(\d{10})(Z|[+-]\d{4})$#', $content, $matches)) {
$content = $matches[1] . '00' . $matches[2];
}
$prefix = substr($content, 0, 2) >= 50 ? '19' : '20';
$content = $prefix . $content;
} elseif (strpos($content, '.') !== false) {
$format.= '.u';
} }
if ($content[strlen($content) - 1] == 'Z') { if ($timezone == 'Z') {
$content = substr($content, 0, -1) . '+0000'; $mktime = 'gmmktime';
$timezone = 0;
} elseif (preg_match('#([+-])(\d\d)(\d\d)#', $timezone, $matches)) {
$mktime = 'gmmktime';
$timezone = 60 * $matches[3] + 3600 * $matches[2];
if ($matches[1] == '-') {
$timezone = -$timezone;
}
} else {
$mktime = 'mktime';
$timezone = 0;
} }
if (strpos($content, '-') !== false || strpos($content, '+') !== false) { return @$mktime($hour, $minute, $second, $month, $day, $year) + $timezone;
$format.= 'O';
}
// error supression isn't necessary as of PHP 7.0:
// http://php.net/manual/en/migration70.other-changes.php
return @DateTime::createFromFormat($format, $content);
} }
/** /**

View File

@ -26,13 +26,14 @@
namespace phpseclib\File; namespace phpseclib\File;
use ParagonIE\ConstantTime\Base64;
use ParagonIE\ConstantTime\Hex;
use phpseclib\Crypt\Hash; use phpseclib\Crypt\Hash;
use phpseclib\Crypt\Random; use phpseclib\Crypt\Random;
use phpseclib\Crypt\RSA; use phpseclib\Crypt\RSA;
use phpseclib\Exception\UnsupportedAlgorithmException;
use phpseclib\File\ASN1\Element; use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger; use phpseclib\Math\BigInteger;
use DateTime;
use DateTimeZone;
/** /**
* Pure-PHP X.509 Parser * Pure-PHP X.509 Parser
@ -246,7 +247,7 @@ class X509
/** /**
* The signature subject * 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. * encoded so we take save the portion of the original cert that the signature would have made for.
* *
* @var string * @var string
@ -305,22 +306,6 @@ class X509
*/ */
var $challenge; 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. * Default Constructor.
* *
@ -1502,9 +1487,7 @@ class X509
$this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); $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/issuer/rdnSequence', $asn1);
$this->_mapInDNs($x509, 'tbsCertificate/subject/rdnSequence', $asn1); $this->_mapInDNs($x509, 'tbsCertificate/subject/rdnSequence', $asn1);
@ -1543,7 +1526,7 @@ class X509
switch ($algorithm) { switch ($algorithm) {
case 'rsaEncryption': case 'rsaEncryption':
$cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'] $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." /* "[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 -- https://tools.ietf.org/html/rfc3279#section-2.3.1
@ -1593,7 +1576,7 @@ class X509
return $cert; return $cert;
// case self::FORMAT_PEM: // case self::FORMAT_PEM:
default: 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) 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++) { for ($i = 0; $i < count($extensions); $i++) {
$id = $extensions[$i]['extnId']; $id = $extensions[$i]['extnId'];
$value = &$extensions[$i]['extnValue']; $value = &$extensions[$i]['extnValue'];
$value = base64_decode($value); $value = Base64::decode($value);
$decoded = $asn1->decodeBER($value); $decoded = $asn1->decodeBER($value);
/* [extnValue] contains the DER encoding of an ASN.1 value /* [extnValue] contains the DER encoding of an ASN.1 value
corresponding to the extension type identified by extnID */ corresponding to the extension type identified by extnID */
@ -1641,7 +1624,7 @@ class X509
} }
} }
} else { } else {
$value = base64_encode($value); $value = Base64::encode($value);
} }
} }
} }
@ -1702,12 +1685,12 @@ class X509
$map = $this->_getMapping($id); $map = $this->_getMapping($id);
if (is_bool($map)) { if (is_bool($map)) {
if (!$map) { if (!$map) {
user_error($id . ' is not a currently supported extension'); //user_error($id . ' is not a currently supported extension');
unset($extensions[$i]); unset($extensions[$i]);
} }
} else { } else {
$temp = $asn1->encodeDER($value, $map, array('iPAddress' => array($this, '_encodeIP'))); $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) { if ($mapped !== false) {
$values[$j] = $mapped; $values[$j] = $mapped;
} }
if ($id == 'pkcs-9-at-extensionRequest' && $this->_isSubArrayValid($values, $j)) { if ($id == 'pkcs-9-at-extensionRequest') {
$this->_mapInExtensions($values, $j, $asn1); $this->_mapInExtensions($values, $j, $asn1);
} }
} elseif ($map) { } elseif ($map) {
$values[$j] = base64_encode($value); $values[$j] = Base64::encode($value);
} }
} }
} }
@ -1775,7 +1758,7 @@ class X509
$id = $attributes[$i]['type']; $id = $attributes[$i]['type'];
$map = $this->_getMapping($id); $map = $this->_getMapping($id);
if ($map === false) { 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]); unset($attributes[$i]);
} elseif (is_array($attributes[$i]['value'])) { } elseif (is_array($attributes[$i]['value'])) {
$values = &$attributes[$i]['value']; $values = &$attributes[$i]['value'];
@ -1925,12 +1908,6 @@ class X509
// "SET Secure Electronic Transaction Specification" // "SET Secure Electronic Transaction Specification"
// http://www.maithean.com/docs/set_bk3.pdf // http://www.maithean.com/docs/set_bk3.pdf
case '2.23.42.7.0': // id-set-hashedRootKey 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; return true;
// CSR attributes // CSR attributes
@ -2051,8 +2028,7 @@ class X509
} }
if ($names = $this->getExtension('id-ce-subjectAltName')) { if ($names = $this->getExtension('id-ce-subjectAltName')) {
foreach ($names as $name) { foreach ($names as $key => $value) {
foreach ($name as $key => $value) {
$value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value); $value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value);
switch ($key) { switch ($key) {
case 'dNSName': case 'dNSName':
@ -2078,7 +2054,6 @@ class X509
} }
} }
} }
}
return false; return false;
} }
@ -2095,7 +2070,7 @@ class X509
* *
* If $date isn't defined it is assumed to be the current date. * If $date isn't defined it is assumed to be the current date.
* *
* @param \DateTime|string $date optional * @param int $date optional
* @access public * @access public
*/ */
function validateDate($date = null) function validateDate($date = null)
@ -2105,7 +2080,7 @@ class X509
} }
if (!isset($date)) { if (!isset($date)) {
$date = new DateTime(null, new DateTimeZone(@date_default_timezone_get())); $date = time();
} }
$notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore']; $notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore'];
@ -2114,133 +2089,15 @@ class X509
$notAfter = $this->currentCert['tbsCertificate']['validity']['notAfter']; $notAfter = $this->currentCert['tbsCertificate']['validity']['notAfter'];
$notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime']; $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) { switch (true) {
case $date < $notBefore: case $date < @strtotime($notBefore):
case $date > $notAfter: case $date > @strtotime($notAfter):
return false; return false;
} }
return true; 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 * Validate a signature
* *
@ -2257,30 +2114,11 @@ class X509
* @return mixed * @return mixed
*/ */
function validateSignature($caonly = true) 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)) { if (!is_array($this->currentCert) || !isset($this->signatureSubject)) {
return null; return null;
} }
if ($count == self::$recur_limit) {
return false;
}
/* TODO: /* TODO:
"emailAddress attribute values are not case-sensitive (e.g., "subscriber@example.com" is the same as "SUBSCRIBER@EXAMPLE.COM")." "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 -- http://tools.ietf.org/html/rfc5280#section-4.1.2.6
@ -2297,8 +2135,7 @@ class X509
$subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier'); $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier');
switch (true) { switch (true) {
case !is_array($authorityKey): case !is_array($authorityKey):
case !$subjectKeyID: case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
$signingCert = $this->currentCert; // working cert $signingCert = $this->currentCert; // working cert
} }
} }
@ -2315,27 +2152,23 @@ class X509
$subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
switch (true) { switch (true) {
case !is_array($authorityKey): case !is_array($authorityKey):
case !$subjectKeyID: case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) {
break 2; // serial mismatch - check other ca
}
$signingCert = $ca; // working cert $signingCert = $ca; // working cert
break 3; break 3;
} }
} }
} }
if (count($this->CAs) == $i && $caonly) { if (count($this->CAs) == $i && $caonly) {
return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly); return false;
} }
} elseif (!isset($signingCert) || $caonly) { } elseif (!isset($signingCert) || $caonly) {
return $this->_testForIntermediate($caonly, $count) && $this->validateSignature($caonly); return false;
} }
return $this->_validateSignature( return $this->_validateSignature(
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'], $this->currentCert['signatureAlgorithm']['algorithm'],
substr(base64_decode($this->currentCert['signature']), 1), substr(Base64::decode($this->currentCert['signature']), 1),
$this->signatureSubject $this->signatureSubject
); );
case isset($this->currentCert['certificationRequestInfo']): case isset($this->currentCert['certificationRequestInfo']):
@ -2343,7 +2176,7 @@ class X509
$this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'], $this->currentCert['certificationRequestInfo']['subjectPKInfo']['algorithm']['algorithm'],
$this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $this->currentCert['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'], $this->currentCert['signatureAlgorithm']['algorithm'],
substr(base64_decode($this->currentCert['signature']), 1), substr(Base64::decode($this->currentCert['signature']), 1),
$this->signatureSubject $this->signatureSubject
); );
case isset($this->currentCert['publicKeyAndChallenge']): case isset($this->currentCert['publicKeyAndChallenge']):
@ -2351,7 +2184,7 @@ class X509
$this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'], $this->currentCert['publicKeyAndChallenge']['spki']['algorithm']['algorithm'],
$this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'], $this->currentCert['publicKeyAndChallenge']['spki']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'], $this->currentCert['signatureAlgorithm']['algorithm'],
substr(base64_decode($this->currentCert['signature']), 1), substr(Base64::decode($this->currentCert['signature']), 1),
$this->signatureSubject $this->signatureSubject
); );
case isset($this->currentCert['tbsCertList']): case isset($this->currentCert['tbsCertList']):
@ -2365,11 +2198,7 @@ class X509
$subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca);
switch (true) { switch (true) {
case !is_array($authorityKey): case !is_array($authorityKey):
case !$subjectKeyID: case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
case isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID:
if (is_array($authorityKey) && isset($authorityKey['authorityCertSerialNumber']) && !$authorityKey['authorityCertSerialNumber']->equals($ca['tbsCertificate']['serialNumber'])) {
break 2; // serial mismatch - check other ca
}
$signingCert = $ca; // working cert $signingCert = $ca; // working cert
break 3; break 3;
} }
@ -2383,7 +2212,7 @@ class X509
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'],
$signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'],
$this->currentCert['signatureAlgorithm']['algorithm'], $this->currentCert['signatureAlgorithm']['algorithm'],
substr(base64_decode($this->currentCert['signature']), 1), substr(Base64::decode($this->currentCert['signature']), 1),
$this->signatureSubject $this->signatureSubject
); );
default: default:
@ -2394,7 +2223,8 @@ class X509
/** /**
* Validates a signature * 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 $publicKeyAlgorithm
* @param string $publicKey * @param string $publicKey
@ -2402,14 +2232,15 @@ class X509
* @param string $signature * @param string $signature
* @param string $signatureSubject * @param string $signatureSubject
* @access private * @access private
* @return int * @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported
* @return bool
*/ */
function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject) function _validateSignature($publicKeyAlgorithm, $publicKey, $signatureAlgorithm, $signature, $signatureSubject)
{ {
switch ($publicKeyAlgorithm) { switch ($publicKeyAlgorithm) {
case 'rsaEncryption': case 'rsaEncryption':
$rsa = new RSA(); $rsa = new RSA();
$rsa->loadKey($publicKey); $rsa->load($publicKey);
switch ($signatureAlgorithm) { switch ($signatureAlgorithm) {
case 'md2WithRSAEncryption': case 'md2WithRSAEncryption':
@ -2420,57 +2251,21 @@ class X509
case 'sha384WithRSAEncryption': case 'sha384WithRSAEncryption':
case 'sha512WithRSAEncryption': case 'sha512WithRSAEncryption':
$rsa->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)); $rsa->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm));
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1); if (!@$rsa->verify($signatureSubject, $signature, RSA::PADDING_PKCS1)) {
if (!@$rsa->verify($signatureSubject, $signature)) {
return false; return false;
} }
break; break;
default: default:
return null; throw new UnsupportedAlgorithmException('Signature algorithm unsupported');
} }
break; break;
default: default:
return null; throw new UnsupportedAlgorithmException('Public key algorithm unsupported');
} }
return true; 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 * 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 // 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 // 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. // 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-----'; '-----END RSA PUBLIC KEY-----';
default: default:
return $key; return $key;
@ -2508,7 +2303,7 @@ class X509
*/ */
function _decodeIP($ip) function _decodeIP($ip)
{ {
return inet_ntop(base64_decode($ip)); return inet_ntop(Base64::decode($ip));
} }
/** /**
@ -2522,7 +2317,7 @@ class X509
*/ */
function _encodeIP($ip) 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); $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 = new Hash('sha1');
$hash = $hash->hash($dn); $hash = $hash->hash($dn);
extract(unpack('Vhash', $hash)); extract(unpack('Vhash', $hash));
return strtolower(bin2hex(pack('N', $hash))); return strtolower(Hex::encode(pack('N', $hash)));
} }
// Default is to return a string. // Default is to return a string.
@ -2923,14 +2714,12 @@ class X509
$value = array_pop($value); // Always strip data type. $value = array_pop($value); // Always strip data type.
} }
} elseif (is_object($value) && $value instanceof Element) { } elseif (is_object($value) && $value instanceof Element) {
$callback = function ($x) { $callback = create_function('$x', 'return "\x" . bin2hex($x[0]);');
return "\x" . bin2hex($x[0]);
};
$value = strtoupper(preg_replace_callback('#[^\x20-\x7E]#', $callback, $value->element)); $value = strtoupper(preg_replace_callback('#[^\x20-\x7E]#', $callback, $value->element));
} }
$output.= $desc . '=' . $value; $output.= $desc . '=' . $value;
$result[$desc] = isset($result[$desc]) ? $result[$desc] = isset($result[$desc]) ?
array_merge((array) $result[$desc], array($value)) : array_merge((array) $dn[$prop], array($value)) :
$value; $value;
$start = false; $start = false;
} }
@ -3146,7 +2935,7 @@ class X509
switch ($keyinfo['algorithm']['algorithm']) { switch ($keyinfo['algorithm']['algorithm']) {
case 'rsaEncryption': case 'rsaEncryption':
$publicKey = new RSA(); $publicKey = new RSA();
$publicKey->loadKey($key); $publicKey->load($key);
$publicKey->setPublicKey(); $publicKey->setPublicKey();
break; break;
default: default:
@ -3224,7 +3013,7 @@ class X509
switch ($algorithm) { switch ($algorithm) {
case 'rsaEncryption': case 'rsaEncryption':
$this->publicKey = new RSA(); $this->publicKey = new RSA();
$this->publicKey->loadKey($key); $this->publicKey->load($key);
$this->publicKey->setPublicKey(); $this->publicKey->setPublicKey();
break; break;
default: default:
@ -3259,10 +3048,7 @@ class X509
switch ($algorithm) { switch ($algorithm) {
case 'rsaEncryption': case 'rsaEncryption':
$csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'] $csr['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']
= base64_encode("\0" . base64_decode(preg_replace('#-.+-|[\r\n]#', '', $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;
} }
} }
@ -3285,7 +3071,7 @@ class X509
return $csr; return $csr;
// case self::FORMAT_PEM: // case self::FORMAT_PEM:
default: 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(); $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_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) { if ($temp != false) {
$spkac = $temp; $spkac = $temp;
} }
@ -3351,7 +3137,7 @@ class X509
switch ($algorithm) { switch ($algorithm) {
case 'rsaEncryption': case 'rsaEncryption':
$this->publicKey = new RSA(); $this->publicKey = new RSA();
$this->publicKey->loadKey($key); $this->publicKey->load($key);
$this->publicKey->setPublicKey(); $this->publicKey->setPublicKey();
break; break;
default: default:
@ -3387,7 +3173,7 @@ class X509
switch ($algorithm) { switch ($algorithm) {
case 'rsaEncryption': case 'rsaEncryption':
$spkac['publicKeyAndChallenge']['spki']['subjectPublicKey'] $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; return $spkac;
// case self::FORMAT_PEM: // case self::FORMAT_PEM:
default: 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 // 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->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']);
$this->_mapInDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1); $this->_mapInDNs($crl, 'tbsCertList/issuer/rdnSequence', $asn1);
if ($this->_isSubArrayValid($crl, 'tbsCertList/crlExtensions')) {
$this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1); $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1);
} $rclist = &$this->_subArray($crl, 'tbsCertList/revokedCertificates');
if ($this->_isSubArrayValid($crl, 'tbsCertList/revokedCertificates')) { if (is_array($rclist)) {
$rclist_ref = &$this->_subArrayUnchecked($crl, 'tbsCertList/revokedCertificates');
if ($rclist_ref) {
$rclist = $crl['tbsCertList']['revokedCertificates'];
foreach ($rclist as $i => $extension) { foreach ($rclist as $i => $extension) {
if ($this->_isSubArrayValid($rclist, "$i/crlEntryExtensions", $asn1)) { $this->_mapInExtensions($rclist, "$i/crlEntryExtensions", $asn1);
$this->_mapInExtensions($rclist_ref, "$i/crlEntryExtensions", $asn1);
}
}
} }
} }
@ -3530,7 +3309,7 @@ class X509
return $crl; return $crl;
// case self::FORMAT_PEM: // case self::FORMAT_PEM:
default: 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) function _timeField($date)
{ {
if ($date instanceof Element) { $year = @gmdate("Y", @strtotime($date)); // the same way ASN1.php parses this
return $date;
}
$dateObj = new DateTime($date, new DateTimeZone('GMT'));
$year = $dateObj->format('Y'); // the same way ASN1.php parses this
if ($year < 2050) { if ($year < 2050) {
return array('utcTime' => $date); return array('utcTime' => $date);
} else { } else {
@ -3573,7 +3348,7 @@ class X509
* @access public * @access public
* @return mixed * @return mixed
*/ */
function sign($issuer, $subject, $signatureAlgorithm = 'sha1WithRSAEncryption') function sign($issuer, $subject, $signatureAlgorithm = 'sha256WithRSAEncryption')
{ {
if (!is_object($issuer->privateKey) || empty($issuer->dn)) { if (!is_object($issuer->privateKey) || empty($issuer->dn)) {
return false; return false;
@ -3617,12 +3392,8 @@ class X509
return false; return false;
} }
$startDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get())); $startDate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O');
$startDate = !empty($this->startDate) ? $this->startDate : $startDate->format('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'));
$endDate = new DateTime('+1 year', new DateTimeZone(@date_default_timezone_get()));
$endDate = !empty($this->endDate) ? $this->endDate : $endDate->format('D, d M Y H:i:s O');
/* "The serial number MUST be a positive integer" /* "The serial number MUST be a positive integer"
"Conforming CAs MUST NOT use serialNumber values longer than 20 octets." "Conforming CAs MUST NOT use serialNumber values longer than 20 octets."
-- https://tools.ietf.org/html/rfc5280#section-4.1.2.2 -- https://tools.ietf.org/html/rfc5280#section-4.1.2.2
@ -3638,7 +3409,7 @@ class X509
'tbsCertificate' => 'tbsCertificate' =>
array( array(
'version' => 'v3', 'version' => 'v3',
'serialNumber' => $serialNumber, // $this->setSerialNumber() 'serialNumber' => $serialNumber, // $this->setserialNumber()
'signature' => array('algorithm' => $signatureAlgorithm), 'signature' => array('algorithm' => $signatureAlgorithm),
'issuer' => false, // this is going to be overwritten later 'issuer' => false, // this is going to be overwritten later
'validity' => array( 'validity' => array(
@ -3684,8 +3455,8 @@ class X509
$altName = array(); $altName = array();
if (isset($subject->domains) && count($subject->domains)) { if (isset($subject->domains) && count($subject->domains) > 1) {
$altName = array_map(array('\phpseclib\File\X509', '_dnsName'), $subject->domains); $altName = array_map(array('X509', '_dnsName'), $subject->domains);
} }
if (isset($subject->ipAddresses) && count($subject->ipAddresses)) { if (isset($subject->ipAddresses) && count($subject->ipAddresses)) {
@ -3730,7 +3501,7 @@ class X509
); );
if (!isset($subject->currentKeyIdentifier)) { 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; $origPublicKey = $this->publicKey;
$class = get_class($this->privateKey); $class = get_class($this->privateKey);
$this->publicKey = new $class(); $this->publicKey = new $class();
$this->publicKey->loadKey($this->privateKey->getPublicKey()); $this->publicKey->load($this->privateKey->getPublicKey());
$this->publicKey->setPublicKey(); $this->publicKey->setPublicKey();
if (!($publicKey = $this->_formatSubjectPublicKey())) { if (!($publicKey = $this->_formatSubjectPublicKey())) {
return false; return false;
@ -3821,7 +3592,7 @@ class X509
$origPublicKey = $this->publicKey; $origPublicKey = $this->publicKey;
$class = get_class($this->privateKey); $class = get_class($this->privateKey);
$this->publicKey = new $class(); $this->publicKey = new $class();
$this->publicKey->loadKey($this->privateKey->getPublicKey()); $this->publicKey->load($this->privateKey->getPublicKey());
$this->publicKey->setPublicKey(); $this->publicKey->setPublicKey();
$publicKey = $this->_formatSubjectPublicKey(); $publicKey = $this->_formatSubjectPublicKey();
if (!$publicKey) { if (!$publicKey) {
@ -3890,9 +3661,7 @@ class X509
$currentCert = isset($this->currentCert) ? $this->currentCert : null; $currentCert = isset($this->currentCert) ? $this->currentCert : null;
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null; $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null;
$thisUpdate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O');
$thisUpdate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
$thisUpdate = !empty($this->startDate) ? $this->startDate : $thisUpdate->format('D, d M Y H:i:s O');
if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) { if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) {
$this->currentCert = $crl->currentCert; $this->currentCert = $crl->currentCert;
@ -4011,6 +3780,7 @@ class X509
* @param \phpseclib\File\X509 $subject * @param \phpseclib\File\X509 $subject
* @param string $signatureAlgorithm * @param string $signatureAlgorithm
* @access public * @access public
* @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported
* @return mixed * @return mixed
*/ */
function _sign($key, $signatureAlgorithm) function _sign($key, $signatureAlgorithm)
@ -4025,14 +3795,15 @@ class X509
case 'sha384WithRSAEncryption': case 'sha384WithRSAEncryption':
case 'sha512WithRSAEncryption': case 'sha512WithRSAEncryption':
$key->setHash(preg_replace('#WithRSAEncryption$#', '', $signatureAlgorithm)); $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; 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) function setStartDate($date)
{ {
if (!is_object($date) || !is_a($date, 'DateTime')) { $this->startDate = @date('D, d M Y H:i:s O', @strtotime($date));
$date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
}
$this->startDate = $date->format('D, d M Y H:i:s O');
} }
/** /**
@ -4071,11 +3838,7 @@ class X509
$temp = chr(ASN1::TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp; $temp = chr(ASN1::TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp;
$this->endDate = new Element($temp); $this->endDate = new Element($temp);
} else { } else {
if (!is_object($date) || !is_a($date, 'DateTime')) { $this->endDate = @date('D, d M Y H:i:s O', @strtotime($date));
$date = new DateTime($date, new DateTimeZone(@date_default_timezone_get()));
}
$this->endDate = $date->format('D, d M Y H:i:s O');
} }
} }
@ -4101,74 +3864,6 @@ class X509
$this->caFlag = true; $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 * Get a reference to a subarray
* *
@ -4285,10 +3980,6 @@ class X509
} }
$extensions = array_values($extensions); $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; return $result;
} }
@ -4618,7 +4309,7 @@ class X509
if (empty($value)) { if (empty($value)) {
unset($this->currentKeyIdentifier); unset($this->currentKeyIdentifier);
} else { } else {
$this->currentKeyIdentifier = base64_encode($value); $this->currentKeyIdentifier = Base64::encode($value);
} }
} }
@ -4666,10 +4357,10 @@ class X509
if (empty($raw)) { if (empty($raw)) {
return false; return false;
} }
$raw = base64_decode($raw); $raw = Base64::decode($raw);
// If the key is private, compute identifier from its corresponding public key. // If the key is private, compute identifier from its corresponding public key.
$key = new RSA(); $key = new RSA();
if (!$key->loadKey($raw)) { if (!$key->load($raw)) {
return false; // Not an unencrypted RSA key. return false; // Not an unencrypted RSA key.
} }
if ($key->getPrivateKey() !== false) { // If private. if ($key->getPrivateKey() !== false) { // If private.
@ -4689,7 +4380,7 @@ class X509
} }
return false; return false;
default: // Should be a key object (i.e.: \phpseclib\Crypt\RSA). default: // Should be a key object (i.e.: \phpseclib\Crypt\RSA).
$key = $key->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1); $key = $key->getPublicKey('PKCS1');
break; break;
} }
@ -4719,10 +4410,10 @@ class X509
if ($this->publicKey instanceof RSA) { 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 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 // 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( return array(
'algorithm' => array('algorithm' => 'rsaEncryption'), '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); $i = count($rclist);
$revocationDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
$rclist[] = array('userCertificate' => $serial, $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; return $i;
} }
@ -5023,7 +4713,7 @@ class X509
$temp = preg_replace('#-+[^-]+-+#', '', $temp); $temp = preg_replace('#-+[^-]+-+#', '', $temp);
// remove new lines // remove new lines
$temp = str_replace(array("\r", "\n", ' '), '', $temp); $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; return $temp != false ? $temp : $str;
} }

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,8 @@
namespace phpseclib\Net; namespace phpseclib\Net;
use phpseclib\Exception\FileNotFoundException;
/** /**
* Pure-PHP implementations of SCP. * Pure-PHP implementations of SCP.
* *
@ -99,7 +101,9 @@ class SCP
* *
* Connects to an SSH server * 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 * @return \phpseclib\Net\SCP
* @access public * @access public
*/ */
@ -135,6 +139,7 @@ class SCP
* @param string $data * @param string $data
* @param int $mode * @param int $mode
* @param callable $callback * @param callable $callback
* @throws \phpseclib\Exception\FileNotFoundException if you're uploading via a file and the file doesn't exist
* @return bool * @return bool
* @access public * @access public
*/ */
@ -144,11 +149,6 @@ class SCP
return false; 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 if (!$this->ssh->exec('scp -t ' . escapeshellarg($remote_file), false)) { // -t = to
return false; return false;
} }
@ -168,8 +168,7 @@ class SCP
$size = strlen($data); $size = strlen($data);
} else { } else {
if (!is_file($data)) { if (!is_file($data)) {
user_error("$data is not a valid file", E_USER_NOTICE); throw new FileNotFoundException("$data is not a valid file");
return false;
} }
$fp = @fopen($data, 'rb'); $fp = @fopen($data, 'rb');
@ -289,6 +288,7 @@ class SCP
* Receives a packet from an SSH server * Receives a packet from an SSH server
* *
* @return string * @return string
* @throws \UnexpectedValueException on receipt of an unexpected packet
* @access private * @access private
*/ */
function _receive() function _receive()
@ -304,9 +304,6 @@ class SCP
$response = $this->ssh->_get_binary_packet(); $response = $this->ssh->_get_binary_packet();
switch ($response[SSH1::RESPONSE_TYPE]) { switch ($response[SSH1::RESPONSE_TYPE]) {
case NET_SSH1_SMSG_STDOUT_DATA: case NET_SSH1_SMSG_STDOUT_DATA:
if (strlen($response[SSH1::RESPONSE_DATA]) < 4) {
return false;
}
extract(unpack('Nlength', $response[SSH1::RESPONSE_DATA])); extract(unpack('Nlength', $response[SSH1::RESPONSE_DATA]));
return $this->ssh->_string_shift($response[SSH1::RESPONSE_DATA], $length); return $this->ssh->_string_shift($response[SSH1::RESPONSE_DATA], $length);
case NET_SSH1_SMSG_STDERR_DATA: case NET_SSH1_SMSG_STDERR_DATA:
@ -317,8 +314,7 @@ class SCP
$this->ssh->bitmap = 0; $this->ssh->bitmap = 0;
return false; return false;
default: default:
user_error('Unknown packet received', E_USER_NOTICE); throw new \UnexpectedValueException('Unknown packet received');
return false;
} }
} }
} }

View File

@ -37,6 +37,9 @@
namespace phpseclib\Net; namespace phpseclib\Net;
use ParagonIE\ConstantTime\Hex;
use phpseclib\Exception\FileNotFoundException;
/** /**
* Pure-PHP implementations of SFTP. * 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 * 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. * concurrent actions, so it's somewhat academic, here.
* *
* @var boolean * @var int
* @see self::_send_sftp_packet() * @see self::_send_sftp_packet()
* @access private * @access private
*/ */
var $use_request_id = false; var $request_id = false;
/** /**
* The Packet Type * The Packet Type
@ -158,7 +161,7 @@ class SFTP extends SSH2
* Current working directory * Current working directory
* *
* @var string * @var string
* @see self::realpath() * @see self::_realpath()
* @see self::chdir() * @see self::chdir()
* @access private * @access private
*/ */
@ -187,7 +190,7 @@ class SFTP extends SSH2
* *
* @see self::getSFTPErrors() * @see self::getSFTPErrors()
* @see self::getLastSFTPError() * @see self::getLastSFTPError()
* @var array * @var string
* @access private * @access private
*/ */
var $sftp_errors = array(); var $sftp_errors = array();
@ -236,29 +239,6 @@ class SFTP extends SSH2
*/ */
var $sortOptions = array(); 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. * 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 // 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. // 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. // 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 // 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 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( $this->open_flags = array(
0x00000001 => 'NET_SFTP_OPEN_READ', 0x00000001 => 'NET_SFTP_OPEN_READ',
0x00000002 => 'NET_SFTP_OPEN_WRITE', 0x00000002 => 'NET_SFTP_OPEN_WRITE',
@ -395,7 +375,7 @@ class SFTP extends SSH2
); );
if (!defined('NET_SFTP_QUEUE_SIZE')) { 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 $username
* @param string $password * @param string $password
* @throws \UnexpectedValueException on receipt of unexpected packets
* @return bool * @return bool
* @access public * @access public
*/ */
@ -432,7 +413,7 @@ class SFTP extends SSH2
$this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN; $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) { if ($response === false) {
return false; return false;
} }
@ -453,7 +434,7 @@ class SFTP extends SSH2
$this->channel_status[self::CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; $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) { if ($response === false) {
// from PuTTY's psftp.exe // from PuTTY's psftp.exe
$command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" . $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; $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) { if ($response === false) {
return false; return false;
} }
@ -491,24 +472,14 @@ class SFTP extends SSH2
$response = $this->_get_sftp_packet(); $response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_VERSION) { if ($this->packet_type != NET_SFTP_VERSION) {
user_error('Expected SSH_FXP_VERSION'); throw new \UnexpectedValueException('Expected SSH_FXP_VERSION');
return false;
} }
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nversion', $this->_string_shift($response, 4))); extract(unpack('Nversion', $this->_string_shift($response, 4)));
$this->version = $version; $this->version = $version;
while (!empty($response)) { while (!empty($response)) {
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
$key = $this->_string_shift($response, $length); $key = $this->_string_shift($response, $length);
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
$value = $this->_string_shift($response, $length); $value = $this->_string_shift($response, $length);
$this->extensions[$key] = $value; $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: A Note on SFTPv4/5/6 support:
@ -598,26 +569,6 @@ class SFTP extends SSH2
$this->stat_cache = array(); $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 * Returns the current directory name
* *
@ -639,15 +590,12 @@ class SFTP extends SSH2
function _logError($response, $status = -1) function _logError($response, $status = -1)
{ {
if ($status == -1) { if ($status == -1) {
if (strlen($response) < 4) {
return;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4))); extract(unpack('Nstatus', $this->_string_shift($response, 4)));
} }
$error = $this->status_codes[$status]; $error = $this->status_codes[$status];
if ($this->version > 2 || strlen($response) < 4) { if ($this->version > 2) {
extract(unpack('Nlength', $this->_string_shift($response, 4))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->sftp_errors[] = $error . ': ' . $this->_string_shift($response, $length); $this->sftp_errors[] = $error . ': ' . $this->_string_shift($response, $length);
} else { } 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 * 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 * 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. * the absolute (canonicalized) path.
* *
* If canonicalize_paths has been disabled using disablePathCanonicalization(), $path is returned as-is.
*
* @see self::chdir() * @see self::chdir()
* @see self::disablePathCanonicalization()
* @param string $path * @param string $path
* @throws \UnexpectedValueException on receipt of unexpected packets
* @return mixed * @return mixed
* @access private * @access private
*/ */
function _realpath($path) function _realpath($path)
{ {
if (!$this->canonicalize_paths) {
return $path;
}
if ($this->pwd === false) { if ($this->pwd === false) {
// http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9 // 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))) { 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 // 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. // 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 $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))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
return $this->_string_shift($response, $length); return $this->_string_shift($response, $length);
case NET_SFTP_STATUS: case NET_SFTP_STATUS:
$this->_logError($response); $this->_logError($response);
return false; return false;
default: default:
user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_NAME or SSH_FXP_STATUS');
return false;
} }
} }
@ -744,6 +667,7 @@ class SFTP extends SSH2
* Changes the current directory * Changes the current directory
* *
* @param string $dir * @param string $dir
* @throws \UnexpectedValueException on receipt of unexpected packets
* @return bool * @return bool
* @access public * @access public
*/ */
@ -788,8 +712,7 @@ class SFTP extends SSH2
$this->_logError($response); $this->_logError($response);
return false; return false;
default: default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
return false;
} }
if (!$this->_close_handle($handle)) { if (!$this->_close_handle($handle)) {
@ -842,7 +765,6 @@ class SFTP extends SSH2
} }
if (is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $value)))) { if (is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $value)))) {
$temp = $this->_nlist_helper($dir . '/' . $value, true, $relativeDir . $value . '/'); $temp = $this->_nlist_helper($dir . '/' . $value, true, $relativeDir . $value . '/');
$temp = is_array($temp) ? $temp : array();
$result = array_merge($result, $temp); $result = array_merge($result, $temp);
} else { } else {
$result[] = $relativeDir . $value; $result[] = $relativeDir . $value;
@ -874,17 +796,7 @@ class SFTP extends SSH2
unset($files[$key]); unset($files[$key]);
continue; continue;
} }
$is_directory = false; if ($key != '.' && $key != '..' && is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key)))) {
if ($key != '.' && $key != '..') {
if ($this->use_stat_cache) {
$is_directory = is_array($this->_query_stat_cache($this->_realpath($dir . '/' . $key)));
} else {
$stat = $this->lstat($dir . '/' . $key);
$is_directory = $stat && $stat['type'] === NET_SFTP_TYPE_DIRECTORY;
}
}
if ($is_directory) {
$depth++; $depth++;
$files[$key] = $this->rawlist($dir . '/' . $key, true); $files[$key] = $this->rawlist($dir . '/' . $key, true);
$depth--; $depth--;
@ -902,6 +814,7 @@ class SFTP extends SSH2
* @param string $dir * @param string $dir
* @param bool $raw * @param bool $raw
* @return mixed * @return mixed
* @throws \UnexpectedValueException on receipt of unexpected packets
* @access private * @access private
*/ */
function _list($dir, $raw = true) function _list($dir, $raw = true)
@ -933,8 +846,7 @@ class SFTP extends SSH2
$this->_logError($response); $this->_logError($response);
return false; return false;
default: default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
return false;
} }
$this->_update_stat_cache($dir, array()); $this->_update_stat_cache($dir, array());
@ -951,19 +863,10 @@ class SFTP extends SSH2
$response = $this->_get_sftp_packet(); $response = $this->_get_sftp_packet();
switch ($this->packet_type) { switch ($this->packet_type) {
case NET_SFTP_NAME: case NET_SFTP_NAME:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Ncount', $this->_string_shift($response, 4))); extract(unpack('Ncount', $this->_string_shift($response, 4)));
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
$shortname = $this->_string_shift($response, $length); $shortname = $this->_string_shift($response, $length);
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
$longname = $this->_string_shift($response, $length); $longname = $this->_string_shift($response, $length);
$attributes = $this->_parseAttributes($response); $attributes = $this->_parseAttributes($response);
@ -990,9 +893,6 @@ class SFTP extends SSH2
} }
break; break;
case NET_SFTP_STATUS: case NET_SFTP_STATUS:
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4))); extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_EOF) { if ($status != NET_SFTP_STATUS_EOF) {
$this->_logError($response, $status); $this->_logError($response, $status);
@ -1000,8 +900,7 @@ class SFTP extends SSH2
} }
break 2; break 2;
default: default:
user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_NAME or SSH_FXP_STATUS');
return false;
} }
} }
@ -1167,7 +1066,7 @@ class SFTP extends SSH2
$temp[$dir] = array(); $temp[$dir] = array();
} }
if ($i === $max) { if ($i === $max) {
if (is_object($temp[$dir]) && is_object($value)) { if (is_object($temp[$dir])) {
if (!isset($value->stat) && isset($temp[$dir]->stat)) { if (!isset($value->stat) && isset($temp[$dir]->stat)) {
$value->stat = $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 * 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. * The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT.
* *
* @param string $filename * @param string $filename
* @param int $type * @param int $type
* @throws \UnexpectedValueException on receipt of unexpected packets
* @return mixed * @return mixed
* @access private * @access private
*/ */
@ -1380,8 +1280,7 @@ class SFTP extends SSH2
return false; return false;
} }
user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS');
return false;
} }
/** /**
@ -1407,6 +1306,7 @@ class SFTP extends SSH2
* @param string $filename * @param string $filename
* @param int $time * @param int $time
* @param int $atime * @param int $atime
* @throws \UnexpectedValueException on receipt of unexpected packets
* @return bool * @return bool
* @access public * @access public
*/ */
@ -1443,8 +1343,7 @@ class SFTP extends SSH2
$this->_logError($response); $this->_logError($response);
break; break;
default: default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
return false;
} }
return $this->_setstat($filename, $attr, false); return $this->_setstat($filename, $attr, false);
@ -1497,6 +1396,7 @@ class SFTP extends SSH2
* @param int $mode * @param int $mode
* @param string $filename * @param string $filename
* @param bool $recursive * @param bool $recursive
* @throws \UnexpectedValueException on receipt of unexpected packets
* @return mixed * @return mixed
* @access public * @access public
*/ */
@ -1516,7 +1416,7 @@ class SFTP extends SSH2
return true; 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 // 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. // tell us if the file actually exists.
// incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following: // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
@ -1535,8 +1435,7 @@ class SFTP extends SSH2
return false; return false;
} }
user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS');
return false;
} }
/** /**
@ -1545,6 +1444,7 @@ class SFTP extends SSH2
* @param string $filename * @param string $filename
* @param string $attr * @param string $attr
* @param bool $recursive * @param bool $recursive
* @throws \UnexpectedValueException on receipt of unexpected packets
* @return bool * @return bool
* @access private * @access private
*/ */
@ -1583,13 +1483,9 @@ class SFTP extends SSH2
*/ */
$response = $this->_get_sftp_packet(); $response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) { if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_STATUS');
return false;
} }
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4))); extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) { if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status); $this->_logError($response, $status);
@ -1675,6 +1571,7 @@ class SFTP extends SSH2
* Return the target of a symbolic link * Return the target of a symbolic link
* *
* @param string $link * @param string $link
* @throws \UnexpectedValueException on receipt of unexpected packets
* @return mixed * @return mixed
* @access public * @access public
*/ */
@ -1698,22 +1595,15 @@ class SFTP extends SSH2
$this->_logError($response); $this->_logError($response);
return false; return false;
default: default:
user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_NAME or SSH_FXP_STATUS');
return false;
} }
if (strlen($response) < 4) {
return false;
}
extract(unpack('Ncount', $this->_string_shift($response, 4))); extract(unpack('Ncount', $this->_string_shift($response, 4)));
// the file isn't a symlink // the file isn't a symlink
if (!$count) { if (!$count) {
return false; return false;
} }
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($response, 4))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
return $this->_string_shift($response, $length); return $this->_string_shift($response, $length);
} }
@ -1725,6 +1615,7 @@ class SFTP extends SSH2
* *
* @param string $target * @param string $target
* @param string $link * @param string $link
* @throws \UnexpectedValueException on receipt of unexpected packets
* @return bool * @return bool
* @access public * @access public
*/ */
@ -1734,7 +1625,7 @@ class SFTP extends SSH2
return false; return false;
} }
//$target = $this->_realpath($target); $target = $this->_realpath($target);
$link = $this->_realpath($link); $link = $this->_realpath($link);
$packet = pack('Na*Na*', strlen($target), $target, strlen($link), $link); $packet = pack('Na*Na*', strlen($target), $target, strlen($link), $link);
@ -1744,13 +1635,9 @@ class SFTP extends SSH2
$response = $this->_get_sftp_packet(); $response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) { if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_STATUS');
return false;
} }
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4))); extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) { if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status); $this->_logError($response, $status);
@ -1800,6 +1687,7 @@ class SFTP extends SSH2
* *
* @param string $dir * @param string $dir
* @return bool * @return bool
* @throws \UnexpectedValueException on receipt of unexpected packets
* @access private * @access private
*/ */
function _mkdir_helper($dir, $attr) function _mkdir_helper($dir, $attr)
@ -1810,13 +1698,9 @@ class SFTP extends SSH2
$response = $this->_get_sftp_packet(); $response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) { if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_STATUS');
return false;
} }
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4))); extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) { if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status); $this->_logError($response, $status);
@ -1830,6 +1714,7 @@ class SFTP extends SSH2
* Removes a directory. * Removes a directory.
* *
* @param string $dir * @param string $dir
* @throws \UnexpectedValueException on receipt of unexpected packets
* @return bool * @return bool
* @access public * @access public
*/ */
@ -1850,13 +1735,9 @@ class SFTP extends SSH2
$response = $this->_get_sftp_packet(); $response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) { if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_STATUS');
return false;
} }
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4))); extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) { if ($status != NET_SFTP_STATUS_OK) {
// presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED? // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED?
@ -1914,6 +1795,9 @@ class SFTP extends SSH2
* @param int $start * @param int $start
* @param int $local_start * @param int $local_start
* @param callable|null $progressCallback * @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 * @return bool
* @access public * @access public
* @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - \phpseclib\Net\SFTP::setMode(). * @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); $this->_logError($response);
return false; return false;
default: default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
return false;
} }
// http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3
@ -1970,26 +1853,18 @@ class SFTP extends SSH2
switch (true) { switch (true) {
case $mode & self::SOURCE_CALLBACK: case $mode & self::SOURCE_CALLBACK:
if (!is_callable($data)) { 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; $dataCallback = $data;
// do nothing // do nothing
break; break;
case is_resource($data): case is_resource($data):
$mode = $mode & ~self::SOURCE_LOCAL_FILE; $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; break;
case $mode & self::SOURCE_LOCAL_FILE: case $mode & self::SOURCE_LOCAL_FILE:
if (!is_file($data)) { if (!is_file($data)) {
user_error("$data is not a valid file"); throw new FileNotFoundException("$data is not a valid file");
return false;
} }
$fp = @fopen($data, 'rb'); $fp = @fopen($data, 'rb');
if (!$fp) { if (!$fp) {
@ -1999,7 +1874,7 @@ class SFTP extends SSH2
if (isset($fp)) { if (isset($fp)) {
$stat = fstat($fp); $stat = fstat($fp);
$size = !empty($stat) ? $stat['size'] : 0; $size = $stat['size'];
if ($local_start >= 0) { if ($local_start >= 0) {
fseek($fp, $local_start); 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" // 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; $sftp_packet_size-= strlen($handle) + 25;
$i = 0; $i = 0;
while ($dataCallback || ($size === 0 || $sent < $size)) { while ($dataCallback || $sent < $size) {
if ($dataCallback) { if ($dataCallback) {
$temp = call_user_func($dataCallback, $sftp_packet_size); $temp = call_user_func($dataCallback, $sftp_packet_size);
if (is_null($temp)) { if (is_null($temp)) {
@ -2026,11 +1901,7 @@ class SFTP extends SSH2
} }
} else { } else {
$temp = isset($fp) ? fread($fp, $sftp_packet_size) : substr($data, $sent, $sftp_packet_size); $temp = isset($fp) ? fread($fp, $sftp_packet_size) : substr($data, $sent, $sftp_packet_size);
if ($temp === false || $temp === '') {
break;
} }
}
$subtemp = $offset + $sent; $subtemp = $offset + $sent;
$packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp); $packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp);
if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet)) { if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet)) {
@ -2078,6 +1949,7 @@ class SFTP extends SSH2
* *
* @param int $i * @param int $i
* @return bool * @return bool
* @throws \UnexpectedValueException on receipt of unexpected packets
* @access private * @access private
*/ */
function _read_put_responses($i) function _read_put_responses($i)
@ -2085,13 +1957,9 @@ class SFTP extends SSH2
while ($i--) { while ($i--) {
$response = $this->_get_sftp_packet(); $response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) { if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_STATUS');
return false;
} }
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4))); extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) { if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status); $this->_logError($response, $status);
@ -2107,6 +1975,7 @@ class SFTP extends SSH2
* *
* @param string $handle * @param string $handle
* @return bool * @return bool
* @throws \UnexpectedValueException on receipt of unexpected packets
* @access private * @access private
*/ */
function _close_handle($handle) 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 // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3
$response = $this->_get_sftp_packet(); $response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) { if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_STATUS');
return false;
} }
if (strlen($response) < 4) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4))); extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) { if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status); $this->_logError($response, $status);
@ -2148,11 +2013,11 @@ class SFTP extends SSH2
* @param string $local_file * @param string $local_file
* @param int $offset * @param int $offset
* @param int $length * @param int $length
* @param callable|null $progressCallback * @throws \UnexpectedValueException on receipt of unexpected packets
* @return mixed * @return mixed
* @access public * @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)) { if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false; return false;
@ -2177,8 +2042,7 @@ class SFTP extends SSH2
$this->_logError($response); $this->_logError($response);
return false; return false;
default: default:
user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
return false;
} }
if (is_resource($local_file)) { 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_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); $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) { if ($fclose_check) {
fclose($fp); fclose($fp);
} }
@ -2218,9 +2082,6 @@ class SFTP extends SSH2
} }
$packet = null; $packet = null;
$read+= $packet_size; $read+= $packet_size;
if (is_callable($progressCallback)) {
call_user_func($progressCallback, $read);
}
$i++; $i++;
} }
@ -2228,17 +2089,15 @@ class SFTP extends SSH2
break; break;
} }
$packets_sent = $i - 1;
$clear_responses = false; $clear_responses = false;
while ($i > 0) { while ($i > 0) {
$i--; $i--;
if ($clear_responses) { if ($clear_responses) {
$this->_get_sftp_packet($packets_sent - $i); $this->_get_sftp_packet();
continue; continue;
} else { } else {
$response = $this->_get_sftp_packet($packets_sent - $i); $response = $this->_get_sftp_packet();
} }
switch ($this->packet_type) { switch ($this->packet_type) {
@ -2261,7 +2120,7 @@ class SFTP extends SSH2
if ($fclose_check) { if ($fclose_check) {
fclose($fp); 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; $response = null;
} }
@ -2297,6 +2156,7 @@ class SFTP extends SSH2
* @param string $path * @param string $path
* @param bool $recursive * @param bool $recursive
* @return bool * @return bool
* @throws \UnexpectedValueException on receipt of unexpected packets
* @access public * @access public
*/ */
function delete($path, $recursive = true) function delete($path, $recursive = true)
@ -2305,15 +2165,6 @@ class SFTP extends SSH2
return false; 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); $path = $this->_realpath($path);
if ($path === false) { if ($path === false) {
return false; return false;
@ -2326,14 +2177,10 @@ class SFTP extends SSH2
$response = $this->_get_sftp_packet(); $response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) { if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_STATUS');
return false;
} }
// if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED // 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))); extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) { if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status); $this->_logError($response, $status);
@ -2732,6 +2579,7 @@ class SFTP extends SSH2
* @param string $oldname * @param string $oldname
* @param string $newname * @param string $newname
* @return bool * @return bool
* @throws \UnexpectedValueException on receipt of unexpected packets
* @access public * @access public
*/ */
function rename($oldname, $newname) function rename($oldname, $newname)
@ -2754,14 +2602,10 @@ class SFTP extends SSH2
$response = $this->_get_sftp_packet(); $response = $this->_get_sftp_packet();
if ($this->packet_type != NET_SFTP_STATUS) { if ($this->packet_type != NET_SFTP_STATUS) {
user_error('Expected SSH_FXP_STATUS'); throw new \UnexpectedValueException('Expected SSH_FXP_STATUS');
return false;
} }
// if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED // 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))); extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) { if ($status != NET_SFTP_STATUS_OK) {
$this->_logError($response, $status); $this->_logError($response, $status);
@ -2789,10 +2633,6 @@ class SFTP extends SSH2
function _parseAttributes(&$response) function _parseAttributes(&$response)
{ {
$attr = array(); $attr = array();
if (strlen($response) < 4) {
user_error('Malformed file attributes');
return array();
}
extract(unpack('Nflags', $this->_string_shift($response, 4))); extract(unpack('Nflags', $this->_string_shift($response, 4)));
// SFTPv4+ have a type field (a byte) that follows the above flag field // SFTPv4+ have a type field (a byte) that follows the above flag field
foreach ($this->attributes as $key => $value) { foreach ($this->attributes as $key => $value) {
@ -2804,20 +2644,12 @@ class SFTP extends SSH2
// IEEE 754 binary64 "double precision" on such platforms and // IEEE 754 binary64 "double precision" on such platforms and
// as such can represent integers of at least 2^50 without loss // as such can represent integers of at least 2^50 without loss
// of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB. // 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; break;
case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only) 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)); $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8));
break; break;
case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004 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)); $attr+= unpack('Npermissions', $this->_string_shift($response, 4));
// mode == permissions; permissions was the original array key and is retained for bc purposes. // 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 // mode was added because that's the more industry standard terminology
@ -2828,29 +2660,13 @@ class SFTP extends SSH2
} }
break; break;
case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008 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)); $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8));
break; break;
case NET_SFTP_ATTR_EXTENDED: // 0x80000000 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))); extract(unpack('Ncount', $this->_string_shift($response, 4)));
for ($i = 0; $i < $count; $i++) { 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))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
$key = $this->_string_shift($response, $length); $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))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
$attr[$key] = $this->_string_shift($response, $length); $attr[$key] = $this->_string_shift($response, $length);
} }
@ -2947,10 +2763,10 @@ class SFTP extends SSH2
* @return bool * @return bool
* @access private * @access private
*/ */
function _send_sftp_packet($type, $data, $request_id = 1) function _send_sftp_packet($type, $data)
{ {
$packet = $this->use_request_id ? $packet = $this->request_id !== false ?
pack('NCNa*', strlen($data) + 5, $type, $request_id, $data) : pack('NCNa*', strlen($data) + 5, $type, $this->request_id, $data) :
pack('NCa*', strlen($data) + 1, $type, $data); pack('NCa*', strlen($data) + 1, $type, $data);
$start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
@ -2988,24 +2804,15 @@ class SFTP extends SSH2
* @return string * @return string
* @access private * @access private
*/ */
function _get_sftp_packet($request_id = null) function _get_sftp_packet()
{ {
if (isset($request_id) && isset($this->requestBuffer[$request_id])) { $this->curTimeout = false;
$this->packet_type = $this->requestBuffer[$request_id]['packet_type'];
$temp = $this->requestBuffer[$request_id]['packet'];
unset($this->requestBuffer[$request_id]);
return $temp;
}
// in SSH2.php the timeout is cumulative per function call. eg. exec() will
// timeout after 10s. but for SFTP.php it's cumulative per packet
$this->curTimeout = $this->timeout;
$start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
// SFTP packet length // SFTP packet length
while (strlen($this->packet_buffer) < 4) { 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)) { if (is_bool($temp)) {
$this->packet_type = false; $this->packet_type = false;
$this->packet_buffer = ''; $this->packet_buffer = '';
@ -3013,23 +2820,13 @@ class SFTP extends SSH2
} }
$this->packet_buffer.= $temp; $this->packet_buffer.= $temp;
} }
if (strlen($this->packet_buffer) < 4) {
return false;
}
extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4))); extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4)));
$tempLength = $length; $tempLength = $length;
$tempLength-= strlen($this->packet_buffer); $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 // SFTP packet type and data payload
while ($tempLength > 0) { while ($tempLength > 0) {
$temp = $this->_get_channel_packet(self::CHANNEL, true); $temp = $this->_get_channel_packet(self::CHANNEL);
if (is_bool($temp)) { if (is_bool($temp)) {
$this->packet_type = false; $this->packet_type = false;
$this->packet_buffer = ''; $this->packet_buffer = '';
@ -3043,8 +2840,8 @@ class SFTP extends SSH2
$this->packet_type = ord($this->_string_shift($this->packet_buffer)); $this->packet_type = ord($this->_string_shift($this->packet_buffer));
if ($this->use_request_id) { if ($this->request_id !== false) {
extract(unpack('Npacket_id', $this->_string_shift($this->packet_buffer, 4))); // remove the request id $this->_string_shift($this->packet_buffer, 4); // remove the request id
$length-= 5; // account for the request id and the packet type $length-= 5; // account for the request id and the packet type
} else { } else {
$length-= 1; // account for the packet type $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; return $packet;
} }
/** /**
* Returns a log of the packets that have been sent and received. * 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 * @access public
* @return string or Array * @return string or Array
@ -3105,7 +2894,7 @@ class SFTP extends SSH2
/** /**
* Returns all errors * Returns all errors
* *
* @return array * @return string
* @access public * @access public
*/ */
function getSFTPErrors() function getSFTPErrors()

View File

@ -19,6 +19,7 @@ namespace phpseclib\Net\SFTP;
use phpseclib\Crypt\RSA; use phpseclib\Crypt\RSA;
use phpseclib\Net\SFTP; use phpseclib\Net\SFTP;
use phpseclib\Net\SSH2;
/** /**
* SFTP Stream Wrapper * SFTP Stream Wrapper
@ -177,13 +178,12 @@ class Stream
} }
} }
if ($host[0] == '$') { if (preg_match('/^{[a-z0-9]+}$/i', $host)) {
$host = substr($host, 1); $host = SSH2::getConnectionByResourceId($host);
global ${$host}; if ($host === false) {
if (($$host instanceof SFTP) === false) {
return false; return false;
} }
$this->sftp = $$host; $this->sftp = $host;
} else { } else {
if (isset($this->context)) { if (isset($this->context)) {
$context = stream_context_get_options($this->context); $context = stream_context_get_options($this->context);

View File

@ -48,6 +48,7 @@
namespace phpseclib\Net; namespace phpseclib\Net;
use ParagonIE\ConstantTime\Hex;
use phpseclib\Crypt\DES; use phpseclib\Crypt\DES;
use phpseclib\Crypt\Random; use phpseclib\Crypt\Random;
use phpseclib\Crypt\TripleDES; use phpseclib\Crypt\TripleDES;
@ -537,14 +538,15 @@ class SSH1
* Connect to an SSHv1 server * Connect to an SSHv1 server
* *
* @return bool * @return bool
* @throws \UnexpectedValueException on receipt of unexpected packets
* @throws \RuntimeException on other errors
* @access private * @access private
*/ */
function _connect() function _connect()
{ {
$this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->connectionTimeout); $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->connectionTimeout);
if (!$this->fsock) { if (!$this->fsock) {
user_error(rtrim("Cannot connect to {$this->host}:{$this->port}. Error $errno. $errstr")); throw new \RuntimeException(rtrim("Cannot connect to $host. Error $errno. $errstr"));
return false;
} }
$this->server_identification = $init_line = fgets($this->fsock, 255); $this->server_identification = $init_line = fgets($this->fsock, 255);
@ -555,66 +557,45 @@ class SSH1
} }
if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) { if (!preg_match('#SSH-([0-9\.]+)-(.+)#', $init_line, $parts)) {
user_error('Can only connect to SSH servers'); throw new \RuntimeException('Can only connect to SSH servers');
return false;
} }
if ($parts[1][0] != 1) { if ($parts[1][0] != 1) {
user_error("Cannot connect to SSH $parts[1] servers"); throw new \RuntimeException("Cannot connect to $parts[1] servers");
return false;
} }
fputs($this->fsock, $this->identifier."\r\n"); fputs($this->fsock, $this->identifier."\r\n");
$response = $this->_get_binary_packet(); $response = $this->_get_binary_packet();
if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) { if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_PUBLIC_KEY) {
user_error('Expected SSH_SMSG_PUBLIC_KEY'); throw new \UnexpectedValueException('Expected SSH_SMSG_PUBLIC_KEY');
return false;
} }
$anti_spoofing_cookie = $this->_string_shift($response[self::RESPONSE_DATA], 8); $anti_spoofing_cookie = $this->_string_shift($response[self::RESPONSE_DATA], 8);
$this->_string_shift($response[self::RESPONSE_DATA], 4); $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)); $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); $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; $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)); $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); $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->server_key_public_modulus = $server_key_public_modulus;
$this->_string_shift($response[self::RESPONSE_DATA], 4); $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)); $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); $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; $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)); $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); $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->host_key_public_modulus = $host_key_public_modulus;
$this->_string_shift($response[self::RESPONSE_DATA], 4); $this->_string_shift($response[self::RESPONSE_DATA], 4);
// get a list of the supported ciphers // 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))); extract(unpack('Nsupported_ciphers_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4)));
foreach ($this->supported_ciphers as $mask => $name) { foreach ($this->supported_ciphers as $mask => $name) {
if (($supported_ciphers_mask & (1 << $mask)) == 0) { if (($supported_ciphers_mask & (1 << $mask)) == 0) {
unset($this->supported_ciphers[$mask]); unset($this->supported_ciphers[$mask]);
@ -622,9 +603,6 @@ class SSH1
} }
// get a list of the supported authentications // 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))); extract(unpack('Nsupported_authentications_mask', $this->_string_shift($response[self::RESPONSE_DATA], 4)));
foreach ($this->supported_authentications as $mask => $name) { foreach ($this->supported_authentications as $mask => $name) {
if (($supported_authentications_mask & (1 << $mask)) == 0) { 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); $session_key = Random::string(32);
$double_encrypted_session_key = $session_key ^ str_pad($session_id, 32, chr(0)); $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); $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)) { if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_SESSION_KEY'); throw new \RuntimeException('Error sending SSH_CMSG_SESSION_KEY');
return false;
} }
switch ($cipher) { switch ($cipher) {
@ -682,16 +659,20 @@ class SSH1
// $this->crypto = new \phpseclib\Crypt\Null(); // $this->crypto = new \phpseclib\Crypt\Null();
// break; // break;
case self::CIPHER_DES: case self::CIPHER_DES:
$this->crypto = new DES(); $this->crypto = new DES(DES::MODE_CBC);
$this->crypto->disablePadding(); $this->crypto->disablePadding();
$this->crypto->enableContinuousBuffer(); $this->crypto->enableContinuousBuffer();
$this->crypto->setKey(substr($session_key, 0, 8)); $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; break;
case self::CIPHER_3DES: case self::CIPHER_3DES:
$this->crypto = new TripleDES(TripleDES::MODE_3CBC); $this->crypto = new TripleDES(TripleDES::MODE_3CBC);
$this->crypto->disablePadding(); $this->crypto->disablePadding();
$this->crypto->enableContinuousBuffer(); $this->crypto->enableContinuousBuffer();
$this->crypto->setKey(substr($session_key, 0, 24)); $this->crypto->setKey(substr($session_key, 0, 24));
// "All three initialization vectors are initialized to zero."
$this->crypto->setIV(str_repeat("\0", 8));
break; break;
//case self::CIPHER_RC4: //case self::CIPHER_RC4:
// $this->crypto = new RC4(); // $this->crypto = new RC4();
@ -703,8 +684,7 @@ class SSH1
$response = $this->_get_binary_packet(); $response = $this->_get_binary_packet();
if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) { if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
user_error('Expected SSH_SMSG_SUCCESS'); throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS');
return false;
} }
$this->bitmap = self::MASK_CONNECTED; $this->bitmap = self::MASK_CONNECTED;
@ -718,6 +698,8 @@ class SSH1
* @param string $username * @param string $username
* @param string $password * @param string $password
* @return bool * @return bool
* @throws \UnexpectedValueException on receipt of unexpected packets
* @throws \RuntimeException on other errors
* @access public * @access public
*/ */
function login($username, $password = '') function login($username, $password = '')
@ -736,8 +718,7 @@ class SSH1
$data = pack('CNa*', NET_SSH1_CMSG_USER, strlen($username), $username); $data = pack('CNa*', NET_SSH1_CMSG_USER, strlen($username), $username);
if (!$this->_send_binary_packet($data)) { if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_USER'); throw new \RuntimeException('Error sending SSH_CMSG_USER');
return false;
} }
$response = $this->_get_binary_packet(); $response = $this->_get_binary_packet();
@ -749,15 +730,13 @@ class SSH1
$this->bitmap |= self::MASK_LOGIN; $this->bitmap |= self::MASK_LOGIN;
return true; return true;
} elseif ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE) { } elseif ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_FAILURE) {
user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE');
return false;
} }
$data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen($password), $password); $data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen($password), $password);
if (!$this->_send_binary_packet($data)) { if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_AUTH_PASSWORD'); throw new \RuntimeException('Error sending SSH_CMSG_AUTH_PASSWORD');
return false;
} }
// remove the username and password from the last logged packet // 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) { } elseif ($response[self::RESPONSE_TYPE] == NET_SSH1_SMSG_FAILURE) {
return false; return false;
} else { } else {
user_error('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE'); throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE');
return false;
} }
} }
@ -813,20 +791,19 @@ class SSH1
* @see self::interactiveWrite() * @see self::interactiveWrite()
* @param string $cmd * @param string $cmd
* @return mixed * @return mixed
* @throws \RuntimeException on error sending command
* @access public * @access public
*/ */
function exec($cmd, $block = true) function exec($cmd, $block = true)
{ {
if (!($this->bitmap & self::MASK_LOGIN)) { if (!($this->bitmap & self::MASK_LOGIN)) {
user_error('Operation disallowed prior to login()'); throw new \RuntimeException('Operation disallowed prior to login()');
return false;
} }
$data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD, strlen($cmd), $cmd); $data = pack('CNa*', NET_SSH1_CMSG_EXEC_CMD, strlen($cmd), $cmd);
if (!$this->_send_binary_packet($data)) { if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_EXEC_CMD'); throw new \RuntimeException('Error sending SSH_CMSG_EXEC_CMD');
return false;
} }
if (!$block) { if (!$block) {
@ -862,6 +839,8 @@ class SSH1
* @see self::interactiveRead() * @see self::interactiveRead()
* @see self::interactiveWrite() * @see self::interactiveWrite()
* @return bool * @return bool
* @throws \UnexpectedValueException on receipt of unexpected packets
* @throws \RuntimeException on other errors
* @access private * @access private
*/ */
function _initShell() 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); $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)) { if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_REQUEST_PTY'); throw new \RuntimeException('Error sending SSH_CMSG_REQUEST_PTY');
return false;
} }
$response = $this->_get_binary_packet(); $response = $this->_get_binary_packet();
@ -882,15 +860,13 @@ class SSH1
return false; return false;
} }
if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) { if ($response[self::RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
user_error('Expected SSH_SMSG_SUCCESS'); throw new \UnexpectedValueException('Expected SSH_SMSG_SUCCESS');
return false;
} }
$data = pack('C', NET_SSH1_CMSG_EXEC_SHELL); $data = pack('C', NET_SSH1_CMSG_EXEC_SHELL);
if (!$this->_send_binary_packet($data)) { if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_EXEC_SHELL'); throw new \RuntimeException('Error sending SSH_CMSG_EXEC_SHELL');
return false;
} }
$this->bitmap |= self::MASK_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 * 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. * a regular expression.
* *
* @see self::write() * @see self::write()
* @param string $expect * @param string $expect
* @param int $mode * @param int $mode
* @return bool * @return bool
* @throws \RuntimeException on connection error
* @access public * @access public
*/ */
function read($expect, $mode = self::READ_SIMPLE) function read($expect, $mode = self::READ__SIMPLE)
{ {
if (!($this->bitmap & self::MASK_LOGIN)) { if (!($this->bitmap & self::MASK_LOGIN)) {
user_error('Operation disallowed prior to login()'); throw new \RuntimeException('Operation disallowed prior to login()');
return false;
} }
if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
user_error('Unable to initiate an interactive shell session'); throw new \RuntimeException('Unable to initiate an interactive shell session');
return false;
} }
$match = $expect; $match = $expect;
while (true) { while (true) {
if ($mode == self::READ_REGEX) { if ($mode == self::READ__REGEX) {
preg_match($expect, $this->interactiveBuffer, $matches); preg_match($expect, $this->interactiveBuffer, $matches);
$match = isset($matches[0]) ? $matches[0] : ''; $match = isset($matches[0]) ? $matches[0] : '';
} }
@ -962,25 +937,23 @@ class SSH1
* @see self::interactiveRead() * @see self::interactiveRead()
* @param string $cmd * @param string $cmd
* @return bool * @return bool
* @throws \RuntimeException on connection error
* @access public * @access public
*/ */
function interactiveWrite($cmd) function interactiveWrite($cmd)
{ {
if (!($this->bitmap & self::MASK_LOGIN)) { if (!($this->bitmap & self::MASK_LOGIN)) {
user_error('Operation disallowed prior to login()'); throw new \RuntimeException('Operation disallowed prior to login()');
return false;
} }
if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
user_error('Unable to initiate an interactive shell session'); throw new \RuntimeException('Unable to initiate an interactive shell session');
return false;
} }
$data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($cmd), $cmd); $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($cmd), $cmd);
if (!$this->_send_binary_packet($data)) { if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_STDIN'); throw new \RuntimeException('Error sending SSH_CMSG_STDIN');
return false;
} }
return true; return true;
@ -997,18 +970,17 @@ class SSH1
* *
* @see self::interactiveRead() * @see self::interactiveRead()
* @return string * @return string
* @throws \RuntimeException on connection error
* @access public * @access public
*/ */
function interactiveRead() function interactiveRead()
{ {
if (!($this->bitmap & self::MASK_LOGIN)) { if (!($this->bitmap & self::MASK_LOGIN)) {
user_error('Operation disallowed prior to login()'); throw new \RuntimeException('Operation disallowed prior to login()');
return false;
} }
if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) { if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
user_error('Unable to initiate an interactive shell session'); throw new \RuntimeException('Unable to initiate an interactive shell session');
return false;
} }
$read = array($this->fsock); $read = array($this->fsock);
@ -1112,11 +1084,7 @@ class SSH1
} }
$start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
$data = fread($this->fsock, 4); $temp = unpack('Nlength', fread($this->fsock, 4));
if (strlen($data) < 4) {
return false;
}
$temp = unpack('Nlength', $data);
$padding_length = 8 - ($temp['length'] & 7); $padding_length = 8 - ($temp['length'] & 7);
$length = $temp['length'] + $padding_length; $length = $temp['length'] + $padding_length;
@ -1137,9 +1105,6 @@ class SSH1
$type = $raw[$padding_length]; $type = $raw[$padding_length];
$data = substr($raw, $padding_length + 1, -4); $data = substr($raw, $padding_length + 1, -4);
if (strlen($raw) < 4) {
return false;
}
$temp = unpack('Ncrc', substr($raw, -4)); $temp = unpack('Ncrc', substr($raw, -4));
//if ( $temp['crc'] != $this->_crc($padding . $type . $data) ) { //if ( $temp['crc'] != $this->_crc($padding . $type . $data) ) {
@ -1341,9 +1306,9 @@ class SSH1
{ {
/* /*
$rsa = new RSA(); $rsa = new RSA();
$rsa->loadKey($key, RSA::PUBLIC_FORMAT_RAW); $rsa->load($key, 'raw');
$rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1); $rsa->setHash('sha1');
return $rsa->encrypt($m); return $rsa->encrypt($m, RSA::PADDING_PKCS1);
*/ */
// To quote from protocol-1.5.txt: // To quote from protocol-1.5.txt:

File diff suppressed because it is too large Load Diff

View File

@ -33,7 +33,9 @@
namespace phpseclib\System\SSH; namespace phpseclib\System\SSH;
use ParagonIE\ConstantTime\Base64;
use phpseclib\Crypt\RSA; use phpseclib\Crypt\RSA;
use phpseclib\Exception\BadConfigurationException;
use phpseclib\System\SSH\Agent\Identity; use phpseclib\System\SSH\Agent\Identity;
/** /**
@ -43,7 +45,7 @@ use phpseclib\System\SSH\Agent\Identity;
* *
* @package SSH\Agent * @package SSH\Agent
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @access public * @access internal
*/ */
class Agent class Agent
{ {
@ -115,11 +117,12 @@ class Agent
* Default Constructor * Default Constructor
* *
* @return \phpseclib\System\SSH\Agent * @return \phpseclib\System\SSH\Agent
* @throws \phpseclib\Exception\BadConfigurationException if SSH_AUTH_SOCK cannot be found
* @throws \RuntimeException on connection errors
* @access public * @access public
*/ */
function __construct($address = null) function __construct()
{ {
if (!$address) {
switch (true) { switch (true) {
case isset($_SERVER['SSH_AUTH_SOCK']): case isset($_SERVER['SSH_AUTH_SOCK']):
$address = $_SERVER['SSH_AUTH_SOCK']; $address = $_SERVER['SSH_AUTH_SOCK'];
@ -128,14 +131,12 @@ class Agent
$address = $_ENV['SSH_AUTH_SOCK']; $address = $_ENV['SSH_AUTH_SOCK'];
break; break;
default: default:
user_error('SSH_AUTH_SOCK not found'); throw new BadConfigurationException('SSH_AUTH_SOCK not found');
return false;
}
} }
$this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr); $this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr);
if (!$this->fsock) { 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 * Returns an array containing zero or more \phpseclib\System\SSH\Agent\Identity objects
* *
* @return array * @return array
* @throws \RuntimeException on receipt of unexpected packets
* @access public * @access public
*/ */
function requestIdentities() function requestIdentities()
@ -156,15 +158,13 @@ class Agent
$packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES); $packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES);
if (strlen($packet) != fputs($this->fsock, $packet)) { if (strlen($packet) != fputs($this->fsock, $packet)) {
user_error('Connection closed while requesting identities'); throw new \RuntimeException('Connection closed while requesting identities');
return array();
} }
$length = current(unpack('N', fread($this->fsock, 4))); $length = current(unpack('N', fread($this->fsock, 4)));
$type = ord(fread($this->fsock, 1)); $type = ord(fread($this->fsock, 1));
if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) { if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) {
user_error('Unable to request identities'); throw new \RuntimeException('Unable to request identities');
return array();
} }
$identities = array(); $identities = array();
@ -172,7 +172,7 @@ class Agent
for ($i = 0; $i < $keyCount; $i++) { for ($i = 0; $i < $keyCount; $i++) {
$length = current(unpack('N', fread($this->fsock, 4))); $length = current(unpack('N', fread($this->fsock, 4)));
$key_blob = fread($this->fsock, $length); $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))); $length = current(unpack('N', fread($this->fsock, 4)));
if ($length) { if ($length) {
$key_str.= ' ' . fread($this->fsock, $length); $key_str.= ' ' . fread($this->fsock, $length);
@ -182,7 +182,7 @@ class Agent
switch ($key_type) { switch ($key_type) {
case 'ssh-rsa': case 'ssh-rsa':
$key = new RSA(); $key = new RSA();
$key->loadKey($key_str); $key->load($key_str);
break; break;
case 'ssh-dss': case 'ssh-dss':
// not currently supported // not currently supported
@ -278,6 +278,7 @@ class Agent
* *
* @param string $data * @param string $data
* @return data from SSH Agent * @return data from SSH Agent
* @throws \RuntimeException on connection errors
* @access private * @access private
*/ */
function _forward_data($data) function _forward_data($data)
@ -296,7 +297,7 @@ class Agent
} }
if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) { 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 = ''; $this->socket_buffer = '';

View File

@ -15,6 +15,8 @@
namespace phpseclib\System\SSH\Agent; namespace phpseclib\System\SSH\Agent;
use phpseclib\Crypt\RSA;
use phpseclib\Exception\UnsupportedAlgorithmException;
use phpseclib\System\SSH\Agent; use phpseclib\System\SSH\Agent;
/** /**
@ -32,17 +34,6 @@ use phpseclib\System\SSH\Agent;
*/ */
class Identity 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 * Key Object
* *
@ -70,16 +61,6 @@ class Identity
*/ */
var $fsock; var $fsock;
/**
* Signature flags
*
* @var int
* @access private
* @see self::sign()
* @see self::setHash()
*/
var $flags = 0;
/** /**
* Default Constructor. * Default Constructor.
* *
@ -125,50 +106,28 @@ class Identity
* *
* Wrapper for $this->key->getPublicKey() * Wrapper for $this->key->getPublicKey()
* *
* @param int $format optional * @param int $type optional
* @return mixed * @return mixed
* @access public * @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 only supports signatures with sha1 hashes but to maintain BC with RSA.php this function exists
* ssh-agent's only supported mode is \phpseclib\Crypt\RSA::SIGNATURE_PKCS1
* *
* @param int $mode * @param string $hash optional
* @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported
* @access public * @access public
*/ */
function setSignatureMode($mode) function setHash($hash = 'sha1')
{ {
} if ($hash != 'sha1') {
throw new UnsupportedAlgorithmException('ssh-agent can only be used with the sha1 hash');
/**
* 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');
} }
} }
@ -178,53 +137,34 @@ class Identity
* See "2.6.2 Protocol 2 private key signature request" * See "2.6.2 Protocol 2 private key signature request"
* *
* @param string $message * @param string $message
* @param int $padding optional
* @return string * @return string
* @throws \RuntimeException on connection errors
* @throws \phpseclib\Exception\UnsupportedAlgorithmException if the algorithm is unsupported
* @access public * @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 // 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); $packet = pack('Na*', strlen($packet), $packet);
if (strlen($packet) != fputs($this->fsock, $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))); $length = current(unpack('N', fread($this->fsock, 4)));
$type = ord(fread($this->fsock, 1)); $type = ord(fread($this->fsock, 1));
if ($type != Agent::SSH_AGENT_SIGN_RESPONSE) { 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); $signature_blob = fread($this->fsock, $length - 1);
$length = current(unpack('N', $this->_string_shift($signature_blob, 4))); // the only other signature format defined - ssh-dss - is the same length as ssh-rsa
if ($length != strlen($signature_blob)) { // the + 12 is for the other various SSH added length fields
user_error('Malformed signature blob'); return substr($signature_blob, strlen('ssh-rsa') + 12);
}
$length = current(unpack('N', $this->_string_shift($signature_blob, 4)));
if ($length > strlen($signature_blob) + 4) {
user_error('Malformed signature blob');
}
$type = $this->_string_shift($signature_blob, $length);
$this->_string_shift($signature_blob, 4);
return $signature_blob;
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param string $string
* @param int $index
* @return string
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
} }
} }

View File

@ -1,14 +1,18 @@
<?php <?php
/** /**
* Bootstrapping File for phpseclib * 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 * @license http://www.opensource.org/licenses/mit-license.html MIT License
*/ */
if (extension_loaded('mbstring')) { if (extension_loaded('mbstring')) {
// 2 - MB_OVERLOAD_STRING // 2 - MB_OVERLOAD_STRING
if (ini_get('mbstring.func_overload') & 2) { if (ini_get('mbstring.func_overload') & 2) {
throw new \UnexpectedValueException( throw new UnexpectedValueException(
'Overloading of string functions using mbstring.func_overload ' . 'Overloading of string functions using mbstring.func_overload ' .
'is not supported by phpseclib.' 'is not supported by phpseclib.'
); );