GcmSivBlockCipher.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.IO;
  5. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes.Gcm;
  7. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  8. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
  9. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  10. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.IO;
  11. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
  12. {
  13. /**
  14. * GCM-SIV Mode.
  15. * <p>It should be noted that the specified limit of 2<sup>36</sup> bytes is not supported. This is because all bytes are
  16. * cached in a <b>ByteArrayOutputStream</b> object (which has a limit of a little less than 2<sup>31</sup> bytes),
  17. * and are output on the <b>DoFinal</b>() call (which can only process a maximum of 2<sup>31</sup> bytes).</p>
  18. * <p>The practical limit of 2<sup>31</sup> - 24 bytes is policed, and attempts to breach the limit will be rejected</p>
  19. * <p>In order to properly support the higher limit, an extended form of <b>ByteArrayOutputStream</b> would be needed
  20. * which would use multiple arrays to store the data. In addition, a new <b>doOutput</b> method would be required (similar
  21. * to that in <b>XOF</b> digests), which would allow the data to be output over multiple calls. Alternatively an extended
  22. * form of <b>ByteArrayInputStream</b> could be used to deliver the data.</p>
  23. */
  24. public class GcmSivBlockCipher
  25. : IAeadBlockCipher
  26. {
  27. /// <summary>The buffer length.</summary>
  28. private static readonly int BUFLEN = 16;
  29. /// <summary>The halfBuffer length.</summary>
  30. private static readonly int HALFBUFLEN = BUFLEN >> 1;
  31. /// <summary>The nonce length.</summary>
  32. private static readonly int NONCELEN = 12;
  33. /**
  34. * The maximum data length (AEAD/PlainText). Due to implementation constraints this is restricted to the maximum
  35. * array length (https://programming.guide/java/array-maximum-length.html) minus the BUFLEN to allow for the MAC
  36. */
  37. private static readonly int MAX_DATALEN = Int32.MaxValue - 8 - BUFLEN;
  38. /**
  39. * The top bit mask.
  40. */
  41. private static readonly byte MASK = (byte)0x80;
  42. /**
  43. * The addition constant.
  44. */
  45. private static readonly byte ADD = (byte)0xE1;
  46. /**
  47. * The initialisation flag.
  48. */
  49. private static readonly int INIT = 1;
  50. /**
  51. * The aeadComplete flag.
  52. */
  53. private static readonly int AEAD_COMPLETE = 2;
  54. /**
  55. * The cipher.
  56. */
  57. private readonly IBlockCipher theCipher;
  58. /**
  59. * The multiplier.
  60. */
  61. private readonly IGcmMultiplier theMultiplier;
  62. /**
  63. * The gHash buffer.
  64. */
  65. internal readonly byte[] theGHash = new byte[BUFLEN];
  66. /**
  67. * The reverse buffer.
  68. */
  69. internal readonly byte[] theReverse = new byte[BUFLEN];
  70. /**
  71. * The aeadHasher.
  72. */
  73. private readonly GcmSivHasher theAEADHasher;
  74. /**
  75. * The dataHasher.
  76. */
  77. private readonly GcmSivHasher theDataHasher;
  78. /**
  79. * The plainDataStream.
  80. */
  81. private GcmSivCache thePlain;
  82. /**
  83. * The encryptedDataStream (decryption only).
  84. */
  85. private GcmSivCache theEncData;
  86. /**
  87. * Are we encrypting?
  88. */
  89. private bool forEncryption;
  90. /**
  91. * The initialAEAD.
  92. */
  93. private byte[] theInitialAEAD;
  94. /**
  95. * The nonce.
  96. */
  97. private byte[] theNonce;
  98. /**
  99. * The flags.
  100. */
  101. private int theFlags;
  102. /**
  103. * Constructor.
  104. */
  105. public GcmSivBlockCipher()
  106. : this(new AesEngine())
  107. {
  108. }
  109. /**
  110. * Constructor.
  111. * @param pCipher the underlying cipher
  112. */
  113. public GcmSivBlockCipher(IBlockCipher pCipher)
  114. : this(pCipher, new Tables4kGcmMultiplier())
  115. {
  116. }
  117. /**
  118. * Constructor.
  119. * @param pCipher the underlying cipher
  120. * @param pMultiplier the multiplier
  121. */
  122. public GcmSivBlockCipher(IBlockCipher pCipher, IGcmMultiplier pMultiplier)
  123. {
  124. /* Ensure that the cipher is the correct size */
  125. if (pCipher.GetBlockSize() != BUFLEN)
  126. throw new ArgumentException("Cipher required with a block size of " + BUFLEN + ".");
  127. /* Store parameters */
  128. theCipher = pCipher;
  129. theMultiplier = pMultiplier;
  130. /* Create the hashers */
  131. theAEADHasher = new GcmSivHasher(this);
  132. theDataHasher = new GcmSivHasher(this);
  133. }
  134. public virtual IBlockCipher GetUnderlyingCipher()
  135. {
  136. return theCipher;
  137. }
  138. public virtual int GetBlockSize()
  139. {
  140. return theCipher.GetBlockSize();
  141. }
  142. public virtual void Init(bool pEncrypt, ICipherParameters cipherParameters)
  143. {
  144. /* Set defaults */
  145. byte[] myInitialAEAD = null;
  146. byte[] myNonce = null;
  147. KeyParameter myKey = null;
  148. /* Access parameters */
  149. if (cipherParameters is AeadParameters)
  150. {
  151. AeadParameters myAEAD = (AeadParameters)cipherParameters;
  152. myInitialAEAD = myAEAD.GetAssociatedText();
  153. myNonce = myAEAD.GetNonce();
  154. myKey = myAEAD.Key;
  155. }
  156. else if (cipherParameters is ParametersWithIV)
  157. {
  158. ParametersWithIV myParms = (ParametersWithIV)cipherParameters;
  159. myNonce = myParms.GetIV();
  160. myKey = (KeyParameter)myParms.Parameters;
  161. }
  162. else
  163. {
  164. throw new ArgumentException("invalid parameters passed to GCM_SIV");
  165. }
  166. /* Check nonceSize */
  167. if (myNonce == null || myNonce.Length != NONCELEN)
  168. {
  169. throw new ArgumentException("Invalid nonce");
  170. }
  171. /* Check keysize */
  172. if (myKey == null)
  173. {
  174. throw new ArgumentException("Invalid key");
  175. }
  176. byte[] k = myKey.GetKey();
  177. if (k.Length != BUFLEN
  178. && k.Length != (BUFLEN << 1))
  179. {
  180. throw new ArgumentException("Invalid key");
  181. }
  182. /* Reset details */
  183. forEncryption = pEncrypt;
  184. theInitialAEAD = myInitialAEAD;
  185. theNonce = myNonce;
  186. /* Initialise the keys */
  187. deriveKeys(myKey);
  188. ResetStreams();
  189. }
  190. public virtual string AlgorithmName
  191. {
  192. get { return theCipher.AlgorithmName + "-GCM-SIV"; }
  193. }
  194. /**
  195. * check AEAD status.
  196. * @param pLen the aeadLength
  197. */
  198. private void CheckAeadStatus(int pLen)
  199. {
  200. /* Check we are initialised */
  201. if ((theFlags & INIT) == 0)
  202. {
  203. throw new InvalidOperationException("Cipher is not initialised");
  204. }
  205. /* Check AAD is allowed */
  206. if ((theFlags & AEAD_COMPLETE) != 0)
  207. {
  208. throw new InvalidOperationException("AEAD data cannot be processed after ordinary data");
  209. }
  210. /* Make sure that we haven't breached AEAD data limit */
  211. if ((long)theAEADHasher.getBytesProcessed() + Int64.MinValue > (MAX_DATALEN - pLen) + Int64.MinValue)
  212. {
  213. throw new InvalidOperationException("AEAD byte count exceeded");
  214. }
  215. }
  216. /**
  217. * check status.
  218. * @param pLen the dataLength
  219. */
  220. private void CheckStatus(int pLen)
  221. {
  222. /* Check we are initialised */
  223. if ((theFlags & INIT) == 0)
  224. {
  225. throw new InvalidOperationException("Cipher is not initialised");
  226. }
  227. /* Complete the AEAD section if this is the first data */
  228. if ((theFlags & AEAD_COMPLETE) == 0)
  229. {
  230. theAEADHasher.completeHash();
  231. theFlags |= AEAD_COMPLETE;
  232. }
  233. /* Make sure that we haven't breached data limit */
  234. long dataLimit = MAX_DATALEN;
  235. long currBytes = thePlain.Length;
  236. if (!forEncryption)
  237. {
  238. dataLimit += BUFLEN;
  239. currBytes = theEncData.Length;
  240. }
  241. if (currBytes + System.Int64.MinValue
  242. > (dataLimit - pLen) + System.Int64.MinValue)
  243. {
  244. throw new InvalidOperationException("byte count exceeded");
  245. }
  246. }
  247. public virtual void ProcessAadByte(byte pByte)
  248. {
  249. /* Check that we can supply AEAD */
  250. CheckAeadStatus(1);
  251. /* Process the aead */
  252. theAEADHasher.updateHash(pByte);
  253. }
  254. public virtual void ProcessAadBytes(byte[] pData, int pOffset, int pLen)
  255. {
  256. /* Check that we can supply AEAD */
  257. CheckAeadStatus(pLen);
  258. /* Check input buffer */
  259. CheckBuffer(pData, pOffset, pLen, false);
  260. /* Process the aead */
  261. theAEADHasher.updateHash(pData, pOffset, pLen);
  262. }
  263. public virtual int ProcessByte(byte pByte, byte[] pOutput, int pOutOffset)
  264. {
  265. /* Check that we have initialised */
  266. CheckStatus(1);
  267. /* Store the data */
  268. if (forEncryption)
  269. {
  270. thePlain.WriteByte(pByte);
  271. theDataHasher.updateHash(pByte);
  272. }
  273. else
  274. {
  275. theEncData.WriteByte(pByte);
  276. }
  277. /* No data returned */
  278. return 0;
  279. }
  280. public virtual int ProcessBytes(byte[] pData, int pOffset, int pLen, byte[] pOutput, int pOutOffset)
  281. {
  282. /* Check that we have initialised */
  283. CheckStatus(pLen);
  284. /* Check input buffer */
  285. CheckBuffer(pData, pOffset, pLen, false);
  286. /* Store the data */
  287. if (forEncryption)
  288. {
  289. thePlain.Write(pData, pOffset, pLen);
  290. theDataHasher.updateHash(pData, pOffset, pLen);
  291. }
  292. else
  293. {
  294. theEncData.Write(pData, pOffset, pLen);
  295. }
  296. /* No data returned */
  297. return 0;
  298. }
  299. public virtual int DoFinal(byte[] pOutput, int pOffset)
  300. {
  301. /* Check that we have initialised */
  302. CheckStatus(0);
  303. /* Check output buffer */
  304. CheckBuffer(pOutput, pOffset, GetOutputSize(0), true);
  305. /* If we are encrypting */
  306. if (forEncryption)
  307. {
  308. /* Derive the tag */
  309. byte[] myTag = calculateTag();
  310. /* encrypt the plain text */
  311. int myDataLen = BUFLEN + encryptPlain(myTag, pOutput, pOffset);
  312. /* Add the tag to the output */
  313. Array.Copy(myTag, 0, pOutput, pOffset + (int)thePlain.Length, BUFLEN);
  314. /* Reset the streams */
  315. ResetStreams();
  316. return myDataLen;
  317. /* else we are decrypting */
  318. }
  319. else
  320. {
  321. /* decrypt to plain text */
  322. decryptPlain();
  323. /* Release plain text */
  324. int myDataLen = Streams.WriteBufTo(thePlain, pOutput, pOffset);
  325. /* Reset the streams */
  326. ResetStreams();
  327. return myDataLen;
  328. }
  329. }
  330. public virtual byte[] GetMac()
  331. {
  332. throw new InvalidOperationException();
  333. }
  334. public virtual int GetUpdateOutputSize(int pLen)
  335. {
  336. return 0;
  337. }
  338. public virtual int GetOutputSize(int pLen)
  339. {
  340. if (forEncryption)
  341. {
  342. return (int)(pLen + thePlain.Length + BUFLEN);
  343. }
  344. int myCurr = (int)(pLen + theEncData.Length);
  345. return myCurr > BUFLEN ? myCurr - BUFLEN : 0;
  346. }
  347. public virtual void Reset()
  348. {
  349. ResetStreams();
  350. }
  351. /**
  352. * Reset Streams.
  353. */
  354. private void ResetStreams()
  355. {
  356. /* Clear the plainText buffer */
  357. if (thePlain != null)
  358. {
  359. thePlain.Position = 0L;
  360. Streams.WriteZeroes(thePlain, thePlain.Capacity);
  361. }
  362. /* Reset hashers */
  363. theAEADHasher.Reset();
  364. theDataHasher.Reset();
  365. /* Recreate streams (to release memory) */
  366. thePlain = new GcmSivCache();
  367. theEncData = forEncryption ? null : new GcmSivCache();
  368. /* Initialise AEAD if required */
  369. theFlags &= ~AEAD_COMPLETE;
  370. Arrays.Fill(theGHash, (byte)0);
  371. if (theInitialAEAD != null)
  372. {
  373. theAEADHasher.updateHash(theInitialAEAD, 0, theInitialAEAD.Length);
  374. }
  375. }
  376. /**
  377. * Obtain buffer length (allowing for null).
  378. * @param pBuffer the buffere
  379. * @return the length
  380. */
  381. private static int bufLength(byte[] pBuffer)
  382. {
  383. return pBuffer == null ? 0 : pBuffer.Length;
  384. }
  385. /**
  386. * Check buffer.
  387. * @param pBuffer the buffer
  388. * @param pOffset the offset
  389. * @param pLen the length
  390. * @param pOutput is this an output buffer?
  391. */
  392. private static void CheckBuffer(byte[] pBuffer, int pOffset, int pLen, bool pOutput)
  393. {
  394. /* Access lengths */
  395. int myBufLen = bufLength(pBuffer);
  396. int myLast = pOffset + pLen;
  397. /* Check for negative values and buffer overflow */
  398. bool badLen = pLen < 0 || pOffset < 0 || myLast < 0;
  399. if (badLen || myLast > myBufLen)
  400. {
  401. throw pOutput
  402. ? new OutputLengthException("Output buffer too short.")
  403. : new DataLengthException("Input buffer too short.");
  404. }
  405. }
  406. /**
  407. * encrypt data stream.
  408. * @param pCounter the counter
  409. * @param pTarget the target buffer
  410. * @param pOffset the target offset
  411. * @return the length of data encrypted
  412. */
  413. private int encryptPlain(byte[] pCounter, byte[] pTarget, int pOffset)
  414. {
  415. /* Access buffer and length */
  416. #if PORTABLE || NETFX_CORE
  417. byte[] thePlainBuf = thePlain.ToArray();
  418. int thePlainLen = thePlainBuf.Length;
  419. #else
  420. byte[] thePlainBuf = thePlain.GetBuffer();
  421. int thePlainLen = (int)thePlain.Length;
  422. #endif
  423. byte[] mySrc = thePlainBuf;
  424. byte[] myCounter = Arrays.Clone(pCounter);
  425. myCounter[BUFLEN - 1] |= MASK;
  426. byte[] myMask = new byte[BUFLEN];
  427. long myRemaining = thePlainLen;
  428. int myOff = 0;
  429. /* While we have data to process */
  430. while (myRemaining > 0)
  431. {
  432. /* Generate the next mask */
  433. theCipher.ProcessBlock(myCounter, 0, myMask, 0);
  434. /* Xor data into mask */
  435. int myLen = (int)System.Math.Min(BUFLEN, myRemaining);
  436. xorBlock(myMask, mySrc, myOff, myLen);
  437. /* Copy encrypted data to output */
  438. Array.Copy(myMask, 0, pTarget, pOffset + myOff, myLen);
  439. /* Adjust counters */
  440. myRemaining -= myLen;
  441. myOff += myLen;
  442. incrementCounter(myCounter);
  443. }
  444. /* Return the amount of data processed */
  445. return thePlainLen;
  446. }
  447. /**
  448. * decrypt data stream.
  449. * @throws InvalidCipherTextException on data too short or mac check failed
  450. */
  451. private void decryptPlain()
  452. {
  453. /* Access buffer and length */
  454. #if PORTABLE || NETFX_CORE
  455. byte[] theEncDataBuf = theEncData.ToArray();
  456. int theEncDataLen = theEncDataBuf.Length;
  457. #else
  458. byte[] theEncDataBuf = theEncData.GetBuffer();
  459. int theEncDataLen = (int)theEncData.Length;
  460. #endif
  461. byte[] mySrc = theEncDataBuf;
  462. int myRemaining = theEncDataLen - BUFLEN;
  463. /* Check for insufficient data */
  464. if (myRemaining < 0)
  465. {
  466. throw new InvalidCipherTextException("Data too short");
  467. }
  468. /* Access counter */
  469. byte[] myExpected = Arrays.CopyOfRange(mySrc, myRemaining, myRemaining + BUFLEN);
  470. byte[] myCounter = Arrays.Clone(myExpected);
  471. myCounter[BUFLEN - 1] |= MASK;
  472. byte[] myMask = new byte[BUFLEN];
  473. int myOff = 0;
  474. /* While we have data to process */
  475. while (myRemaining > 0)
  476. {
  477. /* Generate the next mask */
  478. theCipher.ProcessBlock(myCounter, 0, myMask, 0);
  479. /* Xor data into mask */
  480. int myLen = System.Math.Min(BUFLEN, myRemaining);
  481. xorBlock(myMask, mySrc, myOff, myLen);
  482. /* Write data to plain dataStream */
  483. thePlain.Write(myMask, 0, myLen);
  484. theDataHasher.updateHash(myMask, 0, myLen);
  485. /* Adjust counters */
  486. myRemaining -= myLen;
  487. myOff += myLen;
  488. incrementCounter(myCounter);
  489. }
  490. /* Derive and check the tag */
  491. byte[] myTag = calculateTag();
  492. if (!Arrays.ConstantTimeAreEqual(myTag, myExpected))
  493. {
  494. Reset();
  495. throw new InvalidCipherTextException("mac check failed");
  496. }
  497. }
  498. /**
  499. * calculate tag.
  500. * @return the calculated tag
  501. */
  502. private byte[] calculateTag()
  503. {
  504. /* Complete the hash */
  505. theDataHasher.completeHash();
  506. byte[] myPolyVal = completePolyVal();
  507. /* calculate polyVal */
  508. byte[] myResult = new byte[BUFLEN];
  509. /* Fold in the nonce */
  510. for (int i = 0; i < NONCELEN; i++)
  511. {
  512. myPolyVal[i] ^= theNonce[i];
  513. }
  514. /* Clear top bit */
  515. myPolyVal[BUFLEN - 1] &= (byte)(MASK - 1);
  516. /* Calculate tag and return it */
  517. theCipher.ProcessBlock(myPolyVal, 0, myResult, 0);
  518. return myResult;
  519. }
  520. /**
  521. * complete polyVAL.
  522. * @return the calculated value
  523. */
  524. private byte[] completePolyVal()
  525. {
  526. /* Build the polyVal result */
  527. byte[] myResult = new byte[BUFLEN];
  528. gHashLengths();
  529. fillReverse(theGHash, 0, BUFLEN, myResult);
  530. return myResult;
  531. }
  532. /**
  533. * process lengths.
  534. */
  535. private void gHashLengths()
  536. {
  537. /* Create reversed bigEndian buffer to keep it simple */
  538. byte[] myIn = new byte[BUFLEN];
  539. Pack.UInt64_To_BE((ulong)Bytes.NumBits * theDataHasher.getBytesProcessed(), myIn, 0);
  540. Pack.UInt64_To_BE((ulong)Bytes.NumBits * theAEADHasher.getBytesProcessed(), myIn, Longs.NumBytes);
  541. /* hash value */
  542. gHASH(myIn);
  543. }
  544. /**
  545. * perform the next GHASH step.
  546. * @param pNext the next value
  547. */
  548. private void gHASH(byte[] pNext)
  549. {
  550. xorBlock(theGHash, pNext);
  551. theMultiplier.MultiplyH(theGHash);
  552. }
  553. /**
  554. * Byte reverse a buffer.
  555. * @param pInput the input buffer
  556. * @param pOffset the offset
  557. * @param pLength the length of data (<= BUFLEN)
  558. * @param pOutput the output buffer
  559. */
  560. private static void fillReverse(byte[] pInput, int pOffset, int pLength, byte[] pOutput)
  561. {
  562. /* Loop through the buffer */
  563. for (int i = 0, j = BUFLEN - 1; i < pLength; i++, j--)
  564. {
  565. /* Copy byte */
  566. pOutput[j] = pInput[pOffset + i];
  567. }
  568. }
  569. /**
  570. * xor a full block buffer.
  571. * @param pLeft the left operand and result
  572. * @param pRight the right operand
  573. */
  574. private static void xorBlock(byte[] pLeft, byte[] pRight)
  575. {
  576. /* Loop through the bytes */
  577. for (int i = 0; i < BUFLEN; i++)
  578. {
  579. pLeft[i] ^= pRight[i];
  580. }
  581. }
  582. /**
  583. * xor a partial block buffer.
  584. * @param pLeft the left operand and result
  585. * @param pRight the right operand
  586. * @param pOffset the offset in the right operand
  587. * @param pLength the length of data in the right operand
  588. */
  589. private static void xorBlock(byte[] pLeft, byte[] pRight, int pOffset, int pLength)
  590. {
  591. /* Loop through the bytes */
  592. for (int i = 0; i < pLength; i++)
  593. {
  594. pLeft[i] ^= pRight[i + pOffset];
  595. }
  596. }
  597. /**
  598. * increment the counter.
  599. * @param pCounter the counter to increment
  600. */
  601. private static void incrementCounter(byte[] pCounter)
  602. {
  603. /* Loop through the bytes incrementing counter */
  604. for (int i = 0; i < Integers.NumBytes; i++)
  605. {
  606. if (++pCounter[i] != 0)
  607. {
  608. break;
  609. }
  610. }
  611. }
  612. /**
  613. * multiply by X.
  614. * @param pValue the value to adjust
  615. */
  616. private static void mulX(byte[] pValue)
  617. {
  618. /* Loop through the bytes */
  619. byte myMask = (byte)0;
  620. for (int i = 0; i < BUFLEN; i++)
  621. {
  622. byte myValue = pValue[i];
  623. pValue[i] = (byte)(((myValue >> 1) & ~MASK) | myMask);
  624. myMask = (byte)((myValue & 1) == 0 ? (byte)0 : MASK);
  625. }
  626. /* Xor in addition if last bit was set */
  627. if (myMask != 0)
  628. {
  629. pValue[0] ^= ADD;
  630. }
  631. }
  632. /**
  633. * Derive Keys.
  634. * @param pKey the keyGeneration key
  635. */
  636. private void deriveKeys(KeyParameter pKey)
  637. {
  638. /* Create the buffers */
  639. byte[] myIn = new byte[BUFLEN];
  640. byte[] myOut = new byte[BUFLEN];
  641. byte[] myResult = new byte[BUFLEN];
  642. byte[] myEncKey = new byte[pKey.GetKey().Length];
  643. /* Prepare for encryption */
  644. Array.Copy(theNonce, 0, myIn, BUFLEN - NONCELEN, NONCELEN);
  645. theCipher.Init(true, pKey);
  646. /* Derive authentication key */
  647. int myOff = 0;
  648. theCipher.ProcessBlock(myIn, 0, myOut, 0);
  649. Array.Copy(myOut, 0, myResult, myOff, HALFBUFLEN);
  650. myIn[0]++;
  651. myOff += HALFBUFLEN;
  652. theCipher.ProcessBlock(myIn, 0, myOut, 0);
  653. Array.Copy(myOut, 0, myResult, myOff, HALFBUFLEN);
  654. /* Derive encryption key */
  655. myIn[0]++;
  656. myOff = 0;
  657. theCipher.ProcessBlock(myIn, 0, myOut, 0);
  658. Array.Copy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
  659. myIn[0]++;
  660. myOff += HALFBUFLEN;
  661. theCipher.ProcessBlock(myIn, 0, myOut, 0);
  662. Array.Copy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
  663. /* If we have a 32byte key */
  664. if (myEncKey.Length == BUFLEN << 1)
  665. {
  666. /* Derive remainder of encryption key */
  667. myIn[0]++;
  668. myOff += HALFBUFLEN;
  669. theCipher.ProcessBlock(myIn, 0, myOut, 0);
  670. Array.Copy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
  671. myIn[0]++;
  672. myOff += HALFBUFLEN;
  673. theCipher.ProcessBlock(myIn, 0, myOut, 0);
  674. Array.Copy(myOut, 0, myEncKey, myOff, HALFBUFLEN);
  675. }
  676. /* Initialise the Cipher */
  677. theCipher.Init(true, new KeyParameter(myEncKey));
  678. /* Initialise the multiplier */
  679. fillReverse(myResult, 0, BUFLEN, myOut);
  680. mulX(myOut);
  681. theMultiplier.Init(myOut);
  682. theFlags |= INIT;
  683. }
  684. private class GcmSivCache
  685. : MemoryStream
  686. {
  687. internal GcmSivCache()
  688. {
  689. }
  690. }
  691. /**
  692. * Hash Control.
  693. */
  694. private class GcmSivHasher
  695. {
  696. /**
  697. * Cache.
  698. */
  699. private readonly byte[] theBuffer = new byte[BUFLEN];
  700. /**
  701. * Single byte cache.
  702. */
  703. private readonly byte[] theByte = new byte[1];
  704. /**
  705. * Count of active bytes in cache.
  706. */
  707. private int numActive;
  708. /**
  709. * Count of hashed bytes.
  710. */
  711. private ulong numHashed;
  712. private readonly GcmSivBlockCipher parent;
  713. internal GcmSivHasher(GcmSivBlockCipher parent)
  714. {
  715. this.parent = parent;
  716. }
  717. /**
  718. * Obtain the count of bytes hashed.
  719. * @return the count
  720. */
  721. internal ulong getBytesProcessed()
  722. {
  723. return numHashed;
  724. }
  725. /**
  726. * Reset the hasher.
  727. */
  728. internal void Reset()
  729. {
  730. numActive = 0;
  731. numHashed = 0;
  732. }
  733. /**
  734. * update hash.
  735. * @param pByte the byte
  736. */
  737. internal void updateHash(byte pByte)
  738. {
  739. theByte[0] = pByte;
  740. updateHash(theByte, 0, 1);
  741. }
  742. /**
  743. * update hash.
  744. * @param pBuffer the buffer
  745. * @param pOffset the offset within the buffer
  746. * @param pLen the length of data
  747. */
  748. internal void updateHash(byte[] pBuffer, int pOffset, int pLen)
  749. {
  750. /* If we should process the cache */
  751. int mySpace = BUFLEN - numActive;
  752. int numProcessed = 0;
  753. int myRemaining = pLen;
  754. if (numActive > 0 && pLen >= mySpace)
  755. {
  756. /* Copy data into the cache and hash it */
  757. Array.Copy(pBuffer, pOffset, theBuffer, numActive, mySpace);
  758. fillReverse(theBuffer, 0, BUFLEN, parent.theReverse);
  759. parent.gHASH(parent.theReverse);
  760. /* Adjust counters */
  761. numProcessed += mySpace;
  762. myRemaining -= mySpace;
  763. numActive = 0;
  764. }
  765. /* While we have full blocks */
  766. while (myRemaining >= BUFLEN)
  767. {
  768. /* Access the next data */
  769. fillReverse(pBuffer, pOffset + numProcessed, BUFLEN, parent.theReverse);
  770. parent.gHASH(parent.theReverse);
  771. /* Adjust counters */
  772. numProcessed += mySpace;
  773. myRemaining -= mySpace;
  774. }
  775. /* If we have remaining data */
  776. if (myRemaining > 0)
  777. {
  778. /* Copy data into the cache */
  779. Array.Copy(pBuffer, pOffset + numProcessed, theBuffer, numActive, myRemaining);
  780. numActive += myRemaining;
  781. }
  782. /* Adjust the number of bytes processed */
  783. numHashed += (ulong)pLen;
  784. }
  785. /**
  786. * complete hash.
  787. */
  788. internal void completeHash()
  789. {
  790. /* If we have remaining data */
  791. if (numActive > 0)
  792. {
  793. /* Access the next data */
  794. Arrays.Fill(parent.theReverse, (byte)0);
  795. fillReverse(theBuffer, 0, numActive, parent.theReverse);
  796. /* hash value */
  797. parent.gHASH(parent.theReverse);
  798. }
  799. }
  800. }
  801. }
  802. }
  803. #pragma warning restore
  804. #endif