View Javadoc
1    /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.commons.crypto.cipher;
19  
20  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  
24  import java.nio.ByteBuffer;
25  import java.security.Key;
26  import java.util.Arrays;
27  import java.util.Properties;
28  import java.util.Random;
29  
30  import javax.crypto.AEADBadTagException;
31  import javax.crypto.Cipher;
32  import javax.crypto.spec.GCMParameterSpec;
33  import javax.xml.bind.DatatypeConverter;
34  
35  import org.apache.commons.crypto.utils.AES;
36  import org.apache.commons.crypto.utils.Utils;
37  import org.junit.jupiter.api.BeforeEach;
38  import org.junit.jupiter.api.Test;
39  
40  
41  public class GcmCipherTest {
42  
43      private static final String GCM_NO_PADDING = "AES/GCM/NoPadding";
44      Properties props;
45      String cipherClass;
46      String transformation = GCM_NO_PADDING;
47  
48      private String[] kHex;
49      private String[] pHex;
50      private String[] ivHex;
51      private String[] aadHex;
52      private String[] cHex;
53      private String[] tHex;
54  
55      private void initTestData() {
56  
57          final int casesNumber = 4;
58  
59          kHex = new String[casesNumber];
60          pHex = new String[casesNumber];
61          ivHex = new String[casesNumber];
62          aadHex = new String[casesNumber];
63          cHex = new String[casesNumber];
64          tHex = new String[casesNumber];
65  
66          // http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
67          // http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
68          // NIST Case2  -----------------------------
69          // key length:          16 bytes
70          // plain text length:   16 bytes
71          // iv length:           12 bytes
72          // aad length:          0 bytes
73          kHex[0] = "00000000000000000000000000000000";
74          pHex[0] = "00000000000000000000000000000000";
75          ivHex[0]  = "000000000000000000000000";
76          aadHex[0]  = "";
77          cHex[0]  = "0388dace60b6a392f328c2b971b2fe78";
78          tHex[0]  = "ab6e47d42cec13bdf53a67b21257bddf";
79  
80          // NIST Case4 ---------------------------------
81          // key length:          16 bytes
82          // plain text length:   60 bytes
83          // iv length:           12 bytes
84          // aad length:          20 bytes
85          kHex[1] = "feffe9928665731c6d6a8f9467308308";
86          pHex[1] = "d9313225f88406e5a55909c5aff5269a"
87                  + "86a7a9531534f7da2e4c303d8a318a72"
88                  + "1c3c0c95956809532fcf0e2449a6b525"
89                  + "b16aedf5aa0de657ba637b39";
90          ivHex[1] = "cafebabefacedbaddecaf888";
91          aadHex[1] = "feedfacedeadbeeffeedfacedeadbeef"
92                  + "abaddad2";
93          cHex[1] = "42831ec2217774244b7221b784d0d49c"
94                  + "e3aa212f2c02a4e035c17e2329aca12e"
95                  + "21d514b25466931c7d8f6a5aac84aa05"
96                  + "1ba30b396a0aac973d58e091";
97          tHex[1] = "5bc94fbc3221a5db94fae95ae7121a47";
98  
99          // NIST Case5 ---------------------------------
100         // key length:          16 bytes
101         // plain text length:   60 bytes
102         // iv length:           8 bytes
103         // aad length:          20 bytes
104         kHex[2] = "feffe9928665731c6d6a8f9467308308";
105         pHex[2] = "d9313225f88406e5a55909c5aff5269a"
106                 + "86a7a9531534f7da2e4c303d8a318a72"
107                 + "1c3c0c95956809532fcf0e2449a6b525"
108                 + "b16aedf5aa0de657ba637b39";
109         ivHex[2] ="cafebabefacedbad"; // 64bits < 96bits
110         aadHex[2]="feedfacedeadbeeffeedfacedeadbeef"
111                 + "abaddad2";
112         cHex[2] = "61353b4c2806934a777ff51fa22a4755"
113                 + "699b2a714fcdc6f83766e5f97b6c7423"
114                 + "73806900e49f24b22b097544d4896b42"
115                 + "4989b5e1ebac0f07c23f4598";
116         tHex[2] = "3612d2e79e3b0785561be14aaca2fccb";
117 
118         // NIST Case6 ---------------------------------
119         // key length:          16 bytes
120         // plain text length:   60 bytes
121         // iv length:           60 bytes
122         // aad length:          20 bytes
123         kHex[3] = "feffe9928665731c6d6a8f9467308308";
124         pHex[3] = "d9313225f88406e5a55909c5aff5269a"
125                 + "86a7a9531534f7da2e4c303d8a318a72"
126                 + "1c3c0c95956809532fcf0e2449a6b525"
127                 + "b16aedf5aa0de657ba637b39";
128         ivHex[3] = "9313225df88406e555909c5aff5269aa"
129                 + "6a7a9538534f7da1e4c303d2a318a728"
130                 + "c3c0c95156809539fcf0e2429a6b5254"
131                 + "16aedbf5a0de6a57a637b39b"; // > 96bits
132         aadHex[3] = "feedfacedeadbeeffeedfacedeadbeef"
133                 + "abaddad2";
134         cHex[3] = "8ce24998625615b603a033aca13fb894"
135                 + "be9112a5c3a211a8ba262a3cca7e2ca7"
136                 + "01e4a9a4fba43c90ccdcb281d48c7c6f"
137                 + "d62875d2aca417034c34aee5";
138         tHex[3] = "619cc5aefffe0bfa462af43c1699d050";
139     }
140 
141     @BeforeEach
142     public void setup() {
143         //init
144         cipherClass = OpenSslCipher.class.getName();
145 
146         props = new Properties();
147         props.setProperty(CryptoCipherFactory.CLASSES_KEY,
148                 cipherClass);
149         initTestData();
150     }
151 
152     private void testGcmArbitraryLengthUpdate(final String kHex, final String pHex, final String ivHex, final String aadHex,
153                                               final String cHex, final String tHex) throws Exception {
154 
155         final byte[] keyBytes = DatatypeConverter.parseHexBinary(kHex);
156         final byte[] input = DatatypeConverter.parseHexBinary(pHex);
157         final byte[] ivBytes = DatatypeConverter.parseHexBinary(ivHex);
158         final byte[] aad = DatatypeConverter.parseHexBinary(aadHex);
159         final byte[] expectedOutput = DatatypeConverter.parseHexBinary(cHex+tHex);
160 
161         final byte[] encOutput = new byte[expectedOutput.length];
162         final byte[] decOutput = new byte[input.length];
163 
164         final Random r = new Random();
165 
166         int partLen;
167         int len;
168         try (final CryptoCipher enc = Utils.getCipherInstance(transformation, props)) {
169             final Key key = AES.newSecretKeySpec(keyBytes);
170             final GCMParameterSpec iv = new GCMParameterSpec(128, ivBytes);
171             enc.init(Cipher.ENCRYPT_MODE, key, iv);
172             if (aad.length > 0) {
173                 final int len1 = r.nextInt(aad.length);
174                 final byte[] aad1 = Arrays.copyOfRange(aad, 0, len1);
175                 final byte[] aad2 = Arrays.copyOfRange(aad, len1, aad.length);
176                 enc.updateAAD(aad1);
177                 enc.updateAAD(aad2);
178             }
179 
180             partLen = r.nextInt(input.length);
181             len = enc.update(input, 0, partLen, encOutput, 0);
182             assertEquals(partLen, len);
183             len = enc.doFinal(input, partLen, input.length - partLen, encOutput, partLen);
184             assertEquals((input.length + (iv.getTLen() >> 3) - partLen), len);
185 
186             assertArrayEquals(expectedOutput, encOutput);
187         }
188 
189         // Decryption
190         try (final CryptoCipher dec = Utils.getCipherInstance(transformation, props)) {
191             dec.init(Cipher.DECRYPT_MODE, AES.newSecretKeySpec(keyBytes), new GCMParameterSpec(128, ivBytes));
192             if (aad.length > 0) {
193                 final int len1 = r.nextInt(aad.length);
194                 final byte[] aad1 = Arrays.copyOfRange(aad, 0, len1);
195                 final byte[] aad2 = Arrays.copyOfRange(aad, len1, aad.length);
196                 dec.updateAAD(aad1);
197                 dec.updateAAD(aad2);
198             }
199             final byte[] decInput = encOutput;
200             partLen = r.nextInt(input.length);
201             len = dec.update(decInput, 0, partLen, decOutput, 0);
202             assertEquals(len, 0);
203             len = dec.doFinal(decInput, partLen, decInput.length - partLen, decOutput, 0);
204             assertEquals(input.length, len);
205 
206             assertArrayEquals(input, decOutput);
207         }
208     }
209 
210     private void testGcmByteBuffer(final String kHex, final String pHex, final String ivHex, final String aadHex,
211                                    final String cHex, final String tHex) throws Exception {
212 
213         final byte[] keyBytes = DatatypeConverter.parseHexBinary(kHex);
214         final byte[] plainText = DatatypeConverter.parseHexBinary(pHex);
215         final byte[] ivBytes = DatatypeConverter.parseHexBinary(ivHex);
216         final byte[] aad = DatatypeConverter.parseHexBinary(aadHex);
217         final byte[] cipherText = DatatypeConverter.parseHexBinary(cHex+tHex);
218 
219         final byte[] encOutput = new byte[cipherText.length];
220         final byte[] decOutput = new byte[plainText.length];
221 
222         final ByteBuffer bfAAD = ByteBuffer.allocateDirect(aad.length);
223         bfAAD.put(aad);
224 
225         final ByteBuffer bfPlainText;
226         final ByteBuffer bfCipherText;
227         bfPlainText = ByteBuffer.allocateDirect(plainText.length);
228         bfCipherText = ByteBuffer.allocateDirect(encOutput.length);
229 
230         // Encryption -------------------
231         try (final CryptoCipher c = Utils.getCipherInstance(transformation, props)) {
232             final Key key = AES.newSecretKeySpec(keyBytes);
233             final GCMParameterSpec iv = new GCMParameterSpec(128, ivBytes);
234             c.init(Cipher.ENCRYPT_MODE, key, iv);
235 
236             bfAAD.flip();
237             c.updateAAD(bfAAD);
238 
239             bfPlainText.put(plainText);
240             bfPlainText.flip();
241             bfCipherText.position(0);
242 
243             c.doFinal(bfPlainText, bfCipherText);
244 
245             bfCipherText.flip();
246             bfCipherText.get(encOutput);
247             assertArrayEquals(cipherText, encOutput);
248         }
249 
250         // Decryption -------------------
251         try (final CryptoCipher dec = Utils.getCipherInstance(transformation, props)) {
252             dec.init(Cipher.DECRYPT_MODE, AES.newSecretKeySpec(keyBytes), new GCMParameterSpec(128, ivBytes));
253             bfAAD.flip();
254             dec.updateAAD(bfAAD);
255             bfCipherText.clear();
256             bfPlainText.clear();
257             bfCipherText.put(cipherText);
258             bfCipherText.flip();
259             dec.doFinal(bfCipherText, bfPlainText);
260             bfPlainText.flip();
261             bfPlainText.get(decOutput);
262             assertArrayEquals(plainText, decOutput);
263         }
264     }
265 
266     private void testGcmDecryption(final String kHex, final String pHex, final String ivHex, final String aadHex,
267                                    final String cHex, final String tHex) throws Exception {
268 
269         final byte[] keyBytes = DatatypeConverter.parseHexBinary(kHex);
270         final byte[] plainBytes = DatatypeConverter.parseHexBinary(pHex);
271         final byte[] ivBytes = DatatypeConverter.parseHexBinary(ivHex);
272 
273         final byte[] aad = DatatypeConverter.parseHexBinary(aadHex);
274         final byte[] cipherBytes = DatatypeConverter.parseHexBinary(cHex+tHex);
275 
276         final byte[] input = cipherBytes;
277         final byte[] output = new byte[plainBytes.length];
278 
279         try (final CryptoCipher c = Utils.getCipherInstance(transformation, props)) {
280 
281             final Key key = AES.newSecretKeySpec(keyBytes);
282 
283             final GCMParameterSpec iv = new GCMParameterSpec(128, ivBytes);
284             c.init(Cipher.DECRYPT_MODE, key, iv);
285             c.updateAAD(aad);
286             c.doFinal(input, 0, input.length, output, 0);
287 
288             assertArrayEquals(plainBytes, output);
289         }
290     }
291 
292     private void testGcmEncryption(final String kHex, final String pHex, final String ivHex, final String aadHex,
293                                    final String cHex, final String tHex) throws Exception {
294 
295         final byte[] keyBytes = DatatypeConverter.parseHexBinary(kHex);
296         final byte[] input = DatatypeConverter.parseHexBinary(pHex);
297         final byte[] ivBytes = DatatypeConverter.parseHexBinary(ivHex);
298         final byte[] aad = DatatypeConverter.parseHexBinary(aadHex);
299         final byte[] expectedOutput = DatatypeConverter.parseHexBinary(cHex+tHex);
300 
301         final byte[] output = new byte[expectedOutput.length];
302 
303         try (final CryptoCipher c = Utils.getCipherInstance(transformation, props)) {
304 
305             final Key key = AES.newSecretKeySpec(keyBytes);
306 
307             final GCMParameterSpec iv = new GCMParameterSpec(128, ivBytes);
308             c.init(Cipher.ENCRYPT_MODE, key, iv);
309             c.updateAAD(aad);
310 
311             c.doFinal(input, 0, input.length, output, 0);
312 
313             assertArrayEquals(expectedOutput, output);
314         }
315     }
316 
317     /**
318      * NIST AES Test Vectors
319      * http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
320      * @throws Exception Test failure
321      */
322     @Test
323     public void testGcmNistCase2() throws Exception {
324         // key length:          16 bytes
325         // plain text length:   16 bytes
326         // iv length:           12 bytes
327         // aad length:          0 bytes
328 
329         final String kHex = "00000000000000000000000000000000";
330         final String pHex = "00000000000000000000000000000000";
331         final String ivHex = "000000000000000000000000";
332         final String aadHex = "";
333 
334         final String cHex = "0388dace60b6a392f328c2b971b2fe78";
335         final String tHex = "ab6e47d42cec13bdf53a67b21257bddf";
336 
337         testGcmEncryption(kHex, pHex, ivHex, aadHex, cHex, tHex);
338         testGcmDecryption(kHex, pHex, ivHex, aadHex, cHex, tHex);
339         testGcmByteBuffer(kHex, pHex, ivHex, aadHex, cHex, tHex);
340         testGcmReturnDataAfterTagVerified(kHex, pHex, ivHex, aadHex, cHex, tHex);
341         testGcmArbitraryLengthUpdate(kHex, pHex, ivHex, aadHex, cHex, tHex);
342     }
343 
344     @Test
345     public void testGcmNistCase4() throws Exception {
346         // key length:          16 bytes
347         // plain text length:   60 bytes
348         // iv length:           12 bytes
349         // aad length:          20 bytes
350 
351         final String kHex = "feffe9928665731c6d6a8f9467308308";
352         final String pHex = "d9313225f88406e5a55909c5aff5269a"
353                 + "86a7a9531534f7da2e4c303d8a318a72"
354                 + "1c3c0c95956809532fcf0e2449a6b525"
355                 + "b16aedf5aa0de657ba637b39";
356         final String ivHex = "cafebabefacedbaddecaf888";
357         final String aadHex = "feedfacedeadbeeffeedfacedeadbeef"
358                 + "abaddad2";
359 
360         final String cHex = "42831ec2217774244b7221b784d0d49c"
361                 + "e3aa212f2c02a4e035c17e2329aca12e"
362                 + "21d514b25466931c7d8f6a5aac84aa05"
363                 + "1ba30b396a0aac973d58e091";
364         final String tHex = "5bc94fbc3221a5db94fae95ae7121a47";
365 
366         testGcmEncryption(kHex, pHex, ivHex, aadHex, cHex, tHex);
367         testGcmDecryption(kHex, pHex, ivHex, aadHex, cHex, tHex);
368         testGcmByteBuffer(kHex, pHex, ivHex, aadHex, cHex, tHex);
369         testGcmReturnDataAfterTagVerified(kHex, pHex, ivHex, aadHex, cHex, tHex);
370         testGcmArbitraryLengthUpdate(kHex, pHex, ivHex, aadHex, cHex, tHex);
371     }
372 
373     @Test
374     public void testGcmNistCase5() throws Exception {
375         // key length:          16 bytes
376         // plain text length:   60 bytes
377         // iv length:           8 bytes
378         // aad length:          20 bytes
379 
380         final String kHex = "feffe9928665731c6d6a8f9467308308";
381 
382         final String pHex = "d9313225f88406e5a55909c5aff5269a"
383                 + "86a7a9531534f7da2e4c303d8a318a72"
384                 + "1c3c0c95956809532fcf0e2449a6b525"
385                 + "b16aedf5aa0de657ba637b39";
386 
387         final String ivHex ="cafebabefacedbad"; // 64bits < 96bits
388 
389         final String aadHex="feedfacedeadbeeffeedfacedeadbeef"
390                 + "abaddad2";
391 
392         final String cHex = "61353b4c2806934a777ff51fa22a4755"
393                 + "699b2a714fcdc6f83766e5f97b6c7423"
394                 + "73806900e49f24b22b097544d4896b42"
395                 + "4989b5e1ebac0f07c23f4598";
396 
397         final String tHex = "3612d2e79e3b0785561be14aaca2fccb";
398 
399         testGcmEncryption(kHex, pHex, ivHex, aadHex, cHex, tHex);
400         testGcmDecryption(kHex, pHex, ivHex, aadHex, cHex, tHex);
401         testGcmByteBuffer(kHex, pHex, ivHex, aadHex, cHex, tHex);
402         testGcmReturnDataAfterTagVerified(kHex, pHex, ivHex, aadHex, cHex, tHex);
403         testGcmArbitraryLengthUpdate(kHex, pHex, ivHex, aadHex, cHex, tHex);
404     }
405 
406     @Test
407     public void testGcmNistCase6() throws Exception {
408         // key length:          16 bytes
409         // plain text length:   60 bytes
410         // iv length:           60 bytes
411         // aad length:          20 bytes
412 
413         final String kHex = "feffe9928665731c6d6a8f9467308308";
414 
415         final String pHex = "d9313225f88406e5a55909c5aff5269a"
416                 + "86a7a9531534f7da2e4c303d8a318a72"
417                 + "1c3c0c95956809532fcf0e2449a6b525"
418                 + "b16aedf5aa0de657ba637b39";
419 
420         final String ivHex ="9313225df88406e555909c5aff5269aa"
421                 + "6a7a9538534f7da1e4c303d2a318a728"
422                 + "c3c0c95156809539fcf0e2429a6b5254"
423                 + "16aedbf5a0de6a57a637b39b"; // > 96bits
424 
425         final String aadHex="feedfacedeadbeeffeedfacedeadbeef"
426                 + "abaddad2";
427 
428         final String cHex = "8ce24998625615b603a033aca13fb894"
429                 + "be9112a5c3a211a8ba262a3cca7e2ca7"
430                 + "01e4a9a4fba43c90ccdcb281d48c7c6f"
431                 + "d62875d2aca417034c34aee5";
432 
433         final String tHex = "619cc5aefffe0bfa462af43c1699d050";
434 
435         testGcmEncryption(kHex, pHex, ivHex, aadHex, cHex, tHex);
436         testGcmDecryption(kHex, pHex, ivHex, aadHex, cHex, tHex);
437         testGcmByteBuffer(kHex, pHex, ivHex, aadHex, cHex, tHex);
438         testGcmReturnDataAfterTagVerified(kHex, pHex, ivHex, aadHex, cHex, tHex);
439         testGcmArbitraryLengthUpdate(kHex, pHex, ivHex, aadHex, cHex, tHex);
440     }
441 
442     @Test
443     public void testGcmNistCases() throws Exception {
444         for(int i = 0; i < kHex.length; i++) {
445             testGcmEncryption(kHex[i], pHex[i], ivHex[i], aadHex[i], cHex[i], tHex[i]);
446             testGcmDecryption(kHex[i], pHex[i], ivHex[i], aadHex[i], cHex[i], tHex[i]);
447             testGcmByteBuffer(kHex[i], pHex[i], ivHex[i], aadHex[i], cHex[i], tHex[i]);
448             testGcmReturnDataAfterTagVerified(kHex[i], pHex[i], ivHex[i], aadHex[i], cHex[i], tHex[i]);
449             testGcmArbitraryLengthUpdate(kHex[i], pHex[i], ivHex[i], aadHex[i], cHex[i], tHex[i]);
450         }
451     }
452 
453     private void testGcmReturnDataAfterTagVerified(final String kHex, final String pHex, final String ivHex, final String aadHex,
454                                                    final String cHex, final String tHex) throws Exception {
455 
456         final byte[] keyBytes = DatatypeConverter.parseHexBinary(kHex);
457         final byte[] plainBytes = DatatypeConverter.parseHexBinary(pHex);
458         final byte[] ivBytes = DatatypeConverter.parseHexBinary(ivHex);
459 
460         final byte[] aad = DatatypeConverter.parseHexBinary(aadHex);
461         final byte[] cipherBytes = DatatypeConverter.parseHexBinary(cHex+tHex);
462 
463         final byte[] input = cipherBytes;
464         final byte[] output = new byte[plainBytes.length];
465 
466         try (final CryptoCipher c = Utils.getCipherInstance(transformation, props)) {
467 
468             final Key key = AES.newSecretKeySpec(keyBytes);
469 
470             final GCMParameterSpec iv = new GCMParameterSpec(128, ivBytes);
471             c.init(Cipher.DECRYPT_MODE, key, iv);
472             c.updateAAD(aad);
473 
474             // only return recovered data after tag is successfully verified
475             int len = c.update(input, 0, input.length, output, 0);
476             assertEquals(len, 0);
477             len += c.doFinal(input, input.length, 0, output, 0);
478             assertEquals(plainBytes.length, len);
479 
480             assertArrayEquals(plainBytes, output);
481         }
482     }
483 
484     @Test
485     public void testGcmTamperedData() throws Exception {
486 
487         final Random r = new Random();
488         final int textLength = r.nextInt(1024*1024);
489         final int ivLength = r.nextInt(59) + 1;
490         final int keyLength = 16;
491         final int tagLength = 128;  // bits
492         final int aadLength = r.nextInt(128);
493 
494         final byte[] keyBytes = new byte[keyLength];
495         final byte[] plainBytes = new byte[textLength];
496         final byte[] ivBytes = new byte[ivLength];
497         final byte[] aadBytes = new byte[aadLength];
498 
499         r.nextBytes(keyBytes);
500         r.nextBytes(plainBytes);
501         r.nextBytes(ivBytes);
502         r.nextBytes(aadBytes);
503 
504         final byte[] encOutput = new byte[plainBytes.length + (tagLength >> 3)];
505         final byte[] decOutput = new byte[plainBytes.length];
506 
507         try (final CryptoCipher c = Utils.getCipherInstance(transformation, props)) {
508             final Key key = AES.newSecretKeySpec(keyBytes);
509 
510             final GCMParameterSpec iv = new GCMParameterSpec(tagLength, ivBytes);
511             c.init(Cipher.ENCRYPT_MODE, key, iv);
512             c.updateAAD(aadBytes);
513             c.doFinal(plainBytes, 0, plainBytes.length, encOutput, 0);
514         }
515 
516         // Tamper the encrypted data.
517         encOutput[0] = (byte)(encOutput[0] + 1);
518 
519         try (final CryptoCipher c = Utils.getCipherInstance(transformation, props)) {
520             final Key key = AES.newSecretKeySpec(keyBytes);
521 
522             final GCMParameterSpec iv = new GCMParameterSpec(tagLength, ivBytes);
523             c.init(Cipher.DECRYPT_MODE, key, iv);
524             c.updateAAD(aadBytes);
525             final Exception ex = assertThrows(AEADBadTagException.class, () -> c.doFinal(encOutput, 0, encOutput.length, decOutput, 0));
526             assertEquals(ex.getMessage(), "Tag mismatch!");
527         }
528 
529     }
530 
531     @Test
532     public void testGMac() throws Exception {
533         // for GMAC,  aad is the input data,
534         // tag is the digest message
535 
536         final Random r = new Random();
537         final byte[] keyBytes = new byte[32];
538         final byte[] input = {};  // no input for GMAC
539         final byte[] ivBytes = new byte[16];
540 
541         final byte[] tag_orig = new byte[16]; // JDK's tag
542         final byte[] tag = new byte[16];
543 
544         // aad is the data to be hashed
545         final byte[] aad = new byte[r.nextInt() % 1000 + 1000 ];
546 
547         r.nextBytes(keyBytes);
548         r.nextBytes(input);
549         r.nextBytes(ivBytes);
550         r.nextBytes(aad);
551 
552         {
553             final Cipher c = Cipher.getInstance(transformation);
554             final Key key = AES.newSecretKeySpec(keyBytes);
555             final GCMParameterSpec iv = new GCMParameterSpec(128, ivBytes);
556             c.init(Cipher.ENCRYPT_MODE, key, iv);
557             c.updateAAD(aad);
558             c.doFinal(input, 0, input.length, tag_orig, 0);
559         }
560 
561         try (final CryptoCipher c = Utils.getCipherInstance(transformation, props)) {
562             final Key key = AES.newSecretKeySpec(keyBytes);
563             final GCMParameterSpec iv = new GCMParameterSpec(128, ivBytes);
564             c.init(Cipher.ENCRYPT_MODE, key, iv);
565             c.updateAAD(aad);
566             c.doFinal(input, 0, input.length, tag, 0);
567         }
568 
569         // tag should be the same as JDK's cipher
570         assertArrayEquals(tag_orig, tag);
571 
572         // like JDK's decrypt mode. The plaintext+tag is the input for decrypt mode
573         // let's verify the add & tag now
574         try (final CryptoCipher c = Utils.getCipherInstance(transformation, props)) {
575             final Key key = AES.newSecretKeySpec(keyBytes);
576             final GCMParameterSpec iv = new GCMParameterSpec(128, ivBytes);
577             c.init(Cipher.DECRYPT_MODE, key, iv);
578             c.updateAAD(aad);
579             c.doFinal(tag, 0, tag.length, input, 0);
580         }
581     }
582 
583     @Test
584             //(expected = AEADBadTagException.class)
585     public void testGMacTamperedData() throws Exception {
586         final Random r = new Random();
587         final byte[] keyBytes = new byte[32];
588         final byte[] input = {};
589         final byte[] ivBytes = new byte[16];
590 
591         final byte[] tag = new byte[16];
592 
593         final byte[] aad = new byte[r.nextInt() % 1000 + 1000 ];
594 
595         r.nextBytes(keyBytes);
596         r.nextBytes(input);
597         r.nextBytes(ivBytes);
598         r.nextBytes(aad);
599 
600         try (final CryptoCipher c = Utils.getCipherInstance(transformation, props)) {
601             final Key key = AES.newSecretKeySpec(keyBytes);
602             final GCMParameterSpec iv = new GCMParameterSpec(128, ivBytes);
603             c.init(Cipher.ENCRYPT_MODE, key, iv);
604             c.updateAAD(aad);
605             c.doFinal(input, 0, input.length, tag, 0);
606         }
607 
608         // like JDK's decrypt mode. The plaintext+tag is the input for decrypt mode
609         try (final CryptoCipher c = Utils.getCipherInstance(transformation, props)) {
610             final Key key = AES.newSecretKeySpec(keyBytes);
611             final GCMParameterSpec iv = new GCMParameterSpec(128, ivBytes);
612             c.init(Cipher.DECRYPT_MODE, key, iv);
613 
614             // if the origin data is tampered
615             aad[0] = (byte) (aad[0] + 1);
616             c.updateAAD(aad);
617 
618             final Exception ex = assertThrows(AEADBadTagException.class, () -> c.doFinal(tag, 0, tag.length, input, 0));
619             assertEquals(ex.getMessage(), "Tag mismatch!");
620         }
621 
622     }
623 }