Refactor padding modes into submodules (#312)

The padding mode modules have gotten quite large.

This commit refactors types into respective submodules, with the
toplevel module defining the same-named padding schemes.
This commit is contained in:
Tony Arcieri 2023-04-27 07:58:19 -06:00 committed by GitHub
parent d9968bc0c9
commit f5918ad3bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1436 additions and 1256 deletions

View File

@ -53,7 +53,7 @@ impl PartialEq for RsaPrivateKey {
}
impl Hash for RsaPrivateKey {
fn hash<H: Hasher>(&self, state: &mut H) -> () {
fn hash<H: Hasher>(&self, state: &mut H) {
// Domain separator for RSA private keys
state.write(b"RsaPrivateKey");
Hash::hash(&self.pubkey_components, state);

View File

@ -239,6 +239,7 @@ pub use pkcs8;
pub use sha2;
pub use crate::{
errors::{Error, Result},
key::{RsaPrivateKey, RsaPublicKey},
oaep::Oaep,
pkcs1v15::{Pkcs1v15Encrypt, Pkcs1v15Sign},

View File

@ -4,26 +4,27 @@
//!
//! See [code example in the toplevel rustdoc](../index.html#oaep-encryption).
mod decrypting_key;
mod encrypting_key;
pub use self::{decrypting_key::DecryptingKey, encrypting_key::EncryptingKey};
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::fmt;
use core::marker::PhantomData;
use digest::{Digest, DynDigest, FixedOutputReset};
use num_bigint::BigUint;
use rand_core::CryptoRngCore;
use zeroize::{ZeroizeOnDrop, Zeroizing};
use zeroize::Zeroizing;
use crate::algorithms::oaep::*;
use crate::algorithms::pad::{uint_to_be_pad, uint_to_zeroizing_be_pad};
use crate::algorithms::rsa::{rsa_decrypt_and_check, rsa_encrypt};
use crate::dummy_rng::DummyRng;
use crate::errors::{Error, Result};
use crate::key::{self, RsaPrivateKey, RsaPublicKey};
use crate::traits::PaddingScheme;
use crate::traits::{Decryptor, RandomizedDecryptor, RandomizedEncryptor};
use crate::traits::PublicKeyParts;
use crate::traits::{PaddingScheme, PublicKeyParts};
/// Encryption and Decryption using [OAEP padding](https://datatracker.ietf.org/doc/html/rfc8017#section-7.1).
///
@ -282,149 +283,12 @@ fn decrypt_digest<R: CryptoRngCore + ?Sized, D: Digest, MGD: Digest + FixedOutpu
oaep_decrypt_digest::<D, MGD>(&mut em, label, priv_key.size())
}
/// Encryption key for PKCS#1 v1.5 encryption as described in [RFC8017 § 7.1].
///
/// [RFC8017 § 7.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
#[derive(Debug, Clone)]
pub struct EncryptingKey<D, MGD = D>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
inner: RsaPublicKey,
label: Option<String>,
phantom: PhantomData<D>,
mg_phantom: PhantomData<MGD>,
}
impl<D, MGD> EncryptingKey<D, MGD>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
/// Create a new verifying key from an RSA public key.
pub fn new(key: RsaPublicKey) -> Self {
Self {
inner: key,
label: None,
phantom: Default::default(),
mg_phantom: Default::default(),
}
}
/// Create a new verifying key from an RSA public key using provided label
pub fn new_with_label<S: AsRef<str>>(key: RsaPublicKey, label: S) -> Self {
Self {
inner: key,
label: Some(label.as_ref().to_string()),
phantom: Default::default(),
mg_phantom: Default::default(),
}
}
}
impl<D, MGD> RandomizedEncryptor for EncryptingKey<D, MGD>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
fn encrypt_with_rng<R: CryptoRngCore + ?Sized>(
&self,
rng: &mut R,
msg: &[u8],
) -> Result<Vec<u8>> {
encrypt_digest::<_, D, MGD>(rng, &self.inner, msg, self.label.as_ref().cloned())
}
}
/// Decryption key for PKCS#1 v1.5 decryption as described in [RFC8017 § 7.1].
///
/// [RFC8017 § 7.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
#[derive(Debug, Clone)]
pub struct DecryptingKey<D, MGD = D>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
inner: RsaPrivateKey,
label: Option<String>,
phantom: PhantomData<D>,
mg_phantom: PhantomData<MGD>,
}
impl<D, MGD> DecryptingKey<D, MGD>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
/// Create a new verifying key from an RSA public key.
pub fn new(key: RsaPrivateKey) -> Self {
Self {
inner: key,
label: None,
phantom: Default::default(),
mg_phantom: Default::default(),
}
}
/// Create a new verifying key from an RSA public key using provided label
pub fn new_with_label<S: AsRef<str>>(key: RsaPrivateKey, label: S) -> Self {
Self {
inner: key,
label: Some(label.as_ref().to_string()),
phantom: Default::default(),
mg_phantom: Default::default(),
}
}
}
impl<D, MGD> Decryptor for DecryptingKey<D, MGD>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
decrypt_digest::<DummyRng, D, MGD>(
None,
&self.inner,
ciphertext,
self.label.as_ref().cloned(),
)
}
}
impl<D, MGD> RandomizedDecryptor for DecryptingKey<D, MGD>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
fn decrypt_with_rng<R: CryptoRngCore + ?Sized>(
&self,
rng: &mut R,
ciphertext: &[u8],
) -> Result<Vec<u8>> {
decrypt_digest::<_, D, MGD>(
Some(rng),
&self.inner,
ciphertext,
self.label.as_ref().cloned(),
)
}
}
impl<D, MGD> ZeroizeOnDrop for DecryptingKey<D, MGD>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
}
#[cfg(test)]
mod tests {
use crate::key::{RsaPrivateKey, RsaPublicKey};
use crate::oaep::{DecryptingKey, EncryptingKey, Oaep};
use crate::traits::{Decryptor, RandomizedDecryptor, RandomizedEncryptor};
use crate::traits::PublicKeyParts;
use crate::traits::{Decryptor, RandomizedDecryptor, RandomizedEncryptor};
use alloc::string::String;
use digest::{Digest, DynDigest, FixedOutputReset};

View File

@ -0,0 +1,96 @@
use super::decrypt_digest;
use crate::{
dummy_rng::DummyRng,
traits::{Decryptor, RandomizedDecryptor},
Result, RsaPrivateKey,
};
use alloc::{
string::{String, ToString},
vec::Vec,
};
use core::marker::PhantomData;
use digest::{Digest, FixedOutputReset};
use rand_core::CryptoRngCore;
use zeroize::ZeroizeOnDrop;
/// Decryption key for PKCS#1 v1.5 decryption as described in [RFC8017 § 7.1].
///
/// [RFC8017 § 7.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
#[derive(Debug, Clone)]
pub struct DecryptingKey<D, MGD = D>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
inner: RsaPrivateKey,
label: Option<String>,
phantom: PhantomData<D>,
mg_phantom: PhantomData<MGD>,
}
impl<D, MGD> DecryptingKey<D, MGD>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
/// Create a new verifying key from an RSA public key.
pub fn new(key: RsaPrivateKey) -> Self {
Self {
inner: key,
label: None,
phantom: Default::default(),
mg_phantom: Default::default(),
}
}
/// Create a new verifying key from an RSA public key using provided label
pub fn new_with_label<S: AsRef<str>>(key: RsaPrivateKey, label: S) -> Self {
Self {
inner: key,
label: Some(label.as_ref().to_string()),
phantom: Default::default(),
mg_phantom: Default::default(),
}
}
}
impl<D, MGD> Decryptor for DecryptingKey<D, MGD>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
decrypt_digest::<DummyRng, D, MGD>(
None,
&self.inner,
ciphertext,
self.label.as_ref().cloned(),
)
}
}
impl<D, MGD> RandomizedDecryptor for DecryptingKey<D, MGD>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
fn decrypt_with_rng<R: CryptoRngCore + ?Sized>(
&self,
rng: &mut R,
ciphertext: &[u8],
) -> Result<Vec<u8>> {
decrypt_digest::<_, D, MGD>(
Some(rng),
&self.inner,
ciphertext,
self.label.as_ref().cloned(),
)
}
}
impl<D, MGD> ZeroizeOnDrop for DecryptingKey<D, MGD>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
}

View File

@ -0,0 +1,64 @@
use super::encrypt_digest;
use crate::{traits::RandomizedEncryptor, Result, RsaPublicKey};
use alloc::{
string::{String, ToString},
vec::Vec,
};
use core::marker::PhantomData;
use digest::{Digest, FixedOutputReset};
use rand_core::CryptoRngCore;
/// Encryption key for PKCS#1 v1.5 encryption as described in [RFC8017 § 7.1].
///
/// [RFC8017 § 7.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
#[derive(Debug, Clone)]
pub struct EncryptingKey<D, MGD = D>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
inner: RsaPublicKey,
label: Option<String>,
phantom: PhantomData<D>,
mg_phantom: PhantomData<MGD>,
}
impl<D, MGD> EncryptingKey<D, MGD>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
/// Create a new verifying key from an RSA public key.
pub fn new(key: RsaPublicKey) -> Self {
Self {
inner: key,
label: None,
phantom: Default::default(),
mg_phantom: Default::default(),
}
}
/// Create a new verifying key from an RSA public key using provided label
pub fn new_with_label<S: AsRef<str>>(key: RsaPublicKey, label: S) -> Self {
Self {
inner: key,
label: Some(label.as_ref().to_string()),
phantom: Default::default(),
mg_phantom: Default::default(),
}
}
}
impl<D, MGD> RandomizedEncryptor for EncryptingKey<D, MGD>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
fn encrypt_with_rng<R: CryptoRngCore + ?Sized>(
&self,
rng: &mut R,
msg: &[u8],
) -> Result<Vec<u8>> {
encrypt_digest::<_, D, MGD>(rng, &self.inner, msg, self.label.as_ref().cloned())
}
}

View File

@ -6,36 +6,31 @@
//!
//! [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
use alloc::{boxed::Box, string::ToString, vec::Vec};
use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex};
use core::marker::PhantomData;
mod decrypting_key;
mod encrypting_key;
mod signature;
mod signing_key;
mod verifying_key;
pub use self::{
decrypting_key::DecryptingKey, encrypting_key::EncryptingKey, signature::Signature,
signing_key::SigningKey, verifying_key::VerifyingKey,
};
use alloc::{boxed::Box, vec::Vec};
use core::fmt::Debug;
use digest::Digest;
use num_bigint::BigUint;
use pkcs8::{
spki::{
der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier,
SignatureAlgorithmIdentifier,
},
AssociatedOid, Document, EncodePrivateKey, EncodePublicKey, SecretDocument,
};
use pkcs8::AssociatedOid;
use rand_core::CryptoRngCore;
use signature::{
hazmat::{PrehashSigner, PrehashVerifier},
DigestSigner, DigestVerifier, Keypair, RandomizedDigestSigner, RandomizedSigner,
SignatureEncoding, Signer, Verifier,
};
use zeroize::{ZeroizeOnDrop, Zeroizing};
use zeroize::Zeroizing;
use crate::algorithms::pad::{uint_to_be_pad, uint_to_zeroizing_be_pad};
use crate::algorithms::pkcs1v15::*;
use crate::algorithms::rsa::{rsa_decrypt_and_check, rsa_encrypt};
use crate::dummy_rng::DummyRng;
use crate::errors::{Error, Result};
use crate::key;
use crate::traits::{PaddingScheme, SignatureScheme};
use crate::traits::PublicKeyParts;
use crate::traits::{Decryptor, EncryptingKeypair, RandomizedDecryptor, RandomizedEncryptor};
use crate::{RsaPrivateKey, RsaPublicKey};
use crate::key::{self, RsaPrivateKey, RsaPublicKey};
use crate::traits::{PaddingScheme, PublicKeyParts, SignatureScheme};
/// Encryption using PKCS#1 v1.5 padding.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
@ -138,62 +133,6 @@ impl SignatureScheme for Pkcs1v15Sign {
}
}
/// PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
///
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
#[derive(Clone, PartialEq, Eq)]
pub struct Signature {
inner: BigUint,
len: usize,
}
impl SignatureEncoding for Signature {
type Repr = Box<[u8]>;
}
impl TryFrom<&[u8]> for Signature {
type Error = signature::Error;
fn try_from(bytes: &[u8]) -> signature::Result<Self> {
Ok(Self {
inner: BigUint::from_bytes_be(bytes),
len: bytes.len(),
})
}
}
impl From<Signature> for Box<[u8]> {
fn from(signature: Signature) -> Box<[u8]> {
signature.inner.to_bytes_be().into_boxed_slice()
}
}
impl Debug for Signature {
fn fmt(&self, fmt: &mut Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
fmt.debug_tuple("Signature")
.field(&self.to_string())
.finish()
}
}
impl LowerHex for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:x}", &self.inner)
}
}
impl UpperHex for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:X}", &self.inner)
}
}
impl Display for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:X}", self)
}
}
/// Encrypts the given message with RSA and the padding
/// scheme from PKCS#1 v1.5. The message must be no longer than the
/// length of the public modulus minus 11 bytes.
@ -279,462 +218,6 @@ fn verify(
pkcs1v15_sign_unpad(prefix, hashed, &em, pub_key.size())
}
/// Signing key for PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
///
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
#[derive(Debug, Clone)]
pub struct SigningKey<D>
where
D: Digest,
{
inner: RsaPrivateKey,
prefix: Vec<u8>,
phantom: PhantomData<D>,
}
impl<D> SigningKey<D>
where
D: Digest,
{
/// Create a new signing key from the give RSA private key with an empty prefix.
///
/// ## Note: unprefixed signatures are uncommon
///
/// In most cases you'll want to use [`SigningKey::new`].
pub fn new_unprefixed(key: RsaPrivateKey) -> Self {
Self {
inner: key,
prefix: Vec::new(),
phantom: Default::default(),
}
}
/// Generate a new signing key with an empty prefix.
pub fn random_unprefixed<R: CryptoRngCore + ?Sized>(
rng: &mut R,
bit_size: usize,
) -> Result<Self> {
Ok(Self {
inner: RsaPrivateKey::new(rng, bit_size)?,
prefix: Vec::new(),
phantom: Default::default(),
})
}
}
impl<D> AssociatedAlgorithmIdentifier for SigningKey<D>
where
D: Digest,
{
type Params = AnyRef<'static>;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
}
impl<D> SignatureAlgorithmIdentifier for SigningKey<D>
where
D: Digest + oid::RsaSignatureAssociatedOid,
{
type Params = AnyRef<'static>;
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> =
AlgorithmIdentifierRef {
oid: D::OID,
parameters: Some(AnyRef::NULL),
};
}
impl<D> From<RsaPrivateKey> for SigningKey<D>
where
D: Digest,
{
fn from(key: RsaPrivateKey) -> Self {
Self::new_unprefixed(key)
}
}
impl<D> From<SigningKey<D>> for RsaPrivateKey
where
D: Digest,
{
fn from(key: SigningKey<D>) -> Self {
key.inner
}
}
impl<D> SigningKey<D>
where
D: Digest + AssociatedOid,
{
/// Create a new signing key with a prefix for the digest `D`.
pub fn new(key: RsaPrivateKey) -> Self {
Self {
inner: key,
prefix: pkcs1v15_generate_prefix::<D>(),
phantom: Default::default(),
}
}
/// Generate a new signing key with a prefix for the digest `D`.
pub fn random<R: CryptoRngCore + ?Sized>(rng: &mut R, bit_size: usize) -> Result<Self> {
Ok(Self {
inner: RsaPrivateKey::new(rng, bit_size)?,
prefix: pkcs1v15_generate_prefix::<D>(),
phantom: Default::default(),
})
}
/// Create a new signing key with a prefix for the digest `D`.
#[deprecated(since = "0.9.0", note = "use SigningKey::new instead")]
pub fn new_with_prefix(key: RsaPrivateKey) -> Self {
Self::new(key)
}
/// Generate a new signing key with a prefix for the digest `D`.
#[deprecated(since = "0.9.0", note = "use SigningKey::random instead")]
pub fn random_with_prefix<R: CryptoRngCore + ?Sized>(
rng: &mut R,
bit_size: usize,
) -> Result<Self> {
Self::random(rng, bit_size)
}
}
impl<D> AsRef<RsaPrivateKey> for SigningKey<D>
where
D: Digest,
{
fn as_ref(&self) -> &RsaPrivateKey {
&self.inner
}
}
impl<D> EncodePrivateKey for SigningKey<D>
where
D: Digest,
{
fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
self.inner.to_pkcs8_der()
}
}
impl<D> ZeroizeOnDrop for SigningKey<D> where D: Digest {}
impl<D> Signer<Signature> for SigningKey<D>
where
D: Digest,
{
fn try_sign(&self, msg: &[u8]) -> signature::Result<Signature> {
sign::<DummyRng>(None, &self.inner, &self.prefix, &D::digest(msg))?
.as_slice()
.try_into()
}
}
impl<D> RandomizedSigner<Signature> for SigningKey<D>
where
D: Digest,
{
fn try_sign_with_rng(
&self,
rng: &mut impl CryptoRngCore,
msg: &[u8],
) -> signature::Result<Signature> {
sign(Some(rng), &self.inner, &self.prefix, &D::digest(msg))?
.as_slice()
.try_into()
}
}
impl<D> DigestSigner<D, Signature> for SigningKey<D>
where
D: Digest,
{
fn try_sign_digest(&self, digest: D) -> signature::Result<Signature> {
sign::<DummyRng>(None, &self.inner, &self.prefix, &digest.finalize())?
.as_slice()
.try_into()
}
}
impl<D> RandomizedDigestSigner<D, Signature> for SigningKey<D>
where
D: Digest,
{
fn try_sign_digest_with_rng(
&self,
rng: &mut impl CryptoRngCore,
digest: D,
) -> signature::Result<Signature> {
sign(Some(rng), &self.inner, &self.prefix, &digest.finalize())?
.as_slice()
.try_into()
}
}
impl<D> PrehashSigner<Signature> for SigningKey<D>
where
D: Digest,
{
fn sign_prehash(&self, prehash: &[u8]) -> signature::Result<Signature> {
sign::<DummyRng>(None, &self.inner, &self.prefix, prehash)?
.as_slice()
.try_into()
}
}
/// Verifying key for PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
///
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
#[derive(Debug)]
pub struct VerifyingKey<D>
where
D: Digest,
{
inner: RsaPublicKey,
prefix: Vec<u8>,
phantom: PhantomData<D>,
}
/* Implemented manually so we don't have to bind D with Clone */
impl<D> Clone for VerifyingKey<D>
where
D: Digest,
{
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
prefix: self.prefix.clone(),
phantom: Default::default(),
}
}
}
impl<D> VerifyingKey<D>
where
D: Digest,
{
/// Create a new verifying key from an RSA public key with an empty prefix.
///
/// ## Note: unprefixed signatures are uncommon
///
/// In most cases you'll want to use [`VerifyingKey::new`] instead.
pub fn new_unprefixed(key: RsaPublicKey) -> Self {
Self {
inner: key,
prefix: Vec::new(),
phantom: Default::default(),
}
}
}
impl<D> AssociatedAlgorithmIdentifier for VerifyingKey<D>
where
D: Digest,
{
type Params = AnyRef<'static>;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
}
impl<D> SignatureAlgorithmIdentifier for VerifyingKey<D>
where
D: Digest + oid::RsaSignatureAssociatedOid,
{
type Params = AnyRef<'static>;
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> =
AlgorithmIdentifierRef {
oid: D::OID,
parameters: Some(AnyRef::NULL),
};
}
impl<D> From<RsaPublicKey> for VerifyingKey<D>
where
D: Digest,
{
fn from(key: RsaPublicKey) -> Self {
Self::new_unprefixed(key)
}
}
impl<D> From<VerifyingKey<D>> for RsaPublicKey
where
D: Digest,
{
fn from(key: VerifyingKey<D>) -> Self {
key.inner
}
}
impl<D> VerifyingKey<D>
where
D: Digest + AssociatedOid,
{
/// Create a new verifying key with a prefix for the digest `D`.
pub fn new(key: RsaPublicKey) -> Self {
Self {
inner: key,
prefix: pkcs1v15_generate_prefix::<D>(),
phantom: Default::default(),
}
}
/// Create a new verifying key with a prefix for the digest `D`.
#[deprecated(since = "0.9.0", note = "use VerifyingKey::new instead")]
pub fn new_with_prefix(key: RsaPublicKey) -> Self {
Self::new(key)
}
}
impl<D> AsRef<RsaPublicKey> for VerifyingKey<D>
where
D: Digest,
{
fn as_ref(&self) -> &RsaPublicKey {
&self.inner
}
}
impl<D> Keypair for SigningKey<D>
where
D: Digest,
{
type VerifyingKey = VerifyingKey<D>;
fn verifying_key(&self) -> Self::VerifyingKey {
VerifyingKey {
inner: self.inner.to_public_key(),
prefix: self.prefix.clone(),
phantom: Default::default(),
}
}
}
impl<D> Verifier<Signature> for VerifyingKey<D>
where
D: Digest,
{
fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> {
verify(
&self.inner,
&self.prefix.clone(),
&D::digest(msg),
&signature.inner,
signature.len,
)
.map_err(|e| e.into())
}
}
impl<D> DigestVerifier<D, Signature> for VerifyingKey<D>
where
D: Digest,
{
fn verify_digest(&self, digest: D, signature: &Signature) -> signature::Result<()> {
verify(
&self.inner,
&self.prefix,
&digest.finalize(),
&signature.inner,
signature.len,
)
.map_err(|e| e.into())
}
}
impl<D> PrehashVerifier<Signature> for VerifyingKey<D>
where
D: Digest,
{
fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> signature::Result<()> {
verify(
&self.inner,
&self.prefix,
prehash,
&signature.inner,
signature.len,
)
.map_err(|e| e.into())
}
}
impl<D> EncodePublicKey for VerifyingKey<D>
where
D: Digest,
{
fn to_public_key_der(&self) -> pkcs8::spki::Result<Document> {
self.inner.to_public_key_der()
}
}
/// Encryption key for PKCS#1 v1.5 encryption as described in [RFC8017 § 7.2].
///
/// [RFC8017 § 7.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.2
#[derive(Debug, Clone)]
pub struct EncryptingKey {
inner: RsaPublicKey,
}
impl EncryptingKey {
/// Create a new verifying key from an RSA public key.
pub fn new(key: RsaPublicKey) -> Self {
Self { inner: key }
}
}
impl RandomizedEncryptor for EncryptingKey {
fn encrypt_with_rng<R: CryptoRngCore + ?Sized>(
&self,
rng: &mut R,
msg: &[u8],
) -> Result<Vec<u8>> {
encrypt(rng, &self.inner, msg)
}
}
/// Decryption key for PKCS#1 v1.5 decryption as described in [RFC8017 § 7.2].
///
/// [RFC8017 § 7.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.2
#[derive(Debug, Clone)]
pub struct DecryptingKey {
inner: RsaPrivateKey,
}
impl DecryptingKey {
/// Create a new verifying key from an RSA public key.
pub fn new(key: RsaPrivateKey) -> Self {
Self { inner: key }
}
}
impl Decryptor for DecryptingKey {
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
decrypt::<DummyRng>(None, &self.inner, ciphertext)
}
}
impl RandomizedDecryptor for DecryptingKey {
fn decrypt_with_rng<R: CryptoRngCore + ?Sized>(
&self,
rng: &mut R,
ciphertext: &[u8],
) -> Result<Vec<u8>> {
decrypt(Some(rng), &self.inner, ciphertext)
}
}
impl EncryptingKeypair for DecryptingKey {
type EncryptingKey = EncryptingKey;
fn encrypting_key(&self) -> EncryptingKey {
EncryptingKey {
inner: self.inner.clone().into(),
}
}
}
impl ZeroizeOnDrop for DecryptingKey {}
mod oid {
use const_oid::ObjectIdentifier;
@ -778,6 +261,11 @@ mod oid {
#[cfg(test)]
mod tests {
use super::*;
use ::signature::{
hazmat::{PrehashSigner, PrehashVerifier},
DigestSigner, DigestVerifier, Keypair, RandomizedDigestSigner, RandomizedSigner,
SignatureEncoding, Signer, Verifier,
};
use base64ct::{Base64, Encoding};
use hex_literal::hex;
use num_bigint::BigUint;
@ -790,9 +278,11 @@ mod tests {
use sha1::{Digest, Sha1};
use sha2::Sha256;
use sha3::Sha3_256;
use signature::{RandomizedSigner, Signer, Verifier};
use crate::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
use crate::traits::{
Decryptor, EncryptingKeypair, PublicKeyParts, RandomizedDecryptor, RandomizedEncryptor,
};
use crate::{RsaPrivateKey, RsaPublicKey};
fn get_private_key() -> RsaPrivateKey {
// In order to generate new test vectors you'll need the PEM form of this key:

View File

@ -0,0 +1,51 @@
use super::{decrypt, EncryptingKey};
use crate::{
dummy_rng::DummyRng,
traits::{Decryptor, EncryptingKeypair, RandomizedDecryptor},
Result, RsaPrivateKey,
};
use alloc::vec::Vec;
use rand_core::CryptoRngCore;
use zeroize::ZeroizeOnDrop;
/// Decryption key for PKCS#1 v1.5 decryption as described in [RFC8017 § 7.2].
///
/// [RFC8017 § 7.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.2
#[derive(Debug, Clone)]
pub struct DecryptingKey {
inner: RsaPrivateKey,
}
impl DecryptingKey {
/// Create a new verifying key from an RSA public key.
pub fn new(key: RsaPrivateKey) -> Self {
Self { inner: key }
}
}
impl Decryptor for DecryptingKey {
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
decrypt::<DummyRng>(None, &self.inner, ciphertext)
}
}
impl RandomizedDecryptor for DecryptingKey {
fn decrypt_with_rng<R: CryptoRngCore + ?Sized>(
&self,
rng: &mut R,
ciphertext: &[u8],
) -> Result<Vec<u8>> {
decrypt(Some(rng), &self.inner, ciphertext)
}
}
impl EncryptingKeypair for DecryptingKey {
type EncryptingKey = EncryptingKey;
fn encrypting_key(&self) -> EncryptingKey {
EncryptingKey {
inner: self.inner.clone().into(),
}
}
}
impl ZeroizeOnDrop for DecryptingKey {}

View File

@ -0,0 +1,29 @@
use super::encrypt;
use crate::{traits::RandomizedEncryptor, Result, RsaPublicKey};
use alloc::vec::Vec;
use rand_core::CryptoRngCore;
/// Encryption key for PKCS#1 v1.5 encryption as described in [RFC8017 § 7.2].
///
/// [RFC8017 § 7.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.2
#[derive(Debug, Clone)]
pub struct EncryptingKey {
pub(super) inner: RsaPublicKey,
}
impl EncryptingKey {
/// Create a new verifying key from an RSA public key.
pub fn new(key: RsaPublicKey) -> Self {
Self { inner: key }
}
}
impl RandomizedEncryptor for EncryptingKey {
fn encrypt_with_rng<R: CryptoRngCore + ?Sized>(
&self,
rng: &mut R,
msg: &[u8],
) -> Result<Vec<u8>> {
encrypt(rng, &self.inner, msg)
}
}

65
src/pkcs1v15/signature.rs Normal file
View File

@ -0,0 +1,65 @@
pub use ::signature::{
hazmat::{PrehashSigner, PrehashVerifier},
DigestSigner, DigestVerifier, Error, Keypair, RandomizedDigestSigner, RandomizedSigner, Result,
SignatureEncoding, Signer, Verifier,
};
use alloc::{boxed::Box, string::ToString};
use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex};
use num_bigint::BigUint;
/// PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
///
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
#[derive(Clone, PartialEq, Eq)]
pub struct Signature {
pub(super) inner: BigUint,
pub(super) len: usize,
}
impl SignatureEncoding for Signature {
type Repr = Box<[u8]>;
}
impl TryFrom<&[u8]> for Signature {
type Error = signature::Error;
fn try_from(bytes: &[u8]) -> signature::Result<Self> {
Ok(Self {
inner: BigUint::from_bytes_be(bytes),
len: bytes.len(),
})
}
}
impl From<Signature> for Box<[u8]> {
fn from(signature: Signature) -> Box<[u8]> {
signature.inner.to_bytes_be().into_boxed_slice()
}
}
impl Debug for Signature {
fn fmt(&self, fmt: &mut Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
fmt.debug_tuple("Signature")
.field(&self.to_string())
.finish()
}
}
impl LowerHex for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:x}", &self.inner)
}
}
impl UpperHex for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:X}", &self.inner)
}
}
impl Display for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:X}", self)
}
}

244
src/pkcs1v15/signing_key.rs Normal file
View File

@ -0,0 +1,244 @@
use super::{oid, pkcs1v15_generate_prefix, sign, Signature, VerifyingKey};
use crate::{dummy_rng::DummyRng, Result, RsaPrivateKey};
use alloc::vec::Vec;
use core::marker::PhantomData;
use digest::Digest;
use pkcs8::{
spki::{
der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier,
SignatureAlgorithmIdentifier,
},
AssociatedOid, EncodePrivateKey, SecretDocument,
};
use rand_core::CryptoRngCore;
use signature::{
hazmat::PrehashSigner, DigestSigner, Keypair, RandomizedDigestSigner, RandomizedSigner, Signer,
};
use zeroize::ZeroizeOnDrop;
/// Signing key for PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
///
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
#[derive(Debug, Clone)]
pub struct SigningKey<D>
where
D: Digest,
{
inner: RsaPrivateKey,
prefix: Vec<u8>,
phantom: PhantomData<D>,
}
impl<D> SigningKey<D>
where
D: Digest,
{
/// Create a new signing key from the give RSA private key with an empty prefix.
///
/// ## Note: unprefixed signatures are uncommon
///
/// In most cases you'll want to use [`SigningKey::new`].
pub fn new_unprefixed(key: RsaPrivateKey) -> Self {
Self {
inner: key,
prefix: Vec::new(),
phantom: Default::default(),
}
}
/// Generate a new signing key with an empty prefix.
pub fn random_unprefixed<R: CryptoRngCore + ?Sized>(
rng: &mut R,
bit_size: usize,
) -> Result<Self> {
Ok(Self {
inner: RsaPrivateKey::new(rng, bit_size)?,
prefix: Vec::new(),
phantom: Default::default(),
})
}
}
impl<D> SigningKey<D>
where
D: Digest + AssociatedOid,
{
/// Create a new signing key with a prefix for the digest `D`.
pub fn new(key: RsaPrivateKey) -> Self {
Self {
inner: key,
prefix: pkcs1v15_generate_prefix::<D>(),
phantom: Default::default(),
}
}
/// Generate a new signing key with a prefix for the digest `D`.
pub fn random<R: CryptoRngCore + ?Sized>(rng: &mut R, bit_size: usize) -> Result<Self> {
Ok(Self {
inner: RsaPrivateKey::new(rng, bit_size)?,
prefix: pkcs1v15_generate_prefix::<D>(),
phantom: Default::default(),
})
}
/// Create a new signing key with a prefix for the digest `D`.
#[deprecated(since = "0.9.0", note = "use SigningKey::new instead")]
pub fn new_with_prefix(key: RsaPrivateKey) -> Self {
Self::new(key)
}
/// Generate a new signing key with a prefix for the digest `D`.
#[deprecated(since = "0.9.0", note = "use SigningKey::random instead")]
pub fn random_with_prefix<R: CryptoRngCore + ?Sized>(
rng: &mut R,
bit_size: usize,
) -> Result<Self> {
Self::random(rng, bit_size)
}
}
//
// `*Signer` trait impls
//
impl<D> DigestSigner<D, Signature> for SigningKey<D>
where
D: Digest,
{
fn try_sign_digest(&self, digest: D) -> signature::Result<Signature> {
sign::<DummyRng>(None, &self.inner, &self.prefix, &digest.finalize())?
.as_slice()
.try_into()
}
}
impl<D> PrehashSigner<Signature> for SigningKey<D>
where
D: Digest,
{
fn sign_prehash(&self, prehash: &[u8]) -> signature::Result<Signature> {
sign::<DummyRng>(None, &self.inner, &self.prefix, prehash)?
.as_slice()
.try_into()
}
}
impl<D> RandomizedDigestSigner<D, Signature> for SigningKey<D>
where
D: Digest,
{
fn try_sign_digest_with_rng(
&self,
rng: &mut impl CryptoRngCore,
digest: D,
) -> signature::Result<Signature> {
sign(Some(rng), &self.inner, &self.prefix, &digest.finalize())?
.as_slice()
.try_into()
}
}
impl<D> RandomizedSigner<Signature> for SigningKey<D>
where
D: Digest,
{
fn try_sign_with_rng(
&self,
rng: &mut impl CryptoRngCore,
msg: &[u8],
) -> signature::Result<Signature> {
sign(Some(rng), &self.inner, &self.prefix, &D::digest(msg))?
.as_slice()
.try_into()
}
}
impl<D> Signer<Signature> for SigningKey<D>
where
D: Digest,
{
fn try_sign(&self, msg: &[u8]) -> signature::Result<Signature> {
sign::<DummyRng>(None, &self.inner, &self.prefix, &D::digest(msg))?
.as_slice()
.try_into()
}
}
//
// Other trait impls
//
impl<D> AsRef<RsaPrivateKey> for SigningKey<D>
where
D: Digest,
{
fn as_ref(&self) -> &RsaPrivateKey {
&self.inner
}
}
impl<D> AssociatedAlgorithmIdentifier for SigningKey<D>
where
D: Digest,
{
type Params = AnyRef<'static>;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
}
impl<D> EncodePrivateKey for SigningKey<D>
where
D: Digest,
{
fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
self.inner.to_pkcs8_der()
}
}
impl<D> From<RsaPrivateKey> for SigningKey<D>
where
D: Digest,
{
fn from(key: RsaPrivateKey) -> Self {
Self::new_unprefixed(key)
}
}
impl<D> From<SigningKey<D>> for RsaPrivateKey
where
D: Digest,
{
fn from(key: SigningKey<D>) -> Self {
key.inner
}
}
impl<D> Keypair for SigningKey<D>
where
D: Digest,
{
type VerifyingKey = VerifyingKey<D>;
fn verifying_key(&self) -> Self::VerifyingKey {
VerifyingKey {
inner: self.inner.to_public_key(),
prefix: self.prefix.clone(),
phantom: Default::default(),
}
}
}
impl<D> SignatureAlgorithmIdentifier for SigningKey<D>
where
D: Digest + oid::RsaSignatureAssociatedOid,
{
type Params = AnyRef<'static>;
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> =
AlgorithmIdentifierRef {
oid: D::OID,
parameters: Some(AnyRef::NULL),
};
}
impl<D> ZeroizeOnDrop for SigningKey<D> where D: Digest {}

View File

@ -0,0 +1,192 @@
use super::{oid, pkcs1v15_generate_prefix, verify, Signature};
use crate::RsaPublicKey;
use alloc::vec::Vec;
use core::marker::PhantomData;
use digest::Digest;
use pkcs8::{
spki::{
der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier,
SignatureAlgorithmIdentifier,
},
AssociatedOid, Document, EncodePublicKey,
};
use signature::{hazmat::PrehashVerifier, DigestVerifier, Verifier};
/// Verifying key for PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
///
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
#[derive(Debug)]
pub struct VerifyingKey<D>
where
D: Digest,
{
pub(super) inner: RsaPublicKey,
pub(super) prefix: Vec<u8>,
pub(super) phantom: PhantomData<D>,
}
impl<D> VerifyingKey<D>
where
D: Digest,
{
/// Create a new verifying key from an RSA public key with an empty prefix.
///
/// ## Note: unprefixed signatures are uncommon
///
/// In most cases you'll want to use [`VerifyingKey::new`] instead.
pub fn new_unprefixed(key: RsaPublicKey) -> Self {
Self {
inner: key,
prefix: Vec::new(),
phantom: Default::default(),
}
}
}
impl<D> VerifyingKey<D>
where
D: Digest + AssociatedOid,
{
/// Create a new verifying key with a prefix for the digest `D`.
pub fn new(key: RsaPublicKey) -> Self {
Self {
inner: key,
prefix: pkcs1v15_generate_prefix::<D>(),
phantom: Default::default(),
}
}
/// Create a new verifying key with a prefix for the digest `D`.
#[deprecated(since = "0.9.0", note = "use VerifyingKey::new instead")]
pub fn new_with_prefix(key: RsaPublicKey) -> Self {
Self::new(key)
}
}
//
// `*Verifier` trait impls
//
impl<D> DigestVerifier<D, Signature> for VerifyingKey<D>
where
D: Digest,
{
fn verify_digest(&self, digest: D, signature: &Signature) -> signature::Result<()> {
verify(
&self.inner,
&self.prefix,
&digest.finalize(),
&signature.inner,
signature.len,
)
.map_err(|e| e.into())
}
}
impl<D> PrehashVerifier<Signature> for VerifyingKey<D>
where
D: Digest,
{
fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> signature::Result<()> {
verify(
&self.inner,
&self.prefix,
prehash,
&signature.inner,
signature.len,
)
.map_err(|e| e.into())
}
}
impl<D> Verifier<Signature> for VerifyingKey<D>
where
D: Digest,
{
fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> {
verify(
&self.inner,
&self.prefix.clone(),
&D::digest(msg),
&signature.inner,
signature.len,
)
.map_err(|e| e.into())
}
}
//
// Other trait impls
//
impl<D> AsRef<RsaPublicKey> for VerifyingKey<D>
where
D: Digest,
{
fn as_ref(&self) -> &RsaPublicKey {
&self.inner
}
}
impl<D> AssociatedAlgorithmIdentifier for VerifyingKey<D>
where
D: Digest,
{
type Params = AnyRef<'static>;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
}
// Implemented manually so we don't have to bind D with Clone
impl<D> Clone for VerifyingKey<D>
where
D: Digest,
{
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
prefix: self.prefix.clone(),
phantom: Default::default(),
}
}
}
impl<D> EncodePublicKey for VerifyingKey<D>
where
D: Digest,
{
fn to_public_key_der(&self) -> pkcs8::spki::Result<Document> {
self.inner.to_public_key_der()
}
}
impl<D> From<RsaPublicKey> for VerifyingKey<D>
where
D: Digest,
{
fn from(key: RsaPublicKey) -> Self {
Self::new_unprefixed(key)
}
}
impl<D> From<VerifyingKey<D>> for RsaPublicKey
where
D: Digest,
{
fn from(key: VerifyingKey<D>) -> Self {
key.inner
}
}
impl<D> SignatureAlgorithmIdentifier for VerifyingKey<D>
where
D: Digest + oid::RsaSignatureAssociatedOid,
{
type Params = AnyRef<'static>;
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> =
AlgorithmIdentifierRef {
oid: D::OID,
parameters: Some(AnyRef::NULL),
};
}

View File

@ -9,40 +9,34 @@
//! [Probabilistic Signature Scheme]: https://en.wikipedia.org/wiki/Probabilistic_signature_scheme
//! [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
use alloc::{boxed::Box, string::ToString, vec::Vec};
use core::fmt::{self, Debug, Display, Formatter, LowerHex, UpperHex};
use core::marker::PhantomData;
mod blinded_signing_key;
mod signature;
mod signing_key;
mod verifying_key;
pub use self::{
blinded_signing_key::BlindedSigningKey, signature::Signature, signing_key::SigningKey,
verifying_key::VerifyingKey,
};
use alloc::{boxed::Box, vec::Vec};
use core::fmt::{self, Debug};
use const_oid::{AssociatedOid, ObjectIdentifier};
use digest::{Digest, DynDigest, FixedOutputReset};
use num_bigint::BigUint;
use pkcs1::RsaPssParams;
use pkcs8::{
spki::{
der::{Any, AnyRef},
AlgorithmIdentifierOwned, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier,
DynSignatureAlgorithmIdentifier,
},
Document, EncodePrivateKey, EncodePublicKey, SecretDocument,
};
use pkcs8::spki::{der::Any, AlgorithmIdentifierOwned};
use rand_core::CryptoRngCore;
use signature::{
hazmat::{PrehashVerifier, RandomizedPrehashSigner},
DigestVerifier, Keypair, RandomizedDigestSigner, RandomizedSigner, SignatureEncoding, Verifier,
};
use zeroize::ZeroizeOnDrop;
use crate::algorithms::pad::{uint_to_be_pad, uint_to_zeroizing_be_pad};
use crate::algorithms::pss::*;
use crate::algorithms::rsa::{rsa_decrypt_and_check, rsa_encrypt};
use crate::errors::{Error, Result};
use crate::traits::SignatureScheme;
use crate::traits::PublicKeyParts;
use crate::traits::SignatureScheme;
use crate::{RsaPrivateKey, RsaPublicKey};
#[cfg(feature = "getrandom")]
use {rand_core::OsRng, signature::Signer};
/// Digital signatures using PSS padding.
pub struct Pss {
/// Create blinded signatures.
@ -129,62 +123,6 @@ impl Debug for Pss {
}
}
/// RSASSA-PSS signatures as described in [RFC8017 § 8.1].
///
/// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
#[derive(Clone, PartialEq, Eq)]
pub struct Signature {
inner: BigUint,
len: usize,
}
impl SignatureEncoding for Signature {
type Repr = Box<[u8]>;
}
impl TryFrom<&[u8]> for Signature {
type Error = signature::Error;
fn try_from(bytes: &[u8]) -> signature::Result<Self> {
Ok(Self {
len: bytes.len(),
inner: BigUint::from_bytes_be(bytes),
})
}
}
impl From<Signature> for Box<[u8]> {
fn from(signature: Signature) -> Box<[u8]> {
signature.inner.to_bytes_be().into_boxed_slice()
}
}
impl Debug for Signature {
fn fmt(&self, fmt: &mut Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
fmt.debug_tuple("Signature")
.field(&self.to_string())
.finish()
}
}
impl LowerHex for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:x}", &self.inner)
}
}
impl UpperHex for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:X}", &self.inner)
}
}
impl Display for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:X}", self)
}
}
pub(crate) fn verify(
pub_key: &RsaPublicKey,
hashed: &[u8],
@ -289,64 +227,6 @@ fn sign_pss_with_salt_digest<T: CryptoRngCore + ?Sized, D: Digest + FixedOutputR
)
}
/// Signing key for producing RSASSA-PSS signatures as described in
/// [RFC8017 § 8.1].
///
/// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
#[derive(Debug, Clone)]
pub struct SigningKey<D>
where
D: Digest,
{
inner: RsaPrivateKey,
salt_len: usize,
phantom: PhantomData<D>,
}
impl<D> SigningKey<D>
where
D: Digest,
{
/// Create a new RSASSA-PSS signing key.
/// Digest output size is used as a salt length.
pub fn new(key: RsaPrivateKey) -> Self {
Self::new_with_salt_len(key, <D as Digest>::output_size())
}
/// Create a new RSASSA-PSS signing key with a salt of the given length.
pub fn new_with_salt_len(key: RsaPrivateKey, salt_len: usize) -> Self {
Self {
inner: key,
salt_len,
phantom: Default::default(),
}
}
/// Generate a new random RSASSA-PSS signing key.
/// Digest output size is used as a salt length.
pub fn random<R: CryptoRngCore + ?Sized>(rng: &mut R, bit_size: usize) -> Result<Self> {
Self::random_with_salt_len(rng, bit_size, <D as Digest>::output_size())
}
/// Generate a new random RSASSA-PSS signing key with a salt of the given length.
pub fn random_with_salt_len<R: CryptoRngCore + ?Sized>(
rng: &mut R,
bit_size: usize,
salt_len: usize,
) -> Result<Self> {
Ok(Self {
inner: RsaPrivateKey::new(rng, bit_size)?,
salt_len,
phantom: Default::default(),
})
}
/// Return specified salt length for this key
pub fn salt_len(&self) -> usize {
self.salt_len
}
}
fn get_pss_signature_algo_id<D>(salt_len: u8) -> pkcs8::spki::Result<AlgorithmIdentifierOwned>
where
D: Digest + AssociatedOid,
@ -361,447 +241,6 @@ where
})
}
impl<D> AssociatedAlgorithmIdentifier for SigningKey<D>
where
D: Digest,
{
type Params = AnyRef<'static>;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
}
impl<D> DynSignatureAlgorithmIdentifier for SigningKey<D>
where
D: Digest + AssociatedOid,
{
fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result<AlgorithmIdentifierOwned> {
get_pss_signature_algo_id::<D>(self.salt_len as u8)
}
}
impl<D> From<RsaPrivateKey> for SigningKey<D>
where
D: Digest,
{
fn from(key: RsaPrivateKey) -> Self {
Self::new(key)
}
}
impl<D> From<SigningKey<D>> for RsaPrivateKey
where
D: Digest,
{
fn from(key: SigningKey<D>) -> Self {
key.inner
}
}
impl<D> EncodePrivateKey for SigningKey<D>
where
D: Digest,
{
fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
self.inner.to_pkcs8_der()
}
}
impl<D> Keypair for SigningKey<D>
where
D: Digest,
{
type VerifyingKey = VerifyingKey<D>;
fn verifying_key(&self) -> Self::VerifyingKey {
VerifyingKey {
inner: self.inner.to_public_key(),
salt_len: self.salt_len,
phantom: Default::default(),
}
}
}
#[cfg(feature = "getrandom")]
impl<D> Signer<Signature> for SigningKey<D>
where
D: Digest + FixedOutputReset,
{
fn try_sign(&self, msg: &[u8]) -> signature::Result<Signature> {
self.try_sign_with_rng(&mut OsRng, msg)
}
}
impl<D> RandomizedSigner<Signature> for SigningKey<D>
where
D: Digest + FixedOutputReset,
{
fn try_sign_with_rng(
&self,
rng: &mut impl CryptoRngCore,
msg: &[u8],
) -> signature::Result<Signature> {
sign_digest::<_, D>(rng, false, &self.inner, &D::digest(msg), self.salt_len)?
.as_slice()
.try_into()
}
}
impl<D> RandomizedDigestSigner<D, Signature> for SigningKey<D>
where
D: Digest + FixedOutputReset,
{
fn try_sign_digest_with_rng(
&self,
rng: &mut impl CryptoRngCore,
digest: D,
) -> signature::Result<Signature> {
sign_digest::<_, D>(rng, false, &self.inner, &digest.finalize(), self.salt_len)?
.as_slice()
.try_into()
}
}
impl<D> RandomizedPrehashSigner<Signature> for SigningKey<D>
where
D: Digest + FixedOutputReset,
{
fn sign_prehash_with_rng(
&self,
rng: &mut impl CryptoRngCore,
prehash: &[u8],
) -> signature::Result<Signature> {
sign_digest::<_, D>(rng, false, &self.inner, prehash, self.salt_len)?
.as_slice()
.try_into()
}
}
impl<D> AsRef<RsaPrivateKey> for SigningKey<D>
where
D: Digest,
{
fn as_ref(&self) -> &RsaPrivateKey {
&self.inner
}
}
impl<D> ZeroizeOnDrop for SigningKey<D> where D: Digest {}
/// Signing key for producing "blinded" RSASSA-PSS signatures as described in
/// [draft-irtf-cfrg-rsa-blind-signatures](https://datatracker.ietf.org/doc/draft-irtf-cfrg-rsa-blind-signatures/).
#[derive(Debug, Clone)]
pub struct BlindedSigningKey<D>
where
D: Digest,
{
inner: RsaPrivateKey,
salt_len: usize,
phantom: PhantomData<D>,
}
impl<D> BlindedSigningKey<D>
where
D: Digest,
{
/// Create a new RSASSA-PSS signing key which produces "blinded"
/// signatures.
/// Digest output size is used as a salt length.
pub fn new(key: RsaPrivateKey) -> Self {
Self::new_with_salt_len(key, <D as Digest>::output_size())
}
/// Create a new RSASSA-PSS signing key which produces "blinded"
/// signatures with a salt of the given length.
pub fn new_with_salt_len(key: RsaPrivateKey, salt_len: usize) -> Self {
Self {
inner: key,
salt_len,
phantom: Default::default(),
}
}
/// Create a new random RSASSA-PSS signing key which produces "blinded"
/// signatures.
/// Digest output size is used as a salt length.
pub fn random<R: CryptoRngCore + ?Sized>(rng: &mut R, bit_size: usize) -> Result<Self> {
Self::random_with_salt_len(rng, bit_size, <D as Digest>::output_size())
}
/// Create a new random RSASSA-PSS signing key which produces "blinded"
/// signatures with a salt of the given length.
pub fn random_with_salt_len<R: CryptoRngCore + ?Sized>(
rng: &mut R,
bit_size: usize,
salt_len: usize,
) -> Result<Self> {
Ok(Self {
inner: RsaPrivateKey::new(rng, bit_size)?,
salt_len,
phantom: Default::default(),
})
}
/// Return specified salt length for this key
pub fn salt_len(&self) -> usize {
self.salt_len
}
}
impl<D> AssociatedAlgorithmIdentifier for BlindedSigningKey<D>
where
D: Digest,
{
type Params = AnyRef<'static>;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
}
impl<D> DynSignatureAlgorithmIdentifier for BlindedSigningKey<D>
where
D: Digest + AssociatedOid,
{
fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result<AlgorithmIdentifierOwned> {
get_pss_signature_algo_id::<D>(self.salt_len as u8)
}
}
impl<D> From<RsaPrivateKey> for BlindedSigningKey<D>
where
D: Digest,
{
fn from(key: RsaPrivateKey) -> Self {
Self::new(key)
}
}
impl<D> From<BlindedSigningKey<D>> for RsaPrivateKey
where
D: Digest,
{
fn from(key: BlindedSigningKey<D>) -> Self {
key.inner
}
}
impl<D> EncodePrivateKey for BlindedSigningKey<D>
where
D: Digest,
{
fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
self.inner.to_pkcs8_der()
}
}
impl<D> Keypair for BlindedSigningKey<D>
where
D: Digest,
{
type VerifyingKey = VerifyingKey<D>;
fn verifying_key(&self) -> Self::VerifyingKey {
VerifyingKey {
inner: self.inner.to_public_key(),
salt_len: self.salt_len,
phantom: Default::default(),
}
}
}
impl<D> RandomizedSigner<Signature> for BlindedSigningKey<D>
where
D: Digest + FixedOutputReset,
{
fn try_sign_with_rng(
&self,
rng: &mut impl CryptoRngCore,
msg: &[u8],
) -> signature::Result<Signature> {
sign_digest::<_, D>(rng, true, &self.inner, &D::digest(msg), self.salt_len)?
.as_slice()
.try_into()
}
}
impl<D> RandomizedDigestSigner<D, Signature> for BlindedSigningKey<D>
where
D: Digest + FixedOutputReset,
{
fn try_sign_digest_with_rng(
&self,
rng: &mut impl CryptoRngCore,
digest: D,
) -> signature::Result<Signature> {
sign_digest::<_, D>(rng, true, &self.inner, &digest.finalize(), self.salt_len)?
.as_slice()
.try_into()
}
}
impl<D> RandomizedPrehashSigner<Signature> for BlindedSigningKey<D>
where
D: Digest + FixedOutputReset,
{
fn sign_prehash_with_rng(
&self,
rng: &mut impl CryptoRngCore,
prehash: &[u8],
) -> signature::Result<Signature> {
sign_digest::<_, D>(rng, true, &self.inner, prehash, self.salt_len)?
.as_slice()
.try_into()
}
}
impl<D> AsRef<RsaPrivateKey> for BlindedSigningKey<D>
where
D: Digest,
{
fn as_ref(&self) -> &RsaPrivateKey {
&self.inner
}
}
impl<D> ZeroizeOnDrop for BlindedSigningKey<D> where D: Digest {}
/// Verifying key for checking the validity of RSASSA-PSS signatures as
/// described in [RFC8017 § 8.1].
///
/// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
#[derive(Debug)]
pub struct VerifyingKey<D>
where
D: Digest,
{
inner: RsaPublicKey,
salt_len: usize,
phantom: PhantomData<D>,
}
/* Implemented manually so we don't have to bind D with Clone */
impl<D> Clone for VerifyingKey<D>
where
D: Digest,
{
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
salt_len: self.salt_len,
phantom: Default::default(),
}
}
}
impl<D> VerifyingKey<D>
where
D: Digest,
{
/// Create a new RSASSA-PSS verifying key.
/// Digest output size is used as a salt length.
pub fn new(key: RsaPublicKey) -> Self {
Self::new_with_salt_len(key, <D as Digest>::output_size())
}
/// Create a new RSASSA-PSS verifying key.
pub fn new_with_salt_len(key: RsaPublicKey, salt_len: usize) -> Self {
Self {
inner: key,
salt_len,
phantom: Default::default(),
}
}
}
impl<D> AssociatedAlgorithmIdentifier for VerifyingKey<D>
where
D: Digest,
{
type Params = AnyRef<'static>;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
}
impl<D> From<RsaPublicKey> for VerifyingKey<D>
where
D: Digest,
{
fn from(key: RsaPublicKey) -> Self {
Self::new(key)
}
}
impl<D> From<VerifyingKey<D>> for RsaPublicKey
where
D: Digest,
{
fn from(key: VerifyingKey<D>) -> Self {
key.inner
}
}
impl<D> Verifier<Signature> for VerifyingKey<D>
where
D: Digest + FixedOutputReset,
{
fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> {
verify_digest::<D>(
&self.inner,
&D::digest(msg),
&signature.inner,
signature.len,
self.salt_len,
)
.map_err(|e| e.into())
}
}
impl<D> DigestVerifier<D, Signature> for VerifyingKey<D>
where
D: Digest + FixedOutputReset,
{
fn verify_digest(&self, digest: D, signature: &Signature) -> signature::Result<()> {
verify_digest::<D>(
&self.inner,
&digest.finalize(),
&signature.inner,
signature.len,
self.salt_len,
)
.map_err(|e| e.into())
}
}
impl<D> PrehashVerifier<Signature> for VerifyingKey<D>
where
D: Digest + FixedOutputReset,
{
fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> signature::Result<()> {
verify_digest::<D>(
&self.inner,
prehash,
&signature.inner,
signature.len,
self.salt_len,
)
.map_err(|e| e.into())
}
}
impl<D> AsRef<RsaPublicKey> for VerifyingKey<D>
where
D: Digest,
{
fn as_ref(&self) -> &RsaPublicKey {
&self.inner
}
}
impl<D> EncodePublicKey for VerifyingKey<D>
where
D: Digest,
{
fn to_public_key_der(&self) -> pkcs8::spki::Result<Document> {
self.inner.to_public_key_der()
}
}
#[cfg(test)]
mod test {
use crate::pss::{BlindedSigningKey, Pss, Signature, SigningKey, VerifyingKey};

View File

@ -0,0 +1,200 @@
use super::{get_pss_signature_algo_id, sign_digest, Signature, VerifyingKey};
use crate::{Result, RsaPrivateKey};
use const_oid::AssociatedOid;
use core::marker::PhantomData;
use digest::{Digest, FixedOutputReset};
use pkcs8::{
spki::{
der::AnyRef, AlgorithmIdentifierOwned, AlgorithmIdentifierRef,
AssociatedAlgorithmIdentifier, DynSignatureAlgorithmIdentifier,
},
EncodePrivateKey, SecretDocument,
};
use rand_core::CryptoRngCore;
use signature::{
hazmat::RandomizedPrehashSigner, Keypair, RandomizedDigestSigner, RandomizedSigner,
};
use zeroize::ZeroizeOnDrop;
/// Signing key for producing "blinded" RSASSA-PSS signatures as described in
/// [draft-irtf-cfrg-rsa-blind-signatures](https://datatracker.ietf.org/doc/draft-irtf-cfrg-rsa-blind-signatures/).
#[derive(Debug, Clone)]
pub struct BlindedSigningKey<D>
where
D: Digest,
{
inner: RsaPrivateKey,
salt_len: usize,
phantom: PhantomData<D>,
}
impl<D> BlindedSigningKey<D>
where
D: Digest,
{
/// Create a new RSASSA-PSS signing key which produces "blinded"
/// signatures.
/// Digest output size is used as a salt length.
pub fn new(key: RsaPrivateKey) -> Self {
Self::new_with_salt_len(key, <D as Digest>::output_size())
}
/// Create a new RSASSA-PSS signing key which produces "blinded"
/// signatures with a salt of the given length.
pub fn new_with_salt_len(key: RsaPrivateKey, salt_len: usize) -> Self {
Self {
inner: key,
salt_len,
phantom: Default::default(),
}
}
/// Create a new random RSASSA-PSS signing key which produces "blinded"
/// signatures.
/// Digest output size is used as a salt length.
pub fn random<R: CryptoRngCore + ?Sized>(rng: &mut R, bit_size: usize) -> Result<Self> {
Self::random_with_salt_len(rng, bit_size, <D as Digest>::output_size())
}
/// Create a new random RSASSA-PSS signing key which produces "blinded"
/// signatures with a salt of the given length.
pub fn random_with_salt_len<R: CryptoRngCore + ?Sized>(
rng: &mut R,
bit_size: usize,
salt_len: usize,
) -> Result<Self> {
Ok(Self {
inner: RsaPrivateKey::new(rng, bit_size)?,
salt_len,
phantom: Default::default(),
})
}
/// Return specified salt length for this key
pub fn salt_len(&self) -> usize {
self.salt_len
}
}
//
// `*Signer` trait impls
//
impl<D> RandomizedSigner<Signature> for BlindedSigningKey<D>
where
D: Digest + FixedOutputReset,
{
fn try_sign_with_rng(
&self,
rng: &mut impl CryptoRngCore,
msg: &[u8],
) -> signature::Result<Signature> {
sign_digest::<_, D>(rng, true, &self.inner, &D::digest(msg), self.salt_len)?
.as_slice()
.try_into()
}
}
impl<D> RandomizedDigestSigner<D, Signature> for BlindedSigningKey<D>
where
D: Digest + FixedOutputReset,
{
fn try_sign_digest_with_rng(
&self,
rng: &mut impl CryptoRngCore,
digest: D,
) -> signature::Result<Signature> {
sign_digest::<_, D>(rng, true, &self.inner, &digest.finalize(), self.salt_len)?
.as_slice()
.try_into()
}
}
impl<D> RandomizedPrehashSigner<Signature> for BlindedSigningKey<D>
where
D: Digest + FixedOutputReset,
{
fn sign_prehash_with_rng(
&self,
rng: &mut impl CryptoRngCore,
prehash: &[u8],
) -> signature::Result<Signature> {
sign_digest::<_, D>(rng, true, &self.inner, prehash, self.salt_len)?
.as_slice()
.try_into()
}
}
//
// Other trait impls
//
impl<D> AsRef<RsaPrivateKey> for BlindedSigningKey<D>
where
D: Digest,
{
fn as_ref(&self) -> &RsaPrivateKey {
&self.inner
}
}
impl<D> AssociatedAlgorithmIdentifier for BlindedSigningKey<D>
where
D: Digest,
{
type Params = AnyRef<'static>;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
}
impl<D> DynSignatureAlgorithmIdentifier for BlindedSigningKey<D>
where
D: Digest + AssociatedOid,
{
fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result<AlgorithmIdentifierOwned> {
get_pss_signature_algo_id::<D>(self.salt_len as u8)
}
}
impl<D> EncodePrivateKey for BlindedSigningKey<D>
where
D: Digest,
{
fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
self.inner.to_pkcs8_der()
}
}
impl<D> From<RsaPrivateKey> for BlindedSigningKey<D>
where
D: Digest,
{
fn from(key: RsaPrivateKey) -> Self {
Self::new(key)
}
}
impl<D> From<BlindedSigningKey<D>> for RsaPrivateKey
where
D: Digest,
{
fn from(key: BlindedSigningKey<D>) -> Self {
key.inner
}
}
impl<D> Keypair for BlindedSigningKey<D>
where
D: Digest,
{
type VerifyingKey = VerifyingKey<D>;
fn verifying_key(&self) -> Self::VerifyingKey {
VerifyingKey {
inner: self.inner.to_public_key(),
salt_len: self.salt_len,
phantom: Default::default(),
}
}
}
impl<D> ZeroizeOnDrop for BlindedSigningKey<D> where D: Digest {}

65
src/pss/signature.rs Normal file
View File

@ -0,0 +1,65 @@
pub use ::signature::{
hazmat::{PrehashSigner, PrehashVerifier},
DigestSigner, DigestVerifier, Error, Keypair, RandomizedDigestSigner, RandomizedSigner, Result,
SignatureEncoding, Signer, Verifier,
};
use alloc::{boxed::Box, string::ToString};
use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex};
use num_bigint::BigUint;
/// RSASSA-PSS signatures as described in [RFC8017 § 8.1].
///
/// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
#[derive(Clone, PartialEq, Eq)]
pub struct Signature {
pub(super) inner: BigUint,
pub(super) len: usize,
}
impl SignatureEncoding for Signature {
type Repr = Box<[u8]>;
}
impl TryFrom<&[u8]> for Signature {
type Error = signature::Error;
fn try_from(bytes: &[u8]) -> signature::Result<Self> {
Ok(Self {
len: bytes.len(),
inner: BigUint::from_bytes_be(bytes),
})
}
}
impl From<Signature> for Box<[u8]> {
fn from(signature: Signature) -> Box<[u8]> {
signature.inner.to_bytes_be().into_boxed_slice()
}
}
impl Debug for Signature {
fn fmt(&self, fmt: &mut Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
fmt.debug_tuple("Signature")
.field(&self.to_string())
.finish()
}
}
impl LowerHex for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:x}", &self.inner)
}
}
impl UpperHex for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:X}", &self.inner)
}
}
impl Display for Signature {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{:X}", self)
}
}

222
src/pss/signing_key.rs Normal file
View File

@ -0,0 +1,222 @@
use super::{get_pss_signature_algo_id, sign_digest, Signature, VerifyingKey};
use crate::{Result, RsaPrivateKey};
use const_oid::AssociatedOid;
use core::marker::PhantomData;
use digest::{Digest, FixedOutputReset};
use pkcs8::{
spki::{
der::AnyRef, AlgorithmIdentifierOwned, AlgorithmIdentifierRef,
AssociatedAlgorithmIdentifier, DynSignatureAlgorithmIdentifier,
},
EncodePrivateKey, SecretDocument,
};
use rand_core::CryptoRngCore;
use signature::{
hazmat::RandomizedPrehashSigner, Keypair, RandomizedDigestSigner, RandomizedSigner,
};
use zeroize::ZeroizeOnDrop;
#[cfg(feature = "getrandom")]
use {
rand_core::OsRng,
signature::{hazmat::PrehashSigner, Signer},
};
/// Signing key for producing RSASSA-PSS signatures as described in
/// [RFC8017 § 8.1].
///
/// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
#[derive(Debug, Clone)]
pub struct SigningKey<D>
where
D: Digest,
{
inner: RsaPrivateKey,
salt_len: usize,
phantom: PhantomData<D>,
}
impl<D> SigningKey<D>
where
D: Digest,
{
/// Create a new RSASSA-PSS signing key.
/// Digest output size is used as a salt length.
pub fn new(key: RsaPrivateKey) -> Self {
Self::new_with_salt_len(key, <D as Digest>::output_size())
}
/// Create a new RSASSA-PSS signing key with a salt of the given length.
pub fn new_with_salt_len(key: RsaPrivateKey, salt_len: usize) -> Self {
Self {
inner: key,
salt_len,
phantom: Default::default(),
}
}
/// Generate a new random RSASSA-PSS signing key.
/// Digest output size is used as a salt length.
pub fn random<R: CryptoRngCore + ?Sized>(rng: &mut R, bit_size: usize) -> Result<Self> {
Self::random_with_salt_len(rng, bit_size, <D as Digest>::output_size())
}
/// Generate a new random RSASSA-PSS signing key with a salt of the given length.
pub fn random_with_salt_len<R: CryptoRngCore + ?Sized>(
rng: &mut R,
bit_size: usize,
salt_len: usize,
) -> Result<Self> {
Ok(Self {
inner: RsaPrivateKey::new(rng, bit_size)?,
salt_len,
phantom: Default::default(),
})
}
/// Return specified salt length for this key
pub fn salt_len(&self) -> usize {
self.salt_len
}
}
//
// `*Signer` trait impls
//
impl<D> RandomizedDigestSigner<D, Signature> for SigningKey<D>
where
D: Digest + FixedOutputReset,
{
fn try_sign_digest_with_rng(
&self,
rng: &mut impl CryptoRngCore,
digest: D,
) -> signature::Result<Signature> {
sign_digest::<_, D>(rng, false, &self.inner, &digest.finalize(), self.salt_len)?
.as_slice()
.try_into()
}
}
impl<D> RandomizedSigner<Signature> for SigningKey<D>
where
D: Digest + FixedOutputReset,
{
fn try_sign_with_rng(
&self,
rng: &mut impl CryptoRngCore,
msg: &[u8],
) -> signature::Result<Signature> {
self.try_sign_digest_with_rng(rng, D::new_with_prefix(msg))
}
}
impl<D> RandomizedPrehashSigner<Signature> for SigningKey<D>
where
D: Digest + FixedOutputReset,
{
fn sign_prehash_with_rng(
&self,
rng: &mut impl CryptoRngCore,
prehash: &[u8],
) -> signature::Result<Signature> {
sign_digest::<_, D>(rng, false, &self.inner, prehash, self.salt_len)?
.as_slice()
.try_into()
}
}
#[cfg(feature = "getrandom")]
impl<D> PrehashSigner<Signature> for SigningKey<D>
where
D: Digest + FixedOutputReset,
{
fn sign_prehash(&self, prehash: &[u8]) -> signature::Result<Signature> {
self.sign_prehash_with_rng(&mut OsRng, prehash)
}
}
#[cfg(feature = "getrandom")]
impl<D> Signer<Signature> for SigningKey<D>
where
D: Digest + FixedOutputReset,
{
fn try_sign(&self, msg: &[u8]) -> signature::Result<Signature> {
self.try_sign_with_rng(&mut OsRng, msg)
}
}
//
// Other trait impls
//
impl<D> AsRef<RsaPrivateKey> for SigningKey<D>
where
D: Digest,
{
fn as_ref(&self) -> &RsaPrivateKey {
&self.inner
}
}
impl<D> AssociatedAlgorithmIdentifier for SigningKey<D>
where
D: Digest,
{
type Params = AnyRef<'static>;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
}
impl<D> DynSignatureAlgorithmIdentifier for SigningKey<D>
where
D: Digest + AssociatedOid,
{
fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result<AlgorithmIdentifierOwned> {
get_pss_signature_algo_id::<D>(self.salt_len as u8)
}
}
impl<D> EncodePrivateKey for SigningKey<D>
where
D: Digest,
{
fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
self.inner.to_pkcs8_der()
}
}
impl<D> From<RsaPrivateKey> for SigningKey<D>
where
D: Digest,
{
fn from(key: RsaPrivateKey) -> Self {
Self::new(key)
}
}
impl<D> From<SigningKey<D>> for RsaPrivateKey
where
D: Digest,
{
fn from(key: SigningKey<D>) -> Self {
key.inner
}
}
impl<D> Keypair for SigningKey<D>
where
D: Digest,
{
type VerifyingKey = VerifyingKey<D>;
fn verifying_key(&self) -> Self::VerifyingKey {
VerifyingKey {
inner: self.inner.to_public_key(),
salt_len: self.salt_len,
phantom: Default::default(),
}
}
}
impl<D> ZeroizeOnDrop for SigningKey<D> where D: Digest {}

158
src/pss/verifying_key.rs Normal file
View File

@ -0,0 +1,158 @@
use super::{verify_digest, Signature};
use crate::RsaPublicKey;
use core::marker::PhantomData;
use digest::{Digest, FixedOutputReset};
use pkcs8::{
spki::{der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier},
Document, EncodePublicKey,
};
use signature::{hazmat::PrehashVerifier, DigestVerifier, Verifier};
/// Verifying key for checking the validity of RSASSA-PSS signatures as
/// described in [RFC8017 § 8.1].
///
/// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
#[derive(Debug)]
pub struct VerifyingKey<D>
where
D: Digest,
{
pub(super) inner: RsaPublicKey,
pub(super) salt_len: usize,
pub(super) phantom: PhantomData<D>,
}
impl<D> VerifyingKey<D>
where
D: Digest,
{
/// Create a new RSASSA-PSS verifying key.
/// Digest output size is used as a salt length.
pub fn new(key: RsaPublicKey) -> Self {
Self::new_with_salt_len(key, <D as Digest>::output_size())
}
/// Create a new RSASSA-PSS verifying key.
pub fn new_with_salt_len(key: RsaPublicKey, salt_len: usize) -> Self {
Self {
inner: key,
salt_len,
phantom: Default::default(),
}
}
}
//
// `*Verifier` trait impls
//
impl<D> DigestVerifier<D, Signature> for VerifyingKey<D>
where
D: Digest + FixedOutputReset,
{
fn verify_digest(&self, digest: D, signature: &Signature) -> signature::Result<()> {
verify_digest::<D>(
&self.inner,
&digest.finalize(),
&signature.inner,
signature.len,
self.salt_len,
)
.map_err(|e| e.into())
}
}
impl<D> PrehashVerifier<Signature> for VerifyingKey<D>
where
D: Digest + FixedOutputReset,
{
fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> signature::Result<()> {
verify_digest::<D>(
&self.inner,
prehash,
&signature.inner,
signature.len,
self.salt_len,
)
.map_err(|e| e.into())
}
}
impl<D> Verifier<Signature> for VerifyingKey<D>
where
D: Digest + FixedOutputReset,
{
fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> {
verify_digest::<D>(
&self.inner,
&D::digest(msg),
&signature.inner,
signature.len,
self.salt_len,
)
.map_err(|e| e.into())
}
}
//
// Other trait impls
//
impl<D> AsRef<RsaPublicKey> for VerifyingKey<D>
where
D: Digest,
{
fn as_ref(&self) -> &RsaPublicKey {
&self.inner
}
}
impl<D> AssociatedAlgorithmIdentifier for VerifyingKey<D>
where
D: Digest,
{
type Params = AnyRef<'static>;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID;
}
// Implemented manually so we don't have to bind D with Clone
impl<D> Clone for VerifyingKey<D>
where
D: Digest,
{
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
salt_len: self.salt_len,
phantom: Default::default(),
}
}
}
impl<D> EncodePublicKey for VerifyingKey<D>
where
D: Digest,
{
fn to_public_key_der(&self) -> pkcs8::spki::Result<Document> {
self.inner.to_public_key_der()
}
}
impl<D> From<RsaPublicKey> for VerifyingKey<D>
where
D: Digest,
{
fn from(key: RsaPublicKey) -> Self {
Self::new(key)
}
}
impl<D> From<VerifyingKey<D>> for RsaPublicKey
where
D: Digest,
{
fn from(key: VerifyingKey<D>) -> Self {
key.inner
}
}