Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor PaddingScheme into a trait #244

Merged
merged 5 commits into from Jan 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 3 additions & 9 deletions benches/key.rs
Expand Up @@ -6,7 +6,7 @@ use base64ct::{Base64, Encoding};
use num_bigint::BigUint;
use num_traits::{FromPrimitive, Num};
use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng};
use rsa::{PaddingScheme, RsaPrivateKey};
use rsa::{Pkcs1v15Encrypt, Pkcs1v15Sign, RsaPrivateKey};
use sha2::{Digest, Sha256};
use test::Bencher;

Expand All @@ -31,9 +31,7 @@ fn bench_rsa_2048_pkcsv1_decrypt(b: &mut Bencher) {
let x = Base64::decode_vec(DECRYPT_VAL).unwrap();

b.iter(|| {
let res = priv_key
.decrypt(PaddingScheme::new_pkcs1v15_encrypt(), &x)
.unwrap();
let res = priv_key.decrypt(Pkcs1v15Encrypt, &x).unwrap();
test::black_box(res);
});
}
Expand All @@ -46,11 +44,7 @@ fn bench_rsa_2048_pkcsv1_sign_blinded(b: &mut Bencher) {

b.iter(|| {
let res = priv_key
.sign_blinded(
&mut rng,
PaddingScheme::new_pkcs1v15_sign::<Sha256>(),
&digest,
)
.sign_with_rng(&mut rng, Pkcs1v15Sign::new::<Sha256>(), &digest)
.unwrap();
test::black_box(res);
});
Expand Down
176 changes: 43 additions & 133 deletions src/key.rs
Expand Up @@ -13,9 +13,8 @@ use crate::algorithms::{generate_multi_prime_key, generate_multi_prime_key_with_
use crate::dummy_rng::DummyRng;
use crate::errors::{Error, Result};

use crate::padding::PaddingScheme;
use crate::padding::{PaddingScheme, SignatureScheme};
use crate::raw::{DecryptionPrimitive, EncryptionPrimitive};
use crate::{oaep, pkcs1v15, pss};

/// Components of an RSA public key.
pub trait PublicKeyParts {
Expand Down Expand Up @@ -173,18 +172,20 @@ impl From<&RsaPrivateKey> for RsaPublicKey {
/// Generic trait for operations on a public key.
pub trait PublicKey: EncryptionPrimitive + PublicKeyParts {
/// Encrypt the given message.
fn encrypt<R: CryptoRngCore>(
fn encrypt<R: CryptoRngCore, P: PaddingScheme>(
&self,
rng: &mut R,
padding: PaddingScheme,
padding: P,
msg: &[u8],
) -> Result<Vec<u8>>;

/// Verify a signed message.
/// `hashed`must be the result of hashing the input using the hashing function
///
/// `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<()>;
///
/// If the message is valid `Ok(())` is returned, otherwise an `Err` indicating failure.
fn verify<S: SignatureScheme>(&self, scheme: S, hashed: &[u8], sig: &[u8]) -> Result<()>;
}

impl PublicKeyParts for RsaPublicKey {
Expand All @@ -198,36 +199,17 @@ impl PublicKeyParts for RsaPublicKey {
}

impl PublicKey for RsaPublicKey {
fn encrypt<R: CryptoRngCore>(
fn encrypt<R: CryptoRngCore, P: PaddingScheme>(
&self,
rng: &mut R,
padding: PaddingScheme,
padding: P,
msg: &[u8],
) -> Result<Vec<u8>> {
match padding {
PaddingScheme::PKCS1v15Encrypt => pkcs1v15::encrypt(rng, self, msg),
PaddingScheme::OAEP {
mut digest,
mut mgf_digest,
label,
} => oaep::encrypt(rng, self, msg, &mut *digest, &mut *mgf_digest, label),
_ => Err(Error::InvalidPaddingScheme),
}
padding.encrypt(rng, self, msg)
}

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),
}
fn verify<S: SignatureScheme>(&self, scheme: S, hashed: &[u8], sig: &[u8]) -> Result<()> {
scheme.verify(self, hashed, sig)
}
}

Expand Down Expand Up @@ -448,113 +430,44 @@ impl RsaPrivateKey {
}

/// Decrypt the given message.
pub fn decrypt(&self, padding: PaddingScheme, ciphertext: &[u8]) -> Result<Vec<u8>> {
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::PKCS1v15Encrypt => {
pkcs1v15::decrypt::<DummyRng, _>(None, self, ciphertext)
}
PaddingScheme::OAEP {
mut digest,
mut mgf_digest,
label,
} => oaep::decrypt::<DummyRng, _>(
None,
self,
ciphertext,
&mut *digest,
&mut *mgf_digest,
label,
),
_ => Err(Error::InvalidPaddingScheme),
}
pub fn decrypt<P: PaddingScheme>(&self, padding: P, ciphertext: &[u8]) -> Result<Vec<u8>> {
padding.decrypt(Option::<&mut DummyRng>::None, self, ciphertext)
}

/// Decrypt the given message.
///
/// Uses `rng` to blind the decryption process.
pub fn decrypt_blinded<R: CryptoRngCore>(
pub fn decrypt_blinded<R: CryptoRngCore, P: PaddingScheme>(
&self,
rng: &mut R,
padding: PaddingScheme,
padding: P,
ciphertext: &[u8],
) -> Result<Vec<u8>> {
match padding {
PaddingScheme::PKCS1v15Encrypt => pkcs1v15::decrypt(Some(rng), self, ciphertext),
PaddingScheme::OAEP {
mut digest,
mut mgf_digest,
label,
} => oaep::decrypt(
Some(rng),
self,
ciphertext,
&mut *digest,
&mut *mgf_digest,
label,
),
_ => Err(Error::InvalidPaddingScheme),
}
padding.decrypt(Some(rng), self, ciphertext)
}

/// Sign the given digest.
pub fn sign(&self, padding: PaddingScheme, digest_in: &[u8]) -> Result<Vec<u8>> {
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::<DummyRng, _>(None, self, prefix.as_ref(), digest_in)
}
_ => Err(Error::InvalidPaddingScheme),
}
pub fn sign<S: SignatureScheme>(&self, padding: S, digest_in: &[u8]) -> Result<Vec<u8>> {
padding.sign(Option::<&mut DummyRng>::None, self, digest_in)
}

/// Sign the given digest using the provided rng
/// Sign the given digest using the provided `rng`, which is used in the
/// following ways depending on the [`SignatureScheme`]:
///
/// Use `rng` for signature process.
pub fn sign_with_rng<R: CryptoRngCore>(
/// - [`Pkcs1v15Sign`][`crate::Pkcs1v15Sign`] padding: uses the RNG
/// to mask the private key operation with random blinding, which helps
/// mitigate sidechannel attacks.
/// - [`Pss`][`crate::Pss`] always requires randomness. Use
/// [`Pss::new`][`crate::Pss::new`] for a standard RSASSA-PSS signature, or
/// [`Pss::new_blinded`][`crate::Pss::new_blinded`] for RSA-BSSA blind
/// signatures.
pub fn sign_with_rng<R: CryptoRngCore, S: SignatureScheme>(
&self,
rng: &mut R,
padding: PaddingScheme,
padding: S,
digest_in: &[u8],
) -> Result<Vec<u8>> {
match padding {
PaddingScheme::PSS {
mut digest,
salt_len,
} => pss::sign::<R, _>(rng, false, self, digest_in, salt_len, &mut *digest),
_ => Err(Error::InvalidPaddingScheme),
}
}

/// Sign the given digest.
///
/// Use `rng` for blinding.
pub fn sign_blinded<R: CryptoRngCore>(
&self,
rng: &mut R,
padding: PaddingScheme,
digest_in: &[u8],
) -> Result<Vec<u8>> {
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::<R, _>(rng, true, self, digest_in, salt_len, &mut *digest),
_ => Err(Error::InvalidPaddingScheme),
}
padding.sign(Some(rng), self, digest_in)
}
}

Expand Down Expand Up @@ -591,6 +504,7 @@ fn check_public_with_max_size(public_key: &impl PublicKeyParts, max_size: usize)
mod tests {
use super::*;
use crate::internals;
use crate::oaep::Oaep;

use alloc::string::String;
use digest::{Digest, DynDigest};
Expand Down Expand Up @@ -965,20 +879,20 @@ mod tests {
let pub_key: RsaPublicKey = prk.into();

let ciphertext = if let Some(ref label) = label {
let padding = PaddingScheme::new_oaep_with_label::<D, _>(label);
let padding = Oaep::new_with_label::<D, _>(label);
pub_key.encrypt(&mut rng, padding, &input).unwrap()
} else {
let padding = PaddingScheme::new_oaep::<D>();
let padding = Oaep::new::<D>();
pub_key.encrypt(&mut rng, padding, &input).unwrap()
};

assert_ne!(input, ciphertext);
let blind: bool = rng.next_u32() < (1 << 31);

let padding = if let Some(ref label) = label {
PaddingScheme::new_oaep_with_label::<D, _>(label)
Oaep::new_with_label::<D, _>(label)
} else {
PaddingScheme::new_oaep::<D>()
Oaep::new::<D>()
};

let plaintext = if blind {
Expand Down Expand Up @@ -1013,20 +927,20 @@ mod tests {
let pub_key: RsaPublicKey = prk.into();

let ciphertext = if let Some(ref label) = label {
let padding = PaddingScheme::new_oaep_with_mgf_hash_with_label::<D, U, _>(label);
let padding = Oaep::new_with_mgf_hash_and_label::<D, U, _>(label);
pub_key.encrypt(&mut rng, padding, &input).unwrap()
} else {
let padding = PaddingScheme::new_oaep_with_mgf_hash::<D, U>();
let padding = Oaep::new_with_mgf_hash::<D, U>();
pub_key.encrypt(&mut rng, padding, &input).unwrap()
};

assert_ne!(input, ciphertext);
let blind: bool = rng.next_u32() < (1 << 31);

let padding = if let Some(ref label) = label {
PaddingScheme::new_oaep_with_mgf_hash_with_label::<D, U, _>(label)
Oaep::new_with_mgf_hash_and_label::<D, U, _>(label)
} else {
PaddingScheme::new_oaep_with_mgf_hash::<D, U>()
Oaep::new_with_mgf_hash::<D, U>()
};

let plaintext = if blind {
Expand All @@ -1044,17 +958,13 @@ mod tests {
let priv_key = get_private_key();
let pub_key: RsaPublicKey = (&priv_key).into();
let ciphertext = pub_key
.encrypt(
&mut rng,
PaddingScheme::new_oaep::<Sha1>(),
"a_plain_text".as_bytes(),
)
.encrypt(&mut rng, Oaep::new::<Sha1>(), "a_plain_text".as_bytes())
.unwrap();
assert!(
priv_key
.decrypt_blinded(
&mut rng,
PaddingScheme::new_oaep_with_label::<Sha1, _>("label"),
Oaep::new_with_label::<Sha1, _>("label"),
&ciphertext,
)
.is_err(),
Expand Down
23 changes: 13 additions & 10 deletions src/lib.rs
Expand Up @@ -13,7 +13,7 @@
//!
//! ## PKCS#1 v1.5 encryption
//! ```
//! use rsa::{PublicKey, RsaPrivateKey, RsaPublicKey, PaddingScheme};
//! use rsa::{PublicKey, RsaPrivateKey, RsaPublicKey, Pkcs1v15Encrypt};
//!
//! let mut rng = rand::thread_rng();
//!
Expand All @@ -23,19 +23,17 @@
//!
//! // Encrypt
//! let data = b"hello world";
//! let padding = PaddingScheme::new_pkcs1v15_encrypt();
//! let enc_data = public_key.encrypt(&mut rng, padding, &data[..]).expect("failed to encrypt");
//! let enc_data = public_key.encrypt(&mut rng, Pkcs1v15Encrypt, &data[..]).expect("failed to encrypt");
//! assert_ne!(&data[..], &enc_data[..]);
//!
//! // Decrypt
//! let padding = PaddingScheme::new_pkcs1v15_encrypt();
//! let dec_data = private_key.decrypt(padding, &enc_data).expect("failed to decrypt");
//! let dec_data = private_key.decrypt(Pkcs1v15Encrypt, &enc_data).expect("failed to decrypt");
//! assert_eq!(&data[..], &dec_data[..]);
//! ```
//!
//! ## OAEP encryption
//! ```
//! use rsa::{PublicKey, RsaPrivateKey, RsaPublicKey, PaddingScheme};
//! use rsa::{PublicKey, RsaPrivateKey, RsaPublicKey, Oaep};
//!
//! let mut rng = rand::thread_rng();
//!
Expand All @@ -45,12 +43,12 @@
//!
//! // Encrypt
//! let data = b"hello world";
//! let padding = PaddingScheme::new_oaep::<sha2::Sha256>();
//! let padding = Oaep::new::<sha2::Sha256>();
//! let enc_data = public_key.encrypt(&mut rng, padding, &data[..]).expect("failed to encrypt");
//! assert_ne!(&data[..], &enc_data[..]);
//!
//! // Decrypt
//! let padding = PaddingScheme::new_oaep::<sha2::Sha256>();
//! let padding = Oaep::new::<sha2::Sha256>();
//! let dec_data = private_key.decrypt(padding, &enc_data).expect("failed to decrypt");
//! assert_eq!(&data[..], &dec_data[..]);
//! ```
Expand Down Expand Up @@ -227,8 +225,13 @@ mod raw;
pub use pkcs1;
pub use pkcs8;

pub use self::key::{PublicKey, PublicKeyParts, RsaPrivateKey, RsaPublicKey};
pub use self::padding::PaddingScheme;
pub use crate::{
key::{PublicKey, PublicKeyParts, RsaPrivateKey, RsaPublicKey},
oaep::Oaep,
padding::{PaddingScheme, SignatureScheme},
pkcs1v15::{Pkcs1v15Encrypt, Pkcs1v15Sign},
pss::Pss,
};

/// Internal raw RSA functions.
#[cfg(not(feature = "expose-internals"))]
Expand Down