#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Prng.Drbg
{
/**
* A SP800-90A HMAC DRBG.
*/
public sealed class HMacSP800Drbg
: ISP80090Drbg
{
private readonly static long RESEED_MAX = 1L << (48 - 1);
private readonly static int MAX_BITS_REQUEST = 1 << (19 - 1);
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.
*
* Minimum entropy requirement is the security strength requested.
*
* @param hMac Hash MAC to base the DRBG on.
* @param securityStrength security strength required (in bits)
* @param entropySource source of entropy to use for seeding/reseeding.
* @param personalizationString personalization string to distinguish this DRBG (may be null).
* @param nonce nonce to further distinguish this DRBG (may be null).
*/
public HMacSP800Drbg(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");
if (entropySource.EntropySize < securityStrength)
throw new ArgumentException("Not enough entropy for security strength required");
mHMac = hMac;
mSecurityStrength = securityStrength;
mEntropySource = entropySource;
byte[] entropy = GetEntropy();
byte[] seedMaterial = Arrays.ConcatenateAll(entropy, nonce, personalizationString);
mK = new byte[hMac.GetMacSize()];
mV = new byte[mK.Length];
Arrays.Fill(mV, (byte)1);
hmac_DRBG_Update(seedMaterial);
mReseedCounter = 1;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
private void hmac_DRBG_Update()
{
hmac_DRBG_Update_Func(ReadOnlySpan.Empty, 0x00);
}
private void hmac_DRBG_Update(ReadOnlySpan seedMaterial)
{
hmac_DRBG_Update_Func(seedMaterial, 0x00);
hmac_DRBG_Update_Func(seedMaterial, 0x01);
}
private void hmac_DRBG_Update_Func(ReadOnlySpan 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, 0x00);
if (seedMaterial != null)
{
hmac_DRBG_Update_Func(seedMaterial, 0x01);
}
}
private void hmac_DRBG_Update_Func(byte[] seedMaterial, byte vValue)
{
mHMac.Init(new KeyParameter(mK));
mHMac.BlockUpdate(mV, 0, mV.Length);
mHMac.Update(vValue);
if (seedMaterial != null)
{
mHMac.BlockUpdate(seedMaterial, 0, seedMaterial.Length);
}
mHMac.DoFinal(mK, 0);
mHMac.Init(new KeyParameter(mK));
mHMac.BlockUpdate(mV, 0, mV.Length);
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
{
get { return mV.Length * 8; }
}
/**
* Populate a passed in array with random data.
*
* @param output output array for generated bits.
* @param additionalInput additional input to be added to the DRBG in this step.
* @param predictionResistant true if a reseed should be forced, false otherwise.
*
* @return number of bits generated, -1 if a reseed required.
*/
public int Generate(byte[] output, int outputOff, int outputLen, byte[] additionalInput,
bool predictionResistant)
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
return additionalInput == null
? Generate(output.AsSpan(outputOff, outputLen), predictionResistant)
: GenerateWithInput(output.AsSpan(outputOff, outputLen), additionalInput.AsSpan(), 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)
{
Reseed(additionalInput);
additionalInput = null;
}
// 2.
if (additionalInput != null)
{
hmac_DRBG_Update(additionalInput);
}
// 3.
byte[] rv = new byte[outputLen];
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);
Array.Copy(mV, 0, rv, i * mV.Length, mV.Length);
}
if (m * mV.Length < rv.Length)
{
mHMac.BlockUpdate(mV, 0, mV.Length);
mHMac.DoFinal(mV, 0);
Array.Copy(mV, 0, rv, m * mV.Length, rv.Length - (m * mV.Length));
}
hmac_DRBG_Update(additionalInput);
mReseedCounter++;
Array.Copy(rv, 0, output, outputOff, outputLen);
return numberOfBits;
#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
public int Generate(Span output, 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(ReadOnlySpan.Empty);
}
// 3.
ImplGenerate(output);
hmac_DRBG_Update();
mReseedCounter++;
return numberOfBits;
}
public int GenerateWithInput(Span output, ReadOnlySpan 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 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);
mHMac.DoFinal(mV);
mV.CopyTo(output[(i * mV.Length)..]);
}
int remaining = outputLen - m * mV.Length;
if (remaining > 0)
{
mHMac.BlockUpdate(mV);
mHMac.DoFinal(mV);
mV[..remaining].CopyTo(output[(m * mV.Length)..]);
}
}
#endif
/**
* Reseed the DRBG.
*
* @param additionalInput additional input to be added to the DRBG in this step.
*/
public void Reseed(byte[] additionalInput)
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
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 || UNITY_2021_2_OR_NEWER
public void Reseed(ReadOnlySpan additionalInput)
{
int entropyLength = GetEntropyLength();
int seedMaterialLength = entropyLength + additionalInput.Length;
Span 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();
if (entropy.Length < (mSecurityStrength + 7) / 8)
throw new InvalidOperationException("Insufficient entropy provided by entropy source");
return entropy;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
private int GetEntropy(Span 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
}
}
#pragma warning restore
#endif