phpseclib/phpseclib — pull request #44 by petrich (2013-01-05 09:45:12+00:00)
Title: Fixed CFB Decrypt(). Fixed OFB warnings

Fixed/rewritten: Decrypt() in CFB mode to work clean with enableContinuousBuffer()

Fixed: PHP's strlen(array()) warning in OFB mode,

Fixed: disableContinuousBuffer() clear's now also the $enbuffer/$debuffer arrays so that multiple calls to enable/disableContinuousBuffer() will work without old buffers

Encrypt() in CFB is also rewritten although it was not broken, but for code equality.

Strong testet, also against mcrypt and openssl ciphertext output.

Speed of En/Decrypt() in CFB mode is nearly equal to the old code.

New code is a litte bit faster in enableContinuousBuffer() and a little bit slower without enableContinuousBuffer(), but not worth mentioning.

All also PHP4 tested :-)

DES/3DES and probably AES are also affected regarding the CFB enableContinuousBuffer() bug and the OFB Warnings... but i suggest lets first fix all in Crypt_Rijndael and then the others.

Here is my endlessloop test script for checking speed and strict dataintegrity in enableContinuousBuffer() mode... if openssl and mcrypt if available it will also check produced ciphertext/plaintext against them so that it is not only a selftest.

```php
setKey($key);
$cipher->setIV($iv);
$cipher->setBlockLength(128);

$plaintext = '12345678901234567';
echo "Quicktest related to: https://github.com/phpseclib/phpseclib/issues/39 \n";
echo "Expected: '$plaintext'\n";

$cipher->enableContinuousBuffer();
$output = '';
for($i=0; $iDecrypt($cipher->Encrypt($plaintext[$i]));
}
echo "Output: '$output'\n\n";
$cipher->disableContinuousBuffer();

echo "Speedtest [enableContinuousBuffer()]...\n";

$cipher->enableContinuousBuffer();
$plaintext	=	str_repeat('a',1024*300);
$start	=	microtime(true);
for($i=0, $len=strlen($plaintext); $i<$len;) {
$l=mt_rand(1,32);
$cipher->Decrypt($cipher->Encrypt(substr($plaintext,$i,$l)));
$i+=$l;
}
$time=microtime(true)-$start;
echo "Time needed for $len bytes: $time sec.\n\n";
$cipher->disableContinuousBuffer();

while(1)
{
$plaintext	=	'';
$l	=	mt_rand(1,20000);
for($i=0;$i<$l;$i++) { $plaintext.=chr(mt_rand(0,255)); }
echo "Dataintegritytest of ".strlen($plaintext)." bytes, streamed/de/encrypt randomly partitionated [verify cipher/plaintext against mcrypt=$mcrypt openssl=$openssl] ... \n";flush();

//Testing of currupted ciphertext a random partitionated stream...
for($j=0;$j<5;$j++)
{
$cipher->enableContinuousBuffer();
$ciphertext	=	'';
for($i=0, $len=strlen($plaintext); $i<$len;) {
$l=mt_rand(1,41);
$ciphertext.=$cipher->Encrypt(substr($plaintext,$i,$l));
//echo " $l";
$i+=$l;
}
$cipher->disableContinuousBuffer();

if($ciphertext !== $cipher->Encrypt($plaintext))
{
echo "\nFailed ".basename(__FILE__).':'.__LINE__.": \$ciphertext corruption!";
die;
}

//mcrypt's ncfb operation mode is, in buffered context, broken ... but in unbuffered context it's clean, so we can verify our finished random-partitionated buffered-stream "$ciphertext" against mcrypt_encrypt()'s ciphertext which has to be exactly the same or anything went wrong (very probably with us)...
if($mcrypt && $ciphertext !== mcrypt_encrypt($mcrypt_cipher,$key,$plaintext,$mcrypt_mode,$iv))
{
echo "\nFailed ".basename(__FILE__).':'.__LINE__.": \$ciphertext corruption! [2]";
die;
}

//Checking against openssl... it must be the same ciphertext
if($openssl && $ciphertext !== openssl_encrypt ($plaintext, $openssl_method, $key, true, $iv))
{
echo "\nFailed ".basename(__FILE__).':'.__LINE__.": \$ciphertext corruption! [3]";
die;
}

}

//Testing of currupted plaintext with a random partitionated stream...
for($j=0;$j<5;$j++)
{
$plaintext2	=	'';
$cipher->enableContinuousBuffer();
for($i=0, $len=strlen($ciphertext); $i<$len;) {
$l=mt_rand(1,41);
//echo " $l";
$plaintext2.=$cipher->Decrypt(substr($ciphertext,$i,$l));
$i+=$l;
}
if($plaintext !== $plaintext2)
{
echo "\nFailed ".basename(__FILE__).':'.__LINE__.": \$plaintext corruption!";
die;
}
$cipher->disableContinuousBuffer();
}
if($plaintext !== $cipher->Decrypt($ciphertext))
{
echo "\nFailed ".basename(__FILE__).':'.__LINE__.": Unbuffered Decrypt() failed !";
die;
}

if($mcrypt && $plaintext !== mcrypt_decrypt($mcrypt_cipher,$key,$ciphertext,$mcrypt_mode,$iv))
{
echo "\nFailed ".basename(__FILE__).':'.__LINE__.": Unbuffered Decrypt() failed! [2]";
die;
}

if($openssl && $plaintext !== openssl_decrypt ($ciphertext, $openssl_method, $key, true, $iv))
{
echo "\nFailed ".basename(__FILE__).':'.__LINE__.": Unbuffered Decrypt() failed! [3]";
die;
}

echo "Succeeded\n\n";

//break; // comment it to run 4ever for longtime testing...
}

?>

```