AES: Fix broken CFB and disableContinuousBuffer()

Related to: https://github.com/phpseclib/phpseclib/pull/45
This commit is contained in:
Hans-Jürgen Petrich 2013-01-10 23:27:12 +07:00
parent 46baf1815f
commit 20461866ef

View File

@ -293,7 +293,6 @@ class Crypt_AES extends Crypt_Rijndael {
function encrypt($plaintext) function encrypt($plaintext)
{ {
if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) { if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
$changed = $this->changed;
$this->_mcryptSetup(); $this->_mcryptSetup();
/* /*
if ($this->mode == CRYPT_AES_MODE_CTR) { if ($this->mode == CRYPT_AES_MODE_CTR) {
@ -310,36 +309,43 @@ class Crypt_AES extends Crypt_Rijndael {
// using mcrypt's default handing of CFB the above would output two different things. using phpseclib's // using mcrypt's default handing of CFB the above would output two different things. using phpseclib's
// rewritten CFB implementation the above outputs the same thing twice. // rewritten CFB implementation the above outputs the same thing twice.
if ($this->mode == 'ncfb' && $this->continuousBuffer) { if ($this->mode == 'ncfb' && $this->continuousBuffer) {
if ($changed) { $iv = &$this->encryptIV;
$this->ecb = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, ''); $pos = &$this->enbuffer['pos'];
mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); $len = strlen($plaintext);
}
$buffer = &$this->enbuffer['encrypted'];
if (strlen($buffer)) {
$ciphertext = $plaintext ^ substr($this->encryptIV, strlen($buffer));
$buffer.= $ciphertext;
if (strlen($buffer) == 16) {
$this->encryptIV = $buffer;
$buffer = '';
mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
}
$plaintext = substr($plaintext, strlen($ciphertext));
} else {
$ciphertext = ''; $ciphertext = '';
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = 16 - $pos;
if ($len >= $max) {
$i = $max;
$len-= $max;
$pos = 0;
} else {
$i = $len;
$pos+= $len;
$len = 0;
} }
$ciphertext = substr($iv, $orig_pos) ^ $plaintext;
$last_pos = strlen($plaintext) & 0xFFFFFFF0; $iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
if ($last_pos) { $this->enbuffer['enmcrypt_init'] = true;
$ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, 0, $last_pos));
$this->encryptIV = substr($ciphertext, -16);
} }
if ($len >= 16) {
if (strlen($plaintext) & 0xF) { if ($this->enbuffer['enmcrypt_init'] === true) {
$this->encryptIV = mcrypt_generic($this->ecb, $this->encryptIV); mcrypt_generic_init($this->enmcrypt, $this->key, $iv);
$buffer = substr($plaintext, $last_pos) ^ $this->encryptIV; $this->enbuffer['enmcrypt_init'] = false;
$ciphertext.= $buffer; }
$ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % 16));
$iv = substr($ciphertext, -16);
$i = strlen($ciphertext);
$len%= 16;
}
if ($len) {
$iv = mcrypt_generic($this->ecb, $iv);
$block = substr($iv, $pos) ^ substr($plaintext, $i);
$iv = substr_replace($iv, $block, $pos, $len);
$ciphertext.= $block;
$pos = $len;
} }
return $ciphertext; return $ciphertext;
@ -373,7 +379,6 @@ class Crypt_AES extends Crypt_Rijndael {
function decrypt($ciphertext) function decrypt($ciphertext)
{ {
if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) { if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
$changed = $this->changed;
$this->_mcryptSetup(); $this->_mcryptSetup();
/* /*
if ($this->mode == CRYPT_AES_MODE_CTR) { if ($this->mode == CRYPT_AES_MODE_CTR) {
@ -387,37 +392,44 @@ class Crypt_AES extends Crypt_Rijndael {
} }
*/ */
if ($this->mode == 'ncfb' && $this->continuousBuffer) { if ($this->mode == 'ncfb' && $this->continuousBuffer) {
if ($changed) { $iv = &$this->decryptIV;
$this->ecb = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, ''); $pos = &$this->debuffer['pos'];
mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); $len = strlen($ciphertext);
}
$buffer = &$this->debuffer['ciphertext'];
if (strlen($buffer)) {
$plaintext = $ciphertext ^ substr($this->decryptIV, strlen($buffer));
$buffer.= substr($ciphertext, 0, strlen($plaintext));
if (strlen($buffer) == 16) {
$this->decryptIV = $buffer;
$buffer = '';
mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
}
$ciphertext = substr($ciphertext, strlen($plaintext));
} else {
$plaintext = ''; $plaintext = '';
$i = 0;
if ($pos) {
$orig_pos = $pos;
$max = 16 - $pos;
if ($len >= $max) {
$i = $max;
$len-= $max;
$pos = 0;
} else {
$i = $len;
$pos+= $len;
$len = 0;
} }
// ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
$last_pos = strlen($ciphertext) & 0xFFFFFFF0; $plaintext = substr($iv, $orig_pos) ^ $ciphertext;
if ($last_pos) { $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
$plaintext = mdecrypt_generic($this->demcrypt, substr($ciphertext, 0, $last_pos)); $this->debuffer['demcrypt_init'] = true;
$this->decryptIV = substr($ciphertext, $last_pos - 16, 16);
$this->decryptIV = mcrypt_generic($this->ecb, $this->decryptIV);
} }
if ($len >= 16) {
if (strlen($ciphertext) & 0xF) { if ($this->debuffer['demcrypt_init'] === true) {
$buffer = substr($ciphertext, $last_pos); mcrypt_generic_init($this->demcrypt, $this->key, $iv);
$plaintext.= $buffer ^ $this->decryptIV; $this->debuffer['demcrypt_init'] = false;
}
$cb = substr($ciphertext, $i, $len - $len % 16);
$plaintext.= mdecrypt_generic($this->demcrypt, $cb);
$iv = substr($cb, -16);
$i = strlen($plaintext);
$len%= 16;
}
if ($len) {
$iv = mcrypt_generic($this->ecb, $iv);
$plaintext.= substr($iv, $pos) ^ substr($ciphertext, $i);
$iv = substr_replace($iv, substr($ciphertext, $i, $len), $pos, $len);
$pos = $len;
} }
return $plaintext; return $plaintext;
@ -488,11 +500,20 @@ class Crypt_AES extends Crypt_Rijndael {
$this->demcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, ''); $this->demcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
$this->enmcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, ''); $this->enmcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
if ($mode == 'ncfb') {
$this->ecb = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
}
} // else should mcrypt_generic_deinit be called? } // else should mcrypt_generic_deinit be called?
mcrypt_generic_init($this->demcrypt, $this->key, $this->iv); mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv); mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
if ($this->mode == 'ncfb') {
mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
}
$this->changed = false; $this->changed = false;
} }
@ -608,6 +629,24 @@ class Crypt_AES extends Crypt_Rijndael {
return pack('N*', $state[0], $state[1], $state[2], $state[3]); return pack('N*', $state[0], $state[1], $state[2], $state[3]);
} }
/**
* Treat consecutive "packets" as if they are a continuous buffer.
*
* The default behavior.
*
* @see Crypt_Rijndael::disableContinuousBuffer()
* @access public
*/
function enableContinuousBuffer()
{
parent::enableContinuousBuffer();
if (CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT) {
$this->enbuffer['enmcrypt_init'] = true;
$this->debuffer['demcrypt_init'] = true;
}
}
/** /**
* Treat consecutive packets as if they are a discontinuous buffer. * Treat consecutive packets as if they are a discontinuous buffer.
* *