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

Blake2bDigest

public sealed class Blake2bDigest : IDigest
Implementation of the cryptographic hash function Blake2b. BLAKE2b is optimized for 64-bit platforms and produces digests of any size between 1 and 64 bytes.
using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; using System; using System.Runtime.CompilerServices; namespace Org.BouncyCastle.Crypto.Digests { public sealed class Blake2bDigest : IDigest { private static readonly ulong[] IV = new ulong[8] { 7640891576956012808, 13503953896175478587, 4354685564936845355, 11912009170470909681, 5840696475078001361, 11170449401992604703, 2270897969802886507, 6620516959819538809 }; private static readonly byte[] Sigma = new byte[192] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4, 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8, 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13, 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9, 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11, 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10, 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5, 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }; private const int ROUNDS = 12; private const int BLOCK_LENGTH_BYTES = 128; private readonly ulong[] chainValue = new ulong[8]; private readonly byte[] buffer = new byte[128]; private int digestLength = 64; private byte[] m_salt; private byte[] m_personalization; private byte[] m_key; private int bufferPos; private ulong t0; private ulong t1; private ulong f0; public string AlgorithmName => "BLAKE2b"; public Blake2bDigest() : this(512) { } public Blake2bDigest(Blake2bDigest digest) { Array.Copy(digest.chainValue, 0, chainValue, 0, 8); Array.Copy(digest.buffer, 0, buffer, 0, 128); bufferPos = digest.bufferPos; m_key = Arrays.Clone(digest.m_key); digestLength = digest.digestLength; m_personalization = Arrays.Clone(digest.m_personalization); m_salt = Arrays.Clone(digest.m_salt); t0 = digest.t0; t1 = digest.t1; f0 = digest.f0; } public Blake2bDigest(int digestSize) { if (digestSize < 8 || digestSize > 512 || digestSize % 8 != 0) throw new ArgumentException("BLAKE2b digest bit length must be a multiple of 8 and not greater than 512"); digestLength = digestSize / 8; Init(); } public Blake2bDigest(byte[] key) { digestLength = 64; if (key != null && key.Length != 0) { if (key.Length > 64) throw new ArgumentException("Keys > 64 are not supported", "key"); m_key = (byte[])key.Clone(); } Init(); } public Blake2bDigest(byte[] key, int digestLength, byte[] salt, byte[] personalization) { if (digestLength < 1 || digestLength > 64) throw new ArgumentException("Invalid digest length (required: 1 - 64)"); this.digestLength = digestLength; if (key != null && key.Length != 0) { if (key.Length > 64) throw new ArgumentException("Keys > 64 are not supported", "key"); m_key = (byte[])key.Clone(); } if (salt != null) { if (salt.Length != 16) throw new ArgumentException("salt length must be exactly 16 bytes", "salt"); m_salt = (byte[])salt.Clone(); } if (personalization != null) { if (personalization.Length != 16) throw new ArgumentException("personalization length must be exactly 16 bytes", "personalization"); m_personalization = (byte[])personalization.Clone(); } Init(); } private void Init() { int num = 0; if (m_key != null) { num = m_key.Length; Array.Copy(m_key, 0, buffer, 0, num); bufferPos = 128; } chainValue[0] = (ulong)((long)IV[0] ^ (long)(digestLength | (num << 8) | 16842752)); chainValue[1] = IV[1]; chainValue[2] = IV[2]; chainValue[3] = IV[3]; chainValue[4] = IV[4]; chainValue[5] = IV[5]; if (m_salt != null) { chainValue[4] ^= Pack.LE_To_UInt64(m_salt, 0); chainValue[5] ^= Pack.LE_To_UInt64(m_salt, 8); } chainValue[6] = IV[6]; chainValue[7] = IV[7]; if (m_personalization != null) { chainValue[6] ^= Pack.LE_To_UInt64(m_personalization, 0); chainValue[7] ^= Pack.LE_To_UInt64(m_personalization, 8); } } public void Update(byte b) { if (128 - bufferPos == 0) { t0 += 128; if (t0 == 0) t1++; Compress(buffer, 0); Array.Clear(buffer, 0, buffer.Length); buffer[0] = b; bufferPos = 1; } else { buffer[bufferPos] = b; bufferPos++; } } public void BlockUpdate(byte[] message, int offset, int len) { if (message != null && len != 0) { int num = 0; if (bufferPos != 0) { num = 128 - bufferPos; if (num >= len) { Array.Copy(message, offset, buffer, bufferPos, len); bufferPos += len; return; } Array.Copy(message, offset, buffer, bufferPos, num); t0 += 128; if (t0 == 0) t1++; Compress(buffer, 0); bufferPos = 0; Array.Clear(buffer, 0, buffer.Length); } int num2 = offset + len - 128; int i; for (i = offset + num; i < num2; i += 128) { t0 += 128; if (t0 == 0) t1++; Compress(message, i); } Array.Copy(message, i, buffer, 0, offset + len - i); bufferPos += offset + len - i; } } public int DoFinal(byte[] output, int outOffset) { Check.OutputLength(output, outOffset, digestLength, "output buffer too short"); f0 = ulong.MaxValue; t0 += (ulong)bufferPos; if (bufferPos > 0 && t0 == 0) t1++; Compress(buffer, 0); Array.Clear(buffer, 0, buffer.Length); int num = digestLength >> 3; int num2 = digestLength & 7; Pack.UInt64_To_LE(chainValue, 0, num, output, outOffset); if (num2 > 0) { byte[] array = new byte[8]; Pack.UInt64_To_LE(chainValue[num], array, 0); Array.Copy(array, 0, output, outOffset + digestLength - num2, num2); } Reset(); return digestLength; } public void Reset() { bufferPos = 0; f0 = 0; t0 = 0; t1 = 0; Array.Clear(buffer, 0, buffer.Length); Init(); } private void Compress(byte[] message, int messagePos) { ulong[] array = new ulong[16]; Pack.LE_To_UInt64(message, messagePos, array); ulong a = chainValue[0]; ulong a2 = chainValue[1]; ulong a3 = chainValue[2]; ulong a4 = chainValue[3]; ulong b = chainValue[4]; ulong b2 = chainValue[5]; ulong b3 = chainValue[6]; ulong b4 = chainValue[7]; ulong c = IV[0]; ulong c2 = IV[1]; ulong c3 = IV[2]; ulong c4 = IV[3]; ulong d = IV[4] ^ t0; ulong d2 = IV[5] ^ t1; ulong d3 = IV[6] ^ f0; ulong d4 = IV[7]; for (int i = 0; i < 12; i++) { int num = i * 16; G(array[Sigma[num]], array[Sigma[num + 1]], ref a, ref b, ref c, ref d); G(array[Sigma[num + 2]], array[Sigma[num + 3]], ref a2, ref b2, ref c2, ref d2); G(array[Sigma[num + 4]], array[Sigma[num + 5]], ref a3, ref b3, ref c3, ref d3); G(array[Sigma[num + 6]], array[Sigma[num + 7]], ref a4, ref b4, ref c4, ref d4); G(array[Sigma[num + 8]], array[Sigma[num + 9]], ref a, ref b2, ref c3, ref d4); G(array[Sigma[num + 10]], array[Sigma[num + 11]], ref a2, ref b3, ref c4, ref d); G(array[Sigma[num + 12]], array[Sigma[num + 13]], ref a3, ref b4, ref c, ref d2); G(array[Sigma[num + 14]], array[Sigma[num + 15]], ref a4, ref b, ref c2, ref d3); } chainValue[0] ^= (a ^ c); chainValue[1] ^= (a2 ^ c2); chainValue[2] ^= (a3 ^ c3); chainValue[3] ^= (a4 ^ c4); chainValue[4] ^= (b ^ d); chainValue[5] ^= (b2 ^ d2); chainValue[6] ^= (b3 ^ d3); chainValue[7] ^= (b4 ^ d4); } public int GetDigestSize() { return digestLength; } public int GetByteLength() { return 128; } public void ClearKey() { if (m_key != null) { Array.Clear(m_key, 0, m_key.Length); Array.Clear(buffer, 0, buffer.Length); } } public void ClearSalt() { if (m_salt != null) Array.Clear(m_salt, 0, m_salt.Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void G(ulong m1, ulong m2, ref ulong a, ref ulong b, ref ulong c, ref ulong d) { a += b + m1; d = Longs.RotateRight(d ^ a, 32); c += d; b = Longs.RotateRight(b ^ c, 24); a += b + m2; d = Longs.RotateRight(d ^ a, 16); c += d; b = Longs.RotateRight(b ^ c, 63); } } }