diff options
author | Eric Erhardt <eric.erhardt@microsoft.com> | 2022-06-22 00:27:16 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-22 00:27:16 +0300 |
commit | eed18afa59b00f780461e0f9bd871fbd4b843b7c (patch) | |
tree | 7476e5cbe55aa7507d7ad010c407d551241da4c7 /src/mono/wasm/runtime | |
parent | 89dc073529aadfa3f36bcf7ed029c0a3b59d357e (diff) |
Use crypto.subtle for HMAC on Browser WASM (#70745)
* Use crypto.subtle for HMAC on Browser WASM
Implement the browser "native" portion for HMAC on Browser WASM.
I also made a few refactoring / simplifications where necessary.
Contributes to #40074
Diffstat (limited to 'src/mono/wasm/runtime')
-rw-r--r-- | src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js | 3 | ||||
-rw-r--r-- | src/mono/wasm/runtime/crypto-worker.ts | 23 | ||||
-rw-r--r-- | src/mono/wasm/runtime/es6/dotnet.es6.lib.js | 3 | ||||
-rw-r--r-- | src/mono/wasm/runtime/exports.ts | 9 | ||||
-rw-r--r-- | src/mono/wasm/runtime/workers/dotnet-crypto-worker.js | 44 |
5 files changed, 66 insertions, 16 deletions
diff --git a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js index ceeb9d37d75..d8492ff8ced 100644 --- a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js +++ b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js @@ -70,8 +70,9 @@ const linked_functions = [ "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! -- diff --git a/src/mono/wasm/runtime/crypto-worker.ts b/src/mono/wasm/runtime/crypto-worker.ts index f17a455892d..92171e76562 100644 --- a/src/mono/wasm/runtime/crypto-worker.ts +++ b/src/mono/wasm/runtime/crypto-worker.ts @@ -9,7 +9,7 @@ let mono_wasm_crypto: { 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; } @@ -33,6 +33,27 @@ export function dotnet_browser_simple_digest_hash(ver: number, input_buffer: num 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" diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js index 5866ffed265..3cabbf8833e 100644 --- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js +++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js @@ -107,8 +107,9 @@ const linked_functions = [ "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! -- diff --git a/src/mono/wasm/runtime/exports.ts b/src/mono/wasm/runtime/exports.ts index 439665bcc94..16b7e88054a 100644 --- a/src/mono/wasm/runtime/exports.ts +++ b/src/mono/wasm/runtime/exports.ts @@ -69,7 +69,11 @@ 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"; +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 @@ -370,8 +374,9 @@ export const __linker_exports: any = { 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 = { diff --git a/src/mono/wasm/runtime/workers/dotnet-crypto-worker.js b/src/mono/wasm/runtime/workers/dotnet-crypto-worker.js index 329e6a48a76..5e27dd59b5f 100644 --- a/src/mono/wasm/runtime/workers/dotnet-crypto-worker.js +++ b/src/mono/wasm/runtime/workers/dotnet-crypto-worker.js @@ -163,29 +163,51 @@ var ChannelWorker = { }; 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; } |