1 /** 2 * Licensed under the Apache License, Version 2.0 (the "License"); 3 * you may not use this file except in compliance with the License. 4 * You may obtain a copy of the License at 5 * 6 * http://www.apache.org/licenses/LICENSE-2.0 7 * 8 * Unless required by applicable law or agreed to in writing, software 9 * distributed under the License is distributed on an "AS IS" BASIS, 10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 * See the License for the specific language governing permissions and 12 * limitations under the License. 13 */ 14 package com.google.security.wycheproof; 15 16 import static org.junit.Assert.assertArrayEquals; 17 import static org.junit.Assert.assertEquals; 18 import static org.junit.Assert.assertFalse; 19 import static org.junit.Assert.fail; 20 21 import java.nio.ByteBuffer; 22 import java.security.AlgorithmParameterGenerator; 23 import java.security.AlgorithmParameters; 24 import java.security.GeneralSecurityException; 25 import java.security.InvalidAlgorithmParameterException; 26 import java.security.InvalidKeyException; 27 import java.security.KeyStore; 28 import java.security.KeyStoreException; 29 import java.security.NoSuchAlgorithmException; 30 import java.security.NoSuchProviderException; 31 import java.security.SecureRandom; 32 import java.security.UnrecoverableKeyException; 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import javax.crypto.Cipher; 36 import javax.crypto.NoSuchPaddingException; 37 import javax.crypto.SecretKey; 38 import javax.crypto.ShortBufferException; 39 import javax.crypto.spec.GCMParameterSpec; 40 import javax.crypto.spec.IvParameterSpec; 41 import javax.crypto.spec.SecretKeySpec; 42 import org.junit.Test; 43 import org.junit.Ignore; 44 import org.junit.Before; 45 import org.junit.After; 46 import android.keystore.cts.util.KeyStoreUtil; 47 import android.security.keystore.KeyProtection; 48 import android.security.keystore.KeyProperties; 49 50 // TODO(bleichen): 51 // - For EAX I was able to derive some special cases by inverting OMAC. 52 // Not sure if that is possible here. 53 /** 54 * Testing AES-GCM 55 * 56 * <p>Other tests using AES-GCM are: CipherInputStreamTest.java CipherOutputStreamTest.java 57 */ 58 public class AesGcmTest { 59 private static final String EXPECTED_PROVIDER_NAME = TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME; 60 private KeyStore keyStore; 61 private static final String KEY_ALIAS_1 = "Key1"; 62 private static final String KEY_ALIAS_2 = "Key2"; 63 private static final String KEY_ALIAS_3 = "Key3"; 64 private static final String KEY_ALIAS_4 = "Key4"; 65 private static final String KEY_ALIAS_5 = "Key5"; 66 private static final String KEY_ALIAS_6 = "Key6"; 67 private static final String KEY_ALIAS_7 = "Key7"; 68 private static final String KEY_ALIAS_8 = "Key8"; 69 private static final String KEY_ALIAS_9 = "Key9"; 70 71 @Before setup()72 public void setup() throws Exception { 73 keyStore = KeyStore.getInstance("AndroidKeyStore"); 74 keyStore.load(null); 75 boolean hasStrongBox = KeyStoreUtil.hasStrongBox(); 76 for (GcmTestVector test : GCM_TEST_VECTORS) { 77 setKeystoreEntry(test.alias, test.key, false); 78 if (hasStrongBox) { 79 setKeystoreEntry(test.alias_sb, test.key, true); 80 } 81 } 82 } 83 84 @After tearDown()85 public void tearDown() throws Exception { 86 KeyStoreUtil.cleanUpKeyStore(); 87 } 88 setKeystoreEntry(String alias, SecretKeySpec key, boolean isStrongBox)89 private SecretKey setKeystoreEntry(String alias, SecretKeySpec key, boolean isStrongBox) 90 throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { 91 keyStore.setEntry( 92 alias, 93 new KeyStore.SecretKeyEntry(key), 94 new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) 95 .setBlockModes(KeyProperties.BLOCK_MODE_ECB, KeyProperties.BLOCK_MODE_GCM) 96 .setRandomizedEncryptionRequired(false) 97 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) 98 .setIsStrongBoxBacked(isStrongBox) 99 .build()); 100 // Key imported, obtain a reference to it. 101 return (SecretKey) keyStore.getKey(alias, null); 102 } 103 getKey(String alias)104 private SecretKey getKey(String alias) throws Exception { 105 return (SecretKey) keyStore.getKey(alias, null); 106 } 107 getInitializedCipherInstance(String algorithm, int operationMode, GcmTestVector testVector, boolean isStrongBox)108 private Cipher getInitializedCipherInstance(String algorithm, int operationMode, 109 GcmTestVector testVector, boolean isStrongBox) 110 throws Exception { 111 Cipher cipher = Cipher.getInstance(algorithm, 112 EXPECTED_PROVIDER_NAME); 113 cipher.init(operationMode, getKey(isStrongBox ? testVector.alias_sb : testVector.alias), 114 testVector.parameters); 115 return cipher; 116 } 117 118 /** Test vectors */ 119 public static class GcmTestVector { 120 final byte[] pt; 121 final byte[] aad; 122 final byte[] ct; 123 final String ptHex; 124 final String ctHex; 125 final GCMParameterSpec parameters; 126 final SecretKeySpec key; 127 final int nonceLengthInBits; 128 final int tagLengthInBits; 129 final String alias; 130 final String alias_sb; 131 GcmTestVector( String message, String keyMaterial, String nonce, String aad, String ciphertext, String tag, String alias)132 public GcmTestVector( 133 String message, 134 String keyMaterial, 135 String nonce, 136 String aad, 137 String ciphertext, 138 String tag, 139 String alias) { 140 this.ptHex = message; 141 this.pt = TestUtil.hexToBytes(message); 142 this.aad = TestUtil.hexToBytes(aad); 143 this.ct = TestUtil.hexToBytes(ciphertext + tag); 144 this.ctHex = ciphertext + tag; 145 this.tagLengthInBits = 4 * tag.length(); 146 this.nonceLengthInBits = 4 * nonce.length(); 147 this.parameters = new GCMParameterSpec(tagLengthInBits, TestUtil.hexToBytes(nonce)); 148 this.key = new SecretKeySpec(TestUtil.hexToBytes(keyMaterial), "AES"); 149 this.alias = alias; 150 this.alias_sb = alias + "_sb"; 151 } 152 }; 153 154 private static final GcmTestVector[] GCM_TEST_VECTORS = { 155 new GcmTestVector( 156 "001d0c231287c1182784554ca3a21908", 157 "5b9604fe14eadba931b0ccf34843dab9", 158 "028318abc1824029138141a2", 159 "", 160 "26073cc1d851beff176384dc9896d5ff", 161 "0a3ea7a5487cb5f7d70fb6c58d038554", KEY_ALIAS_1), 162 new GcmTestVector( 163 "001d0c231287c1182784554ca3a21908", 164 "5b9604fe14eadba931b0ccf34843dab9", 165 "921d2507fa8007b7bd067d34", 166 "00112233445566778899aabbccddeeff", 167 "49d8b9783e911913d87094d1f63cc765", 168 "1e348ba07cca2cf04c618cb4", KEY_ALIAS_2), 169 new GcmTestVector( 170 "2035af313d1346ab00154fea78322105", 171 "aa023d0478dcb2b2312498293d9a9129", 172 "0432bc49ac34412081288127", 173 "aac39231129872a2", 174 "eea945f3d0f98cc0fbab472a0cf24e87", 175 "4bb9b4812519dadf9e1232016d068133", KEY_ALIAS_3), 176 new GcmTestVector( 177 "2035af313d1346ab00154fea78322105", 178 "aa023d0478dcb2b2312498293d9a9129", 179 "0432bc49ac344120", 180 "aac39231129872a2", 181 "64c36bb3b732034e3a7d04efc5197785", 182 "b7d0dd70b00d65b97cfd080ff4b819d1", KEY_ALIAS_4), 183 new GcmTestVector( 184 "02efd2e5782312827ed5d230189a2a342b277ce048462193", 185 "2034a82547276c83dd3212a813572bce", 186 "3254202d854734812398127a3d134421", 187 "1a0293d8f90219058902139013908190bc490890d3ff12a3", 188 "64069c2d58690561f27ee199e6b479b6369eec688672bde9", 189 "9b7abadd6e69c1d9ec925786534f5075", KEY_ALIAS_5), 190 // GCM uses GHASH to compute the initial counter J0 if the nonce is not 12 bytes long. 191 // The counter is incremented modulo 2^32 in counter mode. The following test vectors verify 192 // the behavior of an implementation for initial counter values J0 close to a 2^32 limit. 193 // J0:00000000000000000000000000000000 194 new GcmTestVector( 195 "00000000000000000000000000000000000000000000000000000000000000000000000000000000", 196 "00112233445566778899aabbccddeeff", 197 "7b95b8c356810a84711d68150a1b7750", 198 "", 199 "84d4c9c08b4f482861e3a9c6c35bc4d91df927374513bfd49f436bd73f325285daef4ff7e13d46a6", 200 "213a3cb93855d18e69337eee66aeec07", KEY_ALIAS_6), 201 // J0:ffffffffffffffffffffffffffffffff 202 new GcmTestVector( 203 "00000000000000000000000000000000000000000000000000000000000000000000000000000000", 204 "00112233445566778899aabbccddeeff", 205 "1a552e67cdc4dc1a33b824874ebf0bed", 206 "", 207 "948ca37a8e6649e88aeffb1c598f3607007702417ea0e0bc3c60ad5a949886de968cf53ea6462aed", 208 "99b381bfa2af9751c39d1b6e86d1be6a", KEY_ALIAS_7), 209 // J0:000102030405060708090a0bffffffff 210 new GcmTestVector( 211 "00000000000000000000000000000000000000000000000000000000000000000000000000000000", 212 "00112233445566778899aabbccddeeff", 213 "99821c2dd5daecded07300f577f7aff1", 214 "", 215 "127af9b39ecdfc57bb11a2847c7c2d3d8f938f40f877e0c4af37d0fe9af033052bd537c4ae978f60", 216 "07eb2fe4a958f8434d40684899507c7c", KEY_ALIAS_8), 217 // J0:000102030405060708090a0bfffffffe 218 new GcmTestVector( 219 "00000000000000000000000000000000000000000000000000000000000000000000000000000000", 220 "00112233445566778899aabbccddeeff", 221 "5e4a3900142358d1c774d8d124d8d27d", 222 "", 223 "0cf6ae47156b14dce03c8a07a2e172b1127af9b39ecdfc57bb11a2847c7c2d3d8f938f40f877e0c4", 224 "f145c2dcaf339eede427be934357eac0", KEY_ALIAS_9), 225 }; 226 227 /** 228 * Returns the GCM test vectors supported by the current provider. This is necessary since not 229 * every provider supports all parameters sizes. For example SUNJCE does not support 8 byte tags 230 * and Conscrypt only supports 12 byte nonces. Such restrictions are often made because AES-GCM is 231 * a relatively weak algorithm and especially small parameter sizes can lead to easy attacks. 232 * Avoiding such small parameter sizes should not be seen as a bug in the library. 233 * 234 * <p>The only assumption we make here is that all test vectors with 128 bit tags and nonces with 235 * at least 96 bits are supported. 236 */ getTestVectors(boolean isStrongBox)237 private Iterable<GcmTestVector> getTestVectors(boolean isStrongBox) throws Exception { 238 ArrayList<GcmTestVector> supported = new ArrayList<GcmTestVector>(); 239 for (GcmTestVector test : GCM_TEST_VECTORS) { 240 if (test.nonceLengthInBits != 96 || test.tagLengthInBits != 128) { 241 try { 242 // Checks whether the parameter size is supported. 243 // It would be nice if there was a way to check this without trying to encrypt. 244 getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE, 245 test, isStrongBox); 246 } catch (InvalidKeyException | InvalidAlgorithmParameterException ex) { 247 // Not supported 248 continue; 249 } 250 } 251 supported.add(test); 252 } 253 return supported; 254 } 255 256 @Test testVectors()257 public void testVectors() throws Exception { 258 testVectors(false); 259 } 260 @Test testVectors_StrongBox()261 public void testVectors_StrongBox() throws Exception { 262 KeyStoreUtil.assumeStrongBox(); 263 testVectors(true); 264 } testVectors(boolean isStrongBox)265 private void testVectors(boolean isStrongBox) throws Exception { 266 for (GcmTestVector test : getTestVectors(isStrongBox)) { 267 Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE, 268 test, isStrongBox); 269 cipher.updateAAD(test.aad); 270 byte[] ct = cipher.doFinal(test.pt); 271 assertEquals(test.ctHex, TestUtil.bytesToHex(ct)); 272 } 273 } 274 275 /** Test encryption when update and doFinal are done with empty byte arrays. */ 276 @Test testEncryptWithEmptyArrays()277 public void testEncryptWithEmptyArrays() throws Exception { 278 testEncryptWithEmptyArrays(false); 279 } 280 @Test testEncryptWithEmptyArrays_StrongBox()281 public void testEncryptWithEmptyArrays_StrongBox() throws Exception { 282 KeyStoreUtil.assumeStrongBox(); 283 testEncryptWithEmptyArrays(true); 284 } testEncryptWithEmptyArrays(boolean isStrongBox)285 private void testEncryptWithEmptyArrays(boolean isStrongBox) throws Exception { 286 for (GcmTestVector test : getTestVectors(isStrongBox)) { 287 // Encryption 288 byte[] empty = new byte[0]; 289 Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE, 290 test, isStrongBox); 291 int outputSize = cipher.getOutputSize(test.pt.length); 292 ByteBuffer ctBuffer = ByteBuffer.allocate(outputSize); 293 cipher.updateAAD(empty); 294 cipher.updateAAD(test.aad); 295 byte[] res = cipher.update(empty); 296 if (res != null) { 297 ctBuffer.put(res); 298 } 299 res = cipher.update(test.pt); 300 if (res != null) { 301 ctBuffer.put(res); 302 } 303 res = cipher.doFinal(empty); 304 if (res != null) { 305 ctBuffer.put(res); 306 } 307 assertEquals(test.ctHex, TestUtil.byteBufferToHex(ctBuffer)); 308 } 309 } 310 311 @Test testDecryptWithEmptyArrays()312 public void testDecryptWithEmptyArrays() throws Exception { 313 testDecryptWithEmptyArrays(false); 314 } 315 @Test testDecryptWithEmptyArrays_StrongBox()316 public void testDecryptWithEmptyArrays_StrongBox() throws Exception { 317 KeyStoreUtil.assumeStrongBox(); 318 testDecryptWithEmptyArrays(true); 319 } testDecryptWithEmptyArrays(boolean isStrongBox)320 private void testDecryptWithEmptyArrays(boolean isStrongBox) throws Exception { 321 for (GcmTestVector test : getTestVectors(isStrongBox)) { 322 byte[] empty = new byte[0]; 323 Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE, 324 test, isStrongBox); 325 int outputSize = cipher.getOutputSize(test.ct.length); 326 ByteBuffer ptBuffer = ByteBuffer.allocate(outputSize); 327 cipher.updateAAD(empty); 328 cipher.updateAAD(test.aad); 329 byte[] res = cipher.update(empty); 330 if (res != null) { 331 ptBuffer.put(res); 332 } 333 res = cipher.update(test.ct); 334 if (res != null) { 335 ptBuffer.put(res); 336 } 337 res = cipher.doFinal(empty); 338 if (res != null) { 339 ptBuffer.put(res); 340 } 341 assertEquals(test.ptHex, TestUtil.byteBufferToHex(ptBuffer)); 342 343 // Simple test that a modified ciphertext fails. 344 ptBuffer.clear(); 345 cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE, test, 346 isStrongBox); 347 cipher.updateAAD(empty); 348 cipher.updateAAD(test.aad); 349 cipher.updateAAD(new byte[1]); 350 res = cipher.update(empty); 351 if (res != null) { 352 ptBuffer.put(res); 353 } 354 res = cipher.update(test.ct); 355 if (res != null) { 356 ptBuffer.put(res); 357 } 358 try { 359 cipher.doFinal(empty); 360 fail("Accepted modified ciphertext."); 361 } catch (GeneralSecurityException ex) { 362 // Expected 363 } 364 } 365 } 366 367 /** 368 * Typically one should always call updateAAD before any call to update. This test checks what 369 * happens if the order is reversed. The test expects that a correct implementation either 370 * computes the tag correctly or throws an exception. 371 * 372 * <p>For example, OpenJdk did compute incorrect tags in this case. The bug has been fixed in 373 * http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/89c06ca1e6cc 374 * 375 * <p>For example BouncyCastle computes correct tags if the calls are reversed, SunJCE and OpenJdk 376 * now throw exceptions. 377 */ 378 @Test testLateUpdateAAD()379 public void testLateUpdateAAD() throws Exception { 380 testLateUpdateAAD(false); 381 } 382 @Test testLateUpdateAAD_StrongBox()383 public void testLateUpdateAAD_StrongBox() throws Exception { 384 KeyStoreUtil.assumeStrongBox(); 385 testLateUpdateAAD(true); 386 } testLateUpdateAAD(boolean isStrongBox)387 private void testLateUpdateAAD(boolean isStrongBox) throws Exception { 388 for (GcmTestVector test : getTestVectors(isStrongBox)) { 389 Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE, test, 390 isStrongBox); 391 byte[] c0 = cipher.update(test.pt); 392 try { 393 cipher.updateAAD(test.aad); 394 } catch (java.lang.IllegalStateException ex) { 395 // Throwing an exception is valid behaviour. 396 continue; 397 } 398 byte[] c1 = cipher.doFinal(); 399 String result = TestUtil.bytesToHex(c0) + TestUtil.bytesToHex(c1); 400 assertEquals(test.ctHex, result); 401 } 402 } 403 404 /** 405 * JCE has a dangerous feature: after a doFinal the cipher is typically reinitialized using the 406 * previous IV. This "feature" can easily break AES-GCM usages, because encrypting twice with the 407 * same key and IV leaks the authentication key. Hence any reasonable implementation of AES-GCM 408 * should not allow this. The expected behaviour of OpenJDK can be derived from the tests in 409 * jdk/test/com/sun/crypto/provider/Cipher/AES/TestGCMKeyAndIvCheck.java. OpenJDK does not allow 410 * two consecutive initializations for encryption with the same key and IV. 411 * 412 * <p>The test here is weaker than the restrictions in OpenJDK. The only requirement here is that 413 * reusing a Cipher without an explicit init() is caught. 414 * 415 * <p>BouncyCastle 1.52 failed this test 416 * 417 * <p>Conscrypt failed this test 418 */ 419 @Test testIvReuse()420 public void testIvReuse() throws Exception { 421 testIvReuse(false); 422 } 423 @Test testIvReuse_StrongBox()424 public void testIvReuse_StrongBox() throws Exception { 425 KeyStoreUtil.assumeStrongBox(); 426 testIvReuse(true); 427 } testIvReuse(boolean isStrongBox)428 private void testIvReuse(boolean isStrongBox) throws Exception { 429 for (GcmTestVector test : getTestVectors(isStrongBox)) { 430 Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE, test, 431 isStrongBox); 432 cipher.updateAAD(test.aad); 433 byte[] ct1 = cipher.doFinal(test.pt); 434 try { 435 byte[] ct2 = cipher.doFinal(test.pt); 436 fail( 437 "It should not possible to reuse an IV." 438 + " ct1:" 439 + TestUtil.bytesToHex(ct1) 440 + " ct2:" 441 + TestUtil.bytesToHex(ct2)); 442 } catch (java.lang.IllegalStateException ex) { 443 // This is expected. 444 } 445 } 446 } 447 448 /** 449 * Checks whether the implementation requires larger ByteBuffers than necessary. This test has 450 * been added mostly for debugging. E.g., conscrypt failed during decryption with ByteBuffers 451 * simply because the necessary outputSize was computed incorrectly. 452 */ 453 @Test testByteBufferSize()454 public void testByteBufferSize() throws Exception { 455 testByteBufferSize(false); 456 } 457 @Test testByteBufferSize_StrongBox()458 public void testByteBufferSize_StrongBox() throws Exception { 459 KeyStoreUtil.assumeStrongBox(); 460 testByteBufferSize(true); 461 } testByteBufferSize(boolean isStrongBox)462 private void testByteBufferSize(boolean isStrongBox) throws Exception { 463 for (GcmTestVector test : getTestVectors(isStrongBox)) { 464 Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE, 465 test, isStrongBox); 466 int outputSize = cipher.getOutputSize(test.pt.length); 467 assertEquals("plaintext size:" + test.pt.length, test.ct.length, outputSize); 468 // Decryption 469 cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE, 470 test, isStrongBox); 471 outputSize = cipher.getOutputSize(test.ct.length); 472 assertEquals("ciphertext size:" + test.ct.length, test.pt.length, outputSize); 473 } 474 } 475 476 /** Encryption with ByteBuffers. */ 477 @Test testByteBuffer()478 public void testByteBuffer() throws Exception { 479 testByteBuffer(false); 480 } 481 @Test testByteBuffer_StrongBox()482 public void testByteBuffer_StrongBox() throws Exception { 483 KeyStoreUtil.assumeStrongBox(); 484 testByteBuffer(true); 485 } testByteBuffer(boolean isStrongBox)486 private void testByteBuffer(boolean isStrongBox) throws Exception { 487 for (GcmTestVector test : getTestVectors(isStrongBox)) { 488 // Encryption 489 ByteBuffer ptBuffer = ByteBuffer.wrap(test.pt); 490 Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE, 491 test, isStrongBox); 492 int outputSize = cipher.getOutputSize(test.pt.length); 493 ByteBuffer ctBuffer = ByteBuffer.allocate(outputSize); 494 cipher.updateAAD(test.aad); 495 cipher.doFinal(ptBuffer, ctBuffer); 496 assertEquals(test.ctHex, TestUtil.byteBufferToHex(ctBuffer)); 497 498 // Decryption 499 ctBuffer.flip(); 500 cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE, 501 test, isStrongBox); 502 outputSize = cipher.getOutputSize(test.ct.length); 503 ByteBuffer decrypted = ByteBuffer.allocate(outputSize); 504 cipher.updateAAD(test.aad); 505 cipher.doFinal(ctBuffer, decrypted); 506 assertEquals(test.ptHex, TestUtil.byteBufferToHex(decrypted)); 507 } 508 } 509 510 /** Encryption with ByteBuffers should be copy-safe. */ 511 @Test testByteBufferAlias()512 public void testByteBufferAlias() throws Exception { 513 testByteBufferAlias(false); 514 } 515 @Test testByteBufferAlias_StrongBox()516 public void testByteBufferAlias_StrongBox() throws Exception { 517 KeyStoreUtil.assumeStrongBox(); 518 testByteBufferAlias(true); 519 } testByteBufferAlias(boolean isStrongBox)520 private void testByteBufferAlias(boolean isStrongBox) throws Exception { 521 for (GcmTestVector test : getTestVectors(isStrongBox)) { 522 // Encryption 523 Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE, 524 test, isStrongBox); 525 int outputSize = cipher.getOutputSize(test.pt.length); 526 byte[] backingArray = new byte[outputSize]; 527 ByteBuffer ptBuffer = ByteBuffer.wrap(backingArray); 528 ptBuffer.put(test.pt); 529 ptBuffer.flip(); 530 ByteBuffer ctBuffer = ByteBuffer.wrap(backingArray); 531 cipher.updateAAD(test.aad); 532 cipher.doFinal(ptBuffer, ctBuffer); 533 assertEquals(test.ctHex, TestUtil.byteBufferToHex(ctBuffer)); 534 535 // Decryption 536 ByteBuffer decrypted = ByteBuffer.wrap(backingArray); 537 ctBuffer.flip(); 538 cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE, 539 test, isStrongBox); 540 cipher.updateAAD(test.aad); 541 cipher.doFinal(ctBuffer, decrypted); 542 assertEquals(test.ptHex, TestUtil.byteBufferToHex(decrypted)); 543 } 544 } 545 546 /** Encryption and decryption with large arrays should be copy-safe. */ 547 @Test testLargeArrayAlias()548 public void testLargeArrayAlias() throws Exception { 549 testLargeArrayAlias(false); 550 } 551 @Test testLargeArrayAlias_StrongBox()552 public void testLargeArrayAlias_StrongBox() throws Exception { 553 KeyStoreUtil.assumeStrongBox(); 554 testLargeArrayAlias(true); 555 } testLargeArrayAlias(boolean isStrongBox)556 private void testLargeArrayAlias(boolean isStrongBox) throws Exception { 557 byte[] ptVector = new byte[8192]; 558 559 // this offset is relative to the start of the input, not the start of the buffer. 560 for (int outputOffset = -32; outputOffset <= 32; outputOffset++) { 561 // try with doFinal directly as well as with update followed by doFinal 562 for (int useUpdate = 0; useUpdate <= 1; useUpdate++) { 563 SecretKey secretKey = null; 564 try { 565 String alias = "TestKey" + 1; 566 SecretKeySpec keySpec = new SecretKeySpec(new byte[16], "AES"); 567 secretKey = setKeystoreEntry(alias, keySpec, isStrongBox); 568 } catch (Exception e) { 569 fail("Failed to set secret key entry in KeyStore."); 570 } 571 GCMParameterSpec parameters = new GCMParameterSpec(128, new byte[12]); 572 Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); 573 cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameters); 574 575 // these offsets are relative to the start of the buffer 576 int inputOffsetInBuffer = 32; 577 int outputOffsetInBuffer = inputOffsetInBuffer + outputOffset; 578 int sliceLength = cipher.getOutputSize(ptVector.length); 579 580 byte[] inBuf = new byte[sliceLength + Math.max(inputOffsetInBuffer, outputOffsetInBuffer)]; 581 byte[] outBuf = inBuf; 582 583 System.arraycopy(ptVector, 0, inBuf, inputOffsetInBuffer, ptVector.length); 584 585 try { 586 int ctLength = 0; 587 if (useUpdate > 0) { 588 ctLength += 589 cipher.update( 590 inBuf, inputOffsetInBuffer, ptVector.length, outBuf, outputOffsetInBuffer); 591 ctLength += cipher.doFinal(inBuf, 0, 0, outBuf, outputOffsetInBuffer + ctLength); 592 } else { 593 ctLength += 594 cipher.doFinal( 595 inBuf, inputOffsetInBuffer, ptVector.length, outBuf, outputOffsetInBuffer); 596 } 597 598 System.arraycopy(outBuf, outputOffsetInBuffer, inBuf, inputOffsetInBuffer, ctLength); 599 600 cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); 601 cipher.init(Cipher.DECRYPT_MODE, secretKey, parameters); 602 603 int resultPtLength = 0; 604 if (useUpdate > 0) { 605 resultPtLength += 606 cipher.update(inBuf, inputOffsetInBuffer, ctLength, outBuf, outputOffsetInBuffer); 607 resultPtLength += 608 cipher.doFinal(inBuf, 0, 0, outBuf, outputOffsetInBuffer + resultPtLength); 609 } else { 610 resultPtLength += 611 cipher.doFinal(inBuf, inputOffsetInBuffer, ctLength, outBuf, outputOffsetInBuffer); 612 } 613 614 assertEquals(resultPtLength, ptVector.length); 615 assertArrayEquals( 616 ptVector, 617 Arrays.copyOfRange( 618 outBuf, outputOffsetInBuffer, outputOffsetInBuffer + resultPtLength)); 619 } catch (Throwable t) { 620 throw new AssertionError( 621 "testLargeByteBufferAlias failed with outputOffset=" + outputOffset, t); 622 } 623 } 624 } 625 } 626 627 /** 628 * Encryption with ByteBuffers should be copy-safe even if the buffers have different starting 629 * offsets and/or do not make the backing array visible. 630 * 631 * <p>Note that bugs in this often require a sizeable input to reproduce; the default 632 * implementation of engineUpdate(ByteBuffer, ByteBuffer) copies through 4KB bounce buffers, so we 633 * need to use something larger to see any problems - 8KB is what we use here. 634 * 635 * @see https://bugs.openjdk.java.net/browse/JDK-8181386 636 */ 637 @Test testByteBufferShiftedAlias()638 public void testByteBufferShiftedAlias() throws Exception { 639 testByteBufferShiftedAlias(false); 640 } 641 @Test testByteBufferShiftedAlias_StrongBox()642 public void testByteBufferShiftedAlias_StrongBox() throws Exception { 643 KeyStoreUtil.assumeStrongBox(); 644 testByteBufferShiftedAlias(true); 645 } testByteBufferShiftedAlias(boolean isStrongBox)646 private void testByteBufferShiftedAlias(boolean isStrongBox) throws Exception { 647 byte[] ptVector = new byte[8192]; 648 649 for (int i = 0; i < 3; i++) { 650 // outputOffset = offset relative to start of input. 651 for (int outputOffset = -1; outputOffset <= 1; outputOffset++) { 652 653 SecretKey secretKey = null; 654 try { 655 String alias = "TestKey" + 1; 656 SecretKeySpec keySpec = new SecretKeySpec(new byte[16], "AES"); 657 secretKey = setKeystoreEntry(alias, keySpec, isStrongBox); 658 } catch (Exception e) { 659 fail("Failed to set secret key entry in KeyStore."); 660 } 661 GCMParameterSpec parameters = new GCMParameterSpec(128, new byte[12]); 662 Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); 663 cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameters); 664 665 ByteBuffer output, input, inputRO; 666 667 // We'll try three scenarios: Ordinary array backed buffers, array backed buffers where one 668 // is read-only, and direct byte buffers. 669 String mode; 670 // offsets relative to start of buffer 671 int inputOffsetInBuffer = 1; 672 int outputOffsetInBuffer = inputOffsetInBuffer + outputOffset; 673 int sliceLength = cipher.getOutputSize(ptVector.length); 674 int bufferSize = sliceLength + Math.max(inputOffsetInBuffer, outputOffsetInBuffer); 675 switch (i) { 676 case 0: 677 case 1: 678 { 679 byte[] buffer = new byte[bufferSize]; 680 // It's important to slice() here as otherwise later when we flip() position will be 681 // reset to 0. 682 output = ByteBuffer.wrap(buffer, outputOffsetInBuffer, sliceLength).slice(); 683 input = ByteBuffer.wrap(buffer, inputOffsetInBuffer, sliceLength).slice(); 684 685 if (i == 1) { 686 mode = "array backed buffers with RO buffer"; 687 inputRO = input.asReadOnlyBuffer(); 688 } else { 689 mode = "array backed buffers"; 690 inputRO = input.duplicate(); 691 } 692 693 break; 694 } 695 case 2: 696 { 697 mode = "direct buffers"; 698 ByteBuffer buf = ByteBuffer.allocateDirect(bufferSize); 699 output = buf.duplicate(); 700 output.position(outputOffsetInBuffer); 701 output.limit(sliceLength + outputOffsetInBuffer); 702 output = output.slice(); 703 704 input = buf.duplicate(); 705 input.position(inputOffsetInBuffer); 706 input.limit(sliceLength + inputOffsetInBuffer); 707 input = input.slice(); 708 709 inputRO = input.duplicate(); 710 break; 711 } 712 default: 713 { 714 throw new AssertionError("Unknown test index " + i); 715 } 716 } 717 718 // Now that we have our overlapping 'input' and 'output' buffers, we can write our plaintext 719 // into the input buffer. 720 input.put(ptVector); 721 input.flip(); 722 // Make sure the RO input buffer has the same limit in case the plaintext is shorter than 723 // sliceLength (which it generally will be for anything other than ECB or CTR mode) 724 inputRO.limit(input.limit()); 725 726 try { 727 int ctSize = cipher.doFinal(inputRO, output); 728 729 // Now flip the buffers around and undo everything 730 byte[] tmp = new byte[ctSize]; 731 output.flip(); 732 output.get(tmp); 733 734 output.clear(); 735 input.clear(); 736 inputRO.clear(); 737 738 input.put(tmp); 739 input.flip(); 740 inputRO.limit(input.limit()); 741 742 cipher.init(Cipher.DECRYPT_MODE, secretKey, parameters); 743 cipher.doFinal(inputRO, output); 744 745 output.flip(); 746 assertEquals(ByteBuffer.wrap(ptVector), output); 747 } catch (Throwable t) { 748 throw new AssertionError( 749 "Overlapping buffers test failed with buffer type: " 750 + mode 751 + " and output offset " 752 + outputOffset, 753 t); 754 } 755 } 756 } 757 } 758 759 @Test testReadOnlyByteBuffer()760 public void testReadOnlyByteBuffer() throws Exception { 761 testReadOnlyByteBuffer(false); 762 } 763 @Test testReadOnlyByteBuffer_StrongBox()764 public void testReadOnlyByteBuffer_StrongBox() throws Exception { 765 KeyStoreUtil.assumeStrongBox(); 766 testReadOnlyByteBuffer(true); 767 } testReadOnlyByteBuffer(boolean isStrongBox)768 private void testReadOnlyByteBuffer(boolean isStrongBox) throws Exception { 769 for (GcmTestVector test : getTestVectors(isStrongBox)) { 770 // Encryption 771 ByteBuffer ptBuffer = ByteBuffer.wrap(test.pt).asReadOnlyBuffer(); 772 Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE, 773 test, isStrongBox); 774 int outputSize = cipher.getOutputSize(test.pt.length); 775 ByteBuffer ctBuffer = ByteBuffer.allocate(outputSize); 776 cipher.updateAAD(test.aad); 777 cipher.doFinal(ptBuffer, ctBuffer); 778 assertEquals(test.ctHex, TestUtil.byteBufferToHex(ctBuffer)); 779 780 // Decryption 781 ctBuffer.flip(); 782 ctBuffer = ctBuffer.asReadOnlyBuffer(); 783 cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE, 784 test, isStrongBox); 785 outputSize = cipher.getOutputSize(test.ct.length); 786 ByteBuffer decrypted = ByteBuffer.allocate(outputSize); 787 cipher.updateAAD(test.aad); 788 cipher.doFinal(ctBuffer, decrypted); 789 assertEquals(test.ptHex, TestUtil.byteBufferToHex(decrypted)); 790 } 791 } 792 793 /** 794 * If a ByteBuffer is backed by an array and not readonly, then it is possible to access the data 795 * through the .array() method. An implementation using this possibility must ensure that it 796 * considers the offset. 797 */ 798 @Test testByteBufferWithOffset()799 public void testByteBufferWithOffset() throws Exception { 800 testByteBufferWithOffset(false); 801 } 802 @Test testByteBufferWithOffset_StrongBox()803 public void testByteBufferWithOffset_StrongBox() throws Exception { 804 KeyStoreUtil.assumeStrongBox(); 805 testByteBufferWithOffset(true); 806 } testByteBufferWithOffset(boolean isStrongBox)807 private void testByteBufferWithOffset(boolean isStrongBox) throws Exception { 808 for (GcmTestVector test : getTestVectors(isStrongBox)) { 809 // Encryption 810 Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE, 811 test, isStrongBox); 812 ByteBuffer ptBuffer = ByteBuffer.wrap(new byte[test.pt.length + 50]); 813 ptBuffer.position(5); 814 ptBuffer = ptBuffer.slice(); 815 ptBuffer.put(test.pt); 816 ptBuffer.flip(); 817 818 ByteBuffer ctBuffer = ByteBuffer.wrap(new byte[test.ct.length + 50]); 819 ctBuffer.position(8); 820 ctBuffer = ctBuffer.slice(); 821 cipher.updateAAD(test.aad); 822 cipher.doFinal(ptBuffer, ctBuffer); 823 assertEquals(test.ctHex, TestUtil.byteBufferToHex(ctBuffer)); 824 ctBuffer.flip(); 825 826 // Decryption 827 ByteBuffer decBuffer = ByteBuffer.wrap(new byte[test.pt.length + 50]); 828 decBuffer.position(6); 829 decBuffer = decBuffer.slice(); 830 cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE, 831 test, isStrongBox); 832 cipher.updateAAD(test.aad); 833 cipher.doFinal(ctBuffer, decBuffer); 834 assertEquals(test.ptHex, TestUtil.byteBufferToHex(decBuffer)); 835 } 836 } 837 838 @Test testByteBufferTooShort()839 public void testByteBufferTooShort() throws Exception { 840 testByteBufferTooShort(false); 841 } 842 @Test testByteBufferTooShort_StrongBox()843 public void testByteBufferTooShort_StrongBox() throws Exception { 844 KeyStoreUtil.assumeStrongBox(); 845 testByteBufferTooShort(true); 846 } testByteBufferTooShort(boolean isStrongBox)847 private void testByteBufferTooShort(boolean isStrongBox) throws Exception { 848 for (GcmTestVector test : getTestVectors(isStrongBox)) { 849 // Encryption 850 Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE, 851 test, isStrongBox); 852 ByteBuffer ptBuffer = ByteBuffer.wrap(test.pt); 853 ByteBuffer ctBuffer = ByteBuffer.allocate(test.ct.length - 1); 854 cipher.updateAAD(test.aad); 855 try { 856 cipher.doFinal(ptBuffer, ctBuffer); 857 fail("This should not work"); 858 } catch (ShortBufferException ex) { 859 // expected 860 } 861 862 // Decryption 863 ctBuffer = ByteBuffer.wrap(test.ct); 864 ByteBuffer decrypted = ByteBuffer.allocate(test.pt.length - 1); 865 cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE, 866 test, isStrongBox); 867 cipher.updateAAD(test.aad); 868 try { 869 cipher.doFinal(ctBuffer, decrypted); 870 fail("This should not work"); 871 } catch (ShortBufferException ex) { 872 // expected 873 } 874 } 875 } 876 877 /** 878 * Test encryption when update and doFinal are done with empty ByteBuffers. Conscrypt ignored 879 * calls to doFinal() when the ByteBuffer was empty. 880 */ 881 @Test testEncryptWithEmptyByteBuffer()882 public void testEncryptWithEmptyByteBuffer() throws Exception { 883 testEncryptWithEmptyByteBuffer(false); 884 } 885 @Test testEncryptWithEmptyByteBuffer_StrongBox()886 public void testEncryptWithEmptyByteBuffer_StrongBox() throws Exception { 887 KeyStoreUtil.assumeStrongBox(); 888 testEncryptWithEmptyByteBuffer(true); 889 } testEncryptWithEmptyByteBuffer(boolean isStrongBox)890 private void testEncryptWithEmptyByteBuffer(boolean isStrongBox) throws Exception { 891 for (GcmTestVector test : getTestVectors(isStrongBox)) { 892 // Encryption 893 Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE, 894 test, isStrongBox); 895 ByteBuffer empty = ByteBuffer.allocate(0); 896 ByteBuffer ptBuffer = ByteBuffer.wrap(test.pt); 897 int outputSize = cipher.getOutputSize(test.pt.length); 898 ByteBuffer ctBuffer = ByteBuffer.allocate(outputSize); 899 cipher.updateAAD(empty); 900 cipher.updateAAD(test.aad); 901 cipher.update(empty, ctBuffer); 902 cipher.update(ptBuffer, ctBuffer); 903 cipher.doFinal(empty, ctBuffer); 904 assertEquals(test.ctHex, TestUtil.byteBufferToHex(ctBuffer)); 905 } 906 } 907 908 @Test testDecryptWithEmptyBuffer()909 public void testDecryptWithEmptyBuffer() throws Exception { 910 testDecryptWithEmptyBuffer(false); 911 } 912 @Test testDecryptWithEmptyBuffer_StrongBox()913 public void testDecryptWithEmptyBuffer_StrongBox() throws Exception { 914 KeyStoreUtil.assumeStrongBox(); 915 testDecryptWithEmptyBuffer(true); 916 } testDecryptWithEmptyBuffer(boolean isStrongBox)917 private void testDecryptWithEmptyBuffer(boolean isStrongBox) throws Exception { 918 for (GcmTestVector test : getTestVectors(isStrongBox)) { 919 Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE, 920 test, isStrongBox); 921 ByteBuffer empty = ByteBuffer.allocate(0); 922 ByteBuffer ctBuffer = ByteBuffer.wrap(test.ct); 923 int outputSize = cipher.getOutputSize(test.ct.length); 924 ByteBuffer ptBuffer = ByteBuffer.allocate(outputSize); 925 cipher.updateAAD(empty); 926 cipher.updateAAD(test.aad); 927 cipher.update(empty, ptBuffer); 928 cipher.update(ctBuffer, ptBuffer); 929 cipher.doFinal(empty, ptBuffer); 930 assertEquals(test.ptHex, TestUtil.byteBufferToHex(ptBuffer)); 931 932 // Simple test that a modified ciphertext fails. 933 ctBuffer.flip(); 934 ptBuffer.clear(); 935 cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE, 936 test, isStrongBox); 937 cipher.updateAAD(empty); 938 cipher.updateAAD(test.aad); 939 cipher.updateAAD(new byte[1]); 940 cipher.update(empty, ptBuffer); 941 cipher.update(ctBuffer, ptBuffer); 942 try { 943 cipher.doFinal(empty, ptBuffer); 944 fail("Accepted modified ciphertext."); 945 } catch (GeneralSecurityException ex) { 946 // Expected 947 } 948 } 949 } 950 951 /** 952 * The default authentication tag size should be 128-bit by default for the following reasons: 953 * <br> 954 * (1) Security: Ferguson, N., Authentication Weaknesses in GCM, Natl. Inst. Stand. Technol. [Web 955 * page], http://www.csrc.nist.gov/groups/ST/toolkit/BCM/documents/comments/ 956 * CWC-GCM/Ferguson2.pdf, May 20, 2005. This paper points out that a n-bit tag has lower strength 957 * than expected. <br> 958 * (2) Compatibility: Assume an implementer tests some code using one provider than switches to 959 * another provider. Such a switch should ideally not lower the security. <br> 960 * Conscrypt used to have only 12-byte authentication tag (b/26186727). 961 */ 962 @Test 963 @Ignore // IvParameterSpec is not supported in AndroidKeyStore AES/GCM cipher testDefaultTagSizeIvParameterSpec()964 public void testDefaultTagSizeIvParameterSpec() throws Exception { 965 byte[] counter = new byte[12]; 966 byte[] input = new byte[16]; 967 SecretKey secretKey = null; 968 try { 969 String alias = "TestKey" + 1; 970 SecretKeySpec keySpec = new SecretKeySpec(new byte[16], "AES"); 971 secretKey = setKeystoreEntry(alias, keySpec, /*isStrongBox*/ false); 972 } catch (Exception e) { 973 fail("Failed to set secret key entry in KeyStore."); 974 } 975 Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); 976 cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(counter)); 977 byte[] output = cipher.doFinal(input); 978 assertEquals(input.length + 16, output.length); 979 } 980 981 /** 982 * The default authentication tag size should be 128-bit by default for the following reasons: 983 * <br> 984 * (1) Security: Ferguson, N., Authentication Weaknesses in GCM, Natl. Inst. Stand. Technol. [Web 985 * page], http://www.csrc.nist.gov/groups/ST/toolkit/BCM/documents/comments/ 986 * CWC-GCM/Ferguson2.pdf, May 20, 2005. This paper points out that a n-bit tag has lower strength 987 * than expected. <br> 988 * (2) Compatibility: Assume an implementer tests some code using one provider than switches to 989 * another provider. Such a switch should ideally not lower the security. <br> 990 * BouncyCastle used to have only 12-byte authentication tag (b/26186727). 991 */ 992 @Test 993 @Ignore // GCM AlgorithmParameterGenerator is not available. testDefaultTagSizeAlgorithmParameterGenerator()994 public void testDefaultTagSizeAlgorithmParameterGenerator() throws Exception { 995 byte[] input = new byte[10]; 996 SecretKey secretKey = null; 997 try { 998 String alias = "TestKey" + 1; 999 SecretKeySpec keySpec = new SecretKeySpec(new byte[16], "AES"); 1000 secretKey = setKeystoreEntry(alias, keySpec, /*isStrongBox*/ false); 1001 } catch (Exception e) { 1002 fail("Failed to set secret key entry in KeyStore."); 1003 } 1004 Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); 1005 AlgorithmParameterGenerator.getInstance("GCM"); 1006 AlgorithmParameters param = AlgorithmParameterGenerator.getInstance("GCM").generateParameters(); 1007 cipher.init(Cipher.ENCRYPT_MODE, secretKey, param); 1008 byte[] output = cipher.doFinal(input); 1009 assertEquals(input.length + 16, output.length); 1010 } 1011 1012 /** 1013 * Test AES-GCM wrapped around counter bug which leaks plaintext and authentication key. Let's 1014 * consider 12-byte IV, counter = IV || 0^31 || 1. For each encryption block, the last 4 bytes of 1015 * the counter is increased by 1. After 2^32 blocks, the counter will be wrapped around causing 1016 * counter collision and hence, leaking plaintext and authentication key as explained below. The 1017 * library must make a check to make sure that the plaintext's length never exceeds 2^32 - 2 1018 * blocks. Note that this is different from usual IV collisions because it happens even if users 1019 * use different IVs. <br> 1020 * We have: <br> 1021 * J0 = IV || 0^31 || 1 <br> 1022 * Plaintext: P[0], P[1], P[2], .... <br> 1023 * Ciphertext: <br> 1024 * C[0] = Enc(K, (J0 + 1) % 2^32) XOR P[0] <br> 1025 * C[1] = Enc(K, (J0 + 2) % 2^32) XOR P[1] <br> 1026 * C[2] = Enc(K, (J0 + 3) % 2^32) XOR P[2] <br> 1027 * ... <br> 1028 * C[2^32 - 1] = Enc(K, J0) XOR P[2^32 - 1] <br> 1029 * C[2^32] = Enc(K, (J0 + 1)% 2^32) XOR P[2^32] <br> 1030 * It means that after 2^32 blocks, the counter is wrapped around causing counter collisions. In 1031 * counter mode, once the counter is collided then it's reasonable to assume that the plaintext is 1032 * leaked. As the ciphertext is already known to attacker, Enc(K, J0) is leaked. <br> 1033 * Now, as the authentication tag T is computed as GHASH(H, {}, C) XOR E(K, J0), the attacker can 1034 * learn GHASH(H, {}, C}. It essentially means that the attacker finds a polynomial where H is the 1035 * root (see Joux attack http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/Joux_comments.pdf). 1036 * Solving polynomial equation in GF(2^128) is enough to extract the authentication key. 1037 * 1038 * <p>BouncyCastle used to have this bug (CVE-2015-6644). 1039 * 1040 * <p>OpenJDK8 used to have this bug (http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/0c3ed12cdaf5) 1041 * 1042 * <p>The test is slow as we have to encrypt 2^32 blocks. 1043 */ 1044 @Test 1045 @Ignore // This test takes very long time and CTS is timed out hence ignored for now. testWrappedAroundCounter()1046 public void testWrappedAroundCounter() throws Exception { 1047 try { 1048 byte[] iv = new byte[12]; 1049 byte[] input = new byte[16]; 1050 byte[] key = new byte[16]; 1051 (new SecureRandom()).nextBytes(key); 1052 SecretKey secretKey = null; 1053 try { 1054 String alias = "TestKey" + 1; 1055 SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); 1056 secretKey = setKeystoreEntry(alias, keySpec, /*isStrongBox*/ false); 1057 } catch (Exception e) { 1058 fail("Failed to set secret key entry in KeyStore."); 1059 } 1060 Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); 1061 cipher.init( 1062 Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(16 * 8, iv)); 1063 byte[] output = cipher.update(input); 1064 for (long i = 0; i < 4294967296L + 2; i++) { 1065 byte[] output1 = cipher.update(input); 1066 assertFalse("GCM Wrapped Around Counter" + i, Arrays.equals(output, output1)); 1067 } 1068 fail("Expected Exception"); 1069 } catch (Exception expected) { 1070 } 1071 } 1072 1073 /** 1074 * AES-GCM allows IVs of bit length 1 .. 2^64-1. See NIST SP 800 38d, Section 5.2.1.1 1075 * http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf 1076 * 1077 * <p>Disallowing IVs of length 0 is necessary for the following reason: if an empty IV is used 1078 * then the tag is an evaluation of a polynomial with the hash subkey as the value. Since the 1079 * polynomial can be derived from the ciphertext it is known to an attacker. Therefore, any 1080 * message encrypted with an empty IV leaks the hash subkey. In particular, encrypting an empty 1081 * plaintext with an empty IV results in a ciphertext having a tag that is equal to the hash 1082 * subkey used in AES-GCM. I.e. both are the same as encrypting an all zero block. 1083 * 1084 * <p>OpenJDK fails this test. 1085 */ 1086 @Test testEncryptEmptyPlaintextWithEmptyIv()1087 public void testEncryptEmptyPlaintextWithEmptyIv() throws Exception { 1088 testEncryptEmptyPlaintextWithEmptyIv(false); 1089 } 1090 @Test testEncryptEmptyPlaintextWithEmptyIv_StrongBox()1091 public void testEncryptEmptyPlaintextWithEmptyIv_StrongBox() throws Exception { 1092 KeyStoreUtil.assumeStrongBox(); 1093 testEncryptEmptyPlaintextWithEmptyIv(true); 1094 } 1095 testEncryptEmptyPlaintextWithEmptyIv(boolean isStrongBox)1096 private void testEncryptEmptyPlaintextWithEmptyIv(boolean isStrongBox) throws Exception { 1097 byte[] emptyIv = new byte[0]; 1098 byte[] input = new byte[0]; 1099 byte[] key = TestUtil.hexToBytes("56aae7bd5cbefc71d31c4338e6ddd6c5"); 1100 SecretKey secretKey = null; 1101 try { 1102 String alias = "TestKey" + 1; 1103 SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); 1104 secretKey = setKeystoreEntry(alias, keySpec, isStrongBox); 1105 } catch (Exception e) { 1106 fail("Failed to set secret key entry in KeyStore."); 1107 } 1108 Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); 1109 Cipher block = Cipher.getInstance("AES/ECB/NoPadding", EXPECTED_PROVIDER_NAME); 1110 block.init(Cipher.ENCRYPT_MODE, secretKey); 1111 byte[] hashkey = block.doFinal(new byte[16]); 1112 try { 1113 cipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(16 * 8, emptyIv)); 1114 byte[] ct = cipher.doFinal(input); 1115 // If the encryption above is not rejected then the hash key and the ciphertext are the same. 1116 // Both are d1bdd948ddc5a7f7a9250cf78229b84d. 1117 fail("Encrypting with an empty IV leaks the hash subkey."); 1118 } catch (GeneralSecurityException expected) { 1119 // expected behavior 1120 } 1121 } 1122 1123 @Test testDecryptWithEmptyIv()1124 public void testDecryptWithEmptyIv() throws Exception { 1125 testDecryptWithEmptyIv(false); 1126 } 1127 @Test testDecryptWithEmptyIv_StrongBox()1128 public void testDecryptWithEmptyIv_StrongBox() throws Exception { 1129 KeyStoreUtil.assumeStrongBox(); 1130 testDecryptWithEmptyIv(true); 1131 } 1132 testDecryptWithEmptyIv(boolean isStrongBox)1133 private void testDecryptWithEmptyIv(boolean isStrongBox) throws Exception { 1134 byte[] emptyIv = new byte[0]; 1135 byte[] key = TestUtil.hexToBytes("56aae7bd5cbefc71d31c4338e6ddd6c5"); 1136 SecretKey secretKey = null; 1137 try { 1138 String alias = "TestKey" + 1; 1139 SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); 1140 secretKey = setKeystoreEntry(alias, keySpec, isStrongBox); 1141 } catch (Exception e) { 1142 fail("Failed to set secret key entry in KeyStore."); 1143 } 1144 Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); 1145 try { 1146 cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(16 * 8, emptyIv)); 1147 String ciphertext = "2b65876c00d77facf8f3d0e5be792b129bab10b25bcb739b92d6e2eab241245ff449"; 1148 String tag = "c2b2d7086e7fa84ca795a881b540"; 1149 byte[] pt1 = cipher.update(TestUtil.hexToBytes(ciphertext)); 1150 byte[] pt2 = cipher.doFinal(TestUtil.hexToBytes(tag)); 1151 // We shouldn't get here. If a provider releases unverified plaintext additionally to 1152 // accepting empty IVs then chosen ciphertext attacks might be possible. 1153 fail("AES-GCM must not accept an IV of size 0."); 1154 } catch (GeneralSecurityException expected) { 1155 //Expected 1156 } 1157 } 1158 } 1159