Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLayomi Akinrinade <layomia@gmail.com>2022-05-25 01:19:32 +0300
committerGitHub <noreply@github.com>2022-05-25 01:19:32 +0300
commitbfbb78354e536ac616f2f9dabf3db2b8fa8b9f64 (patch)
tree2cb29e98010fa2d9f3612aa980eba18283db1175 /src
parentc5f949efa20bcb555c453037fa954fcc403f9490 (diff)
Use SubtleCrypto API on browser DOM scenarios (#65966)
* Use SubtleCrypto API on browser DOM scenarios * Add sync over async implementation * Address misc feedback and make fixes * Address pinvoke errors * [Attempt] Correct execution of native digest API call at wasm layer * [Fix up] Correct execution of native digest API call at wasm layer * Update src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs * Address feedback and clean up * Re-implement the crypto worker in ts * Address feedback * Revert "Re-implement the crypto worker in ts" This reverts commit 6a743909605fb5b1194cae6bf571c2e6ff059409. * * moved stuff around and renamed it * initialization bit later * Add code to handle errors in worker (particularly on init) * Clean up * Add crypto dll to wasm native project * Add e2e test * Adjust test to reflect lack of SharedArrayBuffer for Chrome in test harness * Enable Chrome test and validate hashed value in tests * fix merge to track assert being renamed to mono_assert Co-authored-by: Eric StJohn <ericstj@microsoft.com> Co-authored-by: pavelsavara <pavel.savara@gmail.com> Co-authored-by: Ankit Jain <radical@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props2
-rw-r--r--src/libraries/Common/src/Interop/Browser/Interop.Libraries.cs12
-rw-r--r--src/libraries/Common/src/Interop/Browser/System.Security.Cryptography.Native.Browser/Interop.SimpleDigestHash.cs32
-rw-r--r--src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj12
-rw-r--r--src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Browser.cs13
-rw-r--r--src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SHAHashProvider.Browser.Managed.cs (renamed from src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SHAHashProvider.Browser.cs)6
-rw-r--r--src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SHAHashProvider.Browser.Native.cs96
-rw-r--r--src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj4
-rw-r--r--src/mono/wasm/build/WasmApp.Native.targets1
-rw-r--r--src/mono/wasm/build/WasmApp.targets2
-rw-r--r--src/mono/wasm/runtime/CMakeLists.txt3
-rw-r--r--src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js4
-rw-r--r--src/mono/wasm/runtime/crypto-worker.ts211
-rw-r--r--src/mono/wasm/runtime/dotnet-crypto-worker.js170
-rw-r--r--src/mono/wasm/runtime/es6/dotnet.es6.lib.js4
-rw-r--r--src/mono/wasm/runtime/exports.ts5
-rw-r--r--src/mono/wasm/runtime/startup.ts3
-rw-r--r--src/mono/wasm/wasm.proj4
-rw-r--r--src/native/libs/CMakeLists.txt2
-rw-r--r--src/native/libs/System.Security.Cryptography.Native.Browser/CMakeLists.txt14
-rw-r--r--src/native/libs/System.Security.Cryptography.Native.Browser/pal_browser.h18
-rw-r--r--src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.c30
-rw-r--r--src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.h25
-rw-r--r--src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs5
-rw-r--r--src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs53
25 files changed, 713 insertions, 18 deletions
diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props
index e98978abda4..6704af1c528 100644
--- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props
+++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props
@@ -95,6 +95,7 @@
<PlatformManifestFileEntry Include="libSystem.Security.Cryptography.Native.Apple.dylib" IsNative="true" />
<PlatformManifestFileEntry Include="libSystem.Security.Cryptography.Native.Android.a" IsNative="true" />
<PlatformManifestFileEntry Include="libSystem.Security.Cryptography.Native.Android.so" IsNative="true" />
+ <PlatformManifestFileEntry Include="libSystem.Security.Cryptography.Native.Browser.a" IsNative="true" />
<PlatformManifestFileEntry Include="libSystem.Security.Cryptography.Native.OpenSsl.a" IsNative="true" />
<PlatformManifestFileEntry Include="libSystem.Security.Cryptography.Native.OpenSsl.dylib" IsNative="true" />
<PlatformManifestFileEntry Include="libSystem.Security.Cryptography.Native.OpenSsl.so" IsNative="true" />
@@ -210,6 +211,7 @@
<PlatformManifestFileEntry Include="libmono-wasm-eh-js.a" IsNative="true" />
<PlatformManifestFileEntry Include="libmono-wasm-eh-wasm.a" IsNative="true" />
<PlatformManifestFileEntry Include="System.Private.Runtime.InteropServices.Javascript.dll" />
+ <PlatformManifestFileEntry Include="dotnet-crypto-worker.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.worker.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.js.symbols" IsNative="true" />
diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Libraries.cs b/src/libraries/Common/src/Interop/Browser/Interop.Libraries.cs
new file mode 100644
index 00000000000..b28d723f0bf
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Browser/Interop.Libraries.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.
+
+internal static partial class Interop
+{
+ internal static partial class Libraries
+ {
+ // Shims
+ internal const string SystemNative = "libSystem.Native";
+ internal const string CryptoNative = "libSystem.Security.Cryptography.Native.Browser";
+ }
+}
diff --git a/src/libraries/Common/src/Interop/Browser/System.Security.Cryptography.Native.Browser/Interop.SimpleDigestHash.cs b/src/libraries/Common/src/Interop/Browser/System.Security.Cryptography.Native.Browser/Interop.SimpleDigestHash.cs
new file mode 100644
index 00000000000..1304b45735b
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Browser/System.Security.Cryptography.Native.Browser/Interop.SimpleDigestHash.cs
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class BrowserCrypto
+ {
+ // These values are also defined in the pal_crypto_webworker header file, and utilized in the dotnet-crypto-worker in the wasm runtime.
+ internal enum SimpleDigest
+ {
+ Sha1,
+ Sha256,
+ Sha384,
+ Sha512,
+ };
+
+ [LibraryImport(Libraries.CryptoNative, EntryPoint = "SystemCryptoNativeBrowser_CanUseSimpleDigestHash")]
+ internal static partial int CanUseSimpleDigestHash();
+
+ [LibraryImport(Libraries.CryptoNative, EntryPoint = "SystemCryptoNativeBrowser_SimpleDigestHash")]
+ internal static unsafe partial int SimpleDigestHash(
+ SimpleDigest hash,
+ byte* input_buffer,
+ int input_len,
+ byte* output_buffer,
+ int output_len);
+ }
+}
diff --git a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj
index 19a6e4ed3f8..53d2ee092b0 100644
--- a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj
+++ b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>$(DefineConstants);INTERNAL_ASYMMETRIC_IMPLEMENTATIONS</DefineConstants>
@@ -532,12 +532,15 @@
</Compile>
</ItemGroup>
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'Browser'">
- <Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs"
- Link="Common\Interop\Unix\Interop.Libraries.cs" />
+ <!-- GetRandomBytes is identical on Unix and Browser -->
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetRandomBytes.cs"
Link="Common\Interop\Unix\System.Native\Interop.GetRandomBytes.cs" />
+ <Compile Include="$(CommonPath)Interop\Browser\Interop.Libraries.cs"
+ Link="Common\Interop\Browser\Interop.Libraries.cs" />
<Compile Include="$(CommonPath)System\Sha1ForNonSecretPurposes.cs"
Link="Common\System\Sha1ForNonSecretPurposes.cs" />
+ <Compile Include="$(CommonPath)Interop\Browser\System.Security.Cryptography.Native.Browser\Interop.SimpleDigestHash.cs"
+ Link="Common\Interop\Browser\System.Security.Cryptography.Native.Browser\Interop.SimpleDigestHash.cs" />
<Compile Include="System\Security\Cryptography\AesCcm.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\AesGcm.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\AesImplementation.NotSupported.cs" />
@@ -563,7 +566,8 @@
<Compile Include="System\Security\Cryptography\RC2Implementation.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\RSACryptoServiceProvider.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\RSA.Create.NotSupported.cs" />
- <Compile Include="System\Security\Cryptography\SHAHashProvider.Browser.cs" />
+ <Compile Include="System\Security\Cryptography\SHAHashProvider.Browser.Managed.cs" />
+ <Compile Include="System\Security\Cryptography\SHAHashProvider.Browser.Native.cs" />
<Compile Include="System\Security\Cryptography\TripleDESCryptoServiceProvider.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\TripleDesImplementation.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\X509Certificates\CertificatePal.NotSupported.cs" />
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Browser.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Browser.cs
index d49be47509f..031d28ffea2 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Browser.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HashProviderDispenser.Browser.cs
@@ -1,17 +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;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Security.Cryptography;
-using Microsoft.Win32.SafeHandles;
using Internal.Cryptography;
namespace System.Security.Cryptography
{
internal static partial class HashProviderDispenser
{
+ internal static readonly bool CanUseSubtleCryptoImpl = Interop.BrowserCrypto.CanUseSimpleDigestHash() == 1;
+
public static HashProvider CreateHashProvider(string hashAlgorithmId)
{
switch (hashAlgorithmId)
@@ -20,7 +17,9 @@ namespace System.Security.Cryptography
case HashAlgorithmNames.SHA256:
case HashAlgorithmNames.SHA384:
case HashAlgorithmNames.SHA512:
- return new SHAHashProvider(hashAlgorithmId);
+ return CanUseSubtleCryptoImpl
+ ? new SHANativeHashProvider(hashAlgorithmId)
+ : new SHAManagedHashProvider(hashAlgorithmId);
}
throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId));
}
@@ -38,7 +37,7 @@ namespace System.Security.Cryptography
public static int HashData(string hashAlgorithmId, ReadOnlySpan<byte> source, Span<byte> destination)
{
- HashProvider provider = HashProviderDispenser.CreateHashProvider(hashAlgorithmId);
+ HashProvider provider = CreateHashProvider(hashAlgorithmId);
provider.AppendHashData(source);
return provider.FinalizeHashAndReset(destination);
}
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SHAHashProvider.Browser.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SHAHashProvider.Browser.Managed.cs
index 5a72d4de54b..574cdf8790b 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SHAHashProvider.Browser.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SHAHashProvider.Browser.Managed.cs
@@ -1,4 +1,4 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.IO;
@@ -9,13 +9,13 @@ using static System.Numerics.BitOperations;
namespace System.Security.Cryptography
{
- internal sealed class SHAHashProvider : HashProvider
+ internal sealed class SHAManagedHashProvider : HashProvider
{
private int hashSizeInBytes;
private SHAManagedImplementationBase impl;
private MemoryStream? buffer;
- public SHAHashProvider(string hashAlgorithmId)
+ public SHAManagedHashProvider(string hashAlgorithmId)
{
switch (hashAlgorithmId)
{
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SHAHashProvider.Browser.Native.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SHAHashProvider.Browser.Native.cs
new file mode 100644
index 00000000000..c037761aafb
--- /dev/null
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SHAHashProvider.Browser.Native.cs
@@ -0,0 +1,96 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Diagnostics;
+using System.Security.Cryptography;
+
+using SimpleDigest = Interop.BrowserCrypto.SimpleDigest;
+
+namespace Internal.Cryptography
+{
+ internal sealed class SHANativeHashProvider : HashProvider
+ {
+ private readonly int _hashSizeInBytes;
+ private readonly SimpleDigest _impl;
+ private MemoryStream? _buffer;
+
+ public SHANativeHashProvider(string hashAlgorithmId)
+ {
+ Debug.Assert(HashProviderDispenser.CanUseSubtleCryptoImpl);
+
+ switch (hashAlgorithmId)
+ {
+ case HashAlgorithmNames.SHA1:
+ _impl = SimpleDigest.Sha1;
+ _hashSizeInBytes = 20;
+ break;
+ case HashAlgorithmNames.SHA256:
+ _impl = SimpleDigest.Sha256;
+ _hashSizeInBytes = 32;
+ break;
+ case HashAlgorithmNames.SHA384:
+ _impl = SimpleDigest.Sha384;
+ _hashSizeInBytes = 48;
+ break;
+ case HashAlgorithmNames.SHA512:
+ _impl = SimpleDigest.Sha512;
+ _hashSizeInBytes = 64;
+ break;
+ default:
+ throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId));
+ }
+ }
+
+ public override void AppendHashData(ReadOnlySpan<byte> data)
+ {
+ _buffer ??= new MemoryStream(1000);
+ _buffer.Write(data);
+ }
+
+ public override int FinalizeHashAndReset(Span<byte> destination)
+ {
+ GetCurrentHash(destination);
+ _buffer = null;
+
+ return _hashSizeInBytes;
+ }
+
+ public override int GetCurrentHash(Span<byte> destination)
+ {
+ Debug.Assert(destination.Length >= _hashSizeInBytes);
+
+ byte[] srcArray = Array.Empty<byte>();
+ int srcLength = 0;
+ if (_buffer != null)
+ {
+ srcArray = _buffer.GetBuffer();
+ srcLength = (int)_buffer.Length;
+ }
+
+ unsafe
+ {
+ fixed (byte* src = srcArray)
+ fixed (byte* dest = destination)
+ {
+ int res = Interop.BrowserCrypto.SimpleDigestHash(_impl, src, srcLength, dest, destination.Length);
+ Debug.Assert(res != 0);
+ }
+ }
+
+ return _hashSizeInBytes;
+ }
+
+ public override int HashSizeInBytes => _hashSizeInBytes;
+
+ public override void Dispose(bool disposing)
+ {
+ }
+
+ public override void Reset()
+ {
+ _buffer = null;
+ }
+ }
+}
diff --git a/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj b/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj
index 6fa63793c4d..f8e1b4697b8 100644
--- a/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj
+++ b/src/libraries/System.Security.Cryptography/tests/System.Security.Cryptography.Tests.csproj
@@ -6,6 +6,10 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<EnableLibraryImportGenerator>true</EnableLibraryImportGenerator>
</PropertyGroup>
+ <PropertyGroup Condition="'$(TargetOS)' == 'Browser'">
+ <Scenario>WasmTestOnBrowser</Scenario>
+ <WasmXHarnessArgs>$(WasmXHarnessArgs) --web-server-use-cop</WasmXHarnessArgs>
+ </PropertyGroup>
<!-- DesignTimeBuild requires all the TargetFramework Derived Properties to not be present in the first property group. -->
<PropertyGroup>
<TargetPlatformIdentifier>$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))</TargetPlatformIdentifier>
diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets
index b92320d735b..a224ecb9b0f 100644
--- a/src/mono/wasm/build/WasmApp.Native.targets
+++ b/src/mono/wasm/build/WasmApp.Native.targets
@@ -271,6 +271,7 @@
<_WasmPInvokeModules Include="libSystem.Native" />
<_WasmPInvokeModules Include="libSystem.IO.Compression.Native" />
<_WasmPInvokeModules Include="libSystem.Globalization.Native" />
+ <_WasmPInvokeModules Include="libSystem.Security.Cryptography.Native.Browser" />
</ItemGroup>
<PInvokeTableGenerator
diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets
index bc598307276..c1c7c84fd90 100644
--- a/src/mono/wasm/build/WasmApp.targets
+++ b/src/mono/wasm/build/WasmApp.targets
@@ -261,6 +261,7 @@
<_HasDotnetWasm Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.wasm'">true</_HasDotnetWasm>
<_HasDotnetJsWorker Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.worker.js'">true</_HasDotnetJsWorker>
+ <_HasDotnetJsCryptoWorker Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet-crypto-worker.js'">true</_HasDotnetJsCryptoWorker>
<_HasDotnetJsSymbols Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js.symbols'">true</_HasDotnetJsSymbols>
<_HasDotnetJs Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js'">true</_HasDotnetJs>
</PropertyGroup>
@@ -270,6 +271,7 @@
<WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.wasm" Condition="'$(_HasDotnetWasm)' != 'true'" />
<WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.js" Condition="'$(_HasDotnetJs)' != 'true'" />
<WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.worker.js" Condition="'$(_HasDotnetJsWorker)' != 'true' and Exists('$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.worker.js')" />
+ <WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet-crypto-worker.js" Condition="'$(_HasDotnetJsCryptoWorker)' != 'true' and Exists('$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet-crypto-worker.js')" />
<WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.js.symbols"
Condition="'$(WasmEmitSymbolMap)' == 'true' and '$(_HasDotnetJs)' != 'true' and Exists('$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.js.symbols')" />
diff --git a/src/mono/wasm/runtime/CMakeLists.txt b/src/mono/wasm/runtime/CMakeLists.txt
index dac8d63e719..9962dbff59c 100644
--- a/src/mono/wasm/runtime/CMakeLists.txt
+++ b/src/mono/wasm/runtime/CMakeLists.txt
@@ -26,7 +26,8 @@ target_link_libraries(dotnet
${MONO_ARTIFACTS_DIR}/libmono-wasm-eh-js.a
${MONO_ARTIFACTS_DIR}/libmono-profiler-aot.a
${NATIVE_BIN_DIR}/libSystem.Native.a
- ${NATIVE_BIN_DIR}/libSystem.IO.Compression.Native.a)
+ ${NATIVE_BIN_DIR}/libSystem.IO.Compression.Native.a
+ ${NATIVE_BIN_DIR}/libSystem.Security.Cryptography.Native.Browser.a)
set_target_properties(dotnet PROPERTIES
LINK_DEPENDS "${NATIVE_BIN_DIR}/src/emcc-default.rsp;${NATIVE_BIN_DIR}/src/cjs/dotnet.cjs.pre.js;${NATIVE_BIN_DIR}/src/cjs/runtime.cjs.iffe.js;${NATIVE_BIN_DIR}/src/cjs/dotnet.cjs.lib.js;${NATIVE_BIN_DIR}/src/pal_random.lib.js;${NATIVE_BIN_DIR}/src/cjs/dotnet.cjs.post.js;${NATIVE_BIN_DIR}/src/cjs/dotnet.cjs.extpost.js;"
diff --git a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js
index 79116860e1c..ec39de8e376 100644
--- a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js
+++ b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js
@@ -67,6 +67,10 @@ const linked_functions = [
// pal_icushim_static.c
"mono_wasm_load_icu_data",
"mono_wasm_get_icudt_name",
+
+ // pal_crypto_webworker.c
+ "dotnet_browser_simple_digest_hash",
+ "dotnet_browser_can_use_simple_digest_hash",
];
// -- this javascript file is evaluated by emcc during compilation! --
diff --git a/src/mono/wasm/runtime/crypto-worker.ts b/src/mono/wasm/runtime/crypto-worker.ts
new file mode 100644
index 00000000000..ea7bd9e6fce
--- /dev/null
+++ b/src/mono/wasm/runtime/crypto-worker.ts
@@ -0,0 +1,211 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+import { Module } from "./imports";
+import { mono_assert } from "./types";
+
+let mono_wasm_crypto: {
+ channel: LibraryChannel
+ worker: Worker
+} | null = null;
+
+export function dotnet_browser_can_use_simple_digest_hash(): number {
+ return mono_wasm_crypto === null ? 0 : 1;
+}
+
+export function dotnet_browser_simple_digest_hash(ver: number, input_buffer: number, input_len: number, output_buffer: number, output_len: number): number {
+ mono_assert(!!mono_wasm_crypto, "subtle crypto not initialized");
+
+ const msg = {
+ func: "digest",
+ type: ver,
+ data: Array.from(Module.HEAPU8.subarray(input_buffer, input_buffer + input_len))
+ };
+
+ const response = mono_wasm_crypto.channel.send_msg(JSON.stringify(msg));
+ const digest = JSON.parse(response);
+ if (digest.length > output_len) {
+ console.info("call_digest: about to throw!");
+ throw "DIGEST HASH: Digest length exceeds output length: " + digest.length + " > " + output_len;
+ }
+
+ Module.HEAPU8.set(digest, output_buffer);
+ return 1;
+}
+
+export function init_crypto(): void {
+ if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined"
+ && typeof SharedArrayBuffer !== "undefined"
+ && typeof Worker !== "undefined"
+ ) {
+ console.debug("MONO_WASM: Initializing Crypto WebWorker");
+
+ const chan = LibraryChannel.create(1024); // 1024 is the buffer size in char units.
+ const worker = new Worker("dotnet-crypto-worker.js");
+ mono_wasm_crypto = {
+ channel: chan,
+ worker: worker,
+ };
+ worker.postMessage({
+ comm_buf: chan.get_comm_buffer(),
+ msg_buf: chan.get_msg_buffer(),
+ msg_char_len: chan.get_msg_len()
+ });
+ worker.onerror = event => {
+ console.warn(`MONO_WASM: Error in Crypto WebWorker. Cryptography digest calls will fallback to managed implementation. Error: ${event.message}`);
+ mono_wasm_crypto = null;
+ };
+ }
+}
+
+class LibraryChannel {
+ private msg_char_len: number;
+ private comm_buf: SharedArrayBuffer;
+ private msg_buf: SharedArrayBuffer;
+ private comm: Int32Array;
+ private msg: Uint16Array;
+
+ // Index constants for the communication buffer.
+ private get STATE_IDX(): number { return 0; }
+ private get MSG_SIZE_IDX(): number { return 1; }
+ private get COMM_LAST_IDX(): number { return this.MSG_SIZE_IDX; }
+
+ // Communication states.
+ private get STATE_SHUTDOWN(): number { return -1; } // Shutdown
+ private get STATE_IDLE(): number { return 0; }
+ private get STATE_REQ(): number { return 1; }
+ private get STATE_RESP(): number { return 2; }
+ private get STATE_REQ_P(): number { return 3; } // Request has multiple parts
+ private get STATE_RESP_P(): number { return 4; } // Response has multiple parts
+ private get STATE_AWAIT(): number { return 5; } // Awaiting the next part
+
+ private constructor(msg_char_len: number) {
+ this.msg_char_len = msg_char_len;
+
+ const int_bytes = 4;
+ const comm_byte_len = int_bytes * (this.COMM_LAST_IDX + 1);
+ this.comm_buf = new SharedArrayBuffer(comm_byte_len);
+
+ // JavaScript character encoding is UTF-16.
+ const char_bytes = 2;
+ const msg_byte_len = char_bytes * this.msg_char_len;
+ this.msg_buf = new SharedArrayBuffer(msg_byte_len);
+
+ // Create the local arrays to use.
+ this.comm = new Int32Array(this.comm_buf);
+ this.msg = new Uint16Array(this.msg_buf);
+ }
+
+ public get_msg_len(): number { return this.msg_char_len; }
+ public get_msg_buffer(): SharedArrayBuffer { return this.msg_buf; }
+ public get_comm_buffer(): SharedArrayBuffer { return this.comm_buf; }
+
+ public send_msg(msg: string): string {
+ if (Atomics.load(this.comm, this.STATE_IDX) !== this.STATE_IDLE) {
+ throw "OWNER: Invalid sync communication channel state. " + Atomics.load(this.comm, this.STATE_IDX);
+ }
+ this.send_request(msg);
+ return this.read_response();
+ }
+
+ public shutdown(): void {
+ if (Atomics.load(this.comm, this.STATE_IDX) !== this.STATE_IDLE) {
+ throw "OWNER: Invalid sync communication channel state. " + Atomics.load(this.comm, this.STATE_IDX);
+ }
+
+ // Notify webworker
+ Atomics.store(this.comm, this.MSG_SIZE_IDX, 0);
+ Atomics.store(this.comm, this.STATE_IDX, this.STATE_SHUTDOWN);
+ Atomics.notify(this.comm, this.STATE_IDX);
+ }
+
+ private send_request(msg: string): void {
+ let state;
+ const msg_len = msg.length;
+ let msg_written = 0;
+
+ for (; ;) {
+ // Write the message and return how much was written.
+ const wrote = this.write_to_msg(msg, msg_written, msg_len);
+ msg_written += wrote;
+
+ // Indicate how much was written to the this.msg buffer.
+ Atomics.store(this.comm, this.MSG_SIZE_IDX, wrote);
+
+ // Indicate if this was the whole message or part of it.
+ state = msg_written === msg_len ? this.STATE_REQ : this.STATE_REQ_P;
+
+ // Notify webworker
+ Atomics.store(this.comm, this.STATE_IDX, state);
+ Atomics.notify(this.comm, this.STATE_IDX);
+
+ // The send message is complete.
+ if (state === this.STATE_REQ)
+ break;
+
+ // Wait for the worker to be ready for the next part.
+ // - Atomics.wait() is not permissible on the main thread.
+ do {
+ state = Atomics.load(this.comm, this.STATE_IDX);
+ } while (state !== this.STATE_AWAIT);
+ }
+ }
+
+ private write_to_msg(input: string, start: number, input_len: number): number {
+ let mi = 0;
+ let ii = start;
+ while (mi < this.msg_char_len && ii < input_len) {
+ this.msg[mi] = input.charCodeAt(ii);
+ ii++; // Next character
+ mi++; // Next buffer index
+ }
+ return ii - start;
+ }
+
+ private read_response(): string {
+ let state;
+ let response = "";
+ for (; ;) {
+ // Wait for webworker response.
+ // - Atomics.wait() is not permissible on the main thread.
+ do {
+ state = Atomics.load(this.comm, this.STATE_IDX);
+ } while (state !== this.STATE_RESP && state !== this.STATE_RESP_P);
+
+ const size_to_read = Atomics.load(this.comm, this.MSG_SIZE_IDX);
+
+ // Append the latest part of the message.
+ response += this.read_from_msg(0, size_to_read);
+
+ // The response is complete.
+ if (state === this.STATE_RESP) {
+ break;
+ }
+
+ // Reset the size and transition to await state.
+ Atomics.store(this.comm, this.MSG_SIZE_IDX, 0);
+ Atomics.store(this.comm, this.STATE_IDX, this.STATE_AWAIT);
+ Atomics.notify(this.comm, this.STATE_IDX);
+ }
+
+ // Reset the communication channel's state and let the
+ // webworker know we are done.
+ Atomics.store(this.comm, this.STATE_IDX, this.STATE_IDLE);
+ Atomics.notify(this.comm, this.STATE_IDX);
+
+ return response;
+ }
+
+ private read_from_msg(begin: number, end: number): string {
+ const slicedMessage: number[] = [];
+ this.msg.slice(begin, end).forEach((value, index) => slicedMessage[index] = value);
+ return String.fromCharCode.apply(null, slicedMessage);
+ }
+
+ public static create(msg_char_len: number): LibraryChannel {
+ if (msg_char_len === undefined) {
+ msg_char_len = 1024; // Default size is arbitrary but is in 'char' units (i.e. UTF-16 code points).
+ }
+ return new LibraryChannel(msg_char_len);
+ }
+}
diff --git a/src/mono/wasm/runtime/dotnet-crypto-worker.js b/src/mono/wasm/runtime/dotnet-crypto-worker.js
new file mode 100644
index 00000000000..c6416492a71
--- /dev/null
+++ b/src/mono/wasm/runtime/dotnet-crypto-worker.js
@@ -0,0 +1,170 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+var ChannelWorker = {
+ _impl: class {
+ // BEGIN ChannelOwner contract - shared constants.
+ get STATE_IDX() { return 0; }
+ get MSG_SIZE_IDX() { return 1; }
+
+ // Communication states.
+ get STATE_SHUTDOWN() { return -1; } // Shutdown
+ get STATE_IDLE() { return 0; }
+ get STATE_REQ() { return 1; }
+ get STATE_RESP() { return 2; }
+ get STATE_REQ_P() { return 3; } // Request has multiple parts
+ get STATE_RESP_P() { return 4; } // Response has multiple parts
+ get STATE_AWAIT() { return 5; } // Awaiting the next part
+ // END ChannelOwner contract - shared constants.
+
+ constructor(comm_buf, msg_buf, msg_char_len) {
+ this.comm = new Int32Array(comm_buf);
+ this.msg = new Uint16Array(msg_buf);
+ this.msg_char_len = msg_char_len;
+ }
+
+ async await_request(async_call) {
+ for (;;) {
+ // Wait for signal to perform operation
+ Atomics.wait(this.comm, this.STATE_IDX, this.STATE_IDLE);
+
+ // Read in request
+ var req = this._read_request();
+ if (req === this.STATE_SHUTDOWN)
+ break;
+
+ var resp = null;
+ try {
+ // Perform async action based on request
+ resp = await async_call(req);
+ }
+ catch (err) {
+ console.log("Request error: " + err);
+ resp = JSON.stringify(err);
+ }
+
+ // Send response
+ this._send_response(resp);
+ }
+ }
+
+ _read_request() {
+ var request = "";
+ for (;;) {
+ // Get the current state and message size
+ var state = Atomics.load(this.comm, this.STATE_IDX);
+ var size_to_read = Atomics.load(this.comm, this.MSG_SIZE_IDX);
+
+ // Append the latest part of the message.
+ request += this._read_from_msg(0, size_to_read);
+
+ // The request is complete.
+ if (state === this.STATE_REQ)
+ break;
+
+ // Shutdown the worker.
+ if (state === this.STATE_SHUTDOWN)
+ return this.STATE_SHUTDOWN;
+
+ // Reset the size and transition to await state.
+ Atomics.store(this.comm, this.MSG_SIZE_IDX, 0);
+ Atomics.store(this.comm, this.STATE_IDX, this.STATE_AWAIT);
+ Atomics.wait(this.comm, this.STATE_IDX, this.STATE_AWAIT);
+ }
+
+ return request;
+ }
+
+ _read_from_msg(begin, end) {
+ return String.fromCharCode.apply(null, this.msg.slice(begin, end));
+ }
+
+ _send_response(msg) {
+ if (Atomics.load(this.comm, this.STATE_IDX) !== this.STATE_REQ)
+ throw "WORKER: Invalid sync communication channel state.";
+
+ var state; // State machine variable
+ const msg_len = msg.length;
+ var msg_written = 0;
+
+ for (;;) {
+ // Write the message and return how much was written.
+ var wrote = this._write_to_msg(msg, msg_written, msg_len);
+ msg_written += wrote;
+
+ // Indicate how much was written to the this.msg buffer.
+ Atomics.store(this.comm, this.MSG_SIZE_IDX, wrote);
+
+ // Indicate if this was the whole message or part of it.
+ state = msg_written === msg_len ? this.STATE_RESP : this.STATE_RESP_P;
+
+ // Update the state
+ Atomics.store(this.comm, this.STATE_IDX, state);
+
+ // Wait for the transition to know the main thread has
+ // received the response by moving onto a new state.
+ Atomics.wait(this.comm, this.STATE_IDX, state);
+
+ // Done sending response.
+ if (state === this.STATE_RESP)
+ break;
+ }
+ }
+
+ _write_to_msg(input, start, input_len) {
+ var mi = 0;
+ var ii = start;
+ while (mi < this.msg_char_len && ii < input_len) {
+ this.msg[mi] = input.charCodeAt(ii);
+ ii++; // Next character
+ mi++; // Next buffer index
+ }
+ return ii - start;
+ }
+ },
+
+ create: function (comm_buf, msg_buf, msg_char_len) {
+ return new this._impl(comm_buf, msg_buf, msg_char_len);
+ }
+};
+
+async function call_digest(type, data) {
+ var digest_type = "";
+ switch(type) {
+ case 0: digest_type = "SHA-1"; break;
+ case 1: digest_type = "SHA-256"; break;
+ case 2: digest_type = "SHA-384"; break;
+ case 3: digest_type = "SHA-512"; break;
+ default:
+ throw "CRYPTO: Unknown digest: " + type;
+ }
+
+ // The 'crypto' API is not available in non-browser
+ // environments (for example, v8 server).
+ var digest = await crypto.subtle.digest(digest_type, data);
+ return Array.from(new Uint8Array(digest));
+}
+
+// Operation to perform.
+async function async_call(msg) {
+ const req = JSON.parse(msg);
+
+ if (req.func === "digest") {
+ var digestArr = await call_digest(req.type, new Uint8Array(req.data));
+ return JSON.stringify(digestArr);
+ } else {
+ throw "CRYPTO: Unknown request: " + req.func;
+ }
+}
+
+var s_channel;
+
+// Initialize WebWorker
+onmessage = function (p) {
+ var data = p;
+ if (p.data !== undefined) {
+ data = p.data;
+ }
+ s_channel = ChannelWorker.create(data.comm_buf, data.msg_buf, data.msg_char_len);
+ s_channel.await_request(async_call);
+};
diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
index f886800ab50..47b59063b7b 100644
--- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
+++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
@@ -104,6 +104,10 @@ const linked_functions = [
// pal_icushim_static.c
"mono_wasm_load_icu_data",
"mono_wasm_get_icudt_name",
+
+ // pal_crypto_webworker.c
+ "dotnet_browser_simple_digest_hash",
+ "dotnet_browser_can_use_simple_digest_hash",
];
// -- this javascript file is evaluated by emcc during compilation! --
diff --git a/src/mono/wasm/runtime/exports.ts b/src/mono/wasm/runtime/exports.ts
index ae7efdfab33..8a77c785c9c 100644
--- a/src/mono/wasm/runtime/exports.ts
+++ b/src/mono/wasm/runtime/exports.ts
@@ -68,6 +68,7 @@ import { fetch_like, readAsync_like } from "./polyfills";
import { EmscriptenModule } from "./types/emscripten";
import { mono_run_main, mono_run_main_and_exit } from "./run";
import { diagnostics } from "./diagnostics";
+import { dotnet_browser_can_use_simple_digest_hash, dotnet_browser_simple_digest_hash } from "./crypto-worker";
const MONO = {
// current "public" MONO API
@@ -365,6 +366,10 @@ export const __linker_exports: any = {
// also keep in sync with pal_icushim_static.c
mono_wasm_load_icu_data,
mono_wasm_get_icudt_name,
+
+ // pal_crypto_webworker.c
+ dotnet_browser_simple_digest_hash,
+ dotnet_browser_can_use_simple_digest_hash,
};
const INTERNAL: any = {
diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts
index 3a34af2244f..5c74ef0eb4b 100644
--- a/src/mono/wasm/runtime/startup.ts
+++ b/src/mono/wasm/runtime/startup.ts
@@ -15,6 +15,7 @@ import { VoidPtr, CharPtr } from "./types/emscripten";
import { DotnetPublicAPI } from "./exports";
import { mono_on_abort } from "./run";
import { mono_wasm_new_root } from "./roots";
+import { init_crypto } from "./crypto-worker";
export let runtime_is_initialized_resolve: Function;
export let runtime_is_initialized_reject: Function;
@@ -119,6 +120,8 @@ async function mono_wasm_pre_init(): Promise<void> {
await requirePromise;
}
+ init_crypto();
+
if (moduleExt.configSrc) {
try {
// sets MONO.config implicitly
diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj
index e439c173cc6..267bf886396 100644
--- a/src/mono/wasm/wasm.proj
+++ b/src/mono/wasm/wasm.proj
@@ -24,6 +24,7 @@
<_EmccCompileRspPath>$(NativeBinDir)src\emcc-compile.rsp</_EmccCompileRspPath>
<_EmccLinkRspPath>$(NativeBinDir)src\emcc-link.rsp</_EmccLinkRspPath>
<WasmNativeStrip Condition="'$(ContinuousIntegrationBuild)' == 'true'">false</WasmNativeStrip>
+ <SystemCryptoNativeDir>$(RepoRoot)\src\native\libs\System.Security.Cryptography.Native.Browser</SystemCryptoNativeDir>
</PropertyGroup>
<Target Name="CheckEnv">
@@ -47,6 +48,7 @@
<WasmPInvokeModule Include="libSystem.Native" />
<WasmPInvokeModule Include="libSystem.IO.Compression.Native" />
<WasmPInvokeModule Include="libSystem.Globalization.Native" />
+ <WasmPInvokeModule Include="libSystem.Security.Cryptography.Native.Browser" />
<WasmPInvokeAssembly Include="@(LibrariesRuntimeFiles)" Condition="'%(Extension)' == '.dll' and '%(IsNative)' != 'true'" />
</ItemGroup>
@@ -224,6 +226,7 @@
<Copy SourceFiles="runtime/driver.c;
runtime/pinvoke.c;
runtime/corebindings.c;
+ runtime/dotnet-crypto-worker.js;
$(SharedNativeRoot)libs\System.Native\pal_random.lib.js;"
DestinationFolder="$(NativeBinDir)src"
SkipUnchangedFiles="true" />
@@ -270,6 +273,7 @@
$(NativeBinDir)dotnet.d.ts;
$(NativeBinDir)package.json;
$(NativeBinDir)dotnet.wasm;
+ $(NativeBinDir)\src\dotnet-crypto-worker.js;
$(NativeBinDir)dotnet.timezones.blat"
DestinationFolder="$(MicrosoftNetCoreAppRuntimePackNativeDir)"
SkipUnchangedFiles="true" />
diff --git a/src/native/libs/CMakeLists.txt b/src/native/libs/CMakeLists.txt
index c15ca54cb10..577a6dee6b7 100644
--- a/src/native/libs/CMakeLists.txt
+++ b/src/native/libs/CMakeLists.txt
@@ -149,7 +149,7 @@ if (CLR_CMAKE_TARGET_UNIX OR CLR_CMAKE_TARGET_BROWSER)
add_subdirectory(System.Native)
if (CLR_CMAKE_TARGET_BROWSER)
- # skip for now
+ add_subdirectory(System.Security.Cryptography.Native.Browser)
elseif (CLR_CMAKE_TARGET_MACCATALYST)
add_subdirectory(System.Net.Security.Native)
# System.Security.Cryptography.Native is intentionally disabled on iOS
diff --git a/src/native/libs/System.Security.Cryptography.Native.Browser/CMakeLists.txt b/src/native/libs/System.Security.Cryptography.Native.Browser/CMakeLists.txt
new file mode 100644
index 00000000000..c411aa9ee9c
--- /dev/null
+++ b/src/native/libs/System.Security.Cryptography.Native.Browser/CMakeLists.txt
@@ -0,0 +1,14 @@
+project(System.Security.Cryptography.Native.Browser C)
+
+set (NATIVE_SOURCES
+ pal_crypto_webworker.c
+)
+
+add_library (System.Security.Cryptography.Native.Browser-Static
+ STATIC
+ ${NATIVE_SOURCES}
+)
+
+set_target_properties(System.Security.Cryptography.Native.Browser-Static PROPERTIES OUTPUT_NAME System.Security.Cryptography.Native.Browser CLEAN_DIRECT_OUTPUT 1)
+
+install (TARGETS System.Security.Cryptography.Native.Browser-Static DESTINATION ${STATIC_LIB_DESTINATION})
diff --git a/src/native/libs/System.Security.Cryptography.Native.Browser/pal_browser.h b/src/native/libs/System.Security.Cryptography.Native.Browser/pal_browser.h
new file mode 100644
index 00000000000..775fe634536
--- /dev/null
+++ b/src/native/libs/System.Security.Cryptography.Native.Browser/pal_browser.h
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#pragma once
+
+#include <emscripten.h>
+
+#ifndef __EMSCRIPTEN__
+#error Cryptography Native Browser is designed to be compiled with Emscripten.
+#endif // __EMSCRIPTEN__
+
+#ifndef PALEXPORT
+#ifdef TARGET_UNIX
+#define PALEXPORT __attribute__ ((__visibility__ ("default")))
+#else
+#define PALEXPORT __declspec(dllexport)
+#endif
+#endif // PALEXPORT
diff --git a/src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.c b/src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.c
new file mode 100644
index 00000000000..5f4da5a9862
--- /dev/null
+++ b/src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.c
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "pal_browser.h"
+#include "pal_crypto_webworker.h"
+
+// Forward declarations
+extern int32_t dotnet_browser_simple_digest_hash(
+ enum simple_digest ver,
+ uint8_t* input_buffer,
+ int32_t input_len,
+ uint8_t* output_buffer,
+ int32_t output_len);
+
+extern int32_t dotnet_browser_can_use_simple_digest_hash(void);
+
+int32_t SystemCryptoNativeBrowser_SimpleDigestHash(
+ enum simple_digest ver,
+ uint8_t* input_buffer,
+ int32_t input_len,
+ uint8_t* output_buffer,
+ int32_t output_len)
+{
+ return dotnet_browser_simple_digest_hash(ver, input_buffer, input_len, output_buffer, output_len);
+}
+
+int32_t SystemCryptoNativeBrowser_CanUseSimpleDigestHash(void)
+{
+ return dotnet_browser_can_use_simple_digest_hash();
+}
diff --git a/src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.h b/src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.h
new file mode 100644
index 00000000000..fe8b4d2762b
--- /dev/null
+++ b/src/native/libs/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.h
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#pragma once
+
+#include <stdint.h>
+
+// These values are also defined in the System.Security.Cryptography library's
+// browser-crypto implementation, and utilized in the dotnet-crypto-worker in the wasm runtime.
+enum simple_digest
+{
+ sd_sha_1,
+ sd_sha_256,
+ sd_sha_384,
+ sd_sha_512,
+};
+
+PALEXPORT int32_t SystemCryptoNativeBrowser_SimpleDigestHash(
+ enum simple_digest ver,
+ uint8_t* input_buffer,
+ int32_t input_len,
+ uint8_t* output_buffer,
+ int32_t output_len);
+
+PALEXPORT int32_t SystemCryptoNativeBrowser_CanUseSimpleDigestHash(void);
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs
index 722d5246cc9..6c11e076aba 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs
@@ -156,7 +156,7 @@ namespace Wasm.Build.Tests
{
RunHost.V8 => ("wasm test", "--js-file=test-main.js --engine=V8 -v trace"),
RunHost.NodeJS => ("wasm test", "--js-file=test-main.js --engine=NodeJS -v trace"),
- _ => ("wasm test-browser", $"-v trace -b {host}")
+ _ => ("wasm test-browser", $"-v trace -b {host} --web-server-use-cop")
};
string testLogPath = Path.Combine(_logPath, host.ToString());
@@ -509,7 +509,8 @@ namespace Wasm.Build.Tests
"dotnet.timezones.blat",
"dotnet.wasm",
"mono-config.json",
- "dotnet.js"
+ "dotnet.js",
+ "dotnet-crypto-worker.js"
});
AssertFilesExist(bundleDir, new[] { "run-v8.sh" }, expectToExist: hasV8Script);
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs
index c659ce90739..e3e013b55f4 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs
@@ -93,5 +93,58 @@ public class Test
Assert.Contains("Size: 26462 Height: 599, Width: 499", output);
}
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [BuildAndRun(aot: false)]
+ [BuildAndRun(aot: true)]
+ public void ProjectUsingBrowserNativeCrypto(BuildArgs buildArgs, RunHost host, string id)
+ {
+ string projectName = $"AppUsingBrowserNativeCrypto";
+ buildArgs = buildArgs with { ProjectName = projectName };
+ buildArgs = ExpandBuildArgs(buildArgs);
+
+ string programText = @"
+using System;
+using System.Security.Cryptography;
+
+public class Test
+{
+ public static int Main()
+ {
+ using (SHA256 mySHA256 = SHA256.Create())
+ {
+ byte[] data = { (byte)'H', (byte)'e', (byte)'l', (byte)'l', (byte)'o' };
+ byte[] hashed = mySHA256.ComputeHash(data);
+ string asStr = string.Join(' ', hashed);
+ Console.WriteLine(""Hashed: "" + asStr);
+ return 0;
+ }
+ }
+}";
+
+ BuildProject(buildArgs,
+ id: id,
+ new BuildProjectOptions(
+ InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText),
+ DotnetWasmFromRuntimePack: !buildArgs.AOT && buildArgs.Config != "Release"));
+
+ string output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 0,
+ test: output => {},
+ host: host, id: id);
+
+ Assert.Contains(
+ "Hashed: 24 95 141 179 34 113 254 37 245 97 166 252 147 139 46 38 67 6 236 48 78 218 81 128 7 209 118 72 38 56 25 105",
+ output);
+
+ string cryptoInitMsg = "MONO_WASM: Initializing Crypto WebWorker";
+ if (host == RunHost.V8 || host == RunHost.NodeJS)
+ {
+ Assert.DoesNotContain(cryptoInitMsg, output);
+ }
+ else
+ {
+ Assert.Contains(cryptoInitMsg, output);
+ }
+ }
}
}