123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
- #pragma warning disable
- using System;
- using System.Collections.Generic;
- using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
- using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
- namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Prng.Drbg
- {
- /**
- * A SP800-90A Hash DRBG.
- */
- public sealed class HashSP800Drbg
- : ISP80090Drbg
- {
- private readonly static byte[] ONE = { 0x01 };
- private readonly static long RESEED_MAX = 1L << (48 - 1);
- private readonly static int MAX_BITS_REQUEST = 1 << (19 - 1);
- private static readonly IDictionary<string, int> SeedLens =
- new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
- static HashSP800Drbg()
- {
- 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);
- }
- 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;
- /**
- * Construct a SP800-90A Hash DRBG.
- * <p>
- * Minimum entropy requirement is the security strength requested.
- * </p>
- * @param digest source digest to use for DRB stream.
- * @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 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];
- // 1. seed_material = entropy_input || nonce || personalization_string.
- // 2. seed = Hash_df (seed_material, seedlen).
- // 3. V = seed.
- // 4. C = Hash_df ((0x00 || V), seedlen). Comment: Preceed V with a byte
- // of zeros.
- // 5. reseed_counter = 1.
- // 6. Return V, C, and reseed_counter as the initial_working_state
- byte[] entropy = GetEntropy();
- byte[] seedMaterial = Arrays.ConcatenateAll(entropy, nonce, personalizationString);
- mV = new byte[(mSeedLength + 7) / 8];
- DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength, mV);
- byte[] subV = new byte[mV.Length + 1];
- Array.Copy(mV, 0, subV, 1, mV.Length);
- mC = new byte[(mSeedLength + 7) / 8];
- DrbgUtilities.HashDF(mDigest, subV, mSeedLength, mC);
- mReseedCounter = 1;
- }
- /**
- * 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
- {
- get { return mDigest.GetDigestSize() * 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
- // 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 = 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)
- {
- 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);
- AddTo(mV, w);
- }
- // 3.
- byte[] rv = Hashgen(mV, outputLen);
- // 4.
- byte[] subH = new byte[mV.Length + 1];
- Array.Copy(mV, 0, subH, 1, mV.Length);
- subH[0] = 0x03;
- byte[] H = Hash(subH);
- // 5.
- AddTo(mV, H);
- AddTo(mV, mC);
- byte[] c = new byte[4];
- Pack.UInt32_To_BE((uint)mReseedCounter, c);
- AddTo(mV, c);
- 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<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.
- // 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(additionalInput);
- }
- 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.
- Hashgen(mV, output);
- // 4.
- mDigest.Update(0x03);
- mDigest.BlockUpdate(mV);
- 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);
- Span<byte> c = stackalloc byte[4];
- Pack.UInt32_To_BE((uint)mReseedCounter, c);
- AddTo(mV, c);
- mReseedCounter++;
- return output.Length * 8;
- }
- #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<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 || UNITY_2021_2_OR_NEWER
- 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] + shorter[i];
- longer[off + i] = (byte)carry;
- carry >>= 8;
- }
- i = off;
- while (--i >= 0)
- {
- carry += longer[i];
- longer[i] = (byte)carry;
- carry >>= 8;
- }
- }
- /**
- * 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
- // 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);
- DrbgUtilities.HashDF(mDigest, seedMaterial, mSeedLength, mV);
- byte[] subV = new byte[mV.Length + 1];
- subV[0] = 0x00;
- Array.Copy(mV, 0, subV, 1, mV.Length);
- DrbgUtilities.HashDF(mDigest, subV, mSeedLength, mC);
- mReseedCounter = 1;
- #endif
- }
- #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
- public void Reseed(ReadOnlySpan<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.
- 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
- #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
- 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.
- #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
- private void Hashgen(ReadOnlySpan<byte> input, Span<byte> output)
- {
- int digestSize = mDigest.GetDigestSize();
- int m = output.Length / digestSize;
- int dataSize = input.Length;
- Span<byte> data = dataSize <= 256
- ? stackalloc byte[input.Length]
- : new byte[input.Length];
- input.CopyTo(data);
- Span<byte> dig = digestSize <= 128
- ? stackalloc byte[digestSize]
- : new byte[digestSize];
- for (int i = 0; i <= m; i++)
- {
- DoHash(data, dig);
- 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
- }
- }
- #pragma warning restore
- #endif
|