<PackageReference Include="BouncyCastle.Cryptography" Version="2.7.0-beta.98" />

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.Runtime.CompilerServices; using System.Runtime.InteropServices; 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() { ulong[] v = Z.v; for (int i = 0; i < 128; i += 16) { GB(ref v[i], ref v[i + 4], ref v[i + 8], ref v[i + 12]); GB(ref v[i + 1], ref v[i + 5], ref v[i + 9], ref v[i + 13]); GB(ref v[i + 2], ref v[i + 6], ref v[i + 10], ref v[i + 14]); GB(ref v[i + 3], ref v[i + 7], ref v[i + 11], ref v[i + 15]); GB(ref v[i], ref v[i + 5], ref v[i + 10], ref v[i + 15]); GB(ref v[i + 1], ref v[i + 6], ref v[i + 11], ref v[i + 12]); GB(ref v[i + 2], ref v[i + 7], ref v[i + 8], ref v[i + 13]); GB(ref v[i + 3], ref v[i + 4], ref v[i + 9], ref v[i + 14]); } for (int j = 0; j < 16; j += 2) { GB(ref v[j], ref v[j + 32], ref v[j + 64], ref v[j + 96]); GB(ref v[j + 1], ref v[j + 33], ref v[j + 65], ref v[j + 97]); GB(ref v[j + 16], ref v[j + 48], ref v[j + 80], ref v[j + 112]); GB(ref v[j + 17], ref v[j + 49], ref v[j + 81], ref v[j + 113]); GB(ref v[j], ref v[j + 33], ref v[j + 80], ref v[j + 113]); GB(ref v[j + 1], ref v[j + 48], ref v[j + 81], ref v[j + 96]); GB(ref v[j + 16], ref v[j + 49], ref v[j + 64], ref v[j + 97]); GB(ref v[j + 17], ref v[j + 32], ref v[j + 65], ref v[j + 112]); } } 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 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]); } if (BitConverter.IsLittleEndian) Hash(MemoryMarshal.AsBytes(block.v.AsSpan()), output.AsSpan(outOff, outLen)); else { block.ToBytes(tmpBlockBytes); Hash(tmpBlockBytes, output, outOff, outLen); } } private static void Hash(byte[] input, byte[] output, int outOff, int outLen) { Hash(input.AsSpan(), output.AsSpan(outOff, outLen)); } private unsafe static void Hash(ReadOnlySpan<byte> input, Span<byte> output) { int length = output.Length; int num = 0; Span<byte> span = new Span<byte>(stackalloc byte[4], 4); Pack.UInt32_To_LE((uint)length, span); int num2 = 64; if (length <= num2) { Blake2bDigest blake2bDigest = new Blake2bDigest(length * 8); ((IDigest)blake2bDigest).BlockUpdate((ReadOnlySpan<byte>)span); ((IDigest)blake2bDigest).BlockUpdate(input); ((IDigest)blake2bDigest).DoFinal(output); } else { int num3 = num2 / 2; int num4 = num; IDigest digest = new Blake2bDigest(num2 * 8); byte[] array = new byte[num2]; digest.BlockUpdate(span); digest.BlockUpdate(input); digest.DoFinal(array); RuntimeHelpers.GetSubArray(array, new Range(0, num3)).CopyTo(output); num4 += num3; int num5 = (length + 31) / 32 - 2; int num6 = 2; int num7; while (num6 <= num5) { digest.BlockUpdate(array, 0, array.Length); digest.DoFinal(array, 0); byte[] subArray = RuntimeHelpers.GetSubArray(array, new Range(0, num3)); num7 = num4; subArray.CopyTo(output.Slice(num7, output.Length - num7)); num6++; num4 += num3; } digest = new Blake2bDigest((length - 32 * num5) * 8); digest.BlockUpdate(array, 0, array.Length); IDigest digest2 = digest; num7 = num4; digest2.DoFinal(output.Slice(num7, output.Length - num7)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void GB(ref ulong a, ref ulong b, ref ulong c, ref ulong d) { a += b + 2 * Mul32((uint)a, (uint)b); d = Longs.RotateRight(d ^ a, 32); c += d + 2 * Mul32((uint)c, (uint)d); b = Longs.RotateRight(b ^ c, 24); a += b + 2 * Mul32((uint)a, (uint)b); d = Longs.RotateRight(d ^ a, 16); c += d + 2 * Mul32((uint)c, (uint)d); b = Longs.RotateRight(b ^ c, 63); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Mul32(uint a, uint b) { return (ulong)((long)a * (long)b); } 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); if (BitConverter.IsLittleEndian) { Span<byte> output = MemoryMarshal.AsBytes(memory[i * laneLength].v.AsSpan()); Span<byte> output2 = MemoryMarshal.AsBytes(memory[i * laneLength + 1].v.AsSpan()); Hash(initialHashWithZeros, output); Hash(array, output2); } else { Hash(initialHashWithZeros, tmpBlockBytes, 0, 1024); memory[i * laneLength].FromBytes(tmpBlockBytes); Hash(array, tmpBlockBytes, 0, 1024); memory[i * laneLength + 1].FromBytes(tmpBlockBytes); } } } } }