SparkleDigest
Sparkle v1.2, based on the current round 3 submission, https://sparkle-lwc.github.io/ .
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
using System;
using System.Runtime.CompilerServices;
namespace Org.BouncyCastle.Crypto.Digests
{
public sealed class SparkleDigest : IDigest
{
public enum SparkleParameters
{
ESCH256,
ESCH384
}
private const int RATE_BYTES = 16;
private const int RATE_WORDS = 4;
private string algorithmName;
private readonly uint[] state;
private readonly byte[] m_buf = new byte[16];
private readonly int DIGEST_BYTES;
private readonly int SPARKLE_STEPS_SLIM;
private readonly int SPARKLE_STEPS_BIG;
private readonly int STATE_WORDS;
private int m_bufPos;
public string AlgorithmName => algorithmName;
public SparkleDigest(SparkleParameters sparkleParameters)
{
switch (sparkleParameters) {
case SparkleParameters.ESCH256:
algorithmName = "ESCH-256";
DIGEST_BYTES = 32;
SPARKLE_STEPS_SLIM = 7;
SPARKLE_STEPS_BIG = 11;
STATE_WORDS = 12;
break;
case SparkleParameters.ESCH384:
algorithmName = "ESCH-384";
DIGEST_BYTES = 48;
SPARKLE_STEPS_SLIM = 8;
SPARKLE_STEPS_BIG = 12;
STATE_WORDS = 16;
break;
default:
throw new ArgumentException("Invalid definition of ESCH instance");
}
state = new uint[STATE_WORDS];
}
public int GetDigestSize()
{
return DIGEST_BYTES;
}
public int GetByteLength()
{
return 16;
}
public void Update(byte input)
{
if (m_bufPos == 16) {
ProcessBlock(m_buf, SPARKLE_STEPS_SLIM);
m_bufPos = 0;
}
m_buf[m_bufPos++] = input;
}
public void BlockUpdate(byte[] input, int inOff, int inLen)
{
Check.DataLength(input, inOff, inLen, "input buffer too short");
BlockUpdate(input.AsSpan(inOff, inLen));
}
public void BlockUpdate(ReadOnlySpan<byte> input)
{
int num = 16 - m_bufPos;
if (input.Length <= num) {
input.CopyTo(m_buf.AsSpan(m_bufPos));
m_bufPos += input.Length;
} else {
if (m_bufPos > 0) {
input.Slice(0, num).CopyTo(m_buf.AsSpan(m_bufPos));
int num2 = num;
input = input.Slice(num2, input.Length - num2);
ProcessBlock(m_buf, SPARKLE_STEPS_SLIM);
}
while (input.Length > 16) {
ProcessBlock(input, SPARKLE_STEPS_SLIM);
input = input.Slice(16, input.Length - 16);
}
input.CopyTo(m_buf);
m_bufPos = input.Length;
}
}
public int DoFinal(byte[] output, int outOff)
{
Check.OutputLength(output, outOff, DIGEST_BYTES, "output buffer too short");
return DoFinal(output.AsSpan(outOff));
}
public int DoFinal(Span<byte> output)
{
if (m_bufPos < 16) {
state[(STATE_WORDS >> 1) - 1] ^= 16777216;
m_buf[m_bufPos] = 128;
while (++m_bufPos < 16) {
m_buf[m_bufPos] = 0;
}
} else
state[(STATE_WORDS >> 1) - 1] ^= 33554432;
ProcessBlock(m_buf, SPARKLE_STEPS_BIG);
Pack.UInt32_To_LE(RuntimeHelpers.GetSubArray(state, Range.EndAt(4)), output);
if (STATE_WORDS == 16) {
SparkleEngine.SparkleOpt16(state, SPARKLE_STEPS_SLIM);
Pack.UInt32_To_LE(RuntimeHelpers.GetSubArray(state, Range.EndAt(4)), output.Slice(16, output.Length - 16));
SparkleEngine.SparkleOpt16(state, SPARKLE_STEPS_SLIM);
Pack.UInt32_To_LE(RuntimeHelpers.GetSubArray(state, Range.EndAt(4)), output.Slice(32, output.Length - 32));
} else {
SparkleEngine.SparkleOpt12(state, SPARKLE_STEPS_SLIM);
Pack.UInt32_To_LE(RuntimeHelpers.GetSubArray(state, Range.EndAt(4)), output.Slice(16, output.Length - 16));
}
Reset();
return DIGEST_BYTES;
}
public void Reset()
{
Arrays.Fill(state, 0);
Arrays.Fill(m_buf, 0);
m_bufPos = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ProcessBlock(ReadOnlySpan<byte> block, int steps)
{
uint num = Pack.LE_To_UInt32(block);
uint num2 = Pack.LE_To_UInt32(block.Slice(4, block.Length - 4));
uint num3 = Pack.LE_To_UInt32(block.Slice(8, block.Length - 8));
uint num4 = Pack.LE_To_UInt32(block.Slice(12, block.Length - 12));
uint num5 = ELL(num ^ num3);
uint num6 = ELL(num2 ^ num4);
state[0] ^= (num ^ num6);
state[1] ^= (num2 ^ num5);
state[2] ^= (num3 ^ num6);
state[3] ^= (num4 ^ num5);
state[4] ^= num6;
state[5] ^= num5;
if (STATE_WORDS == 16) {
state[6] ^= num6;
state[7] ^= num5;
SparkleEngine.SparkleOpt16(state, steps);
} else
SparkleEngine.SparkleOpt12(state, steps);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint ELL(uint x)
{
return Integers.RotateRight(x, 16) ^ (x & 65535);
}
}
}