fix up DH like YK

This commit is contained in:
zzz
2010-12-15 22:59:35 +00:00
parent 4c9558c586
commit fe575a38aa

View File

@ -13,8 +13,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList; import java.util.concurrent.LinkedBlockingQueue;
import java.util.List;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.I2PException; import net.i2p.I2PException;
@ -48,14 +47,15 @@ import net.i2p.util.RandomSource;
* @author jrandom * @author jrandom
*/ */
public class DHSessionKeyBuilder { public class DHSessionKeyBuilder {
private static I2PAppContext _context = I2PAppContext.getGlobalContext(); private static final I2PAppContext _context = I2PAppContext.getGlobalContext();
private final static Log _log = new Log(DHSessionKeyBuilder.class); private static final Log _log;
private static int MIN_NUM_BUILDERS = -1; private static final int MIN_NUM_BUILDERS;
private static int MAX_NUM_BUILDERS = -1; private static final int MAX_NUM_BUILDERS;
private static int CALC_DELAY = -1; private static final int CALC_DELAY;
/* FIXME this should be final if you syncronize FIXME */ private static final LinkedBlockingQueue<DHSessionKeyBuilder> _builders;
private static volatile List _builders = new ArrayList(50); private static final Thread _precalcThread;
private static Thread _precalcThread = null;
// the data of importance
private BigInteger _myPrivateValue; private BigInteger _myPrivateValue;
private BigInteger _myPublicValue; private BigInteger _myPublicValue;
private BigInteger _peerValue; 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_MIN = "crypto.dh.precalc.min";
public final static String PROP_DH_PRECALC_MAX = "crypto.dh.precalc.max"; public final static String PROP_DH_PRECALC_MAX = "crypto.dh.precalc.max";
public final static String PROP_DH_PRECALC_DELAY = "crypto.dh.precalc.delay"; public final static String PROP_DH_PRECALC_DELAY = "crypto.dh.precalc.delay";
public final static int DEFAULT_DH_PRECALC_MIN = 5; public final static int DEFAULT_DH_PRECALC_MIN = 15;
public final static int DEFAULT_DH_PRECALC_MAX = 50; public final static int DEFAULT_DH_PRECALC_MAX = 40;
public final static int DEFAULT_DH_PRECALC_DELAY = 10000; 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 { static {
I2PAppContext ctx = _context; 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 }); _log = ctx.logManager().getLog(DHSessionKeyBuilder.class);
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 }); ctx.statManager().createRateStat("crypto.dhGeneratePublicTime", "How long it takes to create x and X", "Encryption", new long[] { 60*60*1000 });
MIN_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MIN, DEFAULT_DH_PRECALC_MIN); ctx.statManager().createRateStat("crypto.dhCalculateSessionTime", "How long it takes to create the session key", "Encryption", new long[] { 60*60*1000 });
MAX_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MAX, DEFAULT_DH_PRECALC_MAX); 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); CALC_DELAY = ctx.getProperty(PROP_DH_PRECALC_DELAY, DEFAULT_DH_PRECALC_DELAY);
_builders = new LinkedBlockingQueue(MAX_NUM_BUILDERS);
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("DH Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: " _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 * Construct a new DH key builder
* * or pulls a prebuilt one from the queue.
*/ */
public DHSessionKeyBuilder() { public DHSessionKeyBuilder() {
this(false); _context.statManager().addRateData("crypto.DHUsed", 1, 0);
DHSessionKeyBuilder builder = null; DHSessionKeyBuilder builder = _builders.poll();
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");
}
}
if (builder != null) { if (builder != null) {
if (_log.shouldLog(Log.DEBUG)) _log.debug("Removing a builder. # left = " + _builders.size());
_myPrivateValue = builder._myPrivateValue; _myPrivateValue = builder._myPrivateValue;
_myPublicValue = builder._myPublicValue; _myPublicValue = builder._myPublicValue;
_peerValue = builder._peerValue; // these two are still null after precalc
_sessionKey = builder._sessionKey; //_peerValue = builder._peerValue;
//_sessionKey = builder._sessionKey;
_extraExchangedBytes = builder._extraExchangedBytes; _extraExchangedBytes = builder._extraExchangedBytes;
} else { } else {
_myPrivateValue = null; if (_log.shouldLog(Log.INFO)) _log.info("No more builders, creating one now");
_myPublicValue = null; _context.statManager().addRateData("crypto.DHEmpty", 1, 0);
_peerValue = null; // sets _myPrivateValue as a side effect
_sessionKey = null;
_myPublicValue = generateMyValue(); _myPublicValue = generateMyValue();
_extraExchangedBytes = new ByteArray(); _extraExchangedBytes = new ByteArray();
} }
} }
public DHSessionKeyBuilder(boolean usePool) { /**
_myPrivateValue = null; * Only for internal use
_myPublicValue = null; * @parameter usePool unused, just to make it different from other constructor
_peerValue = null; */
_sessionKey = null; private DHSessionKeyBuilder(boolean usePool) {
_extraExchangedBytes = new ByteArray(); _extraExchangedBytes = new ByteArray();
} }
@ -189,18 +196,12 @@ public class DHSessionKeyBuilder {
} }
private static final int getSize() { private static final int getSize() {
synchronized (_builders) {
return _builders.size(); return _builders.size();
} }
}
private static final int addBuilder(DHSessionKeyBuilder builder) { /** @return true if successful, false if full */
int sz = 0; private static final boolean addBuilder(DHSessionKeyBuilder builder) {
synchronized (_builders) { return _builders.offer(builder);
_builders.add(builder);
sz = _builders.size();
}
return sz;
} }
/** /**
@ -210,7 +211,7 @@ public class DHSessionKeyBuilder {
*/ */
public BigInteger generateMyValue() { public BigInteger generateMyValue() {
long start = System.currentTimeMillis(); 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); BigInteger myValue = CryptoConstants.elgg.modPow(_myPrivateValue, CryptoConstants.elgp);
long end = System.currentTimeMillis(); long end = System.currentTimeMillis();
long diff = end - start; 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), * If there aren't enough bytes (with all of them being consumed by the 32 byte key),
* the SHA256 of the key itself is used. * the SHA256 of the key itself is used.
* *
* @return non-null (but rv.getData() may be null)
*/ */
public ByteArray getExtraBytes() { public ByteArray getExtraBytes() {
return _extraExchangedBytes; return _extraExchangedBytes;
@ -406,6 +408,7 @@ public class DHSessionKeyBuilder {
} }
*/ */
/******
public static void main(String args[]) { public static void main(String args[]) {
//if (true) { testValidation(); return; } //if (true) { testValidation(); return; }
@ -419,7 +422,7 @@ public class DHSessionKeyBuilder {
long negTime = 0; long negTime = 0;
try { try {
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
long startNeg = Clock.getInstance().now(); long startNeg = System.currentTimeMillis();
DHSessionKeyBuilder builder1 = new DHSessionKeyBuilder(); DHSessionKeyBuilder builder1 = new DHSessionKeyBuilder();
DHSessionKeyBuilder builder2 = new DHSessionKeyBuilder(); DHSessionKeyBuilder builder2 = new DHSessionKeyBuilder();
BigInteger pub1 = builder1.getMyPublicValue(); BigInteger pub1 = builder1.getMyPublicValue();
@ -428,7 +431,7 @@ public class DHSessionKeyBuilder {
builder1.setPeerPublicValue(pub2); builder1.setPeerPublicValue(pub2);
SessionKey key1 = builder1.getSessionKey(); SessionKey key1 = builder1.getSessionKey();
SessionKey key2 = builder2.getSessionKey(); SessionKey key2 = builder2.getSessionKey();
long endNeg = Clock.getInstance().now(); long endNeg = System.currentTimeMillis();
negTime += endNeg - startNeg; negTime += endNeg - startNeg;
if (!key1.equals(key2)) if (!key1.equals(key2))
@ -458,10 +461,11 @@ public class DHSessionKeyBuilder {
} catch (InterruptedException ie) { // nop } catch (InterruptedException ie) { // nop
} }
} }
******/
private static class DHSessionKeyBuilderPrecalcRunner implements Runnable { private static class DHSessionKeyBuilderPrecalcRunner implements Runnable {
private int _minSize; private final int _minSize;
private int _maxSize; private final int _maxSize;
private DHSessionKeyBuilderPrecalcRunner(int minSize, int maxSize) { private DHSessionKeyBuilderPrecalcRunner(int minSize, int maxSize) {
_minSize = minSize; _minSize = minSize;
@ -472,22 +476,28 @@ public class DHSessionKeyBuilder {
while (true) { while (true) {
int curSize = 0; int curSize = 0;
long start = Clock.getInstance().now(); long start = System.currentTimeMillis();
int startSize = getSize(); 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; curSize = startSize;
while (curSize < _minSize) { if (curSize < _minSize) {
while (curSize < _maxSize) { for (int i = curSize; i < _maxSize; i++) {
long curStart = System.currentTimeMillis(); long curStart = System.currentTimeMillis();
curSize = addBuilder(precalc(curSize)); if (!addBuilder(precalc()))
break;
long curCalc = System.currentTimeMillis() - curStart; long curCalc = System.currentTimeMillis() - curStart;
// for some relief... // for some relief...
try { try {
Thread.sleep(CALC_DELAY + curCalc * 10); Thread.sleep(CALC_DELAY + (curCalc * 3));
} catch (InterruptedException ie) { // nop } catch (InterruptedException ie) { // nop
} }
} }
} }
long end = Clock.getInstance().now(); long end = System.currentTimeMillis();
int numCalc = curSize - startSize; int numCalc = curSize - startSize;
if (numCalc > 0) { if (numCalc > 0) {
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
@ -496,16 +506,15 @@ public class DHSessionKeyBuilder {
+ (CALC_DELAY * numCalc) + "ms relief). now sleeping"); + (CALC_DELAY * numCalc) + "ms relief). now sleeping");
} }
try { try {
Thread.sleep(30 * 1000); Thread.sleep(_checkDelay);
} catch (InterruptedException ie) { // nop } catch (InterruptedException ie) { // nop
} }
} }
} }
private DHSessionKeyBuilder precalc(int i) { private static DHSessionKeyBuilder precalc() {
DHSessionKeyBuilder builder = new DHSessionKeyBuilder(false); DHSessionKeyBuilder builder = new DHSessionKeyBuilder(false);
builder.getMyPublicValue(); builder.getMyPublicValue();
//_log.debug("Precalc " + i + " complete");
return builder; return builder;
} }
} }