diff --git a/phpseclib/Crypt/AES.php b/phpseclib/Crypt/AES.php index 14a0a627..9085a8f1 100644 --- a/phpseclib/Crypt/AES.php +++ b/phpseclib/Crypt/AES.php @@ -152,4 +152,56 @@ class Crypt_AES extends Crypt_Rijndael { return; } + + /** + * Sets the key length + * + * Valid key lengths are 128, 192, and 256. If the length is less than 128, it will be rounded up to + * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount. + * + * @see Crypt_Rijndael:setKeyLength() + * @access public + * @param Integer $length + */ + function setKeyLength($length) + { + switch ($length) { + case 160: + $length = 192; + break; + case 224: + $length = 256; + } + parent::setKeyLength($length); + } + + /** + * Sets the key. + * + * Rijndael supports five different key lengths, AES only supports three. + * + * @see Crypt_Rijndael:setKey() + * @see setKeyLength() + * @access public + * @param String $key + */ + function setKey($key) + { + parent::setKey($key); + + if (!$this->explicit_key_length) { + $length = strlen($key); + switch (true) { + case $length <= 16: + $this->key_size = 16; + break; + case $length <= 24: + $this->key_size = 24; + break; + default: + $this->key_size = 32; + } + $this->_setupEngine(); + } + } } diff --git a/phpseclib/Crypt/Rijndael.php b/phpseclib/Crypt/Rijndael.php index a43283f1..dcc0c19e 100644 --- a/phpseclib/Crypt/Rijndael.php +++ b/phpseclib/Crypt/Rijndael.php @@ -702,9 +702,15 @@ class Crypt_Rijndael extends Crypt_Base case $length <= 16: $this->key_size = 16; break; + case $length <= 20: + $this->key_size = 20; + break; case $length <= 24: $this->key_size = 24; break; + case $length <= 28: + $this->key_size = 28; + break; default: $this->key_size = 32; } diff --git a/tests/Unit/Crypt/AES/ContinuousBufferTest.php b/tests/Unit/Crypt/AES/ContinuousBufferTest.php deleted file mode 100644 index e032dab6..00000000 --- a/tests/Unit/Crypt/AES/ContinuousBufferTest.php +++ /dev/null @@ -1,72 +0,0 @@ - - * @copyright MMXIII Andreas Fischer - * @license http://www.opensource.org/licenses/mit-license.html MIT License - */ - -class Unit_Crypt_AES_ContinuousBufferTest extends Unit_Crypt_AES_TestCase -{ - // String intented - protected $modes = array( - 'CRYPT_AES_MODE_CTR', - 'CRYPT_AES_MODE_OFB', - 'CRYPT_AES_MODE_CFB', - ); - - protected $plaintexts = array( - '', - '12345678901234567', // https://github.com/phpseclib/phpseclib/issues/39 - "\xDE\xAD\xBE\xAF", - ':-):-):-):-):-):-)', // https://github.com/phpseclib/phpseclib/pull/43 - ); - - protected $ivs = array( - '', - 'test123', - ); - - protected $keys = array( - '', - ':-8', // https://github.com/phpseclib/phpseclib/pull/43 - 'FOOBARZ', - ); - - /** - * Produces all combinations of test values. - * - * @return array - */ - public function allCombinations() - { - $result = array(); - - // @codingStandardsIgnoreStart - foreach ($this->modes as $mode) - foreach ($this->plaintexts as $plaintext) - foreach ($this->ivs as $iv) - foreach ($this->keys as $key) - $result[] = array($mode, $plaintext, $iv, $key); - // @codingStandardsIgnoreEnd - - return $result; - } - - /** - * @dataProvider allCombinations - */ - public function testEncryptDecrypt($mode, $plaintext, $iv, $key) - { - $aes = new Crypt_AES(constant($mode)); - $aes->enableContinuousBuffer(); - $aes->setIV($iv); - $aes->setKey($key); - - $actual = ''; - for ($i = 0, $strlen = strlen($plaintext); $i < $strlen; ++$i) { - $actual .= $aes->decrypt($aes->encrypt($plaintext[$i])); - } - - $this->assertEquals($plaintext, $actual); - } -} diff --git a/tests/Unit/Crypt/AES/InternalTest.php b/tests/Unit/Crypt/AES/InternalTest.php new file mode 100644 index 00000000..2a9652ca --- /dev/null +++ b/tests/Unit/Crypt/AES/InternalTest.php @@ -0,0 +1,17 @@ + + * @copyright MMXIII Andreas Fischer + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ + +class Unit_Crypt_AES_InternalTest extends Unit_Crypt_AES_TestCase +{ + static public function setUpBeforeClass() + { + parent::setUpBeforeClass(); + + self::ensureConstant('CRYPT_AES_MODE', CRYPT_AES_MODE_INTERNAL); + self::ensureConstant('CRYPT_RIJNDAEL_MODE', CRYPT_RIJNDAEL_MODE_INTERNAL); + } +} diff --git a/tests/Unit/Crypt/AES/McryptTest.php b/tests/Unit/Crypt/AES/McryptTest.php new file mode 100644 index 00000000..4327c7ea --- /dev/null +++ b/tests/Unit/Crypt/AES/McryptTest.php @@ -0,0 +1,21 @@ + + * @copyright MMXIII Andreas Fischer + * @license http://www.opensource.org/licenses/mit-license.html MIT License + */ + +class Unit_Crypt_AES_McryptTest extends Unit_Crypt_AES_TestCase +{ + static public function setUpBeforeClass() + { + if (!extension_loaded('mcrypt')) { + self::markTestSkipped('mcrypt extension is not available.'); + } + + parent::setUpBeforeClass(); + + self::ensureConstant('CRYPT_AES_MODE', CRYPT_AES_MODE_MCRYPT); + self::ensureConstant('CRYPT_RIJNDAEL_MODE', CRYPT_RIJNDAEL_MODE_MCRYPT); + } +} diff --git a/tests/Unit/Crypt/AES/TestCase.php b/tests/Unit/Crypt/AES/TestCase.php index c4bf57e6..91cb9792 100644 --- a/tests/Unit/Crypt/AES/TestCase.php +++ b/tests/Unit/Crypt/AES/TestCase.php @@ -11,17 +11,97 @@ abstract class Unit_Crypt_AES_TestCase extends PhpseclibTestCase { static public function setUpBeforeClass() { - if (!defined('CRYPT_AES_MODE')) { - define('CRYPT_AES_MODE', CRYPT_AES_MODE_INTERNAL); - } + parent::setUpBeforeClass(); + + self::reRequireFile('Crypt/Rijndael.php'); + self::reRequireFile('Crypt/AES.php'); } - public function setUp() + /** + * Produces all combinations of test values. + * + * @return array + */ + public function continuousBufferCombos() { - if (defined('CRYPT_AES_MODE') && CRYPT_AES_MODE !== CRYPT_AES_MODE_INTERNAL) { - $this->markTestSkipped( - 'Skipping test because CRYPT_AES_MODE is not defined as CRYPT_AES_MODE_INTERNAL.' - ); + $modes = array( + 'CRYPT_AES_MODE_CTR', + 'CRYPT_AES_MODE_OFB', + 'CRYPT_AES_MODE_CFB', + ); + $plaintexts = array( + '', + '12345678901234567', // https://github.com/phpseclib/phpseclib/issues/39 + "\xDE\xAD\xBE\xAF", + ':-):-):-):-):-):-)', // https://github.com/phpseclib/phpseclib/pull/43 + ); + $ivs = array( + '', + 'test123', + ); + $keys = array( + '', + ':-8', // https://github.com/phpseclib/phpseclib/pull/43 + 'FOOBARZ', + ); + + $result = array(); + + // @codingStandardsIgnoreStart + foreach ($modes as $mode) + foreach ($plaintexts as $plaintext) + foreach ($ivs as $iv) + foreach ($keys as $key) + $result[] = array($mode, $plaintext, $iv, $key); + // @codingStandardsIgnoreEnd + + return $result; + } + + /** + * @dataProvider continuousBufferCombos + */ + public function testEncryptDecryptWithContinuousBuffer($mode, $plaintext, $iv, $key) + { + $aes = new Crypt_AES(constant($mode)); + $aes->enableContinuousBuffer(); + $aes->setIV($iv); + $aes->setKey($key); + + $actual = ''; + for ($i = 0, $strlen = strlen($plaintext); $i < $strlen; ++$i) { + $actual .= $aes->decrypt($aes->encrypt($plaintext[$i])); } + + $this->assertEquals($plaintext, $actual); + } + + /** + * @group github451 + */ + public function testKeyPaddingRijndael() + { + // this test case is from the following URL: + // https://web.archive.org/web/20070209120224/http://fp.gladman.plus.com/cryptography_technology/rijndael/aesdvec.zip + + $aes = new Crypt_Rijndael(); + $aes->disablePadding(); + $aes->setKey(pack('H*', '2b7e151628aed2a6abf7158809cf4f3c762e7160')); // 160-bit key. Valid in Rijndael. + $ciphertext = $aes->encrypt(pack('H*', '3243f6a8885a308d313198a2e0370734')); + $this->assertEquals($ciphertext, pack('H*', '231d844639b31b412211cfe93712b880')); + } + + /** + * @group github451 + */ + public function testKeyPaddingAES() + { + // same as the above - just with a different ciphertext + + $aes = new Crypt_AES(); + $aes->disablePadding(); + $aes->setKey(pack('H*', '2b7e151628aed2a6abf7158809cf4f3c762e7160')); // 160-bit key. AES should null pad to 192-bits + $ciphertext = $aes->encrypt(pack('H*', '3243f6a8885a308d313198a2e0370734')); + $this->assertEquals($ciphertext, pack('H*', 'c109292b173f841b88e0ee49f13db8c0')); } }