2005-07-04 jrandom
* Within the tunnel, use xor(IV, msg[0:16]) as the flag to detect dups, rather than the IV by itself, preventing an attack that would let colluding internal adversaries tag a message to determine that they are in the same tunnel. Thanks dvorak for the catch! * Drop long inactive profiles on startup and shutdown * /configstats.jsp: web interface to pick what stats to log * Deliver more session tags to account for wider window sizes * Cache some intermediate values in our HMACSHA256 and BC's HMAC * Track the client send rate (stream.sendBps and client.sendBpsRaw) * UrlLauncher: adjust the browser selection order * I2PAppContext: hooks for dummy HMACSHA256 and a weak PRNG * StreamSinkClient: add support for sending an unlimited amount of data * Migrate the tests out of the default build jars 2005-06-22 Comwiz * Migrate the core tests to junit
This commit is contained in:
@ -10,6 +10,8 @@ import net.i2p.crypto.CryptixAESEngine;
|
||||
import net.i2p.crypto.DSAEngine;
|
||||
import net.i2p.crypto.DummyDSAEngine;
|
||||
import net.i2p.crypto.DummyElGamalEngine;
|
||||
import net.i2p.crypto.DummyHMACSHA256Generator;
|
||||
import net.i2p.crypto.DummyPooledRandomSource;
|
||||
import net.i2p.crypto.ElGamalAESEngine;
|
||||
import net.i2p.crypto.ElGamalEngine;
|
||||
import net.i2p.crypto.HMACSHA256Generator;
|
||||
@ -328,8 +330,12 @@ public class I2PAppContext {
|
||||
}
|
||||
private void initializeHMAC() {
|
||||
synchronized (this) {
|
||||
if (_hmac == null)
|
||||
_hmac= new HMACSHA256Generator(this);
|
||||
if (_hmac == null) {
|
||||
if ("true".equals(getProperty("i2p.fakeHMAC", "false")))
|
||||
_hmac = new DummyHMACSHA256Generator(this);
|
||||
else
|
||||
_hmac= new HMACSHA256Generator(this);
|
||||
}
|
||||
_hmacInitialized = true;
|
||||
}
|
||||
}
|
||||
@ -432,8 +438,12 @@ public class I2PAppContext {
|
||||
}
|
||||
private void initializeRandom() {
|
||||
synchronized (this) {
|
||||
if (_random == null)
|
||||
_random = new PooledRandomSource(this);
|
||||
if (_random == null) {
|
||||
if ("true".equals(getProperty("i2p.weakPRNG", "false")))
|
||||
_random = new DummyPooledRandomSource(this);
|
||||
else
|
||||
_random = new PooledRandomSource(this);
|
||||
}
|
||||
_randomInitialized = true;
|
||||
}
|
||||
}
|
||||
|
@ -39,9 +39,14 @@ import net.i2p.util.Log;
|
||||
class I2CPMessageProducer {
|
||||
private final static Log _log = new Log(I2CPMessageProducer.class);
|
||||
private I2PAppContext _context;
|
||||
private int _sendBps;
|
||||
private long _sendPeriodBytes;
|
||||
private long _sendPeriodBeginTime;
|
||||
|
||||
public I2CPMessageProducer(I2PAppContext context) {
|
||||
_context = context;
|
||||
_sendBps = 0;
|
||||
context.statManager().createRateStat("client.sendBpsRaw", "How fast we pump out I2CP data messages", "ClientMessages", new long[] { 60*1000, 5*60*1000, 10*60*1000, 60*60*1000 });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,8 +99,30 @@ class I2CPMessageProducer {
|
||||
Payload data = createPayload(dest, payload, tag, key, tags, newKey);
|
||||
msg.setPayload(data);
|
||||
session.sendMessage(msg);
|
||||
updateBps(payload.length);
|
||||
}
|
||||
|
||||
private void updateBps(int len) {
|
||||
long now = _context.clock().now();
|
||||
float period = ((float)now-_sendPeriodBeginTime)/1000f;
|
||||
if (period >= 1f) {
|
||||
// first term decays on slow transmission
|
||||
_sendBps = (int)(((float)0.9f * (float)_sendBps) + ((float)0.1f*((float)_sendPeriodBytes)/period));
|
||||
_sendPeriodBytes = len;
|
||||
_sendPeriodBeginTime = now;
|
||||
_context.statManager().addRateData("client.sendBpsRaw", _sendBps, 0);
|
||||
} else {
|
||||
_sendPeriodBytes += len;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we include the I2CP end to end crypto (which is in addition to any
|
||||
* garlic crypto added by the router)
|
||||
*
|
||||
*/
|
||||
static final boolean END_TO_END_CRYPTO = true;
|
||||
|
||||
/**
|
||||
* Create a new signed payload and send it off to the destination
|
||||
*
|
||||
@ -106,6 +133,10 @@ class I2CPMessageProducer {
|
||||
if (payload == null) throw new I2PSessionException("No payload specified");
|
||||
|
||||
Payload data = new Payload();
|
||||
if (!END_TO_END_CRYPTO) {
|
||||
data.setEncryptedData(payload);
|
||||
return data;
|
||||
}
|
||||
// no padding at this level
|
||||
// the garlic may pad, and the tunnels may pad, and the transports may pad
|
||||
int size = payload.length;
|
||||
|
@ -131,15 +131,15 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
long availTimeLeft = _context.sessionKeyManager().getAvailableTimeLeft(dest.getPublicKey(), key);
|
||||
|
||||
if ( (tagsSent == null) || (tagsSent.size() <= 0) ) {
|
||||
if (oldTags < 10) {
|
||||
if (oldTags < NUM_TAGS) {
|
||||
sentTags = createNewTags(NUM_TAGS);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("** sendBestEffort only had " + oldTags + " with " + availTimeLeft + ", adding " + NUM_TAGS);
|
||||
_log.debug("** sendBestEffort only had " + oldTags + " with " + availTimeLeft + ", adding " + NUM_TAGS + ": " + sentTags);
|
||||
} else if (availTimeLeft < 2 * 60 * 1000) {
|
||||
// if we have > 10 tags, but they expire in under 2 minutes, we want more
|
||||
// if we have > 50 tags, but they expire in under 2 minutes, we want more
|
||||
sentTags = createNewTags(NUM_TAGS);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Tags expiring in " + availTimeLeft + ", adding " + NUM_TAGS + " new ones");
|
||||
_log.debug(getPrefix() + "Tags expiring in " + availTimeLeft + ", adding " + NUM_TAGS + " new ones: " + sentTags);
|
||||
//_log.error("** sendBestEffort available time left " + availTimeLeft);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
|
@ -55,6 +55,11 @@ class MessagePayloadMessageHandler extends HandlerImpl {
|
||||
*/
|
||||
private Payload decryptPayload(MessagePayloadMessage msg, I2PSessionImpl session) throws DataFormatException {
|
||||
Payload payload = msg.getPayload();
|
||||
if (!I2CPMessageProducer.END_TO_END_CRYPTO) {
|
||||
payload.setUnencryptedData(payload.getEncryptedData());
|
||||
return payload;
|
||||
}
|
||||
|
||||
byte[] data = _context.elGamalAESEngine().decrypt(payload.getEncryptedData(), session.getDecryptionKey());
|
||||
if (data == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
|
@ -159,7 +159,6 @@ public class AESEngine {
|
||||
System.arraycopy(payload, inIndex, rv, outIndex, rv.length - outIndex);
|
||||
}
|
||||
|
||||
|
||||
public static void main(String args[]) {
|
||||
I2PAppContext ctx = new I2PAppContext();
|
||||
SessionKey key = ctx.keyGenerator().generateSessionKey();
|
||||
|
52
core/java/src/net/i2p/crypto/DummyHMACSHA256Generator.java
Normal file
52
core/java/src/net/i2p/crypto/DummyHMACSHA256Generator.java
Normal file
@ -0,0 +1,52 @@
|
||||
package net.i2p.crypto;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.SessionKey;
|
||||
|
||||
/**
|
||||
* Calculate the HMAC-SHA256 of a key+message. All the good stuff occurs
|
||||
* in {@link org.bouncycastle.crypto.macs.HMac} and
|
||||
* {@link org.bouncycastle.crypto.digests.SHA256Digest}.
|
||||
*
|
||||
*/
|
||||
public class DummyHMACSHA256Generator extends HMACSHA256Generator {
|
||||
private I2PAppContext _context;
|
||||
public DummyHMACSHA256Generator(I2PAppContext context) {
|
||||
super(context);
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public static HMACSHA256Generator getInstance() {
|
||||
return I2PAppContext.getGlobalContext().hmac();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the HMAC of the data with the given key
|
||||
*/
|
||||
public Hash calculate(SessionKey key, byte data[]) {
|
||||
if ((key == null) || (key.getData() == null) || (data == null))
|
||||
throw new NullPointerException("Null arguments for HMAC");
|
||||
return calculate(key, data, 0, data.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the HMAC of the data with the given key
|
||||
*/
|
||||
public Hash calculate(SessionKey key, byte data[], int offset, int length) {
|
||||
if ((key == null) || (key.getData() == null) || (data == null))
|
||||
throw new NullPointerException("Null arguments for HMAC");
|
||||
|
||||
byte rv[] = new byte[Hash.HASH_LENGTH];
|
||||
System.arraycopy(key.getData(), 0, rv, 0, Hash.HASH_LENGTH);
|
||||
if (Hash.HASH_LENGTH >= length)
|
||||
DataHelper.xor(data, offset, rv, 0, rv, 0, length);
|
||||
else
|
||||
DataHelper.xor(data, offset, rv, 0, rv, 0, Hash.HASH_LENGTH);
|
||||
return new Hash(rv);
|
||||
}
|
||||
}
|
94
core/java/src/net/i2p/crypto/DummyPooledRandomSource.java
Normal file
94
core/java/src/net/i2p/crypto/DummyPooledRandomSource.java
Normal file
@ -0,0 +1,94 @@
|
||||
package net.i2p.crypto;
|
||||
|
||||
import java.util.Random;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.PooledRandomSource;
|
||||
import net.i2p.util.RandomSource;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class DummyPooledRandomSource extends PooledRandomSource {
|
||||
public DummyPooledRandomSource(I2PAppContext context) {
|
||||
super(context);
|
||||
_pool = new RandomSource[POOL_SIZE];
|
||||
for (int i = 0; i < POOL_SIZE; i++) {
|
||||
_pool[i] = new DummyRandomSource(context);
|
||||
_pool[i].nextBoolean();
|
||||
}
|
||||
_nextPool = 0;
|
||||
}
|
||||
|
||||
private class DummyRandomSource extends RandomSource {
|
||||
private Random _prng;
|
||||
public DummyRandomSource(I2PAppContext context) {
|
||||
super(context);
|
||||
// when we replace to have hooks for fortuna (etc), replace with
|
||||
// a factory (or just a factory method)
|
||||
_prng = new Random();
|
||||
}
|
||||
|
||||
/**
|
||||
* According to the java docs (http://java.sun.com/j2se/1.4.1/docs/api/java/util/Random.html#nextInt(int))
|
||||
* nextInt(n) should return a number between 0 and n (including 0 and excluding n). However, their pseudocode,
|
||||
* as well as sun's, kaffe's, and classpath's implementation INCLUDES NEGATIVE VALUES.
|
||||
* WTF. Ok, so we're going to have it return between 0 and n (including 0, excluding n), since
|
||||
* thats what it has been used for.
|
||||
*
|
||||
*/
|
||||
public int nextInt(int n) {
|
||||
if (n == 0) return 0;
|
||||
int val = _prng.nextInt(n);
|
||||
if (val < 0) val = 0 - val;
|
||||
if (val >= n) val = val % n;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like the modified nextInt, nextLong(n) returns a random number from 0 through n,
|
||||
* including 0, excluding n.
|
||||
*/
|
||||
public long nextLong(long n) {
|
||||
long v = _prng.nextLong();
|
||||
if (v < 0) v = 0 - v;
|
||||
if (v >= n) v = v % n;
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public boolean nextBoolean() { return _prng.nextBoolean(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public void nextBytes(byte buf[]) { _prng.nextBytes(buf); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public double nextDouble() { return _prng.nextDouble(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public float nextFloat() { return _prng.nextFloat(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public double nextGaussian() { return _prng.nextGaussian(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public int nextInt() { return _prng.nextInt(); }
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public long nextLong() { return _prng.nextLong(); }
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
@ -52,7 +53,7 @@ public class ElGamalAESEngine {
|
||||
_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",
|
||||
_context.statManager().createFrequencyStat("crypto.elGamalAES.decryptFailed",
|
||||
"how frequently we fail to decrypt with ElGamal/AES+SessionTag?", "Encryption",
|
||||
new long[] { 60 * 60 * 1000l, 24 * 60 * 60 * 1000l});
|
||||
}
|
||||
@ -82,20 +83,35 @@ public class ElGamalAESEngine {
|
||||
Set foundTags = new HashSet();
|
||||
byte decrypted[] = null;
|
||||
if (key != null) {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Key is known for tag " + st);
|
||||
//if (_log.shouldLog(Log.DEBUG)) _log.debug("Key is known for tag " + st);
|
||||
usedKey.setData(key.getData());
|
||||
long id = _context.random().nextLong();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(id + ": Decrypting existing session encrypted with tag: " + st.toString() + ": key: " + key.toBase64() + ": " + data.length + " bytes: " + Base64.encode(data, 0, 64));
|
||||
|
||||
decrypted = decryptExistingSession(data, key, targetPrivateKey, foundTags, usedKey, foundKey);
|
||||
if (decrypted != null)
|
||||
if (decrypted != null) {
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.decryptExistingSession");
|
||||
else
|
||||
if ( (foundTags.size() > 0) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn(id + ": ElG/AES decrypt success with " + st + ": found tags: " + foundTags);
|
||||
} else {
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.decryptFailed");
|
||||
if (_log.shouldLog(Log.ERROR)) {
|
||||
_log.error(id + ": ElG decrypt fail: known tag [" + st + "], failed decrypt");
|
||||
}
|
||||
}
|
||||
} 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)
|
||||
if (decrypted != null) {
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.decryptNewSession");
|
||||
else
|
||||
if ( (foundTags.size() > 0) && (_log.shouldLog(Log.WARN)) )
|
||||
_log.warn("ElG decrypt success: found tags: " + foundTags);
|
||||
} else {
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.decryptFailed");
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("ElG decrypt fail: unknown tag: " + st);
|
||||
}
|
||||
}
|
||||
|
||||
if ((key == null) && (decrypted == null)) {
|
||||
@ -104,10 +120,12 @@ public class ElGamalAESEngine {
|
||||
|
||||
if (foundTags.size() > 0) {
|
||||
if (foundKey.getData() != null) {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Found key: " + foundKey);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Found key: " + foundKey.toBase64() + " tags: " + foundTags);
|
||||
_context.sessionKeyManager().tagsReceived(foundKey, foundTags);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Used key: " + usedKey);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Used key: " + usedKey.toBase64() + " tags: " + foundTags);
|
||||
_context.sessionKeyManager().tagsReceived(usedKey, foundTags);
|
||||
}
|
||||
}
|
||||
@ -131,10 +149,10 @@ public class ElGamalAESEngine {
|
||||
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");
|
||||
//if (_log.shouldLog(Log.WARN)) _log.warn("Data is null, unable to decrypt new session");
|
||||
return null;
|
||||
} else if (data.length < 514) {
|
||||
if (_log.shouldLog(Log.WARN)) _log.warn("Data length is too small (" + data.length + ")");
|
||||
//if (_log.shouldLog(Log.WARN)) _log.warn("Data length is too small (" + data.length + ")");
|
||||
return null;
|
||||
}
|
||||
byte elgEncr[] = new byte[514];
|
||||
@ -145,8 +163,8 @@ public class ElGamalAESEngine {
|
||||
}
|
||||
byte elgDecr[] = _context.elGamalEngine().decrypt(elgEncr, targetPrivateKey);
|
||||
if (elgDecr == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("decrypt returned null", new Exception("decrypt failed"));
|
||||
//if (_log.shouldLog(Log.WARN))
|
||||
// _log.warn("decrypt returned null", new Exception("decrypt failed"));
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -172,9 +190,9 @@ public class ElGamalAESEngine {
|
||||
|
||||
byte aesDecr[] = decryptAESBlock(data, 514, data.length-514, usedKey, iv, null, foundTags, foundKey);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Decrypt with a NEW session successfull: # tags read = " + foundTags.size(),
|
||||
new Exception("Decrypted by"));
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Decrypt with a NEW session successfull: # tags read = " + foundTags.size(),
|
||||
// new Exception("Decrypted by"));
|
||||
return aesDecr;
|
||||
}
|
||||
|
||||
@ -211,14 +229,23 @@ public class ElGamalAESEngine {
|
||||
byte decrypted[] = decryptAESBlock(data, 32, data.length-32, key, iv, preIV, foundTags, foundKey);
|
||||
if (decrypted == null) {
|
||||
// it begins with a valid session tag, but thats just a coincidence.
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Decrypt with a non session tag, but tags read: " + foundTags.size());
|
||||
return decryptNewSession(data, targetPrivateKey, foundTags, usedKey, foundKey);
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Decrypt with a non session tag, but tags read: " + foundTags.size());
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Decrypting looks negative... existing key fails with existing tag, lets try as a new one");
|
||||
byte rv[] = decryptNewSession(data, targetPrivateKey, foundTags, usedKey, foundKey);
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
if (rv == null)
|
||||
_log.warn("Decrypting failed with a known existing tag as either an existing message or a new session");
|
||||
else
|
||||
_log.warn("Decrypting suceeded as a new session, even though it used an existing tag!");
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
// existing session decrypted successfully!
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Decrypt with an EXISTING session tag successfull, # tags read: " + foundTags.size(),
|
||||
new Exception("Decrypted by"));
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Decrypt with an EXISTING session tag successfull, # tags read: " + foundTags.size(),
|
||||
// new Exception("Decrypted by"));
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
@ -261,7 +288,7 @@ public class ElGamalAESEngine {
|
||||
long numTags = DataHelper.fromLong(decrypted, cur, 2);
|
||||
cur += 2;
|
||||
//_log.debug("# tags: " + numTags);
|
||||
if ((numTags < 0) || (numTags > 65535)) throw new Exception("Invalid number of session tags");
|
||||
if ((numTags < 0) || (numTags > 200)) throw new Exception("Invalid number of session tags");
|
||||
if (numTags * SessionTag.BYTE_LENGTH > decrypted.length - 2) {
|
||||
throw new Exception("# tags: " + numTags + " is too many for " + (decrypted.length - 2));
|
||||
}
|
||||
@ -333,7 +360,10 @@ public class ElGamalAESEngine {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Current tag is NOT null, encrypting as existing session", new Exception("encrypt existing"));
|
||||
_context.statManager().updateFrequency("crypto.elGamalAES.encryptExistingSession");
|
||||
return encryptExistingSession(data, target, key, tagsForDelivery, currentTag, newKey, paddedSize);
|
||||
byte rv[] = encryptExistingSession(data, target, key, tagsForDelivery, currentTag, newKey, paddedSize);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Existing session encrypted with tag: " + currentTag.toString() + ": " + rv.length + " bytes and key: " + key.toBase64() + ": " + Base64.encode(rv, 0, 64));
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -396,7 +426,7 @@ public class ElGamalAESEngine {
|
||||
if (elgEncr.length < 514) {
|
||||
byte elg[] = new byte[514];
|
||||
int diff = elg.length - elgEncr.length;
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("Difference in size: " + diff);
|
||||
//if (_log.shouldLog(Log.DEBUG)) _log.debug("Difference in size: " + diff);
|
||||
System.arraycopy(elgEncr, 0, elg, diff, elgEncr.length);
|
||||
elgEncr = elg;
|
||||
}
|
||||
@ -415,8 +445,8 @@ public class ElGamalAESEngine {
|
||||
System.arraycopy(aesEncr, 0, rv, elgEncr.length, aesEncr.length);
|
||||
//_log.debug("Return length: " + rv.length);
|
||||
long finish = _context.clock().now();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("after the elgEngine.encrypt took a total of " + (finish - after) + "ms");
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("after the elgEngine.encrypt took a total of " + (finish - after) + "ms");
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -493,7 +523,7 @@ public class ElGamalAESEngine {
|
||||
System.arraycopy(tag.getData(), 0, aesData, cur, SessionTag.BYTE_LENGTH);
|
||||
cur += SessionTag.BYTE_LENGTH;
|
||||
}
|
||||
//_log.debug("# tags created, registered, and written: " + tags.size());
|
||||
//_log.debug("# tags created, registered, and written: " + tagsForDelivery.size());
|
||||
DataHelper.toLong(aesData, cur, 4, data.length);
|
||||
cur += 4;
|
||||
//_log.debug("data length: " + data.length);
|
||||
@ -519,8 +549,8 @@ public class ElGamalAESEngine {
|
||||
System.arraycopy(padding, 0, aesData, cur, padding.length);
|
||||
cur += padding.length;
|
||||
|
||||
//Hash h = _context.sha().calculateHash(aesUnencr);
|
||||
//_log.debug("Hash of entire aes block before encryption: (len=" + aesUnencr.length + ")\n" + DataHelper.toString(h.getData(), 32));
|
||||
//Hash h = _context.sha().calculateHash(data);
|
||||
//_log.debug("Hash of entire aes block before encryption: (len=" + data.length + ")\n" + DataHelper.toString(h.getData(), 32));
|
||||
_context.aes().encrypt(aesData, prefixBytes, aesData, prefixBytes, key, iv, aesData.length - prefixBytes);
|
||||
//_log.debug("Encrypted length: " + aesEncr.length);
|
||||
//return aesEncr;
|
||||
|
@ -19,10 +19,15 @@ import org.bouncycastle.crypto.macs.HMac;
|
||||
*/
|
||||
public class HMACSHA256Generator {
|
||||
private I2PAppContext _context;
|
||||
/** set of available HMAC instances for calculate */
|
||||
private List _available;
|
||||
/** set of available byte[] buffers for verify */
|
||||
private List _availableTmp;
|
||||
|
||||
public HMACSHA256Generator(I2PAppContext context) {
|
||||
_context = context;
|
||||
_available = new ArrayList(32);
|
||||
_availableTmp = new ArrayList(32);
|
||||
}
|
||||
|
||||
public static HMACSHA256Generator getInstance() {
|
||||
@ -54,6 +59,34 @@ public class HMACSHA256Generator {
|
||||
return new Hash(rv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the MAC inline, reducing some unnecessary memory churn.
|
||||
*
|
||||
* @param key session key to verify the MAC with
|
||||
* @param curData MAC to verify
|
||||
* @param curOffset index into curData to MAC
|
||||
* @param curLength how much data in curData do we want to run the HMAC over
|
||||
* @param origMAC what do we expect the MAC of curData to equal
|
||||
* @param origMACOffset index into origMAC
|
||||
* @param origMACLength how much of the MAC do we want to verify
|
||||
*/
|
||||
public boolean verify(SessionKey key, byte curData[], int curOffset, int curLength, byte origMAC[], int origMACOffset, int origMACLength) {
|
||||
if ((key == null) || (key.getData() == null) || (curData == null))
|
||||
throw new NullPointerException("Null arguments for HMAC");
|
||||
|
||||
HMac mac = acquire();
|
||||
mac.init(key.getData());
|
||||
mac.update(curData, curOffset, curLength);
|
||||
byte rv[] = acquireTmp();
|
||||
//byte rv[] = new byte[Hash.HASH_LENGTH];
|
||||
mac.doFinal(rv, 0);
|
||||
release(mac);
|
||||
|
||||
boolean eq = DataHelper.eq(rv, 0, origMAC, origMACOffset, origMACLength);
|
||||
releaseTmp(rv);
|
||||
return eq;
|
||||
}
|
||||
|
||||
private HMac acquire() {
|
||||
synchronized (_available) {
|
||||
if (_available.size() > 0)
|
||||
@ -67,4 +100,24 @@ public class HMACSHA256Generator {
|
||||
_available.add(mac);
|
||||
}
|
||||
}
|
||||
|
||||
// temp buffers for verify(..)
|
||||
private byte[] acquireTmp() {
|
||||
byte rv[] = null;
|
||||
synchronized (_availableTmp) {
|
||||
if (_availableTmp.size() > 0)
|
||||
rv = (byte[])_availableTmp.remove(0);
|
||||
}
|
||||
if (rv != null)
|
||||
Arrays.fill(rv, (byte)0x0);
|
||||
else
|
||||
rv = new byte[Hash.HASH_LENGTH];
|
||||
return rv;
|
||||
}
|
||||
private void releaseTmp(byte tmp[]) {
|
||||
synchronized (_availableTmp) {
|
||||
if (_availableTmp.size() < 64)
|
||||
_availableTmp.add((Object)tmp);
|
||||
}
|
||||
}
|
||||
}
|
@ -174,7 +174,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
if (sess.getCurrentKey().equals(key)) {
|
||||
SessionTag nxt = sess.consumeNext();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Tag consumed: " + nxt);
|
||||
_log.debug("Tag consumed: " + nxt + " with key: " + key.toBase64());
|
||||
return nxt;
|
||||
}
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@ -228,10 +228,10 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
sess.setCurrentKey(key);
|
||||
TagSet set = new TagSet(sessionTags, key, _context.clock().now());
|
||||
sess.addTags(set);
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug("Tags delivered to set " + set + " on session " + sess);
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
//_log.debug("Tags delivered to set " + set + " on session " + sess);
|
||||
if (sessionTags.size() > 0)
|
||||
_log.debug("Tags delivered: " + sessionTags.size() + " total = " + sess.availableTags());
|
||||
_log.warn("Tags delivered: " + sessionTags.size() + " for key: " + key.toBase64() + ": " + sessionTags);
|
||||
}
|
||||
}
|
||||
|
||||
@ -255,10 +255,27 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
for (Iterator iter = sessionTags.iterator(); iter.hasNext();) {
|
||||
SessionTag tag = (SessionTag) iter.next();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Receiving tag " + tag + " for key " + key);
|
||||
_log.debug("Receiving tag " + tag + " for key " + key.toBase64() + " / " + key.toString() + ": tagSet: " + tagSet);
|
||||
synchronized (_inboundTagSets) {
|
||||
_inboundTagSets.put(tag, tagSet);
|
||||
Object old = _inboundTagSets.put(tag, tagSet);
|
||||
overage = _inboundTagSets.size() - MAX_INBOUND_SESSION_TAGS;
|
||||
if (old != null) {
|
||||
TagSet oldTS = (TagSet)old;
|
||||
if (!oldTS.getAssociatedKey().equals(tagSet.getAssociatedKey())) {
|
||||
if (_log.shouldLog(Log.ERROR)) {
|
||||
_log.error("Multiple tags matching! tag: " + tag.toString() + " matches for new tagSet: " + tagSet + " and old tagSet: " + old);
|
||||
_log.error("Earlier tag set creation: " + old + ": key=" + oldTS.getAssociatedKey().toBase64(), oldTS.getCreatedBy());
|
||||
_log.error("Current tag set creation: " + tagSet + ": key=" + tagSet.getAssociatedKey().toBase64(), tagSet.getCreatedBy());
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
//tagSet.getTags().addAll(oldTS.getTags());
|
||||
_log.debug("Multiple tags matching, but equal keys (probably just a retransmission). tag: " + tag.toString() + " matches for new tagSet: " + tagSet + " and old tagSet: " + old);
|
||||
_log.debug("Earlier tag set creation: " + old + ": key=" + oldTS.getAssociatedKey().toBase64(), oldTS.getCreatedBy());
|
||||
_log.debug("Current tag set creation: " + tagSet + ": key=" + tagSet.getAssociatedKey().toBase64(), tagSet.getCreatedBy());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -267,6 +284,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
|
||||
if ( (sessionTags.size() <= 0) && (_log.shouldLog(Log.DEBUG)) )
|
||||
_log.debug("Received 0 tags for key " + key);
|
||||
if (false) aggressiveExpire();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -329,6 +347,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
*
|
||||
*/
|
||||
public SessionKey consumeTag(SessionTag tag) {
|
||||
if (false) aggressiveExpire();
|
||||
synchronized (_inboundTagSets) {
|
||||
TagSet tagSet = (TagSet) _inboundTagSets.remove(tag);
|
||||
if (tagSet == null) {
|
||||
@ -340,7 +359,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
|
||||
SessionKey key = tagSet.getAssociatedKey();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Consuming tag " + tag + " for sessionKey " + key);
|
||||
_log.debug("Consuming tag " + tag.toString() + " for sessionKey " + key.toBase64() + " / " + key.toString() + " on tagSet: " + tagSet);
|
||||
return key;
|
||||
}
|
||||
}
|
||||
@ -378,19 +397,36 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
int removed = 0;
|
||||
int remaining = 0;
|
||||
long now = _context.clock().now();
|
||||
StringBuffer buf = null;
|
||||
StringBuffer bufSummary = null;
|
||||
if (_log.shouldLog(Log.WARN)) {
|
||||
buf = new StringBuffer(128);
|
||||
buf.append("Expiring inbound: ");
|
||||
bufSummary = new StringBuffer(1024);
|
||||
}
|
||||
synchronized (_inboundTagSets) {
|
||||
for (Iterator iter = _inboundTagSets.keySet().iterator(); iter.hasNext();) {
|
||||
SessionTag tag = (SessionTag) iter.next();
|
||||
TagSet ts = (TagSet) _inboundTagSets.get(tag);
|
||||
if (ts.getDate() < now - SESSION_LIFETIME_MAX_MS) {
|
||||
long age = now - ts.getDate();
|
||||
if (age > SESSION_LIFETIME_MAX_MS) {
|
||||
//if (ts.getDate() < now - SESSION_LIFETIME_MAX_MS) {
|
||||
iter.remove();
|
||||
removed++;
|
||||
if (buf != null)
|
||||
buf.append(tag.toString()).append(" @ age ").append(DataHelper.formatDuration(age));
|
||||
} else if (false && (bufSummary != null) ) {
|
||||
bufSummary.append("\nTagSet: " + ts.toString() + ", key: " + ts.getAssociatedKey().toBase64()+"/" + ts.getAssociatedKey().toString()
|
||||
+ ": tag: " + tag.toString());
|
||||
}
|
||||
}
|
||||
remaining = _inboundTagSets.size();
|
||||
}
|
||||
_context.statManager().addRateData("crypto.sessionTagsRemaining", remaining, 0);
|
||||
|
||||
if ( (buf != null) && (removed > 0) )
|
||||
_log.warn(buf.toString());
|
||||
if (bufSummary != null)
|
||||
_log.warn("Cleaning up with remaining: " + bufSummary.toString());
|
||||
|
||||
//_log.warn("Expiring tags: [" + tagsToDrop + "]");
|
||||
|
||||
@ -606,6 +642,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
private Set _sessionTags;
|
||||
private SessionKey _key;
|
||||
private long _date;
|
||||
private Exception _createdBy;
|
||||
|
||||
public TagSet(Set tags, SessionKey key, long date) {
|
||||
if (key == null) throw new IllegalArgumentException("Missing key");
|
||||
@ -613,6 +650,10 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
_sessionTags = tags;
|
||||
_key = key;
|
||||
_date = date;
|
||||
if (true)
|
||||
_createdBy = new Exception("Created by: key=" + _key.toBase64() + " on "
|
||||
+ new Date(I2PAppContext.getGlobalContext().clock().now())
|
||||
+ " via " + Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
/** when the tag set was created */
|
||||
@ -652,6 +693,8 @@ class TransientSessionKeyManager extends SessionKeyManager {
|
||||
_sessionTags.remove(first);
|
||||
return first;
|
||||
}
|
||||
|
||||
public Exception getCreatedBy() { return _createdBy; }
|
||||
|
||||
public int hashCode() {
|
||||
long rv = 0;
|
||||
|
@ -84,6 +84,17 @@ public class Base64 {
|
||||
(byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1',
|
||||
(byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
|
||||
(byte) '8', (byte) '9', (byte) '+', (byte) '/'};
|
||||
private final static byte[] ALPHABET_ALT = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
|
||||
(byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
|
||||
(byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R',
|
||||
(byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X',
|
||||
(byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd',
|
||||
(byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
|
||||
(byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p',
|
||||
(byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v',
|
||||
(byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1',
|
||||
(byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
|
||||
(byte) '8', (byte) '9', (byte) '-', (byte) '~'};
|
||||
|
||||
/**
|
||||
* Translates a Base64 value to either its 6-bit reconstruction value
|
||||
@ -131,6 +142,7 @@ public class Base64 {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
test();
|
||||
if (args.length == 0) {
|
||||
help();
|
||||
return;
|
||||
@ -312,6 +324,49 @@ public class Base64 {
|
||||
} // end switch
|
||||
} // end encode3to4
|
||||
|
||||
private static void encode3to4(byte[] source, int srcOffset, int numSigBytes, StringBuffer buf, byte alpha[]) {
|
||||
// 1 2 3
|
||||
// 01234567890123456789012345678901 Bit position
|
||||
// --------000000001111111122222222 Array position from threeBytes
|
||||
// --------| || || || | Six bit groups to index ALPHABET
|
||||
// >>18 >>12 >> 6 >> 0 Right shift necessary
|
||||
// 0x3f 0x3f 0x3f Additional AND
|
||||
|
||||
// Create buffer with zero-padding if there are only one or two
|
||||
// significant bytes passed in the array.
|
||||
// We have to shift left 24 in order to flush out the 1's that appear
|
||||
// when Java treats a value as negative that is cast from a byte to an int.
|
||||
int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
|
||||
| (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
|
||||
| (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
|
||||
|
||||
switch (numSigBytes) {
|
||||
case 3:
|
||||
buf.append((char)alpha[(inBuff >>> 18)]);
|
||||
buf.append((char)alpha[(inBuff >>> 12) & 0x3f]);
|
||||
buf.append((char)alpha[(inBuff >>> 6) & 0x3f]);
|
||||
buf.append((char)alpha[(inBuff) & 0x3f]);
|
||||
return;
|
||||
|
||||
case 2:
|
||||
buf.append((char)alpha[(inBuff >>> 18)]);
|
||||
buf.append((char)alpha[(inBuff >>> 12) & 0x3f]);
|
||||
buf.append((char)alpha[(inBuff >>> 6) & 0x3f]);
|
||||
buf.append((char)EQUALS_SIGN);
|
||||
return;
|
||||
|
||||
case 1:
|
||||
buf.append((char)alpha[(inBuff >>> 18)]);
|
||||
buf.append((char)alpha[(inBuff >>> 12) & 0x3f]);
|
||||
buf.append((char)EQUALS_SIGN);
|
||||
buf.append((char)EQUALS_SIGN);
|
||||
return;
|
||||
|
||||
default:
|
||||
return;
|
||||
} // end switch
|
||||
} // end encode3to4
|
||||
|
||||
/**
|
||||
* Encodes a byte array into Base64 notation.
|
||||
* Equivalen to calling
|
||||
@ -331,14 +386,12 @@ public class Base64 {
|
||||
private static String safeEncode(byte[] source, int off, int len, boolean useStandardAlphabet) {
|
||||
if (len + off > source.length)
|
||||
throw new ArrayIndexOutOfBoundsException("Trying to encode too much! source.len=" + source.length + " off=" + off + " len=" + len);
|
||||
String encoded = encodeBytes(source, off, len, false);
|
||||
if (useStandardAlphabet) {
|
||||
// noop
|
||||
} else {
|
||||
encoded = encoded.replace('/', '~');
|
||||
encoded = encoded.replace('+', '-');
|
||||
}
|
||||
return encoded;
|
||||
StringBuffer buf = new StringBuffer(len * 4 / 3);
|
||||
if (useStandardAlphabet)
|
||||
encodeBytes(source, off, len, false, buf, ALPHABET);
|
||||
else
|
||||
encodeBytes(source, off, len, false, buf, ALPHABET_ALT);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -381,6 +434,12 @@ public class Base64 {
|
||||
return encodeBytes(source, off, len, true);
|
||||
} // end encodeBytes
|
||||
|
||||
private static String encodeBytes(byte[] source, int off, int len, boolean breakLines) {
|
||||
StringBuffer buf = new StringBuffer( (len*4)/3 );
|
||||
encodeBytes(source, off, len, breakLines, buf, ALPHABET);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a byte array into Base64 notation.
|
||||
*
|
||||
@ -390,32 +449,36 @@ public class Base64 {
|
||||
* @param breakLines Break lines at 80 characters or less.
|
||||
* @since 1.4
|
||||
*/
|
||||
private static String encodeBytes(byte[] source, int off, int len, boolean breakLines) {
|
||||
private static void encodeBytes(byte[] source, int off, int len, boolean breakLines, StringBuffer out, byte alpha[]) {
|
||||
int len43 = len * 4 / 3;
|
||||
byte[] outBuff = new byte[(len43) // Main 4:3
|
||||
+ ((len % 3) > 0 ? 4 : 0) // Account for padding
|
||||
+ (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines
|
||||
//byte[] outBuff = new byte[(len43) // Main 4:3
|
||||
// + ((len % 3) > 0 ? 4 : 0) // Account for padding
|
||||
// + (breakLines ? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines
|
||||
int d = 0;
|
||||
int e = 0;
|
||||
int len2 = len - 2;
|
||||
int lineLength = 0;
|
||||
for (; d < len2; d += 3, e += 4) {
|
||||
encode3to4(source, d + off, 3, outBuff, e);
|
||||
//encode3to4(source, d + off, 3, outBuff, e);
|
||||
encode3to4(source, d + off, 3, out, alpha);
|
||||
|
||||
lineLength += 4;
|
||||
if (breakLines && lineLength == MAX_LINE_LENGTH) {
|
||||
outBuff[e + 4] = NEW_LINE;
|
||||
//outBuff[e + 4] = NEW_LINE;
|
||||
out.append('\n');
|
||||
e++;
|
||||
lineLength = 0;
|
||||
} // end if: end of line
|
||||
} // en dfor: each piece of array
|
||||
|
||||
if (d < len) {
|
||||
encode3to4(source, d + off, len - d, outBuff, e);
|
||||
//encode3to4(source, d + off, len - d, outBuff, e);
|
||||
encode3to4(source, d + off, len - d, out, alpha);
|
||||
e += 4;
|
||||
} // end if: some padding needed
|
||||
|
||||
return new String(outBuff, 0, e);
|
||||
//out.append(new String(outBuff, 0, e));
|
||||
//return new String(outBuff, 0, e);
|
||||
} // end encodeBytes
|
||||
|
||||
/**
|
||||
|
@ -56,19 +56,20 @@ public class ByteArray implements Serializable, Comparable {
|
||||
public final boolean equals(Object o) {
|
||||
if (o == null) return false;
|
||||
if (o instanceof ByteArray) {
|
||||
return compare(getData(), ((ByteArray) o).getData());
|
||||
ByteArray ba = (ByteArray)o;
|
||||
return compare(getData(), _offset, _valid, ba.getData(), ba.getOffset(), ba.getValid());
|
||||
}
|
||||
|
||||
try {
|
||||
byte val[] = (byte[]) o;
|
||||
return compare(getData(), val);
|
||||
return compare(getData(), _offset, _valid, val, 0, val.length);
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final boolean compare(byte[] lhs, byte[] rhs) {
|
||||
return DataHelper.eq(lhs, rhs);
|
||||
private static final boolean compare(byte[] lhs, int loff, int llen, byte[] rhs, int roff, int rlen) {
|
||||
return (llen == rlen) && DataHelper.eq(lhs, loff, rhs, roff, llen);
|
||||
}
|
||||
|
||||
public final int compareTo(Object obj) {
|
||||
|
@ -721,7 +721,7 @@ public class DataHelper {
|
||||
public static int hashCode(byte b[]) {
|
||||
int rv = 0;
|
||||
if (b != null) {
|
||||
for (int i = 0; i < b.length && i < 8; i++)
|
||||
for (int i = 0; i < b.length && i < 32; i++)
|
||||
rv += (b[i] << i);
|
||||
}
|
||||
return rv;
|
||||
|
@ -78,7 +78,8 @@ public class SessionKey extends DataStructureImpl {
|
||||
return DataHelper.hashCode(_data);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
public String toString() {
|
||||
if (true) return super.toString();
|
||||
StringBuffer buf = new StringBuffer(64);
|
||||
buf.append("[SessionKey: ");
|
||||
if (_data == null) {
|
||||
|
@ -30,11 +30,13 @@ public class BufferedStatLog implements StatLog {
|
||||
private BufferedWriter _out;
|
||||
private String _outFile;
|
||||
|
||||
private static final int BUFFER_SIZE = 1024;
|
||||
|
||||
public BufferedStatLog(I2PAppContext ctx) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(BufferedStatLog.class);
|
||||
_events = new StatEvent[1000];
|
||||
for (int i = 0; i < 1000; i++)
|
||||
_events = new StatEvent[BUFFER_SIZE];
|
||||
for (int i = 0; i < BUFFER_SIZE; i++)
|
||||
_events[i] = new StatEvent();
|
||||
_eventNext = 0;
|
||||
_lastWrite = _events.length-1;
|
||||
@ -71,9 +73,9 @@ public class BufferedStatLog implements StatLog {
|
||||
return _statFilters.contains(stat) || _statFilters.contains("*");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void updateFilters() {
|
||||
String val = _context.getProperty("stat.logFilters");
|
||||
String val = _context.getProperty(StatManager.PROP_STAT_FILTER);
|
||||
if (val != null) {
|
||||
if ( (_lastFilters != null) && (_lastFilters.equals(val)) ) {
|
||||
// noop
|
||||
@ -90,9 +92,9 @@ public class BufferedStatLog implements StatLog {
|
||||
synchronized (_statFilters) { _statFilters.clear(); }
|
||||
}
|
||||
|
||||
String filename = _context.getProperty("stat.logFile");
|
||||
String filename = _context.getProperty(StatManager.PROP_STAT_FILE);
|
||||
if (filename == null)
|
||||
filename = "stats.log";
|
||||
filename = StatManager.DEFAULT_STAT_FILE;
|
||||
if ( (_outFile != null) && (_outFile.equals(filename)) ) {
|
||||
// noop
|
||||
} else {
|
||||
|
@ -29,6 +29,10 @@ public class StatManager {
|
||||
private Map _rateStats;
|
||||
private StatLog _statLog;
|
||||
|
||||
public static final String PROP_STAT_FILTER = "stat.logFilters";
|
||||
public static final String PROP_STAT_FILE = "stat.logFile";
|
||||
public static final String DEFAULT_STAT_FILE = "stats.log";
|
||||
|
||||
/**
|
||||
* The stat manager should only be constructed and accessed through the
|
||||
* application context. This constructor should only be used by the
|
||||
@ -139,7 +143,7 @@ public class StatManager {
|
||||
return _frequencyStats.containsKey(statName);
|
||||
}
|
||||
|
||||
/** Group name (String) to a Set of stat names */
|
||||
/** Group name (String) to a Set of stat names, ordered alphabetically */
|
||||
public Map getStatsByGroup() {
|
||||
Map groups = new TreeMap();
|
||||
for (Iterator iter = _frequencyStats.values().iterator(); iter.hasNext();) {
|
||||
@ -156,4 +160,7 @@ public class StatManager {
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
public String getStatFilter() { return _context.getProperty(PROP_STAT_FILTER); }
|
||||
public String getStatFile() { return _context.getProperty(PROP_STAT_FILE, DEFAULT_STAT_FILE); }
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ public class Clock implements Timestamper.UpdateListener {
|
||||
*
|
||||
*/
|
||||
public void setOffset(long offsetMs, boolean force) {
|
||||
if (false) return;
|
||||
long delta = offsetMs - _offset;
|
||||
if (!force) {
|
||||
if ((offsetMs > MAX_OFFSET) || (offsetMs < 0 - MAX_OFFSET)) {
|
||||
|
@ -28,6 +28,8 @@ public class DecayingBloomFilter {
|
||||
private long _currentDuplicates;
|
||||
private boolean _keepDecaying;
|
||||
private DecayEvent _decayEvent;
|
||||
|
||||
private static final boolean ALWAYS_MISS = false;
|
||||
|
||||
/**
|
||||
* Create a bloom filter that will decay its entries over time.
|
||||
@ -41,8 +43,8 @@ public class DecayingBloomFilter {
|
||||
_context = context;
|
||||
_log = context.logManager().getLog(DecayingBloomFilter.class);
|
||||
_entryBytes = entryBytes;
|
||||
_current = new BloomSHA1(23, 11);
|
||||
_previous = new BloomSHA1(23, 11);
|
||||
_current = new BloomSHA1(23, 11); //new BloomSHA1(23, 11);
|
||||
_previous = new BloomSHA1(23, 11); //new BloomSHA1(23, 11);
|
||||
_durationMs = durationMs;
|
||||
int numExtenders = (32+ (entryBytes-1))/entryBytes - 1;
|
||||
if (numExtenders < 0)
|
||||
@ -78,6 +80,7 @@ public class DecayingBloomFilter {
|
||||
*
|
||||
*/
|
||||
public boolean add(byte entry[]) {
|
||||
if (ALWAYS_MISS) return false;
|
||||
if (entry == null)
|
||||
throw new IllegalArgumentException("Null entry");
|
||||
if (entry.length != _entryBytes)
|
||||
@ -95,6 +98,7 @@ public class DecayingBloomFilter {
|
||||
*
|
||||
*/
|
||||
public boolean add(long entry) {
|
||||
if (ALWAYS_MISS) return false;
|
||||
synchronized (this) {
|
||||
if (_entryBytes <= 7)
|
||||
entry &= _longToEntryMask;
|
||||
@ -114,6 +118,7 @@ public class DecayingBloomFilter {
|
||||
*
|
||||
*/
|
||||
public boolean isKnown(long entry) {
|
||||
if (ALWAYS_MISS) return false;
|
||||
synchronized (this) {
|
||||
if (_entryBytes <= 7)
|
||||
entry &= _longToEntryMask;
|
||||
|
@ -17,10 +17,10 @@ import net.i2p.crypto.EntropyHarvester;
|
||||
*/
|
||||
public class PooledRandomSource extends RandomSource {
|
||||
private Log _log;
|
||||
private RandomSource _pool[];
|
||||
private volatile int _nextPool;
|
||||
protected RandomSource _pool[];
|
||||
protected volatile int _nextPool;
|
||||
|
||||
private static final int POOL_SIZE = 16;
|
||||
public static final int POOL_SIZE = 16;
|
||||
|
||||
public PooledRandomSource(I2PAppContext context) {
|
||||
super(context);
|
||||
@ -34,7 +34,7 @@ public class PooledRandomSource extends RandomSource {
|
||||
}
|
||||
|
||||
private final RandomSource pickPRNG() {
|
||||
int i = _nextPool;
|
||||
int i = _nextPool % POOL_SIZE;
|
||||
_nextPool = (++_nextPool) % POOL_SIZE;
|
||||
return _pool[i];
|
||||
}
|
||||
@ -140,4 +140,17 @@ public class PooledRandomSource extends RandomSource {
|
||||
RandomSource prng = pickPRNG();
|
||||
return prng.harvester();
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
PooledRandomSource prng = new PooledRandomSource(I2PAppContext.getGlobalContext());
|
||||
int size = 8*1024*1024;
|
||||
try {
|
||||
java.io.FileOutputStream out = new java.io.FileOutputStream("random.file");
|
||||
for (int i = 0; i < size; i++) {
|
||||
out.write(prng.nextInt());
|
||||
}
|
||||
out.close();
|
||||
} catch (Exception e) { e.printStackTrace(); }
|
||||
System.out.println("Written to random.file");
|
||||
}
|
||||
}
|
||||
|
@ -31,13 +31,17 @@ import org.bouncycastle.crypto.Digest;
|
||||
import org.bouncycastle.crypto.Mac;
|
||||
//import org.bouncycastle.crypto.params.KeyParameter;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* HMAC implementation based on RFC2104
|
||||
*
|
||||
* H(K XOR opad, H(K XOR ipad, text))
|
||||
*
|
||||
* modified by jrandom to use the session key byte array directly
|
||||
* modified by jrandom to use the session key byte array directly and to cache
|
||||
* a frequently used buffer (called on doFinal). changes released into the public
|
||||
* domain in 2005.
|
||||
*
|
||||
*/
|
||||
public class HMac
|
||||
implements Mac
|
||||
@ -137,11 +141,13 @@ implements Mac
|
||||
byte[] out,
|
||||
int outOff)
|
||||
{
|
||||
byte[] tmp = new byte[digestSize];
|
||||
byte[] tmp = acquireTmp();
|
||||
//byte[] tmp = new byte[digestSize];
|
||||
digest.doFinal(tmp, 0);
|
||||
|
||||
digest.update(outputPad, 0, outputPad.length);
|
||||
digest.update(tmp, 0, tmp.length);
|
||||
releaseTmp(tmp);
|
||||
|
||||
int len = digest.doFinal(out, outOff);
|
||||
|
||||
@ -149,6 +155,26 @@ implements Mac
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
private static ArrayList _tmpBuf = new ArrayList();
|
||||
private static byte[] acquireTmp() {
|
||||
byte rv[] = null;
|
||||
synchronized (_tmpBuf) {
|
||||
if (_tmpBuf.size() > 0)
|
||||
rv = (byte[])_tmpBuf.remove(0);
|
||||
}
|
||||
if (rv != null)
|
||||
Arrays.fill(rv, (byte)0x0);
|
||||
else
|
||||
rv = new byte[32]; // hard coded against SHA256 (should be digestSize)
|
||||
return rv;
|
||||
}
|
||||
private static void releaseTmp(byte buf[]) {
|
||||
synchronized (_tmpBuf) {
|
||||
if (_tmpBuf.size() < 100)
|
||||
_tmpBuf.add((Object)buf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the mac generator.
|
||||
|
@ -47,6 +47,16 @@ public class BloomSHA1 {
|
||||
protected final int filterBits;
|
||||
protected final int filterWords;
|
||||
|
||||
public static void main(String args[]) {
|
||||
BloomSHA1 b = new BloomSHA1(24, 11);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
byte v[] = new byte[32];
|
||||
v[0] = (byte)i;
|
||||
b.insert(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a filter with 2^m bits and k 'hash functions', where
|
||||
* each hash function is portion of the 160-bit SHA1 hash.
|
||||
|
Reference in New Issue
Block a user