package net.i2p.util; /* * free (adj.): unencumbered; not under the control of others * Written by jrandom in 2003 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 gnu.crypto.prng.AsyncFortunaStandalone; import java.io.IOException; import java.security.SecureRandom; import java.util.Random; import net.i2p.I2PAppContext; import net.i2p.crypto.EntropyHarvester; /** * Wrapper around GNU-Crypto's Fortuna PRNG. This seeds from /dev/urandom and * ./prngseed.rnd on startup (if they exist), writing a new seed to ./prngseed.rnd * on an explicit call to saveSeed(). * */ public class FortunaRandomSource extends RandomSource implements EntropyHarvester { private final AsyncFortunaStandalone _fortuna; private double _nextGaussian; private boolean _haveNextGaussian; /** * May block up to 10 seconds or forever */ public FortunaRandomSource(I2PAppContext context) { super(context); _fortuna = new AsyncFortunaStandalone(context); byte seed[] = new byte[1024]; // may block for 10 seconds if (initSeed(seed)) { _fortuna.seed(seed); } else { // may block forever //SecureRandom sr = new SecureRandom(); // SecureRandom already failed in initSeed(), so try Random Random sr = new Random(); sr.nextBytes(seed); _fortuna.seed(seed); } _fortuna.startup(); // kickstart it _fortuna.nextBytes(seed); _haveNextGaussian = false; } /** * Note - methods may hang or NPE or throw IllegalStateExceptions after this * @since 0.8.8 */ public void shutdown() { synchronized(_fortuna) { _fortuna.shutdown(); } } @Override public void setSeed(byte buf[]) { synchronized(_fortuna) { _fortuna.addRandomBytes(buf); } } /** * 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. * 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. * */ @Override public int nextInt(int n) { if (n == 0) return 0; int rv = signedNextInt(n); if (rv < 0) rv = 0 - rv; rv %= n; return rv; } @Override public int nextInt() { return signedNextInt(Integer.MAX_VALUE); } /** * Implementation from Sun's java.util.Random javadocs */ private int signedNextInt(int n) { if (n<=0) throw new IllegalArgumentException("n must be positive"); //// // this shortcut from sun's docs neither works nor is necessary. // //if ((n & -n) == n) { // // i.e., n is a power of 2 // return (int)((n * (long)nextBits(31)) >> 31); //} // get at least 4 extra bits if possible for better // distribution after the % // No extra needed if power of two. int numBits; if (n > 0x100000) numBits = 31; else if (n > 0x1000) numBits = 24; else if (n > 0x10) numBits = 16; else numBits = 8; int rv; synchronized(_fortuna) { rv = nextBits(numBits); } return rv % n; //int bits, val; //do { // bits = nextBits(31); // val = bits % n; //} while(bits - val + (n-1) < 0); // //return val; } /** * Like the modified nextInt, nextLong(n) returns a random number from 0 through n, * including 0, excluding n. */ @Override public long nextLong(long n) { if (n == 0) return 0; long rv = signedNextLong(); if (rv < 0) rv = 0 - rv; rv %= n; return rv; } @Override public long nextLong() { return signedNextLong(); } /** * Implementation from Sun's java.util.Random javadocs */ private long signedNextLong() { synchronized(_fortuna) { return ((long)nextBits(32) << 32) + nextBits(32); } } @Override 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); } } /** * Not part of java.util.SecureRandom, but added for efficiency, since Fortuna supports it. * * @since 0.8.12 */ @Override public void nextBytes(byte buf[], int offset, int length) { synchronized(_fortuna) { _fortuna.nextBytes(buf, offset, length); } } /** * Not part of java.util.SecureRandom, but added for efficiency, since Fortuna supports it. * * @since 0.9.24 */ public byte nextByte() { synchronized(_fortuna) { return _fortuna.nextByte(); } } /** * Implementation from sun's java.util.Random javadocs */ @Override public double nextDouble() { long d; synchronized(_fortuna) { d = ((long)nextBits(26) << 27) + nextBits(27); } return d / (double)(1L << 53); } /** * Implementation from sun's java.util.Random javadocs */ @Override public float nextFloat() { int d; synchronized(_fortuna) { d = nextBits(24); } return d / ((float)(1 << 24)); } /** * Implementation from sun's java.util.Random javadocs */ @Override public double nextGaussian() { synchronized (this) { if (_haveNextGaussian) { _haveNextGaussian = false; return _nextGaussian; } double v1, v2, s; do { v1 = 2 * nextDouble() - 1; // between -1.0 and 1.0 v2 = 2 * nextDouble() - 1; // between -1.0 and 1.0 s = v1 * v1 + v2 * v2; } while (s >= 1 || s == 0); double multiplier = Math.sqrt(-2 * Math.log(s)/s); _nextGaussian = v2 * multiplier; _haveNextGaussian = true; return v1 * multiplier; } } /** * Pull the next numBits of random data off the fortuna instance (returning 0 * through 2^numBits-1 * * Caller must synchronize! */ protected int nextBits(int numBits) { long rv = 0; int bytes = (numBits + 7) / 8; for (int i = 0; i < bytes; i++) rv += ((_fortuna.nextByte() & 0xFF) << i*8); //rv >>>= (64-numBits); if (rv < 0) rv = 0 - rv; int off = 8*bytes - numBits; rv >>>= off; return (int)rv; } @Override public EntropyHarvester harvester() { return this; } /** reseed the fortuna */ @Override public void feedEntropy(String source, long data, int bitoffset, int bits) { synchronized(_fortuna) { _fortuna.addRandomByte((byte)(data & 0xFF)); } } /** reseed the fortuna */ @Override public void feedEntropy(String source, byte[] data, int offset, int len) { try { synchronized(_fortuna) { _fortuna.addRandomBytes(data, offset, len); } } catch (RuntimeException e) { // AIOOBE seen, root cause unknown, ticket #1576 Log log = _context.logManager().getLog(FortunaRandomSource.class); log.warn("feedEntropy()", e); } } /** * Outputs to stdout for dieharder: * <code> * java -cp build/i2p.jar net.i2p.util.FortunaRandomSource | dieharder -a -g 200 * </code> */ public static void main(String args[]) { try { java.util.Properties props = new java.util.Properties(); props.setProperty("prng.buffers", "12"); I2PAppContext ctx = new I2PAppContext(props); RandomSource rand = ctx.random(); byte[] buf = new byte[65536]; while (true) { rand.nextBytes(buf); System.out.write(buf); } } catch (IOException e) { e.printStackTrace(); } } }