use crate::{
algorithms::pss::{sign_into, sign_with_rng_into},
errors::{Error, Result},
key::GenericRsaPrivateKey,
traits::{
modular::{ModulusParams, Pow, PowBoundedExp},
PublicKeyParts, UnsignedModularInt,
},
};
use core::fmt;
use core::marker::PhantomData;
use digest::{Digest, FixedOutputReset};
use rand_core::TryCryptoRng;
use zeroize::Zeroize;
pub struct GenericSigningKey<D, T, M>
where
D: Digest,
T: UnsignedModularInt + Zeroize,
M: ModulusParams<Modulus = T>,
{
pub(super) inner: GenericRsaPrivateKey<T, M>,
pub(super) salt_len: usize,
pub(super) phantom: PhantomData<D>,
}
impl<D, T, M> fmt::Debug for GenericSigningKey<D, T, M>
where
D: Digest,
T: UnsignedModularInt + Zeroize,
M: ModulusParams<Modulus = T>,
GenericRsaPrivateKey<T, M>: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GenericSigningKey")
.field("inner", &self.inner)
.field("salt_len", &self.salt_len)
.finish()
}
}
#[cfg(feature = "alloc")]
impl<D, T, M> Clone for GenericSigningKey<D, T, M>
where
D: Digest,
T: UnsignedModularInt + Zeroize + Clone,
M: ModulusParams<Modulus = T> + Clone,
M::MontgomeryForm: Clone,
{
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
salt_len: self.salt_len,
phantom: PhantomData,
}
}
}
#[cfg(not(feature = "alloc"))]
impl<D, T, M> Clone for GenericSigningKey<D, T, M>
where
D: Digest,
T: UnsignedModularInt + Zeroize + Clone,
M: ModulusParams<Modulus = T> + Clone,
{
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
salt_len: self.salt_len,
phantom: PhantomData,
}
}
}
impl<D, T, M> GenericSigningKey<D, T, M>
where
D: Digest,
T: UnsignedModularInt + Zeroize,
M: ModulusParams<Modulus = T>,
{
pub fn new(key: GenericRsaPrivateKey<T, M>) -> Self {
Self {
inner: key,
salt_len: <D as Digest>::output_size(),
phantom: PhantomData,
}
}
pub fn new_with_salt_len(key: GenericRsaPrivateKey<T, M>, salt_len: usize) -> Self {
Self {
inner: key,
salt_len,
phantom: PhantomData,
}
}
pub fn salt_len(&self) -> usize {
self.salt_len
}
}
impl<D, T, M> AsRef<GenericRsaPrivateKey<T, M>> for GenericSigningKey<D, T, M>
where
D: Digest,
T: UnsignedModularInt + Zeroize,
M: ModulusParams<Modulus = T>,
{
fn as_ref(&self) -> &GenericRsaPrivateKey<T, M> {
&self.inner
}
}
impl<D, T, M> GenericSigningKey<D, T, M>
where
D: Digest + FixedOutputReset,
T: UnsignedModularInt + Zeroize + crate::traits::modular::TryRandomMod,
T::Bytes: Zeroize,
M: ModulusParams<Modulus = T> + crate::traits::modular::CtModulusParams,
M::MontgomeryForm: Pow<M>
+ PowBoundedExp<M>
+ crate::traits::modular::InvertCt<M>
+ crate::traits::modular::MulCt<M>,
{
pub fn try_sign_with_rng_into<'sig, R: TryCryptoRng + ?Sized>(
&self,
rng: &mut R,
msg: &[u8],
em_storage: &mut [u8],
sig_storage: &'sig mut [u8],
salt_storage: &mut [u8],
) -> Result<&'sig [u8]> {
let digest = D::digest(msg);
self.try_sign_prehash_with_rng_into(
rng,
digest.as_ref(),
em_storage,
sig_storage,
salt_storage,
)
}
pub fn try_sign_prehash_with_rng_into<'sig, R: TryCryptoRng + ?Sized>(
&self,
rng: &mut R,
prehash: &[u8],
em_storage: &mut [u8],
sig_storage: &'sig mut [u8],
salt_storage: &mut [u8],
) -> Result<&'sig [u8]> {
if prehash.len() != <D as Digest>::output_size() {
return Err(Error::InputNotHashed);
}
let salt = salt_storage
.get_mut(..self.salt_len)
.ok_or(Error::OutputBufferTooSmall)?;
rng.try_fill_bytes(salt).map_err(|_| Error::Rng)?;
let mut hash = D::new();
sign_with_rng_into(
rng,
self.inner.n_params(),
crate::traits::keys::PrivateKeyParts::d(&self.inner),
self.inner.e(),
prehash,
salt,
self.inner.size(),
&mut hash,
em_storage,
sig_storage,
)
}
}
impl<D, T, M> GenericSigningKey<D, T, M>
where
D: Digest + FixedOutputReset,
T: UnsignedModularInt + Zeroize,
T::Bytes: Zeroize,
M: ModulusParams<Modulus = T> + crate::traits::modular::CtModulusParams,
M::MontgomeryForm: Pow<M> + PowBoundedExp<M>,
{
pub fn try_sign_prehash_with_salt_into<'sig>(
&self,
prehash: &[u8],
salt: &[u8],
em_storage: &mut [u8],
sig_storage: &'sig mut [u8],
) -> Result<&'sig [u8]> {
if prehash.len() != <D as Digest>::output_size() {
return Err(Error::InputNotHashed);
}
if salt.len() != self.salt_len {
return Err(Error::InvalidArguments);
}
let mut hash = D::new();
sign_into(
self.inner.n_params(),
crate::traits::keys::PrivateKeyParts::d(&self.inner),
self.inner.e(),
prehash,
salt,
self.inner.size(),
&mut hash,
em_storage,
sig_storage,
)
}
}
impl<D, T, M> Zeroize for GenericSigningKey<D, T, M>
where
D: Digest,
T: UnsignedModularInt + Zeroize,
M: ModulusParams<Modulus = T>,
{
fn zeroize(&mut self) {
self.inner.zeroize();
}
}
#[cfg(test)]
mod tests {
#[test]
#[cfg(feature = "encoding")]
fn signing_key_try_sign_with_rng_into_round_trip() {
use crate::pss::{Signature, SigningKey, VerifyingKey};
use crate::RsaPrivateKey;
use pkcs1::DecodeRsaPrivateKey;
use rand::rngs::ChaCha8Rng;
use rand_core::SeedableRng;
use sha2::Sha256;
use signature::Verifier;
const PRIV_KEY_PKCS1_PEM: &str =
include_str!("../../tests/examples/pkcs1/rsa2048-priv.pem");
let priv_key = RsaPrivateKey::from_pkcs1_pem(PRIV_KEY_PKCS1_PEM).unwrap();
let signing_key = SigningKey::<Sha256>::new(priv_key.clone());
let verifying_key = VerifyingKey::<Sha256>::new(priv_key.to_public_key());
let msg: &[u8] = b"blinded pss sign test message";
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let mut em_storage = [0u8; 256];
let mut sig_storage = [0u8; 256];
let mut salt_storage = [0u8; 32];
let sig_slice = signing_key
.try_sign_with_rng_into(
&mut rng,
msg,
&mut em_storage,
&mut sig_storage,
&mut salt_storage,
)
.unwrap();
assert_eq!(sig_slice.len(), 256);
let sig = Signature::try_from(sig_slice).unwrap();
verifying_key.verify(msg, &sig).unwrap();
}
}