3DES: Fixed ContinuousBuffer() in CTR/CFB/OFB

FIXED: multiple calls to enable/enableContinuousBuffer() work's now in CTR/CFB/OFB
FIXED: "Illegal string offset" and strlen(array()) Warning/Notice
Performance improvement in CFB Mode with enableContinuousBuffer() using MODE_MCRYPT
This commit is contained in:
Hans-Jürgen Petrich 2013-01-14 21:23:20 +07:00
parent c7c150bcd9
commit f7dc23a18e

View File

@ -212,19 +212,19 @@ class Crypt_TripleDES {
* Encryption buffer for CTR, OFB and CFB modes * Encryption buffer for CTR, OFB and CFB modes
* *
* @see Crypt_TripleDES::encrypt() * @see Crypt_TripleDES::encrypt()
* @var String * @var Array
* @access private * @access private
*/ */
var $enbuffer = ''; var $enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true);
/** /**
* Decryption buffer for CTR, OFB and CFB modes * Decryption buffer for CTR, OFB and CFB modes
* *
* @see Crypt_TripleDES::decrypt() * @see Crypt_TripleDES::decrypt()
* @var String * @var Array
* @access private * @access private
*/ */
var $debuffer = ''; var $debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true);
/** /**
* mcrypt resource for CFB mode * mcrypt resource for CFB mode
@ -287,6 +287,7 @@ class Crypt_TripleDES {
break; break;
case CRYPT_DES_MODE_CFB: case CRYPT_DES_MODE_CFB:
$this->mode = 'ncfb'; $this->mode = 'ncfb';
$this->ecb = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, '');
break; break;
case CRYPT_DES_MODE_OFB: case CRYPT_DES_MODE_OFB:
$this->mode = MCRYPT_MODE_NOFB; $this->mode = MCRYPT_MODE_NOFB;
@ -296,6 +297,8 @@ class Crypt_TripleDES {
$this->paddable = true; $this->paddable = true;
$this->mode = MCRYPT_MODE_CBC; $this->mode = MCRYPT_MODE_CBC;
} }
$this->enmcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
$this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
break; break;
default: default:
@ -444,29 +447,24 @@ class Crypt_TripleDES {
* @see Crypt_TripleDES::decrypt() * @see Crypt_TripleDES::decrypt()
* @see Crypt_TripleDES::encrypt() * @see Crypt_TripleDES::encrypt()
* @access private * @access private
* @param Integer $length
* @param String $iv * @param String $iv
*/ */
function _generate_xor($length, &$iv) function _generate_xor(&$iv)
{ {
$xor = ''; $xor = $iv;
$num_blocks = ($length + 7) >> 3; for ($j = 4; $j <= 8; $j+=4) {
for ($i = 0; $i < $num_blocks; $i++) { $temp = substr($iv, -$j, 4);
$xor.= $iv; switch ($temp) {
for ($j = 4; $j <= 8; $j+=4) { case "\xFF\xFF\xFF\xFF":
$temp = substr($iv, -$j, 4); $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
switch ($temp) { break;
case "\xFF\xFF\xFF\xFF": case "\x7F\xFF\xFF\xFF":
$iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4); $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
break; break 2;
case "\x7F\xFF\xFF\xFF": default:
$iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4); extract(unpack('Ncount', $temp));
break 2; $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
default: break 2;
extract(unpack('Ncount', $temp));
$iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
break 2;
}
} }
} }
@ -494,48 +492,64 @@ class Crypt_TripleDES {
if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
if ($this->enchanged) { if ($this->enchanged) {
if (!isset($this->enmcrypt)) {
$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);
if ($this->mode != 'ncfb') { if ($this->mode == 'ncfb') {
$this->enchanged = false; mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0");
} }
$this->enchanged = false;
} }
if ($this->mode != 'ncfb') { if ($this->mode != 'ncfb' || !$this->continuousBuffer) {
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
} else { } else {
if ($this->enchanged) { $iv = &$this->encryptIV;
$this->ecb = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, ''); $pos = &$this->enbuffer['pos'];
mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0"); $len = strlen($plaintext);
$this->enchanged = false; $ciphertext = '';
} $i = 0;
if ($pos) {
if (strlen($this->enbuffer)) { $orig_pos = $pos;
$ciphertext = $plaintext ^ substr($this->encryptIV, strlen($this->enbuffer)); $max = 8 - $pos;
$this->enbuffer.= $ciphertext; if ($len >= $max) {
if (strlen($this->enbuffer) == 8) { $i = $max;
$this->encryptIV = $this->enbuffer; $len-= $max;
$this->enbuffer = ''; $pos = 0;
mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); } else {
$i = $len;
$pos+= $len;
$len = 0;
} }
$plaintext = substr($plaintext, strlen($ciphertext)); $ciphertext = substr($iv, $orig_pos) ^ $plaintext;
} else { $iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
$ciphertext = ''; $this->enbuffer['enmcrypt_init'] = true;
} }
if ($len >= 8) {
$last_pos = strlen($plaintext) & 0xFFFFFFF8; if ($this->enbuffer['enmcrypt_init'] === false || $len > 950) {
$ciphertext.= $last_pos ? mcrypt_generic($this->enmcrypt, substr($plaintext, 0, $last_pos)) : ''; if ($this->enbuffer['enmcrypt_init'] === true) {
mcrypt_generic_init($this->enmcrypt, $this->key, $iv);
if (strlen($plaintext) & 0x7) { $this->enbuffer['enmcrypt_init'] = false;
if (strlen($ciphertext)) { }
$this->encryptIV = substr($ciphertext, -8); $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % 8));
$iv = substr($ciphertext, -8);
$i = strlen($ciphertext);
$len%= 8;
} else {
while ($len >= 8) {
$iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, 8);
$ciphertext.= $iv;
$len-= 8;
$i+= 8;
}
} }
$this->encryptIV = mcrypt_generic($this->ecb, $this->encryptIV); }
$this->enbuffer = substr($plaintext, $last_pos) ^ $this->encryptIV; if ($len) {
$ciphertext.= $this->enbuffer; $iv = mcrypt_generic($this->ecb, $iv);
$block = $iv ^ substr($plaintext, $i);
$iv = substr_replace($iv, $block, 0, $len);
$ciphertext.= $block;
$pos = $len;
} }
return $ciphertext;
} }
if (!$this->continuousBuffer) { if (!$this->continuousBuffer) {
@ -591,7 +605,7 @@ class Crypt_TripleDES {
if (strlen($buffer['encrypted'])) { if (strlen($buffer['encrypted'])) {
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);
$key = $this->_generate_xor(8, $xor); $key = $this->_generate_xor($xor);
$key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT); $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
$key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT); $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
$key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
@ -602,7 +616,7 @@ class Crypt_TripleDES {
} else { } else {
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);
$key = $this->_generate_xor(8, $xor); $key = $this->_generate_xor($xor);
$key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT); $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
$key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT); $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
$key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
@ -612,7 +626,7 @@ class Crypt_TripleDES {
if ($this->continuousBuffer) { if ($this->continuousBuffer) {
$this->encryptIV = $xor; $this->encryptIV = $xor;
if ($start = strlen($plaintext) & 7) { if ($start = strlen($plaintext) & 7) {
$buffer['encrypted'] = substr($key, $start) . $buffer; $buffer['encrypted'] = substr($key, $start) . $buffer['encrypted'];
} }
} }
break; break;
@ -651,13 +665,13 @@ class Crypt_TripleDES {
break; break;
case CRYPT_DES_MODE_OFB: case CRYPT_DES_MODE_OFB:
$xor = $this->encryptIV; $xor = $this->encryptIV;
if (strlen($buffer)) { if (strlen($buffer['xor'])) {
for ($i = 0; $i < strlen($plaintext); $i+=8) { for ($i = 0; $i < strlen($plaintext); $i+=8) {
$xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT); $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
$xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$buffer.= $xor; $buffer['xor'].= $xor;
$key = $this->_string_shift($buffer, 8); $key = $this->_string_shift($buffer['xor'], 8);
$ciphertext.= substr($plaintext, $i, 8) ^ $key; $ciphertext.= substr($plaintext, $i, 8) ^ $key;
} }
} else { } else {
@ -672,7 +686,7 @@ class Crypt_TripleDES {
if ($this->continuousBuffer) { if ($this->continuousBuffer) {
$this->encryptIV = $xor; $this->encryptIV = $xor;
if ($start = strlen($plaintext) & 7) { if ($start = strlen($plaintext) & 7) {
$buffer = substr($key, $start) . $buffer; $buffer['xor'] = substr($key, $start) . $buffer['xor'];
} }
} }
} }
@ -702,50 +716,72 @@ class Crypt_TripleDES {
if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
if ($this->dechanged) { if ($this->dechanged) {
if (!isset($this->demcrypt)) {
$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);
if ($this->mode != 'ncfb') { if ($this->mode == 'ncfb') {
$this->dechanged = false; mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0");
} }
$this->dechanged = false;
} }
if ($this->mode != 'ncfb') { if ($this->mode != 'ncfb' || !$this->continuousBuffer) {
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
} else { } else {
if ($this->dechanged) { $iv = &$this->decryptIV;
$this->ecb = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, ''); $pos = &$this->debuffer['pos'];
mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0"); $len = strlen($ciphertext);
$this->dechanged = false; $plaintext = '';
} $i = 0;
if ($pos) {
if (strlen($this->debuffer)) { $orig_pos = $pos;
$plaintext = $ciphertext ^ substr($this->decryptIV, strlen($this->debuffer)); $max = 8 - $pos;
if ($len >= $max) {
$this->debuffer.= substr($ciphertext, 0, strlen($plaintext)); $i = $max;
if (strlen($this->debuffer) == 8) { $len-= $max;
$this->decryptIV = $this->debuffer; $pos = 0;
$this->debuffer = ''; } else {
mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); $i = $len;
$pos+= $len;
$len = 0;
} }
$ciphertext = substr($ciphertext, strlen($plaintext)); $plaintext = substr($iv, $orig_pos) ^ $ciphertext;
} else { $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
$plaintext = ''; // $this->debuffer['demcrypt_init'] = true;
} }
if ($len >= 8) {
$last_pos = strlen($ciphertext) & 0xFFFFFFF8; // In decrypt() possible. Will work with better performance as the commented code below
$plaintext.= $last_pos ? mdecrypt_generic($this->demcrypt, substr($ciphertext, 0, $last_pos)) : ''; $cb = substr($ciphertext, $i, $len - $len % 8);
$plaintext.= mcrypt_generic($this->ecb, $iv.$cb) ^ $cb;
if (strlen($ciphertext) & 0x7) { $iv = substr($cb, -8);
if (strlen($plaintext)) { $len%= 8;
$this->decryptIV = substr($ciphertext, $last_pos - 8, 8); /*
if ($this->debuffer['demcrypt_init'] === false || $len > 950) {
if ($this->debuffer['demcrypt_init'] === true) {
mcrypt_generic_init($this->demcrypt, $this->key, $iv);
$this->debuffer['demcrypt_init'] = false;
}
$cb = substr($ciphertext, $i, $len - $len % 8);
$plaintext.= mdecrypt_generic($this->demcrypt, $cb);
$iv = substr($cb, -8);
$len%= 8;
} else {
while ($len >= 8) {
$iv = mcrypt_generic($this->ecb,$iv);
$cb = substr($ciphertext, $i, 8);
$plaintext.= $iv ^ $cb;
$iv = $cb;
$i+= 8;
$len-= 8;
}
} }
$this->decryptIV = mcrypt_generic($this->ecb, $this->decryptIV); */
$this->debuffer = substr($ciphertext, $last_pos); }
$plaintext.= $this->debuffer ^ $this->decryptIV; if ($len) {
$iv = mcrypt_generic($this->ecb, $iv);
$cb = substr($ciphertext, -$len);
$plaintext.= $iv ^ $cb;
$iv = substr_replace($iv, $cb, 0, $len);
$pos = $len;
} }
return $plaintext; return $plaintext;
} }
@ -764,7 +800,7 @@ class Crypt_TripleDES {
$des = $this->des; $des = $this->des;
$buffer = &$this->enbuffer; $buffer = &$this->debuffer;
$continuousBuffer = $this->continuousBuffer; $continuousBuffer = $this->continuousBuffer;
$plaintext = ''; $plaintext = '';
switch ($this->mode) { switch ($this->mode) {
@ -796,7 +832,7 @@ class Crypt_TripleDES {
if (strlen($buffer['ciphertext'])) { if (strlen($buffer['ciphertext'])) {
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);
$key = $this->_generate_xor(8, $xor); $key = $this->_generate_xor($xor);
$key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT); $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
$key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT); $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
$key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
@ -807,7 +843,7 @@ class Crypt_TripleDES {
} else { } else {
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);
$key = $this->_generate_xor(8, $xor); $key = $this->_generate_xor($xor);
$key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT); $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
$key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT); $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
$key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
@ -861,13 +897,13 @@ class Crypt_TripleDES {
break; break;
case CRYPT_DES_MODE_OFB: case CRYPT_DES_MODE_OFB:
$xor = $this->decryptIV; $xor = $this->decryptIV;
if (strlen($buffer)) { if (strlen($buffer['xor'])) {
for ($i = 0; $i < strlen($ciphertext); $i+=8) { for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT); $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);
$xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);
$buffer.= $xor; $buffer['xor'].= $xor;
$key = $this->_string_shift($buffer, 8); $key = $this->_string_shift($buffer['xor'], 8);
$plaintext.= substr($ciphertext, $i, 8) ^ $key; $plaintext.= substr($ciphertext, $i, 8) ^ $key;
} }
} else { } else {
@ -882,7 +918,7 @@ class Crypt_TripleDES {
if ($this->continuousBuffer) { if ($this->continuousBuffer) {
$this->decryptIV = $xor; $this->decryptIV = $xor;
if ($start = strlen($ciphertext) & 7) { if ($start = strlen($ciphertext) & 7) {
$buffer = substr($key, $start) . $buffer; $buffer['xor'] = substr($key, $start) . $buffer['xor'];
} }
} }
} }
@ -950,6 +986,10 @@ class Crypt_TripleDES {
$this->continuousBuffer = false; $this->continuousBuffer = false;
$this->encryptIV = $this->iv; $this->encryptIV = $this->iv;
$this->decryptIV = $this->iv; $this->decryptIV = $this->iv;
$this->enchanged = true;
$this->dechanged = true;
$this->enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true);
$this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true);
if ($this->mode == CRYPT_DES_MODE_3CBC) { if ($this->mode == CRYPT_DES_MODE_3CBC) {
$this->des[0]->disableContinuousBuffer(); $this->des[0]->disableContinuousBuffer();