Skip to content

Commit 9245118

Browse files
author
Remo Gloor
committed
Fix HMAC calculation for Zip AES Encryption
1 parent 5392e57 commit 9245118

File tree

3 files changed

+49
-56
lines changed

3 files changed

+49
-56
lines changed

src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/InflaterInputStream.cs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,11 @@ public void Fill()
127127
toRead -= count;
128128
}
129129

130+
clearTextLength = rawLength;
130131
if (cryptoTransform != null)
131132
{
132-
clearTextLength = cryptoTransform.TransformBlock(rawData, 0, rawLength, clearText, 0);
133-
}
134-
else
135-
{
136-
clearTextLength = rawLength;
133+
var size = CalculateDecryptionSize(rawLength);
134+
cryptoTransform.TransformBlock(rawData, 0, size, clearText, 0);
137135
}
138136

139137
available = clearTextLength;
@@ -290,7 +288,9 @@ public ICryptoTransform CryptoTransform
290288
clearTextLength = rawLength;
291289
if (available > 0)
292290
{
293-
cryptoTransform.TransformBlock(rawData, rawLength - available, available, clearText, rawLength - available);
291+
var size = CalculateDecryptionSize(available);
292+
293+
cryptoTransform.TransformBlock(rawData, rawLength - available, size, clearText, rawLength - available);
294294
}
295295
}
296296
else
@@ -301,6 +301,20 @@ public ICryptoTransform CryptoTransform
301301
}
302302
}
303303

304+
private int CalculateDecryptionSize(int availableBufferSize)
305+
{
306+
int size = DecryptSize ?? availableBufferSize;
307+
size = Math.Min(size, availableBufferSize);
308+
if (DecryptSize.HasValue)
309+
{
310+
DecryptSize -= size;
311+
}
312+
313+
return size;
314+
}
315+
316+
public int? DecryptSize { get; set; }
317+
304318
#region Instance Fields
305319

306320
private int rawLength;

src/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public class ZipInputStream : InflaterInputStream
7575
private long size;
7676
private int flags;
7777
private string password;
78+
private ZipAESTransform cryptoTransform;
7879

7980
#endregion Instance Fields
8081

@@ -147,7 +148,8 @@ private static bool IsEntryCompressionMethodSupported(ZipEntry entry)
147148
var entryCompressionMethod = entry.CompressionMethodForHeader;
148149

149150
return entryCompressionMethod == CompressionMethod.Deflated ||
150-
entryCompressionMethod == CompressionMethod.Stored;
151+
entryCompressionMethod == CompressionMethod.Stored ||
152+
entryCompressionMethod == CompressionMethod.WinZipAES;
151153
}
152154

153155
/// <summary>
@@ -353,17 +355,15 @@ protected override void StopDecrypting()
353355
// Final block done. Check Auth code.
354356
}
355357

356-
/*
357-
byte[] calcAuthCode = (this.cryptoTransform as ZipAESTransform).GetAuthCode();
358+
byte[] calcAuthCode = this.cryptoTransform.GetAuthCode();
358359
for (int i = 0; i < ZipConstants.AESAuthCodeLength; i++)
359360
{
360361
if (calcAuthCode[i] != authBytes[i])
361362
{
362-
// throw new Exception("AES Authentication Code does not match. This is a super-CRC check on the data in the file after compression and encryption. \r\n"
363-
// + "The file may be damaged.");
363+
throw new Exception("AES Authentication Code does not match. This is a super-CRC check on the data in the file after compression and encryption. \r\n"
364+
+ "The file may be damaged or tampered.");
364365
}
365366
}
366-
*/
367367

368368
// Dispose the transform?
369369
}
@@ -611,7 +611,9 @@ private int InitialRead(byte[] destination, int offset, int count)
611611
// The AES data has saltLen+AESPasswordVerifyLength bytes as a header, and AESAuthCodeLength bytes
612612
// as a footer.
613613
csize -= (saltLen + ZipConstants.AESPasswordVerifyLength + ZipConstants.AESAuthCodeLength);
614+
inputBuffer.DecryptSize = (int)csize;
614615
inputBuffer.CryptoTransform = decryptor;
616+
this.cryptoTransform = decryptor;
615617
}
616618
}
617619
else

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

Lines changed: 21 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ namespace ICSharpCode.SharpZipLib.Tests.Zip
1010
[TestFixture]
1111
public class ZipEncryptionHandling
1212
{
13+
static ZipEncryptionHandling()
14+
{
15+
var sb = new StringBuilder();
16+
for (int i = 0; i < 1000; i++)
17+
{
18+
sb.Append(Guid.NewGuid());
19+
}
20+
21+
DummyDataString = sb.ToString();
22+
}
23+
1324
[Test]
1425
[Category("Encryption")]
1526
[Category("Zip")]
@@ -127,20 +138,23 @@ public void ZipInputStreamDecryption(int aesKeySize, CompressionMethod compressi
127138

128139
using (var ms = new MemoryStream())
129140
{
130-
WriteEncryptedZipToStream(ms, password, aesKeySize, compressionMethod);
141+
WriteEncryptedZipToStream(ms, 3, password, aesKeySize, compressionMethod);
131142
ms.Seek(0, SeekOrigin.Begin);
132143

133144
using (var zis = new ZipInputStream(ms))
134145
{
135146
zis.IsStreamOwner = false;
136147
zis.Password = password;
137148

138-
var hmm = zis.GetNextEntry();
139-
140-
using (var sr = new StreamReader(zis, Encoding.UTF8))
149+
for (int i = 0; i < 3; i++)
141150
{
142-
var content = sr.ReadToEnd();
143-
Assert.AreEqual(DummyDataString, content, "Decompressed content does not match input data");
151+
var hmm = zis.GetNextEntry();
152+
153+
using (var sr = new StreamReader(zis, Encoding.UTF8, leaveOpen: true, detectEncodingFromByteOrderMarks: true, bufferSize: 1024))
154+
{
155+
var content = sr.ReadToEnd();
156+
Assert.AreEqual(DummyDataString, content, "Decompressed content does not match input data");
157+
}
144158
}
145159
}
146160
}
@@ -473,40 +487,6 @@ public void ZipFileAESReadWithEmptyPassword()
473487
}
474488
}
475489

476-
/// <summary>
477-
/// ZipInputStream can't decrypt AES encrypted entries, but it should report that to the caller
478-
/// rather than just failing.
479-
/// </summary>
480-
[Test]
481-
[Category("Zip")]
482-
public void ZipinputStreamShouldGracefullyFailWithAESStreams()
483-
{
484-
string password = "password";
485-
486-
using (var memoryStream = new MemoryStream())
487-
{
488-
// Try to create a zip stream
489-
WriteEncryptedZipToStream(memoryStream, password, 256);
490-
491-
// reset
492-
memoryStream.Seek(0, SeekOrigin.Begin);
493-
494-
// Try to read
495-
using (var inputStream = new ZipInputStream(memoryStream))
496-
{
497-
inputStream.Password = password;
498-
var entry = inputStream.GetNextEntry();
499-
Assert.That(entry.AESKeySize, Is.EqualTo(256), "Test entry should be AES256 encrypted.");
500-
501-
// CanDecompressEntry should be false.
502-
Assert.That(inputStream.CanDecompressEntry, Is.False, "CanDecompressEntry should be false for AES encrypted entries");
503-
504-
// Should throw on read.
505-
Assert.Throws<ZipException>(() => inputStream.ReadByte());
506-
}
507-
}
508-
}
509-
510490
public void WriteEncryptedZipToStream(Stream stream, string password, int keySize, CompressionMethod compressionMethod = CompressionMethod.Deflated)
511491
{
512492
using (var zs = new ZipOutputStream(stream))
@@ -564,9 +544,6 @@ public void CreateZipWithEncryptedEntries(string password, int keySize, Compress
564544
}
565545
}
566546

567-
private const string DummyDataString = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit.
568-
Fusce bibendum diam ac nunc rutrum ornare. Maecenas blandit elit ligula, eget suscipit lectus rutrum eu.
569-
Maecenas aliquam, purus mattis pulvinar pharetra, nunc orci maximus justo, sed facilisis massa dui sed lorem.
570-
Vestibulum id iaculis leo. Duis porta ante lorem. Duis condimentum enim nec lorem tristique interdum. Fusce in faucibus libero.";
547+
private static readonly string DummyDataString;
571548
}
572549
}

0 commit comments

Comments
 (0)