fix(ext/node): import EC JWK keys (#25266)

This commit is contained in:
Divy Srivastava 2024-08-28 19:54:49 +05:30 committed by GitHub
parent 14a34a0cd7
commit 553bd7dec3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 116 additions and 7 deletions

14
Cargo.lock generated
View File

@ -2694,6 +2694,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
dependencies = [
"base16ct",
"base64ct",
"crypto-bigint",
"digest",
"ff",
@ -2704,6 +2705,8 @@ dependencies = [
"pkcs8",
"rand_core",
"sec1",
"serde_json",
"serdect",
"subtle",
"zeroize",
]
@ -6169,6 +6172,7 @@ dependencies = [
"der",
"generic-array",
"pkcs8",
"serdect",
"subtle",
"zeroize",
]
@ -6314,6 +6318,16 @@ dependencies = [
"v8",
]
[[package]]
name = "serdect"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177"
dependencies = [
"base16ct",
"serde",
]
[[package]]
name = "sha1"
version = "0.10.6"

View File

@ -106,7 +106,7 @@ deno_cache_dir = "=0.11.0"
deno_package_json = { version = "=0.1.1", default-features = false }
dlopen2 = "0.6.1"
ecb = "=0.1.2"
elliptic-curve = { version = "0.13.4", features = ["alloc", "arithmetic", "ecdh", "std", "pem"] }
elliptic-curve = { version = "0.13.4", features = ["alloc", "arithmetic", "ecdh", "std", "pem", "jwk"] }
encoding_rs = "=0.8.33"
fast-socks5 = "0.9.6"
faster-hex = "0.9"
@ -141,8 +141,8 @@ num-bigint = { version = "0.4", features = ["rand"] }
once_cell = "1.17.1"
os_pipe = { version = "=1.1.5", features = ["io_safety"] }
p224 = { version = "0.13.0", features = ["ecdh"] }
p256 = { version = "0.13.2", features = ["ecdh"] }
p384 = { version = "0.13.0", features = ["ecdh"] }
p256 = { version = "0.13.2", features = ["ecdh", "jwk"] }
p384 = { version = "0.13.0", features = ["ecdh", "jwk"] }
parking_lot = "0.12.0"
percent-encoding = "2.3.0"
phf = { version = "0.11", features = ["macros"] }

View File

@ -233,6 +233,7 @@ deno_core::extension!(deno_node,
ops::crypto::op_node_verify_ed25519,
ops::crypto::keys::op_node_create_private_key,
ops::crypto::keys::op_node_create_ed_raw,
ops::crypto::keys::op_node_create_ec_jwk,
ops::crypto::keys::op_node_create_public_key,
ops::crypto::keys::op_node_create_secret_key,
ops::crypto::keys::op_node_derive_public_key_from_private_key,

View File

@ -13,6 +13,7 @@ use deno_core::unsync::spawn_blocking;
use deno_core::GarbageCollected;
use deno_core::ToJsBuffer;
use ed25519_dalek::pkcs8::BitStringRef;
use elliptic_curve::JwkEcKey;
use num_bigint::BigInt;
use num_traits::FromPrimitive as _;
use pkcs8::DecodePrivateKey as _;
@ -571,6 +572,36 @@ impl KeyObjectHandle {
Ok(KeyObjectHandle::AsymmetricPublic(key))
}
pub fn new_ec_jwk(
jwk: &JwkEcKey,
is_public: bool,
) -> Result<KeyObjectHandle, AnyError> {
// https://datatracker.ietf.org/doc/html/rfc7518#section-6.2.1.1
let handle = match jwk.crv() {
"P-256" if is_public => {
KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Ec(
EcPublicKey::P256(p256::PublicKey::from_jwk(jwk)?),
))
}
"P-256" => KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Ec(
EcPrivateKey::P256(p256::SecretKey::from_jwk(jwk)?),
)),
"P-384" if is_public => {
KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Ec(
EcPublicKey::P384(p384::PublicKey::from_jwk(jwk)?),
))
}
"P-384" => KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Ec(
EcPrivateKey::P384(p384::SecretKey::from_jwk(jwk)?),
)),
_ => {
return Err(type_error(format!("unsupported curve: {}", jwk.crv())));
}
};
Ok(handle)
}
pub fn new_ed_raw(
curve: &str,
data: &[u8],
@ -1081,6 +1112,15 @@ pub fn op_node_create_ed_raw(
KeyObjectHandle::new_ed_raw(curve, key, is_public)
}
#[op2]
#[cppgc]
pub fn op_node_create_ec_jwk(
#[serde] jwk: elliptic_curve::JwkEcKey,
is_public: bool,
) -> Result<KeyObjectHandle, AnyError> {
KeyObjectHandle::new_ec_jwk(&jwk, is_public)
}
#[op2]
#[cppgc]
pub fn op_node_create_public_key(

View File

@ -12,6 +12,7 @@ const {
} = primordials;
import {
op_node_create_ec_jwk,
op_node_create_ed_raw,
op_node_create_private_key,
op_node_create_public_key,
@ -311,7 +312,15 @@ function getKeyObjectHandleFromJwk(key, ctx) {
}
if (key.kty === "EC") {
throw new TypeError("ec jwk imports not implemented");
validateString(key.crv, "key.crv");
validateString(key.x, "key.x");
validateString(key.y, "key.y");
if (!isPublic) {
validateString(key.d, "key.d");
}
return op_node_create_ec_jwk(key, isPublic);
}
throw new TypeError("rsa jwk imports not implemented");

View File

@ -440,7 +440,7 @@ Deno.test("create private key with invalid utf-8 string", function () {
);
});
Deno.test("Ed25519 jwk public key #1", function () {
Deno.test("Ed25519 import jwk public key #1", function () {
const key = {
"kty": "OKP",
"crv": "Ed25519",
@ -460,7 +460,7 @@ MCowBQYDK2VwAyEA11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=
assertEquals(spkiActual, spkiExpected);
});
Deno.test("Ed25519 jwk public key #2", function () {
Deno.test("Ed25519 import jwk public key #2", function () {
const key = {
"kty": "OKP",
"crv": "Ed25519",
@ -478,7 +478,7 @@ MCowBQYDK2VwAyEA11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=
assertEquals(spki, spkiExpected);
});
Deno.test("Ed25519 jwk private key", function () {
Deno.test("Ed25519 import jwk private key", function () {
const key = {
"kty": "OKP",
"crv": "Ed25519",
@ -497,3 +497,48 @@ MC4CAQAwBQYDK2VwBCIEIJ1hsZ3v/VpguoRK9JLsLMREScVpezJpGXA7rAMcrn9g
assertEquals(pkcs8Actual, pkcs8Expected);
});
Deno.test("EC import jwk public key", function () {
const publicKey = createPublicKey({
key: {
kty: "EC",
x: "_GGuz19zab5J70zyiUK6sAM5mHqUbsY8H6U2TnVlt-k",
y: "TcZG5efXZDIhNGDp6XuujoJqOEJU2D2ckjG9nOnSPIQ",
crv: "P-256",
},
format: "jwk",
});
const publicSpki = publicKey.export({ type: "spki", format: "pem" });
const spkiExpected = `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/GGuz19zab5J70zyiUK6sAM5mHqU
bsY8H6U2TnVlt+lNxkbl59dkMiE0YOnpe66Ogmo4QlTYPZySMb2c6dI8hA==
-----END PUBLIC KEY-----
`;
assertEquals(publicSpki, spkiExpected);
});
Deno.test("EC import jwk private key", function () {
const privateKey = createPrivateKey({
key: {
kty: "EC",
x: "_GGuz19zab5J70zyiUK6sAM5mHqUbsY8H6U2TnVlt-k",
y: "TcZG5efXZDIhNGDp6XuujoJqOEJU2D2ckjG9nOnSPIQ",
crv: "P-256",
d: "Wobjne0GqlB_1NynKu19rsw7zBHa94tKcWIxwIb88m8",
},
format: "jwk",
});
const privatePkcs8 = privateKey.export({ type: "pkcs8", format: "pem" });
const pkcs8Expected = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWobjne0GqlB/1Nyn
Ku19rsw7zBHa94tKcWIxwIb88m+hRANCAAT8Ya7PX3NpvknvTPKJQrqwAzmYepRu
xjwfpTZOdWW36U3GRuXn12QyITRg6el7ro6CajhCVNg9nJIxvZzp0jyE
-----END PRIVATE KEY-----
`;
assertEquals(privatePkcs8, pkcs8Expected);
});