diff options
author | Eric Erhardt <eric.erhardt@microsoft.com> | 2022-07-09 05:51:14 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-09 05:51:14 +0300 |
commit | 8f75cc9306b0508506f60a94f94d2227ee020798 (patch) | |
tree | 484732c7536978082c09b2a2d8e04acf1d850b30 /src/mono/wasm/runtime | |
parent | 4a38ac3c3f2155218e01336f0fdeefffdece65ef (diff) |
Enable Rfc2898DeriveBytes on Browser WASM (#71768)
* Enable Rfc2898DeriveBytes on Browser WASM
Marks the APIs as supported on Browser, and enables Rfc2898 tests on Browser WASM.
Use SubtleCrypto deriveBits API to implement one shot Pbkdf2.
* Mark HKDF as supported on Browser and enable tests
Contributes to #40074
Diffstat (limited to 'src/mono/wasm/runtime')
-rw-r--r-- | src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js | 1 | ||||
-rw-r--r-- | src/mono/wasm/runtime/crypto-worker.ts | 59 | ||||
-rw-r--r-- | src/mono/wasm/runtime/es6/dotnet.es6.lib.js | 1 | ||||
-rw-r--r-- | src/mono/wasm/runtime/exports.ts | 4 | ||||
-rw-r--r-- | src/mono/wasm/runtime/workers/dotnet-crypto-worker.js | 25 |
5 files changed, 61 insertions, 29 deletions
diff --git a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js index cd78c6a2240..ce03a29be03 100644 --- a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js +++ b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js @@ -91,6 +91,7 @@ const linked_functions = [ "dotnet_browser_simple_digest_hash", "dotnet_browser_sign", "dotnet_browser_encrypt_decrypt", + "dotnet_browser_derive_bits", /// mono-threads-wasm.c #if USE_PTHREADS diff --git a/src/mono/wasm/runtime/crypto-worker.ts b/src/mono/wasm/runtime/crypto-worker.ts index d27e185ac63..c270bc924b1 100644 --- a/src/mono/wasm/runtime/crypto-worker.ts +++ b/src/mono/wasm/runtime/crypto-worker.ts @@ -27,18 +27,7 @@ export function dotnet_browser_simple_digest_hash(ver: number, input_buffer: num data: Array.from(Module.HEAPU8.subarray(input_buffer, input_buffer + input_len)) }; - const result = _send_msg_worker(msg); - if (typeof result === "number") { - return result; - } - - if (result.length > output_len) { - console.error("DIGEST HASH: Digest length exceeds output length: " + result.length + " > " + output_len); - return ERR_ARGS; - } - - Module.HEAPU8.set(result, output_buffer); - return 0; + return _send_simple_msg(msg, "DIGEST HASH", output_buffer, output_len); } 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 { @@ -49,18 +38,7 @@ export function dotnet_browser_sign(hashAlgorithm: number, key_buffer: number, k data: Array.from(Module.HEAPU8.subarray(input_buffer, input_buffer + input_len)) }; - const result = _send_msg_worker(msg); - if (typeof result === "number") { - return result; - } - - if (result.length > output_len) { - console.error("SIGN HASH: Sign length exceeds output length: " + result.length + " > " + output_len); - return ERR_ARGS; - } - - Module.HEAPU8.set(result, output_buffer); - return 0; + return _send_simple_msg(msg, "SIGN HASH", output_buffer, output_len); } const AesBlockSizeBytes = 16; // 128 bits @@ -84,7 +62,7 @@ export function dotnet_browser_encrypt_decrypt(isEncrypting: boolean, key_buffer } if (result.length > output_len) { - console.error("ENCRYPT DECRYPT: Encrypt/Decrypt length exceeds output length: " + result.length + " > " + output_len); + console.error(`ENCRYPT DECRYPT: Encrypt/Decrypt length exceeds output length: ${result.length} > ${output_len}`); return ERR_ARGS; } @@ -92,6 +70,35 @@ export function dotnet_browser_encrypt_decrypt(isEncrypting: boolean, key_buffer return result.length; } +export function dotnet_browser_derive_bits(password_buffer: number, password_len: number, salt_buffer: number, salt_len: number, iterations: number, hashAlgorithm: number, output_buffer: number, output_len: number): number { + const msg = { + func: "derive_bits", + password: Array.from(Module.HEAPU8.subarray(password_buffer, password_buffer + password_len)), + salt: Array.from(Module.HEAPU8.subarray(salt_buffer, salt_buffer + salt_len)), + iterations: iterations, + hashAlgorithm: hashAlgorithm, + lengthInBytes: output_len + }; + + return _send_simple_msg(msg, "DERIVE BITS", output_buffer, output_len); +} + +function _send_simple_msg(msg: any, prefix: string, output_buffer: number, output_len: number): number { + const result = _send_msg_worker(msg); + + if (typeof result === "number") { + return result; + } + + if (result.length > output_len) { + console.error(`${prefix}: Result length exceeds output length: ${result.length} > ${output_len}`); + return ERR_ARGS; + } + + Module.HEAPU8.set(result, output_buffer); + return 0; +} + export function init_crypto(): void { if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.subtle !== "undefined" && typeof SharedArrayBuffer !== "undefined" @@ -117,7 +124,7 @@ export function init_crypto(): void { } } -function _send_msg_worker(msg: any): any { +function _send_msg_worker(msg: any): number | any { mono_assert(!!mono_wasm_crypto, "subtle crypto not initialized"); try { diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js index a559c8f42de..5d3d5d3ecb3 100644 --- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js +++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js @@ -128,6 +128,7 @@ const linked_functions = [ "dotnet_browser_simple_digest_hash", "dotnet_browser_sign", "dotnet_browser_encrypt_decrypt", + "dotnet_browser_derive_bits", /// mono-threads-wasm.c #if USE_PTHREADS diff --git a/src/mono/wasm/runtime/exports.ts b/src/mono/wasm/runtime/exports.ts index 436e0ba347a..22e1fdf340e 100644 --- a/src/mono/wasm/runtime/exports.ts +++ b/src/mono/wasm/runtime/exports.ts @@ -72,7 +72,8 @@ import { dotnet_browser_can_use_subtle_crypto_impl, dotnet_browser_simple_digest_hash, dotnet_browser_sign, - dotnet_browser_encrypt_decrypt + dotnet_browser_encrypt_decrypt, + dotnet_browser_derive_bits, } from "./crypto-worker"; import { mono_wasm_cancel_promise_ref } from "./cancelable-promise"; import { mono_wasm_web_socket_open_ref, mono_wasm_web_socket_send, mono_wasm_web_socket_receive, mono_wasm_web_socket_close_ref, mono_wasm_web_socket_abort } from "./web-socket"; @@ -408,6 +409,7 @@ export const __linker_exports: any = { dotnet_browser_simple_digest_hash, dotnet_browser_sign, dotnet_browser_encrypt_decrypt, + dotnet_browser_derive_bits, // threading exports, if threading is enabled ...mono_wasm_threads_exports, diff --git a/src/mono/wasm/runtime/workers/dotnet-crypto-worker.js b/src/mono/wasm/runtime/workers/dotnet-crypto-worker.js index 5fa89790c22..f93256c169a 100644 --- a/src/mono/wasm/runtime/workers/dotnet-crypto-worker.js +++ b/src/mono/wasm/runtime/workers/dotnet-crypto-worker.js @@ -256,6 +256,24 @@ async function sign(type, key, data) { return Array.from(new Uint8Array(signResult)); } +async function derive_bits(password, salt, iterations, hashAlgorithm, lengthInBytes) { + const hash_name = get_hash_name(hashAlgorithm); + + const passwordKey = await importKey(password, "PBKDF2", ["deriveBits"]); + const result = await crypto.subtle.deriveBits( + { + name: "PBKDF2", + salt: salt, + iterations: iterations, + hash: hash_name + }, + passwordKey, + lengthInBytes * 8 // deriveBits takes number of bits + ); + + return Array.from(new Uint8Array(result)); +} + function get_hash_name(type) { switch (type) { case 0: return "SHA-1"; @@ -272,7 +290,7 @@ const AesBlockSizeBytes = 16; // 128 bits async function encrypt_decrypt(isEncrypting, key, iv, data) { const algorithmName = "AES-CBC"; const keyUsage = isEncrypting ? ["encrypt"] : ["encrypt", "decrypt"]; - const cryptoKey = await importKey(key, algorithmName, keyUsage); + const cryptoKey = await importKey(new Uint8Array(key), algorithmName, keyUsage); const algorithm = { name: algorithmName, iv: new Uint8Array(iv) @@ -328,7 +346,7 @@ async function decrypt(algorithm, cryptoKey, data) { function importKey(key, algorithmName, keyUsage) { return crypto.subtle.importKey( "raw", - new Uint8Array(key), + key, { name: algorithmName }, @@ -349,6 +367,9 @@ async function handle_req_async(msg) { else if (req.func === "encrypt_decrypt") { return await encrypt_decrypt(req.isEncrypting, req.key, req.iv, req.data); } + else if (req.func === "derive_bits") { + return await derive_bits(new Uint8Array(req.password), new Uint8Array(req.salt), req.iterations, req.hashAlgorithm, req.lengthInBytes); + } else { throw new ArgumentsError("CRYPTO: Unknown request: " + req.func); } |