use core::ops::Add;
use ecdsa::der::MaxOverhead;
use ecdsa::der::MaxSize;
use elliptic_curve::FieldBytesSize;
use elliptic_curve::generic_array::ArrayLength;
use rand::rngs::OsRng;
use rsa::signature::hazmat::PrehashSigner as _;
use rsa::signature::hazmat::PrehashVerifier as _;
use rsa::traits::PublicKeyParts as _;
use rsa::traits::SignatureScheme as _;
use spki::der::Decode;
use super::keys::AsymmetricPrivateKey;
use super::keys::AsymmetricPublicKey;
use super::keys::EcPrivateKey;
use super::keys::EcPublicKey;
use super::keys::KeyObjectHandle;
use super::keys::RsaPssHashAlgorithm;
use crate::digest::match_fixed_digest;
use crate::digest::match_fixed_digest_with_oid;
const RSA_PKCS1_PADDING: u32 = 1;
const RSA_PKCS1_PSS_PADDING: u32 = 6;
const RSA_PKCS1_OAEP_PADDING: u32 = 4;
fn dsa_signature<C: elliptic_curve::PrimeCurve>(
encoding: u32,
signature: ecdsa::Signature<C>,
) -> Result<Box<[u8]>, KeyObjectHandlePrehashedSignAndVerifyError>
where
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
match encoding {
0 => Ok(signature.to_der().to_bytes().to_vec().into_boxed_slice()),
1 => Ok(signature.to_bytes().to_vec().into_boxed_slice()),
_ => Err(
KeyObjectHandlePrehashedSignAndVerifyError::InvalidDsaSignatureEncoding,
),
}
}
fn dsa_sig_to_p1363(sig: &dsa::Signature, q_len: usize) -> Box<[u8]> {
let mut result = vec![0u8; q_len * 2];
let r_bytes = sig.r().to_bytes_be();
let s_bytes = sig.s().to_bytes_be();
if r_bytes.len() <= q_len {
result[q_len - r_bytes.len()..q_len].copy_from_slice(&r_bytes);
}
if s_bytes.len() <= q_len {
result[2 * q_len - s_bytes.len()..2 * q_len].copy_from_slice(&s_bytes);
}
result.into_boxed_slice()
}
fn dsa_sig_from_p1363(bytes: &[u8], q_len: usize) -> Option<dsa::Signature> {
if bytes.len() != 2 * q_len {
return None;
}
let r = dsa::BigUint::from_bytes_be(&bytes[..q_len]);
let s = dsa::BigUint::from_bytes_be(&bytes[q_len..]);
dsa::Signature::from_components(r, s).ok()
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
#[class(type)]
pub enum KeyObjectHandlePrehashedSignAndVerifyError {
#[error("invalid DSA signature encoding")]
InvalidDsaSignatureEncoding,
#[error("key is not a private key")]
KeyIsNotPrivate,
#[error("digest not allowed for RSA signature: {0}")]
DigestNotAllowedForRsaSignature(String),
#[class(generic)]
#[error("failed to sign digest with RSA")]
FailedToSignDigestWithRsa,
#[class(generic)]
#[property("code" = "ERR_OSSL_RSA_DIGEST_TOO_BIG_FOR_RSA_KEY")]
#[error("error:02000070:rsa routines::digest too big for rsa key")]
DigestTooBigForRsaKey,
#[error("digest not allowed for RSA-PSS signature: {0}")]
DigestNotAllowedForRsaPssSignature(String),
#[class(generic)]
#[error("failed to sign digest with RSA-PSS")]
FailedToSignDigestWithRsaPss,
#[error("failed to sign digest with DSA")]
FailedToSignDigestWithDsa,
#[error(
"rsa-pss with different mf1 hash algorithm and hash algorithm is not supported"
)]
RsaPssHashAlgorithmUnsupported,
#[class(generic)]
#[error("{actual} digest not allowed")]
PrivateKeyDisallowsUsage { actual: String, expected: String },
#[class(generic)]
#[error("pss saltlen too small")]
PssSaltLenTooSmall,
#[error("failed to sign digest")]
FailedToSignDigest,
#[class(generic)]
#[error("operation not supported for this keytype")]
#[property("code" = "ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE")]
X25519KeyCannotBeUsedForSigning,
#[class(generic)]
#[error("Unsupported crypto operation")]
#[property("code" = "ERR_CRYPTO_UNSUPPORTED_OPERATION")]
Ed25519KeyCannotBeUsedForPrehashedSigning,
#[class(generic)]
#[error("operation not supported for this keytype")]
#[property("code" = "ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE")]
DhKeyCannotBeUsedForSigning,
#[error("key is not a public or private key")]
KeyIsNotPublicOrPrivate,
#[error("Invalid DSA signature")]
InvalidDsaSignature,
#[class(generic)]
#[error("operation not supported for this keytype")]
#[property("code" = "ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE")]
X25519KeyCannotBeUsedForVerification,
#[class(generic)]
#[error("Unsupported crypto operation")]
#[property("code" = "ERR_CRYPTO_UNSUPPORTED_OPERATION")]
Ed25519KeyCannotBeUsedForPrehashedVerification,
#[class(generic)]
#[error("operation not supported for this keytype")]
#[property("code" = "ERR_OSSL_EVP_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE")]
DhKeyCannotBeUsedForVerification,
#[class(generic)]
#[error(
"error:1C8000A5:Provider routines::illegal or unsupported padding mode"
)]
#[property("code" = "ERR_OSSL_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE")]
IllegalOrUnsupportedPaddingMode,
}
const RSA_PSS_SALTLEN_DIGEST: i32 = -1;
const RSA_PSS_SALTLEN_MAX_SIGN: i32 = -2;
fn resolve_pss_salt_length<D: digest::Digest>(
pss_salt_length: Option<i32>,
key_size_bits: Option<usize>,
) -> usize {
match pss_salt_length {
Some(RSA_PSS_SALTLEN_DIGEST) => <D as digest::Digest>::output_size(),
Some(RSA_PSS_SALTLEN_MAX_SIGN) => {
let hash_len = <D as digest::Digest>::output_size();
if let Some(key_bits) = key_size_bits {
let key_bytes = key_bits / 8;
key_bytes.saturating_sub(hash_len + 2)
} else {
hash_len
}
}
Some(len) if len >= 0 => len as usize,
Some(_) => <D as digest::Digest>::output_size(), None => {
if let Some(key_bits) = key_size_bits {
let key_bytes = key_bits / 8;
let hash_len = <D as digest::Digest>::output_size();
key_bytes.saturating_sub(hash_len + 2)
} else {
<D as digest::Digest>::output_size()
}
}
}
}
fn new_pss_scheme(
digest_type: &str,
pss_salt_length: Option<i32>,
key_size_bits: Option<usize>,
) -> Result<rsa::pss::Pss, KeyObjectHandlePrehashedSignAndVerifyError> {
let pss = match_fixed_digest_with_oid!(
digest_type,
fn <D>(algorithm: Option<RsaPssHashAlgorithm>) {
let _: Option<RsaPssHashAlgorithm> = algorithm;
let salt_len = resolve_pss_salt_length::<D>(pss_salt_length, key_size_bits);
rsa::pss::Pss::new_with_salt::<D>(salt_len)
},
_ => {
return Err(KeyObjectHandlePrehashedSignAndVerifyError::DigestNotAllowedForRsaPssSignature(digest_type.to_string()));
}
);
Ok(pss)
}
fn recover_pss_salt_len<D>(key: &rsa::RsaPublicKey, sig: &[u8]) -> Option<usize>
where
D: digest::Digest,
{
use rsa::traits::PublicKeyParts;
let h_len = <D as digest::Digest>::output_size();
let key_bits = key.n().bits();
let em_bits = key_bits - 1;
let em_len = em_bits.div_ceil(8);
let key_len = key_bits.div_ceil(8);
let sig_bn = rsa::BigUint::from_bytes_be(sig);
let em_bn = rsa::hazmat::rsa_encrypt(key, &sig_bn).ok()?;
let em_be = em_bn.to_bytes_be();
if em_be.len() > key_len {
return None;
}
let mut em_padded = vec![0u8; key_len];
em_padded[key_len - em_be.len()..].copy_from_slice(&em_be);
let em = &mut em_padded[key_len - em_len..];
if em[em_len - 1] != 0xBC {
return None;
}
if em_len < h_len + 2 {
return None;
}
let db_len = em_len - h_len - 1;
let h = em[db_len..db_len + h_len].to_vec();
let masked_db = &mut em[..db_len];
let mut counter: u32 = 0;
let mut pos = 0;
while pos < db_len {
let mut hasher = D::new();
hasher.update(h.as_slice());
hasher.update(counter.to_be_bytes());
let hash_out = hasher.finalize();
let hash_bytes: &[u8] = hash_out.as_ref();
let end = (pos + h_len).min(db_len);
for j in pos..end {
masked_db[j] ^= hash_bytes[j - pos];
}
pos += h_len;
counter += 1;
}
let db = masked_db;
let top_bits_to_clear = 8 * em_len - em_bits;
if top_bits_to_clear < 8 {
db[0] &= 0xFF_u8 >> top_bits_to_clear;
}
let mut salt_start = None;
for (i, &b) in db.iter().enumerate() {
if b == 0x01 {
salt_start = Some(i + 1);
break;
} else if b != 0x00 {
return None; }
}
let salt_start = salt_start?;
Some(db_len - salt_start)
}
impl KeyObjectHandle {
pub fn sign_prehashed(
&self,
digest_type: &str,
digest: &[u8],
pss_salt_length: Option<i32>,
padding: Option<u32>,
dsa_signature_encoding: u32,
) -> Result<Box<[u8]>, KeyObjectHandlePrehashedSignAndVerifyError> {
let private_key = self
.as_private_key()
.ok_or(KeyObjectHandlePrehashedSignAndVerifyError::KeyIsNotPrivate)?;
match private_key {
AsymmetricPrivateKey::Rsa(key) => {
if padding == Some(RSA_PKCS1_OAEP_PADDING) {
return Err(KeyObjectHandlePrehashedSignAndVerifyError::IllegalOrUnsupportedPaddingMode);
}
if padding == Some(RSA_PKCS1_PSS_PADDING) {
let pss = new_pss_scheme(
digest_type,
pss_salt_length,
Some(key.n().bits()),
)?;
let signature = pss
.sign(Some(&mut OsRng), key, digest)
.map_err(|_| KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigestWithRsaPss)?;
return Ok(signature.into());
}
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(KeyObjectHandlePrehashedSignAndVerifyError::DigestNotAllowedForRsaSignature(digest_type.to_string()))
}
)
};
let signature = signer.sign(Some(&mut OsRng), key, digest).map_err(
|e| {
if e == rsa::Error::MessageTooLong {
KeyObjectHandlePrehashedSignAndVerifyError::DigestTooBigForRsaKey
} else {
KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigestWithRsa
}
},
)?;
Ok(signature.into())
}
AsymmetricPrivateKey::RsaPss(key) => {
if padding == Some(RSA_PKCS1_PADDING) {
return Err(KeyObjectHandlePrehashedSignAndVerifyError::IllegalOrUnsupportedPaddingMode);
}
let mut hash_algorithm = None;
let mut salt_length = None;
if let Some(details) = &key.details {
hash_algorithm = Some(details.hash_algorithm);
salt_length = Some(details.salt_length as usize);
}
if let Some(min_salt) = salt_length
&& let Some(requested) = pss_salt_length
&& requested >= 0
&& (requested as usize) < min_salt
{
return Err(
KeyObjectHandlePrehashedSignAndVerifyError::PssSaltLenTooSmall,
);
}
let pss = match_fixed_digest_with_oid!(
digest_type,
fn <D>(algorithm: Option<RsaPssHashAlgorithm>) {
if let Some(hash_algorithm) = hash_algorithm.take()
&& Some(hash_algorithm) != algorithm {
return Err(KeyObjectHandlePrehashedSignAndVerifyError::PrivateKeyDisallowsUsage {
actual: digest_type.to_string(),
expected: hash_algorithm.as_str().to_string(),
});
}
let resolved = if pss_salt_length.is_some() {
let r = resolve_pss_salt_length::<D>(pss_salt_length, Some(key.key.n().bits()));
if let Some(min_salt) = salt_length && r < min_salt {
return Err(KeyObjectHandlePrehashedSignAndVerifyError::PssSaltLenTooSmall);
}
r
} else if let Some(sl) = salt_length {
sl
} else {
<D as digest::Digest>::output_size()
};
rsa::pss::Pss::new_with_salt::<D>(resolved)
},
_ => {
return Err(KeyObjectHandlePrehashedSignAndVerifyError::DigestNotAllowedForRsaPssSignature(digest_type.to_string()));
}
);
let signature = pss
.sign(Some(&mut OsRng), &key.key, digest)
.map_err(|_| KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigestWithRsaPss)?;
Ok(signature.into())
}
AsymmetricPrivateKey::Dsa(key) => {
let res = match_fixed_digest!(
digest_type,
fn <D>() {
key.sign_prehashed_rfc6979::<D>(digest)
},
_ => {
return Err(KeyObjectHandlePrehashedSignAndVerifyError::DigestNotAllowedForRsaSignature(digest_type.to_string()))
}
);
let signature =
res.map_err(|_| KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigestWithDsa)?;
if dsa_signature_encoding == 0 {
Ok(signature.into())
} else {
let q_len =
key.verifying_key().components().q().bits().div_ceil(8);
Ok(dsa_sig_to_p1363(&signature, q_len))
}
}
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(|_| KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigest)?;
dsa_signature(dsa_signature_encoding, signature)
}
EcPrivateKey::P256(key) => {
let signing_key = p256::ecdsa::SigningKey::from(key);
let signature: p256::ecdsa::Signature = signing_key
.sign_prehash(digest)
.map_err(|_| KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigest)?;
dsa_signature(dsa_signature_encoding, signature)
}
EcPrivateKey::P384(key) => {
let signing_key = p384::ecdsa::SigningKey::from(key);
let signature: p384::ecdsa::Signature = signing_key
.sign_prehash(digest)
.map_err(|_| KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigest)?;
dsa_signature(dsa_signature_encoding, signature)
}
EcPrivateKey::P521(key) => {
let signing_key = p521::ecdsa::SigningKey::from_bytes(&key.to_bytes())
.map_err(|_| KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigest)?;
let signature: p521::ecdsa::Signature = signing_key
.sign_prehash(digest)
.map_err(|_| KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigest)?;
dsa_signature(dsa_signature_encoding, signature)
}
EcPrivateKey::Secp256k1(key) => {
let signing_key = k256::ecdsa::SigningKey::from(key);
let signature: k256::ecdsa::Signature = signing_key
.sign_prehash(digest)
.map_err(|_| KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigest)?;
dsa_signature(dsa_signature_encoding, signature)
}
},
AsymmetricPrivateKey::X25519(_) | AsymmetricPrivateKey::X448(_) => {
Err(KeyObjectHandlePrehashedSignAndVerifyError::X25519KeyCannotBeUsedForSigning)
}
AsymmetricPrivateKey::Ed25519(_) | AsymmetricPrivateKey::Ed448(_) => Err(KeyObjectHandlePrehashedSignAndVerifyError::Ed25519KeyCannotBeUsedForPrehashedSigning),
AsymmetricPrivateKey::Dh(_) => {
Err(KeyObjectHandlePrehashedSignAndVerifyError::DhKeyCannotBeUsedForSigning)
}
}
}
pub fn verify_prehashed(
&self,
digest_type: &str,
digest: &[u8],
signature: &[u8],
pss_salt_length: Option<i32>,
padding: Option<u32>,
dsa_signature_encoding: u32,
) -> Result<bool, KeyObjectHandlePrehashedSignAndVerifyError> {
let public_key = self.as_public_key().ok_or(
KeyObjectHandlePrehashedSignAndVerifyError::KeyIsNotPublicOrPrivate,
)?;
match &*public_key {
AsymmetricPublicKey::Rsa(key) => {
if padding == Some(RSA_PKCS1_PSS_PADDING) {
let effective_salt_length = if pss_salt_length == Some(RSA_PSS_SALTLEN_MAX_SIGN) {
let recovered = match_fixed_digest_with_oid!(
digest_type,
fn <D>(algorithm: Option<RsaPssHashAlgorithm>) {
let _: Option<RsaPssHashAlgorithm> = algorithm;
recover_pss_salt_len::<D>(key, signature)
},
_ => {
return Err(KeyObjectHandlePrehashedSignAndVerifyError::DigestNotAllowedForRsaPssSignature(digest_type.to_string()));
}
);
match recovered {
Some(s) => Some(s as i32),
None => return Ok(false),
}
} else {
pss_salt_length
};
let pss = new_pss_scheme(
digest_type,
effective_salt_length,
Some(key.n().bits()),
)?;
return Ok(pss.verify(key, digest, signature).is_ok());
}
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(KeyObjectHandlePrehashedSignAndVerifyError::DigestNotAllowedForRsaSignature(digest_type.to_string()))
}
)
};
Ok(signer.verify(key, digest, signature).is_ok())
}
AsymmetricPublicKey::RsaPss(key) => {
if padding == Some(RSA_PKCS1_PADDING) {
return Err(KeyObjectHandlePrehashedSignAndVerifyError::IllegalOrUnsupportedPaddingMode);
}
let mut hash_algorithm = None;
let mut salt_length = None;
if let Some(details) = &key.details {
hash_algorithm = Some(details.hash_algorithm);
salt_length = Some(details.salt_length as usize);
}
let pss = match_fixed_digest_with_oid!(
digest_type,
fn <D>(algorithm: Option<RsaPssHashAlgorithm>) {
if let Some(hash_algorithm) = hash_algorithm.take()
&& Some(hash_algorithm) != algorithm {
return Err(KeyObjectHandlePrehashedSignAndVerifyError::PrivateKeyDisallowsUsage {
actual: digest_type.to_string(),
expected: hash_algorithm.as_str().to_string(),
});
}
let resolved = if pss_salt_length.is_some() {
resolve_pss_salt_length::<D>(pss_salt_length, Some(key.key.n().bits()))
} else if let Some(sl) = salt_length {
sl
} else {
<D as digest::Digest>::output_size()
};
rsa::pss::Pss::new_with_salt::<D>(resolved)
},
_ => {
return Err(KeyObjectHandlePrehashedSignAndVerifyError::DigestNotAllowedForRsaPssSignature(digest_type.to_string()));
}
);
Ok(pss.verify(&key.key, digest, signature).is_ok())
}
AsymmetricPublicKey::Dsa(key) => {
let sig = if dsa_signature_encoding == 0 {
let Ok(sig) = dsa::Signature::from_der(signature) else {
return Ok(false);
};
sig
} else {
let q_len = key.components().q().bits().div_ceil(8);
let Some(sig) = dsa_sig_from_p1363(signature, q_len) else {
return Ok(false);
};
sig
};
Ok(key.verify_prehash(digest, &sig).is_ok())
}
AsymmetricPublicKey::Ec(key) => match key {
EcPublicKey::P224(key) => {
let verifying_key = p224::ecdsa::VerifyingKey::from(key);
let signature = if dsa_signature_encoding == 0 {
p224::ecdsa::Signature::from_der(signature)
} else {
p224::ecdsa::Signature::try_from(signature)
};
let Ok(signature) = signature else {
return Ok(false);
};
Ok(verifying_key.verify_prehash(digest, &signature).is_ok())
}
EcPublicKey::P256(key) => {
let verifying_key = p256::ecdsa::VerifyingKey::from(key);
let signature = if dsa_signature_encoding == 0 {
p256::ecdsa::Signature::from_der(signature)
} else {
p256::ecdsa::Signature::try_from(signature)
};
let Ok(signature) = signature else {
return Ok(false);
};
Ok(verifying_key.verify_prehash(digest, &signature).is_ok())
}
EcPublicKey::P384(key) => {
let verifying_key = p384::ecdsa::VerifyingKey::from(key);
let signature = if dsa_signature_encoding == 0 {
p384::ecdsa::Signature::from_der(signature)
} else {
p384::ecdsa::Signature::try_from(signature)
};
let Ok(signature) = signature else {
return Ok(false);
};
Ok(verifying_key.verify_prehash(digest, &signature).is_ok())
}
EcPublicKey::P521(key) => {
let Ok(verifying_key) = p521::ecdsa::VerifyingKey::from_affine(*key.as_affine()) else {
return Ok(false);
};
let signature = if dsa_signature_encoding == 0 {
p521::ecdsa::Signature::from_der(signature)
} else {
p521::ecdsa::Signature::try_from(signature)
};
let Ok(signature) = signature else {
return Ok(false);
};
Ok(verifying_key.verify_prehash(digest, &signature).is_ok())
}
EcPublicKey::Secp256k1(key) => {
let verifying_key = k256::ecdsa::VerifyingKey::from(key);
let signature = if dsa_signature_encoding == 0 {
k256::ecdsa::Signature::from_der(signature)
} else {
k256::ecdsa::Signature::try_from(signature)
};
let Ok(signature) = signature else {
return Ok(false);
};
Ok(verifying_key.verify_prehash(digest, &signature).is_ok())
}
},
AsymmetricPublicKey::X25519(_) | AsymmetricPublicKey::X448(_) => {
Err(KeyObjectHandlePrehashedSignAndVerifyError::X25519KeyCannotBeUsedForVerification)
}
AsymmetricPublicKey::Ed25519(_) | AsymmetricPublicKey::Ed448(_) => Err(KeyObjectHandlePrehashedSignAndVerifyError::Ed25519KeyCannotBeUsedForPrehashedVerification),
AsymmetricPublicKey::Dh(_) => {
Err(KeyObjectHandlePrehashedSignAndVerifyError::DhKeyCannotBeUsedForVerification)
}
}
}
}