diff --git a/Cargo.lock b/Cargo.lock index ab546e2d695d51..beb648aa67d659 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -483,7 +483,12 @@ name = "deno_crypto" version = "0.9.0" dependencies = [ "deno_core", + "lazy_static", "rand", + "ring", + "serde", + "serde_json", + "tokio 0.2.22", ] [[package]] diff --git a/op_crates/crypto/01_crypto.js b/op_crates/crypto/01_crypto.js index d0b0d98c7c7805..23ee7b68b72b13 100644 --- a/op_crates/crypto/01_crypto.js +++ b/op_crates/crypto/01_crypto.js @@ -1,5 +1,7 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +// Implements https://www.w3.org/TR/WebCryptoAPI + ((window) => { const core = window.Deno.core; @@ -36,15 +38,141 @@ arrayBufferView.byteOffset, arrayBufferView.byteLength, ); - core.jsonOpSync("op_get_random_values", {}, ui8); + core.jsonOpSync("op_crypto_get_random_values", {}, ui8); return arrayBufferView; } + // Algorithm normalization, which involves storing the expected data for + // each pair of algorithm and crypto operation, is done on the JS side because + // it is convenient. + // Shall it stay that way, or are we better to move it on the Rust side? + + // We need this method after initialization, anytime we need to normalize + // a provided algorithm. We store it here to prevent prototype pollution. + const toUpperCase = String.prototype.toUpperCase; + + class RegisteredAlgorithmsContainer { + #nameIndex; + #definitions; + + constructor(definitions) { + this.#nameIndex = Object.create(null); + this.#definitions = Object.create(null); + for (const [name, definition] of Object.entries(definitions)) { + this.#nameIndex[name.toUpperCase()] = name; + this.#definitions[name] = definition; + } + } + + getDefinition(name) { + return this.#definitions[name]; + } + + normalizeName(providedName) { + const upperCaseName = toUpperCase.call(providedName); + return this.#nameIndex[upperCaseName]; + } + } + + const supportedAlgorithms = {}; + + function normalizeAlgorithm(algorithm, registeredAlgorithms) { + let alg; + if (typeof algorithm === "string") { + alg = { name: algorithm }; + } + if (typeof algorithm === "object" && algorithm !== null) { + if (typeof algorithm.name !== "string") { + throw new TypeError("Algorithm name is missing or not a string"); + } + const algorithmName = registeredAlgorithms.normalizeName(algorithm.name); + if (algorithmName === undefined) { + throw new DOMException( + "Unrecognized algorithm name", + "NotSupportedError", + ); + } + const definition = registeredAlgorithms.getDefinition(algorithmName); + // TODO: Validate the algorithm data based on the definition + alg = { name: algorithmName }; + } else { + throw new TypeError("Argument 1 must be an object or a string"); + } + return alg; + } + + const subtle = { + async decrypt(algorithm, key, data) { + await Promise.resolve(); + throw new Error("Not implemented"); + }, + async deriveBits(algorithm, baseKey, length) { + await Promise.resolve(); + throw new Error("Not implemented"); + }, + async deriveKey( + algorithm, + baseKey, + derivedKeyType, + extractable, + keyUsages, + ) { + await Promise.resolve(); + throw new Error("Not implemented"); + }, + async digest(algorithm, data) { + await Promise.resolve(); + throw new Error("Not implemented"); + }, + async encrypt(algorithm, key, data) { + await Promise.resolve(); + throw new Error("Not implemented"); + }, + async exportKey(format, key) { + await Promise.resolve(); + throw new Error("Not implemented"); + }, + async generateKey(algorithm, extractable, keyUsages) { + await Promise.resolve(); + throw new Error("Not implemented"); + }, + async importKey(format, keyData, algorithm, extractable, keyUsages) { + await Promise.resolve(); + throw new Error("Not implemented"); + }, + async sign(algorithm, key, data) { + await Promise.resolve(); + throw new Error("Not implemented"); + }, + async unwrapKey( + format, + wrappedKey, + unwrappingKey, + unwrapAlgorithm, + unwrappedKeyAlgorithm, + extractable, + keyUsages, + ) { + await Promise.resolve(); + throw new Error("Not implemented"); + }, + async verify(algorithm, key, signature, data) { + await Promise.resolve(); + throw new Error("Not implemented"); + }, + async wrapKey(format, key, wrappingKey, wrapAlgorithm) { + await Promise.resolve(); + throw new Error("Not implemented"); + }, + }; + window.crypto = { getRandomValues, + subtle, }; window.__bootstrap = window.__bootstrap || {}; window.__bootstrap.crypto = { getRandomValues, + subtle, }; })(this); diff --git a/op_crates/crypto/Cargo.toml b/op_crates/crypto/Cargo.toml index a08e30d5617c75..d62e51a77cb723 100644 --- a/op_crates/crypto/Cargo.toml +++ b/op_crates/crypto/Cargo.toml @@ -1,5 +1,4 @@ # Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. - [package] name = "deno_crypto" version = "0.9.0" @@ -16,4 +15,8 @@ path = "lib.rs" [dependencies] deno_core = { version = "0.75.0", path = "../../core" } rand = "0.7.3" - +ring = "0.16.19" +lazy_static = "1.4.0" +tokio = { version = "0.2.22", features = ["full"] } +serde_json = { version = "1.0", features = ["preserve_order"] } +serde = { version = "1.0.116", features = ["derive"] } diff --git a/op_crates/crypto/lib.rs b/op_crates/crypto/lib.rs index a211a29b3d9038..c9b22c15b1bbea 100644 --- a/op_crates/crypto/lib.rs +++ b/op_crates/crypto/lib.rs @@ -25,7 +25,7 @@ pub fn init(isolate: &mut JsRuntime) { } } -pub fn op_get_random_values( +pub fn op_crypto_get_random_values( state: &mut OpState, _args: Value, zero_copy: &mut [ZeroCopyBuf], diff --git a/runtime/ops/crypto.rs b/runtime/ops/crypto.rs index a73843a33cacfa..48fd075ffe34f4 100644 --- a/runtime/ops/crypto.rs +++ b/runtime/ops/crypto.rs @@ -1,5 +1,4 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -use deno_crypto::op_get_random_values; use deno_crypto::rand::rngs::StdRng; use deno_crypto::rand::SeedableRng; @@ -10,5 +9,10 @@ pub fn init(rt: &mut deno_core::JsRuntime, maybe_seed: Option) { let mut state = op_state.borrow_mut(); state.put::(rng); } - super::reg_json_sync(rt, "op_get_random_values", op_get_random_values); + super::reg_json_sync( + rt, + "op_crypto_get_random_values", + deno_crypto::op_crypto_get_random_values, + ); + super::reg_json_async(rt, "op_crypto_subtle_digest", deno_crypto::op_crypto_subtle_digest); }