diff -Naurw crypto/source/test/cipher/AllTests.java crypto.1/source/test/cipher/AllTests.java
--- crypto/source/test/cipher/AllTests.java Sat Jun 8 17:09:09 2002
+++ crypto.1/source/test/cipher/AllTests.java Sun Jun 16 19:03:46 2002
@@ -73,6 +73,10 @@
result.addTest(TestOfSquare.suite());
result.addTest(TestOfTwofish.suite());
+ result.addTest(new NISTCipherTest("rijndael", 16));
+ result.addTest(new NISTCipherTest("serpent", 16));
+ result.addTest(new NISTCipherTest("twofish", 16));
+
return result;
}
diff -Naurw crypto/source/test/cipher/NISTCipherTest.java crypto.1/source/test/cipher/NISTCipherTest.java
--- crypto/source/test/cipher/NISTCipherTest.java Wed Dec 31 16:00:00 1969
+++ crypto.1/source/test/cipher/NISTCipherTest.java Mon Jun 17 15:22:39 2002
@@ -0,0 +1,310 @@
+package test.cipher;
+
+// ---------------------------------------------------------------------------
+// $Id$
+//
+// Copyright (C) 2002 The Free Software Foundation, Inc.
+//
+// This program 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.
+//
+// This program 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; if not, write to the
+//
+// Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307
+// USA
+//
+// As a special exception, if you link this library with other files to
+// produce an executable, this library does not by itself cause the
+// resulting executable to be covered by the GNU General Public License.
+// This exception does not however invalidate any other reasons why the
+// executable file might be covered by the GNU General Public License.
+//
+// ---------------------------------------------------------------------------
+
+import java.util.HashMap;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+
+import java.net.URL;
+
+import junit.framework.TestCase;
+
+import gnu.crypto.cipher.CipherFactory;
+import gnu.crypto.cipher.IBlockCipher;
+import gnu.crypto.util.Util;
+
+/**
+ * A generic cipher conformance test against NIST-style test vectors. To
+ * implement a test for a particular cipher, include the files `ecb_vt.txt',
+ * `ecb_vk.txt', `ecb_e_m.txt', `ecb_d_m.txt', `cbc_e_m.txt', and
+ * `cbc_d_m.txt' in the directory `tv/ciphername-blocksize'.
+ *
+ * That is, to test Rijndael with a 128 bit block size, put the appropriate
+ * (and appropriately named) files into the directory `tv/rijndael-128'. Then
+ * create an instance of this class with e.g.
+ *
+ * test = new NISTCipherTest("rijndael", 16);
+ *
+ *
+ * And use test
as your test case.
+ *
+ * Note that a full-conformance test will likely take a while to finish
+ * (it would have to do 48,001,152 encryptions or decryptions).
+ *
+ * References:
+ *
+ * - Known
+ * Answer Tests and Monte Carlo Tests for AES Submissions for an
+ * explanation of the tests and the format of the resulting files.
+ *
+ *
+ * @version $Revision$
+ */
+public class NISTCipherTest extends TestCase {
+
+ // Constants and variables
+ // ------------------------------------------------------------------------
+
+ // file names.
+ protected static final String ECB_VK = "ecb_vk.txt";
+ protected static final String ECB_VT = "ecb_vt.txt";
+ protected static final String ECB_E_M = "ecb_e_m.txt";
+ protected static final String ECB_D_M = "ecb_d_m.txt";
+ protected static final String CBC_E_M = "cbc_e_m.txt";
+ protected static final String CBC_D_M = "cbc_d_m.txt";
+
+ protected static final int ENCRYPTION = 0;
+ protected static final int DECRYPTION = 1;
+
+ protected IBlockCipher cipher;
+ protected HashMap attrib;
+ protected URL ecb_vk;
+ protected URL ecb_vt;
+ protected URL ecb_e_m;
+ protected URL ecb_d_m;
+ protected URL cbc_e_m;
+ protected URL cbc_d_m;
+
+ // Constructors
+ // ------------------------------------------------------------------------
+
+ public NISTCipherTest(String algorithm, int blockSize) {
+ super(algorithm);
+ cipher = CipherFactory.getInstance(algorithm);
+ attrib = new HashMap();
+ attrib.put(cipher.CIPHER_BLOCK_SIZE, new Integer(blockSize));
+ attrib.put(cipher.KEY_MATERIAL, new byte[cipher.defaultKeySize()]);
+ try {
+ cipher.init(attrib);
+ } catch (Exception e) { }
+ }
+
+ // Instance methods.
+ // ------------------------------------------------------------------------
+
+ public void setUp() {
+ String prefix = "/tv/" + cipher.name().toLowerCase() + "/";
+ ecb_vk = NISTCipherTest.class.getResource(prefix + ECB_VK);
+ ecb_vt = NISTCipherTest.class.getResource(prefix + ECB_VT);
+ ecb_e_m = NISTCipherTest.class.getResource(prefix + ECB_E_M);
+ ecb_d_m = NISTCipherTest.class.getResource(prefix + ECB_D_M);
+ cbc_e_m = NISTCipherTest.class.getResource(prefix + CBC_E_M);
+ cbc_d_m = NISTCipherTest.class.getResource(prefix + CBC_D_M);
+ }
+
+ public void runTest() {
+ String s = "Conformance(" + cipher.name() + "): ";
+ try {
+ if (ecb_vk != null) {
+ VarTest(ecb_vk.openStream());
+ }
+ if (ecb_vt != null) {
+ VarTest(ecb_vt.openStream());
+ }
+ if (ecb_e_m != null) {
+ MCTestECB(ecb_e_m.openStream(), ENCRYPTION);
+ }
+ if (ecb_d_m != null) {
+ MCTestECB(ecb_d_m.openStream(), DECRYPTION);
+ }
+ if (cbc_e_m != null) {
+ MCTestCBC(cbc_e_m.openStream(), ENCRYPTION);
+ }
+ if (cbc_d_m != null) {
+ MCTestCBC(cbc_d_m.openStream(), DECRYPTION);
+ }
+ } catch (Exception e) {
+ fail(s + e.getMessage());
+ }
+ }
+
+ // Own methods.
+ // ------------------------------------------------------------------------
+
+ /** Variable-key and variable-text test. */
+ protected void VarTest(InputStream tvIn) throws Exception {
+ LineNumberReader in = new LineNumberReader(new InputStreamReader(tvIn));
+ String line;
+ byte[] key = new byte[cipher.defaultKeySize()];
+ byte[] pt = new byte[cipher.currentBlockSize()];
+ byte[] ct = new byte[cipher.currentBlockSize()];
+ byte[] ect = new byte[cipher.currentBlockSize()];
+ while ((line = in.readLine()) != null) {
+ if (line.startsWith("KEYSIZE=")) {
+ int ks = Integer.parseInt(line.substring(line.indexOf('=')+1));
+ key = new byte[ks / 8];
+ } else if (line.startsWith("PT=")) {
+ stringToBytes(pt, line.substring(line.indexOf('=')+1));
+ } else if (line.startsWith("KEY=")) {
+ stringToBytes(key, line.substring(line.indexOf('=')+1));
+ attrib.put(cipher.KEY_MATERIAL, key);
+ } else if (line.startsWith("CT=")) {
+ stringToBytes(ect, line.substring(line.indexOf('=')+1));
+ cipher.reset();
+ cipher.init(attrib);
+ cipher.encryptBlock(pt, 0, ct, 0);
+ assertTrue(Util.areEqual(ct, ect));
+ }
+ // Other lines are ignored.
+ }
+ in.close();
+ }
+
+ /** Electronic codebook mode monte carlo test. */
+ protected void MCTestECB(InputStream tvIn, int mode) throws Exception {
+ LineNumberReader in = new LineNumberReader(new InputStreamReader(tvIn));
+ String line;
+ byte[] key = new byte[cipher.defaultKeySize()];
+ byte[] pt = new byte[cipher.currentBlockSize()];
+ byte[] ct = new byte[cipher.currentBlockSize()];
+ byte[] et = new byte[cipher.currentBlockSize()];
+ while ((line = in.readLine()) != null) {
+ if (line.startsWith("KEYSIZE=")) {
+ int ks = Integer.parseInt(line.substring(line.indexOf('=')+1));
+ key = new byte[ks / 8];
+ } else if (line.startsWith("PT=")) {
+ if (mode == DECRYPTION) {
+ stringToBytes(et, line.substring(line.indexOf('=')+1));
+ cipher.reset();
+ cipher.init(attrib);
+ for (int i = 0; i < 10000; i++) {
+ cipher.decryptBlock(ct, 0, pt, 0);
+ System.arraycopy(pt, 0, ct, 0, pt.length);
+ }
+ assertTrue(Util.areEqual(pt, et));
+ } else {
+ stringToBytes(pt, line.substring(line.indexOf('=')+1));
+ }
+ } else if (line.startsWith("KEY=")) {
+ stringToBytes(key, line.substring(line.indexOf('=')+1));
+ attrib.put(cipher.KEY_MATERIAL, key);
+ } else if (line.startsWith("CT=")) {
+ if (mode == ENCRYPTION) {
+ stringToBytes(et, line.substring(line.indexOf('=')+1));
+ cipher.reset();
+ cipher.init(attrib);
+ for (int i = 0; i < 10000; i++) {
+ cipher.encryptBlock(pt, 0, ct, 0);
+ System.arraycopy(ct, 0, pt, 0, ct.length);
+ }
+ assertTrue(Util.areEqual(ct, et));
+ } else {
+ stringToBytes(ct, line.substring(line.indexOf('=')+1));
+ }
+ }
+ // Other lines are ignored.
+ }
+ in.close();
+ }
+
+ /** Cipher block chaining mode monte carlo test. */
+ protected void MCTestCBC(InputStream tvIn, int mode) throws Exception {
+ LineNumberReader in = new LineNumberReader(new InputStreamReader(tvIn));
+ String line;
+ byte[] key = new byte[cipher.defaultKeySize()];
+ byte[] pt = new byte[cipher.currentBlockSize()];
+ byte[] ct = new byte[cipher.currentBlockSize()];
+ byte[] et = new byte[cipher.currentBlockSize()];
+ byte[] last = new byte[cipher.currentBlockSize()];
+ byte[] iv = new byte[cipher.currentBlockSize()];
+ while ((line = in.readLine()) != null) {
+ if (line.startsWith("KEYSIZE=")) {
+ int ks = Integer.parseInt(line.substring(line.indexOf('=')+1));
+ key = new byte[ks / 8];
+ if (mode == ENCRYPTION) {
+ for (int i = 0; i < ct.length; i++) {
+ ct[i] = 0;
+ }
+ } else {
+ for (int i = 0; i < pt.length; i++) {
+ pt[i] = 0;
+ }
+ }
+ } else if (line.startsWith("PT=")) {
+ if (mode == DECRYPTION) {
+ stringToBytes(et, line.substring(line.indexOf('=')+1));
+ cipher.reset();
+ cipher.init(attrib);
+ for (int i = 0; i < 10000; i++) {
+ cipher.decryptBlock(ct, 0, pt, 0);
+ for (int j = 0; j < pt.length; j++) {
+ pt[j] ^= iv[j];
+ }
+ System.arraycopy(ct, 0, iv, 0, ct.length);
+ System.arraycopy(pt, 0, ct, 0, pt.length);
+ }
+ assertTrue(Util.areEqual(pt, et));
+ } else {
+ stringToBytes(pt, line.substring(line.indexOf('=')+1));
+ }
+ } else if (line.startsWith("KEY=")) {
+ stringToBytes(key, line.substring(line.indexOf('=')+1));
+ attrib.put(cipher.KEY_MATERIAL, key);
+ } else if (line.startsWith("CT=")) {
+ if (mode == ENCRYPTION) {
+ stringToBytes(et, line.substring(line.indexOf('=')+1));
+ cipher.reset();
+ cipher.init(attrib);
+ for (int i = 0; i < 10000; i++) {
+ for (int j = 0; j < pt.length; j++) {
+ pt[j] ^= iv[j];
+ }
+ System.arraycopy(ct, 0, last, 0, ct.length);
+ cipher.encryptBlock(pt, 0, ct, 0);
+ System.arraycopy(ct, 0, iv, 0, ct.length);
+ System.arraycopy(last, 0, pt, 0, last.length);
+ }
+ assertTrue(Util.areEqual(ct, et));
+ } else {
+ stringToBytes(ct, line.substring(line.indexOf('=')+1));
+ }
+ } else if (line.startsWith("IV=")) {
+ stringToBytes(iv, line.substring(line.indexOf('=')+1));
+ }
+ // Other lines are ignored.
+ }
+ in.close();
+ }
+
+ // Class methods.
+ // -----------------------------------------------------------------------
+
+ /** Convert a hexadecimal string to a byte array. */
+ protected static void stringToBytes(byte[] buf, String str) {
+ for (int i = 0; i < buf.length && i * 2 < str.length(); i++) {
+ buf[i] = (byte) Integer.parseInt(str.substring(i*2, i*2+2), 16);
+ }
+ }
+}