Use random iv values in rijndael.

Using fixed iv values has a number of known problems like dictionary
attacks against the cipher key. Use a random iv value for all future
encrpyted values. Provide backwards compatibility for values encrypted
with fixed iv's, and silently upgrade values for enhanced security.
This commit is contained in:
mark_story 2013-02-09 14:55:13 -05:00
parent 613aa19d94
commit 974ac44fb4
2 changed files with 33 additions and 3 deletions

View file

@ -256,10 +256,27 @@ class SecurityTest extends CakeTestCase {
$result = Security::rijndael('', $key, 'encrypt'); $result = Security::rijndael('', $key, 'encrypt');
$this->assertEquals('', Security::rijndael($result, $key, 'decrypt')); $this->assertEquals('', Security::rijndael($result, $key, 'decrypt'));
$result = Security::rijndael($txt, $key = 'this is my key of over 32 chars, yes it is', 'encrypt'); $key = 'this is my key of over 32 chars, yes it is';
$result = Security::rijndael($txt, $key, 'encrypt');
$this->assertEquals($txt, Security::rijndael($result, $key, 'decrypt')); $this->assertEquals($txt, Security::rijndael($result, $key, 'decrypt'));
} }
/**
* Test that rijndael() can still decrypt values with a fixed iv.
*
* @return
*/
public function testRijndaelBackwardCompatibility() {
$this->skipIf(!function_exists('mcrypt_encrypt'));
$txt = 'The quick brown fox jumped over the lazy dog.';
$key = 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi';
// Encrypted before random iv
$value = base64_decode('1WPjnq96LMzLGwNgmudHF+cAIqVUN5DaUZEpf5tm1EzSgt5iYY9o3d66iRI/fKJLTlTVGsa8HzW0jDNitmVXoQ==');
$this->assertEquals($txt, Security::rijndael($value, $key, 'decrypt'));
}
/** /**
* testRijndaelInvalidOperation method * testRijndaelInvalidOperation method
* *

View file

@ -198,6 +198,10 @@ class Security {
/** /**
* Encrypts/Decrypts a text using the given key using rijndael method. * Encrypts/Decrypts a text using the given key using rijndael method.
* *
* Prior to 2.3.1, a fixed initialization vector was used. This was not
* secure. This method now uses a random iv, and will silently upgrade values when
* they are re-encrypted.
*
* @param string $text Encrypted string to decrypt, normal string to encrypt * @param string $text Encrypted string to decrypt, normal string to encrypt
* @param string $key Key to use as the encryption key for encrypted data. * @param string $key Key to use as the encryption key for encrypted data.
* @param string $operation Operation to perform, encrypt or decrypt * @param string $operation Operation to perform, encrypt or decrypt
@ -218,12 +222,21 @@ class Security {
} }
$algorithm = MCRYPT_RIJNDAEL_256; $algorithm = MCRYPT_RIJNDAEL_256;
$mode = MCRYPT_MODE_CBC; $mode = MCRYPT_MODE_CBC;
$ivSize = mcrypt_get_iv_size($algorithm, $mode);
$cryptKey = substr($key, 0, 32); $cryptKey = substr($key, 0, 32);
$iv = substr($key, strlen($key) - 32, 32);
if ($operation === 'encrypt') { if ($operation === 'encrypt') {
return mcrypt_encrypt($algorithm, $cryptKey, $text, $mode, $iv); $iv = mcrypt_create_iv($ivSize, MCRYPT_RAND);
return $iv . '$$' . mcrypt_encrypt($algorithm, $cryptKey, $text, $mode, $iv);
} }
// Backwards compatible decrypt with fixed iv
if (substr($text, $ivSize, 2) !== '$$') {
$iv = substr($key, strlen($key) - 32, 32);
return rtrim(mcrypt_decrypt($algorithm, $cryptKey, $text, $mode, $iv), "\0");
}
$iv = substr($text, 0, $ivSize);
$text = substr($text, $ivSize + 2);
return rtrim(mcrypt_decrypt($algorithm, $cryptKey, $text, $mode, $iv), "\0"); return rtrim(mcrypt_decrypt($algorithm, $cryptKey, $text, $mode, $iv), "\0");
} }