From c624c785ee88551dc0091abc8663b6e78bbf1a0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Petrich?= Date: Sun, 13 Jan 2013 01:48:48 +0700 Subject: [PATCH] DES: Fixed CFB/OFB and continuousBuffer FIXED: multiple calls to enable/disableCMode() will work now FIXED: CFB/OFB modes FIXED: "Illegal string offset" and strlen(array()) Warning/Notice --- phpseclib/Crypt/DES.php | 334 ++++++++++++++++++++++++---------------- 1 file changed, 197 insertions(+), 137 deletions(-) diff --git a/phpseclib/Crypt/DES.php b/phpseclib/Crypt/DES.php index 6cc60c31..f06610e7 100644 --- a/phpseclib/Crypt/DES.php +++ b/phpseclib/Crypt/DES.php @@ -257,19 +257,19 @@ class Crypt_DES { * Encryption buffer for CTR, OFB and CFB modes * * @see Crypt_DES::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_DES::decrypt() - * @var String + * @var Array * @access private */ - var $debuffer = ''; + var $debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true); /** * mcrypt resource for CFB mode @@ -316,6 +316,7 @@ class Crypt_DES { break; case CRYPT_DES_MODE_CFB: $this->mode = 'ncfb'; + $this->ecb = mcrypt_module_open(MCRYPT_DES, '', MCRYPT_MODE_ECB, ''); break; case CRYPT_DES_MODE_OFB: $this->mode = MCRYPT_MODE_NOFB; @@ -325,6 +326,8 @@ class Crypt_DES { $this->paddable = true; $this->mode = MCRYPT_MODE_CBC; } + $this->enmcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, ''); + $this->demcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, ''); break; default: @@ -363,7 +366,8 @@ class Crypt_DES { function setKey($key) { $this->keys = ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) ? str_pad(substr($key, 0, 8), 8, chr(0)) : $this->_prepareKey($key); - $this->changed = true; + $this->enchanged = true; + $this->dechanged = true; } /** @@ -431,7 +435,8 @@ class Crypt_DES { function setIV($iv) { $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0)); - $this->changed = true; + $this->enchanged = true; + $this->dechanged = true; } /** @@ -497,48 +502,64 @@ class Crypt_DES { if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { if ($this->enchanged) { - if (!isset($this->enmcrypt)) { - $this->enmcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, ''); - } mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV); - if ($this->mode != 'ncfb') { - $this->enchanged = false; + if ($this->mode == 'ncfb') { + mcrypt_generic_init($this->ecb, $this->keys, "\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_DES, '', MCRYPT_MODE_ECB, ''); - mcrypt_generic_init($this->ecb, $this->keys, "\0\0\0\0\0\0\0\0"); - $this->enchanged = false; - } - - if (strlen($this->enbuffer)) { - $ciphertext = $plaintext ^ substr($this->encryptIV, strlen($this->enbuffer)); - $this->enbuffer.= $ciphertext; - if (strlen($this->enbuffer) == 8) { - $this->encryptIV = $this->enbuffer; - $this->enbuffer = ''; - mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV); + $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 > 600) { + if ($this->enbuffer['enmcrypt_init'] === true) { + mcrypt_generic_init($this->enmcrypt, $this->keys, $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) { @@ -597,42 +618,51 @@ class Crypt_DES { } break; case CRYPT_DES_MODE_CFB: - if (strlen($buffer['xor'])) { - $ciphertext = $plaintext ^ $buffer['xor']; - $iv = $buffer['encrypted'] . $ciphertext; - $start = strlen($ciphertext); - $buffer['encrypted'].= $ciphertext; - $buffer['xor'] = substr($buffer['xor'], strlen($ciphertext)); - } else { - $ciphertext = ''; - $iv = $this->encryptIV; - $start = 0; - } - - for ($i = $start; $i < strlen($plaintext); $i+=8) { - $block = substr($plaintext, $i, 8); - $xor = $this->_processBlock($iv, CRYPT_DES_ENCRYPT); - $iv = $block ^ $xor; - if ($continuousBuffer && strlen($iv) != 8) { - $buffer = array( - 'encrypted' => $iv, - 'xor' => substr($xor, strlen($iv)) - ); - } - $ciphertext.= $iv; - } - if ($this->continuousBuffer) { - $this->encryptIV = $iv; + $iv = &$this->encryptIV; + $pos = &$buffer['pos']; + } else { + $iv = $this->encryptIV; + $pos = 0; } - break; + $len = strlen($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($iv, $orig_pos) ^ $plaintext; + $iv = substr_replace($iv, $ciphertext, $orig_pos, $i); + } + while ($len >= 8) { + $iv = $this->_processBlock($iv, CRYPT_DES_ENCRYPT) ^ substr($plaintext, $i, 8); + $ciphertext.= $iv; + $len-= 8; + $i+= 8; + } + if ($len) { + $iv = $this->_processBlock($iv, CRYPT_DES_ENCRYPT); + $block = $iv ^ substr($plaintext, $i); + $iv = substr_replace($iv, $block, 0, $len); + $ciphertext.= $block; + $pos = $len; + } + return $ciphertext; case CRYPT_DES_MODE_OFB: $xor = $this->encryptIV; - if (strlen($buffer)) { + if (strlen($buffer['xor'])) { for ($i = 0; $i < strlen($plaintext); $i+=8) { $xor = $this->_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 { @@ -645,7 +675,7 @@ class Crypt_DES { if ($this->continuousBuffer) { $this->encryptIV = $xor; if ($start = strlen($plaintext) & 7) { - $buffer = substr($key, $start) . $buffer; + $buffer['xor'] = substr($key, $start) . $buffer['xor']; } } } @@ -672,50 +702,65 @@ class Crypt_DES { if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) { if ($this->dechanged) { - if (!isset($this->demcrypt)) { - $this->demcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, ''); - } mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV); - if ($this->mode != 'ncfb') { - $this->dechanged = false; + if ($this->mode == 'ncfb') { + mcrypt_generic_init($this->ecb, $this->keys, "\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_DES, '', MCRYPT_MODE_ECB, ''); - mcrypt_generic_init($this->ecb, $this->keys, "\0\0\0\0\0\0\0\0"); - $this->dechanged = false; - } - - if (strlen($this->debuffer)) { - $plaintext = $ciphertext ^ substr($this->decryptIV, strlen($this->debuffer)); - - $this->debuffer.= substr($ciphertext, 0, strlen($plaintext)); - if (strlen($this->debuffer) == 8) { - $this->decryptIV = $this->debuffer; - $this->debuffer = ''; - mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV); + $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) { + if ($this->debuffer['demcrypt_init'] === false || $len > 600) { + if ($this->debuffer['demcrypt_init'] === true) { + mcrypt_generic_init($this->demcrypt, $this->keys, $iv); + $this->debuffer['demcrypt_init'] = false; + } + $cb = substr($ciphertext, $i, $len - $len % 8); + $plaintext.= mdecrypt_generic($this->demcrypt, $cb); + $iv = substr($cb, -8); + $i = strlen($plaintext); + $len%= 8; + } else { + while ($len >= 8) { + $iv = mcrypt_generic($this->ecb,$iv); + $cb = substr($ciphertext, $i, 8); + $plaintext.= $iv ^ $cb; + $iv = $cb; + $len-= 8; + $i+= 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); + $plaintext.= $iv ^ substr($ciphertext, $i); + $iv = substr_replace($iv, substr($ciphertext, $i, $len), 0, $len); + $pos = $len; + } return $plaintext; } @@ -774,44 +819,52 @@ class Crypt_DES { } break; case CRYPT_DES_MODE_CFB: - if (strlen($buffer['ciphertext'])) { - $plaintext = $ciphertext ^ substr($this->decryptIV, strlen($buffer['ciphertext'])); - $buffer['ciphertext'].= substr($ciphertext, 0, strlen($plaintext)); - if (strlen($buffer['ciphertext']) != 8) { - $block = $this->decryptIV; - } else { - $block = $buffer['ciphertext']; - $xor = $this->_processBlock($buffer['ciphertext'], CRYPT_DES_ENCRYPT); - $buffer['ciphertext'] = ''; - } - $start = strlen($plaintext); - } else { - $plaintext = ''; - $xor = $this->_processBlock($this->decryptIV, CRYPT_DES_ENCRYPT); - $start = 0; - } - - for ($i = $start; $i < strlen($ciphertext); $i+=8) { - $block = substr($ciphertext, $i, 8); - $plaintext.= $block ^ $xor; - if ($continuousBuffer && strlen($block) != 8) { - $buffer['ciphertext'].= $block; - $block = $xor; - } else if (strlen($block) == 8) { - $xor = $this->_processBlock($block, CRYPT_DES_ENCRYPT); - } - } if ($this->continuousBuffer) { - $this->decryptIV = $block; + $iv = &$this->decryptIV; + $pos = &$buffer['pos']; + } else { + $iv = $this->decryptIV; + $pos = 0; } - break; + $len = strlen($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($iv, $orig_pos) ^ $ciphertext; + $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i); + } + while ($len >= 8) { + $iv = $this->_processBlock($iv, CRYPT_DES_ENCRYPT); + $cb = substr($ciphertext, $i, 8); + $plaintext.= $iv ^ $cb; + $iv = $cb; + $len-= 8; + $i+= 8; + } + if ($len) { + $iv = $this->_processBlock($iv, CRYPT_DES_ENCRYPT); + $plaintext.= $iv ^ substr($ciphertext, $i); + $iv = substr_replace($iv, substr($ciphertext, $i, $len), 0, $len); + $pos = $len; + } + return $plaintext; case CRYPT_DES_MODE_OFB: $xor = $this->decryptIV; - if (strlen($buffer)) { + if (strlen($buffer['xor'])) { for ($i = 0; $i < strlen($ciphertext); $i+=8) { $xor = $this->_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 { @@ -824,7 +877,7 @@ class Crypt_DES { if ($this->continuousBuffer) { $this->decryptIV = $xor; if ($start = strlen($ciphertext) % 8) { - $buffer = substr($key, $start) . $buffer; + $buffer['xor'] = substr($key, $start) . $buffer['xor']; } } } @@ -887,6 +940,13 @@ class Crypt_DES { $this->continuousBuffer = false; $this->encryptIV = $this->iv; $this->decryptIV = $this->iv; + $this->enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true); + $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true); + + if (CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT) { + mcrypt_generic_init($this->enmcrypt, $this->keys, $this->iv); + mcrypt_generic_init($this->demcrypt, $this->keys, $this->iv); + } } /** @@ -1294,4 +1354,4 @@ class Crypt_DES { } // vim: ts=4:sw=4:et: -// vim6: fdl=1: \ No newline at end of file +// vim6: fdl=1: