KCcmBlockCipher.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.IO;
  5. using System.Text;
  6. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  7. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  8. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
  9. {
  10. public class KCcmBlockCipher
  11. : IAeadBlockCipher
  12. {
  13. private static readonly int BYTES_IN_INT = 4;
  14. private static readonly int BITS_IN_BYTE = 8;
  15. private static readonly int MAX_MAC_BIT_LENGTH = 512;
  16. private static readonly int MIN_MAC_BIT_LENGTH = 64;
  17. private IBlockCipher engine;
  18. private int macSize;
  19. private bool forEncryption;
  20. private byte[] initialAssociatedText;
  21. private byte[] mac;
  22. private byte[] macBlock;
  23. private byte[] nonce;
  24. private byte[] G1;
  25. private byte[] buffer;
  26. private byte[] s;
  27. private byte[] counter;
  28. private readonly MemoryStream associatedText = new MemoryStream();
  29. private readonly MemoryStream data = new MemoryStream();
  30. /*
  31. *
  32. *
  33. */
  34. private int Nb_ = 4;
  35. private void setNb(int Nb)
  36. {
  37. if (Nb == 4 || Nb == 6 || Nb == 8)
  38. {
  39. Nb_ = Nb;
  40. }
  41. else
  42. {
  43. throw new ArgumentException("Nb = 4 is recommended by DSTU7624 but can be changed to only 6 or 8 in this implementation");
  44. }
  45. }
  46. /// <summary>
  47. /// Base constructor. Nb value is set to 4.
  48. /// </summary>
  49. /// <param name="engine">base cipher to use under CCM.</param>
  50. public KCcmBlockCipher(IBlockCipher engine): this(engine, 4)
  51. {
  52. }
  53. /// <summary>
  54. /// Constructor allowing Nb configuration.
  55. ///
  56. /// Nb is a parameter specified in CCM mode of DSTU7624 standard.
  57. /// This parameter specifies maximum possible length of input.It should
  58. /// be calculated as follows: Nb = 1 / 8 * (-3 + log[2]Nmax) + 1,
  59. /// where Nmax - length of input message in bits.For practical reasons
  60. /// Nmax usually less than 4Gb, e.g. for Nmax = 2^32 - 1, Nb = 4.
  61. /// </summary>
  62. /// <param name="engine">base cipher to use under CCM.</param>
  63. /// <param name="Nb">Nb value to use.</param>
  64. public KCcmBlockCipher(IBlockCipher engine, int Nb)
  65. {
  66. this.engine = engine;
  67. this.macSize = engine.GetBlockSize();
  68. this.nonce = new byte[engine.GetBlockSize()];
  69. this.initialAssociatedText = new byte[engine.GetBlockSize()];
  70. this.mac = new byte[engine.GetBlockSize()];
  71. this.macBlock = new byte[engine.GetBlockSize()];
  72. this.G1 = new byte[engine.GetBlockSize()];
  73. this.buffer = new byte[engine.GetBlockSize()];
  74. this.s = new byte[engine.GetBlockSize()];
  75. this.counter = new byte[engine.GetBlockSize()];
  76. setNb(Nb);
  77. }
  78. public virtual void Init(bool forEncryption, ICipherParameters parameters)
  79. {
  80. ICipherParameters cipherParameters;
  81. if (parameters is AeadParameters)
  82. {
  83. AeadParameters param = (AeadParameters)parameters;
  84. if (param.MacSize > MAX_MAC_BIT_LENGTH || param.MacSize < MIN_MAC_BIT_LENGTH || param.MacSize % 8 != 0)
  85. {
  86. throw new ArgumentException("Invalid mac size specified");
  87. }
  88. nonce = param.GetNonce();
  89. macSize = param.MacSize / BITS_IN_BYTE;
  90. initialAssociatedText = param.GetAssociatedText();
  91. cipherParameters = param.Key;
  92. }
  93. else if (parameters is ParametersWithIV)
  94. {
  95. nonce = ((ParametersWithIV)parameters).GetIV();
  96. macSize = engine.GetBlockSize(); // use default blockSize for MAC if it is not specified
  97. initialAssociatedText = null;
  98. cipherParameters = ((ParametersWithIV)parameters).Parameters;
  99. }
  100. else
  101. {
  102. throw new ArgumentException("Invalid parameters specified");
  103. }
  104. this.mac = new byte[macSize];
  105. this.forEncryption = forEncryption;
  106. engine.Init(true, cipherParameters);
  107. counter[0] = 0x01; // defined in standard
  108. if (initialAssociatedText != null)
  109. {
  110. ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
  111. }
  112. }
  113. public virtual string AlgorithmName => engine.AlgorithmName + "/KCCM";
  114. public virtual int GetBlockSize()
  115. {
  116. return engine.GetBlockSize();
  117. }
  118. public virtual IBlockCipher UnderlyingCipher => engine;
  119. public virtual void ProcessAadByte(byte input)
  120. {
  121. associatedText.WriteByte(input);
  122. }
  123. public virtual void ProcessAadBytes(byte[] input, int inOff, int len)
  124. {
  125. associatedText.Write(input, inOff, len);
  126. }
  127. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  128. public virtual void ProcessAadBytes(ReadOnlySpan<byte> input)
  129. {
  130. associatedText.Write(input);
  131. }
  132. #endif
  133. private void ProcessAAD(byte[] assocText, int assocOff, int assocLen, int dataLen)
  134. {
  135. if (assocLen - assocOff < engine.GetBlockSize())
  136. {
  137. throw new ArgumentException("authText buffer too short");
  138. }
  139. if (assocLen % engine.GetBlockSize() != 0)
  140. {
  141. throw new ArgumentException("padding not supported");
  142. }
  143. Array.Copy(nonce, 0, G1, 0, nonce.Length - Nb_ - 1);
  144. intToBytes(dataLen, buffer, 0); // for G1
  145. Array.Copy(buffer, 0, G1, nonce.Length - Nb_ - 1, BYTES_IN_INT);
  146. G1[G1.Length - 1] = getFlag(true, macSize);
  147. engine.ProcessBlock(G1, 0, macBlock, 0);
  148. intToBytes(assocLen, buffer, 0); // for G2
  149. if (assocLen <= engine.GetBlockSize() - Nb_)
  150. {
  151. for (int byteIndex = 0; byteIndex < assocLen; byteIndex++)
  152. {
  153. buffer[byteIndex + Nb_] ^= assocText[assocOff + byteIndex];
  154. }
  155. for (int byteIndex = 0; byteIndex < engine.GetBlockSize(); byteIndex++)
  156. {
  157. macBlock[byteIndex] ^= buffer[byteIndex];
  158. }
  159. engine.ProcessBlock(macBlock, 0, macBlock, 0);
  160. return;
  161. }
  162. for (int byteIndex = 0; byteIndex < engine.GetBlockSize(); byteIndex++)
  163. {
  164. macBlock[byteIndex] ^= buffer[byteIndex];
  165. }
  166. engine.ProcessBlock(macBlock, 0, macBlock, 0);
  167. int authLen = assocLen;
  168. while (authLen != 0)
  169. {
  170. for (int byteIndex = 0; byteIndex < engine.GetBlockSize(); byteIndex++)
  171. {
  172. macBlock[byteIndex] ^= assocText[byteIndex + assocOff];
  173. }
  174. engine.ProcessBlock(macBlock, 0, macBlock, 0);
  175. assocOff += engine.GetBlockSize();
  176. authLen -= engine.GetBlockSize();
  177. }
  178. }
  179. public virtual int ProcessByte(byte input, byte[] output, int outOff)
  180. {
  181. data.WriteByte(input);
  182. return 0;
  183. }
  184. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  185. public virtual int ProcessByte(byte input, Span<byte> output)
  186. {
  187. data.WriteByte(input);
  188. return 0;
  189. }
  190. #endif
  191. public virtual int ProcessBytes(byte[] input, int inOff, int inLen, byte[] output, int outOff)
  192. {
  193. Check.DataLength(input, inOff, inLen, "input buffer too short");
  194. data.Write(input, inOff, inLen);
  195. return 0;
  196. }
  197. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  198. public virtual int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
  199. {
  200. data.Write(input);
  201. return 0;
  202. }
  203. #endif
  204. public int ProcessPacket(byte[] input, int inOff, int len, byte[] output, int outOff)
  205. {
  206. Check.DataLength(input, inOff, len, "input buffer too short");
  207. Check.OutputLength(output, outOff, len, "output buffer too short");
  208. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  209. return ProcessPacket(input.AsSpan(inOff, len), output.AsSpan(outOff));
  210. #else
  211. if (associatedText.Length > 0)
  212. {
  213. byte[] aad = associatedText.GetBuffer();
  214. int aadLen = Convert.ToInt32(associatedText.Length);
  215. int dataLen = Convert.ToInt32(data.Length) - (forEncryption ? 0 : macSize);
  216. ProcessAAD(aad, 0, aadLen, dataLen);
  217. }
  218. if (forEncryption)
  219. {
  220. Check.DataLength(len % engine.GetBlockSize() != 0, "partial blocks not supported");
  221. CalculateMac(input, inOff, len);
  222. engine.ProcessBlock(nonce, 0, s, 0);
  223. int totalLength = len;
  224. while (totalLength > 0)
  225. {
  226. ProcessBlock(input, inOff, output, outOff);
  227. totalLength -= engine.GetBlockSize();
  228. inOff += engine.GetBlockSize();
  229. outOff += engine.GetBlockSize();
  230. }
  231. for (int byteIndex = 0; byteIndex<counter.Length; byteIndex++)
  232. {
  233. s[byteIndex] += counter[byteIndex];
  234. }
  235. engine.ProcessBlock(s, 0, buffer, 0);
  236. for (int byteIndex = 0; byteIndex<macSize; byteIndex++)
  237. {
  238. output[outOff + byteIndex] = (byte)(buffer[byteIndex] ^ macBlock[byteIndex]);
  239. }
  240. Array.Copy(macBlock, 0, mac, 0, macSize);
  241. Reset();
  242. return len + macSize;
  243. }
  244. else
  245. {
  246. Check.DataLength((len - macSize) % engine.GetBlockSize() != 0, "partial blocks not supported");
  247. engine.ProcessBlock(nonce, 0, s, 0);
  248. int blocks = len / engine.GetBlockSize();
  249. for (int blockNum = 0; blockNum<blocks; blockNum++)
  250. {
  251. ProcessBlock(input, inOff, output, outOff);
  252. inOff += engine.GetBlockSize();
  253. outOff += engine.GetBlockSize();
  254. }
  255. if (len > inOff)
  256. {
  257. for (int byteIndex = 0; byteIndex<counter.Length; byteIndex++)
  258. {
  259. s[byteIndex] += counter[byteIndex];
  260. }
  261. engine.ProcessBlock(s, 0, buffer, 0);
  262. for (int byteIndex = 0; byteIndex<macSize; byteIndex++)
  263. {
  264. output[outOff + byteIndex] = (byte)(buffer[byteIndex] ^ input[inOff + byteIndex]);
  265. }
  266. outOff += macSize;
  267. }
  268. for (int byteIndex = 0; byteIndex<counter.Length; byteIndex++)
  269. {
  270. s[byteIndex] += counter[byteIndex];
  271. }
  272. engine.ProcessBlock(s, 0, buffer, 0);
  273. Array.Copy(output, outOff - macSize, buffer, 0, macSize);
  274. CalculateMac(output, 0, outOff - macSize);
  275. Array.Copy(macBlock, 0, mac, 0, macSize);
  276. byte[] calculatedMac = new byte[macSize];
  277. Array.Copy(buffer, 0, calculatedMac, 0, macSize);
  278. if (!Arrays.ConstantTimeAreEqual(mac, calculatedMac))
  279. {
  280. throw new InvalidCipherTextException("mac check failed");
  281. }
  282. Reset();
  283. return len - macSize;
  284. }
  285. #endif
  286. }
  287. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  288. public int ProcessPacket(ReadOnlySpan<byte> input, Span<byte> output)
  289. {
  290. int len = input.Length;
  291. Check.OutputLength(output, len, "output buffer too short");
  292. if (associatedText.Length > 0)
  293. {
  294. byte[] aad = associatedText.GetBuffer();
  295. int aadLen = Convert.ToInt32(associatedText.Length);
  296. int dataLen = Convert.ToInt32(data.Length) - (forEncryption ? 0 : macSize);
  297. ProcessAAD(aad, 0, aadLen, dataLen);
  298. }
  299. int blockSize = engine.GetBlockSize(), index = 0;
  300. if (forEncryption)
  301. {
  302. Check.DataLength(len % blockSize != 0, "partial blocks not supported");
  303. CalculateMac(input);
  304. engine.ProcessBlock(nonce, s);
  305. int totalLength = len;
  306. while (totalLength > 0)
  307. {
  308. ProcessBlock(input[index..], output[index..]);
  309. totalLength -= blockSize;
  310. index += blockSize;
  311. }
  312. for (int byteIndex = 0; byteIndex < counter.Length; byteIndex++)
  313. {
  314. s[byteIndex] += counter[byteIndex];
  315. }
  316. engine.ProcessBlock(s, buffer);
  317. for (int byteIndex = 0; byteIndex < macSize; byteIndex++)
  318. {
  319. output[index + byteIndex] = (byte)(buffer[byteIndex] ^ macBlock[byteIndex]);
  320. }
  321. Array.Copy(macBlock, 0, mac, 0, macSize);
  322. Reset();
  323. return len + macSize;
  324. }
  325. else
  326. {
  327. Check.DataLength((len - macSize) % blockSize != 0, "partial blocks not supported");
  328. engine.ProcessBlock(nonce, 0, s, 0);
  329. int blocks = len / engine.GetBlockSize();
  330. for (int blockNum = 0; blockNum < blocks; blockNum++)
  331. {
  332. ProcessBlock(input[index..], output[index..]);
  333. index += blockSize;
  334. }
  335. if (len > index)
  336. {
  337. for (int byteIndex = 0; byteIndex < counter.Length; byteIndex++)
  338. {
  339. s[byteIndex] += counter[byteIndex];
  340. }
  341. engine.ProcessBlock(s, buffer);
  342. for (int byteIndex = 0; byteIndex < macSize; byteIndex++)
  343. {
  344. output[index + byteIndex] = (byte)(buffer[byteIndex] ^ input[index + byteIndex]);
  345. }
  346. index += macSize;
  347. }
  348. for (int byteIndex = 0; byteIndex < counter.Length; byteIndex++)
  349. {
  350. s[byteIndex] += counter[byteIndex];
  351. }
  352. engine.ProcessBlock(s, buffer);
  353. output[(index - macSize)..index].CopyTo(buffer);
  354. CalculateMac(output[..(index - macSize)]);
  355. Array.Copy(macBlock, 0, mac, 0, macSize);
  356. Span<byte> calculatedMac = macSize <= 64
  357. ? stackalloc byte[macSize]
  358. : new byte[macSize];
  359. calculatedMac.CopyFrom(buffer);
  360. if (!Arrays.ConstantTimeAreEqual(mac.AsSpan(0, macSize), calculatedMac))
  361. throw new InvalidCipherTextException("mac check failed");
  362. Reset();
  363. return len - macSize;
  364. }
  365. }
  366. #endif
  367. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  368. private void CalculateMac(ReadOnlySpan<byte> authText)
  369. {
  370. int blockSize = engine.GetBlockSize();
  371. while (!authText.IsEmpty)
  372. {
  373. for (int byteIndex = 0; byteIndex < blockSize; byteIndex++)
  374. {
  375. macBlock[byteIndex] ^= authText[byteIndex];
  376. }
  377. engine.ProcessBlock(macBlock, macBlock);
  378. authText = authText[blockSize..];
  379. }
  380. }
  381. private void ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
  382. {
  383. for (int byteIndex = 0; byteIndex < counter.Length; byteIndex++)
  384. {
  385. s[byteIndex] += counter[byteIndex];
  386. }
  387. engine.ProcessBlock(s, buffer);
  388. int blockSize = engine.GetBlockSize();
  389. for (int byteIndex = 0; byteIndex < blockSize; byteIndex++)
  390. {
  391. output[byteIndex] = (byte)(buffer[byteIndex] ^ input[byteIndex]);
  392. }
  393. }
  394. #else
  395. private void CalculateMac(byte[] authText, int authOff, int len)
  396. {
  397. int blockSize = engine.GetBlockSize();
  398. int totalLen = len;
  399. while (totalLen > 0)
  400. {
  401. for (int byteIndex = 0; byteIndex < blockSize; byteIndex++)
  402. {
  403. macBlock[byteIndex] ^= authText[authOff + byteIndex];
  404. }
  405. engine.ProcessBlock(macBlock, 0, macBlock, 0);
  406. totalLen -= blockSize;
  407. authOff += blockSize;
  408. }
  409. }
  410. private void ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
  411. {
  412. for (int byteIndex = 0; byteIndex < counter.Length; byteIndex++)
  413. {
  414. s[byteIndex] += counter[byteIndex];
  415. }
  416. engine.ProcessBlock(s, 0, buffer, 0);
  417. for (int byteIndex = 0; byteIndex < engine.GetBlockSize(); byteIndex++)
  418. {
  419. output[outOff + byteIndex] = (byte)(buffer[byteIndex] ^ input[inOff + byteIndex]);
  420. }
  421. }
  422. #endif
  423. public virtual int DoFinal(byte[] output, int outOff)
  424. {
  425. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  426. return DoFinal(output.AsSpan(outOff));
  427. #else
  428. byte[] buf = data.GetBuffer();
  429. int bufLen = Convert.ToInt32(data.Length);
  430. int len = ProcessPacket(buf, 0, bufLen, output, outOff);
  431. Reset();
  432. return len;
  433. #endif
  434. }
  435. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  436. public virtual int DoFinal(Span<byte> output)
  437. {
  438. byte[] buf = data.GetBuffer();
  439. int bufLen = Convert.ToInt32(data.Length);
  440. int len = ProcessPacket(buf.AsSpan(0, bufLen), output);
  441. Reset();
  442. return len;
  443. }
  444. #endif
  445. public virtual byte[] GetMac()
  446. {
  447. return Arrays.Clone(mac);
  448. }
  449. public virtual int GetUpdateOutputSize(int len)
  450. {
  451. return len;
  452. }
  453. public virtual int GetOutputSize(int len)
  454. {
  455. return len + macSize;
  456. }
  457. public virtual void Reset()
  458. {
  459. Arrays.Fill(G1, (byte)0);
  460. Arrays.Fill(buffer, (byte)0);
  461. Arrays.Fill(counter, (byte)0);
  462. Arrays.Fill(macBlock, (byte)0);
  463. counter[0] = 0x01;
  464. data.SetLength(0);
  465. associatedText.SetLength(0);
  466. if (initialAssociatedText != null)
  467. {
  468. ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
  469. }
  470. }
  471. private void intToBytes(
  472. int num,
  473. byte[] outBytes,
  474. int outOff)
  475. {
  476. outBytes[outOff + 3] = (byte)(num >> 24);
  477. outBytes[outOff + 2] = (byte)(num >> 16);
  478. outBytes[outOff + 1] = (byte)(num >> 8);
  479. outBytes[outOff] = (byte)num;
  480. }
  481. private byte getFlag(bool authTextPresents, int macSize)
  482. {
  483. StringBuilder flagByte = new StringBuilder();
  484. if (authTextPresents)
  485. {
  486. flagByte.Append("1");
  487. }
  488. else
  489. {
  490. flagByte.Append("0");
  491. }
  492. switch (macSize)
  493. {
  494. case 8:
  495. flagByte.Append("010"); // binary 2
  496. break;
  497. case 16:
  498. flagByte.Append("011"); // binary 3
  499. break;
  500. case 32:
  501. flagByte.Append("100"); // binary 4
  502. break;
  503. case 48:
  504. flagByte.Append("101"); // binary 5
  505. break;
  506. case 64:
  507. flagByte.Append("110"); // binary 6
  508. break;
  509. }
  510. string binaryNb = Convert.ToString(Nb_ - 1, 2);
  511. while (binaryNb.Length < 4)
  512. {
  513. binaryNb = new StringBuilder(binaryNb).Insert(0, "0").ToString();
  514. }
  515. flagByte.Append(binaryNb);
  516. return (byte)Convert.ToInt32(flagByte.ToString(), 2);
  517. }
  518. }
  519. }
  520. #pragma warning restore
  521. #endif