mirror of
https://github.com/denoland/std.git
synced 2024-11-21 20:50:22 +00:00
feat(crypto): add std/crypto wrapping and extending runtime WebCrypto (#1025)
This commit is contained in:
parent
5606a10728
commit
662139c097
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@ -96,7 +96,7 @@ jobs:
|
||||
run: |-
|
||||
set -o errexit
|
||||
shopt -s inherit_errexit
|
||||
declare modifications="$(git diff --name-only HEAD~ -- ./hash/_wasm/)"
|
||||
declare modifications="$(git diff --name-only HEAD~ -- ./_wasm_crypto)"
|
||||
declare modified="$([[ "$modifications" ]] && echo true || echo false)"
|
||||
echo "::set-output name=modified::$modified"
|
||||
echo "Hash source modified in this commit? $modified"
|
||||
@ -113,6 +113,7 @@ jobs:
|
||||
# This must match the version in hash/_wasm/rust-toolchain:
|
||||
rust-version: 1.53.0
|
||||
targets: wasm32-unknown-unknown
|
||||
components: rustfmt
|
||||
|
||||
- name: Set up wasm-bindgen-cli
|
||||
run: |-
|
||||
@ -124,7 +125,7 @@ jobs:
|
||||
id: build
|
||||
if: success() && steps.source.outputs.modified == 'true'
|
||||
run: |-
|
||||
./hash/_wasm/build.ts
|
||||
./_wasm_crypto/_build.ts
|
||||
|
||||
set -o errexit
|
||||
shopt -s inherit_errexit
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -6,5 +6,4 @@ package.json
|
||||
package-lock.json
|
||||
.vscode/settings.json
|
||||
**/cov/
|
||||
/hash/_wasm/target
|
||||
/hash/_wasm/out
|
||||
/_wasm_crypto/target
|
||||
|
4
_wasm_crypto/.rustfmt.toml
Normal file
4
_wasm_crypto/.rustfmt.toml
Normal file
@ -0,0 +1,4 @@
|
||||
# Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
max_width = 80
|
||||
tab_spaces = 2
|
||||
edition = "2018"
|
418
_wasm_crypto/Cargo.lock
generated
Normal file
418
_wasm_crypto/Cargo.lock
generated
Normal file
@ -0,0 +1,418 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be4dc07131ffa69b8072d35f5007352af944213cde02545e2103680baed38fcd"
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10a5720225ef5daecf08657f23791354e1685a8c91a4c60c7f3d3b2892f978f4"
|
||||
dependencies = [
|
||||
"crypto-mac 0.8.0",
|
||||
"digest",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blake3"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcd555c66291d5f836dbb6883b48660ece810fe25a31f3bdfb911945dff2691f"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"constant_time_eq",
|
||||
"crypto-mac 0.11.1",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||
dependencies = [
|
||||
"block-padding",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_std_wasm_crypto"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"blake2",
|
||||
"blake3",
|
||||
"derive_more",
|
||||
"digest",
|
||||
"generic-array",
|
||||
"js-sys",
|
||||
"md-5",
|
||||
"ripemd160",
|
||||
"sha-1",
|
||||
"sha2",
|
||||
"sha3",
|
||||
"typenum",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"digest",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
|
||||
dependencies = [
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ripemd160"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"digest",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
|
||||
dependencies = [
|
||||
"pest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.9.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a0c8611594e2ab4ebbf06ec7cbbf0a99450b8570e96cbf5188b5d5f6ef18d81"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"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",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"digest",
|
||||
"keccak",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f"
|
35
_wasm_crypto/Cargo.toml
Normal file
35
_wasm_crypto/Cargo.toml
Normal file
@ -0,0 +1,35 @@
|
||||
# Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "deno_std_wasm_crypto"
|
||||
repository = "https://github.com/denoland/deno_std"
|
||||
authors = ["the Deno authors"]
|
||||
license = "MIT"
|
||||
publish = false
|
||||
version = "0.0.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
opt-level = 3
|
||||
|
||||
[dependencies]
|
||||
blake2 = "0.9.1"
|
||||
derive_more = "0.99.16"
|
||||
digest = "0.9.0"
|
||||
generic-array = "0.14.4"
|
||||
js-sys = "0.3.51"
|
||||
md-5 = "0.9.1"
|
||||
ripemd160 = "0.9.1"
|
||||
sha-1 = "0.9.7"
|
||||
sha2 = "0.9.5"
|
||||
sha3 = "0.9.1"
|
||||
typenum = "1.13.0"
|
||||
wasm-bindgen = "0.2.74"
|
||||
|
||||
[dependencies.blake3]
|
||||
version = "1.0.0"
|
||||
features = ["std", "traits-preview"]
|
25
_wasm_crypto/README.md
Normal file
25
_wasm_crypto/README.md
Normal file
@ -0,0 +1,25 @@
|
||||
`std/_crypto_wasm` is only for internal use, such as by `std/crypto` and
|
||||
`std/node/crypto`. Its interface may not be stable between releases and it
|
||||
should not be imported directly.
|
||||
|
||||
## How to Build
|
||||
|
||||
### Prerequisite
|
||||
|
||||
Requires Rust's WASM target and the wasm-bindgen CLI.
|
||||
|
||||
```sh
|
||||
cargo build --target wasm32-unknown-unknown
|
||||
rustup target add wasm32-unknown-unknown
|
||||
|
||||
# This must match the version in hash/_wasm/Cargo.lock:
|
||||
cargo install -f wasm-bindgen-cli --version 0.2.74
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
```sh
|
||||
deno run --allow-all ./_build.ts
|
||||
```
|
||||
|
||||
This will regenerate `./crypto.js` and `./crypto.wasm.js` from the Rust source.
|
150
_wasm_crypto/_build.ts
Executable file
150
_wasm_crypto/_build.ts
Executable file
@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env -S deno run --allow-run --allow-read --allow-write --allow-env
|
||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
import * as base64 from "../encoding/base64.ts";
|
||||
|
||||
const home = Deno.env.get("HOME");
|
||||
const root = new URL(".", import.meta.url).pathname;
|
||||
|
||||
// Run in the same directory as this script is located.
|
||||
if (new URL(import.meta.url).protocol === "file:") {
|
||||
Deno.chdir(root);
|
||||
} else {
|
||||
console.error("build.ts can only be run locally (from a file: URL).");
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
// Format the Rust code.
|
||||
if (
|
||||
!((await Deno.run({
|
||||
cmd: [
|
||||
"cargo",
|
||||
"fmt",
|
||||
],
|
||||
}).status()).success)
|
||||
) {
|
||||
console.error(`Failed to format the Rust code.`);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
// Compile the Rust code to WASM.
|
||||
if (
|
||||
!((await Deno.run({
|
||||
cmd: [
|
||||
"cargo",
|
||||
"build",
|
||||
"--release",
|
||||
"--target",
|
||||
"wasm32-unknown-unknown",
|
||||
],
|
||||
env: {
|
||||
// eliminate some potential sources of non-determinism
|
||||
SOURCE_DATE_EPOCH: "1600000000",
|
||||
TZ: "UTC",
|
||||
LC_ALL: "C",
|
||||
RUSTFLAGS: `--remap-path-prefix=${root}=. --remap-path-prefix=${home}=~`,
|
||||
},
|
||||
}).status()).success)
|
||||
) {
|
||||
console.error(`Failed to compile the Rust code to WASM.`);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
const copyrightLine = `// Copyright 2018-${
|
||||
new Date().getFullYear()
|
||||
} the Deno authors. All rights reserved. MIT license.`;
|
||||
|
||||
// Generate JavaScript bindings from WASM.
|
||||
if (
|
||||
!((await Deno.run({
|
||||
cmd: [
|
||||
"wasm-bindgen",
|
||||
"./target/wasm32-unknown-unknown/release/deno_std_wasm_crypto.wasm",
|
||||
"--target",
|
||||
"deno",
|
||||
"--weak-refs",
|
||||
"--out-dir",
|
||||
"./target/wasm32-bindgen-deno-js",
|
||||
],
|
||||
}).status()).success)
|
||||
) {
|
||||
console.error(`Failed to generate JavaScript bindings from WASM.`);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
// Encode WASM binary as a TypeScript module.
|
||||
const generatedWasm = await Deno.readFile(
|
||||
"./target/wasm32-bindgen-deno-js/deno_std_wasm_crypto_bg.wasm",
|
||||
);
|
||||
|
||||
// Format WASM binary size with _ thousands separators for human readablity,
|
||||
// so that any changes in size will be clear in diffs.
|
||||
const formattedWasmSize = generatedWasm.length.toString().padStart(
|
||||
Math.ceil(generatedWasm.length.toString().length / 3) * 3,
|
||||
).replace(/...\B/g, "$&_").trim();
|
||||
|
||||
// Generate a hash of the WASM in the format required by subresource integrity.
|
||||
const wasmIntegrity = `sha256-${
|
||||
base64.encode(await crypto.subtle!.digest("SHA-256", generatedWasm))
|
||||
}`;
|
||||
|
||||
const wasmJs = `${copyrightLine}
|
||||
// This file is automatically @generated by _build.ts
|
||||
// It is not intended for manual editing.
|
||||
import * as base64 from "../encoding/base64.ts";
|
||||
|
||||
export const size = ${formattedWasmSize};
|
||||
export const name = "crypto.wasm";
|
||||
export const type = "application/wasm";
|
||||
export const hash = ${JSON.stringify(wasmIntegrity)};
|
||||
export const data = base64.decode("\\\n${
|
||||
base64.encode(generatedWasm).replace(/.{78}/g, "$&\\\n")
|
||||
}\\\n");
|
||||
|
||||
export default data;
|
||||
`;
|
||||
|
||||
// Modify the generated WASM bindings, replacing the runtime fetching of the
|
||||
// WASM binary file with a static TypeScript import of the copy we encoded
|
||||
// above. This eliminates the need for net or read permissions.
|
||||
const generatedScript = await Deno.readTextFile(
|
||||
"./target/wasm32-bindgen-deno-js/deno_std_wasm_crypto.js",
|
||||
);
|
||||
const bindingsJs = `${copyrightLine}
|
||||
// This file is automatically @generated by _build.ts
|
||||
// It is not intended for manual editing.
|
||||
// deno-lint-ignore-file
|
||||
import wasmBytes from "./crypto.wasm.js";
|
||||
|
||||
${
|
||||
generatedScript.replace(
|
||||
/^const file =.*?;\nconst wasmFile =.*?;\nconst wasmModule =.*?;\n/sm,
|
||||
`const wasmModule = new WebAssembly.Module(wasmBytes);`,
|
||||
)
|
||||
}
|
||||
|
||||
// for testing/debugging
|
||||
export const _wasm = wasm;
|
||||
export const _wasmModule = wasmModule;
|
||||
export const _wasmInstance = wasmInstance;
|
||||
export const _wasmBytes = wasmBytes;
|
||||
`;
|
||||
|
||||
await Deno.writeTextFile("./crypto.wasm.js", wasmJs);
|
||||
await Deno.writeTextFile("./crypto.js", bindingsJs);
|
||||
|
||||
// Format the generated files.
|
||||
if (
|
||||
!(await Deno.run({
|
||||
cmd: [
|
||||
"deno",
|
||||
"fmt",
|
||||
"./crypto.wasm.js",
|
||||
"./crypto.js",
|
||||
],
|
||||
}).status()).success
|
||||
) {
|
||||
console.error(
|
||||
`Failed to format generated code.`,
|
||||
);
|
||||
Deno.exit(1);
|
||||
}
|
400
_wasm_crypto/crypto.js
Normal file
400
_wasm_crypto/crypto.js
Normal file
@ -0,0 +1,400 @@
|
||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
// This file is automatically @generated by _build.ts
|
||||
// It is not intended for manual editing.
|
||||
// deno-lint-ignore-file
|
||||
import wasmBytes from "./crypto.wasm.js";
|
||||
|
||||
const heap = new Array(32).fill(undefined);
|
||||
|
||||
heap.push(undefined, null, true, false);
|
||||
|
||||
function getObject(idx) {
|
||||
return heap[idx];
|
||||
}
|
||||
|
||||
let heap_next = heap.length;
|
||||
|
||||
function dropObject(idx) {
|
||||
if (idx < 36) return;
|
||||
heap[idx] = heap_next;
|
||||
heap_next = idx;
|
||||
}
|
||||
|
||||
function takeObject(idx) {
|
||||
const ret = getObject(idx);
|
||||
dropObject(idx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
function addHeapObject(obj) {
|
||||
if (heap_next === heap.length) heap.push(heap.length + 1);
|
||||
const idx = heap_next;
|
||||
heap_next = heap[idx];
|
||||
|
||||
heap[idx] = obj;
|
||||
return idx;
|
||||
}
|
||||
|
||||
let cachedTextDecoder = new TextDecoder("utf-8", {
|
||||
ignoreBOM: true,
|
||||
fatal: true,
|
||||
});
|
||||
|
||||
cachedTextDecoder.decode();
|
||||
|
||||
let cachegetUint8Memory0 = null;
|
||||
function getUint8Memory0() {
|
||||
if (
|
||||
cachegetUint8Memory0 === null ||
|
||||
cachegetUint8Memory0.buffer !== wasm.memory.buffer
|
||||
) {
|
||||
cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetUint8Memory0;
|
||||
}
|
||||
|
||||
function getStringFromWasm0(ptr, len) {
|
||||
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
||||
}
|
||||
|
||||
let WASM_VECTOR_LEN = 0;
|
||||
|
||||
let cachedTextEncoder = new TextEncoder("utf-8");
|
||||
|
||||
const encodeString = function (arg, view) {
|
||||
return cachedTextEncoder.encodeInto(arg, view);
|
||||
};
|
||||
|
||||
function passStringToWasm0(arg, malloc, realloc) {
|
||||
if (realloc === undefined) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
const ptr = malloc(buf.length);
|
||||
getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);
|
||||
WASM_VECTOR_LEN = buf.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
let len = arg.length;
|
||||
let ptr = malloc(len);
|
||||
|
||||
const mem = getUint8Memory0();
|
||||
|
||||
let offset = 0;
|
||||
|
||||
for (; offset < len; offset++) {
|
||||
const code = arg.charCodeAt(offset);
|
||||
if (code > 0x7F) break;
|
||||
mem[ptr + offset] = code;
|
||||
}
|
||||
|
||||
if (offset !== len) {
|
||||
if (offset !== 0) {
|
||||
arg = arg.slice(offset);
|
||||
}
|
||||
ptr = realloc(ptr, len, len = offset + arg.length * 3);
|
||||
const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
|
||||
const ret = encodeString(arg, view);
|
||||
|
||||
offset += ret.written;
|
||||
}
|
||||
|
||||
WASM_VECTOR_LEN = offset;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
function isLikeNone(x) {
|
||||
return x === undefined || x === null;
|
||||
}
|
||||
|
||||
let cachegetInt32Memory0 = null;
|
||||
function getInt32Memory0() {
|
||||
if (
|
||||
cachegetInt32Memory0 === null ||
|
||||
cachegetInt32Memory0.buffer !== wasm.memory.buffer
|
||||
) {
|
||||
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetInt32Memory0;
|
||||
}
|
||||
|
||||
function getArrayU8FromWasm0(ptr, len) {
|
||||
return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len);
|
||||
}
|
||||
/**
|
||||
* Returns the digest of the given `data` using the given hash `algorithm`.
|
||||
*
|
||||
* `length` will usually be left `undefined` to use the default length for
|
||||
* the algorithm. For algorithms with variable-length output, it can be used
|
||||
* to specify a non-negative integer number of bytes.
|
||||
*
|
||||
* An error will be thrown if `algorithm` is not a supported hash algorithm or
|
||||
* `length` is not a supported length for the algorithm.
|
||||
* @param {string} algorithm
|
||||
* @param {Uint8Array} data
|
||||
* @param {number | undefined} length
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function digest(algorithm, data, length) {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
var ptr0 = passStringToWasm0(
|
||||
algorithm,
|
||||
wasm.__wbindgen_malloc,
|
||||
wasm.__wbindgen_realloc,
|
||||
);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
wasm.digest(
|
||||
retptr,
|
||||
ptr0,
|
||||
len0,
|
||||
addHeapObject(data),
|
||||
!isLikeNone(length),
|
||||
isLikeNone(length) ? 0 : length,
|
||||
);
|
||||
var r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||
var r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||
var v1 = getArrayU8FromWasm0(r0, r1).slice();
|
||||
wasm.__wbindgen_free(r0, r1 * 1);
|
||||
return v1;
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
}
|
||||
}
|
||||
|
||||
const DigestContextFinalization = new FinalizationRegistry((ptr) =>
|
||||
wasm.__wbg_digestcontext_free(ptr)
|
||||
);
|
||||
/**
|
||||
* A context for incrementally computing a digest using a given hash algorithm.
|
||||
*/
|
||||
export class DigestContext {
|
||||
static __wrap(ptr) {
|
||||
const obj = Object.create(DigestContext.prototype);
|
||||
obj.ptr = ptr;
|
||||
DigestContextFinalization.register(obj, obj.ptr, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
__destroy_into_raw() {
|
||||
const ptr = this.ptr;
|
||||
this.ptr = 0;
|
||||
DigestContextFinalization.unregister(this);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
free() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.__wbg_digestcontext_free(ptr);
|
||||
}
|
||||
/**
|
||||
* Creates a new context incrementally computing a digest using the given
|
||||
* hash algorithm.
|
||||
*
|
||||
* An error will be thrown if `algorithm` is not a supported hash algorithm.
|
||||
* @param {string} algorithm
|
||||
*/
|
||||
constructor(algorithm) {
|
||||
var ptr0 = passStringToWasm0(
|
||||
algorithm,
|
||||
wasm.__wbindgen_malloc,
|
||||
wasm.__wbindgen_realloc,
|
||||
);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
var ret = wasm.digestcontext_new(ptr0, len0);
|
||||
return DigestContext.__wrap(ret);
|
||||
}
|
||||
/**
|
||||
* Update the digest's internal state with the additional input `data`.
|
||||
*
|
||||
* If the `data` array view is large, it will be split into subarrays (via
|
||||
* JavaScript bindings) which will be processed sequentially in order to
|
||||
* limit the amount of memory that needs to be allocated in the WASM heap.
|
||||
* @param {Uint8Array} data
|
||||
*/
|
||||
update(data) {
|
||||
wasm.digestcontext_update(this.ptr, addHeapObject(data));
|
||||
}
|
||||
/**
|
||||
* Returns the digest of the input data so far. This may be called repeatedly
|
||||
* without side effects.
|
||||
*
|
||||
* `length` will usually be left `undefined` to use the default length for
|
||||
* the algorithm. For algorithms with variable-length output, it can be used
|
||||
* to specify a non-negative integer number of bytes.
|
||||
*
|
||||
* An error will be thrown if `algorithm` is not a supported hash algorithm or
|
||||
* `length` is not a supported length for the algorithm.
|
||||
* @param {number | undefined} length
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
digest(length) {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
wasm.digestcontext_digest(
|
||||
retptr,
|
||||
this.ptr,
|
||||
!isLikeNone(length),
|
||||
isLikeNone(length) ? 0 : length,
|
||||
);
|
||||
var r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||
var r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||
var v0 = getArrayU8FromWasm0(r0, r1).slice();
|
||||
wasm.__wbindgen_free(r0, r1 * 1);
|
||||
return v0;
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns the digest of the input data so far, and resets this context to
|
||||
* its initial state, as though it has not yet been provided with any input
|
||||
* data. (It will still use the same algorithm.)
|
||||
*
|
||||
* `length` will usually be left `undefined` to use the default length for
|
||||
* the algorithm. For algorithms with variable-length output, it can be used
|
||||
* to specify a non-negative integer number of bytes.
|
||||
*
|
||||
* An error will be thrown if `algorithm` is not a supported hash algorithm or
|
||||
* `length` is not a supported length for the algorithm.
|
||||
* @param {number | undefined} length
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
digestAndReset(length) {
|
||||
try {
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
wasm.digestcontext_digestAndReset(
|
||||
retptr,
|
||||
this.ptr,
|
||||
!isLikeNone(length),
|
||||
isLikeNone(length) ? 0 : length,
|
||||
);
|
||||
var r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||
var r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||
var v0 = getArrayU8FromWasm0(r0, r1).slice();
|
||||
wasm.__wbindgen_free(r0, r1 * 1);
|
||||
return v0;
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns the digest of the input data so far, and then drops the context
|
||||
* from memory on the WASM side. This context must no longer be used, and any
|
||||
* further method calls will result in null pointer errors being thrown.
|
||||
* https://github.com/rustwasm/wasm-bindgen/blob/bf39cfd8/crates/backend/src/codegen.rs#L186
|
||||
*
|
||||
* `length` will usually be left `undefined` to use the default length for
|
||||
* the algorithm. For algorithms with variable-length output, it can be used
|
||||
* to specify a non-negative integer number of bytes.
|
||||
*
|
||||
* An error will be thrown if `algorithm` is not a supported hash algorithm or
|
||||
* `length` is not a supported length for the algorithm.
|
||||
* @param {number | undefined} length
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
digestAndDrop(length) {
|
||||
try {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
||||
wasm.digestcontext_digestAndDrop(
|
||||
retptr,
|
||||
ptr,
|
||||
!isLikeNone(length),
|
||||
isLikeNone(length) ? 0 : length,
|
||||
);
|
||||
var r0 = getInt32Memory0()[retptr / 4 + 0];
|
||||
var r1 = getInt32Memory0()[retptr / 4 + 1];
|
||||
var v0 = getArrayU8FromWasm0(r0, r1).slice();
|
||||
wasm.__wbindgen_free(r0, r1 * 1);
|
||||
return v0;
|
||||
} finally {
|
||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Resets this context to its initial state, as though it has not yet been
|
||||
* provided with any input data. (It will still use the same algorithm.)
|
||||
*/
|
||||
reset() {
|
||||
wasm.digestcontext_reset(this.ptr);
|
||||
}
|
||||
/**
|
||||
* Returns a new `DigestContext` that is a copy of this one, i.e., using the
|
||||
* same algorithm and with a copy of the same internal state.
|
||||
*
|
||||
* This may be a more efficient option for computing multiple digests that
|
||||
* start with a common prefix.
|
||||
* @returns {DigestContext}
|
||||
*/
|
||||
clone() {
|
||||
var ret = wasm.digestcontext_clone(this.ptr);
|
||||
return DigestContext.__wrap(ret);
|
||||
}
|
||||
}
|
||||
|
||||
const imports = {
|
||||
__wbindgen_placeholder__: {
|
||||
__wbg_new_f85dbdfb9cdbe2ec: function (arg0, arg1) {
|
||||
var ret = new TypeError(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
},
|
||||
__wbindgen_object_drop_ref: function (arg0) {
|
||||
takeObject(arg0);
|
||||
},
|
||||
__wbg_byteLength_e0515bc94cfc5dee: function (arg0) {
|
||||
var ret = getObject(arg0).byteLength;
|
||||
return ret;
|
||||
},
|
||||
__wbg_byteOffset_77eec84716a2e737: function (arg0) {
|
||||
var ret = getObject(arg0).byteOffset;
|
||||
return ret;
|
||||
},
|
||||
__wbg_buffer_1c5918a4ab656ff7: function (arg0) {
|
||||
var ret = getObject(arg0).buffer;
|
||||
return addHeapObject(ret);
|
||||
},
|
||||
__wbg_newwithbyteoffsetandlength_e57ad1f2ce812c03: function (
|
||||
arg0,
|
||||
arg1,
|
||||
arg2,
|
||||
) {
|
||||
var ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0);
|
||||
return addHeapObject(ret);
|
||||
},
|
||||
__wbg_length_2d56cb37075fcfb1: function (arg0) {
|
||||
var ret = getObject(arg0).length;
|
||||
return ret;
|
||||
},
|
||||
__wbindgen_memory: function () {
|
||||
var ret = wasm.memory;
|
||||
return addHeapObject(ret);
|
||||
},
|
||||
__wbg_buffer_9e184d6f785de5ed: function (arg0) {
|
||||
var ret = getObject(arg0).buffer;
|
||||
return addHeapObject(ret);
|
||||
},
|
||||
__wbg_new_e8101319e4cf95fc: function (arg0) {
|
||||
var ret = new Uint8Array(getObject(arg0));
|
||||
return addHeapObject(ret);
|
||||
},
|
||||
__wbg_set_e8ae7b27314e8b98: function (arg0, arg1, arg2) {
|
||||
getObject(arg0).set(getObject(arg1), arg2 >>> 0);
|
||||
},
|
||||
__wbindgen_throw: function (arg0, arg1) {
|
||||
throw new Error(getStringFromWasm0(arg0, arg1));
|
||||
},
|
||||
__wbindgen_rethrow: function (arg0) {
|
||||
throw takeObject(arg0);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const wasmModule = new WebAssembly.Module(wasmBytes);
|
||||
const wasmInstance = new WebAssembly.Instance(wasmModule, imports);
|
||||
const wasm = wasmInstance.exports;
|
||||
|
||||
// for testing/debugging
|
||||
export const _wasm = wasm;
|
||||
export const _wasmModule = wasmModule;
|
||||
export const _wasmInstance = wasmInstance;
|
||||
export const _wasmBytes = wasmBytes;
|
2492
_wasm_crypto/crypto.wasm.js
Normal file
2492
_wasm_crypto/crypto.wasm.js
Normal file
File diff suppressed because it is too large
Load Diff
40
_wasm_crypto/mod.ts
Normal file
40
_wasm_crypto/mod.ts
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
export * as crypto from "./crypto.js";
|
||||
|
||||
/**
|
||||
* All cryptographic hash/digest algorithms supported by std/_wasm_crypto.
|
||||
*
|
||||
* For algorithms that are supported by WebCrypto, the name here must match the
|
||||
* one used by WebCrypto. Otherwise we should prefer the formatting used in the
|
||||
* official specification. All names are uppercase to faciliate case-insensitive
|
||||
* comparisons required by the WebCrypto spec.
|
||||
*/
|
||||
export const digestAlgorithms = [
|
||||
"BLAKE2B-256",
|
||||
"BLAKE2B-384",
|
||||
"BLAKE2B",
|
||||
"BLAKE2S",
|
||||
"BLAKE3",
|
||||
"KECCAK-224",
|
||||
"KECCAK-256",
|
||||
"KECCAK-384",
|
||||
"KECCAK-512",
|
||||
"SHA-384",
|
||||
"SHA3-224",
|
||||
"SHA3-256",
|
||||
"SHA3-384",
|
||||
"SHA3-512",
|
||||
"SHAKE128",
|
||||
"SHAKE256",
|
||||
// insecure (length-extendable):
|
||||
"RIPEMD-160",
|
||||
"SHA-224",
|
||||
"SHA-256",
|
||||
"SHA-512",
|
||||
// insecure (collidable and length-extendable):
|
||||
"MD5",
|
||||
"SHA-1",
|
||||
] as const;
|
||||
|
||||
/** An algorithm name supported by std/_wasm_crypto. */
|
||||
export type DigestAlgorithm = typeof digestAlgorithms[number];
|
1
_wasm_crypto/rust-toolchain
Normal file
1
_wasm_crypto/rust-toolchain
Normal file
@ -0,0 +1 @@
|
||||
1.53.0
|
325
_wasm_crypto/src/digest.rs
Normal file
325
_wasm_crypto/src/digest.rs
Normal file
@ -0,0 +1,325 @@
|
||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
use digest::{
|
||||
BlockInput, Digest, DynDigest, ExtendableOutput, Reset, Update,
|
||||
VariableOutput,
|
||||
};
|
||||
|
||||
/// Enum wrapper over all supported digest implementations.
|
||||
#[derive(Clone)]
|
||||
pub enum Context {
|
||||
Blake2b(Box<blake2::Blake2b>),
|
||||
Blake2b256(Box<blake2::VarBlake2b>),
|
||||
Blake2b384(Box<blake2::VarBlake2b>),
|
||||
Blake2s(Box<blake2::Blake2s>),
|
||||
Blake3(Box<blake3::Hasher>),
|
||||
Keccak224(Box<sha3::Keccak224>),
|
||||
Keccak256(Box<sha3::Keccak256>),
|
||||
Keccak384(Box<sha3::Keccak384>),
|
||||
Keccak512(Box<sha3::Keccak512>),
|
||||
Md5(Box<md5::Md5>),
|
||||
Ripemd160(Box<ripemd160::Ripemd160>),
|
||||
Sha1(Box<sha1::Sha1>),
|
||||
Sha3_224(Box<sha3::Sha3_224>),
|
||||
Sha3_256(Box<sha3::Sha3_256>),
|
||||
Sha3_384(Box<sha3::Sha3_384>),
|
||||
Sha3_512(Box<sha3::Sha3_512>),
|
||||
Sha224(Box<sha2::Sha224>),
|
||||
Sha256(Box<sha2::Sha256>),
|
||||
Sha384(Box<sha2::Sha384>),
|
||||
Sha512(Box<sha2::Sha512>),
|
||||
Shake128(Box<sha3::Shake128>),
|
||||
Shake256(Box<sha3::Shake256>),
|
||||
}
|
||||
|
||||
use Context::*;
|
||||
|
||||
impl Context {
|
||||
pub fn new(algorithm_name: &str) -> Result<Context, &'static str> {
|
||||
Ok(match algorithm_name {
|
||||
"BLAKE2B" => Blake2b(Default::default()),
|
||||
"BLAKE2B-256" => {
|
||||
Blake2b256(Box::new(blake2::VarBlake2b::new(256 / 8).unwrap()))
|
||||
}
|
||||
"BLAKE2B-384" => {
|
||||
Blake2b384(Box::new(blake2::VarBlake2b::new(384 / 8).unwrap()))
|
||||
}
|
||||
"BLAKE2S" => Blake2s(Default::default()),
|
||||
"BLAKE3" => Blake3(Default::default()),
|
||||
"KECCAK-224" => Keccak224(Default::default()),
|
||||
"KECCAK-256" => Keccak256(Default::default()),
|
||||
"KECCAK-384" => Keccak384(Default::default()),
|
||||
"KECCAK-512" => Keccak512(Default::default()),
|
||||
"MD5" => Md5(Default::default()),
|
||||
"RIPEMD-160" => Ripemd160(Default::default()),
|
||||
"SHA-1" => Sha1(Default::default()),
|
||||
"SHA3-224" => Sha3_224(Default::default()),
|
||||
"SHA3-256" => Sha3_256(Default::default()),
|
||||
"SHA3-384" => Sha3_384(Default::default()),
|
||||
"SHA3-512" => Sha3_512(Default::default()),
|
||||
"SHA-224" => Sha224(Default::default()),
|
||||
"SHA-256" => Sha256(Default::default()),
|
||||
"SHA-384" => Sha384(Default::default()),
|
||||
"SHA-512" => Sha512(Default::default()),
|
||||
"SHAKE128" => Shake128(Default::default()),
|
||||
"SHAKE256" => Shake256(Default::default()),
|
||||
_ => return Err("unsupported algorithm"),
|
||||
})
|
||||
}
|
||||
|
||||
/// The input block length for the algorithm, in bytes.
|
||||
pub fn input_block_length(&self) -> usize {
|
||||
// For algorithm types that implement BlockInput and have a statically
|
||||
// available BlockSize as part of their type definition, we use that value.
|
||||
fn static_block_length<T: BlockInput>(_: &T) -> usize {
|
||||
<T::BlockSize as typenum::Unsigned>::to_usize()
|
||||
}
|
||||
|
||||
match self {
|
||||
Blake2b(context) => static_block_length(&**context),
|
||||
Blake2b256(context) => static_block_length(&**context),
|
||||
Blake2b384(context) => static_block_length(&**context),
|
||||
Blake2s(context) => static_block_length(&**context),
|
||||
Blake3(context) => static_block_length(&**context),
|
||||
Keccak224(context) => static_block_length(&**context),
|
||||
Keccak256(context) => static_block_length(&**context),
|
||||
Keccak384(context) => static_block_length(&**context),
|
||||
Keccak512(context) => static_block_length(&**context),
|
||||
Md5(context) => static_block_length(&**context),
|
||||
Ripemd160(context) => static_block_length(&**context),
|
||||
Sha1(context) => static_block_length(&**context),
|
||||
Sha3_224(context) => static_block_length(&**context),
|
||||
Sha3_256(context) => static_block_length(&**context),
|
||||
Sha3_384(context) => static_block_length(&**context),
|
||||
Sha3_512(context) => static_block_length(&**context),
|
||||
Sha224(context) => static_block_length(&**context),
|
||||
Sha256(context) => static_block_length(&**context),
|
||||
Sha384(context) => static_block_length(&**context),
|
||||
Sha512(context) => static_block_length(&**context),
|
||||
|
||||
// https://doi.org/10.6028/NIST.FIPS.202 specifies that:
|
||||
// - In general, the input block size (in bits) of a sponge function is
|
||||
// its rate.
|
||||
// - SPONGE[f, pad, r] = The sponge function in which the underlying
|
||||
// function is f, the padding rule is pad, and the rate is r.
|
||||
// - KECCAK[c] = SPONGE[KECCAK-p[1600, 24], pad10*1, 1600–c]
|
||||
// - SHAKE128(M, d) = KECCAK[256] (M || 1111, d)
|
||||
Shake128(_) => 168, // implying a rate of (1600 - 256) bits = 168 bytes
|
||||
// - SHAKE256(M, d) = KECCAK[512] (M || 1111, d).
|
||||
Shake256(_) => 136, // implying a rate of (1600 - 512) bits = 136 bytes
|
||||
}
|
||||
}
|
||||
|
||||
/// The output digest length for the algorithm, in bytes.
|
||||
///
|
||||
/// If the algorithm is variable-length, this returns its default length.
|
||||
pub fn output_length(&self) -> usize {
|
||||
match self {
|
||||
Blake2b(context) => context.output_size(),
|
||||
Blake2b256(context) => context.output_size(),
|
||||
Blake2b384(context) => context.output_size(),
|
||||
Blake2s(context) => context.output_size(),
|
||||
Blake3(context) => context.output_size(),
|
||||
Keccak224(context) => context.output_size(),
|
||||
Keccak256(context) => context.output_size(),
|
||||
Keccak384(context) => context.output_size(),
|
||||
Keccak512(context) => context.output_size(),
|
||||
Md5(context) => context.output_size(),
|
||||
Ripemd160(context) => context.output_size(),
|
||||
Sha1(context) => context.output_size(),
|
||||
Sha3_224(context) => context.output_size(),
|
||||
Sha3_256(context) => context.output_size(),
|
||||
Sha3_384(context) => context.output_size(),
|
||||
Sha3_512(context) => context.output_size(),
|
||||
Sha224(context) => context.output_size(),
|
||||
Sha256(context) => context.output_size(),
|
||||
Sha384(context) => context.output_size(),
|
||||
Sha512(context) => context.output_size(),
|
||||
|
||||
// https://doi.org/10.6028/NIST.FIPS.202's Table 4 indicates that in order
|
||||
// to reach the target security strength for these algorithms, the output
|
||||
// size (in bits) needs to be (at least) two times larger than that
|
||||
// security strength (in bits).
|
||||
Shake128(_) => 32, // implying a length of (2 * 128) bits = 32 bytes
|
||||
Shake256(_) => 64, // implying a length of (2 * 256) bits = 64 bytes
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the algorithm has an extendable variable-length digest output
|
||||
/// (whether it is an "XOF").
|
||||
pub const fn extendable(&self) -> bool {
|
||||
matches!(self, Blake3(_) | Shake128(_) | Shake256(_))
|
||||
}
|
||||
|
||||
/// The name of the algorithm used by this context.
|
||||
///
|
||||
/// Names are all uppercase (for ease of case-insensitive comparisons) and
|
||||
/// will match the name formatting used in WebCrypto if the algorithm is
|
||||
/// supported by WebCrypto, and otherwise match the formatting used in the
|
||||
/// official specification for the algorithm.
|
||||
pub const fn algorithm_name(&self) -> &'static str {
|
||||
match self {
|
||||
Blake2b(_) => "BLAKE2B",
|
||||
Blake2b256(_) => "BLAKE2B-256",
|
||||
Blake2b384(_) => "BLAKE2B-384",
|
||||
Blake2s(_) => "BLAKE2S",
|
||||
Blake3(_) => "BLAKE3",
|
||||
Keccak224(_) => "KECCAK-224",
|
||||
Keccak256(_) => "KECCAK-256",
|
||||
Keccak384(_) => "KECCAK-384",
|
||||
Keccak512(_) => "KECCAK-512",
|
||||
Md5(_) => "MD5",
|
||||
Ripemd160(_) => "RIPEMD-160",
|
||||
Sha1(_) => "SHA-1",
|
||||
Sha3_224(_) => "SHA3-224",
|
||||
Sha3_256(_) => "SHA3-256",
|
||||
Sha3_384(_) => "SHA3-384",
|
||||
Sha3_512(_) => "SHA3-512",
|
||||
Sha224(_) => "SHA-224",
|
||||
Sha256(_) => "SHA-256",
|
||||
Sha384(_) => "SHA-384",
|
||||
Sha512(_) => "SHA-512",
|
||||
Shake128(_) => "SHAKE128",
|
||||
Shake256(_) => "SHAKE256",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
match self {
|
||||
Blake2b(context) => Reset::reset(&mut **context),
|
||||
Blake2b256(context) => Reset::reset(&mut **context),
|
||||
Blake2b384(context) => Reset::reset(&mut **context),
|
||||
Blake2s(context) => Reset::reset(&mut **context),
|
||||
Blake3(context) => Reset::reset(&mut **context),
|
||||
Keccak224(context) => Reset::reset(&mut **context),
|
||||
Keccak256(context) => Reset::reset(&mut **context),
|
||||
Keccak384(context) => Reset::reset(&mut **context),
|
||||
Keccak512(context) => Reset::reset(&mut **context),
|
||||
Md5(context) => Reset::reset(&mut **context),
|
||||
Ripemd160(context) => Reset::reset(&mut **context),
|
||||
Sha1(context) => Reset::reset(&mut **context),
|
||||
Sha3_224(context) => Reset::reset(&mut **context),
|
||||
Sha3_256(context) => Reset::reset(&mut **context),
|
||||
Sha3_384(context) => Reset::reset(&mut **context),
|
||||
Sha3_512(context) => Reset::reset(&mut **context),
|
||||
Sha224(context) => Reset::reset(&mut **context),
|
||||
Sha256(context) => Reset::reset(&mut **context),
|
||||
Sha384(context) => Reset::reset(&mut **context),
|
||||
Sha512(context) => Reset::reset(&mut **context),
|
||||
Shake128(context) => Reset::reset(&mut **context),
|
||||
Shake256(context) => Reset::reset(&mut **context),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn update(&mut self, data: &[u8]) {
|
||||
match self {
|
||||
Blake2b(context) => Digest::update(&mut **context, data),
|
||||
Blake2b256(context) => (&mut **context).update(data),
|
||||
Blake2b384(context) => (&mut **context).update(data),
|
||||
Blake2s(context) => Digest::update(&mut **context, data),
|
||||
Blake3(context) => Digest::update(&mut **context, data),
|
||||
Keccak224(context) => Digest::update(&mut **context, data),
|
||||
Keccak256(context) => Digest::update(&mut **context, data),
|
||||
Keccak384(context) => Digest::update(&mut **context, data),
|
||||
Keccak512(context) => Digest::update(&mut **context, data),
|
||||
Md5(context) => Digest::update(&mut **context, data),
|
||||
Ripemd160(context) => Digest::update(&mut **context, data),
|
||||
Sha1(context) => Digest::update(&mut **context, data),
|
||||
Sha3_224(context) => Digest::update(&mut **context, data),
|
||||
Sha3_256(context) => Digest::update(&mut **context, data),
|
||||
Sha3_384(context) => Digest::update(&mut **context, data),
|
||||
Sha3_512(context) => Digest::update(&mut **context, data),
|
||||
Sha224(context) => Digest::update(&mut **context, data),
|
||||
Sha256(context) => Digest::update(&mut **context, data),
|
||||
Sha384(context) => Digest::update(&mut **context, data),
|
||||
Sha512(context) => Digest::update(&mut **context, data),
|
||||
Shake128(context) => (&mut **context).update(data),
|
||||
Shake256(context) => (&mut **context).update(data),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn digest_and_drop(
|
||||
self,
|
||||
length: Option<usize>,
|
||||
) -> Result<Box<[u8]>, &'static str> {
|
||||
if let Some(length) = length {
|
||||
if !self.extendable() && length != self.output_length() {
|
||||
return Err(
|
||||
"non-default length specified for non-extendable algorithm",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let length = length.unwrap_or_else(|| self.output_length());
|
||||
|
||||
Ok(match self {
|
||||
Blake2b(context) => context.finalize(),
|
||||
Blake2b256(context) => context.finalize_boxed(),
|
||||
Blake2b384(context) => context.finalize_boxed(),
|
||||
Blake2s(context) => context.finalize(),
|
||||
Blake3(context) => context.finalize_boxed(length),
|
||||
Keccak224(context) => context.finalize(),
|
||||
Keccak256(context) => context.finalize(),
|
||||
Keccak384(context) => context.finalize(),
|
||||
Keccak512(context) => context.finalize(),
|
||||
Md5(context) => context.finalize(),
|
||||
Ripemd160(context) => context.finalize(),
|
||||
Sha1(context) => context.finalize(),
|
||||
Sha3_224(context) => context.finalize(),
|
||||
Sha3_256(context) => context.finalize(),
|
||||
Sha3_384(context) => context.finalize(),
|
||||
Sha3_512(context) => context.finalize(),
|
||||
Sha224(context) => context.finalize(),
|
||||
Sha256(context) => context.finalize(),
|
||||
Sha384(context) => context.finalize(),
|
||||
Sha512(context) => context.finalize(),
|
||||
Shake128(context) => context.finalize_boxed(length),
|
||||
Shake256(context) => context.finalize_boxed(length),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn digest_and_reset(
|
||||
&mut self,
|
||||
length: Option<usize>,
|
||||
) -> Result<Box<[u8]>, &'static str> {
|
||||
if let Some(length) = length {
|
||||
if !self.extendable() && length != self.output_length() {
|
||||
return Err(
|
||||
"non-default length specified for non-extendable algorithm",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let length = length.unwrap_or_else(|| self.output_length());
|
||||
Ok(match self {
|
||||
Blake2b(context) => DynDigest::finalize_reset(context.as_mut()),
|
||||
Blake2b256(context) => context.finalize_boxed_reset(),
|
||||
Blake2b384(context) => context.finalize_boxed_reset(),
|
||||
Blake2s(context) => DynDigest::finalize_reset(context.as_mut()),
|
||||
Blake3(context) => context.finalize_boxed_reset(length),
|
||||
Keccak224(context) => DynDigest::finalize_reset(context.as_mut()),
|
||||
Keccak256(context) => DynDigest::finalize_reset(context.as_mut()),
|
||||
Keccak384(context) => DynDigest::finalize_reset(context.as_mut()),
|
||||
Keccak512(context) => DynDigest::finalize_reset(context.as_mut()),
|
||||
Md5(context) => DynDigest::finalize_reset(context.as_mut()),
|
||||
Ripemd160(context) => DynDigest::finalize_reset(context.as_mut()),
|
||||
Sha1(context) => DynDigest::finalize_reset(context.as_mut()),
|
||||
Sha3_224(context) => DynDigest::finalize_reset(context.as_mut()),
|
||||
Sha3_256(context) => DynDigest::finalize_reset(context.as_mut()),
|
||||
Sha3_384(context) => DynDigest::finalize_reset(context.as_mut()),
|
||||
Sha3_512(context) => DynDigest::finalize_reset(context.as_mut()),
|
||||
Sha224(context) => DynDigest::finalize_reset(context.as_mut()),
|
||||
Sha256(context) => DynDigest::finalize_reset(context.as_mut()),
|
||||
Sha384(context) => DynDigest::finalize_reset(context.as_mut()),
|
||||
Sha512(context) => DynDigest::finalize_reset(context.as_mut()),
|
||||
Shake128(context) => context.finalize_boxed_reset(length),
|
||||
Shake256(context) => context.finalize_boxed_reset(length),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn digest(
|
||||
&self,
|
||||
length: Option<usize>,
|
||||
) -> Result<Box<[u8]>, &'static str> {
|
||||
self.clone().digest_and_drop(length)
|
||||
}
|
||||
}
|
164
_wasm_crypto/src/lib.rs
Normal file
164
_wasm_crypto/src/lib.rs
Normal file
@ -0,0 +1,164 @@
|
||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
use derive_more::{From, Into};
|
||||
use js_sys::Uint8Array;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
mod digest;
|
||||
|
||||
/// Returns the digest of the given `data` using the given hash `algorithm`.
|
||||
///
|
||||
/// `length` will usually be left `undefined` to use the default length for
|
||||
/// the algorithm. For algorithms with variable-length output, it can be used
|
||||
/// to specify a non-negative integer number of bytes.
|
||||
///
|
||||
/// An error will be thrown if `algorithm` is not a supported hash algorithm or
|
||||
/// `length` is not a supported length for the algorithm.
|
||||
#[wasm_bindgen]
|
||||
pub fn digest(
|
||||
algorithm: String,
|
||||
data: js_sys::Uint8Array,
|
||||
length: Option<usize>,
|
||||
) -> Result<Box<[u8]>, JsValue> {
|
||||
let mut context = DigestContext::new(algorithm)?;
|
||||
context.update(data)?;
|
||||
context.digest_and_drop(length)
|
||||
}
|
||||
|
||||
/// A context for incrementally computing a digest using a given hash algorithm.
|
||||
#[wasm_bindgen]
|
||||
#[derive(Clone, Into, From)]
|
||||
pub struct DigestContext(digest::Context);
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl DigestContext {
|
||||
/// Creates a new context incrementally computing a digest using the given
|
||||
/// hash algorithm.
|
||||
///
|
||||
/// An error will be thrown if `algorithm` is not a supported hash algorithm.
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(algorithm: String) -> Result<DigestContext, JsValue> {
|
||||
Ok(
|
||||
digest::Context::new(&algorithm)
|
||||
.map_err(|message| JsValue::from(js_sys::TypeError::new(message)))?
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Update the digest's internal state with the additional input `data`.
|
||||
///
|
||||
/// If the `data` array view is large, it will be split into subarrays (via
|
||||
/// JavaScript bindings) which will be processed sequentially in order to
|
||||
/// limit the amount of memory that needs to be allocated in the WASM heap.
|
||||
#[wasm_bindgen]
|
||||
pub fn update(&mut self, data: js_sys::Uint8Array) -> Result<(), JsValue> {
|
||||
// Every method call on `data` has to go through the JavaScript bindings, so
|
||||
// try to minimize them. Splitting on the JavaScript side could be more
|
||||
// efficient, but this is called from multiple places in JavaScript so it
|
||||
// seems simpler to keep it here for now.
|
||||
|
||||
let length = data.byte_length();
|
||||
let base_offset = data.byte_offset();
|
||||
let buffer = data.buffer();
|
||||
|
||||
let chunk_size: u32 = 65_536;
|
||||
|
||||
if length <= chunk_size {
|
||||
let chunk = data.to_vec();
|
||||
self.0.update(&chunk);
|
||||
} else {
|
||||
let mut offset = 0;
|
||||
while offset < length {
|
||||
let chunk = Uint8Array::new_with_byte_offset_and_length(
|
||||
&buffer,
|
||||
base_offset + offset,
|
||||
chunk_size.min(length - offset),
|
||||
)
|
||||
.to_vec();
|
||||
self.0.update(&chunk);
|
||||
offset += chunk_size;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the digest of the input data so far. This may be called repeatedly
|
||||
/// without side effects.
|
||||
///
|
||||
/// `length` will usually be left `undefined` to use the default length for
|
||||
/// the algorithm. For algorithms with variable-length output, it can be used
|
||||
/// to specify a non-negative integer number of bytes.
|
||||
///
|
||||
/// An error will be thrown if `algorithm` is not a supported hash algorithm or
|
||||
/// `length` is not a supported length for the algorithm.
|
||||
#[wasm_bindgen]
|
||||
pub fn digest(&self, length: Option<usize>) -> Result<Box<[u8]>, JsValue> {
|
||||
self
|
||||
.0
|
||||
.digest(length)
|
||||
.map_err(|message| JsValue::from(js_sys::TypeError::new(message)))
|
||||
}
|
||||
|
||||
/// Returns the digest of the input data so far, and resets this context to
|
||||
/// its initial state, as though it has not yet been provided with any input
|
||||
/// data. (It will still use the same algorithm.)
|
||||
///
|
||||
/// `length` will usually be left `undefined` to use the default length for
|
||||
/// the algorithm. For algorithms with variable-length output, it can be used
|
||||
/// to specify a non-negative integer number of bytes.
|
||||
///
|
||||
/// An error will be thrown if `algorithm` is not a supported hash algorithm or
|
||||
/// `length` is not a supported length for the algorithm.
|
||||
#[wasm_bindgen(js_name=digestAndReset)]
|
||||
pub fn digest_and_reset(
|
||||
&mut self,
|
||||
length: Option<usize>,
|
||||
) -> Result<Box<[u8]>, JsValue> {
|
||||
self
|
||||
.0
|
||||
.digest_and_reset(length)
|
||||
.map_err(|message| JsValue::from(js_sys::TypeError::new(message)))
|
||||
}
|
||||
|
||||
/// Returns the digest of the input data so far, and then drops the context
|
||||
/// from memory on the WASM side. This context must no longer be used, and any
|
||||
/// further method calls will result in null pointer errors being thrown.
|
||||
/// https://github.com/rustwasm/wasm-bindgen/blob/bf39cfd8/crates/backend/src/codegen.rs#L186
|
||||
///
|
||||
/// `length` will usually be left `undefined` to use the default length for
|
||||
/// the algorithm. For algorithms with variable-length output, it can be used
|
||||
/// to specify a non-negative integer number of bytes.
|
||||
///
|
||||
/// An error will be thrown if `algorithm` is not a supported hash algorithm or
|
||||
/// `length` is not a supported length for the algorithm.
|
||||
#[wasm_bindgen(js_name=digestAndDrop)]
|
||||
pub fn digest_and_drop(
|
||||
mut self,
|
||||
length: Option<usize>,
|
||||
) -> Result<Box<[u8]>, JsValue> {
|
||||
self
|
||||
.0
|
||||
.digest_and_reset(length)
|
||||
.map_err(|message| JsValue::from(js_sys::TypeError::new(message)))
|
||||
}
|
||||
|
||||
/// Resets this context to its initial state, as though it has not yet been
|
||||
/// provided with any input data. (It will still use the same algorithm.)
|
||||
#[wasm_bindgen]
|
||||
pub fn reset(&mut self) -> Result<(), JsValue> {
|
||||
self.0.reset();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a new `DigestContext` that is a copy of this one, i.e., using the
|
||||
/// same algorithm and with a copy of the same internal state.
|
||||
///
|
||||
/// This may be a more efficient option for computing multiple digests that
|
||||
/// start with a common prefix.
|
||||
#[wasm_bindgen]
|
||||
#[allow(clippy::should_implement_trait)]
|
||||
pub fn clone(&self) -> DigestContext {
|
||||
Clone::clone(self)
|
||||
}
|
||||
}
|
27
_wasm_crypto/test.ts
Normal file
27
_wasm_crypto/test.ts
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
import { assertEquals } from "../testing/asserts.ts";
|
||||
|
||||
import { crypto as wasmCrypto } from "./mod.ts";
|
||||
import { _wasmBytes as wasmBytes } from "./crypto.js";
|
||||
import * as wasmFileModule from "./crypto.wasm.js";
|
||||
|
||||
const webCrypto = globalThis.crypto;
|
||||
|
||||
Deno.test("test", async () => {
|
||||
const input = new TextEncoder().encode("SHA-384");
|
||||
|
||||
const wasmDigest = wasmCrypto.digest("SHA-384", input, undefined);
|
||||
|
||||
const webDigest = new Uint8Array(
|
||||
await webCrypto.subtle!.digest("SHA-384", input),
|
||||
);
|
||||
|
||||
assertEquals(wasmDigest, webDigest);
|
||||
});
|
||||
|
||||
Deno.test("Inlined WASM file's metadata should match its content", () => {
|
||||
assertEquals(wasmBytes.length, wasmFileModule.size);
|
||||
assertEquals(wasmBytes.byteLength, wasmFileModule.size);
|
||||
assertEquals(wasmFileModule.data.length, wasmFileModule.size);
|
||||
assertEquals(wasmFileModule.data.buffer.byteLength, wasmFileModule.size);
|
||||
});
|
13
crypto/README.md
Normal file
13
crypto/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# crypto
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import { crypto } from "https://deno.land/std@$STD_VERSION/crypto/mod.ts";
|
||||
|
||||
// This will delegate to the runtime's WebCrypto implementation.
|
||||
console.log(await crypto.subtle.digest("SHA-384", "hello world"));
|
||||
|
||||
// This will use a bundled WASM/Rust implementation.
|
||||
console.log(await crypto.subtle.digest("BLAKE3", "hello world"));
|
||||
```
|
81
crypto/_benches/bench.ts
Normal file
81
crypto/_benches/bench.ts
Normal file
@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env -S deno run
|
||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
import {
|
||||
bench,
|
||||
runBenchmarks,
|
||||
} from "https://deno.land/std@0.102.0/testing/bench.ts";
|
||||
import { createHash } from "https://deno.land/std@0.102.0/hash/mod.ts";
|
||||
import { assert, assertEquals } from "../../testing/asserts.ts";
|
||||
|
||||
import { crypto as stdCrypto } from "../mod.ts";
|
||||
|
||||
const webCrypto = globalThis.crypto;
|
||||
|
||||
// WASM is limited to 32-bit operations, which SHA-256 is optimized for, while
|
||||
// SHA-512 is optimized for 64-bit operations and may be slower.
|
||||
for (const algorithm of ["SHA-256", "SHA-512"] as const) {
|
||||
for (
|
||||
const length of [
|
||||
64,
|
||||
262_144,
|
||||
4_194_304,
|
||||
67_108_864,
|
||||
524_291_328,
|
||||
] as const
|
||||
) {
|
||||
// Create a test input buffer and do some operations to hopefully ensure
|
||||
// it's fully initialized and not optimized away.
|
||||
const buffer = new Uint8Array(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
buffer[i] = ((i + i % 13) + i % 31) % 255;
|
||||
}
|
||||
let sum = 0;
|
||||
for (const byte of buffer) {
|
||||
sum += byte;
|
||||
}
|
||||
assert(sum > 0);
|
||||
|
||||
for (
|
||||
const implementation of [
|
||||
"runtime WebCrypto (target)",
|
||||
"std@0.102.0/hash WASM (baseline)",
|
||||
"std/crypto WASM (you are here)",
|
||||
] as const
|
||||
) {
|
||||
let lastDigest: ArrayBuffer | undefined;
|
||||
|
||||
bench({
|
||||
name: `${algorithm.padEnd(12)} ${
|
||||
length.toString().padStart(12)
|
||||
}B ${implementation}`,
|
||||
async func(timer) {
|
||||
timer.start();
|
||||
|
||||
let digest;
|
||||
if (implementation === "std/crypto WASM (you are here)") {
|
||||
digest = stdCrypto.subtle.digestSync(algorithm, buffer);
|
||||
} else if (implementation === "runtime WebCrypto (target)") {
|
||||
digest = await webCrypto.subtle.digest(algorithm, buffer);
|
||||
} else if (implementation === "std@0.102.0/hash WASM (baseline)") {
|
||||
digest = createHash(
|
||||
algorithm.toLowerCase().replace("-", "") as "sha256" | "sha512",
|
||||
).update(buffer).digest();
|
||||
} else {
|
||||
throw new Error(`Unknown implementation ${implementation}`);
|
||||
}
|
||||
|
||||
timer.stop();
|
||||
|
||||
assert(digest.byteLength > 0);
|
||||
if (lastDigest) {
|
||||
assertEquals(lastDigest, digest);
|
||||
}
|
||||
lastDigest = digest;
|
||||
},
|
||||
runs: 10,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await runBenchmarks();
|
282
crypto/mod.ts
Normal file
282
crypto/mod.ts
Normal file
@ -0,0 +1,282 @@
|
||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
import {
|
||||
crypto as wasmCrypto,
|
||||
DigestAlgorithm as WasmDigestAlgorithm,
|
||||
digestAlgorithms as wasmDigestAlgorithms,
|
||||
} from "../_wasm_crypto/mod.ts";
|
||||
|
||||
type WebCryptoAlgorithmIdentifier = string | { name: string };
|
||||
|
||||
// TODO(jeremyBanks): Remove this once the built-in `Crypto` interface is
|
||||
// complete and stable. For now we use this incomplete-but-stable definition.
|
||||
interface WebCrypto {
|
||||
getRandomValues<T extends BufferSource>(buffer: T): T;
|
||||
randomUUID?(): string;
|
||||
subtle?: {
|
||||
// see https://www.w3.org/TR/WebCryptoAPI/#subtlecrypto-interface
|
||||
|
||||
/**
|
||||
* Returns a new `Promise` object that will encrypt `data` using the
|
||||
* specified `AlgorithmIdentifier` with the supplied `CryptoKey`.
|
||||
*/
|
||||
encrypt?(
|
||||
algorithm: WebCryptoAlgorithmIdentifier,
|
||||
key: unknown,
|
||||
data: BufferSource,
|
||||
): Promise<unknown>;
|
||||
/**
|
||||
* Returns a new `Promise` object that will decrypt `data` using the
|
||||
* specified `AlgorithmIdentifier` with the supplied `CryptoKey`.
|
||||
*/
|
||||
decrypt?(
|
||||
algorithm: WebCryptoAlgorithmIdentifier,
|
||||
key: unknown,
|
||||
data: BufferSource,
|
||||
): Promise<unknown>;
|
||||
|
||||
/**
|
||||
* Returns a new `Promise` object that will sign `data` using the specified
|
||||
* `AlgorithmIdentifier` with the supplied `CryptoKey`.
|
||||
*/
|
||||
sign?(
|
||||
algorithm: WebCryptoAlgorithmIdentifier,
|
||||
key: unknown,
|
||||
data: BufferSource,
|
||||
): Promise<unknown>;
|
||||
/**
|
||||
* Returns a new `Promise` object that will verify `data` using the
|
||||
* specified `AlgorithmIdentifier` with the supplied `CryptoKey`.
|
||||
*/
|
||||
verify?(
|
||||
algorithm: WebCryptoAlgorithmIdentifier,
|
||||
key: unknown,
|
||||
signature: BufferSource,
|
||||
data: BufferSource,
|
||||
): Promise<unknown>;
|
||||
|
||||
/**
|
||||
* Returns a new `Promise` object that will digest `data` using the
|
||||
* specified `AlgorithmIdentifier`.
|
||||
*/
|
||||
digest?(
|
||||
algorithm: WebCryptoAlgorithmIdentifier,
|
||||
data: BufferSource,
|
||||
): Promise<ArrayBuffer>;
|
||||
|
||||
generateKey?(
|
||||
algorithm: WebCryptoAlgorithmIdentifier,
|
||||
extractable: boolean,
|
||||
keyUsages: string[],
|
||||
): Promise<unknown>;
|
||||
deriveKey?(
|
||||
algorithm: WebCryptoAlgorithmIdentifier,
|
||||
baseKey: unknown,
|
||||
derivedKeyType: string,
|
||||
extractable: boolean,
|
||||
keyUsages: string[],
|
||||
): Promise<unknown>;
|
||||
deriveBits?(
|
||||
algorithm: WebCryptoAlgorithmIdentifier,
|
||||
baseKey: unknown,
|
||||
length: number,
|
||||
): Promise<unknown>;
|
||||
|
||||
importKey?(
|
||||
format: string,
|
||||
keyData: BufferSource | unknown,
|
||||
algorithm: WebCryptoAlgorithmIdentifier,
|
||||
extractable: boolean,
|
||||
keyUsages: string[],
|
||||
): Promise<unknown>;
|
||||
exportKey?(format: string, key: unknown): Promise<unknown>;
|
||||
|
||||
wrapKey?(
|
||||
format: string,
|
||||
key: unknown,
|
||||
wrappingKey: unknown,
|
||||
wrappingAlgorithm: WebCryptoAlgorithmIdentifier,
|
||||
): Promise<unknown>;
|
||||
unwrapKey?(
|
||||
format: string,
|
||||
wrappedKey: BufferSource,
|
||||
unwrappingKey: unknown,
|
||||
unwrapAlgorithm: WebCryptoAlgorithmIdentifier,
|
||||
unwrappedKeyAlgorithm: WebCryptoAlgorithmIdentifier,
|
||||
extractable: boolean,
|
||||
keyUsages: string[],
|
||||
): Promise<unknown>;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A copy of the global WebCrypto interface, with methods bound so they're
|
||||
* safe to re-export.
|
||||
*/
|
||||
const webCrypto: WebCrypto = ((crypto) => ({
|
||||
getRandomValues: crypto.getRandomValues?.bind(crypto),
|
||||
randomUUID: crypto.randomUUID?.bind(crypto),
|
||||
subtle: {
|
||||
decrypt: crypto.subtle?.decrypt?.bind(crypto.subtle),
|
||||
deriveBits: crypto.subtle?.deriveBits?.bind(crypto.subtle),
|
||||
deriveKey: crypto.subtle?.deriveKey?.bind(crypto.subtle),
|
||||
digest: crypto.subtle?.digest?.bind(crypto.subtle),
|
||||
encrypt: crypto.subtle?.encrypt?.bind(crypto.subtle),
|
||||
exportKey: crypto.subtle?.exportKey?.bind(crypto.subtle),
|
||||
generateKey: crypto.subtle?.generateKey?.bind(crypto.subtle),
|
||||
importKey: crypto.subtle?.importKey?.bind(crypto.subtle),
|
||||
sign: crypto.subtle?.sign?.bind(crypto.subtle),
|
||||
unwrapKey: crypto.subtle?.unwrapKey?.bind(crypto.subtle),
|
||||
verify: crypto.subtle?.verify?.bind(crypto.subtle),
|
||||
wrapKey: crypto.subtle?.wrapKey?.bind(crypto.subtle),
|
||||
},
|
||||
}))(globalThis.crypto as WebCrypto);
|
||||
|
||||
const bufferSourceBytes = (data: BufferSource | unknown) => {
|
||||
let bytes: Uint8Array | undefined;
|
||||
if (data instanceof Uint8Array) {
|
||||
bytes = data;
|
||||
} else if (ArrayBuffer.isView(data)) {
|
||||
bytes = new Uint8Array(
|
||||
data.buffer,
|
||||
data.byteOffset,
|
||||
data.byteLength,
|
||||
);
|
||||
} else if (data instanceof ArrayBuffer) {
|
||||
bytes = new Uint8Array(data);
|
||||
}
|
||||
return bytes;
|
||||
};
|
||||
|
||||
/**
|
||||
* An wrapper for WebCrypto adding support for additional non-standard
|
||||
* algorithms, but delegating to the runtime WebCrypto implementation whenever
|
||||
* possible.
|
||||
*/
|
||||
const stdCrypto = (<T extends WebCrypto>(x: T) => x)({
|
||||
...webCrypto,
|
||||
subtle: {
|
||||
...webCrypto.subtle,
|
||||
|
||||
/**
|
||||
* Returns a new `Promise` object that will digest `data` using the specified
|
||||
* `AlgorithmIdentifier`.
|
||||
*/
|
||||
async digest(
|
||||
algorithm: DigestAlgorithm,
|
||||
data: BufferSource | AsyncIterable<BufferSource> | Iterable<BufferSource>,
|
||||
): Promise<ArrayBuffer> {
|
||||
const { name, length } = normalizeAlgorithm(algorithm);
|
||||
const bytes = bufferSourceBytes(data);
|
||||
|
||||
// We delegate to WebCrypto whenever possible,
|
||||
if (
|
||||
// if the SubtleCrypto interface is available,
|
||||
webCrypto.subtle?.digest &&
|
||||
// if the algorithm is supported by the WebCrypto standard,
|
||||
(webCryptoDigestAlgorithms as readonly string[]).includes(name) &&
|
||||
// and the data is a single buffer,
|
||||
bytes
|
||||
) {
|
||||
return webCrypto.subtle.digest(
|
||||
algorithm,
|
||||
bytes,
|
||||
);
|
||||
} else if (wasmDigestAlgorithms.includes(name)) {
|
||||
if (bytes) {
|
||||
// Otherwise, we use our bundled WASM implementation via digestSync
|
||||
// if it supports the algorithm.
|
||||
return stdCrypto.subtle.digestSync(algorithm, bytes);
|
||||
} else if ((data as Iterable<BufferSource>)[Symbol.iterator]) {
|
||||
return stdCrypto.subtle.digestSync(
|
||||
algorithm,
|
||||
data as Iterable<BufferSource>,
|
||||
);
|
||||
} else if (
|
||||
(data as AsyncIterable<BufferSource>)[Symbol.asyncIterator]
|
||||
) {
|
||||
const context = new wasmCrypto.DigestContext(name);
|
||||
for await (const chunk of data as AsyncIterable<BufferSource>) {
|
||||
const chunkBytes = bufferSourceBytes(chunk);
|
||||
if (!chunkBytes) {
|
||||
throw new TypeError("data contained chunk of the wrong type");
|
||||
}
|
||||
context.update(chunkBytes);
|
||||
}
|
||||
return context.digestAndDrop(length);
|
||||
} else {
|
||||
throw new TypeError(
|
||||
"data must be a BufferSource or [Async]Iterable<BufferSource>",
|
||||
);
|
||||
}
|
||||
} else if (webCrypto.subtle?.digest) {
|
||||
// (TypeScript type definitions prohibit this case.) If they're trying
|
||||
// to call an algorithm we don't recognize, pass it along to WebCrypto
|
||||
// in case it's a non-standard algorithm supported by the the runtime
|
||||
// they're using.
|
||||
return await webCrypto.subtle.digest(
|
||||
algorithm,
|
||||
data as unknown as Uint8Array,
|
||||
);
|
||||
} else {
|
||||
throw new TypeError(`unsupported digest algorithm: ${algorithm}`);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a ArrayBuffer with the result of digesting `data` using the
|
||||
* specified `AlgorithmIdentifier`.
|
||||
*/
|
||||
digestSync(
|
||||
algorithm: DigestAlgorithm,
|
||||
data: BufferSource | Iterable<BufferSource>,
|
||||
): ArrayBuffer {
|
||||
algorithm = normalizeAlgorithm(algorithm);
|
||||
|
||||
const bytes = bufferSourceBytes(data);
|
||||
|
||||
if (bytes) {
|
||||
return wasmCrypto.digest(algorithm.name, bytes, undefined);
|
||||
} else if ((data as Iterable<BufferSource>)[Symbol.iterator]) {
|
||||
const context = new wasmCrypto.DigestContext(algorithm.name);
|
||||
for (const chunk of data as Iterable<BufferSource>) {
|
||||
const chunkBytes = bufferSourceBytes(chunk);
|
||||
if (!chunkBytes) {
|
||||
throw new TypeError("data contained chunk of the wrong type");
|
||||
}
|
||||
context.update(chunkBytes);
|
||||
}
|
||||
return context.digestAndDrop(algorithm.length);
|
||||
} else {
|
||||
throw new TypeError(
|
||||
"data must be a BufferSource or Iterable<BufferSource>",
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/** Digest algorithms supported by WebCrypto. */
|
||||
const webCryptoDigestAlgorithms = [
|
||||
"SHA-384",
|
||||
"SHA-256",
|
||||
"SHA-512",
|
||||
// insecure (length-extendable and collidable):
|
||||
"SHA-1",
|
||||
] as const;
|
||||
|
||||
type DigestAlgorithmName = WasmDigestAlgorithm;
|
||||
|
||||
type DigestAlgorithmObject = {
|
||||
name: DigestAlgorithmName;
|
||||
length?: number;
|
||||
};
|
||||
|
||||
type DigestAlgorithm = DigestAlgorithmName | DigestAlgorithmObject;
|
||||
|
||||
const normalizeAlgorithm = (algorithm: DigestAlgorithm) =>
|
||||
((typeof algorithm === "string") ? { name: algorithm.toUpperCase() } : {
|
||||
...algorithm,
|
||||
name: algorithm.name.toUpperCase(),
|
||||
}) as DigestAlgorithmObject;
|
||||
|
||||
export { stdCrypto as crypto };
|
1259
crypto/test.ts
Normal file
1259
crypto/test.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
import { createHash } from "../../hash/mod.ts";
|
||||
import { Buffer } from "../buffer.ts";
|
||||
import { createHash } from "../crypto.ts";
|
||||
import { MAX_ALLOC } from "./constants.ts";
|
||||
import { HASH_DATA } from "./types.ts";
|
||||
|
||||
@ -22,20 +22,14 @@ type Algorithms =
|
||||
| "sha384"
|
||||
| "sha512";
|
||||
|
||||
function createHasher(alg: Algorithms) {
|
||||
let normalizedAlg: NormalizedAlgorithms;
|
||||
if (alg === "rmd160") {
|
||||
normalizedAlg = "ripemd160";
|
||||
} else {
|
||||
normalizedAlg = alg;
|
||||
}
|
||||
return (value: Uint8Array) =>
|
||||
Buffer.from(createHash(normalizedAlg).update(value).digest());
|
||||
}
|
||||
const createHasher = (algorithm: string) =>
|
||||
(value: Uint8Array) =>
|
||||
Buffer.from(createHash(algorithm).update(value).digest() as Buffer);
|
||||
|
||||
function getZeroes(zeros: number) {
|
||||
return Buffer.alloc(zeros);
|
||||
}
|
||||
|
||||
const sizes = {
|
||||
md5: 16,
|
||||
sha1: 20,
|
||||
|
@ -385,7 +385,7 @@ Deno.test("pbkdf2 hashes data correctly", () => {
|
||||
dkLen,
|
||||
algorithm as Algorithms,
|
||||
(err, res) => {
|
||||
assert(!err);
|
||||
assert(!err, String(err));
|
||||
assertEquals(
|
||||
res?.toString("hex"),
|
||||
results[algorithm as Algorithms],
|
||||
|
109
node/crypto.ts
109
node/crypto.ts
@ -2,16 +2,35 @@
|
||||
// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license.
|
||||
import { default as randomBytes } from "./_crypto/randomBytes.ts";
|
||||
import {
|
||||
createHash as stdCreateHash,
|
||||
Hasher,
|
||||
SupportedAlgorithm,
|
||||
supportedAlgorithms,
|
||||
} from "../hash/mod.ts";
|
||||
crypto as wasmCrypto,
|
||||
DigestAlgorithm,
|
||||
digestAlgorithms,
|
||||
} from "../_wasm_crypto/mod.ts";
|
||||
import { pbkdf2, pbkdf2Sync } from "./_crypto/pbkdf2.ts";
|
||||
import { Buffer } from "./buffer.ts";
|
||||
import { Transform } from "./stream.ts";
|
||||
import { TransformOptions } from "./_stream/transform.ts";
|
||||
import { encode as encodeToHex } from "../encoding/hex.ts";
|
||||
import { encode as encodeToBase64 } from "../encoding/base64.ts";
|
||||
|
||||
const coerceToBytes = (data: string | BufferSource): Uint8Array => {
|
||||
if (data instanceof Uint8Array) {
|
||||
return data;
|
||||
} else if (typeof data === "string") {
|
||||
// This assumes UTF-8, which may not be correct.
|
||||
return new TextEncoder().encode(data);
|
||||
} else if (ArrayBuffer.isView(data)) {
|
||||
return new Uint8Array(
|
||||
data.buffer,
|
||||
data.byteOffset,
|
||||
data.byteLength,
|
||||
);
|
||||
} else if (data instanceof ArrayBuffer) {
|
||||
return new Uint8Array(data);
|
||||
} else {
|
||||
throw new TypeError("expected data to be string | BufferSource");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The Hash class is a utility for creating hash digests of data. It can be used in one of two ways:
|
||||
@ -22,34 +41,58 @@ import { encode as encodeToHex } from "../encoding/hex.ts";
|
||||
* The crypto.createHash() method is used to create Hash instances. Hash objects are not to be created directly using the new keyword.
|
||||
*/
|
||||
export class Hash extends Transform {
|
||||
public hash: Hasher;
|
||||
constructor(algorithm: SupportedAlgorithm, _opts?: TransformOptions) {
|
||||
#context: wasmCrypto.DigestContext;
|
||||
|
||||
constructor(
|
||||
algorithm: string | wasmCrypto.DigestContext,
|
||||
_opts?: TransformOptions,
|
||||
) {
|
||||
super({
|
||||
transform(chunk: string, _encoding: string, callback: () => void): void {
|
||||
hash.update(chunk);
|
||||
context.update(coerceToBytes(chunk));
|
||||
callback();
|
||||
},
|
||||
flush(callback: () => void): void {
|
||||
this.push(hash.digest());
|
||||
this.push(context.digest(undefined));
|
||||
callback();
|
||||
},
|
||||
});
|
||||
const hash = this.hash = stdCreateHash(algorithm);
|
||||
|
||||
if (typeof algorithm === "string") {
|
||||
// Node/OpenSSL and WebCrypto format some digest names differently;
|
||||
// we attempt to handle those here.
|
||||
algorithm = algorithm.toUpperCase();
|
||||
if (opensslToWebCryptoDigestNames[algorithm]) {
|
||||
algorithm = opensslToWebCryptoDigestNames[algorithm];
|
||||
}
|
||||
this.#context = new wasmCrypto.DigestContext(
|
||||
algorithm as DigestAlgorithm,
|
||||
);
|
||||
} else {
|
||||
this.#context = algorithm;
|
||||
}
|
||||
|
||||
const context = this.#context;
|
||||
}
|
||||
|
||||
// TODO(kt3k): Implement copy method
|
||||
// copy(options) { ... }
|
||||
copy(): Hash {
|
||||
return new Hash(this.#context.clone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the hash content with the given data.
|
||||
*/
|
||||
update(data: string | ArrayBuffer, _encoding?: string): this {
|
||||
let bytes;
|
||||
if (typeof data === "string") {
|
||||
data = new TextEncoder().encode(data);
|
||||
this.hash.update(data);
|
||||
bytes = coerceToBytes(data);
|
||||
} else {
|
||||
this.hash.update(data);
|
||||
bytes = coerceToBytes(data);
|
||||
}
|
||||
|
||||
this.#context.update(bytes);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -58,34 +101,50 @@ export class Hash extends Transform {
|
||||
*
|
||||
* If encoding is provided a string will be returned; otherwise a Buffer is returned.
|
||||
*
|
||||
* Supported encoding is currently 'hex' only. 'binary', 'base64' will be supported in the future versions.
|
||||
* Supported encoding is currently 'hex', 'binary', 'base64'.
|
||||
*/
|
||||
digest(encoding?: string): Buffer | string {
|
||||
const digest = this.hash.digest();
|
||||
const digest = this.#context.digest(undefined);
|
||||
if (encoding === undefined) {
|
||||
return Buffer.from(digest);
|
||||
}
|
||||
|
||||
switch (encoding) {
|
||||
case "hex": {
|
||||
case "hex":
|
||||
return new TextDecoder().decode(encodeToHex(new Uint8Array(digest)));
|
||||
}
|
||||
// TODO(kt3k): Support more output encodings such as base64, binary, etc
|
||||
default: {
|
||||
case "binary":
|
||||
return String.fromCharCode(...digest);
|
||||
case "base64":
|
||||
return encodeToBase64(digest);
|
||||
default:
|
||||
throw new Error(
|
||||
`The output encoding for hash digest is not impelemented: ${encoding}`,
|
||||
`The output encoding for hash digest is not implemented: ${encoding}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported digest names that OpenSSL/Node and WebCrypto identify differently.
|
||||
*/
|
||||
const opensslToWebCryptoDigestNames: Record<string, DigestAlgorithm> = {
|
||||
"BLAKE2B512": "BLAKE2B",
|
||||
"BLAKE2S256": "BLAKE2S",
|
||||
"RIPEMD160": "RIPEMD-160",
|
||||
"RMD160": "RIPEMD-160",
|
||||
"SHA1": "SHA-1",
|
||||
"SHA224": "SHA-224",
|
||||
"SHA256": "SHA-256",
|
||||
"SHA384": "SHA-384",
|
||||
"SHA512": "SHA-512",
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates and returns a Hash object that can be used to generate hash digests
|
||||
* using the given `algorithm`. Optional `options` argument controls stream behavior.
|
||||
*/
|
||||
export function createHash(
|
||||
algorithm: SupportedAlgorithm,
|
||||
algorithm: string,
|
||||
opts?: TransformOptions,
|
||||
) {
|
||||
return new Hash(algorithm, opts);
|
||||
@ -94,8 +153,8 @@ export function createHash(
|
||||
/**
|
||||
* Returns an array of the names of the supported hash algorithms, such as 'sha1'.
|
||||
*/
|
||||
export function getHashes(): SupportedAlgorithm[] {
|
||||
return supportedAlgorithms.slice();
|
||||
export function getHashes(): readonly string[] {
|
||||
return digestAlgorithms;
|
||||
}
|
||||
|
||||
export default { Hash, createHash, getHashes, pbkdf2, pbkdf2Sync, randomBytes };
|
||||
|
11
ws/mod.ts
11
ws/mod.ts
@ -2,7 +2,9 @@
|
||||
import { hasOwnProperty } from "../_util/has_own_property.ts";
|
||||
import { BufReader, BufWriter } from "../io/bufio.ts";
|
||||
import { readLong, readShort, sliceLongToBytes } from "../io/ioutil.ts";
|
||||
import { Sha1 } from "../hash/sha1.ts";
|
||||
import { crypto } from "../crypto/mod.ts";
|
||||
import * as base64 from "../encoding/base64.ts";
|
||||
|
||||
import { writeResponse } from "../http/_io.ts";
|
||||
import { TextProtoReader } from "../textproto/mod.ts";
|
||||
import { Deferred, deferred } from "../async/deferred.ts";
|
||||
@ -410,10 +412,9 @@ const kGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
|
||||
/** Create value of Sec-WebSocket-Accept header from inputted nonce. */
|
||||
export function createSecAccept(nonce: string): string {
|
||||
const sha1 = new Sha1();
|
||||
sha1.update(nonce + kGUID);
|
||||
const bytes = sha1.digest();
|
||||
return btoa(String.fromCharCode(...bytes));
|
||||
return base64.encode(
|
||||
crypto.subtle.digestSync("SHA-1", new TextEncoder().encode(nonce + kGUID)),
|
||||
);
|
||||
}
|
||||
|
||||
/** Upgrade inputted TCP connection into WebSocket connection. */
|
||||
|
Loading…
Reference in New Issue
Block a user