* @copyright 2013 Andreas Fischer * @license http://www.opensource.org/licenses/mit-license.html MIT License */ require_once 'Crypt/AES.php'; abstract class Unit_Crypt_AES_TestCase extends PhpseclibTestCase { protected $engine; private function _checkEngine($aes) { if ($aes->getEngine() != $this->engine) { $engine = 'internal'; switch ($this->engine) { case CRYPT_ENGINE_OPENSSL: $engine = 'OpenSSL'; break; case CRYPT_ENGINE_MCRYPT: $engine = 'mcrypt'; } self::markTestSkipped('Unable to initialize ' . $engine . ' engine'); } } /** * Produces all combinations of test values. * * @return array */ public function continuousBufferCombos() { $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->setPreferredEngine($this->engine); $aes->enableContinuousBuffer(); $aes->setIV($iv); $aes->setKey($key); $this->_checkEngine($aes); $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->setPreferredEngine($this->engine); $aes->disablePadding(); $aes->setKey(pack('H*', '2b7e151628aed2a6abf7158809cf4f3c762e7160')); // 160-bit key. Valid in Rijndael. //$this->_checkEngine($aes); // should only work in internal mode $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->setPreferredEngine($this->engine); $aes->disablePadding(); $aes->setKey(pack('H*', '2b7e151628aed2a6abf7158809cf4f3c762e7160')); // 160-bit key. AES should null pad to 192-bits $this->_checkEngine($aes); $ciphertext = $aes->encrypt(pack('H*', '3243f6a8885a308d313198a2e0370734')); $this->assertEquals($ciphertext, pack('H*', 'c109292b173f841b88e0ee49f13db8c0')); } /** * Produces all combinations of test values. * * @return array */ public function continuousBufferBatteryCombos() { $modes = array( 'CRYPT_MODE_CTR', 'CRYPT_MODE_OFB', 'CRYPT_MODE_CFB', ); $combos = array( array(16), array(17), array(1, 16), array(3, 6, 7), // (3 to test the openssl_encrypt call and the buffer creation, 6 to test the exclusive use of the buffer and 7 to test the buffer's exhaustion and recreation) array(15, 4), // (15 to test openssl_encrypt call and buffer creation and 4 to test something that spans multpile bloc array(3, 6, 10, 16), // this is why the strlen check in the buffer-only code was needed array(16, 16), // two full size blocks array(3, 6, 7, 16), // partial block + full size block array(16, 3, 6, 7), // a few others just for fun array(32,32), array(31,31), array(17,17), array(99, 99) ); $result = array(); // @codingStandardsIgnoreStart foreach ($modes as $mode) foreach ($combos as $combo) foreach (array('encrypt', 'decrypt') as $op) $result[] = array($op, $mode, $combo); // @codingStandardsIgnoreEnd return $result; } /** * @dataProvider continuousBufferBatteryCombos */ public function testContinuousBufferBattery($op, $mode, $test) { $iv = str_repeat('x', 16); $key = str_repeat('a', 16); $aes = new Crypt_AES(constant($mode)); $aes->setPreferredEngine($this->engine); $aes->setKey($key); $aes->setIV($iv); $this->_checkEngine($aes); $str = ''; $result = ''; foreach ($test as $len) { $temp = str_repeat('d', $len); $str.= $temp; } $c1 = $aes->$op($str); $aes = new Crypt_AES(constant($mode)); $aes->setPreferredEngine($this->engine); $aes->enableContinuousBuffer(); $aes->setKey($key); $aes->setIV($iv); $this->_checkEngine($aes); foreach ($test as $len) { $temp = str_repeat('d', $len); $output = $aes->$op($temp); $result.= $output; } $c2 = $result; $this->assertSame(bin2hex($c1), bin2hex($c2)); } /** * @dataProvider continuousBufferBatteryCombos */ // pretty much the same as testContinuousBufferBattery with the caveat that continuous mode is not enabled public function testNonContinuousBufferBattery($op, $mode, $test) { if (count($test) == 1) { return; } $iv = str_repeat('x', 16); $key = str_repeat('a', 16); $aes = new Crypt_AES(constant($mode)); $aes->setPreferredEngine($this->engine); $aes->setKey($key); $aes->setIV($iv); $this->_checkEngine($aes); $str = ''; $result = ''; foreach ($test as $len) { $temp = str_repeat('d', $len); $str.= $temp; } $c1 = $aes->$op($str); $aes = new Crypt_AES(constant($mode)); $aes->setPreferredEngine($this->engine); $aes->setKey($key); $aes->setIV($iv); $this->_checkEngine($aes); foreach ($test as $len) { $temp = str_repeat('d', $len); $output = $aes->$op($temp); $result.= $output; } $c2 = $result; $this->assertNotSame(bin2hex($c1), bin2hex($c2)); } // from http://csrc.nist.gov/groups/STM/cavp/documents/aes/AESAVS.pdf#page=16 public function testGFSBox128() { $aes = new Crypt_AES(); $aes->setKey(pack('H*', '00000000000000000000000000000000')); $aes->setIV(pack('H*', '00000000000000000000000000000000')); $aes->disablePadding(); $aes->setPreferredEngine($this->engine); $this->_checkEngine($aes); $result = bin2hex($aes->encrypt('f34481ec3cc627bacd5dc3fb08f273e6')); $this->assertSame($result, '0336763e966d92595a567cc9ce537f5e'); $result = $aes->encrypt('9798c4640bad75c7c3227db910174e72'); $this->assertSame($result, 'a9a1631bf4996954ebc093957b234589'); $result = $aes->encrypt('96ab5c2ff612d9dfaae8c31f30c42168'); $this->assertSame($result, 'ff4f8391a6a40ca5b25d23bedd44a597'); $result = $aes->encrypt('6a118a874519e64e9963798a503f1d35'); $this->assertSame($result, 'dc43be40be0e53712f7e2bf5ca707209'); $result = $aes->encrypt('cb9fceec81286ca3e989bd979b0cb284'); $this->assertSame($result, '92beedab1895a94faa69b632e5cc47ce'); $result = $aes->encrypt('b26aeb1874e47ca8358ff22378f09144'); $this->assertSame($result, '459264f4798f6a78bacb89c15ed3d601'); $result = $aes->encrypt('58c8e00b2631686d54eab84b91f0aca1'); $this->assertSame($result, '08a4e2efec8a8e3312ca7460b9040bbf'); } public function testGFSBox192() { $aes = new Crypt_AES(); $aes->setKey(pack('H*', '000000000000000000000000000000000000000000000000')); $aes->setIV(pack('H*', '00000000000000000000000000000000')); $aes->disablePadding(); $aes->setPreferredEngine($this->engine); $this->_checkEngine($aes); $result = bin2hex($aes->encrypt('1b077a6af4b7f98229de786d7516b639')); $this->assertSame($result, '275cfc0413d8ccb70513c3859b1d0f72'); $result = $aes->encrypt('9c2d8842e5f48f57648205d39a239af1'); $this->assertSame($result, 'c9b8135ff1b5adc413dfd053b21bd96d'); $result = $aes->encrypt('bff52510095f518ecca60af4205444bb'); $this->assertSame($result, '4a3650c3371ce2eb35e389a171427440'); $result = $aes->encrypt('51719783d3185a535bd75adc65071ce1'); $this->assertSame($result, '4f354592ff7c8847d2d0870ca9481b7c'); $result = $aes->encrypt('26aa49dcfe7629a8901a69a9914e6dfd'); $this->assertSame($result, 'd5e08bf9a182e857cf40b3a36ee248cc'); $result = $aes->encrypt('941a4773058224e1ef66d10e0a6ee782'); $this->assertSame($result, '067cd9d3749207791841562507fa9626'); } public function testGFSBox256() { $aes = new Crypt_AES(); $aes->setKey(pack('H*', '00000000000000000000000000000000' . '00000000000000000000000000000000')); $aes->setIV(pack('H*', '00000000000000000000000000000000')); $aes->disablePadding(); $aes->setPreferredEngine($this->engine); $this->_checkEngine($aes); $result = bin2hex($aes->encrypt('014730f80ac625fe84f026c60bfd547d')); $this->assertSame($result, '5c9d844ed46f9885085e5d6a4f94c7d7'); $result = $aes->encrypt('0b24af36193ce4665f2825d7b4749c98'); $this->assertSame($result, 'a9ff75bd7cf6613d3731c77c3b6d0c04'); $result = $aes->encrypt('761c1fe41a18acf20d241650611d90f1'); $this->assertSame($result, '623a52fcea5d443e48d9181ab32c7421'); $result = $aes->encrypt('8a560769d605868ad80d819bdba03771'); $this->assertSame($result, '38f2c7ae10612415d27ca190d27da8b4'); $result = $aes->encrypt('91fbef2d15a97816060bee1feaa49afe'); $this->assertSame($result, '1bc704f1bce135ceb810341b216d7abe'); } }