SipHash.cs 5.8 KB

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