KCtrBlockCipher.cs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  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 Gamming or Counter (CTR) mode on top of a DSTU 7624 block cipher.
  9. */
  10. public class KCtrBlockCipher
  11. : IStreamCipher, IBlockCipherMode
  12. {
  13. private byte[] IV;
  14. private byte[] ofbV;
  15. private byte[] ofbOutV;
  16. private bool initialised;
  17. private int byteCount;
  18. private readonly int blockSize;
  19. private readonly IBlockCipher cipher;
  20. /**
  21. * Basic constructor.
  22. *
  23. * @param cipher the block cipher to be used as the basis of the
  24. * feedback mode.
  25. */
  26. public KCtrBlockCipher(IBlockCipher cipher)
  27. {
  28. this.cipher = cipher;
  29. this.IV = new byte[cipher.GetBlockSize()];
  30. this.blockSize = cipher.GetBlockSize();
  31. this.ofbV = new byte[cipher.GetBlockSize()];
  32. this.ofbOutV = new byte[cipher.GetBlockSize()];
  33. }
  34. /**
  35. * return the underlying block cipher that we are wrapping.
  36. *
  37. * @return the underlying block cipher that we are wrapping.
  38. */
  39. public IBlockCipher UnderlyingCipher => cipher;
  40. /**
  41. * Initialise the cipher and, possibly, the initialisation vector (IV).
  42. * If an IV isn't passed as part of the parameter, the IV will be all zeros.
  43. * An IV which is too short is handled in FIPS compliant fashion.
  44. *
  45. * @param forEncryption if true the cipher is initialised for
  46. * encryption, if false for decryption.
  47. * @param param the key and other data required by the cipher.
  48. * @exception ArgumentException if the parameters argument is
  49. * inappropriate.
  50. */
  51. public void Init(
  52. bool forEncryption,
  53. ICipherParameters parameters)
  54. {
  55. this.initialised = true;
  56. if (parameters is ParametersWithIV)
  57. {
  58. ParametersWithIV ivParam = (ParametersWithIV)parameters;
  59. byte[] iv = ivParam.GetIV();
  60. int diff = IV.Length - iv.Length;
  61. Array.Clear(IV, 0, IV.Length);
  62. Array.Copy(iv, 0, IV, diff, iv.Length);
  63. parameters = ivParam.Parameters;
  64. }
  65. else
  66. {
  67. throw new ArgumentException("Invalid parameter passed");
  68. }
  69. // if it's null, key is to be reused.
  70. if (parameters != null)
  71. {
  72. cipher.Init(true, parameters);
  73. }
  74. Reset();
  75. }
  76. /**
  77. * return the algorithm name and mode.
  78. *
  79. * @return the name of the underlying algorithm followed by "/KCTR"
  80. * and the block size in bits.
  81. */
  82. public string AlgorithmName
  83. {
  84. get { return cipher.AlgorithmName + "/KCTR"; }
  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 cipher.GetBlockSize();
  98. }
  99. public byte ReturnByte(byte input)
  100. {
  101. return CalculateByte(input);
  102. }
  103. public void ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
  104. {
  105. Check.DataLength(input, inOff, len, "input buffer too small");
  106. Check.OutputLength(output, outOff, len, "output buffer too short");
  107. int inStart = inOff;
  108. int inEnd = inOff + len;
  109. int outStart = outOff;
  110. while (inStart < inEnd)
  111. {
  112. output[outStart++] = CalculateByte(input[inStart++]);
  113. }
  114. }
  115. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  116. public void ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output)
  117. {
  118. Check.OutputLength(output, input.Length, "output buffer too short");
  119. for (int i = 0; i < input.Length; ++i)
  120. {
  121. output[i] = CalculateByte(input[i]);
  122. }
  123. }
  124. #endif
  125. protected byte CalculateByte(byte b)
  126. {
  127. if (byteCount == 0)
  128. {
  129. incrementCounterAt(0);
  130. checkCounter();
  131. cipher.ProcessBlock(ofbV, 0, ofbOutV, 0);
  132. return (byte)(ofbOutV[byteCount++] ^ b);
  133. }
  134. byte rv = (byte)(ofbOutV[byteCount++] ^ b);
  135. if (byteCount == ofbV.Length)
  136. {
  137. byteCount = 0;
  138. }
  139. return rv;
  140. }
  141. /**
  142. * Process one block of input from the array in and write it to
  143. * the out array.
  144. *
  145. * @param input the array containing the input data.
  146. * @param inOff offset into the in array the data starts at.
  147. * @param output the array the output data will be copied into.
  148. * @param outOff the offset into the out array the output will start at.
  149. * @exception DataLengthException if there isn't enough data in in, or
  150. * space in out.
  151. * @exception InvalidOperationException if the cipher isn't initialised.
  152. * @return the number of bytes processed and produced.
  153. */
  154. public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
  155. {
  156. int blockSize = GetBlockSize();
  157. Check.DataLength(input, inOff, blockSize, "input buffer too short");
  158. Check.OutputLength(output, outOff, blockSize, "output buffer too short");
  159. ProcessBytes(input, inOff, blockSize, output, outOff);
  160. return blockSize;
  161. }
  162. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  163. public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
  164. {
  165. int blockSize = GetBlockSize();
  166. Check.DataLength(input, blockSize, "input buffer too short");
  167. Check.OutputLength(output, blockSize, "output buffer too short");
  168. ProcessBytes(input[..blockSize], output);
  169. return blockSize;
  170. }
  171. #endif
  172. /**
  173. * reset the chaining vector back to the IV and reset the underlying
  174. * cipher.
  175. */
  176. public void Reset()
  177. {
  178. if (initialised)
  179. {
  180. cipher.ProcessBlock(IV, 0, ofbV, 0);
  181. }
  182. byteCount = 0;
  183. }
  184. private void incrementCounterAt(int pos)
  185. {
  186. int i = pos;
  187. while (i < ofbV.Length)
  188. {
  189. if (++ofbV[i++] != 0)
  190. {
  191. break;
  192. }
  193. }
  194. }
  195. private void checkCounter()
  196. {
  197. // TODO:
  198. // if the IV is the same as the blocksize we assume the user knows what they are doing
  199. // if (IV.length < ofbV.length)
  200. // {
  201. // for (int i = 0; i != IV.length; i++)
  202. // {
  203. // if (ofbV[i] != IV[i])
  204. // {
  205. // throw new IllegalStateException("Counter in KCTR mode out of range.");
  206. // }
  207. // }
  208. // }
  209. }
  210. }
  211. }
  212. #pragma warning restore
  213. #endif