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