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

SipHash

public class SipHash : IMac
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); } } }