mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
fix(ext/node): import EC JWK keys (#25266)
This commit is contained in:
parent
14a34a0cd7
commit
553bd7dec3
6 changed files with 116 additions and 7 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
@ -2694,6 +2694,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
|
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base16ct",
|
"base16ct",
|
||||||
|
"base64ct",
|
||||||
"crypto-bigint",
|
"crypto-bigint",
|
||||||
"digest",
|
"digest",
|
||||||
"ff",
|
"ff",
|
||||||
|
@ -2704,6 +2705,8 @@ dependencies = [
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
"sec1",
|
"sec1",
|
||||||
|
"serde_json",
|
||||||
|
"serdect",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
@ -6169,6 +6172,7 @@ dependencies = [
|
||||||
"der",
|
"der",
|
||||||
"generic-array",
|
"generic-array",
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
|
"serdect",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
@ -6314,6 +6318,16 @@ dependencies = [
|
||||||
"v8",
|
"v8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serdect"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
|
|
|
@ -106,7 +106,7 @@ deno_cache_dir = "=0.11.0"
|
||||||
deno_package_json = { version = "=0.1.1", default-features = false }
|
deno_package_json = { version = "=0.1.1", default-features = false }
|
||||||
dlopen2 = "0.6.1"
|
dlopen2 = "0.6.1"
|
||||||
ecb = "=0.1.2"
|
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"
|
encoding_rs = "=0.8.33"
|
||||||
fast-socks5 = "0.9.6"
|
fast-socks5 = "0.9.6"
|
||||||
faster-hex = "0.9"
|
faster-hex = "0.9"
|
||||||
|
@ -141,8 +141,8 @@ num-bigint = { version = "0.4", features = ["rand"] }
|
||||||
once_cell = "1.17.1"
|
once_cell = "1.17.1"
|
||||||
os_pipe = { version = "=1.1.5", features = ["io_safety"] }
|
os_pipe = { version = "=1.1.5", features = ["io_safety"] }
|
||||||
p224 = { version = "0.13.0", features = ["ecdh"] }
|
p224 = { version = "0.13.0", features = ["ecdh"] }
|
||||||
p256 = { version = "0.13.2", features = ["ecdh"] }
|
p256 = { version = "0.13.2", features = ["ecdh", "jwk"] }
|
||||||
p384 = { version = "0.13.0", features = ["ecdh"] }
|
p384 = { version = "0.13.0", features = ["ecdh", "jwk"] }
|
||||||
parking_lot = "0.12.0"
|
parking_lot = "0.12.0"
|
||||||
percent-encoding = "2.3.0"
|
percent-encoding = "2.3.0"
|
||||||
phf = { version = "0.11", features = ["macros"] }
|
phf = { version = "0.11", features = ["macros"] }
|
||||||
|
|
|
@ -233,6 +233,7 @@ deno_core::extension!(deno_node,
|
||||||
ops::crypto::op_node_verify_ed25519,
|
ops::crypto::op_node_verify_ed25519,
|
||||||
ops::crypto::keys::op_node_create_private_key,
|
ops::crypto::keys::op_node_create_private_key,
|
||||||
ops::crypto::keys::op_node_create_ed_raw,
|
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_public_key,
|
||||||
ops::crypto::keys::op_node_create_secret_key,
|
ops::crypto::keys::op_node_create_secret_key,
|
||||||
ops::crypto::keys::op_node_derive_public_key_from_private_key,
|
ops::crypto::keys::op_node_derive_public_key_from_private_key,
|
||||||
|
|
|
@ -13,6 +13,7 @@ use deno_core::unsync::spawn_blocking;
|
||||||
use deno_core::GarbageCollected;
|
use deno_core::GarbageCollected;
|
||||||
use deno_core::ToJsBuffer;
|
use deno_core::ToJsBuffer;
|
||||||
use ed25519_dalek::pkcs8::BitStringRef;
|
use ed25519_dalek::pkcs8::BitStringRef;
|
||||||
|
use elliptic_curve::JwkEcKey;
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
use num_traits::FromPrimitive as _;
|
use num_traits::FromPrimitive as _;
|
||||||
use pkcs8::DecodePrivateKey as _;
|
use pkcs8::DecodePrivateKey as _;
|
||||||
|
@ -571,6 +572,36 @@ impl KeyObjectHandle {
|
||||||
Ok(KeyObjectHandle::AsymmetricPublic(key))
|
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(
|
pub fn new_ed_raw(
|
||||||
curve: &str,
|
curve: &str,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
|
@ -1081,6 +1112,15 @@ pub fn op_node_create_ed_raw(
|
||||||
KeyObjectHandle::new_ed_raw(curve, key, is_public)
|
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]
|
#[op2]
|
||||||
#[cppgc]
|
#[cppgc]
|
||||||
pub fn op_node_create_public_key(
|
pub fn op_node_create_public_key(
|
||||||
|
|
|
@ -12,6 +12,7 @@ const {
|
||||||
} = primordials;
|
} = primordials;
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
op_node_create_ec_jwk,
|
||||||
op_node_create_ed_raw,
|
op_node_create_ed_raw,
|
||||||
op_node_create_private_key,
|
op_node_create_private_key,
|
||||||
op_node_create_public_key,
|
op_node_create_public_key,
|
||||||
|
@ -311,7 +312,15 @@ function getKeyObjectHandleFromJwk(key, ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.kty === "EC") {
|
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");
|
throw new TypeError("rsa jwk imports not implemented");
|
||||||
|
|
|
@ -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 = {
|
const key = {
|
||||||
"kty": "OKP",
|
"kty": "OKP",
|
||||||
"crv": "Ed25519",
|
"crv": "Ed25519",
|
||||||
|
@ -460,7 +460,7 @@ MCowBQYDK2VwAyEA11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=
|
||||||
assertEquals(spkiActual, spkiExpected);
|
assertEquals(spkiActual, spkiExpected);
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test("Ed25519 jwk public key #2", function () {
|
Deno.test("Ed25519 import jwk public key #2", function () {
|
||||||
const key = {
|
const key = {
|
||||||
"kty": "OKP",
|
"kty": "OKP",
|
||||||
"crv": "Ed25519",
|
"crv": "Ed25519",
|
||||||
|
@ -478,7 +478,7 @@ MCowBQYDK2VwAyEA11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=
|
||||||
assertEquals(spki, spkiExpected);
|
assertEquals(spki, spkiExpected);
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test("Ed25519 jwk private key", function () {
|
Deno.test("Ed25519 import jwk private key", function () {
|
||||||
const key = {
|
const key = {
|
||||||
"kty": "OKP",
|
"kty": "OKP",
|
||||||
"crv": "Ed25519",
|
"crv": "Ed25519",
|
||||||
|
@ -497,3 +497,48 @@ MC4CAQAwBQYDK2VwBCIEIJ1hsZ3v/VpguoRK9JLsLMREScVpezJpGXA7rAMcrn9g
|
||||||
|
|
||||||
assertEquals(pkcs8Actual, pkcs8Expected);
|
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);
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in a new issue