Merge remote-tracking branch 'upstream/master' into rsa-plugins

Conflicts:
	phpseclib/Crypt/RSA.php
	phpseclib/Net/SSH2.php
This commit is contained in:
terrafrost 2015-10-01 15:57:31 -05:00
commit 91887c957d
30 changed files with 755 additions and 1230 deletions

View File

@ -1,6 +1,6 @@
# phpseclib - PHP Secure Communications Library # phpseclib - PHP Secure Communications Library
[![Build Status](https://secure.travis-ci.org/phpseclib/phpseclib.png?branch=2.0)](http://travis-ci.org/phpseclib/phpseclib) [![Build Status](https://secure.travis-ci.org/phpseclib/phpseclib.png?branch=master)](http://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,
@ -8,7 +8,7 @@ AES, Blowfish, Twofish, SSH-1, SSH-2, SFTP, and X.509
* [Download (1.0.0)](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.0.zip/download) * [Download (1.0.0)](http://sourceforge.net/projects/phpseclib/files/phpseclib1.0.0.zip/download)
* [Browse Git](https://github.com/phpseclib/phpseclib) * [Browse Git](https://github.com/phpseclib/phpseclib)
* [Code Coverage Report](http://phpseclib.bantux.org/code_coverage/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"> <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) PEAR Channel: [phpseclib.sourceforge.net](http://phpseclib.sourceforge.net/pear.htm)
@ -16,7 +16,7 @@ PEAR Channel: [phpseclib.sourceforge.net](http://phpseclib.sourceforge.net/pear.
## Documentation ## Documentation
* [Documentation / Manual](http://phpseclib.sourceforge.net/) * [Documentation / Manual](http://phpseclib.sourceforge.net/)
* [API Documentation](http://phpseclib.bantux.org/api/2.0/) (generated by Sami) * [API Documentation](http://phpseclib.bantux.org/api/master/) (generated by Sami)
## Support ## Support

View File

@ -605,6 +605,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
@ -639,8 +640,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) {
@ -1827,6 +1827,7 @@ abstract class Base
* *
* @see \phpseclib\Crypt\Base::_unpad() * @see \phpseclib\Crypt\Base::_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
*/ */
@ -1838,8 +1839,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;
} }
} }
@ -1856,6 +1856,7 @@ abstract class Base
* *
* @see \phpseclib\Crypt\Base::_pad() * @see \phpseclib\Crypt\Base::_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
*/ */
@ -1868,7 +1869,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);

View File

@ -1,26 +1,20 @@
<?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 \phpseclib\Crypt\Hash::setKey() setKey()} is called,
* * {@link \phpseclib\Crypt\Hash::hash() hash()} will return the HMAC as opposed
* md2, md5, md5-96, sha1, sha1-96, sha256, sha256-96, sha384, and sha512, sha512-96 * to the hash.
*
* If {@link \phpseclib\Crypt\Hash::setKey() setKey()} is called, {@link \phpseclib\Crypt\Hash::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,42 +25,25 @@
* @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
*/ */
namespace phpseclib\Crypt; namespace phpseclib\Crypt;
use phpseclib\Math\BigInteger; use phpseclib\Exception\UnsupportedAlgorithmException;
/** /**
* Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
*
* @package Hash * @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 \phpseclib\Crypt\Hash::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
@ -112,46 +80,14 @@ class Hash
*/ */
var $key = false; var $key = false;
/**
* Outer XOR (Internal HMAC)
*
* @see \phpseclib\Crypt\Hash::setKey()
* @var string
* @access private
*/
var $opad;
/**
* Inner XOR (Internal HMAC)
*
* @see \phpseclib\Crypt\Hash::setKey()
* @var string
* @access private
*/
var $ipad;
/** /**
* 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);
} }
@ -196,96 +132,47 @@ class Hash
case 'sha256-96': case 'sha256-96':
case 'sha512-96': case 'sha512-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': case 'sha256':
$this->l = 32; $this->length = 32;
break; break;
case 'sha384': case 'sha384':
$this->l = 48; $this->length = 48;
break; break;
case 'sha512': case 'sha512':
$this->l = 64; $this->length = 64;
}
switch ($hash) {
case 'md2':
$mode = CRYPT_HASH_MODE == self::MODE_HASH && in_array('md2', hash_algos()) ?
self::MODE_HASH : self::MODE_INTERNAL;
break;
case 'sha384':
case 'sha512':
$mode = CRYPT_HASH_MODE == self::MODE_MHASH ? self::MODE_INTERNAL : CRYPT_HASH_MODE;
break; break;
default: default:
$mode = 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 ($mode) {
case self::MODE_MHASH:
switch ($hash) {
case 'md5':
$this->hash = MHASH_MD5;
break;
case 'sha256':
$this->hash = MHASH_SHA256;
break;
case 'sha1':
default:
$this->hash = MHASH_SHA1;
}
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';
}
return;
}
switch ($hash) {
case 'md2':
$this->b = 16;
$this->hash = array($this, '_md2');
break;
case 'md5':
$this->b = 64;
$this->hash = array($this, '_md5');
break;
case 'sha256':
$this->b = 64;
$this->hash = array($this, '_sha256');
break;
case 'sha384':
case 'sha512':
$this->b = 128;
$this->hash = array($this, '_sha512');
break;
case 'sha1':
default:
$this->b = 64;
$this->hash = array($this, '_sha1');
}
$this->ipad = str_repeat(chr(0x36), $this->b);
$this->opad = str_repeat(chr(0x5C), $this->b);
} }
/** /**
@ -297,45 +184,13 @@ class Hash
*/ */
function hash($text) function hash($text)
{ {
$mode = is_array($this->hash) ? self::MODE_INTERNAL : CRYPT_HASH_MODE; $output = !empty($this->key) || is_string($this->key) ?
hash_hmac($this->hash, $text, $this->key, true) :
hash($this->hash, $text, true);
if (!empty($this->key) || is_string($this->key)) { return strlen($output) > $this->length
switch ($mode) { ? substr($output, 0, $this->length)
case self::MODE_MHASH: : $output;
$output = mhash($this->hash, $text, $this->key);
break;
case self::MODE_HASH:
$output = hash_hmac($this->hash, $text, $this->key, true);
break;
case self::MODE_INTERNAL:
/* "Applications that use keys longer than B bytes will first hash the key using H and then use the
resultant L byte string as the actual key to HMAC."
-- http://tools.ietf.org/html/rfc2104#section-2 */
$key = strlen($this->key) > $this->b ? call_user_func($this->hash, $this->key) : $this->key;
$key = str_pad($key, $this->b, chr(0)); // step 1
$temp = $this->ipad ^ $key; // step 2
$temp .= $text; // step 3
$temp = call_user_func($this->hash, $temp); // step 4
$output = $this->opad ^ $key; // step 5
$output.= $temp; // step 6
$output = call_user_func($this->hash, $output); // step 7
}
} else {
switch ($mode) {
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);
}
}
return substr($output, 0, $this->l);
} }
/** /**
@ -346,480 +201,6 @@ class Hash
*/ */
function getLength() function getLength()
{ {
return $this->l; return $this->length;
}
/**
* Wrapper for MD5
*
* @access private
* @param string $m
*/
function _md5($m)
{
return pack('H*', md5($m));
}
/**
* Wrapper for SHA1
*
* @access private
* @param string $m
*/
function _sha1($m)
{
return pack('H*', sha1($m));
}
/**
* Pure-PHP implementation of MD2
*
* See {@link http://tools.ietf.org/html/rfc1319 RFC1319}.
*
* @access private
* @param string $m
*/
function _md2($m)
{
static $s = array(
41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188,
76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24,
138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251,
245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63,
148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50,
39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165,
181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210,
150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157,
112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27,
96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15,
85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197,
234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65,
129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123,
8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233,
203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228,
166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237,
31, 26, 219, 153, 141, 51, 159, 17, 131, 20
);
// Step 1. Append Padding Bytes
$pad = 16 - (strlen($m) & 0xF);
$m.= str_repeat(chr($pad), $pad);
$length = strlen($m);
// Step 2. Append Checksum
$c = str_repeat(chr(0), 16);
$l = chr(0);
for ($i = 0; $i < $length; $i+= 16) {
for ($j = 0; $j < 16; $j++) {
// RFC1319 incorrectly states that C[j] should be set to S[c xor L]
//$c[$j] = chr($s[ord($m[$i + $j] ^ $l)]);
// per <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)) {
// Initialize variables
$init384 = array( // initial values for SHA384
'cbbb9d5dc1059ed8', '629a292a367cd507', '9159015a3070dd17', '152fecd8f70e5939',
'67332667ffc00b31', '8eb44a8768581511', 'db0c2e0d64f98fa7', '47b5481dbefa4fa4'
);
$init512 = array( // initial values for SHA512
'6a09e667f3bcc908', 'bb67ae8584caa73b', '3c6ef372fe94f82b', 'a54ff53a5f1d36f1',
'510e527fade682d1', '9b05688c2b3e6c1f', '1f83d9abfb41bd6b', '5be0cd19137e2179'
);
for ($i = 0; $i < 8; $i++) {
$init384[$i] = new BigInteger($init384[$i], 16);
$init384[$i]->setPrecision(64);
$init512[$i] = new BigInteger($init512[$i], 16);
$init512[$i]->setPrecision(64);
}
// Initialize table of round constants
// (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409)
$k = array(
'428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc',
'3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118',
'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2',
'72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694',
'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65',
'2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5',
'983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4',
'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70',
'27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df',
'650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b',
'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30',
'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8',
'19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8',
'391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3',
'748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec',
'90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b',
'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178',
'06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b',
'28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c',
'4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817'
);
for ($i = 0; $i < 80; $i++) {
$k[$i] = new BigInteger($k[$i], 16);
}
}
$hash = $this->l == 48 ? $init384 : $init512;
// Pre-processing
$length = strlen($m);
// to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128
$m.= str_repeat(chr(0), 128 - (($length + 16) & 0x7F));
$m[$length] = chr(0x80);
// we don't support hashing strings 512MB long
$m.= pack('N4', 0, 0, 0, $length << 3);
// Process the message in successive 1024-bit chunks
$chunks = str_split($m, 128);
foreach ($chunks as $chunk) {
$w = array();
for ($i = 0; $i < 16; $i++) {
$temp = new BigInteger($this->_string_shift($chunk, 8), 256);
$temp->setPrecision(64);
$w[] = $temp;
}
// Extend the sixteen 32-bit words into eighty 32-bit words
for ($i = 16; $i < 80; $i++) {
$temp = array(
$w[$i - 15]->bitwise_rightRotate(1),
$w[$i - 15]->bitwise_rightRotate(8),
$w[$i - 15]->bitwise_rightShift(7)
);
$s0 = $temp[0]->bitwise_xor($temp[1]);
$s0 = $s0->bitwise_xor($temp[2]);
$temp = array(
$w[$i - 2]->bitwise_rightRotate(19),
$w[$i - 2]->bitwise_rightRotate(61),
$w[$i - 2]->bitwise_rightShift(6)
);
$s1 = $temp[0]->bitwise_xor($temp[1]);
$s1 = $s1->bitwise_xor($temp[2]);
$w[$i] = $w[$i - 16]->copy();
$w[$i] = $w[$i]->add($s0);
$w[$i] = $w[$i]->add($w[$i - 7]);
$w[$i] = $w[$i]->add($s1);
}
// Initialize hash value for this chunk
$a = $hash[0]->copy();
$b = $hash[1]->copy();
$c = $hash[2]->copy();
$d = $hash[3]->copy();
$e = $hash[4]->copy();
$f = $hash[5]->copy();
$g = $hash[6]->copy();
$h = $hash[7]->copy();
// Main loop
for ($i = 0; $i < 80; $i++) {
$temp = array(
$a->bitwise_rightRotate(28),
$a->bitwise_rightRotate(34),
$a->bitwise_rightRotate(39)
);
$s0 = $temp[0]->bitwise_xor($temp[1]);
$s0 = $s0->bitwise_xor($temp[2]);
$temp = array(
$a->bitwise_and($b),
$a->bitwise_and($c),
$b->bitwise_and($c)
);
$maj = $temp[0]->bitwise_xor($temp[1]);
$maj = $maj->bitwise_xor($temp[2]);
$t2 = $s0->add($maj);
$temp = array(
$e->bitwise_rightRotate(14),
$e->bitwise_rightRotate(18),
$e->bitwise_rightRotate(41)
);
$s1 = $temp[0]->bitwise_xor($temp[1]);
$s1 = $s1->bitwise_xor($temp[2]);
$temp = array(
$e->bitwise_and($f),
$g->bitwise_and($e->bitwise_not())
);
$ch = $temp[0]->bitwise_xor($temp[1]);
$t1 = $h->add($s1);
$t1 = $t1->add($ch);
$t1 = $t1->add($k[$i]);
$t1 = $t1->add($w[$i]);
$h = $g->copy();
$g = $f->copy();
$f = $e->copy();
$e = $d->add($t1);
$d = $c->copy();
$c = $b->copy();
$b = $a->copy();
$a = $t1->add($t2);
}
// Add this chunk's hash to result so far
$hash = array(
$hash[0]->add($a),
$hash[1]->add($b),
$hash[2]->add($c),
$hash[3]->add($d),
$hash[4]->add($e),
$hash[5]->add($f),
$hash[6]->add($g),
$hash[7]->add($h)
);
}
// Produce the final hash value (big-endian)
// (\phpseclib\Crypt\Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here)
$temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() .
$hash[4]->toBytes() . $hash[5]->toBytes();
if ($this->l != 48) {
$temp.= $hash[6]->toBytes() . $hash[7]->toBytes();
}
return $temp;
}
/**
* Right Rotate
*
* @access private
* @param int $int
* @param int $amt
* @see _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 _sha256()
* @return int
*/
function _rightShift($int, $amt)
{
$mask = (1 << (32 - $amt)) - 1;
return ($int >> $amt) & $mask;
}
/**
* Not
*
* @access private
* @param int $int
* @see _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 _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;
}
return fmod($result, $mod);
}
/**
* 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

@ -29,11 +29,9 @@
* *
* $plaintext = 'terrafrost'; * $plaintext = 'terrafrost';
* *
* $rsa->loadKey($privatekey); * $signature = $privatekey->sign($plaintext);
* $signature = $rsa->sign($plaintext);
* *
* $rsa->loadKey($publickey); * echo $publickey->verify($plaintext, $signature) ? 'verified' : 'unverified';
* echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
* ?> * ?>
* </code> * </code>
* *
@ -351,6 +349,20 @@ class RSA
} }
} }
/**
* Initialize static variables
*
* @access private
*/
static function _initialize_static_variables()
{
if (!isset(self::$zero)) {
self::$zero= new BigInteger(0);
self::$one = new BigInteger(1);
self::$configFile = __DIR__ . '/../openssl.cnf';
}
}
/** /**
* The constructor * The constructor
* *
@ -664,7 +676,6 @@ class RSA
$this->encryptionMode = $key->encryptionMode; $this->encryptionMode = $key->encryptionMode;
$this->signatureMode = $key->signatureMode; $this->signatureMode = $key->signatureMode;
$this->password = $key->password; $this->password = $key->password;
$this->configFile = $key->configFile;
$this->comment = $key->comment; $this->comment = $key->comment;
if (is_object($key->hash)) { if (is_object($key->hash)) {

View File

@ -49,6 +49,7 @@ 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)
@ -211,8 +212,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);

View File

@ -0,0 +1,26 @@
<?php
/**
* BadConfigurationException
*
* PHP version 5
*
* @category Exception
* @package BadConfigurationException
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Exception;
/**
* BadConfigurationException
*
* @package BadConfigurationException
* @author Jim Wigginton <terrafrost@php.net>
*/
class BadConfigurationException extends \RuntimeException
{
}

View File

@ -0,0 +1,26 @@
<?php
/**
* FileNotFoundException
*
* PHP version 5
*
* @category Exception
* @package FileNotFoundException
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Exception;
/**
* FileNotFoundException
*
* @package FileNotFoundException
* @author Jim Wigginton <terrafrost@php.net>
*/
class FileNotFoundException extends \RuntimeException
{
}

View File

@ -0,0 +1,26 @@
<?php
/**
* NoSupportedAlgorithmsException
*
* PHP version 5
*
* @category Exception
* @package NoSupportedAlgorithmsException
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Exception;
/**
* NoSupportedAlgorithmsException
*
* @package NoSupportedAlgorithmsException
* @author Jim Wigginton <terrafrost@php.net>
*/
class NoSupportedAlgorithmsException extends \RuntimeException
{
}

View File

@ -0,0 +1,26 @@
<?php
/**
* UnsupportedAlgorithmException
*
* PHP version 5
*
* @category Exception
* @package UnsupportedAlgorithmException
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
namespace phpseclib\Exception;
/**
* UnsupportedAlgorithmException
*
* @package UnsupportedAlgorithmException
* @author Jim Wigginton <terrafrost@php.net>
*/
class UnsupportedAlgorithmException extends \RuntimeException
{
}

View File

@ -793,6 +793,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())
@ -985,7 +986,7 @@ class ASN1
case self::TYPE_OBJECT_IDENTIFIER: case self::TYPE_OBJECT_IDENTIFIER:
$oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids); $oid = preg_match('#(?:\d+\.)+#', $source) ? $source : array_search($source, $this->oids);
if ($oid === false) { if ($oid === false) {
user_error('Invalid OID'); throw new \RuntimeException('Invalid OID');
return false; return false;
} }
$value = ''; $value = '';
@ -1038,7 +1039,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);
@ -1062,7 +1063,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;
} }

View File

@ -32,6 +32,7 @@ use phpseclib\Crypt\Random;
use phpseclib\File\ASN1; use phpseclib\File\ASN1;
use phpseclib\File\ASN1\Element; use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger; use phpseclib\Math\BigInteger;
use phpseclib\Exception\UnsupportedAlgorithmException;
/** /**
* Pure-PHP X.509 Parser * Pure-PHP X.509 Parser
@ -1641,7 +1642,7 @@ 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 {
@ -1714,7 +1715,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'];
@ -2107,7 +2108,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
@ -2115,7 +2117,8 @@ 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)
{ {
@ -2139,11 +2142,11 @@ class X509
} }
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;
@ -3628,6 +3631,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)
@ -3646,10 +3650,12 @@ class X509
$this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject)); $this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject));
return $this->currentCert; return $this->currentCert;
default:
throw new UnsupportedAlgorithmException('Signature algorithm unsupported');
} }
} }
return false; throw new UnsupportedAlgorithmException('Unsupported public key algorithm');
} }
/** /**

View File

@ -34,6 +34,7 @@ namespace phpseclib\Net;
use phpseclib\Net\SSH1; use phpseclib\Net\SSH1;
use phpseclib\Net\SSH2; use phpseclib\Net\SSH2;
use phpseclib\Exception\FileNotFoundException;
/** /**
* Pure-PHP implementations of SCP. * Pure-PHP implementations of SCP.
@ -140,6 +141,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
*/ */
@ -168,8 +170,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 +290,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()
@ -314,8 +316,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

@ -38,6 +38,7 @@
namespace phpseclib\Net; namespace phpseclib\Net;
use phpseclib\Net\SSH2; use phpseclib\Net\SSH2;
use phpseclib\Exception\FileNotFoundException;
/** /**
* Pure-PHP implementations of SFTP. * Pure-PHP implementations of SFTP.
@ -383,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
*/ */
@ -470,8 +472,7 @@ 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;
} }
extract(unpack('Nversion', $this->_string_shift($response, 4))); extract(unpack('Nversion', $this->_string_shift($response, 4)));
@ -610,6 +611,7 @@ class SFTP extends SSH2
* *
* @see \phpseclib\Net\SFTP::chdir() * @see \phpseclib\Net\SFTP::chdir()
* @param string $path * @param string $path
* @throws \UnexpectedValueException on receipt of unexpected packets
* @return mixed * @return mixed
* @access private * @access private
*/ */
@ -634,8 +636,7 @@ 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;
} }
} }
@ -666,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
*/ */
@ -710,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)) {
@ -813,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)
@ -844,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());
@ -899,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;
} }
} }
@ -1259,6 +1259,7 @@ class SFTP extends SSH2
* *
* @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
*/ */
@ -1279,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;
} }
/** /**
@ -1306,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
*/ */
@ -1342,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);
@ -1396,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
*/ */
@ -1433,8 +1434,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;
} }
/** /**
@ -1443,6 +1443,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
*/ */
@ -1481,8 +1482,7 @@ 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;
} }
extract(unpack('Nstatus', $this->_string_shift($response, 4))); extract(unpack('Nstatus', $this->_string_shift($response, 4)));
@ -1570,6 +1570,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
*/ */
@ -1593,8 +1594,7 @@ 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;
} }
extract(unpack('Ncount', $this->_string_shift($response, 4))); extract(unpack('Ncount', $this->_string_shift($response, 4)));
@ -1614,6 +1614,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
*/ */
@ -1633,8 +1634,7 @@ 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;
} }
extract(unpack('Nstatus', $this->_string_shift($response, 4))); extract(unpack('Nstatus', $this->_string_shift($response, 4)));
@ -1686,6 +1686,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)
@ -1696,8 +1697,7 @@ 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;
} }
extract(unpack('Nstatus', $this->_string_shift($response, 4))); extract(unpack('Nstatus', $this->_string_shift($response, 4)));
@ -1713,6 +1713,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
*/ */
@ -1733,8 +1734,7 @@ 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;
} }
extract(unpack('Nstatus', $this->_string_shift($response, 4))); extract(unpack('Nstatus', $this->_string_shift($response, 4)));
@ -1794,6 +1794,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().
@ -1841,8 +1844,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
@ -1850,7 +1852,7 @@ 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
@ -1861,8 +1863,7 @@ class SFTP extends SSH2
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) {
@ -1876,10 +1877,7 @@ class SFTP extends SSH2
if ($local_start >= 0) { if ($local_start >= 0) {
fseek($fp, $local_start); fseek($fp, $local_start);
} elseif ($mode & self::RESUME_START) { $size-= $local_start;
// do nothing
} else {
fseek($fp, $offset);
} }
} elseif ($dataCallback) { } elseif ($dataCallback) {
$size = 0; $size = 0;
@ -1950,6 +1948,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)
@ -1957,8 +1956,7 @@ 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;
} }
extract(unpack('Nstatus', $this->_string_shift($response, 4))); extract(unpack('Nstatus', $this->_string_shift($response, 4)));
@ -1976,6 +1974,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)
@ -1988,8 +1987,7 @@ 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;
} }
extract(unpack('Nstatus', $this->_string_shift($response, 4))); extract(unpack('Nstatus', $this->_string_shift($response, 4)));
@ -2014,6 +2012,7 @@ class SFTP extends SSH2
* @param string $local_file * @param string $local_file
* @param int $offset * @param int $offset
* @param int $length * @param int $length
* @throws \UnexpectedValueException on receipt of unexpected packets
* @return mixed * @return mixed
* @access public * @access public
*/ */
@ -2042,8 +2041,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)) {
@ -2091,11 +2089,10 @@ class SFTP extends SSH2
$this->_logError($response); $this->_logError($response);
break 2; break 2;
default: default:
user_error('Expected SSH_FXP_DATA or SSH_FXP_STATUS');
if ($fclose_check) { if ($fclose_check) {
fclose($fp); fclose($fp);
} }
return false; throw new \UnexpectedValueException('Expected SSH_FXP_DATA or SSH_FXP_STATUS');
} }
if ($length > 0 && $length <= $offset - $start) { if ($length > 0 && $length <= $offset - $start) {
@ -2129,6 +2126,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)
@ -2149,8 +2147,7 @@ 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
@ -2482,6 +2479,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)
@ -2504,8 +2502,7 @@ 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

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
@ -166,13 +167,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

@ -537,14 +537,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,20 +556,17 @@ 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);
@ -652,8 +650,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,8 +679,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;
@ -697,6 +693,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 = '')
@ -715,8 +713,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();
@ -728,15 +725,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
@ -756,8 +751,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;
} }
} }
@ -792,20 +786,19 @@ class SSH1
* @see \phpseclib\Net\SSH1::interactiveWrite() * @see \phpseclib\Net\SSH1::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) {
@ -841,6 +834,8 @@ class SSH1
* @see \phpseclib\Net\SSH1::interactiveRead() * @see \phpseclib\Net\SSH1::interactiveRead()
* @see \phpseclib\Net\SSH1::interactiveWrite() * @see \phpseclib\Net\SSH1::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()
@ -851,8 +846,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();
@ -861,15 +855,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;
@ -902,18 +894,17 @@ class SSH1
* @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;
@ -941,25 +932,23 @@ class SSH1
* @see \phpseclib\Net\SSH1::interactiveRead() * @see \phpseclib\Net\SSH1::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;
@ -976,18 +965,17 @@ class SSH1
* *
* @see \phpseclib\Net\SSH1::interactiveRead() * @see \phpseclib\Net\SSH1::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);
@ -1313,7 +1301,7 @@ class SSH1
{ {
/* /*
$rsa = new RSA(); $rsa = new RSA();
$rsa->loadKey($key, RSA::PUBLIC_FORMAT_RAW); $rsa->load($key, RSA::PUBLIC_FORMAT_RAW);
$rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1); $rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1);
return $rsa->encrypt($m); return $rsa->encrypt($m);
*/ */

View File

@ -26,7 +26,7 @@
* *
* $key = new \phpseclib\Crypt\RSA(); * $key = new \phpseclib\Crypt\RSA();
* //$key->setPassword('whatever'); * //$key->setPassword('whatever');
* $key->loadKey(file_get_contents('privatekey')); * $key->load(file_get_contents('privatekey'));
* *
* $ssh = new \phpseclib\Net\SSH2('www.domain.tld'); * $ssh = new \phpseclib\Net\SSH2('www.domain.tld');
* if (!$ssh->login('username', $key)) { * if (!$ssh->login('username', $key)) {
@ -60,6 +60,7 @@ use phpseclib\Crypt\TripleDES;
use phpseclib\Crypt\Twofish; use phpseclib\Crypt\Twofish;
use phpseclib\Math\BigInteger; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification. use phpseclib\Math\BigInteger; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
use phpseclib\System\SSH\Agent; use phpseclib\System\SSH\Agent;
use phpseclib\Exception\NoSupportedAlgorithmsException;
/** /**
* Pure-PHP implementation of SSHv2. * Pure-PHP implementation of SSHv2.
@ -866,6 +867,14 @@ class SSH2
*/ */
var $agent; var $agent;
/**
* Connection storage to replicates ssh2 extension functionality:
* {@link http://php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-examples}
*
* @var SSH2[]
*/
static $connections;
/** /**
* Default Constructor. * Default Constructor.
* *
@ -959,6 +968,8 @@ class SSH2
31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY') 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY')
); );
self::$connections[$this->getResourceId()] = $this;
if (is_resource($host)) { if (is_resource($host)) {
$this->fsock = $host; $this->fsock = $host;
return; return;
@ -989,6 +1000,8 @@ class SSH2
* Connect to an SSHv2 server * Connect to an SSHv2 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()
@ -1008,8 +1021,7 @@ class SSH2
$this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout); $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout);
if (!$this->fsock) { if (!$this->fsock) {
$host = $this->host . ':' . $this->port; $host = $this->host . ':' . $this->port;
user_error(rtrim("Cannot connect to $host. Error $errno. $errstr")); throw new \RuntimeException(rtrim("Cannot connect to $host. Error $errno. $errstr"));
return false;
} }
$elapsed = microtime(true) - $start; $elapsed = microtime(true) - $start;
@ -1060,8 +1072,7 @@ class SSH2
} }
if (feof($this->fsock)) { if (feof($this->fsock)) {
user_error('Connection closed by server'); throw new \RuntimeException('Connection closed by server');
return false;
} }
$this->identifier = $this->_generate_identifier(); $this->identifier = $this->_generate_identifier();
@ -1077,21 +1088,18 @@ class SSH2
} }
if ($matches[1] != '1.99' && $matches[1] != '2.0') { if ($matches[1] != '1.99' && $matches[1] != '2.0') {
user_error("Cannot connect to SSH $matches[1] servers"); throw new \RuntimeException("Cannot connect to SSH $matches[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 === false) { if ($response === false) {
user_error('Connection closed by server'); throw new \RuntimeException('Connection closed by server');
return false;
} }
if (ord($response[0]) != NET_SSH2_MSG_KEXINIT) { if (ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
user_error('Expected SSH_MSG_KEXINIT'); throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT');
return false;
} }
if (!$this->_key_exchange($response)) { if (!$this->_key_exchange($response)) {
@ -1143,6 +1151,9 @@ class SSH2
* Key Exchange * Key Exchange
* *
* @param string $kexinit_payload_server * @param string $kexinit_payload_server
* @throws \UnexpectedValueException on receipt of unexpected packets
* @throws \RuntimeException on other errors
* @throws \phpseclib\Exception\NoSupportedAlgorithmsException when none of the algorithms phpseclib has loaded are compatible
* @access private * @access private
*/ */
function _key_exchange($kexinit_payload_server) function _key_exchange($kexinit_payload_server)
@ -1354,27 +1365,28 @@ class SSH2
// here ends the second place. // here ends the second place.
// we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
// we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
// diffie-hellman key exchange as fast as possible // diffie-hellman key exchange as fast as possible
$decrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_server_to_client); $decrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_server_to_client);
$decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt); $decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt);
if ($decryptKeyLength === null) { if ($decryptKeyLength === null) {
user_error('No compatible server to client encryption algorithms found'); $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new NoSupportedAlgorithmsException('No compatible server to client encryption algorithms found');
} }
$encrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_client_to_server); $encrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_client_to_server);
$encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt); $encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt);
if ($encryptKeyLength === null) { if ($encryptKeyLength === null) {
user_error('No compatible client to server encryption algorithms found'); $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new NoSupportedAlgorithmsException('No compatible client to server encryption algorithms found');
} }
// through diffie-hellman key exchange a symmetric key is obtained // through diffie-hellman key exchange a symmetric key is obtained
$kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms); $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms);
if ($kex_algorithm === false) { if ($kex_algorithm === false) {
user_error('No compatible key exchange algorithms found'); $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new NoSupportedAlgorithmsException('No compatible key exchange algorithms found');
} }
// Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty. // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty.
@ -1491,20 +1503,17 @@ class SSH2
$data = pack('CNa*', $clientKexInitMessage, strlen($eBytes), $eBytes); $data = pack('CNa*', $clientKexInitMessage, strlen($eBytes), $eBytes);
if (!$this->_send_binary_packet($data)) { if (!$this->_send_binary_packet($data)) {
user_error('Connection closed by server'); throw new \RuntimeException('Connection closed by server');
return false;
} }
$response = $this->_get_binary_packet(); $response = $this->_get_binary_packet();
if ($response === false) { if ($response === false) {
user_error('Connection closed by server'); throw new \RuntimeException('Connection closed by server');
return false;
} }
extract(unpack('Ctype', $this->_string_shift($response, 1))); extract(unpack('Ctype', $this->_string_shift($response, 1)));
if ($type != $serverKexReplyMessage) { if ($type != $serverKexReplyMessage) {
user_error('Expected SSH_MSG_KEXDH_REPLY'); throw new \UnexpectedValueException('Expected SSH_MSG_KEXDH_REPLY');
return false;
} }
$temp = unpack('Nlength', $this->_string_shift($response, 4)); $temp = unpack('Nlength', $this->_string_shift($response, 4));
@ -1564,13 +1573,13 @@ class SSH2
$server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms); $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms);
if ($server_host_key_algorithm === false) { if ($server_host_key_algorithm === false) {
user_error('No compatible server host key algorithms found'); $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new NoSupportedAlgorithmsException('No compatible server host key algorithms found');
} }
if ($public_key_format != $server_host_key_algorithm || $this->signature_format != $server_host_key_algorithm) { if ($public_key_format != $server_host_key_algorithm || $this->signature_format != $server_host_key_algorithm) {
user_error('Server Host Key Algorithm Mismatch'); $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new \RuntimeException('Server Host Key Algorithm Mismatch');
} }
$packet = pack( $packet = pack(
@ -1585,15 +1594,13 @@ class SSH2
$response = $this->_get_binary_packet(); $response = $this->_get_binary_packet();
if ($response === false) { if ($response === false) {
user_error('Connection closed by server'); throw new \RuntimeException('Connection closed by server');
return false;
} }
extract(unpack('Ctype', $this->_string_shift($response, 1))); extract(unpack('Ctype', $this->_string_shift($response, 1)));
if ($type != NET_SSH2_MSG_NEWKEYS) { if ($type != NET_SSH2_MSG_NEWKEYS) {
user_error('Expected SSH_MSG_NEWKEYS'); throw new \UnexpectedValueException('Expected SSH_MSG_NEWKEYS');
return false;
} }
$keyBytes = pack('Na*', strlen($keyBytes), $keyBytes); $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
@ -1662,8 +1669,8 @@ class SSH2
$mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_client_to_server); $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_client_to_server);
if ($mac_algorithm === false) { if ($mac_algorithm === false) {
user_error('No compatible client to server message authentication algorithms found'); $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new NoSupportedAlgorithmsException('No compatible client to server message authentication algorithms found');
} }
$createKeyLength = 0; // ie. $mac_algorithm == 'none' $createKeyLength = 0; // ie. $mac_algorithm == 'none'
@ -1691,8 +1698,8 @@ class SSH2
$mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_server_to_client); $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_server_to_client);
if ($mac_algorithm === false) { if ($mac_algorithm === false) {
user_error('No compatible server to client message authentication algorithms found'); $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new NoSupportedAlgorithmsException('No compatible server to client message authentication algorithms found');
} }
$checkKeyLength = 0; $checkKeyLength = 0;
@ -1738,15 +1745,15 @@ class SSH2
$compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_server_to_client); $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_server_to_client);
if ($compression_algorithm === false) { if ($compression_algorithm === false) {
user_error('No compatible server to client compression algorithms found'); $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new NoSupportedAlgorithmsException('No compatible server to client compression algorithms found');
} }
$this->decompress = $compression_algorithm == 'zlib'; $this->decompress = $compression_algorithm == 'zlib';
$compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_client_to_server); $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_client_to_server);
if ($compression_algorithm === false) { if ($compression_algorithm === false) {
user_error('No compatible client to server compression algorithms found'); $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new NoSupportedAlgorithmsException('No compatible client to server compression algorithms found');
} }
$this->compress = $compression_algorithm == 'zlib'; $this->compress = $compression_algorithm == 'zlib';
@ -1891,6 +1898,8 @@ class SSH2
* @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 private * @access private
* @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
* by sending dummy SSH_MSG_IGNORE messages. * by sending dummy SSH_MSG_IGNORE messages.
@ -1915,15 +1924,13 @@ class SSH2
$response = $this->_get_binary_packet(); $response = $this->_get_binary_packet();
if ($response === false) { if ($response === false) {
user_error('Connection closed by server'); throw new \RuntimeException('Connection closed by server');
return false;
} }
extract(unpack('Ctype', $this->_string_shift($response, 1))); extract(unpack('Ctype', $this->_string_shift($response, 1)));
if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) { if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
user_error('Expected SSH_MSG_SERVICE_ACCEPT'); throw new \UnexpectedValueException('Expected SSH_MSG_SERVICE_ACCEPT');
return false;
} }
$this->bitmap |= self::MASK_LOGIN_REQ; $this->bitmap |= self::MASK_LOGIN_REQ;
} }
@ -1964,8 +1971,7 @@ class SSH2
$response = $this->_get_binary_packet(); $response = $this->_get_binary_packet();
if ($response === false) { if ($response === false) {
user_error('Connection closed by server'); throw new \RuntimeException('Connection closed by server');
return false;
} }
extract(unpack('Ctype', $this->_string_shift($response, 1))); extract(unpack('Ctype', $this->_string_shift($response, 1)));
@ -2019,8 +2025,7 @@ class SSH2
$response = $this->_get_binary_packet(); $response = $this->_get_binary_packet();
if ($response === false) { if ($response === false) {
user_error('Connection closed by server'); throw new \RuntimeException('Connection closed by server');
return false;
} }
extract(unpack('Ctype', $this->_string_shift($response, 1))); extract(unpack('Ctype', $this->_string_shift($response, 1)));
@ -2096,6 +2101,7 @@ class SSH2
* *
* @param string $responses... * @param string $responses...
* @return bool * @return bool
* @throws \RuntimeException on connection error
* @access private * @access private
*/ */
function _keyboard_interactive_process() function _keyboard_interactive_process()
@ -2107,8 +2113,7 @@ class SSH2
} else { } else {
$orig = $response = $this->_get_binary_packet(); $orig = $response = $this->_get_binary_packet();
if ($response === false) { if ($response === false) {
user_error('Connection closed by server'); throw new \RuntimeException('Connection closed by server');
return false;
} }
} }
@ -2232,6 +2237,7 @@ class SSH2
* @param string $username * @param string $username
* @param \phpseclib\Crypt\RSA $password * @param \phpseclib\Crypt\RSA $password
* @return bool * @return bool
* @throws \RuntimeException on connection error
* @access private * @access private
* @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
* by sending dummy SSH_MSG_IGNORE messages. * by sending dummy SSH_MSG_IGNORE messages.
@ -2277,8 +2283,7 @@ class SSH2
$response = $this->_get_binary_packet(); $response = $this->_get_binary_packet();
if ($response === false) { if ($response === false) {
user_error('Connection closed by server'); throw new \RuntimeException('Connection closed by server');
return false;
} }
extract(unpack('Ctype', $this->_string_shift($response, 1))); extract(unpack('Ctype', $this->_string_shift($response, 1)));
@ -2312,8 +2317,7 @@ class SSH2
$response = $this->_get_binary_packet(); $response = $this->_get_binary_packet();
if ($response === false) { if ($response === false) {
user_error('Connection closed by server'); throw new \RuntimeException('Connection closed by server');
return false;
} }
extract(unpack('Ctype', $this->_string_shift($response, 1))); extract(unpack('Ctype', $this->_string_shift($response, 1)));
@ -2363,6 +2367,7 @@ class SSH2
* @param string $command * @param string $command
* @param Callback $callback * @param Callback $callback
* @return string * @return string
* @throws \RuntimeException on connection error
* @access public * @access public
*/ */
function exec($command, $callback = null) function exec($command, $callback = null)
@ -2430,8 +2435,7 @@ class SSH2
$response = $this->_get_binary_packet(); $response = $this->_get_binary_packet();
if ($response === false) { if ($response === false) {
user_error('Connection closed by server'); throw new \RuntimeException('Connection closed by server');
return false;
} }
list(, $type) = unpack('C', $this->_string_shift($response, 1)); list(, $type) = unpack('C', $this->_string_shift($response, 1));
@ -2441,8 +2445,8 @@ class SSH2
break; break;
case NET_SSH2_MSG_CHANNEL_FAILURE: case NET_SSH2_MSG_CHANNEL_FAILURE:
default: default:
user_error('Unable to request pseudo-terminal'); $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); throw new \RuntimeException('Unable to request pseudo-terminal');
} }
$this->in_request_pty_exec = true; $this->in_request_pty_exec = true;
} }
@ -2510,6 +2514,8 @@ class SSH2
* @see \phpseclib\Net\SSH2::read() * @see \phpseclib\Net\SSH2::read()
* @see \phpseclib\Net\SSH2::write() * @see \phpseclib\Net\SSH2::write()
* @return bool * @return bool
* @throws \UnexpectedValueException on receipt of unexpected packets
* @throws \RuntimeException on other errors
* @access private * @access private
*/ */
function _initShell() function _initShell()
@ -2566,8 +2572,7 @@ class SSH2
$response = $this->_get_binary_packet(); $response = $this->_get_binary_packet();
if ($response === false) { if ($response === false) {
user_error('Connection closed by server'); throw new \RuntimeException('Connection closed by server');
return false;
} }
list(, $type) = unpack('C', $this->_string_shift($response, 1)); list(, $type) = unpack('C', $this->_string_shift($response, 1));
@ -2578,8 +2583,8 @@ class SSH2
case NET_SSH2_MSG_CHANNEL_FAILURE: case NET_SSH2_MSG_CHANNEL_FAILURE:
break; break;
default: default:
user_error('Unable to request pseudo-terminal'); $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); throw new \UnexpectedValueException('Unable to request pseudo-terminal');
} }
$packet = pack( $packet = pack(
@ -2656,6 +2661,7 @@ class SSH2
* @param string $expect * @param string $expect
* @param int $mode * @param int $mode
* @return string * @return string
* @throws \RuntimeException on connection error
* @access public * @access public
*/ */
function read($expect = '', $mode = self::READ_SIMPLE) function read($expect = '', $mode = self::READ_SIMPLE)
@ -2664,13 +2670,11 @@ class SSH2
$this->is_timeout = false; $this->is_timeout = false;
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;
} }
$channel = $this->_get_interactive_channel(); $channel = $this->_get_interactive_channel();
@ -2701,18 +2705,17 @@ class SSH2
* @see \phpseclib\Net\SSH2::read() * @see \phpseclib\Net\SSH2::read()
* @param string $cmd * @param string $cmd
* @return bool * @return bool
* @throws \RuntimeException on connection error
* @access public * @access public
*/ */
function write($cmd) function write($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;
} }
return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd); return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd);
@ -2836,6 +2839,7 @@ class SSH2
if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) { if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) {
fclose($this->realtime_log_file); fclose($this->realtime_log_file);
} }
unset(self::$connections[$this->getResourceId()]);
} }
/** /**
@ -2869,14 +2873,14 @@ class SSH2
* *
* @see \phpseclib\Net\SSH2::_send_binary_packet() * @see \phpseclib\Net\SSH2::_send_binary_packet()
* @return string * @return string
* @throws \RuntimeException on connection errors
* @access private * @access private
*/ */
function _get_binary_packet() function _get_binary_packet()
{ {
if (!is_resource($this->fsock) || feof($this->fsock)) { if (!is_resource($this->fsock) || feof($this->fsock)) {
user_error('Connection closed prematurely');
$this->bitmap = 0; $this->bitmap = 0;
return false; throw new \RuntimeException('Connection closed prematurely');
} }
$start = microtime(true); $start = microtime(true);
@ -2890,8 +2894,7 @@ class SSH2
$raw = $this->decrypt->decrypt($raw); $raw = $this->decrypt->decrypt($raw);
} }
if ($raw === false) { if ($raw === false) {
user_error('Unable to decrypt content'); throw new \RuntimeException('Unable to decrypt content');
return false;
} }
extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5))); extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)));
@ -2902,17 +2905,15 @@ class SSH2
// "implementations SHOULD check that the packet length is reasonable" // "implementations SHOULD check that the packet length is reasonable"
// PuTTY uses 0x9000 as the actual max packet size and so to shall we // PuTTY uses 0x9000 as the actual max packet size and so to shall we
if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) { if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) {
user_error('Invalid size'); throw new \RuntimeException('Invalid size');
return false;
} }
$buffer = ''; $buffer = '';
while ($remaining_length > 0) { while ($remaining_length > 0) {
$temp = fread($this->fsock, $remaining_length); $temp = fread($this->fsock, $remaining_length);
if ($temp === false || feof($this->fsock)) { if ($temp === false || feof($this->fsock)) {
user_error('Error reading from socket');
$this->bitmap = 0; $this->bitmap = 0;
return false; throw new \RuntimeException('Error reading from socket');
} }
$buffer.= $temp; $buffer.= $temp;
$remaining_length-= strlen($temp); $remaining_length-= strlen($temp);
@ -2928,12 +2929,10 @@ class SSH2
if ($this->hmac_check !== false) { if ($this->hmac_check !== false) {
$hmac = fread($this->fsock, $this->hmac_size); $hmac = fread($this->fsock, $this->hmac_size);
if ($hmac === false || strlen($hmac) != $this->hmac_size) { if ($hmac === false || strlen($hmac) != $this->hmac_size) {
user_error('Error reading socket');
$this->bitmap = 0; $this->bitmap = 0;
return false; throw new \RuntimeException('Error reading socket');
} elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) { } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
user_error('Invalid HMAC'); throw new \RuntimeException('Invalid HMAC');
return false;
} }
} }
@ -3161,6 +3160,7 @@ class SSH2
* *
* @param $client_channel * @param $client_channel
* @return mixed * @return mixed
* @throws \RuntimeException on connection error
* @access private * @access private
*/ */
function _get_channel_packet($client_channel, $skip_extended = false) function _get_channel_packet($client_channel, $skip_extended = false)
@ -3193,8 +3193,7 @@ class SSH2
$response = $this->_get_binary_packet(); $response = $this->_get_binary_packet();
if ($response === false) { if ($response === false) {
user_error('Connection closed by server'); throw new \RuntimeException('Connection closed by server');
return false;
} }
if ($client_channel == -1 && $response === true) { if ($client_channel == -1 && $response === true) {
return true; return true;
@ -3243,8 +3242,8 @@ class SSH2
return $result; return $result;
//case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
default: default:
user_error('Unable to open channel'); $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); throw new \RuntimeException('Unable to open channel');
} }
break; break;
case NET_SSH2_MSG_CHANNEL_REQUEST: case NET_SSH2_MSG_CHANNEL_REQUEST:
@ -3254,8 +3253,8 @@ class SSH2
case NET_SSH2_MSG_CHANNEL_FAILURE: case NET_SSH2_MSG_CHANNEL_FAILURE:
return false; return false;
default: default:
user_error('Unable to fulfill channel request'); $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); throw new \RuntimeException('Unable to fulfill channel request');
} }
case NET_SSH2_MSG_CHANNEL_CLOSE: case NET_SSH2_MSG_CHANNEL_CLOSE:
return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended); return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended);
@ -3364,8 +3363,8 @@ class SSH2
case NET_SSH2_MSG_CHANNEL_EOF: case NET_SSH2_MSG_CHANNEL_EOF:
break; break;
default: default:
user_error('Error reading channel data'); $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); throw new \RuntimeException('Error reading channel data');
} }
} }
} }
@ -3384,9 +3383,8 @@ class SSH2
function _send_binary_packet($data, $logged = null) function _send_binary_packet($data, $logged = null)
{ {
if (!is_resource($this->fsock) || feof($this->fsock)) { if (!is_resource($this->fsock) || feof($this->fsock)) {
user_error('Connection closed prematurely');
$this->bitmap = 0; $this->bitmap = 0;
return false; throw new \RuntimeException('Connection closed prematurely');
} }
//if ($this->compress) { //if ($this->compress) {
@ -3945,6 +3943,8 @@ class SSH2
* is recommended. Returns false if the server signature is not signed correctly with the public host key. * is recommended. Returns false if the server signature is not signed correctly with the public host key.
* *
* @return mixed * @return mixed
* @throws \RuntimeException on badly formatted keys
* @throws \phpseclib\Exception\NoSupportedAlgorithmsException when the key isn't in a supported format
* @access public * @access public
*/ */
function getServerPublicHostKey() function getServerPublicHostKey()
@ -3990,8 +3990,8 @@ class SSH2
padding, unsigned, and in network byte order). */ padding, unsigned, and in network byte order). */
$temp = unpack('Nlength', $this->_string_shift($signature, 4)); $temp = unpack('Nlength', $this->_string_shift($signature, 4));
if ($temp['length'] != 40) { if ($temp['length'] != 40) {
user_error('Invalid signature'); $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new \RuntimeException('Invalid signature');
} }
$r = new BigInteger($this->_string_shift($signature, 20), 256); $r = new BigInteger($this->_string_shift($signature, 20), 256);
@ -4002,8 +4002,8 @@ class SSH2
case $r->compare($q) >= 0: case $r->compare($q) >= 0:
case $s->equals($zero): case $s->equals($zero):
case $s->compare($q) >= 0: case $s->compare($q) >= 0:
user_error('Invalid signature'); $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new \RuntimeException('Invalid signature');
} }
$w = $s->modInverse($q); $w = $s->modInverse($q);
@ -4022,7 +4022,7 @@ class SSH2
list(, $v) = $v->divide($q); list(, $v) = $v->divide($q);
if (!$v->equals($r)) { if (!$v->equals($r)) {
user_error('Bad server signature'); //user_error('Bad server signature');
return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
} }
@ -4042,9 +4042,9 @@ class SSH2
$rsa = new RSA(); $rsa = new RSA();
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1); $rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
$rsa->loadKey(array('e' => $e, 'n' => $n), 'Raw'); $rsa->load(array('e' => $e, 'n' => $n), 'Raw');
if (!$rsa->verify($this->exchange_hash, $signature)) { if (!$rsa->verify($this->exchange_hash, $signature)) {
user_error('Bad server signature'); //user_error('Bad server signature');
return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
} }
*/ */
@ -4059,8 +4059,8 @@ class SSH2
// also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source. // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source.
if ($s->compare(new BigInteger()) < 0 || $s->compare($n->subtract(new BigInteger(1))) > 0) { if ($s->compare(new BigInteger()) < 0 || $s->compare($n->subtract(new BigInteger(1))) > 0) {
user_error('Invalid signature'); $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); throw new \RuntimeException('Invalid signature');
} }
$s = $s->modPow($e, $n); $s = $s->modPow($e, $n);
@ -4070,13 +4070,13 @@ class SSH2
$h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 2 - strlen($h)) . $h; $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 2 - strlen($h)) . $h;
if ($s != $h) { if ($s != $h) {
user_error('Bad server signature'); //user_error('Bad server signature');
return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
} }
break; break;
default: default:
user_error('Unsupported signature format'); $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); throw new NoSupportedAlgorithmsException('Unsupported signature format');
} }
return $this->signature_format . ' ' . base64_encode($this->server_public_host_key); return $this->signature_format . ' ' . base64_encode($this->server_public_host_key);
@ -4152,4 +4152,47 @@ class SSH2
$this->windowColumns = $columns; $this->windowColumns = $columns;
$this->windowRows = $rows; $this->windowRows = $rows;
} }
/**
* @return string
*/
function __toString()
{
return $this->getResourceId();
}
/**
* We use {} because that symbols should not be in URL according to
* {@link http://tools.ietf.org/html/rfc3986#section-2 RFC}.
* It will safe us from any conflicts, because otherwise regexp will
* match all alphanumeric domains.
*
* @return string
*/
function getResourceId()
{
return '{' . spl_object_hash($this) . '}';
}
/**
* Return existing connection
*
* @param string $id
*
* @return bool|SSH2 will return false if no such connection
*/
static function getConnectionByResourceId($id)
{
return isset(self::$connections[$id]) ? self::$connections[$id] : false;
}
/**
* Return all excising connections
*
* @return SSH2[]
*/
static function getConnections()
{
return self::$connections;
}
} }

View File

@ -35,6 +35,7 @@ namespace phpseclib\System\SSH;
use phpseclib\Crypt\RSA; use phpseclib\Crypt\RSA;
use phpseclib\System\SSH\Agent\Identity; use phpseclib\System\SSH\Agent\Identity;
use phpseclib\Exception\BadConfigurationException;
/** /**
* Pure-PHP ssh-agent client identity factory * Pure-PHP ssh-agent client identity factory
@ -115,6 +116,8 @@ 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() function __construct()
@ -127,13 +130,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)");
} }
} }
@ -144,6 +146,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()
@ -154,13 +157,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');
} }
$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');
} }
$identities = array(); $identities = array();
@ -271,6 +274,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)
@ -289,7 +293,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

@ -134,6 +134,7 @@ class Identity
* *
* @param string $message * @param string $message
* @return string * @return string
* @throws \RuntimeException on connection errors
* @access public * @access public
*/ */
function sign($message) function sign($message)
@ -142,13 +143,13 @@ class Identity
$packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, 0); $packet = pack('CNa*Na*N', Agent::SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, 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 retreive signature'); throw new \RuntimeException('Unable to retreive signature');
} }
$signature_blob = fread($this->fsock, $length - 1); $signature_blob = fread($this->fsock, $length - 1);

View File

@ -27,6 +27,19 @@ class Functional_Net_SFTPStreamTest extends Functional_Net_SFTPTestCase
$this->assertSame(0, $this->sftp->size('fooo.txt')); $this->assertSame(0, $this->sftp->size('fooo.txt'));
} }
/**
* Tests connection reuse functionality same as ssh2 extension:
* {@link http://php.net/manual/en/wrappers.ssh2.php#refsect1-wrappers.ssh2-examples}
*/
public function testConnectionReuse()
{
$originalConnectionsCount = count(\phpseclib\Net\SSH2::getConnections());
$session = $this->sftp;
$dirs = scandir("sftp://$session/");
$this->assertCount($originalConnectionsCount, \phpseclib\Net\SSH2::getConnections());
$this->assertEquals(array('.', '..'), array_slice($dirs, 0, 2));
}
protected function buildUrl($suffix) protected function buildUrl($suffix)
{ {
return sprintf( return sprintf(

View File

@ -13,6 +13,9 @@ use phpseclib\Net\SFTP;
*/ */
abstract class Functional_Net_SFTPTestCase extends PhpseclibFunctionalTestCase abstract class Functional_Net_SFTPTestCase extends PhpseclibFunctionalTestCase
{ {
/**
* @var SFTP
*/
protected $sftp; protected $sftp;
protected $scratchDir; protected $scratchDir;

View File

@ -651,5 +651,28 @@ class Functional_Net_SFTPUserStoryTest extends PhpseclibFunctionalTestCase
$this->assertSame($stat['type'], NET_SFTP_TYPE_SYMLINK); $this->assertSame($stat['type'], NET_SFTP_TYPE_SYMLINK);
$sftp->enableStatCache(); $sftp->enableStatCache();
return $sftp;
}
/**
* @depends testStatVsLstat
* @group github830
*/
public function testUploadOffsets($sftp)
{
$sftp->put('offset.txt', 'res.txt', SFTP::SOURCE_LOCAL_FILE, 0, 10);
$this->assertSame(
substr(self::$exampleData, 10),
$sftp->get('offset.txt'),
'Failed asserting that portions of a file could be uploaded.'
);
$sftp->put('offset.txt', 'res.txt', SFTP::SOURCE_LOCAL_FILE, self::$exampleDataLength - 100);
$this->assertSame(
substr(self::$exampleData, 10, -90) . self::$exampleData,
$sftp->get('offset.txt'),
'Failed asserting that you could upload into the middle of a file.'
);
} }
} }

View File

@ -28,9 +28,7 @@ abstract class PhpseclibFunctionalTestCase extends PhpseclibTestCase
'Should have gmp or bcmath extension for functional test.' 'Should have gmp or bcmath extension for functional test.'
); );
} }
self::ensureConstant('CRYPT_HASH_MODE', Hash::MODE_HASH);
self::reRequireFile('Math/BigInteger.php'); self::reRequireFile('Math/BigInteger.php');
self::reRequireFile('Crypt/Hash.php');
} }
parent::setUpBeforeClass(); parent::setUpBeforeClass();
} }

View File

@ -1,49 +0,0 @@
<?php
/**
* @author Andreas Fischer <bantu@phpbb.com>
* @copyright 2012 Andreas Fischer
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
use phpseclib\Crypt\Hash;
class Unit_Crypt_Hash_MD5Test extends Unit_Crypt_Hash_TestCase
{
public function getInstance()
{
return new Hash('md5');
}
/**
* @dataProvider hashData()
*/
public function testHash($message, $result)
{
$this->assertHashesTo($this->getInstance(), $message, $result);
}
public static function hashData()
{
return array(
array('', 'd41d8cd98f00b204e9800998ecf8427e'),
array('The quick brown fox jumps over the lazy dog', '9e107d9d372bb6826bd81d3542a419d6'),
array('The quick brown fox jumps over the lazy dog.', 'e4d909c290d0fb1ca068ffaddf22cbd0'),
);
}
/**
* @dataProvider hmacData()
*/
public function testHMAC($key, $message, $result)
{
$this->assertHMACsTo($this->getInstance(), $key, $message, $result);
}
public static function hmacData()
{
return array(
array('', '', '74e6f7298a9c2d168935f58c001bad88'),
array('key', 'The quick brown fox jumps over the lazy dog', '80070713463e7749b90c2dc24911e275'),
);
}
}

View File

@ -1,81 +0,0 @@
<?php
/**
* @author Andreas Fischer <bantu@phpbb.com>
* @copyright 2014 Andreas Fischer
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
use phpseclib\Crypt\Hash;
class Unit_Crypt_Hash_SHA256Test extends Unit_Crypt_Hash_TestCase
{
public function getInstance()
{
return new Hash('sha256');
}
/**
* @dataProvider hashData()
*/
public function testHash($message, $result)
{
$this->assertHashesTo($this->getInstance(), $message, $result);
}
public static function hashData()
{
return array(
array(
'',
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
),
array(
'The quick brown fox jumps over the lazy dog',
'd7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592',
),
array(
'The quick brown fox jumps over the lazy dog.',
'ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c',
),
);
}
/**
* @dataProvider hmacData()
*/
public function testHMAC($key, $message, $result)
{
$this->assertHMACsTo($this->getInstance(), $key, $message, $result);
}
public static function hmacData()
{
return array(
// RFC 4231
// Test Case 1
array(
pack('H*', '0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b'),
pack('H*', '4869205468657265'),
'b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7',
),
// Test Case 2
array(
pack('H*', '4a656665'),
pack('H*', '7768617420646f2079612077616e7420666f72206e6f7468696e673f'),
'5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843',
),
// Test Case 3
array(
pack('H*', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),
pack('H*', 'dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'),
'773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe',
),
// Test Case 4
array(
pack('H*', '0102030405060708090a0b0c0d0e0f10111213141516171819'),
pack('H*', 'cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd'),
'82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b',
),
);
}
}

View File

@ -1,32 +0,0 @@
<?php
/**
* @author Andreas Fischer <bantu@phpbb.com>
* @copyright 2014 Andreas Fischer
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
use phpseclib\Crypt\Hash;
class Unit_Crypt_Hash_SHA256_96Test extends Unit_Crypt_Hash_SHA256Test
{
public function getInstance()
{
return new Hash('sha256-96');
}
/**
* @dataProvider hashData()
*/
public function testHash($message, $longResult)
{
parent::testHash($message, substr($longResult, 0, 24));
}
/**
* @dataProvider hmacData()
*/
public function testHMAC($key, $message, $longResult)
{
parent::testHMAC($key, $message, substr($longResult, 0, 24));
}
}

View File

@ -1,81 +0,0 @@
<?php
/**
* @author Andreas Fischer <bantu@phpbb.com>
* @copyright 2014 Andreas Fischer
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
use phpseclib\Crypt\Hash;
class Unit_Crypt_Hash_SHA512Test extends Unit_Crypt_Hash_TestCase
{
public function getInstance()
{
return new Hash('sha512');
}
/**
* @dataProvider hashData()
*/
public function testHash($message, $result)
{
$this->assertHashesTo($this->getInstance(), $message, $result);
}
public static function hashData()
{
return array(
array(
'',
'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e'
),
array(
'The quick brown fox jumps over the lazy dog',
'07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6',
),
array(
'The quick brown fox jumps over the lazy dog.',
'91ea1245f20d46ae9a037a989f54f1f790f0a47607eeb8a14d12890cea77a1bbc6c7ed9cf205e67b7f2b8fd4c7dfd3a7a8617e45f3c463d481c7e586c39ac1ed',
),
);
}
/**
* @dataProvider hmacData()
*/
public function testHMAC($key, $message, $result)
{
$this->assertHMACsTo($this->getInstance(), $key, $message, $result);
}
public static function hmacData()
{
return array(
// RFC 4231
// Test Case 1
array(
pack('H*', '0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b'),
pack('H*', '4869205468657265'),
'87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854',
),
// Test Case 2
array(
pack('H*', '4a656665'),
pack('H*', '7768617420646f2079612077616e7420666f72206e6f7468696e673f'),
'164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737',
),
// Test Case 3
array(
pack('H*', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),
pack('H*', 'dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'),
'fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb',
),
// Test Case 4
array(
pack('H*', '0102030405060708090a0b0c0d0e0f10111213141516171819'),
pack('H*', 'cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd'),
'b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd',
),
);
}
}

View File

@ -1,32 +0,0 @@
<?php
/**
* @author Andreas Fischer <bantu@phpbb.com>
* @copyright 2014 Andreas Fischer
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
use phpseclib\Crypt\Hash;
class Unit_Crypt_Hash_SHA512_96Test extends Unit_Crypt_Hash_SHA512Test
{
public function getInstance()
{
return new Hash('sha512-96');
}
/**
* @dataProvider hashData()
*/
public function testHash($message, $longResult)
{
parent::testHash($message, substr($longResult, 0, 24));
}
/**
* @dataProvider hmacData()
*/
public function testHMAC($key, $message, $longResult)
{
parent::testHMAC($key, $message, substr($longResult, 0, 24));
}
}

View File

@ -1,52 +0,0 @@
<?php
/**
* @author Andreas Fischer <bantu@phpbb.com>
* @copyright 2012 Andreas Fischer
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
use phpseclib\Crypt\Hash;
abstract class Unit_Crypt_Hash_TestCase extends PhpseclibTestCase
{
public static function setUpBeforeClass()
{
if (!defined('CRYPT_HASH_MODE')) {
define('CRYPT_HASH_MODE', Hash::MODE_INTERNAL);
}
}
public function setUp()
{
if (defined('CRYPT_HASH_MODE') && CRYPT_HASH_MODE !== Hash::MODE_INTERNAL) {
$this->markTestSkipped(
'Skipping test because CRYPT_HASH_MODE is not defined as \phpseclib\Crypt\Hash::MODE_INTERNAL.'
);
}
}
protected function assertHashesTo(Hash $hash, $message, $expected)
{
$this->assertEquals(
strtolower($expected),
bin2hex($hash->hash($message)),
sprintf("Failed asserting that '%s' hashes to '%s'.", $message, $expected)
);
}
protected function assertHMACsTo(Hash $hash, $key, $message, $expected)
{
$hash->setKey($key);
$this->assertEquals(
strtolower($expected),
bin2hex($hash->hash($message)),
sprintf(
"Failed asserting that '%s' HMACs to '%s' with key '%s'.",
$message,
$expected,
$key
)
);
}
}

View File

@ -0,0 +1,265 @@
<?php
/**
* @author Andreas Fischer <bantu@phpbb.com>
* @copyright 2012 Andreas Fischer
* @license http://www.opensource.org/licenses/mit-license.html MIT License
*/
use phpseclib\Crypt\Hash;
class Unit_Crypt_HashTest extends PhpseclibTestCase
{
protected function assertHashesTo($hash, $message, $expected)
{
$hash = new Hash($hash);
$this->assertSame(
strtolower($expected),
bin2hex($hash->hash($message)),
sprintf("Failed asserting that '%s' hashes to '%s'.", $message, $expected)
);
}
protected function assertHMACsTo($hash, $key, $message, $expected)
{
$hash = new Hash($hash);
$hash->setKey($key);
$this->assertSame(
strtolower($expected),
bin2hex($hash->hash($message)),
sprintf(
"Failed asserting that '%s' HMACs to '%s' with key '%s'.",
$message,
$expected,
$key
)
);
}
public static function hashData()
{
return array(
array('md5', '', 'd41d8cd98f00b204e9800998ecf8427e'),
array('md5', 'The quick brown fox jumps over the lazy dog', '9e107d9d372bb6826bd81d3542a419d6'),
array('md5', 'The quick brown fox jumps over the lazy dog.', 'e4d909c290d0fb1ca068ffaddf22cbd0'),
array('sha1', 'The quick brown fox jumps over the lazy dog', '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12'),
array('sha1', 'The quick brown fox jumps over the lazy dog.', '408d94384216f890ff7a0c3528e8bed1e0b01621'),
array(
'sha256',
'',
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
),
array(
'sha256',
'The quick brown fox jumps over the lazy dog',
'd7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592',
),
array(
'sha256',
'The quick brown fox jumps over the lazy dog.',
'ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c',
),
array(
'sha384',
'',
'38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b'
),
array(
'sha384',
'The quick brown fox jumps over the lazy dog',
'ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1',
),
array(
'sha512',
'',
'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e'
),
array(
'sha512',
'The quick brown fox jumps over the lazy dog',
'07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6',
),
array(
'sha512',
'The quick brown fox jumps over the lazy dog.',
'91ea1245f20d46ae9a037a989f54f1f790f0a47607eeb8a14d12890cea77a1bbc6c7ed9cf205e67b7f2b8fd4c7dfd3a7a8617e45f3c463d481c7e586c39ac1ed',
),
array(
'whirlpool',
'The quick brown fox jumps over the lazy dog.',
'87a7ff096082e3ffeb86db10feb91c5af36c2c71bc426fe310ce662e0338223e217def0eab0b02b80eecf875657802bc5965e48f5c0a05467756f0d3f396faba'
),
array(
'whirlpool',
'The quick brown fox jumps over the lazy dog.',
'87a7ff096082e3ffeb86db10feb91c5af36c2c71bc426fe310ce662e0338223e217def0eab0b02b80eecf875657802bc5965e48f5c0a05467756f0d3f396faba'
),
);
}
/**
* @dataProvider hmacData()
*/
public function testHMAC($hash, $key, $message, $result)
{
$this->assertHMACsTo($hash, $key, $message, $result);
}
/**
* @dataProvider hmacData()
*/
public function testHMAC96($hash, $key, $message, $result)
{
$this->assertHMACsTo($hash . '-96', $key, $message, substr($result, 0, 24));
}
public static function hmacData()
{
return array(
array('md5', '', '', '74e6f7298a9c2d168935f58c001bad88'),
array('md5', 'key', 'The quick brown fox jumps over the lazy dog', '80070713463e7749b90c2dc24911e275'),
// RFC 4231
// Test Case 1
array(
'sha256',
pack('H*', '0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b'),
pack('H*', '4869205468657265'),
'b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7',
),
// Test Case 2
array(
'sha256',
pack('H*', '4a656665'),
pack('H*', '7768617420646f2079612077616e7420666f72206e6f7468696e673f'),
'5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843',
),
// Test Case 3
array(
'sha256',
pack('H*', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),
pack('H*', 'dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'),
'773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe',
),
// Test Case 4
array(
'sha256',
pack('H*', '0102030405060708090a0b0c0d0e0f10111213141516171819'),
pack('H*', 'cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd'),
'82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b',
),
// RFC 4231
// Test Case 1
array(
'sha512',
pack('H*', '0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b'),
pack('H*', '4869205468657265'),
'87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854',
),
// Test Case 2
array(
'sha512',
pack('H*', '4a656665'),
pack('H*', '7768617420646f2079612077616e7420666f72206e6f7468696e673f'),
'164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737',
),
// Test Case 3
array(
'sha512',
pack('H*', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),
pack('H*', 'dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'),
'fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb',
),
// Test Case 4
array(
'sha512',
pack('H*', '0102030405060708090a0b0c0d0e0f10111213141516171819'),
pack('H*', 'cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd'),
'b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd',
),
array(
'whirlpool',
'abcd',
'The quick brown fox jumps over the lazy dog',
'e71aabb2588d789292fa6fef00b35cc269ec3ea912b1c1cd7127daf95f004a5df5392ee563d322bac7e19d9eab161932fe9c257d63e0d09eca0d91ab4010125e',
),
);
}
/**
* @dataProvider hashData()
*/
public function testHash($hash, $message, $result)
{
$this->assertHashesTo($hash, $message, $result);
}
/**
* @dataProvider hashData()
*/
public function testHash96($hash, $message, $result)
{
$this->assertHashesTo($hash . '-96', $message, substr($result, 0, 24));
}
public function testConstructorDefault()
{
$hash = new Hash();
$this->assertSame($hash->getHash(), 'sha256');
}
/**
* @expectedException \phpseclib\Exception\UnsupportedAlgorithmException
*/
public function testConstructorArgumentInvalid()
{
new Hash('abcdefghijklmnopqrst');
}
public function testConstructorArgumentValid()
{
$hash = new Hash('whirlpool');
$this->assertSame($hash->getHash(), 'whirlpool');
}
/**
* @expectedException \phpseclib\Exception\UnsupportedAlgorithmException
*/
public function testSetHashInvalid()
{
$hash = new Hash('md5');
$hash->setHash('abcdefghijklmnopqrst-96');
}
public function testSetHashValid()
{
$hash = new Hash('md5');
$this->assertSame($hash->getHash(), 'md5');
$hash->setHash('sha1');
$this->assertSame($hash->getHash(), 'sha1');
}
/**
* @dataProvider lengths
*/
public function testGetLengthKnown($algorithm, $length)
{
$hash = new Hash($algorithm);
$this->assertSame($hash->getLength(), $length);
}
public function lengths()
{
return array(
// known
array('md5-96', 12),
array('md5', 16),
array('sha1', 20),
array('sha256', 32),
array('sha384', 48),
array('sha512', 64),
// unknown
array('whirlpool', 64),
);
}
}

View File

@ -110,6 +110,18 @@ class Unit_Net_SSH2Test extends PhpseclibTestCase
$this->assertFalse($ssh->isQuietModeEnabled()); $this->assertFalse($ssh->isQuietModeEnabled());
} }
public function testGetConnectionByResourceId()
{
$ssh = new \phpseclib\Net\SSH2('localhost');
$this->assertSame($ssh, \phpseclib\Net\SSH2::getConnectionByResourceId($ssh->getResourceId()));
}
public function testGetResourceId()
{
$ssh = new \phpseclib\Net\SSH2('localhost');
$this->assertSame('{' . spl_object_hash($ssh) . '}', $ssh->getResourceId());
}
/** /**
* @return \phpseclib\Net\SSH2 * @return \phpseclib\Net\SSH2
*/ */