From 819f79fb114d7543e307b3bad749a250bec1224a Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 2 Jan 2020 21:51:43 -0500 Subject: [PATCH 1/2] Introduce raw::{DecryptionPrimitive, EncryptionPrimitive} --- src/key.rs | 3 +- src/lib.rs | 1 + src/pkcs1v15.rs | 57 ++++----------------------------- src/raw.rs | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 52 deletions(-) create mode 100644 src/raw.rs diff --git a/src/key.rs b/src/key.rs index fd39d3e9..26238b2a 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 a96c2a5c..01e1c4f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,7 @@ pub mod padding; mod key; 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 aa753384..b09d8ced 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 00000000..98e7770b --- /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) + } +} From 8c45b3f9cbafe2a06f56a8799a771051546dd809 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 2 Jan 2020 22:28:54 -0500 Subject: [PATCH 2/2] Introduce key::{PrivateKey, PublicKeyParts} RSAPrivateKey no longer implements PublicKey. Instead, RSAPublicKey::from(RSAPrivateKey) should be used to obtain a public key. --- src/internals.rs | 8 ++--- src/key.rs | 83 +++++++++++++++++------------------------------- src/lib.rs | 9 +++--- src/pkcs1v15.rs | 2 +- src/raw.rs | 34 +++++++------------- 5 files changed, 51 insertions(+), 85 deletions(-) diff --git a/src/internals.rs b/src/internals.rs index 5c034f59..eb291288 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::{PublicKeyParts, 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: &K, 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: &K, 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: impl PublicKeyParts, m: &BigUint, unblinder: &BigUint) -> BigUint { (m * unblinder) % key.n() } diff --git a/src/key.rs b/src/key.rs index 26238b2a..ea460039 100644 --- a/src/key.rs +++ b/src/key.rs @@ -12,13 +12,27 @@ use crate::errors::{Error, Result}; use crate::hash::Hash; use crate::padding::PaddingScheme; use crate::pkcs1v15; -use crate::raw::EncryptionPrimitive; +use crate::raw::{DecryptionPrimitive, EncryptionPrimitive}; lazy_static! { static ref MIN_PUB_EXPONENT: BigUint = BigUint::from_u64(2).unwrap(); static ref MAX_PUB_EXPONENT: BigUint = BigUint::from_u64(1 << (31 - 1)).unwrap(); } +pub trait PublicKeyParts { + /// 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 + } +} + +pub trait PrivateKey: DecryptionPrimitive + PublicKeyParts {} + /// Represents the public part of an RSA key. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] @@ -127,17 +141,7 @@ impl From for RSAPublicKey { } /// Generic trait for operations on a public key. -pub trait PublicKey: EncryptionPrimitive { - /// 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 - } - +pub trait PublicKey: EncryptionPrimitive + PublicKeyParts { /// Encrypt the given message. fn encrypt(&self, rng: &mut R, padding: PaddingScheme, msg: &[u8]) -> Result>; @@ -154,7 +158,7 @@ pub trait PublicKey: EncryptionPrimitive { ) -> Result<()>; } -impl PublicKey for RSAPublicKey { +impl PublicKeyParts for RSAPublicKey { fn n(&self) -> &BigUint { &self.n } @@ -162,6 +166,9 @@ impl PublicKey for RSAPublicKey { fn e(&self) -> &BigUint { &self.e } +} + +impl PublicKey for RSAPublicKey { fn encrypt(&self, rng: &mut R, padding: PaddingScheme, msg: &[u8]) -> Result> { match padding { PaddingScheme::PKCS1v15 => pkcs1v15::encrypt(rng, self, msg), @@ -195,7 +202,7 @@ impl RSAPublicKey { } } -impl<'a> PublicKey for &'a RSAPublicKey { +impl<'a> PublicKeyParts for &'a RSAPublicKey { fn n(&self) -> &BigUint { &self.n } @@ -203,7 +210,9 @@ impl<'a> PublicKey for &'a RSAPublicKey { fn e(&self) -> &BigUint { &self.e } +} +impl<'a> PublicKey for &'a RSAPublicKey { fn encrypt(&self, rng: &mut R, padding: PaddingScheme, msg: &[u8]) -> Result> { (*self).encrypt(rng, padding, msg) } @@ -219,7 +228,7 @@ impl<'a> PublicKey for &'a RSAPublicKey { } } -impl PublicKey for RSAPrivateKey { +impl PublicKeyParts for RSAPrivateKey { fn n(&self) -> &BigUint { &self.n } @@ -227,31 +236,11 @@ impl PublicKey for RSAPrivateKey { 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<&H>, - hashed: &[u8], - sig: &[u8], - ) -> Result<()> { - match padding { - PaddingScheme::PKCS1v15 => pkcs1v15::verify(self, hash, hashed, sig), - PaddingScheme::PSS => unimplemented!("not yet implemented"), - _ => Err(Error::InvalidPaddingScheme), - } - } } -impl<'a> PublicKey for &'a RSAPrivateKey { +impl PrivateKey for RSAPrivateKey {} + +impl<'a> PublicKeyParts for &'a RSAPrivateKey { fn n(&self) -> &BigUint { &self.n } @@ -259,22 +248,10 @@ impl<'a> PublicKey for &'a RSAPrivateKey { 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<&H>, - hashed: &[u8], - sig: &[u8], - ) -> Result<()> { - (*self).verify(padding, hash, hashed, sig) - } } +impl<'a> PrivateKey for &'a 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 { @@ -458,7 +435,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: &impl PublicKeyParts) -> Result<()> { if public_key.e() < &*MIN_PUB_EXPONENT { return Err(Error::PublicExponentTooSmall); } diff --git a/src/lib.rs b/src/lib.rs index 01e1c4f8..df25c44f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,20 +8,21 @@ //! extern crate rsa; //! extern crate rand; //! -//! use rsa::{PublicKey, RSAPrivateKey, PaddingScheme}; +//! use rsa::{PublicKey, RSAPrivateKey, RSAPublicKey, PaddingScheme}; //! use rand::rngs::OsRng; //! //! let mut rng = OsRng; //! let bits = 2048; -//! let key = RSAPrivateKey::new(&mut rng, bits).expect("failed to generate a key"); +//! let private_key = RSAPrivateKey::new(&mut rng, bits).expect("failed to generate a key"); +//! let public_key = RSAPublicKey::from(private_key.clone()); //! //! // Encrypt //! let data = b"hello world"; -//! let enc_data = key.encrypt(&mut rng, PaddingScheme::PKCS1v15, &data[..]).expect("failed to encrypt"); +//! let enc_data = public_key.encrypt(&mut rng, PaddingScheme::PKCS1v15, &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 = private_key.decrypt(PaddingScheme::PKCS1v15, &enc_data).expect("failed to decrypt"); //! assert_eq!(&data[..], &dec_data[..]); //! ``` //! diff --git a/src/pkcs1v15.rs b/src/pkcs1v15.rs index b09d8ced..8989d919 100644 --- a/src/pkcs1v15.rs +++ b/src/pkcs1v15.rs @@ -3,7 +3,7 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use crate::errors::{Error, Result}; use crate::hash::Hash; -use crate::key::{self, PublicKey, RSAPrivateKey}; +use crate::key::{self, PublicKey, PublicKeyParts, RSAPrivateKey}; use crate::raw::DecryptionPrimitive; // Encrypts the given message with RSA and the padding diff --git a/src/raw.rs b/src/raw.rs index 98e7770b..496b7a7c 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -4,7 +4,7 @@ use zeroize::Zeroize; use crate::errors::Result; use crate::internals; -use crate::key::{PublicKey, RSAPrivateKey, RSAPublicKey}; +use crate::key::{PublicKeyParts, RSAPrivateKey, RSAPublicKey}; pub trait EncryptionPrimitive { /// Do NOT use directly! Only for implementors. @@ -42,28 +42,6 @@ impl<'a> EncryptionPrimitive for &'a RSAPublicKey { } } -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, @@ -83,3 +61,13 @@ impl DecryptionPrimitive for RSAPrivateKey { Ok(plaintext) } } + +impl<'a> DecryptionPrimitive for &'a RSAPrivateKey { + fn raw_decryption_primitive( + &self, + rng: Option<&mut R>, + ciphertext: &[u8], + ) -> Result> { + (*self).raw_decryption_primitive(rng, ciphertext) + } +}