Skip to content

Commit 7f67548

Browse files
author
GabrielMarquezMatte
committed
fix(crypto): ConfigureAwait convention, destination writability check, self-contained pass rewind, short-stream signature check
1 parent 5b346e9 commit 7f67548

4 files changed

Lines changed: 43 additions & 9 deletions

File tree

src/ExcelReader.Core/Crypto/PackageEncryptor.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ internal static class PackageEncryptor
2626

2727
internal static void Encrypt(Stream package, Stream destination, ExcelPassword password)
2828
{
29+
if (!destination.CanWrite)
30+
{
31+
throw new ArgumentException("The destination stream must be writable.", nameof(destination));
32+
}
2933
(byte[] encryptionInfo, Session session) = Prepare(package, password);
3034
try
3135
{
@@ -63,6 +67,10 @@ internal static void Encrypt(Stream package, Stream destination, ExcelPassword p
6367

6468
internal static async ValueTask EncryptAsync(Stream package, Stream destination, ExcelPassword password, CancellationToken ct)
6569
{
70+
if (!destination.CanWrite)
71+
{
72+
throw new ArgumentException("The destination stream must be writable.", nameof(destination));
73+
}
6674
(byte[] encryptionInfo, Session session) = Prepare(package, password);
6775
try
6876
{
@@ -160,6 +168,11 @@ private static (byte[] EncryptionInfo, Session Session) Prepare(Stream package,
160168
{
161169
throw new ArgumentException("The package stream is empty.", nameof(package));
162170
}
171+
if (plainLength < 4)
172+
{
173+
throw new ArgumentException(
174+
"The package stream is not an OOXML package (missing the PK\\x03\\x04 signature).", nameof(package));
175+
}
163176
if (password.Chars.IsEmpty)
164177
{
165178
throw new ArgumentException("The password must not be empty.", nameof(password));
@@ -254,7 +267,6 @@ internal byte[] ComputeHmac(byte[] hmacKey)
254267
{
255268
using IncrementalHash hmac = PackageIntegrity.CreateHmac(_descriptor.KeyData.Hash, hmacKey);
256269
hmac.AppendData(Prefix);
257-
Package.Position = _origin;
258270
using (Local local = BeginPass())
259271
{
260272
int read;
@@ -269,6 +281,7 @@ internal byte[] ComputeHmac(byte[] hmacKey)
269281

270282
internal Local BeginPass()
271283
{
284+
Package.Position = _origin;
272285
return new Local(_descriptor, _packageKey);
273286
}
274287

src/ExcelReader.Core/Reader/Excel.Encrypt.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,17 @@ public static async ValueTask EncryptPackageAsync(string packagePath, string des
7979
{
8080
ArgumentException.ThrowIfNullOrEmpty(packagePath);
8181
ArgumentException.ThrowIfNullOrEmpty(destinationPath);
82-
#pragma warning disable CA2007, MA0004
83-
await using FileStream package = new(packagePath, FileMode.Open, FileAccess.Read, FileShare.Read,
82+
FileStream package = new(packagePath, FileMode.Open, FileAccess.Read, FileShare.Read,
8483
bufferSize: 4096, useAsync: true);
85-
await using FileStream destination = new(destinationPath, FileMode.Create, FileAccess.Write,
86-
FileShare.None, bufferSize: 4096, useAsync: true);
87-
#pragma warning restore CA2007, MA0004
88-
await PackageEncryptor.EncryptAsync(package, destination, password, ct).ConfigureAwait(false);
84+
await using (package.ConfigureAwait(false))
85+
{
86+
FileStream destination = new(destinationPath, FileMode.Create, FileAccess.Write,
87+
FileShare.None, bufferSize: 4096, useAsync: true);
88+
await using (destination.ConfigureAwait(false))
89+
{
90+
await PackageEncryptor.EncryptAsync(package, destination, password, ct).ConfigureAwait(false);
91+
}
92+
}
8993
}
9094
}
9195
}

tests/ExcelReader.Tests/EncryptedPackageWriteTests.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,10 @@ public async Task EncryptPackageAsync_StreamOverload_RoundTrips()
151151
public void EncryptPackage_NullArguments_Throw()
152152
{
153153
using var stream = new MemoryStream([0x50, 0x4B, 0x03, 0x04], writable: false);
154-
Assert.Throws<ArgumentNullException>(() => Excel.EncryptPackage(null!, stream, Password));
154+
using var destination = new MemoryStream();
155+
Assert.Throws<ArgumentNullException>(() => Excel.EncryptPackage(null!, destination, Password));
155156
Assert.Throws<ArgumentNullException>(() => Excel.EncryptPackage(stream, null!, Password));
156-
Assert.Throws<ArgumentNullException>(() => Excel.EncryptPackage(stream, stream, null!));
157+
Assert.Throws<ArgumentNullException>(() => Excel.EncryptPackage(stream, destination, null!));
157158
}
158159
}
159160
}

tests/ExcelReader.Tests/PackageEncryptorTests.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,22 @@ public void Encrypt_EmptyPackage_Throws()
111111
Assert.Throws<ArgumentException>(() => PackageEncryptor.Encrypt(source, destination, Password));
112112
}
113113

114+
[Fact]
115+
public void Encrypt_PackageShorterThanSignature_ThrowsArgumentException()
116+
{
117+
using var source = new MemoryStream([0x50, 0x4B], writable: false);
118+
using var destination = new MemoryStream();
119+
Assert.Throws<ArgumentException>(() => PackageEncryptor.Encrypt(source, destination, Password));
120+
}
121+
122+
[Fact]
123+
public void Encrypt_NonWritableDestination_ThrowsArgumentException()
124+
{
125+
using var source = new MemoryStream(SamplePackage(), writable: false);
126+
using var destination = new MemoryStream([], writable: false);
127+
Assert.Throws<ArgumentException>(() => PackageEncryptor.Encrypt(source, destination, Password));
128+
}
129+
114130
// The index of a stream's last real data byte, as opposed to the zero padding a big stream
115131
// carries out to its final sector boundary — nothing reads that padding, so flipping a byte
116132
// in it cannot be detected as tampering.

0 commit comments

Comments
 (0)