mirror of
https://github.com/denoland/deno.git
synced 2025-01-11 08:33:43 -05:00
feat(extensions/crypto): implement subtle.digest (#10796)
Co-authored-by: Yacine Hmito yacinehmito@users.noreply.github.com
This commit is contained in:
parent
633c5aab1f
commit
3f9187c366
8 changed files with 204 additions and 15 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -646,6 +646,8 @@ dependencies = [
|
|||
"deno_core",
|
||||
"deno_web",
|
||||
"rand 0.8.3",
|
||||
"ring",
|
||||
"tokio",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
|
|
26
extensions/crypto/00_webidl.js
Normal file
26
extensions/crypto/00_webidl.js
Normal 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);
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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"] }
|
||||
|
|
32
extensions/crypto/lib.deno_crypto.d.ts
vendored
32
extensions/crypto/lib.deno_crypto.d.ts
vendored
|
@ -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;
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue