Skip to content

Commit 82855a2

Browse files
Numpsypiksel
authored andcommitted
Merge PR #308: Allow AES zip to better handle reading partial stream data
* Attempt to get AES decryption working in the case where reading the underlying stream returns less data than requested. * Unit test for reading an AES encrypted zip from a stream whose reads return less than the requested data.
1 parent 3b4966c commit 82855a2

5 files changed

Lines changed: 102 additions & 3 deletions

File tree

src/ICSharpCode.SharpZipLib/Core/StreamUtils.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,54 @@ static public void ReadFully(Stream stream, byte[] buffer, int offset, int count
6464
}
6565
}
6666

67+
/// <summary>
68+
/// Read as much data as possible from a <see cref="Stream"/>", up to the requested number of bytes
69+
/// </summary>
70+
/// <param name="stream">The stream to read data from.</param>
71+
/// <param name="buffer">The buffer to store data in.</param>
72+
/// <param name="offset">The offset at which to begin storing data.</param>
73+
/// <param name="count">The number of bytes of data to store.</param>
74+
/// <exception cref="ArgumentNullException">Required parameter is null</exception>
75+
/// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> and or <paramref name="count"/> are invalid.</exception>
76+
static public int ReadRequestedBytes(Stream stream, byte[] buffer, int offset, int count)
77+
{
78+
if (stream == null)
79+
{
80+
throw new ArgumentNullException(nameof(stream));
81+
}
82+
83+
if (buffer == null)
84+
{
85+
throw new ArgumentNullException(nameof(buffer));
86+
}
87+
88+
// Offset can equal length when buffer and count are 0.
89+
if ((offset < 0) || (offset > buffer.Length))
90+
{
91+
throw new ArgumentOutOfRangeException(nameof(offset));
92+
}
93+
94+
if ((count < 0) || (offset + count > buffer.Length))
95+
{
96+
throw new ArgumentOutOfRangeException(nameof(count));
97+
}
98+
99+
int totalReadCount = 0;
100+
while (count > 0)
101+
{
102+
int readCount = stream.Read(buffer, offset, count);
103+
if (readCount <= 0)
104+
{
105+
break;
106+
}
107+
offset += readCount;
108+
count -= readCount;
109+
totalReadCount += readCount;
110+
}
111+
112+
return totalReadCount;
113+
}
114+
67115
/// <summary>
68116
/// Copy the contents of one <see cref="Stream"/> to another.
69117
/// </summary>

src/ICSharpCode.SharpZipLib/Encryption/ZipAESStream.cs

Lines changed: 2 additions & 1 deletion
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.Core;
45

56
namespace ICSharpCode.SharpZipLib.Encryption
67
{
@@ -78,7 +79,7 @@ public override int Read(byte[] buffer, int offset, int count)
7879
_slideBufFreePos -= _slideBufStartPos; // Note the -=
7980
_slideBufStartPos = 0;
8081
}
81-
int obtained = _stream.Read(_slideBuffer, _slideBufFreePos, lengthToRead);
82+
int obtained = StreamUtils.ReadRequestedBytes(_stream, _slideBuffer, _slideBufFreePos, lengthToRead);
8283
_slideBufFreePos += obtained;
8384

8485
// Recalculate how much data we now have

src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3564,12 +3564,12 @@ private Stream CreateAndInitDecryptionStream(Stream baseStream, ZipEntry entry)
35643564
}
35653565
int saltLen = entry.AESSaltLen;
35663566
byte[] saltBytes = new byte[saltLen];
3567-
int saltIn = baseStream.Read(saltBytes, 0, saltLen);
3567+
int saltIn = StreamUtils.ReadRequestedBytes(baseStream, saltBytes, 0, saltLen);
35683568
if (saltIn != saltLen)
35693569
throw new ZipException("AES Salt expected " + saltLen + " got " + saltIn);
35703570
//
35713571
byte[] pwdVerifyRead = new byte[2];
3572-
baseStream.Read(pwdVerifyRead, 0, 2);
3572+
StreamUtils.ReadFully(baseStream, pwdVerifyRead);
35733573
int blockSize = entry.AESKeySize / 8; // bits to bytes
35743574

35753575
var decryptor = new ZipAESTransform(rawPassword_, saltBytes, blockSize, false);

test/ICSharpCode.SharpZipLib.Tests/TestSupport/Streams.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,4 +521,22 @@ protected override void Dispose(bool disposing)
521521

522522
#endregion Instance Fields
523523
}
524+
525+
internal class SingleByteReadingStream : MemoryStream
526+
{
527+
/// <summary>
528+
/// Initializes a new instance of the <see cref="SingleByteReadingStream"/> class.
529+
/// </summary>
530+
public SingleByteReadingStream()
531+
{
532+
}
533+
534+
public override int Read(byte[] buffer, int offset, int count)
535+
{
536+
if (count > 0)
537+
count = 1;
538+
539+
return base.Read(buffer, offset, count);
540+
}
541+
}
524542
}

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Diagnostics;
55
using System.IO;
66
using System.Text;
7+
using ICSharpCode.SharpZipLib.Tests.TestSupport;
78

89
namespace ICSharpCode.SharpZipLib.Tests.Zip
910
{
@@ -56,6 +57,37 @@ public void ZipFileAesDecryption()
5657
}
5758
}
5859

60+
[Test]
61+
[Category("Encryption")]
62+
[Category("Zip")]
63+
public void ZipFileAesRead()
64+
{
65+
var password = "password";
66+
67+
using (var ms = new SingleByteReadingStream())
68+
{
69+
WriteEncryptedZipToStream(ms, password, 256);
70+
ms.Seek(0, SeekOrigin.Begin);
71+
72+
var zipFile = new ZipFile(ms)
73+
{
74+
Password = password
75+
};
76+
77+
foreach (ZipEntry entry in zipFile)
78+
{
79+
if (!entry.IsFile) continue;
80+
81+
using (var zis = zipFile.GetInputStream(entry))
82+
using (var sr = new StreamReader(zis, Encoding.UTF8))
83+
{
84+
var content = sr.ReadToEnd();
85+
Assert.AreEqual(DummyDataString, content, "Decompressed content does not match input data");
86+
}
87+
}
88+
}
89+
}
90+
5991
private static readonly string[] possible7zPaths = new[] {
6092
// Check in PATH
6193
"7z", "7za",

0 commit comments

Comments
 (0)