1 /* 2 * Copyright (c) 2012, 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 7142509 27 * @summary Cipher.doFinal(ByteBuffer,ByteBuffer) fails to 28 * process when in.remaining() == 0 29 */ 30 31 import java.nio.ByteBuffer; 32 import java.security.SecureRandom; 33 import java.util.Arrays; 34 import java.util.Random; 35 36 import javax.crypto.Cipher; 37 import javax.crypto.SecretKey; 38 import javax.crypto.spec.SecretKeySpec; 39 40 /* 41 * Simple test case to show that Cipher.doFinal(ByteBuffer, ByteBuffer) fails to 42 * process the data internally buffered inBB the cipher when input.remaining() 43 * == 0 and at least one buffer is a direct buffer. 44 */ 45 public class DirectBBRemaining { 46 47 private static Random random = new SecureRandom(); 48 private static int testSizes = 40; 49 private static int outputFrequency = 5; 50 51 public static void main(String args[]) throws Exception { 52 boolean failedOnce = false; 53 Exception failedReason = null; 54 55 byte[] keyBytes = new byte[8]; 56 random.nextBytes(keyBytes); 57 SecretKey key = new SecretKeySpec(keyBytes, "DES"); 58 59 Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding", "SunJCE"); 60 cipher.init(Cipher.ENCRYPT_MODE, key); 61 62 /* 63 * Iterate through various sizes to make sure that the code does empty 64 * blocks, single partial blocks, 1 full block, full + partial blocks, 65 * multiple full blocks, etc. 5 blocks (using DES) is probably overkill 66 * but will feel more confident the fix isn't breaking anything. 67 */ 68 System.out.println("Output test results for every " 69 + outputFrequency + " tests..."); 70 71 for (int size = 0; size <= testSizes; size++) { 72 boolean output = (size % outputFrequency) == 0; 73 if (output) { 74 System.out.print("\nTesting buffer size: " + size + ":"); 75 } 76 77 int outSize = cipher.getOutputSize(size); 78 79 try { 80 encrypt(cipher, size, 81 ByteBuffer.allocate(size), 82 ByteBuffer.allocate(outSize), 83 ByteBuffer.allocateDirect(size), 84 ByteBuffer.allocateDirect(outSize), 85 output); 86 } catch (Exception e) { 87 System.out.print("\n Failed with size " + size); 88 failedOnce = true; 89 failedReason = e; 90 91 // If we got an exception, let's be safe for future 92 // testing and reset the cipher to a known good state. 93 cipher.init(Cipher.ENCRYPT_MODE, key); 94 } 95 } 96 if (failedOnce) { 97 throw failedReason; 98 } 99 System.out.println("\nTest Passed..."); 100 } 101 102 private enum TestVariant { 103 104 HEAP_HEAP, HEAP_DIRECT, DIRECT_HEAP, DIRECT_DIRECT 105 }; 106 107 private static void encrypt(Cipher cipher, int size, 108 ByteBuffer heapIn, ByteBuffer heapOut, 109 ByteBuffer directIn, ByteBuffer directOut, 110 boolean output) throws Exception { 111 112 ByteBuffer inBB = null; 113 ByteBuffer outBB = null; 114 115 // Set up data and encrypt to known/expected values. 116 byte[] testdata = new byte[size]; 117 random.nextBytes(testdata); 118 byte[] expected = cipher.doFinal(testdata); 119 120 for (TestVariant tv : TestVariant.values()) { 121 if (output) { 122 System.out.print(" " + tv); 123 } 124 125 switch (tv) { 126 case HEAP_HEAP: 127 inBB = heapIn; 128 outBB = heapOut; 129 break; 130 case HEAP_DIRECT: 131 inBB = heapIn; 132 outBB = directOut; 133 break; 134 case DIRECT_HEAP: 135 inBB = directIn; 136 outBB = heapOut; 137 break; 138 case DIRECT_DIRECT: 139 inBB = directIn; 140 outBB = directOut; 141 break; 142 } 143 144 inBB.clear(); 145 outBB.clear(); 146 147 inBB.put(testdata); 148 inBB.flip(); 149 150 // Process all data in one shot, but don't call doFinal() yet. 151 // May store up to n-1 bytes (w/block size n) internally. 152 cipher.update(inBB, outBB); 153 if (inBB.hasRemaining()) { 154 throw new Exception("buffer not empty"); 155 } 156 157 // finish encryption and process all data buffered 158 cipher.doFinal(inBB, outBB); 159 outBB.flip(); 160 161 // validate output size 162 if (outBB.remaining() != expected.length) { 163 throw new Exception( 164 "incomplete encryption output, expected " 165 + expected.length + " bytes but was only " 166 + outBB.remaining() + " bytes"); 167 } 168 169 // validate output data 170 byte[] encrypted = new byte[outBB.remaining()]; 171 outBB.get(encrypted); 172 if (!Arrays.equals(expected, encrypted)) { 173 throw new Exception("bad encryption output"); 174 } 175 176 if (!Arrays.equals(cipher.doFinal(), cipher.doFinal())) { 177 throw new Exception("Internal buffers still held data!"); 178 } 179 } 180 } 181 }