CfbBlockCipher.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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 a Cipher-FeedBack (CFB) mode on top of a simple cipher.
  9. */
  10. public class CfbBlockCipher
  11. : IBlockCipherMode
  12. {
  13. private byte[] IV;
  14. private byte[] cfbV;
  15. private byte[] cfbOutV;
  16. private bool encrypting;
  17. private readonly int blockSize;
  18. private readonly IBlockCipher cipher;
  19. /**
  20. * Basic constructor.
  21. *
  22. * @param cipher the block cipher to be used as the basis of the
  23. * feedback mode.
  24. * @param blockSize the block size in bits (note: a multiple of 8)
  25. */
  26. public CfbBlockCipher(
  27. IBlockCipher cipher,
  28. int bitBlockSize)
  29. {
  30. if (bitBlockSize < 8 || (bitBlockSize & 7) != 0)
  31. throw new ArgumentException("CFB" + bitBlockSize + " not supported", "bitBlockSize");
  32. this.cipher = cipher;
  33. this.blockSize = bitBlockSize / 8;
  34. this.IV = new byte[cipher.GetBlockSize()];
  35. this.cfbV = new byte[cipher.GetBlockSize()];
  36. this.cfbOutV = new byte[cipher.GetBlockSize()];
  37. }
  38. /**
  39. * return the underlying block cipher that we are wrapping.
  40. *
  41. * @return the underlying block cipher that we are wrapping.
  42. */
  43. public IBlockCipher UnderlyingCipher => cipher;
  44. /**
  45. * Initialise the cipher and, possibly, the initialisation vector (IV).
  46. * If an IV isn't passed as part of the parameter, the IV will be all zeros.
  47. * An IV which is too short is handled in FIPS compliant fashion.
  48. *
  49. * @param forEncryption if true the cipher is initialised for
  50. * encryption, if false for decryption.
  51. * @param param the key and other data required by the cipher.
  52. * @exception ArgumentException if the parameters argument is
  53. * inappropriate.
  54. */
  55. public void Init(
  56. bool forEncryption,
  57. ICipherParameters parameters)
  58. {
  59. this.encrypting = forEncryption;
  60. if (parameters is ParametersWithIV)
  61. {
  62. ParametersWithIV ivParam = (ParametersWithIV) parameters;
  63. byte[] iv = ivParam.GetIV();
  64. int diff = IV.Length - iv.Length;
  65. Array.Copy(iv, 0, IV, diff, iv.Length);
  66. Array.Clear(IV, 0, diff);
  67. parameters = ivParam.Parameters;
  68. }
  69. Reset();
  70. // if it's null, key is to be reused.
  71. if (parameters != null)
  72. {
  73. cipher.Init(true, parameters);
  74. }
  75. }
  76. /**
  77. * return the algorithm name and mode.
  78. *
  79. * @return the name of the underlying algorithm followed by "/CFB"
  80. * and the block size in bits.
  81. */
  82. public string AlgorithmName
  83. {
  84. get { return cipher.AlgorithmName + "/CFB" + (blockSize * 8); }
  85. }
  86. public bool IsPartialBlockOkay
  87. {
  88. get { return true; }
  89. }
  90. /**
  91. * return the block size we are operating at.
  92. *
  93. * @return the block size we are operating at (in bytes).
  94. */
  95. public int GetBlockSize()
  96. {
  97. return blockSize;
  98. }
  99. public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
  100. {
  101. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  102. return encrypting
  103. ? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff))
  104. : DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
  105. #else
  106. return encrypting
  107. ? EncryptBlock(input, inOff, output, outOff)
  108. : DecryptBlock(input, inOff, output, outOff);
  109. #endif
  110. }
  111. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  112. public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
  113. {
  114. return encrypting
  115. ? EncryptBlock(input, output)
  116. : DecryptBlock(input, output);
  117. }
  118. #endif
  119. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  120. private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
  121. {
  122. Check.DataLength(input, blockSize, "input buffer too short");
  123. Check.OutputLength(output, blockSize, "output buffer too short");
  124. cipher.ProcessBlock(cfbV, cfbOutV);
  125. //
  126. // XOR the cfbV with the plaintext producing the ciphertext
  127. //
  128. for (int i = 0; i < blockSize; i++)
  129. {
  130. output[i] = (byte)(cfbOutV[i] ^ input[i]);
  131. }
  132. //
  133. // change over the input block.
  134. //
  135. Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
  136. output[..blockSize].CopyTo(cfbV.AsSpan(cfbV.Length - blockSize));
  137. return blockSize;
  138. }
  139. private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
  140. {
  141. Check.DataLength(input, blockSize, "input buffer too short");
  142. Check.OutputLength(output, blockSize, "output buffer too short");
  143. cipher.ProcessBlock(cfbV, 0, cfbOutV, 0);
  144. //
  145. // change over the input block.
  146. //
  147. Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
  148. input[..blockSize].CopyTo(cfbV.AsSpan(cfbV.Length - blockSize));
  149. //
  150. // XOR the cfbV with the ciphertext producing the plaintext
  151. //
  152. for (int i = 0; i < blockSize; i++)
  153. {
  154. output[i] = (byte)(cfbOutV[i] ^ input[i]);
  155. }
  156. return blockSize;
  157. }
  158. #else
  159. private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
  160. {
  161. Check.DataLength(input, inOff, blockSize, "input buffer too short");
  162. Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
  163. cipher.ProcessBlock(cfbV, 0, cfbOutV, 0);
  164. //
  165. // XOR the cfbV with the plaintext producing the ciphertext
  166. //
  167. for (int i = 0; i < blockSize; i++)
  168. {
  169. outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]);
  170. }
  171. //
  172. // change over the input block.
  173. //
  174. Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
  175. Array.Copy(outBytes, outOff, cfbV, cfbV.Length - blockSize, blockSize);
  176. return blockSize;
  177. }
  178. private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
  179. {
  180. Check.DataLength(input, inOff, blockSize, "input buffer too short");
  181. Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
  182. cipher.ProcessBlock(cfbV, 0, cfbOutV, 0);
  183. //
  184. // change over the input block.
  185. //
  186. Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
  187. Array.Copy(input, inOff, cfbV, cfbV.Length - blockSize, blockSize);
  188. //
  189. // XOR the cfbV with the ciphertext producing the plaintext
  190. //
  191. for (int i = 0; i < blockSize; i++)
  192. {
  193. outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]);
  194. }
  195. return blockSize;
  196. }
  197. #endif
  198. /**
  199. * reset the chaining vector back to the IV and reset the underlying
  200. * cipher.
  201. */
  202. public void Reset()
  203. {
  204. Array.Copy(IV, 0, cfbV, 0, IV.Length);
  205. }
  206. }
  207. }
  208. #pragma warning restore
  209. #endif