forked from I2P_Developers/i2p.i2p
539 lines
18 KiB
Java
539 lines
18 KiB
Java
package net.i2p;
|
|
|
|
import java.util.HashSet;
|
|
import java.util.Properties;
|
|
import java.util.Set;
|
|
|
|
import net.i2p.client.naming.NamingService;
|
|
import net.i2p.client.naming.PetNameDB;
|
|
import net.i2p.crypto.AESEngine;
|
|
import net.i2p.crypto.CryptixAESEngine;
|
|
import net.i2p.crypto.DSAEngine;
|
|
import net.i2p.crypto.DummyDSAEngine;
|
|
import net.i2p.crypto.DummyElGamalEngine;
|
|
import net.i2p.crypto.DummyPooledRandomSource;
|
|
import net.i2p.crypto.ElGamalAESEngine;
|
|
import net.i2p.crypto.ElGamalEngine;
|
|
import net.i2p.crypto.HMAC256Generator;
|
|
import net.i2p.crypto.HMACGenerator;
|
|
import net.i2p.crypto.KeyGenerator;
|
|
import net.i2p.crypto.PersistentSessionKeyManager;
|
|
import net.i2p.crypto.SHA256Generator;
|
|
import net.i2p.crypto.SessionKeyManager;
|
|
import net.i2p.data.RoutingKeyGenerator;
|
|
import net.i2p.stat.StatManager;
|
|
import net.i2p.util.Clock;
|
|
import net.i2p.util.FortunaRandomSource;
|
|
import net.i2p.util.LogManager;
|
|
import net.i2p.util.PooledRandomSource;
|
|
import net.i2p.util.RandomSource;
|
|
|
|
/**
|
|
* <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;
|
|
|
|
private Properties _overrideProps;
|
|
|
|
private StatManager _statManager;
|
|
private SessionKeyManager _sessionKeyManager;
|
|
private NamingService _namingService;
|
|
private PetNameDB _petnameDb;
|
|
private ElGamalEngine _elGamalEngine;
|
|
private ElGamalAESEngine _elGamalAESEngine;
|
|
private AESEngine _AESEngine;
|
|
private LogManager _logManager;
|
|
private HMACGenerator _hmac;
|
|
private HMAC256Generator _hmac256;
|
|
private SHA256Generator _sha;
|
|
protected Clock _clock; // overridden in RouterContext
|
|
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 _petnameDbInitialized;
|
|
private volatile boolean _elGamalEngineInitialized;
|
|
private volatile boolean _elGamalAESEngineInitialized;
|
|
private volatile boolean _AESEngineInitialized;
|
|
private volatile boolean _logManagerInitialized;
|
|
private volatile boolean _hmacInitialized;
|
|
private volatile boolean _hmac256Initialized;
|
|
private volatile boolean _shaInitialized;
|
|
protected volatile boolean _clockInitialized; // used in RouterContext
|
|
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() {
|
|
synchronized (I2PAppContext.class) {
|
|
if (_globalAppContext == null) {
|
|
_globalAppContext = new I2PAppContext(false, null);
|
|
}
|
|
}
|
|
return _globalAppContext;
|
|
}
|
|
|
|
/**
|
|
* Lets root a brand new context
|
|
*
|
|
*/
|
|
public I2PAppContext() {
|
|
this(true, null);
|
|
}
|
|
|
|
/**
|
|
* Lets root a brand new context
|
|
*
|
|
*/
|
|
public I2PAppContext(Properties envProps) {
|
|
this(true, envProps);
|
|
}
|
|
|
|
/**
|
|
* @param doInit should this context be used as the global one (if necessary)?
|
|
*/
|
|
private I2PAppContext(boolean doInit, Properties envProps) {
|
|
if (doInit) {
|
|
synchronized (I2PAppContext.class) {
|
|
if (_globalAppContext == null)
|
|
_globalAppContext = this;
|
|
}
|
|
}
|
|
_overrideProps = envProps;
|
|
_statManager = null;
|
|
_sessionKeyManager = null;
|
|
_namingService = null;
|
|
_petnameDb = 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, using properties
|
|
* provided during the context construction, or falling back on
|
|
* System.getProperty if no properties were provided during construction
|
|
* (or the specified prop wasn't included).
|
|
*
|
|
*/
|
|
public String getProperty(String propName) {
|
|
if (_overrideProps != null) {
|
|
if (_overrideProps.containsKey(propName))
|
|
return _overrideProps.getProperty(propName);
|
|
}
|
|
return System.getProperty(propName);
|
|
}
|
|
|
|
/**
|
|
* Access the configuration attributes of this context, using properties
|
|
* provided during the context construction, or falling back on
|
|
* System.getProperty if no properties were provided during construction
|
|
* (or the specified prop wasn't included).
|
|
*
|
|
*/
|
|
public String getProperty(String propName, String defaultValue) {
|
|
if (_overrideProps != null) {
|
|
if (_overrideProps.containsKey(propName))
|
|
return _overrideProps.getProperty(propName, defaultValue);
|
|
}
|
|
return System.getProperty(propName, defaultValue);
|
|
}
|
|
|
|
/**
|
|
* Return an int with an int default
|
|
*/
|
|
public int getProperty(String propName, int defaultVal) {
|
|
String val = null;
|
|
if (_overrideProps != null) {
|
|
val = _overrideProps.getProperty(propName);
|
|
if (val == null)
|
|
val = System.getProperty(propName);
|
|
}
|
|
int ival = defaultVal;
|
|
if (val != null) {
|
|
try {
|
|
ival = Integer.parseInt(val);
|
|
} catch (NumberFormatException nfe) {}
|
|
}
|
|
return ival;
|
|
}
|
|
|
|
/**
|
|
* Access the configuration attributes of this context, listing the properties
|
|
* provided during the context construction, as well as the ones included in
|
|
* System.getProperties.
|
|
*
|
|
* @return set of Strings containing the names of defined system properties
|
|
*/
|
|
public Set getPropertyNames() {
|
|
Set names = new HashSet(System.getProperties().keySet());
|
|
if (_overrideProps != null)
|
|
names.addAll(_overrideProps.keySet());
|
|
return names;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
}
|
|
|
|
public PetNameDB petnameDb() {
|
|
if (!_petnameDbInitialized)
|
|
initializePetnameDb();
|
|
return _petnameDb;
|
|
}
|
|
|
|
private void initializePetnameDb() {
|
|
synchronized (this) {
|
|
if (_petnameDb == null) {
|
|
_petnameDb = new PetNameDB();
|
|
}
|
|
_petnameDbInitialized = 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 aes() {
|
|
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 HMACGenerator hmac() {
|
|
if (!_hmacInitialized)
|
|
initializeHMAC();
|
|
return _hmac;
|
|
}
|
|
|
|
private void initializeHMAC() {
|
|
synchronized (this) {
|
|
if (_hmac == null) {
|
|
_hmac= new HMACGenerator(this);
|
|
}
|
|
_hmacInitialized = true;
|
|
}
|
|
}
|
|
|
|
public HMAC256Generator hmac256() {
|
|
if (!_hmac256Initialized)
|
|
initializeHMAC256();
|
|
return _hmac256;
|
|
}
|
|
|
|
private void initializeHMAC256() {
|
|
synchronized (this) {
|
|
if (_hmac256 == null) {
|
|
_hmac256 = new HMAC256Generator(this);
|
|
}
|
|
_hmac256Initialized = 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) {
|
|
if ("off".equals(getProperty("i2p.encryption", "on")))
|
|
_dsa = new DummyDSAEngine(this);
|
|
else
|
|
_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() { // overridden in RouterContext
|
|
if (!_clockInitialized)
|
|
initializeClock();
|
|
return _clock;
|
|
}
|
|
|
|
protected void initializeClock() { // overridden in RouterContext
|
|
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) {
|
|
if (true)
|
|
_random = new FortunaRandomSource(this);
|
|
else if ("true".equals(getProperty("i2p.weakPRNG", "false")))
|
|
_random = new DummyPooledRandomSource(this);
|
|
else
|
|
_random = new PooledRandomSource(this);
|
|
}
|
|
_randomInitialized = true;
|
|
}
|
|
}
|
|
}
|