<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.1" />

HashSP800Drbg

public sealed class HashSP800Drbg : ISP80090Drbg
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); } } } }