OpenPgpCfbBlockCipher.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  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. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
  6. {
  7. /**
  8. * Implements OpenPGP's rather strange version of Cipher-FeedBack (CFB) mode
  9. * on top of a simple cipher. This class assumes the IV has been prepended
  10. * to the data stream already, and just accomodates the reset after
  11. * (blockSize + 2) bytes have been read.
  12. * <p>
  13. * For further info see <a href="http://www.ietf.org/rfc/rfc2440.html">RFC 2440</a>.
  14. * </p>
  15. */
  16. public class OpenPgpCfbBlockCipher
  17. : IBlockCipherMode
  18. {
  19. private byte[] IV;
  20. private byte[] FR;
  21. private byte[] FRE;
  22. private readonly IBlockCipher cipher;
  23. private readonly int blockSize;
  24. private int count;
  25. private bool forEncryption;
  26. /**
  27. * Basic constructor.
  28. *
  29. * @param cipher the block cipher to be used as the basis of the
  30. * feedback mode.
  31. */
  32. public OpenPgpCfbBlockCipher(
  33. IBlockCipher cipher)
  34. {
  35. this.cipher = cipher;
  36. this.blockSize = cipher.GetBlockSize();
  37. this.IV = new byte[blockSize];
  38. this.FR = new byte[blockSize];
  39. this.FRE = new byte[blockSize];
  40. }
  41. /**
  42. * return the underlying block cipher that we are wrapping.
  43. *
  44. * @return the underlying block cipher that we are wrapping.
  45. */
  46. public IBlockCipher UnderlyingCipher => cipher;
  47. /**
  48. * return the algorithm name and mode.
  49. *
  50. * @return the name of the underlying algorithm followed by "/PGPCFB"
  51. * and the block size in bits.
  52. */
  53. public string AlgorithmName
  54. {
  55. get { return cipher.AlgorithmName + "/OpenPGPCFB"; }
  56. }
  57. public bool IsPartialBlockOkay
  58. {
  59. get { return true; }
  60. }
  61. /**
  62. * return the block size we are operating at.
  63. *
  64. * @return the block size we are operating at (in bytes).
  65. */
  66. public int GetBlockSize()
  67. {
  68. return cipher.GetBlockSize();
  69. }
  70. public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
  71. {
  72. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  73. return forEncryption
  74. ? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff))
  75. : DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
  76. #else
  77. return forEncryption
  78. ? EncryptBlock(input, inOff, output, outOff)
  79. : DecryptBlock(input, inOff, output, outOff);
  80. #endif
  81. }
  82. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  83. public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
  84. {
  85. return forEncryption
  86. ? EncryptBlock(input, output)
  87. : DecryptBlock(input, output);
  88. }
  89. #endif
  90. /**
  91. * reset the chaining vector back to the IV and reset the underlying
  92. * cipher.
  93. */
  94. public void Reset()
  95. {
  96. count = 0;
  97. Array.Copy(IV, 0, FR, 0, FR.Length);
  98. }
  99. /**
  100. * Initialise the cipher and, possibly, the initialisation vector (IV).
  101. * If an IV isn't passed as part of the parameter, the IV will be all zeros.
  102. * An IV which is too short is handled in FIPS compliant fashion.
  103. *
  104. * @param forEncryption if true the cipher is initialised for
  105. * encryption, if false for decryption.
  106. * @param parameters the key and other data required by the cipher.
  107. * @exception ArgumentException if the parameters argument is
  108. * inappropriate.
  109. */
  110. public void Init(bool forEncryption, ICipherParameters parameters)
  111. {
  112. this.forEncryption = forEncryption;
  113. if (parameters is ParametersWithIV ivParam)
  114. {
  115. byte[] iv = ivParam.GetIV();
  116. if (iv.Length < IV.Length)
  117. {
  118. // prepend the supplied IV with zeros (per FIPS PUB 81)
  119. Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length);
  120. for (int i = 0; i < IV.Length - iv.Length; i++)
  121. {
  122. IV[i] = 0;
  123. }
  124. }
  125. else
  126. {
  127. Array.Copy(iv, 0, IV, 0, IV.Length);
  128. }
  129. parameters = ivParam.Parameters;
  130. }
  131. Reset();
  132. cipher.Init(true, parameters);
  133. }
  134. /**
  135. * Encrypt one byte of data according to CFB mode.
  136. * @param data the byte to encrypt
  137. * @param blockOff offset in the current block
  138. * @returns the encrypted byte
  139. */
  140. private byte EncryptByte(byte data, int blockOff)
  141. {
  142. return (byte)(FRE[blockOff] ^ data);
  143. }
  144. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  145. private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
  146. {
  147. Check.DataLength(input, blockSize, "input buffer too short");
  148. Check.OutputLength(output, blockSize, "output buffer too short");
  149. if (count > blockSize)
  150. {
  151. FR[blockSize - 2] = output[0] = EncryptByte(input[0], blockSize - 2);
  152. FR[blockSize - 1] = output[1] = EncryptByte(input[1], blockSize - 1);
  153. cipher.ProcessBlock(FR, FRE);
  154. for (int n = 2; n < blockSize; n++)
  155. {
  156. FR[n - 2] = output[n] = EncryptByte(input[n], n - 2);
  157. }
  158. }
  159. else if (count == 0)
  160. {
  161. cipher.ProcessBlock(FR, FRE);
  162. for (int n = 0; n < blockSize; n++)
  163. {
  164. FR[n] = output[n] = EncryptByte(input[n], n);
  165. }
  166. count += blockSize;
  167. }
  168. else if (count == blockSize)
  169. {
  170. cipher.ProcessBlock(FR, FRE);
  171. output[0] = EncryptByte(input[0], 0);
  172. output[1] = EncryptByte(input[1], 1);
  173. //
  174. // do reset
  175. //
  176. Array.Copy(FR, 2, FR, 0, blockSize - 2);
  177. output[..2].CopyTo(FR.AsSpan(blockSize - 2));
  178. cipher.ProcessBlock(FR, FRE);
  179. for (int n = 2; n < blockSize; n++)
  180. {
  181. FR[n - 2] = output[n] = EncryptByte(input[n], n - 2);
  182. }
  183. count += blockSize;
  184. }
  185. return blockSize;
  186. }
  187. private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
  188. {
  189. Check.DataLength(input, blockSize, "input buffer too short");
  190. Check.OutputLength(output, blockSize, "output buffer too short");
  191. if (count > blockSize)
  192. {
  193. byte inVal = input[0];
  194. FR[blockSize - 2] = inVal;
  195. output[0] = EncryptByte(inVal, blockSize - 2);
  196. inVal = input[1];
  197. FR[blockSize - 1] = inVal;
  198. output[1] = EncryptByte(inVal, blockSize - 1);
  199. cipher.ProcessBlock(FR, FRE);
  200. for (int n = 2; n < blockSize; n++)
  201. {
  202. inVal = input[n];
  203. FR[n - 2] = inVal;
  204. output[n] = EncryptByte(inVal, n - 2);
  205. }
  206. }
  207. else if (count == 0)
  208. {
  209. cipher.ProcessBlock(FR, FRE);
  210. for (int n = 0; n < blockSize; n++)
  211. {
  212. FR[n] = input[n];
  213. output[n] = EncryptByte(input[n], n);
  214. }
  215. count += blockSize;
  216. }
  217. else if (count == blockSize)
  218. {
  219. cipher.ProcessBlock(FR, 0, FRE, 0);
  220. byte inVal1 = input[0];
  221. byte inVal2 = input[1];
  222. output[0] = EncryptByte(inVal1, 0);
  223. output[1] = EncryptByte(inVal2, 1);
  224. Array.Copy(FR, 2, FR, 0, blockSize - 2);
  225. FR[blockSize - 2] = inVal1;
  226. FR[blockSize - 1] = inVal2;
  227. cipher.ProcessBlock(FR, 0, FRE, 0);
  228. for (int n = 2; n < blockSize; n++)
  229. {
  230. byte inVal = input[n];
  231. FR[n - 2] = inVal;
  232. output[n] = EncryptByte(inVal, n - 2);
  233. }
  234. count += blockSize;
  235. }
  236. return blockSize;
  237. }
  238. #else
  239. private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
  240. {
  241. Check.DataLength(input, inOff, blockSize, "input buffer too short");
  242. Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
  243. if (count > blockSize)
  244. {
  245. FR[blockSize - 2] = outBytes[outOff] = EncryptByte(input[inOff], blockSize - 2);
  246. FR[blockSize - 1] = outBytes[outOff + 1] = EncryptByte(input[inOff + 1], blockSize - 1);
  247. cipher.ProcessBlock(FR, 0, FRE, 0);
  248. for (int n = 2; n < blockSize; n++)
  249. {
  250. FR[n - 2] = outBytes[outOff + n] = EncryptByte(input[inOff + n], n - 2);
  251. }
  252. }
  253. else if (count == 0)
  254. {
  255. cipher.ProcessBlock(FR, 0, FRE, 0);
  256. for (int n = 0; n < blockSize; n++)
  257. {
  258. FR[n] = outBytes[outOff + n] = EncryptByte(input[inOff + n], n);
  259. }
  260. count += blockSize;
  261. }
  262. else if (count == blockSize)
  263. {
  264. cipher.ProcessBlock(FR, 0, FRE, 0);
  265. outBytes[outOff] = EncryptByte(input[inOff], 0);
  266. outBytes[outOff + 1] = EncryptByte(input[inOff + 1], 1);
  267. //
  268. // do reset
  269. //
  270. Array.Copy(FR, 2, FR, 0, blockSize - 2);
  271. Array.Copy(outBytes, outOff, FR, blockSize - 2, 2);
  272. cipher.ProcessBlock(FR, 0, FRE, 0);
  273. for (int n = 2; n < blockSize; n++)
  274. {
  275. FR[n - 2] = outBytes[outOff + n] = EncryptByte(input[inOff + n], n - 2);
  276. }
  277. count += blockSize;
  278. }
  279. return blockSize;
  280. }
  281. private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
  282. {
  283. Check.DataLength(input, inOff, blockSize, "input buffer too short");
  284. Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
  285. if (count > blockSize)
  286. {
  287. byte inVal = input[inOff];
  288. FR[blockSize - 2] = inVal;
  289. outBytes[outOff] = EncryptByte(inVal, blockSize - 2);
  290. inVal = input[inOff + 1];
  291. FR[blockSize - 1] = inVal;
  292. outBytes[outOff + 1] = EncryptByte(inVal, blockSize - 1);
  293. cipher.ProcessBlock(FR, 0, FRE, 0);
  294. for (int n = 2; n < blockSize; n++)
  295. {
  296. inVal = input[inOff + n];
  297. FR[n - 2] = inVal;
  298. outBytes[outOff + n] = EncryptByte(inVal, n - 2);
  299. }
  300. }
  301. else if (count == 0)
  302. {
  303. cipher.ProcessBlock(FR, 0, FRE, 0);
  304. for (int n = 0; n < blockSize; n++)
  305. {
  306. FR[n] = input[inOff + n];
  307. outBytes[outOff + n] = EncryptByte(input[inOff + n], n);
  308. }
  309. count += blockSize;
  310. }
  311. else if (count == blockSize)
  312. {
  313. cipher.ProcessBlock(FR, 0, FRE, 0);
  314. byte inVal1 = input[inOff];
  315. byte inVal2 = input[inOff + 1];
  316. outBytes[outOff ] = EncryptByte(inVal1, 0);
  317. outBytes[outOff + 1] = EncryptByte(inVal2, 1);
  318. Array.Copy(FR, 2, FR, 0, blockSize - 2);
  319. FR[blockSize - 2] = inVal1;
  320. FR[blockSize - 1] = inVal2;
  321. cipher.ProcessBlock(FR, 0, FRE, 0);
  322. for (int n = 2; n < blockSize; n++)
  323. {
  324. byte inVal = input[inOff + n];
  325. FR[n - 2] = inVal;
  326. outBytes[outOff + n] = EncryptByte(inVal, n - 2);
  327. }
  328. count += blockSize;
  329. }
  330. return blockSize;
  331. }
  332. #endif
  333. }
  334. }
  335. #pragma warning restore
  336. #endif