Skip to content

Commit

Permalink
Introduce web crypto API
Browse files Browse the repository at this point in the history
  • Loading branch information
yacinehmito committed Jan 9, 2021
1 parent 2c7d510 commit d219dc5
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 6 deletions.
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

130 changes: 129 additions & 1 deletion op_crates/crypto/01_crypto.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.

// Implements https://www.w3.org/TR/WebCryptoAPI

((window) => {
const core = window.Deno.core;

Expand Down Expand Up @@ -36,15 +38,141 @@
arrayBufferView.byteOffset,
arrayBufferView.byteLength,
);
core.jsonOpSync("op_get_random_values", {}, ui8);
core.jsonOpSync("op_crypto_get_random_values", {}, ui8);
return arrayBufferView;
}

// Algorithm normalization, which involves storing the expected data for
// each pair of algorithm and crypto operation, is done on the JS side because
// it is convenient.
// Shall it stay that way, or are we better to move it on the Rust side?

// We need this method after initialization, anytime we need to normalize
// a provided algorithm. We store it here to prevent prototype pollution.
const toUpperCase = String.prototype.toUpperCase;

class RegisteredAlgorithmsContainer {
#nameIndex;
#definitions;

constructor(definitions) {
this.#nameIndex = Object.create(null);
this.#definitions = Object.create(null);
for (const [name, definition] of Object.entries(definitions)) {
this.#nameIndex[name.toUpperCase()] = name;
this.#definitions[name] = definition;
}
}

getDefinition(name) {
return this.#definitions[name];
}

normalizeName(providedName) {
const upperCaseName = toUpperCase.call(providedName);
return this.#nameIndex[upperCaseName];
}
}

const supportedAlgorithms = {};

function normalizeAlgorithm(algorithm, registeredAlgorithms) {
let alg;
if (typeof algorithm === "string") {
alg = { name: algorithm };
}
if (typeof algorithm === "object" && algorithm !== null) {
if (typeof algorithm.name !== "string") {
throw new TypeError("Algorithm name is missing or not a string");
}
const algorithmName = registeredAlgorithms.normalizeName(algorithm.name);
if (algorithmName === undefined) {
throw new DOMException(
"Unrecognized algorithm name",
"NotSupportedError",
);
}
const definition = registeredAlgorithms.getDefinition(algorithmName);
// TODO: Validate the algorithm data based on the definition
alg = { name: algorithmName };
} else {
throw new TypeError("Argument 1 must be an object or a string");
}
return alg;
}

const subtle = {
async decrypt(algorithm, key, data) {
await Promise.resolve();
throw new Error("Not implemented");
},
async deriveBits(algorithm, baseKey, length) {
await Promise.resolve();
throw new Error("Not implemented");
},
async deriveKey(
algorithm,
baseKey,
derivedKeyType,
extractable,
keyUsages,
) {
await Promise.resolve();
throw new Error("Not implemented");
},
async digest(algorithm, data) {
await Promise.resolve();
throw new Error("Not implemented");
},
async encrypt(algorithm, key, data) {
await Promise.resolve();
throw new Error("Not implemented");
},
async exportKey(format, key) {
await Promise.resolve();
throw new Error("Not implemented");
},
async generateKey(algorithm, extractable, keyUsages) {
await Promise.resolve();
throw new Error("Not implemented");
},
async importKey(format, keyData, algorithm, extractable, keyUsages) {
await Promise.resolve();
throw new Error("Not implemented");
},
async sign(algorithm, key, data) {
await Promise.resolve();
throw new Error("Not implemented");
},
async unwrapKey(
format,
wrappedKey,
unwrappingKey,
unwrapAlgorithm,
unwrappedKeyAlgorithm,
extractable,
keyUsages,
) {
await Promise.resolve();
throw new Error("Not implemented");
},
async verify(algorithm, key, signature, data) {
await Promise.resolve();
throw new Error("Not implemented");
},
async wrapKey(format, key, wrappingKey, wrapAlgorithm) {
await Promise.resolve();
throw new Error("Not implemented");
},
};

window.crypto = {
getRandomValues,
subtle,
};
window.__bootstrap = window.__bootstrap || {};
window.__bootstrap.crypto = {
getRandomValues,
subtle,
};
})(this);
7 changes: 5 additions & 2 deletions op_crates/crypto/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.

[package]
name = "deno_crypto"
version = "0.9.0"
Expand All @@ -16,4 +15,8 @@ path = "lib.rs"
[dependencies]
deno_core = { version = "0.75.0", path = "../../core" }
rand = "0.7.3"

ring = "0.16.19"
lazy_static = "1.4.0"
tokio = { version = "0.2.22", features = ["full"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
serde = { version = "1.0.116", features = ["derive"] }
2 changes: 1 addition & 1 deletion op_crates/crypto/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub fn init(isolate: &mut JsRuntime) {
}
}

pub fn op_get_random_values(
pub fn op_crypto_get_random_values(
state: &mut OpState,
_args: Value,
zero_copy: &mut [ZeroCopyBuf],
Expand Down
8 changes: 6 additions & 2 deletions runtime/ops/crypto.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use deno_crypto::op_get_random_values;
use deno_crypto::rand::rngs::StdRng;
use deno_crypto::rand::SeedableRng;

Expand All @@ -10,5 +9,10 @@ pub fn init(rt: &mut deno_core::JsRuntime, maybe_seed: Option<u64>) {
let mut state = op_state.borrow_mut();
state.put::<StdRng>(rng);
}
super::reg_json_sync(rt, "op_get_random_values", op_get_random_values);
super::reg_json_sync(
rt,
"op_crypto_get_random_values",
deno_crypto::op_crypto_get_random_values,
);
super::reg_json_async(rt, "op_crypto_subtle_digest", deno_crypto::op_crypto_subtle_digest);
}

0 comments on commit d219dc5

Please sign in to comment.