Poly1305.cs 10.0 KB

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