diff --git a/src/SharpCompress/Common/Zip/ZipEntry.cs b/src/SharpCompress/Common/Zip/ZipEntry.cs index 8c193f294..47df7d9e4 100644 --- a/src/SharpCompress/Common/Zip/ZipEntry.cs +++ b/src/SharpCompress/Common/Zip/ZipEntry.cs @@ -10,6 +10,10 @@ public class ZipEntry : Entry { private readonly ZipFilePart? _filePart; + // WinZip AES extra data constants + private const int MinimumWinZipAesExtraDataLength = 7; + private const int WinZipAesCompressionMethodOffset = 5; + internal ZipEntry(ZipFilePart? filePart, IReaderOptions readerOptions) : base(readerOptions) { @@ -33,24 +37,54 @@ internal ZipEntry(ZipFilePart? filePart, IReaderOptions readerOptions) CreatedTime = times?.UnicodeTimes.Item3; } - public override CompressionType CompressionType => - _filePart?.Header.CompressionMethod switch + public override CompressionType CompressionType + { + get + { + var compressionMethod = GetActualCompressionMethod(); + return compressionMethod switch + { + ZipCompressionMethod.BZip2 => CompressionType.BZip2, + ZipCompressionMethod.Deflate => CompressionType.Deflate, + ZipCompressionMethod.Deflate64 => CompressionType.Deflate64, + ZipCompressionMethod.LZMA => CompressionType.LZMA, + ZipCompressionMethod.PPMd => CompressionType.PPMd, + ZipCompressionMethod.None => CompressionType.None, + ZipCompressionMethod.Shrink => CompressionType.Shrink, + ZipCompressionMethod.Reduce1 => CompressionType.Reduce1, + ZipCompressionMethod.Reduce2 => CompressionType.Reduce2, + ZipCompressionMethod.Reduce3 => CompressionType.Reduce3, + ZipCompressionMethod.Reduce4 => CompressionType.Reduce4, + ZipCompressionMethod.Explode => CompressionType.Explode, + ZipCompressionMethod.ZStandard => CompressionType.ZStandard, + _ => CompressionType.Unknown, + }; + } + } + + private ZipCompressionMethod GetActualCompressionMethod() + { + if (_filePart?.Header.CompressionMethod != ZipCompressionMethod.WinzipAes) { - ZipCompressionMethod.BZip2 => CompressionType.BZip2, - ZipCompressionMethod.Deflate => CompressionType.Deflate, - ZipCompressionMethod.Deflate64 => CompressionType.Deflate64, - ZipCompressionMethod.LZMA => CompressionType.LZMA, - ZipCompressionMethod.PPMd => CompressionType.PPMd, - ZipCompressionMethod.None => CompressionType.None, - ZipCompressionMethod.Shrink => CompressionType.Shrink, - ZipCompressionMethod.Reduce1 => CompressionType.Reduce1, - ZipCompressionMethod.Reduce2 => CompressionType.Reduce2, - ZipCompressionMethod.Reduce3 => CompressionType.Reduce3, - ZipCompressionMethod.Reduce4 => CompressionType.Reduce4, - ZipCompressionMethod.Explode => CompressionType.Explode, - ZipCompressionMethod.ZStandard => CompressionType.ZStandard, - _ => CompressionType.Unknown, - }; + return _filePart?.Header.CompressionMethod ?? ZipCompressionMethod.None; + } + + // For WinZip AES, the actual compression method is stored in the extra data + var aesExtraData = _filePart.Header.Extra.FirstOrDefault(x => + x.Type == ExtraDataType.WinZipAes + ); + + if (aesExtraData is null || aesExtraData.DataBytes.Length < MinimumWinZipAesExtraDataLength) + { + return ZipCompressionMethod.WinzipAes; + } + + // The compression method is at offset 5 in the extra data + return (ZipCompressionMethod) + System.Buffers.Binary.BinaryPrimitives.ReadUInt16LittleEndian( + aesExtraData.DataBytes.AsSpan(WinZipAesCompressionMethodOffset) + ); + } public override long Crc => _filePart?.Header.Crc ?? 0; diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs index 9040bf145..f166187ed 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs @@ -491,6 +491,56 @@ public void Zip_Deflate_WinzipAES_MultiOpenEntryStream() } } + [Fact] + public void Zip_WinzipAES_CompressionType() + { + // Test that WinZip AES encrypted entries correctly report their compression type + using var deflateArchive = ZipArchive.OpenArchive( + Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.WinzipAES.zip"), + new ReaderOptions { Password = "test" } + ); + foreach (var entry in deflateArchive.Entries.Where(x => !x.IsDirectory)) + { + Assert.True(entry.IsEncrypted); + Assert.Equal(CompressionType.Deflate, entry.CompressionType); + } + + using var lzmaArchive = ZipArchive.OpenArchive( + Path.Combine(TEST_ARCHIVES_PATH, "Zip.lzma.WinzipAES.zip"), + new ReaderOptions { Password = "test" } + ); + foreach (var entry in lzmaArchive.Entries.Where(x => !x.IsDirectory)) + { + Assert.True(entry.IsEncrypted); + Assert.Equal(CompressionType.LZMA, entry.CompressionType); + } + } + + [Fact] + public void Zip_Pkware_CompressionType() + { + // Test that Pkware encrypted entries correctly report their compression type + using var deflateArchive = ZipArchive.OpenArchive( + Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.pkware.zip"), + new ReaderOptions { Password = "test" } + ); + foreach (var entry in deflateArchive.Entries.Where(x => !x.IsDirectory)) + { + Assert.True(entry.IsEncrypted); + Assert.Equal(CompressionType.Deflate, entry.CompressionType); + } + + using var bzip2Archive = ZipArchive.OpenArchive( + Path.Combine(TEST_ARCHIVES_PATH, "Zip.bzip2.pkware.zip"), + new ReaderOptions { Password = "test" } + ); + foreach (var entry in bzip2Archive.Entries.Where(x => !x.IsDirectory)) + { + Assert.True(entry.IsEncrypted); + Assert.Equal(CompressionType.BZip2, entry.CompressionType); + } + } + [Fact] public void Zip_Read_Volume_Comment() { diff --git a/tests/SharpCompress.Test/Zip/ZipReaderAsyncTests.cs b/tests/SharpCompress.Test/Zip/ZipReaderAsyncTests.cs index fabbce67f..a9525ede2 100644 --- a/tests/SharpCompress.Test/Zip/ZipReaderAsyncTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipReaderAsyncTests.cs @@ -224,7 +224,7 @@ await Assert.ThrowsAsync(async () => { if (!reader.Entry.IsDirectory) { - Assert.Equal(CompressionType.Unknown, reader.Entry.CompressionType); + Assert.Equal(CompressionType.LZMA, reader.Entry.CompressionType); await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH); } } @@ -252,7 +252,7 @@ public async ValueTask Zip_Deflate_WinzipAES_Read_Async() { if (!reader.Entry.IsDirectory) { - Assert.Equal(CompressionType.Unknown, reader.Entry.CompressionType); + Assert.Equal(CompressionType.Deflate, reader.Entry.CompressionType); await reader.WriteEntryToDirectoryAsync(SCRATCH_FILES_PATH); } } diff --git a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs index e4e6ee7ae..5ca55b7b4 100644 --- a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs @@ -201,7 +201,7 @@ public void Zip_LZMA_WinzipAES_Read() => { if (!reader.Entry.IsDirectory) { - Assert.Equal(CompressionType.Unknown, reader.Entry.CompressionType); + Assert.Equal(CompressionType.LZMA, reader.Entry.CompressionType); reader.WriteEntryToDirectory(SCRATCH_FILES_PATH); } } @@ -223,7 +223,7 @@ public void Zip_Deflate_WinzipAES_Read() { if (!reader.Entry.IsDirectory) { - Assert.Equal(CompressionType.Unknown, reader.Entry.CompressionType); + Assert.Equal(CompressionType.Deflate, reader.Entry.CompressionType); reader.WriteEntryToDirectory(SCRATCH_FILES_PATH); } }