diff options
Diffstat (limited to 'ext/node')
-rw-r--r-- | ext/node/Cargo.toml | 1 | ||||
-rw-r--r-- | ext/node/lib.rs | 1 | ||||
-rw-r--r-- | ext/node/ops/crypto/mod.rs | 108 | ||||
-rw-r--r-- | ext/node/polyfills/internal/crypto/keys.ts | 24 |
4 files changed, 130 insertions, 4 deletions
diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 053286053..8e3e695d6 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -68,6 +68,7 @@ sha-1 = "0.10.0" sha2.workspace = true signature.workspace = true simd-json = "0.13.4" +spki.workspace = true tokio.workspace = true typenum = "1.15.0" url.workspace = true diff --git a/ext/node/lib.rs b/ext/node/lib.rs index f9553a038..f8c9dfc88 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -329,6 +329,7 @@ deno_core::extension!(deno_node, ops::require::op_require_break_on_next_statement, ops::util::op_node_guess_handle_type, ops::crypto::op_node_create_private_key, + ops::crypto::op_node_create_public_key, ops::ipc::op_node_child_ipc_pipe, ops::ipc::op_node_ipc_write, ops::ipc::op_node_ipc_read, diff --git a/ext/node/ops/crypto/mod.rs b/ext/node/ops/crypto/mod.rs index 8db562eef..c2a9b1ab7 100644 --- a/ext/node/ops/crypto/mod.rs +++ b/ext/node/ops/crypto/mod.rs @@ -20,6 +20,7 @@ use rand::distributions::Uniform; use rand::thread_rng; use rand::Rng; use rsa::pkcs1::DecodeRsaPrivateKey; +use rsa::pkcs1::DecodeRsaPublicKey; use rsa::pkcs8; use rsa::pkcs8::der::asn1; use rsa::pkcs8::der::Decode; @@ -1459,3 +1460,110 @@ pub fn op_node_create_private_key( _ => Err(type_error("Unsupported algorithm")), } } + +fn parse_public_key( + key: &[u8], + format: &str, + type_: &str, +) -> Result<pkcs8::Document, AnyError> { + match format { + "pem" => { + let (label, doc) = + pkcs8::Document::from_pem(std::str::from_utf8(key).unwrap())?; + if label != "PUBLIC KEY" { + return Err(type_error("Invalid PEM label")); + } + Ok(doc) + } + "der" => { + match type_ { + "pkcs1" => pkcs8::Document::from_pkcs1_der(key) + .map_err(|_| type_error("Invalid PKCS1 public key")), + // TODO(@iuioiua): spki type + _ => Err(type_error(format!("Unsupported key type: {}", type_))), + } + } + _ => Err(type_error(format!("Unsupported key format: {}", format))), + } +} + +#[op2] +#[serde] +pub fn op_node_create_public_key( + #[buffer] key: &[u8], + #[string] format: &str, + #[string] type_: &str, +) -> Result<AsymmetricKeyDetails, AnyError> { + let doc = parse_public_key(key, format, type_)?; + let pk_info = spki::SubjectPublicKeyInfoRef::try_from(doc.as_bytes())?; + + let alg = pk_info.algorithm.oid; + + match alg { + RSA_ENCRYPTION_OID => { + let public_key = rsa::pkcs1::RsaPublicKey::from_der( + pk_info.subject_public_key.raw_bytes(), + )?; + let modulus_length = public_key.modulus.as_bytes().len() * 8; + + Ok(AsymmetricKeyDetails::Rsa { + modulus_length, + public_exponent: BigInt::from_bytes_be( + num_bigint::Sign::Plus, + public_key.public_exponent.as_bytes(), + ) + .into(), + }) + } + RSASSA_PSS_OID => { + let params = PssPrivateKeyParameters::try_from( + pk_info + .algorithm + .parameters + .ok_or_else(|| type_error("Malformed parameters".to_string()))?, + ) + .map_err(|_| type_error("Malformed parameters".to_string()))?; + + let hash_alg = params.hash_algorithm; + let hash_algorithm = match hash_alg.oid { + ID_SHA1_OID => "sha1", + ID_SHA256_OID => "sha256", + ID_SHA384_OID => "sha384", + ID_SHA512_OID => "sha512", + _ => return Err(type_error("Unsupported hash algorithm")), + }; + + let public_key = rsa::pkcs1::RsaPublicKey::from_der( + pk_info.subject_public_key.raw_bytes(), + )?; + let modulus_length = public_key.modulus.as_bytes().len() * 8; + Ok(AsymmetricKeyDetails::RsaPss { + modulus_length, + public_exponent: BigInt::from_bytes_be( + num_bigint::Sign::Plus, + public_key.public_exponent.as_bytes(), + ) + .into(), + hash_algorithm: hash_algorithm.to_string(), + salt_length: params.salt_length, + }) + } + EC_OID => { + let named_curve = pk_info + .algorithm + .parameters_oid() + .map_err(|_| type_error("malformed parameters"))?; + let named_curve = match named_curve { + ID_SECP256R1_OID => "p256", + ID_SECP384R1_OID => "p384", + ID_SECP521R1_OID => "p521", + _ => return Err(type_error("Unsupported named curve")), + }; + + Ok(AsymmetricKeyDetails::Ec { + named_curve: named_curve.to_string(), + }) + } + _ => Err(type_error("Unsupported algorithm")), + } +} diff --git a/ext/node/polyfills/internal/crypto/keys.ts b/ext/node/polyfills/internal/crypto/keys.ts index ab753582f..33034d824 100644 --- a/ext/node/polyfills/internal/crypto/keys.ts +++ b/ext/node/polyfills/internal/crypto/keys.ts @@ -4,7 +4,10 @@ // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials -import { op_node_create_private_key } from "ext:core/ops"; +import { + op_node_create_private_key, + op_node_create_public_key, +} from "ext:core/ops"; import { kHandle, @@ -239,9 +242,12 @@ export function createPrivateKey( } export function createPublicKey( - _key: PublicKeyInput | string | Buffer | KeyObject | JsonWebKeyInput, -): KeyObject { - notImplemented("crypto.createPublicKey"); + key: PublicKeyInput | string | Buffer | JsonWebKeyInput, +): PublicKeyObject { + const { data, format, type } = prepareAsymmetricKey(key); + const details = op_node_create_public_key(data, format, type); + const handle = setOwnedKey(copyBuffer(data)); + return new PublicKeyObject(handle, details); } function getKeyTypes(allowKeyObject: boolean, bufferOnly = false) { @@ -358,6 +364,16 @@ class PrivateKeyObject extends AsymmetricKeyObject { } } +class PublicKeyObject extends AsymmetricKeyObject { + constructor(handle: unknown, details: unknown) { + super("public", handle, details); + } + + export(_options: unknown) { + notImplemented("crypto.PublicKeyObject.prototype.export"); + } +} + export function setOwnedKey(key: Uint8Array): unknown { const handle = {}; KEY_STORE.set(handle, key); |