use openssl::{
hash::MessageDigest,
pkey::{PKey, Private},
rsa::{Rsa, RsaPrivateKeyBuilder},
sign::Signer,
};
use crate::{RawSigner, RawSignerError, SigningAlg, openssl::OpenSslMutex};
enum RsaSigningAlg {
Ps256,
Ps384,
Ps512,
}
pub(crate) struct RsaSigner {
alg: RsaSigningAlg,
private_key: PKey<Private>,
}
impl RsaSigner {
pub(crate) fn from_private_key(
private_key: &[u8],
alg: SigningAlg,
) -> Result<Self, RawSignerError> {
let _openssl = OpenSslMutex::acquire()?;
let private_key = Rsa::private_key_from_pem(private_key)?;
let n = private_key.n().to_owned()?;
let e = private_key.e().to_owned()?;
let d = private_key.d().to_owned()?;
let po = private_key.p();
let qo = private_key.q();
let dmp1o = private_key.dmp1();
let dmq1o = private_key.dmq1();
let iqmpo = private_key.iqmp();
let mut pk_builder = RsaPrivateKeyBuilder::new(n, e, d)?;
if let Some(p) = po
&& let Some(q) = qo
{
pk_builder = pk_builder.set_factors(p.to_owned()?, q.to_owned()?)?;
}
if let Some(dmp1) = dmp1o
&& let Some(dmq1) = dmq1o
&& let Some(iqmp) = iqmpo
{
pk_builder =
pk_builder.set_crt_params(dmp1.to_owned()?, dmq1.to_owned()?, iqmp.to_owned()?)?;
}
let private_key = PKey::from_rsa(pk_builder.build())?;
let alg: RsaSigningAlg = match alg {
SigningAlg::Ps256 => RsaSigningAlg::Ps256,
SigningAlg::Ps384 => RsaSigningAlg::Ps384,
SigningAlg::Ps512 => RsaSigningAlg::Ps512,
_ => {
return Err(RawSignerError::InternalError(
"RsaSigner should be used only for SigningAlg::Ps***".to_string(),
));
}
};
Ok(RsaSigner { alg, private_key })
}
}
impl RawSigner for RsaSigner {
fn sign(&self, data: &[u8]) -> Result<Vec<u8>, RawSignerError> {
let _openssl = OpenSslMutex::acquire()?;
let mut signer = match self.alg {
RsaSigningAlg::Ps256 => {
let mut signer = Signer::new(MessageDigest::sha256(), &self.private_key)?;
signer.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?;
signer.set_rsa_mgf1_md(MessageDigest::sha256())?;
signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?;
signer
}
RsaSigningAlg::Ps384 => {
let mut signer = Signer::new(MessageDigest::sha384(), &self.private_key)?;
signer.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?;
signer.set_rsa_mgf1_md(MessageDigest::sha384())?;
signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?;
signer
}
RsaSigningAlg::Ps512 => {
let mut signer = Signer::new(MessageDigest::sha512(), &self.private_key)?;
signer.set_rsa_padding(openssl::rsa::Padding::PKCS1_PSS)?;
signer.set_rsa_mgf1_md(MessageDigest::sha512())?;
signer.set_rsa_pss_saltlen(openssl::sign::RsaPssSaltlen::DIGEST_LENGTH)?;
signer
}
};
Ok(signer.sign_oneshot_to_vec(data)?)
}
fn max_signature_size(&self) -> usize {
self.private_key.size()
}
fn alg(&self) -> SigningAlg {
match self.alg {
RsaSigningAlg::Ps256 => SigningAlg::Ps256,
RsaSigningAlg::Ps384 => SigningAlg::Ps384,
RsaSigningAlg::Ps512 => SigningAlg::Ps512,
}
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::panic)]
use super::*;
#[test]
fn rejects_non_rsa_alg() {
let key = include_bytes!("../../../tests/fixtures/raw_signature/ps256.priv");
let Err(err) = RsaSigner::from_private_key(key, SigningAlg::Es256) else {
panic!("expected error");
};
assert!(matches!(err, RawSignerError::InternalError(_)));
}
#[test]
fn rejects_bad_pem() {
let Err(err) = RsaSigner::from_private_key(b"not a PEM key", SigningAlg::Ps256) else {
panic!("expected error");
};
assert!(matches!(err, RawSignerError::CryptoLibraryError(_)));
}
}