* FortunaRandomSource:

- Fix bug that wasted entropy in nextInt()
    - Improved synchronization
This commit is contained in:
zzz
2012-09-11 19:40:20 +00:00
parent d7b48a2256
commit db42d9ec37
3 changed files with 73 additions and 42 deletions

View File

@@ -14,7 +14,7 @@ import net.i2p.util.Log;
* *
* Note that this class is not fully Thread safe! * Note that this class is not fully Thread safe!
* The following methods must be synchronized externally, they are not * The following methods must be synchronized externally, they are not
* sycned here or in super(): * synced here or in super():
* addRandomByte(), addRandomBytes(), nextByte(), nextBytes(), seed() * addRandomByte(), addRandomBytes(), nextByte(), nextBytes(), seed()
* *
*/ */

View File

@@ -55,7 +55,7 @@ public abstract class BasePRNGStandalone implements IRandomStandalone {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
/** The canonical name prefix of the PRNG algorithm. */ /** The canonical name prefix of the PRNG algorithm. */
protected String name; protected final String name;
/** Indicate if this instance has already been initialised or not. */ /** Indicate if this instance has already been initialised or not. */
protected boolean initialised; protected boolean initialised;
@@ -75,10 +75,7 @@ public abstract class BasePRNGStandalone implements IRandomStandalone {
* @param name the canonical name of this instance. * @param name the canonical name of this instance.
*/ */
protected BasePRNGStandalone(String name) { protected BasePRNGStandalone(String name) {
super();
this.name = name; this.name = name;
initialised = false;
buffer = new byte[0]; buffer = new byte[0];
} }

View File

@@ -49,13 +49,17 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste
* @since 0.8.8 * @since 0.8.8
*/ */
public void shutdown() { public void shutdown() {
synchronized(_fortuna) {
_fortuna.shutdown(); _fortuna.shutdown();
} }
}
@Override @Override
public synchronized void setSeed(byte buf[]) { public void setSeed(byte buf[]) {
synchronized(_fortuna) {
_fortuna.addRandomBytes(buf); _fortuna.addRandomBytes(buf);
} }
}
/** /**
* According to the java docs (http://java.sun.com/j2se/1.4.1/docs/api/java/util/Random.html#nextInt(int)) * According to the java docs (http://java.sun.com/j2se/1.4.1/docs/api/java/util/Random.html#nextInt(int))
@@ -93,16 +97,21 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste
// return (int)((n * (long)nextBits(31)) >> 31); // return (int)((n * (long)nextBits(31)) >> 31);
//} //}
int numBits = 0; // get at least 4 extra bits if possible for better
int remaining = n; // distribution after the %
int rv = 0; int numBits;
while (remaining > 0) { if (n > 0xfffff)
remaining >>= 1; numBits = 31;
rv += nextBits(8) << numBits*8; else if (n > 0xfff)
numBits++; numBits = 24;
else if (n > 0xf)
numBits = 16;
else
numBits = 8;
int rv;
synchronized(_fortuna) {
rv = nextBits(numBits);
} }
if (rv < 0)
rv += n;
return rv % n; return rv % n;
//int bits, val; //int bits, val;
@@ -121,7 +130,7 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste
@Override @Override
public long nextLong(long n) { public long nextLong(long n) {
if (n == 0) return 0; if (n == 0) return 0;
long rv = signedNextLong(n); long rv = signedNextLong();
if (rv < 0) if (rv < 0)
rv = 0 - rv; rv = 0 - rv;
rv %= n; rv %= n;
@@ -129,26 +138,32 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste
} }
@Override @Override
public long nextLong() { return signedNextLong(Long.MAX_VALUE); } public long nextLong() { return signedNextLong(); }
/** /**
* Implementation from Sun's java.util.Random javadocs * Implementation from Sun's java.util.Random javadocs
*/ */
private long signedNextLong(long n) { private long signedNextLong() {
synchronized(_fortuna) {
return ((long)nextBits(32) << 32) + nextBits(32); return ((long)nextBits(32) << 32) + nextBits(32);
} }
@Override
public synchronized boolean nextBoolean() {
// wasteful, might be worth caching the boolean byte later
byte val = _fortuna.nextByte();
return ((val & 0x01) == 1);
} }
@Override @Override
public synchronized void nextBytes(byte buf[]) { public boolean nextBoolean() {
byte val;
synchronized(_fortuna) {
val = _fortuna.nextByte();
}
return ((val & 0x01) != 0);
}
@Override
public void nextBytes(byte buf[]) {
synchronized(_fortuna) {
_fortuna.nextBytes(buf); _fortuna.nextBytes(buf);
} }
}
/** /**
* Not part of java.util.SecureRandom, but added for efficiency, since Fortuna supports it. * Not part of java.util.SecureRandom, but added for efficiency, since Fortuna supports it.
@@ -156,33 +171,46 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste
* @since 0.8.12 * @since 0.8.12
*/ */
@Override @Override
public synchronized void nextBytes(byte buf[], int offset, int length) { public void nextBytes(byte buf[], int offset, int length) {
synchronized(_fortuna) {
_fortuna.nextBytes(buf, offset, length); _fortuna.nextBytes(buf, offset, length);
} }
}
/** /**
* Implementation from sun's java.util.Random javadocs * Implementation from sun's java.util.Random javadocs
*/ */
@Override @Override
public double nextDouble() { public double nextDouble() {
return (((long)nextBits(26) << 27) + nextBits(27)) / (double)(1L << 53); long d;
synchronized(_fortuna) {
d = ((long)nextBits(26) << 27) + nextBits(27);
} }
return d / (double)(1L << 53);
}
/** /**
* Implementation from sun's java.util.Random javadocs * Implementation from sun's java.util.Random javadocs
*/ */
@Override @Override
public float nextFloat() { public float nextFloat() {
return nextBits(24) / ((float)(1 << 24)); int d;
synchronized(_fortuna) {
d = nextBits(24);
} }
return d / ((float)(1 << 24));
}
/** /**
* Implementation from sun's java.util.Random javadocs * Implementation from sun's java.util.Random javadocs
*/ */
@Override @Override
public synchronized double nextGaussian() { public double nextGaussian() {
synchronized (this) {
if (_haveNextGaussian) { if (_haveNextGaussian) {
_haveNextGaussian = false; _haveNextGaussian = false;
return _nextGaussian; return _nextGaussian;
} else { }
double v1, v2, s; double v1, v2, s;
do { do {
v1 = 2 * nextDouble() - 1; // between -1.0 and 1.0 v1 = 2 * nextDouble() - 1; // between -1.0 and 1.0
@@ -197,10 +225,12 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste
} }
/** /**
* Pull the next numBits of random data off the fortuna instance (returning -2^numBits-1 * Pull the next numBits of random data off the fortuna instance (returning 0
* through 2^numBits-1 * through 2^numBits-1
*
* Caller must synchronize!
*/ */
protected synchronized int nextBits(int numBits) { protected int nextBits(int numBits) {
long rv = 0; long rv = 0;
int bytes = (numBits + 7) / 8; int bytes = (numBits + 7) / 8;
for (int i = 0; i < bytes; i++) for (int i = 0; i < bytes; i++)
@@ -218,15 +248,19 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste
/** reseed the fortuna */ /** reseed the fortuna */
@Override @Override
public synchronized void feedEntropy(String source, long data, int bitoffset, int bits) { public void feedEntropy(String source, long data, int bitoffset, int bits) {
synchronized(_fortuna) {
_fortuna.addRandomByte((byte)(data & 0xFF)); _fortuna.addRandomByte((byte)(data & 0xFF));
} }
}
/** reseed the fortuna */ /** reseed the fortuna */
@Override @Override
public synchronized void feedEntropy(String source, byte[] data, int offset, int len) { public void feedEntropy(String source, byte[] data, int offset, int len) {
synchronized(_fortuna) {
_fortuna.addRandomBytes(data, offset, len); _fortuna.addRandomBytes(data, offset, len);
} }
}
/***** /*****
public static void main(String args[]) { public static void main(String args[]) {