diff --git a/Cargo.lock b/Cargo.lock index 4db44e8684..a7bfecc65b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,7 +25,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c192eb8f11fc081b0fe4259ba5af04217d4e0faddd02417310a927911abd7c8" dependencies = [ "crypto-common", - "generic-array", + "generic-array 0.14.6", ] [[package]] @@ -68,7 +68,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom", + "getrandom 0.2.7", "once_cell", "version_check", ] @@ -304,7 +304,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array", + "generic-array 0.14.6", ] [[package]] @@ -313,7 +313,7 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ - "generic-array", + "generic-array 0.14.6", ] [[package]] @@ -328,7 +328,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a90ec2df9600c28a01c56c4784c9207a96d2451833aeceb8cc97e4c9548bb78" dependencies = [ - "generic-array", + "generic-array 0.14.6", ] [[package]] @@ -624,8 +624,8 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f2b443d17d49dad5ef0ede301c3179cc923b8822f3393b4d2c28c269dd4a122" dependencies = [ - "generic-array", - "rand_core", + "generic-array 0.14.6", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -636,8 +636,8 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array", - "rand_core", + "generic-array 0.14.6", + "rand_core 0.6.4", "typenum", ] @@ -688,6 +688,32 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" +[[package]] +name = "curve25519-dalek" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" +dependencies = [ + "byteorder", + "digest 0.8.1", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + [[package]] name = "d3d12" version = "0.5.0" @@ -955,6 +981,7 @@ dependencies = [ "cbc", "const-oid", "ctr", + "curve25519-dalek 2.1.3", "deno_core", "deno_web", "elliptic-curve", @@ -973,6 +1000,7 @@ dependencies = [ "spki", "tokio", "uuid", + "x25519-dalek", ] [[package]] @@ -1335,13 +1363,22 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array", + "generic-array 0.14.6", ] [[package]] @@ -1519,12 +1556,12 @@ dependencies = [ "der", "digest 0.10.5", "ff", - "generic-array", + "generic-array 0.14.6", "group", "hkdf", "pem-rfc7468", "pkcs8", - "rand_core", + "rand_core 0.6.4", "sec1", "subtle", "zeroize", @@ -1697,7 +1734,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df689201f395c6b90dfe87127685f8dbfc083a5e779e613575d8bd7314300c3e" dependencies = [ - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -1931,6 +1968,15 @@ dependencies = [ "byteorder", ] +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + [[package]] name = "generic-array" version = "0.14.6" @@ -1941,6 +1987,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.7" @@ -1949,7 +2006,7 @@ checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -2043,7 +2100,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7391856def869c1c81063a03457c676fbcd419709c3dfb33d8d319de484b154d" dependencies = [ "ff", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -2333,7 +2390,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ "block-padding", - "generic-array", + "generic-array 0.14.6", ] [[package]] @@ -2774,7 +2831,7 @@ checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" dependencies = [ "libc", "log 0.4.17", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys", ] @@ -3428,7 +3485,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -3438,7 +3495,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", ] [[package]] @@ -3447,7 +3513,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.7", ] [[package]] @@ -3627,7 +3693,7 @@ dependencies = [ "num-traits", "pkcs1", "pkcs8", - "rand_core", + "rand_core 0.6.4", "smallvec", "subtle", "zeroize", @@ -3821,7 +3887,7 @@ checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ "base16ct", "der", - "generic-array", + "generic-array 0.14.6", "pkcs8", "subtle", "zeroize", @@ -4006,7 +4072,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "deb766570a2825fa972bceff0d195727876a9cdf2460ab2e52d455dc2de47fd9" dependencies = [ "digest 0.10.5", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -4617,6 +4683,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2 1.0.43", + "quote 1.0.21", + "syn 1.0.99", + "unicode-xid 0.2.4", +] + [[package]] name = "sys-info" version = "0.9.1" @@ -5308,7 +5386,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" dependencies = [ - "getrandom", + "getrandom 0.2.7", "serde", ] @@ -5364,6 +5442,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -5671,6 +5755,17 @@ dependencies = [ "toml", ] +[[package]] +name = "x25519-dalek" +version = "2.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5da623d8af10a62342bcbbb230e33e58a63255a58012f8653c578e54bab48df" +dependencies = [ + "curve25519-dalek 3.2.0", + "rand_core 0.6.4", + "zeroize", +] + [[package]] name = "xattr" version = "0.2.3" @@ -5717,6 +5812,21 @@ name = "zeroize" version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" +dependencies = [ + "proc-macro2 1.0.43", + "quote 1.0.21", + "syn 1.0.99", + "synstructure", +] [[package]] name = "zstd" diff --git a/cli/bench/secure_curves.js b/cli/bench/secure_curves.js new file mode 100644 index 0000000000..dfb40c2617 --- /dev/null +++ b/cli/bench/secure_curves.js @@ -0,0 +1,22 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +let [total, count] = typeof Deno !== "undefined" + ? Deno.args + : [process.argv[2], process.argv[3]]; + +total = total ? parseInt(total, 0) : 50; +count = count ? parseInt(count, 10) : 10000; + +async function bench(fun) { + const start = Date.now(); + for (let i = 0; i < count; i++) await fun(); + const elapsed = Date.now() - start; + const rate = Math.floor(count / (elapsed / 1000)); + console.log(`time ${elapsed} ms rate ${rate}`); + if (--total) await bench(fun); +} + +const { subtle } = typeof crypto !== "undefined" + ? crypto + : require("crypto").webcrypto; + +bench(() => subtle.generateKey("X25519", true, ["deriveKey"])); diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index d00f29e7c2..41c85bdd12 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -98,18 +98,22 @@ "AES-GCM": "AesKeyGenParams", "AES-KW": "AesKeyGenParams", "HMAC": "HmacKeyGenParams", + "X25519": null, + "Ed25519": null, }, "sign": { "RSASSA-PKCS1-v1_5": null, "RSA-PSS": "RsaPssParams", "ECDSA": "EcdsaParams", "HMAC": null, + "Ed25519": null, }, "verify": { "RSASSA-PKCS1-v1_5": null, "RSA-PSS": "RsaPssParams", "ECDSA": "EcdsaParams", "HMAC": null, + "Ed25519": null, }, "importKey": { "RSASSA-PKCS1-v1_5": "RsaHashedImportParams", @@ -124,11 +128,13 @@ "AES-CBC": null, "AES-GCM": null, "AES-KW": null, + "X25519": null, }, "deriveBits": { "HKDF": "HkdfParams", "PBKDF2": "Pbkdf2Params", "ECDH": "EcdhKeyDeriveParams", + "X25519": "EcdhKeyDeriveParams", }, "encrypt": { "RSA-OAEP": "RsaOaepParams", @@ -834,6 +840,26 @@ return signature.buffer; } + case "Ed25519": { + // 1. + if (key[_type] !== "private") { + throw new DOMException( + "Key type not supported", + "InvalidAccessError", + ); + } + + // https://briansmith.org/rustdoc/src/ring/ec/curve25519/ed25519/signing.rs.html#260 + const SIGNATURE_LEN = 32 * 2; // ELEM_LEN + SCALAR_LEN + const signature = new Uint8Array(SIGNATURE_LEN); + if (!ops.op_sign_ed25519(keyData, data, signature)) { + throw new DOMException( + "Failed to sign", + "OperationError", + ); + } + return signature.buffer; + } } throw new TypeError("unreachable"); @@ -955,6 +981,22 @@ ["wrapKey", "unwrapKey"], ); } + case "X25519": { + return importKeyX25519( + format, + keyData, + extractable, + keyUsages, + ); + } + case "Ed25519": { + return importKeyEd25519( + format, + keyData, + extractable, + keyUsages, + ); + } default: throw new DOMException("Not implemented", "NotSupportedError"); } @@ -1003,6 +1045,10 @@ result = exportKeyEC(format, key, innerKey); break; } + case "Ed25519": { + result = exportKeyEd25519(format, key, innerKey); + break; + } case "AES-CTR": case "AES-CBC": case "AES-GCM": @@ -1283,6 +1329,17 @@ namedCurve: key[_algorithm].namedCurve, }, data); } + case "Ed25519": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key type not supported", + "InvalidAccessError", + ); + } + + return ops.op_verify_ed25519(keyData, data, signature); + } } throw new TypeError("unreachable"); @@ -1611,7 +1668,6 @@ const usages = keyUsages; const normalizedAlgorithm = normalizeAlgorithm(algorithm, "generateKey"); - const result = await generateKey( normalizedAlgorithm, extractable, @@ -1910,6 +1966,95 @@ return generateKeyAES(normalizedAlgorithm, extractable, usages); } + case "X25519": { + if ( + ArrayPrototypeFind( + usages, + (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + const privateKeyData = new Uint8Array(32); + const publicKeyData = new Uint8Array(32); + ops.op_generate_x25519_keypair(privateKeyData, publicKeyData); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); + + const publicHandle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); + + const algorithm = { + name: algorithmName, + }; + + const publicKey = constructKey( + "public", + true, + usageIntersection(usages, []), + algorithm, + publicHandle, + ); + + const privateKey = constructKey( + "private", + extractable, + usageIntersection(usages, ["deriveKey", "deriveBits"]), + algorithm, + handle, + ); + + return { publicKey, privateKey }; + } + case "Ed25519": { + if ( + ArrayPrototypeFind( + usages, + (u) => !ArrayPrototypeIncludes(["sign", "verify"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + const ED25519_SEED_LEN = 32; + const ED25519_PUBLIC_KEY_LEN = 32; + const privateKeyData = new Uint8Array(ED25519_SEED_LEN); + const publicKeyData = new Uint8Array(ED25519_PUBLIC_KEY_LEN); + if ( + !ops.op_generate_ed25519_keypair(privateKeyData, publicKeyData) + ) { + throw new DOMException("Failed to generate key", "OperationError"); + } + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); + + const publicHandle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); + + const algorithm = { + name: algorithmName, + }; + + const publicKey = constructKey( + "public", + true, + usageIntersection(usages, ["verify"]), + algorithm, + publicHandle, + ); + + const privateKey = constructKey( + "private", + extractable, + usageIntersection(usages, ["sign"]), + algorithm, + handle, + ); + + return { publicKey, privateKey }; + } case "HMAC": { // 1. if ( @@ -1967,6 +2112,439 @@ } } + function importKeyEd25519( + format, + keyData, + extractable, + keyUsages, + ) { + switch (format) { + case "raw": { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["verify"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, keyData); + + // 2-3. + const algorithm = { + name: "Ed25519", + }; + + // 4-6. + return constructKey( + "public", + extractable, + [], + algorithm, + handle, + ); + } + case "spki": { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["verify"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + const publicKeyData = new Uint8Array(32); + if (!ops.op_import_spki_ed25519(keyData, publicKeyData)) { + throw new DOMException("Invalid key data", "DataError"); + } + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); + + const algorithm = { + name: "Ed25519", + }; + + return constructKey( + "public", + extractable, + [], + algorithm, + handle, + ); + } + case "pkcs8": { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["sign"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + const privateKeyData = new Uint8Array(32); + if (!ops.op_import_pkcs8_ed25519(keyData, privateKeyData)) { + throw new DOMException("Invalid key data", "DataError"); + } + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); + + const algorithm = { + name: "Ed25519", + }; + + return constructKey( + "private", + extractable, + [], + algorithm, + handle, + ); + } + case "jwk": { + // 1. + const jwk = keyData; + + // 2. + if (jwk.d !== undefined) { + if ( + ArrayPrototypeFind( + keyUsages, + (u) => + !ArrayPrototypeIncludes( + ["sign"], + u, + ), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + } else { + if ( + ArrayPrototypeFind( + keyUsages, + (u) => + !ArrayPrototypeIncludes( + ["verify"], + u, + ), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + } + + // 3. + if (jwk.kty !== "OKP") { + throw new DOMException("Invalid key type", "DataError"); + } + + // 4. + if (jwk.crv !== "Ed25519") { + throw new DOMException("Invalid curve", "DataError"); + } + + // 5. + if (jwk.alg !== undefined && jwk.alg !== "EdDSA") { + throw new DOMException("Invalid algorithm", "DataError"); + } + + // 6. + if ( + keyUsages.length > 0 && jwk.use !== undefined && jwk.use !== "sig" + ) { + throw new DOMException("Invalid key usage", "DataError"); + } + + // 7. + if (jwk.key_ops !== undefined) { + if ( + ArrayPrototypeFind( + jwk.key_ops, + (u) => !ArrayPrototypeIncludes(recognisedUsages, u), + ) !== undefined + ) { + throw new DOMException( + "'key_ops' property of JsonWebKey is invalid", + "DataError", + ); + } + + if ( + !ArrayPrototypeEvery( + jwk.key_ops, + (u) => ArrayPrototypeIncludes(keyUsages, u), + ) + ) { + throw new DOMException( + "'key_ops' property of JsonWebKey is invalid", + "DataError", + ); + } + } + + // 8. + if (jwk.ext !== undefined && jwk.ext === false && extractable) { + throw new DOMException("Invalid key extractability", "DataError"); + } + + // 9. + if (jwk.d !== undefined) { + // https://www.rfc-editor.org/rfc/rfc8037#section-2 + const privateKeyData = ops.op_crypto_base64url(jwk.d); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); + + const algorithm = { + name: "Ed25519", + }; + + return constructKey( + "private", + extractable, + [], + algorithm, + handle, + ); + } else { + // https://www.rfc-editor.org/rfc/rfc8037#section-2 + const publicKeyData = ops.op_crypto_base64url(jwk.d); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); + + const algorithm = { + name: "Ed25519", + }; + + return constructKey( + "public", + extractable, + [], + algorithm, + handle, + ); + } + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } + } + + function importKeyX25519( + format, + keyData, + extractable, + keyUsages, + ) { + switch (format) { + case "raw": { + // 1. + if (keyUsages.length > 0) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, keyData); + + // 2-3. + const algorithm = { + name: "X25519", + }; + + // 4-6. + return constructKey( + "public", + extractable, + [], + algorithm, + handle, + ); + } + case "spki": { + // 1. + if (keyUsages.length > 0) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + const publicKeyData = new Uint8Array(32); + if (!ops.op_import_spki_x25519(keyData, publicKeyData)) { + throw new DOMException("Invalid key data", "DataError"); + } + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); + + const algorithm = { + name: "X25519", + }; + + return constructKey( + "public", + extractable, + [], + algorithm, + handle, + ); + } + case "pkcs8": { + // 1. + if ( + ArrayPrototypeFind( + keyUsages, + (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + const privateKeyData = new Uint8Array(32); + if (!ops.op_import_pkcs8_x25519(keyData, privateKeyData)) { + throw new DOMException("Invalid key data", "DataError"); + } + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); + + const algorithm = { + name: "X25519", + }; + + return constructKey( + "private", + extractable, + [], + algorithm, + handle, + ); + } + case "jwk": { + // 1. + const jwk = keyData; + + // 2. + if (jwk.d !== undefined) { + if ( + ArrayPrototypeFind( + keyUsages, + (u) => + !ArrayPrototypeIncludes( + SUPPORTED_KEY_USAGES["X25519"].private, + u, + ), + ) !== undefined + ) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + } + + // 3. + if (jwk.d === undefined && keyUsages.length > 0) { + throw new DOMException("Invalid key usages", "SyntaxError"); + } + + // 4. + if (jwk.kty !== "OKP") { + throw new DOMException("Invalid key type", "DataError"); + } + + // 5. + if (jwk.crv !== "X25519") { + throw new DOMException("Invalid curve", "DataError"); + } + + // 6. + if (keyUsages.length > 0 && jwk.use !== undefined) { + if (jwk.use !== "enc") { + throw new DOMException("Invalid key use", "DataError"); + } + } + + // 7. + if (jwk.key_ops !== undefined) { + if ( + ArrayPrototypeFind( + jwk.key_ops, + (u) => !ArrayPrototypeIncludes(recognisedUsages, u), + ) !== undefined + ) { + throw new DOMException( + "'key_ops' property of JsonWebKey is invalid", + "DataError", + ); + } + + if ( + !ArrayPrototypeEvery( + jwk.key_ops, + (u) => ArrayPrototypeIncludes(keyUsages, u), + ) + ) { + throw new DOMException( + "'key_ops' property of JsonWebKey is invalid", + "DataError", + ); + } + } + + // 8. + if (jwk.ext !== undefined && jwk.ext === false && extractable) { + throw new DOMException("Invalid key extractability", "DataError"); + } + + // 9. + if (jwk.d !== undefined) { + // https://www.rfc-editor.org/rfc/rfc8037#section-2 + const privateKeyData = ops.op_crypto_base64url(jwk.d); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData); + + const algorithm = { + name: "X25519", + }; + + return constructKey( + "private", + extractable, + [], + algorithm, + handle, + ); + } else { + // https://www.rfc-editor.org/rfc/rfc8037#section-2 + const publicKeyData = ops.op_crypto_base64url(jwk.d); + + const handle = {}; + WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData); + + const algorithm = { + name: "X25519", + }; + + return constructKey( + "public", + extractable, + [], + algorithm, + handle, + ); + } + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } + } + function exportKeyAES( format, key, @@ -2732,6 +3310,9 @@ private: ["deriveKey", "deriveBits"], jwkUse: "enc", }, + "X25519": { + private: ["deriveKey", "deriveBits"], + }, }; function importKeyRSA( @@ -3430,6 +4011,66 @@ } } + function exportKeyEd25519(format, key, innerKey) { + switch (format) { + case "raw": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); + } + + // 2-3. + return innerKey.buffer; + } + case "spki": { + // 1. + if (key[_type] !== "public") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); + } + + const spkiDer = ops.op_export_spki_ed25519(innerKey); + return spkiDer.buffer; + } + case "pkcs8": { + // 1. + if (key[_type] !== "private") { + throw new DOMException( + "Key is not a public key", + "InvalidAccessError", + ); + } + + const pkcs8Der = ops.op_export_pkcs8_ed25519(innerKey); + return pkcs8Der.buffer; + } + case "jwk": { + const x = key[_type] === "private" + ? ops.op_jwk_x_ed25519(innerKey) + : ops.op_crypto_base64url(innerKey); + const jwk = { + kty: "OKP", + alg: "EdDSA", + crv: "Ed25519", + x, + "key_ops": key.usages, + ext: key[_extractable], + }; + if (key[_type] === "private") { + jwk.d = ops.op_crypto_base64url(innerKey); + } + return jwk; + } + default: + throw new DOMException("Not implemented", "NotSupportedError"); + } + } + function exportKeyEC(format, key, innerKey) { switch (format) { case "raw": { @@ -3713,6 +4354,49 @@ return buf.buffer; } + case "X25519": { + // 1. + if (baseKey[_type] !== "private") { + throw new DOMException("Invalid key type", "InvalidAccessError"); + } + // 2. + const publicKey = normalizedAlgorithm.public; + // 3. + if (publicKey[_type] !== "public") { + throw new DOMException("Invalid key type", "InvalidAccessError"); + } + // 4. + if (publicKey[_algorithm].name !== baseKey[_algorithm].name) { + throw new DOMException( + "Algorithm mismatch", + "InvalidAccessError", + ); + } + + // 5. + const kHandle = baseKey[_handle]; + const k = WeakMapPrototypeGet(KEY_STORE, kHandle); + + const uHandle = publicKey[_handle]; + const u = WeakMapPrototypeGet(KEY_STORE, uHandle); + + const secret = new Uint8Array(32); + const isIdentity = ops.op_derive_bits_x25519(k, u, secret); + + // 6. + if (isIdentity) { + throw new DOMException("Invalid key", "OperationError"); + } + + // 7. + if (length === null) { + return secret.buffer; + } else if (secret.length * 8 < length) { + throw new DOMException("Invalid length", "OperationError"); + } else { + return secret.subarray(0, length / 8).buffer; + } + } default: throw new DOMException("Not implemented", "NotSupportedError"); } diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml index 5828a3e9ab..c03a2676e2 100644 --- a/ext/crypto/Cargo.toml +++ b/ext/crypto/Cargo.toml @@ -22,6 +22,8 @@ block-modes = "0.9.1" cbc = { version = "0.1.2", features = ["alloc"] } const-oid = "0.9.0" ctr = "0.9.1" +# https://github.com/dalek-cryptography/curve25519-dalek/pull/397 +curve25519-dalek = "2.1.3" deno_core = { version = "0.152.0", path = "../../core" } deno_web = { version = "0.101.0", path = "../web" } elliptic-curve = { version = "0.12.1", features = ["std", "pem"] } @@ -40,3 +42,5 @@ sha2 = "0.10.2" spki = "0.6.0" tokio = { version = "1.21", features = ["full"] } uuid = { version = "1.0.0", features = ["v4"] } +# https://github.com/dalek-cryptography/x25519-dalek/pull/89 +x25519-dalek = "2.0.0-pre.1" diff --git a/ext/crypto/ed25519.rs b/ext/crypto/ed25519.rs new file mode 100644 index 0000000000..a8060cae1d --- /dev/null +++ b/ext/crypto/ed25519.rs @@ -0,0 +1,128 @@ +use deno_core::error::AnyError; +use deno_core::op; +use deno_core::ZeroCopyBuf; +use elliptic_curve::pkcs8::PrivateKeyInfo; +use rand::rngs::OsRng; +use rand::RngCore; +use ring::signature::Ed25519KeyPair; +use ring::signature::KeyPair; +use spki::der::Decode; +use spki::der::Encode; + +#[op(fast)] +pub fn op_generate_ed25519_keypair(pkey: &mut [u8], pubkey: &mut [u8]) -> bool { + let mut rng = OsRng; + rng.fill_bytes(pkey); + + let pair = match Ed25519KeyPair::from_seed_unchecked(pkey) { + Ok(p) => p, + Err(_) => return false, + }; + pubkey.copy_from_slice(pair.public_key().as_ref()); + true +} + +#[op(fast)] +pub fn op_sign_ed25519(key: &[u8], data: &[u8], signature: &mut [u8]) -> bool { + let pair = match Ed25519KeyPair::from_seed_unchecked(key) { + Ok(p) => p, + Err(_) => return false, + }; + signature.copy_from_slice(pair.sign(data).as_ref()); + true +} + +#[op(fast)] +pub fn op_verify_ed25519(pubkey: &[u8], data: &[u8], signature: &[u8]) -> bool { + ring::signature::UnparsedPublicKey::new(&ring::signature::ED25519, pubkey) + .verify(data, signature) + .is_ok() +} + +// id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 } +pub const ED25519_OID: const_oid::ObjectIdentifier = + const_oid::ObjectIdentifier::new_unwrap("1.3.101.112"); + +#[op(fast)] +pub fn op_import_spki_ed25519(key_data: &[u8], out: &mut [u8]) -> bool { + // 2-3. + let pk_info = match spki::SubjectPublicKeyInfo::from_der(key_data) { + Ok(pk_info) => pk_info, + Err(_) => return false, + }; + // 4. + let alg = pk_info.algorithm.oid; + if alg != ED25519_OID { + return false; + } + // 5. + if pk_info.algorithm.parameters.is_some() { + return false; + } + out.copy_from_slice(pk_info.subject_public_key); + true +} + +#[op(fast)] +pub fn op_import_pkcs8_ed25519(key_data: &[u8], out: &mut [u8]) -> bool { + // 2-3. + let pk_info = match PrivateKeyInfo::from_der(key_data) { + Ok(pk_info) => pk_info, + Err(_) => return false, + }; + // 4. + let alg = pk_info.algorithm.oid; + if alg != ED25519_OID { + return false; + } + // 5. + if pk_info.algorithm.parameters.is_some() { + return false; + } + // 6. + // CurvePrivateKey ::= OCTET STRING + if pk_info.private_key.len() != 32 { + return false; + } + out.copy_from_slice(pk_info.private_key); + true +} + +#[op] +pub fn op_export_spki_ed25519(pubkey: &[u8]) -> Result { + let key_info = spki::SubjectPublicKeyInfo { + algorithm: spki::AlgorithmIdentifier { + // id-Ed25519 + oid: ED25519_OID, + parameters: None, + }, + subject_public_key: pubkey, + }; + Ok(key_info.to_vec()?.into()) +} + +#[op] +pub fn op_export_pkcs8_ed25519(pkey: &[u8]) -> Result { + let pk_info = rsa::pkcs8::PrivateKeyInfo { + public_key: None, + algorithm: rsa::pkcs8::AlgorithmIdentifier { + // id-Ed25519 + oid: ED25519_OID, + parameters: None, + }, + private_key: pkey, // OCTET STRING + }; + + Ok(pk_info.to_vec()?.into()) +} + +// 'x' from Section 2 of RFC 8037 +// https://www.rfc-editor.org/rfc/rfc8037#section-2 +#[op] +pub fn op_jwk_x_ed25519(pkey: &[u8]) -> Result { + let pair = Ed25519KeyPair::from_seed_unchecked(pkey)?; + Ok(base64::encode_config( + pair.public_key().as_ref(), + base64::URL_SAFE_NO_PAD, + )) +} diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs index f4e1186267..0f1ee565bf 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -55,12 +55,14 @@ use std::path::PathBuf; pub use rand; // Re-export rand mod decrypt; +mod ed25519; mod encrypt; mod export_key; mod generate_key; mod import_key; mod key; mod shared; +mod x25519; pub use crate::decrypt::op_crypto_decrypt; pub use crate::encrypt::op_crypto_encrypt; @@ -98,6 +100,19 @@ pub fn init(maybe_seed: Option) -> Extension { op_crypto_random_uuid::decl(), op_crypto_wrap_key::decl(), op_crypto_unwrap_key::decl(), + op_crypto_base64url::decl(), + x25519::op_generate_x25519_keypair::decl(), + x25519::op_derive_bits_x25519::decl(), + x25519::op_import_spki_x25519::decl(), + x25519::op_import_pkcs8_x25519::decl(), + ed25519::op_generate_ed25519_keypair::decl(), + ed25519::op_import_spki_ed25519::decl(), + ed25519::op_import_pkcs8_ed25519::decl(), + ed25519::op_sign_ed25519::decl(), + ed25519::op_verify_ed25519::decl(), + ed25519::op_export_spki_ed25519::decl(), + ed25519::op_export_pkcs8_ed25519::decl(), + ed25519::op_jwk_x_ed25519::decl(), ]) .state(move |state| { if let Some(seed) = maybe_seed { @@ -108,6 +123,13 @@ pub fn init(maybe_seed: Option) -> Extension { .build() } +#[op] +pub fn op_crypto_base64url(data: String) -> ZeroCopyBuf { + let data: Vec = + base64::encode_config(data, base64::URL_SAFE_NO_PAD).into(); + data.into() +} + #[op] pub fn op_crypto_get_random_values( state: &mut OpState, diff --git a/ext/crypto/x25519.rs b/ext/crypto/x25519.rs new file mode 100644 index 0000000000..b606f4ca0b --- /dev/null +++ b/ext/crypto/x25519.rs @@ -0,0 +1,89 @@ +use curve25519_dalek::montgomery::MontgomeryPoint; +use deno_core::op; +use elliptic_curve::pkcs8::PrivateKeyInfo; +use elliptic_curve::subtle::ConstantTimeEq; +use rand::rngs::OsRng; +use rand::RngCore; +use spki::der::Decode; + +#[op(fast)] +pub fn op_generate_x25519_keypair(pkey: &mut [u8], pubkey: &mut [u8]) { + // u-coordinate of the base point. + const X25519_BASEPOINT_BYTES: [u8; 32] = [ + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ]; + let mut rng = OsRng; + rng.fill_bytes(pkey); + // https://www.rfc-editor.org/rfc/rfc7748#section-6.1 + // pubkey = x25519(a, 9) which is constant-time Montgomery ladder. + // https://eprint.iacr.org/2014/140.pdf page 4 + // https://eprint.iacr.org/2017/212.pdf algorithm 8 + // pubkey is in LE order. + let pkey: [u8; 32] = pkey.try_into().expect("Expected byteLength 32"); + pubkey.copy_from_slice(&x25519_dalek::x25519(pkey, X25519_BASEPOINT_BYTES)); +} + +const MONTGOMERY_IDENTITY: MontgomeryPoint = MontgomeryPoint([0; 32]); + +#[op(fast)] +pub fn op_derive_bits_x25519(k: &[u8], u: &[u8], secret: &mut [u8]) -> bool { + let k: [u8; 32] = k.try_into().expect("Expected byteLength 32"); + let u: [u8; 32] = u.try_into().expect("Expected byteLength 32"); + let sh_sec = x25519_dalek::x25519(k, u); + let point = MontgomeryPoint(sh_sec); + if point.ct_eq(&MONTGOMERY_IDENTITY).unwrap_u8() == 1 { + return false; + } + secret.copy_from_slice(&sh_sec); + true +} + +// id-X25519 OBJECT IDENTIFIER ::= { 1 3 101 110 } +pub const X25519_OID: const_oid::ObjectIdentifier = + const_oid::ObjectIdentifier::new_unwrap("1.3.101.110"); + +#[op(fast)] +pub fn op_import_spki_x25519(key_data: &[u8], out: &mut [u8]) -> bool { + // 2-3. + let pk_info = match spki::SubjectPublicKeyInfo::from_der(key_data) { + Ok(pk_info) => pk_info, + Err(_) => return false, + }; + // 4. + let alg = pk_info.algorithm.oid; + if alg != X25519_OID { + return false; + } + // 5. + if pk_info.algorithm.parameters.is_some() { + return false; + } + out.copy_from_slice(pk_info.subject_public_key); + true +} + +#[op(fast)] +pub fn op_import_pkcs8_x25519(key_data: &[u8], out: &mut [u8]) -> bool { + // 2-3. + let pk_info = match PrivateKeyInfo::from_der(key_data) { + Ok(pk_info) => pk_info, + Err(_) => return false, + }; + // 4. + let alg = pk_info.algorithm.oid; + if alg != X25519_OID { + return false; + } + // 5. + if pk_info.algorithm.parameters.is_some() { + return false; + } + // 6. + // CurvePrivateKey ::= OCTET STRING + if pk_info.private_key.len() != 32 { + return false; + } + out.copy_from_slice(pk_info.private_key); + true +} diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json index f1b476faf2..8bab051595 100644 --- a/tools/wpt/expectation.json +++ b/tools/wpt/expectation.json @@ -573,62 +573,8 @@ "successes_RSASSA-PKCS1-v1_5.https.any.worker.html?11-20": true, "successes_RSASSA-PKCS1-v1_5.https.any.worker.html?21-30": true, "successes_RSASSA-PKCS1-v1_5.https.any.worker.html?31-last": true, - "failures_Ed25519.https.any.html": [ - "Bad usages: generateKey({name: Ed25519}, true, [encrypt])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, encrypt])", - "Bad usages: generateKey({name: Ed25519}, true, [verify, sign, encrypt])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, verify, sign, sign, verify, encrypt])", - "Bad usages: generateKey({name: Ed25519}, true, [decrypt])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, decrypt])", - "Bad usages: generateKey({name: Ed25519}, true, [verify, sign, decrypt])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, verify, sign, sign, verify, decrypt])", - "Bad usages: generateKey({name: Ed25519}, true, [wrapKey])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, wrapKey])", - "Bad usages: generateKey({name: Ed25519}, true, [verify, sign, wrapKey])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, verify, sign, sign, verify, wrapKey])", - "Bad usages: generateKey({name: Ed25519}, true, [unwrapKey])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, unwrapKey])", - "Bad usages: generateKey({name: Ed25519}, true, [verify, sign, unwrapKey])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, verify, sign, sign, verify, unwrapKey])", - "Bad usages: generateKey({name: Ed25519}, true, [deriveKey])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, deriveKey])", - "Bad usages: generateKey({name: Ed25519}, true, [verify, sign, deriveKey])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, verify, sign, sign, verify, deriveKey])", - "Bad usages: generateKey({name: Ed25519}, true, [deriveBits])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, deriveBits])", - "Bad usages: generateKey({name: Ed25519}, true, [verify, sign, deriveBits])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, verify, sign, sign, verify, deriveBits])", - "Empty usages: generateKey({name: Ed25519}, false, [])", - "Empty usages: generateKey({name: Ed25519}, true, [])" - ], - "failures_Ed25519.https.any.worker.html": [ - "Bad usages: generateKey({name: Ed25519}, true, [encrypt])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, encrypt])", - "Bad usages: generateKey({name: Ed25519}, true, [verify, sign, encrypt])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, verify, sign, sign, verify, encrypt])", - "Bad usages: generateKey({name: Ed25519}, true, [decrypt])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, decrypt])", - "Bad usages: generateKey({name: Ed25519}, true, [verify, sign, decrypt])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, verify, sign, sign, verify, decrypt])", - "Bad usages: generateKey({name: Ed25519}, true, [wrapKey])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, wrapKey])", - "Bad usages: generateKey({name: Ed25519}, true, [verify, sign, wrapKey])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, verify, sign, sign, verify, wrapKey])", - "Bad usages: generateKey({name: Ed25519}, true, [unwrapKey])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, unwrapKey])", - "Bad usages: generateKey({name: Ed25519}, true, [verify, sign, unwrapKey])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, verify, sign, sign, verify, unwrapKey])", - "Bad usages: generateKey({name: Ed25519}, true, [deriveKey])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, deriveKey])", - "Bad usages: generateKey({name: Ed25519}, true, [verify, sign, deriveKey])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, verify, sign, sign, verify, deriveKey])", - "Bad usages: generateKey({name: Ed25519}, true, [deriveBits])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, deriveBits])", - "Bad usages: generateKey({name: Ed25519}, true, [verify, sign, deriveBits])", - "Bad usages: generateKey({name: Ed25519}, true, [sign, verify, sign, sign, verify, deriveBits])", - "Empty usages: generateKey({name: Ed25519}, false, [])", - "Empty usages: generateKey({name: Ed25519}, true, [])" - ], + "failures_Ed25519.https.any.html": true, + "failures_Ed25519.https.any.worker.html": true, "failures_Ed448.https.any.html": [ "Bad usages: generateKey({name: Ed448}, true, [encrypt])", "Bad usages: generateKey({name: Ed448}, true, [sign, encrypt])", @@ -685,74 +631,8 @@ "Empty usages: generateKey({name: Ed448}, false, [])", "Empty usages: generateKey({name: Ed448}, true, [])" ], - "failures_X25519.https.any.html": [ - "Bad usages: generateKey({name: X25519}, true, [encrypt])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, encrypt])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, deriveKey, encrypt])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, encrypt])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encrypt])", - "Bad usages: generateKey({name: X25519}, true, [decrypt])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, decrypt])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, deriveKey, decrypt])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, decrypt])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decrypt])", - "Bad usages: generateKey({name: X25519}, true, [sign])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, sign])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, deriveKey, sign])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, sign])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, sign])", - "Bad usages: generateKey({name: X25519}, true, [verify])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, verify])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, deriveKey, verify])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, verify])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, verify])", - "Bad usages: generateKey({name: X25519}, true, [wrapKey])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, wrapKey])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, deriveKey, wrapKey])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, wrapKey])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, wrapKey])", - "Bad usages: generateKey({name: X25519}, true, [unwrapKey])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, unwrapKey])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, deriveKey, unwrapKey])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, unwrapKey])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])", - "Empty usages: generateKey({name: X25519}, false, [])", - "Empty usages: generateKey({name: X25519}, true, [])" - ], - "failures_X25519.https.any.worker.html": [ - "Bad usages: generateKey({name: X25519}, true, [encrypt])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, encrypt])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, deriveKey, encrypt])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, encrypt])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, encrypt])", - "Bad usages: generateKey({name: X25519}, true, [decrypt])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, decrypt])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, deriveKey, decrypt])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, decrypt])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, decrypt])", - "Bad usages: generateKey({name: X25519}, true, [sign])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, sign])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, deriveKey, sign])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, sign])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, sign])", - "Bad usages: generateKey({name: X25519}, true, [verify])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, verify])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, deriveKey, verify])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, verify])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, verify])", - "Bad usages: generateKey({name: X25519}, true, [wrapKey])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, wrapKey])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, deriveKey, wrapKey])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, wrapKey])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, wrapKey])", - "Bad usages: generateKey({name: X25519}, true, [unwrapKey])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, unwrapKey])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, deriveKey, unwrapKey])", - "Bad usages: generateKey({name: X25519}, true, [deriveBits, unwrapKey])", - "Bad usages: generateKey({name: X25519}, true, [deriveKey, deriveBits, deriveKey, deriveBits, deriveKey, deriveBits, unwrapKey])", - "Empty usages: generateKey({name: X25519}, false, [])", - "Empty usages: generateKey({name: X25519}, true, [])" - ], + "failures_X25519.https.any.html": true, + "failures_X25519.https.any.worker.html": true, "failures_X448.https.any.html": [ "Bad usages: generateKey({name: X448}, true, [encrypt])", "Bad usages: generateKey({name: X448}, true, [deriveKey, encrypt])", @@ -821,12 +701,12 @@ "Empty usages: generateKey({name: X448}, false, [])", "Empty usages: generateKey({name: X448}, true, [])" ], - "successes_Ed25519.https.any.html": false, - "successes_Ed25519.https.any.worker.html": false, + "successes_Ed25519.https.any.html": true, + "successes_Ed25519.https.any.worker.html": true, "successes_Ed448.https.any.html": false, "successes_Ed448.https.any.worker.html": false, - "successes_X25519.https.any.html": false, - "successes_X25519.https.any.worker.html": false, + "successes_X25519.https.any.html": true, + "successes_X25519.https.any.worker.html": true, "successes_X448.https.any.html": false, "successes_X448.https.any.worker.html": false }, @@ -899,8 +779,98 @@ "rsa_importKey.https.any.worker.html": true, "symmetric_importKey.https.any.html": true, "symmetric_importKey.https.any.worker.html": true, - "okp_importKey.https.any.html": false, - "okp_importKey.https.any.worker.html": false + "okp_importKey.https.any.html": [ + "Good parameters: Ed25519 bits (spki, buffer(44), {name: Ed25519}, true, [])", + "Good parameters: Ed25519 bits (jwk, object(kty, crv, x), {name: Ed25519}, true, [])", + "Good parameters: Ed25519 bits (pkcs8, buffer(48), {name: Ed25519}, true, [sign])", + "Good parameters: Ed25519 bits (jwk, object(crv, d, x, kty), {name: Ed25519}, true, [sign])", + "Good parameters: Ed25519 bits (spki, buffer(44), {name: Ed25519}, false, [])", + "Good parameters: Ed25519 bits (jwk, object(kty, crv, x), {name: Ed25519}, false, [])", + "Good parameters: Ed25519 bits (pkcs8, buffer(48), {name: Ed25519}, false, [sign])", + "Good parameters: Ed25519 bits (jwk, object(crv, d, x, kty), {name: Ed25519}, false, [sign])", + "Good parameters: Ed448 bits (spki, buffer(69), {name: Ed448}, true, [])", + "Good parameters: Ed448 bits (jwk, object(kty, crv, x), {name: Ed448}, true, [])", + "Good parameters: Ed448 bits (pkcs8, buffer(73), {name: Ed448}, true, [sign])", + "Good parameters: Ed448 bits (jwk, object(crv, d, x, kty), {name: Ed448}, true, [sign])", + "Good parameters: Ed448 bits (spki, buffer(69), {name: Ed448}, false, [])", + "Good parameters: Ed448 bits (jwk, object(kty, crv, x), {name: Ed448}, false, [])", + "Good parameters: Ed448 bits (pkcs8, buffer(73), {name: Ed448}, false, [sign])", + "Good parameters: Ed448 bits (jwk, object(crv, d, x, kty), {name: Ed448}, false, [sign])", + "Good parameters: X25519 bits (spki, buffer(44), {name: X25519}, true, [])", + "Good parameters: X25519 bits (jwk, object(kty, crv, x), {name: X25519}, true, [])", + "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, true, [deriveKey])", + "Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey])", + "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, true, [deriveBits, deriveKey])", + "Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits, deriveKey])", + "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, true, [deriveBits])", + "Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits])", + "Good parameters: X25519 bits (jwk, object(kty, crv, x), {name: X25519}, false, [])", + "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, false, [deriveKey])", + "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, false, [deriveBits, deriveKey])", + "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, false, [deriveBits])", + "Good parameters: X448 bits (spki, buffer(68), {name: X448}, true, [])", + "Good parameters: X448 bits (jwk, object(kty, crv, x), {name: X448}, true, [])", + "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveKey])", + "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])", + "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveBits, deriveKey])", + "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveBits, deriveKey])", + "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveBits])", + "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveBits])", + "Good parameters: X448 bits (spki, buffer(68), {name: X448}, false, [])", + "Good parameters: X448 bits (jwk, object(kty, crv, x), {name: X448}, false, [])", + "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveKey])", + "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveKey])", + "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveBits, deriveKey])", + "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveBits, deriveKey])", + "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveBits])", + "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveBits])" + ], + "okp_importKey.https.any.worker.html": [ + "Good parameters: Ed25519 bits (spki, buffer(44), {name: Ed25519}, true, [])", + "Good parameters: Ed25519 bits (jwk, object(kty, crv, x), {name: Ed25519}, true, [])", + "Good parameters: Ed25519 bits (pkcs8, buffer(48), {name: Ed25519}, true, [sign])", + "Good parameters: Ed25519 bits (jwk, object(crv, d, x, kty), {name: Ed25519}, true, [sign])", + "Good parameters: Ed25519 bits (spki, buffer(44), {name: Ed25519}, false, [])", + "Good parameters: Ed25519 bits (jwk, object(kty, crv, x), {name: Ed25519}, false, [])", + "Good parameters: Ed25519 bits (pkcs8, buffer(48), {name: Ed25519}, false, [sign])", + "Good parameters: Ed25519 bits (jwk, object(crv, d, x, kty), {name: Ed25519}, false, [sign])", + "Good parameters: Ed448 bits (spki, buffer(69), {name: Ed448}, true, [])", + "Good parameters: Ed448 bits (jwk, object(kty, crv, x), {name: Ed448}, true, [])", + "Good parameters: Ed448 bits (pkcs8, buffer(73), {name: Ed448}, true, [sign])", + "Good parameters: Ed448 bits (jwk, object(crv, d, x, kty), {name: Ed448}, true, [sign])", + "Good parameters: Ed448 bits (spki, buffer(69), {name: Ed448}, false, [])", + "Good parameters: Ed448 bits (jwk, object(kty, crv, x), {name: Ed448}, false, [])", + "Good parameters: Ed448 bits (pkcs8, buffer(73), {name: Ed448}, false, [sign])", + "Good parameters: Ed448 bits (jwk, object(crv, d, x, kty), {name: Ed448}, false, [sign])", + "Good parameters: X25519 bits (spki, buffer(44), {name: X25519}, true, [])", + "Good parameters: X25519 bits (jwk, object(kty, crv, x), {name: X25519}, true, [])", + "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, true, [deriveKey])", + "Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveKey])", + "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, true, [deriveBits, deriveKey])", + "Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits, deriveKey])", + "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, true, [deriveBits])", + "Good parameters: X25519 bits (jwk, object(crv, d, x, kty), {name: X25519}, true, [deriveBits])", + "Good parameters: X25519 bits (jwk, object(kty, crv, x), {name: X25519}, false, [])", + "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, false, [deriveKey])", + "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, false, [deriveBits, deriveKey])", + "Good parameters: X25519 bits (pkcs8, buffer(48), {name: X25519}, false, [deriveBits])", + "Good parameters: X448 bits (spki, buffer(68), {name: X448}, true, [])", + "Good parameters: X448 bits (jwk, object(kty, crv, x), {name: X448}, true, [])", + "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveKey])", + "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveKey])", + "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveBits, deriveKey])", + "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveBits, deriveKey])", + "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, true, [deriveBits])", + "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, true, [deriveBits])", + "Good parameters: X448 bits (spki, buffer(68), {name: X448}, false, [])", + "Good parameters: X448 bits (jwk, object(kty, crv, x), {name: X448}, false, [])", + "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveKey])", + "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveKey])", + "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveBits, deriveKey])", + "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveBits, deriveKey])", + "Good parameters: X448 bits (pkcs8, buffer(72), {name: X448}, false, [deriveBits])", + "Good parameters: X448 bits (jwk, object(crv, d, x, kty), {name: X448}, false, [deriveBits])" + ] }, "randomUUID.https.any.html": true, "randomUUID.https.any.worker.html": true,