KeccakDigest.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Diagnostics;
  5. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
  6. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  7. namespace Best.HTTP.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. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  70. public virtual void BlockUpdate(ReadOnlySpan<byte> input)
  71. {
  72. Absorb(input);
  73. }
  74. #endif
  75. public virtual int DoFinal(byte[] output, int outOff)
  76. {
  77. Squeeze(output, outOff, fixedOutputLength);
  78. Reset();
  79. return GetDigestSize();
  80. }
  81. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  82. public virtual int DoFinal(Span<byte> output)
  83. {
  84. int digestSize = GetDigestSize();
  85. Squeeze(output[..digestSize]);
  86. Reset();
  87. return digestSize;
  88. }
  89. #endif
  90. /*
  91. * TODO Possible API change to support partial-byte suffixes.
  92. */
  93. protected virtual int DoFinal(byte[] output, int outOff, byte partialByte, int partialBits)
  94. {
  95. if (partialBits > 0)
  96. {
  97. AbsorbBits(partialByte, partialBits);
  98. }
  99. Squeeze(output, outOff, fixedOutputLength);
  100. Reset();
  101. return GetDigestSize();
  102. }
  103. public virtual void Reset()
  104. {
  105. Init(fixedOutputLength);
  106. }
  107. /**
  108. * Return the size of block that the compression function is applied to in bytes.
  109. *
  110. * @return internal byte length of a block.
  111. */
  112. public virtual int GetByteLength()
  113. {
  114. return rate >> 3;
  115. }
  116. private void Init(int bitLength)
  117. {
  118. switch (bitLength)
  119. {
  120. case 128:
  121. case 224:
  122. case 256:
  123. case 288:
  124. case 384:
  125. case 512:
  126. InitSponge(1600 - (bitLength << 1));
  127. break;
  128. default:
  129. throw new ArgumentException("must be one of 128, 224, 256, 288, 384, or 512.", "bitLength");
  130. }
  131. }
  132. private void InitSponge(int rate)
  133. {
  134. if (rate <= 0 || rate >= 1600 || (rate & 63) != 0)
  135. throw new InvalidOperationException("invalid rate value");
  136. this.rate = rate;
  137. Array.Clear(state, 0, state.Length);
  138. Arrays.Fill(this.dataQueue, (byte)0);
  139. this.bitsInQueue = 0;
  140. this.squeezing = false;
  141. this.fixedOutputLength = (1600 - rate) >> 1;
  142. }
  143. protected void Absorb(byte data)
  144. {
  145. if ((bitsInQueue & 7) != 0)
  146. throw new InvalidOperationException("attempt to absorb with odd length queue");
  147. if (squeezing)
  148. throw new InvalidOperationException("attempt to absorb while squeezing");
  149. dataQueue[bitsInQueue >> 3] = data;
  150. if ((bitsInQueue += 8) == rate)
  151. {
  152. KeccakAbsorb(dataQueue, 0);
  153. bitsInQueue = 0;
  154. }
  155. }
  156. protected void Absorb(byte[] data, int off, int len)
  157. {
  158. if ((bitsInQueue & 7) != 0)
  159. throw new InvalidOperationException("attempt to absorb with odd length queue");
  160. if (squeezing)
  161. throw new InvalidOperationException("attempt to absorb while squeezing");
  162. int bytesInQueue = bitsInQueue >> 3;
  163. int rateBytes = rate >> 3;
  164. int available = rateBytes - bytesInQueue;
  165. if (len < available)
  166. {
  167. Array.Copy(data, off, dataQueue, bytesInQueue, len);
  168. this.bitsInQueue += len << 3;
  169. return;
  170. }
  171. int count = 0;
  172. if (bytesInQueue > 0)
  173. {
  174. Array.Copy(data, off, dataQueue, bytesInQueue, available);
  175. count += available;
  176. KeccakAbsorb(dataQueue, 0);
  177. }
  178. int remaining;
  179. while ((remaining = (len - count)) >= rateBytes)
  180. {
  181. KeccakAbsorb(data, off + count);
  182. count += rateBytes;
  183. }
  184. Array.Copy(data, off + count, dataQueue, 0, remaining);
  185. this.bitsInQueue = remaining << 3;
  186. }
  187. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  188. protected void Absorb(ReadOnlySpan<byte> data)
  189. {
  190. if ((bitsInQueue & 7) != 0)
  191. throw new InvalidOperationException("attempt to absorb with odd length queue");
  192. if (squeezing)
  193. throw new InvalidOperationException("attempt to absorb while squeezing");
  194. int bytesInQueue = bitsInQueue >> 3;
  195. int rateBytes = rate >> 3;
  196. int len = data.Length;
  197. int available = rateBytes - bytesInQueue;
  198. if (len < available)
  199. {
  200. data.CopyTo(dataQueue.AsSpan(bytesInQueue));
  201. this.bitsInQueue += len << 3;
  202. return;
  203. }
  204. int count = 0;
  205. if (bytesInQueue > 0)
  206. {
  207. data[..available].CopyTo(dataQueue.AsSpan(bytesInQueue));
  208. count += available;
  209. KeccakAbsorb(dataQueue, 0);
  210. }
  211. int remaining;
  212. while ((remaining = len - count) >= rateBytes)
  213. {
  214. KeccakAbsorb(data[count..]);
  215. count += rateBytes;
  216. }
  217. data[count..].CopyTo(dataQueue.AsSpan());
  218. this.bitsInQueue = remaining << 3;
  219. }
  220. #endif
  221. protected void AbsorbBits(int data, int bits)
  222. {
  223. if (bits < 1 || bits > 7)
  224. throw new ArgumentException("must be in the range 1 to 7", "bits");
  225. if ((bitsInQueue & 7) != 0)
  226. throw new InvalidOperationException("attempt to absorb with odd length queue");
  227. if (squeezing)
  228. throw new InvalidOperationException("attempt to absorb while squeezing");
  229. int mask = (1 << bits) - 1;
  230. dataQueue[bitsInQueue >> 3] = (byte)(data & mask);
  231. // NOTE: After this, bitsInQueue is no longer a multiple of 8, so no more absorbs will work
  232. bitsInQueue += bits;
  233. }
  234. private void PadAndSwitchToSqueezingPhase()
  235. {
  236. Debug.Assert(bitsInQueue < rate);
  237. dataQueue[bitsInQueue >> 3] |= (byte)(1 << (bitsInQueue & 7));
  238. if (++bitsInQueue == rate)
  239. {
  240. KeccakAbsorb(dataQueue, 0);
  241. }
  242. else
  243. {
  244. int full = bitsInQueue >> 6, partial = bitsInQueue & 63;
  245. int off = 0;
  246. for (int i = 0; i < full; ++i)
  247. {
  248. state[i] ^= Pack.LE_To_UInt64(dataQueue, off);
  249. off += 8;
  250. }
  251. if (partial > 0)
  252. {
  253. ulong mask = (1UL << partial) - 1UL;
  254. state[full] ^= Pack.LE_To_UInt64(dataQueue, off) & mask;
  255. }
  256. }
  257. state[(rate - 1) >> 6] ^= (1UL << 63);
  258. bitsInQueue = 0;
  259. squeezing = true;
  260. }
  261. protected void Squeeze(byte[] output, int offset, long outputLength)
  262. {
  263. if (!squeezing)
  264. {
  265. PadAndSwitchToSqueezingPhase();
  266. }
  267. if ((outputLength & 7L) != 0L)
  268. throw new InvalidOperationException("outputLength not a multiple of 8");
  269. long i = 0;
  270. while (i < outputLength)
  271. {
  272. if (bitsInQueue == 0)
  273. {
  274. KeccakExtract();
  275. }
  276. int partialBlock = (int)System.Math.Min((long)bitsInQueue, outputLength - i);
  277. Array.Copy(dataQueue, (rate - bitsInQueue) >> 3, output, offset + (int)(i >> 3), partialBlock >> 3);
  278. bitsInQueue -= partialBlock;
  279. i += partialBlock;
  280. }
  281. }
  282. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  283. protected void Squeeze(Span<byte> output)
  284. {
  285. if (!squeezing)
  286. {
  287. PadAndSwitchToSqueezingPhase();
  288. }
  289. long outputLength = (long)output.Length << 3;
  290. long i = 0;
  291. while (i < outputLength)
  292. {
  293. if (bitsInQueue == 0)
  294. {
  295. KeccakExtract();
  296. }
  297. int partialBlock = (int)System.Math.Min(bitsInQueue, outputLength - i);
  298. dataQueue.AsSpan((rate - bitsInQueue) >> 3, partialBlock >> 3).CopyTo(output[(int)(i >> 3)..]);
  299. bitsInQueue -= partialBlock;
  300. i += partialBlock;
  301. }
  302. }
  303. #endif
  304. private void KeccakAbsorb(byte[] data, int off)
  305. {
  306. int count = rate >> 6;
  307. for (int i = 0; i < count; ++i)
  308. {
  309. state[i] ^= Pack.LE_To_UInt64(data, off);
  310. off += 8;
  311. }
  312. KeccakPermutation();
  313. }
  314. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  315. private void KeccakAbsorb(ReadOnlySpan<byte> data)
  316. {
  317. int count = rate >> 6, off = 0;
  318. for (int i = 0; i < count; ++i)
  319. {
  320. state[i] ^= Pack.LE_To_UInt64(data[off..]);
  321. off += 8;
  322. }
  323. KeccakPermutation();
  324. }
  325. #endif
  326. private void KeccakExtract()
  327. {
  328. KeccakPermutation();
  329. Pack.UInt64_To_LE(state, 0, rate >> 6, dataQueue, 0);
  330. this.bitsInQueue = rate;
  331. }
  332. private void KeccakPermutation()
  333. {
  334. ulong[] A = state;
  335. ulong a00 = A[ 0], a01 = A[ 1], a02 = A[ 2], a03 = A[ 3], a04 = A[ 4];
  336. ulong a05 = A[ 5], a06 = A[ 6], a07 = A[ 7], a08 = A[ 8], a09 = A[ 9];
  337. ulong a10 = A[10], a11 = A[11], a12 = A[12], a13 = A[13], a14 = A[14];
  338. ulong a15 = A[15], a16 = A[16], a17 = A[17], a18 = A[18], a19 = A[19];
  339. ulong a20 = A[20], a21 = A[21], a22 = A[22], a23 = A[23], a24 = A[24];
  340. for (int i = 0; i < 24; i++)
  341. {
  342. // theta
  343. ulong c0 = a00 ^ a05 ^ a10 ^ a15 ^ a20;
  344. ulong c1 = a01 ^ a06 ^ a11 ^ a16 ^ a21;
  345. ulong c2 = a02 ^ a07 ^ a12 ^ a17 ^ a22;
  346. ulong c3 = a03 ^ a08 ^ a13 ^ a18 ^ a23;
  347. ulong c4 = a04 ^ a09 ^ a14 ^ a19 ^ a24;
  348. ulong d1 = Longs.RotateLeft(c1, 1) ^ c4;
  349. ulong d2 = Longs.RotateLeft(c2, 1) ^ c0;
  350. ulong d3 = Longs.RotateLeft(c3, 1) ^ c1;
  351. ulong d4 = Longs.RotateLeft(c4, 1) ^ c2;
  352. ulong d0 = Longs.RotateLeft(c0, 1) ^ c3;
  353. a00 ^= d1; a05 ^= d1; a10 ^= d1; a15 ^= d1; a20 ^= d1;
  354. a01 ^= d2; a06 ^= d2; a11 ^= d2; a16 ^= d2; a21 ^= d2;
  355. a02 ^= d3; a07 ^= d3; a12 ^= d3; a17 ^= d3; a22 ^= d3;
  356. a03 ^= d4; a08 ^= d4; a13 ^= d4; a18 ^= d4; a23 ^= d4;
  357. a04 ^= d0; a09 ^= d0; a14 ^= d0; a19 ^= d0; a24 ^= d0;
  358. // rho/pi
  359. c1 = Longs.RotateLeft(a01, 1);
  360. a01 = Longs.RotateLeft(a06, 44);
  361. a06 = Longs.RotateLeft(a09, 20);
  362. a09 = Longs.RotateLeft(a22, 61);
  363. a22 = Longs.RotateLeft(a14, 39);
  364. a14 = Longs.RotateLeft(a20, 18);
  365. a20 = Longs.RotateLeft(a02, 62);
  366. a02 = Longs.RotateLeft(a12, 43);
  367. a12 = Longs.RotateLeft(a13, 25);
  368. a13 = Longs.RotateLeft(a19, 8);
  369. a19 = Longs.RotateLeft(a23, 56);
  370. a23 = Longs.RotateLeft(a15, 41);
  371. a15 = Longs.RotateLeft(a04, 27);
  372. a04 = Longs.RotateLeft(a24, 14);
  373. a24 = Longs.RotateLeft(a21, 2);
  374. a21 = Longs.RotateLeft(a08, 55);
  375. a08 = Longs.RotateLeft(a16, 45);
  376. a16 = Longs.RotateLeft(a05, 36);
  377. a05 = Longs.RotateLeft(a03, 28);
  378. a03 = Longs.RotateLeft(a18, 21);
  379. a18 = Longs.RotateLeft(a17, 15);
  380. a17 = Longs.RotateLeft(a11, 10);
  381. a11 = Longs.RotateLeft(a07, 6);
  382. a07 = Longs.RotateLeft(a10, 3);
  383. a10 = c1;
  384. // chi
  385. c0 = a00 ^ (~a01 & a02);
  386. c1 = a01 ^ (~a02 & a03);
  387. a02 ^= ~a03 & a04;
  388. a03 ^= ~a04 & a00;
  389. a04 ^= ~a00 & a01;
  390. a00 = c0;
  391. a01 = c1;
  392. c0 = a05 ^ (~a06 & a07);
  393. c1 = a06 ^ (~a07 & a08);
  394. a07 ^= ~a08 & a09;
  395. a08 ^= ~a09 & a05;
  396. a09 ^= ~a05 & a06;
  397. a05 = c0;
  398. a06 = c1;
  399. c0 = a10 ^ (~a11 & a12);
  400. c1 = a11 ^ (~a12 & a13);
  401. a12 ^= ~a13 & a14;
  402. a13 ^= ~a14 & a10;
  403. a14 ^= ~a10 & a11;
  404. a10 = c0;
  405. a11 = c1;
  406. c0 = a15 ^ (~a16 & a17);
  407. c1 = a16 ^ (~a17 & a18);
  408. a17 ^= ~a18 & a19;
  409. a18 ^= ~a19 & a15;
  410. a19 ^= ~a15 & a16;
  411. a15 = c0;
  412. a16 = c1;
  413. c0 = a20 ^ (~a21 & a22);
  414. c1 = a21 ^ (~a22 & a23);
  415. a22 ^= ~a23 & a24;
  416. a23 ^= ~a24 & a20;
  417. a24 ^= ~a20 & a21;
  418. a20 = c0;
  419. a21 = c1;
  420. // iota
  421. a00 ^= KeccakRoundConstants[i];
  422. }
  423. A[ 0] = a00; A[ 1] = a01; A[ 2] = a02; A[ 3] = a03; A[ 4] = a04;
  424. A[ 5] = a05; A[ 6] = a06; A[ 7] = a07; A[ 8] = a08; A[ 9] = a09;
  425. A[10] = a10; A[11] = a11; A[12] = a12; A[13] = a13; A[14] = a14;
  426. A[15] = a15; A[16] = a16; A[17] = a17; A[18] = a18; A[19] = a19;
  427. A[20] = a20; A[21] = a21; A[22] = a22; A[23] = a23; A[24] = a24;
  428. }
  429. public virtual IMemoable Copy()
  430. {
  431. return new KeccakDigest(this);
  432. }
  433. public virtual void Reset(IMemoable other)
  434. {
  435. CopyIn((KeccakDigest)other);
  436. }
  437. }
  438. }
  439. #pragma warning restore
  440. #endif