summaryrefslogtreecommitdiff
path: root/ext/node
diff options
context:
space:
mode:
Diffstat (limited to 'ext/node')
-rw-r--r--ext/node/Cargo.toml1
-rw-r--r--ext/node/lib.rs1
-rw-r--r--ext/node/ops/crypto/mod.rs108
-rw-r--r--ext/node/polyfills/internal/crypto/keys.ts24
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);