Skip to content

Commit

Permalink
Added entropy pool
Browse files Browse the repository at this point in the history
  • Loading branch information
dghgit committed Mar 25, 2017
1 parent 62cb45b commit 72d0500
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,14 @@ public byte[] generateSeed(int numBytes)
{
return EntropyUtil.generateSeed(entropySource, numBytes);
}

/**
* Force a reseed of the DRBG
*
* @param additionalInput optional additional input
*/
public void reseed(byte[] additionalInput)
{
drbg.reseed(additionalInput);
}
}
120 changes: 117 additions & 3 deletions prov/src/main/java/org/bouncycastle/jcajce/provider/drbg/DRBG.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@
import java.security.Provider;
import java.security.SecureRandom;
import java.security.SecureRandomSpi;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.prng.EntropySource;
import org.bouncycastle.crypto.prng.EntropySourceProvider;
import org.bouncycastle.crypto.prng.SP800SecureRandom;
import org.bouncycastle.crypto.prng.SP800SecureRandomBuilder;
import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider;
Expand Down Expand Up @@ -108,15 +113,22 @@ private static SecureRandom createBaseRandom(boolean isPredictionResistant)

EntropySource initSource = entropyProvider.get(16 * 8);

byte[] personalisationString = isPredictionResistant ? generateDefaultPersonalizationString(initSource.getEntropy())
: generateNonceIVPersonalizationString(initSource.getEntropy());

return new SP800SecureRandomBuilder(entropyProvider)
.setPersonalizationString(generateDefaultPersonalizationString(initSource.getEntropy()))
.setPersonalizationString(personalisationString)
.buildHash(new SHA512Digest(), Arrays.concatenate(initSource.getEntropy(), initSource.getEntropy()), isPredictionResistant);
}
else
{
SecureRandom randomSource = createInitialEntropySource(); // needs to be done late, can't use static
SecureRandom randomSource = new HybridSecureRandom(); // needs to be done late, can't use static

byte[] personalisationString = isPredictionResistant ? generateDefaultPersonalizationString(randomSource.generateSeed(16))
: generateNonceIVPersonalizationString(randomSource.generateSeed(16));

return new SP800SecureRandomBuilder(randomSource, true)
.setPersonalizationString(generateDefaultPersonalizationString(randomSource.generateSeed(16)))
.setPersonalizationString(personalisationString)
.buildHash(new SHA512Digest(), randomSource.generateSeed(32), isPredictionResistant);
}
}
Expand Down Expand Up @@ -196,4 +208,106 @@ private static byte[] generateNonceIVPersonalizationString(byte[] seed)
return Arrays.concatenate(Strings.toByteArray("Nonce"), seed,
Pack.longToLittleEndian(Thread.currentThread().getId()), Pack.longToLittleEndian(System.currentTimeMillis()));
}

private static class HybridSecureRandom
extends SecureRandom
{
private final AtomicBoolean seedAvailable = new AtomicBoolean(false);
private final AtomicInteger samples = new AtomicInteger(0);
private final SecureRandom baseRandom = createInitialEntropySource();
private final SP800SecureRandom drbg;

HybridSecureRandom()
{
drbg = new SP800SecureRandomBuilder(new EntropySourceProvider()
{
public EntropySource get(final int bitsRequired)
{
return new SignallingEntropySource(bitsRequired);
}
})
.setPersonalizationString(Strings.toByteArray("Bouncy Castle Hybrid Entropy Source"))
.buildHMAC(new HMac(new SHA512Digest()), baseRandom.generateSeed(32), false); // 32 byte nonce
}

public byte[] generateSeed(int numBytes)
{
byte[] data = new byte[numBytes];

// after 20 samples we'll start to check if there is new seed material.
if (samples.getAndIncrement() > 20)
{
if (seedAvailable.getAndSet(false))
{
samples.set(0);
drbg.reseed(null);
}
}

drbg.nextBytes(data);

return data;
}

private class SignallingEntropySource
implements EntropySource
{
private final int byteLength;
private final AtomicReference entropy = new AtomicReference();
private final AtomicBoolean scheduled = new AtomicBoolean(false);

SignallingEntropySource(int bitsRequired)
{
this.byteLength = (bitsRequired + 7) / 8;
}

public boolean isPredictionResistant()
{
return true;
}

public byte[] getEntropy()
{
byte[] seed = (byte[])entropy.getAndSet(null);

if (seed == null || seed.length != byteLength)
{
seed = baseRandom.generateSeed(byteLength);
}
else
{
scheduled.set(false);
}

if (!scheduled.getAndSet(true))
{
new Thread(new EntropyGatherer(byteLength)).start();
}

return seed;
}

public int entropySize()
{
return byteLength * 8;
}

private class EntropyGatherer
implements Runnable
{
private final int numBytes;

EntropyGatherer(int numBytes)
{
this.numBytes = numBytes;
}

public void run()
{
entropy.set(baseRandom.generateSeed(numBytes));
seedAvailable.set(true);
}
}
}
}
}

0 comments on commit 72d0500

Please sign in to comment.