1 /* 2 * Copyright (c) 2007, 2015, 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 import java.io.ByteArrayInputStream; 25 import java.io.ByteArrayOutputStream; 26 import java.io.IOException; 27 import java.security.GeneralSecurityException; 28 import java.security.NoSuchAlgorithmException; 29 import java.util.Arrays; 30 import javax.crypto.Cipher; 31 import javax.crypto.CipherInputStream; 32 import javax.crypto.CipherOutputStream; 33 import javax.crypto.KeyGenerator; 34 import javax.crypto.SecretKey; 35 import jdk.testlibrary.RandomFactory; 36 37 /* 38 * @test 39 * @bug 8048596 40 * @key randomness 41 * @summary Test CICO AEAD read/write/skip operations 42 * @library /lib/testlibrary 43 */ 44 public class ReadWriteSkip { 45 46 static enum BufferType { 47 BYTE_ARRAY_BUFFERING, INT_BYTE_BUFFERING 48 } 49 50 static final int KEY_LENGTHS[] = {128, 192, 256}; 51 static final int TXT_LENGTHS[] = {800, 0}; 52 static final int AAD_LENGTHS[] = {0, 100}; 53 static final int BLOCK = 50; 54 static final int SAVE = 45; 55 static final int DISCARD = BLOCK - SAVE; 56 static final String PROVIDER = "SunJCE"; 57 static final String AES = "AES"; 58 static final String GCM = "GCM"; 59 static final String PADDING = "NoPadding"; 60 static final String TRANSFORM = AES + "/" + GCM + "/" + PADDING; 61 62 final SecretKey key; 63 final byte[] plaintext; 64 final byte[] AAD; 65 final int textLength;; 66 final int keyLength; 67 Cipher encryptCipher; 68 Cipher decryptCipher; 69 CipherInputStream ciInput; 70 71 public static void main(String[] args) throws Exception { 72 boolean success = true; 73 for (int keyLength : KEY_LENGTHS) { 74 if (keyLength > Cipher.getMaxAllowedKeyLength(TRANSFORM)) { 75 // skip this if this key length is larger than what's 76 // configured in the jce jurisdiction policy files 77 continue; 78 } 79 for (int textLength : TXT_LENGTHS) { 80 for (int AADLength : AAD_LENGTHS) { 81 System.out.println("Key length = " + keyLength 82 + ", text length = " + textLength 83 + ", AAD length = " + AADLength); 84 try { 85 run(keyLength, textLength, AADLength); 86 System.out.println("Test case passed"); 87 } catch (Exception e) { 88 System.out.println("Test case failed: " + e); 89 success = false; 90 } 91 } 92 } 93 } 94 95 if (!success) { 96 throw new RuntimeException("At least one test case failed"); 97 } 98 99 System.out.println("Test passed"); 100 } 101 102 ReadWriteSkip(int keyLength, int textLength, int AADLength) 103 throws Exception { 104 this.keyLength = keyLength; 105 this.textLength = textLength; 106 107 // init AAD 108 this.AAD = new byte[AADLength]; 109 RandomFactory.getRandom().nextBytes(AAD); 110 111 // init a secret Key 112 KeyGenerator kg = KeyGenerator.getInstance(AES, PROVIDER); 113 kg.init(this.keyLength); 114 this.key = kg.generateKey(); 115 116 this.plaintext = new byte[textLength]; 117 RandomFactory.getRandom().nextBytes(plaintext); 118 } 119 120 final void doTest(BufferType type) throws Exception { 121 // init ciphers 122 encryptCipher = createCipher(Cipher.ENCRYPT_MODE); 123 decryptCipher = createCipher(Cipher.DECRYPT_MODE); 124 125 // init cipher input stream 126 ciInput = new CipherInputStream(new ByteArrayInputStream(plaintext), 127 encryptCipher); 128 129 runTest(type); 130 } 131 132 void runTest(BufferType type) throws Exception {} 133 134 private Cipher createCipher(int mode) throws GeneralSecurityException { 135 Cipher cipher = Cipher.getInstance(TRANSFORM, PROVIDER); 136 if (mode == Cipher.ENCRYPT_MODE) { 137 cipher.init(Cipher.ENCRYPT_MODE, key); 138 } else { 139 if (encryptCipher != null) { 140 cipher.init(Cipher.DECRYPT_MODE, key, 141 encryptCipher.getParameters()); 142 } else { 143 throw new RuntimeException("Can't create a cipher"); 144 } 145 } 146 cipher.updateAAD(AAD); 147 return cipher; 148 } 149 150 /* 151 * Run test cases 152 */ 153 static void run(int keyLength, int textLength, int AADLength) 154 throws Exception { 155 new ReadWriteTest(keyLength, textLength, AADLength) 156 .doTest(BufferType.BYTE_ARRAY_BUFFERING); 157 new ReadWriteTest(keyLength, textLength, AADLength) 158 .doTest(BufferType.INT_BYTE_BUFFERING); 159 new SkipTest(keyLength, textLength, AADLength) 160 .doTest(BufferType.BYTE_ARRAY_BUFFERING); 161 new SkipTest(keyLength, textLength, AADLength) 162 .doTest(BufferType.INT_BYTE_BUFFERING); 163 } 164 165 static void check(byte[] first, byte[] second) { 166 if (!Arrays.equals(first, second)) { 167 throw new RuntimeException("Arrays are not equal"); 168 } 169 } 170 171 /* 172 * CICO AEAD read/write functional test. 173 * 174 * Check if encrypt/decrypt operations work correctly. 175 * 176 * Test scenario: 177 * - initializes plain text with random generated data 178 * - for given AEAD algorithm instantiates encrypt and decrypt Ciphers 179 * - instantiates CipherInputStream with the encrypt Cipher 180 * - instantiates CipherOutputStream with the decrypt Cipher 181 * - performs reading from the CipherInputStream (encryption data) 182 * and writing to the CipherOutputStream (decryption). As a result, 183 * output of the CipherOutputStream should be equal 184 * with original plain text 185 * - check if the original plain text is equal to output 186 * of the CipherOutputStream 187 * - if it is equal the test passes, otherwise it fails 188 */ 189 static class ReadWriteTest extends ReadWriteSkip { 190 191 public ReadWriteTest(int keyLength, int textLength, int AADLength) 192 throws Exception { 193 super(keyLength, textLength, AADLength); 194 } 195 196 @Override 197 public void runTest(BufferType bufType) throws IOException, 198 GeneralSecurityException { 199 200 ByteArrayOutputStream baOutput = new ByteArrayOutputStream(); 201 try (CipherOutputStream ciOutput = new CipherOutputStream(baOutput, 202 decryptCipher)) { 203 if (bufType == BufferType.BYTE_ARRAY_BUFFERING) { 204 doByteTest(ciOutput); 205 } else { 206 doIntTest(ciOutput); 207 } 208 } 209 210 check(plaintext, baOutput.toByteArray()); 211 } 212 213 /* 214 * Implements byte array buffering type test case 215 */ 216 public void doByteTest(CipherOutputStream out) throws IOException { 217 byte[] buffer = new byte[textLength + 1]; 218 int len = ciInput.read(buffer); 219 while (len != -1) { 220 out.write(buffer, 0, len); 221 len = ciInput.read(buffer); 222 } 223 } 224 225 /* 226 * Implements integer buffering type test case 227 */ 228 public void doIntTest(CipherOutputStream out) throws IOException { 229 int buffer = ciInput.read(); 230 while (buffer != -1) { 231 out.write(buffer); 232 buffer = ciInput.read(); 233 } 234 } 235 } 236 237 /* 238 * CICO AEAD SKIP functional test. 239 * 240 * Checks if the encrypt/decrypt operations work correctly 241 * when skip() method is used. 242 * 243 * Test scenario: 244 * - initializes a plain text with random data 245 * - initializes ciphers 246 * - initializes cipher streams 247 * - split plain text to TEXT_SIZE/BLOCK blocks 248 * - read from CipherInputStream2 one block at time 249 * - the last DISCARD = BLOCK - SAVE bytes are skipping for each block 250 * - therefore, plain text data go through CipherInputStream1 (encrypting) 251 * and CipherInputStream2 (decrypting) 252 * - as a result, output should equal to the original text 253 * except DISCART byte for each block 254 * - check result buffers 255 */ 256 static class SkipTest extends ReadWriteSkip { 257 258 private final int numberOfBlocks; 259 private final byte[] outputText; 260 261 public SkipTest(int keyLength, int textLength, int AADLength) 262 throws Exception { 263 super(keyLength, textLength, AADLength); 264 numberOfBlocks = this.textLength / BLOCK; 265 outputText = new byte[numberOfBlocks * SAVE]; 266 } 267 268 private void doByteTest(int blockNum, CipherInputStream cis) 269 throws IOException { 270 int index = blockNum * SAVE; 271 int len = cis.read(outputText, index, SAVE); 272 index += len; 273 int read = 0; 274 while (len != SAVE && read != -1) { 275 read = cis.read(outputText, index, SAVE - len); 276 len += read; 277 index += read; 278 } 279 } 280 281 private void doIntTest(int blockNum, CipherInputStream cis) 282 throws IOException { 283 int i = blockNum * SAVE; 284 for (int j = 0; j < SAVE && i < outputText.length; j++, i++) { 285 int b = cis.read(); 286 if (b != -1) { 287 outputText[i] = (byte) b; 288 } 289 } 290 } 291 292 @Override 293 public void runTest(BufferType type) throws IOException, 294 NoSuchAlgorithmException { 295 try (CipherInputStream cis = new CipherInputStream(ciInput, 296 decryptCipher)) { 297 for (int i = 0; i < numberOfBlocks; i++) { 298 if (type == BufferType.BYTE_ARRAY_BUFFERING) { 299 doByteTest(i, cis); 300 } else { 301 doIntTest(i, cis); 302 } 303 if (cis.available() >= DISCARD) { 304 cis.skip(DISCARD); 305 } else { 306 for (int k = 0; k < DISCARD; k++) { 307 cis.read(); 308 } 309 } 310 } 311 } 312 313 byte[] expectedText = new byte[numberOfBlocks * SAVE]; 314 for (int m = 0; m < numberOfBlocks; m++) { 315 for (int n = 0; n < SAVE; n++) { 316 expectedText[m * SAVE + n] = plaintext[m * BLOCK + n]; 317 } 318 } 319 check(expectedText, outputText); 320 } 321 } 322 }