SipHash.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  5. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
  6. namespace Best.HTTP.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. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  72. BlockUpdate(input.AsSpan(offset, length));
  73. #else
  74. int i = 0, fullWords = length & ~7;
  75. if (wordPos == 0)
  76. {
  77. for (; i < fullWords; i += 8)
  78. {
  79. m = (long)Pack.LE_To_UInt64(input, offset + i);
  80. ProcessMessageWord();
  81. }
  82. for (; i < length; ++i)
  83. {
  84. m = (long)(((ulong)m >> 8) | ((ulong)input[offset + i] << 56));
  85. }
  86. wordPos = length - fullWords;
  87. }
  88. else
  89. {
  90. int bits = wordPos << 3;
  91. for (; i < fullWords; i += 8)
  92. {
  93. ulong n = Pack.LE_To_UInt64(input, offset + i);
  94. m = (long)((n << bits) | ((ulong)m >> -bits));
  95. ProcessMessageWord();
  96. m = (long)n;
  97. }
  98. for (; i < length; ++i)
  99. {
  100. m = (long)(((ulong)m >> 8) | ((ulong)input[offset + i] << 56));
  101. if (++wordPos == 8)
  102. {
  103. ProcessMessageWord();
  104. wordPos = 0;
  105. }
  106. }
  107. }
  108. #endif
  109. }
  110. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  111. public virtual void BlockUpdate(ReadOnlySpan<byte> input)
  112. {
  113. int length = input.Length;
  114. int i = 0, fullWords = length & ~7;
  115. if (wordPos == 0)
  116. {
  117. for (; i < fullWords; i += 8)
  118. {
  119. m = (long)Pack.LE_To_UInt64(input[i..]);
  120. ProcessMessageWord();
  121. }
  122. for (; i < length; ++i)
  123. {
  124. m = (long)(((ulong)m >> 8) | ((ulong)input[i] << 56));
  125. }
  126. wordPos = length - fullWords;
  127. }
  128. else
  129. {
  130. int bits = wordPos << 3;
  131. for (; i < fullWords; i += 8)
  132. {
  133. ulong n = Pack.LE_To_UInt64(input[i..]);
  134. m = (long)((n << bits) | ((ulong)m >> -bits));
  135. ProcessMessageWord();
  136. m = (long)n;
  137. }
  138. for (; i < length; ++i)
  139. {
  140. m = (long)(((ulong)m >> 8) | ((ulong)input[i] << 56));
  141. if (++wordPos == 8)
  142. {
  143. ProcessMessageWord();
  144. wordPos = 0;
  145. }
  146. }
  147. }
  148. }
  149. #endif
  150. public virtual long DoFinal()
  151. {
  152. // NOTE: 2 distinct shifts to avoid "64-bit shift" when wordPos == 0
  153. m = (long)((ulong)m >> ((7 - wordPos) << 3));
  154. m = (long)((ulong)m >> 8);
  155. m = (long)((ulong)m | ((ulong)((wordCount << 3) + wordPos) << 56));
  156. ProcessMessageWord();
  157. v2 ^= 0xffL;
  158. ApplySipRounds(d);
  159. long result = v0 ^ v1 ^ v2 ^ v3;
  160. Reset();
  161. return result;
  162. }
  163. public virtual int DoFinal(byte[] output, int outOff)
  164. {
  165. long result = DoFinal();
  166. Pack.UInt64_To_LE((ulong)result, output, outOff);
  167. return 8;
  168. }
  169. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  170. public int DoFinal(Span<byte> output)
  171. {
  172. long result = DoFinal();
  173. Pack.UInt64_To_LE((ulong)result, output);
  174. return 8;
  175. }
  176. #endif
  177. public virtual void Reset()
  178. {
  179. v0 = k0 ^ 0x736f6d6570736575L;
  180. v1 = k1 ^ 0x646f72616e646f6dL;
  181. v2 = k0 ^ 0x6c7967656e657261L;
  182. v3 = k1 ^ 0x7465646279746573L;
  183. m = 0;
  184. wordPos = 0;
  185. wordCount = 0;
  186. }
  187. protected virtual void ProcessMessageWord()
  188. {
  189. ++wordCount;
  190. v3 ^= m;
  191. ApplySipRounds(c);
  192. v0 ^= m;
  193. }
  194. protected virtual void ApplySipRounds(int n)
  195. {
  196. long r0 = v0, r1 = v1, r2 = v2, r3 = v3;
  197. for (int r = 0; r < n; ++r)
  198. {
  199. r0 += r1;
  200. r2 += r3;
  201. r1 = RotateLeft(r1, 13);
  202. r3 = RotateLeft(r3, 16);
  203. r1 ^= r0;
  204. r3 ^= r2;
  205. r0 = RotateLeft(r0, 32);
  206. r2 += r1;
  207. r0 += r3;
  208. r1 = RotateLeft(r1, 17);
  209. r3 = RotateLeft(r3, 21);
  210. r1 ^= r2;
  211. r3 ^= r0;
  212. r2 = RotateLeft(r2, 32);
  213. }
  214. v0 = r0; v1 = r1; v2 = r2; v3 = r3;
  215. }
  216. protected static long RotateLeft(long x, int n)
  217. {
  218. ulong ux = (ulong)x;
  219. ux = (ux << n) | (ux >> -n);
  220. return (long)ux;
  221. }
  222. }
  223. }
  224. #pragma warning restore
  225. #endif