SipHash
Implementation of SipHash as specified in "SipHash: a fast short-input PRF", by Jean-Philippe
Aumasson and Daniel J. Bernstein (https://131002.net/siphash/siphash.pdf).
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Utilities;
using System;
namespace Org.BouncyCastle.Crypto.Macs
{
public class SipHash : IMac
{
protected readonly int c;
protected readonly int d;
protected long k0;
protected long k1;
protected long v0;
protected long v1;
protected long v2;
protected long v3;
protected long m;
protected int wordPos;
protected int wordCount;
public virtual string AlgorithmName => "SipHash-" + c.ToString() + "-" + d.ToString();
public SipHash()
: this(2, 4)
{
}
public SipHash(int c, int d)
{
this.c = c;
this.d = d;
}
public virtual int GetMacSize()
{
return 8;
}
public virtual void Init(ICipherParameters parameters)
{
KeyParameter obj = parameters as KeyParameter;
if (obj == null)
throw new ArgumentException("must be an instance of KeyParameter", "parameters");
byte[] key = obj.GetKey();
if (key.Length != 16)
throw new ArgumentException("must be a 128-bit key", "parameters");
k0 = (long)Pack.LE_To_UInt64(key, 0);
k1 = (long)Pack.LE_To_UInt64(key, 8);
Reset();
}
public virtual void Update(byte input)
{
m = (long)(((ulong)m >> 8) | ((ulong)input << 56));
if (++wordPos == 8) {
ProcessMessageWord();
wordPos = 0;
}
}
public virtual void BlockUpdate(byte[] input, int offset, int length)
{
BlockUpdate(input.AsSpan(offset, length));
}
public virtual void BlockUpdate(ReadOnlySpan<byte> input)
{
int length = input.Length;
int i = 0;
int num = length & -8;
if (wordPos == 0) {
for (; i < num; i += 8) {
int num2 = i;
m = (long)Pack.LE_To_UInt64(input.Slice(num2, input.Length - num2));
ProcessMessageWord();
}
for (; i < length; i++) {
m = (long)(((ulong)m >> 8) | ((ulong)input[i] << 56));
}
wordPos = length - num;
} else {
int num3 = wordPos << 3;
for (; i < num; i += 8) {
int num2 = i;
ulong num4 = Pack.LE_To_UInt64(input.Slice(num2, input.Length - num2));
m = (long)((num4 << num3) | ((ulong)m >> -num3));
ProcessMessageWord();
m = (long)num4;
}
for (; i < length; i++) {
m = (long)(((ulong)m >> 8) | ((ulong)input[i] << 56));
if (++wordPos == 8) {
ProcessMessageWord();
wordPos = 0;
}
}
}
}
public virtual long DoFinal()
{
m = (long)((ulong)m >> (7 - wordPos << 3));
m = (long)((ulong)m >> 8);
m |= (long)((wordCount << 3) + wordPos) << 56;
ProcessMessageWord();
v2 ^= 255;
ApplySipRounds(d);
long result = v0 ^ v1 ^ v2 ^ v3;
Reset();
return result;
}
public virtual int DoFinal(byte[] output, int outOff)
{
Pack.UInt64_To_LE((ulong)DoFinal(), output, outOff);
return 8;
}
public int DoFinal(Span<byte> output)
{
Pack.UInt64_To_LE((ulong)DoFinal(), output);
return 8;
}
public virtual void Reset()
{
v0 = (k0 ^ 8317987319222330741);
v1 = (k1 ^ 7237128888997146477);
v2 = (k0 ^ 7816392313619706465);
v3 = (k1 ^ 8387220255154660723);
m = 0;
wordPos = 0;
wordCount = 0;
}
protected virtual void ProcessMessageWord()
{
wordCount++;
v3 ^= m;
ApplySipRounds(c);
v0 ^= m;
}
protected virtual void ApplySipRounds(int n)
{
long num = v0;
long num2 = v1;
long num3 = v2;
long num4 = v3;
for (int i = 0; i < n; i++) {
num += num2;
num3 += num4;
num2 = RotateLeft(num2, 13);
num4 = RotateLeft(num4, 16);
num2 ^= num;
num4 ^= num3;
num = RotateLeft(num, 32);
num3 += num2;
num += num4;
num2 = RotateLeft(num2, 17);
num4 = RotateLeft(num4, 21);
num2 ^= num3;
num4 ^= num;
num3 = RotateLeft(num3, 32);
}
v0 = num;
v1 = num2;
v2 = num3;
v3 = num4;
}
protected static long RotateLeft(long x, int n)
{
return (x << n) | (long)((ulong)x >> -n);
}
}
}