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

Blake2sDigest

public sealed class Blake2sDigest : IDigest
Implementation of the cryptographic hash function BLAKE2s. BLAKE2s is optimized for 32-bit platforms and produces digests of any size between 1 and 32 bytes.
using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; using System; using System.Runtime.CompilerServices; namespace Org.BouncyCastle.Crypto.Digests { public sealed class Blake2sDigest : IDigest { private static readonly uint[] IV = new uint[8] { 1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225 }; private static readonly byte[] Sigma = new byte[160] { 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 }; private const int ROUNDS = 10; private const int BLOCK_LENGTH_BYTES = 64; private readonly uint[] chainValue = new uint[8]; private readonly byte[] buffer = new byte[64]; private int digestLength = 32; private byte[] m_salt; private byte[] m_personalization; private byte[] m_key; private int fanout = 1; private int depth = 1; private int leafLength; private long nodeOffset; private int nodeDepth; private int innerHashLength; private int bufferPos; private uint t0; private uint t1; private uint f0; public string AlgorithmName => "BLAKE2s"; public Blake2sDigest() : this(256) { } public Blake2sDigest(Blake2sDigest digest) { Array.Copy(digest.chainValue, 0, chainValue, 0, 8); Array.Copy(digest.buffer, 0, buffer, 0, 64); bufferPos = digest.bufferPos; m_key = Arrays.Clone(digest.m_key); digestLength = digest.digestLength; t0 = digest.t0; t1 = digest.t1; f0 = digest.f0; m_salt = Arrays.Clone(digest.m_salt); m_personalization = Arrays.Clone(digest.m_personalization); fanout = digest.fanout; depth = digest.depth; leafLength = digest.leafLength; nodeOffset = digest.nodeOffset; nodeDepth = digest.nodeDepth; innerHashLength = digest.innerHashLength; } public Blake2sDigest(int digestBits) { if (digestBits < 8 || digestBits > 256 || digestBits % 8 != 0) throw new ArgumentException("BLAKE2s digest bit length must be a multiple of 8 and not greater than 256"); digestLength = digestBits / 8; Init(); } public Blake2sDigest(byte[] key) { digestLength = 32; if (key != null && key.Length != 0) { if (key.Length > 32) throw new ArgumentException("Keys > 32 are not supported", "key"); m_key = (byte[])key.Clone(); } Init(); } public Blake2sDigest(byte[] key, int digestBytes, byte[] salt, byte[] personalization) : this(digestBytes, key, salt, personalization, 0) { } internal Blake2sDigest(int digestBytes, byte[] key, byte[] salt, byte[] personalization, long offset) { if (digestBytes < 1 || digestBytes > 32) throw new ArgumentException("Invalid digest length (required: 1 - 32)"); digestLength = digestBytes; if (key != null && key.Length != 0) { if (key.Length > 32) throw new ArgumentException("Keys > 32 are not supported", "key"); m_key = (byte[])key.Clone(); } if (salt != null) { if (salt.Length != 8) throw new ArgumentException("salt length must be exactly 8 bytes", "salt"); m_salt = (byte[])salt.Clone(); } if (personalization != null) { if (personalization.Length != 8) throw new ArgumentException("personalization length must be exactly 8 bytes", "personalization"); m_personalization = (byte[])personalization.Clone(); } nodeOffset = offset; Init(); } internal Blake2sDigest(int digestBytes, int hashLength, long offset) { digestLength = digestBytes; nodeOffset = offset; fanout = 0; depth = 0; leafLength = hashLength; innerHashLength = hashLength; nodeDepth = 0; Init(); } private void Init() { int num = 0; if (m_key != null) { num = m_key.Length; Array.Copy(m_key, 0, buffer, 0, num); bufferPos = 64; } chainValue[0] = (uint)((int)IV[0] ^ (digestLength | (num << 8) | ((fanout << 16) | (depth << 24)))); chainValue[1] = (uint)((int)IV[1] ^ leafLength); int num2 = (int)(nodeOffset >> 32); int num3 = (int)nodeOffset; chainValue[2] = (uint)((int)IV[2] ^ num3); chainValue[3] = (uint)((int)IV[3] ^ (num2 | (nodeDepth << 16) | (innerHashLength << 24))); chainValue[4] = IV[4]; chainValue[5] = IV[5]; if (m_salt != null) { chainValue[4] ^= Pack.LE_To_UInt32(m_salt, 0); chainValue[5] ^= Pack.LE_To_UInt32(m_salt, 4); } chainValue[6] = IV[6]; chainValue[7] = IV[7]; if (m_personalization != null) { chainValue[6] ^= Pack.LE_To_UInt32(m_personalization, 0); chainValue[7] ^= Pack.LE_To_UInt32(m_personalization, 4); } } public void Update(byte b) { if (64 - bufferPos == 0) { t0 += 64; 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 = 64 - bufferPos; if (num >= len) { Array.Copy(message, offset, buffer, bufferPos, len); bufferPos += len; return; } Array.Copy(message, offset, buffer, bufferPos, num); t0 += 64; if (t0 == 0) t1++; Compress(buffer, 0); bufferPos = 0; Array.Clear(buffer, 0, buffer.Length); } int num2 = offset + len - 64; int i; for (i = offset + num; i < num2; i += 64) { t0 += 64; 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 = uint.MaxValue; t0 += (uint)bufferPos; if (t0 < 0 && bufferPos > 0 - t0) t1++; Compress(buffer, 0); Array.Clear(buffer, 0, buffer.Length); int num = digestLength >> 2; int num2 = digestLength & 3; Pack.UInt32_To_LE(chainValue, 0, num, output, outOffset); if (num2 > 0) { byte[] array = new byte[4]; Pack.UInt32_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) { uint[] array = new uint[16]; Pack.LE_To_UInt32(message, messagePos, array); uint a = chainValue[0]; uint a2 = chainValue[1]; uint a3 = chainValue[2]; uint a4 = chainValue[3]; uint b = chainValue[4]; uint b2 = chainValue[5]; uint b3 = chainValue[6]; uint b4 = chainValue[7]; uint c = IV[0]; uint c2 = IV[1]; uint c3 = IV[2]; uint c4 = IV[3]; uint d = IV[4] ^ t0; uint d2 = IV[5] ^ t1; uint d3 = IV[6] ^ f0; uint d4 = IV[7]; for (int i = 0; i < 10; 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 64; } 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(uint m1, uint m2, ref uint a, ref uint b, ref uint c, ref uint d) { a += b + m1; d = Integers.RotateRight(d ^ a, 16); c += d; b = Integers.RotateRight(b ^ c, 12); a += b + m2; d = Integers.RotateRight(d ^ a, 8); c += d; b = Integers.RotateRight(b ^ c, 7); } } }