From c3d670dbc992ffaff02cd8df82335ee41e88596e Mon Sep 17 00:00:00 2001 From: Levente Kurusa Date: Thu, 27 Apr 2023 18:31:35 +0200 Subject: [PATCH] feat(node/crypto): Elliptic Curve Diffie-Hellman (ECDH) support (#18832) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ECDH class - crypto.createECDH() - Supported curves: - secp256k1 - prime256v1 / secp256r1 - secp384r1 - secp224r1 Co-authored-by: Bartek IwaƄczuk --- Cargo.lock | 205 ++++++++++++++++-- Cargo.toml | 4 + ext/node/Cargo.toml | 5 + ext/node/lib.rs | 4 +- ext/node/ops/crypto/mod.rs | 165 ++++++++++++++ .../internal/crypto/diffiehellman.ts | 75 ++++++- ext/node/polyfills/internal/crypto/util.ts | 44 +++- 7 files changed, 469 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 352ca75106..ddd92ea833 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -243,6 +243,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base32" version = "0.4.0" @@ -595,6 +601,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "crypto-bigint" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" +dependencies = [ + "generic-array 0.14.6", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -877,15 +895,15 @@ dependencies = [ "curve25519-dalek 2.1.3", "deno_core", "deno_web", - "elliptic-curve", + "elliptic-curve 0.12.3", "num-traits", "once_cell", - "p256", - "p384", + "p256 0.11.1", + "p384 0.11.2", "rand", "ring", "rsa", - "sec1", + "sec1 0.3.0", "serde", "serde_bytes", "sha1", @@ -1137,6 +1155,7 @@ dependencies = [ "digest 0.10.6", "dsa", "ecb", + "elliptic-curve 0.13.4", "hex", "hkdf", "idna 0.3.0", @@ -1150,6 +1169,9 @@ dependencies = [ "num-integer", "num-traits", "once_cell", + "p224", + "p256 0.13.2", + "p384 0.13.0", "path-clean", "pbkdf2", "rand", @@ -1158,6 +1180,7 @@ dependencies = [ "ripemd", "rsa", "scrypt", + "secp256k1", "serde", "sha-1", "sha2", @@ -1371,7 +1394,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ "const-oid", - "pem-rfc7468", + "pem-rfc7468 0.6.0", "zeroize", ] @@ -1382,6 +1405,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82b10af9f9f9f2134a42d3f8aa74658660f2e0234b0eb81bd171df8aa32779ed" dependencies = [ "const-oid", + "pem-rfc7468 0.7.0", "zeroize", ] @@ -1628,11 +1652,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ "der 0.6.1", - "elliptic-curve", + "elliptic-curve 0.12.3", "rfc6979 0.3.1", "signature 1.6.4", ] +[[package]] +name = "ecdsa" +version = "0.16.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a48e5d537b8a30c0b023116d981b16334be1485af7ca68db3a2b7024cbc957fd" +dependencies = [ + "der 0.7.3", + "digest 0.10.6", + "elliptic-curve 0.13.4", + "rfc6979 0.4.0", + "signature 2.1.0", +] + [[package]] name = "either" version = "1.8.1" @@ -1645,18 +1682,39 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", - "crypto-bigint", + "base16ct 0.1.1", + "crypto-bigint 0.4.9", "der 0.6.1", "digest 0.10.6", - "ff", + "ff 0.12.1", "generic-array 0.14.6", - "group", + "group 0.12.1", "hkdf", - "pem-rfc7468", + "pem-rfc7468 0.6.0", "pkcs8 0.9.0", "rand_core 0.6.4", - "sec1", + "sec1 0.3.0", + "subtle", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.2", + "digest 0.10.6", + "ff 0.13.0", + "generic-array 0.14.6", + "group 0.13.0", + "hkdf", + "pem-rfc7468 0.7.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1 0.7.1", "subtle", "zeroize", ] @@ -1828,6 +1886,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "filetime" version = "0.2.20" @@ -2051,6 +2119,7 @@ checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -2106,7 +2175,18 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ - "ff", + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", "rand_core 0.6.4", "subtle", ] @@ -3123,14 +3203,38 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" +[[package]] +name = "p224" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30c06436d66652bc2f01ade021592c80a2aad401570a18aa18b82e440d2b9aa1" +dependencies = [ + "ecdsa 0.16.6", + "elliptic-curve 0.13.4", + "primeorder", + "sha2", +] + [[package]] name = "p256" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" dependencies = [ - "ecdsa", - "elliptic-curve", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa 0.16.6", + "elliptic-curve 0.13.4", + "primeorder", "sha2", ] @@ -3140,8 +3244,20 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" dependencies = [ - "ecdsa", - "elliptic-curve", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa 0.16.6", + "elliptic-curve 0.13.4", + "primeorder", "sha2", ] @@ -3244,6 +3360,15 @@ dependencies = [ "base64ct", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.2.0" @@ -3431,6 +3556,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "primeorder" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8d3875361e28f7753baefef104386e7aa47642c93023356d97fdef4003bfb5" +dependencies = [ + "elliptic-curve 0.13.4", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -3689,7 +3823,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ - "crypto-bigint", + "crypto-bigint 0.4.9", "hmac", "zeroize", ] @@ -3955,7 +4089,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "base16ct", + "base16ct 0.1.1", "der 0.6.1", "generic-array 0.14.6", "pkcs8 0.9.0", @@ -3963,6 +4097,39 @@ dependencies = [ "zeroize", ] +[[package]] +name = "sec1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48518a2b5775ba8ca5b46596aae011caa431e6ce7e4a67ead66d92f08884220e" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.3", + "generic-array 0.14.6", + "pkcs8 0.10.2", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" +dependencies = [ + "rand", + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" +dependencies = [ + "cc", +] + [[package]] name = "security-framework" version = "2.8.2" diff --git a/Cargo.toml b/Cargo.toml index 3b0a0abf2a..5664a69ed2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -139,6 +139,10 @@ tower-lsp = { version = "=0.17.0", features = ["proposed"] } url = { version = "2.3.1", features = ["serde", "expose_internals"] } uuid = { version = "1.3.0", features = ["v4"] } zstd = "=0.11.2" +elliptic-curve = { version = "0.13.4", features = ["alloc", "arithmetic", "ecdh", "std", "pem"] } +p224 = { version = "0.13.0", features = ["ecdh"] } +p256 = { version = "0.13.2", features = ["ecdh"] } +p384 = { version = "0.13.0", features = ["ecdh"] } # crypto rsa = { version = "0.7.0", default-features = false, features = ["std", "pem", "hazmat"] } # hazmat needed for PrehashSigner in ext/node diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 576e62d559..14928db307 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -24,6 +24,7 @@ deno_semver.workspace = true digest = { version = "0.10.5", features = ["core-api", "std"] } dsa = "0.6.1" ecb.workspace = true +elliptic-curve.workspace = true hex.workspace = true hkdf.workspace = true idna = "0.3.0" @@ -37,6 +38,9 @@ num-bigint-dig = "0.8.2" num-integer = "0.1.45" num-traits = "0.2.14" once_cell.workspace = true +p224.workspace = true +p256.workspace = true +p384.workspace = true path-clean = "=0.1.0" pbkdf2 = "0.12.1" rand.workspace = true @@ -45,6 +49,7 @@ ring.workspace = true ripemd = "0.1.3" rsa.workspace = true scrypt = "0.11.0" +secp256k1 = { version = "0.27.0", features = ["rand-std"] } serde = "1.0.149" sha-1 = "0.10.0" sha2.workspace = true diff --git a/ext/node/lib.rs b/ext/node/lib.rs index cef92328d9..b5db83297e 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -240,6 +240,9 @@ deno_core::extension!(deno_node, ops::crypto::op_node_random_int, ops::crypto::op_node_scrypt_sync, ops::crypto::op_node_scrypt_async, + ops::crypto::op_node_ecdh_generate_keys, + ops::crypto::op_node_ecdh_compute_secret, + ops::crypto::op_node_ecdh_compute_public_key, ops::crypto::x509::op_node_x509_parse, ops::crypto::x509::op_node_x509_ca, ops::crypto::x509::op_node_x509_check_email, @@ -267,7 +270,6 @@ deno_core::extension!(deno_node, ops::zlib::op_zlib_init, ops::zlib::op_zlib_reset, op_node_build_os, - ops::require::op_require_init_paths, ops::require::op_require_node_module_paths, ops::require::op_require_proxy_path, diff --git a/ext/node/ops/crypto/mod.rs b/ext/node/ops/crypto/mod.rs index 92e3029e0d..9e1a3da989 100644 --- a/ext/node/ops/crypto/mod.rs +++ b/ext/node/ops/crypto/mod.rs @@ -18,12 +18,18 @@ use rand::Rng; use std::future::Future; use std::rc::Rc; +use p224::NistP224; +use p256::NistP256; +use p384::NistP384; use rsa::padding::PaddingScheme; use rsa::pkcs8::DecodePrivateKey; use rsa::pkcs8::DecodePublicKey; use rsa::PublicKey; use rsa::RsaPrivateKey; use rsa::RsaPublicKey; +use secp256k1::ecdh::SharedSecret; +use secp256k1::Secp256k1; +use secp256k1::SecretKey; mod cipher; mod dh; @@ -902,6 +908,165 @@ pub async fn op_node_scrypt_async( .await? } +#[op] +pub fn op_node_ecdh_generate_keys( + curve: &str, + pubbuf: &mut [u8], + privbuf: &mut [u8], +) -> Result { + let mut rng = rand::thread_rng(); + match curve { + "secp256k1" => { + let secp = Secp256k1::new(); + let (privkey, pubkey) = secp.generate_keypair(&mut rng); + pubbuf.copy_from_slice(&pubkey.serialize_uncompressed()); + privbuf.copy_from_slice(&privkey.secret_bytes()); + + Ok(0) + } + "prime256v1" | "secp256r1" => { + let privkey = elliptic_curve::SecretKey::::random(&mut rng); + let pubkey = privkey.public_key(); + pubbuf.copy_from_slice(pubkey.to_sec1_bytes().as_ref()); + privbuf.copy_from_slice(privkey.to_nonzero_scalar().to_bytes().as_ref()); + Ok(0) + } + "secp384r1" => { + let privkey = elliptic_curve::SecretKey::::random(&mut rng); + let pubkey = privkey.public_key(); + pubbuf.copy_from_slice(pubkey.to_sec1_bytes().as_ref()); + privbuf.copy_from_slice(privkey.to_nonzero_scalar().to_bytes().as_ref()); + Ok(0) + } + "secp224r1" => { + let privkey = elliptic_curve::SecretKey::::random(&mut rng); + let pubkey = privkey.public_key(); + pubbuf.copy_from_slice(pubkey.to_sec1_bytes().as_ref()); + privbuf.copy_from_slice(privkey.to_nonzero_scalar().to_bytes().as_ref()); + Ok(0) + } + &_ => todo!(), + } +} + +#[op] +pub fn op_node_ecdh_compute_secret( + curve: &str, + this_priv: Option, + their_pub: &mut [u8], + secret: &mut [u8], +) -> Result<(), AnyError> { + match curve { + "secp256k1" => { + let this_secret_key = SecretKey::from_slice( + this_priv.expect("no private key provided?").as_ref(), + ) + .unwrap(); + let their_public_key = + secp256k1::PublicKey::from_slice(their_pub).unwrap(); + let shared_secret = + SharedSecret::new(&their_public_key, &this_secret_key); + + secret.copy_from_slice(&shared_secret.secret_bytes()); + Ok(()) + } + "prime256v1" | "secp256r1" => { + let their_public_key = + elliptic_curve::PublicKey::::from_sec1_bytes(their_pub) + .expect("bad public key"); + let this_private_key = elliptic_curve::SecretKey::::from_slice( + &this_priv.expect("must supply private key"), + ) + .expect("bad private key"); + let shared_secret = elliptic_curve::ecdh::diffie_hellman( + this_private_key.to_nonzero_scalar(), + their_public_key.as_affine(), + ); + secret.copy_from_slice(shared_secret.raw_secret_bytes()); + + Ok(()) + } + "secp384r1" => { + let their_public_key = + elliptic_curve::PublicKey::::from_sec1_bytes(their_pub) + .expect("bad public key"); + let this_private_key = elliptic_curve::SecretKey::::from_slice( + &this_priv.expect("must supply private key"), + ) + .expect("bad private key"); + let shared_secret = elliptic_curve::ecdh::diffie_hellman( + this_private_key.to_nonzero_scalar(), + their_public_key.as_affine(), + ); + secret.copy_from_slice(shared_secret.raw_secret_bytes()); + + Ok(()) + } + "secp224r1" => { + let their_public_key = + elliptic_curve::PublicKey::::from_sec1_bytes(their_pub) + .expect("bad public key"); + let this_private_key = elliptic_curve::SecretKey::::from_slice( + &this_priv.expect("must supply private key"), + ) + .expect("bad private key"); + let shared_secret = elliptic_curve::ecdh::diffie_hellman( + this_private_key.to_nonzero_scalar(), + their_public_key.as_affine(), + ); + secret.copy_from_slice(shared_secret.raw_secret_bytes()); + + Ok(()) + } + &_ => todo!(), + } +} + +#[op] +pub fn op_node_ecdh_compute_public_key( + curve: &str, + privkey: &[u8], + pubkey: &mut [u8], +) -> Result<(), AnyError> { + match curve { + "secp256k1" => { + let secp = Secp256k1::new(); + let secret_key = SecretKey::from_slice(privkey).unwrap(); + let public_key = + secp256k1::PublicKey::from_secret_key(&secp, &secret_key); + + pubkey.copy_from_slice(&public_key.serialize_uncompressed()); + + Ok(()) + } + "prime256v1" | "secp256r1" => { + let this_private_key = + elliptic_curve::SecretKey::::from_slice(privkey) + .expect("bad private key"); + let public_key = this_private_key.public_key(); + pubkey.copy_from_slice(public_key.to_sec1_bytes().as_ref()); + Ok(()) + } + "secp384r1" => { + let this_private_key = + elliptic_curve::SecretKey::::from_slice(privkey) + .expect("bad private key"); + let public_key = this_private_key.public_key(); + pubkey.copy_from_slice(public_key.to_sec1_bytes().as_ref()); + Ok(()) + } + "secp224r1" => { + let this_private_key = + elliptic_curve::SecretKey::::from_slice(privkey) + .expect("bad private key"); + let public_key = this_private_key.public_key(); + pubkey.copy_from_slice(public_key.to_sec1_bytes().as_ref()); + Ok(()) + } + &_ => todo!(), + } +} + #[inline] fn gen_prime(size: usize) -> ZeroCopyBuf { primes::Prime::generate(size).0.to_bytes_be().into() diff --git a/ext/node/polyfills/internal/crypto/diffiehellman.ts b/ext/node/polyfills/internal/crypto/diffiehellman.ts index 3aa1f80809..62a802126f 100644 --- a/ext/node/polyfills/internal/crypto/diffiehellman.ts +++ b/ext/node/polyfills/internal/crypto/diffiehellman.ts @@ -13,6 +13,8 @@ import { } from "ext:deno_node/internal/validators.mjs"; import { Buffer } from "ext:deno_node/buffer.ts"; import { + EllipticCurve, + ellipticCurves, getDefaultEncoding, toBuf, } from "ext:deno_node/internal/crypto/util.ts"; @@ -24,6 +26,8 @@ import type { import { KeyObject } from "ext:deno_node/internal/crypto/keys.ts"; import type { BufferEncoding } from "ext:deno_node/_global.d.ts"; +const { ops } = Deno.core; + const DH_GENERATOR = 2; export class DiffieHellman { @@ -219,10 +223,21 @@ export class DiffieHellmanGroup { } export class ECDH { + #curve: EllipticCurve; // the selected curve + #privbuf: Buffer; // the private key + #pubbuf: Buffer; // the public key + constructor(curve: string) { validateString(curve, "curve"); - notImplemented("crypto.ECDH"); + const c = ellipticCurves.find((x) => x.name == curve); + if (c == undefined) { + throw new Error("invalid curve"); + } + + this.#curve = c; + this.#pubbuf = Buffer.alloc(this.#curve.publicKeySize); + this.#privbuf = Buffer.alloc(this.#curve.privateKeySize); } static convertKey( @@ -250,44 +265,80 @@ export class ECDH { outputEncoding: BinaryToTextEncoding, ): string; computeSecret( - _otherPublicKey: ArrayBufferView | string, + otherPublicKey: ArrayBufferView | string, _inputEncoding?: BinaryToTextEncoding, _outputEncoding?: BinaryToTextEncoding, ): Buffer | string { - notImplemented("crypto.ECDH.prototype.computeSecret"); + const secretBuf = Buffer.alloc(this.#curve.sharedSecretSize); + + ops.op_node_ecdh_compute_secret( + this.#curve.name, + this.#privbuf, + otherPublicKey, + secretBuf, + ); + + return secretBuf; } generateKeys(): Buffer; generateKeys(encoding: BinaryToTextEncoding, format?: ECDHKeyFormat): string; generateKeys( - _encoding?: BinaryToTextEncoding, + encoding?: BinaryToTextEncoding, _format?: ECDHKeyFormat, ): Buffer | string { - notImplemented("crypto.ECDH.prototype.generateKeys"); + ops.op_node_ecdh_generate_keys( + this.#curve.name, + this.#pubbuf, + this.#privbuf, + ); + + if (encoding !== undefined) { + return this.#pubbuf.toString(encoding); + } + return this.#pubbuf; } getPrivateKey(): Buffer; getPrivateKey(encoding: BinaryToTextEncoding): string; - getPrivateKey(_encoding?: BinaryToTextEncoding): Buffer | string { - notImplemented("crypto.ECDH.prototype.getPrivateKey"); + getPrivateKey(encoding?: BinaryToTextEncoding): Buffer | string { + if (encoding !== undefined) { + return this.#privbuf.toString(encoding); + } + return this.#privbuf; } getPublicKey(): Buffer; getPublicKey(encoding: BinaryToTextEncoding, format?: ECDHKeyFormat): string; getPublicKey( - _encoding?: BinaryToTextEncoding, + encoding?: BinaryToTextEncoding, _format?: ECDHKeyFormat, ): Buffer | string { - notImplemented("crypto.ECDH.prototype.getPublicKey"); + if (encoding !== undefined) { + return this.#pubbuf.toString(encoding); + } + return this.#pubbuf; } setPrivateKey(privateKey: ArrayBufferView): void; setPrivateKey(privateKey: string, encoding: BinaryToTextEncoding): void; setPrivateKey( - _privateKey: ArrayBufferView | string, - _encoding?: BinaryToTextEncoding, + privateKey: ArrayBufferView | string, + encoding?: BinaryToTextEncoding, ): Buffer | string { - notImplemented("crypto.ECDH.prototype.setPrivateKey"); + this.#privbuf = privateKey; + this.#pubbuf = Buffer.alloc(this.#curve.publicKeySize); + + ops.op_node_ecdh_compute_public_key( + this.#curve.name, + this.#privbuf, + this.#pubbuf, + ); + + if (encoding !== undefined) { + return this.#pubbuf.toString(encoding); + } + return this.#pubbuf; } } diff --git a/ext/node/polyfills/internal/crypto/util.ts b/ext/node/polyfills/internal/crypto/util.ts index ccb7726316..2e269b7fad 100644 --- a/ext/node/polyfills/internal/crypto/util.ts +++ b/ext/node/polyfills/internal/crypto/util.ts @@ -46,6 +46,47 @@ const digestAlgorithms = [ "sha1", ]; +export type EllipticCurve = { + name: string; + ephemeral: boolean; + privateKeySize: number; + publicKeySize: number; + sharedSecretSize: number; +}; + +export const ellipticCurves: Array = [ + { + name: "secp256k1", + privateKeySize: 32, + publicKeySize: 65, + sharedSecretSize: 32, + }, // Weierstrass-class EC used by Bitcoin + { + name: "prime256v1", + privateKeySize: 32, + publicKeySize: 65, + sharedSecretSize: 32, + }, // NIST P-256 EC + { + name: "secp256r1", + privateKeySize: 32, + publicKeySize: 65, + sharedSecretSize: 32, + }, // NIST P-256 EC (same as above) + { + name: "secp384r1", + privateKeySize: 48, + publicKeySize: 97, + sharedSecretSize: 48, + }, // NIST P-384 EC + { + name: "secp224r1", + privateKeySize: 28, + publicKeySize: 57, + sharedSecretSize: 28, + }, // NIST P-224 EC +]; + // deno-fmt-ignore const supportedCiphers = [ "aes-128-ecb", "aes-192-ecb", @@ -114,8 +155,9 @@ export function getHashes(): readonly string[] { return digestAlgorithms; } +const curveNames = ellipticCurves.map((x) => x.name); export function getCurves(): readonly string[] { - notImplemented("crypto.getCurves"); + return curveNames; } export interface SecureHeapUsage {