1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

fix(ext/node): support ieee-p1363 ECDSA signatures and pss salt len (#24981)

Fixes https://github.com/denoland/deno/issues/22919
This commit is contained in:
Divy Srivastava 2024-08-11 02:29:53 -07:00 committed by GitHub
parent feba133711
commit d6f662ac82
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 158 additions and 13 deletions

1
Cargo.lock generated
View file

@ -1781,6 +1781,7 @@ dependencies = [
"digest", "digest",
"dsa", "dsa",
"ecb", "ecb",
"ecdsa",
"ed25519-dalek", "ed25519-dalek",
"elliptic-curve", "elliptic-curve",
"errno 0.2.8", "errno 0.2.8",

View file

@ -40,6 +40,7 @@ der = { version = "0.7.9", features = ["derive"] }
digest = { version = "0.10.5", features = ["core-api", "std"] } digest = { version = "0.10.5", features = ["core-api", "std"] }
dsa = "0.6.1" dsa = "0.6.1"
ecb.workspace = true ecb.workspace = true
ecdsa = "0.16.9"
ed25519-dalek = { version = "2.1.1", features = ["digest", "pkcs8", "rand_core", "signature"] } ed25519-dalek = { version = "2.1.1", features = ["digest", "pkcs8", "rand_core", "signature"] }
elliptic-curve.workspace = true elliptic-curve.workspace = true
errno = "0.2.8" errno = "0.2.8"

View file

@ -362,18 +362,33 @@ pub fn op_node_sign(
#[cppgc] handle: &KeyObjectHandle, #[cppgc] handle: &KeyObjectHandle,
#[buffer] digest: &[u8], #[buffer] digest: &[u8],
#[string] digest_type: &str, #[string] digest_type: &str,
#[smi] pss_salt_length: Option<u32>,
#[smi] dsa_signature_encoding: u32,
) -> Result<Box<[u8]>, AnyError> { ) -> Result<Box<[u8]>, AnyError> {
handle.sign_prehashed(digest_type, digest) handle.sign_prehashed(
digest_type,
digest,
pss_salt_length,
dsa_signature_encoding,
)
} }
#[op2(fast)] #[op2]
pub fn op_node_verify( pub fn op_node_verify(
#[cppgc] handle: &KeyObjectHandle, #[cppgc] handle: &KeyObjectHandle,
#[buffer] digest: &[u8], #[buffer] digest: &[u8],
#[string] digest_type: &str, #[string] digest_type: &str,
#[buffer] signature: &[u8], #[buffer] signature: &[u8],
#[smi] pss_salt_length: Option<u32>,
#[smi] dsa_signature_encoding: u32,
) -> Result<bool, AnyError> { ) -> Result<bool, AnyError> {
handle.verify_prehashed(digest_type, digest, signature) handle.verify_prehashed(
digest_type,
digest,
signature,
pss_salt_length,
dsa_signature_encoding,
)
} }
fn pbkdf2_sync( fn pbkdf2_sync(

View file

@ -17,12 +17,36 @@ use super::keys::EcPrivateKey;
use super::keys::EcPublicKey; use super::keys::EcPublicKey;
use super::keys::KeyObjectHandle; use super::keys::KeyObjectHandle;
use super::keys::RsaPssHashAlgorithm; use super::keys::RsaPssHashAlgorithm;
use core::ops::Add;
use ecdsa::der::MaxOverhead;
use ecdsa::der::MaxSize;
use elliptic_curve::generic_array::ArrayLength;
use elliptic_curve::FieldBytesSize;
fn dsa_signature<C: elliptic_curve::PrimeCurve>(
encoding: u32,
signature: ecdsa::Signature<C>,
) -> Result<Box<[u8]>, AnyError>
where
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
match encoding {
// DER
0 => Ok(signature.to_der().to_bytes().to_vec().into_boxed_slice()),
// IEEE P1363
1 => Ok(signature.to_bytes().to_vec().into_boxed_slice()),
_ => Err(type_error("invalid DSA signature encoding")),
}
}
impl KeyObjectHandle { impl KeyObjectHandle {
pub fn sign_prehashed( pub fn sign_prehashed(
&self, &self,
digest_type: &str, digest_type: &str,
digest: &[u8], digest: &[u8],
pss_salt_length: Option<u32>,
dsa_signature_encoding: u32,
) -> Result<Box<[u8]>, AnyError> { ) -> Result<Box<[u8]>, AnyError> {
let private_key = self let private_key = self
.as_private_key() .as_private_key()
@ -67,6 +91,9 @@ impl KeyObjectHandle {
} }
None => {} None => {}
}; };
if let Some(s) = pss_salt_length {
salt_length = Some(s as usize);
}
let pss = match_fixed_digest_with_oid!( let pss = match_fixed_digest_with_oid!(
digest_type, digest_type,
fn <D>(algorithm: Option<RsaPssHashAlgorithm>) { fn <D>(algorithm: Option<RsaPssHashAlgorithm>) {
@ -120,21 +147,24 @@ impl KeyObjectHandle {
let signature: p224::ecdsa::Signature = signing_key let signature: p224::ecdsa::Signature = signing_key
.sign_prehash(digest) .sign_prehash(digest)
.map_err(|_| type_error("failed to sign digest"))?; .map_err(|_| type_error("failed to sign digest"))?;
Ok(signature.to_der().to_bytes())
dsa_signature(dsa_signature_encoding, signature)
} }
EcPrivateKey::P256(key) => { EcPrivateKey::P256(key) => {
let signing_key = p256::ecdsa::SigningKey::from(key); let signing_key = p256::ecdsa::SigningKey::from(key);
let signature: p256::ecdsa::Signature = signing_key let signature: p256::ecdsa::Signature = signing_key
.sign_prehash(digest) .sign_prehash(digest)
.map_err(|_| type_error("failed to sign digest"))?; .map_err(|_| type_error("failed to sign digest"))?;
Ok(signature.to_der().to_bytes())
dsa_signature(dsa_signature_encoding, signature)
} }
EcPrivateKey::P384(key) => { EcPrivateKey::P384(key) => {
let signing_key = p384::ecdsa::SigningKey::from(key); let signing_key = p384::ecdsa::SigningKey::from(key);
let signature: p384::ecdsa::Signature = signing_key let signature: p384::ecdsa::Signature = signing_key
.sign_prehash(digest) .sign_prehash(digest)
.map_err(|_| type_error("failed to sign digest"))?; .map_err(|_| type_error("failed to sign digest"))?;
Ok(signature.to_der().to_bytes())
dsa_signature(dsa_signature_encoding, signature)
} }
}, },
AsymmetricPrivateKey::X25519(_) => { AsymmetricPrivateKey::X25519(_) => {
@ -154,6 +184,8 @@ impl KeyObjectHandle {
digest_type: &str, digest_type: &str,
digest: &[u8], digest: &[u8],
signature: &[u8], signature: &[u8],
pss_salt_length: Option<u32>,
dsa_signature_encoding: u32,
) -> Result<bool, AnyError> { ) -> Result<bool, AnyError> {
let public_key = self let public_key = self
.as_public_key() .as_public_key()
@ -195,6 +227,9 @@ impl KeyObjectHandle {
} }
None => {} None => {}
}; };
if let Some(s) = pss_salt_length {
salt_length = Some(s as usize);
}
let pss = match_fixed_digest_with_oid!( let pss = match_fixed_digest_with_oid!(
digest_type, digest_type,
fn <D>(algorithm: Option<RsaPssHashAlgorithm>) { fn <D>(algorithm: Option<RsaPssHashAlgorithm>) {
@ -229,20 +264,38 @@ impl KeyObjectHandle {
AsymmetricPublicKey::Ec(key) => match key { AsymmetricPublicKey::Ec(key) => match key {
EcPublicKey::P224(key) => { EcPublicKey::P224(key) => {
let verifying_key = p224::ecdsa::VerifyingKey::from(key); let verifying_key = p224::ecdsa::VerifyingKey::from(key);
let signature = p224::ecdsa::Signature::from_der(signature) let signature = if dsa_signature_encoding == 0 {
.map_err(|_| type_error("Invalid ECDSA signature"))?; p224::ecdsa::Signature::from_der(signature)
} else {
p224::ecdsa::Signature::from_bytes(signature.into())
};
let Ok(signature) = signature else {
return Ok(false);
};
Ok(verifying_key.verify_prehash(digest, &signature).is_ok()) Ok(verifying_key.verify_prehash(digest, &signature).is_ok())
} }
EcPublicKey::P256(key) => { EcPublicKey::P256(key) => {
let verifying_key = p256::ecdsa::VerifyingKey::from(key); let verifying_key = p256::ecdsa::VerifyingKey::from(key);
let signature = p256::ecdsa::Signature::from_der(signature) let signature = if dsa_signature_encoding == 0 {
.map_err(|_| type_error("Invalid ECDSA signature"))?; p256::ecdsa::Signature::from_der(signature)
} else {
p256::ecdsa::Signature::from_bytes(signature.into())
};
let Ok(signature) = signature else {
return Ok(false);
};
Ok(verifying_key.verify_prehash(digest, &signature).is_ok()) Ok(verifying_key.verify_prehash(digest, &signature).is_ok())
} }
EcPublicKey::P384(key) => { EcPublicKey::P384(key) => {
let verifying_key = p384::ecdsa::VerifyingKey::from(key); let verifying_key = p384::ecdsa::VerifyingKey::from(key);
let signature = p384::ecdsa::Signature::from_der(signature) let signature = if dsa_signature_encoding == 0 {
.map_err(|_| type_error("Invalid ECDSA signature"))?; p384::ecdsa::Signature::from_der(signature)
} else {
p384::ecdsa::Signature::from_bytes(signature.into())
};
let Ok(signature) = signature else {
return Ok(false);
};
Ok(verifying_key.verify_prehash(digest, &signature).is_ok()) Ok(verifying_key.verify_prehash(digest, &signature).is_ok())
} }
}, },

View file

@ -58,6 +58,35 @@ export interface VerifyKeyObjectInput extends SigningOptions {
key: KeyObject; key: KeyObject;
} }
function getSaltLength(options) {
return getIntOption("saltLength", options);
}
function getDSASignatureEncoding(options) {
if (typeof options === "object") {
const { dsaEncoding = "der" } = options;
if (dsaEncoding === "der") {
return 0;
} else if (dsaEncoding === "ieee-p1363") {
return 1;
}
throw new ERR_INVALID_ARG_VALUE("options.dsaEncoding", dsaEncoding);
}
return 0;
}
function getIntOption(name, options) {
const value = options[name];
if (value !== undefined) {
if (value === value >> 0) {
return value;
}
throw new ERR_INVALID_ARG_VALUE(`options.${name}`, value);
}
return undefined;
}
export type KeyLike = string | Buffer | KeyObject; export type KeyLike = string | Buffer | KeyObject;
export class SignImpl extends Writable { export class SignImpl extends Writable {
@ -86,6 +115,13 @@ export class SignImpl extends Writable {
encoding?: BinaryToTextEncoding, encoding?: BinaryToTextEncoding,
): Buffer | string { ): Buffer | string {
const res = prepareAsymmetricKey(privateKey, kConsumePrivate); const res = prepareAsymmetricKey(privateKey, kConsumePrivate);
// Options specific to RSA-PSS
const pssSaltLength = getSaltLength(privateKey);
// Options specific to (EC)DSA
const dsaSigEnc = getDSASignatureEncoding(privateKey);
let handle; let handle;
if ("handle" in res) { if ("handle" in res) {
handle = res.handle; handle = res.handle;
@ -101,6 +137,8 @@ export class SignImpl extends Writable {
handle, handle,
this.hash.digest(), this.hash.digest(),
this.#digestType, this.#digestType,
pssSaltLength,
dsaSigEnc,
)); ));
return encoding ? ret.toString(encoding) : ret; return encoding ? ret.toString(encoding) : ret;
} }
@ -152,6 +190,13 @@ export class VerifyImpl extends Writable {
encoding?: BinaryToTextEncoding, encoding?: BinaryToTextEncoding,
): boolean { ): boolean {
const res = prepareAsymmetricKey(publicKey, kConsumePublic); const res = prepareAsymmetricKey(publicKey, kConsumePublic);
// Options specific to RSA-PSS
const pssSaltLength = getSaltLength(publicKey);
// Options specific to (EC)DSA
const dsaSigEnc = getDSASignatureEncoding(publicKey);
let handle; let handle;
if ("handle" in res) { if ("handle" in res) {
handle = res.handle; handle = res.handle;
@ -168,6 +213,8 @@ export class VerifyImpl extends Writable {
this.hash.digest(), this.hash.digest(),
this.#digestType, this.#digestType,
Buffer.from(signature, encoding), Buffer.from(signature, encoding),
pssSaltLength,
dsaSigEnc,
); );
} }
} }

View file

@ -1,7 +1,13 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals } from "@std/assert"; import { assert, assertEquals } from "@std/assert";
import { createSign, createVerify, sign, verify } from "node:crypto"; import {
createSign,
createVerify,
generateKeyPairSync,
sign,
verify,
} from "node:crypto";
import { Buffer } from "node:buffer"; import { Buffer } from "node:buffer";
import fixtures from "../testdata/crypto_digest_fixtures.json" with { import fixtures from "../testdata/crypto_digest_fixtures.json" with {
type: "json", type: "json",
@ -179,3 +185,25 @@ Deno.test("crypto.createVerify|verify - compare with node", async (t) => {
}); });
} }
}); });
Deno.test("crypto sign|verify dsaEncoding", () => {
const { privateKey, publicKey } = generateKeyPairSync("ec", {
namedCurve: "P-256",
});
const sign = createSign("SHA256");
sign.write("some data to sign");
sign.end();
// @ts-ignore FIXME: types dont allow this
privateKey.dsaEncoding = "ieee-p1363";
const signature = sign.sign(privateKey, "hex");
const verify = createVerify("SHA256");
verify.write("some data to sign");
verify.end();
// @ts-ignore FIXME: types dont allow this
publicKey.dsaEncoding = "ieee-p1363";
assert(verify.verify(publicKey, signature, "hex"));
});