KeccakDigest.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. using System;
  3. using Org.BouncyCastle.Utilities;
  4. namespace Org.BouncyCastle.Crypto.Digests
  5. {
  6. /// <summary>
  7. /// Implementation of Keccak based on following KeccakNISTInterface.c from http://keccak.noekeon.org/
  8. /// </summary>
  9. /// <remarks>
  10. /// Following the naming conventions used in the C source code to enable easy review of the implementation.
  11. /// </remarks>
  12. public class KeccakDigest
  13. : IDigest, IMemoable
  14. {
  15. private static readonly ulong[] KeccakRoundConstants = KeccakInitializeRoundConstants();
  16. private static readonly int[] KeccakRhoOffsets = KeccakInitializeRhoOffsets();
  17. private static ulong[] KeccakInitializeRoundConstants()
  18. {
  19. ulong[] keccakRoundConstants = new ulong[24];
  20. byte LFSRState = 0x01;
  21. for (int i = 0; i < 24; i++)
  22. {
  23. keccakRoundConstants[i] = 0;
  24. for (int j = 0; j < 7; j++)
  25. {
  26. int bitPosition = (1 << j) - 1;
  27. // LFSR86540
  28. bool loBit = (LFSRState & 0x01) != 0;
  29. if (loBit)
  30. {
  31. keccakRoundConstants[i] ^= 1UL << bitPosition;
  32. }
  33. bool hiBit = (LFSRState & 0x80) != 0;
  34. LFSRState <<= 1;
  35. if (hiBit)
  36. {
  37. LFSRState ^= 0x71;
  38. }
  39. }
  40. }
  41. return keccakRoundConstants;
  42. }
  43. private static int[] KeccakInitializeRhoOffsets()
  44. {
  45. int[] keccakRhoOffsets = new int[25];
  46. int x, y, t, newX, newY;
  47. int rhoOffset = 0;
  48. keccakRhoOffsets[(((0) % 5) + 5 * ((0) % 5))] = rhoOffset;
  49. x = 1;
  50. y = 0;
  51. for (t = 1; t < 25; t++)
  52. {
  53. //rhoOffset = ((t + 1) * (t + 2) / 2) % 64;
  54. rhoOffset = (rhoOffset + t) & 63;
  55. keccakRhoOffsets[(((x) % 5) + 5 * ((y) % 5))] = rhoOffset;
  56. newX = (0 * x + 1 * y) % 5;
  57. newY = (2 * x + 3 * y) % 5;
  58. x = newX;
  59. y = newY;
  60. }
  61. return keccakRhoOffsets;
  62. }
  63. protected byte[] state = new byte[(1600 / 8)];
  64. protected byte[] dataQueue = new byte[(1536 / 8)];
  65. protected int rate;
  66. protected int bitsInQueue;
  67. protected int fixedOutputLength;
  68. protected bool squeezing;
  69. protected int bitsAvailableForSqueezing;
  70. protected byte[] chunk;
  71. protected byte[] oneByte;
  72. private void ClearDataQueueSection(int off, int len)
  73. {
  74. for (int i = off; i != off + len; i++)
  75. {
  76. dataQueue[i] = 0;
  77. }
  78. }
  79. public KeccakDigest()
  80. : this(288)
  81. {
  82. }
  83. public KeccakDigest(int bitLength)
  84. {
  85. Init(bitLength);
  86. }
  87. public KeccakDigest(KeccakDigest source)
  88. {
  89. CopyIn(source);
  90. }
  91. private void CopyIn(KeccakDigest source)
  92. {
  93. Array.Copy(source.state, 0, this.state, 0, source.state.Length);
  94. Array.Copy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.Length);
  95. this.rate = source.rate;
  96. this.bitsInQueue = source.bitsInQueue;
  97. this.fixedOutputLength = source.fixedOutputLength;
  98. this.squeezing = source.squeezing;
  99. this.bitsAvailableForSqueezing = source.bitsAvailableForSqueezing;
  100. this.chunk = Arrays.Clone(source.chunk);
  101. this.oneByte = Arrays.Clone(source.oneByte);
  102. }
  103. public virtual string AlgorithmName
  104. {
  105. get { return "Keccak-" + fixedOutputLength; }
  106. }
  107. public virtual int GetDigestSize()
  108. {
  109. return fixedOutputLength / 8;
  110. }
  111. public virtual void Update(byte input)
  112. {
  113. oneByte[0] = input;
  114. Absorb(oneByte, 0, 8L);
  115. }
  116. public virtual void BlockUpdate(byte[] input, int inOff, int len)
  117. {
  118. Absorb(input, inOff, len * 8L);
  119. }
  120. public virtual int DoFinal(byte[] output, int outOff)
  121. {
  122. Squeeze(output, outOff, fixedOutputLength);
  123. Reset();
  124. return GetDigestSize();
  125. }
  126. /*
  127. * TODO Possible API change to support partial-byte suffixes.
  128. */
  129. protected virtual int DoFinal(byte[] output, int outOff, byte partialByte, int partialBits)
  130. {
  131. if (partialBits > 0)
  132. {
  133. oneByte[0] = partialByte;
  134. Absorb(oneByte, 0, partialBits);
  135. }
  136. Squeeze(output, outOff, fixedOutputLength);
  137. Reset();
  138. return GetDigestSize();
  139. }
  140. public virtual void Reset()
  141. {
  142. Init(fixedOutputLength);
  143. }
  144. /**
  145. * Return the size of block that the compression function is applied to in bytes.
  146. *
  147. * @return internal byte length of a block.
  148. */
  149. public virtual int GetByteLength()
  150. {
  151. return rate / 8;
  152. }
  153. private void Init(int bitLength)
  154. {
  155. switch (bitLength)
  156. {
  157. case 128:
  158. InitSponge(1344, 256);
  159. break;
  160. case 224:
  161. InitSponge(1152, 448);
  162. break;
  163. case 256:
  164. InitSponge(1088, 512);
  165. break;
  166. case 288:
  167. InitSponge(1024, 576);
  168. break;
  169. case 384:
  170. InitSponge(832, 768);
  171. break;
  172. case 512:
  173. InitSponge(576, 1024);
  174. break;
  175. default:
  176. throw new ArgumentException("must be one of 128, 224, 256, 288, 384, or 512.", "bitLength");
  177. }
  178. }
  179. private void InitSponge(int rate, int capacity)
  180. {
  181. if (rate + capacity != 1600)
  182. {
  183. throw new InvalidOperationException("rate + capacity != 1600");
  184. }
  185. if ((rate <= 0) || (rate >= 1600) || ((rate % 64) != 0))
  186. {
  187. throw new InvalidOperationException("invalid rate value");
  188. }
  189. this.rate = rate;
  190. // this is never read, need to check to see why we want to save it
  191. // this.capacity = capacity;
  192. this.fixedOutputLength = 0;
  193. Arrays.Fill(this.state, (byte)0);
  194. Arrays.Fill(this.dataQueue, (byte)0);
  195. this.bitsInQueue = 0;
  196. this.squeezing = false;
  197. this.bitsAvailableForSqueezing = 0;
  198. this.fixedOutputLength = capacity / 2;
  199. this.chunk = new byte[rate / 8];
  200. this.oneByte = new byte[1];
  201. }
  202. private void AbsorbQueue()
  203. {
  204. KeccakAbsorb(state, dataQueue, rate / 8);
  205. bitsInQueue = 0;
  206. }
  207. protected virtual void Absorb(byte[] data, int off, long databitlen)
  208. {
  209. long i, j, wholeBlocks;
  210. if ((bitsInQueue % 8) != 0)
  211. {
  212. throw new InvalidOperationException("attempt to absorb with odd length queue");
  213. }
  214. if (squeezing)
  215. {
  216. throw new InvalidOperationException("attempt to absorb while squeezing");
  217. }
  218. i = 0;
  219. while (i < databitlen)
  220. {
  221. if ((bitsInQueue == 0) && (databitlen >= rate) && (i <= (databitlen - rate)))
  222. {
  223. wholeBlocks = (databitlen - i) / rate;
  224. for (j = 0; j < wholeBlocks; j++)
  225. {
  226. Array.Copy(data, (int)(off + (i / 8) + (j * chunk.Length)), chunk, 0, chunk.Length);
  227. KeccakAbsorb(state, chunk, chunk.Length);
  228. }
  229. i += wholeBlocks * rate;
  230. }
  231. else
  232. {
  233. int partialBlock = (int)(databitlen - i);
  234. if (partialBlock + bitsInQueue > rate)
  235. {
  236. partialBlock = rate - bitsInQueue;
  237. }
  238. int partialByte = partialBlock % 8;
  239. partialBlock -= partialByte;
  240. Array.Copy(data, off + (int)(i / 8), dataQueue, bitsInQueue / 8, partialBlock / 8);
  241. bitsInQueue += partialBlock;
  242. i += partialBlock;
  243. if (bitsInQueue == rate)
  244. {
  245. AbsorbQueue();
  246. }
  247. if (partialByte > 0)
  248. {
  249. int mask = (1 << partialByte) - 1;
  250. dataQueue[bitsInQueue / 8] = (byte)(data[off + ((int)(i / 8))] & mask);
  251. bitsInQueue += partialByte;
  252. i += partialByte;
  253. }
  254. }
  255. }
  256. }
  257. private void PadAndSwitchToSqueezingPhase()
  258. {
  259. if (bitsInQueue + 1 == rate)
  260. {
  261. dataQueue[bitsInQueue / 8] |= (byte)(1U << (bitsInQueue % 8));
  262. AbsorbQueue();
  263. ClearDataQueueSection(0, rate / 8);
  264. }
  265. else
  266. {
  267. ClearDataQueueSection((bitsInQueue + 7) / 8, rate / 8 - (bitsInQueue + 7) / 8);
  268. dataQueue[bitsInQueue / 8] |= (byte)(1U << (bitsInQueue % 8));
  269. }
  270. dataQueue[(rate - 1) / 8] |= (byte)(1U << ((rate - 1) % 8));
  271. AbsorbQueue();
  272. if (rate == 1024)
  273. {
  274. KeccakExtract1024bits(state, dataQueue);
  275. bitsAvailableForSqueezing = 1024;
  276. }
  277. else
  278. {
  279. KeccakExtract(state, dataQueue, rate / 64);
  280. bitsAvailableForSqueezing = rate;
  281. }
  282. squeezing = true;
  283. }
  284. protected virtual void Squeeze(byte[] output, int offset, long outputLength)
  285. {
  286. long i;
  287. int partialBlock;
  288. if (!squeezing)
  289. {
  290. PadAndSwitchToSqueezingPhase();
  291. }
  292. if ((outputLength % 8) != 0)
  293. {
  294. throw new InvalidOperationException("outputLength not a multiple of 8");
  295. }
  296. i = 0;
  297. while (i < outputLength)
  298. {
  299. if (bitsAvailableForSqueezing == 0)
  300. {
  301. KeccakPermutation(state);
  302. if (rate == 1024)
  303. {
  304. KeccakExtract1024bits(state, dataQueue);
  305. bitsAvailableForSqueezing = 1024;
  306. }
  307. else
  308. {
  309. KeccakExtract(state, dataQueue, rate / 64);
  310. bitsAvailableForSqueezing = rate;
  311. }
  312. }
  313. partialBlock = bitsAvailableForSqueezing;
  314. if ((long)partialBlock > outputLength - i)
  315. {
  316. partialBlock = (int)(outputLength - i);
  317. }
  318. Array.Copy(dataQueue, (rate - bitsAvailableForSqueezing) / 8, output, offset + (int)(i / 8), partialBlock / 8);
  319. bitsAvailableForSqueezing -= partialBlock;
  320. i += partialBlock;
  321. }
  322. }
  323. private static void FromBytesToWords(ulong[] stateAsWords, byte[] state)
  324. {
  325. for (int i = 0; i < (1600 / 64); i++)
  326. {
  327. stateAsWords[i] = 0;
  328. int index = i * (64 / 8);
  329. for (int j = 0; j < (64 / 8); j++)
  330. {
  331. stateAsWords[i] |= ((ulong)state[index + j] & 0xff) << ((8 * j));
  332. }
  333. }
  334. }
  335. private static void FromWordsToBytes(byte[] state, ulong[] stateAsWords)
  336. {
  337. for (int i = 0; i < (1600 / 64); i++)
  338. {
  339. int index = i * (64 / 8);
  340. for (int j = 0; j < (64 / 8); j++)
  341. {
  342. state[index + j] = (byte)(stateAsWords[i] >> (8 * j));
  343. }
  344. }
  345. }
  346. private void KeccakPermutation(byte[] state)
  347. {
  348. ulong[] longState = new ulong[state.Length / 8];
  349. FromBytesToWords(longState, state);
  350. KeccakPermutationOnWords(longState);
  351. FromWordsToBytes(state, longState);
  352. }
  353. private void KeccakPermutationAfterXor(byte[] state, byte[] data, int dataLengthInBytes)
  354. {
  355. for (int i = 0; i < dataLengthInBytes; i++)
  356. {
  357. state[i] ^= data[i];
  358. }
  359. KeccakPermutation(state);
  360. }
  361. private void KeccakPermutationOnWords(ulong[] state)
  362. {
  363. int i;
  364. for (i = 0; i < 24; i++)
  365. {
  366. Theta(state);
  367. Rho(state);
  368. Pi(state);
  369. Chi(state);
  370. Iota(state, i);
  371. }
  372. }
  373. ulong[] C = new ulong[5];
  374. private void Theta(ulong[] A)
  375. {
  376. for (int x = 0; x < 5; x++)
  377. {
  378. C[x] = 0;
  379. for (int y = 0; y < 5; y++)
  380. {
  381. C[x] ^= A[x + 5 * y];
  382. }
  383. }
  384. for (int x = 0; x < 5; x++)
  385. {
  386. ulong dX = ((((C[(x + 1) % 5]) << 1) ^ ((C[(x + 1) % 5]) >> (64 - 1)))) ^ C[(x + 4) % 5];
  387. for (int y = 0; y < 5; y++)
  388. {
  389. A[x + 5 * y] ^= dX;
  390. }
  391. }
  392. }
  393. private void Rho(ulong[] A)
  394. {
  395. for (int x = 0; x < 5; x++)
  396. {
  397. for (int y = 0; y < 5; y++)
  398. {
  399. int index = x + 5 * y;
  400. A[index] = ((KeccakRhoOffsets[index] != 0) ? (((A[index]) << KeccakRhoOffsets[index]) ^ ((A[index]) >> (64 - KeccakRhoOffsets[index]))) : A[index]);
  401. }
  402. }
  403. }
  404. ulong[] tempA = new ulong[25];
  405. private void Pi(ulong[] A)
  406. {
  407. Array.Copy(A, 0, tempA, 0, tempA.Length);
  408. for (int x = 0; x < 5; x++)
  409. {
  410. for (int y = 0; y < 5; y++)
  411. {
  412. A[y + 5 * ((2 * x + 3 * y) % 5)] = tempA[x + 5 * y];
  413. }
  414. }
  415. }
  416. ulong[] chiC = new ulong[5];
  417. private void Chi(ulong[] A)
  418. {
  419. for (int y = 0; y < 5; y++)
  420. {
  421. for (int x = 0; x < 5; x++)
  422. {
  423. chiC[x] = A[x + 5 * y] ^ ((~A[(((x + 1) % 5) + 5 * y)]) & A[(((x + 2) % 5) + 5 * y)]);
  424. }
  425. for (int x = 0; x < 5; x++)
  426. {
  427. A[x + 5 * y] = chiC[x];
  428. }
  429. }
  430. }
  431. private static void Iota(ulong[] A, int indexRound)
  432. {
  433. A[(((0) % 5) + 5 * ((0) % 5))] ^= KeccakRoundConstants[indexRound];
  434. }
  435. private void KeccakAbsorb(byte[] byteState, byte[] data, int dataInBytes)
  436. {
  437. KeccakPermutationAfterXor(byteState, data, dataInBytes);
  438. }
  439. private void KeccakExtract1024bits(byte[] byteState, byte[] data)
  440. {
  441. Array.Copy(byteState, 0, data, 0, 128);
  442. }
  443. private void KeccakExtract(byte[] byteState, byte[] data, int laneCount)
  444. {
  445. Array.Copy(byteState, 0, data, 0, laneCount * 8);
  446. }
  447. public virtual IMemoable Copy()
  448. {
  449. return new KeccakDigest(this);
  450. }
  451. public virtual void Reset(IMemoable other)
  452. {
  453. KeccakDigest d = (KeccakDigest)other;
  454. CopyIn(d);
  455. }
  456. }
  457. }
  458. #endif