1 /*
2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /**
25  * @test
26  * @bug 8178374
27  * @summary Test GCM decryption with various types of input/output
28  *     ByteBuffer objects
29  * @key randomness
30  */
31 
32 import java.nio.ByteBuffer;
33 import java.security.*;
34 import java.util.Random;
35 
36 import javax.crypto.Cipher;
37 import javax.crypto.SecretKey;
38 import javax.crypto.AEADBadTagException;
39 import javax.crypto.spec.*;
40 
41 public class TestGCMWithByteBuffer {
42 
43     private static Random random = new SecureRandom();
44     private static int dataSize = 4096; // see javax.crypto.CipherSpi
45     private static int multiples = 3;
46 
main(String args[])47     public static void main(String args[]) throws Exception {
48         Provider[] provs = Security.getProviders();
49         for (Provider p : provs) {
50             try {
51                 Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", p);
52                 test(cipher);
53             } catch (NoSuchAlgorithmException nsae) {
54                 // skip testing due to no support
55                 continue;
56             }
57         }
58     }
59 
test(Cipher cipher)60     private static void test(Cipher cipher) throws Exception {
61         System.out.println("Testing " + cipher.getProvider());
62 
63         boolean failedOnce = false;
64         Exception failedReason = null;
65 
66         int tagLen = 96; // in bits
67         byte[] keyBytes = new byte[16];
68         random.nextBytes(keyBytes);
69         byte[] dataChunk = new byte[dataSize];
70         random.nextBytes(dataChunk);
71 
72         SecretKey key = new SecretKeySpec(keyBytes, "AES");
73         // re-use key bytes as IV as the real test is buffer calculation
74         GCMParameterSpec s = new GCMParameterSpec(tagLen, keyBytes);
75 
76         /*
77          * Iterate through various sizes to make sure that the code works with
78          * internal temp buffer size 4096.
79          */
80         for (int t = 1; t <= multiples; t++) {
81             int size = t * dataSize;
82 
83             System.out.println("\nTesting data size: " + size);
84 
85             try {
86                 decrypt(cipher, key, s, dataChunk, t,
87                         ByteBuffer.allocate(dataSize),
88                         ByteBuffer.allocate(size),
89                         ByteBuffer.allocateDirect(dataSize),
90                         ByteBuffer.allocateDirect(size));
91             } catch (Exception e) {
92                 System.out.println("\tFailed with data size " + size);
93                 failedOnce = true;
94                 failedReason = e;
95             }
96         }
97         if (failedOnce) {
98             throw failedReason;
99         }
100         System.out.println("\n=> Passed...");
101     }
102 
103     private enum TestVariant {
104         HEAP_HEAP, HEAP_DIRECT, DIRECT_HEAP, DIRECT_DIRECT
105     };
106 
decrypt(Cipher cipher, SecretKey key, GCMParameterSpec s, byte[] dataChunk, int multiples, ByteBuffer heapIn, ByteBuffer heapOut, ByteBuffer directIn, ByteBuffer directOut)107     private static void decrypt(Cipher cipher, SecretKey key,
108             GCMParameterSpec s, byte[] dataChunk, int multiples,
109             ByteBuffer heapIn, ByteBuffer heapOut, ByteBuffer directIn,
110             ByteBuffer directOut) throws Exception {
111 
112         ByteBuffer inBB = null;
113         ByteBuffer outBB = null;
114 
115         // try various combinations of input/output
116         for (TestVariant tv : TestVariant.values()) {
117             System.out.println(" " + tv);
118 
119             switch (tv) {
120             case HEAP_HEAP:
121                 inBB = heapIn;
122                 outBB = heapOut;
123                 break;
124             case HEAP_DIRECT:
125                 inBB = heapIn;
126                 outBB = directOut;
127                 break;
128             case DIRECT_HEAP:
129                 inBB = directIn;
130                 outBB = heapOut;
131                 break;
132             case DIRECT_DIRECT:
133                 inBB = directIn;
134                 outBB = directOut;
135                 break;
136             }
137 
138             // prepare input and output buffers
139             inBB.clear();
140             inBB.put(dataChunk);
141 
142             outBB.clear();
143 
144             try {
145                 // Always re-init the Cipher object so cipher is in
146                 // a good state for future testing
147                 cipher.init(Cipher.DECRYPT_MODE, key, s);
148 
149                 for (int i = 0; i < multiples; i++) {
150                     inBB.flip();
151                     cipher.update(inBB, outBB);
152                     if (inBB.hasRemaining()) {
153                         throw new Exception("buffer not empty");
154                     }
155                 }
156                 // finish decryption and process all data buffered
157                 cipher.doFinal(inBB, outBB);
158                 throw new RuntimeException("Error: doFinal completed without exception");
159             } catch (AEADBadTagException ex) {
160                 System.out.println("Expected AEADBadTagException thrown");
161                 continue;
162             }
163         }
164     }
165 }
166