mirror of
https://github.com/denoland/deno.git
synced 2024-11-28 16:20:57 -05:00
fix(ext/node): Support private EC key signing (#22914)
Fixes https://github.com/denoland/deno/issues/18972 Support for web-push VAPID keys & jws signing - Fixes EC keygen to return raw private key and uncompressed public key point. - Support for `EC PRIVATE KEY`
This commit is contained in:
parent
9c348a0acd
commit
b00f076017
4 changed files with 76 additions and 29 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1683,6 +1683,7 @@ dependencies = [
|
|||
"ripemd",
|
||||
"rsa",
|
||||
"scrypt",
|
||||
"sec1",
|
||||
"serde",
|
||||
"sha-1",
|
||||
"sha2",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
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")),
|
||||
}
|
||||
_ => return Err(type_error("Unsupported named curve")),
|
||||
};
|
||||
|
||||
let rng = ring::rand::SystemRandom::new();
|
||||
|
||||
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()))
|
||||
}
|
||||
|
||||
#[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" => {
|
||||
|
|
|
@ -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");
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue