From 2e3fd0abf2e1030dea9f8aa3c82967d771c01caf Mon Sep 17 00:00:00 2001
From: jrandom <jrandom>
Date: Thu, 8 Apr 2004 04:54:58 +0000
Subject: [PATCH] great renaming (cont.)

---
 .../java/test/net/i2p/crypto/AES256Bench.java | 138 ++++++
 core/java/test/net/i2p/crypto/DSABench.java   |  92 ++++
 .../net/i2p/crypto/ElGamalAESEngineTest.java  | 235 ++++++++++
 .../test/net/i2p/crypto/ElGamalBench.java     |  96 +++++
 .../java/test/net/i2p/crypto/SHA256Bench.java |  99 +++++
 .../net/i2p/crypto/SessionEncryptionTest.java | 406 ++++++++++++++++++
 .../test/net/i2p/data/AbuseReasonTest.java    |  30 ++
 .../test/net/i2p/data/AbuseSeverityTest.java  |  30 ++
 core/java/test/net/i2p/data/BooleanTest.java  |  59 +++
 .../test/net/i2p/data/CertificateTest.java    |  34 ++
 .../i2p/data/CreateLeaseSetMessageTest.java   |  37 ++
 .../i2p/data/CreateSessionMessageTest.java    |  31 ++
 core/java/test/net/i2p/data/DateTest.java     |  60 +++
 .../test/net/i2p/data/DestinationTest.java    |  38 ++
 .../i2p/data/DestroySessionMessageTest.java   |  31 ++
 .../net/i2p/data/DisconnectMessageTest.java   |  30 ++
 core/java/test/net/i2p/data/HashTest.java     |  33 ++
 core/java/test/net/i2p/data/LeaseSetTest.java |  38 ++
 core/java/test/net/i2p/data/LeaseTest.java    |  41 ++
 core/java/test/net/i2p/data/MappingTest.java  |  64 +++
 .../java/test/net/i2p/data/MessageIdTest.java |  30 ++
 .../i2p/data/MessagePayloadMessageTest.java   |  56 +++
 .../i2p/data/MessageStatusMessageTest.java    |  35 ++
 core/java/test/net/i2p/data/PayloadTest.java  |  65 +++
 .../test/net/i2p/data/PrivateKeyTest.java     |  33 ++
 .../java/test/net/i2p/data/PublicKeyTest.java |  33 ++
 .../data/ReceiveMessageBeginMessageTest.java  |  33 ++
 .../data/ReceiveMessageEndMessageTest.java    |  33 ++
 .../net/i2p/data/ReportAbuseMessageTest.java  |  37 ++
 .../i2p/data/RequestLeaseSetMessageTest.java  |  38 ++
 .../test/net/i2p/data/RouterAddressTest.java  |  42 ++
 .../test/net/i2p/data/RouterIdentityTest.java |  38 ++
 .../test/net/i2p/data/RouterInfoTest.java     |  85 ++++
 .../net/i2p/data/SendMessageMessageTest.java  |  56 +++
 .../test/net/i2p/data/SessionConfigTest.java  |  42 ++
 .../java/test/net/i2p/data/SessionIdTest.java |  30 ++
 .../test/net/i2p/data/SessionKeyTest.java     |  33 ++
 .../i2p/data/SessionStatusMessageTest.java    |  32 ++
 .../java/test/net/i2p/data/SignatureTest.java |  33 ++
 .../net/i2p/data/SigningPrivateKeyTest.java   |  33 ++
 .../net/i2p/data/SigningPublicKeyTest.java    |  33 ++
 core/java/test/net/i2p/data/StringTest.java   |  59 +++
 .../java/test/net/i2p/data/StructureTest.java |  60 +++
 core/java/test/net/i2p/data/TestData.java     | 150 +++++++
 .../test/net/i2p/data/TestDataGenerator.java  |  16 +
 .../test/net/i2p/data/TestDataPrinter.java    |  18 +
 core/java/test/net/i2p/data/TunnelIdTest.java |  30 ++
 .../net/i2p/data/UnsignedIntegerTest.java     |  59 +++
 48 files changed, 2864 insertions(+)
 create mode 100644 core/java/test/net/i2p/crypto/AES256Bench.java
 create mode 100644 core/java/test/net/i2p/crypto/DSABench.java
 create mode 100644 core/java/test/net/i2p/crypto/ElGamalAESEngineTest.java
 create mode 100644 core/java/test/net/i2p/crypto/ElGamalBench.java
 create mode 100644 core/java/test/net/i2p/crypto/SHA256Bench.java
 create mode 100644 core/java/test/net/i2p/crypto/SessionEncryptionTest.java
 create mode 100644 core/java/test/net/i2p/data/AbuseReasonTest.java
 create mode 100644 core/java/test/net/i2p/data/AbuseSeverityTest.java
 create mode 100644 core/java/test/net/i2p/data/BooleanTest.java
 create mode 100644 core/java/test/net/i2p/data/CertificateTest.java
 create mode 100644 core/java/test/net/i2p/data/CreateLeaseSetMessageTest.java
 create mode 100644 core/java/test/net/i2p/data/CreateSessionMessageTest.java
 create mode 100644 core/java/test/net/i2p/data/DateTest.java
 create mode 100644 core/java/test/net/i2p/data/DestinationTest.java
 create mode 100644 core/java/test/net/i2p/data/DestroySessionMessageTest.java
 create mode 100644 core/java/test/net/i2p/data/DisconnectMessageTest.java
 create mode 100644 core/java/test/net/i2p/data/HashTest.java
 create mode 100644 core/java/test/net/i2p/data/LeaseSetTest.java
 create mode 100644 core/java/test/net/i2p/data/LeaseTest.java
 create mode 100644 core/java/test/net/i2p/data/MappingTest.java
 create mode 100644 core/java/test/net/i2p/data/MessageIdTest.java
 create mode 100644 core/java/test/net/i2p/data/MessagePayloadMessageTest.java
 create mode 100644 core/java/test/net/i2p/data/MessageStatusMessageTest.java
 create mode 100644 core/java/test/net/i2p/data/PayloadTest.java
 create mode 100644 core/java/test/net/i2p/data/PrivateKeyTest.java
 create mode 100644 core/java/test/net/i2p/data/PublicKeyTest.java
 create mode 100644 core/java/test/net/i2p/data/ReceiveMessageBeginMessageTest.java
 create mode 100644 core/java/test/net/i2p/data/ReceiveMessageEndMessageTest.java
 create mode 100644 core/java/test/net/i2p/data/ReportAbuseMessageTest.java
 create mode 100644 core/java/test/net/i2p/data/RequestLeaseSetMessageTest.java
 create mode 100644 core/java/test/net/i2p/data/RouterAddressTest.java
 create mode 100644 core/java/test/net/i2p/data/RouterIdentityTest.java
 create mode 100644 core/java/test/net/i2p/data/RouterInfoTest.java
 create mode 100644 core/java/test/net/i2p/data/SendMessageMessageTest.java
 create mode 100644 core/java/test/net/i2p/data/SessionConfigTest.java
 create mode 100644 core/java/test/net/i2p/data/SessionIdTest.java
 create mode 100644 core/java/test/net/i2p/data/SessionKeyTest.java
 create mode 100644 core/java/test/net/i2p/data/SessionStatusMessageTest.java
 create mode 100644 core/java/test/net/i2p/data/SignatureTest.java
 create mode 100644 core/java/test/net/i2p/data/SigningPrivateKeyTest.java
 create mode 100644 core/java/test/net/i2p/data/SigningPublicKeyTest.java
 create mode 100644 core/java/test/net/i2p/data/StringTest.java
 create mode 100644 core/java/test/net/i2p/data/StructureTest.java
 create mode 100644 core/java/test/net/i2p/data/TestData.java
 create mode 100644 core/java/test/net/i2p/data/TestDataGenerator.java
 create mode 100644 core/java/test/net/i2p/data/TestDataPrinter.java
 create mode 100644 core/java/test/net/i2p/data/TunnelIdTest.java
 create mode 100644 core/java/test/net/i2p/data/UnsignedIntegerTest.java

diff --git a/core/java/test/net/i2p/crypto/AES256Bench.java b/core/java/test/net/i2p/crypto/AES256Bench.java
new file mode 100644
index 0000000000..05718e4f26
--- /dev/null
+++ b/core/java/test/net/i2p/crypto/AES256Bench.java
@@ -0,0 +1,138 @@
+package net.i2p.crypto;
+
+/* 
+ * Copyright (c) 2003, TheCrypto
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * - Redistributions of source code must retain the above copyright notice, this 
+ *   list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice, 
+ *   this list of conditions and the following disclaimer in the documentation 
+ *   and/or other materials provided with the distribution.
+ * -  Neither the name of the TheCrypto may be used to endorse or promote 
+ *    products derived from this software without specific prior written 
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import net.i2p.data.SessionKey;
+import net.i2p.data.DataHelper;
+
+public class AES256Bench {
+
+    public static void main(String args[]) {
+	char[] cplain = {
+	    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+	    0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+	    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+	    0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+	};
+	
+	byte[] plain = new byte[cplain.length];
+	for (int x = 0; x < cplain.length; x++) {
+	    plain[x] = (byte)cplain[x];
+	}
+	char[] ckey = {
+	    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
+	    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+	    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
+	};
+	byte[] bkey = new byte[ckey.length];
+	for (int x = 0; x < ckey.length; x++) {
+	    bkey[x] = (byte)ckey[x];
+	}
+	
+	SessionKey key = new SessionKey();
+	key.setData(bkey);
+	
+	char[] civ = {
+	    0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+	    0xfe, 0xdc, 0xba, 0x98, 0x67, 0x54, 0x32, 0x10
+	};
+	
+	byte[] iv = new byte[civ.length];
+	for (int x = 0; x < iv.length; x++) {
+	    iv[x] = (byte)civ[x];
+	}
+	
+	byte[] e = AESEngine.getInstance().encrypt(plain, key, iv);
+	byte[] d = AESEngine.getInstance().decrypt(e, key, iv);
+	boolean same = true;
+	for (int x = 0; x < d.length; x++) {
+	    if (plain[x] != d[x]) {
+		same = false;
+	    }
+	}
+	
+	System.out.println("Standard test D(E(value)) == value? " + same);
+		
+	plain = "1234567890123456".getBytes();
+	e = AESEngine.getInstance().encrypt(plain, key, iv);
+	d = AESEngine.getInstance().decrypt(e, key, iv);
+	same = DataHelper.eq(plain, d);
+	System.out.println("Different value test D(E(value)) == value? " + same);
+		
+	System.out.println();
+	System.out.println();
+		
+	long times = 100;
+	long encrypttime = 0;
+	long decrypttime = 0;
+	long maxE = 0;
+	long minE = 0;
+	long maxD = 0;
+	long minD = 0;
+	byte[] message = new byte[2*1024];
+	for (int i = 0; i < message.length; i++)
+	    message[i] = (byte)((i%26)+'a');
+	for (int x = 0; x < times; x++) {	
+	    long startencrypt = System.currentTimeMillis();
+	    e = AESEngine.getInstance().encrypt(message, key, iv);
+	    long endencryptstartdecrypt = System.currentTimeMillis();
+	    d = AESEngine.getInstance().decrypt(e, key, iv);
+	    long enddecrypt = System.currentTimeMillis();
+	    System.out.print(".");
+	    encrypttime += endencryptstartdecrypt - startencrypt;
+	    decrypttime += enddecrypt - endencryptstartdecrypt;
+	    if (!DataHelper.eq(d, message)) {
+		System.out.println("Lengths: source [" + message.length + "] dest [" + d.length + "]");
+		System.out.println("Data: dest [" + DataHelper.toString(d, d.length) + "]");
+		throw new RuntimeException("Holy crap, decrypted != source message");
+	    }
+	
+	    if ( (minE == 0) && (minD == 0) ) {
+		minE = endencryptstartdecrypt - startencrypt;
+		maxE = endencryptstartdecrypt - startencrypt;
+		minD = enddecrypt - endencryptstartdecrypt;
+		maxD = enddecrypt - endencryptstartdecrypt;
+	    } else {
+		if (minE > endencryptstartdecrypt - startencrypt) minE = endencryptstartdecrypt - startencrypt;
+		if (maxE < endencryptstartdecrypt - startencrypt) maxE = endencryptstartdecrypt - startencrypt;
+		if (minD > enddecrypt - endencryptstartdecrypt) minD = enddecrypt - endencryptstartdecrypt;
+		if (maxD < enddecrypt - endencryptstartdecrypt) maxD = enddecrypt - endencryptstartdecrypt;
+	    }
+	
+	}
+	
+	System.out.println();
+	System.out.println("Data size                  : " + message.length);
+	System.out.println("Encryption Time Average    : " + (encrypttime/times) + "ms\ttotal: " + encrypttime + "ms\tmin: " + minE + "ms\tmax: " + maxE + "ms\tEncryption Bps: " + (times*message.length*1000)/encrypttime);
+	System.out.println("Decryption Time Average    : " + (decrypttime/times) + "ms\ttotal: " + decrypttime + "ms\tmin: " + minD + "ms\tmax: " + maxD + "ms\tDecryption Bps: " + (times*message.length*1000)/decrypttime);
+    }
+}
+	
diff --git a/core/java/test/net/i2p/crypto/DSABench.java b/core/java/test/net/i2p/crypto/DSABench.java
new file mode 100644
index 0000000000..5d29e1d073
--- /dev/null
+++ b/core/java/test/net/i2p/crypto/DSABench.java
@@ -0,0 +1,92 @@
+package net.i2p.crypto;
+
+/* 
+ * Copyright (c) 2003, TheCrypto
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * - Redistributions of source code must retain the above copyright notice, this 
+ *   list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice, 
+ *   this list of conditions and the following disclaimer in the documentation 
+ *   and/or other materials provided with the distribution.
+ * -  Neither the name of the TheCrypto may be used to endorse or promote 
+ *    products derived from this software without specific prior written 
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import net.i2p.data.Signature;
+import net.i2p.data.SigningPrivateKey;
+import net.i2p.data.SigningPublicKey;
+
+public class DSABench {
+	public static void main(String args[]) {
+		int times = 100;
+		long keygentime = 0;
+		long signtime = 0;
+		long verifytime = 0;
+		long maxKey = 0;
+		long minKey = 0;
+		long maxS = 0;
+		long minS = 0;
+		long maxV = 0;
+		long minV = 0;
+		Object[] keys = KeyGenerator.getInstance().generateSigningKeypair();
+		byte[] message = new byte[32+32];
+		for (int i = 0; i < message.length; i++)
+		    message[i] = (byte)((i%26)+'a');
+		for (int x = 0; x < times; x++) {	
+			long startkeys = System.currentTimeMillis();
+			keys = KeyGenerator.getInstance().generateSigningKeypair();
+			SigningPublicKey pubkey = (SigningPublicKey)keys[0];
+			SigningPrivateKey privkey = (SigningPrivateKey)keys[1];
+			long endkeys = System.currentTimeMillis();
+			long startsign = System.currentTimeMillis();
+			Signature s = DSAEngine.getInstance().sign(message, privkey);
+			long endsignstartverify = System.currentTimeMillis();
+			boolean v = DSAEngine.getInstance().verifySignature(s, message, pubkey);
+			long endverify = System.currentTimeMillis();
+			System.out.print(".");
+			keygentime += endkeys - startkeys;
+			signtime += endsignstartverify - startsign;
+			verifytime += endverify - endsignstartverify;
+			if (!v) {
+			    throw new RuntimeException("Holy crap, did not verify");
+			}
+			if ( (minKey == 0) && (minS == 0) && (minV == 0) ) {
+			    minKey = endkeys - startkeys;
+			    maxKey = endkeys - startkeys;
+			    minS = endsignstartverify - startsign;
+			    maxS = endsignstartverify - startsign;
+			    minV = endverify - endsignstartverify;
+			    maxV = endverify - endsignstartverify;
+			} else {
+			    if (minKey > endkeys - startkeys) minKey = endkeys - startkeys;
+			    if (maxKey < endkeys - startkeys) maxKey = endkeys - startkeys;
+			    if (minS > endsignstartverify - startsign) minS = endsignstartverify - startsign;
+			    if (maxS < endsignstartverify - startsign) maxS = endsignstartverify - startsign;
+			    if (minV > endverify - endsignstartverify) minV = endverify - endsignstartverify;
+			    if (maxV < endverify - endsignstartverify) maxV = endverify - endsignstartverify;
+			}
+		}
+		System.out.println();
+		System.out.println("Key Generation Time Average: " + (keygentime/times) + "\ttotal: " + keygentime + "\tmin: " + minKey + "\tmax: " + maxKey  + "\tKeygen/second: " + (keygentime == 0 ? "NaN" : ""+(times*1000)/keygentime));
+		System.out.println("Signing Time Average       : " + (signtime/times) + "\ttotal: " + signtime + "\tmin: " + minS + "\tmax: " + maxS + "\tSigning Bps: " + (times*message.length*1000)/signtime);
+		System.out.println("Verification Time Average  : " + (verifytime/times) + "\ttotal: " + verifytime + "\tmin: " + minV + "\tmax: " + maxV + "\tDecryption Bps: " + (times*message.length*1000)/verifytime);
+	}
+}
+	
diff --git a/core/java/test/net/i2p/crypto/ElGamalAESEngineTest.java b/core/java/test/net/i2p/crypto/ElGamalAESEngineTest.java
new file mode 100644
index 0000000000..afe2548aa4
--- /dev/null
+++ b/core/java/test/net/i2p/crypto/ElGamalAESEngineTest.java
@@ -0,0 +1,235 @@
+package net.i2p.crypto;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't  make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.Hash;
+import net.i2p.data.SessionKey;
+import net.i2p.data.PublicKey;
+import net.i2p.data.PrivateKey;
+import net.i2p.data.DataHelper;
+import net.i2p.util.RandomSource;
+import net.i2p.util.Log;
+import net.i2p.util.Clock;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import java.util.Set;
+import java.util.HashSet;
+
+class ElGamalAESEngineTest {
+    private final static Log _log = new Log(ElGamalAESEngineTest.class);
+    public void runRoundtripTest() {
+	try {
+	    Object keys[] = KeyGenerator.getInstance().generatePKIKeypair();
+	    PublicKey pubKey = (PublicKey)keys[0];
+	    PrivateKey privKey = (PrivateKey)keys[1];
+
+	    String msg = "Hello world";
+	    Set toBeDelivered = new HashSet();
+	    SessionKey key = SessionKeyManager.getInstance().getCurrentKey(pubKey);
+	    if (key == null)
+		key = SessionKeyManager.getInstance().createSession(pubKey);
+	    byte[] encrypted = ElGamalAESEngine.encrypt(msg.getBytes(), pubKey, key, 64);
+	    byte[] decrypted = ElGamalAESEngine.decrypt(encrypted, privKey);
+	    if (decrypted == null)
+		throw new Exception("Failed to decrypt");
+	    String read = new String(decrypted);
+	    _log.debug("read: " + read);
+	    _log.debug("Match? " + msg.equals(read));
+	} catch (Exception e) {
+	    _log.error("Error", e);
+	    try { Thread.sleep(5000); } catch (InterruptedException ie) {}
+	    System.exit(0);
+	}
+    }
+
+    public void runLoopTest(int runs) {
+	try {
+	    Object keys[] = KeyGenerator.getInstance().generatePKIKeypair();
+	    PublicKey pubKey = (PublicKey)keys[0];
+	    PrivateKey privKey = (PrivateKey)keys[1];
+
+	    long e0 = 0;
+	    long d0 = 0;
+	    long eTot = 0;
+	    long dTot = 0;
+	    for (int i = 0; i < runs; i++) {
+		long times[] = runMessage(pubKey, privKey);
+		_log.debug("E[" + i + "] time: " + times[0] + "ms");
+		_log.debug("D["+i+"] time: " + times[1] + "ms");
+		if (i == 0) {
+		    e0 = times[0];
+		    d0 = times[1];
+		}
+		eTot += times[0];
+		dTot += times[1];
+	    }
+	    _log.debug("E average time: " + eTot/runs + "ms");
+	    _log.debug("D average time: " + dTot/runs + "ms");
+	    _log.debug("Total time to send and receive " + (runs) + "Kb: " + (eTot+dTot)+"ms");
+		
+	} catch (Exception e) {
+	    _log.error("Error", e);
+	    try { Thread.sleep(5000); } catch (InterruptedException ie) {}
+	    System.exit(0);
+	}
+    }
+    
+    private long[] runMessage(PublicKey pubKey, PrivateKey privKey) throws Exception {
+	byte[] msg = new byte[400];
+	RandomSource.getInstance().nextBytes(msg);
+	SessionKey key = SessionKeyManager.getInstance().getCurrentKey(pubKey);
+	if (key == null)
+	    key = SessionKeyManager.getInstance().createSession(pubKey);
+	    
+	long beforeE = Clock.getInstance().now();
+	byte[] encrypted = ElGamalAESEngine.encrypt(msg, pubKey, key, 1024);
+	long afterE = Clock.getInstance().now();
+	byte[] decrypted = ElGamalAESEngine.decrypt(encrypted, privKey);
+	long afterD = Clock.getInstance().now();
+	if (!DataHelper.eq(msg, decrypted)) {
+	    _log.error("WTF, D(E(val)) != val");
+	    return null;
+	}
+
+	long rv[] = new long[2];
+	rv[0] = afterE - beforeE;
+	rv[1] = afterD - afterE;
+	return rv;
+    }
+    
+    public void runAESTest() {
+	try {
+	    SessionKey sessionKey = KeyGenerator.getInstance().generateSessionKey();
+	    Hash h = SHA256Generator.getInstance().calculateHash(sessionKey.getData());
+	    byte iv[] = new byte[16];
+	    System.arraycopy(h.getData(), 0, iv, 0, 16);
+	    
+	    String msg = "Hello world";
+	    
+	    byte encrypted[] = ElGamalAESEngine.encryptAESBlock(msg.getBytes(), sessionKey, iv, null, null, 64);
+	    _log.debug("** Encryption complete.  Beginning decryption");
+	    Set foundTags = new HashSet();
+	    SessionKey foundKey = new SessionKey();
+	    byte decrypted[] = ElGamalAESEngine.decryptAESBlock(encrypted, sessionKey, iv, null, foundTags, foundKey);
+	    if (decrypted == null) throw new Exception("Decryption failed");
+	    String read = new String(decrypted);
+	    _log.debug("read: " + read);
+	    _log.debug("Match? " + msg.equals(read));
+	} catch (Exception e) {
+	    _log.error("Error", e);
+	    try { Thread.sleep(5000); } catch (InterruptedException ie) {}
+	    System.exit(0);
+	}
+    }
+    
+    public void runBasicAESTest() {
+	try {
+	    SessionKey sessionKey = KeyGenerator.getInstance().generateSessionKey();
+	    Hash h = SHA256Generator.getInstance().calculateHash(sessionKey.getData());
+	    byte iv[] = new byte[16];
+	    System.arraycopy(h.getData(), 0, iv, 0, 16);
+	    
+	    String msg = "Hello world01234012345678901234501234567890123450123456789012345";
+	    h = SHA256Generator.getInstance().calculateHash(msg.getBytes());
+	    _log.debug("Hash of entire aes block before encryption: \n" + DataHelper.toString(h.getData(), 32));
+	    byte aesEncr[] = AESEngine.getInstance().encrypt(msg.getBytes(), sessionKey, iv);
+	    byte aesDecr[] = AESEngine.getInstance().decrypt(aesEncr, sessionKey, iv);
+	    h = SHA256Generator.getInstance().calculateHash(aesDecr);
+	    _log.debug("Hash of entire aes block after decryption: \n" + DataHelper.toString(h.getData(), 32));
+	    if (msg.equals(new String(aesDecr))) {
+		_log.debug("**AES Basic test passed!\n\n");
+	    }
+	} catch (Exception e) {
+	    _log.error("Error", e);
+	    try { Thread.sleep(5000); } catch (InterruptedException ie) {}
+	    System.exit(0);
+	}
+    }
+    
+    public void runElGamalTest(int numLoops) {
+	
+	for (int i = 0; i < numLoops; i++) {
+	    Object keys[] = KeyGenerator.getInstance().generatePKIKeypair();
+	    PublicKey pubKey = (PublicKey)keys[0];
+	    PrivateKey privKey = (PrivateKey)keys[1];
+	    SessionKey key = KeyGenerator.getInstance().generateSessionKey();
+	
+	    runBasicElGamalTest(key, pubKey, privKey);
+	}
+    }
+    
+    public void runBasicElGamalTest(SessionKey key, PublicKey pubKey, PrivateKey privKey) {
+	try {
+	    ByteArrayOutputStream elgSrc = new ByteArrayOutputStream(256);
+	    key.writeBytes(elgSrc);
+	    byte preIV[] = new byte[32];
+	    RandomSource.getInstance().nextBytes(preIV);
+	    elgSrc.write(preIV);
+//	    byte rnd[] = new byte[191];
+//	    RandomSource.getInstance().nextBytes(rnd);
+//	    elgSrc.write(rnd);
+	    elgSrc.flush();
+	    
+	    byte elgEncr[] = ElGamalEngine.getInstance().encrypt(elgSrc.toByteArray(), pubKey);
+	    byte elgDecr[] = ElGamalEngine.getInstance().decrypt(elgEncr, privKey);
+	
+	    ByteArrayInputStream bais = new ByteArrayInputStream(elgDecr);
+	    SessionKey nk = new SessionKey();
+	
+	    nk.readBytes(bais);
+	    byte postpreIV[] = new byte[32];
+	    int read = bais.read(postpreIV);
+	    if (read != postpreIV.length) {
+		// hmm, this can't really happen...
+		throw new Exception("Somehow ElGamal broke and 256 bytes is less than 32 bytes...");
+	    }
+	    // ignore the next 192 bytes
+	    boolean eq = (DataHelper.eq(preIV, postpreIV) && DataHelper.eq(key, nk));
+	    if (!eq) {
+		_log.error("elgEncr.length: " + elgEncr.length + " elgDecr.length: " + elgDecr.length);
+		_log.error("Pre IV.................: " + DataHelper.toString(preIV, 32));
+		_log.error("Pre IV after decryption: " + DataHelper.toString(postpreIV, 32));
+		_log.error("SessionKey.................: " + DataHelper.toString(key.getData(), 32));
+		_log.error("SessionKey after decryption: " + DataHelper.toString(nk.getData(), 32));
+		_log.error("PublicKey: " + DataHelper.toDecimalString(pubKey.getData(), pubKey.getData().length));
+		_log.error("PrivateKey: " + DataHelper.toDecimalString(privKey.getData(), privKey.getData().length));
+	    
+		throw new Exception("Not equal!");
+	    } else {
+		_log.debug("Basic ElG D(E(val)) == val");
+	    }
+	    
+	} catch (Exception e) {
+	    _log.error("Error", e);
+	    try { Thread.sleep(5000); } catch (InterruptedException ie) {}
+	    System.exit(0);
+	}
+    }
+    
+    public static void main(String args[]) {
+	ElGamalAESEngineTest tst = new ElGamalAESEngineTest();
+	Object o = YKGenerator.class;
+	try { Thread.sleep(120*1000); } catch (InterruptedException ie) {}
+	
+	tst.runBasicAESTest();
+	tst.runAESTest();
+	tst.runRoundtripTest();
+	tst.runElGamalTest(2);
+	// test bug
+	for (int i = 0; i < 3; i++)
+	    tst.runLoopTest(1);
+	// test throughput
+	tst.runLoopTest(5);
+	
+	net.i2p.stat.SimpleStatDumper.dumpStats(Log.CRIT);
+	try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
+    }
+}
diff --git a/core/java/test/net/i2p/crypto/ElGamalBench.java b/core/java/test/net/i2p/crypto/ElGamalBench.java
new file mode 100644
index 0000000000..64ca0aa845
--- /dev/null
+++ b/core/java/test/net/i2p/crypto/ElGamalBench.java
@@ -0,0 +1,96 @@
+package net.i2p.crypto;
+
+/* 
+ * Copyright (c) 2003, TheCrypto
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * - Redistributions of source code must retain the above copyright notice, this 
+ *   list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice, 
+ *   this list of conditions and the following disclaimer in the documentation 
+ *   and/or other materials provided with the distribution.
+ * -  Neither the name of the TheCrypto may be used to endorse or promote 
+ *    products derived from this software without specific prior written 
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import net.i2p.data.DataHelper;
+import net.i2p.data.PrivateKey;
+import net.i2p.data.PublicKey;
+
+public class ElGamalBench {
+	public static void main(String args[]) {
+		int times = 100;
+		long keygentime = 0;
+		long encrypttime = 0;
+		long decrypttime = 0;
+		long maxKey = 0;
+		long minKey = 0;
+		long maxE = 0;
+		long minE = 0;
+		long maxD = 0;
+		long minD = 0;
+		Object[] keys = KeyGenerator.getInstance().generatePKIKeypair();
+		byte[] message = new byte[222];
+		for (int i = 0; i < message.length; i++)
+		    message[i] = (byte)((i%26)+'a');
+		for (int x = 0; x < times; x++) {	
+			long startkeys = System.currentTimeMillis();
+			keys = KeyGenerator.getInstance().generatePKIKeypair();
+			PublicKey pubkey = (PublicKey)keys[0];
+			PrivateKey privkey = (PrivateKey)keys[1];
+			long endkeys = System.currentTimeMillis();
+			long startencrypt = System.currentTimeMillis();
+			byte[] e = ElGamalEngine.getInstance().encrypt(message, pubkey);
+			long endencryptstartdecrypt = System.currentTimeMillis();
+			byte[] d = ElGamalEngine.getInstance().decrypt(e, privkey);
+			long enddecrypt = System.currentTimeMillis();
+			System.out.print(".");
+			keygentime += endkeys - startkeys;
+			encrypttime += endencryptstartdecrypt - startencrypt;
+			decrypttime += enddecrypt - endencryptstartdecrypt;
+			if (!DataHelper.eq(d, message)) {
+			    System.out.println("Lengths: source [" + message.length + "] dest [" + d.length + "]");
+			    byte hash1[] = SHA256Generator.getInstance().calculateHash(message).getData();
+			    byte hash2[] = SHA256Generator.getInstance().calculateHash(d).getData();
+			    System.out.println("Hashes: source [" + DataHelper.toString(hash1, hash1.length) + "] dest [" + DataHelper.toString(hash2, hash2.length) + "]");
+			    throw new RuntimeException("Holy crap, decrypted != source message");
+			}
+			if ( (minKey == 0) && (minE == 0) && (minD == 0) ) {
+			    minKey = endkeys - startkeys;
+			    maxKey = endkeys - startkeys;
+			    minE = endencryptstartdecrypt - startencrypt;
+			    maxE = endencryptstartdecrypt - startencrypt;
+			    minD = enddecrypt - endencryptstartdecrypt;
+			    maxD = enddecrypt - endencryptstartdecrypt;
+			} else {
+			    if (minKey > endkeys - startkeys) minKey = endkeys - startkeys;
+			    if (maxKey < endkeys - startkeys) maxKey = endkeys - startkeys;
+			    if (minE > endencryptstartdecrypt - startencrypt) minE = endencryptstartdecrypt - startencrypt;
+			    if (maxE < endencryptstartdecrypt - startencrypt) maxE = endencryptstartdecrypt - startencrypt;
+			    if (minD > enddecrypt - endencryptstartdecrypt) minD = enddecrypt - endencryptstartdecrypt;
+			    if (maxD < enddecrypt - endencryptstartdecrypt) maxD = enddecrypt - endencryptstartdecrypt;
+			}
+		}
+		System.out.println();
+		System.out.println("Key Generation Time Average: " + (keygentime/times) + "\ttotal: " + keygentime + "\tmin: " + minKey + "\tmax: " + maxKey  + "\tKeygen/second: " + (keygentime == 0 ? "NaN" : ""+(times*1000)/keygentime));
+		System.out.println("Encryption Time Average    : " + (encrypttime/times) + "\ttotal: " + encrypttime + "\tmin: " + minE + "\tmax: " + maxE + "\tEncryption Bps: " + (times*message.length*1000)/encrypttime);
+		System.out.println("Decryption Time Average    : " + (decrypttime/times) + "\ttotal: " + decrypttime + "\tmin: " + minD + "\tmax: " + maxD + "\tDecryption Bps: " + (times*message.length*1000)/decrypttime);
+	}
+}
+	
diff --git a/core/java/test/net/i2p/crypto/SHA256Bench.java b/core/java/test/net/i2p/crypto/SHA256Bench.java
new file mode 100644
index 0000000000..05527a0e22
--- /dev/null
+++ b/core/java/test/net/i2p/crypto/SHA256Bench.java
@@ -0,0 +1,99 @@
+package net.i2p.crypto;
+
+/* 
+ * Copyright (c) 2003, TheCrypto
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * - Redistributions of source code must retain the above copyright notice, this 
+ *   list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice, 
+ *   this list of conditions and the following disclaimer in the documentation 
+ *   and/or other materials provided with the distribution.
+ * -  Neither the name of the TheCrypto may be used to endorse or promote 
+ *    products derived from this software without specific prior written 
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import net.i2p.data.Hash;
+
+public class SHA256Bench {
+	public static void main(String args[]) {
+		Hash asdfs = SHA256Generator.getInstance().calculateHash("qwerty".getBytes());
+			
+		int times = 100;
+		long shorttime = 0;
+		long medtime = 0;
+		long longtime = 0;
+		long minShort = 0;
+		long maxShort = 0;
+		long minMed = 0;
+		long maxMed = 0;
+		long minLong = 0;
+		long maxLong = 0;
+		byte[] smess = new String("abc").getBytes();
+		StringBuffer buf = new StringBuffer();
+		for (int x = 0; x < 10*1024; x++) {
+			buf.append("a");
+		}
+		byte[] mmess = buf.toString().getBytes(); // new String("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq").getBytes();
+		buf = new StringBuffer();
+		for (int x = 0; x < 1000000; x++) {
+			buf.append("a");
+		}
+		byte[] lmess = buf.toString().getBytes();
+		// warm up the engines
+		SHA256Generator.getInstance().calculateHash(smess);
+		SHA256Generator.getInstance().calculateHash(mmess);
+		SHA256Generator.getInstance().calculateHash(lmess);
+		// now do it
+		for (int x = 0; x < times; x++) {	
+			long startshort = System.currentTimeMillis();
+			Hash s = SHA256Generator.getInstance().calculateHash(smess);
+			long endshortstartmed = System.currentTimeMillis();
+			Hash m = SHA256Generator.getInstance().calculateHash(mmess);
+			long endmedstartlong = System.currentTimeMillis();
+			Hash l = SHA256Generator.getInstance().calculateHash(lmess);
+			long endlong = System.currentTimeMillis();
+			System.out.print(".");
+			shorttime += endshortstartmed - startshort;
+			medtime += endmedstartlong - endshortstartmed;
+			longtime += endlong - endmedstartlong;
+			
+			if ((minShort == 0) && (minMed == 0) && (minLong == 0) ) {
+			    minShort = endshortstartmed - startshort;
+			    maxShort = endshortstartmed - startshort;
+			    minMed = endmedstartlong - endshortstartmed;
+			    maxMed = endmedstartlong - endshortstartmed;
+			    minLong = endlong - endmedstartlong;
+			    maxLong = endlong - endmedstartlong;
+			} else {
+			    if (minShort > endshortstartmed - startshort) minShort = endshortstartmed - startshort;
+			    if (maxShort < endshortstartmed - startshort) maxShort = endshortstartmed - startshort;
+			    if (minMed > endmedstartlong - endshortstartmed) minMed = endmedstartlong - endshortstartmed;
+			    if (maxMed < endmedstartlong - endshortstartmed) maxMed = endmedstartlong - endshortstartmed;
+			    if (minLong > endlong - endmedstartlong) minLong = endlong - endmedstartlong;
+			    if (maxLong < endlong - endmedstartlong) maxLong = endlong - endmedstartlong;
+			}
+		}
+		System.out.println();
+		System.out.println("Short Message Time Average  : " + (shorttime/times) + "\ttotal: " + shorttime + "\tmin: " + minShort + "\tmax: " + maxShort + "\tBps: " + (shorttime == 0 ? "NaN" : ""+(times*smess.length)/shorttime));
+		System.out.println("Medium Message Time Average : " + (medtime/times) + "\ttotal: " + medtime + "\tmin: " + minMed + "\tmax: " + maxMed + "\tBps: " + (times*mmess.length*1000)/medtime);
+		System.out.println("Long Message Time Average   : " + (longtime/times) + "\ttotal: " + longtime + "\tmin: " + minLong + "\tmax: " + maxLong + "\tBps: " + (times*lmess.length*1000)/longtime);
+	}
+}
+	
diff --git a/core/java/test/net/i2p/crypto/SessionEncryptionTest.java b/core/java/test/net/i2p/crypto/SessionEncryptionTest.java
new file mode 100644
index 0000000000..5a1364e55e
--- /dev/null
+++ b/core/java/test/net/i2p/crypto/SessionEncryptionTest.java
@@ -0,0 +1,406 @@
+package net.i2p.crypto;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't  make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.PublicKey;
+import net.i2p.data.PrivateKey;
+import net.i2p.data.SessionKey;
+import net.i2p.data.SessionTag;
+import net.i2p.data.DataHelper;
+
+import net.i2p.util.Log;
+import net.i2p.util.Clock;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ *
+ * session key management unit tests:
+ *
+ *		Run	tagsIncluded	useTag	rekey
+ * // no sessions
+ * 		1	no		no	no
+ *		2	no		no	no
+ * // session
+ * 		3	yes (2)		no	no
+ * 		4	no		yes	no
+ * 		5	yes (2)		yes	no
+ * 		6	no		yes	no
+ * 		7	no		yes	no
+ * // rekeying
+ * 		8	yes (2)		no	no
+ * 		9	no		yes	no
+ * 		10	yes (2)		yes	yes
+ * 		11	no		yes	no
+ * 		12	no		yes	no
+ * // long session
+ *		13-1000	20 tags every 10 messages, rekey every 50
+ */
+public class SessionEncryptionTest {
+    private final static Log _log = new Log(SessionEncryptionTest.class);
+    public static void main(String args[]) {
+	SessionEncryptionTest test = new SessionEncryptionTest();
+	try {
+	    //test.testNoSessions();
+	    //test.testSessions();
+	    //test.testRekeying();
+	    test.testLongSession();
+	} catch (Throwable t) {
+	    _log.error("Error running tests", t);
+	}
+	try { Thread.sleep(60*1000); } catch (InterruptedException ie) {}
+    }
+   
+    /**
+     *		Run	tagsIncluded	useTag	rekey
+     * 		1	no		no	no
+     *		2	no		no	no
+     */
+    public void testNoSessions() throws Exception {
+	Object keys[] = KeyGenerator.getInstance().generatePKIKeypair();
+	PublicKey pubKey = (PublicKey)keys[0];
+	PrivateKey privKey = (PrivateKey)keys[1];
+	SessionKey curKey = SessionKeyManager.getInstance().createSession(pubKey);
+	
+	byte[] msg1 = "msg 1".getBytes();
+	byte[] msg2 = "msg 2".getBytes();
+
+	byte emsg1[] = ElGamalAESEngine.encrypt(msg1, pubKey, curKey, 64);
+	byte dmsg1[] = ElGamalAESEngine.decrypt(emsg1, privKey);
+	if (DataHelper.eq(dmsg1, msg1)) 
+	    _log.info("PASSED: No sessions msg 1");
+	else
+	    _log.error("FAILED: No sessions msg 1");
+	
+	byte emsg2[] = ElGamalAESEngine.encrypt(msg2, pubKey, curKey, 64);
+	byte dmsg2[] = ElGamalAESEngine.decrypt(emsg2, privKey);
+	if (DataHelper.eq(dmsg2, msg2)) 
+	    _log.info("PASSED: No sessions msg 2");
+	else
+	    _log.error("FAILED: No sessions msg 2");
+   }
+   
+    /**
+     *		Run	tagsIncluded	useTag	rekey
+     * 		1	yes (2)		no	no
+     * 		2	no		yes	no
+     * 		3	yes (2)		yes	no
+     * 		4	no		yes	no
+     * 		5	no		yes	no
+     */
+    public void testSessions() throws Exception {
+	Object keys[] = KeyGenerator.getInstance().generatePKIKeypair();
+	PublicKey pubKey = (PublicKey)keys[0];
+	PrivateKey privKey = (PrivateKey)keys[1];
+	SessionKey curKey = SessionKeyManager.getInstance().createSession(pubKey);
+	
+	SessionTag tag1 = new SessionTag(true);
+	SessionTag tag2 = new SessionTag(true);
+	SessionTag tag3 = new SessionTag(true);
+	SessionTag tag4 = new SessionTag(true);
+
+	HashSet firstTags = new HashSet();
+	firstTags.add(tag1);
+	firstTags.add(tag2);
+	
+	HashSet secondTags = new HashSet();
+	secondTags.add(tag3);
+	secondTags.add(tag4);
+	
+	byte[] msg1 = "msg 1".getBytes();
+	byte[] msg2 = "msg 2".getBytes();
+	byte[] msg3 = "msg 3".getBytes();
+	byte[] msg4 = "msg 4".getBytes();
+	byte[] msg5 = "msg 5".getBytes();
+
+	byte emsg1[] = ElGamalAESEngine.encrypt(msg1, pubKey, curKey, firstTags, 64);
+	byte dmsg1[] = ElGamalAESEngine.decrypt(emsg1, privKey);
+	if (DataHelper.eq(dmsg1, msg1)) 
+	    _log.info("PASSED: Sessions msg 1");
+	else {
+	    _log.error("FAILED: Sessions msg 1");
+	    return;
+	}
+	
+	SessionKeyManager.getInstance().tagsDelivered(pubKey, curKey, firstTags);
+	
+	curKey = SessionKeyManager.getInstance().getCurrentKey(pubKey);
+	SessionTag curTag = SessionKeyManager.getInstance().consumeNextAvailableTag(pubKey, curKey);
+	if (curTag == null) {
+	    _log.error("Not able to consume next tag for message 2");
+	    return;
+	}
+	
+	byte emsg2[] = ElGamalAESEngine.encrypt(msg2, pubKey, curKey, null, curTag, 64);
+	byte dmsg2[] = ElGamalAESEngine.decrypt(emsg2, privKey);
+	if (DataHelper.eq(dmsg2, msg2)) 
+	    _log.info("PASSED: Sessions msg 2");
+	else {
+	    _log.error("FAILED: Sessions msg 2");
+	    return;
+	}
+	
+	curKey = SessionKeyManager.getInstance().getCurrentKey(pubKey);
+	curTag = SessionKeyManager.getInstance().consumeNextAvailableTag(pubKey, curKey);
+	
+	if (curTag == null) {
+	    _log.error("Not able to consume next tag for message 3");
+	    return;
+	}
+	if (curKey == null) {
+	    _log.error("Not able to consume next KEY for message 3");
+	    return;
+	}
+	
+	byte emsg3[] = ElGamalAESEngine.encrypt(msg3, pubKey, curKey, secondTags, curTag, 64);
+	byte dmsg3[] = ElGamalAESEngine.decrypt(emsg3, privKey);
+	if (DataHelper.eq(dmsg3, msg3)) 
+	    _log.info("PASSED: Sessions msg 3");
+	else {
+	    _log.error("FAILED: Sessions msg 3");
+	    return;
+	}
+	
+	SessionKeyManager.getInstance().tagsDelivered(pubKey, curKey, secondTags);
+	
+	curKey = SessionKeyManager.getInstance().getCurrentKey(pubKey);
+	curTag = SessionKeyManager.getInstance().consumeNextAvailableTag(pubKey, curKey);
+	
+	if (curTag == null) {
+	    _log.error("Not able to consume next tag for message 4");
+	    return;
+	}
+	if (curKey == null) {
+	    _log.error("Not able to consume next KEY for message 4");
+	    return;
+	}
+	
+	byte emsg4[] = ElGamalAESEngine.encrypt(msg4, pubKey, curKey, null, curTag, 64);
+	byte dmsg4[] = ElGamalAESEngine.decrypt(emsg4, privKey);
+	if (DataHelper.eq(dmsg4, msg4)) 
+	    _log.info("PASSED: Sessions msg 4");
+	else {
+	    _log.error("FAILED: Sessions msg 4");
+	    return;
+	}
+	
+	curKey = SessionKeyManager.getInstance().getCurrentKey(pubKey);
+	curTag = SessionKeyManager.getInstance().consumeNextAvailableTag(pubKey, curKey);
+	
+	if (curTag == null) {
+	    _log.error("Not able to consume next tag for message 5");
+	    return;
+	}
+	if (curKey == null) {
+	    _log.error("Not able to consume next KEY for message 5");
+	    return;
+	}
+	
+	byte emsg5[] = ElGamalAESEngine.encrypt(msg5, pubKey, curKey, null, curTag, 64);
+	byte dmsg5[] = ElGamalAESEngine.decrypt(emsg5, privKey);
+	if (DataHelper.eq(dmsg5, msg5)) 
+	    _log.info("PASSED: Sessions msg 5");
+	else {
+	    _log.error("FAILED: Sessions msg 5");
+	    return;
+	}
+   }
+    
+    /**
+     *		Run	tagsIncluded	useTag	rekey
+     * 		1	yes (2)		no	no
+     * 		2	no		yes	no
+     * 		3	yes (2)		yes	yes
+     * 		4	no		yes	no
+     * 		5	no		yes	no
+     */
+    public void testRekeying() throws Exception {
+	Object keys[] = KeyGenerator.getInstance().generatePKIKeypair();
+	PublicKey pubKey = (PublicKey)keys[0];
+	PrivateKey privKey = (PrivateKey)keys[1];
+	SessionKey curKey = SessionKeyManager.getInstance().createSession(pubKey);
+	SessionKey nextKey = KeyGenerator.getInstance().generateSessionKey();
+	
+	SessionTag tag1 = new SessionTag(true);
+	SessionTag tag2 = new SessionTag(true);
+	SessionTag tag3 = new SessionTag(true);
+	SessionTag tag4 = new SessionTag(true);
+
+	HashSet firstTags = new HashSet();
+	firstTags.add(tag1);
+	firstTags.add(tag2);
+	
+	HashSet secondTags = new HashSet();
+	secondTags.add(tag3);
+	secondTags.add(tag4);
+	
+	byte[] msg1 = "msg 1".getBytes();
+	byte[] msg2 = "msg 2".getBytes();
+	byte[] msg3 = "msg 3".getBytes();
+	byte[] msg4 = "msg 4".getBytes();
+	byte[] msg5 = "msg 5".getBytes();
+
+	byte emsg1[] = ElGamalAESEngine.encrypt(msg1, pubKey, curKey, firstTags, 64);
+	byte dmsg1[] = ElGamalAESEngine.decrypt(emsg1, privKey);
+	if (DataHelper.eq(dmsg1, msg1)) 
+	    _log.info("PASSED: Sessions msg 1");
+	else {
+	    _log.error("FAILED: Sessions msg 1");
+	    return;
+	}
+	
+	SessionKeyManager.getInstance().tagsDelivered(pubKey, curKey, firstTags);
+	
+	curKey = SessionKeyManager.getInstance().getCurrentKey(pubKey);
+	SessionTag curTag = SessionKeyManager.getInstance().consumeNextAvailableTag(pubKey, curKey);
+	if (curTag == null) {
+	    _log.error("Not able to consume next tag for message 2");
+	    return;
+	}
+	
+	byte emsg2[] = ElGamalAESEngine.encrypt(msg2, pubKey, curKey, null, curTag, 64);
+	byte dmsg2[] = ElGamalAESEngine.decrypt(emsg2, privKey);
+	if (DataHelper.eq(dmsg2, msg2)) 
+	    _log.info("PASSED: Sessions msg 2");
+	else {
+	    _log.error("FAILED: Sessions msg 2");
+	    return;
+	}
+	
+	curKey = SessionKeyManager.getInstance().getCurrentKey(pubKey);
+	curTag = SessionKeyManager.getInstance().consumeNextAvailableTag(pubKey, curKey);
+	
+	if (curTag == null) {
+	    _log.error("Not able to consume next tag for message 3");
+	    return;
+	}
+	if (curKey == null) {
+	    _log.error("Not able to consume next KEY for message 3");
+	    return;
+	}
+	
+	byte emsg3[] = ElGamalAESEngine.encrypt(msg3, pubKey, curKey, secondTags, curTag, nextKey, 64);
+	byte dmsg3[] = ElGamalAESEngine.decrypt(emsg3, privKey);
+	if (DataHelper.eq(dmsg3, msg3)) 
+	    _log.info("PASSED: Sessions msg 3");
+	else {
+	    _log.error("FAILED: Sessions msg 3");
+	    return;
+	}
+	
+	SessionKeyManager.getInstance().tagsDelivered(pubKey, nextKey, secondTags); // note nextKey not curKey
+	
+	curKey = SessionKeyManager.getInstance().getCurrentKey(pubKey);
+	curTag = SessionKeyManager.getInstance().consumeNextAvailableTag(pubKey, curKey);
+	
+	if (curTag == null) {
+	    _log.error("Not able to consume next tag for message 4");
+	    return;
+	}
+	if (curKey == null) {
+	    _log.error("Not able to consume next KEY for message 4");
+	    return;
+	}
+	
+	byte emsg4[] = ElGamalAESEngine.encrypt(msg4, pubKey, curKey, null, curTag, 64);
+	byte dmsg4[] = ElGamalAESEngine.decrypt(emsg4, privKey);
+	if (DataHelper.eq(dmsg4, msg4)) 
+	    _log.info("PASSED: Sessions msg 4");
+	else {
+	    _log.error("FAILED: Sessions msg 4");
+	    return;
+	}
+	
+	curKey = SessionKeyManager.getInstance().getCurrentKey(pubKey);
+	curTag = SessionKeyManager.getInstance().consumeNextAvailableTag(pubKey, curKey);
+	
+	if (curTag == null) {
+	    _log.error("Not able to consume next tag for message 5");
+	    return;
+	}
+	if (curKey == null) {
+	    _log.error("Not able to consume next KEY for message 5");
+	    return;
+	}
+	
+	byte emsg5[] = ElGamalAESEngine.encrypt(msg5, pubKey, curKey, null, curTag, 64);
+	byte dmsg5[] = ElGamalAESEngine.decrypt(emsg5, privKey);
+	if (DataHelper.eq(dmsg5, msg5)) 
+	    _log.info("PASSED: Sessions msg 5");
+	else {
+	    _log.error("FAILED: Sessions msg 5");
+	    return;
+	}
+   }
+ 
+    
+    /**
+     *	20 tags every 10 messages, rekey every 50
+     */
+    public void testLongSession() throws Exception {
+	int num = 1000;
+	long start = Clock.getInstance().now();
+	testLongSession(num);
+	long end = Clock.getInstance().now();
+	long time = end - start;
+	float msEach = (float)num / time;
+	_log.error("Test long session duration: " + num + " messages in " + time + "ms (or " + msEach + "ms each)");
+    }
+    
+    public void testLongSession(int numMsgs) throws Exception {
+	Object keys[] = KeyGenerator.getInstance().generatePKIKeypair();
+	PublicKey pubKey = (PublicKey)keys[0];
+	PrivateKey privKey = (PrivateKey)keys[1];
+	SessionKey curKey = SessionKeyManager.getInstance().createSession(pubKey);
+	
+	for (int i = 0; i < numMsgs; i++) {
+	    Set tags = null;
+	    SessionKey nextKey = null;
+	    curKey = SessionKeyManager.getInstance().getCurrentKey(pubKey);
+	    SessionTag curTag = SessionKeyManager.getInstance().consumeNextAvailableTag(pubKey, curKey);
+	    
+	    int availTags = SessionKeyManager.getInstance().getAvailableTags(pubKey, curKey);
+	    if ((availTags < 1)) {
+		tags = generateNewTags(50);
+		_log.info("Generating new tags");
+	    } else {
+		_log.info("Tags already available: " + availTags + " curTag: " + curTag);
+	    }
+	    if (i % 50 == 0)
+		nextKey = KeyGenerator.getInstance().generateSessionKey();
+	
+	    byte[] msg = ("msg " + i).getBytes();
+
+	    byte emsg[] = ElGamalAESEngine.encrypt(msg, pubKey, curKey, tags, curTag, nextKey, 64);
+	    byte dmsg[] = ElGamalAESEngine.decrypt(emsg, privKey);
+	    if (DataHelper.eq(dmsg, msg)) 
+		_log.info("PASSED: Long session msg " + i);
+	    else {
+		_log.error("FAILED: Long session msg " + i);
+		return;
+	    }
+	
+	    if ( (tags != null) && (tags.size() > 0) ) {
+		if (nextKey == null) {
+		    SessionKeyManager.getInstance().tagsDelivered(pubKey, curKey, tags);
+		} else {
+		    SessionKeyManager.getInstance().tagsDelivered(pubKey, nextKey, tags);
+		}
+	    }
+	}
+    }
+
+    private Set generateNewTags(int numTags) {
+	Set tags = new HashSet(numTags);
+	for (int i = 0; i < numTags; i++)
+	    tags.add(new SessionTag(true));
+	return tags;
+    }
+}
diff --git a/core/java/test/net/i2p/data/AbuseReasonTest.java b/core/java/test/net/i2p/data/AbuseReasonTest.java
new file mode 100644
index 0000000000..eebe5f9ac0
--- /dev/null
+++ b/core/java/test/net/i2p/data/AbuseReasonTest.java
@@ -0,0 +1,30 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.i2cp.AbuseReason;
+
+/**
+ * Test harness for loading / storing Hash objects
+ *
+ * @author jrandom
+ */
+class AbuseReasonTest extends StructureTest {
+    static {
+        TestData.registerTest(new AbuseReasonTest(), "AbuseReason");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        AbuseReason res = new AbuseReason();
+        res.setReason("Because they're mean");
+        return res;
+    }
+    public DataStructure createStructureToRead() { return new AbuseReason(); }
+}
diff --git a/core/java/test/net/i2p/data/AbuseSeverityTest.java b/core/java/test/net/i2p/data/AbuseSeverityTest.java
new file mode 100644
index 0000000000..ffdb1b0632
--- /dev/null
+++ b/core/java/test/net/i2p/data/AbuseSeverityTest.java
@@ -0,0 +1,30 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.i2cp.AbuseSeverity;
+
+/**
+ * Test harness for loading / storing Hash objects
+ *
+ * @author jrandom
+ */
+class AbuseSeverityTest extends StructureTest {
+    static {
+        TestData.registerTest(new AbuseSeverityTest(), "AbuseSeverity");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        AbuseSeverity sev = new AbuseSeverity();
+        sev.setSeverity(64);
+        return sev;
+    }
+    public DataStructure createStructureToRead() { return new AbuseSeverity(); }
+}
diff --git a/core/java/test/net/i2p/data/BooleanTest.java b/core/java/test/net/i2p/data/BooleanTest.java
new file mode 100644
index 0000000000..aa6c4a03ca
--- /dev/null
+++ b/core/java/test/net/i2p/data/BooleanTest.java
@@ -0,0 +1,59 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataHelper;
+import net.i2p.util.Log;
+
+/**
+ * Test harness for the boolean structure
+ *
+ * @author jrandom
+ */
+class BooleanTest implements TestDataGenerator, TestDataPrinter {
+    static {
+        TestData.registerGenerator(new BooleanTest(), "Boolean");
+        TestData.registerPrinter(new BooleanTest(), "Boolean");
+    }
+    private static final Log _log = new Log(BooleanTest.class);
+    
+    public byte[] getData() {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            DataHelper.writeBoolean(baos, Boolean.TRUE);
+            return baos.toByteArray();
+        } catch (DataFormatException dfe) {
+            _log.error("Error writing the boolean", dfe);
+            return null;
+        } catch (IOException ioe) {
+            _log.error("Error writing the boolean", ioe);
+            return null;
+        }
+    }
+    
+    public String testData(InputStream inputStream) {
+        try {
+            Boolean b = DataHelper.readBoolean(inputStream);
+            return ""+b;
+        } catch (DataFormatException dfe) {
+            _log.error("Error reading the boolean", dfe);
+            return null;
+        } catch (IOException ioe) {
+            _log.error("Error reading the boolean", ioe);
+            return null;
+        }
+    }
+    
+    
+}
diff --git a/core/java/test/net/i2p/data/CertificateTest.java b/core/java/test/net/i2p/data/CertificateTest.java
new file mode 100644
index 0000000000..df39ec9e27
--- /dev/null
+++ b/core/java/test/net/i2p/data/CertificateTest.java
@@ -0,0 +1,34 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.Certificate;
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+
+/**
+ * Test harness for loading / storing Hash objects
+ *
+ * @author jrandom
+ */
+class CertificateTest extends StructureTest {
+    static {
+        TestData.registerTest(new CertificateTest(), "Certificate");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        Certificate cert = new Certificate();
+        byte data[] = new byte[32];
+        for (int i = 0; i < data.length; i++)
+            data[i] = (byte)(i%16);
+        cert.setPayload(data);
+        cert.setCertificateType(Certificate.CERTIFICATE_TYPE_NULL);
+        return cert; 
+    }
+    public DataStructure createStructureToRead() { return new Certificate(); }
+}
diff --git a/core/java/test/net/i2p/data/CreateLeaseSetMessageTest.java b/core/java/test/net/i2p/data/CreateLeaseSetMessageTest.java
new file mode 100644
index 0000000000..e2150ff452
--- /dev/null
+++ b/core/java/test/net/i2p/data/CreateLeaseSetMessageTest.java
@@ -0,0 +1,37 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.LeaseSet;
+import net.i2p.data.PrivateKey;
+import net.i2p.data.SigningPrivateKey;
+import net.i2p.data.i2cp.CreateLeaseSetMessage;
+import net.i2p.data.i2cp.SessionId;
+
+/**
+ * Test harness for loading / storing CreateLeaseSetMessage objects
+ *
+ * @author jrandom
+ */
+class CreateLeaseSetMessageTest extends StructureTest {
+    static {
+        TestData.registerTest(new CreateLeaseSetMessageTest(), "CreateLeaseSetMessage");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        CreateLeaseSetMessage msg = new CreateLeaseSetMessage();
+	msg.setPrivateKey((PrivateKey)(new PrivateKeyTest()).createDataStructure());
+	msg.setSigningPrivateKey((SigningPrivateKey)(new SigningPrivateKeyTest()).createDataStructure());
+	msg.setLeaseSet((LeaseSet)(new LeaseSetTest()).createDataStructure());
+        msg.setSessionId((SessionId)(new SessionIdTest()).createDataStructure());
+        return msg; 
+    }
+    public DataStructure createStructureToRead() { return new CreateLeaseSetMessage(); }
+}
diff --git a/core/java/test/net/i2p/data/CreateSessionMessageTest.java b/core/java/test/net/i2p/data/CreateSessionMessageTest.java
new file mode 100644
index 0000000000..8ba444beec
--- /dev/null
+++ b/core/java/test/net/i2p/data/CreateSessionMessageTest.java
@@ -0,0 +1,31 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.i2cp.CreateSessionMessage;
+import net.i2p.data.i2cp.SessionConfig;
+
+/**
+ * Test harness for loading / storing Hash objects
+ *
+ * @author jrandom
+ */
+class CreateSessionMessageTest extends StructureTest {
+    static {
+        TestData.registerTest(new CreateSessionMessageTest(), "CreateSessionMessage");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        CreateSessionMessage msg = new CreateSessionMessage();
+        msg.setSessionConfig((SessionConfig)(new SessionConfigTest()).createDataStructure());
+        return msg; 
+    }
+    public DataStructure createStructureToRead() { return new CreateSessionMessage(); }
+}
diff --git a/core/java/test/net/i2p/data/DateTest.java b/core/java/test/net/i2p/data/DateTest.java
new file mode 100644
index 0000000000..616a3f2793
--- /dev/null
+++ b/core/java/test/net/i2p/data/DateTest.java
@@ -0,0 +1,60 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataHelper;
+import net.i2p.util.Log;
+
+/**
+ * Test harness for the boolean structure
+ *
+ * @author jrandom
+ */
+class DateTest implements TestDataGenerator, TestDataPrinter {
+    static {
+        TestData.registerGenerator(new DateTest(), "Date");
+        TestData.registerPrinter(new DateTest(), "Date");
+    }
+    private static final Log _log = new Log(DateTest.class);
+    
+    public byte[] getData() {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            DataHelper.writeDate(baos, new Date());
+            return baos.toByteArray();
+        } catch (DataFormatException dfe) {
+            _log.error("Error writing the date", dfe);
+            return null;
+        } catch (IOException ioe) {
+            _log.error("Error writing the date", ioe);
+            return null;
+        }
+    }
+    
+    public String testData(InputStream inputStream) {
+        try {
+            Date d = DataHelper.readDate(inputStream);
+            return ""+d;
+        } catch (DataFormatException dfe) {
+            _log.error("Error reading the date", dfe);
+            return null;
+        } catch (IOException ioe) {
+            _log.error("Error reading the date", ioe);
+            return null;
+        }
+    }
+    
+    
+}
diff --git a/core/java/test/net/i2p/data/DestinationTest.java b/core/java/test/net/i2p/data/DestinationTest.java
new file mode 100644
index 0000000000..8d49776391
--- /dev/null
+++ b/core/java/test/net/i2p/data/DestinationTest.java
@@ -0,0 +1,38 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.Certificate;
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.Destination;
+import net.i2p.data.PublicKey;
+import net.i2p.data.SigningPublicKey;
+
+/**
+ * Test harness for loading / storing Hash objects
+ *
+ * @author jrandom
+ */
+class DestinationTest extends StructureTest {
+    static {
+        TestData.registerTest(new DestinationTest(), "Destination");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        Destination dest = new Destination();
+        StructureTest tst = new CertificateTest();
+        dest.setCertificate((Certificate)tst.createDataStructure());
+        tst = new PublicKeyTest();
+        dest.setPublicKey((PublicKey)tst.createDataStructure());
+        tst = new SigningPublicKeyTest();
+        dest.setSigningPublicKey((SigningPublicKey)tst.createDataStructure());
+        return dest; 
+    }
+    public DataStructure createStructureToRead() { return new Destination(); }
+}
diff --git a/core/java/test/net/i2p/data/DestroySessionMessageTest.java b/core/java/test/net/i2p/data/DestroySessionMessageTest.java
new file mode 100644
index 0000000000..42aedcea1b
--- /dev/null
+++ b/core/java/test/net/i2p/data/DestroySessionMessageTest.java
@@ -0,0 +1,31 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.i2cp.DestroySessionMessage;
+import net.i2p.data.i2cp.SessionId;
+
+/**
+ * Test harness for loading / storing Hash objects
+ *
+ * @author jrandom
+ */
+class DestroySessionMessageTest extends StructureTest {
+    static {
+        TestData.registerTest(new DestroySessionMessageTest(), "DestroySessionMessage");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        DestroySessionMessage msg = new DestroySessionMessage();
+        msg.setSessionId((SessionId)(new SessionIdTest()).createDataStructure());
+        return msg; 
+    }
+    public DataStructure createStructureToRead() { return new DestroySessionMessage(); }
+}
diff --git a/core/java/test/net/i2p/data/DisconnectMessageTest.java b/core/java/test/net/i2p/data/DisconnectMessageTest.java
new file mode 100644
index 0000000000..22575fd6d7
--- /dev/null
+++ b/core/java/test/net/i2p/data/DisconnectMessageTest.java
@@ -0,0 +1,30 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.i2cp.DisconnectMessage;
+
+/**
+ * Test harness for loading / storing Hash objects
+ *
+ * @author jrandom
+ */
+class DisconnectMessageTest extends StructureTest {
+    static {
+        TestData.registerTest(new DisconnectMessageTest(), "DisconnectMessage");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        DisconnectMessage msg = new DisconnectMessage();
+        msg.setReason("Because I say so");
+        return msg; 
+    }
+    public DataStructure createStructureToRead() { return new DisconnectMessage(); }
+}
diff --git a/core/java/test/net/i2p/data/HashTest.java b/core/java/test/net/i2p/data/HashTest.java
new file mode 100644
index 0000000000..08f278c00a
--- /dev/null
+++ b/core/java/test/net/i2p/data/HashTest.java
@@ -0,0 +1,33 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.Hash;
+
+/**
+ * Test harness for loading / storing Hash objects
+ *
+ * @author jrandom
+ */
+class HashTest extends StructureTest {
+    static {
+        TestData.registerTest(new HashTest(), "Hash");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        Hash hash = new Hash();
+        byte data[] = new byte[32];
+        for (int i = 0; i < data.length; i++)
+            data[i] = (byte)(i%16);
+        hash.setData(data);
+        return hash; 
+    }
+    public DataStructure createStructureToRead() { return new Hash(); }
+}
diff --git a/core/java/test/net/i2p/data/LeaseSetTest.java b/core/java/test/net/i2p/data/LeaseSetTest.java
new file mode 100644
index 0000000000..2bfa07c32d
--- /dev/null
+++ b/core/java/test/net/i2p/data/LeaseSetTest.java
@@ -0,0 +1,38 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.Destination;
+import net.i2p.data.LeaseSet;
+import net.i2p.data.PublicKey;
+import net.i2p.data.Signature;
+import net.i2p.data.SigningPublicKey;
+
+/**
+ * Test harness for loading / storing Lease objects
+ *
+ * @author jrandom
+ */
+class LeaseSetTest extends StructureTest {
+    static {
+        TestData.registerTest(new LeaseSetTest(), "LeaseSet");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        LeaseSet leaseSet = new LeaseSet();
+        leaseSet.setDestination((Destination)(new DestinationTest()).createDataStructure());
+        leaseSet.setEncryptionKey((PublicKey)(new PublicKeyTest()).createDataStructure());
+	leaseSet.setSignature((Signature)(new SignatureTest()).createDataStructure());
+	leaseSet.setSigningKey((SigningPublicKey)(new SigningPublicKeyTest()).createDataStructure());
+	//leaseSet.setVersion(42l);
+        return leaseSet; 
+    }
+    public DataStructure createStructureToRead() { return new LeaseSet(); }
+}
diff --git a/core/java/test/net/i2p/data/LeaseTest.java b/core/java/test/net/i2p/data/LeaseTest.java
new file mode 100644
index 0000000000..9f214ed23f
--- /dev/null
+++ b/core/java/test/net/i2p/data/LeaseTest.java
@@ -0,0 +1,41 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.util.Date;
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.Lease;
+import net.i2p.data.RouterIdentity;
+import net.i2p.data.TunnelId;
+
+/**
+ * Test harness for loading / storing Lease objects
+ *
+ * @author jrandom
+ */
+class LeaseTest extends StructureTest {
+    static {
+        TestData.registerTest(new LeaseTest(), "Lease");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        Lease lease = new Lease();
+        StructureTest tst = new DestinationTest();
+        lease.setEndDate(new Date(1000*60*2));
+        //lease.setStartDate(new Date(1000*60));
+        tst = new RouterIdentityTest();
+        lease.setRouterIdentity((RouterIdentity)tst.createDataStructure());
+        tst = new TunnelIdTest();
+        lease.setTunnelId((TunnelId)tst.createDataStructure());
+        
+        return lease; 
+    }
+    public DataStructure createStructureToRead() { return new Lease(); }
+}
diff --git a/core/java/test/net/i2p/data/MappingTest.java b/core/java/test/net/i2p/data/MappingTest.java
new file mode 100644
index 0000000000..b1b8bf9e0c
--- /dev/null
+++ b/core/java/test/net/i2p/data/MappingTest.java
@@ -0,0 +1,64 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataHelper;
+import net.i2p.util.Log;
+
+/**
+ * Test harness for the mapping structure (in java, a Properties map)
+ *
+ * @author jrandom
+ */
+class MappingTest implements TestDataGenerator, TestDataPrinter {
+    static {
+        TestData.registerGenerator(new MappingTest(), "Mapping");
+        TestData.registerPrinter(new MappingTest(), "Mapping");
+    }
+    private static final Log _log = new Log(MappingTest.class);
+    
+    public byte[] getData() {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        Properties options = new Properties();
+        options.setProperty("key1", "val1");
+        options.setProperty("key2", "val2");
+        options.setProperty("key3", "val3");
+        try {
+            DataHelper.writeProperties(baos, options);
+            return baos.toByteArray();
+        } catch (DataFormatException dfe) {
+            _log.error("Error writing the mapping", dfe);
+            return null;
+        } catch (IOException ioe) {
+            _log.error("Error writing the mapping", ioe);
+            return null;
+        }
+    }
+    
+    public String testData(InputStream inputStream) {
+        try {
+            Properties options = DataHelper.readProperties(inputStream);
+            return DataHelper.toString(options);
+        } catch (DataFormatException dfe) {
+            _log.error("Error reading the mapping", dfe);
+            return null;
+        } catch (IOException ioe) {
+            _log.error("Error reading the mapping", ioe);
+            return null;
+        }
+    }
+    
+    
+}
diff --git a/core/java/test/net/i2p/data/MessageIdTest.java b/core/java/test/net/i2p/data/MessageIdTest.java
new file mode 100644
index 0000000000..e92d26f378
--- /dev/null
+++ b/core/java/test/net/i2p/data/MessageIdTest.java
@@ -0,0 +1,30 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.i2cp.MessageId;
+
+/**
+ * Test harness for loading / storing Hash objects
+ *
+ * @author jrandom
+ */
+class MessageIdTest extends StructureTest {
+    static {
+        TestData.registerTest(new MessageIdTest(), "MessageId");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        MessageId id = new MessageId();
+        id.setMessageId(101);
+        return id;
+    }
+    public DataStructure createStructureToRead() { return new MessageId(); }
+}
diff --git a/core/java/test/net/i2p/data/MessagePayloadMessageTest.java b/core/java/test/net/i2p/data/MessagePayloadMessageTest.java
new file mode 100644
index 0000000000..8234a518e6
--- /dev/null
+++ b/core/java/test/net/i2p/data/MessagePayloadMessageTest.java
@@ -0,0 +1,56 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.Payload;
+import net.i2p.data.i2cp.MessageId;
+import net.i2p.data.i2cp.MessagePayloadMessage;
+import net.i2p.data.i2cp.SessionId;
+import net.i2p.util.Log;
+
+/**
+ * Test harness for loading / storing SendMessageMessage objects
+ *
+ * @author jrandom
+ */
+class MessagePayloadMessageTest extends StructureTest {
+    private final static Log _log = new Log(MessagePayloadMessage.class);
+
+    static {
+        TestData.registerTest(new MessagePayloadMessageTest(), "MessagePayloadMessage");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        MessagePayloadMessage msg = new MessagePayloadMessage();
+        msg.setMessageId((MessageId)(new MessageIdTest()).createDataStructure());
+        msg.setPayload((Payload)(new PayloadTest()).createDataStructure());
+        msg.setSessionId((SessionId)(new SessionIdTest()).createDataStructure());
+        return msg; 
+    }
+    public DataStructure createStructureToRead() { return new MessagePayloadMessage(); }   
+    public String testData(InputStream inputStream) {
+        try {
+            DataStructure structure = createStructureToRead();
+            structure.readBytes(inputStream);
+            ((MessagePayloadMessage)structure).getPayload().setUnencryptedData(((MessagePayloadMessage)structure).getPayload().getEncryptedData());
+            return structure.toString();
+        } catch (DataFormatException dfe) {
+            _log.error("Error reading the data structure", dfe);
+            return null;
+        } catch (IOException ioe) {
+            _log.error("Error reading the data structure", ioe);
+            return null;
+        }
+    }
+ 
+}
diff --git a/core/java/test/net/i2p/data/MessageStatusMessageTest.java b/core/java/test/net/i2p/data/MessageStatusMessageTest.java
new file mode 100644
index 0000000000..7b2db8c994
--- /dev/null
+++ b/core/java/test/net/i2p/data/MessageStatusMessageTest.java
@@ -0,0 +1,35 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.i2cp.MessageId;
+import net.i2p.data.i2cp.MessageStatusMessage;
+import net.i2p.data.i2cp.SessionId;
+
+/**
+ * Test harness for loading / storing MessageStatusMessage objects
+ *
+ * @author jrandom
+ */
+class MessageStatusMessageTest extends StructureTest {
+    static {
+        TestData.registerTest(new MessageStatusMessageTest(), "MessageStatusMessage");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        MessageStatusMessage msg = new MessageStatusMessage();
+        msg.setSessionId((SessionId)(new SessionIdTest()).createDataStructure());
+        msg.setMessageId((MessageId)(new MessageIdTest()).createDataStructure());
+        msg.setSize(1024*1024*42L);
+        msg.setStatus(MessageStatusMessage.STATUS_AVAILABLE);
+        return msg; 
+    }
+    public DataStructure createStructureToRead() { return new MessageStatusMessage(); }
+}
diff --git a/core/java/test/net/i2p/data/PayloadTest.java b/core/java/test/net/i2p/data/PayloadTest.java
new file mode 100644
index 0000000000..2fe01de778
--- /dev/null
+++ b/core/java/test/net/i2p/data/PayloadTest.java
@@ -0,0 +1,65 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.Destination;
+import net.i2p.data.Hash;
+import net.i2p.data.Payload;
+import net.i2p.data.SessionKey;
+import net.i2p.util.Log;
+
+/**
+ * Test harness for loading / storing Payload objects
+ *
+ * @author jrandom
+ */
+class PayloadTest extends StructureTest {
+    private final static Log _log = new Log(PayloadTest.class);
+
+    static {
+        TestData.registerTest(new PayloadTest(), "Payload");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        Payload payload = new Payload();
+        SessionKey key = (SessionKey)(new SessionKeyTest()).createDataStructure();
+        //payload.setEncryptionKey(key);
+        byte data[] = "Hello, I2P".getBytes();
+        payload.setUnencryptedData(data);
+        Hash hash = (Hash)(new HashTest()).createDataStructure();
+        //payload.setHash(hash);
+        Destination target = (Destination)(new DestinationTest()).createDataStructure();
+	payload.setEncryptedData(data);
+        //payload.encryptPayload(target, 128);
+        return payload; 
+    }
+    public DataStructure createStructureToRead() { return new Payload(); }
+    
+    public String testData(InputStream inputStream) {
+        try {
+            DataStructure structure = createStructureToRead();
+            structure.readBytes(inputStream);
+	    Payload payload = (Payload)structure;
+	    payload.setUnencryptedData(payload.getEncryptedData());
+            //((Payload)structure).decryptPayload((PrivateKey)(new PrivateKeyTest()).createDataStructure());
+            return structure.toString();
+        } catch (DataFormatException dfe) {
+            _log.error("Error reading the data structure", dfe);
+            return null;
+        } catch (IOException ioe) {
+            _log.error("Error reading the data structure", ioe);
+            return null;
+        }
+    }
+    
+}
diff --git a/core/java/test/net/i2p/data/PrivateKeyTest.java b/core/java/test/net/i2p/data/PrivateKeyTest.java
new file mode 100644
index 0000000000..6b78e2610c
--- /dev/null
+++ b/core/java/test/net/i2p/data/PrivateKeyTest.java
@@ -0,0 +1,33 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.PrivateKey;
+
+/**
+ * Test harness for loading / storing PrivateKey objects
+ *
+ * @author jrandom
+ */
+class PrivateKeyTest extends StructureTest {
+    static {
+        TestData.registerTest(new PrivateKeyTest(), "PrivateKey");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        PrivateKey privateKey = new PrivateKey();
+        byte data[] = new byte[PrivateKey.KEYSIZE_BYTES];
+        for (int i = 0; i < data.length; i++)
+            data[i] = (byte)(i%16);
+        privateKey.setData(data);
+        return privateKey; 
+    }
+    public DataStructure createStructureToRead() { return new PrivateKey(); }
+}
diff --git a/core/java/test/net/i2p/data/PublicKeyTest.java b/core/java/test/net/i2p/data/PublicKeyTest.java
new file mode 100644
index 0000000000..8fe78cef6a
--- /dev/null
+++ b/core/java/test/net/i2p/data/PublicKeyTest.java
@@ -0,0 +1,33 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.PublicKey;
+
+/**
+ * Test harness for loading / storing PublicKey objects
+ *
+ * @author jrandom
+ */
+class PublicKeyTest extends StructureTest {
+    static {
+        TestData.registerTest(new PublicKeyTest(), "PublicKey");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        PublicKey publicKey = new PublicKey();
+        byte data[] = new byte[PublicKey.KEYSIZE_BYTES];
+        for (int i = 0; i < data.length; i++)
+            data[i] = (byte)(i%16);
+        publicKey.setData(data);
+        return publicKey; 
+    }
+    public DataStructure createStructureToRead() { return new PublicKey(); }
+}
diff --git a/core/java/test/net/i2p/data/ReceiveMessageBeginMessageTest.java b/core/java/test/net/i2p/data/ReceiveMessageBeginMessageTest.java
new file mode 100644
index 0000000000..140a9b8071
--- /dev/null
+++ b/core/java/test/net/i2p/data/ReceiveMessageBeginMessageTest.java
@@ -0,0 +1,33 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.i2cp.MessageId;
+import net.i2p.data.i2cp.ReceiveMessageBeginMessage;
+import net.i2p.data.i2cp.SessionId;
+
+/**
+ * Test harness for loading / storing Hash objects
+ *
+ * @author jrandom
+ */
+class ReceiveMessageBeginMessageTest extends StructureTest {
+    static {
+        TestData.registerTest(new ReceiveMessageBeginMessageTest(), "ReceiveMessageBeginMessage");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        ReceiveMessageBeginMessage msg = new ReceiveMessageBeginMessage();
+        msg.setSessionId((SessionId)(new SessionIdTest()).createDataStructure());
+        msg.setMessageId((MessageId)(new MessageIdTest()).createDataStructure());
+        return msg; 
+    }
+    public DataStructure createStructureToRead() { return new ReceiveMessageBeginMessage(); }
+}
diff --git a/core/java/test/net/i2p/data/ReceiveMessageEndMessageTest.java b/core/java/test/net/i2p/data/ReceiveMessageEndMessageTest.java
new file mode 100644
index 0000000000..c1c34b5d17
--- /dev/null
+++ b/core/java/test/net/i2p/data/ReceiveMessageEndMessageTest.java
@@ -0,0 +1,33 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.i2cp.MessageId;
+import net.i2p.data.i2cp.ReceiveMessageEndMessage;
+import net.i2p.data.i2cp.SessionId;
+
+/**
+ * Test harness for loading / storing Hash objects
+ *
+ * @author jrandom
+ */
+class ReceiveMessageEndMessageTest extends StructureTest {
+    static {
+        TestData.registerTest(new ReceiveMessageEndMessageTest(), "ReceiveMessageEndMessage");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        ReceiveMessageEndMessage msg = new ReceiveMessageEndMessage();
+        msg.setSessionId((SessionId)(new SessionIdTest()).createDataStructure());
+        msg.setMessageId((MessageId)(new MessageIdTest()).createDataStructure());
+        return msg; 
+    }
+    public DataStructure createStructureToRead() { return new ReceiveMessageEndMessage(); }
+}
diff --git a/core/java/test/net/i2p/data/ReportAbuseMessageTest.java b/core/java/test/net/i2p/data/ReportAbuseMessageTest.java
new file mode 100644
index 0000000000..7696946359
--- /dev/null
+++ b/core/java/test/net/i2p/data/ReportAbuseMessageTest.java
@@ -0,0 +1,37 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.i2cp.AbuseReason;
+import net.i2p.data.i2cp.AbuseSeverity;
+import net.i2p.data.i2cp.MessageId;
+import net.i2p.data.i2cp.ReportAbuseMessage;
+import net.i2p.data.i2cp.SessionId;
+
+/**
+ * Test harness for loading / storing Hash objects
+ *
+ * @author jrandom
+ */
+class ReportAbuseMessageTest extends StructureTest {
+    static {
+        TestData.registerTest(new ReportAbuseMessageTest(), "ReportAbuseMessage");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        ReportAbuseMessage msg = new ReportAbuseMessage();
+        msg.setMessageId((MessageId)(new MessageIdTest()).createDataStructure());
+        msg.setReason((AbuseReason)(new AbuseReasonTest()).createDataStructure());
+        msg.setSessionId((SessionId)(new SessionIdTest()).createDataStructure());
+        msg.setSeverity((AbuseSeverity)(new AbuseSeverityTest()).createDataStructure());
+        return msg; 
+    }
+    public DataStructure createStructureToRead() { return new ReportAbuseMessage(); }
+}
diff --git a/core/java/test/net/i2p/data/RequestLeaseSetMessageTest.java b/core/java/test/net/i2p/data/RequestLeaseSetMessageTest.java
new file mode 100644
index 0000000000..3bcb546c86
--- /dev/null
+++ b/core/java/test/net/i2p/data/RequestLeaseSetMessageTest.java
@@ -0,0 +1,38 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.util.Date;
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.RouterIdentity;
+import net.i2p.data.TunnelId;
+import net.i2p.data.i2cp.RequestLeaseSetMessage;
+import net.i2p.data.i2cp.SessionId;
+
+/**
+ * Test harness for loading / storing RequestLeaseSetMessage objects
+ *
+ * @author jrandom
+ */
+class RequestLeaseSetMessageTest extends StructureTest {
+    static {
+        TestData.registerTest(new RequestLeaseSetMessageTest(), "RequestLeaseSetMessage");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        RequestLeaseSetMessage msg = new RequestLeaseSetMessage();
+        msg.setSessionId((SessionId)(new SessionIdTest()).createDataStructure());
+        msg.setEndDate(new Date(1000*60*60*12));
+	msg.addEndpoint((RouterIdentity)(new RouterIdentityTest()).createDataStructure(), 
+		        (TunnelId)(new TunnelIdTest()).createDataStructure());
+        return msg; 
+    }
+    public DataStructure createStructureToRead() { return new RequestLeaseSetMessage(); }
+}
diff --git a/core/java/test/net/i2p/data/RouterAddressTest.java b/core/java/test/net/i2p/data/RouterAddressTest.java
new file mode 100644
index 0000000000..6c48d53f17
--- /dev/null
+++ b/core/java/test/net/i2p/data/RouterAddressTest.java
@@ -0,0 +1,42 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.util.Date;
+import java.util.Properties;
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.RouterAddress;
+
+/**
+ * Test harness for loading / storing Hash objects
+ *
+ * @author jrandom
+ */
+class RouterAddressTest extends StructureTest {
+    static {
+        TestData.registerTest(new RouterAddressTest(), "RouterAddress");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        RouterAddress addr = new RouterAddress();
+        byte data[] = new byte[32];
+        for (int i = 0; i < data.length; i++)
+            data[i] = (byte)(i%16);
+        addr.setCost(42);
+        addr.setExpiration(new Date(1000*60*60*24)); // jan 2 1970
+        Properties options = new Properties();
+        options.setProperty("hostname", "localhost");
+        options.setProperty("portnum", "1234");
+        addr.setOptions(options);
+        addr.setTransportStyle("Blah");
+        return addr; 
+    }
+    public DataStructure createStructureToRead() { return new RouterAddress(); }
+}
diff --git a/core/java/test/net/i2p/data/RouterIdentityTest.java b/core/java/test/net/i2p/data/RouterIdentityTest.java
new file mode 100644
index 0000000000..82a8e6cd81
--- /dev/null
+++ b/core/java/test/net/i2p/data/RouterIdentityTest.java
@@ -0,0 +1,38 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.Certificate;
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.PublicKey;
+import net.i2p.data.RouterIdentity;
+import net.i2p.data.SigningPublicKey;
+
+/**
+ * Test harness for loading / storing Hash objects
+ *
+ * @author jrandom
+ */
+class RouterIdentityTest extends StructureTest {
+    static {
+        TestData.registerTest(new RouterIdentityTest(), "RouterIdentity");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        RouterIdentity ident = new RouterIdentity();
+        Certificate cert = (Certificate)(new CertificateTest()).createDataStructure();
+        ident.setCertificate(cert);
+        PublicKey pk = (PublicKey)(new PublicKeyTest()).createDataStructure();
+        ident.setPublicKey(pk);
+        SigningPublicKey k = (SigningPublicKey)(new SigningPublicKeyTest()).createDataStructure();
+        ident.setSigningPublicKey(k);
+        return ident;
+    }
+    public DataStructure createStructureToRead() { return new RouterIdentity(); }
+}
diff --git a/core/java/test/net/i2p/data/RouterInfoTest.java b/core/java/test/net/i2p/data/RouterInfoTest.java
new file mode 100644
index 0000000000..1f3f7d48bc
--- /dev/null
+++ b/core/java/test/net/i2p/data/RouterInfoTest.java
@@ -0,0 +1,85 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.util.HashSet;
+import java.util.Properties;
+
+import net.i2p.crypto.KeyGenerator;
+import net.i2p.data.Certificate;
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.PrivateKey;
+import net.i2p.data.PublicKey;
+import net.i2p.data.RouterIdentity;
+import net.i2p.data.RouterInfo;
+import net.i2p.data.SigningPrivateKey;
+import net.i2p.data.SigningPublicKey;
+import net.i2p.util.Log;
+
+/**
+ * Test harness for loading / storing Hash objects
+ *
+ * @author jrandom
+ */
+class RouterInfoTest extends StructureTest {
+    private final static Log _log = new Log(RouterInfoTest.class);
+    static {
+        TestData.registerTest(new RouterInfoTest(), "RouterInfo");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        RouterInfo info = new RouterInfo();
+        HashSet addresses = new HashSet();
+        DataStructure structure = (new RouterAddressTest()).createDataStructure();
+        addresses.add(structure);
+        info.setAddresses(addresses);
+        
+	PublicKey pubKey = null;
+	SigningPublicKey signingPubKey = null;
+	PrivateKey privKey = null;
+	SigningPrivateKey signingPrivKey = null;
+	
+	Object obj[] = KeyGenerator.getInstance().generatePKIKeypair();
+	pubKey = (PublicKey)obj[0];
+	privKey = (PrivateKey)obj[1];
+	obj = KeyGenerator.getInstance().generateSigningKeypair();
+	signingPubKey = (SigningPublicKey)obj[0];
+	signingPrivKey = (SigningPrivateKey)obj[1];
+	
+	_log.debug("SigningPublicKey: " + signingPubKey);
+	_log.debug("SigningPrivateKey: " + signingPrivKey);
+	
+	RouterIdentity ident = new RouterIdentity();
+	ident.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
+	ident.setPublicKey(pubKey);
+	ident.setSigningPublicKey(signingPubKey);
+	
+        info.setIdentity(ident);
+        
+        Properties options = new Properties();
+	for (int i = 0; i < 16; i++) {
+	    options.setProperty("option." + i, "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890$:." + i);
+	}
+        options.setProperty("netConnectionSpeed", "OC12");
+        info.setOptions(options);
+	
+        HashSet peers = new HashSet();
+        structure = (new HashTest()).createDataStructure();
+        peers.add(structure);
+        info.setPeers(peers);
+	info.setPublished(System.currentTimeMillis());
+        
+        //info.setVersion(69);
+        
+	info.sign(signingPrivKey);
+        
+        return info;
+    }
+    public DataStructure createStructureToRead() { return new RouterInfo(); }
+}
diff --git a/core/java/test/net/i2p/data/SendMessageMessageTest.java b/core/java/test/net/i2p/data/SendMessageMessageTest.java
new file mode 100644
index 0000000000..019147ba96
--- /dev/null
+++ b/core/java/test/net/i2p/data/SendMessageMessageTest.java
@@ -0,0 +1,56 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.Destination;
+import net.i2p.data.Payload;
+import net.i2p.data.i2cp.SendMessageMessage;
+import net.i2p.data.i2cp.SessionId;
+import net.i2p.util.Log;
+
+/**
+ * Test harness for loading / storing SendMessageMessage objects
+ *
+ * @author jrandom
+ */
+class SendMessageMessageTest extends StructureTest {
+    private final static Log _log = new Log(SendMessageMessageTest.class);
+
+    static {
+        TestData.registerTest(new SendMessageMessageTest(), "SendMessageMessage");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        SendMessageMessage msg = new SendMessageMessage();
+        msg.setDestination((Destination)(new DestinationTest()).createDataStructure());
+        msg.setPayload((Payload)(new PayloadTest()).createDataStructure());
+        msg.setSessionId((SessionId)(new SessionIdTest()).createDataStructure());
+        return msg; 
+    }
+    public DataStructure createStructureToRead() { return new SendMessageMessage(); }   
+    public String testData(InputStream inputStream) {
+        try {
+            DataStructure structure = createStructureToRead();
+            structure.readBytes(inputStream);
+            ((SendMessageMessage)structure).getPayload().setUnencryptedData(((SendMessageMessage)structure).getPayload().getEncryptedData());
+            return structure.toString();
+        } catch (DataFormatException dfe) {
+            _log.error("Error reading the data structure", dfe);
+            return null;
+        } catch (IOException ioe) {
+            _log.error("Error reading the data structure", ioe);
+            return null;
+        }
+    }
+ 
+}
diff --git a/core/java/test/net/i2p/data/SessionConfigTest.java b/core/java/test/net/i2p/data/SessionConfigTest.java
new file mode 100644
index 0000000000..c5f72d40ae
--- /dev/null
+++ b/core/java/test/net/i2p/data/SessionConfigTest.java
@@ -0,0 +1,42 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.util.Properties;
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.Destination;
+import net.i2p.data.Signature;
+import net.i2p.data.SigningPrivateKey;
+import net.i2p.data.i2cp.SessionConfig;
+
+/**
+ * Test harness for loading / storing Hash objects
+ *
+ * @author jrandom
+ */
+class SessionConfigTest extends StructureTest {
+    static {
+        TestData.registerTest(new SessionConfigTest(), "SessionConfig");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        SessionConfig cfg = new SessionConfig();
+        cfg.setDestination((Destination)(new DestinationTest()).createDataStructure());
+        cfg.setSignature((Signature)(new SignatureTest()).createDataStructure());
+        Properties options = new Properties();
+        options.setProperty("routerHost", "localhost");
+        options.setProperty("routerPort", "54321");
+        options.setProperty("routerSecret", "blah");
+        cfg.setOptions(options);
+        cfg.signSessionConfig((SigningPrivateKey)(new SigningPrivateKeyTest()).createDataStructure());
+        return cfg; 
+    }
+    public DataStructure createStructureToRead() { return new SessionConfig(); }
+}
diff --git a/core/java/test/net/i2p/data/SessionIdTest.java b/core/java/test/net/i2p/data/SessionIdTest.java
new file mode 100644
index 0000000000..2be8a9f148
--- /dev/null
+++ b/core/java/test/net/i2p/data/SessionIdTest.java
@@ -0,0 +1,30 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.i2cp.SessionId;
+
+/**
+ * Test harness for loading / storing SessionId objects
+ *
+ * @author jrandom
+ */
+class SessionIdTest extends StructureTest {
+    static {
+        TestData.registerTest(new SessionIdTest(), "SessionId");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        SessionId id = new SessionId();
+        id.setSessionId(7);
+        return id;
+    }
+    public DataStructure createStructureToRead() { return new SessionId(); }
+}
diff --git a/core/java/test/net/i2p/data/SessionKeyTest.java b/core/java/test/net/i2p/data/SessionKeyTest.java
new file mode 100644
index 0000000000..862056e9e9
--- /dev/null
+++ b/core/java/test/net/i2p/data/SessionKeyTest.java
@@ -0,0 +1,33 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.SessionKey;
+
+/**
+ * Test harness for loading / storing SessionKey objects
+ *
+ * @author jrandom
+ */
+class SessionKeyTest extends StructureTest {
+    static {
+        TestData.registerTest(new SessionKeyTest(), "SessionKey");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        SessionKey key = new SessionKey();
+        byte data[] = new byte[SessionKey.KEYSIZE_BYTES];
+        for (int i = 0; i < data.length; i++)
+            data[i] = (byte)(i%16);
+        key.setData(data);
+        return key; 
+    }
+    public DataStructure createStructureToRead() { return new SessionKey(); }
+}
diff --git a/core/java/test/net/i2p/data/SessionStatusMessageTest.java b/core/java/test/net/i2p/data/SessionStatusMessageTest.java
new file mode 100644
index 0000000000..0626272db7
--- /dev/null
+++ b/core/java/test/net/i2p/data/SessionStatusMessageTest.java
@@ -0,0 +1,32 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.i2cp.SessionId;
+import net.i2p.data.i2cp.SessionStatusMessage;
+
+/**
+ * Test harness for loading / storing SessionStatusMessage objects
+ *
+ * @author jrandom
+ */
+class SessionStatusMessageTest extends StructureTest {
+    static {
+        TestData.registerTest(new SessionStatusMessageTest(), "SessionStatusMessage");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        SessionStatusMessage msg = new SessionStatusMessage();
+        msg.setSessionId((SessionId)(new SessionIdTest()).createDataStructure());
+        msg.setStatus(SessionStatusMessage.STATUS_CREATED);
+        return msg; 
+    }
+    public DataStructure createStructureToRead() { return new SessionStatusMessage(); }
+}
diff --git a/core/java/test/net/i2p/data/SignatureTest.java b/core/java/test/net/i2p/data/SignatureTest.java
new file mode 100644
index 0000000000..f49eb15869
--- /dev/null
+++ b/core/java/test/net/i2p/data/SignatureTest.java
@@ -0,0 +1,33 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.Signature;
+
+/**
+ * Test harness for loading / storing Signature objects
+ *
+ * @author jrandom
+ */
+class SignatureTest extends StructureTest {
+    static {
+        TestData.registerTest(new SignatureTest(), "Signature");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        Signature sig = new Signature();
+        byte data[] = new byte[Signature.SIGNATURE_BYTES];
+        for (int i = 0; i < data.length; i++)
+            data[i] = (byte)(i%16);
+        sig.setData(data);
+        return sig; 
+    }
+    public DataStructure createStructureToRead() { return new Signature(); }
+}
diff --git a/core/java/test/net/i2p/data/SigningPrivateKeyTest.java b/core/java/test/net/i2p/data/SigningPrivateKeyTest.java
new file mode 100644
index 0000000000..e89193a9f2
--- /dev/null
+++ b/core/java/test/net/i2p/data/SigningPrivateKeyTest.java
@@ -0,0 +1,33 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.SigningPrivateKey;
+
+/**
+ * Test harness for loading / storing SigningPrivateKey objects
+ *
+ * @author jrandom
+ */
+class SigningPrivateKeyTest extends StructureTest {
+    static {
+        TestData.registerTest(new SigningPrivateKeyTest(), "SigningPrivateKey");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        SigningPrivateKey privateKey = new SigningPrivateKey();
+        byte data[] = new byte[SigningPrivateKey.KEYSIZE_BYTES];
+        for (int i = 0; i < data.length; i++)
+            data[i] = (byte)(i%16);
+        privateKey.setData(data);
+        return privateKey; 
+    }
+    public DataStructure createStructureToRead() { return new SigningPrivateKey(); }
+}
diff --git a/core/java/test/net/i2p/data/SigningPublicKeyTest.java b/core/java/test/net/i2p/data/SigningPublicKeyTest.java
new file mode 100644
index 0000000000..72129cf3b7
--- /dev/null
+++ b/core/java/test/net/i2p/data/SigningPublicKeyTest.java
@@ -0,0 +1,33 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.SigningPublicKey;
+
+/**
+ * Test harness for loading / storing PublicKey objects
+ *
+ * @author jrandom
+ */
+class SigningPublicKeyTest extends StructureTest {
+    static {
+        TestData.registerTest(new SigningPublicKeyTest(), "SigningPublicKey");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        SigningPublicKey publicKey = new SigningPublicKey();
+        byte data[] = new byte[SigningPublicKey.KEYSIZE_BYTES];
+        for (int i = 0; i < data.length; i++)
+            data[i] = (byte)(i%16);
+        publicKey.setData(data);
+        return publicKey; 
+    }
+    public DataStructure createStructureToRead() { return new SigningPublicKey(); }
+}
diff --git a/core/java/test/net/i2p/data/StringTest.java b/core/java/test/net/i2p/data/StringTest.java
new file mode 100644
index 0000000000..7647666a0b
--- /dev/null
+++ b/core/java/test/net/i2p/data/StringTest.java
@@ -0,0 +1,59 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataHelper;
+import net.i2p.util.Log;
+
+/**
+ * Test harness for the boolean structure
+ *
+ * @author jrandom
+ */
+class StringTest implements TestDataGenerator, TestDataPrinter {
+    static {
+        TestData.registerGenerator(new StringTest(), "String");
+        TestData.registerPrinter(new StringTest(), "String");
+    }
+    private static final Log _log = new Log(StringTest.class);
+    
+    public byte[] getData() {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            DataHelper.writeString(baos, "Hello, I2P");
+            return baos.toByteArray();
+        } catch (DataFormatException dfe) {
+            _log.error("Error writing the string", dfe);
+            return null;
+        } catch (IOException ioe) {
+            _log.error("Error writing the string", ioe);
+            return null;
+        }
+    }
+    
+    public String testData(InputStream inputStream) {
+        try {
+            String str = DataHelper.readString(inputStream);
+            return ""+str;
+        } catch (DataFormatException dfe) {
+            _log.error("Error reading the string", dfe);
+            return null;
+        } catch (IOException ioe) {
+            _log.error("Error reading the string", ioe);
+            return null;
+        }
+    }
+    
+    
+}
diff --git a/core/java/test/net/i2p/data/StructureTest.java b/core/java/test/net/i2p/data/StructureTest.java
new file mode 100644
index 0000000000..df6089a265
--- /dev/null
+++ b/core/java/test/net/i2p/data/StructureTest.java
@@ -0,0 +1,60 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.util.Log;
+
+/**
+ * Utility class for wrapping data structure tests
+ *
+ * @author jrandom
+ */
+abstract class StructureTest implements TestDataGenerator, TestDataPrinter {
+    private static final Log _log = new Log(StructureTest.class);
+    
+    public abstract DataStructure createDataStructure() throws DataFormatException;
+    public abstract DataStructure createStructureToRead();
+
+    public byte[] getData() {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            DataStructure structure = createDataStructure();
+            structure.writeBytes(baos);
+        } catch (DataFormatException dfe) {
+            _log.error("Error writing the data structure", dfe);
+            return null;
+        } catch (IOException ioe) {
+            _log.error("Error writing the data structure", ioe);
+            return null;
+        }
+        return baos.toByteArray();
+    }
+    
+    public String testData(InputStream inputStream) {
+        try {
+            DataStructure structure = createStructureToRead();
+            structure.readBytes(inputStream);
+	    return structure.toString() + "\n\nIn base 64: " + structure.toBase64();
+        } catch (DataFormatException dfe) {
+            _log.error("Error reading the data structure", dfe);
+            return null;
+        } catch (IOException ioe) {
+            _log.error("Error reading the data structure", ioe);
+            return null;
+        }
+    }
+    
+    
+}
diff --git a/core/java/test/net/i2p/data/TestData.java b/core/java/test/net/i2p/data/TestData.java
new file mode 100644
index 0000000000..89f213c5a7
--- /dev/null
+++ b/core/java/test/net/i2p/data/TestData.java
@@ -0,0 +1,150 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+import net.i2p.util.Log;
+
+/**
+ * Test harness for loading / storing data structures
+ *
+ * @author jrandom
+ */
+public class TestData {
+    private final static Log _log = new Log(TestData.class);
+    private final static String HELP = "\nUsage: TestData generate objectType outFile\n" +
+                                       "       TestData display objectType inFile\n" +
+                                       "       TestData test objectType tempFile\n" +
+                                       "Known types: ";
+    
+    private final static String OP_GENERATE = "generate";
+    private final static String OP_DISPLAY = "display";
+    private final static String OP_TEST = "test";
+    
+    private final static HashMap _generators;
+    private final static HashMap _printers;
+    static {
+        _generators = new HashMap();
+        _generators.put("NullType", new TestDataGenerator() { public byte[] getData() { return new byte[1]; } });
+        
+        _printers = new HashMap();
+        _printers.put("NullType", new TestDataPrinter() { public String testData(InputStream in) { return "Null data read successfully"; } });
+    }
+    
+    static void registerTest(StructureTest test, String name) {
+        registerGenerator(test, name);
+        registerPrinter(test, name);
+    }
+    static void registerGenerator(TestDataGenerator test, String name) {
+        _generators.put(name, test);
+    }
+    static void registerPrinter(TestDataPrinter test, String name) {
+        _printers.put(name, test);
+    }
+    
+    public static void main(String args[]) {
+        if (args.length < 1) {
+            showHelp();
+            return;
+        }
+        
+        if (OP_GENERATE.equalsIgnoreCase(args[0])) {
+            validateTest(args[1]);
+            if (args.length != 3) {
+                showHelp();
+                return;
+            }
+            generate(args[1], args[2]);
+            return;
+        } else if (OP_DISPLAY.equalsIgnoreCase(args[0])) {
+            validateTest(args[1]);
+            if (args.length != 3) {
+                showHelp();
+                return;
+            }
+            display(args[1], args[2]);
+        } else if (OP_TEST.equalsIgnoreCase(args[0])) {
+            validateTest(args[1]);
+            if (args.length != 3) {
+                showHelp();
+                return;
+            }
+            generate(args[1], args[2]);
+            display(args[1], args[2]);
+        } else {
+            showHelp();
+        }
+	try { Thread.sleep(2000); } catch (InterruptedException ie) {}
+    }
+    
+    private static void validateTest(String objectType) {
+        try {
+            String clsName = TestData.class.getPackage().getName() + "." + objectType + "Test";
+            Class.forName(clsName);
+        } catch (Throwable t) {
+            _log.error("Error validating the object type", t);
+        }
+    }
+    
+    public static void generate(String objectType, String outFile) {
+        TestDataGenerator gen = (TestDataGenerator)_generators.get(objectType);
+        byte[] data = gen.getData();
+	if (data == null) {
+	    _log.error("Error generating the data.  fail");
+	    return;
+	}
+        try {
+            File f = new File(outFile);
+            FileOutputStream out = new FileOutputStream(f);
+            out.write(data);
+            out.flush();
+            out.close();
+            _log.debug("Wrote the file out to " + f.getAbsolutePath());
+        } catch (IOException ioe) {
+            _log.error("Error writing out the object", ioe);
+        }
+    }
+    
+    public static void display(String type, String inFile) {
+        try {
+            File f = new File(inFile);
+            FileInputStream in = new FileInputStream(f);
+            TestDataPrinter printer = (TestDataPrinter)_printers.get(type);
+            String display = printer.testData(in);
+            in.close();
+            _log.info("Displaying " + inFile + " of type: " + type);
+            _log.info(display);
+        } catch (IOException ioe) {
+            _log.error("Error reading the file to display", ioe);
+        }
+    }
+        
+    private static String listTypes() {
+        StringBuffer buf = new StringBuffer();
+        for (Iterator iter = _generators.keySet().iterator(); iter.hasNext(); ) {
+            String type = (String)iter.next();
+            buf.append(type);
+            if (iter.hasNext())
+                buf.append(", ");
+        }
+        return buf.toString();
+    }
+    
+    public static void showHelp() {
+        _log.info(HELP+listTypes());
+    }
+}
diff --git a/core/java/test/net/i2p/data/TestDataGenerator.java b/core/java/test/net/i2p/data/TestDataGenerator.java
new file mode 100644
index 0000000000..371b72d6c3
--- /dev/null
+++ b/core/java/test/net/i2p/data/TestDataGenerator.java
@@ -0,0 +1,16 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+/**
+ * Defines a method to create the serialization of an object
+ */
+public interface TestDataGenerator {
+    public byte[] getData();
+}
diff --git a/core/java/test/net/i2p/data/TestDataPrinter.java b/core/java/test/net/i2p/data/TestDataPrinter.java
new file mode 100644
index 0000000000..b95ac8531c
--- /dev/null
+++ b/core/java/test/net/i2p/data/TestDataPrinter.java
@@ -0,0 +1,18 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.io.InputStream;
+
+/**
+ * Defines a method to read the serialization of an object and display it
+ */
+public interface TestDataPrinter {
+    public String testData(InputStream stream);
+}
diff --git a/core/java/test/net/i2p/data/TunnelIdTest.java b/core/java/test/net/i2p/data/TunnelIdTest.java
new file mode 100644
index 0000000000..0dbf8c3ee5
--- /dev/null
+++ b/core/java/test/net/i2p/data/TunnelIdTest.java
@@ -0,0 +1,30 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataStructure;
+import net.i2p.data.TunnelId;
+
+/**
+ * Test harness for loading / storing TunnelId objects
+ *
+ * @author jrandom
+ */
+class TunnelIdTest extends StructureTest {
+    static {
+        TestData.registerTest(new TunnelIdTest(), "TunnelId");
+    }
+    public DataStructure createDataStructure() throws DataFormatException {
+        TunnelId id = new TunnelId();
+        id.setTunnelId(42);
+        return id; 
+    }
+    public DataStructure createStructureToRead() { return new TunnelId(); }
+}
diff --git a/core/java/test/net/i2p/data/UnsignedIntegerTest.java b/core/java/test/net/i2p/data/UnsignedIntegerTest.java
new file mode 100644
index 0000000000..50ace12c30
--- /dev/null
+++ b/core/java/test/net/i2p/data/UnsignedIntegerTest.java
@@ -0,0 +1,59 @@
+package net.i2p.data;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain 
+ * with no warranty of any kind, either expressed or implied.  
+ * It probably won't make your computer catch on fire, or eat 
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import net.i2p.data.DataFormatException;
+import net.i2p.data.DataHelper;
+import net.i2p.util.Log;
+
+/**
+ * Test harness for the numerical structure (in java, an UnsignedInteger)
+ *
+ * @author jrandom
+ */
+class UnsignedIntegerTest implements TestDataGenerator, TestDataPrinter {
+    static {
+        TestData.registerGenerator(new UnsignedIntegerTest(), "UnsignedInteger");
+        TestData.registerPrinter(new UnsignedIntegerTest(), "UnsignedInteger");
+    }
+    private static final Log _log = new Log(UnsignedIntegerTest.class);
+    
+    public byte[] getData() {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            DataHelper.writeLong(baos, 4, 42);
+            return baos.toByteArray();
+        } catch (DataFormatException dfe) {
+            _log.error("Error writing the integer", dfe);
+            return null;
+        } catch (IOException ioe) {
+            _log.error("Error writing the integer", ioe);
+            return null;
+        }
+    }
+    
+    public String testData(InputStream inputStream) {
+        try {
+            long val = DataHelper.readLong(inputStream, 4);
+            return ""+val;
+        } catch (DataFormatException dfe) {
+            _log.error("Error reading the integer", dfe);
+            return null;
+        } catch (IOException ioe) {
+            _log.error("Error reading the integer", ioe);
+            return null;
+        }
+    }
+    
+    
+}
-- 
GitLab