HMacSP800Drbg
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Utilities;
using System;
namespace Org.BouncyCastle.Crypto.Prng.Drbg
{
public sealed class HMacSP800Drbg : ISP80090Drbg
{
private static readonly long RESEED_MAX = 140737488355328;
private static readonly int MAX_BITS_REQUEST = 262144;
private readonly byte[] mK;
private readonly byte[] mV;
private readonly IEntropySource mEntropySource;
private readonly IMac mHMac;
private readonly int mSecurityStrength;
private long mReseedCounter;
public int BlockSize => mV.Length * 8;
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[] array = Arrays.ConcatenateAll(entropy, nonce, personalizationString);
mK = new byte[hMac.GetMacSize()];
mV = new byte[mK.Length];
Arrays.Fill(mV, 1);
hmac_DRBG_Update(array);
mReseedCounter = 1;
}
private void hmac_DRBG_Update()
{
hmac_DRBG_Update_Func(ReadOnlySpan<byte>.Empty, 0);
}
private void hmac_DRBG_Update(ReadOnlySpan<byte> seedMaterial)
{
hmac_DRBG_Update_Func(seedMaterial, 0);
hmac_DRBG_Update_Func(seedMaterial, 1);
}
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);
}
public int Generate(byte[] output, int outputOff, int outputLen, byte[] additionalInput, bool predictionResistant)
{
Span<byte> output2 = output.AsSpan(outputOff, outputLen);
if (additionalInput != null)
return GenerateWithInput(output2, additionalInput.AsSpan(), predictionResistant);
return Generate(output2, predictionResistant);
}
public int Generate(Span<byte> output, bool predictionResistant)
{
int num = output.Length * 8;
if (num > MAX_BITS_REQUEST)
throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST.ToString(), "output");
if (mReseedCounter > RESEED_MAX)
return -1;
if (predictionResistant)
Reseed(ReadOnlySpan<byte>.Empty);
ImplGenerate(output);
hmac_DRBG_Update();
mReseedCounter++;
return num;
}
public int GenerateWithInput(Span<byte> output, ReadOnlySpan<byte> additionalInput, bool predictionResistant)
{
int num = output.Length * 8;
if (num > MAX_BITS_REQUEST)
throw new ArgumentException("Number of bits per request limited to " + MAX_BITS_REQUEST.ToString(), "output");
if (mReseedCounter > RESEED_MAX)
return -1;
if (predictionResistant)
Reseed(additionalInput);
else
hmac_DRBG_Update(additionalInput);
ImplGenerate(output);
if (predictionResistant)
hmac_DRBG_Update();
else
hmac_DRBG_Update(additionalInput);
mReseedCounter++;
return num;
}
private void ImplGenerate(Span<byte> output)
{
int length = output.Length;
int num = length / mV.Length;
mHMac.Init(new KeyParameter(mK));
for (int i = 0; i < num; i++) {
mHMac.BlockUpdate(mV);
mHMac.DoFinal(mV);
byte[] source = mV;
int num2 = i * mV.Length;
source.CopyTo(output.Slice(num2, output.Length - num2));
}
int num3 = length - num * mV.Length;
if (num3 > 0) {
mHMac.BlockUpdate(mV);
mHMac.DoFinal(mV);
Span<byte> span = mV.AsSpan(0, num3);
int num2 = num * mV.Length;
span.CopyTo(output.Slice(num2, output.Length - num2));
}
}
public void Reseed(byte[] additionalInput)
{
Reseed(Spans.FromNullableReadOnly(additionalInput));
}
public unsafe void Reseed(ReadOnlySpan<byte> additionalInput)
{
int entropyLength = GetEntropyLength();
int num = entropyLength + additionalInput.Length;
Span<byte> span;
int num2;
if (num <= 256) {
num2 = num;
span = new Span<byte>(stackalloc byte[(int)(uint)num2], num2);
} else
span = new byte[num];
Span<byte> span2 = span;
GetEntropy(span2);
num2 = entropyLength;
additionalInput.CopyTo(span2.Slice(num2, span2.Length - num2));
hmac_DRBG_Update(span2);
mReseedCounter = 1;
}
private byte[] GetEntropy()
{
byte[] entropy = mEntropySource.GetEntropy();
if (entropy.Length < (mSecurityStrength + 7) / 8)
throw new InvalidOperationException("Insufficient entropy provided by entropy source");
return entropy;
}
private int GetEntropy(Span<byte> output)
{
int entropy = mEntropySource.GetEntropy(output);
if (entropy < (mSecurityStrength + 7) / 8)
throw new InvalidOperationException("Insufficient entropy provided by entropy source");
return entropy;
}
private int GetEntropyLength()
{
return (mEntropySource.EntropySize + 7) / 8;
}
}
}