Skip to content

Commit

Permalink
Add ZipArchiveEntry.IsEncrypted for password-protection detection (#7…
Browse files Browse the repository at this point in the history
…0036)

* Add ZipArchiveEntry.IsEncrypted for password-protection detection

* Apply suggestions from code review

Co-authored-by: David Cantú <[email protected]>

* Run GenerateReferenceAssemblySource for System.IO.Compression

* Revert WriteAsync default parameter value removal

* Update DeflateStream.WriteAsync to include the default param value

* Revert unrelated change from GenerateReferenceAssemblySource

* Revert unrelated change after syncing with GenerateReferenceAssemblySource

Co-authored-by: David Cantú <[email protected]>
  • Loading branch information
jeffhandley and jozkee authored Jun 20, 2022
1 parent 6be8d27 commit 19fcc31
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 6 deletions.
11 changes: 6 additions & 5 deletions src/libraries/System.IO.Compression/ref/System.IO.Compression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte> 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<byte> buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public override void WriteByte(byte value) { }
}
public partial class GZipStream : System.IO.Stream
{
Expand Down Expand Up @@ -84,17 +84,17 @@ 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<byte> 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<byte> buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public override void WriteByte(byte value) { }
}
public partial class ZipArchive : System.IDisposable
{
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<System.IO.Compression.ZipArchiveEntry> Entries { get { throw null; } }
public System.IO.Compression.ZipArchiveMode Mode { get { throw null; } }
Expand All @@ -108,13 +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 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; } }
Expand Down Expand Up @@ -159,8 +160,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<byte> 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<byte> buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public override void WriteByte(byte value) { }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -55,6 +56,7 @@ internal ZipArchiveEntry(ZipArchive archive, ZipCentralDirectoryFileHeader cd)
_versionMadeBySpecification = (ZipVersionNeededValues)cd.VersionMadeBySpecification;
_versionToExtract = (ZipVersionNeededValues)cd.VersionNeededToExtract;
_generalPurposeBitFlag = (BitFlagValues)cd.GeneralPurposeBitFlag;
_isEncrypted = (_generalPurposeBitFlag & BitFlagValues.IsEncrypted) != 0;
CompressionMethod = (CompressionMethodValues)cd.CompressionMethod;
_lastModified = new DateTimeOffset(ZipHelper.DosTimeToDateTime(cd.LastModified));
_compressedSize = cd.CompressedSize;
Expand Down Expand Up @@ -151,6 +153,11 @@ internal ZipArchiveEntry(ZipArchive archive, string entryName)
[CLSCompliant(false)]
public uint Crc32 => _crc32;

/// <summary>
/// Gets a value that indicates whether the entry is encrypted.
/// </summary>
public bool IsEncrypted => _isEncrypted;

/// <summary>
/// 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.
/// </summary>
Expand Down Expand Up @@ -1305,7 +1312,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 }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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<string, bool>();

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<string, bool>()
{
{ "file1-encrypted.txt", true },
{ "file2-unencrypted.txt", false },
{ "file3-encrypted.txt", true },
{ "file4-unencrypted.txt", false },
};

Assert.Equal(expectedEntries, entriesEncrypted);
}
}
}

0 comments on commit 19fcc31

Please sign in to comment.