diff --git a/benches/key.rs b/benches/key.rs index 26c6616a..ece0887a 100644 --- a/benches/key.rs +++ b/benches/key.rs @@ -6,6 +6,7 @@ use base64ct::{Base64, Encoding}; use num_bigint::BigUint; use num_traits::{FromPrimitive, Num}; use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; +use rsa::signature::RandomizedSigner; use rsa::{PaddingScheme, RsaPrivateKey}; use sha2::{Digest, Sha256}; use test::Bencher; @@ -41,17 +42,25 @@ fn bench_rsa_2048_pkcsv1_decrypt(b: &mut Bencher) { #[bench] fn bench_rsa_2048_pkcsv1_sign_blinded(b: &mut Bencher) { let priv_key = get_key(); + let signing_key = rsa::pkcs1v15::SigningKey::::new_with_prefix(priv_key); let digest = Sha256::digest(b"testing").to_vec(); let mut rng = ChaCha8Rng::from_seed([42; 32]); b.iter(|| { - let res = priv_key - .sign_blinded( - &mut rng, - PaddingScheme::new_pkcs1v15_sign::(), - &digest, - ) - .unwrap(); + let res = signing_key.sign_with_rng(&mut rng, &digest); + test::black_box(res); + }); +} + +#[bench] +fn bench_rsa_2048_pss_sign_blinded(b: &mut Bencher) { + let priv_key = get_key(); + let signing_key = rsa::pss::SigningKey::::new(priv_key); + let digest = Sha256::digest(b"testing").to_vec(); + let mut rng = ChaCha8Rng::from_seed([42; 32]); + + b.iter(|| { + let res = signing_key.sign_with_rng(&mut rng, &digest); test::black_box(res); }); } diff --git a/src/key.rs b/src/key.rs index bebdeb8f..df706d99 100644 --- a/src/key.rs +++ b/src/key.rs @@ -15,7 +15,7 @@ use crate::errors::{Error, Result}; use crate::padding::PaddingScheme; use crate::raw::{DecryptionPrimitive, EncryptionPrimitive}; -use crate::{oaep, pkcs1v15, pss}; +use crate::{oaep, pkcs1v15}; /// Components of an RSA public key. pub trait PublicKeyParts { @@ -179,12 +179,6 @@ pub trait PublicKey: EncryptionPrimitive + PublicKeyParts { padding: PaddingScheme, msg: &[u8], ) -> Result>; - - /// Verify a signed message. - /// `hashed`must be the result of hashing the input using the hashing function - /// passed in through `hash`. - /// If the message is valid `Ok(())` is returned, otherwiese an `Err` indicating failure. - fn verify(&self, padding: PaddingScheme, hashed: &[u8], sig: &[u8]) -> Result<()>; } impl PublicKeyParts for RsaPublicKey { @@ -211,22 +205,6 @@ impl PublicKey for RsaPublicKey { mut mgf_digest, label, } => oaep::encrypt(rng, self, msg, &mut *digest, &mut *mgf_digest, label), - _ => Err(Error::InvalidPaddingScheme), - } - } - - fn verify(&self, padding: PaddingScheme, hashed: &[u8], sig: &[u8]) -> Result<()> { - match padding { - PaddingScheme::PKCS1v15Sign { hash_len, prefix } => { - if let Some(hash_len) = hash_len { - if hashed.len() != hash_len { - return Err(Error::InputNotHashed); - } - } - pkcs1v15::verify(self, prefix.as_ref(), hashed, sig) - } - PaddingScheme::PSS { mut digest, .. } => pss::verify(self, hashed, sig, &mut *digest), - _ => Err(Error::InvalidPaddingScheme), } } } @@ -466,7 +444,6 @@ impl RsaPrivateKey { &mut *mgf_digest, label, ), - _ => Err(Error::InvalidPaddingScheme), } } @@ -493,67 +470,6 @@ impl RsaPrivateKey { &mut *mgf_digest, label, ), - _ => Err(Error::InvalidPaddingScheme), - } - } - - /// Sign the given digest. - pub fn sign(&self, padding: PaddingScheme, digest_in: &[u8]) -> Result> { - match padding { - // need to pass any Rng as the type arg, so the type checker is happy, it is not actually used for anything - PaddingScheme::PKCS1v15Sign { hash_len, prefix } => { - if let Some(hash_len) = hash_len { - if digest_in.len() != hash_len { - return Err(Error::InputNotHashed); - } - } - pkcs1v15::sign::(None, self, prefix.as_ref(), digest_in) - } - _ => Err(Error::InvalidPaddingScheme), - } - } - - /// Sign the given digest using the provided rng - /// - /// Use `rng` for signature process. - pub fn sign_with_rng( - &self, - rng: &mut R, - padding: PaddingScheme, - digest_in: &[u8], - ) -> Result> { - match padding { - PaddingScheme::PSS { - mut digest, - salt_len, - } => pss::sign::(rng, false, self, digest_in, salt_len, &mut *digest), - _ => Err(Error::InvalidPaddingScheme), - } - } - - /// Sign the given digest. - /// - /// Use `rng` for blinding. - pub fn sign_blinded( - &self, - rng: &mut R, - padding: PaddingScheme, - digest_in: &[u8], - ) -> Result> { - match padding { - PaddingScheme::PKCS1v15Sign { hash_len, prefix } => { - if let Some(hash_len) = hash_len { - if digest_in.len() != hash_len { - return Err(Error::InputNotHashed); - } - } - pkcs1v15::sign(Some(rng), self, prefix.as_ref(), digest_in) - } - PaddingScheme::PSS { - mut digest, - salt_len, - } => pss::sign::(rng, true, self, digest_in, salt_len, &mut *digest), - _ => Err(Error::InvalidPaddingScheme), } } } diff --git a/src/padding.rs b/src/padding.rs index f2545143..7d632570 100644 --- a/src/padding.rs +++ b/src/padding.rs @@ -5,24 +5,12 @@ use alloc::string::{String, ToString}; use core::fmt; use digest::{Digest, DynDigest}; -use pkcs8::AssociatedOid; - -use crate::pkcs1v15; /// Available padding schemes. pub enum PaddingScheme { /// Encryption and Decryption using PKCS1v15 padding. PKCS1v15Encrypt, - /// Sign and Verify using PKCS1v15 padding. - PKCS1v15Sign { - /// Length of hash to use. - hash_len: Option, - - /// Prefix. - prefix: Box<[u8]>, - }, - /// Encryption and Decryption using [OAEP padding](https://datatracker.ietf.org/doc/html/rfc8017#section-7.1). /// /// - `digest` is used to hash the label. The maximum possible plaintext length is `m = k - 2 * h_len - 2`, @@ -44,32 +32,16 @@ pub enum PaddingScheme { /// Optional label. label: Option, }, - - /// Sign and Verify using PSS padding. - PSS { - /// Digest type to use. - digest: Box, - - /// Salt length. - salt_len: Option, - }, } impl fmt::Debug for PaddingScheme { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { PaddingScheme::PKCS1v15Encrypt => write!(f, "PaddingScheme::PKCS1v15Encrypt"), - PaddingScheme::PKCS1v15Sign { prefix, .. } => { - write!(f, "PaddingScheme::PKCS1v15Sign({:?})", prefix) - } PaddingScheme::OAEP { ref label, .. } => { // TODO: How to print the digest name? write!(f, "PaddingScheme::OAEP({:?})", label) } - PaddingScheme::PSS { ref salt_len, .. } => { - // TODO: How to print the digest name? - write!(f, "PaddingScheme::PSS(salt_len: {:?})", salt_len) - } } } } @@ -80,30 +52,6 @@ impl PaddingScheme { PaddingScheme::PKCS1v15Encrypt } - /// Create new PKCS#1 v1.5 padding for computing a raw signature. - /// - /// This sets `hash_len` to `None` and uses an empty `prefix`. - pub fn new_pkcs1v15_sign_raw() -> Self { - PaddingScheme::PKCS1v15Sign { - hash_len: None, - prefix: Box::new([]), - } - } - - /// Create new PKCS#1 v1.5 padding for the given digest. - /// - /// The digest must have an [`AssociatedOid`]. Make sure to enable the `oid` - /// feature of the relevant digest crate. - pub fn new_pkcs1v15_sign() -> Self - where - D: Digest + AssociatedOid, - { - PaddingScheme::PKCS1v15Sign { - hash_len: Some(::output_size()), - prefix: pkcs1v15::generate_prefix::().into_boxed_slice(), - } - } - /// Create a new OAEP `PaddingScheme`, using `T` as the hash function for the default (empty) label, and `U` as the hash function for MGF1. /// If a label is needed use `PaddingScheme::new_oaep_with_label` or `PaddingScheme::new_oaep_with_mgf_hash_with_label`. /// @@ -183,20 +131,4 @@ impl PaddingScheme { label: Some(label.as_ref().to_string()), } } - - /// New PSS padding for the given digest. - pub fn new_pss() -> Self { - PaddingScheme::PSS { - digest: Box::new(T::new()), - salt_len: None, - } - } - - /// New PSS padding for the given digest with a salt value of the given length. - pub fn new_pss_with_salt(len: usize) -> Self { - PaddingScheme::PSS { - digest: Box::new(T::new()), - salt_len: Some(len), - } - } } diff --git a/src/pkcs1v15.rs b/src/pkcs1v15.rs index e8fe3d78..3d3baee6 100644 --- a/src/pkcs1v15.rs +++ b/src/pkcs1v15.rs @@ -629,7 +629,7 @@ mod tests { use sha3::Sha3_256; use signature::{RandomizedSigner, Signer, Verifier}; - use crate::{PaddingScheme, PublicKey, PublicKeyParts, RsaPrivateKey, RsaPublicKey}; + use crate::{PaddingScheme, PublicKeyParts, RsaPrivateKey, RsaPublicKey}; #[test] fn test_non_zero_bytes() { @@ -719,39 +719,6 @@ mod tests { } } - #[test] - fn test_sign_pkcs1v15() { - let priv_key = get_private_key(); - - let tests = [( - "Test.\n", - hex!( - "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" - "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae" - ), - )]; - - for (text, expected) in &tests { - let digest = Sha1::digest(text.as_bytes()).to_vec(); - - let out = priv_key - .sign(PaddingScheme::new_pkcs1v15_sign::(), &digest) - .unwrap(); - assert_ne!(out, digest); - assert_eq!(out, expected); - - let mut rng = ChaCha8Rng::from_seed([42; 32]); - let out2 = priv_key - .sign_blinded( - &mut rng, - PaddingScheme::new_pkcs1v15_sign::(), - &digest, - ) - .unwrap(); - assert_eq!(out2, expected); - } - } - #[test] fn test_sign_pkcs1v15_signer() { let priv_key = get_private_key(); @@ -858,43 +825,6 @@ mod tests { } } - #[test] - fn test_verify_pkcs1v15() { - let priv_key = get_private_key(); - - let tests = [ - ( - "Test.\n", - hex!( - "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" - "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae" - ), - true, - ), - ( - "Test.\n", - hex!( - "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" - "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362af" - ), - false, - ), - ]; - let pub_key: RsaPublicKey = priv_key.into(); - - for (text, sig, expected) in &tests { - let digest = Sha1::digest(text.as_bytes()).to_vec(); - - let result = pub_key.verify(PaddingScheme::new_pkcs1v15_sign::(), &digest, sig); - match expected { - true => result.expect("failed to verify"), - false => { - result.expect_err("expected verifying error"); - } - } - } - } - #[test] fn test_verify_pkcs1v15_signer() { let priv_key = get_private_key(); @@ -972,22 +902,6 @@ mod tests { } } } - #[test] - fn test_unpadded_signature() { - let msg = b"Thu Dec 19 18:06:16 EST 2013\n"; - let expected_sig = Base64::decode_vec("pX4DR8azytjdQ1rtUiC040FjkepuQut5q2ZFX1pTjBrOVKNjgsCDyiJDGZTCNoh9qpXYbhl7iEym30BWWwuiZg==").unwrap(); - let priv_key = get_private_key(); - - let sig = priv_key - .sign(PaddingScheme::new_pkcs1v15_sign_raw(), msg) - .unwrap(); - assert_eq!(expected_sig, sig); - - let pub_key: RsaPublicKey = priv_key.into(); - pub_key - .verify(PaddingScheme::new_pkcs1v15_sign_raw(), msg, &sig) - .expect("failed to verify"); - } #[test] fn test_unpadded_signature_hazmat() { diff --git a/src/pss.rs b/src/pss.rs index 104c5e0b..f9a54243 100644 --- a/src/pss.rs +++ b/src/pss.rs @@ -14,7 +14,7 @@ use alloc::vec::Vec; use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex}; use core::marker::PhantomData; -use digest::{Digest, DynDigest, FixedOutputReset}; +use digest::{Digest, FixedOutputReset}; use pkcs8::{Document, EncodePrivateKey, EncodePublicKey, SecretDocument}; use rand_core::CryptoRngCore; use signature::{ @@ -23,7 +23,7 @@ use signature::{ }; use subtle::ConstantTimeEq; -use crate::algorithms::{mgf1_xor, mgf1_xor_digest}; +use crate::algorithms::mgf1_xor_digest; use crate::errors::{Error, Result}; use crate::key::{PrivateKey, PublicKey}; use crate::{RsaPrivateKey, RsaPublicKey}; @@ -98,23 +98,6 @@ impl Display for Signature { } } -pub(crate) fn verify( - pub_key: &PK, - hashed: &[u8], - sig: &[u8], - digest: &mut dyn DynDigest, -) -> Result<()> { - if sig.len() != pub_key.size() { - return Err(Error::Verification); - } - - let em_bits = pub_key.n().bits() - 1; - let em_len = (em_bits + 7) / 8; - let mut em = pub_key.raw_encryption_primitive(sig, em_len)?; - - emsa_pss_verify(hashed, &mut em, em_bits, None, digest) -} - pub(crate) fn verify_digest(pub_key: &PK, hashed: &[u8], sig: &[u8]) -> Result<()> where PK: PublicKey, @@ -131,24 +114,6 @@ where emsa_pss_verify_digest::(hashed, &mut em, em_bits, None) } -/// SignPSS calculates the signature of hashed using RSASSA-PSS. -/// -/// Note that hashed must be the result of hashing the input message using the -/// given hash function. The opts argument may be nil, in which case sensible -/// defaults are used. -pub(crate) fn sign( - rng: &mut T, - blind: bool, - priv_key: &SK, - hashed: &[u8], - salt_len: Option, - digest: &mut dyn DynDigest, -) -> Result> { - let salt = generate_salt(rng, priv_key, salt_len, digest.output_size()); - - sign_pss_with_salt(blind.then(|| rng), priv_key, hashed, &salt, digest) -} - pub(crate) fn sign_digest< T: CryptoRngCore + ?Sized, SK: PrivateKey, @@ -184,19 +149,6 @@ 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( - blind_rng: Option<&mut T>, - priv_key: &SK, - hashed: &[u8], - salt: &[u8], - digest: &mut dyn DynDigest, -) -> Result> { - let em_bits = priv_key.n().bits() - 1; - let em = emsa_pss_encode(hashed, em_bits, salt, digest)?; - - priv_key.raw_decryption_primitive(blind_rng, &em, priv_key.size()) -} - fn sign_pss_with_salt_digest< T: CryptoRngCore + ?Sized, SK: PrivateKey, @@ -213,79 +165,6 @@ fn sign_pss_with_salt_digest< priv_key.raw_decryption_primitive(blind_rng, &em, priv_key.size()) } -fn emsa_pss_encode( - m_hash: &[u8], - em_bits: usize, - salt: &[u8], - hash: &mut dyn DynDigest, -) -> Result> { - // See [1], section 9.1.1 - let h_len = hash.output_size(); - let s_len = salt.len(); - let em_len = (em_bits + 7) / 8; - - // 1. If the length of M is greater than the input limitation for the - // hash function (2^61 - 1 octets for SHA-1), output "message too - // long" and stop. - // - // 2. Let mHash = Hash(M), an octet string of length hLen. - if m_hash.len() != h_len { - return Err(Error::InputNotHashed); - } - - // 3. If em_len < h_len + s_len + 2, output "encoding error" and stop. - if em_len < h_len + s_len + 2 { - // TODO: Key size too small - return Err(Error::Internal); - } - - let mut em = vec![0; em_len]; - - let (db, h) = em.split_at_mut(em_len - h_len - 1); - let h = &mut h[..(em_len - 1) - db.len()]; - - // 4. Generate a random octet string salt of length s_len; if s_len = 0, - // then salt is the empty string. - // - // 5. Let - // M' = (0x)00 00 00 00 00 00 00 00 || m_hash || salt; - // - // M' is an octet string of length 8 + h_len + s_len with eight - // initial zero octets. - // - // 6. Let H = Hash(M'), an octet string of length h_len. - let prefix = [0u8; 8]; - - hash.update(&prefix); - hash.update(m_hash); - hash.update(salt); - - let hashed = hash.finalize_reset(); - h.copy_from_slice(&hashed); - - // 7. Generate an octet string PS consisting of em_len - s_len - h_len - 2 - // zero octets. The length of PS may be 0. - // - // 8. Let DB = PS || 0x01 || salt; DB is an octet string of length - // emLen - hLen - 1. - db[em_len - s_len - h_len - 2] = 0x01; - db[em_len - s_len - h_len - 1..].copy_from_slice(salt); - - // 9. Let dbMask = MGF(H, emLen - hLen - 1). - // - // 10. Let maskedDB = DB \xor dbMask. - mgf1_xor(db, hash, h); - - // 11. Set the leftmost 8 * em_len - em_bits bits of the leftmost octet in - // maskedDB to zero. - db[0] &= 0xFF >> (8 * em_len - em_bits); - - // 12. Let EM = maskedDB || H || 0xbc. - em[em_len - 1] = 0xBC; - - Ok(em) -} - fn emsa_pss_encode_digest(m_hash: &[u8], em_bits: usize, salt: &[u8]) -> Result> where D: Digest + FixedOutputReset, @@ -438,50 +317,6 @@ fn emsa_pss_get_salt( Ok(salt) } -fn emsa_pss_verify( - m_hash: &[u8], - em: &mut [u8], - em_bits: usize, - s_len: Option, - hash: &mut dyn DynDigest, -) -> Result<()> { - let em_len = em.len(); //(em_bits + 7) / 8; - let h_len = hash.output_size(); - - let (db, h) = emsa_pss_verify_pre(m_hash, em, em_bits, s_len, h_len)?; - - // 7. Let dbMask = MGF(H, em_len - h_len - 1) - // - // 8. Let DB = maskedDB \xor dbMask - mgf1_xor(db, hash, &*h); - - // 9. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in DB - // to zero. - db[0] &= 0xFF >> /*uint*/(8 * em_len - em_bits); - - let salt = emsa_pss_get_salt(db, em_len, s_len, h_len)?; - - // 12. Let - // M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ; - // M' is an octet string of length 8 + hLen + sLen with eight - // initial zero octets. - // - // 13. Let H' = Hash(M'), an octet string of length hLen. - let prefix = [0u8; 8]; - - hash.update(&prefix[..]); - hash.update(m_hash); - hash.update(salt); - let h0 = hash.finalize_reset(); - - // 14. If H = H', output "consistent." Otherwise, output "inconsistent." - if h0.ct_eq(h).into() { - Ok(()) - } else { - Err(Error::Verification) - } -} - fn emsa_pss_verify_digest( m_hash: &[u8], em: &mut [u8], @@ -921,7 +756,7 @@ where #[cfg(test)] mod test { use crate::pss::{BlindedSigningKey, Signature, SigningKey, VerifyingKey}; - use crate::{PaddingScheme, PublicKey, RsaPrivateKey, RsaPublicKey}; + use crate::{RsaPrivateKey, RsaPublicKey}; use hex_literal::hex; use num_bigint::BigUint; @@ -954,42 +789,6 @@ mod test { ).unwrap() } - #[test] - fn test_verify_pss() { - let priv_key = get_private_key(); - - let tests = [ - ( - "test\n", - hex!( - "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae" - "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962f" - ), - true, - ), - ( - "test\n", - hex!( - "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae" - "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962e" - ), - false, - ), - ]; - let pub_key: RsaPublicKey = priv_key.into(); - - for (text, sig, expected) in &tests { - let digest = Sha1::digest(text.as_bytes()).to_vec(); - let result = pub_key.verify(PaddingScheme::new_pss::(), &digest, sig); - match expected { - true => result.expect("failed to verify"), - false => { - result.expect_err("expected verifying error"); - } - } - } - } - #[test] fn test_verify_pss_signer() { let priv_key = get_private_key(); @@ -1068,44 +867,6 @@ mod test { } } - #[test] - fn test_sign_and_verify_roundtrip() { - let priv_key = get_private_key(); - - let tests = ["test\n"]; - let rng = ChaCha8Rng::from_seed([42; 32]); - - for test in &tests { - let digest = Sha1::digest(test.as_bytes()).to_vec(); - let sig = priv_key - .sign_with_rng(&mut rng.clone(), PaddingScheme::new_pss::(), &digest) - .expect("failed to sign"); - - priv_key - .verify(PaddingScheme::new_pss::(), &digest, &sig) - .expect("failed to verify"); - } - } - - #[test] - fn test_sign_blinded_and_verify_roundtrip() { - let priv_key = get_private_key(); - - let tests = ["test\n"]; - let rng = ChaCha8Rng::from_seed([42; 32]); - - for test in &tests { - let digest = Sha1::digest(test.as_bytes()).to_vec(); - let sig = priv_key - .sign_blinded(&mut rng.clone(), PaddingScheme::new_pss::(), &digest) - .expect("failed to sign"); - - priv_key - .verify(PaddingScheme::new_pss::(), &digest, &sig) - .expect("failed to verify"); - } - } - #[test] fn test_sign_and_verify_roundtrip_signer() { let priv_key = get_private_key();