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

CtrSP800Drbg

public sealed class CtrSP800Drbg : ISP80090Drbg
using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; using System; namespace Org.BouncyCastle.Crypto.Prng.Drbg { public sealed class CtrSP800Drbg : ISP80090Drbg { private static readonly long TDEA_RESEED_MAX = 2147483648; private static readonly long AES_RESEED_MAX = 140737488355328; private static readonly int TDEA_MAX_BITS_REQUEST = 4096; private static readonly int AES_MAX_BITS_REQUEST = 262144; private readonly IEntropySource mEntropySource; private readonly IBlockCipher mEngine; private readonly int mKeySizeInBits; private readonly int mSeedLength; private readonly int mSecurityStrength; private byte[] mKey; private byte[] mV; private long mReseedCounter; private bool mIsTdea; private static readonly byte[] K_BITS = Hex.DecodeStrict("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); public int BlockSize => mV.Length * 8; public CtrSP800Drbg(IBlockCipher engine, int keySizeInBits, int securityStrength, IEntropySource entropySource, byte[] personalizationString, byte[] nonce) { if (securityStrength > 256) throw new ArgumentException("Requested security strength is not supported by the derivation function"); if (GetMaxSecurityStrength(engine, keySizeInBits) < securityStrength) throw new ArgumentException("Requested security strength is not supported by block cipher and key size"); if (entropySource.EntropySize < securityStrength) throw new ArgumentException("Not enough entropy for security strength required"); mEntropySource = entropySource; mEngine = engine; mKeySizeInBits = keySizeInBits; mSecurityStrength = securityStrength; mSeedLength = keySizeInBits + engine.GetBlockSize() * 8; mIsTdea = IsTdea(engine); CTR_DRBG_Instantiate_algorithm(personalizationString, nonce); } private void CTR_DRBG_Instantiate_algorithm(byte[] personalisationString, byte[] nonce) { byte[] entropy = GetEntropy(); byte[] input = Arrays.ConcatenateAll(entropy, nonce, personalisationString); byte[] array = BlockCipherDF(input, mSeedLength / 8); int blockSize = mEngine.GetBlockSize(); mKey = new byte[(mKeySizeInBits + 7) / 8]; mV = new byte[blockSize]; CTR_DRBG_Update(array, mKey, mV); mReseedCounter = 1; } private unsafe void CTR_DRBG_Update(ReadOnlySpan<byte> seed, Span<byte> key, Span<byte> v) { int length = seed.Length; Span<byte> span; int num; if (length <= 256) { num = length; span = new Span<byte>(stackalloc byte[(int)(uint)num], num); } else span = new byte[length]; Span<byte> span2 = span; int blockSize = mEngine.GetBlockSize(); if (blockSize <= 64) { num = blockSize; span = new Span<byte>(stackalloc byte[(int)(uint)num], num); } else span = new byte[blockSize]; Span<byte> output = span; mEngine.Init(true, ExpandToKeyParameter(key)); for (int i = 0; i * blockSize < seed.Length; i++) { AddOneTo(v); mEngine.ProcessBlock(v, output); int length2 = System.Math.Min(blockSize, span2.Length - i * blockSize); span = output.Slice(0, length2); num = i * blockSize; span.CopyTo(span2.Slice(num, span2.Length - num)); } Bytes.XorTo(length, seed, span2); key.CopyFrom(span2); num = key.Length; v.CopyFrom(span2.Slice(num, span2.Length - num)); } private unsafe void CTR_DRBG_Reseed_algorithm(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.Slice(0, entropyLength)); num2 = entropyLength; additionalInput.CopyTo(span2.Slice(num2, span2.Length - num2)); byte[] array = BlockCipherDF(span2, mSeedLength / 8); span2.Fill(0); CTR_DRBG_Update(array, mKey, mV); Array.Clear(array, 0, array.Length); mReseedCounter = 1; } private void AddOneTo(Span<byte> longer) { uint num = 1; int num2 = longer.Length; while (--num2 >= 0) { num += longer[num2]; longer[num2] = (byte)num; num >>= 8; } } private byte[] GetEntropy() { byte[] entropy = mEntropySource.GetEntropy(); if (entropy == null || 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 byte[] BlockCipherDF(byte[] input, int N) { return BlockCipherDF(input.AsSpan(), N); } private unsafe byte[] BlockCipherDF(ReadOnlySpan<byte> input, int N) { int blockSize = mEngine.GetBlockSize(); int length = input.Length; int num = (8 + length + 1 + blockSize - 1) / blockSize * blockSize; 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; Pack.UInt32_To_BE((uint)length, span2); Pack.UInt32_To_BE((uint)N, span2.Slice(4, span2.Length - 4)); input.CopyTo(span2.Slice(8, span2.Length - 8)); span2[8 + length] = 128; int num3 = mKeySizeInBits / 8; int num4 = num3 + blockSize; if (num4 <= 128) { num2 = num4; span = new Span<byte>(stackalloc byte[(int)(uint)num2], num2); } else span = new byte[num4]; Span<byte> span3 = span; if (blockSize <= 64) { num2 = blockSize; span = new Span<byte>(stackalloc byte[(int)(uint)num2], num2); } else span = new byte[blockSize]; Span<byte> bccOut = span; if (blockSize <= 64) { num2 = blockSize; span = new Span<byte>(stackalloc byte[(int)(uint)num2], num2); } else span = new byte[blockSize]; Span<byte> span4 = span; KeyParameter parameters = ExpandToKeyParameter(K_BITS.AsSpan(0, num3)); mEngine.Init(true, parameters); for (int i = 0; i * blockSize < num4; i++) { Pack.UInt32_To_BE((uint)i, span4); BCC(bccOut, span4, span2); int length2 = System.Math.Min(blockSize, num4 - i * blockSize); span = bccOut.Slice(0, length2); num2 = i * blockSize; span.CopyTo(span3.Slice(num2, span3.Length - num2)); } KeyParameter parameters2 = ExpandToKeyParameter(span3.Slice(0, num3)); mEngine.Init(true, parameters2); num2 = num3; Span<byte> span5 = span3.Slice(num2, span3.Length - num2); byte[] array = new byte[N]; for (int j = 0; j * blockSize < array.Length; j++) { mEngine.ProcessBlock(span5, span5); int length3 = System.Math.Min(blockSize, array.Length - j * blockSize); span = span5.Slice(0, length3); span.CopyTo(array.AsSpan(j * blockSize)); } return array; } private unsafe void BCC(Span<byte> bccOut, ReadOnlySpan<byte> iV, ReadOnlySpan<byte> data) { int blockSize = mEngine.GetBlockSize(); Span<byte> span; if (blockSize <= 64) { int num = blockSize; span = new Span<byte>(stackalloc byte[(int)(uint)num], num); } else span = new byte[blockSize]; Span<byte> span2 = span; if (blockSize <= 64) { int num = blockSize; span = new Span<byte>(stackalloc byte[(int)(uint)num], num); } else span = new byte[blockSize]; Span<byte> span3 = span; mEngine.ProcessBlock(iV, span2); int num2 = data.Length / blockSize; for (int i = 0; i < num2; i++) { int len = blockSize; ReadOnlySpan<byte> x = span2; int num = i * blockSize; Bytes.Xor(len, x, data.Slice(num, data.Length - num), span3); mEngine.ProcessBlock(span3, span2); } bccOut.CopyFrom(span2); } 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 length = output.Length; if (mIsTdea) { if (mReseedCounter > TDEA_RESEED_MAX) return -1; if (length > TDEA_MAX_BITS_REQUEST / 8) throw new ArgumentException("Number of bits per request limited to " + TDEA_MAX_BITS_REQUEST.ToString(), "output"); } else { if (mReseedCounter > AES_RESEED_MAX) return -1; if (length > AES_MAX_BITS_REQUEST / 8) throw new ArgumentException("Number of bits per request limited to " + AES_MAX_BITS_REQUEST.ToString(), "output"); } if (predictionResistant) CTR_DRBG_Reseed_algorithm(ReadOnlySpan<byte>.Empty); byte[] array = new byte[mSeedLength / 8]; return ImplGenerate(array, output); } public int GenerateWithInput(Span<byte> output, ReadOnlySpan<byte> additionalInput, bool predictionResistant) { int length = output.Length; if (mIsTdea) { if (mReseedCounter > TDEA_RESEED_MAX) return -1; if (length > TDEA_MAX_BITS_REQUEST / 8) throw new ArgumentException("Number of bits per request limited to " + TDEA_MAX_BITS_REQUEST.ToString(), "output"); } else { if (mReseedCounter > AES_RESEED_MAX) return -1; if (length > AES_MAX_BITS_REQUEST / 8) throw new ArgumentException("Number of bits per request limited to " + AES_MAX_BITS_REQUEST.ToString(), "output"); } int num = mSeedLength / 8; byte[] array; if (predictionResistant) { CTR_DRBG_Reseed_algorithm(additionalInput); array = new byte[num]; } else { array = BlockCipherDF(additionalInput, num); CTR_DRBG_Update(array, mKey, mV); } return ImplGenerate(array, output); } private unsafe int ImplGenerate(ReadOnlySpan<byte> seed, Span<byte> output) { mEngine.Init(true, ExpandToKeyParameter(mKey)); Span<byte> span; if (mV.Length <= 64) { int num = mV.Length; span = new Span<byte>(stackalloc byte[(int)(uint)num], num); } else span = new byte[mV.Length]; Span<byte> output2 = span; int length = output.Length; int i = 0; for (int num2 = length / output2.Length; i <= num2; i++) { int num3 = System.Math.Min(output2.Length, length - i * output2.Length); if (num3 != 0) { AddOneTo(mV); mEngine.ProcessBlock(mV, output2); span = output2.Slice(0, num3); int num = i * output2.Length; span.CopyTo(output.Slice(num, output.Length - num)); } } CTR_DRBG_Update(seed, mKey, mV); mReseedCounter++; return length * 8; } public void Reseed(byte[] additionalInput) { Reseed(Spans.FromNullableReadOnly(additionalInput)); } public void Reseed(ReadOnlySpan<byte> additionalInput) { CTR_DRBG_Reseed_algorithm(additionalInput); } private bool IsTdea(IBlockCipher cipher) { if (!cipher.AlgorithmName.Equals("DESede")) return cipher.AlgorithmName.Equals("TDEA"); return true; } private int GetMaxSecurityStrength(IBlockCipher cipher, int keySizeInBits) { if (IsTdea(cipher) && keySizeInBits == 168) return 112; if (cipher.AlgorithmName.Equals("AES")) return keySizeInBits; return -1; } private KeyParameter ExpandToKeyParameter(byte[] key) { if (!mIsTdea) return new KeyParameter(key); byte[] array = new byte[24]; PadKey(key, 0, array, 0); PadKey(key, 7, array, 8); PadKey(key, 14, array, 16); return new KeyParameter(array); } private unsafe KeyParameter ExpandToKeyParameter(ReadOnlySpan<byte> key) { if (!mIsTdea) return new KeyParameter(key); Span<byte> span = new Span<byte>(stackalloc byte[24], 24); PadKey(key, span); PadKey(key.Slice(7, key.Length - 7), span.Slice(8, span.Length - 8)); PadKey(key.Slice(14, key.Length - 14), span.Slice(16, span.Length - 16)); return new KeyParameter(span); } private void PadKey(byte[] keyMaster, int keyOff, byte[] tmp, int tmpOff) { PadKey(keyMaster.AsSpan(keyOff), tmp.AsSpan(tmpOff)); } private void PadKey(ReadOnlySpan<byte> keyMaster, Span<byte> tmp) { tmp[0] = (byte)(keyMaster[0] & 254); tmp[1] = (byte)((keyMaster[0] << 7) | ((keyMaster[1] & 252) >> 1)); tmp[2] = (byte)((keyMaster[1] << 6) | ((keyMaster[2] & 248) >> 2)); tmp[3] = (byte)((keyMaster[2] << 5) | ((keyMaster[3] & 240) >> 3)); tmp[4] = (byte)((keyMaster[3] << 4) | ((keyMaster[4] & 224) >> 4)); tmp[5] = (byte)((keyMaster[4] << 3) | ((keyMaster[5] & 192) >> 5)); tmp[6] = (byte)((keyMaster[5] << 2) | ((keyMaster[6] & 128) >> 6)); tmp[7] = (byte)(keyMaster[6] << 1); DesParameters.SetOddParity(tmp.Slice(0, 8)); } } }