WinzipAesCryptoStream.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. #if !NO_CRYPTO
  2. using System;
  3. using System.IO;
  4. using System.Security.Cryptography;
  5. using SharpCompress.Converters;
  6. namespace SharpCompress.Common.Zip
  7. {
  8. internal class WinzipAesCryptoStream : Stream
  9. {
  10. private const int BLOCK_SIZE_IN_BYTES = 16;
  11. private readonly SymmetricAlgorithm _cipher;
  12. private readonly byte[] _counter = new byte[BLOCK_SIZE_IN_BYTES];
  13. private readonly Stream _stream;
  14. private readonly ICryptoTransform _transform;
  15. private int _nonce = 1;
  16. private byte[] _counterOut = new byte[BLOCK_SIZE_IN_BYTES];
  17. private bool _isFinalBlock;
  18. private long _totalBytesLeftToRead;
  19. private bool _isDisposed;
  20. internal WinzipAesCryptoStream(Stream stream, WinzipAesEncryptionData winzipAesEncryptionData, long length)
  21. {
  22. this._stream = stream;
  23. _totalBytesLeftToRead = length;
  24. _cipher = CreateCipher(winzipAesEncryptionData);
  25. var iv = new byte[BLOCK_SIZE_IN_BYTES];
  26. _transform = _cipher.CreateEncryptor(winzipAesEncryptionData.KeyBytes, iv);
  27. }
  28. private SymmetricAlgorithm CreateCipher(WinzipAesEncryptionData winzipAesEncryptionData)
  29. {
  30. var cipher = Aes.Create();
  31. cipher.BlockSize = BLOCK_SIZE_IN_BYTES * 8;
  32. cipher.KeySize = winzipAesEncryptionData.KeyBytes.Length * 8;
  33. cipher.Mode = CipherMode.ECB;
  34. cipher.Padding = PaddingMode.None;
  35. return cipher;
  36. }
  37. public override bool CanRead
  38. {
  39. get { return true; }
  40. }
  41. public override bool CanSeek
  42. {
  43. get { return false; }
  44. }
  45. public override bool CanWrite
  46. {
  47. get { return false; }
  48. }
  49. public override long Length
  50. {
  51. get { throw new NotSupportedException(); }
  52. }
  53. public override long Position
  54. {
  55. get { throw new NotSupportedException(); }
  56. set { throw new NotSupportedException(); }
  57. }
  58. protected override void Dispose(bool disposing)
  59. {
  60. if (_isDisposed)
  61. {
  62. return;
  63. }
  64. _isDisposed = true;
  65. if (disposing)
  66. {
  67. //read out last 10 auth bytes
  68. var ten = new byte[10];
  69. _stream.ReadFully(ten);
  70. _stream.Dispose();
  71. }
  72. }
  73. public override void Flush()
  74. {
  75. throw new NotSupportedException();
  76. }
  77. public override int Read(byte[] buffer, int offset, int count)
  78. {
  79. if (_totalBytesLeftToRead == 0)
  80. {
  81. return 0;
  82. }
  83. int bytesToRead = count;
  84. if (count > _totalBytesLeftToRead)
  85. {
  86. bytesToRead = (int)_totalBytesLeftToRead;
  87. }
  88. int read = _stream.Read(buffer, offset, bytesToRead);
  89. _totalBytesLeftToRead -= read;
  90. ReadTransformBlocks(buffer, offset, read);
  91. return read;
  92. }
  93. private int ReadTransformOneBlock(byte[] buffer, int offset, int last)
  94. {
  95. if (_isFinalBlock)
  96. {
  97. throw new InvalidOperationException();
  98. }
  99. int bytesRemaining = last - offset;
  100. int bytesToRead = (bytesRemaining > BLOCK_SIZE_IN_BYTES)
  101. ? BLOCK_SIZE_IN_BYTES
  102. : bytesRemaining;
  103. // update the counter
  104. DataConverter.LittleEndian.PutBytes(_counter, 0, _nonce++);
  105. // Determine if this is the final block
  106. if ((bytesToRead == bytesRemaining) && (_totalBytesLeftToRead == 0))
  107. {
  108. _counterOut = _transform.TransformFinalBlock(_counter,
  109. 0,
  110. BLOCK_SIZE_IN_BYTES);
  111. _isFinalBlock = true;
  112. }
  113. else
  114. {
  115. _transform.TransformBlock(_counter,
  116. 0, // offset
  117. BLOCK_SIZE_IN_BYTES,
  118. _counterOut,
  119. 0); // offset
  120. }
  121. XorInPlace(buffer, offset, bytesToRead);
  122. return bytesToRead;
  123. }
  124. private void XorInPlace(byte[] buffer, int offset, int count)
  125. {
  126. for (int i = 0; i < count; i++)
  127. {
  128. buffer[offset + i] = (byte)(_counterOut[i] ^ buffer[offset + i]);
  129. }
  130. }
  131. private void ReadTransformBlocks(byte[] buffer, int offset, int count)
  132. {
  133. int posn = offset;
  134. int last = count + offset;
  135. while (posn < buffer.Length && posn < last)
  136. {
  137. int n = ReadTransformOneBlock(buffer, posn, last);
  138. posn += n;
  139. }
  140. }
  141. public override long Seek(long offset, SeekOrigin origin)
  142. {
  143. throw new NotSupportedException();
  144. }
  145. public override void SetLength(long value)
  146. {
  147. throw new NotSupportedException();
  148. }
  149. public override void Write(byte[] buffer, int offset, int count)
  150. {
  151. throw new NotSupportedException();
  152. }
  153. }
  154. }
  155. #endif