SipHash.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  5. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
  6. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Macs
  7. {
  8. /// <summary>
  9. /// Implementation of SipHash as specified in "SipHash: a fast short-input PRF", by Jean-Philippe
  10. /// Aumasson and Daniel J. Bernstein (https://131002.net/siphash/siphash.pdf).
  11. /// </summary>
  12. /// <remarks>
  13. /// "SipHash is a family of PRFs SipHash-c-d where the integer parameters c and d are the number of
  14. /// compression rounds and the number of finalization rounds. A compression round is identical to a
  15. /// finalization round and this round function is called SipRound. Given a 128-bit key k and a
  16. /// (possibly empty) byte string m, SipHash-c-d returns a 64-bit value..."
  17. /// </remarks>
  18. public class SipHash
  19. : IMac
  20. {
  21. protected readonly int c, d;
  22. protected long k0, k1;
  23. protected long v0, v1, v2, v3;
  24. protected long m = 0;
  25. protected int wordPos = 0;
  26. protected int wordCount = 0;
  27. /// <summary>SipHash-2-4</summary>
  28. public SipHash()
  29. : this(2, 4)
  30. {
  31. }
  32. /// <summary>SipHash-c-d</summary>
  33. /// <param name="c">the number of compression rounds</param>
  34. /// <param name="d">the number of finalization rounds</param>
  35. public SipHash(int c, int d)
  36. {
  37. this.c = c;
  38. this.d = d;
  39. }
  40. public virtual string AlgorithmName
  41. {
  42. get { return "SipHash-" + c + "-" + d; }
  43. }
  44. public virtual int GetMacSize()
  45. {
  46. return 8;
  47. }
  48. public virtual void Init(ICipherParameters parameters)
  49. {
  50. KeyParameter keyParameter = parameters as KeyParameter;
  51. if (keyParameter == null)
  52. throw new ArgumentException("must be an instance of KeyParameter", "parameters");
  53. byte[] key = keyParameter.GetKey();
  54. if (key.Length != 16)
  55. throw new ArgumentException("must be a 128-bit key", "parameters");
  56. this.k0 = (long)Pack.LE_To_UInt64(key, 0);
  57. this.k1 = (long)Pack.LE_To_UInt64(key, 8);
  58. Reset();
  59. }
  60. public virtual void Update(byte input)
  61. {
  62. m = (long)(((ulong)m >> 8) | ((ulong)input << 56));
  63. if (++wordPos == 8)
  64. {
  65. ProcessMessageWord();
  66. wordPos = 0;
  67. }
  68. }
  69. public virtual void BlockUpdate(byte[] input, int offset, int length)
  70. {
  71. int i = 0, fullWords = length & ~7;
  72. if (wordPos == 0)
  73. {
  74. for (; i < fullWords; i += 8)
  75. {
  76. m = (long)Pack.LE_To_UInt64(input, offset + i);
  77. ProcessMessageWord();
  78. }
  79. for (; i < length; ++i)
  80. {
  81. m = (long)(((ulong)m >> 8) | ((ulong)input[offset + i] << 56));
  82. }
  83. wordPos = length - fullWords;
  84. }
  85. else
  86. {
  87. int bits = wordPos << 3;
  88. for (; i < fullWords; i += 8)
  89. {
  90. ulong n = Pack.LE_To_UInt64(input, offset + i);
  91. m = (long)((n << bits) | ((ulong)m >> -bits));
  92. ProcessMessageWord();
  93. m = (long)n;
  94. }
  95. for (; i < length; ++i)
  96. {
  97. m = (long)(((ulong)m >> 8) | ((ulong)input[offset + i] << 56));
  98. if (++wordPos == 8)
  99. {
  100. ProcessMessageWord();
  101. wordPos = 0;
  102. }
  103. }
  104. }
  105. }
  106. public virtual long DoFinal()
  107. {
  108. // NOTE: 2 distinct shifts to avoid "64-bit shift" when wordPos == 0
  109. m = (long)((ulong)m >> ((7 - wordPos) << 3));
  110. m = (long)((ulong)m >> 8);
  111. m = (long)((ulong)m | ((ulong)((wordCount << 3) + wordPos) << 56));
  112. ProcessMessageWord();
  113. v2 ^= 0xffL;
  114. ApplySipRounds(d);
  115. long result = v0 ^ v1 ^ v2 ^ v3;
  116. Reset();
  117. return result;
  118. }
  119. public virtual int DoFinal(byte[] output, int outOff)
  120. {
  121. long result = DoFinal();
  122. Pack.UInt64_To_LE((ulong)result, output, outOff);
  123. return 8;
  124. }
  125. public virtual void Reset()
  126. {
  127. v0 = k0 ^ 0x736f6d6570736575L;
  128. v1 = k1 ^ 0x646f72616e646f6dL;
  129. v2 = k0 ^ 0x6c7967656e657261L;
  130. v3 = k1 ^ 0x7465646279746573L;
  131. m = 0;
  132. wordPos = 0;
  133. wordCount = 0;
  134. }
  135. protected virtual void ProcessMessageWord()
  136. {
  137. ++wordCount;
  138. v3 ^= m;
  139. ApplySipRounds(c);
  140. v0 ^= m;
  141. }
  142. protected virtual void ApplySipRounds(int n)
  143. {
  144. long r0 = v0, r1 = v1, r2 = v2, r3 = v3;
  145. for (int r = 0; r < n; ++r)
  146. {
  147. r0 += r1;
  148. r2 += r3;
  149. r1 = RotateLeft(r1, 13);
  150. r3 = RotateLeft(r3, 16);
  151. r1 ^= r0;
  152. r3 ^= r2;
  153. r0 = RotateLeft(r0, 32);
  154. r2 += r1;
  155. r0 += r3;
  156. r1 = RotateLeft(r1, 17);
  157. r3 = RotateLeft(r3, 21);
  158. r1 ^= r2;
  159. r3 ^= r0;
  160. r2 = RotateLeft(r2, 32);
  161. }
  162. v0 = r0; v1 = r1; v2 = r2; v3 = r3;
  163. }
  164. protected static long RotateLeft(long x, int n)
  165. {
  166. ulong ux = (ulong)x;
  167. ux = (ux << n) | (ux >> -n);
  168. return (long)ux;
  169. }
  170. }
  171. }
  172. #pragma warning restore
  173. #endif