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..c099dba0 100644 --- a/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs +++ b/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs @@ -64,8 +64,24 @@ namespace SharpCompress.Common.Zip.Headers internal uint UncompressedSize { get; set; } internal List Extra { 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); + + return encryptionData; + } - internal PkwareTraditionalEncryptionData PkwareTraditionalEncryptionData { get; set; } #if !NO_CRYPTO internal WinzipAesEncryptionData WinzipAesEncryptionData { get; set; } #endif diff --git a/src/SharpCompress/Common/Zip/ZipFilePart.cs b/src/SharpCompress/Common/Zip/ZipFilePart.cs index 458ff35e..ebb55c64 100644 --- a/src/SharpCompress/Common/Zip/ZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/ZipFilePart.cs @@ -126,16 +126,13 @@ 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 + bool isFileEncrypted = FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted); + + if (Header.CompressedSize == 0 && isFileEncrypted) { throw new NotSupportedException("Cannot encrypt file with unknown size at start."); } + if ((Header.CompressedSize == 0) && FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor)) { @@ -145,18 +142,40 @@ namespace SharpCompress.Common.Zip { plainStream = new ReadOnlySubStream(plainStream, Header.CompressedSize); //make sure AES doesn't close } - if (Header.PkwareTraditionalEncryptionData != null) + + if (isFileEncrypted) { - return new PkwareTraditionalCryptoStream(plainStream, Header.PkwareTraditionalEncryptionData, - CryptoMode.Decrypt); - } + 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); - } + if (Header.WinzipAesEncryptionData != null) + { + return new WinzipAesCryptoStream(plainStream, Header.WinzipAesEncryptionData, Header.CompressedSize - 10); + } #endif + return plainStream; + } + + default: + { + throw new ArgumentOutOfRangeException(); + } + + } + } + return plainStream; } } diff --git a/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs index 2f8a9877..cb01f134 100644 --- a/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs @@ -111,46 +111,43 @@ namespace SharpCompress.Common.Zip { if (FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.Encrypted)) { - if (!entryHeader.IsDirectory && - entryHeader.CompressedSize == 0 && + 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."); + 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 + + 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; @@ -168,13 +165,15 @@ namespace SharpCompress.Common.Zip { entryHeader.DataStartPosition = stream.Position; stream.Position += entryHeader.CompressedSize; - } break; + } + case StreamingMode.Streaming: { entryHeader.PackedStream = stream; - } break; + } + default: { throw new InvalidFormatException("Invalid StreamingMode"); diff --git a/test/SharpCompress.Test/Zip/ZipArchiveTests.cs b/test/SharpCompress.Test/Zip/ZipArchiveTests.cs index f38e0918..3f469241 100644 --- a/test/SharpCompress.Test/Zip/ZipArchiveTests.cs +++ b/test/SharpCompress.Test/Zip/ZipArchiveTests.cs @@ -374,6 +374,29 @@ 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"); + + 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 { 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