KeccakDigest.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Diagnostics;
  5. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  7. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Digests
  8. {
  9. /// <summary>
  10. /// Implementation of Keccak based on following KeccakNISTInterface.c from http://keccak.noekeon.org/
  11. /// </summary>
  12. /// <remarks>
  13. /// Following the naming conventions used in the C source code to enable easy review of the implementation.
  14. /// </remarks>
  15. public class KeccakDigest
  16. : IDigest, IMemoable
  17. {
  18. private static readonly ulong[] KeccakRoundConstants = new ulong[]{
  19. 0x0000000000000001UL, 0x0000000000008082UL, 0x800000000000808aUL, 0x8000000080008000UL,
  20. 0x000000000000808bUL, 0x0000000080000001UL, 0x8000000080008081UL, 0x8000000000008009UL,
  21. 0x000000000000008aUL, 0x0000000000000088UL, 0x0000000080008009UL, 0x000000008000000aUL,
  22. 0x000000008000808bUL, 0x800000000000008bUL, 0x8000000000008089UL, 0x8000000000008003UL,
  23. 0x8000000000008002UL, 0x8000000000000080UL, 0x000000000000800aUL, 0x800000008000000aUL,
  24. 0x8000000080008081UL, 0x8000000000008080UL, 0x0000000080000001UL, 0x8000000080008008UL
  25. };
  26. private ulong[] state = new ulong[25];
  27. protected byte[] dataQueue = new byte[192];
  28. protected int rate;
  29. protected int bitsInQueue;
  30. protected internal int fixedOutputLength;
  31. protected bool squeezing;
  32. public KeccakDigest()
  33. : this(288)
  34. {
  35. }
  36. public KeccakDigest(int bitLength)
  37. {
  38. Init(bitLength);
  39. }
  40. public KeccakDigest(KeccakDigest source)
  41. {
  42. CopyIn(source);
  43. }
  44. private void CopyIn(KeccakDigest source)
  45. {
  46. Array.Copy(source.state, 0, this.state, 0, source.state.Length);
  47. Array.Copy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.Length);
  48. this.rate = source.rate;
  49. this.bitsInQueue = source.bitsInQueue;
  50. this.fixedOutputLength = source.fixedOutputLength;
  51. this.squeezing = source.squeezing;
  52. }
  53. public virtual string AlgorithmName
  54. {
  55. get { return "Keccak-" + fixedOutputLength; }
  56. }
  57. public virtual int GetDigestSize()
  58. {
  59. return fixedOutputLength >> 3;
  60. }
  61. public virtual void Update(byte input)
  62. {
  63. Absorb(input);
  64. }
  65. public virtual void BlockUpdate(byte[] input, int inOff, int len)
  66. {
  67. Absorb(input, inOff, len);
  68. }
  69. public virtual int DoFinal(byte[] output, int outOff)
  70. {
  71. Squeeze(output, outOff, fixedOutputLength);
  72. Reset();
  73. return GetDigestSize();
  74. }
  75. /*
  76. * TODO Possible API change to support partial-byte suffixes.
  77. */
  78. protected virtual int DoFinal(byte[] output, int outOff, byte partialByte, int partialBits)
  79. {
  80. if (partialBits > 0)
  81. {
  82. AbsorbBits(partialByte, partialBits);
  83. }
  84. Squeeze(output, outOff, fixedOutputLength);
  85. Reset();
  86. return GetDigestSize();
  87. }
  88. public virtual void Reset()
  89. {
  90. Init(fixedOutputLength);
  91. }
  92. /**
  93. * Return the size of block that the compression function is applied to in bytes.
  94. *
  95. * @return internal byte length of a block.
  96. */
  97. public virtual int GetByteLength()
  98. {
  99. return rate >> 3;
  100. }
  101. private void Init(int bitLength)
  102. {
  103. switch (bitLength)
  104. {
  105. case 128:
  106. case 224:
  107. case 256:
  108. case 288:
  109. case 384:
  110. case 512:
  111. InitSponge(1600 - (bitLength << 1));
  112. break;
  113. default:
  114. throw new ArgumentException("must be one of 128, 224, 256, 288, 384, or 512.", "bitLength");
  115. }
  116. }
  117. private void InitSponge(int rate)
  118. {
  119. if (rate <= 0 || rate >= 1600 || (rate & 63) != 0)
  120. throw new InvalidOperationException("invalid rate value");
  121. this.rate = rate;
  122. Array.Clear(state, 0, state.Length);
  123. Arrays.Fill(this.dataQueue, (byte)0);
  124. this.bitsInQueue = 0;
  125. this.squeezing = false;
  126. this.fixedOutputLength = (1600 - rate) >> 1;
  127. }
  128. protected void Absorb(byte data)
  129. {
  130. if ((bitsInQueue & 7) != 0)
  131. throw new InvalidOperationException("attempt to absorb with odd length queue");
  132. if (squeezing)
  133. throw new InvalidOperationException("attempt to absorb while squeezing");
  134. dataQueue[bitsInQueue >> 3] = data;
  135. if ((bitsInQueue += 8) == rate)
  136. {
  137. KeccakAbsorb(dataQueue, 0);
  138. bitsInQueue = 0;
  139. }
  140. }
  141. protected void Absorb(byte[] data, int off, int len)
  142. {
  143. if ((bitsInQueue & 7) != 0)
  144. throw new InvalidOperationException("attempt to absorb with odd length queue");
  145. if (squeezing)
  146. throw new InvalidOperationException("attempt to absorb while squeezing");
  147. int bytesInQueue = bitsInQueue >> 3;
  148. int rateBytes = rate >> 3;
  149. int available = rateBytes - bytesInQueue;
  150. if (len < available)
  151. {
  152. Array.Copy(data, off, dataQueue, bytesInQueue, len);
  153. this.bitsInQueue += len << 3;
  154. return;
  155. }
  156. int count = 0;
  157. if (bytesInQueue > 0)
  158. {
  159. Array.Copy(data, off, dataQueue, bytesInQueue, available);
  160. count += available;
  161. KeccakAbsorb(dataQueue, 0);
  162. }
  163. int remaining;
  164. while ((remaining = (len - count)) >= rateBytes)
  165. {
  166. KeccakAbsorb(data, off + count);
  167. count += rateBytes;
  168. }
  169. Array.Copy(data, off + count, dataQueue, 0, remaining);
  170. this.bitsInQueue = remaining << 3;
  171. }
  172. protected void AbsorbBits(int data, int bits)
  173. {
  174. if (bits < 1 || bits > 7)
  175. throw new ArgumentException("must be in the range 1 to 7", "bits");
  176. if ((bitsInQueue & 7) != 0)
  177. throw new InvalidOperationException("attempt to absorb with odd length queue");
  178. if (squeezing)
  179. throw new InvalidOperationException("attempt to absorb while squeezing");
  180. int mask = (1 << bits) - 1;
  181. dataQueue[bitsInQueue >> 3] = (byte)(data & mask);
  182. // NOTE: After this, bitsInQueue is no longer a multiple of 8, so no more absorbs will work
  183. bitsInQueue += bits;
  184. }
  185. private void PadAndSwitchToSqueezingPhase()
  186. {
  187. Debug.Assert(bitsInQueue < rate);
  188. dataQueue[bitsInQueue >> 3] |= (byte)(1 << (bitsInQueue & 7));
  189. if (++bitsInQueue == rate)
  190. {
  191. KeccakAbsorb(dataQueue, 0);
  192. }
  193. else
  194. {
  195. int full = bitsInQueue >> 6, partial = bitsInQueue & 63;
  196. int off = 0;
  197. for (int i = 0; i < full; ++i)
  198. {
  199. state[i] ^= Pack.LE_To_UInt64(dataQueue, off);
  200. off += 8;
  201. }
  202. if (partial > 0)
  203. {
  204. ulong mask = (1UL << partial) - 1UL;
  205. state[full] ^= Pack.LE_To_UInt64(dataQueue, off) & mask;
  206. }
  207. }
  208. state[(rate - 1) >> 6] ^= (1UL << 63);
  209. bitsInQueue = 0;
  210. squeezing = true;
  211. }
  212. protected void Squeeze(byte[] output, int offset, long outputLength)
  213. {
  214. if (!squeezing)
  215. {
  216. PadAndSwitchToSqueezingPhase();
  217. }
  218. if ((outputLength & 7L) != 0L)
  219. throw new InvalidOperationException("outputLength not a multiple of 8");
  220. long i = 0;
  221. while (i < outputLength)
  222. {
  223. if (bitsInQueue == 0)
  224. {
  225. KeccakExtract();
  226. }
  227. int partialBlock = (int)System.Math.Min((long)bitsInQueue, outputLength - i);
  228. Array.Copy(dataQueue, (rate - bitsInQueue) >> 3, output, offset + (int)(i >> 3), partialBlock >> 3);
  229. bitsInQueue -= partialBlock;
  230. i += partialBlock;
  231. }
  232. }
  233. private void KeccakAbsorb(byte[] data, int off)
  234. {
  235. int count = rate >> 6;
  236. for (int i = 0; i < count; ++i)
  237. {
  238. state[i] ^= Pack.LE_To_UInt64(data, off);
  239. off += 8;
  240. }
  241. KeccakPermutation();
  242. }
  243. private void KeccakExtract()
  244. {
  245. KeccakPermutation();
  246. Pack.UInt64_To_LE(state, 0, rate >> 6, dataQueue, 0);
  247. this.bitsInQueue = rate;
  248. }
  249. private void KeccakPermutation()
  250. {
  251. ulong[] A = state;
  252. ulong a00 = A[ 0], a01 = A[ 1], a02 = A[ 2], a03 = A[ 3], a04 = A[ 4];
  253. ulong a05 = A[ 5], a06 = A[ 6], a07 = A[ 7], a08 = A[ 8], a09 = A[ 9];
  254. ulong a10 = A[10], a11 = A[11], a12 = A[12], a13 = A[13], a14 = A[14];
  255. ulong a15 = A[15], a16 = A[16], a17 = A[17], a18 = A[18], a19 = A[19];
  256. ulong a20 = A[20], a21 = A[21], a22 = A[22], a23 = A[23], a24 = A[24];
  257. for (int i = 0; i < 24; i++)
  258. {
  259. // theta
  260. ulong c0 = a00 ^ a05 ^ a10 ^ a15 ^ a20;
  261. ulong c1 = a01 ^ a06 ^ a11 ^ a16 ^ a21;
  262. ulong c2 = a02 ^ a07 ^ a12 ^ a17 ^ a22;
  263. ulong c3 = a03 ^ a08 ^ a13 ^ a18 ^ a23;
  264. ulong c4 = a04 ^ a09 ^ a14 ^ a19 ^ a24;
  265. ulong d1 = (c1 << 1 | c1 >> -1) ^ c4;
  266. ulong d2 = (c2 << 1 | c2 >> -1) ^ c0;
  267. ulong d3 = (c3 << 1 | c3 >> -1) ^ c1;
  268. ulong d4 = (c4 << 1 | c4 >> -1) ^ c2;
  269. ulong d0 = (c0 << 1 | c0 >> -1) ^ c3;
  270. a00 ^= d1; a05 ^= d1; a10 ^= d1; a15 ^= d1; a20 ^= d1;
  271. a01 ^= d2; a06 ^= d2; a11 ^= d2; a16 ^= d2; a21 ^= d2;
  272. a02 ^= d3; a07 ^= d3; a12 ^= d3; a17 ^= d3; a22 ^= d3;
  273. a03 ^= d4; a08 ^= d4; a13 ^= d4; a18 ^= d4; a23 ^= d4;
  274. a04 ^= d0; a09 ^= d0; a14 ^= d0; a19 ^= d0; a24 ^= d0;
  275. // rho/pi
  276. c1 = a01 << 1 | a01 >> 63;
  277. a01 = a06 << 44 | a06 >> 20;
  278. a06 = a09 << 20 | a09 >> 44;
  279. a09 = a22 << 61 | a22 >> 3;
  280. a22 = a14 << 39 | a14 >> 25;
  281. a14 = a20 << 18 | a20 >> 46;
  282. a20 = a02 << 62 | a02 >> 2;
  283. a02 = a12 << 43 | a12 >> 21;
  284. a12 = a13 << 25 | a13 >> 39;
  285. a13 = a19 << 8 | a19 >> 56;
  286. a19 = a23 << 56 | a23 >> 8;
  287. a23 = a15 << 41 | a15 >> 23;
  288. a15 = a04 << 27 | a04 >> 37;
  289. a04 = a24 << 14 | a24 >> 50;
  290. a24 = a21 << 2 | a21 >> 62;
  291. a21 = a08 << 55 | a08 >> 9;
  292. a08 = a16 << 45 | a16 >> 19;
  293. a16 = a05 << 36 | a05 >> 28;
  294. a05 = a03 << 28 | a03 >> 36;
  295. a03 = a18 << 21 | a18 >> 43;
  296. a18 = a17 << 15 | a17 >> 49;
  297. a17 = a11 << 10 | a11 >> 54;
  298. a11 = a07 << 6 | a07 >> 58;
  299. a07 = a10 << 3 | a10 >> 61;
  300. a10 = c1;
  301. // chi
  302. c0 = a00 ^ (~a01 & a02);
  303. c1 = a01 ^ (~a02 & a03);
  304. a02 ^= ~a03 & a04;
  305. a03 ^= ~a04 & a00;
  306. a04 ^= ~a00 & a01;
  307. a00 = c0;
  308. a01 = c1;
  309. c0 = a05 ^ (~a06 & a07);
  310. c1 = a06 ^ (~a07 & a08);
  311. a07 ^= ~a08 & a09;
  312. a08 ^= ~a09 & a05;
  313. a09 ^= ~a05 & a06;
  314. a05 = c0;
  315. a06 = c1;
  316. c0 = a10 ^ (~a11 & a12);
  317. c1 = a11 ^ (~a12 & a13);
  318. a12 ^= ~a13 & a14;
  319. a13 ^= ~a14 & a10;
  320. a14 ^= ~a10 & a11;
  321. a10 = c0;
  322. a11 = c1;
  323. c0 = a15 ^ (~a16 & a17);
  324. c1 = a16 ^ (~a17 & a18);
  325. a17 ^= ~a18 & a19;
  326. a18 ^= ~a19 & a15;
  327. a19 ^= ~a15 & a16;
  328. a15 = c0;
  329. a16 = c1;
  330. c0 = a20 ^ (~a21 & a22);
  331. c1 = a21 ^ (~a22 & a23);
  332. a22 ^= ~a23 & a24;
  333. a23 ^= ~a24 & a20;
  334. a24 ^= ~a20 & a21;
  335. a20 = c0;
  336. a21 = c1;
  337. // iota
  338. a00 ^= KeccakRoundConstants[i];
  339. }
  340. A[ 0] = a00; A[ 1] = a01; A[ 2] = a02; A[ 3] = a03; A[ 4] = a04;
  341. A[ 5] = a05; A[ 6] = a06; A[ 7] = a07; A[ 8] = a08; A[ 9] = a09;
  342. A[10] = a10; A[11] = a11; A[12] = a12; A[13] = a13; A[14] = a14;
  343. A[15] = a15; A[16] = a16; A[17] = a17; A[18] = a18; A[19] = a19;
  344. A[20] = a20; A[21] = a21; A[22] = a22; A[23] = a23; A[24] = a24;
  345. }
  346. public virtual IMemoable Copy()
  347. {
  348. return new KeccakDigest(this);
  349. }
  350. public virtual void Reset(IMemoable other)
  351. {
  352. CopyIn((KeccakDigest)other);
  353. }
  354. }
  355. }
  356. #pragma warning restore
  357. #endif