PaddedBufferedBlockCipher.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  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.Modes;
  5. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  6. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Security;
  7. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Paddings
  8. {
  9. /**
  10. * A wrapper class that allows block ciphers to be used to process data in
  11. * a piecemeal fashion with padding. The PaddedBufferedBlockCipher
  12. * outputs a block only when the buffer is full and more data is being added,
  13. * or on a doFinal (unless the current block in the buffer is a pad block).
  14. * The default padding mechanism used is the one outlined in Pkcs5/Pkcs7.
  15. */
  16. public class PaddedBufferedBlockCipher
  17. : BufferedBlockCipher
  18. {
  19. private readonly IBlockCipherPadding padding;
  20. public PaddedBufferedBlockCipher(IBlockCipher cipher, IBlockCipherPadding padding)
  21. : this(EcbBlockCipher.GetBlockCipherMode(cipher), padding)
  22. {
  23. }
  24. /**
  25. * Create a buffered block cipher with the desired padding.
  26. *
  27. * @param cipher the underlying block cipher this buffering object wraps.
  28. * @param padding the padding type.
  29. */
  30. public PaddedBufferedBlockCipher(IBlockCipherMode cipherMode, IBlockCipherPadding padding)
  31. {
  32. m_cipherMode = cipherMode;
  33. this.padding = padding;
  34. buf = new byte[m_cipherMode.GetBlockSize()];
  35. bufOff = 0;
  36. }
  37. /**
  38. * Create a buffered block cipher Pkcs7 padding
  39. *
  40. * @param cipher the underlying block cipher this buffering object wraps.
  41. */
  42. public PaddedBufferedBlockCipher(IBlockCipherMode cipherMode)
  43. : this(cipherMode, new Pkcs7Padding())
  44. {
  45. }
  46. /**
  47. * initialise the cipher.
  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 override void Init(bool forEncryption, ICipherParameters parameters)
  56. {
  57. this.forEncryption = forEncryption;
  58. SecureRandom initRandom = null;
  59. if (parameters is ParametersWithRandom withRandom)
  60. {
  61. initRandom = withRandom.Random;
  62. parameters = withRandom.Parameters;
  63. }
  64. Reset();
  65. padding.Init(initRandom);
  66. m_cipherMode.Init(forEncryption, parameters);
  67. }
  68. /**
  69. * return the minimum size of the output buffer required for an update
  70. * plus a doFinal with an input of len bytes.
  71. *
  72. * @param len the length of the input.
  73. * @return the space required to accommodate a call to update and doFinal
  74. * with len bytes of input.
  75. */
  76. public override int GetOutputSize(
  77. int length)
  78. {
  79. int total = length + bufOff;
  80. int leftOver = total % buf.Length;
  81. if (leftOver == 0)
  82. {
  83. if (forEncryption)
  84. {
  85. return total + buf.Length;
  86. }
  87. return total;
  88. }
  89. return total - leftOver + buf.Length;
  90. }
  91. /**
  92. * return the size of the output buffer required for an update
  93. * an input of len bytes.
  94. *
  95. * @param len the length of the input.
  96. * @return the space required to accommodate a call to update
  97. * with len bytes of input.
  98. */
  99. public override int GetUpdateOutputSize(
  100. int length)
  101. {
  102. int total = length + bufOff;
  103. int leftOver = total % buf.Length;
  104. if (leftOver == 0)
  105. {
  106. return total - buf.Length;
  107. }
  108. return total - leftOver;
  109. }
  110. /**
  111. * process a single byte, producing an output block if necessary.
  112. *
  113. * @param in the input byte.
  114. * @param out the space for any output that might be produced.
  115. * @param outOff the offset from which the output will be copied.
  116. * @return the number of output bytes copied to out.
  117. * @exception DataLengthException if there isn't enough space in out.
  118. * @exception InvalidOperationException if the cipher isn't initialised.
  119. */
  120. public override int ProcessByte(byte input, byte[] output, int outOff)
  121. {
  122. int resultLen = 0;
  123. if (bufOff == buf.Length)
  124. {
  125. resultLen = m_cipherMode.ProcessBlock(buf, 0, output, outOff);
  126. bufOff = 0;
  127. }
  128. buf[bufOff++] = input;
  129. return resultLen;
  130. }
  131. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  132. public override int ProcessByte(byte input, Span<byte> output)
  133. {
  134. int resultLen = 0;
  135. if (bufOff == buf.Length)
  136. {
  137. resultLen = m_cipherMode.ProcessBlock(buf, output);
  138. bufOff = 0;
  139. }
  140. buf[bufOff++] = input;
  141. return resultLen;
  142. }
  143. #endif
  144. /**
  145. * process an array of bytes, producing output if necessary.
  146. *
  147. * @param in the input byte array.
  148. * @param inOff the offset at which the input data starts.
  149. * @param len the number of bytes to be copied out of the input array.
  150. * @param out the space for any output that might be produced.
  151. * @param outOff the offset from which the output will be copied.
  152. * @return the number of output bytes copied to out.
  153. * @exception DataLengthException if there isn't enough space in out.
  154. * @exception InvalidOperationException if the cipher isn't initialised.
  155. */
  156. public override int ProcessBytes(byte[] input, int inOff, int length, byte[] output, int outOff)
  157. {
  158. if (length < 0)
  159. throw new ArgumentException("Can't have a negative input length!");
  160. int blockSize = GetBlockSize();
  161. int outLength = GetUpdateOutputSize(length);
  162. if (outLength > 0)
  163. {
  164. Check.OutputLength(output, outOff, outLength, "output buffer too short");
  165. }
  166. int resultLen = 0;
  167. int gapLen = buf.Length - bufOff;
  168. if (length > gapLen)
  169. {
  170. Array.Copy(input, inOff, buf, bufOff, gapLen);
  171. resultLen = m_cipherMode.ProcessBlock(buf, 0, output, outOff);
  172. bufOff = 0;
  173. length -= gapLen;
  174. inOff += gapLen;
  175. while (length > buf.Length)
  176. {
  177. resultLen += m_cipherMode.ProcessBlock(input, inOff, output, outOff + resultLen);
  178. length -= blockSize;
  179. inOff += blockSize;
  180. }
  181. }
  182. Array.Copy(input, inOff, buf, bufOff, length);
  183. bufOff += length;
  184. return resultLen;
  185. }
  186. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  187. public override int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
  188. {
  189. int blockSize = GetBlockSize();
  190. int outLength = GetUpdateOutputSize(input.Length);
  191. if (outLength > 0)
  192. {
  193. Check.OutputLength(output, outLength, "output buffer too short");
  194. }
  195. int resultLen = 0;
  196. int gapLen = buf.Length - bufOff;
  197. if (input.Length > gapLen)
  198. {
  199. input[..gapLen].CopyTo(buf.AsSpan(bufOff));
  200. resultLen = m_cipherMode.ProcessBlock(buf, output);
  201. bufOff = 0;
  202. input = input[gapLen..];
  203. while (input.Length > buf.Length)
  204. {
  205. resultLen += m_cipherMode.ProcessBlock(input, output[resultLen..]);
  206. input = input[blockSize..];
  207. }
  208. }
  209. input.CopyTo(buf.AsSpan(bufOff));
  210. bufOff += input.Length;
  211. return resultLen;
  212. }
  213. #endif
  214. /**
  215. * Process the last block in the buffer. If the buffer is currently
  216. * full and padding needs to be added a call to doFinal will produce
  217. * 2 * GetBlockSize() bytes.
  218. *
  219. * @param out the array the block currently being held is copied into.
  220. * @param outOff the offset at which the copying starts.
  221. * @return the number of output bytes copied to out.
  222. * @exception DataLengthException if there is insufficient space in out for
  223. * the output or we are decrypting and the input is not block size aligned.
  224. * @exception InvalidOperationException if the underlying cipher is not
  225. * initialised.
  226. * @exception InvalidCipherTextException if padding is expected and not found.
  227. */
  228. public override int DoFinal(byte[] output, int outOff)
  229. {
  230. int blockSize = m_cipherMode.GetBlockSize();
  231. int resultLen = 0;
  232. if (forEncryption)
  233. {
  234. if (bufOff == blockSize)
  235. {
  236. if ((outOff + 2 * blockSize) > output.Length)
  237. {
  238. Reset();
  239. throw new OutputLengthException("output buffer too short");
  240. }
  241. resultLen = m_cipherMode.ProcessBlock(buf, 0, output, outOff);
  242. bufOff = 0;
  243. }
  244. padding.AddPadding(buf, bufOff);
  245. resultLen += m_cipherMode.ProcessBlock(buf, 0, output, outOff + resultLen);
  246. Reset();
  247. }
  248. else
  249. {
  250. if (bufOff == blockSize)
  251. {
  252. resultLen = m_cipherMode.ProcessBlock(buf, 0, buf, 0);
  253. bufOff = 0;
  254. }
  255. else
  256. {
  257. Reset();
  258. throw new DataLengthException("last block incomplete in decryption");
  259. }
  260. try
  261. {
  262. resultLen -= padding.PadCount(buf);
  263. Array.Copy(buf, 0, output, outOff, resultLen);
  264. }
  265. finally
  266. {
  267. Reset();
  268. }
  269. }
  270. return resultLen;
  271. }
  272. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  273. public override int DoFinal(Span<byte> output)
  274. {
  275. int blockSize = m_cipherMode.GetBlockSize();
  276. int resultLen = 0;
  277. if (forEncryption)
  278. {
  279. if (bufOff == blockSize)
  280. {
  281. if ((2 * blockSize) > output.Length)
  282. {
  283. Reset();
  284. throw new OutputLengthException("output buffer too short");
  285. }
  286. resultLen = m_cipherMode.ProcessBlock(buf, output);
  287. bufOff = 0;
  288. }
  289. padding.AddPadding(buf, bufOff);
  290. resultLen += m_cipherMode.ProcessBlock(buf, output[resultLen..]);
  291. Reset();
  292. }
  293. else
  294. {
  295. if (bufOff != blockSize)
  296. {
  297. Reset();
  298. throw new DataLengthException("last block incomplete in decryption");
  299. }
  300. resultLen = m_cipherMode.ProcessBlock(buf, buf);
  301. bufOff = 0;
  302. try
  303. {
  304. resultLen -= padding.PadCount(buf);
  305. buf.AsSpan(0, resultLen).CopyTo(output);
  306. }
  307. finally
  308. {
  309. Reset();
  310. }
  311. }
  312. return resultLen;
  313. }
  314. #endif
  315. }
  316. }
  317. #pragma warning restore
  318. #endif