From e8088d693d6bd8b08c1cdc397bbdebd7067844a5 Mon Sep 17 00:00:00 2001 From: Andrey Andreev Date: Thu, 6 Feb 2014 05:01:48 +0200 Subject: CI_Encryption: CAST-128/CAST5 and RC4/ARCFour compatibility --- system/libraries/Encryption.php | 65 +++++++++++++++++++------ tests/codeigniter/libraries/Encryption_test.php | 23 +++++++++ 2 files changed, 73 insertions(+), 15 deletions(-) diff --git a/system/libraries/Encryption.php b/system/libraries/Encryption.php index f5950ee46..583ddac3b 100644 --- a/system/libraries/Encryption.php +++ b/system/libraries/Encryption.php @@ -367,11 +367,21 @@ class CI_Encryption { } elseif ( ! isset($params['iv'])) { - $params['iv'] = ($iv_size = mcrypt_enc_get_iv_size($params['handle'])) + // The greater-than-1 comparison is mostly a work-around for a bug, + // where 1 is returned for ARCFour instead of 0. + $params['iv'] = (($iv_size = mcrypt_enc_get_iv_size($params['handle'])) > 1) ? mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM) : NULL; } + // CAST-128 compatibility (http://tools.ietf.org/rfc/rfc2144.txt) + // + // RFC2144 says that keys shorter than 16 bytes are to be padded with + // zero bytes to 16 bytes, but (surprise) MCrypt doesn't do that. + if ($params['cipher'] === 'cast-128' && ($kl = strlen($params['key'])) < 16) + { + $params['key'] .= str_repeat("\x0", 16 - $kl); + } if (mcrypt_generic_init($params['handle'], $params['key'], $params['iv']) < 0) { @@ -541,7 +551,9 @@ class CI_Encryption { } elseif ( ! isset($params['iv'])) { - if ($iv_size = mcrypt_enc_get_iv_size($params['handle'])) + // The greater-than-1 comparison is mostly a work-around for a bug, + // where 1 is returned for ARCFour instead of 0. + if (($iv_size = mcrypt_enc_get_iv_size($params['handle'])) > 1) { if (mcrypt_enc_get_modes_name($params['handle']) !== 'ECB') { @@ -560,6 +572,15 @@ class CI_Encryption { } } + // CAST-128 compatibility (http://tools.ietf.org/rfc/rfc2144.txt) + // + // RFC2144 says that keys shorter than 16 bytes are to be padded with + // zero bytes to 16 bytes, but (surprise) MCrypt doesn't do that. + if ($params['cipher'] === 'cast-128' && ($kl = strlen($params['key'])) < 16) + { + $params['key'] .= str_repeat("\x0", 16 - $kl); + } + if (mcrypt_generic_init($params['handle'], $params['key'], $params['iv']) < 0) { if ($params['handle'] !== $this->_handle) @@ -760,35 +781,49 @@ class CI_Encryption { 'aes-256' => 'rijndael-128', 'des3-ede3' => 'tripledes', 'bf' => 'blowfish', + 'cast5' => 'cast-128', + 'rc4' => 'arcfour', + 'rc4-40' => 'arcfour' ), 'openssl' => array( 'rijndael-128' => 'aes-128', 'tripledes' => 'des-ede3', - 'blowfish' => 'bf' + 'blowfish' => 'bf', + 'cast-128' => 'cast5', + 'arcfour' => 'rc4-40', + 'rc4' => 'rc4-40' ) ); // Notes: // + // - Rijndael-128 is, at the same time all three of AES-128, + // AES-192 and AES-256. The only difference between them is + // the key size. Rijndael-192, Rijndael-256 on the other hand + // also have different block sizes and are NOT AES-compatible. + // // - Blowfish is said to be supporting key sizes between // 4 and 56 bytes, but it appears that between MCrypt and // OpenSSL, only those of 16 and more bytes are compatible. + // Also, don't know what MCrypt's 'blowfish-compat' is. // - // Other seemingly matching ciphers between MCrypt, OpenSSL: + // - CAST-128/CAST5 produces a longer cipher when encrypted via + // OpenSSL, but (strangely enough) can be decrypted by either + // extension anyway. + // Also, RFC2144 says that the cipher supports key sizes + // between 5 and 16 bytes by the implementation actually + // zero-padding them to 16 bytes, but MCrypt doesn't do that. // - // - DES is compatible, but doesn't need an alias - // - CAST-128/CAST5 is NOT compatible - // mcrypt: 'cast-128' - // openssl: 'cast5' - // - RC2 is NOT compatible - // mcrypt: 'rc2' - // openssl: 'rc2', 'rc2-40', 'rc2-64' + // - RC4 (ARCFour) has a strange implementation under OpenSSL. + // Its 'rc4-40' cipher method seems to work flawlessly, yet + // there's another one, 'rc4' that only works with a 16-byte key. // - // To avoid any other confusion due to a popular (but incorrect) - // belief, it should also be noted that Rijndael-192/256 are NOT - // the same ciphers as AES-192/256 like Rijndael-128 and AES-256 is. + // - DES is compatible, but doesn't need an alias. + // + // Other seemingly matching ciphers between MCrypt, OpenSSL: // - // All compatibility tests were done in CBC mode. + // - RC2 is NOT compatible and only an obscure forum post + // confirms that it is MCrypt's fault. } if (isset($dictionary[$this->_driver][$cipher])) diff --git a/tests/codeigniter/libraries/Encryption_test.php b/tests/codeigniter/libraries/Encryption_test.php index da9870fc5..41617c2f4 100644 --- a/tests/codeigniter/libraries/Encryption_test.php +++ b/tests/codeigniter/libraries/Encryption_test.php @@ -66,6 +66,29 @@ class Encryption_test extends CI_TestCase { array('blowfish', 'cfb', 56), array('blowfish', 'ofb', 56), array('blowfish', 'ecb', 56), + array('cast5', 'cbc', 5), + array('cast5', 'cfb', 5), + array('cast5', 'ofb', 5), + array('cast5', 'ecb', 5), + array('cast5', 'cbc', 8), + array('cast5', 'cfb', 8), + array('cast5', 'ofb', 8), + array('cast5', 'ecb', 8), + array('cast5', 'cbc', 10), + array('cast5', 'cfb', 10), + array('cast5', 'ofb', 10), + array('cast5', 'ecb', 10), + array('cast5', 'cbc', 16), + array('cast5', 'cfb', 16), + array('cast5', 'ofb', 16), + array('cast5', 'ecb', 16), + array('rc4', 'stream', 5), + array('rc4', 'stream', 8), + array('rc4', 'stream', 16), + array('rc4', 'stream', 32), + array('rc4', 'stream', 64), + array('rc4', 'stream', 128), + array('rc4', 'stream', 256) ); $driver_index = array('mcrypt', 'openssl'); -- cgit v1.2.3-24-g4f1b