package gnu.testlet.gnu.crypto.jce;
// --------------------------------------------------------------------------
// $Id: TestOfCipher.java,v 1.7 2005/10/06 04:24:19 rsdio Exp $
//
// Copyright (C) 2002, 2003 Free Software Foundation, Inc.
//
// This file is part of GNU Crypto.
//
// GNU Crypto is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License or (at your
// option) any later version.
//
// GNU Crypto 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 for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; see the file COPYING. If not, write to the
//
// Free Software Foundation Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA 02110-1301
// USA
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library. Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//
// As a special exception, the copyright holders of this library give
// you permission to link this library with independent modules to
// produce an executable, regardless of the license terms of these
// independent modules, and to copy and distribute the resulting
// executable under terms of your choice, provided that you also meet,
// for each linked independent module, the terms and conditions of the
// license of that module. An independent module is a module which is
// not derived from or based on this library. If you modify this
// library, you may extend this exception to your version of the
// library, but you are not obligated to do so. If you do not wish to
// do so, delete this exception statement from your version.
// --------------------------------------------------------------------------
// Tags: GNU-CRYPTO
import gnu.crypto.Registry;
import gnu.crypto.cipher.CipherFactory;
import gnu.crypto.cipher.IBlockCipher;
import gnu.crypto.jce.GnuCrypto;
import gnu.crypto.mode.IMode;
import gnu.crypto.mode.ModeFactory;
import gnu.crypto.pad.IPad;
import gnu.crypto.pad.PadFactory;
import gnu.testlet.TestHarness;
import gnu.testlet.Testlet;
import java.security.Security;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* <p>Conformance tests for the JCE Provider implementations of the Cipher SPI
* classes.</p>
*
* @version $Revision: 1.7 $
*/
public class TestOfCipher implements Testlet {
// Constants and variables.
// -----------------------------------------------------------------------
// Constructors.
// -----------------------------------------------------------------------
// default 0-arguments constructor
// Class methods.
// -----------------------------------------------------------------------
// Instance methods.
// -----------------------------------------------------------------------
public void test(TestHarness harness) {
setUp();
testUnknownCipher(harness);
testEquality(harness);
testPadding(harness);
testPartial(harness);
testDoFinal(harness);
}
/** Should fail with an unknown algorithm. */
public void testUnknownCipher(TestHarness harness) {
harness.checkPoint("testUnknownCipher");
try {
Cipher.getInstance("Godot", Registry.GNU_CRYPTO);
harness.fail("testUnknownCipher()");
} catch (Exception x) {
harness.check(true);
}
}
/**
* Tests if the result of using a cipher through gnu.crypto Factory classes
* yields same value as using instances obtained the JCE way.
*/
public void testEquality(TestHarness harness) {
harness.checkPoint("testEquality");
String cipherName = null, modeName;
IMode gnu = null;
Cipher jce = null;
HashMap attrib = new HashMap();
byte[] pt = null;
// byte[] iv = null;
byte[] ct1 = null, ct2 = null;
byte[] cpt1 = null, cpt2 = null;
Iterator ci, mi;
int bs;
try {
for (ci = CipherFactory.getNames().iterator(); ci.hasNext(); ) {
cipherName = (String) ci.next();
IBlockCipher cipher = CipherFactory.getInstance(cipherName);
bs = cipher.defaultBlockSize();
for (mi = ModeFactory.getNames().iterator(); mi.hasNext(); ) {
modeName = (String) mi.next();
gnu = ModeFactory.getInstance(modeName, cipher, bs);
jce = Cipher.getInstance(cipherName + "/" + modeName
+ "/NoPadding", Registry.GNU_CRYPTO);
pt = new byte[bs];
for (int i = 0; i < bs; i++) {
pt[i] = (byte) i;
}
attrib.put(IBlockCipher.CIPHER_BLOCK_SIZE, new Integer(bs));
attrib.put(IMode.IV, pt);
for (Iterator ks = cipher.keySizes(); ks.hasNext(); ) {
byte[] kb = new byte[((Integer) ks.next()).intValue()];
for (int i = 0; i < kb.length; i++) {
kb[i] = (byte) i;
}
attrib.put(IMode.STATE, new Integer(IMode.ENCRYPTION));
attrib.put(IBlockCipher.KEY_MATERIAL, kb);
gnu.reset();
gnu.init(attrib);
ct1 = new byte[bs];
gnu.update(pt, 0, ct1, 0);
jce.init(Cipher.ENCRYPT_MODE,
new SecretKeySpec(kb, cipherName),
new IvParameterSpec(pt));
ct2 = new byte[bs];
jce.doFinal(pt, 0, bs, ct2, 0);
harness.check(Arrays.equals(ct1, ct2), "testEquality("+cipherName+")");
attrib.put(IMode.STATE, new Integer(IMode.DECRYPTION));
cpt1 = new byte[bs];
gnu.reset();
gnu.init(attrib);
gnu.update(ct1, 0, cpt1, 0);
harness.check(Arrays.equals(pt, cpt1), "testEquality("+cipherName+")");
jce.init(Cipher.DECRYPT_MODE,
new SecretKeySpec(kb, cipherName),
new IvParameterSpec(pt));
cpt2 = new byte[bs];
jce.doFinal(ct2, 0, bs, cpt2, 0);
harness.check(Arrays.equals(pt, cpt2), "testEquality("+cipherName+")");
harness.check(Arrays.equals(cpt1, cpt2), "testEquality("+cipherName+")");
}
}
}
} catch (Exception x) {
harness.debug(x);
harness.fail("testEquality("+cipherName+"): " + String.valueOf(x));
}
}
/**
* Test that the padding results in the same cipher/plaintexts for instances
* derived from both the GNU Factory and the JCE one.
*/
public void testPadding(TestHarness harness) {
harness.checkPoint("testPadding");
String padName = null;
IMode gnu = ModeFactory.getInstance("ECB", "AES", 16);
IPad pad;
Cipher jce;
byte[] kb = new byte[32];
for (int i = 0; i < kb.length; i++) {
kb[i] = (byte) i;
}
byte[] pt = new byte[42];
for (int i = 0; i < pt.length; i++) {
pt[i] = (byte) i;
}
byte[] ppt = new byte[48]; // padded plaintext.
System.arraycopy(pt, 0, ppt, 0, 42);
byte[] ct1 = new byte[48], ct2 = new byte[48];
byte[] cpt1 = new byte[42], cpt2 = new byte[42];
HashMap attrib = new HashMap();
attrib.put(IBlockCipher.KEY_MATERIAL, kb);
try {
for (Iterator it = PadFactory.getNames().iterator(); it.hasNext(); ) {
padName = (String) it.next();
// skip EME-PKCS1-V1.5 padding since it's not a true block cipher
// padding algorithm
if (padName.equalsIgnoreCase(Registry.EME_PKCS1_V1_5_PAD)) {
continue;
}
pad = PadFactory.getInstance(padName);
pad.reset();
pad.init(16);
byte[] padding = pad.pad(pt, 0, pt.length);
System.arraycopy(padding, 0, ppt, 42, padding.length);
attrib.put(IMode.STATE, new Integer(IMode.ENCRYPTION));
gnu.reset();
gnu.init(attrib);
for (int i = 0; i < ppt.length; i += 16) {
gnu.update(ppt, i, ct1, i);
}
jce = Cipher.getInstance("AES/ECB/" + padName, Registry.GNU_CRYPTO);
jce.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kb, "AES"));
jce.doFinal(pt, 0, pt.length, ct2, 0);
harness.check(Arrays.equals(ct1, ct2), "testPadding("+padName+")");
attrib.put(IMode.STATE, new Integer(IMode.DECRYPTION));
gnu.reset();
gnu.init(attrib);
byte[] pcpt = new byte[48];
for (int i = 0; i < ct1.length; i += 16) {
gnu.update(ct1, i, pcpt, i);
}
int trim = pad.unpad(pcpt, 0, pcpt.length);
System.arraycopy(pcpt, 0, cpt1, 0, pcpt.length-trim);
jce.init(Cipher.DECRYPT_MODE, new SecretKeySpec(kb, "AES"));
jce.doFinal(ct2, 0, ct2.length, cpt2, 0);
harness.check(Arrays.equals(cpt1, cpt2), "testPadding("+padName+")");
harness.check(Arrays.equals(cpt1, pt), "testPadding("+padName+")");
}
} catch (Exception x) {
harness.debug(x);
harness.fail("testPadding("+padName+"): " + String.valueOf(x));
}
}
/** Test the update() methods with incomplete blocks. */
public void testPartial(TestHarness harness) {
harness.checkPoint("testPartial");
String cipherName = null;
Cipher full, part1, part2;
IBlockCipher gnu;
byte[] pt;
byte[] kb;
byte[] ct1, ct2, ct3, ct4;
int i, blockSize;
try {
for (Iterator it = CipherFactory.getNames().iterator(); it.hasNext(); ) {
cipherName = (String) it.next();
gnu = CipherFactory.getInstance(cipherName);
full = Cipher.getInstance(cipherName, Registry.GNU_CRYPTO);
part1 = Cipher.getInstance(cipherName, Registry.GNU_CRYPTO);
part2 = Cipher.getInstance(cipherName, Registry.GNU_CRYPTO);
// pt = new byte[gnu.defaultBlockSize()];
blockSize = gnu.defaultBlockSize();
pt = new byte[2 * blockSize];
for (i = 0; i < pt.length; i++) {
pt[i] = (byte) i;
}
kb = new byte[gnu.defaultKeySize()];
for (i = 0; i < kb.length; i++) {
kb[i] = (byte) i;
}
full.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kb, cipherName));
// ct1 = full.doFinal(pt);
ct1 = full.doFinal(pt, blockSize, blockSize);
part1.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kb, cipherName));
// for (i = 0; i < pt.length - 1; i++) {
for (i = blockSize; i < pt.length - 1; i++) {
part1.update(pt, i, 1);
}
ct2 = part1.doFinal(pt, i, 1);
harness.check(Arrays.equals(ct1, ct2), "testPartial1("+cipherName+")");
// this is tricky: only the update of the last byte should return
// a full block. also, the doFinal() should return an empty byte[]
part2.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kb, cipherName));
// for (i = 0; i < pt.length - 1; i++) {
for (i = blockSize; i < pt.length - 1; i++) {
part2.update(pt, i, 1);
}
ct3 = part2.update(pt, i, 1);
harness.check(Arrays.equals(ct3, ct2), "testPartial2("+cipherName+")");
ct4 = part2.doFinal();
harness.check(ct4 != null && ct4.length == 0, "testPartial3("+cipherName+")");
}
} catch (Exception x) {
harness.debug(x);
harness.fail("testPartial("+cipherName+"): " + String.valueOf(x));
}
}
/** doFinal() with a short block and no padding should invariably fail. */
public void testDoFinal(TestHarness harness) {
harness.checkPoint("testDoFinal");
String cipherName = null;
Cipher jce;
IBlockCipher gnu;
byte[] pt;
byte[] kb;
Iterator it;
for (it = CipherFactory.getNames().iterator(); it.hasNext(); ) {
try {
cipherName = (String) it.next();
gnu = CipherFactory.getInstance(cipherName);
jce = Cipher.getInstance(cipherName, Registry.GNU_CRYPTO);
pt = new byte[gnu.defaultBlockSize() - 1];
for (int i = 0; i < pt.length; i++) {
pt[i] = (byte) i;
}
kb = new byte[gnu.defaultKeySize()];
for (int i = 0; i < kb.length; i++) {
kb[i] = (byte) i;
}
jce.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kb, cipherName));
jce.doFinal(pt);
harness.fail("testDoFinal("+cipherName+")");
} catch (IllegalBlockSizeException ibse) {
harness.check(true, "testDoFinal("+cipherName+")");
} catch (Exception x) {
harness.debug(x);
harness.fail("testDoFinal("+cipherName+"): " + String.valueOf(x));
}
}
}
// helper methods ----------------------------------------------------------
private void setUp() {
Security.addProvider(new GnuCrypto());
}
}