Poly1305.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Generators;
  5. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
  7. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Macs
  8. {
  9. /// <summary>
  10. /// Poly1305 message authentication code, designed by D. J. Bernstein.
  11. /// </summary>
  12. /// <remarks>
  13. /// Poly1305 computes a 128-bit (16 bytes) authenticator, using a 128 bit nonce and a 256 bit key
  14. /// consisting of a 128 bit key applied to an underlying cipher, and a 128 bit key (with 106
  15. /// effective key bits) used in the authenticator.
  16. ///
  17. /// The polynomial calculation in this implementation is adapted from the public domain <a
  18. /// href="https://github.com/floodyberry/poly1305-donna">poly1305-donna-unrolled</a> C implementation
  19. /// by Andrew M (@floodyberry).
  20. /// </remarks>
  21. /// <seealso cref="BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Generators.Poly1305KeyGenerator"/>
  22. public class Poly1305
  23. : IMac
  24. {
  25. private const int BlockSize = 16;
  26. private readonly IBlockCipher cipher;
  27. private readonly byte[] singleByte = new byte[1];
  28. // Initialised state
  29. /** Polynomial key */
  30. private uint r0, r1, r2, r3, r4;
  31. /** Precomputed 5 * r[1..4] */
  32. private uint s1, s2, s3, s4;
  33. /** Encrypted nonce */
  34. private uint k0, k1, k2, k3;
  35. // Accumulating state
  36. /** Current block of buffered input */
  37. private byte[] currentBlock = new byte[BlockSize];
  38. /** Current offset in input buffer */
  39. private int currentBlockOffset = 0;
  40. /** Polynomial accumulator */
  41. private uint h0, h1, h2, h3, h4;
  42. /**
  43. * Constructs a Poly1305 MAC, where the key passed to init() will be used directly.
  44. */
  45. public Poly1305()
  46. {
  47. this.cipher = null;
  48. }
  49. /**
  50. * Constructs a Poly1305 MAC, using a 128 bit block cipher.
  51. */
  52. public Poly1305(IBlockCipher cipher)
  53. {
  54. if (cipher.GetBlockSize() != BlockSize)
  55. {
  56. throw new ArgumentException("Poly1305 requires a 128 bit block cipher.");
  57. }
  58. this.cipher = cipher;
  59. }
  60. /// <summary>
  61. /// Initialises the Poly1305 MAC.
  62. /// </summary>
  63. /// <param name="parameters">a {@link ParametersWithIV} containing a 128 bit nonce and a {@link KeyParameter} with
  64. /// a 256 bit key complying to the {@link Poly1305KeyGenerator Poly1305 key format}.</param>
  65. public void Init(ICipherParameters parameters)
  66. {
  67. byte[] nonce = null;
  68. if (cipher != null)
  69. {
  70. if (!(parameters is ParametersWithIV))
  71. throw new ArgumentException("Poly1305 requires an IV when used with a block cipher.", "parameters");
  72. ParametersWithIV ivParams = (ParametersWithIV)parameters;
  73. nonce = ivParams.GetIV();
  74. parameters = ivParams.Parameters;
  75. }
  76. if (!(parameters is KeyParameter))
  77. throw new ArgumentException("Poly1305 requires a key.");
  78. KeyParameter keyParams = (KeyParameter)parameters;
  79. SetKey(keyParams.GetKey(), nonce);
  80. Reset();
  81. }
  82. private void SetKey(byte[] key, byte[] nonce)
  83. {
  84. if (key.Length != 32)
  85. throw new ArgumentException("Poly1305 key must be 256 bits.");
  86. if (cipher != null && (nonce == null || nonce.Length != BlockSize))
  87. throw new ArgumentException("Poly1305 requires a 128 bit IV.");
  88. // Extract r portion of key (and "clamp" the values)
  89. uint t0 = Pack.LE_To_UInt32(key, 0);
  90. uint t1 = Pack.LE_To_UInt32(key, 4);
  91. uint t2 = Pack.LE_To_UInt32(key, 8);
  92. uint t3 = Pack.LE_To_UInt32(key, 12);
  93. // NOTE: The masks perform the key "clamping" implicitly
  94. r0 = t0 & 0x03FFFFFFU;
  95. r1 = ((t0 >> 26) | (t1 << 6)) & 0x03FFFF03U;
  96. r2 = ((t1 >> 20) | (t2 << 12)) & 0x03FFC0FFU;
  97. r3 = ((t2 >> 14) | (t3 << 18)) & 0x03F03FFFU;
  98. r4 = (t3 >> 8) & 0x000FFFFFU;
  99. // Precompute multipliers
  100. s1 = r1 * 5;
  101. s2 = r2 * 5;
  102. s3 = r3 * 5;
  103. s4 = r4 * 5;
  104. byte[] kBytes;
  105. int kOff;
  106. if (cipher == null)
  107. {
  108. kBytes = key;
  109. kOff = BlockSize;
  110. }
  111. else
  112. {
  113. // Compute encrypted nonce
  114. kBytes = new byte[BlockSize];
  115. kOff = 0;
  116. cipher.Init(true, new KeyParameter(key, BlockSize, BlockSize));
  117. cipher.ProcessBlock(nonce, 0, kBytes, 0);
  118. }
  119. k0 = Pack.LE_To_UInt32(kBytes, kOff + 0);
  120. k1 = Pack.LE_To_UInt32(kBytes, kOff + 4);
  121. k2 = Pack.LE_To_UInt32(kBytes, kOff + 8);
  122. k3 = Pack.LE_To_UInt32(kBytes, kOff + 12);
  123. }
  124. public string AlgorithmName
  125. {
  126. get { return cipher == null ? "Poly1305" : "Poly1305-" + cipher.AlgorithmName; }
  127. }
  128. public int GetMacSize()
  129. {
  130. return BlockSize;
  131. }
  132. public void Update(byte input)
  133. {
  134. singleByte[0] = input;
  135. BlockUpdate(singleByte, 0, 1);
  136. }
  137. public void BlockUpdate(byte[] input, int inOff, int len)
  138. {
  139. int copied = 0;
  140. while (len > copied)
  141. {
  142. if (currentBlockOffset == BlockSize)
  143. {
  144. ProcessBlock();
  145. currentBlockOffset = 0;
  146. }
  147. int toCopy = System.Math.Min((len - copied), BlockSize - currentBlockOffset);
  148. Array.Copy(input, copied + inOff, currentBlock, currentBlockOffset, toCopy);
  149. copied += toCopy;
  150. currentBlockOffset += toCopy;
  151. }
  152. }
  153. private void ProcessBlock()
  154. {
  155. if (currentBlockOffset < BlockSize)
  156. {
  157. currentBlock[currentBlockOffset] = 1;
  158. for (int i = currentBlockOffset + 1; i < BlockSize; i++)
  159. {
  160. currentBlock[i] = 0;
  161. }
  162. }
  163. ulong t0 = Pack.LE_To_UInt32(currentBlock, 0);
  164. ulong t1 = Pack.LE_To_UInt32(currentBlock, 4);
  165. ulong t2 = Pack.LE_To_UInt32(currentBlock, 8);
  166. ulong t3 = Pack.LE_To_UInt32(currentBlock, 12);
  167. h0 += (uint)(t0 & 0x3ffffffU);
  168. h1 += (uint)((((t1 << 32) | t0) >> 26) & 0x3ffffff);
  169. h2 += (uint)((((t2 << 32) | t1) >> 20) & 0x3ffffff);
  170. h3 += (uint)((((t3 << 32) | t2) >> 14) & 0x3ffffff);
  171. h4 += (uint)(t3 >> 8);
  172. if (currentBlockOffset == BlockSize)
  173. {
  174. h4 += (1 << 24);
  175. }
  176. ulong tp0 = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1);
  177. ulong tp1 = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2);
  178. ulong tp2 = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3);
  179. ulong tp3 = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4);
  180. ulong tp4 = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0);
  181. h0 = (uint)tp0 & 0x3ffffff; tp1 += (tp0 >> 26);
  182. h1 = (uint)tp1 & 0x3ffffff; tp2 += (tp1 >> 26);
  183. h2 = (uint)tp2 & 0x3ffffff; tp3 += (tp2 >> 26);
  184. h3 = (uint)tp3 & 0x3ffffff; tp4 += (tp3 >> 26);
  185. h4 = (uint)tp4 & 0x3ffffff;
  186. h0 += (uint)(tp4 >> 26) * 5;
  187. h1 += (h0 >> 26); h0 &= 0x3ffffff;
  188. }
  189. public int DoFinal(byte[] output, int outOff)
  190. {
  191. Check.DataLength(output, outOff, BlockSize, "Output buffer is too short.");
  192. if (currentBlockOffset > 0)
  193. {
  194. // Process padded block
  195. ProcessBlock();
  196. }
  197. h1 += (h0 >> 26); h0 &= 0x3ffffff;
  198. h2 += (h1 >> 26); h1 &= 0x3ffffff;
  199. h3 += (h2 >> 26); h2 &= 0x3ffffff;
  200. h4 += (h3 >> 26); h3 &= 0x3ffffff;
  201. h0 += (h4 >> 26) * 5; h4 &= 0x3ffffff;
  202. h1 += (h0 >> 26); h0 &= 0x3ffffff;
  203. uint g0, g1, g2, g3, g4, b;
  204. g0 = h0 + 5; b = g0 >> 26; g0 &= 0x3ffffff;
  205. g1 = h1 + b; b = g1 >> 26; g1 &= 0x3ffffff;
  206. g2 = h2 + b; b = g2 >> 26; g2 &= 0x3ffffff;
  207. g3 = h3 + b; b = g3 >> 26; g3 &= 0x3ffffff;
  208. g4 = h4 + b - (1 << 26);
  209. b = (g4 >> 31) - 1;
  210. uint nb = ~b;
  211. h0 = (h0 & nb) | (g0 & b);
  212. h1 = (h1 & nb) | (g1 & b);
  213. h2 = (h2 & nb) | (g2 & b);
  214. h3 = (h3 & nb) | (g3 & b);
  215. h4 = (h4 & nb) | (g4 & b);
  216. ulong f0, f1, f2, f3;
  217. f0 = ((h0 ) | (h1 << 26)) + (ulong)k0;
  218. f1 = ((h1 >> 6 ) | (h2 << 20)) + (ulong)k1;
  219. f2 = ((h2 >> 12) | (h3 << 14)) + (ulong)k2;
  220. f3 = ((h3 >> 18) | (h4 << 8 )) + (ulong)k3;
  221. Pack.UInt32_To_LE((uint)f0, output, outOff);
  222. f1 += (f0 >> 32);
  223. Pack.UInt32_To_LE((uint)f1, output, outOff + 4);
  224. f2 += (f1 >> 32);
  225. Pack.UInt32_To_LE((uint)f2, output, outOff + 8);
  226. f3 += (f2 >> 32);
  227. Pack.UInt32_To_LE((uint)f3, output, outOff + 12);
  228. Reset();
  229. return BlockSize;
  230. }
  231. public void Reset()
  232. {
  233. currentBlockOffset = 0;
  234. h0 = h1 = h2 = h3 = h4 = 0;
  235. }
  236. private static ulong mul32x32_64(uint i1, uint i2)
  237. {
  238. return ((ulong)i1) * i2;
  239. }
  240. }
  241. }
  242. #pragma warning restore
  243. #endif