IdeaEngine.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  5. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  6. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines
  7. {
  8. /**
  9. * A class that provides a basic International Data Encryption Algorithm (IDEA) engine.
  10. * <p>
  11. * This implementation is based on the "HOWTO: INTERNATIONAL DATA ENCRYPTION ALGORITHM"
  12. * implementation summary by Fauzan Mirza (F.U.Mirza@sheffield.ac.uk). (barring 1 typo at the
  13. * end of the MulInv function!).
  14. * </p>
  15. * <p>
  16. * It can be found at ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/idea/
  17. * </p>
  18. * <p>
  19. * Note: This algorithm was patented in the USA, Japan and Europe. These patents expired in 2011/2012.
  20. * </p>
  21. */
  22. public class IdeaEngine
  23. : IBlockCipher
  24. {
  25. private const int BLOCK_SIZE = 8;
  26. private int[] workingKey;
  27. /**
  28. * standard constructor.
  29. */
  30. public IdeaEngine()
  31. {
  32. }
  33. /**
  34. * initialise an IDEA cipher.
  35. *
  36. * @param forEncryption whether or not we are for encryption.
  37. * @param parameters the parameters required to set up the cipher.
  38. * @exception ArgumentException if the parameters argument is
  39. * inappropriate.
  40. */
  41. public virtual void Init(
  42. bool forEncryption,
  43. ICipherParameters parameters)
  44. {
  45. if (!(parameters is KeyParameter))
  46. throw new ArgumentException("invalid parameter passed to IDEA init - " + Org.BouncyCastle.Utilities.Platform.GetTypeName(parameters));
  47. workingKey = GenerateWorkingKey(forEncryption,
  48. ((KeyParameter)parameters).GetKey());
  49. }
  50. public virtual string AlgorithmName
  51. {
  52. get { return "IDEA"; }
  53. }
  54. public virtual int GetBlockSize()
  55. {
  56. return BLOCK_SIZE;
  57. }
  58. public virtual int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
  59. {
  60. if (workingKey == null)
  61. throw new InvalidOperationException("IDEA engine not initialised");
  62. Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
  63. Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
  64. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  65. IdeaFunc(workingKey, input.AsSpan(inOff), output.AsSpan(outOff));
  66. #else
  67. IdeaFunc(workingKey, input, inOff, output, outOff);
  68. #endif
  69. return BLOCK_SIZE;
  70. }
  71. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  72. public virtual int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
  73. {
  74. if (workingKey == null)
  75. throw new InvalidOperationException("IDEA engine not initialised");
  76. Check.DataLength(input, BLOCK_SIZE, "input buffer too short");
  77. Check.OutputLength(output, BLOCK_SIZE, "output buffer too short");
  78. IdeaFunc(workingKey, input, output);
  79. return BLOCK_SIZE;
  80. }
  81. #endif
  82. private static readonly int MASK = 0xffff;
  83. private static readonly int BASE = 0x10001;
  84. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  85. private int BytesToWord(ReadOnlySpan<byte> input)
  86. {
  87. return ((input[0] << 8) & 0xff00) + (input[1] & 0xff);
  88. }
  89. private void WordToBytes(int word, Span<byte> output)
  90. {
  91. output[0] = (byte)((uint)word >> 8);
  92. output[1] = (byte)word;
  93. }
  94. #else
  95. private int BytesToWord(byte[] input, int inOff)
  96. {
  97. return ((input[inOff] << 8) & 0xff00) + (input[inOff + 1] & 0xff);
  98. }
  99. private void WordToBytes(int word, byte[] outBytes, int outOff)
  100. {
  101. outBytes[outOff] = (byte)((uint) word >> 8);
  102. outBytes[outOff + 1] = (byte)word;
  103. }
  104. #endif
  105. /**
  106. * return x = x * y where the multiplication is done modulo
  107. * 65537 (0x10001) (as defined in the IDEA specification) and
  108. * a zero input is taken to be 65536 (0x10000).
  109. *
  110. * @param x the x value
  111. * @param y the y value
  112. * @return x = x * y
  113. */
  114. private int Mul(
  115. int x,
  116. int y)
  117. {
  118. if (x == 0)
  119. {
  120. x = (BASE - y);
  121. }
  122. else if (y == 0)
  123. {
  124. x = (BASE - x);
  125. }
  126. else
  127. {
  128. int p = x * y;
  129. y = p & MASK;
  130. x = (int) ((uint) p >> 16);
  131. x = y - x + ((y < x) ? 1 : 0);
  132. }
  133. return x & MASK;
  134. }
  135. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  136. private void IdeaFunc(int[] workingKey, ReadOnlySpan<byte> input, Span<byte> output)
  137. {
  138. int x0 = BytesToWord(input);
  139. int x1 = BytesToWord(input[2..]);
  140. int x2 = BytesToWord(input[4..]);
  141. int x3 = BytesToWord(input[6..]);
  142. int keyOff = 0, t0, t1;
  143. for (int round = 0; round < 8; round++)
  144. {
  145. x0 = Mul(x0, workingKey[keyOff++]);
  146. x1 += workingKey[keyOff++];
  147. x1 &= MASK;
  148. x2 += workingKey[keyOff++];
  149. x2 &= MASK;
  150. x3 = Mul(x3, workingKey[keyOff++]);
  151. t0 = x1;
  152. t1 = x2;
  153. x2 ^= x0;
  154. x1 ^= x3;
  155. x2 = Mul(x2, workingKey[keyOff++]);
  156. x1 += x2;
  157. x1 &= MASK;
  158. x1 = Mul(x1, workingKey[keyOff++]);
  159. x2 += x1;
  160. x2 &= MASK;
  161. x0 ^= x1;
  162. x3 ^= x2;
  163. x1 ^= t1;
  164. x2 ^= t0;
  165. }
  166. WordToBytes(Mul(x0, workingKey[keyOff++]), output);
  167. WordToBytes(x2 + workingKey[keyOff++], output[2..]); /* NB: Order */
  168. WordToBytes(x1 + workingKey[keyOff++], output[4..]);
  169. WordToBytes(Mul(x3, workingKey[keyOff]), output[6..]);
  170. }
  171. #else
  172. private void IdeaFunc(int[] workingKey, byte[] input, int inOff, byte[] outBytes, int outOff)
  173. {
  174. int x0 = BytesToWord(input, inOff);
  175. int x1 = BytesToWord(input, inOff + 2);
  176. int x2 = BytesToWord(input, inOff + 4);
  177. int x3 = BytesToWord(input, inOff + 6);
  178. int keyOff = 0, t0, t1;
  179. for (int round = 0; round < 8; round++)
  180. {
  181. x0 = Mul(x0, workingKey[keyOff++]);
  182. x1 += workingKey[keyOff++];
  183. x1 &= MASK;
  184. x2 += workingKey[keyOff++];
  185. x2 &= MASK;
  186. x3 = Mul(x3, workingKey[keyOff++]);
  187. t0 = x1;
  188. t1 = x2;
  189. x2 ^= x0;
  190. x1 ^= x3;
  191. x2 = Mul(x2, workingKey[keyOff++]);
  192. x1 += x2;
  193. x1 &= MASK;
  194. x1 = Mul(x1, workingKey[keyOff++]);
  195. x2 += x1;
  196. x2 &= MASK;
  197. x0 ^= x1;
  198. x3 ^= x2;
  199. x1 ^= t1;
  200. x2 ^= t0;
  201. }
  202. WordToBytes(Mul(x0, workingKey[keyOff++]), outBytes, outOff);
  203. WordToBytes(x2 + workingKey[keyOff++], outBytes, outOff + 2); /* NB: Order */
  204. WordToBytes(x1 + workingKey[keyOff++], outBytes, outOff + 4);
  205. WordToBytes(Mul(x3, workingKey[keyOff]), outBytes, outOff + 6);
  206. }
  207. #endif
  208. /**
  209. * The following function is used to expand the user key to the encryption
  210. * subkey. The first 16 bytes are the user key, and the rest of the subkey
  211. * is calculated by rotating the previous 16 bytes by 25 bits to the left,
  212. * and so on until the subkey is completed.
  213. */
  214. private int[] ExpandKey(byte[] uKey)
  215. {
  216. int[] key = new int[52];
  217. if (uKey.Length < 16)
  218. {
  219. byte[] tmp = new byte[16];
  220. Array.Copy(uKey, 0, tmp, tmp.Length - uKey.Length, uKey.Length);
  221. uKey = tmp;
  222. }
  223. for (int i = 0; i < 8; i++)
  224. {
  225. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  226. key[i] = BytesToWord(uKey[(i * 2)..]);
  227. #else
  228. key[i] = BytesToWord(uKey, i * 2);
  229. #endif
  230. }
  231. for (int i = 8; i < 52; i++)
  232. {
  233. if ((i & 7) < 6)
  234. {
  235. key[i] = ((key[i - 7] & 127) << 9 | key[i - 6] >> 7) & MASK;
  236. }
  237. else if ((i & 7) == 6)
  238. {
  239. key[i] = ((key[i - 7] & 127) << 9 | key[i - 14] >> 7) & MASK;
  240. }
  241. else
  242. {
  243. key[i] = ((key[i - 15] & 127) << 9 | key[i - 14] >> 7) & MASK;
  244. }
  245. }
  246. return key;
  247. }
  248. /**
  249. * This function computes multiplicative inverse using Euclid's Greatest
  250. * Common Divisor algorithm. Zero and one are self inverse.
  251. * <p>
  252. * i.e. x * MulInv(x) == 1 (modulo BASE)
  253. * </p>
  254. */
  255. private int MulInv(
  256. int x)
  257. {
  258. int t0, t1, q, y;
  259. if (x < 2)
  260. {
  261. return x;
  262. }
  263. t0 = 1;
  264. t1 = BASE / x;
  265. y = BASE % x;
  266. while (y != 1)
  267. {
  268. q = x / y;
  269. x = x % y;
  270. t0 = (t0 + (t1 * q)) & MASK;
  271. if (x == 1)
  272. {
  273. return t0;
  274. }
  275. q = y / x;
  276. y = y % x;
  277. t1 = (t1 + (t0 * q)) & MASK;
  278. }
  279. return (1 - t1) & MASK;
  280. }
  281. /**
  282. * Return the additive inverse of x.
  283. * <p>
  284. * i.e. x + AddInv(x) == 0
  285. * </p>
  286. */
  287. int AddInv(
  288. int x)
  289. {
  290. return (0 - x) & MASK;
  291. }
  292. /**
  293. * The function to invert the encryption subkey to the decryption subkey.
  294. * It also involves the multiplicative inverse and the additive inverse functions.
  295. */
  296. private int[] InvertKey(
  297. int[] inKey)
  298. {
  299. int t1, t2, t3, t4;
  300. int p = 52; /* We work backwards */
  301. int[] key = new int[52];
  302. int inOff = 0;
  303. t1 = MulInv(inKey[inOff++]);
  304. t2 = AddInv(inKey[inOff++]);
  305. t3 = AddInv(inKey[inOff++]);
  306. t4 = MulInv(inKey[inOff++]);
  307. key[--p] = t4;
  308. key[--p] = t3;
  309. key[--p] = t2;
  310. key[--p] = t1;
  311. for (int round = 1; round < 8; round++)
  312. {
  313. t1 = inKey[inOff++];
  314. t2 = inKey[inOff++];
  315. key[--p] = t2;
  316. key[--p] = t1;
  317. t1 = MulInv(inKey[inOff++]);
  318. t2 = AddInv(inKey[inOff++]);
  319. t3 = AddInv(inKey[inOff++]);
  320. t4 = MulInv(inKey[inOff++]);
  321. key[--p] = t4;
  322. key[--p] = t2; /* NB: Order */
  323. key[--p] = t3;
  324. key[--p] = t1;
  325. }
  326. t1 = inKey[inOff++];
  327. t2 = inKey[inOff++];
  328. key[--p] = t2;
  329. key[--p] = t1;
  330. t1 = MulInv(inKey[inOff++]);
  331. t2 = AddInv(inKey[inOff++]);
  332. t3 = AddInv(inKey[inOff++]);
  333. t4 = MulInv(inKey[inOff]);
  334. key[--p] = t4;
  335. key[--p] = t3;
  336. key[--p] = t2;
  337. key[--p] = t1;
  338. return key;
  339. }
  340. private int[] GenerateWorkingKey(
  341. bool forEncryption,
  342. byte[] userKey)
  343. {
  344. if (forEncryption)
  345. {
  346. return ExpandKey(userKey);
  347. }
  348. else
  349. {
  350. return InvertKey(ExpandKey(userKey));
  351. }
  352. }
  353. }
  354. }
  355. #pragma warning restore
  356. #endif