summaryrefslogtreecommitdiff
path: root/ext/crypto/00_crypto.js
diff options
context:
space:
mode:
authorSean Michael Wykes <8363933+SeanWykes@users.noreply.github.com>2021-12-16 13:28:43 -0300
committerGitHub <noreply@github.com>2021-12-16 17:28:43 +0100
commit60faf7a0edd492771943e603ec3d01c3602150e8 (patch)
tree81de27bb87d50365b8245a37691faf92d3a787b1 /ext/crypto/00_crypto.js
parent8efe829fca5ddea855b7f569c74b67c161ec4b06 (diff)
feat(ext/crypto): support importing ECSDA and ECDH (#13088)
Co-authored-by: Luca Casonato <hello@lcas.dev>
Diffstat (limited to 'ext/crypto/00_crypto.js')
-rw-r--r--ext/crypto/00_crypto.js271
1 files changed, 266 insertions, 5 deletions
diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js
index c8c7d9810..f15238b3a 100644
--- a/ext/crypto/00_crypto.js
+++ b/ext/crypto/00_crypto.js
@@ -108,6 +108,8 @@
"RSASSA-PKCS1-v1_5": "RsaHashedImportParams",
"RSA-PSS": "RsaHashedImportParams",
"RSA-OAEP": "RsaHashedImportParams",
+ "ECDSA": "EcImportParams",
+ "ECDH": "EcImportParams",
"HMAC": "HmacImportParams",
"HKDF": null,
"PBKDF2": null,
@@ -796,8 +798,9 @@
keyUsages,
);
}
+ case "ECDH":
case "ECDSA": {
- return importKeyECDSA(
+ return importKeyEC(
format,
normalizedAlgorithm,
keyData,
@@ -1144,6 +1147,7 @@
}
// 2.
const hash = normalizedAlgorithm.hash.name;
+
// 3-8.
return await core.opAsync("op_crypto_verify_key", {
key: keyData,
@@ -2195,13 +2199,28 @@
return key;
}
- function importKeyECDSA(
+ const SUPPORTED_EC_KEY_USAGES = {
+ "ECDSA": {
+ public: ["verify"],
+ private: ["sign"],
+ jwtUse: "sig",
+ },
+ "ECDH": {
+ public: [],
+ private: ["deriveKey", "deriveBits"],
+ jwtUse: "enc",
+ },
+ };
+
+ function importKeyEC(
format,
normalizedAlgorithm,
keyData,
extractable,
keyUsages,
) {
+ const supportedUsages = SUPPORTED_EC_KEY_USAGES[normalizedAlgorithm.name];
+
switch (format) {
case "raw": {
// 1.
@@ -2221,7 +2240,7 @@
if (
ArrayPrototypeFind(
keyUsages,
- (u) => !ArrayPrototypeIncludes(["verify"], u),
+ (u) => !ArrayPrototypeIncludes(supportedUsages.public, u),
) !== undefined
) {
throw new DOMException("Invalid key usages", "SyntaxError");
@@ -2229,7 +2248,7 @@
// 3.
const { rawData } = core.opSync("op_crypto_import_key", {
- algorithm: "ECDSA",
+ algorithm: normalizedAlgorithm.name,
namedCurve: normalizedAlgorithm.namedCurve,
}, { raw: keyData });
@@ -2238,7 +2257,7 @@
// 4-5.
const algorithm = {
- name: "ECDSA",
+ name: normalizedAlgorithm.name,
namedCurve: normalizedAlgorithm.namedCurve,
};
@@ -2253,6 +2272,248 @@
return key;
}
+ case "pkcs8": {
+ // 1.
+ if (
+ ArrayPrototypeFind(
+ keyUsages,
+ (u) => !ArrayPrototypeIncludes(supportedUsages.private, u),
+ ) !== undefined
+ ) {
+ throw new DOMException("Invalid key usages", "SyntaxError");
+ }
+
+ // 2-9.
+ const { rawData } = core.opSync("op_crypto_import_key", {
+ algorithm: normalizedAlgorithm.name,
+ namedCurve: normalizedAlgorithm.namedCurve,
+ }, { pkcs8: keyData });
+
+ const handle = {};
+ WeakMapPrototypeSet(KEY_STORE, handle, rawData);
+
+ const algorithm = {
+ name: normalizedAlgorithm.name,
+ namedCurve: normalizedAlgorithm.namedCurve,
+ };
+
+ const key = constructKey(
+ "private",
+ extractable,
+ usageIntersection(keyUsages, recognisedUsages),
+ algorithm,
+ handle,
+ );
+
+ return key;
+ }
+ case "spki": {
+ // 1.
+ if (normalizedAlgorithm.name == "ECDSA") {
+ if (
+ ArrayPrototypeFind(
+ keyUsages,
+ (u) => !ArrayPrototypeIncludes(supportedUsages.public, u),
+ ) !== undefined
+ ) {
+ throw new DOMException("Invalid key usages", "SyntaxError");
+ }
+ } else if (keyUsages.length != 0) {
+ throw new DOMException("Key usage must be empty", "SyntaxError");
+ }
+
+ // 2-12
+ const { rawData } = core.opSync("op_crypto_import_key", {
+ algorithm: normalizedAlgorithm.name,
+ namedCurve: normalizedAlgorithm.namedCurve,
+ }, { spki: keyData });
+
+ const handle = {};
+ WeakMapPrototypeSet(KEY_STORE, handle, rawData);
+
+ const algorithm = {
+ name: normalizedAlgorithm.name,
+ namedCurve: normalizedAlgorithm.namedCurve,
+ };
+
+ // 6-8.
+ const key = constructKey(
+ "public",
+ extractable,
+ usageIntersection(keyUsages, recognisedUsages),
+ algorithm,
+ handle,
+ );
+
+ return key;
+ }
+ case "jwk": {
+ const jwk = keyData;
+
+ const keyType = (jwk.d !== undefined) ? "private" : "public";
+
+ // 2.
+ if (
+ ArrayPrototypeFind(
+ keyUsages,
+ (u) => !ArrayPrototypeIncludes(supportedUsages[keyType], u),
+ ) !== undefined
+ ) {
+ throw new DOMException("Invalid key usages", "SyntaxError");
+ }
+
+ // 3.
+ if (jwk.kty !== "EC") {
+ throw new DOMException(
+ "'kty' property of JsonWebKey must be 'EC'",
+ "DataError",
+ );
+ }
+
+ // 4.
+ if (
+ keyUsages.length > 0 && jwk.use !== undefined &&
+ jwk.use !== supportedUsages.jwkUse
+ ) {
+ throw new DOMException(
+ `'use' property of JsonWebKey must be '${supportedUsages.jwkUse}'`,
+ "DataError",
+ );
+ }
+
+ // 5.
+ // Section 4.3 of RFC7517
+ if (jwk.key_ops !== undefined) {
+ if (
+ ArrayPrototypeFind(
+ jwk.key_ops,
+ (u) => !ArrayPrototypeIncludes(recognisedUsages, u),
+ ) !== undefined
+ ) {
+ throw new DOMException(
+ "'key_ops' member of JsonWebKey is invalid",
+ "DataError",
+ );
+ }
+
+ if (
+ !ArrayPrototypeEvery(
+ jwk.key_ops,
+ (u) => ArrayPrototypeIncludes(keyUsages, u),
+ )
+ ) {
+ throw new DOMException(
+ "'key_ops' member of JsonWebKey is invalid",
+ "DataError",
+ );
+ }
+ }
+
+ // 6.
+ if (jwk.ext === false && extractable === true) {
+ throw new DOMException(
+ "'ext' property of JsonWebKey must not be false if extractable is true",
+ "DataError",
+ );
+ }
+
+ // 9.
+ if (jwk.alg !== undefined && normalizedAlgorithm.name == "ECDSA") {
+ let algNamedCurve;
+
+ switch (jwk.alg) {
+ case "ES256": {
+ algNamedCurve = "P-256";
+ break;
+ }
+ case "ES384": {
+ algNamedCurve = "P-384";
+ break;
+ }
+ case "ES512": {
+ algNamedCurve = "P-521";
+ break;
+ }
+ default:
+ throw new DOMException(
+ "Curve algorithm not supported",
+ "DataError",
+ );
+ }
+
+ if (algNamedCurve) {
+ if (algNamedCurve !== normalizedAlgorithm.namedCurve) {
+ throw new DOMException(
+ "Mismatched curve algorithm",
+ "DataError",
+ );
+ }
+ }
+ }
+
+ // Validate that this is a valid public key.
+ if (jwk.x === undefined) {
+ throw new DOMException(
+ "'x' property of JsonWebKey is required for EC keys",
+ "DataError",
+ );
+ }
+ if (jwk.y === undefined) {
+ throw new DOMException(
+ "'y' property of JsonWebKey is required for EC keys",
+ "DataError",
+ );
+ }
+
+ if (jwk.d !== undefined) {
+ // it's also a Private key
+ const { rawData } = core.opSync("op_crypto_import_key", {
+ algorithm: normalizedAlgorithm.name,
+ namedCurve: normalizedAlgorithm.namedCurve,
+ }, { jwkPrivateEc: jwk });
+
+ const handle = {};
+ WeakMapPrototypeSet(KEY_STORE, handle, rawData);
+
+ const algorithm = {
+ name: normalizedAlgorithm.name,
+ namedCurve: normalizedAlgorithm.namedCurve,
+ };
+
+ const key = constructKey(
+ "private",
+ extractable,
+ usageIntersection(keyUsages, recognisedUsages),
+ algorithm,
+ handle,
+ );
+
+ return key;
+ } else {
+ const { rawData } = core.opSync("op_crypto_import_key", {
+ algorithm: normalizedAlgorithm.name,
+ namedCurve: normalizedAlgorithm.namedCurve,
+ }, { jwkPublicEc: jwk });
+
+ const handle = {};
+ WeakMapPrototypeSet(KEY_STORE, handle, rawData);
+
+ const algorithm = {
+ name: normalizedAlgorithm.name,
+ namedCurve: normalizedAlgorithm.namedCurve,
+ };
+
+ const key = constructKey(
+ "public",
+ extractable,
+ usageIntersection(keyUsages, recognisedUsages),
+ algorithm,
+ handle,
+ );
+
+ return key;
+ }
+ }
default:
throw new DOMException("Not implemented", "NotSupportedError");
}