From 5d28baf32a30173ebef0ab0c5d9cd2e98c522074 Mon Sep 17 00:00:00 2001 From: roblabla Date: Wed, 21 Aug 2019 09:33:50 +0000 Subject: [PATCH 1/7] Implement PSS --- Cargo.toml | 5 +- src/key.rs | 33 +++-- src/lib.rs | 1 + src/pkcs1v15.rs | 4 +- src/pss.rs | 380 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 403 insertions(+), 20 deletions(-) create mode 100644 src/pss.rs diff --git a/Cargo.toml b/Cargo.toml index a9415be0..272b6360 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,9 @@ rand = "0.6.5" byteorder = "1.3.1" failure = "0.1.5" subtle = "2.0.0" +signature = { version = "0.2.0" } +sha2 = { version = "0.8" } +sha-1 = { version = "0.8.1" } [dependencies.zeroize] version = "0.6" @@ -34,8 +37,6 @@ features = ["std", "derive"] [dev-dependencies] base64 = "0.10.1" -sha-1 = "0.8.1" -sha2 = "0.8.0" hex = "0.3.2" serde_test = "1.0.89" diff --git a/src/key.rs b/src/key.rs index 27164af9..645e0330 100644 --- a/src/key.rs +++ b/src/key.rs @@ -9,9 +9,10 @@ use zeroize::{Zeroize, ZeroizeOnDrop}; use crate::algorithms::generate_multi_prime_key; use crate::errors::{Error, Result}; -use crate::hash::Hash; +use crate::hash::{Hash, Hashes}; use crate::padding::PaddingScheme; use crate::pkcs1v15; +use crate::pss; lazy_static! { static ref MIN_PUB_EXPONENT: BigUint = BigUint::from_u64(2).unwrap(); @@ -132,10 +133,10 @@ pub trait PublicKey { /// `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( + fn verify( &self, padding: PaddingScheme, - hash: Option<&H>, + hash: Option<&Hashes>, hashed: &[u8], sig: &[u8], ) -> Result<()>; @@ -157,16 +158,16 @@ impl PublicKey for RSAPublicKey { } } - fn verify( + fn verify( &self, padding: PaddingScheme, - hash: Option<&H>, + hash: Option<&Hashes>, hashed: &[u8], sig: &[u8], ) -> Result<()> { match padding { PaddingScheme::PKCS1v15 => pkcs1v15::verify(self, hash, hashed, sig), - PaddingScheme::PSS => unimplemented!("not yet implemented"), + PaddingScheme::PSS => pss::verify(self, hash.unwrap(), hashed, sig), _ => Err(Error::InvalidPaddingScheme), } } @@ -195,10 +196,10 @@ impl<'a> PublicKey for &'a RSAPublicKey { (*self).encrypt(rng, padding, msg) } - fn verify( + fn verify( &self, padding: PaddingScheme, - hash: Option<&H>, + hash: Option<&Hashes>, hashed: &[u8], sig: &[u8], ) -> Result<()> { @@ -223,16 +224,16 @@ impl PublicKey for RSAPrivateKey { } } - fn verify( + fn verify( &self, padding: PaddingScheme, - hash: Option<&H>, + hash: Option<&Hashes>, hashed: &[u8], sig: &[u8], ) -> Result<()> { match padding { PaddingScheme::PKCS1v15 => pkcs1v15::verify(self, hash, hashed, sig), - PaddingScheme::PSS => unimplemented!("not yet implemented"), + PaddingScheme::PSS => pss::verify(self, hash.unwrap(), hashed, sig), _ => Err(Error::InvalidPaddingScheme), } } @@ -251,10 +252,10 @@ impl<'a> PublicKey for &'a RSAPrivateKey { (*self).encrypt(rng, padding, msg) } - fn verify( + fn verify( &self, padding: PaddingScheme, - hash: Option<&H>, + hash: Option<&Hashes>, hashed: &[u8], sig: &[u8], ) -> Result<()> { @@ -428,16 +429,16 @@ impl RSAPrivateKey { /// Sign the given digest. /// Use `rng` for blinding. - pub fn sign_blinded( + pub fn sign_blinded( &self, rng: &mut R, padding: PaddingScheme, - hash: Option<&H>, + hash: Option<&Hashes>, digest: &[u8], ) -> Result> { match padding { PaddingScheme::PKCS1v15 => pkcs1v15::sign(Some(rng), self, hash, digest), - PaddingScheme::PSS => unimplemented!("not yet implemented"), + PaddingScheme::PSS => pss::sign(rng, self, hash.expect("Can't use None hash with PSS"), digest, None), _ => Err(Error::InvalidPaddingScheme), } } diff --git a/src/lib.rs b/src/lib.rs index 42799cd5..5ebdb43c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,7 @@ pub mod padding; mod key; mod pkcs1v15; +mod pss; pub use self::key::{PublicKey, RSAPrivateKey, RSAPublicKey}; pub use self::padding::PaddingScheme; diff --git a/src/pkcs1v15.rs b/src/pkcs1v15.rs index aa753384..82d9304c 100644 --- a/src/pkcs1v15.rs +++ b/src/pkcs1v15.rs @@ -171,7 +171,7 @@ fn hash_info(hash: Option<&H>, digest_len: usize) -> Result<(usize, Vec } #[inline] -fn copy_with_left_pad(dest: &mut [u8], src: &[u8]) { +pub fn copy_with_left_pad(dest: &mut [u8], src: &[u8]) { // left pad with zeros let padding_bytes = dest.len() - src.len(); for el in dest.iter_mut().take(padding_bytes) { @@ -414,7 +414,7 @@ mod tests { let pub_key: RSAPublicKey = priv_key.into(); pub_key - .verify::(PaddingScheme::PKCS1v15, None, msg, &sig) + .verify(PaddingScheme::PKCS1v15, None, msg, &sig) .expect("failed to verify"); } } diff --git a/src/pss.rs b/src/pss.rs new file mode 100644 index 00000000..bcbf5682 --- /dev/null +++ b/src/pss.rs @@ -0,0 +1,380 @@ +use crate::pkcs1v15::copy_with_left_pad; +use crate::internals; +use crate::hash::{Hashes, Hash}; +use crate::key::{RSAPrivateKey, PublicKey}; +use crate::errors::{Error, Result}; + +use std::vec::Vec; +use num_bigint::BigUint; +use subtle::ConstantTimeEq; +use sha2::{Digest, Sha256}; +use sha1::Sha1; +use rand::Rng; + +pub fn verify( + pub_key: &K, + hash: &Hashes, + hashed: &[u8], + sig: &[u8]) -> Result<()> +{ + let n_bits = pub_key.n().bits(); + if sig.len() != (n_bits + 7) / 8 { + return Err(Error::Verification); + } + let s = BigUint::from_bytes_be(sig); + let m = internals::encrypt(pub_key, &s).to_bytes_be(); + let em_bits = n_bits - 1; + let em_len = (em_bits + 7) / 8; + + if em_len < m.len() { + return Err(Error::Verification); + } + + let mut em = vec![0; em_len]; + copy_with_left_pad(&mut em, &m); + + match hash { + Hashes::SHA1 => { + emsa_pss_verify(hashed, &mut em, em_bits, None, Sha1::new()) + }, + Hashes::SHA2_256 => { + emsa_pss_verify(hashed, &mut em, em_bits, None, Sha256::new()) + }, + _ => unimplemented!() + } +} + + +/// SignPSS calculates the signature of hashed using RSASSA-PSS [1]. +/// 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 fn sign(rng: &mut T, priv_key: &RSAPrivateKey, hash: &Hashes, hashed: &[u8], salt_len: Option) -> Result> { + let salt_len = salt_len.unwrap_or_else(|| { + (priv_key.n().bits() + 7) / 8 - 2 - hash.size() + }); + + let mut salt = vec![0; salt_len]; + rng.fill(&mut salt[..]); + + return sign_pss_with_salt(rng, priv_key, hash, hashed, &salt) +} + + +// signPSSWithSalt calculates the signature of hashed using PSS [1] with specified 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(rng: &mut T, priv_key: &RSAPrivateKey, hash: &Hashes, hashed: &[u8], salt: &[u8]) -> Result> { + let n_bits = priv_key.n().bits(); + let mut em = vec![0; ((n_bits - 1) + 7) / 8]; + match hash { + Hashes::SHA1 => { + emsa_pss_encode(&mut em, hashed, n_bits - 1, salt, Sha1::new())?; + }, + Hashes::SHA2_256 => { + emsa_pss_encode(&mut em, hashed, n_bits - 1, salt, Sha256::new())?; + }, + _ => unimplemented!() + } + + let mut m = BigUint::from_bytes_be(&em); + let mut c = internals::decrypt_and_check(Some(rng), priv_key, &m)?.to_bytes_be(); + + let mut s = vec![0; (n_bits + 7) / 8]; + copy_with_left_pad(&mut s, &c); + return Ok(s) +} + +fn emsa_pss_encode(em: &mut [u8], m_hash: &[u8], em_bits: usize, salt: &[u8], mut hash: H) -> Result<()> { + // See [1], section 9.1.1 + let h_len = H::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); + } + + if em.len() != em_len { + return Err(Error::Internal); + } + + let (db, h) = em.split_at_mut(em_len - s_len - h_len - 2 + 1 + s_len); + 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.input(&prefix); + hash.input(m_hash); + hash.input(salt); + + let hashed = hash.result_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, &mut 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; + + return Ok(()) +} + +fn emsa_pss_verify(m_hash: &[u8], em: &mut [u8], em_bits: usize, s_len: Option, mut hash: H) -> Result<()> { + // 1. If the length of M is greater than the input limitation for the + // hash function (2^61 - 1 octets for SHA-1), output "inconsistent" + // and stop. + // + // 2. Let mHash = Hash(M), an octet string of length hLen + let h_len = H::output_size(); + if m_hash.len() != h_len { + return Err(Error::Verification); + } + + // 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop. + let em_len = em.len();//(em_bits + 7) / 8; + if em_len < h_len + 2 { + return Err(Error::Verification) + } + + // 4. If the rightmost octet of EM does not have hexadecimal value + // 0xbc, output "inconsistent" and stop. + if em[em.len() - 1] != 0xBC { + return Err(Error::Verification) + } + + // 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and + // let H be the next hLen octets. + let (db, h) = em.split_at_mut(em_len - h_len - 1); + let h = &mut h[..(em_len - 1) - (em_len - h_len - 1)]; + + // 6. If the leftmost 8 * em_len - em_bits bits of the leftmost octet in + // maskedDB are not all equal to zero, output "inconsistent" and + // stop. + if db[0] & (0xFF << /*uint*/(8 - (8 * em_len - em_bits))) != 0 { + return Err(Error::Verification) + } + + // 7. Let dbMask = MGF(H, em_len - h_len - 1) + // + // 8. Let DB = maskedDB \xor dbMask + mgf1_xor(db, &mut 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 s_len = match s_len { + None => (0..=em_len - (h_len + 2)).rev().try_fold(None, |state, i| { + match (state, db[em_len - h_len - i - 2]) { + (Some(i), _) => Ok(Some(i)), + (_, 1) => Ok(Some(i)), + (_, 0) => Ok(None), + _ => Err(Error::Verification) + } + })?.ok_or(Error::Verification)?, + Some(s_len) => { + // 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero + // or if the octet at position emLen - hLen - sLen - 1 (the leftmost + // position is "position 1") does not have hexadecimal value 0x01, + // output "inconsistent" and stop. + for e in &db[..em_len - h_len - s_len - 2] { + if *e != 0x00 { + return Err(Error::Verification); + } + } + if db[em_len - h_len - s_len - 2] != 0x01 { + return Err(Error::Verification) + } + s_len + } + }; + + // 11. Let salt be the last s_len octets of DB. + let salt = &db[db.len() - s_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.input(prefix); + hash.input(m_hash); + hash.input(salt); + let h0 = hash.result(); + + // 14. If H = H', output "consistent." Otherwise, output "inconsistent." + if Into::::into(h0.ct_eq(h)) { + Ok(()) + } else { + Err(Error::Verification) + } +} + +fn inc_counter(counter: &mut [u8]) { + if counter[3] == u8::max_value() { + counter[3] = 0; + } else { + counter[3] += 1; + return; + } + + if counter[2] == u8::max_value() { + counter[2] = 0; + } else { + counter[2] += 1; + return; + } + + if counter[1] == u8::max_value() { + counter[1] = 0; + } else { + counter[1] += 1; + return; + } + + if counter[0] == u8::max_value() { + counter[0] = 0u8; + counter[1] = 0u8; + counter[2] = 0u8; + counter[3] = 0u8; + } else { + counter[0] += 1; + } +} + +/// Mask generation function +fn mgf1_xor(out: &mut [u8], digest: &mut T, seed: &[u8]) { + let mut counter = vec![0u8; 4]; + let mut i = 0; + + while i < out.len() { + let mut digest_input = vec![0u8; seed.len() + 4]; + digest_input[0..seed.len()].copy_from_slice(seed); + digest_input[seed.len()..].copy_from_slice(&counter); + + digest.input(digest_input.as_slice()); + let digest_output = &*digest.result_reset(); + let mut j = 0; + loop { + if j >= digest_output.len() || i >= out.len() { + break; + } + + out[i] ^= digest_output[j]; + j += 1; + i += 1; + } + inc_counter(counter.as_mut_slice()); + } +} + +#[cfg(test)] +mod test { + use crate::{PaddingScheme, RSAPrivateKey, RSAPublicKey, PublicKey}; + use crate::hash::Hashes; + + use num_bigint::BigUint; + use num_traits::{FromPrimitive, Num}; + use sha1::{Digest, Sha1}; + use rand::thread_rng; + + fn get_private_key() -> RSAPrivateKey { + // In order to generate new test vectors you'll need the PEM form of this key: + // -----BEGIN RSA PRIVATE KEY----- + // MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0 + // fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu + // /ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu + // RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/ + // EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A + // IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS + // tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V + // -----END RSA PRIVATE KEY----- + + RSAPrivateKey::from_components( + BigUint::from_str_radix("9353930466774385905609975137998169297361893554149986716853295022578535724979677252958524466350471210367835187480748268864277464700638583474144061408845077", 10).unwrap(), + BigUint::from_u64(65537).unwrap(), + BigUint::from_str_radix("7266398431328116344057699379749222532279343923819063639497049039389899328538543087657733766554155839834519529439851673014800261285757759040931985506583861", 10).unwrap(), + vec![ + BigUint::from_str_radix("98920366548084643601728869055592650835572950932266967461790948584315647051443",10).unwrap(), + BigUint::from_str_radix("94560208308847015747498523884063394671606671904944666360068158221458669711639", 10).unwrap() + ], + ) + } + + #[test] + fn test_verify_pss() { + let priv_key = get_private_key(); + + let tests = [[ + "test\n", "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962f" + ]]; + let pub_key: RSAPublicKey = priv_key.into(); + + for test in &tests { + let digest = Sha1::digest(test[0].as_bytes()).to_vec(); + let sig = hex::decode(test[1]).unwrap(); + + pub_key + .verify(PaddingScheme::PSS, Some(&Hashes::SHA1), &digest, &sig) + .expect("failed to verify"); + } + } + + #[test] + fn test_sign_and_verify_roundtrip() { + let priv_key = get_private_key(); + + let tests = ["test\n"]; + + for test in &tests { + let digest = Sha1::digest(test.as_bytes()).to_vec(); + let sig = priv_key + .sign_blinded(&mut thread_rng(), PaddingScheme::PSS, Some(&Hashes::SHA1), &digest) + .expect("failed to sign"); + + priv_key + .verify(PaddingScheme::PSS, Some(&Hashes::SHA1), &digest, &sig) + .expect("failed to verify"); + } + } +} \ No newline at end of file From 2462f1dd56f79deb211efe7e298a9d8c2b2b8d61 Mon Sep 17 00:00:00 2001 From: roblabla Date: Thu, 26 Sep 2019 12:40:43 +0000 Subject: [PATCH 2/7] Remove PublicKey trait, use deref. Removes the PublicKey trait, using Deref to provide the RSAPublicKey methods to RSAPrivateKey. --- src/internals.rs | 8 +-- src/key.rs | 166 ++++++++++++----------------------------------- src/lib.rs | 2 +- src/pkcs1v15.rs | 8 +-- 4 files changed, 50 insertions(+), 134 deletions(-) diff --git a/src/internals.rs b/src/internals.rs index 5c034f59..ecc12198 100644 --- a/src/internals.rs +++ b/src/internals.rs @@ -5,11 +5,11 @@ use std::borrow::Cow; use zeroize::Zeroize; use crate::errors::{Error, Result}; -use crate::key::{PublicKey, RSAPrivateKey}; +use crate::key::{RSAPublicKey, RSAPrivateKey}; /// Raw RSA encryption of m with the public key. No padding is performed. #[inline] -pub fn encrypt(key: &K, m: &BigUint) -> BigUint { +pub fn encrypt(key: &RSAPublicKey, m: &BigUint) -> BigUint { m.modpow(key.e(), key.n()) } @@ -125,7 +125,7 @@ pub fn decrypt_and_check( } /// Returns the blinded c, along with the unblinding factor. -pub fn blind(rng: &mut R, key: &K, c: &BigUint) -> (BigUint, BigUint) { +pub fn blind(rng: &mut R, key: &RSAPublicKey, c: &BigUint) -> (BigUint, BigUint) { // Blinding involves multiplying c by r^e. // Then the decryption operation performs (m^e * r^e)^d mod n // which equals mr mod n. The factor of r can then be removed @@ -162,7 +162,7 @@ pub fn blind(rng: &mut R, key: &K, c: &BigUint) -> (BigUin } /// Given an m and and unblinding factor, unblind the m. -pub fn unblind(key: impl PublicKey, m: &BigUint, unblinder: &BigUint) -> BigUint { +pub fn unblind(key: &RSAPublicKey, m: &BigUint, unblinder: &BigUint) -> BigUint { (m * unblinder) % key.n() } diff --git a/src/key.rs b/src/key.rs index 645e0330..4c2290c7 100644 --- a/src/key.rs +++ b/src/key.rs @@ -1,3 +1,4 @@ +use std::ops::Deref; use num_bigint::traits::ModInverse; use num_bigint::Sign::Plus; use num_bigint::{BigInt, BigUint}; @@ -31,10 +32,8 @@ pub struct RSAPublicKey { #[derive(Debug, Clone, ZeroizeOnDrop)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct RSAPrivateKey { - /// Modulus - n: BigUint, - /// Public exponent - e: BigUint, + /// Public components of the private key. + pubkey_components: RSAPublicKey, /// Private exponent d: BigUint, /// Prime factors of N, contains >= 2 elements. @@ -47,7 +46,7 @@ pub struct RSAPrivateKey { impl PartialEq for RSAPrivateKey { #[inline] fn eq(&self, other: &RSAPrivateKey) -> bool { - self.n == other.n && self.e == other.e && self.d == other.d && self.primes == other.primes + self.pubkey_components == other.pubkey_components && self.d == other.d && self.primes == other.primes } } @@ -66,6 +65,13 @@ impl Zeroize for RSAPrivateKey { } } +impl Deref for RSAPrivateKey { + type Target = RSAPublicKey; + fn deref(&self) -> &RSAPublicKey { + &self.pubkey_components + } +} + #[derive(Debug, Clone, ZeroizeOnDrop)] pub(crate) struct PrecomputedValues { /// D mod (P-1) @@ -106,70 +112,16 @@ pub(crate) struct CRTValue { } impl From for RSAPublicKey { - fn from(private_key: RSAPrivateKey) -> Self { - let n = private_key.n.clone(); - let e = private_key.e.clone(); - - RSAPublicKey { n, e } - } -} - -/// Generic trait for operations on a public key. -pub trait PublicKey { - /// Returns the modulus of the key. - fn n(&self) -> &BigUint; - /// Returns the public exponent of the key. - fn e(&self) -> &BigUint; - /// Returns the modulus size in bytes. Raw signatures and ciphertexts for - /// or by this public key will have the same size. - fn size(&self) -> usize { - (self.n().bits() + 7) / 8 - } - - /// Encrypt the given message. - fn encrypt(&self, rng: &mut R, 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, - hash: Option<&Hashes>, - hashed: &[u8], - sig: &[u8], - ) -> Result<()>; -} - -impl PublicKey for RSAPublicKey { - fn n(&self) -> &BigUint { - &self.n - } - - fn e(&self) -> &BigUint { - &self.e - } - fn encrypt(&self, rng: &mut R, padding: PaddingScheme, msg: &[u8]) -> Result> { - match padding { - PaddingScheme::PKCS1v15 => pkcs1v15::encrypt(rng, self, msg), - PaddingScheme::OAEP => unimplemented!("not yet implemented"), - _ => Err(Error::InvalidPaddingScheme), - } - } - - fn verify( - &self, - padding: PaddingScheme, - hash: Option<&Hashes>, - hashed: &[u8], - sig: &[u8], - ) -> Result<()> { - match padding { - PaddingScheme::PKCS1v15 => pkcs1v15::verify(self, hash, hashed, sig), - PaddingScheme::PSS => pss::verify(self, hash.unwrap(), hashed, sig), - _ => Err(Error::InvalidPaddingScheme), - } + fn from(mut private_key: RSAPrivateKey) -> Self { + let mut broken_key = RSAPublicKey { + // Fast, no-allocation creation of a biguint. + n: BigUint::new_native(Default::default()), + e: BigUint::new_native(Default::default()) + }; + // The private key is going to get dropped after this, so temporarily + // making it invalid is fine. + let pubkey = core::mem::replace(&mut private_key.pubkey_components, broken_key); + pubkey } } @@ -181,42 +133,25 @@ impl RSAPublicKey { Ok(k) } -} -impl<'a> PublicKey for &'a RSAPublicKey { - fn n(&self) -> &BigUint { + /// Returns the modulus of the key. + pub fn n(&self) -> &BigUint { &self.n } - fn e(&self) -> &BigUint { + /// Returns the public exponent of the key. + pub fn e(&self) -> &BigUint { &self.e } - fn encrypt(&self, rng: &mut R, padding: PaddingScheme, msg: &[u8]) -> Result> { - (*self).encrypt(rng, padding, msg) - } - - fn verify( - &self, - padding: PaddingScheme, - hash: Option<&Hashes>, - hashed: &[u8], - sig: &[u8], - ) -> Result<()> { - (*self).verify(padding, hash, hashed, sig) - } -} - -impl PublicKey for RSAPrivateKey { - fn n(&self) -> &BigUint { - &self.n - } - - fn e(&self) -> &BigUint { - &self.e + /// Returns the modulus size in bytes. Raw signatures and ciphertexts for + /// or by this public key will have the same size. + pub fn size(&self) -> usize { + (self.n().bits() + 7) / 8 } - fn encrypt(&self, rng: &mut R, padding: PaddingScheme, msg: &[u8]) -> Result> { + /// Encrypt the given message + pub fn encrypt(&self, rng: &mut R, padding: PaddingScheme, msg: &[u8]) -> Result> { match padding { PaddingScheme::PKCS1v15 => pkcs1v15::encrypt(rng, self, msg), PaddingScheme::OAEP => unimplemented!("not yet implemented"), @@ -224,10 +159,14 @@ impl PublicKey for RSAPrivateKey { } } - fn verify( + /// Verify a signed message. + /// `hashed` must be the result of hashing the input using the hashing function + /// identified using the ASN1 prefix in `hash_asn1_prefix`. + /// If the message is valid `Ok(())` is returned, otherwiese an `Err` indicating failure. + pub fn verify( &self, padding: PaddingScheme, - hash: Option<&Hashes>, + hash: Option<&H>, hashed: &[u8], sig: &[u8], ) -> Result<()> { @@ -239,30 +178,6 @@ impl PublicKey for RSAPrivateKey { } } -impl<'a> PublicKey for &'a RSAPrivateKey { - fn n(&self) -> &BigUint { - &self.n - } - - fn e(&self) -> &BigUint { - &self.e - } - - fn encrypt(&self, rng: &mut R, padding: PaddingScheme, msg: &[u8]) -> Result> { - (*self).encrypt(rng, padding, msg) - } - - fn verify( - &self, - padding: PaddingScheme, - hash: Option<&Hashes>, - hashed: &[u8], - sig: &[u8], - ) -> Result<()> { - (*self).verify(padding, hash, hashed, sig) - } -} - 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 { @@ -277,8 +192,9 @@ impl RSAPrivateKey { primes: Vec, ) -> RSAPrivateKey { let mut k = RSAPrivateKey { - n, - e, + pubkey_components: RSAPublicKey { + n, e + }, d, primes, precomputed: None, @@ -446,7 +362,7 @@ impl RSAPrivateKey { /// Check that the public key is well formed and has an exponent within acceptable bounds. #[inline] -pub fn check_public(public_key: &impl PublicKey) -> Result<()> { +pub fn check_public(public_key: &RSAPublicKey) -> RsaResult<()> { if public_key.e() < &*MIN_PUB_EXPONENT { return Err(Error::PublicExponentTooSmall); } diff --git a/src/lib.rs b/src/lib.rs index 5ebdb43c..d292f257 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,7 +64,7 @@ mod key; mod pkcs1v15; mod pss; -pub use self::key::{PublicKey, RSAPrivateKey, RSAPublicKey}; +pub use self::key::{RSAPrivateKey, RSAPublicKey}; pub use self::padding::PaddingScheme; // Optionally expose internals if requested via feature-flag. diff --git a/src/pkcs1v15.rs b/src/pkcs1v15.rs index 82d9304c..3a8d2c27 100644 --- a/src/pkcs1v15.rs +++ b/src/pkcs1v15.rs @@ -6,13 +6,13 @@ use zeroize::Zeroize; use crate::errors::{Error, Result}; use crate::hash::Hash; use crate::internals; -use crate::key::{self, PublicKey, RSAPrivateKey}; +use crate::key::{self, RSAPublicKey, RSAPrivateKey}; // 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. #[inline] -pub fn encrypt(rng: &mut R, pub_key: &K, msg: &[u8]) -> Result> { +pub fn encrypt(rng: &mut R, pub_key: &RSAPublicKey, msg: &[u8]) -> Result> { key::check_public(pub_key)?; let k = pub_key.size(); @@ -116,8 +116,8 @@ pub fn sign( /// Verifies an RSA PKCS#1 v1.5 signature. #[inline] -pub fn verify( - pub_key: &K, +pub fn verify( + pub_key: &RSAPublicKey, hash: Option<&H>, hashed: &[u8], sig: &[u8], From 071f07f6c8288efea12f557fe226749450e3f5bd Mon Sep 17 00:00:00 2001 From: roblabla Date: Thu, 26 Sep 2019 12:42:54 +0000 Subject: [PATCH 3/7] Avoid using vectors to store the asn1 prefixes --- src/hash.rs | 26 +++++++++++++------------- src/pkcs1v15.rs | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/hash.rs b/src/hash.rs index 6af2ae5b..71dbc63c 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -5,7 +5,7 @@ pub trait Hash { fn size(&self) -> usize; /// Returns the ASN1 DER prefix for the the hash function. - fn asn1_prefix(&self) -> Vec; + fn asn1_prefix(&self) -> &'static [u8]; } /// A list of provided hashes, implementing `Hash`. @@ -41,50 +41,50 @@ impl Hash for Hashes { } } - fn asn1_prefix(&self) -> Vec { + fn asn1_prefix(&self) -> &'static [u8] { match *self { - Hashes::MD5 => vec![ + Hashes::MD5 => &[ 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10, ], - Hashes::SHA1 => vec![ + Hashes::SHA1 => &[ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, ], - Hashes::SHA2_224 => vec![ + Hashes::SHA2_224 => &[ 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c, ], - Hashes::SHA2_256 => vec![ + Hashes::SHA2_256 => &[ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20, ], - Hashes::SHA2_384 => vec![ + Hashes::SHA2_384 => &[ 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30, ], - Hashes::SHA2_512 => vec![ + Hashes::SHA2_512 => &[ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40, ], // A special TLS case which doesn't use an ASN1 prefix - Hashes::MD5SHA1 => Vec::new(), - Hashes::RIPEMD160 => vec![ + Hashes::MD5SHA1 => &[], + Hashes::RIPEMD160 => &[ 0x30, 0x20, 0x30, 0x08, 0x06, 0x06, 0x28, 0xcf, 0x06, 0x03, 0x00, 0x31, 0x04, 0x14, ], - Hashes::SHA3_256 => vec![ + Hashes::SHA3_256 => &[ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x08, 0x05, 0x00, 0x04, 0x20, ], - Hashes::SHA3_384 => vec![ + Hashes::SHA3_384 => &[ 30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x08, 0x05, 0x00, 0x04, 0x20, ], - Hashes::SHA3_512 => vec![ + Hashes::SHA3_512 => &[ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0a, 0x05, 0x00, 0x04, 0x40, ], diff --git a/src/pkcs1v15.rs b/src/pkcs1v15.rs index 3a8d2c27..02dd2939 100644 --- a/src/pkcs1v15.rs +++ b/src/pkcs1v15.rs @@ -155,7 +155,7 @@ pub fn verify( } #[inline] -fn hash_info(hash: Option<&H>, digest_len: usize) -> Result<(usize, Vec)> { +fn hash_info(hash: Option<&H>, digest_len: usize) -> Result<(usize, &'static [u8])> { match hash { Some(hash) => { let hash_len = hash.size(); @@ -166,7 +166,7 @@ fn hash_info(hash: Option<&H>, digest_len: usize) -> Result<(usize, Vec Ok((hash_len, hash.asn1_prefix())) } // this means the data is signed directly - None => Ok((digest_len, Vec::new())), + None => Ok((digest_len, &[])), } } From 527acfca3156ddb7eaec79f502c472b97222d948 Mon Sep 17 00:00:00 2001 From: roblabla Date: Thu, 26 Sep 2019 12:43:56 +0000 Subject: [PATCH 4/7] Use separate functions for different padding schemes --- src/key.rs | 85 +++++++++++++++++++----------------------------------- 1 file changed, 29 insertions(+), 56 deletions(-) diff --git a/src/key.rs b/src/key.rs index 4c2290c7..0dd67cd6 100644 --- a/src/key.rs +++ b/src/key.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; use zeroize::{Zeroize, ZeroizeOnDrop}; use crate::algorithms::generate_multi_prime_key; -use crate::errors::{Error, Result}; +use crate::errors::{Error, Result as RsaResult}; use crate::hash::{Hash, Hashes}; use crate::padding::PaddingScheme; use crate::pkcs1v15; @@ -127,7 +127,7 @@ impl From for RSAPublicKey { impl RSAPublicKey { /// Create a new key from its components. - pub fn new(n: BigUint, e: BigUint) -> Result { + pub fn new(n: BigUint, e: BigUint) -> RsaResult { let k = RSAPublicKey { n, e }; check_public(&k)?; @@ -150,37 +150,28 @@ impl RSAPublicKey { (self.n().bits() + 7) / 8 } - /// Encrypt the given message - pub fn encrypt(&self, rng: &mut R, padding: PaddingScheme, msg: &[u8]) -> Result> { - match padding { - PaddingScheme::PKCS1v15 => pkcs1v15::encrypt(rng, self, msg), - PaddingScheme::OAEP => unimplemented!("not yet implemented"), - _ => Err(Error::InvalidPaddingScheme), - } + /// Encrypt the given message, using the PKCS1v15 padding scheme. + pub fn encrypt_pkcs1v15(&self, rng: &mut R, msg: &[u8]) -> RsaResult> { + pkcs1v15::encrypt(rng, self, msg) } - /// Verify a signed message. + /// Verify a message signed with the PKCS1v15 padding scheme. /// `hashed` must be the result of hashing the input using the hashing function /// identified using the ASN1 prefix in `hash_asn1_prefix`. /// If the message is valid `Ok(())` is returned, otherwiese an `Err` indicating failure. - pub fn verify( + pub fn verify_pkcs1v15( &self, - padding: PaddingScheme, hash: Option<&H>, hashed: &[u8], sig: &[u8], - ) -> Result<()> { - match padding { - PaddingScheme::PKCS1v15 => pkcs1v15::verify(self, hash, hashed, sig), - PaddingScheme::PSS => pss::verify(self, hash.unwrap(), hashed, sig), - _ => Err(Error::InvalidPaddingScheme), - } + ) -> RsaResult<()> { + pkcs1v15::verify(self, hash, hashed, sig) } } 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) -> RsaResult { generate_multi_prime_key(rng, 2, bit_size) } @@ -271,7 +262,7 @@ impl RSAPrivateKey { /// Performs basic sanity checks on the key. /// Returns `Ok(())` if everything is good, otherwise an approriate error. - pub fn validate(&self) -> Result<()> { + pub fn validate(&self) -> RsaResult<()> { check_public(self)?; // Check that Πprimes == n. @@ -304,59 +295,41 @@ impl RSAPrivateKey { Ok(()) } - /// Decrypt the given message. - pub fn decrypt(&self, padding: PaddingScheme, ciphertext: &[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::PKCS1v15 => pkcs1v15::decrypt::(None, self, ciphertext), - PaddingScheme::OAEP => unimplemented!("not yet implemented"), - _ => Err(Error::InvalidPaddingScheme), - } + /// Decrypt the given message, using the PKCS1v15 padding scheme. + pub fn decrypt_pkcs1v15(&self, ciphertext: &[u8]) -> RsaResult> { + pkcs1v15::decrypt::(None, self, ciphertext) } - /// Decrypt the given message. + /// Decrypt the given message, using the PKCS1v15 padding scheme. + /// /// Uses `rng` to blind the decryption process. - pub fn decrypt_blinded( + pub fn decrypt_pkcs1v15_blinded( &self, rng: &mut R, - padding: PaddingScheme, ciphertext: &[u8], - ) -> Result> { - match padding { - PaddingScheme::PKCS1v15 => pkcs1v15::decrypt(Some(rng), self, ciphertext), - PaddingScheme::OAEP => unimplemented!("not yet implemented"), - _ => Err(Error::InvalidPaddingScheme), - } + ) -> RsaResult> { + pkcs1v15::decrypt(Some(rng), self, ciphertext) } - /// Sign the given digest. - pub fn sign( + /// Sign the given digest using the PKCS1v15 padding scheme. + pub fn sign_pkcs1v15( &self, - padding: PaddingScheme, hash: Option<&H>, digest: &[u8], - ) -> Result> { - match padding { - PaddingScheme::PKCS1v15 => pkcs1v15::sign::(None, self, hash, digest), - PaddingScheme::PSS => unimplemented!("not yet implemented"), - _ => Err(Error::InvalidPaddingScheme), - } + ) -> RsaResult> { + pkcs1v15::sign::(None, self, hash, digest) } - /// Sign the given digest. + /// Sign the given digest using the PKCS1v15 padding scheme. + /// /// Use `rng` for blinding. - pub fn sign_blinded( + pub fn sign_pkcs1v15_blinded( &self, rng: &mut R, - padding: PaddingScheme, - hash: Option<&Hashes>, + hash: Option<&H>, digest: &[u8], - ) -> Result> { - match padding { - PaddingScheme::PKCS1v15 => pkcs1v15::sign(Some(rng), self, hash, digest), - PaddingScheme::PSS => pss::sign(rng, self, hash.expect("Can't use None hash with PSS"), digest, None), - _ => Err(Error::InvalidPaddingScheme), - } + ) -> RsaResult> { + pkcs1v15::sign(Some(rng), self, hash, digest) } } From b821cc6cc2adb7fd94c367ef330f18076c8ea5d6 Mon Sep 17 00:00:00 2001 From: roblabla Date: Thu, 26 Sep 2019 13:15:04 +0000 Subject: [PATCH 5/7] Add new sign_pss/verify_pss --- src/key.rs | 39 +++++++++++++++++++++++++--- src/pss.rs | 74 ++++++++++++++++++++++++------------------------------ 2 files changed, 69 insertions(+), 44 deletions(-) diff --git a/src/key.rs b/src/key.rs index 0dd67cd6..128733e9 100644 --- a/src/key.rs +++ b/src/key.rs @@ -7,11 +7,11 @@ use rand::{rngs::ThreadRng, Rng}; #[cfg(feature = "serde1")] use serde::{Deserialize, Serialize}; use zeroize::{Zeroize, ZeroizeOnDrop}; +use digest::Digest; use crate::algorithms::generate_multi_prime_key; use crate::errors::{Error, Result as RsaResult}; -use crate::hash::{Hash, Hashes}; -use crate::padding::PaddingScheme; +use crate::hash::Hash; use crate::pkcs1v15; use crate::pss; @@ -113,7 +113,7 @@ pub(crate) struct CRTValue { impl From for RSAPublicKey { fn from(mut private_key: RSAPrivateKey) -> Self { - let mut broken_key = RSAPublicKey { + let broken_key = RSAPublicKey { // Fast, no-allocation creation of a biguint. n: BigUint::new_native(Default::default()), e: BigUint::new_native(Default::default()) @@ -167,6 +167,20 @@ impl RSAPublicKey { ) -> RsaResult<()> { pkcs1v15::verify(self, hash, hashed, sig) } + + /// Verify that the given signature is valid using the PSS padding scheme. + /// + /// The first parameter should be a pre-hashed message, using D as the + /// hashing scheme. + /// + /// The salt length is auto-detected. + pub fn verify_pss( + &self, + hashed: &[u8], + sig: &[u8] + ) -> RsaResult<()> { + pss::verify::(self, hashed, sig) + } } impl RSAPrivateKey { @@ -331,6 +345,25 @@ impl RSAPrivateKey { ) -> RsaResult> { pkcs1v15::sign(Some(rng), self, hash, digest) } + + /// Sign the given pre-hashed message using the PSS padding scheme. The + /// message should be hashed using the Digest algorithm passed as a generic + /// argument. + /// + /// RNG is used for PSS salt generation, and if `blind` is true, it will + /// also be used to blind the RSA encryption. + /// + /// The length of the salt can be controlled with the salt_len parameter. If + /// it is None, then it will be calculated to be as large as possible. + pub fn sign_pss( + &self, + rng: &mut R, + digest: &[u8], + salt_len: Option, + blind: bool + ) -> RsaResult> { + pss::sign::(rng, self, digest, salt_len, blind) + } } /// Check that the public key is well formed and has an exponent within acceptable bounds. diff --git a/src/pss.rs b/src/pss.rs index bcbf5682..e3fe1ba8 100644 --- a/src/pss.rs +++ b/src/pss.rs @@ -1,19 +1,16 @@ use crate::pkcs1v15::copy_with_left_pad; use crate::internals; -use crate::hash::{Hashes, Hash}; -use crate::key::{RSAPrivateKey, PublicKey}; +use crate::key::{RSAPrivateKey, RSAPublicKey}; use crate::errors::{Error, Result}; use std::vec::Vec; use num_bigint::BigUint; use subtle::ConstantTimeEq; -use sha2::{Digest, Sha256}; -use sha1::Sha1; +use digest::Digest; use rand::Rng; -pub fn verify( - pub_key: &K, - hash: &Hashes, +pub fn verify( + pub_key: &RSAPublicKey, hashed: &[u8], sig: &[u8]) -> Result<()> { @@ -33,15 +30,7 @@ pub fn verify( let mut em = vec![0; em_len]; copy_with_left_pad(&mut em, &m); - match hash { - Hashes::SHA1 => { - emsa_pss_verify(hashed, &mut em, em_bits, None, Sha1::new()) - }, - Hashes::SHA2_256 => { - emsa_pss_verify(hashed, &mut em, em_bits, None, Sha256::new()) - }, - _ => unimplemented!() - } + emsa_pss_verify::(hashed, &mut em, em_bits, None) } @@ -49,15 +38,15 @@ pub fn verify( /// 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 fn sign(rng: &mut T, priv_key: &RSAPrivateKey, hash: &Hashes, hashed: &[u8], salt_len: Option) -> Result> { +pub fn sign(rng: &mut T, priv_key: &RSAPrivateKey, hashed: &[u8], salt_len: Option, blind: bool) -> Result> { let salt_len = salt_len.unwrap_or_else(|| { - (priv_key.n().bits() + 7) / 8 - 2 - hash.size() + (priv_key.n().bits() + 7) / 8 - 2 - H::output_size() }); let mut salt = vec![0; salt_len]; rng.fill(&mut salt[..]); - return sign_pss_with_salt(rng, priv_key, hash, hashed, &salt) + return sign_pss_with_salt::<_, H>(rng, priv_key, hashed, &salt, blind) } @@ -65,28 +54,27 @@ pub fn sign(rng: &mut T, priv_key: &RSAPrivateKey, hash: &Hashes, hashed // 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(rng: &mut T, priv_key: &RSAPrivateKey, hash: &Hashes, hashed: &[u8], salt: &[u8]) -> Result> { +fn sign_pss_with_salt(rng: &mut T, priv_key: &RSAPrivateKey, hashed: &[u8], salt: &[u8], blind: bool) -> Result> { let n_bits = priv_key.n().bits(); let mut em = vec![0; ((n_bits - 1) + 7) / 8]; - match hash { - Hashes::SHA1 => { - emsa_pss_encode(&mut em, hashed, n_bits - 1, salt, Sha1::new())?; - }, - Hashes::SHA2_256 => { - emsa_pss_encode(&mut em, hashed, n_bits - 1, salt, Sha256::new())?; - }, - _ => unimplemented!() - } + emsa_pss_encode::(&mut em, hashed, n_bits - 1, salt)?; - let mut m = BigUint::from_bytes_be(&em); - let mut c = internals::decrypt_and_check(Some(rng), priv_key, &m)?.to_bytes_be(); + let m = BigUint::from_bytes_be(&em); + + let blind_rng = if blind { + Some(rng) + } else { + None + }; + + let c = internals::decrypt_and_check(blind_rng, priv_key, &m)?.to_bytes_be(); let mut s = vec![0; (n_bits + 7) / 8]; copy_with_left_pad(&mut s, &c); return Ok(s) } -fn emsa_pss_encode(em: &mut [u8], m_hash: &[u8], em_bits: usize, salt: &[u8], mut hash: H) -> Result<()> { +fn emsa_pss_encode(em: &mut [u8], m_hash: &[u8], em_bits: usize, salt: &[u8]) -> Result<()> { // See [1], section 9.1.1 let h_len = H::output_size(); let s_len = salt.len(); @@ -125,11 +113,13 @@ fn emsa_pss_encode(em: &mut [u8], m_hash: &[u8], em_bits: usize, salt // // 6. Let H = Hash(M'), an octet string of length h_len. let prefix = [0u8; 8]; + let mut hash = H::new(); + hash.input(&prefix); hash.input(m_hash); hash.input(salt); - let hashed = hash.result_reset(); + let hashed = hash.result(); h.copy_from_slice(&hashed); // 7. Generate an octet string PS consisting of em_len - s_len - h_len - 2 @@ -143,7 +133,7 @@ fn emsa_pss_encode(em: &mut [u8], m_hash: &[u8], em_bits: usize, salt // 9. Let dbMask = MGF(H, emLen - hLen - 1). // // 10. Let maskedDB = DB \xor dbMask. - mgf1_xor(db, &mut hash, &h); + mgf1_xor(db, &mut H::new(), &h); // 11. Set the leftmost 8 * em_len - em_bits bits of the leftmost octet in // maskedDB to zero. @@ -155,7 +145,7 @@ fn emsa_pss_encode(em: &mut [u8], m_hash: &[u8], em_bits: usize, salt return Ok(()) } -fn emsa_pss_verify(m_hash: &[u8], em: &mut [u8], em_bits: usize, s_len: Option, mut hash: H) -> Result<()> { +fn emsa_pss_verify(m_hash: &[u8], em: &mut [u8], em_bits: usize, s_len: Option) -> Result<()> { // 1. If the length of M is greater than the input limitation for the // hash function (2^61 - 1 octets for SHA-1), output "inconsistent" // and stop. @@ -193,7 +183,7 @@ fn emsa_pss_verify(m_hash: &[u8], em: &mut [u8], em_bits: usize, s_le // 7. Let dbMask = MGF(H, em_len - h_len - 1) // // 8. Let DB = maskedDB \xor dbMask - mgf1_xor(db, &mut hash, &*h); + mgf1_xor(db, &mut H::new(), &*h); // 9. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in DB @@ -237,6 +227,7 @@ fn emsa_pss_verify(m_hash: &[u8], em: &mut [u8], em_bits: usize, s_le // 13. Let H' = Hash(M'), an octet string of length hLen. let prefix = [0u8; 8]; + let mut hash = H::new(); hash.input(prefix); hash.input(m_hash); hash.input(salt); @@ -283,6 +274,8 @@ fn inc_counter(counter: &mut [u8]) { } /// Mask generation function +/// +/// Will reset the Digest before returning. fn mgf1_xor(out: &mut [u8], digest: &mut T, seed: &[u8]) { let mut counter = vec![0u8; 4]; let mut i = 0; @@ -310,8 +303,7 @@ fn mgf1_xor(out: &mut [u8], digest: &mut T, seed: &[u8]) { #[cfg(test)] mod test { - use crate::{PaddingScheme, RSAPrivateKey, RSAPublicKey, PublicKey}; - use crate::hash::Hashes; + use crate::{RSAPrivateKey, RSAPublicKey}; use num_bigint::BigUint; use num_traits::{FromPrimitive, Num}; @@ -355,7 +347,7 @@ mod test { let sig = hex::decode(test[1]).unwrap(); pub_key - .verify(PaddingScheme::PSS, Some(&Hashes::SHA1), &digest, &sig) + .verify_pss::(&digest, &sig) .expect("failed to verify"); } } @@ -369,11 +361,11 @@ mod test { for test in &tests { let digest = Sha1::digest(test.as_bytes()).to_vec(); let sig = priv_key - .sign_blinded(&mut thread_rng(), PaddingScheme::PSS, Some(&Hashes::SHA1), &digest) + .sign_pss::(&mut thread_rng(), &digest, None, true) .expect("failed to sign"); priv_key - .verify(PaddingScheme::PSS, Some(&Hashes::SHA1), &digest, &sig) + .verify_pss::(&digest, &sig) .expect("failed to verify"); } } From 30220cfd4484ec14e27c21cefb1652d401e3ce67 Mon Sep 17 00:00:00 2001 From: roblabla Date: Thu, 26 Sep 2019 13:16:25 +0000 Subject: [PATCH 6/7] Fix the tests --- Cargo.toml | 5 ++--- src/key.rs | 6 ++++-- src/lib.rs | 10 +++------- src/padding.rs | 7 ------- src/pkcs1v15.rs | 14 ++++++-------- 5 files changed, 15 insertions(+), 27 deletions(-) delete mode 100644 src/padding.rs diff --git a/Cargo.toml b/Cargo.toml index 272b6360..2e77bb5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,9 +21,7 @@ rand = "0.6.5" byteorder = "1.3.1" failure = "0.1.5" subtle = "2.0.0" -signature = { version = "0.2.0" } -sha2 = { version = "0.8" } -sha-1 = { version = "0.8.1" } +digest = "0.8" [dependencies.zeroize] version = "0.6" @@ -39,6 +37,7 @@ features = ["std", "derive"] base64 = "0.10.1" hex = "0.3.2" serde_test = "1.0.89" +sha-1 = "0.8.1" [[bench]] diff --git a/src/key.rs b/src/key.rs index 128733e9..e7c3b1c1 100644 --- a/src/key.rs +++ b/src/key.rs @@ -390,8 +390,10 @@ mod tests { #[test] fn test_from_into() { let private_key = RSAPrivateKey { - n: BigUint::from_u64(100).unwrap(), - e: BigUint::from_u64(200).unwrap(), + pubkey_components: RSAPublicKey { + n: BigUint::from_u64(100).unwrap(), + e: BigUint::from_u64(200).unwrap(), + }, d: BigUint::from_u64(123).unwrap(), primes: vec![], precomputed: None, diff --git a/src/lib.rs b/src/lib.rs index d292f257..ddb75e86 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ //! extern crate rsa; //! extern crate rand; //! -//! use rsa::{PublicKey, RSAPrivateKey, PaddingScheme}; +//! use rsa::RSAPrivateKey; //! use rand::rngs::OsRng; //! //! let mut rng = OsRng::new().expect("no secure randomness available"); @@ -17,11 +17,11 @@ //! //! // Encrypt //! let data = b"hello world"; -//! let enc_data = key.encrypt(&mut rng, PaddingScheme::PKCS1v15, &data[..]).expect("failed to encrypt"); +//! let enc_data = key.encrypt_pkcs1v15(&mut rng, &data[..]).expect("failed to encrypt"); //! assert_ne!(&data[..], &enc_data[..]); //! //! // Decrypt -//! let dec_data = key.decrypt(PaddingScheme::PKCS1v15, &enc_data).expect("failed to decrypt"); +//! let dec_data = key.decrypt_pkcs1v15(&enc_data).expect("failed to decrypt"); //! assert_eq!(&data[..], &dec_data[..]); //! ``` //! @@ -57,15 +57,11 @@ pub mod errors; /// Supported hash functions. pub mod hash; -/// Supported padding schemes. -pub mod padding; - mod key; mod pkcs1v15; mod pss; pub use self::key::{RSAPrivateKey, RSAPublicKey}; -pub use self::padding::PaddingScheme; // Optionally expose internals if requested via feature-flag. diff --git a/src/padding.rs b/src/padding.rs deleted file mode 100644 index 4525ecb2..00000000 --- a/src/padding.rs +++ /dev/null @@ -1,7 +0,0 @@ -/// Available padding schemes. -#[derive(Debug, Clone, Copy)] -pub enum PaddingScheme { - PKCS1v15, - OAEP, - PSS, -} diff --git a/src/pkcs1v15.rs b/src/pkcs1v15.rs index 02dd2939..d3dc1771 100644 --- a/src/pkcs1v15.rs +++ b/src/pkcs1v15.rs @@ -266,7 +266,6 @@ mod tests { use crate::hash::Hashes; use crate::key::RSAPublicKey; - use crate::padding::PaddingScheme; #[test] fn test_non_zero_bytes() { @@ -323,7 +322,7 @@ mod tests { for test in &tests { let out = priv_key - .decrypt(PaddingScheme::PKCS1v15, &base64::decode(test[0]).unwrap()) + .decrypt_pkcs1v15(&base64::decode(test[0]).unwrap()) .unwrap(); assert_eq!(out, test[1].as_bytes()); } @@ -364,16 +363,15 @@ mod tests { let expected = hex::decode(test[1]).unwrap(); let out = priv_key - .sign(PaddingScheme::PKCS1v15, Some(&Hashes::SHA1), &digest) + .sign_pkcs1v15(Some(&Hashes::SHA1), &digest) .unwrap(); assert_ne!(out, digest); assert_eq!(out, expected); let mut rng = thread_rng(); let out2 = priv_key - .sign_blinded( + .sign_pkcs1v15_blinded( &mut rng, - PaddingScheme::PKCS1v15, Some(&Hashes::SHA1), &digest, ) @@ -396,7 +394,7 @@ mod tests { let sig = hex::decode(test[1]).unwrap(); pub_key - .verify(PaddingScheme::PKCS1v15, Some(&Hashes::SHA1), &digest, &sig) + .verify_pkcs1v15(Some(&Hashes::SHA1), &digest, &sig) .expect("failed to verify"); } } @@ -408,13 +406,13 @@ mod tests { let priv_key = get_private_key(); let sig = priv_key - .sign::(PaddingScheme::PKCS1v15, None, msg) + .sign_pkcs1v15::(None, msg) .unwrap(); assert_eq!(expected_sig, sig); let pub_key: RSAPublicKey = priv_key.into(); pub_key - .verify(PaddingScheme::PKCS1v15, None, msg, &sig) + .verify_pkcs1v15::(None, msg, &sig) .expect("failed to verify"); } } From 610c4cc95f614dad40651e585756858cca4f6e20 Mon Sep 17 00:00:00 2001 From: roblabla Date: Thu, 26 Sep 2019 13:22:46 +0000 Subject: [PATCH 7/7] Move copy_with_left_pad to algorithms --- src/algorithms.rs | 10 ++++++++++ src/pkcs1v15.rs | 11 +---------- src/pss.rs | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/algorithms.rs b/src/algorithms.rs index a02e884b..5b6d3850 100644 --- a/src/algorithms.rs +++ b/src/algorithms.rs @@ -110,3 +110,13 @@ pub fn generate_multi_prime_key( primes, )) } + +#[inline] +pub fn copy_with_left_pad(dest: &mut [u8], src: &[u8]) { + // left pad with zeros + let padding_bytes = dest.len() - src.len(); + for el in dest.iter_mut().take(padding_bytes) { + *el = 0; + } + dest[padding_bytes..].copy_from_slice(src); +} \ No newline at end of file diff --git a/src/pkcs1v15.rs b/src/pkcs1v15.rs index d3dc1771..f85cd76d 100644 --- a/src/pkcs1v15.rs +++ b/src/pkcs1v15.rs @@ -3,6 +3,7 @@ use rand::Rng; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use zeroize::Zeroize; +use crate::algorithms::copy_with_left_pad; use crate::errors::{Error, Result}; use crate::hash::Hash; use crate::internals; @@ -170,16 +171,6 @@ fn hash_info(hash: Option<&H>, digest_len: usize) -> Result<(usize, &'s } } -#[inline] -pub fn copy_with_left_pad(dest: &mut [u8], src: &[u8]) { - // left pad with zeros - let padding_bytes = dest.len() - src.len(); - for el in dest.iter_mut().take(padding_bytes) { - *el = 0; - } - dest[padding_bytes..].copy_from_slice(src); -} - /// Decrypts ciphertext using `priv_key` and blinds the operation if /// `rng` is given. It returns one or zero in valid that indicates whether the /// plaintext was correctly structured. In either case, the plaintext is diff --git a/src/pss.rs b/src/pss.rs index e3fe1ba8..f136f303 100644 --- a/src/pss.rs +++ b/src/pss.rs @@ -1,4 +1,4 @@ -use crate::pkcs1v15::copy_with_left_pad; +use crate::algorithms::copy_with_left_pad; use crate::internals; use crate::key::{RSAPrivateKey, RSAPublicKey}; use crate::errors::{Error, Result};