summaryrefslogtreecommitdiff
path: root/ext/node/ops/crypto
diff options
context:
space:
mode:
authorLuca Casonato <hello@lcas.dev>2024-08-08 11:35:29 +0200
committerGitHub <noreply@github.com>2024-08-08 15:05:29 +0530
commit93d479252b5a18e6e782c74b808240bd3ef036bd (patch)
tree4c8b83797fb0e30d1ce3fcebad3fbde6cf33ef1d /ext/node/ops/crypto
parent507e5b74ff21161ba8bd947d7d9cee317c0af379 (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.rs156
-rw-r--r--ext/node/ops/crypto/mod.rs86
-rw-r--r--ext/node/ops/crypto/pkcs3.rs20
-rw-r--r--ext/node/ops/crypto/primes.rs1
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(&params.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(&params.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, &params).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, &params).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 {