beginning of format, updated imports. (shendaras)

This commit is contained in:
shendaras
2004-04-10 11:39:00 +00:00
committed by zzz
parent dbe5dea525
commit 8a8e68146f
233 changed files with 10086 additions and 8697 deletions

View File

@@ -1,7 +1,12 @@
package net.i2p.httptunnel; package net.i2p.httptunnel;
import java.util.*; import java.util.ArrayList;
import net.i2p.client.streaming.*; import java.util.Arrays;
import java.util.HashMap;
import java.util.Properties;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
/** /**
* Produces SocketManagers in a thread and gives them to those who * Produces SocketManagers in a thread and gives them to those who

View File

@@ -1,9 +1,11 @@
package net.i2p.httptunnel.filter; package net.i2p.httptunnel.filter;
import net.i2p.util.Log; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.io.*; import net.i2p.util.Log;
import java.util.*;
/** /**
* Chain multiple filters. Decorator pattern... * Chain multiple filters. Decorator pattern...

View File

@@ -50,7 +50,6 @@ import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import net.i2p.I2PException; import net.i2p.I2PException;
import net.i2p.i2ptunnel.socks.I2PSOCKSTunnel;
import net.i2p.client.I2PClient; import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory; import net.i2p.client.I2PClientFactory;
import net.i2p.client.naming.NamingService; import net.i2p.client.naming.NamingService;
@@ -58,6 +57,7 @@ import net.i2p.data.Base64;
import net.i2p.data.DataFormatException; import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Destination; import net.i2p.data.Destination;
import net.i2p.i2ptunnel.socks.I2PSOCKSTunnel;
import net.i2p.util.EventDispatcher; import net.i2p.util.EventDispatcher;
import net.i2p.util.EventDispatcherImpl; import net.i2p.util.EventDispatcherImpl;
import net.i2p.util.Log; import net.i2p.util.Log;

View File

@@ -20,8 +20,8 @@ import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.client.streaming.I2PSocketOptions; import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.Destination; import net.i2p.data.Destination;
import net.i2p.util.EventDispatcher; import net.i2p.util.EventDispatcher;
import net.i2p.util.Log;
import net.i2p.util.I2PThread; import net.i2p.util.I2PThread;
import net.i2p.util.Log;
public abstract class I2PTunnelClientBase extends I2PTunnelTask public abstract class I2PTunnelClientBase extends I2PTunnelTask
implements Runnable { implements Runnable {

View File

@@ -13,9 +13,9 @@ import java.util.HashMap;
import net.i2p.client.I2PSession; import net.i2p.client.I2PSession;
import net.i2p.client.streaming.I2PSocket; import net.i2p.client.streaming.I2PSocket;
import net.i2p.util.Log;
import net.i2p.util.Clock; import net.i2p.util.Clock;
import net.i2p.util.I2PThread; import net.i2p.util.I2PThread;
import net.i2p.util.Log;
public class I2PTunnelRunner extends I2PThread { public class I2PTunnelRunner extends I2PThread {
private final static Log _log = new Log(I2PTunnelRunner.class); private final static Log _log = new Log(I2PTunnelRunner.class);

View File

@@ -11,7 +11,8 @@ import java.io.InputStream;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
import java.net.SocketException; import java.net.SocketException;
import java.util.*; import java.util.Iterator;
import java.util.Properties;
import net.i2p.I2PException; import net.i2p.I2PException;
import net.i2p.client.I2PClient; import net.i2p.client.I2PClient;
@@ -22,8 +23,8 @@ import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory; import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.data.Base64; import net.i2p.data.Base64;
import net.i2p.util.EventDispatcher; import net.i2p.util.EventDispatcher;
import net.i2p.util.Log;
import net.i2p.util.I2PThread; import net.i2p.util.I2PThread;
import net.i2p.util.Log;
public class I2PTunnelServer extends I2PTunnelTask public class I2PTunnelServer extends I2PTunnelTask
implements Runnable { implements Runnable {

View File

@@ -14,8 +14,8 @@ import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocketManager; import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.data.Destination; import net.i2p.data.Destination;
import net.i2p.util.EventDispatcher; import net.i2p.util.EventDispatcher;
import net.i2p.util.Log;
import net.i2p.util.I2PThread; import net.i2p.util.I2PThread;
import net.i2p.util.Log;
public class I2Ping extends I2PTunnelTask implements Runnable { public class I2Ping extends I2PTunnelTask implements Runnable {
private final static Log _log = new Log(I2Ping.class); private final static Log _log = new Log(I2Ping.class);

View File

@@ -3,15 +3,14 @@
*/ */
package net.i2p.i2ptunnel; package net.i2p.i2ptunnel;
import net.i2p.util.Log; import java.io.BufferedReader;
import java.util.StringTokenizer;
import java.net.Socket;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.Socket;
import java.util.StringTokenizer;
import net.i2p.util.Log;
/** /**
* Runner thread that reads commands from the socket and fires off commands to * Runner thread that reads commands from the socket and fires off commands to

View File

@@ -8,14 +8,13 @@ package net.i2p.i2ptunnel.socks;
import java.net.Socket; import java.net.Socket;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket; import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager; import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory; import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.client.streaming.I2PSocketOptions; import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.DataFormatException; import net.i2p.data.DataFormatException;
import net.i2p.I2PException;
import net.i2p.i2ptunnel.I2PTunnel; import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.util.Log; import net.i2p.util.Log;
/** /**

View File

@@ -1,10 +1,10 @@
package net.i2p.client.streaming; package net.i2p.client.streaming;
import net.i2p.data.Destination; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException;
import net.i2p.data.Destination;
/** /**
* Minimalistic adapter between the socket api and I2PTunnel's way. * Minimalistic adapter between the socket api and I2PTunnel's way.

View File

@@ -8,8 +8,8 @@ import java.io.OutputStream;
import net.i2p.I2PException; import net.i2p.I2PException;
import net.i2p.client.I2PSessionException; import net.i2p.client.I2PSessionException;
import net.i2p.data.Destination; import net.i2p.data.Destination;
import net.i2p.util.Log;
import net.i2p.util.I2PThread; import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/** /**
* Initial stub implementation for the socket * Initial stub implementation for the socket

View File

@@ -1,20 +1,19 @@
package net.i2p.client.streaming; package net.i2p.client.streaming;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.I2PSessionException;
import net.i2p.I2PException;
import net.i2p.data.Destination;
import net.i2p.util.Log;
import java.io.InputStream;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.Properties; import java.util.Properties;
import net.i2p.I2PException;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.data.Destination;
import net.i2p.util.Log;
/** /**
* Simplify the creation of I2PSession and transient I2P Destination objects if * Simplify the creation of I2PSession and transient I2P Destination objects if
* necessary to create a socket manager. This class is most likely how classes * necessary to create a socket manager. This class is most likely how classes

View File

@@ -8,16 +8,16 @@ package net.i2p.phttprelay;
* *
*/ */
import net.i2p.util.Log;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import net.i2p.util.Log;
abstract class PHTTPRelayServlet extends HttpServlet { abstract class PHTTPRelayServlet extends HttpServlet {
private Log _log = new Log(getClass()); private Log _log = new Log(getClass());
protected String _baseDir; protected String _baseDir;

View File

@@ -8,8 +8,15 @@
* Licensed unter GNU General Public License. * Licensed unter GNU General Public License.
*/ */
import java.io.*; import java.io.BufferedReader;
import java.net.*; import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.ConnectException;
import java.net.Socket;
import java.net.SocketException;
/** /**
* The main engine for the EchoTester. * The main engine for the EchoTester.

View File

@@ -1,4 +1,5 @@
package net.i2p; package net.i2p;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -13,10 +14,11 @@ package net.i2p;
* *
*/ */
public class CoreVersion { public class CoreVersion {
public final static String ID = "$Revision: 1.33 $ $Date: 2004/04/04 13:40:34 $"; public final static String ID = "$Revision: 1.1 $ $Date: 2004/04/07 23:48:42 $";
public final static String VERSION = "0.3.0.3"; public final static String VERSION = "0.3.0.3";
public static void main(String args[]) { public static void main(String args[]) {
System.out.println("I2P Core version: " + VERSION); System.out.println("I2P Core version: " + VERSION);
System.out.println("ID: " + ID); System.out.println("ID: " + ID);
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p; package net.i2p;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -18,31 +19,32 @@ import java.io.PrintWriter;
*/ */
public class I2PException extends Exception { public class I2PException extends Exception {
private Throwable _source; private Throwable _source;
public I2PException() { public I2PException() {
this(null, null); this(null, null);
} }
public I2PException(String msg) { public I2PException(String msg) {
this(msg, null); this(msg, null);
} }
public I2PException(String msg, Throwable source) { public I2PException(String msg, Throwable source) {
super(msg); super(msg);
_source = source; _source = source;
} }
public void printStackTrace() { public void printStackTrace() {
if (_source != null) if (_source != null) _source.printStackTrace();
_source.printStackTrace();
super.printStackTrace(); super.printStackTrace();
} }
public void printStackTrace(PrintStream ps) { public void printStackTrace(PrintStream ps) {
if (_source != null) if (_source != null) _source.printStackTrace(ps);
_source.printStackTrace(ps); super.printStackTrace(ps);
super.printStackTrace(ps);
} }
public void printStackTrace(PrintWriter pw) { public void printStackTrace(PrintWriter pw) {
if (_source != null) if (_source != null) _source.printStackTrace(pw);
_source.printStackTrace(pw); super.printStackTrace(pw);
super.printStackTrace(pw);
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -26,10 +27,10 @@ import java.util.Properties;
import net.i2p.I2PException; import net.i2p.I2PException;
import net.i2p.data.DataFormatException; import net.i2p.data.DataFormatException;
import net.i2p.data.Destination; import net.i2p.data.Destination;
import net.i2p.util.Clock;
import net.i2p.util.I2PThread; import net.i2p.util.I2PThread;
import net.i2p.util.Log; import net.i2p.util.Log;
import net.i2p.util.LogManager; import net.i2p.util.LogManager;
import net.i2p.util.Clock;
/** /**
* ATalk - anonymous talk, demonstrating a trivial I2P usage scenario. * ATalk - anonymous talk, demonstrating a trivial I2P usage scenario.
@@ -59,27 +60,39 @@ public class ATalk implements I2PSessionListener, Runnable {
/** string that messages must begin with to be treated as files */ /** string that messages must begin with to be treated as files */
private final static String FILE_COMMAND = ".file: "; private final static String FILE_COMMAND = ".file: ";
/** the, erm, manual */ /** the, erm, manual */
private final static String MANUAL = private final static String MANUAL = "ATalk: Anonymous Talk, a demo program for the Invisible Internet Project SDK"
"ATalk: Anonymous Talk, a demo program for the Invisible Internet Project SDK" + NL + + NL
"To generate a new destination:" + NL + + "To generate a new destination:"
"\tATalk [fileToSavePrivateKeyIn] [fileToSavePublicKeyIn]" + NL + + NL
"To talk to another destination:" + NL + + "\tATalk [fileToSavePrivateKeyIn] [fileToSavePublicKeyIn]"
"\tATalk [myPrivateKeyFile] [peerPublicKey] [shouldLogToScreen]" + NL + + NL
"shouldLogToScreen is 'true' or 'false', depending on whether you want log info on the screen" + NL + + "To talk to another destination:"
"When talking to another destination, messages are sent after you hit return" + NL + + NL
"To send a file, send a message saying:" + NL + + "\tATalk [myPrivateKeyFile] [peerPublicKey] [shouldLogToScreen]"
"\t" + FILE_COMMAND + "[filenameToSend]" + NL + + NL
"The peer will then recieve the file and be notified of where it has been saved" + NL + + "shouldLogToScreen is 'true' or 'false', depending on whether you want log info on the screen"
"To end the talk session, enter a period on a line by itself and hit return" + NL; + NL
+ "When talking to another destination, messages are sent after you hit return"
+ NL
+ "To send a file, send a message saying:"
+ NL
+ "\t"
+ FILE_COMMAND
+ "[filenameToSend]"
+ NL
+ "The peer will then recieve the file and be notified of where it has been saved"
+ NL
+ "To end the talk session, enter a period on a line by itself and hit return"
+ NL;
public final static String PROP_CONFIG_LOCATION = "configFile"; public final static String PROP_CONFIG_LOCATION = "configFile";
private static final SimpleDateFormat _fmt = new SimpleDateFormat("hh:mm:ss.SSS"); private static final SimpleDateFormat _fmt = new SimpleDateFormat("hh:mm:ss.SSS");
/** Construct the talk engine, but don't connect yet */ /** Construct the talk engine, but don't connect yet */
public ATalk(String myKeyFile, String theirDestFile) { public ATalk(String myKeyFile, String theirDestFile) {
_myKeyFile = myKeyFile; _myKeyFile = myKeyFile;
_theirDestinationFile = theirDestFile; _theirDestinationFile = theirDestFile;
} }
/** Actually start up the connection to the I2P network. /** Actually start up the connection to the I2P network.
* Successful connect does not mean the peer is online or reachable. * Successful connect does not mean the peer is online or reachable.
* *
@@ -88,102 +101,102 @@ public class ATalk implements I2PSessionListener, Runnable {
* @throws I2PSessionException if there is a problem contacting the I2P router * @throws I2PSessionException if there is a problem contacting the I2P router
*/ */
public void connect() throws IOException, I2PSessionException, DataFormatException { public void connect() throws IOException, I2PSessionException, DataFormatException {
I2PClient client = I2PClientFactory.createClient(); I2PClient client = I2PClientFactory.createClient();
File myFile = new File(_myKeyFile); File myFile = new File(_myKeyFile);
Properties props = new Properties(); Properties props = new Properties();
String configLocation = System.getProperty(PROP_CONFIG_LOCATION, "atalk.config"); String configLocation = System.getProperty(PROP_CONFIG_LOCATION, "atalk.config");
try { try {
props.load(new FileInputStream(configLocation)); props.load(new FileInputStream(configLocation));
} catch (FileNotFoundException fnfe) { } catch (FileNotFoundException fnfe) {
_log.warn("Unable to load up the ATalk config file " + configLocation); _log.warn("Unable to load up the ATalk config file " + configLocation);
} }
// Provide any router or client API configuration here. // Provide any router or client API configuration here.
if (!props.containsKey(I2PClient.PROP_TCP_HOST)) if (!props.containsKey(I2PClient.PROP_TCP_HOST)) props.setProperty(I2PClient.PROP_TCP_HOST, "localhost");
props.setProperty(I2PClient.PROP_TCP_HOST, "localhost"); if (!props.containsKey(I2PClient.PROP_TCP_PORT)) props.setProperty(I2PClient.PROP_TCP_PORT, "7654");
if (!props.containsKey(I2PClient.PROP_TCP_PORT)) if (!props.containsKey(I2PClient.PROP_RELIABILITY))
props.setProperty(I2PClient.PROP_TCP_PORT, "7654"); props.setProperty(I2PClient.PROP_RELIABILITY,
if (!props.containsKey(I2PClient.PROP_RELIABILITY)) I2PClient.PROP_RELIABILITY_BEST_EFFORT);
props.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT); _session = client.createSession(new FileInputStream(myFile), props);
_session = client.createSession(new FileInputStream(myFile), props); _session.setSessionListener(this);
_session.setSessionListener(this); _session.connect();
_session.connect();
File peerDestFile = new File(_theirDestinationFile);
File peerDestFile = new File(_theirDestinationFile); _peerDestination = new Destination();
_peerDestination = new Destination(); _peerDestination.readBytes(new FileInputStream(peerDestFile));
_peerDestination.readBytes(new FileInputStream(peerDestFile)); return;
return;
} }
/** Actual bulk processing of the application, reading in user input, /** Actual bulk processing of the application, reading in user input,
* sending messages, and displaying results. When this function exits, the * sending messages, and displaying results. When this function exits, the
* application is complete. * application is complete.
* *
*/ */
public void run() { public void run() {
try { try {
connect(); connect();
_in = new BufferedReader(new InputStreamReader(System.in)); _in = new BufferedReader(new InputStreamReader(System.in));
_out = new BufferedWriter(new OutputStreamWriter(System.out)); _out = new BufferedWriter(new OutputStreamWriter(System.out));
_out.write("Starting up anonymous talk session"+NL); _out.write("Starting up anonymous talk session" + NL);
while (true) { while (true) {
String line = _in.readLine(); String line = _in.readLine();
if ( (line == null) || (line.trim().length() <= 0) ) if ((line == null) || (line.trim().length() <= 0)) continue;
continue; if (".".equals(line)) {
if (".".equals(line)) { boolean ok = _session.sendMessage(_peerDestination, ("Peer disconnected at " + now()).getBytes());
boolean ok = _session.sendMessage(_peerDestination, ("Peer disconnected at " + now()).getBytes()); // ignore ok, we're closing
// ignore ok, we're closing break;
break; }
} if (line.startsWith(FILE_COMMAND) && (line.trim().length() > FILE_COMMAND.length())) {
if (line.startsWith(FILE_COMMAND) && (line.trim().length() > FILE_COMMAND.length())) { try {
try { String file = line.substring(FILE_COMMAND.length());
String file = line.substring(FILE_COMMAND.length()); boolean sent = sendFile(file);
boolean sent = sendFile(file); if (!sent) {
if (!sent) { _out.write("Failed sending the file: " + file + NL);
_out.write("Failed sending the file: " + file+NL); }
} } catch (IOException ioe) {
} catch (IOException ioe) { _out.write("Error sending the file: " + ioe.getMessage() + NL);
_out.write("Error sending the file: " + ioe.getMessage()+NL); _log.error("Error sending the file", ioe);
_log.error("Error sending the file", ioe); }
} } else {
} else { boolean ok = _session.sendMessage(_peerDestination, ("[" + now() + "] " + line).getBytes());
boolean ok = _session.sendMessage(_peerDestination, ("[" + now() + "] " + line).getBytes()); if (!ok) {
if (!ok) { _out.write("Failed sending message. Peer disconnected?" + NL);
_out.write("Failed sending message. Peer disconnected?" + NL); }
} }
} }
} } catch (IOException ioe) {
} catch (IOException ioe) { _log.error("Error running", ioe);
_log.error("Error running", ioe); } catch (I2PSessionException ise) {
} catch (I2PSessionException ise) { _log.error("Error communicating", ise);
_log.error("Error communicating", ise); } catch (DataFormatException dfe) {
} catch (DataFormatException dfe) { _log.error("Peer destination file is not valid", dfe);
_log.error("Peer destination file is not valid", dfe); } finally {
} finally { try {
try { _log.debug("Exiting anonymous talk session");
_log.debug("Exiting anonymous talk session"); if (_out != null) _out.write("Exiting anonymous talk session");
if (_out != null) } catch (IOException ioe) {
_out.write("Exiting anonymous talk session"); // ignored
} catch (IOException ioe) { }
// ignored if (_session != null) {
} try {
if (_session != null) { _session.destroySession();
try { } catch (I2PSessionException ise) {
_session.destroySession(); // ignored
} catch (I2PSessionException ise) { }
// ignored }
} try {
} Thread.sleep(5000);
try { Thread.sleep(5000); } catch (InterruptedException ie) {} } catch (InterruptedException ie) {
} }
}
} }
private String now() { private String now() {
Date now = new Date(Clock.getInstance().now()); Date now = new Date(Clock.getInstance().now());
return _fmt.format(now); return _fmt.format(now);
} }
/** Send the given file to the current peer. This works by sending a message /** Send the given file to the current peer. This works by sending a message
* saying ".file: filename\nbodyOfFile", where filename is the name of the file * saying ".file: filename\nbodyOfFile", where filename is the name of the file
* (which the recipient will be shown), and the bodyOfFile is the set of raw * (which the recipient will be shown), and the bodyOfFile is the set of raw
@@ -193,35 +206,34 @@ public class ATalk implements I2PSessionListener, Runnable {
* @return false if the file could not be sent to the peer * @return false if the file could not be sent to the peer
*/ */
private boolean sendFile(String filename) throws IOException, I2PSessionException { private boolean sendFile(String filename) throws IOException, I2PSessionException {
_log.debug("Sending file [" + filename + "]"); _log.debug("Sending file [" + filename + "]");
ByteArrayOutputStream baos = new ByteArrayOutputStream(4096); ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
baos.write((FILE_COMMAND + filename+"\n").getBytes()); baos.write((FILE_COMMAND + filename + "\n").getBytes());
FileInputStream fin = new FileInputStream(filename); FileInputStream fin = new FileInputStream(filename);
byte buf[] = new byte[4096]; byte buf[] = new byte[4096];
try { try {
while (true) { while (true) {
int len = fin.read(buf); int len = fin.read(buf);
if (len == -1) if (len == -1) break;
break; baos.write(buf, 0, len);
baos.write(buf, 0, len); }
} } catch (IOException ioe) {
} catch (IOException ioe) { _log.debug("Failed reading the file", ioe);
_log.debug("Failed reading the file", ioe); return false;
return false; }
} baos.close();
baos.close(); byte val[] = baos.toByteArray();
byte val[] = baos.toByteArray(); _log.debug("Sending " + filename + " with a full payload of " + val.length);
_log.debug("Sending " + filename + " with a full payload of " + val.length); try {
try { boolean rv = _session.sendMessage(_peerDestination, val);
boolean rv = _session.sendMessage(_peerDestination, val); _log.debug("Sending " + filename + " complete: rv = " + rv);
_log.debug("Sending " + filename + " complete: rv = " + rv); return rv;
return rv; } catch (Throwable t) {
} catch (Throwable t) { _log.error("Error sending file", t);
_log.error("Error sending file", t); return false;
return false; }
}
} }
/** I2PSessionListener.messageAvailable requires this method to be called whenever /** I2PSessionListener.messageAvailable requires this method to be called whenever
* I2P wants to tell the session that a message is available. ATalk always grabs * I2P wants to tell the session that a message is available. ATalk always grabs
* the message immediately and either processes it as a "send file" command (passing * the message immediately and either processes it as a "send file" command (passing
@@ -230,27 +242,27 @@ public class ATalk implements I2PSessionListener, Runnable {
* *
*/ */
public void messageAvailable(I2PSession session, int msgId, long size) { public void messageAvailable(I2PSession session, int msgId, long size) {
_log.debug("Message available: id = " + msgId + " size = " + size); _log.debug("Message available: id = " + msgId + " size = " + size);
try { try {
byte msg[] = session.receiveMessage(msgId); byte msg[] = session.receiveMessage(msgId);
// inefficient way to just read the first line of text, but its easy // inefficient way to just read the first line of text, but its easy
BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(msg))); BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(msg)));
String line = reader.readLine(); String line = reader.readLine();
if (line.startsWith(FILE_COMMAND)) { if (line.startsWith(FILE_COMMAND)) {
handleRecieveFile(line, msg); handleRecieveFile(line, msg);
} else { } else {
// not a file command, so just plop 'er out on the screen // not a file command, so just plop 'er out on the screen
_out.write(now() + " --> " + new String(msg)); _out.write(now() + " --> " + new String(msg));
_out.write(NL); _out.write(NL);
_out.flush(); _out.flush();
} }
} catch (I2PSessionException ise) { } catch (I2PSessionException ise) {
_log.error("Error fetching available message", ise); _log.error("Error fetching available message", ise);
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error writing out the message", ioe); _log.error("Error writing out the message", ioe);
} }
} }
/** React to a file being sent our way from the peer via {@link #sendFile sendFile} /** React to a file being sent our way from the peer via {@link #sendFile sendFile}
* by saving the file to a temporary location and displaying where, what, and how large * by saving the file to a temporary location and displaying where, what, and how large
* it is. * it is.
@@ -261,48 +273,56 @@ public class ATalk implements I2PSessionListener, Runnable {
* @param msg the entire message recieved, including the firstline * @param msg the entire message recieved, including the firstline
*/ */
private void handleRecieveFile(String firstline, byte msg[]) throws IOException { private void handleRecieveFile(String firstline, byte msg[]) throws IOException {
_log.debug("handleRecieveFile called"); _log.debug("handleRecieveFile called");
File f = File.createTempFile("recieve", ".dat", new File(".")); File f = File.createTempFile("recieve", ".dat", new File("."));
FileOutputStream fos = new FileOutputStream(f); FileOutputStream fos = new FileOutputStream(f);
int lineLen = firstline.getBytes().length+"\n".getBytes().length; int lineLen = firstline.getBytes().length + "\n".getBytes().length;
int lenToCopy = msg.length - lineLen; int lenToCopy = msg.length - lineLen;
byte buf[] = new byte[lenToCopy]; byte buf[] = new byte[lenToCopy];
System.arraycopy(msg, lineLen, buf, 0, lenToCopy); System.arraycopy(msg, lineLen, buf, 0, lenToCopy);
fos.write(buf); fos.write(buf);
fos.close(); fos.close();
String name = firstline.substring(FILE_COMMAND.length()); String name = firstline.substring(FILE_COMMAND.length());
_out.write("Recieved a file called [" + name + "] of size [" + lenToCopy + "] bytes, saved as [" + f.getAbsolutePath() + "]" + NL); _out.write("Recieved a file called [" + name + "] of size [" + lenToCopy + "] bytes, saved as ["
_out.flush(); + f.getAbsolutePath() + "]" + NL);
_out.flush();
} }
/** driver */ /** driver */
public static void main(String args[]) { public static void main(String args[]) {
if (args.length == 2) { if (args.length == 2) {
String myKeyFile = args[0]; String myKeyFile = args[0];
String myDestinationFile = args[1]; String myDestinationFile = args[1];
boolean success = generateKeys(myKeyFile, myDestinationFile); boolean success = generateKeys(myKeyFile, myDestinationFile);
if (success) if (success)
_log.debug("Keys generated (private key file: " + myKeyFile + " destination file: " + myDestinationFile + ")"); _log.debug("Keys generated (private key file: " + myKeyFile + " destination file: " + myDestinationFile
else + ")");
_log.debug("Keys generation failed"); else
try { Thread.sleep(5000); } catch (InterruptedException ie) {} _log.debug("Keys generation failed");
} else if (args.length == 3) { try {
_log.debug("Starting chat"); Thread.sleep(5000);
String myKeyfile = args[0]; } catch (InterruptedException ie) {
String peerDestFile = args[1]; }
String shouldLog = args[2]; } else if (args.length == 3) {
if (Boolean.TRUE.toString().equalsIgnoreCase(shouldLog)) _log.debug("Starting chat");
LogManager.getInstance().setDisplayOnScreen(true); String myKeyfile = args[0];
else String peerDestFile = args[1];
LogManager.getInstance().setDisplayOnScreen(false); String shouldLog = args[2];
String logFile = args[2]; if (Boolean.TRUE.toString().equalsIgnoreCase(shouldLog))
Thread talkThread = new I2PThread(new ATalk(myKeyfile, peerDestFile)); LogManager.getInstance().setDisplayOnScreen(true);
talkThread.start(); else
} else { LogManager.getInstance().setDisplayOnScreen(false);
System.out.println(MANUAL); String logFile = args[2];
try { Thread.sleep(5000); } catch (InterruptedException ie) {} Thread talkThread = new I2PThread(new ATalk(myKeyfile, peerDestFile));
System.exit(-1); talkThread.start();
} } else {
System.out.println(MANUAL);
try {
Thread.sleep(5000);
} catch (InterruptedException ie) {
}
System.exit(-1);
}
} }
/** Generate a new Destination, saving that destination and the associated /** Generate a new Destination, saving that destination and the associated
@@ -314,25 +334,33 @@ public class ATalk implements I2PSessionListener, Runnable {
* @param destinationFile file in which the Destination is serialized in * @param destinationFile file in which the Destination is serialized in
*/ */
private static boolean generateKeys(String privKeyFile, String destinationFile) { private static boolean generateKeys(String privKeyFile, String destinationFile) {
try { try {
Destination d = I2PClientFactory.createClient().createDestination(new FileOutputStream(privKeyFile)); Destination d = I2PClientFactory.createClient().createDestination(new FileOutputStream(privKeyFile));
FileOutputStream fos = new FileOutputStream(destinationFile); FileOutputStream fos = new FileOutputStream(destinationFile);
d.writeBytes(fos); d.writeBytes(fos);
fos.flush(); fos.flush();
fos.close(); fos.close();
return true; return true;
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error generating keys", ioe); _log.error("Error generating keys", ioe);
} catch (I2PException ipe) { } catch (I2PException ipe) {
_log.error("Error generating keys", ipe); _log.error("Error generating keys", ipe);
} }
return false; return false;
} }
/** required by {@link I2PSessionListener I2PSessionListener} to notify of disconnect */ /** required by {@link I2PSessionListener I2PSessionListener} to notify of disconnect */
public void disconnected(I2PSession session) { _log.debug("Disconnected"); } public void disconnected(I2PSession session) {
_log.debug("Disconnected");
}
/** required by {@link I2PSessionListener I2PSessionListener} to notify of error */ /** required by {@link I2PSessionListener I2PSessionListener} to notify of error */
public void errorOccurred(I2PSession session, String message, Throwable error) { _log.debug("Error occurred: " + message, error); } public void errorOccurred(I2PSession session, String message, Throwable error) {
_log.debug("Error occurred: " + message, error);
}
/** required by {@link I2PSessionListener I2PSessionListener} to notify of abuse */ /** required by {@link I2PSessionListener I2PSessionListener} to notify of abuse */
public void reportAbuse(I2PSession session, int severity) { _log.debug("Abuse reported of severity " + severity); } public void reportAbuse(I2PSession session, int severity) {
_log.debug("Abuse reported of severity " + severity);
}
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,47 +9,42 @@ package net.i2p.client;
* *
*/ */
import net.i2p.util.Log; import java.io.IOException;
import net.i2p.util.Clock; import java.io.OutputStream;
import java.net.Socket;
import net.i2p.data.i2cp.I2CPMessage; import java.util.Collections;
import net.i2p.data.i2cp.I2CPMessageReader; import java.util.Date;
import net.i2p.data.i2cp.I2CPMessageException;
import net.i2p.data.i2cp.SessionStatusMessage;
import net.i2p.data.i2cp.SendMessageMessage;
import net.i2p.data.i2cp.CreateSessionMessage;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.data.i2cp.DisconnectMessage;
import net.i2p.data.i2cp.ReceiveMessageBeginMessage;
import net.i2p.data.i2cp.ReceiveMessageEndMessage;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.data.i2cp.RequestLeaseSetMessage;
import net.i2p.data.i2cp.SessionId;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.SessionConfig;
import net.i2p.data.Destination;
import net.i2p.data.Payload;
import net.i2p.data.TunnelId;
import net.i2p.data.RouterIdentity;
import net.i2p.data.PublicKey;
import net.i2p.data.SigningPublicKey;
import net.i2p.data.Certificate;
import net.i2p.crypto.KeyGenerator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.HashSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.Date;
import java.net.Socket; import net.i2p.crypto.KeyGenerator;
import net.i2p.data.Certificate;
import java.io.OutputStream; import net.i2p.data.Destination;
import java.io.IOException; import net.i2p.data.Payload;
import net.i2p.data.PublicKey;
import net.i2p.data.RouterIdentity;
import net.i2p.data.SigningPublicKey;
import net.i2p.data.TunnelId;
import net.i2p.data.i2cp.CreateSessionMessage;
import net.i2p.data.i2cp.DisconnectMessage;
import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.I2CPMessageException;
import net.i2p.data.i2cp.I2CPMessageReader;
import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.data.i2cp.ReceiveMessageBeginMessage;
import net.i2p.data.i2cp.ReceiveMessageEndMessage;
import net.i2p.data.i2cp.RequestLeaseSetMessage;
import net.i2p.data.i2cp.SendMessageMessage;
import net.i2p.data.i2cp.SessionConfig;
import net.i2p.data.i2cp.SessionId;
import net.i2p.data.i2cp.SessionStatusMessage;
import net.i2p.util.Clock;
import net.i2p.util.Log;
/** /**
* Run the server side of a connection as part of the TestServer. This class * Run the server side of a connection as part of the TestServer. This class
@@ -83,31 +79,49 @@ class ConnectionRunner implements I2CPMessageReader.I2CPMessageEventListener {
/** next available message id */ /** next available message id */
private static int _messageId = 0; private static int _messageId = 0;
private SessionConfig _config; private SessionConfig _config;
private Object _sessionIdLock = new Object(); private Object _sessionIdLock = new Object();
private Object _messageIdLock = new Object(); private Object _messageIdLock = new Object();
// this *should* be mod 65536, but UnsignedInteger is still b0rked. FIXME // this *should* be mod 65536, but UnsignedInteger is still b0rked. FIXME
protected int getNextSessionId() { synchronized (_sessionIdLock) { int id = (++_id)%32767; _id = id; return id; } } protected int getNextSessionId() {
synchronized (_sessionIdLock) {
int id = (++_id) % 32767;
_id = id;
return id;
}
}
// this *should* be mod 65536, but UnsignedInteger is still b0rked. FIXME // this *should* be mod 65536, but UnsignedInteger is still b0rked. FIXME
protected int getNextMessageId() { synchronized (_messageIdLock) { int id = (++_messageId)%32767; _messageId = id; return id; } } protected int getNextMessageId() {
protected SessionId getSessionId() { return _sessionId; } synchronized (_messageIdLock) {
int id = (++_messageId) % 32767;
_messageId = id;
return id;
}
}
protected SessionId getSessionId() {
return _sessionId;
}
protected ConnectionRunner getRunner(Destination dest) { protected ConnectionRunner getRunner(Destination dest) {
return (ConnectionRunner)_connections.get(dest); return (ConnectionRunner) _connections.get(dest);
} }
protected Set getRunnerDestinations() { protected Set getRunnerDestinations() {
return new HashSet(_connections.keySet()); return new HashSet(_connections.keySet());
} }
/** /**
* Create a new runner against the given socket * Create a new runner against the given socket
* *
*/ */
public ConnectionRunner(Socket socket) { public ConnectionRunner(Socket socket) {
_socket = socket; _socket = socket;
_config = null; _config = null;
} }
/** /**
* Actually run the connection - listen for I2CP messages and respond. This * Actually run the connection - listen for I2CP messages and respond. This
* is the main driver for this class, though it gets all its meat from the * is the main driver for this class, though it gets all its meat from the
@@ -115,16 +129,16 @@ class ConnectionRunner implements I2CPMessageReader.I2CPMessageEventListener {
* *
*/ */
public void doYourThing() throws IOException { public void doYourThing() throws IOException {
I2CPMessageReader reader = new I2CPMessageReader(_socket.getInputStream(), this); I2CPMessageReader reader = new I2CPMessageReader(_socket.getInputStream(), this);
_out = _socket.getOutputStream(); _out = _socket.getOutputStream();
reader.startReading(); reader.startReading();
} }
/** /**
* Recieve notifiation that the peer disconnected * Recieve notifiation that the peer disconnected
*/ */
public void disconnected(I2CPMessageReader reader) { public void disconnected(I2CPMessageReader reader) {
_log.info("Disconnected"); _log.info("Disconnected");
} }
/** /**
@@ -132,166 +146,170 @@ class ConnectionRunner implements I2CPMessageReader.I2CPMessageEventListener {
* *
*/ */
public void messageReceived(I2CPMessageReader reader, I2CPMessage message) { public void messageReceived(I2CPMessageReader reader, I2CPMessage message) {
_log.info("Message recieved: \n" + message); _log.info("Message recieved: \n" + message);
switch (message.getType()) { switch (message.getType()) {
case CreateSessionMessage.MESSAGE_TYPE: case CreateSessionMessage.MESSAGE_TYPE:
handleCreateSession(reader, (CreateSessionMessage)message); handleCreateSession(reader, (CreateSessionMessage) message);
break; break;
case SendMessageMessage.MESSAGE_TYPE: case SendMessageMessage.MESSAGE_TYPE:
handleSendMessage(reader, (SendMessageMessage)message); handleSendMessage(reader, (SendMessageMessage) message);
break; break;
case ReceiveMessageBeginMessage.MESSAGE_TYPE: case ReceiveMessageBeginMessage.MESSAGE_TYPE:
handleReceiveBegin(reader, (ReceiveMessageBeginMessage)message); handleReceiveBegin(reader, (ReceiveMessageBeginMessage) message);
break; break;
case ReceiveMessageEndMessage.MESSAGE_TYPE: case ReceiveMessageEndMessage.MESSAGE_TYPE:
handleReceiveEnd(reader, (ReceiveMessageEndMessage)message); handleReceiveEnd(reader, (ReceiveMessageEndMessage) message);
break; break;
} }
} }
/** /**
* Handle a CreateSessionMessage * Handle a CreateSessionMessage
* *
*/ */
protected void handleCreateSession(I2CPMessageReader reader, CreateSessionMessage message) { protected void handleCreateSession(I2CPMessageReader reader, CreateSessionMessage message) {
if (message.getSessionConfig().verifySignature()) { if (message.getSessionConfig().verifySignature()) {
_log.debug("Signature verified correctly on create session message"); _log.debug("Signature verified correctly on create session message");
} else { } else {
_log.error("Signature verification *FAILED* on a create session message. Hijack attempt?"); _log.error("Signature verification *FAILED* on a create session message. Hijack attempt?");
DisconnectMessage msg = new DisconnectMessage(); DisconnectMessage msg = new DisconnectMessage();
msg.setReason("Invalid signature on CreateSessionMessage"); msg.setReason("Invalid signature on CreateSessionMessage");
try { try {
doSend(msg); doSend(msg);
} catch (I2CPMessageException ime) { } catch (I2CPMessageException ime) {
_log.error("Error writing out the disconnect message", ime); _log.error("Error writing out the disconnect message", ime);
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error writing out the disconnect message", ioe); _log.error("Error writing out the disconnect message", ioe);
} }
return; return;
} }
SessionStatusMessage msg = new SessionStatusMessage(); SessionStatusMessage msg = new SessionStatusMessage();
SessionId id = new SessionId(); SessionId id = new SessionId();
id.setSessionId(getNextSessionId()); // should be mod 65535, but UnsignedInteger isn't fixed yet. FIXME. id.setSessionId(getNextSessionId()); // should be mod 65535, but UnsignedInteger isn't fixed yet. FIXME.
_sessionId = id; _sessionId = id;
msg.setSessionId(id); msg.setSessionId(id);
msg.setStatus(SessionStatusMessage.STATUS_CREATED); msg.setStatus(SessionStatusMessage.STATUS_CREATED);
try { try {
doSend(msg); doSend(msg);
_connections.put(message.getSessionConfig().getDestination(), this); _connections.put(message.getSessionConfig().getDestination(), this);
_config = message.getSessionConfig(); _config = message.getSessionConfig();
sessionCreated(); sessionCreated();
} catch (I2CPMessageException ime) { } catch (I2CPMessageException ime) {
_log.error("Error writing out the session status message", ime); _log.error("Error writing out the session status message", ime);
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error writing out the session status message", ioe); _log.error("Error writing out the session status message", ioe);
} }
// lets also request a new fake lease // lets also request a new fake lease
RequestLeaseSetMessage rlsm = new RequestLeaseSetMessage(); RequestLeaseSetMessage rlsm = new RequestLeaseSetMessage();
rlsm.setEndDate(new Date(Clock.getInstance().now() + 60*60*1000)); rlsm.setEndDate(new Date(Clock.getInstance().now() + 60 * 60 * 1000));
rlsm.setSessionId(id); rlsm.setSessionId(id);
RouterIdentity ri = new RouterIdentity(); RouterIdentity ri = new RouterIdentity();
Object rikeys[] = KeyGenerator.getInstance().generatePKIKeypair(); Object rikeys[] = KeyGenerator.getInstance().generatePKIKeypair();
Object riSigningkeys[] = KeyGenerator.getInstance().generateSigningKeypair(); Object riSigningkeys[] = KeyGenerator.getInstance().generateSigningKeypair();
ri.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); ri.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null));
ri.setPublicKey((PublicKey)rikeys[0]); ri.setPublicKey((PublicKey) rikeys[0]);
ri.setSigningPublicKey((SigningPublicKey)riSigningkeys[0]); ri.setSigningPublicKey((SigningPublicKey) riSigningkeys[0]);
TunnelId tunnel = new TunnelId(); TunnelId tunnel = new TunnelId();
tunnel.setTunnelId(42); tunnel.setTunnelId(42);
rlsm.addEndpoint(ri, tunnel); rlsm.addEndpoint(ri, tunnel);
try { try {
doSend(rlsm); doSend(rlsm);
} catch (I2CPMessageException ime) { } catch (I2CPMessageException ime) {
_log.error("Error writing out the request for a lease set", ime); _log.error("Error writing out the request for a lease set", ime);
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error writing out the request for a lease set", ioe); _log.error("Error writing out the request for a lease set", ioe);
} }
} }
protected void sessionCreated() { } protected void sessionCreated() {
protected SessionConfig getConfig() { return _config; } }
protected SessionConfig getConfig() {
return _config;
}
/** /**
* Handle a SendMessageMessage * Handle a SendMessageMessage
* *
*/ */
protected void handleSendMessage(I2CPMessageReader reader, SendMessageMessage message) { protected void handleSendMessage(I2CPMessageReader reader, SendMessageMessage message) {
_log.debug("handleSendMessage called"); _log.debug("handleSendMessage called");
Payload payload = message.getPayload(); Payload payload = message.getPayload();
Destination dest = message.getDestination(); Destination dest = message.getDestination();
MessageId id = new MessageId(); MessageId id = new MessageId();
id.setMessageId(getNextMessageId()); id.setMessageId(getNextMessageId());
_log.debug("** Recieving message [" + id.getMessageId() + "] with payload: " + "[" + payload + "]"); _log.debug("** Recieving message [" + id.getMessageId() + "] with payload: " + "[" + payload + "]");
_messages.put(id, payload); _messages.put(id, payload);
MessageStatusMessage status = new MessageStatusMessage(); MessageStatusMessage status = new MessageStatusMessage();
status.setMessageId(id); status.setMessageId(id);
status.setSessionId(message.getSessionId()); status.setSessionId(message.getSessionId());
status.setSize(0L); status.setSize(0L);
status.setNonce(message.getNonce()); status.setNonce(message.getNonce());
status.setStatus(MessageStatusMessage.STATUS_SEND_ACCEPTED); status.setStatus(MessageStatusMessage.STATUS_SEND_ACCEPTED);
try { try {
doSend(status); doSend(status);
} catch (I2CPMessageException ime) { } catch (I2CPMessageException ime) {
_log.error("Error writing out the message status message", ime); _log.error("Error writing out the message status message", ime);
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error writing out the message status message", ioe); _log.error("Error writing out the message status message", ioe);
} }
distributeMessageToPeer(status, dest, id); distributeMessageToPeer(status, dest, id);
} }
/** /**
* distribute the message to the destination, passing on the appropriate status * distribute the message to the destination, passing on the appropriate status
* messages to the sender of the SendMessageMessage * messages to the sender of the SendMessageMessage
* *
*/ */
private void distributeMessageToPeer(MessageStatusMessage status, Destination dest, MessageId id) { private void distributeMessageToPeer(MessageStatusMessage status, Destination dest, MessageId id) {
ConnectionRunner runner = (ConnectionRunner)_connections.get(dest); ConnectionRunner runner = (ConnectionRunner) _connections.get(dest);
if (runner == null) { if (runner == null) {
distributeNonLocal(status, dest, id); distributeNonLocal(status, dest, id);
} else { } else {
distributeLocal(runner, status, dest, id); distributeLocal(runner, status, dest, id);
} }
_log.debug("Done handling send message"); _log.debug("Done handling send message");
} }
protected void distributeLocal(ConnectionRunner runner, MessageStatusMessage status, Destination dest, MessageId id) { protected void distributeLocal(ConnectionRunner runner, MessageStatusMessage status, Destination dest, MessageId id) {
if (runner.messageAvailable(id, 0L)) { if (runner.messageAvailable(id, 0L)) {
status.setStatus(MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS); status.setStatus(MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS);
status.setNonce(2); status.setNonce(2);
try { try {
doSend(status); doSend(status);
} catch (I2CPMessageException ime) { } catch (I2CPMessageException ime) {
_log.error("Error writing out the success status message", ime); _log.error("Error writing out the success status message", ime);
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error writing out the success status message", ioe); _log.error("Error writing out the success status message", ioe);
} }
_log.debug("Guaranteed success with the status message sent"); _log.debug("Guaranteed success with the status message sent");
} else { } else {
status.setStatus(MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE); status.setStatus(MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE);
try { try {
doSend(status); doSend(status);
} catch (I2CPMessageException ime) { } catch (I2CPMessageException ime) {
_log.error("Error writing out the failure status message", ime); _log.error("Error writing out the failure status message", ime);
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error writing out the failure status message", ioe); _log.error("Error writing out the failure status message", ioe);
} }
_log.debug("Guaranteed failure since messageAvailable failed"); _log.debug("Guaranteed failure since messageAvailable failed");
} }
} }
protected void distributeNonLocal(MessageStatusMessage status, Destination dest, MessageId id) { protected void distributeNonLocal(MessageStatusMessage status, Destination dest, MessageId id) {
status.setStatus(MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE); status.setStatus(MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE);
try { try {
doSend(status); doSend(status);
} catch (I2CPMessageException ime) { } catch (I2CPMessageException ime) {
_log.error("Error writing out the failure status message", ime); _log.error("Error writing out the failure status message", ime);
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error writing out the failure status message", ioe); _log.error("Error writing out the failure status message", ioe);
} }
_log.debug("Guaranteed failure!"); _log.debug("Guaranteed failure!");
} }
/** /**
* The client asked for a message, so we send it to them. This currently * The client asked for a message, so we send it to them. This currently
* does not do any security checking (like making sure they're the one to * does not do any security checking (like making sure they're the one to
@@ -300,30 +318,31 @@ class ConnectionRunner implements I2CPMessageReader.I2CPMessageEventListener {
* *
*/ */
public void handleReceiveBegin(I2CPMessageReader reader, ReceiveMessageBeginMessage message) { public void handleReceiveBegin(I2CPMessageReader reader, ReceiveMessageBeginMessage message) {
_log.debug("Handling recieve begin: id = " + message.getMessageId()); _log.debug("Handling recieve begin: id = " + message.getMessageId());
MessagePayloadMessage msg = new MessagePayloadMessage(); MessagePayloadMessage msg = new MessagePayloadMessage();
msg.setMessageId(message.getMessageId()); msg.setMessageId(message.getMessageId());
msg.setSessionId(_sessionId); msg.setSessionId(_sessionId);
Payload payload = (Payload)_messages.get(message.getMessageId()); Payload payload = (Payload) _messages.get(message.getMessageId());
if (payload == null) { if (payload == null) {
_log.error("Payload for message id [" + message.getMessageId() + "] is null! Unknown message id?", new Exception("Error, null payload")); _log.error("Payload for message id [" + message.getMessageId() + "] is null! Unknown message id?",
StringBuffer buf = new StringBuffer(); new Exception("Error, null payload"));
for (Iterator iter = _messages.keySet().iterator(); iter.hasNext(); ) { StringBuffer buf = new StringBuffer();
buf.append("messageId: ").append(iter.next()).append(", "); for (Iterator iter = _messages.keySet().iterator(); iter.hasNext();) {
} buf.append("messageId: ").append(iter.next()).append(", ");
_log.error("Known message IDs: " + buf.toString()); }
return; _log.error("Known message IDs: " + buf.toString());
} return;
msg.setPayload(payload); }
try { msg.setPayload(payload);
doSend(msg); try {
} catch (IOException ioe) { doSend(msg);
_log.error("Error delivering the payload", ioe); } catch (IOException ioe) {
} catch (I2CPMessageException ime) { _log.error("Error delivering the payload", ioe);
_log.error("Error delivering the payload", ime); } catch (I2CPMessageException ime) {
} _log.error("Error delivering the payload", ime);
}
} }
/** /**
* The client told us that the message has been recieved completely. This currently * The client told us that the message has been recieved completely. This currently
* does not do any security checking prior to removing the message from the * does not do any security checking prior to removing the message from the
@@ -331,45 +350,46 @@ class ConnectionRunner implements I2CPMessageReader.I2CPMessageEventListener {
* *
*/ */
public void handleReceiveEnd(I2CPMessageReader reader, ReceiveMessageEndMessage message) { public void handleReceiveEnd(I2CPMessageReader reader, ReceiveMessageEndMessage message) {
_messages.remove(message.getMessageId()); _messages.remove(message.getMessageId());
} }
/** /**
* Deliver notification to the client that the given message is available. * Deliver notification to the client that the given message is available.
* This is called from the ConnectionRunner the message was sent from. * This is called from the ConnectionRunner the message was sent from.
* *
*/ */
public boolean messageAvailable(MessageId id, long size) { public boolean messageAvailable(MessageId id, long size) {
MessageStatusMessage msg = new MessageStatusMessage(); MessageStatusMessage msg = new MessageStatusMessage();
msg.setMessageId(id); msg.setMessageId(id);
msg.setSessionId(_sessionId); msg.setSessionId(_sessionId);
msg.setSize(size); msg.setSize(size);
msg.setNonce(1); msg.setNonce(1);
msg.setStatus(MessageStatusMessage.STATUS_AVAILABLE); msg.setStatus(MessageStatusMessage.STATUS_AVAILABLE);
try { try {
doSend(msg); doSend(msg);
return true; return true;
} catch (I2CPMessageException ime) { } catch (I2CPMessageException ime) {
_log.error("Error writing out the message status message", ime); _log.error("Error writing out the message status message", ime);
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error writing out the message status message", ioe); _log.error("Error writing out the message status message", ioe);
} }
return false; return false;
} }
/** /**
* Handle notifiation that there was an error * Handle notifiation that there was an error
* *
*/ */
public void readError(I2CPMessageReader reader, Exception error) { public void readError(I2CPMessageReader reader, Exception error) {
_log.info("Error occurred", error); _log.info("Error occurred", error);
} }
private Object _sendLock = new Object(); private Object _sendLock = new Object();
protected void doSend(I2CPMessage msg) throws I2CPMessageException, IOException { protected void doSend(I2CPMessage msg) throws I2CPMessageException, IOException {
synchronized (_sendLock) { synchronized (_sendLock) {
msg.writeMessage(_out); msg.writeMessage(_out);
_out.flush(); _out.flush();
} }
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,7 +9,8 @@ package net.i2p.client;
* *
*/ */
import net.i2p.data.i2cp.*; import net.i2p.data.i2cp.DisconnectMessage;
import net.i2p.data.i2cp.I2CPMessage;
/** /**
* Handle I2CP disconnect messages from the router * Handle I2CP disconnect messages from the router
@@ -19,6 +21,7 @@ class DisconnectMessageHandler extends HandlerImpl {
public DisconnectMessageHandler() { public DisconnectMessageHandler() {
super(DisconnectMessage.MESSAGE_TYPE); super(DisconnectMessage.MESSAGE_TYPE);
} }
public void handleMessage(I2CPMessage message, I2PSessionImpl session) { public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
_log.debug("Handle message " + message); _log.debug("Handle message " + message);
session.destroySession(false); session.destroySession(false);

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -18,9 +19,13 @@ import net.i2p.util.Log;
abstract class HandlerImpl implements I2CPMessageHandler { abstract class HandlerImpl implements I2CPMessageHandler {
protected Log _log; protected Log _log;
private int _type; private int _type;
public HandlerImpl(int type) { public HandlerImpl(int type) {
_type = type; _type = type;
_log = new Log(getClass()); _log = new Log(getClass());
} }
public int getType() { return _type; }
} public int getType() {
return _type;
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -17,5 +18,6 @@ import net.i2p.data.i2cp.I2CPMessage;
*/ */
interface I2CPMessageHandler { interface I2CPMessageHandler {
public int getType(); public int getType();
public void handleMessage(I2CPMessage message, I2PSessionImpl session); public void handleMessage(I2CPMessage message, I2PSessionImpl session);
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -40,7 +41,7 @@ import net.i2p.util.RandomSource;
class I2CPMessageProducer { class I2CPMessageProducer {
private final static Log _log = new Log(I2CPMessageProducer.class); private final static Log _log = new Log(I2CPMessageProducer.class);
private final static RandomSource _rand = RandomSource.getInstance(); private final static RandomSource _rand = RandomSource.getInstance();
/** /**
* Send all the messages that a client needs to send to a router to establish * Send all the messages that a client needs to send to a router to establish
* a new session. * a new session.
@@ -58,68 +59,69 @@ class I2CPMessageProducer {
msg.setSessionConfig(cfg); msg.setSessionConfig(cfg);
session.sendMessage(msg); session.sendMessage(msg);
} }
/** /**
* Send messages to the router destroying the session and disconnecting * Send messages to the router destroying the session and disconnecting
* *
*/ */
public void disconnect(I2PSessionImpl session) throws I2PSessionException { public void disconnect(I2PSessionImpl session) throws I2PSessionException {
DestroySessionMessage dmsg = new DestroySessionMessage(); DestroySessionMessage dmsg = new DestroySessionMessage();
dmsg.setSessionId(session.getSessionId()); dmsg.setSessionId(session.getSessionId());
session.sendMessage(dmsg); session.sendMessage(dmsg);
// use DisconnectMessage only if we fail and drop connection... // use DisconnectMessage only if we fail and drop connection...
// todo: update the code to fire off DisconnectMessage on socket error // todo: update the code to fire off DisconnectMessage on socket error
//DisconnectMessage msg = new DisconnectMessage(); //DisconnectMessage msg = new DisconnectMessage();
//msg.setReason("Destroy called"); //msg.setReason("Destroy called");
//session.sendMessage(msg); //session.sendMessage(msg);
} }
/** /**
* Package up and send the payload to the router for delivery * Package up and send the payload to the router for delivery
* *
*/ */
public void sendMessage(I2PSessionImpl session, Destination dest, long nonce, byte[] payload, SessionTag tag, SessionKey key, Set tags, SessionKey newKey) throws I2PSessionException { public void sendMessage(I2PSessionImpl session, Destination dest, long nonce, byte[] payload, SessionTag tag,
SessionKey key, Set tags, SessionKey newKey) throws I2PSessionException {
SendMessageMessage msg = new SendMessageMessage(); SendMessageMessage msg = new SendMessageMessage();
msg.setDestination(dest); msg.setDestination(dest);
msg.setSessionId(session.getSessionId()); msg.setSessionId(session.getSessionId());
msg.setNonce(nonce); msg.setNonce(nonce);
Payload data = createPayload(dest, payload, tag, key, tags, newKey); Payload data = createPayload(dest, payload, tag, key, tags, newKey);
msg.setPayload(data); msg.setPayload(data);
session.sendMessage(msg); session.sendMessage(msg);
} }
/** /**
* Create a new signed payload and send it off to the destination * Create a new signed payload and send it off to the destination
* *
*/ */
private Payload createPayload(Destination dest, byte[] payload, SessionTag tag, SessionKey key, Set tags, SessionKey newKey) throws I2PSessionException { private Payload createPayload(Destination dest, byte[] payload, SessionTag tag, SessionKey key, Set tags,
if (dest == null) SessionKey newKey) throws I2PSessionException {
throw new I2PSessionException("No destination specified"); if (dest == null) throw new I2PSessionException("No destination specified");
if (payload == null) if (payload == null) throw new I2PSessionException("No payload specified");
throw new I2PSessionException("No payload specified");
Payload data = new Payload(); Payload data = new Payload();
// randomize padding // randomize padding
int size = payload.length + RandomSource.getInstance().nextInt(1024); int size = payload.length + RandomSource.getInstance().nextInt(1024);
byte encr[] = ElGamalAESEngine.encrypt(payload, dest.getPublicKey(), key, tags, tag, newKey, size); byte encr[] = ElGamalAESEngine.encrypt(payload, dest.getPublicKey(), key, tags, tag, newKey, size);
// yes, in an intelligent component, newTags would be queued for confirmation along with key, and // yes, in an intelligent component, newTags would be queued for confirmation along with key, and
// generateNewTags would only generate tags if necessary // generateNewTags would only generate tags if necessary
data.setEncryptedData(encr); data.setEncryptedData(encr);
_log.debug("Encrypting the payload to public key " + dest.getPublicKey().toBase64() + "\nPayload: " + data.calculateHash()); _log.debug("Encrypting the payload to public key " + dest.getPublicKey().toBase64() + "\nPayload: "
return data; + data.calculateHash());
return data;
} }
private static Set generateNewTags() { private static Set generateNewTags() {
Set tags = new HashSet(); Set tags = new HashSet();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
byte tag[] = new byte[SessionTag.BYTE_LENGTH]; byte tag[] = new byte[SessionTag.BYTE_LENGTH];
RandomSource.getInstance().nextBytes(tag); RandomSource.getInstance().nextBytes(tag);
tags.add(new SessionTag(tag)); tags.add(new SessionTag(tag));
} }
return tags; return tags;
} }
/** /**
* Send an abuse message to the router * Send an abuse message to the router
*/ */
@@ -136,18 +138,19 @@ class I2CPMessageProducer {
msg.setSeverity(sv); msg.setSeverity(sv);
session.sendMessage(msg); session.sendMessage(msg);
} }
/** /**
* Create a new signed leaseSet in response to a request to do so and send it * Create a new signed leaseSet in response to a request to do so and send it
* to the router * to the router
* *
*/ */
public void createLeaseSet(I2PSessionImpl session, LeaseSet leaseSet, SigningPrivateKey signingPriv, PrivateKey priv) throws I2PSessionException { public void createLeaseSet(I2PSessionImpl session, LeaseSet leaseSet, SigningPrivateKey signingPriv, PrivateKey priv)
throws I2PSessionException {
CreateLeaseSetMessage msg = new CreateLeaseSetMessage(); CreateLeaseSetMessage msg = new CreateLeaseSetMessage();
msg.setLeaseSet(leaseSet); msg.setLeaseSet(leaseSet);
msg.setPrivateKey(priv); msg.setPrivateKey(priv);
msg.setSigningPrivateKey(signingPriv); msg.setSigningPrivateKey(signingPriv);
msg.setSessionId(session.getSessionId()); msg.setSessionId(session.getSessionId());
session.sendMessage(msg); session.sendMessage(msg);
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,16 +9,15 @@ package net.i2p.client;
* *
*/ */
import net.i2p.I2PException; import java.io.IOException;
import net.i2p.data.Destination;
import net.i2p.data.Certificate;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException;
import java.util.Properties; import java.util.Properties;
import net.i2p.I2PException;
import net.i2p.data.Certificate;
import net.i2p.data.Destination;
/** /**
* Define the standard means of interacting with the I2P system * Define the standard means of interacting with the I2P system
* *
@@ -34,10 +34,10 @@ public interface I2PClient {
public final static String PROP_RELIABILITY_BEST_EFFORT = "BestEffort"; public final static String PROP_RELIABILITY_BEST_EFFORT = "BestEffort";
/** Reliability value: guaranteed */ /** Reliability value: guaranteed */
public final static String PROP_RELIABILITY_GUARANTEED = "Guaranteed"; public final static String PROP_RELIABILITY_GUARANTEED = "Guaranteed";
/** protocol flag that must be sent when opening the i2cp connection to the router */ /** protocol flag that must be sent when opening the i2cp connection to the router */
public final static int PROTOCOL_BYTE = 0x2A; public final static int PROTOCOL_BYTE = 0x2A;
/** Create a new client session for the Destination stored at the destKeyStream /** Create a new client session for the Destination stored at the destKeyStream
* using the specified options to both connect to the router, to instruct * using the specified options to both connect to the router, to instruct
* the router how to handle the new session, and to configure the end to end * the router how to handle the new session, and to configure the end to end
@@ -46,16 +46,16 @@ public interface I2PClient {
* @param options set of options to configure the router with * @param options set of options to configure the router with
* @return new session allowing a Destination to recieve all of its messages and send messages to any other Destination. * @return new session allowing a Destination to recieve all of its messages and send messages to any other Destination.
*/ */
public I2PSession createSession(InputStream destKeyStream, Properties options) throws I2PSessionException; public I2PSession createSession(InputStream destKeyStream, Properties options) throws I2PSessionException;
/** Create a new destination with the default certificate creation properties and store /** Create a new destination with the default certificate creation properties and store
* it, along with the private encryption and signing keys at the specified location * it, along with the private encryption and signing keys at the specified location
* @param destKeyStream create a new destination and write out the object to the given stream, * @param destKeyStream create a new destination and write out the object to the given stream,
* formatted as Destination, PrivateKey, and SigningPrivateKey * formatted as Destination, PrivateKey, and SigningPrivateKey
* @return new destination * @return new destination
*/ */
public Destination createDestination(OutputStream destKeyStream) throws I2PException, IOException; public Destination createDestination(OutputStream destKeyStream) throws I2PException, IOException;
/** Create a new destination with the given certificate and store it, along with the private /** Create a new destination with the given certificate and store it, along with the private
* encryption and signing keys at the specified location * encryption and signing keys at the specified location
* *

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -19,5 +20,5 @@ public class I2PClientFactory {
*/ */
public static I2PClient createClient() { public static I2PClient createClient() {
return new I2PClientImpl(); return new I2PClientImpl();
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -37,7 +38,7 @@ class I2PClientImpl implements I2PClient {
cert.setPayload(null); cert.setPayload(null);
return createDestination(destKeyStream, cert); return createDestination(destKeyStream, cert);
} }
/** /**
* Create the destination with the given payload and write it out along with * Create the destination with the given payload and write it out along with
* the PrivateKey and SigningPrivateKey to the destKeyStream * the PrivateKey and SigningPrivateKey to the destKeyStream
@@ -48,28 +49,28 @@ class I2PClientImpl implements I2PClient {
d.setCertificate(cert); d.setCertificate(cert);
PublicKey publicKey = new PublicKey(); PublicKey publicKey = new PublicKey();
Object keypair[] = KeyGenerator.getInstance().generatePKIKeypair(); Object keypair[] = KeyGenerator.getInstance().generatePKIKeypair();
publicKey = (PublicKey)keypair[0]; publicKey = (PublicKey) keypair[0];
PrivateKey privateKey = (PrivateKey)keypair[1]; PrivateKey privateKey = (PrivateKey) keypair[1];
Object signingKeys[] = KeyGenerator.getInstance().generateSigningKeypair(); Object signingKeys[] = KeyGenerator.getInstance().generateSigningKeypair();
SigningPublicKey signingPubKey = (SigningPublicKey)signingKeys[0]; SigningPublicKey signingPubKey = (SigningPublicKey) signingKeys[0];
SigningPrivateKey signingPrivKey = (SigningPrivateKey)signingKeys[1]; SigningPrivateKey signingPrivKey = (SigningPrivateKey) signingKeys[1];
d.setPublicKey(publicKey); d.setPublicKey(publicKey);
d.setSigningPublicKey(signingPubKey); d.setSigningPublicKey(signingPubKey);
d.writeBytes(destKeyStream); d.writeBytes(destKeyStream);
privateKey.writeBytes(destKeyStream); privateKey.writeBytes(destKeyStream);
signingPrivKey.writeBytes(destKeyStream); signingPrivKey.writeBytes(destKeyStream);
destKeyStream.flush(); destKeyStream.flush();
return d; return d;
} }
/** /**
* Create a new session (though do not connect it yet) * Create a new session (though do not connect it yet)
* *
*/ */
public I2PSession createSession(InputStream destKeyStream, Properties options) throws I2PSessionException { public I2PSession createSession(InputStream destKeyStream, Properties options) throws I2PSessionException {
//return new I2PSessionImpl(destKeyStream, options); // not thread safe //return new I2PSessionImpl(destKeyStream, options); // not thread safe
return new I2PSessionImpl2(destKeyStream, options); // thread safe return new I2PSessionImpl2(destKeyStream, options); // thread safe
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,11 +9,16 @@ package net.i2p.client;
* *
*/ */
import net.i2p.data.i2cp.*;
import net.i2p.util.Log;
import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
import net.i2p.data.i2cp.DisconnectMessage;
import net.i2p.data.i2cp.MessagePayloadMessage;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.data.i2cp.RequestLeaseSetMessage;
import net.i2p.data.i2cp.SessionStatusMessage;
import net.i2p.data.i2cp.SetDateMessage;
import net.i2p.util.Log;
/** /**
* Contains a map of message handlers that a session will want to use * Contains a map of message handlers that a session will want to use
@@ -23,7 +29,7 @@ class I2PClientMessageHandlerMap {
private final static Log _log = new Log(I2PClientMessageHandlerMap.class); private final static Log _log = new Log(I2PClientMessageHandlerMap.class);
/** map of message type id --> I2CPMessageHandler */ /** map of message type id --> I2CPMessageHandler */
private static Map _handlers; private static Map _handlers;
static { static {
_handlers = new HashMap(); _handlers = new HashMap();
_handlers.put(new Integer(DisconnectMessage.MESSAGE_TYPE), new DisconnectMessageHandler()); _handlers.put(new Integer(DisconnectMessage.MESSAGE_TYPE), new DisconnectMessageHandler());
@@ -33,9 +39,9 @@ class I2PClientMessageHandlerMap {
_handlers.put(new Integer(MessageStatusMessage.MESSAGE_TYPE), new MessageStatusMessageHandler()); _handlers.put(new Integer(MessageStatusMessage.MESSAGE_TYPE), new MessageStatusMessageHandler());
_handlers.put(new Integer(SetDateMessage.MESSAGE_TYPE), new SetDateMessageHandler()); _handlers.put(new Integer(SetDateMessage.MESSAGE_TYPE), new SetDateMessageHandler());
} }
public static I2CPMessageHandler getHandler(int messageTypeId) { public static I2CPMessageHandler getHandler(int messageTypeId) {
I2CPMessageHandler handler = (I2CPMessageHandler)_handlers.get(new Integer(messageTypeId)); I2CPMessageHandler handler = (I2CPMessageHandler) _handlers.get(new Integer(messageTypeId));
return handler; return handler;
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -11,8 +12,8 @@ package net.i2p.client;
import java.util.Set; import java.util.Set;
import net.i2p.data.Destination; import net.i2p.data.Destination;
import net.i2p.data.SessionKey;
import net.i2p.data.PrivateKey; import net.i2p.data.PrivateKey;
import net.i2p.data.SessionKey;
import net.i2p.data.SigningPrivateKey; import net.i2p.data.SigningPrivateKey;
/** /**
@@ -30,6 +31,7 @@ public interface I2PSession {
* @return whether it was accepted by the router for delivery or not * @return whether it was accepted by the router for delivery or not
*/ */
public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException; public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException;
/** /**
* Like sendMessage above, except the key used and the tags sent are exposed to the * Like sendMessage above, except the key used and the tags sent are exposed to the
* application. <p /> * application. <p />
@@ -57,15 +59,16 @@ public interface I2PSession {
* the contents of the set is ignored during the call, but afterwards it contains a set of SessionTag * the contents of the set is ignored during the call, but afterwards it contains a set of SessionTag
* objects that were sent along side the given keyUsed. * objects that were sent along side the given keyUsed.
*/ */
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException; public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent)
throws I2PSessionException;
/** Receive a message that the router has notified the client about, returning /** Receive a message that the router has notified the client about, returning
* the payload. * the payload.
* @param msgId message to fetch * @param msgId message to fetch
* @return unencrypted body of the message * @return unencrypted body of the message
*/ */
public byte[] receiveMessage(int msgId) throws I2PSessionException; public byte[] receiveMessage(int msgId) throws I2PSessionException;
/** Instruct the router that the message received was abusive (including how /** Instruct the router that the message received was abusive (including how
* abusive on a 1-100 scale) in the hopes the router can do something to * abusive on a 1-100 scale) in the hopes the router can do something to
* minimize receiving abusive messages like that in the future. * minimize receiving abusive messages like that in the future.
@@ -73,39 +76,39 @@ public interface I2PSession {
* @param severity how abusive * @param severity how abusive
*/ */
public void reportAbuse(int msgId, int severity) throws I2PSessionException; public void reportAbuse(int msgId, int severity) throws I2PSessionException;
/** Instruct the I2PSession where it should send event notifications /** Instruct the I2PSession where it should send event notifications
* @param lsnr listener to retrieve events * @param lsnr listener to retrieve events
*/ */
public void setSessionListener(I2PSessionListener lsnr); public void setSessionListener(I2PSessionListener lsnr);
/** /**
* Tear down the session and release any resources. * Tear down the session and release any resources.
* *
*/ */
public void destroySession() throws I2PSessionException; public void destroySession() throws I2PSessionException;
/** /**
* Actually connect the session and start recieving/sending messages * Actually connect the session and start recieving/sending messages
* *
*/ */
public void connect() throws I2PSessionException; public void connect() throws I2PSessionException;
/** /**
* Retrieve the Destination this session serves as the endpoint for. * Retrieve the Destination this session serves as the endpoint for.
* Returns null if no destination is available. * Returns null if no destination is available.
* *
*/ */
public Destination getMyDestination(); public Destination getMyDestination();
/** /**
* Retrieve the decryption PrivateKey associated with the Destination * Retrieve the decryption PrivateKey associated with the Destination
* *
*/ */
public PrivateKey getDecryptionKey(); public PrivateKey getDecryptionKey();
/** /**
* Retrieve the signing SigningPrivateKey associated with the Destination * Retrieve the signing SigningPrivateKey associated with the Destination
*/ */
public SigningPrivateKey getPrivateKey(); public SigningPrivateKey getPrivateKey();
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,8 +9,8 @@ package net.i2p.client;
* *
*/ */
import net.i2p.util.Log;
import net.i2p.I2PException; import net.i2p.I2PException;
import net.i2p.util.Log;
/** /**
* Thrown when there is a problem doing something on the session * Thrown when there is a problem doing something on the session
@@ -18,10 +19,11 @@ import net.i2p.I2PException;
*/ */
public class I2PSessionException extends I2PException { public class I2PSessionException extends I2PException {
private final static Log _log = new Log(I2PSessionException.class); private final static Log _log = new Log(I2PSessionException.class);
public I2PSessionException(String msg, Throwable t) { public I2PSessionException(String msg, Throwable t) {
super(msg, t); super(msg, t);
} }
public I2PSessionException(String msg) { public I2PSessionException(String msg) {
super(msg); super(msg);
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -59,7 +60,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
private SessionId _sessionId; private SessionId _sessionId;
/** currently granted lease set, or null */ /** currently granted lease set, or null */
private LeaseSet _leaseSet; private LeaseSet _leaseSet;
/** hostname of router */ /** hostname of router */
private String _hostname; private String _hostname;
/** port num to router */ /** port num to router */
@@ -70,37 +71,37 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
private I2CPMessageReader _reader; private I2CPMessageReader _reader;
/** where we pipe our messages */ /** where we pipe our messages */
private OutputStream _out; private OutputStream _out;
/** who we send events to */ /** who we send events to */
private I2PSessionListener _sessionListener; private I2PSessionListener _sessionListener;
/** class that generates new messages */ /** class that generates new messages */
protected I2CPMessageProducer _producer; protected I2CPMessageProducer _producer;
/** map of integer --> MessagePayloadMessage */ /** map of integer --> MessagePayloadMessage */
Map _availableMessages; Map _availableMessages;
/** MessageStatusMessage status from the most recent send that hasn't been consumed */ /** MessageStatusMessage status from the most recent send that hasn't been consumed */
private List _receivedStatus; private List _receivedStatus;
private int _totalReconnectAttempts; private int _totalReconnectAttempts;
/** monitor for waiting until a lease set has been granted */ /** monitor for waiting until a lease set has been granted */
private Object _leaseSetWait = new Object(); private Object _leaseSetWait = new Object();
/** whether the session connection has already been closed (or not yet opened) */ /** whether the session connection has already been closed (or not yet opened) */
private boolean _closed; private boolean _closed;
/** have we received the current date from the router yet? */ /** have we received the current date from the router yet? */
private boolean _dateReceived; private boolean _dateReceived;
/** lock that we wait upon, that the SetDateMessageHandler notifies */ /** lock that we wait upon, that the SetDateMessageHandler notifies */
private Object _dateReceivedLock = new Object(); private Object _dateReceivedLock = new Object();
void dateUpdated() { void dateUpdated() {
_dateReceived = true; _dateReceived = true;
synchronized (_dateReceivedLock) { synchronized (_dateReceivedLock) {
_dateReceivedLock.notifyAll(); _dateReceivedLock.notifyAll();
} }
} }
/** /**
* Create a new session, reading the Destination, PrivateKey, and SigningPrivateKey * Create a new session, reading the Destination, PrivateKey, and SigningPrivateKey
* from the destKeyStream, and using the specified options to connect to the router * from the destKeyStream, and using the specified options to connect to the router
@@ -108,7 +109,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* @throws I2PSessionException if there is a problem loading the private keys or * @throws I2PSessionException if there is a problem loading the private keys or
*/ */
public I2PSessionImpl(InputStream destKeyStream, Properties options) throws I2PSessionException { public I2PSessionImpl(InputStream destKeyStream, Properties options) throws I2PSessionException {
_closed = true; _closed = true;
_producer = new I2CPMessageProducer(); _producer = new I2CPMessageProducer();
_availableMessages = new HashMap(); _availableMessages = new HashMap();
try { try {
@@ -120,11 +121,11 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
} }
loadConfig(options); loadConfig(options);
_sessionId = null; _sessionId = null;
_receivedStatus = new LinkedList(); _receivedStatus = new LinkedList();
_leaseSet = null; _leaseSet = null;
_totalReconnectAttempts = 0; _totalReconnectAttempts = 0;
} }
/** /**
* Parse the config for anything we know about * Parse the config for anything we know about
* *
@@ -133,51 +134,61 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
_options = new Properties(); _options = new Properties();
_options.putAll(filter(options)); _options.putAll(filter(options));
_hostname = _options.getProperty(I2PClient.PROP_TCP_HOST, "localhost"); _hostname = _options.getProperty(I2PClient.PROP_TCP_HOST, "localhost");
String portNum = _options.getProperty(I2PClient.PROP_TCP_PORT, TestServer.LISTEN_PORT+""); String portNum = _options.getProperty(I2PClient.PROP_TCP_PORT, TestServer.LISTEN_PORT + "");
try { try {
_portNum = Integer.parseInt(portNum); _portNum = Integer.parseInt(portNum);
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
if (_log.shouldLog(Log.WARN)) _log.warn("Invalid port number specified, defaulting to "+TestServer.LISTEN_PORT, nfe); if (_log.shouldLog(Log.WARN))
_log.warn("Invalid port number specified, defaulting to "
+ TestServer.LISTEN_PORT, nfe);
_portNum = TestServer.LISTEN_PORT; _portNum = TestServer.LISTEN_PORT;
} }
} }
private static Properties filter(Properties options) { private static Properties filter(Properties options) {
Properties rv = new Properties(); Properties rv = new Properties();
for (Iterator iter = options.keySet().iterator(); iter.hasNext(); ) { for (Iterator iter = options.keySet().iterator(); iter.hasNext();) {
String key = (String)iter.next(); String key = (String) iter.next();
String val = options.getProperty(key); String val = options.getProperty(key);
if (key.startsWith("java")) { if (key.startsWith("java")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping java.* property: " + key); if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping java.* property: " + key);
} else if (key.startsWith("user")) { } else if (key.startsWith("user")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping user.* property: " + key); if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping user.* property: " + key);
} else if (key.startsWith("os")) { } else if (key.startsWith("os")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping os.* property: " + key); if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping os.* property: " + key);
} else if (key.startsWith("sun")) { } else if (key.startsWith("sun")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping sun.* property: " + key); if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping sun.* property: " + key);
} else if (key.startsWith("file")) { } else if (key.startsWith("file")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping file.* property: " + key); if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping file.* property: " + key);
} else if (key.startsWith("line")) { } else if (key.startsWith("line")) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping line.* property: " + key); if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping line.* property: " + key);
} else if ( (key.length() > 255) || (val.length() > 255) ) { } else if ((key.length() > 255) || (val.length() > 255)) {
if (_log.shouldLog(Log.WARN)) _log.warn("Not passing on property [" + key + "] in the session configuration as the value is too long (max = 255): " + val); if (_log.shouldLog(Log.WARN))
} else { _log
rv.setProperty(key, val); .warn("Not passing on property ["
} + key
} + "] in the session configuration as the value is too long (max = 255): "
return rv; + val);
} else {
rv.setProperty(key, val);
}
}
return rv;
} }
void setLeaseSet(LeaseSet ls) { void setLeaseSet(LeaseSet ls) {
_leaseSet = ls; _leaseSet = ls;
if (ls != null) { if (ls != null) {
synchronized (_leaseSetWait) { synchronized (_leaseSetWait) {
_leaseSetWait.notifyAll(); _leaseSetWait.notifyAll();
} }
} }
} }
LeaseSet getLeaseSet() { return _leaseSet; }
LeaseSet getLeaseSet() {
return _leaseSet;
}
/** /**
* Load up the destKeyFile for our Destination, PrivateKey, and SigningPrivateKey * Load up the destKeyFile for our Destination, PrivateKey, and SigningPrivateKey
* *
@@ -192,7 +203,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
_privateKey.readBytes(destKeyStream); _privateKey.readBytes(destKeyStream);
_signingPrivateKey.readBytes(destKeyStream); _signingPrivateKey.readBytes(destKeyStream);
} }
/** /**
* Connect to the router and establish a session. This call blocks until * Connect to the router and establish a session. This call blocks until
* a session is granted. * a session is granted.
@@ -201,70 +212,79 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* not reachable * not reachable
*/ */
public void connect() throws I2PSessionException { public void connect() throws I2PSessionException {
_closed = false; _closed = false;
long startConnect = Clock.getInstance().now(); long startConnect = Clock.getInstance().now();
try { try {
if (_log.shouldLog(Log.DEBUG)) _log.debug("connect begin to " + _hostname + ":" + _portNum); if (_log.shouldLog(Log.DEBUG)) _log.debug("connect begin to " + _hostname + ":" + _portNum);
_socket = new Socket(_hostname, _portNum); _socket = new Socket(_hostname, _portNum);
_out = _socket.getOutputStream(); _out = _socket.getOutputStream();
synchronized (_out) { synchronized (_out) {
_out.write(I2PClient.PROTOCOL_BYTE); _out.write(I2PClient.PROTOCOL_BYTE);
} }
InputStream in = _socket.getInputStream(); InputStream in = _socket.getInputStream();
_reader = new I2CPMessageReader(in, this); _reader = new I2CPMessageReader(in, this);
if (_log.shouldLog(Log.DEBUG)) _log.debug("before startReading"); if (_log.shouldLog(Log.DEBUG)) _log.debug("before startReading");
_reader.startReading(); _reader.startReading();
if (_log.shouldLog(Log.DEBUG)) _log.debug("Before getDate");
sendMessage(new GetDateMessage());
if (_log.shouldLog(Log.DEBUG)) _log.debug("After getDate / begin waiting for a response");
while (!_dateReceived) {
try {
synchronized (_dateReceivedLock) { _dateReceivedLock.wait(1000); }
} catch (InterruptedException ie) {}
}
if (_log.shouldLog(Log.DEBUG)) _log.debug("After received a SetDate response");
if (_log.shouldLog(Log.DEBUG)) _log.debug("Before producer.connect()"); if (_log.shouldLog(Log.DEBUG)) _log.debug("Before getDate");
_producer.connect(this); sendMessage(new GetDateMessage());
if (_log.shouldLog(Log.DEBUG)) _log.debug("After producer.connect()"); if (_log.shouldLog(Log.DEBUG)) _log.debug("After getDate / begin waiting for a response");
while (!_dateReceived) {
// wait until we have created a lease set try {
while (_leaseSet == null) { synchronized (_dateReceivedLock) {
synchronized (_leaseSetWait) { _dateReceivedLock.wait(1000);
try { _leaseSetWait.wait(1000); } catch (InterruptedException ie) {} }
} } catch (InterruptedException ie) {
} }
long connected = Clock.getInstance().now(); }
if (_log.shouldLog(Log.INFO)) _log.info("Lease set created with inbound tunnels after " + (connected-startConnect) + "ms - ready to participate in the network!"); if (_log.shouldLog(Log.DEBUG)) _log.debug("After received a SetDate response");
if (_log.shouldLog(Log.DEBUG)) _log.debug("Before producer.connect()");
_producer.connect(this);
if (_log.shouldLog(Log.DEBUG)) _log.debug("After producer.connect()");
// wait until we have created a lease set
while (_leaseSet == null) {
synchronized (_leaseSetWait) {
try {
_leaseSetWait.wait(1000);
} catch (InterruptedException ie) {
}
}
}
long connected = Clock.getInstance().now();
if (_log.shouldLog(Log.INFO))
_log.info("Lease set created with inbound tunnels after "
+ (connected - startConnect)
+ "ms - ready to participate in the network!");
} catch (UnknownHostException uhe) { } catch (UnknownHostException uhe) {
_closed = true; _closed = true;
throw new I2PSessionException("Invalid session configuration", uhe); throw new I2PSessionException("Invalid session configuration", uhe);
} catch (IOException ioe) { } catch (IOException ioe) {
_closed = true; _closed = true;
throw new I2PSessionException("Problem connecting to " + _hostname + " on port " + _portNum, ioe); throw new I2PSessionException("Problem connecting to " + _hostname + " on port " + _portNum, ioe);
} }
} }
/** /**
* Pull the unencrypted data from the message that we've already prefetched and * Pull the unencrypted data from the message that we've already prefetched and
* notified the user that its available. * notified the user that its available.
* *
*/ */
public byte[] receiveMessage(int msgId) throws I2PSessionException { public byte[] receiveMessage(int msgId) throws I2PSessionException {
MessagePayloadMessage msg = (MessagePayloadMessage)_availableMessages.remove(new Integer(msgId)); MessagePayloadMessage msg = (MessagePayloadMessage) _availableMessages.remove(new Integer(msgId));
if (msg == null) return null; if (msg == null) return null;
return msg.getPayload().getUnencryptedData(); return msg.getPayload().getUnencryptedData();
} }
/** /**
* Report abuse with regards to the given messageId * Report abuse with regards to the given messageId
*/ */
public void reportAbuse(int msgId, int severity) throws I2PSessionException { public void reportAbuse(int msgId, int severity) throws I2PSessionException {
if (isClosed()) throw new I2PSessionException("Already closed"); if (isClosed()) throw new I2PSessionException("Already closed");
_producer.reportAbuse(this, msgId, severity); _producer.reportAbuse(this, msgId, severity);
} }
/** /**
* Send the data to the destination. * Send the data to the destination.
* TODO: this currently always returns true, regardless of whether the message was * TODO: this currently always returns true, regardless of whether the message was
@@ -272,43 +292,49 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
* *
*/ */
public abstract boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException; public abstract boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException;
public abstract boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException;
public abstract boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent)
throws I2PSessionException;
public abstract void receiveStatus(int msgId, long nonce, int status); public abstract void receiveStatus(int msgId, long nonce, int status);
protected boolean isGuaranteed() { protected boolean isGuaranteed() {
return I2PClient.PROP_RELIABILITY_GUARANTEED.equals(_options.getProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED)); return I2PClient.PROP_RELIABILITY_GUARANTEED
.equals(_options.getProperty(I2PClient.PROP_RELIABILITY,
I2PClient.PROP_RELIABILITY_GUARANTEED));
} }
protected static final Set createNewTags(int num) { protected static final Set createNewTags(int num) {
Set tags = new HashSet(); Set tags = new HashSet();
for (int i = 0; i < num; i++) for (int i = 0; i < num; i++)
tags.add(new SessionTag(true)); tags.add(new SessionTag(true));
return tags; return tags;
} }
/** /**
* Recieve a payload message and let the app know its available * Recieve a payload message and let the app know its available
*/ */
public void addNewMessage(MessagePayloadMessage msg) { public void addNewMessage(MessagePayloadMessage msg) {
_availableMessages.put(new Integer(msg.getMessageId().getMessageId()), msg); _availableMessages.put(new Integer(msg.getMessageId().getMessageId()), msg);
final int id = msg.getMessageId().getMessageId(); final int id = msg.getMessageId().getMessageId();
byte data[] = msg.getPayload().getUnencryptedData(); byte data[] = msg.getPayload().getUnencryptedData();
if ( (data == null) || (data.length <= 0) ) { if ((data == null) || (data.length <= 0)) {
if (_log.shouldLog(Log.ERROR)) _log.error("addNewMessage of a message with no unencrypted data", new Exception("Empty message")); if (_log.shouldLog(Log.ERROR))
} else { _log.error("addNewMessage of a message with no unencrypted data",
final long size = data.length; new Exception("Empty message"));
Thread notifier = new I2PThread(new Runnable() { } else {
public void run() { final long size = data.length;
if (_sessionListener != null) Thread notifier = new I2PThread(new Runnable() {
_sessionListener.messageAvailable(I2PSessionImpl.this, id, size); public void run() {
} if (_sessionListener != null) _sessionListener.messageAvailable(I2PSessionImpl.this, id, size);
}); }
notifier.setName("Notifier [" + _sessionId + "/" + id + "]"); });
notifier.setDaemon(true); notifier.setName("Notifier [" + _sessionId + "/" + id + "]");
notifier.start(); notifier.setDaemon(true);
} notifier.start();
}
} }
/** /**
* Recieve notification of some I2CP message and handle it if possible * Recieve notification of some I2CP message and handle it if possible
* *
@@ -316,177 +342,208 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
public void messageReceived(I2CPMessageReader reader, I2CPMessage message) { public void messageReceived(I2CPMessageReader reader, I2CPMessage message) {
I2CPMessageHandler handler = I2PClientMessageHandlerMap.getHandler(message.getType()); I2CPMessageHandler handler = I2PClientMessageHandlerMap.getHandler(message.getType());
if (handler == null) { if (handler == null) {
if (_log.shouldLog(Log.WARN)) _log.warn("Unknown message or unhandleable message received: type = " + message.getType()); if (_log.shouldLog(Log.WARN))
_log.warn("Unknown message or unhandleable message received: type = "
+ message.getType());
} else { } else {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Message received of type " + message.getType() + " to be handled by " + handler); if (_log.shouldLog(Log.DEBUG))
_log.debug("Message received of type " + message.getType()
+ " to be handled by " + handler);
handler.handleMessage(message, this); handler.handleMessage(message, this);
} }
} }
/** /**
* Recieve notifiation of an error reading the I2CP stream * Recieve notifiation of an error reading the I2CP stream
* *
*/ */
public void readError(I2CPMessageReader reader, Exception error) { public void readError(I2CPMessageReader reader, Exception error) {
propogateError("There was an error reading data", error); propogateError("There was an error reading data", error);
disconnect(); disconnect();
} }
/** /**
* Retrieve the destination of the session * Retrieve the destination of the session
*/ */
public Destination getMyDestination() { return _myDestination; } public Destination getMyDestination() {
return _myDestination;
}
/** /**
* Retrieve the decryption PrivateKey * Retrieve the decryption PrivateKey
*/ */
public PrivateKey getDecryptionKey() { return _privateKey; } public PrivateKey getDecryptionKey() {
return _privateKey;
}
/** /**
* Retrieve the signing SigningPrivateKey * Retrieve the signing SigningPrivateKey
*/ */
public SigningPrivateKey getPrivateKey() { return _signingPrivateKey; } public SigningPrivateKey getPrivateKey() {
return _signingPrivateKey;
}
/** /**
* Retrieve the helper that generates I2CP messages * Retrieve the helper that generates I2CP messages
*/ */
I2CPMessageProducer getProducer() { return _producer; } I2CPMessageProducer getProducer() {
return _producer;
}
/** /**
* Retrieve the configuration options * Retrieve the configuration options
*/ */
Properties getOptions() { return _options; } Properties getOptions() {
return _options;
}
/** /**
* Retrieve the session's ID * Retrieve the session's ID
*/ */
SessionId getSessionId() { return _sessionId; } SessionId getSessionId() {
return _sessionId;
}
/** /**
* Configure the session's ID * Configure the session's ID
*/ */
void setSessionId(SessionId id) { void setSessionId(SessionId id) {
_sessionId = id; _sessionId = id;
} }
/** configure the listener */ /** configure the listener */
public void setSessionListener(I2PSessionListener lsnr) { _sessionListener = lsnr; } public void setSessionListener(I2PSessionListener lsnr) {
_sessionListener = lsnr;
}
/** has the session been closed (or not yet connected)? */ /** has the session been closed (or not yet connected)? */
public boolean isClosed() { return _closed; } public boolean isClosed() {
return _closed;
}
/** /**
* Deliver an I2CP message to the router * Deliver an I2CP message to the router
* *
* @throws I2PSessionException if the message is malformed or there is an error writing it out * @throws I2PSessionException if the message is malformed or there is an error writing it out
*/ */
void sendMessage(I2CPMessage message) throws I2PSessionException { void sendMessage(I2CPMessage message) throws I2PSessionException {
if (isClosed()) throw new I2PSessionException("Already closed"); if (isClosed()) throw new I2PSessionException("Already closed");
try { try {
synchronized(_out) { synchronized (_out) {
message.writeMessage(_out); message.writeMessage(_out);
_out.flush(); _out.flush();
} }
if (_log.shouldLog(Log.DEBUG)) _log.debug("Message written out and flushed"); if (_log.shouldLog(Log.DEBUG)) _log.debug("Message written out and flushed");
} catch (I2CPMessageException ime) { } catch (I2CPMessageException ime) {
throw new I2PSessionException("Error writing out the message", ime); throw new I2PSessionException("Error writing out the message", ime);
} catch (IOException ioe) { } catch (IOException ioe) {
throw new I2PSessionException("Error writing out the message", ioe); throw new I2PSessionException("Error writing out the message", ioe);
} }
} }
/** /**
* Pass off the error to the listener * Pass off the error to the listener
*/ */
void propogateError(String msg, Throwable error) { void propogateError(String msg, Throwable error) {
if (_log.shouldLog(Log.ERROR)) _log.error("Error occurred: " + msg, error); if (_log.shouldLog(Log.ERROR)) _log.error("Error occurred: " + msg, error);
if (_sessionListener != null) if (_sessionListener != null) _sessionListener.errorOccurred(this, msg, error);
_sessionListener.errorOccurred(this, msg, error);
} }
/** /**
* Tear down the session, and do NOT reconnect * Tear down the session, and do NOT reconnect
*/ */
public void destroySession() { destroySession(true); } public void destroySession() {
public void destroySession(boolean sendDisconnect) { destroySession(true);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Destroy the session", new Exception("DestroySession()"));
_closed = true;
if (sendDisconnect) {
try {
_producer.disconnect(this);
} catch (I2PSessionException ipe) {
propogateError("Error destroying the session", ipe);
}
}
closeSocket();
if (_sessionListener != null)
_sessionListener.disconnected(this);
} }
public void destroySession(boolean sendDisconnect) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Destroy the session", new Exception("DestroySession()"));
_closed = true;
if (sendDisconnect) {
try {
_producer.disconnect(this);
} catch (I2PSessionException ipe) {
propogateError("Error destroying the session", ipe);
}
}
closeSocket();
if (_sessionListener != null) _sessionListener.disconnected(this);
}
/** /**
* Close the socket carefully * Close the socket carefully
* *
*/ */
private void closeSocket() { private void closeSocket() {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Closing the socket", new Exception("closeSocket")); if (_log.shouldLog(Log.DEBUG)) _log.debug("Closing the socket", new Exception("closeSocket"));
_closed = true; _closed = true;
if (_reader != null) if (_reader != null) _reader.stopReading();
_reader.stopReading(); _reader = null;
_reader = null;
if (_socket != null) { if (_socket != null) {
try { try {
_socket.close(); _socket.close();
} catch (IOException ioe) { } catch (IOException ioe) {
propogateError("Caught an IO error closing the socket. ignored", ioe); propogateError("Caught an IO error closing the socket. ignored", ioe);
} finally { } finally {
_socket = null; // so when propogateError calls closeSocket, it doesnt loop _socket = null; // so when propogateError calls closeSocket, it doesnt loop
} }
} }
} }
/** /**
* Recieve notification that the I2CP connection was disconnected * Recieve notification that the I2CP connection was disconnected
*/ */
public void disconnected(I2CPMessageReader reader) { public void disconnected(I2CPMessageReader reader) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Disconnected", new Exception("Disconnected")); if (_log.shouldLog(Log.DEBUG)) _log.debug("Disconnected", new Exception("Disconnected"));
disconnect(); disconnect();
} }
protected void disconnect() {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Disconnect() called", new Exception("Disconnect"));
if (shouldReconnect()) {
if (reconnect()) {
if (_log.shouldLog(Log.INFO)) _log.info("I2CP reconnection successful");
return;
} else {
_log.error("I2CP reconnection failed");
}
}
_log.error("Disconned from the router, and not trying to reconnect further. I hope you're not hoping anything else will happen");
if (_sessionListener != null)
_sessionListener.disconnected(this);
closeSocket(); protected void disconnect() {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Disconnect() called", new Exception("Disconnect"));
if (shouldReconnect()) {
if (reconnect()) {
if (_log.shouldLog(Log.INFO)) _log.info("I2CP reconnection successful");
return;
} else {
_log.error("I2CP reconnection failed");
}
}
_log
.error("Disconned from the router, and not trying to reconnect further. I hope you're not hoping anything else will happen");
if (_sessionListener != null) _sessionListener.disconnected(this);
closeSocket();
} }
private final static int MAX_RECONNECT_ATTEMPTS = 1; private final static int MAX_RECONNECT_ATTEMPTS = 1;
private final static int MAX_TOTAL_RECONNECT_ATTEMPTS = 3; private final static int MAX_TOTAL_RECONNECT_ATTEMPTS = 3;
protected boolean shouldReconnect() { return true; } protected boolean shouldReconnect() {
return true;
}
protected boolean reconnect() { protected boolean reconnect() {
closeSocket(); closeSocket();
if (_totalReconnectAttempts < MAX_TOTAL_RECONNECT_ATTEMPTS) { if (_totalReconnectAttempts < MAX_TOTAL_RECONNECT_ATTEMPTS) {
_totalReconnectAttempts++; _totalReconnectAttempts++;
} else { } else {
if (_log.shouldLog(Log.CRIT)) _log.log(Log.CRIT, "Max number of reconnects exceeded [" + _totalReconnectAttempts + "], we give up!"); if (_log.shouldLog(Log.CRIT))
return false; _log.log(Log.CRIT, "Max number of reconnects exceeded ["
} + _totalReconnectAttempts + "], we give up!");
if (_log.shouldLog(Log.INFO)) _log.info("Reconnecting..."); return false;
for (int i = 0; i < MAX_RECONNECT_ATTEMPTS; i++) { }
try { if (_log.shouldLog(Log.INFO)) _log.info("Reconnecting...");
connect(); for (int i = 0; i < MAX_RECONNECT_ATTEMPTS; i++) {
return true; try {
} catch (I2PSessionException ise) { connect();
if (_log.shouldLog(Log.ERROR)) _log.error("Error reconnecting on attempt " + i, ise); return true;
} } catch (I2PSessionException ise) {
} if (_log.shouldLog(Log.ERROR)) _log.error("Error reconnecting on attempt " + i, ise);
return false; }
}
return false;
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -16,10 +17,10 @@ import java.util.Set;
import net.i2p.crypto.KeyGenerator; import net.i2p.crypto.KeyGenerator;
import net.i2p.crypto.SessionKeyManager; import net.i2p.crypto.SessionKeyManager;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination; import net.i2p.data.Destination;
import net.i2p.data.SessionKey; import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag; import net.i2p.data.SessionTag;
import net.i2p.data.DataHelper;
import net.i2p.data.i2cp.MessageId; import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.MessageStatusMessage; import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.util.Clock; import net.i2p.util.Clock;
@@ -33,14 +34,14 @@ import net.i2p.util.RandomSource;
*/ */
class I2PSessionImpl2 extends I2PSessionImpl { class I2PSessionImpl2 extends I2PSessionImpl {
private final static Log _log = new Log(I2PSessionImpl2.class); private final static Log _log = new Log(I2PSessionImpl2.class);
/** set of MessageState objects, representing all of the messages in the process of being sent */ /** set of MessageState objects, representing all of the messages in the process of being sent */
private Set _sendingStates; private Set _sendingStates;
/** max # seconds to wait for confirmation of the message send */ /** max # seconds to wait for confirmation of the message send */
private final static long SEND_TIMEOUT = 60*1000; // 60 seconds to send private final static long SEND_TIMEOUT = 60 * 1000; // 60 seconds to send
/** should we gzip each payload prior to sending it? */ /** should we gzip each payload prior to sending it? */
private final static boolean SHOULD_COMPRESS = true; private final static boolean SHOULD_COMPRESS = true;
/** /**
* Create a new session, reading the Destination, PrivateKey, and SigningPrivateKey * Create a new session, reading the Destination, PrivateKey, and SigningPrivateKey
* from the destKeyStream, and using the specified options to connect to the router * from the destKeyStream, and using the specified options to connect to the router
@@ -48,240 +49,270 @@ class I2PSessionImpl2 extends I2PSessionImpl {
* @throws I2PSessionException if there is a problem loading the private keys or * @throws I2PSessionException if there is a problem loading the private keys or
*/ */
public I2PSessionImpl2(InputStream destKeyStream, Properties options) throws I2PSessionException { public I2PSessionImpl2(InputStream destKeyStream, Properties options) throws I2PSessionException {
super(destKeyStream, options); super(destKeyStream, options);
_sendingStates = new HashSet(32); _sendingStates = new HashSet(32);
} }
protected long getTimeout() { return SEND_TIMEOUT; } protected long getTimeout() {
return SEND_TIMEOUT;
}
public void destroySession(boolean sendDisconnect) { public void destroySession(boolean sendDisconnect) {
clearStates(); clearStates();
super.destroySession(sendDisconnect); super.destroySession(sendDisconnect);
} }
public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException { public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException {
return sendMessage(dest, payload, new SessionKey(), new HashSet(64)); return sendMessage(dest, payload, new SessionKey(), new HashSet(64));
} }
public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException { public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent)
if (isClosed()) throw new I2PSessionException("Already closed"); throws I2PSessionException {
if (SHOULD_COMPRESS) if (isClosed()) throw new I2PSessionException("Already closed");
payload = DataHelper.compress(payload); if (SHOULD_COMPRESS) payload = DataHelper.compress(payload);
if (isGuaranteed()) { if (isGuaranteed()) {
return sendGuaranteed(dest, payload, keyUsed, tagsSent); return sendGuaranteed(dest, payload, keyUsed, tagsSent);
} else { } else {
return sendBestEffort(dest, payload, keyUsed, tagsSent); return sendBestEffort(dest, payload, keyUsed, tagsSent);
} }
} }
/** /**
* pull the unencrypted AND DECOMPRESSED data * pull the unencrypted AND DECOMPRESSED data
*/ */
public byte[] receiveMessage(int msgId) throws I2PSessionException { public byte[] receiveMessage(int msgId) throws I2PSessionException {
byte compressed[] = super.receiveMessage(msgId); byte compressed[] = super.receiveMessage(msgId);
if (SHOULD_COMPRESS) if (SHOULD_COMPRESS)
return DataHelper.decompress(compressed); return DataHelper.decompress(compressed);
else else
return compressed; return compressed;
} }
private boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent) throws I2PSessionException {
SessionKey key = SessionKeyManager.getInstance().getCurrentKey(dest.getPublicKey());
if (key == null)
key = SessionKeyManager.getInstance().createSession(dest.getPublicKey());
SessionTag tag = SessionKeyManager.getInstance().consumeNextAvailableTag(dest.getPublicKey(), key);
Set sentTags = null;
if (SessionKeyManager.getInstance().getAvailableTags(dest.getPublicKey(), key) < 10) {
sentTags = createNewTags(50);
} else if (SessionKeyManager.getInstance().getAvailableTimeLeft(dest.getPublicKey(), key) < 30*1000) {
// if we have > 10 tags, but they expire in under 30 seconds, we want more
sentTags = createNewTags(50);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Tags are almost expired, adding 50 new ones");
}
SessionKey newKey = null;
if (false) // rekey
newKey = KeyGenerator.getInstance().generateSessionKey();
long nonce = (long)RandomSource.getInstance().nextInt(Integer.MAX_VALUE); private boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent)
MessageState state = new MessageState(nonce); throws I2PSessionException {
state.setKey(key); SessionKey key = SessionKeyManager.getInstance().getCurrentKey(dest.getPublicKey());
state.setTags(sentTags); if (key == null) key = SessionKeyManager.getInstance().createSession(dest.getPublicKey());
state.setNewKey(newKey); SessionTag tag = SessionKeyManager.getInstance().consumeNextAvailableTag(dest.getPublicKey(), key);
state.setTo(dest); Set sentTags = null;
if (_log.shouldLog(Log.DEBUG)) _log.debug("Setting key = " + key); if (SessionKeyManager.getInstance().getAvailableTags(dest.getPublicKey(), key) < 10) {
sentTags = createNewTags(50);
} else if (SessionKeyManager.getInstance().getAvailableTimeLeft(dest.getPublicKey(), key) < 30 * 1000) {
// if we have > 10 tags, but they expire in under 30 seconds, we want more
sentTags = createNewTags(50);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Tags are almost expired, adding 50 new ones");
}
SessionKey newKey = null;
if (false) // rekey
newKey = KeyGenerator.getInstance().generateSessionKey();
if (keyUsed != null) { long nonce = (long) RandomSource.getInstance().nextInt(Integer.MAX_VALUE);
if (newKey != null) MessageState state = new MessageState(nonce);
keyUsed.setData(newKey.getData()); state.setKey(key);
else state.setTags(sentTags);
keyUsed.setData(key.getData()); state.setNewKey(newKey);
} state.setTo(dest);
if (tagsSent != null) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Setting key = " + key);
if (sentTags != null) {
tagsSent.addAll(sentTags);
}
}
synchronized (_sendingStates) { if (keyUsed != null) {
_sendingStates.add(state); if (newKey != null)
} keyUsed.setData(newKey.getData());
if (_log.shouldLog(Log.DEBUG)) _log.debug("Adding sending state " + state.getMessageId() + " / " + state.getNonce()); else
_producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey); keyUsed.setData(key.getData());
state.waitFor(MessageStatusMessage.STATUS_SEND_ACCEPTED, Clock.getInstance().now() + getTimeout()); }
synchronized (_sendingStates) { if (tagsSent != null) {
_sendingStates.remove(state); if (sentTags != null) {
} tagsSent.addAll(sentTags);
boolean found = state.received(MessageStatusMessage.STATUS_SEND_ACCEPTED); }
if (_log.shouldLog(Log.DEBUG)) _log.debug("After waitFor sending state " + state.getMessageId().getMessageId() + " / " + state.getNonce() + " found = " + found); }
if (found) {
if (_log.shouldLog(Log.INFO)) _log.info("Message sent after " + state.getElapsed() + "ms with " + payload.length + " bytes"); synchronized (_sendingStates) {
} else { _sendingStates.add(state);
if (_log.shouldLog(Log.INFO)) _log.info("Message send failed after " + state.getElapsed() + "ms with " + payload.length + " bytes"); }
if (_log.shouldLog(Log.ERROR)) _log.error("Never received *accepted* from the router! dropping and reconnecting"); if (_log.shouldLog(Log.DEBUG))
disconnect(); _log.debug("Adding sending state " + state.getMessageId() + " / "
return false; + state.getNonce());
} _producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey);
return found; state.waitFor(MessageStatusMessage.STATUS_SEND_ACCEPTED, Clock.getInstance().now() + getTimeout());
synchronized (_sendingStates) {
_sendingStates.remove(state);
}
boolean found = state.received(MessageStatusMessage.STATUS_SEND_ACCEPTED);
if (_log.shouldLog(Log.DEBUG))
_log.debug("After waitFor sending state " + state.getMessageId().getMessageId()
+ " / " + state.getNonce() + " found = " + found);
if (found) {
if (_log.shouldLog(Log.INFO))
_log.info("Message sent after " + state.getElapsed() + "ms with "
+ payload.length + " bytes");
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Message send failed after " + state.getElapsed() + "ms with "
+ payload.length + " bytes");
if (_log.shouldLog(Log.ERROR))
_log
.error("Never received *accepted* from the router! dropping and reconnecting");
disconnect();
return false;
}
return found;
} }
private boolean sendGuaranteed(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent) throws I2PSessionException {
SessionKey key = SessionKeyManager.getInstance().getCurrentKey(dest.getPublicKey());
if (key == null)
key = SessionKeyManager.getInstance().createSession(dest.getPublicKey());
SessionTag tag = SessionKeyManager.getInstance().consumeNextAvailableTag(dest.getPublicKey(), key);
Set sentTags = null;
if (SessionKeyManager.getInstance().getAvailableTags(dest.getPublicKey(), key) < 10) {
sentTags = createNewTags(50);
} else if (SessionKeyManager.getInstance().getAvailableTimeLeft(dest.getPublicKey(), key) < 30*1000) {
// if we have > 10 tags, but they expire in under 30 seconds, we want more
sentTags = createNewTags(50);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Tags are almost expired, adding 50 new ones");
}
SessionKey newKey = null;
if (false) // rekey
newKey = KeyGenerator.getInstance().generateSessionKey();
long nonce = (long)RandomSource.getInstance().nextInt(Integer.MAX_VALUE); private boolean sendGuaranteed(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent)
MessageState state = new MessageState(nonce); throws I2PSessionException {
state.setKey(key); SessionKey key = SessionKeyManager.getInstance().getCurrentKey(dest.getPublicKey());
state.setTags(sentTags); if (key == null) key = SessionKeyManager.getInstance().createSession(dest.getPublicKey());
state.setNewKey(newKey); SessionTag tag = SessionKeyManager.getInstance().consumeNextAvailableTag(dest.getPublicKey(), key);
state.setTo(dest); Set sentTags = null;
if (_log.shouldLog(Log.DEBUG)) _log.debug("Setting key = " + key); if (SessionKeyManager.getInstance().getAvailableTags(dest.getPublicKey(), key) < 10) {
sentTags = createNewTags(50);
} else if (SessionKeyManager.getInstance().getAvailableTimeLeft(dest.getPublicKey(), key) < 30 * 1000) {
// if we have > 10 tags, but they expire in under 30 seconds, we want more
sentTags = createNewTags(50);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Tags are almost expired, adding 50 new ones");
}
SessionKey newKey = null;
if (false) // rekey
newKey = KeyGenerator.getInstance().generateSessionKey();
if (keyUsed != null) { long nonce = (long) RandomSource.getInstance().nextInt(Integer.MAX_VALUE);
if (newKey != null) MessageState state = new MessageState(nonce);
keyUsed.setData(newKey.getData()); state.setKey(key);
else state.setTags(sentTags);
keyUsed.setData(key.getData()); state.setNewKey(newKey);
} state.setTo(dest);
if (tagsSent != null) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Setting key = " + key);
if (sentTags != null) {
tagsSent.addAll(sentTags);
}
}
synchronized (_sendingStates) { if (keyUsed != null) {
_sendingStates.add(state); if (newKey != null)
} keyUsed.setData(newKey.getData());
if (_log.shouldLog(Log.DEBUG)) _log.debug("Adding sending state " + state.getMessageId() + " / " + state.getNonce()); else
_producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey); keyUsed.setData(key.getData());
state.waitFor(MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS, Clock.getInstance().now() + SEND_TIMEOUT); }
synchronized (_sendingStates) { if (tagsSent != null) {
_sendingStates.remove(state); if (sentTags != null) {
} tagsSent.addAll(sentTags);
boolean found = state.received(MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS); }
boolean accepted = state.received(MessageStatusMessage.STATUS_SEND_ACCEPTED); }
if ( (!accepted) || (state.getMessageId() == null) ) { synchronized (_sendingStates) {
if (_log.shouldLog(Log.ERROR)) _log.error("State with nonce " + state.getNonce() + " was not accepted? (no messageId!!)"); _sendingStates.add(state);
nackTags(state); }
if (_log.shouldLog(Log.CRIT)) _log.log(Log.CRIT,"Disconnecting/reconnecting because we never were accepted!"); if (_log.shouldLog(Log.DEBUG))
disconnect(); _log.debug("Adding sending state " + state.getMessageId() + " / "
return false; + state.getNonce());
} _producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey);
state.waitFor(MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS, Clock.getInstance().now() + SEND_TIMEOUT);
if (_log.shouldLog(Log.DEBUG)) _log.debug("After waitFor sending state " + state.getMessageId().getMessageId() + " / " + state.getNonce() + " found = " + found); synchronized (_sendingStates) {
if (found) { _sendingStates.remove(state);
if (_log.shouldLog(Log.INFO)) _log.info("Message sent after " + state.getElapsed() + "ms with " + payload.length + " bytes"); }
ackTags(state); boolean found = state.received(MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS);
} else { boolean accepted = state.received(MessageStatusMessage.STATUS_SEND_ACCEPTED);
if (_log.shouldLog(Log.INFO)) _log.info("Message send failed after " + state.getElapsed() + "ms with " + payload.length + " bytes");
nackTags(state); if ((!accepted) || (state.getMessageId() == null)) {
} if (_log.shouldLog(Log.ERROR))
return found; _log.error("State with nonce " + state.getNonce()
+ " was not accepted? (no messageId!!)");
nackTags(state);
if (_log.shouldLog(Log.CRIT))
_log.log(Log.CRIT,
"Disconnecting/reconnecting because we never were accepted!");
disconnect();
return false;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("After waitFor sending state " + state.getMessageId().getMessageId()
+ " / " + state.getNonce() + " found = " + found);
if (found) {
if (_log.shouldLog(Log.INFO))
_log.info("Message sent after " + state.getElapsed() + "ms with "
+ payload.length + " bytes");
ackTags(state);
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Message send failed after " + state.getElapsed() + "ms with "
+ payload.length + " bytes");
nackTags(state);
}
return found;
} }
private void ackTags(MessageState state) { private void ackTags(MessageState state) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("ack tags for msgId " + state.getMessageId() + " / " + state.getNonce() + " key = " + state.getKey() + ", tags = " + state.getTags()); if (_log.shouldLog(Log.DEBUG))
if ( (state.getTags() != null) && (state.getTags().size() > 0) ) { _log.debug("ack tags for msgId " + state.getMessageId() + " / "
if (state.getNewKey() == null) + state.getNonce() + " key = " + state.getKey() + ", tags = "
SessionKeyManager.getInstance().tagsDelivered(state.getTo().getPublicKey(), state.getKey(), state.getTags()); + state.getTags());
else if ((state.getTags() != null) && (state.getTags().size() > 0)) {
SessionKeyManager.getInstance().tagsDelivered(state.getTo().getPublicKey(), state.getNewKey(), state.getTags()); if (state.getNewKey() == null)
} SessionKeyManager.getInstance().tagsDelivered(state.getTo().getPublicKey(), state.getKey(),
state.getTags());
else
SessionKeyManager.getInstance().tagsDelivered(state.getTo().getPublicKey(), state.getNewKey(),
state.getTags());
}
} }
private void nackTags(MessageState state) { private void nackTags(MessageState state) {
if (_log.shouldLog(Log.INFO)) _log.info("nack tags for msgId " + state.getMessageId() + " / " + state.getNonce() + " key = " + state.getKey()); if (_log.shouldLog(Log.INFO))
SessionKeyManager.getInstance().failTags(state.getTo().getPublicKey()); _log.info("nack tags for msgId " + state.getMessageId() + " / " + state.getNonce()
+ " key = " + state.getKey());
SessionKeyManager.getInstance().failTags(state.getTo().getPublicKey());
} }
public void receiveStatus(int msgId, long nonce, int status) { public void receiveStatus(int msgId, long nonce, int status) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received status " + status + " for msgId " + msgId + " / " + nonce); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received status " + status + " for msgId " + msgId + " / " + nonce);
MessageState state = null; MessageState state = null;
synchronized (_sendingStates) { synchronized (_sendingStates) {
for (Iterator iter = _sendingStates.iterator(); iter.hasNext(); ) { for (Iterator iter = _sendingStates.iterator(); iter.hasNext();) {
state = (MessageState)iter.next(); state = (MessageState) iter.next();
if (_log.shouldLog(Log.DEBUG)) _log.debug("State " + state.getMessageId() + " / " + state.getNonce()); if (_log.shouldLog(Log.DEBUG)) _log.debug("State " + state.getMessageId() + " / " + state.getNonce());
if (state.getNonce() == nonce) { if (state.getNonce() == nonce) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Found a matching state"); if (_log.shouldLog(Log.DEBUG)) _log.debug("Found a matching state");
break; break;
} else if ( (state.getMessageId() != null) && (state.getMessageId().getMessageId() == msgId) ) { } else if ((state.getMessageId() != null) && (state.getMessageId().getMessageId() == msgId)) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Found a matching state by msgId"); if (_log.shouldLog(Log.DEBUG)) _log.debug("Found a matching state by msgId");
break; break;
} else { } else {
if (_log.shouldLog(Log.DEBUG)) _log.debug("State does not match"); if (_log.shouldLog(Log.DEBUG)) _log.debug("State does not match");
state = null; state = null;
} }
} }
} }
if (state != null) { if (state != null) {
if (state.getMessageId() == null) { if (state.getMessageId() == null) {
MessageId id = new MessageId(); MessageId id = new MessageId();
id.setMessageId(msgId); id.setMessageId(msgId);
state.setMessageId(id); state.setMessageId(id);
} }
state.receive(status); state.receive(status);
} else { } else {
if (_log.shouldLog(Log.INFO)) _log.info("No matching state for messageId " + msgId + " / " + nonce + " w/ status = " + status); if (_log.shouldLog(Log.INFO))
} _log.info("No matching state for messageId " + msgId + " / " + nonce
+ " w/ status = " + status);
}
} }
/** /**
* Called whenever we want to reconnect (used only in the superclass). We need * Called whenever we want to reconnect (used only in the superclass). We need
* to override this to clear out the message state * to override this to clear out the message state
* *
*/ */
protected boolean reconnect() { protected boolean reconnect() {
// even if we succeed in reconnecting, we want to clear the old states, // even if we succeed in reconnecting, we want to clear the old states,
// since this will be a new sessionId // since this will be a new sessionId
clearStates(); clearStates();
return super.reconnect(); return super.reconnect();
} }
private void clearStates() { private void clearStates() {
synchronized (_sendingStates) { synchronized (_sendingStates) {
for (Iterator iter = _sendingStates.iterator(); iter.hasNext(); ) { for (Iterator iter = _sendingStates.iterator(); iter.hasNext();) {
MessageState state = (MessageState)iter.next(); MessageState state = (MessageState) iter.next();
state.cancel(); state.cancel();
} }
if (_log.shouldLog(Log.INFO)) _log.info("Disconnecting " + _sendingStates.size() + " states"); if (_log.shouldLog(Log.INFO)) _log.info("Disconnecting " + _sendingStates.size() + " states");
_sendingStates.clear(); _sendingStates.clear();
} }
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -22,20 +23,20 @@ public interface I2PSessionListener {
* @param size size of the message * @param size size of the message
*/ */
void messageAvailable(I2PSession session, int msgId, long size); void messageAvailable(I2PSession session, int msgId, long size);
/** Instruct the client that the session specified seems to be under attack /** Instruct the client that the session specified seems to be under attack
* and that the client may wish to move its destination to another router. * and that the client may wish to move its destination to another router.
* @param session session to report abuse to * @param session session to report abuse to
* @param severity how bad the abuse is * @param severity how bad the abuse is
*/ */
void reportAbuse(I2PSession session, int severity); void reportAbuse(I2PSession session, int severity);
/** /**
* Notify the client that the session has been terminated * Notify the client that the session has been terminated
* *
*/ */
void disconnected(I2PSession session); void disconnected(I2PSession session);
/** /**
* Notify the client that some error occurred * Notify the client that some error occurred
* *

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -27,36 +28,39 @@ class MessagePayloadMessageHandler extends HandlerImpl {
public MessagePayloadMessageHandler() { public MessagePayloadMessageHandler() {
super(MessagePayloadMessage.MESSAGE_TYPE); super(MessagePayloadMessage.MESSAGE_TYPE);
} }
public void handleMessage(I2CPMessage message, I2PSessionImpl session) { public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
_log.debug("Handle message " + message); _log.debug("Handle message " + message);
try { try {
MessagePayloadMessage msg = (MessagePayloadMessage)message; MessagePayloadMessage msg = (MessagePayloadMessage) message;
MessageId id = msg.getMessageId(); MessageId id = msg.getMessageId();
Payload payload = decryptPayload(msg, session); Payload payload = decryptPayload(msg, session);
session.addNewMessage(msg); session.addNewMessage(msg);
ReceiveMessageEndMessage m = new ReceiveMessageEndMessage(); ReceiveMessageEndMessage m = new ReceiveMessageEndMessage();
m.setMessageId(id); m.setMessageId(id);
m.setSessionId(msg.getSessionId()); m.setSessionId(msg.getSessionId());
session.sendMessage(m); session.sendMessage(m);
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
session.propogateError("Error handling a new payload message", dfe); session.propogateError("Error handling a new payload message", dfe);
} catch (I2PSessionException ise) { } catch (I2PSessionException ise) {
session.propogateError("Error handling a new payload message", ise); session.propogateError("Error handling a new payload message", ise);
} }
} }
/** /**
* Decrypt the payload * Decrypt the payload
*/ */
private Payload decryptPayload(MessagePayloadMessage msg, I2PSessionImpl session) throws DataFormatException { private Payload decryptPayload(MessagePayloadMessage msg, I2PSessionImpl session) throws DataFormatException {
Payload payload = msg.getPayload(); Payload payload = msg.getPayload();
byte[] data = ElGamalAESEngine.decrypt(payload.getEncryptedData(), session.getDecryptionKey()); byte[] data = ElGamalAESEngine.decrypt(payload.getEncryptedData(), session.getDecryptionKey());
if (data == null) { if (data == null) {
_log.error("Error decrypting the payload to public key " + session.getMyDestination().getPublicKey().toBase64() + "\nPayload: " + payload.calculateHash()); _log
throw new DataFormatException("Unable to decrypt the payload"); .error("Error decrypting the payload to public key "
} + session.getMyDestination().getPublicKey().toBase64() + "\nPayload: " + payload.calculateHash());
payload.setUnencryptedData(data); throw new DataFormatException("Unable to decrypt the payload");
}
payload.setUnencryptedData(data);
return payload; return payload;
} }
} }

View File

@@ -1,18 +1,17 @@
package net.i2p.client; package net.i2p.client;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.i2p.data.Destination;
import net.i2p.data.SessionKey;
import net.i2p.data.i2cp.MessageId; import net.i2p.data.i2cp.MessageId;
import net.i2p.data.i2cp.MessageStatusMessage; import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.data.SessionKey;
import net.i2p.data.Destination;
import net.i2p.util.Log;
import net.i2p.util.Clock; import net.i2p.util.Clock;
import net.i2p.util.Log;
import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
/** /**
* Contains the state of a payload message being sent to a peer * Contains the state of a payload message being sent to a peer
@@ -30,179 +29,240 @@ class MessageState {
private boolean _cancelled; private boolean _cancelled;
private long _created; private long _created;
private Object _lock = new Object(); private Object _lock = new Object();
public MessageState(long nonce) { public MessageState(long nonce) {
_nonce = nonce; _nonce = nonce;
_id = null; _id = null;
_receivedStatus = new HashSet(); _receivedStatus = new HashSet();
_cancelled = false; _cancelled = false;
_key = null; _key = null;
_newKey = null; _newKey = null;
_tags = null; _tags = null;
_to = null; _to = null;
_created = Clock.getInstance().now(); _created = Clock.getInstance().now();
} }
public void receive(int status) { public void receive(int status) {
synchronized (_receivedStatus) { synchronized (_receivedStatus) {
_receivedStatus.add(new Integer(status)); _receivedStatus.add(new Integer(status));
} }
synchronized (_lock) { synchronized (_lock) {
_lock.notifyAll(); _lock.notifyAll();
} }
} }
public void setMessageId(MessageId id) { _id = id; } public void setMessageId(MessageId id) {
public MessageId getMessageId() { return _id; } _id = id;
public long getNonce() { return _nonce; }
public void setKey(SessionKey key) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Setting key [" + _key + "] to [" + key + "]");
_key = key;
} }
public SessionKey getKey() { return _key; }
public void setNewKey(SessionKey key) { _newKey = key; }
public SessionKey getNewKey() { return _newKey; }
public void setTags(Set tags) { _tags = tags; }
public Set getTags() { return _tags; }
public void setTo(Destination dest) { _to = dest; }
public Destination getTo() { return _to; }
public long getElapsed() { return Clock.getInstance().now() - _created; } public MessageId getMessageId() {
return _id;
}
public long getNonce() {
return _nonce;
}
public void setKey(SessionKey key) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Setting key [" + _key + "] to [" + key + "]");
_key = key;
}
public SessionKey getKey() {
return _key;
}
public void setNewKey(SessionKey key) {
_newKey = key;
}
public SessionKey getNewKey() {
return _newKey;
}
public void setTags(Set tags) {
_tags = tags;
}
public Set getTags() {
return _tags;
}
public void setTo(Destination dest) {
_to = dest;
}
public Destination getTo() {
return _to;
}
public long getElapsed() {
return Clock.getInstance().now() - _created;
}
public void waitFor(int status, long expiration) { public void waitFor(int status, long expiration) {
while (true) { while (true) {
if (_cancelled) return; if (_cancelled) return;
long timeToWait = expiration - Clock.getInstance().now(); long timeToWait = expiration - Clock.getInstance().now();
if (timeToWait <= 0) { if (timeToWait <= 0) {
if (_log.shouldLog(Log.WARN)) _log.warn("Expired waiting for the status [" + status + "]"); if (_log.shouldLog(Log.WARN)) _log.warn("Expired waiting for the status [" + status + "]");
return; return;
} }
if (isSuccess(status) || isFailure(status)) { if (isSuccess(status) || isFailure(status)) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received a confirm (one way or the other)"); if (_log.shouldLog(Log.DEBUG)) _log.debug("Received a confirm (one way or the other)");
return; return;
} }
if (timeToWait > 5000) { if (timeToWait > 5000) {
timeToWait = 5000; timeToWait = 5000;
} }
synchronized (_lock) { synchronized (_lock) {
try { try {
_lock.wait(timeToWait); _lock.wait(timeToWait);
} catch (InterruptedException ie) {} } catch (InterruptedException ie) {
} }
} }
}
} }
private boolean isSuccess(int wantedStatus) { private boolean isSuccess(int wantedStatus) {
List received = null; List received = null;
synchronized (_receivedStatus) { synchronized (_receivedStatus) {
received = new ArrayList(_receivedStatus); received = new ArrayList(_receivedStatus);
//_receivedStatus.clear(); //_receivedStatus.clear();
} }
boolean rv = false; boolean rv = false;
if (_log.shouldLog(Log.DEBUG)) _log.debug("isSuccess(" + wantedStatus + "): " + received); if (_log.shouldLog(Log.DEBUG)) _log.debug("isSuccess(" + wantedStatus + "): " + received);
for (Iterator iter = received.iterator(); iter.hasNext(); ) { for (Iterator iter = received.iterator(); iter.hasNext();) {
Integer val = (Integer)iter.next(); Integer val = (Integer) iter.next();
int recv = val.intValue(); int recv = val.intValue();
switch (recv) { switch (recv) {
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE: case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
if (_log.shouldLog(Log.WARN)) _log.warn("Received best effort failure after " + getElapsed() + " from " + this.toString()); if (_log.shouldLog(Log.WARN))
rv = false; _log.warn("Received best effort failure after " + getElapsed() + " from "
break; + this.toString());
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE: rv = false;
if (_log.shouldLog(Log.WARN)) _log.warn("Received guaranteed failure after " + getElapsed() + " from " + this.toString()); break;
rv = false; case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
break; if (_log.shouldLog(Log.WARN))
case MessageStatusMessage.STATUS_SEND_ACCEPTED: _log.warn("Received guaranteed failure after " + getElapsed() + " from "
if (wantedStatus == MessageStatusMessage.STATUS_SEND_ACCEPTED) { + this.toString());
return true; // if we're only looking for accepted, take it directly (don't let any GUARANTEED_* override it) rv = false;
} else { break;
if (_log.shouldLog(Log.DEBUG)) _log.debug("Got accepted, but we're waiting for more from " + this.toString()); case MessageStatusMessage.STATUS_SEND_ACCEPTED:
continue; if (wantedStatus == MessageStatusMessage.STATUS_SEND_ACCEPTED) {
// ignore accepted, as we want something better return true; // if we're only looking for accepted, take it directly (don't let any GUARANTEED_* override it)
} } else {
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS: if (_log.shouldLog(Log.DEBUG))
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received best effort success after " + getElapsed() + " from " + this.toString()); _log.debug("Got accepted, but we're waiting for more from "
if (wantedStatus == recv) { + this.toString());
rv = true; continue;
} else { // ignore accepted, as we want something better
if (_log.shouldLog(Log.DEBUG)) _log.debug("Not guaranteed success, but best effort after " + getElapsed() + " will do... from " + this.toString()); }
rv = true; case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS:
} if (_log.shouldLog(Log.DEBUG))
break; _log.debug("Received best effort success after " + getElapsed()
case MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS: + " from " + this.toString());
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received guaranteed success after " + getElapsed() + " from " + this.toString()); if (wantedStatus == recv) {
// even if we're waiting for best effort success, guaranteed is good enough rv = true;
rv = true; } else {
break; if (_log.shouldLog(Log.DEBUG))
case -1: _log.debug("Not guaranteed success, but best effort after "
continue; + getElapsed() + " will do... from " + this.toString());
default: rv = true;
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received something else [" + recv + "]..."); }
} break;
} case MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS:
return rv; if (_log.shouldLog(Log.DEBUG))
_log.debug("Received guaranteed success after " + getElapsed() + " from "
+ this.toString());
// even if we're waiting for best effort success, guaranteed is good enough
rv = true;
break;
case -1:
continue;
default:
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received something else [" + recv + "]...");
}
}
return rv;
} }
private boolean isFailure(int wantedStatus) { private boolean isFailure(int wantedStatus) {
List received = null; List received = null;
synchronized (_receivedStatus) { synchronized (_receivedStatus) {
received = new ArrayList(_receivedStatus); received = new ArrayList(_receivedStatus);
//_receivedStatus.clear(); //_receivedStatus.clear();
} }
boolean rv = false; boolean rv = false;
if (_log.shouldLog(Log.DEBUG)) _log.debug("isFailure(" + wantedStatus + "): " + received); if (_log.shouldLog(Log.DEBUG)) _log.debug("isFailure(" + wantedStatus + "): " + received);
for (Iterator iter = received.iterator(); iter.hasNext(); ) { for (Iterator iter = received.iterator(); iter.hasNext();) {
Integer val = (Integer)iter.next(); Integer val = (Integer) iter.next();
int recv = val.intValue(); int recv = val.intValue();
switch (recv) { switch (recv) {
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE: case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
if (_log.shouldLog(Log.DEBUG)) _log.warn("Received best effort failure after " + getElapsed() + " from " + this.toString()); if (_log.shouldLog(Log.DEBUG))
rv = true; _log.warn("Received best effort failure after " + getElapsed() + " from "
break; + this.toString());
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE: rv = true;
if (_log.shouldLog(Log.DEBUG)) _log.warn("Received guaranteed failure after " + getElapsed() + " from " + this.toString()); break;
rv = true; case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
break; if (_log.shouldLog(Log.DEBUG))
case MessageStatusMessage.STATUS_SEND_ACCEPTED: _log.warn("Received guaranteed failure after " + getElapsed() + " from "
if (wantedStatus == MessageStatusMessage.STATUS_SEND_ACCEPTED) { + this.toString());
rv = false; rv = true;
} else { break;
if (_log.shouldLog(Log.DEBUG)) _log.debug("Got accepted, but we're waiting for more from " + this.toString()); case MessageStatusMessage.STATUS_SEND_ACCEPTED:
continue; if (wantedStatus == MessageStatusMessage.STATUS_SEND_ACCEPTED) {
// ignore accepted, as we want something better rv = false;
} } else {
break; if (_log.shouldLog(Log.DEBUG))
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS: _log.debug("Got accepted, but we're waiting for more from "
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received best effort success after " + getElapsed() + " from " + this.toString()); + this.toString());
if (wantedStatus == recv) { continue;
rv = false; // ignore accepted, as we want something better
} else { }
if (_log.shouldLog(Log.DEBUG)) _log.debug("Not guaranteed success, but best effort after " + getElapsed() + " will do... from " + this.toString()); break;
rv = false; case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS:
} if (_log.shouldLog(Log.DEBUG))
break; _log.debug("Received best effort success after " + getElapsed()
case MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS: + " from " + this.toString());
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received guaranteed success after " + getElapsed() + " from " + this.toString()); if (wantedStatus == recv) {
// even if we're waiting for best effort success, guaranteed is good enough rv = false;
rv = false; } else {
break; if (_log.shouldLog(Log.DEBUG))
case -1: _log.debug("Not guaranteed success, but best effort after "
continue; + getElapsed() + " will do... from " + this.toString());
default: rv = false;
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received something else [" + recv + "]..."); }
} break;
} case MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS:
return rv; if (_log.shouldLog(Log.DEBUG))
_log.debug("Received guaranteed success after " + getElapsed() + " from "
+ this.toString());
// even if we're waiting for best effort success, guaranteed is good enough
rv = false;
break;
case -1:
continue;
default:
if (_log.shouldLog(Log.DEBUG)) _log.debug("Received something else [" + recv + "]...");
}
}
return rv;
} }
/** true if the given status (or an equivilant) was received */ /** true if the given status (or an equivilant) was received */
public boolean received(int status) { public boolean received(int status) {
return isSuccess(status); return isSuccess(status);
} }
public void cancel() { public void cancel() {
_cancelled = true; _cancelled = true;
synchronized (_lock) { synchronized (_lock) {
_lock.notifyAll(); _lock.notifyAll();
} }
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,7 +9,9 @@ package net.i2p.client;
* *
*/ */
import net.i2p.data.i2cp.*; import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.MessageStatusMessage;
import net.i2p.data.i2cp.ReceiveMessageBeginMessage;
/** /**
* Handle I2CP MessageStatusMessages from the router. This currently only takes * Handle I2CP MessageStatusMessages from the router. This currently only takes
@@ -21,42 +24,44 @@ class MessageStatusMessageHandler extends HandlerImpl {
public MessageStatusMessageHandler() { public MessageStatusMessageHandler() {
super(MessageStatusMessage.MESSAGE_TYPE); super(MessageStatusMessage.MESSAGE_TYPE);
} }
public void handleMessage(I2CPMessage message, I2PSessionImpl session) { public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
boolean skipStatus = true; boolean skipStatus = true;
if (I2PClient.PROP_RELIABILITY_GUARANTEED.equals(session.getOptions().getProperty( if (I2PClient.PROP_RELIABILITY_GUARANTEED.equals(session.getOptions()
I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT))) .getProperty(I2PClient.PROP_RELIABILITY,
skipStatus = false; I2PClient.PROP_RELIABILITY_BEST_EFFORT)))
skipStatus = false;
_log.debug("Handle message " + message); _log.debug("Handle message " + message);
MessageStatusMessage msg = (MessageStatusMessage)message; MessageStatusMessage msg = (MessageStatusMessage) message;
switch (msg.getStatus()) { switch (msg.getStatus()) {
case MessageStatusMessage.STATUS_AVAILABLE: case MessageStatusMessage.STATUS_AVAILABLE:
ReceiveMessageBeginMessage m = new ReceiveMessageBeginMessage(); ReceiveMessageBeginMessage m = new ReceiveMessageBeginMessage();
m.setMessageId(msg.getMessageId()); m.setMessageId(msg.getMessageId());
m.setSessionId(msg.getSessionId()); m.setSessionId(msg.getSessionId());
try { try {
session.sendMessage(m); session.sendMessage(m);
} catch (I2PSessionException ise) { } catch (I2PSessionException ise) {
_log.error("Error asking for the message", ise); _log.error("Error asking for the message", ise);
} }
return; return;
case MessageStatusMessage.STATUS_SEND_ACCEPTED: case MessageStatusMessage.STATUS_SEND_ACCEPTED:
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus()); session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
// noop // noop
return; return;
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS: case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_SUCCESS:
case MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS: case MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS:
_log.info("Message delivery succeeded for message " + msg.getMessageId()); _log.info("Message delivery succeeded for message " + msg.getMessageId());
//if (!skipStatus) //if (!skipStatus)
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus()); session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
return; return;
case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE: case MessageStatusMessage.STATUS_SEND_BEST_EFFORT_FAILURE:
case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE: case MessageStatusMessage.STATUS_SEND_GUARANTEED_FAILURE:
_log.info("Message delivery FAILED for message " + msg.getMessageId()); _log.info("Message delivery FAILED for message " + msg.getMessageId());
//if (!skipStatus) //if (!skipStatus)
session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus()); session.receiveStatus(msg.getMessageId().getMessageId(), msg.getNonce(), msg.getStatus());
return; return;
default: default:
_log.error("Invalid message delivery status received: " + msg.getStatus()); _log.error("Invalid message delivery status received: " + msg.getStatus());
} }
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -33,91 +34,100 @@ import net.i2p.util.Log;
class RequestLeaseSetMessageHandler extends HandlerImpl { class RequestLeaseSetMessageHandler extends HandlerImpl {
private final static Log _log = new Log(RequestLeaseSetMessageHandler.class); private final static Log _log = new Log(RequestLeaseSetMessageHandler.class);
private Map _existingLeaseSets; private Map _existingLeaseSets;
public RequestLeaseSetMessageHandler() { public RequestLeaseSetMessageHandler() {
super(RequestLeaseSetMessage.MESSAGE_TYPE); super(RequestLeaseSetMessage.MESSAGE_TYPE);
_existingLeaseSets = new HashMap(32); _existingLeaseSets = new HashMap(32);
} }
public void handleMessage(I2CPMessage message, I2PSessionImpl session) { public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
_log.debug("Handle message " + message); _log.debug("Handle message " + message);
RequestLeaseSetMessage msg = (RequestLeaseSetMessage)message; RequestLeaseSetMessage msg = (RequestLeaseSetMessage) message;
LeaseSet leaseSet = new LeaseSet(); LeaseSet leaseSet = new LeaseSet();
for (int i = 0; i < msg.getEndpoints(); i++) { for (int i = 0; i < msg.getEndpoints(); i++) {
Lease lease = new Lease(); Lease lease = new Lease();
lease.setRouterIdentity(msg.getRouter(i)); lease.setRouterIdentity(msg.getRouter(i));
lease.setTunnelId(msg.getTunnelId(i)); lease.setTunnelId(msg.getTunnelId(i));
lease.setEndDate(msg.getEndDate()); lease.setEndDate(msg.getEndDate());
//lease.setStartDate(msg.getStartDate()); //lease.setStartDate(msg.getStartDate());
leaseSet.addLease(lease); leaseSet.addLease(lease);
} }
// also, if this session is connected to multiple routers, include other leases here // also, if this session is connected to multiple routers, include other leases here
leaseSet.setDestination(session.getMyDestination()); leaseSet.setDestination(session.getMyDestination());
// reuse the old keys for the client // reuse the old keys for the client
LeaseInfo li = null; LeaseInfo li = null;
synchronized (_existingLeaseSets) { synchronized (_existingLeaseSets) {
if (_existingLeaseSets.containsKey(session.getMyDestination())) if (_existingLeaseSets.containsKey(session.getMyDestination()))
li = (LeaseInfo)_existingLeaseSets.get(session.getMyDestination()); li = (LeaseInfo) _existingLeaseSets.get(session.getMyDestination());
} }
if (li == null) { if (li == null) {
li = new LeaseInfo(session.getMyDestination()); li = new LeaseInfo(session.getMyDestination());
synchronized (_existingLeaseSets) { synchronized (_existingLeaseSets) {
_existingLeaseSets.put(session.getMyDestination(), li); _existingLeaseSets.put(session.getMyDestination(), li);
} }
_log.debug("Creating new leaseInfo keys", new Exception("new leaseInfo keys")); _log.debug("Creating new leaseInfo keys", new Exception("new leaseInfo keys"));
} else { } else {
_log.debug("Caching the old leaseInfo keys", new Exception("cached! w00t")); _log.debug("Caching the old leaseInfo keys", new Exception("cached! w00t"));
} }
leaseSet.setEncryptionKey(li.getPublicKey()); leaseSet.setEncryptionKey(li.getPublicKey());
leaseSet.setSigningKey(li.getSigningPublicKey()); leaseSet.setSigningKey(li.getSigningPublicKey());
try { try {
leaseSet.sign(session.getPrivateKey()); leaseSet.sign(session.getPrivateKey());
session.getProducer().createLeaseSet(session, leaseSet, li.getSigningPrivateKey(), li.getPrivateKey()); session.getProducer().createLeaseSet(session, leaseSet, li.getSigningPrivateKey(), li.getPrivateKey());
session.setLeaseSet(leaseSet); session.setLeaseSet(leaseSet);
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
session.propogateError("Error signing the leaseSet", dfe); session.propogateError("Error signing the leaseSet", dfe);
} catch (I2PSessionException ise) { } catch (I2PSessionException ise) {
session.propogateError("Error sending the signed leaseSet", ise); session.propogateError("Error sending the signed leaseSet", ise);
} }
} }
private static class LeaseInfo { private static class LeaseInfo {
private PublicKey _pubKey; private PublicKey _pubKey;
private PrivateKey _privKey; private PrivateKey _privKey;
private SigningPublicKey _signingPubKey; private SigningPublicKey _signingPubKey;
private SigningPrivateKey _signingPrivKey; private SigningPrivateKey _signingPrivKey;
private Destination _dest; private Destination _dest;
public LeaseInfo(Destination dest) { public LeaseInfo(Destination dest) {
_dest = dest; _dest = dest;
Object encKeys[] = KeyGenerator.getInstance().generatePKIKeypair(); Object encKeys[] = KeyGenerator.getInstance().generatePKIKeypair();
Object signKeys[] = KeyGenerator.getInstance().generateSigningKeypair(); Object signKeys[] = KeyGenerator.getInstance().generateSigningKeypair();
_pubKey = (PublicKey)encKeys[0]; _pubKey = (PublicKey) encKeys[0];
_privKey = (PrivateKey)encKeys[1]; _privKey = (PrivateKey) encKeys[1];
_signingPubKey = (SigningPublicKey)signKeys[0]; _signingPubKey = (SigningPublicKey) signKeys[0];
_signingPrivKey = (SigningPrivateKey)signKeys[1]; _signingPrivKey = (SigningPrivateKey) signKeys[1];
} }
public PublicKey getPublicKey() { return _pubKey; }
public PrivateKey getPrivateKey() { return _privKey; } public PublicKey getPublicKey() {
public SigningPublicKey getSigningPublicKey() { return _signingPubKey; } return _pubKey;
public SigningPrivateKey getSigningPrivateKey() { return _signingPrivKey; } }
public int hashCode() { public PrivateKey getPrivateKey() {
return DataHelper.hashCode(_pubKey) + return _privKey;
7*DataHelper.hashCode(_privKey) + }
7*7*DataHelper.hashCode(_signingPubKey) +
7*7*7*DataHelper.hashCode(_signingPrivKey); public SigningPublicKey getSigningPublicKey() {
} return _signingPubKey;
}
public boolean equals(Object obj) {
if ( (obj == null) || !(obj instanceof LeaseInfo) ) public SigningPrivateKey getSigningPrivateKey() {
return false; return _signingPrivKey;
LeaseInfo li = (LeaseInfo)obj; }
return DataHelper.eq(_pubKey, li.getPublicKey()) &&
DataHelper.eq(_privKey, li.getPrivateKey()) && public int hashCode() {
DataHelper.eq(_signingPubKey, li.getSigningPublicKey()) && return DataHelper.hashCode(_pubKey) + 7 * DataHelper.hashCode(_privKey) + 7 * 7
DataHelper.eq(_signingPrivKey, li.getSigningPrivateKey()); * DataHelper.hashCode(_signingPubKey) + 7 * 7 * 7 * DataHelper.hashCode(_signingPrivKey);
} }
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof LeaseInfo)) return false;
LeaseInfo li = (LeaseInfo) obj;
return DataHelper.eq(_pubKey, li.getPublicKey()) && DataHelper.eq(_privKey, li.getPrivateKey())
&& DataHelper.eq(_signingPubKey, li.getSigningPublicKey())
&& DataHelper.eq(_signingPrivKey, li.getSigningPrivateKey());
}
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,7 +9,8 @@ package net.i2p.client;
* *
*/ */
import net.i2p.data.i2cp.*; import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.SessionStatusMessage;
/** /**
* Handle I2CP SessionStatusMessagese from the router, updating the session as * Handle I2CP SessionStatusMessagese from the router, updating the session as
@@ -20,26 +22,27 @@ class SessionStatusMessageHandler extends HandlerImpl {
public SessionStatusMessageHandler() { public SessionStatusMessageHandler() {
super(SessionStatusMessage.MESSAGE_TYPE); super(SessionStatusMessage.MESSAGE_TYPE);
} }
public void handleMessage(I2CPMessage message, I2PSessionImpl session) { public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
_log.debug("Handle message " + message); _log.debug("Handle message " + message);
SessionStatusMessage msg = (SessionStatusMessage)message; SessionStatusMessage msg = (SessionStatusMessage) message;
session.setSessionId(msg.getSessionId()); session.setSessionId(msg.getSessionId());
switch (msg.getStatus()) { switch (msg.getStatus()) {
case SessionStatusMessage.STATUS_CREATED: case SessionStatusMessage.STATUS_CREATED:
_log.info("Session created successfully"); _log.info("Session created successfully");
break; break;
case SessionStatusMessage.STATUS_DESTROYED: case SessionStatusMessage.STATUS_DESTROYED:
_log.info("Session destroyed"); _log.info("Session destroyed");
session.destroySession(); session.destroySession();
break; break;
case SessionStatusMessage.STATUS_INVALID: case SessionStatusMessage.STATUS_INVALID:
session.destroySession(); session.destroySession();
break; break;
case SessionStatusMessage.STATUS_UPDATED: case SessionStatusMessage.STATUS_UPDATED:
_log.info("Session status updated"); _log.info("Session status updated");
break; break;
default: default:
_log.warn("Unknown session status sent: " + msg.getStatus()); _log.warn("Unknown session status sent: " + msg.getStatus());
} }
return; return;
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,7 +9,8 @@ package net.i2p.client;
* *
*/ */
import net.i2p.data.i2cp.*; import net.i2p.data.i2cp.I2CPMessage;
import net.i2p.data.i2cp.SetDateMessage;
import net.i2p.util.Clock; import net.i2p.util.Clock;
/** /**
@@ -20,10 +22,11 @@ class SetDateMessageHandler extends HandlerImpl {
public SetDateMessageHandler() { public SetDateMessageHandler() {
super(SetDateMessage.MESSAGE_TYPE); super(SetDateMessage.MESSAGE_TYPE);
} }
public void handleMessage(I2CPMessage message, I2PSessionImpl session) { public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
_log.debug("Handle message " + message); _log.debug("Handle message " + message);
SetDateMessage msg = (SetDateMessage)message; SetDateMessage msg = (SetDateMessage) message;
Clock.getInstance().setNow(msg.getDate().getTime()); Clock.getInstance().setNow(msg.getDate().getTime());
session.dateUpdated(); session.dateUpdated();
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -30,9 +31,9 @@ public class TestClient implements I2PSessionListener {
private static Destination _dest1; private static Destination _dest1;
private static Destination _dest2; private static Destination _dest2;
private boolean _stillRunning; private boolean _stillRunning;
public void runTest(String keyfile, boolean isDest1) { public void runTest(String keyfile, boolean isDest1) {
_stillRunning = true; _stillRunning = true;
try { try {
I2PClient client = I2PClientFactory.createClient(); I2PClient client = I2PClientFactory.createClient();
File file = new File(keyfile); File file = new File(keyfile);
@@ -54,68 +55,91 @@ public class TestClient implements I2PSessionListener {
options.setProperty(I2PClient.PROP_TCP_PORT, System.getProperty(I2PClient.PROP_TCP_PORT)); options.setProperty(I2PClient.PROP_TCP_PORT, System.getProperty(I2PClient.PROP_TCP_PORT));
I2PSession session = client.createSession(new FileInputStream(file), options); I2PSession session = client.createSession(new FileInputStream(file), options);
session.setSessionListener(this); session.setSessionListener(this);
_log.debug("Before connect..."); _log.debug("Before connect...");
session.connect(); session.connect();
_log.debug("Connected"); _log.debug("Connected");
// wait until the other one is connected // wait until the other one is connected
while ( (_dest1 == null) || (_dest2 == null) ) while ((_dest1 == null) || (_dest2 == null))
try { Thread.sleep(500); } catch (InterruptedException ie) {} try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
if (isDest1) { if (isDest1) {
Destination otherD = (isDest1 ? _dest2 : _dest1); Destination otherD = (isDest1 ? _dest2 : _dest1);
boolean accepted = session.sendMessage(otherD, ("Hello other side. I am" + (isDest1 ? "" : " NOT") + " dest1").getBytes()); boolean accepted = session
} else { .sendMessage(
while (_stillRunning) { otherD,
try { ("Hello other side. I am" + (isDest1 ? "" : " NOT") + " dest1")
_log.debug("waiting for a message..."); .getBytes());
Thread.sleep(1000); } else {
} catch (InterruptedException ie) {} while (_stillRunning) {
} try {
try { Thread.sleep(5000); } catch (InterruptedException ie) {} _log.debug("waiting for a message...");
System.exit(0); Thread.sleep(1000);
} } catch (InterruptedException ie) {
}
}
try {
Thread.sleep(5000);
} catch (InterruptedException ie) {
}
System.exit(0);
}
//session.destroySession(); //session.destroySession();
} catch (Exception e) { } catch (Exception e) {
_log.error("Error running the test for isDest1? " + isDest1, e); _log.error("Error running the test for isDest1? " + isDest1, e);
} }
} }
public static void main(String args[]) { public static void main(String args[]) {
doTest(); doTest();
try { Thread.sleep(30*1000); } catch (InterruptedException ie) {} try {
Thread.sleep(30 * 1000);
} catch (InterruptedException ie) {
}
} }
static void doTest() { static void doTest() {
Thread test1 = new I2PThread(new Runnable() { public void run() { (new TestClient()).runTest("test1.keyfile", true); } } ); Thread test1 = new I2PThread(new Runnable() {
Thread test2 = new I2PThread(new Runnable() { public void run() { (new TestClient()).runTest("test2.keyfile", false); } } ); public void run() {
(new TestClient()).runTest("test1.keyfile", true);
}
});
Thread test2 = new I2PThread(new Runnable() {
public void run() {
(new TestClient()).runTest("test2.keyfile", false);
}
});
test1.start(); test1.start();
test2.start(); test2.start();
_log.debug("Test threads started"); _log.debug("Test threads started");
} }
public void disconnected(I2PSession session) { public void disconnected(I2PSession session) {
_log.debug("Disconnected"); _log.debug("Disconnected");
_stillRunning = false; _stillRunning = false;
} }
public void errorOccurred(I2PSession session, String message, Throwable error) { public void errorOccurred(I2PSession session, String message, Throwable error) {
_log.debug("Error occurred: " + message, error); _log.debug("Error occurred: " + message, error);
} }
public void messageAvailable(I2PSession session, int msgId, long size) { public void messageAvailable(I2PSession session, int msgId, long size) {
_log.debug("Message available for us! id = " + msgId + " of size " + size); _log.debug("Message available for us! id = " + msgId + " of size " + size);
try { try {
byte msg[] = session.receiveMessage(msgId); byte msg[] = session.receiveMessage(msgId);
_log.debug("Content of message " + msgId+ ":\n"+new String(msg)); _log.debug("Content of message " + msgId + ":\n" + new String(msg));
_stillRunning = false; _stillRunning = false;
} catch (I2PSessionException ise) { } catch (I2PSessionException ise) {
_log.error("Error fetching available message", ise); _log.error("Error fetching available message", ise);
} }
} }
public void reportAbuse(I2PSession session, int severity) { public void reportAbuse(I2PSession session, int severity) {
_log.debug("Abuse reported of severity " + severity); _log.debug("Abuse reported of severity " + severity);
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.client; package net.i2p.client;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -27,9 +28,11 @@ public class TestServer implements Runnable {
private final static Log _log = new Log(TestServer.class); private final static Log _log = new Log(TestServer.class);
private ServerSocket _socket; private ServerSocket _socket;
public static int LISTEN_PORT = 7654; public static int LISTEN_PORT = 7654;
protected void setPort(int port) { LISTEN_PORT = port; } protected void setPort(int port) {
LISTEN_PORT = port;
}
/** /**
* Start up the socket listener, listens for connections, and * Start up the socket listener, listens for connections, and
* fires those connections off via {@link #runConnection runConnection}. * fires those connections off via {@link #runConnection runConnection}.
@@ -38,47 +41,49 @@ public class TestServer implements Runnable {
* *
*/ */
public void runServer() { public void runServer() {
try { try {
_socket = new ServerSocket(LISTEN_PORT); _socket = new ServerSocket(LISTEN_PORT);
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error listening", ioe); _log.error("Error listening", ioe);
return; return;
} }
while (true) { while (true) {
try { try {
Socket socket = _socket.accept(); Socket socket = _socket.accept();
runConnection(socket); runConnection(socket);
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Server error accepting", ioe); _log.error("Server error accepting", ioe);
} }
} }
} }
/** /**
* Handle the connection by passing it off to a ConnectionRunner * Handle the connection by passing it off to a ConnectionRunner
* *
*/ */
protected void runConnection(Socket socket) throws IOException { protected void runConnection(Socket socket) throws IOException {
ConnectionRunner runner = new ConnectionRunner(socket); ConnectionRunner runner = new ConnectionRunner(socket);
runner.doYourThing(); runner.doYourThing();
} }
public void run() { runServer(); } public void run() {
runServer();
}
/** /**
* Fire up the router * Fire up the router
*/ */
public static void main(String args[]) { public static void main(String args[]) {
if (args.length == 1) { if (args.length == 1) {
} else if (args.length == 2) { } else if (args.length == 2) {
try { try {
LISTEN_PORT = Integer.parseInt(args[1]); LISTEN_PORT = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
_log.error("Invalid port number specified (" + args[1] + "), using " + LISTEN_PORT, nfe); _log.error("Invalid port number specified (" + args[1] + "), using " + LISTEN_PORT, nfe);
} }
} }
TestServer server = new TestServer(); TestServer server = new TestServer();
Thread t = new I2PThread(server); Thread t = new I2PThread(server);
t.start(); t.start();
} }
} }

View File

@@ -1,23 +1,23 @@
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by mihi in 2004 and released into the public domain * Written by mihi in 2004 and released into the public domain
* with no warranty of any kind, either expressed or implied. * with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat * It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk. * your children, but it might. Use at your own risk.
*/ */
package net.i2p.client.naming; package net.i2p.client.naming;
import net.i2p.data.Destination; import net.i2p.data.Destination;
/** /**
* A Dummy naming service that can only handle base64 destinations. * A Dummy naming service that can only handle base64 destinations.
*/ */
class DummyNamingService extends NamingService { class DummyNamingService extends NamingService {
public Destination lookup(String hostname) { public Destination lookup(String hostname) {
return lookupBase64(hostname); return lookupBase64(hostname);
} }
public String reverseLookup(Destination dest) { public String reverseLookup(Destination dest) {
return null; return null;
} }
} }

View File

@@ -25,42 +25,44 @@ public class HostsTxtNamingService extends NamingService {
* given file for hostname=destKey values when resolving names * given file for hostname=destKey values when resolving names
*/ */
public final static String PROP_HOSTS_FILE = "i2p.hostsfile"; public final static String PROP_HOSTS_FILE = "i2p.hostsfile";
/** default hosts.txt filename */ /** default hosts.txt filename */
public final static String DEFAULT_HOSTS_FILE = "hosts.txt"; public final static String DEFAULT_HOSTS_FILE = "hosts.txt";
private final static Log _log = new Log(HostsTxtNamingService.class); private final static Log _log = new Log(HostsTxtNamingService.class);
public Destination lookup(String hostname) { public Destination lookup(String hostname) {
// Try to look it up in hosts.txt // Try to look it up in hosts.txt
// Reload file each time to catch changes. // Reload file each time to catch changes.
// (and it's easier :P // (and it's easier :P
String hostsfile=System.getProperty(PROP_HOSTS_FILE, String hostsfile = System.getProperty(PROP_HOSTS_FILE, DEFAULT_HOSTS_FILE);
DEFAULT_HOSTS_FILE ); Properties hosts = new Properties();
Properties hosts=new Properties(); FileInputStream fis = null;
FileInputStream fis = null; try {
try { File f = new File(hostsfile);
File f = new File(hostsfile); if (f.canRead()) {
if(f.canRead()) { fis = new FileInputStream(f);
fis = new FileInputStream(f); hosts.load(fis);
hosts.load(fis); } else {
} else { _log.error("Hosts file " + hostsfile + " does not exist.");
_log.error("Hosts file " + hostsfile + " does not exist."); }
} } catch (Exception ioe) {
} catch (Exception ioe) { _log.error("Error loading hosts file " + hostsfile, ioe);
_log.error("Error loading hosts file " + hostsfile, ioe ); } finally {
} finally { if (fis != null) try {
if (fis != null) try {fis.close();} catch (IOException ioe) {} fis.close();
} } catch (IOException ioe) {
String res = hosts.getProperty(hostname); }
// If we can't find name in hosts, assume it's a key. }
if ((res == null) || (res.trim().length() == 0)) { String res = hosts.getProperty(hostname);
res = hostname; // If we can't find name in hosts, assume it's a key.
} if ((res == null) || (res.trim().length() == 0)) {
return lookupBase64(res); res = hostname;
}
return lookupBase64(res);
} }
public String reverseLookup(Destination dest) { public String reverseLookup(Destination dest) {
return null; return null;
} }
} }

View File

@@ -19,9 +19,8 @@ public abstract class NamingService {
private final static Log _log = new Log(NamingService.class); private final static Log _log = new Log(NamingService.class);
private static final String PROP_IMPL = "i2p.naming.impl"; private static final String PROP_IMPL = "i2p.naming.impl";
private static final String DEFAULT_IMPL= private static final String DEFAULT_IMPL = "net.i2p.client.naming.HostsTxtNamingService";
"net.i2p.client.naming.HostsTxtNamingService";
/** /**
* Look up a host name. * Look up a host name.
* @return the Destination for this host name, or * @return the Destination for this host name, or
@@ -43,19 +42,18 @@ public abstract class NamingService {
* implementations. * implementations.
*/ */
protected Destination lookupBase64(String hostname) { protected Destination lookupBase64(String hostname) {
try { try {
Destination result = new Destination(); Destination result = new Destination();
result.fromBase64(hostname); result.fromBase64(hostname);
return result; return result;
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
if (_log.shouldLog(Log.WARN)) if (_log.shouldLog(Log.WARN)) _log.warn("Error translating [" + hostname + "]", dfe);
_log.warn("Error translating [" + hostname + "]", dfe); return null;
return null; }
}
} }
private static NamingService instance = null; private static NamingService instance = null;
/** /**
* Get a naming service instance. This method ensures that there * Get a naming service instance. This method ensures that there
* will be only one naming service instance (singleton) as well as * will be only one naming service instance (singleton) as well as
@@ -63,16 +61,15 @@ public abstract class NamingService {
* property. * property.
*/ */
public static synchronized NamingService getInstance() { public static synchronized NamingService getInstance() {
if (instance == null) { if (instance == null) {
String impl = System.getProperty(PROP_IMPL, String impl = System.getProperty(PROP_IMPL, DEFAULT_IMPL);
DEFAULT_IMPL); try {
try { instance = (NamingService) Class.forName(impl).newInstance();
instance = (NamingService) Class.forName(impl).newInstance(); } catch (Exception ex) {
} catch (Exception ex) { _log.error("Cannot loadNaming service implementation", ex);
_log.error("Cannot loadNaming service implementation", ex); instance = new DummyNamingService(); // fallback
instance = new DummyNamingService(); // fallback }
} }
} return instance;
return instance;
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.crypto; package net.i2p.crypto;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,137 +9,138 @@ package net.i2p.crypto;
* *
*/ */
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import net.i2p.data.DataFormatException; import net.i2p.data.DataFormatException;
import net.i2p.data.SessionKey;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.SessionKey;
import net.i2p.util.Log; import net.i2p.util.Log;
import net.i2p.util.RandomSource; import net.i2p.util.RandomSource;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
/** /**
* Wrapper singleton for AES cypher operation. * Wrapper singleton for AES cypher operation.
* *
* @author jrandom * @author jrandom
*/ */
public class AESEngine { public class AESEngine {
private final static Log _log = new Log(AESEngine.class); private final static Log _log = new Log(AESEngine.class);
private static AESEngine _engine; private static AESEngine _engine;
static { static {
if ("off".equals(System.getProperty("i2p.encryption", "on"))) if ("off".equals(System.getProperty("i2p.encryption", "on")))
_engine = new AESEngine(); _engine = new AESEngine();
else else
_engine = new CryptixAESEngine(); _engine = new CryptixAESEngine();
}
public static AESEngine getInstance() {
return _engine;
} }
public static AESEngine getInstance() { return _engine; }
/** Encrypt the payload with the session key /** Encrypt the payload with the session key
* @param payload data to be encrypted * @param payload data to be encrypted
* @param sessionKey private esession key to encrypt to * @param sessionKey private esession key to encrypt to
* @param initializationVector IV for CBC * @param initializationVector IV for CBC
* @return encrypted data * @return encrypted data
*/ */
public byte[] encrypt(byte payload[], SessionKey sessionKey, byte initializationVector[]) { public byte[] encrypt(byte payload[], SessionKey sessionKey, byte initializationVector[]) {
if ( (initializationVector == null) || (payload == null) || (sessionKey == null) || (initializationVector.length != 16) ) if ((initializationVector == null) || (payload == null) || (sessionKey == null)
return null; || (initializationVector.length != 16)) return null;
byte cyphertext[] = new byte[payload.length+(16-(payload.length%16))]; byte cyphertext[] = new byte[payload.length + (16 - (payload.length % 16))];
_log.warn("Warning: AES is disabled"); _log.warn("Warning: AES is disabled");
System.arraycopy(payload, 0, cyphertext, 0, payload.length); System.arraycopy(payload, 0, cyphertext, 0, payload.length);
return cyphertext; return cyphertext;
} }
public byte[] safeEncrypt(byte payload[], SessionKey sessionKey, byte iv[], int paddedSize) { public byte[] safeEncrypt(byte payload[], SessionKey sessionKey, byte iv[], int paddedSize) {
if ( (iv == null) || (payload == null) || (sessionKey == null) || (iv.length != 16) ) if ((iv == null) || (payload == null) || (sessionKey == null) || (iv.length != 16)) return null;
return null;
ByteArrayOutputStream baos = new ByteArrayOutputStream(paddedSize + 64);
ByteArrayOutputStream baos = new ByteArrayOutputStream(paddedSize+64); Hash h = SHA256Generator.getInstance().calculateHash(sessionKey.getData());
Hash h = SHA256Generator.getInstance().calculateHash(sessionKey.getData()); try {
try { h.writeBytes(baos);
h.writeBytes(baos); DataHelper.writeLong(baos, 4, payload.length);
DataHelper.writeLong(baos, 4, payload.length); baos.write(payload);
baos.write(payload); byte tv[] = baos.toByteArray();
byte tv[] = baos.toByteArray(); baos.write(ElGamalAESEngine.getPadding(tv.length, paddedSize));
baos.write(ElGamalAESEngine.getPadding(tv.length, paddedSize)); } catch (IOException ioe) {
} catch (IOException ioe) { _log.error("Error writing data", ioe);
_log.error("Error writing data", ioe); return null;
return null; } catch (DataFormatException dfe) {
} catch (DataFormatException dfe) { _log.error("Error writing data", dfe);
_log.error("Error writing data", dfe); return null;
return null; }
} return encrypt(baos.toByteArray(), sessionKey, iv);
return encrypt(baos.toByteArray(), sessionKey, iv);
} }
public byte[] safeDecrypt(byte payload[], SessionKey sessionKey, byte iv[]) { public byte[] safeDecrypt(byte payload[], SessionKey sessionKey, byte iv[]) {
if ( (iv == null) || (payload == null) || (sessionKey == null) || (iv.length != 16) ) if ((iv == null) || (payload == null) || (sessionKey == null) || (iv.length != 16)) return null;
return null;
byte decr[] = decrypt(payload, sessionKey, iv);
byte decr[] = decrypt(payload, sessionKey, iv); if (decr == null) {
if (decr == null) { _log.error("Error decrypting the data - payload " + payload.length + " decrypted to null");
_log.error("Error decrypting the data - payload " + payload.length + " decrypted to null"); return null;
return null; }
} ByteArrayInputStream bais = new ByteArrayInputStream(decr);
ByteArrayInputStream bais = new ByteArrayInputStream(decr); Hash h = SHA256Generator.getInstance().calculateHash(sessionKey.getData());
Hash h = SHA256Generator.getInstance().calculateHash(sessionKey.getData()); try {
try { Hash rh = new Hash();
Hash rh = new Hash(); rh.readBytes(bais);
rh.readBytes(bais); if (!h.equals(rh)) {
if (!h.equals(rh)) { _log.error("Hash does not match [key=" + sessionKey + " / iv =" + DataHelper.toString(iv, iv.length)
_log.error("Hash does not match [key=" + sessionKey + " / iv =" + DataHelper.toString(iv, iv.length) + "]", new Exception("Hash error")); + "]", new Exception("Hash error"));
return null; return null;
} }
long len = DataHelper.readLong(bais, 4); long len = DataHelper.readLong(bais, 4);
byte data[] = new byte[(int)len]; byte data[] = new byte[(int) len];
int read = bais.read(data); int read = bais.read(data);
if (read != len) { if (read != len) {
_log.error("Not enough to read"); _log.error("Not enough to read");
return null; return null;
} }
return data; return data;
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error writing data", ioe); _log.error("Error writing data", ioe);
return null; return null;
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
_log.error("Error writing data", dfe); _log.error("Error writing data", dfe);
return null; return null;
} }
} }
/** decrypt the data with the session key provided /** decrypt the data with the session key provided
* @param cyphertext encrypted data * @param cyphertext encrypted data
* @param sessionKey private session key * @param sessionKey private session key
* @param initializationVector IV for CBC * @param initializationVector IV for CBC
* @return unencrypted data * @return unencrypted data
*/ */
public byte[] decrypt(byte cyphertext[], SessionKey sessionKey, byte initializationVector[]) { public byte[] decrypt(byte cyphertext[], SessionKey sessionKey, byte initializationVector[]) {
if ( (initializationVector == null) || (cyphertext == null) || (sessionKey == null) || (initializationVector.length != 16) ) if ((initializationVector == null) || (cyphertext == null) || (sessionKey == null)
return null; || (initializationVector.length != 16)) return null;
byte payload[] = new byte[cyphertext.length]; byte payload[] = new byte[cyphertext.length];
_log.warn("Warning: AES is disabled"); _log.warn("Warning: AES is disabled");
return cyphertext; return cyphertext;
} }
public static void main(String args[]) { public static void main(String args[]) {
SessionKey key = KeyGenerator.getInstance().generateSessionKey(); SessionKey key = KeyGenerator.getInstance().generateSessionKey();
byte iv[] = new byte[16]; byte iv[] = new byte[16];
RandomSource.getInstance().nextBytes(iv); RandomSource.getInstance().nextBytes(iv);
byte sbuf[] = new byte[16]; byte sbuf[] = new byte[16];
RandomSource.getInstance().nextBytes(sbuf); RandomSource.getInstance().nextBytes(sbuf);
byte se[] = AESEngine.getInstance().encrypt(sbuf, key, iv); byte se[] = AESEngine.getInstance().encrypt(sbuf, key, iv);
byte sd[] = AESEngine.getInstance().decrypt(se, key, iv); byte sd[] = AESEngine.getInstance().decrypt(se, key, iv);
_log.debug("Short test: " + DataHelper.eq(sd, sbuf)); _log.debug("Short test: " + DataHelper.eq(sd, sbuf));
byte lbuf[] = new byte[1024]; byte lbuf[] = new byte[1024];
RandomSource.getInstance().nextBytes(sbuf); RandomSource.getInstance().nextBytes(sbuf);
byte le[] = AESEngine.getInstance().safeEncrypt(lbuf, key, iv, 2048); byte le[] = AESEngine.getInstance().safeEncrypt(lbuf, key, iv, 2048);
byte ld[] = AESEngine.getInstance().safeDecrypt(le, key, iv); byte ld[] = AESEngine.getInstance().safeDecrypt(le, key, iv);
_log.debug("Long test: " + DataHelper.eq(ld, lbuf)); _log.debug("Long test: " + DataHelper.eq(ld, lbuf));
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.crypto; package net.i2p.crypto;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -20,8 +21,8 @@ import net.i2p.data.Base64;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.SessionKey; import net.i2p.data.SessionKey;
import net.i2p.util.Log;
import net.i2p.util.Clock; import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.util.RandomSource; import net.i2p.util.RandomSource;
/** /**
@@ -45,106 +46,118 @@ public class AESInputStream extends FilterInputStream {
private long _cumulativePaddingStripped; // how many bytes have been stripped private long _cumulativePaddingStripped; // how many bytes have been stripped
private ByteArrayOutputStream _encryptedBuf; // read from the stream but not yet decrypted private ByteArrayOutputStream _encryptedBuf; // read from the stream but not yet decrypted
private List _readyBuf; // list of Bytes ready to be consumed, where index 0 is the first private List _readyBuf; // list of Bytes ready to be consumed, where index 0 is the first
private final static int BLOCK_SIZE = CryptixRijndael_Algorithm._BLOCK_SIZE; private final static int BLOCK_SIZE = CryptixRijndael_Algorithm._BLOCK_SIZE;
private final static int READ_SIZE = BLOCK_SIZE; private final static int READ_SIZE = BLOCK_SIZE;
private final static int DECRYPT_SIZE = BLOCK_SIZE-1; private final static int DECRYPT_SIZE = BLOCK_SIZE - 1;
public AESInputStream(InputStream source, SessionKey key, byte iv[]) { public AESInputStream(InputStream source, SessionKey key, byte iv[]) {
super(source); super(source);
_key = key; _key = key;
_lastBlock = new byte[BLOCK_SIZE]; _lastBlock = new byte[BLOCK_SIZE];
System.arraycopy(iv, 0, _lastBlock, 0, BLOCK_SIZE); System.arraycopy(iv, 0, _lastBlock, 0, BLOCK_SIZE);
_encryptedBuf = new ByteArrayOutputStream(BLOCK_SIZE); _encryptedBuf = new ByteArrayOutputStream(BLOCK_SIZE);
_readyBuf = new LinkedList(); _readyBuf = new LinkedList();
_cumulativePaddingStripped = 0; _cumulativePaddingStripped = 0;
_eofFound = false; _eofFound = false;
} }
public int read() throws IOException { public int read() throws IOException {
while ( (!_eofFound) && (_readyBuf.size() <= 0) ) { while ((!_eofFound) && (_readyBuf.size() <= 0)) {
refill(READ_SIZE); refill(READ_SIZE);
} }
Integer nval = getNext(); Integer nval = getNext();
if (nval != null) { if (nval != null) {
return nval.intValue(); return nval.intValue();
} else { } else {
//_log.debug("No byte available. eof? " + _eofFound); //_log.debug("No byte available. eof? " + _eofFound);
if (_eofFound) if (_eofFound)
return -1; return -1;
else { else {
throw new IOException("Not EOF, but none available? " + _readyBuf.size() + "/" + _encryptedBuf.size() + "/" + _cumulativeRead + "... impossible"); throw new IOException("Not EOF, but none available? " + _readyBuf.size() + "/" + _encryptedBuf.size()
} + "/" + _cumulativeRead + "... impossible");
} }
} }
}
public int read(byte dest[]) throws IOException {
for (int i = 0; i < dest.length; i++) { public int read(byte dest[]) throws IOException {
int val = read(); for (int i = 0; i < dest.length; i++) {
if (val == -1) { int val = read();
// no more to read... can they expect more? if (val == -1) {
if (_eofFound && (i == 0)) // no more to read... can they expect more?
return -1; if (_eofFound && (i == 0))
else return -1;
return i; else
} else { return i;
dest[i] = (byte)val; } else {
} dest[i] = (byte) val;
} }
_log.debug("Read the full buffer of size " + dest.length); }
return dest.length; _log.debug("Read the full buffer of size " + dest.length);
} return dest.length;
}
public int read(byte dest[], int off, int len) throws IOException {
byte buf[] = new byte[len]; public int read(byte dest[], int off, int len) throws IOException {
int read = read(buf); byte buf[] = new byte[len];
if (read == -1) int read = read(buf);
return -1; if (read == -1) return -1;
System.arraycopy(buf, 0, dest, off, read); System.arraycopy(buf, 0, dest, off, read);
return read; return read;
} }
public long skip(long numBytes) throws IOException {
for (long l = 0; l < numBytes; l++) { public long skip(long numBytes) throws IOException {
int val = read(); for (long l = 0; l < numBytes; l++) {
if (val == -1) int val = read();
return l; if (val == -1) return l;
} }
return numBytes; return numBytes;
} }
public int available() throws IOException { return _readyBuf.size(); } public int available() throws IOException {
public void close() throws IOException { return _readyBuf.size();
//_log.debug("We have " + _encryptedBuf.size() + " available to decrypt... doing so"); }
//decrypt();
//byte buf[] = new byte[_readyBuf.size()]; public void close() throws IOException {
//for (int i = 0; i < buf.length; i++) //_log.debug("We have " + _encryptedBuf.size() + " available to decrypt... doing so");
// buf[i] = ((Integer)_readyBuf.get(i)).byteValue(); //decrypt();
//_log.debug("After decrypt: readyBuf.size: " + _readyBuf.size() + "\n val:\t" + Base64.encode(buf)); //byte buf[] = new byte[_readyBuf.size()];
int ready = _readyBuf.size(); //for (int i = 0; i < buf.length; i++)
int encrypted = _readyBuf.size(); // buf[i] = ((Integer)_readyBuf.get(i)).byteValue();
_readyBuf.clear(); //_log.debug("After decrypt: readyBuf.size: " + _readyBuf.size() + "\n val:\t" + Base64.encode(buf));
_encryptedBuf.reset(); int ready = _readyBuf.size();
in.close(); int encrypted = _readyBuf.size();
_log.debug("Cumulative bytes read from source/decrypted/stripped: " + _cumulativeRead + "/"+_cumulativePrepared +"/" + _cumulativePaddingStripped + "] remaining [" + ready + " ready, " + encrypted + " still encrypted]"); _readyBuf.clear();
_encryptedBuf.reset();
in.close();
_log.debug("Cumulative bytes read from source/decrypted/stripped: " + _cumulativeRead + "/"
+ _cumulativePrepared + "/" + _cumulativePaddingStripped + "] remaining [" + ready + " ready, "
+ encrypted + " still encrypted]");
}
public void mark(int readLimit) {
}
public void reset() throws IOException {
throw new IOException("Reset not supported");
}
public boolean markSupported() {
return false;
} }
public void mark(int readLimit) {}
public void reset() throws IOException { throw new IOException("Reset not supported"); }
public boolean markSupported() { return false; }
/** /**
* Retrieve the next ready byte, or null if no bytes are ready. this does not refill or block * Retrieve the next ready byte, or null if no bytes are ready. this does not refill or block
* *
*/ */
private Integer getNext() { private Integer getNext() {
if (_readyBuf.size() > 0) { if (_readyBuf.size() > 0) {
return (Integer)_readyBuf.remove(0); return (Integer) _readyBuf.remove(0);
} else { } else {
return null; return null;
} }
} }
/** /**
* Read at least one new byte from the underlying stream, and up to max new bytes, * Read at least one new byte from the underlying stream, and up to max new bytes,
* but not necessarily enough for a new decrypted block. This blocks until at least * but not necessarily enough for a new decrypted block. This blocks until at least
@@ -152,32 +165,32 @@ public class AESInputStream extends FilterInputStream {
* *
*/ */
private void refill(int max) throws IOException { private void refill(int max) throws IOException {
if (!_eofFound) { if (!_eofFound) {
byte buf[] = new byte[max]; byte buf[] = new byte[max];
int read = in.read(buf); int read = in.read(buf);
if (read == -1) { if (read == -1) {
_eofFound = true; _eofFound = true;
} else if (read > 0) { } else if (read > 0) {
//_log.debug("Read from the source stream " + read + " bytes"); //_log.debug("Read from the source stream " + read + " bytes");
_cumulativeRead += read; _cumulativeRead += read;
_encryptedBuf.write(buf, 0, read); _encryptedBuf.write(buf, 0, read);
} }
} }
if (false) return; // true to keep the data for decrypt/display on close if (false) return; // true to keep the data for decrypt/display on close
if (_encryptedBuf.size() > 0) { if (_encryptedBuf.size() > 0) {
if (_encryptedBuf.size() >= DECRYPT_SIZE) { if (_encryptedBuf.size() >= DECRYPT_SIZE) {
//_log.debug("We have " + _encryptedBuf.size() + " available to decrypt... doing so"); //_log.debug("We have " + _encryptedBuf.size() + " available to decrypt... doing so");
decrypt(); decrypt();
//if (_encryptedBuf.size() > 0) //if (_encryptedBuf.size() > 0)
// _log.debug("Bytes left in the encrypted buffer after decrypt: " + _encryptedBuf.size()); // _log.debug("Bytes left in the encrypted buffer after decrypt: " + _encryptedBuf.size());
} else { } else {
if (_eofFound) { if (_eofFound) {
//_log.debug("EOF and not enough bytes to decrypt [size = " + _encryptedBuf.size() + " totalCumulative: " + _cumulativeRead + "/"+_cumulativePrepared +"]!"); //_log.debug("EOF and not enough bytes to decrypt [size = " + _encryptedBuf.size() + " totalCumulative: " + _cumulativeRead + "/"+_cumulativePrepared +"]!");
} else { } else {
//_log.debug("Not enough bytes to decrypt [size = " + _encryptedBuf.size() + " totalCumulative: " + _cumulativeRead + "/"+_cumulativePrepared +"]"); //_log.debug("Not enough bytes to decrypt [size = " + _encryptedBuf.size() + " totalCumulative: " + _cumulativeRead + "/"+_cumulativePrepared +"]");
} }
} }
} }
} }
/** /**
@@ -186,52 +199,59 @@ public class AESInputStream extends FilterInputStream {
* *
*/ */
private void decrypt() throws IOException { private void decrypt() throws IOException {
byte encrypted[] = _encryptedBuf.toByteArray(); byte encrypted[] = _encryptedBuf.toByteArray();
_encryptedBuf.reset(); _encryptedBuf.reset();
if ( (encrypted == null) || (encrypted.length <= 0) ) if ((encrypted == null) || (encrypted.length <= 0))
throw new IOException("Error decrypting - no data to decrypt"); throw new IOException("Error decrypting - no data to decrypt");
int numBlocks = encrypted.length / BLOCK_SIZE; int numBlocks = encrypted.length / BLOCK_SIZE;
if ( (encrypted.length % BLOCK_SIZE) != 0) { if ((encrypted.length % BLOCK_SIZE) != 0) {
// it was flushed / handled off the BLOCK_SIZE segments, so put the excess // it was flushed / handled off the BLOCK_SIZE segments, so put the excess
// back into the _encryptedBuf for later handling // back into the _encryptedBuf for later handling
int trailing = encrypted.length % BLOCK_SIZE; int trailing = encrypted.length % BLOCK_SIZE;
_encryptedBuf.write(encrypted, encrypted.length - trailing, trailing); _encryptedBuf.write(encrypted, encrypted.length - trailing, trailing);
byte nencrypted[] = new byte[encrypted.length - trailing]; byte nencrypted[] = new byte[encrypted.length - trailing];
System.arraycopy(encrypted, 0, nencrypted, 0, nencrypted.length); System.arraycopy(encrypted, 0, nencrypted, 0, nencrypted.length);
encrypted = nencrypted; encrypted = nencrypted;
_log.warn("Decrypt got odd segment - " + trailing + " bytes pushed back for later decryption - corrupted or slow data stream perhaps?"); _log.warn("Decrypt got odd segment - " + trailing
} else { + " bytes pushed back for later decryption - corrupted or slow data stream perhaps?");
//_log.info(encrypted.length + " bytes makes up " + numBlocks + " blocks to decrypt normally"); } else {
} //_log.info(encrypted.length + " bytes makes up " + numBlocks + " blocks to decrypt normally");
}
byte block[] = new byte[BLOCK_SIZE];
for (int i = 0; i < numBlocks; i++) { byte block[] = new byte[BLOCK_SIZE];
System.arraycopy(encrypted, i*BLOCK_SIZE, block, 0, BLOCK_SIZE); for (int i = 0; i < numBlocks; i++) {
byte decrypted[] = _engine.decrypt(block, _key, _lastBlock); System.arraycopy(encrypted, i * BLOCK_SIZE, block, 0, BLOCK_SIZE);
byte data[] = CryptixAESEngine.xor(decrypted, _lastBlock); byte decrypted[] = _engine.decrypt(block, _key, _lastBlock);
int cleaned[] = stripPadding(data); byte data[] = CryptixAESEngine.xor(decrypted, _lastBlock);
for (int j = 0; j < cleaned.length; j++) { int cleaned[] = stripPadding(data);
if ( ((int)cleaned[j]) <= 0) { for (int j = 0; j < cleaned.length; j++) {
cleaned[j] += 256; if (((int) cleaned[j]) <= 0) {
//_log.error("(modified: " + cleaned[j] + ")"); cleaned[j] += 256;
} //_log.error("(modified: " + cleaned[j] + ")");
_readyBuf.add(new Integer(cleaned[j])); }
} _readyBuf.add(new Integer(cleaned[j]));
_cumulativePrepared += cleaned.length; }
//_log.debug("Updating last block for inputStream"); _cumulativePrepared += cleaned.length;
System.arraycopy(decrypted, 0, _lastBlock, 0, BLOCK_SIZE); //_log.debug("Updating last block for inputStream");
} System.arraycopy(decrypted, 0, _lastBlock, 0, BLOCK_SIZE);
}
int remaining = encrypted.length % BLOCK_SIZE;
if (remaining != 0) { int remaining = encrypted.length % BLOCK_SIZE;
_encryptedBuf.write(encrypted, encrypted.length-remaining, remaining); if (remaining != 0) {
_log.debug("After pushing " + remaining + " bytes back onto the buffer, lets delay 1s our action so we don't fast busy until the net transfers data"); _encryptedBuf.write(encrypted, encrypted.length - remaining, remaining);
try { Thread.sleep(1000); } catch (InterruptedException ie) {} _log
} else { .debug("After pushing "
//_log.debug("No remaining encrypted bytes beyond the block size"); + remaining
} + " bytes back onto the buffer, lets delay 1s our action so we don't fast busy until the net transfers data");
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
}
} else {
//_log.debug("No remaining encrypted bytes beyond the block size");
}
} }
/** /**
@@ -249,163 +269,167 @@ public class AESInputStream extends FilterInputStream {
* *
*/ */
private int[] stripPadding(byte data[]) throws IOException { private int[] stripPadding(byte data[]) throws IOException {
int numPadBytes = (int)data[data.length-1]; int numPadBytes = (int) data[data.length - 1];
if ( (numPadBytes >= data.length) || (numPadBytes <= 0) ) if ((numPadBytes >= data.length) || (numPadBytes <= 0)) throw new IOException("Invalid number of pad bytes");
throw new IOException("Invalid number of pad bytes"); int rv[] = new int[data.length - numPadBytes];
int rv[] = new int[data.length-numPadBytes]; // optional, but a really good idea: verify the padding
// optional, but a really good idea: verify the padding if (true) {
if (true) { for (int i = data.length - numPadBytes; i < data.length; i++) {
for (int i = data.length - numPadBytes; i < data.length; i++) { if (data[i] != (byte) numPadBytes) { throw new IOException("Incorrect padding on decryption: data[" + i
if (data[i] != (byte)numPadBytes) { + "] = " + data[i] + " not " + numPadBytes); }
throw new IOException("Incorrect padding on decryption: data["+i+"] = " + data[i] + " not " + numPadBytes); }
} }
} for (int i = 0; i < rv.length; i++)
} rv[i] = data[i];
for (int i = 0; i < rv.length; i++) _cumulativePaddingStripped += numPadBytes;
rv[i] = data[i]; return rv;
_cumulativePaddingStripped += numPadBytes;
return rv;
} }
int remainingBytes() { return _encryptedBuf.size(); } int remainingBytes() {
int readyBytes() { return _readyBuf.size(); } return _encryptedBuf.size();
}
int readyBytes() {
return _readyBuf.size();
}
/** /**
* Test AESOutputStream/AESInputStream * Test AESOutputStream/AESInputStream
*/ */
public static void main(String args[]) { public static void main(String args[]) {
byte orig[] = new byte[1024*32]; byte orig[] = new byte[1024 * 32];
RandomSource.getInstance().nextBytes(orig); RandomSource.getInstance().nextBytes(orig);
//byte orig[] = "you are my sunshine, my only sunshine".getBytes(); //byte orig[] = "you are my sunshine, my only sunshine".getBytes();
SessionKey key = KeyGenerator.getInstance().generateSessionKey(); SessionKey key = KeyGenerator.getInstance().generateSessionKey();
byte iv[] = "there once was a".getBytes(); byte iv[] = "there once was a".getBytes();
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
runTest(orig, key, iv); runTest(orig, key, iv);
} }
_log.info("Done testing 32KB data"); _log.info("Done testing 32KB data");
orig = new byte[20]; orig = new byte[20];
RandomSource.getInstance().nextBytes(orig); RandomSource.getInstance().nextBytes(orig);
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
runTest(orig, key, iv); runTest(orig, key, iv);
} }
_log.info("Done testing 20 byte data"); _log.info("Done testing 20 byte data");
orig = new byte[3]; orig = new byte[3];
RandomSource.getInstance().nextBytes(orig); RandomSource.getInstance().nextBytes(orig);
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
runTest(orig, key, iv); runTest(orig, key, iv);
} }
_log.info("Done testing 3 byte data"); _log.info("Done testing 3 byte data");
orig = new byte[0]; orig = new byte[0];
RandomSource.getInstance().nextBytes(orig); RandomSource.getInstance().nextBytes(orig);
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
runTest(orig, key, iv); runTest(orig, key, iv);
} }
_log.info("Done testing 0 byte data"); _log.info("Done testing 0 byte data");
orig = new byte[32]; orig = new byte[32];
RandomSource.getInstance().nextBytes(orig); RandomSource.getInstance().nextBytes(orig);
runOffsetTest(orig, key, iv); runOffsetTest(orig, key, iv);
_log.info("Done testing offset test (it should have come back with a statement NOT EQUAL!)"); _log.info("Done testing offset test (it should have come back with a statement NOT EQUAL!)");
try { Thread.sleep(30*1000); } catch (InterruptedException ie) {} try {
Thread.sleep(30 * 1000);
} catch (InterruptedException ie) {
}
} }
private static void runTest(byte orig[], SessionKey key, byte[] iv) { private static void runTest(byte orig[], SessionKey key, byte[] iv) {
try { try {
long start = Clock.getInstance().now(); long start = Clock.getInstance().now();
ByteArrayOutputStream origStream = new ByteArrayOutputStream(512); ByteArrayOutputStream origStream = new ByteArrayOutputStream(512);
AESOutputStream out = new AESOutputStream(origStream, key, iv); AESOutputStream out = new AESOutputStream(origStream, key, iv);
out.write(orig); out.write(orig);
out.close(); out.close();
byte encrypted[] = origStream.toByteArray(); byte encrypted[] = origStream.toByteArray();
long endE = Clock.getInstance().now(); long endE = Clock.getInstance().now();
ByteArrayInputStream encryptedStream = new ByteArrayInputStream(encrypted); ByteArrayInputStream encryptedStream = new ByteArrayInputStream(encrypted);
AESInputStream in = new AESInputStream(encryptedStream, key, iv); AESInputStream in = new AESInputStream(encryptedStream, key, iv);
ByteArrayOutputStream baos = new ByteArrayOutputStream(512); ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
byte buf[] = new byte[1024*32]; byte buf[] = new byte[1024 * 32];
int read = DataHelper.read(in, buf); int read = DataHelper.read(in, buf);
if (read > 0) if (read > 0) baos.write(buf, 0, read);
baos.write(buf, 0, read); in.close();
in.close(); byte fin[] = baos.toByteArray();
byte fin[] = baos.toByteArray(); long end = Clock.getInstance().now();
long end = Clock.getInstance().now(); Hash origHash = SHA256Generator.getInstance().calculateHash(orig);
Hash origHash = SHA256Generator.getInstance().calculateHash(orig);
Hash newHash = SHA256Generator.getInstance().calculateHash(fin);
Hash newHash = SHA256Generator.getInstance().calculateHash(fin); boolean eq = origHash.equals(newHash);
boolean eq = origHash.equals(newHash); if (eq)
if (eq) _log.info("Equal hashes. hash: " + origHash);
_log.info("Equal hashes. hash: " + origHash); else
else _log.error("NOT EQUAL! \norig: \t" + Base64.encode(orig) + "\nnew : \t" + Base64.encode(fin));
_log.error("NOT EQUAL! \norig: \t" + Base64.encode(orig) + "\nnew : \t" + Base64.encode(fin)); boolean ok = DataHelper.eq(orig, fin);
boolean ok = DataHelper.eq(orig, fin); _log.debug("EQ data? " + ok + " origLen: " + orig.length + " fin.length: " + fin.length);
_log.debug("EQ data? " + ok + " origLen: " + orig.length + " fin.length: " + fin.length); _log.debug("Time to D(E(" + orig.length + ")): " + (end - start) + "ms");
_log.debug("Time to D(E(" + orig.length + ")): " + (end - start) + "ms"); _log.debug("Time to E(" + orig.length + "): " + (endE - start) + "ms");
_log.debug("Time to E(" + orig.length + "): " + (endE - start) + "ms"); _log.debug("Time to D(" + orig.length + "): " + (end - endE) + "ms");
_log.debug("Time to D(" + orig.length + "): " + (end - endE) + "ms");
} catch (Throwable t) {
} catch (Throwable t) { _log.error("ERROR transferring", t);
_log.error("ERROR transferring", t); }
} //try { Thread.sleep(5000); } catch (Throwable t) {}
//try { Thread.sleep(5000); } catch (Throwable t) {}
} }
private static void runOffsetTest(byte orig[], SessionKey key, byte[] iv) {
try {
long start = Clock.getInstance().now();
ByteArrayOutputStream origStream = new ByteArrayOutputStream(512);
AESOutputStream out = new AESOutputStream(origStream, key, iv);
out.write(orig);
out.close();
byte encrypted[] = origStream.toByteArray(); private static void runOffsetTest(byte orig[], SessionKey key, byte[] iv) {
long endE = Clock.getInstance().now(); try {
long start = Clock.getInstance().now();
_log.info("Encrypted segment length: " + encrypted.length); ByteArrayOutputStream origStream = new ByteArrayOutputStream(512);
byte encryptedSegment[] = new byte[40]; AESOutputStream out = new AESOutputStream(origStream, key, iv);
System.arraycopy(encrypted, 0, encryptedSegment, 0, 40); out.write(orig);
out.close();
ByteArrayInputStream encryptedStream = new ByteArrayInputStream(encryptedSegment);
AESInputStream in = new AESInputStream(encryptedStream, key, iv); byte encrypted[] = origStream.toByteArray();
ByteArrayOutputStream baos = new ByteArrayOutputStream(512); long endE = Clock.getInstance().now();
byte buf[] = new byte[1024*32];
int read = DataHelper.read(in, buf); _log.info("Encrypted segment length: " + encrypted.length);
int remaining = in.remainingBytes(); byte encryptedSegment[] = new byte[40];
int readyBytes = in.readyBytes(); System.arraycopy(encrypted, 0, encryptedSegment, 0, 40);
_log.info("Read: " + read);
if (read > 0) ByteArrayInputStream encryptedStream = new ByteArrayInputStream(encryptedSegment);
baos.write(buf, 0, read); AESInputStream in = new AESInputStream(encryptedStream, key, iv);
in.close(); ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
byte fin[] = baos.toByteArray(); byte buf[] = new byte[1024 * 32];
_log.info("fin.length: " + fin.length + " remaining: " + remaining + " ready: " + readyBytes); int read = DataHelper.read(in, buf);
long end = Clock.getInstance().now(); int remaining = in.remainingBytes();
Hash origHash = SHA256Generator.getInstance().calculateHash(orig); int readyBytes = in.readyBytes();
_log.info("Read: " + read);
Hash newHash = SHA256Generator.getInstance().calculateHash(fin); if (read > 0) baos.write(buf, 0, read);
boolean eq = origHash.equals(newHash); in.close();
if (eq) byte fin[] = baos.toByteArray();
_log.info("Equal hashes. hash: " + origHash); _log.info("fin.length: " + fin.length + " remaining: " + remaining + " ready: " + readyBytes);
else long end = Clock.getInstance().now();
_log.error("NOT EQUAL! \norig: \t" + Base64.encode(orig) + "\nnew : \t" + Base64.encode(fin)); Hash origHash = SHA256Generator.getInstance().calculateHash(orig);
boolean ok = DataHelper.eq(orig, fin);
_log.debug("EQ data? " + ok + " origLen: " + orig.length + " fin.length: " + fin.length); Hash newHash = SHA256Generator.getInstance().calculateHash(fin);
_log.debug("Time to D(E(" + orig.length + ")): " + (end - start) + "ms"); boolean eq = origHash.equals(newHash);
_log.debug("Time to E(" + orig.length + "): " + (endE - start) + "ms"); if (eq)
_log.debug("Time to D(" + orig.length + "): " + (end - endE) + "ms"); _log.info("Equal hashes. hash: " + origHash);
else
} catch (Throwable t) { _log.error("NOT EQUAL! \norig: \t" + Base64.encode(orig) + "\nnew : \t" + Base64.encode(fin));
_log.error("ERROR transferring", t); boolean ok = DataHelper.eq(orig, fin);
} _log.debug("EQ data? " + ok + " origLen: " + orig.length + " fin.length: " + fin.length);
//try { Thread.sleep(5000); } catch (Throwable t) {} _log.debug("Time to D(E(" + orig.length + ")): " + (end - start) + "ms");
_log.debug("Time to E(" + orig.length + "): " + (endE - start) + "ms");
_log.debug("Time to D(" + orig.length + "): " + (end - endE) + "ms");
} catch (Throwable t) {
_log.error("ERROR transferring", t);
}
//try { Thread.sleep(5000); } catch (Throwable t) {}
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.crypto; package net.i2p.crypto;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -37,50 +38,54 @@ public class AESOutputStream extends FilterOutputStream {
private long _cumulativePadding; // how many bytes of padding written private long _cumulativePadding; // how many bytes of padding written
public final static float EXPANSION_FACTOR = 1.0625f; // 6% overhead w/ the padding public final static float EXPANSION_FACTOR = 1.0625f; // 6% overhead w/ the padding
private final static int BLOCK_SIZE = CryptixRijndael_Algorithm._BLOCK_SIZE; private final static int BLOCK_SIZE = CryptixRijndael_Algorithm._BLOCK_SIZE;
private final static int MAX_BUF = 256; private final static int MAX_BUF = 256;
public AESOutputStream(OutputStream source, SessionKey key, byte[] iv) { public AESOutputStream(OutputStream source, SessionKey key, byte[] iv) {
super(source); super(source);
_key = key; _key = key;
_lastBlock = new byte[BLOCK_SIZE]; _lastBlock = new byte[BLOCK_SIZE];
System.arraycopy(iv, 0, _lastBlock, 0, BLOCK_SIZE); System.arraycopy(iv, 0, _lastBlock, 0, BLOCK_SIZE);
_inBuf = new ByteArrayOutputStream(MAX_BUF); _inBuf = new ByteArrayOutputStream(MAX_BUF);
} }
public void write(int val) throws IOException { public void write(int val) throws IOException {
_cumulativeProvided++; _cumulativeProvided++;
_inBuf.write(val); _inBuf.write(val);
if (_inBuf.size() > MAX_BUF) if (_inBuf.size() > MAX_BUF) doFlush();
doFlush();
} }
public void write(byte src[]) throws IOException {
_cumulativeProvided += src.length; public void write(byte src[]) throws IOException {
_inBuf.write(src); _cumulativeProvided += src.length;
if (_inBuf.size() > MAX_BUF) _inBuf.write(src);
doFlush(); if (_inBuf.size() > MAX_BUF) doFlush();
} }
public void write(byte src[], int off, int len) throws IOException {
_cumulativeProvided += len; public void write(byte src[], int off, int len) throws IOException {
_inBuf.write(src, off, len); _cumulativeProvided += len;
if (_inBuf.size() > MAX_BUF) _inBuf.write(src, off, len);
doFlush(); if (_inBuf.size() > MAX_BUF) doFlush();
} }
public void close() throws IOException { public void close() throws IOException {
flush(); flush();
out.close(); out.close();
_inBuf.reset(); _inBuf.reset();
_log.debug("Cumulative bytes provided to this stream / written out / padded: " + _cumulativeProvided + "/" + _cumulativeWritten + "/" + _cumulativePadding); _log.debug("Cumulative bytes provided to this stream / written out / padded: " + _cumulativeProvided + "/"
+ _cumulativeWritten + "/" + _cumulativePadding);
} }
public void flush() throws IOException { doFlush(); out.flush(); }
public void flush() throws IOException {
doFlush();
out.flush();
}
private void doFlush() throws IOException { private void doFlush() throws IOException {
writeEncrypted(_inBuf.toByteArray()); writeEncrypted(_inBuf.toByteArray());
_inBuf.reset(); _inBuf.reset();
} }
/** /**
* Encrypt an arbitrary size array with AES using CBC and PKCS#5 padding, * Encrypt an arbitrary size array with AES using CBC and PKCS#5 padding,
* write it to the stream, and set _lastBlock to the last encrypted * write it to the stream, and set _lastBlock to the last encrypted
@@ -92,34 +97,34 @@ public class AESOutputStream extends FilterOutputStream {
* *
*/ */
private void writeEncrypted(byte src[]) throws IOException { private void writeEncrypted(byte src[]) throws IOException {
if ( (src == null) || (src.length == 0) ) return; if ((src == null) || (src.length == 0)) return;
int numBlocks = src.length/(BLOCK_SIZE-1); int numBlocks = src.length / (BLOCK_SIZE - 1);
byte block[] = new byte[BLOCK_SIZE]; byte block[] = new byte[BLOCK_SIZE];
block[BLOCK_SIZE-1] = 0x01; // the padding byte for "full" blocks block[BLOCK_SIZE - 1] = 0x01; // the padding byte for "full" blocks
for (int i = 0; i < numBlocks; i++) { for (int i = 0; i < numBlocks; i++) {
System.arraycopy(src, i*15, block, 0, 15); System.arraycopy(src, i * 15, block, 0, 15);
byte data[] = _engine.xor(block, _lastBlock); byte data[] = _engine.xor(block, _lastBlock);
byte encrypted[] = _engine.encrypt(data, _key, _lastBlock); byte encrypted[] = _engine.encrypt(data, _key, _lastBlock);
_cumulativeWritten += encrypted.length; _cumulativeWritten += encrypted.length;
out.write(encrypted); out.write(encrypted);
System.arraycopy(encrypted, encrypted.length-BLOCK_SIZE, _lastBlock, 0, BLOCK_SIZE); System.arraycopy(encrypted, encrypted.length - BLOCK_SIZE, _lastBlock, 0, BLOCK_SIZE);
_cumulativePadding++; _cumulativePadding++;
} }
if (src.length % 15 != 0) { if (src.length % 15 != 0) {
// we need to do non trivial padding // we need to do non trivial padding
int remainingBytes = src.length-numBlocks*15; int remainingBytes = src.length - numBlocks * 15;
int paddingBytes = BLOCK_SIZE - remainingBytes; int paddingBytes = BLOCK_SIZE - remainingBytes;
System.arraycopy(src, numBlocks*15, block, 0, remainingBytes); System.arraycopy(src, numBlocks * 15, block, 0, remainingBytes);
Arrays.fill(block, remainingBytes, BLOCK_SIZE, (byte)paddingBytes); Arrays.fill(block, remainingBytes, BLOCK_SIZE, (byte) paddingBytes);
byte data[] = _engine.xor(block, _lastBlock); byte data[] = _engine.xor(block, _lastBlock);
byte encrypted[] = _engine.encrypt(data, _key, _lastBlock); byte encrypted[] = _engine.encrypt(data, _key, _lastBlock);
out.write(encrypted); out.write(encrypted);
System.arraycopy(encrypted, encrypted.length-BLOCK_SIZE, _lastBlock, 0, BLOCK_SIZE); System.arraycopy(encrypted, encrypted.length - BLOCK_SIZE, _lastBlock, 0, BLOCK_SIZE);
_cumulativePadding += paddingBytes; _cumulativePadding += paddingBytes;
_cumulativeWritten += encrypted.length; _cumulativeWritten += encrypted.length;
} }
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.crypto; package net.i2p.crypto;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -20,7 +21,7 @@ import net.i2p.util.Log;
* Only supports data of size mod 16 bytes - no inherent padding. * Only supports data of size mod 16 bytes - no inherent padding.
* *
* @author jrandom, thecrypto * @author jrandom, thecrypto
*/ */
public class CryptixAESEngine extends AESEngine { public class CryptixAESEngine extends AESEngine {
private final static Log _log = new Log(CryptixAESEngine.class); private final static Log _log = new Log(CryptixAESEngine.class);
private final static CryptixRijndael_Algorithm _algo = new CryptixRijndael_Algorithm(); private final static CryptixRijndael_Algorithm _algo = new CryptixRijndael_Algorithm();
@@ -28,122 +29,119 @@ public class CryptixAESEngine extends AESEngine {
private final static byte FAKE_KEY = 0x2A; private final static byte FAKE_KEY = 0x2A;
public byte[] encrypt(byte payload[], SessionKey sessionKey, byte initializationVector[]) { public byte[] encrypt(byte payload[], SessionKey sessionKey, byte initializationVector[]) {
if ( (initializationVector == null) || (payload == null) || (payload.length <= 0) || (sessionKey == null) || (initializationVector.length != 16) ) if ((initializationVector == null) || (payload == null) || (payload.length <= 0) || (sessionKey == null)
return null; || (initializationVector.length != 16)) return null;
if (USE_FAKE_CRYPTO) { if (USE_FAKE_CRYPTO) {
_log.warn("AES Crypto disabled! Using trivial XOR"); _log.warn("AES Crypto disabled! Using trivial XOR");
byte rv[] = new byte[payload.length]; byte rv[] = new byte[payload.length];
for (int i = 0; i < rv.length; i++) for (int i = 0; i < rv.length; i++)
rv[i] = (byte)(payload[i] ^ FAKE_KEY); rv[i] = (byte) (payload[i] ^ FAKE_KEY);
return rv; return rv;
} }
int numblock = payload.length/16; int numblock = payload.length / 16;
if (payload.length % 16 != 0) if (payload.length % 16 != 0) numblock++;
numblock++; byte[][] plain = new byte[numblock][16];
byte[][] plain = new byte[numblock][16]; for (int x = 0; x < numblock; x++) {
for (int x = 0; x < numblock; x++) { for (int y = 0; y < 16; y++) {
for (int y = 0; y < 16; y++) { plain[x][y] = payload[x * 16 + y];
plain[x][y] = payload[x*16+y]; }
} }
}
byte[][] cipher = new byte[numblock][16];
byte[][] cipher = new byte[numblock][16]; cipher[0] = encrypt(xor(initializationVector, plain[0]), sessionKey);
cipher[0] = encrypt(xor(initializationVector, plain[0]), sessionKey); for (int x = 1; x < numblock; x++) {
for (int x = 1; x < numblock; x++) { cipher[x] = encrypt(xor(cipher[x - 1], plain[x]), sessionKey);
cipher[x] = encrypt(xor(cipher[x-1], plain[x]), sessionKey); }
}
byte[] ret = new byte[numblock * 16];
byte[] ret = new byte[numblock * 16]; for (int x = 0; x < numblock; x++) {
for (int x = 0; x < numblock; x++) { for (int y = 0; y < 16; y++) {
for (int y = 0; y < 16; y++) { ret[x * 16 + y] = cipher[x][y];
ret[x*16+y] = cipher[x][y]; }
} }
}
return ret;
return ret;
} }
public byte[] decrypt(byte payload[], SessionKey sessionKey, byte initializationVector[]) { public byte[] decrypt(byte payload[], SessionKey sessionKey, byte initializationVector[]) {
if ( (initializationVector == null) || (payload == null) || (payload.length <= 0) || (sessionKey == null) || (initializationVector.length != 16) ) if ((initializationVector == null) || (payload == null) || (payload.length <= 0) || (sessionKey == null)
return null; || (initializationVector.length != 16)) return null;
if (USE_FAKE_CRYPTO) { if (USE_FAKE_CRYPTO) {
_log.warn("AES Crypto disabled! Using trivial XOR"); _log.warn("AES Crypto disabled! Using trivial XOR");
byte rv[] = new byte[payload.length]; byte rv[] = new byte[payload.length];
for (int i = 0; i < rv.length; i++) for (int i = 0; i < rv.length; i++)
rv[i] = (byte)(payload[i] ^ FAKE_KEY); rv[i] = (byte) (payload[i] ^ FAKE_KEY);
return rv; return rv;
} }
int numblock = payload.length/16; int numblock = payload.length / 16;
if (payload.length % 16 != 0) if (payload.length % 16 != 0) numblock++;
numblock++; byte[][] cipher = new byte[numblock][16];
byte[][] cipher = new byte[numblock][16]; for (int x = 0; x < numblock; x++) {
for (int x = 0; x < numblock; x++) { for (int y = 0; y < 16; y++) {
for (int y = 0; y < 16; y++) { cipher[x][y] = payload[x * 16 + y];
cipher[x][y] = payload[x*16+y]; }
} }
}
byte[][] plain = new byte[numblock][16];
byte[][] plain = new byte[numblock][16]; plain[0] = xor(decrypt(cipher[0], sessionKey), initializationVector);
plain[0] = xor(decrypt(cipher[0], sessionKey), initializationVector); for (int x = 1; x < numblock; x++) {
for (int x = 1; x < numblock; x++) { plain[x] = xor(decrypt(cipher[x], sessionKey), cipher[x - 1]);
plain[x] = xor(decrypt(cipher[x], sessionKey), cipher[x-1]); }
}
byte[] ret = new byte[numblock * 16];
byte[] ret = new byte[numblock * 16]; for (int x = 0; x < numblock; x++) {
for (int x = 0; x < numblock; x++) { for (int y = 0; y < 16; y++) {
for (int y = 0; y < 16; y++) { ret[x * 16 + y] = plain[x][y];
ret[x*16+y] = plain[x][y]; }
} }
}
return ret;
return ret;
} }
final static byte[] xor(byte[] a, byte[] b) {
final static byte[] xor (byte[] a, byte[] b) { if ((a == null) || (b == null) || (a.length != b.length)) return null;
if ( (a == null) || (b == null) || (a.length != b.length) ) byte[] ret = new byte[a.length];
return null; for (int x = 0; x < a.length; x++) {
byte[] ret = new byte[a.length]; ret[x] = (byte) (a[x] ^ b[x]);
for (int x = 0; x < a.length; x++) { }
ret[x] = (byte)(a[x] ^ b[x]); return ret;
}
return ret;
} }
/** Encrypt the payload with the session key /** Encrypt the payload with the session key
* @param payload data to be encrypted * @param payload data to be encrypted
* @param sessionKey private esession key to encrypt to * @param sessionKey private esession key to encrypt to
* @return encrypted data * @return encrypted data
*/ */
final static byte[] encrypt(byte payload[], SessionKey sessionKey) { final static byte[] encrypt(byte payload[], SessionKey sessionKey) {
try { try {
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16); Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16);
byte rv[] = new byte[payload.length]; byte rv[] = new byte[payload.length];
CryptixRijndael_Algorithm.blockEncrypt(payload, rv, 0, key, 16); CryptixRijndael_Algorithm.blockEncrypt(payload, rv, 0, key, 16);
return rv; return rv;
} catch (InvalidKeyException ike) { } catch (InvalidKeyException ike) {
_log.error("Invalid key", ike); _log.error("Invalid key", ike);
return null; return null;
} }
} }
/** decrypt the data with the session key provided /** decrypt the data with the session key provided
* @param payload encrypted data * @param payload encrypted data
* @param sessionKey private session key * @param sessionKey private session key
* @return unencrypted data * @return unencrypted data
*/ */
final static byte[] decrypt(byte payload[], SessionKey sessionKey) { final static byte[] decrypt(byte payload[], SessionKey sessionKey) {
try { try {
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16); Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16);
byte rv[] = new byte[payload.length]; byte rv[] = new byte[payload.length];
CryptixRijndael_Algorithm.blockDecrypt(payload, rv, 0, key, 16); CryptixRijndael_Algorithm.blockDecrypt(payload, rv, 0, key, 16);
return rv; return rv;
} catch (InvalidKeyException ike) { } catch (InvalidKeyException ike) {
_log.error("Invalid key", ike); _log.error("Invalid key", ike);
return null; return null;
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
package net.i2p.crypto; package net.i2p.crypto;
/* /*
* Copyright (c) 2003, TheCrypto * Copyright (c) 2003, TheCrypto
* All rights reserved. * All rights reserved.
@@ -29,6 +30,7 @@ package net.i2p.crypto;
*/ */
import java.math.BigInteger; import java.math.BigInteger;
import net.i2p.util.NativeBigInteger; import net.i2p.util.NativeBigInteger;
/** /**
@@ -36,29 +38,29 @@ import net.i2p.util.NativeBigInteger;
* http://www.ietf.org/proceedings/03mar/I-D/draft-ietf-ipsec-ike-modp-groups-05.txt * http://www.ietf.org/proceedings/03mar/I-D/draft-ietf-ipsec-ike-modp-groups-05.txt
*/ */
public class CryptoConstants { public class CryptoConstants {
public static final BigInteger dsap = new NativeBigInteger( public static final BigInteger dsap = new NativeBigInteger(
"9c05b2aa960d9b97b8931963c9cc9e8c3026e9b8ed92fad0a69cc886d5bf8015fcadae31"+ "9c05b2aa960d9b97b8931963c9cc9e8c3026e9b8ed92fad0a69cc886d5bf8015fcadae31"
"a0ad18fab3f01b00a358de237655c4964afaa2b337e96ad316b9fb1cc564b5aec5b69a9f"+ + "a0ad18fab3f01b00a358de237655c4964afaa2b337e96ad316b9fb1cc564b5aec5b69a9f"
"f6c3e4548707fef8503d91dd8602e867e6d35d2235c1869ce2479c3b9d5401de04e0727f"+ + "f6c3e4548707fef8503d91dd8602e867e6d35d2235c1869ce2479c3b9d5401de04e0727f"
"b33d6511285d4cf29538d9e3b6051f5b22cc1c93", 16); + "b33d6511285d4cf29538d9e3b6051f5b22cc1c93",
public static final BigInteger dsaq = new NativeBigInteger( 16);
"a5dfc28fef4ca1e286744cd8eed9d29d684046b7", 16); public static final BigInteger dsaq = new NativeBigInteger("a5dfc28fef4ca1e286744cd8eed9d29d684046b7", 16);
public static final BigInteger dsag = new NativeBigInteger( public static final BigInteger dsag = new NativeBigInteger(
"c1f4d27d40093b429e962d7223824e0bbc47e7c832a39236fc683af84889581075ff9082"+ "c1f4d27d40093b429e962d7223824e0bbc47e7c832a39236fc683af84889581075ff9082"
"ed32353d4374d7301cda1d23c431f4698599dda02451824ff369752593647cc3ddc197de"+ + "ed32353d4374d7301cda1d23c431f4698599dda02451824ff369752593647cc3ddc197de"
"985e43d136cdcfc6bd5409cd2f450821142a5e6f8eb1c3ab5d0484b8129fcf17bce4f7f3"+ + "985e43d136cdcfc6bd5409cd2f450821142a5e6f8eb1c3ab5d0484b8129fcf17bce4f7f3"
"3321c3cb3dbb14a905e7b2b3e93be4708cbcc82", 16); + "3321c3cb3dbb14a905e7b2b3e93be4708cbcc82",
public static final BigInteger elgp = new NativeBigInteger( 16);
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"+ public static final BigInteger elgp = new NativeBigInteger("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD"+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"+ + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"+ + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
"83655D23DCA3AD961C62F356208552BB9ED529077096966D"+ + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"+ + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510"+ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
"15728E5A8AACAA68FFFFFFFFFFFFFFFF", 16); + "15728E5A8AACAA68FFFFFFFFFFFFFFFF", 16);
public static final BigInteger elgg = new NativeBigInteger("2"); public static final BigInteger elgg = new NativeBigInteger("2");
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.crypto; package net.i2p.crypto;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,18 +9,18 @@ package net.i2p.crypto;
* *
*/ */
import net.i2p.data.SessionKey;
import net.i2p.data.ByteArray;
import net.i2p.util.RandomSource;
import net.i2p.util.Log;
import net.i2p.util.NativeBigInteger;
import net.i2p.util.Clock;
import net.i2p.util.I2PThread;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import net.i2p.data.ByteArray;
import net.i2p.data.SessionKey;
import net.i2p.util.Clock;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
import net.i2p.util.NativeBigInteger;
import net.i2p.util.RandomSource;
/** /**
* Generate a new session key through a diffie hellman exchange. This uses the * Generate a new session key through a diffie hellman exchange. This uses the
* constants defined in CryptoConstants, which causes the exchange to create a * constants defined in CryptoConstants, which causes the exchange to create a
@@ -52,262 +53,303 @@ public class DHSessionKeyBuilder {
private BigInteger _peerValue; private BigInteger _peerValue;
private SessionKey _sessionKey; private SessionKey _sessionKey;
private ByteArray _extraExchangedBytes; // bytes after the session key from the DH exchange private ByteArray _extraExchangedBytes; // bytes after the session key from the DH exchange
public final static String PROP_DH_PRECALC_MIN = "crypto.dh.precalc.min"; public final static String PROP_DH_PRECALC_MIN = "crypto.dh.precalc.min";
public final static String PROP_DH_PRECALC_MAX = "crypto.dh.precalc.max"; public final static String PROP_DH_PRECALC_MAX = "crypto.dh.precalc.max";
public final static String PROP_DH_PRECALC_DELAY = "crypto.dh.precalc.delay"; public final static String PROP_DH_PRECALC_DELAY = "crypto.dh.precalc.delay";
public final static String DEFAULT_DH_PRECALC_MIN = "5"; public final static String DEFAULT_DH_PRECALC_MIN = "5";
public final static String DEFAULT_DH_PRECALC_MAX = "10"; public final static String DEFAULT_DH_PRECALC_MAX = "10";
public final static String DEFAULT_DH_PRECALC_DELAY = "1000"; public final static String DEFAULT_DH_PRECALC_DELAY = "1000";
static { static {
try { try {
int val = Integer.parseInt(System.getProperty(PROP_DH_PRECALC_MIN, DEFAULT_DH_PRECALC_MIN)); int val = Integer.parseInt(System.getProperty(PROP_DH_PRECALC_MIN, DEFAULT_DH_PRECALC_MIN));
MIN_NUM_BUILDERS = val; MIN_NUM_BUILDERS = val;
} catch (Throwable t) { } catch (Throwable t) {
int val = Integer.parseInt(DEFAULT_DH_PRECALC_MIN); int val = Integer.parseInt(DEFAULT_DH_PRECALC_MIN);
MIN_NUM_BUILDERS = val; MIN_NUM_BUILDERS = val;
} }
try { try {
int val = Integer.parseInt(System.getProperty(PROP_DH_PRECALC_MAX, DEFAULT_DH_PRECALC_MAX)); int val = Integer.parseInt(System.getProperty(PROP_DH_PRECALC_MAX, DEFAULT_DH_PRECALC_MAX));
MAX_NUM_BUILDERS = val; MAX_NUM_BUILDERS = val;
} catch (Throwable t) { } catch (Throwable t) {
int val = Integer.parseInt(DEFAULT_DH_PRECALC_MAX); int val = Integer.parseInt(DEFAULT_DH_PRECALC_MAX);
MAX_NUM_BUILDERS = val; MAX_NUM_BUILDERS = val;
} }
try { try {
int val = Integer.parseInt(System.getProperty(PROP_DH_PRECALC_DELAY, DEFAULT_DH_PRECALC_DELAY)); int val = Integer.parseInt(System.getProperty(PROP_DH_PRECALC_DELAY, DEFAULT_DH_PRECALC_DELAY));
CALC_DELAY = val; CALC_DELAY = val;
} catch (Throwable t) { } catch (Throwable t) {
int val = Integer.parseInt(DEFAULT_DH_PRECALC_DELAY); int val = Integer.parseInt(DEFAULT_DH_PRECALC_DELAY);
CALC_DELAY = val; CALC_DELAY = val;
} }
if (_log.shouldLog(Log.DEBUG)) _log.debug("DH Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: " + CALC_DELAY + ")"); if (_log.shouldLog(Log.DEBUG))
_log.debug("DH Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: "
_precalcThread = new I2PThread(new DHSessionKeyBuilderPrecalcRunner(MIN_NUM_BUILDERS, MAX_NUM_BUILDERS)); + CALC_DELAY + ")");
_precalcThread.setName("DH Precalc");
_precalcThread.setDaemon(true); _precalcThread = new I2PThread(new DHSessionKeyBuilderPrecalcRunner(MIN_NUM_BUILDERS, MAX_NUM_BUILDERS));
_precalcThread.setPriority(Thread.MIN_PRIORITY); _precalcThread.setName("DH Precalc");
_precalcThread.start(); _precalcThread.setDaemon(true);
_precalcThread.setPriority(Thread.MIN_PRIORITY);
_precalcThread.start();
} }
/** /**
* Construct a new DH key builder * Construct a new DH key builder
* *
*/ */
public DHSessionKeyBuilder() { public DHSessionKeyBuilder() {
this(false); this(false);
DHSessionKeyBuilder builder = null; DHSessionKeyBuilder builder = null;
synchronized (_builders) { synchronized (_builders) {
if (_builders.size() > 0) { if (_builders.size() > 0) {
builder = (DHSessionKeyBuilder)_builders.remove(0); builder = (DHSessionKeyBuilder) _builders.remove(0);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Removing a builder. # left = " + _builders.size()); if (_log.shouldLog(Log.DEBUG)) _log.debug("Removing a builder. # left = " + _builders.size());
} else { } else {
if (_log.shouldLog(Log.WARN)) _log.warn("NO MORE BUILDERS! creating one now"); if (_log.shouldLog(Log.WARN)) _log.warn("NO MORE BUILDERS! creating one now");
} }
} }
if (builder != null) { if (builder != null) {
_myPrivateValue = builder._myPrivateValue; _myPrivateValue = builder._myPrivateValue;
_myPublicValue = builder._myPublicValue; _myPublicValue = builder._myPublicValue;
_peerValue = builder._peerValue; _peerValue = builder._peerValue;
_sessionKey = builder._sessionKey; _sessionKey = builder._sessionKey;
_extraExchangedBytes = builder._extraExchangedBytes; _extraExchangedBytes = builder._extraExchangedBytes;
} else { } else {
_myPrivateValue = null; _myPrivateValue = null;
_myPublicValue = null; _myPublicValue = null;
_peerValue = null; _peerValue = null;
_sessionKey = null; _sessionKey = null;
_myPublicValue = generateMyValue(); _myPublicValue = generateMyValue();
_extraExchangedBytes = new ByteArray(); _extraExchangedBytes = new ByteArray();
} }
} }
public DHSessionKeyBuilder(boolean usePool) { public DHSessionKeyBuilder(boolean usePool) {
_myPrivateValue = null; _myPrivateValue = null;
_myPublicValue = null; _myPublicValue = null;
_peerValue = null; _peerValue = null;
_sessionKey = null; _sessionKey = null;
_extraExchangedBytes = new ByteArray(); _extraExchangedBytes = new ByteArray();
} }
private static final int getSize() { synchronized (_builders) { return _builders.size(); } } private static final int getSize() {
synchronized (_builders) {
return _builders.size();
}
}
private static final int addBuilder(DHSessionKeyBuilder builder) { private static final int addBuilder(DHSessionKeyBuilder builder) {
int sz = 0; int sz = 0;
synchronized (_builders) { synchronized (_builders) {
_builders.add(builder); _builders.add(builder);
sz = _builders.size(); sz = _builders.size();
} }
return sz; return sz;
} }
/** /**
* Create a new private value for the DH exchange, and return the number to * Create a new private value for the DH exchange, and return the number to
* be exchanged, leaving the actual private value accessible through getMyPrivateValue() * be exchanged, leaving the actual private value accessible through getMyPrivateValue()
* *
*/ */
public BigInteger generateMyValue() { public BigInteger generateMyValue() {
long start = Clock.getInstance().now(); long start = Clock.getInstance().now();
_myPrivateValue = new NativeBigInteger(2048, RandomSource.getInstance()); _myPrivateValue = new NativeBigInteger(2048, RandomSource.getInstance());
BigInteger myValue = CryptoConstants.elgg.modPow(_myPrivateValue, CryptoConstants.elgp); BigInteger myValue = CryptoConstants.elgg.modPow(_myPrivateValue, CryptoConstants.elgp);
long end = Clock.getInstance().now(); long end = Clock.getInstance().now();
long diff = end - start; long diff = end - start;
if (diff > 1000) { if (diff > 1000) {
if (_log.shouldLog(Log.WARN)) _log.warn("Took more than a second (" + diff + "ms) to generate local DH value"); if (_log.shouldLog(Log.WARN))
} else { _log.warn("Took more than a second (" + diff + "ms) to generate local DH value");
if (_log.shouldLog(Log.DEBUG)) _log.debug("Took " + diff + "ms to generate local DH value"); } else {
} if (_log.shouldLog(Log.DEBUG)) _log.debug("Took " + diff + "ms to generate local DH value");
return myValue; }
return myValue;
} }
/** /**
* Retrieve the private value used by the local participant in the DH exchange * Retrieve the private value used by the local participant in the DH exchange
*/ */
public BigInteger getMyPrivateValue() { return _myPrivateValue; } public BigInteger getMyPrivateValue() {
return _myPrivateValue;
}
/** /**
* Retrieve the public value used by the local participant in the DH exchange, * Retrieve the public value used by the local participant in the DH exchange,
* generating it if necessary * generating it if necessary
*/ */
public BigInteger getMyPublicValue() { public BigInteger getMyPublicValue() {
if (_myPublicValue == null) if (_myPublicValue == null) _myPublicValue = generateMyValue();
_myPublicValue = generateMyValue(); return _myPublicValue;
return _myPublicValue;
} }
/** /**
* Specify the value given by the peer for use in the session key negotiation * Specify the value given by the peer for use in the session key negotiation
* *
*/ */
public void setPeerPublicValue(BigInteger peerVal) { _peerValue = peerVal; } public void setPeerPublicValue(BigInteger peerVal) {
public BigInteger getPeerPublicValue() { return _peerValue; } _peerValue = peerVal;
}
public BigInteger getPeerPublicValue() {
return _peerValue;
}
/** /**
* Retrieve the session key, calculating it if necessary (and if possible). * Retrieve the session key, calculating it if necessary (and if possible).
* *
* @return session key exchanged, or null if the exchange is not complete * @return session key exchanged, or null if the exchange is not complete
*/ */
public SessionKey getSessionKey() { public SessionKey getSessionKey() {
if (_sessionKey != null) return _sessionKey; if (_sessionKey != null) return _sessionKey;
if (_peerValue != null) { if (_peerValue != null) {
if (_myPrivateValue == null) generateMyValue(); if (_myPrivateValue == null) generateMyValue();
_sessionKey = calculateSessionKey(_myPrivateValue, _peerValue); _sessionKey = calculateSessionKey(_myPrivateValue, _peerValue);
} else { } else {
System.err.println("Not ready yet.. privateValue and peerValue must be set (" + (_myPrivateValue != null ? "set":"null") + "," + (_peerValue != null ? "set":"null") + ")"); System.err.println("Not ready yet.. privateValue and peerValue must be set ("
} + (_myPrivateValue != null ? "set" : "null") + ","
return _sessionKey; + (_peerValue != null ? "set" : "null") + ")");
}
return _sessionKey;
} }
/** /**
* Retrieve the extra bytes beyond the session key resulting from the DH exchange. * Retrieve the extra bytes beyond the session key resulting from the DH exchange.
* If there aren't enough bytes (with all of them being consumed by the 32 byte key), * If there aren't enough bytes (with all of them being consumed by the 32 byte key),
* the SHA256 of the key itself is used. * the SHA256 of the key itself is used.
* *
*/ */
public ByteArray getExtraBytes() { return _extraExchangedBytes; } public ByteArray getExtraBytes() {
return _extraExchangedBytes;
}
/** /**
* Calculate a session key based on the private value and the public peer value * Calculate a session key based on the private value and the public peer value
* *
*/ */
private final SessionKey calculateSessionKey(BigInteger myPrivateValue, BigInteger publicPeerValue) { private final SessionKey calculateSessionKey(BigInteger myPrivateValue, BigInteger publicPeerValue) {
long start = Clock.getInstance().now(); long start = Clock.getInstance().now();
SessionKey key = new SessionKey(); SessionKey key = new SessionKey();
BigInteger exchangedKey = publicPeerValue.modPow(myPrivateValue, CryptoConstants.elgp); BigInteger exchangedKey = publicPeerValue.modPow(myPrivateValue, CryptoConstants.elgp);
byte buf[] = exchangedKey.toByteArray(); byte buf[] = exchangedKey.toByteArray();
byte val[] = new byte[32]; byte val[] = new byte[32];
if (buf.length < val.length) { if (buf.length < val.length) {
System.arraycopy(buf, 0, val, 0, buf.length); System.arraycopy(buf, 0, val, 0, buf.length);
byte remaining[] = SHA256Generator.getInstance().calculateHash(val).getData(); byte remaining[] = SHA256Generator.getInstance().calculateHash(val).getData();
_extraExchangedBytes.setData(remaining); _extraExchangedBytes.setData(remaining);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Storing " + remaining.length + " bytes from the DH exchange by SHA256 the session key"); if (_log.shouldLog(Log.DEBUG))
} else { // (buf.length >= val.length) _log.debug("Storing " + remaining.length + " bytes from the DH exchange by SHA256 the session key");
System.arraycopy(buf, 0, val, 0, val.length); } else { // (buf.length >= val.length)
byte remaining[] = new byte[buf.length - val.length]; System.arraycopy(buf, 0, val, 0, val.length);
System.arraycopy(buf, val.length, remaining, 0, remaining.length); byte remaining[] = new byte[buf.length - val.length];
_extraExchangedBytes.setData(remaining); System.arraycopy(buf, val.length, remaining, 0, remaining.length);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Storing " + remaining.length + " bytes from the end of the DH exchange"); _extraExchangedBytes.setData(remaining);
} if (_log.shouldLog(Log.DEBUG))
key.setData(val); _log.debug("Storing " + remaining.length + " bytes from the end of the DH exchange");
long end = Clock.getInstance().now(); }
long diff = end - start; key.setData(val);
if (diff > 1000) { long end = Clock.getInstance().now();
if (_log.shouldLog(Log.WARN)) _log.warn("Generating session key took too long ("+ diff +" ms"); long diff = end - start;
} else { if (diff > 1000) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Generating session key "+ diff +" ms"); if (_log.shouldLog(Log.WARN)) _log.warn("Generating session key took too long (" + diff + " ms");
} } else {
return key; if (_log.shouldLog(Log.DEBUG)) _log.debug("Generating session key " + diff + " ms");
}
return key;
} }
public static void main(String args[]) { public static void main(String args[]) {
RandomSource.getInstance().nextBoolean(); // warm it up RandomSource.getInstance().nextBoolean(); // warm it up
try { Thread.sleep(20*1000); } catch (InterruptedException ie) {} try {
_log.debug("\n\n\n\nBegin test\n"); Thread.sleep(20 * 1000);
long negTime = 0; } catch (InterruptedException ie) {
for (int i = 0; i < 5; i++) { }
long startNeg = Clock.getInstance().now(); _log.debug("\n\n\n\nBegin test\n");
DHSessionKeyBuilder builder1 = new DHSessionKeyBuilder(); long negTime = 0;
DHSessionKeyBuilder builder2 = new DHSessionKeyBuilder(); for (int i = 0; i < 5; i++) {
BigInteger pub1 = builder1.getMyPublicValue(); long startNeg = Clock.getInstance().now();
builder2.setPeerPublicValue(pub1); DHSessionKeyBuilder builder1 = new DHSessionKeyBuilder();
BigInteger pub2 = builder2.getMyPublicValue(); DHSessionKeyBuilder builder2 = new DHSessionKeyBuilder();
builder1.setPeerPublicValue(pub2); BigInteger pub1 = builder1.getMyPublicValue();
SessionKey key1 = builder1.getSessionKey(); builder2.setPeerPublicValue(pub1);
SessionKey key2 = builder2.getSessionKey(); BigInteger pub2 = builder2.getMyPublicValue();
long endNeg = Clock.getInstance().now(); builder1.setPeerPublicValue(pub2);
negTime += endNeg - startNeg; SessionKey key1 = builder1.getSessionKey();
SessionKey key2 = builder2.getSessionKey();
if (!key1.equals(key2)) long endNeg = Clock.getInstance().now();
_log.error("**ERROR: Keys do not match"); negTime += endNeg - startNeg;
else
_log.debug("**Success: Keys match");
byte iv[] = new byte[16]; if (!key1.equals(key2))
RandomSource.getInstance().nextBytes(iv); _log.error("**ERROR: Keys do not match");
String origVal = "1234567890123456"; // 16 bytes max using AESEngine else
byte enc[] = AESEngine.getInstance().encrypt(origVal.getBytes(), key1, iv); _log.debug("**Success: Keys match");
byte dec[] = AESEngine.getInstance().decrypt(enc, key2, iv);
String tranVal = new String(dec); byte iv[] = new byte[16];
if (origVal.equals(tranVal)) RandomSource.getInstance().nextBytes(iv);
_log.debug("**Success: D(E(val)) == val"); String origVal = "1234567890123456"; // 16 bytes max using AESEngine
else byte enc[] = AESEngine.getInstance().encrypt(origVal.getBytes(), key1, iv);
_log.error("**ERROR: D(E(val)) != val [val=(" + tranVal + "), origVal=(" + origVal + ")"); byte dec[] = AESEngine.getInstance().decrypt(enc, key2, iv);
} String tranVal = new String(dec);
_log.debug("Negotiation time for 5 runs: " + negTime + " @ " + negTime/5l + "ms each"); if (origVal.equals(tranVal))
try { Thread.sleep(2000); } catch (InterruptedException ie) {} _log.debug("**Success: D(E(val)) == val");
else
_log.error("**ERROR: D(E(val)) != val [val=(" + tranVal + "), origVal=(" + origVal + ")");
}
_log.debug("Negotiation time for 5 runs: " + negTime + " @ " + negTime / 5l + "ms each");
try {
Thread.sleep(2000);
} catch (InterruptedException ie) {
}
} }
private static class DHSessionKeyBuilderPrecalcRunner implements Runnable {
private int _minSize;
private int _maxSize;
private DHSessionKeyBuilderPrecalcRunner(int minSize, int maxSize) {
_minSize = minSize;
_maxSize = maxSize;
}
public void run() {
while (true) {
int curSize = 0; private static class DHSessionKeyBuilderPrecalcRunner implements Runnable {
long start = Clock.getInstance().now(); private int _minSize;
int startSize = getSize(); private int _maxSize;
curSize = startSize;
while (curSize < _minSize) { private DHSessionKeyBuilderPrecalcRunner(int minSize, int maxSize) {
while (curSize < _maxSize) { _minSize = minSize;
curSize = addBuilder(precalc(curSize)); _maxSize = maxSize;
// for some relief... }
try { Thread.sleep(CALC_DELAY); } catch (InterruptedException ie) {}
} public void run() {
} while (true) {
long end = Clock.getInstance().now();
int numCalc = curSize - startSize; int curSize = 0;
if (numCalc > 0) { long start = Clock.getInstance().now();
if (_log.shouldLog(Log.DEBUG)) _log.debug("Precalced " + numCalc + " to " + curSize + " in " + (end-start-CALC_DELAY*numCalc) + "ms (not counting " + (CALC_DELAY*numCalc) + "ms relief). now sleeping"); int startSize = getSize();
} curSize = startSize;
try { Thread.sleep(30*1000); } catch (InterruptedException ie) {} while (curSize < _minSize) {
} while (curSize < _maxSize) {
} curSize = addBuilder(precalc(curSize));
// for some relief...
private DHSessionKeyBuilder precalc(int i) { try {
DHSessionKeyBuilder builder = new DHSessionKeyBuilder(false); Thread.sleep(CALC_DELAY);
builder.getMyPublicValue(); } catch (InterruptedException ie) {
//_log.debug("Precalc " + i + " complete"); }
return builder; }
} }
long end = Clock.getInstance().now();
int numCalc = curSize - startSize;
if (numCalc > 0) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Precalced " + numCalc + " to " + curSize + " in "
+ (end - start - CALC_DELAY * numCalc) + "ms (not counting "
+ (CALC_DELAY * numCalc) + "ms relief). now sleeping");
}
try {
Thread.sleep(30 * 1000);
} catch (InterruptedException ie) {
}
}
}
private DHSessionKeyBuilder precalc(int i) {
DHSessionKeyBuilder builder = new DHSessionKeyBuilder(false);
builder.getMyPublicValue();
//_log.debug("Precalc " + i + " complete");
return builder;
}
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.crypto; package net.i2p.crypto;
/* /*
* Copyright (c) 2003, TheCrypto * Copyright (c) 2003, TheCrypto
* All rights reserved. * All rights reserved.
@@ -28,254 +29,256 @@ package net.i2p.crypto;
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
import java.math.BigInteger;
import net.i2p.data.Hash;
import net.i2p.data.Signature; import net.i2p.data.Signature;
import net.i2p.data.SigningPrivateKey; import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SigningPublicKey; import net.i2p.data.SigningPublicKey;
import net.i2p.data.Hash; import net.i2p.util.Clock;
import net.i2p.crypto.CryptoConstants; import net.i2p.util.Log;
import net.i2p.util.NativeBigInteger; import net.i2p.util.NativeBigInteger;
import net.i2p.util.RandomSource; import net.i2p.util.RandomSource;
import net.i2p.util.Clock;
import net.i2p.util.Log;
import java.math.BigInteger;
public class DSAEngine { public class DSAEngine {
private final static Log _log = new Log(DSAEngine.class); private final static Log _log = new Log(DSAEngine.class);
private static DSAEngine _instance = new DSAEngine(); private static DSAEngine _instance = new DSAEngine();
public static DSAEngine getInstance() { return _instance; }
public boolean verifySignature(Signature signature, byte signedData[], SigningPublicKey verifyingKey) {
long start = Clock.getInstance().now();
byte[] sigbytes = signature.getData();
byte rbytes[] = new byte[20];
byte sbytes[] = new byte[20];
for (int x = 0; x < 40; x++) {
if (x < 20) {
rbytes[x] = sigbytes[x];
} else {
sbytes[x-20] = sigbytes[x];
}
}
BigInteger s = new NativeBigInteger(1, sbytes);
BigInteger r = new NativeBigInteger(1, rbytes);
BigInteger y = new NativeBigInteger(1, verifyingKey.getData());
BigInteger w = s.modInverse(CryptoConstants.dsaq);
BigInteger u1 = ((new NativeBigInteger(1, calculateHash(signedData).getData())).multiply(w)).mod(CryptoConstants.dsaq);
BigInteger u2 = r.multiply(w).mod(CryptoConstants.dsaq);
BigInteger v = ((CryptoConstants.dsag.modPow(u1, CryptoConstants.dsap)).multiply(y.modPow(u2, CryptoConstants.dsap))).mod(CryptoConstants.dsap).mod(CryptoConstants.dsaq);
boolean ok = v.compareTo(r) == 0; public static DSAEngine getInstance() {
return _instance;
long diff = Clock.getInstance().now() - start;
if (diff > 1000) {
if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to verify the signature ("+ diff + "ms)");
}
return ok;
} }
public boolean verifySignature(Signature signature, byte signedData[], SigningPublicKey verifyingKey) {
long start = Clock.getInstance().now();
byte[] sigbytes = signature.getData();
byte rbytes[] = new byte[20];
byte sbytes[] = new byte[20];
for (int x = 0; x < 40; x++) {
if (x < 20) {
rbytes[x] = sigbytes[x];
} else {
sbytes[x - 20] = sigbytes[x];
}
}
BigInteger s = new NativeBigInteger(1, sbytes);
BigInteger r = new NativeBigInteger(1, rbytes);
BigInteger y = new NativeBigInteger(1, verifyingKey.getData());
BigInteger w = s.modInverse(CryptoConstants.dsaq);
BigInteger u1 = ((new NativeBigInteger(1, calculateHash(signedData).getData())).multiply(w))
.mod(CryptoConstants.dsaq);
BigInteger u2 = r.multiply(w).mod(CryptoConstants.dsaq);
BigInteger v = ((CryptoConstants.dsag.modPow(u1, CryptoConstants.dsap))
.multiply(y.modPow(u2,
CryptoConstants.dsap)))
.mod(
CryptoConstants.dsap)
.mod(
CryptoConstants.dsaq);
boolean ok = v.compareTo(r) == 0;
long diff = Clock.getInstance().now() - start;
if (diff > 1000) {
if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to verify the signature (" + diff + "ms)");
}
return ok;
}
public Signature sign(byte data[], SigningPrivateKey signingKey) { public Signature sign(byte data[], SigningPrivateKey signingKey) {
if ( (signingKey == null) || (data == null) || (data.length <= 0) ) return null; if ((signingKey == null) || (data == null) || (data.length <= 0)) return null;
long start = Clock.getInstance().now(); long start = Clock.getInstance().now();
Signature sig = new Signature(); Signature sig = new Signature();
BigInteger k; BigInteger k;
do { do {
k = new BigInteger(160, RandomSource.getInstance()); k = new BigInteger(160, RandomSource.getInstance());
} while (k.compareTo(CryptoConstants.dsaq) != 1); } while (k.compareTo(CryptoConstants.dsaq) != 1);
BigInteger r = CryptoConstants.dsag.modPow(k, CryptoConstants.dsap).mod(CryptoConstants.dsaq); BigInteger r = CryptoConstants.dsag.modPow(k, CryptoConstants.dsap).mod(CryptoConstants.dsaq);
BigInteger kinv = k.modInverse(CryptoConstants.dsaq); BigInteger kinv = k.modInverse(CryptoConstants.dsaq);
Hash h = calculateHash(data); Hash h = calculateHash(data);
if (h == null) return null; if (h == null) return null;
BigInteger M = new NativeBigInteger(1, h.getData()); BigInteger M = new NativeBigInteger(1, h.getData());
BigInteger x = new NativeBigInteger(1, signingKey.getData()); BigInteger x = new NativeBigInteger(1, signingKey.getData());
BigInteger s = (kinv.multiply(M.add(x.multiply(r)))).mod(CryptoConstants.dsaq); BigInteger s = (kinv.multiply(M.add(x.multiply(r)))).mod(CryptoConstants.dsaq);
byte[] rbytes = r.toByteArray(); byte[] rbytes = r.toByteArray();
byte[] sbytes = s.toByteArray(); byte[] sbytes = s.toByteArray();
byte[] out = new byte[40]; byte[] out = new byte[40];
if (rbytes.length == 20) { if (rbytes.length == 20) {
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
out[i] = rbytes[i]; out[i] = rbytes[i];
} }
} else if (rbytes.length == 21) { } else if (rbytes.length == 21) {
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
out[i] = rbytes[i+1]; out[i] = rbytes[i + 1];
} }
} else { } else {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Using short rbytes.length [" + rbytes.length + "]"); if (_log.shouldLog(Log.DEBUG)) _log.debug("Using short rbytes.length [" + rbytes.length + "]");
for (int i = 0; i < rbytes.length; i++) for (int i = 0; i < rbytes.length; i++)
out[i+20-rbytes.length] = rbytes[i]; out[i + 20 - rbytes.length] = rbytes[i];
} }
if (sbytes.length == 20) { if (sbytes.length == 20) {
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
out[i+20] = sbytes[i]; out[i + 20] = sbytes[i];
} }
} else if (sbytes.length == 21) { } else if (sbytes.length == 21) {
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
out[i+20] = sbytes[i+1]; out[i + 20] = sbytes[i + 1];
} }
} else { } else {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Using short sbytes.length [" + sbytes.length + "]"); if (_log.shouldLog(Log.DEBUG)) _log.debug("Using short sbytes.length [" + sbytes.length + "]");
for (int i = 0; i< sbytes.length; i++) for (int i = 0; i < sbytes.length; i++)
out[i+20+20-sbytes.length] = sbytes[i]; out[i + 20 + 20 - sbytes.length] = sbytes[i];
} }
sig.setData(out); sig.setData(out);
long diff = Clock.getInstance().now() - start; long diff = Clock.getInstance().now() - start;
if (diff > 1000) { if (diff > 1000) {
if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to sign (" + diff + "ms)"); if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to sign (" + diff + "ms)");
} }
return sig; return sig;
} }
private int[] H0 = { private int[] H0 = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0};
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0
};
private Hash calculateHash(byte[]source) { private Hash calculateHash(byte[] source) {
long length = source.length * 8; long length = source.length * 8;
int k = 448 - (int) ((length + 1) % 512); int k = 448 - (int) ((length + 1) % 512);
if (k < 0) { if (k < 0) {
k += 512; k += 512;
} }
int padbytes = k / 8; int padbytes = k / 8;
int wordlength = (int) (source.length / 4 + padbytes / 4 + 3); int wordlength = (int) (source.length / 4 + padbytes / 4 + 3);
int[] M0 = new int[wordlength]; int[] M0 = new int[wordlength];
int wordcount = 0; int wordcount = 0;
int x = 0; int x = 0;
for (x = 0; x < (source.length / 4) * 4; x += 4) { for (x = 0; x < (source.length / 4) * 4; x += 4) {
M0[wordcount] = source[ x ] << 24 >>> 24 << 24; M0[wordcount] = source[x] << 24 >>> 24 << 24;
M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16; M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16;
M0[wordcount] |= source[x + 2] << 24 >>> 24 << 8; M0[wordcount] |= source[x + 2] << 24 >>> 24 << 8;
M0[wordcount] |= source[x + 3] << 24 >>> 24 << 0; M0[wordcount] |= source[x + 3] << 24 >>> 24 << 0;
wordcount++; wordcount++;
} }
switch (source.length - (wordcount + 1) * 4 + 4) { switch (source.length - (wordcount + 1) * 4 + 4) {
case 0: case 0:
M0[wordcount] |= 0x80000000; M0[wordcount] |= 0x80000000;
break; break;
case 1: case 1:
M0[wordcount] = source[x] << 24 >>> 24 << 24; M0[wordcount] = source[x] << 24 >>> 24 << 24;
M0[wordcount] |= 0x00800000; M0[wordcount] |= 0x00800000;
break; break;
case 2: case 2:
M0[wordcount] = source[ x ] << 24 >>> 24 << 24; M0[wordcount] = source[x] << 24 >>> 24 << 24;
M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16; M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16;
M0[wordcount] |= 0x00008000; M0[wordcount] |= 0x00008000;
break; break;
case 3: case 3:
M0[wordcount] = source[ x ] << 24 >>> 24 << 24; M0[wordcount] = source[x] << 24 >>> 24 << 24;
M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16; M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16;
M0[wordcount] |= source[x + 2] << 24 >>> 24 << 8; M0[wordcount] |= source[x + 2] << 24 >>> 24 << 8;
M0[wordcount] |= 0x00000080; M0[wordcount] |= 0x00000080;
break; break;
} }
M0[wordlength - 2] = (int) (length >>> 32); M0[wordlength - 2] = (int) (length >>> 32);
M0[wordlength - 1] = (int) (length); M0[wordlength - 1] = (int) (length);
int[] H = new int[5]; int[] H = new int[5];
for (x = 0; x < 5; x++) { for (x = 0; x < 5; x++) {
H[x] = H0[x]; H[x] = H0[x];
} }
int blocks = M0.length / 16; int blocks = M0.length / 16;
for (int bl = 0; bl < blocks; bl++) { for (int bl = 0; bl < blocks; bl++) {
int a = H[0]; int a = H[0];
int b = H[1]; int b = H[1];
int c = H[2]; int c = H[2];
int d = H[3]; int d = H[3];
int e = H[4]; int e = H[4];
int[] W = new int[80]; int[] W = new int[80];
for (x = 0; x < 80; x++) { for (x = 0; x < 80; x++) {
if (x < 16) { if (x < 16) {
W[x] = M0[bl * 16 + x]; W[x] = M0[bl * 16 + x];
} else { } else {
W[x] = ROTL(1, W[x - 3] ^ W[x - 8] ^ W[x - 14] ^ W[x - 16]); W[x] = ROTL(1, W[x - 3] ^ W[x - 8] ^ W[x - 14] ^ W[x - 16]);
} }
} }
for (x = 0; x < 80; x++) { for (x = 0; x < 80; x++) {
int T = add(ROTL(5, a), add(f(x, b, c, d), add(e, add(k(x), W[x])))); int T = add(ROTL(5, a), add(f(x, b, c, d), add(e, add(k(x), W[x]))));
e = d; e = d;
d = c; d = c;
c = ROTL(30, b); c = ROTL(30, b);
b = a; b = a;
a = T; a = T;
} }
H[0] = add(a, H[0]); H[0] = add(a, H[0]);
H[1] = add(b, H[1]); H[1] = add(b, H[1]);
H[2] = add(c, H[2]); H[2] = add(c, H[2]);
H[3] = add(d, H[3]); H[3] = add(d, H[3]);
H[4] = add(e, H[4]); H[4] = add(e, H[4]);
} }
byte[] hashbytes = new byte[20]; byte[] hashbytes = new byte[20];
for (x = 0; x < 5; x++) { for (x = 0; x < 5; x++) {
hashbytes[ x * 4 ] = (byte) (H[x] << 0 >>> 24); hashbytes[x * 4] = (byte) (H[x] << 0 >>> 24);
hashbytes[x * 4 + 1] = (byte) (H[x] << 8 >>> 24); hashbytes[x * 4 + 1] = (byte) (H[x] << 8 >>> 24);
hashbytes[x * 4 + 2] = (byte) (H[x] << 16 >>> 24); hashbytes[x * 4 + 2] = (byte) (H[x] << 16 >>> 24);
hashbytes[x * 4 + 3] = (byte) (H[x] << 24 >>> 24); hashbytes[x * 4 + 3] = (byte) (H[x] << 24 >>> 24);
} }
Hash hash = new Hash(); Hash hash = new Hash();
hash.setData(hashbytes); hash.setData(hashbytes);
return hash; return hash;
} }
private int k(int t) { private int k(int t) {
if (t > -1 && t < 20) { if (t > -1 && t < 20) {
return 0x5a827999; return 0x5a827999;
} else if (t > 19 && t < 40) { } else if (t > 19 && t < 40) {
return 0x6ed9eba1; return 0x6ed9eba1;
} else if (t > 39 && t < 60) { } else if (t > 39 && t < 60) {
return 0x8f1bbcdc; return 0x8f1bbcdc;
} else if (t > 59 && t < 80) { } else if (t > 59 && t < 80) { return 0xca62c1d6; }
return 0xca62c1d6; return 0x00000000;
}
return 0x00000000;
} }
private int f(int t, int x, int y, int z) { private int f(int t, int x, int y, int z) {
if (t > -1 && t < 20) { if (t > -1 && t < 20) {
return Ch(x, y, z); return Ch(x, y, z);
} else if (t > 19 && t < 40) { } else if (t > 19 && t < 40) {
return Parity(x, y, z); return Parity(x, y, z);
} else if (t > 39 && t < 60) { } else if (t > 39 && t < 60) {
return Maj(x, y, z); return Maj(x, y, z);
} else if (t > 59 && t < 80) { } else if (t > 59 && t < 80) { return Parity(x, y, z); }
return Parity(x, y, z); return 0x00000000;
}
return 0x00000000;
} }
private int Ch(int x, int y, int z) { private int Ch(int x, int y, int z) {
return (x & y) ^ (~x & z); return (x & y) ^ (~x & z);
} }
private int Parity(int x, int y, int z) { private int Parity(int x, int y, int z) {
return x ^ y ^ z; return x ^ y ^ z;
} }
private int Maj(int x, int y, int z) { private int Maj(int x, int y, int z) {
return (x & y) ^ (x & z) ^ (y & z); return (x & y) ^ (x & z) ^ (y & z);
} }
private int ROTL(int n, int x) { private int ROTL(int n, int x) {
return (x << n) | (x >>> 32 - n); return (x << n) | (x >>> 32 - n);
} }
private int add(int x, int y) { private int add(int x, int y) {
return x + y; return x + y;
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.crypto; package net.i2p.crypto;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -23,69 +24,72 @@ import net.i2p.util.Log;
* @author jrandom * @author jrandom
*/ */
public class DummyElGamalEngine extends ElGamalEngine { public class DummyElGamalEngine extends ElGamalEngine {
private final static Log _log = new Log(DummyElGamalEngine.class); private final static Log _log = new Log(DummyElGamalEngine.class);
public DummyElGamalEngine() { public DummyElGamalEngine() {
_log.log(Log.CRIT, "Dummy ElGamal engine in use! NO DATA SECURITY. Danger Will Robinson, Danger!", new Exception("I really hope you know what you're doing")); _log.log(Log.CRIT, "Dummy ElGamal engine in use! NO DATA SECURITY. Danger Will Robinson, Danger!",
new Exception("I really hope you know what you're doing"));
} }
/** encrypt the data to the public key /** encrypt the data to the public key
* @return encrypted data * @return encrypted data
* @param publicKey public key encrypt to * @param publicKey public key encrypt to
* @param data data to encrypt * @param data data to encrypt
*/ */
public byte[] encrypt(byte data[], PublicKey publicKey) { public byte[] encrypt(byte data[], PublicKey publicKey) {
if ( (data == null) || (data.length >= 223) ) throw new IllegalArgumentException("Data to encrypt must be < 223 bytes at the moment"); if ((data == null) || (data.length >= 223))
if (publicKey == null) throw new IllegalArgumentException("Null public key specified"); throw new IllegalArgumentException("Data to encrypt must be < 223 bytes at the moment");
ByteArrayOutputStream baos = new ByteArrayOutputStream(256); if (publicKey == null) throw new IllegalArgumentException("Null public key specified");
try { ByteArrayOutputStream baos = new ByteArrayOutputStream(256);
baos.write(0xFF); try {
Hash hash = SHA256Generator.getInstance().calculateHash(data); baos.write(0xFF);
hash.writeBytes(baos); Hash hash = SHA256Generator.getInstance().calculateHash(data);
baos.write(data); hash.writeBytes(baos);
baos.flush(); baos.write(data);
} catch (Exception e) { baos.flush();
_log.error("Internal error writing to buffer", e); } catch (Exception e) {
return null; _log.error("Internal error writing to buffer", e);
} return null;
byte d2[] = baos.toByteArray(); }
byte[] out = new byte[514]; byte d2[] = baos.toByteArray();
System.arraycopy(d2, 0, out, (d2.length < 257 ? 257 - d2.length : 0), (d2.length > 257 ? 257 : d2.length)); byte[] out = new byte[514];
return out; System.arraycopy(d2, 0, out, (d2.length < 257 ? 257 - d2.length : 0), (d2.length > 257 ? 257 : d2.length));
return out;
} }
/** Decrypt the data /** Decrypt the data
* @param encrypted encrypted data * @param encrypted encrypted data
* @param privateKey private key to decrypt with * @param privateKey private key to decrypt with
* @return unencrypted data * @return unencrypted data
*/ */
public byte[] decrypt(byte encrypted[], PrivateKey privateKey) { public byte[] decrypt(byte encrypted[], PrivateKey privateKey) {
if ( (encrypted == null) || (encrypted.length > 514) ) throw new IllegalArgumentException("Data to decrypt must be <= 514 bytes at the moment"); if ((encrypted == null) || (encrypted.length > 514))
byte val[] = new byte[257]; throw new IllegalArgumentException("Data to decrypt must be <= 514 bytes at the moment");
System.arraycopy(encrypted, 0, val, 0, val.length); byte val[] = new byte[257];
int i = 0; System.arraycopy(encrypted, 0, val, 0, val.length);
for (i = 0; i < val.length; i++) int i = 0;
if (val[i] != (byte)0x00) for (i = 0; i < val.length; i++)
break; if (val[i] != (byte) 0x00) break;
ByteArrayInputStream bais = new ByteArrayInputStream(val, i, val.length - i); ByteArrayInputStream bais = new ByteArrayInputStream(val, i, val.length - i);
Hash hash = new Hash(); Hash hash = new Hash();
byte rv[] = null; byte rv[] = null;
try { try {
bais.read(); // skip first byte bais.read(); // skip first byte
hash.readBytes(bais); hash.readBytes(bais);
rv = new byte[val.length - i - 1 - 32]; rv = new byte[val.length - i - 1 - 32];
bais.read(rv); bais.read(rv);
} catch (Exception e) { } catch (Exception e) {
_log.error("Internal error reading value", e); _log.error("Internal error reading value", e);
return null; return null;
} }
Hash calcHash = SHA256Generator.getInstance().calculateHash(rv); Hash calcHash = SHA256Generator.getInstance().calculateHash(rv);
if (calcHash.equals(hash)) { if (calcHash.equals(hash)) {
_log.debug("Hash matches: " + DataHelper.toString(hash.getData(), hash.getData().length)); _log.debug("Hash matches: " + DataHelper.toString(hash.getData(), hash.getData().length));
return rv; return rv;
} else { } else {
_log.debug("Doesn't match hash [calc=" + calcHash + " sent hash=" + hash + "]\ndata = " + new String(rv), new Exception("Doesn't match")); _log.debug("Doesn't match hash [calc=" + calcHash + " sent hash=" + hash + "]\ndata = " + new String(rv),
return null; new Exception("Doesn't match"));
} return null;
}
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.crypto; package net.i2p.crypto;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -24,10 +25,10 @@ import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey; import net.i2p.data.PublicKey;
import net.i2p.data.SessionKey; import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag; import net.i2p.data.SessionTag;
import net.i2p.stat.StatManager;
import net.i2p.util.Clock;
import net.i2p.util.Log; import net.i2p.util.Log;
import net.i2p.util.RandomSource; import net.i2p.util.RandomSource;
import net.i2p.util.Clock;
import net.i2p.stat.StatManager;
/** /**
* Handles the actual ElGamal+AES encryption and decryption scenarios using the * Handles the actual ElGamal+AES encryption and decryption scenarios using the
@@ -36,76 +37,87 @@ import net.i2p.stat.StatManager;
public class ElGamalAESEngine { public class ElGamalAESEngine {
private final static Log _log = new Log(ElGamalAESEngine.class); private final static Log _log = new Log(ElGamalAESEngine.class);
private final static int MIN_ENCRYPTED_SIZE = 80; // smallest possible resulting size private final static int MIN_ENCRYPTED_SIZE = 80; // smallest possible resulting size
static { static {
StatManager.getInstance().createFrequencyStat("crypto.elGamalAES.encryptNewSession", StatManager.getInstance()
"how frequently we encrypt to a new ElGamal/AES+SessionTag session?", "Encryption", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l } ); .createFrequencyStat("crypto.elGamalAES.encryptNewSession",
StatManager.getInstance().createFrequencyStat("crypto.elGamalAES.encryptExistingSession", "how frequently we encrypt to a new ElGamal/AES+SessionTag session?",
"how frequently we encrypt to an existing ElGamal/AES+SessionTag session?", "Encryption", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l } ); "Encryption", new long[] { 60 * 1000l, 60 * 60 * 1000l, 24 * 60 * 60 * 1000l});
StatManager.getInstance().createFrequencyStat("crypto.elGamalAES.decryptNewSession", StatManager.getInstance()
"how frequently we decrypt with a new ElGamal/AES+SessionTag session?", "Encryption", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l } ); .createFrequencyStat("crypto.elGamalAES.encryptExistingSession",
StatManager.getInstance().createFrequencyStat("crypto.elGamalAES.decryptExistingSession", "how frequently we encrypt to an existing ElGamal/AES+SessionTag session?",
"how frequently we decrypt with an existing ElGamal/AES+SessionTag session?", "Encryption", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l } ); "Encryption", new long[] { 60 * 1000l, 60 * 60 * 1000l, 24 * 60 * 60 * 1000l});
StatManager.getInstance().createFrequencyStat("crypto.elGamalAES.decryptFail", StatManager.getInstance()
"how frequently we fail to decrypt with ElGamal/AES+SessionTag?", "Encryption", new long[] { 60*60*1000l, 24*60*60*1000l } ); .createFrequencyStat("crypto.elGamalAES.decryptNewSession",
"how frequently we decrypt with a new ElGamal/AES+SessionTag session?",
"Encryption", new long[] { 60 * 1000l, 60 * 60 * 1000l, 24 * 60 * 60 * 1000l});
StatManager.getInstance()
.createFrequencyStat("crypto.elGamalAES.decryptExistingSession",
"how frequently we decrypt with an existing ElGamal/AES+SessionTag session?",
"Encryption", new long[] { 60 * 1000l, 60 * 60 * 1000l, 24 * 60 * 60 * 1000l});
StatManager.getInstance()
.createFrequencyStat("crypto.elGamalAES.decryptFail",
"how frequently we fail to decrypt with ElGamal/AES+SessionTag?", "Encryption",
new long[] { 60 * 60 * 1000l, 24 * 60 * 60 * 1000l});
} }
/** /**
* Decrypt the message using the given private key. This works according to the * Decrypt the message using the given private key. This works according to the
* ElGamal+AES algorithm in the data structure spec. * ElGamal+AES algorithm in the data structure spec.
* *
*/ */
public static byte[] decrypt(byte data[], PrivateKey targetPrivateKey) throws DataFormatException { public static byte[] decrypt(byte data[], PrivateKey targetPrivateKey) throws DataFormatException {
if (data == null) { if (data == null) {
if (_log.shouldLog(Log.WARN)) _log.warn("Null data being decrypted?"); if (_log.shouldLog(Log.WARN)) _log.warn("Null data being decrypted?");
return null; return null;
} else if (data.length < MIN_ENCRYPTED_SIZE) { } else if (data.length < MIN_ENCRYPTED_SIZE) {
if (_log.shouldLog(Log.WARN)) _log.warn("Data is less than the minimum size (" + data.length +" < " + MIN_ENCRYPTED_SIZE + ")"); if (_log.shouldLog(Log.WARN))
return null; _log.warn("Data is less than the minimum size (" + data.length + " < " + MIN_ENCRYPTED_SIZE + ")");
} return null;
}
byte tag[] = new byte[32];
System.arraycopy(data, 0, tag, 0, tag.length); byte tag[] = new byte[32];
SessionTag st = new SessionTag(tag); System.arraycopy(data, 0, tag, 0, tag.length);
SessionKey key = SessionKeyManager.getInstance().consumeTag(st); SessionTag st = new SessionTag(tag);
SessionKey foundKey = new SessionKey(); SessionKey key = SessionKeyManager.getInstance().consumeTag(st);
foundKey.setData(null); SessionKey foundKey = new SessionKey();
SessionKey usedKey = new SessionKey(); foundKey.setData(null);
Set foundTags = new HashSet(); SessionKey usedKey = new SessionKey();
byte decrypted[] = null; Set foundTags = new HashSet();
if (key != null) { byte decrypted[] = null;
if (_log.shouldLog(Log.DEBUG)) _log.debug("Key is known for tag " + st); if (key != null) {
usedKey.setData(key.getData()); if (_log.shouldLog(Log.DEBUG)) _log.debug("Key is known for tag " + st);
decrypted = decryptExistingSession(data, key, targetPrivateKey, foundTags, usedKey, foundKey); usedKey.setData(key.getData());
if (decrypted != null) decrypted = decryptExistingSession(data, key, targetPrivateKey, foundTags, usedKey, foundKey);
StatManager.getInstance().updateFrequency("crypto.elGamalAES.decryptExistingSession"); if (decrypted != null)
else StatManager.getInstance().updateFrequency("crypto.elGamalAES.decryptExistingSession");
StatManager.getInstance().updateFrequency("crypto.elGamalAES.decryptFailed"); else
} else { StatManager.getInstance().updateFrequency("crypto.elGamalAES.decryptFailed");
if (_log.shouldLog(Log.DEBUG)) _log.debug("Key is NOT known for tag " + st); } else {
decrypted = decryptNewSession(data, targetPrivateKey, foundTags, usedKey, foundKey); if (_log.shouldLog(Log.DEBUG)) _log.debug("Key is NOT known for tag " + st);
if (decrypted != null) decrypted = decryptNewSession(data, targetPrivateKey, foundTags, usedKey, foundKey);
StatManager.getInstance().updateFrequency("crypto.elGamalAES.decryptNewSession"); if (decrypted != null)
else StatManager.getInstance().updateFrequency("crypto.elGamalAES.decryptNewSession");
StatManager.getInstance().updateFrequency("crypto.elGamalAES.decryptFailed"); else
} StatManager.getInstance().updateFrequency("crypto.elGamalAES.decryptFailed");
}
if ( (key == null) && (decrypted == null) ) {
//_log.debug("Unable to decrypt the data starting with tag [" + st + "] - did the tag expire recently?", new Exception("Decrypt failure")); if ((key == null) && (decrypted == null)) {
} //_log.debug("Unable to decrypt the data starting with tag [" + st + "] - did the tag expire recently?", new Exception("Decrypt failure"));
}
if (foundTags.size() > 0) {
if (foundKey.getData() != null) { if (foundTags.size() > 0) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Found key: " + foundKey); if (foundKey.getData() != null) {
SessionKeyManager.getInstance().tagsReceived(foundKey, foundTags); if (_log.shouldLog(Log.DEBUG)) _log.debug("Found key: " + foundKey);
} else { SessionKeyManager.getInstance().tagsReceived(foundKey, foundTags);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Used key: " + usedKey); } else {
SessionKeyManager.getInstance().tagsReceived(usedKey, foundTags); if (_log.shouldLog(Log.DEBUG)) _log.debug("Used key: " + usedKey);
} SessionKeyManager.getInstance().tagsReceived(usedKey, foundTags);
} }
return decrypted; }
return decrypted;
} }
/** /**
* scenario 1: * scenario 1:
* Begin with 222 bytes, ElG encrypted, containing: * Begin with 222 bytes, ElG encrypted, containing:
@@ -120,55 +132,56 @@ public class ElGamalAESEngine {
* *
* @return null if decryption fails * @return null if decryption fails
*/ */
static byte[] decryptNewSession(byte data[], PrivateKey targetPrivateKey, Set foundTags, SessionKey usedKey, SessionKey foundKey) throws DataFormatException { static byte[] decryptNewSession(byte data[], PrivateKey targetPrivateKey, Set foundTags, SessionKey usedKey,
if (data == null) { SessionKey foundKey) throws DataFormatException {
if (_log.shouldLog(Log.WARN)) _log.warn("Data is null, unable to decrypt new session"); if (data == null) {
return null; if (_log.shouldLog(Log.WARN)) _log.warn("Data is null, unable to decrypt new session");
} else if (data.length < 514) { return null;
if (_log.shouldLog(Log.WARN)) _log.warn("Data length is too small ("+ data.length + ")"); } else if (data.length < 514) {
return null; if (_log.shouldLog(Log.WARN)) _log.warn("Data length is too small (" + data.length + ")");
} return null;
byte elgEncr[] = new byte[514]; }
if (data.length > 514) { byte elgEncr[] = new byte[514];
System.arraycopy(data, 0, elgEncr, 0, 514); if (data.length > 514) {
} else { System.arraycopy(data, 0, elgEncr, 0, 514);
System.arraycopy(data, 0, elgEncr, 514-data.length, data.length); } else {
} System.arraycopy(data, 0, elgEncr, 514 - data.length, data.length);
byte elgDecr[] = ElGamalEngine.getInstance().decrypt(elgEncr, targetPrivateKey); }
if (elgDecr == null) byte elgDecr[] = ElGamalEngine.getInstance().decrypt(elgEncr, targetPrivateKey);
return null; if (elgDecr == null) return null;
ByteArrayInputStream bais = new ByteArrayInputStream(elgDecr); ByteArrayInputStream bais = new ByteArrayInputStream(elgDecr);
byte preIV[] = null; byte preIV[] = null;
try { try {
usedKey.readBytes(bais); usedKey.readBytes(bais);
preIV = new byte[32]; preIV = new byte[32];
int read = bais.read(preIV); int read = bais.read(preIV);
if (read != preIV.length) { if (read != preIV.length) {
// hmm, this can't really happen... // hmm, this can't really happen...
throw new DataFormatException("Somehow ElGamal broke and 256 bytes is less than 32 bytes..."); throw new DataFormatException("Somehow ElGamal broke and 256 bytes is less than 32 bytes..."); }
} } catch (IOException ioe) {
} catch (IOException ioe) { if (_log.shouldLog(Log.ERROR)) _log.error("Error decrypting the new session", ioe);
if (_log.shouldLog(Log.ERROR)) _log.error("Error decrypting the new session", ioe); return null;
return null; }
} // ignore the next 192 bytes
// ignore the next 192 bytes byte aesEncr[] = new byte[data.length - 514];
byte aesEncr[] = new byte[data.length - 514]; System.arraycopy(data, 514, aesEncr, 0, aesEncr.length);
System.arraycopy(data, 514, aesEncr, 0, aesEncr.length);
//_log.debug("Pre IV for decryptNewSession: " + DataHelper.toString(preIV, 32));
//_log.debug("Pre IV for decryptNewSession: " + DataHelper.toString(preIV, 32)); //_log.debug("SessionKey for decryptNewSession: " + DataHelper.toString(key.getData(), 32));
//_log.debug("SessionKey for decryptNewSession: " + DataHelper.toString(key.getData(), 32)); Hash ivHash = SHA256Generator.getInstance().calculateHash(preIV);
Hash ivHash = SHA256Generator.getInstance().calculateHash(preIV); byte iv[] = new byte[16];
byte iv[] = new byte[16]; System.arraycopy(ivHash.getData(), 0, iv, 0, 16);
System.arraycopy(ivHash.getData(), 0, iv, 0, 16);
byte aesDecr[] = decryptAESBlock(aesEncr, usedKey, iv, null, foundTags, foundKey);
byte aesDecr[] = decryptAESBlock(aesEncr, usedKey, iv, null, foundTags, foundKey);
if (_log.shouldLog(Log.DEBUG))
if (_log.shouldLog(Log.DEBUG)) _log.debug("Decrypt with a NEW session successfull: # tags read = " + foundTags.size(), new Exception("Decrypted by")); _log.debug("Decrypt with a NEW session successfull: # tags read = " + foundTags.size(),
return aesDecr; new Exception("Decrypted by"));
return aesDecr;
} }
/** /**
* scenario 2: * scenario 2:
* The data begins with 32 byte session tag, which also serves as the preIV. * The data begins with 32 byte session tag, which also serves as the preIV.
@@ -187,31 +200,35 @@ public class ElGamalAESEngine {
* @param foundKey session key which may be filled with a new sessionKey found during decryption * @param foundKey session key which may be filled with a new sessionKey found during decryption
* *
*/ */
static byte[] decryptExistingSession(byte data[], SessionKey key, PrivateKey targetPrivateKey, Set foundTags, SessionKey usedKey, SessionKey foundKey) throws DataFormatException { static byte[] decryptExistingSession(byte data[], SessionKey key, PrivateKey targetPrivateKey, Set foundTags,
byte preIV[] = new byte[32]; SessionKey usedKey, SessionKey foundKey) throws DataFormatException {
System.arraycopy(data, 0, preIV, 0, preIV.length); byte preIV[] = new byte[32];
byte encr[] = new byte[data.length-32]; System.arraycopy(data, 0, preIV, 0, preIV.length);
System.arraycopy(data, 32, encr, 0, encr.length); byte encr[] = new byte[data.length - 32];
Hash ivHash = SHA256Generator.getInstance().calculateHash(preIV); System.arraycopy(data, 32, encr, 0, encr.length);
byte iv[] = new byte[16]; Hash ivHash = SHA256Generator.getInstance().calculateHash(preIV);
System.arraycopy(ivHash.getData(), 0, iv, 0, 16); byte iv[] = new byte[16];
System.arraycopy(ivHash.getData(), 0, iv, 0, 16);
usedKey.setData(key.getData());
usedKey.setData(key.getData());
//_log.debug("Pre IV for decryptExistingSession: " + DataHelper.toString(preIV, 32));
//_log.debug("SessionKey for decryptNewSession: " + DataHelper.toString(key.getData(), 32)); //_log.debug("Pre IV for decryptExistingSession: " + DataHelper.toString(preIV, 32));
byte decrypted[] = decryptAESBlock(encr, key, iv, preIV, foundTags, foundKey); //_log.debug("SessionKey for decryptNewSession: " + DataHelper.toString(key.getData(), 32));
if (decrypted == null) { byte decrypted[] = decryptAESBlock(encr, key, iv, preIV, foundTags, foundKey);
// it begins with a valid session tag, but thats just a coincidence. if (decrypted == null) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Decrypt with a non session tag, but tags read: " + foundTags.size()); // it begins with a valid session tag, but thats just a coincidence.
return decryptNewSession(data, targetPrivateKey, foundTags, usedKey, foundKey); if (_log.shouldLog(Log.DEBUG))
} else { _log.debug("Decrypt with a non session tag, but tags read: " + foundTags.size());
// existing session decrypted successfully! return decryptNewSession(data, targetPrivateKey, foundTags, usedKey, foundKey);
if (_log.shouldLog(Log.DEBUG)) _log.debug("Decrypt with an EXISTING session tag successfull, # tags read: " + foundTags.size(), new Exception("Decrypted by")); } else {
return decrypted; // existing session decrypted successfully!
} if (_log.shouldLog(Log.DEBUG))
_log.debug("Decrypt with an EXISTING session tag successfull, # tags read: " + foundTags.size(),
new Exception("Decrypted by"));
return decrypted;
}
} }
/** /**
* Decrypt the AES data with the session key and IV. The result should be: * Decrypt the AES data with the session key and IV. The result should be:
* - 2 byte integer specifying the # of session tags * - 2 byte integer specifying the # of session tags
@@ -229,68 +246,63 @@ public class ElGamalAESEngine {
* @param foundTags set which is filled with any sessionTags found during decryption * @param foundTags set which is filled with any sessionTags found during decryption
* @param foundKey session key which may be filled with a new sessionKey found during decryption * @param foundKey session key which may be filled with a new sessionKey found during decryption
*/ */
static byte[] decryptAESBlock(byte encrypted[], SessionKey key, byte iv[], byte sentTag[], Set foundTags, SessionKey foundKey) throws DataFormatException { static byte[] decryptAESBlock(byte encrypted[], SessionKey key, byte iv[], byte sentTag[], Set foundTags,
//_log.debug("iv for decryption: " + DataHelper.toString(iv, 16)); SessionKey foundKey) throws DataFormatException {
//_log.debug("decrypting AES block. encr.length = " + (encrypted == null? -1 : encrypted.length) + " sentTag: " + DataHelper.toString(sentTag, 32)); //_log.debug("iv for decryption: " + DataHelper.toString(iv, 16));
byte decrypted[] = AESEngine.getInstance().decrypt(encrypted, key, iv); //_log.debug("decrypting AES block. encr.length = " + (encrypted == null? -1 : encrypted.length) + " sentTag: " + DataHelper.toString(sentTag, 32));
Hash h = SHA256Generator.getInstance().calculateHash(decrypted); byte decrypted[] = AESEngine.getInstance().decrypt(encrypted, key, iv);
//_log.debug("Hash of entire aes block after decryption: \n" + DataHelper.toString(h.getData(), 32)); Hash h = SHA256Generator.getInstance().calculateHash(decrypted);
try { //_log.debug("Hash of entire aes block after decryption: \n" + DataHelper.toString(h.getData(), 32));
SessionKey newKey = null; try {
Hash readHash = null; SessionKey newKey = null;
List tags = new ArrayList(); Hash readHash = null;
List tags = new ArrayList();
ByteArrayInputStream bais = new ByteArrayInputStream(decrypted);
long numTags = DataHelper.readLong(bais, 2); ByteArrayInputStream bais = new ByteArrayInputStream(decrypted);
//_log.debug("# tags: " + numTags); long numTags = DataHelper.readLong(bais, 2);
if ( (numTags < 0) || (numTags > 65535) ) //_log.debug("# tags: " + numTags);
throw new Exception("Invalid number of session tags"); if ((numTags < 0) || (numTags > 65535)) throw new Exception("Invalid number of session tags");
for (int i = 0; i < numTags; i++) { for (int i = 0; i < numTags; i++) {
byte tag[] = new byte[32]; byte tag[] = new byte[32];
int read = bais.read(tag); int read = bais.read(tag);
if (read != 32) if (read != 32)
throw new Exception("Invalid session tag - # tags: " + numTags + " curTag #: " + i + " read: " + read); throw new Exception("Invalid session tag - # tags: " + numTags + " curTag #: " + i + " read: "
tags.add(new SessionTag(tag)); + read);
} tags.add(new SessionTag(tag));
long len = DataHelper.readLong(bais, 4); }
//_log.debug("len: " + len); long len = DataHelper.readLong(bais, 4);
if ( (len < 0) || (len > encrypted.length) ) //_log.debug("len: " + len);
throw new Exception("Invalid size of payload"); if ((len < 0) || (len > encrypted.length)) throw new Exception("Invalid size of payload");
byte hashval[] = new byte[32]; byte hashval[] = new byte[32];
int read = bais.read(hashval); int read = bais.read(hashval);
if (read != hashval.length) if (read != hashval.length) throw new Exception("Invalid size of hash");
throw new Exception("Invalid size of hash"); readHash = new Hash();
readHash = new Hash(); readHash.setData(hashval);
readHash.setData(hashval); byte flag = (byte) bais.read();
byte flag = (byte)bais.read(); if (flag == 0x01) {
if (flag == 0x01) { byte rekeyVal[] = new byte[32];
byte rekeyVal[] = new byte[32]; read = bais.read(rekeyVal);
read = bais.read(rekeyVal); if (read != rekeyVal.length) throw new Exception("Invalid size of the rekeyed session key");
if (read != rekeyVal.length) newKey = new SessionKey();
throw new Exception("Invalid size of the rekeyed session key"); newKey.setData(rekeyVal);
newKey = new SessionKey(); }
newKey.setData(rekeyVal); byte unencrData[] = new byte[(int) len];
} read = bais.read(unencrData);
byte unencrData[] = new byte[(int)len]; if (read != unencrData.length) throw new Exception("Invalid size of the data read");
read = bais.read(unencrData); Hash calcHash = SHA256Generator.getInstance().calculateHash(unencrData);
if (read != unencrData.length) if (calcHash.equals(readHash)) {
throw new Exception("Invalid size of the data read"); // everything matches. w00t.
Hash calcHash = SHA256Generator.getInstance().calculateHash(unencrData); foundTags.addAll(tags);
if (calcHash.equals(readHash)) { if (newKey != null) foundKey.setData(newKey.getData());
// everything matches. w00t. return unencrData;
foundTags.addAll(tags); } else {
if (newKey != null) throw new Exception("Hash does not match");
foundKey.setData(newKey.getData()); }
return unencrData; } catch (Exception e) {
} else { if (_log.shouldLog(Log.WARN)) _log.warn("Unable to decrypt AES block", e);
throw new Exception("Hash does not match"); return null;
} }
} catch (Exception e) {
if (_log.shouldLog(Log.WARN)) _log.warn("Unable to decrypt AES block", e);
return null;
}
} }
/** /**
* Encrypt the unencrypted data to the target. The total size returned will be * Encrypt the unencrypted data to the target. The total size returned will be
@@ -305,39 +317,43 @@ public class ElGamalAESEngine {
* @param paddedSize minimum size in bytes of the body after padding it (if less than the * @param paddedSize minimum size in bytes of the body after padding it (if less than the
* body's real size, no bytes are appended but the body is not truncated) * body's real size, no bytes are appended but the body is not truncated)
*/ */
public static byte[] encrypt(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery, SessionTag currentTag, SessionKey newKey, long paddedSize) { public static byte[] encrypt(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
if (currentTag == null) { SessionTag currentTag, SessionKey newKey, long paddedSize) {
if (_log.shouldLog(Log.INFO)) _log.info("Current tag is null, encrypting as new session", new Exception("encrypt new")); if (currentTag == null) {
StatManager.getInstance().updateFrequency("crypto.elGamalAES.encryptNewSession"); if (_log.shouldLog(Log.INFO))
return encryptNewSession(data, target, key, tagsForDelivery, newKey, paddedSize); _log.info("Current tag is null, encrypting as new session", new Exception("encrypt new"));
} else { StatManager.getInstance().updateFrequency("crypto.elGamalAES.encryptNewSession");
if (_log.shouldLog(Log.INFO)) _log.info("Current tag is NOT null, encrypting as existing session", new Exception("encrypt existing")); return encryptNewSession(data, target, key, tagsForDelivery, newKey, paddedSize);
StatManager.getInstance().updateFrequency("crypto.elGamalAES.encryptExistingSession"); } else {
return encryptExistingSession(data, target, key, tagsForDelivery, currentTag, newKey, paddedSize); if (_log.shouldLog(Log.INFO))
} _log.info("Current tag is NOT null, encrypting as existing session", new Exception("encrypt existing"));
StatManager.getInstance().updateFrequency("crypto.elGamalAES.encryptExistingSession");
return encryptExistingSession(data, target, key, tagsForDelivery, currentTag, newKey, paddedSize);
}
} }
/** /**
* Encrypt the data to the target using the given key and deliver the specified tags * Encrypt the data to the target using the given key and deliver the specified tags
*/ */
public static byte[] encrypt(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery, SessionTag currentTag, long paddedSize) { public static byte[] encrypt(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
return encrypt(data, target, key, tagsForDelivery, currentTag, null, paddedSize); SessionTag currentTag, long paddedSize) {
return encrypt(data, target, key, tagsForDelivery, currentTag, null, paddedSize);
} }
/** /**
* Encrypt the data to the target using the given key and deliver the specified tags * Encrypt the data to the target using the given key and deliver the specified tags
*/ */
public static byte[] encrypt(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery, long paddedSize) { public static byte[] encrypt(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery, long paddedSize) {
return encrypt(data, target, key, tagsForDelivery, null, null, paddedSize); return encrypt(data, target, key, tagsForDelivery, null, null, paddedSize);
} }
/** /**
* Encrypt the data to the target using the given key delivering no tags * Encrypt the data to the target using the given key delivering no tags
*/ */
public static byte[] encrypt(byte data[], PublicKey target, SessionKey key, long paddedSize) { public static byte[] encrypt(byte data[], PublicKey target, SessionKey key, long paddedSize) {
return encrypt(data, target, key, null, null, null, paddedSize); return encrypt(data, target, key, null, null, null, paddedSize);
} }
/** /**
* scenario 1: * scenario 1:
* Begin with 222 bytes, ElG encrypted, containing: * Begin with 222 bytes, ElG encrypted, containing:
@@ -354,56 +370,59 @@ public class ElGamalAESEngine {
* - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0 * - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0
* *
*/ */
static byte[] encryptNewSession(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery, SessionKey newKey, long paddedSize) { static byte[] encryptNewSession(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
//_log.debug("Encrypting to a NEW session"); SessionKey newKey, long paddedSize) {
try { //_log.debug("Encrypting to a NEW session");
ByteArrayOutputStream elgSrc = new ByteArrayOutputStream(64); try {
key.writeBytes(elgSrc); ByteArrayOutputStream elgSrc = new ByteArrayOutputStream(64);
byte preIV[] = new byte[32]; key.writeBytes(elgSrc);
RandomSource.getInstance().nextBytes(preIV); byte preIV[] = new byte[32];
elgSrc.write(preIV); RandomSource.getInstance().nextBytes(preIV);
byte rnd[] = new byte[158]; elgSrc.write(preIV);
RandomSource.getInstance().nextBytes(rnd); byte rnd[] = new byte[158];
elgSrc.write(rnd); RandomSource.getInstance().nextBytes(rnd);
elgSrc.flush(); elgSrc.write(rnd);
elgSrc.flush();
//_log.debug("Pre IV for encryptNewSession: " + DataHelper.toString(preIV, 32));
//_log.debug("SessionKey for encryptNewSession: " + DataHelper.toString(key.getData(), 32)); //_log.debug("Pre IV for encryptNewSession: " + DataHelper.toString(preIV, 32));
long before = Clock.getInstance().now(); //_log.debug("SessionKey for encryptNewSession: " + DataHelper.toString(key.getData(), 32));
byte elgEncr[] = ElGamalEngine.getInstance().encrypt(elgSrc.toByteArray(), target); long before = Clock.getInstance().now();
long after = Clock.getInstance().now(); byte elgEncr[] = ElGamalEngine.getInstance().encrypt(elgSrc.toByteArray(), target);
if (_log.shouldLog(Log.INFO)) _log.info("elgEngine.encrypt of the session key took " + (after-before) + "ms"); long after = Clock.getInstance().now();
if (elgEncr.length < 514) { if (_log.shouldLog(Log.INFO))
byte elg[] = new byte[514]; _log.info("elgEngine.encrypt of the session key took " + (after - before) + "ms");
int diff = elg.length - elgEncr.length; if (elgEncr.length < 514) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Difference in size: " + diff); byte elg[] = new byte[514];
System.arraycopy(elgEncr, 0, elg, diff, elgEncr.length); int diff = elg.length - elgEncr.length;
elgEncr = elg; if (_log.shouldLog(Log.DEBUG)) _log.debug("Difference in size: " + diff);
} System.arraycopy(elgEncr, 0, elg, diff, elgEncr.length);
//_log.debug("ElGamal encrypted length: " + elgEncr.length + " elGamal source length: " + elgSrc.toByteArray().length); elgEncr = elg;
}
Hash ivHash = SHA256Generator.getInstance().calculateHash(preIV); //_log.debug("ElGamal encrypted length: " + elgEncr.length + " elGamal source length: " + elgSrc.toByteArray().length);
byte iv[] = new byte[16];
System.arraycopy(ivHash.getData(), 0, iv, 0, 16); Hash ivHash = SHA256Generator.getInstance().calculateHash(preIV);
byte aesEncr[] = encryptAESBlock(data, key, iv, tagsForDelivery, newKey, paddedSize); byte iv[] = new byte[16];
//_log.debug("AES encrypted length: " + aesEncr.length); System.arraycopy(ivHash.getData(), 0, iv, 0, 16);
byte aesEncr[] = encryptAESBlock(data, key, iv, tagsForDelivery, newKey, paddedSize);
byte rv[] = new byte[elgEncr.length + aesEncr.length]; //_log.debug("AES encrypted length: " + aesEncr.length);
System.arraycopy(elgEncr, 0, rv, 0, elgEncr.length);
System.arraycopy(aesEncr, 0, rv, elgEncr.length, aesEncr.length); byte rv[] = new byte[elgEncr.length + aesEncr.length];
//_log.debug("Return length: " + rv.length); System.arraycopy(elgEncr, 0, rv, 0, elgEncr.length);
long finish = Clock.getInstance().now(); System.arraycopy(aesEncr, 0, rv, elgEncr.length, aesEncr.length);
if (_log.shouldLog(Log.DEBUG)) _log.debug("after the elgEngine.encrypt took a total of " + (finish-after) +"ms"); //_log.debug("Return length: " + rv.length);
return rv; long finish = Clock.getInstance().now();
} catch (IOException ioe) { if (_log.shouldLog(Log.DEBUG))
_log.error("Error encrypting the new session", ioe); _log.debug("after the elgEngine.encrypt took a total of " + (finish - after) + "ms");
return null; return rv;
} catch (DataFormatException dfe) { } catch (IOException ioe) {
_log.error("Error writing out the bytes for the new session", dfe); _log.error("Error encrypting the new session", ioe);
return null; return null;
} } catch (DataFormatException dfe) {
_log.error("Error writing out the bytes for the new session", dfe);
return null;
}
} }
/** /**
* scenario 2: * scenario 2:
* Begin with 32 byte session tag, which also serves as the preIV. * Begin with 32 byte session tag, which also serves as the preIV.
@@ -417,25 +436,26 @@ public class ElGamalAESEngine {
* - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0 * - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0
* *
*/ */
static byte[] encryptExistingSession(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery, SessionTag currentTag, SessionKey newKey, long paddedSize) { static byte[] encryptExistingSession(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
//_log.debug("Encrypting to an EXISTING session"); SessionTag currentTag, SessionKey newKey, long paddedSize) {
byte rawTag[] = currentTag.getData(); //_log.debug("Encrypting to an EXISTING session");
byte rawTag[] = currentTag.getData();
//_log.debug("Pre IV for encryptExistingSession (aka tag): " + currentTag.toString());
//_log.debug("SessionKey for encryptNewSession: " + DataHelper.toString(key.getData(), 32)); //_log.debug("Pre IV for encryptExistingSession (aka tag): " + currentTag.toString());
//_log.debug("SessionKey for encryptNewSession: " + DataHelper.toString(key.getData(), 32));
Hash ivHash = SHA256Generator.getInstance().calculateHash(rawTag); Hash ivHash = SHA256Generator.getInstance().calculateHash(rawTag);
byte iv[] = new byte[16]; byte iv[] = new byte[16];
System.arraycopy(ivHash.getData(), 0, iv, 0, 16); System.arraycopy(ivHash.getData(), 0, iv, 0, 16);
byte aesEncr[] = encryptAESBlock(data, key, iv, tagsForDelivery, newKey, paddedSize); byte aesEncr[] = encryptAESBlock(data, key, iv, tagsForDelivery, newKey, paddedSize);
byte rv[] = new byte[rawTag.length + aesEncr.length]; byte rv[] = new byte[rawTag.length + aesEncr.length];
System.arraycopy(rawTag, 0, rv, 0, rawTag.length); System.arraycopy(rawTag, 0, rv, 0, rawTag.length);
System.arraycopy(aesEncr, 0, rv, rawTag.length, aesEncr.length); System.arraycopy(aesEncr, 0, rv, rawTag.length, aesEncr.length);
return rv; return rv;
} }
private final static Set EMPTY_SET = new HashSet(); private final static Set EMPTY_SET = new HashSet();
/** /**
* For both scenarios, this method encrypts the AES area using the given key, iv * For both scenarios, this method encrypts the AES area using the given key, iv
* and making sure the resulting data is at least as long as the paddedSize and * and making sure the resulting data is at least as long as the paddedSize and
@@ -449,72 +469,71 @@ public class ElGamalAESEngine {
* - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0 * - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0
* *
*/ */
final static byte[] encryptAESBlock(byte data[], SessionKey key, byte[] iv, Set tagsForDelivery, SessionKey newKey, long paddedSize) { final static byte[] encryptAESBlock(byte data[], SessionKey key, byte[] iv, Set tagsForDelivery, SessionKey newKey,
//_log.debug("iv for encryption: " + DataHelper.toString(iv, 16)); long paddedSize) {
//_log.debug("Encrypting AES"); //_log.debug("iv for encryption: " + DataHelper.toString(iv, 16));
try { //_log.debug("Encrypting AES");
ByteArrayOutputStream aesSrc = new ByteArrayOutputStream((int)paddedSize); try {
if (tagsForDelivery == null) tagsForDelivery = EMPTY_SET; ByteArrayOutputStream aesSrc = new ByteArrayOutputStream((int) paddedSize);
DataHelper.writeLong(aesSrc, 2, tagsForDelivery.size()); if (tagsForDelivery == null) tagsForDelivery = EMPTY_SET;
for (Iterator iter = tagsForDelivery.iterator(); iter.hasNext(); ) { DataHelper.writeLong(aesSrc, 2, tagsForDelivery.size());
SessionTag tag = (SessionTag)iter.next(); for (Iterator iter = tagsForDelivery.iterator(); iter.hasNext();) {
aesSrc.write(tag.getData()); SessionTag tag = (SessionTag) iter.next();
} aesSrc.write(tag.getData());
//_log.debug("# tags created, registered, and written: " + tags.size()); }
DataHelper.writeLong(aesSrc, 4, data.length); //_log.debug("# tags created, registered, and written: " + tags.size());
//_log.debug("data length: " + data.length); DataHelper.writeLong(aesSrc, 4, data.length);
Hash hash = SHA256Generator.getInstance().calculateHash(data); //_log.debug("data length: " + data.length);
hash.writeBytes(aesSrc); Hash hash = SHA256Generator.getInstance().calculateHash(data);
//_log.debug("hash of data: " + DataHelper.toString(hash.getData(), 32)); hash.writeBytes(aesSrc);
if (newKey == null) { //_log.debug("hash of data: " + DataHelper.toString(hash.getData(), 32));
byte flag = 0x00; // don't rekey if (newKey == null) {
aesSrc.write(flag); byte flag = 0x00; // don't rekey
//_log.debug("flag written"); aesSrc.write(flag);
} else { //_log.debug("flag written");
byte flag = 0x01; // rekey } else {
aesSrc.write(flag); byte flag = 0x01; // rekey
aesSrc.write(newKey.getData()); aesSrc.write(flag);
} aesSrc.write(newKey.getData());
aesSrc.write(data); }
int len = aesSrc.toByteArray().length; aesSrc.write(data);
//_log.debug("raw data written: " + len); int len = aesSrc.toByteArray().length;
byte padding[] = getPadding(len, paddedSize); //_log.debug("raw data written: " + len);
//_log.debug("padding length: " + padding.length); byte padding[] = getPadding(len, paddedSize);
aesSrc.write(padding); //_log.debug("padding length: " + padding.length);
aesSrc.write(padding);
byte aesUnencr[] = aesSrc.toByteArray();
Hash h = SHA256Generator.getInstance().calculateHash(aesUnencr); byte aesUnencr[] = aesSrc.toByteArray();
//_log.debug("Hash of entire aes block before encryption: (len=" + aesUnencr.length + ")\n" + DataHelper.toString(h.getData(), 32)); Hash h = SHA256Generator.getInstance().calculateHash(aesUnencr);
byte aesEncr[] = AESEngine.getInstance().encrypt(aesUnencr, key, iv); //_log.debug("Hash of entire aes block before encryption: (len=" + aesUnencr.length + ")\n" + DataHelper.toString(h.getData(), 32));
//_log.debug("Encrypted length: " + aesEncr.length); byte aesEncr[] = AESEngine.getInstance().encrypt(aesUnencr, key, iv);
return aesEncr; //_log.debug("Encrypted length: " + aesEncr.length);
} catch (IOException ioe) { return aesEncr;
if (_log.shouldLog(Log.ERROR)) _log.error("Error encrypting AES chunk", ioe); } catch (IOException ioe) {
return null; if (_log.shouldLog(Log.ERROR)) _log.error("Error encrypting AES chunk", ioe);
} catch (DataFormatException dfe) { return null;
if (_log.shouldLog(Log.ERROR)) _log.error("Error formatting the bytes to write the AES chunk", dfe); } catch (DataFormatException dfe) {
return null; if (_log.shouldLog(Log.ERROR)) _log.error("Error formatting the bytes to write the AES chunk", dfe);
} return null;
}
} }
/** /**
* Return random bytes for padding the data to a mod 16 size so that it is * Return random bytes for padding the data to a mod 16 size so that it is
* at least minPaddedSize * at least minPaddedSize
* *
*/ */
final static byte[] getPadding(int curSize, long minPaddedSize) { final static byte[] getPadding(int curSize, long minPaddedSize) {
int diff = 0; int diff = 0;
if (curSize < minPaddedSize) { if (curSize < minPaddedSize) {
diff = (int)minPaddedSize - curSize; diff = (int) minPaddedSize - curSize;
} }
int numPadding = diff; int numPadding = diff;
if (((curSize + diff) % 16) != 0) if (((curSize + diff) % 16) != 0) numPadding += (16 - ((curSize + diff) % 16));
numPadding += (16-((curSize + diff) % 16)); byte rv[] = new byte[numPadding];
byte rv[] = new byte[numPadding]; RandomSource.getInstance().nextBytes(rv);
RandomSource.getInstance().nextBytes(rv); return rv;
return rv;
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.crypto; package net.i2p.crypto;
/* /*
* Copyright (c) 2003, TheCrypto * Copyright (c) 2003, TheCrypto
* All rights reserved. * All rights reserved.
@@ -37,11 +38,11 @@ import net.i2p.data.DataHelper;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.PrivateKey; import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey; import net.i2p.data.PublicKey;
import net.i2p.stat.StatManager;
import net.i2p.util.Clock; import net.i2p.util.Clock;
import net.i2p.util.Log; import net.i2p.util.Log;
import net.i2p.util.NativeBigInteger; import net.i2p.util.NativeBigInteger;
import net.i2p.util.RandomSource; import net.i2p.util.RandomSource;
import net.i2p.stat.StatManager;
/** /**
* Wrapper for ElGamal encryption/signature schemes. * Wrapper for ElGamal encryption/signature schemes.
@@ -55,204 +56,224 @@ import net.i2p.stat.StatManager;
*/ */
public class ElGamalEngine { public class ElGamalEngine {
private final static Log _log = new Log(ElGamalEngine.class); private final static Log _log = new Log(ElGamalEngine.class);
private static ElGamalEngine _engine; private static ElGamalEngine _engine;
static { static {
if ("off".equals(System.getProperty("i2p.encryption", "on"))) if ("off".equals(System.getProperty("i2p.encryption", "on")))
_engine = new DummyElGamalEngine(); _engine = new DummyElGamalEngine();
else else
_engine = new ElGamalEngine(); _engine = new ElGamalEngine();
StatManager.getInstance().createRateStat("crypto.elGamal.encrypt", StatManager.getInstance().createRateStat("crypto.elGamal.encrypt",
"how long does it take to do a full ElGamal encryption", "Encryption", new long[] { 60*1000, 60*60*1000, 24*60*60*1000 } ); "how long does it take to do a full ElGamal encryption", "Encryption",
StatManager.getInstance().createRateStat("crypto.elGamal.decrypt", new long[] { 60 * 1000, 60 * 60 * 1000, 24 * 60 * 60 * 1000});
"how long does it take to do a full ElGamal decryption", "Encryption", new long[] { 60*1000, 60*60*1000, 24*60*60*1000 } ); StatManager.getInstance().createRateStat("crypto.elGamal.decrypt",
"how long does it take to do a full ElGamal decryption", "Encryption",
new long[] { 60 * 1000, 60 * 60 * 1000, 24 * 60 * 60 * 1000});
} }
public static ElGamalEngine getInstance() { return _engine; }
private final static BigInteger _two = new NativeBigInteger(1, new byte[] { 0x02 } ); public static ElGamalEngine getInstance() {
return _engine;
private BigInteger[] getNextYK() { return YKGenerator.getNextYK(); } }
private final static BigInteger _two = new NativeBigInteger(1, new byte[] { 0x02});
private BigInteger[] getNextYK() {
return YKGenerator.getNextYK();
}
/** encrypt the data to the public key /** encrypt the data to the public key
* @return encrypted data * @return encrypted data
* @param publicKey public key encrypt to * @param publicKey public key encrypt to
* @param data data to encrypt * @param data data to encrypt
*/ */
public byte[] encrypt(byte data[], PublicKey publicKey) { public byte[] encrypt(byte data[], PublicKey publicKey) {
if ( (data == null) || (data.length >= 223) ) throw new IllegalArgumentException("Data to encrypt must be < 223 bytes at the moment"); if ((data == null) || (data.length >= 223))
if (publicKey == null) throw new IllegalArgumentException("Null public key specified"); throw new IllegalArgumentException("Data to encrypt must be < 223 bytes at the moment");
if (publicKey == null) throw new IllegalArgumentException("Null public key specified");
long start = Clock.getInstance().now();
ByteArrayOutputStream baos = new ByteArrayOutputStream(256);
try {
baos.write(0xFF);
Hash hash = SHA256Generator.getInstance().calculateHash(data);
hash.writeBytes(baos);
baos.write(data);
baos.flush();
} catch (Exception e) {
if (_log.shouldLog(Log.ERROR)) _log.error("Internal error writing to buffer", e);
return null;
}
byte d2[] = baos.toByteArray();
long t0 = Clock.getInstance().now();
BigInteger m = new NativeBigInteger(1, d2);
long t1 = Clock.getInstance().now();
if (m.compareTo(CryptoConstants.elgp) >= 0)
throw new IllegalArgumentException("ARGH. Data cannot be larger than the ElGamal prime. FIXME");
long t2 = Clock.getInstance().now();
BigInteger aalpha = new NativeBigInteger(1, publicKey.getData());
long t3 = Clock.getInstance().now();
BigInteger yk[] = getNextYK();
BigInteger k = yk[1];
BigInteger y = yk[0];
long t7 = Clock.getInstance().now(); long start = Clock.getInstance().now();
BigInteger d = aalpha.modPow(k, CryptoConstants.elgp);
long t8 = Clock.getInstance().now();
d = d.multiply(m);
long t9 = Clock.getInstance().now();
d = d.mod(CryptoConstants.elgp);
long t10 = Clock.getInstance().now();
byte[] ybytes = y.toByteArray(); ByteArrayOutputStream baos = new ByteArrayOutputStream(256);
byte[] dbytes = d.toByteArray(); try {
byte[] out = new byte[514]; baos.write(0xFF);
System.arraycopy(ybytes, 0, out, (ybytes.length < 257 ? 257 - ybytes.length : 0), (ybytes.length > 257 ? 257 : ybytes.length)); Hash hash = SHA256Generator.getInstance().calculateHash(data);
System.arraycopy(dbytes, 0, out, (dbytes.length < 257 ? 514 - dbytes.length : 257), (dbytes.length > 257 ? 257 : dbytes.length)); hash.writeBytes(baos);
StringBuffer buf = new StringBuffer(1024); baos.write(data);
buf.append("Timing\n"); baos.flush();
buf.append("0-1: ").append(t1-t0).append('\n'); } catch (Exception e) {
buf.append("1-2: ").append(t2-t1).append('\n'); if (_log.shouldLog(Log.ERROR)) _log.error("Internal error writing to buffer", e);
buf.append("2-3: ").append(t3-t2).append('\n'); return null;
//buf.append("3-4: ").append(t4-t3).append('\n'); }
//buf.append("4-5: ").append(t5-t4).append('\n');
//buf.append("5-6: ").append(t6-t5).append('\n');
//buf.append("6-7: ").append(t7-t6).append('\n');
buf.append("7-8: ").append(t8-t7).append('\n');
buf.append("8-9: ").append(t9-t8).append('\n');
buf.append("9-10: ").append(t10-t9).append('\n');
//_log.debug(buf.toString());
long end = Clock.getInstance().now();
long diff = end - start; byte d2[] = baos.toByteArray();
if (diff > 1000) { long t0 = Clock.getInstance().now();
if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to encrypt ElGamal block (" + diff + "ms)"); BigInteger m = new NativeBigInteger(1, d2);
} long t1 = Clock.getInstance().now();
if (m.compareTo(CryptoConstants.elgp) >= 0)
StatManager.getInstance().addRateData("crypto.elGamal.encrypt", diff, diff); throw new IllegalArgumentException("ARGH. Data cannot be larger than the ElGamal prime. FIXME");
return out; long t2 = Clock.getInstance().now();
BigInteger aalpha = new NativeBigInteger(1, publicKey.getData());
long t3 = Clock.getInstance().now();
BigInteger yk[] = getNextYK();
BigInteger k = yk[1];
BigInteger y = yk[0];
long t7 = Clock.getInstance().now();
BigInteger d = aalpha.modPow(k, CryptoConstants.elgp);
long t8 = Clock.getInstance().now();
d = d.multiply(m);
long t9 = Clock.getInstance().now();
d = d.mod(CryptoConstants.elgp);
long t10 = Clock.getInstance().now();
byte[] ybytes = y.toByteArray();
byte[] dbytes = d.toByteArray();
byte[] out = new byte[514];
System.arraycopy(ybytes, 0, out, (ybytes.length < 257 ? 257 - ybytes.length : 0),
(ybytes.length > 257 ? 257 : ybytes.length));
System.arraycopy(dbytes, 0, out, (dbytes.length < 257 ? 514 - dbytes.length : 257),
(dbytes.length > 257 ? 257 : dbytes.length));
StringBuffer buf = new StringBuffer(1024);
buf.append("Timing\n");
buf.append("0-1: ").append(t1 - t0).append('\n');
buf.append("1-2: ").append(t2 - t1).append('\n');
buf.append("2-3: ").append(t3 - t2).append('\n');
//buf.append("3-4: ").append(t4-t3).append('\n');
//buf.append("4-5: ").append(t5-t4).append('\n');
//buf.append("5-6: ").append(t6-t5).append('\n');
//buf.append("6-7: ").append(t7-t6).append('\n');
buf.append("7-8: ").append(t8 - t7).append('\n');
buf.append("8-9: ").append(t9 - t8).append('\n');
buf.append("9-10: ").append(t10 - t9).append('\n');
//_log.debug(buf.toString());
long end = Clock.getInstance().now();
long diff = end - start;
if (diff > 1000) {
if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to encrypt ElGamal block (" + diff + "ms)");
}
StatManager.getInstance().addRateData("crypto.elGamal.encrypt", diff, diff);
return out;
} }
/** Decrypt the data /** Decrypt the data
* @param encrypted encrypted data * @param encrypted encrypted data
* @param privateKey private key to decrypt with * @param privateKey private key to decrypt with
* @return unencrypted data * @return unencrypted data
*/ */
public byte[] decrypt(byte encrypted[], PrivateKey privateKey) { public byte[] decrypt(byte encrypted[], PrivateKey privateKey) {
if ( (encrypted == null) || (encrypted.length > 514) ) throw new IllegalArgumentException("Data to decrypt must be <= 514 bytes at the moment"); if ((encrypted == null) || (encrypted.length > 514))
long start = Clock.getInstance().now(); throw new IllegalArgumentException("Data to decrypt must be <= 514 bytes at the moment");
long start = Clock.getInstance().now();
byte[] ybytes = new byte[257];
byte[] dbytes = new byte[257];
System.arraycopy(encrypted, 0, ybytes, 0, 257);
System.arraycopy(encrypted, 257, dbytes, 0, 257);
BigInteger y = new NativeBigInteger(1, ybytes);
BigInteger d = new NativeBigInteger(1, dbytes);
BigInteger a = new NativeBigInteger(1, privateKey.getData());
BigInteger y1p = CryptoConstants.elgp.subtract(BigInteger.ONE).subtract(a);
BigInteger ya = y.modPow(y1p, CryptoConstants.elgp);
BigInteger m = ya.multiply(d); m = m.mod(CryptoConstants.elgp);
byte val[] = m.toByteArray();
int i = 0;
for (i = 0; i < val.length; i++)
if (val[i] != (byte)0x00)
break;
ByteArrayInputStream bais = new ByteArrayInputStream(val, i, val.length - i); byte[] ybytes = new byte[257];
Hash hash = new Hash(); byte[] dbytes = new byte[257];
byte rv[] = null; System.arraycopy(encrypted, 0, ybytes, 0, 257);
try { System.arraycopy(encrypted, 257, dbytes, 0, 257);
bais.read(); // skip first byte BigInteger y = new NativeBigInteger(1, ybytes);
hash.readBytes(bais); BigInteger d = new NativeBigInteger(1, dbytes);
rv = new byte[val.length - i - 1 - 32]; BigInteger a = new NativeBigInteger(1, privateKey.getData());
bais.read(rv); BigInteger y1p = CryptoConstants.elgp.subtract(BigInteger.ONE).subtract(a);
} catch (Exception e) { BigInteger ya = y.modPow(y1p, CryptoConstants.elgp);
if (_log.shouldLog(Log.ERROR)) _log.error("Internal error reading value", e); BigInteger m = ya.multiply(d);
return null; m = m.mod(CryptoConstants.elgp);
} byte val[] = m.toByteArray();
int i = 0;
for (i = 0; i < val.length; i++)
if (val[i] != (byte) 0x00) break;
ByteArrayInputStream bais = new ByteArrayInputStream(val, i, val.length - i);
Hash hash = new Hash();
byte rv[] = null;
try {
bais.read(); // skip first byte
hash.readBytes(bais);
rv = new byte[val.length - i - 1 - 32];
bais.read(rv);
} catch (Exception e) {
if (_log.shouldLog(Log.ERROR)) _log.error("Internal error reading value", e);
return null;
}
Hash calcHash = SHA256Generator.getInstance().calculateHash(rv); Hash calcHash = SHA256Generator.getInstance().calculateHash(rv);
boolean ok = calcHash.equals(hash); boolean ok = calcHash.equals(hash);
long end = Clock.getInstance().now(); long end = Clock.getInstance().now();
long diff = end - start; long diff = end - start;
if (diff > 1000) { if (diff > 1000) {
if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to decrypt and verify ElGamal block (" + diff + "ms)"); if (_log.shouldLog(Log.WARN))
} _log.warn("Took too long to decrypt and verify ElGamal block (" + diff + "ms)");
}
StatManager.getInstance().addRateData("crypto.elGamal.decrypt", diff, diff); StatManager.getInstance().addRateData("crypto.elGamal.decrypt", diff, diff);
if (ok) { if (ok) {
//_log.debug("Hash matches: " + DataHelper.toString(hash.getData(), hash.getData().length)); //_log.debug("Hash matches: " + DataHelper.toString(hash.getData(), hash.getData().length));
return rv; return rv;
} else { } else {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Doesn't match hash [calc=" + calcHash + " sent hash=" + hash + "]\ndata = " + Base64.encode(rv), new Exception("Doesn't match")); if (_log.shouldLog(Log.DEBUG))
return null; _log.debug("Doesn't match hash [calc=" + calcHash + " sent hash=" + hash + "]\ndata = "
} + Base64.encode(rv), new Exception("Doesn't match"));
return null;
}
} }
public static void main(String args[]) { public static void main(String args[]) {
long eTime = 0; long eTime = 0;
long dTime = 0; long dTime = 0;
long gTime = 0; long gTime = 0;
int numRuns = 100; int numRuns = 100;
if (args.length > 0) if (args.length > 0) try {
try { numRuns = Integer.parseInt(args[0]);
numRuns = Integer.parseInt(args[0]); } catch (NumberFormatException nfe) {
} catch (NumberFormatException nfe) {} }
try { Thread.sleep(30*1000); } catch (InterruptedException ie) {} try {
Thread.sleep(30 * 1000);
RandomSource.getInstance().nextBoolean(); } catch (InterruptedException ie) {
}
System.out.println("Running " + numRuns + " times");
RandomSource.getInstance().nextBoolean();
for (int i = 0; i < numRuns; i++) {
long startG = Clock.getInstance().now(); System.out.println("Running " + numRuns + " times");
Object pair[] = KeyGenerator.getInstance().generatePKIKeypair();
long endG = Clock.getInstance().now(); for (int i = 0; i < numRuns; i++) {
long startG = Clock.getInstance().now();
PublicKey pubkey = (PublicKey)pair[0]; Object pair[] = KeyGenerator.getInstance().generatePKIKeypair();
PrivateKey privkey = (PrivateKey)pair[1]; long endG = Clock.getInstance().now();
byte buf[] = new byte[128];
RandomSource.getInstance().nextBytes(buf); PublicKey pubkey = (PublicKey) pair[0];
long startE = Clock.getInstance().now(); PrivateKey privkey = (PrivateKey) pair[1];
byte encr[] = ElGamalEngine.getInstance().encrypt(buf, pubkey); byte buf[] = new byte[128];
long endE = Clock.getInstance().now(); RandomSource.getInstance().nextBytes(buf);
byte decr[] = ElGamalEngine.getInstance().decrypt(encr, privkey); long startE = Clock.getInstance().now();
long endD = Clock.getInstance().now(); byte encr[] = ElGamalEngine.getInstance().encrypt(buf, pubkey);
eTime += endE - startE; long endE = Clock.getInstance().now();
dTime += endD - endE; byte decr[] = ElGamalEngine.getInstance().decrypt(encr, privkey);
gTime += endG - startG; long endD = Clock.getInstance().now();
eTime += endE - startE;
if (!DataHelper.eq(decr, buf)) { dTime += endD - endE;
System.out.println("PublicKey : " + DataHelper.toString(pubkey.getData(), pubkey.getData().length)); gTime += endG - startG;
System.out.println("PrivateKey : " + DataHelper.toString(privkey.getData(), privkey.getData().length));
System.out.println("orig : " + DataHelper.toString(buf, buf.length)); if (!DataHelper.eq(decr, buf)) {
System.out.println("d(e(orig) : " + DataHelper.toString(decr, decr.length)); System.out.println("PublicKey : " + DataHelper.toString(pubkey.getData(), pubkey.getData().length));
System.out.println("orig.len : " + buf.length); System.out.println("PrivateKey : "
System.out.println("d(e(orig).len : " + decr.length); + DataHelper.toString(privkey.getData(), privkey.getData().length));
System.out.println("Not equal!"); System.out.println("orig : " + DataHelper.toString(buf, buf.length));
System.exit(0); System.out.println("d(e(orig) : " + DataHelper.toString(decr, decr.length));
} else { System.out.println("orig.len : " + buf.length);
System.out.println("*Run " +i+" is successful, with encr.length = " + encr.length + " [E: " + (endE-startE) + " D: " + (endD-endE) + " G: " + (endG - startG) + "]\n"); System.out.println("d(e(orig).len : " + decr.length);
} System.out.println("Not equal!");
} System.exit(0);
System.out.println("\n\nAll "+numRuns+" tests successful, average encryption time: " + (eTime/numRuns) + " average decryption time: " + (dTime / numRuns) + " average key generation time: " + (gTime / numRuns)); } else {
System.out.println("*Run " + i + " is successful, with encr.length = " + encr.length + " [E: "
+ (endE - startE) + " D: " + (endD - endE) + " G: " + (endG - startG) + "]\n");
}
}
System.out.println("\n\nAll " + numRuns + " tests successful, average encryption time: " + (eTime / numRuns)
+ " average decryption time: " + (dTime / numRuns) + " average key generation time: "
+ (gTime / numRuns));
} }
} }

View File

@@ -1,18 +1,21 @@
package net.i2p.crypto; package net.i2p.crypto;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.SessionKey; import net.i2p.data.SessionKey;
import net.i2p.data.DataHelper;
/** /**
* Calculate the HMAC-SHA256 of a key+message. Currently FAKE - returns a stupid * Calculate the HMAC-SHA256 of a key+message. Currently FAKE - returns a stupid
* kludgy hash: H(H(key) XOR H(data)). Fix me! * kludgy hash: H(H(key) XOR H(data)). Fix me!
* *
*/ */
public abstract class HMACSHA256Generator { public abstract class HMACSHA256Generator {
private static HMACSHA256Generator _generator = new DummyHMACSHA256Generator(); private static HMACSHA256Generator _generator = new DummyHMACSHA256Generator();
public static HMACSHA256Generator getInstance() { return _generator; }
public static HMACSHA256Generator getInstance() {
return _generator;
}
public abstract Hash calculate(SessionKey key, byte data[]); public abstract Hash calculate(SessionKey key, byte data[]);
} }
@@ -20,14 +23,14 @@ public abstract class HMACSHA256Generator {
* jrandom smells. * jrandom smells.
* *
*/ */
class DummyHMACSHA256Generator extends HMACSHA256Generator { class DummyHMACSHA256Generator extends HMACSHA256Generator {
public Hash calculate(SessionKey key, byte data[]) { public Hash calculate(SessionKey key, byte data[]) {
if ( (key == null) || (key.getData() == null) || (data == null) ) if ((key == null) || (key.getData() == null) || (data == null))
throw new NullPointerException("Null arguments for HMAC"); throw new NullPointerException("Null arguments for HMAC");
Hash hkey = SHA256Generator.getInstance().calculateHash(key.getData()); Hash hkey = SHA256Generator.getInstance().calculateHash(key.getData());
Hash hdata = SHA256Generator.getInstance().calculateHash(data); Hash hdata = SHA256Generator.getInstance().calculateHash(data);
return SHA256Generator.getInstance().calculateHash(DataHelper.xor(hkey.getData(), hdata.getData())); return SHA256Generator.getInstance().calculateHash(DataHelper.xor(hkey.getData(), hdata.getData()));
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.crypto; package net.i2p.crypto;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -29,8 +30,11 @@ public class KeyGenerator {
private final static Log _log = new Log(KeyGenerator.class); private final static Log _log = new Log(KeyGenerator.class);
private static final RandomSource _random = RandomSource.getInstance(); private static final RandomSource _random = RandomSource.getInstance();
private static KeyGenerator _generator = new KeyGenerator(); private static KeyGenerator _generator = new KeyGenerator();
public static KeyGenerator getInstance() { return _generator; }
public static KeyGenerator getInstance() {
return _generator;
}
/** Generate a private 256 bit session key /** Generate a private 256 bit session key
* @return session key * @return session key
*/ */
@@ -38,121 +42,124 @@ public class KeyGenerator {
// 256bit random # as a session key // 256bit random # as a session key
SessionKey key = new SessionKey(); SessionKey key = new SessionKey();
byte data[] = new byte[SessionKey.KEYSIZE_BYTES]; byte data[] = new byte[SessionKey.KEYSIZE_BYTES];
_random.nextBytes(data); _random.nextBytes(data);
key.setData(data); key.setData(data);
return key; return key;
} }
/** Generate a pair of keys, where index 0 is a PublicKey, and /** Generate a pair of keys, where index 0 is a PublicKey, and
* index 1 is a PrivateKey * index 1 is a PrivateKey
* @return pair of keys * @return pair of keys
*/ */
public Object[] generatePKIKeypair() { public Object[] generatePKIKeypair() {
BigInteger a = new NativeBigInteger(2048, _random); BigInteger a = new NativeBigInteger(2048, _random);
BigInteger aalpha = CryptoConstants.elgg.modPow(a, CryptoConstants.elgp); BigInteger aalpha = CryptoConstants.elgg.modPow(a, CryptoConstants.elgp);
Object[] keys = new Object[2]; Object[] keys = new Object[2];
keys[0] = new PublicKey(); keys[0] = new PublicKey();
keys[1] = new PrivateKey(); keys[1] = new PrivateKey();
byte[] k0 = aalpha.toByteArray(); byte[] k0 = aalpha.toByteArray();
byte[] k1 = a.toByteArray(); byte[] k1 = a.toByteArray();
// bigInteger.toByteArray returns SIGNED integers, but since they'return positive,
// signed two's complement is the same as unsigned
((PublicKey)keys[0]).setData(padBuffer(k0, PublicKey.KEYSIZE_BYTES)); // bigInteger.toByteArray returns SIGNED integers, but since they'return positive,
((PrivateKey)keys[1]).setData(padBuffer(k1, PrivateKey.KEYSIZE_BYTES)); // signed two's complement is the same as unsigned
return keys; ((PublicKey) keys[0]).setData(padBuffer(k0, PublicKey.KEYSIZE_BYTES));
((PrivateKey) keys[1]).setData(padBuffer(k1, PrivateKey.KEYSIZE_BYTES));
return keys;
} }
/** Generate a pair of DSA keys, where index 0 is a SigningPublicKey, and /** Generate a pair of DSA keys, where index 0 is a SigningPublicKey, and
* index 1 is a SigningPrivateKey * index 1 is a SigningPrivateKey
* @return pair of keys * @return pair of keys
*/ */
public Object[] generateSigningKeypair() { public Object[] generateSigningKeypair() {
Object[] keys = new Object[2]; Object[] keys = new Object[2];
BigInteger x = null; BigInteger x = null;
// make sure the random key is less than the DSA q
do {
x = new NativeBigInteger(160, _random);
} while (x.compareTo(CryptoConstants.dsaq) >= 0);
BigInteger y = CryptoConstants.dsag.modPow(x, CryptoConstants.dsap);
keys[0] = new SigningPublicKey();
keys[1] = new SigningPrivateKey();
byte k0[] = padBuffer(y.toByteArray(), SigningPublicKey.KEYSIZE_BYTES);
byte k1[] = padBuffer(x.toByteArray(), SigningPrivateKey.KEYSIZE_BYTES);
((SigningPublicKey)keys[0]).setData(k0); // make sure the random key is less than the DSA q
((SigningPrivateKey)keys[1]).setData(k1); do {
return keys; x = new NativeBigInteger(160, _random);
} while (x.compareTo(CryptoConstants.dsaq) >= 0);
BigInteger y = CryptoConstants.dsag.modPow(x, CryptoConstants.dsap);
keys[0] = new SigningPublicKey();
keys[1] = new SigningPrivateKey();
byte k0[] = padBuffer(y.toByteArray(), SigningPublicKey.KEYSIZE_BYTES);
byte k1[] = padBuffer(x.toByteArray(), SigningPrivateKey.KEYSIZE_BYTES);
((SigningPublicKey) keys[0]).setData(k0);
((SigningPrivateKey) keys[1]).setData(k1);
return keys;
} }
/** /**
* Pad the buffer w/ leading 0s or trim off leading bits so the result is the * Pad the buffer w/ leading 0s or trim off leading bits so the result is the
* given length. * given length.
*/ */
private final static byte[] padBuffer(byte src[], int length) { private final static byte[] padBuffer(byte src[], int length) {
byte buf[] = new byte[length]; byte buf[] = new byte[length];
if (src.length > buf.length) // extra bits, chop leading bits
System.arraycopy(src, src.length - buf.length, buf, 0, buf.length);
else if (src.length < buf.length) // short bits, padd w/ 0s
System.arraycopy(src, 0, buf, buf.length - src.length, src.length);
else // eq
System.arraycopy(src, 0, buf, 0, buf.length);
return buf;
}
public static void main(String args[]) {
Log log = new Log("keygenTest");
RandomSource.getInstance().nextBoolean();
byte src[] = new byte[200];
RandomSource.getInstance().nextBytes(src);
long time = 0;
for (int i = 0; i < 10; i++) {
long start = Clock.getInstance().now();
Object keys[] = KeyGenerator.getInstance().generatePKIKeypair();
long end = Clock.getInstance().now();
byte ctext[] = ElGamalEngine.getInstance().encrypt(src, (PublicKey)keys[0]);
byte ptext[] = ElGamalEngine.getInstance().decrypt(ctext, (PrivateKey)keys[1]);
time += end - start;
if (DataHelper.eq(ptext, src))
log.debug("D(E(data)) == data");
else
log.error("D(E(data)) != data!!!!!!");
}
log.info("Keygen 10 times: " + time + "ms");
Object obj[] = KeyGenerator.getInstance().generateSigningKeypair();
SigningPublicKey fake = (SigningPublicKey)obj[0];
time = 0;
for (int i = 0; i < 10; i++) {
long start = Clock.getInstance().now();
Object keys[] = KeyGenerator.getInstance().generateSigningKeypair();
long end = Clock.getInstance().now();
Signature sig = DSAEngine.getInstance().sign(src, (SigningPrivateKey)keys[1]);
boolean ok = DSAEngine.getInstance().verifySignature(sig, src, (SigningPublicKey)keys[0]);
boolean fakeOk = DSAEngine.getInstance().verifySignature(sig, src, fake);
time += end - start;
log.debug("V(S(data)) == " + ok + " fake verify correctly failed? " + (fakeOk == false));
}
log.info("Signing Keygen 10 times: " + time + "ms");
time = 0; if (src.length > buf.length) // extra bits, chop leading bits
for (int i = 0; i < 1000; i++) { System.arraycopy(src, src.length - buf.length, buf, 0, buf.length);
long start = Clock.getInstance().now(); else if (src.length < buf.length) // short bits, padd w/ 0s
KeyGenerator.getInstance().generateSessionKey(); System.arraycopy(src, 0, buf, buf.length - src.length, src.length);
long end = Clock.getInstance().now(); else
time += end - start; // eq
} System.arraycopy(src, 0, buf, 0, buf.length);
log.info("Session keygen 1000 times: " + time + "ms");
return buf;
try { Thread.sleep(5000); } catch (InterruptedException ie) {}
} }
}
public static void main(String args[]) {
Log log = new Log("keygenTest");
RandomSource.getInstance().nextBoolean();
byte src[] = new byte[200];
RandomSource.getInstance().nextBytes(src);
long time = 0;
for (int i = 0; i < 10; i++) {
long start = Clock.getInstance().now();
Object keys[] = KeyGenerator.getInstance().generatePKIKeypair();
long end = Clock.getInstance().now();
byte ctext[] = ElGamalEngine.getInstance().encrypt(src, (PublicKey) keys[0]);
byte ptext[] = ElGamalEngine.getInstance().decrypt(ctext, (PrivateKey) keys[1]);
time += end - start;
if (DataHelper.eq(ptext, src))
log.debug("D(E(data)) == data");
else
log.error("D(E(data)) != data!!!!!!");
}
log.info("Keygen 10 times: " + time + "ms");
Object obj[] = KeyGenerator.getInstance().generateSigningKeypair();
SigningPublicKey fake = (SigningPublicKey) obj[0];
time = 0;
for (int i = 0; i < 10; i++) {
long start = Clock.getInstance().now();
Object keys[] = KeyGenerator.getInstance().generateSigningKeypair();
long end = Clock.getInstance().now();
Signature sig = DSAEngine.getInstance().sign(src, (SigningPrivateKey) keys[1]);
boolean ok = DSAEngine.getInstance().verifySignature(sig, src, (SigningPublicKey) keys[0]);
boolean fakeOk = DSAEngine.getInstance().verifySignature(sig, src, fake);
time += end - start;
log.debug("V(S(data)) == " + ok + " fake verify correctly failed? " + (fakeOk == false));
}
log.info("Signing Keygen 10 times: " + time + "ms");
time = 0;
for (int i = 0; i < 1000; i++) {
long start = Clock.getInstance().now();
KeyGenerator.getInstance().generateSessionKey();
long end = Clock.getInstance().now();
time += end - start;
}
log.info("Session keygen 1000 times: " + time + "ms");
try {
Thread.sleep(5000);
} catch (InterruptedException ie) {
}
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.crypto; package net.i2p.crypto;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -35,130 +36,135 @@ import net.i2p.util.Log;
*/ */
public class PersistentSessionKeyManager extends TransientSessionKeyManager { public class PersistentSessionKeyManager extends TransientSessionKeyManager {
private final static Log _log = new Log(PersistentSessionKeyManager.class); private final static Log _log = new Log(PersistentSessionKeyManager.class);
private Object _yk = YKGenerator.class; private Object _yk = YKGenerator.class;
/** /**
* Write the session key data to the given stream * Write the session key data to the given stream
* *
*/ */
public void saveState(OutputStream out) throws IOException, DataFormatException { public void saveState(OutputStream out) throws IOException, DataFormatException {
Set tagSets = getInboundTagSets(); Set tagSets = getInboundTagSets();
Set sessions = getOutboundSessions(); Set sessions = getOutboundSessions();
_log.info("Saving state with " + tagSets.size() + " inbound tagSets and " + sessions.size() + " outbound sessions"); _log.info("Saving state with " + tagSets.size() + " inbound tagSets and " + sessions.size()
+ " outbound sessions");
DataHelper.writeLong(out, 4, tagSets.size());
for (Iterator iter = tagSets.iterator(); iter.hasNext(); ) { DataHelper.writeLong(out, 4, tagSets.size());
TagSet ts = (TagSet)iter.next(); for (Iterator iter = tagSets.iterator(); iter.hasNext();) {
writeTagSet(out, ts); TagSet ts = (TagSet) iter.next();
} writeTagSet(out, ts);
DataHelper.writeLong(out, 4, sessions.size()); }
for (Iterator iter = sessions.iterator(); iter.hasNext(); ) { DataHelper.writeLong(out, 4, sessions.size());
OutboundSession sess = (OutboundSession)iter.next(); for (Iterator iter = sessions.iterator(); iter.hasNext();) {
writeOutboundSession(out, sess); OutboundSession sess = (OutboundSession) iter.next();
} writeOutboundSession(out, sess);
}
} }
/** /**
* Load the session key data from the given stream * Load the session key data from the given stream
* *
*/ */
public void loadState(InputStream in) throws IOException, DataFormatException { public void loadState(InputStream in) throws IOException, DataFormatException {
int inboundSets = (int)DataHelper.readLong(in, 4); int inboundSets = (int) DataHelper.readLong(in, 4);
Set tagSets = new HashSet(inboundSets); Set tagSets = new HashSet(inboundSets);
for (int i = 0; i < inboundSets; i++) { for (int i = 0; i < inboundSets; i++) {
TagSet ts = readTagSet(in); TagSet ts = readTagSet(in);
tagSets.add(ts); tagSets.add(ts);
} }
int outboundSessions = (int)DataHelper.readLong(in, 4); int outboundSessions = (int) DataHelper.readLong(in, 4);
Set sessions = new HashSet(outboundSessions); Set sessions = new HashSet(outboundSessions);
for (int i = 0; i < outboundSessions; i++) { for (int i = 0; i < outboundSessions; i++) {
OutboundSession sess = readOutboundSession(in); OutboundSession sess = readOutboundSession(in);
sessions.add(sess); sessions.add(sess);
} }
_log.info("Loading state with " + tagSets.size() + " inbound tagSets and " + sessions.size() + " outbound sessions"); _log.info("Loading state with " + tagSets.size() + " inbound tagSets and " + sessions.size()
setData(tagSets, sessions); + " outbound sessions");
setData(tagSets, sessions);
} }
private void writeOutboundSession(OutputStream out, OutboundSession sess) throws IOException, DataFormatException { private void writeOutboundSession(OutputStream out, OutboundSession sess) throws IOException, DataFormatException {
sess.getTarget().writeBytes(out); sess.getTarget().writeBytes(out);
sess.getCurrentKey().writeBytes(out); sess.getCurrentKey().writeBytes(out);
DataHelper.writeDate(out, new Date(sess.getEstablishedDate())); DataHelper.writeDate(out, new Date(sess.getEstablishedDate()));
DataHelper.writeDate(out, new Date(sess.getLastUsedDate())); DataHelper.writeDate(out, new Date(sess.getLastUsedDate()));
List sets = sess.getTagSets(); List sets = sess.getTagSets();
DataHelper.writeLong(out, 2, sets.size()); DataHelper.writeLong(out, 2, sets.size());
for (Iterator iter = sets.iterator(); iter.hasNext(); ) { for (Iterator iter = sets.iterator(); iter.hasNext();) {
TagSet set = (TagSet)iter.next(); TagSet set = (TagSet) iter.next();
writeTagSet(out, set); writeTagSet(out, set);
} }
} }
private void writeTagSet(OutputStream out, TagSet ts) throws IOException, DataFormatException { private void writeTagSet(OutputStream out, TagSet ts) throws IOException, DataFormatException {
ts.getAssociatedKey().writeBytes(out); ts.getAssociatedKey().writeBytes(out);
DataHelper.writeDate(out, new Date(ts.getDate())); DataHelper.writeDate(out, new Date(ts.getDate()));
DataHelper.writeLong(out, 2, ts.getTags().size()); DataHelper.writeLong(out, 2, ts.getTags().size());
for (Iterator iter = ts.getTags().iterator(); iter.hasNext(); ) { for (Iterator iter = ts.getTags().iterator(); iter.hasNext();) {
SessionTag tag = (SessionTag)iter.next(); SessionTag tag = (SessionTag) iter.next();
out.write(tag.getData()); out.write(tag.getData());
} }
} }
private OutboundSession readOutboundSession(InputStream in) throws IOException, DataFormatException { private OutboundSession readOutboundSession(InputStream in) throws IOException, DataFormatException {
PublicKey key = new PublicKey(); PublicKey key = new PublicKey();
key.readBytes(in); key.readBytes(in);
SessionKey skey = new SessionKey(); SessionKey skey = new SessionKey();
skey.readBytes(in); skey.readBytes(in);
Date established = DataHelper.readDate(in); Date established = DataHelper.readDate(in);
Date lastUsed = DataHelper.readDate(in); Date lastUsed = DataHelper.readDate(in);
int tagSets = (int)DataHelper.readLong(in, 2); int tagSets = (int) DataHelper.readLong(in, 2);
ArrayList sets = new ArrayList(tagSets); ArrayList sets = new ArrayList(tagSets);
for (int i = 0; i < tagSets; i++) { for (int i = 0; i < tagSets; i++) {
TagSet ts = readTagSet(in); TagSet ts = readTagSet(in);
sets.add(ts); sets.add(ts);
} }
return new OutboundSession(key, skey, established.getTime(), lastUsed.getTime(), sets); return new OutboundSession(key, skey, established.getTime(), lastUsed.getTime(), sets);
} }
private TagSet readTagSet(InputStream in) throws IOException, DataFormatException { private TagSet readTagSet(InputStream in) throws IOException, DataFormatException {
SessionKey key = new SessionKey(); SessionKey key = new SessionKey();
key.readBytes(in); key.readBytes(in);
Date date = DataHelper.readDate(in); Date date = DataHelper.readDate(in);
int numTags = (int)DataHelper.readLong(in, 2); int numTags = (int) DataHelper.readLong(in, 2);
Set tags = new HashSet(numTags); Set tags = new HashSet(numTags);
for (int i = 0; i < numTags; i++) { for (int i = 0; i < numTags; i++) {
SessionTag tag = new SessionTag(); SessionTag tag = new SessionTag();
byte val[] = new byte[SessionTag.BYTE_LENGTH]; byte val[] = new byte[SessionTag.BYTE_LENGTH];
int read = DataHelper.read(in, val); int read = DataHelper.read(in, val);
if (read != SessionTag.BYTE_LENGTH) if (read != SessionTag.BYTE_LENGTH)
throw new IOException("Unable to fully read a session tag [" + read + " not " + SessionTag.BYTE_LENGTH + ")"); throw new IOException("Unable to fully read a session tag [" + read + " not " + SessionTag.BYTE_LENGTH
tag.setData(val); + ")");
tags.add(tag); tag.setData(val);
} tags.add(tag);
TagSet ts = new TagSet(tags, key); }
ts.setDate(date.getTime()); TagSet ts = new TagSet(tags, key);
return ts; ts.setDate(date.getTime());
return ts;
} }
public static void main(String args[]) { public static void main(String args[]) {
PersistentSessionKeyManager mgr = new PersistentSessionKeyManager(); PersistentSessionKeyManager mgr = new PersistentSessionKeyManager();
try { try {
mgr.loadState(new FileInputStream("sessionKeys.dat")); mgr.loadState(new FileInputStream("sessionKeys.dat"));
String state = mgr.renderStatusHTML(); String state = mgr.renderStatusHTML();
FileOutputStream fos = new FileOutputStream("sessionKeysBeforeExpire.html"); FileOutputStream fos = new FileOutputStream("sessionKeysBeforeExpire.html");
fos.write(state.getBytes()); fos.write(state.getBytes());
fos.close(); fos.close();
int expired = mgr.aggressiveExpire(); int expired = mgr.aggressiveExpire();
_log.error("Expired: " + expired); _log.error("Expired: " + expired);
String stateAfter = mgr.renderStatusHTML(); String stateAfter = mgr.renderStatusHTML();
FileOutputStream fos2 = new FileOutputStream("sessionKeysAfterExpire.html"); FileOutputStream fos2 = new FileOutputStream("sessionKeysAfterExpire.html");
fos2.write(stateAfter.getBytes()); fos2.write(stateAfter.getBytes());
fos2.close(); fos2.close();
} catch (Throwable t) { } catch (Throwable t) {
_log.error("Error loading/storing sessionKeys", t); _log.error("Error loading/storing sessionKeys", t);
} }
try { Thread.sleep(3000); } catch (Throwable t) {} try {
Thread.sleep(3000);
} catch (Throwable t) {
}
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.crypto; package net.i2p.crypto;
/* /*
* Copyright (c) 2003, TheCrypto * Copyright (c) 2003, TheCrypto
* All rights reserved. * All rights reserved.
@@ -36,138 +37,156 @@ import net.i2p.data.Hash;
* *
* @author thecrypto,jrandom * @author thecrypto,jrandom
*/ */
public class SHA256Generator { public class SHA256Generator {
private static SHA256Generator _generator = new SHA256Generator(); private static SHA256Generator _generator = new SHA256Generator();
public static SHA256Generator getInstance() { return _generator; }
static int[] K = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
static int[] H0 = { public static SHA256Generator getInstance() {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 return _generator;
}; }
static int[] K = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};
static int[] H0 = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
/** Calculate the SHA-256 has of the source /** Calculate the SHA-256 has of the source
* @param source what to hash * @param source what to hash
* @return hash of the source * @return hash of the source
*/ */
public Hash calculateHash(byte[] source) { public Hash calculateHash(byte[] source) {
long length = source.length * 8; long length = source.length * 8;
int k = 448 - (int)((length+1) % 512); int k = 448 - (int) ((length + 1) % 512);
if (k < 0) { if (k < 0) {
k += 512; k += 512;
} }
int padbytes = k/8; int padbytes = k / 8;
int wordlength = (int)(source.length/4 + padbytes/4 + 3); int wordlength = (int) (source.length / 4 + padbytes / 4 + 3);
int[] M0 = new int[wordlength]; int[] M0 = new int[wordlength];
int wordcount = 0; int wordcount = 0;
int x = 0; int x = 0;
for (x = 0; x < (source.length / 4) * 4; x += 4) { for (x = 0; x < (source.length / 4) * 4; x += 4) {
M0[wordcount] = source[ x ] << 24 >>> 24 << 24; M0[wordcount] = source[x] << 24 >>> 24 << 24;
M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16; M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16;
M0[wordcount] |= source[x + 2] << 24 >>> 24 << 8; M0[wordcount] |= source[x + 2] << 24 >>> 24 << 8;
M0[wordcount] |= source[x + 3] << 24 >>> 24 << 0; M0[wordcount] |= source[x + 3] << 24 >>> 24 << 0;
wordcount++; wordcount++;
} }
switch (source.length - (wordcount + 1) * 4 + 4) { switch (source.length - (wordcount + 1) * 4 + 4) {
case 0: case 0:
M0[wordcount] |= 0x80000000; M0[wordcount] |= 0x80000000;
break; break;
case 1: case 1:
M0[wordcount] = source[ x ] << 24 >>> 24 << 24; M0[wordcount] = source[x] << 24 >>> 24 << 24;
M0[wordcount] |= 0x00800000; M0[wordcount] |= 0x00800000;
break; break;
case 2: case 2:
M0[wordcount] = source[ x ] << 24 >>> 24 << 24; M0[wordcount] = source[x] << 24 >>> 24 << 24;
M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16; M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16;
M0[wordcount] |= 0x00008000; M0[wordcount] |= 0x00008000;
break; break;
case 3: case 3:
M0[wordcount] = source[ x ] << 24 >>> 24 << 24; M0[wordcount] = source[x] << 24 >>> 24 << 24;
M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16; M0[wordcount] |= source[x + 1] << 24 >>> 24 << 16;
M0[wordcount] |= source[x + 2] << 24 >>> 24 << 8; M0[wordcount] |= source[x + 2] << 24 >>> 24 << 8;
M0[wordcount] |= 0x00000080; M0[wordcount] |= 0x00000080;
break; break;
} }
M0[wordlength - 2] = (int)(length >>> 32); M0[wordlength - 2] = (int) (length >>> 32);
M0[wordlength - 1] = (int)(length); M0[wordlength - 1] = (int) (length);
int[] H = new int[8]; int[] H = new int[8];
for (x = 0; x < 8; x++) { for (x = 0; x < 8; x++) {
H[x] = H0[x]; H[x] = H0[x];
} }
int blocks = M0.length/16; int blocks = M0.length / 16;
for (int bl = 0; bl < blocks; bl++) { for (int bl = 0; bl < blocks; bl++) {
int a = H[0]; int b = H[1]; int c = H[2]; int d = H[3]; int a = H[0];
int e = H[4]; int f = H[5]; int g = H[6]; int h = H[7]; int b = H[1];
int[] W = new int[64]; int c = H[2];
for (x = 0; x < 64; x++) { int d = H[3];
if (x < 16) { int e = H[4];
W[x] = M0[bl*16 + x]; int f = H[5];
} else { int g = H[6];
W[x] = add(o1(W[x-2]), add(W[x-7], add(o0(W[x-15]), W[x-16]))); int h = H[7];
} int[] W = new int[64];
} for (x = 0; x < 64; x++) {
for (x = 0; x < 64; x++) { if (x < 16) {
int T1 = add(h, add(e1(e), add(Ch(e, f, g), add(K[x], W[x])))); W[x] = M0[bl * 16 + x];
int T2 = add(e0(a), Maj(a, b, c)); } else {
h = g; g = f; f = e; e = add(d, T1); d = c; c = b; b = a; a = add(T1, T2); W[x] = add(o1(W[x - 2]), add(W[x - 7], add(o0(W[x - 15]), W[x - 16])));
} }
H[0] = add(a, H[0]); H[1] = add(b, H[1]); H[2] = add(c, H[2]); H[3] = add(d, H[3]); }
H[4] = add(e, H[4]); H[5] = add(f, H[5]); H[6] = add(g, H[6]); H[7] = add(h, H[7]); for (x = 0; x < 64; x++) {
} int T1 = add(h, add(e1(e), add(Ch(e, f, g), add(K[x], W[x]))));
byte[] hashbytes = new byte[32]; int T2 = add(e0(a), Maj(a, b, c));
for (x = 0; x < 8; x++) { h = g;
hashbytes[ x * 4 ] = (byte)(H[x] << 0 >>> 24); g = f;
hashbytes[x * 4 + 1] = (byte)(H[x] << 8 >>> 24); f = e;
hashbytes[x * 4 + 2] = (byte)(H[x] << 16 >>> 24); e = add(d, T1);
hashbytes[x * 4 + 3] = (byte)(H[x] << 24 >>> 24); d = c;
} c = b;
b = a;
a = add(T1, T2);
}
H[0] = add(a, H[0]);
H[1] = add(b, H[1]);
H[2] = add(c, H[2]);
H[3] = add(d, H[3]);
H[4] = add(e, H[4]);
H[5] = add(f, H[5]);
H[6] = add(g, H[6]);
H[7] = add(h, H[7]);
}
byte[] hashbytes = new byte[32];
for (x = 0; x < 8; x++) {
hashbytes[x * 4] = (byte) (H[x] << 0 >>> 24);
hashbytes[x * 4 + 1] = (byte) (H[x] << 8 >>> 24);
hashbytes[x * 4 + 2] = (byte) (H[x] << 16 >>> 24);
hashbytes[x * 4 + 3] = (byte) (H[x] << 24 >>> 24);
}
Hash hash = new Hash(); Hash hash = new Hash();
hash.setData(hashbytes); hash.setData(hashbytes);
return hash; return hash;
} }
private static int Ch(int x, int y, int z) { private static int Ch(int x, int y, int z) {
return (x & y) ^ (~x & z); return (x & y) ^ (~x & z);
} }
private static int Maj(int x, int y, int z) { private static int Maj(int x, int y, int z) {
return (x & y) ^ (x & z) ^ (y & z); return (x & y) ^ (x & z) ^ (y & z);
} }
private static int ROTR (int x, int n) { private static int ROTR(int x, int n) {
return (x >>> n) | (x << 32 - n); return (x >>> n) | (x << 32 - n);
} }
private static int e0(int x) {
return ROTR(x, 2) ^ ROTR (x, 13) ^ ROTR(x, 22);
}
private static int e1(int x) {
return ROTR(x, 6) ^ ROTR (x, 11) ^ ROTR(x, 25);
}
private static int SHR(int x, int n) { private static int e0(int x) {
return x >>> n; return ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22);
} }
private static int o0(int x) { private static int e1(int x) {
return ROTR(x, 7) ^ ROTR (x, 18) ^ SHR(x, 3); return ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25);
} }
private static int o1(int x) { private static int SHR(int x, int n) {
return ROTR(x, 17) ^ ROTR (x, 19) ^ SHR(x, 10); return x >>> n;
} }
private static int add (int x, int y) { private static int o0(int x) {
return x+y; return ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3);
} }
}
private static int o1(int x) {
return ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10);
}
private static int add(int x, int y) {
return x + y;
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.crypto; package net.i2p.crypto;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,12 +9,12 @@ package net.i2p.crypto;
* *
*/ */
import java.util.Set;
import net.i2p.data.PublicKey; import net.i2p.data.PublicKey;
import net.i2p.data.SessionKey; import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag; import net.i2p.data.SessionTag;
import java.util.Set;
/** /**
* Manage the session keys and session tags used for encryption and decryption. * Manage the session keys and session tags used for encryption and decryption.
* This base implementation simply ignores sessions and acts as if everything is * This base implementation simply ignores sessions and acts as if everything is
@@ -23,32 +24,38 @@ import java.util.Set;
*/ */
public class SessionKeyManager { public class SessionKeyManager {
private final static SessionKeyManager _instance = new PersistentSessionKeyManager(); // new TransientSessionKeyManager(); // SessionKeyManager(); private final static SessionKeyManager _instance = new PersistentSessionKeyManager(); // new TransientSessionKeyManager(); // SessionKeyManager();
public final static SessionKeyManager getInstance() { return _instance; }
public final static SessionKeyManager getInstance() {
return _instance;
}
/** /**
* Retrieve the session key currently associated with encryption to the target, * Retrieve the session key currently associated with encryption to the target,
* or null if a new session key should be generated. * or null if a new session key should be generated.
* *
*/ */
public SessionKey getCurrentKey(PublicKey target) { return null; } public SessionKey getCurrentKey(PublicKey target) {
return null;
}
/** /**
* Associate a new session key with the specified target. Metrics to determine * Associate a new session key with the specified target. Metrics to determine
* when to expire that key begin with this call. * when to expire that key begin with this call.
* *
*/ */
public void createSession(PublicKey target, SessionKey key) { } public void createSession(PublicKey target, SessionKey key) {
}
/** /**
* Generate a new session key and associate it with the specified target. * Generate a new session key and associate it with the specified target.
* *
*/ */
public SessionKey createSession(PublicKey target) { public SessionKey createSession(PublicKey target) {
SessionKey key = KeyGenerator.getInstance().generateSessionKey(); SessionKey key = KeyGenerator.getInstance().generateSessionKey();
createSession(target, key); createSession(target, key);
return key; return key;
} }
/** /**
* Retrieve the next available session tag for identifying the use of the given * Retrieve the next available session tag for identifying the use of the given
* key when communicating with the target. If this returns null, no tags are * key when communicating with the target. If this returns null, no tags are
@@ -56,43 +63,52 @@ public class SessionKeyManager {
* NOT be used) * NOT be used)
* *
*/ */
public SessionTag consumeNextAvailableTag(PublicKey target, SessionKey key) { return null; } public SessionTag consumeNextAvailableTag(PublicKey target, SessionKey key) {
return null;
}
/** /**
* Determine (approximately) how many available session tags for the current target * Determine (approximately) how many available session tags for the current target
* have been confirmed and are available * have been confirmed and are available
* *
*/ */
public int getAvailableTags(PublicKey target, SessionKey key) { return 0; } public int getAvailableTags(PublicKey target, SessionKey key) {
return 0;
}
/** /**
* Determine how long the available tags will be available for before expiring, in * Determine how long the available tags will be available for before expiring, in
* milliseconds * milliseconds
*/ */
public long getAvailableTimeLeft(PublicKey target, SessionKey key) { return 0; } public long getAvailableTimeLeft(PublicKey target, SessionKey key) {
return 0;
}
/** /**
* Take note of the fact that the given sessionTags associated with the key for * Take note of the fact that the given sessionTags associated with the key for
* encryption to the target have definitely been received at the target (aka call this * encryption to the target have definitely been received at the target (aka call this
* method after receiving an ack to a message delivering them) * method after receiving an ack to a message delivering them)
* *
*/ */
public void tagsDelivered(PublicKey target, SessionKey key, Set sessionTags) { } public void tagsDelivered(PublicKey target, SessionKey key, Set sessionTags) {
}
/** /**
* Mark all of the tags delivered to the target up to this point as invalid, since the peer * Mark all of the tags delivered to the target up to this point as invalid, since the peer
* has failed to respond when they should have. This call essentially lets the system recover * has failed to respond when they should have. This call essentially lets the system recover
* from corrupted tag sets and crashes * from corrupted tag sets and crashes
* *
*/ */
public void failTags(PublicKey target) {} public void failTags(PublicKey target) {
}
/** /**
* Accept the given tags and associate them with the given key for decryption * Accept the given tags and associate them with the given key for decryption
* *
*/ */
public void tagsReceived(SessionKey key, Set sessionTags) { } public void tagsReceived(SessionKey key, Set sessionTags) {
}
/** /**
* Determine if we have received a session key associated with the given session tag, * Determine if we have received a session key associated with the given session tag,
* and if so, discard it (but keep track for frequent dups) and return the decryption * and if so, discard it (but keep track for frequent dups) and return the decryption
@@ -100,12 +116,15 @@ public class SessionKeyManager {
* matches * matches
* *
*/ */
public SessionKey consumeTag(SessionTag tag) { return null; } public SessionKey consumeTag(SessionTag tag) {
return null;
}
/** /**
* Called when the system is closing down, instructing the session key manager to take * Called when the system is closing down, instructing the session key manager to take
* whatever precautions are necessary (saving state, etc) * whatever precautions are necessary (saving state, etc)
* *
*/ */
public void shutdown() {} public void shutdown() {
}
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.crypto; package net.i2p.crypto;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,22 +9,21 @@ package net.i2p.crypto;
* *
*/ */
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.i2p.data.DataHelper;
import net.i2p.data.PublicKey; import net.i2p.data.PublicKey;
import net.i2p.data.SessionKey; import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag; import net.i2p.data.SessionTag;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
import net.i2p.util.Clock; import net.i2p.util.Clock;
import net.i2p.util.Log;
import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Date;
/** /**
* Implement the session key management, but keep everything in memory (don't write * Implement the session key management, but keep everything in memory (don't write
@@ -35,94 +35,98 @@ class TransientSessionKeyManager extends SessionKeyManager {
private final static Log _log = new Log(TransientSessionKeyManager.class); private final static Log _log = new Log(TransientSessionKeyManager.class);
private Map _outboundSessions; // PublicKey --> OutboundSession private Map _outboundSessions; // PublicKey --> OutboundSession
private Map _inboundTagSets; // SessionTag --> TagSet private Map _inboundTagSets; // SessionTag --> TagSet
/** /**
* Let session tags sit around for 10 minutes before expiring them. We can now have such a large * Let session tags sit around for 10 minutes before expiring them. We can now have such a large
* value since there is the persistent session key manager. This value is for outbound tags - * value since there is the persistent session key manager. This value is for outbound tags -
* inbound tags are managed by SESSION_LIFETIME_MAX_MS * inbound tags are managed by SESSION_LIFETIME_MAX_MS
* *
*/ */
public final static long SESSION_TAG_DURATION_MS = 10*60*1000; public final static long SESSION_TAG_DURATION_MS = 10 * 60 * 1000;
/** /**
* Keep unused inbound session tags around for up to 15 minutes (5 minutes longer than * Keep unused inbound session tags around for up to 15 minutes (5 minutes longer than
* session tags are used on the outbound side so that no reasonable network lag * session tags are used on the outbound side so that no reasonable network lag
* can cause failed decrypts) * can cause failed decrypts)
* *
*/ */
public final static long SESSION_LIFETIME_MAX_MS = SESSION_TAG_DURATION_MS + 5*60*1000; public final static long SESSION_LIFETIME_MAX_MS = SESSION_TAG_DURATION_MS + 5 * 60 * 1000;
public final static int MAX_INBOUND_SESSION_TAGS = 100*1000; // this will consume at most 3.2M public final static int MAX_INBOUND_SESSION_TAGS = 100 * 1000; // this will consume at most 3.2M
public TransientSessionKeyManager() { public TransientSessionKeyManager() {
super(); super();
_outboundSessions = new HashMap(64); _outboundSessions = new HashMap(64);
_inboundTagSets = new HashMap(1024); _inboundTagSets = new HashMap(1024);
} }
/** TagSet */ /** TagSet */
protected Set getInboundTagSets() { protected Set getInboundTagSets() {
synchronized (_inboundTagSets) { synchronized (_inboundTagSets) {
return new HashSet(_inboundTagSets.values()); return new HashSet(_inboundTagSets.values());
} }
} }
/** OutboundSession */ /** OutboundSession */
protected Set getOutboundSessions() { protected Set getOutboundSessions() {
synchronized (_outboundSessions) { synchronized (_outboundSessions) {
return new HashSet(_outboundSessions.values()); return new HashSet(_outboundSessions.values());
} }
} }
protected void setData(Set inboundTagSets, Set outboundSessions) {
_log.info("Loading " + inboundTagSets.size() + " inbound tag sets, and " + outboundSessions.size() + " outbound sessions"); protected void setData(Set inboundTagSets, Set outboundSessions) {
Map tagSets = new HashMap(inboundTagSets.size()); _log.info("Loading " + inboundTagSets.size() + " inbound tag sets, and " + outboundSessions.size()
for (Iterator iter = inboundTagSets.iterator(); iter.hasNext(); ) { + " outbound sessions");
TagSet ts = (TagSet)iter.next(); Map tagSets = new HashMap(inboundTagSets.size());
for (Iterator tsIter = ts.getTags().iterator(); tsIter.hasNext(); ) { for (Iterator iter = inboundTagSets.iterator(); iter.hasNext();) {
SessionTag tag = (SessionTag)tsIter.next(); TagSet ts = (TagSet) iter.next();
tagSets.put(tag, ts); for (Iterator tsIter = ts.getTags().iterator(); tsIter.hasNext();) {
} SessionTag tag = (SessionTag) tsIter.next();
} tagSets.put(tag, ts);
synchronized (_inboundTagSets) { }
_inboundTagSets.clear(); }
_inboundTagSets.putAll(tagSets); synchronized (_inboundTagSets) {
} _inboundTagSets.clear();
Map sessions = new HashMap(outboundSessions.size()); _inboundTagSets.putAll(tagSets);
for (Iterator iter = outboundSessions.iterator(); iter.hasNext(); ) { }
OutboundSession sess = (OutboundSession)iter.next(); Map sessions = new HashMap(outboundSessions.size());
sessions.put(sess.getTarget(), sess); for (Iterator iter = outboundSessions.iterator(); iter.hasNext();) {
} OutboundSession sess = (OutboundSession) iter.next();
synchronized (_outboundSessions) { sessions.put(sess.getTarget(), sess);
_outboundSessions.clear(); }
_outboundSessions.putAll(sessions); synchronized (_outboundSessions) {
} _outboundSessions.clear();
_outboundSessions.putAll(sessions);
}
} }
/** /**
* Retrieve the session key currently associated with encryption to the target, * Retrieve the session key currently associated with encryption to the target,
* or null if a new session key should be generated. * or null if a new session key should be generated.
* *
*/ */
public SessionKey getCurrentKey(PublicKey target) { public SessionKey getCurrentKey(PublicKey target) {
OutboundSession sess = getSession(target); OutboundSession sess = getSession(target);
if (sess == null) return null; if (sess == null) return null;
long now = Clock.getInstance().now(); long now = Clock.getInstance().now();
if (sess.getEstablishedDate() < now - SESSION_LIFETIME_MAX_MS) { if (sess.getEstablishedDate() < now - SESSION_LIFETIME_MAX_MS) {
_log.info("Expiring old session key established on " + new Date(sess.getEstablishedDate()) + " with target " + target); _log.info("Expiring old session key established on " + new Date(sess.getEstablishedDate())
return null; + " with target " + target);
} else { return null;
return sess.getCurrentKey(); } else {
} return sess.getCurrentKey();
}
} }
/** /**
* Associate a new session key with the specified target. Metrics to determine * Associate a new session key with the specified target. Metrics to determine
* when to expire that key begin with this call. * when to expire that key begin with this call.
* *
*/ */
public void createSession(PublicKey target, SessionKey key) { public void createSession(PublicKey target, SessionKey key) {
OutboundSession sess = new OutboundSession(target); OutboundSession sess = new OutboundSession(target);
sess.setCurrentKey(key); sess.setCurrentKey(key);
addSession(sess); addSession(sess);
} }
/** /**
* Retrieve the next available session tag for identifying the use of the given * Retrieve the next available session tag for identifying the use of the given
* key when communicating with the target. If this returns null, no tags are * key when communicating with the target. If this returns null, no tags are
@@ -130,110 +134,103 @@ class TransientSessionKeyManager extends SessionKeyManager {
* NOT be used) * NOT be used)
* *
*/ */
public SessionTag consumeNextAvailableTag(PublicKey target, SessionKey key) { public SessionTag consumeNextAvailableTag(PublicKey target, SessionKey key) {
OutboundSession sess = getSession(target); OutboundSession sess = getSession(target);
if (sess == null) { if (sess == null) {
_log.debug("No session for " + target); _log.debug("No session for " + target);
return null; return null;
} }
if (sess.getCurrentKey().equals(key)) { if (sess.getCurrentKey().equals(key)) {
SessionTag nxt = sess.consumeNext(); SessionTag nxt = sess.consumeNext();
_log.debug("Tag consumed: " + nxt); _log.debug("Tag consumed: " + nxt);
return nxt; return nxt;
} else { } else {
_log.debug("Key does not match existing key, no tag"); _log.debug("Key does not match existing key, no tag");
return null; return null;
} }
} }
/** /**
* Determine (approximately) how many available session tags for the current target * Determine (approximately) how many available session tags for the current target
* have been confirmed and are available * have been confirmed and are available
* *
*/ */
public int getAvailableTags(PublicKey target, SessionKey key) { public int getAvailableTags(PublicKey target, SessionKey key) {
OutboundSession sess = getSession(target); OutboundSession sess = getSession(target);
if (sess == null) { if (sess == null) { return 0; }
return 0; if (sess.getCurrentKey().equals(key)) {
} return sess.availableTags();
if (sess.getCurrentKey().equals(key)) { } else {
return sess.availableTags(); return 0;
} else { }
return 0;
}
} }
/** /**
* Determine how long the available tags will be available for before expiring, in * Determine how long the available tags will be available for before expiring, in
* milliseconds * milliseconds
*/ */
public long getAvailableTimeLeft(PublicKey target, SessionKey key) { public long getAvailableTimeLeft(PublicKey target, SessionKey key) {
OutboundSession sess = getSession(target); OutboundSession sess = getSession(target);
if (sess == null) { if (sess == null) { return 0; }
return 0; if (sess.getCurrentKey().equals(key)) {
} return (sess.getLastExpirationDate() + SESSION_TAG_DURATION_MS) - Clock.getInstance().now();
if (sess.getCurrentKey().equals(key)) { } else {
return (sess.getLastExpirationDate() + SESSION_TAG_DURATION_MS) - Clock.getInstance().now(); return 0;
} else { }
return 0;
}
} }
/** /**
* Take note of the fact that the given sessionTags associated with the key for * Take note of the fact that the given sessionTags associated with the key for
* encryption to the target have definitely been received at the target (aka call this * encryption to the target have definitely been received at the target (aka call this
* method after receiving an ack to a message delivering them) * method after receiving an ack to a message delivering them)
* *
*/ */
public void tagsDelivered(PublicKey target, SessionKey key, Set sessionTags) { public void tagsDelivered(PublicKey target, SessionKey key, Set sessionTags) {
OutboundSession sess = getSession(target); OutboundSession sess = getSession(target);
if (sess == null) { if (sess == null) {
createSession(target, key); createSession(target, key);
sess = getSession(target); sess = getSession(target);
} }
sess.setCurrentKey(key); sess.setCurrentKey(key);
TagSet set = new TagSet(sessionTags, key); TagSet set = new TagSet(sessionTags, key);
sess.addTags(set); sess.addTags(set);
_log.debug("Tags delivered to set " + set + " on session " + sess); _log.debug("Tags delivered to set " + set + " on session " + sess);
} }
/** /**
* Mark all of the tags delivered to the target up to this point as invalid, since the peer * Mark all of the tags delivered to the target up to this point as invalid, since the peer
* has failed to respond when they should have. This call essentially lets the system recover * has failed to respond when they should have. This call essentially lets the system recover
* from corrupted tag sets and crashes * from corrupted tag sets and crashes
* *
*/ */
public void failTags(PublicKey target) { public void failTags(PublicKey target) {
removeSession(target); removeSession(target);
} }
/** /**
* Accept the given tags and associate them with the given key for decryption * Accept the given tags and associate them with the given key for decryption
* *
*/ */
public void tagsReceived(SessionKey key, Set sessionTags) { public void tagsReceived(SessionKey key, Set sessionTags) {
TagSet tagSet = new TagSet(sessionTags, key); TagSet tagSet = new TagSet(sessionTags, key);
for (Iterator iter = sessionTags.iterator(); iter.hasNext(); ) { for (Iterator iter = sessionTags.iterator(); iter.hasNext();) {
SessionTag tag = (SessionTag)iter.next(); SessionTag tag = (SessionTag) iter.next();
_log.debug("Receiving tag " + tag + " for key " + key); _log.debug("Receiving tag " + tag + " for key " + key);
synchronized (_inboundTagSets) { synchronized (_inboundTagSets) {
_inboundTagSets.put(tag, tagSet); _inboundTagSets.put(tag, tagSet);
} }
} }
synchronized (_inboundTagSets) { synchronized (_inboundTagSets) {
// todo: make this limit the tags by sessionKey and actually enforce the limit! // todo: make this limit the tags by sessionKey and actually enforce the limit!
int overage = _inboundTagSets.size() - MAX_INBOUND_SESSION_TAGS; int overage = _inboundTagSets.size() - MAX_INBOUND_SESSION_TAGS;
if (overage > 0) { if (overage > 0) {
_log.error("TOO MANY SESSION TAGS! " + (_inboundTagSets.size())); _log.error("TOO MANY SESSION TAGS! " + (_inboundTagSets.size()));
} }
} }
if (sessionTags.size() <= 0) if (sessionTags.size() <= 0) _log.debug("Received 0 tags for key " + key);
_log.debug("Received 0 tags for key " + key);
} }
/** /**
* Determine if we have received a session key associated with the given session tag, * Determine if we have received a session key associated with the given session tag,
* and if so, discard it (but keep track for frequent dups) and return the decryption * and if so, discard it (but keep track for frequent dups) and return the decryption
@@ -241,287 +238,320 @@ class TransientSessionKeyManager extends SessionKeyManager {
* matches * matches
* *
*/ */
public SessionKey consumeTag(SessionTag tag) { public SessionKey consumeTag(SessionTag tag) {
synchronized (_inboundTagSets) { synchronized (_inboundTagSets) {
TagSet tagSet = (TagSet)_inboundTagSets.remove(tag); TagSet tagSet = (TagSet) _inboundTagSets.remove(tag);
if (tagSet == null) { if (tagSet == null) {
_log.debug("Cannot consume tag " + tag + " as it is not known"); _log.debug("Cannot consume tag " + tag + " as it is not known");
return null; return null;
} else { } else {
tagSet.consume(tag); tagSet.consume(tag);
} }
SessionKey key = tagSet.getAssociatedKey(); SessionKey key = tagSet.getAssociatedKey();
_log.debug("Consuming tag " + tag + " for sessionKey " + key); _log.debug("Consuming tag " + tag + " for sessionKey " + key);
return key; return key;
} }
} }
private OutboundSession getSession(PublicKey target) { private OutboundSession getSession(PublicKey target) {
synchronized (_outboundSessions) { synchronized (_outboundSessions) {
return (OutboundSession)_outboundSessions.get(target); return (OutboundSession) _outboundSessions.get(target);
} }
} }
private void addSession(OutboundSession sess) { private void addSession(OutboundSession sess) {
synchronized (_outboundSessions) { synchronized (_outboundSessions) {
_outboundSessions.put(sess.getTarget(), sess); _outboundSessions.put(sess.getTarget(), sess);
} }
} }
private void removeSession(PublicKey target) { private void removeSession(PublicKey target) {
if (target == null) return; if (target == null) return;
synchronized (_outboundSessions) { synchronized (_outboundSessions) {
_outboundSessions.remove(target); _outboundSessions.remove(target);
} }
} }
/** /**
* Aggressively expire inbound tag sets and outbound sessions * Aggressively expire inbound tag sets and outbound sessions
* *
* @return number of tag sets expired * @return number of tag sets expired
*/ */
public int aggressiveExpire() { public int aggressiveExpire() {
int removed = 0; int removed = 0;
long now = Clock.getInstance().now(); long now = Clock.getInstance().now();
Set tagsToDrop = new HashSet(64); Set tagsToDrop = new HashSet(64);
synchronized (_inboundTagSets) { synchronized (_inboundTagSets) {
for (Iterator iter = _inboundTagSets.keySet().iterator(); iter.hasNext(); ) { for (Iterator iter = _inboundTagSets.keySet().iterator(); iter.hasNext();) {
SessionTag tag = (SessionTag)iter.next(); SessionTag tag = (SessionTag) iter.next();
TagSet ts = (TagSet)_inboundTagSets.get(tag); TagSet ts = (TagSet) _inboundTagSets.get(tag);
if (ts.getDate() < now - SESSION_LIFETIME_MAX_MS) { if (ts.getDate() < now - SESSION_LIFETIME_MAX_MS) {
tagsToDrop.add(tag); tagsToDrop.add(tag);
} }
} }
removed += tagsToDrop.size(); removed += tagsToDrop.size();
for (Iterator iter = tagsToDrop.iterator(); iter.hasNext(); ) for (Iterator iter = tagsToDrop.iterator(); iter.hasNext();)
_inboundTagSets.remove(iter.next()); _inboundTagSets.remove(iter.next());
} }
//_log.warn("Expiring tags: [" + tagsToDrop + "]"); //_log.warn("Expiring tags: [" + tagsToDrop + "]");
synchronized (_outboundSessions) {
Set sessionsToDrop = new HashSet(64);
for (Iterator iter = _outboundSessions.keySet().iterator(); iter.hasNext(); ) {
PublicKey key = (PublicKey)iter.next();
OutboundSession sess = (OutboundSession)_outboundSessions.get(key);
removed += sess.expireTags();
if (sess.getTagSets().size() <= 0)
sessionsToDrop.add(key);
}
for (Iterator iter = sessionsToDrop.iterator(); iter.hasNext(); )
_outboundSessions.remove(iter.next());
}
return removed;
}
public String renderStatusHTML() {
StringBuffer buf = new StringBuffer(1024);
buf.append("<h2>Inbound sessions</h2>");
buf.append("<table border=\"1\">");
Set inbound = getInboundTagSets();
Map inboundSets = new HashMap(inbound.size());
for (Iterator iter = inbound.iterator(); iter.hasNext(); ) {
TagSet ts = (TagSet)iter.next();
if (!inboundSets.containsKey(ts.getAssociatedKey()))
inboundSets.put(ts.getAssociatedKey(), new HashSet());
Set sets = (Set)inboundSets.get(ts.getAssociatedKey());
sets.add(ts);
}
for (Iterator iter = inboundSets.keySet().iterator(); iter.hasNext(); ) {
SessionKey skey = (SessionKey)iter.next();
Set sets = (Set)inboundSets.get(skey);
buf.append("<tr><td><b>Session key</b>: ").append(skey.toBase64()).append("</td>");
buf.append("<td><b># Sets:</b> ").append(sets.size()).append("</td></tr>");
buf.append("<tr><td colspan=\"2\"><ul>");
for (Iterator siter = sets.iterator(); siter.hasNext(); ) {
TagSet ts = (TagSet)siter.next();
buf.append("<li><b>Received on:</b> ").append(new Date(ts.getDate())).append(" with ").append(ts.getTags().size()).append(" tags remaining</li>");
}
buf.append("</ul></td></tr>");
}
buf.append("</table>");
buf.append("<h2><b>Outbound sessions</b></h2>");
buf.append("<table border=\"1\">");
Set outbound = getOutboundSessions();
for (Iterator iter = outbound.iterator(); iter.hasNext(); ) {
OutboundSession sess = (OutboundSession)iter.next();
buf.append("<tr><td><b>Target key:</b> ").append(sess.getTarget().toString()).append("<br />");
buf.append("<b>Established:</b> ").append(new Date(sess.getEstablishedDate())).append("<br />");
buf.append("<b>Last Used:</b> ").append(new Date(sess.getLastUsedDate())).append("<br />");
buf.append("<b># Sets:</b> ").append(sess.getTagSets().size()).append("</td></tr>");
buf.append("<tr><td><b>Session key:</b> ").append(sess.getCurrentKey().toBase64()).append("</td></tr>");
buf.append("<tr><td><ul>");
for (Iterator siter = sess.getTagSets().iterator(); siter.hasNext(); ) {
TagSet ts = (TagSet)siter.next();
buf.append("<li><b>Sent on:</b> ").append(new Date(ts.getDate())).append(" with ").append(ts.getTags().size()).append(" tags remaining</li>");
}
buf.append("</ul></td></tr>");
}
buf.append("</table>");
return buf.toString();
}
static class OutboundSession {
private PublicKey _target;
private SessionKey _currentKey;
private long _established;
private long _lastUsed;
private List _tagSets;
public OutboundSession(PublicKey target) {
this(target, null, Clock.getInstance().now(), Clock.getInstance().now(), new ArrayList());
}
OutboundSession(PublicKey target, SessionKey curKey, long established, long lastUsed, List tagSets) { synchronized (_outboundSessions) {
_target = target; Set sessionsToDrop = new HashSet(64);
_currentKey = curKey; for (Iterator iter = _outboundSessions.keySet().iterator(); iter.hasNext();) {
_established = established; PublicKey key = (PublicKey) iter.next();
_lastUsed = lastUsed; OutboundSession sess = (OutboundSession) _outboundSessions.get(key);
_tagSets = tagSets; removed += sess.expireTags();
} if (sess.getTagSets().size() <= 0) sessionsToDrop.add(key);
}
/** list of TagSet objects */ for (Iterator iter = sessionsToDrop.iterator(); iter.hasNext();)
List getTagSets() { _outboundSessions.remove(iter.next());
synchronized (_tagSets) { }
return new ArrayList(_tagSets); return removed;
}
}
public PublicKey getTarget() { return _target; }
public SessionKey getCurrentKey() { return _currentKey; }
public void setCurrentKey(SessionKey key) {
if (_currentKey != null) {
if (!_currentKey.equals(key)) {
int dropped = 0;
List sets = _tagSets;
_tagSets = new ArrayList();
for (int i = 0; i < sets.size(); i++) {
TagSet set = (TagSet)sets.get(i);
dropped += set.getTags().size();
}
_log.info("Rekeyed from " + _currentKey + " to " + key + ": dropping " + dropped + " session tags");
}
}
_currentKey = key;
}
public long getEstablishedDate() { return _established; }
public long getLastUsedDate() { return _lastUsed; }
/**
* Expire old tags, returning the number of tag sets removed
*/
public int expireTags() {
long now = Clock.getInstance().now();
Set toRemove = new HashSet(64);
synchronized (_tagSets) {
for (int i = 0; i < _tagSets.size(); i++) {
TagSet set = (TagSet)_tagSets.get(i);
if (set.getDate() + SESSION_TAG_DURATION_MS <= now) {
toRemove.add(set);
}
}
_tagSets.removeAll(toRemove);
}
return toRemove.size();
}
public SessionTag consumeNext() {
long now = Clock.getInstance().now();
synchronized (_tagSets) {
while (_tagSets.size() > 0) {
TagSet set = (TagSet)_tagSets.get(0);
if (set.getDate() + SESSION_TAG_DURATION_MS > now) {
SessionTag tag = set.consumeNext();
if (tag != null) return tag;
} else {
_log.info("TagSet from " + new Date(set.getDate()) + " expired");
}
_tagSets.remove(0);
}
}
return null;
}
public int availableTags() {
int tags = 0;
synchronized (_tagSets) {
for (int i = 0; i < _tagSets.size(); i++) {
TagSet set = (TagSet)_tagSets.get(i);
tags += set.getTags().size();
}
}
return tags;
}
/**
* Get the furthest away tag set expiration date - after which all of the
* tags will have expired
*
*/
public long getLastExpirationDate() {
long last = 0;
synchronized (_tagSets) {
for (Iterator iter = _tagSets.iterator(); iter.hasNext(); ) {
TagSet set = (TagSet)iter.next();
if (set.getDate() > last)
last = set.getDate();
}
}
return last + SESSION_TAG_DURATION_MS;
}
public void addTags(TagSet set) {
synchronized (_tagSets) {
_tagSets.add(set);
}
}
} }
public String renderStatusHTML() {
StringBuffer buf = new StringBuffer(1024);
buf.append("<h2>Inbound sessions</h2>");
buf.append("<table border=\"1\">");
Set inbound = getInboundTagSets();
Map inboundSets = new HashMap(inbound.size());
for (Iterator iter = inbound.iterator(); iter.hasNext();) {
TagSet ts = (TagSet) iter.next();
if (!inboundSets.containsKey(ts.getAssociatedKey())) inboundSets.put(ts.getAssociatedKey(), new HashSet());
Set sets = (Set) inboundSets.get(ts.getAssociatedKey());
sets.add(ts);
}
for (Iterator iter = inboundSets.keySet().iterator(); iter.hasNext();) {
SessionKey skey = (SessionKey) iter.next();
Set sets = (Set) inboundSets.get(skey);
buf.append("<tr><td><b>Session key</b>: ").append(skey.toBase64()).append("</td>");
buf.append("<td><b># Sets:</b> ").append(sets.size()).append("</td></tr>");
buf.append("<tr><td colspan=\"2\"><ul>");
for (Iterator siter = sets.iterator(); siter.hasNext();) {
TagSet ts = (TagSet) siter.next();
buf.append("<li><b>Received on:</b> ").append(new Date(ts.getDate())).append(" with ")
.append(ts.getTags().size()).append(" tags remaining</li>");
}
buf.append("</ul></td></tr>");
}
buf.append("</table>");
buf.append("<h2><b>Outbound sessions</b></h2>");
buf.append("<table border=\"1\">");
Set outbound = getOutboundSessions();
for (Iterator iter = outbound.iterator(); iter.hasNext();) {
OutboundSession sess = (OutboundSession) iter.next();
buf.append("<tr><td><b>Target key:</b> ").append(sess.getTarget().toString()).append("<br />");
buf.append("<b>Established:</b> ").append(new Date(sess.getEstablishedDate())).append("<br />");
buf.append("<b>Last Used:</b> ").append(new Date(sess.getLastUsedDate())).append("<br />");
buf.append("<b># Sets:</b> ").append(sess.getTagSets().size()).append("</td></tr>");
buf.append("<tr><td><b>Session key:</b> ").append(sess.getCurrentKey().toBase64()).append("</td></tr>");
buf.append("<tr><td><ul>");
for (Iterator siter = sess.getTagSets().iterator(); siter.hasNext();) {
TagSet ts = (TagSet) siter.next();
buf.append("<li><b>Sent on:</b> ").append(new Date(ts.getDate())).append(" with ").append(
ts.getTags()
.size())
.append(" tags remaining</li>");
}
buf.append("</ul></td></tr>");
}
buf.append("</table>");
return buf.toString();
}
static class OutboundSession {
private PublicKey _target;
private SessionKey _currentKey;
private long _established;
private long _lastUsed;
private List _tagSets;
public OutboundSession(PublicKey target) {
this(target, null, Clock.getInstance().now(), Clock.getInstance().now(), new ArrayList());
}
OutboundSession(PublicKey target, SessionKey curKey, long established, long lastUsed, List tagSets) {
_target = target;
_currentKey = curKey;
_established = established;
_lastUsed = lastUsed;
_tagSets = tagSets;
}
/** list of TagSet objects */
List getTagSets() {
synchronized (_tagSets) {
return new ArrayList(_tagSets);
}
}
public PublicKey getTarget() {
return _target;
}
public SessionKey getCurrentKey() {
return _currentKey;
}
public void setCurrentKey(SessionKey key) {
if (_currentKey != null) {
if (!_currentKey.equals(key)) {
int dropped = 0;
List sets = _tagSets;
_tagSets = new ArrayList();
for (int i = 0; i < sets.size(); i++) {
TagSet set = (TagSet) sets.get(i);
dropped += set.getTags().size();
}
_log.info("Rekeyed from " + _currentKey + " to " + key + ": dropping " + dropped + " session tags");
}
}
_currentKey = key;
}
public long getEstablishedDate() {
return _established;
}
public long getLastUsedDate() {
return _lastUsed;
}
/**
* Expire old tags, returning the number of tag sets removed
*/
public int expireTags() {
long now = Clock.getInstance().now();
Set toRemove = new HashSet(64);
synchronized (_tagSets) {
for (int i = 0; i < _tagSets.size(); i++) {
TagSet set = (TagSet) _tagSets.get(i);
if (set.getDate() + SESSION_TAG_DURATION_MS <= now) {
toRemove.add(set);
}
}
_tagSets.removeAll(toRemove);
}
return toRemove.size();
}
public SessionTag consumeNext() {
long now = Clock.getInstance().now();
synchronized (_tagSets) {
while (_tagSets.size() > 0) {
TagSet set = (TagSet) _tagSets.get(0);
if (set.getDate() + SESSION_TAG_DURATION_MS > now) {
SessionTag tag = set.consumeNext();
if (tag != null) return tag;
} else {
_log.info("TagSet from " + new Date(set.getDate()) + " expired");
}
_tagSets.remove(0);
}
}
return null;
}
public int availableTags() {
int tags = 0;
synchronized (_tagSets) {
for (int i = 0; i < _tagSets.size(); i++) {
TagSet set = (TagSet) _tagSets.get(i);
tags += set.getTags().size();
}
}
return tags;
}
/**
* Get the furthest away tag set expiration date - after which all of the
* tags will have expired
*
*/
public long getLastExpirationDate() {
long last = 0;
synchronized (_tagSets) {
for (Iterator iter = _tagSets.iterator(); iter.hasNext();) {
TagSet set = (TagSet) iter.next();
if (set.getDate() > last) last = set.getDate();
}
}
return last + SESSION_TAG_DURATION_MS;
}
public void addTags(TagSet set) {
synchronized (_tagSets) {
_tagSets.add(set);
}
}
}
static class TagSet { static class TagSet {
private Set _sessionTags; private Set _sessionTags;
private SessionKey _key; private SessionKey _key;
private long _date; private long _date;
public TagSet(Set tags, SessionKey key) {
if (key == null) public TagSet(Set tags, SessionKey key) {
throw new IllegalArgumentException("Missing key"); if (key == null) throw new IllegalArgumentException("Missing key");
if (tags == null) if (tags == null) throw new IllegalArgumentException("Missing tags");
throw new IllegalArgumentException("Missing tags"); _sessionTags = tags;
_sessionTags = tags; _key = key;
_key = key; _date = Clock.getInstance().now();
_date = Clock.getInstance().now(); }
}
public long getDate() { return _date; } public long getDate() {
void setDate(long when) { _date = when; } return _date;
public Set getTags() { return _sessionTags; } }
public SessionKey getAssociatedKey() { return _key; }
public boolean contains(SessionTag tag) { return _sessionTags.contains(tag); } void setDate(long when) {
public void consume(SessionTag tag) { _date = when;
if (contains(tag)) { }
_sessionTags.remove(tag);
} public Set getTags() {
} return _sessionTags;
public SessionTag consumeNext() { }
if (_sessionTags.size() <= 0) {
return null; public SessionKey getAssociatedKey() {
} else { return _key;
SessionTag first = (SessionTag)_sessionTags.iterator().next(); }
_sessionTags.remove(first);
return first; public boolean contains(SessionTag tag) {
} return _sessionTags.contains(tag);
} }
public int hashCode() {
long rv = 0; public void consume(SessionTag tag) {
if (_key != null) if (contains(tag)) {
rv = rv*7 + _key.hashCode(); _sessionTags.remove(tag);
rv = rv*7 + _date; }
if (_sessionTags != null) }
rv = rv*7 + DataHelper.hashCode(_sessionTags);
return (int)rv; public SessionTag consumeNext() {
} if (_sessionTags.size() <= 0) {
public boolean equals(Object o) { return null;
if ( (o == null) || !(o instanceof TagSet) ) return false; } else {
TagSet ts = (TagSet)o; SessionTag first = (SessionTag) _sessionTags.iterator().next();
return DataHelper.eq(ts.getAssociatedKey(), getAssociatedKey()) && _sessionTags.remove(first);
DataHelper.eq(ts.getTags(), getTags()) && return first;
ts.getDate() == getDate(); }
} }
public int hashCode() {
long rv = 0;
if (_key != null) rv = rv * 7 + _key.hashCode();
rv = rv * 7 + _date;
if (_sessionTags != null) rv = rv * 7 + DataHelper.hashCode(_sessionTags);
return (int) rv;
}
public boolean equals(Object o) {
if ((o == null) || !(o instanceof TagSet)) return false;
TagSet ts = (TagSet) o;
return DataHelper.eq(ts.getAssociatedKey(), getAssociatedKey()) && DataHelper.eq(ts.getTags(), getTags())
&& ts.getDate() == getDate();
}
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.crypto; package net.i2p.crypto;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -13,8 +14,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import net.i2p.util.Clock; import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.util.I2PThread; import net.i2p.util.I2PThread;
import net.i2p.util.Log;
import net.i2p.util.NativeBigInteger; import net.i2p.util.NativeBigInteger;
import net.i2p.util.RandomSource; import net.i2p.util.RandomSource;
@@ -43,152 +44,174 @@ class YKGenerator {
private static int CALC_DELAY = -1; private static int CALC_DELAY = -1;
private static volatile List _values = new ArrayList(50); // list of BigInteger[] values (y and k) private static volatile List _values = new ArrayList(50); // list of BigInteger[] values (y and k)
private static Thread _precalcThread = null; private static Thread _precalcThread = null;
public final static String PROP_YK_PRECALC_MIN = "crypto.yk.precalc.min"; public final static String PROP_YK_PRECALC_MIN = "crypto.yk.precalc.min";
public final static String PROP_YK_PRECALC_MAX = "crypto.yk.precalc.max"; public final static String PROP_YK_PRECALC_MAX = "crypto.yk.precalc.max";
public final static String PROP_YK_PRECALC_DELAY = "crypto.yk.precalc.delay"; public final static String PROP_YK_PRECALC_DELAY = "crypto.yk.precalc.delay";
public final static String DEFAULT_YK_PRECALC_MIN = "10"; public final static String DEFAULT_YK_PRECALC_MIN = "10";
public final static String DEFAULT_YK_PRECALC_MAX = "30"; public final static String DEFAULT_YK_PRECALC_MAX = "30";
public final static String DEFAULT_YK_PRECALC_DELAY = "10000"; public final static String DEFAULT_YK_PRECALC_DELAY = "10000";
/** check every 30 seconds whether we have less than the minimum */ /** check every 30 seconds whether we have less than the minimum */
private final static long CHECK_DELAY = 30*1000; private final static long CHECK_DELAY = 30 * 1000;
static { static {
try { try {
int val = Integer.parseInt(System.getProperty(PROP_YK_PRECALC_MIN, DEFAULT_YK_PRECALC_MIN)); int val = Integer.parseInt(System.getProperty(PROP_YK_PRECALC_MIN, DEFAULT_YK_PRECALC_MIN));
MIN_NUM_BUILDERS = val; MIN_NUM_BUILDERS = val;
} catch (Throwable t) { } catch (Throwable t) {
int val = Integer.parseInt(DEFAULT_YK_PRECALC_MIN); int val = Integer.parseInt(DEFAULT_YK_PRECALC_MIN);
MIN_NUM_BUILDERS = val; MIN_NUM_BUILDERS = val;
} }
try { try {
int val = Integer.parseInt(System.getProperty(PROP_YK_PRECALC_MAX, DEFAULT_YK_PRECALC_MAX)); int val = Integer.parseInt(System.getProperty(PROP_YK_PRECALC_MAX, DEFAULT_YK_PRECALC_MAX));
MAX_NUM_BUILDERS = val; MAX_NUM_BUILDERS = val;
} catch (Throwable t) { } catch (Throwable t) {
int val = Integer.parseInt(DEFAULT_YK_PRECALC_MAX); int val = Integer.parseInt(DEFAULT_YK_PRECALC_MAX);
MAX_NUM_BUILDERS = val; MAX_NUM_BUILDERS = val;
} }
try { try {
int val = Integer.parseInt(System.getProperty(PROP_YK_PRECALC_DELAY, DEFAULT_YK_PRECALC_DELAY)); int val = Integer.parseInt(System.getProperty(PROP_YK_PRECALC_DELAY, DEFAULT_YK_PRECALC_DELAY));
CALC_DELAY = val; CALC_DELAY = val;
} catch (Throwable t) { } catch (Throwable t) {
int val = Integer.parseInt(DEFAULT_YK_PRECALC_DELAY); int val = Integer.parseInt(DEFAULT_YK_PRECALC_DELAY);
CALC_DELAY = val; CALC_DELAY = val;
} }
if (_log.shouldLog(Log.DEBUG)) _log.debug("ElGamal YK Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: " + CALC_DELAY + ")"); if (_log.shouldLog(Log.DEBUG))
_log.debug("ElGamal YK Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: "
_precalcThread = new I2PThread(new YKPrecalcRunner(MIN_NUM_BUILDERS, MAX_NUM_BUILDERS)); + CALC_DELAY + ")");
_precalcThread.setName("YK Precalc");
_precalcThread.setDaemon(true); _precalcThread = new I2PThread(new YKPrecalcRunner(MIN_NUM_BUILDERS, MAX_NUM_BUILDERS));
_precalcThread.setPriority(Thread.MIN_PRIORITY); _precalcThread.setName("YK Precalc");
_precalcThread.start(); _precalcThread.setDaemon(true);
_precalcThread.setPriority(Thread.MIN_PRIORITY);
_precalcThread.start();
} }
private static final int getSize() { synchronized (_values) { return _values.size(); } } private static final int getSize() {
synchronized (_values) {
return _values.size();
}
}
private static final int addValues(BigInteger yk[]) { private static final int addValues(BigInteger yk[]) {
int sz = 0; int sz = 0;
synchronized (_values) { synchronized (_values) {
_values.add(yk); _values.add(yk);
sz = _values.size(); sz = _values.size();
} }
return sz; return sz;
} }
public static BigInteger[] getNextYK() { public static BigInteger[] getNextYK() {
if (true) { if (true) {
synchronized (_values) { synchronized (_values) {
if (_values.size() > 0) { if (_values.size() > 0) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Sufficient precalculated YK values - fetch the existing"); if (_log.shouldLog(Log.DEBUG))
return (BigInteger[])_values.remove(0); _log.debug("Sufficient precalculated YK values - fetch the existing");
} return (BigInteger[]) _values.remove(0);
} }
} }
if (_log.shouldLog(Log.INFO)) _log.info("Insufficient precalculated YK values - create a new one"); }
return generateYK(); if (_log.shouldLog(Log.INFO)) _log.info("Insufficient precalculated YK values - create a new one");
return generateYK();
} }
private final static BigInteger _two = new NativeBigInteger(1, new byte[] { 0x02 } ); private final static BigInteger _two = new NativeBigInteger(1, new byte[] { 0x02});
private static final BigInteger[] generateYK() { private static final BigInteger[] generateYK() {
NativeBigInteger k = null; NativeBigInteger k = null;
BigInteger y = null; BigInteger y = null;
long t0 = 0; long t0 = 0;
long t1 = 0; long t1 = 0;
while (k == null) { while (k == null) {
t0 = Clock.getInstance().now(); t0 = Clock.getInstance().now();
k = new NativeBigInteger(2048, RandomSource.getInstance()); k = new NativeBigInteger(2048, RandomSource.getInstance());
t1 = Clock.getInstance().now(); t1 = Clock.getInstance().now();
if (BigInteger.ZERO.compareTo(k) == 0) { if (BigInteger.ZERO.compareTo(k) == 0) {
k = null; k = null;
continue; continue;
} }
BigInteger kPlus2 = k.add(_two); BigInteger kPlus2 = k.add(_two);
if (kPlus2.compareTo(CryptoConstants.elgp) > 0) if (kPlus2.compareTo(CryptoConstants.elgp) > 0) k = null;
k = null; }
} long t2 = Clock.getInstance().now();
long t2 = Clock.getInstance().now(); y = CryptoConstants.elgg.modPow(k, CryptoConstants.elgp);
y = CryptoConstants.elgg.modPow(k, CryptoConstants.elgp);
BigInteger yk[] = new BigInteger[2];
BigInteger yk[] = new BigInteger[2]; yk[0] = y;
yk[0] = y; yk[1] = k;
yk[1] = k;
long diff = t2 - t0;
long diff = t2 - t0; if (diff > 1000) {
if (diff > 1000) { if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to generate YK value for ElGamal (" + diff + "ms)");
if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to generate YK value for ElGamal (" + diff + "ms)"); }
}
return yk;
return yk;
} }
public static void main(String args[]) { public static void main(String args[]) {
RandomSource.getInstance().nextBoolean(); // warm it up RandomSource.getInstance().nextBoolean(); // warm it up
try { Thread.sleep(20*1000); } catch (InterruptedException ie) {} try {
_log.debug("\n\n\n\nBegin test\n"); Thread.sleep(20 * 1000);
long negTime = 0; } catch (InterruptedException ie) {
for (int i = 0; i < 5; i++) { }
long startNeg = Clock.getInstance().now(); _log.debug("\n\n\n\nBegin test\n");
getNextYK(); long negTime = 0;
long endNeg = Clock.getInstance().now(); for (int i = 0; i < 5; i++) {
} long startNeg = Clock.getInstance().now();
_log.debug("YK fetch time for 5 runs: " + negTime + " @ " + negTime/5l + "ms each"); getNextYK();
try { Thread.sleep(30*1000); } catch (InterruptedException ie) {} long endNeg = Clock.getInstance().now();
}
_log.debug("YK fetch time for 5 runs: " + negTime + " @ " + negTime / 5l + "ms each");
try {
Thread.sleep(30 * 1000);
} catch (InterruptedException ie) {
}
} }
private static class YKPrecalcRunner implements Runnable { private static class YKPrecalcRunner implements Runnable {
private int _minSize; private int _minSize;
private int _maxSize; private int _maxSize;
private YKPrecalcRunner(int minSize, int maxSize) {
_minSize = minSize; private YKPrecalcRunner(int minSize, int maxSize) {
_maxSize = maxSize; _minSize = minSize;
} _maxSize = maxSize;
public void run() { }
while (true) {
int curSize = 0; public void run() {
long start = Clock.getInstance().now(); while (true) {
int startSize = getSize(); int curSize = 0;
curSize = startSize; long start = Clock.getInstance().now();
while (curSize < _minSize) { int startSize = getSize();
while (curSize < _maxSize) { curSize = startSize;
long begin = Clock.getInstance().now(); while (curSize < _minSize) {
curSize = addValues(generateYK()); while (curSize < _maxSize) {
long end = Clock.getInstance().now(); long begin = Clock.getInstance().now();
if (_log.shouldLog(Log.DEBUG)) _log.debug("Precalculated YK value in " + (end-begin) + "ms"); curSize = addValues(generateYK());
// for some relief... long end = Clock.getInstance().now();
try { Thread.sleep(CALC_DELAY); } catch (InterruptedException ie) {} if (_log.shouldLog(Log.DEBUG)) _log.debug("Precalculated YK value in " + (end - begin) + "ms");
} // for some relief...
} try {
long end = Clock.getInstance().now(); Thread.sleep(CALC_DELAY);
int numCalc = curSize - startSize; } catch (InterruptedException ie) {
if (numCalc > 0) { }
if (_log.shouldLog(Log.DEBUG)) _log.debug("Precalced " + numCalc + " to " + curSize + " in " + (end-start-CALC_DELAY*numCalc) + "ms (not counting " + (CALC_DELAY*numCalc) + "ms relief). now sleeping"); }
} }
try { Thread.sleep(CHECK_DELAY); } catch (InterruptedException ie) {} long end = Clock.getInstance().now();
} int numCalc = curSize - startSize;
} if (numCalc > 0) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Precalced " + numCalc + " to " + curSize + " in "
+ (end - start - CALC_DELAY * numCalc) + "ms (not counting "
+ (CALC_DELAY * numCalc) + "ms relief). now sleeping");
}
try {
Thread.sleep(CHECK_DELAY);
} catch (InterruptedException ie) {
}
}
}
} }
} }

View File

@@ -36,177 +36,168 @@ import java.io.OutputStream;
* @author rob@iharder.net * @author rob@iharder.net
* @version 1.3.4 * @version 1.3.4
*/ */
public class Base64 public class Base64 {
{ public static String encode(byte[] source) {
public static String encode(byte[] source) { return safeEncode(source); } return safeEncode(source);
public static byte[] decode(String s) { return safeDecode(s); } }
public static byte[] decode(String s) {
return safeDecode(s);
}
/** Maximum line length (76) of Base64 output. */ /** Maximum line length (76) of Base64 output. */
private final static int MAX_LINE_LENGTH = 76; private final static int MAX_LINE_LENGTH = 76;
/** The equals sign (=) as a byte. */ /** The equals sign (=) as a byte. */
private final static byte EQUALS_SIGN = (byte)'='; private final static byte EQUALS_SIGN = (byte) '=';
/** The new line character (\n) as a byte. */ /** The new line character (\n) as a byte. */
private final static byte NEW_LINE = (byte)'\n'; private final static byte NEW_LINE = (byte) '\n';
/** The 64 valid Base64 values. */ /** The 64 valid Base64 values. */
private final static byte[] ALPHABET = private final static byte[] ALPHABET = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
{ (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
(byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R',
(byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X',
(byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd',
(byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
(byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p',
(byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v',
(byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1',
(byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
(byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte) '8', (byte) '9', (byte) '+', (byte) '/'};
(byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
};
/** /**
* Translates a Base64 value to either its 6-bit reconstruction value * Translates a Base64 value to either its 6-bit reconstruction value
* or a negative number indicating some other meaning. * or a negative number indicating some other meaning.
**/ **/
private final static byte[] DECODABET = private final static byte[] DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
{ -5, -5, // Whitespace: Tab and Linefeed
-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 -9, -9, // Decimal 11 - 12
-5,-5, // Whitespace: Tab and Linefeed -5, // Whitespace: Carriage Return
-9,-9, // Decimal 11 - 12 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
-5, // Whitespace: Carriage Return -9, -9, -9, -9, -9, // Decimal 27 - 31
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 -5, // Whitespace: Space
-9,-9,-9,-9,-9, // Decimal 27 - 31 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
-5, // Whitespace: Space 62, // Plus sign at decimal 43
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 -9, -9, -9, // Decimal 44 - 46
62, // Plus sign at decimal 43 63, // Slash at decimal 47
-9,-9,-9, // Decimal 44 - 46 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
63, // Slash at decimal 47 -9, -9, -9, // Decimal 58 - 60
52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine -1, // Equals sign at decimal 61
-9,-9,-9, // Decimal 58 - 60 -9, -9, -9, // Decimal 62 - 64
-1, // Equals sign at decimal 61 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
-9,-9,-9, // Decimal 62 - 64 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
-9,-9,-9,-9,-9,-9, // Decimal 91 - 96 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' -9, -9, -9, -9 // Decimal 123 - 126
39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
-9,-9,-9,-9 // Decimal 123 - 126 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
/*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
}; };
private final static byte BAD_ENCODING = -9; // Indicates error in encoding private final static byte BAD_ENCODING = -9; // Indicates error in encoding
private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
/** Defeats instantiation. */ /** Defeats instantiation. */
private Base64(){} private Base64() {
public static void main(String[] args)
{
if (args.length == 0) {
help();
return;
} }
runApp(args);
public static void main(String[] args) {
if (args.length == 0) {
help();
return;
}
runApp(args);
} }
private static void runApp(String args[]) { private static void runApp(String args[]) {
try { try {
InputStream in = System.in; InputStream in = System.in;
OutputStream out = System.out; OutputStream out = System.out;
if (args.length >= 3) { if (args.length >= 3) {
out = new FileOutputStream(args[2]); out = new FileOutputStream(args[2]);
} }
if (args.length >= 2) { if (args.length >= 2) {
in = new FileInputStream(args[1]); in = new FileInputStream(args[1]);
} }
if ("encode".equalsIgnoreCase(args[0])) { if ("encode".equalsIgnoreCase(args[0])) {
encode(in, out); encode(in, out);
return; return;
} }
if ("decode".equalsIgnoreCase(args[0])) { if ("decode".equalsIgnoreCase(args[0])) {
decode(in, out); decode(in, out);
return; return;
} }
} catch (IOException ioe) { } catch (IOException ioe) {
ioe.printStackTrace(System.err); ioe.printStackTrace(System.err);
} }
} }
private static byte[] read(InputStream in) throws IOException { private static byte[] read(InputStream in) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(4096); ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
byte buf[] = new byte[4096]; byte buf[] = new byte[4096];
while (true) { while (true) {
int read = in.read(buf); int read = in.read(buf);
if (read < 0) if (read < 0) break;
break; baos.write(buf, 0, read);
baos.write(buf, 0, read); }
} return baos.toByteArray();
return baos.toByteArray();
} }
private static void encode(InputStream in, OutputStream out) throws IOException { private static void encode(InputStream in, OutputStream out) throws IOException {
String encoded = encode(read(in)); String encoded = encode(read(in));
out.write(encoded.getBytes()); out.write(encoded.getBytes());
} }
private static void decode(InputStream in, OutputStream out) throws IOException { private static void decode(InputStream in, OutputStream out) throws IOException {
byte decoded[] = decode(new String(read(in))); byte decoded[] = decode(new String(read(in)));
out.write(decoded); out.write(decoded);
} }
private static void help() { private static void help() {
System.out.println("Syntax: Base64 encode <inFile> <outFile>"); System.out.println("Syntax: Base64 encode <inFile> <outFile>");
System.out.println("or : Base64 encode <inFile>"); System.out.println("or : Base64 encode <inFile>");
System.out.println("or : Base64 encode"); System.out.println("or : Base64 encode");
System.out.println("or : Base64 decode <inFile> <outFile>"); System.out.println("or : Base64 decode <inFile> <outFile>");
System.out.println("or : Base64 decode <inFile>"); System.out.println("or : Base64 decode <inFile>");
System.out.println("or : Base64 decode"); System.out.println("or : Base64 decode");
System.out.println("or : Base64 test"); System.out.println("or : Base64 test");
} }
private static void test() { private static void test() {
String orig = "you smell"; String orig = "you smell";
String encoded = Base64.encode(orig.getBytes()); String encoded = Base64.encode(orig.getBytes());
System.out.println("Encoded: [" + encoded + "]"); System.out.println("Encoded: [" + encoded + "]");
byte decoded[] = Base64.decode(encoded); byte decoded[] = Base64.decode(encoded);
String transformed = new String(decoded); String transformed = new String(decoded);
if (orig.equals(transformed)) if (orig.equals(transformed))
System.out.println("D(E('you smell')) == 'you smell'"); System.out.println("D(E('you smell')) == 'you smell'");
else else
throw new RuntimeException("D(E('you smell')) != 'you smell'!!! transformed = [" + transformed + "]"); throw new RuntimeException("D(E('you smell')) != 'you smell'!!! transformed = [" + transformed + "]");
byte all[] = new byte[256]; byte all[] = new byte[256];
for (int i = 0; i < all.length; i++) for (int i = 0; i < all.length; i++)
all[i] = (byte)(0xFF & i); all[i] = (byte) (0xFF & i);
encoded = Base64.encode(all); encoded = Base64.encode(all);
System.out.println("Encoded: [" + encoded + "]"); System.out.println("Encoded: [" + encoded + "]");
decoded = Base64.decode(encoded); decoded = Base64.decode(encoded);
if (DataHelper.eq(decoded, all)) if (DataHelper.eq(decoded, all))
System.out.println("D(E([all bytes])) == [all bytes]"); System.out.println("D(E([all bytes])) == [all bytes]");
else else
throw new RuntimeException("D(E([all bytes])) != [all bytes]!!!"); throw new RuntimeException("D(E([all bytes])) != [all bytes]!!!");
} }
/* ******** E N C O D I N G M E T H O D S ******** */
/* ******** E N C O D I N G M E T H O D S ******** */
/** /**
* Encodes the first three bytes of array <var>threeBytes</var> * Encodes the first three bytes of array <var>threeBytes</var>
* and returns a four-byte array in Base64 notation. * and returns a four-byte array in Base64 notation.
@@ -215,12 +206,10 @@ public class Base64
* @return four byte array in Base64 notation. * @return four byte array in Base64 notation.
* @since 1.3 * @since 1.3
*/ */
private static byte[] encode3to4( byte[] threeBytes ) private static byte[] encode3to4(byte[] threeBytes) {
{ return encode3to4( threeBytes, 3 ); return encode3to4(threeBytes, 3);
} // end encodeToBytes } // end encodeToBytes
/** /**
* Encodes up to the first three bytes of array <var>threeBytes</var> * Encodes up to the first three bytes of array <var>threeBytes</var>
* and returns a four-byte array in Base64 notation. * and returns a four-byte array in Base64 notation.
@@ -234,14 +223,12 @@ public class Base64
* @return four byte array in Base64 notation. * @return four byte array in Base64 notation.
* @since 1.3 * @since 1.3
*/ */
private static byte[] encode3to4( byte[] threeBytes, int numSigBytes ) private static byte[] encode3to4(byte[] threeBytes, int numSigBytes) {
{ byte[] dest = new byte[4]; byte[] dest = new byte[4];
encode3to4( threeBytes, 0, numSigBytes, dest, 0 ); encode3to4(threeBytes, 0, numSigBytes, dest, 0);
return dest; return dest;
} }
/** /**
* Encodes up to three bytes of the array <var>source</var> * Encodes up to three bytes of the array <var>source</var>
* and writes the resulting four Base64 bytes to <var>destination</var>. * and writes the resulting four Base64 bytes to <var>destination</var>.
@@ -263,53 +250,49 @@ public class Base64
* @return the <var>destination</var> array * @return the <var>destination</var> array
* @since 1.3 * @since 1.3
*/ */
private static byte[] encode3to4( private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes, byte[] destination, int destOffset) {
byte[] source, int srcOffset, int numSigBytes,
byte[] destination, int destOffset )
{
// 1 2 3 // 1 2 3
// 01234567890123456789012345678901 Bit position // 01234567890123456789012345678901 Bit position
// --------000000001111111122222222 Array position from threeBytes // --------000000001111111122222222 Array position from threeBytes
// --------| || || || | Six bit groups to index ALPHABET // --------| || || || | Six bit groups to index ALPHABET
// >>18 >>12 >> 6 >> 0 Right shift necessary // >>18 >>12 >> 6 >> 0 Right shift necessary
// 0x3f 0x3f 0x3f Additional AND // 0x3f 0x3f 0x3f Additional AND
// Create buffer with zero-padding if there are only one or two // Create buffer with zero-padding if there are only one or two
// significant bytes passed in the array. // significant bytes passed in the array.
// We have to shift left 24 in order to flush out the 1's that appear // We have to shift left 24 in order to flush out the 1's that appear
// when Java treats a value as negative that is cast from a byte to an int. // when Java treats a value as negative that is cast from a byte to an int.
int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
| ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
| ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
switch (numSigBytes) {
case 3:
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
return destination;
case 2:
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
destination[destOffset + 3] = EQUALS_SIGN;
return destination;
case 1:
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = EQUALS_SIGN;
destination[destOffset + 3] = EQUALS_SIGN;
return destination;
default:
return destination;
} // end switch
} // end encode3to4
switch( numSigBytes )
{
case 3:
destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ];
return destination;
case 2:
destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
destination[ destOffset + 3 ] = EQUALS_SIGN;
return destination;
case 1:
destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
destination[ destOffset + 2 ] = EQUALS_SIGN;
destination[ destOffset + 3 ] = EQUALS_SIGN;
return destination;
default:
return destination;
} // end switch
} // end encode3to4
/** /**
* Encodes a byte array into Base64 notation. * Encodes a byte array into Base64 notation.
* Equivalen to calling * Equivalen to calling
@@ -318,33 +301,31 @@ public class Base64
* @param source The data to convert * @param source The data to convert
* @since 1.4 * @since 1.4
*/ */
private static String encodeBytes( byte[] source ) private static String encodeBytes(byte[] source) {
{ return encodeBytes(source, false); // don't add newlines
return encodeBytes( source, false ); // don't add newlines } // end encodeBytes
} // end encodeBytes
/** /**
* Same as encodeBytes, except uses a filesystem / URL friendly set of characters, * Same as encodeBytes, except uses a filesystem / URL friendly set of characters,
* replacing / with ~, and + with - * replacing / with ~, and + with -
*/ */
private static String safeEncode(byte[] source) { private static String safeEncode(byte[] source) {
String encoded = encodeBytes(source); String encoded = encodeBytes(source);
encoded = encoded.replace('/', '~'); encoded = encoded.replace('/', '~');
encoded = encoded.replace('+', '-'); encoded = encoded.replace('+', '-');
return encoded; return encoded;
} }
/** /**
* Same as decode, except from a filesystem / URL friendly set of characters, * Same as decode, except from a filesystem / URL friendly set of characters,
* replacing / with ~, and + with - * replacing / with ~, and + with -
*/ */
private static byte[] safeDecode(String source) { private static byte[] safeDecode(String source) {
String toDecode = source.replace('~', '/'); String toDecode = source.replace('~', '/');
toDecode = toDecode.replace('-', '+'); toDecode = toDecode.replace('-', '+');
return standardDecode(toDecode); return standardDecode(toDecode);
} }
/** /**
* Encodes a byte array into Base64 notation. * Encodes a byte array into Base64 notation.
* Equivalen to calling * Equivalen to calling
@@ -354,12 +335,10 @@ public class Base64
* @param breakLines Break lines at 80 characters or less. * @param breakLines Break lines at 80 characters or less.
* @since 1.4 * @since 1.4
*/ */
private static String encodeBytes( byte[] source, boolean breakLines ) private static String encodeBytes(byte[] source, boolean breakLines) {
{ return encodeBytes(source, 0, source.length, breakLines);
return encodeBytes( source, 0, source.length, breakLines ); } // end encodeBytes
} // end encodeBytes
/** /**
* Encodes a byte array into Base64 notation. * Encodes a byte array into Base64 notation.
* *
@@ -368,12 +347,10 @@ public class Base64
* @param len Length of data to convert * @param len Length of data to convert
* @since 1.4 * @since 1.4
*/ */
private static String encodeBytes( byte[] source, int off, int len ) private static String encodeBytes(byte[] source, int off, int len) {
{ return encodeBytes(source, off, len, true);
return encodeBytes( source, off, len, true ); } // end encodeBytes
} // end encodeBytes
/** /**
* Encodes a byte array into Base64 notation. * Encodes a byte array into Base64 notation.
* *
@@ -383,39 +360,34 @@ public class Base64
* @param breakLines Break lines at 80 characters or less. * @param breakLines Break lines at 80 characters or less.
* @since 1.4 * @since 1.4
*/ */
private static String encodeBytes( byte[] source, int off, int len, boolean breakLines ) private static String encodeBytes(byte[] source, int off, int len, boolean breakLines) {
{ int len43 = len * 4 / 3;
int len43 = len * 4 / 3; byte[] outBuff = new byte[(len43) // Main 4:3
byte[] outBuff = new byte[ ( len43 ) // Main 4:3 + ((len % 3) > 0 ? 4 : 0) // Account for padding
+ ( (len % 3) > 0 ? 4 : 0 ) // Account for padding + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines
+ (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
int d = 0; int d = 0;
int e = 0; int e = 0;
int len2 = len - 2; int len2 = len - 2;
int lineLength = 0; int lineLength = 0;
for( ; d < len2; d+=3, e+=4 ) for (; d < len2; d += 3, e += 4) {
{ encode3to4(source, d + off, 3, outBuff, e);
encode3to4( source, d+off, 3, outBuff, e );
lineLength += 4; lineLength += 4;
if( breakLines && lineLength == MAX_LINE_LENGTH ) if (breakLines && lineLength == MAX_LINE_LENGTH) {
{ outBuff[e + 4] = NEW_LINE;
outBuff[e+4] = NEW_LINE;
e++; e++;
lineLength = 0; lineLength = 0;
} // end if: end of line } // end if: end of line
} // en dfor: each piece of array } // en dfor: each piece of array
if( d < len ) if (d < len) {
{ encode3to4(source, d + off, len - d, outBuff, e);
encode3to4( source, d+off, len - d, outBuff, e );
e += 4; e += 4;
} // end if: some padding needed } // end if: some padding needed
return new String( outBuff, 0, e ); return new String(outBuff, 0, e);
} // end encodeBytes } // end encodeBytes
/** /**
* Encodes a string in Base64 notation with line breaks * Encodes a string in Base64 notation with line breaks
* after every 75 Base64 characters. * after every 75 Base64 characters.
@@ -424,11 +396,10 @@ public class Base64
* @return the encoded string * @return the encoded string
* @since 1.3 * @since 1.3
*/ */
private static String encodeString( String s ) private static String encodeString(String s) {
{ return encodeString(s, true);
return encodeString( s, true ); } // end encodeString
} // end encodeString
/** /**
* Encodes a string in Base64 notation with line breaks * Encodes a string in Base64 notation with line breaks
* after every 75 Base64 characters. * after every 75 Base64 characters.
@@ -438,17 +409,12 @@ public class Base64
* @return the encoded string * @return the encoded string
* @since 1.3 * @since 1.3
*/ */
private static String encodeString( String s, boolean breakLines ) private static String encodeString(String s, boolean breakLines) {
{ return encodeBytes(s.getBytes(), breakLines);
return encodeBytes( s.getBytes(), breakLines ); } // end encodeString
} // end encodeString
/* ******** D E C O D I N G M E T H O D S ******** */
/* ******** D E C O D I N G M E T H O D S ******** */
/** /**
* Decodes the first four bytes of array <var>fourBytes</var> * Decodes the first four bytes of array <var>fourBytes</var>
* and returns an array up to three bytes long with the * and returns an array up to three bytes long with the
@@ -458,21 +424,17 @@ public class Base64
* @return array with decoded values * @return array with decoded values
* @since 1.3 * @since 1.3
*/ */
private static byte[] decode4to3( byte[] fourBytes ) private static byte[] decode4to3(byte[] fourBytes) {
{
byte[] outBuff1 = new byte[3]; byte[] outBuff1 = new byte[3];
int count = decode4to3( fourBytes, 0, outBuff1, 0 ); int count = decode4to3(fourBytes, 0, outBuff1, 0);
byte[] outBuff2 = new byte[ count ]; byte[] outBuff2 = new byte[count];
for( int i = 0; i < count; i++ ) for (int i = 0; i < count; i++)
outBuff2[i] = outBuff1[i]; outBuff2[i] = outBuff1[i];
return outBuff2; return outBuff2;
} }
/** /**
* Decodes four bytes from array <var>source</var> * Decodes four bytes from array <var>source</var>
* and writes the resulting bytes (up to three of them) * and writes the resulting bytes (up to three of them)
@@ -495,67 +457,62 @@ public class Base64
* @return the number of decoded bytes converted * @return the number of decoded bytes converted
* @since 1.3 * @since 1.3
*/ */
private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset ) private static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset) {
{
// Example: Dk== // Example: Dk==
if( source[ srcOffset + 2] == EQUALS_SIGN ) if (source[srcOffset + 2] == EQUALS_SIGN) {
{
// Two ways to do the same thing. Don't know which way I like best. // Two ways to do the same thing. Don't know which way I like best.
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
// | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
| ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
destination[ destOffset ] = (byte)( outBuff >>> 16 ); destination[destOffset] = (byte) (outBuff >>> 16);
return 1; return 1;
} }
// Example: DkL= // Example: DkL=
else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) else if (source[srcOffset + 3] == EQUALS_SIGN) {
{
// Two ways to do the same thing. Don't know which way I like best. // Two ways to do the same thing. Don't know which way I like best.
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
| ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
| ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
destination[ destOffset ] = (byte)( outBuff >>> 16 ); destination[destOffset] = (byte) (outBuff >>> 16);
destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); destination[destOffset + 1] = (byte) (outBuff >>> 8);
return 2; return 2;
} }
// Example: DkLE // Example: DkLE
else else {
{ try {
try{ // Two ways to do the same thing. Don't know which way I like best.
// Two ways to do the same thing. Don't know which way I like best. //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
// | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
// | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
// | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
| ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6)
| ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) | ((DECODABET[source[srcOffset + 3]] & 0xFF));
| ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) );
destination[destOffset] = (byte) (outBuff >> 16);
destination[ destOffset ] = (byte)( outBuff >> 16 ); destination[destOffset + 1] = (byte) (outBuff >> 8);
destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); destination[destOffset + 2] = (byte) (outBuff);
destination[ destOffset + 2 ] = (byte)( outBuff );
return 3; return 3;
}catch( Exception e){ } catch (Exception e) {
System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) ); System.out.println("" + source[srcOffset] + ": " + (DECODABET[source[srcOffset]]));
System.out.println(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) ); System.out.println("" + source[srcOffset + 1] + ": " + (DECODABET[source[srcOffset + 1]]));
System.out.println(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) ); System.out.println("" + source[srcOffset + 2] + ": " + (DECODABET[source[srcOffset + 2]]));
System.out.println(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) ); System.out.println("" + source[srcOffset + 3] + ": " + (DECODABET[source[srcOffset + 3]]));
return -1; return -1;
} //e nd catch } //e nd catch
} }
} // end decodeToBytes } // end decodeToBytes
/** /**
* Decodes data from Base64 notation. * Decodes data from Base64 notation.
* *
@@ -563,13 +520,11 @@ public class Base64
* @return the decoded data * @return the decoded data
* @since 1.4 * @since 1.4
*/ */
private static byte[] standardDecode( String s ) private static byte[] standardDecode(String s) {
{
byte[] bytes = s.getBytes(); byte[] bytes = s.getBytes();
return decode( bytes, 0, bytes.length ); return decode(bytes, 0, bytes.length);
} // end decode } // end decode
/** /**
* Decodes data from Base64 notation and * Decodes data from Base64 notation and
* returns it as a string. * returns it as a string.
@@ -580,10 +535,10 @@ public class Base64
* @return The data as a string * @return The data as a string
* @since 1.4 * @since 1.4
*/ */
private static String decodeToString( String s ) private static String decodeToString(String s) {
{ return new String( decode( s ) ); return new String(decode(s));
} // end decodeToString } // end decodeToString
/** /**
* Decodes Base64 content in byte array format and returns * Decodes Base64 content in byte array format and returns
* the decoded byte array. * the decoded byte array.
@@ -594,49 +549,43 @@ public class Base64
* @return decoded data * @return decoded data
* @since 1.3 * @since 1.3
*/ */
private static byte[] decode( byte[] source, int off, int len ) private static byte[] decode(byte[] source, int off, int len) {
{ int len34 = len * 3 / 4;
int len34 = len * 3 / 4; byte[] outBuff = new byte[len34]; // Upper limit on size of output
byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output int outBuffPosn = 0;
int outBuffPosn = 0;
byte[] b4 = new byte[4];
byte[] b4 = new byte[4]; int b4Posn = 0;
int b4Posn = 0; int i = 0;
int i = 0; byte sbiCrop = 0;
byte sbiCrop = 0; byte sbiDecode = 0;
byte sbiDecode = 0; for (i = 0; i < len; i++) {
for( i = 0; i < len; i++ ) sbiCrop = (byte) (source[i] & 0x7f); // Only the low seven bits
{ sbiDecode = DECODABET[sbiCrop];
sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
sbiDecode = DECODABET[ sbiCrop ]; if (sbiDecode >= WHITE_SPACE_ENC) // White space, Equals sign or better
if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
{ {
if( sbiDecode >= EQUALS_SIGN_ENC ) if (sbiDecode >= EQUALS_SIGN_ENC) {
{ b4[b4Posn++] = sbiCrop;
b4[ b4Posn++ ] = sbiCrop; if (b4Posn > 3) {
if( b4Posn > 3 ) outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn);
{
outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn );
b4Posn = 0; b4Posn = 0;
// If that was the equals sign, break out of 'for' loop // If that was the equals sign, break out of 'for' loop
if( sbiCrop == EQUALS_SIGN ) if (sbiCrop == EQUALS_SIGN) break;
break; } // end if: quartet built
} // end if: quartet built
} // end if: equals sign or better
} // end if: equals sign or better
} // end if: white space, equals sign or better
} // end if: white space, equals sign or better else {
else System.err.println("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)");
{
System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
return null; return null;
} // end else: } // end else:
} // each input character } // each input character
byte[] out = new byte[ outBuffPosn ]; byte[] out = new byte[outBuffPosn];
System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
return out; return out;
} // end decode } // end decode
} // end class Base64 } // end class Base64

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -18,29 +19,46 @@ import java.io.Serializable;
*/ */
public class ByteArray implements Serializable { public class ByteArray implements Serializable {
private byte[] _data; private byte[] _data;
public ByteArray() { this(null); }
public ByteArray(byte[] data) { _data = data; } public ByteArray() {
public byte[] getData() { return _data; } this(null);
public void setData(byte[] data) { _data = data; } }
public ByteArray(byte[] data) {
_data = data;
}
public byte[] getData() {
return _data;
}
public void setData(byte[] data) {
_data = data;
}
public boolean equals(Object o) { public boolean equals(Object o) {
if (o == null) return false; if (o == null) return false;
if (o instanceof ByteArray) { if (o instanceof ByteArray) {
return compare(getData(), ((ByteArray)o).getData()); return compare(getData(), ((ByteArray) o).getData());
} else { } else {
try { try {
byte val[] = (byte[])o; byte val[] = (byte[]) o;
return compare(getData(), val); return compare(getData(), val);
} catch (Throwable t) { } catch (Throwable t) {
return false; return false;
} }
} }
} }
private boolean compare(byte[] lhs, byte[] rhs) { private boolean compare(byte[] lhs, byte[] rhs) {
return DataHelper.eq(lhs, rhs); return DataHelper.eq(lhs, rhs);
} }
public int hashCode() { return DataHelper.hashCode(getData()); } public int hashCode() {
public String toString() { return DataHelper.toString(getData(), 32); } return DataHelper.hashCode(getData());
} }
public String toString() {
return DataHelper.toString(getData(), 32);
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,9 +9,9 @@ package net.i2p.data;
* *
*/ */
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException;
import net.i2p.util.Log; import net.i2p.util.Log;
@@ -28,68 +29,75 @@ public class Certificate extends DataStructureImpl {
private final static Log _log = new Log(Certificate.class); private final static Log _log = new Log(Certificate.class);
private int _type; private int _type;
private byte[] _payload; private byte[] _payload;
/** Specifies a null certificate type with no payload */ /** Specifies a null certificate type with no payload */
public final static int CERTIFICATE_TYPE_NULL = 0; public final static int CERTIFICATE_TYPE_NULL = 0;
/** specifies a Hashcash style certificate */ /** specifies a Hashcash style certificate */
public final static int CERTIFICATE_TYPE_HASHCASH = 1; public final static int CERTIFICATE_TYPE_HASHCASH = 1;
public Certificate() { public Certificate() {
_type = 0; _type = 0;
_payload = null; _payload = null;
} }
public Certificate(int type, byte[] payload) { public Certificate(int type, byte[] payload) {
_type = type; _type = type;
_payload = payload; _payload = payload;
} }
/** */ /** */
public int getCertificateType() { return _type; } public int getCertificateType() {
public void setCertificateType(int type) { _type = type; } return _type;
}
public byte[] getPayload() { return _payload; }
public void setPayload(byte[] payload) { _payload = payload; } public void setCertificateType(int type) {
_type = type;
}
public byte[] getPayload() {
return _payload;
}
public void setPayload(byte[] payload) {
_payload = payload;
}
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
_type = (int)DataHelper.readLong(in, 1); _type = (int) DataHelper.readLong(in, 1);
int length = (int)DataHelper.readLong(in, 2); int length = (int) DataHelper.readLong(in, 2);
if (length > 0) { if (length > 0) {
_payload = new byte[length]; _payload = new byte[length];
int read = read(in, _payload); int read = read(in, _payload);
if (read != length) if (read != length)
throw new DataFormatException("Not enough bytes for the payload (read: " + read + " length: " + length + ")"); throw new DataFormatException("Not enough bytes for the payload (read: " + read + " length: " + length
+ ")");
} }
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_type < 0) if (_type < 0) throw new DataFormatException("Invalid certificate type: " + _type);
throw new DataFormatException("Invalid certificate type: " + _type); if ((_type != 0) && (_payload == null)) throw new DataFormatException("Payload is required for non null type");
if ( (_type != 0) && (_payload == null) )
throw new DataFormatException("Payload is required for non null type"); DataHelper.writeLong(out, 1, (long) _type);
DataHelper.writeLong(out, 1, (long)_type);
if (_payload != null) { if (_payload != null) {
DataHelper.writeLong(out, 2, (long)_payload.length); DataHelper.writeLong(out, 2, (long) _payload.length);
out.write(_payload); out.write(_payload);
} else { } else {
DataHelper.writeLong(out, 2, 0L); DataHelper.writeLong(out, 2, 0L);
} }
} }
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object == null) || !(object instanceof Certificate) ) if ((object == null) || !(object instanceof Certificate)) return false;
return false; Certificate cert = (Certificate) object;
Certificate cert = (Certificate)object; return getCertificateType() == cert.getCertificateType() && DataHelper.eq(getPayload(), cert.getPayload());
return getCertificateType() == cert.getCertificateType() &&
DataHelper.eq(getPayload(), cert.getPayload());
} }
public int hashCode() { public int hashCode() {
return getCertificateType() + DataHelper.hashCode(getPayload()); return getCertificateType() + DataHelper.hashCode(getPayload());
} }
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(64); StringBuffer buf = new StringBuffer(64);
buf.append("[Certificate: type: "); buf.append("[Certificate: type: ");
if (getCertificateType() == CERTIFICATE_TYPE_NULL) if (getCertificateType() == CERTIFICATE_TYPE_NULL)
@@ -98,18 +106,17 @@ public class Certificate extends DataStructureImpl {
buf.append("Hashcash certificate"); buf.append("Hashcash certificate");
else else
buf.append("Unknown certificiate type (").append(getCertificateType()).append(")"); buf.append("Unknown certificiate type (").append(getCertificateType()).append(")");
if (_payload == null) { if (_payload == null) {
buf.append(" null payload"); buf.append(" null payload");
} else { } else {
buf.append(" payload size: ").append(_payload.length); buf.append(" payload size: ").append(_payload.length);
int len = 32; int len = 32;
if (len > _payload.length) if (len > _payload.length) len = _payload.length;
len = _payload.length;
buf.append(" first ").append(len).append(" bytes: "); buf.append(" first ").append(len).append(" bytes: ");
buf.append(DataHelper.toString(_payload, len)); buf.append(DataHelper.toString(_payload, len));
} }
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,8 +9,8 @@ package net.i2p.data;
* *
*/ */
import net.i2p.util.Log;
import net.i2p.I2PException; import net.i2p.I2PException;
import net.i2p.util.Log;
/** /**
* Thrown when the data was not available to read or write a DataStructure * Thrown when the data was not available to read or write a DataStructure
@@ -18,11 +19,12 @@ import net.i2p.I2PException;
*/ */
public class DataFormatException extends I2PException { public class DataFormatException extends I2PException {
private final static Log _log = new Log(DataFormatException.class); private final static Log _log = new Log(DataFormatException.class);
public DataFormatException(String msg, Throwable t) { public DataFormatException(String msg, Throwable t) {
super(msg, t); super(msg, t);
} }
public DataFormatException(String msg) { public DataFormatException(String msg) {
super(msg); super(msg);
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -37,7 +38,7 @@ public class DataHelper {
private final static Log _log = new Log(DataHelper.class); private final static Log _log = new Log(DataHelper.class);
private final static String _equal = "="; // in UTF-8 private final static String _equal = "="; // in UTF-8
private final static String _semicolon = ";"; // in UTF-8 private final static String _semicolon = ";"; // in UTF-8
/** Read a mapping from the stream, as defined by the I2P data structure spec, /** Read a mapping from the stream, as defined by the I2P data structure spec,
* and store it into a Properties object. * and store it into a Properties object.
* *
@@ -58,35 +59,34 @@ public class DataHelper {
public static Properties readProperties(InputStream rawStream) throws DataFormatException, IOException { public static Properties readProperties(InputStream rawStream) throws DataFormatException, IOException {
Properties props = new OrderedProperties(); Properties props = new OrderedProperties();
long size = readLong(rawStream, 2); long size = readLong(rawStream, 2);
byte data[] = new byte[(int)size]; byte data[] = new byte[(int) size];
int read = read(rawStream, data); int read = read(rawStream, data);
if (read != size) if (read != size) throw new DataFormatException("Not enough data to read the properties");
throw new DataFormatException("Not enough data to read the properties"); ByteArrayInputStream in = new ByteArrayInputStream(data);
ByteArrayInputStream in = new ByteArrayInputStream(data); byte eqBuf[] = _equal.getBytes();
byte eqBuf[] = _equal.getBytes(); byte semiBuf[] = _semicolon.getBytes();
byte semiBuf[] = _semicolon.getBytes(); try {
try { while (in.available() > 0) {
while (in.available() > 0) { String key = readString(in);
String key = readString(in); read = read(in, eqBuf);
read = read(in, eqBuf); if ((read != eqBuf.length) || (!eq(new String(eqBuf), _equal))) {
if ((read != eqBuf.length) || (!eq(new String(eqBuf), _equal))) { _log.debug("Failed eqtest [" + new String(eqBuf) + "]");
_log.debug("Failed eqtest [" + new String(eqBuf) + "]"); break;
break; }
} String val = readString(in);
String val = readString(in); read = read(in, semiBuf);
read = read(in, semiBuf); if ((read != semiBuf.length) || (!eq(new String(semiBuf), _semicolon))) {
if ((read != semiBuf.length) || (!eq(new String(semiBuf), _semicolon))) { _log.debug("Failed semitest [" + new String(semiBuf) + "]");
_log.debug("Failed semitest [" + new String(semiBuf) + "]"); break;
break; }
} props.put(key, val);
props.put(key, val); }
} } catch (IOException ioe) {
} catch (IOException ioe) { _log.warn("Error reading properties", ioe);
_log.warn("Error reading properties", ioe); }
}
return props; return props;
} }
/** /**
* Write a mapping to the stream, as defined by the I2P data structure spec, * Write a mapping to the stream, as defined by the I2P data structure spec,
* and store it into a Properties object. See readProperties for the format. * and store it into a Properties object. See readProperties for the format.
@@ -96,43 +96,43 @@ public class DataHelper {
* @throws DataFormatException if there is not enough valid data to write out * @throws DataFormatException if there is not enough valid data to write out
* @throws IOException if there is an IO error writing out the data * @throws IOException if there is an IO error writing out the data
*/ */
public static void writeProperties(OutputStream rawStream, Properties props) throws DataFormatException, IOException { public static void writeProperties(OutputStream rawStream, Properties props) throws DataFormatException,
OrderedProperties p = new OrderedProperties(); IOException {
if (props != null) OrderedProperties p = new OrderedProperties();
p.putAll(props); if (props != null) p.putAll(props);
ByteArrayOutputStream baos = new ByteArrayOutputStream(32); ByteArrayOutputStream baos = new ByteArrayOutputStream(32);
for (Iterator iter = p.keySet().iterator(); iter.hasNext(); ) { for (Iterator iter = p.keySet().iterator(); iter.hasNext();) {
String key = (String)iter.next(); String key = (String) iter.next();
String val = p.getProperty(key); String val = p.getProperty(key);
// now make sure they're in UTF-8 // now make sure they're in UTF-8
key = new String(key.getBytes(), "UTF-8"); key = new String(key.getBytes(), "UTF-8");
val = new String(val.getBytes(), "UTF-8"); val = new String(val.getBytes(), "UTF-8");
writeString(baos, key); writeString(baos, key);
baos.write(_equal.getBytes()); baos.write(_equal.getBytes());
writeString(baos, val); writeString(baos, val);
baos.write(_semicolon.getBytes()); baos.write(_semicolon.getBytes());
} }
baos.close(); baos.close();
byte propBytes[] = baos.toByteArray(); byte propBytes[] = baos.toByteArray();
writeLong(rawStream, 2, propBytes.length); writeLong(rawStream, 2, propBytes.length);
rawStream.write(propBytes); rawStream.write(propBytes);
} }
/** /**
* Pretty print the mapping * Pretty print the mapping
* *
*/ */
public static String toString(Properties options) { public static String toString(Properties options) {
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
if (options != null) { if (options != null) {
for (Iterator iter = options.keySet().iterator(); iter.hasNext(); ) { for (Iterator iter = options.keySet().iterator(); iter.hasNext();) {
String key = (String)iter.next(); String key = (String) iter.next();
String val = options.getProperty(key); String val = options.getProperty(key);
buf.append("[").append(key).append("] = [").append(val).append("]"); buf.append("[").append(key).append("] = [").append(val).append("]");
} }
} else { } else {
buf.append("(null properties map)"); buf.append("(null properties map)");
} }
return buf.toString(); return buf.toString();
} }
@@ -143,58 +143,59 @@ public class DataHelper {
public static String toString(Collection col) { public static String toString(Collection col) {
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
if (col != null) { if (col != null) {
for (Iterator iter = col.iterator(); iter.hasNext(); ) { for (Iterator iter = col.iterator(); iter.hasNext();) {
Object o = iter.next(); Object o = iter.next();
buf.append("[").append(o).append("]"); buf.append("[").append(o).append("]");
if (iter.hasNext()) if (iter.hasNext()) buf.append(", ");
buf.append(", ");
} }
} else { } else {
buf.append("null"); buf.append("null");
} }
return buf.toString(); return buf.toString();
} }
public static String toString(byte buf[]) { public static String toString(byte buf[]) {
if (buf == null) return ""; if (buf == null)
else return toString(buf, buf.length); return "";
else
return toString(buf, buf.length);
} }
public static String toString(byte buf[], int len) { public static String toString(byte buf[], int len) {
if (buf == null) buf = "".getBytes(); if (buf == null) buf = "".getBytes();
StringBuffer out = new StringBuffer(); StringBuffer out = new StringBuffer();
if (len > buf.length) { if (len > buf.length) {
for (int i = 0; i < len - buf.length; i++) for (int i = 0; i < len - buf.length; i++)
out.append("00"); out.append("00");
} }
for (int i = 0; i < buf.length && i < len; i++) { for (int i = 0; i < buf.length && i < len; i++) {
StringBuffer temp = new StringBuffer(Integer.toHexString((int)buf[i])); StringBuffer temp = new StringBuffer(Integer.toHexString((int) buf[i]));
while (temp.length() < 2) { while (temp.length() < 2) {
temp.insert(0, '0'); temp.insert(0, '0');
} }
temp = new StringBuffer(temp.substring(temp.length()-2)); temp = new StringBuffer(temp.substring(temp.length() - 2));
out.append(temp.toString()); out.append(temp.toString());
} }
return out.toString(); return out.toString();
} }
public static String toDecimalString(byte buf[], int len) { public static String toDecimalString(byte buf[], int len) {
if (buf == null) buf = "".getBytes(); if (buf == null) buf = "".getBytes();
BigInteger val = new BigInteger(1, buf); BigInteger val = new BigInteger(1, buf);
return val.toString(10); return val.toString(10);
} }
public final static String toHexString(byte data[]) { public final static String toHexString(byte data[]) {
if ( (data == null) || (data.length <= 0) ) return "00"; if ((data == null) || (data.length <= 0)) return "00";
BigInteger bi = new BigInteger(1, data); BigInteger bi = new BigInteger(1, data);
return bi.toString(16); return bi.toString(16);
} }
public final static byte[] fromHexString(String val) { public final static byte[] fromHexString(String val) {
BigInteger bv = new BigInteger(val, 16); BigInteger bv = new BigInteger(val, 16);
return bv.toByteArray(); return bv.toByteArray();
} }
/** Read the stream for an integer as defined by the I2P data structure specification. /** Read the stream for an integer as defined by the I2P data structure specification.
* Integers are a fixed number of bytes (numBytes), stored as unsigned integers in network byte order. * Integers are a fixed number of bytes (numBytes), stored as unsigned integers in network byte order.
* @param rawStream stream to read from * @param rawStream stream to read from
@@ -205,16 +206,17 @@ public class DataHelper {
*/ */
public static long readLong(InputStream rawStream, int numBytes) throws DataFormatException, IOException { public static long readLong(InputStream rawStream, int numBytes) throws DataFormatException, IOException {
if (numBytes > 8) if (numBytes > 8)
throw new DataFormatException("readLong doesn't currently support reading numbers > 8 bytes [as thats bigger than java's long]"); throw new DataFormatException(
"readLong doesn't currently support reading numbers > 8 bytes [as thats bigger than java's long]");
byte data[] = new byte[numBytes]; byte data[] = new byte[numBytes];
int num = read(rawStream, data); int num = read(rawStream, data);
if (num != numBytes) if (num != numBytes)
throw new DataFormatException("Not enough bytes [" + num + "] as required for the field [" + numBytes + "]"); throw new DataFormatException("Not enough bytes [" + num + "] as required for the field [" + numBytes + "]");
UnsignedInteger val = new UnsignedInteger(data); UnsignedInteger val = new UnsignedInteger(data);
return val.getLong(); return val.getLong();
} }
/** Write an integer as defined by the I2P data structure specification to the stream. /** Write an integer as defined by the I2P data structure specification to the stream.
* Integers are a fixed number of bytes (numBytes), stored as unsigned integers in network byte order. * Integers are a fixed number of bytes (numBytes), stored as unsigned integers in network byte order.
* @param value value to write out * @param value value to write out
@@ -223,11 +225,12 @@ public class DataHelper {
* @throws DataFormatException if the stream doesn't contain a validly formatted number of that many bytes * @throws DataFormatException if the stream doesn't contain a validly formatted number of that many bytes
* @throws IOException if there is an IO error writing to the stream * @throws IOException if there is an IO error writing to the stream
*/ */
public static void writeLong(OutputStream rawStream, int numBytes, long value) throws DataFormatException, IOException { public static void writeLong(OutputStream rawStream, int numBytes, long value) throws DataFormatException,
IOException {
UnsignedInteger i = new UnsignedInteger(value); UnsignedInteger i = new UnsignedInteger(value);
rawStream.write(i.getBytes(numBytes)); rawStream.write(i.getBytes(numBytes));
} }
/** Read in a date from the stream as specified by the I2P data structure spec. /** Read in a date from the stream as specified by the I2P data structure spec.
* A date is an 8 byte unsigned integer in network byte order specifying the number of * A date is an 8 byte unsigned integer in network byte order specifying the number of
* milliseconds since midnight on January 1, 1970 in the GMT timezone. If the number is * milliseconds since midnight on January 1, 1970 in the GMT timezone. If the number is
@@ -244,7 +247,7 @@ public class DataHelper {
else else
return new Date(date); return new Date(date);
} }
/** Write out a date to the stream as specified by the I2P data structure spec. /** Write out a date to the stream as specified by the I2P data structure spec.
* @param out stream to write to * @param out stream to write to
* @param date date to write (can be null) * @param date date to write (can be null)
@@ -267,13 +270,13 @@ public class DataHelper {
* @return UTF-8 string * @return UTF-8 string
*/ */
public static String readString(InputStream in) throws DataFormatException, IOException { public static String readString(InputStream in) throws DataFormatException, IOException {
int size = (int)readLong(in, 1); int size = (int) readLong(in, 1);
byte raw[] = new byte[size]; byte raw[] = new byte[size];
int read = read(in, raw); int read = read(in, raw);
if (read != size) if (read != size) throw new DataFormatException("Not enough bytes to read the string");
throw new DataFormatException("Not enough bytes to read the string");
return new String(raw); return new String(raw);
} }
/** Write out a string to the stream as specified by the I2P data structure spec. Note that the max /** Write out a string to the stream as specified by the I2P data structure spec. Note that the max
* size for a string allowed by the spec is 255 bytes. * size for a string allowed by the spec is 255 bytes.
* *
@@ -284,15 +287,16 @@ public class DataHelper {
* @throws IOException if there is an IO error writing the string * @throws IOException if there is an IO error writing the string
*/ */
public static void writeString(OutputStream out, String string) throws DataFormatException, IOException { public static void writeString(OutputStream out, String string) throws DataFormatException, IOException {
if (string == null) { if (string == null) {
writeLong(out, 1, 0); writeLong(out, 1, 0);
} else { } else {
if (string.length() > 255) if (string.length() > 255)
throw new DataFormatException("The I2P data spec limits strings to 255 bytes or less, but this is " + string.length() + " [" + string + "]"); throw new DataFormatException("The I2P data spec limits strings to 255 bytes or less, but this is "
byte raw[] = string.getBytes(); + string.length() + " [" + string + "]");
writeLong(out, 1, raw.length); byte raw[] = string.getBytes();
out.write(raw); writeLong(out, 1, raw.length);
} out.write(raw);
}
} }
/** Read in a boolean as specified by the I2P data structure spec. /** Read in a boolean as specified by the I2P data structure spec.
@@ -303,18 +307,20 @@ public class DataHelper {
* @return boolean value, or null * @return boolean value, or null
*/ */
public static Boolean readBoolean(InputStream in) throws DataFormatException, IOException { public static Boolean readBoolean(InputStream in) throws DataFormatException, IOException {
int val = (int)readLong(in, 1); int val = (int) readLong(in, 1);
switch (val) { switch (val) {
case 0: case 0:
return Boolean.FALSE; return Boolean.FALSE;
case 1: case 1:
return Boolean.TRUE; return Boolean.TRUE;
case 2: case 2:
return null; return null;
default: default:
throw new DataFormatException("Uhhh.. readBoolean read a value that isn't a known ternary val (0,1,2): " + val); throw new DataFormatException("Uhhh.. readBoolean read a value that isn't a known ternary val (0,1,2): "
+ val);
} }
} }
/** Write out a boolean as specified by the I2P data structure spec. /** Write out a boolean as specified by the I2P data structure spec.
* A boolean is 1 byte that is either 0 (false), 1 (true), or 2 (null) * A boolean is 1 byte that is either 0 (false), 1 (true), or 2 (null)
* @param out stream to write to * @param out stream to write to
@@ -330,13 +336,12 @@ public class DataHelper {
else else
writeLong(out, 1, 0); writeLong(out, 1, 0);
} }
// //
// The following comparator helpers make it simpler to write consistently comparing // The following comparator helpers make it simpler to write consistently comparing
// functions for objects based on their value, not JVM memory address // functions for objects based on their value, not JVM memory address
// //
/** /**
* Helper util to compare two objects, including null handling. * Helper util to compare two objects, including null handling.
* <p /> * <p />
@@ -344,15 +349,15 @@ public class DataHelper {
* This treats (null == null) as true, and (null == (!null)) as false. * This treats (null == null) as true, and (null == (!null)) as false.
*/ */
public final static boolean eq(Object lhs, Object rhs) { public final static boolean eq(Object lhs, Object rhs) {
try { try {
boolean eq = ( ( (lhs == null) && (rhs == null) ) || boolean eq = (((lhs == null) && (rhs == null)) || ((lhs != null) && (lhs.equals(rhs))));
( (lhs != null) && (lhs.equals(rhs))) ); return eq;
return eq; } catch (ClassCastException cce) {
} catch (ClassCastException cce) { _log.warn("Error comparing [" + lhs + "] with [" + rhs + "]", cce);
_log.warn("Error comparing [" + lhs + "] with [" + rhs + "]", cce); return false;
return false; }
}
} }
/** /**
* Run a deep comparison across the two collections. * Run a deep comparison across the two collections.
* <p /> * <p />
@@ -366,17 +371,16 @@ public class DataHelper {
* *
*/ */
public final static boolean eq(Collection lhs, Collection rhs) { public final static boolean eq(Collection lhs, Collection rhs) {
if ( (lhs == null) && (rhs == null) ) return true; if ((lhs == null) && (rhs == null)) return true;
if ( (lhs == null) || (rhs == null) ) return false; if ((lhs == null) || (rhs == null)) return false;
if (lhs.size() != rhs.size()) return false; if (lhs.size() != rhs.size()) return false;
Iterator liter = lhs.iterator(); Iterator liter = lhs.iterator();
Iterator riter = rhs.iterator(); Iterator riter = rhs.iterator();
while ( (liter.hasNext()) && (riter.hasNext()) ) while ((liter.hasNext()) && (riter.hasNext()))
if (!(eq(liter.next(), riter.next()))) if (!(eq(liter.next(), riter.next()))) return false;
return false;
return true; return true;
} }
/** /**
* Run a comparison on the byte arrays, byte by byte. <p /> * Run a comparison on the byte arrays, byte by byte. <p />
* *
@@ -385,79 +389,93 @@ public class DataHelper {
* *
*/ */
public final static boolean eq(byte lhs[], byte rhs[]) { public final static boolean eq(byte lhs[], byte rhs[]) {
boolean eq = ( ( (lhs == null) && (rhs == null) ) || boolean eq = (((lhs == null) && (rhs == null)) || ((lhs != null) && (rhs != null) && (Arrays.equals(lhs, rhs))));
( (lhs != null) && (rhs != null) && (Arrays.equals(lhs, rhs)) ) );
return eq; return eq;
} }
/** /**
* Compare two integers, really just for consistency. * Compare two integers, really just for consistency.
*/ */
public final static boolean eq(int lhs, int rhs) { return lhs == rhs; } public final static boolean eq(int lhs, int rhs) {
return lhs == rhs;
}
/** /**
* Compare two longs, really just for consistency. * Compare two longs, really just for consistency.
*/ */
public final static boolean eq(long lhs, long rhs) { return lhs == rhs; } public final static boolean eq(long lhs, long rhs) {
return lhs == rhs;
}
/** /**
* Compare two bytes, really just for consistency. * Compare two bytes, really just for consistency.
*/ */
public final static boolean eq(byte lhs, byte rhs) { return lhs == rhs; } public final static boolean eq(byte lhs, byte rhs) {
return lhs == rhs;
}
public final static int compareTo(byte lhs[], byte rhs[]) { public final static int compareTo(byte lhs[], byte rhs[]) {
if ( (rhs == null) && (lhs == null) ) return 0; if ((rhs == null) && (lhs == null)) return 0;
if (lhs == null) return -1; if (lhs == null) return -1;
if (rhs == null) return 1; if (rhs == null) return 1;
if (rhs.length < lhs.length) return 1; if (rhs.length < lhs.length) return 1;
if (rhs.length > lhs.length) return -1; if (rhs.length > lhs.length) return -1;
for (int i = 0; i < rhs.length; i++) { for (int i = 0; i < rhs.length; i++) {
if (rhs[i] > lhs[i]) return -1; if (rhs[i] > lhs[i])
else if (rhs[i] < lhs[i]) return 1; return -1;
} else if (rhs[i] < lhs[i]) return 1;
return 0; }
return 0;
} }
public final static byte[] xor(byte lhs[], byte rhs[]) { public final static byte[] xor(byte lhs[], byte rhs[]) {
if ( (lhs == null) || (rhs == null) || (lhs.length != rhs.length) ) return null; if ((lhs == null) || (rhs == null) || (lhs.length != rhs.length)) return null;
byte diff[] = new byte[lhs.length]; byte diff[] = new byte[lhs.length];
for (int i = 0; i < lhs.length; i++) for (int i = 0; i < lhs.length; i++)
diff[i] = (byte)(lhs[i] ^ rhs[i]); diff[i] = (byte) (lhs[i] ^ rhs[i]);
return diff; return diff;
} }
// //
// The following hashcode helpers make it simpler to write consistently hashing // The following hashcode helpers make it simpler to write consistently hashing
// functions for objects based on their value, not JVM memory address // functions for objects based on their value, not JVM memory address
// //
/** /**
* Calculate the hashcode of the object, using 0 for null * Calculate the hashcode of the object, using 0 for null
* *
*/ */
public static int hashCode(Object obj) { public static int hashCode(Object obj) {
if (obj == null) return 0; if (obj == null)
else return obj.hashCode(); return 0;
else
return obj.hashCode();
} }
/** /**
* Calculate the hashcode of the date, using 0 for null * Calculate the hashcode of the date, using 0 for null
* *
*/ */
public static int hashCode(Date obj) { public static int hashCode(Date obj) {
if (obj == null) return 0; if (obj == null)
else return (int)obj.getTime(); return 0;
else
return (int) obj.getTime();
} }
/** /**
* Calculate the hashcode of the byte array, using 0 for null * Calculate the hashcode of the byte array, using 0 for null
* *
*/ */
public static int hashCode(byte b[]) { public static int hashCode(byte b[]) {
int rv = 0; int rv = 0;
if (b != null) { if (b != null) {
for (int i = 0; i < b.length && i < 8; i++) for (int i = 0; i < b.length && i < 8; i++)
rv += b[i]; rv += b[i];
} }
return rv; return rv;
} }
/** /**
* Calculate the hashcode of the collection, using 0 for null * Calculate the hashcode of the collection, using 0 for null
* *
@@ -465,95 +483,95 @@ public class DataHelper {
public static int hashCode(Collection col) { public static int hashCode(Collection col) {
if (col == null) return 0; if (col == null) return 0;
int c = 0; int c = 0;
for (Iterator iter = col.iterator(); iter.hasNext(); ) for (Iterator iter = col.iterator(); iter.hasNext();)
c = 7*c + hashCode(iter.next()); c = 7 * c + hashCode(iter.next());
return c; return c;
} }
public static int read(InputStream in, byte target[]) throws IOException { public static int read(InputStream in, byte target[]) throws IOException {
int cur = 0; int cur = 0;
while (cur < target.length) { while (cur < target.length) {
int numRead = in.read(target, cur, target.length - cur); int numRead = in.read(target, cur, target.length - cur);
if (numRead == -1) { if (numRead == -1) {
if (cur == 0) if (cur == 0)
return -1; // throw new EOFException("EOF Encountered during reading"); return -1; // throw new EOFException("EOF Encountered during reading");
else else
return cur; return cur;
} }
cur += numRead; cur += numRead;
} }
return cur; return cur;
} }
public static List sortStructures(Collection dataStructures) { public static List sortStructures(Collection dataStructures) {
if (dataStructures == null) return new ArrayList(); if (dataStructures == null) return new ArrayList();
ArrayList rv = new ArrayList(dataStructures.size()); ArrayList rv = new ArrayList(dataStructures.size());
TreeMap tm = new TreeMap(); TreeMap tm = new TreeMap();
for (Iterator iter = dataStructures.iterator(); iter.hasNext(); ) { for (Iterator iter = dataStructures.iterator(); iter.hasNext();) {
DataStructure struct = (DataStructure)iter.next(); DataStructure struct = (DataStructure) iter.next();
tm.put(struct.calculateHash().toString(), struct); tm.put(struct.calculateHash().toString(), struct);
} }
for (Iterator iter = tm.keySet().iterator(); iter.hasNext();) { for (Iterator iter = tm.keySet().iterator(); iter.hasNext();) {
Object k = iter.next(); Object k = iter.next();
rv.add(tm.get(k)); rv.add(tm.get(k));
} }
return rv; return rv;
} }
public static String formatDuration(long ms) { public static String formatDuration(long ms) {
if (ms < 30*1000) { if (ms < 30 * 1000) {
return ms + "ms"; return ms + "ms";
} else if (ms < 5*60*1000) { } else if (ms < 5 * 60 * 1000) {
return (ms/1000) + "s"; return (ms / 1000) + "s";
} else if (ms < 90*60*1000) { } else if (ms < 90 * 60 * 1000) {
return (ms / (60*1000)) + "m"; return (ms / (60 * 1000)) + "m";
} else if (ms < 3*24*60*60*1000) { } else if (ms < 3 * 24 * 60 * 60 * 1000) {
return (ms / (60*60*1000)) + "h"; return (ms / (60 * 60 * 1000)) + "h";
} else { } else {
return (ms / (24*60*60*1000)) + "d"; return (ms / (24 * 60 * 60 * 1000)) + "d";
} }
} }
/** compress the data and return a new GZIP compressed array */ /** compress the data and return a new GZIP compressed array */
public static byte[] compress(byte orig[]) { public static byte[] compress(byte orig[]) {
if ( (orig == null) || (orig.length <= 0) ) return orig; if ((orig == null) || (orig.length <= 0)) return orig;
try { try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(orig.length); ByteArrayOutputStream baos = new ByteArrayOutputStream(orig.length);
GZIPOutputStream out = new GZIPOutputStream(baos, orig.length); GZIPOutputStream out = new GZIPOutputStream(baos, orig.length);
out.write(orig); out.write(orig);
out.finish(); out.finish();
out.flush(); out.flush();
byte rv[] = baos.toByteArray(); byte rv[] = baos.toByteArray();
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Compression of " + orig.length + " into " + rv.length + " (or " + 100.0d*(((double)orig.length) / ((double)rv.length)) + "% savings)"); _log.debug("Compression of " + orig.length + " into " + rv.length + " (or " + 100.0d
return rv; * (((double) orig.length) / ((double) rv.length)) + "% savings)");
} catch (IOException ioe) { return rv;
_log.error("Error compressing?!", ioe); } catch (IOException ioe) {
return null; _log.error("Error compressing?!", ioe);
} return null;
}
} }
/** decompress the GZIP compressed data (returning null on error) */ /** decompress the GZIP compressed data (returning null on error) */
public static byte[] decompress(byte orig[]) { public static byte[] decompress(byte orig[]) {
if ( (orig == null) || (orig.length <= 0) ) return orig; if ((orig == null) || (orig.length <= 0)) return orig;
try { try {
GZIPInputStream in = new GZIPInputStream(new ByteArrayInputStream(orig), orig.length); GZIPInputStream in = new GZIPInputStream(new ByteArrayInputStream(orig), orig.length);
ByteArrayOutputStream baos = new ByteArrayOutputStream(orig.length * 2); ByteArrayOutputStream baos = new ByteArrayOutputStream(orig.length * 2);
byte buf[] = new byte[4*1024]; byte buf[] = new byte[4 * 1024];
while (true) { while (true) {
int read = in.read(buf); int read = in.read(buf);
if (read == -1) if (read == -1) break;
break; baos.write(buf, 0, read);
baos.write(buf, 0, read); }
} byte rv[] = baos.toByteArray();
byte rv[] = baos.toByteArray(); if (_log.shouldLog(Log.DEBUG))
if (_log.shouldLog(Log.DEBUG)) _log.debug("Decompression of " + orig.length + " into " + rv.length + " (or " + 100.0d
_log.debug("Decompression of " + orig.length + " into " + rv.length + " (or " + 100.0d*(((double)rv.length) / ((double)orig.length)) + "% savings)"); * (((double) rv.length) / ((double) orig.length)) + "% savings)");
return rv; return rv;
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error decompressing?", ioe); _log.error("Error decompressing?", ioe);
return null; return null;
} }
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,9 +9,9 @@ package net.i2p.data;
* *
*/ */
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
/** /**
@@ -29,7 +30,7 @@ public interface DataStructure extends Serializable {
* @throws IOException if there was a problem reading the stream * @throws IOException if there was a problem reading the stream
*/ */
public void readBytes(InputStream in) throws DataFormatException, IOException; public void readBytes(InputStream in) throws DataFormatException, IOException;
/** /**
* Write out the data structure to the stream, using the format defined in the * Write out the data structure to the stream, using the format defined in the
* I2P data structure specification. * I2P data structure specification.
@@ -39,26 +40,27 @@ public interface DataStructure extends Serializable {
* @throws IOException if there was a problem writing to the stream * @throws IOException if there was a problem writing to the stream
*/ */
public void writeBytes(OutputStream out) throws DataFormatException, IOException; public void writeBytes(OutputStream out) throws DataFormatException, IOException;
/** /**
* render the structure into modified base 64 notation * render the structure into modified base 64 notation
* @return null on error * @return null on error
*/ */
public String toBase64(); public String toBase64();
/** /**
* Load the structure from the base 64 encoded data provided * Load the structure from the base 64 encoded data provided
* *
*/ */
public void fromBase64(String data) throws DataFormatException; public void fromBase64(String data) throws DataFormatException;
public byte[] toByteArray(); public byte[] toByteArray();
public void fromByteArray(byte data[]) throws DataFormatException; public void fromByteArray(byte data[]) throws DataFormatException;
/** /**
* Calculate the SHA256 value of this object (useful for a few scenarios) * Calculate the SHA256 value of this object (useful for a few scenarios)
* *
* @return SHA256 hash, or null if there were problems (data format or io errors) * @return SHA256 hash, or null if there were problems (data format or io errors)
*/ */
public Hash calculateHash(); public Hash calculateHash();
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -23,58 +24,57 @@ import net.i2p.util.Log;
*/ */
public abstract class DataStructureImpl implements DataStructure { public abstract class DataStructureImpl implements DataStructure {
private final static Log _log = new Log(DataStructureImpl.class); private final static Log _log = new Log(DataStructureImpl.class);
public String toBase64() { public String toBase64() {
byte data[] = toByteArray(); byte data[] = toByteArray();
if (data == null) return null; if (data == null)
else return Base64.encode(data); return null;
else
return Base64.encode(data);
} }
public void fromBase64(String data) throws DataFormatException { public void fromBase64(String data) throws DataFormatException {
if (data == null) if (data == null) throw new DataFormatException("Null data passed in");
throw new DataFormatException("Null data passed in"); byte bytes[] = Base64.decode(data);
byte bytes[] = Base64.decode(data); fromByteArray(bytes);
fromByteArray(bytes);
} }
public Hash calculateHash() { public Hash calculateHash() {
byte data[] = toByteArray(); byte data[] = toByteArray();
if (data != null) if (data != null) return SHA256Generator.getInstance().calculateHash(data);
return SHA256Generator.getInstance().calculateHash(data); return null;
return null;
} }
public byte[] toByteArray() { public byte[] toByteArray() {
try { try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(512); ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
writeBytes(baos); writeBytes(baos);
return baos.toByteArray(); return baos.toByteArray();
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("Error writing out the byte array", ioe); _log.error("Error writing out the byte array", ioe);
return null; return null;
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
_log.error("Error writing out the byte array", dfe); _log.error("Error writing out the byte array", dfe);
return null; return null;
} }
} }
public void fromByteArray(byte data[]) throws DataFormatException { public void fromByteArray(byte data[]) throws DataFormatException {
if (data == null) if (data == null) throw new DataFormatException("Null data passed in");
throw new DataFormatException("Null data passed in"); try {
try { ByteArrayInputStream bais = new ByteArrayInputStream(data);
ByteArrayInputStream bais = new ByteArrayInputStream(data); readBytes(bais);
readBytes(bais); } catch (IOException ioe) {
} catch (IOException ioe) { throw new DataFormatException("Error reading the byte array", ioe);
throw new DataFormatException("Error reading the byte array", ioe); }
}
} }
/** /**
* Repeated reads until the buffer is full or IOException is thrown * Repeated reads until the buffer is full or IOException is thrown
* *
* @return number of bytes read (should always equal target.length) * @return number of bytes read (should always equal target.length)
*/ */
protected int read(InputStream in, byte target[]) throws IOException { protected int read(InputStream in, byte target[]) throws IOException {
return DataHelper.read(in, target); return DataHelper.read(in, target);
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -26,30 +27,41 @@ public class Destination extends DataStructureImpl {
private SigningPublicKey _signingKey; private SigningPublicKey _signingKey;
private PublicKey _publicKey; private PublicKey _publicKey;
private Hash __calculatedHash; private Hash __calculatedHash;
public Destination() { public Destination() {
setCertificate(null); setCertificate(null);
setSigningPublicKey(null); setSigningPublicKey(null);
setPublicKey(null); setPublicKey(null);
__calculatedHash = null; __calculatedHash = null;
} }
public Certificate getCertificate() { return _certificate; } public Certificate getCertificate() {
public void setCertificate(Certificate cert) { return _certificate;
_certificate = cert;
__calculatedHash = null;
} }
public PublicKey getPublicKey() { return _publicKey; }
public void setPublicKey(PublicKey key) { public void setCertificate(Certificate cert) {
_publicKey = key; _certificate = cert;
__calculatedHash = null; __calculatedHash = null;
} }
public SigningPublicKey getSigningPublicKey() { return _signingKey; }
public void setSigningPublicKey(SigningPublicKey key) { public PublicKey getPublicKey() {
_signingKey = key; return _publicKey;
__calculatedHash = null;
} }
public void setPublicKey(PublicKey key) {
_publicKey = key;
__calculatedHash = null;
}
public SigningPublicKey getSigningPublicKey() {
return _signingKey;
}
public void setSigningPublicKey(SigningPublicKey key) {
_signingKey = key;
__calculatedHash = null;
}
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
_publicKey = new PublicKey(); _publicKey = new PublicKey();
_publicKey.readBytes(in); _publicKey.readBytes(in);
@@ -57,32 +69,30 @@ public class Destination extends DataStructureImpl {
_signingKey.readBytes(in); _signingKey.readBytes(in);
_certificate = new Certificate(); _certificate = new Certificate();
_certificate.readBytes(in); _certificate.readBytes(in);
__calculatedHash = null; __calculatedHash = null;
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if ( (_certificate == null) || (_publicKey == null) || (_signingKey == null) ) if ((_certificate == null) || (_publicKey == null) || (_signingKey == null))
throw new DataFormatException("Not enough data to format the destination"); throw new DataFormatException("Not enough data to format the destination");
_publicKey.writeBytes(out); _publicKey.writeBytes(out);
_signingKey.writeBytes(out); _signingKey.writeBytes(out);
_certificate.writeBytes(out); _certificate.writeBytes(out);
} }
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object == null) || !(object instanceof Destination)) if ((object == null) || !(object instanceof Destination)) return false;
return false; Destination dst = (Destination) object;
Destination dst = (Destination)object; return DataHelper.eq(getCertificate(), dst.getCertificate())
return DataHelper.eq(getCertificate(), dst.getCertificate()) && && DataHelper.eq(getSigningPublicKey(), dst.getSigningPublicKey())
DataHelper.eq(getSigningPublicKey(), dst.getSigningPublicKey()) && && DataHelper.eq(getPublicKey(), dst.getPublicKey());
DataHelper.eq(getPublicKey(), dst.getPublicKey());
} }
public int hashCode() { public int hashCode() {
return DataHelper.hashCode(getCertificate()) + return DataHelper.hashCode(getCertificate()) + DataHelper.hashCode(getSigningPublicKey())
DataHelper.hashCode(getSigningPublicKey()) + + DataHelper.hashCode(getPublicKey());
DataHelper.hashCode(getPublicKey());
} }
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(128); StringBuffer buf = new StringBuffer(128);
buf.append("[Destination: "); buf.append("[Destination: ");
@@ -93,10 +103,9 @@ public class Destination extends DataStructureImpl {
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
public Hash calculateHash() { public Hash calculateHash() {
if (__calculatedHash == null) if (__calculatedHash == null) __calculatedHash = super.calculateHash();
__calculatedHash = super.calculateHash(); return __calculatedHash;
return __calculatedHash;
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,9 +9,9 @@ package net.i2p.data;
* *
*/ */
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException;
import net.i2p.util.Log; import net.i2p.util.Log;
@@ -24,56 +25,61 @@ public class Hash extends DataStructureImpl {
private final static Log _log = new Log(Hash.class); private final static Log _log = new Log(Hash.class);
private byte[] _data; private byte[] _data;
private volatile String _stringified; private volatile String _stringified;
public final static int HASH_LENGTH = 32; public final static int HASH_LENGTH = 32;
public final static Hash FAKE_HASH = new Hash(new byte[HASH_LENGTH]); public final static Hash FAKE_HASH = new Hash(new byte[HASH_LENGTH]);
public Hash() { setData(null); } public Hash() {
public Hash(byte data[]) { setData(data); } setData(null);
public byte[] getData() { return _data; }
public void setData(byte[] data) {
_data = data;
_stringified = null;
} }
public Hash(byte data[]) {
setData(data);
}
public byte[] getData() {
return _data;
}
public void setData(byte[] data) {
_data = data;
_stringified = null;
}
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
_data = new byte[HASH_LENGTH]; _data = new byte[HASH_LENGTH];
_stringified = null; _stringified = null;
int read = read(in, _data); int read = read(in, _data);
if (read != HASH_LENGTH) if (read != HASH_LENGTH) throw new DataFormatException("Not enough bytes to read the hash");
throw new DataFormatException("Not enough bytes to read the hash");
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_data == null) if (_data == null) throw new DataFormatException("No data in the hash to write out");
throw new DataFormatException("No data in the hash to write out"); if (_data.length != HASH_LENGTH) throw new DataFormatException("Invalid size of data in the private key");
if (_data.length != HASH_LENGTH)
throw new DataFormatException("Invalid size of data in the private key");
out.write(_data); out.write(_data);
} }
public boolean equals(Object obj) { public boolean equals(Object obj) {
if ( (obj == null) || !(obj instanceof Hash)) if ((obj == null) || !(obj instanceof Hash)) return false;
return false; return DataHelper.eq(_data, ((Hash) obj)._data);
return DataHelper.eq(_data, ((Hash)obj)._data);
} }
public int hashCode() { public int hashCode() {
return DataHelper.hashCode(_data); return DataHelper.hashCode(_data);
} }
public String toString() {
if (_stringified == null) { public String toString() {
StringBuffer buf = new StringBuffer(64); if (_stringified == null) {
buf.append("[Hash: "); StringBuffer buf = new StringBuffer(64);
if (_data == null) { buf.append("[Hash: ");
buf.append("null hash"); if (_data == null) {
} else { buf.append("null hash");
buf.append(toBase64()); } else {
} buf.append(toBase64());
buf.append("]"); }
_stringified = buf.toString(); buf.append("]");
} _stringified = buf.toString();
return _stringified; }
return _stringified;
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -13,8 +14,8 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Date; import java.util.Date;
import net.i2p.util.Log;
import net.i2p.util.Clock; import net.i2p.util.Clock;
import net.i2p.util.Log;
/** /**
* Defines the proof that a particular router / tunnel is allowed to receive * Defines the proof that a particular router / tunnel is allowed to receive
@@ -29,58 +30,88 @@ public class Lease extends DataStructureImpl {
private Date _end; private Date _end;
private int _numSuccess; private int _numSuccess;
private int _numFailure; private int _numFailure;
public Lease() { public Lease() {
setRouterIdentity(null); setRouterIdentity(null);
setTunnelId(null); setTunnelId(null);
setEndDate(null); setEndDate(null);
setNumSuccess(0); setNumSuccess(0);
setNumFailure(0); setNumFailure(0);
} }
/** Retrieve the router at which the destination can be contacted /** Retrieve the router at which the destination can be contacted
* @return identity of the router acting as a gateway * @return identity of the router acting as a gateway
*/ */
public RouterIdentity getRouterIdentity() { return _routerIdentity; } public RouterIdentity getRouterIdentity() {
return _routerIdentity;
}
/** Configure the router at which the destination can be contacted /** Configure the router at which the destination can be contacted
* @param ident router acting as the gateway * @param ident router acting as the gateway
*/ */
public void setRouterIdentity(RouterIdentity ident) { _routerIdentity = ident; } public void setRouterIdentity(RouterIdentity ident) {
_routerIdentity = ident;
}
/** Tunnel on the gateway to communicate with /** Tunnel on the gateway to communicate with
* @return tunnel ID * @return tunnel ID
*/ */
public TunnelId getTunnelId() { return _tunnelId; } public TunnelId getTunnelId() {
return _tunnelId;
}
/** Configure the tunnel on the gateway to communicate with /** Configure the tunnel on the gateway to communicate with
* @param id tunnel ID * @param id tunnel ID
*/ */
public void setTunnelId(TunnelId id) { _tunnelId = id; } public void setTunnelId(TunnelId id) {
public Date getEndDate() { return _end; } _tunnelId = id;
public void setEndDate(Date date) { _end = date; } }
public Date getEndDate() {
return _end;
}
public void setEndDate(Date date) {
_end = date;
}
/** /**
* Transient attribute of the lease, used to note how many times messages sent * Transient attribute of the lease, used to note how many times messages sent
* to the destination through the current lease were successful. * to the destination through the current lease were successful.
* *
*/ */
public int getNumSuccess() { return _numSuccess; } public int getNumSuccess() {
public void setNumSuccess(int num) { _numSuccess = num; } return _numSuccess;
}
public void setNumSuccess(int num) {
_numSuccess = num;
}
/** /**
* Transient attribute of the lease, used to note how many times messages sent * Transient attribute of the lease, used to note how many times messages sent
* to the destination through the current lease failed. * to the destination through the current lease failed.
* *
*/ */
public int getNumFailure() { return _numFailure; } public int getNumFailure() {
public void setNumFailure(int num) { _numFailure = num; } return _numFailure;
}
public void setNumFailure(int num) {
_numFailure = num;
}
/** has this lease already expired? */ /** has this lease already expired? */
public boolean isExpired() { return isExpired(0); } public boolean isExpired() {
return isExpired(0);
}
/** has this lease already expired (giving allowing up the fudgeFactor milliseconds for clock skew)? */ /** has this lease already expired (giving allowing up the fudgeFactor milliseconds for clock skew)? */
public boolean isExpired(long fudgeFactor) { public boolean isExpired(long fudgeFactor) {
if (_end == null) return true; if (_end == null) return true;
return _end.getTime() < Clock.getInstance().now() - fudgeFactor; return _end.getTime() < Clock.getInstance().now() - fudgeFactor;
} }
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
_routerIdentity = new RouterIdentity(); _routerIdentity = new RouterIdentity();
_routerIdentity.readBytes(in); _routerIdentity.readBytes(in);
@@ -88,31 +119,29 @@ public class Lease extends DataStructureImpl {
_tunnelId.readBytes(in); _tunnelId.readBytes(in);
_end = DataHelper.readDate(in); _end = DataHelper.readDate(in);
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if ( (_routerIdentity == null) || (_tunnelId == null) ) if ((_routerIdentity == null) || (_tunnelId == null))
throw new DataFormatException("Not enough data to write out a Lease"); throw new DataFormatException("Not enough data to write out a Lease");
_routerIdentity.writeBytes(out); _routerIdentity.writeBytes(out);
_tunnelId.writeBytes(out); _tunnelId.writeBytes(out);
DataHelper.writeDate(out, _end); DataHelper.writeDate(out, _end);
} }
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object == null) || !(object instanceof Lease) ) if ((object == null) || !(object instanceof Lease)) return false;
return false; Lease lse = (Lease) object;
Lease lse = (Lease)object; return DataHelper.eq(getEndDate(), lse.getEndDate())
return DataHelper.eq(getEndDate(), lse.getEndDate()) && && DataHelper.eq(getRouterIdentity(), lse.getRouterIdentity());
DataHelper.eq(getRouterIdentity(), lse.getRouterIdentity());
} }
public int hashCode() { public int hashCode() {
return DataHelper.hashCode(getEndDate()) + return DataHelper.hashCode(getEndDate()) + DataHelper.hashCode(getRouterIdentity())
DataHelper.hashCode(getRouterIdentity()) + + DataHelper.hashCode(getTunnelId());
DataHelper.hashCode(getTunnelId());
} }
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(128); StringBuffer buf = new StringBuffer(128);
buf.append("[Lease: "); buf.append("[Lease: ");
@@ -122,4 +151,4 @@ public class Lease extends DataStructureImpl {
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -17,8 +18,8 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import net.i2p.crypto.DSAEngine; import net.i2p.crypto.DSAEngine;
import net.i2p.util.Log;
import net.i2p.util.Clock; import net.i2p.util.Clock;
import net.i2p.util.Log;
/** /**
* Defines the set of leases a destination currently has. * Defines the set of leases a destination currently has.
@@ -34,57 +35,96 @@ public class LeaseSet extends DataStructureImpl {
private Signature _signature; private Signature _signature;
private volatile Hash _currentRoutingKey; private volatile Hash _currentRoutingKey;
private volatile byte[] _routingKeyGenMod; private volatile byte[] _routingKeyGenMod;
/** um, no lease can last more than a year. */ /** um, no lease can last more than a year. */
private final static long MAX_FUTURE_EXPIRATION = 365*24*60*60*1000L; private final static long MAX_FUTURE_EXPIRATION = 365 * 24 * 60 * 60 * 1000L;
public LeaseSet() { public LeaseSet() {
setDestination(null); setDestination(null);
setEncryptionKey(null); setEncryptionKey(null);
setSigningKey(null); setSigningKey(null);
setSignature(null); setSignature(null);
setRoutingKey(null); setRoutingKey(null);
_leases = new ArrayList(); _leases = new ArrayList();
_routingKeyGenMod = null; _routingKeyGenMod = null;
} }
public Destination getDestination() { return _destination; } public Destination getDestination() {
public void setDestination(Destination dest) { _destination = dest; } return _destination;
public PublicKey getEncryptionKey() { return _encryptionKey; } }
public void setEncryptionKey(PublicKey encryptionKey) { _encryptionKey = encryptionKey; }
public SigningPublicKey getSigningKey() { return _signingKey; } public void setDestination(Destination dest) {
public void setSigningKey(SigningPublicKey key) { _signingKey = key; } _destination = dest;
public void addLease(Lease lease) { _leases.add(lease); } }
public void removeLease(Lease lease) { _leases.remove(lease); }
public int getLeaseCount() { return _leases.size(); } public PublicKey getEncryptionKey() {
public Lease getLease(int index) { return (Lease)_leases.get(index); } return _encryptionKey;
public Signature getSignature() { return _signature; } }
public void setSignature(Signature sig) { _signature = sig; }
public void setEncryptionKey(PublicKey encryptionKey) {
_encryptionKey = encryptionKey;
}
public SigningPublicKey getSigningKey() {
return _signingKey;
}
public void setSigningKey(SigningPublicKey key) {
_signingKey = key;
}
public void addLease(Lease lease) {
_leases.add(lease);
}
public void removeLease(Lease lease) {
_leases.remove(lease);
}
public int getLeaseCount() {
return _leases.size();
}
public Lease getLease(int index) {
return (Lease) _leases.get(index);
}
public Signature getSignature() {
return _signature;
}
public void setSignature(Signature sig) {
_signature = sig;
}
/** /**
* Get the routing key for the structure using the current modifier in the RoutingKeyGenerator. * Get the routing key for the structure using the current modifier in the RoutingKeyGenerator.
* This only calculates a new one when necessary though (if the generator's key modifier changes) * This only calculates a new one when necessary though (if the generator's key modifier changes)
* *
*/ */
public Hash getRoutingKey() { public Hash getRoutingKey() {
RoutingKeyGenerator gen = RoutingKeyGenerator.getInstance(); RoutingKeyGenerator gen = RoutingKeyGenerator.getInstance();
if ( (gen.getModData() == null) || (_routingKeyGenMod == null) || (!DataHelper.eq(gen.getModData(), _routingKeyGenMod)) ) { if ((gen.getModData() == null) || (_routingKeyGenMod == null)
setRoutingKey(gen.getRoutingKey(getDestination().calculateHash())); || (!DataHelper.eq(gen.getModData(), _routingKeyGenMod))) {
_routingKeyGenMod = gen.getModData(); setRoutingKey(gen.getRoutingKey(getDestination().calculateHash()));
} _routingKeyGenMod = gen.getModData();
return _currentRoutingKey; }
return _currentRoutingKey;
} }
public void setRoutingKey(Hash key) { _currentRoutingKey = key; }
public void setRoutingKey(Hash key) {
_currentRoutingKey = key;
}
public boolean validateRoutingKey() { public boolean validateRoutingKey() {
Hash destKey = getDestination().calculateHash(); Hash destKey = getDestination().calculateHash();
Hash rk = RoutingKeyGenerator.getInstance().getRoutingKey(destKey); Hash rk = RoutingKeyGenerator.getInstance().getRoutingKey(destKey);
if (rk.equals(getRoutingKey())) if (rk.equals(getRoutingKey()))
return true; return true;
else else
return false; return false;
} }
/** /**
* Retrieve the end date of the earliest lease include in this leaseSet. * Retrieve the end date of the earliest lease include in this leaseSet.
* This is the date that should be used in comparisons for leaseSet age - to * This is the date that should be used in comparisons for leaseSet age - to
@@ -94,64 +134,66 @@ public class LeaseSet extends DataStructureImpl {
* @return earliest end date of any lease in the set, or -1 if there are no leases * @return earliest end date of any lease in the set, or -1 if there are no leases
*/ */
public long getEarliestLeaseDate() { public long getEarliestLeaseDate() {
long when = -1; long when = -1;
for (int i = 0; i < getLeaseCount(); i++) { for (int i = 0; i < getLeaseCount(); i++) {
Lease lse = (Lease)getLease(i); Lease lse = (Lease) getLease(i);
if ( (lse != null) && (lse.getEndDate() != null) ) { if ((lse != null) && (lse.getEndDate() != null)) {
if ( (when <= 0) || (lse.getEndDate().getTime() < when) ) if ((when <= 0) || (lse.getEndDate().getTime() < when)) when = lse.getEndDate().getTime();
when = lse.getEndDate().getTime(); }
} }
} return when;
return when;
} }
/** /**
* Sign the structure using the supplied signing key * Sign the structure using the supplied signing key
* *
*/ */
public void sign(SigningPrivateKey key) throws DataFormatException { public void sign(SigningPrivateKey key) throws DataFormatException {
byte[] bytes = getBytes(); byte[] bytes = getBytes();
if (bytes == null) throw new DataFormatException("Not enough data to sign"); if (bytes == null) throw new DataFormatException("Not enough data to sign");
// now sign with the key // now sign with the key
Signature sig = DSAEngine.getInstance().sign(bytes, key); Signature sig = DSAEngine.getInstance().sign(bytes, key);
setSignature(sig); setSignature(sig);
} }
/** /**
* Verify that the signature matches the lease set's destination's signing public key. * Verify that the signature matches the lease set's destination's signing public key.
* *
* @return true only if the signature matches * @return true only if the signature matches
*/ */
public boolean verifySignature() { public boolean verifySignature() {
if (getSignature() == null) return false; if (getSignature() == null) return false;
if (getDestination() == null) return false; if (getDestination() == null) return false;
byte data[] = getBytes(); byte data[] = getBytes();
if (data == null) return false; if (data == null) return false;
boolean signedByDest = DSAEngine.getInstance().verifySignature(getSignature(), data, getDestination().getSigningPublicKey()); boolean signedByDest = DSAEngine.getInstance().verifySignature(getSignature(), data,
boolean signedByRevoker = false; getDestination().getSigningPublicKey());
if (!signedByDest) { boolean signedByRevoker = false;
signedByRevoker = DSAEngine.getInstance().verifySignature(getSignature(), data, _signingKey); if (!signedByDest) {
} signedByRevoker = DSAEngine.getInstance().verifySignature(getSignature(), data, _signingKey);
return signedByDest || signedByRevoker; }
return signedByDest || signedByRevoker;
} }
/** /**
* Verify that the signature matches the lease set's destination's signing public key. * Verify that the signature matches the lease set's destination's signing public key.
* *
* @return true only if the signature matches * @return true only if the signature matches
*/ */
public boolean verifySignature(SigningPublicKey signingKey) { public boolean verifySignature(SigningPublicKey signingKey) {
if (getSignature() == null) return false; if (getSignature() == null) return false;
if (getDestination() == null) return false; if (getDestination() == null) return false;
byte data[] = getBytes(); byte data[] = getBytes();
if (data == null) return false; if (data == null) return false;
boolean signedByDest = DSAEngine.getInstance().verifySignature(getSignature(), data, getDestination().getSigningPublicKey()); boolean signedByDest = DSAEngine.getInstance().verifySignature(getSignature(), data,
boolean signedByRevoker = false; getDestination().getSigningPublicKey());
if (!signedByDest) { boolean signedByRevoker = false;
signedByRevoker = DSAEngine.getInstance().verifySignature(getSignature(), data, signingKey); if (!signedByDest) {
} signedByRevoker = DSAEngine.getInstance().verifySignature(getSignature(), data, signingKey);
return signedByDest || signedByRevoker; }
return signedByDest || signedByRevoker;
} }
/** /**
* Determine whether there are currently valid leases, at least within a given * Determine whether there are currently valid leases, at least within a given
* fudge factor * fudge factor
@@ -160,109 +202,104 @@ public class LeaseSet extends DataStructureImpl {
* @return true if there are current leases, false otherwise * @return true if there are current leases, false otherwise
*/ */
public boolean isCurrent(long fudge) { public boolean isCurrent(long fudge) {
long now = Clock.getInstance().now(); long now = Clock.getInstance().now();
long insane = now + MAX_FUTURE_EXPIRATION; long insane = now + MAX_FUTURE_EXPIRATION;
int cnt = getLeaseCount(); int cnt = getLeaseCount();
for (int i = 0; i < cnt; i++) { for (int i = 0; i < cnt; i++) {
Lease l = getLease(i); Lease l = getLease(i);
if (l.getEndDate().getTime() > insane) { if (l.getEndDate().getTime() > insane) {
_log.warn("LeaseSet" + calculateHash() + " expires an insane amount in the future - skip it: " + l); _log.warn("LeaseSet" + calculateHash() + " expires an insane amount in the future - skip it: " + l);
return false; return false;
} }
// if it hasn't finished, we're current // if it hasn't finished, we're current
if (l.getEndDate().getTime() > now) { if (l.getEndDate().getTime() > now) {
_log.debug("LeaseSet " + calculateHash() + " isn't exired: " + l); _log.debug("LeaseSet " + calculateHash() + " isn't exired: " + l);
return true; return true;
} else if (l.getEndDate().getTime() > now - fudge) { } else if (l.getEndDate().getTime() > now - fudge) {
_log.debug("LeaseSet " + calculateHash() + " isn't quite expired, but its within the fudge factor so we'll let it slide: " + l); _log.debug("LeaseSet " + calculateHash()
return true; + " isn't quite expired, but its within the fudge factor so we'll let it slide: " + l);
} return true;
} }
return false; }
return false;
} }
private byte[] getBytes() { private byte[] getBytes() {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
try { try {
if ( (_destination == null) || (_encryptionKey == null) || (_signingKey == null) || if ((_destination == null) || (_encryptionKey == null) || (_signingKey == null) || (_leases == null))
(_leases == null) ) return null;
return null;
_destination.writeBytes(out);
_destination.writeBytes(out); _encryptionKey.writeBytes(out);
_encryptionKey.writeBytes(out); _signingKey.writeBytes(out);
_signingKey.writeBytes(out); DataHelper.writeLong(out, 1, _leases.size());
DataHelper.writeLong(out, 1, _leases.size()); //DataHelper.writeLong(out, 4, _version);
//DataHelper.writeLong(out, 4, _version); for (Iterator iter = _leases.iterator(); iter.hasNext();) {
for (Iterator iter = _leases.iterator(); iter.hasNext();) { Lease lease = (Lease) iter.next();
Lease lease = (Lease)iter.next(); lease.writeBytes(out);
lease.writeBytes(out); }
}
} catch (IOException ioe) { } catch (IOException ioe) {
return null; return null;
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
return null; return null;
} }
return out.toByteArray(); return out.toByteArray();
} }
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
_destination = new Destination(); _destination = new Destination();
_destination.readBytes(in); _destination.readBytes(in);
_encryptionKey = new PublicKey(); _encryptionKey = new PublicKey();
_encryptionKey.readBytes(in); _encryptionKey.readBytes(in);
_signingKey = new SigningPublicKey(); _signingKey = new SigningPublicKey();
_signingKey.readBytes(in); _signingKey.readBytes(in);
int numLeases = (int)DataHelper.readLong(in, 1); int numLeases = (int) DataHelper.readLong(in, 1);
//_version = DataHelper.readLong(in, 4); //_version = DataHelper.readLong(in, 4);
_leases.clear(); _leases.clear();
for (int i = 0; i < numLeases; i++) { for (int i = 0; i < numLeases; i++) {
Lease lease = new Lease(); Lease lease = new Lease();
lease.readBytes(in); lease.readBytes(in);
_leases.add(lease); _leases.add(lease);
} }
_signature = new Signature(); _signature = new Signature();
_signature.readBytes(in); _signature.readBytes(in);
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if ( (_destination == null) || (_encryptionKey == null) || (_signingKey == null) || if ((_destination == null) || (_encryptionKey == null) || (_signingKey == null) || (_leases == null)
(_leases == null) || (_signature == null) ) || (_signature == null)) throw new DataFormatException("Not enough data to write out a LeaseSet");
throw new DataFormatException("Not enough data to write out a LeaseSet");
_destination.writeBytes(out);
_destination.writeBytes(out); _encryptionKey.writeBytes(out);
_encryptionKey.writeBytes(out); _signingKey.writeBytes(out);
_signingKey.writeBytes(out); DataHelper.writeLong(out, 1, _leases.size());
DataHelper.writeLong(out, 1, _leases.size()); //DataHelper.writeLong(out, 4, _version);
//DataHelper.writeLong(out, 4, _version); for (Iterator iter = _leases.iterator(); iter.hasNext();) {
for (Iterator iter = _leases.iterator(); iter.hasNext();) { Lease lease = (Lease) iter.next();
Lease lease = (Lease)iter.next(); lease.writeBytes(out);
lease.writeBytes(out); }
} _signature.writeBytes(out);
_signature.writeBytes(out);
} }
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object == null) || !(object instanceof LeaseSet) ) if ((object == null) || !(object instanceof LeaseSet)) return false;
return false; LeaseSet ls = (LeaseSet) object;
LeaseSet ls = (LeaseSet)object;
return DataHelper.eq(getEncryptionKey(), ls.getEncryptionKey()) && return DataHelper.eq(getEncryptionKey(), ls.getEncryptionKey()) &&
//DataHelper.eq(getVersion(), ls.getVersion()) && //DataHelper.eq(getVersion(), ls.getVersion()) &&
DataHelper.eq(_leases, ls._leases) && DataHelper.eq(_leases, ls._leases) && DataHelper.eq(getSignature(), ls.getSignature())
DataHelper.eq(getSignature(), ls.getSignature()) && && DataHelper.eq(getSigningKey(), ls.getSigningKey())
DataHelper.eq(getSigningKey(), ls.getSigningKey()) && && DataHelper.eq(getDestination(), ls.getDestination());
DataHelper.eq(getDestination(), ls.getDestination());
} }
public int hashCode() { public int hashCode() {
return DataHelper.hashCode(getEncryptionKey()) + return DataHelper.hashCode(getEncryptionKey()) +
//(int)_version + //(int)_version +
DataHelper.hashCode(_leases) + DataHelper.hashCode(_leases) + DataHelper.hashCode(getSignature())
DataHelper.hashCode(getSignature()) + + DataHelper.hashCode(getSigningKey()) + DataHelper.hashCode(getDestination());
DataHelper.hashCode(getSigningKey()) +
DataHelper.hashCode(getDestination());
} }
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(128); StringBuffer buf = new StringBuffer(128);
buf.append("[LeaseSet: "); buf.append("[LeaseSet: ");
@@ -272,9 +309,9 @@ public class LeaseSet extends DataStructureImpl {
//buf.append("\n\tVersion: ").append(getVersion()); //buf.append("\n\tVersion: ").append(getVersion());
buf.append("\n\tSignature: ").append(getSignature()); buf.append("\n\tSignature: ").append(getSignature());
buf.append("\n\tLeases: #").append(getLeaseCount()); buf.append("\n\tLeases: #").append(getLeaseCount());
for (int i = 0; i < getLeaseCount(); i++) for (int i = 0; i < getLeaseCount(); i++)
buf.append("\n\t\tLease (").append(i).append("): ").append(getLease(i)); buf.append("\n\t\tLease (").append(i).append("): ").append(getLease(i));
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -24,69 +25,74 @@ public class Payload extends DataStructureImpl {
private final static Log _log = new Log(Payload.class); private final static Log _log = new Log(Payload.class);
private byte[] _encryptedData; private byte[] _encryptedData;
private byte[] _unencryptedData; private byte[] _unencryptedData;
public Payload() { public Payload() {
setUnencryptedData(null); setUnencryptedData(null);
setEncryptedData(null); setEncryptedData(null);
} }
/** /**
* Retrieve the unencrypted body of the message. * Retrieve the unencrypted body of the message.
* *
* @return body of the message, or null if the message has either not been * @return body of the message, or null if the message has either not been
* decrypted yet or if the hash is not correct * decrypted yet or if the hash is not correct
*/ */
public byte[] getUnencryptedData() { public byte[] getUnencryptedData() {
return _unencryptedData; return _unencryptedData;
} }
/** /**
* Populate the message body with data. This does not automatically encrypt * Populate the message body with data. This does not automatically encrypt
* yet. * yet.
* *
*/ */
public void setUnencryptedData(byte[] data) { _unencryptedData = data; } public void setUnencryptedData(byte[] data) {
public byte[] getEncryptedData() { return _encryptedData; } _unencryptedData = data;
public void setEncryptedData(byte[] data) { _encryptedData = data; }
public int getSize() {
if (_unencryptedData != null)
return _unencryptedData.length;
else if (_encryptedData != null)
return _encryptedData.length;
else
return 0;
} }
public byte[] getEncryptedData() {
return _encryptedData;
}
public void setEncryptedData(byte[] data) {
_encryptedData = data;
}
public int getSize() {
if (_unencryptedData != null)
return _unencryptedData.length;
else if (_encryptedData != null)
return _encryptedData.length;
else
return 0;
}
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
int size = (int)DataHelper.readLong(in, 4); int size = (int) DataHelper.readLong(in, 4);
_encryptedData = new byte[size]; _encryptedData = new byte[size];
int read = read(in, _encryptedData); int read = read(in, _encryptedData);
if (read != size) if (read != size) throw new DataFormatException("Incorrect number of bytes read in the payload structure");
throw new DataFormatException("Incorrect number of bytes read in the payload structure");
_log.debug("read payload: " + read + " bytes"); _log.debug("read payload: " + read + " bytes");
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_encryptedData == null) if (_encryptedData == null) throw new DataFormatException("Not yet encrypted. Please set the encrypted data");
throw new DataFormatException("Not yet encrypted. Please set the encrypted data");
DataHelper.writeLong(out, 4, _encryptedData.length); DataHelper.writeLong(out, 4, _encryptedData.length);
out.write(_encryptedData); out.write(_encryptedData);
_log.debug("wrote payload: " + _encryptedData.length); _log.debug("wrote payload: " + _encryptedData.length);
} }
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object == null) || !(object instanceof Payload) ) if ((object == null) || !(object instanceof Payload)) return false;
return false; Payload p = (Payload) object;
Payload p = (Payload)object; return DataHelper.eq(getUnencryptedData(), p.getUnencryptedData())
return DataHelper.eq(getUnencryptedData(), p.getUnencryptedData()) && && DataHelper.eq(getEncryptedData(), p.getEncryptedData());
DataHelper.eq(getEncryptedData(), p.getEncryptedData());
} }
public int hashCode() { public int hashCode() {
return DataHelper.hashCode(getUnencryptedData()); return DataHelper.hashCode(getUnencryptedData());
} }
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(128); StringBuffer buf = new StringBuffer(128);
buf.append("[Payload: "); buf.append("[Payload: ");
@@ -97,4 +103,4 @@ public class Payload extends DataStructureImpl {
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,9 +9,9 @@ package net.i2p.data;
* *
*/ */
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException;
import net.i2p.util.Log; import net.i2p.util.Log;
@@ -24,40 +25,44 @@ import net.i2p.util.Log;
public class PrivateKey extends DataStructureImpl { public class PrivateKey extends DataStructureImpl {
private final static Log _log = new Log(PrivateKey.class); private final static Log _log = new Log(PrivateKey.class);
private byte[] _data; private byte[] _data;
public final static int KEYSIZE_BYTES = 256; public final static int KEYSIZE_BYTES = 256;
public PrivateKey() { setData(null); } public PrivateKey() {
setData(null);
public byte[] getData() { return _data; } }
public void setData(byte[] data) { _data = data; }
public byte[] getData() {
return _data;
}
public void setData(byte[] data) {
_data = data;
}
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
_data = new byte[KEYSIZE_BYTES]; _data = new byte[KEYSIZE_BYTES];
int read = read(in, _data); int read = read(in, _data);
if (read != KEYSIZE_BYTES) if (read != KEYSIZE_BYTES) throw new DataFormatException("Not enough bytes to read the private key");
throw new DataFormatException("Not enough bytes to read the private key");
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_data == null) if (_data == null) throw new DataFormatException("No data in the private key to write out");
throw new DataFormatException("No data in the private key to write out");
if (_data.length != KEYSIZE_BYTES) if (_data.length != KEYSIZE_BYTES)
throw new DataFormatException("Invalid size of data in the private key [" + _data.length + "]"); throw new DataFormatException("Invalid size of data in the private key [" + _data.length + "]");
out.write(_data); out.write(_data);
} }
public boolean equals(Object obj) { public boolean equals(Object obj) {
if ( (obj == null) || !(obj instanceof PrivateKey)) if ((obj == null) || !(obj instanceof PrivateKey)) return false;
return false; return DataHelper.eq(_data, ((PrivateKey) obj)._data);
return DataHelper.eq(_data, ((PrivateKey)obj)._data);
} }
public int hashCode() { public int hashCode() {
return DataHelper.hashCode(_data); return DataHelper.hashCode(_data);
} }
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(64); StringBuffer buf = new StringBuffer(64);
buf.append("[PrivateKey: "); buf.append("[PrivateKey: ");
if (_data == null) { if (_data == null) {
@@ -65,12 +70,11 @@ public class PrivateKey extends DataStructureImpl {
} else { } else {
buf.append("size: ").append(_data.length); buf.append("size: ").append(_data.length);
int len = 32; int len = 32;
if (len > _data.length) if (len > _data.length) len = _data.length;
len = _data.length;
buf.append(" first ").append(len).append(" bytes: "); buf.append(" first ").append(len).append(" bytes: ");
buf.append(DataHelper.toString(_data, len)); buf.append(DataHelper.toString(_data, len));
} }
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,9 +9,9 @@ package net.i2p.data;
* *
*/ */
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException;
import net.i2p.util.Log; import net.i2p.util.Log;
@@ -24,39 +25,43 @@ import net.i2p.util.Log;
public class PublicKey extends DataStructureImpl { public class PublicKey extends DataStructureImpl {
private final static Log _log = new Log(PublicKey.class); private final static Log _log = new Log(PublicKey.class);
private byte[] _data; private byte[] _data;
public final static int KEYSIZE_BYTES = 256; public final static int KEYSIZE_BYTES = 256;
public PublicKey() { setData(null); } public PublicKey() {
setData(null);
public byte[] getData() { return _data; } }
public void setData(byte[] data) { _data = data; }
public byte[] getData() {
return _data;
}
public void setData(byte[] data) {
_data = data;
}
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
_data = new byte[KEYSIZE_BYTES]; _data = new byte[KEYSIZE_BYTES];
int read = read(in, _data); int read = read(in, _data);
if (read != KEYSIZE_BYTES) if (read != KEYSIZE_BYTES) throw new DataFormatException("Not enough bytes to read the public key");
throw new DataFormatException("Not enough bytes to read the public key");
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_data == null) if (_data == null) throw new DataFormatException("No data in the public key to write out");
throw new DataFormatException("No data in the public key to write out"); if (_data.length != KEYSIZE_BYTES) throw new DataFormatException("Invalid size of data in the public key");
if (_data.length != KEYSIZE_BYTES)
throw new DataFormatException("Invalid size of data in the public key");
out.write(_data); out.write(_data);
} }
public boolean equals(Object obj) { public boolean equals(Object obj) {
if ( (obj == null) || !(obj instanceof PublicKey)) if ((obj == null) || !(obj instanceof PublicKey)) return false;
return false; return DataHelper.eq(_data, ((PublicKey) obj)._data);
return DataHelper.eq(_data, ((PublicKey)obj)._data);
} }
public int hashCode() { public int hashCode() {
return DataHelper.hashCode(_data); return DataHelper.hashCode(_data);
} }
public String toString() {
public String toString() {
StringBuffer buf = new StringBuffer(64); StringBuffer buf = new StringBuffer(64);
buf.append("[PublicKey: "); buf.append("[PublicKey: ");
if (_data == null) { if (_data == null) {
@@ -64,12 +69,11 @@ public class PublicKey extends DataStructureImpl {
} else { } else {
buf.append("size: ").append(_data.length); buf.append("size: ").append(_data.length);
int len = 32; int len = 32;
if (len > _data.length) if (len > _data.length) len = _data.length;
len = _data.length;
buf.append(" first ").append(len).append(" bytes: "); buf.append(" first ").append(len).append(" bytes: ");
buf.append(DataHelper.toString(_data, len)); buf.append(DataHelper.toString(_data, len));
} }
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,13 +9,12 @@ package net.i2p.data;
* *
*/ */
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException;
import java.util.Properties;
import java.util.Iterator;
import java.util.Date; import java.util.Date;
import java.util.Iterator;
import java.util.Properties;
import net.i2p.util.Log; import net.i2p.util.Log;
@@ -29,93 +29,112 @@ public class RouterAddress extends DataStructureImpl {
private Date _expiration; private Date _expiration;
private String _transportStyle; private String _transportStyle;
private Properties _options; private Properties _options;
public RouterAddress() { public RouterAddress() {
setCost(-1); setCost(-1);
setExpiration(null); setExpiration(null);
setTransportStyle(null); setTransportStyle(null);
setOptions(null); setOptions(null);
} }
/** /**
* Retrieve the weighted cost of this address, relative to other methods of * Retrieve the weighted cost of this address, relative to other methods of
* contacting this router. The value 0 means free and 255 means really expensive. * contacting this router. The value 0 means free and 255 means really expensive.
* No value above 255 is allowed. * No value above 255 is allowed.
* *
*/ */
public int getCost() { return _cost; } public int getCost() {
return _cost;
}
/** /**
* Configure the weighted cost of using the address. * Configure the weighted cost of using the address.
* No value above 255 is allowed. * No value above 255 is allowed.
* *
*/ */
public void setCost(int cost) { _cost = cost; } public void setCost(int cost) {
_cost = cost;
}
/** /**
* Retrieve the date after which the address should not be used. If this * Retrieve the date after which the address should not be used. If this
* is null, then the address never expires. * is null, then the address never expires.
* *
*/ */
public Date getExpiration() { return _expiration; } public Date getExpiration() {
return _expiration;
}
/** /**
* Configure the expiration date of the address (null for no expiration) * Configure the expiration date of the address (null for no expiration)
* *
*/ */
public void setExpiration(Date expiration) { _expiration = expiration; } public void setExpiration(Date expiration) {
_expiration = expiration;
}
/** /**
* Retrieve the type of transport that must be used to communicate on this address. * Retrieve the type of transport that must be used to communicate on this address.
* *
*/ */
public String getTransportStyle() { return _transportStyle; } public String getTransportStyle() {
return _transportStyle;
}
/** /**
* Configure the type of transport that must be used to communicate on this address * Configure the type of transport that must be used to communicate on this address
* *
*/ */
public void setTransportStyle(String transportStyle) { _transportStyle = transportStyle; } public void setTransportStyle(String transportStyle) {
_transportStyle = transportStyle;
}
/** /**
* Retrieve the transport specific options necessary for communication * Retrieve the transport specific options necessary for communication
* *
*/ */
public Properties getOptions() { return _options; } public Properties getOptions() {
return _options;
}
/** /**
* Specify the transport specific options necessary for communication * Specify the transport specific options necessary for communication
* *
*/ */
public void setOptions(Properties options) { _options = options; } public void setOptions(Properties options) {
_options = options;
}
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
_cost = (int)DataHelper.readLong(in, 1); _cost = (int) DataHelper.readLong(in, 1);
_expiration = DataHelper.readDate(in); _expiration = DataHelper.readDate(in);
_transportStyle = DataHelper.readString(in); _transportStyle = DataHelper.readString(in);
_options = DataHelper.readProperties(in); _options = DataHelper.readProperties(in);
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if ( (_cost < 0) || (_transportStyle == null) || (_options == null) ) if ((_cost < 0) || (_transportStyle == null) || (_options == null))
throw new DataFormatException("Not enough data to write a router address"); throw new DataFormatException("Not enough data to write a router address");
DataHelper.writeLong(out, 1, _cost); DataHelper.writeLong(out, 1, _cost);
DataHelper.writeDate(out, _expiration); DataHelper.writeDate(out, _expiration);
DataHelper.writeString(out, _transportStyle); DataHelper.writeString(out, _transportStyle);
DataHelper.writeProperties(out, _options); DataHelper.writeProperties(out, _options);
} }
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object == null) || !(object instanceof RouterAddress) ) if ((object == null) || !(object instanceof RouterAddress)) return false;
return false; RouterAddress addr = (RouterAddress) object;
RouterAddress addr = (RouterAddress)object; return DataHelper.eq(getCost(), addr.getCost()) && DataHelper.eq(getExpiration(), addr.getExpiration())
return DataHelper.eq(getCost(), addr.getCost()) && && DataHelper.eq(getOptions(), addr.getOptions())
DataHelper.eq(getExpiration(), addr.getExpiration()) && && DataHelper.eq(getTransportStyle(), addr.getTransportStyle());
DataHelper.eq(getOptions(), addr.getOptions()) &&
DataHelper.eq(getTransportStyle(), addr.getTransportStyle());
} }
public int hashCode() { public int hashCode() {
return getCost() + return getCost() + DataHelper.hashCode(getTransportStyle()) + DataHelper.hashCode(getExpiration())
DataHelper.hashCode(getTransportStyle()) + + DataHelper.hashCode(getOptions());
DataHelper.hashCode(getExpiration()) +
DataHelper.hashCode(getOptions());
} }
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(64); StringBuffer buf = new StringBuffer(64);
buf.append("[RouterAddress: "); buf.append("[RouterAddress: ");
buf.append("\n\tTransportStyle: ").append(getTransportStyle()); buf.append("\n\tTransportStyle: ").append(getTransportStyle());
@@ -123,11 +142,11 @@ public class RouterAddress extends DataStructureImpl {
buf.append("\n\tExpiration: ").append(getExpiration()); buf.append("\n\tExpiration: ").append(getExpiration());
buf.append("\n\tOptions: #: ").append(getOptions().size()); buf.append("\n\tOptions: #: ").append(getOptions().size());
for (Iterator iter = getOptions().keySet().iterator(); iter.hasNext();) { for (Iterator iter = getOptions().keySet().iterator(); iter.hasNext();) {
String key = (String)iter.next(); String key = (String) iter.next();
String val = getOptions().getProperty(key); String val = getOptions().getProperty(key);
buf.append("\n\t\t[").append(key).append("] = [").append(val).append("]"); buf.append("\n\t\t[").append(key).append("] = [").append(val).append("]");
} }
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,10 +9,10 @@ package net.i2p.data;
* *
*/ */
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import net.i2p.crypto.SHA256Generator; import net.i2p.crypto.SHA256Generator;
import net.i2p.util.Log; import net.i2p.util.Log;
@@ -28,30 +29,41 @@ public class RouterIdentity extends DataStructureImpl {
private SigningPublicKey _signingKey; private SigningPublicKey _signingKey;
private PublicKey _publicKey; private PublicKey _publicKey;
private Hash __calculatedHash; private Hash __calculatedHash;
public RouterIdentity() { public RouterIdentity() {
setCertificate(null); setCertificate(null);
setSigningPublicKey(null); setSigningPublicKey(null);
setPublicKey(null); setPublicKey(null);
__calculatedHash = null; __calculatedHash = null;
} }
public Certificate getCertificate() { return _certificate; } public Certificate getCertificate() {
public void setCertificate(Certificate cert) { return _certificate;
_certificate = cert;
__calculatedHash = null;
} }
public PublicKey getPublicKey() { return _publicKey; }
public void setPublicKey(PublicKey key) { public void setCertificate(Certificate cert) {
_publicKey = key; _certificate = cert;
__calculatedHash = null; __calculatedHash = null;
} }
public SigningPublicKey getSigningPublicKey() { return _signingKey; }
public void setSigningPublicKey(SigningPublicKey key) { public PublicKey getPublicKey() {
_signingKey = key; return _publicKey;
__calculatedHash = null;
} }
public void setPublicKey(PublicKey key) {
_publicKey = key;
__calculatedHash = null;
}
public SigningPublicKey getSigningPublicKey() {
return _signingKey;
}
public void setSigningPublicKey(SigningPublicKey key) {
_signingKey = key;
__calculatedHash = null;
}
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
_publicKey = new PublicKey(); _publicKey = new PublicKey();
_publicKey.readBytes(in); _publicKey.readBytes(in);
@@ -59,32 +71,31 @@ public class RouterIdentity extends DataStructureImpl {
_signingKey.readBytes(in); _signingKey.readBytes(in);
_certificate = new Certificate(); _certificate = new Certificate();
_certificate.readBytes(in); _certificate.readBytes(in);
__calculatedHash = null; __calculatedHash = null;
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if ( (_certificate == null) || (_publicKey == null) || (_signingKey == null) ) if ((_certificate == null) || (_publicKey == null) || (_signingKey == null))
throw new DataFormatException("Not enough data to format the destination"); throw new DataFormatException("Not enough data to format the destination");
_publicKey.writeBytes(out); _publicKey.writeBytes(out);
_signingKey.writeBytes(out); _signingKey.writeBytes(out);
_certificate.writeBytes(out); _certificate.writeBytes(out);
} }
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object == null) || !(object instanceof RouterIdentity) ) if ((object == null) || !(object instanceof RouterIdentity)) return false;
return false; RouterIdentity ident = (RouterIdentity) object;
RouterIdentity ident = (RouterIdentity)object; return DataHelper.eq(getCertificate(), ident.getCertificate())
return DataHelper.eq(getCertificate(), ident.getCertificate()) && && DataHelper.eq(getSigningPublicKey(), ident.getSigningPublicKey())
DataHelper.eq(getSigningPublicKey(), ident.getSigningPublicKey()) && && DataHelper.eq(getPublicKey(), ident.getPublicKey());
DataHelper.eq(getPublicKey(), ident.getPublicKey());
} }
public int hashCode() { public int hashCode() {
return DataHelper.hashCode(getCertificate()) + return DataHelper.hashCode(getCertificate()) + DataHelper.hashCode(getSigningPublicKey())
DataHelper.hashCode(getSigningPublicKey()) + + DataHelper.hashCode(getPublicKey());
DataHelper.hashCode(getPublicKey());
} }
public String toString() {
public String toString() {
StringBuffer buf = new StringBuffer(64); StringBuffer buf = new StringBuffer(64);
buf.append("[RouterIdentity: "); buf.append("[RouterIdentity: ");
buf.append("\n\tHash: ").append(getHash().toBase64()); buf.append("\n\tHash: ").append(getHash().toBase64());
@@ -94,24 +105,25 @@ public class RouterIdentity extends DataStructureImpl {
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
public Hash calculateHash() { return getHash(); } public Hash calculateHash() {
return getHash();
public Hash getHash() {
if (__calculatedHash != null) {
//_log.info("Returning cached ident hash");
return __calculatedHash;
}
byte identBytes[] = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
writeBytes(baos);
identBytes = baos.toByteArray();
} catch (Throwable t) {
_log.error("Error writing out hash");
return null;
}
__calculatedHash = SHA256Generator.getInstance().calculateHash(identBytes);
return __calculatedHash;
} }
}
public Hash getHash() {
if (__calculatedHash != null) {
//_log.info("Returning cached ident hash");
return __calculatedHash; }
byte identBytes[] = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
writeBytes(baos);
identBytes = baos.toByteArray();
} catch (Throwable t) {
_log.error("Error writing out hash");
return null;
}
__calculatedHash = SHA256Generator.getInstance().calculateHash(identBytes);
return __calculatedHash;
}
}

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,23 +9,22 @@ package net.i2p.data;
* *
*/ */
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.Set; import java.io.OutputStream;
import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Properties;
import java.util.List; import java.util.List;
import java.util.Date; import java.util.Properties;
import java.util.Set;
import net.i2p.util.Log;
import net.i2p.util.OrderedProperties;
import net.i2p.util.Clock;
import net.i2p.crypto.DSAEngine; import net.i2p.crypto.DSAEngine;
import net.i2p.crypto.SHA256Generator; import net.i2p.crypto.SHA256Generator;
import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.util.OrderedProperties;
/** /**
* Defines the data that a router either publishes to the global routing table or * Defines the data that a router either publishes to the global routing table or
@@ -46,49 +46,53 @@ public class RouterInfo extends DataStructureImpl {
private volatile boolean _isValid; private volatile boolean _isValid;
private volatile String _stringified; private volatile String _stringified;
private volatile byte _byteified[]; private volatile byte _byteified[];
public RouterInfo() { public RouterInfo() {
setIdentity(null); setIdentity(null);
setPublished(0); setPublished(0);
_addresses = new HashSet(); _addresses = new HashSet();
_peers = new HashSet(); _peers = new HashSet();
_options = new OrderedProperties(); _options = new OrderedProperties();
setSignature(null); setSignature(null);
_validated = false; _validated = false;
_isValid = false; _isValid = false;
_currentRoutingKey = null; _currentRoutingKey = null;
_stringified = null; _stringified = null;
_byteified = null; _byteified = null;
} }
public RouterInfo(RouterInfo old) { public RouterInfo(RouterInfo old) {
this(); this();
setIdentity(old.getIdentity()); setIdentity(old.getIdentity());
setPublished(old.getPublished()); setPublished(old.getPublished());
setAddresses(old.getAddresses()); setAddresses(old.getAddresses());
setPeers(old.getPeers()); setPeers(old.getPeers());
setOptions(old.getOptions()); setOptions(old.getOptions());
setSignature(old.getSignature()); setSignature(old.getSignature());
}
private void resetCache() {
_stringified = null;
_byteified = null;
} }
private void resetCache() {
_stringified = null;
_byteified = null;
}
/** /**
* Retrieve the identity of the router represented * Retrieve the identity of the router represented
* *
*/ */
public RouterIdentity getIdentity() { return _identity; } public RouterIdentity getIdentity() {
return _identity;
}
/** /**
* Configure the identity of the router represented * Configure the identity of the router represented
* *
*/ */
public void setIdentity(RouterIdentity ident) { public void setIdentity(RouterIdentity ident) {
_identity = ident; _identity = ident;
resetCache(); resetCache();
} }
/** /**
* Retrieve the approximate date on which the info was published * Retrieve the approximate date on which the info was published
* (essentially a version number for the routerInfo structure, except that * (essentially a version number for the routerInfo structure, except that
@@ -97,185 +101,202 @@ public class RouterInfo extends DataStructureImpl {
* old routerInfo structures * old routerInfo structures
* *
*/ */
public long getPublished() { return _published; } public long getPublished() {
return _published;
}
/** /**
* Date on which it was published, in milliseconds since Midnight GMT on Jan 01, 1970 * Date on which it was published, in milliseconds since Midnight GMT on Jan 01, 1970
* *
*/ */
public void setPublished(long published) { _published = published; } public void setPublished(long published) {
_published = published;
}
/** /**
* Retrieve the set of RouterAddress structures at which this * Retrieve the set of RouterAddress structures at which this
* router can be contacted. * router can be contacted.
* *
*/ */
public Set getAddresses() { public Set getAddresses() {
synchronized (_addresses) { synchronized (_addresses) {
return new HashSet(_addresses); return new HashSet(_addresses);
} }
} }
/** /**
* Specify a set of RouterAddress structures at which this router * Specify a set of RouterAddress structures at which this router
* can be contacted. * can be contacted.
* *
*/ */
public void setAddresses(Set addresses) { public void setAddresses(Set addresses) {
synchronized (_addresses) { synchronized (_addresses) {
_addresses.clear(); _addresses.clear();
if (addresses != null) if (addresses != null) _addresses.addAll(addresses);
_addresses.addAll(addresses); }
} resetCache();
resetCache();
} }
/** /**
* Retrieve a set of SHA-256 hashes of RouterIdentities from rotuers * Retrieve a set of SHA-256 hashes of RouterIdentities from rotuers
* this router can be reached through. * this router can be reached through.
* *
*/ */
public Set getPeers() { return _peers; } public Set getPeers() {
return _peers;
}
/** /**
* Specify a set of SHA-256 hashes of RouterIdentities from rotuers * Specify a set of SHA-256 hashes of RouterIdentities from rotuers
* this router can be reached through. * this router can be reached through.
* *
*/ */
public void setPeers(Set peers) { public void setPeers(Set peers) {
synchronized (_peers) { synchronized (_peers) {
_peers.clear(); _peers.clear();
if (peers != null) if (peers != null) _peers.addAll(peers);
_peers.addAll(peers); }
} resetCache();
resetCache();
} }
/** /**
* Retrieve a set of options or statistics that the router can expose * Retrieve a set of options or statistics that the router can expose
* *
*/ */
public Properties getOptions() { public Properties getOptions() {
if (_options == null) return new Properties(); if (_options == null) return new Properties();
synchronized (_options) { synchronized (_options) {
return (Properties)_options.clone(); return (Properties) _options.clone();
} }
} }
/** /**
* Configure a set of options or statistics that the router can expose * Configure a set of options or statistics that the router can expose
* *
*/ */
public void setOptions(Properties options) { public void setOptions(Properties options) {
synchronized (_options) { synchronized (_options) {
_options.clear(); _options.clear();
if (options != null) { if (options != null) {
for (Iterator iter = options.keySet().iterator(); iter.hasNext(); ) { for (Iterator iter = options.keySet().iterator(); iter.hasNext();) {
String name = (String)iter.next(); String name = (String) iter.next();
if (name == null) continue; if (name == null) continue;
String val = options.getProperty(name); String val = options.getProperty(name);
if (val == null) continue; if (val == null) continue;
_options.setProperty(name, val); _options.setProperty(name, val);
} }
} }
} }
resetCache(); resetCache();
} }
/** /**
* Retrieve the proof that the identity stands behind the info here * Retrieve the proof that the identity stands behind the info here
* *
*/ */
public Signature getSignature() { return _signature; } public Signature getSignature() {
return _signature;
}
/** /**
* Configure the proof that the entity stands behind the info here * Configure the proof that the entity stands behind the info here
* *
*/ */
public void setSignature(Signature signature) { public void setSignature(Signature signature) {
_signature = signature; _signature = signature;
resetCache(); resetCache();
} }
/** /**
* Sign the structure using the supplied signing key * Sign the structure using the supplied signing key
* *
*/ */
public synchronized void sign(SigningPrivateKey key) throws DataFormatException { public synchronized void sign(SigningPrivateKey key) throws DataFormatException {
byte[] bytes = getBytes(); byte[] bytes = getBytes();
if (bytes == null) throw new DataFormatException("Not enough data to sign"); if (bytes == null) throw new DataFormatException("Not enough data to sign");
// now sign with the key // now sign with the key
Signature sig = DSAEngine.getInstance().sign(bytes, key); Signature sig = DSAEngine.getInstance().sign(bytes, key);
setSignature(sig); setSignature(sig);
//_log.debug("Signed " + SHA256Generator.getInstance().calculateHash(bytes).toBase64() + " with " + key); //_log.debug("Signed " + SHA256Generator.getInstance().calculateHash(bytes).toBase64() + " with " + key);
//_log.debug("verify ok? " + DSAEngine.getInstance().verifySignature(sig, bytes, getIdentity().getSigningPublicKey())); //_log.debug("verify ok? " + DSAEngine.getInstance().verifySignature(sig, bytes, getIdentity().getSigningPublicKey()));
//_log.debug("Signed data: \n" + Base64.encode(bytes)); //_log.debug("Signed data: \n" + Base64.encode(bytes));
//_log.debug("Signature: " + getSignature()); //_log.debug("Signature: " + getSignature());
resetCache(); resetCache();
} }
private byte[] getBytes() throws DataFormatException { private byte[] getBytes() throws DataFormatException {
if (_byteified != null) return _byteified; if (_byteified != null) return _byteified;
if (_identity == null) throw new IllegalStateException("Router identity isn't set? wtf!"); if (_identity == null) throw new IllegalStateException("Router identity isn't set? wtf!");
if (_addresses == null) throw new IllegalStateException("Router addressess isn't set? wtf!"); if (_addresses == null) throw new IllegalStateException("Router addressess isn't set? wtf!");
if (_peers == null) throw new IllegalStateException("Router peers isn't set? wtf!"); if (_peers == null) throw new IllegalStateException("Router peers isn't set? wtf!");
if (_options == null) throw new IllegalStateException("Router options isn't set? wtf!"); if (_options == null) throw new IllegalStateException("Router options isn't set? wtf!");
long before = Clock.getInstance().now(); long before = Clock.getInstance().now();
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
try { try {
_identity.writeBytes(out); _identity.writeBytes(out);
DataHelper.writeDate(out, new Date(_published)); DataHelper.writeDate(out, new Date(_published));
DataHelper.writeLong(out, 1, _addresses.size()); DataHelper.writeLong(out, 1, _addresses.size());
List addresses = DataHelper.sortStructures(_addresses); List addresses = DataHelper.sortStructures(_addresses);
for (Iterator iter = addresses.iterator(); iter.hasNext(); ) { for (Iterator iter = addresses.iterator(); iter.hasNext();) {
RouterAddress addr = (RouterAddress)iter.next(); RouterAddress addr = (RouterAddress) iter.next();
addr.writeBytes(out); addr.writeBytes(out);
} }
DataHelper.writeLong(out, 1, _peers.size()); DataHelper.writeLong(out, 1, _peers.size());
List peers = DataHelper.sortStructures(_peers); List peers = DataHelper.sortStructures(_peers);
for (Iterator iter = peers.iterator(); iter.hasNext(); ) { for (Iterator iter = peers.iterator(); iter.hasNext();) {
Hash peerHash = (Hash)iter.next(); Hash peerHash = (Hash) iter.next();
peerHash.writeBytes(out); peerHash.writeBytes(out);
} }
DataHelper.writeProperties(out, _options); DataHelper.writeProperties(out, _options);
} catch (IOException ioe) { } catch (IOException ioe) {
throw new DataFormatException("IO Error getting bytes", ioe); throw new DataFormatException("IO Error getting bytes", ioe);
} }
byte data[] = out.toByteArray(); byte data[] = out.toByteArray();
long after = Clock.getInstance().now(); long after = Clock.getInstance().now();
_log.debug("getBytes() took " + (after-before) + "ms"); _log.debug("getBytes() took " + (after - before) + "ms");
_byteified = data; _byteified = data;
return data; return data;
} }
/** /**
* Determine whether this router info is authorized with a valid signature * Determine whether this router info is authorized with a valid signature
* *
*/ */
public synchronized boolean isValid() { public synchronized boolean isValid() {
if (!_validated) if (!_validated) doValidate();
doValidate();
return _isValid; return _isValid;
} }
/** /**
* Get the routing key for the structure using the current modifier in the RoutingKeyGenerator. * Get the routing key for the structure using the current modifier in the RoutingKeyGenerator.
* This only calculates a new one when necessary though (if the generator's key modifier changes) * This only calculates a new one when necessary though (if the generator's key modifier changes)
* *
*/ */
public synchronized Hash getRoutingKey() { public synchronized Hash getRoutingKey() {
RoutingKeyGenerator gen = RoutingKeyGenerator.getInstance(); RoutingKeyGenerator gen = RoutingKeyGenerator.getInstance();
if ( (gen.getModData() == null) || (_routingKeyGenMod == null) || (!DataHelper.eq(gen.getModData(), _routingKeyGenMod)) ) { if ((gen.getModData() == null) || (_routingKeyGenMod == null)
setRoutingKey(gen.getRoutingKey(getIdentity().getHash())); || (!DataHelper.eq(gen.getModData(), _routingKeyGenMod))) {
_routingKeyGenMod = gen.getModData(); setRoutingKey(gen.getRoutingKey(getIdentity().getHash()));
} _routingKeyGenMod = gen.getModData();
return _currentRoutingKey; }
return _currentRoutingKey;
} }
public void setRoutingKey(Hash key) { _currentRoutingKey = key; }
public void setRoutingKey(Hash key) {
_currentRoutingKey = key;
}
public boolean validateRoutingKey() { public boolean validateRoutingKey() {
Hash identKey = getIdentity().getHash(); Hash identKey = getIdentity().getHash();
Hash rk = RoutingKeyGenerator.getInstance().getRoutingKey(identKey); Hash rk = RoutingKeyGenerator.getInstance().getRoutingKey(identKey);
if (rk.equals(getRoutingKey())) if (rk.equals(getRoutingKey()))
return true; return true;
else else
return false; return false;
} }
/** /**
* Determine whether the router was published recently (within the given age milliseconds). * Determine whether the router was published recently (within the given age milliseconds).
* The age should be large enough to take into consideration any clock fudge factor, so * The age should be large enough to take into consideration any clock fudge factor, so
@@ -285,56 +306,57 @@ public class RouterInfo extends DataStructureImpl {
* @return true if it was published recently, false otherwise * @return true if it was published recently, false otherwise
*/ */
public boolean isCurrent(long maxAgeMs) { public boolean isCurrent(long maxAgeMs) {
long earliestExpire = Clock.getInstance().now() - maxAgeMs; long earliestExpire = Clock.getInstance().now() - maxAgeMs;
if (getPublished() < earliestExpire) { if (getPublished() < earliestExpire) {
return false; return false;
} else { } else {
return true; return true;
} }
} }
/** /**
* Actually validate the signature * Actually validate the signature
*/ */
private synchronized void doValidate() { private synchronized void doValidate() {
_validated = true; _validated = true;
if (getSignature() == null) { if (getSignature() == null) {
_log.error("Signature is null"); _log.error("Signature is null");
_isValid = false; _isValid = false;
return; return;
} }
byte data[] = null; byte data[] = null;
try { try {
data = getBytes(); data = getBytes();
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
_log.error("Error validating", dfe); _log.error("Error validating", dfe);
_isValid = false; _isValid = false;
return; return;
} }
if (data == null) { if (data == null) {
_log.error("Data could not be loaded"); _log.error("Data could not be loaded");
_isValid = false; _isValid = false;
return; return;
} }
_isValid = DSAEngine.getInstance().verifySignature(_signature, data, _identity.getSigningPublicKey()); _isValid = DSAEngine.getInstance().verifySignature(_signature, data, _identity.getSigningPublicKey());
if (!_isValid) { if (!_isValid) {
_log.error("Invalid [" + SHA256Generator.getInstance().calculateHash(data).toBase64() + "] w/ signing key: " + _identity.getSigningPublicKey(), new Exception("Signature failed")); _log.error("Invalid [" + SHA256Generator.getInstance().calculateHash(data).toBase64()
_log.debug("Failed data: \n" + Base64.encode(data)); + "] w/ signing key: " + _identity.getSigningPublicKey(), new Exception("Signature failed"));
_log.debug("Signature: " + getSignature()); _log.debug("Failed data: \n" + Base64.encode(data));
} _log.debug("Signature: " + getSignature());
}
} }
public synchronized void readBytes(InputStream in) throws DataFormatException, IOException { public synchronized void readBytes(InputStream in) throws DataFormatException, IOException {
_identity = new RouterIdentity(); _identity = new RouterIdentity();
_identity.readBytes(in); _identity.readBytes(in);
_published = DataHelper.readDate(in).getTime(); _published = DataHelper.readDate(in).getTime();
int numAddresses = (int)DataHelper.readLong(in, 1); int numAddresses = (int) DataHelper.readLong(in, 1);
for (int i = 0; i < numAddresses; i++) { for (int i = 0; i < numAddresses; i++) {
RouterAddress address = new RouterAddress(); RouterAddress address = new RouterAddress();
address.readBytes(in); address.readBytes(in);
_addresses.add(address); _addresses.add(address);
} }
int numPeers = (int)DataHelper.readLong(in, 1); int numPeers = (int) DataHelper.readLong(in, 1);
for (int i = 0; i < numPeers; i++) { for (int i = 0; i < numPeers; i++) {
Hash peerIdentityHash = new Hash(); Hash peerIdentityHash = new Hash();
peerIdentityHash.readBytes(in); peerIdentityHash.readBytes(in);
@@ -343,66 +365,56 @@ public class RouterInfo extends DataStructureImpl {
_options = DataHelper.readProperties(in); _options = DataHelper.readProperties(in);
_signature = new Signature(); _signature = new Signature();
_signature.readBytes(in); _signature.readBytes(in);
resetCache(); resetCache();
//_log.debug("Read routerInfo: " + toString()); //_log.debug("Read routerInfo: " + toString());
} }
public synchronized void writeBytes(OutputStream out) throws DataFormatException, IOException { public synchronized void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_identity == null) if (_identity == null) throw new DataFormatException("Missing identity");
throw new DataFormatException("Missing identity"); if (_published <= 0) throw new DataFormatException("Invalid published date: " + _published);
if (_published <= 0) if (_signature == null) throw new DataFormatException("Signature is null");
throw new DataFormatException("Invalid published date: " + _published); //if (!isValid())
if (_signature == null) // throw new DataFormatException("Data is not valid");
throw new DataFormatException("Signature is null"); ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
//if (!isValid())
// throw new DataFormatException("Data is not valid");
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
_identity.writeBytes(baos); _identity.writeBytes(baos);
DataHelper.writeDate(baos, new Date(_published)); DataHelper.writeDate(baos, new Date(_published));
DataHelper.writeLong(baos, 1, _addresses.size()); DataHelper.writeLong(baos, 1, _addresses.size());
for (Iterator iter = _addresses.iterator(); iter.hasNext(); ) { for (Iterator iter = _addresses.iterator(); iter.hasNext();) {
RouterAddress addr = (RouterAddress)iter.next(); RouterAddress addr = (RouterAddress) iter.next();
addr.writeBytes(baos); addr.writeBytes(baos);
} }
DataHelper.writeLong(baos, 1, _peers.size()); DataHelper.writeLong(baos, 1, _peers.size());
for (Iterator iter = _peers.iterator(); iter.hasNext(); ) { for (Iterator iter = _peers.iterator(); iter.hasNext();) {
Hash peerHash = (Hash)iter.next(); Hash peerHash = (Hash) iter.next();
peerHash.writeBytes(baos); peerHash.writeBytes(baos);
} }
DataHelper.writeProperties(baos, _options); DataHelper.writeProperties(baos, _options);
_signature.writeBytes(baos); _signature.writeBytes(baos);
byte data[] = baos.toByteArray(); byte data[] = baos.toByteArray();
//_log.debug("Writing routerInfo [len=" + data.length + "]: " + toString()); //_log.debug("Writing routerInfo [len=" + data.length + "]: " + toString());
out.write(data); out.write(data);
} }
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object == null) || !(object instanceof RouterInfo) ) if ((object == null) || !(object instanceof RouterInfo)) return false;
return false; RouterInfo info = (RouterInfo) object;
RouterInfo info = (RouterInfo)object; return DataHelper.eq(getAddresses(), info.getAddresses()) && DataHelper.eq(getIdentity(), info.getIdentity())
return DataHelper.eq(getAddresses(), info.getAddresses()) && && DataHelper.eq(getOptions(), info.getOptions()) && DataHelper.eq(getPeers(), info.getPeers())
DataHelper.eq(getIdentity(), info.getIdentity()) && && DataHelper.eq(getSignature(), info.getSignature())
DataHelper.eq(getOptions(), info.getOptions()) && && DataHelper.eq(getPublished(), info.getPublished());
DataHelper.eq(getPeers(), info.getPeers()) &&
DataHelper.eq(getSignature(), info.getSignature()) &&
DataHelper.eq(getPublished(), info.getPublished());
} }
public int hashCode() { public int hashCode() {
return DataHelper.hashCode(getAddresses()) + return DataHelper.hashCode(getAddresses()) + DataHelper.hashCode(getIdentity())
DataHelper.hashCode(getIdentity()) + + DataHelper.hashCode(getOptions()) + DataHelper.hashCode(getPeers())
DataHelper.hashCode(getOptions()) + + DataHelper.hashCode(getSignature()) + (int) getPublished();
DataHelper.hashCode(getPeers()) +
DataHelper.hashCode(getSignature()) +
(int)getPublished();
} }
public String toString() { public String toString() {
if (_stringified != null) if (_stringified != null) return _stringified;
return _stringified;
StringBuffer buf = new StringBuffer(128); StringBuffer buf = new StringBuffer(128);
buf.append("[RouterInfo: "); buf.append("[RouterInfo: ");
buf.append("\n\tIdentity: ").append(getIdentity()); buf.append("\n\tIdentity: ").append(getIdentity());
@@ -410,23 +422,23 @@ public class RouterInfo extends DataStructureImpl {
buf.append("\n\tPublished on: ").append(new Date(getPublished())); buf.append("\n\tPublished on: ").append(new Date(getPublished()));
buf.append("\n\tAddresses: #: ").append(getAddresses().size()); buf.append("\n\tAddresses: #: ").append(getAddresses().size());
for (Iterator iter = getAddresses().iterator(); iter.hasNext();) { for (Iterator iter = getAddresses().iterator(); iter.hasNext();) {
RouterAddress addr = (RouterAddress)iter.next(); RouterAddress addr = (RouterAddress) iter.next();
buf.append("\n\t\tAddress: ").append(addr); buf.append("\n\t\tAddress: ").append(addr);
} }
buf.append("\n\tPeers: #: ").append(getPeers().size()); buf.append("\n\tPeers: #: ").append(getPeers().size());
for (Iterator iter = getPeers().iterator(); iter.hasNext();) { for (Iterator iter = getPeers().iterator(); iter.hasNext();) {
Hash hash = (Hash)iter.next(); Hash hash = (Hash) iter.next();
buf.append("\n\t\tPeer hash: ").append(hash); buf.append("\n\t\tPeer hash: ").append(hash);
} }
Properties options = getOptions(); Properties options = getOptions();
buf.append("\n\tOptions: #: ").append(options.size()); buf.append("\n\tOptions: #: ").append(options.size());
for (Iterator iter = options.keySet().iterator(); iter.hasNext();) { for (Iterator iter = options.keySet().iterator(); iter.hasNext();) {
String key = (String)iter.next(); String key = (String) iter.next();
String val = options.getProperty(key); String val = options.getProperty(key);
buf.append("\n\t\t[").append(key).append("] = [").append(val).append("]"); buf.append("\n\t\t[").append(key).append("] = [").append(val).append("]");
} }
buf.append("]"); buf.append("]");
_stringified = buf.toString(); _stringified = buf.toString();
return _stringified; return _stringified;
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,17 +9,16 @@ package net.i2p.data;
* *
*/ */
import net.i2p.crypto.SHA256Generator; import java.text.SimpleDateFormat;
import net.i2p.util.Log;
import net.i2p.util.RandomSource;
import net.i2p.util.Clock;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.Date;
import java.text.SimpleDateFormat; import net.i2p.crypto.SHA256Generator;
import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.util.RandomSource;
/** /**
* Component to manage the munging of hashes into routing keys - given a hash, * Component to manage the munging of hashes into routing keys - given a hash,
@@ -41,41 +41,47 @@ import java.text.SimpleDateFormat;
*/ */
public class RoutingKeyGenerator { public class RoutingKeyGenerator {
private final static RoutingKeyGenerator _instance = new RoutingKeyGenerator(); private final static RoutingKeyGenerator _instance = new RoutingKeyGenerator();
public static RoutingKeyGenerator getInstance() { return _instance; }
public static RoutingKeyGenerator getInstance() {
return _instance;
}
private final static Log _log = new Log(RoutingKeyGenerator.class); private final static Log _log = new Log(RoutingKeyGenerator.class);
private byte _currentModData[]; private byte _currentModData[];
private final static Calendar _cal = GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT")); private final static Calendar _cal = GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT"));
private final static SimpleDateFormat _fmt = new SimpleDateFormat("yyyyMMdd"); private final static SimpleDateFormat _fmt = new SimpleDateFormat("yyyyMMdd");
public byte[] getModData() { return _currentModData; } public byte[] getModData() {
public void setModData(byte modData[]) { return _currentModData;
_currentModData = modData;
} }
public void setModData(byte modData[]) {
_currentModData = modData;
}
/** /**
* Update the current modifier data with some bytes derived from the current * Update the current modifier data with some bytes derived from the current
* date (yyyyMMdd in GMT) * date (yyyyMMdd in GMT)
* *
*/ */
public void generateDateBasedModData() { public void generateDateBasedModData() {
Date today = null; Date today = null;
synchronized (_cal) { synchronized (_cal) {
_cal.setTime(new Date(Clock.getInstance().now())); _cal.setTime(new Date(Clock.getInstance().now()));
_cal.set(Calendar.HOUR_OF_DAY, 0); _cal.set(Calendar.HOUR_OF_DAY, 0);
_cal.set(Calendar.MINUTE, 0); _cal.set(Calendar.MINUTE, 0);
_cal.set(Calendar.SECOND, 0); _cal.set(Calendar.SECOND, 0);
_cal.set(Calendar.MILLISECOND, 0); _cal.set(Calendar.MILLISECOND, 0);
today = _cal.getTime(); today = _cal.getTime();
} }
byte mod[] = null; byte mod[] = null;
synchronized (_fmt) { synchronized (_fmt) {
mod = _fmt.format(today).getBytes(); mod = _fmt.format(today).getBytes();
} }
_log.info("Routing modifier generated: " + new String(mod)); _log.info("Routing modifier generated: " + new String(mod));
setModData(mod); setModData(mod);
} }
/** /**
* Generate a modified (yet consistent) hash from the origKey by generating the * Generate a modified (yet consistent) hash from the origKey by generating the
* SHA256 of the targetKey with the current modData appended to it, *then* * SHA256 of the targetKey with the current modData appended to it, *then*
@@ -86,27 +92,29 @@ public class RoutingKeyGenerator {
* @throws IllegalArgumentException if origKey is null * @throws IllegalArgumentException if origKey is null
*/ */
public Hash getRoutingKey(Hash origKey) { public Hash getRoutingKey(Hash origKey) {
if (origKey == null) if (origKey == null) throw new IllegalArgumentException("Original key is null");
throw new IllegalArgumentException("Original key is null"); if (_currentModData == null) generateDateBasedModData();
if (_currentModData == null) generateDateBasedModData(); byte modVal[] = new byte[Hash.HASH_LENGTH + _currentModData.length];
byte modVal[] = new byte[Hash.HASH_LENGTH+_currentModData.length]; System.arraycopy(origKey.getData(), 0, modVal, 0, Hash.HASH_LENGTH);
System.arraycopy(origKey.getData(), 0, modVal, 0, Hash.HASH_LENGTH); System.arraycopy(_currentModData, 0, modVal, Hash.HASH_LENGTH, _currentModData.length);
System.arraycopy(_currentModData, 0, modVal, Hash.HASH_LENGTH, _currentModData.length); return SHA256Generator.getInstance().calculateHash(modVal);
return SHA256Generator.getInstance().calculateHash(modVal);
} }
public static void main(String args[]) {
Hash k1 = new Hash();
byte k1d[] = new byte[Hash.HASH_LENGTH];
RandomSource.getInstance().nextBytes(k1d);
k1.setData(k1d);
for (int i = 0; i < 10; i++) { public static void main(String args[]) {
System.out.println("K1: " + k1); Hash k1 = new Hash();
Hash k1m = RoutingKeyGenerator.getInstance().getRoutingKey(k1); byte k1d[] = new byte[Hash.HASH_LENGTH];
System.out.println("MOD: " + new String(RoutingKeyGenerator.getInstance().getModData())); RandomSource.getInstance().nextBytes(k1d);
System.out.println("K1M: " + k1m); k1.setData(k1d);
}
try { Thread.sleep(2000); } catch (Throwable t) {} for (int i = 0; i < 10; i++) {
System.out.println("K1: " + k1);
Hash k1m = RoutingKeyGenerator.getInstance().getRoutingKey(k1);
System.out.println("MOD: " + new String(RoutingKeyGenerator.getInstance().getModData()));
System.out.println("K1M: " + k1m);
}
try {
Thread.sleep(2000);
} catch (Throwable t) {
}
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,9 +9,9 @@ package net.i2p.data;
* *
*/ */
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException;
import net.i2p.util.Log; import net.i2p.util.Log;
@@ -23,40 +24,43 @@ import net.i2p.util.Log;
public class SessionKey extends DataStructureImpl { public class SessionKey extends DataStructureImpl {
private final static Log _log = new Log(SessionKey.class); private final static Log _log = new Log(SessionKey.class);
private byte[] _data; private byte[] _data;
public final static int KEYSIZE_BYTES = 32; public final static int KEYSIZE_BYTES = 32;
public SessionKey() { setData(null); } public SessionKey() {
setData(null);
public byte[] getData() { return _data; } }
public void setData(byte[] data) { _data = data; }
public byte[] getData() {
return _data;
}
public void setData(byte[] data) {
_data = data;
}
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
_data = new byte[KEYSIZE_BYTES]; _data = new byte[KEYSIZE_BYTES];
int read = read(in, _data); int read = read(in, _data);
if (read != KEYSIZE_BYTES) if (read != KEYSIZE_BYTES) throw new DataFormatException("Not enough bytes to read the session key");
throw new DataFormatException("Not enough bytes to read the session key");
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_data == null) if (_data == null) throw new DataFormatException("No data in the session key to write out");
throw new DataFormatException("No data in the session key to write out"); if (_data.length != KEYSIZE_BYTES) throw new DataFormatException("Invalid size of data in the private key");
if (_data.length != KEYSIZE_BYTES)
throw new DataFormatException("Invalid size of data in the private key");
out.write(_data); out.write(_data);
} }
public boolean equals(Object obj) { public boolean equals(Object obj) {
if ( (obj == null) || !(obj instanceof SessionKey)) if ((obj == null) || !(obj instanceof SessionKey)) return false;
return false; return DataHelper.eq(_data, ((SessionKey) obj)._data);
return DataHelper.eq(_data, ((SessionKey)obj)._data);
} }
public int hashCode() { public int hashCode() {
return DataHelper.hashCode(_data); return DataHelper.hashCode(_data);
} }
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(64); StringBuffer buf = new StringBuffer(64);
buf.append("[SessionKey: "); buf.append("[SessionKey: ");
if (_data == null) { if (_data == null) {
@@ -64,12 +68,11 @@ public class SessionKey extends DataStructureImpl {
} else { } else {
buf.append("size: ").append(_data.length); buf.append("size: ").append(_data.length);
int len = 32; int len = 32;
if (len > _data.length) if (len > _data.length) len = _data.length;
len = _data.length;
buf.append(" first ").append(len).append(" bytes: "); buf.append(" first ").append(len).append(" bytes: ");
buf.append(DataHelper.toString(_data, len)); buf.append(DataHelper.toString(_data, len));
} }
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -12,23 +13,29 @@ import net.i2p.util.RandomSource;
public class SessionTag extends ByteArray { public class SessionTag extends ByteArray {
public final static int BYTE_LENGTH = 32; public final static int BYTE_LENGTH = 32;
public SessionTag() { super(); }
public SessionTag() {
super();
}
public SessionTag(boolean create) { public SessionTag(boolean create) {
super(); super();
if (create) { if (create) {
byte buf[] = new byte[BYTE_LENGTH]; byte buf[] = new byte[BYTE_LENGTH];
RandomSource.getInstance().nextBytes(buf); RandomSource.getInstance().nextBytes(buf);
setData(buf); setData(buf);
} }
} }
public SessionTag(byte val[]) { public SessionTag(byte val[]) {
super(); super();
setData(val); setData(val);
} }
public void setData(byte val[]) throws IllegalArgumentException { public void setData(byte val[]) throws IllegalArgumentException {
if (val == null) super.setData(null); if (val == null) super.setData(null);
if (val.length != BYTE_LENGTH) throw new IllegalArgumentException("SessionTags must be " + BYTE_LENGTH + " bytes"); if (val.length != BYTE_LENGTH)
super.setData(val); throw new IllegalArgumentException("SessionTags must be " + BYTE_LENGTH + " bytes");
super.setData(val);
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,9 +9,9 @@ package net.i2p.data;
* *
*/ */
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException;
import net.i2p.util.Log; import net.i2p.util.Log;
@@ -24,45 +25,48 @@ import net.i2p.util.Log;
public class Signature extends DataStructureImpl { public class Signature extends DataStructureImpl {
private final static Log _log = new Log(Signature.class); private final static Log _log = new Log(Signature.class);
private byte[] _data; private byte[] _data;
public final static int SIGNATURE_BYTES = 40; public final static int SIGNATURE_BYTES = 40;
public final static byte[] FAKE_SIGNATURE = new byte[SIGNATURE_BYTES]; public final static byte[] FAKE_SIGNATURE = new byte[SIGNATURE_BYTES];
static { static {
for (int i = 0; i < SIGNATURE_BYTES; i++) for (int i = 0; i < SIGNATURE_BYTES; i++)
FAKE_SIGNATURE[i] = 0x00; FAKE_SIGNATURE[i] = 0x00;
} }
public Signature() { setData(null); } public Signature() {
setData(null);
public byte[] getData() { return _data; } }
public void setData(byte[] data) { _data = data; }
public byte[] getData() {
return _data;
}
public void setData(byte[] data) {
_data = data;
}
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
_data = new byte[SIGNATURE_BYTES]; _data = new byte[SIGNATURE_BYTES];
int read = read(in, _data); int read = read(in, _data);
if (read != SIGNATURE_BYTES) if (read != SIGNATURE_BYTES) throw new DataFormatException("Not enough bytes to read the signature");
throw new DataFormatException("Not enough bytes to read the signature");
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_data == null) if (_data == null) throw new DataFormatException("No data in the signature to write out");
throw new DataFormatException("No data in the signature to write out"); if (_data.length != SIGNATURE_BYTES) throw new DataFormatException("Invalid size of data in the private key");
if (_data.length != SIGNATURE_BYTES)
throw new DataFormatException("Invalid size of data in the private key");
out.write(_data); out.write(_data);
} }
public boolean equals(Object obj) { public boolean equals(Object obj) {
if ( (obj == null) || !(obj instanceof Signature)) if ((obj == null) || !(obj instanceof Signature)) return false;
return false; return DataHelper.eq(_data, ((Signature) obj)._data);
return DataHelper.eq(_data, ((Signature)obj)._data);
} }
public int hashCode() { public int hashCode() {
return DataHelper.hashCode(_data); return DataHelper.hashCode(_data);
} }
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(64); StringBuffer buf = new StringBuffer(64);
buf.append("[Signature: "); buf.append("[Signature: ");
if (_data == null) { if (_data == null) {
@@ -70,12 +74,11 @@ public class Signature extends DataStructureImpl {
} else { } else {
buf.append("size: ").append(_data.length); buf.append("size: ").append(_data.length);
int len = 32; int len = 32;
if (len > _data.length) if (len > _data.length) len = _data.length;
len = _data.length;
buf.append(" first ").append(len).append(" bytes: "); buf.append(" first ").append(len).append(" bytes: ");
buf.append(DataHelper.toString(_data, len)); buf.append(DataHelper.toString(_data, len));
} }
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,9 +9,9 @@ package net.i2p.data;
* *
*/ */
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException;
import net.i2p.util.Log; import net.i2p.util.Log;
@@ -25,40 +26,43 @@ import net.i2p.util.Log;
public class SigningPrivateKey extends DataStructureImpl { public class SigningPrivateKey extends DataStructureImpl {
private final static Log _log = new Log(SigningPrivateKey.class); private final static Log _log = new Log(SigningPrivateKey.class);
private byte[] _data; private byte[] _data;
public final static int KEYSIZE_BYTES = 20; public final static int KEYSIZE_BYTES = 20;
public SigningPrivateKey() { setData(null); } public SigningPrivateKey() {
setData(null);
public byte[] getData() { return _data; } }
public void setData(byte[] data) { _data = data; }
public byte[] getData() {
return _data;
}
public void setData(byte[] data) {
_data = data;
}
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
_data = new byte[KEYSIZE_BYTES]; _data = new byte[KEYSIZE_BYTES];
int read = read(in, _data); int read = read(in, _data);
if (read != KEYSIZE_BYTES) if (read != KEYSIZE_BYTES) throw new DataFormatException("Not enough bytes to read the private key");
throw new DataFormatException("Not enough bytes to read the private key");
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_data == null) if (_data == null) throw new DataFormatException("No data in the private key to write out");
throw new DataFormatException("No data in the private key to write out"); if (_data.length != KEYSIZE_BYTES) throw new DataFormatException("Invalid size of data in the private key");
if (_data.length != KEYSIZE_BYTES)
throw new DataFormatException("Invalid size of data in the private key");
out.write(_data); out.write(_data);
} }
public boolean equals(Object obj) { public boolean equals(Object obj) {
if ( (obj == null) || !(obj instanceof SigningPrivateKey)) if ((obj == null) || !(obj instanceof SigningPrivateKey)) return false;
return false; return DataHelper.eq(_data, ((SigningPrivateKey) obj)._data);
return DataHelper.eq(_data, ((SigningPrivateKey)obj)._data);
} }
public int hashCode() { public int hashCode() {
return DataHelper.hashCode(_data); return DataHelper.hashCode(_data);
} }
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(64); StringBuffer buf = new StringBuffer(64);
buf.append("[SigningPrivateKey: "); buf.append("[SigningPrivateKey: ");
if (_data == null) { if (_data == null) {
@@ -66,12 +70,11 @@ public class SigningPrivateKey extends DataStructureImpl {
} else { } else {
buf.append("size: ").append(_data.length); buf.append("size: ").append(_data.length);
int len = 32; int len = 32;
if (len > _data.length) if (len > _data.length) len = _data.length;
len = _data.length;
buf.append(" first ").append(len).append(" bytes: "); buf.append(" first ").append(len).append(" bytes: ");
buf.append(DataHelper.toString(_data, len)); buf.append(DataHelper.toString(_data, len));
} }
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,9 +9,9 @@ package net.i2p.data;
* *
*/ */
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException;
import net.i2p.util.Log; import net.i2p.util.Log;
@@ -25,39 +26,43 @@ import net.i2p.util.Log;
public class SigningPublicKey extends DataStructureImpl { public class SigningPublicKey extends DataStructureImpl {
private final static Log _log = new Log(SigningPublicKey.class); private final static Log _log = new Log(SigningPublicKey.class);
private byte[] _data; private byte[] _data;
public final static int KEYSIZE_BYTES = 128; public final static int KEYSIZE_BYTES = 128;
public SigningPublicKey() { setData(null); } public SigningPublicKey() {
setData(null);
public byte[] getData() { return _data; } }
public void setData(byte[] data) { _data = data; }
public byte[] getData() {
return _data;
}
public void setData(byte[] data) {
_data = data;
}
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
_data = new byte[KEYSIZE_BYTES]; _data = new byte[KEYSIZE_BYTES];
int read = read(in, _data); int read = read(in, _data);
if (read != KEYSIZE_BYTES) if (read != KEYSIZE_BYTES) throw new DataFormatException("Not enough bytes to read the public key");
throw new DataFormatException("Not enough bytes to read the public key");
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_data == null) if (_data == null) throw new DataFormatException("No data in the public key to write out");
throw new DataFormatException("No data in the public key to write out"); if (_data.length != KEYSIZE_BYTES) throw new DataFormatException("Invalid size of data in the public key");
if (_data.length != KEYSIZE_BYTES)
throw new DataFormatException("Invalid size of data in the public key");
out.write(_data); out.write(_data);
} }
public boolean equals(Object obj) { public boolean equals(Object obj) {
if ( (obj == null) || !(obj instanceof SigningPublicKey)) if ((obj == null) || !(obj instanceof SigningPublicKey)) return false;
return false; return DataHelper.eq(_data, ((SigningPublicKey) obj)._data);
return DataHelper.eq(_data, ((SigningPublicKey)obj)._data);
} }
public int hashCode() { public int hashCode() {
return DataHelper.hashCode(_data); return DataHelper.hashCode(_data);
} }
public String toString() {
public String toString() {
StringBuffer buf = new StringBuffer(64); StringBuffer buf = new StringBuffer(64);
buf.append("[SigningPublicKey: "); buf.append("[SigningPublicKey: ");
if (_data == null) { if (_data == null) {
@@ -65,12 +70,11 @@ public class SigningPublicKey extends DataStructureImpl {
} else { } else {
buf.append("size: ").append(_data.length); buf.append("size: ").append(_data.length);
int len = 32; int len = 32;
if (len > _data.length) if (len > _data.length) len = _data.length;
len = _data.length;
buf.append(" first ").append(len).append(" bytes: "); buf.append(" first ").append(len).append(" bytes: ");
buf.append(DataHelper.toString(_data, len)); buf.append(DataHelper.toString(_data, len));
} }
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
} }

View File

@@ -8,9 +8,9 @@ package net.i2p.data;
* *
*/ */
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException;
import net.i2p.util.Log; import net.i2p.util.Log;

View File

@@ -1,4 +1,5 @@
package net.i2p.data; package net.i2p.data;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,10 +9,10 @@ package net.i2p.data;
* *
*/ */
import net.i2p.util.Log;
import java.math.BigInteger; import java.math.BigInteger;
import net.i2p.util.Log;
/** /**
* Manage an arbitrarily large unsigned integer, using the first bit and first byte * Manage an arbitrarily large unsigned integer, using the first bit and first byte
* as the most significant one. Also allows the exporting to byte arrays with whatever * as the most significant one. Also allows the exporting to byte arrays with whatever
@@ -26,7 +27,7 @@ public class UnsignedInteger {
private final static Log _log = new Log(UnsignedInteger.class); private final static Log _log = new Log(UnsignedInteger.class);
private byte[] _data; private byte[] _data;
private long _value; private long _value;
/** /**
* Construct the integer from the bytes given, making the value accessible * Construct the integer from the bytes given, making the value accessible
* immediately. * immediately.
@@ -46,11 +47,11 @@ public class UnsignedInteger {
} }
_data = new byte[data.length - start]; _data = new byte[data.length - start];
for (int i = 0; i < _data.length; i++) for (int i = 0; i < _data.length; i++)
_data[i] = data[i+start]; _data[i] = data[i + start];
// done stripping excess bytes, now calc // done stripping excess bytes, now calc
_value = calculateValue(_data); _value = calculateValue(_data);
} }
/** /**
* Construct the integer with the java number given, making the bytes * Construct the integer with the java number given, making the bytes
* available immediately. * available immediately.
@@ -61,114 +62,132 @@ public class UnsignedInteger {
_value = value; _value = value;
_data = calculateBytes(value); _data = calculateBytes(value);
} }
/** /**
* Calculate the value of the array of bytes, treating it as an unsigned integer * Calculate the value of the array of bytes, treating it as an unsigned integer
* with the most significant bit and byte first * with the most significant bit and byte first
* *
*/ */
private static long calculateValue(byte[] data) { private static long calculateValue(byte[] data) {
if (data == null) { if (data == null) {
_log.error("Null data to be calculating for", new Exception("Argh")); _log.error("Null data to be calculating for", new Exception("Argh"));
return 0; return 0;
} else if (data.length == 0) { } else if (data.length == 0) { return 0; }
return 0;
}
BigInteger bi = new BigInteger(1, data); BigInteger bi = new BigInteger(1, data);
return bi.longValue(); return bi.longValue();
} }
/** /**
* hexify the byte array * hexify the byte array
* *
*/ */
private final static String toString(byte[] val) { private final static String toString(byte[] val) {
return "0x" + DataHelper.toString(val, val.length); return "0x" + DataHelper.toString(val, val.length);
} }
/** /**
* Calculate the bytes as an unsigned integer with the most significant bit and byte in the first position * Calculate the bytes as an unsigned integer with the most significant bit and byte in the first position
*/ */
private static byte[] calculateBytes(long value) { private static byte[] calculateBytes(long value) {
BigInteger bi = new BigInteger(""+value); BigInteger bi = new BigInteger("" + value);
byte buf[] = bi.toByteArray(); byte buf[] = bi.toByteArray();
if ( (buf == null) || (buf.length <= 0) ) if ((buf == null) || (buf.length <= 0))
throw new IllegalArgumentException("Value [" + value + "] cannot be transformed"); throw new IllegalArgumentException("Value [" + value + "] cannot be transformed");
int trim = 0; int trim = 0;
while ( (trim < buf.length) && (buf[trim] == 0x00) ) while ((trim < buf.length) && (buf[trim] == 0x00))
trim++; trim++;
byte rv[] = new byte[buf.length - trim]; byte rv[] = new byte[buf.length - trim];
System.arraycopy(buf, trim, rv, 0, rv.length); System.arraycopy(buf, trim, rv, 0, rv.length);
return rv; return rv;
} }
/** /**
* Get the unsigned bytes, most significant bit and bytes first, without any padding * Get the unsigned bytes, most significant bit and bytes first, without any padding
* *
*/ */
public byte[] getBytes() { return _data; } public byte[] getBytes() {
return _data;
}
/** /**
* Get the unsigned bytes, most significant bit and bytes first, zero padded to the * Get the unsigned bytes, most significant bit and bytes first, zero padded to the
* specified number of bytes * specified number of bytes
* *
* @throws IllegalArgumentException if numBytes < necessary number of bytes * @throws IllegalArgumentException if numBytes < necessary number of bytes
*/ */
public byte[] getBytes(int numBytes) throws IllegalArgumentException { public byte[] getBytes(int numBytes) throws IllegalArgumentException {
if ( (_data == null) || (numBytes < _data.length) ) if ((_data == null) || (numBytes < _data.length))
throw new IllegalArgumentException("Value (" +_value+") is greater than the requested number of bytes ("+numBytes+")"); throw new IllegalArgumentException("Value (" + _value + ") is greater than the requested number of bytes ("
+ numBytes + ")");
byte[] data = new byte[numBytes]; byte[] data = new byte[numBytes];
System.arraycopy(_data, 0, data, numBytes - _data.length, _data.length); System.arraycopy(_data, 0, data, numBytes - _data.length, _data.length);
return data; return data;
} }
public BigInteger getBigInteger() { return new BigInteger(1, _data); } public BigInteger getBigInteger() {
public long getLong() { return _value; } return new BigInteger(1, _data);
public int getInt() { return (int)_value; } }
public short getShort() { return (short)_value; }
public long getLong() {
return _value;
}
public int getInt() {
return (int) _value;
}
public short getShort() {
return (short) _value;
}
public boolean equals(Object obj) { public boolean equals(Object obj) {
if ( (obj != null) && (obj instanceof UnsignedInteger) ) { if ((obj != null) && (obj instanceof UnsignedInteger)) {
return DataHelper.eq(_data, ((UnsignedInteger)obj)._data) && return DataHelper.eq(_data, ((UnsignedInteger) obj)._data)
DataHelper.eq(_value, ((UnsignedInteger)obj)._value); && DataHelper.eq(_value, ((UnsignedInteger) obj)._value);
} else { } else {
return false; return false;
} }
} }
public int hashCode() { public int hashCode() {
return DataHelper.hashCode(_data) + (int)_value; return DataHelper.hashCode(_data) + (int) _value;
} }
public String toString() { public String toString() {
return "UnsignedInteger: " + getLong() + "/" + toString(getBytes()); return "UnsignedInteger: " + getLong() + "/" + toString(getBytes());
} }
public static void main(String args[]) { public static void main(String args[]) {
_log.debug("Testing 1024"); _log.debug("Testing 1024");
testNum(1024L); testNum(1024L);
_log.debug("Testing 1025"); _log.debug("Testing 1025");
testNum(1025L); testNum(1025L);
_log.debug("Testing 2Gb-1"); _log.debug("Testing 2Gb-1");
testNum(1024*1024*1024*2L-1L); testNum(1024 * 1024 * 1024 * 2L - 1L);
_log.debug("Testing 4Gb-1"); _log.debug("Testing 4Gb-1");
testNum(1024*1024*1024*4L-1L); testNum(1024 * 1024 * 1024 * 4L - 1L);
_log.debug("Testing 4Gb"); _log.debug("Testing 4Gb");
testNum(1024*1024*1024*4L); testNum(1024 * 1024 * 1024 * 4L);
_log.debug("Testing 4Gb+1"); _log.debug("Testing 4Gb+1");
testNum(1024*1024*1024*4L+1L); testNum(1024 * 1024 * 1024 * 4L + 1L);
_log.debug("Testing MaxLong"); _log.debug("Testing MaxLong");
testNum(Long.MAX_VALUE); testNum(Long.MAX_VALUE);
try { Thread.sleep(1000); } catch (Throwable t) {} try {
Thread.sleep(1000);
} catch (Throwable t) {
}
} }
private static void testNum(long num) { private static void testNum(long num) {
UnsignedInteger i = new UnsignedInteger(num); UnsignedInteger i = new UnsignedInteger(num);
_log.debug(num + " turned into an unsigned integer: " + i + " (" + i.getLong() + "/" + toString(i.getBytes()) + ")"); _log.debug(num + " turned into an unsigned integer: " + i + " (" + i.getLong() + "/" + toString(i.getBytes())
+ ")");
_log.debug(num + " turned into an BigInteger: " + i.getBigInteger()); _log.debug(num + " turned into an BigInteger: " + i.getBigInteger());
byte[] val = i.getBytes(); byte[] val = i.getBytes();
UnsignedInteger val2 = new UnsignedInteger(val); UnsignedInteger val2 = new UnsignedInteger(val);
_log.debug(num + " turned into a byte array and back again: " + val2 + " (" + val2.getLong() + "/" + toString(val2.getBytes()) + ")"); _log.debug(num + " turned into a byte array and back again: " + val2 + " (" + val2.getLong() + "/"
+ toString(val2.getBytes()) + ")");
_log.debug(num + " As an 8 byte array: " + toString(val2.getBytes(8))); _log.debug(num + " As an 8 byte array: " + toString(val2.getBytes(8)));
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data.i2cp; package net.i2p.data.i2cp;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,15 +9,14 @@ package net.i2p.data.i2cp;
* *
*/ */
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException;
import net.i2p.util.Log;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.DataStructureImpl; import net.i2p.data.DataStructureImpl;
import net.i2p.data.DataFormatException; import net.i2p.util.Log;
/** /**
* Defines the structure for why abuse was reported either by the client to * Defines the structure for why abuse was reported either by the client to
@@ -27,29 +27,37 @@ import net.i2p.data.DataFormatException;
public class AbuseReason extends DataStructureImpl { public class AbuseReason extends DataStructureImpl {
private final static Log _log = new Log(AbuseReason.class); private final static Log _log = new Log(AbuseReason.class);
private String _reason; private String _reason;
public AbuseReason() { setReason(null); } public AbuseReason() {
setReason(null);
public String getReason() { return _reason; } }
public void setReason(String reason) { _reason = reason; }
public String getReason() {
return _reason;
}
public void setReason(String reason) {
_reason = reason;
}
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
_reason = DataHelper.readString(in); _reason = DataHelper.readString(in);
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_reason == null) throw new DataFormatException("Invalid abuse reason"); if (_reason == null) throw new DataFormatException("Invalid abuse reason");
DataHelper.writeString(out, _reason); DataHelper.writeString(out, _reason);
} }
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object == null) || !(object instanceof AbuseReason) ) if ((object == null) || !(object instanceof AbuseReason)) return false;
return false; return DataHelper.eq(getReason(), ((AbuseReason) object).getReason());
return DataHelper.eq(getReason(), ((AbuseReason)object).getReason());
} }
public int hashCode() { return DataHelper.hashCode(getReason()); } public int hashCode() {
return DataHelper.hashCode(getReason());
}
public String toString() { public String toString() {
return "[AbuseReason: " + getReason() + "]"; return "[AbuseReason: " + getReason() + "]";
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data.i2cp; package net.i2p.data.i2cp;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,15 +9,14 @@ package net.i2p.data.i2cp;
* *
*/ */
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException;
import net.i2p.util.Log;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.DataStructureImpl; import net.i2p.data.DataStructureImpl;
import net.i2p.data.DataFormatException; import net.i2p.util.Log;
/** /**
* Provides a severity level (larger numbers are more severe) in association with * Provides a severity level (larger numbers are more severe) in association with
@@ -28,29 +28,37 @@ import net.i2p.data.DataFormatException;
public class AbuseSeverity extends DataStructureImpl { public class AbuseSeverity extends DataStructureImpl {
private final static Log _log = new Log(AbuseSeverity.class); private final static Log _log = new Log(AbuseSeverity.class);
private int _severityId; private int _severityId;
public AbuseSeverity() { setSeverity(-1); } public AbuseSeverity() {
setSeverity(-1);
public int getSeverity() { return _severityId; }
public void setSeverity(int id) { _severityId = id; }
public void readBytes(InputStream in) throws DataFormatException, IOException {
_severityId = (int)DataHelper.readLong(in, 1);
} }
public int getSeverity() {
return _severityId;
}
public void setSeverity(int id) {
_severityId = id;
}
public void readBytes(InputStream in) throws DataFormatException, IOException {
_severityId = (int) DataHelper.readLong(in, 1);
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_severityId < 0) throw new DataFormatException("Invalid abuse severity: " + _severityId); if (_severityId < 0) throw new DataFormatException("Invalid abuse severity: " + _severityId);
DataHelper.writeLong(out, 1, _severityId); DataHelper.writeLong(out, 1, _severityId);
} }
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object == null) || !(object instanceof AbuseSeverity) ) if ((object == null) || !(object instanceof AbuseSeverity)) return false;
return false; return DataHelper.eq(getSeverity(), ((AbuseSeverity) object).getSeverity());
return DataHelper.eq(getSeverity(), ((AbuseSeverity)object).getSeverity());
} }
public int hashCode() { return getSeverity(); } public int hashCode() {
return getSeverity();
}
public String toString() { public String toString() {
return "[AbuseSeverity: " + getSeverity() + "]"; return "[AbuseSeverity: " + getSeverity() + "]";
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data.i2cp; package net.i2p.data.i2cp;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -32,68 +33,93 @@ public class CreateLeaseSetMessage extends I2CPMessageImpl {
private LeaseSet _leaseSet; private LeaseSet _leaseSet;
private SigningPrivateKey _signingPrivateKey; private SigningPrivateKey _signingPrivateKey;
private PrivateKey _privateKey; private PrivateKey _privateKey;
public CreateLeaseSetMessage() { public CreateLeaseSetMessage() {
setSessionId(null); setSessionId(null);
setLeaseSet(null); setLeaseSet(null);
setSigningPrivateKey(null); setSigningPrivateKey(null);
setPrivateKey(null); setPrivateKey(null);
} }
public SessionId getSessionId() { return _sessionId; } public SessionId getSessionId() {
public void setSessionId(SessionId id) { _sessionId = id; } return _sessionId;
public SigningPrivateKey getSigningPrivateKey() { return _signingPrivateKey; } }
public void setSigningPrivateKey(SigningPrivateKey key) { _signingPrivateKey = key; }
public PrivateKey getPrivateKey() { return _privateKey; } public void setSessionId(SessionId id) {
public void setPrivateKey(PrivateKey privateKey) { _privateKey = privateKey; } _sessionId = id;
public LeaseSet getLeaseSet() { return _leaseSet; } }
public void setLeaseSet(LeaseSet leaseSet) { _leaseSet = leaseSet; }
public SigningPrivateKey getSigningPrivateKey() {
return _signingPrivateKey;
}
public void setSigningPrivateKey(SigningPrivateKey key) {
_signingPrivateKey = key;
}
public PrivateKey getPrivateKey() {
return _privateKey;
}
public void setPrivateKey(PrivateKey privateKey) {
_privateKey = privateKey;
}
public LeaseSet getLeaseSet() {
return _leaseSet;
}
public void setLeaseSet(LeaseSet leaseSet) {
_leaseSet = leaseSet;
}
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException { protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
try { try {
_sessionId = new SessionId(); _sessionId = new SessionId();
_sessionId.readBytes(in); _sessionId.readBytes(in);
_signingPrivateKey = new SigningPrivateKey(); _signingPrivateKey = new SigningPrivateKey();
_signingPrivateKey.readBytes(in); _signingPrivateKey.readBytes(in);
_privateKey = new PrivateKey(); _privateKey = new PrivateKey();
_privateKey.readBytes(in); _privateKey.readBytes(in);
_leaseSet = new LeaseSet(); _leaseSet = new LeaseSet();
_leaseSet.readBytes(in); _leaseSet.readBytes(in);
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
throw new I2CPMessageException("Error reading the CreateLeaseSetMessage", dfe); throw new I2CPMessageException("Error reading the CreateLeaseSetMessage", dfe);
} }
} }
protected byte[] doWriteMessage() throws I2CPMessageException, IOException { protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
if ( (_sessionId == null) || (_signingPrivateKey == null) || (_privateKey == null) || (_leaseSet == null) ) if ((_sessionId == null) || (_signingPrivateKey == null) || (_privateKey == null) || (_leaseSet == null))
throw new I2CPMessageException("Unable to write out the message as there is not enough data"); throw new I2CPMessageException("Unable to write out the message as there is not enough data");
ByteArrayOutputStream os = new ByteArrayOutputStream(512); ByteArrayOutputStream os = new ByteArrayOutputStream(512);
try { try {
_sessionId.writeBytes(os); _sessionId.writeBytes(os);
_signingPrivateKey.writeBytes(os); _signingPrivateKey.writeBytes(os);
_privateKey.writeBytes(os); _privateKey.writeBytes(os);
_leaseSet.writeBytes(os); _leaseSet.writeBytes(os);
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
throw new I2CPMessageException("Error writing out the message data", dfe); throw new I2CPMessageException("Error writing out the message data", dfe);
} }
return os.toByteArray(); return os.toByteArray();
} }
public int getType() { return MESSAGE_TYPE; } public int getType() {
return MESSAGE_TYPE;
}
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object != null) && (object instanceof CreateLeaseSetMessage) ) { if ((object != null) && (object instanceof CreateLeaseSetMessage)) {
CreateLeaseSetMessage msg = (CreateLeaseSetMessage)object; CreateLeaseSetMessage msg = (CreateLeaseSetMessage) object;
return DataHelper.eq(getSessionId(),msg.getSessionId()) && return DataHelper.eq(getSessionId(), msg.getSessionId())
DataHelper.eq(getSigningPrivateKey(),msg.getSigningPrivateKey()) && && DataHelper.eq(getSigningPrivateKey(), msg.getSigningPrivateKey())
DataHelper.eq(getPrivateKey(), msg.getPrivateKey()) && && DataHelper.eq(getPrivateKey(), msg.getPrivateKey())
DataHelper.eq(getLeaseSet(),msg.getLeaseSet()); && DataHelper.eq(getLeaseSet(), msg.getLeaseSet());
} else { } else {
return false; return false;
} }
} }
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
buf.append("[CreateLeaseSetMessage: "); buf.append("[CreateLeaseSetMessage: ");
buf.append("\n\tLeaseSet: ").append(getLeaseSet()); buf.append("\n\tLeaseSet: ").append(getLeaseSet());

View File

@@ -1,4 +1,5 @@
package net.i2p.data.i2cp; package net.i2p.data.i2cp;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -26,13 +27,23 @@ public class CreateSessionMessage extends I2CPMessageImpl {
private final static Log _log = new Log(CreateSessionMessage.class); private final static Log _log = new Log(CreateSessionMessage.class);
public final static int MESSAGE_TYPE = 1; public final static int MESSAGE_TYPE = 1;
private SessionConfig _sessionConfig; private SessionConfig _sessionConfig;
public CreateSessionMessage(SessionConfig config) { setSessionConfig(config); } public CreateSessionMessage(SessionConfig config) {
public CreateSessionMessage() { setSessionConfig(new SessionConfig()); } setSessionConfig(config);
}
public SessionConfig getSessionConfig() { return _sessionConfig; }
public void setSessionConfig(SessionConfig config) { _sessionConfig = config; } public CreateSessionMessage() {
setSessionConfig(new SessionConfig());
}
public SessionConfig getSessionConfig() {
return _sessionConfig;
}
public void setSessionConfig(SessionConfig config) {
_sessionConfig = config;
}
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException { protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
SessionConfig config = new SessionConfig(); SessionConfig config = new SessionConfig();
try { try {
@@ -42,7 +53,7 @@ public class CreateSessionMessage extends I2CPMessageImpl {
} }
setSessionConfig(config); setSessionConfig(config);
} }
protected byte[] doWriteMessage() throws I2CPMessageException, IOException { protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
if (_sessionConfig == null) if (_sessionConfig == null)
throw new I2CPMessageException("Unable to write out the message as there is not enough data"); throw new I2CPMessageException("Unable to write out the message as there is not enough data");
@@ -54,23 +65,25 @@ public class CreateSessionMessage extends I2CPMessageImpl {
} }
return os.toByteArray(); return os.toByteArray();
} }
public int getType() { return MESSAGE_TYPE; } public int getType() {
return MESSAGE_TYPE;
}
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object != null) && (object instanceof CreateSessionMessage) ) { if ((object != null) && (object instanceof CreateSessionMessage)) {
CreateSessionMessage msg = (CreateSessionMessage)object; CreateSessionMessage msg = (CreateSessionMessage) object;
return DataHelper.eq(getSessionConfig(), msg.getSessionConfig()); return DataHelper.eq(getSessionConfig(), msg.getSessionConfig());
} else { } else {
return false; return false;
} }
} }
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
buf.append("[CreateSessionMessage: "); buf.append("[CreateSessionMessage: ");
buf.append("\n\tConfig: ").append(getSessionConfig()); buf.append("\n\tConfig: ").append(getSessionConfig());
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data.i2cp; package net.i2p.data.i2cp;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -26,14 +27,19 @@ public class DestroySessionMessage extends I2CPMessageImpl {
private final static Log _log = new Log(DestroySessionMessage.class); private final static Log _log = new Log(DestroySessionMessage.class);
public final static int MESSAGE_TYPE = 3; public final static int MESSAGE_TYPE = 3;
private SessionId _sessionId; private SessionId _sessionId;
public DestroySessionMessage() { public DestroySessionMessage() {
setSessionId(null); setSessionId(null);
} }
public SessionId getSessionId() { return _sessionId; } public SessionId getSessionId() {
public void setSessionId(SessionId id) { _sessionId = id; } return _sessionId;
}
public void setSessionId(SessionId id) {
_sessionId = id;
}
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException { protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
SessionId id = new SessionId(); SessionId id = new SessionId();
try { try {
@@ -43,7 +49,7 @@ public class DestroySessionMessage extends I2CPMessageImpl {
} }
setSessionId(id); setSessionId(id);
} }
protected byte[] doWriteMessage() throws I2CPMessageException, IOException { protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
if (_sessionId == null) if (_sessionId == null)
throw new I2CPMessageException("Unable to write out the message as there is not enough data"); throw new I2CPMessageException("Unable to write out the message as there is not enough data");
@@ -55,23 +61,25 @@ public class DestroySessionMessage extends I2CPMessageImpl {
} }
return os.toByteArray(); return os.toByteArray();
} }
public int getType() { return MESSAGE_TYPE; } public int getType() {
return MESSAGE_TYPE;
}
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object != null) && (object instanceof DestroySessionMessage) ) { if ((object != null) && (object instanceof DestroySessionMessage)) {
DestroySessionMessage msg = (DestroySessionMessage)object; DestroySessionMessage msg = (DestroySessionMessage) object;
return DataHelper.eq(getSessionId(),msg.getSessionId()); return DataHelper.eq(getSessionId(), msg.getSessionId());
} else { } else {
return false; return false;
} }
} }
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
buf.append("[DestroySessionMessage: "); buf.append("[DestroySessionMessage: ");
buf.append("\n\tSessionId: ").append(getSessionId()); buf.append("\n\tSessionId: ").append(getSessionId());
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data.i2cp; package net.i2p.data.i2cp;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -26,14 +27,19 @@ public class DisconnectMessage extends I2CPMessageImpl {
private final static Log _log = new Log(DisconnectMessage.class); private final static Log _log = new Log(DisconnectMessage.class);
public final static int MESSAGE_TYPE = 30; public final static int MESSAGE_TYPE = 30;
private String _reason; private String _reason;
public DisconnectMessage() { public DisconnectMessage() {
setReason(null); setReason(null);
} }
public String getReason() { return _reason; } public String getReason() {
public void setReason(String reason) { _reason = reason; } return _reason;
}
public void setReason(String reason) {
_reason = reason;
}
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException { protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
try { try {
_reason = DataHelper.readString(in); _reason = DataHelper.readString(in);
@@ -41,7 +47,7 @@ public class DisconnectMessage extends I2CPMessageImpl {
throw new I2CPMessageException("Unable to load the message data", dfe); throw new I2CPMessageException("Unable to load the message data", dfe);
} }
} }
protected byte[] doWriteMessage() throws I2CPMessageException, IOException { protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream(64); ByteArrayOutputStream os = new ByteArrayOutputStream(64);
try { try {
@@ -51,23 +57,25 @@ public class DisconnectMessage extends I2CPMessageImpl {
} }
return os.toByteArray(); return os.toByteArray();
} }
public int getType() { return MESSAGE_TYPE; } public int getType() {
return MESSAGE_TYPE;
}
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object != null) && (object instanceof DisconnectMessage) ) { if ((object != null) && (object instanceof DisconnectMessage)) {
DisconnectMessage msg = (DisconnectMessage)object; DisconnectMessage msg = (DisconnectMessage) object;
return DataHelper.eq(getReason(),msg.getReason()); return DataHelper.eq(getReason(), msg.getReason());
} else { } else {
return false; return false;
} }
} }
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
buf.append("[DisconnectMessage: "); buf.append("[DisconnectMessage: ");
buf.append("\n\tReason: ").append(getReason()); buf.append("\n\tReason: ").append(getReason());
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data.i2cp; package net.i2p.data.i2cp;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -20,33 +21,35 @@ import net.i2p.util.Log;
public class GetDateMessage extends I2CPMessageImpl { public class GetDateMessage extends I2CPMessageImpl {
private final static Log _log = new Log(GetDateMessage.class); private final static Log _log = new Log(GetDateMessage.class);
public final static int MESSAGE_TYPE = 32; public final static int MESSAGE_TYPE = 32;
public GetDateMessage() { public GetDateMessage() {
super(); super();
} }
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException { protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
// noop // noop
} }
protected byte[] doWriteMessage() throws I2CPMessageException, IOException { protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
byte rv[] = new byte[0]; byte rv[] = new byte[0];
return rv; return rv;
} }
public int getType() { return MESSAGE_TYPE; } public int getType() {
return MESSAGE_TYPE;
}
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object != null) && (object instanceof GetDateMessage) ) { if ((object != null) && (object instanceof GetDateMessage)) {
return true; return true;
} else { } else {
return false; return false;
} }
} }
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
buf.append("[GetDateMessage]"); buf.append("[GetDateMessage]");
return buf.toString(); return buf.toString();
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data.i2cp; package net.i2p.data.i2cp;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -34,6 +35,7 @@ public interface I2CPMessage extends DataStructure {
* @throws IOException if there is a problem reading from the stream * @throws IOException if there is a problem reading from the stream
*/ */
public void readMessage(InputStream in, int size, int type) throws I2CPMessageException, IOException; public void readMessage(InputStream in, int size, int type) throws I2CPMessageException, IOException;
/** /**
* Read the contents from the input stream into the current class's format. * Read the contents from the input stream into the current class's format.
* The stream should be the message header and body as defined by the I2CP * The stream should be the message header and body as defined by the I2CP
@@ -45,7 +47,7 @@ public interface I2CPMessage extends DataStructure {
* @throws IOException if there is a problem reading from the stream * @throws IOException if there is a problem reading from the stream
*/ */
public void readMessage(InputStream in) throws I2CPMessageException, IOException; public void readMessage(InputStream in) throws I2CPMessageException, IOException;
/** /**
* Write the current message to the output stream as a full message following * Write the current message to the output stream as a full message following
* the specification from the I2CP definition. * the specification from the I2CP definition.
@@ -55,10 +57,10 @@ public interface I2CPMessage extends DataStructure {
* @throws IOException if there is a problem writing to the stream * @throws IOException if there is a problem writing to the stream
*/ */
public void writeMessage(OutputStream out) throws I2CPMessageException, IOException; public void writeMessage(OutputStream out) throws I2CPMessageException, IOException;
/** /**
* Return the unique identifier for this type of APIMessage, as specified in the * Return the unique identifier for this type of APIMessage, as specified in the
* network specification document under #ClientAccessLayerMessages * network specification document under #ClientAccessLayerMessages
*/ */
public int getType(); public int getType();
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data.i2cp; package net.i2p.data.i2cp;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -18,11 +19,12 @@ import net.i2p.util.Log;
*/ */
public class I2CPMessageException extends I2PException { public class I2CPMessageException extends I2PException {
private final static Log _log = new Log(I2CPMessageException.class); private final static Log _log = new Log(I2CPMessageException.class);
public I2CPMessageException(String message, Throwable parent) { public I2CPMessageException(String message, Throwable parent) {
super(message, parent); super(message, parent);
} }
public I2CPMessageException(String message) { public I2CPMessageException(String message) {
super(message); super(message);
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data.i2cp; package net.i2p.data.i2cp;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,13 +9,12 @@ package net.i2p.data.i2cp;
* *
*/ */
import java.io.InputStream;
import java.io.IOException;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import net.i2p.data.DataHelper;
import net.i2p.data.DataFormatException; import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.util.Log; import net.i2p.util.Log;
/** /**
@@ -23,7 +23,7 @@ import net.i2p.util.Log;
*/ */
public class I2CPMessageHandler { public class I2CPMessageHandler {
private final static Log _log = new Log(I2CPMessageHandler.class); private final static Log _log = new Log(I2CPMessageHandler.class);
/** /**
* Read an I2CPMessage from the stream and return the fully populated object. * Read an I2CPMessage from the stream and return the fully populated object.
* *
@@ -33,9 +33,9 @@ public class I2CPMessageHandler {
*/ */
public static I2CPMessage readMessage(InputStream in) throws IOException, I2CPMessageException { public static I2CPMessage readMessage(InputStream in) throws IOException, I2CPMessageException {
try { try {
int length = (int)DataHelper.readLong(in, 4); int length = (int) DataHelper.readLong(in, 4);
if (length < 0) throw new I2CPMessageException("Invalid message length specified"); if (length < 0) throw new I2CPMessageException("Invalid message length specified");
int type = (int)DataHelper.readLong(in, 1); int type = (int) DataHelper.readLong(in, 1);
I2CPMessage msg = createMessage(in, length, type); I2CPMessage msg = createMessage(in, length, type);
msg.readMessage(in, length, type); msg.readMessage(in, length, type);
return msg; return msg;
@@ -43,46 +43,47 @@ public class I2CPMessageHandler {
throw new I2CPMessageException("Error reading the message", dfe); throw new I2CPMessageException("Error reading the message", dfe);
} }
} }
/** /**
* Yes, this is fairly ugly, but its the only place it ever happens. * Yes, this is fairly ugly, but its the only place it ever happens.
* *
*/ */
private static I2CPMessage createMessage(InputStream in, int length, int type) throws IOException, I2CPMessageException { private static I2CPMessage createMessage(InputStream in, int length, int type) throws IOException,
I2CPMessageException {
switch (type) { switch (type) {
case CreateLeaseSetMessage.MESSAGE_TYPE: case CreateLeaseSetMessage.MESSAGE_TYPE:
return new CreateLeaseSetMessage(); return new CreateLeaseSetMessage();
case CreateSessionMessage.MESSAGE_TYPE: case CreateSessionMessage.MESSAGE_TYPE:
return new CreateSessionMessage(); return new CreateSessionMessage();
case DestroySessionMessage.MESSAGE_TYPE: case DestroySessionMessage.MESSAGE_TYPE:
return new DestroySessionMessage(); return new DestroySessionMessage();
case DisconnectMessage.MESSAGE_TYPE: case DisconnectMessage.MESSAGE_TYPE:
return new DisconnectMessage(); return new DisconnectMessage();
case MessageStatusMessage.MESSAGE_TYPE: case MessageStatusMessage.MESSAGE_TYPE:
return new MessageStatusMessage(); return new MessageStatusMessage();
case MessagePayloadMessage.MESSAGE_TYPE: case MessagePayloadMessage.MESSAGE_TYPE:
return new MessagePayloadMessage(); return new MessagePayloadMessage();
case ReceiveMessageBeginMessage.MESSAGE_TYPE: case ReceiveMessageBeginMessage.MESSAGE_TYPE:
return new ReceiveMessageBeginMessage(); return new ReceiveMessageBeginMessage();
case ReceiveMessageEndMessage.MESSAGE_TYPE: case ReceiveMessageEndMessage.MESSAGE_TYPE:
return new ReceiveMessageEndMessage(); return new ReceiveMessageEndMessage();
case ReportAbuseMessage.MESSAGE_TYPE: case ReportAbuseMessage.MESSAGE_TYPE:
return new ReportAbuseMessage(); return new ReportAbuseMessage();
case RequestLeaseSetMessage.MESSAGE_TYPE: case RequestLeaseSetMessage.MESSAGE_TYPE:
return new RequestLeaseSetMessage(); return new RequestLeaseSetMessage();
case SendMessageMessage.MESSAGE_TYPE: case SendMessageMessage.MESSAGE_TYPE:
return new SendMessageMessage(); return new SendMessageMessage();
case SessionStatusMessage.MESSAGE_TYPE: case SessionStatusMessage.MESSAGE_TYPE:
return new SessionStatusMessage(); return new SessionStatusMessage();
case GetDateMessage.MESSAGE_TYPE: case GetDateMessage.MESSAGE_TYPE:
return new GetDateMessage(); return new GetDateMessage();
case SetDateMessage.MESSAGE_TYPE: case SetDateMessage.MESSAGE_TYPE:
return new SetDateMessage(); return new SetDateMessage();
default: default:
throw new I2CPMessageException("The type "+ type + " is an unknown I2CP message"); throw new I2CPMessageException("The type " + type + " is an unknown I2CP message");
} }
} }
public static void main(String args[]) { public static void main(String args[]) {
try { try {
I2CPMessage msg = readMessage(new FileInputStream(args[0])); I2CPMessage msg = readMessage(new FileInputStream(args[0]));
@@ -91,4 +92,4 @@ public class I2CPMessageHandler {
e.printStackTrace(); e.printStackTrace();
} }
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data.i2cp; package net.i2p.data.i2cp;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -25,8 +26,10 @@ import net.i2p.util.Log;
*/ */
public abstract class I2CPMessageImpl extends DataStructureImpl implements I2CPMessage { public abstract class I2CPMessageImpl extends DataStructureImpl implements I2CPMessage {
private final static Log _log = new Log(I2CPMessageImpl.class); private final static Log _log = new Log(I2CPMessageImpl.class);
public I2CPMessageImpl() {}
public I2CPMessageImpl() {
}
/** /**
* Validate the type and size of the message, and then read the message into the data structures. <p /> * Validate the type and size of the message, and then read the message into the data structures. <p />
* *
@@ -34,51 +37,54 @@ public abstract class I2CPMessageImpl extends DataStructureImpl implements I2CPM
public void readMessage(InputStream in) throws I2CPMessageException, IOException { public void readMessage(InputStream in) throws I2CPMessageException, IOException {
int length = 0; int length = 0;
try { try {
length = (int)DataHelper.readLong(in, 4); length = (int) DataHelper.readLong(in, 4);
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
throw new I2CPMessageException("Error reading the length bytes", dfe); throw new I2CPMessageException("Error reading the length bytes", dfe);
} }
if (length < 0) throw new I2CPMessageException("Invalid message length specified"); if (length < 0) throw new I2CPMessageException("Invalid message length specified");
int type = -1; int type = -1;
try { try {
type = (int)DataHelper.readLong(in, 1); type = (int) DataHelper.readLong(in, 1);
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
throw new I2CPMessageException("Error reading the type byte", dfe); throw new I2CPMessageException("Error reading the type byte", dfe);
} }
readMessage(in, length, type); readMessage(in, length, type);
} }
/** /**
* Read the body into the data structures * Read the body into the data structures
* *
*/ */
public void readMessage(InputStream in, int length, int type) throws I2CPMessageException, IOException { public void readMessage(InputStream in, int length, int type) throws I2CPMessageException, IOException {
if (type != getType()) throw new I2CPMessageException("Invalid message type (found: " + type + " supported: " + getType() + " class: " + getClass().getName()+ ")"); if (type != getType())
throw new I2CPMessageException("Invalid message type (found: " + type + " supported: " + getType()
+ " class: " + getClass().getName() + ")");
if (length < 0) throw new IOException("Negative payload size"); if (length < 0) throw new IOException("Negative payload size");
byte buf[] = new byte[length];
int read = DataHelper.read(in, buf);
if (read != length)
throw new IOException("Not able to read enough bytes [" + read + "] read, expected [ " + length + "]");
ByteArrayInputStream bis = new ByteArrayInputStream(buf); byte buf[] = new byte[length];
int read = DataHelper.read(in, buf);
if (read != length)
throw new IOException("Not able to read enough bytes [" + read + "] read, expected [ " + length + "]");
ByteArrayInputStream bis = new ByteArrayInputStream(buf);
doReadMessage(bis, length); doReadMessage(bis, length);
} }
/** /**
* Read in the payload part of the message (after the initial 4 byte size and 1 * Read in the payload part of the message (after the initial 4 byte size and 1
* byte type) * byte type)
* *
*/ */
protected abstract void doReadMessage(InputStream buf, int size) throws I2CPMessageException, IOException; protected abstract void doReadMessage(InputStream buf, int size) throws I2CPMessageException, IOException;
/** /**
* Write out the payload part of the message (not including the 4 byte size and * Write out the payload part of the message (not including the 4 byte size and
* 1 byte type) * 1 byte type)
* *
*/ */
protected abstract byte[] doWriteMessage() throws I2CPMessageException, IOException; protected abstract byte[] doWriteMessage() throws I2CPMessageException, IOException;
/** /**
* Write out the full message to the stream, including the 4 byte size and 1 * Write out the full message to the stream, including the 4 byte size and 1
* byte type header. * byte type header.
@@ -94,7 +100,7 @@ public abstract class I2CPMessageImpl extends DataStructureImpl implements I2CPM
} }
out.write(data); out.write(data);
} }
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
try { try {
readMessage(in); readMessage(in);
@@ -102,6 +108,7 @@ public abstract class I2CPMessageImpl extends DataStructureImpl implements I2CPM
throw new DataFormatException("Error reading the message", ime); throw new DataFormatException("Error reading the message", ime);
} }
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
try { try {
writeMessage(out); writeMessage(out);
@@ -109,4 +116,4 @@ public abstract class I2CPMessageImpl extends DataStructureImpl implements I2CPM
throw new DataFormatException("Error writing the message", ime); throw new DataFormatException("Error writing the message", ime);
} }
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data.i2cp; package net.i2p.data.i2cp;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -29,40 +30,56 @@ public class I2CPMessageReader {
private I2CPMessageEventListener _listener; private I2CPMessageEventListener _listener;
private I2CPMessageReaderRunner _reader; private I2CPMessageReaderRunner _reader;
private Thread _readerThread; private Thread _readerThread;
public I2CPMessageReader(InputStream stream, I2CPMessageEventListener lsnr) { public I2CPMessageReader(InputStream stream, I2CPMessageEventListener lsnr) {
_stream = stream; _stream = stream;
setListener(lsnr); setListener(lsnr);
_reader = new I2CPMessageReaderRunner(); _reader = new I2CPMessageReaderRunner();
_readerThread = new I2PThread(_reader); _readerThread = new I2PThread(_reader);
_readerThread.setDaemon(true); _readerThread.setDaemon(true);
_readerThread.setName("I2CP Reader"); _readerThread.setName("I2CP Reader");
} }
public void setListener(I2CPMessageEventListener lsnr) { _listener = lsnr; } public void setListener(I2CPMessageEventListener lsnr) {
public I2CPMessageEventListener getListener() { return _listener; } _listener = lsnr;
}
public I2CPMessageEventListener getListener() {
return _listener;
}
/** /**
* Instruct the reader to begin reading messages off the stream * Instruct the reader to begin reading messages off the stream
* *
*/ */
public void startReading() { _readerThread.start(); } public void startReading() {
_readerThread.start();
}
/** /**
* Have the already started reader pause its reading indefinitely * Have the already started reader pause its reading indefinitely
* *
*/ */
public void pauseReading() { _reader.pauseRunner(); } public void pauseReading() {
_reader.pauseRunner();
}
/** /**
* Resume reading after a pause * Resume reading after a pause
* *
*/ */
public void resumeReading() { _reader.resumeRunner(); } public void resumeReading() {
_reader.resumeRunner();
}
/** /**
* Cancel reading. * Cancel reading.
* *
*/ */
public void stopReading() { _reader.cancelRunner(); } public void stopReading() {
_reader.cancelRunner();
}
/** /**
* Defines the different events the reader produces while reading the stream * Defines the different events the reader produces while reading the stream
* *
@@ -74,41 +91,52 @@ public class I2CPMessageReader {
* *
*/ */
public void messageReceived(I2CPMessageReader reader, I2CPMessage message); public void messageReceived(I2CPMessageReader reader, I2CPMessage message);
/** /**
* Notify the listener that an exception was thrown while reading from the given * Notify the listener that an exception was thrown while reading from the given
* reader * reader
* *
*/ */
public void readError(I2CPMessageReader reader, Exception error); public void readError(I2CPMessageReader reader, Exception error);
/** /**
* Notify the listener that the stream the given reader was running off * Notify the listener that the stream the given reader was running off
* closed * closed
* *
*/ */
public void disconnected(I2CPMessageReader reader); public void disconnected(I2CPMessageReader reader);
} }
private class I2CPMessageReaderRunner implements Runnable { private class I2CPMessageReaderRunner implements Runnable {
private boolean _doRun; private boolean _doRun;
private boolean _stayAlive; private boolean _stayAlive;
public I2CPMessageReaderRunner() { public I2CPMessageReaderRunner() {
_doRun = true; _doRun = true;
_stayAlive = true; _stayAlive = true;
} }
public void pauseRunner() { _doRun = false; }
public void resumeRunner() { _doRun = true; } public void pauseRunner() {
public void cancelRunner() {
_doRun = false; _doRun = false;
_stayAlive = false;
if (_stream != null) {
try {
_stream.close();
} catch (IOException ioe) {
_log.error("Error closing the stream", ioe);
}
}
_stream = null;
} }
public void resumeRunner() {
_doRun = true;
}
public void cancelRunner() {
_doRun = false;
_stayAlive = false;
if (_stream != null) {
try {
_stream.close();
} catch (IOException ioe) {
_log.error("Error closing the stream", ioe);
}
}
_stream = null;
}
public void run() { public void run() {
while (_stayAlive) { while (_stayAlive) {
while (_doRun) { while (_doRun) {
@@ -116,30 +144,33 @@ public class I2CPMessageReader {
try { try {
I2CPMessage msg = I2CPMessageHandler.readMessage(_stream); I2CPMessage msg = I2CPMessageHandler.readMessage(_stream);
if (msg != null) { if (msg != null) {
_log.debug("Before handling the newly received message"); _log.debug("Before handling the newly received message");
_listener.messageReceived(I2CPMessageReader.this, msg); _listener.messageReceived(I2CPMessageReader.this, msg);
_log.debug("After handling the newly received message"); _log.debug("After handling the newly received message");
} }
} catch (I2CPMessageException ime) { } catch (I2CPMessageException ime) {
_log.error("Error handling message", ime); _log.error("Error handling message", ime);
_listener.readError(I2CPMessageReader.this, ime); _listener.readError(I2CPMessageReader.this, ime);
cancelRunner(); cancelRunner();
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error("IO Error handling message", ioe); _log.error("IO Error handling message", ioe);
_listener.disconnected(I2CPMessageReader.this); _listener.disconnected(I2CPMessageReader.this);
cancelRunner(); cancelRunner();
} catch (Throwable t) { } catch (Throwable t) {
_log.log(Log.CRIT, "Unhandled error reading I2CP stream", t); _log.log(Log.CRIT, "Unhandled error reading I2CP stream", t);
_listener.disconnected(I2CPMessageReader.this); _listener.disconnected(I2CPMessageReader.this);
cancelRunner(); cancelRunner();
} }
} }
if (!_doRun) { if (!_doRun) {
// pause .5 secs when we're paused // pause .5 secs when we're paused
try { Thread.sleep(500); } catch (InterruptedException ie) {} try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
} }
} }
// boom bye bye bad bwoy // boom bye bye bad bwoy
} }
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data.i2cp; package net.i2p.data.i2cp;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -8,15 +9,14 @@ package net.i2p.data.i2cp;
* *
*/ */
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.IOException;
import net.i2p.util.Log;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.DataStructureImpl; import net.i2p.data.DataStructureImpl;
import net.i2p.data.DataFormatException; import net.i2p.util.Log;
/** /**
* Defines the message ID of a message delivered between a router and a client * Defines the message ID of a message delivered between a router and a client
@@ -27,30 +27,38 @@ import net.i2p.data.DataFormatException;
public class MessageId extends DataStructureImpl { public class MessageId extends DataStructureImpl {
private final static Log _log = new Log(MessageId.class); private final static Log _log = new Log(MessageId.class);
private int _messageId; private int _messageId;
public MessageId() { setMessageId(-1); } public MessageId() {
setMessageId(-1);
public int getMessageId() { return _messageId; }
public void setMessageId(int id) { _messageId = id; }
public void readBytes(InputStream in) throws DataFormatException, IOException {
_messageId = (int)DataHelper.readLong(in, 4);
} }
public int getMessageId() {
return _messageId;
}
public void setMessageId(int id) {
_messageId = id;
}
public void readBytes(InputStream in) throws DataFormatException, IOException {
_messageId = (int) DataHelper.readLong(in, 4);
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_messageId < 0) throw new DataFormatException("Invalid message ID: " + _messageId); if (_messageId < 0) throw new DataFormatException("Invalid message ID: " + _messageId);
DataHelper.writeLong(out, 4, _messageId); DataHelper.writeLong(out, 4, _messageId);
} }
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object == null) || !(object instanceof MessageId) ) if ((object == null) || !(object instanceof MessageId)) return false;
return false; return DataHelper.eq(getMessageId(), ((MessageId) object).getMessageId());
return DataHelper.eq(getMessageId(), ((MessageId)object).getMessageId());
} }
public int hashCode() { return getMessageId(); } public int hashCode() {
return getMessageId();
}
public String toString() { public String toString() {
return "[MessageId: " + getMessageId() + "]"; return "[MessageId: " + getMessageId() + "]";
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data.i2cp; package net.i2p.data.i2cp;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -29,20 +30,37 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
private SessionId _sessionId; private SessionId _sessionId;
private MessageId _messageId; private MessageId _messageId;
private Payload _payload; private Payload _payload;
public MessagePayloadMessage() { public MessagePayloadMessage() {
setSessionId(null); setSessionId(null);
setMessageId(null); setMessageId(null);
setPayload(null); setPayload(null);
} }
public SessionId getSessionId() { return _sessionId; } public SessionId getSessionId() {
public void setSessionId(SessionId id) { _sessionId = id; } return _sessionId;
public MessageId getMessageId() { return _messageId; } }
public void setMessageId(MessageId id) { _messageId = id; }
public Payload getPayload() { return _payload; } public void setSessionId(SessionId id) {
public void setPayload(Payload payload) { _payload = payload; } _sessionId = id;
}
public MessageId getMessageId() {
return _messageId;
}
public void setMessageId(MessageId id) {
_messageId = id;
}
public Payload getPayload() {
return _payload;
}
public void setPayload(Payload payload) {
_payload = payload;
}
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException { protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
try { try {
_sessionId = new SessionId(); _sessionId = new SessionId();
@@ -55,15 +73,15 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
throw new I2CPMessageException("Unable to load the message data", dfe); throw new I2CPMessageException("Unable to load the message data", dfe);
} }
} }
protected byte[] doWriteMessage() throws I2CPMessageException, IOException { protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
if (_sessionId == null) if (_sessionId == null)
throw new I2CPMessageException("Unable to write out the message, as the session ID has not been defined"); throw new I2CPMessageException("Unable to write out the message, as the session ID has not been defined");
if (_messageId == null) if (_messageId == null)
throw new I2CPMessageException("Unable to write out the message, as the message ID has not been defined"); throw new I2CPMessageException("Unable to write out the message, as the message ID has not been defined");
if (_payload == null) if (_payload == null)
throw new I2CPMessageException("Unable to write out the message, as the payload has not been defined"); throw new I2CPMessageException("Unable to write out the message, as the payload has not been defined");
ByteArrayOutputStream os = new ByteArrayOutputStream(512); ByteArrayOutputStream os = new ByteArrayOutputStream(512);
try { try {
_sessionId.writeBytes(os); _sessionId.writeBytes(os);
@@ -74,21 +92,23 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
} }
return os.toByteArray(); return os.toByteArray();
} }
public int getType() { return MESSAGE_TYPE; } public int getType() {
return MESSAGE_TYPE;
}
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object != null) && (object instanceof MessagePayloadMessage) ) { if ((object != null) && (object instanceof MessagePayloadMessage)) {
MessagePayloadMessage msg = (MessagePayloadMessage)object; MessagePayloadMessage msg = (MessagePayloadMessage) object;
return DataHelper.eq(getSessionId(),msg.getSessionId()) && return DataHelper.eq(getSessionId(), msg.getSessionId())
DataHelper.eq(getMessageId(),msg.getMessageId()) && && DataHelper.eq(getMessageId(), msg.getMessageId())
DataHelper.eq(getPayload(),msg.getPayload()); && DataHelper.eq(getPayload(), msg.getPayload());
} else { } else {
return false; return false;
} }
} }
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
buf.append("[MessagePayloadMessage: "); buf.append("[MessagePayloadMessage: ");
buf.append("\n\tSessionId: ").append(getSessionId()); buf.append("\n\tSessionId: ").append(getSessionId());
@@ -97,4 +117,4 @@ public class MessagePayloadMessage extends I2CPMessageImpl {
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
} }

View File

@@ -1,4 +1,5 @@
package net.i2p.data.i2cp; package net.i2p.data.i2cp;
/* /*
* free (adj.): unencumbered; not under the control of others * free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain * Written by jrandom in 2003 and released into the public domain
@@ -30,61 +31,97 @@ public class MessageStatusMessage extends I2CPMessageImpl {
private long _nonce; private long _nonce;
private long _size; private long _size;
private int _status; private int _status;
public final static int STATUS_AVAILABLE = 0; public final static int STATUS_AVAILABLE = 0;
public final static int STATUS_SEND_ACCEPTED = 1; public final static int STATUS_SEND_ACCEPTED = 1;
public final static int STATUS_SEND_BEST_EFFORT_SUCCESS = 2; public final static int STATUS_SEND_BEST_EFFORT_SUCCESS = 2;
public final static int STATUS_SEND_BEST_EFFORT_FAILURE = 3; public final static int STATUS_SEND_BEST_EFFORT_FAILURE = 3;
public final static int STATUS_SEND_GUARANTEED_SUCCESS = 4; public final static int STATUS_SEND_GUARANTEED_SUCCESS = 4;
public final static int STATUS_SEND_GUARANTEED_FAILURE = 5; public final static int STATUS_SEND_GUARANTEED_FAILURE = 5;
public MessageStatusMessage() { public MessageStatusMessage() {
setSessionId(null); setSessionId(null);
setStatus(-1); setStatus(-1);
setMessageId(null); setMessageId(null);
setSize(-1); setSize(-1);
setNonce(-1); setNonce(-1);
} }
public SessionId getSessionId() { return _sessionId; } public SessionId getSessionId() {
public void setSessionId(SessionId id) { _sessionId = id; } return _sessionId;
public int getStatus() { return _status; }
public void setStatus(int status) { _status = status; }
public MessageId getMessageId() { return _messageId; }
public void setMessageId(MessageId id) { _messageId = id; }
public long getSize() { return _size; }
public void setSize(long size) { _size = size; }
public long getNonce() { return _nonce; }
public void setNonce(long nonce) { _nonce = nonce; }
public static final String getStatusString(int status) {
switch (status) {
case STATUS_AVAILABLE: return "AVAILABLE ";
case STATUS_SEND_ACCEPTED: return "SEND ACCEPTED ";
case STATUS_SEND_BEST_EFFORT_SUCCESS: return "BEST EFFORT SUCCESS";
case STATUS_SEND_BEST_EFFORT_FAILURE: return "BEST EFFORT FAILURE";
case STATUS_SEND_GUARANTEED_SUCCESS: return "GUARANTEED SUCCESS ";
case STATUS_SEND_GUARANTEED_FAILURE: return "GUARANTEED FAILURE ";
default: return "***INVALID STATUS: " + status;
}
} }
public void setSessionId(SessionId id) {
_sessionId = id;
}
public int getStatus() {
return _status;
}
public void setStatus(int status) {
_status = status;
}
public MessageId getMessageId() {
return _messageId;
}
public void setMessageId(MessageId id) {
_messageId = id;
}
public long getSize() {
return _size;
}
public void setSize(long size) {
_size = size;
}
public long getNonce() {
return _nonce;
}
public void setNonce(long nonce) {
_nonce = nonce;
}
public static final String getStatusString(int status) {
switch (status) {
case STATUS_AVAILABLE:
return "AVAILABLE ";
case STATUS_SEND_ACCEPTED:
return "SEND ACCEPTED ";
case STATUS_SEND_BEST_EFFORT_SUCCESS:
return "BEST EFFORT SUCCESS";
case STATUS_SEND_BEST_EFFORT_FAILURE:
return "BEST EFFORT FAILURE";
case STATUS_SEND_GUARANTEED_SUCCESS:
return "GUARANTEED SUCCESS ";
case STATUS_SEND_GUARANTEED_FAILURE:
return "GUARANTEED FAILURE ";
default:
return "***INVALID STATUS: " + status;
}
}
protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException { protected void doReadMessage(InputStream in, int size) throws I2CPMessageException, IOException {
try { try {
_sessionId = new SessionId(); _sessionId = new SessionId();
_sessionId.readBytes(in); _sessionId.readBytes(in);
_messageId = new MessageId(); _messageId = new MessageId();
_messageId.readBytes(in); _messageId.readBytes(in);
_status = (int)DataHelper.readLong(in, 1); _status = (int) DataHelper.readLong(in, 1);
_size = DataHelper.readLong(in, 4); _size = DataHelper.readLong(in, 4);
_nonce = DataHelper.readLong(in, 4); _nonce = DataHelper.readLong(in, 4);
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
throw new I2CPMessageException("Unable to load the message data", dfe); throw new I2CPMessageException("Unable to load the message data", dfe);
} }
} }
protected byte[] doWriteMessage() throws I2CPMessageException, IOException { protected byte[] doWriteMessage() throws I2CPMessageException, IOException {
if ( (_sessionId == null) || (_messageId == null) || (_status < 0) || (_nonce <= 0) ) if ((_sessionId == null) || (_messageId == null) || (_status < 0) || (_nonce <= 0))
throw new I2CPMessageException("Unable to write out the message as there is not enough data"); throw new I2CPMessageException("Unable to write out the message as there is not enough data");
ByteArrayOutputStream os = new ByteArrayOutputStream(64); ByteArrayOutputStream os = new ByteArrayOutputStream(64);
try { try {
@@ -98,23 +135,23 @@ public class MessageStatusMessage extends I2CPMessageImpl {
} }
return os.toByteArray(); return os.toByteArray();
} }
public int getType() { return MESSAGE_TYPE; } public int getType() {
return MESSAGE_TYPE;
}
public boolean equals(Object object) { public boolean equals(Object object) {
if ( (object != null) && (object instanceof MessageStatusMessage) ) { if ((object != null) && (object instanceof MessageStatusMessage)) {
MessageStatusMessage msg = (MessageStatusMessage)object; MessageStatusMessage msg = (MessageStatusMessage) object;
return DataHelper.eq(getSessionId(),msg.getSessionId()) && return DataHelper.eq(getSessionId(), msg.getSessionId())
DataHelper.eq(getMessageId(),msg.getMessageId()) && && DataHelper.eq(getMessageId(), msg.getMessageId()) && (getNonce() == msg.getNonce())
(getNonce() == msg.getNonce()) && && DataHelper.eq(getSize(), msg.getSize()) && DataHelper.eq(getStatus(), msg.getStatus());
DataHelper.eq(getSize(),msg.getSize()) &&
DataHelper.eq(getStatus(),msg.getStatus());
} else { } else {
return false; return false;
} }
} }
public String toString() { public String toString() {
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
buf.append("[MessageStatusMessage: "); buf.append("[MessageStatusMessage: ");
buf.append("\n\tSessionId: ").append(getSessionId()); buf.append("\n\tSessionId: ").append(getSessionId());
@@ -125,4 +162,4 @@ public class MessageStatusMessage extends I2CPMessageImpl {
buf.append("]"); buf.append("]");
return buf.toString(); return buf.toString();
} }
} }

Some files were not shown because too many files have changed in this diff Show More