diff options
Diffstat (limited to 'extensions/crypto')
-rw-r--r-- | extensions/crypto/00_webidl.js | 26 | ||||
-rw-r--r-- | extensions/crypto/01_crypto.js | 107 | ||||
-rw-r--r-- | extensions/crypto/Cargo.toml | 2 | ||||
-rw-r--r-- | extensions/crypto/lib.deno_crypto.d.ts | 32 | ||||
-rw-r--r-- | extensions/crypto/lib.rs | 29 |
5 files changed, 195 insertions, 1 deletions
diff --git a/extensions/crypto/00_webidl.js b/extensions/crypto/00_webidl.js new file mode 100644 index 000000000..4545526bf --- /dev/null +++ b/extensions/crypto/00_webidl.js @@ -0,0 +1,26 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +"use strict"; + +((window) => { + const webidl = window.__bootstrap.webidl; + webidl.converters["AlgorithmIdentifier"] = (V, opts) => { + // Union for (object or DOMString) + if (typeof V == "object") { + return webidl.converters["object"](V, opts); + } + + return webidl.converters["DOMString"](V, opts); + }; + + const algorithmDictionary = [ + { + key: "name", + converter: webidl.converters["DOMString"], + }, + ]; + + webidl.converters["Algorithm"] = webidl.createDictionaryConverter( + "Algorithm", + algorithmDictionary, + ); +})(this); diff --git a/extensions/crypto/01_crypto.js b/extensions/crypto/01_crypto.js index 2b3982c32..0f8f72937 100644 --- a/extensions/crypto/01_crypto.js +++ b/extensions/crypto/01_crypto.js @@ -5,6 +5,102 @@ const core = window.Deno.core; const webidl = window.__bootstrap.webidl; + const supportedAlgorithms = { + "digest": { + "SHA-1": {}, + "SHA-256": {}, + "SHA-384": {}, + "SHA-512": {}, + }, + }; + + function normalizeAlgorithm(algorithm, op) { + if (typeof algorithm == "string") { + return normalizeAlgorithm({ name: algorithm }, op); + } + + const initialAlgorithm = webidl.converters["Algorithm"](algorithm, { + context: "Argument 1", + }); + + const registeredAlgorithms = supportedAlgorithms[op]; + const algorithmName = Object.keys(registeredAlgorithms) + .find((key) => key.toLowerCase() == initialAlgorithm.name.toLowerCase()); + + if (algorithmName === undefined) { + throw new DOMException( + "Unrecognized algorithm name", + "NotSupportedError", + ); + } + + // TODO(caspervonb) Step 6 (create from webidl definition), when the need arises. + // See https://www.w3.org/TR/WebCryptoAPI/#dfn-normalize-an-algorithm + const normalizedAlgorithm = {}; + normalizedAlgorithm.name = algorithmName; + + // TODO(caspervonb) Step 9 and 10, when the need arises. + // See https://www.w3.org/TR/WebCryptoAPI/#dfn-normalize-an-algorithm + return normalizedAlgorithm; + } + + // Should match op_crypto_subtle_digest() in extensions/crypto/lib.rs + function digestToId(name) { + switch (name) { + case "SHA-1": + return 0; + case "SHA-256": + return 1; + case "SHA-384": + return 2; + case "SHA-512": + return 3; + } + } + + class SubtleCrypto { + constructor() { + webidl.illegalConstructor(); + } + + async digest(algorithm, data) { + const prefix = "Failed to execute 'digest' on 'SubtleCrypto'"; + + webidl.assertBranded(this, SubtleCrypto); + webidl.requiredArguments(arguments.length, 2); + + algorithm = webidl.converters.AlgorithmIdentifier(algorithm, { + prefix, + context: "Argument 1", + }); + + data = webidl.converters.BufferSource(data, { + prefix, + context: "Argument 2", + }); + + if (ArrayBuffer.isView(data)) { + data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength); + } else { + data = new Uint8Array(data); + } + + data = data.slice(); + + algorithm = normalizeAlgorithm(algorithm, "digest"); + + const result = await core.opAsync( + "op_crypto_subtle_digest", + digestToId(algorithm.name), + data, + ); + + return result.buffer; + } + } + + const subtle = webidl.createBranded(SubtleCrypto); + class Crypto { constructor() { webidl.illegalConstructor(); @@ -48,6 +144,11 @@ return core.opSync("op_crypto_random_uuid"); } + get subtle() { + webidl.assertBranded(this, Crypto); + return subtle; + } + get [Symbol.toStringTag]() { return "Crypto"; } @@ -57,7 +158,13 @@ } } + Object.defineProperty(Crypto.prototype, "subtle", { + configurable: true, + enumerable: true, + }); + window.__bootstrap.crypto = { + SubtleCrypto, crypto: webidl.createBranded(Crypto), Crypto, }; diff --git a/extensions/crypto/Cargo.toml b/extensions/crypto/Cargo.toml index b263b6014..3f4ea1e1a 100644 --- a/extensions/crypto/Cargo.toml +++ b/extensions/crypto/Cargo.toml @@ -16,5 +16,7 @@ path = "lib.rs" [dependencies] deno_core = { version = "0.88.1", path = "../../core" } deno_web = { version = "0.38.1", path = "../web" } +tokio = { version = "1.6.1", features = ["full"] } rand = "0.8.3" +ring = "0.16.20" uuid = { version = "0.8.2", features = ["v4"] } diff --git a/extensions/crypto/lib.deno_crypto.d.ts b/extensions/crypto/lib.deno_crypto.d.ts index 854696676..c787f5e3c 100644 --- a/extensions/crypto/lib.deno_crypto.d.ts +++ b/extensions/crypto/lib.deno_crypto.d.ts @@ -6,7 +6,7 @@ declare var crypto: Crypto; declare interface Crypto { - readonly subtle: null; + readonly subtle: SubtleCrypto; getRandomValues< T extends | Int8Array @@ -25,3 +25,33 @@ declare interface Crypto { ): T; randomUUID(): string; } + +interface Algorithm { + name: string; +} + +/** This Web Crypto API interface provides a number of low-level cryptographic functions. It is accessed via the Crypto.subtle properties available in a window context (via Window.crypto). */ +interface SubtleCrypto { + digest( + algorithm: AlgorithmIdentifier, + data: + | Int8Array + | Int16Array + | Int32Array + | Uint8Array + | Uint16Array + | Uint32Array + | Uint8ClampedArray + | Float32Array + | Float64Array + | DataView + | ArrayBuffer, + ): Promise<ArrayBuffer>; +} + +declare var SubtleCrypto: { + prototype: SubtleCrypto; + new (): SubtleCrypto; +}; + +type AlgorithmIdentifier = string | Algorithm; diff --git a/extensions/crypto/lib.rs b/extensions/crypto/lib.rs index 0ec7f4717..9d71cc761 100644 --- a/extensions/crypto/lib.rs +++ b/extensions/crypto/lib.rs @@ -1,7 +1,9 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +use deno_core::error::null_opbuf; use deno_core::error::AnyError; use deno_core::include_js_files; +use deno_core::op_async; use deno_core::op_sync; use deno_core::Extension; use deno_core::OpState; @@ -10,7 +12,10 @@ use rand::rngs::StdRng; use rand::thread_rng; use rand::Rng; use rand::SeedableRng; +use ring::digest; +use std::cell::RefCell; use std::path::PathBuf; +use std::rc::Rc; pub use rand; // Re-export rand @@ -18,6 +23,7 @@ pub fn init(maybe_seed: Option<u64>) -> Extension { Extension::builder() .js(include_js_files!( prefix "deno:extensions/crypto", + "00_webidl.js", "01_crypto.js", )) .ops(vec![ @@ -25,6 +31,7 @@ pub fn init(maybe_seed: Option<u64>) -> Extension { "op_crypto_get_random_values", op_sync(op_crypto_get_random_values), ), + ("op_crypto_subtle_digest", op_async(op_crypto_subtle_digest)), ("op_crypto_random_uuid", op_sync(op_crypto_random_uuid)), ]) .state(move |state| { @@ -78,6 +85,28 @@ pub fn op_crypto_random_uuid( Ok(uuid.to_string()) } +pub async fn op_crypto_subtle_digest( + _state: Rc<RefCell<OpState>>, + algorithm_id: i8, + data: Option<ZeroCopyBuf>, +) -> Result<ZeroCopyBuf, AnyError> { + let algorithm = match algorithm_id { + 0 => &digest::SHA1_FOR_LEGACY_USE_ONLY, + 1 => &digest::SHA256, + 2 => &digest::SHA384, + 3 => &digest::SHA512, + _ => panic!("Invalid algorithm id"), + }; + + let input = data.ok_or_else(null_opbuf)?; + let output = tokio::task::spawn_blocking(move || { + digest::digest(algorithm, &input).as_ref().to_vec().into() + }) + .await?; + + Ok(output) +} + pub fn get_declaration() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_crypto.d.ts") } |