From b5b21341d768e22b7c72157644b60aef83a73d35 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Mon, 10 Apr 2023 21:44:33 +0530 Subject: [PATCH 01/14] feat(ext/node): implement asymmetric keygen --- ext/node/crypto/mod.rs | 36 ++++ ext/node/polyfills/internal/crypto/keygen.ts | 212 ++++++++++++++++++- 2 files changed, 244 insertions(+), 4 deletions(-) diff --git a/ext/node/crypto/mod.rs b/ext/node/crypto/mod.rs index adacdf6d68e976..f7bf62989ccc11 100644 --- a/ext/node/crypto/mod.rs +++ b/ext/node/crypto/mod.rs @@ -9,6 +9,7 @@ use deno_core::StringOrBuffer; use deno_core::ZeroCopyBuf; use hkdf::Hkdf; use num_bigint::BigInt; +use num_traits::FromPrimitive; use rand::Rng; use std::future::Future; use std::rc::Rc; @@ -477,3 +478,38 @@ pub async fn op_node_hkdf_async( }) .await? } + +use rsa::pkcs1::EncodeRsaPrivateKey; +use rsa::pkcs1::EncodeRsaPublicKey; + +// For RSA-PSS, see `generate_rsa_pss()` +fn generate_rsa( + modulus_length: usize, + public_exponent: usize, +) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + let mut rng = rand::thread_rng(); + let private_key = + RsaPrivateKey::new_with_exp(&mut rng, modulus_length, &rsa::BigUint::from_usize(public_exponent).unwrap())?; + let public_key = private_key.to_public_key(); + let private_key_der = private_key.to_pkcs1_der()?.as_bytes().to_vec(); + let public_key_der = public_key.to_pkcs1_der()?.to_vec(); + + Ok((private_key_der.into(), public_key_der.into())) +} + +#[op] +pub fn op_node_generate_rsa( + modulus_length: usize, + public_exponent: usize, +) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + generate_rsa(modulus_length, public_exponent) +} + +#[op] +pub async fn op_node_generate_rsa_async( + modulus_length: usize, + public_exponent: usize, +) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + tokio::task::spawn_blocking(move || generate_rsa(modulus_length, public_exponent)) + .await? +} \ No newline at end of file diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts index b490cedd79f1fd..f4a8777b0fe570 100644 --- a/ext/node/polyfills/internal/crypto/keygen.ts +++ b/ext/node/polyfills/internal/crypto/keygen.ts @@ -8,13 +8,20 @@ import { setOwnedKey, } from "ext:deno_node/internal/crypto/keys.ts"; import { notImplemented } from "ext:deno_node/_utils.ts"; -import { ERR_INVALID_ARG_VALUE } from "ext:deno_node/internal/errors.ts"; import { + ERR_INCOMPATIBLE_OPTION_PAIR, + ERR_INVALID_ARG_VALUE, + ERR_MISSING_OPTION, +} from "ext:deno_node/internal/errors.ts"; +import { + validateBuffer, validateFunction, + validateInt32, validateInteger, validateObject, validateOneOf, validateString, + validateUint32, } from "ext:deno_node/internal/validators.mjs"; import { Buffer } from "ext:deno_node/buffer.ts"; import { KeyFormat, KeyType } from "ext:deno_node/internal/crypto/types.ts"; @@ -716,12 +723,209 @@ export function generateKeyPairSync( options?: X448KeyPairKeyObjectOptions, ): KeyPairKeyObjectResult; export function generateKeyPairSync( - _type: KeyType, - _options: unknown, + type: KeyType, + options: unknown, ): | KeyPairKeyObjectResult | KeyPairSyncResult { - notImplemented("crypto.generateKeyPairSync"); + return createJob(kSync, type, options); +} + +const kSync = 0; +const kAsync = 1; + +function createJob(mode, type, options) { + validateString(type, "type"); + + if (options !== undefined) { + validateObject(options, "options"); + } + + switch (type) { + case "rsa": + case "rsa-pss": { + validateObject(options, "options"); + const { modulusLength } = options; + validateUint32(modulusLength, "options.modulusLength"); + + let { publicExponent } = options; + if (publicExponent == null) { + publicExponent = 0x10001; + } else { + validateUint32(publicExponent, "options.publicExponent"); + } + + if (type === "rsa") { + if (mode === kSync) { + return ops.op_node_generate_rsa( + modulusLength, + publicExponent, + ); + } else { + return core.opAsync("op_node_generate_rsa_async", modulusLength, publicExponent); + } + } + + const { + hash, + mgf1Hash, + hashAlgorithm, + mgf1HashAlgorithm, + saltLength, + } = options; + + if (saltLength !== undefined) { + validateInt32(saltLength, "options.saltLength", 0); + } + if (hashAlgorithm !== undefined) { + validateString(hashAlgorithm, "options.hashAlgorithm"); + } + if (mgf1HashAlgorithm !== undefined) { + validateString(mgf1HashAlgorithm, "options.mgf1HashAlgorithm"); + } + if (hash !== undefined) { + process.emitWarning( + '"options.hash" is deprecated, ' + + 'use "options.hashAlgorithm" instead.', + "DeprecationWarning", + "DEP0154", + ); + validateString(hash, "options.hash"); + if (hashAlgorithm && hash !== hashAlgorithm) { + throw new ERR_INVALID_ARG_VALUE("options.hash", hash); + } + } + if (mgf1Hash !== undefined) { + process.emitWarning( + '"options.mgf1Hash" is deprecated, ' + + 'use "options.mgf1HashAlgorithm" instead.', + "DeprecationWarning", + "DEP0154", + ); + validateString(mgf1Hash, "options.mgf1Hash"); + if (mgf1HashAlgorithm && mgf1Hash !== mgf1HashAlgorithm) { + throw new ERR_INVALID_ARG_VALUE("options.mgf1Hash", mgf1Hash); + } + } + + return new RsaKeyPairGenJob( + mode, + kKeyVariantRSA_PSS, + modulusLength, + publicExponent, + hashAlgorithm || hash, + mgf1HashAlgorithm || mgf1Hash, + saltLength, + ...encoding, + ); + } + case "dsa": { + validateObject(options, "options"); + const { modulusLength } = options; + validateUint32(modulusLength, "options.modulusLength"); + + let { divisorLength } = options; + if (divisorLength == null) { + divisorLength = -1; + } else { + validateInt32(divisorLength, "options.divisorLength", 0); + } + + return new DsaKeyPairGenJob( + mode, + modulusLength, + divisorLength, + ...encoding, + ); + } + case "ec": { + validateObject(options, "options"); + const { namedCurve } = options; + validateString(namedCurve, "options.namedCurve"); + let { paramEncoding } = options; + if (paramEncoding == null || paramEncoding === "named") { + paramEncoding = OPENSSL_EC_NAMED_CURVE; + } else if (paramEncoding === "explicit") { + paramEncoding = OPENSSL_EC_EXPLICIT_CURVE; + } else { + throw new ERR_INVALID_ARG_VALUE("options.paramEncoding", paramEncoding); + } + + return new EcKeyPairGenJob( + mode, + namedCurve, + paramEncoding, + ...encoding, + ); + } + case "ed25519": + case "ed448": + case "x25519": + case "x448": { + let id; + switch (type) { + case "ed25519": + id = EVP_PKEY_ED25519; + break; + case "ed448": + id = EVP_PKEY_ED448; + break; + case "x25519": + id = EVP_PKEY_X25519; + break; + case "x448": + id = EVP_PKEY_X448; + break; + } + return new NidKeyPairGenJob(mode, id, ...encoding); + } + case "dh": { + validateObject(options, "options"); + const { group, primeLength, prime, generator } = options; + if (group != null) { + if (prime != null) { + throw new ERR_INCOMPATIBLE_OPTION_PAIR("group", "prime"); + } + if (primeLength != null) { + throw new ERR_INCOMPATIBLE_OPTION_PAIR("group", "primeLength"); + } + if (generator != null) { + throw new ERR_INCOMPATIBLE_OPTION_PAIR("group", "generator"); + } + + validateString(group, "options.group"); + + return new DhKeyPairGenJob(mode, group, ...encoding); + } + + if (prime != null) { + if (primeLength != null) { + throw new ERR_INCOMPATIBLE_OPTION_PAIR("prime", "primeLength"); + } + + validateBuffer(prime, "options.prime"); + } else if (primeLength != null) { + validateInt32(primeLength, "options.primeLength", 0); + } else { + throw new ERR_MISSING_OPTION( + "At least one of the group, prime, or primeLength options", + ); + } + + if (generator != null) { + validateInt32(generator, "options.generator", 0); + } + return new DhKeyPairGenJob( + mode, + prime != null ? prime : primeLength, + generator == null ? 2 : generator, + ...encoding, + ); + } + default: + // Fall through + } + throw new ERR_INVALID_ARG_VALUE("type", type, "must be a supported key type"); } export default { From f02383bfe78183f117a30245046bae1cf966d882 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Wed, 12 Apr 2023 14:11:30 +0530 Subject: [PATCH 02/14] dsa --- Cargo.lock | 103 +++++++++++++++---- ext/node/Cargo.toml | 1 + ext/node/crypto/mod.rs | 70 +++++++++++-- ext/node/polyfills/internal/crypto/keygen.ts | 12 +-- 4 files changed, 155 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0498b9d2d8833d..8bb0296aca6bd9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -887,8 +887,8 @@ dependencies = [ "serde_bytes", "sha1", "sha2", - "signature", - "spki", + "signature 1.6.4", + "spki 0.6.0", "tokio", "uuid", "x25519-dalek", @@ -1110,6 +1110,7 @@ dependencies = [ "cbc", "deno_core", "digest 0.10.6", + "dsa", "ecb", "hex", "hkdf", @@ -1132,7 +1133,7 @@ dependencies = [ "sha-1 0.10.0", "sha2", "sha3", - "signature", + "signature 1.6.4", "tokio", "typenum", ] @@ -1339,6 +1340,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b10af9f9f9f2134a42d3f8aa74658660f2e0234b0eb81bd171df8aa32779ed" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -1493,6 +1504,22 @@ dependencies = [ "text_lines", ] +[[package]] +name = "dsa" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5638f6d17447bc0ffc46354949ee366847e83450e2a07895862942085cc9761" +dependencies = [ + "digest 0.10.6", + "num-bigint-dig", + "num-traits", + "pkcs8 0.10.2", + "rfc6979 0.4.0", + "sha2", + "signature 2.1.0", + "zeroize", +] + [[package]] name = "dyn-clone" version = "1.0.11" @@ -1540,10 +1567,10 @@ version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ - "der", + "der 0.6.1", "elliptic-curve", - "rfc6979", - "signature", + "rfc6979 0.3.1", + "signature 1.6.4", ] [[package]] @@ -1560,14 +1587,14 @@ checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ "base16ct", "crypto-bigint", - "der", + "der 0.6.1", "digest 0.10.6", "ff", "generic-array 0.14.6", "group", "hkdf", "pem-rfc7468", - "pkcs8", + "pkcs8 0.9.0", "rand_core 0.6.4", "sec1", "subtle", @@ -3176,9 +3203,9 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eff33bdbdfc54cc98a2eca766ebdec3e1b8fb7387523d5c9c9a2891da856f719" dependencies = [ - "der", - "pkcs8", - "spki", + "der 0.6.1", + "pkcs8 0.9.0", + "spki 0.6.0", "zeroize", ] @@ -3188,8 +3215,18 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ - "der", - "spki", + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.3", + "spki 0.7.1", ] [[package]] @@ -3518,6 +3555,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "ring" version = "0.16.20" @@ -3555,9 +3602,9 @@ dependencies = [ "num-iter", "num-traits", "pkcs1", - "pkcs8", + "pkcs8 0.9.0", "rand_core 0.6.4", - "signature", + "signature 1.6.4", "smallvec", "subtle", "zeroize", @@ -3740,9 +3787,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ "base16ct", - "der", + "der 0.6.1", "generic-array 0.14.6", - "pkcs8", + "pkcs8 0.9.0", "subtle", "zeroize", ] @@ -3961,6 +4008,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.6", + "rand_core 0.6.4", +] + [[package]] name = "simdutf8" version = "0.1.4" @@ -4037,7 +4094,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", - "der", + "der 0.6.1", +] + +[[package]] +name = "spki" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a5be806ab6f127c3da44b7378837ebf01dadca8510a0e572460216b228bd0e" +dependencies = [ + "base64ct", + "der 0.7.3", ] [[package]] diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index e74cf380535352..40f9975b208caa 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -43,3 +43,4 @@ sha3 = "0.10.5" signature.workspace = true tokio.workspace = true typenum = "1.15.0" +dsa = "0.6.1" diff --git a/ext/node/crypto/mod.rs b/ext/node/crypto/mod.rs index f7bf62989ccc11..b6c0cd082957de 100644 --- a/ext/node/crypto/mod.rs +++ b/ext/node/crypto/mod.rs @@ -482,14 +482,16 @@ pub async fn op_node_hkdf_async( use rsa::pkcs1::EncodeRsaPrivateKey; use rsa::pkcs1::EncodeRsaPublicKey; -// For RSA-PSS, see `generate_rsa_pss()` fn generate_rsa( modulus_length: usize, public_exponent: usize, ) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { let mut rng = rand::thread_rng(); - let private_key = - RsaPrivateKey::new_with_exp(&mut rng, modulus_length, &rsa::BigUint::from_usize(public_exponent).unwrap())?; + let private_key = RsaPrivateKey::new_with_exp( + &mut rng, + modulus_length, + &rsa::BigUint::from_usize(public_exponent).unwrap(), + )?; let public_key = private_key.to_public_key(); let private_key_der = private_key.to_pkcs1_der()?.as_bytes().to_vec(); let public_key_der = public_key.to_pkcs1_der()?.to_vec(); @@ -510,6 +512,62 @@ pub async fn op_node_generate_rsa_async( modulus_length: usize, public_exponent: usize, ) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { - tokio::task::spawn_blocking(move || generate_rsa(modulus_length, public_exponent)) - .await? -} \ No newline at end of file + tokio::task::spawn_blocking(move || { + generate_rsa(modulus_length, public_exponent) + }) + .await? +} + +fn dsa_generate( + modulus_length: usize, + divisor_length: usize, +) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + let mut rng = rand::thread_rng(); + use dsa::pkcs8::EncodePrivateKey; + use dsa::pkcs8::EncodePublicKey; + use dsa::{Components, KeySize, SigningKey}; + + let key_size = match (modulus_length, divisor_length) { + (1024, 160) => KeySize::DSA_1024_160, + (2048, 224) => KeySize::DSA_2048_224, + (2048, 256) => KeySize::DSA_2048_256, + (3072, 256) => KeySize::DSA_3072_256, + _ => return Err(type_error("Invalid modulus_length or divisor_length")), + }; + let components = Components::generate(&mut rng, key_size); + let signing_key = SigningKey::generate(&mut rng, components); + let verifying_key = signing_key.verifying_key(); + + Ok(( + signing_key + .to_pkcs8_der() + .map_err(|_| type_error(""))? + .as_bytes() + .to_vec() + .into(), + verifying_key + .to_public_key_der() + .map_err(|_| type_error(""))? + .to_vec() + .into(), + )) +} + +#[op] +pub fn op_node_dsa_generate( + modulus_length: usize, + divisor_length: usize, +) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + dsa_generate(modulus_length, divisor_length) +} + +#[op] +pub async fn op_node_dsa_generate_async( + modulus_length: usize, + divisor_length: usize, +) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + tokio::task::spawn_blocking(move || { + dsa_generate(modulus_length, divisor_length) + }) + .await? +} diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts index f4a8777b0fe570..3a2f66cf20e6d2 100644 --- a/ext/node/polyfills/internal/crypto/keygen.ts +++ b/ext/node/polyfills/internal/crypto/keygen.ts @@ -830,13 +830,11 @@ function createJob(mode, type, options) { } else { validateInt32(divisorLength, "options.divisorLength", 0); } - - return new DsaKeyPairGenJob( - mode, - modulusLength, - divisorLength, - ...encoding, - ); + + if (mode === "sync") { + return ops.op_node_generate_dsa(modulusLength, divisorLength); + } + return core.opAsync("op_node_generate_dsa_async", modulusLength, divisorLength); } case "ec": { validateObject(options, "options"); From d3438d5ea9c76020902aa286580418ae21d05b05 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Wed, 12 Apr 2023 19:14:48 +0530 Subject: [PATCH 03/14] ECC --- Cargo.lock | 1 + ext/node/Cargo.toml | 3 +- ext/node/crypto/mod.rs | 43 +++++++++++++++++++- ext/node/polyfills/internal/crypto/keygen.ts | 34 ++++++++++------ 4 files changed, 66 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8bb0296aca6bd9..732988b6cb307f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1127,6 +1127,7 @@ dependencies = [ "pbkdf2", "rand", "regex", + "ring", "ripemd", "rsa", "serde", diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 40f9975b208caa..2570e40ed3b27a 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -18,6 +18,7 @@ aes.workspace = true cbc.workspace = true deno_core.workspace = true digest = { version = "0.10.5", features = ["core-api", "std"] } +dsa = "0.6.1" ecb.workspace = true hex.workspace = true hkdf.workspace = true @@ -34,6 +35,7 @@ path-clean = "=0.1.0" pbkdf2 = "0.12.1" rand.workspace = true regex.workspace = true +ring.workspace = true ripemd = "0.1.3" rsa.workspace = true serde = "1.0.149" @@ -43,4 +45,3 @@ sha3 = "0.10.5" signature.workspace = true tokio.workspace = true typenum = "1.15.0" -dsa = "0.6.1" diff --git a/ext/node/crypto/mod.rs b/ext/node/crypto/mod.rs index b6c0cd082957de..0c2d3eac94e757 100644 --- a/ext/node/crypto/mod.rs +++ b/ext/node/crypto/mod.rs @@ -525,7 +525,9 @@ fn dsa_generate( let mut rng = rand::thread_rng(); use dsa::pkcs8::EncodePrivateKey; use dsa::pkcs8::EncodePublicKey; - use dsa::{Components, KeySize, SigningKey}; + use dsa::Components; + use dsa::KeySize; + use dsa::SigningKey; let key_size = match (modulus_length, divisor_length) { (1024, 160) => KeySize::DSA_1024_160, @@ -571,3 +573,42 @@ pub async fn op_node_dsa_generate_async( }) .await? } + +fn ec_generate( + named_curve: &str, +) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + use ring::signature::EcdsaKeyPair; + use ring::signature::KeyPair; + + let curve = match named_curve { + "p256" => &ring::signature::ECDSA_P256_SHA256_FIXED_SIGNING, + "p384" => &ring::signature::ECDSA_P384_SHA384_FIXED_SIGNING, + _ => return Err(type_error("Unsupported named curve")), + }; + + let rng = ring::rand::SystemRandom::new(); + + let pkcs8 = EcdsaKeyPair::generate_pkcs8(curve, &rng) + .map_err(|_| type_error("Failed to generate EC key"))?; + + let public_key = EcdsaKeyPair::from_pkcs8(curve, pkcs8.as_ref()) + .map_err(|_| type_error("Failed to generate EC key"))? + .public_key() + .as_ref() + .to_vec(); + Ok((pkcs8.as_ref().to_vec().into(), public_key.into())) +} + +#[op] +pub fn op_node_ec_generate( + named_curve: &str, +) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + ec_generate(named_curve) +} + +#[op] +pub async fn op_node_ec_generate_async( + named_curve: String, +) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + tokio::task::spawn_blocking(move || ec_generate(&named_curve)).await? +} diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts index 3a2f66cf20e6d2..a76c622467296e 100644 --- a/ext/node/polyfills/internal/crypto/keygen.ts +++ b/ext/node/polyfills/internal/crypto/keygen.ts @@ -728,7 +728,7 @@ export function generateKeyPairSync( ): | KeyPairKeyObjectResult | KeyPairSyncResult { - return createJob(kSync, type, options); + const [privateKey, publicKey] = createJob(kSync, type, options); } const kSync = 0; @@ -762,7 +762,11 @@ function createJob(mode, type, options) { publicExponent, ); } else { - return core.opAsync("op_node_generate_rsa_async", modulusLength, publicExponent); + return core.opAsync( + "op_node_generate_rsa_async", + modulusLength, + publicExponent, + ); } } @@ -830,11 +834,15 @@ function createJob(mode, type, options) { } else { validateInt32(divisorLength, "options.divisorLength", 0); } - - if (mode === "sync") { + + if (mode === kSync) { return ops.op_node_generate_dsa(modulusLength, divisorLength); } - return core.opAsync("op_node_generate_dsa_async", modulusLength, divisorLength); + return core.opAsync( + "op_node_generate_dsa_async", + modulusLength, + divisorLength, + ); } case "ec": { validateObject(options, "options"); @@ -842,19 +850,19 @@ function createJob(mode, type, options) { validateString(namedCurve, "options.namedCurve"); let { paramEncoding } = options; if (paramEncoding == null || paramEncoding === "named") { - paramEncoding = OPENSSL_EC_NAMED_CURVE; + // pass. } else if (paramEncoding === "explicit") { - paramEncoding = OPENSSL_EC_EXPLICIT_CURVE; + // TODO(@littledivy): Explicit param encoding is very rarely used, and not supported by the ring crate. + throw new TypeError("Explicit encoding is not supported"); } else { throw new ERR_INVALID_ARG_VALUE("options.paramEncoding", paramEncoding); } - return new EcKeyPairGenJob( - mode, - namedCurve, - paramEncoding, - ...encoding, - ); + if (mode === kSync) { + return ops.op_node_generate_ec(namedCurve); + } else { + return core.opAsync("op_node_generate_ec_async", namedCurve); + } } case "ed25519": case "ed448": From af4b98d9127e783e0db439684ab4fb69b7fb0311 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 13 Apr 2023 17:46:37 +0530 Subject: [PATCH 04/14] x --- ext/node/crypto/mod.rs | 1 + ext/node/polyfills/internal/crypto/keygen.ts | 17 +---------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/ext/node/crypto/mod.rs b/ext/node/crypto/mod.rs index 0c2d3eac94e757..2ab85c3a0391ba 100644 --- a/ext/node/crypto/mod.rs +++ b/ext/node/crypto/mod.rs @@ -612,3 +612,4 @@ pub async fn op_node_ec_generate_async( ) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { tokio::task::spawn_blocking(move || ec_generate(&named_curve)).await? } + diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts index a76c622467296e..af6e7f1db64a2f 100644 --- a/ext/node/polyfills/internal/crypto/keygen.ts +++ b/ext/node/polyfills/internal/crypto/keygen.ts @@ -868,22 +868,7 @@ function createJob(mode, type, options) { case "ed448": case "x25519": case "x448": { - let id; - switch (type) { - case "ed25519": - id = EVP_PKEY_ED25519; - break; - case "ed448": - id = EVP_PKEY_ED448; - break; - case "x25519": - id = EVP_PKEY_X25519; - break; - case "x448": - id = EVP_PKEY_X448; - break; - } - return new NidKeyPairGenJob(mode, id, ...encoding); + unimplemented(type); } case "dh": { validateObject(options, "options"); From f0f4b33460c276808dc67c60eec1191cf2060828 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 13 Apr 2023 23:08:21 +0530 Subject: [PATCH 05/14] ed25519 and x25519 --- Cargo.lock | 1 + ext/node/Cargo.toml | 2 + ext/node/crypto/mod.rs | 67 +++++++++++++++++++- ext/node/lib.rs | 10 +++ ext/node/polyfills/internal/crypto/keygen.ts | 52 ++++++++++----- 5 files changed, 115 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 732988b6cb307f..9343a2301036ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1137,6 +1137,7 @@ dependencies = [ "signature 1.6.4", "tokio", "typenum", + "x25519-dalek", ] [[package]] diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 2570e40ed3b27a..0cbecb99d62c65 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -45,3 +45,5 @@ sha3 = "0.10.5" signature.workspace = true tokio.workspace = true typenum = "1.15.0" +# https://github.com/dalek-cryptography/x25519-dalek/pull/89 +x25519-dalek = "2.0.0-pre.1" diff --git a/ext/node/crypto/mod.rs b/ext/node/crypto/mod.rs index 2ab85c3a0391ba..da25b8eeb69076 100644 --- a/ext/node/crypto/mod.rs +++ b/ext/node/crypto/mod.rs @@ -10,6 +10,7 @@ use deno_core::ZeroCopyBuf; use hkdf::Hkdf; use num_bigint::BigInt; use num_traits::FromPrimitive; +use rand::thread_rng; use rand::Rng; use std::future::Future; use std::rc::Rc; @@ -530,6 +531,7 @@ fn dsa_generate( use dsa::SigningKey; let key_size = match (modulus_length, divisor_length) { + #[allow(deprecated)] (1024, 160) => KeySize::DSA_1024_160, (2048, 224) => KeySize::DSA_2048_224, (2048, 256) => KeySize::DSA_2048_256, @@ -543,13 +545,13 @@ fn dsa_generate( Ok(( signing_key .to_pkcs8_der() - .map_err(|_| type_error(""))? + .map_err(|_| type_error("Not valid pkcs8"))? .as_bytes() .to_vec() .into(), verifying_key .to_public_key_der() - .map_err(|_| type_error(""))? + .map_err(|_| type_error("Not valid spki"))? .to_vec() .into(), )) @@ -613,3 +615,64 @@ pub async fn op_node_ec_generate_async( tokio::task::spawn_blocking(move || ec_generate(&named_curve)).await? } +fn ed25519_generate() -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + use ring::signature::Ed25519KeyPair; + use ring::signature::KeyPair; + + let mut rng = thread_rng(); + let mut seed = vec![0u8; 32]; + rng.fill(seed.as_mut_slice()); + + let pair = Ed25519KeyPair::from_seed_unchecked(&seed) + .map_err(|_| type_error("Failed to generate Ed25519 key"))?; + + let public_key = pair.public_key().as_ref().to_vec(); + Ok((seed.into(), public_key.into())) +} + +#[op] +pub fn op_node_ed25519_generate() -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> +{ + ed25519_generate() +} + +#[op] +pub async fn op_node_ed25519_generate_async( +) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + tokio::task::spawn_blocking(ed25519_generate).await? +} + +fn x25519_generate() -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + // u-coordinate of the base point. + const X25519_BASEPOINT_BYTES: [u8; 32] = [ + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ]; + + let mut pkey = [0; 32]; + + let mut rng = thread_rng(); + rng.fill(pkey.as_mut_slice()); + + let pkey_copy = pkey.to_vec(); + // https://www.rfc-editor.org/rfc/rfc7748#section-6.1 + // pubkey = x25519(a, 9) which is constant-time Montgomery ladder. + // https://eprint.iacr.org/2014/140.pdf page 4 + // https://eprint.iacr.org/2017/212.pdf algorithm 8 + // pubkey is in LE order. + let pubkey = x25519_dalek::x25519(pkey, X25519_BASEPOINT_BYTES); + + Ok((pkey_copy.into(), pubkey.to_vec().into())) +} + +#[op] +pub fn op_node_x25519_generate() -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> +{ + x25519_generate() +} + +#[op] +pub async fn op_node_x25519_generate_async( +) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + tokio::task::spawn_blocking(x25519_generate).await? +} diff --git a/ext/node/lib.rs b/ext/node/lib.rs index bf947f5e8dcf30..3cb62e26163e4d 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -194,6 +194,16 @@ deno_core::extension!(deno_node, crypto::op_node_generate_secret, crypto::op_node_generate_secret_async, crypto::op_node_sign, + crypto::op_node_generate_rsa, + crypto::op_node_generate_rsa_async, + crypto::op_node_dsa_generate, + crypto::op_node_dsa_generate_async, + crypto::op_node_ec_generate, + crypto::op_node_ec_generate_async, + crypto::op_node_ed25519_generate, + crypto::op_node_ed25519_generate_async, + crypto::op_node_x25519_generate, + crypto::op_node_x25519_generate_async, winerror::op_node_sys_to_uv_error, v8::op_v8_cached_data_version_tag, v8::op_v8_get_heap_statistics, diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts index af6e7f1db64a2f..e1d4cc55c7ddad 100644 --- a/ext/node/polyfills/internal/crypto/keygen.ts +++ b/ext/node/polyfills/internal/crypto/keygen.ts @@ -728,7 +728,24 @@ export function generateKeyPairSync( ): | KeyPairKeyObjectResult | KeyPairSyncResult { - const [privateKey, publicKey] = createJob(kSync, type, options); + let [privateKey, publicKey] = createJob(kSync, type, options); + + privateKey = new KeyObject("private", setOwnedKey(privateKey)); + publicKey = new KeyObject("public", setOwnedKey(publicKey)); + + if (typeof options === "object" && options !== null) { + const { publicKeyEncoding, privateKeyEncoding } = options as any; + + if (publicKeyEncoding) { + publicKey = publicKey.export(publicKeyEncoding); + } + + if (privateKeyEncoding) { + privateKey = privateKey.export(privateKeyEncoding); + } + } + + return { publicKey, privateKey }; } const kSync = 0; @@ -836,10 +853,10 @@ function createJob(mode, type, options) { } if (mode === kSync) { - return ops.op_node_generate_dsa(modulusLength, divisorLength); + return ops.op_node_dsa_generate(modulusLength, divisorLength); } return core.opAsync( - "op_node_generate_dsa_async", + "op_node_dsa_generate_async", modulusLength, divisorLength, ); @@ -859,16 +876,26 @@ function createJob(mode, type, options) { } if (mode === kSync) { - return ops.op_node_generate_ec(namedCurve); + return ops.op_node_ec_generate(namedCurve); } else { - return core.opAsync("op_node_generate_ec_async", namedCurve); + return core.opAsync("op_node_ec_generate_async", namedCurve); } } - case "ed25519": + case "ed25519": { + if (mode === kSync) { + return ops.op_node_ed25519_generate(); + } + return core.opAsync("op_node_ed25519_generate_async"); + } + case "x25519": { + if (mode === kSync) { + return ops.op_node_x25519_generate(); + } + return core.opAsync("op_node_x25519_generate_async"); + } case "ed448": - case "x25519": case "x448": { - unimplemented(type); + notImplemented(type); } case "dh": { validateObject(options, "options"); @@ -886,7 +913,7 @@ function createJob(mode, type, options) { validateString(group, "options.group"); - return new DhKeyPairGenJob(mode, group, ...encoding); + notImplemented("DH"); } if (prime != null) { @@ -906,12 +933,7 @@ function createJob(mode, type, options) { if (generator != null) { validateInt32(generator, "options.generator", 0); } - return new DhKeyPairGenJob( - mode, - prime != null ? prime : primeLength, - generator == null ? 2 : generator, - ...encoding, - ); + notImplemented("DH"); } default: // Fall through From be5cd47470592a7578a046f3c068dbbc9a558e49 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Fri, 14 Apr 2023 10:52:14 +0530 Subject: [PATCH 06/14] dh --- ext/node/crypto/dh.rs | 275 +++++++++++++++++++++++++++++++++++++++++ ext/node/crypto/mod.rs | 30 +++++ ext/node/lib.rs | 2 + 3 files changed, 307 insertions(+) create mode 100644 ext/node/crypto/dh.rs diff --git a/ext/node/crypto/dh.rs b/ext/node/crypto/dh.rs new file mode 100644 index 00000000000000..f2ae658e02fb12 --- /dev/null +++ b/ext/node/crypto/dh.rs @@ -0,0 +1,275 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +use num_bigint::BigUint; +use num_traits::FromPrimitive; +use rand::Rng; + +pub struct PublicKey(BigUint); + +impl PublicKey { + pub fn into_vec(self) -> Vec { + self.0.to_bytes_be() + } +} + +pub struct PrivateKey(BigUint); + +impl PrivateKey { + pub fn new(exponent_size: usize) -> Self { + let mut rng = rand::thread_rng(); + let exponent = BigUint::from_bytes_be(&rng.gen::<[u8; 32]>()); + Self(exponent) + } + + /// Diffie-Hellman modular exponentiation. + /// s = g^x mod p + pub fn compute_public_key( + &self, + generator: &BigUint, + modulus: &BigUint, + ) -> PublicKey { + let public_key = generator.modpow(&self.0, modulus); + PublicKey(public_key) + } + + pub fn into_vec(self) -> Vec { + self.0.to_bytes_be() + } +} + +/// Classic DH +pub struct DiffieHellman { + pub private_key: PrivateKey, + pub public_key: PublicKey, +} + +impl DiffieHellman { + pub fn group() -> Self + where + G: DiffieHellmanGroup, + { + let private_key = PrivateKey::new(G::EXPONENT_SIZE); + + let generator = BigUint::from_usize(G::GENERATOR).unwrap(); + let modulus = BigUint::from_slice(G::MODULUS); + + let public_key = private_key.compute_public_key(&generator, &modulus); + + Self { + private_key, + public_key, + } + } +} + +/// Well-known modp groups +// +/// More Modular Exponential (MODP) Diffie-Hellman groups for Internet Key Exchange (IKE) +/// https://www.rfc-editor.org/rfc/rfc3526 +/// +/// Insecure groups `modp1` and `modp2` from https://www.rfc-editor.org/rfc/rfc2409.txt +/// are deprecated in Node.js. We don't support them. +pub trait DiffieHellmanGroup { + const GENERATOR: usize; + const MODULUS: &'static [u32]; + /// Size of the exponent in bytes + const EXPONENT_SIZE: usize; +} + +// 1536-bit MODP Group +// https://www.rfc-editor.org/rfc/rfc3526#section-2 +pub struct Modp1536; +impl DiffieHellmanGroup for Modp1536 { + const GENERATOR: usize = 2; + const EXPONENT_SIZE: usize = 192; + + const MODULUS: &'static [u32] = &[ + 0xFFFFFFFF, 0xFFFFFFFF, 0xC90FDAA2, 0x2168C234, 0xC4C6628B, 0x80DC1CD1, + 0x29024E08, 0x8A67CC74, 0x020BBEA6, 0x3B139B22, 0x514A0879, 0x8E3404DD, + 0xEF9519B3, 0xCD3A431B, 0x302B0A6D, 0xF25F1437, 0x4FE1356D, 0x6D51C245, + 0xE485B576, 0x625E7EC6, 0xF44C42E9, 0xA637ED6B, 0x0BFF5CB6, 0xF406B7ED, + 0xEE386BFB, 0x5A899FA5, 0xAE9F2411, 0x7C4B1FE6, 0x49286651, 0xECE45B3D, + 0xC2007CB8, 0xA163BF05, 0x98DA4836, 0x1C55D39A, 0x69163FA8, 0xFD24CF5F, + 0x83655D23, 0xDCA3AD96, 0x1C62F356, 0x208552BB, 0x9ED52907, 0x7096966D, + 0x670C354E, 0x4ABC9804, 0xF1746C08, 0xCA237327, 0xFFFFFFFF, 0xFFFFFFFF, + ]; +} + +// 2048-bit MODP Group +// https://www.rfc-editor.org/rfc/rfc3526#section-3 +pub struct Modp2048; +impl DiffieHellmanGroup for Modp2048 { + const GENERATOR: usize = 2; + const EXPONENT_SIZE: usize = 256; + + const MODULUS: &'static [u32] = &[ + 0xFFFFFFFF, 0xFFFFFFFF, 0xC90FDAA2, 0x2168C234, 0xC4C6628B, 0x80DC1CD1, + 0x29024E08, 0x8A67CC74, 0x020BBEA6, 0x3B139B22, 0x514A0879, 0x8E3404DD, + 0xEF9519B3, 0xCD3A431B, 0x302B0A6D, 0xF25F1437, 0x4FE1356D, 0x6D51C245, + 0xE485B576, 0x625E7EC6, 0xF44C42E9, 0xA637ED6B, 0x0BFF5CB6, 0xF406B7ED, + 0xEE386BFB, 0x5A899FA5, 0xAE9F2411, 0x7C4B1FE6, 0x49286651, 0xECE45B3D, + 0xC2007CB8, 0xA163BF05, 0x98DA4836, 0x1C55D39A, 0x69163FA8, 0xFD24CF5F, + 0x83655D23, 0xDCA3AD96, 0x1C62F356, 0x208552BB, 0x9ED52907, 0x7096966D, + 0x670C354E, 0x4ABC9804, 0xF1746C08, 0xCA18217C, 0x32905E46, 0x2E36CE3B, + 0xE39E772C, 0x180E8603, 0x9B2783A2, 0xEC07A28F, 0xB5C55DF0, 0x6F4C52C9, + 0xDE2BCBF6, 0x95581718, 0x3995497C, 0xEA956AE5, 0x15D22618, 0x98FA0510, + 0x15728E5A, 0x8AACAA68, 0xFFFFFFFF, 0xFFFFFFFF, + ]; +} + +// 3072-bit MODP Group +// https://www.rfc-editor.org/rfc/rfc3526#section-4 +pub struct Modp3072; +impl DiffieHellmanGroup for Modp3072 { + const GENERATOR: usize = 2; + const EXPONENT_SIZE: usize = 384; + + const MODULUS: &'static [u32] = &[ + 0xFFFFFFFF, 0xFFFFFFFF, 0xC90FDAA2, 0x2168C234, 0xC4C6628B, 0x80DC1CD1, + 0x29024E08, 0x8A67CC74, 0x020BBEA6, 0x3B139B22, 0x514A0879, 0x8E3404DD, + 0xEF9519B3, 0xCD3A431B, 0x302B0A6D, 0xF25F1437, 0x4FE1356D, 0x6D51C245, + 0xE485B576, 0x625E7EC6, 0xF44C42E9, 0xA637ED6B, 0x0BFF5CB6, 0xF406B7ED, + 0xEE386BFB, 0x5A899FA5, 0xAE9F2411, 0x7C4B1FE6, 0x49286651, 0xECE45B3D, + 0xC2007CB8, 0xA163BF05, 0x98DA4836, 0x1C55D39A, 0x69163FA8, 0xFD24CF5F, + 0x83655D23, 0xDCA3AD96, 0x1C62F356, 0x208552BB, 0x9ED52907, 0x7096966D, + 0x670C354E, 0x4ABC9804, 0xF1746C08, 0xCA18217C, 0x32905E46, 0x2E36CE3B, + 0xE39E772C, 0x180E8603, 0x9B2783A2, 0xEC07A28F, 0xB5C55DF0, 0x6F4C52C9, + 0xDE2BCBF6, 0x95581718, 0x3995497C, 0xEA956AE5, 0x15D22618, 0x98FA0510, + 0x15728E5A, 0x8AAAC42D, 0xAD33170D, 0x04507A33, 0xA85521AB, 0xDF1CBA64, + 0xECFB8504, 0x58DBEF0A, 0x8AEA7157, 0x5D060C7D, 0xB3970F85, 0xA6E1E4C7, + 0xABF5AE8C, 0xDB0933D7, 0x1E8C94E0, 0x4A25619D, 0xCEE3D226, 0x1AD2EE6B, + 0xF12FFA06, 0xD98A0864, 0xD8760273, 0x3EC86A64, 0x521F2B18, 0x177B200C, + 0xBBE11757, 0x7A615D6C, 0x770988C0, 0xBAD946E2, 0x08E24FA0, 0x74E5AB31, + 0x43DB5BFC, 0xE0FD108E, 0x4B82D120, 0xA93AD2CA, 0xFFFFFFFF, 0xFFFFFFFF, + ]; +} + +/// 4096-bit MODP Group +/// https://www.rfc-editor.org/rfc/rfc3526#section-5 +pub struct Modp4096; +impl DiffieHellmanGroup for Modp4096 { + const GENERATOR: usize = 2; + const EXPONENT_SIZE: usize = 512; + + const MODULUS: &'static [u32] = &[ + 0xFFFFFFFF, 0xFFFFFFFF, 0xC90FDAA2, 0x2168C234, 0xC4C6628B, 0x80DC1CD1, + 0x29024E08, 0x8A67CC74, 0x020BBEA6, 0x3B139B22, 0x514A0879, 0x8E3404DD, + 0xEF9519B3, 0xCD3A431B, 0x302B0A6D, 0xF25F1437, 0x4FE1356D, 0x6D51C245, + 0xE485B576, 0x625E7EC6, 0xF44C42E9, 0xA637ED6B, 0x0BFF5CB6, 0xF406B7ED, + 0xEE386BFB, 0x5A899FA5, 0xAE9F2411, 0x7C4B1FE6, 0x49286651, 0xECE45B3D, + 0xC2007CB8, 0xA163BF05, 0x98DA4836, 0x1C55D39A, 0x69163FA8, 0xFD24CF5F, + 0x83655D23, 0xDCA3AD96, 0x1C62F356, 0x208552BB, 0x9ED52907, 0x7096966D, + 0x670C354E, 0x4ABC9804, 0xF1746C08, 0xCA18217C, 0x32905E46, 0x2E36CE3B, + 0xE39E772C, 0x180E8603, 0x9B2783A2, 0xEC07A28F, 0xB5C55DF0, 0x6F4C52C9, + 0xDE2BCBF6, 0x95581718, 0x3995497C, 0xEA956AE5, 0x15D22618, 0x98FA0510, + 0x15728E5A, 0x8AAAC42D, 0xAD33170D, 0x04507A33, 0xA85521AB, 0xDF1CBA64, + 0xECFB8504, 0x58DBEF0A, 0x8AEA7157, 0x5D060C7D, 0xB3970F85, 0xA6E1E4C7, + 0xABF5AE8C, 0xDB0933D7, 0x1E8C94E0, 0x4A25619D, 0xCEE3D226, 0x1AD2EE6B, + 0xF12FFA06, 0xD98A0864, 0xD8760273, 0x3EC86A64, 0x521F2B18, 0x177B200C, + 0xBBE11757, 0x7A615D6C, 0x770988C0, 0xBAD946E2, 0x08E24FA0, 0x74E5AB31, + 0x43DB5BFC, 0xE0FD108E, 0x4B82D120, 0xA9210801, 0x1A723C12, 0xA787E6D7, + 0x88719A10, 0xBDBA5B26, 0x99C32718, 0x6AF4E23C, 0x1A946834, 0xB6150BDA, + 0x2583E9CA, 0x2AD44CE8, 0xDBBBC2DB, 0x04DE8EF9, 0x2E8EFC14, 0x1FBECAA6, + 0x287C5947, 0x4E6BC05D, 0x99B2964F, 0xA090C3A2, 0x233BA186, 0x515BE7ED, + 0x1F612970, 0xCEE2D7AF, 0xB81BDD76, 0x2170481C, 0xD0069127, 0xD5B05AA9, + 0x93B4EA98, 0x8D8FDDC1, 0x86FFB7DC, 0x90A6C08F, 0x4DF435C9, 0x34063199, + 0xFFFFFFFF, 0xFFFFFFFF, + ]; +} + +/// 6144-bit MODP Group +/// https://www.rfc-editor.org/rfc/rfc3526#section-6 +pub struct Modp6144; +impl DiffieHellmanGroup for Modp6144 { + const GENERATOR: usize = 2; + const EXPONENT_SIZE: usize = 768; + + const MODULUS: &'static [u32] = &[ + 0xFFFFFFFF, 0xFFFFFFFF, 0xC90FDAA2, 0x2168C234, 0xC4C6628B, 0x80DC1CD1, + 0x29024E08, 0x8A67CC74, 0x020BBEA6, 0x3B139B22, 0x514A0879, 0x8E3404DD, + 0xEF9519B3, 0xCD3A431B, 0x302B0A6D, 0xF25F1437, 0x4FE1356D, 0x6D51C245, + 0xE485B576, 0x625E7EC6, 0xF44C42E9, 0xA637ED6B, 0x0BFF5CB6, 0xF406B7ED, + 0xEE386BFB, 0x5A899FA5, 0xAE9F2411, 0x7C4B1FE6, 0x49286651, 0xECE45B3D, + 0xC2007CB8, 0xA163BF05, 0x98DA4836, 0x1C55D39A, 0x69163FA8, 0xFD24CF5F, + 0x83655D23, 0xDCA3AD96, 0x1C62F356, 0x208552BB, 0x9ED52907, 0x7096966D, + 0x670C354E, 0x4ABC9804, 0xF1746C08, 0xCA18217C, 0x32905E46, 0x2E36CE3B, + 0xE39E772C, 0x180E8603, 0x9B2783A2, 0xEC07A28F, 0xB5C55DF0, 0x6F4C52C9, + 0xDE2BCBF6, 0x95581718, 0x3995497C, 0xEA956AE5, 0x15D22618, 0x98FA0510, + 0x15728E5A, 0x8AAAC42D, 0xAD33170D, 0x04507A33, 0xA85521AB, 0xDF1CBA64, + 0xECFB8504, 0x58DBEF0A, 0x8AEA7157, 0x5D060C7D, 0xB3970F85, 0xA6E1E4C7, + 0xABF5AE8C, 0xDB0933D7, 0x1E8C94E0, 0x4A25619D, 0xCEE3D226, 0x1AD2EE6B, + 0xF12FFA06, 0xD98A0864, 0xD8760273, 0x3EC86A64, 0x521F2B18, 0x177B200C, + 0xBBE11757, 0x7A615D6C, 0x770988C0, 0xBAD946E2, 0x08E24FA0, 0x74E5AB31, + 0x43DB5BFC, 0xE0FD108E, 0x4B82D120, 0xA9210801, 0x1A723C12, 0xA787E6D7, + 0x88719A10, 0xBDBA5B26, 0x99C32718, 0x6AF4E23C, 0x1A946834, 0xB6150BDA, + 0x2583E9CA, 0x2AD44CE8, 0xDBBBC2DB, 0x04DE8EF9, 0x2E8EFC14, 0x1FBECAA6, + 0x287C5947, 0x4E6BC05D, 0x99B2964F, 0xA090C3A2, 0x233BA186, 0x515BE7ED, + 0x1F612970, 0xCEE2D7AF, 0xB81BDD76, 0x2170481C, 0xD0069127, 0xD5B05AA9, + 0x93B4EA98, 0x8D8FDDC1, 0x86FFB7DC, 0x90A6C08F, 0x4DF435C9, 0x34028492, + 0x36C3FAB4, 0xD27C7026, 0xC1D4DCB2, 0x602646DE, 0xC9751E76, 0x3DBA37BD, + 0xF8FF9406, 0xAD9E530E, 0xE5DB382F, 0x413001AE, 0xB06A53ED, 0x9027D831, + 0x179727B0, 0x865A8918, 0xDA3EDBEB, 0xCF9B14ED, 0x44CE6CBA, 0xCED4BB1B, + 0xDB7F1447, 0xE6CC254B, 0x33205151, 0x2BD7AF42, 0x6FB8F401, 0x378CD2BF, + 0x5983CA01, 0xC64B92EC, 0xF032EA15, 0xD1721D03, 0xF482D7CE, 0x6E74FEF6, + 0xD55E702F, 0x46980C82, 0xB5A84031, 0x900B1C9E, 0x59E7C97F, 0xBEC7E8F3, + 0x23A97A7E, 0x36CC88BE, 0x0F1D45B7, 0xFF585AC5, 0x4BD407B2, 0x2B4154AA, + 0xCC8F6D7E, 0xBF48E1D8, 0x14CC5ED2, 0x0F8037E0, 0xA79715EE, 0xF29BE328, + 0x06A1D58B, 0xB7C5DA76, 0xF550AA3D, 0x8A1FBFF0, 0xEB19CCB1, 0xA313D55C, + 0xDA56C9EC, 0x2EF29632, 0x387FE8D7, 0x6E3C0468, 0x043E8F66, 0x3F4860EE, + 0x12BF2D5B, 0x0B7474D6, 0xE694F91E, 0x6DCC4024, 0xFFFFFFFF, 0xFFFFFFFF, + ]; +} + +/// 8192-bit MODP Group +/// https://www.rfc-editor.org/rfc/rfc3526#section-7 +pub struct Modp8192; +impl DiffieHellmanGroup for Modp8192 { + const GENERATOR: usize = 2; + const EXPONENT_SIZE: usize = 1024; + + const MODULUS: &'static [u32] = &[ + 0xFFFFFFFF, 0xFFFFFFFF, 0xC90FDAA2, 0x2168C234, 0xC4C6628B, 0x80DC1CD1, + 0x29024E08, 0x8A67CC74, 0x020BBEA6, 0x3B139B22, 0x514A0879, 0x8E3404DD, + 0xEF9519B3, 0xCD3A431B, 0x302B0A6D, 0xF25F1437, 0x4FE1356D, 0x6D51C245, + 0xE485B576, 0x625E7EC6, 0xF44C42E9, 0xA637ED6B, 0x0BFF5CB6, 0xF406B7ED, + 0xEE386BFB, 0x5A899FA5, 0xAE9F2411, 0x7C4B1FE6, 0x49286651, 0xECE45B3D, + 0xC2007CB8, 0xA163BF05, 0x98DA4836, 0x1C55D39A, 0x69163FA8, 0xFD24CF5F, + 0x83655D23, 0xDCA3AD96, 0x1C62F356, 0x208552BB, 0x9ED52907, 0x7096966D, + 0x670C354E, 0x4ABC9804, 0xF1746C08, 0xCA18217C, 0x32905E46, 0x2E36CE3B, + 0xE39E772C, 0x180E8603, 0x9B2783A2, 0xEC07A28F, 0xB5C55DF0, 0x6F4C52C9, + 0xDE2BCBF6, 0x95581718, 0x3995497C, 0xEA956AE5, 0x15D22618, 0x98FA0510, + 0x15728E5A, 0x8AAAC42D, 0xAD33170D, 0x04507A33, 0xA85521AB, 0xDF1CBA64, + 0xECFB8504, 0x58DBEF0A, 0x8AEA7157, 0x5D060C7D, 0xB3970F85, 0xA6E1E4C7, + 0xABF5AE8C, 0xDB0933D7, 0x1E8C94E0, 0x4A25619D, 0xCEE3D226, 0x1AD2EE6B, + 0xF12FFA06, 0xD98A0864, 0xD8760273, 0x3EC86A64, 0x521F2B18, 0x177B200C, + 0xBBE11757, 0x7A615D6C, 0x770988C0, 0xBAD946E2, 0x08E24FA0, 0x74E5AB31, + 0x43DB5BFC, 0xE0FD108E, 0x4B82D120, 0xA9210801, 0x1A723C12, 0xA787E6D7, + 0x88719A10, 0xBDBA5B26, 0x99C32718, 0x6AF4E23C, 0x1A946834, 0xB6150BDA, + 0x2583E9CA, 0x2AD44CE8, 0xDBBBC2DB, 0x04DE8EF9, 0x2E8EFC14, 0x1FBECAA6, + 0x287C5947, 0x4E6BC05D, 0x99B2964F, 0xA090C3A2, 0x233BA186, 0x515BE7ED, + 0x1F612970, 0xCEE2D7AF, 0xB81BDD76, 0x2170481C, 0xD0069127, 0xD5B05AA9, + 0x93B4EA98, 0x8D8FDDC1, 0x86FFB7DC, 0x90A6C08F, 0x4DF435C9, 0x34028492, + 0x36C3FAB4, 0xD27C7026, 0xC1D4DCB2, 0x602646DE, 0xC9751E76, 0x3DBA37BD, + 0xF8FF9406, 0xAD9E530E, 0xE5DB382F, 0x413001AE, 0xB06A53ED, 0x9027D831, + 0x179727B0, 0x865A8918, 0xDA3EDBEB, 0xCF9B14ED, 0x44CE6CBA, 0xCED4BB1B, + 0xDB7F1447, 0xE6CC254B, 0x33205151, 0x2BD7AF42, 0x6FB8F401, 0x378CD2BF, + 0x5983CA01, 0xC64B92EC, 0xF032EA15, 0xD1721D03, 0xF482D7CE, 0x6E74FEF6, + 0xD55E702F, 0x46980C82, 0xB5A84031, 0x900B1C9E, 0x59E7C97F, 0xBEC7E8F3, + 0x23A97A7E, 0x36CC88BE, 0x0F1D45B7, 0xFF585AC5, 0x4BD407B2, 0x2B4154AA, + 0xCC8F6D7E, 0xBF48E1D8, 0x14CC5ED2, 0x0F8037E0, 0xA79715EE, 0xF29BE328, + 0x06A1D58B, 0xB7C5DA76, 0xF550AA3D, 0x8A1FBFF0, 0xEB19CCB1, 0xA313D55C, + 0xDA56C9EC, 0x2EF29632, 0x387FE8D7, 0x6E3C0468, 0x043E8F66, 0x3F4860EE, + 0x12BF2D5B, 0x0B7474D6, 0xE694F91E, 0x6DBE1159, 0x74A3926F, 0x12FEE5E4, + 0x38777CB6, 0xA932DF8C, 0xD8BEC4D0, 0x73B931BA, 0x3BC832B6, 0x8D9DD300, + 0x741FA7BF, 0x8AFC47ED, 0x2576F693, 0x6BA42466, 0x3AAB639C, 0x5AE4F568, + 0x3423B474, 0x2BF1C978, 0x238F16CB, 0xE39D652D, 0xE3FDB8BE, 0xFC848AD9, + 0x22222E04, 0xA4037C07, 0x13EB57A8, 0x1A23F0C7, 0x3473FC64, 0x6CEA306B, + 0x4BCBC886, 0x2F8385DD, 0xFA9D4B7F, 0xA2C087E8, 0x79683303, 0xED5BDD3A, + 0x062B3CF5, 0xB3A278A6, 0x6D2A13F8, 0x3F44F82D, 0xDF310EE0, 0x74AB6A36, + 0x4597E899, 0xA0255DC1, 0x64F31CC5, 0x0846851D, 0xF9AB4819, 0x5DED7EA1, + 0xB1D510BD, 0x7EE74D73, 0xFAF36BC3, 0x1ECFA268, 0x359046F4, 0xEB879F92, + 0x4009438B, 0x481C6CD7, 0x889A002E, 0xD5EE382B, 0xC9190DA6, 0xFC026E47, + 0x9558E447, 0x5677E9AA, 0x9E3050E2, 0x765694DF, 0xC81F56E8, 0x80B96E71, + 0x60C980DD, 0x98EDD3DF, 0xFFFFFFFF, 0xFFFFFFFF, + ]; +} diff --git a/ext/node/crypto/mod.rs b/ext/node/crypto/mod.rs index da25b8eeb69076..51ca2650b0616e 100644 --- a/ext/node/crypto/mod.rs +++ b/ext/node/crypto/mod.rs @@ -23,6 +23,7 @@ use rsa::RsaPrivateKey; use rsa::RsaPublicKey; mod cipher; +mod dh; mod digest; mod primes; @@ -676,3 +677,32 @@ pub async fn op_node_x25519_generate_async( ) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { tokio::task::spawn_blocking(x25519_generate).await? } + +fn dh_generate_group( + group_name: &str, +) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + let dh = match group_name { + "modp5" => dh::DiffieHellman::group::(), + "modp14" => dh::DiffieHellman::group::(), + _ => return Err(type_error("Unsupported group name")), + }; + + Ok(( + dh.private_key.into_vec().into(), + dh.public_key.into_vec().into(), + )) +} + +#[op] +pub fn op_node_dh_generate_group( + group_name: &str, +) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + dh_generate_group(group_name) +} + +#[op] +pub async fn op_node_dh_generate_group_async( + group_name: String, +) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + tokio::task::spawn_blocking(move || dh_generate_group(&group_name)).await? +} diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 3cb62e26163e4d..1bbd2417e582d0 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -204,6 +204,8 @@ deno_core::extension!(deno_node, crypto::op_node_ed25519_generate_async, crypto::op_node_x25519_generate, crypto::op_node_x25519_generate_async, + crypto::op_node_dh_generate_group, + crypto::op_node_dh_generate_group_async, winerror::op_node_sys_to_uv_error, v8::op_v8_cached_data_version_tag, v8::op_v8_get_heap_statistics, From 74f402d9053654b228054b26e64c90bae98f16d6 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Fri, 14 Apr 2023 11:31:37 +0530 Subject: [PATCH 07/14] fixes to dh groups --- ext/node/crypto/dh.rs | 12 ++++++------ ext/node/crypto/mod.rs | 6 ++++++ ext/node/polyfills/internal/crypto/keygen.ts | 6 +++++- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/ext/node/crypto/dh.rs b/ext/node/crypto/dh.rs index f2ae658e02fb12..1a4c08a6532be8 100644 --- a/ext/node/crypto/dh.rs +++ b/ext/node/crypto/dh.rs @@ -76,8 +76,8 @@ pub trait DiffieHellmanGroup { const EXPONENT_SIZE: usize; } -// 1536-bit MODP Group -// https://www.rfc-editor.org/rfc/rfc3526#section-2 +/// 1536-bit MODP Group +/// https://www.rfc-editor.org/rfc/rfc3526#section-2 pub struct Modp1536; impl DiffieHellmanGroup for Modp1536 { const GENERATOR: usize = 2; @@ -95,8 +95,8 @@ impl DiffieHellmanGroup for Modp1536 { ]; } -// 2048-bit MODP Group -// https://www.rfc-editor.org/rfc/rfc3526#section-3 +/// 2048-bit MODP Group +/// https://www.rfc-editor.org/rfc/rfc3526#section-3 pub struct Modp2048; impl DiffieHellmanGroup for Modp2048 { const GENERATOR: usize = 2; @@ -117,8 +117,8 @@ impl DiffieHellmanGroup for Modp2048 { ]; } -// 3072-bit MODP Group -// https://www.rfc-editor.org/rfc/rfc3526#section-4 +/// 3072-bit MODP Group +/// https://www.rfc-editor.org/rfc/rfc3526#section-4 pub struct Modp3072; impl DiffieHellmanGroup for Modp3072 { const GENERATOR: usize = 2; diff --git a/ext/node/crypto/mod.rs b/ext/node/crypto/mod.rs index 51ca2650b0616e..daab3b6ebc40c8 100644 --- a/ext/node/crypto/mod.rs +++ b/ext/node/crypto/mod.rs @@ -684,6 +684,10 @@ fn dh_generate_group( let dh = match group_name { "modp5" => dh::DiffieHellman::group::(), "modp14" => dh::DiffieHellman::group::(), + "modp15" => dh::DiffieHellman::group::(), + "modp16" => dh::DiffieHellman::group::(), + "modp17" => dh::DiffieHellman::group::(), + "modp18" => dh::DiffieHellman::group::(), _ => return Err(type_error("Unsupported group name")), }; @@ -706,3 +710,5 @@ pub async fn op_node_dh_generate_group_async( ) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { tokio::task::spawn_blocking(move || dh_generate_group(&group_name)).await? } + +fn dh_generate() {} diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts index e1d4cc55c7ddad..d04dc7e98cb22e 100644 --- a/ext/node/polyfills/internal/crypto/keygen.ts +++ b/ext/node/polyfills/internal/crypto/keygen.ts @@ -913,7 +913,11 @@ function createJob(mode, type, options) { validateString(group, "options.group"); - notImplemented("DH"); + if (mode === kSync) { + return ops.op_node_dh_generate_group(group); + } else { + return core.opAsync("op_node_dh_generate_group_async", group); + } } if (prime != null) { From 22b4310ab5d7db103cd11e9d52ce3730c545b676 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 15 Apr 2023 10:36:47 +0530 Subject: [PATCH 08/14] stuff --- Cargo.lock | 17 ++++++ ext/node/Cargo.toml | 1 + ext/node/crypto/dh.rs | 23 +++++-- ext/node/crypto/mod.rs | 39 +++++++++++- ext/node/crypto/primes.rs | 25 ++++++++ ext/node/lib.rs | 2 + ext/node/polyfills/internal/crypto/keygen.ts | 63 +++++++++++++++----- 7 files changed, 149 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a823cd16fcf8a..c9f753bd02c03d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1163,6 +1163,7 @@ dependencies = [ "md-5", "md4", "num-bigint", + "num-bigint-dig", "num-integer", "num-traits", "once_cell", @@ -1181,6 +1182,7 @@ dependencies = [ "tokio", "typenum", "x25519-dalek", + "x509-parser", ] [[package]] @@ -1397,6 +1399,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -2989,6 +3005,7 @@ dependencies = [ "num-iter", "num-traits", "rand", + "serde", "smallvec", "zeroize", ] diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 43ce20b2e29a0b..dff74066e54258 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -30,6 +30,7 @@ libz-sys = { version = "1.1.8", features = ["static"] } md-5 = "0.10.5" md4 = "0.10.2" num-bigint.workspace = true +num-bigint-dig = "0.8.2" num-integer = "0.1.45" num-traits = "0.2.14" once_cell.workspace = true diff --git a/ext/node/crypto/dh.rs b/ext/node/crypto/dh.rs index 1a4c08a6532be8..4da9a01bf8c2ad 100644 --- a/ext/node/crypto/dh.rs +++ b/ext/node/crypto/dh.rs @@ -1,8 +1,9 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use num_bigint::BigUint; +use super::primes::Prime; +use num_bigint_dig::BigUint; +use num_bigint_dig::RandBigInt; use num_traits::FromPrimitive; -use rand::Rng; pub struct PublicKey(BigUint); @@ -17,7 +18,7 @@ pub struct PrivateKey(BigUint); impl PrivateKey { pub fn new(exponent_size: usize) -> Self { let mut rng = rand::thread_rng(); - let exponent = BigUint::from_bytes_be(&rng.gen::<[u8; 32]>()); + let exponent = rng.gen_biguint(exponent_size); Self(exponent) } @@ -48,7 +49,7 @@ impl DiffieHellman { where G: DiffieHellmanGroup, { - let private_key = PrivateKey::new(G::EXPONENT_SIZE); + let private_key = PrivateKey::new(G::EXPONENT_SIZE / 8); let generator = BigUint::from_usize(G::GENERATOR).unwrap(); let modulus = BigUint::from_slice(G::MODULUS); @@ -60,6 +61,18 @@ impl DiffieHellman { public_key, } } + + pub fn new(prime: Prime, generator: usize) -> Self { + let private_key = PrivateKey::new(32); + + let generator = BigUint::from_usize(generator).unwrap(); + let public_key = private_key.compute_public_key(&generator, &prime); + + Self { + private_key, + public_key, + } + } } /// Well-known modp groups @@ -72,7 +85,7 @@ impl DiffieHellman { pub trait DiffieHellmanGroup { const GENERATOR: usize; const MODULUS: &'static [u32]; - /// Size of the exponent in bytes + /// Size of the exponent in bits const EXPONENT_SIZE: usize; } diff --git a/ext/node/crypto/mod.rs b/ext/node/crypto/mod.rs index 600ad299ca3b82..569dfd3b959162 100644 --- a/ext/node/crypto/mod.rs +++ b/ext/node/crypto/mod.rs @@ -487,6 +487,8 @@ pub async fn op_node_hkdf_async( use rsa::pkcs1::EncodeRsaPrivateKey; use rsa::pkcs1::EncodeRsaPublicKey; +use self::primes::Prime; + fn generate_rsa( modulus_length: usize, public_exponent: usize, @@ -714,7 +716,42 @@ pub async fn op_node_dh_generate_group_async( tokio::task::spawn_blocking(move || dh_generate_group(&group_name)).await? } -fn dh_generate() {} +fn dh_generate( + prime: Option<&[u8]>, + prime_len: usize, + generator: usize, +) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + let prime = prime + .map(|p| p.into()) + .unwrap_or_else(|| Prime::generate(prime_len)); + let dh = dh::DiffieHellman::new(prime, generator); + + Ok(( + dh.private_key.into_vec().into(), + dh.public_key.into_vec().into(), + )) +} + +#[op] +pub fn op_node_dh_generate( + prime: Option<&[u8]>, + prime_len: usize, + generator: usize, +) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + dh_generate(prime, prime_len, generator) +} + +#[op] +pub async fn op_node_dh_generate_async( + prime: Option, + prime_len: usize, + generator: usize, +) -> Result<(ZeroCopyBuf, ZeroCopyBuf), AnyError> { + tokio::task::spawn_blocking(move || { + dh_generate(prime.as_deref(), prime_len, generator) + }) + .await? +} #[op] pub fn op_node_random_int(min: i32, max: i32) -> Result { diff --git a/ext/node/crypto/primes.rs b/ext/node/crypto/primes.rs index f438d87259410d..d03398f024b482 100644 --- a/ext/node/crypto/primes.rs +++ b/ext/node/crypto/primes.rs @@ -1,10 +1,35 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. use num_bigint::BigInt; +use num_bigint_dig::RandPrime; use num_integer::Integer; use num_traits::One; use num_traits::Zero; use rand::Rng; +use std::ops::Deref; + +pub struct Prime(num_bigint_dig::BigUint); + +impl Prime { + pub fn generate(n: usize) -> Self { + let mut rng = rand::thread_rng(); + Self(rng.gen_prime(n)) + } +} + +impl From<&[u8]> for Prime { + fn from(value: &[u8]) -> Self { + Self(num_bigint_dig::BigUint::from_bytes_be(value)) + } +} + +impl Deref for Prime { + type Target = num_bigint_dig::BigUint; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} struct Witness { pow: BigInt, diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 9b0fd1b1ab3ce3..1aa6d67b9c2ab7 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -206,6 +206,8 @@ deno_core::extension!(deno_node, crypto::op_node_x25519_generate_async, crypto::op_node_dh_generate_group, crypto::op_node_dh_generate_group_async, + crypto::op_node_dh_generate, + crypto::op_node_dh_generate_async, crypto::op_node_random_int, crypto::x509::op_node_x509_parse, crypto::x509::op_node_x509_ca, diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts index d04dc7e98cb22e..f69289bc4a8a74 100644 --- a/ext/node/polyfills/internal/crypto/keygen.ts +++ b/ext/node/polyfills/internal/crypto/keygen.ts @@ -536,9 +536,9 @@ export function generateKeyPair( ) => void, ): void; export function generateKeyPair( - _type: KeyType, - _options: unknown, - _callback: ( + type: KeyType, + options: unknown, + callback: ( err: Error | null, // deno-lint-ignore no-explicit-any publicKey: any, @@ -546,7 +546,26 @@ export function generateKeyPair( privateKey: any, ) => void, ) { - notImplemented("crypto.generateKeyPair"); + createJob(kAsync, type, options).then(([privateKey, publicKey]) => { + privateKey = new KeyObject("private", setOwnedKey(privateKey)); + publicKey = new KeyObject("public", setOwnedKey(publicKey)); + + if (typeof options === "object" && options !== null) { + const { publicKeyEncoding, privateKeyEncoding } = options as any; + + if (publicKeyEncoding) { + publicKey = publicKey.export(publicKeyEncoding); + } + + if (privateKeyEncoding) { + privateKey = privateKey.export(privateKeyEncoding); + } + } + + callback(null, publicKey, privateKey); + }).catch((err) => { + callback(err, null, null); + }); } export interface KeyPairKeyObjectResult { @@ -829,16 +848,18 @@ function createJob(mode, type, options) { } } - return new RsaKeyPairGenJob( - mode, - kKeyVariantRSA_PSS, - modulusLength, - publicExponent, - hashAlgorithm || hash, - mgf1HashAlgorithm || mgf1Hash, - saltLength, - ...encoding, - ); + if (mode === kSync) { + return ops.op_node_generate_rsa( + modulusLength, + publicExponent, + ); + } else { + return core.opAsync( + "op_node_generate_rsa_async", + modulusLength, + publicExponent, + ); + } } case "dsa": { validateObject(options, "options"); @@ -937,7 +958,19 @@ function createJob(mode, type, options) { if (generator != null) { validateInt32(generator, "options.generator", 0); } - notImplemented("DH"); + + const g = generator == null ? 2 : generator; + + if (mode === kSync) { + return ops.op_node_dh_generate(prime, primeLength ?? 0, g); + } else { + return core.opAsync( + "op_node_dh_generate_async", + prime, + primeLength ?? 0, + g, + ); + } } default: // Fall through From 091d1d299eff5dcaf43a674f4201cecb28727916 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 15 Apr 2023 11:05:09 +0530 Subject: [PATCH 09/14] tests --- cli/tests/unit_node/crypto_key.ts | 88 +++++++++++++++++++- ext/node/crypto/mod.rs | 4 +- ext/node/polyfills/internal/crypto/keygen.ts | 2 +- 3 files changed, 89 insertions(+), 5 deletions(-) diff --git a/cli/tests/unit_node/crypto_key.ts b/cli/tests/unit_node/crypto_key.ts index d1a33db9e5f387..e83777b1d1de9d 100644 --- a/cli/tests/unit_node/crypto_key.ts +++ b/cli/tests/unit_node/crypto_key.ts @@ -1,7 +1,15 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -import { createSecretKey, randomBytes } from "node:crypto"; +import { + createSecretKey, + generateKeyPair, + generateKeyPairSync, + randomBytes, +} from "node:crypto"; import { Buffer } from "node:buffer"; -import { assertEquals } from "../../../test_util/std/testing/asserts.ts"; +import { + assertEquals, + assertThrows, +} from "../../../test_util/std/testing/asserts.ts"; import { createHmac } from "node:crypto"; Deno.test({ @@ -45,3 +53,79 @@ Deno.test({ ); }, }); + +for (const type of ["rsa", "rsa-pss", "dsa"]) { + for (const modulusLength of [2048, 3072]) { + Deno.test({ + name: `generate ${type} key`, + fn() { + const { publicKey, privateKey } = generateKeyPairSync(type as any, { + modulusLength, + }); + + assertEquals(publicKey.type, "public"); + assertEquals(privateKey.type, "private"); + }, + }); + } +} + +for (const namedCurve of ["P-384", "P-256"]) { + Deno.test({ + name: `generate ec key ${namedCurve}`, + fn() { + const { publicKey, privateKey } = generateKeyPairSync("ec", { + namedCurve, + }); + + assertEquals(publicKey.type, "public"); + assertEquals(privateKey.type, "private"); + }, + }); + + Deno.test({ + name: `generate ec key ${namedCurve} paramEncoding=explicit false`, + fn() { + assertThrows(() => { + // @ts-ignore + generateKeyPairSync("ec", { + namedCurve, + paramEncoding: "explicit", + }); + }); + }, + }); +} + +for ( + const groupName of ["modp5", "modp14", "modp15", "modp16", "modp17", "modp18"] +) { + Deno.test({ + name: `generate dh key ${groupName}`, + fn() { + // @ts-ignore + const { publicKey, privateKey } = generateKeyPairSync("dh", { + group: groupName, + }); + + assertEquals(publicKey.type, "public"); + assertEquals(privateKey.type, "private"); + }, + }); +} + +for (const primeLength of [1024, 2048, 4096]) { + Deno.test({ + name: `generate dh key ${primeLength}`, + fn() { + // @ts-ignore + const { publicKey, privateKey } = generateKeyPairSync("dh", { + primeLength, + generator: 2, + }); + + assertEquals(publicKey.type, "public"); + assertEquals(privateKey.type, "private"); + }, + }); +} diff --git a/ext/node/crypto/mod.rs b/ext/node/crypto/mod.rs index 569dfd3b959162..7063ca30264914 100644 --- a/ext/node/crypto/mod.rs +++ b/ext/node/crypto/mod.rs @@ -589,8 +589,8 @@ fn ec_generate( use ring::signature::KeyPair; let curve = match named_curve { - "p256" => &ring::signature::ECDSA_P256_SHA256_FIXED_SIGNING, - "p384" => &ring::signature::ECDSA_P384_SHA384_FIXED_SIGNING, + "P-256" => &ring::signature::ECDSA_P256_SHA256_FIXED_SIGNING, + "P-384" => &ring::signature::ECDSA_P384_SHA384_FIXED_SIGNING, _ => return Err(type_error("Unsupported named curve")), }; diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts index f69289bc4a8a74..48fb9ecc007ae6 100644 --- a/ext/node/polyfills/internal/crypto/keygen.ts +++ b/ext/node/polyfills/internal/crypto/keygen.ts @@ -868,7 +868,7 @@ function createJob(mode, type, options) { let { divisorLength } = options; if (divisorLength == null) { - divisorLength = -1; + divisorLength = 256; } else { validateInt32(divisorLength, "options.divisorLength", 0); } From aa29f7122b8e641b0cac6f8a918c156cea9d8176 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 15 Apr 2023 11:22:28 +0530 Subject: [PATCH 10/14] ci From 98fb3fd5e6e03c64eec445fb6f61caa2ef319ed2 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 15 Apr 2023 11:56:42 +0530 Subject: [PATCH 11/14] x --- cli/tests/unit_node/crypto_key.ts | 81 +++++++++++++++++++- ext/node/polyfills/internal/crypto/keygen.ts | 5 +- 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/cli/tests/unit_node/crypto_key.ts b/cli/tests/unit_node/crypto_key.ts index e83777b1d1de9d..49d81003f01c79 100644 --- a/cli/tests/unit_node/crypto_key.ts +++ b/cli/tests/unit_node/crypto_key.ts @@ -1,10 +1,14 @@ +// deno-lint-ignore-file no-explicit-any + // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. import { createSecretKey, generateKeyPair, generateKeyPairSync, + KeyObject, randomBytes, } from "node:crypto"; +import { promisify } from "node:util"; import { Buffer } from "node:buffer"; import { assertEquals, @@ -12,6 +16,24 @@ import { } from "../../../test_util/std/testing/asserts.ts"; import { createHmac } from "node:crypto"; +const generateKeyPairAsync = promisify( + ( + type: any, + options: any, + callback: ( + err: Error | null, + key: { publicKey: KeyObject; privateKey: KeyObject }, + ) => void, + ) => + generateKeyPair( + type, + options, + (err: Error | null, publicKey: KeyObject, privateKey: KeyObject) => { + callback(err, { publicKey, privateKey }); + }, + ), +); + Deno.test({ name: "create secret key", fn() { @@ -67,6 +89,18 @@ for (const type of ["rsa", "rsa-pss", "dsa"]) { assertEquals(privateKey.type, "private"); }, }); + + Deno.test({ + name: `generate ${type} key async`, + async fn() { + const x = await generateKeyPairAsync(type as any, { + modulusLength, + }); + const { publicKey, privateKey } = x; + assertEquals(publicKey.type, "public"); + assertEquals(privateKey.type, "private"); + }, + }); } } @@ -84,10 +118,22 @@ for (const namedCurve of ["P-384", "P-256"]) { }); Deno.test({ - name: `generate ec key ${namedCurve} paramEncoding=explicit false`, + name: `generate ec key ${namedCurve} async`, + async fn() { + const { publicKey, privateKey } = await generateKeyPairAsync("ec", { + namedCurve, + }); + + assertEquals(publicKey.type, "public"); + assertEquals(privateKey.type, "private"); + }, + }); + + Deno.test({ + name: `generate ec key ${namedCurve} paramEncoding=explicit fails`, fn() { assertThrows(() => { - // @ts-ignore + // @ts-ignore: @types/node is broken? generateKeyPairSync("ec", { namedCurve, paramEncoding: "explicit", @@ -103,7 +149,7 @@ for ( Deno.test({ name: `generate dh key ${groupName}`, fn() { - // @ts-ignore + // @ts-ignore: @types/node is broken? const { publicKey, privateKey } = generateKeyPairSync("dh", { group: groupName, }); @@ -112,13 +158,26 @@ for ( assertEquals(privateKey.type, "private"); }, }); + + Deno.test({ + name: `generate dh key ${groupName} async`, + async fn() { + // @ts-ignore: @types/node is broken? + const { publicKey, privateKey } = await generateKeyPairAsync("dh", { + group: groupName, + }); + + assertEquals(publicKey.type, "public"); + assertEquals(privateKey.type, "private"); + }, + }); } for (const primeLength of [1024, 2048, 4096]) { Deno.test({ name: `generate dh key ${primeLength}`, fn() { - // @ts-ignore + // @ts-ignore: @types/node is broken? const { publicKey, privateKey } = generateKeyPairSync("dh", { primeLength, generator: 2, @@ -128,4 +187,18 @@ for (const primeLength of [1024, 2048, 4096]) { assertEquals(privateKey.type, "private"); }, }); + + Deno.test({ + name: `generate dh key ${primeLength} async`, + async fn() { + // @ts-ignore: @types/node is broken? + const { publicKey, privateKey } = await generateKeyPairAsync("dh", { + primeLength, + generator: 2, + }); + + assertEquals(publicKey.type, "public"); + assertEquals(privateKey.type, "private"); + }, + }); } diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts index 48fb9ecc007ae6..658d9560dc754f 100644 --- a/ext/node/polyfills/internal/crypto/keygen.ts +++ b/ext/node/polyfills/internal/crypto/keygen.ts @@ -1,3 +1,5 @@ +// deno-lint-ignore-file no-explicit-any + // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. // Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. @@ -886,7 +888,7 @@ function createJob(mode, type, options) { validateObject(options, "options"); const { namedCurve } = options; validateString(namedCurve, "options.namedCurve"); - let { paramEncoding } = options; + const { paramEncoding } = options; if (paramEncoding == null || paramEncoding === "named") { // pass. } else if (paramEncoding === "explicit") { @@ -917,6 +919,7 @@ function createJob(mode, type, options) { case "ed448": case "x448": { notImplemented(type); + break; } case "dh": { validateObject(options, "options"); From 68a86dff32cd906395320a058c254c453784e15b Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 15 Apr 2023 12:41:34 +0530 Subject: [PATCH 12/14] x --- ext/node/polyfills/internal/crypto/keygen.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts index 658d9560dc754f..cdc616db6d796f 100644 --- a/ext/node/polyfills/internal/crypto/keygen.ts +++ b/ext/node/polyfills/internal/crypto/keygen.ts @@ -542,9 +542,7 @@ export function generateKeyPair( options: unknown, callback: ( err: Error | null, - // deno-lint-ignore no-explicit-any publicKey: any, - // deno-lint-ignore no-explicit-any privateKey: any, ) => void, ) { From 0a06aaa28fc3d98982bf9c3e130093887dc99296 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sat, 15 Apr 2023 17:07:48 +0530 Subject: [PATCH 13/14] purge cache --- .github/workflows/ci.generate.ts | 2 +- .github/workflows/ci.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index e7fbd82512d422..5bf55f62b4828e 100755 --- a/.github/workflows/ci.generate.ts +++ b/.github/workflows/ci.generate.ts @@ -17,7 +17,7 @@ const Runners = (() => { })(); // bump the number at the start when you want to purge the cache const prCacheKeyPrefix = - "18-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ matrix.job }}-"; + "19-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ matrix.job }}-"; const installPkgsCommand = "sudo apt-get install --no-install-recommends debootstrap clang-15 lld-15"; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91fb642a439c7f..b0dab622aab8bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -302,7 +302,7 @@ jobs: !./target/*/*.zip !./target/*/*.tar.gz key: never_saved - restore-keys: '18-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ matrix.job }}-' + restore-keys: '19-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ matrix.job }}-' - name: Apply and update mtime cache if: '!(github.event_name == ''pull_request'' && matrix.skip_pr) && (!startsWith(github.ref, ''refs/tags/''))' uses: ./.github/mtime_cache @@ -578,7 +578,7 @@ jobs: !./target/*/gn_out !./target/*/*.zip !./target/*/*.tar.gz - key: '18-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' + key: '19-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' publish-canary: name: publish canary runs-on: ubuntu-22.04 From b4552298cbd9ab4d6ffdea35e4407e3bc2d70b60 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Wed, 19 Apr 2023 21:19:25 +0530 Subject: [PATCH 14/14] cycle cache keys again --- .github/workflows/ci.generate.ts | 2 +- .github/workflows/ci.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index 5bf55f62b4828e..241b38ef990ac2 100755 --- a/.github/workflows/ci.generate.ts +++ b/.github/workflows/ci.generate.ts @@ -17,7 +17,7 @@ const Runners = (() => { })(); // bump the number at the start when you want to purge the cache const prCacheKeyPrefix = - "19-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ matrix.job }}-"; + "20-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ matrix.job }}-"; const installPkgsCommand = "sudo apt-get install --no-install-recommends debootstrap clang-15 lld-15"; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0dab622aab8bd..c7144164b60846 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -302,7 +302,7 @@ jobs: !./target/*/*.zip !./target/*/*.tar.gz key: never_saved - restore-keys: '19-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ matrix.job }}-' + restore-keys: '20-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ matrix.job }}-' - name: Apply and update mtime cache if: '!(github.event_name == ''pull_request'' && matrix.skip_pr) && (!startsWith(github.ref, ''refs/tags/''))' uses: ./.github/mtime_cache @@ -578,7 +578,7 @@ jobs: !./target/*/gn_out !./target/*/*.zip !./target/*/*.tar.gz - key: '19-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' + key: '20-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' publish-canary: name: publish canary runs-on: ubuntu-22.04