From f39d2bf53aece72550cf75f8db3dbb4f95137fe7 Mon Sep 17 00:00:00 2001 From: Vladimir Demidov Date: Thu, 24 Nov 2016 15:14:29 +0300 Subject: [PATCH 1/5] Open branch, to fix multiple crashes on repeated zip archives reading. Added fix. --- SharpCompress.sln | 88 ++++++------ .../Common/Zip/Headers/ZipFileEntry.cs | 23 +++- src/SharpCompress/Common/Zip/ZipFilePart.cs | 74 +++++----- .../Common/Zip/ZipHeaderFactory.cs | 129 +++++++++--------- .../SharpCompress.Test/Zip/ZipArchiveTests.cs | 25 +++- .../Archives/Zip.deflate.pkware.zip | Bin 0 -> 274 bytes 6 files changed, 190 insertions(+), 149 deletions(-) create mode 100644 test/TestArchives/Archives/Zip.deflate.pkware.zip diff --git a/SharpCompress.sln b/SharpCompress.sln index 950881e8..e82ccc1a 100644 --- a/SharpCompress.sln +++ b/SharpCompress.sln @@ -1,44 +1,44 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.24720.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F18F1765-4A02-42FD-9BEF-F0E2FCBD9D17}" - ProjectSection(SolutionItems) = preProject - global.json = global.json - EndProjectSection -EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SharpCompress", "src\SharpCompress\SharpCompress.xproj", "{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3C5BE746-03E5-4895-9988-0B57F162F86C}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0F0901FF-E8D9-426A-B5A2-17C7F47C1529}" -EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SharpCompress.Test", "test\SharpCompress.Test\SharpCompress.Test.xproj", "{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}" - ProjectSection(ProjectDependencies) = postProject - {FD19DDD8-72B2-4024-8665-0D1F7A2AA998} = {FD19DDD8-72B2-4024-8665-0D1F7A2AA998} - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Release|Any CPU.Build.0 = Release|Any CPU - {3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {FD19DDD8-72B2-4024-8665-0D1F7A2AA998} = {3C5BE746-03E5-4895-9988-0B57F162F86C} - {3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5} = {0F0901FF-E8D9-426A-B5A2-17C7F47C1529} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F18F1765-4A02-42FD-9BEF-F0E2FCBD9D17}" + ProjectSection(SolutionItems) = preProject + global.json = global.json + EndProjectSection +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SharpCompress", "src\SharpCompress\SharpCompress.xproj", "{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3C5BE746-03E5-4895-9988-0B57F162F86C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0F0901FF-E8D9-426A-B5A2-17C7F47C1529}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SharpCompress.Test", "test\SharpCompress.Test\SharpCompress.Test.xproj", "{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}" + ProjectSection(ProjectDependencies) = postProject + {FD19DDD8-72B2-4024-8665-0D1F7A2AA998} = {FD19DDD8-72B2-4024-8665-0D1F7A2AA998} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Release|Any CPU.Build.0 = Release|Any CPU + {3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {FD19DDD8-72B2-4024-8665-0D1F7A2AA998} = {3C5BE746-03E5-4895-9988-0B57F162F86C} + {3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5} = {0F0901FF-E8D9-426A-B5A2-17C7F47C1529} + EndGlobalSection +EndGlobal diff --git a/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs b/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs index 07b0b7df..9ae0fd67 100644 --- a/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs +++ b/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs @@ -65,9 +65,28 @@ namespace SharpCompress.Common.Zip.Headers internal List Extra { get; set; } - internal PkwareTraditionalEncryptionData PkwareTraditionalEncryptionData { get; set; } + //internal PkwareTraditionalEncryptionData PkwareTraditionalEncryptionData { get; set; } + + public string Password { get; set; } + + internal PkwareTraditionalEncryptionData ComposeEncryptionData(Stream archiveStream) + { + if (archiveStream == null) + { + throw new ArgumentNullException(nameof(archiveStream)); + } + + var buffer = new byte[12]; + archiveStream.Read(buffer, 0, 12); + + PkwareTraditionalEncryptionData encryptionData = PkwareTraditionalEncryptionData.ForRead(Password, this, buffer); + //CompressedSize -= 12; + + return encryptionData; + } + #if !NO_CRYPTO - internal WinzipAesEncryptionData WinzipAesEncryptionData { get; set; } + internal WinzipAesEncryptionData WinzipAesEncryptionData { get; set; } #endif internal ushort LastModifiedDate { get; set; } diff --git a/src/SharpCompress/Common/Zip/ZipFilePart.cs b/src/SharpCompress/Common/Zip/ZipFilePart.cs index 458ff35e..463a48e8 100644 --- a/src/SharpCompress/Common/Zip/ZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/ZipFilePart.cs @@ -124,40 +124,46 @@ namespace SharpCompress.Common.Zip } } - protected Stream GetCryptoStream(Stream plainStream) - { - if ((Header.CompressedSize == 0) -#if !NO_CRYPTO - && ((Header.PkwareTraditionalEncryptionData != null) - || (Header.WinzipAesEncryptionData != null))) -#else - && (Header.PkwareTraditionalEncryptionData != null)) -#endif - { - throw new NotSupportedException("Cannot encrypt file with unknown size at start."); - } - if ((Header.CompressedSize == 0) - && FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor)) - { - plainStream = new NonDisposingStream(plainStream); //make sure AES doesn't close - } - else - { - plainStream = new ReadOnlySubStream(plainStream, Header.CompressedSize); //make sure AES doesn't close - } - if (Header.PkwareTraditionalEncryptionData != null) - { - return new PkwareTraditionalCryptoStream(plainStream, Header.PkwareTraditionalEncryptionData, - CryptoMode.Decrypt); - } + protected Stream GetCryptoStream(Stream plainStream) + { + if ((Header.CompressedSize == 0) && !string.IsNullOrEmpty(Header.Password)) + { + throw new NotSupportedException("Cannot encrypt file with unknown size at start."); + } + + if ((Header.CompressedSize == 0) + && FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor)) + { + plainStream = new NonDisposingStream(plainStream); //make sure AES doesn't close + } + else + { + plainStream = new ReadOnlySubStream(plainStream, Header.CompressedSize); //make sure AES doesn't close + } + + if (FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted)) + { + switch (Header.CompressionMethod) + { + case ZipCompressionMethod.None: + case ZipCompressionMethod.Deflate: + case ZipCompressionMethod.Deflate64: + case ZipCompressionMethod.BZip2: + case ZipCompressionMethod.LZMA: + case ZipCompressionMethod.PPMd: + return new PkwareTraditionalCryptoStream(plainStream, Header.ComposeEncryptionData(plainStream), CryptoMode.Decrypt); + + case ZipCompressionMethod.WinzipAes: #if !NO_FILE - if (Header.WinzipAesEncryptionData != null) - { - //only read 10 less because the last ten are auth bytes - return new WinzipAesCryptoStream(plainStream, Header.WinzipAesEncryptionData, Header.CompressedSize - 10); - } + return Header.WinzipAesEncryptionData != null ? new WinzipAesCryptoStream(plainStream, Header.WinzipAesEncryptionData, Header.CompressedSize - 10) : plainStream; #endif - return plainStream; - } - } + + default: + throw new ArgumentOutOfRangeException(); + } + } + + return plainStream; + } + } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs index 2f8a9877..bc9d94a1 100644 --- a/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs @@ -107,81 +107,74 @@ namespace SharpCompress.Common.Zip } } - private void LoadHeader(ZipFileEntry entryHeader, Stream stream) - { - if (FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.Encrypted)) - { - if (!entryHeader.IsDirectory && - entryHeader.CompressedSize == 0 && - FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.UsePostDataDescriptor)) - { - throw new NotSupportedException( - "SharpCompress cannot currently read non-seekable Zip Streams with encrypted data that has been written in a non-seekable manner."); - } - if (password == null) - { - throw new CryptographicException("No password supplied for encrypted zip."); - } - if (entryHeader.CompressionMethod != ZipCompressionMethod.WinzipAes) - { - byte[] buffer = new byte[12]; - stream.Read(buffer, 0, 12); - entryHeader.PkwareTraditionalEncryptionData = PkwareTraditionalEncryptionData.ForRead(password, - entryHeader, - buffer); - entryHeader.CompressedSize -= 12; - } - else - { + private void LoadHeader(ZipFileEntry entryHeader, Stream stream) + { + if (FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.Encrypted)) + { + if (!entryHeader.IsDirectory && entryHeader.CompressedSize == 0 && + FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.UsePostDataDescriptor)) + { + throw new NotSupportedException("SharpCompress cannot currently read non-seekable Zip Streams with encrypted data that has been written in a non-seekable manner."); + } + + if (password == null) + { + throw new CryptographicException("No password supplied for encrypted zip."); + } + + entryHeader.Password = password; + + if (entryHeader.CompressionMethod == ZipCompressionMethod.WinzipAes) + { #if NO_CRYPTO throw new NotSupportedException("Cannot decrypt Winzip AES with Silverlight or WP7."); #else - var data = entryHeader.Extra.SingleOrDefault(x => x.Type == ExtraDataType.WinZipAes); - WinzipAesKeySize keySize = (WinzipAesKeySize) data.DataBytes[4]; + ExtraData data = entryHeader.Extra.SingleOrDefault(x => x.Type == ExtraDataType.WinZipAes); + if (data != null) + { + var keySize = (WinzipAesKeySize)data.DataBytes[4]; - byte[] salt = new byte[WinzipAesEncryptionData.KeyLengthInBytes(keySize)/2]; - byte[] passwordVerifyValue = new byte[2]; - stream.Read(salt, 0, salt.Length); - stream.Read(passwordVerifyValue, 0, 2); - entryHeader.WinzipAesEncryptionData = new WinzipAesEncryptionData(keySize, salt, passwordVerifyValue, - password); - entryHeader.CompressedSize -= (uint) (salt.Length + 2); + var salt = new byte[WinzipAesEncryptionData.KeyLengthInBytes(keySize) / 2]; + var passwordVerifyValue = new byte[2]; + stream.Read(salt, 0, salt.Length); + stream.Read(passwordVerifyValue, 0, 2); + entryHeader.WinzipAesEncryptionData = + new WinzipAesEncryptionData(keySize, salt, passwordVerifyValue, password); + entryHeader.CompressedSize -= (uint)(salt.Length + 2); + } #endif - } - } - if (entryHeader.IsDirectory) - { - return; - } + } + } - //if (FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.UsePostDataDescriptor)) - //{ - // entryHeader.PackedStream = new ReadOnlySubStream(stream); - //} - //else - //{ - switch (mode) - { - case StreamingMode.Seekable: - { - entryHeader.DataStartPosition = stream.Position; - stream.Position += entryHeader.CompressedSize; - } - break; - case StreamingMode.Streaming: - { - entryHeader.PackedStream = stream; - } - break; - default: - { - throw new InvalidFormatException("Invalid StreamingMode"); - } - } + if (entryHeader.IsDirectory) + { + return; + } - //} - } - } + //if (FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.UsePostDataDescriptor)) + //{ + // entryHeader.PackedStream = new ReadOnlySubStream(stream); + //} + //else + //{ + switch (mode) + { + case StreamingMode.Seekable: + entryHeader.DataStartPosition = stream.Position; + stream.Position += entryHeader.CompressedSize; + break; + + case StreamingMode.Streaming: + entryHeader.PackedStream = stream; + break; + + default: + throw new InvalidFormatException("Invalid StreamingMode"); + } + + //} + } + } } \ No newline at end of file diff --git a/test/SharpCompress.Test/Zip/ZipArchiveTests.cs b/test/SharpCompress.Test/Zip/ZipArchiveTests.cs index f38e0918..22292584 100644 --- a/test/SharpCompress.Test/Zip/ZipArchiveTests.cs +++ b/test/SharpCompress.Test/Zip/ZipArchiveTests.cs @@ -374,8 +374,31 @@ namespace SharpCompress.Test Assert.Equal(count3, 3); } + [Fact] + public void Zip_Deflate_PKWear_Multipy_Entry_Access() + { + string zipFile = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.pkware.zip"); - class NonSeekableMemoryStream : MemoryStream + using (FileStream fileStream = File.Open(zipFile, FileMode.Open)) + { + using (IArchive archive = ArchiveFactory.Open(fileStream, new ReaderOptions { Password = "12345678" })) + { + var entries = archive.Entries.Where(entry => !entry.IsDirectory); + foreach (IArchiveEntry entry in entries) + { + for (var i = 0; i < 100; i++) + { + using (var memoryStream = new MemoryStream()) + using (Stream entryStream = entry.OpenEntryStream()) + entryStream.CopyTo(memoryStream); + } + } + } + } + + } + + class NonSeekableMemoryStream : MemoryStream { public override bool CanSeek { diff --git a/test/TestArchives/Archives/Zip.deflate.pkware.zip b/test/TestArchives/Archives/Zip.deflate.pkware.zip new file mode 100644 index 0000000000000000000000000000000000000000..efd41eb7d5f65813b4fd76fe013bf3e3bce63d90 GIT binary patch literal 274 zcmWIWW@Zs#00GUmI7bi-!|V(U3~u>3DXB&J5Jk)k91P0qi#(@xz2O3B5oTau;72HO z%gjmDE2$`H|4=9?)_UoG+FU*Zi#sysg{K<|1$Z+u*)xDNF)(1-qXM;307^q` Date: Thu, 24 Nov 2016 16:21:02 +0300 Subject: [PATCH 2/5] Fixed some issues after review. --- src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs | 7 ++----- src/SharpCompress/Common/Zip/ZipFilePart.cs | 6 +++++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs b/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs index 9ae0fd67..7b75c43d 100644 --- a/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs +++ b/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs @@ -64,9 +64,7 @@ namespace SharpCompress.Common.Zip.Headers internal uint UncompressedSize { get; set; } internal List Extra { get; set; } - - //internal PkwareTraditionalEncryptionData PkwareTraditionalEncryptionData { get; set; } - + public string Password { get; set; } internal PkwareTraditionalEncryptionData ComposeEncryptionData(Stream archiveStream) @@ -80,8 +78,7 @@ namespace SharpCompress.Common.Zip.Headers archiveStream.Read(buffer, 0, 12); PkwareTraditionalEncryptionData encryptionData = PkwareTraditionalEncryptionData.ForRead(Password, this, buffer); - //CompressedSize -= 12; - + return encryptionData; } diff --git a/src/SharpCompress/Common/Zip/ZipFilePart.cs b/src/SharpCompress/Common/Zip/ZipFilePart.cs index 463a48e8..40b5f178 100644 --- a/src/SharpCompress/Common/Zip/ZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/ZipFilePart.cs @@ -155,8 +155,12 @@ namespace SharpCompress.Common.Zip case ZipCompressionMethod.WinzipAes: #if !NO_FILE - return Header.WinzipAesEncryptionData != null ? new WinzipAesCryptoStream(plainStream, Header.WinzipAesEncryptionData, Header.CompressedSize - 10) : plainStream; + if (Header.WinzipAesEncryptionData != null) + { + return new WinzipAesCryptoStream(plainStream, Header.WinzipAesEncryptionData, Header.CompressedSize - 10); + } #endif + return plainStream; default: throw new ArgumentOutOfRangeException(); From e181fa8c4ad7a2b01a99ce3d6ac482c7bb5f7b1e Mon Sep 17 00:00:00 2001 From: Vladimir Demidov Date: Thu, 24 Nov 2016 17:11:43 +0300 Subject: [PATCH 3/5] Restored original tabs. --- .../Common/Zip/Headers/ZipFileEntry.cs | 30 ++--- src/SharpCompress/Common/Zip/ZipFilePart.cs | 78 ++++++------- .../Common/Zip/ZipHeaderFactory.cs | 110 +++++++++--------- .../SharpCompress.Test/Zip/ZipArchiveTests.cs | 44 +++---- 4 files changed, 131 insertions(+), 131 deletions(-) diff --git a/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs b/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs index 7b75c43d..c099dba0 100644 --- a/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs +++ b/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs @@ -64,26 +64,26 @@ namespace SharpCompress.Common.Zip.Headers internal uint UncompressedSize { get; set; } internal List Extra { get; set; } - - public string Password { get; set; } + + public string Password { get; set; } - internal PkwareTraditionalEncryptionData ComposeEncryptionData(Stream archiveStream) - { - if (archiveStream == null) - { - throw new ArgumentNullException(nameof(archiveStream)); - } + internal PkwareTraditionalEncryptionData ComposeEncryptionData(Stream archiveStream) + { + if (archiveStream == null) + { + throw new ArgumentNullException(nameof(archiveStream)); + } - var buffer = new byte[12]; - archiveStream.Read(buffer, 0, 12); + var buffer = new byte[12]; + archiveStream.Read(buffer, 0, 12); - PkwareTraditionalEncryptionData encryptionData = PkwareTraditionalEncryptionData.ForRead(Password, this, buffer); - - return encryptionData; - } + PkwareTraditionalEncryptionData encryptionData = PkwareTraditionalEncryptionData.ForRead(Password, this, buffer); + + return encryptionData; + } #if !NO_CRYPTO - internal WinzipAesEncryptionData WinzipAesEncryptionData { get; set; } + internal WinzipAesEncryptionData WinzipAesEncryptionData { get; set; } #endif internal ushort LastModifiedDate { get; set; } diff --git a/src/SharpCompress/Common/Zip/ZipFilePart.cs b/src/SharpCompress/Common/Zip/ZipFilePart.cs index 40b5f178..ed0cc568 100644 --- a/src/SharpCompress/Common/Zip/ZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/ZipFilePart.cs @@ -124,50 +124,50 @@ namespace SharpCompress.Common.Zip } } - protected Stream GetCryptoStream(Stream plainStream) - { - if ((Header.CompressedSize == 0) && !string.IsNullOrEmpty(Header.Password)) - { - throw new NotSupportedException("Cannot encrypt file with unknown size at start."); - } + protected Stream GetCryptoStream(Stream plainStream) + { + if ((Header.CompressedSize == 0) && !string.IsNullOrEmpty(Header.Password)) + { + throw new NotSupportedException("Cannot encrypt file with unknown size at start."); + } - if ((Header.CompressedSize == 0) - && FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor)) - { - plainStream = new NonDisposingStream(plainStream); //make sure AES doesn't close - } - else - { - plainStream = new ReadOnlySubStream(plainStream, Header.CompressedSize); //make sure AES doesn't close - } + if ((Header.CompressedSize == 0) + && FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor)) + { + plainStream = new NonDisposingStream(plainStream); //make sure AES doesn't close + } + else + { + plainStream = new ReadOnlySubStream(plainStream, Header.CompressedSize); //make sure AES doesn't close + } - if (FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted)) - { - switch (Header.CompressionMethod) - { - case ZipCompressionMethod.None: - case ZipCompressionMethod.Deflate: - case ZipCompressionMethod.Deflate64: - case ZipCompressionMethod.BZip2: - case ZipCompressionMethod.LZMA: - case ZipCompressionMethod.PPMd: - return new PkwareTraditionalCryptoStream(plainStream, Header.ComposeEncryptionData(plainStream), CryptoMode.Decrypt); + if (FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted)) + { + switch (Header.CompressionMethod) + { + case ZipCompressionMethod.None: + case ZipCompressionMethod.Deflate: + case ZipCompressionMethod.Deflate64: + case ZipCompressionMethod.BZip2: + case ZipCompressionMethod.LZMA: + case ZipCompressionMethod.PPMd: + return new PkwareTraditionalCryptoStream(plainStream, Header.ComposeEncryptionData(plainStream), CryptoMode.Decrypt); - case ZipCompressionMethod.WinzipAes: + case ZipCompressionMethod.WinzipAes: #if !NO_FILE - if (Header.WinzipAesEncryptionData != null) - { - return new WinzipAesCryptoStream(plainStream, Header.WinzipAesEncryptionData, Header.CompressedSize - 10); - } + if (Header.WinzipAesEncryptionData != null) + { + return new WinzipAesCryptoStream(plainStream, Header.WinzipAesEncryptionData, Header.CompressedSize - 10); + } #endif - return plainStream; + return plainStream; - default: - throw new ArgumentOutOfRangeException(); - } - } + default: + throw new ArgumentOutOfRangeException(); + } + } - return plainStream; - } - } + return plainStream; + } + } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs index bc9d94a1..b1ecc403 100644 --- a/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs @@ -107,74 +107,74 @@ namespace SharpCompress.Common.Zip } } - private void LoadHeader(ZipFileEntry entryHeader, Stream stream) - { - if (FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.Encrypted)) - { - if (!entryHeader.IsDirectory && entryHeader.CompressedSize == 0 && - FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.UsePostDataDescriptor)) - { - throw new NotSupportedException("SharpCompress cannot currently read non-seekable Zip Streams with encrypted data that has been written in a non-seekable manner."); - } + private void LoadHeader(ZipFileEntry entryHeader, Stream stream) + { + if (FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.Encrypted)) + { + if (!entryHeader.IsDirectory && entryHeader.CompressedSize == 0 && + FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.UsePostDataDescriptor)) + { + throw new NotSupportedException("SharpCompress cannot currently read non-seekable Zip Streams with encrypted data that has been written in a non-seekable manner."); + } - if (password == null) - { - throw new CryptographicException("No password supplied for encrypted zip."); - } + if (password == null) + { + throw new CryptographicException("No password supplied for encrypted zip."); + } - entryHeader.Password = password; + entryHeader.Password = password; - if (entryHeader.CompressionMethod == ZipCompressionMethod.WinzipAes) - { + if (entryHeader.CompressionMethod == ZipCompressionMethod.WinzipAes) + { #if NO_CRYPTO throw new NotSupportedException("Cannot decrypt Winzip AES with Silverlight or WP7."); #else - ExtraData data = entryHeader.Extra.SingleOrDefault(x => x.Type == ExtraDataType.WinZipAes); - if (data != null) - { - var keySize = (WinzipAesKeySize)data.DataBytes[4]; + ExtraData data = entryHeader.Extra.SingleOrDefault(x => x.Type == ExtraDataType.WinZipAes); + if (data != null) + { + var keySize = (WinzipAesKeySize)data.DataBytes[4]; - var salt = new byte[WinzipAesEncryptionData.KeyLengthInBytes(keySize) / 2]; - var passwordVerifyValue = new byte[2]; - stream.Read(salt, 0, salt.Length); - stream.Read(passwordVerifyValue, 0, 2); - entryHeader.WinzipAesEncryptionData = - new WinzipAesEncryptionData(keySize, salt, passwordVerifyValue, password); + var salt = new byte[WinzipAesEncryptionData.KeyLengthInBytes(keySize) / 2]; + var passwordVerifyValue = new byte[2]; + stream.Read(salt, 0, salt.Length); + stream.Read(passwordVerifyValue, 0, 2); + entryHeader.WinzipAesEncryptionData = + new WinzipAesEncryptionData(keySize, salt, passwordVerifyValue, password); - entryHeader.CompressedSize -= (uint)(salt.Length + 2); - } + entryHeader.CompressedSize -= (uint)(salt.Length + 2); + } #endif - } - } + } + } - if (entryHeader.IsDirectory) - { - return; - } + if (entryHeader.IsDirectory) + { + return; + } - //if (FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.UsePostDataDescriptor)) - //{ - // entryHeader.PackedStream = new ReadOnlySubStream(stream); - //} - //else - //{ - switch (mode) - { - case StreamingMode.Seekable: - entryHeader.DataStartPosition = stream.Position; - stream.Position += entryHeader.CompressedSize; - break; + //if (FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.UsePostDataDescriptor)) + //{ + // entryHeader.PackedStream = new ReadOnlySubStream(stream); + //} + //else + //{ + switch (mode) + { + case StreamingMode.Seekable: + entryHeader.DataStartPosition = stream.Position; + stream.Position += entryHeader.CompressedSize; + break; - case StreamingMode.Streaming: - entryHeader.PackedStream = stream; - break; + case StreamingMode.Streaming: + entryHeader.PackedStream = stream; + break; - default: - throw new InvalidFormatException("Invalid StreamingMode"); - } + default: + throw new InvalidFormatException("Invalid StreamingMode"); + } - //} - } - } + //} + } + } } \ No newline at end of file diff --git a/test/SharpCompress.Test/Zip/ZipArchiveTests.cs b/test/SharpCompress.Test/Zip/ZipArchiveTests.cs index 22292584..3f469241 100644 --- a/test/SharpCompress.Test/Zip/ZipArchiveTests.cs +++ b/test/SharpCompress.Test/Zip/ZipArchiveTests.cs @@ -374,31 +374,31 @@ namespace SharpCompress.Test Assert.Equal(count3, 3); } - [Fact] - public void Zip_Deflate_PKWear_Multipy_Entry_Access() - { - string zipFile = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.pkware.zip"); + [Fact] + public void Zip_Deflate_PKWear_Multipy_Entry_Access() + { + string zipFile = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.pkware.zip"); - using (FileStream fileStream = File.Open(zipFile, FileMode.Open)) - { - using (IArchive archive = ArchiveFactory.Open(fileStream, new ReaderOptions { Password = "12345678" })) - { - var entries = archive.Entries.Where(entry => !entry.IsDirectory); - foreach (IArchiveEntry entry in entries) - { - for (var i = 0; i < 100; i++) - { - using (var memoryStream = new MemoryStream()) - using (Stream entryStream = entry.OpenEntryStream()) - entryStream.CopyTo(memoryStream); - } - } - } - } + using (FileStream fileStream = File.Open(zipFile, FileMode.Open)) + { + using (IArchive archive = ArchiveFactory.Open(fileStream, new ReaderOptions { Password = "12345678" })) + { + var entries = archive.Entries.Where(entry => !entry.IsDirectory); + foreach (IArchiveEntry entry in entries) + { + for (var i = 0; i < 100; i++) + { + using (var memoryStream = new MemoryStream()) + using (Stream entryStream = entry.OpenEntryStream()) + entryStream.CopyTo(memoryStream); + } + } + } + } - } + } - class NonSeekableMemoryStream : MemoryStream + class NonSeekableMemoryStream : MemoryStream { public override bool CanSeek { From 031286c5ebee3488d935426fc480eebb4dee03e7 Mon Sep 17 00:00:00 2001 From: Vladimir Demidov Date: Thu, 24 Nov 2016 18:01:49 +0300 Subject: [PATCH 4/5] Fixed defects after review. --- src/SharpCompress/Common/Zip/ZipFilePart.cs | 9 ++++++++- src/SharpCompress/Common/Zip/ZipHeaderFactory.cs | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/SharpCompress/Common/Zip/ZipFilePart.cs b/src/SharpCompress/Common/Zip/ZipFilePart.cs index ed0cc568..4309aa83 100644 --- a/src/SharpCompress/Common/Zip/ZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/ZipFilePart.cs @@ -126,7 +126,7 @@ namespace SharpCompress.Common.Zip protected Stream GetCryptoStream(Stream plainStream) { - if ((Header.CompressedSize == 0) && !string.IsNullOrEmpty(Header.Password)) + if (Header.CompressedSize == 0) { throw new NotSupportedException("Cannot encrypt file with unknown size at start."); } @@ -151,9 +151,12 @@ namespace SharpCompress.Common.Zip case ZipCompressionMethod.BZip2: case ZipCompressionMethod.LZMA: case ZipCompressionMethod.PPMd: + { return new PkwareTraditionalCryptoStream(plainStream, Header.ComposeEncryptionData(plainStream), CryptoMode.Decrypt); + } case ZipCompressionMethod.WinzipAes: + { #if !NO_FILE if (Header.WinzipAesEncryptionData != null) { @@ -161,9 +164,13 @@ namespace SharpCompress.Common.Zip } #endif return plainStream; + } default: + { throw new ArgumentOutOfRangeException(); + } + } } diff --git a/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs index b1ecc403..cb01f134 100644 --- a/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs @@ -162,16 +162,22 @@ namespace SharpCompress.Common.Zip switch (mode) { case StreamingMode.Seekable: + { entryHeader.DataStartPosition = stream.Position; stream.Position += entryHeader.CompressedSize; break; + } case StreamingMode.Streaming: + { entryHeader.PackedStream = stream; break; + } default: + { throw new InvalidFormatException("Invalid StreamingMode"); + } } //} From 2959b4d70199035a21ee20a525b5e6bc5fb62cb3 Mon Sep 17 00:00:00 2001 From: Vladimir Demidov Date: Thu, 24 Nov 2016 20:41:08 +0300 Subject: [PATCH 5/5] Modified check integrity condition for the encrypted file. --- src/SharpCompress/Common/Zip/ZipFilePart.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/SharpCompress/Common/Zip/ZipFilePart.cs b/src/SharpCompress/Common/Zip/ZipFilePart.cs index 4309aa83..ebb55c64 100644 --- a/src/SharpCompress/Common/Zip/ZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/ZipFilePart.cs @@ -126,7 +126,9 @@ namespace SharpCompress.Common.Zip protected Stream GetCryptoStream(Stream plainStream) { - if (Header.CompressedSize == 0) + bool isFileEncrypted = FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted); + + if (Header.CompressedSize == 0 && isFileEncrypted) { throw new NotSupportedException("Cannot encrypt file with unknown size at start."); } @@ -141,7 +143,7 @@ namespace SharpCompress.Common.Zip plainStream = new ReadOnlySubStream(plainStream, Header.CompressedSize); //make sure AES doesn't close } - if (FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted)) + if (isFileEncrypted) { switch (Header.CompressionMethod) {