CtsBlockCipher.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Diagnostics;
  5. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes
  6. {
  7. /**
  8. * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to
  9. * be used to produce cipher text which is the same outLength as the plain text.
  10. */
  11. public class CtsBlockCipher
  12. : BufferedBlockCipher
  13. {
  14. private readonly int blockSize;
  15. public CtsBlockCipher(IBlockCipher cipher)
  16. : this(EcbBlockCipher.GetBlockCipherMode(cipher))
  17. {
  18. }
  19. /**
  20. * Create a buffered block cipher that uses Cipher Text Stealing
  21. *
  22. * @param cipher the underlying block cipher this buffering object wraps.
  23. */
  24. public CtsBlockCipher(IBlockCipherMode cipherMode)
  25. {
  26. if (!(cipherMode is CbcBlockCipher || cipherMode is EcbBlockCipher))
  27. throw new ArgumentException("CtsBlockCipher can only accept ECB, or CBC ciphers");
  28. m_cipherMode = cipherMode;
  29. blockSize = cipherMode.GetBlockSize();
  30. buf = new byte[blockSize * 2];
  31. bufOff = 0;
  32. }
  33. /**
  34. * return the size of the output buffer required for an update of 'length' bytes.
  35. *
  36. * @param length the outLength of the input.
  37. * @return the space required to accommodate a call to update
  38. * with length bytes of input.
  39. */
  40. public override int GetUpdateOutputSize(
  41. int length)
  42. {
  43. int total = length + bufOff;
  44. int leftOver = total % buf.Length;
  45. if (leftOver == 0)
  46. {
  47. return total - buf.Length;
  48. }
  49. return total - leftOver;
  50. }
  51. /**
  52. * return the size of the output buffer required for an update plus a
  53. * doFinal with an input of length bytes.
  54. *
  55. * @param length the outLength of the input.
  56. * @return the space required to accommodate a call to update and doFinal
  57. * with length bytes of input.
  58. */
  59. public override int GetOutputSize(
  60. int length)
  61. {
  62. return length + bufOff;
  63. }
  64. /**
  65. * process a single byte, producing an output block if necessary.
  66. *
  67. * @param in the input byte.
  68. * @param out the space for any output that might be produced.
  69. * @param outOff the offset from which the output will be copied.
  70. * @return the number of output bytes copied to out.
  71. * @exception DataLengthException if there isn't enough space in out.
  72. * @exception InvalidOperationException if the cipher isn't initialised.
  73. */
  74. public override int ProcessByte(byte input, byte[] output, int outOff)
  75. {
  76. int resultLen = 0;
  77. if (bufOff == buf.Length)
  78. {
  79. resultLen = m_cipherMode.ProcessBlock(buf, 0, output, outOff);
  80. Debug.Assert(resultLen == blockSize);
  81. Array.Copy(buf, blockSize, buf, 0, blockSize);
  82. bufOff = blockSize;
  83. }
  84. buf[bufOff++] = input;
  85. return resultLen;
  86. }
  87. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  88. public override int ProcessByte(byte input, Span<byte> output)
  89. {
  90. int resultLen = 0;
  91. if (bufOff == buf.Length)
  92. {
  93. resultLen = m_cipherMode.ProcessBlock(buf, output);
  94. Debug.Assert(resultLen == blockSize);
  95. Array.Copy(buf, blockSize, buf, 0, blockSize);
  96. bufOff = blockSize;
  97. }
  98. buf[bufOff++] = input;
  99. return resultLen;
  100. }
  101. #endif
  102. /**
  103. * process an array of bytes, producing output if necessary.
  104. *
  105. * @param in the input byte array.
  106. * @param inOff the offset at which the input data starts.
  107. * @param length the number of bytes to be copied out of the input array.
  108. * @param out the space for any output that might be produced.
  109. * @param outOff the offset from which the output will be copied.
  110. * @return the number of output bytes copied to out.
  111. * @exception DataLengthException if there isn't enough space in out.
  112. * @exception InvalidOperationException if the cipher isn't initialised.
  113. */
  114. public override int ProcessBytes(byte[] input, int inOff, int length, byte[] output, int outOff)
  115. {
  116. if (length < 0)
  117. throw new ArgumentException("Can't have a negative input length!");
  118. int blockSize = GetBlockSize();
  119. int outLength = GetUpdateOutputSize(length);
  120. if (outLength > 0)
  121. {
  122. Check.OutputLength(output, outOff, outLength, "output buffer too short");
  123. }
  124. int resultLen = 0;
  125. int gapLen = buf.Length - bufOff;
  126. if (length > gapLen)
  127. {
  128. Array.Copy(input, inOff, buf, bufOff, gapLen);
  129. resultLen = m_cipherMode.ProcessBlock(buf, 0, output, outOff);
  130. Array.Copy(buf, blockSize, buf, 0, blockSize);
  131. bufOff = blockSize;
  132. length -= gapLen;
  133. inOff += gapLen;
  134. while (length > blockSize)
  135. {
  136. Array.Copy(input, inOff, buf, bufOff, blockSize);
  137. resultLen += m_cipherMode.ProcessBlock(buf, 0, output, outOff + resultLen);
  138. Array.Copy(buf, blockSize, buf, 0, blockSize);
  139. length -= blockSize;
  140. inOff += blockSize;
  141. }
  142. }
  143. Array.Copy(input, inOff, buf, bufOff, length);
  144. bufOff += length;
  145. return resultLen;
  146. }
  147. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  148. public override int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
  149. {
  150. int blockSize = GetBlockSize();
  151. int outLength = GetUpdateOutputSize(input.Length);
  152. if (outLength > 0)
  153. {
  154. Check.OutputLength(output, outLength, "output buffer too short");
  155. }
  156. int resultLen = 0;
  157. int gapLen = buf.Length - bufOff;
  158. if (input.Length > gapLen)
  159. {
  160. input[..gapLen].CopyTo(buf.AsSpan(bufOff));
  161. resultLen = m_cipherMode.ProcessBlock(buf, output);
  162. Array.Copy(buf, blockSize, buf, 0, blockSize);
  163. bufOff = blockSize;
  164. input = input[gapLen..];
  165. while (input.Length > blockSize)
  166. {
  167. input[..blockSize].CopyTo(buf.AsSpan(bufOff));
  168. resultLen += m_cipherMode.ProcessBlock(buf, output[resultLen..]);
  169. Array.Copy(buf, blockSize, buf, 0, blockSize);
  170. input = input[blockSize..];
  171. }
  172. }
  173. input.CopyTo(buf.AsSpan(bufOff));
  174. bufOff += input.Length;
  175. return resultLen;
  176. }
  177. #endif
  178. /**
  179. * Process the last block in the buffer.
  180. *
  181. * @param out the array the block currently being held is copied into.
  182. * @param outOff the offset at which the copying starts.
  183. * @return the number of output bytes copied to out.
  184. * @exception DataLengthException if there is insufficient space in out for
  185. * the output.
  186. * @exception InvalidOperationException if the underlying cipher is not
  187. * initialised.
  188. * @exception InvalidCipherTextException if cipher text decrypts wrongly (in
  189. * case the exception will never Get thrown).
  190. */
  191. public override int DoFinal(byte[] output, int outOff)
  192. {
  193. if (bufOff + outOff > output.Length)
  194. throw new DataLengthException("output buffer too small in DoFinal");
  195. int blockSize = m_cipherMode.GetBlockSize();
  196. int length = bufOff - blockSize;
  197. byte[] block = new byte[blockSize];
  198. if (forEncryption)
  199. {
  200. m_cipherMode.ProcessBlock(buf, 0, block, 0);
  201. if (bufOff < blockSize)
  202. throw new DataLengthException("need at least one block of input for CTS");
  203. for (int i = bufOff; i != buf.Length; i++)
  204. {
  205. buf[i] = block[i - blockSize];
  206. }
  207. for (int i = blockSize; i != bufOff; i++)
  208. {
  209. buf[i] ^= block[i - blockSize];
  210. }
  211. m_cipherMode.UnderlyingCipher.ProcessBlock(buf, blockSize, output, outOff);
  212. Array.Copy(block, 0, output, outOff + blockSize, length);
  213. }
  214. else
  215. {
  216. byte[] lastBlock = new byte[blockSize];
  217. m_cipherMode.UnderlyingCipher.ProcessBlock(buf, 0, block, 0);
  218. for (int i = blockSize; i != bufOff; i++)
  219. {
  220. lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]);
  221. }
  222. Array.Copy(buf, blockSize, block, 0, length);
  223. m_cipherMode.ProcessBlock(block, 0, output, outOff);
  224. Array.Copy(lastBlock, 0, output, outOff + blockSize, length);
  225. }
  226. int offset = bufOff;
  227. Reset();
  228. return offset;
  229. }
  230. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  231. public override int DoFinal(Span<byte> output)
  232. {
  233. if (bufOff > output.Length)
  234. throw new DataLengthException("output buffer too small in DoFinal");
  235. int blockSize = m_cipherMode.GetBlockSize();
  236. int length = bufOff - blockSize;
  237. Span<byte> block = blockSize <= 64
  238. ? stackalloc byte[blockSize]
  239. : new byte[blockSize];
  240. if (forEncryption)
  241. {
  242. m_cipherMode.ProcessBlock(buf, block);
  243. if (bufOff < blockSize)
  244. throw new DataLengthException("need at least one block of input for CTS");
  245. for (int i = bufOff; i != buf.Length; i++)
  246. {
  247. buf[i] = block[i - blockSize];
  248. }
  249. for (int i = blockSize; i != bufOff; i++)
  250. {
  251. buf[i] ^= block[i - blockSize];
  252. }
  253. m_cipherMode.UnderlyingCipher.ProcessBlock(buf.AsSpan(blockSize), output);
  254. block[..length].CopyTo(output[blockSize..]);
  255. }
  256. else
  257. {
  258. Span<byte> lastBlock = blockSize <= 64
  259. ? stackalloc byte[blockSize]
  260. : new byte[blockSize];
  261. m_cipherMode.UnderlyingCipher.ProcessBlock(buf, block);
  262. for (int i = blockSize; i != bufOff; i++)
  263. {
  264. lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]);
  265. }
  266. buf.AsSpan(blockSize, length).CopyTo(block);
  267. m_cipherMode.ProcessBlock(block, output);
  268. lastBlock[..length].CopyTo(output[blockSize..]);
  269. }
  270. int offset = bufOff;
  271. Reset();
  272. return offset;
  273. }
  274. #endif
  275. }
  276. }
  277. #pragma warning restore
  278. #endif