OpenPgpCfbBlockCipher.cs 10 KB

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