From d77c85218d1b74c02427132db808378b44a059de Mon Sep 17 00:00:00 2001 From: Jim Wigginton Date: Tue, 9 Feb 2010 06:10:26 +0000 Subject: [PATCH] - removed setMCrypt from the Crypt_* objects (you can achieve the same effect by doing ini_set('mcrypt.algorithms_dir', $path)) - added support for CTR mode to the various Crypt_* objects git-svn-id: http://phpseclib.svn.sourceforge.net/svnroot/phpseclib/trunk@83 21d32557-59b3-4da0-833f-c5933fad653e --- phpseclib/Crypt/AES.php | 132 +++++++++++++++++--------- phpseclib/Crypt/DES.php | 167 +++++++++++++++++++++++++-------- phpseclib/Crypt/Rijndael.php | 105 ++++++++++++++++++--- phpseclib/Crypt/TripleDES.php | 170 +++++++++++++++++++++++++--------- phpseclib/Net/SSH2.php | 83 ++++++++++++++--- 5 files changed, 504 insertions(+), 153 deletions(-) diff --git a/phpseclib/Crypt/AES.php b/phpseclib/Crypt/AES.php index b195e9d6..edeae6ab 100644 --- a/phpseclib/Crypt/AES.php +++ b/phpseclib/Crypt/AES.php @@ -56,7 +56,7 @@ * @author Jim Wigginton * @copyright MMVIII Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: AES.php,v 1.6 2010-01-04 07:59:01 terrafrost Exp $ + * @version $Id: AES.php,v 1.7 2010-02-09 06:10:25 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -70,6 +70,14 @@ require_once 'Rijndael.php'; * @see Crypt_AES::encrypt() * @see Crypt_AES::decrypt() */ +/** + * Encrypt / decrypt using the Counter mode. + * + * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 + */ +define('CRYPT_AES_MODE_CTR', -1); /** * Encrypt / decrypt using the Electronic Code Book mode. * @@ -108,13 +116,28 @@ define('CRYPT_AES_MODE_MCRYPT', 2); */ class Crypt_AES extends Crypt_Rijndael { /** - * MCrypt parameters + * mcrypt resource for encryption * - * @see Crypt_AES::setMCrypt() - * @var Array + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see Crypt_AES::encrypt() + * @var String * @access private */ - var $mcrypt = array('', ''); + var $enmcrypt; + + /** + * mcrypt resource for decryption + * + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see Crypt_AES::decrypt() + * @var String + * @access private + */ + var $demcrypt; /** * Default Constructor. @@ -147,6 +170,13 @@ class Crypt_AES extends Crypt_Rijndael { case CRYPT_AES_MODE_ECB: $this->mode = MCRYPT_MODE_ECB; break; + case CRYPT_AES_MODE_CTR: + // ctr doesn't have a constant associated with it even though it appears to be fairly widely + // supported. in lieu of knowing just how widely supported it is, i've, for now, opted not to + // include a compatibility layer. the layer has been implemented but, for now, is commented out. + $this->mode = 'ctr'; + //$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_AES_MODE_CTR; + break; case CRYPT_AES_MODE_CBC: default: $this->mode = MCRYPT_MODE_CBC; @@ -158,6 +188,9 @@ class Crypt_AES extends Crypt_Rijndael { case CRYPT_AES_MODE_ECB: $this->mode = CRYPT_RIJNDAEL_MODE_ECB; break; + case CRYPT_AES_MODE_CTR: + $this->mode = CRYPT_RIJNDAEL_MODE_CTR; + break; case CRYPT_AES_MODE_CBC: default: $this->mode = CRYPT_RIJNDAEL_MODE_CBC; @@ -203,18 +236,26 @@ class Crypt_AES extends Crypt_Rijndael { { if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) { $this->_mcryptSetup(); - $plaintext = $this->_pad($plaintext); + /* + if ($this->mode == CRYPT_AES_MODE_CTR) { + $iv = $this->encryptIV; + $xor = mcrypt_generic($this->enmcrypt, $this->_generate_xor(strlen($plaintext), $iv)); + $ciphertext = $plaintext ^ $xor; + if ($this->continuousBuffer) { + $this->encryptIV = $iv; + } + return $ciphertext; + } + */ - $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, $this->mcrypt[0], $this->mode, $this->mcrypt[1]); - mcrypt_generic_init($td, $this->key, $this->encryptIV); + if ($this->mode != 'ctr') { + $plaintext = $this->_pad($plaintext); + } - $ciphertext = mcrypt_generic($td, $plaintext); + $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); - mcrypt_generic_deinit($td); - mcrypt_module_close($td); - - if ($this->continuousBuffer) { - $this->encryptIV = substr($ciphertext, -16); + if (!$this->continuousBuffer) { + mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv); } return $ciphertext; @@ -234,46 +275,38 @@ class Crypt_AES extends Crypt_Rijndael { */ function decrypt($ciphertext) { - // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : - // "The data is padded with "\0" to make sure the length of the data is n * blocksize." - $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 15) & 0xFFFFFFF0, chr(0)); - if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) { $this->_mcryptSetup(); + /* + if ($this->mode == CRYPT_AES_MODE_CTR) { + $iv = $this->decryptIV; + $xor = mcrypt_generic($this->enmcrypt, $this->_generate_xor(strlen($ciphertext), $iv)); + $plaintext = $ciphertext ^ $xor; + if ($this->continuousBuffer) { + $this->decryptIV = $iv; + } + return $plaintext; + } + */ - $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, $this->mcrypt[0], $this->mode, $this->mcrypt[1]); - mcrypt_generic_init($td, $this->key, $this->decryptIV); - - $plaintext = mdecrypt_generic($td, $ciphertext); - - mcrypt_generic_deinit($td); - mcrypt_module_close($td); - - if ($this->continuousBuffer) { - $this->decryptIV = substr($ciphertext, -16); + if ($this->mode != 'ctr') { + // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : + // "The data is padded with "\0" to make sure the length of the data is n * blocksize." + $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 15) & 0xFFFFFFF0, chr(0)); } - return $this->_unpad($plaintext); + $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); + + if (!$this->continuousBuffer) { + mcrypt_generic_init($this->demcrypt, $this->key, $this->iv); + } + + return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext; } return parent::decrypt($ciphertext); } - /** - * Sets MCrypt parameters. (optional) - * - * If MCrypt is being used, empty strings will be used, unless otherwise specified. - * - * @link http://php.net/function.mcrypt-module-open#function.mcrypt-module-open - * @access public - * @param optional Integer $algorithm_directory - * @param optional Integer $mode_directory - */ - function setMCrypt($algorithm_directory = '', $mode_directory = '') - { - $this->mcrypt = array($algorithm_directory, $mode_directory); - } - /** * Setup mcrypt * @@ -315,6 +348,17 @@ class Crypt_AES extends Crypt_Rijndael { $this->key = substr($this->key, 0, $this->key_size); $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, 16), 16, chr(0)); + if (!isset($this->enmcrypt)) { + $mode = $this->mode; + //$mode = $this->mode == CRYPT_AES_MODE_CTR ? MCRYPT_MODE_ECB : $this->mode; + + $this->demcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, ''); + $this->enmcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, ''); + } // else should mcrypt_generic_deinit be called? + + mcrypt_generic_init($this->demcrypt, $this->key, $this->iv); + mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv); + $this->changed = false; } diff --git a/phpseclib/Crypt/DES.php b/phpseclib/Crypt/DES.php index 10616f2b..f694a080 100644 --- a/phpseclib/Crypt/DES.php +++ b/phpseclib/Crypt/DES.php @@ -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.11 2010-01-04 07:59:01 terrafrost Exp $ + * @version $Id: DES.php,v 1.12 2010-02-09 06:10:26 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -77,6 +77,14 @@ define('CRYPT_DES_DECRYPT', 1); * @see Crypt_DES::encrypt() * @see Crypt_DES::decrypt() */ +/** + * Encrypt / decrypt using the Counter mode. + * + * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 + */ +define('CRYPT_DES_MODE_CTR', -1); /** * Encrypt / decrypt using the Electronic Code Book mode. * @@ -178,13 +186,38 @@ class Crypt_DES { var $decryptIV = "\0\0\0\0\0\0\0\0"; /** - * MCrypt parameters + * mcrypt resource for encryption * - * @see Crypt_DES::setMCrypt() - * @var Array + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see Crypt_AES::encrypt() + * @var String * @access private */ - var $mcrypt = array('', ''); + var $enmcrypt; + + /** + * mcrypt resource for decryption + * + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see Crypt_AES::decrypt() + * @var String + * @access private + */ + var $demcrypt; + + /** + * Does the (en|de)mcrypt resource need to be (re)initialized? + * + * @see setKey() + * @see setIV() + * @var Boolean + * @access private + */ + var $changed = true; /** * Default Constructor. @@ -217,6 +250,10 @@ class Crypt_DES { case CRYPT_DES_MODE_ECB: $this->mode = MCRYPT_MODE_ECB; break; + case CRYPT_DES_MODE_CTR: + $this->mode = 'ctr'; + //$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_DES_MODE_CTR; + break; case CRYPT_DES_MODE_CBC: default: $this->mode = MCRYPT_MODE_CBC; @@ -226,6 +263,7 @@ class Crypt_DES { default: switch ($mode) { case CRYPT_DES_MODE_ECB: + case CRYPT_DES_MODE_CTR: case CRYPT_DES_MODE_CBC: $this->mode = $mode; break; @@ -252,6 +290,7 @@ class Crypt_DES { function setKey($key) { $this->keys = ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) ? substr($key, 0, 8) : $this->_prepareKey($key); + $this->changed = true; } /** @@ -265,22 +304,46 @@ class Crypt_DES { */ 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, 8), 8, chr(0)); + $this->changed = true; } /** - * Sets MCrypt parameters. (optional) + * Generate CTR XOR encryption key * - * If MCrypt is being used, empty strings will be used, unless otherwise specified. + * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the + * plaintext / ciphertext in CTR mode. * - * @link http://php.net/function.mcrypt-module-open#function.mcrypt-module-open + * @see Crypt_DES::decrypt() + * @see Crypt_DES::encrypt() * @access public - * @param optional Integer $algorithm_directory - * @param optional Integer $mode_directory + * @param Integer $length + * @param String $iv */ - function setMCrypt($algorithm_directory = '', $mode_directory = '') + function _generate_xor($length, &$iv) { - $this->mcrypt = array($algorithm_directory, $mode_directory); + $xor = ''; + $num_blocks = ($length + 7) >> 3; + for ($i = 0; $i < $num_blocks; $i++) { + $xor.= $iv; + for ($j = 4; $j <= 8; $j+=4) { + $temp = substr($iv, -$j, 4); + switch ($temp) { + case "\xFF\xFF\xFF\xFF": + $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4); + break; + case "\x7F\xFF\xFF\xFF": + $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4); + break 2; + default: + extract(unpack('Ncount', $temp)); + $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4); + break 2; + } + } + } + + return $xor; } /** @@ -302,19 +365,23 @@ class Crypt_DES { */ function encrypt($plaintext) { - $plaintext = $this->_pad($plaintext); + if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') { + $plaintext = $this->_pad($plaintext); + } if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { - $td = mcrypt_module_open(MCRYPT_DES, $this->mcrypt[0], $this->mode, $this->mcrypt[1]); - mcrypt_generic_init($td, $this->keys, $this->encryptIV); + if ($this->changed) { + if (!isset($this->enmcrypt)) { + $this->enmcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, ''); + } + mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV); + $this->changed = false; + } - $ciphertext = mcrypt_generic($td, $plaintext); + $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); - mcrypt_generic_deinit($td); - mcrypt_module_close($td); - - if ($this->continuousBuffer) { - $this->encryptIV = substr($ciphertext, -8); + if (!$this->continuousBuffer) { + mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV); } return $ciphertext; @@ -342,6 +409,17 @@ class Crypt_DES { if ($this->continuousBuffer) { $this->encryptIV = $xor; } + break; + case CRYPT_DES_MODE_CTR: + $xor = $this->encryptIV; + for ($i = 0; $i < strlen($plaintext); $i+=8) { + $block = substr($plaintext, $i, 8); + $key = $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT); + $ciphertext.= $block ^ $key; + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + } } return $ciphertext; @@ -358,24 +436,28 @@ class Crypt_DES { */ function decrypt($ciphertext) { - // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : - // "The data is padded with "\0" to make sure the length of the data is n * blocksize." - $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0)); + if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') { + // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : + // "The data is padded with "\0" to make sure the length of the data is n * blocksize." + $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0)); + } if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { - $td = mcrypt_module_open(MCRYPT_DES, $this->mcrypt[0], $this->mode, $this->mcrypt[1]); - mcrypt_generic_init($td, $this->keys, $this->decryptIV); - - $plaintext = mdecrypt_generic($td, $ciphertext); - - mcrypt_generic_deinit($td); - mcrypt_module_close($td); - - if ($this->continuousBuffer) { - $this->decryptIV = substr($ciphertext, -8); + if ($this->changed) { + if (!isset($this->demcrypt)) { + $this->demcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, ''); + } + mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV); + $this->changed = false; } - return $this->_unpad($plaintext); + $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); + + if (!$this->continuousBuffer) { + mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV); + } + + return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext; } if (!is_array($this->keys)) { @@ -399,9 +481,20 @@ class Crypt_DES { if ($this->continuousBuffer) { $this->decryptIV = $xor; } + break; + case CRYPT_DES_MODE_CTR: + $xor = $this->decryptIV; + for ($i = 0; $i < strlen($ciphertext); $i+=8) { + $block = substr($ciphertext, $i, 8); + $key = $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT); + $plaintext.= $block ^ $key; + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + } } - return $this->_unpad($plaintext); + return $this->mode != CRYPT_DES_MODE_CTR ? $this->_unpad($plaintext) : $plaintext; } /** diff --git a/phpseclib/Crypt/Rijndael.php b/phpseclib/Crypt/Rijndael.php index 4e6d39ef..6309e173 100644 --- a/phpseclib/Crypt/Rijndael.php +++ b/phpseclib/Crypt/Rijndael.php @@ -64,7 +64,7 @@ * @author Jim Wigginton * @copyright MMVIII Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: Rijndael.php,v 1.11 2010-01-23 17:36:49 terrafrost Exp $ + * @version $Id: Rijndael.php,v 1.12 2010-02-09 06:10:26 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -73,6 +73,14 @@ * @see Crypt_Rijndael::encrypt() * @see Crypt_Rijndael::decrypt() */ +/** + * Encrypt / decrypt using the Counter mode. + * + * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode. + * + * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29 + */ +define('CRYPT_RIJNDAEL_MODE_CTR', -1); /** * Encrypt / decrypt using the Electronic Code Book mode. * @@ -363,6 +371,7 @@ class Crypt_Rijndael { switch ($mode) { case CRYPT_RIJNDAEL_MODE_ECB: case CRYPT_RIJNDAEL_MODE_CBC: + case CRYPT_RIJNDAEL_MODE_CTR: $this->mode = $mode; break; default: @@ -542,6 +551,45 @@ class Crypt_Rijndael { $this->changed = true; } + /** + * Generate CTR XOR encryption key + * + * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the + * plaintext / ciphertext in CTR mode. + * + * @see Crypt_Rijndael::decrypt() + * @see Crypt_Rijndael::encrypt() + * @access public + * @param Integer $length + * @param String $iv + */ + function _generate_xor($length, &$iv) + { + $xor = ''; + $block_size = $this->block_size; + $num_blocks = floor(($length + ($block_size - 1)) / $block_size); + for ($i = 0; $i < $num_blocks; $i++) { + $xor.= $iv; + for ($j = 4; $j <= $block_size; $j+=4) { + $temp = substr($iv, -$j, 4); + switch ($temp) { + case "\xFF\xFF\xFF\xFF": + $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4); + break; + case "\x7F\xFF\xFF\xFF": + $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4); + break 2; + default: + extract(unpack('Ncount', $temp)); + $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4); + break 2; + } + } + } + + return $xor; + } + /** * Encrypts a message. * @@ -563,19 +611,22 @@ class Crypt_Rijndael { function encrypt($plaintext) { $this->_setup(); - $plaintext = $this->_pad($plaintext); + if ($this->mode != CRYPT_RIJNDAEL_MODE_CTR) { + $plaintext = $this->_pad($plaintext); + } + $block_size = $this->block_size; $ciphertext = ''; switch ($this->mode) { case CRYPT_RIJNDAEL_MODE_ECB: - for ($i = 0; $i < strlen($plaintext); $i+=$this->block_size) { - $ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $this->block_size)); + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $block_size)); } break; case CRYPT_RIJNDAEL_MODE_CBC: $xor = $this->encryptIV; - for ($i = 0; $i < strlen($plaintext); $i+=$this->block_size) { - $block = substr($plaintext, $i, $this->block_size); + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $block = substr($plaintext, $i, $block_size); $block = $this->_encryptBlock($block ^ $xor); $xor = $block; $ciphertext.= $block; @@ -583,6 +634,17 @@ class Crypt_Rijndael { if ($this->continuousBuffer) { $this->encryptIV = $xor; } + break; + case CRYPT_RIJNDAEL_MODE_CTR: + $xor = $this->encryptIV; + for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { + $block = substr($plaintext, $i, $block_size); + $key = $this->_encryptBlock($this->_generate_xor($block_size, $xor)); + $ciphertext.= $block ^ $key; + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + } } return $ciphertext; @@ -601,30 +663,45 @@ class Crypt_Rijndael { function decrypt($ciphertext) { $this->_setup(); - // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : - // "The data is padded with "\0" to make sure the length of the data is n * blocksize." - $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + $this->block_size - 1) % $this->block_size, chr(0)); + if ($this->mode != CRYPT_RIJNDAEL_MODE_CTR) { + // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : + // "The data is padded with "\0" to make sure the length of the data is n * blocksize." + $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + $this->block_size - 1) % $this->block_size, chr(0)); + } + + $block_size = $this->block_size; $plaintext = ''; switch ($this->mode) { case CRYPT_RIJNDAEL_MODE_ECB: - for ($i = 0; $i < strlen($ciphertext); $i+=$this->block_size) { - $plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $this->block_size)); + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $block_size)); } break; case CRYPT_RIJNDAEL_MODE_CBC: $xor = $this->decryptIV; - for ($i = 0; $i < strlen($ciphertext); $i+=$this->block_size) { - $block = substr($ciphertext, $i, $this->block_size); + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $block = substr($ciphertext, $i, $block_size); $plaintext.= $this->_decryptBlock($block) ^ $xor; $xor = $block; } if ($this->continuousBuffer) { $this->decryptIV = $xor; } + break; + case CRYPT_RIJNDAEL_MODE_CTR: + $xor = $this->decryptIV; + for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { + $block = substr($ciphertext, $i, $block_size); + $key = $this->_encryptBlock($this->_generate_xor($block_size, $xor)); + $plaintext.= $block ^ $key; + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + } } - return $this->_unpad($plaintext); + return $this->mode != CRYPT_RIJNDAEL_MODE_CTR ? $this->_unpad($plaintext) : $plaintext; } /** diff --git a/phpseclib/Crypt/TripleDES.php b/phpseclib/Crypt/TripleDES.php index 00ce6a27..203e2847 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.11 2010-01-04 07:59:01 terrafrost Exp $ + * @version $Id: TripleDES.php,v 1.12 2010-02-09 06:10:26 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -142,15 +142,6 @@ class Crypt_TripleDES { */ var $decryptIV = "\0\0\0\0\0\0\0\0"; - /** - * MCrypt parameters - * - * @see Crypt_TripleDES::setMCrypt() - * @var Array - * @access private - */ - var $mcrypt = array('', ''); - /** * The Crypt_DES objects * @@ -159,6 +150,40 @@ class Crypt_TripleDES { */ var $des; + /** + * mcrypt resource for encryption + * + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see Crypt_AES::encrypt() + * @var String + * @access private + */ + var $enmcrypt; + + /** + * mcrypt resource for decryption + * + * The mcrypt resource can be recreated every time something needs to be created or it can be created just once. + * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode. + * + * @see Crypt_AES::decrypt() + * @var String + * @access private + */ + var $demcrypt; + + /** + * Does the (en|de)mcrypt resource need to be (re)initialized? + * + * @see setKey() + * @see setIV() + * @var Boolean + * @access private + */ + var $changed = true; + /** * Default Constructor. * @@ -204,7 +229,11 @@ class Crypt_TripleDES { 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_CTR: + $this->mode = 'ctr'; + break; case CRYPT_DES_MODE_CBC: default: $this->mode = MCRYPT_MODE_CBC; @@ -225,6 +254,7 @@ class Crypt_TripleDES { switch ($mode) { case CRYPT_DES_MODE_ECB: + case CRYPT_DES_MODE_CTR: case CRYPT_DES_MODE_CBC: $this->mode = $mode; break; @@ -264,6 +294,7 @@ class Crypt_TripleDES { $this->des[1]->setKey(substr($key, 8, 8)); $this->des[2]->setKey(substr($key, 16, 8)); } + $this->changed = true; } /** @@ -283,26 +314,45 @@ class Crypt_TripleDES { $this->des[1]->setIV($iv); $this->des[2]->setIV($iv); } + $this->changed = true; } /** - * Sets MCrypt parameters. (optional) + * Generate CTR XOR encryption key * - * If MCrypt is being used, empty strings will be used, unless otherwise specified. + * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the + * plaintext / ciphertext in CTR mode. * - * @link http://php.net/function.mcrypt-module-open#function.mcrypt-module-open + * @see Crypt_DES::decrypt() + * @see Crypt_DES::encrypt() * @access public - * @param optional Integer $algorithm_directory - * @param optional Integer $mode_directory + * @param Integer $length + * @param String $iv */ - function setMCrypt($algorithm_directory = '', $mode_directory = '') + function _generate_xor($length, &$iv) { - $this->mcrypt = array($algorithm_directory, $mode_directory); - if ( $this->mode == CRYPT_DES_MODE_3CBC ) { - $this->des[0]->setMCrypt($algorithm_directory, $mode_directory); - $this->des[1]->setMCrypt($algorithm_directory, $mode_directory); - $this->des[2]->setMCrypt($algorithm_directory, $mode_directory); + $xor = ''; + $num_blocks = ($length + 7) >> 3; + for ($i = 0; $i < $num_blocks; $i++) { + $xor.= $iv; + for ($j = 4; $j <= 8; $j+=4) { + $temp = substr($iv, -$j, 4); + switch ($temp) { + case "\xFF\xFF\xFF\xFF": + $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4); + break; + case "\x7F\xFF\xFF\xFF": + $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4); + break 2; + default: + extract(unpack('Ncount', $temp)); + $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4); + break 2; + } + } } + + return $xor; } /** @@ -313,7 +363,9 @@ class Crypt_TripleDES { */ function encrypt($plaintext) { - $plaintext = $this->_pad($plaintext); + if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') { + $plaintext = $this->_pad($plaintext); + } // if the key is smaller then 8, do what we'd normally do if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) { @@ -323,16 +375,18 @@ class Crypt_TripleDES { } if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { - $td = mcrypt_module_open(MCRYPT_3DES, $this->mcrypt[0], $this->mode, $this->mcrypt[1]); - mcrypt_generic_init($td, $this->key, $this->encryptIV); + if ($this->changed) { + if (!isset($this->enmcrypt)) { + $this->enmcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, ''); + } + mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + $this->changed = false; + } - $ciphertext = mcrypt_generic($td, $plaintext); + $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); - mcrypt_generic_deinit($td); - mcrypt_module_close($td); - - if ($this->continuousBuffer) { - $this->encryptIV = substr($ciphertext, -8); + if (!$this->continuousBuffer) { + mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV); } return $ciphertext; @@ -374,6 +428,20 @@ class Crypt_TripleDES { if ($this->continuousBuffer) { $this->encryptIV = $xor; } + break; + case CRYPT_DES_MODE_CTR: + $xor = $this->encryptIV; + for ($i = 0; $i < strlen($plaintext); $i+=8) { + $key = $this->_generate_xor(8, $xor); + $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT); + $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT); + $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); + $block = substr($plaintext, $i, 8); + $ciphertext.= $block ^ $key; + } + if ($this->continuousBuffer) { + $this->encryptIV = $xor; + } } return $ciphertext; @@ -398,19 +466,21 @@ class Crypt_TripleDES { $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0)); if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { - $td = mcrypt_module_open(MCRYPT_3DES, $this->mcrypt[0], $this->mode, $this->mcrypt[1]); - mcrypt_generic_init($td, $this->key, $this->decryptIV); - - $plaintext = mdecrypt_generic($td, $ciphertext); - - mcrypt_generic_deinit($td); - mcrypt_module_close($td); - - if ($this->continuousBuffer) { - $this->decryptIV = substr($ciphertext, -8); + if ($this->changed) { + if (!isset($this->demcrypt)) { + $this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, ''); + } + mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + $this->changed = false; } - return $this->_unpad($plaintext); + $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); + + if (!$this->continuousBuffer) { + mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV); + } + + return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext; } if (strlen($this->key) <= 8) { @@ -445,9 +515,23 @@ class Crypt_TripleDES { if ($this->continuousBuffer) { $this->decryptIV = $xor; } + break; + case CRYPT_DES_MODE_CTR: + $xor = $this->decryptIV; + for ($i = 0; $i < strlen($ciphertext); $i+=8) { + $key = $this->_generate_xor(8, $xor); + $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT); + $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT); + $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); + $block = substr($ciphertext, $i, 8); + $plaintext.= $block ^ $key; + } + if ($this->continuousBuffer) { + $this->decryptIV = $xor; + } } - return $this->_unpad($plaintext); + return $this->mode != CRYPT_DES_MODE_CTR ? $this->_unpad($plaintext) : $plaintext; } /** diff --git a/phpseclib/Net/SSH2.php b/phpseclib/Net/SSH2.php index e5e02d4d..edbc8541 100644 --- a/phpseclib/Net/SSH2.php +++ b/phpseclib/Net/SSH2.php @@ -60,7 +60,7 @@ * @author Jim Wigginton * @copyright MMVII Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: SSH2.php,v 1.34 2010-01-21 07:33:05 terrafrost Exp $ + * @version $Id: SSH2.php,v 1.35 2010-02-09 06:10:26 terrafrost Exp $ * @link http://phpseclib.sourceforge.net */ @@ -674,10 +674,22 @@ class Net_SSH2 { ); static $encryption_algorithms = array( - 'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key 'aes128-cbc', // RECOMMENDED AES with a 128-bit key 'aes192-cbc', // OPTIONAL AES with a 192-bit key 'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key + + // from : + 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key + 'aes192-ctr', // RECOMMENDED AES with 192-bit key + 'aes256-ctr', // RECOMMENDED AES with 256-bit key + '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode + + // from : + // (commented out since i don't know of any ssh servers that support them and thus am unable to test them) + //'arcfour128', + //'arcfour256', + + '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 ); @@ -777,20 +789,28 @@ class Net_SSH2 { $decrypt = $encryption_algorithms[$i]; switch ($decrypt) { case '3des-cbc': + case '3des-ctr': $decryptKeyLength = 24; // eg. 192 / 8 break; case 'aes256-cbc': + case 'aes256-ctr': $decryptKeyLength = 32; // eg. 256 / 8 break; case 'aes192-cbc': + case 'aes192-ctr': $decryptKeyLength = 24; // eg. 192 / 8 break; case 'aes128-cbc': + case 'aes128-ctr': $decryptKeyLength = 16; // eg. 128 / 8 break; case 'arcfour': + case 'arcfour128': $decryptKeyLength = 16; // eg. 128 / 8 break; + case 'arcfour256': + $decryptKeyLength = 32; // eg. 128 / 8 + break; case 'none'; $decryptKeyLength = 0; } @@ -804,20 +824,28 @@ class Net_SSH2 { $encrypt = $encryption_algorithms[$i]; switch ($encrypt) { case '3des-cbc': + case '3des-ctr': $encryptKeyLength = 24; break; case 'aes256-cbc': + case 'aes256-ctr': $encryptKeyLength = 32; break; case 'aes192-cbc': + case 'aes192-ctr': $encryptKeyLength = 24; break; case 'aes128-cbc': + case 'aes128-ctr': $encryptKeyLength = 16; break; case 'arcfour': + case 'arcfour128': $encryptKeyLength = 16; break; + case 'arcfour256': + $encryptKeyLength = 32; + break; case 'none'; $encryptKeyLength = 0; } @@ -1073,19 +1101,25 @@ class Net_SSH2 { $this->encrypt = new Crypt_TripleDES(); // $this->encrypt_block_size = 64 / 8 == the default break; + case '3des-ctr': + $this->encrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR); + // $this->encrypt_block_size = 64 / 8 == the default + break; case 'aes256-cbc': + case 'aes192-cbc': + case 'aes128-cbc': $this->encrypt = new Crypt_AES(); $this->encrypt_block_size = 16; // eg. 128 / 8 break; - case 'aes192-cbc': - $this->encrypt = new Crypt_AES(); - $this->encrypt_block_size = 16; - break; - case 'aes128-cbc': - $this->encrypt = new Crypt_AES(); - $this->encrypt_block_size = 16; + case 'aes256-ctr': + case 'aes192-ctr': + case 'aes128-ctr': + $this->encrypt = new Crypt_AES(CRYPT_AES_MODE_CTR); + $this->encrypt_block_size = 16; // eg. 128 / 8 break; case 'arcfour': + case 'arcfour128': + case 'arcfour256': $this->encrypt = new Crypt_RC4(); break; case 'none'; @@ -1096,19 +1130,24 @@ class Net_SSH2 { case '3des-cbc': $this->decrypt = new Crypt_TripleDES(); break; + case '3des-ctr': + $this->decrypt = new Crypt_TripleDES(CRYPT_DES_MODE_CTR); + break; case 'aes256-cbc': - $this->decrypt = new Crypt_AES(); - $this->decrypt_block_size = 16; - break; case 'aes192-cbc': - $this->decrypt = new Crypt_AES(); - $this->decrypt_block_size = 16; - break; case 'aes128-cbc': $this->decrypt = new Crypt_AES(); $this->decrypt_block_size = 16; break; + case 'aes256-ctr': + case 'aes192-ctr': + case 'aes128-ctr': + $this->decrypt = new Crypt_AES(CRYPT_AES_MODE_CTR); + $this->decrypt_block_size = 16; + break; case 'arcfour': + case 'arcfour128': + case 'arcfour256': $this->decrypt = new Crypt_RC4(); break; case 'none'; @@ -1151,6 +1190,20 @@ class Net_SSH2 { $this->decrypt->setKey(substr($key, 0, $decryptKeyLength)); } + /* The "arcfour128" algorithm is the RC4 cipher, as described in + [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream + generated by the cipher MUST be discarded, and the first byte of the + first encrypted packet MUST be encrypted using the 1537th byte of + keystream. + + -- http://tools.ietf.org/html/rfc4345#section-4 */ + if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') { + $this->encrypt->encrypt(str_repeat("\0", 1536)); + } + if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') { + $this->decrypt->decrypt(str_repeat("\0", 1536)); + } + for ($i = 0; $i < count($mac_algorithms) && !in_array($mac_algorithms[$i], $this->mac_algorithms_client_to_server); $i++); if ($i == count($mac_algorithms)) { user_error('No compatible client to server message authentication algorithms found', E_USER_NOTICE);