diff --git a/src/ICSharpCode.SharpZipLib/Core/StreamUtils.cs b/src/ICSharpCode.SharpZipLib/Core/StreamUtils.cs index 348fe68de..6d0d9b304 100644 --- a/src/ICSharpCode.SharpZipLib/Core/StreamUtils.cs +++ b/src/ICSharpCode.SharpZipLib/Core/StreamUtils.cs @@ -64,6 +64,54 @@ static public void ReadFully(Stream stream, byte[] buffer, int offset, int count } } + /// + /// Read as much data as possible from a ", up to the requested number of bytes + /// + /// The stream to read data from. + /// The buffer to store data in. + /// The offset at which to begin storing data. + /// The number of bytes of data to store. + /// Required parameter is null + /// and or are invalid. + static public int ReadRequestedBytes(Stream stream, byte[] buffer, int offset, int count) + { + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + // Offset can equal length when buffer and count are 0. + if ((offset < 0) || (offset > buffer.Length)) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + + if ((count < 0) || (offset + count > buffer.Length)) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + int totalReadCount = 0; + while (count > 0) + { + int readCount = stream.Read(buffer, offset, count); + if (readCount <= 0) + { + break; + } + offset += readCount; + count -= readCount; + totalReadCount += readCount; + } + + return totalReadCount; + } + /// /// Copy the contents of one to another. /// diff --git a/src/ICSharpCode.SharpZipLib/Encryption/ZipAESStream.cs b/src/ICSharpCode.SharpZipLib/Encryption/ZipAESStream.cs index dc16a7c4d..cf0792c47 100644 --- a/src/ICSharpCode.SharpZipLib/Encryption/ZipAESStream.cs +++ b/src/ICSharpCode.SharpZipLib/Encryption/ZipAESStream.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Security.Cryptography; +using ICSharpCode.SharpZipLib.Core; namespace ICSharpCode.SharpZipLib.Encryption { @@ -78,7 +79,7 @@ public override int Read(byte[] buffer, int offset, int count) _slideBufFreePos -= _slideBufStartPos; // Note the -= _slideBufStartPos = 0; } - int obtained = _stream.Read(_slideBuffer, _slideBufFreePos, lengthToRead); + int obtained = StreamUtils.ReadRequestedBytes(_stream, _slideBuffer, _slideBufFreePos, lengthToRead); _slideBufFreePos += obtained; // Recalculate how much data we now have diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs index fa4c09401..75d298922 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs @@ -3523,12 +3523,12 @@ private Stream CreateAndInitDecryptionStream(Stream baseStream, ZipEntry entry) } int saltLen = entry.AESSaltLen; byte[] saltBytes = new byte[saltLen]; - int saltIn = baseStream.Read(saltBytes, 0, saltLen); + int saltIn = StreamUtils.ReadRequestedBytes(baseStream, saltBytes, 0, saltLen); if (saltIn != saltLen) throw new ZipException("AES Salt expected " + saltLen + " got " + saltIn); // byte[] pwdVerifyRead = new byte[2]; - baseStream.Read(pwdVerifyRead, 0, 2); + StreamUtils.ReadFully(baseStream, pwdVerifyRead); int blockSize = entry.AESKeySize / 8; // bits to bytes var decryptor = new ZipAESTransform(rawPassword_, saltBytes, blockSize, false); diff --git a/test/ICSharpCode.SharpZipLib.Tests/TestSupport/Streams.cs b/test/ICSharpCode.SharpZipLib.Tests/TestSupport/Streams.cs index dd8dd1dd9..f649e4175 100644 --- a/test/ICSharpCode.SharpZipLib.Tests/TestSupport/Streams.cs +++ b/test/ICSharpCode.SharpZipLib.Tests/TestSupport/Streams.cs @@ -453,4 +453,22 @@ protected override void Dispose(bool disposing) #endregion Instance Fields } + + internal class SingleByteReadingStream : MemoryStream + { + /// + /// Initializes a new instance of the class. + /// + public SingleByteReadingStream() + { + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count > 0) + count = 1; + + return base.Read(buffer, offset, count); + } + } } diff --git a/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipEncryptionHandling.cs b/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipEncryptionHandling.cs index 6d4c3d74b..bac6d2fb3 100644 --- a/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipEncryptionHandling.cs +++ b/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipEncryptionHandling.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.IO; using System.Text; +using ICSharpCode.SharpZipLib.Tests.TestSupport; namespace ICSharpCode.SharpZipLib.Tests.Zip { @@ -56,6 +57,37 @@ public void ZipFileAesDecryption() } } + [Test] + [Category("Encryption")] + [Category("Zip")] + public void ZipFileAesRead() + { + var password = "password"; + + using (var ms = new SingleByteReadingStream()) + { + WriteEncryptedZipToStream(ms, password, 256); + ms.Seek(0, SeekOrigin.Begin); + + var zipFile = new ZipFile(ms) + { + Password = password + }; + + foreach (ZipEntry entry in zipFile) + { + if (!entry.IsFile) continue; + + using (var zis = zipFile.GetInputStream(entry)) + using (var sr = new StreamReader(zis, Encoding.UTF8)) + { + var content = sr.ReadToEnd(); + Assert.AreEqual(DummyDataString, content, "Decompressed content does not match input data"); + } + } + } + } + private static readonly string[] possible7zPaths = new[] { // Check in PATH "7z", "7za",