diff --git a/Cargo.toml b/Cargo.toml index c834110e..05387e68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rsa" -version = "0.7.2" +version = "0.8.0-pre" authors = ["RustCrypto Developers", "dignifiedquire "] edition = "2021" description = "Pure Rust RSA implementation" @@ -17,13 +17,13 @@ num-bigint = { version = "0.8.1", features = ["i128", "u64_digit", "prime", "zer num-traits = { version= "0.2.9", default-features = false, features = ["libm"] } num-integer = { version = "0.1.39", default-features = false } num-iter = { version = "0.1.37", default-features = false } -rand_core = { version = "0.6", default-features = false } +rand_core = { version = "0.6.4", default-features = false } byteorder = { version = "1.3.1", default-features = false } subtle = { version = "2.1.1", default-features = false } digest = { version = "0.10.5", default-features = false, features = ["alloc", "oid"] } pkcs1 = { version = "0.4", default-features = false, features = ["pkcs8", "alloc"] } pkcs8 = { version = "0.9", default-features = false, features = ["alloc"] } -signature = { version = "1.6.4", default-features = false , features = ["digest-preview", "rand-preview"] } +signature = { version = "2.0.0-pre.2", default-features = false , features = ["digest-preview", "rand-preview"] } zeroize = { version = "1", features = ["alloc"] } # Temporary workaround until https://github.com/dignifiedquire/num-bigint/pull/42 lands @@ -53,7 +53,6 @@ name = "key" [features] default = ["std", "pem"] -hazmat = ["signature/hazmat-preview"] nightly = ["num-bigint/nightly"] serde = ["num-bigint/serde", "serde_crate"] expose-internals = [] diff --git a/src/algorithms.rs b/src/algorithms.rs index bd252e3f..8bdc1d9a 100644 --- a/src/algorithms.rs +++ b/src/algorithms.rs @@ -6,7 +6,7 @@ use num_bigint::{BigUint, RandPrime}; #[allow(unused_imports)] use num_traits::Float; use num_traits::{FromPrimitive, One, Zero}; -use rand_core::{CryptoRng, RngCore}; +use rand_core::CryptoRngCore; use crate::errors::{Error, Result}; use crate::key::RsaPrivateKey; @@ -29,7 +29,7 @@ const EXP: u64 = 65537; /// /// [1]: https://patents.google.com/patent/US4405829A/en /// [2]: https://cacr.uwaterloo.ca/techreports/2006/cacr2006-16.pdf -pub fn generate_multi_prime_key( +pub fn generate_multi_prime_key( rng: &mut R, nprimes: usize, bit_size: usize, @@ -49,7 +49,7 @@ pub fn generate_multi_prime_key( /// /// [1]: https://patents.google.com/patent/US4405829A/en /// [2]: http://www.cacr.math.uwaterloo.ca/techreports/2006/cacr2006-16.pdf -pub fn generate_multi_prime_key_with_exp( +pub fn generate_multi_prime_key_with_exp( rng: &mut R, nprimes: usize, bit_size: usize, diff --git a/src/key.rs b/src/key.rs index 86120dd0..bebdeb8f 100644 --- a/src/key.rs +++ b/src/key.rs @@ -4,7 +4,7 @@ use num_bigint::traits::ModInverse; use num_bigint::Sign::Plus; use num_bigint::{BigInt, BigUint}; use num_traits::{One, ToPrimitive}; -use rand_core::{CryptoRng, RngCore}; +use rand_core::CryptoRngCore; #[cfg(feature = "serde")] use serde_crate::{Deserialize, Serialize}; use zeroize::Zeroize; @@ -173,7 +173,7 @@ impl From<&RsaPrivateKey> for RsaPublicKey { /// Generic trait for operations on a public key. pub trait PublicKey: EncryptionPrimitive + PublicKeyParts { /// Encrypt the given message. - fn encrypt( + fn encrypt( &self, rng: &mut R, padding: PaddingScheme, @@ -198,7 +198,7 @@ impl PublicKeyParts for RsaPublicKey { } impl PublicKey for RsaPublicKey { - fn encrypt( + fn encrypt( &self, rng: &mut R, padding: PaddingScheme, @@ -281,7 +281,7 @@ impl PrivateKey for RsaPrivateKey {} impl RsaPrivateKey { /// Generate a new Rsa key pair of the given bit size using the passed in `rng`. - pub fn new(rng: &mut R, bit_size: usize) -> Result { + pub fn new(rng: &mut R, bit_size: usize) -> Result { generate_multi_prime_key(rng, 2, bit_size) } @@ -289,7 +289,7 @@ impl RsaPrivateKey { /// using the passed in `rng`. /// /// Unless you have specific needs, you should use `RsaPrivateKey::new` instead. - pub fn new_with_exp( + pub fn new_with_exp( rng: &mut R, bit_size: usize, exp: &BigUint, @@ -473,7 +473,7 @@ impl RsaPrivateKey { /// Decrypt the given message. /// /// Uses `rng` to blind the decryption process. - pub fn decrypt_blinded( + pub fn decrypt_blinded( &self, rng: &mut R, padding: PaddingScheme, @@ -516,7 +516,7 @@ impl RsaPrivateKey { /// Sign the given digest using the provided rng /// /// Use `rng` for signature process. - pub fn sign_with_rng( + pub fn sign_with_rng( &self, rng: &mut R, padding: PaddingScheme, @@ -534,7 +534,7 @@ impl RsaPrivateKey { /// Sign the given digest. /// /// Use `rng` for blinding. - pub fn sign_blinded( + pub fn sign_blinded( &self, rng: &mut R, padding: PaddingScheme, diff --git a/src/lib.rs b/src/lib.rs index 30892743..f30e7630 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,7 +59,7 @@ //! ``` //! use rsa::RsaPrivateKey; //! use rsa::pkcs1v15::{SigningKey, VerifyingKey}; -//! use rsa::signature::{RandomizedSigner, Signature, Verifier}; +//! use rsa::signature::{Keypair, RandomizedSigner, SignatureEncoding, Verifier}; //! use sha2::{Digest, Sha256}; //! //! let mut rng = rand::thread_rng(); @@ -67,12 +67,12 @@ //! let bits = 2048; //! let private_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key"); //! let signing_key = SigningKey::::new_with_prefix(private_key); -//! let verifying_key: VerifyingKey<_> = (&signing_key).into(); +//! let verifying_key = signing_key.verifying_key(); //! //! // Sign //! let data = b"hello world"; //! let signature = signing_key.sign_with_rng(&mut rng, data); -//! assert_ne!(signature.as_bytes(), data); +//! assert_ne!(signature.to_bytes().as_ref(), data.as_slice()); //! //! // Verify //! verifying_key.verify(data, &signature).expect("failed to verify"); @@ -82,7 +82,7 @@ //! ``` //! use rsa::RsaPrivateKey; //! use rsa::pss::{BlindedSigningKey, VerifyingKey}; -//! use rsa::signature::{RandomizedSigner, Signature, Verifier}; +//! use rsa::signature::{Keypair,RandomizedSigner, SignatureEncoding, Verifier}; //! use sha2::{Digest, Sha256}; //! //! let mut rng = rand::thread_rng(); @@ -90,12 +90,12 @@ //! let bits = 2048; //! let private_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key"); //! let signing_key = BlindedSigningKey::::new(private_key); -//! let verifying_key: VerifyingKey<_> = (&signing_key).into(); +//! let verifying_key = signing_key.verifying_key(); //! //! // Sign //! let data = b"hello world"; //! let signature = signing_key.sign_with_rng(&mut rng, data); -//! assert_ne!(signature.as_bytes(), data); +//! assert_ne!(signature.to_bytes().as_ref(), data); //! //! // Verify //! verifying_key.verify(data, &signature).expect("failed to verify"); diff --git a/src/pkcs1v15.rs b/src/pkcs1v15.rs index 290e41ab..75883671 100644 --- a/src/pkcs1v15.rs +++ b/src/pkcs1v15.rs @@ -6,18 +6,17 @@ //! //! [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2 +use alloc::boxed::Box; use alloc::vec::Vec; use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex}; use core::marker::PhantomData; -use core::ops::Deref; use digest::Digest; use pkcs8::{AssociatedOid, Document, EncodePrivateKey, EncodePublicKey, SecretDocument}; -use rand_core::{CryptoRng, RngCore}; -#[cfg(feature = "hazmat")] -use signature::hazmat::{PrehashSigner, PrehashVerifier}; +use rand_core::CryptoRngCore; use signature::{ - DigestSigner, DigestVerifier, RandomizedDigestSigner, RandomizedSigner, - Signature as SignSignature, Signer, Verifier, + hazmat::{PrehashSigner, PrehashVerifier}, + DigestSigner, DigestVerifier, Keypair, RandomizedDigestSigner, RandomizedSigner, + SignatureEncoding, Signer, Verifier, }; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use zeroize::Zeroizing; @@ -30,60 +29,52 @@ use crate::{RsaPrivateKey, RsaPublicKey}; /// 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)] +#[derive(Clone, PartialEq, Eq)] pub struct Signature { - bytes: Vec, + bytes: Box<[u8]>, } -impl signature::Signature for Signature { - fn from_bytes(bytes: &[u8]) -> signature::Result { - Ok(Signature { - bytes: bytes.into(), - }) - } - - fn as_bytes(&self) -> &[u8] { - self.bytes.as_slice() - } +impl SignatureEncoding for Signature { + type Repr = Box<[u8]>; } -impl From> for Signature { - fn from(bytes: Vec) -> Self { +impl From> for Signature { + fn from(bytes: Box<[u8]>) -> Self { Self { bytes } } } -impl Deref for Signature { - type Target = [u8]; +impl<'a> TryFrom<&'a [u8]> for Signature { + type Error = signature::Error; - fn deref(&self) -> &Self::Target { - self.as_bytes() + fn try_from(bytes: &'a [u8]) -> signature::Result { + Ok(Self { + bytes: bytes.into(), + }) } } -impl PartialEq for Signature { - fn eq(&self, other: &Self) -> bool { - self.as_bytes() == other.as_bytes() +impl From for Box<[u8]> { + fn from(signature: Signature) -> Box<[u8]> { + signature.bytes } } -impl Eq for Signature {} - -impl Debug for Signature { - fn fmt(&self, fmt: &mut Formatter<'_>) -> core::result::Result<(), core::fmt::Error> { - fmt.debug_list().entries(self.as_bytes().iter()).finish() +impl AsRef<[u8]> for Signature { + fn as_ref(&self) -> &[u8] { + self.bytes.as_ref() } } -impl AsRef<[u8]> for Signature { - fn as_ref(&self) -> &[u8] { - self.as_bytes() +impl Debug for Signature { + fn fmt(&self, fmt: &mut Formatter<'_>) -> core::result::Result<(), core::fmt::Error> { + fmt.debug_list().entries(self.bytes.iter()).finish() } } impl LowerHex for Signature { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - for byte in self.as_bytes() { + for byte in self.bytes.iter() { write!(f, "{:02x}", byte)?; } Ok(()) @@ -92,7 +83,7 @@ impl LowerHex for Signature { impl UpperHex for Signature { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - for byte in self.as_bytes() { + for byte in self.bytes.iter() { write!(f, "{:02X}", byte)?; } Ok(()) @@ -109,7 +100,7 @@ impl Display for Signature { /// scheme from PKCS#1 v1.5. The message must be no longer than the /// length of the public modulus minus 11 bytes. #[inline] -pub(crate) fn encrypt( +pub(crate) fn encrypt( rng: &mut R, pub_key: &PK, msg: &[u8], @@ -141,7 +132,7 @@ pub(crate) fn encrypt( /// forge signatures as if they had the private key. See /// `decrypt_session_key` for a way of solving this problem. #[inline] -pub(crate) fn decrypt( +pub(crate) fn decrypt( rng: Option<&mut R>, priv_key: &SK, ciphertext: &[u8], @@ -170,7 +161,7 @@ pub(crate) fn decrypt( /// messages to signatures and identify the signed messages. As ever, /// signatures provide authenticity, not confidentiality. #[inline] -pub(crate) fn sign( +pub(crate) fn sign( rng: Option<&mut R>, priv_key: &SK, prefix: &[u8], @@ -258,7 +249,7 @@ where /// in order to maintain constant memory access patterns. If the plaintext was /// valid then index contains the index of the original message in em. #[inline] -fn decrypt_inner( +fn decrypt_inner( rng: Option<&mut R>, priv_key: &SK, ciphertext: &[u8], @@ -303,7 +294,7 @@ fn decrypt_inner( /// Fills the provided slice with random values, which are guaranteed /// to not be zero. #[inline] -fn non_zero_random_bytes(rng: &mut R, data: &mut [u8]) { +fn non_zero_random_bytes(rng: &mut R, data: &mut [u8]) { rng.fill_bytes(data); for el in data { @@ -342,12 +333,13 @@ where } } - pub(crate) fn key(&self) -> &RsaPrivateKey { - &self.inner - } - - pub(crate) fn prefix(&self) -> Vec { - self.prefix.clone() + /// Generate a new signing key. + pub fn random(rng: &mut R, bit_size: usize) -> Result { + Ok(Self { + inner: RsaPrivateKey::new(rng, bit_size)?, + prefix: Vec::new(), + phantom: Default::default(), + }) } } @@ -373,7 +365,7 @@ impl SigningKey where D: Digest + AssociatedOid, { - /// Create a new verifying key with a prefix for the digest `D`. + /// Create a new signing key with a prefix for the digest `D`. pub fn new_with_prefix(key: RsaPrivateKey) -> Self { Self { inner: key, @@ -381,6 +373,18 @@ where phantom: Default::default(), } } + + /// Generate a new signing key with a prefix for the digest `D`. + pub fn random_with_prefix( + rng: &mut R, + bit_size: usize, + ) -> Result { + Ok(Self { + inner: RsaPrivateKey::new(rng, bit_size)?, + prefix: generate_prefix::(), + phantom: Default::default(), + }) + } } impl AsRef for SigningKey @@ -407,7 +411,7 @@ where { fn try_sign(&self, msg: &[u8]) -> signature::Result { sign::(None, &self.inner, &self.prefix, &D::digest(msg)) - .map(|v| v.into()) + .map(|v| v.into_boxed_slice().into()) .map_err(|e| e.into()) } } @@ -418,11 +422,11 @@ where { fn try_sign_with_rng( &self, - mut rng: impl CryptoRng + RngCore, + rng: &mut impl CryptoRngCore, msg: &[u8], ) -> signature::Result { - sign(Some(&mut rng), &self.inner, &self.prefix, &D::digest(msg)) - .map(|v| v.into()) + sign(Some(rng), &self.inner, &self.prefix, &D::digest(msg)) + .map(|v| v.into_boxed_slice().into()) .map_err(|e| e.into()) } } @@ -433,7 +437,7 @@ where { fn try_sign_digest(&self, digest: D) -> signature::Result { sign::(None, &self.inner, &self.prefix, &digest.finalize()) - .map(|v| v.into()) + .map(|v| v.into_boxed_slice().into()) .map_err(|e| e.into()) } } @@ -444,28 +448,22 @@ where { fn try_sign_digest_with_rng( &self, - mut rng: impl CryptoRng + RngCore, + rng: &mut impl CryptoRngCore, digest: D, ) -> signature::Result { - sign( - Some(&mut rng), - &self.inner, - &self.prefix, - &digest.finalize(), - ) - .map(|v| v.into()) - .map_err(|e| e.into()) + sign(Some(rng), &self.inner, &self.prefix, &digest.finalize()) + .map(|v| v.into_boxed_slice().into()) + .map_err(|e| e.into()) } } -#[cfg(feature = "hazmat")] impl PrehashSigner for SigningKey where D: Digest, { fn sign_prehash(&self, prehash: &[u8]) -> signature::Result { sign::(None, &self.inner, &self.prefix, prehash) - .map(|v| v.into()) + .map(|v| v.into_boxed_slice().into()) .map_err(|e| e.into()) } } @@ -473,7 +471,7 @@ where /// 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, Clone)] +#[derive(Debug)] pub struct VerifyingKey where D: Digest, @@ -483,6 +481,20 @@ where 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, @@ -538,27 +550,15 @@ where } } -impl From> for VerifyingKey +impl Keypair for SigningKey where D: Digest, { - fn from(key: SigningKey) -> Self { - Self { - inner: key.key().into(), - prefix: key.prefix(), - phantom: Default::default(), - } - } -} - -impl From<&SigningKey> for VerifyingKey -where - D: Digest, -{ - fn from(key: &SigningKey) -> Self { - Self { - inner: key.key().into(), - prefix: key.prefix(), + type VerifyingKey = VerifyingKey; + fn verifying_key(&self) -> Self::VerifyingKey { + VerifyingKey { + inner: self.inner.to_public_key(), + prefix: self.prefix.clone(), phantom: Default::default(), } } @@ -571,7 +571,7 @@ where fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> { verify( &self.inner, - &self.prefix, + &self.prefix.clone(), &D::digest(msg), signature.as_ref(), ) @@ -594,7 +594,6 @@ where } } -#[cfg(feature = "hazmat")] impl PrehashVerifier for VerifyingKey where D: Digest, @@ -621,11 +620,14 @@ mod tests { use num_bigint::BigUint; use num_traits::FromPrimitive; use num_traits::Num; - use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; + use rand_chacha::{ + rand_core::{RngCore, SeedableRng}, + ChaCha8Rng, + }; use sha1::{Digest, Sha1}; use sha2::Sha256; use sha3::Sha3_256; - use signature::{RandomizedSigner, Signature, Signer, Verifier}; + use signature::{RandomizedSigner, Signer, Verifier}; use crate::{PaddingScheme, PublicKey, PublicKeyParts, RsaPrivateKey, RsaPublicKey}; @@ -919,8 +921,10 @@ mod tests { let verifying_key = VerifyingKey::::new_with_prefix(pub_key); for (text, sig, expected) in &tests { - let result = - verifying_key.verify(text.as_bytes(), &Signature::from_bytes(sig).unwrap()); + let result = verifying_key.verify( + text.as_bytes(), + &Signature::try_from(sig.as_slice()).unwrap(), + ); match expected { true => result.expect("failed to verify"), false => { @@ -958,7 +962,8 @@ mod tests { for (text, sig, expected) in &tests { let mut digest = Sha1::new(); digest.update(text.as_bytes()); - let result = verifying_key.verify_digest(digest, &Signature::from_bytes(sig).unwrap()); + let result = + verifying_key.verify_digest(digest, &Signature::try_from(sig.as_slice()).unwrap()); match expected { true => result.expect("failed to verify"), false => { @@ -984,7 +989,6 @@ mod tests { .expect("failed to verify"); } - #[cfg(feature = "hazmat")] #[test] fn test_unpadded_signature_hazmat() { let msg = b"Thu Dec 19 18:06:16 EST 2013\n"; @@ -995,9 +999,12 @@ mod tests { let sig = signing_key.sign_prehash(msg).expect("Failure during sign"); assert_eq!(sig.as_ref(), expected_sig); - let verifying_key: VerifyingKey<_> = (&signing_key).into(); + let verifying_key = signing_key.verifying_key(); verifying_key - .verify_prehash(msg, &Signature::from_bytes(&expected_sig).unwrap()) + .verify_prehash( + msg, + &Signature::try_from(expected_sig.into_boxed_slice()).unwrap(), + ) .expect("failed to verify"); } } diff --git a/src/pss.rs b/src/pss.rs index 0855a8c2..0e321d6f 100644 --- a/src/pss.rs +++ b/src/pss.rs @@ -9,18 +9,17 @@ //! [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; use alloc::vec::Vec; use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex}; use core::marker::PhantomData; -use core::ops::Deref; use digest::{Digest, DynDigest, FixedOutputReset}; use pkcs8::{Document, EncodePrivateKey, EncodePublicKey, SecretDocument}; -use rand_core::{CryptoRng, RngCore}; -#[cfg(feature = "hazmat")] -use signature::hazmat::{PrehashVerifier, RandomizedPrehashSigner}; +use rand_core::CryptoRngCore; use signature::{ - DigestVerifier, RandomizedDigestSigner, RandomizedSigner, Signature as SignSignature, Verifier, + hazmat::{PrehashVerifier, RandomizedPrehashSigner}, + DigestVerifier, Keypair, RandomizedDigestSigner, RandomizedSigner, SignatureEncoding, Verifier, }; use subtle::ConstantTimeEq; @@ -32,60 +31,52 @@ use crate::{RsaPrivateKey, RsaPublicKey}; /// RSASSA-PSS signatures as described in [RFC8017 § 8.1]. /// /// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1 -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct Signature { - bytes: Vec, + bytes: Box<[u8]>, } -impl signature::Signature for Signature { - fn from_bytes(bytes: &[u8]) -> signature::Result { - Ok(Signature { - bytes: bytes.into(), - }) - } - - fn as_bytes(&self) -> &[u8] { - self.bytes.as_slice() - } +impl SignatureEncoding for Signature { + type Repr = Box<[u8]>; } -impl From> for Signature { - fn from(bytes: Vec) -> Self { +impl From> for Signature { + fn from(bytes: Box<[u8]>) -> Self { Self { bytes } } } -impl Deref for Signature { - type Target = [u8]; +impl<'a> TryFrom<&'a [u8]> for Signature { + type Error = signature::Error; - fn deref(&self) -> &Self::Target { - self.as_bytes() + fn try_from(bytes: &'a [u8]) -> signature::Result { + Ok(Self { + bytes: bytes.into(), + }) } } -impl PartialEq for Signature { - fn eq(&self, other: &Self) -> bool { - self.as_bytes() == other.as_bytes() +impl From for Box<[u8]> { + fn from(signature: Signature) -> Box<[u8]> { + signature.bytes } } -impl Eq for Signature {} - impl Debug for Signature { fn fmt(&self, fmt: &mut Formatter<'_>) -> core::result::Result<(), core::fmt::Error> { - fmt.debug_list().entries(self.as_bytes().iter()).finish() + fmt.debug_list().entries(self.bytes.iter()).finish() } } impl AsRef<[u8]> for Signature { fn as_ref(&self) -> &[u8] { - self.as_bytes() + self.bytes.as_ref() } } impl LowerHex for Signature { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - for byte in self.as_bytes() { + for byte in self.bytes.iter() { write!(f, "{:02x}", byte)?; } Ok(()) @@ -94,7 +85,7 @@ impl LowerHex for Signature { impl UpperHex for Signature { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - for byte in self.as_bytes() { + for byte in self.bytes.iter() { write!(f, "{:02X}", byte)?; } Ok(()) @@ -146,7 +137,7 @@ where /// given hash function. The opts argument may be nil, in which case sensible /// defaults are used. // TODO: bind T with the CryptoRng trait -pub(crate) fn sign( +pub(crate) fn sign( rng: &mut T, blind: bool, priv_key: &SK, @@ -159,7 +150,7 @@ pub(crate) fn sign( sign_pss_with_salt(blind.then(|| rng), priv_key, hashed, &salt, digest) } -pub(crate) fn sign_digest( +pub(crate) fn sign_digest( rng: &mut T, blind: bool, priv_key: &SK, @@ -171,7 +162,7 @@ pub(crate) fn sign_digest(blind.then(|| rng), priv_key, hashed, &salt) } -fn generate_salt( +fn generate_salt( rng: &mut T, priv_key: &SK, salt_len: Option, @@ -190,7 +181,7 @@ fn generate_salt( /// Note that hashed must be the result of hashing the input message using the /// given hash function. salt is a random sequence of bytes whose length will be /// later used to verify the signature. -fn sign_pss_with_salt( +fn sign_pss_with_salt( blind_rng: Option<&mut T>, priv_key: &SK, hashed: &[u8], @@ -203,11 +194,7 @@ fn sign_pss_with_salt( priv_key.raw_decryption_primitive(blind_rng, &em, priv_key.size()) } -fn sign_pss_with_salt_digest< - T: CryptoRng + RngCore, - SK: PrivateKey, - D: Digest + FixedOutputReset, ->( +fn sign_pss_with_salt_digest( blind_rng: Option<&mut T>, priv_key: &SK, hashed: &[u8], @@ -572,8 +559,26 @@ where } } - pub(crate) fn key(&self) -> &RsaPrivateKey { - &self.inner + /// Generate a new random RSASSA-PSS signing key. + pub fn random(rng: &mut R, bit_size: usize) -> Result { + Ok(Self { + inner: RsaPrivateKey::new(rng, bit_size)?, + salt_len: None, + phantom: Default::default(), + }) + } + + /// 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: Some(salt_len), + phantom: Default::default(), + }) } } @@ -604,17 +609,30 @@ where } } +impl Keypair for SigningKey +where + D: Digest, +{ + type VerifyingKey = VerifyingKey; + fn verifying_key(&self) -> Self::VerifyingKey { + VerifyingKey { + inner: self.inner.to_public_key(), + phantom: Default::default(), + } + } +} + impl RandomizedSigner for SigningKey where D: Digest + FixedOutputReset, { fn try_sign_with_rng( &self, - mut rng: impl CryptoRng + RngCore, + rng: &mut impl CryptoRngCore, msg: &[u8], ) -> signature::Result { - sign_digest::<_, _, D>(&mut rng, false, &self.inner, &D::digest(msg), self.salt_len) - .map(|v| v.into()) + sign_digest::<_, _, D>(rng, false, &self.inner, &D::digest(msg), self.salt_len) + .map(|v| v.into_boxed_slice().into()) .map_err(|e| e.into()) } } @@ -625,33 +643,26 @@ where { fn try_sign_digest_with_rng( &self, - mut rng: impl CryptoRng + RngCore, + rng: &mut impl CryptoRngCore, digest: D, ) -> signature::Result { - sign_digest::<_, _, D>( - &mut rng, - false, - &self.inner, - &digest.finalize(), - self.salt_len, - ) - .map(|v| v.into()) - .map_err(|e| e.into()) + sign_digest::<_, _, D>(rng, false, &self.inner, &digest.finalize(), self.salt_len) + .map(|v| v.into_boxed_slice().into()) + .map_err(|e| e.into()) } } -#[cfg(feature = "hazmat")] impl RandomizedPrehashSigner for SigningKey where D: Digest + FixedOutputReset, { fn sign_prehash_with_rng( &self, - mut rng: impl CryptoRng + RngCore, + rng: &mut impl CryptoRngCore, prehash: &[u8], ) -> signature::Result { - sign_digest::<_, _, D>(&mut rng, false, &self.inner, prehash, self.salt_len) - .map(|v| v.into()) + sign_digest::<_, _, D>(rng, false, &self.inner, prehash, self.salt_len) + .map(|v| v.into_boxed_slice().into()) .map_err(|e| e.into()) } } @@ -700,10 +711,6 @@ where phantom: Default::default(), } } - - pub(crate) fn key(&self) -> &RsaPrivateKey { - &self.inner - } } impl From for BlindedSigningKey @@ -733,17 +740,30 @@ where } } +impl Keypair for BlindedSigningKey +where + D: Digest, +{ + type VerifyingKey = VerifyingKey; + fn verifying_key(&self) -> Self::VerifyingKey { + VerifyingKey { + inner: self.inner.to_public_key(), + phantom: Default::default(), + } + } +} + impl RandomizedSigner for BlindedSigningKey where D: Digest + FixedOutputReset, { fn try_sign_with_rng( &self, - mut rng: impl CryptoRng + RngCore, + rng: &mut impl CryptoRngCore, msg: &[u8], ) -> signature::Result { - sign_digest::<_, _, D>(&mut rng, true, &self.inner, &D::digest(msg), self.salt_len) - .map(|v| v.into()) + sign_digest::<_, _, D>(rng, true, &self.inner, &D::digest(msg), self.salt_len) + .map(|v| v.into_boxed_slice().into()) .map_err(|e| e.into()) } } @@ -754,33 +774,26 @@ where { fn try_sign_digest_with_rng( &self, - mut rng: impl CryptoRng + RngCore, + rng: &mut impl CryptoRngCore, digest: D, ) -> signature::Result { - sign_digest::<_, _, D>( - &mut rng, - true, - &self.inner, - &digest.finalize(), - self.salt_len, - ) - .map(|v| v.into()) - .map_err(|e| e.into()) + sign_digest::<_, _, D>(rng, true, &self.inner, &digest.finalize(), self.salt_len) + .map(|v| v.into_boxed_slice().into()) + .map_err(|e| e.into()) } } -#[cfg(feature = "hazmat")] impl RandomizedPrehashSigner for BlindedSigningKey where D: Digest + FixedOutputReset, { fn sign_prehash_with_rng( &self, - mut rng: impl CryptoRng + RngCore, + rng: &mut impl CryptoRngCore, prehash: &[u8], ) -> signature::Result { - sign_digest::<_, _, D>(&mut rng, true, &self.inner, prehash, self.salt_len) - .map(|v| v.into()) + sign_digest::<_, _, D>(rng, true, &self.inner, prehash, self.salt_len) + .map(|v| v.into_boxed_slice().into()) .map_err(|e| e.into()) } } @@ -798,7 +811,7 @@ where /// described in [RFC8017 § 8.1]. /// /// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1 -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct VerifyingKey where D: Digest, @@ -807,6 +820,19 @@ where 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(), + phantom: Default::default(), + } + } +} + impl VerifyingKey where D: Digest, @@ -838,54 +864,6 @@ where } } -impl From> for VerifyingKey -where - D: Digest, -{ - fn from(key: SigningKey) -> Self { - Self { - inner: key.key().into(), - phantom: Default::default(), - } - } -} - -impl From<&SigningKey> for VerifyingKey -where - D: Digest, -{ - fn from(key: &SigningKey) -> Self { - Self { - inner: key.key().into(), - phantom: Default::default(), - } - } -} - -impl From> for VerifyingKey -where - D: Digest, -{ - fn from(key: BlindedSigningKey) -> Self { - Self { - inner: key.key().into(), - phantom: Default::default(), - } - } -} - -impl From<&BlindedSigningKey> for VerifyingKey -where - D: Digest, -{ - fn from(key: &BlindedSigningKey) -> Self { - Self { - inner: key.key().into(), - phantom: Default::default(), - } - } -} - impl Verifier for VerifyingKey where D: Digest + FixedOutputReset, @@ -906,7 +884,6 @@ where } } -#[cfg(feature = "hazmat")] impl PrehashVerifier for VerifyingKey where D: Digest + FixedOutputReset, @@ -936,7 +913,7 @@ where #[cfg(test)] mod test { - use crate::pss::{BlindedSigningKey, SigningKey, VerifyingKey}; + use crate::pss::{BlindedSigningKey, Signature, SigningKey, VerifyingKey}; use crate::{PaddingScheme, PublicKey, RsaPrivateKey, RsaPublicKey}; use hex_literal::hex; @@ -944,11 +921,8 @@ mod test { use num_traits::{FromPrimitive, Num}; use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; use sha1::{Digest, Sha1}; - #[cfg(feature = "hazmat")] use signature::hazmat::{PrehashVerifier, RandomizedPrehashSigner}; - use signature::{ - DigestVerifier, RandomizedDigestSigner, RandomizedSigner, Signature, Verifier, - }; + use signature::{DigestVerifier, Keypair, RandomizedDigestSigner, RandomizedSigner, Verifier}; fn get_private_key() -> RsaPrivateKey { // In order to generate new test vectors you'll need the PEM form of this key: @@ -1035,8 +1009,10 @@ mod test { let verifying_key: VerifyingKey = VerifyingKey::new(pub_key); for (text, sig, expected) in &tests { - let result = - verifying_key.verify(text.as_bytes(), &Signature::from_bytes(sig).unwrap()); + let result = verifying_key.verify( + text.as_bytes(), + &Signature::try_from(sig.as_slice()).unwrap(), + ); match expected { true => result.expect("failed to verify"), false => { @@ -1074,7 +1050,8 @@ mod test { for (text, sig, expected) in &tests { let mut digest = Sha1::new(); digest.update(text.as_bytes()); - let result = verifying_key.verify_digest(digest, &Signature::from_bytes(sig).unwrap()); + let result = + verifying_key.verify_digest(digest, &Signature::try_from(sig.as_slice()).unwrap()); match expected { true => result.expect("failed to verify"), false => { @@ -1129,7 +1106,7 @@ mod test { let tests = ["test\n"]; let mut rng = ChaCha8Rng::from_seed([42; 32]); let signing_key = SigningKey::::new(priv_key); - let verifying_key = VerifyingKey::from(&signing_key); + let verifying_key = signing_key.verifying_key(); for test in &tests { let sig = signing_key.sign_with_rng(&mut rng, test.as_bytes()); @@ -1146,7 +1123,7 @@ mod test { let tests = ["test\n"]; let mut rng = ChaCha8Rng::from_seed([42; 32]); let signing_key = BlindedSigningKey::::new(priv_key); - let verifying_key = VerifyingKey::from(&signing_key); + let verifying_key = signing_key.verifying_key(); for test in &tests { let sig = signing_key.sign_with_rng(&mut rng, test.as_bytes()); @@ -1163,7 +1140,7 @@ mod test { let tests = ["test\n"]; let mut rng = ChaCha8Rng::from_seed([42; 32]); let signing_key = SigningKey::new(priv_key); - let verifying_key = VerifyingKey::from(&signing_key); + let verifying_key = signing_key.verifying_key(); for test in &tests { let mut digest = Sha1::new(); @@ -1185,7 +1162,7 @@ mod test { let tests = ["test\n"]; let mut rng = ChaCha8Rng::from_seed([42; 32]); let signing_key = BlindedSigningKey::::new(priv_key); - let verifying_key = VerifyingKey::from(&signing_key); + let verifying_key = signing_key.verifying_key(); for test in &tests { let mut digest = Sha1::new(); @@ -1200,7 +1177,6 @@ mod test { } } - #[cfg(feature = "hazmat")] #[test] fn test_verify_pss_hazmat() { let priv_key = get_private_key(); @@ -1227,8 +1203,8 @@ mod test { let verifying_key = VerifyingKey::::new(pub_key); for (text, sig, expected) in &tests { - let result = - verifying_key.verify_prehash(text.as_ref(), &Signature::from_bytes(sig).unwrap()); + let result = verifying_key + .verify_prehash(text.as_ref(), &Signature::try_from(sig.as_slice()).unwrap()); match expected { true => result.expect("failed to verify"), false => { @@ -1238,7 +1214,6 @@ mod test { } } - #[cfg(feature = "hazmat")] #[test] fn test_sign_and_verify_pss_hazmat() { let priv_key = get_private_key(); @@ -1246,7 +1221,7 @@ mod test { let tests = [Sha1::digest("test\n")]; let mut rng = ChaCha8Rng::from_seed([42; 32]); let signing_key = SigningKey::::new(priv_key); - let verifying_key = VerifyingKey::from(&signing_key); + let verifying_key = signing_key.verifying_key(); for test in &tests { let sig = signing_key @@ -1258,7 +1233,6 @@ mod test { } } - #[cfg(feature = "hazmat")] #[test] fn test_sign_and_verify_pss_blinded_hazmat() { let priv_key = get_private_key(); @@ -1266,7 +1240,7 @@ mod test { let tests = [Sha1::digest("test\n")]; let mut rng = ChaCha8Rng::from_seed([42; 32]); let signing_key = BlindedSigningKey::::new(priv_key); - let verifying_key = VerifyingKey::from(&signing_key); + let verifying_key = signing_key.verifying_key(); for test in &tests { let sig = signing_key