diff options
Diffstat (limited to 'core/src/main/java/org/bouncycastle/crypto/prng')
18 files changed, 2347 insertions, 0 deletions
diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/BasicEntropySourceProvider.java b/core/src/main/java/org/bouncycastle/crypto/prng/BasicEntropySourceProvider.java new file mode 100644 index 00000000..9f1d0427 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/BasicEntropySourceProvider.java @@ -0,0 +1,53 @@ +package org.bouncycastle.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/bouncycastle/crypto/prng/DRBGProvider.java b/core/src/main/java/org/bouncycastle/crypto/prng/DRBGProvider.java new file mode 100644 index 00000000..c39760c9 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/DRBGProvider.java @@ -0,0 +1,8 @@ +package org.bouncycastle.crypto.prng; + +import org.bouncycastle.crypto.prng.drbg.SP80090DRBG; + +interface DRBGProvider +{ + SP80090DRBG get(EntropySource entropySource); +} diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/DigestRandomGenerator.java b/core/src/main/java/org/bouncycastle/crypto/prng/DigestRandomGenerator.java new file mode 100644 index 00000000..f36b62c2 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/DigestRandomGenerator.java @@ -0,0 +1,123 @@ +package org.bouncycastle.crypto.prng; + +import org.bouncycastle.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/bouncycastle/crypto/prng/EntropySource.java b/core/src/main/java/org/bouncycastle/crypto/prng/EntropySource.java new file mode 100644 index 00000000..53bc549d --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/EntropySource.java @@ -0,0 +1,25 @@ +package org.bouncycastle.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/bouncycastle/crypto/prng/EntropySourceProvider.java b/core/src/main/java/org/bouncycastle/crypto/prng/EntropySourceProvider.java new file mode 100644 index 00000000..190bf624 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/EntropySourceProvider.java @@ -0,0 +1,6 @@ +package org.bouncycastle.crypto.prng; + +public interface EntropySourceProvider +{ + EntropySource get(final int bitsRequired); +} diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java b/core/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java new file mode 100644 index 00000000..209b5e21 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java @@ -0,0 +1,135 @@ +package org.bouncycastle.crypto.prng; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.SecureRandom; + +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; + } + + // + // 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/bouncycastle/crypto/prng/RandomGenerator.java b/core/src/main/java/org/bouncycastle/crypto/prng/RandomGenerator.java new file mode 100644 index 00000000..47ff68e3 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/RandomGenerator.java @@ -0,0 +1,38 @@ +package org.bouncycastle.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/bouncycastle/crypto/prng/ReversedWindowGenerator.java b/core/src/main/java/org/bouncycastle/crypto/prng/ReversedWindowGenerator.java new file mode 100644 index 00000000..fbb2639c --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/ReversedWindowGenerator.java @@ -0,0 +1,111 @@ +package org.bouncycastle.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/bouncycastle/crypto/prng/SP800SecureRandom.java b/core/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java new file mode 100644 index 00000000..e1ec6c28 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandom.java @@ -0,0 +1,74 @@ +package org.bouncycastle.crypto.prng; + +import java.security.SecureRandom; + +import org.bouncycastle.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/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java b/core/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java new file mode 100644 index 00000000..66f05c5f --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java @@ -0,0 +1,249 @@ +package org.bouncycastle.crypto.prng; + +import java.security.SecureRandom; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.prng.drbg.CTRSP800DRBG; +import org.bouncycastle.crypto.prng.drbg.DualECSP800DRBG; +import org.bouncycastle.crypto.prng.drbg.HMacSP800DRBG; +import org.bouncycastle.crypto.prng.drbg.HashSP800DRBG; +import org.bouncycastle.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. + * + * @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); + } + + 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 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/bouncycastle/crypto/prng/ThreadedSeedGenerator.java b/core/src/main/java/org/bouncycastle/crypto/prng/ThreadedSeedGenerator.java new file mode 100644 index 00000000..6b2d5ec2 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/ThreadedSeedGenerator.java @@ -0,0 +1,95 @@ +package org.bouncycastle.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/bouncycastle/crypto/prng/VMPCRandomGenerator.java b/core/src/main/java/org/bouncycastle/crypto/prng/VMPCRandomGenerator.java new file mode 100644 index 00000000..2146af7f --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/VMPCRandomGenerator.java @@ -0,0 +1,127 @@ +package org.bouncycastle.crypto.prng; + +import org.bouncycastle.crypto.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/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java new file mode 100644 index 00000000..84fe4a40 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/CTRSP800DRBG.java @@ -0,0 +1,468 @@ +package org.bouncycastle.crypto.prng.drbg; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.prng.EntropySource; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.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)); + } + + /** + * 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/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java new file mode 100644 index 00000000..3cee39cb --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java @@ -0,0 +1,267 @@ +package org.bouncycastle.crypto.prng.drbg; + +import java.math.BigInteger; + +import org.bouncycastle.asn1.nist.NISTNamedCurves; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.prng.EntropySource; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.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 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; + + /** + * 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) + { + _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); + + if (securityStrength <= 128) + { + if (Utils.getMaxSecurityStrength(digest) < 128) + { + throw new IllegalArgumentException("Requested security strength is not supported by digest"); + } + _seedlen = 256; + _outlen = 240 / 8; + _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-256").getCurve(); + _P = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p256_Px), new ECFieldElement.Fp(_curve.getQ(), p256_Py)); + _Q = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p256_Qx), new ECFieldElement.Fp(_curve.getQ(), p256_Qy)); + } + else if (securityStrength <= 192) + { + if (Utils.getMaxSecurityStrength(digest) < 192) + { + throw new IllegalArgumentException("Requested security strength is not supported by digest"); + } + _seedlen = 384; + _outlen = 368 / 8; + _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-384").getCurve(); + _P = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p384_Px), new ECFieldElement.Fp(_curve.getQ(), p384_Py)); + _Q = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p384_Qx), new ECFieldElement.Fp(_curve.getQ(), p384_Qy)); + } + else if (securityStrength <= 256) + { + if (Utils.getMaxSecurityStrength(digest) < 256) + { + throw new IllegalArgumentException("Requested security strength is not supported by digest"); + } + _seedlen = 521; + _outlen = 504 / 8; + _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-521").getCurve(); + _P = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p521_Px), new ECFieldElement.Fp(_curve.getQ(), p521_Py)); + _Q = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p521_Qx), new ECFieldElement.Fp(_curve.getQ(), p521_Qy)); + } + else + { + throw new IllegalArgumentException("security strength cannot be greater than 256 bits"); + } + + _s = Utils.hash_df(_digest, seedMaterial, _seedlen); + _sLength = _s.length; + + _reseedCounter = 0; + } + + /** + * 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; + } + + 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); + } + + for (int i = 0; i < m; i++) + { + BigInteger t = new BigInteger(1, xor(_s, additionalInput)); + + _s = _P.multiply(t).getX().toBigInteger().toByteArray(); + + //System.err.println("S: " + new String(Hex.encode(_s))); + + byte[] r = _Q.multiply(new BigInteger(1, _s)).getX().toBigInteger().toByteArray(); + + if (r.length > _outlen) + { + System.arraycopy(r, r.length - _outlen, output, i * _outlen, _outlen); + } + else + { + System.arraycopy(r, 0, output, i * _outlen + (_outlen - r.length), r.length); + } + + //System.err.println("R: " + new String(Hex.encode(r))); + additionalInput = null; + + _reseedCounter++; + } + + if (m * _outlen < output.length) + { + BigInteger t = new BigInteger(1, xor(_s, additionalInput)); + + _s = _P.multiply(t).getX().toBigInteger().toByteArray(); + + byte[] r = _Q.multiply(new BigInteger(1, _s)).getX().toBigInteger().toByteArray(); + + System.arraycopy(r, 0, output, m * _outlen, output.length - (m * _outlen)); + } + + // Need to preserve length of S as unsigned int. + _s = BigIntegers.asUnsignedByteArray(_sLength, _P.multiply(new BigInteger(1, _s)).getX().toBigInteger()); + + 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; + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java new file mode 100644 index 00000000..3ddeaac6 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/HMacSP800DRBG.java @@ -0,0 +1,171 @@ +package org.bouncycastle.crypto.prng.drbg; + +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.prng.EntropySource; +import org.bouncycastle.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); + } + + /** + * 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/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java new file mode 100644 index 00000000..4ed57163 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/HashSP800DRBG.java @@ -0,0 +1,269 @@ +package org.bouncycastle.crypto.prng.drbg; + +import java.util.Hashtable; + +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.prng.EntropySource; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.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; + } + + /** + * 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) + { + _digest.update(input, 0, input.length); + byte[] hash = new byte[_digest.getDigestSize()]; + _digest.doFinal(hash, 0); + return hash; + } + + // 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; + for (int i = 0; i <= m; i++) + { + dig = hash(data); + + 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/bouncycastle/crypto/prng/drbg/SP80090DRBG.java b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/SP80090DRBG.java new file mode 100644 index 00000000..93bc8945 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/SP80090DRBG.java @@ -0,0 +1,25 @@ +package org.bouncycastle.crypto.prng.drbg; + +/** + * Interface to SP800-90A deterministic random bit generators. + */ +public interface SP80090DRBG +{ + /** + * 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/bouncycastle/crypto/prng/drbg/Utils.java b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/Utils.java new file mode 100644 index 00000000..f7a41176 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/Utils.java @@ -0,0 +1,103 @@ +package org.bouncycastle.crypto.prng.drbg; + +import java.util.Hashtable; + +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.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; + } +} |