#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
using System;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Utilities;
namespace Org.BouncyCastle.Crypto.Macs
{
///
/// 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).
///
///
/// "SipHash is a family of PRFs SipHash-c-d where the integer parameters c and d are the number of
/// compression rounds and the number of finalization rounds. A compression round is identical to a
/// finalization round and this round function is called SipRound. Given a 128-bit key k and a
/// (possibly empty) byte string m, SipHash-c-d returns a 64-bit value..."
///
public class SipHash
: IMac
{
protected readonly int c, d;
protected long k0, k1;
protected long v0, v1, v2, v3;
protected long m = 0;
protected int wordPos = 0;
protected int wordCount = 0;
/// SipHash-2-4
public SipHash()
: this(2, 4)
{
}
/// SipHash-c-d
/// the number of compression rounds
/// the number of finalization rounds
public SipHash(int c, int d)
{
this.c = c;
this.d = d;
}
public virtual string AlgorithmName
{
get { return "SipHash-" + c + "-" + d; }
}
public virtual int GetMacSize()
{
return 8;
}
public virtual void Init(ICipherParameters parameters)
{
KeyParameter keyParameter = parameters as KeyParameter;
if (keyParameter == null)
throw new ArgumentException("must be an instance of KeyParameter", "parameters");
byte[] key = keyParameter.GetKey();
if (key.Length != 16)
throw new ArgumentException("must be a 128-bit key", "parameters");
this.k0 = (long)Pack.LE_To_UInt64(key, 0);
this.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)
{
int i = 0, fullWords = length & ~7;
if (wordPos == 0)
{
for (; i < fullWords; i += 8)
{
m = (long)Pack.LE_To_UInt64(input, offset + i);
ProcessMessageWord();
}
for (; i < length; ++i)
{
m = (long)(((ulong)m >> 8) | ((ulong)input[offset + i] << 56));
}
wordPos = length - fullWords;
}
else
{
int bits = wordPos << 3;
for (; i < fullWords; i += 8)
{
ulong n = Pack.LE_To_UInt64(input, offset + i);
m = (long)((n << bits) | ((ulong)m >> -bits));
ProcessMessageWord();
m = (long)n;
}
for (; i < length; ++i)
{
m = (long)(((ulong)m >> 8) | ((ulong)input[offset + i] << 56));
if (++wordPos == 8)
{
ProcessMessageWord();
wordPos = 0;
}
}
}
}
public virtual long DoFinal()
{
// NOTE: 2 distinct shifts to avoid "64-bit shift" when wordPos == 0
m = (long)((ulong)m >> ((7 - wordPos) << 3));
m = (long)((ulong)m >> 8);
m = (long)((ulong)m | ((ulong)((wordCount << 3) + wordPos) << 56));
ProcessMessageWord();
v2 ^= 0xffL;
ApplySipRounds(d);
long result = v0 ^ v1 ^ v2 ^ v3;
Reset();
return result;
}
public virtual int DoFinal(byte[] output, int outOff)
{
long result = DoFinal();
Pack.UInt64_To_LE((ulong)result, output, outOff);
return 8;
}
public virtual void Reset()
{
v0 = k0 ^ 0x736f6d6570736575L;
v1 = k1 ^ 0x646f72616e646f6dL;
v2 = k0 ^ 0x6c7967656e657261L;
v3 = k1 ^ 0x7465646279746573L;
m = 0;
wordPos = 0;
wordCount = 0;
}
protected virtual void ProcessMessageWord()
{
++wordCount;
v3 ^= m;
ApplySipRounds(c);
v0 ^= m;
}
protected virtual void ApplySipRounds(int n)
{
long r0 = v0, r1 = v1, r2 = v2, r3 = v3;
for (int r = 0; r < n; ++r)
{
r0 += r1;
r2 += r3;
r1 = RotateLeft(r1, 13);
r3 = RotateLeft(r3, 16);
r1 ^= r0;
r3 ^= r2;
r0 = RotateLeft(r0, 32);
r2 += r1;
r0 += r3;
r1 = RotateLeft(r1, 17);
r3 = RotateLeft(r3, 21);
r1 ^= r2;
r3 ^= r0;
r2 = RotateLeft(r2, 32);
}
v0 = r0; v1 = r1; v2 = r2; v3 = r3;
}
protected static long RotateLeft(long x, int n)
{
ulong ux = (ulong)x;
ux = (ux << n) | (ux >> -n);
return (long)ux;
}
}
}
#endif