1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-11 16:42:21 -05:00

feat(extensions/crypto): implement subtle.digest (#10796)

Co-authored-by: Yacine Hmito yacinehmito@users.noreply.github.com
This commit is contained in:
Casper Beyer 2021-06-06 18:57:10 +08:00 committed by GitHub
parent 633c5aab1f
commit 3f9187c366
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 204 additions and 15 deletions

2
Cargo.lock generated
View file

@ -646,6 +646,8 @@ dependencies = [
"deno_core", "deno_core",
"deno_web", "deno_web",
"rand 0.8.3", "rand 0.8.3",
"ring",
"tokio",
"uuid", "uuid",
] ]

View file

@ -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);

View file

@ -5,6 +5,102 @@
const core = window.Deno.core; const core = window.Deno.core;
const webidl = window.__bootstrap.webidl; 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 { class Crypto {
constructor() { constructor() {
webidl.illegalConstructor(); webidl.illegalConstructor();
@ -48,6 +144,11 @@
return core.opSync("op_crypto_random_uuid"); return core.opSync("op_crypto_random_uuid");
} }
get subtle() {
webidl.assertBranded(this, Crypto);
return subtle;
}
get [Symbol.toStringTag]() { get [Symbol.toStringTag]() {
return "Crypto"; return "Crypto";
} }
@ -57,7 +158,13 @@
} }
} }
Object.defineProperty(Crypto.prototype, "subtle", {
configurable: true,
enumerable: true,
});
window.__bootstrap.crypto = { window.__bootstrap.crypto = {
SubtleCrypto,
crypto: webidl.createBranded(Crypto), crypto: webidl.createBranded(Crypto),
Crypto, Crypto,
}; };

View file

@ -16,5 +16,7 @@ path = "lib.rs"
[dependencies] [dependencies]
deno_core = { version = "0.88.1", path = "../../core" } deno_core = { version = "0.88.1", path = "../../core" }
deno_web = { version = "0.38.1", path = "../web" } deno_web = { version = "0.38.1", path = "../web" }
tokio = { version = "1.6.1", features = ["full"] }
rand = "0.8.3" rand = "0.8.3"
ring = "0.16.20"
uuid = { version = "0.8.2", features = ["v4"] } uuid = { version = "0.8.2", features = ["v4"] }

View file

@ -6,7 +6,7 @@
declare var crypto: Crypto; declare var crypto: Crypto;
declare interface Crypto { declare interface Crypto {
readonly subtle: null; readonly subtle: SubtleCrypto;
getRandomValues< getRandomValues<
T extends T extends
| Int8Array | Int8Array
@ -25,3 +25,33 @@ declare interface Crypto {
): T; ): T;
randomUUID(): string; 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;

View file

@ -1,7 +1,9 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. // 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::error::AnyError;
use deno_core::include_js_files; use deno_core::include_js_files;
use deno_core::op_async;
use deno_core::op_sync; use deno_core::op_sync;
use deno_core::Extension; use deno_core::Extension;
use deno_core::OpState; use deno_core::OpState;
@ -10,7 +12,10 @@ use rand::rngs::StdRng;
use rand::thread_rng; use rand::thread_rng;
use rand::Rng; use rand::Rng;
use rand::SeedableRng; use rand::SeedableRng;
use ring::digest;
use std::cell::RefCell;
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc;
pub use rand; // Re-export rand pub use rand; // Re-export rand
@ -18,6 +23,7 @@ pub fn init(maybe_seed: Option<u64>) -> Extension {
Extension::builder() Extension::builder()
.js(include_js_files!( .js(include_js_files!(
prefix "deno:extensions/crypto", prefix "deno:extensions/crypto",
"00_webidl.js",
"01_crypto.js", "01_crypto.js",
)) ))
.ops(vec![ .ops(vec![
@ -25,6 +31,7 @@ pub fn init(maybe_seed: Option<u64>) -> Extension {
"op_crypto_get_random_values", "op_crypto_get_random_values",
op_sync(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)), ("op_crypto_random_uuid", op_sync(op_crypto_random_uuid)),
]) ])
.state(move |state| { .state(move |state| {
@ -78,6 +85,28 @@ pub fn op_crypto_random_uuid(
Ok(uuid.to_string()) 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 { pub fn get_declaration() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_crypto.d.ts") PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_crypto.d.ts")
} }

View file

@ -314,6 +314,7 @@ delete Object.prototype.__proto__;
), ),
crypto: util.readOnly(crypto.crypto), crypto: util.readOnly(crypto.crypto),
Crypto: util.nonEnumerable(crypto.Crypto), Crypto: util.nonEnumerable(crypto.Crypto),
SubtleCrypto: util.nonEnumerable(crypto.SubtleCrypto),
fetch: util.writable(fetch.fetch), fetch: util.writable(fetch.fetch),
performance: util.writable(performance.performance), performance: util.writable(performance.performance),
setInterval: util.writable(timers.setInterval), setInterval: util.writable(timers.setInterval),

View file

@ -19,7 +19,7 @@
"pbkdf2.https.any.html?8001-last": false "pbkdf2.https.any.html?8001-last": false
}, },
"digest": { "digest": {
"digest.https.any.html": false "digest.https.any.html": true
}, },
"encrypt_decrypt": { "encrypt_decrypt": {
"aes_cbc.https.any.html": false, "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?21-30": false,
"successes_RSASSA-PKCS1-v1_5.https.any.html?31-last": 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": [ "idlharness.https.any.html": [
"Crypto interface: attribute subtle",
"Crypto interface: operation getRandomValues(ArrayBufferView)", "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: existence and properties of interface object",
"CryptoKey interface object length", "CryptoKey interface object length",
"CryptoKey interface object name", "CryptoKey interface object name",
@ -85,12 +86,6 @@
"CryptoKey interface: attribute extractable", "CryptoKey interface: attribute extractable",
"CryptoKey interface: attribute algorithm", "CryptoKey interface: attribute algorithm",
"CryptoKey interface: attribute usages", "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 encrypt(AlgorithmIdentifier, CryptoKey, BufferSource)",
"SubtleCrypto interface: operation decrypt(AlgorithmIdentifier, CryptoKey, BufferSource)", "SubtleCrypto interface: operation decrypt(AlgorithmIdentifier, CryptoKey, BufferSource)",
"SubtleCrypto interface: operation sign(AlgorithmIdentifier, CryptoKey, BufferSource)", "SubtleCrypto interface: operation sign(AlgorithmIdentifier, CryptoKey, BufferSource)",
@ -103,7 +98,6 @@
"SubtleCrypto interface: operation exportKey(KeyFormat, CryptoKey)", "SubtleCrypto interface: operation exportKey(KeyFormat, CryptoKey)",
"SubtleCrypto interface: operation wrapKey(KeyFormat, CryptoKey, CryptoKey, AlgorithmIdentifier)", "SubtleCrypto interface: operation wrapKey(KeyFormat, CryptoKey, CryptoKey, AlgorithmIdentifier)",
"SubtleCrypto interface: operation unwrapKey(KeyFormat, BufferSource, CryptoKey, AlgorithmIdentifier, AlgorithmIdentifier, boolean, sequence<KeyUsage>)", "SubtleCrypto interface: operation unwrapKey(KeyFormat, BufferSource, CryptoKey, AlgorithmIdentifier, AlgorithmIdentifier, boolean, sequence<KeyUsage>)",
"SubtleCrypto must be primary interface of crypto.subtle",
"Stringification of crypto.subtle", "Stringification of crypto.subtle",
"SubtleCrypto interface: crypto.subtle must inherit property \"encrypt(AlgorithmIdentifier, CryptoKey, BufferSource)\" with the proper type", "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", "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: 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: 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: 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: 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: 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", "SubtleCrypto interface: crypto.subtle must inherit property \"deriveKey(AlgorithmIdentifier, CryptoKey, AlgorithmIdentifier, boolean, sequence<KeyUsage>)\" with the proper type",