diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2015-11-16 21:30:04 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2015-11-16 21:30:04 +0700 |
commit | 8bda1de4207632587d2df5fbd08797e22d5f8463 (patch) | |
tree | 206f6e7b0427cd96e7203d580ac08ffb4557180b /crypto/src | |
parent | Remove redundant semicolons (diff) | |
download | BouncyCastle.NET-ed25519-8bda1de4207632587d2df5fbd08797e22d5f8463.tar.xz |
Finish port of latest PRNG/DRBG stuff from Java
Diffstat (limited to 'crypto/src')
-rw-r--r-- | crypto/src/crypto/prng/BasicEntropySourceProvider.cs | 70 | ||||
-rw-r--r-- | crypto/src/crypto/prng/IDrbgProvider.cs | 11 | ||||
-rw-r--r-- | crypto/src/crypto/prng/IEntropySource.cs | 4 | ||||
-rw-r--r-- | crypto/src/crypto/prng/IEntropySourceProvider.cs | 4 | ||||
-rw-r--r-- | crypto/src/crypto/prng/SP800SecureRandom.cs | 82 | ||||
-rw-r--r-- | crypto/src/crypto/prng/SP800SecureRandomBuilder.cs | 208 | ||||
-rw-r--r-- | crypto/src/crypto/prng/X931Rng.cs | 141 | ||||
-rw-r--r-- | crypto/src/crypto/prng/X931SecureRandom.cs | 72 | ||||
-rw-r--r-- | crypto/src/crypto/prng/X931SecureRandomBuilder.cs | 87 | ||||
-rw-r--r-- | crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs | 275 | ||||
-rw-r--r-- | crypto/src/crypto/prng/drbg/DrbgUtilities.cs | 42 | ||||
-rw-r--r-- | crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs | 126 | ||||
-rw-r--r-- | crypto/src/crypto/prng/drbg/HashSP800Drbg.cs | 240 | ||||
-rw-r--r-- | crypto/src/crypto/prng/drbg/ISP80090Drbg.cs | 4 | ||||
-rw-r--r-- | crypto/src/security/SecureRandom.cs | 35 | ||||
-rw-r--r-- | crypto/src/util/Arrays.cs | 64 |
16 files changed, 1035 insertions, 430 deletions
diff --git a/crypto/src/crypto/prng/BasicEntropySourceProvider.cs b/crypto/src/crypto/prng/BasicEntropySourceProvider.cs new file mode 100644 index 000000000..0e929e0fd --- /dev/null +++ b/crypto/src/crypto/prng/BasicEntropySourceProvider.cs @@ -0,0 +1,70 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Prng +{ + /** + * An EntropySourceProvider where entropy generation is based on a SecureRandom output using SecureRandom.generateSeed(). + */ + public class BasicEntropySourceProvider + : IEntropySourceProvider + { + private readonly SecureRandom mSecureRandom; + private readonly bool mPredictionResistant; + + /** + * Create a entropy source provider based on the passed in SecureRandom. + * + * @param secureRandom 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 secureRandom, bool isPredictionResistant) + { + mSecureRandom = secureRandom; + mPredictionResistant = 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 IEntropySource Get(int bitsRequired) + { + return new BasicEntropySource(mSecureRandom, mPredictionResistant, bitsRequired); + } + + private class BasicEntropySource + : IEntropySource + { + private readonly SecureRandom mSecureRandom; + private readonly bool mPredictionResistant; + private readonly int mEntropySize; + + internal BasicEntropySource(SecureRandom secureRandom, bool predictionResistant, int entropySize) + { + this.mSecureRandom = secureRandom; + this.mPredictionResistant = predictionResistant; + this.mEntropySize = entropySize; + } + + bool IEntropySource.IsPredictionResistant + { + get { return mPredictionResistant; } + } + + byte[] IEntropySource.GetEntropy() + { + return mSecureRandom.GenerateSeed((mEntropySize + 7) / 8); + } + + int IEntropySource.EntropySize + { + get { return mEntropySize; } + } + } + } +} diff --git a/crypto/src/crypto/prng/IDrbgProvider.cs b/crypto/src/crypto/prng/IDrbgProvider.cs new file mode 100644 index 000000000..5ebf5fd8d --- /dev/null +++ b/crypto/src/crypto/prng/IDrbgProvider.cs @@ -0,0 +1,11 @@ +using System; + +using Org.BouncyCastle.Crypto.Prng.Drbg; + +namespace Org.BouncyCastle.Crypto.Prng +{ + internal interface IDrbgProvider + { + ISP80090Drbg Get(IEntropySource entropySource); + } +} diff --git a/crypto/src/crypto/prng/IEntropySource.cs b/crypto/src/crypto/prng/IEntropySource.cs index 33ce1b801..90b9e61af 100644 --- a/crypto/src/crypto/prng/IEntropySource.cs +++ b/crypto/src/crypto/prng/IEntropySource.cs @@ -1,9 +1,9 @@ using System; -namespace Org.BouncyCastle.Crypto +namespace Org.BouncyCastle.Crypto.Prng { /// <summary> - /// Base interface describing an entropy source for a DRGB. + /// Base interface describing an entropy source for a DRBG. /// </summary> public interface IEntropySource { diff --git a/crypto/src/crypto/prng/IEntropySourceProvider.cs b/crypto/src/crypto/prng/IEntropySourceProvider.cs index 990c6497b..643f92ef8 100644 --- a/crypto/src/crypto/prng/IEntropySourceProvider.cs +++ b/crypto/src/crypto/prng/IEntropySourceProvider.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; -using System.Text; -namespace Org.BouncyCastle.Crypto +namespace Org.BouncyCastle.Crypto.Prng { /// <summary> /// Base interface describing a provider of entropy sources. diff --git a/crypto/src/crypto/prng/SP800SecureRandom.cs b/crypto/src/crypto/prng/SP800SecureRandom.cs new file mode 100644 index 000000000..996ba0846 --- /dev/null +++ b/crypto/src/crypto/prng/SP800SecureRandom.cs @@ -0,0 +1,82 @@ +using System; + +using Org.BouncyCastle.Crypto.Prng.Drbg; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Prng +{ + public class SP800SecureRandom + : SecureRandom + { + private readonly IDrbgProvider mDrbgProvider; + private readonly bool mPredictionResistant; + private readonly SecureRandom mRandomSource; + private readonly IEntropySource mEntropySource; + + private ISP80090Drbg mDrbg; + + internal SP800SecureRandom(SecureRandom randomSource, IEntropySource entropySource, IDrbgProvider drbgProvider, bool predictionResistant) + : base((IRandomGenerator)null) + { + this.mRandomSource = randomSource; + this.mEntropySource = entropySource; + this.mDrbgProvider = drbgProvider; + this.mPredictionResistant = predictionResistant; + } + + public override void SetSeed(byte[] seed) + { + lock (this) + { + if (mRandomSource != null) + { + this.mRandomSource.SetSeed(seed); + } + } + } + + public override void SetSeed(long seed) + { + lock (this) + { + // this will happen when SecureRandom() is created + if (mRandomSource != null) + { + this.mRandomSource.SetSeed(seed); + } + } + } + + public override void NextBytes(byte[] bytes) + { + lock (this) + { + if (mDrbg == null) + { + mDrbg = mDrbgProvider.Get(mEntropySource); + } + + // check if a reseed is required... + if (mDrbg.Generate(bytes, null, mPredictionResistant) < 0) + { + mDrbg.Reseed(null); + mDrbg.Generate(bytes, null, mPredictionResistant); + } + } + } + + public override void NextBytes(byte[] buf, int off, int len) + { + byte[] bytes = new byte[len]; + NextBytes(bytes); + Array.Copy(bytes, 0, buf, off, len); + } + + public override byte[] GenerateSeed(int numBytes) + { + byte[] bytes = new byte[numBytes]; + NextBytes(bytes); + return bytes; + } + } +} diff --git a/crypto/src/crypto/prng/SP800SecureRandomBuilder.cs b/crypto/src/crypto/prng/SP800SecureRandomBuilder.cs new file mode 100644 index 000000000..7199f1ae7 --- /dev/null +++ b/crypto/src/crypto/prng/SP800SecureRandomBuilder.cs @@ -0,0 +1,208 @@ +using System; + +using Org.BouncyCastle.Crypto.Prng.Drbg; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Prng +{ + /** + * Builder class for making SecureRandom objects based on SP 800-90A Deterministic Random Bit Generators (DRBG). + */ + public class SP800SecureRandomBuilder + { + private readonly SecureRandom mRandom; + private readonly IEntropySourceProvider mEntropySourceProvider; + + private byte[] mPersonalizationString = null; + private int mSecurityStrength = 256; + private int mEntropyBitsRequired = 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, bool predictionResistant) + { + this.mRandom = entropySource; + this.mEntropySourceProvider = new BasicEntropySourceProvider(entropySource, 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(IEntropySourceProvider entropySourceProvider) + { + this.mRandom = null; + this.mEntropySourceProvider = 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.mPersonalizationString = 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.mSecurityStrength = 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.mEntropyBitsRequired = 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(IDigest digest, byte[] nonce, bool predictionResistant) + { + return new SP800SecureRandom(mRandom, mEntropySourceProvider.Get(mEntropyBitsRequired), + new HashDrbgProvider(digest, nonce, mPersonalizationString, mSecurityStrength), 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(IBlockCipher cipher, int keySizeInBits, byte[] nonce, bool predictionResistant) + { + return new SP800SecureRandom(mRandom, mEntropySourceProvider.Get(mEntropyBitsRequired), + new CtrDrbgProvider(cipher, keySizeInBits, nonce, mPersonalizationString, mSecurityStrength), 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(IMac hMac, byte[] nonce, bool predictionResistant) + { + return new SP800SecureRandom(mRandom, mEntropySourceProvider.Get(mEntropyBitsRequired), + new HMacDrbgProvider(hMac, nonce, mPersonalizationString, mSecurityStrength), predictionResistant); + } + + private class HashDrbgProvider + : IDrbgProvider + { + private readonly IDigest mDigest; + private readonly byte[] mNonce; + private readonly byte[] mPersonalizationString; + private readonly int mSecurityStrength; + + public HashDrbgProvider(IDigest digest, byte[] nonce, byte[] personalizationString, int securityStrength) + { + this.mDigest = digest; + this.mNonce = nonce; + this.mPersonalizationString = personalizationString; + this.mSecurityStrength = securityStrength; + } + + public ISP80090Drbg Get(IEntropySource entropySource) + { + return new HashSP800Drbg(mDigest, mSecurityStrength, entropySource, mPersonalizationString, mNonce); + } + } + + private class HMacDrbgProvider + : IDrbgProvider + { + private readonly IMac mHMac; + private readonly byte[] mNonce; + private readonly byte[] mPersonalizationString; + private readonly int mSecurityStrength; + + public HMacDrbgProvider(IMac hMac, byte[] nonce, byte[] personalizationString, int securityStrength) + { + this.mHMac = hMac; + this.mNonce = nonce; + this.mPersonalizationString = personalizationString; + this.mSecurityStrength = securityStrength; + } + + public ISP80090Drbg Get(IEntropySource entropySource) + { + return new HMacSP800Drbg(mHMac, mSecurityStrength, entropySource, mPersonalizationString, mNonce); + } + } + + private class CtrDrbgProvider + : IDrbgProvider + { + private readonly IBlockCipher mBlockCipher; + private readonly int mKeySizeInBits; + private readonly byte[] mNonce; + private readonly byte[] mPersonalizationString; + private readonly int mSecurityStrength; + + public CtrDrbgProvider(IBlockCipher blockCipher, int keySizeInBits, byte[] nonce, byte[] personalizationString, int securityStrength) + { + this.mBlockCipher = blockCipher; + this.mKeySizeInBits = keySizeInBits; + this.mNonce = nonce; + this.mPersonalizationString = personalizationString; + this.mSecurityStrength = securityStrength; + } + + public ISP80090Drbg Get(IEntropySource entropySource) + { + return new CtrSP800Drbg(mBlockCipher, mKeySizeInBits, mSecurityStrength, entropySource, mPersonalizationString, mNonce); + } + } + } +} diff --git a/crypto/src/crypto/prng/X931Rng.cs b/crypto/src/crypto/prng/X931Rng.cs new file mode 100644 index 000000000..d09cc6618 --- /dev/null +++ b/crypto/src/crypto/prng/X931Rng.cs @@ -0,0 +1,141 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Prng +{ + internal class X931Rng + { + private const long BLOCK64_RESEED_MAX = 1L << (16 - 1); + private const long BLOCK128_RESEED_MAX = 1L << (24 - 1); + private const int BLOCK64_MAX_BITS_REQUEST = 1 << (13 - 1); + private const int BLOCK128_MAX_BITS_REQUEST = 1 << (19 - 1); + + private readonly IBlockCipher mEngine; + private readonly IEntropySource mEntropySource; + + private readonly byte[] mDT; + private readonly byte[] mI; + private readonly byte[] mR; + + private byte[] mV; + + private long mReseedCounter = 1; + + /** + * + * @param engine + * @param entropySource + */ + internal X931Rng(IBlockCipher engine, byte[] dateTimeVector, IEntropySource entropySource) + { + this.mEngine = engine; + this.mEntropySource = entropySource; + + this.mDT = new byte[engine.GetBlockSize()]; + + Array.Copy(dateTimeVector, 0, mDT, 0, mDT.Length); + + this.mI = new byte[engine.GetBlockSize()]; + this.mR = new byte[engine.GetBlockSize()]; + } + + /** + * Populate a passed in array with random data. + * + * @param output output array for generated bits. + * @param predictionResistant true if a reseed should be forced, false otherwise. + * + * @return number of bits generated, -1 if a reseed required. + */ + internal int Generate(byte[] output, bool predictionResistant) + { + if (mR.Length == 8) // 64 bit block size + { + if (mReseedCounter > BLOCK64_RESEED_MAX) + return -1; + + if (IsTooLarge(output, BLOCK64_MAX_BITS_REQUEST / 8)) + throw new ArgumentException("Number of bits per request limited to " + BLOCK64_MAX_BITS_REQUEST, "output"); + } + else + { + if (mReseedCounter > BLOCK128_RESEED_MAX) + return -1; + + if (IsTooLarge(output, BLOCK128_MAX_BITS_REQUEST / 8)) + throw new ArgumentException("Number of bits per request limited to " + BLOCK128_MAX_BITS_REQUEST, "output"); + } + + if (predictionResistant || mV == null) + { + mV = mEntropySource.GetEntropy(); + if (mV.Length != mEngine.GetBlockSize()) + throw new InvalidOperationException("Insufficient entropy returned"); + } + + int m = output.Length / mR.Length; + + for (int i = 0; i < m; i++) + { + mEngine.ProcessBlock(mDT, 0, mI, 0); + Process(mR, mI, mV); + Process(mV, mR, mI); + + Array.Copy(mR, 0, output, i * mR.Length, mR.Length); + + Increment(mDT); + } + + int bytesToCopy = (output.Length - m * mR.Length); + + if (bytesToCopy > 0) + { + mEngine.ProcessBlock(mDT, 0, mI, 0); + Process(mR, mI, mV); + Process(mV, mR, mI); + + Array.Copy(mR, 0, output, m * mR.Length, bytesToCopy); + + Increment(mDT); + } + + mReseedCounter++; + + return output.Length; + } + + /** + * Reseed the RNG. + */ + internal void Reseed() + { + mV = mEntropySource.GetEntropy(); + if (mV.Length != mEngine.GetBlockSize()) + throw new InvalidOperationException("Insufficient entropy returned"); + mReseedCounter = 1; + } + + private void Process(byte[] res, byte[] a, byte[] b) + { + for (int i = 0; i != res.Length; i++) + { + res[i] = (byte)(a[i] ^ b[i]); + } + + mEngine.ProcessBlock(res, 0, res, 0); + } + + private void Increment(byte[] val) + { + for (int i = val.Length - 1; i >= 0; i--) + { + if (++val[i] != 0) + break; + } + } + + private static bool IsTooLarge(byte[] bytes, int maxBytes) + { + return bytes != null && bytes.Length > maxBytes; + } + } +} diff --git a/crypto/src/crypto/prng/X931SecureRandom.cs b/crypto/src/crypto/prng/X931SecureRandom.cs new file mode 100644 index 000000000..a87bf1567 --- /dev/null +++ b/crypto/src/crypto/prng/X931SecureRandom.cs @@ -0,0 +1,72 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Prng +{ + public class X931SecureRandom + : SecureRandom + { + private readonly bool mPredictionResistant; + private readonly SecureRandom mRandomSource; + private readonly X931Rng mDrbg; + + internal X931SecureRandom(SecureRandom randomSource, X931Rng drbg, bool predictionResistant) + : base((IRandomGenerator)null) + { + this.mRandomSource = randomSource; + this.mDrbg = drbg; + this.mPredictionResistant = predictionResistant; + } + + public override void SetSeed(byte[] seed) + { + lock (this) + { + if (mRandomSource != null) + { + this.mRandomSource.SetSeed(seed); + } + } + } + + public override void SetSeed(long seed) + { + lock (this) + { + // this will happen when SecureRandom() is created + if (mRandomSource != null) + { + this.mRandomSource.SetSeed(seed); + } + } + } + + public override void NextBytes(byte[] bytes) + { + lock (this) + { + // check if a reseed is required... + if (mDrbg.Generate(bytes, mPredictionResistant) < 0) + { + mDrbg.Reseed(); + mDrbg.Generate(bytes, mPredictionResistant); + } + } + } + + public override void NextBytes(byte[] buf, int off, int len) + { + byte[] bytes = new byte[len]; + NextBytes(bytes); + Array.Copy(bytes, 0, buf, off, len); + } + + public override byte[] GenerateSeed(int numBytes) + { + byte[] bytes = new byte[numBytes]; + NextBytes(bytes); + return bytes; + } + } +} diff --git a/crypto/src/crypto/prng/X931SecureRandomBuilder.cs b/crypto/src/crypto/prng/X931SecureRandomBuilder.cs new file mode 100644 index 000000000..31e94312e --- /dev/null +++ b/crypto/src/crypto/prng/X931SecureRandomBuilder.cs @@ -0,0 +1,87 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Crypto.Prng +{ + public class X931SecureRandomBuilder + { + private readonly SecureRandom mRandom; // JDK 1.1 complains on final. + + private IEntropySourceProvider mEntropySourceProvider; + private byte[] mDateTimeVector; + + /** + * 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 X931SecureRandomBuilder() + : 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 X931SecureRandomBuilder(SecureRandom entropySource, bool predictionResistant) + { + this.mRandom = entropySource; + this.mEntropySourceProvider = new BasicEntropySourceProvider(mRandom, 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 X931SecureRandomBuilder(IEntropySourceProvider entropySourceProvider) + { + this.mRandom = null; + this.mEntropySourceProvider = entropySourceProvider; + } + + public X931SecureRandomBuilder SetDateTimeVector(byte[] dateTimeVector) + { + this.mDateTimeVector = dateTimeVector; + return this; + } + + /** + * Construct a X9.31 secure random generator using the passed in engine and key. If predictionResistant is true the + * generator will be reseeded on each request. + * + * @param engine a block cipher to use as the operator. + * @param key the block cipher key to initialise engine with. + * @param predictionResistant true if engine to be reseeded on each use, false otherwise. + * @return a SecureRandom. + */ + public X931SecureRandom Build(IBlockCipher engine, KeyParameter key, bool predictionResistant) + { + if (mDateTimeVector == null) + { + mDateTimeVector = new byte[engine.GetBlockSize()]; + Pack.UInt64_To_BE((ulong)DateTimeUtilities.CurrentUnixMs(), mDateTimeVector, 0); + } + + engine.Init(true, key); + + return new X931SecureRandom(mRandom, new X931Rng(engine, mDateTimeVector, mEntropySourceProvider.Get(engine.GetBlockSize() * 8)), predictionResistant); + } + } +} diff --git a/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs b/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs index d9595d530..2e4fbe4ed 100644 --- a/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs +++ b/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs @@ -1,5 +1,6 @@ using System; +using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; @@ -8,24 +9,25 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg /** * A SP800-90A CTR DRBG. */ - public class CTRSP800Drbg: SP80090Drbg + public class CtrSP800Drbg + : ISP80090Drbg { - private static readonly long TDEA_RESEED_MAX = 1L << (32 - 1); - private static readonly long AES_RESEED_MAX = 1L << (48 - 1); - private static readonly int TDEA_MAX_BITS_REQUEST = 1 << (13 - 1); - private static readonly int AES_MAX_BITS_REQUEST = 1 << (19 - 1); - - private IEntropySource _entropySource; - private IBlockCipher _engine; - private int _keySizeInBits; - private int _seedLength; - private int _securityStrength; - - // internal state - private byte[] _Key; - private byte[] _V; - private long _reseedCounter = 0; - private bool _isTDEA = false; + private static readonly long TDEA_RESEED_MAX = 1L << (32 - 1); + private static readonly long AES_RESEED_MAX = 1L << (48 - 1); + private static readonly int TDEA_MAX_BITS_REQUEST = 1 << (13 - 1); + private static readonly int AES_MAX_BITS_REQUEST = 1 << (19 - 1); + + private readonly IEntropySource mEntropySource; + private readonly IBlockCipher mEngine; + private readonly int mKeySizeInBits; + private readonly int mSeedLength; + private readonly int mSecurityStrength; + + // internal state + private byte[] mKey; + private byte[] mV; + private long mReseedCounter = 0; + private bool mIsTdea = false; /** * Construct a SP800-90A CTR DRBG. @@ -39,66 +41,58 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg * @param personalizationString personalization string to distinguish this DRBG (may be null). * @param nonce nonce to further distinguish this DRBG (may be null). */ - public CTRSP800Drbg(IBlockCipher engine, int keySizeInBits, int securityStrength, IEntropySource entropySource, byte[] personalizationString, byte[] nonce) + public CtrSP800Drbg(IBlockCipher engine, int keySizeInBits, int securityStrength, IEntropySource entropySource, + byte[] personalizationString, byte[] nonce) { - _entropySource = entropySource; - _engine = engine; - - _keySizeInBits = keySizeInBits; - _securityStrength = securityStrength; - _seedLength = keySizeInBits + engine.getBlockSize() * 8; - _isTDEA = isTDEA(engine); - if (securityStrength > 256) - { throw new ArgumentException("Requested security strength is not supported by the derivation function"); - } - - if (getMaxSecurityStrength(engine, keySizeInBits) < securityStrength) - { + if (GetMaxSecurityStrength(engine, keySizeInBits) < securityStrength) throw new ArgumentException("Requested security strength is not supported by block cipher and key size"); - } - if (entropySource.EntropySize < securityStrength) - { throw new ArgumentException("Not enough entropy for security strength required"); - } - byte[] entropy = getEntropy(); // Get_entropy_input + mEntropySource = entropySource; + mEngine = engine; + + mKeySizeInBits = keySizeInBits; + mSecurityStrength = securityStrength; + mSeedLength = keySizeInBits + engine.GetBlockSize() * 8; + mIsTdea = IsTdea(engine); + + byte[] entropy = GetEntropy(); // Get_entropy_input - CTR_DRBG_Instantiate_algorithm(entropy, nonce, personalizationString); + CTR_DRBG_Instantiate_algorithm(entropy, nonce, personalizationString); } - private void CTR_DRBG_Instantiate_algorithm(byte[] entropy, byte[] nonce, - byte[] personalisationString) + 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); + byte[] seedMaterial = Arrays.ConcatenateAll(entropy, nonce, personalisationString); + byte[] seed = Block_Cipher_df(seedMaterial, mSeedLength); - int outlen = _engine.getBlockSize(); + int outlen = mEngine.GetBlockSize(); - _Key = new byte[(_keySizeInBits + 7) / 8]; - _V = new byte[outlen]; + mKey = new byte[(mKeySizeInBits + 7) / 8]; + mV = new byte[outlen]; - // _Key & _V are modified by this call - CTR_DRBG_Update(seed, _Key, _V); + // mKey & mV are modified by this call + CTR_DRBG_Update(seed, mKey, mV); - _reseedCounter = 1; + mReseedCounter = 1; } - private void CTR_DRBG_Update(byte[] seed, byte[] key, byte[] v) + 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(); + byte[] outputBlock = new byte[mEngine.GetBlockSize()]; + + int i = 0; + int outLen = mEngine.GetBlockSize(); - _engine.init(true, new KeyParameter(expandKey(key))); + mEngine.Init(true, new KeyParameter(ExpandKey(key))); while (i*outLen < seed.Length) { - addOneTo(v); - _engine.processBlock(v, 0, outputBlock, 0); + AddOneTo(v); + mEngine.ProcessBlock(v, 0, outputBlock, 0); int bytesToCopy = ((temp.Length - i * outLen) > outLen) ? outLen : (temp.Length - i * outLen); @@ -115,49 +109,48 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg private void CTR_DRBG_Reseed_algorithm(byte[] additionalInput) { - byte[] seedMaterial = Arrays.Concatenate(getEntropy(), additionalInput); + byte[] seedMaterial = Arrays.Concatenate(GetEntropy(), additionalInput); - seedMaterial = Block_Cipher_df(seedMaterial, _seedLength); + seedMaterial = Block_Cipher_df(seedMaterial, mSeedLength); - CTR_DRBG_Update(seedMaterial, _Key, _V); + CTR_DRBG_Update(seedMaterial, mKey, mV); - _reseedCounter = 1; + mReseedCounter = 1; } - private void XOR(byte[] output, byte[] a, byte[] b, int bOff) + private void XOR(byte[] output, byte[] a, byte[] b, int bOff) { - for (int i=0; i< output.Length; i++) + for (int i = 0; i < output.Length; i++) { - output[i] = (byte)(a[i] ^ b[i+bOff]); + output[i] = (byte)(a[i] ^ b[bOff + i]); } } - - private void addOneTo(byte[] longer) + + 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; - } + uint carry = 1; + int i = longer.Length; + while (--i >= 0) + { + carry += longer[i]; + longer[i] = (byte)carry; + carry >>= 8; + } } - private byte[] getEntropy() + private byte[] GetEntropy() { - byte[] entropy = _entropySource.getEntropy(); - if (entropy.Length < (_securityStrength + 7) / 8) - { - throw new IllegalStateException("Insufficient entropy provided by entropy source"); - } + byte[] entropy = mEntropySource.GetEntropy(); + if (entropy.Length < (mSecurityStrength + 7) / 8) + throw new InvalidOperationException("Insufficient entropy provided by entropy source"); return entropy; } // -- 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 + private static readonly 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. @@ -219,7 +212,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg // 15. Return SUCCESS and requested_bits. private byte[] Block_Cipher_df(byte[] inputString, int bitLength) { - int outLen = _engine.getBlockSize(); + int outLen = mEngine.GetBlockSize(); int L = inputString.Length; // already in bytes int N = bitLength / 8; // 4 S = L || N || inputstring || 0x80 @@ -232,16 +225,16 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg S[8 + L] = (byte)0x80; // S already padded with zeros - byte[] temp = new byte[_keySizeInBits / 8 + outLen]; + byte[] temp = new byte[mKeySizeInBits / 8 + outLen]; byte[] bccOut = new byte[outLen]; byte[] IV = new byte[outLen]; int i = 0; - byte[] K = new byte[_keySizeInBits / 8]; + byte[] K = new byte[mKeySizeInBits / 8]; Array.Copy(K_BITS, 0, K, 0, K.Length); - while (i*outLen*8 < _keySizeInBits + outLen *8) + while (i*outLen*8 < mKeySizeInBits + outLen *8) { copyIntToByteArray(IV, i, 0); BCC(bccOut, K, IV, S); @@ -261,11 +254,11 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg temp = new byte[bitLength / 2]; i = 0; - _engine.init(true, new KeyParameter(expandKey(K))); + mEngine.Init(true, new KeyParameter(ExpandKey(K))); while (i * outLen < temp.Length) { - _engine.processBlock(X, 0, X, 0); + mEngine.ProcessBlock(X, 0, X, 0); int bytesToCopy = ((temp.Length - i * outLen) > outLen) ? outLen @@ -292,23 +285,23 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg */ private void BCC(byte[] bccOut, byte[] k, byte[] iV, byte[] data) { - int outlen = _engine.getBlockSize(); + int outlen = mEngine.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))); + mEngine.Init(true, new KeyParameter(ExpandKey(k))); - _engine.processBlock(iV, 0, chainingValue, 0); + mEngine.ProcessBlock(iV, 0, chainingValue, 0); - for (int i = 0; i < n; i++) + for (int i = 0; i < n; i++) { XOR(inputBlock, chainingValue, data, i*outlen); - _engine.processBlock(inputBlock, 0, chainingValue, 0); + mEngine.ProcessBlock(inputBlock, 0, chainingValue, 0); } - Array.Copy(chainingValue, 0, bccOut, 0, bccOut.Length); + Array.Copy(chainingValue, 0, bccOut, 0, bccOut.Length); } private void copyIntToByteArray(byte[] buf, int value, int offSet) @@ -326,9 +319,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg */ public int BlockSize { - get { - return _V.Length * 8; - } + get { return mV.Length * 8; } } /** @@ -342,32 +333,24 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg */ public int Generate(byte[] output, byte[] additionalInput, bool predictionResistant) { - if (_isTDEA) + if (mIsTdea) { - if (_reseedCounter > TDEA_RESEED_MAX) - { + if (mReseedCounter > TDEA_RESEED_MAX) return -1; - } - if (Utils.isTooLarge(output, TDEA_MAX_BITS_REQUEST / 8)) - { - throw new ArgumentException("Number of bits per request limited to " + TDEA_MAX_BITS_REQUEST); - } + if (DrbgUtilities.IsTooLarge(output, TDEA_MAX_BITS_REQUEST / 8)) + throw new ArgumentException("Number of bits per request limited to " + TDEA_MAX_BITS_REQUEST, "output"); } else { - if (_reseedCounter > AES_RESEED_MAX) - { + if (mReseedCounter > AES_RESEED_MAX) return -1; - } - if (Utils.isTooLarge(output, AES_MAX_BITS_REQUEST / 8)) - { - throw new ArgumentException("Number of bits per request limited to " + AES_MAX_BITS_REQUEST); - } + if (DrbgUtilities.IsTooLarge(output, AES_MAX_BITS_REQUEST / 8)) + throw new ArgumentException("Number of bits per request limited to " + AES_MAX_BITS_REQUEST, "output"); } - if (predictionResistant) + if (predictionResistant) { CTR_DRBG_Reseed_algorithm(additionalInput); additionalInput = null; @@ -375,39 +358,39 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg if (additionalInput != null) { - additionalInput = Block_Cipher_df(additionalInput, _seedLength); - CTR_DRBG_Update(additionalInput, _Key, _V); + additionalInput = Block_Cipher_df(additionalInput, mSeedLength); + CTR_DRBG_Update(additionalInput, mKey, mV); } else { - additionalInput = new byte[_seedLength]; + additionalInput = new byte[mSeedLength]; } - byte[] tmp = new byte[_V.Length]; + byte[] tmp = new byte[mV.Length]; - _engine.init(true, new KeyParameter(expandKey(_Key))); + mEngine.Init(true, new KeyParameter(ExpandKey(mKey))); - for (int i = 0; i <= output.Length / tmp.Length; i++) + for (int i = 0; i <= output.Length / tmp.Length; i++) { - int bytesToCopy = ((output.Length - i * tmp.Length) > tmp.Length) - ? tmp.Length - : (output.Length - i * _V.Length); + int bytesToCopy = ((output.Length - i * tmp.Length) > tmp.Length) + ? tmp.Length + : (output.Length - i * mV.Length); - if (bytesToCopy != 0) + if (bytesToCopy != 0) { - addOneTo(_V); + AddOneTo(mV); - _engine.processBlock(_V, 0, tmp, 0); + mEngine.ProcessBlock(mV, 0, tmp, 0); - Array.Copy(tmp, 0, output, i * tmp.Length, bytesToCopy); + Array.Copy(tmp, 0, output, i * tmp.Length, bytesToCopy); } } - CTR_DRBG_Update(additionalInput, _Key, _V); + CTR_DRBG_Update(additionalInput, mKey, mV); - _reseedCounter++; + mReseedCounter++; - return output.Length * 8; + return output.Length * 8; } /** @@ -420,14 +403,14 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg CTR_DRBG_Reseed_algorithm(additionalInput); } - private boolean isTDEA(BlockCipher cipher) + private bool IsTdea(IBlockCipher cipher) { return cipher.AlgorithmName.Equals("DESede") || cipher.AlgorithmName.Equals("TDEA"); } - private int getMaxSecurityStrength(BlockCipher cipher, int keySizeInBits) + private int GetMaxSecurityStrength(IBlockCipher cipher, int keySizeInBits) { - if (isTDEA(cipher) && keySizeInBits == 168) + if (IsTdea(cipher) && keySizeInBits == 168) { return 112; } @@ -436,19 +419,19 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg return keySizeInBits; } - return -1; + return -1; } - byte[] expandKey(byte[] key) + private byte[] ExpandKey(byte[] key) { - if (_isTDEA) + if (mIsTdea) { // 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); + PadKey(key, 0, tmp, 0); + PadKey(key, 7, tmp, 8); + PadKey(key, 14, tmp, 16); return tmp; } @@ -466,7 +449,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg * @param tmp * @param tmpOff */ - private void padKey(byte[] keyMaster, int keyOff, byte[] tmp, int 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)); @@ -479,15 +462,15 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg 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)); + uint b = tmp[i]; + + uint parity = b ^ 1U; + parity ^= (parity >> 4); + parity ^= (parity >> 2); + parity ^= (parity >> 1); + parity &= 1U; + + tmp[i] = (byte)(b ^ parity); } } } diff --git a/crypto/src/crypto/prng/drbg/DrbgUtilities.cs b/crypto/src/crypto/prng/drbg/DrbgUtilities.cs index b5f3b5830..d9a1c439c 100644 --- a/crypto/src/crypto/prng/drbg/DrbgUtilities.cs +++ b/crypto/src/crypto/prng/drbg/DrbgUtilities.cs @@ -5,39 +5,39 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Prng.Drbg { - internal class Utils + internal class DrbgUtilities { private static readonly IDictionary maxSecurityStrengths = Platform.CreateHashtable(); - static Utils() + static DrbgUtilities() { - maxSecurityStrengths.put("SHA-1", 128); + maxSecurityStrengths.Add("SHA-1", 128); - maxSecurityStrengths.put("SHA-224", 192); - maxSecurityStrengths.put("SHA-256", 256); - maxSecurityStrengths.put("SHA-384", 256); - maxSecurityStrengths.put("SHA-512", 256); + maxSecurityStrengths.Add("SHA-224", 192); + maxSecurityStrengths.Add("SHA-256", 256); + maxSecurityStrengths.Add("SHA-384", 256); + maxSecurityStrengths.Add("SHA-512", 256); - maxSecurityStrengths.put("SHA-512/224", 192); - maxSecurityStrengths.put("SHA-512/256", 256); + maxSecurityStrengths.Add("SHA-512/224", 192); + maxSecurityStrengths.Add("SHA-512/256", 256); } - internal static int getMaxSecurityStrength(IDigest d) + internal static int GetMaxSecurityStrength(IDigest d) { return (int)maxSecurityStrengths[d.AlgorithmName]; } - internal static int getMaxSecurityStrength(IMac m) + internal static int GetMaxSecurityStrength(IMac m) { - String name = m.getAlgorithmName(); + string name = m.AlgorithmName; - return (int)maxSecurityStrengths[name.substring(0, name.indexOf("/"))]; + return (int)maxSecurityStrengths[name.Substring(0, name.IndexOf("/"))]; } /** * Used by both Dual EC and Hash. */ - internal static byte[] hash_df(Digest digest, byte[] seedMaterial, int seedLength) + internal static byte[] HashDF(IDigest digest, byte[] seedMaterial, int seedLength) { // 1. temp = the Null string. // 2. . @@ -52,10 +52,10 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg // 6. Return SUCCESS and requested_bits. byte[] temp = new byte[(seedLength + 7) / 8]; - int len = temp.Length / digest.getDigestSize(); + int len = temp.Length / digest.GetDigestSize(); int counter = 1; - byte[] dig = new byte[digest.getDigestSize()]; + byte[] dig = new byte[digest.GetDigestSize()]; for (int i = 0; i <= len; i++) { @@ -82,20 +82,20 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg if (seedLength % 8 != 0) { int shift = 8 - (seedLength % 8); - int carry = 0; + uint carry = 0; - for (int i = 0; i != temp.Length; i++) + for (int i = 0; i != temp.Length; i++) { - uint b = temp[i] & 0xff; + uint b = temp[i]; temp[i] = (byte)((b >> shift) | (carry << (8 - shift))); carry = b; } } - return temp; + return temp; } - internal static boolean isTooLarge(byte[] bytes, int maxBytes) + internal static bool IsTooLarge(byte[] bytes, int maxBytes) { return bytes != null && bytes.Length > maxBytes; } diff --git a/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs b/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs index 78a710097..78331705e 100644 --- a/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs +++ b/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs @@ -8,19 +8,21 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg /** * A SP800-90A HMAC DRBG. */ - public class HMacSP800Drbg: SP80090Drbg + public class HMacSP800Drbg + : ISP80090Drbg { - private readonly static long RESEED_MAX = 1L << (48 - 1); - private readonly static int MAX_BITS_REQUEST = 1 << (19 - 1); + private readonly static long RESEED_MAX = 1L << (48 - 1); + private readonly static int MAX_BITS_REQUEST = 1 << (19 - 1); - private byte[] _K; - private byte[] _V; - private long _reseedCounter; - private EntropySource _entropySource; - private Mac _hMac; - private int _securityStrength; + private readonly byte[] mK; + private readonly byte[] mV; + private readonly IEntropySource mEntropySource; + private readonly IMac mHMac; + private readonly int mSecurityStrength; - /** + private long mReseedCounter; + + /** * Construct a SP800-90A Hash DRBG. * <p> * Minimum entropy requirement is the security strength requested. @@ -33,33 +35,28 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg */ public HMacSP800Drbg(IMac hMac, int securityStrength, IEntropySource entropySource, byte[] personalizationString, byte[] nonce) { - if (securityStrength > Utils.getMaxSecurityStrength(hMac)) - { + if (securityStrength > DrbgUtilities.GetMaxSecurityStrength(hMac)) throw new ArgumentException("Requested security strength is not supported by the derivation function"); - } - if (entropySource.EntropySize < securityStrength) - { throw new ArgumentException("Not enough entropy for security strength required"); - } - _securityStrength = securityStrength; - _entropySource = entropySource; - _hMac = hMac; + mHMac = hMac; + mSecurityStrength = securityStrength; + mEntropySource = entropySource; - byte[] entropy = getEntropy(); - byte[] seedMaterial = Arrays.Concatenate(entropy, nonce, personalizationString); + byte[] entropy = GetEntropy(); + byte[] seedMaterial = Arrays.ConcatenateAll(entropy, nonce, personalizationString); - _K = new byte[hMac.GetMacSize()]; - _V = new byte[_K.Length]; - Arrays.fill(_V, (byte)1); + mK = new byte[hMac.GetMacSize()]; + mV = new byte[mK.Length]; + Arrays.Fill(mV, (byte)1); - hmac_DRBG_Update(seedMaterial); + hmac_DRBG_Update(seedMaterial); - _reseedCounter = 1; + mReseedCounter = 1; } - private void hmac_DRBG_Update(byte[] seedMaterial) + private void hmac_DRBG_Update(byte[] seedMaterial) { hmac_DRBG_Update_Func(seedMaterial, (byte)0x00); if (seedMaterial != null) @@ -70,22 +67,22 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg private void hmac_DRBG_Update_Func(byte[] seedMaterial, byte vValue) { - _hMac.Init(new KeyParameter(_K)); + mHMac.Init(new KeyParameter(mK)); - _hMac.BlockUpdate(_V, 0, _V.Length); - _hMac.Update(vValue); + mHMac.BlockUpdate(mV, 0, mV.Length); + mHMac.Update(vValue); if (seedMaterial != null) { - _hMac.update(seedMaterial, 0, seedMaterial.Length); + mHMac.BlockUpdate(seedMaterial, 0, seedMaterial.Length); } - _hMac.DoFinal(_K, 0); + mHMac.DoFinal(mK, 0); - _hMac.Init(new KeyParameter(_K)); - _hMac.BlockUpdate(_V, 0, _V.Length); + mHMac.Init(new KeyParameter(mK)); + mHMac.BlockUpdate(mV, 0, mV.Length); - _hMac.DoFinal(_V, 0); + mHMac.DoFinal(mV, 0); } /** @@ -95,9 +92,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg */ public int BlockSize { - get { - return _V.Length * 8; - } + get { return mV.Length * 8; } } /** @@ -109,23 +104,21 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg * * @return number of bits generated, -1 if a reseed required. */ - public int Generate(byte[] output, byte[] additionalInput, boolean predictionResistant) + public int Generate(byte[] output, byte[] additionalInput, bool 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 (numberOfBits > MAX_BITS_REQUEST) + throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output"); - if (_reseedCounter > RESEED_MAX) + if (mReseedCounter > RESEED_MAX) { return -1; } - if (predictionResistant) + if (predictionResistant) { - reseed(additionalInput); + Reseed(additionalInput); additionalInput = null; } @@ -135,36 +128,36 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg hmac_DRBG_Update(additionalInput); } - // 3. + // 3. byte[] rv = new byte[output.Length]; - int m = output.Length / _V.Length; + int m = output.Length / mV.Length; - _hMac.Init(new KeyParameter(_K)); + mHMac.Init(new KeyParameter(mK)); for (int i = 0; i < m; i++) { - _hMac.BlockUpdate(_V, 0, _V.Length); - _hMac.DoFinal(_V, 0); + mHMac.BlockUpdate(mV, 0, mV.Length); + mHMac.DoFinal(mV, 0); - Array.Copy(_V, 0, rv, i * _V.Length, _V.Length); + Array.Copy(mV, 0, rv, i * mV.Length, mV.Length); } - if (m * _V.Length < rv.Length) + if (m * mV.Length < rv.Length) { - _hMac.BlockUpdate(_V, 0, _V.Length); - _hMac.DoFinal(_V, 0); + mHMac.BlockUpdate(mV, 0, mV.Length); + mHMac.DoFinal(mV, 0); - Array.Copy(_V, 0, rv, m * _V.Length, rv.Length - (m * _V.Length)); + Array.Copy(mV, 0, rv, m * mV.Length, rv.Length - (m * mV.Length)); } - hmac_DRBG_Update(additionalInput); + hmac_DRBG_Update(additionalInput); - _reseedCounter++; + mReseedCounter++; Array.Copy(rv, 0, output, 0, output.Length); - return numberOfBits; + return numberOfBits; } /** @@ -174,22 +167,19 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg */ public void Reseed(byte[] additionalInput) { - byte[] entropy = getEntropy(); + byte[] entropy = GetEntropy(); byte[] seedMaterial = Arrays.Concatenate(entropy, additionalInput); hmac_DRBG_Update(seedMaterial); - _reseedCounter = 1; + mReseedCounter = 1; } - private byte[] getEntropy() + private byte[] GetEntropy() { - byte[] entropy = _entropySource.GetEntropy(); - - if (entropy.Length < (_securityStrength + 7) / 8) - { - throw new IllegalStateException("Insufficient entropy provided by entropy source"); - } + byte[] entropy = mEntropySource.GetEntropy(); + if (entropy.Length < (mSecurityStrength + 7) / 8) + throw new InvalidOperationException("Insufficient entropy provided by entropy source"); return entropy; } } diff --git a/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs b/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs index 868778817..493da5a75 100644 --- a/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs +++ b/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs @@ -8,36 +8,37 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg /** * A SP800-90A Hash DRBG. */ - public class HashSP800Drbg: SP80090Drbg + public class HashSP800Drbg + : ISP80090Drbg { - private readonly static byte[] ONE = { 0x01 }; + private readonly static byte[] ONE = { 0x01 }; - private readonly static long RESEED_MAX = 1L << (48 - 1); - private readonly static int MAX_BITS_REQUEST = 1 << (19 - 1); + private readonly static long RESEED_MAX = 1L << (48 - 1); + private readonly static int MAX_BITS_REQUEST = 1 << (19 - 1); private static readonly IDictionary seedlens = Platform.CreateHashtable(); static HashSP800Drbg() { - seedlens.Add("SHA-1", 440); - seedlens.Add("SHA-224", 440); - seedlens.Add("SHA-256", 440); - seedlens.Add("SHA-512/256", 440); - seedlens.Add("SHA-512/224", 440); - seedlens.Add("SHA-384", 888); - seedlens.Add("SHA-512", 888); - + seedlens.Add("SHA-1", 440); + seedlens.Add("SHA-224", 440); + seedlens.Add("SHA-256", 440); + seedlens.Add("SHA-512/256", 440); + seedlens.Add("SHA-512/224", 440); + seedlens.Add("SHA-384", 888); + seedlens.Add("SHA-512", 888); } - private IDigest _digest; - private byte[] _V; - private byte[] _C; - private long _reseedCounter; - private IEntropySource _entropySource; - private int _securityStrength; - private int _seedLength; + private readonly IDigest mDigest; + private readonly IEntropySource mEntropySource; + private readonly int mSecurityStrength; + private readonly int mSeedLength; - /** + private byte[] mV; + private byte[] mC; + private long mReseedCounter; + + /** * Construct a SP800-90A Hash DRBG. * <p> * Minimum entropy requirement is the security strength requested. @@ -50,22 +51,17 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg */ public HashSP800Drbg(IDigest digest, int securityStrength, IEntropySource entropySource, byte[] personalizationString, byte[] nonce) { - if (securityStrength > Utils.getMaxSecurityStrength(digest)) - { + if (securityStrength > DrbgUtilities.GetMaxSecurityStrength(digest)) throw new ArgumentException("Requested security strength is not supported by the derivation function"); - } - if (entropySource.EntropySize < securityStrength) - { throw new ArgumentException("Not enough entropy for security strength required"); - } - _digest = digest; - _entropySource = entropySource; - _securityStrength = securityStrength; - _seedLength = ((Integer)seedlens.get(digest.getAlgorithmName())).intValue(); + mDigest = digest; + mEntropySource = entropySource; + mSecurityStrength = securityStrength; + mSeedLength = (int)seedlens[digest.AlgorithmName]; - // 1. seed_material = entropy_input || nonce || personalization_string. + // 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 @@ -73,16 +69,16 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg // 5. reseed_counter = 1. // 6. Return V, C, and reseed_counter as the initial_working_state - byte[] entropy = getEntropy(); - byte[] seedMaterial = Arrays.Concatenate(entropy, nonce, personalizationString); - byte[] seed = Utils.hash_df(_digest, seedMaterial, _seedLength); + byte[] entropy = GetEntropy(); + byte[] seedMaterial = Arrays.ConcatenateAll(entropy, nonce, personalizationString); + byte[] seed = DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength); - _V = seed; - byte[] subV = new byte[_V.Length + 1]; - Array.Copy(_V, 0, subV, 1, _V.Length); - _C = Utils.hash_df(_digest, subV, _seedLength); + mV = seed; + byte[] subV = new byte[mV.Length + 1]; + Array.Copy(mV, 0, subV, 1, mV.Length); + mC = DrbgUtilities.HashDF(mDigest, subV, mSeedLength); - _reseedCounter = 1; + mReseedCounter = 1; } /** @@ -92,9 +88,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg */ public int BlockSize { - get { - return _digest.GetDigestSize () * 8; - } + get { return mDigest.GetDigestSize () * 8; } } /** @@ -121,97 +115,95 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg // 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; + int numberOfBits = output.Length * 8; if (numberOfBits > MAX_BITS_REQUEST) - { - throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST); - } + throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output"); - if (_reseedCounter > RESEED_MAX) - { + if (mReseedCounter > RESEED_MAX) return -1; - } - if (predictionResistant) + if (predictionResistant) { - reseed(additionalInput); + Reseed(additionalInput); additionalInput = null; } // 2. if (additionalInput != null) { - byte[] newInput = new byte[1 + _V.Length + additionalInput.Length]; + byte[] newInput = new byte[1 + mV.Length + additionalInput.Length]; newInput[0] = 0x02; - Array.Copy(_V, 0, newInput, 1, _V.Length); + Array.Copy(mV, 0, newInput, 1, mV.Length); // TODO: inOff / inLength - Array.Copy(additionalInput, 0, newInput, 1 + _V.Length, additionalInput.Length); - byte[] w = hash(newInput); + Array.Copy(additionalInput, 0, newInput, 1 + mV.Length, additionalInput.Length); + byte[] w = Hash(newInput); - addTo(_V, w); + AddTo(mV, w); } - - // 3. - byte[] rv = hashgen(_V, numberOfBits); - - // 4. - byte[] subH = new byte[_V.Length + 1]; - Array.Copy(_V, 0, subH, 1, _V.Length); + + // 3. + byte[] rv = hashgen(mV, numberOfBits); + + // 4. + byte[] subH = new byte[mV.Length + 1]; + Array.Copy(mV, 0, subH, 1, mV.Length); subH[0] = 0x03; - - byte[] H = hash(subH); - - // 5. - addTo(_V, H); - addTo(_V, _C); + + byte[] H = Hash(subH); + + // 5. + AddTo(mV, H); + AddTo(mV, mC); 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); + c[0] = (byte)(mReseedCounter >> 24); + c[1] = (byte)(mReseedCounter >> 16); + c[2] = (byte)(mReseedCounter >> 8); + c[3] = (byte)mReseedCounter; + + AddTo(mV, c); - _reseedCounter++; + mReseedCounter++; Array.Copy(rv, 0, output, 0, output.Length); return numberOfBits; } - private byte[] getEntropy() + private byte[] GetEntropy() { - byte[] entropy = _entropySource.getEntropy(); - if (entropy.Length < (_securityStrength + 7) / 8) - { - throw new IllegalStateException("Insufficient entropy provided by entropy source"); - } + byte[] entropy = mEntropySource.GetEntropy(); + if (entropy.Length < (mSecurityStrength + 7) / 8) + throw new InvalidOperationException("Insufficient entropy provided by entropy source"); return entropy; } // 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) + 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; - } + int off = longer.Length - shorter.Length; + + uint carry = 0; + int i = shorter.Length; + while (--i >= 0) + { + carry += (uint)longer[off + i] + (uint)shorter[i]; + longer[off + i] = (byte)carry; + carry >>= 8; + } + + i = off; + while (--i >= 0) + { + carry += longer[i]; + longer[i] = (byte)carry; + carry >>= 8; + } } - /** + /** * Reseed the DRBG. * * @param additionalInput additional input to be added to the DRBG in this step. @@ -231,33 +223,33 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg // 6. Return V, C, and reseed_counter for the new_working_state. // // Comment: Precede with a byte of all zeros. - byte[] entropy = getEntropy(); - byte[] seedMaterial = Arrays.Concatenate(ONE, _V, entropy, additionalInput); - byte[] seed = Utils.hash_df(_digest, seedMaterial, _seedLength); + byte[] entropy = GetEntropy(); + byte[] seedMaterial = Arrays.ConcatenateAll(ONE, mV, entropy, additionalInput); + byte[] seed = DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength); - _V = seed; - byte[] subV = new byte[_V.Length + 1]; + mV = seed; + byte[] subV = new byte[mV.Length + 1]; subV[0] = 0x00; - Array.Copy(_V, 0, subV, 1, _V.Length); - _C = Utils.hash_df(_digest, subV, _seedLength); + Array.Copy(mV, 0, subV, 1, mV.Length); + mC = DrbgUtilities.HashDF(mDigest, subV, mSeedLength); - _reseedCounter = 1; - } - - private byte[] hash(byte[] input) - { - byte[] hash = new byte[_digest.GetDigestSize()]; - doHash(input, hash); - return hash; + mReseedCounter = 1; } - private void doHash(byte[] input, byte[] output) - { - _digest.BlockUpdate(input, 0, input.Length); - _digest.DoFinal(output, 0); - } + private byte[] Hash(byte[] input) + { + byte[] hash = new byte[mDigest.GetDigestSize()]; + DoHash(input, hash); + return hash; + } + + private void DoHash(byte[] input, byte[] output) + { + mDigest.BlockUpdate(input, 0, input.Length); + mDigest.DoFinal(output, 0); + } - // 1. m = [requested_number_of_bits / outlen] + // 1. m = [requested_number_of_bits / outlen] // 2. data = V. // 3. W = the Null string. // 4. For i = 1 to m @@ -268,25 +260,25 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg // 5. returned_bits = Leftmost (requested_no_of_bits) bits of W. private byte[] hashgen(byte[] input, int lengthInBits) { - int digestSize = _digest.GetDigestSize(); + int digestSize = mDigest.GetDigestSize(); int m = (lengthInBits / 8) / digestSize; - byte[] data = new byte[input.Length]; + byte[] data = new byte[input.Length]; Array.Copy(input, 0, data, 0, input.Length); byte[] W = new byte[lengthInBits / 8]; - byte[] dig = new byte[_digest.GetDigestSize()]; + byte[] dig = new byte[mDigest.GetDigestSize()]; for (int i = 0; i <= m; i++) { - doHash(data, dig); + DoHash(data, dig); int bytesToCopy = ((W.Length - i * dig.Length) > dig.Length) ? dig.Length : (W.Length - i * dig.Length); Array.Copy(dig, 0, W, i * dig.Length, bytesToCopy); - addTo(data, ONE); + AddTo(data, ONE); } return W; diff --git a/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs b/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs index c39cf365f..0e398209e 100644 --- a/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs +++ b/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs @@ -1,9 +1,11 @@ +using System; + namespace Org.BouncyCastle.Crypto.Prng.Drbg { /** * Interface to SP800-90A deterministic random bit generators. */ - public interface SP80090Drbg + public interface ISP80090Drbg { /** * Return the block size of the DRBG. diff --git a/crypto/src/security/SecureRandom.cs b/crypto/src/security/SecureRandom.cs index 4894a93e6..875709e57 100644 --- a/crypto/src/security/SecureRandom.cs +++ b/crypto/src/security/SecureRandom.cs @@ -164,13 +164,7 @@ namespace Org.BouncyCastle.Security public override int Next() { - for (;;) - { - int i = NextInt() & int.MaxValue; - - if (i != int.MaxValue) - return i; - } + return NextInt() & int.MaxValue; } public override int Next(int maxValue) @@ -184,11 +178,9 @@ namespace Org.BouncyCastle.Security } // Test whether maxValue is a power of 2 - if ((maxValue & -maxValue) == maxValue) + if ((maxValue & (maxValue - 1)) == 0) { - int val = NextInt() & int.MaxValue; - long lr = ((long) maxValue * (long) val) >> 31; - return (int) lr; + return NextInt() & (maxValue - 1); } int bits, result; @@ -244,16 +236,17 @@ namespace Org.BouncyCastle.Security public virtual int NextInt() { - byte[] intBytes = new byte[4]; - NextBytes(intBytes); - - int result = 0; - for (int i = 0; i < 4; i++) - { - result = (result << 8) + (intBytes[i] & 0xff); - } - - return result; + byte[] bytes = new byte[4]; + NextBytes(bytes); + + uint result = bytes[0]; + result <<= 8; + result |= bytes[1]; + result <<= 8; + result |= bytes[2]; + result <<= 8; + result |= bytes[3]; + return (int)result; } public virtual long NextLong() diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs index 32c56b8b5..df9b4e7ee 100644 --- a/crypto/src/util/Arrays.cs +++ b/crypto/src/util/Arrays.cs @@ -591,57 +591,33 @@ namespace Org.BouncyCastle.Utilities return rv; } - public static byte[] Concatenate(byte[] a, byte[] b, byte[] c) + public static byte[] ConcatenateAll(params byte[][] vs) { - if (a != null && b != null && c != null) - { - byte[] rv = new byte[a.Length + b.Length + c.Length]; - - Array.Copy(a, 0, rv, 0, a.Length); - Array.Copy(b, 0, rv, a.Length, b.Length); - Array.Copy(c, 0, rv, a.Length + b.Length, c.Length); + byte[][] nonNull = new byte[vs.Length][]; + int count = 0; + int totalLength = 0; - return rv; - } - else if (b == null) - { - return Concatenate(a, c); - } - else + for (int i = 0; i < vs.Length; ++i) + { + byte[] v = vs[i]; + if (v != null) { - return Concatenate(a, b); + nonNull[count++] = v; + totalLength += v.Length; } - } + } - public static byte[] Concatenate(byte[] a, byte[] b, byte[] c, byte[] d) - { - if (a != null && b != null && c != null && d != null) - { - byte[] rv = new byte[a.Length + b.Length + c.Length + d.Length]; + byte[] result = new byte[totalLength]; + int pos = 0; - Array.Copy(a, 0, rv, 0, a.Length); - Array.Copy(b, 0, rv, a.Length, b.Length); - Array.Copy(c, 0, rv, a.Length + b.Length, c.Length); - Array.Copy(d, 0, rv, a.Length + b.Length + c.Length, d.Length); + for (int j = 0; j < count; ++j) + { + byte[] v = nonNull[j]; + Array.Copy(v, 0, result, pos, v.Length); + pos += v.Length; + } - return rv; - } - else if (d == null) - { - return Concatenate(a, b, c); - } - else if (c == null) - { - return Concatenate(a, b, d); - } - else if (b == null) - { - return Concatenate(a, c, d); - } - else - { - return Concatenate(b, c, d); - } + return result; } public static int[] Concatenate(int[] a, int[] b) |