diff options
| author | Luca Casonato <hello@lcas.dev> | 2024-08-08 11:35:29 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-08-08 15:05:29 +0530 |
| commit | 93d479252b5a18e6e782c74b808240bd3ef036bd (patch) | |
| tree | 4c8b83797fb0e30d1ce3fcebad3fbde6cf33ef1d /ext/node/ops/crypto | |
| parent | 507e5b74ff21161ba8bd947d7d9cee317c0af379 (diff) | |
fix(ext/node): add crypto.diffieHellman (#24938)
Co-authored-by: Divy Srivastava <dj.srivastava23@gmail.com>
Closes #21806
Diffstat (limited to 'ext/node/ops/crypto')
| -rw-r--r-- | ext/node/ops/crypto/keys.rs | 156 | ||||
| -rw-r--r-- | ext/node/ops/crypto/mod.rs | 86 | ||||
| -rw-r--r-- | ext/node/ops/crypto/pkcs3.rs | 20 | ||||
| -rw-r--r-- | ext/node/ops/crypto/primes.rs | 1 |
4 files changed, 230 insertions, 33 deletions
diff --git a/ext/node/ops/crypto/keys.rs b/ext/node/ops/crypto/keys.rs index 86280c11a..5f634b35f 100644 --- a/ext/node/ops/crypto/keys.rs +++ b/ext/node/ops/crypto/keys.rs @@ -30,6 +30,7 @@ use rsa::pkcs1::EncodeRsaPublicKey; use rsa::traits::PublicKeyParts; use rsa::RsaPrivateKey; use rsa::RsaPublicKey; +use sec1::der::Tag; use sec1::der::Writer as _; use sec1::pem::PemLabel as _; use sec1::DecodeEcPrivateKey as _; @@ -46,7 +47,10 @@ use spki::EncodePublicKey as _; use spki::SubjectPublicKeyInfoRef; use super::dh; +use super::dh::DiffieHellmanGroup; use super::digest::match_fixed_digest_with_oid; +use super::pkcs3; +use super::pkcs3::DhParameter; use super::primes::Prime; #[derive(Clone)] @@ -66,8 +70,7 @@ pub enum AsymmetricPrivateKey { Ec(EcPrivateKey), X25519(x25519_dalek::StaticSecret), Ed25519(ed25519_dalek::SigningKey), - #[allow(unused)] - Dh(dh::PrivateKey), + Dh(DhPrivateKey), } #[derive(Clone)] @@ -126,16 +129,20 @@ pub enum EcPrivateKey { } #[derive(Clone)] +pub struct DhPrivateKey { + pub key: dh::PrivateKey, + pub params: DhParameter, +} + +#[derive(Clone)] pub enum AsymmetricPublicKey { Rsa(rsa::RsaPublicKey), RsaPss(RsaPssPublicKey), Dsa(dsa::VerifyingKey), Ec(EcPublicKey), - #[allow(unused)] X25519(x25519_dalek::PublicKey), Ed25519(ed25519_dalek::VerifyingKey), - #[allow(unused)] - Dh(dh::PublicKey), + Dh(DhPublicKey), } #[derive(Clone)] @@ -151,6 +158,12 @@ pub enum EcPublicKey { P384(p384::PublicKey), } +#[derive(Clone)] +pub struct DhPublicKey { + pub key: dh::PublicKey, + pub params: DhParameter, +} + impl KeyObjectHandle { /// Returns the private key if the handle is an asymmetric private key. pub fn as_private_key(&self) -> Option<&AsymmetricPrivateKey> { @@ -492,9 +505,18 @@ impl KeyObjectHandle { bytes.copy_from_slice(string_ref.as_bytes()); AsymmetricPrivateKey::Ed25519(ed25519_dalek::SigningKey::from(bytes)) } - DH_KEY_AGREEMENT_OID => AsymmetricPrivateKey::Dh( - dh::PrivateKey::from_bytes(pk_info.private_key), - ), + DH_KEY_AGREEMENT_OID => { + let params = pk_info + .algorithm + .parameters + .ok_or_else(|| type_error("missing dh parameters"))?; + let params = pkcs3::DhParameter::from_der(¶ms.to_der().unwrap()) + .map_err(|_| type_error("malformed dh parameters"))?; + AsymmetricPrivateKey::Dh(DhPrivateKey { + key: dh::PrivateKey::from_bytes(pk_info.private_key), + params, + }) + } _ => return Err(type_error("unsupported private key oid")), }; @@ -634,11 +656,20 @@ impl KeyObjectHandle { AsymmetricPublicKey::Ed25519(verifying_key) } DH_KEY_AGREEMENT_OID => { + let params = spki + .algorithm + .parameters + .ok_or_else(|| type_error("missing dh parameters"))?; + let params = pkcs3::DhParameter::from_der(¶ms.to_der().unwrap()) + .map_err(|_| type_error("malformed dh parameters"))?; let Some(subject_public_key) = spki.subject_public_key.as_bytes() else { return Err(type_error("malformed or missing public key in dh spki")); }; - AsymmetricPublicKey::Dh(dh::PublicKey::from_bytes(subject_public_key)) + AsymmetricPublicKey::Dh(DhPublicKey { + key: dh::PublicKey::from_bytes(subject_public_key), + params, + }) } _ => return Err(type_error("unsupported public key oid")), }; @@ -788,16 +819,18 @@ impl AsymmetricPublicKey { .into_boxed_slice() } AsymmetricPublicKey::Dh(key) => { - let public_key_bytes = key.clone().into_vec(); - let spki = - SubjectPublicKeyInfoRef { - algorithm: rsa::pkcs8::AlgorithmIdentifierRef { - oid: DH_KEY_AGREEMENT_OID, - parameters: None, - }, - subject_public_key: BitStringRef::from_bytes(&public_key_bytes) - .map_err(|_| type_error("invalid DH public key"))?, - }; + let public_key_bytes = key.key.clone().into_vec(); + let params = key.params.to_der().unwrap(); + let spki = SubjectPublicKeyInfoRef { + algorithm: rsa::pkcs8::AlgorithmIdentifierRef { + oid: DH_KEY_AGREEMENT_OID, + parameters: Some(AnyRef::new(Tag::Sequence, ¶ms).unwrap()), + }, + subject_public_key: BitStringRef::from_bytes(&public_key_bytes) + .map_err(|_| { + type_error("invalid DH public key") + })?, + }; spki .to_der() .map_err(|_| type_error("invalid DH public key"))? @@ -917,11 +950,12 @@ impl AsymmetricPrivateKey { .into_boxed_slice() } AsymmetricPrivateKey::Dh(key) => { - let private_key = key.clone().into_vec(); + let private_key = key.key.clone().into_vec(); + let params = key.params.to_der().unwrap(); let private_key = PrivateKeyInfo { algorithm: rsa::pkcs8::AlgorithmIdentifierRef { oid: DH_KEY_AGREEMENT_OID, - parameters: None, + parameters: Some(AnyRef::new(Tag::Sequence, ¶ms).unwrap()), }, private_key: &private_key, public_key: None, @@ -1514,21 +1548,66 @@ pub async fn op_node_generate_ed25519_key_async() -> KeyObjectHandlePair { spawn_blocking(ed25519_generate).await.unwrap() } +fn u32_slice_to_u8_slice(slice: &[u32]) -> &[u8] { + // SAFETY: just reinterpreting the slice as u8 + unsafe { + std::slice::from_raw_parts( + slice.as_ptr() as *const u8, + std::mem::size_of_val(slice), + ) + } +} + fn dh_group_generate( group_name: &str, ) -> Result<KeyObjectHandlePair, AnyError> { - let dh = match group_name { - "modp5" => dh::DiffieHellman::group::<dh::Modp1536>(), - "modp14" => dh::DiffieHellman::group::<dh::Modp2048>(), - "modp15" => dh::DiffieHellman::group::<dh::Modp3072>(), - "modp16" => dh::DiffieHellman::group::<dh::Modp4096>(), - "modp17" => dh::DiffieHellman::group::<dh::Modp6144>(), - "modp18" => dh::DiffieHellman::group::<dh::Modp8192>(), + let (dh, prime, generator) = match group_name { + "modp5" => ( + dh::DiffieHellman::group::<dh::Modp1536>(), + dh::Modp1536::MODULUS, + dh::Modp1536::GENERATOR, + ), + "modp14" => ( + dh::DiffieHellman::group::<dh::Modp2048>(), + dh::Modp2048::MODULUS, + dh::Modp2048::GENERATOR, + ), + "modp15" => ( + dh::DiffieHellman::group::<dh::Modp3072>(), + dh::Modp3072::MODULUS, + dh::Modp3072::GENERATOR, + ), + "modp16" => ( + dh::DiffieHellman::group::<dh::Modp4096>(), + dh::Modp4096::MODULUS, + dh::Modp4096::GENERATOR, + ), + "modp17" => ( + dh::DiffieHellman::group::<dh::Modp6144>(), + dh::Modp6144::MODULUS, + dh::Modp6144::GENERATOR, + ), + "modp18" => ( + dh::DiffieHellman::group::<dh::Modp8192>(), + dh::Modp8192::MODULUS, + dh::Modp8192::GENERATOR, + ), _ => return Err(type_error("Unsupported group name")), }; + let params = DhParameter { + prime: asn1::Int::new(u32_slice_to_u8_slice(prime)).unwrap(), + base: asn1::Int::new(generator.to_be_bytes().as_slice()).unwrap(), + private_value_length: None, + }; Ok(KeyObjectHandlePair::new( - AsymmetricPrivateKey::Dh(dh.private_key), - AsymmetricPublicKey::Dh(dh.public_key), + AsymmetricPrivateKey::Dh(DhPrivateKey { + key: dh.private_key, + params: params.clone(), + }), + AsymmetricPublicKey::Dh(DhPublicKey { + key: dh.public_key, + params, + }), )) } @@ -1558,10 +1637,21 @@ fn dh_generate( let prime = prime .map(|p| p.into()) .unwrap_or_else(|| Prime::generate(prime_len)); - let dh = dh::DiffieHellman::new(prime, generator); + let dh = dh::DiffieHellman::new(prime.clone(), generator); + let params = DhParameter { + prime: asn1::Int::new(&prime.0.to_bytes_be()).unwrap(), + base: asn1::Int::new(generator.to_be_bytes().as_slice()).unwrap(), + private_value_length: None, + }; Ok(KeyObjectHandlePair::new( - AsymmetricPrivateKey::Dh(dh.private_key), - AsymmetricPublicKey::Dh(dh.public_key), + AsymmetricPrivateKey::Dh(DhPrivateKey { + key: dh.private_key, + params: params.clone(), + }), + AsymmetricPublicKey::Dh(DhPublicKey { + key: dh.public_key, + params, + }), )) } diff --git a/ext/node/ops/crypto/mod.rs b/ext/node/ops/crypto/mod.rs index 07d901cbc..567affd52 100644 --- a/ext/node/ops/crypto/mod.rs +++ b/ext/node/ops/crypto/mod.rs @@ -10,6 +10,10 @@ use deno_core::StringOrBuffer; use deno_core::ToJsBuffer; use elliptic_curve::sec1::ToEncodedPoint; use hkdf::Hkdf; +use keys::AsymmetricPrivateKey; +use keys::AsymmetricPublicKey; +use keys::EcPrivateKey; +use keys::EcPublicKey; use keys::KeyObjectHandle; use num_bigint::BigInt; use num_bigint_dig::BigUint; @@ -34,6 +38,7 @@ mod dh; mod digest; pub mod keys; mod md5_sha1; +mod pkcs3; mod primes; mod sign; pub mod x509; @@ -839,3 +844,84 @@ pub async fn op_node_gen_prime_async( ) -> Result<ToJsBuffer, AnyError> { Ok(spawn_blocking(move || gen_prime(size)).await?) } + +#[op2] +#[buffer] +pub fn op_node_diffie_hellman( + #[cppgc] private: &KeyObjectHandle, + #[cppgc] public: &KeyObjectHandle, +) -> Result<Box<[u8]>, AnyError> { + let private = private + .as_private_key() + .ok_or_else(|| type_error("Expected private key"))?; + let public = public + .as_public_key() + .ok_or_else(|| type_error("Expected public key"))?; + + let res = match (private, &*public) { + ( + AsymmetricPrivateKey::Ec(EcPrivateKey::P224(private)), + AsymmetricPublicKey::Ec(EcPublicKey::P224(public)), + ) => p224::ecdh::diffie_hellman( + private.to_nonzero_scalar(), + public.as_affine(), + ) + .raw_secret_bytes() + .to_vec() + .into_boxed_slice(), + ( + AsymmetricPrivateKey::Ec(EcPrivateKey::P256(private)), + AsymmetricPublicKey::Ec(EcPublicKey::P256(public)), + ) => p256::ecdh::diffie_hellman( + private.to_nonzero_scalar(), + public.as_affine(), + ) + .raw_secret_bytes() + .to_vec() + .into_boxed_slice(), + ( + AsymmetricPrivateKey::Ec(EcPrivateKey::P384(private)), + AsymmetricPublicKey::Ec(EcPublicKey::P384(public)), + ) => p384::ecdh::diffie_hellman( + private.to_nonzero_scalar(), + public.as_affine(), + ) + .raw_secret_bytes() + .to_vec() + .into_boxed_slice(), + ( + AsymmetricPrivateKey::X25519(private), + AsymmetricPublicKey::X25519(public), + ) => private + .diffie_hellman(public) + .to_bytes() + .into_iter() + .collect(), + (AsymmetricPrivateKey::Dh(private), AsymmetricPublicKey::Dh(public)) => { + if private.params.prime != public.params.prime + || private.params.base != public.params.base + { + return Err(type_error("DH parameters mismatch")); + } + + // OSIP - Octet-String-to-Integer primitive + let public_key = public.key.clone().into_vec(); + let pubkey = BigUint::from_bytes_be(&public_key); + + // Exponentiation (z = y^x mod p) + let prime = BigUint::from_bytes_be(private.params.prime.as_bytes()); + let private_key = private.key.clone().into_vec(); + let private_key = BigUint::from_bytes_be(&private_key); + let shared_secret = pubkey.modpow(&private_key, &prime); + + shared_secret.to_bytes_be().into() + } + _ => { + return Err(type_error( + "Unsupported key type for diffie hellman, or key type mismatch", + )) + } + }; + + Ok(res) +} diff --git a/ext/node/ops/crypto/pkcs3.rs b/ext/node/ops/crypto/pkcs3.rs new file mode 100644 index 000000000..577251460 --- /dev/null +++ b/ext/node/ops/crypto/pkcs3.rs @@ -0,0 +1,20 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// +// PKCS #3: Diffie-Hellman Key Agreement Standard + +use der::Sequence; +use spki::der; +use spki::der::asn1; + +// The parameters fields associated with OID dhKeyAgreement +// +// DHParameter ::= SEQUENCE { +// prime INTEGER, -- p +// base INTEGER, -- g +// privateValueLength INTEGER OPTIONAL } +#[derive(Clone, Sequence)] +pub struct DhParameter { + pub prime: asn1::Int, + pub base: asn1::Int, + pub private_value_length: Option<asn1::Int>, +} diff --git a/ext/node/ops/crypto/primes.rs b/ext/node/ops/crypto/primes.rs index 5c9f4c768..2e5d1ff63 100644 --- a/ext/node/ops/crypto/primes.rs +++ b/ext/node/ops/crypto/primes.rs @@ -8,6 +8,7 @@ use num_traits::Zero; use rand::Rng; use std::ops::Deref; +#[derive(Clone)] pub struct Prime(pub num_bigint_dig::BigUint); impl Prime { |
