AesDecoderStream.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. 
  2. #if !NO_CRYPTO
  3. using System;
  4. using System.IO;
  5. using System.Security.Cryptography;
  6. using System.Text;
  7. using SharpCompress.Compressors.LZMA.Utilites;
  8. namespace SharpCompress.Compressors.LZMA
  9. {
  10. internal class AesDecoderStream : DecoderStream2
  11. {
  12. #region Variables
  13. private readonly Stream mStream;
  14. private readonly ICryptoTransform mDecoder;
  15. private readonly byte[] mBuffer;
  16. private long mWritten;
  17. private readonly long mLimit;
  18. private int mOffset;
  19. private int mEnding;
  20. private int mUnderflow;
  21. private bool isDisposed;
  22. #endregion
  23. #region Stream Methods
  24. public AesDecoderStream(Stream input, byte[] info, IPasswordProvider pass, long limit)
  25. {
  26. mStream = input;
  27. mLimit = limit;
  28. if (((uint) input.Length & 15) != 0)
  29. throw new NotSupportedException("AES decoder does not support padding.");
  30. int numCyclesPower;
  31. byte[] salt, seed;
  32. Init(info, out numCyclesPower, out salt, out seed);
  33. byte[] password = Encoding.Unicode.GetBytes(pass.CryptoGetTextPassword());
  34. byte[] key = InitKey(numCyclesPower, salt, password);
  35. using (var aes = Aes.Create())
  36. {
  37. aes.Mode = CipherMode.CBC;
  38. aes.Padding = PaddingMode.None;
  39. mDecoder = aes.CreateDecryptor(key, seed);
  40. }
  41. mBuffer = new byte[4 << 10];
  42. }
  43. protected override void Dispose(bool disposing)
  44. {
  45. try
  46. {
  47. if (isDisposed)
  48. {
  49. return;
  50. }
  51. isDisposed = true;
  52. if (disposing)
  53. {
  54. mStream.Dispose();
  55. mDecoder.Dispose();
  56. }
  57. }
  58. finally
  59. {
  60. base.Dispose(disposing);
  61. }
  62. }
  63. public override long Position
  64. {
  65. get
  66. {
  67. return mWritten;
  68. }
  69. }
  70. public override long Length
  71. {
  72. get
  73. {
  74. return mLimit;
  75. }
  76. }
  77. public override int Read(byte[] buffer, int offset, int count)
  78. {
  79. if (count == 0
  80. || mWritten == mLimit)
  81. return 0;
  82. if (mUnderflow > 0)
  83. return HandleUnderflow(buffer, offset, count);
  84. // Need at least 16 bytes to proceed.
  85. if (mEnding - mOffset < 16)
  86. {
  87. Buffer.BlockCopy(mBuffer, mOffset, mBuffer, 0, mEnding - mOffset);
  88. mEnding -= mOffset;
  89. mOffset = 0;
  90. do
  91. {
  92. int read = mStream.Read(mBuffer, mEnding, mBuffer.Length - mEnding);
  93. if (read == 0)
  94. {
  95. // We are not done decoding and have less than 16 bytes.
  96. throw new EndOfStreamException();
  97. }
  98. mEnding += read;
  99. }
  100. while (mEnding - mOffset < 16);
  101. }
  102. // We shouldn't return more data than we are limited to.
  103. // Currently this is handled by forcing an underflow if
  104. // the stream length is not a multiple of the block size.
  105. if (count > mLimit - mWritten)
  106. count = (int) (mLimit - mWritten);
  107. // We cannot transform less than 16 bytes into the target buffer,
  108. // but we also cannot return zero, so we need to handle this.
  109. // We transform the data locally and use our own buffer as cache.
  110. if (count < 16)
  111. return HandleUnderflow(buffer, offset, count);
  112. if (count > mEnding - mOffset)
  113. count = mEnding - mOffset;
  114. // Otherwise we transform directly into the target buffer.
  115. int processed = mDecoder.TransformBlock(mBuffer, mOffset, count & ~15, buffer, offset);
  116. mOffset += processed;
  117. mWritten += processed;
  118. return processed;
  119. }
  120. #endregion
  121. #region Private Methods
  122. private void Init(byte[] info, out int numCyclesPower, out byte[] salt, out byte[] iv)
  123. {
  124. byte bt = info[0];
  125. numCyclesPower = bt & 0x3F;
  126. if ((bt & 0xC0) == 0)
  127. {
  128. salt = new byte[0];
  129. iv = new byte[0];
  130. return;
  131. }
  132. int saltSize = (bt >> 7) & 1;
  133. int ivSize = (bt >> 6) & 1;
  134. if (info.Length == 1)
  135. throw new InvalidOperationException();
  136. byte bt2 = info[1];
  137. saltSize += (bt2 >> 4);
  138. ivSize += (bt2 & 15);
  139. if (info.Length < 2 + saltSize + ivSize)
  140. throw new InvalidOperationException();
  141. salt = new byte[saltSize];
  142. for (int i = 0; i < saltSize; i++)
  143. salt[i] = info[i + 2];
  144. iv = new byte[16];
  145. for (int i = 0; i < ivSize; i++)
  146. iv[i] = info[i + saltSize + 2];
  147. if (numCyclesPower > 24)
  148. throw new NotSupportedException();
  149. }
  150. private byte[] InitKey(int mNumCyclesPower, byte[] salt, byte[] pass)
  151. {
  152. if (mNumCyclesPower == 0x3F)
  153. {
  154. var key = new byte[32];
  155. int pos;
  156. for (pos = 0; pos < salt.Length; pos++)
  157. key[pos] = salt[pos];
  158. for (int i = 0; i < pass.Length && pos < 32; i++)
  159. key[pos++] = pass[i];
  160. return key;
  161. }
  162. else
  163. {
  164. #if NETSTANDARD1_3
  165. using (IncrementalHash sha = IncrementalHash.CreateHash(HashAlgorithmName.SHA256))
  166. {
  167. byte[] counter = new byte[8];
  168. long numRounds = 1L << mNumCyclesPower;
  169. for (long round = 0; round < numRounds; round++)
  170. {
  171. sha.AppendData(salt, 0, salt.Length);
  172. sha.AppendData(pass, 0, pass.Length);
  173. sha.AppendData(counter, 0, 8);
  174. // This mirrors the counter so we don't have to convert long to byte[] each round.
  175. // (It also ensures the counter is little endian, which BitConverter does not.)
  176. for (int i = 0; i < 8; i++)
  177. if (++counter[i] != 0)
  178. break;
  179. }
  180. return sha.GetHashAndReset();
  181. }
  182. #else
  183. using (var sha = SHA256.Create())
  184. {
  185. byte[] counter = new byte[8];
  186. long numRounds = 1L << mNumCyclesPower;
  187. for (long round = 0; round < numRounds; round++)
  188. {
  189. sha.TransformBlock(salt, 0, salt.Length, null, 0);
  190. sha.TransformBlock(pass, 0, pass.Length, null, 0);
  191. sha.TransformBlock(counter, 0, 8, null, 0);
  192. // This mirrors the counter so we don't have to convert long to byte[] each round.
  193. // (It also ensures the counter is little endian, which BitConverter does not.)
  194. for (int i = 0; i < 8; i++)
  195. if (++counter[i] != 0)
  196. break;
  197. }
  198. sha.TransformFinalBlock(counter, 0, 0);
  199. return sha.Hash;
  200. }
  201. #endif
  202. }
  203. }
  204. private int HandleUnderflow(byte[] buffer, int offset, int count)
  205. {
  206. // If this is zero we were called to create a new underflow buffer.
  207. // Just transform as much as possible so we can feed from it as long as possible.
  208. if (mUnderflow == 0)
  209. {
  210. int blockSize = (mEnding - mOffset) & ~15;
  211. mUnderflow = mDecoder.TransformBlock(mBuffer, mOffset, blockSize, mBuffer, mOffset);
  212. }
  213. if (count > mUnderflow)
  214. count = mUnderflow;
  215. Buffer.BlockCopy(mBuffer, mOffset, buffer, offset, count);
  216. mWritten += count;
  217. mOffset += count;
  218. mUnderflow -= count;
  219. return count;
  220. }
  221. #endregion
  222. }
  223. }
  224. #endif