diff --git a/crypto/src/crypto/IEntropySource.cs b/crypto/src/crypto/IEntropySource.cs
index 62e3bc76c..2e088ed27 100644
--- a/crypto/src/crypto/IEntropySource.cs
+++ b/crypto/src/crypto/IEntropySource.cs
@@ -19,6 +19,10 @@ namespace Org.BouncyCastle.Crypto
/// <returns>The entropy bytes.</returns>
byte[] GetEntropy();
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ int GetEntropy(Span<byte> output);
+#endif
+
/// <summary>
/// Return the number of bits of entropy this source can produce.
/// </summary>
diff --git a/crypto/src/crypto/parameters/DesParameters.cs b/crypto/src/crypto/parameters/DesParameters.cs
index a1f67e2b1..28881f21c 100644
--- a/crypto/src/crypto/parameters/DesParameters.cs
+++ b/crypto/src/crypto/parameters/DesParameters.cs
@@ -135,5 +135,15 @@ namespace Org.BouncyCastle.Crypto.Parameters
bytes[off + i] = SetOddParity(bytes[off + i]);
}
}
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public static void SetOddParity(Span<byte> bytes)
+ {
+ for (int i = 0; i < bytes.Length; i++)
+ {
+ bytes[i] = SetOddParity(bytes[i]);
+ }
+ }
+#endif
}
}
diff --git a/crypto/src/crypto/prng/BasicEntropySourceProvider.cs b/crypto/src/crypto/prng/BasicEntropySourceProvider.cs
index 31a8461f0..485cf25ab 100644
--- a/crypto/src/crypto/prng/BasicEntropySourceProvider.cs
+++ b/crypto/src/crypto/prng/BasicEntropySourceProvider.cs
@@ -62,6 +62,15 @@ namespace Org.BouncyCastle.Crypto.Prng
return SecureRandom.GetNextBytes(mSecureRandom, (mEntropySize + 7) / 8);
}
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ int IEntropySource.GetEntropy(Span<byte> output)
+ {
+ int length = (mEntropySize + 7) / 8;
+ mSecureRandom.NextBytes(output[..length]);
+ return length;
+ }
+#endif
+
int IEntropySource.EntropySize
{
get { return mEntropySize; }
diff --git a/crypto/src/crypto/prng/CryptoApiEntropySourceProvider.cs b/crypto/src/crypto/prng/CryptoApiEntropySourceProvider.cs
index 635af2bd9..9a2f6de2c 100644
--- a/crypto/src/crypto/prng/CryptoApiEntropySourceProvider.cs
+++ b/crypto/src/crypto/prng/CryptoApiEntropySourceProvider.cs
@@ -56,6 +56,15 @@ namespace Org.BouncyCastle.Crypto.Prng
return result;
}
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ int IEntropySource.GetEntropy(Span<byte> output)
+ {
+ int length = (mEntropySize + 7) / 8;
+ mRng.GetBytes(output[..length]);
+ return length;
+ }
+#endif
+
int IEntropySource.EntropySize
{
get { return mEntropySize; }
diff --git a/crypto/src/crypto/prng/SP800SecureRandom.cs b/crypto/src/crypto/prng/SP800SecureRandom.cs
index 1409df2fa..a18576d03 100644
--- a/crypto/src/crypto/prng/SP800SecureRandom.cs
+++ b/crypto/src/crypto/prng/SP800SecureRandom.cs
@@ -68,6 +68,9 @@ namespace Org.BouncyCastle.Crypto.Prng
public override void NextBytes(byte[] buf, int off, int len)
{
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ NextBytes(buf.AsSpan(off, len));
+#else
lock (this)
{
if (mDrbg == null)
@@ -82,6 +85,7 @@ namespace Org.BouncyCastle.Crypto.Prng
mDrbg.Generate(buf, off, len, null, mPredictionResistant);
}
}
+#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
@@ -95,10 +99,10 @@ namespace Org.BouncyCastle.Crypto.Prng
}
// check if a reseed is required...
- if (mDrbg.Generate(buffer, null, mPredictionResistant) < 0)
+ if (mDrbg.Generate(buffer, mPredictionResistant) < 0)
{
- mDrbg.Reseed(null);
- mDrbg.Generate(buffer, null, mPredictionResistant);
+ mDrbg.Reseed(ReadOnlySpan<byte>.Empty);
+ mDrbg.Generate(buffer, mPredictionResistant);
}
}
}
diff --git a/crypto/src/crypto/prng/X931Rng.cs b/crypto/src/crypto/prng/X931Rng.cs
index f0bfdc9f1..25dba89f5 100644
--- a/crypto/src/crypto/prng/X931Rng.cs
+++ b/crypto/src/crypto/prng/X931Rng.cs
@@ -38,16 +38,11 @@ namespace Org.BouncyCastle.Crypto.Prng
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, int outputOff, int outputLen, bool predictionResistant)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ internal int Generate(Span<byte> output, bool predictionResistant)
{
+ int outputLen = output.Length;
+
if (mR.Length == 8) // 64 bit block size
{
if (mReseedCounter > BLOCK64_RESEED_MAX)
@@ -76,24 +71,24 @@ namespace Org.BouncyCastle.Crypto.Prng
for (int i = 0; i < m; i++)
{
- mEngine.ProcessBlock(mDT, 0, mI, 0);
+ mEngine.ProcessBlock(mDT, mI);
Process(mR, mI, mV);
Process(mV, mR, mI);
- Array.Copy(mR, 0, output, outputOff + i * mR.Length, mR.Length);
+ mR.CopyTo(output[(i * mR.Length)..]);
Increment(mDT);
}
- int bytesToCopy = (outputLen - m * mR.Length);
+ int bytesToCopy = outputLen - m * mR.Length;
if (bytesToCopy > 0)
{
- mEngine.ProcessBlock(mDT, 0, mI, 0);
+ mEngine.ProcessBlock(mDT, mI);
Process(mR, mI, mV);
Process(mV, mR, mI);
- Array.Copy(mR, 0, output, outputOff + m * mR.Length, bytesToCopy);
+ mR.AsSpan(0, bytesToCopy).CopyTo(output[(m * mR.Length)..]);
Increment(mDT);
}
@@ -102,12 +97,17 @@ namespace Org.BouncyCastle.Crypto.Prng
return outputLen * 8;
}
-
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
- internal int Generate(Span<byte> output, bool predictionResistant)
+#else
+ /**
+ * 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, int outputOff, int outputLen, bool predictionResistant)
{
- int outputLen = output.Length;
-
if (mR.Length == 8) // 64 bit block size
{
if (mReseedCounter > BLOCK64_RESEED_MAX)
@@ -140,12 +140,12 @@ namespace Org.BouncyCastle.Crypto.Prng
Process(mR, mI, mV);
Process(mV, mR, mI);
- mR.CopyTo(output[(i * mR.Length)..]);
+ Array.Copy(mR, 0, output, outputOff + i * mR.Length, mR.Length);
Increment(mDT);
}
- int bytesToCopy = (outputLen - m * mR.Length);
+ int bytesToCopy = outputLen - m * mR.Length;
if (bytesToCopy > 0)
{
@@ -153,7 +153,7 @@ namespace Org.BouncyCastle.Crypto.Prng
Process(mR, mI, mV);
Process(mV, mR, mI);
- mR.AsSpan(0, bytesToCopy).CopyTo(output[(m * mR.Length)..]);
+ Array.Copy(mR, 0, output, outputOff + m * mR.Length, bytesToCopy);
Increment(mDT);
}
diff --git a/crypto/src/crypto/prng/X931SecureRandom.cs b/crypto/src/crypto/prng/X931SecureRandom.cs
index 2ceff238f..d40134851 100644
--- a/crypto/src/crypto/prng/X931SecureRandom.cs
+++ b/crypto/src/crypto/prng/X931SecureRandom.cs
@@ -62,6 +62,9 @@ namespace Org.BouncyCastle.Crypto.Prng
public override void NextBytes(byte[] buf, int off, int len)
{
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ NextBytes(buf.AsSpan(off, len));
+#else
lock (this)
{
// check if a reseed is required...
@@ -71,6 +74,7 @@ namespace Org.BouncyCastle.Crypto.Prng
mDrbg.Generate(buf, off, len, mPredictionResistant);
}
}
+#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
diff --git a/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs b/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs
index ddb503aa0..cf566ff9c 100644
--- a/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs
+++ b/crypto/src/crypto/prng/drbg/CtrSP800Drbg.cs
@@ -1,6 +1,7 @@
using System;
using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Encoders;
@@ -9,7 +10,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
/**
* A SP800-90A CTR DRBG.
*/
- public class CtrSP800Drbg
+ public sealed class CtrSP800Drbg
: ISP80090Drbg
{
private static readonly long TDEA_RESEED_MAX = 1L << (32 - 1);
@@ -59,20 +60,19 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
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(nonce, personalizationString);
}
- private void CTR_DRBG_Instantiate_algorithm(byte[] entropy, byte[] nonce, byte[] personalisationString)
+ private void CTR_DRBG_Instantiate_algorithm(byte[] nonce, byte[] personalisationString)
{
- byte[] seedMaterial = Arrays.ConcatenateAll(entropy, nonce, personalisationString);
- byte[] seed = Block_Cipher_df(seedMaterial, mSeedLength);
+ byte[] entropy = GetEntropy(); // Get_entropy_input
+ byte[] seedMaterial = Arrays.ConcatenateAll(entropy, nonce, personalisationString);
+ byte[] seed = BlockCipherDF(seedMaterial, mSeedLength / 8);
- int outlen = mEngine.GetBlockSize();
+ int blockSize = mEngine.GetBlockSize();
mKey = new byte[(mKeySizeInBits + 7) / 8];
- mV = new byte[outlen];
+ mV = new byte[blockSize];
// mKey & mV are modified by this call
CTR_DRBG_Update(seed, mKey, mV);
@@ -80,55 +80,130 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
mReseedCounter = 1;
}
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private void CTR_DRBG_Update(ReadOnlySpan<byte> seed, Span<byte> key, Span<byte> v)
+ {
+ int seedLength = seed.Length;
+ Span<byte> temp = seedLength <= 256
+ ? stackalloc byte[seedLength]
+ : new byte[seedLength];
+
+ int blockSize = mEngine.GetBlockSize();
+ Span<byte> block = blockSize <= 64
+ ? stackalloc byte[blockSize]
+ : new byte[blockSize];
+
+ mEngine.Init(true, ExpandToKeyParameter(key));
+ for (int i = 0; i * blockSize < seed.Length; ++i)
+ {
+ AddOneTo(v);
+ mEngine.ProcessBlock(v, block);
+
+ int bytesToCopy = System.Math.Min(blockSize, temp.Length - i * blockSize);
+ block[..bytesToCopy].CopyTo(temp[(i * blockSize)..]);
+ }
+
+ XorWith(seed, temp);
+
+ key.CopyFrom(temp);
+ v.CopyFrom(temp[key.Length..]);
+ }
+#else
private void CTR_DRBG_Update(byte[] seed, byte[] key, byte[] v)
{
- byte[] temp = new byte[seed.Length];
+ byte[] temp = new byte[seed.Length];
byte[] outputBlock = new byte[mEngine.GetBlockSize()];
int i = 0;
int outLen = mEngine.GetBlockSize();
- mEngine.Init(true, new KeyParameter(ExpandKey(key)));
- while (i*outLen < seed.Length)
+ mEngine.Init(true, ExpandToKeyParameter(key));
+ while (i * outLen < seed.Length)
{
AddOneTo(v);
mEngine.ProcessBlock(v, 0, outputBlock, 0);
- int bytesToCopy = ((temp.Length - i * outLen) > outLen)
- ? outLen : (temp.Length - i * outLen);
-
+ int bytesToCopy = System.Math.Min(outLen, temp.Length - i * outLen);
Array.Copy(outputBlock, 0, temp, i * outLen, bytesToCopy);
++i;
}
- XOR(temp, seed, temp, 0);
+ Xor(temp, seed, temp, 0);
Array.Copy(temp, 0, key, 0, key.Length);
Array.Copy(temp, key.Length, v, 0, v.Length);
- }
-
- private void CTR_DRBG_Reseed_algorithm(byte[] additionalInput)
- {
- byte[] seedMaterial = Arrays.Concatenate(GetEntropy(), additionalInput);
+ }
+#endif
- seedMaterial = Block_Cipher_df(seedMaterial, mSeedLength);
+ private void CTR_DRBG_Reseed_algorithm(byte[] additionalInput)
+ {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ CTR_DRBG_Reseed_algorithm(Spans.FromNullableReadOnly(additionalInput));
+#else
+ byte[] seedMaterial = Arrays.Concatenate(GetEntropy(), additionalInput);
+
+ seedMaterial = BlockCipherDF(seedMaterial, mSeedLength / 8);
CTR_DRBG_Update(seedMaterial, mKey, mV);
mReseedCounter = 1;
- }
+#endif
+ }
- private void XOR(byte[] output, byte[] a, byte[] b, int bOff)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private void CTR_DRBG_Reseed_algorithm(ReadOnlySpan<byte> additionalInput)
+ {
+ int entropyLength = GetEntropyLength();
+ int seedLength = entropyLength + additionalInput.Length;
+
+ Span<byte> seedMaterial = seedLength <= 256
+ ? stackalloc byte[seedLength]
+ : new byte[seedLength];
+
+ GetEntropy(seedMaterial[..entropyLength]);
+ additionalInput.CopyTo(seedMaterial[entropyLength..]);
+
+ seedMaterial = BlockCipherDF(seedMaterial, mSeedLength / 8);
+
+ CTR_DRBG_Update(seedMaterial, mKey, mV);
+
+ mReseedCounter = 1;
+ }
+#endif
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private void Xor(ReadOnlySpan<byte> x, ReadOnlySpan<byte> y, Span<byte> z)
+ {
+ for (int i = 0; i < z.Length; ++i)
+ {
+ z[i] = (byte)(x[i] ^ y[i]);
+ }
+ }
+
+ private void XorWith(ReadOnlySpan<byte> x, Span<byte> z)
+ {
+ for (int i = 0; i < z.Length; ++i)
+ {
+ z[i] ^= x[i];
+ }
+ }
+#else
+ private void Xor(byte[] output, byte[] a, byte[] b, int bOff)
{
for (int i = 0; i < output.Length; i++)
{
output[i] = (byte)(a[i] ^ b[bOff + i]);
}
}
+#endif
- private void AddOneTo(byte[] longer)
- {
- uint carry = 1;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private void AddOneTo(Span<byte> longer)
+#else
+ private void AddOneTo(byte[] longer)
+#endif
+ {
+ uint carry = 1;
int i = longer.Length;
while (--i >= 0)
{
@@ -146,83 +221,101 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
return entropy;
}
- // -- Internal state migration ---
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private int GetEntropy(Span<byte> output)
+ {
+ int length = mEntropySource.GetEntropy(output);
+ if (length < (mSecurityStrength + 7) / 8)
+ throw new InvalidOperationException("Insufficient entropy provided by entropy source");
+ return length;
+ }
- private static readonly byte[] K_BITS = Hex.DecodeStrict("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+ private int GetEntropyLength()
+ {
+ return (mEntropySource.EntropySize + 7) / 8;
+ }
+#endif
+
+ // -- Internal state migration ---
+
+ private static readonly byte[] K_BITS = Hex.DecodeStrict(
+ "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 = mEngine.GetBlockSize();
- int L = inputString.Length; // already in bytes
- int N = bitLength / 8;
- // 4 S = L || N || inputstring || 0x80
+ // 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[] BlockCipherDF(byte[] input, int N)
+ {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ return BlockCipherDF(input.AsSpan(), N);
+#else
+ int outLen = mEngine.GetBlockSize();
+ int L = input.Length; // already in bytes
+ // 4 S = L || N || input || 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);
- Array.Copy(inputString, 0, S, 8, L);
- S[8 + L] = (byte)0x80;
+ Pack.UInt32_To_BE((uint)L, S, 0);
+ Pack.UInt32_To_BE((uint)N, S, 4);
+ Array.Copy(input, 0, S, 8, L);
+ S[8 + L] = 0x80;
// S already padded with zeros
byte[] temp = new byte[mKeySizeInBits / 8 + outLen];
@@ -233,16 +326,15 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
int i = 0;
byte[] K = new byte[mKeySizeInBits / 8];
Array.Copy(K_BITS, 0, K, 0, K.Length);
+ var K1 = ExpandToKeyParameter(K);
+ mEngine.Init(true, K1);
while (i*outLen*8 < mKeySizeInBits + outLen *8)
{
- copyIntToByteArray(IV, i, 0);
- BCC(bccOut, K, IV, S);
+ Pack.UInt32_To_BE((uint)i, IV, 0);
+ BCC(bccOut, IV, S);
- int bytesToCopy = ((temp.Length - i * outLen) > outLen)
- ? outLen
- : (temp.Length - i * outLen);
-
+ int bytesToCopy = System.Math.Min(outLen, temp.Length - i * outLen);
Array.Copy(bccOut, 0, temp, i * outLen, bytesToCopy);
++i;
}
@@ -251,39 +343,120 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
Array.Copy(temp, 0, K, 0, K.Length);
Array.Copy(temp, K.Length, X, 0, X.Length);
- temp = new byte[bitLength / 2];
+ temp = new byte[N];
i = 0;
- mEngine.Init(true, new KeyParameter(ExpandKey(K)));
+ mEngine.Init(true, ExpandToKeyParameter(K));
while (i * outLen < temp.Length)
{
mEngine.ProcessBlock(X, 0, X, 0);
- int bytesToCopy = ((temp.Length - i * outLen) > outLen)
- ? outLen
- : (temp.Length - i * outLen);
-
+ int bytesToCopy = System.Math.Min(outLen, temp.Length - i * outLen);
Array.Copy(X, 0, temp, i * outLen, bytesToCopy);
i++;
}
return temp;
- }
+#endif
+ }
- /*
- * 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)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private byte[] BlockCipherDF(ReadOnlySpan<byte> input, int N)
+ {
+ int blockSize = mEngine.GetBlockSize();
+ int L = input.Length; // already in bytes
+ // 4 S = L || N || input || 0x80
+ int sLen = 4 + 4 + L + 1;
+ int blockLen = ((sLen + blockSize - 1) / blockSize) * blockSize;
+ Span<byte> S = blockLen <= 256
+ ? stackalloc byte[blockLen]
+ : new byte[blockLen];
+ Pack.UInt32_To_BE((uint)L, S);
+ Pack.UInt32_To_BE((uint)N, S[4..]);
+ input.CopyTo(S[8..]);
+ S[8 + L] = 0x80;
+ // S already padded with zeros
+
+ int keySize = mKeySizeInBits / 8;
+ int tempSize = keySize + blockSize;
+ Span<byte> temp = tempSize <= 128
+ ? stackalloc byte[tempSize]
+ : new byte[tempSize];
+
+ Span<byte> bccOut = blockSize <= 64
+ ? stackalloc byte[blockSize]
+ : new byte[blockSize];
+
+ Span<byte> IV = blockSize <= 64
+ ? stackalloc byte[blockSize]
+ : new byte[blockSize];
+
+ var K1 = ExpandToKeyParameter(K_BITS.AsSpan(0, keySize));
+ mEngine.Init(true, K1);
+
+ for (int i = 0; i * blockSize < tempSize; ++i)
+ {
+ Pack.UInt32_To_BE((uint)i, IV);
+ BCC(bccOut, IV, S);
+
+ int bytesToCopy = System.Math.Min(blockSize, tempSize - i * blockSize);
+ bccOut[..bytesToCopy].CopyTo(temp[(i * blockSize)..]);
+ }
+
+ var K2 = ExpandToKeyParameter(temp[..keySize]);
+ mEngine.Init(true, K2);
+ var X = temp[keySize..];
+
+ byte[] result = new byte[N];
+ for (int i = 0; i * blockSize < result.Length; ++i)
+ {
+ mEngine.ProcessBlock(X, X);
+
+ int bytesToCopy = System.Math.Min(blockSize, result.Length - i * blockSize);
+ X[..bytesToCopy].CopyTo(result.AsSpan(i * blockSize));
+ }
+ return result;
+ }
+#endif
+
+ /*
+ * 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.
+ */
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private void BCC(Span<byte> bccOut, ReadOnlySpan<byte> iV, ReadOnlySpan<byte> data)
+ {
+ int blockSize = mEngine.GetBlockSize();
+
+ Span<byte> chainingValue = blockSize <= 64
+ ? stackalloc byte[blockSize]
+ : new byte[blockSize];
+ Span<byte> inputBlock = blockSize <= 64
+ ? stackalloc byte[blockSize]
+ : new byte[blockSize];
+
+ mEngine.ProcessBlock(iV, chainingValue);
+
+ int n = data.Length / blockSize;
+ for (int i = 0; i < n; i++)
+ {
+ Xor(chainingValue, data[(i * blockSize)..], inputBlock);
+ mEngine.ProcessBlock(inputBlock, chainingValue);
+ }
+
+ bccOut.CopyFrom(chainingValue);
+ }
+#else
+ private void BCC(byte[] bccOut, byte[] iV, byte[] data)
{
int outlen = mEngine.GetBlockSize();
byte[] chainingValue = new byte[outlen]; // initial values = 0
@@ -291,33 +464,24 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
byte[] inputBlock = new byte[outlen];
- mEngine.Init(true, new KeyParameter(ExpandKey(k)));
-
mEngine.ProcessBlock(iV, 0, chainingValue, 0);
for (int i = 0; i < n; i++)
{
- XOR(inputBlock, chainingValue, data, i*outlen);
+ Xor(inputBlock, chainingValue, data, i*outlen);
mEngine.ProcessBlock(inputBlock, 0, chainingValue, 0);
}
Array.Copy(chainingValue, 0, bccOut, 0, bccOut.Length);
}
+#endif
- private void copyIntToByteArray(byte[] buf, int value, int offSet)
- {
- buf[offSet + 0] = ((byte)(value >> 24));
- buf[offSet + 1] = ((byte)(value >> 16));
- buf[offSet + 2] = ((byte)(value >> 8));
- buf[offSet + 3] = ((byte)(value));
- }
-
- /**
+ /**
* Return the block size (in bits) of the DRBG.
*
* @return the number of bits produced on each internal round of the DRBG.
*/
- public int BlockSize
+ public int BlockSize
{
get { return mV.Length * 8; }
}
@@ -335,7 +499,9 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
bool predictionResistant)
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
- return Generate(output.AsSpan(outputOff, outputLen), additionalInput, predictionResistant);
+ return additionalInput == null
+ ? Generate(output.AsSpan(outputOff, outputLen), predictionResistant)
+ : GenerateWithInput(output.AsSpan(outputOff, outputLen), additionalInput.AsSpan(), predictionResistant);
#else
if (mIsTdea)
{
@@ -362,7 +528,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
if (additionalInput != null)
{
- additionalInput = Block_Cipher_df(additionalInput, mSeedLength);
+ additionalInput = BlockCipherDF(additionalInput, mSeedLength / 8);
CTR_DRBG_Update(additionalInput, mKey, mV);
}
else
@@ -372,7 +538,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
byte[] tmp = new byte[mV.Length];
- mEngine.Init(true, new KeyParameter(ExpandKey(mKey)));
+ mEngine.Init(true, ExpandToKeyParameter(mKey));
for (int i = 0, limit = outputLen / tmp.Length; i <= limit; i++)
{
@@ -397,9 +563,9 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
- public int Generate(Span<byte> output, byte[] additionalInput, bool predictionResistant)
- {
- int outputLen = output.Length;
+ public int Generate(Span<byte> output, bool predictionResistant)
+ {
+ int outputLen = output.Length;
if (mIsTdea)
{
if (mReseedCounter > TDEA_RESEED_MAX)
@@ -419,24 +585,57 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
if (predictionResistant)
{
- CTR_DRBG_Reseed_algorithm(additionalInput);
- additionalInput = null;
+ CTR_DRBG_Reseed_algorithm(ReadOnlySpan<byte>.Empty);
}
- if (additionalInput != null)
+ byte[] seed = new byte[mSeedLength / 8];
+
+ return ImplGenerate(seed, output);
+ }
+
+ public int GenerateWithInput(Span<byte> output, ReadOnlySpan<byte> additionalInput, bool predictionResistant)
+ {
+ int outputLen = output.Length;
+ if (mIsTdea)
{
- additionalInput = Block_Cipher_df(additionalInput, mSeedLength);
- CTR_DRBG_Update(additionalInput, mKey, mV);
+ 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
{
- additionalInput = new byte[mSeedLength];
+ 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");
}
+ int seedLength = mSeedLength / 8;
+ byte[] seed;
+ if (predictionResistant)
+ {
+ CTR_DRBG_Reseed_algorithm(additionalInput);
+ seed = new byte[seedLength];
+ }
+ else
+ {
+ seed = BlockCipherDF(additionalInput, seedLength);
+ CTR_DRBG_Update(seed, mKey, mV);
+ }
+
+ return ImplGenerate(seed, output);
+ }
+
+ private int ImplGenerate(ReadOnlySpan<byte> seed, Span<byte> output)
+ {
byte[] tmp = new byte[mV.Length];
- mEngine.Init(true, new KeyParameter(ExpandKey(mKey)));
+ mEngine.Init(true, ExpandToKeyParameter(mKey));
+ int outputLen = output.Length;
for (int i = 0, limit = outputLen / tmp.Length; i <= limit; i++)
{
int bytesToCopy = System.Math.Min(tmp.Length, outputLen - i * tmp.Length);
@@ -447,11 +646,11 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
mEngine.ProcessBlock(mV, 0, tmp, 0);
- tmp[..bytesToCopy].CopyTo(output[(i * tmp.Length)..]);
+ tmp[..bytesToCopy].CopyTo(output[(i * tmp.Length)..]);
}
}
- CTR_DRBG_Update(additionalInput, mKey, mV);
+ CTR_DRBG_Update(seed, mKey, mV);
mReseedCounter++;
@@ -466,9 +665,20 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
*/
public void Reseed(byte[] additionalInput)
{
- CTR_DRBG_Reseed_algorithm(additionalInput);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ Reseed(Spans.FromNullableReadOnly(additionalInput));
+#else
+ CTR_DRBG_Reseed_algorithm(additionalInput);
+#endif
}
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public void Reseed(ReadOnlySpan<byte> additionalInput)
+ {
+ CTR_DRBG_Reseed_algorithm(additionalInput);
+ }
+#endif
+
private bool IsTdea(IBlockCipher cipher)
{
return cipher.AlgorithmName.Equals("DESede") || cipher.AlgorithmName.Equals("TDEA");
@@ -488,26 +698,39 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
return -1;
}
- private byte[] ExpandKey(byte[] key)
+ private KeyParameter ExpandToKeyParameter(byte[] key)
{
- if (mIsTdea)
- {
- // expand key to 192 bits.
- byte[] tmp = new byte[24];
+ if (!mIsTdea)
+ return new KeyParameter(key);
- PadKey(key, 0, tmp, 0);
- PadKey(key, 7, tmp, 8);
- PadKey(key, 14, tmp, 16);
+ // expand key to 192 bits.
+ byte[] tmp = new byte[24];
- return tmp;
- }
- else
- {
- return key;
- }
- }
+ PadKey(key, 0, tmp, 0);
+ PadKey(key, 7, tmp, 8);
+ PadKey(key, 14, tmp, 16);
- /**
+ return new KeyParameter(tmp);
+ }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private KeyParameter ExpandToKeyParameter(ReadOnlySpan<byte> key)
+ {
+ if (!mIsTdea)
+ return new KeyParameter(key);
+
+ // expand key to 192 bits.
+ Span<byte> tmp = stackalloc byte[24];
+
+ PadKey(key, tmp);
+ PadKey(key[7..], tmp[8..]);
+ PadKey(key[14..], tmp[16..]);
+
+ return new KeyParameter(tmp);
+ }
+#endif
+
+ /**
* Pad out a key for TDEA, setting odd parity for each byte.
*
* @param keyMaster
@@ -528,5 +751,21 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
DesParameters.SetOddParity(tmp, tmpOff, 8);
}
- }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private void PadKey(ReadOnlySpan<byte> keyMaster, Span<byte> tmp)
+ {
+ tmp[0] = (byte)(keyMaster[0] & 0xFE);
+ tmp[1] = (byte)((keyMaster[0] << 7) | ((keyMaster[1] & 0xfc) >> 1));
+ tmp[2] = (byte)((keyMaster[1] << 6) | ((keyMaster[2] & 0xf8) >> 2));
+ tmp[3] = (byte)((keyMaster[2] << 5) | ((keyMaster[3] & 0xf0) >> 3));
+ tmp[4] = (byte)((keyMaster[3] << 4) | ((keyMaster[4] & 0xe0) >> 4));
+ tmp[5] = (byte)((keyMaster[4] << 3) | ((keyMaster[5] & 0xc0) >> 5));
+ tmp[6] = (byte)((keyMaster[5] << 2) | ((keyMaster[6] & 0x80) >> 6));
+ tmp[7] = (byte)(keyMaster[6] << 1);
+
+ DesParameters.SetOddParity(tmp[..8]);
+ }
+#endif
+ }
}
diff --git a/crypto/src/crypto/prng/drbg/DrbgUtilities.cs b/crypto/src/crypto/prng/drbg/DrbgUtilities.cs
index 58baaf5d9..24037b3f1 100644
--- a/crypto/src/crypto/prng/drbg/DrbgUtilities.cs
+++ b/crypto/src/crypto/prng/drbg/DrbgUtilities.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
-using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Crypto.Utilities;
namespace Org.BouncyCastle.Crypto.Prng.Drbg
{
@@ -35,65 +35,77 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
return MaxSecurityStrengths[name.Substring(0, name.IndexOf("/"))];
}
- /**
+ /**
* Used by both Dual EC and Hash.
*/
- internal static byte[] HashDF(IDigest 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();
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ internal static void HashDF(IDigest digest, ReadOnlySpan<byte> seedMaterial, int seedLength, Span<byte> output)
+#else
+ internal static void HashDF(IDigest digest, byte[] seedMaterial, int seedLength, byte[] output)
+#endif
+ {
+ // 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.
+ int outputLength = (seedLength + 7) / 8;
+
+ int digestSize = digest.GetDigestSize();
+ int len = outputLength / digestSize;
int counter = 1;
- byte[] dig = new byte[digest.GetDigestSize()];
-
- for (int i = 0; i <= len; i++)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ Span<byte> dig = digestSize <= 128
+ ? stackalloc byte[digestSize]
+ : new byte[digestSize];
+ Span<byte> header = stackalloc byte[5];
+ Pack.UInt32_To_BE((uint)seedLength, header[1..]);
+#else
+ byte[] dig = new byte[digestSize];
+ byte[] header = new byte[5];
+ Pack.UInt32_To_BE((uint)seedLength, header, 1);
+#endif
+
+ for (int i = 0; i <= len; i++, counter++)
{
- digest.Update((byte)counter);
-
- digest.Update((byte)(seedLength >> 24));
- digest.Update((byte)(seedLength >> 16));
- digest.Update((byte)(seedLength >> 8));
- digest.Update((byte)seedLength);
-
- digest.BlockUpdate(seedMaterial, 0, seedMaterial.Length);
-
- digest.DoFinal(dig, 0);
-
- int bytesToCopy = ((temp.Length - i * dig.Length) > dig.Length)
- ? dig.Length
- : (temp.Length - i * dig.Length);
- Array.Copy(dig, 0, temp, i * dig.Length, bytesToCopy);
-
- counter++;
- }
-
- // do a left shift to get rid of excess bits.
- if (seedLength % 8 != 0)
+ header[0] = (byte)counter;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ digest.BlockUpdate(header);
+ digest.BlockUpdate(seedMaterial);
+ digest.DoFinal(dig);
+
+ int bytesToCopy = System.Math.Min(digestSize, outputLength - i * digestSize);
+ dig[..bytesToCopy].CopyTo(output[(i * digestSize)..]);
+#else
+ digest.BlockUpdate(header, 0, header.Length);
+ digest.BlockUpdate(seedMaterial, 0, seedMaterial.Length);
+ digest.DoFinal(dig, 0);
+
+ int bytesToCopy = System.Math.Min(digestSize, outputLength - i * digestSize);
+ Array.Copy(dig, 0, output, i * digestSize, bytesToCopy);
+#endif
+ }
+
+ // do a left shift to get rid of excess bits.
+ if (seedLength % 8 != 0)
{
int shift = 8 - (seedLength % 8);
uint carry = 0;
- for (int i = 0; i != temp.Length; i++)
+ for (int i = 0; i != outputLength; i++)
{
- uint b = temp[i];
- temp[i] = (byte)((b >> shift) | (carry << (8 - shift)));
+ uint b = output[i];
+ output[i] = (byte)((b >> shift) | (carry << (8 - shift)));
carry = b;
}
}
-
- return temp;
}
}
}
diff --git a/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs b/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs
index 5a684a89d..47e0191dd 100644
--- a/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs
+++ b/crypto/src/crypto/prng/drbg/HMacSP800Drbg.cs
@@ -8,7 +8,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
/**
* A SP800-90A HMAC DRBG.
*/
- public class HMacSP800Drbg
+ public sealed class HMacSP800Drbg
: ISP80090Drbg
{
private readonly static long RESEED_MAX = 1L << (48 - 1);
@@ -33,7 +33,8 @@ 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 HMacSP800Drbg(IMac hMac, int securityStrength, IEntropySource entropySource, byte[] personalizationString, byte[] nonce)
+ public HMacSP800Drbg(IMac hMac, int securityStrength, IEntropySource entropySource,
+ byte[] personalizationString, byte[] nonce)
{
if (securityStrength > DrbgUtilities.GetMaxSecurityStrength(hMac))
throw new ArgumentException("Requested security strength is not supported by the derivation function");
@@ -56,12 +57,41 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
mReseedCounter = 1;
}
- private void hmac_DRBG_Update(byte[] seedMaterial)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private void hmac_DRBG_Update()
+ {
+ hmac_DRBG_Update_Func(ReadOnlySpan<byte>.Empty, 0x00);
+ }
+
+ private void hmac_DRBG_Update(ReadOnlySpan<byte> seedMaterial)
+ {
+ hmac_DRBG_Update_Func(seedMaterial, 0x00);
+ hmac_DRBG_Update_Func(seedMaterial, 0x01);
+ }
+
+ private void hmac_DRBG_Update_Func(ReadOnlySpan<byte> seedMaterial, byte vValue)
+ {
+ mHMac.Init(new KeyParameter(mK));
+
+ mHMac.BlockUpdate(mV);
+ mHMac.Update(vValue);
+ if (!seedMaterial.IsEmpty)
+ {
+ mHMac.BlockUpdate(seedMaterial);
+ }
+ mHMac.DoFinal(mK);
+
+ mHMac.Init(new KeyParameter(mK));
+ mHMac.BlockUpdate(mV);
+ mHMac.DoFinal(mV);
+ }
+#else
+ private void hmac_DRBG_Update(byte[] seedMaterial)
{
- hmac_DRBG_Update_Func(seedMaterial, (byte)0x00);
+ hmac_DRBG_Update_Func(seedMaterial, 0x00);
if (seedMaterial != null)
{
- hmac_DRBG_Update_Func(seedMaterial, (byte)0x01);
+ hmac_DRBG_Update_Func(seedMaterial, 0x01);
}
}
@@ -84,13 +114,14 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
mHMac.DoFinal(mV, 0);
}
+#endif
- /**
+ /**
* Return the block size (in bits) of the DRBG.
*
* @return the number of bits produced on each round of the DRBG.
*/
- public int BlockSize
+ public int BlockSize
{
get { return mV.Length * 8; }
}
@@ -108,7 +139,9 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
bool predictionResistant)
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
- return Generate(output.AsSpan(outputOff, outputLen), additionalInput, predictionResistant);
+ return additionalInput == null
+ ? Generate(output.AsSpan(outputOff, outputLen), predictionResistant)
+ : GenerateWithInput(output.AsSpan(outputOff, outputLen), additionalInput.AsSpan(), predictionResistant);
#else
int numberOfBits = outputLen * 8;
@@ -164,10 +197,9 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
- public int Generate(Span<byte> output, byte[] additionalInput, bool predictionResistant)
+ public int Generate(Span<byte> output, bool predictionResistant)
{
- int outputLen = output.Length;
- int numberOfBits = outputLen * 8;
+ int numberOfBits = output.Length * 8;
if (numberOfBits > MAX_BITS_REQUEST)
throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output");
@@ -177,43 +209,79 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
if (predictionResistant)
{
- Reseed(additionalInput);
- additionalInput = null;
+ Reseed(ReadOnlySpan<byte>.Empty);
}
- // 2.
- if (additionalInput != null)
+ // 3.
+ ImplGenerate(output);
+
+ hmac_DRBG_Update();
+
+ mReseedCounter++;
+
+ return numberOfBits;
+ }
+
+ public int GenerateWithInput(Span<byte> output, ReadOnlySpan<byte> additionalInput, bool predictionResistant)
+ {
+ int numberOfBits = output.Length * 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);
+ }
+ else
+ {
+ // 2.
hmac_DRBG_Update(additionalInput);
}
// 3.
+ ImplGenerate(output);
+
+ if (predictionResistant)
+ {
+ hmac_DRBG_Update();
+ }
+ else
+ {
+ hmac_DRBG_Update(additionalInput);
+ }
+
+ mReseedCounter++;
+
+ return numberOfBits;
+ }
+
+ private void ImplGenerate(Span<byte> output)
+ {
+ int outputLen = output.Length;
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);
+ mHMac.BlockUpdate(mV);
+ mHMac.DoFinal(mV);
- mV.CopyTo(output[(i * mV.Length)..]);
+ mV.CopyTo(output[(i * mV.Length)..]);
}
- int remaining = outputLen - m * mV.Length;
+ int remaining = outputLen - m * mV.Length;
if (remaining > 0)
{
- mHMac.BlockUpdate(mV, 0, mV.Length);
- mHMac.DoFinal(mV, 0);
+ mHMac.BlockUpdate(mV);
+ mHMac.DoFinal(mV);
- mV[..remaining].CopyTo(output[(m * mV.Length)..]);
+ mV[..remaining].CopyTo(output[(m * mV.Length)..]);
}
-
- hmac_DRBG_Update(additionalInput);
-
- mReseedCounter++;
-
- return numberOfBits;
}
#endif
@@ -224,14 +292,35 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
*/
public void Reseed(byte[] additionalInput)
{
- byte[] entropy = GetEntropy();
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ Reseed(Spans.FromNullableReadOnly(additionalInput));
+#else
+ byte[] entropy = GetEntropy();
byte[] seedMaterial = Arrays.Concatenate(entropy, additionalInput);
hmac_DRBG_Update(seedMaterial);
mReseedCounter = 1;
+#endif
}
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public void Reseed(ReadOnlySpan<byte> additionalInput)
+ {
+ int entropyLength = GetEntropyLength();
+ int seedMaterialLength = entropyLength + additionalInput.Length;
+ Span<byte> seedMaterial = seedMaterialLength <= 256
+ ? stackalloc byte[seedMaterialLength]
+ : new byte[seedMaterialLength];
+ GetEntropy(seedMaterial);
+ additionalInput.CopyTo(seedMaterial[entropyLength..]);
+
+ hmac_DRBG_Update(seedMaterial);
+
+ mReseedCounter = 1;
+ }
+#endif
+
private byte[] GetEntropy()
{
byte[] entropy = mEntropySource.GetEntropy();
@@ -239,5 +328,20 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
throw new InvalidOperationException("Insufficient entropy provided by entropy source");
return entropy;
}
- }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private int GetEntropy(Span<byte> output)
+ {
+ int length = mEntropySource.GetEntropy(output);
+ if (length < (mSecurityStrength + 7) / 8)
+ throw new InvalidOperationException("Insufficient entropy provided by entropy source");
+ return length;
+ }
+
+ private int GetEntropyLength()
+ {
+ return (mEntropySource.EntropySize + 7) / 8;
+ }
+#endif
+ }
}
diff --git a/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs b/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs
index 2f73b1f4e..3f9cffbcd 100644
--- a/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs
+++ b/crypto/src/crypto/prng/drbg/HashSP800Drbg.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
+using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Prng.Drbg
@@ -8,7 +9,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
/**
* A SP800-90A Hash DRBG.
*/
- public class HashSP800Drbg
+ public sealed class HashSP800Drbg
: ISP80090Drbg
{
private readonly static byte[] ONE = { 0x01 };
@@ -72,12 +73,13 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
byte[] entropy = GetEntropy();
byte[] seedMaterial = Arrays.ConcatenateAll(entropy, nonce, personalizationString);
- byte[] seed = DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength);
+ mV = new byte[(mSeedLength + 7) / 8];
+ DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength, mV);
- mV = seed;
byte[] subV = new byte[mV.Length + 1];
Array.Copy(mV, 0, subV, 1, mV.Length);
- mC = DrbgUtilities.HashDF(mDigest, subV, mSeedLength);
+ mC = new byte[(mSeedLength + 7) / 8];
+ DrbgUtilities.HashDF(mDigest, subV, mSeedLength, mC);
mReseedCounter = 1;
}
@@ -89,7 +91,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
*/
public int BlockSize
{
- get { return mDigest.GetDigestSize () * 8; }
+ get { return mDigest.GetDigestSize() * 8; }
}
/**
@@ -105,7 +107,9 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
bool predictionResistant)
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
- return Generate(output.AsSpan(outputOff, outputLen), additionalInput, predictionResistant);
+ return additionalInput == null
+ ? Generate(output.AsSpan(outputOff, outputLen), predictionResistant)
+ : GenerateWithInput(output.AsSpan(outputOff, outputLen), additionalInput.AsSpan(), predictionResistant);
#else
// 1. If reseed_counter > reseed_interval, then return an indication that a
// reseed is required.
@@ -147,7 +151,7 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
}
// 3.
- byte[] rv = hashgen(mV, numberOfBits);
+ byte[] rv = Hashgen(mV, outputLen);
// 4.
byte[] subH = new byte[mV.Length + 1];
@@ -159,11 +163,9 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
// 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;
+ Pack.UInt32_To_BE((uint)mReseedCounter, c);
AddTo(mV, c);
@@ -176,7 +178,38 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
- public int Generate(Span<byte> output, byte[] additionalInput, bool predictionResistant)
+ public int Generate(Span<byte> output, 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 = output.Length * 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(ReadOnlySpan<byte>.Empty);
+ }
+
+ return ImplGenerate(output);
+ }
+
+ public int GenerateWithInput(Span<byte> output, ReadOnlySpan<byte> additionalInput, bool predictionResistant)
{
// 1. If reseed_counter > reseed_interval, then return an indication that a
// reseed is required.
@@ -191,8 +224,7 @@ 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 outputLen = output.Length;
- int numberOfBits = outputLen * 8;
+ int numberOfBits = output.Length * 8;
if (numberOfBits > MAX_BITS_REQUEST)
throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST, "output");
@@ -203,47 +235,53 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
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);
+ else
+ {
+ // 2.
+ mDigest.Update(0x02);
+ mDigest.BlockUpdate(mV);
+ mDigest.BlockUpdate(additionalInput);
+
+ int digestSize = mDigest.GetDigestSize();
+ Span<byte> w = digestSize <= 128
+ ? stackalloc byte[digestSize]
+ : new byte[digestSize];
+ mDigest.DoFinal(w);
AddTo(mV, w);
}
+ return ImplGenerate(output);
+ }
+
+ private int ImplGenerate(Span<byte> output)
+ {
// 3.
- byte[] rv = hashgen(mV, numberOfBits);
+ Hashgen(mV, output);
// 4.
- byte[] subH = new byte[mV.Length + 1];
- Array.Copy(mV, 0, subH, 1, mV.Length);
- subH[0] = 0x03;
+ mDigest.Update(0x03);
+ mDigest.BlockUpdate(mV);
- byte[] H = Hash(subH);
+ int digestSize = mDigest.GetDigestSize();
+ Span<byte> H = digestSize <= 128
+ ? stackalloc byte[digestSize]
+ : new byte[digestSize];
+ mDigest.DoFinal(H);
// 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;
+
+ Span<byte> c = stackalloc byte[4];
+ Pack.UInt32_To_BE((uint)mReseedCounter, c);
AddTo(mV, c);
mReseedCounter++;
- rv.CopyTo(output);
-
- return numberOfBits;
+ return output.Length * 8;
}
#endif
@@ -255,18 +293,37 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
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)
- {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private int GetEntropy(Span<byte> output)
+ {
+ int length = mEntropySource.GetEntropy(output);
+ if (length < (mSecurityStrength + 7) / 8)
+ throw new InvalidOperationException("Insufficient entropy provided by entropy source");
+ return length;
+ }
+
+ private int GetEntropyLength()
+ {
+ return (mEntropySource.EntropySize + 7) / 8;
+ }
+#endif
+
+ // this will always add the shorter length byte array mathematically to the
+ // longer length byte array.
+ // be careful....
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private void AddTo(Span<byte> longer, ReadOnlySpan<byte> shorter)
+#else
+ private void AddTo(byte[] longer, byte[] shorter)
+#endif
+ {
int off = longer.Length - shorter.Length;
uint carry = 0;
int i = shorter.Length;
while (--i >= 0)
{
- carry += (uint)longer[off + i] + (uint)shorter[i];
+ carry += (uint)longer[off + i] + shorter[i];
longer[off + i] = (byte)carry;
carry >>= 8;
}
@@ -287,78 +344,155 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
*/
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 = GetEntropy();
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ Reseed(Spans.FromNullableReadOnly(additionalInput));
+#else
+ // 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 = GetEntropy();
byte[] seedMaterial = Arrays.ConcatenateAll(ONE, mV, entropy, additionalInput);
- byte[] seed = DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength);
+ DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength, mV);
- mV = seed;
byte[] subV = new byte[mV.Length + 1];
subV[0] = 0x00;
Array.Copy(mV, 0, subV, 1, mV.Length);
- mC = DrbgUtilities.HashDF(mDigest, subV, mSeedLength);
+ DrbgUtilities.HashDF(mDigest, subV, mSeedLength, mC);
mReseedCounter = 1;
+#endif
}
- private byte[] Hash(byte[] input)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public void Reseed(ReadOnlySpan<byte> additionalInput)
{
- byte[] hash = new byte[mDigest.GetDigestSize()];
- DoHash(input, hash);
- return hash;
+ // 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.
+ int entropyLength = GetEntropyLength();
+
+ int seedMaterialLength = 1 + mV.Length + entropyLength + additionalInput.Length;
+ Span<byte> seedMaterial = seedMaterialLength <= 256
+ ? stackalloc byte[seedMaterialLength]
+ : new byte[seedMaterialLength];
+
+ seedMaterial[0] = 0x01;
+ mV.CopyTo(seedMaterial[1..]);
+ GetEntropy(seedMaterial[(1 + mV.Length)..]);
+ additionalInput.CopyTo(seedMaterial[(1 + mV.Length + entropyLength)..]);
+
+ DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength, mV);
+
+ int subVLength = 1 + mV.Length;
+ Span<byte> subV = subVLength <= 128
+ ? stackalloc byte[subVLength]
+ : new byte[subVLength];
+ subV[0] = 0x00;
+ mV.CopyTo(subV[1..]);
+
+ DrbgUtilities.HashDF(mDigest, subV, mSeedLength, mC);
+
+ mReseedCounter = 1;
}
+#endif
- private void DoHash(byte[] input, byte[] output)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private void DoHash(ReadOnlySpan<byte> input, Span<byte> output)
+ {
+ mDigest.BlockUpdate(input);
+ mDigest.DoFinal(output);
+ }
+#else
+ private void DoHash(byte[] input, byte[] output)
{
mDigest.BlockUpdate(input, 0, input.Length);
mDigest.DoFinal(output, 0);
}
+ private byte[] Hash(byte[] input)
+ {
+ byte[] hash = new byte[mDigest.GetDigestSize()];
+ DoHash(input, hash);
+ return hash;
+ }
+#endif
+
// 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)
+ // 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.
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ private void Hashgen(ReadOnlySpan<byte> input, Span<byte> output)
{
int digestSize = mDigest.GetDigestSize();
- int m = (lengthInBits / 8) / digestSize;
+ int m = output.Length / digestSize;
- byte[] data = new byte[input.Length];
- Array.Copy(input, 0, data, 0, input.Length);
+ int dataSize = input.Length;
+ Span<byte> data = dataSize <= 256
+ ? stackalloc byte[input.Length]
+ : new byte[input.Length];
+ input.CopyTo(data);
- byte[] W = new byte[lengthInBits / 8];
+ Span<byte> dig = digestSize <= 128
+ ? stackalloc byte[digestSize]
+ : new byte[digestSize];
- byte[] dig = new byte[mDigest.GetDigestSize()];
for (int i = 0; i <= m; i++)
{
DoHash(data, dig);
- int bytesToCopy = ((W.Length - i * dig.Length) > dig.Length)
- ? dig.Length
- : (W.Length - i * dig.Length);
- Array.Copy(dig, 0, W, i * dig.Length, bytesToCopy);
+ int bytesToCopy = System.Math.Min(digestSize, output.Length - i * digestSize);
+ dig[..bytesToCopy].CopyTo(output[(i * digestSize)..]);
+ AddTo(data, ONE);
+ }
+ }
+#else
+ private byte[] Hashgen(byte[] input, int length)
+ {
+ int digestSize = mDigest.GetDigestSize();
+ int m = length / digestSize;
+
+ byte[] data = (byte[])input.Clone();
+ byte[] W = new byte[length];
+
+ byte[] dig = new byte[digestSize];
+ for (int i = 0; i <= m; i++)
+ {
+ DoHash(data, dig);
+
+ int bytesToCopy = System.Math.Min(digestSize, length - i * digestSize);
+ Array.Copy(dig, 0, W, i * digestSize, bytesToCopy);
AddTo(data, ONE);
}
return W;
- }
- }
+ }
+#endif
+ }
}
diff --git a/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs b/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs
index e067f20a3..ab7252a8d 100644
--- a/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs
+++ b/crypto/src/crypto/prng/drbg/ISP80090Drbg.cs
@@ -26,7 +26,9 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
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);
+ int Generate(Span<byte> output, bool predictionResistant);
+
+ int GenerateWithInput(Span<byte> output, ReadOnlySpan<byte> additionalInput, bool predictionResistant);
#endif
/**
@@ -35,5 +37,9 @@ namespace Org.BouncyCastle.Crypto.Prng.Drbg
* @param additionalInput additional input to be added to the DRBG in this step.
*/
void Reseed(byte[] additionalInput);
- }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ void Reseed(ReadOnlySpan<byte> additionalInput);
+#endif
+ }
}
diff --git a/crypto/test/src/crypto/prng/test/TestEntropySourceProvider.cs b/crypto/test/src/crypto/prng/test/TestEntropySourceProvider.cs
index 647799630..9ebbb7ccd 100644
--- a/crypto/test/src/crypto/prng/test/TestEntropySourceProvider.cs
+++ b/crypto/test/src/crypto/prng/test/TestEntropySourceProvider.cs
@@ -48,6 +48,18 @@ namespace Org.BouncyCastle.Crypto.Prng.Test
return rv;
}
+ // NOTE: .NET Core 2.1 has Span<T>, but is tested against our .NET Standard 2.0 assembly.
+//#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ int IEntropySource.GetEntropy(Span<byte> output)
+ {
+ int length = bitsRequired / 8;
+ data.AsSpan(index, length).CopyTo(output);
+ index += length;
+ return length;
+ }
+#endif
+
public int EntropySize
{
get { return bitsRequired; }
|