forked from I2P_Developers/i2p.i2p
2005-10-22 jrandom
* Integrated GNU-Crypto's Fortuna PRNG, seeding it off /dev/urandom and
./prngseed.rnd (if they exist), and reseeding it with data out of
various crypto operations (unused bits in a DH exchange, intermediary
bits in a DSA signature generation, extra bits in an ElGamal decrypt).
The Fortuna implementation under gnu.crypto.prng has been modified to
use BouncyCastle's SHA256 and Cryptix's AES (since those are the ones
I2P uses), and the resulting gnu.crypto.prng.* are therefor available
under GPL+Classpath's linking exception (~= LGPL). I2P's SecureRandom
wrapper around it is, of course, public domain.
This commit is contained in:
@@ -26,6 +26,7 @@ import net.i2p.util.Clock;
|
||||
import net.i2p.util.LogManager;
|
||||
import net.i2p.util.RandomSource;
|
||||
import net.i2p.util.PooledRandomSource;
|
||||
import net.i2p.util.FortunaRandomSource;
|
||||
|
||||
/**
|
||||
* <p>Provide a base scope for accessing singletons that I2P exposes. Rather than
|
||||
@@ -456,7 +457,9 @@ public class I2PAppContext {
|
||||
private void initializeRandom() {
|
||||
synchronized (this) {
|
||||
if (_random == null) {
|
||||
if ("true".equals(getProperty("i2p.weakPRNG", "false")))
|
||||
if (true)
|
||||
_random = new FortunaRandomSource(this);
|
||||
else if ("true".equals(getProperty("i2p.weakPRNG", "false")))
|
||||
_random = new DummyPooledRandomSource(this);
|
||||
else
|
||||
_random = new PooledRandomSource(this);
|
||||
@@ -464,4 +467,4 @@ public class I2PAppContext {
|
||||
_randomInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.List;
|
||||
* of code)
|
||||
*
|
||||
*/
|
||||
final class CryptixAESKeyCache {
|
||||
public final class CryptixAESKeyCache {
|
||||
private List _availableKeys;
|
||||
|
||||
private static final int KEYSIZE = 32; // 256bit AES
|
||||
@@ -48,7 +48,7 @@ final class CryptixAESKeyCache {
|
||||
}
|
||||
}
|
||||
|
||||
private static final KeyCacheEntry createNew() {
|
||||
public static final KeyCacheEntry createNew() {
|
||||
KeyCacheEntry e = new KeyCacheEntry();
|
||||
e.Ke = new int[ROUNDS + 1][BC]; // encryption round keys
|
||||
e.Kd = new int[ROUNDS + 1][BC]; // decryption round keys
|
||||
|
||||
@@ -130,9 +130,13 @@ public class DSAEngine {
|
||||
Signature sig = new Signature();
|
||||
BigInteger k;
|
||||
|
||||
boolean ok = false;
|
||||
do {
|
||||
k = new BigInteger(160, _context.random());
|
||||
} while (k.compareTo(CryptoConstants.dsaq) != 1);
|
||||
ok = k.compareTo(CryptoConstants.dsaq) != 1;
|
||||
ok = ok && !k.equals(BigInteger.ZERO);
|
||||
//System.out.println("K picked (ok? " + ok + "): " + k.bitLength() + ": " + k.toString());
|
||||
} while (!ok);
|
||||
|
||||
BigInteger r = CryptoConstants.dsag.modPow(k, CryptoConstants.dsap).mod(CryptoConstants.dsaq);
|
||||
BigInteger kinv = k.modInverse(CryptoConstants.dsaq);
|
||||
@@ -145,6 +149,9 @@ public class DSAEngine {
|
||||
byte[] sbytes = s.toByteArray();
|
||||
byte[] out = new byte[40];
|
||||
|
||||
// (q^random)%p is computationally random
|
||||
_context.random().harvester().feedEntropy("DSA.sign", rbytes, 0, rbytes.length);
|
||||
|
||||
if (rbytes.length == 20) {
|
||||
for (int i = 0; i < 20; i++) {
|
||||
out[i] = rbytes[i];
|
||||
@@ -203,4 +210,19 @@ public class DSAEngine {
|
||||
byte digested[] = h.digest();
|
||||
return new Hash(digested);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
byte data[] = new byte[4096];
|
||||
ctx.random().nextBytes(data);
|
||||
Object keys[] = ctx.keyGenerator().generateSigningKeypair();
|
||||
try {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Signature sig = ctx.dsa().sign(data, (SigningPrivateKey)keys[1]);
|
||||
boolean ok = ctx.dsa().verifySignature(sig, data, (SigningPublicKey)keys[0]);
|
||||
System.out.println("OK: " + ok);
|
||||
}
|
||||
} catch (Exception e) { e.printStackTrace(); }
|
||||
ctx.random().saveSeed();
|
||||
}
|
||||
}
|
||||
|
||||
178
core/java/src/net/i2p/util/FortunaRandomSource.java
Normal file
178
core/java/src/net/i2p/util/FortunaRandomSource.java
Normal file
@@ -0,0 +1,178 @@
|
||||
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 java.security.SecureRandom;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.EntropyHarvester;
|
||||
|
||||
import gnu.crypto.prng.Fortuna;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 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 Fortuna _fortuna;
|
||||
private double _nextGaussian;
|
||||
private boolean _haveNextGaussian;
|
||||
|
||||
public FortunaRandomSource(I2PAppContext context) {
|
||||
super(context);
|
||||
_fortuna = new Fortuna();
|
||||
byte seed[] = new byte[1024];
|
||||
if (initSeed(seed)) {
|
||||
_fortuna.seed(seed);
|
||||
} else {
|
||||
SecureRandom sr = new SecureRandom();
|
||||
sr.nextBytes(seed);
|
||||
_fortuna.seed(seed);
|
||||
}
|
||||
// kickstart it
|
||||
_fortuna.nextBytes(seed);
|
||||
_haveNextGaussian = false;
|
||||
}
|
||||
|
||||
public synchronized void setSeed(byte 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))
|
||||
* 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 rv = signedNextInt(n);
|
||||
if (rv < 0)
|
||||
rv = 0 - rv;
|
||||
rv %= n;
|
||||
return rv;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
if ((n & -n) == n) // i.e., n is a power of 2
|
||||
return (int)((n * (long)nextBits(31)) >> 31);
|
||||
|
||||
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.
|
||||
*/
|
||||
public long nextLong(long n) {
|
||||
if (n == 0) return 0;
|
||||
long rv = signedNextLong(n);
|
||||
if (rv < 0)
|
||||
rv = 0 - rv;
|
||||
rv %= n;
|
||||
return rv;
|
||||
}
|
||||
|
||||
public long nextLong() { return signedNextLong(Long.MAX_VALUE); }
|
||||
|
||||
/**
|
||||
* Implementation from Sun's java.util.Random javadocs
|
||||
*/
|
||||
private long signedNextLong(long n) {
|
||||
return ((long)nextBits(32) << 32) + nextBits(32);
|
||||
}
|
||||
|
||||
public synchronized boolean nextBoolean() {
|
||||
// wasteful, might be worth caching the boolean byte later
|
||||
byte val = _fortuna.nextByte();
|
||||
return ((val & 0x01) == 1);
|
||||
}
|
||||
|
||||
public synchronized void nextBytes(byte buf[]) {
|
||||
_fortuna.nextBytes(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation from sun's java.util.Random javadocs
|
||||
*/
|
||||
public double nextDouble() {
|
||||
return (((long)nextBits(26) << 27) + nextBits(27)) / (double)(1L << 53);
|
||||
}
|
||||
/**
|
||||
* Implementation from sun's java.util.Random javadocs
|
||||
*/
|
||||
public float nextFloat() {
|
||||
return nextBits(24) / ((float)(1 << 24));
|
||||
}
|
||||
/**
|
||||
* Implementation from sun's java.util.Random javadocs
|
||||
*/
|
||||
public synchronized double nextGaussian() {
|
||||
if (_haveNextGaussian) {
|
||||
_haveNextGaussian = false;
|
||||
return _nextGaussian;
|
||||
} else {
|
||||
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 -2^numBits-1
|
||||
* through 2^numBits-1
|
||||
*/
|
||||
protected synchronized int nextBits(int numBits) {
|
||||
int rv = 0;
|
||||
int bytes = (numBits + 7) / 8;
|
||||
for (int i = 0; i < bytes; i++)
|
||||
rv += ((_fortuna.nextByte() & 0xFF) << i*8);
|
||||
return rv;
|
||||
}
|
||||
|
||||
public EntropyHarvester harvester() { return this; }
|
||||
|
||||
/** reseed the fortuna */
|
||||
public synchronized void feedEntropy(String source, long data, int bitoffset, int bits) {
|
||||
_fortuna.addRandomByte((byte)(data & 0xFF));
|
||||
}
|
||||
|
||||
/** reseed the fortuna */
|
||||
public synchronized void feedEntropy(String source, byte[] data, int offset, int len) {
|
||||
_fortuna.addRandomBytes(data, offset, len);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ package net.i2p.util;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.EntropyHarvester;
|
||||
import net.i2p.data.Base64;
|
||||
|
||||
/**
|
||||
* Maintain a set of PRNGs to feed the apps
|
||||
@@ -48,6 +49,9 @@ public class PooledRandomSource extends RandomSource {
|
||||
totalSize = -1;
|
||||
}
|
||||
}
|
||||
|
||||
byte buf[] = new byte[1024];
|
||||
initSeed(buf);
|
||||
for (int i = 0; i < POOL_SIZE; i++) {
|
||||
if (totalSize < 0)
|
||||
_pool[i] = new BufferedRandomSource(context);
|
||||
@@ -55,8 +59,14 @@ public class PooledRandomSource extends RandomSource {
|
||||
_pool[i] = new BufferedRandomSource(context, (totalSize*1024) / POOL_SIZE);
|
||||
else
|
||||
_pool[i] = new RandomSource(context);
|
||||
_pool[i].nextBoolean();
|
||||
_pool[i].setSeed(buf);
|
||||
if (i > 0) {
|
||||
_pool[i-1].nextBytes(buf);
|
||||
_pool[i].setSeed(buf);
|
||||
}
|
||||
}
|
||||
_pool[0].nextBytes(buf);
|
||||
System.out.println("seeded and initialized: " + Base64.encode(buf));
|
||||
_nextPool = 0;
|
||||
}
|
||||
|
||||
@@ -174,7 +184,11 @@ public class PooledRandomSource extends RandomSource {
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
PooledRandomSource prng = new PooledRandomSource(I2PAppContext.getGlobalContext());
|
||||
//PooledRandomSource prng = new PooledRandomSource(I2PAppContext.getGlobalContext());
|
||||
long start = System.currentTimeMillis();
|
||||
RandomSource prng = I2PAppContext.getGlobalContext().random();
|
||||
long created = System.currentTimeMillis();
|
||||
System.out.println("prng type: " + prng.getClass().getName());
|
||||
int size = 8*1024*1024;
|
||||
try {
|
||||
java.io.FileOutputStream out = new java.io.FileOutputStream("random.file");
|
||||
@@ -183,6 +197,8 @@ public class PooledRandomSource extends RandomSource {
|
||||
}
|
||||
out.close();
|
||||
} catch (Exception e) { e.printStackTrace(); }
|
||||
System.out.println("Written to random.file");
|
||||
long done = System.currentTimeMillis();
|
||||
System.out.println("Written to random.file: create took " + (created-start) + ", generate took " + (done-created));
|
||||
prng.saveSeed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,13 +13,19 @@ import java.security.SecureRandom;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.EntropyHarvester;
|
||||
import net.i2p.data.Base64;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Singleton for whatever PRNG i2p uses.
|
||||
*
|
||||
* @author jrandom
|
||||
*/
|
||||
public class RandomSource extends SecureRandom {
|
||||
public class RandomSource extends SecureRandom implements EntropyHarvester {
|
||||
private Log _log;
|
||||
private EntropyHarvester _entropyHarvester;
|
||||
protected I2PAppContext _context;
|
||||
@@ -30,7 +36,7 @@ public class RandomSource extends SecureRandom {
|
||||
_log = context.logManager().getLog(RandomSource.class);
|
||||
// when we replace to have hooks for fortuna (etc), replace with
|
||||
// a factory (or just a factory method)
|
||||
_entropyHarvester = new DummyEntropyHarvester();
|
||||
_entropyHarvester = this;
|
||||
}
|
||||
public static RandomSource getInstance() {
|
||||
return I2PAppContext.getGlobalContext().random();
|
||||
@@ -101,9 +107,105 @@ public class RandomSource extends SecureRandom {
|
||||
|
||||
public EntropyHarvester harvester() { return _entropyHarvester; }
|
||||
|
||||
public void feedEntropy(String source, long data, int bitoffset, int bits) {
|
||||
if (bitoffset == 0)
|
||||
setSeed(data);
|
||||
}
|
||||
|
||||
public void feedEntropy(String source, byte[] data, int offset, int len) {
|
||||
if ( (offset == 0) && (len == data.length) ) {
|
||||
setSeed(data);
|
||||
} else {
|
||||
setSeed(_context.sha().calculateHash(data, offset, len).getData());
|
||||
}
|
||||
}
|
||||
|
||||
public void loadSeed() {
|
||||
byte buf[] = new byte[1024];
|
||||
if (initSeed(buf))
|
||||
setSeed(buf);
|
||||
}
|
||||
|
||||
public void saveSeed() {
|
||||
byte buf[] = new byte[1024];
|
||||
nextBytes(buf);
|
||||
writeSeed(buf);
|
||||
}
|
||||
|
||||
private static final String SEEDFILE = "prngseed.rnd";
|
||||
|
||||
public static final void writeSeed(byte buf[]) {
|
||||
File f = new File(SEEDFILE);
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(f);
|
||||
fos.write(buf);
|
||||
} catch (IOException ioe) {
|
||||
// ignore
|
||||
} finally {
|
||||
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean initSeed(byte buf[]) {
|
||||
// why urandom? because /dev/random blocks, and there are arguments
|
||||
// suggesting such blockages are largely meaningless
|
||||
boolean ok = seedFromFile("/dev/urandom", buf);
|
||||
// we merge (XOR) in the data from /dev/urandom with our own seedfile
|
||||
ok = seedFromFile("prngseed.rnd", buf) || ok;
|
||||
return ok;
|
||||
}
|
||||
|
||||
private static final boolean seedFromFile(String filename, byte buf[]) {
|
||||
File f = new File(filename);
|
||||
if (f.exists()) {
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(f);
|
||||
int read = 0;
|
||||
byte tbuf[] = new byte[buf.length];
|
||||
while (read < buf.length) {
|
||||
int curRead = fis.read(tbuf, read, tbuf.length - read);
|
||||
if (curRead < 0)
|
||||
break;
|
||||
read += curRead;
|
||||
}
|
||||
for (int i = 0; i < read; i++)
|
||||
buf[i] ^= tbuf[i];
|
||||
return true;
|
||||
} catch (IOException ioe) {
|
||||
// ignore
|
||||
} finally {
|
||||
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void main(String args[]) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
RandomSource rs = new RandomSource(I2PAppContext.getGlobalContext());
|
||||
byte buf[] = new byte[1024];
|
||||
boolean seeded = rs.initSeed(buf);
|
||||
System.out.println("PRNG class hierarchy: ");
|
||||
Class c = rs.getClass();
|
||||
while (c != null) {
|
||||
System.out.println("\t" + c.getName());
|
||||
c = c.getSuperclass();
|
||||
}
|
||||
System.out.println("Provider: \n" + rs.getProvider());
|
||||
if (seeded) {
|
||||
System.out.println("Initialized seed: " + Base64.encode(buf));
|
||||
rs.setSeed(buf);
|
||||
}
|
||||
for (int i = 0; i < 64; i++) rs.nextBytes(buf);
|
||||
rs.saveSeed();
|
||||
}
|
||||
}
|
||||
|
||||
// noop
|
||||
private static class DummyEntropyHarvester implements EntropyHarvester {
|
||||
public void feedEntropy(String source, long data, int bitoffset, int bits) {}
|
||||
public void feedEntropy(String source, byte[] data, int offset, int len) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user