diff --git a/src/key.rs b/src/key.rs index 9b0eed1..2208f93 100644 --- a/src/key.rs +++ b/src/key.rs @@ -12,6 +12,7 @@ use crate::errors::{Error, Result}; use crate::hash::Hash; use crate::padding::PaddingScheme; use crate::pkcs1v15; +use crate::raw::EncryptionPrimitive; lazy_static! { static ref MIN_PUB_EXPONENT: BigUint = BigUint::from_u64(2).unwrap(); @@ -126,7 +127,7 @@ impl From for RSAPublicKey { } /// Generic trait for operations on a public key. -pub trait PublicKey { +pub trait PublicKey: EncryptionPrimitive { /// Returns the modulus of the key. fn n(&self) -> &BigUint; /// Returns the public exponent of the key. diff --git a/src/lib.rs b/src/lib.rs index d377316..a6d7781 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,12 +58,13 @@ pub mod hash; /// Supported padding schemes. pub mod padding; -#[cfg(feature="pem")] +#[cfg(feature = "pem")] pub use pem; mod key; -mod pkcs1v15; mod parse; +mod pkcs1v15; +mod raw; pub use self::key::{PublicKey, RSAPrivateKey, RSAPublicKey}; pub use self::padding::PaddingScheme; diff --git a/src/pkcs1v15.rs b/src/pkcs1v15.rs index aa75338..b09d8ce 100644 --- a/src/pkcs1v15.rs +++ b/src/pkcs1v15.rs @@ -1,12 +1,10 @@ -use num_bigint::BigUint; use rand::Rng; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; -use zeroize::Zeroize; use crate::errors::{Error, Result}; use crate::hash::Hash; -use crate::internals; use crate::key::{self, PublicKey, RSAPrivateKey}; +use crate::raw::DecryptionPrimitive; // Encrypts the given message with RSA and the padding // scheme from PKCS#1 v1.5. The message must be no longer than the @@ -27,17 +25,7 @@ pub fn encrypt(rng: &mut R, pub_key: &K, msg: &[u8]) -> Re em[k - msg.len() - 1] = 0; em[k - msg.len()..].copy_from_slice(msg); - { - let mut m = BigUint::from_bytes_be(&em); - let mut c = internals::encrypt(pub_key, &m).to_bytes_be(); - copy_with_left_pad(&mut em, &c); - - // clear out tmp values - m.zeroize(); - c.zeroize(); - } - - Ok(em) + pub_key.raw_encryption_primitive(&em) } /// Decrypts a plaintext using RSA and the padding scheme from PKCS#1 v1.5. @@ -100,18 +88,7 @@ pub fn sign( em[k - t_len..k - hash_len].copy_from_slice(&prefix); em[k - hash_len..k].copy_from_slice(hashed); - { - let mut m = BigUint::from_bytes_be(&em); - let mut c = internals::decrypt_and_check(rng, priv_key, &m)?.to_bytes_be(); - - copy_with_left_pad(&mut em, &c); - - // clear tmp values - m.zeroize(); - c.zeroize(); - } - - Ok(em) + priv_key.raw_decryption_primitive(rng, &em) } /// Verifies an RSA PKCS#1 v1.5 signature. @@ -130,11 +107,7 @@ pub fn verify( return Err(Error::Verification); } - let em = { - let c = BigUint::from_bytes_be(sig); - let m = internals::encrypt(pub_key, &c).to_bytes_be(); - internals::left_pad(&m, k) - }; + let em = pub_key.raw_encryption_primitive(sig)?; // EM = 0x00 || 0x01 || PS || 0x00 || T let mut ok = em[0].ct_eq(&0u8); @@ -170,16 +143,6 @@ fn hash_info(hash: Option<&H>, digest_len: usize) -> Result<(usize, Vec } } -#[inline] -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 @@ -197,16 +160,7 @@ fn decrypt_inner( return Err(Error::Decryption); } - let em = { - let mut c = BigUint::from_bytes_be(ciphertext); - let mut m = internals::decrypt(rng, priv_key, &c)?; - let em = internals::left_pad(&m.to_bytes_be(), k); - - c.zeroize(); - m.zeroize(); - - em - }; + let em = priv_key.raw_decryption_primitive(rng, ciphertext)?; let first_byte_is_zero = em[0].ct_eq(&0u8); let second_byte_is_two = em[1].ct_eq(&2u8); @@ -259,6 +213,7 @@ mod tests { use super::*; use base64; use hex; + use num_bigint::BigUint; use num_traits::FromPrimitive; use num_traits::Num; use rand::thread_rng; diff --git a/src/raw.rs b/src/raw.rs new file mode 100644 index 0000000..98e7770 --- /dev/null +++ b/src/raw.rs @@ -0,0 +1,85 @@ +use num_bigint::BigUint; +use rand::Rng; +use zeroize::Zeroize; + +use crate::errors::Result; +use crate::internals; +use crate::key::{PublicKey, RSAPrivateKey, RSAPublicKey}; + +pub trait EncryptionPrimitive { + /// Do NOT use directly! Only for implementors. + fn raw_encryption_primitive(&self, plaintext: &[u8]) -> Result>; +} + +pub trait DecryptionPrimitive { + /// Do NOT use directly! Only for implementors. + fn raw_decryption_primitive( + &self, + rng: Option<&mut R>, + ciphertext: &[u8], + ) -> Result>; +} + +impl EncryptionPrimitive for RSAPublicKey { + fn raw_encryption_primitive(&self, plaintext: &[u8]) -> Result> { + let mut m = BigUint::from_bytes_be(plaintext); + let mut c = internals::encrypt(self, &m); + let mut c_bytes = c.to_bytes_be(); + let ciphertext = internals::left_pad(&c_bytes, self.size()); + + // clear out tmp values + m.zeroize(); + c.zeroize(); + c_bytes.zeroize(); + + Ok(ciphertext) + } +} + +impl<'a> EncryptionPrimitive for &'a RSAPublicKey { + fn raw_encryption_primitive(&self, plaintext: &[u8]) -> Result> { + (*self).raw_encryption_primitive(plaintext) + } +} + +impl EncryptionPrimitive for RSAPrivateKey { + fn raw_encryption_primitive(&self, plaintext: &[u8]) -> Result> { + let mut m = BigUint::from_bytes_be(plaintext); + let mut c = internals::encrypt(self, &m); + let mut c_bytes = c.to_bytes_be(); + let ciphertext = internals::left_pad(&c_bytes, self.size()); + + // clear out tmp values + m.zeroize(); + c.zeroize(); + c_bytes.zeroize(); + + Ok(ciphertext) + } +} + +impl<'a> EncryptionPrimitive for &'a RSAPrivateKey { + fn raw_encryption_primitive(&self, plaintext: &[u8]) -> Result> { + (*self).raw_encryption_primitive(plaintext) + } +} + +impl DecryptionPrimitive for RSAPrivateKey { + fn raw_decryption_primitive( + &self, + rng: Option<&mut R>, + ciphertext: &[u8], + ) -> Result> { + let mut c = BigUint::from_bytes_be(ciphertext); + let mut m = internals::decrypt_and_check(rng, self, &c)?; + let mut m_bytes = m.to_bytes_be(); + let plaintext = internals::left_pad(&m_bytes, self.size()); + + // clear tmp values + c.zeroize(); + m.zeroize(); + m_bytes.zeroize(); + + Ok(plaintext) + } +}