1/* 2 * Copyright (c) 2015, 2016, 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.security.InvalidAlgorithmParameterException; 27import java.security.InvalidKeyException; 28import java.security.NoSuchAlgorithmException; 29import java.security.NoSuchProviderException; 30import java.security.spec.AlgorithmParameterSpec; 31 32import javax.crypto.BadPaddingException; 33import javax.crypto.Cipher; 34import javax.crypto.IllegalBlockSizeException; 35import javax.crypto.KeyGenerator; 36import javax.crypto.NoSuchPaddingException; 37import javax.crypto.SecretKey; 38import javax.crypto.ShortBufferException; 39import javax.crypto.spec.IvParameterSpec; 40import javax.crypto.spec.SecretKeySpec; 41 42/** 43 * This is a abstract class used to test various ciphers 44 */ 45public abstract class TestCipher { 46 47 private final String SUNJCE = "SunJCE"; 48 private final String ALGORITHM; 49 private final String[] MODES; 50 private final String[] PADDINGS; 51 52 /* Used to test variable-key-length ciphers: 53 Key size tested is increment of KEYCUTTER from minKeySize 54 to min(maxKeySize, Cipher.getMaxAllowedKeyLength(algo)). 55 */ 56 private final int KEYCUTTER = 8; 57 private final int minKeySize; 58 private final int maxKeySize; 59 60 // Used to assert that Encryption/Decryption works with same buffer 61 // TEXT_LEN is multiple of blocks in order to work against ciphers w/ NoPadding 62 private final int TEXT_LEN = 800; 63 private final int ENC_OFFSET = 6; 64 private final int STORAGE_OFFSET = 3; 65 private final int PAD_BYTES = 16; 66 67 private final byte[] IV; 68 private final byte[] INPUT_TEXT; 69 70 // for variable-key-length ciphers 71 TestCipher(String algo, String[] modes, String[] paddings, 72 int minKeySize, int maxKeySize) throws NoSuchAlgorithmException { 73 ALGORITHM = algo; 74 MODES = modes; 75 PADDINGS = paddings; 76 this.minKeySize = minKeySize; 77 int maxAllowedKeySize = Cipher.getMaxAllowedKeyLength(ALGORITHM); 78 if (maxKeySize > maxAllowedKeySize) { 79 maxKeySize = maxAllowedKeySize; 80 } 81 this.maxKeySize = maxKeySize; 82 IV = generateBytes(8); 83 INPUT_TEXT = generateBytes(TEXT_LEN + PAD_BYTES + ENC_OFFSET); 84 } 85 86 // for fixed-key-length ciphers 87 TestCipher(String algo, String[] modes, String[] paddings) { 88 ALGORITHM = algo; 89 MODES = modes; 90 PADDINGS = paddings; 91 this.minKeySize = this.maxKeySize = 0; 92 93 IV = generateBytes(8); 94 INPUT_TEXT = generateBytes(TEXT_LEN + PAD_BYTES + ENC_OFFSET); 95 } 96 97 private static byte[] generateBytes(int length) { 98 byte[] bytes = new byte[length]; 99 for (int i = 0; i < length; i++) { 100 bytes[i] = (byte) (i & 0xff); 101 } 102 return bytes; 103 } 104 105 private boolean isMultipleKeyLengthSupported() { 106 return (maxKeySize != minKeySize); 107 } 108 109 public void runAll() throws InvalidKeyException, 110 NoSuchPaddingException, InvalidAlgorithmParameterException, 111 ShortBufferException, IllegalBlockSizeException, 112 BadPaddingException, NoSuchAlgorithmException, 113 NoSuchProviderException { 114 115 for (String mode : MODES) { 116 for (String padding : PADDINGS) { 117 if (!isMultipleKeyLengthSupported()) { 118 runTest(mode, padding, minKeySize); 119 } else { 120 int keySize = maxKeySize; 121 while (keySize >= minKeySize) { 122 out.println("With Key Strength: " + keySize); 123 runTest(mode, padding, keySize); 124 keySize -= KEYCUTTER; 125 } 126 } 127 } 128 } 129 } 130 131 private void runTest(String mo, String pad, int keySize) 132 throws NoSuchPaddingException, BadPaddingException, 133 ShortBufferException, IllegalBlockSizeException, 134 InvalidAlgorithmParameterException, InvalidKeyException, 135 NoSuchAlgorithmException, NoSuchProviderException { 136 137 String TRANSFORMATION = ALGORITHM + "/" + mo + "/" + pad; 138 out.println("Testing: " + TRANSFORMATION); 139 140 // Initialization 141 Cipher ci = Cipher.getInstance(TRANSFORMATION, SUNJCE); 142 KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM, SUNJCE); 143 if (keySize != 0) { 144 kg.init(keySize); 145 } 146 147 SecretKey key = kg.generateKey(); 148 SecretKeySpec skeySpec = new SecretKeySpec(key.getEncoded(), ALGORITHM); 149 150 AlgorithmParameterSpec aps = new IvParameterSpec(IV); 151 if (mo.equalsIgnoreCase("ECB")) { 152 ci.init(Cipher.ENCRYPT_MODE, key); 153 } else { 154 ci.init(Cipher.ENCRYPT_MODE, key, aps); 155 } 156 157 // Encryption 158 byte[] plainText = INPUT_TEXT.clone(); 159 160 // Generate cipher and save to separate buffer 161 byte[] cipherText = ci.doFinal(INPUT_TEXT, ENC_OFFSET, TEXT_LEN); 162 163 // Generate cipher and save to same buffer 164 int enc_bytes = ci.update( 165 INPUT_TEXT, ENC_OFFSET, TEXT_LEN, INPUT_TEXT, STORAGE_OFFSET); 166 enc_bytes += ci.doFinal(INPUT_TEXT, enc_bytes + STORAGE_OFFSET); 167 168 if (!equalsBlock( 169 INPUT_TEXT, STORAGE_OFFSET, enc_bytes, 170 cipherText, 0, cipherText.length)) { 171 throw new RuntimeException( 172 "Different ciphers generated with same buffer"); 173 } 174 175 // Decryption 176 if (mo.equalsIgnoreCase("ECB")) { 177 ci.init(Cipher.DECRYPT_MODE, skeySpec); 178 } else { 179 ci.init(Cipher.DECRYPT_MODE, skeySpec, aps); 180 } 181 182 // Recover text from cipher and save to separate buffer 183 byte[] recoveredText = ci.doFinal(cipherText, 0, cipherText.length); 184 185 if (!equalsBlock( 186 plainText, ENC_OFFSET, TEXT_LEN, 187 recoveredText, 0, recoveredText.length)) { 188 throw new RuntimeException( 189 "Recovered text not same as plain text"); 190 } else { 191 out.println("Recovered and plain text are same"); 192 } 193 194 // Recover text from cipher and save to same buffer 195 int dec_bytes = ci.update( 196 INPUT_TEXT, STORAGE_OFFSET, enc_bytes, INPUT_TEXT, ENC_OFFSET); 197 dec_bytes += ci.doFinal(INPUT_TEXT, dec_bytes + ENC_OFFSET); 198 199 if (!equalsBlock( 200 plainText, ENC_OFFSET, TEXT_LEN, 201 INPUT_TEXT, ENC_OFFSET, dec_bytes)) { 202 throw new RuntimeException( 203 "Recovered text not same as plain text with same buffer"); 204 } else { 205 out.println("Recovered and plain text are same with same buffer"); 206 } 207 208 out.println("Test Passed."); 209 } 210 211 private static boolean equalsBlock(byte[] b1, int off1, int len1, 212 byte[] b2, int off2, int len2) { 213 if (len1 != len2) { 214 return false; 215 } 216 for (int i = off1, j = off2, k = 0; k < len1; i++, j++, k++) { 217 if (b1[i] != b2[j]) { 218 return false; 219 } 220 } 221 return true; 222 } 223} 224