* DHSessionKeyBuilder:

- Move from core to router/transport
    - Make non-static, instantiate in TransportManager
    - Generate keypair in constructor and make final
      to move more processing to the precalc thread
      and eliminate races
    - Synchronize getSessionKey() to eliminate races
    - Comment out unused methods
  * UDPTransport:
    - Make key builder final in InboundEstablishState to
      eliminate rare NPE (ticket #406)
    - Remove unused static instance
This commit is contained in:
zzz
2012-03-14 12:09:02 +00:00
parent 17f9280b3f
commit 2436d86000
12 changed files with 267 additions and 245 deletions

View File

@@ -30,7 +30,6 @@ import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.CoreVersion;
import net.i2p.crypto.DHSessionKeyBuilder;
import net.i2p.data.Certificate;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
@@ -711,10 +710,8 @@ public class Router implements RouterClock.ClockShiftListener {
private void warmupCrypto() {
_context.random().nextBoolean();
// Use restart() to refire the static refiller threads, in case
// we are restarting the router in the same JVM (Android)
DHSessionKeyBuilder.restart();
_context.elGamalEngine().restart();
// Instantiate to fire up the YK refiller thread
_context.elGamalEngine();
}
private void startupQueue() {
@@ -1091,15 +1088,12 @@ public class Router implements RouterClock.ClockShiftListener {
// shut down I2PAppContext tasks here
// If there are multiple routers in the JVM, we don't want to do this
// to the DH or YK tasks, as they are singletons.
try {
_context.elGamalEngine().shutdown();
} catch (Throwable t) { _log.log(Log.CRIT, "Error shutting elGamal", t); }
if (contexts.isEmpty()) {
try {
DHSessionKeyBuilder.shutdown();
} catch (Throwable t) { _log.log(Log.CRIT, "Error shutting DH", t); }
try {
_context.elGamalEngine().shutdown();
} catch (Throwable t) { _log.log(Log.CRIT, "Error shutting elGamal", t); }
// any thing else to shut down?
} else {
_log.logAlways(Log.WARN, "Warning - " + contexts.size() + " routers remaining in this JVM, not releasing all resources");
}

View File

@@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 13;
public final static long BUILD = 14;
/** for example "-test" */
public final static String EXTRA = "";

View File

@@ -56,9 +56,12 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
startTimestamper();
}
/**
* Cannot be restarted.
*/
public void shutdown() {
if (_manager != null)
_manager.stopListening();
_manager.shutdown();
}
public void restart() {

View File

@@ -30,6 +30,7 @@ import net.i2p.data.i2np.I2NPMessage;
import net.i2p.router.CommSystemFacade;
import net.i2p.router.OutNetMessage;
import net.i2p.router.RouterContext;
import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
import net.i2p.router.transport.ntcp.NTCPTransport;
import net.i2p.router.transport.udp.UDPTransport;
import net.i2p.util.Addresses;
@@ -45,6 +46,7 @@ public class TransportManager implements TransportEventListener {
private final Map<String, Transport> _transports;
private final RouterContext _context;
private final UPnPManager _upnpManager;
private final DHSessionKeyBuilder.PrecalcRunner _dhThread;
/** default true */
public final static String PROP_ENABLE_UDP = "i2np.udp.enable";
@@ -67,6 +69,7 @@ public class TransportManager implements TransportEventListener {
_upnpManager = new UPnPManager(context, this);
else
_upnpManager = null;
_dhThread = new DHSessionKeyBuilder.PrecalcRunner(context);
}
public void addTransport(Transport transport) {
@@ -84,12 +87,12 @@ public class TransportManager implements TransportEventListener {
private void configTransports() {
boolean enableUDP = _context.getBooleanPropertyDefaultTrue(PROP_ENABLE_UDP);
if (enableUDP) {
UDPTransport udp = new UDPTransport(_context);
UDPTransport udp = new UDPTransport(_context, _dhThread);
addTransport(udp);
initializeAddress(udp);
}
if (isNTCPEnabled(_context))
addTransport(new NTCPTransport(_context));
addTransport(new NTCPTransport(_context, _dhThread));
if (_transports.isEmpty())
_log.log(Log.CRIT, "No transports are enabled");
}
@@ -135,6 +138,7 @@ public class TransportManager implements TransportEventListener {
}
public void startListening() {
_dhThread.start();
// For now, only start UPnP if we have no publicly-routable addresses
// so we don't open the listener ports to the world.
// Maybe we need a config option to force on? Probably not.
@@ -161,6 +165,9 @@ public class TransportManager implements TransportEventListener {
startListening();
}
/**
* Can be restarted.
*/
public void stopListening() {
if (_upnpManager != null)
_upnpManager.stop();
@@ -170,6 +177,16 @@ public class TransportManager implements TransportEventListener {
_transports.clear();
}
/**
* Cannot be restarted.
* @since 0.9
*/
public void shutdown() {
stopListening();
_dhThread.shutdown();
}
public Transport getTransport(String style) {
return _transports.get(style);
}

View File

@@ -0,0 +1,565 @@
package net.i2p.router.transport.crypto;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.IOException;
//import java.io.InputStream;
//import java.io.OutputStream;
import java.math.BigInteger;
import java.util.concurrent.LinkedBlockingQueue;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.crypto.CryptoConstants;
import net.i2p.crypto.KeyGenerator;
import net.i2p.crypto.SHA256Generator;
import net.i2p.data.ByteArray;
//import net.i2p.data.DataHelper;
import net.i2p.data.SessionKey;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
import net.i2p.util.NativeBigInteger;
import net.i2p.util.RandomSource;
/**
* Generate a new session key through a diffie hellman exchange. This uses the
* constants defined in CryptoConstants, which causes the exchange to create a
* 256 bit session key.
*
* This class precalcs a set of values on its own thread.
* Whenever the pool has
* less than the minimum, it fills it up again to the max. There is a delay after
* each precalculation so that the CPU isn't hosed during startup.
* These three parameters are controlled by java environmental variables and
* can be adjusted via:
* -Dcrypto.dh.precalc.min=40 -Dcrypto.dh.precalc.max=100 -Dcrypto.dh.precalc.delay=60000
*
* (delay is milliseconds)
*
* To disable precalculation, set min to 0
*
* @since 0.9 moved from net.i2p.crypto
*
* @author jrandom
*/
public class DHSessionKeyBuilder {
// the data of importance
private final BigInteger _myPrivateValue;
private final BigInteger _myPublicValue;
private BigInteger _peerValue;
private SessionKey _sessionKey;
private final ByteArray _extraExchangedBytes; // bytes after the session key from the DH exchange
private final static String PROP_DH_PRECALC_MIN = "crypto.dh.precalc.min";
private final static String PROP_DH_PRECALC_MAX = "crypto.dh.precalc.max";
private final static String PROP_DH_PRECALC_DELAY = "crypto.dh.precalc.delay";
private final static int DEFAULT_DH_PRECALC_MIN = 15;
private final static int DEFAULT_DH_PRECALC_MAX = 40;
private final static int DEFAULT_DH_PRECALC_DELAY = 200;
/**
* Create a new public/private value pair for the DH exchange.
* Only for internal use and unit tests.
* Others should get instances from PrecalcRunner.getBuilder()
*/
DHSessionKeyBuilder() {
this(RandomSource.getInstance());
}
/**
* Create a new public/private value pair for the DH exchange.
* Only for internal use and unit tests.
* Others should get instances from PrecalcRunner.getBuilder()
*/
DHSessionKeyBuilder(RandomSource random) {
_myPrivateValue = new NativeBigInteger(KeyGenerator.PUBKEY_EXPONENT_SIZE, random);
_myPublicValue = CryptoConstants.elgg.modPow(_myPrivateValue, CryptoConstants.elgp);
_extraExchangedBytes = new ByteArray();
}
/**
* Conduct a DH exchange over the streams, returning the resulting data.
*
* unused
* @return exchanged data
* @throws IOException if there is an error (but does not close the streams
*/
/****
public static DHSessionKeyBuilder exchangeKeys(InputStream in, OutputStream out) throws IOException {
DHSessionKeyBuilder builder = new DHSessionKeyBuilder();
// send: X
writeBigI(out, builder.getMyPublicValue());
// read: Y
BigInteger Y = readBigI(in);
if (Y == null) return null;
try {
builder.setPeerPublicValue(Y);
return builder;
} catch (InvalidPublicParameterException ippe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Key exchange failed (hostile peer?)", ippe);
return null;
}
}
****/
/**
* unused
*/
/****
private static BigInteger readBigI(InputStream in) throws IOException {
byte Y[] = new byte[256];
int read = DataHelper.read(in, Y);
if (read != 256) {
return null;
}
if (1 == (Y[0] & 0x80)) {
// high bit set, need to inject an additional byte to keep 2s complement
if (_log.shouldLog(Log.DEBUG))
_log.debug("High bit set");
byte Y2[] = new byte[257];
System.arraycopy(Y, 0, Y2, 1, 256);
Y = Y2;
}
return new NativeBigInteger(1, Y);
}
****/
/**
* Write out the integer as a 256 byte value. This left pads with 0s so
* to keep in 2s complement, and if it is already 257 bytes (due to
* the sign bit) ignore that first byte.
*
* unused
*/
/****
private static void writeBigI(OutputStream out, BigInteger val) throws IOException {
byte x[] = val.toByteArray();
for (int i = x.length; i < 256; i++)
out.write(0);
if (x.length == 257)
out.write(x, 1, 256);
else if (x.length == 256)
out.write(x);
else if (x.length > 257)
throw new IllegalArgumentException("Value is too large! length="+x.length);
out.flush();
}
****/
/**
* Retrieve the private value used by the local participant in the DH exchange
* unused
*/
/*
public BigInteger getMyPrivateValue() {
return _myPrivateValue;
}
*/
/**
* Retrieve the public value used by the local participant in the DH exchange,
*/
public BigInteger getMyPublicValue() {
return _myPublicValue;
}
/**
* Return a 256 byte representation of our public key, with leading 0s
* if necessary.
*
*/
public byte[] getMyPublicValueBytes() {
return toByteArray(getMyPublicValue());
}
private static final byte[] toByteArray(BigInteger bi) {
byte data[] = bi.toByteArray();
byte rv[] = new byte[256];
if (data.length == 257) // high byte has the sign bit
System.arraycopy(data, 1, rv, 0, rv.length);
else if (data.length == 256)
System.arraycopy(data, 0, rv, 0, rv.length);
else
System.arraycopy(data, 0, rv, rv.length-data.length, data.length);
return rv;
}
/**
* Specify the value given by the peer for use in the session key negotiation
*
*/
public void setPeerPublicValue(BigInteger peerVal) throws InvalidPublicParameterException {
validatePublic(peerVal);
_peerValue = peerVal;
}
/**
* @param val 256 bytes
*/
public void setPeerPublicValue(byte val[]) throws InvalidPublicParameterException {
if (val.length != 256)
throw new IllegalArgumentException("Peer public value must be exactly 256 bytes");
if (1 == (val[0] & 0x80)) {
// high bit set, need to inject an additional byte to keep 2s complement
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("High bit set");
byte val2[] = new byte[257];
System.arraycopy(val, 0, val2, 1, 256);
val = val2;
}
setPeerPublicValue(new NativeBigInteger(1, val));
//_peerValue = new NativeBigInteger(val);
}
public BigInteger getPeerPublicValue() {
return _peerValue;
}
/**
* Return a 256 byte representation of his public key, with leading 0s
* if necessary.
*
*/
public byte[] getPeerPublicValueBytes() {
return toByteArray(getPeerPublicValue());
}
/**
* Retrieve the session key, calculating it if necessary (and if possible).
*
* @return session key exchanged, or null if the exchange is not complete
*/
public synchronized SessionKey getSessionKey() {
if (_sessionKey != null) return _sessionKey;
if (_peerValue != null) {
_sessionKey = calculateSessionKey(_myPrivateValue, _peerValue);
} else {
//System.err.println("Not ready yet.. privateValue and peerValue must be set ("
// + (_myPrivateValue != null ? "set" : "null") + ","
// + (_peerValue != null ? "set" : "null") + ")");
}
return _sessionKey;
}
/**
* Retrieve the extra bytes beyond the session key resulting from the DH exchange.
* If there aren't enough bytes (with all of them being consumed by the 32 byte key),
* the SHA256 of the key itself is used.
*
* @return non-null (but rv.getData() may be null)
*/
public ByteArray getExtraBytes() {
return _extraExchangedBytes;
}
/**
* Calculate a session key based on the private value and the public peer value
*
*/
private final SessionKey calculateSessionKey(BigInteger myPrivateValue, BigInteger publicPeerValue) {
//long start = System.currentTimeMillis();
SessionKey key = new SessionKey();
BigInteger exchangedKey = publicPeerValue.modPow(myPrivateValue, CryptoConstants.elgp);
byte buf[] = exchangedKey.toByteArray();
byte val[] = new byte[32];
if (buf.length < val.length) {
System.arraycopy(buf, 0, val, 0, buf.length);
byte remaining[] = SHA256Generator.getInstance().calculateHash(val).getData();
_extraExchangedBytes.setData(remaining);
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Storing " + remaining.length + " bytes from the DH exchange by SHA256 the session key");
} else { // (buf.length >= val.length)
System.arraycopy(buf, 0, val, 0, val.length);
// feed the extra bytes into the PRNG
RandomSource.getInstance().harvester().feedEntropy("DH", buf, val.length, buf.length-val.length);
byte remaining[] = new byte[buf.length - val.length];
System.arraycopy(buf, val.length, remaining, 0, remaining.length);
_extraExchangedBytes.setData(remaining);
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Storing " + remaining.length + " bytes from the end of the DH exchange");
}
key.setData(val);
//long end = System.currentTimeMillis();
//long diff = end - start;
//_context.statManager().addRateData("crypto.dhCalculateSessionTime", diff, diff);
//if (diff > 1000) {
// if (_log.shouldLog(Log.WARN)) _log.warn("Generating session key took too long (" + diff + " ms");
//} else {
// if (_log.shouldLog(Log.DEBUG)) _log.debug("Generating session key " + diff + " ms");
//}
return key;
}
/**
* rfc2631:
* The following algorithm MAY be used to validate a received public key y.
*
* 1. Verify that y lies within the interval [2,p-1]. If it does not,
* the key is invalid.
* 2. Compute y^q mod p. If the result == 1, the key is valid.
* Otherwise the key is invalid.
*/
private static final void validatePublic(BigInteger publicValue) throws InvalidPublicParameterException {
int cmp = publicValue.compareTo(NativeBigInteger.ONE);
if (cmp <= 0)
throw new InvalidPublicParameterException("Public value is below two: " + publicValue.toString());
cmp = publicValue.compareTo(CryptoConstants.elgp);
if (cmp >= 0)
throw new InvalidPublicParameterException("Public value is above p-1: " + publicValue.toString());
// todo:
// whatever validation needs to be done to mirror the rfc's part 2 (we don't have a q, so can't do
// if (NativeBigInteger.ONE.compareTo(publicValue.modPow(q, CryptoConstants.elgp)) != 0)
// throw new InvalidPublicParameterException("Invalid public value with y^q mod p != 1");
//
}
/*
private static void testValidation() {
NativeBigInteger bi = new NativeBigInteger("-3416069082912684797963255430346582466254460710249795973742848334283491150671563023437888953432878859472362439146158925287289114133666004165938814597775594104058593692562989626922979416277152479694258099203456493995467386903611666213773085025718340335205240293383622352894862685806192183268523899615405287022135356656720938278415659792084974076416864813957028335830794117802560169423133816961503981757298122040391506600117301607823659479051969827845787626261515313227076880722069706394405554113103165334903531980102626092646197079218895216346725765704256096661045699444128316078549709132753443706200863682650825635513");
try {
validatePublic(bi);
System.err.println("valid?!");
} catch (InvalidPublicParameterException ippe) {
System.err.println("Ok, invalid. cool");
}
byte val[] = bi.toByteArray();
System.out.println("Len: " + val.length + " first is ok? " + ( (val[0] & 0x80) == 1)
+ "\n" + DataHelper.toString(val, 64));
NativeBigInteger bi2 = new NativeBigInteger(1, val);
try {
validatePublic(bi2);
System.out.println("valid");
} catch (InvalidPublicParameterException ippe) {
System.out.println("invalid");
}
}
*/
/******
public static void main(String args[]) {
//if (true) { testValidation(); return; }
RandomSource.getInstance().nextBoolean(); // warm it up
try {
Thread.sleep(20 * 1000);
} catch (InterruptedException ie) { // nop
}
I2PAppContext ctx = new I2PAppContext();
_log.debug("\n\n\n\nBegin test\n");
long negTime = 0;
try {
for (int i = 0; i < 5; i++) {
long startNeg = System.currentTimeMillis();
DHSessionKeyBuilder builder1 = new DHSessionKeyBuilder();
DHSessionKeyBuilder builder2 = new DHSessionKeyBuilder();
BigInteger pub1 = builder1.getMyPublicValue();
builder2.setPeerPublicValue(pub1);
BigInteger pub2 = builder2.getMyPublicValue();
builder1.setPeerPublicValue(pub2);
SessionKey key1 = builder1.getSessionKey();
SessionKey key2 = builder2.getSessionKey();
long endNeg = System.currentTimeMillis();
negTime += endNeg - startNeg;
if (!key1.equals(key2))
_log.error("**ERROR: Keys do not match");
else
_log.debug("**Success: Keys match");
byte iv[] = new byte[16];
RandomSource.getInstance().nextBytes(iv);
String origVal = "1234567890123456"; // 16 bytes max using AESEngine
byte enc[] = new byte[16];
byte dec[] = new byte[16];
ctx.aes().encrypt(origVal.getBytes(), 0, enc, 0, key1, iv, 16);
ctx.aes().decrypt(enc, 0, dec, 0, key2, iv, 16);
String tranVal = new String(dec);
if (origVal.equals(tranVal))
_log.debug("**Success: D(E(val)) == val");
else
_log.error("**ERROR: D(E(val)) != val [val=(" + tranVal + "), origVal=(" + origVal + ")");
}
} catch (InvalidPublicParameterException ippe) {
_log.error("Invalid dh", ippe);
}
_log.debug("Negotiation time for 5 runs: " + negTime + " @ " + negTime / 5l + "ms each");
try {
Thread.sleep(2000);
} catch (InterruptedException ie) { // nop
}
}
******/
/**
* @since 0.9
*/
public interface Factory {
/**
* Construct a new DH key builder
* or pulls a prebuilt one from the queue.
*/
public DHSessionKeyBuilder getBuilder();
}
public static class PrecalcRunner extends I2PThread implements Factory {
private final I2PAppContext _context;
private final Log _log;
private final int _minSize;
private final int _maxSize;
private final int _calcDelay;
private final LinkedBlockingQueue<DHSessionKeyBuilder> _builders;
private volatile boolean _isRunning;
/** check every 30 seconds whether we have less than the minimum */
private long _checkDelay = 30 * 1000;
public PrecalcRunner(I2PAppContext ctx) {
super("DH Precalc");
_context = ctx;
_log = ctx.logManager().getLog(DHSessionKeyBuilder.class);
ctx.statManager().createRateStat("crypto.dhGeneratePublicTime", "How long it takes to create x and X", "Encryption", new long[] { 60*60*1000 });
//ctx.statManager().createRateStat("crypto.dhCalculateSessionTime", "How long it takes to create the session key", "Encryption", new long[] { 60*60*1000 });
ctx.statManager().createRateStat("crypto.DHUsed", "Need a DH from the queue", "Encryption", new long[] { 60*60*1000 });
ctx.statManager().createRateStat("crypto.DHEmpty", "DH queue empty", "Encryption", new long[] { 60*60*1000 });
// add to the defaults for every 128MB of RAM, up to 512MB
long maxMemory = Runtime.getRuntime().maxMemory();
if (maxMemory == Long.MAX_VALUE)
maxMemory = 127*1024*1024l;
int factor = (int) Math.max(1l, Math.min(4l, 1 + (maxMemory / (128*1024*1024l))));
int defaultMin = DEFAULT_DH_PRECALC_MIN * factor;
int defaultMax = DEFAULT_DH_PRECALC_MAX * factor;
_minSize = ctx.getProperty(PROP_DH_PRECALC_MIN, defaultMin);
_maxSize = ctx.getProperty(PROP_DH_PRECALC_MAX, defaultMax);
_calcDelay = ctx.getProperty(PROP_DH_PRECALC_DELAY, DEFAULT_DH_PRECALC_DELAY);
if (_log.shouldLog(Log.DEBUG))
_log.debug("DH Precalc (minimum: " + _minSize + " max: " + _maxSize + ", delay: "
+ _calcDelay + ")");
_builders = new LinkedBlockingQueue(_maxSize);
setPriority(Thread.MIN_PRIORITY);
}
/**
* Note that this stops the singleton precalc thread.
* You don't want to do this if there are multiple routers in the JVM.
* Fix this if you care. See Router.shutdown().
* @since 0.8.8
*/
public void shutdown() {
_isRunning = false;
this.interrupt();
_builders.clear();
}
public void run() {
_isRunning = true;
while (_isRunning) {
//long start = System.currentTimeMillis();
int startSize = getSize();
// Adjust delay
if (startSize <= (_minSize * 2 / 3) && _checkDelay > 1000)
_checkDelay -= 1000;
else if (startSize > (_minSize * 3 / 2) && _checkDelay < 60*1000)
_checkDelay += 1000;
if (startSize < _minSize) {
// fill all the way up, do the check here so we don't
// throw away one when full in addValues()
while (getSize() < _maxSize && _isRunning) {
long curStart = System.currentTimeMillis();
if (!addBuilder(precalc()))
break;
long curCalc = System.currentTimeMillis() - curStart;
// for some relief...
try {
Thread.sleep(_calcDelay + (curCalc * 3));
} catch (InterruptedException ie) { // nop
}
}
}
//long end = System.currentTimeMillis();
//int numCalc = curSize - startSize;
//if (numCalc > 0) {
// if (_log.shouldLog(Log.DEBUG))
// _log.debug("Precalced " + numCalc + " to " + curSize + " in "
// + (end - start - CALC_DELAY * numCalc) + "ms (not counting "
// + (CALC_DELAY * numCalc) + "ms relief). now sleeping");
//}
if (!_isRunning)
break;
try {
Thread.sleep(_checkDelay);
} catch (InterruptedException ie) { // nop
}
}
}
/**
* Construct a new DH key builder
* or pulls a prebuilt one from the queue.
*
* @since 0.9 moved from DHSKB
*/
public DHSessionKeyBuilder getBuilder() {
_context.statManager().addRateData("crypto.DHUsed", 1, 0);
DHSessionKeyBuilder builder = _builders.poll();
if (builder == null) {
if (_log.shouldLog(Log.INFO)) _log.info("No more builders, creating one now");
_context.statManager().addRateData("crypto.DHEmpty", 1, 0);
builder = precalc();
}
return builder;
}
private DHSessionKeyBuilder precalc() {
long start = System.currentTimeMillis();
DHSessionKeyBuilder builder = new DHSessionKeyBuilder(_context.random());
long end = System.currentTimeMillis();
long diff = end - start;
_context.statManager().addRateData("crypto.dhGeneratePublicTime", diff, diff);
if (diff > 1000) {
if (_log.shouldLog(Log.WARN))
_log.warn("Took more than a second (" + diff + "ms) to generate local DH value");
} else {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Took " + diff + "ms to generate local DH value");
}
return builder;
}
/** @return true if successful, false if full */
private final boolean addBuilder(DHSessionKeyBuilder builder) {
return _builders.offer(builder);
}
private final int getSize() {
return _builders.size();
}
}
public static class InvalidPublicParameterException extends I2PException {
public InvalidPublicParameterException() {
super();
}
public InvalidPublicParameterException(String msg) {
super(msg);
}
}
}

View File

@@ -12,7 +12,6 @@ import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import net.i2p.I2PAppContext;
import net.i2p.crypto.DHSessionKeyBuilder;
import net.i2p.data.Base64;
import net.i2p.data.Certificate;
import net.i2p.data.DataFormatException;
@@ -27,6 +26,7 @@ import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SigningPublicKey;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
import net.i2p.util.Log;
/**
@@ -70,12 +70,12 @@ import net.i2p.util.Log;
*
*/
class EstablishState {
private RouterContext _context;
private Log _log;
private final RouterContext _context;
private final Log _log;
// bob receives (and alice sends)
private byte _X[];
private byte _hX_xor_bobIdentHash[];
private final byte _X[];
private final byte _hX_xor_bobIdentHash[];
private int _aliceIdentSize;
/** contains the decrypted aliceIndexSize + aliceIdent + tsA + padding + aliceSig */
private ByteArrayOutputStream _sz_aliceIdent_tsA_padding_aliceSig;
@@ -100,7 +100,7 @@ class EstablishState {
*/
private int _curEncryptedOffset;
/** decryption buffer */
private byte _curDecrypted[];
private final byte _curDecrypted[];
/** bytes received so far */
private int _received;
@@ -109,10 +109,10 @@ class EstablishState {
private byte _extra[];
private DHSessionKeyBuilder _dh;
private final DHSessionKeyBuilder _dh;
private NTCPTransport _transport;
private NTCPConnection _con;
private final NTCPTransport _transport;
private final NTCPConnection _con;
private boolean _corrupt;
/** error causing the corruption */
private String _err;
@@ -127,15 +127,14 @@ class EstablishState {
_log = ctx.logManager().getLog(getClass());
_transport = transport;
_con = con;
_dh = new DHSessionKeyBuilder();
_dh = _transport.getDHBuilder();
_hX_xor_bobIdentHash = new byte[Hash.HASH_LENGTH];
if (_con.isInbound()) {
_X = new byte[256];
_hX_xor_bobIdentHash = new byte[Hash.HASH_LENGTH];
_sz_aliceIdent_tsA_padding_aliceSig = new ByteArrayOutputStream(512);
} else {
_X = _dh.getMyPublicValueBytes();
_Y = new byte[256];
_hX_xor_bobIdentHash = new byte[Hash.HASH_LENGTH];
byte hx[] = ctx.sha().calculateHash(_X).getData();
DataHelper.xor(hx, 0, con.getRemotePeer().calculateHash().getData(), 0, _hX_xor_bobIdentHash, 0, hx.length);
}

View File

@@ -31,13 +31,14 @@ import net.i2p.router.transport.CommSystemFacadeImpl;
import net.i2p.router.transport.Transport;
import net.i2p.router.transport.TransportBid;
import net.i2p.router.transport.TransportImpl;
import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
import net.i2p.util.Addresses;
import net.i2p.util.ConcurrentHashSet;
import net.i2p.util.Log;
import net.i2p.util.Translate;
/**
*
* The NIO TCP transport
*/
public class NTCPTransport extends TransportImpl {
private final Log _log;
@@ -64,6 +65,7 @@ public class NTCPTransport extends TransportImpl {
public static final String PROP_BIND_INTERFACE = "i2np.ntcp.bindInterface";
private final NTCPSendFinisher _finisher;
private final DHSessionKeyBuilder.Factory _dhFactory;
private long _lastBadSkew;
private static final long[] RATES = { 10*60*1000 };
@@ -71,9 +73,9 @@ public class NTCPTransport extends TransportImpl {
//private static final String THINSP = "&thinsp;/&thinsp;";
private static final String THINSP = " / ";
public NTCPTransport(RouterContext ctx) {
public NTCPTransport(RouterContext ctx, DHSessionKeyBuilder.Factory dh) {
super(ctx);
_dhFactory = dh;
_log = ctx.logManager().getLog(getClass());
_context.statManager().createRateStat("ntcp.sendTime", "Total message lifetime when sent completely", "ntcp", RATES);
@@ -585,6 +587,13 @@ public class NTCPTransport extends TransportImpl {
public String getStyle() { return STYLE; }
EventPumper getPumper() { return _pumper; }
/**
* @since 0.9
*/
DHSessionKeyBuilder getDHBuilder() {
return _dhFactory.getBuilder();
}
/**
* how long from initial connection attempt (accept() or connect()) until
* the con must be established to avoid premature close()ing

View File

@@ -9,7 +9,6 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.crypto.DHSessionKeyBuilder;
import net.i2p.data.Base64;
import net.i2p.data.Hash;
import net.i2p.data.RouterAddress;
@@ -22,6 +21,7 @@ import net.i2p.router.OutNetMessage;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
import net.i2p.util.SimpleScheduler;
@@ -212,7 +212,7 @@ class EstablishmentManager {
}
state = new OutboundEstablishState(_context, remAddr, port,
msg.getTarget().getIdentity(),
sessionKey, addr);
sessionKey, addr, _transport.getDHBuilder());
OutboundEstablishState oldState = _outboundStates.putIfAbsent(to, state);
boolean isNew = oldState == null;
if (!isNew)
@@ -303,7 +303,8 @@ class EstablishmentManager {
}
if (!_transport.allowConnection())
return; // drop the packet
state = new InboundEstablishState(_context, from.getIP(), from.getPort(), _transport.getLocalPort());
state = new InboundEstablishState(_context, from.getIP(), from.getPort(), _transport.getLocalPort(),
_transport.getDHBuilder());
state.receiveSessionRequest(reader.getSessionRequestReader());
InboundEstablishState oldState = _inboundStates.putIfAbsent(from, state);
isNew = oldState == null;

View File

@@ -3,7 +3,6 @@ package net.i2p.router.transport.udp;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import net.i2p.crypto.DHSessionKeyBuilder;
import net.i2p.data.Base64;
import net.i2p.data.ByteArray;
import net.i2p.data.DataFormatException;
@@ -12,6 +11,7 @@ import net.i2p.data.RouterIdentity;
import net.i2p.data.SessionKey;
import net.i2p.data.Signature;
import net.i2p.router.RouterContext;
import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
import net.i2p.util.Addresses;
import net.i2p.util.Log;
@@ -29,8 +29,7 @@ class InboundEstablishState {
private byte _receivedX[];
private byte _bobIP[];
private final int _bobPort;
// try to fix NPE in getSentY() ?????
private volatile DHSessionKeyBuilder _keyBuilder;
private final DHSessionKeyBuilder _keyBuilder;
// SessionCreated message
private byte _sentY[];
private final byte _aliceIP[];
@@ -68,7 +67,8 @@ class InboundEstablishState {
/** we are explicitly failing it */
public static final int STATE_FAILED = 5;
public InboundEstablishState(RouterContext ctx, byte remoteIP[], int remotePort, int localPort) {
public InboundEstablishState(RouterContext ctx, byte remoteIP[], int remotePort, int localPort,
DHSessionKeyBuilder dh) {
_context = ctx;
_log = ctx.logManager().getLog(InboundEstablishState.class);
_aliceIP = remoteIP;
@@ -77,6 +77,7 @@ class InboundEstablishState {
_bobPort = localPort;
_currentState = STATE_UNKNOWN;
_establishBegin = ctx.clock().now();
_keyBuilder = dh;
}
public synchronized int getState() { return _currentState; }
@@ -106,7 +107,6 @@ class InboundEstablishState {
public synchronized void generateSessionKey() throws DHSessionKeyBuilder.InvalidPublicParameterException {
if (_sessionKey != null) return;
_keyBuilder = new DHSessionKeyBuilder();
_keyBuilder.setPeerPublicValue(_receivedX);
_sessionKey = _keyBuilder.getSessionKey();
ByteArray extra = _keyBuilder.getExtraBytes();
@@ -130,7 +130,6 @@ class InboundEstablishState {
public synchronized byte[] getSentY() {
if (_sentY == null)
// Rare NPE seen here...
_sentY = _keyBuilder.getMyPublicValueBytes();
return _sentY;
}

View File

@@ -4,7 +4,6 @@ import java.net.InetAddress;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import net.i2p.crypto.DHSessionKeyBuilder;
import net.i2p.data.Base64;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper;
@@ -13,6 +12,7 @@ import net.i2p.data.SessionKey;
import net.i2p.data.Signature;
import net.i2p.router.OutNetMessage;
import net.i2p.router.RouterContext;
import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
import net.i2p.util.Addresses;
import net.i2p.util.Log;
@@ -26,10 +26,10 @@ class OutboundEstablishState {
private final RouterContext _context;
private final Log _log;
// SessionRequest message
private byte _sentX[];
private final byte _sentX[];
private byte _bobIP[];
private int _bobPort;
private DHSessionKeyBuilder _keyBuilder;
private final DHSessionKeyBuilder _keyBuilder;
// SessionCreated message
private byte _receivedY[];
private byte _aliceIP[];
@@ -73,7 +73,8 @@ class OutboundEstablishState {
public static final int STATE_PENDING_INTRO = 5;
public OutboundEstablishState(RouterContext ctx, InetAddress remoteHost, int remotePort,
RouterIdentity remotePeer, SessionKey introKey, UDPAddress addr) {
RouterIdentity remotePeer, SessionKey introKey, UDPAddress addr,
DHSessionKeyBuilder dh) {
_context = ctx;
_log = ctx.logManager().getLog(OutboundEstablishState.class);
if ( (remoteHost != null) && (remotePort > 0) ) {
@@ -92,6 +93,8 @@ class OutboundEstablishState {
_establishBegin = ctx.clock().now();
_remoteAddress = addr;
_introductionNonce = -1;
_keyBuilder = dh;
_sentX = new byte[UDPPacketReader.SessionRequestReader.X_LENGTH];
prepareSessionRequest();
if ( (addr != null) && (addr.getIntroducerCount() > 0) ) {
if (_log.shouldLog(Log.DEBUG))
@@ -128,10 +131,7 @@ class OutboundEstablishState {
/** called from constructor, no need to synch */
private void prepareSessionRequest() {
_keyBuilder = new DHSessionKeyBuilder();
byte X[] = _keyBuilder.getMyPublicValue().toByteArray();
if (_sentX == null)
_sentX = new byte[UDPPacketReader.SessionRequestReader.X_LENGTH];
if (X.length == 257)
System.arraycopy(X, 1, _sentX, 0, _sentX.length);
else if (X.length == 256)

View File

@@ -34,6 +34,7 @@ import net.i2p.router.RouterContext;
import net.i2p.router.transport.Transport;
import net.i2p.router.transport.TransportBid;
import net.i2p.router.transport.TransportImpl;
import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
import net.i2p.router.util.RandomIterator;
import net.i2p.util.Addresses;
import net.i2p.util.ConcurrentHashSet;
@@ -43,7 +44,7 @@ import net.i2p.util.SimpleTimer;
import net.i2p.util.Translate;
/**
*
* The SSU transport
*/
public class UDPTransport extends TransportImpl implements TimedWeightedPriorityMessageQueue.FailedListener {
private final Log _log;
@@ -70,6 +71,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
private long _reachabilityStatusLastUpdated;
private long _introducersSelectedOn;
private long _lastInboundReceivedOn;
private final DHSessionKeyBuilder.Factory _dhFactory;
/** do we need to rebuild our external router address asap? */
private boolean _needsRebuild;
@@ -178,8 +180,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
//private static final String THINSP = "&thinsp;/&thinsp;";
private static final String THINSP = " / ";
public UDPTransport(RouterContext ctx) {
public UDPTransport(RouterContext ctx, DHSessionKeyBuilder.Factory dh) {
super(ctx);
_dhFactory = dh;
_log = ctx.logManager().getLog(UDPTransport.class);
_peersByIdent = new ConcurrentHashMap(128);
_peersByRemoteHost = new ConcurrentHashMap(128);
@@ -227,7 +230,6 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
_context.statManager().createRateStat("udp.proactiveReestablish", "How long a session was idle for when we proactively reestablished it", "udp", RATES);
_context.statManager().createRateStat("udp.dropPeerDroplist", "How many peers currently have their packets dropped outright when a new peer is added to the list?", "udp", RATES);
_context.statManager().createRateStat("udp.dropPeerConsecutiveFailures", "How many consecutive failed sends to a peer did we attempt before giving up and reestablishing a new session (lifetime is inactivity perood)", "udp", RATES);
__instance = this;
SimpleScheduler.getInstance().addPeriodicEvent(new PingIntroducers(), MIN_EXPIRE_TIMEOUT * 3 / 4);
}
@@ -1623,24 +1625,13 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
return skews;
}
private static UDPTransport __instance;
/** **internal, do not use** */
public static final UDPTransport _instance() { return __instance; }
/** **internal, do not use** return the peers (Hash) of active peers. */
public List<Hash> _getActivePeers() {
List<Hash> peers = new ArrayList(128);
peers.addAll(_peersByIdent.keySet());
long now = _context.clock().now();
for (Iterator<Hash> iter = peers.iterator(); iter.hasNext(); ) {
Hash peer = iter.next();
PeerState state = getPeerState(peer);
if (now-state.getLastReceiveTime() > 5*60*1000)
iter.remove(); // don't include old peers
}
return peers;
/**
* @since 0.9
*/
DHSessionKeyBuilder getDHBuilder() {
return _dhFactory.getBuilder();
}
private static final int FLAG_ALPHA = 0;
private static final int FLAG_IDLE_IN = 1;
private static final int FLAG_IDLE_OUT = 2;