forked from I2P_Developers/i2p.i2p
2005-07-05
* Use a buffered PRNG, pulling the PRNG data off a larger precalculated
buffer, rather than the underlying PRNG's (likely small) one, which in
turn reduces the frequency of recalcing.
* More tuning to reduce temporary allocation churn
This commit is contained in:
@@ -9,12 +9,16 @@ import net.i2p.data.Hash;
|
||||
import net.i2p.data.SessionKey;
|
||||
|
||||
import org.bouncycastle.crypto.digests.SHA256Digest;
|
||||
import org.bouncycastle.crypto.digests.MD5Digest;
|
||||
import org.bouncycastle.crypto.macs.HMac;
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
* {@link org.bouncycastle.crypto.digests.SHA256Digest}. Alternately, if
|
||||
* the context property "i2p.HMACMD5" is set to true, then this whole HMAC
|
||||
* generator will be transformed into HMACMD5, maintaining the same size and
|
||||
* using {@link org.bouncycastle.crypto.digests.MD5Digest}.
|
||||
*
|
||||
*/
|
||||
public class HMACSHA256Generator {
|
||||
@@ -23,11 +27,18 @@ public class HMACSHA256Generator {
|
||||
private List _available;
|
||||
/** set of available byte[] buffers for verify */
|
||||
private List _availableTmp;
|
||||
private boolean _useMD5;
|
||||
|
||||
public static final boolean DEFAULT_USE_MD5 = true;
|
||||
|
||||
public HMACSHA256Generator(I2PAppContext context) {
|
||||
_context = context;
|
||||
_available = new ArrayList(32);
|
||||
_availableTmp = new ArrayList(32);
|
||||
if ("true".equals(context.getProperty("i2p.HMACMD5", Boolean.toString(DEFAULT_USE_MD5).toLowerCase())))
|
||||
_useMD5 = true;
|
||||
else
|
||||
_useMD5 = false;
|
||||
}
|
||||
|
||||
public static HMACSHA256Generator getInstance() {
|
||||
@@ -40,12 +51,15 @@ public class HMACSHA256Generator {
|
||||
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);
|
||||
byte rv[] = new byte[Hash.HASH_LENGTH];
|
||||
calculate(key, data, 0, data.length, rv, 0);
|
||||
return new Hash(rv);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
@@ -58,6 +72,23 @@ public class HMACSHA256Generator {
|
||||
release(mac);
|
||||
return new Hash(rv);
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Calculate the HMAC of the data with the given key
|
||||
*/
|
||||
public void calculate(SessionKey key, byte data[], int offset, int length, byte target[], int targetOffset) {
|
||||
if ((key == null) || (key.getData() == null) || (data == null))
|
||||
throw new NullPointerException("Null arguments for HMAC");
|
||||
|
||||
HMac mac = acquire();
|
||||
mac.init(key.getData());
|
||||
mac.update(data, offset, length);
|
||||
//byte rv[] = new byte[Hash.HASH_LENGTH];
|
||||
mac.doFinal(target, targetOffset);
|
||||
release(mac);
|
||||
//return new Hash(rv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the MAC inline, reducing some unnecessary memory churn.
|
||||
@@ -92,6 +123,9 @@ public class HMACSHA256Generator {
|
||||
if (_available.size() > 0)
|
||||
return (HMac)_available.remove(0);
|
||||
}
|
||||
if (_useMD5)
|
||||
return new HMac(new MD5Digest());
|
||||
else
|
||||
return new HMac(new SHA256Digest());
|
||||
}
|
||||
private void release(HMac mac) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package net.i2p.crypto;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Hash;
|
||||
|
||||
@@ -12,7 +14,10 @@ import org.bouncycastle.crypto.digests.SHA256Digest;
|
||||
*
|
||||
*/
|
||||
public final class SHA256Generator {
|
||||
public SHA256Generator(I2PAppContext context) {}
|
||||
private List _digests;
|
||||
public SHA256Generator(I2PAppContext context) {
|
||||
_digests = new ArrayList(32);
|
||||
}
|
||||
|
||||
public static final SHA256Generator getInstance() {
|
||||
return I2PAppContext.getGlobalContext().sha();
|
||||
@@ -27,11 +32,35 @@ public final class SHA256Generator {
|
||||
}
|
||||
public final Hash calculateHash(byte[] source, int start, int len) {
|
||||
byte rv[] = new byte[Hash.HASH_LENGTH];
|
||||
SHA256Digest digest = new SHA256Digest();
|
||||
digest.update(source, start, len);
|
||||
digest.doFinal(rv, 0);
|
||||
calculateHash(source, start, len, rv, 0);
|
||||
return new Hash(rv);
|
||||
}
|
||||
public final void calculateHash(byte[] source, int start, int len, byte out[], int outOffset) {
|
||||
SHA256Digest digest = acquire();
|
||||
digest.update(source, start, len);
|
||||
digest.doFinal(out, outOffset);
|
||||
release(digest);
|
||||
}
|
||||
|
||||
private SHA256Digest acquire() {
|
||||
SHA256Digest rv = null;
|
||||
synchronized (_digests) {
|
||||
if (_digests.size() > 0)
|
||||
rv = (SHA256Digest)_digests.remove(0);
|
||||
}
|
||||
if (rv != null)
|
||||
rv.reset();
|
||||
else
|
||||
rv = new SHA256Digest();
|
||||
return rv;
|
||||
}
|
||||
private void release(SHA256Digest digest) {
|
||||
synchronized (_digests) {
|
||||
if (_digests.size() < 32) {
|
||||
_digests.add(digest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
|
||||
222
core/java/src/net/i2p/util/BufferedRandomSource.java
Normal file
222
core/java/src/net/i2p/util/BufferedRandomSource.java
Normal file
@@ -0,0 +1,222 @@
|
||||
package net.i2p.util;
|
||||
|
||||
/*
|
||||
* free (adj.): unencumbered; not under the control of others
|
||||
* Written by jrandom in 2005 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.security.SecureRandom;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.EntropyHarvester;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
/**
|
||||
* Allocate data out of a large buffer of data, rather than the PRNG's
|
||||
* (likely) small buffer to reduce the frequency of prng recalcs (though
|
||||
* the recalcs are now more time consuming).
|
||||
*
|
||||
*/
|
||||
public class BufferedRandomSource extends RandomSource {
|
||||
private byte _buffer[];
|
||||
private int _nextByte;
|
||||
private int _nextBit;
|
||||
private static volatile long _reseeds;
|
||||
|
||||
private static final int BUFFER_SIZE = 256*1024;
|
||||
|
||||
public BufferedRandomSource(I2PAppContext context) {
|
||||
super(context);
|
||||
context.statManager().createRateStat("prng.reseedCount", "How many times the prng has been reseeded", "Encryption", new long[] { 60*1000, 10*60*1000, 60*60*1000 } );
|
||||
_buffer = new byte[BUFFER_SIZE];
|
||||
refillBuffer();
|
||||
// stagger reseeding
|
||||
_nextByte = ((int)_reseeds-1) * 16 * 1024;
|
||||
}
|
||||
|
||||
private final void refillBuffer() {
|
||||
long before = System.currentTimeMillis();
|
||||
doRefillBuffer();
|
||||
long duration = System.currentTimeMillis() - before;
|
||||
if ( (_reseeds % 1) == 0)
|
||||
_context.statManager().addRateData("prng.reseedCount", _reseeds, duration);
|
||||
}
|
||||
|
||||
private synchronized final void doRefillBuffer() {
|
||||
super.nextBytes(_buffer);
|
||||
_nextByte = 0;
|
||||
_nextBit = 0;
|
||||
_reseeds++;
|
||||
}
|
||||
|
||||
private static final byte GOBBLE_MASK[] = { 0x0, // 0 bits
|
||||
0x1, // 1 bit
|
||||
0x3, // 2 bits
|
||||
0x7, // 3 bits
|
||||
0xF, // 4 bits
|
||||
0x1F, // 5 bits
|
||||
0x3F, // 6 bits
|
||||
0x7F, // 7 bits
|
||||
(byte)0xFF // 8 bits
|
||||
};
|
||||
|
||||
private synchronized final long nextBits(int numBits) {
|
||||
if (false) {
|
||||
long rv = 0;
|
||||
for (int curBit = 0; curBit < numBits; curBit++) {
|
||||
if (_nextBit >= 8) {
|
||||
_nextBit = 0;
|
||||
_nextByte++;
|
||||
}
|
||||
if (_nextByte >= BUFFER_SIZE)
|
||||
refillBuffer();
|
||||
rv += (_buffer[_nextByte] << curBit);
|
||||
_nextBit++;
|
||||
/*
|
||||
int avail = 8 - _nextBit;
|
||||
// this is not correct! (or is it?)
|
||||
rv += (_buffer[_nextByte] << 8 - avail);
|
||||
_nextBit += avail;
|
||||
numBits -= avail;
|
||||
if (_nextBit >= 8) {
|
||||
_nextBit = 0;
|
||||
_nextByte++;
|
||||
}
|
||||
*/
|
||||
}
|
||||
return rv;
|
||||
} else {
|
||||
long rv = 0;
|
||||
int curBit = 0;
|
||||
while (curBit < numBits) {
|
||||
if (_nextBit >= 8) {
|
||||
_nextBit = 0;
|
||||
_nextByte++;
|
||||
}
|
||||
if (_nextByte >= BUFFER_SIZE)
|
||||
refillBuffer();
|
||||
int gobbleBits = 8 - _nextBit;
|
||||
int want = numBits - curBit;
|
||||
if (gobbleBits > want)
|
||||
gobbleBits = want;
|
||||
curBit += gobbleBits;
|
||||
int shift = 8 - _nextBit - gobbleBits;
|
||||
int c = (_buffer[_nextByte] & (GOBBLE_MASK[gobbleBits] << shift));
|
||||
rv += ((c >>> shift) << (curBit-gobbleBits));
|
||||
_nextBit += gobbleBits;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized final void nextBytes(byte buf[]) {
|
||||
int outOffset = 0;
|
||||
while (outOffset < buf.length) {
|
||||
int availableBytes = BUFFER_SIZE - _nextByte - (_nextBit != 0 ? 1 : 0);
|
||||
if (availableBytes <= 0)
|
||||
refillBuffer();
|
||||
int start = BUFFER_SIZE - availableBytes;
|
||||
int writeSize = Math.min(buf.length - outOffset, availableBytes);
|
||||
System.arraycopy(_buffer, start, buf, outOffset, writeSize);
|
||||
outOffset += writeSize;
|
||||
_nextByte += writeSize;
|
||||
_nextBit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public final int nextInt(int n) {
|
||||
if (n <= 0) return 0;
|
||||
int val = ((int)nextBits(countBits(n))) % n;
|
||||
if (val < 0)
|
||||
return 0 - val;
|
||||
else
|
||||
return val;
|
||||
}
|
||||
|
||||
public final int nextInt() { return nextInt(Integer.MAX_VALUE); }
|
||||
|
||||
/**
|
||||
* Like the modified nextInt, nextLong(n) returns a random number from 0 through n,
|
||||
* including 0, excluding n.
|
||||
*/
|
||||
public final long nextLong(long n) {
|
||||
if (n <= 0) return 0;
|
||||
long val = nextBits(countBits(n)) % n;
|
||||
if (val < 0)
|
||||
return 0 - val;
|
||||
else
|
||||
return val;
|
||||
}
|
||||
|
||||
public final long nextLong() { return nextLong(Long.MAX_VALUE); }
|
||||
|
||||
static final int countBits(long val) {
|
||||
int rv = 0;
|
||||
while (val > Integer.MAX_VALUE) {
|
||||
rv += 31;
|
||||
val >>>= 31;
|
||||
}
|
||||
|
||||
while (val > 0) {
|
||||
rv++;
|
||||
val >>= 1;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* override as synchronized, for those JVMs that don't always pull via
|
||||
* nextBytes (cough ibm)
|
||||
*/
|
||||
public final boolean nextBoolean() {
|
||||
return nextBits(1) != 0;
|
||||
}
|
||||
|
||||
private static final double DOUBLE_DENOMENATOR = (double)(1L << 53);
|
||||
/** defined per javadoc ( ((nextBits(26)<<27) + nextBits(27)) / (1 << 53)) */
|
||||
public final double nextDouble() {
|
||||
long top = (((long)nextBits(26) << 27) + nextBits(27));
|
||||
return top / DOUBLE_DENOMENATOR;
|
||||
}
|
||||
private static final float FLOAT_DENOMENATOR = (float)(1 << 24);
|
||||
/** defined per javadoc (nextBits(24) / ((float)(1 << 24)) ) */
|
||||
public float nextFloat() {
|
||||
long top = nextBits(24);
|
||||
return top / FLOAT_DENOMENATOR;
|
||||
}
|
||||
public double nextGaussian() {
|
||||
// bah, unbuffered
|
||||
return super.nextGaussian();
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
byte data[] = new byte[16*1024];
|
||||
for (int i = 0; i < data.length; i += 4) {
|
||||
long l = ctx.random().nextLong();
|
||||
if (l < 0) l = 0 - l;
|
||||
DataHelper.toLong(data, i, 4, l);
|
||||
}
|
||||
byte compressed[] = DataHelper.compress(data);
|
||||
System.out.println("Compressed: " + compressed.length);
|
||||
System.out.println("Orig: " + data.length + ": " + toString(data));
|
||||
}
|
||||
private static final String toString(byte data[]) {
|
||||
StringBuffer buf = new StringBuffer(data.length * 9);
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if ((data[i] & (1 << j)) != 0)
|
||||
buf.append('1');
|
||||
else
|
||||
buf.append('0');
|
||||
}
|
||||
buf.append(' ');
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,8 @@ public class PooledRandomSource extends RandomSource {
|
||||
_log = context.logManager().getLog(PooledRandomSource.class);
|
||||
_pool = new RandomSource[POOL_SIZE];
|
||||
for (int i = 0; i < POOL_SIZE; i++) {
|
||||
_pool[i] = new RandomSource(context);
|
||||
//_pool[i] = new RandomSource(context);
|
||||
_pool[i] = new BufferedRandomSource(context);
|
||||
_pool[i].nextBoolean();
|
||||
}
|
||||
_nextPool = 0;
|
||||
|
||||
@@ -22,9 +22,11 @@ import net.i2p.crypto.EntropyHarvester;
|
||||
public class RandomSource extends SecureRandom {
|
||||
private Log _log;
|
||||
private EntropyHarvester _entropyHarvester;
|
||||
protected I2PAppContext _context;
|
||||
|
||||
public RandomSource(I2PAppContext context) {
|
||||
super();
|
||||
_context = context;
|
||||
_log = context.logManager().getLog(RandomSource.class);
|
||||
// when we replace to have hooks for fortuna (etc), replace with
|
||||
// a factory (or just a factory method)
|
||||
|
||||
Reference in New Issue
Block a user