updating phpseclib to latest cvs - fixes a bunch of key generation issues

This commit is contained in:
James Walker 2010-03-12 20:01:34 -05:00
parent c5bb41176e
commit 520faaf67d
9 changed files with 10434 additions and 9369 deletions

View File

@ -56,7 +56,7 @@
* @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.gnu.org/licenses/lgpl.txt
* @version $Id: AES.php,v 1.5 2009/11/23 19:06:06 terrafrost Exp $ * @version $Id: AES.php,v 1.7 2010/02/09 06:10:25 terrafrost Exp $
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -70,6 +70,14 @@ require_once 'Rijndael.php';
* @see Crypt_AES::encrypt() * @see Crypt_AES::encrypt()
* @see Crypt_AES::decrypt() * @see Crypt_AES::decrypt()
*/ */
/**
* Encrypt / decrypt using the Counter mode.
*
* Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
*/
define('CRYPT_AES_MODE_CTR', -1);
/** /**
* Encrypt / decrypt using the Electronic Code Book mode. * Encrypt / decrypt using the Electronic Code Book mode.
* *
@ -108,13 +116,28 @@ define('CRYPT_AES_MODE_MCRYPT', 2);
*/ */
class Crypt_AES extends Crypt_Rijndael { class Crypt_AES extends Crypt_Rijndael {
/** /**
* MCrypt parameters * mcrypt resource for encryption
* *
* @see Crypt_AES::setMCrypt() * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
* @var Array * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
*
* @see Crypt_AES::encrypt()
* @var String
* @access private * @access private
*/ */
var $mcrypt = array('', ''); var $enmcrypt;
/**
* mcrypt resource for decryption
*
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
*
* @see Crypt_AES::decrypt()
* @var String
* @access private
*/
var $demcrypt;
/** /**
* Default Constructor. * Default Constructor.
@ -147,6 +170,13 @@ class Crypt_AES extends Crypt_Rijndael {
case CRYPT_AES_MODE_ECB: case CRYPT_AES_MODE_ECB:
$this->mode = MCRYPT_MODE_ECB; $this->mode = MCRYPT_MODE_ECB;
break; break;
case CRYPT_AES_MODE_CTR:
// ctr doesn't have a constant associated with it even though it appears to be fairly widely
// supported. in lieu of knowing just how widely supported it is, i've, for now, opted not to
// include a compatibility layer. the layer has been implemented but, for now, is commented out.
$this->mode = 'ctr';
//$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_AES_MODE_CTR;
break;
case CRYPT_AES_MODE_CBC: case CRYPT_AES_MODE_CBC:
default: default:
$this->mode = MCRYPT_MODE_CBC; $this->mode = MCRYPT_MODE_CBC;
@ -158,6 +188,9 @@ class Crypt_AES extends Crypt_Rijndael {
case CRYPT_AES_MODE_ECB: case CRYPT_AES_MODE_ECB:
$this->mode = CRYPT_RIJNDAEL_MODE_ECB; $this->mode = CRYPT_RIJNDAEL_MODE_ECB;
break; break;
case CRYPT_AES_MODE_CTR:
$this->mode = CRYPT_RIJNDAEL_MODE_CTR;
break;
case CRYPT_AES_MODE_CBC: case CRYPT_AES_MODE_CBC:
default: default:
$this->mode = CRYPT_RIJNDAEL_MODE_CBC; $this->mode = CRYPT_RIJNDAEL_MODE_CBC;
@ -203,18 +236,26 @@ class Crypt_AES extends Crypt_Rijndael {
{ {
if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) { if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
$this->_mcryptSetup(); $this->_mcryptSetup();
$plaintext = $this->_pad($plaintext); /*
if ($this->mode == CRYPT_AES_MODE_CTR) {
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, $this->mcrypt[0], $this->mode, $this->mcrypt[1]); $iv = $this->encryptIV;
mcrypt_generic_init($td, $this->key, $this->encryptIV); $xor = mcrypt_generic($this->enmcrypt, $this->_generate_xor(strlen($plaintext), $iv));
$ciphertext = $plaintext ^ $xor;
$ciphertext = mcrypt_generic($td, $plaintext);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
if ($this->continuousBuffer) { if ($this->continuousBuffer) {
$this->encryptIV = substr($ciphertext, -16); $this->encryptIV = $iv;
}
return $ciphertext;
}
*/
if ($this->mode != 'ctr') {
$plaintext = $this->_pad($plaintext);
}
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
} }
return $ciphertext; return $ciphertext;
@ -234,46 +275,38 @@ class Crypt_AES extends Crypt_Rijndael {
*/ */
function decrypt($ciphertext) function decrypt($ciphertext)
{ {
if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
$this->_mcryptSetup();
/*
if ($this->mode == CRYPT_AES_MODE_CTR) {
$iv = $this->decryptIV;
$xor = mcrypt_generic($this->enmcrypt, $this->_generate_xor(strlen($ciphertext), $iv));
$plaintext = $ciphertext ^ $xor;
if ($this->continuousBuffer) {
$this->decryptIV = $iv;
}
return $plaintext;
}
*/
if ($this->mode != 'ctr') {
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : // 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));
if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
$this->_mcryptSetup();
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, $this->mcrypt[0], $this->mode, $this->mcrypt[1]);
mcrypt_generic_init($td, $this->key, $this->decryptIV);
$plaintext = mdecrypt_generic($td, $ciphertext);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
if ($this->continuousBuffer) {
$this->decryptIV = substr($ciphertext, -16);
} }
return $this->_unpad($plaintext); $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
}
return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext;
} }
return parent::decrypt($ciphertext); return parent::decrypt($ciphertext);
} }
/**
* Sets MCrypt parameters. (optional)
*
* If MCrypt is being used, empty strings will be used, unless otherwise specified.
*
* @link http://php.net/function.mcrypt-module-open#function.mcrypt-module-open
* @access public
* @param optional Integer $algorithm_directory
* @param optional Integer $mode_directory
*/
function setMCrypt($algorithm_directory = '', $mode_directory = '')
{
$this->mcrypt = array($algorithm_directory, $mode_directory);
}
/** /**
* Setup mcrypt * Setup mcrypt
* *
@ -315,6 +348,17 @@ class Crypt_AES extends Crypt_Rijndael {
$this->key = substr($this->key, 0, $this->key_size); $this->key = substr($this->key, 0, $this->key_size);
$this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, 16), 16, chr(0)); $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, 16), 16, chr(0));
if (!isset($this->enmcrypt)) {
$mode = $this->mode;
//$mode = $this->mode == CRYPT_AES_MODE_CTR ? MCRYPT_MODE_ECB : $this->mode;
$this->demcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
$this->enmcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
} // else should mcrypt_generic_deinit be called?
mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
$this->changed = false; $this->changed = false;
} }
@ -332,12 +376,19 @@ class Crypt_AES extends Crypt_Rijndael {
{ {
$state = unpack('N*word', $in); $state = unpack('N*word', $in);
$Nr = $this->Nr;
$w = $this->w;
$t0 = $this->t0;
$t1 = $this->t1;
$t2 = $this->t2;
$t3 = $this->t3;
// addRoundKey and reindex $state // addRoundKey and reindex $state
$state = array( $state = array(
$state['word1'] ^ $this->w[0][0], $state['word1'] ^ $w[0][0],
$state['word2'] ^ $this->w[0][1], $state['word2'] ^ $w[0][1],
$state['word3'] ^ $this->w[0][2], $state['word3'] ^ $w[0][2],
$state['word4'] ^ $this->w[0][3] $state['word4'] ^ $w[0][3]
); );
// shiftRows + subWord + mixColumns + addRoundKey // shiftRows + subWord + mixColumns + addRoundKey
@ -345,10 +396,10 @@ class Crypt_AES extends Crypt_Rijndael {
// only a marginal improvement. since that also, imho, hinders the readability of the code, i've opted not to do it. // only a marginal improvement. since that also, imho, hinders the readability of the code, i've opted not to do it.
for ($round = 1; $round < $this->Nr; $round++) { for ($round = 1; $round < $this->Nr; $round++) {
$state = array( $state = array(
$this->t0[$state[0] & 0xFF000000] ^ $this->t1[$state[1] & 0x00FF0000] ^ $this->t2[$state[2] & 0x0000FF00] ^ $this->t3[$state[3] & 0x000000FF] ^ $this->w[$round][0], $t0[$state[0] & 0xFF000000] ^ $t1[$state[1] & 0x00FF0000] ^ $t2[$state[2] & 0x0000FF00] ^ $t3[$state[3] & 0x000000FF] ^ $w[$round][0],
$this->t0[$state[1] & 0xFF000000] ^ $this->t1[$state[2] & 0x00FF0000] ^ $this->t2[$state[3] & 0x0000FF00] ^ $this->t3[$state[0] & 0x000000FF] ^ $this->w[$round][1], $t0[$state[1] & 0xFF000000] ^ $t1[$state[2] & 0x00FF0000] ^ $t2[$state[3] & 0x0000FF00] ^ $t3[$state[0] & 0x000000FF] ^ $w[$round][1],
$this->t0[$state[2] & 0xFF000000] ^ $this->t1[$state[3] & 0x00FF0000] ^ $this->t2[$state[0] & 0x0000FF00] ^ $this->t3[$state[1] & 0x000000FF] ^ $this->w[$round][2], $t0[$state[2] & 0xFF000000] ^ $t1[$state[3] & 0x00FF0000] ^ $t2[$state[0] & 0x0000FF00] ^ $t3[$state[1] & 0x000000FF] ^ $w[$round][2],
$this->t0[$state[3] & 0xFF000000] ^ $this->t1[$state[0] & 0x00FF0000] ^ $this->t2[$state[1] & 0x0000FF00] ^ $this->t3[$state[2] & 0x000000FF] ^ $this->w[$round][3] $t0[$state[3] & 0xFF000000] ^ $t1[$state[0] & 0x00FF0000] ^ $t2[$state[1] & 0x0000FF00] ^ $t3[$state[2] & 0x000000FF] ^ $w[$round][3]
); );
} }
@ -386,31 +437,38 @@ class Crypt_AES extends Crypt_Rijndael {
{ {
$state = unpack('N*word', $in); $state = unpack('N*word', $in);
$Nr = $this->Nr;
$dw = $this->dw;
$dt0 = $this->dt0;
$dt1 = $this->dt1;
$dt2 = $this->dt2;
$dt3 = $this->dt3;
// addRoundKey and reindex $state // addRoundKey and reindex $state
$state = array( $state = array(
$state['word1'] ^ $this->dw[$this->Nr][0], $state['word1'] ^ $dw[$this->Nr][0],
$state['word2'] ^ $this->dw[$this->Nr][1], $state['word2'] ^ $dw[$this->Nr][1],
$state['word3'] ^ $this->dw[$this->Nr][2], $state['word3'] ^ $dw[$this->Nr][2],
$state['word4'] ^ $this->dw[$this->Nr][3] $state['word4'] ^ $dw[$this->Nr][3]
); );
// invShiftRows + invSubBytes + invMixColumns + addRoundKey // invShiftRows + invSubBytes + invMixColumns + addRoundKey
for ($round = $this->Nr - 1; $round > 0; $round--) { for ($round = $this->Nr - 1; $round > 0; $round--) {
$state = array( $state = array(
$this->dt0[$state[0] & 0xFF000000] ^ $this->dt1[$state[3] & 0x00FF0000] ^ $this->dt2[$state[2] & 0x0000FF00] ^ $this->dt3[$state[1] & 0x000000FF] ^ $this->dw[$round][0], $dt0[$state[0] & 0xFF000000] ^ $dt1[$state[3] & 0x00FF0000] ^ $dt2[$state[2] & 0x0000FF00] ^ $dt3[$state[1] & 0x000000FF] ^ $dw[$round][0],
$this->dt0[$state[1] & 0xFF000000] ^ $this->dt1[$state[0] & 0x00FF0000] ^ $this->dt2[$state[3] & 0x0000FF00] ^ $this->dt3[$state[2] & 0x000000FF] ^ $this->dw[$round][1], $dt0[$state[1] & 0xFF000000] ^ $dt1[$state[0] & 0x00FF0000] ^ $dt2[$state[3] & 0x0000FF00] ^ $dt3[$state[2] & 0x000000FF] ^ $dw[$round][1],
$this->dt0[$state[2] & 0xFF000000] ^ $this->dt1[$state[1] & 0x00FF0000] ^ $this->dt2[$state[0] & 0x0000FF00] ^ $this->dt3[$state[3] & 0x000000FF] ^ $this->dw[$round][2], $dt0[$state[2] & 0xFF000000] ^ $dt1[$state[1] & 0x00FF0000] ^ $dt2[$state[0] & 0x0000FF00] ^ $dt3[$state[3] & 0x000000FF] ^ $dw[$round][2],
$this->dt0[$state[3] & 0xFF000000] ^ $this->dt1[$state[2] & 0x00FF0000] ^ $this->dt2[$state[1] & 0x0000FF00] ^ $this->dt3[$state[0] & 0x000000FF] ^ $this->dw[$round][3] $dt0[$state[3] & 0xFF000000] ^ $dt1[$state[2] & 0x00FF0000] ^ $dt2[$state[1] & 0x0000FF00] ^ $dt3[$state[0] & 0x000000FF] ^ $dw[$round][3]
); );
} }
// invShiftRows + invSubWord + addRoundKey // invShiftRows + invSubWord + addRoundKey
$state = array( $state = array(
$this->_invSubWord(($state[0] & 0xFF000000) ^ ($state[3] & 0x00FF0000) ^ ($state[2] & 0x0000FF00) ^ ($state[1] & 0x000000FF)) ^ $this->dw[0][0], $this->_invSubWord(($state[0] & 0xFF000000) ^ ($state[3] & 0x00FF0000) ^ ($state[2] & 0x0000FF00) ^ ($state[1] & 0x000000FF)) ^ $dw[0][0],
$this->_invSubWord(($state[1] & 0xFF000000) ^ ($state[0] & 0x00FF0000) ^ ($state[3] & 0x0000FF00) ^ ($state[2] & 0x000000FF)) ^ $this->dw[0][1], $this->_invSubWord(($state[1] & 0xFF000000) ^ ($state[0] & 0x00FF0000) ^ ($state[3] & 0x0000FF00) ^ ($state[2] & 0x000000FF)) ^ $dw[0][1],
$this->_invSubWord(($state[2] & 0xFF000000) ^ ($state[1] & 0x00FF0000) ^ ($state[0] & 0x0000FF00) ^ ($state[3] & 0x000000FF)) ^ $this->dw[0][2], $this->_invSubWord(($state[2] & 0xFF000000) ^ ($state[1] & 0x00FF0000) ^ ($state[0] & 0x0000FF00) ^ ($state[3] & 0x000000FF)) ^ $dw[0][2],
$this->_invSubWord(($state[3] & 0xFF000000) ^ ($state[2] & 0x00FF0000) ^ ($state[1] & 0x0000FF00) ^ ($state[0] & 0x000000FF)) ^ $this->dw[0][3] $this->_invSubWord(($state[3] & 0xFF000000) ^ ($state[2] & 0x00FF0000) ^ ($state[1] & 0x0000FF00) ^ ($state[0] & 0x000000FF)) ^ $dw[0][3]
); );
return pack('N*', $state[0], $state[1], $state[2], $state[3]); return pack('N*', $state[0], $state[1], $state[2], $state[3]);

View File

@ -53,7 +53,7 @@
* @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.gnu.org/licenses/lgpl.txt
* @version $Id: DES.php,v 1.9 2009/11/23 19:06:06 terrafrost Exp $ * @version $Id: DES.php,v 1.12 2010/02/09 06:10:26 terrafrost Exp $
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -77,6 +77,14 @@ define('CRYPT_DES_DECRYPT', 1);
* @see Crypt_DES::encrypt() * @see Crypt_DES::encrypt()
* @see Crypt_DES::decrypt() * @see Crypt_DES::decrypt()
*/ */
/**
* Encrypt / decrypt using the Counter mode.
*
* Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
*/
define('CRYPT_DES_MODE_CTR', -1);
/** /**
* Encrypt / decrypt using the Electronic Code Book mode. * Encrypt / decrypt using the Electronic Code Book mode.
* *
@ -178,13 +186,38 @@ class Crypt_DES {
var $decryptIV = "\0\0\0\0\0\0\0\0"; var $decryptIV = "\0\0\0\0\0\0\0\0";
/** /**
* MCrypt parameters * mcrypt resource for encryption
* *
* @see Crypt_DES::setMCrypt() * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
* @var Array * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
*
* @see Crypt_AES::encrypt()
* @var String
* @access private * @access private
*/ */
var $mcrypt = array('', ''); var $enmcrypt;
/**
* mcrypt resource for decryption
*
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
*
* @see Crypt_AES::decrypt()
* @var String
* @access private
*/
var $demcrypt;
/**
* Does the (en|de)mcrypt resource need to be (re)initialized?
*
* @see setKey()
* @see setIV()
* @var Boolean
* @access private
*/
var $changed = true;
/** /**
* Default Constructor. * Default Constructor.
@ -217,6 +250,10 @@ class Crypt_DES {
case CRYPT_DES_MODE_ECB: case CRYPT_DES_MODE_ECB:
$this->mode = MCRYPT_MODE_ECB; $this->mode = MCRYPT_MODE_ECB;
break; break;
case CRYPT_DES_MODE_CTR:
$this->mode = 'ctr';
//$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_DES_MODE_CTR;
break;
case CRYPT_DES_MODE_CBC: case CRYPT_DES_MODE_CBC:
default: default:
$this->mode = MCRYPT_MODE_CBC; $this->mode = MCRYPT_MODE_CBC;
@ -226,6 +263,7 @@ 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->mode = $mode; $this->mode = $mode;
break; break;
@ -252,6 +290,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 ) ? substr($key, 0, 8) : $this->_prepareKey($key);
$this->changed = true;
} }
/** /**
@ -265,22 +304,46 @@ class Crypt_DES {
*/ */
function setIV($iv) function setIV($iv)
{ {
$this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0));; $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0));
$this->changed = true;
} }
/** /**
* Sets MCrypt parameters. (optional) * Generate CTR XOR encryption key
* *
* If MCrypt is being used, empty strings will be used, unless otherwise specified. * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
* plaintext / ciphertext in CTR mode.
* *
* @link http://php.net/function.mcrypt-module-open#function.mcrypt-module-open * @see Crypt_DES::decrypt()
* @see Crypt_DES::encrypt()
* @access public * @access public
* @param optional Integer $algorithm_directory * @param Integer $length
* @param optional Integer $mode_directory * @param String $iv
*/ */
function setMCrypt($algorithm_directory = '', $mode_directory = '') function _generate_xor($length, &$iv)
{ {
$this->mcrypt = array($algorithm_directory, $mode_directory); $xor = '';
$num_blocks = ($length + 7) >> 3;
for ($i = 0; $i < $num_blocks; $i++) {
$xor.= $iv;
for ($j = 4; $j <= 8; $j+=4) {
$temp = substr($iv, -$j, 4);
switch ($temp) {
case "\xFF\xFF\xFF\xFF":
$iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
break;
case "\x7F\xFF\xFF\xFF":
$iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
break 2;
default:
extract(unpack('Ncount', $temp));
$iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
break 2;
}
}
}
return $xor;
} }
/** /**
@ -302,19 +365,23 @@ class Crypt_DES {
*/ */
function encrypt($plaintext) function encrypt($plaintext)
{ {
if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') {
$plaintext = $this->_pad($plaintext); $plaintext = $this->_pad($plaintext);
}
if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
$td = mcrypt_module_open(MCRYPT_DES, $this->mcrypt[0], $this->mode, $this->mcrypt[1]); if ($this->changed) {
mcrypt_generic_init($td, $this->keys, $this->encryptIV); if (!isset($this->enmcrypt)) {
$this->enmcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, '');
}
mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV);
$this->changed = false;
}
$ciphertext = mcrypt_generic($td, $plaintext); $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
mcrypt_generic_deinit($td); if (!$this->continuousBuffer) {
mcrypt_module_close($td); mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV);
if ($this->continuousBuffer) {
$this->encryptIV = substr($ciphertext, -8);
} }
return $ciphertext; return $ciphertext;
@ -342,6 +409,17 @@ class Crypt_DES {
if ($this->continuousBuffer) { if ($this->continuousBuffer) {
$this->encryptIV = $xor; $this->encryptIV = $xor;
} }
break;
case CRYPT_DES_MODE_CTR:
$xor = $this->encryptIV;
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$block = substr($plaintext, $i, 8);
$key = $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT);
$ciphertext.= $block ^ $key;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
}
} }
return $ciphertext; return $ciphertext;
@ -358,24 +436,28 @@ class Crypt_DES {
*/ */
function decrypt($ciphertext) function decrypt($ciphertext)
{ {
if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') {
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : // 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 ) {
$td = mcrypt_module_open(MCRYPT_DES, $this->mcrypt[0], $this->mode, $this->mcrypt[1]);
mcrypt_generic_init($td, $this->keys, $this->decryptIV);
$plaintext = mdecrypt_generic($td, $ciphertext);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
if ($this->continuousBuffer) {
$this->decryptIV = substr($ciphertext, -8);
} }
return $this->_unpad($plaintext); if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
if ($this->changed) {
if (!isset($this->demcrypt)) {
$this->demcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, '');
}
mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV);
$this->changed = false;
}
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV);
}
return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext;
} }
if (!is_array($this->keys)) { if (!is_array($this->keys)) {
@ -399,9 +481,20 @@ class Crypt_DES {
if ($this->continuousBuffer) { if ($this->continuousBuffer) {
$this->decryptIV = $xor; $this->decryptIV = $xor;
} }
break;
case CRYPT_DES_MODE_CTR:
$xor = $this->decryptIV;
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$block = substr($ciphertext, $i, 8);
$key = $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT);
$plaintext.= $block ^ $key;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
}
} }
return $this->_unpad($plaintext); return $this->mode != CRYPT_DES_MODE_CTR ? $this->_unpad($plaintext) : $plaintext;
} }
/** /**
@ -523,7 +616,8 @@ class Crypt_DES {
/** /**
* Unpads a string * Unpads a string
* *
* If padding is enabled and the reported padding length is invalid, padding will be, hence forth, disabled. * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
* and false will be returned.
* *
* @see Crypt_DES::_pad() * @see Crypt_DES::_pad()
* @access private * @access private
@ -537,9 +631,7 @@ class Crypt_DES {
$length = ord($text[strlen($text) - 1]); $length = ord($text[strlen($text) - 1]);
if (!$length || $length > 8) { if (!$length || $length > 8) {
user_error("The number of bytes reported as being padded ($length) is invalid (block size = 8)", E_USER_NOTICE); return false;
$this->padding = false;
return $text;
} }
return substr($text, 0, -$length); return substr($text, 0, -$length);
@ -614,6 +706,8 @@ class Crypt_DES {
) )
); );
$keys = $this->keys;
$temp = unpack('Na/Nb', $block); $temp = unpack('Na/Nb', $block);
$block = array($temp['a'], $temp['b']); $block = array($temp['a'], $temp['b']);
@ -668,14 +762,14 @@ class Crypt_DES {
for ($i = 0; $i < 16; $i++) { for ($i = 0; $i < 16; $i++) {
// start of "the Feistel (F) function" - see the following URL: // start of "the Feistel (F) function" - see the following URL:
// http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png // http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png
$temp = (($sbox[0][((($block[1] >> 27) & 0x1F) | (($block[1] & 1) << 5)) ^ $this->keys[$mode][$i][0]]) << 28) $temp = (($sbox[0][((($block[1] >> 27) & 0x1F) | (($block[1] & 1) << 5)) ^ $keys[$mode][$i][0]]) << 28)
| (($sbox[1][(($block[1] & 0x1F800000) >> 23) ^ $this->keys[$mode][$i][1]]) << 24) | (($sbox[1][(($block[1] & 0x1F800000) >> 23) ^ $keys[$mode][$i][1]]) << 24)
| (($sbox[2][(($block[1] & 0x01F80000) >> 19) ^ $this->keys[$mode][$i][2]]) << 20) | (($sbox[2][(($block[1] & 0x01F80000) >> 19) ^ $keys[$mode][$i][2]]) << 20)
| (($sbox[3][(($block[1] & 0x001F8000) >> 15) ^ $this->keys[$mode][$i][3]]) << 16) | (($sbox[3][(($block[1] & 0x001F8000) >> 15) ^ $keys[$mode][$i][3]]) << 16)
| (($sbox[4][(($block[1] & 0x0001F800) >> 11) ^ $this->keys[$mode][$i][4]]) << 12) | (($sbox[4][(($block[1] & 0x0001F800) >> 11) ^ $keys[$mode][$i][4]]) << 12)
| (($sbox[5][(($block[1] & 0x00001F80) >> 7) ^ $this->keys[$mode][$i][5]]) << 8) | (($sbox[5][(($block[1] & 0x00001F80) >> 7) ^ $keys[$mode][$i][5]]) << 8)
| (($sbox[6][(($block[1] & 0x000001F8) >> 3) ^ $this->keys[$mode][$i][6]]) << 4) | (($sbox[6][(($block[1] & 0x000001F8) >> 3) ^ $keys[$mode][$i][6]]) << 4)
| ( $sbox[7][((($block[1] & 0x1F) << 1) | (($block[1] >> 31) & 1)) ^ $this->keys[$mode][$i][7]]); | ( $sbox[7][((($block[1] & 0x1F) << 1) | (($block[1] >> 31) & 1)) ^ $keys[$mode][$i][7]]);
$msb = ($temp >> 31) & 1; $msb = ($temp >> 31) & 1;
$temp &= 0x7FFFFFFF; $temp &= 0x7FFFFFFF;

View File

@ -62,7 +62,7 @@
* @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.gnu.org/licenses/lgpl.txt
* @version $Id: RSA.php,v 1.3 2009/12/04 21:05:32 terrafrost Exp $ * @version $Id: RSA.php,v 1.14 2010/03/01 17:28:19 terrafrost Exp $
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -332,6 +332,14 @@ class Crypt_RSA {
*/ */
var $mgfHash; var $mgfHash;
/**
* Length of MGF hash function output
*
* @var Integer
* @access private
*/
var $mgfHLen;
/** /**
* Encryption mode * Encryption mode
* *
@ -393,6 +401,7 @@ class Crypt_RSA {
$this->hLen = $this->hash->getLength(); $this->hLen = $this->hash->getLength();
$this->hashName = 'sha1'; $this->hashName = 'sha1';
$this->mgfHash = new Crypt_Hash('sha1'); $this->mgfHash = new Crypt_Hash('sha1');
$this->mgfHLen = $this->mgfHash->getLength();
} }
/** /**
@ -409,7 +418,7 @@ class Crypt_RSA {
* @param optional Integer $timeout * @param optional Integer $timeout
* @param optional Math_BigInteger $p * @param optional Math_BigInteger $p
*/ */
function createKey($bits = 1024, $timeout = false, $primes = array()) function createKey($bits = 1024, $timeout = false, $partial = array())
{ {
if ( CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL ) { if ( CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL ) {
$rsa = openssl_pkey_new(array('private_key_bits' => $bits)); $rsa = openssl_pkey_new(array('private_key_bits' => $bits));
@ -460,15 +469,19 @@ class Crypt_RSA {
$finalMax = $max; $finalMax = $max;
extract($this->_generateMinMax($temp)); extract($this->_generateMinMax($temp));
$exponents = $coefficients = array();
$generator = new Math_BigInteger(); $generator = new Math_BigInteger();
$generator->setRandomGenerator('crypt_random'); $generator->setRandomGenerator('crypt_random');
$n = $this->one->copy(); $n = $this->one->copy();
if (!empty($partial)) {
extract(unserialize($partial));
} else {
$exponents = $coefficients = $primes = array();
$lcm = array( $lcm = array(
'top' => $this->one->copy(), 'top' => $this->one->copy(),
'bottom' => false 'bottom' => false
); );
}
$start = time(); $start = time();
$i0 = count($primes) + 1; $i0 = count($primes) + 1;
@ -479,13 +492,19 @@ class Crypt_RSA {
$timeout-= time() - $start; $timeout-= time() - $start;
$start = time(); $start = time();
if ($timeout <= 0) { if ($timeout <= 0) {
return array( return serialize(array(
'privatekey' => '', 'privatekey' => '',
'publickey' => '', 'publickey' => '',
'partialkey' => $primes 'partialkey' => array(
); 'primes' => $primes,
'coefficients' => $coefficients,
'lcm' => $lcm,
'exponents' => $exponents
)
));
} }
} }
if ($i == $num_primes) { if ($i == $num_primes) {
list($min, $temp) = $absoluteMin->divide($n); list($min, $temp) = $absoluteMin->divide($n);
if (!$temp->equals($this->zero)) { if (!$temp->equals($this->zero)) {
@ -500,7 +519,12 @@ class Crypt_RSA {
return array( return array(
'privatekey' => '', 'privatekey' => '',
'publickey' => '', 'publickey' => '',
'partialkey' => array_slice($primes, 0, $i - 1) 'partialkey' => empty($primes) ? '' : serialize(array(
'primes' => array_slice($primes, 0, $i - 1),
'coefficients' => $coefficients,
'lcm' => $lcm,
'exponents' => $exponents
))
); );
} }
@ -563,7 +587,6 @@ class Crypt_RSA {
function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients) function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
{ {
$num_primes = count($primes); $num_primes = count($primes);
$raw = array( $raw = array(
'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi 'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
'modulus' => $n->toBytes(true), 'modulus' => $n->toBytes(true),
@ -745,34 +768,80 @@ class Crypt_RSA {
implementation are part of the standard, as well. implementation are part of the standard, as well.
* 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: DES-EDE3-CBC,(.+)#', $key, $matches)) { if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
$iv = pack('H*', trim($matches[1])); $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 . $iv)); // 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 = base64_decode(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;
if ($ciphertext === false) { if ($ciphertext === false) {
return false; $ciphertext = $key;
} }
switch ($matches[1]) {
case 'DES-EDE3-CBC':
if (!class_exists('Crypt_TripleDES')) { if (!class_exists('Crypt_TripleDES')) {
require_once('Crypt/TripleDES.php'); require_once('Crypt/TripleDES.php');
} }
$des = new Crypt_TripleDES(); $crypto = new Crypt_TripleDES();
$des->setKey($symkey); break;
$des->setIV($iv); case 'DES-CBC':
$key = $des->decrypt($ciphertext); if (!class_exists('Crypt_DES')) {
} else { require_once('Crypt/DES.php');
$key = base64_decode(preg_replace('#-.+-|[\r\n]#', '', $key)); }
if ($key === false) { $crypto = new Crypt_DES();
break;
default:
return false; return false;
} }
$crypto->setKey($symkey);
$crypto->setIV($iv);
$decoded = $crypto->decrypt($ciphertext);
} else {
$decoded = preg_replace('#-.+-|[\r\n]#', '', $key);
$decoded = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $decoded) ? base64_decode($decoded) : false;
}
if ($decoded !== false) {
$key = $decoded;
} }
$private = false;
$components = array(); $components = array();
$this->_string_shift($key); // skip over CRYPT_RSA_ASN1_SEQUENCE if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
$this->_decodeLength($key); // skip over the length of the above sequence return false;
$this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER }
if ($this->_decodeLength($key) != strlen($key)) {
return false;
}
$tag = ord($this->_string_shift($key));
if ($tag == CRYPT_RSA_ASN1_SEQUENCE) {
/* intended for keys for which OpenSSL's asn1parse returns the following:
0:d=0 hl=4 l= 290 cons: SEQUENCE
4:d=1 hl=2 l= 13 cons: SEQUENCE
6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
17:d=2 hl=2 l= 0 prim: NULL
19:d=1 hl=4 l= 271 prim: BIT STRING */
$this->_string_shift($key, $this->_decodeLength($key));
$this->_string_shift($key); // skip over the BIT STRING tag
$this->_decodeLength($key); // skip over the BIT STRING length
// "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
// unused bits in teh final subsequent octet. The number shall be in the range zero to seven."
// -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
$this->_string_shift($key);
if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
return false;
}
if ($this->_decodeLength($key) != strlen($key)) {
return false;
}
$tag = ord($this->_string_shift($key));
}
if ($tag != CRYPT_RSA_ASN1_INTEGER) {
return false;
}
$length = $this->_decodeLength($key); $length = $this->_decodeLength($key);
$temp = $this->_string_shift($key, $length); $temp = $this->_string_shift($key, $length);
if (strlen($temp) != 1 || ord($temp) > 2) { if (strlen($temp) != 1 || ord($temp) > 2) {
@ -783,7 +852,9 @@ class Crypt_RSA {
return $components; return $components;
} }
$this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_INTEGER) {
return false;
}
$length = $this->_decodeLength($key); $length = $this->_decodeLength($key);
$components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), -256); $components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
$this->_string_shift($key); $this->_string_shift($key);
@ -807,11 +878,16 @@ class Crypt_RSA {
$this->_string_shift($key); $this->_string_shift($key);
$length = $this->_decodeLength($key); $length = $this->_decodeLength($key);
$components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($key, $length), -256)); $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($key, $length), -256));
if (!empty($key)) { if (!empty($key)) {
$key = substr($key, 1); // skip over CRYPT_RSA_ASN1_SEQUENCE if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
return false;
}
$this->_decodeLength($key); $this->_decodeLength($key);
while (!empty($key)) { while (!empty($key)) {
$key = substr($key, 1); // skip over CRYPT_RSA_ASN1_SEQUENCE if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
return false;
}
$this->_decodeLength($key); $this->_decodeLength($key);
$key = substr($key, 1); $key = substr($key, 1);
$length = $this->_decodeLength($key); $length = $this->_decodeLength($key);
@ -832,19 +908,33 @@ class Crypt_RSA {
return false; return false;
} }
$components = array(); $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
extract(unpack('Nlength', $this->_string_shift($key, 4)));
$components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
extract(unpack('Nlength', $this->_string_shift($key, 4)));
$components['publicExponent'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
return $components; extract(unpack('Nlength', $this->_string_shift($key, 4)));
$publicExponent = new Math_BigInteger($this->_string_shift($key, $length), -256);
extract(unpack('Nlength', $this->_string_shift($key, 4)));
$modulus = new Math_BigInteger($this->_string_shift($key, $length), -256);
if ($cleanup && strlen($key)) {
extract(unpack('Nlength', $this->_string_shift($key, 4)));
return array(
'modulus' => new Math_BigInteger($this->_string_shift($key, $length), -256),
'publicExponent' => $modulus
);
} else {
return array(
'modulus' => $modulus,
'publicExponent' => $publicExponent
);
}
} }
} }
/** /**
* Loads a public or private key * Loads a public or private key
* *
* Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
*
* @access public * @access public
* @param String $key * @param String $key
* @param Integer $type optional * @param Integer $type optional
@ -852,6 +942,10 @@ class Crypt_RSA {
function loadKey($key, $type = CRYPT_RSA_PRIVATE_FORMAT_PKCS1) function loadKey($key, $type = CRYPT_RSA_PRIVATE_FORMAT_PKCS1)
{ {
$components = $this->_parseKey($key, $type); $components = $this->_parseKey($key, $type);
if ($components === false) {
return false;
}
$this->modulus = $components['modulus']; $this->modulus = $components['modulus'];
$this->k = strlen($this->modulus->toBytes()); $this->k = strlen($this->modulus->toBytes());
$this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent']; $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
@ -866,6 +960,8 @@ class Crypt_RSA {
$this->coefficients = array(); $this->coefficients = array();
$this->publicExponent = false; $this->publicExponent = false;
} }
return true;
} }
/** /**
@ -906,7 +1002,7 @@ 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 (!$this->modulus->equals($components['modulus'])) { if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
return false; return false;
} }
$this->publicExponent = $components['publicExponent']; $this->publicExponent = $components['publicExponent'];
@ -926,6 +1022,10 @@ class Crypt_RSA {
*/ */
function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1) function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
{ {
if (empty($this->modulus) || empty($this->publicExponent)) {
return false;
}
$oldFormat = $this->publicKeyFormat; $oldFormat = $this->publicKeyFormat;
$this->publicKeyFormat = $type; $this->publicKeyFormat = $type;
$temp = $this->_convertPublicKey($this->modulus, $this->publicExponent); $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
@ -945,7 +1045,7 @@ class Crypt_RSA {
$bytes = $bits >> 3; $bytes = $bits >> 3;
$min = str_repeat(chr(0), $bytes); $min = str_repeat(chr(0), $bytes);
$max = str_repeat(chr(0xFF), $bytes); $max = str_repeat(chr(0xFF), $bytes);
$msb = $num_bits & 7; $msb = $bits & 7;
if ($msb) { if ($msb) {
$min = chr(1 << ($msb - 1)) . $min; $min = chr(1 << ($msb - 1)) . $min;
$max = chr((1 << $msb) - 1) . $max; $max = chr((1 << $msb) - 1) . $max;
@ -975,7 +1075,6 @@ class Crypt_RSA {
if ( $length & 0x80 ) { // definite length, long form if ( $length & 0x80 ) { // definite length, long form
$length&= 0x7F; $length&= 0x7F;
$temp = $this->_string_shift($string, $length); $temp = $this->_string_shift($string, $length);
$start+= $length;
list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4)); list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
} }
return $length; return $length;
@ -1062,14 +1161,13 @@ class Crypt_RSA {
case 'sha384': case 'sha384':
case 'sha512': case 'sha512':
$this->hash = new Crypt_Hash($hash); $this->hash = new Crypt_Hash($hash);
$this->hLen = $this->hash->getLength();
$this->hashName = $hash; $this->hashName = $hash;
break; break;
default: default:
$this->hash = new Crypt_Hash('sha1'); $this->hash = new Crypt_Hash('sha1');
$this->hLen = $this->hash->getLength();
$this->hashName = 'sha1'; $this->hashName = 'sha1';
} }
$this->hLen = $this->hash->getLength();
} }
/** /**
@ -1096,6 +1194,7 @@ class Crypt_RSA {
default: default:
$this->mgfHash = new Crypt_Hash('sha1'); $this->mgfHash = new Crypt_Hash('sha1');
} }
$this->mgfHLen = $this->mgfHash->getLength();
} }
/** /**
@ -1189,6 +1288,8 @@ class Crypt_RSA {
} }
$num_primes = count($this->primes); $num_primes = count($this->primes);
if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
$m_i = array( $m_i = array(
1 => $x->modPow($this->exponents[1], $this->primes[1]), 1 => $x->modPow($this->exponents[1], $this->primes[1]),
2 => $x->modPow($this->exponents[2], $this->primes[2]) 2 => $x->modPow($this->exponents[2], $this->primes[2])
@ -1210,10 +1311,70 @@ class Crypt_RSA {
$m = $m->add($r->multiply($h)); $m = $m->add($r->multiply($h));
} }
} else {
$smallest = $this->primes[1];
for ($i = 2; $i <= $num_primes; $i++) {
if ($smallest->compare($this->primes[$i]) > 0) {
$smallest = $this->primes[$i];
}
}
$one = new Math_BigInteger(1);
$one->setRandomGenerator('crypt_random');
$r = $one->random($one, $smallest->subtract($one));
$m_i = array(
1 => $this->_blind($x, $r, 1),
2 => $this->_blind($x, $r, 2)
);
$h = $m_i[1]->subtract($m_i[2]);
$h = $h->multiply($this->coefficients[2]);
list(, $h) = $h->divide($this->primes[1]);
$m = $m_i[2]->add($h->multiply($this->primes[2]));
$r = $this->primes[1];
for ($i = 3; $i <= $num_primes; $i++) {
$m_i = $this->_blind($x, $r, $i);
$r = $r->multiply($this->primes[$i - 1]);
$h = $m_i->subtract($m);
$h = $h->multiply($this->coefficients[$i]);
list(, $h) = $h->divide($this->primes[$i]);
$m = $m->add($r->multiply($h));
}
}
return $m; return $m;
} }
/**
* Performs RSA Blinding
*
* Protects against timing attacks by employing RSA Blinding.
* Returns $x->modPow($this->exponents[$i], $this->primes[$i])
*
* @access private
* @param Math_BigInteger $x
* @param Math_BigInteger $r
* @param Integer $i
* @return Math_BigInteger
*/
function _blind($x, $r, $i)
{
$x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
$x = $x->modPow($this->exponents[$i], $this->primes[$i]);
$r = $r->modInverse($this->primes[$i]);
$x = $x->multiply($r);
list(, $x) = $x->divide($this->primes[$i]);
return $x;
}
/** /**
* RSAEP * RSAEP
* *
@ -1289,7 +1450,7 @@ class Crypt_RSA {
/** /**
* MGF1 * MGF1
* *
* See {@link http://tools.ietf.org/html/rfc3447#section-B.2.1 RFC3447#section-B.2.1}. * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
* *
* @access private * @access private
* @param String $mgfSeed * @param String $mgfSeed
@ -1301,7 +1462,7 @@ class Crypt_RSA {
// if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output. // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
$t = ''; $t = '';
$count = ceil($maskLen / $this->hLen); $count = ceil($maskLen / $this->mgfHLen);
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$c = pack('N', $i); $c = pack('N', $i);
$t.= $this->mgfHash->hash($mgfSeed . $c); $t.= $this->mgfHash->hash($mgfSeed . $c);
@ -1789,7 +1950,7 @@ class Crypt_RSA {
// Compare // Compare
return $em == $em2; return $em === $em2;
} }
/** /**
@ -1834,7 +1995,12 @@ class Crypt_RSA {
{ {
switch ($this->encryptionMode) { switch ($this->encryptionMode) {
case CRYPT_RSA_ENCRYPTION_PKCS1: case CRYPT_RSA_ENCRYPTION_PKCS1:
$plaintext = str_split($plaintext, $this->k - 11); $length = $this->k - 11;
if ($length <= 0) {
return false;
}
$plaintext = str_split($plaintext, $length);
$ciphertext = ''; $ciphertext = '';
foreach ($plaintext as $m) { foreach ($plaintext as $m) {
$ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m); $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
@ -1842,7 +2008,12 @@ class Crypt_RSA {
return $ciphertext; return $ciphertext;
//case CRYPT_RSA_ENCRYPTION_OAEP: //case CRYPT_RSA_ENCRYPTION_OAEP:
default: default:
$plaintext = str_split($plaintext, $this->k - 2 * $this->hLen - 2); $length = $this->k - 2 * $this->hLen - 2;
if ($length <= 0) {
return false;
}
$plaintext = str_split($plaintext, $length);
$ciphertext = ''; $ciphertext = '';
foreach ($plaintext as $m) { foreach ($plaintext as $m) {
$ciphertext.= $this->_rsaes_oaep_encrypt($m); $ciphertext.= $this->_rsaes_oaep_encrypt($m);
@ -1861,32 +2032,32 @@ class Crypt_RSA {
*/ */
function decrypt($ciphertext) function decrypt($ciphertext)
{ {
if ($this->k <= 0) {
return false;
}
$ciphertext = str_split($ciphertext, $this->k);
$plaintext = '';
switch ($this->encryptionMode) { switch ($this->encryptionMode) {
case CRYPT_RSA_ENCRYPTION_PKCS1: case CRYPT_RSA_ENCRYPTION_PKCS1:
$ciphertext = str_split($ciphertext, $this->k); $decrypt = '_rsaes_pkcs1_v1_5_decrypt';
$plaintext = ''; break;
foreach ($ciphertext as $c) {
$temp = $this->_rsaes_pkcs1_v1_5_decrypt($c);
if ($temp === false) {
return false;
}
$plaintext.= $temp;
}
return $plaintext;
//case CRYPT_RSA_ENCRYPTION_OAEP: //case CRYPT_RSA_ENCRYPTION_OAEP:
default: default:
$ciphertext = str_split($ciphertext, $this->k); $decrypt = '_rsaes_oaep_decrypt';
$plaintext = ''; }
foreach ($ciphertext as $c) { foreach ($ciphertext as $c) {
$temp = $this->_rsaes_oaep_decrypt($c); $temp = $this->$decrypt($c);
if ($temp === false) { if ($temp === false) {
return false; return false;
} }
$plaintext.= $temp; $plaintext.= $temp;
} }
return $plaintext; return $plaintext;
} }
}
/** /**
* Create a signature * Create a signature
@ -1898,6 +2069,10 @@ class Crypt_RSA {
*/ */
function sign($message) function sign($message)
{ {
if (empty($this->modulus) || empty($this->exponent)) {
return false;
}
switch ($this->signatureMode) { switch ($this->signatureMode) {
case CRYPT_RSA_SIGNATURE_PKCS1: case CRYPT_RSA_SIGNATURE_PKCS1:
return $this->_rsassa_pkcs1_v1_5_sign($message); return $this->_rsassa_pkcs1_v1_5_sign($message);
@ -1918,6 +2093,10 @@ class Crypt_RSA {
*/ */
function verify($message, $signature) function verify($message, $signature)
{ {
if (empty($this->modulus) || empty($this->exponent)) {
return false;
}
switch ($this->signatureMode) { switch ($this->signatureMode) {
case CRYPT_RSA_SIGNATURE_PKCS1: case CRYPT_RSA_SIGNATURE_PKCS1:
return $this->_rsassa_pkcs1_v1_5_verify($message, $signature); return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);

View File

@ -35,36 +35,91 @@
* @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.gnu.org/licenses/lgpl.txt
* @version $Id: Random.php,v 1.4 2008/05/21 05:15:32 terrafrost Exp $ * @version $Id: Random.php,v 1.6 2010/02/28 05:28:38 terrafrost Exp $
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
/** /**
* Generate a random value. Feel free to replace this function with a cryptographically secure PRNG. * Generate a random value.
*
* On 32-bit machines, the largest distance that can exist between $min and $max is 2**31.
* If $min and $max are farther apart than that then the last ($max - range) numbers.
*
* Depending on how this is being used, it may be worth while to write a replacement. For example,
* a PHP-based web app that stores its data in an SQL database can collect more entropy than this function
* can.
* *
* @param optional Integer $min * @param optional Integer $min
* @param optional Integer $max * @param optional Integer $max
* @param optional String $randomness_path
* @return Integer * @return Integer
* @access public * @access public
*/ */
function crypt_random($min = 0, $max = 0x7FFFFFFF, $randomness_path = '/dev/urandom') function crypt_random($min = 0, $max = 0x7FFFFFFF)
{ {
static $seeded = false; if ($min == $max) {
return $min;
}
if (!$seeded) { // see http://en.wikipedia.org/wiki//dev/random
$seeded = true; if (file_exists('/dev/urandom')) {
if (file_exists($randomness_path)) { $fp = fopen('/dev/urandom', 'rb');
$fp = fopen($randomness_path, 'r'); extract(unpack('Nrandom', fread($fp, 4)));
$temp = unpack('Nint', fread($fp, 4));
mt_srand($temp['int']);
fclose($fp); fclose($fp);
} else {
list($sec, $usec) = explode(' ', microtime()); // say $min = 0 and $max = 3. if we didn't do abs() then we could have stuff like this:
mt_srand((float) $sec + ((float) $usec * 100000)); // -4 % 3 + 0 = -1, even though -1 < $min
return abs($random) % ($max - $min) + $min;
}
/* Prior to PHP 4.2.0, mt_srand() had to be called before mt_rand() could be called.
Prior to PHP 5.2.6, mt_rand()'s automatic seeding was subpar, as elaborated here:
http://www.suspekt.org/2008/08/17/mt_srand-and-not-so-random-numbers/
The seeding routine is pretty much ripped from PHP's own internal GENERATE_SEED() macro:
http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3_2/ext/standard/php_rand.h?view=markup */
if (version_compare(PHP_VERSION, '5.2.5', '<=')) {
static $seeded;
if (!isset($seeded)) {
$seeded = true;
mt_srand(fmod(time() * getmypid(), 0x7FFFFFFF) ^ fmod(1000000 * lcg_value(), 0x7FFFFFFF));
} }
} }
return mt_rand($min, $max); static $crypto;
// The CSPRNG's Yarrow and Fortuna periodically reseed. This function can be reseeded by hitting F5
// in the browser and reloading the page.
if (!isset($crypto)) {
$key = $iv = '';
for ($i = 0; $i < 8; $i++) {
$key.= pack('n', mt_rand(0, 0xFFFF));
$iv .= pack('n', mt_rand(0, 0xFFFF));
}
switch (true) {
case class_exists('Crypt_AES'):
$crypto = new Crypt_AES(CRYPT_AES_MODE_CTR);
break;
case class_exists('Crypt_TripleDES'):
$crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
break;
case class_exists('Crypt_DES'):
$crypto = new Crypt_DES(CRYPT_DES_MODE_CTR);
break;
case class_exists('Crypt_RC4'):
$crypto = new Crypt_RC4();
break;
default:
extract(unpack('Nrandom', pack('H*', sha1(mt_rand(0, 0x7FFFFFFF)))));
return abs($random) % ($max - $min) + $min;
}
$crypto->setKey($key);
$crypto->setIV($iv);
}
extract(unpack('Nrandom', $crypto->encrypt("\0\0\0\0")));
return abs($random) % ($max - $min) + $min;
} }
?> ?>

View File

@ -64,7 +64,7 @@
* @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.gnu.org/licenses/lgpl.txt
* @version $Id: Rijndael.php,v 1.8 2009/11/23 19:06:07 terrafrost Exp $ * @version $Id: Rijndael.php,v 1.12 2010/02/09 06:10:26 terrafrost Exp $
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -73,6 +73,14 @@
* @see Crypt_Rijndael::encrypt() * @see Crypt_Rijndael::encrypt()
* @see Crypt_Rijndael::decrypt() * @see Crypt_Rijndael::decrypt()
*/ */
/**
* Encrypt / decrypt using the Counter mode.
*
* Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
*/
define('CRYPT_RIJNDAEL_MODE_CTR', -1);
/** /**
* Encrypt / decrypt using the Electronic Code Book mode. * Encrypt / decrypt using the Electronic Code Book mode.
* *
@ -363,16 +371,27 @@ 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:
case CRYPT_RIJNDAEL_MODE_CTR:
$this->mode = $mode; $this->mode = $mode;
break; break;
default: default:
$this->mode = CRYPT_RIJNDAEL_MODE_CBC; $this->mode = CRYPT_RIJNDAEL_MODE_CBC;
} }
$t3 = &$this->t3;
$t2 = &$this->t2;
$t1 = &$this->t1;
$t0 = &$this->t0;
$dt3 = &$this->dt3;
$dt2 = &$this->dt2;
$dt1 = &$this->dt1;
$dt0 = &$this->dt0;
// according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19> (section 5.2.1), // according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19> (section 5.2.1),
// precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so // precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so
// those are the names we'll use. // those are the names we'll use.
$this->t3 = array( $t3 = array(
0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491, 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC, 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC,
0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB, 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
@ -407,7 +426,7 @@ class Crypt_Rijndael {
0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C
); );
$this->dt3 = array( $dt3 = array(
0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B, 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B,
0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5, 0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5,
0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B, 0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B,
@ -443,13 +462,13 @@ class Crypt_Rijndael {
); );
for ($i = 0; $i < 256; $i++) { for ($i = 0; $i < 256; $i++) {
$this->t2[$i << 8] = (($this->t3[$i] << 8) & 0xFFFFFF00) | (($this->t3[$i] >> 24) & 0x000000FF); $t2[$i << 8] = (($t3[$i] << 8) & 0xFFFFFF00) | (($t3[$i] >> 24) & 0x000000FF);
$this->t1[$i << 16] = (($this->t3[$i] << 16) & 0xFFFF0000) | (($this->t3[$i] >> 16) & 0x0000FFFF); $t1[$i << 16] = (($t3[$i] << 16) & 0xFFFF0000) | (($t3[$i] >> 16) & 0x0000FFFF);
$this->t0[$i << 24] = (($this->t3[$i] << 24) & 0xFF000000) | (($this->t3[$i] >> 8) & 0x00FFFFFF); $t0[$i << 24] = (($t3[$i] << 24) & 0xFF000000) | (($t3[$i] >> 8) & 0x00FFFFFF);
$this->dt2[$i << 8] = (($this->dt3[$i] << 8) & 0xFFFFFF00) | (($this->dt3[$i] >> 24) & 0x000000FF); $dt2[$i << 8] = (($this->dt3[$i] << 8) & 0xFFFFFF00) | (($dt3[$i] >> 24) & 0x000000FF);
$this->dt1[$i << 16] = (($this->dt3[$i] << 16) & 0xFFFF0000) | (($this->dt3[$i] >> 16) & 0x0000FFFF); $dt1[$i << 16] = (($this->dt3[$i] << 16) & 0xFFFF0000) | (($dt3[$i] >> 16) & 0x0000FFFF);
$this->dt0[$i << 24] = (($this->dt3[$i] << 24) & 0xFF000000) | (($this->dt3[$i] >> 8) & 0x00FFFFFF); $dt0[$i << 24] = (($this->dt3[$i] << 24) & 0xFF000000) | (($dt3[$i] >> 8) & 0x00FFFFFF);
} }
} }
@ -532,6 +551,45 @@ class Crypt_Rijndael {
$this->changed = true; $this->changed = true;
} }
/**
* Generate CTR XOR encryption key
*
* Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
* plaintext / ciphertext in CTR mode.
*
* @see Crypt_Rijndael::decrypt()
* @see Crypt_Rijndael::encrypt()
* @access public
* @param Integer $length
* @param String $iv
*/
function _generate_xor($length, &$iv)
{
$xor = '';
$block_size = $this->block_size;
$num_blocks = floor(($length + ($block_size - 1)) / $block_size);
for ($i = 0; $i < $num_blocks; $i++) {
$xor.= $iv;
for ($j = 4; $j <= $block_size; $j+=4) {
$temp = substr($iv, -$j, 4);
switch ($temp) {
case "\xFF\xFF\xFF\xFF":
$iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
break;
case "\x7F\xFF\xFF\xFF":
$iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
break 2;
default:
extract(unpack('Ncount', $temp));
$iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
break 2;
}
}
}
return $xor;
}
/** /**
* Encrypts a message. * Encrypts a message.
* *
@ -553,19 +611,22 @@ class Crypt_Rijndael {
function encrypt($plaintext) function encrypt($plaintext)
{ {
$this->_setup(); $this->_setup();
if ($this->mode != CRYPT_RIJNDAEL_MODE_CTR) {
$plaintext = $this->_pad($plaintext); $plaintext = $this->_pad($plaintext);
}
$block_size = $this->block_size;
$ciphertext = ''; $ciphertext = '';
switch ($this->mode) { switch ($this->mode) {
case CRYPT_RIJNDAEL_MODE_ECB: case CRYPT_RIJNDAEL_MODE_ECB:
for ($i = 0; $i < strlen($plaintext); $i+=$this->block_size) { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $this->block_size)); $ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $block_size));
} }
break; break;
case CRYPT_RIJNDAEL_MODE_CBC: case CRYPT_RIJNDAEL_MODE_CBC:
$xor = $this->encryptIV; $xor = $this->encryptIV;
for ($i = 0; $i < strlen($plaintext); $i+=$this->block_size) { for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $this->block_size); $block = substr($plaintext, $i, $block_size);
$block = $this->_encryptBlock($block ^ $xor); $block = $this->_encryptBlock($block ^ $xor);
$xor = $block; $xor = $block;
$ciphertext.= $block; $ciphertext.= $block;
@ -573,6 +634,17 @@ class Crypt_Rijndael {
if ($this->continuousBuffer) { if ($this->continuousBuffer) {
$this->encryptIV = $xor; $this->encryptIV = $xor;
} }
break;
case CRYPT_RIJNDAEL_MODE_CTR:
$xor = $this->encryptIV;
for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
$block = substr($plaintext, $i, $block_size);
$key = $this->_encryptBlock($this->_generate_xor($block_size, $xor));
$ciphertext.= $block ^ $key;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
}
} }
return $ciphertext; return $ciphertext;
@ -591,30 +663,45 @@ class Crypt_Rijndael {
function decrypt($ciphertext) function decrypt($ciphertext)
{ {
$this->_setup(); $this->_setup();
if ($this->mode != CRYPT_RIJNDAEL_MODE_CTR) {
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic : // 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 - 1) % $this->block_size, chr(0));
}
$block_size = $this->block_size;
$plaintext = ''; $plaintext = '';
switch ($this->mode) { switch ($this->mode) {
case CRYPT_RIJNDAEL_MODE_ECB: case CRYPT_RIJNDAEL_MODE_ECB:
for ($i = 0; $i < strlen($ciphertext); $i+=$this->block_size) { for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
$plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $this->block_size)); $plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $block_size));
} }
break; break;
case CRYPT_RIJNDAEL_MODE_CBC: case CRYPT_RIJNDAEL_MODE_CBC:
$xor = $this->decryptIV; $xor = $this->decryptIV;
for ($i = 0; $i < strlen($ciphertext); $i+=$this->block_size) { for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
$block = substr($ciphertext, $i, $this->block_size); $block = substr($ciphertext, $i, $block_size);
$plaintext.= $this->_decryptBlock($block) ^ $xor; $plaintext.= $this->_decryptBlock($block) ^ $xor;
$xor = $block; $xor = $block;
} }
if ($this->continuousBuffer) { if ($this->continuousBuffer) {
$this->decryptIV = $xor; $this->decryptIV = $xor;
} }
break;
case CRYPT_RIJNDAEL_MODE_CTR:
$xor = $this->decryptIV;
for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
$block = substr($ciphertext, $i, $block_size);
$key = $this->_encryptBlock($this->_generate_xor($block_size, $xor));
$plaintext.= $block ^ $key;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
}
} }
return $this->_unpad($plaintext); return $this->mode != CRYPT_RIJNDAEL_MODE_CTR ? $this->_unpad($plaintext) : $plaintext;
} }
/** /**
@ -629,9 +716,19 @@ class Crypt_Rijndael {
$state = array(); $state = array();
$words = unpack('N*word', $in); $words = unpack('N*word', $in);
$w = $this->w;
$t0 = $this->t0;
$t1 = $this->t1;
$t2 = $this->t2;
$t3 = $this->t3;
$Nb = $this->Nb;
$Nr = $this->Nr;
$c = $this->c;
// addRoundKey // addRoundKey
$i = 0;
foreach ($words as $word) { foreach ($words as $word) {
$state[] = $word ^ $this->w[0][count($state)]; $state[] = $word ^ $w[0][$i++];
} }
// fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components - // fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components -
@ -643,49 +740,49 @@ class Crypt_Rijndael {
// [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf // [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf
$temp = array(); $temp = array();
for ($round = 1; $round < $this->Nr; $round++) { for ($round = 1; $round < $Nr; $round++) {
$i = 0; // $this->c[0] == 0 $i = 0; // $c[0] == 0
$j = $this->c[1]; $j = $c[1];
$k = $this->c[2]; $k = $c[2];
$l = $this->c[3]; $l = $c[3];
while ($i < $this->Nb) { while ($i < $this->Nb) {
$temp[$i] = $this->t0[$state[$i] & 0xFF000000] ^ $temp[$i] = $t0[$state[$i] & 0xFF000000] ^
$this->t1[$state[$j] & 0x00FF0000] ^ $t1[$state[$j] & 0x00FF0000] ^
$this->t2[$state[$k] & 0x0000FF00] ^ $t2[$state[$k] & 0x0000FF00] ^
$this->t3[$state[$l] & 0x000000FF] ^ $t3[$state[$l] & 0x000000FF] ^
$this->w[$round][$i]; $w[$round][$i];
$i++; $i++;
$j = ($j + 1) % $this->Nb; $j = ($j + 1) % $Nb;
$k = ($k + 1) % $this->Nb; $k = ($k + 1) % $Nb;
$l = ($l + 1) % $this->Nb; $l = ($l + 1) % $Nb;
} }
for ($i = 0; $i < $this->Nb; $i++) { for ($i = 0; $i < $Nb; $i++) {
$state[$i] = $temp[$i]; $state[$i] = $temp[$i];
} }
} }
// subWord // subWord
for ($i = 0; $i < $this->Nb; $i++) { for ($i = 0; $i < $Nb; $i++) {
$state[$i] = $this->_subWord($state[$i]); $state[$i] = $this->_subWord($state[$i]);
} }
// shiftRows + addRoundKey // shiftRows + addRoundKey
$i = 0; // $this->c[0] == 0 $i = 0; // $c[0] == 0
$j = $this->c[1]; $j = $c[1];
$k = $this->c[2]; $k = $c[2];
$l = $this->c[3]; $l = $c[3];
while ($i < $this->Nb) { while ($i < $this->Nb) {
$temp[$i] = ($state[$i] & 0xFF000000) ^ $temp[$i] = ($state[$i] & 0xFF000000) ^
($state[$j] & 0x00FF0000) ^ ($state[$j] & 0x00FF0000) ^
($state[$k] & 0x0000FF00) ^ ($state[$k] & 0x0000FF00) ^
($state[$l] & 0x000000FF) ^ ($state[$l] & 0x000000FF) ^
$this->w[$this->Nr][$i]; $w[$Nr][$i];
$i++; $i++;
$j = ($j + 1) % $this->Nb; $j = ($j + 1) % $Nb;
$k = ($k + 1) % $this->Nb; $k = ($k + 1) % $Nb;
$l = ($l + 1) % $this->Nb; $l = ($l + 1) % $Nb;
} }
$state = $temp; $state = $temp;
@ -706,51 +803,62 @@ class Crypt_Rijndael {
$state = array(); $state = array();
$words = unpack('N*word', $in); $words = unpack('N*word', $in);
$num_states = count($state);
$dw = $this->dw;
$dt0 = $this->dt0;
$dt1 = $this->dt1;
$dt2 = $this->dt2;
$dt3 = $this->dt3;
$Nb = $this->Nb;
$Nr = $this->Nr;
$c = $this->c;
// addRoundKey // addRoundKey
$i = 0;
foreach ($words as $word) { foreach ($words as $word) {
$state[] = $word ^ $this->dw[0][count($state)]; $state[] = $word ^ $dw[$Nr][$i++];
} }
$temp = array(); $temp = array();
for ($round = $this->Nr - 1; $round > 0; $round--) { for ($round = $Nr - 1; $round > 0; $round--) {
$i = 0; // $this->c[0] == 0 $i = 0; // $c[0] == 0
$j = $this->Nb - $this->c[1]; $j = $Nb - $c[1];
$k = $this->Nb - $this->c[2]; $k = $Nb - $c[2];
$l = $this->Nb - $this->c[3]; $l = $Nb - $c[3];
while ($i < $this->Nb) { while ($i < $Nb) {
$temp[$i] = $this->dt0[$state[$i] & 0xFF000000] ^ $temp[$i] = $dt0[$state[$i] & 0xFF000000] ^
$this->dt1[$state[$j] & 0x00FF0000] ^ $dt1[$state[$j] & 0x00FF0000] ^
$this->dt2[$state[$k] & 0x0000FF00] ^ $dt2[$state[$k] & 0x0000FF00] ^
$this->dt3[$state[$l] & 0x000000FF] ^ $dt3[$state[$l] & 0x000000FF] ^
$this->dw[$round][$i]; $dw[$round][$i];
$i++; $i++;
$j = ($j + 1) % $this->Nb; $j = ($j + 1) % $Nb;
$k = ($k + 1) % $this->Nb; $k = ($k + 1) % $Nb;
$l = ($l + 1) % $this->Nb; $l = ($l + 1) % $Nb;
} }
for ($i = 0; $i < $this->Nb; $i++) { for ($i = 0; $i < $Nb; $i++) {
$state[$i] = $temp[$i]; $state[$i] = $temp[$i];
} }
} }
// invShiftRows + invSubWord + addRoundKey // invShiftRows + invSubWord + addRoundKey
$i = 0; // $this->c[0] == 0 $i = 0; // $c[0] == 0
$j = $this->Nb - $this->c[1]; $j = $Nb - $c[1];
$k = $this->Nb - $this->c[2]; $k = $Nb - $c[2];
$l = $this->Nb - $this->c[3]; $l = $Nb - $c[3];
while ($i < $this->Nb) { while ($i < $Nb) {
$temp[$i] = $this->dw[0][$i] ^ $temp[$i] = $dw[0][$i] ^
$this->_invSubWord(($state[$i] & 0xFF000000) | $this->_invSubWord(($state[$i] & 0xFF000000) |
($state[$j] & 0x00FF0000) | ($state[$j] & 0x00FF0000) |
($state[$k] & 0x0000FF00) | ($state[$k] & 0x0000FF00) |
($state[$l] & 0x000000FF)); ($state[$l] & 0x000000FF));
$i++; $i++;
$j = ($j + 1) % $this->Nb; $j = ($j + 1) % $Nb;
$k = ($k + 1) % $this->Nb; $k = ($k + 1) % $Nb;
$l = ($l + 1) % $this->Nb; $l = ($l + 1) % $Nb;
} }
$state = $temp; $state = $temp;
@ -1034,7 +1142,8 @@ class Crypt_Rijndael {
/** /**
* Unpads a string. * Unpads a string.
* *
* If padding is enabled and the reported padding length is invalid, padding will be, hence forth, disabled. * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
* and false will be returned.
* *
* @see Crypt_Rijndael::_pad() * @see Crypt_Rijndael::_pad()
* @access private * @access private
@ -1048,9 +1157,7 @@ class Crypt_Rijndael {
$length = ord($text[strlen($text) - 1]); $length = ord($text[strlen($text) - 1]);
if (!$length || $length > $this->block_size) { if (!$length || $length > $this->block_size) {
user_error("The number of bytes reported as being padded ($length) is invalid (block size = {$this->block_size})", E_USER_NOTICE); return false;
$this->padding = false;
return $text;
} }
return substr($text, 0, -$length); return substr($text, 0, -$length);

View File

@ -47,7 +47,7 @@
* @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.gnu.org/licenses/lgpl.txt
* @version $Id: TripleDES.php,v 1.9 2009/11/23 19:06:07 terrafrost Exp $ * @version $Id: TripleDES.php,v 1.13 2010/02/26 03:40:25 terrafrost Exp $
* @link http://phpseclib.sourceforge.net * @link http://phpseclib.sourceforge.net
*/ */
@ -142,15 +142,6 @@ class Crypt_TripleDES {
*/ */
var $decryptIV = "\0\0\0\0\0\0\0\0"; var $decryptIV = "\0\0\0\0\0\0\0\0";
/**
* MCrypt parameters
*
* @see Crypt_TripleDES::setMCrypt()
* @var Array
* @access private
*/
var $mcrypt = array('', '');
/** /**
* The Crypt_DES objects * The Crypt_DES objects
* *
@ -159,6 +150,40 @@ class Crypt_TripleDES {
*/ */
var $des; var $des;
/**
* mcrypt resource for encryption
*
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
*
* @see Crypt_AES::encrypt()
* @var String
* @access private
*/
var $enmcrypt;
/**
* mcrypt resource for decryption
*
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
*
* @see Crypt_AES::decrypt()
* @var String
* @access private
*/
var $demcrypt;
/**
* Does the (en|de)mcrypt resource need to be (re)initialized?
*
* @see setKey()
* @see setIV()
* @var Boolean
* @access private
*/
var $changed = true;
/** /**
* Default Constructor. * Default Constructor.
* *
@ -204,7 +229,11 @@ 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->mode = MCRYPT_MODE_ECB; break; $this->mode = MCRYPT_MODE_ECB;
break;
case CRYPT_DES_MODE_CTR:
$this->mode = 'ctr';
break;
case CRYPT_DES_MODE_CBC: case CRYPT_DES_MODE_CBC:
default: default:
$this->mode = MCRYPT_MODE_CBC; $this->mode = MCRYPT_MODE_CBC;
@ -225,6 +254,7 @@ 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->mode = $mode; $this->mode = $mode;
break; break;
@ -254,7 +284,7 @@ class Crypt_TripleDES {
$key = str_pad($key, 24, chr(0)); $key = str_pad($key, 24, chr(0));
// 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);
} }
$this->key = $key; $this->key = $key;
switch (true) { switch (true) {
@ -264,6 +294,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;
} }
/** /**
@ -283,27 +314,46 @@ 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;
} }
/** /**
* Sets MCrypt parameters. (optional) * Generate CTR XOR encryption key
* *
* If MCrypt is being used, empty strings will be used, unless otherwise specified. * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
* plaintext / ciphertext in CTR mode.
* *
* @link http://php.net/function.mcrypt-module-open#function.mcrypt-module-open * @see Crypt_DES::decrypt()
* @see Crypt_DES::encrypt()
* @access public * @access public
* @param optional Integer $algorithm_directory * @param Integer $length
* @param optional Integer $mode_directory * @param String $iv
*/ */
function setMCrypt($algorithm_directory = '', $mode_directory = '') function _generate_xor($length, &$iv)
{ {
$this->mcrypt = array($algorithm_directory, $mode_directory); $xor = '';
if ( $this->mode == CRYPT_DES_MODE_3CBC ) { $num_blocks = ($length + 7) >> 3;
$this->des[0]->setMCrypt($algorithm_directory, $mode_directory); for ($i = 0; $i < $num_blocks; $i++) {
$this->des[1]->setMCrypt($algorithm_directory, $mode_directory); $xor.= $iv;
$this->des[2]->setMCrypt($algorithm_directory, $mode_directory); for ($j = 4; $j <= 8; $j+=4) {
$temp = substr($iv, -$j, 4);
switch ($temp) {
case "\xFF\xFF\xFF\xFF":
$iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
break;
case "\x7F\xFF\xFF\xFF":
$iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
break 2;
default:
extract(unpack('Ncount', $temp));
$iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
break 2;
} }
} }
}
return $xor;
}
/** /**
* Encrypts a message. * Encrypts a message.
@ -313,7 +363,9 @@ class Crypt_TripleDES {
*/ */
function encrypt($plaintext) function encrypt($plaintext)
{ {
if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') {
$plaintext = $this->_pad($plaintext); $plaintext = $this->_pad($plaintext);
}
// if the key is smaller then 8, do what we'd normally do // if the key is smaller then 8, do what we'd normally do
if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) { if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) {
@ -323,16 +375,18 @@ class Crypt_TripleDES {
} }
if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
$td = mcrypt_module_open(MCRYPT_3DES, $this->mcrypt[0], $this->mode, $this->mcrypt[1]); if ($this->changed) {
mcrypt_generic_init($td, $this->key, $this->encryptIV); if (!isset($this->enmcrypt)) {
$this->enmcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
}
mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
$this->changed = false;
}
$ciphertext = mcrypt_generic($td, $plaintext); $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
mcrypt_generic_deinit($td); if (!$this->continuousBuffer) {
mcrypt_module_close($td); mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
if ($this->continuousBuffer) {
$this->encryptIV = substr($ciphertext, -8);
} }
return $ciphertext; return $ciphertext;
@ -348,14 +402,16 @@ class Crypt_TripleDES {
// "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."
$plaintext = str_pad($plaintext, ceil(strlen($plaintext) / 8) * 8, chr(0)); $plaintext = str_pad($plaintext, ceil(strlen($plaintext) / 8) * 8, chr(0));
$des = $this->des;
$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);
$block = $this->des[0]->_processBlock($block, CRYPT_DES_ENCRYPT); $block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
$block = $this->des[1]->_processBlock($block, CRYPT_DES_DECRYPT); $block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT);
$block = $this->des[2]->_processBlock($block, CRYPT_DES_ENCRYPT); $block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT);
$ciphertext.= $block; $ciphertext.= $block;
} }
break; break;
@ -363,15 +419,29 @@ class Crypt_TripleDES {
$xor = $this->encryptIV; $xor = $this->encryptIV;
for ($i = 0; $i < strlen($plaintext); $i+=8) { for ($i = 0; $i < strlen($plaintext); $i+=8) {
$block = substr($plaintext, $i, 8) ^ $xor; $block = substr($plaintext, $i, 8) ^ $xor;
$block = $this->des[0]->_processBlock($block, CRYPT_DES_ENCRYPT); $block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
$block = $this->des[1]->_processBlock($block, CRYPT_DES_DECRYPT); $block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT);
$block = $this->des[2]->_processBlock($block, CRYPT_DES_ENCRYPT); $block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT);
$xor = $block; $xor = $block;
$ciphertext.= $block; $ciphertext.= $block;
} }
if ($this->continuousBuffer) { if ($this->continuousBuffer) {
$this->encryptIV = $xor; $this->encryptIV = $xor;
} }
break;
case CRYPT_DES_MODE_CTR:
$xor = $this->encryptIV;
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$key = $this->_generate_xor(8, $xor);
$key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
$key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
$key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
$block = substr($plaintext, $i, 8);
$ciphertext.= $block ^ $key;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
}
} }
return $ciphertext; return $ciphertext;
@ -396,19 +466,21 @@ class Crypt_TripleDES {
$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 ) {
$td = mcrypt_module_open(MCRYPT_3DES, $this->mcrypt[0], $this->mode, $this->mcrypt[1]); if ($this->changed) {
mcrypt_generic_init($td, $this->key, $this->decryptIV); if (!isset($this->demcrypt)) {
$this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
$plaintext = mdecrypt_generic($td, $ciphertext); }
mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
mcrypt_generic_deinit($td); $this->changed = false;
mcrypt_module_close($td);
if ($this->continuousBuffer) {
$this->decryptIV = substr($ciphertext, -8);
} }
return $this->_unpad($plaintext); $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
}
return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext;
} }
if (strlen($this->key) <= 8) { if (strlen($this->key) <= 8) {
@ -417,14 +489,16 @@ class Crypt_TripleDES {
return $this->_unpad($this->des[0]->decrypt($plaintext)); return $this->_unpad($this->des[0]->decrypt($plaintext));
} }
$des = $this->des;
$plaintext = ''; $plaintext = '';
switch ($this->mode) { switch ($this->mode) {
case CRYPT_DES_MODE_ECB: case CRYPT_DES_MODE_ECB:
for ($i = 0; $i < strlen($ciphertext); $i+=8) { for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$block = substr($ciphertext, $i, 8); $block = substr($ciphertext, $i, 8);
$block = $this->des[2]->_processBlock($block, CRYPT_DES_DECRYPT); $block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT);
$block = $this->des[1]->_processBlock($block, CRYPT_DES_ENCRYPT); $block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT);
$block = $this->des[0]->_processBlock($block, CRYPT_DES_DECRYPT); $block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT);
$plaintext.= $block; $plaintext.= $block;
} }
break; break;
@ -432,18 +506,32 @@ class Crypt_TripleDES {
$xor = $this->decryptIV; $xor = $this->decryptIV;
for ($i = 0; $i < strlen($ciphertext); $i+=8) { for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$orig = $block = substr($ciphertext, $i, 8); $orig = $block = substr($ciphertext, $i, 8);
$block = $this->des[2]->_processBlock($block, CRYPT_DES_DECRYPT); $block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT);
$block = $this->des[1]->_processBlock($block, CRYPT_DES_ENCRYPT); $block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT);
$block = $this->des[0]->_processBlock($block, CRYPT_DES_DECRYPT); $block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT);
$plaintext.= $block ^ $xor; $plaintext.= $block ^ $xor;
$xor = $orig; $xor = $orig;
} }
if ($this->continuousBuffer) { if ($this->continuousBuffer) {
$this->decryptIV = $xor; $this->decryptIV = $xor;
} }
break;
case CRYPT_DES_MODE_CTR:
$xor = $this->decryptIV;
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$key = $this->_generate_xor(8, $xor);
$key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
$key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
$key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
$block = substr($ciphertext, $i, 8);
$plaintext.= $block ^ $key;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
}
} }
return $this->_unpad($plaintext); return $this->mode != CRYPT_DES_MODE_CTR ? $this->_unpad($plaintext) : $plaintext;
} }
/** /**
@ -576,7 +664,8 @@ class Crypt_TripleDES {
/** /**
* Unpads a string * Unpads a string
* *
* If padding is enabled and the reported padding length is invalid, padding will be, hence forth, disabled. * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
* and false will be returned.
* *
* @see Crypt_TripleDES::_pad() * @see Crypt_TripleDES::_pad()
* @access private * @access private
@ -590,9 +679,7 @@ class Crypt_TripleDES {
$length = ord($text[strlen($text) - 1]); $length = ord($text[strlen($text) - 1]);
if (!$length || $length > 8) { if (!$length || $length > 8) {
user_error("The number of bytes reported as being padded ($length) is invalid (block size = 8)", E_USER_NOTICE); return false;
$this->padding = false;
return $text;
} }
return substr($text, 0, -$length); return substr($text, 0, -$length);

File diff suppressed because it is too large Load Diff