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 24import static java.lang.System.out; 25 26import java.io.ByteArrayInputStream; 27import java.io.IOException; 28import java.security.InvalidAlgorithmParameterException; 29import java.security.InvalidKeyException; 30import java.security.NoSuchAlgorithmException; 31import java.security.Provider; 32import java.security.Security; 33import java.security.spec.AlgorithmParameterSpec; 34import java.security.spec.InvalidKeySpecException; 35import javax.crypto.Cipher; 36import javax.crypto.CipherInputStream; 37import javax.crypto.KeyGenerator; 38import javax.crypto.NoSuchPaddingException; 39import javax.crypto.SecretKey; 40import javax.crypto.SecretKeyFactory; 41import javax.crypto.spec.IvParameterSpec; 42import javax.crypto.spec.PBEKeySpec; 43import javax.crypto.spec.PBEParameterSpec; 44 45/* 46 * @test 47 * @bug 8048604 48 * @summary This test verifies the assertion "The skip feature of Filter 49 * streams should be supported." for feature 50 * CipherInputStream and CipherOutputStream 51 */ 52public class CICOSkipTest { 53 /** 54 * Block length. 55 */ 56 private static final int BLOCK = 50; 57 58 /** 59 * Saving bytes length. 60 */ 61 private static final int SAVE = 45; 62 63 /** 64 * Plain text length. 65 */ 66 private static final int PLAIN_TEXT_LENGTH = 800; 67 68 /** 69 * Skip reading byte size. This should be same to BLOCK - SAVE 70 */ 71 private static final int DISCARD = BLOCK - SAVE; 72 73 private static final String[] ALGOS = {"DES", "DESede", "Blowfish"}; 74 private static final String[] MODES = {"ECB", "CBC", "CFB", "CFB32", 75 "OFB", "OFB64", "PCBC"}; 76 private static final String[] PADDINGS = {"NoPadding", "Pkcs5Padding"}; 77 private static final String[] PBE_ALGOS = {"PBEWithMD5AndDES", 78 "PBEWithMD5AndDES/CBC/PKCS5Padding"}; 79 80 public static void main(String[] args) throws Exception { 81 // how many kinds of padding mode such as PKCS5padding and NoPadding 82 for (String algo : ALGOS) { 83 for (String mode : MODES) { 84 int padKinds = 1; 85 if (mode.equalsIgnoreCase("ECB") 86 || mode.equalsIgnoreCase("PCBC") 87 || mode.equalsIgnoreCase("CBC")) { 88 padKinds = PADDINGS.length; 89 } 90 // PKCS5padding is meaningful only for ECB, CBC, PCBC 91 for (int k = 0; k < padKinds; k++) { 92 String info = algo + "/" + mode + "/" + PADDINGS[k]; 93 try { 94 CipherGenerator cg = new CipherGenerator(algo, mode, 95 PADDINGS[k]); 96 for (ReadMethod model : ReadMethod.values()) { 97 runTest(cg.getPair(), info, model); 98 } 99 } catch (LengthLimitException exp) { 100 // skip this if this key length is larger than what's 101 // configured in the jce jurisdiction policy files 102 out.println(exp.getMessage() + " is expected."); 103 } 104 } 105 } 106 } 107 for (String pbeAlgo : PBE_ALGOS) { 108 for (ReadMethod model : ReadMethod.values()) { 109 System.out.println("Testing Algorithm : " + pbeAlgo 110 + " ReadMethod : " + model); 111 runTest(new CipherGenerator(pbeAlgo).getPair(), pbeAlgo, model); 112 } 113 } 114 } 115 116 private static void runTest(Cipher[] pair, String info, ReadMethod whichRead) 117 throws IOException { 118 byte[] plainText = TestUtilities.generateBytes(PLAIN_TEXT_LENGTH); 119 out.println("Testing: " + info + "/" + whichRead); 120 try (ByteArrayInputStream baInput = new ByteArrayInputStream(plainText); 121 CipherInputStream ciInput1 = new CipherInputStream(baInput, 122 pair[0]); 123 CipherInputStream ciInput2 = new CipherInputStream(ciInput1, 124 pair[1]);) { 125 // Skip 5 bytes after read 45 bytes and repeat until finish 126 // (Read from the input and write to the output using 2 types 127 // of buffering : byte[] and int) 128 // So output has size: 129 // (OVERALL/BLOCK)* SAVE = (800 / 50) * 45 = 720 bytes 130 int numOfBlocks = plainText.length / BLOCK; 131 132 // Output buffer. 133 byte[] outputText = new byte[numOfBlocks * SAVE]; 134 int index = 0; 135 for (int i = 0; i < numOfBlocks; i++) { 136 index = whichRead.readByte(ciInput2, outputText, SAVE, index); 137 // If available is more than expected discard byte size. Skip 138 // discard bytes, otherwise try to read discard bytes by read. 139 if (ciInput2.available() >= DISCARD) { 140 ciInput2.skip(DISCARD); 141 } else { 142 for (int k = 0; k < DISCARD; k++) { 143 ciInput2.read(); 144 } 145 } 146 } 147 // Verify output is same as input 148 if (!TestUtilities 149 .equalsBlockPartial(plainText, outputText, BLOCK, SAVE)) { 150 throw new RuntimeException("Test failed with compare fail"); 151 } 152 } 153 } 154} 155 156class CipherGenerator { 157 /** 158 * Initialization vector length. 159 */ 160 private static final int IV_LENGTH = 8; 161 162 private static final String PASSWD = "Sesame!(@#$%^&*)"; 163 164 private final Cipher[] pair = new Cipher[2]; 165 166 // For DES/DESede ciphers 167 CipherGenerator(String algo, String mo, String pad) 168 throws NoSuchAlgorithmException, 169 InvalidAlgorithmParameterException, InvalidKeyException, 170 NoSuchPaddingException, SecurityException, LengthLimitException { 171 // Do initialization 172 KeyGenerator kg = KeyGenerator.getInstance(algo); 173 SecretKey key = kg.generateKey(); 174 if (key.getEncoded().length * 8 > Cipher.getMaxAllowedKeyLength(algo)) { 175 // skip this if this key length is larger than what's 176 // configured in the jce jurisdiction policy files 177 throw new LengthLimitException( 178 "Skip this test if key length is larger than what's" 179 + "configured in the jce jurisdiction policy files"); 180 } 181 AlgorithmParameterSpec aps = null; 182 if (!mo.equalsIgnoreCase("ECB")) { 183 byte[] iv = TestUtilities.generateBytes(IV_LENGTH); 184 aps = new IvParameterSpec(iv); 185 } 186 initCiphers(algo + "/" + mo + "/" + pad, key, aps); 187 } 188 189 // For PBE ciphers 190 CipherGenerator(String algo) throws NoSuchAlgorithmException, 191 InvalidAlgorithmParameterException, InvalidKeyException, 192 NoSuchPaddingException, InvalidKeySpecException { 193 // Do initialization 194 byte[] salt = TestUtilities.generateBytes(IV_LENGTH); 195 int iterCnt = 6; 196 SecretKeyFactory skf = SecretKeyFactory.getInstance(algo.split("/")[0]); 197 SecretKey key = skf 198 .generateSecret(new PBEKeySpec(PASSWD.toCharArray())); 199 AlgorithmParameterSpec aps = new PBEParameterSpec(salt, iterCnt); 200 initCiphers(algo, key, aps); 201 } 202 203 private void initCiphers(String algo, SecretKey key, 204 AlgorithmParameterSpec aps) throws NoSuchAlgorithmException, 205 NoSuchPaddingException, InvalidKeyException, 206 InvalidAlgorithmParameterException { 207 Provider provider = Security.getProvider("SunJCE"); 208 if (provider == null) { 209 throw new RuntimeException("SunJCE provider does not exist."); 210 } 211 Cipher ci1 = Cipher.getInstance(algo, provider); 212 ci1.init(Cipher.ENCRYPT_MODE, key, aps); 213 pair[0] = ci1; 214 Cipher ci2 = Cipher.getInstance(algo, provider); 215 ci2.init(Cipher.DECRYPT_MODE, key, aps); 216 pair[1] = ci2; 217 } 218 219 Cipher[] getPair() { 220 return pair; 221 } 222} 223 224enum ReadMethod { 225 // read one byte at a time for save times 226 READ_ONE_BYTE { 227 @Override 228 int readByte(CipherInputStream ciIn2, byte[] outputText, int save, 229 int index) throws IOException { 230 for (int j = 0; j < save; j++, index++) { 231 int buffer0 = ciIn2.read(); 232 if (buffer0 != -1) { 233 outputText[index] = (byte) buffer0; 234 } else { 235 break; 236 } 237 } 238 return index; 239 } 240 }, 241 // read a chunk of save bytes if possible 242 READ_BLOCK { 243 @Override 244 int readByte(CipherInputStream ciIn2, byte[] outputText, int save, 245 int index) throws IOException { 246 int len1 = ciIn2.read(outputText, index, save); 247 out.println("Init: index=" + index + ",len=" + len1); 248 // read more until save bytes 249 index += len1; 250 int len2 = 0; 251 while (len1 != save && len2 != -1) { 252 len2 = ciIn2.read(outputText, index, save - len1); 253 out.println("Cont: index=" + index + ",len=" + len2); 254 len1 += len2; 255 index += len2; 256 } 257 return index; 258 } 259 }; 260 261 abstract int readByte(CipherInputStream ciIn2, byte[] outputText, int save, 262 int index) throws IOException; 263}; 264 265class LengthLimitException extends Exception { 266 267 public LengthLimitException(String string) { 268 super(string); 269 } 270} 271