From f5918ad3bf3a5c662e506ea2ce0f7fc5d51867b4 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Thu, 27 Apr 2023 07:58:19 -0600 Subject: [PATCH] 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. --- src/key.rs | 2 +- src/lib.rs | 1 + src/oaep.rs | 152 +-------- src/oaep/decrypting_key.rs | 96 ++++++ src/oaep/encrypting_key.rs | 64 ++++ src/pkcs1v15.rs | 562 ++----------------------------- src/pkcs1v15/decrypting_key.rs | 51 +++ src/pkcs1v15/encrypting_key.rs | 29 ++ src/pkcs1v15/signature.rs | 65 ++++ src/pkcs1v15/signing_key.rs | 244 ++++++++++++++ src/pkcs1v15/verifying_key.rs | 192 +++++++++++ src/pss.rs | 589 +-------------------------------- src/pss/blinded_signing_key.rs | 200 +++++++++++ src/pss/signature.rs | 65 ++++ src/pss/signing_key.rs | 222 +++++++++++++ src/pss/verifying_key.rs | 158 +++++++++ 16 files changed, 1436 insertions(+), 1256 deletions(-) create mode 100644 src/oaep/decrypting_key.rs create mode 100644 src/oaep/encrypting_key.rs create mode 100644 src/pkcs1v15/decrypting_key.rs create mode 100644 src/pkcs1v15/encrypting_key.rs create mode 100644 src/pkcs1v15/signature.rs create mode 100644 src/pkcs1v15/signing_key.rs create mode 100644 src/pkcs1v15/verifying_key.rs create mode 100644 src/pss/blinded_signing_key.rs create mode 100644 src/pss/signature.rs create mode 100644 src/pss/signing_key.rs create mode 100644 src/pss/verifying_key.rs diff --git a/src/key.rs b/src/key.rs index ef01183b..ff0d5553 100644 --- a/src/key.rs +++ b/src/key.rs @@ -53,7 +53,7 @@ impl PartialEq for RsaPrivateKey { } impl Hash for RsaPrivateKey { - fn hash(&self, state: &mut H) -> () { + fn hash(&self, state: &mut H) { // Domain separator for RSA private keys state.write(b"RsaPrivateKey"); Hash::hash(&self.pubkey_components, state); diff --git a/src/lib.rs b/src/lib.rs index 4862d0d4..99d670e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -239,6 +239,7 @@ pub use pkcs8; pub use sha2; pub use crate::{ + errors::{Error, Result}, key::{RsaPrivateKey, RsaPublicKey}, oaep::Oaep, pkcs1v15::{Pkcs1v15Encrypt, Pkcs1v15Sign}, diff --git a/src/oaep.rs b/src/oaep.rs index a4c1580c..0cbd1e3b 100644 --- a/src/oaep.rs +++ b/src/oaep.rs @@ -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(&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 -where - D: Digest, - MGD: Digest + FixedOutputReset, -{ - inner: RsaPublicKey, - label: Option, - phantom: PhantomData, - mg_phantom: PhantomData, -} - -impl EncryptingKey -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>(key: RsaPublicKey, label: S) -> Self { - Self { - inner: key, - label: Some(label.as_ref().to_string()), - phantom: Default::default(), - mg_phantom: Default::default(), - } - } -} - -impl RandomizedEncryptor for EncryptingKey -where - D: Digest, - MGD: Digest + FixedOutputReset, -{ - fn encrypt_with_rng( - &self, - rng: &mut R, - msg: &[u8], - ) -> Result> { - 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 -where - D: Digest, - MGD: Digest + FixedOutputReset, -{ - inner: RsaPrivateKey, - label: Option, - phantom: PhantomData, - mg_phantom: PhantomData, -} - -impl DecryptingKey -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>(key: RsaPrivateKey, label: S) -> Self { - Self { - inner: key, - label: Some(label.as_ref().to_string()), - phantom: Default::default(), - mg_phantom: Default::default(), - } - } -} - -impl Decryptor for DecryptingKey -where - D: Digest, - MGD: Digest + FixedOutputReset, -{ - fn decrypt(&self, ciphertext: &[u8]) -> Result> { - decrypt_digest::( - None, - &self.inner, - ciphertext, - self.label.as_ref().cloned(), - ) - } -} - -impl RandomizedDecryptor for DecryptingKey -where - D: Digest, - MGD: Digest + FixedOutputReset, -{ - fn decrypt_with_rng( - &self, - rng: &mut R, - ciphertext: &[u8], - ) -> Result> { - decrypt_digest::<_, D, MGD>( - Some(rng), - &self.inner, - ciphertext, - self.label.as_ref().cloned(), - ) - } -} - -impl ZeroizeOnDrop for DecryptingKey -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}; diff --git a/src/oaep/decrypting_key.rs b/src/oaep/decrypting_key.rs new file mode 100644 index 00000000..eacff38e --- /dev/null +++ b/src/oaep/decrypting_key.rs @@ -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 +where + D: Digest, + MGD: Digest + FixedOutputReset, +{ + inner: RsaPrivateKey, + label: Option, + phantom: PhantomData, + mg_phantom: PhantomData, +} + +impl DecryptingKey +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>(key: RsaPrivateKey, label: S) -> Self { + Self { + inner: key, + label: Some(label.as_ref().to_string()), + phantom: Default::default(), + mg_phantom: Default::default(), + } + } +} + +impl Decryptor for DecryptingKey +where + D: Digest, + MGD: Digest + FixedOutputReset, +{ + fn decrypt(&self, ciphertext: &[u8]) -> Result> { + decrypt_digest::( + None, + &self.inner, + ciphertext, + self.label.as_ref().cloned(), + ) + } +} + +impl RandomizedDecryptor for DecryptingKey +where + D: Digest, + MGD: Digest + FixedOutputReset, +{ + fn decrypt_with_rng( + &self, + rng: &mut R, + ciphertext: &[u8], + ) -> Result> { + decrypt_digest::<_, D, MGD>( + Some(rng), + &self.inner, + ciphertext, + self.label.as_ref().cloned(), + ) + } +} + +impl ZeroizeOnDrop for DecryptingKey +where + D: Digest, + MGD: Digest + FixedOutputReset, +{ +} diff --git a/src/oaep/encrypting_key.rs b/src/oaep/encrypting_key.rs new file mode 100644 index 00000000..0951e652 --- /dev/null +++ b/src/oaep/encrypting_key.rs @@ -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 +where + D: Digest, + MGD: Digest + FixedOutputReset, +{ + inner: RsaPublicKey, + label: Option, + phantom: PhantomData, + mg_phantom: PhantomData, +} + +impl EncryptingKey +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>(key: RsaPublicKey, label: S) -> Self { + Self { + inner: key, + label: Some(label.as_ref().to_string()), + phantom: Default::default(), + mg_phantom: Default::default(), + } + } +} + +impl RandomizedEncryptor for EncryptingKey +where + D: Digest, + MGD: Digest + FixedOutputReset, +{ + fn encrypt_with_rng( + &self, + rng: &mut R, + msg: &[u8], + ) -> Result> { + encrypt_digest::<_, D, MGD>(rng, &self.inner, msg, self.label.as_ref().cloned()) + } +} diff --git a/src/pkcs1v15.rs b/src/pkcs1v15.rs index 18844e13..1beca6a7 100644 --- a/src/pkcs1v15.rs +++ b/src/pkcs1v15.rs @@ -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 { - Ok(Self { - inner: BigUint::from_bytes_be(bytes), - len: bytes.len(), - }) - } -} - -impl From 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 -where - D: Digest, -{ - inner: RsaPrivateKey, - prefix: Vec, - phantom: PhantomData, -} - -impl SigningKey -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( - rng: &mut R, - bit_size: usize, - ) -> Result { - Ok(Self { - inner: RsaPrivateKey::new(rng, bit_size)?, - prefix: Vec::new(), - phantom: Default::default(), - }) - } -} - -impl AssociatedAlgorithmIdentifier for SigningKey -where - D: Digest, -{ - type Params = AnyRef<'static>; - - const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; -} - -impl SignatureAlgorithmIdentifier for SigningKey -where - D: Digest + oid::RsaSignatureAssociatedOid, -{ - type Params = AnyRef<'static>; - - const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = - AlgorithmIdentifierRef { - oid: D::OID, - parameters: Some(AnyRef::NULL), - }; -} - -impl From for SigningKey -where - D: Digest, -{ - fn from(key: RsaPrivateKey) -> Self { - Self::new_unprefixed(key) - } -} - -impl From> for RsaPrivateKey -where - D: Digest, -{ - fn from(key: SigningKey) -> Self { - key.inner - } -} - -impl SigningKey -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::(), - phantom: Default::default(), - } - } - - /// Generate a new signing key with a prefix for the digest `D`. - pub fn random(rng: &mut R, bit_size: usize) -> Result { - Ok(Self { - inner: RsaPrivateKey::new(rng, bit_size)?, - prefix: pkcs1v15_generate_prefix::(), - 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( - rng: &mut R, - bit_size: usize, - ) -> Result { - Self::random(rng, bit_size) - } -} - -impl AsRef for SigningKey -where - D: Digest, -{ - fn as_ref(&self) -> &RsaPrivateKey { - &self.inner - } -} - -impl EncodePrivateKey for SigningKey -where - D: Digest, -{ - fn to_pkcs8_der(&self) -> pkcs8::Result { - self.inner.to_pkcs8_der() - } -} - -impl ZeroizeOnDrop for SigningKey where D: Digest {} - -impl Signer for SigningKey -where - D: Digest, -{ - fn try_sign(&self, msg: &[u8]) -> signature::Result { - sign::(None, &self.inner, &self.prefix, &D::digest(msg))? - .as_slice() - .try_into() - } -} - -impl RandomizedSigner for SigningKey -where - D: Digest, -{ - fn try_sign_with_rng( - &self, - rng: &mut impl CryptoRngCore, - msg: &[u8], - ) -> signature::Result { - sign(Some(rng), &self.inner, &self.prefix, &D::digest(msg))? - .as_slice() - .try_into() - } -} - -impl DigestSigner for SigningKey -where - D: Digest, -{ - fn try_sign_digest(&self, digest: D) -> signature::Result { - sign::(None, &self.inner, &self.prefix, &digest.finalize())? - .as_slice() - .try_into() - } -} - -impl RandomizedDigestSigner for SigningKey -where - D: Digest, -{ - fn try_sign_digest_with_rng( - &self, - rng: &mut impl CryptoRngCore, - digest: D, - ) -> signature::Result { - sign(Some(rng), &self.inner, &self.prefix, &digest.finalize())? - .as_slice() - .try_into() - } -} - -impl PrehashSigner for SigningKey -where - D: Digest, -{ - fn sign_prehash(&self, prehash: &[u8]) -> signature::Result { - sign::(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 -where - D: Digest, -{ - inner: RsaPublicKey, - prefix: Vec, - phantom: PhantomData, -} - -/* Implemented manually so we don't have to bind D with Clone */ -impl Clone for VerifyingKey -where - D: Digest, -{ - fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), - prefix: self.prefix.clone(), - phantom: Default::default(), - } - } -} - -impl VerifyingKey -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 AssociatedAlgorithmIdentifier for VerifyingKey -where - D: Digest, -{ - type Params = AnyRef<'static>; - - const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; -} - -impl SignatureAlgorithmIdentifier for VerifyingKey -where - D: Digest + oid::RsaSignatureAssociatedOid, -{ - type Params = AnyRef<'static>; - - const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = - AlgorithmIdentifierRef { - oid: D::OID, - parameters: Some(AnyRef::NULL), - }; -} - -impl From for VerifyingKey -where - D: Digest, -{ - fn from(key: RsaPublicKey) -> Self { - Self::new_unprefixed(key) - } -} - -impl From> for RsaPublicKey -where - D: Digest, -{ - fn from(key: VerifyingKey) -> Self { - key.inner - } -} - -impl VerifyingKey -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::(), - 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 AsRef for VerifyingKey -where - D: Digest, -{ - fn as_ref(&self) -> &RsaPublicKey { - &self.inner - } -} - -impl Keypair for SigningKey -where - D: Digest, -{ - type VerifyingKey = VerifyingKey; - fn verifying_key(&self) -> Self::VerifyingKey { - VerifyingKey { - inner: self.inner.to_public_key(), - prefix: self.prefix.clone(), - phantom: Default::default(), - } - } -} - -impl Verifier for VerifyingKey -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 DigestVerifier for VerifyingKey -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 PrehashVerifier for VerifyingKey -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 EncodePublicKey for VerifyingKey -where - D: Digest, -{ - fn to_public_key_der(&self) -> pkcs8::spki::Result { - 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( - &self, - rng: &mut R, - msg: &[u8], - ) -> Result> { - 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> { - decrypt::(None, &self.inner, ciphertext) - } -} - -impl RandomizedDecryptor for DecryptingKey { - fn decrypt_with_rng( - &self, - rng: &mut R, - ciphertext: &[u8], - ) -> Result> { - 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: diff --git a/src/pkcs1v15/decrypting_key.rs b/src/pkcs1v15/decrypting_key.rs new file mode 100644 index 00000000..0bd6dc89 --- /dev/null +++ b/src/pkcs1v15/decrypting_key.rs @@ -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> { + decrypt::(None, &self.inner, ciphertext) + } +} + +impl RandomizedDecryptor for DecryptingKey { + fn decrypt_with_rng( + &self, + rng: &mut R, + ciphertext: &[u8], + ) -> Result> { + 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 {} diff --git a/src/pkcs1v15/encrypting_key.rs b/src/pkcs1v15/encrypting_key.rs new file mode 100644 index 00000000..80db0f60 --- /dev/null +++ b/src/pkcs1v15/encrypting_key.rs @@ -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( + &self, + rng: &mut R, + msg: &[u8], + ) -> Result> { + encrypt(rng, &self.inner, msg) + } +} diff --git a/src/pkcs1v15/signature.rs b/src/pkcs1v15/signature.rs new file mode 100644 index 00000000..2dd169ad --- /dev/null +++ b/src/pkcs1v15/signature.rs @@ -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 { + Ok(Self { + inner: BigUint::from_bytes_be(bytes), + len: bytes.len(), + }) + } +} + +impl From 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) + } +} diff --git a/src/pkcs1v15/signing_key.rs b/src/pkcs1v15/signing_key.rs new file mode 100644 index 00000000..fc38958e --- /dev/null +++ b/src/pkcs1v15/signing_key.rs @@ -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 +where + D: Digest, +{ + inner: RsaPrivateKey, + prefix: Vec, + phantom: PhantomData, +} + +impl SigningKey +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( + rng: &mut R, + bit_size: usize, + ) -> Result { + Ok(Self { + inner: RsaPrivateKey::new(rng, bit_size)?, + prefix: Vec::new(), + phantom: Default::default(), + }) + } +} + +impl SigningKey +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::(), + phantom: Default::default(), + } + } + + /// Generate a new signing key with a prefix for the digest `D`. + pub fn random(rng: &mut R, bit_size: usize) -> Result { + Ok(Self { + inner: RsaPrivateKey::new(rng, bit_size)?, + prefix: pkcs1v15_generate_prefix::(), + 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( + rng: &mut R, + bit_size: usize, + ) -> Result { + Self::random(rng, bit_size) + } +} + +// +// `*Signer` trait impls +// + +impl DigestSigner for SigningKey +where + D: Digest, +{ + fn try_sign_digest(&self, digest: D) -> signature::Result { + sign::(None, &self.inner, &self.prefix, &digest.finalize())? + .as_slice() + .try_into() + } +} + +impl PrehashSigner for SigningKey +where + D: Digest, +{ + fn sign_prehash(&self, prehash: &[u8]) -> signature::Result { + sign::(None, &self.inner, &self.prefix, prehash)? + .as_slice() + .try_into() + } +} + +impl RandomizedDigestSigner for SigningKey +where + D: Digest, +{ + fn try_sign_digest_with_rng( + &self, + rng: &mut impl CryptoRngCore, + digest: D, + ) -> signature::Result { + sign(Some(rng), &self.inner, &self.prefix, &digest.finalize())? + .as_slice() + .try_into() + } +} + +impl RandomizedSigner for SigningKey +where + D: Digest, +{ + fn try_sign_with_rng( + &self, + rng: &mut impl CryptoRngCore, + msg: &[u8], + ) -> signature::Result { + sign(Some(rng), &self.inner, &self.prefix, &D::digest(msg))? + .as_slice() + .try_into() + } +} + +impl Signer for SigningKey +where + D: Digest, +{ + fn try_sign(&self, msg: &[u8]) -> signature::Result { + sign::(None, &self.inner, &self.prefix, &D::digest(msg))? + .as_slice() + .try_into() + } +} + +// +// Other trait impls +// + +impl AsRef for SigningKey +where + D: Digest, +{ + fn as_ref(&self) -> &RsaPrivateKey { + &self.inner + } +} + +impl AssociatedAlgorithmIdentifier for SigningKey +where + D: Digest, +{ + type Params = AnyRef<'static>; + + const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; +} + +impl EncodePrivateKey for SigningKey +where + D: Digest, +{ + fn to_pkcs8_der(&self) -> pkcs8::Result { + self.inner.to_pkcs8_der() + } +} + +impl From for SigningKey +where + D: Digest, +{ + fn from(key: RsaPrivateKey) -> Self { + Self::new_unprefixed(key) + } +} + +impl From> for RsaPrivateKey +where + D: Digest, +{ + fn from(key: SigningKey) -> Self { + key.inner + } +} + +impl Keypair for SigningKey +where + D: Digest, +{ + type VerifyingKey = VerifyingKey; + + fn verifying_key(&self) -> Self::VerifyingKey { + VerifyingKey { + inner: self.inner.to_public_key(), + prefix: self.prefix.clone(), + phantom: Default::default(), + } + } +} + +impl SignatureAlgorithmIdentifier for SigningKey +where + D: Digest + oid::RsaSignatureAssociatedOid, +{ + type Params = AnyRef<'static>; + + const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = + AlgorithmIdentifierRef { + oid: D::OID, + parameters: Some(AnyRef::NULL), + }; +} + +impl ZeroizeOnDrop for SigningKey where D: Digest {} diff --git a/src/pkcs1v15/verifying_key.rs b/src/pkcs1v15/verifying_key.rs new file mode 100644 index 00000000..789a1533 --- /dev/null +++ b/src/pkcs1v15/verifying_key.rs @@ -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 +where + D: Digest, +{ + pub(super) inner: RsaPublicKey, + pub(super) prefix: Vec, + pub(super) phantom: PhantomData, +} + +impl VerifyingKey +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 VerifyingKey +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::(), + 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 DigestVerifier for VerifyingKey +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 PrehashVerifier for VerifyingKey +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 Verifier for VerifyingKey +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 AsRef for VerifyingKey +where + D: Digest, +{ + fn as_ref(&self) -> &RsaPublicKey { + &self.inner + } +} + +impl AssociatedAlgorithmIdentifier for VerifyingKey +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 Clone for VerifyingKey +where + D: Digest, +{ + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + prefix: self.prefix.clone(), + phantom: Default::default(), + } + } +} + +impl EncodePublicKey for VerifyingKey +where + D: Digest, +{ + fn to_public_key_der(&self) -> pkcs8::spki::Result { + self.inner.to_public_key_der() + } +} + +impl From for VerifyingKey +where + D: Digest, +{ + fn from(key: RsaPublicKey) -> Self { + Self::new_unprefixed(key) + } +} + +impl From> for RsaPublicKey +where + D: Digest, +{ + fn from(key: VerifyingKey) -> Self { + key.inner + } +} + +impl SignatureAlgorithmIdentifier for VerifyingKey +where + D: Digest + oid::RsaSignatureAssociatedOid, +{ + type Params = AnyRef<'static>; + + const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = + AlgorithmIdentifierRef { + oid: D::OID, + parameters: Some(AnyRef::NULL), + }; +} diff --git a/src/pss.rs b/src/pss.rs index e6c7e51c..1bb6c410 100644 --- a/src/pss.rs +++ b/src/pss.rs @@ -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 { - Ok(Self { - len: bytes.len(), - inner: BigUint::from_bytes_be(bytes), - }) - } -} - -impl From 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 -where - D: Digest, -{ - inner: RsaPrivateKey, - salt_len: usize, - phantom: PhantomData, -} - -impl SigningKey -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, ::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(rng: &mut R, bit_size: usize) -> Result { - Self::random_with_salt_len(rng, bit_size, ::output_size()) - } - - /// Generate a new random RSASSA-PSS signing key with a salt of the given length. - pub fn random_with_salt_len( - rng: &mut R, - bit_size: usize, - salt_len: usize, - ) -> Result { - 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(salt_len: u8) -> pkcs8::spki::Result where D: Digest + AssociatedOid, @@ -361,447 +241,6 @@ where }) } -impl AssociatedAlgorithmIdentifier for SigningKey -where - D: Digest, -{ - type Params = AnyRef<'static>; - - const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; -} - -impl DynSignatureAlgorithmIdentifier for SigningKey -where - D: Digest + AssociatedOid, -{ - fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result { - get_pss_signature_algo_id::(self.salt_len as u8) - } -} - -impl From for SigningKey -where - D: Digest, -{ - fn from(key: RsaPrivateKey) -> Self { - Self::new(key) - } -} - -impl From> for RsaPrivateKey -where - D: Digest, -{ - fn from(key: SigningKey) -> Self { - key.inner - } -} - -impl EncodePrivateKey for SigningKey -where - D: Digest, -{ - fn to_pkcs8_der(&self) -> pkcs8::Result { - self.inner.to_pkcs8_der() - } -} - -impl Keypair for SigningKey -where - D: Digest, -{ - type VerifyingKey = VerifyingKey; - 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 Signer for SigningKey -where - D: Digest + FixedOutputReset, -{ - fn try_sign(&self, msg: &[u8]) -> signature::Result { - self.try_sign_with_rng(&mut OsRng, msg) - } -} - -impl RandomizedSigner for SigningKey -where - D: Digest + FixedOutputReset, -{ - fn try_sign_with_rng( - &self, - rng: &mut impl CryptoRngCore, - msg: &[u8], - ) -> signature::Result { - sign_digest::<_, D>(rng, false, &self.inner, &D::digest(msg), self.salt_len)? - .as_slice() - .try_into() - } -} - -impl RandomizedDigestSigner for SigningKey -where - D: Digest + FixedOutputReset, -{ - fn try_sign_digest_with_rng( - &self, - rng: &mut impl CryptoRngCore, - digest: D, - ) -> signature::Result { - sign_digest::<_, D>(rng, false, &self.inner, &digest.finalize(), self.salt_len)? - .as_slice() - .try_into() - } -} - -impl RandomizedPrehashSigner for SigningKey -where - D: Digest + FixedOutputReset, -{ - fn sign_prehash_with_rng( - &self, - rng: &mut impl CryptoRngCore, - prehash: &[u8], - ) -> signature::Result { - sign_digest::<_, D>(rng, false, &self.inner, prehash, self.salt_len)? - .as_slice() - .try_into() - } -} - -impl AsRef for SigningKey -where - D: Digest, -{ - fn as_ref(&self) -> &RsaPrivateKey { - &self.inner - } -} - -impl ZeroizeOnDrop for SigningKey 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 -where - D: Digest, -{ - inner: RsaPrivateKey, - salt_len: usize, - phantom: PhantomData, -} - -impl BlindedSigningKey -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, ::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(rng: &mut R, bit_size: usize) -> Result { - Self::random_with_salt_len(rng, bit_size, ::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( - rng: &mut R, - bit_size: usize, - salt_len: usize, - ) -> Result { - 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 AssociatedAlgorithmIdentifier for BlindedSigningKey -where - D: Digest, -{ - type Params = AnyRef<'static>; - - const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; -} - -impl DynSignatureAlgorithmIdentifier for BlindedSigningKey -where - D: Digest + AssociatedOid, -{ - fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result { - get_pss_signature_algo_id::(self.salt_len as u8) - } -} - -impl From for BlindedSigningKey -where - D: Digest, -{ - fn from(key: RsaPrivateKey) -> Self { - Self::new(key) - } -} - -impl From> for RsaPrivateKey -where - D: Digest, -{ - fn from(key: BlindedSigningKey) -> Self { - key.inner - } -} - -impl EncodePrivateKey for BlindedSigningKey -where - D: Digest, -{ - fn to_pkcs8_der(&self) -> pkcs8::Result { - self.inner.to_pkcs8_der() - } -} - -impl Keypair for BlindedSigningKey -where - D: Digest, -{ - type VerifyingKey = VerifyingKey; - fn verifying_key(&self) -> Self::VerifyingKey { - VerifyingKey { - inner: self.inner.to_public_key(), - salt_len: self.salt_len, - phantom: Default::default(), - } - } -} - -impl RandomizedSigner for BlindedSigningKey -where - D: Digest + FixedOutputReset, -{ - fn try_sign_with_rng( - &self, - rng: &mut impl CryptoRngCore, - msg: &[u8], - ) -> signature::Result { - sign_digest::<_, D>(rng, true, &self.inner, &D::digest(msg), self.salt_len)? - .as_slice() - .try_into() - } -} - -impl RandomizedDigestSigner for BlindedSigningKey -where - D: Digest + FixedOutputReset, -{ - fn try_sign_digest_with_rng( - &self, - rng: &mut impl CryptoRngCore, - digest: D, - ) -> signature::Result { - sign_digest::<_, D>(rng, true, &self.inner, &digest.finalize(), self.salt_len)? - .as_slice() - .try_into() - } -} - -impl RandomizedPrehashSigner for BlindedSigningKey -where - D: Digest + FixedOutputReset, -{ - fn sign_prehash_with_rng( - &self, - rng: &mut impl CryptoRngCore, - prehash: &[u8], - ) -> signature::Result { - sign_digest::<_, D>(rng, true, &self.inner, prehash, self.salt_len)? - .as_slice() - .try_into() - } -} - -impl AsRef for BlindedSigningKey -where - D: Digest, -{ - fn as_ref(&self) -> &RsaPrivateKey { - &self.inner - } -} - -impl ZeroizeOnDrop for BlindedSigningKey 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 -where - D: Digest, -{ - inner: RsaPublicKey, - salt_len: usize, - phantom: PhantomData, -} - -/* Implemented manually so we don't have to bind D with Clone */ -impl Clone for VerifyingKey -where - D: Digest, -{ - fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), - salt_len: self.salt_len, - phantom: Default::default(), - } - } -} - -impl VerifyingKey -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, ::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 AssociatedAlgorithmIdentifier for VerifyingKey -where - D: Digest, -{ - type Params = AnyRef<'static>; - - const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; -} - -impl From for VerifyingKey -where - D: Digest, -{ - fn from(key: RsaPublicKey) -> Self { - Self::new(key) - } -} - -impl From> for RsaPublicKey -where - D: Digest, -{ - fn from(key: VerifyingKey) -> Self { - key.inner - } -} - -impl Verifier for VerifyingKey -where - D: Digest + FixedOutputReset, -{ - fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> { - verify_digest::( - &self.inner, - &D::digest(msg), - &signature.inner, - signature.len, - self.salt_len, - ) - .map_err(|e| e.into()) - } -} - -impl DigestVerifier for VerifyingKey -where - D: Digest + FixedOutputReset, -{ - fn verify_digest(&self, digest: D, signature: &Signature) -> signature::Result<()> { - verify_digest::( - &self.inner, - &digest.finalize(), - &signature.inner, - signature.len, - self.salt_len, - ) - .map_err(|e| e.into()) - } -} - -impl PrehashVerifier for VerifyingKey -where - D: Digest + FixedOutputReset, -{ - fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> signature::Result<()> { - verify_digest::( - &self.inner, - prehash, - &signature.inner, - signature.len, - self.salt_len, - ) - .map_err(|e| e.into()) - } -} - -impl AsRef for VerifyingKey -where - D: Digest, -{ - fn as_ref(&self) -> &RsaPublicKey { - &self.inner - } -} - -impl EncodePublicKey for VerifyingKey -where - D: Digest, -{ - fn to_public_key_der(&self) -> pkcs8::spki::Result { - self.inner.to_public_key_der() - } -} - #[cfg(test)] mod test { use crate::pss::{BlindedSigningKey, Pss, Signature, SigningKey, VerifyingKey}; diff --git a/src/pss/blinded_signing_key.rs b/src/pss/blinded_signing_key.rs new file mode 100644 index 00000000..adc0ff5f --- /dev/null +++ b/src/pss/blinded_signing_key.rs @@ -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 +where + D: Digest, +{ + inner: RsaPrivateKey, + salt_len: usize, + phantom: PhantomData, +} + +impl BlindedSigningKey +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, ::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(rng: &mut R, bit_size: usize) -> Result { + Self::random_with_salt_len(rng, bit_size, ::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( + rng: &mut R, + bit_size: usize, + salt_len: usize, + ) -> Result { + 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 RandomizedSigner for BlindedSigningKey +where + D: Digest + FixedOutputReset, +{ + fn try_sign_with_rng( + &self, + rng: &mut impl CryptoRngCore, + msg: &[u8], + ) -> signature::Result { + sign_digest::<_, D>(rng, true, &self.inner, &D::digest(msg), self.salt_len)? + .as_slice() + .try_into() + } +} + +impl RandomizedDigestSigner for BlindedSigningKey +where + D: Digest + FixedOutputReset, +{ + fn try_sign_digest_with_rng( + &self, + rng: &mut impl CryptoRngCore, + digest: D, + ) -> signature::Result { + sign_digest::<_, D>(rng, true, &self.inner, &digest.finalize(), self.salt_len)? + .as_slice() + .try_into() + } +} + +impl RandomizedPrehashSigner for BlindedSigningKey +where + D: Digest + FixedOutputReset, +{ + fn sign_prehash_with_rng( + &self, + rng: &mut impl CryptoRngCore, + prehash: &[u8], + ) -> signature::Result { + sign_digest::<_, D>(rng, true, &self.inner, prehash, self.salt_len)? + .as_slice() + .try_into() + } +} + +// +// Other trait impls +// + +impl AsRef for BlindedSigningKey +where + D: Digest, +{ + fn as_ref(&self) -> &RsaPrivateKey { + &self.inner + } +} + +impl AssociatedAlgorithmIdentifier for BlindedSigningKey +where + D: Digest, +{ + type Params = AnyRef<'static>; + + const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; +} + +impl DynSignatureAlgorithmIdentifier for BlindedSigningKey +where + D: Digest + AssociatedOid, +{ + fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result { + get_pss_signature_algo_id::(self.salt_len as u8) + } +} + +impl EncodePrivateKey for BlindedSigningKey +where + D: Digest, +{ + fn to_pkcs8_der(&self) -> pkcs8::Result { + self.inner.to_pkcs8_der() + } +} + +impl From for BlindedSigningKey +where + D: Digest, +{ + fn from(key: RsaPrivateKey) -> Self { + Self::new(key) + } +} + +impl From> for RsaPrivateKey +where + D: Digest, +{ + fn from(key: BlindedSigningKey) -> Self { + key.inner + } +} + +impl Keypair for BlindedSigningKey +where + D: Digest, +{ + type VerifyingKey = VerifyingKey; + fn verifying_key(&self) -> Self::VerifyingKey { + VerifyingKey { + inner: self.inner.to_public_key(), + salt_len: self.salt_len, + phantom: Default::default(), + } + } +} + +impl ZeroizeOnDrop for BlindedSigningKey where D: Digest {} diff --git a/src/pss/signature.rs b/src/pss/signature.rs new file mode 100644 index 00000000..0718d33e --- /dev/null +++ b/src/pss/signature.rs @@ -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 { + Ok(Self { + len: bytes.len(), + inner: BigUint::from_bytes_be(bytes), + }) + } +} + +impl From 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) + } +} diff --git a/src/pss/signing_key.rs b/src/pss/signing_key.rs new file mode 100644 index 00000000..39b41472 --- /dev/null +++ b/src/pss/signing_key.rs @@ -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 +where + D: Digest, +{ + inner: RsaPrivateKey, + salt_len: usize, + phantom: PhantomData, +} + +impl SigningKey +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, ::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(rng: &mut R, bit_size: usize) -> Result { + Self::random_with_salt_len(rng, bit_size, ::output_size()) + } + + /// Generate a new random RSASSA-PSS signing key with a salt of the given length. + pub fn random_with_salt_len( + rng: &mut R, + bit_size: usize, + salt_len: usize, + ) -> Result { + 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 RandomizedDigestSigner for SigningKey +where + D: Digest + FixedOutputReset, +{ + fn try_sign_digest_with_rng( + &self, + rng: &mut impl CryptoRngCore, + digest: D, + ) -> signature::Result { + sign_digest::<_, D>(rng, false, &self.inner, &digest.finalize(), self.salt_len)? + .as_slice() + .try_into() + } +} + +impl RandomizedSigner for SigningKey +where + D: Digest + FixedOutputReset, +{ + fn try_sign_with_rng( + &self, + rng: &mut impl CryptoRngCore, + msg: &[u8], + ) -> signature::Result { + self.try_sign_digest_with_rng(rng, D::new_with_prefix(msg)) + } +} + +impl RandomizedPrehashSigner for SigningKey +where + D: Digest + FixedOutputReset, +{ + fn sign_prehash_with_rng( + &self, + rng: &mut impl CryptoRngCore, + prehash: &[u8], + ) -> signature::Result { + sign_digest::<_, D>(rng, false, &self.inner, prehash, self.salt_len)? + .as_slice() + .try_into() + } +} + +#[cfg(feature = "getrandom")] +impl PrehashSigner for SigningKey +where + D: Digest + FixedOutputReset, +{ + fn sign_prehash(&self, prehash: &[u8]) -> signature::Result { + self.sign_prehash_with_rng(&mut OsRng, prehash) + } +} + +#[cfg(feature = "getrandom")] +impl Signer for SigningKey +where + D: Digest + FixedOutputReset, +{ + fn try_sign(&self, msg: &[u8]) -> signature::Result { + self.try_sign_with_rng(&mut OsRng, msg) + } +} + +// +// Other trait impls +// + +impl AsRef for SigningKey +where + D: Digest, +{ + fn as_ref(&self) -> &RsaPrivateKey { + &self.inner + } +} + +impl AssociatedAlgorithmIdentifier for SigningKey +where + D: Digest, +{ + type Params = AnyRef<'static>; + + const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; +} + +impl DynSignatureAlgorithmIdentifier for SigningKey +where + D: Digest + AssociatedOid, +{ + fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result { + get_pss_signature_algo_id::(self.salt_len as u8) + } +} + +impl EncodePrivateKey for SigningKey +where + D: Digest, +{ + fn to_pkcs8_der(&self) -> pkcs8::Result { + self.inner.to_pkcs8_der() + } +} + +impl From for SigningKey +where + D: Digest, +{ + fn from(key: RsaPrivateKey) -> Self { + Self::new(key) + } +} + +impl From> for RsaPrivateKey +where + D: Digest, +{ + fn from(key: SigningKey) -> Self { + key.inner + } +} + +impl Keypair for SigningKey +where + D: Digest, +{ + type VerifyingKey = VerifyingKey; + fn verifying_key(&self) -> Self::VerifyingKey { + VerifyingKey { + inner: self.inner.to_public_key(), + salt_len: self.salt_len, + phantom: Default::default(), + } + } +} + +impl ZeroizeOnDrop for SigningKey where D: Digest {} diff --git a/src/pss/verifying_key.rs b/src/pss/verifying_key.rs new file mode 100644 index 00000000..ed065492 --- /dev/null +++ b/src/pss/verifying_key.rs @@ -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 +where + D: Digest, +{ + pub(super) inner: RsaPublicKey, + pub(super) salt_len: usize, + pub(super) phantom: PhantomData, +} + +impl VerifyingKey +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, ::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 DigestVerifier for VerifyingKey +where + D: Digest + FixedOutputReset, +{ + fn verify_digest(&self, digest: D, signature: &Signature) -> signature::Result<()> { + verify_digest::( + &self.inner, + &digest.finalize(), + &signature.inner, + signature.len, + self.salt_len, + ) + .map_err(|e| e.into()) + } +} + +impl PrehashVerifier for VerifyingKey +where + D: Digest + FixedOutputReset, +{ + fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> signature::Result<()> { + verify_digest::( + &self.inner, + prehash, + &signature.inner, + signature.len, + self.salt_len, + ) + .map_err(|e| e.into()) + } +} + +impl Verifier for VerifyingKey +where + D: Digest + FixedOutputReset, +{ + fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> { + verify_digest::( + &self.inner, + &D::digest(msg), + &signature.inner, + signature.len, + self.salt_len, + ) + .map_err(|e| e.into()) + } +} + +// +// Other trait impls +// + +impl AsRef for VerifyingKey +where + D: Digest, +{ + fn as_ref(&self) -> &RsaPublicKey { + &self.inner + } +} + +impl AssociatedAlgorithmIdentifier for VerifyingKey +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 Clone for VerifyingKey +where + D: Digest, +{ + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + salt_len: self.salt_len, + phantom: Default::default(), + } + } +} + +impl EncodePublicKey for VerifyingKey +where + D: Digest, +{ + fn to_public_key_der(&self) -> pkcs8::spki::Result { + self.inner.to_public_key_der() + } +} + +impl From for VerifyingKey +where + D: Digest, +{ + fn from(key: RsaPublicKey) -> Self { + Self::new(key) + } +} + +impl From> for RsaPublicKey +where + D: Digest, +{ + fn from(key: VerifyingKey) -> Self { + key.inner + } +}