diff --git a/eng/SignCheckExclusionsFile.txt b/eng/SignCheckExclusionsFile.txt index b45e2daaceb8d..8316fedacad43 100644 --- a/eng/SignCheckExclusionsFile.txt +++ b/eng/SignCheckExclusionsFile.txt @@ -13,3 +13,4 @@ *comhosttemplatecomhostdll.dll;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 *staticapphosttemplateapphostexe.exe;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 *dotnet.js;;Workaround, https://github.com/dotnet/core-eng/issues/9933 +*dotnet_crypto_worker.js;;Workaround, https://github.com/dotnet/core-eng/issues/9933 diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets index 66da71ac53f75..117837a1676b5 100644 --- a/eng/liveBuilds.targets +++ b/eng/liveBuilds.targets @@ -169,6 +169,7 @@ $(LibrariesNativeArtifactsPath)dotnet.js; $(LibrariesNativeArtifactsPath)dotnet.wasm; $(LibrariesNativeArtifactsPath)dotnet.timezones.blat; + $(LibrariesNativeArtifactsPath)dotnet_crypto_worker.js; $(LibrariesNativeArtifactsPath)*.dat;" IsNative="true" /> + @@ -201,6 +202,7 @@ + @@ -210,7 +212,9 @@ + + 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 0000000000000..b28d723f0bfc9 --- /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 0000000000000..a5e5a4765961b --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/System.Security.Cryptography.Native.Browser/Interop.SimpleDigestHash.cs @@ -0,0 +1,28 @@ +// 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 + { + internal enum SimpleDigest + { + Sha1, + Sha256, + Sha384, + Sha512, + }; + + [DllImport(Libraries.CryptoNative, EntryPoint = "SystemCryptoNativeBrowser_SimpleDigestHash")] + internal static extern unsafe int SimpleDigestHash( + SimpleDigest hash, + byte* input_buffer, + int input_len, + byte* output_buffer, + int output_len); + } +} diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 34c4bb86c37de..261f49278a2d1 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -62,6 +62,9 @@ public static partial class PlatformDetection public static bool IsUsingLimitedCultures => !IsNotMobile; public static bool IsNotUsingLimitedCultures => IsNotMobile; + public static bool IsPlatformCryptoSupported => !IsBrowser || IsBrowserDomSupported; + public static bool IsNotPlatformCryptoSupported => !IsPlatformCryptoSupported; + // Please make sure that you have the libgdiplus dependency installed. // For details, see https://docs.microsoft.com/dotnet/core/install/dependencies?pivots=os-macos&tabs=netcore31#libgdiplus public static bool IsDrawingSupported diff --git a/src/libraries/Native/AnyOS/System.Security.Cryptography.Native.Browser/pal_browser.h b/src/libraries/Native/AnyOS/System.Security.Cryptography.Native.Browser/pal_browser.h new file mode 100644 index 0000000000000..775fe634536e2 --- /dev/null +++ b/src/libraries/Native/AnyOS/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 + +#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/libraries/Native/AnyOS/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.c b/src/libraries/Native/AnyOS/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.c new file mode 100644 index 0000000000000..50adb4d84af70 --- /dev/null +++ b/src/libraries/Native/AnyOS/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.c @@ -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. + +#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); + +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); +} diff --git a/src/libraries/Native/AnyOS/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.h b/src/libraries/Native/AnyOS/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.h new file mode 100644 index 0000000000000..93dae44653db6 --- /dev/null +++ b/src/libraries/Native/AnyOS/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.h @@ -0,0 +1,21 @@ +// 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 + +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); diff --git a/src/libraries/Native/AnyOS/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.js b/src/libraries/Native/AnyOS/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.js new file mode 100644 index 0000000000000..a617fc08182c1 --- /dev/null +++ b/src/libraries/Native/AnyOS/System.Security.Cryptography.Native.Browser/pal_crypto_webworker.js @@ -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. + +var CryptoWebWorkerLib = { + $CRYPTOWEBWORKER: { + call_digest: function (hash, input_buffer, input_len, output_buffer, output_len) { + if (MONO.mono_wasm_crypto.channel === null) { + return 0; // Not supported + } + + var msg = { + func: "digest", + type: hash, + data: Array.from(Module.HEAPU8.subarray (input_buffer, input_buffer + input_len)) + }; + var response = MONO.mono_wasm_crypto.channel.send_msg (JSON.stringify (msg)); + var digest = JSON.parse (response); + if (digest.length > output_len) { + throw "DIGEST HASH: Digest length exceeds output length: " + digest.length + " > " + output_len; + } + + Module.HEAPU8.set (digest, output_buffer); + return 1; + } + }, + dotnet_browser_simple_digest_hash: function (hash, input_buffer, input_len, output_buffer, output_len) { + return CRYPTOWEBWORKER.call_digest (hash, input_buffer, input_len, output_buffer, output_len); + }, +}; + +autoAddDeps(CryptoWebWorkerLib, '$CRYPTOWEBWORKER') +mergeInto(LibraryManager.library, CryptoWebWorkerLib) diff --git a/src/libraries/Native/Unix/CMakeLists.txt b/src/libraries/Native/Unix/CMakeLists.txt index 8ebce76af457d..f95e7bccfcee2 100644 --- a/src/libraries/Native/Unix/CMakeLists.txt +++ b/src/libraries/Native/Unix/CMakeLists.txt @@ -251,7 +251,7 @@ endif() 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/libraries/Native/Unix/System.Security.Cryptography.Native.Browser/CMakeLists.txt b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Browser/CMakeLists.txt new file mode 100644 index 0000000000000..8005bbceadee1 --- /dev/null +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Browser/CMakeLists.txt @@ -0,0 +1,17 @@ +project(System.Security.Cryptography.Native.Browser C) + +set(ANYOS_SOURCES_DIR ../../AnyOS/System.Security.Cryptography.Native.Browser) +include_directories("${ANYOS_SOURCES_DIR}") + +set (NATIVE_SOURCES + ${ANYOS_SOURCES_DIR}/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}) \ No newline at end of file diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/SHAHashProvider.Browser.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/SHAHashProvider.Browser.cs index dff66e5ab8b61..ce590b6b87f75 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/SHAHashProvider.Browser.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/SHAHashProvider.Browser.cs @@ -11,8 +11,8 @@ namespace Internal.Cryptography { internal sealed class SHAHashProvider : HashProvider { - private int hashSizeInBytes; - private SHAManagedImplementationBase impl; + private readonly int hashSizeInBytes; + private readonly Interop.BrowserCrypto.SimpleDigest impl; private MemoryStream buffer; public SHAHashProvider(string hashAlgorithmId) @@ -20,19 +20,19 @@ public SHAHashProvider(string hashAlgorithmId) switch (hashAlgorithmId) { case HashAlgorithmNames.SHA1: - impl = new SHA1ManagedImplementation(); + impl = Interop.BrowserCrypto.SimpleDigest.Sha1; hashSizeInBytes = 20; break; case HashAlgorithmNames.SHA256: - impl = new SHA256ManagedImplementation(); + impl = Interop.BrowserCrypto.SimpleDigest.Sha256; hashSizeInBytes = 32; break; case HashAlgorithmNames.SHA384: - impl = new SHA384ManagedImplementation(); + impl = Interop.BrowserCrypto.SimpleDigest.Sha384; hashSizeInBytes = 48; break; case HashAlgorithmNames.SHA512: - impl = new SHA512ManagedImplementation(); + impl = Interop.BrowserCrypto.SimpleDigest.Sha512; hashSizeInBytes = 64; break; default: @@ -62,965 +62,34 @@ public override int GetCurrentHash(Span destination) { Debug.Assert(destination.Length >= hashSizeInBytes); - impl.Initialize(); + byte[] srcArray = Array.Empty(); + int srcLength = 0; if (buffer != null) { - impl.HashCore(buffer.GetBuffer(), 0, (int)buffer.Length); + srcArray = buffer.GetBuffer(); + srcLength = (int)buffer.Length; } - impl.HashFinal().CopyTo(destination); - return hashSizeInBytes; - } - - public override int HashSizeInBytes => hashSizeInBytes; - - public override void Dispose(bool disposing) - { - } - - private abstract class SHAManagedImplementationBase - { - public abstract void Initialize(); - public abstract void HashCore(byte[] partIn, int ibStart, int cbSize); - public abstract byte[] HashFinal(); - } - - private sealed class SHA1ManagedImplementation : SHAManagedImplementationBase - { - // It's ok to use a "non-secret purposes" hashing implementation here, as this is only - // used in wasm scenarios, and as of the current release we don't make any security guarantees - // about our crypto primitives in wasm environments. - private Sha1ForNonSecretPurposes _state; // mutable struct - don't make readonly - - public override void Initialize() - { - _state = default; - _state.Start(); - } - - public override void HashCore(byte[] partIn, int ibStart, int cbSize) - { - _state.Append(partIn.AsSpan(ibStart, cbSize)); - } - - public override byte[] HashFinal() - { - byte[] output = new byte[20]; - _state.Finish(output); - return output; - } - } - - // ported from https://github.com/microsoft/referencesource/blob/a48449cb48a9a693903668a71449ac719b76867c/mscorlib/system/security/cryptography/sha256managed.cs - private sealed class SHA256ManagedImplementation : SHAManagedImplementationBase - { - private byte[] _buffer; - private long _count; // Number of bytes in the hashed message - private uint[] _stateSHA256; - private uint[] _W; - - public SHA256ManagedImplementation() - { - _stateSHA256 = new uint[8]; - _buffer = new byte[64]; - _W = new uint[64]; - - InitializeState(); - } - - public override void Initialize() + unsafe { - InitializeState(); - - // Zeroize potentially sensitive information. - Array.Clear(_buffer, 0, _buffer.Length); - Array.Clear(_W, 0, _W.Length); - } - - private void InitializeState() - { - _count = 0; - - _stateSHA256[0] = 0x6a09e667; - _stateSHA256[1] = 0xbb67ae85; - _stateSHA256[2] = 0x3c6ef372; - _stateSHA256[3] = 0xa54ff53a; - _stateSHA256[4] = 0x510e527f; - _stateSHA256[5] = 0x9b05688c; - _stateSHA256[6] = 0x1f83d9ab; - _stateSHA256[7] = 0x5be0cd19; - } - - /* SHA256 block update operation. Continues an SHA message-digest - operation, processing another message block, and updating the - context. - */ - public override unsafe void HashCore(byte[] partIn, int ibStart, int cbSize) - { - int bufferLen; - int partInLen = cbSize; - int partInBase = ibStart; - - /* Compute length of buffer */ - bufferLen = (int)(_count & 0x3f); - - /* Update number of bytes */ - _count += partInLen; - - fixed (uint* stateSHA256 = _stateSHA256) + fixed (byte* src = srcArray) + fixed (byte* dest = destination) { - fixed (byte* buffer = _buffer) + int res = Interop.BrowserCrypto.SimpleDigestHash(impl, src, srcLength, dest, destination.Length); + if (res == 0) { - fixed (uint* expandedBuffer = _W) - { - if ((bufferLen > 0) && (bufferLen + partInLen >= 64)) - { - Buffer.BlockCopy(partIn, partInBase, _buffer, bufferLen, 64 - bufferLen); - partInBase += (64 - bufferLen); - partInLen -= (64 - bufferLen); - SHATransform(expandedBuffer, stateSHA256, buffer); - bufferLen = 0; - } - - /* Copy input to temporary buffer and hash */ - while (partInLen >= 64) - { - Buffer.BlockCopy(partIn, partInBase, _buffer, 0, 64); - partInBase += 64; - partInLen -= 64; - SHATransform(expandedBuffer, stateSHA256, buffer); - } - - if (partInLen > 0) - { - Buffer.BlockCopy(partIn, partInBase, _buffer, bufferLen, partInLen); - } - } + throw new PlatformNotSupportedException(SR.SystemSecurityCryptographyAlgorithms_PlatformNotSupported); } } } - /* SHA256 finalization. Ends an SHA256 message-digest operation, writing - the message digest. - */ - public override byte[] HashFinal() - { - byte[] pad; - int padLen; - long bitCount; - byte[] hash = new byte[32]; // HashSizeValue = 256 - - /* Compute padding: 80 00 00 ... 00 00 - */ - - padLen = 64 - (int)(_count & 0x3f); - if (padLen <= 8) - padLen += 64; - - pad = new byte[padLen]; - pad[0] = 0x80; - - // Convert count to bit count - bitCount = _count * 8; - - pad[padLen - 8] = (byte)((bitCount >> 56) & 0xff); - pad[padLen - 7] = (byte)((bitCount >> 48) & 0xff); - pad[padLen - 6] = (byte)((bitCount >> 40) & 0xff); - pad[padLen - 5] = (byte)((bitCount >> 32) & 0xff); - pad[padLen - 4] = (byte)((bitCount >> 24) & 0xff); - pad[padLen - 3] = (byte)((bitCount >> 16) & 0xff); - pad[padLen - 2] = (byte)((bitCount >> 8) & 0xff); - pad[padLen - 1] = (byte)((bitCount >> 0) & 0xff); - - /* Digest padding */ - HashCore(pad, 0, pad.Length); - - /* Store digest */ - SHAUtils.DWORDToBigEndian(hash, _stateSHA256, 8); - - return hash; - } - - private static readonly uint[] _K = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 - }; - - private static unsafe void SHATransform(uint* expandedBuffer, uint* state, byte* block) - { - uint a, b, c, d, e, f, h, g; - uint aa, bb, cc, dd, ee, ff, hh, gg; - uint T1; - - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - f = state[5]; - g = state[6]; - h = state[7]; - - // fill in the first 16 bytes of W. - SHAUtils.DWORDFromBigEndian(expandedBuffer, 16, block); - SHA256Expand(expandedBuffer); - - /* Apply the SHA256 compression function */ - // We are trying to be smart here and avoid as many copies as we can - // The perf gain with this method over the straightforward modify and shift - // forward is >= 20%, so it's worth the pain - for (int j = 0; j < 64;) - { - T1 = h + Sigma_1(e) + Ch(e, f, g) + _K[j] + expandedBuffer[j]; - ee = d + T1; - aa = T1 + Sigma_0(a) + Maj(a, b, c); - j++; - - T1 = g + Sigma_1(ee) + Ch(ee, e, f) + _K[j] + expandedBuffer[j]; - ff = c + T1; - bb = T1 + Sigma_0(aa) + Maj(aa, a, b); - j++; - - T1 = f + Sigma_1(ff) + Ch(ff, ee, e) + _K[j] + expandedBuffer[j]; - gg = b + T1; - cc = T1 + Sigma_0(bb) + Maj(bb, aa, a); - j++; - - T1 = e + Sigma_1(gg) + Ch(gg, ff, ee) + _K[j] + expandedBuffer[j]; - hh = a + T1; - dd = T1 + Sigma_0(cc) + Maj(cc, bb, aa); - j++; - - T1 = ee + Sigma_1(hh) + Ch(hh, gg, ff) + _K[j] + expandedBuffer[j]; - h = aa + T1; - d = T1 + Sigma_0(dd) + Maj(dd, cc, bb); - j++; - - T1 = ff + Sigma_1(h) + Ch(h, hh, gg) + _K[j] + expandedBuffer[j]; - g = bb + T1; - c = T1 + Sigma_0(d) + Maj(d, dd, cc); - j++; - - T1 = gg + Sigma_1(g) + Ch(g, h, hh) + _K[j] + expandedBuffer[j]; - f = cc + T1; - b = T1 + Sigma_0(c) + Maj(c, d, dd); - j++; - - T1 = hh + Sigma_1(f) + Ch(f, g, h) + _K[j] + expandedBuffer[j]; - e = dd + T1; - a = T1 + Sigma_0(b) + Maj(b, c, d); - j++; - } - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - state[5] += f; - state[6] += g; - state[7] += h; - } - - private static uint RotateRight(uint x, int n) - { - return (((x) >> (n)) | ((x) << (32 - (n)))); - } - - private static uint Ch(uint x, uint y, uint z) - { - return ((x & y) ^ ((x ^ 0xffffffff) & z)); - } - - private static uint Maj(uint x, uint y, uint z) - { - return ((x & y) ^ (x & z) ^ (y & z)); - } - - private static uint sigma_0(uint x) - { - return (RotateRight(x, 7) ^ RotateRight(x, 18) ^ (x >> 3)); - } - - private static uint sigma_1(uint x) - { - return (RotateRight(x, 17) ^ RotateRight(x, 19) ^ (x >> 10)); - } - - private static uint Sigma_0(uint x) - { - return (RotateRight(x, 2) ^ RotateRight(x, 13) ^ RotateRight(x, 22)); - } - - private static uint Sigma_1(uint x) - { - return (RotateRight(x, 6) ^ RotateRight(x, 11) ^ RotateRight(x, 25)); - } - - /* This function creates W_16,...,W_63 according to the formula - W_j <- sigma_1(W_{j-2}) + W_{j-7} + sigma_0(W_{j-15}) + W_{j-16}; - */ - private static unsafe void SHA256Expand(uint* x) - { - for (int i = 16; i < 64; i++) - { - x[i] = sigma_1(x[i - 2]) + x[i - 7] + sigma_0(x[i - 15]) + x[i - 16]; - } - } - } - - // ported from https://github.com/microsoft/referencesource/blob/a48449cb48a9a693903668a71449ac719b76867c/mscorlib/system/security/cryptography/sha384managed.cs - private sealed class SHA384ManagedImplementation : SHAManagedImplementationBase - { - private byte[] _buffer; - private ulong _count; // Number of bytes in the hashed message - private ulong[] _stateSHA384; - private ulong[] _W; - - public SHA384ManagedImplementation() - { - _stateSHA384 = new ulong[8]; - _buffer = new byte[128]; - _W = new ulong[80]; - - InitializeState(); - } - - public override void Initialize() - { - InitializeState(); - - // Zeroize potentially sensitive information. - Array.Clear(_buffer, 0, _buffer.Length); - Array.Clear(_W, 0, _W.Length); - } - - private void InitializeState() - { - _count = 0; - - _stateSHA384[0] = 0xcbbb9d5dc1059ed8; - _stateSHA384[1] = 0x629a292a367cd507; - _stateSHA384[2] = 0x9159015a3070dd17; - _stateSHA384[3] = 0x152fecd8f70e5939; - _stateSHA384[4] = 0x67332667ffc00b31; - _stateSHA384[5] = 0x8eb44a8768581511; - _stateSHA384[6] = 0xdb0c2e0d64f98fa7; - _stateSHA384[7] = 0x47b5481dbefa4fa4; - } - - /* SHA384 block update operation. Continues an SHA message-digest - operation, processing another message block, and updating the - context. - */ - public override unsafe void HashCore(byte[] partIn, int ibStart, int cbSize) - { - int bufferLen; - int partInLen = cbSize; - int partInBase = ibStart; - - /* Compute length of buffer */ - bufferLen = (int)(_count & 0x7f); - - /* Update number of bytes */ - _count += (ulong)partInLen; - - fixed (ulong* stateSHA384 = _stateSHA384) - { - fixed (byte* buffer = _buffer) - { - fixed (ulong* expandedBuffer = _W) - { - if ((bufferLen > 0) && (bufferLen + partInLen >= 128)) - { - Buffer.BlockCopy(partIn, partInBase, _buffer, bufferLen, 128 - bufferLen); - partInBase += (128 - bufferLen); - partInLen -= (128 - bufferLen); - SHATransform(expandedBuffer, stateSHA384, buffer); - bufferLen = 0; - } - - /* Copy input to temporary buffer and hash */ - while (partInLen >= 128) - { - Buffer.BlockCopy(partIn, partInBase, _buffer, 0, 128); - partInBase += 128; - partInLen -= 128; - SHATransform(expandedBuffer, stateSHA384, buffer); - } - - if (partInLen > 0) - { - Buffer.BlockCopy(partIn, partInBase, _buffer, bufferLen, partInLen); - } - } - } - } - } - - /* SHA384 finalization. Ends an SHA384 message-digest operation, writing - the message digest. - */ - public override byte[] HashFinal() - { - byte[] pad; - int padLen; - ulong bitCount; - byte[] hash = new byte[48]; // HashSizeValue = 384 - - /* Compute padding: 80 00 00 ... 00 00 - */ - - padLen = 128 - (int)(_count & 0x7f); - if (padLen <= 16) - padLen += 128; - - pad = new byte[padLen]; - pad[0] = 0x80; - - // Convert count to bit count - bitCount = _count * 8; - - // bitCount is at most 8 * 128 = 1024. Its representation as a 128-bit number has all bits set to zero - // except eventually the 11 lower bits - - //pad[padLen-16] = (byte) ((bitCount >> 120) & 0xff); - //pad[padLen-15] = (byte) ((bitCount >> 112) & 0xff); - //pad[padLen-14] = (byte) ((bitCount >> 104) & 0xff); - //pad[padLen-13] = (byte) ((bitCount >> 96) & 0xff); - //pad[padLen-12] = (byte) ((bitCount >> 88) & 0xff); - //pad[padLen-11] = (byte) ((bitCount >> 80) & 0xff); - //pad[padLen-10] = (byte) ((bitCount >> 72) & 0xff); - //pad[padLen-9] = (byte) ((bitCount >> 64) & 0xff); - pad[padLen - 8] = (byte)((bitCount >> 56) & 0xff); - pad[padLen - 7] = (byte)((bitCount >> 48) & 0xff); - pad[padLen - 6] = (byte)((bitCount >> 40) & 0xff); - pad[padLen - 5] = (byte)((bitCount >> 32) & 0xff); - pad[padLen - 4] = (byte)((bitCount >> 24) & 0xff); - pad[padLen - 3] = (byte)((bitCount >> 16) & 0xff); - pad[padLen - 2] = (byte)((bitCount >> 8) & 0xff); - pad[padLen - 1] = (byte)((bitCount >> 0) & 0xff); - - /* Digest padding */ - HashCore(pad, 0, pad.Length); - - /* Store digest */ - SHAUtils.QuadWordToBigEndian(hash, _stateSHA384, 6); - - return hash; - } - - private static readonly ulong[] _K = { - 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, - 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, - 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, - 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, - 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, - 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, - 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, - 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, - 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, - 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, - 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, - 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, - 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, - 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, - 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, - 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, - 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, - 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, - 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, - 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817, - }; - - private static unsafe void SHATransform(ulong* expandedBuffer, ulong* state, byte* block) - { - ulong a, b, c, d, e, f, g, h; - ulong aa, bb, cc, dd, ee, ff, hh, gg; - ulong T1; - - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - f = state[5]; - g = state[6]; - h = state[7]; - - // fill in the first 16 blocks of W. - SHAUtils.QuadWordFromBigEndian(expandedBuffer, 16, block); - SHA384Expand(expandedBuffer); - - /* Apply the SHA384 compression function */ - // We are trying to be smart here and avoid as many copies as we can - // The perf gain with this method over the straightforward modify and shift - // forward is >= 20%, so it's worth the pain - for (int j = 0; j < 80;) - { - T1 = h + Sigma_1(e) + Ch(e, f, g) + _K[j] + expandedBuffer[j]; - ee = d + T1; - aa = T1 + Sigma_0(a) + Maj(a, b, c); - j++; - - T1 = g + Sigma_1(ee) + Ch(ee, e, f) + _K[j] + expandedBuffer[j]; - ff = c + T1; - bb = T1 + Sigma_0(aa) + Maj(aa, a, b); - j++; - - T1 = f + Sigma_1(ff) + Ch(ff, ee, e) + _K[j] + expandedBuffer[j]; - gg = b + T1; - cc = T1 + Sigma_0(bb) + Maj(bb, aa, a); - j++; - - T1 = e + Sigma_1(gg) + Ch(gg, ff, ee) + _K[j] + expandedBuffer[j]; - hh = a + T1; - dd = T1 + Sigma_0(cc) + Maj(cc, bb, aa); - j++; - - T1 = ee + Sigma_1(hh) + Ch(hh, gg, ff) + _K[j] + expandedBuffer[j]; - h = aa + T1; - d = T1 + Sigma_0(dd) + Maj(dd, cc, bb); - j++; - - T1 = ff + Sigma_1(h) + Ch(h, hh, gg) + _K[j] + expandedBuffer[j]; - g = bb + T1; - c = T1 + Sigma_0(d) + Maj(d, dd, cc); - j++; - - T1 = gg + Sigma_1(g) + Ch(g, h, hh) + _K[j] + expandedBuffer[j]; - f = cc + T1; - b = T1 + Sigma_0(c) + Maj(c, d, dd); - j++; - - T1 = hh + Sigma_1(f) + Ch(f, g, h) + _K[j] + expandedBuffer[j]; - e = dd + T1; - a = T1 + Sigma_0(b) + Maj(b, c, d); - j++; - } - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - state[5] += f; - state[6] += g; - state[7] += h; - } - - private static ulong RotateRight(ulong x, int n) - { - return (((x) >> (n)) | ((x) << (64 - (n)))); - } - - private static ulong Ch(ulong x, ulong y, ulong z) - { - return ((x & y) ^ ((x ^ 0xffffffffffffffff) & z)); - } - - private static ulong Maj(ulong x, ulong y, ulong z) - { - return ((x & y) ^ (x & z) ^ (y & z)); - } - - private static ulong Sigma_0(ulong x) - { - return (RotateRight(x, 28) ^ RotateRight(x, 34) ^ RotateRight(x, 39)); - } - - private static ulong Sigma_1(ulong x) - { - return (RotateRight(x, 14) ^ RotateRight(x, 18) ^ RotateRight(x, 41)); - } - - private static ulong sigma_0(ulong x) - { - return (RotateRight(x, 1) ^ RotateRight(x, 8) ^ (x >> 7)); - } - - private static ulong sigma_1(ulong x) - { - return (RotateRight(x, 19) ^ RotateRight(x, 61) ^ (x >> 6)); - } - - /* This function creates W_16,...,W_79 according to the formula - W_j <- sigma_1(W_{j-2}) + W_{j-7} + sigma_0(W_{j-15}) + W_{j-16}; - */ - private static unsafe void SHA384Expand(ulong* x) - { - for (int i = 16; i < 80; i++) - { - x[i] = sigma_1(x[i - 2]) + x[i - 7] + sigma_0(x[i - 15]) + x[i - 16]; - } - } + return hashSizeInBytes; } - // ported from https://github.com/microsoft/referencesource/blob/a48449cb48a9a693903668a71449ac719b76867c/mscorlib/system/security/cryptography/sha512managed.cs - private sealed class SHA512ManagedImplementation : SHAManagedImplementationBase - { - private byte[] _buffer; - private ulong _count; // Number of bytes in the hashed message - private ulong[] _stateSHA512; - private ulong[] _W; - - public SHA512ManagedImplementation() - { - _stateSHA512 = new ulong[8]; - _buffer = new byte[128]; - _W = new ulong[80]; - - InitializeState(); - } - - public override void Initialize() - { - InitializeState(); - - // Zeroize potentially sensitive information. - Array.Clear(_buffer, 0, _buffer.Length); - Array.Clear(_W, 0, _W.Length); - } - - private void InitializeState() - { - _count = 0; - - _stateSHA512[0] = 0x6a09e667f3bcc908; - _stateSHA512[1] = 0xbb67ae8584caa73b; - _stateSHA512[2] = 0x3c6ef372fe94f82b; - _stateSHA512[3] = 0xa54ff53a5f1d36f1; - _stateSHA512[4] = 0x510e527fade682d1; - _stateSHA512[5] = 0x9b05688c2b3e6c1f; - _stateSHA512[6] = 0x1f83d9abfb41bd6b; - _stateSHA512[7] = 0x5be0cd19137e2179; - } - - /* SHA512 block update operation. Continues an SHA message-digest - operation, processing another message block, and updating the - context. - */ - public override unsafe void HashCore(byte[] partIn, int ibStart, int cbSize) - { - int bufferLen; - int partInLen = cbSize; - int partInBase = ibStart; - - /* Compute length of buffer */ - bufferLen = (int)(_count & 0x7f); - - /* Update number of bytes */ - _count += (ulong)partInLen; - - fixed (ulong* stateSHA512 = _stateSHA512) - { - fixed (byte* buffer = _buffer) - { - fixed (ulong* expandedBuffer = _W) - { - if ((bufferLen > 0) && (bufferLen + partInLen >= 128)) - { - Buffer.BlockCopy(partIn, partInBase, _buffer, bufferLen, 128 - bufferLen); - partInBase += (128 - bufferLen); - partInLen -= (128 - bufferLen); - SHATransform(expandedBuffer, stateSHA512, buffer); - bufferLen = 0; - } - - /* Copy input to temporary buffer and hash */ - while (partInLen >= 128) - { - Buffer.BlockCopy(partIn, partInBase, _buffer, 0, 128); - partInBase += 128; - partInLen -= 128; - SHATransform(expandedBuffer, stateSHA512, buffer); - } - - if (partInLen > 0) - { - Buffer.BlockCopy(partIn, partInBase, _buffer, bufferLen, partInLen); - } - } - } - } - } - - /* SHA512 finalization. Ends an SHA512 message-digest operation, writing - the message digest. - */ - public override byte[] HashFinal() - { - byte[] pad; - int padLen; - ulong bitCount; - byte[] hash = new byte[64]; // HashSizeValue = 512 - - /* Compute padding: 80 00 00 ... 00 00 - */ - - padLen = 128 - (int)(_count & 0x7f); - if (padLen <= 16) - padLen += 128; - - pad = new byte[padLen]; - pad[0] = 0x80; - - // Convert count to bit count - bitCount = _count * 8; - - // If we ever have UInt128 for bitCount, then these need to be uncommented. - // Note that C# only looks at the low 6 bits of the shift value for ulongs, - // so >>0 and >>64 are equal! - - //pad[padLen-16] = (byte) ((bitCount >> 120) & 0xff); - //pad[padLen-15] = (byte) ((bitCount >> 112) & 0xff); - //pad[padLen-14] = (byte) ((bitCount >> 104) & 0xff); - //pad[padLen-13] = (byte) ((bitCount >> 96) & 0xff); - //pad[padLen-12] = (byte) ((bitCount >> 88) & 0xff); - //pad[padLen-11] = (byte) ((bitCount >> 80) & 0xff); - //pad[padLen-10] = (byte) ((bitCount >> 72) & 0xff); - //pad[padLen-9] = (byte) ((bitCount >> 64) & 0xff); - pad[padLen - 8] = (byte)((bitCount >> 56) & 0xff); - pad[padLen - 7] = (byte)((bitCount >> 48) & 0xff); - pad[padLen - 6] = (byte)((bitCount >> 40) & 0xff); - pad[padLen - 5] = (byte)((bitCount >> 32) & 0xff); - pad[padLen - 4] = (byte)((bitCount >> 24) & 0xff); - pad[padLen - 3] = (byte)((bitCount >> 16) & 0xff); - pad[padLen - 2] = (byte)((bitCount >> 8) & 0xff); - pad[padLen - 1] = (byte)((bitCount >> 0) & 0xff); - - /* Digest padding */ - HashCore(pad, 0, pad.Length); - - /* Store digest */ - SHAUtils.QuadWordToBigEndian(hash, _stateSHA512, 8); - - return hash; - } - - private static readonly ulong[] _K = { - 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, - 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, - 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, - 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, - 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, - 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, - 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, - 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, - 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, - 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, - 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, - 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, - 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, - 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, - 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, - 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, - 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, - 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, - 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, - 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817, - }; - - private static unsafe void SHATransform(ulong* expandedBuffer, ulong* state, byte* block) - { - ulong a, b, c, d, e, f, g, h; - ulong aa, bb, cc, dd, ee, ff, hh, gg; - ulong T1; - - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - f = state[5]; - g = state[6]; - h = state[7]; - - // fill in the first 16 blocks of W. - SHAUtils.QuadWordFromBigEndian(expandedBuffer, 16, block); - SHA512Expand(expandedBuffer); - - /* Apply the SHA512 compression function */ - // We are trying to be smart here and avoid as many copies as we can - // The perf gain with this method over the straightforward modify and shift - // forward is >= 20%, so it's worth the pain - for (int j = 0; j < 80;) - { - T1 = h + Sigma_1(e) + Ch(e, f, g) + _K[j] + expandedBuffer[j]; - ee = d + T1; - aa = T1 + Sigma_0(a) + Maj(a, b, c); - j++; - - T1 = g + Sigma_1(ee) + Ch(ee, e, f) + _K[j] + expandedBuffer[j]; - ff = c + T1; - bb = T1 + Sigma_0(aa) + Maj(aa, a, b); - j++; - - T1 = f + Sigma_1(ff) + Ch(ff, ee, e) + _K[j] + expandedBuffer[j]; - gg = b + T1; - cc = T1 + Sigma_0(bb) + Maj(bb, aa, a); - j++; - - T1 = e + Sigma_1(gg) + Ch(gg, ff, ee) + _K[j] + expandedBuffer[j]; - hh = a + T1; - dd = T1 + Sigma_0(cc) + Maj(cc, bb, aa); - j++; - - T1 = ee + Sigma_1(hh) + Ch(hh, gg, ff) + _K[j] + expandedBuffer[j]; - h = aa + T1; - d = T1 + Sigma_0(dd) + Maj(dd, cc, bb); - j++; - - T1 = ff + Sigma_1(h) + Ch(h, hh, gg) + _K[j] + expandedBuffer[j]; - g = bb + T1; - c = T1 + Sigma_0(d) + Maj(d, dd, cc); - j++; - - T1 = gg + Sigma_1(g) + Ch(g, h, hh) + _K[j] + expandedBuffer[j]; - f = cc + T1; - b = T1 + Sigma_0(c) + Maj(c, d, dd); - j++; - - T1 = hh + Sigma_1(f) + Ch(f, g, h) + _K[j] + expandedBuffer[j]; - e = dd + T1; - a = T1 + Sigma_0(b) + Maj(b, c, d); - j++; - } - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - state[5] += f; - state[6] += g; - state[7] += h; - } - - private static ulong RotateRight(ulong x, int n) - { - return (((x) >> (n)) | ((x) << (64 - (n)))); - } - - private static ulong Ch(ulong x, ulong y, ulong z) - { - return ((x & y) ^ ((x ^ 0xffffffffffffffff) & z)); - } - - private static ulong Maj(ulong x, ulong y, ulong z) - { - return ((x & y) ^ (x & z) ^ (y & z)); - } - - private static ulong Sigma_0(ulong x) - { - return (RotateRight(x, 28) ^ RotateRight(x, 34) ^ RotateRight(x, 39)); - } - - private static ulong Sigma_1(ulong x) - { - return (RotateRight(x, 14) ^ RotateRight(x, 18) ^ RotateRight(x, 41)); - } - - private static ulong sigma_0(ulong x) - { - return (RotateRight(x, 1) ^ RotateRight(x, 8) ^ (x >> 7)); - } - - private static ulong sigma_1(ulong x) - { - return (RotateRight(x, 19) ^ RotateRight(x, 61) ^ (x >> 6)); - } - - /* This function creates W_16,...,W_79 according to the formula - W_j <- sigma_1(W_{j-2}) + W_{j-7} + sigma_0(W_{j-15}) + W_{j-16}; - */ - private static unsafe void SHA512Expand(ulong* x) - { - for (int i = 16; i < 80; i++) - { - x[i] = sigma_1(x[i - 2]) + x[i - 7] + sigma_0(x[i - 15]) + x[i - 16]; - } - } - } + public override int HashSizeInBytes => hashSizeInBytes; - // ported from https://github.com/microsoft/referencesource/blob/a48449cb48a9a693903668a71449ac719b76867c/mscorlib/system/security/cryptography/utils.cs - private static class SHAUtils + public override void Dispose(bool disposing) { - // digits == number of DWORDs - public static unsafe void DWORDFromBigEndian(uint* x, int digits, byte* block) - { - int i; - int j; - - for (i = 0, j = 0; i < digits; i++, j += 4) - x[i] = (uint)((block[j] << 24) | (block[j + 1] << 16) | (block[j + 2] << 8) | block[j + 3]); - } - - // encodes x (DWORD) into block (unsigned char), most significant byte first. - // digits == number of DWORDs - public static void DWORDToBigEndian(byte[] block, uint[] x, int digits) - { - int i; - int j; - - for (i = 0, j = 0; i < digits; i++, j += 4) - { - block[j] = (byte)((x[i] >> 24) & 0xff); - block[j + 1] = (byte)((x[i] >> 16) & 0xff); - block[j + 2] = (byte)((x[i] >> 8) & 0xff); - block[j + 3] = (byte)(x[i] & 0xff); - } - } - - // digits == number of QWORDs - public static unsafe void QuadWordFromBigEndian(ulong* x, int digits, byte* block) - { - int i; - int j; - - for (i = 0, j = 0; i < digits; i++, j += 8) - x[i] = ( - (((ulong)block[j]) << 56) | (((ulong)block[j + 1]) << 48) | - (((ulong)block[j + 2]) << 40) | (((ulong)block[j + 3]) << 32) | - (((ulong)block[j + 4]) << 24) | (((ulong)block[j + 5]) << 16) | - (((ulong)block[j + 6]) << 8) | ((ulong)block[j + 7]) - ); - } - - // encodes x (DWORD) into block (unsigned char), most significant byte first. - // digits = number of QWORDS - public static void QuadWordToBigEndian(byte[] block, ulong[] x, int digits) - { - int i; - int j; - - for (i = 0, j = 0; i < digits; i++, j += 8) - { - block[j] = (byte)((x[i] >> 56) & 0xff); - block[j + 1] = (byte)((x[i] >> 48) & 0xff); - block[j + 2] = (byte)((x[i] >> 40) & 0xff); - block[j + 3] = (byte)((x[i] >> 32) & 0xff); - block[j + 4] = (byte)((x[i] >> 24) & 0xff); - block[j + 5] = (byte)((x[i] >> 16) & 0xff); - block[j + 6] = (byte)((x[i] >> 8) & 0xff); - block[j + 7] = (byte)(x[i] & 0xff); - } - } } } } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj b/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj index 6dbe4fe97575a..1b628074e716a 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj @@ -688,12 +688,15 @@ + - + + diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/IncrementalHashTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/IncrementalHashTests.cs index 2a0f551c27903..df469cd68ad90 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/IncrementalHashTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/IncrementalHashTests.cs @@ -68,7 +68,7 @@ public static void InvalidArguments_Throw() } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] [MemberData(nameof(GetHashAlgorithms))] public static void VerifyIncrementalHash(HashAlgorithm referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -142,7 +142,7 @@ private static void VerifyIncrementalResult(HashAlgorithm referenceAlgorithm, In Assert.Equal(referenceHash, incrementalB); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] [MemberData(nameof(GetHashAlgorithms))] public static void VerifyEmptyHash(HashAlgorithm referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -183,7 +183,7 @@ public static void VerifyEmptyHMAC(HMAC referenceAlgorithm, HashAlgorithmName ha } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] [MemberData(nameof(GetHashAlgorithms))] public static void VerifyTrivialHash(HashAlgorithm referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -214,7 +214,7 @@ public static void VerifyTrivialHMAC(HMAC referenceAlgorithm, HashAlgorithmName } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] public static void AppendDataAfterHashClose() { using (IncrementalHash hash = IncrementalHash.CreateHash(HashAlgorithmName.SHA256)) @@ -243,7 +243,7 @@ public static void AppendDataAfterHMACClose() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] public static void GetHashTwice() { using (IncrementalHash hash = IncrementalHash.CreateHash(HashAlgorithmName.SHA256)) @@ -268,7 +268,7 @@ public static void GetHMACTwice() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] public static void ModifyAfterHashDispose() { using (IncrementalHash hash = IncrementalHash.CreateHash(HashAlgorithmName.SHA256)) @@ -306,7 +306,7 @@ public static void UnknownHmacAlgorithm() () => IncrementalHash.CreateHMAC(new HashAlgorithmName("SHA0"), Array.Empty())); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] [MemberData(nameof(GetHashAlgorithms))] public static void VerifyIncrementalHash_Span(HashAlgorithm referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -374,7 +374,7 @@ private static void VerifyIncrementalResult_Span(HashAlgorithm referenceAlgorith Assert.Equal(new Span(referenceHash, 0, referenceHashLength).ToArray(), incrementalB); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] [MemberData(nameof(GetHashAlgorithms))] public static void VerifyEmptyHash_Span(HashAlgorithm referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -417,7 +417,7 @@ public static void VerifyEmptyHMAC_Span(HMAC referenceAlgorithm, HashAlgorithmNa } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] [MemberData(nameof(GetHashAlgorithms))] public static void VerifyTrivialHash_Span(HashAlgorithm referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -450,7 +450,7 @@ public static void VerifyTrivialHMAC_Span(HMAC referenceAlgorithm, HashAlgorithm } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] [MemberData(nameof(GetHashAlgorithms))] public static void Dispose_HashAlgorithm_ThrowsException(HashAlgorithm referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -497,7 +497,7 @@ public static void Dispose_HMAC_ThrowsException(HMAC referenceAlgorithm, HashAlg Assert.Throws(() => incrementalHash.TryGetCurrentHash(tmpDest, out int _)); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] [MemberData(nameof(GetHashAlgorithms))] public static void VerifyGetCurrentHash_Digest(HashAlgorithm referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -568,7 +568,7 @@ public static void VerifyBounds_GetHashAndReset_HMAC(HMAC referenceAlgorithm, Ha } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] [MemberData(nameof(GetHashAlgorithms))] public static void VerifyBounds_GetCurrentHash_Hash(HashAlgorithm referenceAlgorithm, HashAlgorithmName hashAlgorithm) { @@ -587,7 +587,7 @@ public static void VerifyBounds_GetCurrentHash_Hash(HashAlgorithm referenceAlgor } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] [MemberData(nameof(GetHashAlgorithms))] public static void VerifyBounds_GetHashAndReset_Hash(HashAlgorithm referenceAlgorithm, HashAlgorithmName hashAlgorithm) { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/InvalidUsageTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/InvalidUsageTests.cs index 0adfe66cef73e..0e65a2d10a52e 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/InvalidUsageTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/InvalidUsageTests.cs @@ -23,7 +23,7 @@ public void InvalidHashCoreArgumentsFromDerivedType() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] public void InvalidHashCoreArgumentsFromStream() { using (SHA1 sha1 = SHA1.Create()) diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha1ManagedTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha1ManagedTests.cs index 5139841dcf829..2a3d6b3b94232 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha1ManagedTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha1ManagedTests.cs @@ -1,11 +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; +using Xunit; + namespace System.Security.Cryptography.Hashing.Algorithms.Tests { /// /// Sha1Managed has a copy of the same implementation as SHA1 /// + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] public class Sha1ManagedTests : Sha1Tests { protected override HashAlgorithm Create() diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha1Tests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha1Tests.cs index 646d2dbb79801..2b17b644de29b 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha1Tests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha1Tests.cs @@ -1,10 +1,12 @@ // 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 Xunit; namespace System.Security.Cryptography.Hashing.Algorithms.Tests { + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] public class Sha1Tests : HashAlgorithmTest { protected override HashAlgorithm Create() diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha256ManagedTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha256ManagedTests.cs index b1a0422272e86..4779f7b9eaac1 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha256ManagedTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha256ManagedTests.cs @@ -1,11 +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; +using Xunit; + namespace System.Security.Cryptography.Hashing.Algorithms.Tests { /// /// Sha256Managed has a copy of the same implementation as SHA256 /// + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] public class Sha256ManagedTests : Sha256Tests { protected override HashAlgorithm Create() diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha256Tests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha256Tests.cs index c53a7e83f299e..6a53639003db8 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha256Tests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha256Tests.cs @@ -1,10 +1,12 @@ // 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 Xunit; namespace System.Security.Cryptography.Hashing.Algorithms.Tests { + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] public class Sha256Tests : HashAlgorithmTest { protected override HashAlgorithm Create() diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha384ManagedTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha384ManagedTests.cs index 42b13c63cd122..efaf85de6edb7 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha384ManagedTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha384ManagedTests.cs @@ -1,11 +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; +using Xunit; + namespace System.Security.Cryptography.Hashing.Algorithms.Tests { /// /// Sha384Managed has a copy of the same implementation as SHA384 /// + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] public class Sha384ManagedTests : Sha384Tests { protected override HashAlgorithm Create() diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha384Tests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha384Tests.cs index a0beb5d5faf1b..d2edea206dc2b 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha384Tests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha384Tests.cs @@ -1,10 +1,12 @@ // 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 Xunit; namespace System.Security.Cryptography.Hashing.Algorithms.Tests { + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] public class Sha384Tests : HashAlgorithmTest { protected override HashAlgorithm Create() diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha512ManagedTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha512ManagedTests.cs index b3fae310692c0..0ef094644bb6c 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha512ManagedTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha512ManagedTests.cs @@ -1,11 +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; +using Xunit; + namespace System.Security.Cryptography.Hashing.Algorithms.Tests { /// /// Sha512Managed has a copy of the same implementation as SHA512 /// + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] public class Sha512ManagedTests : Sha512Tests { protected override HashAlgorithm Create() diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha512Tests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha512Tests.cs index ef1d02fc818f7..53672a8734351 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha512Tests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/Sha512Tests.cs @@ -1,10 +1,12 @@ // 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 Xunit; namespace System.Security.Cryptography.Hashing.Algorithms.Tests { + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsPlatformCryptoSupported))] public class Sha512Tests : HashAlgorithmTest { protected override HashAlgorithm Create() diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/ShaTests.NoPlatformCrypto.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/ShaTests.NoPlatformCrypto.cs new file mode 100644 index 0000000000000..4eb8e5a52ce22 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/ShaTests.NoPlatformCrypto.cs @@ -0,0 +1,28 @@ +// 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.Collections.Generic; +using Xunit; + +namespace System.Security.Cryptography.Hashing.Algorithms.Tests +{ + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotPlatformCryptoSupported))] + public class ShaTests_NoPlatformCrypto + { + public static IEnumerable ShaImpl() + { + yield return new object[]{ SHA1.Create() }; + yield return new object[]{ SHA256.Create() }; + yield return new object[]{ SHA384.Create() }; + yield return new object[]{ SHA512.Create() }; + } + + [Theory] + [MemberData(nameof(ShaImpl))] + public void Sha_PlatformNotSupportedException(HashAlgorithm hash) + { + Assert.Throws(() => hash.ComputeHash(Array.Empty())); + } + } +} \ No newline at end of file diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj b/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj index b21e01b4995d9..a8c91d48c9140 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj @@ -244,6 +244,7 @@ + diff --git a/src/mono/sample/wasm/wasm.mk b/src/mono/sample/wasm/wasm.mk index 1858ca3791182..746be79165d9d 100644 --- a/src/mono/sample/wasm/wasm.mk +++ b/src/mono/sample/wasm/wasm.mk @@ -23,7 +23,7 @@ run-browser: echo "The tool dotnet-serve could not be found. Install with: $(DOTNET) tool install --global dotnet-serve"; \ exit 1; \ else \ - $(DOTNET) serve -d bin/$(CONFIG)/AppBundle -p 8000; \ + $(DOTNET) serve -d:bin/$(CONFIG)/AppBundle -p 8000; \ fi run-console: diff --git a/src/mono/wasm/Makefile b/src/mono/wasm/Makefile index abb93b97f4068..fba2f1d289954 100644 --- a/src/mono/wasm/Makefile +++ b/src/mono/wasm/Makefile @@ -20,6 +20,7 @@ MONO_BIN_DIR?=$(BINDIR)/mono/Browser.wasm.$(CONFIG) NATIVE_BIN_DIR?=$(BINDIR)/native/net6.0-Browser-$(CONFIG)-wasm ICU_LIBDIR?= SYSTEM_NATIVE_LIBDIR?=$(TOP)/src/libraries/Native/Unix/System.Native +SYSTEM_CRYPTO_NATIVE_LIBDIR?=$(TOP)/src/libraries/Native/AnyOS/System.Security.Cryptography.Native.Browser ENABLE_ES6?=false _MSBUILD_WASM_BUILD_ARGS=/p:TargetOS=Browser /p:TargetArchitecture=wasm /p:Configuration=$(CONFIG) ENABLE_METADATA_UPDATE?=false @@ -60,6 +61,7 @@ MONO_LIBS = \ $(MONO_BIN_DIR)/libmono-profiler-aot.a \ ${NATIVE_BIN_DIR}/libSystem.Native.a \ ${NATIVE_BIN_DIR}/libSystem.IO.Compression.Native.a \ + ${NATIVE_BIN_DIR}/libSystem.Security.Cryptography.Native.Browser.a \ $(ICU_LIBDIR)/libicuuc.a \ $(ICU_LIBDIR)/libicui18n.a @@ -71,7 +73,7 @@ ifeq ($(ENABLE_METADATA_UPDATE),true) EMCC_FLAGS+=-DENABLE_METADATA_UPDATE=1 endif -EMCC_DEBUG_FLAGS =-g -Os -s ASSERTIONS=1 -DDEBUG=1 +EMCC_DEBUG_FLAGS =-g4 -Os -s ASSERTIONS=1 -DDEBUG=1 EMCC_RELEASE_FLAGS=-Oz --llvm-opts 2 ifeq ($(NOSTRIP),) @@ -101,8 +103,8 @@ $(NATIVE_BIN_DIR)/include/wasm: $(BUILDS_OBJ_DIR): mkdir -p $$@ -$(NATIVE_BIN_DIR)/dotnet.js: $(BUILDS_OBJ_DIR)/driver.o $(BUILDS_OBJ_DIR)/pinvoke.o $(BUILDS_OBJ_DIR)/corebindings.o runtime/library_mono.js runtime/binding_support.js runtime/dotnet_support.js $(SYSTEM_NATIVE_LIBDIR)/pal_random.js $(2) | $(NATIVE_BIN_DIR) - $(EMCC) $(EMCC_FLAGS) $(1) --js-library runtime/library_mono.js --js-library runtime/binding_support.js --js-library runtime/dotnet_support.js --js-library $(SYSTEM_NATIVE_LIBDIR)/pal_random.js $(BUILDS_OBJ_DIR)/driver.o $(BUILDS_OBJ_DIR)/pinvoke.o $(BUILDS_OBJ_DIR)/corebindings.o $(2) -o $(NATIVE_BIN_DIR)/dotnet.js $(3) +$(NATIVE_BIN_DIR)/dotnet.js: $(BUILDS_OBJ_DIR)/driver.o $(BUILDS_OBJ_DIR)/pinvoke.o $(BUILDS_OBJ_DIR)/corebindings.o runtime/library_mono.js runtime/binding_support.js runtime/dotnet_support.js runtime/library_channel.js $(SYSTEM_NATIVE_LIBDIR)/pal_random.js $(SYSTEM_CRYPTO_NATIVE_LIBDIR)/pal_crypto_webworker.js $(2) | $(NATIVE_BIN_DIR) + $(EMCC) $(EMCC_FLAGS) $(1) --js-library runtime/library_mono.js --js-library runtime/binding_support.js --js-library runtime/dotnet_support.js --pre-js runtime/library_channel.js --js-library $(SYSTEM_NATIVE_LIBDIR)/pal_random.js --js-library $(SYSTEM_CRYPTO_NATIVE_LIBDIR)/pal_crypto_webworker.js $(BUILDS_OBJ_DIR)/driver.o $(BUILDS_OBJ_DIR)/pinvoke.o $(BUILDS_OBJ_DIR)/corebindings.o $(2) -o $(NATIVE_BIN_DIR)/dotnet.js $(3) $(BUILDS_OBJ_DIR)/pinvoke-table.h: $(PINVOKE_TABLE) | $(BUILDS_OBJ_DIR) if cmp -s $(PINVOKE_TABLE) $$@ ; then : ; else cp $(PINVOKE_TABLE) $$@ ; fi @@ -116,13 +118,16 @@ $(BUILDS_OBJ_DIR)/pinvoke.o: runtime/pinvoke.c runtime/pinvoke.h $(BUILDS_OBJ_DI $(BUILDS_OBJ_DIR)/corebindings.o: runtime/corebindings.c | $(BUILDS_OBJ_DIR) $(EMCC) $(EMCC_FLAGS) $(1) -Oz -I$(MONO_INCLUDE_DIR) runtime/corebindings.c -c -o $$@ +$(NATIVE_BIN_DIR)/dotnet_crypto_worker.js: runtime/dotnet_crypto_worker.js | $(NATIVE_BIN_DIR) + cp runtime/dotnet_crypto_worker.js $(NATIVE_BIN_DIR)/dotnet_crypto_worker.js + $(NATIVE_BIN_DIR)/src/emcc-flags.txt: | $(NATIVE_BIN_DIR)/src Makefile echo "$(call escape_quote,$(EMCC_FLAGS)) $(1)" > $$@ $(NATIVE_BIN_DIR)/src/emcc-version.txt: $(EMSDK_PATH)/upstream/.emsdk_version | $(NATIVE_BIN_DIR)/src $(EMCC) --version | head -1 > $$@ -build-native: $(NATIVE_BIN_DIR)/dotnet.js $(NATIVE_BIN_DIR)/src/emcc-flags.txt $(NATIVE_BIN_DIR)/src/emcc-version.txt +build-native: $(NATIVE_BIN_DIR)/dotnet.js $(NATIVE_BIN_DIR)/dotnet_crypto_worker.js $(NATIVE_BIN_DIR)/src/emcc-flags.txt $(NATIVE_BIN_DIR)/src/emcc-version.txt endef @@ -141,7 +146,7 @@ clean: icu-files: $(wildcard $(ICU_LIBDIR)/*.dat) $(ICU_LIBDIR)/libicuuc.a $(ICU_LIBDIR)/libicui18n.a | $(NATIVE_BIN_DIR) cp $^ $(NATIVE_BIN_DIR) -source-files: runtime/driver.c runtime/pinvoke.c runtime/corebindings.c runtime/binding_support.js runtime/dotnet_support.js runtime/library_mono.js $(SYSTEM_NATIVE_LIBDIR)/pal_random.js | $(NATIVE_BIN_DIR)/src +source-files: runtime/driver.c runtime/pinvoke.c runtime/corebindings.c runtime/binding_support.js runtime/dotnet_support.js runtime/library_mono.js runtime/library_channel.js $(SYSTEM_NATIVE_LIBDIR)/pal_random.js $(SYSTEM_CRYPTO_NATIVE_LIBDIR)/pal_crypto_webworker.js | $(NATIVE_BIN_DIR)/src cp $^ $(NATIVE_BIN_DIR)/src header-files: runtime/pinvoke.h | $(NATIVE_BIN_DIR)/include/wasm diff --git a/src/mono/wasm/README.md b/src/mono/wasm/README.md index 6a5dff401ab21..6cd66c9f94072 100644 --- a/src/mono/wasm/README.md +++ b/src/mono/wasm/README.md @@ -9,8 +9,7 @@ Note: Irrespective of `$(EMSDK_PATH)`'s value, `provision-wasm` will always inst `EMSDK_PATH` is set to `$reporoot/src/mono/wasm/emsdk` by default, by the Makefile. -Note: `EMSDK_PATH` is set by default in `src/mono/wasm/Makefile`, so building targets from that will have it set. But you might need to set it manually if -you are directly using the `dotnet build`, or `build.sh`. +Note: `EMSDK_PATH` is set by default in `src/mono/wasm/Makefile`, so building targets from that will have it set. But you might need to set it manually if you are directly using the `dotnet build`, or `build.sh`. * Alternatively you can install **correct version** yourself from the [Emscripten SDK guide](https://emscripten.org/docs/getting_started/downloads.html). Do not install `latest` but rather specific version e.g. `./emsdk install 2.0.12`. See [emscripten-version.txt](./emscripten-version.txt) diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index c5042c84080cb..7e9b7517e8c8f 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -322,12 +322,14 @@ <_HasDotnetWasm Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.wasm'">true <_HasDotnetJs Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js'">true + <_HasCryptoWorkerJs Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet_crypto_worker.js'">true + @@ -376,13 +378,36 @@ $(EmccFlags) -g $(EmccFlags) -DENABLE_AOT=1 -DDRIVER_GEN=1 $(EmccFlags) -DINVARIANT_GLOBALIZATION=1 + <_WasmRuntimePackIncludeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include')) + <_WasmRuntimePackSrcDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src')) + - <_WasmPInvokeModules Include="libSystem.Native" /> - <_WasmPInvokeModules Include="libSystem.IO.Compression.Native" /> - <_WasmPInvokeModules Include="libSystem.Globalization.Native" /> + <_WasmRuntimePackSystemLibs Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\libSystem.*.a"/> + <_WasmRuntimePackNativeLibs Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a"/> + + <_WasmPInvokeModules Include="@(_WasmRuntimePackSystemLibs->'%(FileName)')" /> + + <_WasmPInvokeModules Include="libSystem.Globalization.Native" /> + + <_WasmObjectsToBuild Include="$(_WasmRuntimePackSrcDir)\*.c" /> + <_WasmObjectsToBuild OutputPath="$(_WasmIntermediateOutputPath)%(FileName).o" /> + + <_WasmObjects Include="@(_WasmRuntimePackNativeLibs)" /> + <_WasmObjects Include="@(_WasmObjectsToBuild->'%(OutputPath)')" /> + + <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)\*.js" /> + + <_DotnetJSSrcPreFile Include="$(_WasmRuntimePackSrcDir)*channel.js" /> + + <_DotnetJSSrcFile Remove="@(_DotnetJSSrcPreFile)" /> + + <_AOTAssemblies Include="@(_WasmAssembliesInternal)" Condition="'%(_WasmAssembliesInternal._InternalForceInterpret)' != 'true'" /> + <_BitcodeFile Include="%(_WasmAssembliesInternal.LlvmBitcodeFile)" /> + + $(EmccFlags) -DLINK_ICALLS=1 <_WasmRuntimePackIncludeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include')) <_WasmRuntimePackSrcDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src')) - - - - <_WasmRuntimePackNativeLibs Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a" /> - <_WasmObjectsToBuild Include="$(_WasmRuntimePackSrcDir)\*.c" /> - <_WasmObjectsToBuild OutputPath="$(_WasmIntermediateOutputPath)%(FileName).o" /> - - <_WasmObjects Include="@(_WasmRuntimePackNativeLibs)" /> - <_WasmObjects Include="@(_WasmObjectsToBuild->'%(OutputPath)')" /> - <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)\*.js" /> - - <_AOTAssemblies Include="@(_WasmAssembliesInternal)" Condition="'%(_WasmAssembliesInternal._InternalForceInterpret)' != 'true'" /> - <_BitcodeFile Include="%(_WasmAssembliesInternal.LlvmBitcodeFile)" /> - + + <_WasmRuntimePackWebWorkerDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir))) + @@ -432,11 +446,13 @@ + <_EmccLinkerArguments Include="$(EmccLDFlags)" /> <_EmccLinkerArguments Include="--js-library "%(_DotnetJSSrcFile.Identity)"" /> + <_EmccLinkerArguments Include="--pre-js "%(_DotnetJSSrcPreFile.Identity)"" Condition="@(_DotNetJSSrcPreFile->Count()) > 0" /> <_EmccLinkerArguments Include=""%(_BitcodeFile.Identity)"" /> <_EmccLinkerArguments Include=""%(_WasmObjects.Identity)"" /> <_EmccLinkerArguments Include="-o "$(_WasmIntermediateOutputPath)dotnet.js"" /> @@ -455,6 +471,7 @@ + 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 0000000000000..5ea07d2bfc09a --- /dev/null +++ b/src/mono/wasm/runtime/dotnet_crypto_worker.js @@ -0,0 +1,173 @@ +// 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) { + // console.log("await_request()"); + + 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(); + // console.log("Request: " + req); + 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/library_channel.js b/src/mono/wasm/runtime/library_channel.js new file mode 100644 index 0000000000000..a2cf2b8cdb406 --- /dev/null +++ b/src/mono/wasm/runtime/library_channel.js @@ -0,0 +1,147 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +Module [ 'channel' ] = { + _impl: class { + // Index constants for the communication buffer. + get STATE_IDX() { return 0; } + get MSG_SIZE_IDX() { return 1; } + get COMM_LAST_IDX() { return this.MSG_SIZE_IDX; } + + // 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 + + constructor(msg_char_len) { + 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); + } + + get_msg_len() { return this.msg_char_len; } + get_msg_buffer() { return this.msg_buf; } + get_comm_buffer() { return this.comm_buf; } + + send_msg(msg) { + 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(); + } + + shutdown() { + 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); + } + + _send_request(msg) { + var state; + 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_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); + } + } + + _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; + } + + _read_response() { + var state; + var 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); + + var 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; + } + + _read_from_msg(begin, end) { + return String.fromCharCode.apply(null, this.msg.slice(begin, end)); + } + }, + + create: function (msg_char_len) { + 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 this._impl(msg_char_len); + } +}; diff --git a/src/mono/wasm/runtime/library_mono.js b/src/mono/wasm/runtime/library_mono.js index 685aaaf375d85..88223dd988495 100644 --- a/src/mono/wasm/runtime/library_mono.js +++ b/src/mono/wasm/runtime/library_mono.js @@ -48,13 +48,17 @@ */ var MonoSupportLib = { - $MONO__postset: 'MONO.export_functions (Module);', + $MONO__postset: 'MONO.export_functions (Module); MONO._initialize_workers ();', $MONO: { pump_count: 0, timeout_queue: [], _vt_stack: [], mono_wasm_runtime_is_ready : false, mono_wasm_ignore_pdb_load_errors: true, + mono_wasm_crypto: { + channel: null, + worker: null + }, /** @type {object.} */ _id_table: {}, @@ -1738,6 +1742,33 @@ var MonoSupportLib = { return Module.ccall ('mono_wasm_get_icudt_name', 'string', ['string'], [culture]); }, + _initialize_workers: function () { + console.debug ("MONO_WASM: Initialize WebWorkers"); + // Crypto support is only available in the Browser. + if (ENVIRONMENT_IS_WEB) { + var chan = Module.channel.create (); + var worker = new Worker ("dotnet_crypto_worker.js"); + worker.postMessage ({ + comm_buf: chan.get_comm_buffer(), + msg_buf: chan.get_msg_buffer(), + msg_char_len: chan.get_msg_len() + }); + MONO.mono_wasm_crypto.channel = chan; + MONO.mono_wasm_crypto.worker = worker; + } + }, + + _shutdown_workers: function () { + console.debug ("MONO_WASM: Shutdown WebWorkers"); + // Crypto support is only available in the Browser. + if (ENVIRONMENT_IS_WEB) { + var chan = MONO.mono_wasm_crypto.channel; + if (chan !== null) { + chan.shutdown (); + } + } + }, + _finalize_startup: function (args, ctx) { var loaded_files_with_debug_info = []; @@ -1777,6 +1808,9 @@ var MonoSupportLib = { MONO.mono_wasm_setenv ("TZ", tz || "UTC"); MONO.mono_wasm_runtime_ready (); args.loaded_cb (); + + // [TODO] Shutdown workers when run under test environment + // MONO._shutdown_workers(); }, _load_assets_and_runtime: function (args) { diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index dda6fa29f6272..4f461af81457b 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -44,6 +44,7 @@ + @@ -91,6 +92,7 @@ emcc --profiling-funcs -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s BINARYEN=1 -s ALIASING_FUNCTION_POINTERS=0 -s NO_EXIT_RUNTIME=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'FS_createPath', 'FS_createDataFile', 'cwrap', 'setValue', 'getValue', 'UTF8ToString', 'UTF8ArrayToString', 'addFunction']" -s "EXPORTED_FUNCTIONS=['_putchar']" --source-map-base http://example.com -emit-llvm -s FORCE_FILESYSTEM=1 -s USE_ZLIB=1 $(EmccFlags) -s MODULARIZE=1 -s EXPORT_ES6=1 - -g -Os -s ASSERTIONS=1 -DENABLE_NETCORE=1 -DDEBUG=1 + -g4 -Os -s ASSERTIONS=1 -DENABLE_NETCORE=1 -DDEBUG=1 -Oz --llvm-opts 2 -DENABLE_NETCORE=1 "$(EMSDK_PATH)/upstream/bin/wasm-opt" --strip-dwarf "$(NativeBinDir)/dotnet.wasm" -o "$(NativeBinDir)/dotnet.wasm" $(ArtifactsObjDir)wasm $(WasmObjDir)\emcc-version.txt $(MonoArtifactsPath)include/mono-2.0 $(RepoRoot)src\libraries\Native\Unix\System.Native + $(RepoRoot)src\libraries\Native\AnyOS\System.Security.Cryptography.Native.Browser + $(NativeBinDir)libSystem.IO.Compression.Native.a; + $(NativeBinDir)libSystem.Security.Cryptography.Native.Browser.a" /> @@ -156,7 +160,7 @@ EmSdkPath="$(EMSDK_PATH)" IgnoreStandardErrorWarningFormat="true" /> - @@ -179,6 +183,11 @@ + + + @@ -205,7 +216,8 @@