Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/java/org/spongycastle/crypto/prng')
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/BasicEntropySourceProvider.java53
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/DRBGProvider.java8
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/DigestRandomGenerator.java123
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/EntropySource.java25
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/EntropySourceProvider.java6
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/FixedSecureRandom.java147
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/RandomGenerator.java38
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/ReversedWindowGenerator.java111
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/SP800SecureRandom.java74
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/SP800SecureRandomBuilder.java290
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/ThreadedSeedGenerator.java95
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/VMPCRandomGenerator.java127
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/drbg/CTRSP800DRBG.java478
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/drbg/DualECPoints.java82
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/drbg/DualECSP800DRBG.java320
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/drbg/HMacSP800DRBG.java181
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/drbg/HashSP800DRBG.java284
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/drbg/SP80090DRBG.java32
-rw-r--r--core/src/main/java/org/spongycastle/crypto/prng/drbg/Utils.java103
19 files changed, 2577 insertions, 0 deletions
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/BasicEntropySourceProvider.java b/core/src/main/java/org/spongycastle/crypto/prng/BasicEntropySourceProvider.java
new file mode 100644
index 00000000..176b5fb0
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/BasicEntropySourceProvider.java
@@ -0,0 +1,53 @@
+package org.spongycastle.crypto.prng;
+
+import java.security.SecureRandom;
+
+/**
+ * An EntropySourceProvider where entropy generation is based on a SecureRandom output using SecureRandom.generateSeed().
+ */
+public class BasicEntropySourceProvider
+ implements EntropySourceProvider
+{
+ private final SecureRandom _sr;
+ private final boolean _predictionResistant;
+
+ /**
+ * Create a entropy source provider based on the passed in SecureRandom.
+ *
+ * @param random the SecureRandom to base EntropySource construction on.
+ * @param isPredictionResistant boolean indicating if the SecureRandom is based on prediction resistant entropy or not (true if it is).
+ */
+ public BasicEntropySourceProvider(SecureRandom random, boolean isPredictionResistant)
+ {
+ _sr = random;
+ _predictionResistant = isPredictionResistant;
+ }
+
+ /**
+ * Return an entropy source that will create bitsRequired bits of entropy on
+ * each invocation of getEntropy().
+ *
+ * @param bitsRequired size (in bits) of entropy to be created by the provided source.
+ * @return an EntropySource that generates bitsRequired bits of entropy on each call to its getEntropy() method.
+ */
+ public EntropySource get(final int bitsRequired)
+ {
+ return new EntropySource()
+ {
+ public boolean isPredictionResistant()
+ {
+ return _predictionResistant;
+ }
+
+ public byte[] getEntropy()
+ {
+ return _sr.generateSeed((bitsRequired + 7) / 8);
+ }
+
+ public int entropySize()
+ {
+ return bitsRequired;
+ }
+ };
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/DRBGProvider.java b/core/src/main/java/org/spongycastle/crypto/prng/DRBGProvider.java
new file mode 100644
index 00000000..f9067323
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/DRBGProvider.java
@@ -0,0 +1,8 @@
+package org.spongycastle.crypto.prng;
+
+import org.spongycastle.crypto.prng.drbg.SP80090DRBG;
+
+interface DRBGProvider
+{
+ SP80090DRBG get(EntropySource entropySource);
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/DigestRandomGenerator.java b/core/src/main/java/org/spongycastle/crypto/prng/DigestRandomGenerator.java
new file mode 100644
index 00000000..5505338a
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/DigestRandomGenerator.java
@@ -0,0 +1,123 @@
+package org.spongycastle.crypto.prng;
+
+import org.spongycastle.crypto.Digest;
+
+/**
+ * Random generation based on the digest with counter. Calling addSeedMaterial will
+ * always increase the entropy of the hash.
+ * <p>
+ * Internal access to the digest is synchronized so a single one of these can be shared.
+ * </p>
+ */
+public class DigestRandomGenerator
+ implements RandomGenerator
+{
+ private static long CYCLE_COUNT = 10;
+
+ private long stateCounter;
+ private long seedCounter;
+ private Digest digest;
+ private byte[] state;
+ private byte[] seed;
+
+ // public constructors
+ public DigestRandomGenerator(
+ Digest digest)
+ {
+ this.digest = digest;
+
+ this.seed = new byte[digest.getDigestSize()];
+ this.seedCounter = 1;
+
+ this.state = new byte[digest.getDigestSize()];
+ this.stateCounter = 1;
+ }
+
+ public void addSeedMaterial(byte[] inSeed)
+ {
+ synchronized (this)
+ {
+ digestUpdate(inSeed);
+ digestUpdate(seed);
+ digestDoFinal(seed);
+ }
+ }
+
+ public void addSeedMaterial(long rSeed)
+ {
+ synchronized (this)
+ {
+ digestAddCounter(rSeed);
+ digestUpdate(seed);
+
+ digestDoFinal(seed);
+ }
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ nextBytes(bytes, 0, bytes.length);
+ }
+
+ public void nextBytes(byte[] bytes, int start, int len)
+ {
+ synchronized (this)
+ {
+ int stateOff = 0;
+
+ generateState();
+
+ int end = start + len;
+ for (int i = start; i != end; i++)
+ {
+ if (stateOff == state.length)
+ {
+ generateState();
+ stateOff = 0;
+ }
+ bytes[i] = state[stateOff++];
+ }
+ }
+ }
+
+ private void cycleSeed()
+ {
+ digestUpdate(seed);
+ digestAddCounter(seedCounter++);
+
+ digestDoFinal(seed);
+ }
+
+ private void generateState()
+ {
+ digestAddCounter(stateCounter++);
+ digestUpdate(state);
+ digestUpdate(seed);
+
+ digestDoFinal(state);
+
+ if ((stateCounter % CYCLE_COUNT) == 0)
+ {
+ cycleSeed();
+ }
+ }
+
+ private void digestAddCounter(long seed)
+ {
+ for (int i = 0; i != 8; i++)
+ {
+ digest.update((byte)seed);
+ seed >>>= 8;
+ }
+ }
+
+ private void digestUpdate(byte[] inSeed)
+ {
+ digest.update(inSeed, 0, inSeed.length);
+ }
+
+ private void digestDoFinal(byte[] result)
+ {
+ digest.doFinal(result, 0);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/EntropySource.java b/core/src/main/java/org/spongycastle/crypto/prng/EntropySource.java
new file mode 100644
index 00000000..783f375c
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/EntropySource.java
@@ -0,0 +1,25 @@
+package org.spongycastle.crypto.prng;
+
+public interface EntropySource
+{
+ /**
+ * Return whether or not this entropy source is regarded as prediction resistant.
+ *
+ * @return true if it is, false otherwise.
+ */
+ boolean isPredictionResistant();
+
+ /**
+ * Return a byte array of entropy.
+ *
+ * @return entropy bytes.
+ */
+ byte[] getEntropy();
+
+ /**
+ * Return the number of bits of entropy this source can produce.
+ *
+ * @return size in bits of the return value of getEntropy.
+ */
+ int entropySize();
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/EntropySourceProvider.java b/core/src/main/java/org/spongycastle/crypto/prng/EntropySourceProvider.java
new file mode 100644
index 00000000..1efabb48
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/EntropySourceProvider.java
@@ -0,0 +1,6 @@
+package org.spongycastle.crypto.prng;
+
+public interface EntropySourceProvider
+{
+ EntropySource get(final int bitsRequired);
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/FixedSecureRandom.java b/core/src/main/java/org/spongycastle/crypto/prng/FixedSecureRandom.java
new file mode 100644
index 00000000..cea20329
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/FixedSecureRandom.java
@@ -0,0 +1,147 @@
+package org.spongycastle.crypto.prng;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+
+/**
+ * A secure random that returns pre-seeded data to calls of nextBytes() or generateSeed().
+ */
+public class FixedSecureRandom
+ extends SecureRandom
+{
+ private byte[] _data;
+
+ private int _index;
+ private int _intPad;
+
+ public FixedSecureRandom(byte[] value)
+ {
+ this(false, new byte[][] { value });
+ }
+
+ public FixedSecureRandom(
+ byte[][] values)
+ {
+ this(false, values);
+ }
+
+ /**
+ * Pad the data on integer boundaries. This is necessary for the classpath project's BigInteger
+ * implementation.
+ */
+ public FixedSecureRandom(
+ boolean intPad,
+ byte[] value)
+ {
+ this(intPad, new byte[][] { value });
+ }
+
+ /**
+ * Pad the data on integer boundaries. This is necessary for the classpath project's BigInteger
+ * implementation.
+ */
+ public FixedSecureRandom(
+ boolean intPad,
+ byte[][] values)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ for (int i = 0; i != values.length; i++)
+ {
+ try
+ {
+ bOut.write(values[i]);
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("can't save value array.");
+ }
+ }
+
+ _data = bOut.toByteArray();
+
+ if (intPad)
+ {
+ _intPad = _data.length % 4;
+ }
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ System.arraycopy(_data, _index, bytes, 0, bytes.length);
+
+ _index += bytes.length;
+ }
+
+ public byte[] generateSeed(int numBytes)
+ {
+ byte[] bytes = new byte[numBytes];
+
+ this.nextBytes(bytes);
+
+ return bytes;
+ }
+
+ //
+ // classpath's implementation of SecureRandom doesn't currently go back to nextBytes
+ // when next is called. We can't override next as it's a final method.
+ //
+ public int nextInt()
+ {
+ int val = 0;
+
+ val |= nextValue() << 24;
+ val |= nextValue() << 16;
+
+ if (_intPad == 2)
+ {
+ _intPad--;
+ }
+ else
+ {
+ val |= nextValue() << 8;
+ }
+
+ if (_intPad == 1)
+ {
+ _intPad--;
+ }
+ else
+ {
+ val |= nextValue();
+ }
+
+ return val;
+ }
+
+ //
+ // classpath's implementation of SecureRandom doesn't currently go back to nextBytes
+ // when next is called. We can't override next as it's a final method.
+ //
+ public long nextLong()
+ {
+ long val = 0;
+
+ val |= (long)nextValue() << 56;
+ val |= (long)nextValue() << 48;
+ val |= (long)nextValue() << 40;
+ val |= (long)nextValue() << 32;
+ val |= (long)nextValue() << 24;
+ val |= (long)nextValue() << 16;
+ val |= (long)nextValue() << 8;
+ val |= (long)nextValue();
+
+ return val;
+ }
+
+ public boolean isExhausted()
+ {
+ return _index == _data.length;
+ }
+
+ private int nextValue()
+ {
+ return _data[_index++] & 0xff;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/RandomGenerator.java b/core/src/main/java/org/spongycastle/crypto/prng/RandomGenerator.java
new file mode 100644
index 00000000..aa9a3834
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/RandomGenerator.java
@@ -0,0 +1,38 @@
+package org.spongycastle.crypto.prng;
+
+/**
+ * Generic interface for objects generating random bytes.
+ */
+public interface RandomGenerator
+{
+ /**
+ * Add more seed material to the generator.
+ *
+ * @param seed a byte array to be mixed into the generator's state.
+ */
+ void addSeedMaterial(byte[] seed);
+
+ /**
+ * Add more seed material to the generator.
+ *
+ * @param seed a long value to be mixed into the generator's state.
+ */
+ void addSeedMaterial(long seed);
+
+ /**
+ * Fill bytes with random values.
+ *
+ * @param bytes byte array to be filled.
+ */
+ void nextBytes(byte[] bytes);
+
+ /**
+ * Fill part of bytes with random values.
+ *
+ * @param bytes byte array to be filled.
+ * @param start index to start filling at.
+ * @param len length of segment to fill.
+ */
+ void nextBytes(byte[] bytes, int start, int len);
+
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/ReversedWindowGenerator.java b/core/src/main/java/org/spongycastle/crypto/prng/ReversedWindowGenerator.java
new file mode 100644
index 00000000..0e12d41d
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/ReversedWindowGenerator.java
@@ -0,0 +1,111 @@
+package org.spongycastle.crypto.prng;
+
+/**
+ * Takes bytes generated by an underling RandomGenerator and reverses the order in
+ * each small window (of configurable size).
+ * <p>
+ * Access to internals is synchronized so a single one of these can be shared.
+ * </p>
+ */
+public class ReversedWindowGenerator
+ implements RandomGenerator
+{
+ private final RandomGenerator generator;
+
+ private byte[] window;
+ private int windowCount;
+
+ public ReversedWindowGenerator(
+ RandomGenerator generator,
+ int windowSize)
+ {
+ if (generator == null)
+ {
+ throw new IllegalArgumentException("generator cannot be null");
+ }
+ if (windowSize < 2)
+ {
+ throw new IllegalArgumentException("windowSize must be at least 2");
+ }
+
+ this.generator = generator;
+ this.window = new byte[windowSize];
+ }
+
+ /**
+ * Add more seed material to the generator.
+ *
+ * @param seed a byte array to be mixed into the generator's state.
+ */
+ public void addSeedMaterial(
+ byte[] seed)
+ {
+ synchronized (this)
+ {
+ windowCount = 0;
+ generator.addSeedMaterial(seed);
+ }
+ }
+
+ /**
+ * Add more seed material to the generator.
+ *
+ * @param seed a long value to be mixed into the generator's state.
+ */
+ public void addSeedMaterial(
+ long seed)
+ {
+ synchronized (this)
+ {
+ windowCount = 0;
+ generator.addSeedMaterial(seed);
+ }
+ }
+
+ /**
+ * Fill bytes with random values.
+ *
+ * @param bytes byte array to be filled.
+ */
+ public void nextBytes(
+ byte[] bytes)
+ {
+ doNextBytes(bytes, 0, bytes.length);
+ }
+
+ /**
+ * Fill part of bytes with random values.
+ *
+ * @param bytes byte array to be filled.
+ * @param start index to start filling at.
+ * @param len length of segment to fill.
+ */
+ public void nextBytes(
+ byte[] bytes,
+ int start,
+ int len)
+ {
+ doNextBytes(bytes, start, len);
+ }
+
+ private void doNextBytes(
+ byte[] bytes,
+ int start,
+ int len)
+ {
+ synchronized (this)
+ {
+ int done = 0;
+ while (done < len)
+ {
+ if (windowCount < 1)
+ {
+ generator.nextBytes(window, 0, window.length);
+ windowCount = window.length;
+ }
+
+ bytes[start + done++] = window[--windowCount];
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/SP800SecureRandom.java b/core/src/main/java/org/spongycastle/crypto/prng/SP800SecureRandom.java
new file mode 100644
index 00000000..1a452f9e
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/SP800SecureRandom.java
@@ -0,0 +1,74 @@
+package org.spongycastle.crypto.prng;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.prng.drbg.SP80090DRBG;
+
+public class SP800SecureRandom
+ extends SecureRandom
+{
+ private final DRBGProvider drbgProvider;
+ private final boolean predictionResistant;
+ private final SecureRandom randomSource;
+ private final EntropySource entropySource;
+
+ private SP80090DRBG drbg;
+
+ SP800SecureRandom(SecureRandom randomSource, EntropySource entropySource, DRBGProvider drbgProvider, boolean predictionResistant)
+ {
+ this.randomSource = randomSource;
+ this.entropySource = entropySource;
+ this.drbgProvider = drbgProvider;
+ this.predictionResistant = predictionResistant;
+ }
+
+ public void setSeed(byte[] seed)
+ {
+ synchronized (this)
+ {
+ if (randomSource != null)
+ {
+ this.randomSource.setSeed(seed);
+ }
+ }
+ }
+
+ public void setSeed(long seed)
+ {
+ synchronized (this)
+ {
+ // this will happen when SecureRandom() is created
+ if (randomSource != null)
+ {
+ this.randomSource.setSeed(seed);
+ }
+ }
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ synchronized (this)
+ {
+ if (drbg == null)
+ {
+ drbg = drbgProvider.get(entropySource);
+ }
+
+ // check if a reseed is required...
+ if (drbg.generate(bytes, null, predictionResistant) < 0)
+ {
+ drbg.reseed(entropySource.getEntropy());
+ drbg.generate(bytes, null, predictionResistant);
+ }
+ }
+ }
+
+ public byte[] generateSeed(int numBytes)
+ {
+ byte[] bytes = new byte[numBytes];
+
+ this.nextBytes(bytes);
+
+ return bytes;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/SP800SecureRandomBuilder.java b/core/src/main/java/org/spongycastle/crypto/prng/SP800SecureRandomBuilder.java
new file mode 100644
index 00000000..6e9e3ccc
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/SP800SecureRandomBuilder.java
@@ -0,0 +1,290 @@
+package org.spongycastle.crypto.prng;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.prng.drbg.CTRSP800DRBG;
+import org.spongycastle.crypto.prng.drbg.DualECPoints;
+import org.spongycastle.crypto.prng.drbg.DualECSP800DRBG;
+import org.spongycastle.crypto.prng.drbg.HMacSP800DRBG;
+import org.spongycastle.crypto.prng.drbg.HashSP800DRBG;
+import org.spongycastle.crypto.prng.drbg.SP80090DRBG;
+
+/**
+ * Builder class for making SecureRandom objects based on SP 800-90A Deterministic Random Bit Generators (DRBG).
+ */
+public class SP800SecureRandomBuilder
+{
+ private final SecureRandom random;
+ private final EntropySourceProvider entropySourceProvider;
+
+ private byte[] personalizationString;
+ private int securityStrength = 256;
+ private int entropyBitsRequired = 256;
+
+ /**
+ * Basic constructor, creates a builder using an EntropySourceProvider based on the default SecureRandom with
+ * predictionResistant set to false.
+ * <p>
+ * Any SecureRandom created from a builder constructed like this will make use of input passed to SecureRandom.setSeed() if
+ * the default SecureRandom does for its generateSeed() call.
+ * </p>
+ */
+ public SP800SecureRandomBuilder()
+ {
+ this(new SecureRandom(), false);
+ }
+
+ /**
+ * Construct a builder with an EntropySourceProvider based on the passed in SecureRandom and the passed in value
+ * for prediction resistance.
+ * <p>
+ * Any SecureRandom created from a builder constructed like this will make use of input passed to SecureRandom.setSeed() if
+ * the passed in SecureRandom does for its generateSeed() call.
+ * </p>
+ * @param entropySource
+ * @param predictionResistant
+ */
+ public SP800SecureRandomBuilder(SecureRandom entropySource, boolean predictionResistant)
+ {
+ this.random = entropySource;
+ this.entropySourceProvider = new BasicEntropySourceProvider(random, predictionResistant);
+ }
+
+ /**
+ * Create a builder which makes creates the SecureRandom objects from a specified entropy source provider.
+ * <p>
+ * <b>Note:</b> If this constructor is used any calls to setSeed() in the resulting SecureRandom will be ignored.
+ * </p>
+ * @param entropySourceProvider a provider of EntropySource objects.
+ */
+ public SP800SecureRandomBuilder(EntropySourceProvider entropySourceProvider)
+ {
+ this.random = null;
+ this.entropySourceProvider = entropySourceProvider;
+ }
+
+ /**
+ * Set the personalization string for DRBG SecureRandoms created by this builder
+ * @param personalizationString the personalisation string for the underlying DRBG.
+ * @return the current builder.
+ */
+ public SP800SecureRandomBuilder setPersonalizationString(byte[] personalizationString)
+ {
+ this.personalizationString = personalizationString;
+
+ return this;
+ }
+
+ /**
+ * Set the security strength required for DRBGs used in building SecureRandom objects.
+ *
+ * @param securityStrength the security strength (in bits)
+ * @return the current builder.
+ */
+ public SP800SecureRandomBuilder setSecurityStrength(int securityStrength)
+ {
+ this.securityStrength = securityStrength;
+
+ return this;
+ }
+
+ /**
+ * Set the amount of entropy bits required for seeding and reseeding DRBGs used in building SecureRandom objects.
+ *
+ * @param entropyBitsRequired the number of bits of entropy to be requested from the entropy source on each seed/reseed.
+ * @return the current builder.
+ */
+ public SP800SecureRandomBuilder setEntropyBitsRequired(int entropyBitsRequired)
+ {
+ this.entropyBitsRequired = entropyBitsRequired;
+
+ return this;
+ }
+
+ /**
+ * Build a SecureRandom based on a SP 800-90A Hash DRBG.
+ *
+ * @param digest digest algorithm to use in the DRBG underneath the SecureRandom.
+ * @param nonce nonce value to use in DRBG construction.
+ * @param predictionResistant specify whether the underlying DRBG in the resulting SecureRandom should reseed on each request for bytes.
+ * @return a SecureRandom supported by a Hash DRBG.
+ */
+ public SP800SecureRandom buildHash(Digest digest, byte[] nonce, boolean predictionResistant)
+ {
+ return new SP800SecureRandom(random, entropySourceProvider.get(entropyBitsRequired), new HashDRBGProvider(digest, nonce, personalizationString, securityStrength), predictionResistant);
+ }
+
+ /**
+ * Build a SecureRandom based on a SP 800-90A CTR DRBG.
+ *
+ * @param cipher the block cipher to base the DRBG on.
+ * @param keySizeInBits key size in bits to be used with the block cipher.
+ * @param nonce nonce value to use in DRBG construction.
+ * @param predictionResistant specify whether the underlying DRBG in the resulting SecureRandom should reseed on each request for bytes.
+ * @return a SecureRandom supported by a CTR DRBG.
+ */
+ public SP800SecureRandom buildCTR(BlockCipher cipher, int keySizeInBits, byte[] nonce, boolean predictionResistant)
+ {
+ return new SP800SecureRandom(random, entropySourceProvider.get(entropyBitsRequired), new CTRDRBGProvider(cipher, keySizeInBits, nonce, personalizationString, securityStrength), predictionResistant);
+ }
+
+ /**
+ * Build a SecureRandom based on a SP 800-90A HMAC DRBG.
+ *
+ * @param hMac HMAC algorithm to use in the DRBG underneath the SecureRandom.
+ * @param nonce nonce value to use in DRBG construction.
+ * @param predictionResistant specify whether the underlying DRBG in the resulting SecureRandom should reseed on each request for bytes.
+ * @return a SecureRandom supported by a HMAC DRBG.
+ */
+ public SP800SecureRandom buildHMAC(Mac hMac, byte[] nonce, boolean predictionResistant)
+ {
+ return new SP800SecureRandom(random, entropySourceProvider.get(entropyBitsRequired), new HMacDRBGProvider(hMac, nonce, personalizationString, securityStrength), predictionResistant);
+ }
+
+ /**
+ * Build a SecureRandom based on a SP 800-90A Dual EC DRBG using the NIST point set.
+ *
+ * @param digest digest algorithm to use in the DRBG underneath the SecureRandom.
+ * @param nonce nonce value to use in DRBG construction.
+ * @param predictionResistant specify whether the underlying DRBG in the resulting SecureRandom should reseed on each request for bytes.
+ * @return a SecureRandom supported by a Dual EC DRBG.
+ */
+ public SP800SecureRandom buildDualEC(Digest digest, byte[] nonce, boolean predictionResistant)
+ {
+ return new SP800SecureRandom(random, entropySourceProvider.get(entropyBitsRequired), new DualECDRBGProvider(digest, nonce, personalizationString, securityStrength), predictionResistant);
+ }
+
+ /**
+ * Build a SecureRandom based on a SP 800-90A Dual EC DRBG according to a defined point set.
+ *
+ * @param pointSet an array of DualECPoints to use for DRB generation.
+ * @param digest digest algorithm to use in the DRBG underneath the SecureRandom.
+ * @param nonce nonce value to use in DRBG construction.
+ * @param predictionResistant specify whether the underlying DRBG in the resulting SecureRandom should reseed on each request for bytes.
+ * @return a SecureRandom supported by a Dual EC DRBG.
+ */
+ public SP800SecureRandom buildDualEC(DualECPoints[] pointSet, Digest digest, byte[] nonce, boolean predictionResistant)
+ {
+ return new SP800SecureRandom(random, entropySourceProvider.get(entropyBitsRequired), new ConfigurableDualECDRBGProvider(pointSet, digest, nonce, personalizationString, securityStrength), predictionResistant);
+ }
+
+
+ private static class HashDRBGProvider
+ implements DRBGProvider
+ {
+ private final Digest digest;
+ private final byte[] nonce;
+ private final byte[] personalizationString;
+ private final int securityStrength;
+
+ public HashDRBGProvider(Digest digest, byte[] nonce, byte[] personalizationString, int securityStrength)
+ {
+ this.digest = digest;
+ this.nonce = nonce;
+ this.personalizationString = personalizationString;
+ this.securityStrength = securityStrength;
+ }
+
+ public SP80090DRBG get(EntropySource entropySource)
+ {
+ return new HashSP800DRBG(digest, securityStrength, entropySource, personalizationString, nonce);
+ }
+ }
+
+ private static class DualECDRBGProvider
+ implements DRBGProvider
+ {
+ private final Digest digest;
+ private final byte[] nonce;
+ private final byte[] personalizationString;
+ private final int securityStrength;
+
+ public DualECDRBGProvider(Digest digest, byte[] nonce, byte[] personalizationString, int securityStrength)
+ {
+ this.digest = digest;
+ this.nonce = nonce;
+ this.personalizationString = personalizationString;
+ this.securityStrength = securityStrength;
+ }
+
+ public SP80090DRBG get(EntropySource entropySource)
+ {
+ return new DualECSP800DRBG(digest, securityStrength, entropySource, personalizationString, nonce);
+ }
+ }
+
+ private static class ConfigurableDualECDRBGProvider
+ implements DRBGProvider
+ {
+ private final DualECPoints[] pointSet;
+ private final Digest digest;
+ private final byte[] nonce;
+ private final byte[] personalizationString;
+ private final int securityStrength;
+
+ public ConfigurableDualECDRBGProvider(DualECPoints[] pointSet, Digest digest, byte[] nonce, byte[] personalizationString, int securityStrength)
+ {
+ this.pointSet = new DualECPoints[pointSet.length];
+ System.arraycopy(pointSet, 0, this.pointSet, 0, pointSet.length);
+ this.digest = digest;
+ this.nonce = nonce;
+ this.personalizationString = personalizationString;
+ this.securityStrength = securityStrength;
+ }
+
+ public SP80090DRBG get(EntropySource entropySource)
+ {
+ return new DualECSP800DRBG(pointSet, digest, securityStrength, entropySource, personalizationString, nonce);
+ }
+ }
+
+ private static class HMacDRBGProvider
+ implements DRBGProvider
+ {
+ private final Mac hMac;
+ private final byte[] nonce;
+ private final byte[] personalizationString;
+ private final int securityStrength;
+
+ public HMacDRBGProvider(Mac hMac, byte[] nonce, byte[] personalizationString, int securityStrength)
+ {
+ this.hMac = hMac;
+ this.nonce = nonce;
+ this.personalizationString = personalizationString;
+ this.securityStrength = securityStrength;
+ }
+
+ public SP80090DRBG get(EntropySource entropySource)
+ {
+ return new HMacSP800DRBG(hMac, securityStrength, entropySource, personalizationString, nonce);
+ }
+ }
+
+ private static class CTRDRBGProvider
+ implements DRBGProvider
+ {
+
+ private final BlockCipher blockCipher;
+ private final int keySizeInBits;
+ private final byte[] nonce;
+ private final byte[] personalizationString;
+ private final int securityStrength;
+
+ public CTRDRBGProvider(BlockCipher blockCipher, int keySizeInBits, byte[] nonce, byte[] personalizationString, int securityStrength)
+ {
+ this.blockCipher = blockCipher;
+ this.keySizeInBits = keySizeInBits;
+ this.nonce = nonce;
+ this.personalizationString = personalizationString;
+ this.securityStrength = securityStrength;
+ }
+
+ public SP80090DRBG get(EntropySource entropySource)
+ {
+ return new CTRSP800DRBG(blockCipher, keySizeInBits, securityStrength, entropySource, personalizationString, nonce);
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/ThreadedSeedGenerator.java b/core/src/main/java/org/spongycastle/crypto/prng/ThreadedSeedGenerator.java
new file mode 100644
index 00000000..fc103542
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/ThreadedSeedGenerator.java
@@ -0,0 +1,95 @@
+package org.spongycastle.crypto.prng;
+
+/**
+ * A thread based seed generator - one source of randomness.
+ * <p>
+ * Based on an idea from Marcus Lippert.
+ * </p>
+ */
+public class ThreadedSeedGenerator
+{
+ private class SeedGenerator
+ implements Runnable
+ {
+ private volatile int counter = 0;
+ private volatile boolean stop = false;
+
+ public void run()
+ {
+ while (!this.stop)
+ {
+ this.counter++;
+ }
+
+ }
+
+ public byte[] generateSeed(
+ int numbytes,
+ boolean fast)
+ {
+ Thread t = new Thread(this);
+ byte[] result = new byte[numbytes];
+ this.counter = 0;
+ this.stop = false;
+ int last = 0;
+ int end;
+
+ t.start();
+ if(fast)
+ {
+ end = numbytes;
+ }
+ else
+ {
+ end = numbytes * 8;
+ }
+ for (int i = 0; i < end; i++)
+ {
+ while (this.counter == last)
+ {
+ try
+ {
+ Thread.sleep(1);
+ }
+ catch (InterruptedException e)
+ {
+ // ignore
+ }
+ }
+ last = this.counter;
+ if (fast)
+ {
+ result[i] = (byte) (last & 0xff);
+ }
+ else
+ {
+ int bytepos = i/8;
+ result[bytepos] = (byte) ((result[bytepos] << 1) | (last & 1));
+ }
+
+ }
+ stop = true;
+ return result;
+ }
+ }
+
+ /**
+ * Generate seed bytes. Set fast to false for best quality.
+ * <p>
+ * If fast is set to true, the code should be round about 8 times faster when
+ * generating a long sequence of random bytes. 20 bytes of random values using
+ * the fast mode take less than half a second on a Nokia e70. If fast is set to false,
+ * it takes round about 2500 ms.
+ * </p>
+ * @param numBytes the number of bytes to generate
+ * @param fast true if fast mode should be used
+ */
+ public byte[] generateSeed(
+ int numBytes,
+ boolean fast)
+ {
+ SeedGenerator gen = new SeedGenerator();
+
+ return gen.generateSeed(numBytes, fast);
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/VMPCRandomGenerator.java b/core/src/main/java/org/spongycastle/crypto/prng/VMPCRandomGenerator.java
new file mode 100644
index 00000000..bae1bc39
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/VMPCRandomGenerator.java
@@ -0,0 +1,127 @@
+package org.spongycastle.crypto.prng;
+
+import org.spongycastle.util.Pack;
+
+public class VMPCRandomGenerator implements RandomGenerator
+{
+ private byte n = 0;
+
+ /**
+ * Permutation generated by code: <code>
+ * // First 1850 fractional digit of Pi number.
+ * byte[] key = new BigInteger("14159265358979323846...5068006422512520511").toByteArray();
+ * s = 0;
+ * P = new byte[256];
+ * for (int i = 0; i < 256; i++) {
+ * P[i] = (byte) i;
+ * }
+ * for (int m = 0; m < 768; m++) {
+ * s = P[(s + P[m & 0xff] + key[m % key.length]) & 0xff];
+ * byte temp = P[m & 0xff];
+ * P[m & 0xff] = P[s & 0xff];
+ * P[s & 0xff] = temp;
+ * } </code>
+ */
+ private byte[] P =
+ {
+ (byte) 0xbb, (byte) 0x2c, (byte) 0x62, (byte) 0x7f,
+ (byte) 0xb5, (byte) 0xaa, (byte) 0xd4, (byte) 0x0d, (byte) 0x81,
+ (byte) 0xfe, (byte) 0xb2, (byte) 0x82, (byte) 0xcb, (byte) 0xa0,
+ (byte) 0xa1, (byte) 0x08, (byte) 0x18, (byte) 0x71, (byte) 0x56,
+ (byte) 0xe8, (byte) 0x49, (byte) 0x02, (byte) 0x10, (byte) 0xc4,
+ (byte) 0xde, (byte) 0x35, (byte) 0xa5, (byte) 0xec, (byte) 0x80,
+ (byte) 0x12, (byte) 0xb8, (byte) 0x69, (byte) 0xda, (byte) 0x2f,
+ (byte) 0x75, (byte) 0xcc, (byte) 0xa2, (byte) 0x09, (byte) 0x36,
+ (byte) 0x03, (byte) 0x61, (byte) 0x2d, (byte) 0xfd, (byte) 0xe0,
+ (byte) 0xdd, (byte) 0x05, (byte) 0x43, (byte) 0x90, (byte) 0xad,
+ (byte) 0xc8, (byte) 0xe1, (byte) 0xaf, (byte) 0x57, (byte) 0x9b,
+ (byte) 0x4c, (byte) 0xd8, (byte) 0x51, (byte) 0xae, (byte) 0x50,
+ (byte) 0x85, (byte) 0x3c, (byte) 0x0a, (byte) 0xe4, (byte) 0xf3,
+ (byte) 0x9c, (byte) 0x26, (byte) 0x23, (byte) 0x53, (byte) 0xc9,
+ (byte) 0x83, (byte) 0x97, (byte) 0x46, (byte) 0xb1, (byte) 0x99,
+ (byte) 0x64, (byte) 0x31, (byte) 0x77, (byte) 0xd5, (byte) 0x1d,
+ (byte) 0xd6, (byte) 0x78, (byte) 0xbd, (byte) 0x5e, (byte) 0xb0,
+ (byte) 0x8a, (byte) 0x22, (byte) 0x38, (byte) 0xf8, (byte) 0x68,
+ (byte) 0x2b, (byte) 0x2a, (byte) 0xc5, (byte) 0xd3, (byte) 0xf7,
+ (byte) 0xbc, (byte) 0x6f, (byte) 0xdf, (byte) 0x04, (byte) 0xe5,
+ (byte) 0x95, (byte) 0x3e, (byte) 0x25, (byte) 0x86, (byte) 0xa6,
+ (byte) 0x0b, (byte) 0x8f, (byte) 0xf1, (byte) 0x24, (byte) 0x0e,
+ (byte) 0xd7, (byte) 0x40, (byte) 0xb3, (byte) 0xcf, (byte) 0x7e,
+ (byte) 0x06, (byte) 0x15, (byte) 0x9a, (byte) 0x4d, (byte) 0x1c,
+ (byte) 0xa3, (byte) 0xdb, (byte) 0x32, (byte) 0x92, (byte) 0x58,
+ (byte) 0x11, (byte) 0x27, (byte) 0xf4, (byte) 0x59, (byte) 0xd0,
+ (byte) 0x4e, (byte) 0x6a, (byte) 0x17, (byte) 0x5b, (byte) 0xac,
+ (byte) 0xff, (byte) 0x07, (byte) 0xc0, (byte) 0x65, (byte) 0x79,
+ (byte) 0xfc, (byte) 0xc7, (byte) 0xcd, (byte) 0x76, (byte) 0x42,
+ (byte) 0x5d, (byte) 0xe7, (byte) 0x3a, (byte) 0x34, (byte) 0x7a,
+ (byte) 0x30, (byte) 0x28, (byte) 0x0f, (byte) 0x73, (byte) 0x01,
+ (byte) 0xf9, (byte) 0xd1, (byte) 0xd2, (byte) 0x19, (byte) 0xe9,
+ (byte) 0x91, (byte) 0xb9, (byte) 0x5a, (byte) 0xed, (byte) 0x41,
+ (byte) 0x6d, (byte) 0xb4, (byte) 0xc3, (byte) 0x9e, (byte) 0xbf,
+ (byte) 0x63, (byte) 0xfa, (byte) 0x1f, (byte) 0x33, (byte) 0x60,
+ (byte) 0x47, (byte) 0x89, (byte) 0xf0, (byte) 0x96, (byte) 0x1a,
+ (byte) 0x5f, (byte) 0x93, (byte) 0x3d, (byte) 0x37, (byte) 0x4b,
+ (byte) 0xd9, (byte) 0xa8, (byte) 0xc1, (byte) 0x1b, (byte) 0xf6,
+ (byte) 0x39, (byte) 0x8b, (byte) 0xb7, (byte) 0x0c, (byte) 0x20,
+ (byte) 0xce, (byte) 0x88, (byte) 0x6e, (byte) 0xb6, (byte) 0x74,
+ (byte) 0x8e, (byte) 0x8d, (byte) 0x16, (byte) 0x29, (byte) 0xf2,
+ (byte) 0x87, (byte) 0xf5, (byte) 0xeb, (byte) 0x70, (byte) 0xe3,
+ (byte) 0xfb, (byte) 0x55, (byte) 0x9f, (byte) 0xc6, (byte) 0x44,
+ (byte) 0x4a, (byte) 0x45, (byte) 0x7d, (byte) 0xe2, (byte) 0x6b,
+ (byte) 0x5c, (byte) 0x6c, (byte) 0x66, (byte) 0xa9, (byte) 0x8c,
+ (byte) 0xee, (byte) 0x84, (byte) 0x13, (byte) 0xa7, (byte) 0x1e,
+ (byte) 0x9d, (byte) 0xdc, (byte) 0x67, (byte) 0x48, (byte) 0xba,
+ (byte) 0x2e, (byte) 0xe6, (byte) 0xa4, (byte) 0xab, (byte) 0x7c,
+ (byte) 0x94, (byte) 0x00, (byte) 0x21, (byte) 0xef, (byte) 0xea,
+ (byte) 0xbe, (byte) 0xca, (byte) 0x72, (byte) 0x4f, (byte) 0x52,
+ (byte) 0x98, (byte) 0x3f, (byte) 0xc2, (byte) 0x14, (byte) 0x7b,
+ (byte) 0x3b, (byte) 0x54 };
+
+ /**
+ * Value generated in the same way as {@link VMPCRandomGenerator#P};
+ */
+ private byte s = (byte) 0xbe;
+
+ public VMPCRandomGenerator()
+ {
+ }
+
+ public void addSeedMaterial(byte[] seed)
+ {
+ for (int m = 0; m < seed.length; m++)
+ {
+ s = P[(s + P[n & 0xff] + seed[m]) & 0xff];
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+ }
+ }
+
+ public void addSeedMaterial(long seed)
+ {
+ addSeedMaterial(Pack.longToBigEndian(seed));
+ }
+
+ public void nextBytes(byte[] bytes)
+ {
+ nextBytes(bytes, 0, bytes.length);
+ }
+
+ public void nextBytes(byte[] bytes, int start, int len)
+ {
+ synchronized (P)
+ {
+ int end = start + len;
+ for (int i = start; i != end; i++)
+ {
+ s = P[(s + P[n & 0xff]) & 0xff];
+ bytes[i] = P[(P[(P[s & 0xff]) & 0xff] + 1) & 0xff];
+ byte temp = P[n & 0xff];
+ P[n & 0xff] = P[s & 0xff];
+ P[s & 0xff] = temp;
+ n = (byte) ((n + 1) & 0xff);
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/drbg/CTRSP800DRBG.java b/core/src/main/java/org/spongycastle/crypto/prng/drbg/CTRSP800DRBG.java
new file mode 100644
index 00000000..26316685
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/drbg/CTRSP800DRBG.java
@@ -0,0 +1,478 @@
+package org.spongycastle.crypto.prng.drbg;
+
+import org.spongycastle.crypto.BlockCipher;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.prng.EntropySource;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.encoders.Hex;
+
+/**
+ * A SP800-90A CTR DRBG.
+ */
+public class CTRSP800DRBG
+ implements SP80090DRBG
+{
+ private static final long TDEA_RESEED_MAX = 1L << (32 - 1);
+ private static final long AES_RESEED_MAX = 1L << (48 - 1);
+ private static final int TDEA_MAX_BITS_REQUEST = 1 << (13 - 1);
+ private static final int AES_MAX_BITS_REQUEST = 1 << (19 - 1);
+
+ private EntropySource _entropySource;
+ private BlockCipher _engine;
+ private int _keySizeInBits;
+ private int _seedLength;
+
+ // internal state
+ private byte[] _Key;
+ private byte[] _V;
+ private long _reseedCounter = 0;
+ private boolean _isTDEA = false;
+
+ /**
+ * Construct a SP800-90A CTR DRBG.
+ * <p>
+ * Minimum entropy requirement is the security strength requested.
+ * </p>
+ * @param engine underlying block cipher to use to support DRBG
+ * @param keySizeInBits size of the key to use with the block cipher.
+ * @param securityStrength security strength required (in bits)
+ * @param entropySource source of entropy to use for seeding/reseeding.
+ * @param personalizationString personalization string to distinguish this DRBG (may be null).
+ * @param nonce nonce to further distinguish this DRBG (may be null).
+ */
+ public CTRSP800DRBG(BlockCipher engine, int keySizeInBits, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce)
+ {
+ _entropySource = entropySource;
+ _engine = engine;
+
+ _keySizeInBits = keySizeInBits;
+ _seedLength = keySizeInBits + engine.getBlockSize() * 8;
+ _isTDEA = isTDEA(engine);
+
+ if (securityStrength > 256)
+ {
+ throw new IllegalArgumentException("Requested security strength is not supported by the derivation function");
+ }
+
+ if (getMaxSecurityStrength(engine, keySizeInBits) < securityStrength)
+ {
+ throw new IllegalArgumentException("Requested security strength is not supported by block cipher and key size");
+ }
+
+ if (entropySource.entropySize() < securityStrength)
+ {
+ throw new IllegalArgumentException("Not enough entropy for security strength required");
+ }
+
+ byte[] entropy = entropySource.getEntropy(); // Get_entropy_input
+
+ CTR_DRBG_Instantiate_algorithm(entropy, nonce, personalizationString);
+ }
+
+ private void CTR_DRBG_Instantiate_algorithm(byte[] entropy, byte[] nonce,
+ byte[] personalisationString)
+ {
+ byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalisationString);
+ byte[] seed = Block_Cipher_df(seedMaterial, _seedLength);
+
+ int outlen = _engine.getBlockSize();
+
+ _Key = new byte[(_keySizeInBits + 7) / 8];
+ _V = new byte[outlen];
+
+ // _Key & _V are modified by this call
+ CTR_DRBG_Update(seed, _Key, _V);
+
+ _reseedCounter = 1;
+ }
+
+ private void CTR_DRBG_Update(byte[] seed, byte[] key, byte[] v)
+ {
+ byte[] temp = new byte[seed.length];
+ byte[] outputBlock = new byte[_engine.getBlockSize()];
+
+ int i=0;
+ int outLen = _engine.getBlockSize();
+
+ _engine.init(true, new KeyParameter(expandKey(key)));
+ while (i*outLen < seed.length)
+ {
+ addOneTo(v);
+ _engine.processBlock(v, 0, outputBlock, 0);
+
+ int bytesToCopy = ((temp.length - i * outLen) > outLen)
+ ? outLen : (temp.length - i * outLen);
+
+ System.arraycopy(outputBlock, 0, temp, i * outLen, bytesToCopy);
+ ++i;
+ }
+
+ XOR(temp, seed, temp, 0);
+
+ System.arraycopy(temp, 0, key, 0, key.length);
+ System.arraycopy(temp, key.length, v, 0, v.length);
+ }
+
+ private void CTR_DRBG_Reseed_algorithm(EntropySource entropy, byte[] additionalInput)
+ {
+ byte[] seedMaterial = Arrays.concatenate(entropy.getEntropy(), additionalInput);
+
+ seedMaterial = Block_Cipher_df(seedMaterial, _seedLength);
+
+ CTR_DRBG_Update(seedMaterial, _Key, _V);
+
+ _reseedCounter = 1;
+ }
+
+ private void XOR(byte[] out, byte[] a, byte[] b, int bOff)
+ {
+ for (int i=0; i< out.length; i++)
+ {
+ out[i] = (byte)(a[i] ^ b[i+bOff]);
+ }
+ }
+
+ private void addOneTo(byte[] longer)
+ {
+ int carry = 1;
+ for (int i = 1; i <= longer.length; i++) // warning
+ {
+ int res = (longer[longer.length - i] & 0xff) + carry;
+ carry = (res > 0xff) ? 1 : 0;
+ longer[longer.length - i] = (byte)res;
+ }
+ }
+
+ // -- Internal state migration ---
+
+ private static final byte[] K_BITS = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+
+ // 1. If (number_of_bits_to_return > max_number_of_bits), then return an
+ // ERROR_FLAG.
+ // 2. L = len (input_string)/8.
+ // 3. N = number_of_bits_to_return/8.
+ // Comment: L is the bitstring represention of
+ // the integer resulting from len (input_string)/8.
+ // L shall be represented as a 32-bit integer.
+ //
+ // Comment : N is the bitstring represention of
+ // the integer resulting from
+ // number_of_bits_to_return/8. N shall be
+ // represented as a 32-bit integer.
+ //
+ // 4. S = L || N || input_string || 0x80.
+ // 5. While (len (S) mod outlen)
+ // Comment : Pad S with zeros, if necessary.
+ // 0, S = S || 0x00.
+ //
+ // Comment : Compute the starting value.
+ // 6. temp = the Null string.
+ // 7. i = 0.
+ // 8. K = Leftmost keylen bits of 0x00010203...1D1E1F.
+ // 9. While len (temp) < keylen + outlen, do
+ //
+ // IV = i || 0outlen - len (i).
+ //
+ // 9.1
+ //
+ // temp = temp || BCC (K, (IV || S)).
+ //
+ // 9.2
+ //
+ // i = i + 1.
+ //
+ // 9.3
+ //
+ // Comment : i shall be represented as a 32-bit
+ // integer, i.e., len (i) = 32.
+ //
+ // Comment: The 32-bit integer represenation of
+ // i is padded with zeros to outlen bits.
+ //
+ // Comment: Compute the requested number of
+ // bits.
+ //
+ // 10. K = Leftmost keylen bits of temp.
+ //
+ // 11. X = Next outlen bits of temp.
+ //
+ // 12. temp = the Null string.
+ //
+ // 13. While len (temp) < number_of_bits_to_return, do
+ //
+ // 13.1 X = Block_Encrypt (K, X).
+ //
+ // 13.2 temp = temp || X.
+ //
+ // 14. requested_bits = Leftmost number_of_bits_to_return of temp.
+ //
+ // 15. Return SUCCESS and requested_bits.
+ private byte[] Block_Cipher_df(byte[] inputString, int bitLength)
+ {
+ int outLen = _engine.getBlockSize();
+ int L = inputString.length; // already in bytes
+ int N = bitLength / 8;
+ // 4 S = L || N || inputstring || 0x80
+ int sLen = 4 + 4 + L + 1;
+ int blockLen = ((sLen + outLen - 1) / outLen) * outLen;
+ byte[] S = new byte[blockLen];
+ copyIntToByteArray(S, L, 0);
+ copyIntToByteArray(S, N, 4);
+ System.arraycopy(inputString, 0, S, 8, L);
+ S[8 + L] = (byte)0x80;
+ // S already padded with zeros
+
+ byte[] temp = new byte[_keySizeInBits / 8 + outLen];
+ byte[] bccOut = new byte[outLen];
+
+ byte[] IV = new byte[outLen];
+
+ int i = 0;
+ byte[] K = new byte[_keySizeInBits / 8];
+ System.arraycopy(K_BITS, 0, K, 0, K.length);
+
+ while (i*outLen*8 < _keySizeInBits + outLen *8)
+ {
+ copyIntToByteArray(IV, i, 0);
+ BCC(bccOut, K, IV, S);
+
+ int bytesToCopy = ((temp.length - i * outLen) > outLen)
+ ? outLen
+ : (temp.length - i * outLen);
+
+ System.arraycopy(bccOut, 0, temp, i * outLen, bytesToCopy);
+ ++i;
+ }
+
+ byte[] X = new byte[outLen];
+ System.arraycopy(temp, 0, K, 0, K.length);
+ System.arraycopy(temp, K.length, X, 0, X.length);
+
+ temp = new byte[bitLength / 2];
+
+ i = 0;
+ _engine.init(true, new KeyParameter(expandKey(K)));
+
+ while (i * outLen < temp.length)
+ {
+ _engine.processBlock(X, 0, X, 0);
+
+ int bytesToCopy = ((temp.length - i * outLen) > outLen)
+ ? outLen
+ : (temp.length - i * outLen);
+
+ System.arraycopy(X, 0, temp, i * outLen, bytesToCopy);
+ i++;
+ }
+
+ return temp;
+ }
+
+ /*
+ * 1. chaining_value = 0^outlen
+ * . Comment: Set the first chaining value to outlen zeros.
+ * 2. n = len (data)/outlen.
+ * 3. Starting with the leftmost bits of data, split the data into n blocks of outlen bits
+ * each, forming block(1) to block(n).
+ * 4. For i = 1 to n do
+ * 4.1 input_block = chaining_value ^ block(i) .
+ * 4.2 chaining_value = Block_Encrypt (Key, input_block).
+ * 5. output_block = chaining_value.
+ * 6. Return output_block.
+ */
+ private void BCC(byte[] bccOut, byte[] k, byte[] iV, byte[] data)
+ {
+ int outlen = _engine.getBlockSize();
+ byte[] chainingValue = new byte[outlen]; // initial values = 0
+ int n = data.length / outlen;
+
+ byte[] inputBlock = new byte[outlen];
+
+ _engine.init(true, new KeyParameter(expandKey(k)));
+
+ _engine.processBlock(iV, 0, chainingValue, 0);
+
+ for (int i = 0; i < n; i++)
+ {
+ XOR(inputBlock, chainingValue, data, i*outlen);
+ _engine.processBlock(inputBlock, 0, chainingValue, 0);
+ }
+
+ System.arraycopy(chainingValue, 0, bccOut, 0, bccOut.length);
+ }
+
+ private void copyIntToByteArray(byte[] buf, int value, int offSet)
+ {
+ buf[offSet + 0] = ((byte)(value >> 24));
+ buf[offSet + 1] = ((byte)(value >> 16));
+ buf[offSet + 2] = ((byte)(value >> 8));
+ buf[offSet + 3] = ((byte)(value));
+ }
+
+ /**
+ * Return the block size (in bits) of the DRBG.
+ *
+ * @return the number of bits produced on each internal round of the DRBG.
+ */
+ public int getBlockSize()
+ {
+ return _V.length * 8;
+ }
+
+ /**
+ * Populate a passed in array with random data.
+ *
+ * @param output output array for generated bits.
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ * @param predictionResistant true if a reseed should be forced, false otherwise.
+ *
+ * @return number of bits generated, -1 if a reseed required.
+ */
+ public int generate(byte[] output, byte[] additionalInput, boolean predictionResistant)
+ {
+ if (_isTDEA)
+ {
+ if (_reseedCounter > TDEA_RESEED_MAX)
+ {
+ return -1;
+ }
+
+ if (Utils.isTooLarge(output, TDEA_MAX_BITS_REQUEST / 8))
+ {
+ throw new IllegalArgumentException("Number of bits per request limited to " + TDEA_MAX_BITS_REQUEST);
+ }
+ }
+ else
+ {
+ if (_reseedCounter > AES_RESEED_MAX)
+ {
+ return -1;
+ }
+
+ if (Utils.isTooLarge(output, AES_MAX_BITS_REQUEST / 8))
+ {
+ throw new IllegalArgumentException("Number of bits per request limited to " + AES_MAX_BITS_REQUEST);
+ }
+ }
+
+ if (predictionResistant)
+ {
+ CTR_DRBG_Reseed_algorithm(_entropySource, additionalInput);
+ additionalInput = null;
+ }
+
+ if (additionalInput != null)
+ {
+ additionalInput = Block_Cipher_df(additionalInput, _seedLength);
+ CTR_DRBG_Update(additionalInput, _Key, _V);
+ }
+ else
+ {
+ additionalInput = new byte[_seedLength];
+ }
+
+ byte[] out = new byte[_V.length];
+
+ _engine.init(true, new KeyParameter(expandKey(_Key)));
+
+ for (int i = 0; i < output.length / out.length; i++)
+ {
+ addOneTo(_V);
+
+ _engine.processBlock(_V, 0, out, 0);
+
+ int bytesToCopy = ((output.length - i * out.length) > out.length)
+ ? out.length
+ : (output.length - i * _V.length);
+
+ System.arraycopy(out, 0, output, i * out.length, bytesToCopy);
+ }
+
+ CTR_DRBG_Update(additionalInput, _Key, _V);
+
+ _reseedCounter++;
+
+ return output.length * 8;
+ }
+
+ /**
+ * Reseed the DRBG.
+ *
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ */
+ public void reseed(byte[] additionalInput)
+ {
+ CTR_DRBG_Reseed_algorithm(_entropySource, additionalInput);
+ }
+
+ private boolean isTDEA(BlockCipher cipher)
+ {
+ return cipher.getAlgorithmName().equals("DESede") || cipher.getAlgorithmName().equals("TDEA");
+ }
+
+ private int getMaxSecurityStrength(BlockCipher cipher, int keySizeInBits)
+ {
+ if (isTDEA(cipher) && keySizeInBits == 168)
+ {
+ return 112;
+ }
+ if (cipher.getAlgorithmName().equals("AES"))
+ {
+ return keySizeInBits;
+ }
+
+ return -1;
+ }
+
+ byte[] expandKey(byte[] key)
+ {
+ if (_isTDEA)
+ {
+ // expand key to 192 bits.
+ byte[] tmp = new byte[24];
+
+ padKey(key, 0, tmp, 0);
+ padKey(key, 7, tmp, 8);
+ padKey(key, 14, tmp, 16);
+
+ return tmp;
+ }
+ else
+ {
+ return key;
+ }
+ }
+
+ /**
+ * Pad out a key for TDEA, setting odd parity for each byte.
+ *
+ * @param keyMaster
+ * @param keyOff
+ * @param tmp
+ * @param tmpOff
+ */
+ private void padKey(byte[] keyMaster, int keyOff, byte[] tmp, int tmpOff)
+ {
+ tmp[tmpOff + 0] = (byte)(keyMaster[keyOff + 0] & 0xfe);
+ tmp[tmpOff + 1] = (byte)((keyMaster[keyOff + 0] << 7) | ((keyMaster[keyOff + 1] & 0xfc) >>> 1));
+ tmp[tmpOff + 2] = (byte)((keyMaster[keyOff + 1] << 6) | ((keyMaster[keyOff + 2] & 0xf8) >>> 2));
+ tmp[tmpOff + 3] = (byte)((keyMaster[keyOff + 2] << 5) | ((keyMaster[keyOff + 3] & 0xf0) >>> 3));
+ tmp[tmpOff + 4] = (byte)((keyMaster[keyOff + 3] << 4) | ((keyMaster[keyOff + 4] & 0xe0) >>> 4));
+ tmp[tmpOff + 5] = (byte)((keyMaster[keyOff + 4] << 3) | ((keyMaster[keyOff + 5] & 0xc0) >>> 5));
+ tmp[tmpOff + 6] = (byte)((keyMaster[keyOff + 5] << 2) | ((keyMaster[keyOff + 6] & 0x80) >>> 6));
+ tmp[tmpOff + 7] = (byte)(keyMaster[keyOff + 6] << 1);
+
+ for (int i = tmpOff; i <= tmpOff + 7; i++)
+ {
+ int b = tmp[i];
+ tmp[i] = (byte)((b & 0xfe) |
+ ((((b >> 1) ^
+ (b >> 2) ^
+ (b >> 3) ^
+ (b >> 4) ^
+ (b >> 5) ^
+ (b >> 6) ^
+ (b >> 7)) ^ 0x01) & 0x01));
+ }
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/drbg/DualECPoints.java b/core/src/main/java/org/spongycastle/crypto/prng/drbg/DualECPoints.java
new file mode 100644
index 00000000..251b319f
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/drbg/DualECPoints.java
@@ -0,0 +1,82 @@
+package org.spongycastle.crypto.prng.drbg;
+
+import org.spongycastle.math.ec.ECPoint;
+
+/**
+ * General class for providing point pairs for use with DualEC DRBG. See NIST SP 800-90A for further details.
+ */
+public class DualECPoints
+{
+ private final ECPoint p;
+ private final ECPoint q;
+ private final int securityStrength;
+ private final int cofactor;
+
+ /**
+ * Base Constructor.
+ * <p>
+ * The cofactor is used to calculate the output block length (maxOutlen) according to
+ * <pre>
+ * max_outlen = largest multiple of 8 less than ((field size in bits) - (13 + log2(cofactor))
+ * </pre>
+ *
+ * @param securityStrength maximum security strength to be associated with these parameters
+ * @param p the P point.
+ * @param q the Q point.
+ * @param cofactor cofactor associated with the domain parameters for the point generation.
+ */
+ public DualECPoints(int securityStrength, ECPoint p, ECPoint q, int cofactor)
+ {
+ if (!p.getCurve().equals(q.getCurve()))
+ {
+ throw new IllegalArgumentException("points need to be on the same curve");
+ }
+
+ this.securityStrength = securityStrength;
+ this.p = p;
+ this.q = q;
+ this.cofactor = cofactor;
+ }
+
+ public int getSeedLen()
+ {
+ return p.getCurve().getFieldSize();
+ }
+
+ public int getMaxOutlen()
+ {
+ return ((p.getCurve().getFieldSize() - (13 + log2(cofactor))) / 8) * 8;
+ }
+
+ public ECPoint getP()
+ {
+ return p;
+ }
+
+ public ECPoint getQ()
+ {
+ return q;
+ }
+
+ public int getSecurityStrength()
+ {
+ return securityStrength;
+ }
+
+ public int getCofactor()
+ {
+ return cofactor;
+ }
+
+ private static int log2(int value)
+ {
+ int log = 0;
+
+ while ((value >>= 1) != 0)
+ {
+ log++;
+ }
+
+ return log;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/drbg/DualECSP800DRBG.java b/core/src/main/java/org/spongycastle/crypto/prng/drbg/DualECSP800DRBG.java
new file mode 100644
index 00000000..239ffe57
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/drbg/DualECSP800DRBG.java
@@ -0,0 +1,320 @@
+package org.spongycastle.crypto.prng.drbg;
+
+import java.math.BigInteger;
+
+import org.spongycastle.asn1.nist.NISTNamedCurves;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.prng.EntropySource;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECMultiplier;
+import org.spongycastle.math.ec.ECPoint;
+import org.spongycastle.math.ec.FixedPointCombMultiplier;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.BigIntegers;
+
+/**
+ * A SP800-90A Dual EC DRBG.
+ */
+public class DualECSP800DRBG
+ implements SP80090DRBG
+{
+ /*
+ * Default P, Q values for each curve
+ */
+ private static final BigInteger p256_Px = new BigInteger("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16);
+ private static final BigInteger p256_Py = new BigInteger("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16);
+ private static final BigInteger p256_Qx = new BigInteger("c97445f45cdef9f0d3e05e1e585fc297235b82b5be8ff3efca67c59852018192", 16);
+ private static final BigInteger p256_Qy = new BigInteger("b28ef557ba31dfcbdd21ac46e2a91e3c304f44cb87058ada2cb815151e610046", 16);
+
+ private static final BigInteger p384_Px = new BigInteger("aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7", 16);
+ private static final BigInteger p384_Py = new BigInteger("3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f", 16);
+ private static final BigInteger p384_Qx = new BigInteger("8e722de3125bddb05580164bfe20b8b432216a62926c57502ceede31c47816edd1e89769124179d0b695106428815065", 16);
+ private static final BigInteger p384_Qy = new BigInteger("023b1660dd701d0839fd45eec36f9ee7b32e13b315dc02610aa1b636e346df671f790f84c5e09b05674dbb7e45c803dd", 16);
+
+ private static final BigInteger p521_Px = new BigInteger("c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", 16);
+ private static final BigInteger p521_Py = new BigInteger("11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650", 16);
+ private static final BigInteger p521_Qx = new BigInteger("1b9fa3e518d683c6b65763694ac8efbaec6fab44f2276171a42726507dd08add4c3b3f4c1ebc5b1222ddba077f722943b24c3edfa0f85fe24d0c8c01591f0be6f63", 16);
+ private static final BigInteger p521_Qy = new BigInteger("1f3bdba585295d9a1110d1df1f9430ef8442c5018976ff3437ef91b81dc0b8132c8d5c39c32d0e004a3092b7d327c0e7a4d26d2c7b69b58f9066652911e457779de", 16);
+
+ private static final DualECPoints[] nistPoints;
+
+ static
+ {
+ nistPoints = new DualECPoints[3];
+
+ ECCurve.Fp curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-256").getCurve();
+
+ nistPoints[0] = new DualECPoints(128, curve.createPoint(p256_Px, p256_Py), curve.createPoint(p256_Qx, p256_Qy), 1);
+
+ curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-384").getCurve();
+
+ nistPoints[1] = new DualECPoints(192, curve.createPoint(p384_Px, p384_Py), curve.createPoint(p384_Qx, p384_Qy), 1);
+
+ curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-521").getCurve();
+
+ nistPoints[2] = new DualECPoints(256, curve.createPoint(p521_Px, p521_Py), curve.createPoint(p521_Qx, p521_Qy), 1);
+ }
+
+
+ private static final long RESEED_MAX = 1L << (32 - 1);
+ private static final int MAX_ADDITIONAL_INPUT = 1 << (13 - 1);
+ private static final int MAX_ENTROPY_LENGTH = 1 << (13 - 1);
+ private static final int MAX_PERSONALIZATION_STRING = 1 << (13 -1);
+
+ private Digest _digest;
+ private long _reseedCounter;
+ private EntropySource _entropySource;
+ private int _securityStrength;
+ private int _seedlen;
+ private int _outlen;
+ private ECCurve.Fp _curve;
+ private ECPoint _P;
+ private ECPoint _Q;
+ private byte[] _s;
+ private int _sLength;
+ private ECMultiplier _fixedPointMultiplier = new FixedPointCombMultiplier();
+
+ /**
+ * Construct a SP800-90A Dual EC DRBG.
+ * <p>
+ * Minimum entropy requirement is the security strength requested.
+ * </p>
+ * @param digest source digest to use with the DRB stream.
+ * @param securityStrength security strength required (in bits)
+ * @param entropySource source of entropy to use for seeding/reseeding.
+ * @param personalizationString personalization string to distinguish this DRBG (may be null).
+ * @param nonce nonce to further distinguish this DRBG (may be null).
+ */
+ public DualECSP800DRBG(Digest digest, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce)
+ {
+ this(nistPoints, digest, securityStrength, entropySource, personalizationString, nonce);
+ }
+
+ /**
+ * Construct a SP800-90A Dual EC DRBG.
+ * <p>
+ * Minimum entropy requirement is the security strength requested.
+ * </p>
+ * @param pointSet an array of points to choose from, in order of increasing security strength
+ * @param digest source digest to use with the DRB stream.
+ * @param securityStrength security strength required (in bits)
+ * @param entropySource source of entropy to use for seeding/reseeding.
+ * @param personalizationString personalization string to distinguish this DRBG (may be null).
+ * @param nonce nonce to further distinguish this DRBG (may be null).
+ */
+ public DualECSP800DRBG(DualECPoints[] pointSet, Digest digest, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce)
+ {
+ _digest = digest;
+ _entropySource = entropySource;
+ _securityStrength = securityStrength;
+
+ if (Utils.isTooLarge(personalizationString, MAX_PERSONALIZATION_STRING / 8))
+ {
+ throw new IllegalArgumentException("Personalization string too large");
+ }
+
+ if (entropySource.entropySize() < securityStrength || entropySource.entropySize() > MAX_ENTROPY_LENGTH)
+ {
+ throw new IllegalArgumentException("EntropySource must provide between " + securityStrength + " and " + MAX_ENTROPY_LENGTH + " bits");
+ }
+
+ byte[] entropy = entropySource.getEntropy();
+ byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalizationString);
+
+ for (int i = 0; i != pointSet.length; i++)
+ {
+ if (securityStrength <= pointSet[i].getSecurityStrength())
+ {
+ if (Utils.getMaxSecurityStrength(digest) < pointSet[i].getSecurityStrength())
+ {
+ throw new IllegalArgumentException("Requested security strength is not supported by digest");
+ }
+ _seedlen = pointSet[i].getSeedLen();
+ _outlen = pointSet[i].getMaxOutlen() / 8;
+ _P = pointSet[i].getP();
+ _Q = pointSet[i].getQ();
+ break;
+ }
+ }
+
+ if (_P == null)
+ {
+ throw new IllegalArgumentException("security strength cannot be greater than 256 bits");
+ }
+
+ _s = Utils.hash_df(_digest, seedMaterial, _seedlen);
+ _sLength = _s.length;
+
+ _reseedCounter = 0;
+ }
+
+ /**
+ * Return the block size (in bits) of the DRBG.
+ *
+ * @return the number of bits produced on each internal round of the DRBG.
+ */
+ public int getBlockSize()
+ {
+ return _outlen * 8;
+ }
+
+ /**
+ * Populate a passed in array with random data.
+ *
+ * @param output output array for generated bits.
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ * @param predictionResistant true if a reseed should be forced, false otherwise.
+ *
+ * @return number of bits generated, -1 if a reseed required.
+ */
+ public int generate(byte[] output, byte[] additionalInput, boolean predictionResistant)
+ {
+ int numberOfBits = output.length*8;
+ int m = output.length / _outlen;
+
+ if (Utils.isTooLarge(additionalInput, MAX_ADDITIONAL_INPUT / 8))
+ {
+ throw new IllegalArgumentException("Additional input too large");
+ }
+
+ if (_reseedCounter + m > RESEED_MAX)
+ {
+ return -1;
+ }
+
+ if (predictionResistant)
+ {
+ reseed(additionalInput);
+ additionalInput = null;
+ }
+
+ BigInteger s;
+
+ if (additionalInput != null)
+ {
+ // Note: we ignore the use of pad8 on the additional input as we mandate byte arrays for it.
+ additionalInput = Utils.hash_df(_digest, additionalInput, _seedlen);
+ s = new BigInteger(1, xor(_s, additionalInput));
+ }
+ else
+ {
+ s = new BigInteger(1, _s);
+ }
+
+ // make sure we start with a clean output array.
+ Arrays.fill(output, (byte)0);
+
+ int outOffset = 0;
+
+ for (int i = 0; i < m; i++)
+ {
+ s = getScalarMultipleXCoord(_P, s);
+
+ //System.err.println("S: " + new String(Hex.encode(_s)));
+
+ byte[] r = getScalarMultipleXCoord(_Q, s).toByteArray();
+
+ if (r.length > _outlen)
+ {
+ System.arraycopy(r, r.length - _outlen, output, outOffset, _outlen);
+ }
+ else
+ {
+ System.arraycopy(r, 0, output, outOffset + (_outlen - r.length), r.length);
+ }
+
+ //System.err.println("R: " + new String(Hex.encode(r)));
+ outOffset += _outlen;
+
+ _reseedCounter++;
+ }
+
+ if (outOffset < output.length)
+ {
+ s = getScalarMultipleXCoord(_P, s);
+
+ byte[] r = getScalarMultipleXCoord(_Q, s).toByteArray();
+
+ int required = output.length - outOffset;
+
+ if (r.length > _outlen)
+ {
+ System.arraycopy(r, r.length - _outlen, output, outOffset, required);
+ }
+ else
+ {
+ System.arraycopy(r, 0, output, outOffset + (_outlen - r.length), required);
+ }
+
+ _reseedCounter++;
+ }
+
+ // Need to preserve length of S as unsigned int.
+ _s = BigIntegers.asUnsignedByteArray(_sLength, getScalarMultipleXCoord(_P, s));
+
+ return numberOfBits;
+ }
+
+ /**
+ * Reseed the DRBG.
+ *
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ */
+ public void reseed(byte[] additionalInput)
+ {
+ if (Utils.isTooLarge(additionalInput, MAX_ADDITIONAL_INPUT / 8))
+ {
+ throw new IllegalArgumentException("Additional input string too large");
+ }
+
+ byte[] entropy = _entropySource.getEntropy();
+ byte[] seedMaterial = Arrays.concatenate(pad8(_s, _seedlen), entropy, additionalInput);
+
+ _s = Utils.hash_df(_digest, seedMaterial, _seedlen);
+
+ _reseedCounter = 0;
+ }
+
+ private byte[] xor(byte[] a, byte[] b)
+ {
+ if (b == null)
+ {
+ return a;
+ }
+
+ byte[] rv = new byte[a.length];
+
+ for (int i = 0; i != rv.length; i++)
+ {
+ rv[i] = (byte)(a[i] ^ b[i]);
+ }
+
+ return rv;
+ }
+
+ // Note: works in place
+ private byte[] pad8(byte[] s, int seedlen)
+ {
+ if (seedlen % 8 == 0)
+ {
+ return s;
+ }
+
+ int shift = 8 - (seedlen % 8);
+ int carry = 0;
+
+ for (int i = s.length - 1; i >= 0; i--)
+ {
+ int b = s[i] & 0xff;
+ s[i] = (byte)((b << shift) | (carry >> (8 - shift)));
+ carry = b;
+ }
+
+ return s;
+ }
+
+ private BigInteger getScalarMultipleXCoord(ECPoint p, BigInteger s)
+ {
+ return _fixedPointMultiplier.multiply(p, s).normalize().getAffineXCoord().toBigInteger();
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/drbg/HMacSP800DRBG.java b/core/src/main/java/org/spongycastle/crypto/prng/drbg/HMacSP800DRBG.java
new file mode 100644
index 00000000..6d2d4247
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/drbg/HMacSP800DRBG.java
@@ -0,0 +1,181 @@
+package org.spongycastle.crypto.prng.drbg;
+
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.crypto.params.KeyParameter;
+import org.spongycastle.crypto.prng.EntropySource;
+import org.spongycastle.util.Arrays;
+
+/**
+ * A SP800-90A HMAC DRBG.
+ */
+public class HMacSP800DRBG
+ implements SP80090DRBG
+{
+ private final static long RESEED_MAX = 1L << (48 - 1);
+ private final static int MAX_BITS_REQUEST = 1 << (19 - 1);
+
+ private byte[] _K;
+ private byte[] _V;
+ private long _reseedCounter;
+ private EntropySource _entropySource;
+ private Mac _hMac;
+
+ /**
+ * Construct a SP800-90A Hash DRBG.
+ * <p>
+ * Minimum entropy requirement is the security strength requested.
+ * </p>
+ * @param hMac Hash MAC to base the DRBG on.
+ * @param securityStrength security strength required (in bits)
+ * @param entropySource source of entropy to use for seeding/reseeding.
+ * @param personalizationString personalization string to distinguish this DRBG (may be null).
+ * @param nonce nonce to further distinguish this DRBG (may be null).
+ */
+ public HMacSP800DRBG(Mac hMac, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce)
+ {
+ if (securityStrength > Utils.getMaxSecurityStrength(hMac))
+ {
+ throw new IllegalArgumentException("Requested security strength is not supported by the derivation function");
+ }
+
+ if (entropySource.entropySize() < securityStrength)
+ {
+ throw new IllegalArgumentException("Not enough entropy for security strength required");
+ }
+
+ _entropySource = entropySource;
+ _hMac = hMac;
+
+ byte[] entropy = entropySource.getEntropy();
+ byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalizationString);
+
+ _K = new byte[hMac.getMacSize()];
+ _V = new byte[_K.length];
+ Arrays.fill(_V, (byte)1);
+
+ hmac_DRBG_Update(seedMaterial);
+
+ _reseedCounter = 1;
+ }
+
+ private void hmac_DRBG_Update(byte[] seedMaterial)
+ {
+ hmac_DRBG_Update_Func(seedMaterial, (byte)0x00);
+ if (seedMaterial != null)
+ {
+ hmac_DRBG_Update_Func(seedMaterial, (byte)0x01);
+ }
+ }
+
+ private void hmac_DRBG_Update_Func(byte[] seedMaterial, byte vValue)
+ {
+ _hMac.init(new KeyParameter(_K));
+
+ _hMac.update(_V, 0, _V.length);
+ _hMac.update(vValue);
+
+ if (seedMaterial != null)
+ {
+ _hMac.update(seedMaterial, 0, seedMaterial.length);
+ }
+
+ _hMac.doFinal(_K, 0);
+
+ _hMac.init(new KeyParameter(_K));
+ _hMac.update(_V, 0, _V.length);
+
+ _hMac.doFinal(_V, 0);
+ }
+
+ /**
+ * Return the block size (in bits) of the DRBG.
+ *
+ * @return the number of bits produced on each round of the DRBG.
+ */
+ public int getBlockSize()
+ {
+ return _V.length * 8;
+ }
+
+ /**
+ * Populate a passed in array with random data.
+ *
+ * @param output output array for generated bits.
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ * @param predictionResistant true if a reseed should be forced, false otherwise.
+ *
+ * @return number of bits generated, -1 if a reseed required.
+ */
+ public int generate(byte[] output, byte[] additionalInput, boolean predictionResistant)
+ {
+ int numberOfBits = output.length * 8;
+
+ if (numberOfBits > MAX_BITS_REQUEST)
+ {
+ throw new IllegalArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST);
+ }
+
+ if (_reseedCounter > RESEED_MAX)
+ {
+ return -1;
+ }
+
+ if (predictionResistant)
+ {
+ reseed(additionalInput);
+ additionalInput = null;
+ }
+
+ // 2.
+ if (additionalInput != null)
+ {
+ hmac_DRBG_Update(additionalInput);
+ }
+
+ // 3.
+ byte[] rv = new byte[output.length];
+
+ int m = output.length / _V.length;
+
+ _hMac.init(new KeyParameter(_K));
+
+ for (int i = 0; i < m; i++)
+ {
+ _hMac.update(_V, 0, _V.length);
+ _hMac.doFinal(_V, 0);
+
+ System.arraycopy(_V, 0, rv, i * _V.length, _V.length);
+ }
+
+ if (m * _V.length < rv.length)
+ {
+ _hMac.update(_V, 0, _V.length);
+ _hMac.doFinal(_V, 0);
+
+ System.arraycopy(_V, 0, rv, m * _V.length, rv.length - (m * _V.length));
+ }
+
+ hmac_DRBG_Update(additionalInput);
+
+ _reseedCounter++;
+
+ System.arraycopy(rv, 0, output, 0, output.length);
+
+ return numberOfBits;
+ }
+
+ /**
+ * Reseed the DRBG.
+ *
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ */
+ public void reseed(byte[] additionalInput)
+ {
+ byte[] entropy = _entropySource.getEntropy();
+ byte[] seedMaterial = Arrays.concatenate(entropy, additionalInput);
+
+ hmac_DRBG_Update(seedMaterial);
+
+ _reseedCounter = 1;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/drbg/HashSP800DRBG.java b/core/src/main/java/org/spongycastle/crypto/prng/drbg/HashSP800DRBG.java
new file mode 100644
index 00000000..13498046
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/drbg/HashSP800DRBG.java
@@ -0,0 +1,284 @@
+package org.spongycastle.crypto.prng.drbg;
+
+import java.util.Hashtable;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.prng.EntropySource;
+import org.spongycastle.util.Arrays;
+import org.spongycastle.util.Integers;
+
+/**
+ * A SP800-90A Hash DRBG.
+ */
+public class HashSP800DRBG
+ implements SP80090DRBG
+{
+ private final static byte[] ONE = { 0x01 };
+
+ private final static long RESEED_MAX = 1L << (48 - 1);
+ private final static int MAX_BITS_REQUEST = 1 << (19 - 1);
+
+ private final static Hashtable seedlens = new Hashtable();
+
+ static
+ {
+ seedlens.put("SHA-1", Integers.valueOf(440));
+ seedlens.put("SHA-224", Integers.valueOf(440));
+ seedlens.put("SHA-256", Integers.valueOf(440));
+ seedlens.put("SHA-512/256", Integers.valueOf(440));
+ seedlens.put("SHA-512/224", Integers.valueOf(440));
+ seedlens.put("SHA-384", Integers.valueOf(888));
+ seedlens.put("SHA-512", Integers.valueOf(888));
+ }
+
+ private Digest _digest;
+ private byte[] _V;
+ private byte[] _C;
+ private long _reseedCounter;
+ private EntropySource _entropySource;
+ private int _securityStrength;
+ private int _seedLength;
+
+ /**
+ * Construct a SP800-90A Hash DRBG.
+ * <p>
+ * Minimum entropy requirement is the security strength requested.
+ * </p>
+ * @param digest source digest to use for DRB stream.
+ * @param securityStrength security strength required (in bits)
+ * @param entropySource source of entropy to use for seeding/reseeding.
+ * @param personalizationString personalization string to distinguish this DRBG (may be null).
+ * @param nonce nonce to further distinguish this DRBG (may be null).
+ */
+ public HashSP800DRBG(Digest digest, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce)
+ {
+ if (securityStrength > Utils.getMaxSecurityStrength(digest))
+ {
+ throw new IllegalArgumentException("Requested security strength is not supported by the derivation function");
+ }
+
+ if (entropySource.entropySize() < securityStrength)
+ {
+ throw new IllegalArgumentException("Not enough entropy for security strength required");
+ }
+
+ _digest = digest;
+ _entropySource = entropySource;
+ _securityStrength = securityStrength;
+ _seedLength = ((Integer)seedlens.get(digest.getAlgorithmName())).intValue();
+
+ // 1. seed_material = entropy_input || nonce || personalization_string.
+ // 2. seed = Hash_df (seed_material, seedlen).
+ // 3. V = seed.
+ // 4. C = Hash_df ((0x00 || V), seedlen). Comment: Preceed V with a byte
+ // of zeros.
+ // 5. reseed_counter = 1.
+ // 6. Return V, C, and reseed_counter as the initial_working_state
+
+ byte[] entropy = entropySource.getEntropy();
+ byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalizationString);
+ byte[] seed = Utils.hash_df(_digest, seedMaterial, _seedLength);
+
+ _V = seed;
+ byte[] subV = new byte[_V.length + 1];
+ System.arraycopy(_V, 0, subV, 1, _V.length);
+ _C = Utils.hash_df(_digest, subV, _seedLength);
+
+ _reseedCounter = 1;
+ }
+
+ /**
+ * Return the block size (in bits) of the DRBG.
+ *
+ * @return the number of bits produced on each internal round of the DRBG.
+ */
+ public int getBlockSize()
+ {
+ return _digest.getDigestSize() * 8;
+ }
+
+ /**
+ * Populate a passed in array with random data.
+ *
+ * @param output output array for generated bits.
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ * @param predictionResistant true if a reseed should be forced, false otherwise.
+ *
+ * @return number of bits generated, -1 if a reseed required.
+ */
+ public int generate(byte[] output, byte[] additionalInput, boolean predictionResistant)
+ {
+ // 1. If reseed_counter > reseed_interval, then return an indication that a
+ // reseed is required.
+ // 2. If (additional_input != Null), then do
+ // 2.1 w = Hash (0x02 || V || additional_input).
+ // 2.2 V = (V + w) mod 2^seedlen
+ // .
+ // 3. (returned_bits) = Hashgen (requested_number_of_bits, V).
+ // 4. H = Hash (0x03 || V).
+ // 5. V = (V + H + C + reseed_counter) mod 2^seedlen
+ // .
+ // 6. reseed_counter = reseed_counter + 1.
+ // 7. Return SUCCESS, returned_bits, and the new values of V, C, and
+ // reseed_counter for the new_working_state.
+ int numberOfBits = output.length*8;
+
+ if (numberOfBits > MAX_BITS_REQUEST)
+ {
+ throw new IllegalArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST);
+ }
+
+ if (_reseedCounter > RESEED_MAX)
+ {
+ return -1;
+ }
+
+ if (predictionResistant)
+ {
+ reseed(additionalInput);
+ additionalInput = null;
+ }
+
+ // 2.
+ if (additionalInput != null)
+ {
+ byte[] newInput = new byte[1 + _V.length + additionalInput.length];
+ newInput[0] = 0x02;
+ System.arraycopy(_V, 0, newInput, 1, _V.length);
+ // TODO: inOff / inLength
+ System.arraycopy(additionalInput, 0, newInput, 1 + _V.length, additionalInput.length);
+ byte[] w = hash(newInput);
+
+ addTo(_V, w);
+ }
+
+ // 3.
+ byte[] rv = hashgen(_V, numberOfBits);
+
+ // 4.
+ byte[] subH = new byte[_V.length + 1];
+ System.arraycopy(_V, 0, subH, 1, _V.length);
+ subH[0] = 0x03;
+
+ byte[] H = hash(subH);
+
+ // 5.
+ addTo(_V, H);
+ addTo(_V, _C);
+ byte[] c = new byte[4];
+ c[0] = (byte)(_reseedCounter >> 24);
+ c[1] = (byte)(_reseedCounter >> 16);
+ c[2] = (byte)(_reseedCounter >> 8);
+ c[3] = (byte)_reseedCounter;
+
+ addTo(_V, c);
+
+ _reseedCounter++;
+
+ System.arraycopy(rv, 0, output, 0, output.length);
+
+ return numberOfBits;
+ }
+
+ // this will always add the shorter length byte array mathematically to the
+ // longer length byte array.
+ // be careful....
+ private void addTo(byte[] longer, byte[] shorter)
+ {
+ int carry = 0;
+ for (int i=1;i <= shorter.length; i++) // warning
+ {
+ int res = (longer[longer.length-i] & 0xff) + (shorter[shorter.length-i] & 0xff) + carry;
+ carry = (res > 0xff) ? 1 : 0;
+ longer[longer.length-i] = (byte)res;
+ }
+
+ for (int i=shorter.length+1;i <= longer.length; i++) // warning
+ {
+ int res = (longer[longer.length-i] & 0xff) + carry;
+ carry = (res > 0xff) ? 1 : 0;
+ longer[longer.length-i] = (byte)res;
+ }
+ }
+
+ /**
+ * Reseed the DRBG.
+ *
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ */
+ public void reseed(byte[] additionalInput)
+ {
+ // 1. seed_material = 0x01 || V || entropy_input || additional_input.
+ //
+ // 2. seed = Hash_df (seed_material, seedlen).
+ //
+ // 3. V = seed.
+ //
+ // 4. C = Hash_df ((0x00 || V), seedlen).
+ //
+ // 5. reseed_counter = 1.
+ //
+ // 6. Return V, C, and reseed_counter for the new_working_state.
+ //
+ // Comment: Precede with a byte of all zeros.
+ byte[] entropy = _entropySource.getEntropy();
+ byte[] seedMaterial = Arrays.concatenate(ONE, _V, entropy, additionalInput);
+ byte[] seed = Utils.hash_df(_digest, seedMaterial, _seedLength);
+
+ _V = seed;
+ byte[] subV = new byte[_V.length + 1];
+ subV[0] = 0x00;
+ System.arraycopy(_V, 0, subV, 1, _V.length);
+ _C = Utils.hash_df(_digest, subV, _seedLength);
+
+ _reseedCounter = 1;
+ }
+
+ private byte[] hash(byte[] input)
+ {
+ byte[] hash = new byte[_digest.getDigestSize()];
+ doHash(input, hash);
+ return hash;
+ }
+
+ private void doHash(byte[] input, byte[] output)
+ {
+ _digest.update(input, 0, input.length);
+ _digest.doFinal(output, 0);
+ }
+
+ // 1. m = [requested_number_of_bits / outlen]
+ // 2. data = V.
+ // 3. W = the Null string.
+ // 4. For i = 1 to m
+ // 4.1 wi = Hash (data).
+ // 4.2 W = W || wi.
+ // 4.3 data = (data + 1) mod 2^seedlen
+ // .
+ // 5. returned_bits = Leftmost (requested_no_of_bits) bits of W.
+ private byte[] hashgen(byte[] input, int lengthInBits)
+ {
+ int digestSize = _digest.getDigestSize();
+ int m = (lengthInBits / 8) / digestSize;
+
+ byte[] data = new byte[input.length];
+ System.arraycopy(input, 0, data, 0, input.length);
+
+ byte[] W = new byte[lengthInBits / 8];
+
+ byte[] dig = new byte[_digest.getDigestSize()];
+ for (int i = 0; i <= m; i++)
+ {
+ doHash(data, dig);
+
+ int bytesToCopy = ((W.length - i * dig.length) > dig.length)
+ ? dig.length
+ : (W.length - i * dig.length);
+ System.arraycopy(dig, 0, W, i * dig.length, bytesToCopy);
+
+ addTo(data, ONE);
+ }
+
+ return W;
+ }
+} \ No newline at end of file
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/drbg/SP80090DRBG.java b/core/src/main/java/org/spongycastle/crypto/prng/drbg/SP80090DRBG.java
new file mode 100644
index 00000000..a1ec3b26
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/drbg/SP80090DRBG.java
@@ -0,0 +1,32 @@
+package org.spongycastle.crypto.prng.drbg;
+
+/**
+ * Interface to SP800-90A deterministic random bit generators.
+ */
+public interface SP80090DRBG
+{
+ /**
+ * Return the block size of the DRBG.
+ *
+ * @return the block size (in bits) produced by each round of the DRBG.
+ */
+ int getBlockSize();
+
+ /**
+ * Populate a passed in array with random data.
+ *
+ * @param output output array for generated bits.
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ * @param predictionResistant true if a reseed should be forced, false otherwise.
+ *
+ * @return number of bits generated, -1 if a reseed required.
+ */
+ int generate(byte[] output, byte[] additionalInput, boolean predictionResistant);
+
+ /**
+ * Reseed the DRBG.
+ *
+ * @param additionalInput additional input to be added to the DRBG in this step.
+ */
+ void reseed(byte[] additionalInput);
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/prng/drbg/Utils.java b/core/src/main/java/org/spongycastle/crypto/prng/drbg/Utils.java
new file mode 100644
index 00000000..bf2626d0
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/prng/drbg/Utils.java
@@ -0,0 +1,103 @@
+package org.spongycastle.crypto.prng.drbg;
+
+import java.util.Hashtable;
+
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.crypto.Mac;
+import org.spongycastle.util.Integers;
+
+class Utils
+{
+ static final Hashtable maxSecurityStrengths = new Hashtable();
+
+ static
+ {
+ maxSecurityStrengths.put("SHA-1", Integers.valueOf(128));
+
+ maxSecurityStrengths.put("SHA-224", Integers.valueOf(192));
+ maxSecurityStrengths.put("SHA-256", Integers.valueOf(256));
+ maxSecurityStrengths.put("SHA-384", Integers.valueOf(256));
+ maxSecurityStrengths.put("SHA-512", Integers.valueOf(256));
+
+ maxSecurityStrengths.put("SHA-512/224", Integers.valueOf(192));
+ maxSecurityStrengths.put("SHA-512/256", Integers.valueOf(256));
+ }
+
+ static int getMaxSecurityStrength(Digest d)
+ {
+ return ((Integer)maxSecurityStrengths.get(d.getAlgorithmName())).intValue();
+ }
+
+ static int getMaxSecurityStrength(Mac m)
+ {
+ String name = m.getAlgorithmName();
+
+ return ((Integer)maxSecurityStrengths.get(name.substring(0, name.indexOf("/")))).intValue();
+ }
+
+ /**
+ * Used by both Dual EC and Hash.
+ */
+ static byte[] hash_df(Digest digest, byte[] seedMaterial, int seedLength)
+ {
+ // 1. temp = the Null string.
+ // 2. .
+ // 3. counter = an 8-bit binary value representing the integer "1".
+ // 4. For i = 1 to len do
+ // Comment : In step 4.1, no_of_bits_to_return
+ // is used as a 32-bit string.
+ // 4.1 temp = temp || Hash (counter || no_of_bits_to_return ||
+ // input_string).
+ // 4.2 counter = counter + 1.
+ // 5. requested_bits = Leftmost (no_of_bits_to_return) of temp.
+ // 6. Return SUCCESS and requested_bits.
+ byte[] temp = new byte[(seedLength + 7) / 8];
+
+ int len = temp.length / digest.getDigestSize();
+ int counter = 1;
+
+ byte[] dig = new byte[digest.getDigestSize()];
+
+ for (int i = 0; i <= len; i++)
+ {
+ digest.update((byte)counter);
+
+ digest.update((byte)(seedLength >> 24));
+ digest.update((byte)(seedLength >> 16));
+ digest.update((byte)(seedLength >> 8));
+ digest.update((byte)seedLength);
+
+ digest.update(seedMaterial, 0, seedMaterial.length);
+
+ digest.doFinal(dig, 0);
+
+ int bytesToCopy = ((temp.length - i * dig.length) > dig.length)
+ ? dig.length
+ : (temp.length - i * dig.length);
+ System.arraycopy(dig, 0, temp, i * dig.length, bytesToCopy);
+
+ counter++;
+ }
+
+ // do a left shift to get rid of excess bits.
+ if (seedLength % 8 != 0)
+ {
+ int shift = 8 - (seedLength % 8);
+ int carry = 0;
+
+ for (int i = 0; i != temp.length; i++)
+ {
+ int b = temp[i] & 0xff;
+ temp[i] = (byte)((b >>> shift) | (carry << (8 - shift)));
+ carry = b;
+ }
+ }
+
+ return temp;
+ }
+
+ static boolean isTooLarge(byte[] bytes, int maxBytes)
+ {
+ return bytes != null && bytes.length > maxBytes;
+ }
+}