PRNG: Switch to /dev/random by default

This commit is contained in:
zzz
2025-04-04 12:00:55 +00:00
parent ec1458d75d
commit 8ac8ad4238
4 changed files with 41 additions and 68 deletions

View File

@ -40,14 +40,13 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl
private AsyncBuffer _currentBuffer; private AsyncBuffer _currentBuffer;
public AsyncFortunaStandalone(I2PAppContext context) { public AsyncFortunaStandalone(I2PAppContext context) {
super(context.getBooleanProperty("prng.useDevRandom") && !SystemVersion.isWindows() && !SystemVersion.isSlow()); super(context.getBooleanPropertyDefaultTrue("prng.useDevRandom") && !SystemVersion.isWindows() && !SystemVersion.isSlow());
_bufferCount = Math.max(context.getProperty("prng.buffers", DEFAULT_BUFFERS), 2); _bufferCount = Math.max(context.getProperty("prng.buffers", DEFAULT_BUFFERS), 2);
_bufferSize = Math.max(context.getProperty("prng.bufferSize", DEFAULT_BUFSIZE), 16*1024); _bufferSize = Math.max(context.getProperty("prng.bufferSize", DEFAULT_BUFSIZE), 16*1024);
_emptyBuffers = new LinkedBlockingQueue<AsyncBuffer>(_bufferCount); _emptyBuffers = new LinkedBlockingQueue<AsyncBuffer>(_bufferCount);
_fullBuffers = new LinkedBlockingQueue<AsyncBuffer>(_bufferCount); _fullBuffers = new LinkedBlockingQueue<AsyncBuffer>(_bufferCount);
_context = context; _context = context;
context.statManager().createRateStat("prng.bufferWaitTime", "Delay for random number buffer (ms)", "Encryption", new long[] { 60*1000, 10*60*1000, 60*60*1000 } ); context.statManager().createRateStat("prng.bufferFillTime", "Time to fill random number buffer (ms)", "Encryption", new long[] { 60*60*1000 } );
context.statManager().createRateStat("prng.bufferFillTime", "Time to fill random number buffer (ms)", "Encryption", new long[] { 60*1000, 10*60*1000, 60*60*1000 } );
_log = context.logManager().getLog(AsyncFortunaStandalone.class); _log = context.logManager().getLog(AsyncFortunaStandalone.class);
} }
@ -80,12 +79,8 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl
public void seed(byte val[]) { public void seed(byte val[]) {
Map<String, byte[]> props = Collections.singletonMap(SEED, val); Map<String, byte[]> props = Collections.singletonMap(SEED, val);
init(props); init(props);
//fillBlock();
} }
@Override
protected void allocBuffer() {}
private static class AsyncBuffer { private static class AsyncBuffer {
public final byte[] buffer; public final byte[] buffer;
@ -103,7 +98,6 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl
AsyncBuffer old = _currentBuffer; AsyncBuffer old = _currentBuffer;
if (old != null) if (old != null)
_emptyBuffers.offer(old); _emptyBuffers.offer(old);
long before = System.currentTimeMillis();
AsyncBuffer nextBuffer = null; AsyncBuffer nextBuffer = null;
while (nextBuffer == null) { while (nextBuffer == null) {
@ -115,11 +109,6 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl
continue; continue;
} }
} }
long waited = System.currentTimeMillis()-before;
_context.statManager().addRateData("prng.bufferWaitTime", waited, 0);
if (waited > 10*1000 && _log.shouldLog(Log.WARN))
_log.warn(Thread.currentThread().getName() + ": Took " + waited
+ "ms for a full PRNG buffer to be found");
_currentBuffer = nextBuffer; _currentBuffer = nextBuffer;
buffer = nextBuffer.buffer; buffer = nextBuffer.buffer;
} }
@ -162,26 +151,22 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl
} }
private void doFill(byte buf[]) { private void doFill(byte buf[]) {
//long start = System.currentTimeMillis(); if (pools != null) {
if (pool0Count >= MIN_POOL_SIZE if (pool0Count >= MIN_POOL_SIZE
&& System.currentTimeMillis() - lastReseed > 100) && System.currentTimeMillis() - lastReseed > 100)
{
reseedCount++;
//byte[] seed = new byte[0];
for (int i = 0; i < NUM_POOLS; i++)
{ {
if (reseedCount % (1 << i) == 0) { reseedCount++;
generator.addRandomBytes(pools[i].digest()); //byte[] seed = new byte[0];
} for (int i = 0; i < NUM_POOLS; i++)
{
if (reseedCount % (1 << i) == 0) {
generator.addRandomBytes(pools[i].digest());
}
}
lastReseed = System.currentTimeMillis();
} }
lastReseed = System.currentTimeMillis(); } // else we're using DevRandom
}
generator.nextBytes(buf); generator.nextBytes(buf);
//long now = System.currentTimeMillis();
//long diff = now-lastRefill;
//lastRefill = now;
//long refillTime = now-start;
//System.out.println("Refilling " + (++refillCount) + " after " + diff + " for the PRNG took " + refillTime);
} }
/***** /*****

View File

@ -18,10 +18,17 @@ class DevRandom implements IRandomStandalone {
private static final String F = "/dev/random"; private static final String F = "/dev/random";
private final File file = new File(F); private final File file = new File(F);
/**
* @since 0.9.66
*/
public static boolean isSupported() {
return (new File(F)).canRead();
}
public String name() { return F; } public String name() { return F; }
public void init(Map<String, byte[]> attributes) { public void init(Map<String, byte[]> attributes) {
if (!file.canRead()) if (!isSupported())
throw new IllegalStateException("Cannot open " + F); throw new IllegalStateException("Cannot open " + F);
} }
@ -29,9 +36,6 @@ class DevRandom implements IRandomStandalone {
throw new IllegalStateException("unsupported"); throw new IllegalStateException("unsupported");
} }
/**
* @since 0.9.58 added to interface
*/
public void nextBytes(byte[] out) throws IllegalStateException { public void nextBytes(byte[] out) throws IllegalStateException {
nextBytes(out, 0, out.length); nextBytes(out, 0, out.length);
} }

View File

@ -48,7 +48,6 @@ import java.io.Serializable;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Map; import java.util.Map;
import net.i2p.crypto.CryptixAESKeyCache; import net.i2p.crypto.CryptixAESKeyCache;
@ -104,6 +103,7 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl
static final int NUM_POOLS = 32; static final int NUM_POOLS = 32;
static final int MIN_POOL_SIZE = 64; static final int MIN_POOL_SIZE = 64;
protected final IRandomStandalone generator; protected final IRandomStandalone generator;
/** null if using DevRandom */
protected final MessageDigest[] pools; protected final MessageDigest[] pools;
protected long lastReseed; protected long lastReseed;
private int pool; private int pool;
@ -121,23 +121,22 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl
*/ */
public FortunaStandalone(boolean useDevRandom) { public FortunaStandalone(boolean useDevRandom) {
super("Fortuna i2p"); super("Fortuna i2p");
if (useDevRandom && !DevRandom.isSupported())
useDevRandom = false;
generator = useDevRandom ? new DevRandom() : new Generator(); generator = useDevRandom ? new DevRandom() : new Generator();
pools = new MessageDigest[NUM_POOLS]; if (useDevRandom) {
for (int i = 0; i < NUM_POOLS; i++) pools = null;
pools[i] = SHA256Generator.getDigestInstance(); } else {
allocBuffer(); pools = new MessageDigest[NUM_POOLS];
} for (int i = 0; i < NUM_POOLS; i++) {
pools[i] = SHA256Generator.getDigestInstance();
/** Unused, see AsyncFortunaStandalone */ }
protected void allocBuffer() { }
buffer = new byte[64*1024];
} }
/** Unused, see AsyncFortunaStandalone */ /** Unused, see AsyncFortunaStandalone */
public void seed(byte val[]) { public void seed(byte val[]) {
Map<String, byte[]> props = Collections.singletonMap(SEED, val); throw new UnsupportedOperationException("use override");
init(props);
fillBlock();
} }
public void setup(Map<String, byte[]> attributes) public void setup(Map<String, byte[]> attributes)
@ -152,31 +151,14 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl
/** Unused, see AsyncFortunaStandalone */ /** Unused, see AsyncFortunaStandalone */
public void fillBlock() public void fillBlock()
{ {
//long start = System.currentTimeMillis(); throw new UnsupportedOperationException("use override");
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) {
generator.addRandomBytes(pools[i].digest());
}
}
lastReseed = System.currentTimeMillis();
}
generator.nextBytes(buffer);
//long now = System.currentTimeMillis();
//long diff = now-lastRefill;
//lastRefill = now;
//long refillTime = now-start;
//System.out.println("Refilling " + (++refillCount) + " after " + diff + " for the PRNG took " + refillTime);
} }
@Override @Override
public void addRandomByte(byte b) public void addRandomByte(byte b)
{ {
if (pools == null)
return;
pools[pool].update(b); pools[pool].update(b);
if (pool == 0) if (pool == 0)
pool0Count++; pool0Count++;
@ -186,6 +168,8 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl
@Override @Override
public void addRandomBytes(byte[] buf, int offset, int length) public void addRandomBytes(byte[] buf, int offset, int length)
{ {
if (pools == null)
return;
pools[pool].update(buf, offset, length); pools[pool].update(buf, offset, length);
if (pool == 0) if (pool == 0)
pool0Count += length; pool0Count += length;

View File

@ -170,7 +170,7 @@ public class RouterContext extends I2PAppContext {
// or about 2 seconds per buffer - so about 200x faster // or about 2 seconds per buffer - so about 200x faster
// to fill than to drain - so we don't need too many // to fill than to drain - so we don't need too many
long maxMemory = SystemVersion.getMaxMemory(); long maxMemory = SystemVersion.getMaxMemory();
long maxBuffs = (SystemVersion.isAndroid() || SystemVersion.isARM()) ? 3 : 5; long maxBuffs = 3;
long buffs = Math.min(maxBuffs, Math.max(2, maxMemory / (21 * 1024 * 1024))); long buffs = Math.min(maxBuffs, Math.max(2, maxMemory / (21 * 1024 * 1024)));
envProps.setProperty("prng.buffers", Long.toString(buffs)); envProps.setProperty("prng.buffers", Long.toString(buffs));
} }