--- /dev/null
+// 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
+ {
+ [LibraryImport(Libraries.CryptoNative, EntryPoint = "SystemCryptoNativeBrowser_Sign")]
+ internal static unsafe partial int Sign(
+ SimpleDigest hashAlgorithm,
+ byte* key_buffer,
+ int key_len,
+ byte* input_buffer,
+ int input_len,
+ byte* output_buffer,
+ int output_len);
+ }
+}
Sha512,
};
- [LibraryImport(Libraries.CryptoNative, EntryPoint = "SystemCryptoNativeBrowser_CanUseSimpleDigestHash")]
- internal static partial int CanUseSimpleDigestHash();
+ [LibraryImport(Libraries.CryptoNative, EntryPoint = "SystemCryptoNativeBrowser_CanUseSubtleCryptoImpl")]
+ internal static partial int CanUseSubtleCryptoImpl();
[LibraryImport(Libraries.CryptoNative, EntryPoint = "SystemCryptoNativeBrowser_SimpleDigestHash")]
internal static unsafe partial int SimpleDigestHash(
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="$(CommonPath)Interop\Browser\System.Security.Cryptography.Native.Browser\Interop.Sign.cs"
+ Link="Common\Interop\Browser\System.Security.Cryptography.Native.Browser\Interop.Sign.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" />
<Compile Include="System\Security\Cryptography\ECDsa.Create.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\HashProviderDispenser.Browser.cs" />
<Compile Include="System\Security\Cryptography\HMACHashProvider.Browser.Managed.cs" />
+ <Compile Include="System\Security\Cryptography\HMACHashProvider.Browser.Native.cs" />
<Compile Include="System\Security\Cryptography\LiteHash.Browser.cs" />
<Compile Include="System\Security\Cryptography\OidLookup.NoFallback.cs" />
<Compile Include="System\Security\Cryptography\OpenSsl.NotSupported.cs" />
--- /dev/null
+// 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 System.Security.Cryptography
+{
+ internal sealed class HMACNativeHashProvider : HashProvider
+ {
+ private readonly int _hashSizeInBytes;
+ private readonly SimpleDigest _hashAlgorithm;
+ private readonly byte[] _key;
+ private MemoryStream? _buffer;
+
+ public HMACNativeHashProvider(string hashAlgorithmId, ReadOnlySpan<byte> key)
+ {
+ Debug.Assert(HashProviderDispenser.CanUseSubtleCryptoImpl);
+
+ (_hashAlgorithm, _hashSizeInBytes) = SHANativeHashProvider.HashAlgorithmToPal(hashAlgorithmId);
+ _key = key.ToArray();
+ }
+
+ public override void AppendHashData(ReadOnlySpan<byte> data)
+ {
+ _buffer ??= new MemoryStream(1000);
+ _buffer.Write(data);
+ }
+
+ public override int FinalizeHashAndReset(Span<byte> destination)
+ {
+ int written = GetCurrentHash(destination);
+ _buffer = null;
+
+ return written;
+ }
+
+ 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* key = _key)
+ fixed (byte* src = srcArray)
+ fixed (byte* dest = destination)
+ {
+ int res = Interop.BrowserCrypto.Sign(_hashAlgorithm, key, _key.Length, src, srcLength, dest, destination.Length);
+ Debug.Assert(res != 0);
+ }
+ }
+
+ return _hashSizeInBytes;
+ }
+
+ public static unsafe int MacDataOneShot(string hashAlgorithmId, ReadOnlySpan<byte> key, ReadOnlySpan<byte> data, Span<byte> destination)
+ {
+ (SimpleDigest hashName, int hashSizeInBytes) = SHANativeHashProvider.HashAlgorithmToPal(hashAlgorithmId);
+ Debug.Assert(destination.Length >= hashSizeInBytes);
+
+ fixed (byte* k = key)
+ fixed (byte* src = data)
+ fixed (byte* dest = destination)
+ {
+ int res = Interop.BrowserCrypto.Sign(hashName, k, key.Length, src, data.Length, dest, destination.Length);
+ Debug.Assert(res != 0);
+ }
+
+ return hashSizeInBytes;
+ }
+
+ public override int HashSizeInBytes => _hashSizeInBytes;
+
+ public override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ CryptographicOperations.ZeroMemory(_key);
+ }
+ }
+
+ public override void Reset()
+ {
+ _buffer = null;
+ }
+ }
+}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using Internal.Cryptography;
-
namespace System.Security.Cryptography
{
internal static partial class HashProviderDispenser
{
- internal static readonly bool CanUseSubtleCryptoImpl = Interop.BrowserCrypto.CanUseSimpleDigestHash() == 1;
+ internal static readonly bool CanUseSubtleCryptoImpl = Interop.BrowserCrypto.CanUseSubtleCryptoImpl() == 1;
public static HashProvider CreateHashProvider(string hashAlgorithmId)
{
ReadOnlySpan<byte> source,
Span<byte> destination)
{
- HashProvider provider = CreateMacProvider(hashAlgorithmId, key);
- provider.AppendHashData(source);
- return provider.FinalizeHashAndReset(destination);
+ if (CanUseSubtleCryptoImpl)
+ {
+ return HMACNativeHashProvider.MacDataOneShot(hashAlgorithmId, key, source, destination);
+ }
+ else
+ {
+ using HashProvider provider = CreateMacProvider(hashAlgorithmId, key);
+ provider.AppendHashData(source);
+ return provider.FinalizeHashAndReset(destination);
+ }
}
public static int HashData(string hashAlgorithmId, ReadOnlySpan<byte> source, Span<byte> destination)
case HashAlgorithmNames.SHA256:
case HashAlgorithmNames.SHA384:
case HashAlgorithmNames.SHA512:
- return new HMACManagedHashProvider(hashAlgorithmId, key);
+ return CanUseSubtleCryptoImpl
+ ? new HMACNativeHashProvider(hashAlgorithmId, key)
+ : new HMACManagedHashProvider(hashAlgorithmId, key);
}
throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId));
}
using SimpleDigest = Interop.BrowserCrypto.SimpleDigest;
-namespace Internal.Cryptography
+namespace System.Security.Cryptography
{
internal sealed class SHANativeHashProvider : HashProvider
{
_buffer = null;
}
- private static (SimpleDigest, int) HashAlgorithmToPal(string hashAlgorithmId)
+ internal static (SimpleDigest HashName, int HashSizeInBytes) HashAlgorithmToPal(string hashAlgorithmId)
{
return hashAlgorithmId switch
{
}
[Fact]
+ public void HMacMD5_EmptyKey()
+ {
+ VerifyRepeating(
+ input: "Crypto is fun!",
+ 1,
+ hexKey: "",
+ output: "7554A8C4641CBA36BE2AC20CACEA1136");
+ }
+
+ [Fact]
public void HmacMD5_Stream_MultipleOf4096()
{
// Verfied with:
}
[Fact]
+ public void HmacSha1_EmptyKey()
+ {
+ VerifyRepeating(
+ input: "Crypto is fun!",
+ 1,
+ hexKey: "",
+ output: "C979AD8DE8CC546CF82D948226FDD8024599F6CE");
+ }
+
+ [Fact]
public void HmacSha1_Rfc2202_1()
{
VerifyHmac(1, s_testMacs2202[1]);
}
[Fact]
+ public void HmacSha256_EmptyKey()
+ {
+ VerifyRepeating(
+ input: "Crypto is fun!",
+ 1,
+ hexKey: "",
+ output: "DE26DD5A23A91021F61EACF8A8DD324AB5637977486A10D701C4DFA4AE33CB4F");
+ }
+
+ [Fact]
public void HmacSha256_Stream_MultipleOf4096()
{
// Verfied with:
}
[Fact]
+ public void HmacSha384_EmptyKey()
+ {
+ VerifyRepeating(
+ input: "Crypto is fun!",
+ 1,
+ hexKey: "",
+ output: "CFEB81812C8DB4EDB385FCC7CB81E4D715685741AAB1E470FB0B395A414F89867E510E4A2BA2F1F11D7005849FA0DF11");
+ }
+
+ [Fact]
public void HmacSha384_Stream_MultipleOf4096()
{
// Verfied with:
}
[Fact]
+ public void HmacSha512_EmptyKey()
+ {
+ VerifyRepeating(
+ input: "Crypto is fun!",
+ 1,
+ hexKey: "",
+ output: "0C75CCE182743282AAB081BA12AA6C9DEA44852E567063B4EEBD7B33F940B6C8BC16958F9A23401E6FAA00483962A2A8FC7DE9D8B7A14EDD55B49419A211BC37");
+ }
+
+ [Fact]
public void HmacSha512_Stream_MultipleOf4096()
{
// Verfied with:
"mono_wasm_get_icudt_name",
// pal_crypto_webworker.c
+ "dotnet_browser_can_use_subtle_crypto_impl",
"dotnet_browser_simple_digest_hash",
- "dotnet_browser_can_use_simple_digest_hash",
+ "dotnet_browser_sign",
];
// -- this javascript file is evaluated by emcc during compilation! --
worker: Worker
} | null = null;
-export function dotnet_browser_can_use_simple_digest_hash(): number {
+export function dotnet_browser_can_use_subtle_crypto_impl(): number {
return mono_wasm_crypto === null ? 0 : 1;
}
return 1;
}
+export function dotnet_browser_sign(hashAlgorithm: number, key_buffer: number, key_len: 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: "sign",
+ type: hashAlgorithm,
+ key: Array.from(Module.HEAPU8.subarray(key_buffer, key_buffer + key_len)),
+ data: Array.from(Module.HEAPU8.subarray(input_buffer, input_buffer + input_len))
+ };
+
+ const response = mono_wasm_crypto.channel.send_msg(JSON.stringify(msg));
+ const signResult = JSON.parse(response);
+ if (signResult.length > output_len) {
+ console.info("dotnet_browser_sign: about to throw!");
+ throw "SIGN HASH: Sign length exceeds output length: " + signResult.length + " > " + output_len;
+ }
+
+ Module.HEAPU8.set(signResult, output_buffer);
+ return 1;
+}
+
export function init_crypto(): void {
if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined"
&& typeof SharedArrayBuffer !== "undefined"
"mono_wasm_get_icudt_name",
// pal_crypto_webworker.c
+ "dotnet_browser_can_use_subtle_crypto_impl",
"dotnet_browser_simple_digest_hash",
- "dotnet_browser_can_use_simple_digest_hash",
+ "dotnet_browser_sign",
];
// -- this javascript file is evaluated by emcc during compilation! --
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";
+import {
+ dotnet_browser_can_use_subtle_crypto_impl,
+ dotnet_browser_simple_digest_hash,
+ dotnet_browser_sign
+} from "./crypto-worker";
const MONO = {
// current "public" MONO API
mono_wasm_get_icudt_name,
// pal_crypto_webworker.c
+ dotnet_browser_can_use_subtle_crypto_impl,
dotnet_browser_simple_digest_hash,
- dotnet_browser_can_use_simple_digest_hash,
+ dotnet_browser_sign
};
const INTERNAL: any = {
};
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;
- }
+ const digest_type = get_hash_name(type);
// The 'crypto' API is not available in non-browser
// environments (for example, v8 server).
- var digest = await crypto.subtle.digest(digest_type, data);
+ const digest = await crypto.subtle.digest(digest_type, data);
return Array.from(new Uint8Array(digest));
}
+async function sign(type, key, data) {
+ const hash_name = get_hash_name(type);
+
+ if (key.length === 0) {
+ // crypto.subtle.importKey will raise an error for an empty key.
+ // To prevent an error, reset it to a key with just a `0x00` byte. This is equivalent
+ // since HMAC keys get zero-extended up to the block size of the algorithm.
+ key = new Uint8Array([0]);
+ }
+
+ const cryptoKey = await crypto.subtle.importKey("raw", key, {name: "HMAC", hash: hash_name}, false /* extractable */, ["sign"]);
+ const signResult = await crypto.subtle.sign("HMAC", cryptoKey, data);
+ return Array.from(new Uint8Array(signResult));
+}
+
+function get_hash_name(type) {
+ switch(type) {
+ case 0: return "SHA-1";
+ case 1: return "SHA-256";
+ case 2: return "SHA-384";
+ case 3: return "SHA-512";
+ default:
+ throw "CRYPTO: Unknown digest: " + type;
+ }
+}
+
// 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));
+ const digestArr = await call_digest(req.type, new Uint8Array(req.data));
return JSON.stringify(digestArr);
+ }
+ else if (req.func === "sign") {
+ const signResult = await sign(req.type, new Uint8Array(req.key), new Uint8Array(req.data));
+ return JSON.stringify(signResult);
} else {
throw "CRYPTO: Unknown request: " + req.func;
}
uint8_t* output_buffer,
int32_t output_len);
-extern int32_t dotnet_browser_can_use_simple_digest_hash(void);
+extern int32_t dotnet_browser_sign(
+ enum simple_digest hashAlgorithm,
+ uint8_t* key_buffer,
+ int32_t key_len,
+ uint8_t* input_buffer,
+ int32_t input_len,
+ uint8_t* output_buffer,
+ int32_t output_len);
+
+extern int32_t dotnet_browser_can_use_subtle_crypto_impl(void);
int32_t SystemCryptoNativeBrowser_SimpleDigestHash(
enum simple_digest ver,
return dotnet_browser_simple_digest_hash(ver, input_buffer, input_len, output_buffer, output_len);
}
-int32_t SystemCryptoNativeBrowser_CanUseSimpleDigestHash(void)
+int32_t SystemCryptoNativeBrowser_Sign(
+ enum simple_digest hashAlgorithm,
+ uint8_t* key_buffer,
+ int32_t key_len,
+ uint8_t* input_buffer,
+ int32_t input_len,
+ uint8_t* output_buffer,
+ int32_t output_len)
+{
+ return dotnet_browser_sign(hashAlgorithm, key_buffer, key_len, input_buffer, input_len, output_buffer, output_len);
+}
+
+int32_t SystemCryptoNativeBrowser_CanUseSubtleCryptoImpl(void)
{
- return dotnet_browser_can_use_simple_digest_hash();
+ return dotnet_browser_can_use_subtle_crypto_impl();
}
uint8_t* output_buffer,
int32_t output_len);
-PALEXPORT int32_t SystemCryptoNativeBrowser_CanUseSimpleDigestHash(void);
+PALEXPORT int32_t SystemCryptoNativeBrowser_Sign(
+ enum simple_digest ver,
+ uint8_t* key_buffer,
+ int32_t key_len,
+ uint8_t* input_buffer,
+ int32_t input_len,
+ uint8_t* output_buffer,
+ int32_t output_len);
+
+PALEXPORT int32_t SystemCryptoNativeBrowser_CanUseSubtleCryptoImpl(void);