summaryrefslogtreecommitdiff
path: root/ext/crypto/00_crypto.js
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2024-10-07 16:34:40 +0530
committerGitHub <noreply@github.com>2024-10-07 12:04:40 +0100
commit39a2034967207b89069cf64a76308e1446b1ad26 (patch)
treefc0c5c3961957e29df261db75edd420ad8d3e07d /ext/crypto/00_crypto.js
parent2de4faa483982478e9a36ad4ab891a887b4779f1 (diff)
feat(ext/crypto): X448 support (#26043)
Signed-off-by: Divy Srivastava <dj.srivastava23@gmail.com>
Diffstat (limited to 'ext/crypto/00_crypto.js')
-rw-r--r--ext/crypto/00_crypto.js377
1 files changed, 377 insertions, 0 deletions
diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js
index 8e43b76f7..63b190514 100644
--- a/ext/crypto/00_crypto.js
+++ b/ext/crypto/00_crypto.js
@@ -18,21 +18,27 @@ import {
op_crypto_decrypt,
op_crypto_derive_bits,
op_crypto_derive_bits_x25519,
+ op_crypto_derive_bits_x448,
op_crypto_encrypt,
op_crypto_export_key,
op_crypto_export_pkcs8_ed25519,
op_crypto_export_pkcs8_x25519,
+ op_crypto_export_pkcs8_x448,
op_crypto_export_spki_ed25519,
op_crypto_export_spki_x25519,
+ op_crypto_export_spki_x448,
op_crypto_generate_ed25519_keypair,
op_crypto_generate_key,
op_crypto_generate_x25519_keypair,
+ op_crypto_generate_x448_keypair,
op_crypto_get_random_values,
op_crypto_import_key,
op_crypto_import_pkcs8_ed25519,
op_crypto_import_pkcs8_x25519,
+ op_crypto_import_pkcs8_x448,
op_crypto_import_spki_ed25519,
op_crypto_import_spki_x25519,
+ op_crypto_import_spki_x448,
op_crypto_jwk_x_ed25519,
op_crypto_random_uuid,
op_crypto_sign_ed25519,
@@ -134,6 +140,7 @@ const supportedAlgorithms = {
"AES-KW": "AesKeyGenParams",
"HMAC": "HmacKeyGenParams",
"X25519": null,
+ "X448": null,
"Ed25519": null,
},
"sign": {
@@ -165,12 +172,14 @@ const supportedAlgorithms = {
"AES-KW": null,
"Ed25519": null,
"X25519": null,
+ "X448": null,
},
"deriveBits": {
"HKDF": "HkdfParams",
"PBKDF2": "Pbkdf2Params",
"ECDH": "EcdhKeyDeriveParams",
"X25519": "EcdhKeyDeriveParams",
+ "X448": "EcdhKeyDeriveParams",
},
"encrypt": {
"RSA-OAEP": "RsaOaepParams",
@@ -1037,6 +1046,10 @@ class SubtleCrypto {
result = exportKeyEd25519(format, key, innerKey);
break;
}
+ case "X448": {
+ result = exportKeyX448(format, key, innerKey);
+ break;
+ }
case "X25519": {
result = exportKeyX25519(format, key, innerKey);
break;
@@ -1954,6 +1967,48 @@ async function generateKey(normalizedAlgorithm, extractable, usages) {
return generateKeyAES(normalizedAlgorithm, extractable, usages);
}
+ case "X448": {
+ if (
+ ArrayPrototypeFind(
+ usages,
+ (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u),
+ ) !== undefined
+ ) {
+ throw new DOMException("Invalid key usage", "SyntaxError");
+ }
+ const privateKeyData = new Uint8Array(56);
+ const publicKeyData = new Uint8Array(56);
+
+ op_crypto_generate_x448_keypair(privateKeyData, publicKeyData);
+
+ const handle = {};
+ WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData);
+
+ const publicHandle = {};
+ WeakMapPrototypeSet(KEY_STORE, publicHandle, 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 "X25519": {
if (
ArrayPrototypeFind(
@@ -2100,6 +2155,211 @@ async function generateKey(normalizedAlgorithm, extractable, usages) {
}
}
+function importKeyX448(
+ format,
+ keyData,
+ extractable,
+ keyUsages,
+) {
+ switch (format) {
+ case "raw": {
+ // 1.
+ if (keyUsages.length > 0) {
+ throw new DOMException("Invalid key usage", "SyntaxError");
+ }
+
+ const handle = {};
+ WeakMapPrototypeSet(KEY_STORE, handle, keyData);
+
+ // 2-3.
+ const algorithm = {
+ name: "X448",
+ };
+
+ // 4-6.
+ return constructKey(
+ "public",
+ extractable,
+ [],
+ algorithm,
+ handle,
+ );
+ }
+ case "spki": {
+ // 1.
+ if (keyUsages.length > 0) {
+ throw new DOMException("Invalid key usage", "SyntaxError");
+ }
+
+ const publicKeyData = new Uint8Array(56);
+ if (!op_crypto_import_spki_x448(keyData, publicKeyData)) {
+ throw new DOMException("Invalid key data", "DataError");
+ }
+
+ const handle = {};
+ WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData);
+
+ const algorithm = {
+ name: "X448",
+ };
+
+ return constructKey(
+ "public",
+ extractable,
+ [],
+ algorithm,
+ handle,
+ );
+ }
+ case "pkcs8": {
+ // 1.
+ if (
+ ArrayPrototypeFind(
+ keyUsages,
+ (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u),
+ ) !== undefined
+ ) {
+ throw new DOMException("Invalid key usage", "SyntaxError");
+ }
+
+ const privateKeyData = new Uint8Array(32);
+ if (!op_crypto_import_pkcs8_x448(keyData, privateKeyData)) {
+ throw new DOMException("Invalid key data", "DataError");
+ }
+
+ const handle = {};
+ WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData);
+
+ const algorithm = {
+ name: "X448",
+ };
+
+ return constructKey(
+ "private",
+ extractable,
+ usageIntersection(keyUsages, recognisedUsages),
+ algorithm,
+ handle,
+ );
+ }
+ case "jwk": {
+ // 1.
+ const jwk = keyData;
+
+ // 2.
+ if (jwk.d !== undefined) {
+ if (
+ ArrayPrototypeFind(
+ keyUsages,
+ (u) =>
+ !ArrayPrototypeIncludes(
+ ["deriveKey", "deriveBits"],
+ u,
+ ),
+ ) !== undefined
+ ) {
+ throw new DOMException("Invalid key usage", "SyntaxError");
+ }
+ }
+
+ // 3.
+ if (jwk.d === undefined && keyUsages.length > 0) {
+ throw new DOMException("Invalid key usage", "SyntaxError");
+ }
+
+ // 4.
+ if (jwk.kty !== "OKP") {
+ throw new DOMException("Invalid key type", "DataError");
+ }
+
+ // 5.
+ if (jwk.crv !== "X448") {
+ 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 = op_crypto_base64url_decode(jwk.d);
+
+ const handle = {};
+ WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData);
+
+ const algorithm = {
+ name: "X448",
+ };
+
+ return constructKey(
+ "private",
+ extractable,
+ usageIntersection(keyUsages, ["deriveKey", "deriveBits"]),
+ algorithm,
+ handle,
+ );
+ } else {
+ // https://www.rfc-editor.org/rfc/rfc8037#section-2
+ const publicKeyData = op_crypto_base64url_decode(jwk.x);
+
+ const handle = {};
+ WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData);
+
+ const algorithm = {
+ name: "X448",
+ };
+
+ return constructKey(
+ "public",
+ extractable,
+ [],
+ algorithm,
+ handle,
+ );
+ }
+ }
+ default:
+ throw new DOMException("Not implemented", "NotSupportedError");
+ }
+}
+
function importKeyEd25519(
format,
keyData,
@@ -3358,6 +3618,14 @@ async function importKeyInner(
["wrapKey", "unwrapKey"],
);
}
+ case "X448": {
+ return importKeyX448(
+ format,
+ keyData,
+ extractable,
+ keyUsages,
+ );
+ }
case "X25519": {
return importKeyX25519(
format,
@@ -4162,6 +4430,66 @@ function exportKeyEd25519(format, key, innerKey) {
}
}
+function exportKeyX448(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 TypedArrayPrototypeGetBuffer(innerKey);
+ }
+ case "spki": {
+ // 1.
+ if (key[_type] !== "public") {
+ throw new DOMException(
+ "Key is not a public key",
+ "InvalidAccessError",
+ );
+ }
+
+ const spkiDer = op_crypto_export_spki_x448(innerKey);
+ return TypedArrayPrototypeGetBuffer(spkiDer);
+ }
+ case "pkcs8": {
+ // 1.
+ if (key[_type] !== "private") {
+ throw new DOMException(
+ "Key is not a private key",
+ "InvalidAccessError",
+ );
+ }
+
+ const pkcs8Der = op_crypto_export_pkcs8_x448(
+ new Uint8Array([0x04, 0x22, ...new SafeArrayIterator(innerKey)]),
+ );
+ pkcs8Der[15] = 0x20;
+ return TypedArrayPrototypeGetBuffer(pkcs8Der);
+ }
+ case "jwk": {
+ if (key[_type] === "private") {
+ throw new DOMException("Not implemented", "NotSupportedError");
+ }
+ const x = op_crypto_base64url_encode(innerKey);
+ const jwk = {
+ kty: "OKP",
+ crv: "X448",
+ x,
+ "key_ops": key.usages,
+ ext: key[_extractable],
+ };
+ return jwk;
+ }
+ default:
+ throw new DOMException("Not implemented", "NotSupportedError");
+ }
+}
+
function exportKeyX25519(format, key, innerKey) {
switch (format) {
case "raw": {
@@ -4519,6 +4847,55 @@ async function deriveBits(normalizedAlgorithm, baseKey, length) {
return TypedArrayPrototypeGetBuffer(buf);
}
+ case "X448": {
+ // 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(56);
+ const isIdentity = op_crypto_derive_bits_x448(k, u, secret);
+
+ // 6.
+ if (isIdentity) {
+ throw new DOMException("Invalid key", "OperationError");
+ }
+
+ // 7.
+ if (length === null) {
+ return TypedArrayPrototypeGetBuffer(secret);
+ } else if (
+ TypedArrayPrototypeGetByteLength(secret) * 8 < length
+ ) {
+ throw new DOMException("Invalid length", "OperationError");
+ } else {
+ return ArrayBufferPrototypeSlice(
+ TypedArrayPrototypeGetBuffer(secret),
+ 0,
+ MathCeil(length / 8),
+ );
+ }
+ }
case "X25519": {
// 1.
if (baseKey[_type] !== "private") {