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:
jrandom
2005-10-22 18:06:02 +00:00
committed by zzz
parent 3fbc6f41af
commit c7b9525d2c
15 changed files with 1283 additions and 15 deletions

View File

@ -0,0 +1,183 @@
package gnu.crypto.prng;
// ----------------------------------------------------------------------------
// Copyright (C) 2001, 2002, Free Software Foundation, Inc.
//
// This file is part of GNU Crypto.
//
// GNU Crypto is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// GNU Crypto is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; see the file COPYING. If not, write to the
//
// Free Software Foundation Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA 02110-1301
// USA
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library. Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//
// As a special exception, the copyright holders of this library give
// you permission to link this library with independent modules to
// produce an executable, regardless of the license terms of these
// independent modules, and to copy and distribute the resulting
// executable under terms of your choice, provided that you also meet,
// for each linked independent module, the terms and conditions of the
// license of that module. An independent module is a module which is
// not derived from or based on this library. If you modify this
// library, you may extend this exception to your version of the
// library, but you are not obligated to do so. If you do not wish to
// do so, delete this exception statement from your version.
// ----------------------------------------------------------------------------
import java.util.Map;
/**
* <p>An abstract class to facilitate implementing PRNG algorithms.</p>
*
* Modified slightly by jrandom for I2P (removing unneeded exceptions)
* @version $Revision: 1.12 $
*/
public abstract class BasePRNG implements IRandom {
// Constants and variables
// -------------------------------------------------------------------------
/** The canonical name prefix of the PRNG algorithm. */
protected String name;
/** Indicate if this instance has already been initialised or not. */
protected boolean initialised;
/** A temporary buffer to serve random bytes. */
protected byte[] buffer;
/** The index into buffer of where the next byte will come from. */
protected int ndx;
// Constructor(s)
// -------------------------------------------------------------------------
/**
* <p>Trivial constructor for use by concrete subclasses.</p>
*
* @param name the canonical name of this instance.
*/
protected BasePRNG(String name) {
super();
this.name = name;
initialised = false;
buffer = new byte[0];
}
// Class methods
// -------------------------------------------------------------------------
// Instance methods
// -------------------------------------------------------------------------
// IRandom interface implementation ----------------------------------------
public String name() {
return name;
}
public void init(Map attributes) {
this.setup(attributes);
ndx = 0;
initialised = true;
}
public byte nextByte() throws IllegalStateException {//, LimitReachedException {
if (!initialised) {
throw new IllegalStateException();
}
return nextByteInternal();
}
public void nextBytes(byte[] out) throws IllegalStateException {//, LimitReachedException {
nextBytes(out, 0, out.length);
}
public void nextBytes(byte[] out, int offset, int length)
throws IllegalStateException //, LimitReachedException
{
if (!initialised)
throw new IllegalStateException("not initialized");
if (length == 0)
return;
if (offset < 0 || length < 0 || offset + length > out.length)
throw new ArrayIndexOutOfBoundsException("offset=" + offset + " length="
+ length + " limit=" + out.length);
if (ndx >= buffer.length) {
fillBlock();
ndx = 0;
}
int count = 0;
while (count < length) {
int amount = Math.min(buffer.length - ndx, length - count);
System.arraycopy(buffer, ndx, out, offset+count, amount);
count += amount;
ndx += amount;
if (ndx >= buffer.length) {
fillBlock();
ndx = 0;
}
}
}
public void addRandomByte(byte b) {
throw new UnsupportedOperationException("random state is non-modifiable");
}
public void addRandomBytes(byte[] buffer) {
addRandomBytes(buffer, 0, buffer.length);
}
public void addRandomBytes(byte[] buffer, int offset, int length) {
throw new UnsupportedOperationException("random state is non-modifiable");
}
// Instance methods
// -------------------------------------------------------------------------
public boolean isInitialised() {
return initialised;
}
private byte nextByteInternal() {//throws LimitReachedException {
if (ndx >= buffer.length) {
this.fillBlock();
ndx = 0;
}
return buffer[ndx++];
}
// abstract methods to implement by subclasses -----------------------------
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
public abstract void setup(Map attributes);
public abstract void fillBlock(); //throws LimitReachedException;
}

View File

@ -0,0 +1,358 @@
/* Fortuna.java -- The Fortuna PRNG.
Copyright (C) 2004 Free Software Foundation, Inc.
This file is part of GNU Crypto.
GNU Crypto is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
GNU Crypto is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to the
Free Software Foundation Inc.,
51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301
USA
Linking this library statically or dynamically with other modules is
making a combined work based on this library. Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module. An independent module is a module which is not derived from
or based on this library. If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
package gnu.crypto.prng;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.security.InvalidKeyException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import org.bouncycastle.crypto.digests.SHA256Digest;
import net.i2p.crypto.CryptixRijndael_Algorithm;
import net.i2p.crypto.CryptixAESKeyCache;
/**
* The Fortuna continuously-seeded pseudo-random number generator. This
* generator is composed of two major pieces: the entropy accumulator
* and the generator function. The former takes in random bits and
* incorporates them into the generator's state. The latter takes this
* base entropy and generates pseudo-random bits from it.
*
* <p>There are some things users of this class <em>must</em> be aware of:
*
* <dl>
* <dt>Adding Random Data</dt>
* <dd>This class does not do any polling of random sources, but rather
* provides an interface for adding random events. Applications that use
* this code <em>must</em> provide this mechanism. We use this design
* because an application writer who knows the system he is targeting
* is in a better position to judge what random data is available.</dd>
*
* <dt>Storing the Seed</dt>
* <dd>This class implements {@link Serializable} in such a way that it
* writes a 64 byte seed to the stream, and reads it back again when being
* deserialized. This is the extent of seed file management, however, and
* those using this class are encouraged to think deeply about when, how
* often, and where to store the seed.</dd>
* </dl>
*
* <p><b>References:</b></p>
*
* <ul>
* <li>Niels Ferguson and Bruce Schneier, <i>Practical Cryptography</i>,
* pp. 155--184. Wiley Publishing, Indianapolis. (2003 Niels Ferguson and
* Bruce Schneier). ISBN 0-471-22357-3.</li>
* </ul>
*
* Modified by jrandom for I2P to use Bouncycastle's SHA256, Cryptix's AES,
* to strip out some unnecessary dependencies and increase the buffer size.
*
*/
public class Fortuna extends BasePRNG
implements Serializable, RandomEventListener
{
private static final long serialVersionUID = 0xFACADE;
private static final int SEED_FILE_SIZE = 64;
private static final int NUM_POOLS = 32;
private static final int MIN_POOL_SIZE = 64;
private final Generator generator;
private final SHA256Digest[] pools;
private long lastReseed;
private int pool;
private int pool0Count;
private int reseedCount;
public static final String SEED = "gnu.crypto.prng.fortuna.seed";
public Fortuna()
{
super("Fortuna i2p");
generator = new Generator();
pools = new SHA256Digest[NUM_POOLS];
for (int i = 0; i < NUM_POOLS; i++)
pools[i] = new SHA256Digest();
lastReseed = 0;
pool = 0;
pool0Count = 0;
buffer = new byte[4*1024*1024]; //256]; // larger buffer to reduce churn
}
public void seed(byte val[]) {
Map props = new HashMap(1);
props.put(SEED, (Object)val);
init(props);
fillBlock();
}
public void setup(Map attributes)
{
lastReseed = 0;
reseedCount = 0;
pool = 0;
pool0Count = 0;
generator.init(attributes);
}
/** fillBlock is not thread safe, so will be locked anyway */
private byte fillBlockBuf[] = new byte[32];
public void fillBlock()
{
if (pool0Count >= MIN_POOL_SIZE
&& System.currentTimeMillis() - lastReseed > 100)
{
reseedCount++;
//byte[] seed = new byte[0];
for (int i = 0; i < NUM_POOLS; i++)
{
if (reseedCount % (1 << i) == 0) {
byte buf[] = fillBlockBuf;//new byte[32];
pools[i].doFinal(buf, 0);
generator.addRandomBytes(buf);//pools[i].digest());
}
}
lastReseed = System.currentTimeMillis();
}
generator.nextBytes(buffer);
}
public void addRandomByte(byte b)
{
pools[pool].update(b);
if (pool == 0)
pool0Count++;
pool = (pool + 1) % NUM_POOLS;
}
public void addRandomBytes(byte[] buf, int offset, int length)
{
pools[pool].update(buf, offset, length);
if (pool == 0)
pool0Count += length;
pool = (pool + 1) % NUM_POOLS;
}
public void addRandomEvent(RandomEvent event)
{
if (event.getPoolNumber() < 0 || event.getPoolNumber() >= pools.length)
throw new IllegalArgumentException("pool number out of range: "
+ event.getPoolNumber());
pools[event.getPoolNumber()].update(event.getSourceNumber());
pools[event.getPoolNumber()].update((byte) event.getData().length);
byte data[] = event.getData();
pools[event.getPoolNumber()].update(data, 0, data.length); //event.getData());
if (event.getPoolNumber() == 0)
pool0Count += event.getData().length;
}
// Reading and writing this object is equivalent to storing and retrieving
// the seed.
private void writeObject(ObjectOutputStream out) throws IOException
{
byte[] seed = new byte[SEED_FILE_SIZE];
generator.nextBytes(seed);
out.write(seed);
}
private void readObject(ObjectInputStream in) throws IOException
{
byte[] seed = new byte[SEED_FILE_SIZE];
in.readFully(seed);
generator.addRandomBytes(seed);
}
/**
* The Fortuna generator function. The generator is a PRNG in its own
* right; Fortuna itself is basically a wrapper around this generator
* that manages reseeding in a secure way.
*/
public static class Generator extends BasePRNG implements Cloneable
{
private static final int LIMIT = 1 << 20;
private final SHA256Digest hash;
private final byte[] counter;
private final byte[] key;
/** current encryption key built from the keying material */
private Object cryptixKey;
private CryptixAESKeyCache.KeyCacheEntry cryptixKeyBuf;
private boolean seeded;
public Generator ()
{
super("Fortuna.generator.i2p");
this.hash = new SHA256Digest();
counter = new byte[16]; //cipher.defaultBlockSize()];
buffer = new byte[16]; //cipher.defaultBlockSize()];
int keysize = 32;
key = new byte[keysize];
cryptixKeyBuf = CryptixAESKeyCache.createNew();
}
public final byte nextByte()
{
byte[] b = new byte[1];
nextBytes(b, 0, 1);
return b[0];
}
public final void nextBytes(byte[] out, int offset, int length)
{
if (!seeded)
throw new IllegalStateException("generator not seeded");
int count = 0;
do
{
int amount = Math.min(LIMIT, length - count);
super.nextBytes(out, offset+count, amount);
count += amount;
for (int i = 0; i < key.length; i += counter.length)
{
//fillBlock(); // inlined
CryptixRijndael_Algorithm.blockEncrypt(counter, buffer, 0, 0, cryptixKey);
incrementCounter();
int l = Math.min(key.length - i, 16);//cipher.currentBlockSize());
System.arraycopy(buffer, 0, key, i, l);
}
resetKey();
}
while (count < length);
//fillBlock(); // inlined
CryptixRijndael_Algorithm.blockEncrypt(counter, buffer, 0, 0, cryptixKey);
incrementCounter();
ndx = 0;
}
public final void addRandomByte(byte b)
{
addRandomBytes(new byte[] { b });
}
public final void addRandomBytes(byte[] seed, int offset, int length)
{
hash.update(key, 0, key.length);
hash.update(seed, offset, length);
//byte[] newkey = hash.digest();
//System.arraycopy(newkey, 0, key, 0, Math.min(key.length, newkey.length));
hash.doFinal(key, 0);
resetKey();
incrementCounter();
seeded = true;
}
public final void fillBlock()
{
////i2p: this is not being checked as a microoptimization
//if (!seeded)
// throw new IllegalStateException("generator not seeded");
CryptixRijndael_Algorithm.blockEncrypt(counter, buffer, 0, 0, cryptixKey);
incrementCounter();
}
public void setup(Map attributes)
{
seeded = false;
Arrays.fill(key, (byte) 0);
Arrays.fill(counter, (byte) 0);
byte[] seed = (byte[]) attributes.get(SEED);
if (seed != null)
addRandomBytes(seed);
}
/**
* Resets the cipher's key. This is done after every reseed, which
* combines the old key and the seed, and processes that throigh the
* hash function.
*/
private final void resetKey()
{
try {
cryptixKey = CryptixRijndael_Algorithm.makeKey(key, 16, cryptixKeyBuf);
} catch (InvalidKeyException ike) {
throw new Error("hrmf", ike);
}
}
/**
* Increment `counter' as a sixteen-byte little-endian unsigned integer
* by one.
*/
private final void incrementCounter()
{
for (int i = 0; i < counter.length; i++)
{
counter[i]++;
if (counter[i] != 0)
break;
}
}
}
public static void main(String args[]) {
Fortuna f = new Fortuna();
java.util.HashMap props = new java.util.HashMap();
byte initSeed[] = new byte[1234];
new java.util.Random().nextBytes(initSeed);
long before = System.currentTimeMillis();
props.put(SEED, (byte[])initSeed);
f.init(props);
byte buf[] = new byte[8*1024];
for (int i = 0; i < 64*1024; i++) {
f.nextBytes(buf);
}
long time = System.currentTimeMillis() - before;
System.out.println("512MB took " + time + ", or " + (8*64d)/((double)time/1000d) +"MBps");
}
}

View File

@ -0,0 +1,186 @@
package gnu.crypto.prng;
// ----------------------------------------------------------------------------
// $Id: IRandom.java,v 1.12 2005/10/06 04:24:17 rsdio Exp $
//
// Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
//
// This file is part of GNU Crypto.
//
// GNU Crypto is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// GNU Crypto is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; see the file COPYING. If not, write to the
//
// Free Software Foundation Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA 02110-1301
// USA
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library. Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//
// As a special exception, the copyright holders of this library give
// you permission to link this library with independent modules to
// produce an executable, regardless of the license terms of these
// independent modules, and to copy and distribute the resulting
// executable under terms of your choice, provided that you also meet,
// for each linked independent module, the terms and conditions of the
// license of that module. An independent module is a module which is
// not derived from or based on this library. If you modify this
// library, you may extend this exception to your version of the
// library, but you are not obligated to do so. If you do not wish to
// do so, delete this exception statement from your version.
// ----------------------------------------------------------------------------
import java.util.Map;
/**
* <p>The basic visible methods of any pseudo-random number generator.</p>
*
* <p>The [HAC] defines a PRNG (as implemented in this library) as follows:</p>
*
* <ul>
* <li>"5.6 Definition: A pseudorandom bit generator (PRBG) is said to pass
* the <em>next-bit test</em> if there is no polynomial-time algorithm which,
* on input of the first <code>L</code> bits of an output sequence <code>S</code>,
* can predict the <code>(L+1)</code>st bit of <code>S</code> with a
* probability significantly grater than <code>1/2</code>."</li>
*
* <li>"5.8 Definition: A PRBG that passes the <em>next-bit test</em>
* (possibly under some plausible but unproved mathematical assumption such
* as the intractability of factoring integers) is called a
* <em>cryptographically secure pseudorandom bit generator</em> (CSPRBG)."</li>
* </ul>
*
* <p><b>IMPLEMENTATION NOTE</b>: Although all the concrete classes in this
* package implement the {@link Cloneable} interface, it is important to note
* here that such an operation, for those algorithms that use an underlting
* symmetric key block cipher, <b>DOES NOT</b> clone any session key material
* that may have been used in initialising the source PRNG (the instance to be
* cloned). Instead a clone of an already initialised PRNG, that uses and
* underlying symmetric key block cipher, is another instance with a clone of
* the same cipher that operates with the <b>same block size</b> but without any
* knowledge of neither key material nor key size.</p>
*
* <p>References:</p>
*
* <ol>
* <li><a href="http://www.cacr.math.uwaterloo.ca/hac">[HAC]</a>: Handbook of
* Applied Cryptography.<br>
* CRC Press, Inc. ISBN 0-8493-8523-7, 1997<br>
* Menezes, A., van Oorschot, P. and S. Vanstone.</li>
* </ol>
*
* @version $Revision: 1.12 $
*/
public interface IRandom extends Cloneable {
// Constants
// -------------------------------------------------------------------------
// Methods
// -------------------------------------------------------------------------
/**
* <p>Returns the canonical name of this instance.</p>
*
* @return the canonical name of this instance. */
String name();
/**
* <p>Initialises the pseudo-random number generator scheme with the
* appropriate attributes.</p>
*
* @param attributes a set of name-value pairs that describe the desired
* future instance behaviour.
* @exception IllegalArgumentException if at least one of the defined name/
* value pairs contains invalid data.
*/
void init(Map attributes);
/**
* <p>Returns the next 8 bits of random data generated from this instance.</p>
*
* @return the next 8 bits of random data generated from this instance.
* @exception IllegalStateException if the instance is not yet initialised.
* @exception LimitReachedException if this instance has reached its
* theoretical limit for generating non-repetitive pseudo-random data.
*/
byte nextByte() throws IllegalStateException, LimitReachedException;
/**
* <p>Fills the designated byte array, starting from byte at index
* <code>offset</code>, for a maximum of <code>length</code> bytes with the
* output of this generator instance.
*
* @param out the placeholder to contain the generated random bytes.
* @param offset the starting index in <i>out</i> to consider. This method
* does nothing if this parameter is not within <code>0</code> and
* <code>out.length</code>.
* @param length the maximum number of required random bytes. This method
* does nothing if this parameter is less than <code>1</code>.
* @exception IllegalStateException if the instance is not yet initialised.
* @exception LimitReachedException if this instance has reached its
* theoretical limit for generating non-repetitive pseudo-random data.
*/
void nextBytes(byte[] out, int offset, int length)
throws IllegalStateException, LimitReachedException;
/**
* <p>Supplement, or possibly replace, the random state of this PRNG with
* a random byte.</p>
*
* <p>Implementations are not required to implement this method in any
* meaningful way; this may be a no-operation, and implementations may
* throw an {@link UnsupportedOperationException}.</p>
*
* @param b The byte to add.
*/
void addRandomByte(byte b);
/**
* <p>Supplement, or possibly replace, the random state of this PRNG with
* a sequence of new random bytes.</p>
*
* <p>Implementations are not required to implement this method in any
* meaningful way; this may be a no-operation, and implementations may
* throw an {@link UnsupportedOperationException}.</p>
*
* @param in The buffer of new random bytes to add.
*/
void addRandomBytes(byte[] in);
/**
* <p>Supplement, or possibly replace, the random state of this PRNG with
* a sequence of new random bytes.</p>
*
* <p>Implementations are not required to implement this method in any
* meaningful way; this may be a no-operation, and implementations may
* throw an {@link UnsupportedOperationException}.</p>
*
* @param in The buffer of new random bytes to add.
* @param offset The offset from whence to begin reading random bytes.
* @param length The number of random bytes to add.
* @exception IndexOutOfBoundsException If <i>offset</i>, <i>length</i>,
* or <i>offset</i>+<i>length</i> is out of bounds.
*/
void addRandomBytes(byte[] in, int offset, int length);
/**
* <p>Returns a clone copy of this instance.</p>
*
* @return a clone copy of this instance.
*/
Object clone() throws CloneNotSupportedException;
}

View File

@ -0,0 +1,73 @@
package gnu.crypto.prng;
// ----------------------------------------------------------------------------
// $Id: LimitReachedException.java,v 1.5 2005/10/06 04:24:17 rsdio Exp $
//
// Copyright (C) 2001, 2002, Free Software Foundation, Inc.
//
// This file is part of GNU Crypto.
//
// GNU Crypto is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// GNU Crypto is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; see the file COPYING. If not, write to the
//
// Free Software Foundation Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA 02110-1301
// USA
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library. Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//
// As a special exception, the copyright holders of this library give
// you permission to link this library with independent modules to
// produce an executable, regardless of the license terms of these
// independent modules, and to copy and distribute the resulting
// executable under terms of your choice, provided that you also meet,
// for each linked independent module, the terms and conditions of the
// license of that module. An independent module is a module which is
// not derived from or based on this library. If you modify this
// library, you may extend this exception to your version of the
// library, but you are not obligated to do so. If you do not wish to
// do so, delete this exception statement from your version.
// ----------------------------------------------------------------------------
/**
* A checked exception that indicates that a pseudo random number generated has
* reached its theoretical limit in generating random bytes.
*
* @version $Revision: 1.5 $
*/
public class LimitReachedException extends Exception {
// Constants and variables
// -------------------------------------------------------------------------
// Constructor(s)
// -------------------------------------------------------------------------
public LimitReachedException() {
super();
}
public LimitReachedException(String msg) {
super(msg);
}
// Class methods
// -------------------------------------------------------------------------
// Instant methods
// -------------------------------------------------------------------------
}

View File

@ -0,0 +1,82 @@
/* RandomEvent.java -- a random event.
Copyright (C) 2004 Free Software Foundation, Inc.
This file is part of GNU Crypto.
GNU Crypto is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
GNU Crypto is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to the
Free Software Foundation Inc.,
51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301
USA
Linking this library statically or dynamically with other modules is
making a combined work based on this library. Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module. An independent module is a module which is not derived from
or based on this library. If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
package gnu.crypto.prng;
import java.util.EventObject;
/**
* An interface for entropy accumulators that will be notified of random
* events.
*/
public class RandomEvent extends EventObject
{
private final byte sourceNumber;
private final byte poolNumber;
private final byte[] data;
public RandomEvent(Object source, byte sourceNumber, byte poolNumber,
byte[] data)
{
super(source);
this.sourceNumber = sourceNumber;
this.poolNumber = poolNumber;
if (data.length == 0 || data.length > 32)
throw new IllegalArgumentException("random events take between 1 and 32 bytes of data");
this.data = (byte[]) data.clone();
}
public byte getSourceNumber()
{
return sourceNumber;
}
public byte getPoolNumber()
{
return poolNumber;
}
public byte[] getData()
{
return data;
}
}

View File

@ -0,0 +1,53 @@
/* RandomEventListener.java -- event listener
Copyright (C) 2004 Free Software Foundation, Inc.
This file is part of GNU Crypto.
GNU Crypto is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
GNU Crypto is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to the
Free Software Foundation Inc.,
51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301
USA
Linking this library statically or dynamically with other modules is
making a combined work based on this library. Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module. An independent module is a module which is not derived from
or based on this library. If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
package gnu.crypto.prng;
import java.util.EventListener;
/**
* An interface for entropy accumulators that will be notified of random
* events.
*/
public interface RandomEventListener extends EventListener
{
void addRandomEvent(RandomEvent event);
}

View File

@ -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;
}
}
}
}

View File

@ -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

View File

@ -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();
}
}

View 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);
}
}

View File

@ -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();
}
}

View File

@ -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) {}
}
}
}

View File

@ -1,4 +1,15 @@
$Id: history.txt,v 1.303 2005/10/20 03:56:40 jrandom Exp $
$Id: history.txt,v 1.304 2005/10/20 14:42:13 dust Exp $
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.
2005-10-20 dust
* Fix bug in ircclient that prevented it to use its own dest (i.e. was

View File

@ -770,6 +770,7 @@ public class Router {
public void shutdown(int exitCode) {
_isAlive = false;
_context.random().saveSeed();
I2PThread.removeOOMEventListener(_oomListener);
try { _context.jobQueue().shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the job queue", t); }
//try { _context.adminManager().shutdown(); } catch (Throwable t) { _log.log(Log.CRIT, "Error shutting down the admin manager", t); }

View File

@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
*
*/
public class RouterVersion {
public final static String ID = "$Revision: 1.274 $ $Date: 2005/10/20 03:56:39 $";
public final static String ID = "$Revision: 1.275 $ $Date: 2005/10/20 14:42:16 $";
public final static String VERSION = "0.6.1.3";
public final static long BUILD = 5;
public final static long BUILD = 6;
public static void main(String args[]) {
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
System.out.println("Router ID: " + RouterVersion.ID);