big ol' update to strip out the singletons, replacing them with
a rooted app context. The core itself has its own I2PAppContext (see its javadoc for, uh, docs), and the router extends that to expose the router's singletons. The main point of this is to make it so that we can run multiple routers in the same JVM, even to allow different apps in the same JVM to switch singleton implementations (e.g. run some routers with one set of profile calculators, and other routers with a different one). There is still some work to be done regarding the actual boot up of multiple routers in a JVM, as well as their configuration, though the plan is to have the RouterContext override the I2PAppContext's getProperty/getPropertyNames methods to read from a config file (seperate ones per context) instead of using the System.getProperty that the base I2PAppContext uses. Once the multi-router is working, i'll shim in a VMCommSystem that doesn't depend upon sockets or threads to read/write (and that uses configurable message send delays / disconnects / etc, perhaps using data from the routerContext.getProperty to drive it). I could hold off until the sim is all working, but there's a truckload of changes in here and I hate dealing with conflicts ;) Everything works - I've been running 'er for a while and kicked the tires a bit, but if you see something amiss, please let me know.
This commit is contained in:
425
core/java/src/net/i2p/I2PAppContext.java
Normal file
425
core/java/src/net/i2p/I2PAppContext.java
Normal file
@ -0,0 +1,425 @@
|
||||
package net.i2p;
|
||||
|
||||
import net.i2p.stat.StatManager;
|
||||
import net.i2p.crypto.SessionKeyManager;
|
||||
import net.i2p.crypto.PersistentSessionKeyManager;
|
||||
import net.i2p.crypto.ElGamalAESEngine;
|
||||
import net.i2p.crypto.ElGamalEngine;
|
||||
import net.i2p.crypto.DummyElGamalEngine;
|
||||
import net.i2p.crypto.SHA256Generator;
|
||||
import net.i2p.crypto.HMACSHA256Generator;
|
||||
import net.i2p.crypto.AESEngine;
|
||||
import net.i2p.crypto.CryptixAESEngine;
|
||||
import net.i2p.crypto.DSAEngine;
|
||||
import net.i2p.client.naming.NamingService;
|
||||
import net.i2p.util.LogManager;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.RandomSource;
|
||||
import net.i2p.data.RoutingKeyGenerator;
|
||||
import net.i2p.crypto.KeyGenerator;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* <p>Provide a base scope for accessing singletons that I2P exposes. Rather than
|
||||
* using the traditional singleton, where any component can access the component
|
||||
* in question directly, all of those I2P related singletons are exposed through
|
||||
* a particular I2PAppContext. This helps not only with understanding their use
|
||||
* and the components I2P exposes, but it also allows multiple isolated
|
||||
* environments to operate concurrently within the same JVM - particularly useful
|
||||
* for stubbing out implementations of the rooted components and simulating the
|
||||
* software's interaction between multiple instances.</p>
|
||||
*
|
||||
* As a simplification, there is also a global context - if some component needs
|
||||
* access to one of the singletons but doesn't have its own context from which
|
||||
* to root itself, it binds to the I2PAppContext's globalAppContext(), which is
|
||||
* the first context that was created within the JVM, or a new one if no context
|
||||
* existed already. This functionality is often used within the I2P core for
|
||||
* logging - e.g. <pre>
|
||||
* private static final Log _log = new Log(someClass.class);
|
||||
* </pre>
|
||||
* It is for this reason that applications that care about working with multiple
|
||||
* contexts should build their own context as soon as possible (within the main(..))
|
||||
* so that any referenced components will latch on to that context instead of
|
||||
* instantiating a new one. However, there are situations in which both can be
|
||||
* relevent.
|
||||
*
|
||||
*/
|
||||
public class I2PAppContext {
|
||||
/** the context that components without explicit root are bound */
|
||||
protected static I2PAppContext _globalAppContext;
|
||||
/**
|
||||
* Determine if the app context been initialized. If this is false
|
||||
* and something asks for the globalAppContext, a new one is created,
|
||||
* otherwise the existing one is used.
|
||||
*
|
||||
*/
|
||||
protected static volatile boolean _globalAppContextInitialized;
|
||||
|
||||
private StatManager _statManager;
|
||||
private SessionKeyManager _sessionKeyManager;
|
||||
private NamingService _namingService;
|
||||
private ElGamalEngine _elGamalEngine;
|
||||
private ElGamalAESEngine _elGamalAESEngine;
|
||||
private AESEngine _AESEngine;
|
||||
private LogManager _logManager;
|
||||
private HMACSHA256Generator _hmac;
|
||||
private SHA256Generator _sha;
|
||||
private Clock _clock;
|
||||
private DSAEngine _dsa;
|
||||
private RoutingKeyGenerator _routingKeyGenerator;
|
||||
private RandomSource _random;
|
||||
private KeyGenerator _keyGenerator;
|
||||
private volatile boolean _statManagerInitialized;
|
||||
private volatile boolean _sessionKeyManagerInitialized;
|
||||
private volatile boolean _namingServiceInitialized;
|
||||
private volatile boolean _elGamalEngineInitialized;
|
||||
private volatile boolean _elGamalAESEngineInitialized;
|
||||
private volatile boolean _AESEngineInitialized;
|
||||
private volatile boolean _logManagerInitialized;
|
||||
private volatile boolean _hmacInitialized;
|
||||
private volatile boolean _shaInitialized;
|
||||
private volatile boolean _clockInitialized;
|
||||
private volatile boolean _dsaInitialized;
|
||||
private volatile boolean _routingKeyGeneratorInitialized;
|
||||
private volatile boolean _randomInitialized;
|
||||
private volatile boolean _keyGeneratorInitialized;
|
||||
|
||||
/**
|
||||
* Pull the default context, creating a new one if necessary, else using
|
||||
* the first one created.
|
||||
*
|
||||
*/
|
||||
public static I2PAppContext getGlobalContext() {
|
||||
if (!_globalAppContextInitialized) {
|
||||
synchronized (I2PAppContext.class) {
|
||||
System.err.println("*** Building seperate global context!");
|
||||
if (_globalAppContext == null)
|
||||
_globalAppContext = new I2PAppContext(false);
|
||||
_globalAppContextInitialized = true;
|
||||
}
|
||||
}
|
||||
return _globalAppContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets root a brand new context
|
||||
*
|
||||
*/
|
||||
public I2PAppContext() {
|
||||
this(true);
|
||||
}
|
||||
/**
|
||||
* @param doInit should this context be used as the global one (if necessary)?
|
||||
*/
|
||||
private I2PAppContext(boolean doInit) {
|
||||
//System.out.println("App context created: " + this);
|
||||
if (doInit) {
|
||||
if (!_globalAppContextInitialized) {
|
||||
synchronized (I2PAppContext.class) {
|
||||
if (_globalAppContext == null) {
|
||||
_globalAppContext = this;
|
||||
_globalAppContextInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_statManager = null;
|
||||
_sessionKeyManager = null;
|
||||
_namingService = null;
|
||||
_elGamalEngine = null;
|
||||
_elGamalAESEngine = null;
|
||||
_logManager = null;
|
||||
_statManagerInitialized = false;
|
||||
_sessionKeyManagerInitialized = false;
|
||||
_namingServiceInitialized = false;
|
||||
_elGamalEngineInitialized = false;
|
||||
_elGamalAESEngineInitialized = false;
|
||||
_logManagerInitialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the configuration attributes of this context (aka System.getProperty)
|
||||
* This can be overloaded by subclasses to allow different system
|
||||
* properties for different app contexts.
|
||||
*
|
||||
*/
|
||||
public String getProperty(String propName) {
|
||||
return System.getProperty(propName);
|
||||
}
|
||||
/**
|
||||
* Access the configuration attributes of this context (aka System.getProperty)
|
||||
* This can be overloaded by subclasses to allow different system
|
||||
* properties for different app contexts.
|
||||
*
|
||||
*/
|
||||
public String getProperty(String propName, String defaultValue) {
|
||||
return System.getProperty(propName, defaultValue);
|
||||
}
|
||||
/**
|
||||
* Access the configuration attributes of this context (aka System.getProperties)
|
||||
* This can be overloaded by subclasses to allow different system
|
||||
* properties for different app contexts.
|
||||
*
|
||||
* @return set of Strings containing the names of defined system properties
|
||||
*/
|
||||
public Set getPropertyNames() {
|
||||
return new HashSet(System.getProperties().keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* The statistics component with which we can track various events
|
||||
* over time.
|
||||
*/
|
||||
public StatManager statManager() {
|
||||
if (!_statManagerInitialized) initializeStatManager();
|
||||
return _statManager;
|
||||
}
|
||||
private void initializeStatManager() {
|
||||
synchronized (this) {
|
||||
if (_statManager == null)
|
||||
_statManager = new StatManager(this);
|
||||
_statManagerInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The session key manager which coordinates the sessionKey / sessionTag
|
||||
* data. This component allows transparent operation of the
|
||||
* ElGamal/AES+SessionTag algorithm, and contains all of the session tags
|
||||
* for one particular application. If you want to seperate multiple apps
|
||||
* to have their own sessionTags and sessionKeys, they should use different
|
||||
* I2PAppContexts, and hence, different sessionKeyManagers.
|
||||
*
|
||||
*/
|
||||
public SessionKeyManager sessionKeyManager() {
|
||||
if (!_sessionKeyManagerInitialized) initializeSessionKeyManager();
|
||||
return _sessionKeyManager;
|
||||
}
|
||||
private void initializeSessionKeyManager() {
|
||||
synchronized (this) {
|
||||
if (_sessionKeyManager == null)
|
||||
_sessionKeyManager = new PersistentSessionKeyManager(this);
|
||||
_sessionKeyManagerInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull up the naming service used in this context. The naming service itself
|
||||
* works by querying the context's properties, so those props should be
|
||||
* specified to customize the naming service exposed.
|
||||
*/
|
||||
public NamingService namingService() {
|
||||
if (!_namingServiceInitialized) initializeNamingService();
|
||||
return _namingService;
|
||||
}
|
||||
private void initializeNamingService() {
|
||||
synchronized (this) {
|
||||
if (_namingService == null) {
|
||||
_namingService = NamingService.createInstance(this);
|
||||
}
|
||||
_namingServiceInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the ElGamal engine used within this context. While it doesn't
|
||||
* really have anything substantial that is context specific (the algorithm
|
||||
* just does the algorithm), it does transparently use the context for logging
|
||||
* its performance and activity. In addition, the engine can be swapped with
|
||||
* the context's properties (though only someone really crazy should mess with
|
||||
* it ;)
|
||||
*/
|
||||
public ElGamalEngine elGamalEngine() {
|
||||
if (!_elGamalEngineInitialized) initializeElGamalEngine();
|
||||
return _elGamalEngine;
|
||||
}
|
||||
private void initializeElGamalEngine() {
|
||||
synchronized (this) {
|
||||
if (_elGamalEngine == null) {
|
||||
if ("off".equals(getProperty("i2p.encryption", "on")))
|
||||
_elGamalEngine = new DummyElGamalEngine(this);
|
||||
else
|
||||
_elGamalEngine = new ElGamalEngine(this);
|
||||
}
|
||||
_elGamalEngineInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the ElGamal/AES+SessionTag engine for this context. The algorithm
|
||||
* makes use of the context's sessionKeyManager to coordinate transparent
|
||||
* access to the sessionKeys and sessionTags, as well as the context's elGamal
|
||||
* engine (which in turn keeps stats, etc).
|
||||
*
|
||||
*/
|
||||
public ElGamalAESEngine elGamalAESEngine() {
|
||||
if (!_elGamalAESEngineInitialized) initializeElGamalAESEngine();
|
||||
return _elGamalAESEngine;
|
||||
}
|
||||
private void initializeElGamalAESEngine() {
|
||||
synchronized (this) {
|
||||
if (_elGamalAESEngine == null)
|
||||
_elGamalAESEngine = new ElGamalAESEngine(this);
|
||||
_elGamalAESEngineInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ok, I'll admit it. there is no good reason for having a context specific
|
||||
* AES engine. We dont really keep stats on it, since its just too fast to
|
||||
* matter. Though for the crazy people out there, we do expose a way to
|
||||
* disable it.
|
||||
*/
|
||||
public AESEngine AESEngine() {
|
||||
if (!_AESEngineInitialized) initializeAESEngine();
|
||||
return _AESEngine;
|
||||
}
|
||||
private void initializeAESEngine() {
|
||||
synchronized (this) {
|
||||
if (_AESEngine == null) {
|
||||
if ("off".equals(getProperty("i2p.encryption", "on")))
|
||||
_AESEngine = new AESEngine(this);
|
||||
else
|
||||
_AESEngine = new CryptixAESEngine(this);
|
||||
}
|
||||
_AESEngineInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the log manager for this context, which may in turn have its own
|
||||
* set of configuration settings (loaded from the context's properties).
|
||||
* Each context's logManager keeps its own isolated set of Log instances with
|
||||
* their own log levels, output locations, and rotation configuration.
|
||||
*/
|
||||
public LogManager logManager() {
|
||||
if (!_logManagerInitialized) initializeLogManager();
|
||||
return _logManager;
|
||||
}
|
||||
private void initializeLogManager() {
|
||||
synchronized (this) {
|
||||
if (_logManager == null)
|
||||
_logManager = new LogManager(this);
|
||||
_logManagerInitialized = true;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* There is absolutely no good reason to make this context specific,
|
||||
* other than for consistency, and perhaps later we'll want to
|
||||
* include some stats.
|
||||
*/
|
||||
public HMACSHA256Generator hmac() {
|
||||
if (!_hmacInitialized) initializeHMAC();
|
||||
return _hmac;
|
||||
}
|
||||
private void initializeHMAC() {
|
||||
synchronized (this) {
|
||||
if (_hmac == null)
|
||||
_hmac= new HMACSHA256Generator(this);
|
||||
_hmacInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Our SHA256 instance (see the hmac discussion for why its context specific)
|
||||
*
|
||||
*/
|
||||
public SHA256Generator sha() {
|
||||
if (!_shaInitialized) initializeSHA();
|
||||
return _sha;
|
||||
}
|
||||
private void initializeSHA() {
|
||||
synchronized (this) {
|
||||
if (_sha == null)
|
||||
_sha= new SHA256Generator(this);
|
||||
_shaInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Our DSA engine (see HMAC and SHA above)
|
||||
*
|
||||
*/
|
||||
public DSAEngine dsa() {
|
||||
if (!_dsaInitialized) initializeDSA();
|
||||
return _dsa;
|
||||
}
|
||||
private void initializeDSA() {
|
||||
synchronized (this) {
|
||||
if (_dsa == null)
|
||||
_dsa = new DSAEngine(this);
|
||||
_dsaInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Component to generate ElGamal, DSA, and Session keys. For why it is in
|
||||
* the appContext, see the DSA, HMAC, and SHA comments above.
|
||||
*/
|
||||
public KeyGenerator keyGenerator() {
|
||||
if (!_keyGeneratorInitialized) initializeKeyGenerator();
|
||||
return _keyGenerator;
|
||||
}
|
||||
private void initializeKeyGenerator() {
|
||||
synchronized (this) {
|
||||
if (_keyGenerator == null)
|
||||
_keyGenerator = new KeyGenerator(this);
|
||||
_keyGeneratorInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The context's synchronized clock, which is kept context specific only to
|
||||
* enable simulators to play with clock skew among different instances.
|
||||
*
|
||||
*/
|
||||
public Clock clock() {
|
||||
if (!_clockInitialized) initializeClock();
|
||||
return _clock;
|
||||
}
|
||||
private void initializeClock() {
|
||||
synchronized (this) {
|
||||
if (_clock == null)
|
||||
_clock = new Clock(this);
|
||||
_clockInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine how much do we want to mess with the keys to turn them
|
||||
* into something we can route. This is context specific because we
|
||||
* may want to test out how things react when peers don't agree on
|
||||
* how to skew.
|
||||
*
|
||||
*/
|
||||
public RoutingKeyGenerator routingKeyGenerator() {
|
||||
if (!_routingKeyGeneratorInitialized) initializeRoutingKeyGenerator();
|
||||
return _routingKeyGenerator;
|
||||
}
|
||||
private void initializeRoutingKeyGenerator() {
|
||||
synchronized (this) {
|
||||
if (_routingKeyGenerator == null)
|
||||
_routingKeyGenerator = new RoutingKeyGenerator(this);
|
||||
_routingKeyGeneratorInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [insert snarky comment here]
|
||||
*
|
||||
*/
|
||||
public RandomSource random() {
|
||||
if (!_randomInitialized) initializeRandom();
|
||||
return _random;
|
||||
}
|
||||
private void initializeRandom() {
|
||||
synchronized (this) {
|
||||
if (_random == null)
|
||||
_random = new RandomSource(this);
|
||||
_randomInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@ import net.i2p.util.Clock;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.LogManager;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* ATalk - anonymous talk, demonstrating a trivial I2P usage scenario.
|
||||
@ -290,6 +291,7 @@ public class ATalk implements I2PSessionListener, Runnable {
|
||||
|
||||
/** driver */
|
||||
public static void main(String args[]) {
|
||||
I2PAppContext context = new I2PAppContext();
|
||||
if (args.length == 2) {
|
||||
String myKeyFile = args[0];
|
||||
String myDestinationFile = args[1];
|
||||
@ -309,9 +311,9 @@ public class ATalk implements I2PSessionListener, Runnable {
|
||||
String peerDestFile = args[1];
|
||||
String shouldLog = args[2];
|
||||
if (Boolean.TRUE.toString().equalsIgnoreCase(shouldLog))
|
||||
LogManager.getInstance().setDisplayOnScreen(true);
|
||||
context.logManager().setDisplayOnScreen(true);
|
||||
else
|
||||
LogManager.getInstance().setDisplayOnScreen(false);
|
||||
context.logManager().setDisplayOnScreen(false);
|
||||
String logFile = args[2];
|
||||
Thread talkThread = new I2PThread(new ATalk(myKeyfile, peerDestFile));
|
||||
talkThread.start();
|
||||
|
@ -11,6 +11,7 @@ package net.i2p.client;
|
||||
|
||||
import net.i2p.data.i2cp.DisconnectMessage;
|
||||
import net.i2p.data.i2cp.I2CPMessage;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Handle I2CP disconnect messages from the router
|
||||
@ -18,8 +19,8 @@ import net.i2p.data.i2cp.I2CPMessage;
|
||||
* @author jrandom
|
||||
*/
|
||||
class DisconnectMessageHandler extends HandlerImpl {
|
||||
public DisconnectMessageHandler() {
|
||||
super(DisconnectMessage.MESSAGE_TYPE);
|
||||
public DisconnectMessageHandler(I2PAppContext context) {
|
||||
super(context, DisconnectMessage.MESSAGE_TYPE);
|
||||
}
|
||||
|
||||
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
|
||||
|
@ -10,6 +10,7 @@ package net.i2p.client;
|
||||
*/
|
||||
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Base class for handling I2CP messages
|
||||
@ -19,8 +20,10 @@ import net.i2p.util.Log;
|
||||
abstract class HandlerImpl implements I2CPMessageHandler {
|
||||
protected Log _log;
|
||||
private int _type;
|
||||
protected I2PAppContext _context;
|
||||
|
||||
public HandlerImpl(int type) {
|
||||
public HandlerImpl(I2PAppContext context, int type) {
|
||||
_context = context;
|
||||
_type = type;
|
||||
_log = new Log(getClass());
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import net.i2p.data.i2cp.SendMessageMessage;
|
||||
import net.i2p.data.i2cp.SessionConfig;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.RandomSource;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Produce the various messages the session needs to send to the router.
|
||||
@ -41,7 +42,12 @@ import net.i2p.util.RandomSource;
|
||||
class I2CPMessageProducer {
|
||||
private final static Log _log = new Log(I2CPMessageProducer.class);
|
||||
private final static RandomSource _rand = RandomSource.getInstance();
|
||||
private I2PAppContext _context;
|
||||
|
||||
public I2CPMessageProducer(I2PAppContext context) {
|
||||
_context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send all the messages that a client needs to send to a router to establish
|
||||
* a new session.
|
||||
@ -102,7 +108,7 @@ class I2CPMessageProducer {
|
||||
Payload data = new Payload();
|
||||
// randomize padding
|
||||
int size = payload.length + RandomSource.getInstance().nextInt(1024);
|
||||
byte encr[] = ElGamalAESEngine.encrypt(payload, dest.getPublicKey(), key, tags, tag, newKey, size);
|
||||
byte encr[] = _context.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
|
||||
// generateNewTags would only generate tags if necessary
|
||||
|
||||
|
@ -22,6 +22,7 @@ import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Base client implementation
|
||||
@ -70,7 +71,13 @@ class I2PClientImpl implements I2PClient {
|
||||
*
|
||||
*/
|
||||
public I2PSession createSession(InputStream destKeyStream, Properties options) throws I2PSessionException {
|
||||
//return new I2PSessionImpl(destKeyStream, options); // not thread safe
|
||||
return new I2PSessionImpl2(destKeyStream, options); // thread safe
|
||||
return createSession(I2PAppContext.getGlobalContext(), destKeyStream, options);
|
||||
}
|
||||
/**
|
||||
* Create a new session (though do not connect it yet)
|
||||
*
|
||||
*/
|
||||
public I2PSession createSession(I2PAppContext context, InputStream destKeyStream, Properties options) throws I2PSessionException {
|
||||
return new I2PSessionImpl2(context, destKeyStream, options); // thread safe
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ import net.i2p.data.i2cp.RequestLeaseSetMessage;
|
||||
import net.i2p.data.i2cp.SessionStatusMessage;
|
||||
import net.i2p.data.i2cp.SetDateMessage;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Contains a map of message handlers that a session will want to use
|
||||
@ -28,19 +29,19 @@ import net.i2p.util.Log;
|
||||
class I2PClientMessageHandlerMap {
|
||||
private final static Log _log = new Log(I2PClientMessageHandlerMap.class);
|
||||
/** map of message type id --> I2CPMessageHandler */
|
||||
private static Map _handlers;
|
||||
private Map _handlers;
|
||||
|
||||
static {
|
||||
public I2PClientMessageHandlerMap(I2PAppContext context) {
|
||||
_handlers = new HashMap();
|
||||
_handlers.put(new Integer(DisconnectMessage.MESSAGE_TYPE), new DisconnectMessageHandler());
|
||||
_handlers.put(new Integer(SessionStatusMessage.MESSAGE_TYPE), new SessionStatusMessageHandler());
|
||||
_handlers.put(new Integer(RequestLeaseSetMessage.MESSAGE_TYPE), new RequestLeaseSetMessageHandler());
|
||||
_handlers.put(new Integer(MessagePayloadMessage.MESSAGE_TYPE), new MessagePayloadMessageHandler());
|
||||
_handlers.put(new Integer(MessageStatusMessage.MESSAGE_TYPE), new MessageStatusMessageHandler());
|
||||
_handlers.put(new Integer(SetDateMessage.MESSAGE_TYPE), new SetDateMessageHandler());
|
||||
_handlers.put(new Integer(DisconnectMessage.MESSAGE_TYPE), new DisconnectMessageHandler(context));
|
||||
_handlers.put(new Integer(SessionStatusMessage.MESSAGE_TYPE), new SessionStatusMessageHandler(context));
|
||||
_handlers.put(new Integer(RequestLeaseSetMessage.MESSAGE_TYPE), new RequestLeaseSetMessageHandler(context));
|
||||
_handlers.put(new Integer(MessagePayloadMessage.MESSAGE_TYPE), new MessagePayloadMessageHandler(context));
|
||||
_handlers.put(new Integer(MessageStatusMessage.MESSAGE_TYPE), new MessageStatusMessageHandler(context));
|
||||
_handlers.put(new Integer(SetDateMessage.MESSAGE_TYPE), new SetDateMessageHandler(context));
|
||||
}
|
||||
|
||||
public static I2CPMessageHandler getHandler(int messageTypeId) {
|
||||
public I2CPMessageHandler getHandler(int messageTypeId) {
|
||||
I2CPMessageHandler handler = (I2CPMessageHandler) _handlers.get(new Integer(messageTypeId));
|
||||
return handler;
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ import net.i2p.data.i2cp.SessionId;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Implementation of an I2P session running over TCP. This class is NOT thread safe -
|
||||
@ -47,7 +48,7 @@ import net.i2p.util.Log;
|
||||
* @author jrandom
|
||||
*/
|
||||
abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessageEventListener {
|
||||
private final static Log _log = new Log(I2PSessionImpl.class);
|
||||
private Log _log;
|
||||
/** who we are */
|
||||
private Destination _myDestination;
|
||||
/** private key for decryption */
|
||||
@ -79,6 +80,11 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
protected I2CPMessageProducer _producer;
|
||||
/** map of integer --> MessagePayloadMessage */
|
||||
Map _availableMessages;
|
||||
|
||||
protected I2PClientMessageHandlerMap _handlerMap;
|
||||
|
||||
/** used to seperate things out so we can get rid of singletons */
|
||||
protected I2PAppContext _context;
|
||||
|
||||
/** MessageStatusMessage status from the most recent send that hasn't been consumed */
|
||||
private List _receivedStatus;
|
||||
@ -108,9 +114,12 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
*
|
||||
* @throws I2PSessionException if there is a problem loading the private keys or
|
||||
*/
|
||||
public I2PSessionImpl(InputStream destKeyStream, Properties options) throws I2PSessionException {
|
||||
public I2PSessionImpl(I2PAppContext context, InputStream destKeyStream, Properties options) throws I2PSessionException {
|
||||
_context = context;
|
||||
_log = context.logManager().getLog(I2PSessionImpl.class);
|
||||
_handlerMap = new I2PClientMessageHandlerMap(context);
|
||||
_closed = true;
|
||||
_producer = new I2CPMessageProducer();
|
||||
_producer = new I2CPMessageProducer(context);
|
||||
_availableMessages = new HashMap();
|
||||
try {
|
||||
readDestination(destKeyStream);
|
||||
@ -139,13 +148,13 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
_portNum = Integer.parseInt(portNum);
|
||||
} catch (NumberFormatException nfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Invalid port number specified, defaulting to "
|
||||
+ TestServer.LISTEN_PORT, nfe);
|
||||
_log.warn("Invalid port number specified, defaulting to "
|
||||
+ TestServer.LISTEN_PORT, nfe);
|
||||
_portNum = TestServer.LISTEN_PORT;
|
||||
}
|
||||
}
|
||||
|
||||
private static Properties filter(Properties options) {
|
||||
private Properties filter(Properties options) {
|
||||
Properties rv = new Properties();
|
||||
for (Iterator iter = options.keySet().iterator(); iter.hasNext();) {
|
||||
String key = (String) iter.next();
|
||||
@ -212,7 +221,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
*/
|
||||
public void connect() throws I2PSessionException {
|
||||
_closed = false;
|
||||
long startConnect = Clock.getInstance().now();
|
||||
long startConnect = _context.clock().now();
|
||||
try {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("connect begin to " + _hostname + ":" + _portNum);
|
||||
_socket = new Socket(_hostname, _portNum);
|
||||
@ -251,7 +260,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
}
|
||||
}
|
||||
}
|
||||
long connected = Clock.getInstance().now();
|
||||
long connected = _context.clock().now();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Lease set created with inbound tunnels after "
|
||||
+ (connected - startConnect)
|
||||
@ -339,7 +348,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
*
|
||||
*/
|
||||
public void messageReceived(I2CPMessageReader reader, I2CPMessage message) {
|
||||
I2CPMessageHandler handler = I2PClientMessageHandlerMap.getHandler(message.getType());
|
||||
I2CPMessageHandler handler = _handlerMap.getHandler(message.getType());
|
||||
if (handler == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unknown message or unhandleable message received: type = "
|
||||
|
@ -26,6 +26,7 @@ import net.i2p.data.i2cp.MessageStatusMessage;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.RandomSource;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Thread safe implementation of an I2P session running over TCP.
|
||||
@ -33,7 +34,7 @@ import net.i2p.util.RandomSource;
|
||||
* @author jrandom
|
||||
*/
|
||||
class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
private final static Log _log = new Log(I2PSessionImpl2.class);
|
||||
private Log _log;
|
||||
|
||||
/** set of MessageState objects, representing all of the messages in the process of being sent */
|
||||
private Set _sendingStates;
|
||||
@ -48,8 +49,9 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
*
|
||||
* @throws I2PSessionException if there is a problem loading the private keys or
|
||||
*/
|
||||
public I2PSessionImpl2(InputStream destKeyStream, Properties options) throws I2PSessionException {
|
||||
super(destKeyStream, options);
|
||||
public I2PSessionImpl2(I2PAppContext ctx, InputStream destKeyStream, Properties options) throws I2PSessionException {
|
||||
super(ctx, destKeyStream, options);
|
||||
_log = ctx.logManager().getLog(I2PSessionImpl2.class);
|
||||
_sendingStates = new HashSet(32);
|
||||
}
|
||||
|
||||
@ -95,22 +97,22 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
|
||||
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);
|
||||
SessionKey key = _context.sessionKeyManager().getCurrentKey(dest.getPublicKey());
|
||||
if (key == null) key = _context.sessionKeyManager().createSession(dest.getPublicKey());
|
||||
SessionTag tag = _context.sessionKeyManager().consumeNextAvailableTag(dest.getPublicKey(), key);
|
||||
Set sentTags = null;
|
||||
if (SessionKeyManager.getInstance().getAvailableTags(dest.getPublicKey(), key) < 10) {
|
||||
if (_context.sessionKeyManager().getAvailableTags(dest.getPublicKey(), key) < 10) {
|
||||
sentTags = createNewTags(50);
|
||||
} else if (SessionKeyManager.getInstance().getAvailableTimeLeft(dest.getPublicKey(), key) < 30 * 1000) {
|
||||
} else if (_context.sessionKeyManager().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();
|
||||
newKey = _context.keyGenerator().generateSessionKey();
|
||||
|
||||
long nonce = (long) RandomSource.getInstance().nextInt(Integer.MAX_VALUE);
|
||||
long nonce = (long)_context.random().nextInt(Integer.MAX_VALUE);
|
||||
MessageState state = new MessageState(nonce);
|
||||
state.setKey(key);
|
||||
state.setTags(sentTags);
|
||||
@ -137,7 +139,8 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
_log.debug("Adding sending state " + state.getMessageId() + " / "
|
||||
+ state.getNonce());
|
||||
_producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey);
|
||||
state.waitFor(MessageStatusMessage.STATUS_SEND_ACCEPTED, Clock.getInstance().now() + getTimeout());
|
||||
state.waitFor(MessageStatusMessage.STATUS_SEND_ACCEPTED,
|
||||
_context.clock().now() + getTimeout());
|
||||
synchronized (_sendingStates) {
|
||||
_sendingStates.remove(state);
|
||||
}
|
||||
@ -163,22 +166,22 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
|
||||
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);
|
||||
SessionKey key = _context.sessionKeyManager().getCurrentKey(dest.getPublicKey());
|
||||
if (key == null) key = _context.sessionKeyManager().createSession(dest.getPublicKey());
|
||||
SessionTag tag = _context.sessionKeyManager().consumeNextAvailableTag(dest.getPublicKey(), key);
|
||||
Set sentTags = null;
|
||||
if (SessionKeyManager.getInstance().getAvailableTags(dest.getPublicKey(), key) < 10) {
|
||||
if (_context.sessionKeyManager().getAvailableTags(dest.getPublicKey(), key) < 10) {
|
||||
sentTags = createNewTags(50);
|
||||
} else if (SessionKeyManager.getInstance().getAvailableTimeLeft(dest.getPublicKey(), key) < 30 * 1000) {
|
||||
} else if (_context.sessionKeyManager().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();
|
||||
newKey = _context.keyGenerator().generateSessionKey();
|
||||
|
||||
long nonce = (long) RandomSource.getInstance().nextInt(Integer.MAX_VALUE);
|
||||
long nonce = (long)_context.random().nextInt(Integer.MAX_VALUE);
|
||||
MessageState state = new MessageState(nonce);
|
||||
state.setKey(key);
|
||||
state.setTags(sentTags);
|
||||
@ -206,9 +209,11 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
+ state.getNonce());
|
||||
_producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey);
|
||||
if (isGuaranteed())
|
||||
state.waitFor(MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS, Clock.getInstance().now() + SEND_TIMEOUT);
|
||||
state.waitFor(MessageStatusMessage.STATUS_SEND_GUARANTEED_SUCCESS,
|
||||
_context.clock().now() + SEND_TIMEOUT);
|
||||
else
|
||||
state.waitFor(MessageStatusMessage.STATUS_SEND_ACCEPTED, Clock.getInstance().now() + SEND_TIMEOUT);
|
||||
state.waitFor(MessageStatusMessage.STATUS_SEND_ACCEPTED,
|
||||
_context.clock().now() + SEND_TIMEOUT);
|
||||
synchronized (_sendingStates) {
|
||||
_sendingStates.remove(state);
|
||||
}
|
||||
@ -250,9 +255,9 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
+ state.getTags());
|
||||
if ((state.getTags() != null) && (state.getTags().size() > 0)) {
|
||||
if (state.getNewKey() == null)
|
||||
SessionKeyManager.getInstance().tagsDelivered(state.getTo().getPublicKey(), state.getKey(), state.getTags());
|
||||
_context.sessionKeyManager().tagsDelivered(state.getTo().getPublicKey(), state.getKey(), state.getTags());
|
||||
else
|
||||
SessionKeyManager.getInstance().tagsDelivered(state.getTo().getPublicKey(), state.getNewKey(), state.getTags());
|
||||
_context.sessionKeyManager().tagsDelivered(state.getTo().getPublicKey(), state.getNewKey(), state.getTags());
|
||||
}
|
||||
}
|
||||
|
||||
@ -260,7 +265,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("nack tags for msgId " + state.getMessageId() + " / " + state.getNonce()
|
||||
+ " key = " + state.getKey());
|
||||
SessionKeyManager.getInstance().failTags(state.getTo().getPublicKey());
|
||||
_context.sessionKeyManager().failTags(state.getTo().getPublicKey());
|
||||
}
|
||||
|
||||
public void receiveStatus(int msgId, long nonce, int status) {
|
||||
|
@ -16,6 +16,7 @@ import net.i2p.data.i2cp.I2CPMessage;
|
||||
import net.i2p.data.i2cp.MessageId;
|
||||
import net.i2p.data.i2cp.MessagePayloadMessage;
|
||||
import net.i2p.data.i2cp.ReceiveMessageEndMessage;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Handle I2CP MessagePayloadMessages from the router delivering the contents
|
||||
@ -25,8 +26,8 @@ import net.i2p.data.i2cp.ReceiveMessageEndMessage;
|
||||
* @author jrandom
|
||||
*/
|
||||
class MessagePayloadMessageHandler extends HandlerImpl {
|
||||
public MessagePayloadMessageHandler() {
|
||||
super(MessagePayloadMessage.MESSAGE_TYPE);
|
||||
public MessagePayloadMessageHandler(I2PAppContext context) {
|
||||
super(context, MessagePayloadMessage.MESSAGE_TYPE);
|
||||
}
|
||||
|
||||
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
|
||||
@ -53,7 +54,7 @@ class MessagePayloadMessageHandler extends HandlerImpl {
|
||||
*/
|
||||
private Payload decryptPayload(MessagePayloadMessage msg, I2PSessionImpl session) throws DataFormatException {
|
||||
Payload payload = msg.getPayload();
|
||||
byte[] data = ElGamalAESEngine.decrypt(payload.getEncryptedData(), session.getDecryptionKey());
|
||||
byte[] data = _context.elGamalAESEngine().decrypt(payload.getEncryptedData(), session.getDecryptionKey());
|
||||
if (data == null) {
|
||||
_log
|
||||
.error("Error decrypting the payload to public key "
|
||||
|
@ -12,6 +12,7 @@ package net.i2p.client;
|
||||
import net.i2p.data.i2cp.I2CPMessage;
|
||||
import net.i2p.data.i2cp.MessageStatusMessage;
|
||||
import net.i2p.data.i2cp.ReceiveMessageBeginMessage;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Handle I2CP MessageStatusMessages from the router. This currently only takes
|
||||
@ -21,8 +22,8 @@ import net.i2p.data.i2cp.ReceiveMessageBeginMessage;
|
||||
* @author jrandom
|
||||
*/
|
||||
class MessageStatusMessageHandler extends HandlerImpl {
|
||||
public MessageStatusMessageHandler() {
|
||||
super(MessageStatusMessage.MESSAGE_TYPE);
|
||||
public MessageStatusMessageHandler(I2PAppContext context) {
|
||||
super(context, MessageStatusMessage.MESSAGE_TYPE);
|
||||
}
|
||||
|
||||
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
|
||||
|
@ -25,6 +25,7 @@ import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.data.i2cp.I2CPMessage;
|
||||
import net.i2p.data.i2cp.RequestLeaseSetMessage;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Handle I2CP RequestLeaseSetMessage from the router by granting all leases
|
||||
@ -35,8 +36,8 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
|
||||
private final static Log _log = new Log(RequestLeaseSetMessageHandler.class);
|
||||
private Map _existingLeaseSets;
|
||||
|
||||
public RequestLeaseSetMessageHandler() {
|
||||
super(RequestLeaseSetMessage.MESSAGE_TYPE);
|
||||
public RequestLeaseSetMessageHandler(I2PAppContext context) {
|
||||
super(context, RequestLeaseSetMessage.MESSAGE_TYPE);
|
||||
_existingLeaseSets = new HashMap(32);
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ package net.i2p.client;
|
||||
|
||||
import net.i2p.data.i2cp.I2CPMessage;
|
||||
import net.i2p.data.i2cp.SessionStatusMessage;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Handle I2CP SessionStatusMessagese from the router, updating the session as
|
||||
@ -19,8 +20,8 @@ import net.i2p.data.i2cp.SessionStatusMessage;
|
||||
* @author jrandom
|
||||
*/
|
||||
class SessionStatusMessageHandler extends HandlerImpl {
|
||||
public SessionStatusMessageHandler() {
|
||||
super(SessionStatusMessage.MESSAGE_TYPE);
|
||||
public SessionStatusMessageHandler(I2PAppContext context) {
|
||||
super(context, SessionStatusMessage.MESSAGE_TYPE);
|
||||
}
|
||||
|
||||
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
|
||||
|
@ -12,6 +12,7 @@ package net.i2p.client;
|
||||
import net.i2p.data.i2cp.I2CPMessage;
|
||||
import net.i2p.data.i2cp.SetDateMessage;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Handle I2CP time messages from the router
|
||||
@ -19,8 +20,8 @@ import net.i2p.util.Clock;
|
||||
* @author jrandom
|
||||
*/
|
||||
class SetDateMessageHandler extends HandlerImpl {
|
||||
public SetDateMessageHandler() {
|
||||
super(SetDateMessage.MESSAGE_TYPE);
|
||||
public SetDateMessageHandler(I2PAppContext ctx) {
|
||||
super(ctx, SetDateMessage.MESSAGE_TYPE);
|
||||
}
|
||||
|
||||
public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
|
||||
|
@ -8,11 +8,21 @@
|
||||
package net.i2p.client.naming;
|
||||
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* A Dummy naming service that can only handle base64 destinations.
|
||||
*/
|
||||
class DummyNamingService extends NamingService {
|
||||
/**
|
||||
* The naming service should only be constructed and accessed through the
|
||||
* application context. This constructor should only be used by the
|
||||
* appropriate application context itself.
|
||||
*
|
||||
*/
|
||||
protected DummyNamingService(I2PAppContext context) { super(context); }
|
||||
private DummyNamingService() { super(null); }
|
||||
|
||||
public Destination lookup(String hostname) {
|
||||
return lookupBase64(hostname);
|
||||
}
|
||||
|
@ -14,12 +14,22 @@ import java.util.Properties;
|
||||
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* A naming service based on the "hosts.txt" file.
|
||||
*/
|
||||
public class HostsTxtNamingService extends NamingService {
|
||||
|
||||
/**
|
||||
* The naming service should only be constructed and accessed through the
|
||||
* application context. This constructor should only be used by the
|
||||
* appropriate application context itself.
|
||||
*
|
||||
*/
|
||||
protected HostsTxtNamingService(I2PAppContext context) { super(context); }
|
||||
private HostsTxtNamingService() { super(null); }
|
||||
|
||||
/**
|
||||
* If this system property is specified, the tunnel will read the
|
||||
* given file for hostname=destKey values when resolving names
|
||||
@ -35,7 +45,7 @@ public class HostsTxtNamingService extends NamingService {
|
||||
// Try to look it up in hosts.txt
|
||||
// Reload file each time to catch changes.
|
||||
// (and it's easier :P
|
||||
String hostsfile = System.getProperty(PROP_HOSTS_FILE, DEFAULT_HOSTS_FILE);
|
||||
String hostsfile = _context.getProperty(PROP_HOSTS_FILE, DEFAULT_HOSTS_FILE);
|
||||
Properties hosts = new Properties();
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
|
@ -10,6 +10,9 @@ package net.i2p.client.naming;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
/**
|
||||
* Naming services create a subclass of this class.
|
||||
@ -17,10 +20,23 @@ import net.i2p.util.Log;
|
||||
public abstract class NamingService {
|
||||
|
||||
private final static Log _log = new Log(NamingService.class);
|
||||
protected I2PAppContext _context;
|
||||
|
||||
private static final String PROP_IMPL = "i2p.naming.impl";
|
||||
private static final String DEFAULT_IMPL = "net.i2p.client.naming.HostsTxtNamingService";
|
||||
|
||||
|
||||
/**
|
||||
* The naming service should only be constructed and accessed through the
|
||||
* application context. This constructor should only be used by the
|
||||
* appropriate application context itself.
|
||||
*
|
||||
*/
|
||||
protected NamingService(I2PAppContext context) {
|
||||
_context = context;
|
||||
}
|
||||
private NamingService() {}
|
||||
|
||||
/**
|
||||
* Look up a host name.
|
||||
* @return the Destination for this host name, or
|
||||
@ -52,23 +68,22 @@ public abstract class NamingService {
|
||||
}
|
||||
}
|
||||
|
||||
private static NamingService instance = null;
|
||||
|
||||
/**
|
||||
* Get a naming service instance. This method ensures that there
|
||||
* will be only one naming service instance (singleton) as well as
|
||||
* choose the implementation from the "i2p.naming.impl" system
|
||||
* property.
|
||||
*/
|
||||
public static synchronized NamingService getInstance() {
|
||||
if (instance == null) {
|
||||
String impl = System.getProperty(PROP_IMPL, DEFAULT_IMPL);
|
||||
try {
|
||||
instance = (NamingService) Class.forName(impl).newInstance();
|
||||
} catch (Exception ex) {
|
||||
_log.error("Cannot loadNaming service implementation", ex);
|
||||
instance = new DummyNamingService(); // fallback
|
||||
}
|
||||
public static final synchronized NamingService createInstance(I2PAppContext context) {
|
||||
NamingService instance = null;
|
||||
String impl = context.getProperty(PROP_IMPL, DEFAULT_IMPL);
|
||||
try {
|
||||
Class cls = Class.forName(impl);
|
||||
Constructor con = cls.getConstructor(new Class[] { I2PAppContext.class });
|
||||
instance = (NamingService)con.newInstance(new Object[] { context });
|
||||
} catch (Exception ex) {
|
||||
_log.error("Cannot loadNaming service implementation", ex);
|
||||
instance = new DummyNamingService(context); // fallback
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
@ -19,26 +19,20 @@ import net.i2p.data.Hash;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.RandomSource;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Wrapper singleton for AES cypher operation.
|
||||
* Dummy wrapper for AES cipher operation.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class AESEngine {
|
||||
private final static Log _log = new Log(AESEngine.class);
|
||||
private static AESEngine _engine;
|
||||
static {
|
||||
if ("off".equals(System.getProperty("i2p.encryption", "on")))
|
||||
_engine = new AESEngine();
|
||||
else
|
||||
_engine = new CryptixAESEngine();
|
||||
private Log _log;
|
||||
private I2PAppContext _context;
|
||||
public AESEngine(I2PAppContext ctx) {
|
||||
_context = ctx;
|
||||
_log = _context.logManager().getLog(AESEngine.class);
|
||||
}
|
||||
|
||||
public static AESEngine getInstance() {
|
||||
return _engine;
|
||||
}
|
||||
|
||||
|
||||
/** Encrypt the payload with the session key
|
||||
* @param payload data to be encrypted
|
||||
* @param sessionKey private esession key to encrypt to
|
||||
@ -59,13 +53,13 @@ public class AESEngine {
|
||||
if ((iv == null) || (payload == null) || (sessionKey == null) || (iv.length != 16)) return null;
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(paddedSize + 64);
|
||||
Hash h = SHA256Generator.getInstance().calculateHash(sessionKey.getData());
|
||||
Hash h = _context.sha().calculateHash(sessionKey.getData());
|
||||
try {
|
||||
h.writeBytes(baos);
|
||||
DataHelper.writeLong(baos, 4, payload.length);
|
||||
baos.write(payload);
|
||||
byte tv[] = baos.toByteArray();
|
||||
baos.write(ElGamalAESEngine.getPadding(tv.length, paddedSize));
|
||||
baos.write(ElGamalAESEngine.getPadding(_context, tv.length, paddedSize));
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error writing data", ioe);
|
||||
return null;
|
||||
@ -85,7 +79,7 @@ public class AESEngine {
|
||||
return null;
|
||||
}
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(decr);
|
||||
Hash h = SHA256Generator.getInstance().calculateHash(sessionKey.getData());
|
||||
Hash h = _context.sha().calculateHash(sessionKey.getData());
|
||||
try {
|
||||
Hash rh = new Hash();
|
||||
rh.readBytes(bais);
|
||||
@ -127,20 +121,21 @@ public class AESEngine {
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
SessionKey key = KeyGenerator.getInstance().generateSessionKey();
|
||||
I2PAppContext ctx = new I2PAppContext();
|
||||
SessionKey key = ctx.keyGenerator().generateSessionKey();
|
||||
byte iv[] = new byte[16];
|
||||
RandomSource.getInstance().nextBytes(iv);
|
||||
|
||||
byte sbuf[] = new byte[16];
|
||||
RandomSource.getInstance().nextBytes(sbuf);
|
||||
byte se[] = AESEngine.getInstance().encrypt(sbuf, key, iv);
|
||||
byte sd[] = AESEngine.getInstance().decrypt(se, key, iv);
|
||||
_log.debug("Short test: " + DataHelper.eq(sd, sbuf));
|
||||
byte se[] = ctx.AESEngine().encrypt(sbuf, key, iv);
|
||||
byte sd[] = ctx.AESEngine().decrypt(se, key, iv);
|
||||
ctx.logManager().getLog(AESEngine.class).debug("Short test: " + DataHelper.eq(sd, sbuf));
|
||||
|
||||
byte lbuf[] = new byte[1024];
|
||||
RandomSource.getInstance().nextBytes(sbuf);
|
||||
byte le[] = AESEngine.getInstance().safeEncrypt(lbuf, key, iv, 2048);
|
||||
byte ld[] = AESEngine.getInstance().safeDecrypt(le, key, iv);
|
||||
_log.debug("Long test: " + DataHelper.eq(ld, lbuf));
|
||||
byte le[] = ctx.AESEngine().safeEncrypt(lbuf, key, iv, 2048);
|
||||
byte ld[] = ctx.AESEngine().safeDecrypt(le, key, iv);
|
||||
ctx.logManager().getLog(AESEngine.class).debug("Long test: " + DataHelper.eq(ld, lbuf));
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ import net.i2p.data.SessionKey;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.RandomSource;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* This reads an underlying stream as written by AESOutputStream - AES256 encrypted
|
||||
@ -36,8 +37,8 @@ import net.i2p.util.RandomSource;
|
||||
*
|
||||
*/
|
||||
public class AESInputStream extends FilterInputStream {
|
||||
private final static Log _log = new Log(AESInputStream.class);
|
||||
private final static CryptixAESEngine _engine = new CryptixAESEngine();
|
||||
private Log _log;
|
||||
private I2PAppContext _context;
|
||||
private SessionKey _key;
|
||||
private byte[] _lastBlock;
|
||||
private boolean _eofFound;
|
||||
@ -52,8 +53,10 @@ public class AESInputStream extends FilterInputStream {
|
||||
private final static int READ_SIZE = BLOCK_SIZE;
|
||||
private final static int DECRYPT_SIZE = BLOCK_SIZE - 1;
|
||||
|
||||
public AESInputStream(InputStream source, SessionKey key, byte iv[]) {
|
||||
public AESInputStream(I2PAppContext context, InputStream source, SessionKey key, byte iv[]) {
|
||||
super(source);
|
||||
_context = context;
|
||||
_log = context.logManager().getLog(AESInputStream.class);
|
||||
_key = key;
|
||||
_lastBlock = new byte[BLOCK_SIZE];
|
||||
System.arraycopy(iv, 0, _lastBlock, 0, BLOCK_SIZE);
|
||||
@ -223,8 +226,8 @@ public class AESInputStream extends FilterInputStream {
|
||||
byte block[] = new byte[BLOCK_SIZE];
|
||||
for (int i = 0; i < numBlocks; i++) {
|
||||
System.arraycopy(encrypted, i * BLOCK_SIZE, block, 0, BLOCK_SIZE);
|
||||
byte decrypted[] = _engine.decrypt(block, _key, _lastBlock);
|
||||
byte data[] = CryptixAESEngine.xor(decrypted, _lastBlock);
|
||||
byte decrypted[] = _context.AESEngine().decrypt(block, _key, _lastBlock);
|
||||
byte data[] = DataHelper.xor(decrypted, _lastBlock);
|
||||
int cleaned[] = stripPadding(data);
|
||||
for (int j = 0; j < cleaned.length; j++) {
|
||||
if (((int) cleaned[j]) <= 0) {
|
||||
@ -297,6 +300,8 @@ public class AESInputStream extends FilterInputStream {
|
||||
* Test AESOutputStream/AESInputStream
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
I2PAppContext ctx = new I2PAppContext();
|
||||
Log log = ctx.logManager().getLog(AESInputStream.class);
|
||||
byte orig[] = new byte[1024 * 32];
|
||||
RandomSource.getInstance().nextBytes(orig);
|
||||
//byte orig[] = "you are my sunshine, my only sunshine".getBytes();
|
||||
@ -304,40 +309,40 @@ public class AESInputStream extends FilterInputStream {
|
||||
byte iv[] = "there once was a".getBytes();
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
runTest(orig, key, iv);
|
||||
runTest(ctx, orig, key, iv);
|
||||
}
|
||||
|
||||
_log.info("Done testing 32KB data");
|
||||
log.info("Done testing 32KB data");
|
||||
|
||||
orig = new byte[20];
|
||||
RandomSource.getInstance().nextBytes(orig);
|
||||
for (int i = 0; i < 20; i++) {
|
||||
runTest(orig, key, iv);
|
||||
runTest(ctx, orig, key, iv);
|
||||
}
|
||||
|
||||
_log.info("Done testing 20 byte data");
|
||||
log.info("Done testing 20 byte data");
|
||||
|
||||
orig = new byte[3];
|
||||
RandomSource.getInstance().nextBytes(orig);
|
||||
for (int i = 0; i < 20; i++) {
|
||||
runTest(orig, key, iv);
|
||||
runTest(ctx, orig, key, iv);
|
||||
}
|
||||
|
||||
_log.info("Done testing 3 byte data");
|
||||
log.info("Done testing 3 byte data");
|
||||
|
||||
orig = new byte[0];
|
||||
RandomSource.getInstance().nextBytes(orig);
|
||||
for (int i = 0; i < 20; i++) {
|
||||
runTest(orig, key, iv);
|
||||
runTest(ctx, orig, key, iv);
|
||||
}
|
||||
|
||||
_log.info("Done testing 0 byte data");
|
||||
log.info("Done testing 0 byte data");
|
||||
|
||||
orig = new byte[32];
|
||||
RandomSource.getInstance().nextBytes(orig);
|
||||
runOffsetTest(orig, key, iv);
|
||||
runOffsetTest(ctx, 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);
|
||||
@ -345,11 +350,12 @@ public class AESInputStream extends FilterInputStream {
|
||||
}
|
||||
}
|
||||
|
||||
private static void runTest(byte orig[], SessionKey key, byte[] iv) {
|
||||
private static void runTest(I2PAppContext ctx, byte orig[], SessionKey key, byte[] iv) {
|
||||
Log log = ctx.logManager().getLog(AESInputStream.class);
|
||||
try {
|
||||
long start = Clock.getInstance().now();
|
||||
ByteArrayOutputStream origStream = new ByteArrayOutputStream(512);
|
||||
AESOutputStream out = new AESOutputStream(origStream, key, iv);
|
||||
AESOutputStream out = new AESOutputStream(ctx, origStream, key, iv);
|
||||
out.write(orig);
|
||||
out.close();
|
||||
|
||||
@ -357,7 +363,7 @@ public class AESInputStream extends FilterInputStream {
|
||||
long endE = Clock.getInstance().now();
|
||||
|
||||
ByteArrayInputStream encryptedStream = new ByteArrayInputStream(encrypted);
|
||||
AESInputStream in = new AESInputStream(encryptedStream, key, iv);
|
||||
AESInputStream in = new AESInputStream(ctx, encryptedStream, key, iv);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
|
||||
byte buf[] = new byte[1024 * 32];
|
||||
int read = DataHelper.read(in, buf);
|
||||
@ -370,65 +376,66 @@ public class AESInputStream extends FilterInputStream {
|
||||
Hash newHash = SHA256Generator.getInstance().calculateHash(fin);
|
||||
boolean eq = origHash.equals(newHash);
|
||||
if (eq)
|
||||
_log.info("Equal hashes. hash: " + origHash);
|
||||
log.info("Equal hashes. hash: " + origHash);
|
||||
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);
|
||||
_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 E(" + orig.length + "): " + (endE - start) + "ms");
|
||||
_log.debug("Time to D(" + orig.length + "): " + (end - endE) + "ms");
|
||||
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 E(" + orig.length + "): " + (endE - start) + "ms");
|
||||
log.debug("Time to D(" + orig.length + "): " + (end - endE) + "ms");
|
||||
|
||||
} catch (Throwable t) {
|
||||
_log.error("ERROR transferring", t);
|
||||
log.error("ERROR transferring", t);
|
||||
}
|
||||
//try { Thread.sleep(5000); } catch (Throwable t) {}
|
||||
}
|
||||
|
||||
private static void runOffsetTest(byte orig[], SessionKey key, byte[] iv) {
|
||||
private static void runOffsetTest(I2PAppContext ctx, byte orig[], SessionKey key, byte[] iv) {
|
||||
Log log = ctx.logManager().getLog(AESInputStream.class);
|
||||
try {
|
||||
long start = Clock.getInstance().now();
|
||||
ByteArrayOutputStream origStream = new ByteArrayOutputStream(512);
|
||||
AESOutputStream out = new AESOutputStream(origStream, key, iv);
|
||||
AESOutputStream out = new AESOutputStream(ctx, origStream, key, iv);
|
||||
out.write(orig);
|
||||
out.close();
|
||||
|
||||
byte encrypted[] = origStream.toByteArray();
|
||||
long endE = Clock.getInstance().now();
|
||||
|
||||
_log.info("Encrypted segment length: " + encrypted.length);
|
||||
log.info("Encrypted segment length: " + encrypted.length);
|
||||
byte encryptedSegment[] = new byte[40];
|
||||
System.arraycopy(encrypted, 0, encryptedSegment, 0, 40);
|
||||
|
||||
ByteArrayInputStream encryptedStream = new ByteArrayInputStream(encryptedSegment);
|
||||
AESInputStream in = new AESInputStream(encryptedStream, key, iv);
|
||||
AESInputStream in = new AESInputStream(ctx, encryptedStream, key, iv);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
|
||||
byte buf[] = new byte[1024 * 32];
|
||||
int read = DataHelper.read(in, buf);
|
||||
int remaining = in.remainingBytes();
|
||||
int readyBytes = in.readyBytes();
|
||||
_log.info("Read: " + read);
|
||||
log.info("Read: " + read);
|
||||
if (read > 0) baos.write(buf, 0, read);
|
||||
in.close();
|
||||
byte fin[] = baos.toByteArray();
|
||||
_log.info("fin.length: " + fin.length + " remaining: " + remaining + " ready: " + readyBytes);
|
||||
log.info("fin.length: " + fin.length + " remaining: " + remaining + " ready: " + readyBytes);
|
||||
long end = Clock.getInstance().now();
|
||||
Hash origHash = SHA256Generator.getInstance().calculateHash(orig);
|
||||
|
||||
Hash newHash = SHA256Generator.getInstance().calculateHash(fin);
|
||||
boolean eq = origHash.equals(newHash);
|
||||
if (eq)
|
||||
_log.info("Equal hashes. hash: " + origHash);
|
||||
log.info("Equal hashes. hash: " + origHash);
|
||||
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);
|
||||
_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 E(" + orig.length + "): " + (endE - start) + "ms");
|
||||
_log.debug("Time to D(" + orig.length + "): " + (end - endE) + "ms");
|
||||
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 E(" + orig.length + "): " + (endE - start) + "ms");
|
||||
log.debug("Time to D(" + orig.length + "): " + (end - endE) + "ms");
|
||||
|
||||
} catch (Throwable t) {
|
||||
_log.error("ERROR transferring", t);
|
||||
log.error("ERROR transferring", t);
|
||||
}
|
||||
//try { Thread.sleep(5000); } catch (Throwable t) {}
|
||||
}
|
||||
|
@ -16,7 +16,9 @@ import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* This writes everything as CBC with PKCS#5 padding, but each block is padded
|
||||
@ -28,8 +30,8 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
public class AESOutputStream extends FilterOutputStream {
|
||||
private final static CryptixAESEngine _engine = new CryptixAESEngine();
|
||||
private final static Log _log = new Log(AESOutputStream.class);
|
||||
private Log _log;
|
||||
private I2PAppContext _context;
|
||||
private SessionKey _key;
|
||||
private byte[] _lastBlock;
|
||||
private ByteArrayOutputStream _inBuf;
|
||||
@ -42,8 +44,10 @@ public class AESOutputStream extends FilterOutputStream {
|
||||
private final static int BLOCK_SIZE = CryptixRijndael_Algorithm._BLOCK_SIZE;
|
||||
private final static int MAX_BUF = 256;
|
||||
|
||||
public AESOutputStream(OutputStream source, SessionKey key, byte[] iv) {
|
||||
public AESOutputStream(I2PAppContext context, OutputStream source, SessionKey key, byte[] iv) {
|
||||
super(source);
|
||||
_context = context;
|
||||
_log = context.logManager().getLog(AESOutputStream.class);
|
||||
_key = key;
|
||||
_lastBlock = new byte[BLOCK_SIZE];
|
||||
System.arraycopy(iv, 0, _lastBlock, 0, BLOCK_SIZE);
|
||||
@ -104,8 +108,8 @@ public class AESOutputStream extends FilterOutputStream {
|
||||
block[BLOCK_SIZE - 1] = 0x01; // the padding byte for "full" blocks
|
||||
for (int i = 0; i < numBlocks; i++) {
|
||||
System.arraycopy(src, i * 15, block, 0, 15);
|
||||
byte data[] = _engine.xor(block, _lastBlock);
|
||||
byte encrypted[] = _engine.encrypt(data, _key, _lastBlock);
|
||||
byte data[] = DataHelper.xor(block, _lastBlock);
|
||||
byte encrypted[] = _context.AESEngine().encrypt(data, _key, _lastBlock);
|
||||
_cumulativeWritten += encrypted.length;
|
||||
out.write(encrypted);
|
||||
System.arraycopy(encrypted, encrypted.length - BLOCK_SIZE, _lastBlock, 0, BLOCK_SIZE);
|
||||
@ -118,8 +122,8 @@ public class AESOutputStream extends FilterOutputStream {
|
||||
int paddingBytes = BLOCK_SIZE - remainingBytes;
|
||||
System.arraycopy(src, numBlocks * 15, block, 0, remainingBytes);
|
||||
Arrays.fill(block, remainingBytes, BLOCK_SIZE, (byte) paddingBytes);
|
||||
byte data[] = _engine.xor(block, _lastBlock);
|
||||
byte encrypted[] = _engine.encrypt(data, _key, _lastBlock);
|
||||
byte data[] = DataHelper.xor(block, _lastBlock);
|
||||
byte encrypted[] = _context.AESEngine().encrypt(data, _key, _lastBlock);
|
||||
out.write(encrypted);
|
||||
System.arraycopy(encrypted, encrypted.length - BLOCK_SIZE, _lastBlock, 0, BLOCK_SIZE);
|
||||
_cumulativePadding += paddingBytes;
|
||||
|
@ -13,6 +13,7 @@ import java.security.InvalidKeyException;
|
||||
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Wrapper for AES cypher operation using Cryptix's Rijndael implementation. Implements
|
||||
@ -23,10 +24,15 @@ import net.i2p.util.Log;
|
||||
* @author jrandom, thecrypto
|
||||
*/
|
||||
public class CryptixAESEngine extends AESEngine {
|
||||
private final static Log _log = new Log(CryptixAESEngine.class);
|
||||
private Log _log;
|
||||
private final static CryptixRijndael_Algorithm _algo = new CryptixRijndael_Algorithm();
|
||||
private final static boolean USE_FAKE_CRYPTO = false;
|
||||
private final static byte FAKE_KEY = 0x2A;
|
||||
|
||||
public CryptixAESEngine(I2PAppContext context) {
|
||||
super(context);
|
||||
_log = context.logManager().getLog(CryptixAESEngine.class);
|
||||
}
|
||||
|
||||
public byte[] encrypt(byte payload[], SessionKey sessionKey, byte initializationVector[]) {
|
||||
if ((initializationVector == null) || (payload == null) || (payload.length <= 0) || (sessionKey == null)
|
||||
@ -116,7 +122,7 @@ public class CryptixAESEngine extends AESEngine {
|
||||
* @param sessionKey private esession key to encrypt to
|
||||
* @return encrypted data
|
||||
*/
|
||||
final static byte[] encrypt(byte payload[], SessionKey sessionKey) {
|
||||
final byte[] encrypt(byte payload[], SessionKey sessionKey) {
|
||||
try {
|
||||
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16);
|
||||
byte rv[] = new byte[payload.length];
|
||||
@ -133,7 +139,7 @@ public class CryptixAESEngine extends AESEngine {
|
||||
* @param sessionKey private session key
|
||||
* @return unencrypted data
|
||||
*/
|
||||
final static byte[] decrypt(byte payload[], SessionKey sessionKey) {
|
||||
final byte[] decrypt(byte payload[], SessionKey sessionKey) {
|
||||
try {
|
||||
Object key = CryptixRijndael_Algorithm.makeKey(sessionKey.getData(), 16);
|
||||
byte rv[] = new byte[payload.length];
|
||||
|
@ -20,6 +20,7 @@ import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.NativeBigInteger;
|
||||
import net.i2p.util.RandomSource;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Generate a new session key through a diffie hellman exchange. This uses the
|
||||
@ -62,22 +63,23 @@ public class DHSessionKeyBuilder {
|
||||
public final static String DEFAULT_DH_PRECALC_DELAY = "1000";
|
||||
|
||||
static {
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
try {
|
||||
int val = Integer.parseInt(System.getProperty(PROP_DH_PRECALC_MIN, DEFAULT_DH_PRECALC_MIN));
|
||||
int val = Integer.parseInt(ctx.getProperty(PROP_DH_PRECALC_MIN, DEFAULT_DH_PRECALC_MIN));
|
||||
MIN_NUM_BUILDERS = val;
|
||||
} catch (Throwable t) {
|
||||
int val = Integer.parseInt(DEFAULT_DH_PRECALC_MIN);
|
||||
MIN_NUM_BUILDERS = val;
|
||||
}
|
||||
try {
|
||||
int val = Integer.parseInt(System.getProperty(PROP_DH_PRECALC_MAX, DEFAULT_DH_PRECALC_MAX));
|
||||
int val = Integer.parseInt(ctx.getProperty(PROP_DH_PRECALC_MAX, DEFAULT_DH_PRECALC_MAX));
|
||||
MAX_NUM_BUILDERS = val;
|
||||
} catch (Throwable t) {
|
||||
int val = Integer.parseInt(DEFAULT_DH_PRECALC_MAX);
|
||||
MAX_NUM_BUILDERS = val;
|
||||
}
|
||||
try {
|
||||
int val = Integer.parseInt(System.getProperty(PROP_DH_PRECALC_DELAY, DEFAULT_DH_PRECALC_DELAY));
|
||||
int val = Integer.parseInt(ctx.getProperty(PROP_DH_PRECALC_DELAY, DEFAULT_DH_PRECALC_DELAY));
|
||||
CALC_DELAY = val;
|
||||
} catch (Throwable t) {
|
||||
int val = Integer.parseInt(DEFAULT_DH_PRECALC_DELAY);
|
||||
@ -266,6 +268,7 @@ public class DHSessionKeyBuilder {
|
||||
Thread.sleep(20 * 1000);
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
I2PAppContext ctx = new I2PAppContext();
|
||||
_log.debug("\n\n\n\nBegin test\n");
|
||||
long negTime = 0;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
@ -289,8 +292,8 @@ public class DHSessionKeyBuilder {
|
||||
byte iv[] = new byte[16];
|
||||
RandomSource.getInstance().nextBytes(iv);
|
||||
String origVal = "1234567890123456"; // 16 bytes max using AESEngine
|
||||
byte enc[] = AESEngine.getInstance().encrypt(origVal.getBytes(), key1, iv);
|
||||
byte dec[] = AESEngine.getInstance().decrypt(enc, key2, iv);
|
||||
byte enc[] = ctx.AESEngine().encrypt(origVal.getBytes(), key1, iv);
|
||||
byte dec[] = ctx.AESEngine().decrypt(enc, key2, iv);
|
||||
String tranVal = new String(dec);
|
||||
if (origVal.equals(tranVal))
|
||||
_log.debug("**Success: D(E(val)) == val");
|
||||
|
@ -39,17 +39,22 @@ import net.i2p.util.Clock;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.NativeBigInteger;
|
||||
import net.i2p.util.RandomSource;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
public class DSAEngine {
|
||||
private final static Log _log = new Log(DSAEngine.class);
|
||||
private static DSAEngine _instance = new DSAEngine();
|
||||
private Log _log;
|
||||
private I2PAppContext _context;
|
||||
|
||||
public static DSAEngine getInstance() {
|
||||
return _instance;
|
||||
public DSAEngine(I2PAppContext context) {
|
||||
_log = context.logManager().getLog(DSAEngine.class);
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public static DSAEngine getInstance() {
|
||||
return I2PAppContext.getGlobalContext().dsa();
|
||||
}
|
||||
|
||||
public boolean verifySignature(Signature signature, byte signedData[], SigningPublicKey verifyingKey) {
|
||||
long start = Clock.getInstance().now();
|
||||
long start = _context.clock().now();
|
||||
|
||||
byte[] sigbytes = signature.getData();
|
||||
byte rbytes[] = new byte[20];
|
||||
@ -65,22 +70,20 @@ public class DSAEngine {
|
||||
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);
|
||||
byte data[] = calculateHash(signedData).getData();
|
||||
NativeBigInteger bi = new NativeBigInteger(1, data);
|
||||
BigInteger u1 = bi.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);
|
||||
BigInteger modval = CryptoConstants.dsag.modPow(u1, CryptoConstants.dsap);
|
||||
BigInteger modmulval = modval.multiply(y.modPow(u2,CryptoConstants.dsap));
|
||||
BigInteger v = (modmulval).mod(CryptoConstants.dsap).mod(CryptoConstants.dsaq);
|
||||
|
||||
boolean ok = v.compareTo(r) == 0;
|
||||
|
||||
long diff = Clock.getInstance().now() - start;
|
||||
long diff = _context.clock().now() - start;
|
||||
if (diff > 1000) {
|
||||
if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to verify the signature (" + diff + "ms)");
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Took too long to verify the signature (" + diff + "ms)");
|
||||
}
|
||||
|
||||
return ok;
|
||||
@ -88,13 +91,13 @@ public class DSAEngine {
|
||||
|
||||
public Signature sign(byte data[], SigningPrivateKey signingKey) {
|
||||
if ((signingKey == null) || (data == null) || (data.length <= 0)) return null;
|
||||
long start = Clock.getInstance().now();
|
||||
long start = _context.clock().now();
|
||||
|
||||
Signature sig = new Signature();
|
||||
BigInteger k;
|
||||
|
||||
do {
|
||||
k = new BigInteger(160, RandomSource.getInstance());
|
||||
k = new BigInteger(160, _context.random());
|
||||
} while (k.compareTo(CryptoConstants.dsaq) != 1);
|
||||
|
||||
BigInteger r = CryptoConstants.dsag.modPow(k, CryptoConstants.dsap).mod(CryptoConstants.dsaq);
|
||||
@ -139,7 +142,7 @@ public class DSAEngine {
|
||||
}
|
||||
sig.setData(out);
|
||||
|
||||
long diff = Clock.getInstance().now() - start;
|
||||
long diff = _context.clock().now() - start;
|
||||
if (diff > 1000) {
|
||||
if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to sign (" + diff + "ms)");
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import net.i2p.data.Hash;
|
||||
import net.i2p.data.PrivateKey;
|
||||
import net.i2p.data.PublicKey;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Fake ElG E and D, useful for when performance isn't being tested
|
||||
@ -26,11 +27,19 @@ import net.i2p.util.Log;
|
||||
public class DummyElGamalEngine extends ElGamalEngine {
|
||||
private final static Log _log = new Log(DummyElGamalEngine.class);
|
||||
|
||||
public DummyElGamalEngine() {
|
||||
/**
|
||||
* The ElGamal engine should only be constructed and accessed through the
|
||||
* application context. This constructor should only be used by the
|
||||
* appropriate application context itself.
|
||||
*
|
||||
*/
|
||||
public DummyElGamalEngine(I2PAppContext context) {
|
||||
super(context);
|
||||
_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"));
|
||||
}
|
||||
|
||||
private DummyElGamalEngine() { super(null); }
|
||||
|
||||
/** encrypt the data to the public key
|
||||
* @return encrypted data
|
||||
* @param publicKey public key encrypt to
|
||||
|
@ -29,6 +29,7 @@ import net.i2p.stat.StatManager;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.RandomSource;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Handles the actual ElGamal+AES encryption and decryption scenarios using the
|
||||
@ -37,28 +38,27 @@ import net.i2p.util.RandomSource;
|
||||
public class ElGamalAESEngine {
|
||||
private final static Log _log = new Log(ElGamalAESEngine.class);
|
||||
private final static int MIN_ENCRYPTED_SIZE = 80; // smallest possible resulting size
|
||||
private I2PAppContext _context;
|
||||
|
||||
static {
|
||||
StatManager.getInstance()
|
||||
.createFrequencyStat("crypto.elGamalAES.encryptNewSession",
|
||||
"how frequently we encrypt to a new ElGamal/AES+SessionTag session?",
|
||||
"Encryption", new long[] { 60 * 1000l, 60 * 60 * 1000l, 24 * 60 * 60 * 1000l});
|
||||
StatManager.getInstance()
|
||||
.createFrequencyStat("crypto.elGamalAES.encryptExistingSession",
|
||||
"how frequently we encrypt to an existing ElGamal/AES+SessionTag session?",
|
||||
"Encryption", new long[] { 60 * 1000l, 60 * 60 * 1000l, 24 * 60 * 60 * 1000l});
|
||||
StatManager.getInstance()
|
||||
.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});
|
||||
private ElGamalAESEngine() {}
|
||||
public ElGamalAESEngine(I2PAppContext ctx) {
|
||||
_context = ctx;
|
||||
|
||||
_context.statManager().createFrequencyStat("crypto.elGamalAES.encryptNewSession",
|
||||
"how frequently we encrypt to a new ElGamal/AES+SessionTag session?",
|
||||
"Encryption", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l});
|
||||
_context.statManager().createFrequencyStat("crypto.elGamalAES.encryptExistingSession",
|
||||
"how frequently we encrypt to an existing ElGamal/AES+SessionTag session?",
|
||||
"Encryption", new long[] { 60 * 1000l, 60 * 60 * 1000l, 24 * 60 * 60 * 1000l});
|
||||
_context.statManager().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});
|
||||
_context.statManager().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});
|
||||
_context.statManager().createFrequencyStat("crypto.elGamalAES.decryptFail",
|
||||
"how frequently we fail to decrypt with ElGamal/AES+SessionTag?", "Encryption",
|
||||
new long[] { 60 * 60 * 1000l, 24 * 60 * 60 * 1000l});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,7 +66,7 @@ public class ElGamalAESEngine {
|
||||
* ElGamal+AES algorithm in the data structure spec.
|
||||
*
|
||||
*/
|
||||
public static byte[] decrypt(byte data[], PrivateKey targetPrivateKey) throws DataFormatException {
|
||||
public byte[] decrypt(byte data[], PrivateKey targetPrivateKey) throws DataFormatException {
|
||||
if (data == null) {
|
||||
if (_log.shouldLog(Log.WARN)) _log.warn("Null data being decrypted?");
|
||||
return null;
|
||||
@ -79,7 +79,7 @@ public class ElGamalAESEngine {
|
||||
byte tag[] = new byte[32];
|
||||
System.arraycopy(data, 0, tag, 0, tag.length);
|
||||
SessionTag st = new SessionTag(tag);
|
||||
SessionKey key = SessionKeyManager.getInstance().consumeTag(st);
|
||||
SessionKey key = _context.sessionKeyManager().consumeTag(st);
|
||||
SessionKey foundKey = new SessionKey();
|
||||
foundKey.setData(null);
|
||||
SessionKey usedKey = new SessionKey();
|
||||
@ -90,16 +90,16 @@ public class ElGamalAESEngine {
|
||||
usedKey.setData(key.getData());
|
||||
decrypted = decryptExistingSession(data, key, targetPrivateKey, foundTags, usedKey, foundKey);
|
||||
if (decrypted != null)
|
||||
StatManager.getInstance().updateFrequency("crypto.elGamalAES.decryptExistingSession");
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.decryptExistingSession");
|
||||
else
|
||||
StatManager.getInstance().updateFrequency("crypto.elGamalAES.decryptFailed");
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.decryptFailed");
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Key is NOT known for tag " + st);
|
||||
decrypted = decryptNewSession(data, targetPrivateKey, foundTags, usedKey, foundKey);
|
||||
if (decrypted != null)
|
||||
StatManager.getInstance().updateFrequency("crypto.elGamalAES.decryptNewSession");
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.decryptNewSession");
|
||||
else
|
||||
StatManager.getInstance().updateFrequency("crypto.elGamalAES.decryptFailed");
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.decryptFailed");
|
||||
}
|
||||
|
||||
if ((key == null) && (decrypted == null)) {
|
||||
@ -109,10 +109,10 @@ public class ElGamalAESEngine {
|
||||
if (foundTags.size() > 0) {
|
||||
if (foundKey.getData() != null) {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Found key: " + foundKey);
|
||||
SessionKeyManager.getInstance().tagsReceived(foundKey, foundTags);
|
||||
_context.sessionKeyManager().tagsReceived(foundKey, foundTags);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Used key: " + usedKey);
|
||||
SessionKeyManager.getInstance().tagsReceived(usedKey, foundTags);
|
||||
_context.sessionKeyManager().tagsReceived(usedKey, foundTags);
|
||||
}
|
||||
}
|
||||
return decrypted;
|
||||
@ -132,7 +132,7 @@ public class ElGamalAESEngine {
|
||||
*
|
||||
* @return null if decryption fails
|
||||
*/
|
||||
static byte[] decryptNewSession(byte data[], PrivateKey targetPrivateKey, Set foundTags, SessionKey usedKey,
|
||||
byte[] decryptNewSession(byte data[], PrivateKey targetPrivateKey, Set foundTags, SessionKey usedKey,
|
||||
SessionKey foundKey) throws DataFormatException {
|
||||
if (data == null) {
|
||||
if (_log.shouldLog(Log.WARN)) _log.warn("Data is null, unable to decrypt new session");
|
||||
@ -147,7 +147,7 @@ public class ElGamalAESEngine {
|
||||
} else {
|
||||
System.arraycopy(data, 0, elgEncr, 514 - data.length, data.length);
|
||||
}
|
||||
byte elgDecr[] = ElGamalEngine.getInstance().decrypt(elgEncr, targetPrivateKey);
|
||||
byte elgDecr[] = _context.elGamalEngine().decrypt(elgEncr, targetPrivateKey);
|
||||
if (elgDecr == null) return null;
|
||||
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(elgDecr);
|
||||
@ -170,7 +170,7 @@ public class ElGamalAESEngine {
|
||||
|
||||
//_log.debug("Pre IV for decryptNewSession: " + DataHelper.toString(preIV, 32));
|
||||
//_log.debug("SessionKey for decryptNewSession: " + DataHelper.toString(key.getData(), 32));
|
||||
Hash ivHash = SHA256Generator.getInstance().calculateHash(preIV);
|
||||
Hash ivHash = _context.sha().calculateHash(preIV);
|
||||
byte iv[] = new byte[16];
|
||||
System.arraycopy(ivHash.getData(), 0, iv, 0, 16);
|
||||
|
||||
@ -200,13 +200,13 @@ public class ElGamalAESEngine {
|
||||
* @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,
|
||||
byte[] decryptExistingSession(byte data[], SessionKey key, PrivateKey targetPrivateKey, Set foundTags,
|
||||
SessionKey usedKey, SessionKey foundKey) throws DataFormatException {
|
||||
byte preIV[] = new byte[32];
|
||||
System.arraycopy(data, 0, preIV, 0, preIV.length);
|
||||
byte encr[] = new byte[data.length - 32];
|
||||
System.arraycopy(data, 32, encr, 0, encr.length);
|
||||
Hash ivHash = SHA256Generator.getInstance().calculateHash(preIV);
|
||||
Hash ivHash = _context.sha().calculateHash(preIV);
|
||||
byte iv[] = new byte[16];
|
||||
System.arraycopy(ivHash.getData(), 0, iv, 0, 16);
|
||||
|
||||
@ -246,12 +246,12 @@ public class ElGamalAESEngine {
|
||||
* @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
|
||||
*/
|
||||
static byte[] decryptAESBlock(byte encrypted[], SessionKey key, byte iv[], byte sentTag[], Set foundTags,
|
||||
byte[] decryptAESBlock(byte encrypted[], SessionKey key, byte iv[], byte sentTag[], Set foundTags,
|
||||
SessionKey foundKey) throws DataFormatException {
|
||||
//_log.debug("iv for decryption: " + DataHelper.toString(iv, 16));
|
||||
//_log.debug("decrypting AES block. encr.length = " + (encrypted == null? -1 : encrypted.length) + " sentTag: " + DataHelper.toString(sentTag, 32));
|
||||
byte decrypted[] = AESEngine.getInstance().decrypt(encrypted, key, iv);
|
||||
Hash h = SHA256Generator.getInstance().calculateHash(decrypted);
|
||||
byte decrypted[] = _context.AESEngine().decrypt(encrypted, key, iv);
|
||||
Hash h = _context.sha().calculateHash(decrypted);
|
||||
//_log.debug("Hash of entire aes block after decryption: \n" + DataHelper.toString(h.getData(), 32));
|
||||
try {
|
||||
SessionKey newKey = null;
|
||||
@ -289,7 +289,7 @@ public class ElGamalAESEngine {
|
||||
byte unencrData[] = new byte[(int) len];
|
||||
read = bais.read(unencrData);
|
||||
if (read != unencrData.length) throw new Exception("Invalid size of the data read");
|
||||
Hash calcHash = SHA256Generator.getInstance().calculateHash(unencrData);
|
||||
Hash calcHash = _context.sha().calculateHash(unencrData);
|
||||
if (calcHash.equals(readHash)) {
|
||||
// everything matches. w00t.
|
||||
foundTags.addAll(tags);
|
||||
@ -317,17 +317,17 @@ public class ElGamalAESEngine {
|
||||
* @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)
|
||||
*/
|
||||
public static byte[] encrypt(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
|
||||
public byte[] encrypt(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
|
||||
SessionTag currentTag, SessionKey newKey, long paddedSize) {
|
||||
if (currentTag == null) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Current tag is null, encrypting as new session", new Exception("encrypt new"));
|
||||
StatManager.getInstance().updateFrequency("crypto.elGamalAES.encryptNewSession");
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.encryptNewSession");
|
||||
return encryptNewSession(data, target, key, tagsForDelivery, newKey, paddedSize);
|
||||
} else {
|
||||
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");
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.encryptExistingSession");
|
||||
return encryptExistingSession(data, target, key, tagsForDelivery, currentTag, newKey, paddedSize);
|
||||
}
|
||||
}
|
||||
@ -335,7 +335,7 @@ public class ElGamalAESEngine {
|
||||
/**
|
||||
* 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,
|
||||
public byte[] encrypt(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
|
||||
SessionTag currentTag, long paddedSize) {
|
||||
return encrypt(data, target, key, tagsForDelivery, currentTag, null, paddedSize);
|
||||
}
|
||||
@ -343,14 +343,14 @@ public class ElGamalAESEngine {
|
||||
/**
|
||||
* 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 byte[] encrypt(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery, long paddedSize) {
|
||||
return encrypt(data, target, key, tagsForDelivery, null, null, paddedSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 byte[] encrypt(byte data[], PublicKey target, SessionKey key, long paddedSize) {
|
||||
return encrypt(data, target, key, null, null, null, paddedSize);
|
||||
}
|
||||
|
||||
@ -370,25 +370,25 @@ public class ElGamalAESEngine {
|
||||
* - 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,
|
||||
byte[] encryptNewSession(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
|
||||
SessionKey newKey, long paddedSize) {
|
||||
//_log.debug("Encrypting to a NEW session");
|
||||
try {
|
||||
ByteArrayOutputStream elgSrc = new ByteArrayOutputStream(64);
|
||||
key.writeBytes(elgSrc);
|
||||
byte preIV[] = new byte[32];
|
||||
RandomSource.getInstance().nextBytes(preIV);
|
||||
_context.random().nextBytes(preIV);
|
||||
elgSrc.write(preIV);
|
||||
byte rnd[] = new byte[158];
|
||||
RandomSource.getInstance().nextBytes(rnd);
|
||||
_context.random().nextBytes(rnd);
|
||||
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));
|
||||
long before = Clock.getInstance().now();
|
||||
byte elgEncr[] = ElGamalEngine.getInstance().encrypt(elgSrc.toByteArray(), target);
|
||||
long after = Clock.getInstance().now();
|
||||
long before = _context.clock().now();
|
||||
byte elgEncr[] = _context.elGamalEngine().encrypt(elgSrc.toByteArray(), target);
|
||||
long after = _context.clock().now();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("elgEngine.encrypt of the session key took " + (after - before) + "ms");
|
||||
if (elgEncr.length < 514) {
|
||||
@ -400,7 +400,7 @@ public class ElGamalAESEngine {
|
||||
}
|
||||
//_log.debug("ElGamal encrypted length: " + elgEncr.length + " elGamal source length: " + elgSrc.toByteArray().length);
|
||||
|
||||
Hash ivHash = SHA256Generator.getInstance().calculateHash(preIV);
|
||||
Hash ivHash = _context.sha().calculateHash(preIV);
|
||||
byte iv[] = new byte[16];
|
||||
System.arraycopy(ivHash.getData(), 0, iv, 0, 16);
|
||||
byte aesEncr[] = encryptAESBlock(data, key, iv, tagsForDelivery, newKey, paddedSize);
|
||||
@ -410,7 +410,7 @@ public class ElGamalAESEngine {
|
||||
System.arraycopy(elgEncr, 0, rv, 0, elgEncr.length);
|
||||
System.arraycopy(aesEncr, 0, rv, elgEncr.length, aesEncr.length);
|
||||
//_log.debug("Return length: " + rv.length);
|
||||
long finish = Clock.getInstance().now();
|
||||
long finish = _context.clock().now();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("after the elgEngine.encrypt took a total of " + (finish - after) + "ms");
|
||||
return rv;
|
||||
@ -436,14 +436,14 @@ public class ElGamalAESEngine {
|
||||
* - 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,
|
||||
byte[] encryptExistingSession(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
|
||||
SessionTag currentTag, SessionKey newKey, long paddedSize) {
|
||||
//_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));
|
||||
Hash ivHash = SHA256Generator.getInstance().calculateHash(rawTag);
|
||||
Hash ivHash = _context.sha().calculateHash(rawTag);
|
||||
byte iv[] = new byte[16];
|
||||
System.arraycopy(ivHash.getData(), 0, iv, 0, 16);
|
||||
|
||||
@ -469,7 +469,7 @@ public class ElGamalAESEngine {
|
||||
* - 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,
|
||||
final byte[] encryptAESBlock(byte data[], SessionKey key, byte[] iv, Set tagsForDelivery, SessionKey newKey,
|
||||
long paddedSize) {
|
||||
//_log.debug("iv for encryption: " + DataHelper.toString(iv, 16));
|
||||
//_log.debug("Encrypting AES");
|
||||
@ -484,7 +484,7 @@ public class ElGamalAESEngine {
|
||||
//_log.debug("# tags created, registered, and written: " + tags.size());
|
||||
DataHelper.writeLong(aesSrc, 4, data.length);
|
||||
//_log.debug("data length: " + data.length);
|
||||
Hash hash = SHA256Generator.getInstance().calculateHash(data);
|
||||
Hash hash = _context.sha().calculateHash(data);
|
||||
hash.writeBytes(aesSrc);
|
||||
//_log.debug("hash of data: " + DataHelper.toString(hash.getData(), 32));
|
||||
if (newKey == null) {
|
||||
@ -499,14 +499,14 @@ public class ElGamalAESEngine {
|
||||
aesSrc.write(data);
|
||||
int len = aesSrc.toByteArray().length;
|
||||
//_log.debug("raw data written: " + len);
|
||||
byte padding[] = getPadding(len, paddedSize);
|
||||
byte padding[] = getPadding(_context, len, paddedSize);
|
||||
//_log.debug("padding length: " + padding.length);
|
||||
aesSrc.write(padding);
|
||||
|
||||
byte aesUnencr[] = aesSrc.toByteArray();
|
||||
Hash h = SHA256Generator.getInstance().calculateHash(aesUnencr);
|
||||
Hash h = _context.sha().calculateHash(aesUnencr);
|
||||
//_log.debug("Hash of entire aes block before encryption: (len=" + aesUnencr.length + ")\n" + DataHelper.toString(h.getData(), 32));
|
||||
byte aesEncr[] = AESEngine.getInstance().encrypt(aesUnencr, key, iv);
|
||||
byte aesEncr[] = _context.AESEngine().encrypt(aesUnencr, key, iv);
|
||||
//_log.debug("Encrypted length: " + aesEncr.length);
|
||||
return aesEncr;
|
||||
} catch (IOException ioe) {
|
||||
@ -523,7 +523,7 @@ public class ElGamalAESEngine {
|
||||
* at least minPaddedSize
|
||||
*
|
||||
*/
|
||||
final static byte[] getPadding(int curSize, long minPaddedSize) {
|
||||
final static byte[] getPadding(I2PAppContext context, int curSize, long minPaddedSize) {
|
||||
int diff = 0;
|
||||
if (curSize < minPaddedSize) {
|
||||
diff = (int) minPaddedSize - curSize;
|
||||
@ -532,7 +532,7 @@ public class ElGamalAESEngine {
|
||||
int numPadding = diff;
|
||||
if (((curSize + diff) % 16) != 0) numPadding += (16 - ((curSize + diff) % 16));
|
||||
byte rv[] = new byte[numPadding];
|
||||
RandomSource.getInstance().nextBytes(rv);
|
||||
context.random().nextBytes(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ import net.i2p.util.Clock;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.NativeBigInteger;
|
||||
import net.i2p.util.RandomSource;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Wrapper for ElGamal encryption/signature schemes.
|
||||
@ -56,25 +57,28 @@ import net.i2p.util.RandomSource;
|
||||
*/
|
||||
|
||||
public class ElGamalEngine {
|
||||
private final static Log _log = new Log(ElGamalEngine.class);
|
||||
private static ElGamalEngine _engine;
|
||||
static {
|
||||
if ("off".equals(System.getProperty("i2p.encryption", "on")))
|
||||
_engine = new DummyElGamalEngine();
|
||||
else
|
||||
_engine = new ElGamalEngine();
|
||||
|
||||
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});
|
||||
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});
|
||||
private Log _log;
|
||||
private I2PAppContext _context;
|
||||
|
||||
/**
|
||||
* The ElGamal engine should only be constructed and accessed through the
|
||||
* application context. This constructor should only be used by the
|
||||
* appropriate application context itself.
|
||||
*
|
||||
*/
|
||||
public ElGamalEngine(I2PAppContext context) {
|
||||
context.statManager().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});
|
||||
context.statManager().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});
|
||||
_context = context;
|
||||
_log = context.logManager().getLog(ElGamalEngine.class);
|
||||
}
|
||||
private ElGamalEngine() {}
|
||||
|
||||
public static ElGamalEngine getInstance() {
|
||||
return _engine;
|
||||
}
|
||||
|
||||
private final static BigInteger _two = new NativeBigInteger(1, new byte[] { 0x02});
|
||||
|
||||
private BigInteger[] getNextYK() {
|
||||
@ -91,12 +95,12 @@ public class ElGamalEngine {
|
||||
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();
|
||||
long start = _context.clock().now();
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(256);
|
||||
try {
|
||||
baos.write(0xFF);
|
||||
Hash hash = SHA256Generator.getInstance().calculateHash(data);
|
||||
Hash hash = _context.sha().calculateHash(data);
|
||||
hash.writeBytes(baos);
|
||||
baos.write(data);
|
||||
baos.flush();
|
||||
@ -106,25 +110,25 @@ public class ElGamalEngine {
|
||||
}
|
||||
|
||||
byte d2[] = baos.toByteArray();
|
||||
long t0 = Clock.getInstance().now();
|
||||
long t0 = _context.clock().now();
|
||||
BigInteger m = new NativeBigInteger(1, d2);
|
||||
long t1 = Clock.getInstance().now();
|
||||
long t1 = _context.clock().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();
|
||||
long t2 = _context.clock().now();
|
||||
BigInteger aalpha = new NativeBigInteger(1, publicKey.getData());
|
||||
long t3 = Clock.getInstance().now();
|
||||
long t3 = _context.clock().now();
|
||||
BigInteger yk[] = getNextYK();
|
||||
BigInteger k = yk[1];
|
||||
BigInteger y = yk[0];
|
||||
|
||||
long t7 = Clock.getInstance().now();
|
||||
long t7 = _context.clock().now();
|
||||
BigInteger d = aalpha.modPow(k, CryptoConstants.elgp);
|
||||
long t8 = Clock.getInstance().now();
|
||||
long t8 = _context.clock().now();
|
||||
d = d.multiply(m);
|
||||
long t9 = Clock.getInstance().now();
|
||||
long t9 = _context.clock().now();
|
||||
d = d.mod(CryptoConstants.elgp);
|
||||
long t10 = Clock.getInstance().now();
|
||||
long t10 = _context.clock().now();
|
||||
|
||||
byte[] ybytes = y.toByteArray();
|
||||
byte[] dbytes = d.toByteArray();
|
||||
@ -146,14 +150,14 @@ public class ElGamalEngine {
|
||||
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 end = _context.clock().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);
|
||||
_context.statManager().addRateData("crypto.elGamal.encrypt", diff, diff);
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -165,7 +169,7 @@ public class ElGamalEngine {
|
||||
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");
|
||||
long start = Clock.getInstance().now();
|
||||
long start = _context.clock().now();
|
||||
|
||||
byte[] ybytes = new byte[257];
|
||||
byte[] dbytes = new byte[257];
|
||||
@ -196,10 +200,10 @@ public class ElGamalEngine {
|
||||
return null;
|
||||
}
|
||||
|
||||
Hash calcHash = SHA256Generator.getInstance().calculateHash(rv);
|
||||
Hash calcHash = _context.sha().calculateHash(rv);
|
||||
boolean ok = calcHash.equals(hash);
|
||||
|
||||
long end = Clock.getInstance().now();
|
||||
long end = _context.clock().now();
|
||||
|
||||
long diff = end - start;
|
||||
if (diff > 1000) {
|
||||
@ -207,7 +211,7 @@ public class ElGamalEngine {
|
||||
_log.warn("Took too long to decrypt and verify ElGamal block (" + diff + "ms)");
|
||||
}
|
||||
|
||||
StatManager.getInstance().addRateData("crypto.elGamal.decrypt", diff, diff);
|
||||
_context.statManager().addRateData("crypto.elGamal.decrypt", diff, diff);
|
||||
|
||||
if (ok) {
|
||||
//_log.debug("Hash matches: " + DataHelper.toString(hash.getData(), hash.getData().length));
|
||||
@ -236,6 +240,7 @@ public class ElGamalEngine {
|
||||
}
|
||||
|
||||
RandomSource.getInstance().nextBoolean();
|
||||
I2PAppContext context = new I2PAppContext();
|
||||
|
||||
System.out.println("Running " + numRuns + " times");
|
||||
|
||||
@ -249,9 +254,9 @@ public class ElGamalEngine {
|
||||
byte buf[] = new byte[128];
|
||||
RandomSource.getInstance().nextBytes(buf);
|
||||
long startE = Clock.getInstance().now();
|
||||
byte encr[] = ElGamalEngine.getInstance().encrypt(buf, pubkey);
|
||||
byte encr[] = context.elGamalEngine().encrypt(buf, pubkey);
|
||||
long endE = Clock.getInstance().now();
|
||||
byte decr[] = ElGamalEngine.getInstance().decrypt(encr, privkey);
|
||||
byte decr[] = context.elGamalEngine().decrypt(encr, privkey);
|
||||
long endD = Clock.getInstance().now();
|
||||
eTime += endE - startE;
|
||||
dTime += endD - endE;
|
||||
@ -259,8 +264,7 @@ public class ElGamalEngine {
|
||||
|
||||
if (!DataHelper.eq(decr, buf)) {
|
||||
System.out.println("PublicKey : " + DataHelper.toString(pubkey.getData(), pubkey.getData().length));
|
||||
System.out.println("PrivateKey : "
|
||||
+ DataHelper.toString(privkey.getData(), privkey.getData().length));
|
||||
System.out.println("PrivateKey : " + DataHelper.toString(privkey.getData(), privkey.getData().length));
|
||||
System.out.println("orig : " + DataHelper.toString(buf, buf.length));
|
||||
System.out.println("d(e(orig) : " + DataHelper.toString(decr, decr.length));
|
||||
System.out.println("orig.len : " + buf.length);
|
||||
|
@ -3,28 +3,23 @@ package net.i2p.crypto;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Calculate the HMAC-SHA256 of a key+message. Currently FAKE - returns a stupid
|
||||
* kludgy hash: H(H(key) XOR H(data)). Fix me!
|
||||
*
|
||||
*/
|
||||
public abstract class HMACSHA256Generator {
|
||||
private static HMACSHA256Generator _generator = new DummyHMACSHA256Generator();
|
||||
|
||||
public class HMACSHA256Generator {
|
||||
public HMACSHA256Generator(I2PAppContext context) {};
|
||||
public static HMACSHA256Generator getInstance() {
|
||||
return _generator;
|
||||
return I2PAppContext.getGlobalContext().hmac();
|
||||
}
|
||||
|
||||
public abstract Hash calculate(SessionKey key, byte data[]);
|
||||
}
|
||||
|
||||
/**
|
||||
* jrandom smells.
|
||||
*
|
||||
*/
|
||||
|
||||
class DummyHMACSHA256Generator extends HMACSHA256Generator {
|
||||
|
||||
/**
|
||||
* This should calculate the HMAC/SHA256, but it DOESNT. Its just a kludge.
|
||||
* Fix me.
|
||||
*/
|
||||
public Hash calculate(SessionKey key, byte data[]) {
|
||||
if ((key == null) || (key.getData() == null) || (data == null))
|
||||
throw new NullPointerException("Null arguments for HMAC");
|
||||
|
@ -22,18 +22,24 @@ import net.i2p.util.Clock;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.NativeBigInteger;
|
||||
import net.i2p.util.RandomSource;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/** Define a way of generating asymetrical key pairs as well as symetrical keys
|
||||
* @author jrandom
|
||||
*/
|
||||
public class KeyGenerator {
|
||||
private final static Log _log = new Log(KeyGenerator.class);
|
||||
private static final RandomSource _random = RandomSource.getInstance();
|
||||
private static KeyGenerator _generator = new KeyGenerator();
|
||||
private Log _log;
|
||||
private I2PAppContext _context;
|
||||
|
||||
public static KeyGenerator getInstance() {
|
||||
return _generator;
|
||||
public KeyGenerator(I2PAppContext context) {
|
||||
_log = context.logManager().getLog(KeyGenerator.class);
|
||||
_context = context;
|
||||
}
|
||||
public static KeyGenerator getInstance() {
|
||||
return I2PAppContext.getGlobalContext().keyGenerator();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Generate a private 256 bit session key
|
||||
* @return session key
|
||||
@ -42,7 +48,7 @@ public class KeyGenerator {
|
||||
// 256bit random # as a session key
|
||||
SessionKey key = new SessionKey();
|
||||
byte data[] = new byte[SessionKey.KEYSIZE_BYTES];
|
||||
_random.nextBytes(data);
|
||||
_context.random().nextBytes(data);
|
||||
key.setData(data);
|
||||
return key;
|
||||
}
|
||||
@ -52,7 +58,7 @@ public class KeyGenerator {
|
||||
* @return pair of keys
|
||||
*/
|
||||
public Object[] generatePKIKeypair() {
|
||||
BigInteger a = new NativeBigInteger(2048, _random);
|
||||
BigInteger a = new NativeBigInteger(2048, _context.random());
|
||||
BigInteger aalpha = CryptoConstants.elgg.modPow(a, CryptoConstants.elgp);
|
||||
|
||||
Object[] keys = new Object[2];
|
||||
@ -80,7 +86,7 @@ public class KeyGenerator {
|
||||
|
||||
// make sure the random key is less than the DSA q
|
||||
do {
|
||||
x = new NativeBigInteger(160, _random);
|
||||
x = new NativeBigInteger(160, _context.random());
|
||||
} while (x.compareTo(CryptoConstants.dsaq) >= 0);
|
||||
|
||||
BigInteger y = CryptoConstants.dsag.modPow(x, CryptoConstants.dsap);
|
||||
@ -118,13 +124,14 @@ public class KeyGenerator {
|
||||
byte src[] = new byte[200];
|
||||
RandomSource.getInstance().nextBytes(src);
|
||||
|
||||
I2PAppContext ctx = new I2PAppContext();
|
||||
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]);
|
||||
byte ctext[] = ctx.elGamalEngine().encrypt(src, (PublicKey) keys[0]);
|
||||
byte ptext[] = ctx.elGamalEngine().decrypt(ctext, (PrivateKey) keys[1]);
|
||||
time += end - start;
|
||||
if (DataHelper.eq(ptext, src))
|
||||
log.debug("D(E(data)) == data");
|
||||
|
@ -27,6 +27,7 @@ import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.SessionTag;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Expose the functionality to allow people to write out and read in the
|
||||
@ -39,6 +40,19 @@ public class PersistentSessionKeyManager extends TransientSessionKeyManager {
|
||||
|
||||
private Object _yk = YKGenerator.class;
|
||||
|
||||
|
||||
/**
|
||||
* The session key manager should only be constructed and accessed through the
|
||||
* application context. This constructor should only be used by the
|
||||
* appropriate application context itself.
|
||||
*
|
||||
*/
|
||||
public PersistentSessionKeyManager(I2PAppContext context) {
|
||||
super(context);
|
||||
}
|
||||
private PersistentSessionKeyManager() {
|
||||
super(null);
|
||||
}
|
||||
/**
|
||||
* Write the session key data to the given stream
|
||||
*
|
||||
@ -146,7 +160,8 @@ public class PersistentSessionKeyManager extends TransientSessionKeyManager {
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
PersistentSessionKeyManager mgr = new PersistentSessionKeyManager();
|
||||
I2PAppContext ctx = new I2PAppContext();
|
||||
PersistentSessionKeyManager mgr = (PersistentSessionKeyManager)ctx.sessionKeyManager();
|
||||
try {
|
||||
mgr.loadState(new FileInputStream("sessionKeys.dat"));
|
||||
String state = mgr.renderStatusHTML();
|
||||
|
@ -30,6 +30,7 @@ package net.i2p.crypto;
|
||||
*/
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/** Defines a wrapper for SHA-256 operation
|
||||
*
|
||||
@ -38,10 +39,9 @@ import net.i2p.data.Hash;
|
||||
* @author thecrypto,jrandom
|
||||
*/
|
||||
public class SHA256Generator {
|
||||
private static SHA256Generator _generator = new SHA256Generator();
|
||||
|
||||
public SHA256Generator(I2PAppContext context) {};
|
||||
public static SHA256Generator getInstance() {
|
||||
return _generator;
|
||||
return I2PAppContext.getGlobalContext().sha();
|
||||
}
|
||||
|
||||
static int[] K = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
|
@ -14,6 +14,7 @@ import java.util.Set;
|
||||
import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.SessionTag;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Manage the session keys and session tags used for encryption and decryption.
|
||||
@ -23,12 +24,11 @@ import net.i2p.data.SessionTag;
|
||||
*
|
||||
*/
|
||||
public class SessionKeyManager {
|
||||
private final static SessionKeyManager _instance = new PersistentSessionKeyManager(); // new TransientSessionKeyManager(); // SessionKeyManager();
|
||||
|
||||
public final static SessionKeyManager getInstance() {
|
||||
return _instance;
|
||||
}
|
||||
|
||||
/** session key managers must be created through an app context */
|
||||
protected SessionKeyManager(I2PAppContext context) {}
|
||||
/** see above */
|
||||
private SessionKeyManager() {}
|
||||
|
||||
/**
|
||||
* Retrieve the session key currently associated with encryption to the target,
|
||||
* or null if a new session key should be generated.
|
||||
|
@ -18,6 +18,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.SessionKey;
|
||||
@ -52,11 +53,18 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
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 TransientSessionKeyManager() {
|
||||
super();
|
||||
/**
|
||||
* The session key manager should only be constructed and accessed through the
|
||||
* application context. This constructor should only be used by the
|
||||
* appropriate application context itself.
|
||||
*
|
||||
*/
|
||||
public TransientSessionKeyManager(I2PAppContext context) {
|
||||
super(context);
|
||||
_outboundSessions = new HashMap(64);
|
||||
_inboundTagSets = new HashMap(1024);
|
||||
}
|
||||
private TransientSessionKeyManager() { super(null); }
|
||||
|
||||
/** TagSet */
|
||||
protected Set getInboundTagSets() {
|
||||
|
@ -18,6 +18,7 @@ import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.NativeBigInteger;
|
||||
import net.i2p.util.RandomSource;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Precalculate the Y and K for ElGamal encryption operations.
|
||||
@ -56,22 +57,23 @@ class YKGenerator {
|
||||
private final static long CHECK_DELAY = 30 * 1000;
|
||||
|
||||
static {
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
try {
|
||||
int val = Integer.parseInt(System.getProperty(PROP_YK_PRECALC_MIN, DEFAULT_YK_PRECALC_MIN));
|
||||
int val = Integer.parseInt(ctx.getProperty(PROP_YK_PRECALC_MIN, DEFAULT_YK_PRECALC_MIN));
|
||||
MIN_NUM_BUILDERS = val;
|
||||
} catch (Throwable t) {
|
||||
int val = Integer.parseInt(DEFAULT_YK_PRECALC_MIN);
|
||||
MIN_NUM_BUILDERS = val;
|
||||
}
|
||||
try {
|
||||
int val = Integer.parseInt(System.getProperty(PROP_YK_PRECALC_MAX, DEFAULT_YK_PRECALC_MAX));
|
||||
int val = Integer.parseInt(ctx.getProperty(PROP_YK_PRECALC_MAX, DEFAULT_YK_PRECALC_MAX));
|
||||
MAX_NUM_BUILDERS = val;
|
||||
} catch (Throwable t) {
|
||||
int val = Integer.parseInt(DEFAULT_YK_PRECALC_MAX);
|
||||
MAX_NUM_BUILDERS = val;
|
||||
}
|
||||
try {
|
||||
int val = Integer.parseInt(System.getProperty(PROP_YK_PRECALC_DELAY, DEFAULT_YK_PRECALC_DELAY));
|
||||
int val = Integer.parseInt(ctx.getProperty(PROP_YK_PRECALC_DELAY, DEFAULT_YK_PRECALC_DELAY));
|
||||
CALC_DELAY = val;
|
||||
} catch (Throwable t) {
|
||||
int val = Integer.parseInt(DEFAULT_YK_PRECALC_DELAY);
|
||||
|
@ -35,7 +35,6 @@ import net.i2p.util.OrderedProperties;
|
||||
* @author jrandom
|
||||
*/
|
||||
public class DataHelper {
|
||||
private final static Log _log = new Log(DataHelper.class);
|
||||
private final static String _equal = "="; // in UTF-8
|
||||
private final static String _semicolon = ";"; // in UTF-8
|
||||
|
||||
@ -56,7 +55,8 @@ public class DataHelper {
|
||||
* @throws IOException if there is a problem reading the data
|
||||
* @return mapping
|
||||
*/
|
||||
public static Properties readProperties(InputStream rawStream) throws DataFormatException, IOException {
|
||||
public static Properties readProperties(InputStream rawStream)
|
||||
throws DataFormatException, IOException {
|
||||
Properties props = new OrderedProperties();
|
||||
long size = readLong(rawStream, 2);
|
||||
byte data[] = new byte[(int) size];
|
||||
@ -65,24 +65,18 @@ public class DataHelper {
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(data);
|
||||
byte eqBuf[] = _equal.getBytes();
|
||||
byte semiBuf[] = _semicolon.getBytes();
|
||||
try {
|
||||
while (in.available() > 0) {
|
||||
String key = readString(in);
|
||||
read = read(in, eqBuf);
|
||||
if ((read != eqBuf.length) || (!eq(new String(eqBuf), _equal))) {
|
||||
_log.debug("Failed eqtest [" + new String(eqBuf) + "]");
|
||||
break;
|
||||
}
|
||||
String val = readString(in);
|
||||
read = read(in, semiBuf);
|
||||
if ((read != semiBuf.length) || (!eq(new String(semiBuf), _semicolon))) {
|
||||
_log.debug("Failed semitest [" + new String(semiBuf) + "]");
|
||||
break;
|
||||
}
|
||||
props.put(key, val);
|
||||
while (in.available() > 0) {
|
||||
String key = readString(in);
|
||||
read = read(in, eqBuf);
|
||||
if ((read != eqBuf.length) || (!eq(new String(eqBuf), _equal))) {
|
||||
break;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.warn("Error reading properties", ioe);
|
||||
String val = readString(in);
|
||||
read = read(in, semiBuf);
|
||||
if ((read != semiBuf.length) || (!eq(new String(semiBuf), _semicolon))) {
|
||||
break;
|
||||
}
|
||||
props.put(key, val);
|
||||
}
|
||||
return props;
|
||||
}
|
||||
@ -96,8 +90,8 @@ public class DataHelper {
|
||||
* @throws DataFormatException if there is not enough valid data to write out
|
||||
* @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, IOException {
|
||||
OrderedProperties p = new OrderedProperties();
|
||||
if (props != null) p.putAll(props);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(32);
|
||||
@ -204,10 +198,10 @@ public class DataHelper {
|
||||
* @throws IOException if there is an IO error reading the number
|
||||
* @return number
|
||||
*/
|
||||
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)
|
||||
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];
|
||||
int num = read(rawStream, data);
|
||||
if (num != numBytes)
|
||||
@ -225,8 +219,8 @@ public class DataHelper {
|
||||
* @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
|
||||
*/
|
||||
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);
|
||||
rawStream.write(i.getBytes(numBytes));
|
||||
}
|
||||
@ -254,7 +248,8 @@ public class DataHelper {
|
||||
* @throws DataFormatException if the date is not valid
|
||||
* @throws IOException if there is an IO error writing the date
|
||||
*/
|
||||
public static void writeDate(OutputStream out, Date date) throws DataFormatException, IOException {
|
||||
public static void writeDate(OutputStream out, Date date)
|
||||
throws DataFormatException, IOException {
|
||||
if (date == null)
|
||||
writeLong(out, 8, 0L);
|
||||
else
|
||||
@ -286,7 +281,8 @@ public class DataHelper {
|
||||
* @throws DataFormatException if the string is not valid
|
||||
* @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) {
|
||||
writeLong(out, 1, 0);
|
||||
} else {
|
||||
@ -328,7 +324,8 @@ public class DataHelper {
|
||||
* @throws DataFormatException if the boolean is not valid
|
||||
* @throws IOException if there is an IO error writing the boolean
|
||||
*/
|
||||
public static void writeBoolean(OutputStream out, Boolean bool) throws DataFormatException, IOException {
|
||||
public static void writeBoolean(OutputStream out, Boolean bool)
|
||||
throws DataFormatException, IOException {
|
||||
if (bool == null)
|
||||
writeLong(out, 1, 2);
|
||||
else if (Boolean.TRUE.equals(bool))
|
||||
@ -353,7 +350,6 @@ public class DataHelper {
|
||||
boolean eq = (((lhs == null) && (rhs == null)) || ((lhs != null) && (lhs.equals(rhs))));
|
||||
return eq;
|
||||
} catch (ClassCastException cce) {
|
||||
_log.warn("Error comparing [" + lhs + "] with [" + rhs + "]", cce);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -542,12 +538,12 @@ public class DataHelper {
|
||||
out.finish();
|
||||
out.flush();
|
||||
byte rv[] = baos.toByteArray();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Compression of " + orig.length + " into " + rv.length + " (or " + 100.0d
|
||||
* (((double) orig.length) / ((double) rv.length)) + "% savings)");
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Compression of " + orig.length + " into " + rv.length + " (or " + 100.0d
|
||||
// * (((double) orig.length) / ((double) rv.length)) + "% savings)");
|
||||
return rv;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error compressing?!", ioe);
|
||||
//_log.error("Error compressing?!", ioe);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -565,12 +561,12 @@ public class DataHelper {
|
||||
baos.write(buf, 0, read);
|
||||
}
|
||||
byte rv[] = baos.toByteArray();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Decompression of " + orig.length + " into " + rv.length + " (or " + 100.0d
|
||||
* (((double) rv.length) / ((double) orig.length)) + "% savings)");
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Decompression of " + orig.length + " into " + rv.length + " (or " + 100.0d
|
||||
// * (((double) rv.length) / ((double) orig.length)) + "% savings)");
|
||||
return rv;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error decompressing?", ioe);
|
||||
//_log.error("Error decompressing?", ioe);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import net.i2p.crypto.SHA256Generator;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.RandomSource;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Component to manage the munging of hashes into routing keys - given a hash,
|
||||
@ -40,12 +41,17 @@ import net.i2p.util.RandomSource;
|
||||
*
|
||||
*/
|
||||
public class RoutingKeyGenerator {
|
||||
private final static RoutingKeyGenerator _instance = new RoutingKeyGenerator();
|
||||
private Log _log;
|
||||
private I2PAppContext _context;
|
||||
|
||||
public static RoutingKeyGenerator getInstance() {
|
||||
return _instance;
|
||||
public RoutingKeyGenerator(I2PAppContext context) {
|
||||
_log = context.logManager().getLog(RoutingKeyGenerator.class);
|
||||
_context = context;
|
||||
}
|
||||
private final static Log _log = new Log(RoutingKeyGenerator.class);
|
||||
public static RoutingKeyGenerator getInstance() {
|
||||
return I2PAppContext.getGlobalContext().routingKeyGenerator();
|
||||
}
|
||||
|
||||
private byte _currentModData[];
|
||||
|
||||
private final static Calendar _cal = GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
@ -67,7 +73,7 @@ public class RoutingKeyGenerator {
|
||||
public void generateDateBasedModData() {
|
||||
Date today = null;
|
||||
synchronized (_cal) {
|
||||
_cal.setTime(new Date(Clock.getInstance().now()));
|
||||
_cal.setTime(new Date(_context.clock().now()));
|
||||
_cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
_cal.set(Calendar.MINUTE, 0);
|
||||
_cal.set(Calendar.SECOND, 0);
|
||||
|
@ -6,24 +6,25 @@ import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
public class SimpleStatDumper {
|
||||
private final static Log _log = new Log(SimpleStatDumper.class);
|
||||
|
||||
public static void dumpStats(int logLevel) {
|
||||
public static void dumpStats(I2PAppContext context, int logLevel) {
|
||||
if (!_log.shouldLog(logLevel)) return;
|
||||
|
||||
StringBuffer buf = new StringBuffer(4 * 1024);
|
||||
dumpFrequencies(buf);
|
||||
dumpRates(buf);
|
||||
dumpFrequencies(context, buf);
|
||||
dumpRates(context, buf);
|
||||
_log.log(logLevel, buf.toString());
|
||||
}
|
||||
|
||||
private static void dumpFrequencies(StringBuffer buf) {
|
||||
Set frequencies = new TreeSet(StatManager.getInstance().getFrequencyNames());
|
||||
private static void dumpFrequencies(I2PAppContext ctx, StringBuffer buf) {
|
||||
Set frequencies = new TreeSet(ctx.statManager().getFrequencyNames());
|
||||
for (Iterator iter = frequencies.iterator(); iter.hasNext();) {
|
||||
String name = (String) iter.next();
|
||||
FrequencyStat freq = StatManager.getInstance().getFrequency(name);
|
||||
FrequencyStat freq = ctx.statManager().getFrequency(name);
|
||||
buf.append('\n');
|
||||
buf.append(freq.getGroupName()).append('.').append(freq.getName()).append(": ")
|
||||
.append(freq.getDescription()).append('\n');
|
||||
@ -39,11 +40,11 @@ public class SimpleStatDumper {
|
||||
}
|
||||
}
|
||||
|
||||
private static void dumpRates(StringBuffer buf) {
|
||||
Set rates = new TreeSet(StatManager.getInstance().getRateNames());
|
||||
private static void dumpRates(I2PAppContext ctx, StringBuffer buf) {
|
||||
Set rates = new TreeSet(ctx.statManager().getRateNames());
|
||||
for (Iterator iter = rates.iterator(); iter.hasNext();) {
|
||||
String name = (String) iter.next();
|
||||
RateStat rate = StatManager.getInstance().getRate(name);
|
||||
RateStat rate = ctx.statManager().getRate(name);
|
||||
buf.append('\n');
|
||||
buf.append(rate.getGroupName()).append('.').append(rate.getName()).append(": ")
|
||||
.append(rate.getDescription()).append('\n');
|
||||
|
@ -10,6 +10,7 @@ import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Coordinate the management of various frequencies and rates within I2P components,
|
||||
@ -19,18 +20,23 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
public class StatManager {
|
||||
private final static Log _log = new Log(StatManager.class);
|
||||
private final static StatManager _instance = new StatManager();
|
||||
private Log _log;
|
||||
private I2PAppContext _context;
|
||||
|
||||
public final static StatManager getInstance() {
|
||||
return _instance;
|
||||
}
|
||||
/** stat name to FrequencyStat */
|
||||
private Map _frequencyStats;
|
||||
/** stat name to RateStat */
|
||||
private Map _rateStats;
|
||||
|
||||
private StatManager() {
|
||||
/**
|
||||
* The stat manager should only be constructed and accessed through the
|
||||
* application context. This constructor should only be used by the
|
||||
* appropriate application context itself.
|
||||
*
|
||||
*/
|
||||
public StatManager(I2PAppContext context) {
|
||||
_log = context.logManager().getLog(StatManager.class);
|
||||
_context = context;
|
||||
_frequencyStats = Collections.synchronizedMap(new HashMap(128));
|
||||
_rateStats = Collections.synchronizedMap(new HashMap(128));
|
||||
}
|
||||
@ -44,6 +50,7 @@ public class StatManager {
|
||||
* @param periods array of period lengths (in milliseconds)
|
||||
*/
|
||||
public void createFrequencyStat(String name, String description, String group, long periods[]) {
|
||||
if (_frequencyStats.containsKey(name)) return;
|
||||
_frequencyStats.put(name, new FrequencyStat(name, description, group, periods));
|
||||
}
|
||||
|
||||
@ -56,6 +63,7 @@ public class StatManager {
|
||||
* @param periods array of period lengths (in milliseconds)
|
||||
*/
|
||||
public void createRateStat(String name, String description, String group, long periods[]) {
|
||||
if (_rateStats.containsKey(name)) return;
|
||||
_rateStats.put(name, new RateStat(name, description, group, periods));
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,8 @@ import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Alternate location for determining the time which takes into account an offset.
|
||||
* This offset will ideally be periodically updated so as to serve as the difference
|
||||
@ -12,12 +14,20 @@ import java.util.Set;
|
||||
*
|
||||
*/
|
||||
public class Clock {
|
||||
private final static Log _log = new Log(Clock.class);
|
||||
private final static Clock _instance = new Clock();
|
||||
|
||||
public final static Clock getInstance() {
|
||||
return _instance;
|
||||
private I2PAppContext _context;
|
||||
public Clock(I2PAppContext context) {
|
||||
_context = context;
|
||||
_offset = 0;
|
||||
_alreadyChanged = false;
|
||||
_listeners = new HashSet(64);
|
||||
}
|
||||
public static Clock getInstance() {
|
||||
return I2PAppContext.getGlobalContext().clock();
|
||||
}
|
||||
|
||||
/** we fetch it on demand to avoid circular dependencies (logging uses the clock) */
|
||||
private Log getLog() { return _context.logManager().getLog(Clock.class); }
|
||||
|
||||
private volatile long _offset;
|
||||
private boolean _alreadyChanged;
|
||||
private Set _listeners;
|
||||
@ -27,12 +37,6 @@ public class Clock {
|
||||
/** if the clock skewed changes by less than 1s, ignore the update (so we don't slide all over the place) */
|
||||
public final static long MIN_OFFSET_CHANGE = 30 * 1000;
|
||||
|
||||
private Clock() {
|
||||
_offset = 0;
|
||||
_alreadyChanged = false;
|
||||
_listeners = new HashSet(64);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify how far away from the "correct" time the computer is - a positive
|
||||
* value means that we are slow, while a negative value means we are fast.
|
||||
@ -40,18 +44,18 @@ public class Clock {
|
||||
*/
|
||||
public void setOffset(long offsetMs) {
|
||||
if ((offsetMs > MAX_OFFSET) || (offsetMs < 0 - MAX_OFFSET)) {
|
||||
_log.error("Maximum offset shift exceeded [" + offsetMs + "], NOT HONORING IT");
|
||||
getLog().error("Maximum offset shift exceeded [" + offsetMs + "], NOT HONORING IT");
|
||||
return;
|
||||
}
|
||||
long delta = offsetMs - _offset;
|
||||
if ((delta < MIN_OFFSET_CHANGE) && (delta > 0 - MIN_OFFSET_CHANGE)) {
|
||||
_log.debug("Not changing offset since it is only " + delta + "ms");
|
||||
getLog().debug("Not changing offset since it is only " + delta + "ms");
|
||||
return;
|
||||
}
|
||||
if (_alreadyChanged)
|
||||
_log.log(Log.CRIT, "Updating clock offset to " + offsetMs + "ms from " + _offset + "ms");
|
||||
getLog().log(Log.CRIT, "Updating clock offset to " + offsetMs + "ms from " + _offset + "ms");
|
||||
else
|
||||
_log.log(Log.INFO, "Initializing clock offset to " + offsetMs + "ms from " + _offset + "ms");
|
||||
getLog().log(Log.INFO, "Initializing clock offset to " + offsetMs + "ms from " + _offset + "ms");
|
||||
_alreadyChanged = true;
|
||||
_offset = offsetMs;
|
||||
fireOffsetChanged(delta);
|
||||
|
@ -9,6 +9,9 @@ package net.i2p.util;
|
||||
*
|
||||
*/
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Wrapper class for whatever logging system I2P uses. This class should be
|
||||
* instantiated and kept as a variable for each class it is used by, ala:
|
||||
@ -24,6 +27,8 @@ public class Log {
|
||||
private Class _class;
|
||||
private String _name;
|
||||
private int _minPriority;
|
||||
private LogScope _scope;
|
||||
private LogManager _manager;
|
||||
|
||||
public final static int DEBUG = 10;
|
||||
public final static int INFO = 20;
|
||||
@ -65,33 +70,46 @@ public class Log {
|
||||
}
|
||||
|
||||
public Log(Class cls) {
|
||||
this(cls, null);
|
||||
this(I2PAppContext.getGlobalContext().logManager(), cls, null);
|
||||
_manager.addLog(this);
|
||||
}
|
||||
|
||||
public Log(String name) {
|
||||
this(null, name);
|
||||
this(I2PAppContext.getGlobalContext().logManager(), null, name);
|
||||
_manager.addLog(this);
|
||||
}
|
||||
|
||||
public Log(Class cls, String name) {
|
||||
Log(LogManager manager, Class cls) {
|
||||
this(manager, cls, null);
|
||||
}
|
||||
|
||||
Log(LogManager manager, String name) {
|
||||
this(manager, null, name);
|
||||
}
|
||||
|
||||
Log(LogManager manager, Class cls, String name) {
|
||||
_manager = manager;
|
||||
_class = cls;
|
||||
_name = name;
|
||||
_minPriority = DEBUG;
|
||||
LogManager.getInstance().registerLog(this);
|
||||
_scope = new LogScope(name, cls);
|
||||
//_manager.addRecord(new LogRecord(Log.class, null, Thread.currentThread().getName(), Log.DEBUG,
|
||||
// "Log created with manager " + manager + " for class " + cls, null));
|
||||
}
|
||||
|
||||
public void log(int priority, String msg) {
|
||||
if (priority >= _minPriority) {
|
||||
LogManager.getInstance().addRecord(
|
||||
new LogRecord(_class, _name, Thread.currentThread().getName(), priority,
|
||||
msg, null));
|
||||
_manager.addRecord(new LogRecord(_class, _name,
|
||||
Thread.currentThread().getName(), priority,
|
||||
msg, null));
|
||||
}
|
||||
}
|
||||
|
||||
public void log(int priority, String msg, Throwable t) {
|
||||
if (priority >= _minPriority) {
|
||||
LogManager.getInstance().addRecord(
|
||||
new LogRecord(_class, _name, Thread.currentThread().getName(), priority,
|
||||
msg, t));
|
||||
_manager.addRecord(new LogRecord(_class, _name,
|
||||
Thread.currentThread().getName(), priority,
|
||||
msg, t));
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,6 +151,9 @@ public class Log {
|
||||
|
||||
public void setMinimumPriority(int priority) {
|
||||
_minPriority = priority;
|
||||
//_manager.addRecord(new LogRecord(Log.class, null, Thread.currentThread().getName(), Log.DEBUG,
|
||||
// "Log with manager " + _manager + " for class " + _class
|
||||
// + " new priority " + toLevelString(priority), null));
|
||||
}
|
||||
|
||||
public boolean shouldLog(int priority) {
|
||||
@ -145,5 +166,32 @@ public class Log {
|
||||
else
|
||||
return _name;
|
||||
}
|
||||
|
||||
|
||||
public Object getScope() { return _scope; }
|
||||
private static final class LogScope {
|
||||
private String _scopeName;
|
||||
private Class _scopeClass;
|
||||
public LogScope(String name, Class cls) {
|
||||
_scopeName = name;
|
||||
_scopeClass = cls;
|
||||
}
|
||||
public int hashCode() {
|
||||
if (_scopeClass != null)
|
||||
return _scopeClass.hashCode();
|
||||
else if (_scopeName != null)
|
||||
return _scopeName.hashCode();
|
||||
else
|
||||
return 42;
|
||||
}
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) throw new NullPointerException("Null object scope?");
|
||||
if (obj instanceof LogScope) {
|
||||
LogScope s = (LogScope)obj;
|
||||
return DataHelper.eq(s._scopeName, _scopeName) &&
|
||||
DataHelper.eq(s._scopeClass, _scopeClass);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +1,24 @@
|
||||
package net.i2p.util;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Offer a glimpse into the last few console messages generated
|
||||
*
|
||||
*/
|
||||
public class LogConsoleBuffer {
|
||||
private final static LogConsoleBuffer _instance = new LogConsoleBuffer();
|
||||
|
||||
public final static LogConsoleBuffer getInstance() {
|
||||
return _instance;
|
||||
}
|
||||
private I2PAppContext _context;
|
||||
private List _buffer;
|
||||
|
||||
private LogConsoleBuffer() {
|
||||
_buffer = new LinkedList();
|
||||
public LogConsoleBuffer(I2PAppContext context) {
|
||||
_context = context;
|
||||
_buffer = new ArrayList();
|
||||
}
|
||||
|
||||
void add(String msg) {
|
||||
int lim = LogManager.getInstance().getConsoleBufferSize();
|
||||
int lim = _context.logManager().getConsoleBufferSize();
|
||||
synchronized (_buffer) {
|
||||
while (_buffer.size() >= lim)
|
||||
_buffer.remove(0);
|
||||
@ -36,7 +34,7 @@ public class LogConsoleBuffer {
|
||||
*/
|
||||
public List getMostRecentMessages() {
|
||||
synchronized (_buffer) {
|
||||
return new LinkedList(_buffer);
|
||||
return new ArrayList(_buffer);
|
||||
}
|
||||
}
|
||||
}
|
@ -19,6 +19,10 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Manages the logging system, loading (and reloading) the configuration file,
|
||||
@ -31,14 +35,6 @@ public class LogManager {
|
||||
public final static String CONFIG_LOCATION_PROP = "loggerConfigLocation";
|
||||
public final static String FILENAME_OVERRIDE_PROP = "loggerFilenameOverride";
|
||||
public final static String CONFIG_LOCATION_DEFAULT = "logger.config";
|
||||
|
||||
public static final LogManager getInstance() {
|
||||
return _instance;
|
||||
}
|
||||
private static final LogManager _instance = new LogManager(System.getProperty(CONFIG_LOCATION_PROP,
|
||||
CONFIG_LOCATION_DEFAULT));
|
||||
private static final Log _log = new Log(LogManager.class);
|
||||
|
||||
/**
|
||||
* These define the characters in the format line of the config file
|
||||
*/
|
||||
@ -65,12 +61,15 @@ public class LogManager {
|
||||
public final static String DEFAULT_DEFALTLEVEL = Log.STR_DEBUG;
|
||||
public final static String DEFAULT_ONSCREENLEVEL = Log.STR_DEBUG;
|
||||
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
|
||||
private long _configLastRead;
|
||||
|
||||
private String _location;
|
||||
private List _records;
|
||||
private Set _limits;
|
||||
private Set _logs;
|
||||
private Map _logs;
|
||||
private LogWriter _writer;
|
||||
|
||||
private int _defaultLimit;
|
||||
@ -83,7 +82,59 @@ public class LogManager {
|
||||
|
||||
private boolean _displayOnScreen;
|
||||
private int _consoleBufferSize;
|
||||
|
||||
private LogConsoleBuffer _consoleBuffer;
|
||||
|
||||
public LogManager(I2PAppContext context) {
|
||||
_displayOnScreen = true;
|
||||
_records = new ArrayList();
|
||||
_limits = new HashSet();
|
||||
_logs = new HashMap(128);
|
||||
_defaultLimit = Log.DEBUG;
|
||||
_configLastRead = 0;
|
||||
_location = context.getProperty(CONFIG_LOCATION_PROP, CONFIG_LOCATION_DEFAULT);
|
||||
_context = context;
|
||||
_log = getLog(LogManager.class);
|
||||
_consoleBuffer = new LogConsoleBuffer(context);
|
||||
loadConfig();
|
||||
_writer = new LogWriter(this);
|
||||
Thread t = new I2PThread(_writer);
|
||||
t.setName("LogWriter");
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
Runtime.getRuntime().addShutdownHook(new ShutdownHook());
|
||||
System.out.println("Created logManager " + this + " with context: " + context);
|
||||
}
|
||||
private LogManager() {}
|
||||
|
||||
public Log getLog(Class cls) { return getLog(cls, null); }
|
||||
public Log getLog(String name) { return getLog(null, name); }
|
||||
public Log getLog(Class cls, String name) {
|
||||
Log rv = null;
|
||||
synchronized (_logs) {
|
||||
Log newLog = new Log(this, cls, name);
|
||||
if (_logs.containsKey(newLog.getScope())) {
|
||||
Log oldLog = (Log)_logs.get(newLog.getScope());
|
||||
rv = oldLog;
|
||||
//_log.error("Duplicate log creation for " + cls);
|
||||
} else {
|
||||
_logs.put(newLog.getScope(), newLog);
|
||||
rv = newLog;
|
||||
}
|
||||
}
|
||||
updateLimit(rv);
|
||||
return rv;
|
||||
}
|
||||
void addLog(Log log) {
|
||||
synchronized (_logs) {
|
||||
if (!_logs.containsKey(log.getScope()))
|
||||
_logs.put(log.getScope(), log);
|
||||
}
|
||||
updateLimit(log);
|
||||
}
|
||||
|
||||
public LogConsoleBuffer getBuffer() { return _consoleBuffer; }
|
||||
|
||||
public void setDisplayOnScreen(boolean yes) {
|
||||
_displayOnScreen = yes;
|
||||
}
|
||||
@ -123,18 +174,7 @@ public class LogManager {
|
||||
_records.add(record);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during Log construction
|
||||
*
|
||||
*/
|
||||
void registerLog(Log log) {
|
||||
synchronized (_logs) {
|
||||
_logs.add(log);
|
||||
}
|
||||
updateLimit(log);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called periodically by the log writer's thread
|
||||
*
|
||||
@ -148,23 +188,6 @@ public class LogManager {
|
||||
///
|
||||
///
|
||||
|
||||
private LogManager(String location) {
|
||||
_displayOnScreen = true;
|
||||
_location = location;
|
||||
_records = new ArrayList();
|
||||
_limits = new HashSet();
|
||||
_logs = new HashSet();
|
||||
_defaultLimit = Log.DEBUG;
|
||||
_configLastRead = 0;
|
||||
loadConfig();
|
||||
_writer = new LogWriter();
|
||||
Thread t = new I2PThread(_writer);
|
||||
t.setName("LogWriter");
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
Runtime.getRuntime().addShutdownHook(new ShutdownHook());
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
@ -175,6 +198,8 @@ public class LogManager {
|
||||
if ((_configLastRead > 0) && (_configLastRead > cfgFile.lastModified())) {
|
||||
_log.debug("Short circuiting config read");
|
||||
return;
|
||||
} else {
|
||||
_log.debug("Loading config from " + _location);
|
||||
}
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
@ -212,7 +237,7 @@ public class LogManager {
|
||||
_displayOnScreen = false;
|
||||
}
|
||||
|
||||
String filenameOverride = System.getProperty(FILENAME_OVERRIDE_PROP);
|
||||
String filenameOverride = _context.getProperty(FILENAME_OVERRIDE_PROP);
|
||||
if (filenameOverride != null)
|
||||
_baseLogfilename = filenameOverride;
|
||||
else
|
||||
@ -297,11 +322,11 @@ public class LogManager {
|
||||
}
|
||||
|
||||
private void updateLimits() {
|
||||
Set logs = new HashSet();
|
||||
Map logs = null;
|
||||
synchronized (_logs) {
|
||||
logs.addAll(_logs);
|
||||
logs = new HashMap(_logs);
|
||||
}
|
||||
for (Iterator iter = logs.iterator(); iter.hasNext();) {
|
||||
for (Iterator iter = logs.values().iterator(); iter.hasNext();) {
|
||||
Log log = (Log) iter.next();
|
||||
updateLimit(log);
|
||||
}
|
||||
@ -322,10 +347,13 @@ public class LogManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (max != null)
|
||||
if (max != null) {
|
||||
log.setMinimumPriority(max.getLimit());
|
||||
else
|
||||
} else {
|
||||
//if (_log != null)
|
||||
// _log.debug("The log for " + log.getClass() + " has no matching limits");
|
||||
log.setMinimumPriority(_defaultLimit);
|
||||
}
|
||||
}
|
||||
|
||||
private List getLimits(Log log) {
|
||||
@ -373,10 +401,11 @@ public class LogManager {
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
Log l1 = new Log("test.1");
|
||||
Log l2 = new Log("test.2");
|
||||
Log l21 = new Log("test.2.1");
|
||||
Log l = new Log("test");
|
||||
I2PAppContext ctx = new I2PAppContext();
|
||||
Log l1 = ctx.logManager().getLog("test.1");
|
||||
Log l2 = ctx.logManager().getLog("test.2");
|
||||
Log l21 = ctx.logManager().getLog("test.2.1");
|
||||
Log l = ctx.logManager().getLog("test");
|
||||
l.debug("this should fail");
|
||||
l.info("this should pass");
|
||||
l1.warn("this should pass");
|
||||
|
@ -26,13 +26,13 @@ class LogRecordFormatter {
|
||||
private final static int MAX_THREAD_LENGTH = 12;
|
||||
private final static int MAX_PRIORITY_LENGTH = 5;
|
||||
|
||||
public static String formatRecord(LogRecord rec) {
|
||||
public static String formatRecord(LogManager manager, LogRecord rec) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
char format[] = LogManager.getInstance()._getFormat();
|
||||
char format[] = manager._getFormat();
|
||||
for (int i = 0; i < format.length; ++i) {
|
||||
switch ((int) format[i]) {
|
||||
case (int) LogManager.DATE:
|
||||
buf.append(getWhen(rec));
|
||||
buf.append(getWhen(manager, rec));
|
||||
break;
|
||||
case (int) LogManager.CLASS:
|
||||
buf.append(getWhere(rec));
|
||||
@ -71,8 +71,8 @@ class LogRecordFormatter {
|
||||
return toString(logRecord.getThreadName(), MAX_THREAD_LENGTH);
|
||||
}
|
||||
|
||||
private static String getWhen(LogRecord logRecord) {
|
||||
return LogManager.getInstance()._getDateFormat().format(new Date(logRecord.getDate()));
|
||||
private static String getWhen(LogManager manager, LogRecord logRecord) {
|
||||
return manager._getDateFormat().format(new Date(logRecord.getDate()));
|
||||
}
|
||||
|
||||
private static String getPriority(LogRecord rec) {
|
||||
|
@ -29,8 +29,14 @@ class LogWriter implements Runnable {
|
||||
private int _rotationNum = -1;
|
||||
private String _logFilenamePattern;
|
||||
private File _currentFile;
|
||||
private LogManager _manager;
|
||||
|
||||
private boolean _write;
|
||||
|
||||
private LogWriter() {}
|
||||
public LogWriter(LogManager manager) {
|
||||
_manager = manager;
|
||||
}
|
||||
|
||||
public void stopWriting() {
|
||||
_write = false;
|
||||
@ -46,7 +52,7 @@ class LogWriter implements Runnable {
|
||||
|
||||
public void flushRecords() {
|
||||
try {
|
||||
List records = LogManager.getInstance()._removeAll();
|
||||
List records = _manager._removeAll();
|
||||
for (int i = 0; i < records.size(); i++) {
|
||||
LogRecord rec = (LogRecord) records.get(i);
|
||||
writeRecord(rec);
|
||||
@ -68,19 +74,19 @@ class LogWriter implements Runnable {
|
||||
}
|
||||
long now = Clock.getInstance().now();
|
||||
if (now - _lastReadConfig > CONFIG_READ_ITERVAL) {
|
||||
LogManager.getInstance().rereadConfig();
|
||||
_manager.rereadConfig();
|
||||
_lastReadConfig = now;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeRecord(LogRecord rec) {
|
||||
String val = LogRecordFormatter.formatRecord(rec);
|
||||
String val = LogRecordFormatter.formatRecord(_manager, rec);
|
||||
writeRecord(val);
|
||||
|
||||
if (LogManager.getInstance().getDisplayOnScreenLevel() <= rec.getPriority()) {
|
||||
if (_manager.getDisplayOnScreenLevel() <= rec.getPriority()) {
|
||||
// we always add to the console buffer, but only sometimes write to stdout
|
||||
LogConsoleBuffer.getInstance().add(val);
|
||||
if (LogManager.getInstance().displayOnScreen()) {
|
||||
_manager.getBuffer().add(val);
|
||||
if (_manager.displayOnScreen()) {
|
||||
System.out.print(val);
|
||||
}
|
||||
}
|
||||
@ -98,7 +104,7 @@ class LogWriter implements Runnable {
|
||||
System.err.println("Error writing record, disk full?");
|
||||
t.printStackTrace();
|
||||
}
|
||||
if (_numBytesInCurrentFile >= LogManager.getInstance()._getFileSize()) {
|
||||
if (_numBytesInCurrentFile >= _manager._getFileSize()) {
|
||||
rotateFile();
|
||||
}
|
||||
}
|
||||
@ -108,7 +114,7 @@ class LogWriter implements Runnable {
|
||||
*
|
||||
*/
|
||||
private void rotateFile() {
|
||||
String pattern = LogManager.getInstance()._getBaseLogfilename();
|
||||
String pattern = _manager._getBaseLogfilename();
|
||||
File f = getNextFile(pattern);
|
||||
_currentFile = f;
|
||||
_numBytesInCurrentFile = 0;
|
||||
@ -129,7 +135,7 @@ class LogWriter implements Runnable {
|
||||
if (pattern.indexOf('#') < 0) {
|
||||
return new File(pattern);
|
||||
} else {
|
||||
int max = LogManager.getInstance()._getRotationLimit();
|
||||
int max = _manager._getRotationLimit();
|
||||
if (_rotationNum == -1) {
|
||||
return getFirstFile(pattern, max);
|
||||
} else {
|
||||
|
@ -10,6 +10,7 @@ package net.i2p.util;
|
||||
*/
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Singleton for whatever PRNG i2p uses.
|
||||
@ -17,14 +18,14 @@ import java.security.SecureRandom;
|
||||
* @author jrandom
|
||||
*/
|
||||
public class RandomSource extends SecureRandom {
|
||||
private final static RandomSource _random = new RandomSource();
|
||||
private Log _log;
|
||||
|
||||
private RandomSource() {
|
||||
public RandomSource(I2PAppContext context) {
|
||||
super();
|
||||
_log = context.logManager().getLog(RandomSource.class);
|
||||
}
|
||||
|
||||
public static RandomSource getInstance() {
|
||||
return _random;
|
||||
return I2PAppContext.getGlobalContext().random();
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user