• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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