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 00000000..efd41eb7 Binary files /dev/null and b/test/TestArchives/Archives/Zip.deflate.pkware.zip differ