new phpseclib

This commit is contained in:
Roland Gruber 2011-05-19 18:02:11 +00:00
parent 55e4ba8634
commit dd380bc594
14 changed files with 2367 additions and 434 deletions

View File

@ -36,26 +36,29 @@
* ?> * ?>
* </code> * </code>
* *
* LICENSE: This library is free software; you can redistribute it and/or * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
* modify it under the terms of the GNU Lesser General Public * of this software and associated documentation files (the "Software"), to deal
* License as published by the Free Software Foundation; either * in the Software without restriction, including without limitation the rights
* version 2.1 of the License, or (at your option) any later version. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* * copies of the Software, and to permit persons to whom the Software is
* This library is distributed in the hope that it will be useful, * furnished to do so, subject to the following conditions:
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * The above copyright notice and this permission notice shall be included in
* Lesser General Public License for more details. * all copies or substantial portions of the Software.
* *
* You should have received a copy of the GNU Lesser General Public * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* License along with this library; if not, write to the Free Software * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* MA 02111-1307 USA * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* @category Crypt * @category Crypt
* @package Crypt_AES * @package Crypt_AES
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVIII Jim Wigginton * @copyright MMVIII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version $Id$ * @version $Id$
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -90,6 +93,18 @@ define('CRYPT_AES_MODE_ECB', 1);
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
*/ */
define('CRYPT_AES_MODE_CBC', 2); define('CRYPT_AES_MODE_CBC', 2);
/**
* Encrypt / decrypt using the Cipher Feedback mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
*/
define('CRYPT_AES_MODE_CFB', 3);
/**
* Encrypt / decrypt using the Cipher Feedback mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
*/
define('CRYPT_AES_MODE_OFB', 4);
/**#@-*/ /**#@-*/
/**#@+ /**#@+
@ -139,6 +154,16 @@ class Crypt_AES extends Crypt_Rijndael {
*/ */
var $demcrypt; var $demcrypt;
/**
* mcrypt resource for CFB mode
*
* @see Crypt_AES::encrypt()
* @see Crypt_AES::decrypt()
* @var String
* @access private
*/
var $ecb;
/** /**
* Default Constructor. * Default Constructor.
* *
@ -168,6 +193,7 @@ class Crypt_AES extends Crypt_Rijndael {
case CRYPT_AES_MODE_MCRYPT: case CRYPT_AES_MODE_MCRYPT:
switch ($mode) { switch ($mode) {
case CRYPT_AES_MODE_ECB: case CRYPT_AES_MODE_ECB:
$this->paddable = true;
$this->mode = MCRYPT_MODE_ECB; $this->mode = MCRYPT_MODE_ECB;
break; break;
case CRYPT_AES_MODE_CTR: case CRYPT_AES_MODE_CTR:
@ -177,22 +203,39 @@ class Crypt_AES extends Crypt_Rijndael {
$this->mode = 'ctr'; $this->mode = 'ctr';
//$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_AES_MODE_CTR; //$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_AES_MODE_CTR;
break; break;
case CRYPT_AES_MODE_CFB:
$this->mode = 'ncfb';
break;
case CRYPT_AES_MODE_OFB:
$this->mode = MCRYPT_MODE_NOFB;
break;
case CRYPT_AES_MODE_CBC: case CRYPT_AES_MODE_CBC:
default: default:
$this->paddable = true;
$this->mode = MCRYPT_MODE_CBC; $this->mode = MCRYPT_MODE_CBC;
} }
$this->debuffer = $this->enbuffer = '';
break; break;
default: default:
switch ($mode) { switch ($mode) {
case CRYPT_AES_MODE_ECB: case CRYPT_AES_MODE_ECB:
$this->paddable = true;
$this->mode = CRYPT_RIJNDAEL_MODE_ECB; $this->mode = CRYPT_RIJNDAEL_MODE_ECB;
break; break;
case CRYPT_AES_MODE_CTR: case CRYPT_AES_MODE_CTR:
$this->mode = CRYPT_RIJNDAEL_MODE_CTR; $this->mode = CRYPT_RIJNDAEL_MODE_CTR;
break; break;
case CRYPT_AES_MODE_CFB:
$this->mode = CRYPT_RIJNDAEL_MODE_CFB;
break;
case CRYPT_AES_MODE_OFB:
$this->mode = CRYPT_RIJNDAEL_MODE_OFB;
break;
case CRYPT_AES_MODE_CBC: case CRYPT_AES_MODE_CBC:
default: default:
$this->paddable = true;
$this->mode = CRYPT_RIJNDAEL_MODE_CBC; $this->mode = CRYPT_RIJNDAEL_MODE_CBC;
} }
} }
@ -235,6 +278,7 @@ class Crypt_AES extends Crypt_Rijndael {
function encrypt($plaintext) function encrypt($plaintext)
{ {
if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) { if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
$changed = $this->changed;
$this->_mcryptSetup(); $this->_mcryptSetup();
/* /*
if ($this->mode == CRYPT_AES_MODE_CTR) { if ($this->mode == CRYPT_AES_MODE_CTR) {
@ -247,8 +291,44 @@ class Crypt_AES extends Crypt_Rijndael {
return $ciphertext; return $ciphertext;
} }
*/ */
// re: http://phpseclib.sourceforge.net/cfb-demo.phps
// using mcrypt's default handing of CFB the above would output two different things. using phpseclib's
// rewritten CFB implementation the above outputs the same thing twice.
if ($this->mode == 'ncfb') {
if ($changed) {
$this->ecb = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
}
if ($this->mode != 'ctr') { if (strlen($this->enbuffer)) {
$ciphertext = $plaintext ^ substr($this->encryptIV, strlen($this->enbuffer));
$this->enbuffer.= $ciphertext;
if (strlen($this->enbuffer) == 16) {
$this->encryptIV = $this->enbuffer;
$this->enbuffer = '';
mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
}
$plaintext = substr($plaintext, strlen($ciphertext));
} else {
$ciphertext = '';
}
$last_pos = strlen($plaintext) & 0xFFFFFFF0;
$ciphertext.= $last_pos ? mcrypt_generic($this->enmcrypt, substr($plaintext, 0, $last_pos)) : '';
if (strlen($plaintext) & 0xF) {
if (strlen($ciphertext)) {
$this->encryptIV = substr($ciphertext, -16);
}
$this->encryptIV = mcrypt_generic($this->ecb, $this->encryptIV);
$this->enbuffer = substr($plaintext, $last_pos) ^ $this->encryptIV;
$ciphertext.= $this->enbuffer;
}
return $ciphertext;
}
if ($this->paddable) {
$plaintext = $this->_pad($plaintext); $plaintext = $this->_pad($plaintext);
} }
@ -276,6 +356,7 @@ class Crypt_AES extends Crypt_Rijndael {
function decrypt($ciphertext) function decrypt($ciphertext)
{ {
if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) { if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
$changed = $this->changed;
$this->_mcryptSetup(); $this->_mcryptSetup();
/* /*
if ($this->mode == CRYPT_AES_MODE_CTR) { if ($this->mode == CRYPT_AES_MODE_CTR) {
@ -288,8 +369,42 @@ class Crypt_AES extends Crypt_Rijndael {
return $plaintext; return $plaintext;
} }
*/ */
if ($this->mode == 'ncfb') {
if ($changed) {
$this->ecb = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
}
if ($this->mode != 'ctr') { if (strlen($this->debuffer)) {
$plaintext = $ciphertext ^ substr($this->decryptIV, strlen($this->debuffer));
$this->debuffer.= substr($ciphertext, 0, strlen($plaintext));
if (strlen($this->debuffer) == 16) {
$this->decryptIV = $this->debuffer;
$this->debuffer = '';
mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
}
$ciphertext = substr($ciphertext, strlen($plaintext));
} else {
$plaintext = '';
}
$last_pos = strlen($ciphertext) & 0xFFFFFFF0;
$plaintext.= $last_pos ? mdecrypt_generic($this->demcrypt, substr($ciphertext, 0, $last_pos)) : '';
if (strlen($ciphertext) & 0xF) {
if (strlen($plaintext)) {
$this->decryptIV = substr($ciphertext, $last_pos - 16, 16);
}
$this->decryptIV = mcrypt_generic($this->ecb, $this->decryptIV);
$this->debuffer = substr($ciphertext, $last_pos);
$plaintext.= $this->debuffer ^ $this->decryptIV;
}
return $plaintext;
}
if ($this->paddable) {
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : // 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." // "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)); $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 15) & 0xFFFFFFF0, chr(0));
@ -301,7 +416,7 @@ class Crypt_AES extends Crypt_Rijndael {
mcrypt_generic_init($this->demcrypt, $this->key, $this->iv); mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
} }
return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext; return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
} }
return parent::decrypt($ciphertext); return parent::decrypt($ciphertext);
@ -345,7 +460,7 @@ class Crypt_AES extends Crypt_Rijndael {
$this->key_size = 32; $this->key_size = 32;
} }
$this->key = substr($this->key, 0, $this->key_size); $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, chr(0));
$this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, 16), 16, chr(0)); $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, 16), 16, chr(0));
if (!isset($this->enmcrypt)) { if (!isset($this->enmcrypt)) {

View File

@ -33,26 +33,29 @@
* ?> * ?>
* </code> * </code>
* *
* LICENSE: This library is free software; you can redistribute it and/or * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
* modify it under the terms of the GNU Lesser General Public * of this software and associated documentation files (the "Software"), to deal
* License as published by the Free Software Foundation; either * in the Software without restriction, including without limitation the rights
* version 2.1 of the License, or (at your option) any later version. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* * copies of the Software, and to permit persons to whom the Software is
* This library is distributed in the hope that it will be useful, * furnished to do so, subject to the following conditions:
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * The above copyright notice and this permission notice shall be included in
* Lesser General Public License for more details. * all copies or substantial portions of the Software.
* *
* You should have received a copy of the GNU Lesser General Public * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* License along with this library; if not, write to the Free Software * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* MA 02111-1307 USA * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* @category Crypt * @category Crypt
* @package Crypt_DES * @package Crypt_DES
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVII Jim Wigginton * @copyright MMVII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version $Id$ * @version $Id$
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -97,6 +100,18 @@ define('CRYPT_DES_MODE_ECB', 1);
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
*/ */
define('CRYPT_DES_MODE_CBC', 2); define('CRYPT_DES_MODE_CBC', 2);
/**
* Encrypt / decrypt using the Cipher Feedback mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
*/
define('CRYPT_DES_MODE_CFB', 3);
/**
* Encrypt / decrypt using the Cipher Feedback mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
*/
define('CRYPT_DES_MODE_OFB', 4);
/**#@-*/ /**#@-*/
/**#@+ /**#@+
@ -191,7 +206,7 @@ class Crypt_DES {
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once. * 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. * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
* *
* @see Crypt_AES::encrypt() * @see Crypt_DES::encrypt()
* @var String * @var String
* @access private * @access private
*/ */
@ -203,21 +218,68 @@ class Crypt_DES {
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once. * 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. * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
* *
* @see Crypt_AES::decrypt() * @see Crypt_DES::decrypt()
* @var String * @var String
* @access private * @access private
*/ */
var $demcrypt; var $demcrypt;
/** /**
* Does the (en|de)mcrypt resource need to be (re)initialized? * Does the enmcrypt resource need to be (re)initialized?
* *
* @see setKey() * @see Crypt_DES::setKey()
* @see setIV() * @see Crypt_DES::setIV()
* @var Boolean * @var Boolean
* @access private * @access private
*/ */
var $changed = true; var $enchanged = true;
/**
* Does the demcrypt resource need to be (re)initialized?
*
* @see Crypt_DES::setKey()
* @see Crypt_DES::setIV()
* @var Boolean
* @access private
*/
var $dechanged = true;
/**
* Is the mode one that is paddable?
*
* @see Crypt_DES::Crypt_DES()
* @var Boolean
* @access private
*/
var $paddable = false;
/**
* Encryption buffer for CTR, OFB and CFB modes
*
* @see Crypt_DES::encrypt()
* @var String
* @access private
*/
var $enbuffer = '';
/**
* Decryption buffer for CTR, OFB and CFB modes
*
* @see Crypt_DES::decrypt()
* @var String
* @access private
*/
var $debuffer = '';
/**
* mcrypt resource for CFB mode
*
* @see Crypt_DES::encrypt()
* @see Crypt_DES::decrypt()
* @var String
* @access private
*/
var $ecb;
/** /**
* Default Constructor. * Default Constructor.
@ -248,14 +310,22 @@ class Crypt_DES {
case CRYPT_DES_MODE_MCRYPT: case CRYPT_DES_MODE_MCRYPT:
switch ($mode) { switch ($mode) {
case CRYPT_DES_MODE_ECB: case CRYPT_DES_MODE_ECB:
$this->paddable = true;
$this->mode = MCRYPT_MODE_ECB; $this->mode = MCRYPT_MODE_ECB;
break; break;
case CRYPT_DES_MODE_CTR: case CRYPT_DES_MODE_CTR:
$this->mode = 'ctr'; $this->mode = 'ctr';
//$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_DES_MODE_CTR; //$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_DES_MODE_CTR;
break; break;
case CRYPT_DES_MODE_CFB:
$this->mode = 'ncfb';
break;
case CRYPT_DES_MODE_OFB:
$this->mode = MCRYPT_MODE_NOFB;
break;
case CRYPT_DES_MODE_CBC: case CRYPT_DES_MODE_CBC:
default: default:
$this->paddable = true;
$this->mode = MCRYPT_MODE_CBC; $this->mode = MCRYPT_MODE_CBC;
} }
@ -263,11 +333,17 @@ class Crypt_DES {
default: default:
switch ($mode) { switch ($mode) {
case CRYPT_DES_MODE_ECB: case CRYPT_DES_MODE_ECB:
case CRYPT_DES_MODE_CTR:
case CRYPT_DES_MODE_CBC: case CRYPT_DES_MODE_CBC:
$this->paddable = true;
$this->mode = $mode;
break;
case CRYPT_DES_MODE_CTR:
case CRYPT_DES_MODE_CFB:
case CRYPT_DES_MODE_OFB:
$this->mode = $mode; $this->mode = $mode;
break; break;
default: default:
$this->paddable = true;
$this->mode = CRYPT_DES_MODE_CBC; $this->mode = CRYPT_DES_MODE_CBC;
} }
} }
@ -289,7 +365,7 @@ class Crypt_DES {
*/ */
function setKey($key) function setKey($key)
{ {
$this->keys = ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) ? substr($key, 0, 8) : $this->_prepareKey($key); $this->keys = ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) ? str_pad(substr($key, 0, 8), 8, chr(0)) : $this->_prepareKey($key);
$this->changed = true; $this->changed = true;
} }
@ -365,20 +441,55 @@ class Crypt_DES {
*/ */
function encrypt($plaintext) function encrypt($plaintext)
{ {
if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') { if ($this->paddable) {
$plaintext = $this->_pad($plaintext); $plaintext = $this->_pad($plaintext);
} }
if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
if ($this->changed) { if ($this->enchanged) {
if (!isset($this->enmcrypt)) { if (!isset($this->enmcrypt)) {
$this->enmcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, ''); $this->enmcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, '');
} }
mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV); mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV);
$this->changed = false; if ($this->mode != 'ncfb') {
$this->enchanged = false;
}
} }
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); if ($this->mode != 'ncfb') {
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
} else {
if ($this->enchanged) {
$this->ecb = mcrypt_module_open(MCRYPT_DES, '', MCRYPT_MODE_ECB, '');
mcrypt_generic_init($this->ecb, $this->keys, "\0\0\0\0\0\0\0\0");
$this->enchanged = false;
}
if (strlen($this->enbuffer)) {
$ciphertext = $plaintext ^ substr($this->encryptIV, strlen($this->enbuffer));
$this->enbuffer.= $ciphertext;
if (strlen($this->enbuffer) == 8) {
$this->encryptIV = $this->enbuffer;
$this->enbuffer = '';
mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV);
}
$plaintext = substr($plaintext, strlen($ciphertext));
} else {
$ciphertext = '';
}
$last_pos = strlen($plaintext) & 0xFFFFFFF8;
$ciphertext.= $last_pos ? mcrypt_generic($this->enmcrypt, substr($plaintext, 0, $last_pos)) : '';
if (strlen($plaintext) & 0x7) {
if (strlen($ciphertext)) {
$this->encryptIV = substr($ciphertext, -8);
}
$this->encryptIV = mcrypt_generic($this->ecb, $this->encryptIV);
$this->enbuffer = substr($plaintext, $last_pos) ^ $this->encryptIV;
$ciphertext.= $this->enbuffer;
}
}
if (!$this->continuousBuffer) { if (!$this->continuousBuffer) {
mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV); mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV);
@ -391,6 +502,8 @@ class Crypt_DES {
$this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0"); $this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0");
} }
$buffer = &$this->enbuffer;
$continuousBuffer = $this->continuousBuffer;
$ciphertext = ''; $ciphertext = '';
switch ($this->mode) { switch ($this->mode) {
case CRYPT_DES_MODE_ECB: case CRYPT_DES_MODE_ECB:
@ -412,13 +525,78 @@ class Crypt_DES {
break; break;
case CRYPT_DES_MODE_CTR: case CRYPT_DES_MODE_CTR:
$xor = $this->encryptIV; $xor = $this->encryptIV;
for ($i = 0; $i < strlen($plaintext); $i+=8) { if (strlen($buffer)) {
$block = substr($plaintext, $i, 8); for ($i = 0; $i < strlen($plaintext); $i+=8) {
$key = $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT); $block = substr($plaintext, $i, 8);
$ciphertext.= $block ^ $key; $buffer.= $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT);
$key = $this->_string_shift($buffer, 8);
$ciphertext.= $block ^ $key;
}
} else {
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) { if ($this->continuousBuffer) {
$this->encryptIV = $xor; $this->encryptIV = $xor;
if ($start = strlen($plaintext) & 7) {
$buffer = substr($key, $start) . $buffer;
}
}
break;
case CRYPT_DES_MODE_CFB:
if (!empty($buffer['xor'])) {
$ciphertext = $plaintext ^ $buffer['xor'];
$iv = $buffer['encrypted'] . $ciphertext;
$start = strlen($ciphertext);
$buffer['encrypted'].= $ciphertext;
$buffer['xor'] = substr($buffer['xor'], strlen($ciphertext));
} else {
$ciphertext = '';
$iv = $this->encryptIV;
$start = 0;
}
for ($i = $start; $i < strlen($plaintext); $i+=8) {
$block = substr($plaintext, $i, 8);
$xor = $this->_processBlock($iv, CRYPT_DES_ENCRYPT);
$iv = $block ^ $xor;
if ($continuousBuffer && strlen($iv) != 8) {
$buffer = array(
'encrypted' => $iv,
'xor' => substr($xor, strlen($iv))
);
}
$ciphertext.= $iv;
}
if ($this->continuousBuffer) {
$this->encryptIV = $iv;
}
break;
case CRYPT_DES_MODE_OFB:
$xor = $this->encryptIV;
if (strlen($buffer)) {
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT);
$buffer.= $xor;
$key = $this->_string_shift($buffer, 8);
$ciphertext.= substr($plaintext, $i, 8) ^ $key;
}
} else {
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT);
$ciphertext.= substr($plaintext, $i, 8) ^ $xor;
}
$key = $xor;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
if ($start = strlen($plaintext) & 7) {
$buffer = substr($key, $start) . $buffer;
}
} }
} }
@ -436,22 +614,60 @@ class Crypt_DES {
*/ */
function decrypt($ciphertext) function decrypt($ciphertext)
{ {
if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') { if ($this->paddable) {
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : // 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." // "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)); $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0));
} }
if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
if ($this->changed) { if ($this->dechanged) {
if (!isset($this->demcrypt)) { if (!isset($this->demcrypt)) {
$this->demcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, ''); $this->demcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, '');
} }
mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV); mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV);
$this->changed = false; if ($this->mode != 'ncfb') {
$this->dechanged = false;
}
} }
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); if ($this->mode != 'ncfb') {
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
} else {
if ($this->dechanged) {
$this->ecb = mcrypt_module_open(MCRYPT_DES, '', MCRYPT_MODE_ECB, '');
mcrypt_generic_init($this->ecb, $this->keys, "\0\0\0\0\0\0\0\0");
$this->dechanged = false;
}
if (strlen($this->debuffer)) {
$plaintext = $ciphertext ^ substr($this->decryptIV, strlen($this->debuffer));
$this->debuffer.= substr($ciphertext, 0, strlen($plaintext));
if (strlen($this->debuffer) == 8) {
$this->decryptIV = $this->debuffer;
$this->debuffer = '';
mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV);
}
$ciphertext = substr($ciphertext, strlen($plaintext));
} else {
$plaintext = '';
}
$last_pos = strlen($ciphertext) & 0xFFFFFFF8;
$plaintext.= $last_pos ? mdecrypt_generic($this->demcrypt, substr($ciphertext, 0, $last_pos)) : '';
if (strlen($ciphertext) & 0x7) {
if (strlen($plaintext)) {
$this->decryptIV = substr($ciphertext, $last_pos - 8, 8);
}
$this->decryptIV = mcrypt_generic($this->ecb, $this->decryptIV);
$this->debuffer = substr($ciphertext, $last_pos);
$plaintext.= $this->debuffer ^ $this->decryptIV;
}
return $plaintext;
}
if (!$this->continuousBuffer) { if (!$this->continuousBuffer) {
mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV); mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV);
@ -464,6 +680,8 @@ class Crypt_DES {
$this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0"); $this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0");
} }
$buffer = &$this->debuffer;
$continuousBuffer = $this->continuousBuffer;
$plaintext = ''; $plaintext = '';
switch ($this->mode) { switch ($this->mode) {
case CRYPT_DES_MODE_ECB: case CRYPT_DES_MODE_ECB:
@ -484,17 +702,82 @@ class Crypt_DES {
break; break;
case CRYPT_DES_MODE_CTR: case CRYPT_DES_MODE_CTR:
$xor = $this->decryptIV; $xor = $this->decryptIV;
for ($i = 0; $i < strlen($ciphertext); $i+=8) { if (strlen($buffer)) {
$block = substr($ciphertext, $i, 8); for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$key = $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT); $block = substr($ciphertext, $i, 8);
$plaintext.= $block ^ $key; $buffer.= $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT);
$key = $this->_string_shift($buffer, 8);
$plaintext.= $block ^ $key;
}
} else {
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) { if ($this->continuousBuffer) {
$this->decryptIV = $xor; $this->decryptIV = $xor;
if ($start = strlen($ciphertext) % 8) {
$buffer = substr($key, $start) . $buffer;
}
}
break;
case CRYPT_DES_MODE_CFB:
if (!empty($buffer['ciphertext'])) {
$plaintext = $ciphertext ^ substr($this->decryptIV, strlen($buffer['ciphertext']));
$buffer['ciphertext'].= substr($ciphertext, 0, strlen($plaintext));
if (strlen($buffer['ciphertext']) == 8) {
$xor = $this->_processBlock($buffer['ciphertext'], CRYPT_DES_ENCRYPT);
$buffer['ciphertext'] = '';
}
$start = strlen($plaintext);
$block = $this->decryptIV;
} else {
$plaintext = '';
$xor = $this->_processBlock($this->decryptIV, CRYPT_DES_ENCRYPT);
$start = 0;
}
for ($i = $start; $i < strlen($ciphertext); $i+=8) {
$block = substr($ciphertext, $i, 8);
$plaintext.= $block ^ $xor;
if ($continuousBuffer && strlen($block) != 8) {
$buffer['ciphertext'].= $block;
$block = $xor;
} else if (strlen($block) == 8) {
$xor = $this->_processBlock($block, CRYPT_DES_ENCRYPT);
}
}
if ($this->continuousBuffer) {
$this->decryptIV = $block;
}
break;
case CRYPT_DES_MODE_OFB:
$xor = $this->decryptIV;
if (strlen($buffer)) {
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT);
$buffer.= $xor;
$key = $this->_string_shift($buffer, 8);
$plaintext.= substr($ciphertext, $i, 8) ^ $key;
}
} else {
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$xor = $this->_processBlock($xor, CRYPT_DES_ENCRYPT);
$plaintext.= substr($ciphertext, $i, 8) ^ $xor;
}
$key = $xor;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
if ($start = strlen($ciphertext) % 8) {
$buffer = substr($key, $start) . $buffer;
}
} }
} }
return $this->mode != CRYPT_DES_MODE_CTR ? $this->_unpad($plaintext) : $plaintext; return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
} }
/** /**
@ -939,6 +1222,23 @@ class Crypt_DES {
return $temp; return $temp;
} }
/**
* String Shift
*
* Inspired by array_shift
*
* @param String $string
* @param optional Integer $index
* @return String
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
} }
// vim: ts=4:sw=4:et: // vim: ts=4:sw=4:et:

View File

@ -29,26 +29,29 @@
* ?> * ?>
* </code> * </code>
* *
* LICENSE: This library is free software; you can redistribute it and/or * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
* modify it under the terms of the GNU Lesser General Public * of this software and associated documentation files (the "Software"), to deal
* License as published by the Free Software Foundation; either * in the Software without restriction, including without limitation the rights
* version 2.1 of the License, or (at your option) any later version. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* * copies of the Software, and to permit persons to whom the Software is
* This library is distributed in the hope that it will be useful, * furnished to do so, subject to the following conditions:
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * The above copyright notice and this permission notice shall be included in
* Lesser General Public License for more details. * all copies or substantial portions of the Software.
* *
* You should have received a copy of the GNU Lesser General Public * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* License along with this library; if not, write to the Free Software * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* MA 02111-1307 USA * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* @category Crypt * @category Crypt
* @package Crypt_Hash * @package Crypt_Hash
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVII Jim Wigginton * @copyright MMVII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version $Id$ * @version $Id$
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -204,7 +207,8 @@ class Crypt_Hash {
switch ($hash) { switch ($hash) {
case 'md2': case 'md2':
$mode = CRYPT_HASH_MODE_INTERNAL; $mode = CRYPT_HASH_MODE == CRYPT_HASH_MODE_HASH && in_array('md2', hash_algos()) ?
CRYPT_HASH_MODE_HASH : CRYPT_HASH_MODE_INTERNAL;
break; break;
case 'sha384': case 'sha384':
case 'sha512': case 'sha512':
@ -236,6 +240,7 @@ class Crypt_Hash {
case 'md5-96': case 'md5-96':
$this->hash = 'md5'; $this->hash = 'md5';
return; return;
case 'md2':
case 'sha256': case 'sha256':
case 'sha384': case 'sha384':
case 'sha512': case 'sha512':
@ -303,7 +308,7 @@ class Crypt_Hash {
resultant L byte string as the actual key to HMAC." resultant L byte string as the actual key to HMAC."
-- http://tools.ietf.org/html/rfc2104#section-2 */ -- http://tools.ietf.org/html/rfc2104#section-2 */
$key = strlen($this->key) > $this->b ? call_user_func($this->$hash, $this->key) : $this->key; $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 $key = str_pad($key, $this->b, chr(0)); // step 1
$temp = $this->ipad ^ $key; // step 2 $temp = $this->ipad ^ $key; // step 2
@ -332,7 +337,7 @@ class Crypt_Hash {
/** /**
* Returns the hash length (in bytes) * Returns the hash length (in bytes)
* *
* @access private * @access public
* @return Integer * @return Integer
*/ */
function getLength() function getLength()
@ -404,7 +409,10 @@ class Crypt_Hash {
$l = chr(0); $l = chr(0);
for ($i = 0; $i < $length; $i+= 16) { for ($i = 0; $i < $length; $i+= 16) {
for ($j = 0; $j < 16; $j++) { for ($j = 0; $j < 16; $j++) {
$c[$j] = chr($s[ord($m[$i + $j] ^ $l)]); // 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]; $l = $c[$j];
} }
} }

View File

@ -35,26 +35,29 @@
* ?> * ?>
* </code> * </code>
* *
* LICENSE: This library is free software; you can redistribute it and/or * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
* modify it under the terms of the GNU Lesser General Public * of this software and associated documentation files (the "Software"), to deal
* License as published by the Free Software Foundation; either * in the Software without restriction, including without limitation the rights
* version 2.1 of the License, or (at your option) any later version. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* * copies of the Software, and to permit persons to whom the Software is
* This library is distributed in the hope that it will be useful, * furnished to do so, subject to the following conditions:
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * The above copyright notice and this permission notice shall be included in
* Lesser General Public License for more details. * all copies or substantial portions of the Software.
* *
* You should have received a copy of the GNU Lesser General Public * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* License along with this library; if not, write to the Free Software * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* MA 02111-1307 USA * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* @category Crypt * @category Crypt
* @package Crypt_RC4 * @package Crypt_RC4
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVII Jim Wigginton * @copyright MMVII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version $Id$ * @version $Id$
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -159,6 +162,15 @@ class Crypt_RC4 {
*/ */
var $mode; var $mode;
/**
* Continuous Buffer status
*
* @see Crypt_RC4::enableContinuousBuffer()
* @var Boolean
* @access private
*/
var $continuousBuffer = false;
/** /**
* Default Constructor. * Default Constructor.
* *

View File

@ -42,26 +42,29 @@
* ?> * ?>
* </code> * </code>
* *
* LICENSE: This library is free software; you can redistribute it and/or * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
* modify it under the terms of the GNU Lesser General Public * of this software and associated documentation files (the "Software"), to deal
* License as published by the Free Software Foundation; either * in the Software without restriction, including without limitation the rights
* version 2.1 of the License, or (at your option) any later version. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* * copies of the Software, and to permit persons to whom the Software is
* This library is distributed in the hope that it will be useful, * furnished to do so, subject to the following conditions:
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * The above copyright notice and this permission notice shall be included in
* Lesser General Public License for more details. * all copies or substantial portions of the Software.
* *
* You should have received a copy of the GNU Lesser General Public * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* License along with this library; if not, write to the Free Software * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* MA 02111-1307 USA * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* @category Crypt * @category Crypt
* @package Crypt_RSA * @package Crypt_RSA
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @copyright MMIX Jim Wigginton * @copyright MMIX Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version $Id$ * @version $Id$
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -170,6 +173,14 @@ define('CRYPT_RSA_MODE_OPENSSL', 2);
* Used by OpenSSH * Used by OpenSSH
*/ */
define('CRYPT_RSA_PRIVATE_FORMAT_PKCS1', 0); define('CRYPT_RSA_PRIVATE_FORMAT_PKCS1', 0);
/**
* PuTTY formatted private key
*/
define('CRYPT_RSA_PRIVATE_FORMAT_PUTTY', 1);
/**
* XML formatted private key
*/
define('CRYPT_RSA_PRIVATE_FORMAT_XML', 2);
/**#@-*/ /**#@-*/
/**#@+ /**#@+
@ -190,17 +201,21 @@ define('CRYPT_RSA_PRIVATE_FORMAT_PKCS1', 0);
* *
* 1, n, modulo, modulus * 1, n, modulo, modulus
*/ */
define('CRYPT_RSA_PUBLIC_FORMAT_RAW', 1); define('CRYPT_RSA_PUBLIC_FORMAT_RAW', 3);
/** /**
* PKCS#1 formatted public key * PKCS#1 formatted public key
*/ */
define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1', 2); define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1', 4);
/**
* XML formatted public key
*/
define('CRYPT_RSA_PUBLIC_FORMAT_XML', 5);
/** /**
* OpenSSH formatted public key * OpenSSH formatted public key
* *
* Place in $HOME/.ssh/authorized_keys * Place in $HOME/.ssh/authorized_keys
*/ */
define('CRYPT_RSA_PUBLIC_FORMAT_OPENSSH', 3); define('CRYPT_RSA_PUBLIC_FORMAT_OPENSSH', 6);
/**#@-*/ /**#@-*/
/** /**
@ -372,6 +387,30 @@ class Crypt_RSA {
*/ */
var $password = ''; var $password = '';
/**
* Components
*
* For use with parsing XML formatted keys. PHP's XML Parser functions use utilized - instead of PHP's DOM functions -
* because PHP's XML Parser functions work on PHP4 whereas PHP's DOM functions - although surperior - don't.
*
* @see Crypt_RSA::_start_element_handler()
* @var Array
* @access private
*/
var $components = array();
/**
* Current String
*
* For use with parsing XML formatted keys.
*
* @see Crypt_RSA::_character_handler()
* @see Crypt_RSA::_stop_element_handler()
* @var Mixed
* @access private
*/
var $current;
/** /**
* The constructor * The constructor
* *
@ -492,16 +531,16 @@ class Crypt_RSA {
$timeout-= time() - $start; $timeout-= time() - $start;
$start = time(); $start = time();
if ($timeout <= 0) { if ($timeout <= 0) {
return serialize(array( return array(
'privatekey' => '', 'privatekey' => '',
'publickey' => '', 'publickey' => '',
'partialkey' => array( 'partialkey' => serialize(array(
'primes' => $primes, 'primes' => $primes,
'coefficients' => $coefficients, 'coefficients' => $coefficients,
'lcm' => $lcm, 'lcm' => $lcm,
'exponents' => $exponents 'exponents' => $exponents
) ))
)); );
} }
} }
@ -516,15 +555,22 @@ class Crypt_RSA {
} }
if ($primes[$i] === false) { // if we've reached the timeout if ($primes[$i] === false) { // if we've reached the timeout
return array( if (count($primes) > 1) {
'privatekey' => '', $partialkey = '';
'publickey' => '', } else {
'partialkey' => empty($primes) ? '' : serialize(array( array_pop($primes);
'primes' => array_slice($primes, 0, $i - 1), $partialkey = serialize(array(
'primes' => $primes,
'coefficients' => $coefficients, 'coefficients' => $coefficients,
'lcm' => $lcm, 'lcm' => $lcm,
'exponents' => $exponents 'exponents' => $exponents
)) ));
}
return array(
'privatekey' => '',
'publickey' => '',
'partialkey' => $partialkey
); );
} }
@ -770,7 +816,7 @@ class Crypt_RSA {
* OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */ * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
$iv = pack('H*', trim($matches[2])); $iv = pack('H*', trim($matches[2]));
$symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
$symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8); $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
$ciphertext = preg_replace('#.+(\r|\n|\r\n)\1|[\r\n]|-.+-#s', '', $key); $ciphertext = preg_replace('#.+(\r|\n|\r\n)\1|[\r\n]|-.+-#s', '', $key);
$ciphertext = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $ciphertext) ? base64_decode($ciphertext) : false; $ciphertext = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $ciphertext) ? base64_decode($ciphertext) : false;
@ -778,6 +824,19 @@ class Crypt_RSA {
$ciphertext = $key; $ciphertext = $key;
} }
switch ($matches[1]) { switch ($matches[1]) {
case 'AES-128-CBC':
if (!class_exists('Crypt_AES')) {
require_once('Crypt/AES.php');
}
$symkey = substr($symkey, 0, 16);
$crypto = new Crypt_AES();
break;
case 'DES-EDE3-CFB':
if (!class_exists('Crypt_TripleDES')) {
require_once('Crypt/TripleDES.php');
}
$crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CFB);
break;
case 'DES-EDE3-CBC': case 'DES-EDE3-CBC':
if (!class_exists('Crypt_TripleDES')) { if (!class_exists('Crypt_TripleDES')) {
require_once('Crypt/TripleDES.php'); require_once('Crypt/TripleDES.php');
@ -927,9 +986,164 @@ class Crypt_RSA {
'publicExponent' => $publicExponent 'publicExponent' => $publicExponent
); );
} }
// http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
// http://en.wikipedia.org/wiki/XML_Signature
case CRYPT_RSA_PRIVATE_FORMAT_XML:
case CRYPT_RSA_PUBLIC_FORMAT_XML:
$this->components = array();
$xml = xml_parser_create('UTF-8');
xml_set_object($xml, $this);
xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler');
xml_set_character_data_handler($xml, '_data_handler');
if (!xml_parse($xml, $key)) {
return false;
}
return $this->components;
// from PuTTY's SSHPUBK.C
case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
$components = array();
$key = preg_split('#\r\n|\r|\n#', $key);
$type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
if ($type != 'ssh-rsa') {
return false;
}
$encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
$publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
$public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
$public = substr($public, 11);
extract(unpack('Nlength', $this->_string_shift($public, 4)));
$components['publicExponent'] = new Math_BigInteger($this->_string_shift($public, $length), -256);
extract(unpack('Nlength', $this->_string_shift($public, 4)));
$components['modulus'] = new Math_BigInteger($this->_string_shift($public, $length), -256);
$privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4]));
$private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength))));
switch ($encryption) {
case 'aes256-cbc':
if (!class_exists('Crypt_AES')) {
require_once('Crypt/AES.php');
}
$symkey = '';
$sequence = 0;
while (strlen($symkey) < 32) {
$temp = pack('Na*', $sequence++, $this->password);
$symkey.= pack('H*', sha1($temp));
}
$symkey = substr($symkey, 0, 32);
$crypto = new Crypt_AES();
}
if ($encryption != 'none') {
$crypto->setKey($symkey);
$crypto->disablePadding();
$private = $crypto->decrypt($private);
if ($private === false) {
return false;
}
}
extract(unpack('Nlength', $this->_string_shift($private, 4)));
$components['privateExponent'] = new Math_BigInteger($this->_string_shift($private, $length), -256);
extract(unpack('Nlength', $this->_string_shift($private, 4)));
$components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($private, $length), -256));
extract(unpack('Nlength', $this->_string_shift($private, 4)));
$components['primes'][] = new Math_BigInteger($this->_string_shift($private, $length), -256);
$temp = $components['primes'][1]->subtract($this->one);
$components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
$temp = $components['primes'][2]->subtract($this->one);
$components['exponents'][] = $components['publicExponent']->modInverse($temp);
extract(unpack('Nlength', $this->_string_shift($private, 4)));
$components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($private, $length), -256));
return $components;
} }
} }
/**
* Start Element Handler
*
* Called by xml_set_element_handler()
*
* @access private
* @param Resource $parser
* @param String $name
* @param Array $attribs
*/
function _start_element_handler($parser, $name, $attribs)
{
//$name = strtoupper($name);
switch ($name) {
case 'MODULUS':
$this->current = &$this->components['modulus'];
break;
case 'EXPONENT':
$this->current = &$this->components['publicExponent'];
break;
case 'P':
$this->current = &$this->components['primes'][1];
break;
case 'Q':
$this->current = &$this->components['primes'][2];
break;
case 'DP':
$this->current = &$this->components['exponents'][1];
break;
case 'DQ':
$this->current = &$this->components['exponents'][2];
break;
case 'INVERSEQ':
$this->current = &$this->components['coefficients'][2];
break;
case 'D':
$this->current = &$this->components['privateExponent'];
break;
default:
unset($this->current);
}
$this->current = '';
}
/**
* Stop Element Handler
*
* Called by xml_set_element_handler()
*
* @access private
* @param Resource $parser
* @param String $name
*/
function _stop_element_handler($parser, $name)
{
//$name = strtoupper($name);
if ($name == 'RSAKEYVALUE') {
return;
}
$this->current = new Math_BigInteger(base64_decode($this->current), 256);
}
/**
* Data Handler
*
* Called by xml_set_character_data_handler()
*
* @access private
* @param Resource $parser
* @param String $data
*/
function _data_handler($parser, $data)
{
if (!isset($this->current) || is_object($this->current)) {
return;
}
$this->current.= trim($data);
}
/** /**
* Loads a public or private key * Loads a public or private key
* *
@ -939,9 +1153,27 @@ class Crypt_RSA {
* @param String $key * @param String $key
* @param Integer $type optional * @param Integer $type optional
*/ */
function loadKey($key, $type = CRYPT_RSA_PRIVATE_FORMAT_PKCS1) function loadKey($key, $type = false)
{ {
$components = $this->_parseKey($key, $type); if ($type === false) {
$types = array(
CRYPT_RSA_PUBLIC_FORMAT_RAW,
CRYPT_RSA_PRIVATE_FORMAT_PKCS1,
CRYPT_RSA_PRIVATE_FORMAT_XML,
CRYPT_RSA_PRIVATE_FORMAT_PUTTY,
CRYPT_RSA_PUBLIC_FORMAT_OPENSSH
);
foreach ($types as $type) {
$components = $this->_parseKey($key, $type);
if ($components !== false) {
break;
}
}
} else {
$components = $this->_parseKey($key, $type);
}
if ($components === false) { if ($components === false) {
return false; return false;
} }
@ -1002,10 +1234,15 @@ class Crypt_RSA {
function setPublicKey($key, $type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1) function setPublicKey($key, $type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
{ {
$components = $this->_parseKey($key, $type); $components = $this->_parseKey($key, $type);
if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) { if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
user_error('Trying to load a public key? Use loadKey() instead. It\'s called loadKey() and not loadPrivateKey() for a reason.', E_USER_NOTICE);
return false; return false;
} }
$this->publicExponent = $components['publicExponent']; $this->publicExponent = $components['publicExponent'];
return true;
} }
/** /**
@ -1365,7 +1602,6 @@ class Crypt_RSA {
function _blind($x, $r, $i) function _blind($x, $r, $i)
{ {
$x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i])); $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
$x = $x->modPow($this->exponents[$i], $this->primes[$i]); $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
$r = $r->modInverse($this->primes[$i]); $r = $r->modInverse($this->primes[$i]);
@ -1662,6 +1898,7 @@ class Crypt_RSA {
$c = $this->_os2ip($c); $c = $this->_os2ip($c);
$m = $this->_rsadp($c); $m = $this->_rsadp($c);
if ($m === false) { if ($m === false) {
user_error('Decryption error', E_USER_NOTICE); user_error('Decryption error', E_USER_NOTICE);
return false; return false;
@ -1752,8 +1989,8 @@ class Crypt_RSA {
return false; return false;
} }
$maskedDB = substr($em, 0, $em - $this->hLen - 1); $maskedDB = substr($em, 0, -$this->hLen - 1);
$h = substr($em, $em - $this->hLen - 1, $this->hLen); $h = substr($em, -$this->hLen - 1, $this->hLen);
$temp = chr(0xFF << ($emBits & 7)); $temp = chr(0xFF << ($emBits & 7));
if ((~$maskedDB[0] & $temp) != $temp) { if ((~$maskedDB[0] & $temp) != $temp) {
return false; return false;

View File

@ -15,26 +15,29 @@
* ?> * ?>
* </code> * </code>
* *
* LICENSE: This library is free software; you can redistribute it and/or * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
* modify it under the terms of the GNU Lesser General Public * of this software and associated documentation files (the "Software"), to deal
* License as published by the Free Software Foundation; either * in the Software without restriction, including without limitation the rights
* version 2.1 of the License, or (at your option) any later version. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* * copies of the Software, and to permit persons to whom the Software is
* This library is distributed in the hope that it will be useful, * furnished to do so, subject to the following conditions:
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * The above copyright notice and this permission notice shall be included in
* Lesser General Public License for more details. * all copies or substantial portions of the Software.
* *
* You should have received a copy of the GNU Lesser General Public * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* License along with this library; if not, write to the Free Software * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* MA 02111-1307 USA * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* @category Crypt * @category Crypt
* @package Crypt_Random * @package Crypt_Random
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVII Jim Wigginton * @copyright MMVII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version $Id$ * @version $Id$
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -61,14 +64,14 @@ function crypt_random($min = 0, $max = 0x7FFFFFFF)
} }
// see http://en.wikipedia.org/wiki//dev/random // see http://en.wikipedia.org/wiki//dev/random
// if open_basedir is enabled file_exists() will ouput an "open_basedir restriction in effect" warning, static $urandom = true;
// so we suppress it. if ($urandom === true) {
if (@file_exists('/dev/urandom')) { // Warning's will be output unles the error suppression operator is used. Errors such as
static $fp; // "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc.
if (!$fp) { $urandom = @fopen('/dev/urandom', 'rb');
$fp = fopen('/dev/urandom', 'rb'); }
} if (!is_bool($urandom)) {
extract(unpack('Nrandom', fread($fp, 4))); extract(unpack('Nrandom', fread($urandom, 4)));
// say $min = 0 and $max = 3. if we didn't do abs() then we could have stuff like this: // say $min = 0 and $max = 3. if we didn't do abs() then we could have stuff like this:
// -4 % 3 + 0 = -1, even though -1 < $min // -4 % 3 + 0 = -1, even though -1 < $min

View File

@ -44,26 +44,29 @@
* ?> * ?>
* </code> * </code>
* *
* LICENSE: This library is free software; you can redistribute it and/or * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
* modify it under the terms of the GNU Lesser General Public * of this software and associated documentation files (the "Software"), to deal
* License as published by the Free Software Foundation; either * in the Software without restriction, including without limitation the rights
* version 2.1 of the License, or (at your option) any later version. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* * copies of the Software, and to permit persons to whom the Software is
* This library is distributed in the hope that it will be useful, * furnished to do so, subject to the following conditions:
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * The above copyright notice and this permission notice shall be included in
* Lesser General Public License for more details. * all copies or substantial portions of the Software.
* *
* You should have received a copy of the GNU Lesser General Public * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* License along with this library; if not, write to the Free Software * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* MA 02111-1307 USA * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* @category Crypt * @category Crypt
* @package Crypt_Rijndael * @package Crypt_Rijndael
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVIII Jim Wigginton * @copyright MMVIII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version $Id$ * @version $Id$
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -93,6 +96,18 @@ define('CRYPT_RIJNDAEL_MODE_ECB', 1);
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29 * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
*/ */
define('CRYPT_RIJNDAEL_MODE_CBC', 2); define('CRYPT_RIJNDAEL_MODE_CBC', 2);
/**
* Encrypt / decrypt using the Cipher Feedback mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
*/
define('CRYPT_RIJNDAEL_MODE_CFB', 3);
/**
* Encrypt / decrypt using the Cipher Feedback mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
*/
define('CRYPT_RIJNDAEL_MODE_OFB', 4);
/**#@-*/ /**#@-*/
/**#@+ /**#@+
@ -356,6 +371,33 @@ class Crypt_Rijndael {
*/ */
var $dt3; var $dt3;
/**
* Is the mode one that is paddable?
*
* @see Crypt_Rijndael::Crypt_Rijndael()
* @var Boolean
* @access private
*/
var $paddable = false;
/**
* Encryption buffer for CTR, OFB and CFB modes
*
* @see Crypt_Rijndael::encrypt()
* @var String
* @access private
*/
var $enbuffer = array('encrypted' => '', 'xor' => '');
/**
* Decryption buffer for CTR, OFB and CFB modes
*
* @see Crypt_Rijndael::decrypt()
* @var String
* @access private
*/
var $debuffer = array('ciphertext' => '');
/** /**
* Default Constructor. * Default Constructor.
* *
@ -371,10 +413,16 @@ class Crypt_Rijndael {
switch ($mode) { switch ($mode) {
case CRYPT_RIJNDAEL_MODE_ECB: case CRYPT_RIJNDAEL_MODE_ECB:
case CRYPT_RIJNDAEL_MODE_CBC: case CRYPT_RIJNDAEL_MODE_CBC:
$this->paddable = true;
$this->mode = $mode;
break;
case CRYPT_RIJNDAEL_MODE_CTR: case CRYPT_RIJNDAEL_MODE_CTR:
case CRYPT_RIJNDAEL_MODE_CFB:
case CRYPT_RIJNDAEL_MODE_OFB:
$this->mode = $mode; $this->mode = $mode;
break; break;
default: default:
$this->paddable = true;
$this->mode = CRYPT_RIJNDAEL_MODE_CBC; $this->mode = CRYPT_RIJNDAEL_MODE_CBC;
} }
@ -611,11 +659,13 @@ class Crypt_Rijndael {
function encrypt($plaintext) function encrypt($plaintext)
{ {
$this->_setup(); $this->_setup();
if ($this->mode != CRYPT_RIJNDAEL_MODE_CTR) { if ($this->paddable) {
$plaintext = $this->_pad($plaintext); $plaintext = $this->_pad($plaintext);
} }
$block_size = $this->block_size; $block_size = $this->block_size;
$buffer = &$this->enbuffer;
$continuousBuffer = $this->continuousBuffer;
$ciphertext = ''; $ciphertext = '';
switch ($this->mode) { switch ($this->mode) {
case CRYPT_RIJNDAEL_MODE_ECB: case CRYPT_RIJNDAEL_MODE_ECB:
@ -637,13 +687,78 @@ class Crypt_Rijndael {
break; break;
case CRYPT_RIJNDAEL_MODE_CTR: case CRYPT_RIJNDAEL_MODE_CTR:
$xor = $this->encryptIV; $xor = $this->encryptIV;
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) { if (!empty($buffer)) {
$block = substr($plaintext, $i, $block_size); for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$key = $this->_encryptBlock($this->_generate_xor($block_size, $xor)); $block = substr($plaintext, $i, $block_size);
$ciphertext.= $block ^ $key; $buffer.= $this->_encryptBlock($this->_generate_xor($block_size, $xor));
$key = $this->_string_shift($buffer, $block_size);
$ciphertext.= $block ^ $key;
}
} else {
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) { if ($this->continuousBuffer) {
$this->encryptIV = $xor; $this->encryptIV = $xor;
if ($start = strlen($plaintext) % $block_size) {
$buffer = substr($key, $start) . $buffer;
}
}
break;
case CRYPT_RIJNDAEL_MODE_CFB:
if (!empty($buffer['xor'])) {
$ciphertext = $plaintext ^ $buffer['xor'];
$iv = $buffer['encrypted'] . $ciphertext;
$start = strlen($ciphertext);
$buffer['encrypted'].= $ciphertext;
$buffer['xor'] = substr($buffer['xor'], strlen($ciphertext));
} else {
$ciphertext = '';
$iv = $this->encryptIV;
$start = 0;
}
for ($i = $start; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
$xor = $this->_encryptBlock($iv);
$iv = $block ^ $xor;
if ($continuousBuffer && strlen($iv) != $block_size) {
$buffer = array(
'encrypted' => $iv,
'xor' => substr($xor, strlen($iv))
);
}
$ciphertext.= $iv;
}
if ($this->continuousBuffer) {
$this->encryptIV = $iv;
}
break;
case CRYPT_RIJNDAEL_MODE_OFB:
$xor = $this->encryptIV;
if (strlen($buffer)) {
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$xor = $this->_encryptBlock($xor);
$buffer.= $xor;
$key = $this->_string_shift($buffer, $block_size);
$ciphertext.= substr($plaintext, $i, $block_size) ^ $key;
}
} else {
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$xor = $this->_encryptBlock($xor);
$ciphertext.= substr($plaintext, $i, $block_size) ^ $xor;
}
$key = $xor;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
if ($start = strlen($plaintext) % $block_size) {
$buffer = substr($key, $start) . $buffer;
}
} }
} }
@ -664,13 +779,15 @@ class Crypt_Rijndael {
{ {
$this->_setup(); $this->_setup();
if ($this->mode != CRYPT_RIJNDAEL_MODE_CTR) { if ($this->paddable) {
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : // 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." // "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)); $ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($this->block_size - strlen($ciphertext) % $this->block_size) % $this->block_size, chr(0));
} }
$block_size = $this->block_size; $block_size = $this->block_size;
$buffer = &$this->debuffer;
$continuousBuffer = $this->continuousBuffer;
$plaintext = ''; $plaintext = '';
switch ($this->mode) { switch ($this->mode) {
case CRYPT_RIJNDAEL_MODE_ECB: case CRYPT_RIJNDAEL_MODE_ECB:
@ -691,17 +808,82 @@ class Crypt_Rijndael {
break; break;
case CRYPT_RIJNDAEL_MODE_CTR: case CRYPT_RIJNDAEL_MODE_CTR:
$xor = $this->decryptIV; $xor = $this->decryptIV;
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) { if (strlen($buffer)) {
$block = substr($ciphertext, $i, $block_size); for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
$key = $this->_encryptBlock($this->_generate_xor($block_size, $xor)); $block = substr($ciphertext, $i, $block_size);
$plaintext.= $block ^ $key; $buffer.= $this->_encryptBlock($this->_generate_xor($block_size, $xor));
$key = $this->_string_shift($buffer, $block_size);
$plaintext.= $block ^ $key;
}
} else {
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) { if ($this->continuousBuffer) {
$this->decryptIV = $xor; $this->decryptIV = $xor;
if ($start = strlen($ciphertext) % $block_size) {
$buffer = substr($key, $start) . $buffer;
}
}
break;
case CRYPT_RIJNDAEL_MODE_CFB:
if (!empty($buffer['ciphertext'])) {
$plaintext = $ciphertext ^ substr($this->decryptIV, strlen($buffer['ciphertext']));
$buffer['ciphertext'].= substr($ciphertext, 0, strlen($plaintext));
if (strlen($buffer['ciphertext']) == $block_size) {
$xor = $this->_encryptBlock($buffer['ciphertext']);
$buffer['ciphertext'] = '';
}
$start = strlen($plaintext);
$block = $this->decryptIV;
} else {
$plaintext = '';
$xor = $this->_encryptBlock($this->decryptIV);
$start = 0;
}
for ($i = $start; $i < strlen($ciphertext); $i+=$block_size) {
$block = substr($ciphertext, $i, $block_size);
$plaintext.= $block ^ $xor;
if ($continuousBuffer && strlen($block) != $block_size) {
$buffer['ciphertext'].= $block;
$block = $xor;
} else if (strlen($block) == $block_size) {
$xor = $this->_encryptBlock($block);
}
}
if ($this->continuousBuffer) {
$this->decryptIV = $block;
}
break;
case CRYPT_RIJNDAEL_MODE_OFB:
$xor = $this->decryptIV;
if (strlen($buffer)) {
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
$xor = $this->_encryptBlock($xor);
$buffer.= $xor;
$key = $this->_string_shift($buffer, $block_size);
$plaintext.= substr($ciphertext, $i, $block_size) ^ $key;
}
} else {
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
$xor = $this->_encryptBlock($xor);
$plaintext.= substr($ciphertext, $i, $block_size) ^ $xor;
}
$key = $xor;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
if ($start = strlen($ciphertext) % $block_size) {
$buffer = substr($key, $start) . $buffer;
}
} }
} }
return $this->mode != CRYPT_RIJNDAEL_MODE_CTR ? $this->_unpad($plaintext) : $plaintext; return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
} }
/** /**

View File

@ -27,26 +27,29 @@
* ?> * ?>
* </code> * </code>
* *
* LICENSE: This library is free software; you can redistribute it and/or * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
* modify it under the terms of the GNU Lesser General Public * of this software and associated documentation files (the "Software"), to deal
* License as published by the Free Software Foundation; either * in the Software without restriction, including without limitation the rights
* version 2.1 of the License, or (at your option) any later version. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* * copies of the Software, and to permit persons to whom the Software is
* This library is distributed in the hope that it will be useful, * furnished to do so, subject to the following conditions:
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * The above copyright notice and this permission notice shall be included in
* Lesser General Public License for more details. * all copies or substantial portions of the Software.
* *
* You should have received a copy of the GNU Lesser General Public * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* License along with this library; if not, write to the Free Software * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* MA 02111-1307 USA * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* @category Crypt * @category Crypt
* @package Crypt_TripleDES * @package Crypt_TripleDES
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVII Jim Wigginton * @copyright MMVII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version $Id$ * @version $Id$
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -54,14 +57,14 @@
/** /**
* Include Crypt_DES * Include Crypt_DES
*/ */
require_once 'DES.php'; require_once('DES.php');
/** /**
* Encrypt / decrypt using inner chaining * Encrypt / decrypt using inner chaining
* *
* Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (CRYPT_DES_MODE_CBC3). * Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (CRYPT_DES_MODE_CBC3).
*/ */
define('CRYPT_DES_MODE_3CBC', 3); define('CRYPT_DES_MODE_3CBC', -2);
/** /**
* Encrypt / decrypt using outer chaining * Encrypt / decrypt using outer chaining
@ -156,7 +159,7 @@ class Crypt_TripleDES {
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once. * 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. * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
* *
* @see Crypt_AES::encrypt() * @see Crypt_TripleDES::encrypt()
* @var String * @var String
* @access private * @access private
*/ */
@ -168,21 +171,68 @@ class Crypt_TripleDES {
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once. * 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. * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
* *
* @see Crypt_AES::decrypt() * @see Crypt_TripleDES::decrypt()
* @var String * @var String
* @access private * @access private
*/ */
var $demcrypt; var $demcrypt;
/** /**
* Does the (en|de)mcrypt resource need to be (re)initialized? * Does the enmcrypt resource need to be (re)initialized?
* *
* @see setKey() * @see Crypt_TripleDES::setKey()
* @see setIV() * @see Crypt_TripleDES::setIV()
* @var Boolean * @var Boolean
* @access private * @access private
*/ */
var $changed = true; var $enchanged = true;
/**
* Does the demcrypt resource need to be (re)initialized?
*
* @see Crypt_TripleDES::setKey()
* @see Crypt_TripleDES::setIV()
* @var Boolean
* @access private
*/
var $dechanged = true;
/**
* Is the mode one that is paddable?
*
* @see Crypt_TripleDES::Crypt_TripleDES()
* @var Boolean
* @access private
*/
var $paddable = false;
/**
* Encryption buffer for CTR, OFB and CFB modes
*
* @see Crypt_TripleDES::encrypt()
* @var String
* @access private
*/
var $enbuffer = '';
/**
* Decryption buffer for CTR, OFB and CFB modes
*
* @see Crypt_TripleDES::decrypt()
* @var String
* @access private
*/
var $debuffer = '';
/**
* mcrypt resource for CFB mode
*
* @see Crypt_TripleDES::encrypt()
* @see Crypt_TripleDES::decrypt()
* @var String
* @access private
*/
var $ecb;
/** /**
* Default Constructor. * Default Constructor.
@ -229,13 +279,21 @@ class Crypt_TripleDES {
case CRYPT_DES_MODE_MCRYPT: case CRYPT_DES_MODE_MCRYPT:
switch ($mode) { switch ($mode) {
case CRYPT_DES_MODE_ECB: case CRYPT_DES_MODE_ECB:
$this->paddable = true;
$this->mode = MCRYPT_MODE_ECB; $this->mode = MCRYPT_MODE_ECB;
break; break;
case CRYPT_DES_MODE_CTR: case CRYPT_DES_MODE_CTR:
$this->mode = 'ctr'; $this->mode = 'ctr';
break; break;
case CRYPT_DES_MODE_CFB:
$this->mode = 'ncfb';
break;
case CRYPT_DES_MODE_OFB:
$this->mode = MCRYPT_MODE_NOFB;
break;
case CRYPT_DES_MODE_CBC: case CRYPT_DES_MODE_CBC:
default: default:
$this->paddable = true;
$this->mode = MCRYPT_MODE_CBC; $this->mode = MCRYPT_MODE_CBC;
} }
@ -254,11 +312,17 @@ class Crypt_TripleDES {
switch ($mode) { switch ($mode) {
case CRYPT_DES_MODE_ECB: case CRYPT_DES_MODE_ECB:
case CRYPT_DES_MODE_CTR:
case CRYPT_DES_MODE_CBC: case CRYPT_DES_MODE_CBC:
$this->paddable = true;
$this->mode = $mode;
break;
case CRYPT_DES_MODE_CTR:
case CRYPT_DES_MODE_CFB:
case CRYPT_DES_MODE_OFB:
$this->mode = $mode; $this->mode = $mode;
break; break;
default: default:
$this->paddable = true;
$this->mode = CRYPT_DES_MODE_CBC; $this->mode = CRYPT_DES_MODE_CBC;
} }
} }
@ -285,6 +349,8 @@ class Crypt_TripleDES {
// if $key is between 64 and 128-bits, use the first 64-bits as the last, per this: // if $key is between 64 and 128-bits, use the first 64-bits as the last, per this:
// http://php.net/function.mcrypt-encrypt#47973 // http://php.net/function.mcrypt-encrypt#47973
//$key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24); //$key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24);
} else {
$key = str_pad($key, 8, chr(0));
} }
$this->key = $key; $this->key = $key;
switch (true) { switch (true) {
@ -294,7 +360,7 @@ class Crypt_TripleDES {
$this->des[1]->setKey(substr($key, 8, 8)); $this->des[1]->setKey(substr($key, 8, 8));
$this->des[2]->setKey(substr($key, 16, 8)); $this->des[2]->setKey(substr($key, 16, 8));
} }
$this->changed = true; $this->enchanged = $this->dechanged = true;
} }
/** /**
@ -314,7 +380,7 @@ class Crypt_TripleDES {
$this->des[1]->setIV($iv); $this->des[1]->setIV($iv);
$this->des[2]->setIV($iv); $this->des[2]->setIV($iv);
} }
$this->changed = true; $this->enchanged = $this->dechanged = true;
} }
/** /**
@ -323,9 +389,9 @@ class Crypt_TripleDES {
* Encrypt the output of this and XOR it against the ciphertext / plaintext to get the * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
* plaintext / ciphertext in CTR mode. * plaintext / ciphertext in CTR mode.
* *
* @see Crypt_DES::decrypt() * @see Crypt_TripleDES::decrypt()
* @see Crypt_DES::encrypt() * @see Crypt_TripleDES::encrypt()
* @access public * @access private
* @param Integer $length * @param Integer $length
* @param String $iv * @param String $iv
*/ */
@ -363,7 +429,7 @@ class Crypt_TripleDES {
*/ */
function encrypt($plaintext) function encrypt($plaintext)
{ {
if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') { if ($this->paddable) {
$plaintext = $this->_pad($plaintext); $plaintext = $this->_pad($plaintext);
} }
@ -375,15 +441,50 @@ class Crypt_TripleDES {
} }
if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
if ($this->changed) { if ($this->enchanged) {
if (!isset($this->enmcrypt)) { if (!isset($this->enmcrypt)) {
$this->enmcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, ''); $this->enmcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
} }
mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
$this->changed = false; if ($this->mode != 'ncfb') {
$this->enchanged = false;
}
} }
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); if ($this->mode != 'ncfb') {
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
} else {
if ($this->enchanged) {
$this->ecb = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, '');
mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0");
$this->enchanged = false;
}
if (strlen($this->enbuffer)) {
$ciphertext = $plaintext ^ substr($this->encryptIV, strlen($this->enbuffer));
$this->enbuffer.= $ciphertext;
if (strlen($this->enbuffer) == 8) {
$this->encryptIV = $this->enbuffer;
$this->enbuffer = '';
mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
}
$plaintext = substr($plaintext, strlen($ciphertext));
} else {
$ciphertext = '';
}
$last_pos = strlen($plaintext) & 0xFFFFFFF8;
$ciphertext.= $last_pos ? mcrypt_generic($this->enmcrypt, substr($plaintext, 0, $last_pos)) : '';
if (strlen($plaintext) & 0x7) {
if (strlen($ciphertext)) {
$this->encryptIV = substr($ciphertext, -8);
}
$this->encryptIV = mcrypt_generic($this->ecb, $this->encryptIV);
$this->enbuffer = substr($plaintext, $last_pos) ^ $this->encryptIV;
$ciphertext.= $this->enbuffer;
}
}
if (!$this->continuousBuffer) { if (!$this->continuousBuffer) {
mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
@ -398,17 +499,21 @@ class Crypt_TripleDES {
return $this->des[0]->encrypt($plaintext); return $this->des[0]->encrypt($plaintext);
} }
// 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."
$plaintext = str_pad($plaintext, ceil(strlen($plaintext) / 8) * 8, chr(0));
$des = $this->des; $des = $this->des;
$buffer = &$this->enbuffer;
$continuousBuffer = $this->continuousBuffer;
$ciphertext = ''; $ciphertext = '';
switch ($this->mode) { switch ($this->mode) {
case CRYPT_DES_MODE_ECB: case CRYPT_DES_MODE_ECB:
for ($i = 0; $i < strlen($plaintext); $i+=8) { for ($i = 0; $i < strlen($plaintext); $i+=8) {
$block = substr($plaintext, $i, 8); $block = substr($plaintext, $i, 8);
// all of these _processBlock calls could, in theory, be put in a function - say Crypt_TripleDES::_ede_encrypt() or something.
// only problem with that: it would slow encryption and decryption down. $this->des would have to be called every time that
// function is called, instead of once for the whole string of text that's being encrypted, which would, in turn, make
// encryption and decryption take more time, per this:
//
// http://blog.libssh2.org/index.php?/archives/21-Compiled-Variables.html
$block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT); $block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
$block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT); $block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT);
$block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT); $block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT);
@ -431,16 +536,92 @@ class Crypt_TripleDES {
break; break;
case CRYPT_DES_MODE_CTR: case CRYPT_DES_MODE_CTR:
$xor = $this->encryptIV; $xor = $this->encryptIV;
for ($i = 0; $i < strlen($plaintext); $i+=8) { if (strlen($buffer)) {
$key = $this->_generate_xor(8, $xor); for ($i = 0; $i < strlen($plaintext); $i+=8) {
$key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT); $block = substr($plaintext, $i, 8);
$key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT); $key = $this->_generate_xor(8, $xor);
$key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
$block = substr($plaintext, $i, 8); $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
$ciphertext.= $block ^ $key; $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
$buffer.= $key;
$key = $this->_string_shift($buffer, 8);
$ciphertext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$block = substr($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);
$ciphertext.= $block ^ $key;
}
} }
if ($this->continuousBuffer) { if ($this->continuousBuffer) {
$this->encryptIV = $xor; $this->encryptIV = $xor;
if ($start = strlen($plaintext) & 7) {
$buffer = substr($key, $start) . $buffer;
}
}
break;
case CRYPT_DES_MODE_CFB:
if (!empty($buffer['xor'])) {
$ciphertext = $plaintext ^ $buffer['xor'];
$iv = $buffer['encrypted'] . $ciphertext;
$start = strlen($ciphertext);
$buffer['encrypted'].= $ciphertext;
$buffer['xor'] = substr($buffer['xor'], strlen($ciphertext));
} else {
$ciphertext = '';
$iv = $this->encryptIV;
$start = 0;
}
for ($i = $start; $i < strlen($plaintext); $i+=8) {
$block = substr($plaintext, $i, 8);
$iv = $des[0]->_processBlock($iv, CRYPT_DES_ENCRYPT);
$iv = $des[1]->_processBlock($iv, CRYPT_DES_DECRYPT);
$xor= $des[2]->_processBlock($iv, CRYPT_DES_ENCRYPT);
$iv = $block ^ $xor;
if ($continuousBuffer && strlen($iv) != 8) {
$buffer = array(
'encrypted' => $iv,
'xor' => substr($xor, strlen($iv))
);
}
$ciphertext.= $iv;
}
if ($this->continuousBuffer) {
$this->encryptIV = $iv;
}
break;
case CRYPT_DES_MODE_OFB:
$xor = $this->encryptIV;
if (strlen($buffer)) {
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
$xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$buffer.= $xor;
$key = $this->_string_shift($buffer, 8);
$ciphertext.= substr($plaintext, $i, 8) ^ $key;
}
} else {
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
$xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$ciphertext.= substr($plaintext, $i, 8) ^ $xor;
}
$key = $xor;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
if ($start = strlen($plaintext) & 7) {
$buffer = substr($key, $start) . $buffer;
}
} }
} }
@ -461,36 +642,78 @@ class Crypt_TripleDES {
return $this->_unpad($plaintext); return $this->_unpad($plaintext);
} }
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : if ($this->paddable) {
// "The data is padded with "\0" to make sure the length of the data is n * blocksize." // we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
$ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0)); // "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 ) { if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
if ($this->changed) { if ($this->dechanged) {
if (!isset($this->demcrypt)) { if (!isset($this->demcrypt)) {
$this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, ''); $this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
} }
mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
$this->changed = false; if ($this->mode != 'ncfb') {
$this->dechanged = false;
}
} }
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); if ($this->mode != 'ncfb') {
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
} else {
if ($this->dechanged) {
$this->ecb = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, '');
mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0");
$this->dechanged = false;
}
if (strlen($this->debuffer)) {
$plaintext = $ciphertext ^ substr($this->decryptIV, strlen($this->debuffer));
$this->debuffer.= substr($ciphertext, 0, strlen($plaintext));
if (strlen($this->debuffer) == 8) {
$this->decryptIV = $this->debuffer;
$this->debuffer = '';
mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
}
$ciphertext = substr($ciphertext, strlen($plaintext));
} else {
$plaintext = '';
}
$last_pos = strlen($ciphertext) & 0xFFFFFFF8;
$plaintext.= $last_pos ? mdecrypt_generic($this->demcrypt, substr($ciphertext, 0, $last_pos)) : '';
if (strlen($ciphertext) & 0x7) {
if (strlen($plaintext)) {
$this->decryptIV = substr($ciphertext, $last_pos - 8, 8);
}
$this->decryptIV = mcrypt_generic($this->ecb, $this->decryptIV);
$this->debuffer = substr($ciphertext, $last_pos);
$plaintext.= $this->debuffer ^ $this->decryptIV;
}
return $plaintext;
}
if (!$this->continuousBuffer) { if (!$this->continuousBuffer) {
mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
} }
return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext; return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
} }
if (strlen($this->key) <= 8) { if (strlen($this->key) <= 8) {
$this->des[0]->mode = $this->mode; $this->des[0]->mode = $this->mode;
$plaintext = $this->des[0]->decrypt($ciphertext);
return $this->_unpad($this->des[0]->decrypt($plaintext)); return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
} }
$des = $this->des; $des = $this->des;
$buffer = &$this->enbuffer;
$continuousBuffer = $this->continuousBuffer;
$plaintext = ''; $plaintext = '';
switch ($this->mode) { switch ($this->mode) {
case CRYPT_DES_MODE_ECB: case CRYPT_DES_MODE_ECB:
@ -518,20 +741,99 @@ class Crypt_TripleDES {
break; break;
case CRYPT_DES_MODE_CTR: case CRYPT_DES_MODE_CTR:
$xor = $this->decryptIV; $xor = $this->decryptIV;
for ($i = 0; $i < strlen($ciphertext); $i+=8) { if (strlen($buffer)) {
$key = $this->_generate_xor(8, $xor); for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT); $block = substr($ciphertext, $i, 8);
$key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT); $key = $this->_generate_xor(8, $xor);
$key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
$block = substr($ciphertext, $i, 8); $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
$plaintext.= $block ^ $key; $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
$buffer.= $key;
$key = $this->_string_shift($buffer, 8);
$plaintext.= $block ^ $key;
}
} else {
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$block = substr($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);
$plaintext.= $block ^ $key;
}
} }
if ($this->continuousBuffer) { if ($this->continuousBuffer) {
$this->decryptIV = $xor; $this->decryptIV = $xor;
if ($start = strlen($plaintext) & 7) {
$buffer = substr($key, $start) . $buffer;
}
}
break;
case CRYPT_DES_MODE_CFB:
if (!empty($buffer['ciphertext'])) {
$plaintext = $ciphertext ^ substr($this->decryptIV, strlen($buffer['ciphertext']));
$buffer['ciphertext'].= substr($ciphertext, 0, strlen($plaintext));
if (strlen($buffer['ciphertext']) == 8) {
$xor = $des[0]->_processBlock($buffer['ciphertext'], CRYPT_DES_ENCRYPT);
$xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
$xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$buffer['ciphertext'] = '';
}
$start = strlen($plaintext);
$block = $this->decryptIV;
} else {
$plaintext = '';
$xor = $des[0]->_processBlock($this->decryptIV, CRYPT_DES_ENCRYPT);
$xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
$xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$start = 0;
}
for ($i = $start; $i < strlen($ciphertext); $i+=8) {
$block = substr($ciphertext, $i, 8);
$plaintext.= $block ^ $xor;
if ($continuousBuffer && strlen($block) != 8) {
$buffer['ciphertext'].= $block;
$block = $xor;
} else if (strlen($block) == 8) {
$xor = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
$xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
$xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
}
}
if ($this->continuousBuffer) {
$this->decryptIV = $block;
}
break;
case CRYPT_DES_MODE_OFB:
$xor = $this->decryptIV;
if (strlen($buffer)) {
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
$xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$buffer.= $xor;
$key = $this->_string_shift($buffer, 8);
$plaintext.= substr($ciphertext, $i, 8) ^ $key;
}
} else {
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
$xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$plaintext.= substr($ciphertext, $i, 8) ^ $xor;
}
$key = $xor;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
if ($start = strlen($ciphertext) & 7) {
$buffer = substr($key, $start) . $buffer;
}
} }
} }
return $this->mode != CRYPT_DES_MODE_CTR ? $this->_unpad($plaintext) : $plaintext; return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
} }
/** /**
@ -684,6 +986,23 @@ class Crypt_TripleDES {
return substr($text, 0, -$length); return substr($text, 0, -$length);
} }
/**
* String Shift
*
* Inspired by array_shift
*
* @param String $string
* @param optional Integer $index
* @return String
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
} }
// vim: ts=4:sw=4:et: // vim: ts=4:sw=4:et:

View File

@ -47,26 +47,29 @@
* ?> * ?>
* </code> * </code>
* *
* LICENSE: This library is free software; you can redistribute it and/or * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
* modify it under the terms of the GNU Lesser General Public * of this software and associated documentation files (the "Software"), to deal
* License as published by the Free Software Foundation; either * in the Software without restriction, including without limitation the rights
* version 2.1 of the License, or (at your option) any later version. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* * copies of the Software, and to permit persons to whom the Software is
* This library is distributed in the hope that it will be useful, * furnished to do so, subject to the following conditions:
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * The above copyright notice and this permission notice shall be included in
* Lesser General Public License for more details. * all copies or substantial portions of the Software.
* *
* You should have received a copy of the GNU Lesser General Public * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* License along with this library; if not, write to the Free Software * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* MA 02111-1307 USA * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* @category Math * @category Math
* @package Math_BigInteger * @package Math_BigInteger
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVI Jim Wigginton * @copyright MMVI Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version $Id$ * @version $Id$
* @link http://pear.php.net/package/Math_BigInteger * @link http://pear.php.net/package/Math_BigInteger
*/ */
@ -596,9 +599,12 @@ class Math_BigInteger {
{ {
$hex = $this->toHex($twos_compliment); $hex = $this->toHex($twos_compliment);
$bits = ''; $bits = '';
for ($i = 0; $i < strlen($hex); $i+=8) { for ($i = 0, $end = strlen($hex) & 0xFFFFFFF8; $i < $end; $i+=8) {
$bits.= str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT); $bits.= str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT);
} }
if ($end != strlen($hex)) { // hexdec('') == 0
$bits.= str_pad(decbin(hexdec(substr($hex, $end))), strlen($hex) & 7, '0', STR_PAD_LEFT);
}
return $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0'); return $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');
} }

View File

@ -28,27 +28,29 @@
* ?> * ?>
* </code> * </code>
* *
* LICENSE: This library is free software; you can redistribute it and/or * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
* modify it under the terms of the GNU Lesser General Public * of this software and associated documentation files (the "Software"), to deal
* License as published by the Free Software Foundation; either * in the Software without restriction, including without limitation the rights
* version 2.1 of the License, or (at your option) any later version. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* * copies of the Software, and to permit persons to whom the Software is
* This library is distributed in the hope that it will be useful, * furnished to do so, subject to the following conditions:
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * The above copyright notice and this permission notice shall be included in
* Lesser General Public License for more details. * all copies or substantial portions of the Software.
* *
* You should have received a copy of the GNU Lesser General Public * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* License along with this library; if not, write to the Free Software * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* MA 02111-1307 USA * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* @category Net * @category Net
* @package Net_SFTP * @package Net_SFTP
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @copyright MMIX Jim Wigginton * @copyright MMIX Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version $Id$
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -74,7 +76,7 @@ define('NET_SFTP_LOG_COMPLEX', NET_SSH2_LOG_COMPLEX);
/** /**
* SFTP channel constant * SFTP channel constant
* *
* Net_SSH2::exec() uses 0 and Net_SSH2::interactiveRead() / Net_SSH2::interactiveWrite() use 1. * Net_SSH2::exec() uses 0 and Net_SSH2::read() / Net_SSH2::write() use 1.
* *
* @see Net_SSH2::_send_channel_packet() * @see Net_SSH2::_send_channel_packet()
* @see Net_SSH2::_get_channel_packet() * @see Net_SSH2::_get_channel_packet()
@ -210,7 +212,16 @@ class Net_SFTP extends Net_SSH2 {
* @var String * @var String
* @access private * @access private
*/ */
var $errors = array(); var $sftp_errors = array();
/**
* File Type
*
* @see Net_SFTP::_parseLongname()
* @var Integer
* @access private
*/
var $fileType = 0;
/** /**
* Default Constructor. * Default Constructor.
@ -236,7 +247,7 @@ class Net_SFTP extends Net_SSH2 {
4 => 'NET_SFTP_CLOSE', 4 => 'NET_SFTP_CLOSE',
5 => 'NET_SFTP_READ', 5 => 'NET_SFTP_READ',
6 => 'NET_SFTP_WRITE', 6 => 'NET_SFTP_WRITE',
8 => 'NET_SFTP_FSTAT', 7 => 'NET_SFTP_LSTAT',
9 => 'NET_SFTP_SETSTAT', 9 => 'NET_SFTP_SETSTAT',
11 => 'NET_SFTP_OPENDIR', 11 => 'NET_SFTP_OPENDIR',
12 => 'NET_SFTP_READDIR', 12 => 'NET_SFTP_READDIR',
@ -279,7 +290,11 @@ class Net_SFTP extends Net_SSH2 {
0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+ 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+
0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS', 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME', 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
-1 => 'NET_SFTP_ATTR_EXTENDED' // unpack('N', "\xFF\xFF\xFF\xFF") == array(1 => int(-1)) // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers
// yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in
// two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000.
// that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored.
-1 << 31 => 'NET_SFTP_ATTR_EXTENDED'
); );
// http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3
// the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name
@ -290,11 +305,20 @@ class Net_SFTP extends Net_SSH2 {
0x00000008 => 'NET_SFTP_OPEN_CREATE', 0x00000008 => 'NET_SFTP_OPEN_CREATE',
0x00000010 => 'NET_SFTP_OPEN_TRUNCATE' 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE'
); );
// http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
// see Net_SFTP::_parseLongname() for an explanation
$this->file_types = array(
1 => 'NET_SFTP_TYPE_REGULAR',
2 => 'NET_SFTP_TYPE_DIRECTORY',
3 => 'NET_SFTP_TYPE_SYMLINK',
4 => 'NET_SFTP_TYPE_SPECIAL'
);
$this->_define_array( $this->_define_array(
$this->packet_types, $this->packet_types,
$this->status_codes, $this->status_codes,
$this->attributes, $this->attributes,
$this->open_flags $this->open_flags,
$this->file_types
); );
} }
@ -492,6 +516,11 @@ class Net_SFTP extends Net_SSH2 {
$this->_string_shift($response, 4); // skip over the count - it should be 1, anyway $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway
extract(unpack('Nlength', $this->_string_shift($response, 4))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
$realpath = $this->_string_shift($response, $length); $realpath = $this->_string_shift($response, $length);
// the following is SFTPv3 only code. see Net_SFTP::_parseLongname() for more information.
// per the above comment, this is a shot in the dark that, on most servers, won't help us in determining
// the file type for Net_SFTP::stat() and Net_SFTP::lstat() but it's worth a shot.
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->fileType = $this->_parseLongname($this->_string_shift($response, $length));
break; break;
case NET_SFTP_STATUS: case NET_SFTP_STATUS:
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8))); extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
@ -579,7 +608,7 @@ class Net_SFTP extends Net_SSH2 {
} }
/** /**
* Returns a list of files in the given directory * Returns a detailed list of files in the given directory
* *
* @param optional String $dir * @param optional String $dir
* @return Mixed * @return Mixed
@ -590,6 +619,13 @@ class Net_SFTP extends Net_SSH2 {
return $this->_list($dir, true); return $this->_list($dir, true);
} }
/**
* Reads a list, be it detailed or not, of files in the given directory
*
* @param optional String $dir
* @return Mixed
* @access private
*/
function _list($dir, $raw = true) function _list($dir, $raw = true)
{ {
if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
@ -641,12 +677,16 @@ class Net_SFTP extends Net_SSH2 {
extract(unpack('Nlength', $this->_string_shift($response, 4))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
$shortname = $this->_string_shift($response, $length); $shortname = $this->_string_shift($response, $length);
extract(unpack('Nlength', $this->_string_shift($response, 4))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->_string_shift($response, $length); // SFTPv4+ drop this field - the "longname" field $longname = $this->_string_shift($response, $length);
$attributes = $this->_parseAttributes($response); // we also don't care about the attributes $attributes = $this->_parseAttributes($response); // we also don't care about the attributes
if (!$raw) { if (!$raw) {
$contents[] = $shortname; $contents[] = $shortname;
} else { } else {
$contents[$shortname] = $attributes; $contents[$shortname] = $attributes;
$fileType = $this->_parseLongname($longname);
if ($fileType) {
$contents[$shortname]['type'] = $fileType;
}
} }
// SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the
// final SSH_FXP_STATUS packet should tell us that, already. // final SSH_FXP_STATUS packet should tell us that, already.
@ -693,7 +733,7 @@ class Net_SFTP extends Net_SSH2 {
* *
* Files larger than 4GB will show up as being exactly 4GB. * Files larger than 4GB will show up as being exactly 4GB.
* *
* @param optional String $dir * @param String $filename
* @return Mixed * @return Mixed
* @access public * @access public
*/ */
@ -708,17 +748,82 @@ class Net_SFTP extends Net_SSH2 {
return false; return false;
} }
return $this->_size($filename);
}
/**
* Returns general information about a file.
*
* Returns an array on success and false otherwise.
*
* @param String $filename
* @return Mixed
* @access public
*/
function stat($filename)
{
if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
return false;
}
$filename = $this->_realpath($filename);
if ($filename === false) {
return false;
}
return $this->_stat($filename, NET_SFTP_STAT);
}
/**
* Returns general information about a file or symbolic link.
*
* Returns an array on success and false otherwise.
*
* @param String $filename
* @return Mixed
* @access public
*/
function lstat($filename)
{
if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
return false;
}
$filename = $this->_realpath($filename);
if ($filename === false) {
return false;
}
return $this->_stat($filename, NET_SFTP_LSTAT);
}
/**
* Returns general information about a file or symbolic link
*
* Determines information without calling Net_SFTP::_realpath().
* The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT.
*
* @param String $filename
* @param Integer $type
* @return Mixed
* @access private
*/
function _stat($filename, $type)
{
// SFTPv4+ adds an additional 32-bit integer field - flags - to the following: // SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
$packet = pack('Na*', strlen($filename), $filename); $packet = pack('Na*', strlen($filename), $filename);
if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) { if (!$this->_send_sftp_packet($type, $packet)) {
return false; return false;
} }
$response = $this->_get_sftp_packet(); $response = $this->_get_sftp_packet();
switch ($this->packet_type) { switch ($this->packet_type) {
case NET_SFTP_ATTRS: case NET_SFTP_ATTRS:
$attrs = $this->_parseAttributes($response); $attributes = $this->_parseAttributes($response);
return $attrs['size']; if ($this->fileType) {
$attributes['type'] = $this->fileType;
}
return $attributes;
case NET_SFTP_STATUS: case NET_SFTP_STATUS:
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8))); extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length); $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
@ -729,6 +834,21 @@ class Net_SFTP extends Net_SSH2 {
return false; return false;
} }
/**
* Returns the file size, in bytes, or false, on failure
*
* Determines the size without calling Net_SFTP::_realpath()
*
* @param String $filename
* @return Mixed
* @access private
*/
function _size($filename)
{
$result = $this->_stat($filename, NET_SFTP_LSTAT);
return $result === false ? false : $result['size'];
}
/** /**
* Set permissions on a file. * Set permissions on a file.
* *
@ -894,7 +1014,7 @@ class Net_SFTP extends Net_SSH2 {
* *
* @param String $remote_file * @param String $remote_file
* @param String $data * @param String $data
* @param optional Integer $flags * @param optional Integer $mode
* @return Boolean * @return Boolean
* @access public * @access public
* @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - Net_SFTP::setMode(). * @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - Net_SFTP::setMode().
@ -937,7 +1057,7 @@ class Net_SFTP extends Net_SSH2 {
user_error("$data is not a valid file", E_USER_NOTICE); user_error("$data is not a valid file", E_USER_NOTICE);
return false; return false;
} }
$fp = fopen($data, 'rb'); $fp = @fopen($data, 'rb');
if (!$fp) { if (!$fp) {
return false; return false;
} }
@ -950,7 +1070,7 @@ class Net_SFTP extends Net_SSH2 {
$size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size; $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;
$sftp_packet_size = 34000; // PuTTY uses 4096 $sftp_packet_size = 4096; // PuTTY uses 4096
$i = 0; $i = 0;
while ($sent < $size) { while ($sent < $size) {
$temp = $mode == NET_SFTP_LOCAL_FILE ? fread($fp, $sftp_packet_size) : $this->_string_shift($data, $sftp_packet_size); $temp = $mode == NET_SFTP_LOCAL_FILE ? fread($fp, $sftp_packet_size) : $this->_string_shift($data, $sftp_packet_size);
@ -1051,6 +1171,11 @@ class Net_SFTP extends Net_SSH2 {
return false; return false;
} }
$size = $this->_size($remote_file);
if ($size === false) {
return false;
}
$packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0); $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false; return false;
@ -1070,25 +1195,6 @@ class Net_SFTP extends Net_SSH2 {
return false; return false;
} }
$packet = pack('Na*', strlen($handle), $handle);
if (!$this->_send_sftp_packet(NET_SFTP_FSTAT, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_ATTRS:
$attrs = $this->_parseAttributes($response);
break;
case NET_SFTP_STATUS:
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
return false;
default:
user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS', E_USER_NOTICE);
return false;
}
if ($local_file !== false) { if ($local_file !== false) {
$fp = fopen($local_file, 'wb'); $fp = fopen($local_file, 'wb');
if (!$fp) { if (!$fp) {
@ -1099,7 +1205,7 @@ class Net_SFTP extends Net_SSH2 {
} }
$read = 0; $read = 0;
while ($read < $attrs['size']) { while ($read < $size) {
$packet = pack('Na*N3', strlen($handle), $handle, 0, $read, 1 << 20); $packet = pack('Na*N3', strlen($handle), $handle, 0, $read, 1 << 20);
if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) { if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) {
return false; return false;
@ -1136,6 +1242,14 @@ class Net_SFTP extends Net_SSH2 {
return false; return false;
} }
extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
$this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
// check the status from the NET_SFTP_STATUS case in the above switch after the file has been closed
if ($status != NET_SFTP_STATUS_OK) {
return false;
}
extract(unpack('Nstatus', $this->_string_shift($response, 4))); extract(unpack('Nstatus', $this->_string_shift($response, 4)));
if ($status != NET_SFTP_STATUS_OK) { if ($status != NET_SFTP_STATUS_OK) {
extract(unpack('Nlength', $this->_string_shift($response, 4))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
@ -1164,7 +1278,7 @@ class Net_SFTP extends Net_SSH2 {
return false; return false;
} }
$remote_file = $this->_realpath($path); $path = $this->_realpath($path);
if ($path === false) { if ($path === false) {
return false; return false;
} }
@ -1285,6 +1399,40 @@ class Net_SFTP extends Net_SSH2 {
return $attr; return $attr;
} }
/**
* Parse Longname
*
* SFTPv3 doesn't provide any easy way of identifying a file type. You could try to open
* a file as a directory and see if an error is returned or you could try to parse the
* SFTPv3-specific longname field of the SSH_FXP_NAME packet. That's what this function does.
* The result is returned using the
* {@link http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 SFTPv4 type constants}.
*
* If the longname is in an unrecognized format bool(false) is returned.
*
* @param String $longname
* @return Mixed
* @access private
*/
function _parseLongname($longname)
{
// http://en.wikipedia.org/wiki/Unix_file_types
if (preg_match('#^[^/]([r-][w-][x-]){3}#', $longname)) {
switch ($longname[0]) {
case '-':
return NET_SFTP_TYPE_REGULAR;
case 'd':
return NET_SFTP_TYPE_DIRECTORY;
case 'l':
return NET_SFTP_TYPE_SYMLINK;
default:
return NET_SFTP_TYPE_SPECIAL;
}
}
return false;
}
/** /**
* Sends SFTP Packets * Sends SFTP Packets
* *
@ -1428,7 +1576,7 @@ class Net_SFTP extends Net_SSH2 {
*/ */
function getLastSFTPError() function getLastSFTPError()
{ {
return $this->sftp_errors[count($this->sftp_errors) - 1]; return count($this->sftp_errors) ? $this->sftp_errors[count($this->sftp_errors) - 1] : '';
} }
/** /**

View File

@ -16,15 +16,7 @@
* exit('Login Failed'); * exit('Login Failed');
* } * }
* *
* while (true) { * echo $ssh->exec('ls -la');
* echo $ssh->interactiveRead();
*
* $read = array(STDIN);
* $write = $except = NULL;
* if (stream_select($read, $write, $except, 0)) {
* $ssh->interactiveWrite(fread(STDIN, 1));
* }
* }
* ?> * ?>
* </code> * </code>
* *
@ -38,33 +30,38 @@
* exit('Login Failed'); * exit('Login Failed');
* } * }
* *
* echo $ssh->exec('ls -la'); * echo $ssh->read('username@username:~$');
* $ssh->write("ls -la\n");
* echo $ssh->read('username@username:~$');
* ?> * ?>
* </code> * </code>
* *
* More information on the SSHv1 specification can be found by reading * More information on the SSHv1 specification can be found by reading
* {@link http://www.snailbook.com/docs/protocol-1.5.txt protocol-1.5.txt}. * {@link http://www.snailbook.com/docs/protocol-1.5.txt protocol-1.5.txt}.
* *
* LICENSE: This library is free software; you can redistribute it and/or * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
* modify it under the terms of the GNU Lesser General Public * of this software and associated documentation files (the "Software"), to deal
* License as published by the Free Software Foundation; either * in the Software without restriction, including without limitation the rights
* version 2.1 of the License, or (at your option) any later version. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* * copies of the Software, and to permit persons to whom the Software is
* This library is distributed in the hope that it will be useful, * furnished to do so, subject to the following conditions:
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * The above copyright notice and this permission notice shall be included in
* Lesser General Public License for more details. * all copies or substantial portions of the Software.
* *
* You should have received a copy of the GNU Lesser General Public * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* License along with this library; if not, write to the Free Software * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* MA 02111-1307 USA * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* @category Net * @category Net
* @package Net_SSH1 * @package Net_SSH1
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVII Jim Wigginton * @copyright MMVII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version $Id$ * @version $Id$
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -101,28 +98,6 @@ require_once('Crypt/RC4.php');
*/ */
require_once('Crypt/Random.php'); require_once('Crypt/Random.php');
/**#@+
* Protocol Flags
*
* @access private
*/
define('NET_SSH1_MSG_DISCONNECT', 1);
define('NET_SSH1_SMSG_PUBLIC_KEY', 2);
define('NET_SSH1_CMSG_SESSION_KEY', 3);
define('NET_SSH1_CMSG_USER', 4);
define('NET_SSH1_CMSG_AUTH_PASSWORD', 9);
define('NET_SSH1_CMSG_REQUEST_PTY', 10);
define('NET_SSH1_CMSG_EXEC_SHELL', 12);
define('NET_SSH1_CMSG_EXEC_CMD', 13);
define('NET_SSH1_SMSG_SUCCESS', 14);
define('NET_SSH1_SMSG_FAILURE', 15);
define('NET_SSH1_CMSG_STDIN_DATA', 16);
define('NET_SSH1_SMSG_STDOUT_DATA', 17);
define('NET_SSH1_SMSG_STDERR_DATA', 18);
define('NET_SSH1_SMSG_EXITSTATUS', 20);
define('NET_SSH1_CMSG_EXIT_CONFIRMATION', 33);
/**#@-*/
/**#@+ /**#@+
* Encryption Methods * Encryption Methods
* *
@ -245,6 +220,34 @@ define('NET_SSH1_MASK_LOGIN', 0x00000002);
define('NET_SSH1_MASK_SHELL', 0x00000004); define('NET_SSH1_MASK_SHELL', 0x00000004);
/**#@-*/ /**#@-*/
/**#@+
* @access public
* @see Net_SSH1::getLog()
*/
/**
* Returns the message numbers
*/
define('NET_SSH1_LOG_SIMPLE', 1);
/**
* Returns the message content
*/
define('NET_SSH1_LOG_COMPLEX', 2);
/**#@-*/
/**#@+
* @access public
* @see Net_SSH1::read()
*/
/**
* Returns when a string matching $expect exactly is found
*/
define('NET_SSH1_READ_SIMPLE', 1);
/**
* Returns when a string matching the regular expression $expect is found
*/
define('NET_SSH1_READ_REGEX', 2);
/**#@-*/
/** /**
* Pure-PHP implementation of SSHv1. * Pure-PHP implementation of SSHv1.
* *
@ -281,7 +284,7 @@ class Net_SSH1 {
/** /**
* Execution Bitmap * Execution Bitmap
* *
* The bits that are set reprsent functions that have been called already. This is used to determine * The bits that are set represent functions that have been called already. This is used to determine
* if a requisite function has been successfully executed. If not, an error should be thrown. * if a requisite function has been successfully executed. If not, an error should be thrown.
* *
* @var Integer * @var Integer
@ -377,6 +380,42 @@ class Net_SSH1 {
*/ */
var $server_identification = ''; var $server_identification = '';
/**
* Protocol Flags
*
* @see Net_SSH1::Net_SSH1()
* @var Array
* @access private
*/
var $protocol_flags = array();
/**
* Protocol Flag Log
*
* @see Net_SSH1::getLog()
* @var Array
* @access private
*/
var $protocol_flag_log = array();
/**
* Message Log
*
* @see Net_SSH1::getLog()
* @var Array
* @access private
*/
var $message_log = array();
/**
* Interactive Buffer
*
* @see Net_SSH1::read()
* @var Array
* @access private
*/
var $interactive_buffer = '';
/** /**
* Default Constructor. * Default Constructor.
* *
@ -391,6 +430,27 @@ class Net_SSH1 {
*/ */
function Net_SSH1($host, $port = 22, $timeout = 10, $cipher = NET_SSH1_CIPHER_3DES) function Net_SSH1($host, $port = 22, $timeout = 10, $cipher = NET_SSH1_CIPHER_3DES)
{ {
$this->protocol_flags = array(
1 => 'NET_SSH1_MSG_DISCONNECT',
2 => 'NET_SSH1_SMSG_PUBLIC_KEY',
3 => 'NET_SSH1_CMSG_SESSION_KEY',
4 => 'NET_SSH1_CMSG_USER',
9 => 'NET_SSH1_CMSG_AUTH_PASSWORD',
10 => 'NET_SSH1_CMSG_REQUEST_PTY',
12 => 'NET_SSH1_CMSG_EXEC_SHELL',
13 => 'NET_SSH1_CMSG_EXEC_CMD',
14 => 'NET_SSH1_SMSG_SUCCESS',
15 => 'NET_SSH1_SMSG_FAILURE',
16 => 'NET_SSH1_CMSG_STDIN_DATA',
17 => 'NET_SSH1_SMSG_STDOUT_DATA',
18 => 'NET_SSH1_SMSG_STDERR_DATA',
19 => 'NET_SSH1_CMSG_EOF',
20 => 'NET_SSH1_SMSG_EXITSTATUS',
33 => 'NET_SSH1_CMSG_EXIT_CONFIRMATION'
);
$this->_define_array($this->protocol_flags);
$this->fsock = @fsockopen($host, $port, $errno, $errstr, $timeout); $this->fsock = @fsockopen($host, $port, $errno, $errstr, $timeout);
if (!$this->fsock) { if (!$this->fsock) {
user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"), E_USER_NOTICE); user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"), E_USER_NOTICE);
@ -398,6 +458,17 @@ class Net_SSH1 {
} }
$this->server_identification = $init_line = fgets($this->fsock, 255); $this->server_identification = $init_line = fgets($this->fsock, 255);
if (defined('NET_SSH1_LOGGING')) {
$this->protocol_flags_log[] = '<-';
$this->protocol_flags_log[] = '->';
if (NET_SSH1_LOGGING == NET_SSH1_LOG_COMPLEX) {
$this->message_log[] = $this->server_identification;
$this->message_log[] = $this->identifier . "\r\n";
}
}
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', E_USER_NOTICE); user_error('Can only connect to SSH servers', E_USER_NOTICE);
return; return;
@ -574,6 +645,12 @@ class Net_SSH1 {
return false; return false;
} }
// remove the username and password from the last logged packet
if (defined('NET_SSH1_LOGGING') && NET_SSH1_LOGGING == NET_SSH1_LOG_COMPLEX) {
$data = pack('CNa*', NET_SSH1_CMSG_AUTH_PASSWORD, strlen('password'), 'password');
$this->message_log[count($this->message_log) - 1] = $data; // zzzzz
}
$response = $this->_get_binary_packet(); $response = $this->_get_binary_packet();
if ($response[NET_SSH1_RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) { if ($response[NET_SSH1_RESPONSE_TYPE] == NET_SSH1_SMSG_SUCCESS) {
@ -607,30 +684,13 @@ class Net_SSH1 {
* @return mixed * @return mixed
* @access public * @access public
*/ */
function exec($cmd) function exec($cmd, $block = true)
{ {
if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) { if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
user_error('Operation disallowed prior to login()', E_USER_NOTICE); user_error('Operation disallowed prior to login()', E_USER_NOTICE);
return false; return false;
} }
// connect using the sample parameters in protocol-1.5.txt.
// according to wikipedia.org's entry on text terminals, "the fundamental type of application running on a text
// terminal is a command line interpreter or shell". thus, opening a terminal session to run the shell.
$data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, NET_SSH1_TTY_OP_END);
if (!$this->_send_binary_packet($data)) {
user_error('Error sending SSH_CMSG_REQUEST_PTY', E_USER_NOTICE);
return false;
}
$response = $this->_get_binary_packet();
if ($response[NET_SSH1_RESPONSE_TYPE] != NET_SSH1_SMSG_SUCCESS) {
user_error('Expected SSH_SMSG_SUCCESS', E_USER_NOTICE);
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)) {
@ -638,6 +698,10 @@ class Net_SSH1 {
return false; return false;
} }
if (!$block) {
return true;
}
$output = ''; $output = '';
$response = $this->_get_binary_packet(); $response = $this->_get_binary_packet();
@ -669,6 +733,9 @@ class Net_SSH1 {
*/ */
function _initShell() function _initShell()
{ {
// connect using the sample parameters in protocol-1.5.txt.
// according to wikipedia.org's entry on text terminals, "the fundamental type of application running on a text
// terminal is a command line interpreter or shell". thus, opening a terminal session to run the shell.
$data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, NET_SSH1_TTY_OP_END); $data = pack('CNa*N4C', NET_SSH1_CMSG_REQUEST_PTY, strlen('vt100'), 'vt100', 24, 80, 0, 0, NET_SSH1_TTY_OP_END);
if (!$this->_send_binary_packet($data)) { if (!$this->_send_binary_packet($data)) {
@ -697,6 +764,58 @@ class Net_SSH1 {
return true; return true;
} }
/**
* Inputs a command into an interactive shell.
*
* @see Net_SSH1::interactiveWrite()
* @param String $cmd
* @return Boolean
* @access public
*/
function write($cmd)
{
return $this->interactiveWrite($cmd);
}
/**
* Returns the output of an interactive shell when there's a match for $expect
*
* $expect can take the form of a string literal or, if $mode == NET_SSH1_READ_REGEX,
* a regular expression.
*
* @see Net_SSH1::write()
* @param String $expect
* @param Integer $mode
* @return Boolean
* @access public
*/
function read($expect, $mode = NET_SSH1_READ_SIMPLE)
{
if (!($this->bitmap & NET_SSH1_MASK_LOGIN)) {
user_error('Operation disallowed prior to login()', E_USER_NOTICE);
return false;
}
if (!($this->bitmap & NET_SSH1_MASK_SHELL) && !$this->_initShell()) {
user_error('Unable to initiate an interactive shell session', E_USER_NOTICE);
return false;
}
$match = $expect;
while (true) {
if ($mode == NET_SSH1_READ_REGEX) {
preg_match($expect, $this->interactiveBuffer, $matches);
$match = $matches[0];
}
$pos = strpos($this->interactiveBuffer, $match);
if ($pos !== false) {
return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
}
$response = $this->_get_binary_packet();
$this->interactiveBuffer.= substr($response[NET_SSH1_RESPONSE_DATA], 4);
}
}
/** /**
* Inputs a command into an interactive shell. * Inputs a command into an interactive shell.
* *
@ -728,7 +847,7 @@ class Net_SSH1 {
} }
/** /**
* Reads the output of an interactive shell. * Returns the output of an interactive shell when no more output is available.
* *
* Requires PHP 4.3.0 or later due to the use of the stream_select() function. If you see stuff like * Requires PHP 4.3.0 or later due to the use of the stream_select() function. If you see stuff like
* "", you're seeing ANSI escape codes. According to * "", you're seeing ANSI escape codes. According to
@ -794,7 +913,18 @@ class Net_SSH1 {
function _disconnect($msg = 'Client Quit') function _disconnect($msg = 'Client Quit')
{ {
if ($this->bitmap) { if ($this->bitmap) {
$data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg); $data = pack('C', NET_SSH1_CMSG_EOF);
$this->_send_binary_packet($data);
$response = $this->_get_binary_packet();
switch ($response[NET_SSH1_RESPONSE_TYPE]) {
case NET_SSH1_SMSG_EXITSTATUS:
$data = pack('C', NET_SSH1_CMSG_EXIT_CONFIRMATION);
break;
default:
$data = pack('CNa*', NET_SSH1_MSG_DISCONNECT, strlen($msg), $msg);
}
$this->_send_binary_packet($data); $this->_send_binary_packet($data);
fclose($this->fsock); fclose($this->fsock);
$this->bitmap = 0; $this->bitmap = 0;
@ -825,7 +955,9 @@ class Net_SSH1 {
$padding_length = 8 - ($temp['length'] & 7); $padding_length = 8 - ($temp['length'] & 7);
$length = $temp['length'] + $padding_length; $length = $temp['length'] + $padding_length;
$start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
$raw = fread($this->fsock, $length); $raw = fread($this->fsock, $length);
$stop = strtok(microtime(), ' ') + strtok('');
if ($this->crypto !== false) { if ($this->crypto !== false) {
$raw = $this->crypto->decrypt($raw); $raw = $this->crypto->decrypt($raw);
@ -842,8 +974,19 @@ class Net_SSH1 {
// return false; // return false;
//} //}
$type = ord($type);
if (defined('NET_SSH1_LOGGING')) {
$temp = isset($this->protocol_flags[$type]) ? $this->protocol_flags[$type] : 'UNKNOWN';
$this->protocol_flags_log[] = '<- ' . $temp .
' (' . round($stop - $start, 4) . 's)';
if (NET_SSH1_LOGGING == NET_SSH1_LOG_COMPLEX) {
$this->message_log[] = $data;
}
}
return array( return array(
NET_SSH1_RESPONSE_TYPE => ord($type), NET_SSH1_RESPONSE_TYPE => $type,
NET_SSH1_RESPONSE_DATA => $data NET_SSH1_RESPONSE_DATA => $data
); );
} }
@ -864,6 +1007,15 @@ class Net_SSH1 {
return false; return false;
} }
if (defined('NET_SSH1_LOGGING')) {
$temp = isset($this->protocol_flags[ord($data[0])]) ? $this->protocol_flags[ord($data[0])] : 'UNKNOWN';
$this->protocol_flags_log[] = '-> ' . $temp .
' (' . round($stop - $start, 4) . 's)';
if (NET_SSH1_LOGGING == NET_SSH1_LOG_COMPLEX) {
$this->message_log[] = substr($data, 1);
}
}
$length = strlen($data) + 4; $length = strlen($data) + 4;
$padding_length = 8 - ($length & 7); $padding_length = 8 - ($length & 7);
@ -881,7 +1033,11 @@ class Net_SSH1 {
$packet = pack('Na*', $length, $data); $packet = pack('Na*', $length, $data);
return strlen($packet) == fputs($this->fsock, $packet); $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
$result = strlen($packet) == fputs($this->fsock, $packet);
$stop = strtok(microtime(), ' ') + strtok('');
return $result;
} }
/** /**
@ -1054,6 +1210,98 @@ class Net_SSH1 {
return $m->toBytes(); return $m->toBytes();
} }
/**
* Define Array
*
* Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of
* named constants from it, using the value as the name of the constant and the index as the value of the constant.
* If any of the constants that would be defined already exists, none of the constants will be defined.
*
* @param Array $array
* @access private
*/
function _define_array()
{
$args = func_get_args();
foreach ($args as $arg) {
foreach ($arg as $key=>$value) {
if (!defined($value)) {
define($value, $key);
} else {
break 2;
}
}
}
}
/**
* Returns a log of the packets that have been sent and received.
*
* Returns a string if NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX, an array if NET_SSH2_LOGGING == NET_SSH2_LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING')
*
* @access public
* @return String or Array
*/
function getLog()
{
if (!defined('NET_SSH1_LOGGING')) {
return false;
}
switch (NET_SSH1_LOGGING) {
case NET_SSH1_LOG_SIMPLE:
return $this->message_number_log;
break;
case NET_SSH1_LOG_COMPLEX:
return $this->_format_log($this->message_log, $this->protocol_flags_log);
break;
default:
return false;
}
}
/**
* Formats a log for printing
*
* @param Array $message_log
* @param Array $message_number_log
* @access private
* @return String
*/
function _format_log($message_log, $message_number_log)
{
static $boundary = ':', $long_width = 65, $short_width = 16;
$output = '';
for ($i = 0; $i < count($message_log); $i++) {
$output.= $message_number_log[$i] . "\r\n";
$current_log = $message_log[$i];
$j = 0;
do {
if (!empty($current_log)) {
$output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 ';
}
$fragment = $this->_string_shift($current_log, $short_width);
$hex = substr(
preg_replace(
'#(.)#es',
'"' . $boundary . '" . str_pad(dechex(ord(substr("\\1", -1))), 2, "0", STR_PAD_LEFT)',
$fragment),
strlen($boundary)
);
// replace non ASCII printable characters with dots
// http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
// also replace < with a . since < messes up the output on web browsers
$raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
$output.= str_pad($hex, $long_width - $short_width, ' ') . $raw . "\r\n";
$j++;
} while (!empty($current_log));
$output.= "\r\n";
}
return $output;
}
/** /**
* Return the server key public exponent * Return the server key public exponent
* *

View File

@ -35,31 +35,35 @@
* exit('Login Failed'); * exit('Login Failed');
* } * }
* *
* echo $ssh->exec('pwd'); * echo $ssh->read('username@username:~$');
* echo $ssh->exec('ls -la'); * $ssh->write("ls -la\n");
* echo $ssh->read('username@username:~$');
* ?> * ?>
* </code> * </code>
* *
* LICENSE: This library is free software; you can redistribute it and/or * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
* modify it under the terms of the GNU Lesser General Public * of this software and associated documentation files (the "Software"), to deal
* License as published by the Free Software Foundation; either * in the Software without restriction, including without limitation the rights
* version 2.1 of the License, or (at your option) any later version. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* * copies of the Software, and to permit persons to whom the Software is
* This library is distributed in the hope that it will be useful, * furnished to do so, subject to the following conditions:
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * The above copyright notice and this permission notice shall be included in
* Lesser General Public License for more details. * all copies or substantial portions of the Software.
* *
* You should have received a copy of the GNU Lesser General Public * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* License along with this library; if not, write to the Free Software * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* MA 02111-1307 USA * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* *
* @category Net * @category Net
* @package Net_SSH2 * @package Net_SSH2
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVII Jim Wigginton * @copyright MMVII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version $Id$ * @version $Id$
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -104,6 +108,7 @@ require_once('Crypt/AES.php');
*/ */
define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001); define('NET_SSH2_MASK_CONSTRUCTOR', 0x00000001);
define('NET_SSH2_MASK_LOGIN', 0x00000002); define('NET_SSH2_MASK_LOGIN', 0x00000002);
define('NET_SSH2_MASK_SHELL', 0x00000004);
/**#@-*/ /**#@-*/
/**#@+ /**#@+
@ -123,6 +128,7 @@ define('NET_SSH2_MASK_LOGIN', 0x00000002);
* @access private * @access private
*/ */
define('NET_SSH2_CHANNEL_EXEC', 0); // PuTTy uses 0x100 define('NET_SSH2_CHANNEL_EXEC', 0); // PuTTy uses 0x100
define('NET_SSH2_CHANNEL_SHELL',1);
/**#@-*/ /**#@-*/
/**#@+ /**#@+
@ -139,6 +145,20 @@ define('NET_SSH2_LOG_SIMPLE', 1);
define('NET_SSH2_LOG_COMPLEX', 2); define('NET_SSH2_LOG_COMPLEX', 2);
/**#@-*/ /**#@-*/
/**#@+
* @access public
* @see Net_SSH2::read()
*/
/**
* Returns when a string matching $expect exactly is found
*/
define('NET_SSH2_READ_SIMPLE', 1);
/**
* Returns when a string matching the regular expression $expect is found
*/
define('NET_SSH2_READ_REGEX', 2);
/**#@-*/
/** /**
* Pure-PHP implementation of SSHv2. * Pure-PHP implementation of SSHv2.
* *
@ -384,6 +404,17 @@ class Net_SSH2 {
*/ */
var $session_id = false; var $session_id = false;
/**
* Exchange hash
*
* The current exchange hash
*
* @see Net_SSH2::_key_exchange()
* @var String
* @access private
*/
var $exchange_hash = false;
/** /**
* Message Numbers * Message Numbers
* *
@ -563,6 +594,15 @@ class Net_SSH2 {
*/ */
var $signature_format = ''; var $signature_format = '';
/**
* Interactive Buffer
*
* @see Net_SSH2::read()
* @var Array
* @access private
*/
var $interactiveBuffer = '';
/** /**
* Default Constructor. * Default Constructor.
* *
@ -641,7 +681,9 @@ class Net_SSH2 {
$this->terminal_modes, $this->terminal_modes,
$this->channel_extended_data_type_codes, $this->channel_extended_data_type_codes,
array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'), array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK') array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'),
array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE')
); );
$this->fsock = @fsockopen($host, $port, $errno, $errstr, $timeout); $this->fsock = @fsockopen($host, $port, $errno, $errstr, $timeout);
@ -667,6 +709,11 @@ class Net_SSH2 {
$temp.= fgets($this->fsock, 255); $temp.= fgets($this->fsock, 255);
} }
if (feof($this->fsock)) {
user_error('Connection closed by server', E_USER_NOTICE);
return false;
}
$ext = array(); $ext = array();
if (extension_loaded('mcrypt')) { if (extension_loaded('mcrypt')) {
$ext[] = 'mcrypt'; $ext[] = 'mcrypt';
@ -1009,17 +1056,17 @@ class Net_SSH2 {
$key = $f->modPow($x, $p); $key = $f->modPow($x, $p);
$keyBytes = $key->toBytes(true); $keyBytes = $key->toBytes(true);
$this->exchange_hash = pack('Na*Na*Na*Na*Na*Na*Na*Na*',
strlen($this->identifier), $this->identifier, strlen($this->server_identifier), $this->server_identifier,
strlen($kexinit_payload_client), $kexinit_payload_client, strlen($kexinit_payload_server),
$kexinit_payload_server, strlen($this->server_public_host_key), $this->server_public_host_key, strlen($eBytes),
$eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes
);
$this->exchange_hash = pack('H*', $hash($this->exchange_hash));
if ($this->session_id === false) { if ($this->session_id === false) {
$source = pack('Na*Na*Na*Na*Na*Na*Na*Na*', $this->session_id = $this->exchange_hash;
strlen($this->identifier), $this->identifier, strlen($this->server_identifier), $this->server_identifier,
strlen($kexinit_payload_client), $kexinit_payload_client, strlen($kexinit_payload_server),
$kexinit_payload_server, strlen($this->server_public_host_key), $this->server_public_host_key, strlen($eBytes),
$eBytes, strlen($fBytes), $fBytes, strlen($keyBytes), $keyBytes
);
$source = pack('H*', $hash($source));
$this->session_id = $source;
} }
for ($i = 0; $i < count($server_host_key_algorithms) && !in_array($server_host_key_algorithms[$i], $this->server_host_key_algorithms); $i++); for ($i = 0; $i < count($server_host_key_algorithms) && !in_array($server_host_key_algorithms[$i], $this->server_host_key_algorithms); $i++);
@ -1119,15 +1166,15 @@ class Net_SSH2 {
$this->encrypt->enableContinuousBuffer(); $this->encrypt->enableContinuousBuffer();
$this->encrypt->disablePadding(); $this->encrypt->disablePadding();
$iv = pack('H*', $hash($keyBytes . $this->session_id . 'A' . $this->session_id)); $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id));
while ($this->encrypt_block_size > strlen($iv)) { while ($this->encrypt_block_size > strlen($iv)) {
$iv.= pack('H*', $hash($keyBytes . $this->session_id . $iv)); $iv.= pack('H*', $hash($keyBytes . $this->exchange_hash . $iv));
} }
$this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size)); $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
$key = pack('H*', $hash($keyBytes . $this->session_id . 'C' . $this->session_id)); $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id));
while ($encryptKeyLength > strlen($key)) { while ($encryptKeyLength > strlen($key)) {
$key.= pack('H*', $hash($keyBytes . $this->session_id . $key)); $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
} }
$this->encrypt->setKey(substr($key, 0, $encryptKeyLength)); $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
} }
@ -1136,15 +1183,15 @@ class Net_SSH2 {
$this->decrypt->enableContinuousBuffer(); $this->decrypt->enableContinuousBuffer();
$this->decrypt->disablePadding(); $this->decrypt->disablePadding();
$iv = pack('H*', $hash($keyBytes . $this->session_id . 'B' . $this->session_id)); $iv = pack('H*', $hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id));
while ($this->decrypt_block_size > strlen($iv)) { while ($this->decrypt_block_size > strlen($iv)) {
$iv.= pack('H*', $hash($keyBytes . $this->session_id . $iv)); $iv.= pack('H*', $hash($keyBytes . $this->exchange_hash . $iv));
} }
$this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size)); $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
$key = pack('H*', $hash($keyBytes . $this->session_id . 'D' . $this->session_id)); $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id));
while ($decryptKeyLength > strlen($key)) { while ($decryptKeyLength > strlen($key)) {
$key.= pack('H*', $hash($keyBytes . $this->session_id . $key)); $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
} }
$this->decrypt->setKey(substr($key, 0, $decryptKeyLength)); $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
} }
@ -1218,15 +1265,15 @@ class Net_SSH2 {
$this->hmac_size = 12; $this->hmac_size = 12;
} }
$key = pack('H*', $hash($keyBytes . $this->session_id . 'E' . $this->session_id)); $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id));
while ($createKeyLength > strlen($key)) { while ($createKeyLength > strlen($key)) {
$key.= pack('H*', $hash($keyBytes . $this->session_id . $key)); $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
} }
$this->hmac_create->setKey(substr($key, 0, $createKeyLength)); $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
$key = pack('H*', $hash($keyBytes . $this->session_id . 'F' . $this->session_id)); $key = pack('H*', $hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id));
while ($checkKeyLength > strlen($key)) { while ($checkKeyLength > strlen($key)) {
$key.= pack('H*', $hash($keyBytes . $this->session_id . $key)); $key.= pack('H*', $hash($keyBytes . $this->exchange_hash . $key));
} }
$this->hmac_check->setKey(substr($key, 0, $checkKeyLength)); $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
@ -1250,6 +1297,8 @@ class Net_SSH2 {
/** /**
* Login * Login
* *
* The $password parameter can be a plaintext password or a Crypt_RSA object.
*
* @param String $username * @param String $username
* @param optional String $password * @param optional String $password
* @return Boolean * @return Boolean
@ -1285,7 +1334,7 @@ class Net_SSH2 {
} }
// although PHP5's get_class() preserves the case, PHP4's does not // although PHP5's get_class() preserves the case, PHP4's does not
if (is_object($password) && strtolower(get_class($password)) == 'crypt_rsa') { if (is_object($password) && strtolower(get_class($password)) == 'crypt_rsa') {
return $this->_privatekey_login($username, $password); return $this->_privatekey_login($username, $password);
} }
@ -1325,7 +1374,17 @@ class Net_SSH2 {
$this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length)); $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length));
return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
case NET_SSH2_MSG_USERAUTH_FAILURE: case NET_SSH2_MSG_USERAUTH_FAILURE:
// either the login is bad or the server employees multi-factor authentication // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees
// multi-factor authentication
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$auth_methods = explode(',', $this->_string_shift($response, $length));
if (in_array('keyboard-interactive', $auth_methods)) {
if ($this->_keyboard_interactive_login($username, $password)) {
$this->bitmap |= NET_SSH2_MASK_LOGIN;
return true;
}
return false;
}
return false; return false;
case NET_SSH2_MSG_USERAUTH_SUCCESS: case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= NET_SSH2_MASK_LOGIN; $this->bitmap |= NET_SSH2_MASK_LOGIN;
@ -1335,6 +1394,117 @@ class Net_SSH2 {
return false; return false;
} }
/**
* Login via keyboard-interactive authentication
*
* See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details. This is not a full-featured keyboard-interactive authenticator.
*
* @param String $username
* @param String $password
* @return Boolean
* @access private
*/
function _keyboard_interactive_login($username, $password)
{
$packet = pack('CNa*Na*Na*Na*Na*',
NET_SSH2_MSG_USERAUTH_REQUEST, strlen($username), $username, strlen('ssh-connection'), 'ssh-connection',
strlen('keyboard-interactive'), 'keyboard-interactive', 0, '', 0, ''
);
if (!$this->_send_binary_packet($packet)) {
return false;
}
return $this->_keyboard_interactive_process($password);
}
/**
* Handle the keyboard-interactive requests / responses.
*
* @param String $responses...
* @return Boolean
* @access private
*/
function _keyboard_interactive_process()
{
$responses = func_get_args();
$response = $this->_get_binary_packet();
if ($response === false) {
user_error('Connection closed by server', E_USER_NOTICE);
return false;
}
extract(unpack('Ctype', $this->_string_shift($response, 1)));
switch ($type) {
case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
// see http://tools.ietf.org/html/rfc4256#section-3.2
if (defined('NET_SSH2_LOGGING')) {
$this->message_number_log[count($this->message_number_log) - 1] = str_replace(
'UNKNOWN',
'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
$this->message_number_log[count($this->message_number_log) - 1]
);
}
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->_string_shift($response, $length); // name; may be empty
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->_string_shift($response, $length); // instruction; may be empty
extract(unpack('Nlength', $this->_string_shift($response, 4)));
$this->_string_shift($response, $length); // language tag; may be empty
extract(unpack('Nnum_prompts', $this->_string_shift($response, 4)));
/*
for ($i = 0; $i < $num_prompts; $i++) {
extract(unpack('Nlength', $this->_string_shift($response, 4)));
// prompt - ie. "Password: "; must not be empty
$this->_string_shift($response, $length);
$echo = $this->_string_shift($response) != chr(0);
}
*/
/*
After obtaining the requested information from the user, the client
MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message.
*/
// see http://tools.ietf.org/html/rfc4256#section-3.4
$packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses));
for ($i = 0; $i < count($responses); $i++) {
$packet.= pack('Na*', strlen($responses[$i]), $responses[$i]);
$logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer');
}
if (!$this->_send_binary_packet($packet)) {
return false;
}
if (defined('NET_SSH2_LOGGING')) {
$this->message_number_log[count($this->message_number_log) - 1] = str_replace(
'UNKNOWN',
'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE',
$this->message_number_log[count($this->message_number_log) - 1]
);
$this->message_log[count($this->message_log) - 1] = $logged;
}
/*
After receiving the response, the server MUST send either an
SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another
SSH_MSG_USERAUTH_INFO_REQUEST message.
*/
// maybe phpseclib should force close the connection after x request / responses? unless something like that is done
// there could be an infinite loop of request / responses.
return $this->_keyboard_interactive_process();
case NET_SSH2_MSG_USERAUTH_SUCCESS:
return true;
case NET_SSH2_MSG_USERAUTH_FAILURE:
return false;
}
return false;
}
/** /**
* Login with an RSA private key * Login with an RSA private key
* *
@ -1368,7 +1538,6 @@ class Net_SSH2 {
$part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey); $part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey);
$packet = $part1 . chr(0) . $part2; $packet = $part1 . chr(0) . $part2;
if (!$this->_send_binary_packet($packet)) { if (!$this->_send_binary_packet($packet)) {
return false; return false;
} }
@ -1390,7 +1559,11 @@ class Net_SSH2 {
// we'll just take it on faith that the public key blob and the public key algorithm name are as // we'll just take it on faith that the public key blob and the public key algorithm name are as
// they should be // they should be
if (defined('NET_SSH2_LOGGING')) { if (defined('NET_SSH2_LOGGING')) {
$this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PK_OK'; $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
'UNKNOWN',
'NET_SSH2_MSG_USERAUTH_PK_OK',
$this->message_number_log[count($this->message_number_log) - 1]
);
} }
} }
@ -1427,11 +1600,15 @@ class Net_SSH2 {
/** /**
* Execute Command * Execute Command
* *
* If $block is set to false then Net_SSH2::_get_channel_packet(NET_SSH2_CHANNEL_EXEC) will need to be called manually.
* In all likelihood, this is not a feature you want to be taking advantage of.
*
* @param String $command * @param String $command
* @param optional Boolean $block
* @return String * @return String
* @access public * @access public
*/ */
function exec($command) function exec($command, $block = true)
{ {
if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
return false; return false;
@ -1462,7 +1639,7 @@ class Net_SSH2 {
// sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
// down. the one place where it might be desirable is if you're doing something like Net_SSH2::exec('ping localhost &'). // down. the one place where it might be desirable is if you're doing something like Net_SSH2::exec('ping localhost &').
// with a pty-req SSH_MSG_cHANNEL_REQUEST, exec() will return immediately and the ping process will then // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then
// then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but // then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but
// neither will your script. // neither will your script.
@ -1484,6 +1661,10 @@ class Net_SSH2 {
$this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA; $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
if (!$block) {
return true;
}
$output = ''; $output = '';
while (true) { while (true) {
$temp = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC); $temp = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
@ -1498,6 +1679,143 @@ class Net_SSH2 {
} }
} }
/**
* Creates an interactive shell
*
* @see Net_SSH2::read()
* @see Net_SSH2::write()
* @return Boolean
* @access private
*/
function _initShell()
{
$this->window_size_client_to_server[NET_SSH2_CHANNEL_SHELL] = 0x7FFFFFFF;
$packet_size = 0x4000;
$packet = pack('CNa*N3',
NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SSH2_CHANNEL_SHELL, $this->window_size_client_to_server[NET_SSH2_CHANNEL_SHELL], $packet_size);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;
$response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
if ($response === false) {
return false;
}
$terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
$packet = pack('CNNa*CNa*N5a*',
NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SHELL], strlen('pty-req'), 'pty-req', 1, strlen('vt100'), 'vt100',
80, 24, 0, 0, strlen($terminal_modes), $terminal_modes);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$response = $this->_get_binary_packet();
if ($response === false) {
user_error('Connection closed by server', E_USER_NOTICE);
return false;
}
list(, $type) = unpack('C', $this->_string_shift($response, 1));
switch ($type) {
case NET_SSH2_MSG_CHANNEL_SUCCESS:
break;
case NET_SSH2_MSG_CHANNEL_FAILURE:
default:
user_error('Unable to request pseudo-terminal', E_USER_NOTICE);
return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
}
$packet = pack('CNNa*C',
NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SSH2_CHANNEL_SHELL], strlen('shell'), 'shell', 1);
if (!$this->_send_binary_packet($packet)) {
return false;
}
$this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
$response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
if ($response === false) {
return false;
}
$this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
$this->bitmap |= NET_SSH2_MASK_SHELL;
return true;
}
/**
* Returns the output of an interactive shell
*
* Returns when there's a match for $expect, which can take the form of a string literal or,
* if $mode == NET_SSH2_READ_REGEX, a regular expression.
*
* @see Net_SSH2::read()
* @param String $expect
* @param Integer $mode
* @return String
* @access public
*/
function read($expect, $mode = NET_SSH2_READ_SIMPLE)
{
if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
user_error('Operation disallowed prior to login()', E_USER_NOTICE);
return false;
}
if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
user_error('Unable to initiate an interactive shell session', E_USER_NOTICE);
return false;
}
$match = $expect;
while (true) {
if ($mode == NET_SSH2_READ_REGEX) {
preg_match($expect, $this->interactiveBuffer, $matches);
$match = $matches[0];
}
$pos = strpos($this->interactiveBuffer, $match);
if ($pos !== false) {
return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
}
$response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
$this->interactiveBuffer.= $response;
}
}
/**
* Inputs a command into an interactive shell.
*
* @see Net_SSH1::interactiveWrite()
* @param String $cmd
* @return Boolean
* @access public
*/
function write($cmd)
{
if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
user_error('Operation disallowed prior to login()', E_USER_NOTICE);
return false;
}
if (!($this->bitmap & NET_SSH2_MASK_SHELL) && !$this->_initShell()) {
user_error('Unable to initiate an interactive shell session', E_USER_NOTICE);
return false;
}
return $this->_send_channel_packet(NET_SSH2_CHANNEL_SHELL, $cmd);
}
/** /**
* Disconnect * Disconnect
* *
@ -1541,6 +1859,10 @@ class Net_SSH2 {
$raw = fread($this->fsock, $this->decrypt_block_size); $raw = fread($this->fsock, $this->decrypt_block_size);
$stop = strtok(microtime(), ' ') + strtok(''); $stop = strtok(microtime(), ' ') + strtok('');
if (empty($raw)) {
return '';
}
if ($this->decrypt !== false) { if ($this->decrypt !== false) {
$raw = $this->decrypt->decrypt($raw); $raw = $this->decrypt->decrypt($raw);
} }
@ -1577,7 +1899,7 @@ class Net_SSH2 {
$this->get_seq_no++; $this->get_seq_no++;
if (defined('NET_SSH2_LOGGING')) { if (defined('NET_SSH2_LOGGING')) {
$temp = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN'; $temp = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';
$this->message_number_log[] = '<- ' . $temp . $this->message_number_log[] = '<- ' . $temp .
' (' . round($stop - $start, 4) . 's)'; ' (' . round($stop - $start, 4) . 's)';
if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) { if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
@ -1683,7 +2005,7 @@ class Net_SSH2 {
* @return Mixed * @return Mixed
* @access private * @access private
*/ */
function _get_channel_packet($client_channel) function _get_channel_packet($client_channel, $skip_extended = false)
{ {
if (!empty($this->channel_buffers[$client_channel])) { if (!empty($this->channel_buffers[$client_channel])) {
return array_shift($this->channel_buffers[$client_channel]); return array_shift($this->channel_buffers[$client_channel]);
@ -1696,6 +2018,10 @@ class Net_SSH2 {
return false; return false;
} }
if (empty($response)) {
return '';
}
extract(unpack('Ctype/Nchannel', $this->_string_shift($response, 5))); extract(unpack('Ctype/Nchannel', $this->_string_shift($response, 5)));
switch ($this->channel_status[$channel]) { switch ($this->channel_status[$channel]) {
@ -1728,6 +2054,7 @@ class Net_SSH2 {
switch ($type) { switch ($type) {
case NET_SSH2_MSG_CHANNEL_DATA: case NET_SSH2_MSG_CHANNEL_DATA:
/*
if ($client_channel == NET_SSH2_CHANNEL_EXEC) { if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
// SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server
// this actually seems to make things twice as fast. more to the point, the message right after // this actually seems to make things twice as fast. more to the point, the message right after
@ -1735,6 +2062,7 @@ class Net_SSH2 {
// in OpenSSH it slows things down but only by a couple thousandths of a second. // in OpenSSH it slows things down but only by a couple thousandths of a second.
$this->_send_channel_packet($client_channel, chr(0)); $this->_send_channel_packet($client_channel, chr(0));
} }
*/
extract(unpack('Nlength', $this->_string_shift($response, 4))); extract(unpack('Nlength', $this->_string_shift($response, 4)));
$data = $this->_string_shift($response, $length); $data = $this->_string_shift($response, $length);
if ($client_channel == $channel) { if ($client_channel == $channel) {
@ -1746,9 +2074,14 @@ class Net_SSH2 {
$this->channel_buffers[$client_channel][] = $data; $this->channel_buffers[$client_channel][] = $data;
break; break;
case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
if ($skip_extended) {
break;
}
/*
if ($client_channel == NET_SSH2_CHANNEL_EXEC) { if ($client_channel == NET_SSH2_CHANNEL_EXEC) {
$this->_send_channel_packet($client_channel, chr(0)); $this->_send_channel_packet($client_channel, chr(0));
} }
*/
// currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8))); extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
$data = $this->_string_shift($response, $length); $data = $this->_string_shift($response, $length);
@ -1844,7 +2177,7 @@ class Net_SSH2 {
$stop = strtok(microtime(), ' ') + strtok(''); $stop = strtok(microtime(), ' ') + strtok('');
if (defined('NET_SSH2_LOGGING')) { if (defined('NET_SSH2_LOGGING')) {
$temp = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN'; $temp = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')';
$this->message_number_log[] = '-> ' . $temp . $this->message_number_log[] = '-> ' . $temp .
' (' . round($stop - $start, 4) . 's)'; ' (' . round($stop - $start, 4) . 's)';
if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) { if (NET_SSH2_LOGGING == NET_SSH2_LOG_COMPLEX) {
@ -1907,6 +2240,31 @@ class Net_SSH2 {
$data)); $data));
} }
/**
* Closes and flushes a channel
*
* Net_SSH2 doesn't properly close most channels. For exec() channels are normally closed by the server
* and for SFTP channels are presumably closed when the client disconnects. This functions is intended
* for SCP more than anything.
*
* @param Integer $client_channel
* @return Boolean
* @access private
*/
function _close_channel($client_channel)
{
// see http://tools.ietf.org/html/rfc4254#section-5.3
$packet = pack('CN',
NET_SSH2_MSG_CHANNEL_EOF,
$this->server_channels[$client_channel]);
if (!$this->_send_binary_packet($packet)) {
return false;
}
while ($this->_get_channel_packet($client_channel) !== true);
}
/** /**
* Disconnect * Disconnect
* *
@ -2227,7 +2585,7 @@ class Net_SSH2 {
$w = $s->modInverse($q); $w = $s->modInverse($q);
$u1 = $w->multiply(new Math_BigInteger(sha1($this->session_id), 16)); $u1 = $w->multiply(new Math_BigInteger(sha1($this->exchange_hash), 16));
list(, $u1) = $u1->divide($q); list(, $u1) = $u1->divide($q);
$u2 = $w->multiply($r); $u2 = $w->multiply($r);
@ -2265,7 +2623,7 @@ class Net_SSH2 {
$rsa = new Crypt_RSA(); $rsa = new Crypt_RSA();
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1); $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$rsa->loadKey(array('e' => $e, 'n' => $n), CRYPT_RSA_PUBLIC_FORMAT_RAW); $rsa->loadKey(array('e' => $e, 'n' => $n), CRYPT_RSA_PUBLIC_FORMAT_RAW);
if (!$rsa->verify($this->session_id, $signature)) { if (!$rsa->verify($this->exchange_hash, $signature)) {
user_error('Bad server signature', E_USER_NOTICE); user_error('Bad server signature', E_USER_NOTICE);
return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
} }
@ -2288,7 +2646,7 @@ class Net_SSH2 {
$s = $s->modPow($e, $n); $s = $s->modPow($e, $n);
$s = $s->toBytes(); $s = $s->toBytes();
$h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($this->session_id)); $h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($this->exchange_hash));
$h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 3 - strlen($h)) . $h; $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 3 - strlen($h)) . $h;
if ($s != $h) { if ($s != $h) {

View File

@ -7,7 +7,7 @@
* *
* @category PHP * @category PHP
* @package PHP_Compat * @package PHP_Compat
* @license LGPL - http://www.gnu.org/licenses/lgpl.html * @license http://www.opensource.org/licenses/mit-license.html MIT License
* @copyright 2004-2007 Aidan Lister <aidan@php.net>, Arpad Ray <arpad@php.net> * @copyright 2004-2007 Aidan Lister <aidan@php.net>, Arpad Ray <arpad@php.net>
* @link http://php.net/function.array_fill * @link http://php.net/function.array_fill
* @author Jim Wigginton <terrafrost@php.net> * @author Jim Wigginton <terrafrost@php.net>
@ -38,6 +38,4 @@ if (!function_exists('array_fill')) {
{ {
return php_compat_array_fill($start_index, $num, $value); return php_compat_array_fill($start_index, $num, $value);
} }
} }
?>

View File

@ -63,5 +63,4 @@ if (!function_exists('bcpowmod')) {
{ {
return php_compat_bcpowmod($x, $y, $modulus, $scale); return php_compat_bcpowmod($x, $y, $modulus, $scale);
} }
} }
?>