Skip to content

Commit 460f067

Browse files
author
Remo Gloor
committed
Add support for AES encrypted zip entries whose size is determined by a data descriptor rather than a local file header.
1 parent fbfcc97 commit 460f067

File tree

4 files changed

+81
-11
lines changed

4 files changed

+81
-11
lines changed

src/ICSharpCode.SharpZipLib/Encryption/ZipAESTransform.cs

+34-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ static class HashAlgorithmName
5151
private byte[] _authCode = null;
5252

5353
private bool _writeMode;
54+
private Action<int> _appendHmac = remaining => { };
5455

5556
/// <summary>
5657
/// Constructor.
@@ -88,6 +89,23 @@ public ZipAESTransform(string key, byte[] saltBytes, int blockSize, bool writeMo
8889
_writeMode = writeMode;
8990
}
9091

92+
/// <summary>
93+
/// Append all of the last transformed input data to the HMAC.
94+
/// </summary>
95+
public void AppendAllPending()
96+
{
97+
_appendHmac(0);
98+
}
99+
100+
/// <summary>
101+
/// Append all except the number of bytes specified by remaining of the last transformed input data to the HMAC.
102+
/// </summary>
103+
/// <param name="remaining">The number of bytes not to be added to the HMAC. The excluded bytes are form the end.</param>
104+
public void AppendFinal(int remaining)
105+
{
106+
_appendHmac(remaining);
107+
}
108+
91109
/// <summary>
92110
/// Implement the ICryptoTransform method.
93111
/// </summary>
@@ -97,8 +115,16 @@ public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, b
97115
// This does not change the inputBuffer. Do this before decryption for read mode.
98116
if (!_writeMode)
99117
{
100-
_hmacsha1.AppendData(inputBuffer, inputOffset, inputCount);
118+
if (!ManualHmac)
119+
{
120+
_hmacsha1.AppendData(inputBuffer, inputOffset, inputCount);
121+
}
122+
else
123+
{
124+
_appendHmac = remaining => _hmacsha1.AppendData(inputBuffer, inputOffset, inputCount - remaining);
125+
}
101126
}
127+
102128
// Encrypt with AES in CTR mode. Regards to Dr Brian Gladman for this.
103129
int ix = 0;
104130
while (ix < inputCount)
@@ -208,6 +234,13 @@ public bool CanReuseTransform
208234
}
209235
}
210236

237+
/// <summary>
238+
/// Gets of sets a value indicating if the HMAC is updates on every read of if updating the HMAC has to be controlled manually
239+
/// Manual control of HMAC is needed in case not all the Transformed data should be automatically added to the HMAC.
240+
/// E.g. because its not know how much data belongs to the current entry before the data is decrypted and analyzed.
241+
/// </summary>
242+
public bool ManualHmac { get; set; }
243+
211244
/// <summary>
212245
/// Cleanup internal state.
213246
/// </summary>

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

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.IO;
33
using System.Security.Cryptography;
4+
using ICSharpCode.SharpZipLib.Encryption;
45

56
namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
67
{
@@ -92,7 +93,15 @@ public byte[] ClearText
9293
public int Available
9394
{
9495
get { return available; }
95-
set { available = value; }
96+
set
97+
{
98+
if (cryptoTransform is ZipAESTransform ct)
99+
{
100+
ct.AppendFinal(value);
101+
}
102+
103+
available = value;
104+
}
96105
}
97106

98107
/// <summary>
@@ -121,6 +130,11 @@ public void SetInflaterInput(Inflater inflater)
121130
/// </summary>
122131
public void Fill()
123132
{
133+
if (cryptoTransform is ZipAESTransform ct)
134+
{
135+
ct.AppendAllPending();
136+
}
137+
124138
rawLength = 0;
125139
int toRead = rawData.Length;
126140

@@ -313,6 +327,11 @@ private int CalculateDecryptionSize(int availableBufferSize)
313327
{
314328
int size = DecryptionLimit ?? availableBufferSize;
315329
size = Math.Min(size, availableBufferSize);
330+
if (size < 0)
331+
{
332+
size = availableBufferSize;
333+
}
334+
316335
if (DecryptionLimit.HasValue)
317336
{
318337
DecryptionLimit -= size;

src/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs

+1
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,7 @@ private int InitialRead(byte[] destination, int offset, int count)
618618
int blockSize = entry.AESKeySize / 8; // bits to bytes
619619

620620
var decryptor = new ZipAESTransform(password, saltBytes, blockSize, false);
621+
decryptor.ManualHmac = csize < 0;
621622
byte[] pwdVerifyCalc = decryptor.PwdVerifier;
622623
if (pwdVerifyCalc[0] != pwdVerifyRead[0] || pwdVerifyCalc[1] != pwdVerifyRead[1])
623624
{

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

+26-9
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ static ZipEncryptionHandling()
1515
var sb = new StringBuilder();
1616
for (int i = 0; i < 1000; i++)
1717
{
18-
sb.Append(Guid.NewGuid());
18+
sb.AppendLine(Guid.NewGuid().ToString());
1919
}
2020

2121
DummyDataString = sb.ToString();
@@ -126,17 +126,23 @@ public void ZipFileAesDecryption()
126126
[Test]
127127
[Category("Encryption")]
128128
[Category("Zip")]
129-
[TestCase(0, CompressionMethod.Deflated)]
130-
[TestCase(0, CompressionMethod.Stored)]
131-
[TestCase(128, CompressionMethod.Deflated)]
132-
[TestCase(128, CompressionMethod.Stored)]
133-
[TestCase(256, CompressionMethod.Deflated)]
134-
[TestCase(256, CompressionMethod.Stored)]
135-
public void ZipInputStreamDecryption(int aesKeySize, CompressionMethod compressionMethod)
129+
[TestCase(0, CompressionMethod.Deflated, false)]
130+
[TestCase(0, CompressionMethod.Stored, false)]
131+
[TestCase(128, CompressionMethod.Deflated, false)]
132+
[TestCase(128, CompressionMethod.Stored, false)]
133+
[TestCase(256, CompressionMethod.Deflated, false)]
134+
[TestCase(256, CompressionMethod.Stored, false)]
135+
[TestCase(0, CompressionMethod.Deflated, true)]
136+
[TestCase(0, CompressionMethod.Stored, true)]
137+
[TestCase(128, CompressionMethod.Deflated, true)]
138+
[TestCase(128, CompressionMethod.Stored, true)]
139+
[TestCase(256, CompressionMethod.Deflated, true)]
140+
[TestCase(256, CompressionMethod.Stored, true)]
141+
public void ZipInputStreamDecryption(int aesKeySize, CompressionMethod compressionMethod, bool forceDataDescriptor)
136142
{
137143
var password = "password";
138144

139-
using (var ms = new MemoryStream())
145+
using (var ms = forceDataDescriptor ? new NonSeekableMemoryStream() : new MemoryStream())
140146
{
141147
WriteEncryptedZipToStream(ms, 3, password, aesKeySize, compressionMethod);
142148
ms.Seek(0, SeekOrigin.Begin);
@@ -544,6 +550,17 @@ public void CreateZipWithEncryptedEntries(string password, int keySize, Compress
544550
}
545551
}
546552

553+
private class NonSeekableMemoryStream : MemoryStream
554+
{
555+
public override bool CanSeek
556+
{
557+
get
558+
{
559+
return false;
560+
}
561+
}
562+
}
563+
547564
private static readonly string DummyDataString;
548565
}
549566
}

0 commit comments

Comments
 (0)