diff --git a/core/java/src/net/i2p/crypto/DHSessionKeyBuilder.java b/core/java/src/net/i2p/crypto/DHSessionKeyBuilder.java index b438f2b471df9025e9de49b2cbc229ba5d92ed12..192bde062920e187cb639320e498a6756f5934de 100644 --- a/core/java/src/net/i2p/crypto/DHSessionKeyBuilder.java +++ b/core/java/src/net/i2p/crypto/DHSessionKeyBuilder.java @@ -13,8 +13,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.math.BigInteger; -import java.util.ArrayList; -import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; import net.i2p.I2PAppContext; import net.i2p.I2PException; @@ -48,14 +47,15 @@ import net.i2p.util.RandomSource; * @author jrandom */ public class DHSessionKeyBuilder { - private static I2PAppContext _context = I2PAppContext.getGlobalContext(); - private final static Log _log = new Log(DHSessionKeyBuilder.class); - private static int MIN_NUM_BUILDERS = -1; - private static int MAX_NUM_BUILDERS = -1; - private static int CALC_DELAY = -1; - /* FIXME this should be final if you syncronize FIXME */ - private static volatile List _builders = new ArrayList(50); - private static Thread _precalcThread = null; + private static final I2PAppContext _context = I2PAppContext.getGlobalContext(); + private static final Log _log; + private static final int MIN_NUM_BUILDERS; + private static final int MAX_NUM_BUILDERS; + private static final int CALC_DELAY; + private static final LinkedBlockingQueue<DHSessionKeyBuilder> _builders; + private static final Thread _precalcThread; + + // the data of importance private BigInteger _myPrivateValue; private BigInteger _myPublicValue; private BigInteger _peerValue; @@ -65,17 +65,31 @@ public class DHSessionKeyBuilder { public final static String PROP_DH_PRECALC_MIN = "crypto.dh.precalc.min"; public final static String PROP_DH_PRECALC_MAX = "crypto.dh.precalc.max"; public final static String PROP_DH_PRECALC_DELAY = "crypto.dh.precalc.delay"; - public final static int DEFAULT_DH_PRECALC_MIN = 5; - public final static int DEFAULT_DH_PRECALC_MAX = 50; - public final static int DEFAULT_DH_PRECALC_DELAY = 10000; + public final static int DEFAULT_DH_PRECALC_MIN = 15; + public final static int DEFAULT_DH_PRECALC_MAX = 40; + public final static int DEFAULT_DH_PRECALC_DELAY = 200; + + /** check every 30 seconds whether we have less than the minimum */ + private static long _checkDelay = 30 * 1000; static { I2PAppContext ctx = _context; - ctx.statManager().createRateStat("crypto.dhGeneratePublicTime", "How long it takes to create x and X", "Encryption", new long[] { 60*1000, 5*60*1000, 60*60*1000 }); - ctx.statManager().createRateStat("crypto.dhCalculateSessionTime", "How long it takes to create the session key", "Encryption", new long[] { 60*1000, 5*60*1000, 60*60*1000 }); - MIN_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MIN, DEFAULT_DH_PRECALC_MIN); - MAX_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MAX, DEFAULT_DH_PRECALC_MAX); + _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(); + int factor = Math.min(4, (int) (1 + (maxMemory / (128*1024*1024l)))); + int defaultMin = DEFAULT_DH_PRECALC_MIN * factor; + int defaultMax = DEFAULT_DH_PRECALC_MAX * factor; + MIN_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MIN, defaultMin); + MAX_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MAX, defaultMax); + CALC_DELAY = ctx.getProperty(PROP_DH_PRECALC_DELAY, DEFAULT_DH_PRECALC_DELAY); + _builders = new LinkedBlockingQueue(MAX_NUM_BUILDERS); if (_log.shouldLog(Log.DEBUG)) _log.debug("DH Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: " @@ -90,40 +104,33 @@ public class DHSessionKeyBuilder { /** * Construct a new DH key builder - * + * or pulls a prebuilt one from the queue. */ public DHSessionKeyBuilder() { - this(false); - DHSessionKeyBuilder builder = null; - synchronized (_builders) { - if (!_builders.isEmpty()) { - builder = (DHSessionKeyBuilder) _builders.remove(0); - if (_log.shouldLog(Log.DEBUG)) _log.debug("Removing a builder. # left = " + _builders.size()); - } else { - if (_log.shouldLog(Log.WARN)) _log.warn("NO MORE BUILDERS! creating one now"); - } - } + _context.statManager().addRateData("crypto.DHUsed", 1, 0); + DHSessionKeyBuilder builder = _builders.poll(); if (builder != null) { + if (_log.shouldLog(Log.DEBUG)) _log.debug("Removing a builder. # left = " + _builders.size()); _myPrivateValue = builder._myPrivateValue; _myPublicValue = builder._myPublicValue; - _peerValue = builder._peerValue; - _sessionKey = builder._sessionKey; + // these two are still null after precalc + //_peerValue = builder._peerValue; + //_sessionKey = builder._sessionKey; _extraExchangedBytes = builder._extraExchangedBytes; } else { - _myPrivateValue = null; - _myPublicValue = null; - _peerValue = null; - _sessionKey = null; + if (_log.shouldLog(Log.INFO)) _log.info("No more builders, creating one now"); + _context.statManager().addRateData("crypto.DHEmpty", 1, 0); + // sets _myPrivateValue as a side effect _myPublicValue = generateMyValue(); _extraExchangedBytes = new ByteArray(); } } - public DHSessionKeyBuilder(boolean usePool) { - _myPrivateValue = null; - _myPublicValue = null; - _peerValue = null; - _sessionKey = null; + /** + * Only for internal use + * @parameter usePool unused, just to make it different from other constructor + */ + private DHSessionKeyBuilder(boolean usePool) { _extraExchangedBytes = new ByteArray(); } @@ -189,18 +196,12 @@ public class DHSessionKeyBuilder { } private static final int getSize() { - synchronized (_builders) { return _builders.size(); - } } - private static final int addBuilder(DHSessionKeyBuilder builder) { - int sz = 0; - synchronized (_builders) { - _builders.add(builder); - sz = _builders.size(); - } - return sz; + /** @return true if successful, false if full */ + private static final boolean addBuilder(DHSessionKeyBuilder builder) { + return _builders.offer(builder); } /** @@ -210,7 +211,7 @@ public class DHSessionKeyBuilder { */ public BigInteger generateMyValue() { long start = System.currentTimeMillis(); - _myPrivateValue = new NativeBigInteger(KeyGenerator.PUBKEY_EXPONENT_SIZE, RandomSource.getInstance()); + _myPrivateValue = new NativeBigInteger(KeyGenerator.PUBKEY_EXPONENT_SIZE, _context.random()); BigInteger myValue = CryptoConstants.elgg.modPow(_myPrivateValue, CryptoConstants.elgp); long end = System.currentTimeMillis(); long diff = end - start; @@ -314,6 +315,7 @@ public class DHSessionKeyBuilder { * 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; @@ -406,6 +408,7 @@ public class DHSessionKeyBuilder { } */ +/****** public static void main(String args[]) { //if (true) { testValidation(); return; } @@ -419,7 +422,7 @@ public class DHSessionKeyBuilder { long negTime = 0; try { for (int i = 0; i < 5; i++) { - long startNeg = Clock.getInstance().now(); + long startNeg = System.currentTimeMillis(); DHSessionKeyBuilder builder1 = new DHSessionKeyBuilder(); DHSessionKeyBuilder builder2 = new DHSessionKeyBuilder(); BigInteger pub1 = builder1.getMyPublicValue(); @@ -428,7 +431,7 @@ public class DHSessionKeyBuilder { builder1.setPeerPublicValue(pub2); SessionKey key1 = builder1.getSessionKey(); SessionKey key2 = builder2.getSessionKey(); - long endNeg = Clock.getInstance().now(); + long endNeg = System.currentTimeMillis(); negTime += endNeg - startNeg; if (!key1.equals(key2)) @@ -458,10 +461,11 @@ public class DHSessionKeyBuilder { } catch (InterruptedException ie) { // nop } } +******/ private static class DHSessionKeyBuilderPrecalcRunner implements Runnable { - private int _minSize; - private int _maxSize; + private final int _minSize; + private final int _maxSize; private DHSessionKeyBuilderPrecalcRunner(int minSize, int maxSize) { _minSize = minSize; @@ -472,22 +476,28 @@ public class DHSessionKeyBuilder { while (true) { int curSize = 0; - long start = Clock.getInstance().now(); + 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; curSize = startSize; - while (curSize < _minSize) { - while (curSize < _maxSize) { + if (curSize < _minSize) { + for (int i = curSize; i < _maxSize; i++) { long curStart = System.currentTimeMillis(); - curSize = addBuilder(precalc(curSize)); + if (!addBuilder(precalc())) + break; long curCalc = System.currentTimeMillis() - curStart; // for some relief... try { - Thread.sleep(CALC_DELAY + curCalc * 10); + Thread.sleep(CALC_DELAY + (curCalc * 3)); } catch (InterruptedException ie) { // nop } } } - long end = Clock.getInstance().now(); + long end = System.currentTimeMillis(); int numCalc = curSize - startSize; if (numCalc > 0) { if (_log.shouldLog(Log.DEBUG)) @@ -496,16 +506,15 @@ public class DHSessionKeyBuilder { + (CALC_DELAY * numCalc) + "ms relief). now sleeping"); } try { - Thread.sleep(30 * 1000); + Thread.sleep(_checkDelay); } catch (InterruptedException ie) { // nop } } } - private DHSessionKeyBuilder precalc(int i) { + private static DHSessionKeyBuilder precalc() { DHSessionKeyBuilder builder = new DHSessionKeyBuilder(false); builder.getMyPublicValue(); - //_log.debug("Precalc " + i + " complete"); return builder; } }