KCcmBlockCipher.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  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 BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  7. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  8. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
  9. {
  10. public class KCcmBlockCipher: IAeadBlockCipher
  11. {
  12. private static readonly int BYTES_IN_INT = 4;
  13. private static readonly int BITS_IN_BYTE = 8;
  14. private static readonly int MAX_MAC_BIT_LENGTH = 512;
  15. private static readonly int MIN_MAC_BIT_LENGTH = 64;
  16. private IBlockCipher engine;
  17. private int macSize;
  18. private bool forEncryption;
  19. private byte[] initialAssociatedText;
  20. private byte[] mac;
  21. private byte[] macBlock;
  22. private byte[] nonce;
  23. private byte[] G1;
  24. private byte[] buffer;
  25. private byte[] s;
  26. private byte[] counter;
  27. private readonly MemoryStream associatedText = new MemoryStream();
  28. private readonly MemoryStream data = new MemoryStream();
  29. /*
  30. *
  31. *
  32. */
  33. private int Nb_ = 4;
  34. private void setNb(int Nb)
  35. {
  36. if (Nb == 4 || Nb == 6 || Nb == 8)
  37. {
  38. Nb_ = Nb;
  39. }
  40. else
  41. {
  42. throw new ArgumentException("Nb = 4 is recommended by DSTU7624 but can be changed to only 6 or 8 in this implementation");
  43. }
  44. }
  45. /// <summary>
  46. /// Base constructor. Nb value is set to 4.
  47. /// </summary>
  48. /// <param name="engine">base cipher to use under CCM.</param>
  49. public KCcmBlockCipher(IBlockCipher engine): this(engine, 4)
  50. {
  51. }
  52. /// <summary>
  53. /// Constructor allowing Nb configuration.
  54. ///
  55. /// Nb is a parameter specified in CCM mode of DSTU7624 standard.
  56. /// This parameter specifies maximum possible length of input.It should
  57. /// be calculated as follows: Nb = 1 / 8 * (-3 + log[2]Nmax) + 1,
  58. /// where Nmax - length of input message in bits.For practical reasons
  59. /// Nmax usually less than 4Gb, e.g. for Nmax = 2^32 - 1, Nb = 4.
  60. /// </summary>
  61. /// <param name="engine">base cipher to use under CCM.</param>
  62. /// <param name="Nb">Nb value to use.</param>
  63. public KCcmBlockCipher(IBlockCipher engine, int Nb)
  64. {
  65. this.engine = engine;
  66. this.macSize = engine.GetBlockSize();
  67. this.nonce = new byte[engine.GetBlockSize()];
  68. this.initialAssociatedText = new byte[engine.GetBlockSize()];
  69. this.mac = new byte[engine.GetBlockSize()];
  70. this.macBlock = new byte[engine.GetBlockSize()];
  71. this.G1 = new byte[engine.GetBlockSize()];
  72. this.buffer = new byte[engine.GetBlockSize()];
  73. this.s = new byte[engine.GetBlockSize()];
  74. this.counter = new byte[engine.GetBlockSize()];
  75. setNb(Nb);
  76. }
  77. public virtual void Init(bool forEncryption, ICipherParameters parameters)
  78. {
  79. ICipherParameters cipherParameters;
  80. if (parameters is AeadParameters)
  81. {
  82. AeadParameters param = (AeadParameters)parameters;
  83. if (param.MacSize > MAX_MAC_BIT_LENGTH || param.MacSize < MIN_MAC_BIT_LENGTH || param.MacSize % 8 != 0)
  84. {
  85. throw new ArgumentException("Invalid mac size specified");
  86. }
  87. nonce = param.GetNonce();
  88. macSize = param.MacSize / BITS_IN_BYTE;
  89. initialAssociatedText = param.GetAssociatedText();
  90. cipherParameters = param.Key;
  91. }
  92. else if (parameters is ParametersWithIV)
  93. {
  94. nonce = ((ParametersWithIV)parameters).GetIV();
  95. macSize = engine.GetBlockSize(); // use default blockSize for MAC if it is not specified
  96. initialAssociatedText = null;
  97. cipherParameters = ((ParametersWithIV)parameters).Parameters;
  98. }
  99. else
  100. {
  101. throw new ArgumentException("Invalid parameters specified");
  102. }
  103. this.mac = new byte[macSize];
  104. this.forEncryption = forEncryption;
  105. engine.Init(true, cipherParameters);
  106. counter[0] = 0x01; // defined in standard
  107. if (initialAssociatedText != null)
  108. {
  109. ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
  110. }
  111. }
  112. public virtual String AlgorithmName
  113. {
  114. get
  115. {
  116. return engine.AlgorithmName + "/KCCM";
  117. }
  118. }
  119. public virtual int GetBlockSize()
  120. {
  121. return engine.GetBlockSize();
  122. }
  123. public virtual IBlockCipher GetUnderlyingCipher()
  124. {
  125. return engine;
  126. }
  127. public virtual void ProcessAadByte(byte input)
  128. {
  129. associatedText.WriteByte(input);
  130. }
  131. public virtual void ProcessAadBytes(byte[] input, int inOff, int len)
  132. {
  133. associatedText.Write(input, inOff, len);
  134. }
  135. private void ProcessAAD(byte[] assocText, int assocOff, int assocLen, int dataLen)
  136. {
  137. if (assocLen - assocOff < engine.GetBlockSize())
  138. {
  139. throw new ArgumentException("authText buffer too short");
  140. }
  141. if (assocLen % engine.GetBlockSize() != 0)
  142. {
  143. throw new ArgumentException("padding not supported");
  144. }
  145. Array.Copy(nonce, 0, G1, 0, nonce.Length - Nb_ - 1);
  146. intToBytes(dataLen, buffer, 0); // for G1
  147. Array.Copy(buffer, 0, G1, nonce.Length - Nb_ - 1, BYTES_IN_INT);
  148. G1[G1.Length - 1] = getFlag(true, macSize);
  149. engine.ProcessBlock(G1, 0, macBlock, 0);
  150. intToBytes(assocLen, buffer, 0); // for G2
  151. if (assocLen <= engine.GetBlockSize() - Nb_)
  152. {
  153. for (int byteIndex = 0; byteIndex < assocLen; byteIndex++)
  154. {
  155. buffer[byteIndex + Nb_] ^= assocText[assocOff + byteIndex];
  156. }
  157. for (int byteIndex = 0; byteIndex < engine.GetBlockSize(); byteIndex++)
  158. {
  159. macBlock[byteIndex] ^= buffer[byteIndex];
  160. }
  161. engine.ProcessBlock(macBlock, 0, macBlock, 0);
  162. return;
  163. }
  164. for (int byteIndex = 0; byteIndex < engine.GetBlockSize(); byteIndex++)
  165. {
  166. macBlock[byteIndex] ^= buffer[byteIndex];
  167. }
  168. engine.ProcessBlock(macBlock, 0, macBlock, 0);
  169. int authLen = assocLen;
  170. while (authLen != 0)
  171. {
  172. for (int byteIndex = 0; byteIndex < engine.GetBlockSize(); byteIndex++)
  173. {
  174. macBlock[byteIndex] ^= assocText[byteIndex + assocOff];
  175. }
  176. engine.ProcessBlock(macBlock, 0, macBlock, 0);
  177. assocOff += engine.GetBlockSize();
  178. authLen -= engine.GetBlockSize();
  179. }
  180. }
  181. public virtual int ProcessByte(byte input, byte[] output, int outOff)
  182. {
  183. data.WriteByte(input);
  184. return 0;
  185. }
  186. public virtual int ProcessBytes(byte[] input, int inOff, int inLen, byte[] output, int outOff)
  187. {
  188. Check.DataLength(input, inOff, inLen, "input buffer too short");
  189. data.Write(input, inOff, inLen);
  190. return 0;
  191. }
  192. public int ProcessPacket(byte[] input, int inOff, int len, byte[] output, int outOff)
  193. {
  194. Check.DataLength(input, inOff, len, "input buffer too short");
  195. Check.OutputLength(output, outOff, len, "output buffer too short");
  196. if (associatedText.Length > 0)
  197. {
  198. #if PORTABLE || NETFX_CORE
  199. byte[] aad = associatedText.ToArray();
  200. int aadLen = aad.Length;
  201. #else
  202. byte[] aad = associatedText.GetBuffer();
  203. int aadLen = (int)associatedText.Length;
  204. #endif
  205. int dataLen = forEncryption ? (int)data.Length : ((int)data.Length - macSize);
  206. ProcessAAD(aad, 0, aadLen, dataLen);
  207. }
  208. if (forEncryption)
  209. {
  210. Check.DataLength(len % engine.GetBlockSize() != 0, "partial blocks not supported");
  211. CalculateMac(input, inOff, len);
  212. engine.ProcessBlock(nonce, 0, s, 0);
  213. int totalLength = len;
  214. while (totalLength > 0)
  215. {
  216. ProcessBlock(input, inOff, len, output, outOff);
  217. totalLength -= engine.GetBlockSize();
  218. inOff += engine.GetBlockSize();
  219. outOff += engine.GetBlockSize();
  220. }
  221. for (int byteIndex = 0; byteIndex<counter.Length; byteIndex++)
  222. {
  223. s[byteIndex] += counter[byteIndex];
  224. }
  225. engine.ProcessBlock(s, 0, buffer, 0);
  226. for (int byteIndex = 0; byteIndex<macSize; byteIndex++)
  227. {
  228. output[outOff + byteIndex] = (byte)(buffer[byteIndex] ^ macBlock[byteIndex]);
  229. }
  230. Array.Copy(macBlock, 0, mac, 0, macSize);
  231. Reset();
  232. return len + macSize;
  233. }
  234. else
  235. {
  236. Check.DataLength((len - macSize) % engine.GetBlockSize() != 0, "partial blocks not supported");
  237. engine.ProcessBlock(nonce, 0, s, 0);
  238. int blocks = len / engine.GetBlockSize();
  239. for (int blockNum = 0; blockNum<blocks; blockNum++)
  240. {
  241. ProcessBlock(input, inOff, len, output, outOff);
  242. inOff += engine.GetBlockSize();
  243. outOff += engine.GetBlockSize();
  244. }
  245. if (len > inOff)
  246. {
  247. for (int byteIndex = 0; byteIndex<counter.Length; byteIndex++)
  248. {
  249. s[byteIndex] += counter[byteIndex];
  250. }
  251. engine.ProcessBlock(s, 0, buffer, 0);
  252. for (int byteIndex = 0; byteIndex<macSize; byteIndex++)
  253. {
  254. output[outOff + byteIndex] = (byte)(buffer[byteIndex] ^ input[inOff + byteIndex]);
  255. }
  256. outOff += macSize;
  257. }
  258. for (int byteIndex = 0; byteIndex<counter.Length; byteIndex++)
  259. {
  260. s[byteIndex] += counter[byteIndex];
  261. }
  262. engine.ProcessBlock(s, 0, buffer, 0);
  263. Array.Copy(output, outOff - macSize, buffer, 0, macSize);
  264. CalculateMac(output, 0, outOff - macSize);
  265. Array.Copy(macBlock, 0, mac, 0, macSize);
  266. byte[] calculatedMac = new byte[macSize];
  267. Array.Copy(buffer, 0, calculatedMac, 0, macSize);
  268. if (!Arrays.ConstantTimeAreEqual(mac, calculatedMac))
  269. {
  270. throw new InvalidCipherTextException("mac check failed");
  271. }
  272. Reset();
  273. return len - macSize;
  274. }
  275. }
  276. private void ProcessBlock(byte[] input, int inOff, int len, byte[] output, int outOff)
  277. {
  278. for (int byteIndex = 0; byteIndex < counter.Length; byteIndex++)
  279. {
  280. s[byteIndex] += counter[byteIndex];
  281. }
  282. engine.ProcessBlock(s, 0, buffer, 0);
  283. for (int byteIndex = 0; byteIndex < engine.GetBlockSize(); byteIndex++)
  284. {
  285. output[outOff + byteIndex] = (byte)(buffer[byteIndex] ^ input[inOff + byteIndex]);
  286. }
  287. }
  288. private void CalculateMac(byte[] authText, int authOff, int len)
  289. {
  290. int totalLen = len;
  291. while (totalLen > 0)
  292. {
  293. for (int byteIndex = 0; byteIndex < engine.GetBlockSize(); byteIndex++)
  294. {
  295. macBlock[byteIndex] ^= authText[authOff + byteIndex];
  296. }
  297. engine.ProcessBlock(macBlock, 0, macBlock, 0);
  298. totalLen -= engine.GetBlockSize();
  299. authOff += engine.GetBlockSize();
  300. }
  301. }
  302. public virtual int DoFinal(byte[] output, int outOff)
  303. {
  304. #if PORTABLE || NETFX_CORE
  305. byte[] buf = data.ToArray();
  306. int bufLen = buf.Length;
  307. #else
  308. byte[] buf = data.GetBuffer();
  309. int bufLen = (int)data.Length;
  310. #endif
  311. int len = ProcessPacket(buf, 0, bufLen, output, outOff);
  312. Reset();
  313. return len;
  314. }
  315. public virtual byte[] GetMac()
  316. {
  317. return Arrays.Clone(mac);
  318. }
  319. public virtual int GetUpdateOutputSize(int len)
  320. {
  321. return len;
  322. }
  323. public virtual int GetOutputSize(int len)
  324. {
  325. return len + macSize;
  326. }
  327. public virtual void Reset()
  328. {
  329. Arrays.Fill(G1, (byte)0);
  330. Arrays.Fill(buffer, (byte)0);
  331. Arrays.Fill(counter, (byte)0);
  332. Arrays.Fill(macBlock, (byte)0);
  333. counter[0] = 0x01;
  334. data.SetLength(0);
  335. associatedText.SetLength(0);
  336. if (initialAssociatedText != null)
  337. {
  338. ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
  339. }
  340. }
  341. private void intToBytes(
  342. int num,
  343. byte[] outBytes,
  344. int outOff)
  345. {
  346. outBytes[outOff + 3] = (byte)(num >> 24);
  347. outBytes[outOff + 2] = (byte)(num >> 16);
  348. outBytes[outOff + 1] = (byte)(num >> 8);
  349. outBytes[outOff] = (byte)num;
  350. }
  351. private byte getFlag(bool authTextPresents, int macSize)
  352. {
  353. StringBuilder flagByte = new StringBuilder();
  354. if (authTextPresents)
  355. {
  356. flagByte.Append("1");
  357. }
  358. else
  359. {
  360. flagByte.Append("0");
  361. }
  362. switch (macSize)
  363. {
  364. case 8:
  365. flagByte.Append("010"); // binary 2
  366. break;
  367. case 16:
  368. flagByte.Append("011"); // binary 3
  369. break;
  370. case 32:
  371. flagByte.Append("100"); // binary 4
  372. break;
  373. case 48:
  374. flagByte.Append("101"); // binary 5
  375. break;
  376. case 64:
  377. flagByte.Append("110"); // binary 6
  378. break;
  379. }
  380. String binaryNb = Convert.ToString(Nb_ - 1, 2);
  381. while (binaryNb.Length < 4)
  382. {
  383. binaryNb = new StringBuilder(binaryNb).Insert(0, "0").ToString();
  384. }
  385. flagByte.Append(binaryNb);
  386. return (byte)Convert.ToInt32(flagByte.ToString(), 2);
  387. }
  388. }
  389. }
  390. #pragma warning restore
  391. #endif