HashSP800Drbg
using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
using System;
using System.Collections.Generic;
namespace Org.BouncyCastle.Crypto.Prng.Drbg
{
public sealed class HashSP800Drbg : ISP80090Drbg
{
private static readonly byte[] ONE;
private static readonly long RESEED_MAX;
private static readonly int MAX_BITS_REQUEST;
private static readonly IDictionary<string, int> SeedLens;
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;
public int BlockSize => mDigest.GetDigestSize() * 8;
static HashSP800Drbg()
{
ONE = new byte[1] {
1
};
RESEED_MAX = 140737488355328;
MAX_BITS_REQUEST = 262144;
SeedLens = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
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);
}
public HashSP800Drbg(IDigest digest, int securityStrength, IEntropySource entropySource, byte[] personalizationString, byte[] nonce)
{
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");
mDigest = digest;
mEntropySource = entropySource;
mSecurityStrength = securityStrength;
mSeedLength = SeedLens[digest.AlgorithmName];
byte[] entropy = GetEntropy();
byte[] array = Arrays.ConcatenateAll(entropy, nonce, personalizationString);
mV = new byte[(mSeedLength + 7) / 8];
DrbgUtilities.HashDF(mDigest, array, mSeedLength, mV);
byte[] array2 = new byte[mV.Length + 1];
Array.Copy(mV, 0, array2, 1, mV.Length);
mC = new byte[(mSeedLength + 7) / 8];
DrbgUtilities.HashDF(mDigest, array2, mSeedLength, mC);
mReseedCounter = 1;
}
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)
{
if (output.Length * 8 > 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);
return ImplGenerate(output);
}
public unsafe int GenerateWithInput(Span<byte> output, ReadOnlySpan<byte> additionalInput, bool predictionResistant)
{
if (output.Length * 8 > 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 {
mDigest.Update(2);
mDigest.BlockUpdate(mV);
mDigest.BlockUpdate(additionalInput);
int digestSize = mDigest.GetDigestSize();
Span<byte> span;
if (digestSize <= 128) {
int num = digestSize;
span = new Span<byte>(stackalloc byte[(int)(uint)num], num);
} else
span = new byte[digestSize];
Span<byte> span2 = span;
mDigest.DoFinal(span2);
AddTo(mV, span2);
}
return ImplGenerate(output);
}
private unsafe int ImplGenerate(Span<byte> output)
{
Hashgen(mV, output);
mDigest.Update(3);
mDigest.BlockUpdate(mV);
int digestSize = mDigest.GetDigestSize();
Span<byte> span;
if (digestSize <= 128) {
int num = digestSize;
span = new Span<byte>(stackalloc byte[(int)(uint)num], num);
} else
span = new byte[digestSize];
Span<byte> span2 = span;
mDigest.DoFinal(span2);
AddTo(mV, span2);
AddTo(mV, mC);
Span<byte> span3 = new Span<byte>(stackalloc byte[4], 4);
Pack.UInt32_To_BE((uint)mReseedCounter, span3);
AddTo(mV, span3);
mReseedCounter++;
return output.Length * 8;
}
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;
}
private void AddTo(Span<byte> longer, ReadOnlySpan<byte> shorter)
{
int num = longer.Length - shorter.Length;
uint num2 = 0;
int num3 = shorter.Length;
while (--num3 >= 0) {
num2 = (uint)((int)num2 + (longer[num + num3] + shorter[num3]));
longer[num + num3] = (byte)num2;
num2 >>= 8;
}
num3 = num;
while (--num3 >= 0) {
num2 += longer[num3];
longer[num3] = (byte)num2;
num2 >>= 8;
}
}
public void Reseed(byte[] additionalInput)
{
Reseed(Spans.FromNullableReadOnly(additionalInput));
}
public unsafe void Reseed(ReadOnlySpan<byte> additionalInput)
{
int entropyLength = GetEntropyLength();
int num = 1 + mV.Length + entropyLength + additionalInput.Length;
int num2;
Span<byte> span;
if (num <= 256) {
num2 = num;
span = new Span<byte>(stackalloc byte[(int)(uint)num2], num2);
} else
span = new byte[num];
Span<byte> span2 = span;
span2[0] = 1;
mV.CopyTo(span2.Slice(1, span2.Length - 1));
num2 = 1 + mV.Length;
GetEntropy(span2.Slice(num2, span2.Length - num2));
num2 = 1 + mV.Length + entropyLength;
additionalInput.CopyTo(span2.Slice(num2, span2.Length - num2));
DrbgUtilities.HashDF(mDigest, span2, mSeedLength, mV);
int num3 = 1 + mV.Length;
if (num3 <= 128) {
num2 = num3;
span = new Span<byte>(stackalloc byte[(int)(uint)num2], num2);
} else
span = new byte[num3];
Span<byte> span3 = span;
span3[0] = 0;
mV.CopyTo(span3.Slice(1, span3.Length - 1));
DrbgUtilities.HashDF(mDigest, span3, mSeedLength, mC);
mReseedCounter = 1;
}
private void DoHash(ReadOnlySpan<byte> input, Span<byte> output)
{
mDigest.BlockUpdate(input);
mDigest.DoFinal(output);
}
private unsafe void Hashgen(ReadOnlySpan<byte> input, Span<byte> output)
{
int digestSize = mDigest.GetDigestSize();
int num = output.Length / digestSize;
Span<byte> span;
if (input.Length <= 256) {
int length = input.Length;
span = new Span<byte>(stackalloc byte[(int)(uint)length], length);
} else
span = new byte[input.Length];
Span<byte> span2 = span;
input.CopyTo(span2);
if (digestSize <= 128) {
int length = digestSize;
span = new Span<byte>(stackalloc byte[(int)(uint)length], length);
} else
span = new byte[digestSize];
Span<byte> output2 = span;
for (int i = 0; i <= num; i++) {
DoHash(span2, output2);
int length2 = System.Math.Min(digestSize, output.Length - i * digestSize);
span = output2.Slice(0, length2);
int length = i * digestSize;
span.CopyTo(output.Slice(length, output.Length - length));
AddTo(span2, ONE);
}
}
}
}