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