FastGcmBlockCipher.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using BestHTTP.PlatformSupport.Memory;
  5. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Macs;
  7. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes;
  8. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes.Gcm;
  9. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  10. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
  11. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  12. namespace BestHTTP.Connections.TLS.Crypto.Impl
  13. {
  14. /// <summary>
  15. /// Implements the Galois/Counter mode (GCM) detailed in
  16. /// NIST Special Publication 800-38D.
  17. /// </summary>
  18. [BestHTTP.PlatformSupport.IL2CPP.Il2CppSetOption(BestHTTP.PlatformSupport.IL2CPP.Option.NullChecks, false)]
  19. [BestHTTP.PlatformSupport.IL2CPP.Il2CppSetOption(BestHTTP.PlatformSupport.IL2CPP.Option.ArrayBoundsChecks, false)]
  20. [BestHTTP.PlatformSupport.IL2CPP.Il2CppSetOption(BestHTTP.PlatformSupport.IL2CPP.Option.DivideByZeroChecks, false)]
  21. [BestHTTP.PlatformSupport.IL2CPP.Il2CppEagerStaticClassConstructionAttribute]
  22. public sealed class FastGcmBlockCipher
  23. : IAeadBlockCipher
  24. {
  25. private const int BlockSize = 16;
  26. byte[] ctrBlock = new byte[BlockSize];
  27. private readonly IBlockCipher cipher;
  28. private IGcmExponentiator exp;
  29. // These fields are set by Init and not modified by processing
  30. private bool forEncryption;
  31. private bool initialised;
  32. private int macSize;
  33. private byte[] lastKey;
  34. private byte[] nonce;
  35. private byte[] initialAssociatedText;
  36. private byte[] H;
  37. private byte[] J0;
  38. // These fields are modified during processing
  39. private int bufLength;
  40. private byte[] bufBlock;
  41. private byte[] macBlock;
  42. private byte[] S, S_at, S_atPre;
  43. private byte[] counter;
  44. private uint blocksRemaining;
  45. private int bufOff;
  46. private ulong totalLength;
  47. private byte[] atBlock;
  48. private int atBlockPos;
  49. private ulong atLength;
  50. private ulong atLengthPre;
  51. public FastGcmBlockCipher(
  52. IBlockCipher c)
  53. : this(c, null)
  54. {
  55. }
  56. public FastGcmBlockCipher(
  57. IBlockCipher c,
  58. IGcmMultiplier m)
  59. {
  60. if (c.GetBlockSize() != BlockSize)
  61. throw new ArgumentException("cipher required with a block size of " + BlockSize + ".");
  62. if (m != null)
  63. throw new NotImplementedException("IGcmMultiplier");
  64. this.cipher = c;
  65. }
  66. public string AlgorithmName
  67. {
  68. get { return cipher.AlgorithmName + "/GCM"; }
  69. }
  70. public IBlockCipher GetUnderlyingCipher()
  71. {
  72. return cipher;
  73. }
  74. public int GetBlockSize()
  75. {
  76. return BlockSize;
  77. }
  78. /// <remarks>
  79. /// MAC sizes from 32 bits to 128 bits (must be a multiple of 8) are supported. The default is 128 bits.
  80. /// Sizes less than 96 are not recommended, but are supported for specialized applications.
  81. /// </remarks>
  82. public void Init(
  83. bool forEncryption,
  84. ICipherParameters parameters)
  85. {
  86. this.forEncryption = forEncryption;
  87. //this.macBlock = null;
  88. if (this.macBlock != null)
  89. Array.Clear(this.macBlock, 0, this.macBlock.Length);
  90. this.initialised = true;
  91. NoCopyKeyParameter keyParam;
  92. byte[] newNonce = null;
  93. if (parameters is FastAeadParameters)
  94. {
  95. FastAeadParameters param = (FastAeadParameters)parameters;
  96. newNonce = param.GetNonce();
  97. initialAssociatedText = param.GetAssociatedText();
  98. int macSizeBits = param.MacSize;
  99. if (macSizeBits < 32 || macSizeBits > 128 || macSizeBits % 8 != 0)
  100. {
  101. throw new ArgumentException("Invalid value for MAC size: " + macSizeBits);
  102. }
  103. macSize = macSizeBits / 8;
  104. keyParam = param.Key;
  105. }
  106. else if (parameters is FastParametersWithIV)
  107. {
  108. FastParametersWithIV param = (FastParametersWithIV)parameters;
  109. newNonce = param.GetIV();
  110. initialAssociatedText = null;
  111. macSize = 16;
  112. keyParam = (NoCopyKeyParameter)param.Parameters;
  113. }
  114. else
  115. {
  116. throw new ArgumentException("invalid parameters passed to GCM");
  117. }
  118. this.bufLength = forEncryption ? BlockSize : (BlockSize + macSize);
  119. if (this.bufBlock == null || this.bufLength < this.bufBlock.Length)
  120. BufferPool.Resize(ref this.bufBlock, this.bufLength, true, true);
  121. if (newNonce == null || newNonce.Length < 1)
  122. {
  123. throw new ArgumentException("IV must be at least 1 byte");
  124. }
  125. if (forEncryption)
  126. {
  127. if (nonce != null && Arrays.AreEqual(nonce, newNonce))
  128. {
  129. if (keyParam == null)
  130. {
  131. throw new ArgumentException("cannot reuse nonce for GCM encryption");
  132. }
  133. if (lastKey != null && Arrays.AreEqual(lastKey, keyParam.GetKey()))
  134. {
  135. throw new ArgumentException("cannot reuse nonce for GCM encryption");
  136. }
  137. }
  138. }
  139. nonce = newNonce;
  140. if (keyParam != null)
  141. {
  142. lastKey = keyParam.GetKey();
  143. }
  144. // TODO Restrict macSize to 16 if nonce length not 12?
  145. // Cipher always used in forward mode
  146. // if keyParam is null we're reusing the last key.
  147. if (keyParam != null)
  148. {
  149. cipher.Init(true, keyParam);
  150. if (this.H == null)
  151. this.H = new byte[BlockSize];
  152. else
  153. Array.Clear(this.H, 0, BlockSize);
  154. cipher.ProcessBlock(H, 0, H, 0);
  155. // if keyParam is null we're reusing the last key and the multiplier doesn't need re-init
  156. Tables8kGcmMultiplier_Init(H);
  157. exp = null;
  158. }
  159. else if (this.H == null)
  160. {
  161. throw new ArgumentException("Key must be specified in initial init");
  162. }
  163. if (this.J0 == null)
  164. this.J0 = new byte[BlockSize];
  165. else
  166. Array.Clear(this.J0, 0, BlockSize);
  167. if (nonce.Length == 12)
  168. {
  169. Array.Copy(nonce, 0, J0, 0, nonce.Length);
  170. this.J0[BlockSize - 1] = 0x01;
  171. }
  172. else
  173. {
  174. gHASH(J0, nonce, nonce.Length);
  175. byte[] X = BufferPool.Get(BlockSize, false);
  176. Pack.UInt64_To_BE((ulong)nonce.Length * 8UL, X, 8);
  177. gHASHBlock(J0, X);
  178. BufferPool.Release(X);
  179. }
  180. //BufferPool.Resize(ref this.S, BlockSize, false, true);
  181. //BufferPool.Resize(ref this.S_at, BlockSize, false, true);
  182. //BufferPool.Resize(ref this.S_atPre, BlockSize, false, true);
  183. //BufferPool.Resize(ref this.atBlock, BlockSize, false, true);
  184. if (this.S == null)
  185. this.S = new byte[BlockSize];
  186. else
  187. Array.Clear(this.S, 0, this.S.Length);
  188. if (this.S_at == null)
  189. this.S_at = new byte[BlockSize];
  190. else
  191. Array.Clear(this.S_at, 0, this.S_at.Length);
  192. if (this.S_atPre == null)
  193. this.S_atPre = new byte[BlockSize];
  194. else
  195. Array.Clear(this.S_atPre, 0, this.S_atPre.Length);
  196. if (this.atBlock == null)
  197. this.atBlock = new byte[BlockSize];
  198. else
  199. Array.Clear(this.atBlock, 0, this.atBlock.Length);
  200. this.atBlockPos = 0;
  201. this.atLength = 0;
  202. this.atLengthPre = 0;
  203. //this.counter = Arrays.Clone(J0);
  204. //BufferPool.Resize(ref this.counter, BlockSize, false, true);
  205. if (this.counter == null)
  206. this.counter = new byte[BlockSize];
  207. else
  208. Array.Clear(this.counter, 0, this.counter.Length);
  209. Array.Copy(this.J0, 0, this.counter, 0, BlockSize);
  210. this.blocksRemaining = uint.MaxValue - 1; // page 8, len(P) <= 2^39 - 256, 1 block used by tag
  211. this.bufOff = 0;
  212. this.totalLength = 0;
  213. if (initialAssociatedText != null)
  214. {
  215. ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
  216. }
  217. }
  218. public byte[] GetMac()
  219. {
  220. return macBlock == null
  221. ? new byte[macSize]
  222. : Arrays.Clone(macBlock);
  223. }
  224. public int GetOutputSize(
  225. int len)
  226. {
  227. int totalData = len + bufOff;
  228. if (forEncryption)
  229. {
  230. return totalData + macSize;
  231. }
  232. return totalData < macSize ? 0 : totalData - macSize;
  233. }
  234. public int GetUpdateOutputSize(
  235. int len)
  236. {
  237. int totalData = len + bufOff;
  238. if (!forEncryption)
  239. {
  240. if (totalData < macSize)
  241. {
  242. return 0;
  243. }
  244. totalData -= macSize;
  245. }
  246. return totalData - totalData % BlockSize;
  247. }
  248. public void ProcessAadByte(byte input)
  249. {
  250. CheckStatus();
  251. atBlock[atBlockPos] = input;
  252. if (++atBlockPos == BlockSize)
  253. {
  254. // Hash each block as it fills
  255. gHASHBlock(S_at, atBlock);
  256. atBlockPos = 0;
  257. atLength += BlockSize;
  258. }
  259. }
  260. public void ProcessAadBytes(byte[] inBytes, int inOff, int len)
  261. {
  262. CheckStatus();
  263. for (int i = 0; i < len; ++i)
  264. {
  265. atBlock[atBlockPos] = inBytes[inOff + i];
  266. if (++atBlockPos == BlockSize)
  267. {
  268. // Hash each block as it fills
  269. gHASHBlock(S_at, atBlock);
  270. atBlockPos = 0;
  271. atLength += BlockSize;
  272. }
  273. }
  274. }
  275. private void InitCipher()
  276. {
  277. if (atLength > 0)
  278. {
  279. Array.Copy(S_at, 0, S_atPre, 0, BlockSize);
  280. atLengthPre = atLength;
  281. }
  282. // Finish hash for partial AAD block
  283. if (atBlockPos > 0)
  284. {
  285. gHASHPartial(S_atPre, atBlock, 0, atBlockPos);
  286. atLengthPre += (uint)atBlockPos;
  287. }
  288. if (atLengthPre > 0)
  289. {
  290. Array.Copy(S_atPre, 0, S, 0, BlockSize);
  291. }
  292. }
  293. public int ProcessByte(
  294. byte input,
  295. byte[] output,
  296. int outOff)
  297. {
  298. CheckStatus();
  299. bufBlock[bufOff] = input;
  300. if (++bufOff == bufLength)
  301. {
  302. ProcessBlock(bufBlock, 0, output, outOff);
  303. if (forEncryption)
  304. {
  305. bufOff = 0;
  306. }
  307. else
  308. {
  309. Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize);
  310. bufOff = macSize;
  311. }
  312. return BlockSize;
  313. }
  314. return 0;
  315. }
  316. public unsafe int ProcessBytes(
  317. byte[] input,
  318. int inOff,
  319. int len,
  320. byte[] output,
  321. int outOff)
  322. {
  323. CheckStatus();
  324. Check.DataLength(input, inOff, len, "input buffer too short");
  325. int resultLen = 0;
  326. if (forEncryption)
  327. {
  328. if (bufOff != 0)
  329. {
  330. while (len > 0)
  331. {
  332. --len;
  333. bufBlock[bufOff] = input[inOff++];
  334. if (++bufOff == BlockSize)
  335. {
  336. ProcessBlock(bufBlock, 0, output, outOff);
  337. bufOff = 0;
  338. resultLen += BlockSize;
  339. break;
  340. }
  341. }
  342. }
  343. fixed (byte* pctrBlock = ctrBlock, pbuf = input, pS = S, poutput = output)
  344. {
  345. while (len >= BlockSize)
  346. {
  347. // ProcessBlock(byte[] buf, int bufOff, byte[] output, int outOff)
  348. #region ProcessBlock(buf: input, bufOff: inOff, output: output, outOff: outOff + resultLen);
  349. if (totalLength == 0)
  350. InitCipher();
  351. #region GetNextCtrBlock(ctrBlock);
  352. blocksRemaining--;
  353. uint c = 1;
  354. c += counter[15]; counter[15] = (byte)c; c >>= 8;
  355. c += counter[14]; counter[14] = (byte)c; c >>= 8;
  356. c += counter[13]; counter[13] = (byte)c; c >>= 8;
  357. c += counter[12]; counter[12] = (byte)c;
  358. cipher.ProcessBlock(counter, 0, ctrBlock, 0);
  359. #endregion
  360. ulong* pulongBuf = (ulong*)&pbuf[inOff];
  361. ulong* pulongctrBlock = (ulong*)pctrBlock;
  362. pulongctrBlock[0] ^= pulongBuf[0];
  363. pulongctrBlock[1] ^= pulongBuf[1];
  364. ulong* pulongS = (ulong*)pS;
  365. pulongS[0] ^= pulongctrBlock[0];
  366. pulongS[1] ^= pulongctrBlock[1];
  367. Tables8kGcmMultiplier_MultiplyH(S);
  368. ulong* pulongoutput = (ulong*)&poutput[outOff + resultLen];
  369. pulongoutput[0] = pulongctrBlock[0];
  370. pulongoutput[1] = pulongctrBlock[1];
  371. totalLength += BlockSize;
  372. #endregion
  373. inOff += BlockSize;
  374. len -= BlockSize;
  375. resultLen += BlockSize;
  376. }
  377. }
  378. if (len > 0)
  379. {
  380. Array.Copy(input, inOff, bufBlock, 0, len);
  381. bufOff = len;
  382. }
  383. }
  384. else
  385. {
  386. fixed (byte* pinput = input, pbufBlock = bufBlock, pctrBlock = ctrBlock, pS = S, poutput = output)
  387. {
  388. ulong* pulongbufBlock = (ulong*)pbufBlock;
  389. // adjust bufOff to be on a 8 byte boundary
  390. int adjustCount = 0;
  391. for (int i = 0; i < len && (bufOff % 8) != 0; ++i)
  392. {
  393. pbufBlock[bufOff++] = pinput[inOff++ + i];
  394. adjustCount++;
  395. if (bufOff == bufLength)
  396. {
  397. ProcessBlock(bufBlock, 0, output, outOff + resultLen);
  398. pulongbufBlock[0] = pulongbufBlock[2];
  399. pulongbufBlock[1] = pulongbufBlock[3];
  400. bufOff = macSize;
  401. resultLen += BlockSize;
  402. }
  403. }
  404. int longLen = (len - adjustCount) / 8;
  405. if (longLen > 0)
  406. {
  407. ulong* pulonginput = (ulong*)&pinput[inOff];
  408. int bufLongOff = bufOff / 8;
  409. // copy 8 bytes per cycle instead of just 1
  410. for (int i = 0; i < longLen; ++i)
  411. {
  412. pulongbufBlock[bufLongOff++] = pulonginput[i];
  413. bufOff += 8;
  414. if (bufOff == bufLength)
  415. {
  416. #region ProcessBlock(buf: bufBlock, bufOff: 0, output: output, outOff: outOff + resultLen);
  417. if (totalLength == 0)
  418. InitCipher();
  419. #region GetNextCtrBlock(ctrBlock);
  420. blocksRemaining--;
  421. uint c = 1;
  422. c += counter[15]; counter[15] = (byte)c; c >>= 8;
  423. c += counter[14]; counter[14] = (byte)c; c >>= 8;
  424. c += counter[13]; counter[13] = (byte)c; c >>= 8;
  425. c += counter[12]; counter[12] = (byte)c;
  426. cipher.ProcessBlock(counter, 0, ctrBlock, 0);
  427. #endregion
  428. ulong* pulongS = (ulong*)pS;
  429. pulongS[0] ^= pulongbufBlock[0];
  430. pulongS[1] ^= pulongbufBlock[1];
  431. Tables8kGcmMultiplier_MultiplyH(S);
  432. ulong* pulongOutput = (ulong*)&poutput[outOff + resultLen];
  433. ulong* pulongctrBlock = (ulong*)pctrBlock;
  434. pulongOutput[0] = pulongctrBlock[0] ^ pulongbufBlock[0];
  435. pulongOutput[1] = pulongctrBlock[1] ^ pulongbufBlock[1];
  436. totalLength += BlockSize;
  437. #endregion
  438. pulongbufBlock[0] = pulongbufBlock[2];
  439. pulongbufBlock[1] = pulongbufBlock[3];
  440. bufOff = macSize;
  441. resultLen += BlockSize;
  442. bufLongOff = bufOff / 8;
  443. }
  444. }
  445. }
  446. for (int i = longLen * 8; i < len; i++)
  447. {
  448. pbufBlock[bufOff++] = pinput[inOff + i];
  449. if (bufOff == bufLength)
  450. {
  451. ProcessBlock(bufBlock, 0, output, outOff + resultLen);
  452. pulongbufBlock[0] = pulongbufBlock[2];
  453. pulongbufBlock[1] = pulongbufBlock[3];
  454. bufOff = macSize;
  455. resultLen += BlockSize;
  456. }
  457. }
  458. }
  459. }
  460. return resultLen;
  461. }
  462. private unsafe void ProcessBlock(byte[] buf, int bufOff, byte[] output, int outOff)
  463. {
  464. if (totalLength == 0)
  465. InitCipher();
  466. GetNextCtrBlock(ctrBlock);
  467. if (forEncryption)
  468. {
  469. fixed (byte* pctrBlock = ctrBlock, pbuf = buf, pS = S)
  470. {
  471. ulong* pulongBuf = (ulong*)&pbuf[bufOff];
  472. ulong* pulongctrBlock = (ulong*)pctrBlock;
  473. pulongctrBlock[0] ^= pulongBuf[0];
  474. pulongctrBlock[1] ^= pulongBuf[1];
  475. ulong* pulongS = (ulong*)pS;
  476. pulongS[0] ^= pulongctrBlock[0];
  477. pulongS[1] ^= pulongctrBlock[1];
  478. Tables8kGcmMultiplier_MultiplyH(S);
  479. fixed (byte* poutput = output)
  480. {
  481. ulong* pulongoutput = (ulong*)&poutput[outOff];
  482. pulongoutput[0] = pulongctrBlock[0];
  483. pulongoutput[1] = pulongctrBlock[1];
  484. }
  485. }
  486. }
  487. else
  488. {
  489. // moved this part to ProcessBytes's main part
  490. fixed (byte* pctrBlock = ctrBlock, pbuf = buf, pS = S, poutput = output)
  491. {
  492. ulong* pulongS = (ulong*)pS;
  493. ulong* pulongBuf = (ulong*)&pbuf[bufOff];
  494. pulongS[0] ^= pulongBuf[0];
  495. pulongS[1] ^= pulongBuf[1];
  496. Tables8kGcmMultiplier_MultiplyH(S);
  497. ulong* pulongOutput = (ulong*)&poutput[outOff];
  498. ulong* pulongctrBlock = (ulong*)pctrBlock;
  499. pulongOutput[0] = pulongctrBlock[0] ^ pulongBuf[0];
  500. pulongOutput[1] = pulongctrBlock[1] ^ pulongBuf[1];
  501. }
  502. }
  503. totalLength += BlockSize;
  504. }
  505. public int DoFinal(byte[] output, int outOff)
  506. {
  507. CheckStatus();
  508. if (totalLength == 0)
  509. {
  510. InitCipher();
  511. }
  512. int extra = bufOff;
  513. if (forEncryption)
  514. {
  515. Check.OutputLength(output, outOff, extra + macSize, "Output buffer too short");
  516. }
  517. else
  518. {
  519. if (extra < macSize)
  520. throw new InvalidCipherTextException("data too short");
  521. extra -= macSize;
  522. Check.OutputLength(output, outOff, extra, "Output buffer too short");
  523. }
  524. if (extra > 0)
  525. {
  526. ProcessPartial(bufBlock, 0, extra, output, outOff);
  527. }
  528. atLength += (uint)atBlockPos;
  529. if (atLength > atLengthPre)
  530. {
  531. /*
  532. * Some AAD was sent after the cipher started. We determine the difference b/w the hash value
  533. * we actually used when the cipher started (S_atPre) and the final hash value calculated (S_at).
  534. * Then we carry this difference forward by multiplying by H^c, where c is the number of (full or
  535. * partial) cipher-text blocks produced, and adjust the current hash.
  536. */
  537. // Finish hash for partial AAD block
  538. if (atBlockPos > 0)
  539. {
  540. gHASHPartial(S_at, atBlock, 0, atBlockPos);
  541. }
  542. // Find the difference between the AAD hashes
  543. if (atLengthPre > 0)
  544. {
  545. GcmUtilities.Xor(S_at, S_atPre);
  546. }
  547. // Number of cipher-text blocks produced
  548. long c = (long)(((totalLength * 8) + 127) >> 7);
  549. // Calculate the adjustment factor
  550. byte[] H_c = BufferPool.Get(16, true);
  551. if (exp == null)
  552. {
  553. exp = new Tables1kGcmExponentiator();
  554. exp.Init(H);
  555. }
  556. exp.ExponentiateX(c, H_c);
  557. // Carry the difference forward
  558. GcmUtilities.Multiply(S_at, H_c);
  559. // Adjust the current hash
  560. GcmUtilities.Xor(S, S_at);
  561. BufferPool.Release(H_c);
  562. }
  563. // Final gHASH
  564. byte[] X = BufferPool.Get(BlockSize, false);
  565. Pack.UInt64_To_BE(atLength * 8UL, X, 0);
  566. Pack.UInt64_To_BE(totalLength * 8UL, X, 8);
  567. gHASHBlock(S, X);
  568. BufferPool.Release(X);
  569. // T = MSBt(GCTRk(J0,S))
  570. byte[] tag = BufferPool.Get(BlockSize, false);
  571. cipher.ProcessBlock(J0, 0, tag, 0);
  572. GcmUtilities.Xor(tag, S);
  573. int resultLen = extra;
  574. // We place into macBlock our calculated value for T
  575. if (this.macBlock == null || this.macBlock.Length < macSize)
  576. this.macBlock = BufferPool.Resize(ref this.macBlock, macSize, false, false);
  577. Array.Copy(tag, 0, macBlock, 0, macSize);
  578. BufferPool.Release(tag);
  579. if (forEncryption)
  580. {
  581. // Append T to the message
  582. Array.Copy(macBlock, 0, output, outOff + bufOff, macSize);
  583. resultLen += macSize;
  584. }
  585. else
  586. {
  587. // Retrieve the T value from the message and compare to calculated one
  588. byte[] msgMac = BufferPool.Get(macSize, false);
  589. Array.Copy(bufBlock, extra, msgMac, 0, macSize);
  590. if (!Arrays.ConstantTimeAreEqual(this.macBlock, msgMac))
  591. throw new InvalidCipherTextException("mac check in GCM failed");
  592. BufferPool.Release(msgMac);
  593. }
  594. Reset(false);
  595. return resultLen;
  596. }
  597. public void Reset()
  598. {
  599. Reset(true);
  600. }
  601. private unsafe void Reset(
  602. bool clearMac)
  603. {
  604. cipher.Reset();
  605. // note: we do not reset the nonce.
  606. //BufferPool.Resize(ref this.S, BlockSize, false, true);
  607. //BufferPool.Resize(ref this.S_at, BlockSize, false, true);
  608. //BufferPool.Resize(ref this.S_atPre, BlockSize, false, true);
  609. //BufferPool.Resize(ref this.atBlock, BlockSize, false, true);
  610. fixed (byte* pS = S, pS_at = S_at, pS_atPre = S_atPre, patBlock = atBlock)
  611. {
  612. for (int i = 0; i < BlockSize; ++i)
  613. {
  614. pS[i] = pS_at[i] = pS_atPre[i] = patBlock[i] = 0;
  615. }
  616. }
  617. atBlockPos = 0;
  618. atLength = 0;
  619. atLengthPre = 0;
  620. //BufferPool.Resize(ref this.counter, BlockSize, false, false);
  621. Array.Copy(this.J0, 0, this.counter, 0, BlockSize);
  622. blocksRemaining = uint.MaxValue - 1;
  623. bufOff = 0;
  624. totalLength = 0;
  625. if (bufBlock != null)
  626. {
  627. //Arrays.Fill(bufBlock, 0);
  628. }
  629. if (clearMac)
  630. {
  631. //macBlock = null;
  632. Array.Clear(this.macBlock, 0, this.macSize);
  633. }
  634. if (forEncryption)
  635. {
  636. initialised = false;
  637. }
  638. else
  639. {
  640. if (initialAssociatedText != null)
  641. {
  642. ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
  643. }
  644. }
  645. }
  646. private void ProcessPartial(byte[] buf, int off, int len, byte[] output, int outOff)
  647. {
  648. //byte[] ctrBlock = new byte[BlockSize];
  649. GetNextCtrBlock(ctrBlock);
  650. if (forEncryption)
  651. {
  652. GcmUtilities.Xor(buf, off, ctrBlock, 0, len);
  653. gHASHPartial(S, buf, off, len);
  654. }
  655. else
  656. {
  657. gHASHPartial(S, buf, off, len);
  658. GcmUtilities.Xor(buf, off, ctrBlock, 0, len);
  659. }
  660. Array.Copy(buf, off, output, outOff, len);
  661. totalLength += (uint)len;
  662. }
  663. private void gHASH(byte[] Y, byte[] b, int len)
  664. {
  665. for (int pos = 0; pos < len; pos += BlockSize)
  666. {
  667. int num = System.Math.Min(len - pos, BlockSize);
  668. gHASHPartial(Y, b, pos, num);
  669. }
  670. }
  671. private void gHASHBlock(byte[] Y, byte[] b)
  672. {
  673. GcmUtilities.Xor(Y, b);
  674. Tables8kGcmMultiplier_MultiplyH(Y);
  675. }
  676. private void gHASHBlock(byte[] Y, byte[] b, int off)
  677. {
  678. GcmUtilities.Xor(Y, b, off);
  679. Tables8kGcmMultiplier_MultiplyH(Y);
  680. }
  681. private void gHASHPartial(byte[] Y, byte[] b, int off, int len)
  682. {
  683. GcmUtilities.Xor(Y, b, off, len);
  684. Tables8kGcmMultiplier_MultiplyH(Y);
  685. }
  686. private void GetNextCtrBlock(byte[] block)
  687. {
  688. if (blocksRemaining == 0)
  689. throw new InvalidOperationException("Attempt to process too many blocks");
  690. blocksRemaining--;
  691. uint c = 1;
  692. c += counter[15]; counter[15] = (byte)c; c >>= 8;
  693. c += counter[14]; counter[14] = (byte)c; c >>= 8;
  694. c += counter[13]; counter[13] = (byte)c; c >>= 8;
  695. c += counter[12]; counter[12] = (byte)c;
  696. cipher.ProcessBlock(counter, 0, block, 0);
  697. }
  698. private void CheckStatus()
  699. {
  700. if (!initialised)
  701. {
  702. if (forEncryption)
  703. {
  704. throw new InvalidOperationException("GCM cipher cannot be reused for encryption");
  705. }
  706. throw new InvalidOperationException("GCM cipher needs to be initialised");
  707. }
  708. }
  709. #region Tables8kGcmMultiplier
  710. private byte[] Tables8kGcmMultiplier_H;
  711. private uint[][][] Tables8kGcmMultiplier_M;
  712. public void Tables8kGcmMultiplier_Init(byte[] H)
  713. {
  714. if (Tables8kGcmMultiplier_M == null)
  715. {
  716. Tables8kGcmMultiplier_M = new uint[32][][];
  717. }
  718. else if (Arrays.AreEqual(this.Tables8kGcmMultiplier_H, H))
  719. {
  720. return;
  721. }
  722. this.Tables8kGcmMultiplier_H = Arrays.Clone(H);
  723. Tables8kGcmMultiplier_M[0] = new uint[16][];
  724. Tables8kGcmMultiplier_M[1] = new uint[16][];
  725. Tables8kGcmMultiplier_M[0][0] = new uint[4];
  726. Tables8kGcmMultiplier_M[1][0] = new uint[4];
  727. Tables8kGcmMultiplier_M[1][8] = GcmUtilities.AsUints(H);
  728. for (int j = 4; j >= 1; j >>= 1)
  729. {
  730. uint[] tmp = (uint[])Tables8kGcmMultiplier_M[1][j + j].Clone();
  731. GcmUtilities.MultiplyP(tmp);
  732. Tables8kGcmMultiplier_M[1][j] = tmp;
  733. }
  734. {
  735. uint[] tmp = (uint[])Tables8kGcmMultiplier_M[1][1].Clone();
  736. GcmUtilities.MultiplyP(tmp);
  737. Tables8kGcmMultiplier_M[0][8] = tmp;
  738. }
  739. for (int j = 4; j >= 1; j >>= 1)
  740. {
  741. uint[] tmp = (uint[])Tables8kGcmMultiplier_M[0][j + j].Clone();
  742. GcmUtilities.MultiplyP(tmp);
  743. Tables8kGcmMultiplier_M[0][j] = tmp;
  744. }
  745. for (int i = 0; ;)
  746. {
  747. for (int j = 2; j < 16; j += j)
  748. {
  749. for (int k = 1; k < j; ++k)
  750. {
  751. uint[] tmp = (uint[])Tables8kGcmMultiplier_M[i][j].Clone();
  752. GcmUtilities.Xor(tmp, Tables8kGcmMultiplier_M[i][k]);
  753. Tables8kGcmMultiplier_M[i][j + k] = tmp;
  754. }
  755. }
  756. if (++i == 32) return;
  757. if (i > 1)
  758. {
  759. Tables8kGcmMultiplier_M[i] = new uint[16][];
  760. Tables8kGcmMultiplier_M[i][0] = new uint[4];
  761. for (int j = 8; j > 0; j >>= 1)
  762. {
  763. uint[] tmp = (uint[])Tables8kGcmMultiplier_M[i - 2][j].Clone();
  764. GcmUtilities.MultiplyP8(tmp);
  765. Tables8kGcmMultiplier_M[i][j] = tmp;
  766. }
  767. }
  768. }
  769. }
  770. uint[] Tables8kGcmMultiplier_z = new uint[4];
  771. public unsafe void Tables8kGcmMultiplier_MultiplyH(byte[] x)
  772. {
  773. fixed (byte* px = x)
  774. fixed (uint* pz = Tables8kGcmMultiplier_z)
  775. {
  776. ulong* pulongZ = (ulong*)pz;
  777. pulongZ[0] = 0;
  778. pulongZ[1] = 0;
  779. for (int i = 15; i >= 0; --i)
  780. {
  781. uint[] m = Tables8kGcmMultiplier_M[i + i][px[i] & 0x0f];
  782. fixed (uint* pm = m)
  783. {
  784. ulong* pulongm = (ulong*)pm;
  785. pulongZ[0] ^= pulongm[0];
  786. pulongZ[1] ^= pulongm[1];
  787. }
  788. m = Tables8kGcmMultiplier_M[i + i + 1][(px[i] & 0xf0) >> 4];
  789. fixed (uint* pm = m)
  790. {
  791. ulong* pulongm = (ulong*)pm;
  792. pulongZ[0] ^= pulongm[0];
  793. pulongZ[1] ^= pulongm[1];
  794. }
  795. }
  796. byte* pbyteZ = (byte*)pz;
  797. px[0] = pbyteZ[3];
  798. px[1] = pbyteZ[2];
  799. px[2] = pbyteZ[1];
  800. px[3] = pbyteZ[0];
  801. px[4] = pbyteZ[7];
  802. px[5] = pbyteZ[6];
  803. px[6] = pbyteZ[5];
  804. px[7] = pbyteZ[4];
  805. px[8] = pbyteZ[11];
  806. px[9] = pbyteZ[10];
  807. px[10] = pbyteZ[9];
  808. px[11] = pbyteZ[8];
  809. px[12] = pbyteZ[15];
  810. px[13] = pbyteZ[14];
  811. px[14] = pbyteZ[13];
  812. px[15] = pbyteZ[12];
  813. }
  814. }
  815. #endregion
  816. }
  817. }
  818. #pragma warning restore
  819. #endif