FastCbcBlockCipher.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using Best.HTTP.Shared.TLS.Crypto.Impl;
  5. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto;
  6. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes;
  7. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  8. namespace Best.HTTP.Shared.TLS.Crypto
  9. {
  10. /**
  11. * implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher.
  12. */
  13. [Best.HTTP.Shared.PlatformSupport.IL2CPP.Il2CppEagerStaticClassConstructionAttribute]
  14. public sealed class FastCbcBlockCipher
  15. : IBlockCipherMode
  16. {
  17. private byte[] IV, cbcV, cbcNextV;
  18. private int blockSize;
  19. private IBlockCipher cipher;
  20. private bool encrypting;
  21. /**
  22. * Basic constructor.
  23. *
  24. * @param cipher the block cipher to be used as the basis of chaining.
  25. */
  26. public FastCbcBlockCipher(
  27. IBlockCipher cipher)
  28. {
  29. this.cipher = cipher;
  30. this.blockSize = cipher.GetBlockSize();
  31. this.IV = new byte[blockSize];
  32. this.cbcV = new byte[blockSize];
  33. this.cbcNextV = new byte[blockSize];
  34. }
  35. /**
  36. * return the underlying block cipher that we are wrapping.
  37. *
  38. * @return the underlying block cipher that we are wrapping.
  39. */
  40. public IBlockCipher UnderlyingCipher => cipher;
  41. /**
  42. * Initialise the cipher and, possibly, the initialisation vector (IV).
  43. * If an IV isn't passed as part of the parameter, the IV will be all zeros.
  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(bool forEncryption, ICipherParameters parameters)
  52. {
  53. bool oldEncrypting = this.encrypting;
  54. this.encrypting = forEncryption;
  55. if (parameters is ParametersWithIV ivParam)
  56. {
  57. byte[] iv = ivParam.GetIV();
  58. if (iv.Length != blockSize)
  59. throw new ArgumentException("initialisation vector must be the same length as block size");
  60. Array.Copy(iv, 0, IV, 0, iv.Length);
  61. parameters = ivParam.Parameters;
  62. }
  63. Reset();
  64. // if null it's an IV changed only.
  65. if (parameters != null)
  66. {
  67. cipher.Init(encrypting, parameters);
  68. }
  69. else if (oldEncrypting != encrypting)
  70. {
  71. throw new ArgumentException("cannot change encrypting state without providing key.");
  72. }
  73. }
  74. /**
  75. * return the algorithm name and mode.
  76. *
  77. * @return the name of the underlying algorithm followed by "/CBC".
  78. */
  79. public string AlgorithmName
  80. {
  81. get { return cipher.AlgorithmName + "/CBC"; }
  82. }
  83. public bool IsPartialBlockOkay
  84. {
  85. get { return false; }
  86. }
  87. /**
  88. * return the block size of the underlying cipher.
  89. *
  90. * @return the block size of the underlying cipher.
  91. */
  92. public int GetBlockSize()
  93. {
  94. return cipher.GetBlockSize();
  95. }
  96. public int ProcessBlock(byte[] input, int inOff, byte[] output, int outOff)
  97. {
  98. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  99. return encrypting
  100. ? EncryptBlock(input.AsSpan(inOff), output.AsSpan(outOff))
  101. : DecryptBlock(input.AsSpan(inOff), output.AsSpan(outOff));
  102. #else
  103. return encrypting
  104. ? EncryptBlock(input, inOff, output, outOff)
  105. : DecryptBlock(input, inOff, output, outOff);
  106. #endif
  107. }
  108. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  109. public int ProcessBlock(ReadOnlySpan<byte> input, Span<byte> output)
  110. {
  111. return encrypting
  112. ? EncryptBlock(input, output)
  113. : DecryptBlock(input, output);
  114. }
  115. #endif
  116. /**
  117. * reset the chaining vector back to the IV and reset the underlying
  118. * cipher.
  119. */
  120. public void Reset()
  121. {
  122. Array.Copy(IV, 0, cbcV, 0, IV.Length);
  123. Array.Clear(cbcNextV, 0, cbcNextV.Length);
  124. }
  125. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  126. private int EncryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
  127. {
  128. Check.DataLength(input, blockSize, "input buffer too short");
  129. Check.OutputLength(output, blockSize, "output buffer too short");
  130. for (int i = 0; i < blockSize; i++)
  131. {
  132. cbcV[i] ^= input[i];
  133. }
  134. int length = cipher.ProcessBlock(cbcV, output);
  135. output[..blockSize].CopyTo(cbcV);
  136. return length;
  137. }
  138. private int DecryptBlock(ReadOnlySpan<byte> input, Span<byte> output)
  139. {
  140. Check.DataLength(input, blockSize, "input buffer too short");
  141. Check.OutputLength(output, blockSize, "output buffer too short");
  142. input[..blockSize].CopyTo(cbcNextV);
  143. int length = cipher.ProcessBlock(input, output);
  144. for (int i = 0; i < blockSize; i++)
  145. {
  146. output[i] ^= cbcV[i];
  147. }
  148. byte[] tmp = cbcV;
  149. cbcV = cbcNextV;
  150. cbcNextV = tmp;
  151. return length;
  152. }
  153. #else
  154. private int EncryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
  155. {
  156. Check.DataLength(input, inOff, blockSize, "input buffer too short");
  157. Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
  158. for (int i = 0; i < blockSize; i++)
  159. {
  160. cbcV[i] ^= input[inOff + i];
  161. }
  162. int length = cipher.ProcessBlock(cbcV, 0, outBytes, outOff);
  163. Array.Copy(outBytes, outOff, cbcV, 0, cbcV.Length);
  164. return length;
  165. }
  166. private int DecryptBlock(byte[] input, int inOff, byte[] outBytes, int outOff)
  167. {
  168. Check.DataLength(input, inOff, blockSize, "input buffer too short");
  169. Check.OutputLength(outBytes, outOff, blockSize, "output buffer too short");
  170. Array.Copy(input, inOff, cbcNextV, 0, blockSize);
  171. int length = cipher.ProcessBlock(input, inOff, outBytes, outOff);
  172. for (int i = 0; i < blockSize; i++)
  173. {
  174. outBytes[outOff + i] ^= cbcV[i];
  175. }
  176. byte[] tmp = cbcV;
  177. cbcV = cbcNextV;
  178. cbcNextV = tmp;
  179. return length;
  180. }
  181. #endif
  182. }
  183. }
  184. #pragma warning restore
  185. #endif