diff --git a/Cargo.lock b/Cargo.lock
index 4ffb40c285ff93..951249e204ab4c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -175,6 +175,12 @@ dependencies = [
"syn 1.0.65",
]
+[[package]]
+name = "autocfg"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
+
[[package]]
name = "autocfg"
version = "1.0.1"
@@ -443,7 +449,7 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278"
dependencies = [
- "autocfg",
+ "autocfg 1.0.1",
"cfg-if 1.0.0",
"lazy_static",
]
@@ -646,8 +652,14 @@ version = "0.24.1"
dependencies = [
"deno_core",
"deno_web",
+ "lazy_static",
+ "num-traits",
"rand 0.8.4",
"ring",
+ "rsa",
+ "serde",
+ "sha-1",
+ "sha2",
"tokio",
"uuid",
]
@@ -953,7 +965,7 @@ checksum = "cd5de0d5e8bdbbad4f6d71d754164803ba4aef42f50db2e65c8a5ca4185ac69f"
dependencies = [
"bumpalo",
"fnv",
- "num-bigint",
+ "num-bigint 0.2.6",
"swc_atoms",
"swc_common",
"swc_ecmascript",
@@ -1247,7 +1259,7 @@ version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121"
dependencies = [
- "autocfg",
+ "autocfg 1.0.1",
"proc-macro-hack",
"proc-macro2 1.0.26",
"quote 1.0.9",
@@ -1272,7 +1284,7 @@ version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967"
dependencies = [
- "autocfg",
+ "autocfg 1.0.1",
"futures-channel",
"futures-core",
"futures-io",
@@ -1724,7 +1736,7 @@ version = "1.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
dependencies = [
- "autocfg",
+ "autocfg 1.0.1",
"hashbrown 0.9.1",
"serde",
]
@@ -1862,6 +1874,9 @@ name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+dependencies = [
+ "spin",
+]
[[package]]
name = "lexical"
@@ -1902,6 +1917,12 @@ dependencies = [
"winapi 0.3.9",
]
+[[package]]
+name = "libm"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
+
[[package]]
name = "libsqlite3-sys"
version = "0.22.2"
@@ -2051,7 +2072,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
"adler",
- "autocfg",
+ "autocfg 1.0.1",
]
[[package]]
@@ -2153,19 +2174,59 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
dependencies = [
- "autocfg",
+ "autocfg 1.0.1",
"num-integer",
"num-traits",
"serde",
]
+[[package]]
+name = "num-bigint"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512"
+dependencies = [
+ "autocfg 1.0.1",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-bigint-dig"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4547ee5541c18742396ae2c895d0717d0f886d8823b8399cdaf7b07d63ad0480"
+dependencies = [
+ "autocfg 0.1.7",
+ "byteorder",
+ "lazy_static",
+ "libm",
+ "num-integer",
+ "num-iter",
+ "num-traits",
+ "rand 0.8.4",
+ "smallvec",
+ "zeroize",
+]
+
[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
- "autocfg",
+ "autocfg 1.0.1",
+ "num-traits",
+]
+
+[[package]]
+name = "num-iter"
+version = "0.1.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
+dependencies = [
+ "autocfg 1.0.1",
+ "num-integer",
"num-traits",
]
@@ -2175,7 +2236,8 @@ version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
- "autocfg",
+ "autocfg 1.0.1",
+ "libm",
]
[[package]]
@@ -2263,6 +2325,17 @@ dependencies = [
"winapi 0.3.9",
]
+[[package]]
+name = "pem"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb"
+dependencies = [
+ "base64 0.13.0",
+ "once_cell",
+ "regex",
+]
+
[[package]]
name = "percent-encoding"
version = "2.1.0"
@@ -2741,6 +2814,26 @@ dependencies = [
"serde",
]
+[[package]]
+name = "rsa"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68ef841a26fc5d040ced0417c6c6a64ee851f42489df11cdf0218e545b6f8d28"
+dependencies = [
+ "byteorder",
+ "digest",
+ "lazy_static",
+ "num-bigint-dig",
+ "num-integer",
+ "num-iter",
+ "num-traits",
+ "pem",
+ "rand 0.8.4",
+ "simple_asn1",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "rusqlite"
version = "0.25.3"
@@ -2962,6 +3055,19 @@ dependencies = [
"opaque-debug",
]
+[[package]]
+name = "sha2"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12"
+dependencies = [
+ "block-buffer",
+ "cfg-if 1.0.0",
+ "cpufeatures",
+ "digest",
+ "opaque-debug",
+]
+
[[package]]
name = "shell-escape"
version = "0.1.5"
@@ -2977,6 +3083,18 @@ dependencies = [
"libc",
]
+[[package]]
+name = "simple_asn1"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eb4ea60fb301dc81dfc113df680571045d375ab7345d171c5dc7d7e13107a80"
+dependencies = [
+ "chrono",
+ "num-bigint 0.4.0",
+ "num-traits",
+ "thiserror",
+]
+
[[package]]
name = "siphasher"
version = "0.3.5"
@@ -3136,6 +3254,12 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
+[[package]]
+name = "subtle"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
+
[[package]]
name = "swc_atoms"
version = "0.2.6"
@@ -3187,7 +3311,7 @@ dependencies = [
"from_variant",
"fxhash",
"log",
- "num-bigint",
+ "num-bigint 0.2.6",
"once_cell",
"owning_ref",
"scoped-tls",
@@ -3206,7 +3330,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8060c27a8932c57e5a878acc1c64b44371cce1c88eb3af1f3d12a2628d606a6"
dependencies = [
"is-macro",
- "num-bigint",
+ "num-bigint 0.2.6",
"serde",
"string_enum",
"swc_atoms",
@@ -3220,7 +3344,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936fd096ebfc8d8c8d7f5cfdc79b13d06e56e0810abbe3de37376b537a40debb"
dependencies = [
"bitflags",
- "num-bigint",
+ "num-bigint 0.2.6",
"sourcemap",
"swc_atoms",
"swc_common",
@@ -3278,7 +3402,7 @@ dependencies = [
"fxhash",
"lexical",
"log",
- "num-bigint",
+ "num-bigint 0.2.6",
"serde",
"smallvec",
"swc_atoms",
@@ -3444,7 +3568,7 @@ version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d5b93c94bd4fca51ad5c2588e34b46dc5a9b1f079c9b1ec595a968eb4ca8acf"
dependencies = [
- "num-bigint",
+ "num-bigint 0.2.6",
"swc_atoms",
"swc_common",
"swc_ecma_ast",
@@ -3536,6 +3660,18 @@ dependencies = [
"unicode-xid 0.2.2",
]
+[[package]]
+name = "synstructure"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
+dependencies = [
+ "proc-macro2 1.0.26",
+ "quote 1.0.9",
+ "syn 1.0.65",
+ "unicode-xid 0.2.2",
+]
+
[[package]]
name = "sys-info"
version = "0.9.0"
@@ -3672,7 +3808,7 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "570c2eb13b3ab38208130eccd41be92520388791207fde783bda7c1e8ace28d4"
dependencies = [
- "autocfg",
+ "autocfg 1.0.1",
"bytes",
"libc",
"memchr",
@@ -4340,3 +4476,24 @@ checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5"
dependencies = [
"winapi 0.3.9",
]
+
+[[package]]
+name = "zeroize"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81a974bcdd357f0dca4d41677db03436324d45a4c9ed2d0b873a5a360ce41c36"
+dependencies = [
+ "zeroize_derive",
+]
+
+[[package]]
+name = "zeroize_derive"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3f369ddb18862aba61aa49bf31e74d29f0f162dec753063200e1dc084345d16"
+dependencies = [
+ "proc-macro2 1.0.26",
+ "quote 1.0.9",
+ "syn 1.0.65",
+ "synstructure",
+]
diff --git a/cli/tests/unit/webcrypto_test.ts b/cli/tests/unit/webcrypto_test.ts
new file mode 100644
index 00000000000000..4361d3a10b28ba
--- /dev/null
+++ b/cli/tests/unit/webcrypto_test.ts
@@ -0,0 +1,58 @@
+import { assert, assertEquals, unitTest } from "./test_util.ts";
+
+unitTest(async function testGenerateRSAKey() {
+ const subtle = window.crypto.subtle;
+ assert(subtle);
+
+ const keyPair = await subtle.generateKey(
+ {
+ name: "RSA-PSS",
+ modulusLength: 2048,
+ publicExponent: new Uint8Array([1, 0, 1]),
+ hash: "SHA-256",
+ },
+ true,
+ ["sign", "verify"],
+ );
+
+ assert(keyPair.privateKey);
+ assert(keyPair.publicKey);
+ assertEquals(keyPair.privateKey.extractable, true);
+ assert(keyPair.privateKey.usages.includes("sign"));
+});
+
+unitTest(async function testGenerateHMACKey() {
+ const key = await window.crypto.subtle.generateKey(
+ {
+ name: "HMAC",
+ hash: "SHA-512",
+ },
+ true,
+ ["sign", "verify"],
+ );
+
+ assert(key);
+ assertEquals(key.extractable, true);
+ assert(key.usages.includes("sign"));
+});
+
+unitTest(async function testSignECDSA() {
+ const key = await window.crypto.subtle.generateKey(
+ {
+ name: "ECDSA",
+ namedCurve: "P-384",
+ },
+ true,
+ ["sign", "verify"],
+ );
+
+ const encoder = new TextEncoder();
+ const encoded = encoder.encode("Hello, World!");
+ const signature = await window.crypto.subtle.sign(
+ { name: "ECDSA", hash: "SHA-384" },
+ key.privateKey,
+ encoded,
+ );
+
+ assert(signature);
+});
diff --git a/extensions/crypto/00_crypto.js b/extensions/crypto/00_crypto.js
new file mode 100644
index 00000000000000..a6c6c07225649b
--- /dev/null
+++ b/extensions/crypto/00_crypto.js
@@ -0,0 +1,675 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+// @ts-check
+///
+///
+///
+
+"use strict";
+
+((window) => {
+ const core = window.Deno.core;
+ const webidl = window.__bootstrap.webidl;
+ const { DOMException } = window.__bootstrap.domException;
+
+ // P-521 is not yet supported.
+ const supportedNamedCurves = ["P-256", "P-384"];
+
+ const simpleAlgorithmDictionaries = {
+ RsaHashedKeyGenParams: { hash: "HashAlgorithmIdentifier" },
+ EcKeyGenParams: {},
+ HmacKeyGenParams: { hash: "HashAlgorithmIdentifier" },
+ RsaPssParams: {},
+ EcdsaParams: { hash: "HashAlgorithmIdentifier" },
+ };
+
+ const supportedAlgorithms = {
+ "digest": {
+ "SHA-1": null,
+ "SHA-256": null,
+ "SHA-384": null,
+ "SHA-512": null,
+ },
+ "generateKey": {
+ "RSASSA-PKCS1-v1_5": "RsaHashedKeyGenParams",
+ "RSA-PSS": "RsaHashedKeyGenParams",
+ "ECDSA": "EcKeyGenParams",
+ "HMAC": "HmacKeyGenParams",
+ },
+ "sign": {
+ "RSASSA-PKCS1-v1_5": null,
+ "RSA-PSS": "RsaPssParams",
+ "ECDSA": "EcdsaParams",
+ "HMAC": null,
+ },
+ };
+
+ // See https://www.w3.org/TR/WebCryptoAPI/#dfn-normalize-an-algorithm
+ function normalizeAlgorithm(algorithm, op) {
+ if (typeof algorithm == "string") {
+ return normalizeAlgorithm({ name: algorithm }, op);
+ }
+
+ // 1.
+ const registeredAlgorithms = supportedAlgorithms[op];
+ // 2. 3.
+ const initialAlg = webidl.converters.Algorithm(algorithm, {
+ prefix: "Failed to normalize algorithm",
+ context: "passed algorithm",
+ });
+ // 4.
+ let algName = initialAlg.name;
+
+ // 5.
+ let desiredType = undefined;
+ for (const key in registeredAlgorithms) {
+ if (key.toLowerCase() === algName.toLowerCase()) {
+ algName = key;
+ desiredType = registeredAlgorithms[key];
+ }
+ }
+ if (desiredType === undefined) {
+ throw new DOMException(
+ "Unrecognized algorithm name",
+ "NotSupportedError",
+ );
+ }
+
+ // Fast path everything below if the registered dictionary is "None".
+ if (desiredType === null) {
+ return { name: algName };
+ }
+
+ const normalizedAlgorithm = webidl.converters[desiredType](algorithm, {
+ prefix: "Failed to normalize algorithm",
+ context: "passed algorithm",
+ });
+ normalizedAlgorithm.name = algName;
+
+ const dict = simpleAlgorithmDictionaries[desiredType];
+ for (const member in dict) {
+ const idlType = dict[member];
+ const idlValue = normalizedAlgorithm[member];
+
+ if (idlType === "BufferSource") {
+ normalizedAlgorithm[member] = new Uint8Array(
+ (ArrayBuffer.isView(idlValue) ? idlValue.buffer : idlValue).slice(
+ idlValue.byteOffset ?? 0,
+ idlValue.byteLength,
+ ),
+ );
+ } else if (idlType === "HashAlgorithmIdentifier") {
+ normalizedAlgorithm[member] = normalizeAlgorithm(idlValue, "digest");
+ } else if (idlType === "AlgorithmIdentifier") {
+ // TODO(lucacasonato): implement
+ throw new TypeError("unimplemented");
+ }
+ }
+
+ 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;
+ }
+ }
+
+ const _handle = Symbol("[[handle]]");
+ const _algorithm = Symbol("[[algorithm]]");
+ const _extractable = Symbol("[[extractable]]");
+ const _usages = Symbol("[[usages]]");
+ const _type = Symbol("[[type]]");
+
+ class CryptoKey {
+ /** @type {string} */
+ [_type];
+ /** @type {boolean} */
+ [_extractable];
+ /** @type {object} */
+ [_algorithm];
+ /** @type {string[]} */
+ [_usages];
+ /** @type {object} */
+ [_handle];
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /** @returns {string} */
+ get type() {
+ webidl.assertBranded(this, CryptoKey);
+ return this[_type];
+ }
+
+ /** @returns {boolean} */
+ get extractable() {
+ webidl.assertBranded(this, CryptoKey);
+ return this[_extractable];
+ }
+
+ /** @returns {string[]} */
+ get usages() {
+ webidl.assertBranded(this, CryptoKey);
+ // TODO(lucacasonato): return a SameObject copy
+ return this[_usages];
+ }
+
+ /** @returns {object} */
+ get algorithm() {
+ webidl.assertBranded(this, CryptoKey);
+ // TODO(lucacasonato): return a SameObject copy
+ return this[_algorithm];
+ }
+
+ get [Symbol.toStringTag]() {
+ return "CryptoKey";
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ type: this.type,
+ extractable: this.extractable,
+ algorithm: this.algorithm,
+ usages: this.usages,
+ })
+ }`;
+ }
+ }
+
+ webidl.configurePrototype(CryptoKey);
+
+ /**
+ * @param {string} type
+ * @param {boolean} extractable
+ * @param {string[]} usages
+ * @param {object} algorithm
+ * @param {object} handle
+ * @returns
+ */
+ function constructKey(type, extractable, usages, algorithm, handle) {
+ const key = webidl.createBranded(CryptoKey);
+ key[_type] = type;
+ key[_extractable] = extractable;
+ key[_usages] = usages;
+ key[_algorithm] = algorithm;
+ key[_handle] = handle;
+ return key;
+ }
+
+ // https://w3c.github.io/webcrypto/#concept-usage-intersection
+ // TODO(littledivy): When the need arises, make `b` a list.
+ /**
+ * @param {string[]} a
+ * @param {string} b
+ * @returns
+ */
+ function usageIntersection(a, b) {
+ return a.includes(b) ? [b] : [];
+ }
+
+ // TODO(lucacasonato): this should be moved to rust
+ /** @type {WeakMap