diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2022-08-29 23:35:25 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2022-08-29 23:35:25 +0700 |
commit | 2fd2b7871f6466666bd422bdf578733aa3e9d197 (patch) | |
tree | 2190c275da3fab3364fc3065ce77a559c158277d /crypto | |
parent | SecureRandom fixups in tests (diff) | |
download | BouncyCastle.NET-ed25519-2fd2b7871f6466666bd422bdf578733aa3e9d197.tar.xz |
Span-based variant for ISP80090Drbg.Generate
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/src/crypto/prng/SP800SecureRandom.cs | 30 | ||||
-rw-r--r-- | crypto/src/crypto/prng/X931SecureRandom.cs | 2 | ||||
-rw-r--r-- | crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs | 75 | ||||
-rw-r--r-- | crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs | 66 | ||||
-rw-r--r-- | crypto/src/crypto/prng/drbg/HashSP800Drbg.cs | 108 | ||||
-rw-r--r-- | crypto/src/crypto/prng/drbg/ISP80090Drbg.cs | 12 |
6 files changed, 255 insertions, 38 deletions
diff --git a/crypto/src/crypto/prng/SP800SecureRandom.cs b/crypto/src/crypto/prng/SP800SecureRandom.cs index fb5c3a677..e08a0242c 100644 --- a/crypto/src/crypto/prng/SP800SecureRandom.cs +++ b/crypto/src/crypto/prng/SP800SecureRandom.cs @@ -15,8 +15,9 @@ namespace Org.BouncyCastle.Crypto.Prng private ISP80090Drbg mDrbg; - internal SP800SecureRandom(SecureRandom randomSource, IEntropySource entropySource, IDrbgProvider drbgProvider, bool predictionResistant) - : base((IRandomGenerator)null) + internal SP800SecureRandom(SecureRandom randomSource, IEntropySource entropySource, IDrbgProvider drbgProvider, + bool predictionResistant) + : base(null) { this.mRandomSource = randomSource; this.mEntropySource = entropySource; @@ -70,12 +71,25 @@ namespace Org.BouncyCastle.Crypto.Prng } } - // TODO Add efficient override (needs ISP80090Drbg support for spans) -//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER -// public override void NextBytes(Span<byte> buffer) -// { -// } -//#endif +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void NextBytes(Span<byte> buffer) + { + lock (this) + { + if (mDrbg == null) + { + mDrbg = mDrbgProvider.Get(mEntropySource); + } + + // check if a reseed is required... + if (mDrbg.Generate(buffer, null, mPredictionResistant) < 0) + { + mDrbg.Reseed(null); + mDrbg.Generate(buffer, null, mPredictionResistant); + } + } + } +#endif public override byte[] GenerateSeed(int numBytes) { diff --git a/crypto/src/crypto/prng/X931SecureRandom.cs b/crypto/src/crypto/prng/X931SecureRandom.cs index 01678af8f..d38e6d6d8 100644 --- a/crypto/src/crypto/prng/X931SecureRandom.cs +++ b/crypto/src/crypto/prng/X931SecureRandom.cs @@ -12,7 +12,7 @@ namespace Org.BouncyCastle.Crypto.Prng private readonly X931Rng mDrbg; internal X931SecureRandom(SecureRandom randomSource, X931Rng drbg, bool predictionResistant) - : base((IRandomGenerator)null) + : base(null) { this.mRandomSource = randomSource; this.mDrbg = drbg; diff --git a/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs b/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs index a7b1326c3..ddb503aa0 100644 --- a/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs +++ b/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs @@ -334,7 +334,10 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg public int Generate(byte[] output, int outputOff, int outputLen, byte[] additionalInput, bool predictionResistant) { - if (mIsTdea) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return Generate(output.AsSpan(outputOff, outputLen), additionalInput, predictionResistant); +#else + if (mIsTdea) { if (mReseedCounter > TDEA_RESEED_MAX) return -1; @@ -390,14 +393,78 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg mReseedCounter++; return outputLen * 8; - } +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int Generate(Span<byte> output, byte[] additionalInput, bool predictionResistant) + { + int outputLen = output.Length; + if (mIsTdea) + { + if (mReseedCounter > TDEA_RESEED_MAX) + return -1; - /** + if (outputLen > TDEA_MAX_BITS_REQUEST / 8) + throw new ArgumentException("Number of bits per request limited to " + TDEA_MAX_BITS_REQUEST, "output"); + } + else + { + if (mReseedCounter > AES_RESEED_MAX) + return -1; + + if (outputLen > AES_MAX_BITS_REQUEST / 8) + throw new ArgumentException("Number of bits per request limited to " + AES_MAX_BITS_REQUEST, "output"); + } + + if (predictionResistant) + { + CTR_DRBG_Reseed_algorithm(additionalInput); + additionalInput = null; + } + + if (additionalInput != null) + { + additionalInput = Block_Cipher_df(additionalInput, mSeedLength); + CTR_DRBG_Update(additionalInput, mKey, mV); + } + else + { + additionalInput = new byte[mSeedLength]; + } + + byte[] tmp = new byte[mV.Length]; + + mEngine.Init(true, new KeyParameter(ExpandKey(mKey))); + + for (int i = 0, limit = outputLen / tmp.Length; i <= limit; i++) + { + int bytesToCopy = System.Math.Min(tmp.Length, outputLen - i * tmp.Length); + + if (bytesToCopy != 0) + { + AddOneTo(mV); + + mEngine.ProcessBlock(mV, 0, tmp, 0); + + tmp[..bytesToCopy].CopyTo(output[(i * tmp.Length)..]); + } + } + + CTR_DRBG_Update(additionalInput, mKey, mV); + + mReseedCounter++; + + return outputLen * 8; + } +#endif + + /** * Reseed the DRBG. * * @param additionalInput additional input to be added to the DRBG in this step. */ - public void Reseed(byte[] additionalInput) + public void Reseed(byte[] additionalInput) { CTR_DRBG_Reseed_algorithm(additionalInput); } diff --git a/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs b/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs index 0ec0e8b71..5a684a89d 100644 --- a/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs +++ b/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs @@ -107,15 +107,16 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg public int Generate(byte[] output, int outputOff, int outputLen, byte[] additionalInput, bool predictionResistant) { - int numberOfBits = outputLen * 8; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return Generate(output.AsSpan(outputOff, outputLen), additionalInput, predictionResistant); +#else + int numberOfBits = outputLen * 8; if (numberOfBits > MAX_BITS_REQUEST) throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output"); if (mReseedCounter > RESEED_MAX) - { return -1; - } if (predictionResistant) { @@ -159,14 +160,69 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg Array.Copy(rv, 0, output, outputOff, outputLen); return numberOfBits; +#endif } - /** +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int Generate(Span<byte> output, byte[] additionalInput, bool predictionResistant) + { + int outputLen = output.Length; + int numberOfBits = outputLen * 8; + + if (numberOfBits > MAX_BITS_REQUEST) + throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output"); + + if (mReseedCounter > RESEED_MAX) + return -1; + + if (predictionResistant) + { + Reseed(additionalInput); + additionalInput = null; + } + + // 2. + if (additionalInput != null) + { + hmac_DRBG_Update(additionalInput); + } + + // 3. + int m = outputLen / mV.Length; + + mHMac.Init(new KeyParameter(mK)); + + for (int i = 0; i < m; i++) + { + mHMac.BlockUpdate(mV, 0, mV.Length); + mHMac.DoFinal(mV, 0); + + mV.CopyTo(output[(i * mV.Length)..]); + } + + int remaining = outputLen - m * mV.Length; + if (remaining > 0) + { + mHMac.BlockUpdate(mV, 0, mV.Length); + mHMac.DoFinal(mV, 0); + + mV[..remaining].CopyTo(output[(m * mV.Length)..]); + } + + hmac_DRBG_Update(additionalInput); + + mReseedCounter++; + + return numberOfBits; + } +#endif + + /** * Reseed the DRBG. * * @param additionalInput additional input to be added to the DRBG in this step. */ - public void Reseed(byte[] additionalInput) + public void Reseed(byte[] additionalInput) { byte[] entropy = GetEntropy(); byte[] seedMaterial = Arrays.Concatenate(entropy, additionalInput); diff --git a/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs b/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs index 3f1c628bd..2f73b1f4e 100644 --- a/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs +++ b/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs @@ -104,20 +104,23 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg public int Generate(byte[] output, int outputOff, int outputLen, byte[] additionalInput, bool 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 = outputLen * 8; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return Generate(output.AsSpan(outputOff, outputLen), additionalInput, predictionResistant); +#else + // 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 = outputLen * 8; if (numberOfBits > MAX_BITS_REQUEST) throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output"); @@ -169,9 +172,82 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg Array.Copy(rv, 0, output, outputOff, outputLen); return numberOfBits; - } +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int Generate(Span<byte> output, byte[] additionalInput, bool 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 outputLen = output.Length; + int numberOfBits = outputLen * 8; + + if (numberOfBits > MAX_BITS_REQUEST) + throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output"); + + if (mReseedCounter > RESEED_MAX) + return -1; + + if (predictionResistant) + { + Reseed(additionalInput); + additionalInput = null; + } + + // 2. + if (additionalInput != null) + { + byte[] newInput = new byte[1 + mV.Length + additionalInput.Length]; + newInput[0] = 0x02; + Array.Copy(mV, 0, newInput, 1, mV.Length); + Array.Copy(additionalInput, 0, newInput, 1 + mV.Length, additionalInput.Length); + byte[] w = Hash(newInput); + + AddTo(mV, w); + } + + // 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(mV, H); + AddTo(mV, mC); + byte[] c = new byte[4]; + c[0] = (byte)(mReseedCounter >> 24); + c[1] = (byte)(mReseedCounter >> 16); + c[2] = (byte)(mReseedCounter >> 8); + c[3] = (byte)mReseedCounter; + + AddTo(mV, c); + + mReseedCounter++; + + rv.CopyTo(output); + + return numberOfBits; + } +#endif - private byte[] GetEntropy() + private byte[] GetEntropy() { byte[] entropy = mEntropySource.GetEntropy(); if (entropy.Length < (mSecurityStrength + 7) / 8) diff --git a/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs b/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs index 78cbcd92f..e067f20a3 100644 --- a/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs +++ b/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs @@ -14,7 +14,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg */ int BlockSize { get; } - /** + /** * Populate a passed in array with random data. * * @param output output array for generated bits. @@ -23,13 +23,17 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg * * @return number of bits generated, -1 if a reseed required. */ - int Generate(byte[] output, int outputOff, int outputLen, byte[] additionalInput, bool predictionResistant); + int Generate(byte[] output, int outputOff, int outputLen, byte[] additionalInput, bool predictionResistant); - /** +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + int Generate(Span<byte> output, byte[] additionalInput, bool predictionResistant); +#endif + + /** * Reseed the DRBG. * * @param additionalInput additional input to be added to the DRBG in this step. */ - void Reseed(byte[] additionalInput); + void Reseed(byte[] additionalInput); } } |