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

Argon2BytesGenerator

public sealed class Argon2BytesGenerator
using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Math.Raw; using Org.BouncyCastle.Utilities; using System; using System.Threading.Tasks; namespace Org.BouncyCastle.Crypto.Generators { public sealed class Argon2BytesGenerator { private sealed class FillBlock { private readonly Block R = new Block(); private readonly Block Z = new Block(); internal readonly Block addressBlock = new Block(); internal readonly Block inputBlock = new Block(); internal void ApplyBlake() { for (int i = 0; i < 8; i++) { int num = 16 * i; RoundFunction(Z, num, num + 1, num + 2, num + 3, num + 4, num + 5, num + 6, num + 7, num + 8, num + 9, num + 10, num + 11, num + 12, num + 13, num + 14, num + 15); } for (int j = 0; j < 8; j++) { int num2 = 2 * j; RoundFunction(Z, num2, num2 + 1, num2 + 16, num2 + 17, num2 + 32, num2 + 33, num2 + 48, num2 + 49, num2 + 64, num2 + 65, num2 + 80, num2 + 81, num2 + 96, num2 + 97, num2 + 112, num2 + 113); } } internal void Fill(Block Y, Block currentBlock) { Z.CopyBlock(Y); ApplyBlake(); currentBlock.Xor(Y, Z); } internal void Fill(Block X, Block Y, Block currentBlock) { R.Xor(X, Y); Z.CopyBlock(R); ApplyBlake(); currentBlock.Xor(R, Z); } internal void FillBlockWithXor(Block X, Block Y, Block currentBlock) { R.Xor(X, Y); Z.CopyBlock(R); ApplyBlake(); currentBlock.XorWith(R, Z); } } private sealed class Block { private const int Size = 128; internal readonly ulong[] v; internal Block() { v = new ulong[128]; } internal void FromBytes(byte[] input) { if (input.Length < 1024) throw new ArgumentException("input shorter than blocksize"); Pack.LE_To_UInt64(input, 0, v); } internal void ToBytes(byte[] output) { if (output.Length < 1024) throw new ArgumentException("output shorter than blocksize"); Pack.UInt64_To_LE(v, output, 0); } internal void CopyBlock(Block other) { Array.Copy(other.v, 0, v, 0, 128); } internal void Xor(Block b1, Block b2) { Nat.Xor64(128, b1.v, b2.v, v); } internal void XorWith(Block b1) { Nat.XorTo64(128, b1.v, v); } internal void XorWith(Block b1, Block b2) { Nat.XorBothTo64(128, b1.v, b2.v, v); } internal Block Clear() { Arrays.Fill(v, 0); return this; } } private sealed class Position { internal readonly int pass; internal readonly int slice; internal readonly int lane; internal Position(int pass, int slice, int lane) { this.pass = pass; this.slice = slice; this.lane = lane; } } private const int Argon2BlockSize = 1024; private const int Argon2QwordsInBlock = 128; private const int Argon2AddressesInBlock = 128; private const int Argon2PrehashDigestLength = 64; private const int Argon2PrehashSeedLength = 72; private const int Argon2SyncPoints = 4; private const int MinParallelism = 1; private const int MaxParallelism = 16777215; private const int MinOutlen = 4; private const int MinIterations = 1; private const ulong M32L = 4294967295; private readonly byte[] ZeroBytes = new byte[4]; private readonly TaskFactory m_taskFactory; private Argon2Parameters parameters; private Block[] memory; private int segmentLength; private int laneLength; public Argon2BytesGenerator() : this(null) { } public Argon2BytesGenerator(TaskFactory taskFactory) { m_taskFactory = taskFactory; } public void Init(Argon2Parameters parameters) { if (parameters.Version != Argon2Parameters.Version10 && parameters.Version != Argon2Parameters.Version13) throw new NotSupportedException("unknown Argon2 version"); if (parameters.Type != Argon2Parameters.Argon2d && parameters.Type != Argon2Parameters.Argon2i && parameters.Type != Argon2Parameters.Argon2id) throw new NotSupportedException("unknown Argon2 type"); int num; if (parameters.Parallelism < 1) { num = 1; throw new InvalidOperationException("parallelism must be at least " + num.ToString()); } if (parameters.Parallelism > 16777215) { num = 16777215; throw new InvalidOperationException("parallelism must be at most " + num.ToString()); } if (parameters.Iterations < 1) { num = 1; throw new InvalidOperationException("iterations must be at least " + num.ToString()); } this.parameters = parameters; int num2 = System.Math.Max(parameters.Memory, 8 * parameters.Parallelism); segmentLength = num2 / (4 * parameters.Parallelism); laneLength = segmentLength * 4; num2 = parameters.Parallelism * laneLength; memory = new Block[num2]; for (int i = 0; i < memory.Length; i++) { memory[i] = new Block(); } } public int GenerateBytes(char[] password, byte[] output) { return GenerateBytes(parameters.CharToByteConverter.Convert(password), output); } public int GenerateBytes(char[] password, byte[] output, int outOff, int outLen) { return GenerateBytes(parameters.CharToByteConverter.Convert(password), output, outOff, outLen); } public int GenerateBytes(byte[] password, byte[] output) { return GenerateBytes(password, output, 0, output.Length); } public int GenerateBytes(byte[] password, byte[] output, int outOff, int outLen) { if (outLen < 4) throw new InvalidOperationException("output length less than " + 4.ToString()); byte[] tmpBlockBytes = new byte[1024]; Initialize(tmpBlockBytes, password, outLen); FillMemoryBlocks(); Digest(tmpBlockBytes, output, outOff, outLen); Reset(); return outLen; } private void Reset() { if (memory != null) { for (int i = 0; i < memory.Length; i++) { memory[i]?.Clear(); } } } private void FillMemoryBlocks() { for (int i = 0; i < parameters.Iterations; i++) { for (int j = 0; j < 4; j++) { if (m_taskFactory == null || parameters.Parallelism <= 1) { for (int k = 0; k < parameters.Parallelism; k++) { Position position2 = new Position(i, j, k); FillSegment(position2); } } else { Task[] array = new Task[parameters.Parallelism]; for (int l = 0; l < parameters.Parallelism; l++) { Position position = new Position(i, j, l); array[l] = m_taskFactory.StartNew(delegate { FillSegment(position); }); } Task.WaitAll(array); } } } } private void FillSegment(Position position) { Block addressBlock = null; Block inputBlock = null; FillBlock fillBlock = new FillBlock(); bool flag = IsDataIndependentAddressing(position); int startingIndex = GetStartingIndex(position); int num = position.lane * laneLength + position.slice * segmentLength + startingIndex; int num2 = GetPrevOffset(num); if (flag) { addressBlock = fillBlock.addressBlock.Clear(); inputBlock = fillBlock.inputBlock.Clear(); InitAddressBlocks(fillBlock, position, inputBlock, addressBlock); } bool flag2 = IsWithXor(position); for (int i = startingIndex; i < segmentLength; i++) { ulong pseudoRandom = GetPseudoRandom(fillBlock, i, addressBlock, inputBlock, num2, flag); int refLane = GetRefLane(position, pseudoRandom); int refColumn = GetRefColumn(position, i, pseudoRandom, refLane == position.lane); Block x = memory[num2]; Block y = memory[laneLength * refLane + refColumn]; Block currentBlock = memory[num]; if (flag2) fillBlock.FillBlockWithXor(x, y, currentBlock); else fillBlock.Fill(x, y, currentBlock); num2 = num; num++; } } private bool IsDataIndependentAddressing(Position position) { if (parameters.Type != Argon2Parameters.Argon2i) { if (parameters.Type == Argon2Parameters.Argon2id && position.pass == 0) return position.slice < 2; return false; } return true; } private void InitAddressBlocks(FillBlock filler, Position position, Block inputBlock, Block addressBlock) { inputBlock.v[0] = (ulong)position.pass; inputBlock.v[1] = (ulong)position.lane; inputBlock.v[2] = (ulong)position.slice; inputBlock.v[3] = (ulong)memory.Length; inputBlock.v[4] = (ulong)parameters.Iterations; inputBlock.v[5] = (ulong)parameters.Type; if (position.pass == 0 && position.slice == 0) NextAddresses(filler, inputBlock, addressBlock); } private bool IsWithXor(Position position) { if (position.pass != 0) return parameters.Version != Argon2Parameters.Version10; return false; } private int GetPrevOffset(int currentOffset) { if (currentOffset % laneLength == 0) return currentOffset + laneLength - 1; return currentOffset - 1; } private static int GetStartingIndex(Position position) { if (position.pass == 0 && position.slice == 0) return 2; return 0; } private static void NextAddresses(FillBlock filler, Block inputBlock, Block addressBlock) { inputBlock.v[6]++; filler.Fill(inputBlock, addressBlock); filler.Fill(addressBlock, addressBlock); } private ulong GetPseudoRandom(FillBlock filler, int index, Block addressBlock, Block inputBlock, int prevOffset, bool dataIndependentAddressing) { if (dataIndependentAddressing) { int num = index % 128; if (num == 0) NextAddresses(filler, inputBlock, addressBlock); return addressBlock.v[num]; } return memory[prevOffset].v[0]; } private int GetRefLane(Position position, ulong pseudoRandom) { int result = (int)((long)(pseudoRandom >> 32) % (long)parameters.Parallelism); if (position.pass == 0 && position.slice == 0) result = position.lane; return result; } private int GetRefColumn(Position position, int index, ulong pseudoRandom, bool sameLane) { ulong num; ulong num2; if (position.pass == 0) { num = 0; num2 = (ulong)((!sameLane) ? (position.slice * segmentLength + ((index == 0) ? (-1) : 0)) : (position.slice * segmentLength + index - 1)); } else { num = (ulong)((position.slice + 1) * segmentLength % laneLength); num2 = (ulong)((!sameLane) ? (laneLength - segmentLength + ((index == 0) ? (-1) : 0)) : (laneLength - segmentLength + index - 1)); } ulong num3 = pseudoRandom & uint.MaxValue; num3 = num3 * num3 >> 32; num3 = num2 - 1 - (num2 * num3 >> 32); return (int)(num + num3) % laneLength; } private void Digest(byte[] tmpBlockBytes, byte[] output, int outOff, int outLen) { Block block = memory[laneLength - 1]; for (int i = 1; i < parameters.Parallelism; i++) { int num = i * laneLength + (laneLength - 1); block.XorWith(memory[num]); } block.ToBytes(tmpBlockBytes); Hash(tmpBlockBytes, output, outOff, outLen); } private static void Hash(byte[] input, byte[] output, int outOff, int outLen) { byte[] array = new byte[4]; Pack.UInt32_To_LE((uint)outLen, array); int num = 64; if (outLen <= num) { Blake2bDigest blake2bDigest = new Blake2bDigest(outLen * 8); ((IDigest)blake2bDigest).BlockUpdate(array, 0, array.Length); ((IDigest)blake2bDigest).BlockUpdate(input, 0, input.Length); ((IDigest)blake2bDigest).DoFinal(output, outOff); } else { int num2 = num / 2; IDigest digest = new Blake2bDigest(num * 8); byte[] array2 = new byte[num]; digest.BlockUpdate(array, 0, array.Length); digest.BlockUpdate(input, 0, input.Length); digest.DoFinal(array2, 0); Array.Copy(array2, 0, output, outOff, num2); int num3 = outOff + num2; int num4 = (outLen + 31) / 32 - 2; int num5 = 2; while (num5 <= num4) { digest.BlockUpdate(array2, 0, array2.Length); digest.DoFinal(array2, 0); Array.Copy(array2, 0, output, num3, num2); num5++; num3 += num2; } digest = new Blake2bDigest((outLen - 32 * num4) * 8); digest.BlockUpdate(array2, 0, array2.Length); digest.DoFinal(output, num3); } } private static void RoundFunction(Block block, int v0, int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8, int v9, int v10, int v11, int v12, int v13, int v14, int v15) { ulong[] v16 = block.v; F(v16, v0, v4, v8, v12); F(v16, v1, v5, v9, v13); F(v16, v2, v6, v10, v14); F(v16, v3, v7, v11, v15); F(v16, v0, v5, v10, v15); F(v16, v1, v6, v11, v12); F(v16, v2, v7, v8, v13); F(v16, v3, v4, v9, v14); } private static void F(ulong[] v, int a, int b, int c, int d) { QuarterRound(v, a, b, d, 32); QuarterRound(v, c, d, b, 24); QuarterRound(v, a, b, d, 16); QuarterRound(v, c, d, b, 63); } private static void QuarterRound(ulong[] v, int x, int y, int z, int s) { ulong num = v[x]; ulong num2 = v[y]; ulong num3 = v[z]; num += num2 + 2 * (num & uint.MaxValue) * (num2 & uint.MaxValue); num3 = Longs.RotateRight(num3 ^ num, s); v[x] = num; v[z] = num3; } private void Initialize(byte[] tmpBlockBytes, byte[] password, int outputLength) { Blake2bDigest blake2bDigest = new Blake2bDigest(512); uint[] array = new uint[6] { (uint)parameters.Parallelism, (uint)outputLength, (uint)parameters.Memory, (uint)parameters.Iterations, (uint)parameters.Version, (uint)parameters.Type }; Pack.UInt32_To_LE(array, tmpBlockBytes, 0); blake2bDigest.BlockUpdate(tmpBlockBytes, 0, array.Length * 4); AddByteString(tmpBlockBytes, blake2bDigest, password); AddByteString(tmpBlockBytes, blake2bDigest, parameters.Salt); AddByteString(tmpBlockBytes, blake2bDigest, parameters.Secret); AddByteString(tmpBlockBytes, blake2bDigest, parameters.Additional); byte[] array2 = new byte[72]; blake2bDigest.DoFinal(array2, 0); FillFirstBlocks(tmpBlockBytes, array2); } private void AddByteString(byte[] tmpBlockBytes, IDigest digest, byte[] octets) { if (octets == null) digest.BlockUpdate(ZeroBytes, 0, 4); else { Pack.UInt32_To_LE((uint)octets.Length, tmpBlockBytes, 0); digest.BlockUpdate(tmpBlockBytes, 0, 4); digest.BlockUpdate(octets, 0, octets.Length); } } private void FillFirstBlocks(byte[] tmpBlockBytes, byte[] initialHashWithZeros) { byte[] array = new byte[72]; Array.Copy(initialHashWithZeros, 0, array, 0, 64); array[64] = 1; for (int i = 0; i < parameters.Parallelism; i++) { Pack.UInt32_To_LE((uint)i, initialHashWithZeros, 68); Pack.UInt32_To_LE((uint)i, array, 68); Hash(initialHashWithZeros, tmpBlockBytes, 0, 1024); memory[i * laneLength].FromBytes(tmpBlockBytes); Hash(array, tmpBlockBytes, 0, 1024); memory[i * laneLength + 1].FromBytes(tmpBlockBytes); } } } }