summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--ext/node/Cargo.toml1
-rw-r--r--ext/node/ops/crypto/mod.rs92
-rw-r--r--tests/unit_node/crypto/crypto_sign_test.ts11
4 files changed, 76 insertions, 29 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 9eb1c5d2e..50005a04b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1683,6 +1683,7 @@ dependencies = [
"ripemd",
"rsa",
"scrypt",
+ "sec1",
"serde",
"sha-1",
"sha2",
diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml
index 8e3e695d6..8569a0491 100644
--- a/ext/node/Cargo.toml
+++ b/ext/node/Cargo.toml
@@ -63,6 +63,7 @@ ring.workspace = true
ripemd = "0.1.3"
rsa.workspace = true
scrypt = "0.11.0"
+sec1 = "0.7"
serde = "1.0.149"
sha-1 = "0.10.0"
sha2.workspace = true
diff --git a/ext/node/ops/crypto/mod.rs b/ext/node/ops/crypto/mod.rs
index 7f593520b..6b1ca9a38 100644
--- a/ext/node/ops/crypto/mod.rs
+++ b/ext/node/ops/crypto/mod.rs
@@ -373,20 +373,36 @@ pub fn op_node_sign(
let oid;
let pkey = match format {
- "pem" => {
- if label == "PRIVATE KEY" {
+ "pem" => match label {
+ "PRIVATE KEY" => {
let pk_info = pkcs8::PrivateKeyInfo::try_from(doc.as_bytes())?;
oid = pk_info.algorithm.oid;
pk_info.private_key
- } else if label == "RSA PRIVATE KEY" {
+ }
+ "RSA PRIVATE KEY" => {
oid = RSA_ENCRYPTION_OID;
doc.as_bytes()
- } else {
- return Err(type_error("Invalid PEM label"));
}
- }
+ "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;
@@ -419,6 +435,25 @@ pub fn op_node_sign(
.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")),
}
}
@@ -704,30 +739,32 @@ pub async fn op_node_dsa_generate_async(
fn ec_generate(
named_curve: &str,
) -> Result<(ToJsBuffer, ToJsBuffer), AnyError> {
- use ring::signature::EcdsaKeyPair;
- use ring::signature::KeyPair;
+ use elliptic_curve::sec1::ToEncodedPoint;
- let curve = match named_curve {
+ let mut rng = rand::thread_rng();
+ // TODO(@littledivy): Support public key point encoding.
+ // Default is uncompressed.
+ match named_curve {
"P-256" | "prime256v1" | "secp256r1" => {
- &ring::signature::ECDSA_P256_SHA256_FIXED_SIGNING
+ 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" => {
- &ring::signature::ECDSA_P384_SHA384_FIXED_SIGNING
- }
- _ => return Err(type_error("Unsupported named curve")),
- };
-
- let rng = ring::rand::SystemRandom::new();
+ let key = p384::SecretKey::random(&mut rng);
+ let public_key = key.public_key();
- let pkcs8 = EcdsaKeyPair::generate_pkcs8(curve, &rng)
- .map_err(|_| type_error("Failed to generate EC key"))?;
-
- let public_key = EcdsaKeyPair::from_pkcs8(curve, pkcs8.as_ref(), &rng)
- .map_err(|_| type_error("Failed to generate EC key"))?
- .public_key()
- .as_ref()
- .to_vec();
- Ok((pkcs8.as_ref().to_vec().into(), public_key.into()))
+ 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]
@@ -1363,11 +1400,8 @@ fn parse_private_key(
) -> Result<pkcs8::SecretDocument, AnyError> {
match format {
"pem" => {
- let (label, doc) =
+ let (_, doc) =
pkcs8::SecretDocument::from_pem(std::str::from_utf8(key).unwrap())?;
- if label != "PRIVATE KEY" {
- return Err(type_error("Invalid PEM label"));
- }
Ok(doc)
}
"der" => {
diff --git a/tests/unit_node/crypto/crypto_sign_test.ts b/tests/unit_node/crypto/crypto_sign_test.ts
index be459bf52..2ca0af943 100644
--- a/tests/unit_node/crypto/crypto_sign_test.ts
+++ b/tests/unit_node/crypto/crypto_sign_test.ts
@@ -127,3 +127,14 @@ Deno.test({
}
},
});
+
+Deno.test({
+ name: "crypto.createSign|sign - EC PRIVATE KEY",
+ fn() {
+ const pem = `-----BEGIN EC PRIVATE KEY-----
+MDECAQEEIIThPSZ00CNW1UD5Ju9mhplv6SSs3T5objYjlx11gHW9oAoGCCqGSM49
+AwEH
+-----END EC PRIVATE KEY-----`;
+ createSign("SHA256").update("test").sign(pem, "base64");
+ },
+});