From 8cf8ad57081c6013e2ca9324ef8eba8869d932f2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 14 Jul 2026 13:30:44 +0000
Subject: [PATCH 01/10] Exclude ZIP encryption crypto from browser builds
Co-authored-by: alinpahontu2912 <56953855+alinpahontu2912@users.noreply.github.com>
---
.../src/Resources/Strings.resx | 3 ++
.../src/System.IO.Compression.csproj | 9 ++---
.../IO/Compression/ZipArchiveEntry.Async.cs | 4 +--
.../System/IO/Compression/ZipArchiveEntry.cs | 4 +--
.../ZipEncryption.PlatformNotSupported.cs | 36 +++++++++++++++++++
.../tests/ZipArchive/zip_ReadTests.cs | 22 ++++++++++++
.../tests/ZipCryptoStreamConformanceTests.cs | 2 +-
.../wasm/Wasm.Build.Tests/NativeBuildTests.cs | 2 ++
8 files changed, 73 insertions(+), 9 deletions(-)
create mode 100644 src/libraries/System.IO.Compression/src/System/IO/Compression/ZipEncryption.PlatformNotSupported.cs
diff --git a/src/libraries/System.IO.Compression/src/Resources/Strings.resx b/src/libraries/System.IO.Compression/src/Resources/Strings.resx
index 8f70a3e2dea3d2..47dd45445299ab 100644
--- a/src/libraries/System.IO.Compression/src/Resources/Strings.resx
+++ b/src/libraries/System.IO.Compression/src/Resources/Strings.resx
@@ -426,6 +426,9 @@
WinZip AES encryption is not supported on the browser platform.
+
+ ZIP encryption is not supported on the browser platform.
+
The entry's encryption method is not supported.
diff --git a/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj b/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
index c71e8f1e4b181f..698300f947ac5f 100644
--- a/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
+++ b/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
@@ -65,9 +65,10 @@
-
-
-
+
+
+
+
@@ -111,7 +112,7 @@
-
+
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.Async.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.Async.cs
index 6a74dc6f308525..274c09cd07e9c2 100644
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.Async.cs
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.Async.cs
@@ -728,7 +728,7 @@ private async Task WriteLocalFileHeaderAndDataIfNeededAsync(bool forceWrite, Can
ushort verifierLow2Bytes = (ushort)ZipHelper.DateTimeToDosTime(_lastModified.DateTime);
- ZipCryptoStream encryptionStream = ZipCryptoStream.Create(
+ Stream encryptionStream = ZipCryptoStream.Create(
baseStream: _archive.ArchiveStream,
keys: _derivedZipCryptoKeyMaterial.Value,
passwordVerifierLow2Bytes: verifierLow2Bytes,
@@ -782,7 +782,7 @@ private async Task WriteLocalFileHeaderAndDataIfNeededAsync(bool forceWrite, Can
// The AES extra field stores the real compression method
bool useDeflate = _compressionLevel != CompressionLevel.NoCompression;
- WinZipAesStream encryptionStream = WinZipAesStream.Create(
+ Stream encryptionStream = WinZipAesStream.Create(
baseStream: _archive.ArchiveStream,
keyMaterial: _derivedAesKeyMaterial.Value,
totalStreamSize: -1,
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs
index f6f0dfc2af3106..76c1f2c4692df3 100644
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs
@@ -1776,7 +1776,7 @@ private unsafe void WriteLocalFileHeaderAndDataIfNeeded(bool forceWrite)
ushort verifierLow2Bytes = (ushort)ZipHelper.DateTimeToDosTime(_lastModified.DateTime);
- using (ZipCryptoStream encryptionStream = ZipCryptoStream.Create(
+ using (Stream encryptionStream = ZipCryptoStream.Create(
baseStream: _archive.ArchiveStream,
keys: _derivedZipCryptoKeyMaterial.Value,
passwordVerifierLow2Bytes: verifierLow2Bytes,
@@ -1807,7 +1807,7 @@ private unsafe void WriteLocalFileHeaderAndDataIfNeeded(bool forceWrite)
bool useDeflate = _compressionLevel != CompressionLevel.NoCompression;
- using (WinZipAesStream encryptionStream = WinZipAesStream.Create(
+ using (Stream encryptionStream = WinZipAesStream.Create(
baseStream: _archive.ArchiveStream,
keyMaterial: _derivedAesKeyMaterial.Value,
totalStreamSize: -1,
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipEncryption.PlatformNotSupported.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipEncryption.PlatformNotSupported.cs
new file mode 100644
index 00000000000000..2fa6cad6621997
--- /dev/null
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipEncryption.PlatformNotSupported.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.IO.Compression
+{
+ internal readonly struct WinZipAesKeyMaterial;
+
+ internal static class WinZipAesStream
+ {
+ internal static int GetSaltSize(int keySizeBits) => throw CreateException();
+
+ internal static WinZipAesKeyMaterial CreateKey(ReadOnlySpan password, byte[]? salt, int keySizeBits) => throw CreateException();
+
+ internal static Stream Create(Stream baseStream, WinZipAesKeyMaterial keyMaterial, long totalStreamSize, bool encrypting, bool leaveOpen = false) => throw CreateException();
+
+ internal static Task CreateAsync(Stream baseStream, WinZipAesKeyMaterial keyMaterial, long totalStreamSize, bool encrypting, bool leaveOpen = false, CancellationToken cancellationToken = default) => throw CreateException();
+
+ private static PlatformNotSupportedException CreateException() => new(SR.ZipEncryptionNotSupportedOnBrowser);
+ }
+
+ internal static class ZipCryptoStream
+ {
+ internal static ZipCryptoKeys CreateKey(ReadOnlySpan password) => throw CreateException();
+
+ internal static Stream Create(Stream baseStream, ZipCryptoKeys keys, byte expectedCheckByte, bool encrypting, bool leaveOpen = false) => throw CreateException();
+
+ internal static Task CreateAsync(Stream baseStream, ZipCryptoKeys keys, byte expectedCheckByte, bool encrypting, CancellationToken cancellationToken = default, bool leaveOpen = false) => throw CreateException();
+
+ internal static Stream Create(Stream baseStream, ZipCryptoKeys keys, ushort passwordVerifierLow2Bytes, bool encrypting, uint? crc32 = null, bool leaveOpen = false) => throw CreateException();
+
+ private static PlatformNotSupportedException CreateException() => new(SR.ZipEncryptionNotSupportedOnBrowser);
+ }
+}
diff --git a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs
index 8ab7802ff61ac5..69e763579ff74b 100644
--- a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs
+++ b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs
@@ -1052,6 +1052,28 @@ public async Task DecryptEntries_MixedEncryptions(bool async)
await DisposeZipArchive(async, archive);
}
+ [Theory]
+ [MemberData(nameof(Get_Booleans_Data))]
+ [PlatformSpecific(TestPlatforms.Browser)]
+ public async Task DecryptZipCryptoEntry_ThrowsPlatformNotSupported(bool async)
+ {
+ using Stream archiveStream = await StreamHelpers.CreateTempCopyStream(passwordProtected("PasswordProtected_MixedEncryptions.zip"));
+ ZipArchive archive = await CreateZipArchive(async, archiveStream, ZipArchiveMode.Read);
+
+ try
+ {
+ ZipArchiveEntry entry = archive.GetEntry("hello.txt");
+ Assert.NotNull(entry);
+ Assert.Equal(ZipEncryptionMethod.ZipCrypto, entry.EncryptionMethod);
+
+ await Assert.ThrowsAsync(() => OpenEntryStream(async, entry, "S3cur3P@ssw0rd"));
+ }
+ finally
+ {
+ await DisposeZipArchive(async, archive);
+ }
+ }
+
[Theory]
[MemberData(nameof(Get_Booleans_Data))]
[SkipOnPlatform(TestPlatforms.Browser, "WinZip AES encryption is not supported on browser.")]
diff --git a/src/libraries/System.IO.Compression/tests/ZipCryptoStreamConformanceTests.cs b/src/libraries/System.IO.Compression/tests/ZipCryptoStreamConformanceTests.cs
index 3d89614d329d84..1d6f571194ea62 100644
--- a/src/libraries/System.IO.Compression/tests/ZipCryptoStreamConformanceTests.cs
+++ b/src/libraries/System.IO.Compression/tests/ZipCryptoStreamConformanceTests.cs
@@ -125,6 +125,6 @@ static ZipCryptoStreamConformanceTests()
// the tests are correctly skipped instead of failing during type initialization.
internal static class ZipCryptoStreamTestsSupport
{
- public static bool IsSupported => PlatformDetection.IsReflectionEmitSupported && PlatformDetection.IsMultithreadingSupported;
+ public static bool IsSupported => !PlatformDetection.IsBrowser && PlatformDetection.IsReflectionEmitSupported && PlatformDetection.IsMultithreadingSupported;
}
}
diff --git a/src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs b/src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs
index 3e107ad21bb180..93f450bf1bf73e 100644
--- a/src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs
@@ -115,6 +115,8 @@ public async Task ZipArchiveInteropTest()
Configuration config = Configuration.Debug;
ProjectInfo info = CopyTestAsset(config, false, TestAsset.WasmBasicTestApp, "ZipArchiveInteropTest", extraProperties: "true");
BuildProject(info, config, new BuildOptions(AssertAppBundle: false));
+ BuildPaths paths = GetBuildPaths(config, forPublish: false);
+ Assert.DoesNotContain("System_Security_Cryptography", File.ReadAllText(Path.Combine(paths.ObjWasmDir, "driver-gen.c")));
RunResult result = await RunForBuildWithDotnetRun(new BrowserRunOptions(config, TestScenario: "ZipArchiveInteropTest"));
Assert.Collection(
result.TestOutput,
From c315ffbbd97565c45df9dccdaa6885fc1ab2fd50 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 14 Jul 2026 13:34:55 +0000
Subject: [PATCH 02/10] Preserve browser ZipCrypto support
Co-authored-by: alinpahontu2912 <56953855+alinpahontu2912@users.noreply.github.com>
---
.../src/Resources/Strings.resx | 4 +---
.../src/System.IO.Compression.csproj | 4 ++--
...d.cs => WinZipAes.PlatformNotSupported.cs} | 15 +------------
.../System/IO/Compression/ZipCryptoStream.cs | 7 +++---
.../tests/ZipArchive/zip_ReadTests.cs | 22 +++++++++----------
.../tests/ZipCryptoStreamConformanceTests.cs | 2 +-
6 files changed, 19 insertions(+), 35 deletions(-)
rename src/libraries/System.IO.Compression/src/System/IO/Compression/{ZipEncryption.PlatformNotSupported.cs => WinZipAes.PlatformNotSupported.cs} (52%)
diff --git a/src/libraries/System.IO.Compression/src/Resources/Strings.resx b/src/libraries/System.IO.Compression/src/Resources/Strings.resx
index 47dd45445299ab..dcded6ccfb88e1 100644
--- a/src/libraries/System.IO.Compression/src/Resources/Strings.resx
+++ b/src/libraries/System.IO.Compression/src/Resources/Strings.resx
@@ -426,9 +426,7 @@
WinZip AES encryption is not supported on the browser platform.
-
- ZIP encryption is not supported on the browser platform.
-
+
The entry's encryption method is not supported.
diff --git a/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj b/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
index 698300f947ac5f..2a30f33bbc0f96 100644
--- a/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
+++ b/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
@@ -65,10 +65,10 @@
-
+
-
+
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipEncryption.PlatformNotSupported.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/WinZipAes.PlatformNotSupported.cs
similarity index 52%
rename from src/libraries/System.IO.Compression/src/System/IO/Compression/ZipEncryption.PlatformNotSupported.cs
rename to src/libraries/System.IO.Compression/src/System/IO/Compression/WinZipAes.PlatformNotSupported.cs
index 2fa6cad6621997..75c9de825295e3 100644
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipEncryption.PlatformNotSupported.cs
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/WinZipAes.PlatformNotSupported.cs
@@ -18,19 +18,6 @@ internal static class WinZipAesStream
internal static Task CreateAsync(Stream baseStream, WinZipAesKeyMaterial keyMaterial, long totalStreamSize, bool encrypting, bool leaveOpen = false, CancellationToken cancellationToken = default) => throw CreateException();
- private static PlatformNotSupportedException CreateException() => new(SR.ZipEncryptionNotSupportedOnBrowser);
- }
-
- internal static class ZipCryptoStream
- {
- internal static ZipCryptoKeys CreateKey(ReadOnlySpan password) => throw CreateException();
-
- internal static Stream Create(Stream baseStream, ZipCryptoKeys keys, byte expectedCheckByte, bool encrypting, bool leaveOpen = false) => throw CreateException();
-
- internal static Task CreateAsync(Stream baseStream, ZipCryptoKeys keys, byte expectedCheckByte, bool encrypting, CancellationToken cancellationToken = default, bool leaveOpen = false) => throw CreateException();
-
- internal static Stream Create(Stream baseStream, ZipCryptoKeys keys, ushort passwordVerifierLow2Bytes, bool encrypting, uint? crc32 = null, bool leaveOpen = false) => throw CreateException();
-
- private static PlatformNotSupportedException CreateException() => new(SR.ZipEncryptionNotSupportedOnBrowser);
+ private static PlatformNotSupportedException CreateException() => new(SR.WinZipEncryptionNotSupportedOnBrowser);
}
}
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.cs
index 3243c56566ebb0..0381a9a2c7ed9e 100644
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.cs
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.cs
@@ -4,7 +4,6 @@
using System.Buffers;
using System.Buffers.Binary;
using System.Diagnostics;
-using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -138,7 +137,7 @@ internal static ZipCryptoKeys CreateKey(ReadOnlySpan password)
}
finally
{
- CryptographicOperations.ZeroMemory(passwordBytes);
+ Array.Clear(passwordBytes);
ArrayPool.Shared.Return(passwordBytes);
}
@@ -150,7 +149,9 @@ private void CalculateHeader(Span header)
Debug.Assert(header.Length == 12);
// bytes 0..9 random
- RandomNumberGenerator.Fill(header.Slice(0, 10));
+ Span randomBytes = stackalloc byte[16];
+ Guid.NewGuid().TryWriteBytes(randomBytes);
+ randomBytes.Slice(0, 10).CopyTo(header);
// bytes 10..11 verifier
if (_crc32ForHeader.HasValue)
diff --git a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs
index 69e763579ff74b..e79f9eeead138e 100644
--- a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs
+++ b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs
@@ -1055,23 +1055,21 @@ public async Task DecryptEntries_MixedEncryptions(bool async)
[Theory]
[MemberData(nameof(Get_Booleans_Data))]
[PlatformSpecific(TestPlatforms.Browser)]
- public async Task DecryptZipCryptoEntry_ThrowsPlatformNotSupported(bool async)
+ public async Task DecryptZipCryptoEntry_Browser(bool async)
{
+ const string Password = "S3cur3P@ssw0rd";
using Stream archiveStream = await StreamHelpers.CreateTempCopyStream(passwordProtected("PasswordProtected_MixedEncryptions.zip"));
ZipArchive archive = await CreateZipArchive(async, archiveStream, ZipArchiveMode.Read);
- try
- {
- ZipArchiveEntry entry = archive.GetEntry("hello.txt");
- Assert.NotNull(entry);
- Assert.Equal(ZipEncryptionMethod.ZipCrypto, entry.EncryptionMethod);
+ ZipArchiveEntry entry = archive.GetEntry("hello.txt");
+ Assert.NotNull(entry);
+ Assert.Equal(ZipEncryptionMethod.ZipCrypto, entry.EncryptionMethod);
- await Assert.ThrowsAsync(() => OpenEntryStream(async, entry, "S3cur3P@ssw0rd"));
- }
- finally
- {
- await DisposeZipArchive(async, archive);
- }
+ using Stream entryStream = await OpenEntryStream(async, entry, Password);
+ using StreamReader reader = new(entryStream);
+ Assert.Equal("Hello", reader.ReadToEnd().TrimEnd());
+
+ await DisposeZipArchive(async, archive);
}
[Theory]
diff --git a/src/libraries/System.IO.Compression/tests/ZipCryptoStreamConformanceTests.cs b/src/libraries/System.IO.Compression/tests/ZipCryptoStreamConformanceTests.cs
index 1d6f571194ea62..3d89614d329d84 100644
--- a/src/libraries/System.IO.Compression/tests/ZipCryptoStreamConformanceTests.cs
+++ b/src/libraries/System.IO.Compression/tests/ZipCryptoStreamConformanceTests.cs
@@ -125,6 +125,6 @@ static ZipCryptoStreamConformanceTests()
// the tests are correctly skipped instead of failing during type initialization.
internal static class ZipCryptoStreamTestsSupport
{
- public static bool IsSupported => !PlatformDetection.IsBrowser && PlatformDetection.IsReflectionEmitSupported && PlatformDetection.IsMultithreadingSupported;
+ public static bool IsSupported => PlatformDetection.IsReflectionEmitSupported && PlatformDetection.IsMultithreadingSupported;
}
}
From 119fe70e42185b61eeb8ce40c02f3cfa8930d317 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 15 Jul 2026 08:34:19 +0000
Subject: [PATCH 03/10] Check WASM P/Invoke table in ZIP test
Co-authored-by: alinpahontu2912 <56953855+alinpahontu2912@users.noreply.github.com>
---
src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs b/src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs
index 93f450bf1bf73e..0e90507526bd2f 100644
--- a/src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs
@@ -116,7 +116,7 @@ public async Task ZipArchiveInteropTest()
ProjectInfo info = CopyTestAsset(config, false, TestAsset.WasmBasicTestApp, "ZipArchiveInteropTest", extraProperties: "true");
BuildProject(info, config, new BuildOptions(AssertAppBundle: false));
BuildPaths paths = GetBuildPaths(config, forPublish: false);
- Assert.DoesNotContain("System_Security_Cryptography", File.ReadAllText(Path.Combine(paths.ObjWasmDir, "driver-gen.c")));
+ Assert.DoesNotContain("System_Security_Cryptography", File.ReadAllText(Path.Combine(paths.ObjWasmDir, "pinvoke-table.h")));
RunResult result = await RunForBuildWithDotnetRun(new BrowserRunOptions(config, TestScenario: "ZipArchiveInteropTest"));
Assert.Collection(
result.TestOutput,
From 7ad60748d6aeb2007fb23b8e3eceaa1b26bb6275 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 15 Jul 2026 08:48:36 +0000
Subject: [PATCH 04/10] Handle both WASM runtime table names
Co-authored-by: alinpahontu2912 <56953855+alinpahontu2912@users.noreply.github.com>
---
src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs b/src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs
index 0e90507526bd2f..5473271b457946 100644
--- a/src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs
@@ -116,7 +116,8 @@ public async Task ZipArchiveInteropTest()
ProjectInfo info = CopyTestAsset(config, false, TestAsset.WasmBasicTestApp, "ZipArchiveInteropTest", extraProperties: "true");
BuildProject(info, config, new BuildOptions(AssertAppBundle: false));
BuildPaths paths = GetBuildPaths(config, forPublish: false);
- Assert.DoesNotContain("System_Security_Cryptography", File.ReadAllText(Path.Combine(paths.ObjWasmDir, "pinvoke-table.h")));
+ string pinvokeTableFileName = IsCoreClrRuntime ? "pinvoke-table.cpp" : "pinvoke-table.h";
+ Assert.DoesNotContain("System_Security_Cryptography", File.ReadAllText(Path.Combine(paths.ObjWasmDir, pinvokeTableFileName)));
RunResult result = await RunForBuildWithDotnetRun(new BrowserRunOptions(config, TestScenario: "ZipArchiveInteropTest"));
Assert.Collection(
result.TestOutput,
From 88e7cf08817d75932d5514c0e90d264ffce9fd9f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 15 Jul 2026 08:53:10 +0000
Subject: [PATCH 05/10] Use CoreCLR PInvoke output name
Co-authored-by: alinpahontu2912 <56953855+alinpahontu2912@users.noreply.github.com>
---
src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs b/src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs
index 5473271b457946..e0d1fad124f520 100644
--- a/src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/NativeBuildTests.cs
@@ -116,7 +116,7 @@ public async Task ZipArchiveInteropTest()
ProjectInfo info = CopyTestAsset(config, false, TestAsset.WasmBasicTestApp, "ZipArchiveInteropTest", extraProperties: "true");
BuildProject(info, config, new BuildOptions(AssertAppBundle: false));
BuildPaths paths = GetBuildPaths(config, forPublish: false);
- string pinvokeTableFileName = IsCoreClrRuntime ? "pinvoke-table.cpp" : "pinvoke-table.h";
+ string pinvokeTableFileName = IsCoreClrRuntime ? "callhelpers-pinvoke.cpp" : "pinvoke-table.h";
Assert.DoesNotContain("System_Security_Cryptography", File.ReadAllText(Path.Combine(paths.ObjWasmDir, pinvokeTableFileName)));
RunResult result = await RunForBuildWithDotnetRun(new BrowserRunOptions(config, TestScenario: "ZipArchiveInteropTest"));
Assert.Collection(
From d199061775f8c549ad5f16001a902d28ed8e4841 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 31 Jul 2026 07:19:24 +0000
Subject: [PATCH 06/10] Use CSPRNG for ZipCrypto header bytes
Co-authored-by: alinpahontu2912 <56953855+alinpahontu2912@users.noreply.github.com>
---
.../src/System/IO/Compression/ZipCryptoStream.cs | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.cs
index 0381a9a2c7ed9e..bf3b80cfa11000 100644
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.cs
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.cs
@@ -4,6 +4,7 @@
using System.Buffers;
using System.Buffers.Binary;
using System.Diagnostics;
+using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -149,9 +150,9 @@ private void CalculateHeader(Span header)
Debug.Assert(header.Length == 12);
// bytes 0..9 random
- Span randomBytes = stackalloc byte[16];
- Guid.NewGuid().TryWriteBytes(randomBytes);
- randomBytes.Slice(0, 10).CopyTo(header);
+ Span randomBytes = stackalloc byte[10];
+ RandomNumberGenerator.Fill(randomBytes);
+ randomBytes.CopyTo(header);
// bytes 10..11 verifier
if (_crc32ForHeader.HasValue)
From 9b8d47cc5bf9bd7d18ae1e83d3a15a65135ad6fe Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 31 Jul 2026 07:47:57 +0000
Subject: [PATCH 07/10] WIP: switch ZipCrypto header randomness to CSPRNG with
browser fallback
Co-authored-by: alinpahontu2912 <56953855+alinpahontu2912@users.noreply.github.com>
---
.../src/System.IO.Compression.csproj | 2 ++
.../Compression/ZipCryptoStream.Random.Browser.cs | 15 +++++++++++++++
.../ZipCryptoStream.Random.NonBrowser.cs | 15 +++++++++++++++
.../src/System/IO/Compression/ZipCryptoStream.cs | 7 ++-----
4 files changed, 34 insertions(+), 5 deletions(-)
create mode 100644 src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.Browser.cs
create mode 100644 src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.NonBrowser.cs
diff --git a/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj b/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
index 2a30f33bbc0f96..b6ffb04cc72c5d 100644
--- a/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
+++ b/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
@@ -66,6 +66,8 @@
+
+
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.Browser.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.Browser.cs
new file mode 100644
index 00000000000000..e04973563585d7
--- /dev/null
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.Browser.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.IO.Compression
+{
+ internal sealed partial class ZipCryptoStream
+ {
+ private static void FillHeaderRandomBytes(Span header)
+ {
+ Span guidBytes = stackalloc byte[16];
+ Guid.NewGuid().TryWriteBytes(guidBytes);
+ guidBytes.Slice(0, 10).CopyTo(header);
+ }
+ }
+}
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.NonBrowser.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.NonBrowser.cs
new file mode 100644
index 00000000000000..56c63770d6fa69
--- /dev/null
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.NonBrowser.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Security.Cryptography;
+
+namespace System.IO.Compression
+{
+ internal sealed partial class ZipCryptoStream
+ {
+ private static void FillHeaderRandomBytes(Span header)
+ {
+ RandomNumberGenerator.Fill(header.Slice(0, 10));
+ }
+ }
+}
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.cs
index bf3b80cfa11000..64450569184a34 100644
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.cs
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.cs
@@ -4,14 +4,13 @@
using System.Buffers;
using System.Buffers.Binary;
using System.Diagnostics;
-using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace System.IO.Compression
{
- internal sealed class ZipCryptoStream : Stream
+ internal sealed partial class ZipCryptoStream : Stream
{
private const int EncryptionBufferSize = 4096;
@@ -150,9 +149,7 @@ private void CalculateHeader(Span header)
Debug.Assert(header.Length == 12);
// bytes 0..9 random
- Span randomBytes = stackalloc byte[10];
- RandomNumberGenerator.Fill(randomBytes);
- randomBytes.CopyTo(header);
+ FillHeaderRandomBytes(header);
// bytes 10..11 verifier
if (_crc32ForHeader.HasValue)
From 77c2d4c3dbe37e15a4cbef373f8b3ed20241522e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 31 Jul 2026 08:41:52 +0000
Subject: [PATCH 08/10] Enforce CSPRNG for ZipCrypto header on browser and wasi
Co-authored-by: alinpahontu2912 <56953855+alinpahontu2912@users.noreply.github.com>
---
.../src/Resources/Strings.resx | 3 +++
.../src/System.IO.Compression.csproj | 5 ++--
...p.GetCryptographicallySecureRandomBytes.cs | 13 +++++++++++
.../ZipCryptoStream.Random.Browser.cs | 15 ------------
.../ZipCryptoStream.Random.BrowserOrWasi.cs | 23 +++++++++++++++++++
...r.cs => ZipCryptoStream.Random.Default.cs} | 0
6 files changed, 42 insertions(+), 17 deletions(-)
create mode 100644 src/libraries/System.IO.Compression/src/System/IO/Compression/Interop.GetCryptographicallySecureRandomBytes.cs
delete mode 100644 src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.Browser.cs
create mode 100644 src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.BrowserOrWasi.cs
rename src/libraries/System.IO.Compression/src/System/IO/Compression/{ZipCryptoStream.Random.NonBrowser.cs => ZipCryptoStream.Random.Default.cs} (100%)
diff --git a/src/libraries/System.IO.Compression/src/Resources/Strings.resx b/src/libraries/System.IO.Compression/src/Resources/Strings.resx
index dcded6ccfb88e1..d2fb9df005cfba 100644
--- a/src/libraries/System.IO.Compression/src/Resources/Strings.resx
+++ b/src/libraries/System.IO.Compression/src/Resources/Strings.resx
@@ -426,6 +426,9 @@
WinZip AES encryption is not supported on the browser platform.
+
+ Unable to generate cryptographically secure random bytes.
+
The entry's encryption method is not supported.
diff --git a/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj b/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
index b6ffb04cc72c5d..9af63969bfaef5 100644
--- a/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
+++ b/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
@@ -66,8 +66,9 @@
-
-
+
+
+
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/Interop.GetCryptographicallySecureRandomBytes.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/Interop.GetCryptographicallySecureRandomBytes.cs
new file mode 100644
index 00000000000000..bc93ee82bac327
--- /dev/null
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/Interop.GetCryptographicallySecureRandomBytes.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class Sys
+ {
+ [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetCryptographicallySecureRandomBytes")]
+ internal static unsafe partial int GetCryptographicallySecureRandomBytes(byte* buffer, int length);
+ }
+}
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.Browser.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.Browser.cs
deleted file mode 100644
index e04973563585d7..00000000000000
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.Browser.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace System.IO.Compression
-{
- internal sealed partial class ZipCryptoStream
- {
- private static void FillHeaderRandomBytes(Span header)
- {
- Span guidBytes = stackalloc byte[16];
- Guid.NewGuid().TryWriteBytes(guidBytes);
- guidBytes.Slice(0, 10).CopyTo(header);
- }
- }
-}
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.BrowserOrWasi.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.BrowserOrWasi.cs
new file mode 100644
index 00000000000000..68df99653c4d70
--- /dev/null
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.BrowserOrWasi.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.IO.Compression
+{
+ internal sealed partial class ZipCryptoStream
+ {
+ private static unsafe void FillHeaderRandomBytes(Span header)
+ {
+ // System.Security.Cryptography's RandomNumberGenerator is not referenced on browser and is
+ // not supported on wasi, so call the shared native CSPRNG directly. It is backed by
+ // crypto.getRandomValues() on browser and by wasi:random (getentropy/__wasi_random_get) on wasi.
+ Span randomBytes = header.Slice(0, 10);
+ fixed (byte* pRandomBytes = randomBytes)
+ {
+ if (Interop.Sys.GetCryptographicallySecureRandomBytes(pRandomBytes, randomBytes.Length) != 0)
+ {
+ throw new IOException(SR.UnableToGenerateRandomBytes);
+ }
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.NonBrowser.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.Default.cs
similarity index 100%
rename from src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.NonBrowser.cs
rename to src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.Random.Default.cs
From 6ef6c9911024b1b34ffe51a99d8c74bc7fc5b55a Mon Sep 17 00:00:00 2001
From: Stefan-Alin Pahontu <56953855+alinpahontu2912@users.noreply.github.com>
Date: Fri, 31 Jul 2026 11:58:14 +0200
Subject: [PATCH 09/10] Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---
.../Interop.GetCryptographicallySecureRandomBytes.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/Interop.GetCryptographicallySecureRandomBytes.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/Interop.GetCryptographicallySecureRandomBytes.cs
index bc93ee82bac327..128abfa506a90d 100644
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/Interop.GetCryptographicallySecureRandomBytes.cs
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/Interop.GetCryptographicallySecureRandomBytes.cs
@@ -7,7 +7,7 @@ internal static partial class Interop
{
internal static partial class Sys
{
- [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetCryptographicallySecureRandomBytes")]
+ [LibraryImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_GetCryptographicallySecureRandomBytes")]
internal static unsafe partial int GetCryptographicallySecureRandomBytes(byte* buffer, int length);
}
}
From 3e34fd01a1cdf0b4c8069c97c81c32d069274f07 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 31 Jul 2026 10:31:52 +0000
Subject: [PATCH 10/10] Preserve secure wipe of ZipCrypto password material on
non-browser platforms
Co-authored-by: alinpahontu2912 <56953855+alinpahontu2912@users.noreply.github.com>
---
.../src/System.IO.Compression.csproj | 2 ++
.../ZipCryptoStream.ClearSensitiveData.Browser.cs | 12 ++++++++++++
.../ZipCryptoStream.ClearSensitiveData.Default.cs | 14 ++++++++++++++
.../src/System/IO/Compression/ZipCryptoStream.cs | 2 +-
4 files changed, 29 insertions(+), 1 deletion(-)
create mode 100644 src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.ClearSensitiveData.Browser.cs
create mode 100644 src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.ClearSensitiveData.Default.cs
diff --git a/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj b/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
index 9af63969bfaef5..4390030a64ad79 100644
--- a/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
+++ b/src/libraries/System.IO.Compression/src/System.IO.Compression.csproj
@@ -69,6 +69,8 @@
+
+
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.ClearSensitiveData.Browser.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.ClearSensitiveData.Browser.cs
new file mode 100644
index 00000000000000..d689b2978e0f06
--- /dev/null
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.ClearSensitiveData.Browser.cs
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.IO.Compression
+{
+ internal sealed partial class ZipCryptoStream
+ {
+ // System.Security.Cryptography is not referenced on the browser platform, so
+ // CryptographicOperations.ZeroMemory is unavailable and Array.Clear is used instead.
+ private static void ClearSensitiveData(Span buffer) => buffer.Clear();
+ }
+}
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.ClearSensitiveData.Default.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.ClearSensitiveData.Default.cs
new file mode 100644
index 00000000000000..5ba117b88a4ac5
--- /dev/null
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.ClearSensitiveData.Default.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Security.Cryptography;
+
+namespace System.IO.Compression
+{
+ internal sealed partial class ZipCryptoStream
+ {
+ // Uses CryptographicOperations.ZeroMemory so the clear is not elided by the JIT,
+ // preserving the guarantee that password material is actually wiped from memory.
+ private static void ClearSensitiveData(Span buffer) => CryptographicOperations.ZeroMemory(buffer);
+ }
+}
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.cs
index 64450569184a34..3bca870a2e7148 100644
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.cs
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCryptoStream.cs
@@ -137,7 +137,7 @@ internal static ZipCryptoKeys CreateKey(ReadOnlySpan password)
}
finally
{
- Array.Clear(passwordBytes);
+ ClearSensitiveData(passwordBytes);
ArrayPool.Shared.Return(passwordBytes);
}