/* * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ import static java.lang.System.out; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.spec.AlgorithmParameterSpec; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.ShortBufferException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; /** * This is a abstract class used to test various ciphers */ public abstract class TestCipher { private final String SUNJCE = "SunJCE"; private final String ALGORITHM; private final String[] MODES; private final String[] PADDINGS; /* Used to test variable-key-length ciphers: Key size tested is increment of KEYCUTTER from minKeySize to min(maxKeySize, Cipher.getMaxAllowedKeyLength(algo)). */ private final int KEYCUTTER = 8; private final int minKeySize; private final int maxKeySize; // Used to assert that Encryption/Decryption works with same buffer // TEXT_LEN is multiple of blocks in order to work against ciphers w/ NoPadding private final int TEXT_LEN = 800; private final int ENC_OFFSET = 6; private final int STORAGE_OFFSET = 3; private final int PAD_BYTES = 16; private final byte[] IV; private final byte[] INPUT_TEXT; // for variable-key-length ciphers TestCipher(String algo, String[] modes, String[] paddings, int minKeySize, int maxKeySize) throws NoSuchAlgorithmException { ALGORITHM = algo; MODES = modes; PADDINGS = paddings; this.minKeySize = minKeySize; int maxAllowedKeySize = Cipher.getMaxAllowedKeyLength(ALGORITHM); if (maxKeySize > maxAllowedKeySize) { maxKeySize = maxAllowedKeySize; } this.maxKeySize = maxKeySize; IV = generateBytes(8); INPUT_TEXT = generateBytes(TEXT_LEN + PAD_BYTES + ENC_OFFSET); } // for fixed-key-length ciphers TestCipher(String algo, String[] modes, String[] paddings) { ALGORITHM = algo; MODES = modes; PADDINGS = paddings; this.minKeySize = this.maxKeySize = 0; IV = generateBytes(8); INPUT_TEXT = generateBytes(TEXT_LEN + PAD_BYTES + ENC_OFFSET); } private static byte[] generateBytes(int length) { byte[] bytes = new byte[length]; for (int i = 0; i < length; i++) { bytes[i] = (byte) (i & 0xff); } return bytes; } private boolean isMultipleKeyLengthSupported() { return (maxKeySize != minKeySize); } public void runAll() throws InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException, ShortBufferException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchProviderException { for (String mode : MODES) { for (String padding : PADDINGS) { if (!isMultipleKeyLengthSupported()) { runTest(mode, padding, minKeySize); } else { int keySize = maxKeySize; while (keySize >= minKeySize) { out.println("With Key Strength: " + keySize); runTest(mode, padding, keySize); keySize -= KEYCUTTER; } } } } } private void runTest(String mo, String pad, int keySize) throws NoSuchPaddingException, BadPaddingException, ShortBufferException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException { String TRANSFORMATION = ALGORITHM + "/" + mo + "/" + pad; out.println("Testing: " + TRANSFORMATION); // Initialization Cipher ci = Cipher.getInstance(TRANSFORMATION, SUNJCE); KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM, SUNJCE); if (keySize != 0) { kg.init(keySize); } SecretKey key = kg.generateKey(); SecretKeySpec skeySpec = new SecretKeySpec(key.getEncoded(), ALGORITHM); AlgorithmParameterSpec aps = new IvParameterSpec(IV); if (mo.equalsIgnoreCase("ECB")) { ci.init(Cipher.ENCRYPT_MODE, key); } else { ci.init(Cipher.ENCRYPT_MODE, key, aps); } // Encryption byte[] plainText = INPUT_TEXT.clone(); // Generate cipher and save to separate buffer byte[] cipherText = ci.doFinal(INPUT_TEXT, ENC_OFFSET, TEXT_LEN); // Generate cipher and save to same buffer int enc_bytes = ci.update( INPUT_TEXT, ENC_OFFSET, TEXT_LEN, INPUT_TEXT, STORAGE_OFFSET); enc_bytes += ci.doFinal(INPUT_TEXT, enc_bytes + STORAGE_OFFSET); if (!equalsBlock( INPUT_TEXT, STORAGE_OFFSET, enc_bytes, cipherText, 0, cipherText.length)) { throw new RuntimeException( "Different ciphers generated with same buffer"); } // Decryption if (mo.equalsIgnoreCase("ECB")) { ci.init(Cipher.DECRYPT_MODE, skeySpec); } else { ci.init(Cipher.DECRYPT_MODE, skeySpec, aps); } // Recover text from cipher and save to separate buffer byte[] recoveredText = ci.doFinal(cipherText, 0, cipherText.length); if (!equalsBlock( plainText, ENC_OFFSET, TEXT_LEN, recoveredText, 0, recoveredText.length)) { throw new RuntimeException( "Recovered text not same as plain text"); } else { out.println("Recovered and plain text are same"); } // Recover text from cipher and save to same buffer int dec_bytes = ci.update( INPUT_TEXT, STORAGE_OFFSET, enc_bytes, INPUT_TEXT, ENC_OFFSET); dec_bytes += ci.doFinal(INPUT_TEXT, dec_bytes + ENC_OFFSET); if (!equalsBlock( plainText, ENC_OFFSET, TEXT_LEN, INPUT_TEXT, ENC_OFFSET, dec_bytes)) { throw new RuntimeException( "Recovered text not same as plain text with same buffer"); } else { out.println("Recovered and plain text are same with same buffer"); } out.println("Test Passed."); } private static boolean equalsBlock(byte[] b1, int off1, int len1, byte[] b2, int off2, int len2) { if (len1 != len2) { return false; } for (int i = off1, j = off2, k = 0; k < len1; i++, j++, k++) { if (b1[i] != b2[j]) { return false; } } return true; } }