CtsBlockCipher.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. using System;
  3. using System.Diagnostics;
  4. using Org.BouncyCastle.Crypto;
  5. using Org.BouncyCastle.Crypto.Parameters;
  6. namespace Org.BouncyCastle.Crypto.Modes
  7. {
  8. /**
  9. * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to
  10. * be used to produce cipher text which is the same outLength as the plain text.
  11. */
  12. public class CtsBlockCipher
  13. : BufferedBlockCipher
  14. {
  15. private readonly int blockSize;
  16. /**
  17. * Create a buffered block cipher that uses Cipher Text Stealing
  18. *
  19. * @param cipher the underlying block cipher this buffering object wraps.
  20. */
  21. public CtsBlockCipher(
  22. IBlockCipher cipher)
  23. {
  24. // TODO Should this test for acceptable ones instead?
  25. if (cipher is OfbBlockCipher || cipher is CfbBlockCipher)
  26. throw new ArgumentException("CtsBlockCipher can only accept ECB, or CBC ciphers");
  27. this.cipher = cipher;
  28. blockSize = cipher.GetBlockSize();
  29. buf = new byte[blockSize * 2];
  30. bufOff = 0;
  31. }
  32. /**
  33. * return the size of the output buffer required for an update of 'length' bytes.
  34. *
  35. * @param length the outLength of the input.
  36. * @return the space required to accommodate a call to update
  37. * with length bytes of input.
  38. */
  39. public override int GetUpdateOutputSize(
  40. int length)
  41. {
  42. int total = length + bufOff;
  43. int leftOver = total % buf.Length;
  44. if (leftOver == 0)
  45. {
  46. return total - buf.Length;
  47. }
  48. return total - leftOver;
  49. }
  50. /**
  51. * return the size of the output buffer required for an update plus a
  52. * doFinal with an input of length bytes.
  53. *
  54. * @param length the outLength of the input.
  55. * @return the space required to accommodate a call to update and doFinal
  56. * with length bytes of input.
  57. */
  58. public override int GetOutputSize(
  59. int length)
  60. {
  61. return length + bufOff;
  62. }
  63. /**
  64. * process a single byte, producing an output block if necessary.
  65. *
  66. * @param in the input byte.
  67. * @param out the space for any output that might be produced.
  68. * @param outOff the offset from which the output will be copied.
  69. * @return the number of output bytes copied to out.
  70. * @exception DataLengthException if there isn't enough space in out.
  71. * @exception InvalidOperationException if the cipher isn't initialised.
  72. */
  73. public override int ProcessByte(
  74. byte input,
  75. byte[] output,
  76. int outOff)
  77. {
  78. int resultLen = 0;
  79. if (bufOff == buf.Length)
  80. {
  81. resultLen = cipher.ProcessBlock(buf, 0, output, outOff);
  82. Debug.Assert(resultLen == blockSize);
  83. Array.Copy(buf, blockSize, buf, 0, blockSize);
  84. bufOff = blockSize;
  85. }
  86. buf[bufOff++] = input;
  87. return resultLen;
  88. }
  89. /**
  90. * process an array of bytes, producing output if necessary.
  91. *
  92. * @param in the input byte array.
  93. * @param inOff the offset at which the input data starts.
  94. * @param length the number of bytes to be copied out of the input array.
  95. * @param out the space for any output that might be produced.
  96. * @param outOff the offset from which the output will be copied.
  97. * @return the number of output bytes copied to out.
  98. * @exception DataLengthException if there isn't enough space in out.
  99. * @exception InvalidOperationException if the cipher isn't initialised.
  100. */
  101. public override int ProcessBytes(
  102. byte[] input,
  103. int inOff,
  104. int length,
  105. byte[] output,
  106. int outOff)
  107. {
  108. if (length < 0)
  109. {
  110. throw new ArgumentException("Can't have a negative input outLength!");
  111. }
  112. int blockSize = GetBlockSize();
  113. int outLength = GetUpdateOutputSize(length);
  114. if (outLength > 0)
  115. {
  116. if ((outOff + outLength) > output.Length)
  117. {
  118. throw new DataLengthException("output buffer too short");
  119. }
  120. }
  121. int resultLen = 0;
  122. int gapLen = buf.Length - bufOff;
  123. if (length > gapLen)
  124. {
  125. Array.Copy(input, inOff, buf, bufOff, gapLen);
  126. resultLen += cipher.ProcessBlock(buf, 0, output, outOff);
  127. Array.Copy(buf, blockSize, buf, 0, blockSize);
  128. bufOff = blockSize;
  129. length -= gapLen;
  130. inOff += gapLen;
  131. while (length > blockSize)
  132. {
  133. Array.Copy(input, inOff, buf, bufOff, blockSize);
  134. resultLen += cipher.ProcessBlock(buf, 0, output, outOff + resultLen);
  135. Array.Copy(buf, blockSize, buf, 0, blockSize);
  136. length -= blockSize;
  137. inOff += blockSize;
  138. }
  139. }
  140. Array.Copy(input, inOff, buf, bufOff, length);
  141. bufOff += length;
  142. return resultLen;
  143. }
  144. /**
  145. * Process the last block in the buffer.
  146. *
  147. * @param out the array the block currently being held is copied into.
  148. * @param outOff the offset at which the copying starts.
  149. * @return the number of output bytes copied to out.
  150. * @exception DataLengthException if there is insufficient space in out for
  151. * the output.
  152. * @exception InvalidOperationException if the underlying cipher is not
  153. * initialised.
  154. * @exception InvalidCipherTextException if cipher text decrypts wrongly (in
  155. * case the exception will never Get thrown).
  156. */
  157. public override int DoFinal(
  158. byte[] output,
  159. int outOff)
  160. {
  161. if (bufOff + outOff > output.Length)
  162. {
  163. throw new DataLengthException("output buffer too small in doFinal");
  164. }
  165. int blockSize = cipher.GetBlockSize();
  166. int length = bufOff - blockSize;
  167. byte[] block = new byte[blockSize];
  168. if (forEncryption)
  169. {
  170. cipher.ProcessBlock(buf, 0, block, 0);
  171. if (bufOff < blockSize)
  172. {
  173. throw new DataLengthException("need at least one block of input for CTS");
  174. }
  175. for (int i = bufOff; i != buf.Length; i++)
  176. {
  177. buf[i] = block[i - blockSize];
  178. }
  179. for (int i = blockSize; i != bufOff; i++)
  180. {
  181. buf[i] ^= block[i - blockSize];
  182. }
  183. IBlockCipher c = (cipher is CbcBlockCipher)
  184. ? ((CbcBlockCipher)cipher).GetUnderlyingCipher()
  185. : cipher;
  186. c.ProcessBlock(buf, blockSize, output, outOff);
  187. Array.Copy(block, 0, output, outOff + blockSize, length);
  188. }
  189. else
  190. {
  191. byte[] lastBlock = new byte[blockSize];
  192. IBlockCipher c = (cipher is CbcBlockCipher)
  193. ? ((CbcBlockCipher)cipher).GetUnderlyingCipher()
  194. : cipher;
  195. c.ProcessBlock(buf, 0, block, 0);
  196. for (int i = blockSize; i != bufOff; i++)
  197. {
  198. lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]);
  199. }
  200. Array.Copy(buf, blockSize, block, 0, length);
  201. cipher.ProcessBlock(block, 0, output, outOff);
  202. Array.Copy(lastBlock, 0, output, outOff + blockSize, length);
  203. }
  204. int offset = bufOff;
  205. Reset();
  206. return offset;
  207. }
  208. }
  209. }
  210. #endif