summaryrefslogtreecommitdiff
path: root/ext/node/ops
diff options
context:
space:
mode:
Diffstat (limited to 'ext/node/ops')
-rw-r--r--ext/node/ops/crypto/dh.rs12
-rw-r--r--ext/node/ops/crypto/digest.rs45
-rw-r--r--ext/node/ops/crypto/keys.rs1727
-rw-r--r--ext/node/ops/crypto/mod.rs882
-rw-r--r--ext/node/ops/crypto/sign.rs396
-rw-r--r--ext/node/ops/vm_internal.rs1
6 files changed, 2186 insertions, 877 deletions
diff --git a/ext/node/ops/crypto/dh.rs b/ext/node/ops/crypto/dh.rs
index f60f84277..ff2bd030e 100644
--- a/ext/node/ops/crypto/dh.rs
+++ b/ext/node/ops/crypto/dh.rs
@@ -5,14 +5,21 @@ use num_bigint_dig::BigUint;
use num_bigint_dig::RandBigInt;
use num_traits::FromPrimitive;
+#[derive(Clone)]
pub struct PublicKey(BigUint);
impl PublicKey {
+ pub fn from_bytes(bytes: &[u8]) -> Self {
+ let public_key = BigUint::from_bytes_be(bytes);
+ Self(public_key)
+ }
+
pub fn into_vec(self) -> Vec<u8> {
self.0.to_bytes_be()
}
}
+#[derive(Clone)]
pub struct PrivateKey(BigUint);
impl PrivateKey {
@@ -22,6 +29,11 @@ impl PrivateKey {
Self(exponent)
}
+ pub fn from_bytes(bytes: &[u8]) -> Self {
+ let exponent = BigUint::from_bytes_be(bytes);
+ Self(exponent)
+ }
+
/// Diffie-Hellman modular exponentiation.
/// s = g^x mod p
pub fn compute_public_key(
diff --git a/ext/node/ops/crypto/digest.rs b/ext/node/ops/crypto/digest.rs
index 0a21a395a..1bb028155 100644
--- a/ext/node/ops/crypto/digest.rs
+++ b/ext/node/ops/crypto/digest.rs
@@ -67,7 +67,7 @@ macro_rules! match_fixed_digest {
type $type = ::blake2::Blake2s256;
$body
}
- _ => match_fixed_digest_with_eager_block_buffer!($algorithm_name, fn <$type>() $body, _ => $other)
+ _ => crate::ops::crypto::digest::match_fixed_digest_with_eager_block_buffer!($algorithm_name, fn <$type>() $body, _ => $other)
}
};
}
@@ -84,22 +84,24 @@ macro_rules! match_fixed_digest_with_eager_block_buffer {
type $type = crate::ops::crypto::md5_sha1::Md5Sha1;
$body
}
- _ => match_fixed_digest_with_oid!($algorithm_name, fn <$type>() $body, _ => $other)
+ _ => crate::ops::crypto::digest::match_fixed_digest_with_oid!($algorithm_name, fn <$type>() $body, _ => $other)
}
};
}
pub(crate) use match_fixed_digest_with_eager_block_buffer;
macro_rules! match_fixed_digest_with_oid {
- ($algorithm_name:expr, fn <$type:ident>() $body:block, _ => $other:block) => {
+ ($algorithm_name:expr, fn $(<$type:ident>)?($($hash_algorithm:ident: Option<RsaPssHashAlgorithm>)?) $body:block, _ => $other:block) => {
match $algorithm_name {
"rsa-md5" | "md5" | "md5withrsaencryption" | "ssl3-md5" => {
- type $type = ::md5::Md5;
+ $(let $hash_algorithm = None;)?
+ $(type $type = ::md5::Md5;)?
$body
}
"rsa-ripemd160" | "ripemd" | "ripemd160" | "ripemd160withrsa"
| "rmd160" => {
- type $type = ::ripemd::Ripemd160;
+ $(let $hash_algorithm = None;)?
+ $(type $type = ::ripemd::Ripemd160;)?
$body
}
"rsa-sha1"
@@ -108,47 +110,58 @@ macro_rules! match_fixed_digest_with_oid {
| "sha1-2"
| "sha1withrsaencryption"
| "ssl3-sha1" => {
- type $type = ::sha1::Sha1;
+ $(let $hash_algorithm = Some(RsaPssHashAlgorithm::Sha1);)?
+ $(type $type = ::sha1::Sha1;)?
$body
}
"rsa-sha224" | "sha224" | "sha224withrsaencryption" => {
- type $type = ::sha2::Sha224;
+ $(let $hash_algorithm = Some(RsaPssHashAlgorithm::Sha224);)?
+ $(type $type = ::sha2::Sha224;)?
$body
}
"rsa-sha256" | "sha256" | "sha256withrsaencryption" => {
- type $type = ::sha2::Sha256;
+ $(let $hash_algorithm = Some(RsaPssHashAlgorithm::Sha256);)?
+ $(type $type = ::sha2::Sha256;)?
$body
}
"rsa-sha384" | "sha384" | "sha384withrsaencryption" => {
- type $type = ::sha2::Sha384;
+ $(let $hash_algorithm = Some(RsaPssHashAlgorithm::Sha384);)?
+ $(type $type = ::sha2::Sha384;)?
$body
}
"rsa-sha512" | "sha512" | "sha512withrsaencryption" => {
- type $type = ::sha2::Sha512;
+ $(let $hash_algorithm = Some(RsaPssHashAlgorithm::Sha512);)?
+ $(type $type = ::sha2::Sha512;)?
$body
}
"rsa-sha512/224" | "sha512-224" | "sha512-224withrsaencryption" => {
- type $type = ::sha2::Sha512_224;
+ $(let $hash_algorithm = Some(RsaPssHashAlgorithm::Sha512_224);)?
+ $(type $type = ::sha2::Sha512_224;)?
$body
}
"rsa-sha512/256" | "sha512-256" | "sha512-256withrsaencryption" => {
- type $type = ::sha2::Sha512_256;
+ $(let $hash_algorithm = Some(RsaPssHashAlgorithm::Sha512_256);)?
+ $(type $type = ::sha2::Sha512_256;)?
$body
}
"rsa-sha3-224" | "id-rsassa-pkcs1-v1_5-with-sha3-224" | "sha3-224" => {
- type $type = ::sha3::Sha3_224;
+ $(let $hash_algorithm = None;)?
+ $(type $type = ::sha3::Sha3_224;)?
$body
}
"rsa-sha3-256" | "id-rsassa-pkcs1-v1_5-with-sha3-256" | "sha3-256" => {
- type $type = ::sha3::Sha3_256;
+ $(let $hash_algorithm = None;)?
+ $(type $type = ::sha3::Sha3_256;)?
$body
}
"rsa-sha3-384" | "id-rsassa-pkcs1-v1_5-with-sha3-384" | "sha3-384" => {
- type $type = ::sha3::Sha3_384;
+ $(let $hash_algorithm = None;)?
+ $(type $type = ::sha3::Sha3_384;)?
$body
}
"rsa-sha3-512" | "id-rsassa-pkcs1-v1_5-with-sha3-512" | "sha3-512" => {
- type $type = ::sha3::Sha3_512;
+ $(let $hash_algorithm = None;)?
+ $(type $type = ::sha3::Sha3_512;)?
$body
}
_ => $other,
diff --git a/ext/node/ops/crypto/keys.rs b/ext/node/ops/crypto/keys.rs
new file mode 100644
index 000000000..86280c11a
--- /dev/null
+++ b/ext/node/ops/crypto/keys.rs
@@ -0,0 +1,1727 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+use std::borrow::Cow;
+use std::cell::RefCell;
+
+use base64::Engine;
+use deno_core::error::generic_error;
+use deno_core::error::type_error;
+use deno_core::error::AnyError;
+use deno_core::op2;
+use deno_core::serde_v8::BigInt as V8BigInt;
+use deno_core::unsync::spawn_blocking;
+use deno_core::GarbageCollected;
+use deno_core::ToJsBuffer;
+use ed25519_dalek::pkcs8::BitStringRef;
+use num_bigint::BigInt;
+use num_traits::FromPrimitive as _;
+use pkcs8::DecodePrivateKey as _;
+use pkcs8::Document;
+use pkcs8::EncodePrivateKey as _;
+use pkcs8::EncryptedPrivateKeyInfo;
+use pkcs8::PrivateKeyInfo;
+use pkcs8::SecretDocument;
+use rand::thread_rng;
+use rand::RngCore as _;
+use rsa::pkcs1::DecodeRsaPrivateKey as _;
+use rsa::pkcs1::DecodeRsaPublicKey;
+use rsa::pkcs1::EncodeRsaPrivateKey as _;
+use rsa::pkcs1::EncodeRsaPublicKey;
+use rsa::traits::PublicKeyParts;
+use rsa::RsaPrivateKey;
+use rsa::RsaPublicKey;
+use sec1::der::Writer as _;
+use sec1::pem::PemLabel as _;
+use sec1::DecodeEcPrivateKey as _;
+use sec1::LineEnding;
+use spki::der::asn1;
+use spki::der::asn1::OctetStringRef;
+use spki::der::AnyRef;
+use spki::der::Decode as _;
+use spki::der::Encode as _;
+use spki::der::PemWriter;
+use spki::der::Reader as _;
+use spki::DecodePublicKey as _;
+use spki::EncodePublicKey as _;
+use spki::SubjectPublicKeyInfoRef;
+
+use super::dh;
+use super::digest::match_fixed_digest_with_oid;
+use super::primes::Prime;
+
+#[derive(Clone)]
+pub enum KeyObjectHandle {
+ AsymmetricPrivate(AsymmetricPrivateKey),
+ AsymmetricPublic(AsymmetricPublicKey),
+ Secret(Box<[u8]>),
+}
+
+impl GarbageCollected for KeyObjectHandle {}
+
+#[derive(Clone)]
+pub enum AsymmetricPrivateKey {
+ Rsa(RsaPrivateKey),
+ RsaPss(RsaPssPrivateKey),
+ Dsa(dsa::SigningKey),
+ Ec(EcPrivateKey),
+ X25519(x25519_dalek::StaticSecret),
+ Ed25519(ed25519_dalek::SigningKey),
+ #[allow(unused)]
+ Dh(dh::PrivateKey),
+}
+
+#[derive(Clone)]
+pub struct RsaPssPrivateKey {
+ pub key: RsaPrivateKey,
+ pub details: Option<RsaPssDetails>,
+}
+
+#[derive(Clone, Copy)]
+pub struct RsaPssDetails {
+ pub hash_algorithm: RsaPssHashAlgorithm,
+ pub mf1_hash_algorithm: RsaPssHashAlgorithm,
+ pub salt_length: u32,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub enum RsaPssHashAlgorithm {
+ Sha1,
+ Sha224,
+ Sha256,
+ Sha384,
+ Sha512,
+ Sha512_224,
+ Sha512_256,
+}
+
+impl RsaPssHashAlgorithm {
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ RsaPssHashAlgorithm::Sha1 => "sha1",
+ RsaPssHashAlgorithm::Sha224 => "sha224",
+ RsaPssHashAlgorithm::Sha256 => "sha256",
+ RsaPssHashAlgorithm::Sha384 => "sha384",
+ RsaPssHashAlgorithm::Sha512 => "sha512",
+ RsaPssHashAlgorithm::Sha512_224 => "sha512-224",
+ RsaPssHashAlgorithm::Sha512_256 => "sha512-256",
+ }
+ }
+
+ pub fn salt_length(&self) -> u32 {
+ match self {
+ RsaPssHashAlgorithm::Sha1 => 20,
+ RsaPssHashAlgorithm::Sha224 | RsaPssHashAlgorithm::Sha512_224 => 28,
+ RsaPssHashAlgorithm::Sha256 | RsaPssHashAlgorithm::Sha512_256 => 32,
+ RsaPssHashAlgorithm::Sha384 => 48,
+ RsaPssHashAlgorithm::Sha512 => 64,
+ }
+ }
+}
+
+#[derive(Clone)]
+pub enum EcPrivateKey {
+ P224(p224::SecretKey),
+ P256(p256::SecretKey),
+ P384(p384::SecretKey),
+}
+
+#[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),
+}
+
+#[derive(Clone)]
+pub struct RsaPssPublicKey {
+ pub key: rsa::RsaPublicKey,
+ pub details: Option<RsaPssDetails>,
+}
+
+#[derive(Clone)]
+pub enum EcPublicKey {
+ P224(p224::PublicKey),
+ P256(p256::PublicKey),
+ P384(p384::PublicKey),
+}
+
+impl KeyObjectHandle {
+ /// Returns the private key if the handle is an asymmetric private key.
+ pub fn as_private_key(&self) -> Option<&AsymmetricPrivateKey> {
+ match self {
+ KeyObjectHandle::AsymmetricPrivate(key) => Some(key),
+ _ => None,
+ }
+ }
+
+ /// Returns the public key if the handle is an asymmetric public key. If it is
+ /// a private key, it derives the public key from it and returns that.
+ pub fn as_public_key(&self) -> Option<Cow<'_, AsymmetricPublicKey>> {
+ match self {
+ KeyObjectHandle::AsymmetricPrivate(key) => {
+ Some(Cow::Owned(key.to_public_key()))
+ }
+ KeyObjectHandle::AsymmetricPublic(key) => Some(Cow::Borrowed(key)),
+ _ => None,
+ }
+ }
+
+ /// Returns the secret key if the handle is a secret key.
+ pub fn as_secret_key(&self) -> Option<&[u8]> {
+ match self {
+ KeyObjectHandle::Secret(key) => Some(key),
+ _ => None,
+ }
+ }
+}
+
+impl AsymmetricPrivateKey {
+ /// Derives the public key from the private key.
+ pub fn to_public_key(&self) -> AsymmetricPublicKey {
+ match self {
+ AsymmetricPrivateKey::Rsa(key) => {
+ AsymmetricPublicKey::Rsa(key.to_public_key())
+ }
+ AsymmetricPrivateKey::RsaPss(key) => {
+ AsymmetricPublicKey::RsaPss(key.to_public_key())
+ }
+ AsymmetricPrivateKey::Dsa(key) => {
+ AsymmetricPublicKey::Dsa(key.verifying_key().clone())
+ }
+ AsymmetricPrivateKey::Ec(key) => {
+ AsymmetricPublicKey::Ec(key.to_public_key())
+ }
+ AsymmetricPrivateKey::X25519(key) => {
+ AsymmetricPublicKey::X25519(x25519_dalek::PublicKey::from(key))
+ }
+ AsymmetricPrivateKey::Ed25519(key) => {
+ AsymmetricPublicKey::Ed25519(key.verifying_key())
+ }
+ AsymmetricPrivateKey::Dh(_) => {
+ panic!("cannot derive public key from DH private key")
+ }
+ }
+ }
+}
+
+impl RsaPssPrivateKey {
+ /// Derives the public key from the private key.
+ pub fn to_public_key(&self) -> RsaPssPublicKey {
+ RsaPssPublicKey {
+ key: self.key.to_public_key(),
+ details: self.details,
+ }
+ }
+}
+
+impl EcPrivateKey {
+ /// Derives the public key from the private key.
+ pub fn to_public_key(&self) -> EcPublicKey {
+ match self {
+ EcPrivateKey::P224(key) => EcPublicKey::P224(key.public_key()),
+ EcPrivateKey::P256(key) => EcPublicKey::P256(key.public_key()),
+ EcPrivateKey::P384(key) => EcPublicKey::P384(key.public_key()),
+ }
+ }
+}
+
+// https://oidref.com/
+const ID_SHA1_OID: rsa::pkcs8::ObjectIdentifier =
+ rsa::pkcs8::ObjectIdentifier::new_unwrap("1.3.14.3.2.26");
+const ID_SHA224_OID: rsa::pkcs8::ObjectIdentifier =
+ rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.4");
+const ID_SHA256_OID: rsa::pkcs8::ObjectIdentifier =
+ rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1");
+const ID_SHA384_OID: rsa::pkcs8::ObjectIdentifier =
+ rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2");
+const ID_SHA512_OID: rsa::pkcs8::ObjectIdentifier =
+ rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.3");
+const ID_SHA512_224_OID: rsa::pkcs8::ObjectIdentifier =
+ rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.5");
+const ID_SHA512_256_OID: rsa::pkcs8::ObjectIdentifier =
+ rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.6");
+
+const ID_MFG1: rsa::pkcs8::ObjectIdentifier =
+ rsa::pkcs8::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.8");
+pub const ID_SECP224R1_OID: const_oid::ObjectIdentifier =
+ const_oid::ObjectIdentifier::new_unwrap("1.3.132.0.33");
+pub const ID_SECP256R1_OID: const_oid::ObjectIdentifier =
+ const_oid::ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");
+pub const ID_SECP384R1_OID: const_oid::ObjectIdentifier =
+ const_oid::ObjectIdentifier::new_unwrap("1.3.132.0.34");
+
+pub const RSA_ENCRYPTION_OID: const_oid::ObjectIdentifier =
+ const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1");
+pub const RSASSA_PSS_OID: const_oid::ObjectIdentifier =
+ const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10");
+pub const DSA_OID: const_oid::ObjectIdentifier =
+ const_oid::ObjectIdentifier::new_unwrap("1.2.840.10040.4.1");
+pub const EC_OID: const_oid::ObjectIdentifier =
+ const_oid::ObjectIdentifier::new_unwrap("1.2.840.10045.2.1");
+pub const X25519_OID: const_oid::ObjectIdentifier =
+ const_oid::ObjectIdentifier::new_unwrap("1.3.101.110");
+pub const ED25519_OID: const_oid::ObjectIdentifier =
+ const_oid::ObjectIdentifier::new_unwrap("1.3.101.112");
+pub const DH_KEY_AGREEMENT_OID: const_oid::ObjectIdentifier =
+ const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.3.1");
+
+// The parameters field associated with OID id-RSASSA-PSS
+// Defined in RFC 3447, section A.2.3
+//
+// RSASSA-PSS-params ::= SEQUENCE {
+// hashAlgorithm [0] HashAlgorithm DEFAULT sha1,
+// maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1,
+// saltLength [2] INTEGER DEFAULT 20,
+// trailerField [3] TrailerField DEFAULT trailerFieldBC
+// }
+pub struct RsaPssParameters<'a> {
+ pub hash_algorithm: Option<rsa::pkcs8::AlgorithmIdentifierRef<'a>>,
+ pub mask_gen_algorithm: Option<rsa::pkcs8::AlgorithmIdentifierRef<'a>>,
+ pub salt_length: Option<u32>,
+}
+
+// Context-specific tag number for hashAlgorithm.
+const HASH_ALGORITHM_TAG: rsa::pkcs8::der::TagNumber =
+ rsa::pkcs8::der::TagNumber::new(0);
+
+// Context-specific tag number for maskGenAlgorithm.
+const MASK_GEN_ALGORITHM_TAG: rsa::pkcs8::der::TagNumber =
+ rsa::pkcs8::der::TagNumber::new(1);
+
+// Context-specific tag number for saltLength.
+const SALT_LENGTH_TAG: rsa::pkcs8::der::TagNumber =
+ rsa::pkcs8::der::TagNumber::new(2);
+
+impl<'a> TryFrom<rsa::pkcs8::der::asn1::AnyRef<'a>> for RsaPssParameters<'a> {
+ type Error = rsa::pkcs8::der::Error;
+
+ fn try_from(
+ any: rsa::pkcs8::der::asn1::AnyRef<'a>,
+ ) -> rsa::pkcs8::der::Result<RsaPssParameters> {
+ any.sequence(|decoder| {
+ let hash_algorithm = decoder
+ .context_specific::<rsa::pkcs8::AlgorithmIdentifierRef>(
+ HASH_ALGORITHM_TAG,
+ pkcs8::der::TagMode::Explicit,
+ )?
+ .map(TryInto::try_into)
+ .transpose()?;
+
+ let mask_gen_algorithm = decoder
+ .context_specific::<rsa::pkcs8::AlgorithmIdentifierRef>(
+ MASK_GEN_ALGORITHM_TAG,
+ pkcs8::der::TagMode::Explicit,
+ )?
+ .map(TryInto::try_into)
+ .transpose()?;
+
+ let salt_length = decoder
+ .context_specific::<u32>(
+ SALT_LENGTH_TAG,
+ pkcs8::der::TagMode::Explicit,
+ )?
+ .map(TryInto::try_into)
+ .transpose()?;
+
+ Ok(Self {
+ hash_algorithm,
+ mask_gen_algorithm,
+ salt_length,
+ })
+ })
+ }
+}
+
+impl KeyObjectHandle {
+ pub fn new_asymmetric_private_key_from_js(
+ key: &[u8],
+ format: &str,
+ typ: &str,
+ passphrase: Option<&[u8]>,
+ ) -> Result<KeyObjectHandle, AnyError> {
+ let document = match format {
+ "pem" => {
+ let pem = std::str::from_utf8(key).map_err(|err| {
+ type_error(format!(
+ "invalid PEM private key: not valid utf8 starting at byte {}",
+ err.valid_up_to()
+ ))
+ })?;
+
+ if let Some(passphrase) = passphrase {
+ SecretDocument::from_pkcs8_encrypted_pem(pem, passphrase)
+ .map_err(|_| type_error("invalid encrypted PEM private key"))?
+ } else {
+ let (label, doc) = SecretDocument::from_pem(pem)
+ .map_err(|_| type_error("invalid PEM private key"))?;
+
+ match label {
+ EncryptedPrivateKeyInfo::PEM_LABEL => {
+ return Err(type_error(
+ "encrypted private key requires a passphrase to decrypt",
+ ))
+ }
+ PrivateKeyInfo::PEM_LABEL => doc,
+ rsa::pkcs1::RsaPrivateKey::PEM_LABEL => {
+ SecretDocument::from_pkcs1_der(doc.as_bytes())
+ .map_err(|_| type_error("invalid PKCS#1 private key"))?
+ }
+ sec1::EcPrivateKey::PEM_LABEL => {
+ SecretDocument::from_sec1_der(doc.as_bytes())
+ .map_err(|_| type_error("invalid SEC1 private key"))?
+ }
+ _ => {
+ return Err(type_error(format!(
+ "unsupported PEM label: {}",
+ label
+ )))
+ }
+ }
+ }
+ }
+ "der" => match typ {
+ "pkcs8" => {
+ if let Some(passphrase) = passphrase {
+ SecretDocument::from_pkcs8_encrypted_der(key, passphrase)
+ .map_err(|_| type_error("invalid encrypted PKCS#8 private key"))?
+ } else {
+ SecretDocument::from_pkcs8_der(key)
+ .map_err(|_| type_error("invalid PKCS#8 private key"))?
+ }
+ }
+ "pkcs1" => {
+ if passphrase.is_some() {
+ return Err(type_error(
+ "PKCS#1 private key does not support encryption with passphrase",
+ ));
+ }
+ SecretDocument::from_pkcs1_der(key)
+ .map_err(|_| type_error("invalid PKCS#1 private key"))?
+ }
+ "sec1" => {
+ if passphrase.is_some() {
+ return Err(type_error(
+ "SEC1 private key does not support encryption with passphrase",
+ ));
+ }
+ SecretDocument::from_sec1_der(key)
+ .map_err(|_| type_error("invalid SEC1 private key"))?
+ }
+ _ => return Err(type_error(format!("unsupported key type: {}", typ))),
+ },
+ _ => {
+ return Err(type_error(format!("unsupported key format: {}", format)))
+ }
+ };
+
+ let pk_info = PrivateKeyInfo::try_from(document.as_bytes())
+ .map_err(|_| type_error("invalid private key"))?;
+
+ let alg = pk_info.algorithm.oid;
+ let private_key = match alg {
+ RSA_ENCRYPTION_OID => {
+ let private_key =
+ rsa::RsaPrivateKey::from_pkcs1_der(pk_info.private_key)
+ .map_err(|_| type_error("invalid PKCS#1 private key"))?;
+ AsymmetricPrivateKey::Rsa(private_key)
+ }
+ RSASSA_PSS_OID => {
+ let details = parse_rsa_pss_params(pk_info.algorithm.parameters)?;
+ let private_key =
+ rsa::RsaPrivateKey::from_pkcs1_der(pk_info.private_key)
+ .map_err(|_| type_error("invalid PKCS#1 private key"))?;
+ AsymmetricPrivateKey::RsaPss(RsaPssPrivateKey {
+ key: private_key,
+ details,
+ })
+ }
+ DSA_OID => {
+ let private_key = dsa::SigningKey::try_from(pk_info)
+ .map_err(|_| type_error("invalid DSA private key"))?;
+ AsymmetricPrivateKey::Dsa(private_key)
+ }
+ EC_OID => {
+ let named_curve = pk_info.algorithm.parameters_oid().map_err(|_| {
+ type_error("malformed or missing named curve in ec parameters")
+ })?;
+ match named_curve {
+ ID_SECP224R1_OID => {
+ let secret_key =
+ p224::SecretKey::from_sec1_der(pk_info.private_key)
+ .map_err(|_| type_error("invalid SEC1 private key"))?;
+ AsymmetricPrivateKey::Ec(EcPrivateKey::P224(secret_key))
+ }
+ ID_SECP256R1_OID => {
+ let secret_key =
+ p256::SecretKey::from_sec1_der(pk_info.private_key)
+ .map_err(|_| type_error("invalid SEC1 private key"))?;
+ AsymmetricPrivateKey::Ec(EcPrivateKey::P256(secret_key))
+ }
+ ID_SECP384R1_OID => {
+ let secret_key =
+ p384::SecretKey::from_sec1_der(pk_info.private_key)
+ .map_err(|_| type_error("invalid SEC1 private key"))?;
+ AsymmetricPrivateKey::Ec(EcPrivateKey::P384(secret_key))
+ }
+ _ => return Err(type_error("unsupported ec named curve")),
+ }
+ }
+ X25519_OID => {
+ let string_ref = OctetStringRef::from_der(pk_info.private_key)
+ .map_err(|_| type_error("invalid x25519 private key"))?;
+ if string_ref.as_bytes().len() != 32 {
+ return Err(type_error("x25519 private key is the wrong length"));
+ }
+ let mut bytes = [0; 32];
+ bytes.copy_from_slice(string_ref.as_bytes());
+ AsymmetricPrivateKey::X25519(x25519_dalek::StaticSecret::from(bytes))
+ }
+ ED25519_OID => {
+ let string_ref = OctetStringRef::from_der(pk_info.private_key)
+ .map_err(|_| type_error("invalid Ed25519 private key"))?;
+ if string_ref.as_bytes().len() != 32 {
+ return Err(type_error("Ed25519 private key is the wrong length"));
+ }
+ let mut bytes = [0; 32];
+ 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),
+ ),
+ _ => return Err(type_error("unsupported private key oid")),
+ };
+
+ Ok(KeyObjectHandle::AsymmetricPrivate(private_key))
+ }
+
+ pub fn new_asymmetric_public_key_from_js(
+ key: &[u8],
+ format: &str,
+ typ: &str,
+ _passphrase: Option<&[u8]>,
+ ) -> Result<KeyObjectHandle, AnyError> {
+ let document = match format {
+ "pem" => {
+ let pem = std::str::from_utf8(key).map_err(|err| {
+ type_error(format!(
+ "invalid PEM public key: not valid utf8 starting at byte {}",
+ err.valid_up_to()
+ ))
+ })?;
+
+ let (label, document) = Document::from_pem(pem)
+ .map_err(|_| type_error("invalid PEM public key"))?;
+
+ match label {
+ SubjectPublicKeyInfoRef::PEM_LABEL => document,
+ rsa::pkcs1::RsaPublicKey::PEM_LABEL => {
+ Document::from_pkcs1_der(document.as_bytes())
+ .map_err(|_| type_error("invalid PKCS#1 public key"))?
+ }
+ EncryptedPrivateKeyInfo::PEM_LABEL => {
+ // FIXME
+ return Err(type_error(
+ "deriving public key from encrypted private key",
+ ));
+ }
+ PrivateKeyInfo::PEM_LABEL => {
+ // FIXME
+ return Err(type_error("public key cannot be a private key"));
+ }
+ sec1::EcPrivateKey::PEM_LABEL => {
+ // FIXME
+ return Err(type_error("deriving public key from ec private key"));
+ }
+ rsa::pkcs1::RsaPrivateKey::PEM_LABEL => {
+ // FIXME
+ return Err(type_error("deriving public key from rsa private key"));
+ }
+ // TODO: handle x509 certificates as public keys
+ _ => {
+ return Err(type_error(format!("unsupported PEM label: {}", label)))
+ }
+ }
+ }
+ "der" => match typ {
+ "pkcs1" => Document::from_pkcs1_der(key)
+ .map_err(|_| type_error("invalid PKCS#1 public key"))?,
+ "spki" => Document::from_public_key_der(key)
+ .map_err(|_| type_error("invalid SPKI public key"))?,
+ _ => return Err(type_error(format!("unsupported key type: {}", typ))),
+ },
+ _ => {
+ return Err(type_error(format!("unsupported key format: {}", format)))
+ }
+ };
+
+ let spki = SubjectPublicKeyInfoRef::try_from(document.as_bytes())?;
+
+ let public_key = match spki.algorithm.oid {
+ RSA_ENCRYPTION_OID => {
+ let public_key = RsaPublicKey::from_pkcs1_der(
+ spki.subject_public_key.as_bytes().unwrap(),
+ )?;
+ AsymmetricPublicKey::Rsa(public_key)
+ }
+ RSASSA_PSS_OID => {
+ let details = parse_rsa_pss_params(spki.algorithm.parameters)?;
+ let public_key = RsaPublicKey::from_pkcs1_der(
+ spki.subject_public_key.as_bytes().unwrap(),
+ )?;
+ AsymmetricPublicKey::RsaPss(RsaPssPublicKey {
+ key: public_key,
+ details,
+ })
+ }
+ DSA_OID => {
+ let verifying_key = dsa::VerifyingKey::try_from(spki)
+ .map_err(|_| type_error("malformed DSS public key"))?;
+ AsymmetricPublicKey::Dsa(verifying_key)
+ }
+ EC_OID => {
+ let named_curve = spki.algorithm.parameters_oid().map_err(|_| {
+ type_error("malformed or missing named curve in ec parameters")
+ })?;
+ let data = spki.subject_public_key.as_bytes().ok_or_else(|| {
+ type_error("malformed or missing public key in ec spki")
+ })?;
+
+ match named_curve {
+ ID_SECP224R1_OID => {
+ let public_key = p224::PublicKey::from_sec1_bytes(data)?;
+ AsymmetricPublicKey::Ec(EcPublicKey::P224(public_key))
+ }
+ ID_SECP256R1_OID => {
+ let public_key = p256::PublicKey::from_sec1_bytes(data)?;
+ AsymmetricPublicKey::Ec(EcPublicKey::P256(public_key))
+ }
+ ID_SECP384R1_OID => {
+ let public_key = p384::PublicKey::from_sec1_bytes(data)?;
+ AsymmetricPublicKey::Ec(EcPublicKey::P384(public_key))
+ }
+ _ => return Err(type_error("unsupported ec named curve")),
+ }
+ }
+ X25519_OID => {
+ let mut bytes = [0; 32];
+ let data = spki.subject_public_key.as_bytes().ok_or_else(|| {
+ type_error("malformed or missing public key in x25519 spki")
+ })?;
+ if data.len() < 32 {
+ return Err(type_error("x25519 public key is too short"));
+ }
+ bytes.copy_from_slice(&data[0..32]);
+ AsymmetricPublicKey::X25519(x25519_dalek::PublicKey::from(bytes))
+ }
+ ED25519_OID => {
+ let mut bytes = [0; 32];
+ let data = spki.subject_public_key.as_bytes().ok_or_else(|| {
+ type_error("malformed or missing public key in ed25519 spki")
+ })?;
+ if data.len() < 32 {
+ return Err(type_error("ed25519 public key is too short"));
+ }
+ bytes.copy_from_slice(&data[0..32]);
+ let verifying_key = ed25519_dalek::VerifyingKey::from_bytes(&bytes)
+ .map_err(|_| type_error("ed25519 public key is malformed"))?;
+ AsymmetricPublicKey::Ed25519(verifying_key)
+ }
+ DH_KEY_AGREEMENT_OID => {
+ 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))
+ }
+ _ => return Err(type_error("unsupported public key oid")),
+ };
+
+ Ok(KeyObjectHandle::AsymmetricPublic(public_key))
+ }
+}
+
+fn parse_rsa_pss_params(
+ parameters: Option<AnyRef<'_>>,
+) -> Result<Option<RsaPssDetails>, deno_core::anyhow::Error> {
+ let details = if let Some(parameters) = parameters {
+ let params = RsaPssParameters::try_from(parameters)
+ .map_err(|_| type_error("malformed pss private key parameters"))?;
+
+ let hash_algorithm = match params.hash_algorithm.map(|k| k.oid) {
+ Some(ID_SHA1_OID) => RsaPssHashAlgorithm::Sha1,
+ Some(ID_SHA224_OID) => RsaPssHashAlgorithm::Sha224,
+ Some(ID_SHA256_OID) => RsaPssHashAlgorithm::Sha256,
+ Some(ID_SHA384_OID) => RsaPssHashAlgorithm::Sha384,
+ Some(ID_SHA512_OID) => RsaPssHashAlgorithm::Sha512,
+ Some(ID_SHA512_224_OID) => RsaPssHashAlgorithm::Sha512_224,
+ Some(ID_SHA512_256_OID) => RsaPssHashAlgorithm::Sha512_256,
+ None => RsaPssHashAlgorithm::Sha1,
+ _ => return Err(type_error("unsupported pss hash algorithm")),
+ };
+
+ let mf1_hash_algorithm = match params.mask_gen_algorithm {
+ Some(alg) => {
+ if alg.oid != ID_MFG1 {
+ return Err(type_error("unsupported pss mask gen algorithm"));
+ }
+ let params = alg.parameters_oid().map_err(|_| {
+ type_error("malformed or missing pss mask gen algorithm parameters")
+ })?;
+ match params {
+ ID_SHA1_OID => RsaPssHashAlgorithm::Sha1,
+ ID_SHA224_OID => RsaPssHashAlgorithm::Sha224,
+ ID_SHA256_OID => RsaPssHashAlgorithm::Sha256,
+ ID_SHA384_OID => RsaPssHashAlgorithm::Sha384,
+ ID_SHA512_OID => RsaPssHashAlgorithm::Sha512,
+ ID_SHA512_224_OID => RsaPssHashAlgorithm::Sha512_224,
+ ID_SHA512_256_OID => RsaPssHashAlgorithm::Sha512_256,
+ _ => return Err(type_error("unsupported pss mask gen algorithm")),
+ }
+ }
+ None => hash_algorithm,
+ };
+
+ let salt_length = params
+ .salt_length
+ .unwrap_or_else(|| hash_algorithm.salt_length());
+
+ Some(RsaPssDetails {
+ hash_algorithm,
+ mf1_hash_algorithm,
+ salt_length,
+ })
+ } else {
+ None
+ };
+ Ok(details)
+}
+
+impl AsymmetricPublicKey {
+ fn export_der(&self, typ: &str) -> Result<Box<[u8]>, AnyError> {
+ match typ {
+ "pkcs1" => match self {
+ AsymmetricPublicKey::Rsa(key) => {
+ let der = key
+ .to_pkcs1_der()
+ .map_err(|_| type_error("invalid RSA public key"))?
+ .into_vec()
+ .into_boxed_slice();
+ Ok(der)
+ }
+ _ => Err(type_error(
+ "exporting non-RSA public key as PKCS#1 is not supported",
+ )),
+ },
+ "spki" => {
+ let der = match self {
+ AsymmetricPublicKey::Rsa(key) => key
+ .to_public_key_der()
+ .map_err(|_| type_error("invalid RSA public key"))?
+ .into_vec()
+ .into_boxed_slice(),
+ AsymmetricPublicKey::RsaPss(_key) => {
+ return Err(generic_error(
+ "exporting RSA-PSS public key as SPKI is not supported yet",
+ ))
+ }
+ AsymmetricPublicKey::Dsa(key) => key
+ .to_public_key_der()
+ .map_err(|_| type_error("invalid DSA public key"))?
+ .into_vec()
+ .into_boxed_slice(),
+ AsymmetricPublicKey::Ec(key) => {
+ let (sec1, oid) = match key {
+ EcPublicKey::P224(key) => (key.to_sec1_bytes(), ID_SECP224R1_OID),
+ EcPublicKey::P256(key) => (key.to_sec1_bytes(), ID_SECP256R1_OID),
+ EcPublicKey::P384(key) => (key.to_sec1_bytes(), ID_SECP384R1_OID),
+ };
+
+ let spki = SubjectPublicKeyInfoRef {
+ algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
+ oid: EC_OID,
+ parameters: Some(asn1::AnyRef::from(&oid)),
+ },
+ subject_public_key: BitStringRef::from_bytes(&sec1)
+ .map_err(|_| type_error("invalid EC public key"))?,
+ };
+
+ spki
+ .to_der()
+ .map_err(|_| type_error("invalid EC public key"))?
+ .into_boxed_slice()
+ }
+ AsymmetricPublicKey::X25519(key) => {
+ let spki = SubjectPublicKeyInfoRef {
+ algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
+ oid: X25519_OID,
+ parameters: None,
+ },
+ subject_public_key: BitStringRef::from_bytes(key.as_bytes())
+ .map_err(|_| type_error("invalid X25519 public key"))?,
+ };
+
+ spki
+ .to_der()
+ .map_err(|_| type_error("invalid X25519 public key"))?
+ .into_boxed_slice()
+ }
+ AsymmetricPublicKey::Ed25519(key) => {
+ let spki = SubjectPublicKeyInfoRef {
+ algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
+ oid: ED25519_OID,
+ parameters: None,
+ },
+ subject_public_key: BitStringRef::from_bytes(key.as_bytes())
+ .map_err(|_| type_error("invalid Ed25519 public key"))?,
+ };
+
+ spki
+ .to_der()
+ .map_err(|_| type_error("invalid Ed25519 public key"))?
+ .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"))?,
+ };
+ spki
+ .to_der()
+ .map_err(|_| type_error("invalid DH public key"))?
+ .into_boxed_slice()
+ }
+ };
+ Ok(der)
+ }
+ _ => Err(type_error(format!("unsupported key type: {}", typ))),
+ }
+ }
+}
+
+impl AsymmetricPrivateKey {
+ fn export_der(
+ &self,
+ typ: &str,
+ // cipher: Option<&str>,
+ // passphrase: Option<&str>,
+ ) -> Result<Box<[u8]>, AnyError> {
+ match typ {
+ "pkcs1" => match self {
+ AsymmetricPrivateKey::Rsa(key) => {
+ let der = key
+ .to_pkcs1_der()
+ .map_err(|_| type_error("invalid RSA private key"))?
+ .to_bytes()
+ .to_vec()
+ .into_boxed_slice();
+ Ok(der)
+ }
+ _ => Err(type_error(
+ "exporting non-RSA private key as PKCS#1 is not supported",
+ )),
+ },
+ "sec1" => match self {
+ AsymmetricPrivateKey::Ec(key) => {
+ let sec1 = match key {
+ EcPrivateKey::P224(key) => key.to_sec1_der(),
+ EcPrivateKey::P256(key) => key.to_sec1_der(),
+ EcPrivateKey::P384(key) => key.to_sec1_der(),
+ }
+ .map_err(|_| type_error("invalid EC private key"))?;
+ Ok(sec1.to_vec().into_boxed_slice())
+ }
+ _ => Err(type_error(
+ "exporting non-EC private key as SEC1 is not supported",
+ )),
+ },
+ "pkcs8" => {
+ let der = match self {
+ AsymmetricPrivateKey::Rsa(key) => {
+ let document = key
+ .to_pkcs8_der()
+ .map_err(|_| type_error("invalid RSA private key"))?;
+ document.to_bytes().to_vec().into_boxed_slice()
+ }
+ AsymmetricPrivateKey::RsaPss(_key) => {
+ return Err(generic_error(
+ "exporting RSA-PSS private key as PKCS#8 is not supported yet",
+ ))
+ }
+ AsymmetricPrivateKey::Dsa(key) => {
+ let document = key
+ .to_pkcs8_der()
+ .map_err(|_| type_error("invalid DSA private key"))?;
+ document.to_bytes().to_vec().into_boxed_slice()
+ }
+ AsymmetricPrivateKey::Ec(key) => {
+ let document = match key {
+ EcPrivateKey::P224(key) => key.to_pkcs8_der(),
+ EcPrivateKey::P256(key) => key.to_pkcs8_der(),
+ EcPrivateKey::P384(key) => key.to_pkcs8_der(),
+ }
+ .map_err(|_| type_error("invalid EC private key"))?;
+ document.to_bytes().to_vec().into_boxed_slice()
+ }
+ AsymmetricPrivateKey::X25519(key) => {
+ let private_key = OctetStringRef::new(key.as_bytes())
+ .map_err(|_| type_error("invalid X25519 private key"))?
+ .to_der()
+ .map_err(|_| type_error("invalid X25519 private key"))?;
+
+ let private_key = PrivateKeyInfo {
+ algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
+ oid: X25519_OID,
+ parameters: None,
+ },
+ private_key: &private_key,
+ public_key: None,
+ };
+
+ let der = private_key
+ .to_der()
+ .map_err(|_| type_error("invalid X25519 private key"))?
+ .into_boxed_slice();
+ return Ok(der);
+ }
+ AsymmetricPrivateKey::Ed25519(key) => {
+ let private_key = OctetStringRef::new(key.as_bytes())
+ .map_err(|_| type_error("invalid Ed25519 private key"))?
+ .to_der()
+ .map_err(|_| type_error("invalid Ed25519 private key"))?;
+
+ let private_key = PrivateKeyInfo {
+ algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
+ oid: ED25519_OID,
+ parameters: None,
+ },
+ private_key: &private_key,
+ public_key: None,
+ };
+
+ private_key
+ .to_der()
+ .map_err(|_| type_error("invalid ED25519 private key"))?
+ .into_boxed_slice()
+ }
+ AsymmetricPrivateKey::Dh(key) => {
+ let private_key = key.clone().into_vec();
+ let private_key = PrivateKeyInfo {
+ algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
+ oid: DH_KEY_AGREEMENT_OID,
+ parameters: None,
+ },
+ private_key: &private_key,
+ public_key: None,
+ };
+
+ private_key
+ .to_der()
+ .map_err(|_| type_error("invalid DH private key"))?
+ .into_boxed_slice()
+ }
+ };
+
+ Ok(der)
+ }
+ _ => Err(type_error(format!("unsupported key type: {}", typ))),
+ }
+ }
+}
+
+#[op2]
+#[cppgc]
+pub fn op_node_create_private_key(
+ #[buffer] key: &[u8],
+ #[string] format: &str,
+ #[string] typ: &str,
+ #[buffer] passphrase: Option<&[u8]>,
+) -> Result<KeyObjectHandle, AnyError> {
+ KeyObjectHandle::new_asymmetric_private_key_from_js(
+ key, format, typ, passphrase,
+ )
+}
+
+#[op2]
+#[cppgc]
+pub fn op_node_create_public_key(
+ #[buffer] key: &[u8],
+ #[string] format: &str,
+ #[string] typ: &str,
+ #[buffer] passphrase: Option<&[u8]>,
+) -> Result<KeyObjectHandle, AnyError> {
+ KeyObjectHandle::new_asymmetric_public_key_from_js(
+ key, format, typ, passphrase,
+ )
+}
+
+#[op2]
+#[cppgc]
+pub fn op_node_create_secret_key(
+ #[buffer(copy)] key: Box<[u8]>,
+) -> KeyObjectHandle {
+ KeyObjectHandle::Secret(key)
+}
+
+#[op2]
+#[string]
+pub fn op_node_get_asymmetric_key_type(
+ #[cppgc] handle: &KeyObjectHandle,
+) -> Result<&'static str, AnyError> {
+ match handle {
+ KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Rsa(_))
+ | KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Rsa(_)) => {
+ Ok("rsa")
+ }
+ KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::RsaPss(_))
+ | KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::RsaPss(_)) => {
+ Ok("rsa-pss")
+ }
+ KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Dsa(_))
+ | KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Dsa(_)) => {
+ Ok("dsa")
+ }
+ KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Ec(_))
+ | KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Ec(_)) => Ok("ec"),
+ KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::X25519(_))
+ | KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::X25519(_)) => {
+ Ok("x25519")
+ }
+ KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Ed25519(_))
+ | KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Ed25519(_)) => {
+ Ok("ed25519")
+ }
+ KeyObjectHandle::AsymmetricPrivate(AsymmetricPrivateKey::Dh(_))
+ | KeyObjectHandle::AsymmetricPublic(AsymmetricPublicKey::Dh(_)) => Ok("dh"),
+ KeyObjectHandle::Secret(_) => {
+ Err(type_error("symmetric key is not an asymmetric key"))
+ }
+ }
+}
+
+#[derive(serde::Serialize)]
+#[serde(untagged)]
+pub enum AsymmetricKeyDetails {
+ #[serde(rename_all = "camelCase")]
+ Rsa {
+ modulus_length: usize,
+ public_exponent: V8BigInt,
+ },
+ #[serde(rename_all = "camelCase")]
+ RsaPss {
+ modulus_length: usize,
+ public_exponent: V8BigInt,
+ hash_algorithm: &'static str,
+ mgf1_hash_algorithm: &'static str,
+ salt_length: u32,
+ },
+ #[serde(rename = "rsaPss")]
+ RsaPssBasic {
+ modulus_length: usize,
+ public_exponent: V8BigInt,
+ },
+ #[serde(rename_all = "camelCase")]
+ Dsa {
+ modulus_length: usize,
+ divisor_length: usize,
+ },
+ #[serde(rename_all = "camelCase")]
+ Ec {
+ named_curve: &'static str,
+ },
+ X25519,
+ Ed25519,
+ Dh,
+}
+
+#[op2]
+#[serde]
+pub fn op_node_get_asymmetric_key_details(
+ #[cppgc] handle: &KeyObjectHandle,
+) -> Result<AsymmetricKeyDetails, AnyError> {
+ match handle {
+ KeyObjectHandle::AsymmetricPrivate(private_key) => match private_key {
+ AsymmetricPrivateKey::Rsa(key) => {
+ let modulus_length = key.n().bits();
+ let public_exponent =
+ BigInt::from_bytes_be(num_bigint::Sign::Plus, &key.e().to_bytes_be());
+ Ok(AsymmetricKeyDetails::Rsa {
+ modulus_length,
+ public_exponent: V8BigInt::from(public_exponent),
+ })
+ }
+ AsymmetricPrivateKey::RsaPss(key) => {
+ let modulus_length = key.key.n().bits();
+ let public_exponent = BigInt::from_bytes_be(
+ num_bigint::Sign::Plus,
+ &key.key.e().to_bytes_be(),
+ );
+ let public_exponent = V8BigInt::from(public_exponent);
+ let details = match key.details {
+ Some(details) => AsymmetricKeyDetails::RsaPss {
+ modulus_length,
+ public_exponent,
+ hash_algorithm: details.hash_algorithm.as_str(),
+ mgf1_hash_algorithm: details.mf1_hash_algorithm.as_str(),
+ salt_length: details.salt_length,
+ },
+ None => AsymmetricKeyDetails::RsaPssBasic {
+ modulus_length,
+ public_exponent,
+ },
+ };
+ Ok(details)
+ }
+ AsymmetricPrivateKey::Dsa(key) => {
+ let components = key.verifying_key().components();
+ let modulus_length = components.p().bits();
+ let divisor_length = components.q().bits();
+ Ok(AsymmetricKeyDetails::Dsa {
+ modulus_length,
+ divisor_length,
+ })
+ }
+ AsymmetricPrivateKey::Ec(key) => {
+ let named_curve = match key {
+ EcPrivateKey::P224(_) => "p224",
+ EcPrivateKey::P256(_) => "p256",
+ EcPrivateKey::P384(_) => "p384",
+ };
+ Ok(AsymmetricKeyDetails::Ec { named_curve })
+ }
+ AsymmetricPrivateKey::X25519(_) => Ok(AsymmetricKeyDetails::X25519),
+ AsymmetricPrivateKey::Ed25519(_) => Ok(AsymmetricKeyDetails::Ed25519),
+ AsymmetricPrivateKey::Dh(_) => Ok(AsymmetricKeyDetails::Dh),
+ },
+ KeyObjectHandle::AsymmetricPublic(public_key) => match public_key {
+ AsymmetricPublicKey::Rsa(key) => {
+ let modulus_length = key.n().bits();
+ let public_exponent =
+ BigInt::from_bytes_be(num_bigint::Sign::Plus, &key.e().to_bytes_be());
+ Ok(AsymmetricKeyDetails::Rsa {
+ modulus_length,
+ public_exponent: V8BigInt::from(public_exponent),
+ })
+ }
+ AsymmetricPublicKey::RsaPss(key) => {
+ let modulus_length = key.key.n().bits();
+ let public_exponent = BigInt::from_bytes_be(
+ num_bigint::Sign::Plus,
+ &key.key.e().to_bytes_be(),
+ );
+ let public_exponent = V8BigInt::from(public_exponent);
+ let details = match key.details {
+ Some(details) => AsymmetricKeyDetails::RsaPss {
+ modulus_length,
+ public_exponent,
+ hash_algorithm: details.hash_algorithm.as_str(),
+ mgf1_hash_algorithm: details.mf1_hash_algorithm.as_str(),
+ salt_length: details.salt_length,
+ },
+ None => AsymmetricKeyDetails::RsaPssBasic {
+ modulus_length,
+ public_exponent,
+ },
+ };
+ Ok(details)
+ }
+ AsymmetricPublicKey::Dsa(key) => {
+ let components = key.components();
+ let modulus_length = components.p().bits();
+ let divisor_length = components.q().bits();
+ Ok(AsymmetricKeyDetails::Dsa {
+ modulus_length,
+ divisor_length,
+ })
+ }
+ AsymmetricPublicKey::Ec(key) => {
+ let named_curve = match key {
+ EcPublicKey::P224(_) => "p224",
+ EcPublicKey::P256(_) => "p256",
+ EcPublicKey::P384(_) => "p384",
+ };
+ Ok(AsymmetricKeyDetails::Ec { named_curve })
+ }
+ AsymmetricPublicKey::X25519(_) => Ok(AsymmetricKeyDetails::X25519),
+ AsymmetricPublicKey::Ed25519(_) => Ok(AsymmetricKeyDetails::Ed25519),
+ AsymmetricPublicKey::Dh(_) => Ok(AsymmetricKeyDetails::Dh),
+ },
+ KeyObjectHandle::Secret(_) => {
+ Err(type_error("symmetric key is not an asymmetric key"))
+ }
+ }
+}
+
+#[op2(fast)]
+#[smi]
+pub fn op_node_get_symmetric_key_size(
+ #[cppgc] handle: &KeyObjectHandle,
+) -> Result<usize, AnyError> {
+ match handle {
+ KeyObjectHandle::AsymmetricPrivate(_) => {
+ Err(type_error("asymmetric key is not a symmetric key"))
+ }
+ KeyObjectHandle::AsymmetricPublic(_) => {
+ Err(type_error("asymmetric key is not a symmetric key"))
+ }
+ KeyObjectHandle::Secret(key) => Ok(key.len() * 8),
+ }
+}
+
+#[op2]
+#[cppgc]
+pub fn op_node_generate_secret_key(#[smi] len: usize) -> KeyObjectHandle {
+ let mut key = vec![0u8; len];
+ thread_rng().fill_bytes(&mut key);
+ KeyObjectHandle::Secret(key.into_boxed_slice())
+}
+
+#[op2(async)]
+#[cppgc]
+pub async fn op_node_generate_secret_key_async(
+ #[smi] len: usize,
+) -> KeyObjectHandle {
+ spawn_blocking(move || {
+ let mut key = vec![0u8; len];
+ thread_rng().fill_bytes(&mut key);
+ KeyObjectHandle::Secret(key.into_boxed_slice())
+ })
+ .await
+ .unwrap()
+}
+
+struct KeyObjectHandlePair {
+ private_key: RefCell<Option<KeyObjectHandle>>,
+ public_key: RefCell<Option<KeyObjectHandle>>,
+}
+
+impl GarbageCollected for KeyObjectHandlePair {}
+
+impl KeyObjectHandlePair {
+ pub fn new(
+ private_key: AsymmetricPrivateKey,
+ public_key: AsymmetricPublicKey,
+ ) -> Self {
+ Self {
+ private_key: RefCell::new(Some(KeyObjectHandle::AsymmetricPrivate(
+ private_key,
+ ))),
+ public_key: RefCell::new(Some(KeyObjectHandle::AsymmetricPublic(
+ public_key,
+ ))),
+ }
+ }
+}
+
+#[op2]
+#[cppgc]
+pub fn op_node_get_public_key_from_pair(
+ #[cppgc] pair: &KeyObjectHandlePair,
+) -> Option<KeyObjectHandle> {
+ pair.public_key.borrow_mut().take()
+}
+
+#[op2]
+#[cppgc]
+pub fn op_node_get_private_key_from_pair(
+ #[cppgc] pair: &KeyObjectHandlePair,
+) -> Option<KeyObjectHandle> {
+ pair.private_key.borrow_mut().take()
+}
+
+fn generate_rsa(
+ modulus_length: usize,
+ public_exponent: usize,
+) -> KeyObjectHandlePair {
+ let private_key = RsaPrivateKey::new_with_exp(
+ &mut thread_rng(),
+ modulus_length,
+ &rsa::BigUint::from_usize(public_exponent).unwrap(),
+ )
+ .unwrap();
+
+ let private_key = AsymmetricPrivateKey::Rsa(private_key);
+ let public_key = private_key.to_public_key();
+
+ KeyObjectHandlePair::new(private_key, public_key)
+}
+
+#[op2]
+#[cppgc]
+pub fn op_node_generate_rsa_key(
+ #[smi] modulus_length: usize,
+ #[smi] public_exponent: usize,
+) -> KeyObjectHandlePair {
+ generate_rsa(modulus_length, public_exponent)
+}
+
+#[op2(async)]
+#[cppgc]
+pub async fn op_node_generate_rsa_key_async(
+ #[smi] modulus_length: usize,
+ #[smi] public_exponent: usize,
+) -> KeyObjectHandlePair {
+ spawn_blocking(move || generate_rsa(modulus_length, public_exponent))
+ .await
+ .unwrap()
+}
+
+fn generate_rsa_pss(
+ modulus_length: usize,
+ public_exponent: usize,
+ hash_algorithm: Option<&str>,
+ mf1_hash_algorithm: Option<&str>,
+ salt_length: Option<u32>,
+) -> Result<KeyObjectHandlePair, AnyError> {
+ let key = RsaPrivateKey::new_with_exp(
+ &mut thread_rng(),
+ modulus_length,
+ &rsa::BigUint::from_usize(public_exponent).unwrap(),
+ )
+ .unwrap();
+
+ let details = if hash_algorithm.is_none()
+ && mf1_hash_algorithm.is_none()
+ && salt_length.is_none()
+ {
+ None
+ } else {
+ let hash_algorithm = hash_algorithm.unwrap_or("sha1");
+ let mf1_hash_algorithm = mf1_hash_algorithm.unwrap_or(hash_algorithm);
+ let hash_algorithm = match_fixed_digest_with_oid!(
+ hash_algorithm,
+ fn (algorithm: Option<RsaPssHashAlgorithm>) {
+ algorithm.ok_or_else(|| type_error("digest not allowed for RSA-PSS keys: {}"))?
+ },
+ _ => {
+ return Err(type_error(format!(
+ "digest not allowed for RSA-PSS keys: {}",
+ hash_algorithm
+ )))
+ }
+ );
+ let mf1_hash_algorithm = match_fixed_digest_with_oid!(
+ mf1_hash_algorithm,
+ fn (algorithm: Option<RsaPssHashAlgorithm>) {
+ algorithm.ok_or_else(|| type_error("digest not allowed for RSA-PSS keys: {}"))?
+ },
+ _ => {
+ return Err(type_error(format!(
+ "digest not allowed for RSA-PSS keys: {}",
+ mf1_hash_algorithm
+ )))
+ }
+ );
+ let salt_length =
+ salt_length.unwrap_or_else(|| hash_algorithm.salt_length());
+
+ Some(RsaPssDetails {
+ hash_algorithm,
+ mf1_hash_algorithm,
+ salt_length,
+ })
+ };
+
+ let private_key =
+ AsymmetricPrivateKey::RsaPss(RsaPssPrivateKey { key, details });
+ let public_key = private_key.to_public_key();
+
+ Ok(KeyObjectHandlePair::new(private_key, public_key))
+}
+
+#[op2]
+#[cppgc]
+pub fn op_node_generate_rsa_pss_key(
+ #[smi] modulus_length: usize,
+ #[smi] public_exponent: usize,
+ #[string] hash_algorithm: Option<String>, // todo: Option<&str> not supproted in ops yet
+ #[string] mf1_hash_algorithm: Option<String>, // todo: Option<&str> not supproted in ops yet
+ #[smi] salt_length: Option<u32>,
+) -> Result<KeyObjectHandlePair, AnyError> {
+ generate_rsa_pss(
+ modulus_length,
+ public_exponent,
+ hash_algorithm.as_deref(),
+ mf1_hash_algorithm.as_deref(),
+ salt_length,
+ )
+}
+
+#[op2(async)]
+#[cppgc]
+pub async fn op_node_generate_rsa_pss_key_async(
+ #[smi] modulus_length: usize,
+ #[smi] public_exponent: usize,
+ #[string] hash_algorithm: Option<String>, // todo: Option<&str> not supproted in ops yet
+ #[string] mf1_hash_algorithm: Option<String>, // todo: Option<&str> not supproted in ops yet
+ #[smi] salt_length: Option<u32>,
+) -> Result<KeyObjectHandlePair, AnyError> {
+ spawn_blocking(move || {
+ generate_rsa_pss(
+ modulus_length,
+ public_exponent,
+ hash_algorithm.as_deref(),
+ mf1_hash_algorithm.as_deref(),
+ salt_length,
+ )
+ })
+ .await
+ .unwrap()
+}
+
+fn dsa_generate(
+ modulus_length: usize,
+ divisor_length: usize,
+) -> Result<KeyObjectHandlePair, AnyError> {
+ let mut rng = rand::thread_rng();
+ use dsa::Components;
+ use dsa::KeySize;
+ 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,
+ (3072, 256) => KeySize::DSA_3072_256,
+ _ => {
+ return Err(type_error(
+ "Invalid modulusLength+divisorLength combination",
+ ))
+ }
+ };
+ let components = Components::generate(&mut rng, key_size);
+ let signing_key = SigningKey::generate(&mut rng, components);
+ let private_key = AsymmetricPrivateKey::Dsa(signing_key);
+ let public_key = private_key.to_public_key();
+
+ Ok(KeyObjectHandlePair::new(private_key, public_key))
+}
+
+#[op2]
+#[cppgc]
+pub fn op_node_generate_dsa_key(
+ #[smi] modulus_length: usize,
+ #[smi] divisor_length: usize,
+) -> Result<KeyObjectHandlePair, AnyError> {
+ dsa_generate(modulus_length, divisor_length)
+}
+
+#[op2(async)]
+#[cppgc]
+pub async fn op_node_generate_dsa_key_async(
+ #[smi] modulus_length: usize,
+ #[smi] divisor_length: usize,
+) -> Result<KeyObjectHandlePair, AnyError> {
+ spawn_blocking(move || dsa_generate(modulus_length, divisor_length))
+ .await
+ .unwrap()
+}
+
+fn ec_generate(named_curve: &str) -> Result<KeyObjectHandlePair, AnyError> {
+ let mut rng = rand::thread_rng();
+ // TODO(@littledivy): Support public key point encoding.
+ // Default is uncompressed.
+ let private_key = match named_curve {
+ "P-224" | "prime224v1" | "secp224r1" => {
+ let key = p224::SecretKey::random(&mut rng);
+ AsymmetricPrivateKey::Ec(EcPrivateKey::P224(key))
+ }
+ "P-256" | "prime256v1" | "secp256r1" => {
+ let key = p256::SecretKey::random(&mut rng);
+ AsymmetricPrivateKey::Ec(EcPrivateKey::P256(key))
+ }
+ "P-384" | "prime384v1" | "secp384r1" => {
+ let key = p384::SecretKey::random(&mut rng);
+ AsymmetricPrivateKey::Ec(EcPrivateKey::P384(key))
+ }
+ _ => {
+ return Err(type_error(format!(
+ "unsupported named curve: {}",
+ named_curve
+ )))
+ }
+ };
+ let public_key = private_key.to_public_key();
+ Ok(KeyObjectHandlePair::new(private_key, public_key))
+}
+
+#[op2]
+#[cppgc]
+pub fn op_node_generate_ec_key(
+ #[string] named_curve: &str,
+) -> Result<KeyObjectHandlePair, AnyError> {
+ ec_generate(named_curve)
+}
+
+#[op2(async)]
+#[cppgc]
+pub async fn op_node_generate_ec_key_async(
+ #[string] named_curve: String,
+) -> Result<KeyObjectHandlePair, AnyError> {
+ spawn_blocking(move || ec_generate(&named_curve))
+ .await
+ .unwrap()
+}
+
+fn x25519_generate() -> KeyObjectHandlePair {
+ let keypair = x25519_dalek::StaticSecret::random_from_rng(thread_rng());
+ let private_key = AsymmetricPrivateKey::X25519(keypair);
+ let public_key = private_key.to_public_key();
+ KeyObjectHandlePair::new(private_key, public_key)
+}
+
+#[op2]
+#[cppgc]
+pub fn op_node_generate_x25519_key() -> KeyObjectHandlePair {
+ x25519_generate()
+}
+
+#[op2(async)]
+#[cppgc]
+pub async fn op_node_generate_x25519_key_async() -> KeyObjectHandlePair {
+ spawn_blocking(x25519_generate).await.unwrap()
+}
+
+fn ed25519_generate() -> KeyObjectHandlePair {
+ let keypair = ed25519_dalek::SigningKey::generate(&mut thread_rng());
+ let private_key = AsymmetricPrivateKey::Ed25519(keypair);
+ let public_key = private_key.to_public_key();
+ KeyObjectHandlePair::new(private_key, public_key)
+}
+
+#[op2]
+#[cppgc]
+pub fn op_node_generate_ed25519_key() -> KeyObjectHandlePair {
+ ed25519_generate()
+}
+
+#[op2(async)]
+#[cppgc]
+pub async fn op_node_generate_ed25519_key_async() -> KeyObjectHandlePair {
+ spawn_blocking(ed25519_generate).await.unwrap()
+}
+
+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>(),
+ _ => return Err(type_error("Unsupported group name")),
+ };
+ Ok(KeyObjectHandlePair::new(
+ AsymmetricPrivateKey::Dh(dh.private_key),
+ AsymmetricPublicKey::Dh(dh.public_key),
+ ))
+}
+
+#[op2]
+#[cppgc]
+pub fn op_node_generate_dh_group_key(
+ #[string] group_name: &str,
+) -> Result<KeyObjectHandlePair, AnyError> {
+ dh_group_generate(group_name)
+}
+
+#[op2(async)]
+#[cppgc]
+pub async fn op_node_generate_dh_group_key_async(
+ #[string] group_name: String,
+) -> Result<KeyObjectHandlePair, AnyError> {
+ spawn_blocking(move || dh_group_generate(&group_name))
+ .await
+ .unwrap()
+}
+
+fn dh_generate(
+ prime: Option<&[u8]>,
+ prime_len: usize,
+ generator: usize,
+) -> Result<KeyObjectHandlePair, AnyError> {
+ let prime = prime
+ .map(|p| p.into())
+ .unwrap_or_else(|| Prime::generate(prime_len));
+ let dh = dh::DiffieHellman::new(prime, generator);
+ Ok(KeyObjectHandlePair::new(
+ AsymmetricPrivateKey::Dh(dh.private_key),
+ AsymmetricPublicKey::Dh(dh.public_key),
+ ))
+}
+
+#[op2]
+#[cppgc]
+pub fn op_node_generate_dh_key(
+ #[buffer] prime: Option<&[u8]>,
+ #[smi] prime_len: usize,
+ #[smi] generator: usize,
+) -> Result<KeyObjectHandlePair, AnyError> {
+ dh_generate(prime, prime_len, generator)
+}
+
+#[op2(async)]
+#[cppgc]
+pub async fn op_node_generate_dh_key_async(
+ #[buffer(copy)] prime: Option<Box<[u8]>>,
+ #[smi] prime_len: usize,
+ #[smi] generator: usize,
+) -> Result<KeyObjectHandlePair, AnyError> {
+ spawn_blocking(move || dh_generate(prime.as_deref(), prime_len, generator))
+ .await
+ .unwrap()
+}
+
+#[op2]
+#[serde]
+pub fn op_node_dh_keys_generate_and_export(
+ #[buffer] prime: Option<&[u8]>,
+ #[smi] prime_len: usize,
+ #[smi] generator: usize,
+) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
+ let prime = prime
+ .map(|p| p.into())
+ .unwrap_or_else(|| Prime::generate(prime_len));
+ let dh = dh::DiffieHellman::new(prime, generator);
+ let private_key = dh.private_key.into_vec().into_boxed_slice();
+ let public_key = dh.public_key.into_vec().into_boxed_slice();
+ Ok((private_key.into(), public_key.into()))
+}
+
+#[op2]
+#[buffer]
+pub fn op_node_export_secret_key(
+ #[cppgc] handle: &KeyObjectHandle,
+) -> Result<Box<[u8]>, AnyError> {
+ let key = handle
+ .as_secret_key()
+ .ok_or_else(|| type_error("key is not a secret key"))?;
+ Ok(key.to_vec().into_boxed_slice())
+}
+
+#[op2]
+#[string]
+pub fn op_node_export_secret_key_b64url(
+ #[cppgc] handle: &KeyObjectHandle,
+) -> Result<String, AnyError> {
+ let key = handle
+ .as_secret_key()
+ .ok_or_else(|| type_error("key is not a secret key"))?;
+ Ok(base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(key))
+}
+
+#[op2]
+#[string]
+pub fn op_node_export_public_key_pem(
+ #[cppgc] handle: &KeyObjectHandle,
+ #[string] typ: &str,
+) -> Result<String, AnyError> {
+ let public_key = handle
+ .as_public_key()
+ .ok_or_else(|| type_error("key is not an asymmetric public key"))?;
+ let data = public_key.export_der(typ)?;
+
+ let label = match typ {
+ "pkcs1" => "RSA PUBLIC KEY",
+ "spki" => "PUBLIC KEY",
+ _ => unreachable!("export_der would have errored"),
+ };
+
+ let mut out = vec![0; 2048];
+ let mut writer = PemWriter::new(label, LineEnding::LF, &mut out)?;
+ writer.write(&data)?;
+ let len = writer.finish()?;
+ out.truncate(len);
+
+ Ok(String::from_utf8(out).expect("invalid pem is not possible"))
+}
+
+#[op2]
+#[buffer]
+pub fn op_node_export_public_key_der(
+ #[cppgc] handle: &KeyObjectHandle,
+ #[string] typ: &str,
+) -> Result<Box<[u8]>, AnyError> {
+ let public_key = handle
+ .as_public_key()
+ .ok_or_else(|| type_error("key is not an asymmetric public key"))?;
+ public_key.export_der(typ)
+}
+
+#[op2]
+#[string]
+pub fn op_node_export_private_key_pem(
+ #[cppgc] handle: &KeyObjectHandle,
+ #[string] typ: &str,
+) -> Result<String, AnyError> {
+ let private_key = handle
+ .as_private_key()
+ .ok_or_else(|| type_error("key is not an asymmetric private key"))?;
+ let data = private_key.export_der(typ)?;
+
+ let label = match typ {
+ "pkcs1" => "RSA PRIVATE KEY",
+ "pkcs8" => "PRIVATE KEY",
+ "sec1" => "EC PRIVATE KEY",
+ _ => unreachable!("export_der would have errored"),
+ };
+
+ let mut out = vec![0; 2048];
+ let mut writer = PemWriter::new(label, LineEnding::LF, &mut out)?;
+ writer.write(&data)?;
+ let len = writer.finish()?;
+ out.truncate(len);
+
+ Ok(String::from_utf8(out).expect("invalid pem is not possible"))
+}
+
+#[op2]
+#[buffer]
+pub fn op_node_export_private_key_der(
+ #[cppgc] handle: &KeyObjectHandle,
+ #[string] typ: &str,
+) -> Result<Box<[u8]>, AnyError> {
+ let private_key = handle
+ .as_private_key()
+ .ok_or_else(|| type_error("key is not an asymmetric private key"))?;
+ private_key.export_der(typ)
+}
+
+#[op2]
+#[string]
+pub fn op_node_key_type(#[cppgc] handle: &KeyObjectHandle) -> &'static str {
+ match handle {
+ KeyObjectHandle::AsymmetricPrivate(_) => "private",
+ KeyObjectHandle::AsymmetricPublic(_) => "public",
+ KeyObjectHandle::Secret(_) => "secret",
+ }
+}
+
+#[op2]
+#[cppgc]
+pub fn op_node_derive_public_key_from_private_key(
+ #[cppgc] handle: &KeyObjectHandle,
+) -> Result<KeyObjectHandle, AnyError> {
+ let Some(private_key) = handle.as_private_key() else {
+ return Err(type_error("expected private key"));
+ };
+
+ Ok(KeyObjectHandle::AsymmetricPublic(
+ private_key.to_public_key(),
+ ))
+}
diff --git a/ext/node/ops/crypto/mod.rs b/ext/node/ops/crypto/mod.rs
index 1b545e024..07d901cbc 100644
--- a/ext/node/ops/crypto/mod.rs
+++ b/ext/node/ops/crypto/mod.rs
@@ -3,7 +3,6 @@ use deno_core::error::generic_error;
use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::op2;
-use deno_core::serde_v8::BigInt as V8BigInt;
use deno_core::unsync::spawn_blocking;
use deno_core::JsBuffer;
use deno_core::OpState;
@@ -11,21 +10,12 @@ use deno_core::StringOrBuffer;
use deno_core::ToJsBuffer;
use elliptic_curve::sec1::ToEncodedPoint;
use hkdf::Hkdf;
+use keys::KeyObjectHandle;
use num_bigint::BigInt;
use num_bigint_dig::BigUint;
-use num_traits::FromPrimitive;
-use once_cell::sync::Lazy;
use rand::distributions::Distribution;
use rand::distributions::Uniform;
-use rand::thread_rng;
use rand::Rng;
-use rsa::pkcs1::DecodeRsaPrivateKey;
-use rsa::pkcs1::DecodeRsaPublicKey;
-use rsa::pkcs8;
-use rsa::pkcs8::der::asn1;
-use rsa::pkcs8::der::Decode;
-use rsa::pkcs8::der::Encode;
-use rsa::pkcs8::der::Reader;
use std::future::Future;
use std::rc::Rc;
@@ -34,24 +24,21 @@ use p256::NistP256;
use p384::NistP384;
use rsa::pkcs8::DecodePrivateKey;
use rsa::pkcs8::DecodePublicKey;
-use rsa::signature::hazmat::PrehashSigner;
-use rsa::signature::hazmat::PrehashVerifier;
-use rsa::signature::SignatureEncoding;
use rsa::Oaep;
use rsa::Pkcs1v15Encrypt;
use rsa::RsaPrivateKey;
use rsa::RsaPublicKey;
-use spki::EncodePublicKey;
mod cipher;
mod dh;
mod digest;
+pub mod keys;
mod md5_sha1;
mod primes;
+mod sign;
pub mod x509;
use self::digest::match_fixed_digest_with_eager_block_buffer;
-use self::digest::match_fixed_digest_with_oid;
#[op2(fast)]
pub fn op_node_check_prime(
@@ -339,156 +326,23 @@ pub fn op_node_decipheriv_final(
}
#[op2]
-#[serde]
+#[buffer]
pub fn op_node_sign(
+ #[cppgc] handle: &KeyObjectHandle,
#[buffer] digest: &[u8],
#[string] digest_type: &str,
- #[serde] key: StringOrBuffer,
- #[string] _type: &str,
- #[string] format: &str,
-) -> Result<ToJsBuffer, AnyError> {
- let (label, doc) =
- pkcs8::SecretDocument::from_pem(std::str::from_utf8(&key).unwrap())?;
-
- let oid;
- let pkey = match format {
- "pem" => match label {
- "PRIVATE KEY" => {
- let pk_info = pkcs8::PrivateKeyInfo::try_from(doc.as_bytes())?;
- oid = pk_info.algorithm.oid;
- pk_info.private_key
- }
- "RSA PRIVATE KEY" => {
- oid = RSA_ENCRYPTION_OID;
- doc.as_bytes()
- }
- "EC PRIVATE KEY" => {
- let ec_pk = sec1::EcPrivateKey::from_der(doc.as_bytes())?;
- match ec_pk.parameters {
- Some(sec1::EcParameters::NamedCurve(o)) => {
- oid = o;
- ec_pk.private_key
- }
- // https://datatracker.ietf.org/doc/html/rfc5915#section-3
- //
- // Though the ASN.1 indicates that
- // the parameters field is OPTIONAL, implementations that conform to
- // this document MUST always include the parameters field.
- _ => return Err(type_error("invalid ECPrivateKey params")),
- }
- }
- _ => return Err(type_error("Invalid PEM label")),
- },
- _ => return Err(type_error("Unsupported key format")),
- };
-
- match oid {
- RSA_ENCRYPTION_OID => {
- use rsa::pkcs1v15::SigningKey;
- let key = RsaPrivateKey::from_pkcs1_der(pkey)?;
-
- // md5-sha1 is special, because it's not wrapped in an ASN.1 DigestInfo
- // (so has no prefix).
- // See https://github.com/openssl/openssl/blob/af82623d32962b3eff5b0f0b0dedec5eb730b231/crypto/rsa/rsa_sign.c#L285
- if digest_type == "md5-sha1" {
- let signing_key = SigningKey::<md5_sha1::Md5Sha1>::new_unprefixed(key);
- let signature = signing_key.sign_prehash(digest)?.to_vec();
- return Ok(signature.into());
- }
-
- let signature = match_fixed_digest_with_oid!(
- digest_type,
- fn <D>() {
- let signing_key = SigningKey::<D>::new(key);
- signing_key.sign_prehash(digest)?.to_vec()
- },
- _ => {
- return Err(type_error(format!(
- "digest not allowed for RSA signature: {}",
- digest_type
- )))
- }
- );
- Ok(signature.into())
- }
- // signature structure encoding is DER by default for DSA and ECDSA.
- //
- // TODO(@littledivy): Validate public_key if present
- ID_SECP256R1_OID => {
- let key = p256::ecdsa::SigningKey::from_slice(pkey)?;
- Ok(
- key
- .sign_prehash(digest)
- .map(|sig: p256::ecdsa::Signature| sig.to_der().to_vec().into())?,
- )
- }
- ID_SECP384R1_OID => {
- let key = p384::ecdsa::SigningKey::from_slice(pkey)?;
- Ok(
- key
- .sign_prehash(digest)
- .map(|sig: p384::ecdsa::Signature| sig.to_der().to_vec().into())?,
- )
- }
- _ => Err(type_error("Unsupported signing key")),
- }
+) -> Result<Box<[u8]>, AnyError> {
+ handle.sign_prehashed(digest_type, digest)
}
-#[op2]
+#[op2(fast)]
pub fn op_node_verify(
+ #[cppgc] handle: &KeyObjectHandle,
#[buffer] digest: &[u8],
#[string] digest_type: &str,
- #[serde] key: StringOrBuffer,
- #[string] key_type: &str,
- #[string] key_format: &str,
#[buffer] signature: &[u8],
) -> Result<bool, AnyError> {
- match key_type {
- "rsa" => {
- use rsa::pkcs1v15::VerifyingKey;
- let key = match key_format {
- "pem" => RsaPublicKey::from_public_key_pem((&key).try_into()?)
- .map_err(|_| type_error("Invalid RSA public key"))?,
- // TODO(kt3k): Support der and jwk formats
- _ => {
- return Err(type_error(format!(
- "Unsupported key format: {}",
- key_format
- )))
- }
- };
-
- // md5-sha1 is special, because it's not wrapped in an ASN.1 DigestInfo
- // (so has no prefix).
- // See https://github.com/openssl/openssl/blob/af82623d32962b3eff5b0f0b0dedec5eb730b231/crypto/rsa/rsa_sign.c#L285
- if digest_type == "md5-sha1" {
- let verifying_key =
- VerifyingKey::<md5_sha1::Md5Sha1>::new_unprefixed(key);
- let verified = verifying_key
- .verify_prehash(digest, &signature.try_into()?)
- .is_ok();
- return Ok(verified);
- }
-
- Ok(match_fixed_digest_with_oid!(
- digest_type,
- fn <D>() {
- let verifying_key = VerifyingKey::<D>::new(key);
- verifying_key.verify_prehash(digest, &signature.try_into()?).is_ok()
- },
- _ => {
- return Err(type_error(format!(
- "digest not allowed for RSA signature: {}",
- digest_type
- )))
- }
- ))
- }
- _ => Err(type_error(format!(
- "Verifying with {} keys is not supported",
- key_type
- ))),
- }
+ handle.verify_prehashed(digest_type, digest, signature)
}
fn pbkdf2_sync(
@@ -542,13 +396,13 @@ pub async fn op_node_pbkdf2_async(
}
#[op2(fast)]
-pub fn op_node_generate_secret(#[buffer] buf: &mut [u8]) {
+pub fn op_node_fill_random(#[buffer] buf: &mut [u8]) {
rand::thread_rng().fill(buf);
}
#[op2(async)]
#[serde]
-pub async fn op_node_generate_secret_async(#[smi] len: i32) -> ToJsBuffer {
+pub async fn op_node_fill_random_async(#[smi] len: i32) -> ToJsBuffer {
spawn_blocking(move || {
let mut buf = vec![0u8; len as usize];
rand::thread_rng().fill(&mut buf[..]);
@@ -560,11 +414,15 @@ pub async fn op_node_generate_secret_async(#[smi] len: i32) -> ToJsBuffer {
fn hkdf_sync(
digest_algorithm: &str,
- ikm: &[u8],
+ handle: &KeyObjectHandle,
salt: &[u8],
info: &[u8],
okm: &mut [u8],
) -> Result<(), AnyError> {
+ let Some(ikm) = handle.as_secret_key() else {
+ return Err(type_error("expected secret key"));
+ };
+
match_fixed_digest_with_eager_block_buffer!(
digest_algorithm,
fn <D>() {
@@ -581,328 +439,32 @@ fn hkdf_sync(
#[op2(fast)]
pub fn op_node_hkdf(
#[string] digest_algorithm: &str,
- #[buffer] ikm: &[u8],
+ #[cppgc] handle: &KeyObjectHandle,
#[buffer] salt: &[u8],
#[buffer] info: &[u8],
#[buffer] okm: &mut [u8],
) -> Result<(), AnyError> {
- hkdf_sync(digest_algorithm, ikm, salt, info, okm)
+ hkdf_sync(digest_algorithm, handle, salt, info, okm)
}
#[op2(async)]
#[serde]
pub async fn op_node_hkdf_async(
#[string] digest_algorithm: String,
- #[buffer] ikm: JsBuffer,
+ #[cppgc] handle: &KeyObjectHandle,
#[buffer] salt: JsBuffer,
#[buffer] info: JsBuffer,
#[number] okm_len: usize,
) -> Result<ToJsBuffer, AnyError> {
+ let handle = handle.clone();
spawn_blocking(move || {
let mut okm = vec![0u8; okm_len];
- hkdf_sync(&digest_algorithm, &ikm, &salt, &info, &mut okm)?;
+ hkdf_sync(&digest_algorithm, &handle, &salt, &info, &mut okm)?;
Ok(okm.into())
})
.await?
}
-use rsa::pkcs1::EncodeRsaPrivateKey;
-use rsa::pkcs1::EncodeRsaPublicKey;
-
-use self::primes::Prime;
-
-fn generate_rsa(
- modulus_length: usize,
- public_exponent: usize,
-) -> Result<(ToJsBuffer, ToJsBuffer), 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()))
-}
-
-#[op2]
-#[serde]
-pub fn op_node_generate_rsa(
- #[number] modulus_length: usize,
- #[number] public_exponent: usize,
-) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
- generate_rsa(modulus_length, public_exponent)
-}
-
-#[op2(async)]
-#[serde]
-pub async fn op_node_generate_rsa_async(
- #[number] modulus_length: usize,
- #[number] public_exponent: usize,
-) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
- spawn_blocking(move || generate_rsa(modulus_length, public_exponent)).await?
-}
-
-#[op2]
-#[string]
-pub fn op_node_export_rsa_public_pem(
- #[buffer] pkcs1_der: &[u8],
-) -> Result<String, AnyError> {
- let public_key = RsaPublicKey::from_pkcs1_der(pkcs1_der)?;
- let export = public_key.to_public_key_pem(Default::default())?;
- Ok(export)
-}
-
-#[op2]
-#[serde]
-pub fn op_node_export_rsa_spki_der(
- #[buffer] pkcs1_der: &[u8],
-) -> Result<ToJsBuffer, AnyError> {
- let public_key = RsaPublicKey::from_pkcs1_der(pkcs1_der)?;
- let export = public_key.to_public_key_der()?.to_vec();
- Ok(export.into())
-}
-
-fn dsa_generate(
- modulus_length: usize,
- divisor_length: usize,
-) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
- let mut rng = rand::thread_rng();
- use dsa::pkcs8::EncodePrivateKey;
- use dsa::Components;
- use dsa::KeySize;
- 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,
- (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("Not valid pkcs8"))?
- .as_bytes()
- .to_vec()
- .into(),
- verifying_key
- .to_public_key_der()
- .map_err(|_| type_error("Not valid spki"))?
- .to_vec()
- .into(),
- ))
-}
-
-#[op2]
-#[serde]
-pub fn op_node_dsa_generate(
- #[number] modulus_length: usize,
- #[number] divisor_length: usize,
-) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
- dsa_generate(modulus_length, divisor_length)
-}
-
-#[op2(async)]
-#[serde]
-pub async fn op_node_dsa_generate_async(
- #[number] modulus_length: usize,
- #[number] divisor_length: usize,
-) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
- spawn_blocking(move || dsa_generate(modulus_length, divisor_length)).await?
-}
-
-fn ec_generate(
- named_curve: &str,
-) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
- let mut rng = rand::thread_rng();
- // TODO(@littledivy): Support public key point encoding.
- // Default is uncompressed.
- match named_curve {
- "P-256" | "prime256v1" | "secp256r1" => {
- let key = p256::SecretKey::random(&mut rng);
- let public_key = key.public_key();
-
- Ok((
- key.to_bytes().to_vec().into(),
- public_key.to_encoded_point(false).as_ref().to_vec().into(),
- ))
- }
- "P-384" | "prime384v1" | "secp384r1" => {
- let key = p384::SecretKey::random(&mut rng);
- let public_key = key.public_key();
-
- Ok((
- key.to_bytes().to_vec().into(),
- public_key.to_encoded_point(false).as_ref().to_vec().into(),
- ))
- }
- _ => Err(type_error("Unsupported named curve")),
- }
-}
-
-#[op2]
-#[serde]
-pub fn op_node_ec_generate(
- #[string] named_curve: &str,
-) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
- ec_generate(named_curve)
-}
-
-#[op2(async)]
-#[serde]
-pub async fn op_node_ec_generate_async(
- #[string] named_curve: String,
-) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
- spawn_blocking(move || ec_generate(&named_curve)).await?
-}
-
-fn ed25519_generate() -> Result<(ToJsBuffer, ToJsBuffer), 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()))
-}
-
-#[op2]
-#[serde]
-pub fn op_node_ed25519_generate() -> Result<(ToJsBuffer, ToJsBuffer), AnyError>
-{
- ed25519_generate()
-}
-
-#[op2(async)]
-#[serde]
-pub async fn op_node_ed25519_generate_async(
-) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
- spawn_blocking(ed25519_generate).await?
-}
-
-fn x25519_generate() -> Result<(ToJsBuffer, ToJsBuffer), 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()))
-}
-
-#[op2]
-#[serde]
-pub fn op_node_x25519_generate() -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
- x25519_generate()
-}
-
-#[op2(async)]
-#[serde]
-pub async fn op_node_x25519_generate_async(
-) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
- spawn_blocking(x25519_generate).await?
-}
-
-fn dh_generate_group(
- group_name: &str,
-) -> Result<(ToJsBuffer, ToJsBuffer), 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>(),
- _ => return Err(type_error("Unsupported group name")),
- };
-
- Ok((
- dh.private_key.into_vec().into(),
- dh.public_key.into_vec().into(),
- ))
-}
-
-#[op2]
-#[serde]
-pub fn op_node_dh_generate_group(
- #[string] group_name: &str,
-) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
- dh_generate_group(group_name)
-}
-
-#[op2(async)]
-#[serde]
-pub async fn op_node_dh_generate_group_async(
- #[string] group_name: String,
-) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
- spawn_blocking(move || dh_generate_group(&group_name)).await?
-}
-
-fn dh_generate(
- prime: Option<&[u8]>,
- prime_len: usize,
- generator: usize,
-) -> Result<(ToJsBuffer, ToJsBuffer), 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(),
- ))
-}
-
-#[op2]
-#[serde]
-pub fn op_node_dh_generate(
- #[serde] prime: Option<&[u8]>,
- #[number] prime_len: usize,
- #[number] generator: usize,
-) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
- dh_generate(prime, prime_len, generator)
-}
-
-// TODO(lev): This duplication should be avoided.
-#[op2]
-#[serde]
-pub fn op_node_dh_generate2(
- #[buffer] prime: JsBuffer,
- #[number] prime_len: usize,
- #[number] generator: usize,
-) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
- dh_generate(Some(prime).as_deref(), prime_len, generator)
-}
-
#[op2]
#[serde]
pub fn op_node_dh_compute_secret(
@@ -918,17 +480,6 @@ pub fn op_node_dh_compute_secret(
Ok(shared_secret.to_bytes_be().into())
}
-#[op2(async)]
-#[serde]
-pub async fn op_node_dh_generate_async(
- #[buffer] prime: Option<JsBuffer>,
- #[number] prime_len: usize,
- #[number] generator: usize,
-) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
- spawn_blocking(move || dh_generate(prime.as_deref(), prime_len, generator))
- .await?
-}
-
#[op2(fast)]
#[smi]
pub fn op_node_random_int(
@@ -1288,392 +839,3 @@ pub async fn op_node_gen_prime_async(
) -> Result<ToJsBuffer, AnyError> {
Ok(spawn_blocking(move || gen_prime(size)).await?)
}
-
-#[derive(serde::Serialize)]
-#[serde(tag = "type")]
-pub enum AsymmetricKeyDetails {
- #[serde(rename = "rsa")]
- #[serde(rename_all = "camelCase")]
- Rsa {
- modulus_length: usize,
- public_exponent: V8BigInt,
- },
- #[serde(rename = "rsa-pss")]
- #[serde(rename_all = "camelCase")]
- RsaPss {
- modulus_length: usize,
- public_exponent: V8BigInt,
- hash_algorithm: String,
- salt_length: u32,
- },
- #[serde(rename = "ec")]
- #[serde(rename_all = "camelCase")]
- Ec { named_curve: String },
- #[serde(rename = "dh")]
- Dh,
-}
-
-// https://oidref.com/
-const ID_SHA1_OID: rsa::pkcs8::ObjectIdentifier =
- rsa::pkcs8::ObjectIdentifier::new_unwrap("1.3.14.3.2.26");
-const ID_SHA256_OID: rsa::pkcs8::ObjectIdentifier =
- rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1");
-const ID_SHA384_OID: rsa::pkcs8::ObjectIdentifier =
- rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2");
-const ID_SHA512_OID: rsa::pkcs8::ObjectIdentifier =
- rsa::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.3");
-const ID_MFG1: rsa::pkcs8::ObjectIdentifier =
- rsa::pkcs8::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.8");
-pub const ID_SECP256R1_OID: const_oid::ObjectIdentifier =
- const_oid::ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");
-pub const ID_SECP384R1_OID: const_oid::ObjectIdentifier =
- const_oid::ObjectIdentifier::new_unwrap("1.3.132.0.34");
-pub const ID_SECP521R1_OID: const_oid::ObjectIdentifier =
- const_oid::ObjectIdentifier::new_unwrap("1.3.132.0.35");
-
-// Default HashAlgorithm for RSASSA-PSS-params (sha1)
-//
-// sha1 HashAlgorithm ::= {
-// algorithm id-sha1,
-// parameters SHA1Parameters : NULL
-// }
-//
-// SHA1Parameters ::= NULL
-static SHA1_HASH_ALGORITHM: Lazy<rsa::pkcs8::AlgorithmIdentifierRef<'static>> =
- Lazy::new(|| rsa::pkcs8::AlgorithmIdentifierRef {
- // id-sha1
- oid: ID_SHA1_OID,
- // NULL
- parameters: Some(asn1::AnyRef::from(asn1::Null)),
- });
-
-// TODO(@littledivy): `pkcs8` should provide AlgorithmIdentifier to Any conversion.
-static ENCODED_SHA1_HASH_ALGORITHM: Lazy<Vec<u8>> =
- Lazy::new(|| SHA1_HASH_ALGORITHM.to_der().unwrap());
-
-// Default MaskGenAlgrithm for RSASSA-PSS-params (mgf1SHA1)
-//
-// mgf1SHA1 MaskGenAlgorithm ::= {
-// algorithm id-mgf1,
-// parameters HashAlgorithm : sha1
-// }
-static MGF1_SHA1_MASK_ALGORITHM: Lazy<
- rsa::pkcs8::AlgorithmIdentifierRef<'static>,
-> = Lazy::new(|| rsa::pkcs8::AlgorithmIdentifierRef {
- // id-mgf1
- oid: ID_MFG1,
- // sha1
- parameters: Some(
- asn1::AnyRef::from_der(&ENCODED_SHA1_HASH_ALGORITHM).unwrap(),
- ),
-});
-
-pub const RSA_ENCRYPTION_OID: const_oid::ObjectIdentifier =
- const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1");
-pub const DH_KEY_AGREEMENT_OID: const_oid::ObjectIdentifier =
- const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.3.1");
-pub const RSASSA_PSS_OID: const_oid::ObjectIdentifier =
- const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10");
-pub const EC_OID: const_oid::ObjectIdentifier =
- const_oid::ObjectIdentifier::new_unwrap("1.2.840.10045.2.1");
-
-// The parameters field associated with OID id-RSASSA-PSS
-// Defined in RFC 3447, section A.2.3
-//
-// RSASSA-PSS-params ::= SEQUENCE {
-// hashAlgorithm [0] HashAlgorithm DEFAULT sha1,
-// maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1,
-// saltLength [2] INTEGER DEFAULT 20,
-// trailerField [3] TrailerField DEFAULT trailerFieldBC
-// }
-pub struct PssPrivateKeyParameters<'a> {
- pub hash_algorithm: rsa::pkcs8::AlgorithmIdentifierRef<'a>,
- #[allow(dead_code)]
- pub mask_gen_algorithm: rsa::pkcs8::AlgorithmIdentifierRef<'a>,
- pub salt_length: u32,
-}
-
-// Context-specific tag number for hashAlgorithm.
-const HASH_ALGORITHM_TAG: rsa::pkcs8::der::TagNumber =
- rsa::pkcs8::der::TagNumber::new(0);
-
-// Context-specific tag number for maskGenAlgorithm.
-const MASK_GEN_ALGORITHM_TAG: rsa::pkcs8::der::TagNumber =
- rsa::pkcs8::der::TagNumber::new(1);
-
-// Context-specific tag number for saltLength.
-const SALT_LENGTH_TAG: rsa::pkcs8::der::TagNumber =
- rsa::pkcs8::der::TagNumber::new(2);
-
-impl<'a> TryFrom<rsa::pkcs8::der::asn1::AnyRef<'a>>
- for PssPrivateKeyParameters<'a>
-{
- type Error = rsa::pkcs8::der::Error;
-
- fn try_from(
- any: rsa::pkcs8::der::asn1::AnyRef<'a>,
- ) -> rsa::pkcs8::der::Result<PssPrivateKeyParameters> {
- any.sequence(|decoder| {
- let hash_algorithm = decoder
- .context_specific::<rsa::pkcs8::AlgorithmIdentifierRef>(
- HASH_ALGORITHM_TAG,
- pkcs8::der::TagMode::Explicit,
- )?
- .map(TryInto::try_into)
- .transpose()?
- .unwrap_or(*SHA1_HASH_ALGORITHM);
-
- let mask_gen_algorithm = decoder
- .context_specific::<rsa::pkcs8::AlgorithmIdentifierRef>(
- MASK_GEN_ALGORITHM_TAG,
- pkcs8::der::TagMode::Explicit,
- )?
- .map(TryInto::try_into)
- .transpose()?
- .unwrap_or(*MGF1_SHA1_MASK_ALGORITHM);
-
- let salt_length = decoder
- .context_specific::<u32>(
- SALT_LENGTH_TAG,
- pkcs8::der::TagMode::Explicit,
- )?
- .map(TryInto::try_into)
- .transpose()?
- .unwrap_or(20);
-
- Ok(Self {
- hash_algorithm,
- mask_gen_algorithm,
- salt_length,
- })
- })
- }
-}
-
-fn parse_private_key(
- key: &[u8],
- format: &str,
- type_: &str,
-) -> Result<pkcs8::SecretDocument, AnyError> {
- match format {
- "pem" => {
- let pem = std::str::from_utf8(key).map_err(|err| {
- type_error(format!(
- "Invalid PEM private key: not valid utf8 starting at byte {}",
- err.valid_up_to()
- ))
- })?;
- let (_, doc) = pkcs8::SecretDocument::from_pem(pem)?;
- Ok(doc)
- }
- "der" => {
- match type_ {
- "pkcs8" => pkcs8::SecretDocument::from_pkcs8_der(key)
- .map_err(|_| type_error("Invalid PKCS8 private key")),
- "pkcs1" => pkcs8::SecretDocument::from_pkcs1_der(key)
- .map_err(|_| type_error("Invalid PKCS1 private key")),
- // TODO(@littledivy): sec1 type
- _ => Err(type_error(format!("Unsupported key type: {}", type_))),
- }
- }
- _ => Err(type_error(format!("Unsupported key format: {}", format))),
- }
-}
-
-#[op2]
-#[serde]
-pub fn op_node_create_private_key(
- #[buffer] key: &[u8],
- #[string] format: &str,
- #[string] type_: &str,
-) -> Result<AsymmetricKeyDetails, AnyError> {
- use rsa::pkcs1::der::Decode;
-
- let doc = parse_private_key(key, format, type_)?;
- let pk_info = pkcs8::PrivateKeyInfo::try_from(doc.as_bytes())?;
-
- let alg = pk_info.algorithm.oid;
-
- match alg {
- RSA_ENCRYPTION_OID => {
- let private_key =
- rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key)?;
- let modulus_length = private_key.modulus.as_bytes().len() * 8;
-
- Ok(AsymmetricKeyDetails::Rsa {
- modulus_length,
- public_exponent: BigInt::from_bytes_be(
- num_bigint::Sign::Plus,
- private_key.public_exponent.as_bytes(),
- )
- .into(),
- })
- }
- DH_KEY_AGREEMENT_OID => Ok(AsymmetricKeyDetails::Dh),
- RSASSA_PSS_OID => {
- let params = PssPrivateKeyParameters::try_from(
- pk_info
- .algorithm
- .parameters
- .ok_or_else(|| type_error("Malformed parameters".to_string()))?,
- )
- .map_err(|_| type_error("Malformed parameters".to_string()))?;
-
- let hash_alg = params.hash_algorithm;
- let hash_algorithm = match hash_alg.oid {
- ID_SHA1_OID => "sha1",
- ID_SHA256_OID => "sha256",
- ID_SHA384_OID => "sha384",
- ID_SHA512_OID => "sha512",
- _ => return Err(type_error("Unsupported hash algorithm")),
- };
-
- let private_key =
- rsa::pkcs1::RsaPrivateKey::from_der(pk_info.private_key)?;
- let modulus_length = private_key.modulus.as_bytes().len() * 8;
- Ok(AsymmetricKeyDetails::RsaPss {
- modulus_length,
- public_exponent: BigInt::from_bytes_be(
- num_bigint::Sign::Plus,
- private_key.public_exponent.as_bytes(),
- )
- .into(),
- hash_algorithm: hash_algorithm.to_string(),
- salt_length: params.salt_length,
- })
- }
- EC_OID => {
- let named_curve = pk_info
- .algorithm
- .parameters_oid()
- .map_err(|_| type_error("malformed parameters"))?;
- let named_curve = match named_curve {
- ID_SECP256R1_OID => "p256",
- ID_SECP384R1_OID => "p384",
- ID_SECP521R1_OID => "p521",
- _ => return Err(type_error("Unsupported named curve")),
- };
-
- Ok(AsymmetricKeyDetails::Ec {
- named_curve: named_curve.to_string(),
- })
- }
- _ => Err(type_error("Unsupported algorithm")),
- }
-}
-
-fn parse_public_key(
- key: &[u8],
- format: &str,
- type_: &str,
-) -> Result<pkcs8::Document, AnyError> {
- match format {
- "pem" => {
- let pem = std::str::from_utf8(key).map_err(|err| {
- type_error(format!(
- "Invalid PEM private key: not valid utf8 starting at byte {}",
- err.valid_up_to()
- ))
- })?;
- let (label, doc) = pkcs8::Document::from_pem(pem)?;
- if label != "PUBLIC KEY" {
- return Err(type_error("Invalid PEM label"));
- }
- Ok(doc)
- }
- "der" => match type_ {
- "pkcs1" => pkcs8::Document::from_pkcs1_der(key)
- .map_err(|_| type_error("Invalid PKCS1 public key")),
- _ => Err(type_error(format!("Unsupported key type: {}", type_))),
- },
- _ => Err(type_error(format!("Unsupported key format: {}", format))),
- }
-}
-
-#[op2]
-#[serde]
-pub fn op_node_create_public_key(
- #[buffer] key: &[u8],
- #[string] format: &str,
- #[string] type_: &str,
-) -> Result<AsymmetricKeyDetails, AnyError> {
- let mut doc = None;
-
- let pk_info = if type_ != "spki" {
- doc.replace(parse_public_key(key, format, type_)?);
- spki::SubjectPublicKeyInfoRef::try_from(doc.as_ref().unwrap().as_bytes())?
- } else {
- spki::SubjectPublicKeyInfoRef::try_from(key)?
- };
-
- let alg = pk_info.algorithm.oid;
-
- match alg {
- RSA_ENCRYPTION_OID => {
- let public_key = rsa::pkcs1::RsaPublicKey::from_der(
- pk_info.subject_public_key.raw_bytes(),
- )?;
- let modulus_length = public_key.modulus.as_bytes().len() * 8;
-
- Ok(AsymmetricKeyDetails::Rsa {
- modulus_length,
- public_exponent: BigInt::from_bytes_be(
- num_bigint::Sign::Plus,
- public_key.public_exponent.as_bytes(),
- )
- .into(),
- })
- }
- RSASSA_PSS_OID => {
- let params = PssPrivateKeyParameters::try_from(
- pk_info
- .algorithm
- .parameters
- .ok_or_else(|| type_error("Malformed parameters".to_string()))?,
- )
- .map_err(|_| type_error("Malformed parameters".to_string()))?;
-
- let hash_alg = params.hash_algorithm;
- let hash_algorithm = match hash_alg.oid {
- ID_SHA1_OID => "sha1",
- ID_SHA256_OID => "sha256",
- ID_SHA384_OID => "sha384",
- ID_SHA512_OID => "sha512",
- _ => return Err(type_error("Unsupported hash algorithm")),
- };
-
- let public_key = rsa::pkcs1::RsaPublicKey::from_der(
- pk_info.subject_public_key.raw_bytes(),
- )?;
- let modulus_length = public_key.modulus.as_bytes().len() * 8;
- Ok(AsymmetricKeyDetails::RsaPss {
- modulus_length,
- public_exponent: BigInt::from_bytes_be(
- num_bigint::Sign::Plus,
- public_key.public_exponent.as_bytes(),
- )
- .into(),
- hash_algorithm: hash_algorithm.to_string(),
- salt_length: params.salt_length,
- })
- }
- EC_OID => {
- let named_curve = pk_info
- .algorithm
- .parameters_oid()
- .map_err(|_| type_error("malformed parameters"))?;
- let named_curve = match named_curve {
- ID_SECP256R1_OID => "p256",
- ID_SECP384R1_OID => "p384",
- ID_SECP521R1_OID => "p521",
- _ => return Err(type_error("Unsupported named curve")),
- };
-
- Ok(AsymmetricKeyDetails::Ec {
- named_curve: named_curve.to_string(),
- })
- }
- DH_KEY_AGREEMENT_OID => Ok(AsymmetricKeyDetails::Dh),
- _ => Err(type_error("Unsupported algorithm")),
- }
-}
diff --git a/ext/node/ops/crypto/sign.rs b/ext/node/ops/crypto/sign.rs
new file mode 100644
index 000000000..9aea3aab7
--- /dev/null
+++ b/ext/node/ops/crypto/sign.rs
@@ -0,0 +1,396 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+use deno_core::error::generic_error;
+use deno_core::error::type_error;
+use deno_core::error::AnyError;
+use digest::Digest;
+use digest::FixedOutput;
+use digest::FixedOutputReset;
+use digest::OutputSizeUser;
+use digest::Reset;
+use digest::Update;
+use rand::rngs::OsRng;
+use rsa::signature::hazmat::PrehashSigner as _;
+use rsa::signature::hazmat::PrehashVerifier as _;
+use rsa::traits::SignatureScheme as _;
+use spki::der::Decode;
+
+use crate::ops::crypto::digest::match_fixed_digest;
+use crate::ops::crypto::digest::match_fixed_digest_with_oid;
+
+use super::keys::AsymmetricPrivateKey;
+use super::keys::AsymmetricPublicKey;
+use super::keys::EcPrivateKey;
+use super::keys::EcPublicKey;
+use super::keys::KeyObjectHandle;
+use super::keys::RsaPssHashAlgorithm;
+
+impl KeyObjectHandle {
+ pub fn sign_prehashed(
+ &self,
+ digest_type: &str,
+ digest: &[u8],
+ ) -> Result<Box<[u8]>, AnyError> {
+ let private_key = self
+ .as_private_key()
+ .ok_or_else(|| type_error("key is not a private key"))?;
+
+ match private_key {
+ AsymmetricPrivateKey::Rsa(key) => {
+ let signer = if digest_type == "md5-sha1" {
+ rsa::pkcs1v15::Pkcs1v15Sign::new_unprefixed()
+ } else {
+ match_fixed_digest_with_oid!(
+ digest_type,
+ fn <D>() {
+ rsa::pkcs1v15::Pkcs1v15Sign::new::<D>()
+ },
+ _ => {
+ return Err(type_error(format!(
+ "digest not allowed for RSA signature: {}",
+ digest_type
+ )))
+ }
+ )
+ };
+
+ let signature = signer
+ .sign(Some(&mut OsRng), key, digest)
+ .map_err(|_| generic_error("failed to sign digest with RSA"))?;
+ Ok(signature.into())
+ }
+ AsymmetricPrivateKey::RsaPss(key) => {
+ let mut hash_algorithm = None;
+ let mut salt_length = None;
+ match &key.details {
+ Some(details) => {
+ if details.hash_algorithm != details.mf1_hash_algorithm {
+ return Err(type_error(
+ "rsa-pss with different mf1 hash algorithm and hash algorithm is not supported",
+ ));
+ }
+ hash_algorithm = Some(details.hash_algorithm);
+ salt_length = Some(details.salt_length as usize);
+ }
+ None => {}
+ };
+ let pss = match_fixed_digest_with_oid!(
+ digest_type,
+ fn <D>(algorithm: Option<RsaPssHashAlgorithm>) {
+ if let Some(hash_algorithm) = hash_algorithm.take() {
+ if Some(hash_algorithm) != algorithm {
+ return Err(type_error(format!(
+ "private key does not allow {} to be used, expected {}",
+ digest_type, hash_algorithm.as_str()
+ )));
+ }
+ }
+ if let Some(salt_length) = salt_length {
+ rsa::pss::Pss::new_with_salt::<D>(salt_length)
+ } else {
+ rsa::pss::Pss::new::<D>()
+ }
+ },
+ _ => {
+ return Err(type_error(format!(
+ "digest not allowed for RSA-PSS signature: {}",
+ digest_type
+ )))
+ }
+ );
+ let signature = pss
+ .sign(Some(&mut OsRng), &key.key, digest)
+ .map_err(|_| generic_error("failed to sign digest with RSA-PSS"))?;
+ Ok(signature.into())
+ }
+ AsymmetricPrivateKey::Dsa(key) => {
+ let res = match_fixed_digest!(
+ digest_type,
+ fn <D>() {
+ key.sign_prehashed_rfc6979::<D>(digest)
+ },
+ _ => {
+ return Err(type_error(format!(
+ "digest not allowed for RSA signature: {}",
+ digest_type
+ )))
+ }
+ );
+
+ let signature =
+ res.map_err(|_| generic_error("failed to sign digest with DSA"))?;
+ Ok(signature.into())
+ }
+ AsymmetricPrivateKey::Ec(key) => match key {
+ EcPrivateKey::P224(key) => {
+ let signing_key = p224::ecdsa::SigningKey::from(key);
+ let signature: p224::ecdsa::Signature = signing_key
+ .sign_prehash(digest)
+ .map_err(|_| type_error("failed to sign digest"))?;
+ Ok(signature.to_der().to_bytes())
+ }
+ EcPrivateKey::P256(key) => {
+ let signing_key = p256::ecdsa::SigningKey::from(key);
+ let signature: p256::ecdsa::Signature = signing_key
+ .sign_prehash(digest)
+ .map_err(|_| type_error("failed to sign digest"))?;
+ Ok(signature.to_der().to_bytes())
+ }
+ EcPrivateKey::P384(key) => {
+ let signing_key = p384::ecdsa::SigningKey::from(key);
+ let signature: p384::ecdsa::Signature = signing_key
+ .sign_prehash(digest)
+ .map_err(|_| type_error("failed to sign digest"))?;
+ Ok(signature.to_der().to_bytes())
+ }
+ },
+ AsymmetricPrivateKey::X25519(_) => {
+ Err(type_error("x25519 key cannot be used for signing"))
+ }
+ AsymmetricPrivateKey::Ed25519(key) => {
+ if !matches!(
+ digest_type,
+ "rsa-sha512" | "sha512" | "sha512withrsaencryption"
+ ) {
+ return Err(type_error(format!(
+ "digest not allowed for Ed25519 signature: {}",
+ digest_type
+ )));
+ }
+
+ let mut precomputed_digest = PrecomputedDigest([0; 64]);
+ if digest.len() != precomputed_digest.0.len() {
+ return Err(type_error("Invalid sha512 digest"));
+ }
+ precomputed_digest.0.copy_from_slice(digest);
+
+ let signature = key
+ .sign_prehashed(precomputed_digest, None)
+ .map_err(|_| generic_error("failed to sign digest with Ed25519"))?;
+
+ Ok(signature.to_bytes().into())
+ }
+ AsymmetricPrivateKey::Dh(_) => {
+ Err(type_error("DH key cannot be used for signing"))
+ }
+ }
+ }
+
+ pub fn verify_prehashed(
+ &self,
+ digest_type: &str,
+ digest: &[u8],
+ signature: &[u8],
+ ) -> Result<bool, AnyError> {
+ let public_key = self
+ .as_public_key()
+ .ok_or_else(|| type_error("key is not a public or private key"))?;
+
+ match &*public_key {
+ AsymmetricPublicKey::Rsa(key) => {
+ let signer = if digest_type == "md5-sha1" {
+ rsa::pkcs1v15::Pkcs1v15Sign::new_unprefixed()
+ } else {
+ match_fixed_digest_with_oid!(
+ digest_type,
+ fn <D>() {
+ rsa::pkcs1v15::Pkcs1v15Sign::new::<D>()
+ },
+ _ => {
+ return Err(type_error(format!(
+ "digest not allowed for RSA signature: {}",
+ digest_type
+ )))
+ }
+ )
+ };
+
+ Ok(signer.verify(key, digest, signature).is_ok())
+ }
+ AsymmetricPublicKey::RsaPss(key) => {
+ let mut hash_algorithm = None;
+ let mut salt_length = None;
+ match &key.details {
+ Some(details) => {
+ if details.hash_algorithm != details.mf1_hash_algorithm {
+ return Err(type_error(
+ "rsa-pss with different mf1 hash algorithm and hash algorithm is not supported",
+ ));
+ }
+ hash_algorithm = Some(details.hash_algorithm);
+ salt_length = Some(details.salt_length as usize);
+ }
+ None => {}
+ };
+ let pss = match_fixed_digest_with_oid!(
+ digest_type,
+ fn <D>(algorithm: Option<RsaPssHashAlgorithm>) {
+ if let Some(hash_algorithm) = hash_algorithm.take() {
+ if Some(hash_algorithm) != algorithm {
+ return Err(type_error(format!(
+ "private key does not allow {} to be used, expected {}",
+ digest_type, hash_algorithm.as_str()
+ )));
+ }
+ }
+ if let Some(salt_length) = salt_length {
+ rsa::pss::Pss::new_with_salt::<D>(salt_length)
+ } else {
+ rsa::pss::Pss::new::<D>()
+ }
+ },
+ _ => {
+ return Err(type_error(format!(
+ "digest not allowed for RSA-PSS signature: {}",
+ digest_type
+ )))
+ }
+ );
+ Ok(pss.verify(&key.key, digest, signature).is_ok())
+ }
+ AsymmetricPublicKey::Dsa(key) => {
+ let signature = dsa::Signature::from_der(signature)
+ .map_err(|_| type_error("Invalid DSA signature"))?;
+ Ok(key.verify_prehash(digest, &signature).is_ok())
+ }
+ AsymmetricPublicKey::Ec(key) => match key {
+ EcPublicKey::P224(key) => {
+ let verifying_key = p224::ecdsa::VerifyingKey::from(key);
+ let signature = p224::ecdsa::Signature::from_der(signature)
+ .map_err(|_| type_error("Invalid ECDSA signature"))?;
+ Ok(verifying_key.verify_prehash(digest, &signature).is_ok())
+ }
+ EcPublicKey::P256(key) => {
+ let verifying_key = p256::ecdsa::VerifyingKey::from(key);
+ let signature = p256::ecdsa::Signature::from_der(signature)
+ .map_err(|_| type_error("Invalid ECDSA signature"))?;
+ Ok(verifying_key.verify_prehash(digest, &signature).is_ok())
+ }
+ EcPublicKey::P384(key) => {
+ let verifying_key = p384::ecdsa::VerifyingKey::from(key);
+ let signature = p384::ecdsa::Signature::from_der(signature)
+ .map_err(|_| type_error("Invalid ECDSA signature"))?;
+ Ok(verifying_key.verify_prehash(digest, &signature).is_ok())
+ }
+ },
+ AsymmetricPublicKey::X25519(_) => {
+ Err(type_error("x25519 key cannot be used for verification"))
+ }
+ AsymmetricPublicKey::Ed25519(key) => {
+ if !matches!(
+ digest_type,
+ "rsa-sha512" | "sha512" | "sha512withrsaencryption"
+ ) {
+ return Err(type_error(format!(
+ "digest not allowed for Ed25519 signature: {}",
+ digest_type
+ )));
+ }
+
+ let mut signature_fixed = [0u8; 64];
+ if signature.len() != signature_fixed.len() {
+ return Err(type_error("Invalid Ed25519 signature"));
+ }
+ signature_fixed.copy_from_slice(signature);
+
+ let signature = ed25519_dalek::Signature::from_bytes(&signature_fixed);
+
+ let mut precomputed_digest = PrecomputedDigest([0; 64]);
+ precomputed_digest.0.copy_from_slice(digest);
+
+ Ok(
+ key
+ .verify_prehashed_strict(precomputed_digest, None, &signature)
+ .is_ok(),
+ )
+ }
+ AsymmetricPublicKey::Dh(_) => {
+ Err(type_error("DH key cannot be used for verification"))
+ }
+ }
+ }
+}
+
+struct PrecomputedDigest([u8; 64]);
+
+impl OutputSizeUser for PrecomputedDigest {
+ type OutputSize = <sha2::Sha512 as OutputSizeUser>::OutputSize;
+}
+
+impl Digest for PrecomputedDigest {
+ fn new() -> Self {
+ unreachable!()
+ }
+
+ fn new_with_prefix(_data: impl AsRef<[u8]>) -> Self {
+ unreachable!()
+ }
+
+ fn update(&mut self, _data: impl AsRef<[u8]>) {
+ unreachable!()
+ }
+
+ fn chain_update(self, _data: impl AsRef<[u8]>) -> Self {
+ unreachable!()
+ }
+
+ fn finalize(self) -> digest::Output<Self> {
+ self.0.into()
+ }
+
+ fn finalize_into(self, _out: &mut digest::Output<Self>) {
+ unreachable!()
+ }
+
+ fn finalize_reset(&mut self) -> digest::Output<Self>
+ where
+ Self: digest::FixedOutputReset,
+ {
+ unreachable!()
+ }
+
+ fn finalize_into_reset(&mut self, _out: &mut digest::Output<Self>)
+ where
+ Self: digest::FixedOutputReset,
+ {
+ unreachable!()
+ }
+
+ fn reset(&mut self)
+ where
+ Self: digest::Reset,
+ {
+ unreachable!()
+ }
+
+ fn output_size() -> usize {
+ unreachable!()
+ }
+
+ fn digest(_data: impl AsRef<[u8]>) -> digest::Output<Self> {
+ unreachable!()
+ }
+}
+
+impl Reset for PrecomputedDigest {
+ fn reset(&mut self) {
+ unreachable!()
+ }
+}
+
+impl FixedOutputReset for PrecomputedDigest {
+ fn finalize_into_reset(&mut self, _out: &mut digest::Output<Self>) {
+ unreachable!()
+ }
+}
+
+impl FixedOutput for PrecomputedDigest {
+ fn finalize_into(self, _out: &mut digest::Output<Self>) {
+ unreachable!()
+ }
+}
+
+impl Update for PrecomputedDigest {
+ fn update(&mut self, _data: &[u8]) {
+ unreachable!()
+ }
+}
diff --git a/ext/node/ops/vm_internal.rs b/ext/node/ops/vm_internal.rs
index f61308228..815f570ea 100644
--- a/ext/node/ops/vm_internal.rs
+++ b/ext/node/ops/vm_internal.rs
@@ -93,7 +93,6 @@ impl ContextifyContext {
sandbox_obj: v8::Local<v8::Object>,
) {
let tmp = init_global_template(scope, ContextInitMode::UseSnapshot);
-
let context = create_v8_context(scope, tmp, ContextInitMode::UseSnapshot);
Self::from_context(scope, context, sandbox_obj);
}