Skip to content

fix(ext/node): add crypto.diffieHellman #24938

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion ext/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ deno_net.workspace = true
deno_package_json.workspace = true
deno_permissions.workspace = true
deno_whoami = "0.1.0"
der = { version = "0.7.9", features = ["derive"] }
digest = { version = "0.10.5", features = ["core-api", "std"] }
dsa = "0.6.1"
ecb.workspace = true
ed25519-dalek = { version = "2.1.1", features = ["digest", "pkcs8", "rand_core", "signature"] }
ed25519-dalek = { version = "2.1.1", features = ["digest", "pkcs8", "rand_core", "signature" ] }
elliptic-curve.workspace = true
errno = "0.2.8"
faster-hex.workspace = true
Expand Down
57 changes: 29 additions & 28 deletions ext/node/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,46 +219,47 @@ deno_core::extension!(deno_node,

ops::buffer::op_is_ascii,
ops::buffer::op_is_utf8,
ops::crypto::op_node_create_decipheriv,
ops::crypto::op_node_check_prime_async,
ops::crypto::op_node_check_prime_bytes_async,
ops::crypto::op_node_check_prime_bytes,
ops::crypto::op_node_check_prime,
ops::crypto::op_node_cipheriv_encrypt,
ops::crypto::op_node_cipheriv_final,
ops::crypto::op_node_cipheriv_set_aad,
ops::crypto::op_node_decipheriv_set_aad,
ops::crypto::op_node_create_cipheriv,
ops::crypto::op_node_create_decipheriv,
ops::crypto::op_node_create_hash,
ops::crypto::op_node_get_hashes,
ops::crypto::op_node_decipheriv_decrypt,
ops::crypto::op_node_decipheriv_final,
ops::crypto::op_node_hash_update,
ops::crypto::op_node_hash_update_str,
ops::crypto::op_node_hash_digest,
ops::crypto::op_node_hash_digest_hex,
ops::crypto::op_node_decipheriv_set_aad,
ops::crypto::op_node_dh_compute_secret,
ops::crypto::op_node_diffie_hellman,
ops::crypto::op_node_ecdh_compute_public_key,
ops::crypto::op_node_ecdh_compute_secret,
ops::crypto::op_node_ecdh_encode_pubkey,
ops::crypto::op_node_ecdh_generate_keys,
ops::crypto::op_node_fill_random_async,
ops::crypto::op_node_fill_random,
ops::crypto::op_node_gen_prime_async,
ops::crypto::op_node_gen_prime,
ops::crypto::op_node_get_hashes,
ops::crypto::op_node_hash_clone,
ops::crypto::op_node_private_encrypt,
ops::crypto::op_node_hash_digest_hex,
ops::crypto::op_node_hash_digest,
ops::crypto::op_node_hash_update_str,
ops::crypto::op_node_hash_update,
ops::crypto::op_node_hkdf_async,
ops::crypto::op_node_hkdf,
ops::crypto::op_node_pbkdf2_async,
ops::crypto::op_node_pbkdf2,
ops::crypto::op_node_private_decrypt,
ops::crypto::op_node_private_encrypt,
ops::crypto::op_node_public_encrypt,
ops::crypto::op_node_check_prime,
ops::crypto::op_node_check_prime_async,
ops::crypto::op_node_check_prime_bytes,
ops::crypto::op_node_check_prime_bytes_async,
ops::crypto::op_node_gen_prime,
ops::crypto::op_node_gen_prime_async,
ops::crypto::op_node_pbkdf2,
ops::crypto::op_node_pbkdf2_async,
ops::crypto::op_node_hkdf,
ops::crypto::op_node_hkdf_async,
ops::crypto::op_node_fill_random,
ops::crypto::op_node_fill_random_async,
ops::crypto::op_node_sign,
ops::crypto::op_node_dh_compute_secret,
ops::crypto::op_node_verify,
ops::crypto::op_node_random_int,
ops::crypto::op_node_scrypt_sync,
ops::crypto::op_node_scrypt_async,
ops::crypto::op_node_ecdh_generate_keys,
ops::crypto::op_node_ecdh_compute_secret,
ops::crypto::op_node_ecdh_compute_public_key,
ops::crypto::op_node_ecdh_encode_pubkey,
ops::crypto::op_node_scrypt_sync,
ops::crypto::op_node_sign,
ops::crypto::op_node_verify,
ops::crypto::keys::op_node_create_private_key,
ops::crypto::keys::op_node_create_public_key,
ops::crypto::keys::op_node_create_secret_key,
Expand Down
156 changes: 123 additions & 33 deletions ext/node/ops/crypto/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 _;
Expand All @@ -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)]
Expand All @@ -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)]
Expand Down Expand Up @@ -125,17 +128,21 @@ pub enum EcPrivateKey {
P384(p384::SecretKey),
}

#[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)]
Expand All @@ -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> {
Expand Down Expand Up @@ -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.value())
.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")),
};

Expand Down Expand Up @@ -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.value())
.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")),
};
Expand Down Expand Up @@ -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"))?
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
slice.len() * std::mem::size_of::<u32>(),
)
}
}

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,
}),
))
}

Expand Down Expand Up @@ -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,
}),
))
}

Expand Down
Loading
Loading