OpenPgpCfbBlockCipher.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  5. namespace BestHTTP.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. : IBlockCipher
  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 GetUnderlyingCipher()
  47. {
  48. return cipher;
  49. }
  50. /**
  51. * return the algorithm name and mode.
  52. *
  53. * @return the name of the underlying algorithm followed by "/PGPCFB"
  54. * and the block size in bits.
  55. */
  56. public string AlgorithmName
  57. {
  58. get { return cipher.AlgorithmName + "/OpenPGPCFB"; }
  59. }
  60. public bool IsPartialBlockOkay
  61. {
  62. get { return true; }
  63. }
  64. /**
  65. * return the block size we are operating at.
  66. *
  67. * @return the block size we are operating at (in bytes).
  68. */
  69. public int GetBlockSize()
  70. {
  71. return cipher.GetBlockSize();
  72. }
  73. /**
  74. * Process one block of input from the array in and write it to
  75. * the out array.
  76. *
  77. * @param in the array containing the input data.
  78. * @param inOff offset into the in array the data starts at.
  79. * @param out the array the output data will be copied into.
  80. * @param outOff the offset into the out array the output will start at.
  81. * @exception DataLengthException if there isn't enough data in in, or
  82. * space in out.
  83. * @exception InvalidOperationException if the cipher isn't initialised.
  84. * @return the number of bytes processed and produced.
  85. */
  86. public int ProcessBlock(
  87. byte[] input,
  88. int inOff,
  89. byte[] output,
  90. int outOff)
  91. {
  92. return (forEncryption) ? EncryptBlock(input, inOff, output, outOff) : DecryptBlock(input, inOff, output, outOff);
  93. }
  94. /**
  95. * reset the chaining vector back to the IV and reset the underlying
  96. * cipher.
  97. */
  98. public void Reset()
  99. {
  100. count = 0;
  101. Array.Copy(IV, 0, FR, 0, FR.Length);
  102. cipher.Reset();
  103. }
  104. /**
  105. * Initialise the cipher and, possibly, the initialisation vector (IV).
  106. * If an IV isn't passed as part of the parameter, the IV will be all zeros.
  107. * An IV which is too short is handled in FIPS compliant fashion.
  108. *
  109. * @param forEncryption if true the cipher is initialised for
  110. * encryption, if false for decryption.
  111. * @param parameters the key and other data required by the cipher.
  112. * @exception ArgumentException if the parameters argument is
  113. * inappropriate.
  114. */
  115. public void Init(
  116. bool forEncryption,
  117. ICipherParameters parameters)
  118. {
  119. this.forEncryption = forEncryption;
  120. if (parameters is ParametersWithIV)
  121. {
  122. ParametersWithIV ivParam = (ParametersWithIV)parameters;
  123. byte[] iv = ivParam.GetIV();
  124. if (iv.Length < IV.Length)
  125. {
  126. // prepend the supplied IV with zeros (per FIPS PUB 81)
  127. Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length);
  128. for (int i = 0; i < IV.Length - iv.Length; i++)
  129. {
  130. IV[i] = 0;
  131. }
  132. }
  133. else
  134. {
  135. Array.Copy(iv, 0, IV, 0, IV.Length);
  136. }
  137. parameters = ivParam.Parameters;
  138. }
  139. Reset();
  140. cipher.Init(true, parameters);
  141. }
  142. /**
  143. * Encrypt one byte of data according to CFB mode.
  144. * @param data the byte to encrypt
  145. * @param blockOff offset in the current block
  146. * @returns the encrypted byte
  147. */
  148. private byte EncryptByte(byte data, int blockOff)
  149. {
  150. return (byte)(FRE[blockOff] ^ data);
  151. }
  152. /**
  153. * Do the appropriate processing for CFB IV mode encryption.
  154. *
  155. * @param in the array containing the data to be encrypted.
  156. * @param inOff offset into the in array the data starts at.
  157. * @param out the array the encrypted data will be copied into.
  158. * @param outOff the offset into the out array the output will start at.
  159. * @exception DataLengthException if there isn't enough data in in, or
  160. * space in out.
  161. * @exception InvalidOperationException if the cipher isn't initialised.
  162. * @return the number of bytes processed and produced.
  163. */
  164. private int EncryptBlock(
  165. byte[] input,
  166. int inOff,
  167. byte[] outBytes,
  168. int outOff)
  169. {
  170. if ((inOff + blockSize) > input.Length)
  171. {
  172. throw new DataLengthException("input buffer too short");
  173. }
  174. if ((outOff + blockSize) > outBytes.Length)
  175. {
  176. throw new DataLengthException("output buffer too short");
  177. }
  178. if (count > blockSize)
  179. {
  180. FR[blockSize - 2] = outBytes[outOff] = EncryptByte(input[inOff], blockSize - 2);
  181. FR[blockSize - 1] = outBytes[outOff + 1] = EncryptByte(input[inOff + 1], blockSize - 1);
  182. cipher.ProcessBlock(FR, 0, FRE, 0);
  183. for (int n = 2; n < blockSize; n++)
  184. {
  185. FR[n - 2] = outBytes[outOff + n] = EncryptByte(input[inOff + n], n - 2);
  186. }
  187. }
  188. else if (count == 0)
  189. {
  190. cipher.ProcessBlock(FR, 0, FRE, 0);
  191. for (int n = 0; n < blockSize; n++)
  192. {
  193. FR[n] = outBytes[outOff + n] = EncryptByte(input[inOff + n], n);
  194. }
  195. count += blockSize;
  196. }
  197. else if (count == blockSize)
  198. {
  199. cipher.ProcessBlock(FR, 0, FRE, 0);
  200. outBytes[outOff] = EncryptByte(input[inOff], 0);
  201. outBytes[outOff + 1] = EncryptByte(input[inOff + 1], 1);
  202. //
  203. // do reset
  204. //
  205. Array.Copy(FR, 2, FR, 0, blockSize - 2);
  206. Array.Copy(outBytes, outOff, FR, blockSize - 2, 2);
  207. cipher.ProcessBlock(FR, 0, FRE, 0);
  208. for (int n = 2; n < blockSize; n++)
  209. {
  210. FR[n - 2] = outBytes[outOff + n] = EncryptByte(input[inOff + n], n - 2);
  211. }
  212. count += blockSize;
  213. }
  214. return blockSize;
  215. }
  216. /**
  217. * Do the appropriate processing for CFB IV mode decryption.
  218. *
  219. * @param in the array containing the data to be decrypted.
  220. * @param inOff offset into the in array the data starts at.
  221. * @param out the array the encrypted data will be copied into.
  222. * @param outOff the offset into the out array the output will start at.
  223. * @exception DataLengthException if there isn't enough data in in, or
  224. * space in out.
  225. * @exception InvalidOperationException if the cipher isn't initialised.
  226. * @return the number of bytes processed and produced.
  227. */
  228. private int DecryptBlock(
  229. byte[] input,
  230. int inOff,
  231. byte[] outBytes,
  232. int outOff)
  233. {
  234. if ((inOff + blockSize) > input.Length)
  235. {
  236. throw new DataLengthException("input buffer too short");
  237. }
  238. if ((outOff + blockSize) > outBytes.Length)
  239. {
  240. throw new DataLengthException("output buffer too short");
  241. }
  242. if (count > blockSize)
  243. {
  244. byte inVal = input[inOff];
  245. FR[blockSize - 2] = inVal;
  246. outBytes[outOff] = EncryptByte(inVal, blockSize - 2);
  247. inVal = input[inOff + 1];
  248. FR[blockSize - 1] = inVal;
  249. outBytes[outOff + 1] = EncryptByte(inVal, blockSize - 1);
  250. cipher.ProcessBlock(FR, 0, FRE, 0);
  251. for (int n = 2; n < blockSize; n++)
  252. {
  253. inVal = input[inOff + n];
  254. FR[n - 2] = inVal;
  255. outBytes[outOff + n] = EncryptByte(inVal, n - 2);
  256. }
  257. }
  258. else if (count == 0)
  259. {
  260. cipher.ProcessBlock(FR, 0, FRE, 0);
  261. for (int n = 0; n < blockSize; n++)
  262. {
  263. FR[n] = input[inOff + n];
  264. outBytes[n] = EncryptByte(input[inOff + n], n);
  265. }
  266. count += blockSize;
  267. }
  268. else if (count == blockSize)
  269. {
  270. cipher.ProcessBlock(FR, 0, FRE, 0);
  271. byte inVal1 = input[inOff];
  272. byte inVal2 = input[inOff + 1];
  273. outBytes[outOff ] = EncryptByte(inVal1, 0);
  274. outBytes[outOff + 1] = EncryptByte(inVal2, 1);
  275. Array.Copy(FR, 2, FR, 0, blockSize - 2);
  276. FR[blockSize - 2] = inVal1;
  277. FR[blockSize - 1] = inVal2;
  278. cipher.ProcessBlock(FR, 0, FRE, 0);
  279. for (int n = 2; n < blockSize; n++)
  280. {
  281. byte inVal = input[inOff + n];
  282. FR[n - 2] = inVal;
  283. outBytes[outOff + n] = EncryptByte(inVal, n - 2);
  284. }
  285. count += blockSize;
  286. }
  287. return blockSize;
  288. }
  289. }
  290. }
  291. #pragma warning restore
  292. #endif