From 39de68ab488f9854ea0d765d3f99741976c8459d Mon Sep 17 00:00:00 2001 From: Jim Wigginton Date: Mon, 16 Feb 2009 22:22:13 +0000 Subject: [PATCH] - [bug] Crypt_Rijndael calculated IVs incorrectly - [new] renamed Crypt_HMAC to Crypt_Hash and revised the API - [new] added Crypt_AES - [new] added AES support to Net_SSH2 git-svn-id: http://phpseclib.svn.sourceforge.net/svnroot/phpseclib/trunk@21 21d32557-59b3-4da0-833f-c5933fad653e --- phpseclib/Crypt/DES.php | 40 ++++--- phpseclib/Crypt/{HMAC.php => Hash.php} | 160 +++++++++++++++---------- phpseclib/Crypt/RC4.php | 8 +- phpseclib/Crypt/Rijndael.php | 31 ++++- phpseclib/Crypt/TripleDES.php | 35 ++++-- phpseclib/Net/SSH2.php | 116 ++++++++++-------- 6 files changed, 240 insertions(+), 150 deletions(-) rename phpseclib/Crypt/{HMAC.php => Hash.php} (50%) diff --git a/phpseclib/Crypt/DES.php b/phpseclib/Crypt/DES.php index dc829d7e..4084a87e 100644 --- a/phpseclib/Crypt/DES.php +++ b/phpseclib/Crypt/DES.php @@ -2,7 +2,7 @@ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ /** - * Pure-PHP implementations of DES. + * Pure-PHP implementation of DES. * * Uses mcrypt, if available, and an internal implementation, otherwise. * @@ -53,7 +53,7 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: DES.php,v 1.5 2008-08-04 17:59:12 terrafrost Exp $ + * @version $Id: DES.php,v 1.6 2009-02-16 22:22:13 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -154,7 +154,7 @@ class Crypt_DES { * The Initialization Vector * * @see Crypt_DES::setIV() - * @var Integer + * @var String * @access private */ var $iv = "\0\0\0\0\0\0\0\0"; @@ -163,7 +163,7 @@ class Crypt_DES { * A "sliding" Initialization Vector * * @see Crypt_DES::enableContinuousBuffer() - * @var Integer + * @var String * @access private */ var $encryptIV = "\0\0\0\0\0\0\0\0"; @@ -172,7 +172,7 @@ class Crypt_DES { * A "sliding" Initialization Vector * * @see Crypt_DES::enableContinuousBuffer() - * @var Integer + * @var String * @access private */ var $decryptIV = "\0\0\0\0\0\0\0\0"; @@ -215,7 +215,8 @@ class Crypt_DES { case CRYPT_DES_MODE_MCRYPT: switch ($mode) { case CRYPT_DES_MODE_ECB: - $this->mode = MCRYPT_MODE_ECB; break; + $this->mode = MCRYPT_MODE_ECB; + break; case CRYPT_DES_MODE_CBC: default: $this->mode = MCRYPT_MODE_CBC; @@ -274,10 +275,10 @@ class Crypt_DES { * * @link http://php.net/function.mcrypt-module-open#function.mcrypt-module-open * @access public - * @param Integer $algorithm_directory - * @param Integer $mode_directory + * @param optional Integer $algorithm_directory + * @param optional Integer $mode_directory */ - function setMCrypt($algorithm_directory, $mode_directory) + function setMCrypt($algorithm_directory = '', $mode_directory = '') { $this->mcrypt = array($algorithm_directory, $mode_directory); } @@ -504,21 +505,26 @@ class Crypt_DES { */ function _pad($text) { + $length = strlen($text); + if (!$this->padding) { - if (strlen($text) & 7 == 0) { + if ($length & 7 == 0) { return $text; } else { + user_error("The plaintext's length ($length) is not a multiple of the block size (8)", E_USER_NOTICE); $this->padding = true; } } - $length = 8 - (strlen($text) & 7); - return str_pad($text, strlen($text) + $length, chr($length)); + $pad = 8 - ($length & 7); + return str_pad($text, $length + $pad, chr($pad)); } /** * Unpads a string * + * If padding is enabled and the reported padding length exceeds the block size, padding will be, hence forth, disabled. + * * @see Crypt_DES::_pad() * @access private */ @@ -529,6 +535,13 @@ class Crypt_DES { } $length = ord($text[strlen($text) - 1]); + + if ($length > 8) { + user_error("The number of bytes reported as being padded ($length) exceeds the block size (8)", E_USER_NOTICE); + $this->padding = false; + return $text; + } + return substr($text, 0, -$length); } @@ -835,5 +848,4 @@ class Crypt_DES { } // vim: ts=4:sw=4:et: -// vim6: fdl=1: -?> \ No newline at end of file +// vim6: fdl=1: \ No newline at end of file diff --git a/phpseclib/Crypt/HMAC.php b/phpseclib/Crypt/Hash.php similarity index 50% rename from phpseclib/Crypt/HMAC.php rename to phpseclib/Crypt/Hash.php index dacd4a81..ec657bd9 100644 --- a/phpseclib/Crypt/HMAC.php +++ b/phpseclib/Crypt/Hash.php @@ -2,12 +2,30 @@ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ /** - * Pure-PHP implementation of keyed-hash message authentication codes (HMACs). + * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions. * - * Uses hash() or mhash() if available and an internal implementation, otherwise. Currently supports md5 and sha1. + * Uses hash() or mhash() if available and an internal implementation, otherwise. Currently supports md5, md5-96, sha1, and + * sha1-96. If {@link Crypt_Hash::setKey() setKey()} is called, {@link Crypt_Hash::hash() hash()} will return the HMAC as + * as opposed to the hash. If no valid algorithm is provided, sha1 will be used. * * PHP versions 4 and 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: + * + * setKey('abcdefg'); + * + * echo base64_encode($hash->hash('abcdefg')); + * ?> + * + * * LICENSE: This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -24,58 +42,54 @@ * MA 02111-1307 USA * * @category Crypt - * @package Crypt_HMAC + * @package Crypt_Hash * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: HMAC.php,v 1.3 2007-09-23 04:41:39 terrafrost Exp $ + * @version $Id: Hash.php,v 1.1 2009-02-16 22:22:13 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ /**#@+ * @access private - * @see Crypt_HMAC::Crypt_HMAC() + * @see Crypt_Hash::Crypt_Hash() */ /** * Toggles the internal implementation */ -define('CRYPT_HMAC_MODE_INTERNAL', 1); +define('CRYPT_HASH_MODE_INTERNAL', 1); /** * Toggles the mhash() implementation, which has been deprecated on PHP 5.3.0+. */ -define('CRYPT_HMAC_MODE_MHASH', 2); +define('CRYPT_HASH_MODE_MHASH', 2); /** * Toggles the hash() implementation, which works on PHP 5.1.2+. */ -define('CRYPT_HMAC_MODE_HASH', 3); +define('CRYPT_HASH_MODE_HASH', 3); /**#@-*/ /** - * Pure-PHP implementation of keyed-hash message authentication codes (HMACs). + * Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions. * * @author Jim Wigginton * @version 0.1.0 * @access public - * @package Crypt_HMAC + * @package Crypt_Hash */ -class Crypt_HMAC { +class Crypt_Hash { /** - * Byte-length of compression blocks + * Byte-length of compression blocks / key (Internal HMAC) * - * The following URL provides more information: - * - * {@link http://tools.ietf.org/html/rfc2104#section-2 http://tools.ietf.org/html/rfc2104#section-2} - * - * @see Crypt_HMAC::setHash() + * @see Crypt_Hash::setAlgorithm() * @var Integer * @access private */ var $b; /** - * Byte-length of hash outputs + * Byte-length of hash output (Internal HMAC) * - * @see Crypt_HMAC::setHash() + * @see Crypt_Hash::setHash() * @var Integer * @access private */ @@ -84,7 +98,7 @@ class Crypt_HMAC { /** * Hash Algorithm * - * @see Crypt_HMAC::setHash() + * @see Crypt_Hash::setHash() * @var String * @access private */ @@ -93,65 +107,59 @@ class Crypt_HMAC { /** * Key * - * @see Crypt_HMAC::setKey() + * @see Crypt_Hash::setKey() * @var String * @access private */ var $key = ''; /** - * Outer XOR + * Outer XOR (Internal HMAC) * - * @see Crypt_HMAC::setKey() + * @see Crypt_Hash::setKey() * @var String * @access private */ var $opad; /** - * Inner XOR + * Inner XOR (Internal HMAC) * - * @see Crypt_HMAC::setKey() + * @see Crypt_Hash::setKey() * @var String * @access private */ var $ipad; - /** - * Final HMAC Length - * - * @see Crypt_HMAC::hmac() - * @var String - * @access private - */ - var $length = 0; - /** * Default Constructor. * - * @return Crypt_HMAC + * @param optional String $hash + * @return Crypt_Hash * @access public */ - function Crypt_HMAC() + function Crypt_Hash($hash = 'sha1') { - if ( !defined('CRYPT_HMAC_MODE') ) { + if ( !defined('CRYPT_HASH_MODE') ) { switch (true) { case extension_loaded('hash'): - define('CRYPT_HMAC_MODE', CRYPT_HMAC_MODE_HASH); + define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_HASH); break; case extension_loaded('mhash'): - define('CRYPT_HMAC_MODE', CRYPT_HMAC_MODE_MHASH); + define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_MHASH); break; default: - define('CRYPT_HMAC_MODE', CRYPT_HMAC_MODE_INTERNAL); + define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_INTERNAL); } } - $this->setHash('sha1'); + $this->setHash($hash); } /** - * Sets the key. + * Sets the key for HMACs + * + * Keys can be of any length. * * @access public * @param String $key @@ -164,8 +172,6 @@ class Crypt_HMAC { /** * Sets the hash function. * - * Currently, only 'sha1' and 'md5' are supported. If you do not supply a valid $hash, 'sha1' will be used. - * * @access public * @param String $hash */ @@ -174,7 +180,13 @@ class Crypt_HMAC { switch ($hash) { case 'md5-96': case 'sha1-96': - $this->length = 12; + $this->l = 12; // 96 / 8 = 12 + break; + case 'md5': + $this->l = 16; + break; + case 'sha1': + $this->l = 20; } switch (CRYPT_HMAC_MODE) { @@ -208,14 +220,12 @@ class Crypt_HMAC { case 'md5': case 'md5-96': $this->b = 64; - $this->l = 16; $this->hash = 'md5'; break; case 'sha1': case 'sha1-96': default: $this->b = 64; - $this->l = 20; $this->hash = 'sha1'; } @@ -229,28 +239,46 @@ class Crypt_HMAC { * @access public * @param String $text */ - function hmac($text) + function hash($text) { - switch (CRYPT_HMAC_MODE) { - case CRYPT_HMAC_MODE_MHASH: - $hmac = mhash($this->hash, $text, $this->key); - break; - case CRYPT_HMAC_MODE_HASH: - $hmac = hash_hmac($this->hash, $text, $this->key, true); - break; - case CRYPT_HMAC_MODE_INTERNAL: - $hash = $this->hash; + if (!empty($this->key)) { + switch (CRYPT_HASH_MODE) { + case CRYPT_HASH_MODE_MHASH: + $output = mhash($this->hash, $text, $this->key); + break; + case CRYPT_HASH_MODE_HASH: + $output = hash_hmac($this->hash, $text, $this->key, true); + break; + case CRYPT_HASH_MODE_INTERNAL: + $hash = $this->hash; + /* "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." - $key = strlen($this->key) > $this->b ? $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 = pack('H*', $hash($temp)); // step 4 - $hmac = $this->opad ^ $key; // step 5 - $hmac.= $temp; // step 6 - $hmac = pack('H*', $hash($hmac)); // step 7 + -- http://tools.ietf.org/html/rfc2104#section-2 */ + $key = strlen($this->key) > $this->b ? $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 = pack('H*', $hash($temp)); // step 4 + $output = $this->opad ^ $key; // step 5 + $output.= $temp; // step 6 + $output = pack('H*', $hash($output)); // step 7 + } + } else { + switch (CRYPT_HASH_MODE) { + case CRYPT_HASH_MODE_MHASH: + $output = mhash($this->hash, $text); + break; + case CRYPT_HASH_MODE_MHASH: + $output = hash($this->hash, $text, true); + break; + case CRYPT_HASH_MODE_INTERNAL: + $hash = $this->hash; + $output = pack('H*', $hash($output)); + } } - return $this->length ? substr($hmac, 0, $this->length) : $hmac; + return substr($output, 0, $this->l); } } \ No newline at end of file diff --git a/phpseclib/Crypt/RC4.php b/phpseclib/Crypt/RC4.php index 5008d70b..e5a2aa7e 100644 --- a/phpseclib/Crypt/RC4.php +++ b/phpseclib/Crypt/RC4.php @@ -55,7 +55,7 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: RC4.php,v 1.3 2007-07-25 21:56:14 terrafrost Exp $ + * @version $Id: RC4.php,v 1.4 2009-02-16 22:22:13 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -259,10 +259,10 @@ class Crypt_RC4 { * * @link http://php.net/function.mcrypt-module-open#function.mcrypt-module-open * @access public - * @param Integer $algorithm_directory - * @param Integer $mode_directory + * @param optional Integer $algorithm_directory + * @param optional Integer $mode_directory */ - function setMCrypt($algorithm_directory, $mode_directory) + function setMCrypt($algorithm_directory = '', $mode_directory = '') { if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) { $this->mcrypt = array($algorithm_directory, $mode_directory); diff --git a/phpseclib/Crypt/Rijndael.php b/phpseclib/Crypt/Rijndael.php index 83a152a5..fd3b8c33 100644 --- a/phpseclib/Crypt/Rijndael.php +++ b/phpseclib/Crypt/Rijndael.php @@ -4,8 +4,7 @@ /** * Pure-PHP implementation of Rijndael. * - * Does not use mcrypt, even when available, since mcrypt doesn't implement Rijndael - it implements a subset of Rijndael - * known as AES. + * Does not use mcrypt, even when available, for reasons that are explained below. * * PHP versions 4 and 5 * @@ -15,8 +14,8 @@ * 136-bits it'll be null-padded to 160-bits and 160 bits will be the key length until * {@link Crypt_Rijndael::setKey() setKey()} is called, again, at which point, it'll be recalculated. * - * Not all Rijndael implementations may support 160-bits or 224-bits as the block length / key length. AES, itself, only - * supports block lengths of 128 and key lengths of 128, 192, and 256. + * Not all Rijndael implementations may support 160-bits or 224-bits as the block length / key length. mcrypt, for example, + * does not. AES, itself, only supports block lengths of 128 and key lengths of 128, 192, and 256. * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=10 Rijndael-ammended.pdf#page=10} defines the * algorithm for block lengths of 192 and 256 but not for block lengths / key lengths of 160 and 224. Indeed, 160 and 224 * are first defined as valid key / block lengths in @@ -65,7 +64,7 @@ * @author Jim Wigginton * @copyright MMVIII Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: Rijndael.php,v 1.1 2009-02-01 15:37:25 terrafrost Exp $ + * @version $Id: Rijndael.php,v 1.2 2009-02-16 22:22:13 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -88,6 +87,20 @@ define('CRYPT_RIJNDAEL_MODE_ECB', 1); define('CRYPT_RIJNDAEL_MODE_CBC', 2); /**#@-*/ +/**#@+ + * @access private + * @see Crypt_AES::Crypt_AES() + */ +/** + * Toggles the internal implementation + */ +define('CRYPT_RIJNDAEL_MODE_INTERNAL', 1); +/** + * Toggles the mcrypt implementation + */ +define('CRYPT_RIJNDAEL_MODE_MCRYPT', 2); +/**#@-*/ + /** * Pure-PHP implementation of Rijndael. * @@ -338,6 +351,11 @@ class Crypt_Rijndael { /** * Default Constructor. * + * Determines whether or not the mcrypt extension should be used. $mode should only, at present, be + * CRYPT_RIJNDAEL_MODE_ECB or CRYPT_RIJNDAEL_MODE_CBC. If not explictly set, CRYPT_RIJNDAEL_MODE_CBC will be used. + * + * @param optional Integer $mode + * @return Crypt_Rijndael * @access public */ function Crypt_Rijndael($mode = CRYPT_MODE_RIJNDAEL_CBC) { @@ -464,7 +482,7 @@ class Crypt_Rijndael { */ function setIV($iv) { - $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0));; + $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, $this->block_size), $this->block_size, chr(0));; } /** @@ -1013,6 +1031,7 @@ class Crypt_Rijndael { } $pad = $this->block_size - ($length % $this->block_size); + return str_pad($text, $length + $pad, chr($pad)); } diff --git a/phpseclib/Crypt/TripleDES.php b/phpseclib/Crypt/TripleDES.php index 51ef0441..c47985bf 100644 --- a/phpseclib/Crypt/TripleDES.php +++ b/phpseclib/Crypt/TripleDES.php @@ -47,7 +47,7 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: TripleDES.php,v 1.4 2008-08-04 17:59:12 terrafrost Exp $ + * @version $Id: TripleDES.php,v 1.5 2009-02-16 22:22:13 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -119,7 +119,7 @@ class Crypt_TripleDES { * The Initialization Vector * * @see Crypt_TripleDES::setIV() - * @var Integer + * @var String * @access private */ var $iv = "\0\0\0\0\0\0\0\0"; @@ -128,7 +128,7 @@ class Crypt_TripleDES { * A "sliding" Initialization Vector * * @see Crypt_TripleDES::enableContinuousBuffer() - * @var Integer + * @var String * @access private */ var $encryptIV = "\0\0\0\0\0\0\0\0"; @@ -137,7 +137,7 @@ class Crypt_TripleDES { * A "sliding" Initialization Vector * * @see Crypt_TripleDES::enableContinuousBuffer() - * @var Integer + * @var String * @access private */ var $decryptIV = "\0\0\0\0\0\0\0\0"; @@ -292,10 +292,10 @@ class Crypt_TripleDES { * * @link http://php.net/function.mcrypt-module-open#function.mcrypt-module-open * @access public - * @param Integer $algorithm_directory - * @param Integer $mode_directory + * @param optional Integer $algorithm_directory + * @param optional Integer $mode_directory */ - function setMCrypt($algorithm_directory, $mode_directory) + function setMCrypt($algorithm_directory = '', $mode_directory = '') { $this->mcrypt = array($algorithm_directory, $mode_directory); if ( $this->mode == CRYPT_DES_MODE_3CBC ) { @@ -560,21 +560,26 @@ class Crypt_TripleDES { */ function _pad($text) { + $length = strlen($text); + if (!$this->padding) { - if (strlen($text) & 7 == 0) { + if ($length & 7 == 0) { return $text; } else { + user_error("The plaintext's length ($length) is not a multiple of the block size (8)", E_USER_NOTICE); $this->padding = true; } } - $length = 8 - (strlen($text) & 7); - return str_pad($text, strlen($text) + $length, chr($length)); + $pad = 8 - ($length & 7); + return str_pad($text, $length + $pad, chr($pad)); } /** * Unpads a string * + * If padding is enabled and the reported padding length exceeds the block size, padding will be, hence forth, disabled. + * * @see Crypt_TripleDES::_pad() * @access private */ @@ -585,10 +590,16 @@ class Crypt_TripleDES { } $length = ord($text[strlen($text) - 1]); + + if ($length > 8) { + user_error("The number of bytes reported as being padded ($length) exceeds the block size (8)", E_USER_NOTICE); + $this->padding = false; + return $text; + } + return substr($text, 0, -$length); } } // vim: ts=4:sw=4:et: -// vim6: fdl=1: -?> \ No newline at end of file +// vim6: fdl=1: \ No newline at end of file diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index 2b95ed6f..c507a0ed 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -41,7 +41,7 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: SSH2.php,v 1.8 2008-05-26 19:42:01 terrafrost Exp $ + * @version $Id: SSH2.php,v 1.9 2009-02-16 22:22:13 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -58,20 +58,25 @@ require_once('Math/BigInteger.php'); require_once('Crypt/Random.php'); /** - * Include Crypt_HMAC.php + * Include Crypt_Hash */ -require_once('Crypt/HMAC.php'); +require_once('Crypt/Hash.php'); /** - * Include Crypt_TripleDES.php + * Include Crypt_TripleDES */ require_once('Crypt/TripleDES.php'); /** - * Include Crypt_RC4.php + * Include Crypt_RC4 */ require_once('Crypt/RC4.php'); +/** + * Include Crypt_AES + */ +require_once('Crypt/AES.php'); + /**#@+ * Execution Bitmap Masks * @@ -227,13 +232,31 @@ class Net_SSH2 { var $languages_client_to_server; /** - * Block Size + * Block Size for Server to Client Encryption * + * "Note that the length of the concatenation of 'packet_length', + * 'padding_length', 'payload', and 'random padding' MUST be a multiple + * of the cipher block size or 8, whichever is larger. This constraint + * MUST be enforced, even when using stream ciphers." + * + * -- http://tools.ietf.org/html/rfc4253#section-6 + * + * @see Net_SSH2::Net_SSH2() * @see Net_SSH2::_send_binary_packet() * @var Integer * @access private */ - var $block_size = 8; + var $encrypt_block_size = 8; + + /** + * Block Size for Client to Server Encryption + * + * @see Net_SSH2::Net_SSH2() + * @see Net_SSH2::_get_binary_packet() + * @var Integer + * @access private + */ + var $decrypt_block_size = 8; /** * Server to Client Encryption Object @@ -525,11 +548,11 @@ class Net_SSH2 { ); static $encryption_algorithms = array( - '3des-cbc', // REQUIRED three-key 3DES in CBC mode 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key 'aes192-cbc', // OPTIONAL AES with a 192-bit key 'aes128-cbc', // RECOMMENDED AES with a 128-bit key 'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key + '3des-cbc', // REQUIRED three-key 3DES in CBC mode 'none' // OPTIONAL no encryption; NOT RECOMMENDED ); @@ -628,9 +651,7 @@ class Net_SSH2 { $decrypt = $encryption_algorithms[$i]; switch ($decrypt) { case '3des-cbc': - // uses the default block size $decryptKeyLength = 24; // eg. 192 / 8 - $decryptIVLength = 8; // eg. 64 / 8 break; case 'aes256-cbc': $decryptKeyLength = 32; // eg. 256 / 8 @@ -643,7 +664,6 @@ class Net_SSH2 { break; case 'arcfour': $decryptKeyLength = 16; // eg. 128 / 8 - $decryptIVLength = 0; break; case 'none'; $decryptKeyLength = 0; @@ -659,7 +679,6 @@ class Net_SSH2 { switch ($encrypt) { case '3des-cbc': $encryptKeyLength = 24; - $encryptIVLength = 8; break; case 'aes256-cbc': $encryptKeyLength = 32; @@ -672,7 +691,6 @@ class Net_SSH2 { break; case 'arcfour': $encryptKeyLength = 16; - $encryptIVLength = 0; break; case 'none'; $encryptKeyLength = 0; @@ -917,15 +935,19 @@ class Net_SSH2 { switch ($encrypt) { case '3des-cbc': $this->encrypt = new Crypt_TripleDES(); + // $this->encrypt_block_size = 64 / 8 == the default break; case 'aes256-cbc': - //$this->encrypt = new Crypt_AES(); + $this->encrypt = new Crypt_AES(); + $this->encrypt_block_size = 16; // eg. 128 / 8 break; case 'aes192-cbc': - //$this->encrypt = new Crypt_AES(); + $this->encrypt = new Crypt_AES(); + $this->encrypt_block_size = 16; break; case 'aes128-cbc': - //$this->encrypt = new Crypt_AES(); + $this->encrypt = new Crypt_AES(); + $this->encrypt_block_size = 16; break; case 'arcfour': $this->encrypt = new Crypt_RC4(); @@ -939,13 +961,16 @@ class Net_SSH2 { $this->decrypt = new Crypt_TripleDES(); break; case 'aes256-cbc': - //$this->decrypt = new Crypt_AES(); + $this->decrypt = new Crypt_AES(); + $this->decrypt_block_size = 16; break; case 'aes192-cbc': - //$this->decrypt = new Crypt_AES(); + $this->decrypt = new Crypt_AES(); + $this->decrypt_block_size = 16; break; case 'aes128-cbc': - //$this->decrypt = new Crypt_AES(); + $this->decrypt = new Crypt_AES(); + $this->decrypt_block_size = 16; break; case 'arcfour': $this->decrypt = new Crypt_RC4(); @@ -966,26 +991,22 @@ class Net_SSH2 { return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } - $this->hmac_create = new Crypt_HMAC(); + $createKeyLength = 0; // ie. $mac_algorithms[$i] == 'none' switch ($mac_algorithms[$i]) { - case 'none': - $this->hmac_create->setHash('none'); - $createKeyLength = 0; - break; case 'hmac-sha1': - $this->hmac_create->setHash('sha1'); + $this->hmac_create = new Crypt_Hash('sha1'); $createKeyLength = 20; break; case 'hmac-sha1-96': - $this->hmac_create->setHash('sha1-96'); + $this->hmac_create = new Crypt_Hash('sha1-96'); $createKeyLength = 20; break; case 'hmac-md5': - $this->hmac_create->setHash('md5'); + $this->hmac_create = new Crypt_Hash('md5'); $createKeyLength = 16; break; case 'hmac-md5-96': - $this->hmac_create->setHash('md5-96'); + $this->hmac_create = new Crypt_Hash('md5-96'); $createKeyLength = 16; } @@ -995,30 +1016,26 @@ class Net_SSH2 { return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } - $this->hmac_check = new Crypt_HMAC(); + $checkKeyLength = 0; + $this->hmac_size = 0; switch ($mac_algorithms[$i]) { - case 'none': - $this->hmac_check->setHash('none'); - $checkKeyLength = 0; - $this->hmac_size = 0; - break; case 'hmac-sha1': - $this->hmac_check->setHash('sha1'); + $this->hmac_check = new Crypt_Hash('sha1'); $checkKeyLength = 20; $this->hmac_size = 20; break; case 'hmac-sha1-96': - $this->hmac_check->setHash('sha1-96'); + $this->hmac_check = new Crypt_Hash('sha1-96'); $checkKeyLength = 20; $this->hmac_size = 12; break; case 'hmac-md5': - $this->hmac_check->setHash('md5'); + $this->hmac_check = new Crypt_Hash('md5'); $checkKeyLength = 16; $this->hmac_size = 16; break; case 'hmac-md5-96': - $this->hmac_check->setHash('md5-96'); + $this->hmac_check = new Crypt_Hash('md5-96'); $checkKeyLength = 16; $this->hmac_size = 12; } @@ -1026,16 +1043,16 @@ class Net_SSH2 { $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes); $iv = pack('H*', $hash($keyBytes . $source . 'A' . $this->session_id)); - while ($encryptIVLength > strlen($iv)) { + while ($this->encrypt_block_size > strlen($iv)) { $iv.= pack('H*', $hash($keyBytes . $source . $iv)); } - $this->encrypt->setIV(substr($iv, 0, $encryptIVLength)); + $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size)); $iv = pack('H*', $hash($keyBytes . $source . 'B' . $this->session_id)); - while ($decryptIVLength > strlen($iv)) { + while ($this->decrypt_block_size > strlen($iv)) { $iv.= pack('H*', $hash($keyBytes . $source . $iv)); } - $this->decrypt->setIV(substr($iv, 0, $decryptIVLength)); + $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size)); $key = pack('H*', $hash($keyBytes . $source . 'C' . $this->session_id)); while ($encryptKeyLength > strlen($key)) { @@ -1331,7 +1348,7 @@ class Net_SSH2 { return false; } - $raw = fread($this->fsock, $this->block_size); + $raw = fread($this->fsock, $this->decrypt_block_size); if ($this->decrypt !== false) { $raw = $this->decrypt->decrypt($raw); @@ -1341,8 +1358,11 @@ class Net_SSH2 { $packet_length = $temp['packet_length']; $padding_length = $temp['padding_length']; - $temp = fread($this->fsock, $packet_length + 4 - $this->block_size); - $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($temp) : $temp; + $remaining_length = $packet_length + 4 - $this->decrypt_block_size; + if ($remaining_length > 0) { + $temp = fread($this->fsock, $remaining_length); + $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($temp) : $temp; + } $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1); $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty @@ -1464,10 +1484,10 @@ class Net_SSH2 { return false; } - // 4, for the packet length + 1, for the padding length + 4, for the minimal padding amount + // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9 $packet_length = strlen($data) + 9; - // round up to the nearest $this->block_size - $packet_length+= (($this->block_size - 1) * $packet_length) % $this->block_size; + // round up to the nearest $this->encrypt_block_size + $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size; // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length $padding_length = $packet_length - strlen($data) - 5;