Blake2sDigest
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);
}
}
}