Skip to content

Commit 8f3fa5c

Browse files
authored
Merge PR #440: Use CompressionMethodForHeader for header entries
* Extra unit tests for adding/removing files from AES encrypted zip archives * Use ZipEntry.CompressionMethodForHeader when writing the header entries for files.
1 parent a1e5280 commit 8f3fa5c

File tree

2 files changed

+227
-35
lines changed

2 files changed

+227
-35
lines changed

src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2104,7 +2104,7 @@ private void WriteLocalEntryHeader(ZipUpdate update)
21042104
WriteLEShort(entry.Version);
21052105
WriteLEShort(entry.Flags);
21062106

2107-
WriteLEShort((byte)entry.CompressionMethod);
2107+
WriteLEShort((byte)entry.CompressionMethodForHeader);
21082108
WriteLEInt((int)entry.DosTime);
21092109

21102110
if (!entry.HasCrc)
@@ -2214,7 +2214,7 @@ private int WriteCentralDirectoryHeader(ZipEntry entry)
22142214

22152215
unchecked
22162216
{
2217-
WriteLEShort((byte)entry.CompressionMethod);
2217+
WriteLEShort((byte)entry.CompressionMethodForHeader);
22182218
WriteLEInt((int)entry.DosTime);
22192219
WriteLEInt((int)entry.Crc);
22202220
}

test/ICSharpCode.SharpZipLib.Tests/Zip/ZipEncryptionHandling.cs

+225-33
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,167 @@ public void ZipFileStoreAesPartialRead()
204204
}
205205
}
206206

207+
/// <summary>
208+
/// Test adding files to an encrypted zip
209+
/// </summary>
210+
[Test]
211+
[Category("Encryption")]
212+
[Category("Zip")]
213+
public void ZipFileAesAdd()
214+
{
215+
string password = "password";
216+
string testData = "AdditionalData";
217+
int keySize = 256;
218+
219+
using (var memoryStream = new MemoryStream())
220+
{
221+
// Try to create a zip stream
222+
WriteEncryptedZipToStream(memoryStream, password, keySize, CompressionMethod.Deflated);
223+
224+
// reset
225+
memoryStream.Seek(0, SeekOrigin.Begin);
226+
227+
// Update the archive with ZipFile
228+
{
229+
using (var zipFile = new ZipFile(memoryStream, leaveOpen: true) { Password = password })
230+
{
231+
zipFile.BeginUpdate();
232+
zipFile.Add(new StringMemoryDataSource(testData), "AdditionalEntry", CompressionMethod.Deflated);
233+
zipFile.CommitUpdate();
234+
}
235+
}
236+
237+
// Test the updated archive
238+
{
239+
memoryStream.Seek(0, SeekOrigin.Begin);
240+
241+
using (var zipFile = new ZipFile(memoryStream, leaveOpen: true) { Password = password })
242+
{
243+
Assert.That(zipFile.Count, Is.EqualTo(2), "Incorrect entry count in updated archive");
244+
245+
// Disabled because of bug #317
246+
// Assert.That(zipFile.TestArchive(true), Is.True);
247+
248+
// Check the original entry
249+
{
250+
var originalEntry = zipFile.GetEntry("test");
251+
Assert.That(originalEntry.IsCrypted, Is.True);
252+
Assert.That(originalEntry.AESKeySize, Is.EqualTo(keySize));
253+
254+
255+
using (var zis = zipFile.GetInputStream(originalEntry))
256+
using (var sr = new StreamReader(zis, Encoding.UTF8))
257+
{
258+
var content = sr.ReadToEnd();
259+
Assert.That(content, Is.EqualTo(DummyDataString), "Decompressed content does not match input data");
260+
}
261+
}
262+
263+
// Check the additional entry
264+
// This should be encrypted, though currently only with ZipCrypto
265+
{
266+
var additionalEntry = zipFile.GetEntry("AdditionalEntry");
267+
Assert.That(additionalEntry.IsCrypted, Is.True);
268+
269+
using (var zis = zipFile.GetInputStream(additionalEntry))
270+
using (var sr = new StreamReader(zis, Encoding.UTF8))
271+
{
272+
var content = sr.ReadToEnd();
273+
Assert.That(content, Is.EqualTo(testData), "Decompressed content does not match input data");
274+
}
275+
}
276+
}
277+
}
278+
279+
// As an extra test, verify the file with 7-zip
280+
VerifyZipWith7Zip(memoryStream, password);
281+
}
282+
}
283+
284+
/// <summary>
285+
/// Test deleting files from an encrypted zip
286+
/// </summary>
287+
[Test]
288+
[Category("Encryption")]
289+
[Category("Zip")]
290+
public void ZipFileAesDelete()
291+
{
292+
string password = "password";
293+
int keySize = 256;
294+
295+
using (var memoryStream = new MemoryStream())
296+
{
297+
// Try to create a zip stream
298+
WriteEncryptedZipToStream(memoryStream, 3, password, keySize, CompressionMethod.Deflated);
299+
300+
// reset
301+
memoryStream.Seek(0, SeekOrigin.Begin);
302+
303+
// delete one of the entries from the file
304+
{
305+
using (var zipFile = new ZipFile(memoryStream, leaveOpen: true) { Password = password })
306+
{
307+
// Must have 3 entries to start with
308+
Assert.That(zipFile.Count, Is.EqualTo(3), "Must have 3 entries to start with");
309+
310+
var entryToDelete = zipFile.GetEntry("test-1");
311+
Assert.That(entryToDelete, Is.Not.Null, "the entry that we want to delete must exist");
312+
313+
zipFile.BeginUpdate();
314+
zipFile.Delete(entryToDelete);
315+
zipFile.CommitUpdate();
316+
}
317+
}
318+
319+
// Test the updated archive
320+
{
321+
memoryStream.Seek(0, SeekOrigin.Begin);
322+
323+
using (var zipFile = new ZipFile(memoryStream, leaveOpen: true) { Password = password })
324+
{
325+
// We should now only have 2 files
326+
Assert.That(zipFile.Count, Is.EqualTo(2), "Incorrect entry count in updated archive");
327+
328+
// Disabled because of bug #317
329+
// Assert.That(zipFile.TestArchive(true), Is.True);
330+
331+
// Check the first entry
332+
{
333+
var originalEntry = zipFile.GetEntry("test-0");
334+
Assert.That(originalEntry.IsCrypted, Is.True);
335+
Assert.That(originalEntry.AESKeySize, Is.EqualTo(keySize));
336+
337+
338+
using (var zis = zipFile.GetInputStream(originalEntry))
339+
using (var sr = new StreamReader(zis, Encoding.UTF8))
340+
{
341+
var content = sr.ReadToEnd();
342+
Assert.That(content, Is.EqualTo(DummyDataString), "Decompressed content does not match input data");
343+
}
344+
}
345+
346+
// Check the second entry
347+
{
348+
var originalEntry = zipFile.GetEntry("test-2");
349+
Assert.That(originalEntry.IsCrypted, Is.True);
350+
Assert.That(originalEntry.AESKeySize, Is.EqualTo(keySize));
351+
352+
353+
using (var zis = zipFile.GetInputStream(originalEntry))
354+
using (var sr = new StreamReader(zis, Encoding.UTF8))
355+
{
356+
var content = sr.ReadToEnd();
357+
Assert.That(content, Is.EqualTo(DummyDataString), "Decompressed content does not match input data");
358+
}
359+
}
360+
}
361+
}
362+
363+
// As an extra test, verify the file with 7-zip
364+
VerifyZipWith7Zip(memoryStream, password);
365+
}
366+
}
367+
207368
private static readonly string[] possible7zPaths = new[] {
208369
// Check in PATH
209370
"7z", "7za",
@@ -259,65 +420,96 @@ public void WriteEncryptedZipToStream(Stream stream, string password, int keySiz
259420
zs.SetLevel(9); // 0-9, 9 being the highest level of compression
260421
zs.Password = password; // optional. Null is the same as not setting. Required if using AES.
261422

262-
ZipEntry zipEntry = new ZipEntry("test");
263-
zipEntry.AESKeySize = keySize;
264-
zipEntry.DateTime = DateTime.Now;
265-
zipEntry.CompressionMethod = compressionMethod;
266-
267-
zs.PutNextEntry(zipEntry);
423+
AddEncrypedEntryToStream(zs, $"test", keySize, compressionMethod);
424+
}
425+
}
268426

269-
byte[] dummyData = Encoding.UTF8.GetBytes(DummyDataString);
427+
public void WriteEncryptedZipToStream(Stream stream, int entryCount, string password, int keySize, CompressionMethod compressionMethod)
428+
{
429+
using (var zs = new ZipOutputStream(stream))
430+
{
431+
zs.IsStreamOwner = false;
432+
zs.SetLevel(9); // 0-9, 9 being the highest level of compression
433+
zs.Password = password; // optional. Null is the same as not setting. Required if using AES.
270434

271-
using (var dummyStream = new MemoryStream(dummyData))
435+
for (int i = 0; i < entryCount; i++)
272436
{
273-
dummyStream.CopyTo(zs);
437+
AddEncrypedEntryToStream(zs, $"test-{i}", keySize, compressionMethod);
274438
}
439+
}
440+
}
441+
442+
private void AddEncrypedEntryToStream(ZipOutputStream zipOutputStream, string entryName, int keySize, CompressionMethod compressionMethod)
443+
{
444+
ZipEntry zipEntry = new ZipEntry(entryName)
445+
{
446+
AESKeySize = keySize,
447+
DateTime = DateTime.Now,
448+
CompressionMethod = compressionMethod
449+
};
450+
451+
zipOutputStream.PutNextEntry(zipEntry);
275452

276-
zs.CloseEntry();
453+
byte[] dummyData = Encoding.UTF8.GetBytes(DummyDataString);
454+
455+
using (var dummyStream = new MemoryStream(dummyData))
456+
{
457+
dummyStream.CopyTo(zipOutputStream);
277458
}
459+
460+
zipOutputStream.CloseEntry();
278461
}
279462

280463
public void CreateZipWithEncryptedEntries(string password, int keySize, CompressionMethod compressionMethod = CompressionMethod.Deflated)
281464
{
282465
using (var ms = new MemoryStream())
283466
{
284467
WriteEncryptedZipToStream(ms, password, keySize, compressionMethod);
468+
VerifyZipWith7Zip(ms, password);
469+
}
470+
}
285471

286-
if (TryGet7zBinPath(out string path7z))
287-
{
288-
Console.WriteLine($"Using 7z path: \"{path7z}\"");
289-
290-
ms.Seek(0, SeekOrigin.Begin);
472+
/// <summary>
473+
/// Helper function to verify the provided zip stream with 7Zip.
474+
/// </summary>
475+
/// <param name="zipStream">A stream containing the zip archive to test.</param>
476+
/// <param name="password">The password for the archive.</param>
477+
private void VerifyZipWith7Zip(Stream zipStream, string password)
478+
{
479+
if (TryGet7zBinPath(out string path7z))
480+
{
481+
Console.WriteLine($"Using 7z path: \"{path7z}\"");
291482

292-
var fileName = Path.GetTempFileName();
483+
var fileName = Path.GetTempFileName();
293484

294-
try
485+
try
486+
{
487+
using (var fs = File.OpenWrite(fileName))
295488
{
296-
using (var fs = File.OpenWrite(fileName))
297-
{
298-
ms.CopyTo(fs);
299-
}
300-
301-
var p = Process.Start(path7z, $"t -p{password} \"{fileName}\"");
302-
if (!p.WaitForExit(2000))
303-
{
304-
Assert.Warn("Timed out verifying zip file!");
305-
}
306-
307-
Assert.AreEqual(0, p.ExitCode, "Archive verification failed");
489+
zipStream.Seek(0, SeekOrigin.Begin);
490+
zipStream.CopyTo(fs);
308491
}
309-
finally
492+
493+
var p = Process.Start(path7z, $"t -p{password} \"{fileName}\"");
494+
if (!p.WaitForExit(2000))
310495
{
311-
File.Delete(fileName);
496+
Assert.Warn("Timed out verifying zip file!");
312497
}
498+
499+
Assert.AreEqual(0, p.ExitCode, "Archive verification failed");
313500
}
314-
else
501+
finally
315502
{
316-
Assert.Warn("Skipping file verification since 7za is not in path");
503+
File.Delete(fileName);
317504
}
318505
}
506+
else
507+
{
508+
Assert.Warn("Skipping file verification since 7za is not in path");
509+
}
319510
}
320511

512+
321513
private const string DummyDataString = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit.
322514
Fusce bibendum diam ac nunc rutrum ornare. Maecenas blandit elit ligula, eget suscipit lectus rutrum eu.
323515
Maecenas aliquam, purus mattis pulvinar pharetra, nunc orci maximus justo, sed facilisis massa dui sed lorem.

0 commit comments

Comments
 (0)