summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
Diffstat (limited to 'ext')
-rw-r--r--ext/node/Cargo.toml5
-rw-r--r--ext/node/lib.rs4
-rw-r--r--ext/node/ops/crypto/mod.rs165
-rw-r--r--ext/node/polyfills/internal/crypto/diffiehellman.ts75
-rw-r--r--ext/node/polyfills/internal/crypto/util.ts44
5 files changed, 279 insertions, 14 deletions
diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml
index 576e62d55..14928db30 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 cef92328d..b5db83297 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<Env>,
ops::require::op_require_proxy_path,
diff --git a/ext/node/ops/crypto/mod.rs b/ext/node/ops/crypto/mod.rs
index 92e3029e0..9e1a3da98 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<ResourceId, AnyError> {
+ 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::<NistP256>::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::<NistP384>::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::<NistP224>::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<ZeroCopyBuf>,
+ 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::<NistP256>::from_sec1_bytes(their_pub)
+ .expect("bad public key");
+ let this_private_key = elliptic_curve::SecretKey::<NistP256>::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::<NistP384>::from_sec1_bytes(their_pub)
+ .expect("bad public key");
+ let this_private_key = elliptic_curve::SecretKey::<NistP384>::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::<NistP224>::from_sec1_bytes(their_pub)
+ .expect("bad public key");
+ let this_private_key = elliptic_curve::SecretKey::<NistP224>::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::<NistP256>::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::<NistP384>::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::<NistP224>::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 3aa1f8080..62a802126 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 ccb772631..2e269b7fa 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<EllipticCurve> = [
+ {
+ 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 {