From f7dc23a18e13d6bdfab3d0dc2c9594f3219bef31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Petrich?= Date: Mon, 14 Jan 2013 21:23:20 +0700 Subject: [PATCH] 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 --- phpseclib/Crypt/TripleDES.php | 248 ++++++++++++++++++++-------------- 1 file changed, 144 insertions(+), 104 deletions(-) diff --git a/phpseclib/Crypt/TripleDES.php b/phpseclib/Crypt/TripleDES.php index bc9e6c2d..87bd74a6 100644 --- a/phpseclib/Crypt/TripleDES.php +++ b/phpseclib/Crypt/TripleDES.php @@ -212,19 +212,19 @@ class Crypt_TripleDES { * Encryption buffer for CTR, OFB and CFB modes * * @see Crypt_TripleDES::encrypt() - * @var String + * @var Array * @access private */ - var $enbuffer = ''; + var $enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); /** * Decryption buffer for CTR, OFB and CFB modes * * @see Crypt_TripleDES::decrypt() - * @var String + * @var Array * @access private */ - var $debuffer = ''; + var $debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true); /** * mcrypt resource for CFB mode @@ -287,6 +287,7 @@ class Crypt_TripleDES { break; case CRYPT_DES_MODE_CFB: $this->mode = 'ncfb'; + $this->ecb = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, ''); break; case CRYPT_DES_MODE_OFB: $this->mode = MCRYPT_MODE_NOFB; @@ -296,6 +297,8 @@ class Crypt_TripleDES { $this->paddable = true; $this->mode = MCRYPT_MODE_CBC; } + $this->enmcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, ''); + $this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, ''); break; default: @@ -444,29 +447,24 @@ class Crypt_TripleDES { * @see Crypt_TripleDES::decrypt() * @see Crypt_TripleDES::encrypt() * @access private - * @param Integer $length * @param String $iv */ - function _generate_xor($length, &$iv) + function _generate_xor(&$iv) { - $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; - } + $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; } } @@ -494,48 +492,64 @@ class Crypt_TripleDES { if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { 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); - if ($this->mode != 'ncfb') { - $this->enchanged = false; + if ($this->mode == 'ncfb') { + 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); } else { - if ($this->enchanged) { - $this->ecb = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, ''); - mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0"); - $this->enchanged = false; - } - - if (strlen($this->enbuffer)) { - $ciphertext = $plaintext ^ substr($this->encryptIV, strlen($this->enbuffer)); - $this->enbuffer.= $ciphertext; - if (strlen($this->enbuffer) == 8) { - $this->encryptIV = $this->enbuffer; - $this->enbuffer = ''; - mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + $iv = &$this->encryptIV; + $pos = &$this->enbuffer['pos']; + $len = strlen($plaintext); + $ciphertext = ''; + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = 8 - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; } - $plaintext = substr($plaintext, strlen($ciphertext)); - } else { - $ciphertext = ''; + $ciphertext = substr($iv, $orig_pos) ^ $plaintext; + $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); + $this->enbuffer['enmcrypt_init'] = true; } - - $last_pos = strlen($plaintext) & 0xFFFFFFF8; - $ciphertext.= $last_pos ? mcrypt_generic($this->enmcrypt, substr($plaintext, 0, $last_pos)) : ''; - - if (strlen($plaintext) & 0x7) { - if (strlen($ciphertext)) { - $this->encryptIV = substr($ciphertext, -8); + if ($len >= 8) { + if ($this->enbuffer['enmcrypt_init'] === false || $len > 950) { + if ($this->enbuffer['enmcrypt_init'] === true) { + mcrypt_generic_init($this->enmcrypt, $this->key, $iv); + $this->enbuffer['enmcrypt_init'] = false; + } + $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; - $ciphertext.= $this->enbuffer; + } + if ($len) { + $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) { @@ -591,7 +605,7 @@ class Crypt_TripleDES { if (strlen($buffer['encrypted'])) { for ($i = 0; $i < strlen($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[1]->_processBlock($key, CRYPT_DES_DECRYPT); $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); @@ -602,7 +616,7 @@ class Crypt_TripleDES { } else { for ($i = 0; $i < strlen($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[1]->_processBlock($key, CRYPT_DES_DECRYPT); $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); @@ -612,7 +626,7 @@ class Crypt_TripleDES { if ($this->continuousBuffer) { $this->encryptIV = $xor; if ($start = strlen($plaintext) & 7) { - $buffer['encrypted'] = substr($key, $start) . $buffer; + $buffer['encrypted'] = substr($key, $start) . $buffer['encrypted']; } } break; @@ -651,13 +665,13 @@ class Crypt_TripleDES { break; case CRYPT_DES_MODE_OFB: $xor = $this->encryptIV; - if (strlen($buffer)) { + if (strlen($buffer['xor'])) { for ($i = 0; $i < strlen($plaintext); $i+=8) { $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT); $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); - $buffer.= $xor; - $key = $this->_string_shift($buffer, 8); + $buffer['xor'].= $xor; + $key = $this->_string_shift($buffer['xor'], 8); $ciphertext.= substr($plaintext, $i, 8) ^ $key; } } else { @@ -672,7 +686,7 @@ class Crypt_TripleDES { if ($this->continuousBuffer) { $this->encryptIV = $xor; 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 ($this->dechanged) { - if (!isset($this->demcrypt)) { - $this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, ''); - } mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); - if ($this->mode != 'ncfb') { - $this->dechanged = false; + if ($this->mode == 'ncfb') { + 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); } else { - if ($this->dechanged) { - $this->ecb = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, ''); - mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0"); - $this->dechanged = false; - } - - if (strlen($this->debuffer)) { - $plaintext = $ciphertext ^ substr($this->decryptIV, strlen($this->debuffer)); - - $this->debuffer.= substr($ciphertext, 0, strlen($plaintext)); - if (strlen($this->debuffer) == 8) { - $this->decryptIV = $this->debuffer; - $this->debuffer = ''; - mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + $iv = &$this->decryptIV; + $pos = &$this->debuffer['pos']; + $len = strlen($ciphertext); + $plaintext = ''; + $i = 0; + if ($pos) { + $orig_pos = $pos; + $max = 8 - $pos; + if ($len >= $max) { + $i = $max; + $len-= $max; + $pos = 0; + } else { + $i = $len; + $pos+= $len; + $len = 0; } - $ciphertext = substr($ciphertext, strlen($plaintext)); - } else { - $plaintext = ''; + $plaintext = substr($iv, $orig_pos) ^ $ciphertext; + $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); + // $this->debuffer['demcrypt_init'] = true; } - - $last_pos = strlen($ciphertext) & 0xFFFFFFF8; - $plaintext.= $last_pos ? mdecrypt_generic($this->demcrypt, substr($ciphertext, 0, $last_pos)) : ''; - - if (strlen($ciphertext) & 0x7) { - if (strlen($plaintext)) { - $this->decryptIV = substr($ciphertext, $last_pos - 8, 8); + if ($len >= 8) { + // In decrypt() possible. Will work with better performance as the commented code below + $cb = substr($ciphertext, $i, $len - $len % 8); + $plaintext.= mcrypt_generic($this->ecb, $iv.$cb) ^ $cb; + $iv = substr($cb, -8); + $len%= 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; } @@ -764,7 +800,7 @@ class Crypt_TripleDES { $des = $this->des; - $buffer = &$this->enbuffer; + $buffer = &$this->debuffer; $continuousBuffer = $this->continuousBuffer; $plaintext = ''; switch ($this->mode) { @@ -796,7 +832,7 @@ class Crypt_TripleDES { if (strlen($buffer['ciphertext'])) { for ($i = 0; $i < strlen($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[1]->_processBlock($key, CRYPT_DES_DECRYPT); $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); @@ -807,7 +843,7 @@ class Crypt_TripleDES { } else { for ($i = 0; $i < strlen($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[1]->_processBlock($key, CRYPT_DES_DECRYPT); $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT); @@ -861,13 +897,13 @@ class Crypt_TripleDES { break; case CRYPT_DES_MODE_OFB: $xor = $this->decryptIV; - if (strlen($buffer)) { + if (strlen($buffer['xor'])) { for ($i = 0; $i < strlen($ciphertext); $i+=8) { $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT); $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT); $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT); - $buffer.= $xor; - $key = $this->_string_shift($buffer, 8); + $buffer['xor'].= $xor; + $key = $this->_string_shift($buffer['xor'], 8); $plaintext.= substr($ciphertext, $i, 8) ^ $key; } } else { @@ -882,7 +918,7 @@ class Crypt_TripleDES { if ($this->continuousBuffer) { $this->decryptIV = $xor; 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->encryptIV = $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) { $this->des[0]->disableContinuousBuffer();