From b9c144df6fdee9b5e89f6f7787463b366164d622 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Wed, 28 Aug 2024 20:33:02 +0530 Subject: fix(ext/node): export JWK public key (#25239) Fixes https://github.com/denoland/deno/issues/18928 Signed-off-by: Divy Srivastava --- ext/node/lib.rs | 1 + ext/node/ops/crypto/keys.rs | 78 ++++++++++++++++++++++++++++++ ext/node/polyfills/internal/crypto/keys.ts | 4 +- 3 files changed, 82 insertions(+), 1 deletion(-) (limited to 'ext') diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 1023fced3..bf7db1475 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -242,6 +242,7 @@ deno_core::extension!(deno_node, ops::crypto::keys::op_node_export_private_key_pem, ops::crypto::keys::op_node_export_public_key_der, ops::crypto::keys::op_node_export_public_key_pem, + ops::crypto::keys::op_node_export_public_key_jwk, ops::crypto::keys::op_node_export_secret_key_b64url, ops::crypto::keys::op_node_export_secret_key, ops::crypto::keys::op_node_generate_dh_group_key_async, diff --git a/ext/node/ops/crypto/keys.rs b/ext/node/ops/crypto/keys.rs index eccd08564..7334fb8eb 100644 --- a/ext/node/ops/crypto/keys.rs +++ b/ext/node/ops/crypto/keys.rs @@ -235,6 +235,16 @@ impl RsaPssPrivateKey { } } +impl EcPublicKey { + pub fn to_jwk(&self) -> Result { + match self { + EcPublicKey::P224(_) => Err(type_error("Unsupported JWK EC curve: P224")), + EcPublicKey::P256(key) => Ok(key.to_jwk()), + EcPublicKey::P384(key) => Ok(key.to_jwk()), + } + } +} + impl EcPrivateKey { /// Derives the public key from the private key. pub fn to_public_key(&self) -> EcPublicKey { @@ -848,7 +858,63 @@ fn parse_rsa_pss_params( Ok(details) } +use base64::prelude::BASE64_URL_SAFE_NO_PAD; + +fn bytes_to_b64(bytes: &[u8]) -> String { + BASE64_URL_SAFE_NO_PAD.encode(bytes) +} + impl AsymmetricPublicKey { + fn export_jwk(&self) -> Result { + match self { + AsymmetricPublicKey::Ec(key) => { + let jwk = key.to_jwk()?; + Ok(deno_core::serde_json::json!(jwk)) + } + AsymmetricPublicKey::X25519(key) => { + let bytes = key.as_bytes(); + let jwk = deno_core::serde_json::json!({ + "kty": "OKP", + "crv": "X25519", + "x": bytes_to_b64(bytes), + }); + Ok(jwk) + } + AsymmetricPublicKey::Ed25519(key) => { + let bytes = key.to_bytes(); + let jwk = deno_core::serde_json::json!({ + "kty": "OKP", + "crv": "Ed25519", + "x": bytes_to_b64(&bytes), + }); + Ok(jwk) + } + AsymmetricPublicKey::Rsa(key) => { + let n = key.n(); + let e = key.e(); + + let jwk = deno_core::serde_json::json!({ + "kty": "RSA", + "n": bytes_to_b64(&n.to_bytes_be()), + "e": bytes_to_b64(&e.to_bytes_be()), + }); + Ok(jwk) + } + AsymmetricPublicKey::RsaPss(key) => { + let n = key.key.n(); + let e = key.key.e(); + + let jwk = deno_core::serde_json::json!({ + "kty": "RSA", + "n": bytes_to_b64(&n.to_bytes_be()), + "e": bytes_to_b64(&e.to_bytes_be()), + }); + Ok(jwk) + } + _ => Err(type_error("jwk export not implemented for this key type")), + } + } + fn export_der(&self, typ: &str) -> Result, AnyError> { match typ { "pkcs1" => match self { @@ -1848,6 +1914,18 @@ pub fn op_node_export_secret_key_b64url( Ok(base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(key)) } +#[op2] +#[serde] +pub fn op_node_export_public_key_jwk( + #[cppgc] handle: &KeyObjectHandle, +) -> Result { + let public_key = handle + .as_public_key() + .ok_or_else(|| type_error("key is not an asymmetric public key"))?; + + public_key.export_jwk() +} + #[op2] #[string] pub fn op_node_export_public_key_pem( diff --git a/ext/node/polyfills/internal/crypto/keys.ts b/ext/node/polyfills/internal/crypto/keys.ts index c2e9d95ee..49a618b65 100644 --- a/ext/node/polyfills/internal/crypto/keys.ts +++ b/ext/node/polyfills/internal/crypto/keys.ts @@ -21,6 +21,7 @@ import { op_node_export_private_key_der, op_node_export_private_key_pem, op_node_export_public_key_der, + op_node_export_public_key_jwk, op_node_export_public_key_pem, op_node_export_secret_key, op_node_export_secret_key_b64url, @@ -786,8 +787,9 @@ export class PublicKeyObject extends AsymmetricKeyObject { export(options: JwkKeyExportOptions | KeyExportOptions) { if (options && options.format === "jwk") { - notImplemented("jwk public key export not implemented"); + return op_node_export_public_key_jwk(this[kHandle]); } + const { format, type, -- cgit v1.2.3