#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR) #pragma warning disable using System; using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters; using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities; namespace BestHTTP.SecureProtocol.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; } } } #pragma warning restore #endif