From 89e7bdd9fe9ee7e2edebd7434de944277959571f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= Date: Sat, 6 Feb 2021 08:08:06 +0100 Subject: [PATCH] PR #554: Skip CRC calculation for AES zip entries --- src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs | 17 +++++++-- .../Zip/ZipHelperStream.cs | 4 +- .../Zip/ZipOutputStream.cs | 38 ++++++++++++------- .../Zip/FastZipHandling.cs | 7 +++- 4 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs index 62015a932..6daeb7521 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs @@ -960,6 +960,9 @@ public bool TestArchive(bool testData, TestStrategy strategy, ZipTestResultHandl if (testing && testData && this[entryIndex].IsFile) { + // Don't check CRC for AES encrypted archives + var checkCRC = this[entryIndex].AESKeySize == 0; + if (resultHandler != null) { status.SetOperation(TestOperation.EntryData); @@ -975,7 +978,10 @@ public bool TestArchive(bool testData, TestStrategy strategy, ZipTestResultHandl int bytesRead; while ((bytesRead = entryStream.Read(buffer, 0, buffer.Length)) > 0) { - crc.Update(new ArraySegment(buffer, 0, bytesRead)); + if (checkCRC) + { + crc.Update(new ArraySegment(buffer, 0, bytesRead)); + } if (resultHandler != null) { @@ -986,7 +992,7 @@ public bool TestArchive(bool testData, TestStrategy strategy, ZipTestResultHandl } } - if (this[entryIndex].Crc != crc.Value) + if (checkCRC && this[entryIndex].Crc != crc.Value) { status.AddError(); @@ -1000,7 +1006,8 @@ public bool TestArchive(bool testData, TestStrategy strategy, ZipTestResultHandl var helper = new ZipHelperStream(baseStream_); var data = new DescriptorData(); helper.ReadDataDescriptor(this[entryIndex].LocalHeaderRequiresZip64, data); - if (this[entryIndex].Crc != data.Crc) + + if (checkCRC && this[entryIndex].Crc != data.Crc) { status.AddError(); resultHandler?.Invoke(status, "Descriptor CRC mismatch"); @@ -2687,6 +2694,8 @@ private void AddEntry(ZipFile workFile, ZipUpdate update) } } + var useCrc = update.Entry.AESKeySize == 0; + if (source != null) { using (source) @@ -2711,7 +2720,7 @@ private void AddEntry(ZipFile workFile, ZipUpdate update) using (Stream output = workFile.GetOutputStream(update.OutEntry)) { - CopyBytes(update, output, source, sourceStreamLength, true); + CopyBytes(update, output, source, sourceStreamLength, useCrc); } long dataEnd = workFile.baseStream_.Position; diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs index dd7d25d94..da65630c6 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs @@ -565,7 +565,7 @@ public int WriteDataDescriptor(ZipEntry entry) if ((entry.Flags & (int)GeneralBitFlags.Descriptor) != 0) { // The signature is not PKZIP originally but is now described as optional - // in the PKZIP Appnote documenting trhe format. + // in the PKZIP Appnote documenting the format. WriteLEInt(ZipConstants.DataDescriptorSignature); WriteLEInt(unchecked((int)(entry.Crc))); @@ -599,7 +599,7 @@ public void ReadDataDescriptor(bool zip64, DescriptorData data) int intValue = ReadLEInt(); // In theory this may not be a descriptor according to PKZIP appnote. - // In practise its always there. + // In practice its always there. if (intValue != ZipConstants.DataDescriptorSignature) { throw new ZipException("Data descriptor signature not found"); diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs index b9131d040..3c49ec8cb 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs @@ -500,6 +500,9 @@ public void PutNextEntry(ZipEntry entry) /// /// Closes the current entry, updating header and footer information as required /// + /// + /// Invalid entry field values. + /// /// /// An I/O error occurs. /// @@ -530,7 +533,7 @@ public void CloseEntry() } else if (curMethod == CompressionMethod.Stored) { - // This is done by Finsh() for Deflated entries, but we need to do it + // This is done by Finish() for Deflated entries, but we need to do it // ourselves for Stored ones base.GetAuthCodeIfAES(); } @@ -539,6 +542,19 @@ public void CloseEntry() if (curEntry.AESKeySize > 0) { baseOutputStream_.Write(AESAuthCode, 0, 10); + // Always use 0 as CRC for AE-2 format + curEntry.Crc = 0; + } + else + { + if (curEntry.Crc < 0) + { + curEntry.Crc = crc.Value; + } + else if (curEntry.Crc != crc.Value) + { + throw new ZipException($"crc was {crc.Value}, but {curEntry.Crc} was expected"); + } } if (curEntry.Size < 0) @@ -547,7 +563,7 @@ public void CloseEntry() } else if (curEntry.Size != size) { - throw new ZipException("size was " + size + ", but I expected " + curEntry.Size); + throw new ZipException($"size was {size}, but {curEntry.Size} was expected"); } if (curEntry.CompressedSize < 0) @@ -556,16 +572,7 @@ public void CloseEntry() } else if (curEntry.CompressedSize != csize) { - throw new ZipException("compressed size was " + csize + ", but I expected " + curEntry.CompressedSize); - } - - if (curEntry.Crc < 0) - { - curEntry.Crc = crc.Value; - } - else if (curEntry.Crc != crc.Value) - { - throw new ZipException("crc was " + crc.Value + ", but I expected " + curEntry.Crc); + throw new ZipException($"compressed size was {csize}, but {curEntry.CompressedSize} expected"); } offset += csize; @@ -718,7 +725,12 @@ public override void Write(byte[] buffer, int offset, int count) throw new ArgumentException("Invalid offset/count combination"); } - crc.Update(new ArraySegment(buffer, offset, count)); + if (curEntry.AESKeySize == 0) + { + // Only update CRC if AES is not enabled + crc.Update(new ArraySegment(buffer, offset, count)); + } + size += count; switch (curMethod) diff --git a/test/ICSharpCode.SharpZipLib.Tests/Zip/FastZipHandling.cs b/test/ICSharpCode.SharpZipLib.Tests/Zip/FastZipHandling.cs index 753fc8623..8be25a4dc 100644 --- a/test/ICSharpCode.SharpZipLib.Tests/Zip/FastZipHandling.cs +++ b/test/ICSharpCode.SharpZipLib.Tests/Zip/FastZipHandling.cs @@ -193,7 +193,12 @@ public void Encryption(ZipEncryptionMethod encryptionMethod) ZipEntry entry = zf[0]; Assert.AreEqual(tempName1, entry.Name); Assert.AreEqual(1, entry.Size); - Assert.IsTrue(zf.TestArchive(true)); + Assert.IsTrue(zf.TestArchive(true, TestStrategy.FindFirstError, (status, message) => + { + if(!string.IsNullOrEmpty(message)) { + Console.WriteLine($"{message} ({status.Entry?.Name ?? "-"})"); + } + })); Assert.IsTrue(entry.IsCrypted); switch (encryptionMethod)