mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
feat(crypto): implement generateKey() and sign() (#9614)
Co-authored-by: Luca Casonato <hello@lcas.dev> Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
This commit is contained in:
parent
2ed222fceb
commit
570309d795
12 changed files with 3459 additions and 275 deletions
187
Cargo.lock
generated
187
Cargo.lock
generated
|
@ -175,6 +175,12 @@ dependencies = [
|
||||||
"syn 1.0.65",
|
"syn 1.0.65",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -443,7 +449,7 @@ version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278"
|
checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg 1.0.1",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
@ -646,8 +652,14 @@ version = "0.24.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deno_core",
|
"deno_core",
|
||||||
"deno_web",
|
"deno_web",
|
||||||
|
"lazy_static",
|
||||||
|
"num-traits",
|
||||||
"rand 0.8.4",
|
"rand 0.8.4",
|
||||||
"ring",
|
"ring",
|
||||||
|
"rsa",
|
||||||
|
"serde",
|
||||||
|
"sha-1",
|
||||||
|
"sha2",
|
||||||
"tokio",
|
"tokio",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
@ -953,7 +965,7 @@ checksum = "cd5de0d5e8bdbbad4f6d71d754164803ba4aef42f50db2e65c8a5ca4185ac69f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"fnv",
|
"fnv",
|
||||||
"num-bigint",
|
"num-bigint 0.2.6",
|
||||||
"swc_atoms",
|
"swc_atoms",
|
||||||
"swc_common",
|
"swc_common",
|
||||||
"swc_ecmascript",
|
"swc_ecmascript",
|
||||||
|
@ -1247,7 +1259,7 @@ version = "0.3.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121"
|
checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg 1.0.1",
|
||||||
"proc-macro-hack",
|
"proc-macro-hack",
|
||||||
"proc-macro2 1.0.26",
|
"proc-macro2 1.0.26",
|
||||||
"quote 1.0.9",
|
"quote 1.0.9",
|
||||||
|
@ -1272,7 +1284,7 @@ version = "0.3.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967"
|
checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg 1.0.1",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
|
@ -1724,7 +1736,7 @@ version = "1.6.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
|
checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg 1.0.1",
|
||||||
"hashbrown 0.9.1",
|
"hashbrown 0.9.1",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
@ -1862,6 +1874,9 @@ name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
dependencies = [
|
||||||
|
"spin",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lexical"
|
name = "lexical"
|
||||||
|
@ -1902,6 +1917,12 @@ dependencies = [
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libm"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libsqlite3-sys"
|
name = "libsqlite3-sys"
|
||||||
version = "0.22.2"
|
version = "0.22.2"
|
||||||
|
@ -2051,7 +2072,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
|
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler",
|
"adler",
|
||||||
"autocfg",
|
"autocfg 1.0.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2153,19 +2174,59 @@ version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
|
checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg 1.0.1",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-bigint"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 1.0.1",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-bigint-dig"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4547ee5541c18742396ae2c895d0717d0f886d8823b8399cdaf7b07d63ad0480"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 0.1.7",
|
||||||
|
"byteorder",
|
||||||
|
"lazy_static",
|
||||||
|
"libm",
|
||||||
|
"num-integer",
|
||||||
|
"num-iter",
|
||||||
|
"num-traits",
|
||||||
|
"rand 0.8.4",
|
||||||
|
"smallvec",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.44"
|
version = "0.1.44"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg 1.0.1",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-iter"
|
||||||
|
version = "0.1.42"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 1.0.1",
|
||||||
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2175,7 +2236,8 @@ version = "0.2.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg 1.0.1",
|
||||||
|
"libm",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2263,6 +2325,17 @@ dependencies = [
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pem"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.13.0",
|
||||||
|
"once_cell",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
@ -2741,6 +2814,26 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rsa"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68ef841a26fc5d040ced0417c6c6a64ee851f42489df11cdf0218e545b6f8d28"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"digest",
|
||||||
|
"lazy_static",
|
||||||
|
"num-bigint-dig",
|
||||||
|
"num-integer",
|
||||||
|
"num-iter",
|
||||||
|
"num-traits",
|
||||||
|
"pem",
|
||||||
|
"rand 0.8.4",
|
||||||
|
"simple_asn1",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rusqlite"
|
name = "rusqlite"
|
||||||
version = "0.25.3"
|
version = "0.25.3"
|
||||||
|
@ -2962,6 +3055,19 @@ dependencies = [
|
||||||
"opaque-debug",
|
"opaque-debug",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
"opaque-debug",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shell-escape"
|
name = "shell-escape"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
|
@ -2977,6 +3083,18 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simple_asn1"
|
||||||
|
version = "0.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8eb4ea60fb301dc81dfc113df680571045d375ab7345d171c5dc7d7e13107a80"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"num-bigint 0.4.0",
|
||||||
|
"num-traits",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
|
@ -3136,6 +3254,12 @@ version = "0.9.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
|
checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "swc_atoms"
|
name = "swc_atoms"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
|
@ -3187,7 +3311,7 @@ dependencies = [
|
||||||
"from_variant",
|
"from_variant",
|
||||||
"fxhash",
|
"fxhash",
|
||||||
"log",
|
"log",
|
||||||
"num-bigint",
|
"num-bigint 0.2.6",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"owning_ref",
|
"owning_ref",
|
||||||
"scoped-tls",
|
"scoped-tls",
|
||||||
|
@ -3206,7 +3330,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8060c27a8932c57e5a878acc1c64b44371cce1c88eb3af1f3d12a2628d606a6"
|
checksum = "f8060c27a8932c57e5a878acc1c64b44371cce1c88eb3af1f3d12a2628d606a6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"is-macro",
|
"is-macro",
|
||||||
"num-bigint",
|
"num-bigint 0.2.6",
|
||||||
"serde",
|
"serde",
|
||||||
"string_enum",
|
"string_enum",
|
||||||
"swc_atoms",
|
"swc_atoms",
|
||||||
|
@ -3220,7 +3344,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "936fd096ebfc8d8c8d7f5cfdc79b13d06e56e0810abbe3de37376b537a40debb"
|
checksum = "936fd096ebfc8d8c8d7f5cfdc79b13d06e56e0810abbe3de37376b537a40debb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"num-bigint",
|
"num-bigint 0.2.6",
|
||||||
"sourcemap",
|
"sourcemap",
|
||||||
"swc_atoms",
|
"swc_atoms",
|
||||||
"swc_common",
|
"swc_common",
|
||||||
|
@ -3278,7 +3402,7 @@ dependencies = [
|
||||||
"fxhash",
|
"fxhash",
|
||||||
"lexical",
|
"lexical",
|
||||||
"log",
|
"log",
|
||||||
"num-bigint",
|
"num-bigint 0.2.6",
|
||||||
"serde",
|
"serde",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"swc_atoms",
|
"swc_atoms",
|
||||||
|
@ -3444,7 +3568,7 @@ version = "0.34.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d5b93c94bd4fca51ad5c2588e34b46dc5a9b1f079c9b1ec595a968eb4ca8acf"
|
checksum = "3d5b93c94bd4fca51ad5c2588e34b46dc5a9b1f079c9b1ec595a968eb4ca8acf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint 0.2.6",
|
||||||
"swc_atoms",
|
"swc_atoms",
|
||||||
"swc_common",
|
"swc_common",
|
||||||
"swc_ecma_ast",
|
"swc_ecma_ast",
|
||||||
|
@ -3536,6 +3660,18 @@ dependencies = [
|
||||||
"unicode-xid 0.2.2",
|
"unicode-xid 0.2.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "synstructure"
|
||||||
|
version = "0.12.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.26",
|
||||||
|
"quote 1.0.9",
|
||||||
|
"syn 1.0.65",
|
||||||
|
"unicode-xid 0.2.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sys-info"
|
name = "sys-info"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
@ -3672,7 +3808,7 @@ version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "570c2eb13b3ab38208130eccd41be92520388791207fde783bda7c1e8ace28d4"
|
checksum = "570c2eb13b3ab38208130eccd41be92520388791207fde783bda7c1e8ace28d4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg 1.0.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -4340,3 +4476,24 @@ checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "81a974bcdd357f0dca4d41677db03436324d45a4c9ed2d0b873a5a360ce41c36"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize_derive"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3f369ddb18862aba61aa49bf31e74d29f0f162dec753063200e1dc084345d16"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.26",
|
||||||
|
"quote 1.0.9",
|
||||||
|
"syn 1.0.65",
|
||||||
|
"synstructure",
|
||||||
|
]
|
||||||
|
|
58
cli/tests/unit/webcrypto_test.ts
Normal file
58
cli/tests/unit/webcrypto_test.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import { assert, assertEquals, unitTest } from "./test_util.ts";
|
||||||
|
|
||||||
|
unitTest(async function testGenerateRSAKey() {
|
||||||
|
const subtle = window.crypto.subtle;
|
||||||
|
assert(subtle);
|
||||||
|
|
||||||
|
const keyPair = await subtle.generateKey(
|
||||||
|
{
|
||||||
|
name: "RSA-PSS",
|
||||||
|
modulusLength: 2048,
|
||||||
|
publicExponent: new Uint8Array([1, 0, 1]),
|
||||||
|
hash: "SHA-256",
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
["sign", "verify"],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(keyPair.privateKey);
|
||||||
|
assert(keyPair.publicKey);
|
||||||
|
assertEquals(keyPair.privateKey.extractable, true);
|
||||||
|
assert(keyPair.privateKey.usages.includes("sign"));
|
||||||
|
});
|
||||||
|
|
||||||
|
unitTest(async function testGenerateHMACKey() {
|
||||||
|
const key = await window.crypto.subtle.generateKey(
|
||||||
|
{
|
||||||
|
name: "HMAC",
|
||||||
|
hash: "SHA-512",
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
["sign", "verify"],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(key);
|
||||||
|
assertEquals(key.extractable, true);
|
||||||
|
assert(key.usages.includes("sign"));
|
||||||
|
});
|
||||||
|
|
||||||
|
unitTest(async function testSignECDSA() {
|
||||||
|
const key = await window.crypto.subtle.generateKey(
|
||||||
|
{
|
||||||
|
name: "ECDSA",
|
||||||
|
namedCurve: "P-384",
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
["sign", "verify"],
|
||||||
|
);
|
||||||
|
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const encoded = encoder.encode("Hello, World!");
|
||||||
|
const signature = await window.crypto.subtle.sign(
|
||||||
|
{ name: "ECDSA", hash: "SHA-384" },
|
||||||
|
key.privateKey,
|
||||||
|
encoded,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(signature);
|
||||||
|
});
|
675
extensions/crypto/00_crypto.js
Normal file
675
extensions/crypto/00_crypto.js
Normal file
|
@ -0,0 +1,675 @@
|
||||||
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
// @ts-check
|
||||||
|
/// <reference path="../../core/lib.deno_core.d.ts" />
|
||||||
|
/// <reference path="../webidl/internal.d.ts" />
|
||||||
|
/// <reference path="../web/lib.deno_web.d.ts" />
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
((window) => {
|
||||||
|
const core = window.Deno.core;
|
||||||
|
const webidl = window.__bootstrap.webidl;
|
||||||
|
const { DOMException } = window.__bootstrap.domException;
|
||||||
|
|
||||||
|
// P-521 is not yet supported.
|
||||||
|
const supportedNamedCurves = ["P-256", "P-384"];
|
||||||
|
|
||||||
|
const simpleAlgorithmDictionaries = {
|
||||||
|
RsaHashedKeyGenParams: { hash: "HashAlgorithmIdentifier" },
|
||||||
|
EcKeyGenParams: {},
|
||||||
|
HmacKeyGenParams: { hash: "HashAlgorithmIdentifier" },
|
||||||
|
RsaPssParams: {},
|
||||||
|
EcdsaParams: { hash: "HashAlgorithmIdentifier" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const supportedAlgorithms = {
|
||||||
|
"digest": {
|
||||||
|
"SHA-1": null,
|
||||||
|
"SHA-256": null,
|
||||||
|
"SHA-384": null,
|
||||||
|
"SHA-512": null,
|
||||||
|
},
|
||||||
|
"generateKey": {
|
||||||
|
"RSASSA-PKCS1-v1_5": "RsaHashedKeyGenParams",
|
||||||
|
"RSA-PSS": "RsaHashedKeyGenParams",
|
||||||
|
"ECDSA": "EcKeyGenParams",
|
||||||
|
"HMAC": "HmacKeyGenParams",
|
||||||
|
},
|
||||||
|
"sign": {
|
||||||
|
"RSASSA-PKCS1-v1_5": null,
|
||||||
|
"RSA-PSS": "RsaPssParams",
|
||||||
|
"ECDSA": "EcdsaParams",
|
||||||
|
"HMAC": null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// See https://www.w3.org/TR/WebCryptoAPI/#dfn-normalize-an-algorithm
|
||||||
|
function normalizeAlgorithm(algorithm, op) {
|
||||||
|
if (typeof algorithm == "string") {
|
||||||
|
return normalizeAlgorithm({ name: algorithm }, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.
|
||||||
|
const registeredAlgorithms = supportedAlgorithms[op];
|
||||||
|
// 2. 3.
|
||||||
|
const initialAlg = webidl.converters.Algorithm(algorithm, {
|
||||||
|
prefix: "Failed to normalize algorithm",
|
||||||
|
context: "passed algorithm",
|
||||||
|
});
|
||||||
|
// 4.
|
||||||
|
let algName = initialAlg.name;
|
||||||
|
|
||||||
|
// 5.
|
||||||
|
let desiredType = undefined;
|
||||||
|
for (const key in registeredAlgorithms) {
|
||||||
|
if (key.toLowerCase() === algName.toLowerCase()) {
|
||||||
|
algName = key;
|
||||||
|
desiredType = registeredAlgorithms[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (desiredType === undefined) {
|
||||||
|
throw new DOMException(
|
||||||
|
"Unrecognized algorithm name",
|
||||||
|
"NotSupportedError",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast path everything below if the registered dictionary is "None".
|
||||||
|
if (desiredType === null) {
|
||||||
|
return { name: algName };
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedAlgorithm = webidl.converters[desiredType](algorithm, {
|
||||||
|
prefix: "Failed to normalize algorithm",
|
||||||
|
context: "passed algorithm",
|
||||||
|
});
|
||||||
|
normalizedAlgorithm.name = algName;
|
||||||
|
|
||||||
|
const dict = simpleAlgorithmDictionaries[desiredType];
|
||||||
|
for (const member in dict) {
|
||||||
|
const idlType = dict[member];
|
||||||
|
const idlValue = normalizedAlgorithm[member];
|
||||||
|
|
||||||
|
if (idlType === "BufferSource") {
|
||||||
|
normalizedAlgorithm[member] = new Uint8Array(
|
||||||
|
(ArrayBuffer.isView(idlValue) ? idlValue.buffer : idlValue).slice(
|
||||||
|
idlValue.byteOffset ?? 0,
|
||||||
|
idlValue.byteLength,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (idlType === "HashAlgorithmIdentifier") {
|
||||||
|
normalizedAlgorithm[member] = normalizeAlgorithm(idlValue, "digest");
|
||||||
|
} else if (idlType === "AlgorithmIdentifier") {
|
||||||
|
// TODO(lucacasonato): implement
|
||||||
|
throw new TypeError("unimplemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should match op_crypto_subtle_digest() in extensions/crypto/lib.rs
|
||||||
|
function digestToId(name) {
|
||||||
|
switch (name) {
|
||||||
|
case "SHA-1":
|
||||||
|
return 0;
|
||||||
|
case "SHA-256":
|
||||||
|
return 1;
|
||||||
|
case "SHA-384":
|
||||||
|
return 2;
|
||||||
|
case "SHA-512":
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const _handle = Symbol("[[handle]]");
|
||||||
|
const _algorithm = Symbol("[[algorithm]]");
|
||||||
|
const _extractable = Symbol("[[extractable]]");
|
||||||
|
const _usages = Symbol("[[usages]]");
|
||||||
|
const _type = Symbol("[[type]]");
|
||||||
|
|
||||||
|
class CryptoKey {
|
||||||
|
/** @type {string} */
|
||||||
|
[_type];
|
||||||
|
/** @type {boolean} */
|
||||||
|
[_extractable];
|
||||||
|
/** @type {object} */
|
||||||
|
[_algorithm];
|
||||||
|
/** @type {string[]} */
|
||||||
|
[_usages];
|
||||||
|
/** @type {object} */
|
||||||
|
[_handle];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
webidl.illegalConstructor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {string} */
|
||||||
|
get type() {
|
||||||
|
webidl.assertBranded(this, CryptoKey);
|
||||||
|
return this[_type];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {boolean} */
|
||||||
|
get extractable() {
|
||||||
|
webidl.assertBranded(this, CryptoKey);
|
||||||
|
return this[_extractable];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {string[]} */
|
||||||
|
get usages() {
|
||||||
|
webidl.assertBranded(this, CryptoKey);
|
||||||
|
// TODO(lucacasonato): return a SameObject copy
|
||||||
|
return this[_usages];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {object} */
|
||||||
|
get algorithm() {
|
||||||
|
webidl.assertBranded(this, CryptoKey);
|
||||||
|
// TODO(lucacasonato): return a SameObject copy
|
||||||
|
return this[_algorithm];
|
||||||
|
}
|
||||||
|
|
||||||
|
get [Symbol.toStringTag]() {
|
||||||
|
return "CryptoKey";
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.for("Deno.customInspect")](inspect) {
|
||||||
|
return `${this.constructor.name} ${
|
||||||
|
inspect({
|
||||||
|
type: this.type,
|
||||||
|
extractable: this.extractable,
|
||||||
|
algorithm: this.algorithm,
|
||||||
|
usages: this.usages,
|
||||||
|
})
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webidl.configurePrototype(CryptoKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} type
|
||||||
|
* @param {boolean} extractable
|
||||||
|
* @param {string[]} usages
|
||||||
|
* @param {object} algorithm
|
||||||
|
* @param {object} handle
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function constructKey(type, extractable, usages, algorithm, handle) {
|
||||||
|
const key = webidl.createBranded(CryptoKey);
|
||||||
|
key[_type] = type;
|
||||||
|
key[_extractable] = extractable;
|
||||||
|
key[_usages] = usages;
|
||||||
|
key[_algorithm] = algorithm;
|
||||||
|
key[_handle] = handle;
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webcrypto/#concept-usage-intersection
|
||||||
|
// TODO(littledivy): When the need arises, make `b` a list.
|
||||||
|
/**
|
||||||
|
* @param {string[]} a
|
||||||
|
* @param {string} b
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function usageIntersection(a, b) {
|
||||||
|
return a.includes(b) ? [b] : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(lucacasonato): this should be moved to rust
|
||||||
|
/** @type {WeakMap<object, object>} */
|
||||||
|
const KEY_STORE = new WeakMap();
|
||||||
|
|
||||||
|
class SubtleCrypto {
|
||||||
|
constructor() {
|
||||||
|
webidl.illegalConstructor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} algorithm
|
||||||
|
* @param {BufferSource} data
|
||||||
|
* @returns {Promise<Uint8Array>}
|
||||||
|
*/
|
||||||
|
async digest(algorithm, data) {
|
||||||
|
webidl.assertBranded(this, SubtleCrypto);
|
||||||
|
const prefix = "Failed to execute 'digest' on 'SubtleCrypto'";
|
||||||
|
webidl.requiredArguments(arguments.length, 2, { prefix });
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} algorithm
|
||||||
|
* @param {CryptoKey} key
|
||||||
|
* @param {BufferSource} data
|
||||||
|
* @returns {Promise<any>}
|
||||||
|
*/
|
||||||
|
async sign(algorithm, key, data) {
|
||||||
|
webidl.assertBranded(this, SubtleCrypto);
|
||||||
|
const prefix = "Failed to execute 'sign' on 'SubtleCrypto'";
|
||||||
|
webidl.requiredArguments(arguments.length, 3, { prefix });
|
||||||
|
algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
key = webidl.converters.CryptoKey(key, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
data = webidl.converters.BufferSource(data, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 3",
|
||||||
|
});
|
||||||
|
|
||||||
|
// 1.
|
||||||
|
if (ArrayBuffer.isView(data)) {
|
||||||
|
data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
||||||
|
} else {
|
||||||
|
data = new Uint8Array(data);
|
||||||
|
}
|
||||||
|
data = data.slice();
|
||||||
|
|
||||||
|
// 2.
|
||||||
|
const normalizedAlgorithm = normalizeAlgorithm(algorithm, "sign");
|
||||||
|
|
||||||
|
const handle = key[_handle];
|
||||||
|
const keyData = KEY_STORE.get(handle);
|
||||||
|
|
||||||
|
// 8.
|
||||||
|
if (normalizedAlgorithm.name !== key[_algorithm].name) {
|
||||||
|
throw new DOMException(
|
||||||
|
"Signing algorithm doesn't match key algorithm.",
|
||||||
|
"InvalidAccessError",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9.
|
||||||
|
if (!key[_usages].includes("sign")) {
|
||||||
|
throw new DOMException(
|
||||||
|
"Key does not support the 'sign' operation.",
|
||||||
|
"InvalidAccessError",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (normalizedAlgorithm.name) {
|
||||||
|
case "RSASSA-PKCS1-v1_5": {
|
||||||
|
// 1.
|
||||||
|
if (key[_type] !== "private") {
|
||||||
|
throw new DOMException(
|
||||||
|
"Key type not supported",
|
||||||
|
"InvalidAccessError",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.
|
||||||
|
const hashAlgorithm = key[_algorithm].hash.name;
|
||||||
|
const signature = await core.opAsync("op_crypto_sign_key", {
|
||||||
|
key: keyData,
|
||||||
|
algorithm: "RSASSA-PKCS1-v1_5",
|
||||||
|
hash: hashAlgorithm,
|
||||||
|
}, data);
|
||||||
|
|
||||||
|
return signature.buffer;
|
||||||
|
}
|
||||||
|
case "RSA-PSS": {
|
||||||
|
// 1.
|
||||||
|
if (key[_type] !== "private") {
|
||||||
|
throw new DOMException(
|
||||||
|
"Key type not supported",
|
||||||
|
"InvalidAccessError",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.
|
||||||
|
const hashAlgorithm = key[_algorithm].hash.name;
|
||||||
|
const signature = await core.opAsync("op_crypto_sign_key", {
|
||||||
|
key: keyData,
|
||||||
|
algorithm: "RSA-PSS",
|
||||||
|
hash: hashAlgorithm,
|
||||||
|
saltLength: normalizedAlgorithm.saltLength,
|
||||||
|
}, data);
|
||||||
|
|
||||||
|
return signature.buffer;
|
||||||
|
}
|
||||||
|
case "ECDSA": {
|
||||||
|
// 1.
|
||||||
|
if (key[_type] !== "private") {
|
||||||
|
throw new DOMException(
|
||||||
|
"Key type not supported",
|
||||||
|
"InvalidAccessError",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.
|
||||||
|
const hashAlgorithm = normalizedAlgorithm.hash.name;
|
||||||
|
const namedCurve = key[_algorithm].namedCurve;
|
||||||
|
if (!supportedNamedCurves.includes(namedCurve)) {
|
||||||
|
throw new DOMException("Curve not supported", "NotSupportedError");
|
||||||
|
}
|
||||||
|
|
||||||
|
const signature = await core.opAsync("op_crypto_sign_key", {
|
||||||
|
key: keyData,
|
||||||
|
algorithm: "ECDSA",
|
||||||
|
hash: hashAlgorithm,
|
||||||
|
namedCurve,
|
||||||
|
}, data);
|
||||||
|
|
||||||
|
return signature.buffer;
|
||||||
|
}
|
||||||
|
case "HMAC": {
|
||||||
|
const hashAlgorithm = key[_algorithm].hash.name;
|
||||||
|
|
||||||
|
const signature = await core.opAsync("op_crypto_sign_key", {
|
||||||
|
key: keyData,
|
||||||
|
algorithm: "HMAC",
|
||||||
|
hash: hashAlgorithm,
|
||||||
|
}, data);
|
||||||
|
|
||||||
|
return signature.buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new TypeError("unreachable");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} algorithm
|
||||||
|
* @param {boolean} extractable
|
||||||
|
* @param {KeyUsage[]} keyUsages
|
||||||
|
* @returns {Promise<any>}
|
||||||
|
*/
|
||||||
|
async generateKey(algorithm, extractable, keyUsages) {
|
||||||
|
webidl.assertBranded(this, SubtleCrypto);
|
||||||
|
const prefix = "Failed to execute 'generateKey' on 'SubtleCrypto'";
|
||||||
|
webidl.requiredArguments(arguments.length, 3, { prefix });
|
||||||
|
algorithm = webidl.converters.AlgorithmIdentifier(algorithm, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
extractable = webidl.converters["boolean"](extractable, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
keyUsages = webidl.converters["sequence<KeyUsage>"](keyUsages, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 3",
|
||||||
|
});
|
||||||
|
|
||||||
|
const usages = keyUsages;
|
||||||
|
|
||||||
|
const normalizedAlgorithm = normalizeAlgorithm(algorithm, "generateKey");
|
||||||
|
|
||||||
|
// https://github.com/denoland/deno/pull/9614#issuecomment-866049433
|
||||||
|
if (!extractable) {
|
||||||
|
throw new DOMException(
|
||||||
|
"Non-extractable keys are not supported",
|
||||||
|
"SecurityError",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await generateKey(
|
||||||
|
normalizedAlgorithm,
|
||||||
|
extractable,
|
||||||
|
usages,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result instanceof CryptoKey) {
|
||||||
|
const type = result[_type];
|
||||||
|
if ((type === "secret" || type === "private") && usages.length === 0) {
|
||||||
|
throw new DOMException("Invalid key usages", "SyntaxError");
|
||||||
|
}
|
||||||
|
} else if (result.privateKey instanceof CryptoKey) {
|
||||||
|
if (result.privateKey[_usages].length === 0) {
|
||||||
|
throw new DOMException("Invalid key usages", "SyntaxError");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateKey(normalizedAlgorithm, extractable, usages) {
|
||||||
|
switch (normalizedAlgorithm.name) {
|
||||||
|
case "RSASSA-PKCS1-v1_5":
|
||||||
|
case "RSA-PSS": {
|
||||||
|
// 1.
|
||||||
|
if (usages.find((u) => !["sign", "verify"].includes(u)) !== undefined) {
|
||||||
|
throw new DOMException("Invalid key usages", "SyntaxError");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.
|
||||||
|
const keyData = await core.opAsync(
|
||||||
|
"op_crypto_generate_key",
|
||||||
|
{
|
||||||
|
name: normalizedAlgorithm.name,
|
||||||
|
modulusLength: normalizedAlgorithm.modulusLength,
|
||||||
|
publicExponent: normalizedAlgorithm.publicExponent,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const handle = {};
|
||||||
|
KEY_STORE.set(handle, { type: "pkcs8", data: keyData });
|
||||||
|
|
||||||
|
// 4-8.
|
||||||
|
const algorithm = {
|
||||||
|
name: normalizedAlgorithm.name,
|
||||||
|
modulusLength: normalizedAlgorithm.modulusLength,
|
||||||
|
publicExponent: normalizedAlgorithm.publicExponent,
|
||||||
|
hash: normalizedAlgorithm.hash,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 9-13.
|
||||||
|
const publicKey = constructKey(
|
||||||
|
"public",
|
||||||
|
true,
|
||||||
|
usageIntersection(usages, "verify"),
|
||||||
|
algorithm,
|
||||||
|
handle,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 14-18.
|
||||||
|
const privateKey = constructKey(
|
||||||
|
"private",
|
||||||
|
extractable,
|
||||||
|
usageIntersection(usages, "sign"),
|
||||||
|
algorithm,
|
||||||
|
handle,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 19-22.
|
||||||
|
return { publicKey, privateKey };
|
||||||
|
}
|
||||||
|
// TODO(lucacasonato): RSA-OAEP
|
||||||
|
case "ECDSA": {
|
||||||
|
// 1.
|
||||||
|
if (usages.find((u) => !["sign", "verify"].includes(u)) !== undefined) {
|
||||||
|
throw new DOMException("Invalid key usages", "SyntaxError");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2-3.
|
||||||
|
const handle = {};
|
||||||
|
if (supportedNamedCurves.includes(normalizedAlgorithm.namedCurve)) {
|
||||||
|
const keyData = await core.opAsync("op_crypto_generate_key", {
|
||||||
|
name: "ECDSA",
|
||||||
|
namedCurve: normalizedAlgorithm.namedCurve,
|
||||||
|
});
|
||||||
|
KEY_STORE.set(handle, { type: "pkcs8", data: keyData });
|
||||||
|
} else {
|
||||||
|
throw new DOMException("Curve not supported", "NotSupportedError");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4-6.
|
||||||
|
const algorithm = {
|
||||||
|
name: "ECDSA",
|
||||||
|
namedCurve: normalizedAlgorithm.namedCurve,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 7-11.
|
||||||
|
const publicKey = constructKey(
|
||||||
|
"public",
|
||||||
|
true,
|
||||||
|
usageIntersection(usages, "verify"),
|
||||||
|
algorithm,
|
||||||
|
handle,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 12-16.
|
||||||
|
const privateKey = constructKey(
|
||||||
|
"private",
|
||||||
|
extractable,
|
||||||
|
usageIntersection(usages, "sign"),
|
||||||
|
algorithm,
|
||||||
|
handle,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 17-20.
|
||||||
|
return { publicKey, privateKey };
|
||||||
|
}
|
||||||
|
// TODO(lucacasonato): ECDH
|
||||||
|
// TODO(lucacasonato): AES-CTR
|
||||||
|
// TODO(lucacasonato): AES-CBC
|
||||||
|
// TODO(lucacasonato): AES-GCM
|
||||||
|
// TODO(lucacasonato): AES-KW
|
||||||
|
case "HMAC": {
|
||||||
|
// 1.
|
||||||
|
if (
|
||||||
|
usages.find((u) => !["sign", "verify"].includes(u)) !== undefined
|
||||||
|
) {
|
||||||
|
throw new DOMException("Invalid key usages", "SyntaxError");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.
|
||||||
|
let length;
|
||||||
|
if (normalizedAlgorithm.length === undefined) {
|
||||||
|
length = null;
|
||||||
|
} else if (normalizedAlgorithm.length !== 0) {
|
||||||
|
length = normalizedAlgorithm.length;
|
||||||
|
} else {
|
||||||
|
throw new DOMException("Invalid length", "OperationError");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3-4.
|
||||||
|
const keyData = await core.opAsync("op_crypto_generate_key", {
|
||||||
|
name: "HMAC",
|
||||||
|
hash: normalizedAlgorithm.hash.name,
|
||||||
|
length,
|
||||||
|
});
|
||||||
|
const handle = {};
|
||||||
|
KEY_STORE.set(handle, { type: "raw", data: keyData });
|
||||||
|
|
||||||
|
// 6-10.
|
||||||
|
const algorithm = {
|
||||||
|
name: "HMAC",
|
||||||
|
hash: {
|
||||||
|
name: normalizedAlgorithm.hash.name,
|
||||||
|
},
|
||||||
|
length: keyData.byteLength * 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 5, 11-13.
|
||||||
|
const key = constructKey(
|
||||||
|
"secret",
|
||||||
|
extractable,
|
||||||
|
usages,
|
||||||
|
algorithm,
|
||||||
|
handle,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 14.
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const subtle = webidl.createBranded(SubtleCrypto);
|
||||||
|
|
||||||
|
class Crypto {
|
||||||
|
constructor() {
|
||||||
|
webidl.illegalConstructor();
|
||||||
|
}
|
||||||
|
|
||||||
|
getRandomValues(arrayBufferView) {
|
||||||
|
webidl.assertBranded(this, Crypto);
|
||||||
|
const prefix = "Failed to execute 'getRandomValues' on 'Crypto'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
arrayBufferView = webidl.converters.ArrayBufferView(arrayBufferView, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
arrayBufferView instanceof Int8Array ||
|
||||||
|
arrayBufferView instanceof Uint8Array ||
|
||||||
|
arrayBufferView instanceof Int16Array ||
|
||||||
|
arrayBufferView instanceof Uint16Array ||
|
||||||
|
arrayBufferView instanceof Int32Array ||
|
||||||
|
arrayBufferView instanceof Uint32Array ||
|
||||||
|
arrayBufferView instanceof Uint8ClampedArray
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new DOMException(
|
||||||
|
"The provided ArrayBufferView is not an integer array type",
|
||||||
|
"TypeMismatchError",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const ui8 = new Uint8Array(
|
||||||
|
arrayBufferView.buffer,
|
||||||
|
arrayBufferView.byteOffset,
|
||||||
|
arrayBufferView.byteLength,
|
||||||
|
);
|
||||||
|
core.opSync("op_crypto_get_random_values", ui8);
|
||||||
|
return arrayBufferView;
|
||||||
|
}
|
||||||
|
|
||||||
|
randomUUID() {
|
||||||
|
webidl.assertBranded(this, Crypto);
|
||||||
|
return core.opSync("op_crypto_random_uuid");
|
||||||
|
}
|
||||||
|
|
||||||
|
get subtle() {
|
||||||
|
webidl.assertBranded(this, Crypto);
|
||||||
|
return subtle;
|
||||||
|
}
|
||||||
|
|
||||||
|
get [Symbol.toStringTag]() {
|
||||||
|
return "Crypto";
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.for("Deno.customInspect")](inspect) {
|
||||||
|
return `${this.constructor.name} ${inspect({})}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webidl.configurePrototype(Crypto);
|
||||||
|
|
||||||
|
window.__bootstrap.crypto = {
|
||||||
|
SubtleCrypto,
|
||||||
|
crypto: webidl.createBranded(Crypto),
|
||||||
|
Crypto,
|
||||||
|
CryptoKey,
|
||||||
|
};
|
||||||
|
})(this);
|
|
@ -1,26 +0,0 @@
|
||||||
// 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);
|
|
|
@ -1,169 +0,0 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
|
||||||
const core = window.Deno.core;
|
|
||||||
const webidl = window.__bootstrap.webidl;
|
|
||||||
const { DOMException } = window.__bootstrap.domException;
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
getRandomValues(arrayBufferView) {
|
|
||||||
webidl.assertBranded(this, Crypto);
|
|
||||||
const prefix = "Failed to execute 'getRandomValues' on 'Crypto'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
arrayBufferView = webidl.converters.ArrayBufferView(arrayBufferView, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
if (
|
|
||||||
!(
|
|
||||||
arrayBufferView instanceof Int8Array ||
|
|
||||||
arrayBufferView instanceof Uint8Array ||
|
|
||||||
arrayBufferView instanceof Int16Array ||
|
|
||||||
arrayBufferView instanceof Uint16Array ||
|
|
||||||
arrayBufferView instanceof Int32Array ||
|
|
||||||
arrayBufferView instanceof Uint32Array ||
|
|
||||||
arrayBufferView instanceof Uint8ClampedArray
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
throw new DOMException(
|
|
||||||
"The provided ArrayBufferView is not an integer array type",
|
|
||||||
"TypeMismatchError",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const ui8 = new Uint8Array(
|
|
||||||
arrayBufferView.buffer,
|
|
||||||
arrayBufferView.byteOffset,
|
|
||||||
arrayBufferView.byteLength,
|
|
||||||
);
|
|
||||||
core.opSync("op_crypto_get_random_values", ui8);
|
|
||||||
return arrayBufferView;
|
|
||||||
}
|
|
||||||
|
|
||||||
randomUUID() {
|
|
||||||
webidl.assertBranded(this, Crypto);
|
|
||||||
return core.opSync("op_crypto_random_uuid");
|
|
||||||
}
|
|
||||||
|
|
||||||
get subtle() {
|
|
||||||
webidl.assertBranded(this, Crypto);
|
|
||||||
return subtle;
|
|
||||||
}
|
|
||||||
|
|
||||||
get [Symbol.toStringTag]() {
|
|
||||||
return "Crypto";
|
|
||||||
}
|
|
||||||
|
|
||||||
[Symbol.for("Deno.customInspect")](inspect) {
|
|
||||||
return `${this.constructor.name} ${inspect({})}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
webidl.configurePrototype(Crypto);
|
|
||||||
|
|
||||||
window.__bootstrap.crypto = {
|
|
||||||
SubtleCrypto,
|
|
||||||
crypto: webidl.createBranded(Crypto),
|
|
||||||
Crypto,
|
|
||||||
};
|
|
||||||
})(this);
|
|
164
extensions/crypto/01_webidl.js
Normal file
164
extensions/crypto/01_webidl.js
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
// @ts-check
|
||||||
|
/// <reference path="../../core/lib.deno_core.d.ts" />
|
||||||
|
/// <reference path="../webidl/internal.d.ts" />
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
((window) => {
|
||||||
|
const webidl = window.__bootstrap.webidl;
|
||||||
|
const { CryptoKey } = window.__bootstrap.crypto;
|
||||||
|
|
||||||
|
webidl.converters.AlgorithmIdentifier = (V, opts) => {
|
||||||
|
// Union for (object or DOMString)
|
||||||
|
if (webidl.type(V) == "Object") {
|
||||||
|
return webidl.converters.object(V, opts);
|
||||||
|
}
|
||||||
|
return webidl.converters.DOMString(V, opts);
|
||||||
|
};
|
||||||
|
|
||||||
|
webidl.converters.KeyType = webidl.createEnumConverter("KeyType", [
|
||||||
|
"public",
|
||||||
|
"private",
|
||||||
|
"secret",
|
||||||
|
]);
|
||||||
|
|
||||||
|
webidl.converters.KeyUsage = webidl.createEnumConverter("KeyUsage", [
|
||||||
|
"encrypt",
|
||||||
|
"decrypt",
|
||||||
|
"sign",
|
||||||
|
"verify",
|
||||||
|
"deriveKey",
|
||||||
|
"deriveBits",
|
||||||
|
"wrapKey",
|
||||||
|
"unwrapKey",
|
||||||
|
]);
|
||||||
|
|
||||||
|
webidl.converters["sequence<KeyUsage>"] = webidl.createSequenceConverter(
|
||||||
|
webidl.converters.KeyUsage,
|
||||||
|
);
|
||||||
|
|
||||||
|
webidl.converters.HashAlgorithmIdentifier =
|
||||||
|
webidl.converters.AlgorithmIdentifier;
|
||||||
|
|
||||||
|
/** @type {__bootstrap.webidl.Dictionary} */
|
||||||
|
const dictAlgorithm = [{
|
||||||
|
key: "name",
|
||||||
|
converter: webidl.converters.DOMString,
|
||||||
|
required: true,
|
||||||
|
}];
|
||||||
|
|
||||||
|
webidl.converters.Algorithm = webidl
|
||||||
|
.createDictionaryConverter("Algorithm", dictAlgorithm);
|
||||||
|
|
||||||
|
webidl.converters.BigInteger = webidl.converters.Uint8Array;
|
||||||
|
|
||||||
|
/** @type {__bootstrap.webidl.Dictionary} */
|
||||||
|
const dictRsaKeyGenParams = [
|
||||||
|
...dictAlgorithm,
|
||||||
|
{
|
||||||
|
key: "modulusLength",
|
||||||
|
converter: (V, opts) =>
|
||||||
|
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "publicExponent",
|
||||||
|
converter: webidl.converters.BigInteger,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters.RsaKeyGenParams = webidl
|
||||||
|
.createDictionaryConverter("RsaKeyGenParams", dictRsaKeyGenParams);
|
||||||
|
|
||||||
|
const dictRsaHashedKeyGenParams = [
|
||||||
|
...dictRsaKeyGenParams,
|
||||||
|
{
|
||||||
|
key: "hash",
|
||||||
|
converter: webidl.converters.HashAlgorithmIdentifier,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters.RsaHashedKeyGenParams = webidl.createDictionaryConverter(
|
||||||
|
"RsaHashedKeyGenParams",
|
||||||
|
dictRsaHashedKeyGenParams,
|
||||||
|
);
|
||||||
|
|
||||||
|
webidl.converters.NamedCurve = webidl.converters.DOMString;
|
||||||
|
|
||||||
|
const dictEcKeyGenParams = [
|
||||||
|
...dictAlgorithm,
|
||||||
|
{
|
||||||
|
key: "namedCurve",
|
||||||
|
converter: webidl.converters.NamedCurve,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters.EcKeyGenParams = webidl
|
||||||
|
.createDictionaryConverter("EcKeyGenParams", dictEcKeyGenParams);
|
||||||
|
|
||||||
|
const dictHmacKeyGenParams = [
|
||||||
|
...dictAlgorithm,
|
||||||
|
{
|
||||||
|
key: "hash",
|
||||||
|
converter: webidl.converters.HashAlgorithmIdentifier,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "length",
|
||||||
|
converter: (V, opts) =>
|
||||||
|
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters.HmacKeyGenParams = webidl
|
||||||
|
.createDictionaryConverter("HmacKeyGenParams", dictHmacKeyGenParams);
|
||||||
|
|
||||||
|
const dictRsaPssParams = [
|
||||||
|
...dictAlgorithm,
|
||||||
|
{
|
||||||
|
key: "saltLength",
|
||||||
|
converter: (V, opts) =>
|
||||||
|
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters.RsaPssParams = webidl
|
||||||
|
.createDictionaryConverter("RsaPssParams", dictRsaPssParams);
|
||||||
|
|
||||||
|
const dictEcdsaParams = [
|
||||||
|
...dictAlgorithm,
|
||||||
|
{
|
||||||
|
key: "hash",
|
||||||
|
converter: webidl.converters.HashAlgorithmIdentifier,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters["EcdsaParams"] = webidl
|
||||||
|
.createDictionaryConverter("EcdsaParams", dictEcdsaParams);
|
||||||
|
|
||||||
|
webidl.converters.CryptoKey = webidl.createInterfaceConverter(
|
||||||
|
"CryptoKey",
|
||||||
|
CryptoKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
const dictCryptoKeyPair = [
|
||||||
|
{
|
||||||
|
key: "publicKey",
|
||||||
|
converter: webidl.converters.CryptoKey,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "privateKey",
|
||||||
|
converter: webidl.converters.CryptoKey,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters.CryptoKeyPair = webidl
|
||||||
|
.createDictionaryConverter("CryptoKeyPair", dictCryptoKeyPair);
|
||||||
|
})(this);
|
|
@ -18,5 +18,11 @@ deno_core = { version = "0.92.0", path = "../../core" }
|
||||||
deno_web = { version = "0.41.1", path = "../web" }
|
deno_web = { version = "0.41.1", path = "../web" }
|
||||||
tokio = { version = "1.8.0", features = ["full"] }
|
tokio = { version = "1.8.0", features = ["full"] }
|
||||||
rand = "0.8.3"
|
rand = "0.8.3"
|
||||||
ring = "0.16.20"
|
ring = { version = "0.16.20", features = ["std"] }
|
||||||
|
rsa = "0.4.0" # TODO: remove "pem" feature when next release is on crates.io
|
||||||
|
sha-1 = "0.9.6"
|
||||||
|
sha2 = "0.9.5"
|
||||||
|
serde = { version = "1.0.123", features = ["derive"] }
|
||||||
uuid = { version = "0.8.2", features = ["v4"] }
|
uuid = { version = "0.8.2", features = ["v4"] }
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
num-traits = "0.2.14"
|
105
extensions/crypto/key.rs
Normal file
105
extensions/crypto/key.rs
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use ring::agreement::Algorithm as RingAlgorithm;
|
||||||
|
use ring::hmac::Algorithm as HmacAlgorithm;
|
||||||
|
use ring::signature::EcdsaSigningAlgorithm;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Copy, Clone)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum KeyType {
|
||||||
|
Public,
|
||||||
|
Private,
|
||||||
|
Secret,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Copy, Clone)]
|
||||||
|
pub enum CryptoHash {
|
||||||
|
#[serde(rename = "SHA-1")]
|
||||||
|
Sha1,
|
||||||
|
#[serde(rename = "SHA-256")]
|
||||||
|
Sha256,
|
||||||
|
#[serde(rename = "SHA-384")]
|
||||||
|
Sha384,
|
||||||
|
#[serde(rename = "SHA-512")]
|
||||||
|
Sha512,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Copy, Clone)]
|
||||||
|
pub enum CryptoNamedCurve {
|
||||||
|
#[serde(rename = "P-256")]
|
||||||
|
P256,
|
||||||
|
#[serde(rename = "P-384")]
|
||||||
|
P384,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CryptoNamedCurve> for &RingAlgorithm {
|
||||||
|
fn from(curve: CryptoNamedCurve) -> &'static RingAlgorithm {
|
||||||
|
match curve {
|
||||||
|
CryptoNamedCurve::P256 => &ring::agreement::ECDH_P256,
|
||||||
|
CryptoNamedCurve::P384 => &ring::agreement::ECDH_P384,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CryptoNamedCurve> for &EcdsaSigningAlgorithm {
|
||||||
|
fn from(curve: CryptoNamedCurve) -> &'static EcdsaSigningAlgorithm {
|
||||||
|
match curve {
|
||||||
|
CryptoNamedCurve::P256 => {
|
||||||
|
&ring::signature::ECDSA_P256_SHA256_FIXED_SIGNING
|
||||||
|
}
|
||||||
|
CryptoNamedCurve::P384 => {
|
||||||
|
&ring::signature::ECDSA_P384_SHA384_FIXED_SIGNING
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CryptoHash> for HmacAlgorithm {
|
||||||
|
fn from(hash: CryptoHash) -> HmacAlgorithm {
|
||||||
|
match hash {
|
||||||
|
CryptoHash::Sha1 => ring::hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY,
|
||||||
|
CryptoHash::Sha256 => ring::hmac::HMAC_SHA256,
|
||||||
|
CryptoHash::Sha384 => ring::hmac::HMAC_SHA384,
|
||||||
|
CryptoHash::Sha512 => ring::hmac::HMAC_SHA512,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum KeyUsage {
|
||||||
|
Encrypt,
|
||||||
|
Decrypt,
|
||||||
|
Sign,
|
||||||
|
Verify,
|
||||||
|
DeriveKey,
|
||||||
|
DeriveBits,
|
||||||
|
WrapKey,
|
||||||
|
UnwrapKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Copy)]
|
||||||
|
pub enum Algorithm {
|
||||||
|
#[serde(rename = "RSASSA-PKCS1-v1_5")]
|
||||||
|
RsassaPkcs1v15,
|
||||||
|
#[serde(rename = "RSA-PSS")]
|
||||||
|
RsaPss,
|
||||||
|
#[serde(rename = "RSA-OAEP")]
|
||||||
|
RsaOaep,
|
||||||
|
#[serde(rename = "ECDSA")]
|
||||||
|
Ecdsa,
|
||||||
|
#[serde(rename = "ECDH")]
|
||||||
|
Ecdh,
|
||||||
|
#[serde(rename = "AES-CTR")]
|
||||||
|
AesCtr,
|
||||||
|
#[serde(rename = "AES-CBC")]
|
||||||
|
AesCbc,
|
||||||
|
#[serde(rename = "AES-GCM")]
|
||||||
|
AesGcm,
|
||||||
|
#[serde(rename = "AES-KW")]
|
||||||
|
AesKw,
|
||||||
|
#[serde(rename = "HMAC")]
|
||||||
|
Hmac,
|
||||||
|
}
|
144
extensions/crypto/lib.deno_crypto.d.ts
vendored
144
extensions/crypto/lib.deno_crypto.d.ts
vendored
|
@ -5,6 +5,129 @@
|
||||||
|
|
||||||
declare var crypto: Crypto;
|
declare var crypto: Crypto;
|
||||||
|
|
||||||
|
interface Algorithm {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KeyAlgorithm {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type AlgorithmIdentifier = string | Algorithm;
|
||||||
|
type HashAlgorithmIdentifier = AlgorithmIdentifier;
|
||||||
|
type KeyType = "private" | "public" | "secret";
|
||||||
|
type KeyUsage =
|
||||||
|
| "decrypt"
|
||||||
|
| "deriveBits"
|
||||||
|
| "deriveKey"
|
||||||
|
| "encrypt"
|
||||||
|
| "sign"
|
||||||
|
| "unwrapKey"
|
||||||
|
| "verify"
|
||||||
|
| "wrapKey";
|
||||||
|
|
||||||
|
type NamedCurve = string;
|
||||||
|
|
||||||
|
interface HmacKeyGenParams extends Algorithm {
|
||||||
|
hash: HashAlgorithmIdentifier;
|
||||||
|
length?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EcKeyGenParams extends Algorithm {
|
||||||
|
namedCurve: NamedCurve;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EcdsaParams extends Algorithm {
|
||||||
|
hash: HashAlgorithmIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RsaHashedKeyGenParams extends RsaKeyGenParams {
|
||||||
|
hash: HashAlgorithmIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RsaKeyGenParams extends Algorithm {
|
||||||
|
modulusLength: number;
|
||||||
|
publicExponent: Uint8Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RsaPssParams extends Algorithm {
|
||||||
|
saltLength: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The CryptoKey dictionary of the Web Crypto API represents a cryptographic key. */
|
||||||
|
interface CryptoKey {
|
||||||
|
readonly algorithm: KeyAlgorithm;
|
||||||
|
readonly extractable: boolean;
|
||||||
|
readonly type: KeyType;
|
||||||
|
readonly usages: KeyUsage[];
|
||||||
|
}
|
||||||
|
|
||||||
|
declare var CryptoKey: {
|
||||||
|
prototype: CryptoKey;
|
||||||
|
new (): CryptoKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** The CryptoKeyPair dictionary of the Web Crypto API represents a key pair for an asymmetric cryptography algorithm, also known as a public-key algorithm. */
|
||||||
|
interface CryptoKeyPair {
|
||||||
|
privateKey: CryptoKey;
|
||||||
|
publicKey: CryptoKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare var CryptoKeyPair: {
|
||||||
|
prototype: CryptoKeyPair;
|
||||||
|
new (): CryptoKeyPair;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 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 {
|
||||||
|
generateKey(
|
||||||
|
algorithm: RsaHashedKeyGenParams | EcKeyGenParams,
|
||||||
|
extractable: boolean,
|
||||||
|
keyUsages: KeyUsage[],
|
||||||
|
): Promise<CryptoKeyPair>;
|
||||||
|
generateKey(
|
||||||
|
algorithm: HmacKeyGenParams,
|
||||||
|
extractable: boolean,
|
||||||
|
keyUsages: KeyUsage[],
|
||||||
|
): Promise<CryptoKey>;
|
||||||
|
generateKey(
|
||||||
|
algorithm: AlgorithmIdentifier,
|
||||||
|
extractable: boolean,
|
||||||
|
keyUsages: KeyUsage[],
|
||||||
|
): Promise<CryptoKeyPair | CryptoKey>;
|
||||||
|
sign(
|
||||||
|
algorithm: AlgorithmIdentifier | RsaPssParams | EcdsaParams,
|
||||||
|
key: CryptoKey,
|
||||||
|
data:
|
||||||
|
| Int8Array
|
||||||
|
| Int16Array
|
||||||
|
| Int32Array
|
||||||
|
| Uint8Array
|
||||||
|
| Uint16Array
|
||||||
|
| Uint32Array
|
||||||
|
| Uint8ClampedArray
|
||||||
|
| Float32Array
|
||||||
|
| Float64Array
|
||||||
|
| DataView
|
||||||
|
| ArrayBuffer,
|
||||||
|
): Promise<ArrayBuffer>;
|
||||||
|
digest(
|
||||||
|
algorithm: AlgorithmIdentifier,
|
||||||
|
data:
|
||||||
|
| Int8Array
|
||||||
|
| Int16Array
|
||||||
|
| Int32Array
|
||||||
|
| Uint8Array
|
||||||
|
| Uint16Array
|
||||||
|
| Uint32Array
|
||||||
|
| Uint8ClampedArray
|
||||||
|
| Float32Array
|
||||||
|
| Float64Array
|
||||||
|
| DataView
|
||||||
|
| ArrayBuffer,
|
||||||
|
): Promise<ArrayBuffer>;
|
||||||
|
}
|
||||||
|
|
||||||
declare interface Crypto {
|
declare interface Crypto {
|
||||||
readonly subtle: SubtleCrypto;
|
readonly subtle: SubtleCrypto;
|
||||||
getRandomValues<
|
getRandomValues<
|
||||||
|
@ -30,28 +153,7 @@ interface Algorithm {
|
||||||
name: string;
|
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: {
|
declare var SubtleCrypto: {
|
||||||
prototype: SubtleCrypto;
|
prototype: SubtleCrypto;
|
||||||
new (): SubtleCrypto;
|
new (): SubtleCrypto;
|
||||||
};
|
};
|
||||||
|
|
||||||
type AlgorithmIdentifier = string | Algorithm;
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use deno_core::error::custom_error;
|
||||||
|
use deno_core::error::not_supported;
|
||||||
use deno_core::error::null_opbuf;
|
use deno_core::error::null_opbuf;
|
||||||
|
use deno_core::error::type_error;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::include_js_files;
|
use deno_core::include_js_files;
|
||||||
use deno_core::op_async;
|
use deno_core::op_async;
|
||||||
|
@ -8,29 +11,65 @@ use deno_core::op_sync;
|
||||||
use deno_core::Extension;
|
use deno_core::Extension;
|
||||||
use deno_core::OpState;
|
use deno_core::OpState;
|
||||||
use deno_core::ZeroCopyBuf;
|
use deno_core::ZeroCopyBuf;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use num_traits::cast::FromPrimitive;
|
||||||
|
use rand::rngs::OsRng;
|
||||||
use rand::rngs::StdRng;
|
use rand::rngs::StdRng;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rand::SeedableRng;
|
use rand::SeedableRng;
|
||||||
use ring::digest;
|
use ring::digest;
|
||||||
use std::cell::RefCell;
|
use ring::hmac::Algorithm as HmacAlgorithm;
|
||||||
|
use ring::hmac::Key as HmacKey;
|
||||||
|
use ring::rand as RingRand;
|
||||||
|
use ring::rand::SecureRandom;
|
||||||
|
use ring::signature::EcdsaKeyPair;
|
||||||
|
use ring::signature::EcdsaSigningAlgorithm;
|
||||||
|
use rsa::padding::PaddingScheme;
|
||||||
|
use rsa::BigUint;
|
||||||
|
use rsa::PrivateKeyEncoding;
|
||||||
|
use rsa::RSAPrivateKey;
|
||||||
|
use sha1::Sha1;
|
||||||
|
use sha2::Digest;
|
||||||
|
use sha2::Sha256;
|
||||||
|
use sha2::Sha384;
|
||||||
|
use sha2::Sha512;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
pub use rand; // Re-export rand
|
pub use rand; // Re-export rand
|
||||||
|
|
||||||
|
mod key;
|
||||||
|
|
||||||
|
use crate::key::Algorithm;
|
||||||
|
use crate::key::CryptoHash;
|
||||||
|
use crate::key::CryptoNamedCurve;
|
||||||
|
|
||||||
|
// Allowlist for RSA public exponents.
|
||||||
|
lazy_static! {
|
||||||
|
static ref PUB_EXPONENT_1: BigUint = BigUint::from_u64(3).unwrap();
|
||||||
|
static ref PUB_EXPONENT_2: BigUint = BigUint::from_u64(65537).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init(maybe_seed: Option<u64>) -> Extension {
|
pub fn init(maybe_seed: Option<u64>) -> Extension {
|
||||||
Extension::builder()
|
Extension::builder()
|
||||||
.js(include_js_files!(
|
.js(include_js_files!(
|
||||||
prefix "deno:extensions/crypto",
|
prefix "deno:extensions/crypto",
|
||||||
"00_webidl.js",
|
"00_crypto.js",
|
||||||
"01_crypto.js",
|
"01_webidl.js",
|
||||||
))
|
))
|
||||||
.ops(vec![
|
.ops(vec![
|
||||||
(
|
(
|
||||||
"op_crypto_get_random_values",
|
"op_crypto_get_random_values",
|
||||||
op_sync(op_crypto_get_random_values),
|
op_sync(op_crypto_get_random_values),
|
||||||
),
|
),
|
||||||
|
("op_crypto_generate_key", op_async(op_crypto_generate_key)),
|
||||||
|
("op_crypto_sign_key", op_async(op_crypto_sign_key)),
|
||||||
("op_crypto_subtle_digest", op_async(op_crypto_subtle_digest)),
|
("op_crypto_subtle_digest", op_async(op_crypto_subtle_digest)),
|
||||||
("op_crypto_random_uuid", op_sync(op_crypto_random_uuid)),
|
("op_crypto_random_uuid", op_sync(op_crypto_random_uuid)),
|
||||||
])
|
])
|
||||||
|
@ -66,6 +105,251 @@ pub fn op_crypto_get_random_values(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct AlgorithmArg {
|
||||||
|
name: Algorithm,
|
||||||
|
modulus_length: Option<u32>,
|
||||||
|
public_exponent: Option<ZeroCopyBuf>,
|
||||||
|
named_curve: Option<CryptoNamedCurve>,
|
||||||
|
hash: Option<CryptoHash>,
|
||||||
|
length: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn op_crypto_generate_key(
|
||||||
|
_state: Rc<RefCell<OpState>>,
|
||||||
|
args: AlgorithmArg,
|
||||||
|
_: (),
|
||||||
|
) -> Result<ZeroCopyBuf, AnyError> {
|
||||||
|
let algorithm = args.name;
|
||||||
|
|
||||||
|
let key = match algorithm {
|
||||||
|
Algorithm::RsassaPkcs1v15 | Algorithm::RsaPss => {
|
||||||
|
let public_exponent = args.public_exponent.ok_or_else(not_supported)?;
|
||||||
|
let modulus_length = args.modulus_length.ok_or_else(not_supported)?;
|
||||||
|
|
||||||
|
let exponent = BigUint::from_bytes_be(&public_exponent);
|
||||||
|
if exponent != *PUB_EXPONENT_1 && exponent != *PUB_EXPONENT_2 {
|
||||||
|
return Err(custom_error(
|
||||||
|
"DOMExceptionOperationError",
|
||||||
|
"Bad public exponent",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut rng = OsRng;
|
||||||
|
|
||||||
|
let private_key: RSAPrivateKey = tokio::task::spawn_blocking(
|
||||||
|
move || -> Result<RSAPrivateKey, rsa::errors::Error> {
|
||||||
|
RSAPrivateKey::new_with_exp(
|
||||||
|
&mut rng,
|
||||||
|
modulus_length as usize,
|
||||||
|
&exponent,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.map_err(|e| custom_error("DOMExceptionOperationError", e.to_string()))?;
|
||||||
|
|
||||||
|
private_key.to_pkcs8()?
|
||||||
|
}
|
||||||
|
Algorithm::Ecdsa => {
|
||||||
|
let curve: &EcdsaSigningAlgorithm =
|
||||||
|
args.named_curve.ok_or_else(not_supported)?.into();
|
||||||
|
let rng = RingRand::SystemRandom::new();
|
||||||
|
let private_key: Vec<u8> = tokio::task::spawn_blocking(
|
||||||
|
move || -> Result<Vec<u8>, ring::error::Unspecified> {
|
||||||
|
let pkcs8 = EcdsaKeyPair::generate_pkcs8(curve, &rng)?;
|
||||||
|
Ok(pkcs8.as_ref().to_vec())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.map_err(|_| {
|
||||||
|
custom_error("DOMExceptionOperationError", "Key generation failed")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
private_key
|
||||||
|
}
|
||||||
|
Algorithm::Hmac => {
|
||||||
|
let hash: HmacAlgorithm = args.hash.ok_or_else(not_supported)?.into();
|
||||||
|
|
||||||
|
let length = if let Some(length) = args.length {
|
||||||
|
if (length % 8) != 0 {
|
||||||
|
return Err(custom_error(
|
||||||
|
"DOMExceptionOperationError",
|
||||||
|
"hmac block length must be byte aligned",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let length = length / 8;
|
||||||
|
if length > ring::digest::MAX_BLOCK_LEN {
|
||||||
|
return Err(custom_error(
|
||||||
|
"DOMExceptionOperationError",
|
||||||
|
"hmac block length is too large",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
length
|
||||||
|
} else {
|
||||||
|
hash.digest_algorithm().block_len
|
||||||
|
};
|
||||||
|
|
||||||
|
let rng = RingRand::SystemRandom::new();
|
||||||
|
let mut key_bytes = [0; ring::digest::MAX_BLOCK_LEN];
|
||||||
|
let key_bytes = &mut key_bytes[..length];
|
||||||
|
rng.fill(key_bytes).map_err(|_| {
|
||||||
|
custom_error("DOMExceptionOperationError", "Key generation failed")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
key_bytes.to_vec()
|
||||||
|
}
|
||||||
|
_ => return Err(not_supported()),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(key.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum KeyFormat {
|
||||||
|
Raw,
|
||||||
|
Pkcs8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub struct KeyData {
|
||||||
|
// TODO(littledivy): Kept here to be used to importKey() in future.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
r#type: KeyFormat,
|
||||||
|
data: ZeroCopyBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct SignArg {
|
||||||
|
key: KeyData,
|
||||||
|
algorithm: Algorithm,
|
||||||
|
salt_length: Option<u32>,
|
||||||
|
hash: Option<CryptoHash>,
|
||||||
|
named_curve: Option<CryptoNamedCurve>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn op_crypto_sign_key(
|
||||||
|
_state: Rc<RefCell<OpState>>,
|
||||||
|
args: SignArg,
|
||||||
|
zero_copy: Option<ZeroCopyBuf>,
|
||||||
|
) -> Result<ZeroCopyBuf, AnyError> {
|
||||||
|
let zero_copy = zero_copy.ok_or_else(null_opbuf)?;
|
||||||
|
let data = &*zero_copy;
|
||||||
|
let algorithm = args.algorithm;
|
||||||
|
|
||||||
|
let signature = match algorithm {
|
||||||
|
Algorithm::RsassaPkcs1v15 => {
|
||||||
|
let private_key = RSAPrivateKey::from_pkcs8(&*args.key.data)?;
|
||||||
|
let padding = match args
|
||||||
|
.hash
|
||||||
|
.ok_or_else(|| type_error("Missing argument hash".to_string()))?
|
||||||
|
{
|
||||||
|
CryptoHash::Sha1 => PaddingScheme::PKCS1v15Sign {
|
||||||
|
hash: Some(rsa::hash::Hash::SHA1),
|
||||||
|
},
|
||||||
|
CryptoHash::Sha256 => PaddingScheme::PKCS1v15Sign {
|
||||||
|
hash: Some(rsa::hash::Hash::SHA2_256),
|
||||||
|
},
|
||||||
|
CryptoHash::Sha384 => PaddingScheme::PKCS1v15Sign {
|
||||||
|
hash: Some(rsa::hash::Hash::SHA2_384),
|
||||||
|
},
|
||||||
|
CryptoHash::Sha512 => PaddingScheme::PKCS1v15Sign {
|
||||||
|
hash: Some(rsa::hash::Hash::SHA2_512),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
private_key.sign(padding, &data)?
|
||||||
|
}
|
||||||
|
Algorithm::RsaPss => {
|
||||||
|
let private_key = RSAPrivateKey::from_pkcs8(&*args.key.data)?;
|
||||||
|
|
||||||
|
let salt_len = args
|
||||||
|
.salt_length
|
||||||
|
.ok_or_else(|| type_error("Missing argument saltLength".to_string()))?
|
||||||
|
as usize;
|
||||||
|
|
||||||
|
let rng = OsRng;
|
||||||
|
let (padding, digest_in) = match args
|
||||||
|
.hash
|
||||||
|
.ok_or_else(|| type_error("Missing argument hash".to_string()))?
|
||||||
|
{
|
||||||
|
CryptoHash::Sha1 => {
|
||||||
|
let mut hasher = Sha1::new();
|
||||||
|
hasher.update(&data);
|
||||||
|
(
|
||||||
|
PaddingScheme::new_pss_with_salt::<Sha1, _>(rng, salt_len),
|
||||||
|
hasher.finalize()[..].to_vec(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
CryptoHash::Sha256 => {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(&data);
|
||||||
|
(
|
||||||
|
PaddingScheme::new_pss_with_salt::<Sha256, _>(rng, salt_len),
|
||||||
|
hasher.finalize()[..].to_vec(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
CryptoHash::Sha384 => {
|
||||||
|
let mut hasher = Sha384::new();
|
||||||
|
hasher.update(&data);
|
||||||
|
(
|
||||||
|
PaddingScheme::new_pss_with_salt::<Sha384, _>(rng, salt_len),
|
||||||
|
hasher.finalize()[..].to_vec(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
CryptoHash::Sha512 => {
|
||||||
|
let mut hasher = Sha512::new();
|
||||||
|
hasher.update(&data);
|
||||||
|
(
|
||||||
|
PaddingScheme::new_pss_with_salt::<Sha512, _>(rng, salt_len),
|
||||||
|
hasher.finalize()[..].to_vec(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sign data based on computed padding and return buffer
|
||||||
|
private_key.sign(padding, &digest_in)?
|
||||||
|
}
|
||||||
|
Algorithm::Ecdsa => {
|
||||||
|
let curve: &EcdsaSigningAlgorithm =
|
||||||
|
args.named_curve.ok_or_else(not_supported)?.try_into()?;
|
||||||
|
|
||||||
|
let key_pair = EcdsaKeyPair::from_pkcs8(curve, &*args.key.data)?;
|
||||||
|
// We only support P256-SHA256 & P384-SHA384. These are recommended signature pairs.
|
||||||
|
// https://briansmith.org/rustdoc/ring/signature/index.html#statics
|
||||||
|
if let Some(hash) = args.hash {
|
||||||
|
match hash {
|
||||||
|
CryptoHash::Sha256 | CryptoHash::Sha384 => (),
|
||||||
|
_ => return Err(type_error("Unsupported algorithm")),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let rng = RingRand::SystemRandom::new();
|
||||||
|
let signature = key_pair.sign(&rng, &data)?;
|
||||||
|
|
||||||
|
// Signature data as buffer.
|
||||||
|
signature.as_ref().to_vec()
|
||||||
|
}
|
||||||
|
Algorithm::Hmac => {
|
||||||
|
let hash: HmacAlgorithm = args.hash.ok_or_else(not_supported)?.into();
|
||||||
|
|
||||||
|
let key = HmacKey::new(hash, &*args.key.data);
|
||||||
|
|
||||||
|
let signature = ring::hmac::sign(&key, &data);
|
||||||
|
signature.as_ref().to_vec()
|
||||||
|
}
|
||||||
|
_ => return Err(type_error("Unsupported algorithm".to_string())),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(signature.into())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn op_crypto_random_uuid(
|
pub fn op_crypto_random_uuid(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
_: (),
|
_: (),
|
||||||
|
|
|
@ -316,6 +316,7 @@ delete Object.prototype.__proto__;
|
||||||
CountQueuingStrategy: util.nonEnumerable(
|
CountQueuingStrategy: util.nonEnumerable(
|
||||||
streams.CountQueuingStrategy,
|
streams.CountQueuingStrategy,
|
||||||
),
|
),
|
||||||
|
CryptoKey: util.nonEnumerable(crypto.CryptoKey),
|
||||||
CustomEvent: util.nonEnumerable(CustomEvent),
|
CustomEvent: util.nonEnumerable(CustomEvent),
|
||||||
DOMException: util.nonEnumerable(domException.DOMException),
|
DOMException: util.nonEnumerable(domException.DOMException),
|
||||||
ErrorEvent: util.nonEnumerable(ErrorEvent),
|
ErrorEvent: util.nonEnumerable(ErrorEvent),
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue