EncrypterTest.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. <?php
  2. namespace Illuminate\Tests\Encryption;
  3. use Illuminate\Contracts\Encryption\DecryptException;
  4. use Illuminate\Encryption\Encrypter;
  5. use PHPUnit\Framework\TestCase;
  6. use RuntimeException;
  7. class EncrypterTest extends TestCase
  8. {
  9. public function testEncryption()
  10. {
  11. $e = new Encrypter(str_repeat('a', 16));
  12. $encrypted = $e->encrypt('foo');
  13. $this->assertNotSame('foo', $encrypted);
  14. $this->assertSame('foo', $e->decrypt($encrypted));
  15. }
  16. public function testRawStringEncryption()
  17. {
  18. $e = new Encrypter(str_repeat('a', 16));
  19. $encrypted = $e->encryptString('foo');
  20. $this->assertNotSame('foo', $encrypted);
  21. $this->assertSame('foo', $e->decryptString($encrypted));
  22. }
  23. public function testEncryptionUsingBase64EncodedKey()
  24. {
  25. $e = new Encrypter(random_bytes(16));
  26. $encrypted = $e->encrypt('foo');
  27. $this->assertNotSame('foo', $encrypted);
  28. $this->assertSame('foo', $e->decrypt($encrypted));
  29. }
  30. public function testEncryptedLengthIsFixed()
  31. {
  32. $e = new Encrypter(str_repeat('a', 16));
  33. $lengths = [];
  34. for ($i = 0; $i < 100; $i++) {
  35. $lengths[] = strlen($e->encrypt('foo'));
  36. }
  37. $this->assertSame(min($lengths), max($lengths));
  38. }
  39. public function testWithCustomCipher()
  40. {
  41. $e = new Encrypter(str_repeat('b', 32), 'AES-256-GCM');
  42. $encrypted = $e->encrypt('bar');
  43. $this->assertNotSame('bar', $encrypted);
  44. $this->assertSame('bar', $e->decrypt($encrypted));
  45. $e = new Encrypter(random_bytes(32), 'AES-256-GCM');
  46. $encrypted = $e->encrypt('foo');
  47. $this->assertNotSame('foo', $encrypted);
  48. $this->assertSame('foo', $e->decrypt($encrypted));
  49. }
  50. public function testCipherNamesCanBeMixedCase()
  51. {
  52. $upper = new Encrypter(str_repeat('b', 16), 'AES-128-GCM');
  53. $encrypted = $upper->encrypt('bar');
  54. $this->assertNotSame('bar', $encrypted);
  55. $lower = new Encrypter(str_repeat('b', 16), 'aes-128-gcm');
  56. $this->assertSame('bar', $lower->decrypt($encrypted));
  57. $mixed = new Encrypter(str_repeat('b', 16), 'aEs-128-GcM');
  58. $this->assertSame('bar', $mixed->decrypt($encrypted));
  59. }
  60. public function testThatAnAeadCipherIncludesTag()
  61. {
  62. $e = new Encrypter(str_repeat('b', 32), 'AES-256-GCM');
  63. $encrypted = $e->encrypt('foo');
  64. $data = json_decode(base64_decode($encrypted));
  65. $this->assertEmpty($data->mac);
  66. $this->assertNotEmpty($data->tag);
  67. }
  68. public function testThatAnAeadTagMustBeProvidedInFullLength()
  69. {
  70. $e = new Encrypter(str_repeat('b', 32), 'AES-256-GCM');
  71. $encrypted = $e->encrypt('foo');
  72. $data = json_decode(base64_decode($encrypted));
  73. $this->expectException(DecryptException::class);
  74. $this->expectExceptionMessage('Could not decrypt the data.');
  75. $data->tag = substr($data->tag, 0, 4);
  76. $encrypted = base64_encode(json_encode($data));
  77. $e->decrypt($encrypted);
  78. }
  79. public function testThatAnAeadTagCantBeModified()
  80. {
  81. $e = new Encrypter(str_repeat('b', 32), 'AES-256-GCM');
  82. $encrypted = $e->encrypt('foo');
  83. $data = json_decode(base64_decode($encrypted));
  84. $this->expectException(DecryptException::class);
  85. $this->expectExceptionMessage('Could not decrypt the data.');
  86. $data->tag[0] = $data->tag[0] === 'A' ? 'B' : 'A';
  87. $encrypted = base64_encode(json_encode($data));
  88. $e->decrypt($encrypted);
  89. }
  90. public function testThatANonAeadCipherIncludesMac()
  91. {
  92. $e = new Encrypter(str_repeat('b', 32), 'AES-256-CBC');
  93. $encrypted = $e->encrypt('foo');
  94. $data = json_decode(base64_decode($encrypted));
  95. $this->assertEmpty($data->tag);
  96. $this->assertNotEmpty($data->mac);
  97. }
  98. public function testDoNoAllowLongerKey()
  99. {
  100. $this->expectException(RuntimeException::class);
  101. $this->expectExceptionMessage('Unsupported cipher or incorrect key length. Supported ciphers are: aes-128-cbc, aes-256-cbc, aes-128-gcm, aes-256-gcm.');
  102. new Encrypter(str_repeat('z', 32));
  103. }
  104. public function testWithBadKeyLength()
  105. {
  106. $this->expectException(RuntimeException::class);
  107. $this->expectExceptionMessage('Unsupported cipher or incorrect key length. Supported ciphers are: aes-128-cbc, aes-256-cbc, aes-128-gcm, aes-256-gcm.');
  108. new Encrypter(str_repeat('a', 5));
  109. }
  110. public function testWithBadKeyLengthAlternativeCipher()
  111. {
  112. $this->expectException(RuntimeException::class);
  113. $this->expectExceptionMessage('Unsupported cipher or incorrect key length. Supported ciphers are: aes-128-cbc, aes-256-cbc, aes-128-gcm, aes-256-gcm.');
  114. new Encrypter(str_repeat('a', 16), 'AES-256-GCM');
  115. }
  116. public function testWithUnsupportedCipher()
  117. {
  118. $this->expectException(RuntimeException::class);
  119. $this->expectExceptionMessage('Unsupported cipher or incorrect key length. Supported ciphers are: aes-128-cbc, aes-256-cbc, aes-128-gcm, aes-256-gcm.');
  120. new Encrypter(str_repeat('c', 16), 'AES-256-CFB8');
  121. }
  122. public function testExceptionThrownWhenPayloadIsInvalid()
  123. {
  124. $this->expectException(DecryptException::class);
  125. $this->expectExceptionMessage('The payload is invalid.');
  126. $e = new Encrypter(str_repeat('a', 16));
  127. $payload = $e->encrypt('foo');
  128. $payload = str_shuffle($payload);
  129. $e->decrypt($payload);
  130. }
  131. public function testDecryptionExceptionIsThrownWhenUnexpectedTagIsAdded()
  132. {
  133. $this->expectException(DecryptException::class);
  134. $this->expectExceptionMessage('Unable to use tag because the cipher algorithm does not support AEAD.');
  135. $e = new Encrypter(str_repeat('a', 16));
  136. $payload = $e->encrypt('foo');
  137. $decodedPayload = json_decode(base64_decode($payload));
  138. $decodedPayload->tag = 'set-manually';
  139. $e->decrypt(base64_encode(json_encode($decodedPayload)));
  140. }
  141. public function testExceptionThrownWithDifferentKey()
  142. {
  143. $this->expectException(DecryptException::class);
  144. $this->expectExceptionMessage('The MAC is invalid.');
  145. $a = new Encrypter(str_repeat('a', 16));
  146. $b = new Encrypter(str_repeat('b', 16));
  147. $b->decrypt($a->encrypt('baz'));
  148. }
  149. public function testExceptionThrownWhenIvIsTooLong()
  150. {
  151. $this->expectException(DecryptException::class);
  152. $this->expectExceptionMessage('The payload is invalid.');
  153. $e = new Encrypter(str_repeat('a', 16));
  154. $payload = $e->encrypt('foo');
  155. $data = json_decode(base64_decode($payload), true);
  156. $data['iv'] .= $data['value'][0];
  157. $data['value'] = substr($data['value'], 1);
  158. $modified_payload = base64_encode(json_encode($data));
  159. $e->decrypt($modified_payload);
  160. }
  161. public function testSupportedMethodAcceptsAnyCasing()
  162. {
  163. $key = str_repeat('a', 16);
  164. $this->assertTrue(Encrypter::supported($key, 'AES-128-GCM'));
  165. $this->assertTrue(Encrypter::supported($key, 'aes-128-CBC'));
  166. $this->assertTrue(Encrypter::supported($key, 'aes-128-cbc'));
  167. }
  168. }