summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCasper Beyer <caspervonb@pm.me>2021-06-06 18:57:10 +0800
committerGitHub <noreply@github.com>2021-06-06 12:57:10 +0200
commit3f9187c366be362a219274ded5be9e679b96af98 (patch)
tree75dae6273afd4fa8fb300360df18bcbd2914edfc
parent633c5aab1f28538dde619fe755f8ec9fa77a3719 (diff)
feat(extensions/crypto): implement subtle.digest (#10796)
Co-authored-by: Yacine Hmito yacinehmito@users.noreply.github.com
-rw-r--r--Cargo.lock2
-rw-r--r--extensions/crypto/00_webidl.js26
-rw-r--r--extensions/crypto/01_crypto.js107
-rw-r--r--extensions/crypto/Cargo.toml2
-rw-r--r--extensions/crypto/lib.deno_crypto.d.ts32
-rw-r--r--extensions/crypto/lib.rs29
-rw-r--r--runtime/js/99_main.js1
-rw-r--r--tools/wpt/expectation.json20
8 files changed, 204 insertions, 15 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d313c3f1d..211ce5469 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -646,6 +646,8 @@ dependencies = [
"deno_core",
"deno_web",
"rand 0.8.3",
+ "ring",
+ "tokio",
"uuid",
]
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")
}
diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js
index 6c79468d1..f196f3008 100644
--- a/runtime/js/99_main.js
+++ b/runtime/js/99_main.js
@@ -314,6 +314,7 @@ delete Object.prototype.__proto__;
),
crypto: util.readOnly(crypto.crypto),
Crypto: util.nonEnumerable(crypto.Crypto),
+ SubtleCrypto: util.nonEnumerable(crypto.SubtleCrypto),
fetch: util.writable(fetch.fetch),
performance: util.writable(performance.performance),
setInterval: util.writable(timers.setInterval),
diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json
index d87017c9e..d724b6b5c 100644
--- a/tools/wpt/expectation.json
+++ b/tools/wpt/expectation.json
@@ -19,7 +19,7 @@
"pbkdf2.https.any.html?8001-last": false
},
"digest": {
- "digest.https.any.html": false
+ "digest.https.any.html": true
},
"encrypt_decrypt": {
"aes_cbc.https.any.html": false,
@@ -70,11 +70,12 @@
"successes_RSASSA-PKCS1-v1_5.https.any.html?21-30": false,
"successes_RSASSA-PKCS1-v1_5.https.any.html?31-last": false
},
- "historical.any.html": true,
+ "historical.any.html": [
+ "Non-secure context window does not have access to crypto.subtle",
+ "Non-secure context window does not have access to SubtleCrypto"
+ ],
"idlharness.https.any.html": [
- "Crypto interface: attribute subtle",
"Crypto interface: operation getRandomValues(ArrayBufferView)",
- "Crypto interface: crypto must inherit property \"subtle\" with the proper type",
"CryptoKey interface: existence and properties of interface object",
"CryptoKey interface object length",
"CryptoKey interface object name",
@@ -85,12 +86,6 @@
"CryptoKey interface: attribute extractable",
"CryptoKey interface: attribute algorithm",
"CryptoKey interface: attribute usages",
- "SubtleCrypto interface: existence and properties of interface object",
- "SubtleCrypto interface object length",
- "SubtleCrypto interface object name",
- "SubtleCrypto interface: existence and properties of interface prototype object",
- "SubtleCrypto interface: existence and properties of interface prototype object's \"constructor\" property",
- "SubtleCrypto interface: existence and properties of interface prototype object's @@unscopables property",
"SubtleCrypto interface: operation encrypt(AlgorithmIdentifier, CryptoKey, BufferSource)",
"SubtleCrypto interface: operation decrypt(AlgorithmIdentifier, CryptoKey, BufferSource)",
"SubtleCrypto interface: operation sign(AlgorithmIdentifier, CryptoKey, BufferSource)",
@@ -103,7 +98,6 @@
"SubtleCrypto interface: operation exportKey(KeyFormat, CryptoKey)",
"SubtleCrypto interface: operation wrapKey(KeyFormat, CryptoKey, CryptoKey, AlgorithmIdentifier)",
"SubtleCrypto interface: operation unwrapKey(KeyFormat, BufferSource, CryptoKey, AlgorithmIdentifier, AlgorithmIdentifier, boolean, sequence<KeyUsage>)",
- "SubtleCrypto must be primary interface of crypto.subtle",
"Stringification of crypto.subtle",
"SubtleCrypto interface: crypto.subtle must inherit property \"encrypt(AlgorithmIdentifier, CryptoKey, BufferSource)\" with the proper type",
"SubtleCrypto interface: calling encrypt(AlgorithmIdentifier, CryptoKey, BufferSource) on crypto.subtle with too few arguments must throw TypeError",
@@ -113,8 +107,6 @@
"SubtleCrypto interface: calling sign(AlgorithmIdentifier, CryptoKey, BufferSource) on crypto.subtle with too few arguments must throw TypeError",
"SubtleCrypto interface: crypto.subtle must inherit property \"verify(AlgorithmIdentifier, CryptoKey, BufferSource, BufferSource)\" with the proper type",
"SubtleCrypto interface: calling verify(AlgorithmIdentifier, CryptoKey, BufferSource, BufferSource) on crypto.subtle with too few arguments must throw TypeError",
- "SubtleCrypto interface: crypto.subtle must inherit property \"digest(AlgorithmIdentifier, BufferSource)\" with the proper type",
- "SubtleCrypto interface: calling digest(AlgorithmIdentifier, BufferSource) on crypto.subtle with too few arguments must throw TypeError",
"SubtleCrypto interface: crypto.subtle must inherit property \"generateKey(AlgorithmIdentifier, boolean, sequence<KeyUsage>)\" with the proper type",
"SubtleCrypto interface: calling generateKey(AlgorithmIdentifier, boolean, sequence<KeyUsage>) on crypto.subtle with too few arguments must throw TypeError",
"SubtleCrypto interface: crypto.subtle must inherit property \"deriveKey(AlgorithmIdentifier, CryptoKey, AlgorithmIdentifier, boolean, sequence<KeyUsage>)\" with the proper type",
@@ -1218,4 +1210,4 @@
"set.any.html": true
}
}
-} \ No newline at end of file
+}