From 506e3af52c96410dc2354c73d93845e1d7f6e0b7 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Thu, 5 May 2022 04:25:56 -0700 Subject: [PATCH 1/7] Add ZipArchiveEntry.IsEncrypted for password-protection detection --- .../ref/System.IO.Compression.cs | 1 + .../System/IO/Compression/ZipArchiveEntry.cs | 9 +++- .../tests/ZipArchive/zip_ReadTests.cs | 42 +++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs b/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs index 3090d4aaba384c..0193cc7603df39 100644 --- a/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs +++ b/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs @@ -110,6 +110,7 @@ internal ZipArchiveEntry() { } public System.IO.Compression.ZipArchive Archive { get { throw null; } } [System.Diagnostics.CodeAnalysis.AllowNull] public string Comment { get { throw null; } set { } } + public bool IsEncrypted { get { throw null; } } public long CompressedLength { get { throw null; } } [System.CLSCompliantAttribute(false)] public uint Crc32 { get { throw null; } } diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs index 0e1fb42209594e..6f80eebbb67b20 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs @@ -21,6 +21,7 @@ public partial class ZipArchiveEntry private ZipVersionNeededValues _versionMadeBySpecification; internal ZipVersionNeededValues _versionToExtract; private BitFlagValues _generalPurposeBitFlag; + private bool _isEncrypted; private CompressionMethodValues _storedCompressionMethod; private DateTimeOffset _lastModified; private long _compressedSize; @@ -55,6 +56,7 @@ internal ZipArchiveEntry(ZipArchive archive, ZipCentralDirectoryFileHeader cd) _versionMadeBySpecification = (ZipVersionNeededValues)cd.VersionMadeBySpecification; _versionToExtract = (ZipVersionNeededValues)cd.VersionNeededToExtract; _generalPurposeBitFlag = (BitFlagValues)cd.GeneralPurposeBitFlag; + _isEncrypted = _generalPurposeBitFlag.HasFlag(BitFlagValues.IsEncrypted); CompressionMethod = (CompressionMethodValues)cd.CompressionMethod; _lastModified = new DateTimeOffset(ZipHelper.DosTimeToDateTime(cd.LastModified)); _compressedSize = cd.CompressedSize; @@ -151,6 +153,11 @@ internal ZipArchiveEntry(ZipArchive archive, string entryName) [CLSCompliant(false)] public uint Crc32 => _crc32; + /// + /// Gets whether or not this archive entry is encrypted. + /// + public bool IsEncrypted { get => _isEncrypted; } + /// /// The compressed size of the entry. If the archive that the entry belongs to is in Create mode, attempts to get this property will always throw an exception. If the archive that the entry belongs to is in update mode, this property will only be valid if the entry has not been opened. /// @@ -1306,7 +1313,7 @@ protected override void Dispose(bool disposing) } [Flags] - internal enum BitFlagValues : ushort { DataDescriptor = 0x8, UnicodeFileNameAndComment = 0x800 } + internal enum BitFlagValues : ushort { IsEncrypted = 0x1, DataDescriptor = 0x8, UnicodeFileNameAndComment = 0x800 } internal enum CompressionMethodValues : ushort { Stored = 0x0, Deflate = 0x8, Deflate64 = 0x9, BZip2 = 0xC, LZMA = 0xE } } diff --git a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs index 577c60ad087e9a..93418b7af2ac40 100644 --- a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs +++ b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Threading.Tasks; using Xunit; @@ -207,5 +208,46 @@ public static void TestEmptyLastModifiedEntryValueNotThrowingInternalException() using var archive = new ZipArchive(memoryStream, ZipArchiveMode.Read, true); Assert.Equal(archive.Entries[0].LastWriteTime, emptyDateIndicator); } + + [Theory] + [InlineData("normal.zip")] + [InlineData("small.zip")] + public static async Task EntriesNotEncryptedByDefault(string zipFile) + { + using (ZipArchive archive = new ZipArchive(await StreamHelpers.CreateTempCopyStream(zfile(zipFile)), ZipArchiveMode.Read)) + { + foreach (ZipArchiveEntry entry in archive.Entries) + { + Assert.False(entry.IsEncrypted); + } + } + } + + [Theory] + [InlineData("encrypted_entries_weak.zip")] + [InlineData("encrypted_entries_aes256.zip")] + [InlineData("encrypted_entries_mixed.zip")] + public static async Task IdentifyEncryptedEntries(string zipFile) + { + var entriesEncrypted = new Dictionary(); + + using (ZipArchive archive = new ZipArchive(await StreamHelpers.CreateTempCopyStream(zfile(zipFile)), ZipArchiveMode.Read)) + { + foreach (ZipArchiveEntry entry in archive.Entries) + { + entriesEncrypted.Add(entry.Name, entry.IsEncrypted); + } + } + + var expectedEntries = new Dictionary() + { + { "file1-encrypted.txt", true }, + { "file2-unencrypted.txt", false }, + { "file3-encrypted.txt", true }, + { "file4-unencrypted.txt", false }, + }; + + Assert.Equal(expectedEntries, entriesEncrypted); + } } } From 6ddca7741dbcbcb71fc299d4efb2a111a9f25658 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Tue, 31 May 2022 20:57:26 -0700 Subject: [PATCH 2/7] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David CantĂș --- .../src/System/IO/Compression/ZipArchiveEntry.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs index 6f80eebbb67b20..3227a032bb3138 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs @@ -56,7 +56,7 @@ internal ZipArchiveEntry(ZipArchive archive, ZipCentralDirectoryFileHeader cd) _versionMadeBySpecification = (ZipVersionNeededValues)cd.VersionMadeBySpecification; _versionToExtract = (ZipVersionNeededValues)cd.VersionNeededToExtract; _generalPurposeBitFlag = (BitFlagValues)cd.GeneralPurposeBitFlag; - _isEncrypted = _generalPurposeBitFlag.HasFlag(BitFlagValues.IsEncrypted); + _isEncrypted = (_generalPurposeBitFlag & BitFlagValues.IsEncrypted) != 0; CompressionMethod = (CompressionMethodValues)cd.CompressionMethod; _lastModified = new DateTimeOffset(ZipHelper.DosTimeToDateTime(cd.LastModified)); _compressedSize = cd.CompressedSize; @@ -154,9 +154,9 @@ internal ZipArchiveEntry(ZipArchive archive, string entryName) public uint Crc32 => _crc32; /// - /// Gets whether or not this archive entry is encrypted. + /// Gets a value that indicates whether the entry is encrypted. /// - public bool IsEncrypted { get => _isEncrypted; } + public bool IsEncrypted => _isEncrypted; /// /// The compressed size of the entry. If the archive that the entry belongs to is in Create mode, attempts to get this property will always throw an exception. If the archive that the entry belongs to is in update mode, this property will only be valid if the entry has not been opened. From 010d26640e4a513430bbab26ae496fa7e6cb77e9 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Tue, 31 May 2022 21:05:52 -0700 Subject: [PATCH 3/7] Run GenerateReferenceAssemblySource for System.IO.Compression --- .../ref/System.IO.Compression.cs | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs b/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs index 0193cc7603df39..b7e53d31b5abb1 100644 --- a/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs +++ b/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs @@ -49,9 +49,9 @@ public override void Flush() { } public override void SetLength(long value) { } public override void Write(byte[] buffer, int offset, int count) { } public override void Write(System.ReadOnlySpan buffer) { } - public override void WriteByte(byte value) { } public override System.Threading.Tasks.Task WriteAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { throw null; } - public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken) { throw null; } + public override void WriteByte(byte value) { } } public partial class GZipStream : System.IO.Stream { @@ -84,9 +84,9 @@ public override void Flush() { } public override void SetLength(long value) { } public override void Write(byte[] buffer, int offset, int count) { } public override void Write(System.ReadOnlySpan buffer) { } - public override void WriteByte(byte value) { } public override System.Threading.Tasks.Task WriteAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { throw null; } public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public override void WriteByte(byte value) { } } public partial class ZipArchive : System.IDisposable { @@ -94,7 +94,7 @@ public ZipArchive(System.IO.Stream stream) { } public ZipArchive(System.IO.Stream stream, System.IO.Compression.ZipArchiveMode mode) { } public ZipArchive(System.IO.Stream stream, System.IO.Compression.ZipArchiveMode mode, bool leaveOpen) { } public ZipArchive(System.IO.Stream stream, System.IO.Compression.ZipArchiveMode mode, bool leaveOpen, System.Text.Encoding? entryNameEncoding) { } - [System.Diagnostics.CodeAnalysis.AllowNull] + [System.Diagnostics.CodeAnalysis.AllowNullAttribute] public string Comment { get { throw null; } set { } } public System.Collections.ObjectModel.ReadOnlyCollection Entries { get { throw null; } } public System.IO.Compression.ZipArchiveMode Mode { get { throw null; } } @@ -108,14 +108,14 @@ public partial class ZipArchiveEntry { internal ZipArchiveEntry() { } public System.IO.Compression.ZipArchive Archive { get { throw null; } } - [System.Diagnostics.CodeAnalysis.AllowNull] + [System.Diagnostics.CodeAnalysis.AllowNullAttribute] public string Comment { get { throw null; } set { } } - public bool IsEncrypted { get { throw null; } } public long CompressedLength { get { throw null; } } [System.CLSCompliantAttribute(false)] public uint Crc32 { get { throw null; } } public int ExternalAttributes { get { throw null; } set { } } public string FullName { get { throw null; } } + public bool IsEncrypted { get { throw null; } } public System.DateTimeOffset LastWriteTime { get { throw null; } set { } } public long Length { get { throw null; } } public string Name { get { throw null; } } @@ -129,6 +129,14 @@ public enum ZipArchiveMode Create = 1, Update = 2, } + public partial class ZLibException : System.IO.IOException, System.Runtime.Serialization.ISerializable + { + public ZLibException() { } + protected ZLibException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public ZLibException(string? message, System.Exception? innerException) { } + public ZLibException(string? message, string? zlibErrorContext, int zlibErrorCode, string? zlibErrorMessage) { } + void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) { } + } public sealed partial class ZLibStream : System.IO.Stream { public ZLibStream(System.IO.Stream stream, System.IO.Compression.CompressionLevel compressionLevel) { } @@ -160,8 +168,8 @@ public override void Flush() { } public override void SetLength(long value) { } public override void Write(byte[] buffer, int offset, int count) { } public override void Write(System.ReadOnlySpan buffer) { } - public override void WriteByte(byte value) { } public override System.Threading.Tasks.Task WriteAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { throw null; } public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public override void WriteByte(byte value) { } } } From 8e655927894c0ece284628709fbdc3fa256cb6ce Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Tue, 31 May 2022 21:51:51 -0700 Subject: [PATCH 4/7] Revert WriteAsync default parameter value removal --- .../System.IO.Compression/ref/System.IO.Compression.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs b/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs index b7e53d31b5abb1..ff3622dd35963f 100644 --- a/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs +++ b/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs @@ -50,7 +50,7 @@ public override void SetLength(long value) { } public override void Write(byte[] buffer, int offset, int count) { } public override void Write(System.ReadOnlySpan buffer) { } public override System.Threading.Tasks.Task WriteAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { throw null; } - public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken) { throw null; } + public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public override void WriteByte(byte value) { } } public partial class GZipStream : System.IO.Stream From 9c61384d3ea7212622205f8d03c68467820a9cd8 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Tue, 31 May 2022 21:55:40 -0700 Subject: [PATCH 5/7] Update DeflateStream.WriteAsync to include the default param value --- .../src/System/IO/Compression/DeflateZLib/DeflateStream.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs index 7d8a30937704d3..0c35d7a51397ae 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs @@ -778,7 +778,7 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati return WriteAsyncMemory(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); } - public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken) + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { if (GetType() != typeof(DeflateStream)) { From 58b4351065420b6fdd7946afe9efcbc684896f85 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Thu, 2 Jun 2022 10:09:19 -0700 Subject: [PATCH 6/7] Revert unrelated change from GenerateReferenceAssemblySource --- .../System.IO.Compression/ref/System.IO.Compression.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs b/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs index ff3622dd35963f..b2037fd74e3408 100644 --- a/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs +++ b/src/libraries/System.IO.Compression/ref/System.IO.Compression.cs @@ -129,14 +129,6 @@ public enum ZipArchiveMode Create = 1, Update = 2, } - public partial class ZLibException : System.IO.IOException, System.Runtime.Serialization.ISerializable - { - public ZLibException() { } - protected ZLibException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } - public ZLibException(string? message, System.Exception? innerException) { } - public ZLibException(string? message, string? zlibErrorContext, int zlibErrorCode, string? zlibErrorMessage) { } - void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) { } - } public sealed partial class ZLibStream : System.IO.Stream { public ZLibStream(System.IO.Stream stream, System.IO.Compression.CompressionLevel compressionLevel) { } From 04dbaa3592073c6c9a8cfd8ff408ec05753c3288 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Thu, 2 Jun 2022 10:12:28 -0700 Subject: [PATCH 7/7] Revert unrelated change after syncing with GenerateReferenceAssemblySource --- .../src/System/IO/Compression/DeflateZLib/DeflateStream.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs index 0c35d7a51397ae..7d8a30937704d3 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.cs @@ -778,7 +778,7 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati return WriteAsyncMemory(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); } - public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken) { if (GetType() != typeof(DeflateStream)) {