Refactor PaddingScheme
into a trait (#244)
Splits up the `PaddingScheme` enum into four structs, named after the previous variants of the struct (adopting capitalization from the Rust API guidelines): - `oaep::Oaep` - `pkcs1v15::{Pkcs1v15Encrypt, Pkcs1v15Sign}` - `pss::Pss` All of these are re-exported from the toplevel. Each of these structs impls one or more of the following traits: - `PaddingScheme`: used for encryption - `SignatureScheme`: used for signing The `PaddingScheme` constructors have been remapped as follows: - `new_oaep` => `Oaep::new` - `new_oaep_with_label` => `Oaep::new_with_label` - `new_oaep_with_mgf_hash` => `Oaep::new_with_mgf_hash` - `new_oaep_with_mgf_hash_with_label` => `Oaep::new_with_mgf_hash_and_label` - `new_pkcs1v15_encrypt` => `Pkcs1v15Encrypt` - `new_pkcs1v15_sign` => `Pkcs1v15Sign::new` - `new_pkcs1v15_sign_raw` => `Pkcs1v15Sign::new_raw` - `new_pss` => `Pss::{new, new_blinded}` - `new_pss_with_salt` => `Pss::{new_with_salt new_blinded_with_salt}`
This commit is contained in:
parent
35a32093f0
commit
35372d9516
@ -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;
|
||||
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
@ -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);
|
||||
});
|
||||
|
176
src/key.rs
176
src/key.rs
@ -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 {
|
||||
@ -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 {
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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};
|
||||
@ -965,10 +879,10 @@ 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()
|
||||
};
|
||||
|
||||
@ -976,9 +890,9 @@ mod tests {
|
||||
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 {
|
||||
@ -1013,10 +927,10 @@ 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()
|
||||
};
|
||||
|
||||
@ -1024,9 +938,9 @@ mod tests {
|
||||
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 {
|
||||
@ -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(),
|
||||
|
23
src/lib.rs
23
src/lib.rs
@ -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();
|
||||
//!
|
||||
@ -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();
|
||||
//!
|
||||
@ -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[..]);
|
||||
//! ```
|
||||
@ -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"))]
|
||||
|
157
src/oaep.rs
157
src/oaep.rs
@ -1,20 +1,171 @@
|
||||
use alloc::string::String;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt;
|
||||
use rand_core::CryptoRngCore;
|
||||
|
||||
use digest::DynDigest;
|
||||
use digest::{Digest, DynDigest};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use crate::algorithms::mgf1_xor;
|
||||
use crate::errors::{Error, Result};
|
||||
use crate::key::{self, PrivateKey, PublicKey};
|
||||
use crate::padding::PaddingScheme;
|
||||
|
||||
// 2**61 -1 (pow is not const yet)
|
||||
// TODO: This is the maximum for SHA-1, unclear from the RFC what the values are for other hashing functions.
|
||||
const MAX_LABEL_LEN: u64 = 2_305_843_009_213_693_951;
|
||||
|
||||
/// 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`,
|
||||
/// where `k` is the size of the RSA modulus.
|
||||
/// - `mgf_digest` specifies the hash function that is used in the [MGF1](https://datatracker.ietf.org/doc/html/rfc8017#appendix-B.2).
|
||||
/// - `label` is optional data that can be associated with the message.
|
||||
///
|
||||
/// The two hash functions can, but don't need to be the same.
|
||||
///
|
||||
/// A prominent example is the [`AndroidKeyStore`](https://developer.android.com/guide/topics/security/cryptography#oaep-mgf1-digest).
|
||||
/// It uses SHA-1 for `mgf_digest` and a user-chosen SHA flavour for `digest`.
|
||||
pub struct Oaep {
|
||||
/// Digest type to use.
|
||||
pub digest: Box<dyn DynDigest + Send + Sync>,
|
||||
|
||||
/// Digest to use for Mask Generation Function (MGF).
|
||||
pub mgf_digest: Box<dyn DynDigest + Send + Sync>,
|
||||
|
||||
/// Optional label.
|
||||
pub label: Option<String>,
|
||||
}
|
||||
|
||||
impl Oaep {
|
||||
/// Create a new OAEP `PaddingScheme`, using `T` as the hash function for both the default (empty) label and for MGF1.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use sha1::Sha1;
|
||||
/// use sha2::Sha256;
|
||||
/// use rsa::{BigUint, RsaPublicKey, Oaep, PublicKey};
|
||||
/// use base64ct::{Base64, Encoding};
|
||||
///
|
||||
/// let n = Base64::decode_vec("ALHgDoZmBQIx+jTmgeeHW6KsPOrj11f6CvWsiRleJlQpW77AwSZhd21ZDmlTKfaIHBSUxRUsuYNh7E2SHx8rkFVCQA2/gXkZ5GK2IUbzSTio9qXA25MWHvVxjMfKSL8ZAxZyKbrG94FLLszFAFOaiLLY8ECs7g+dXOriYtBwLUJK+lppbd+El+8ZA/zH0bk7vbqph5pIoiWggxwdq3mEz4LnrUln7r6dagSQzYErKewY8GADVpXcq5mfHC1xF2DFBub7bFjMVM5fHq7RK+pG5xjNDiYITbhLYrbVv3X0z75OvN0dY49ITWjM7xyvMWJXVJS7sJlgmCCL6RwWgP8PhcE=").unwrap();
|
||||
/// let e = Base64::decode_vec("AQAB").unwrap();
|
||||
///
|
||||
/// let mut rng = rand::thread_rng();
|
||||
/// let key = RsaPublicKey::new(BigUint::from_bytes_be(&n), BigUint::from_bytes_be(&e)).unwrap();
|
||||
/// let padding = Oaep::new::<Sha256>();
|
||||
/// let encrypted_data = key.encrypt(&mut rng, padding, b"secret").unwrap();
|
||||
/// ```
|
||||
pub fn new<T: 'static + Digest + DynDigest + Send + Sync>() -> Self {
|
||||
Self {
|
||||
digest: Box::new(T::new()),
|
||||
mgf_digest: Box::new(T::new()),
|
||||
label: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new OAEP `PaddingScheme` with an associated `label`, using `T` as the hash function for both the label and for MGF1.
|
||||
pub fn new_with_label<T: 'static + Digest + DynDigest + Send + Sync, S: AsRef<str>>(
|
||||
label: S,
|
||||
) -> Self {
|
||||
Self {
|
||||
digest: Box::new(T::new()),
|
||||
mgf_digest: Box::new(T::new()),
|
||||
label: Some(label.as_ref().to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use sha1::Sha1;
|
||||
/// use sha2::Sha256;
|
||||
/// use rsa::{BigUint, RsaPublicKey, Oaep, PublicKey};
|
||||
/// use base64ct::{Base64, Encoding};
|
||||
///
|
||||
/// let n = Base64::decode_vec("ALHgDoZmBQIx+jTmgeeHW6KsPOrj11f6CvWsiRleJlQpW77AwSZhd21ZDmlTKfaIHBSUxRUsuYNh7E2SHx8rkFVCQA2/gXkZ5GK2IUbzSTio9qXA25MWHvVxjMfKSL8ZAxZyKbrG94FLLszFAFOaiLLY8ECs7g+dXOriYtBwLUJK+lppbd+El+8ZA/zH0bk7vbqph5pIoiWggxwdq3mEz4LnrUln7r6dagSQzYErKewY8GADVpXcq5mfHC1xF2DFBub7bFjMVM5fHq7RK+pG5xjNDiYITbhLYrbVv3X0z75OvN0dY49ITWjM7xyvMWJXVJS7sJlgmCCL6RwWgP8PhcE=").unwrap();
|
||||
/// let e = Base64::decode_vec("AQAB").unwrap();
|
||||
///
|
||||
/// let mut rng = rand::thread_rng();
|
||||
/// let key = RsaPublicKey::new(BigUint::from_bytes_be(&n), BigUint::from_bytes_be(&e)).unwrap();
|
||||
/// let padding = Oaep::new_with_mgf_hash::<Sha256, Sha1>();
|
||||
/// let encrypted_data = key.encrypt(&mut rng, padding, b"secret").unwrap();
|
||||
/// ```
|
||||
pub fn new_with_mgf_hash<
|
||||
T: 'static + Digest + DynDigest + Send + Sync,
|
||||
U: 'static + Digest + DynDigest + Send + Sync,
|
||||
>() -> Self {
|
||||
Self {
|
||||
digest: Box::new(T::new()),
|
||||
mgf_digest: Box::new(U::new()),
|
||||
label: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new OAEP `PaddingScheme` with an associated `label`, using `T` as the hash function for the label, and `U` as the hash function for MGF1.
|
||||
pub fn new_with_mgf_hash_and_label<
|
||||
T: 'static + Digest + DynDigest + Send + Sync,
|
||||
U: 'static + Digest + DynDigest + Send + Sync,
|
||||
S: AsRef<str>,
|
||||
>(
|
||||
label: S,
|
||||
) -> Self {
|
||||
Self {
|
||||
digest: Box::new(T::new()),
|
||||
mgf_digest: Box::new(U::new()),
|
||||
label: Some(label.as_ref().to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PaddingScheme for Oaep {
|
||||
fn decrypt<Rng: CryptoRngCore, Priv: PrivateKey>(
|
||||
mut self,
|
||||
rng: Option<&mut Rng>,
|
||||
priv_key: &Priv,
|
||||
ciphertext: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
decrypt(
|
||||
rng,
|
||||
priv_key,
|
||||
ciphertext,
|
||||
&mut *self.digest,
|
||||
&mut *self.mgf_digest,
|
||||
self.label,
|
||||
)
|
||||
}
|
||||
|
||||
fn encrypt<Rng: CryptoRngCore, Pub: PublicKey>(
|
||||
mut self,
|
||||
rng: &mut Rng,
|
||||
pub_key: &Pub,
|
||||
msg: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
encrypt(
|
||||
rng,
|
||||
pub_key,
|
||||
msg,
|
||||
&mut *self.digest,
|
||||
&mut *self.mgf_digest,
|
||||
self.label,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Oaep {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("OAEP")
|
||||
.field("digest", &"...")
|
||||
.field("mgf_digest", &"...")
|
||||
.field("label", &self.label)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Encrypts the given message with RSA and the padding scheme from
|
||||
/// [PKCS#1 OAEP].
|
||||
///
|
||||
@ -57,7 +208,7 @@ pub fn encrypt<R: CryptoRngCore, K: PublicKey>(
|
||||
|
||||
digest.update(label.as_bytes());
|
||||
let p_hash = digest.finalize_reset();
|
||||
db[0..h_size].copy_from_slice(&*p_hash);
|
||||
db[0..h_size].copy_from_slice(&p_hash);
|
||||
db[db_len - msg.len() - 1] = 1;
|
||||
db[db_len - msg.len()..].copy_from_slice(msg);
|
||||
|
||||
|
231
src/padding.rs
231
src/padding.rs
@ -1,202 +1,49 @@
|
||||
//! Supported padding schemes.
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::{String, ToString};
|
||||
use core::fmt;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use digest::{Digest, DynDigest};
|
||||
use pkcs8::AssociatedOid;
|
||||
use rand_core::CryptoRngCore;
|
||||
|
||||
use crate::pkcs1v15;
|
||||
use crate::errors::Result;
|
||||
use crate::key::{PrivateKey, PublicKey};
|
||||
|
||||
/// 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<usize>,
|
||||
|
||||
/// Prefix.
|
||||
prefix: Box<[u8]>,
|
||||
},
|
||||
|
||||
/// Encryption and Decryption using [OAEP padding](https://datatracker.ietf.org/doc/html/rfc8017#section-7.1).
|
||||
/// Padding scheme used for encryption.
|
||||
pub trait PaddingScheme {
|
||||
/// Decrypt the given message using the given private key.
|
||||
///
|
||||
/// - `digest` is used to hash the label. The maximum possible plaintext length is `m = k - 2 * h_len - 2`,
|
||||
/// where `k` is the size of the RSA modulus.
|
||||
/// - `mgf_digest` specifies the hash function that is used in the [MGF1](https://datatracker.ietf.org/doc/html/rfc8017#appendix-B.2).
|
||||
/// - `label` is optional data that can be associated with the message.
|
||||
///
|
||||
/// The two hash functions can, but don't need to be the same.
|
||||
///
|
||||
/// A prominent example is the [`AndroidKeyStore`](https://developer.android.com/guide/topics/security/cryptography#oaep-mgf1-digest).
|
||||
/// It uses SHA-1 for `mgf_digest` and a user-chosen SHA flavour for `digest`.
|
||||
OAEP {
|
||||
/// Digest type to use.
|
||||
digest: Box<dyn DynDigest + Send + Sync>,
|
||||
/// If an `rng` is passed, it uses RSA blinding to help mitigate timing
|
||||
/// side-channel attacks.
|
||||
fn decrypt<Rng: CryptoRngCore, Priv: PrivateKey>(
|
||||
self,
|
||||
rng: Option<&mut Rng>,
|
||||
priv_key: &Priv,
|
||||
ciphertext: &[u8],
|
||||
) -> Result<Vec<u8>>;
|
||||
|
||||
/// Digest to use for Mask Generation Function (MGF).
|
||||
mgf_digest: Box<dyn DynDigest + Send + Sync>,
|
||||
|
||||
/// Optional label.
|
||||
label: Option<String>,
|
||||
},
|
||||
|
||||
/// Sign and Verify using PSS padding.
|
||||
PSS {
|
||||
/// Digest type to use.
|
||||
digest: Box<dyn DynDigest + Send + Sync>,
|
||||
|
||||
/// Salt length.
|
||||
salt_len: Option<usize>,
|
||||
},
|
||||
/// Encrypt the given message using the given public key.
|
||||
fn encrypt<Rng: CryptoRngCore, Pub: PublicKey>(
|
||||
self,
|
||||
rng: &mut Rng,
|
||||
pub_key: &Pub,
|
||||
msg: &[u8],
|
||||
) -> Result<Vec<u8>>;
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PaddingScheme {
|
||||
/// Create new PKCS#1 v1.5 encryption padding.
|
||||
pub fn new_pkcs1v15_encrypt() -> Self {
|
||||
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<D>() -> Self
|
||||
where
|
||||
D: Digest + AssociatedOid,
|
||||
{
|
||||
PaddingScheme::PKCS1v15Sign {
|
||||
hash_len: Some(<D as Digest>::output_size()),
|
||||
prefix: pkcs1v15::generate_prefix::<D>().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`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use sha1::Sha1;
|
||||
/// use sha2::Sha256;
|
||||
/// use rsa::{BigUint, RsaPublicKey, PaddingScheme, PublicKey};
|
||||
/// use base64ct::{Base64, Encoding};
|
||||
///
|
||||
/// let n = Base64::decode_vec("ALHgDoZmBQIx+jTmgeeHW6KsPOrj11f6CvWsiRleJlQpW77AwSZhd21ZDmlTKfaIHBSUxRUsuYNh7E2SHx8rkFVCQA2/gXkZ5GK2IUbzSTio9qXA25MWHvVxjMfKSL8ZAxZyKbrG94FLLszFAFOaiLLY8ECs7g+dXOriYtBwLUJK+lppbd+El+8ZA/zH0bk7vbqph5pIoiWggxwdq3mEz4LnrUln7r6dagSQzYErKewY8GADVpXcq5mfHC1xF2DFBub7bFjMVM5fHq7RK+pG5xjNDiYITbhLYrbVv3X0z75OvN0dY49ITWjM7xyvMWJXVJS7sJlgmCCL6RwWgP8PhcE=").unwrap();
|
||||
/// let e = Base64::decode_vec("AQAB").unwrap();
|
||||
///
|
||||
/// let mut rng = rand::thread_rng();
|
||||
/// let key = RsaPublicKey::new(BigUint::from_bytes_be(&n), BigUint::from_bytes_be(&e)).unwrap();
|
||||
/// let padding = PaddingScheme::new_oaep_with_mgf_hash::<Sha256, Sha1>();
|
||||
/// let encrypted_data = key.encrypt(&mut rng, padding, b"secret").unwrap();
|
||||
/// ```
|
||||
pub fn new_oaep_with_mgf_hash<
|
||||
T: 'static + Digest + DynDigest + Send + Sync,
|
||||
U: 'static + Digest + DynDigest + Send + Sync,
|
||||
>() -> Self {
|
||||
PaddingScheme::OAEP {
|
||||
digest: Box::new(T::new()),
|
||||
mgf_digest: Box::new(U::new()),
|
||||
label: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new OAEP `PaddingScheme`, using `T` as the hash function for both the default (empty) label and for MGF1.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use sha1::Sha1;
|
||||
/// use sha2::Sha256;
|
||||
/// use rsa::{BigUint, RsaPublicKey, PaddingScheme, PublicKey};
|
||||
/// use base64ct::{Base64, Encoding};
|
||||
///
|
||||
/// let n = Base64::decode_vec("ALHgDoZmBQIx+jTmgeeHW6KsPOrj11f6CvWsiRleJlQpW77AwSZhd21ZDmlTKfaIHBSUxRUsuYNh7E2SHx8rkFVCQA2/gXkZ5GK2IUbzSTio9qXA25MWHvVxjMfKSL8ZAxZyKbrG94FLLszFAFOaiLLY8ECs7g+dXOriYtBwLUJK+lppbd+El+8ZA/zH0bk7vbqph5pIoiWggxwdq3mEz4LnrUln7r6dagSQzYErKewY8GADVpXcq5mfHC1xF2DFBub7bFjMVM5fHq7RK+pG5xjNDiYITbhLYrbVv3X0z75OvN0dY49ITWjM7xyvMWJXVJS7sJlgmCCL6RwWgP8PhcE=").unwrap();
|
||||
/// let e = Base64::decode_vec("AQAB").unwrap();
|
||||
///
|
||||
/// let mut rng = rand::thread_rng();
|
||||
/// let key = RsaPublicKey::new(BigUint::from_bytes_be(&n), BigUint::from_bytes_be(&e)).unwrap();
|
||||
/// let padding = PaddingScheme::new_oaep::<Sha256>();
|
||||
/// let encrypted_data = key.encrypt(&mut rng, padding, b"secret").unwrap();
|
||||
/// ```
|
||||
pub fn new_oaep<T: 'static + Digest + DynDigest + Send + Sync>() -> Self {
|
||||
PaddingScheme::OAEP {
|
||||
digest: Box::new(T::new()),
|
||||
mgf_digest: Box::new(T::new()),
|
||||
label: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new OAEP `PaddingScheme` with an associated `label`, using `T` as the hash function for the label, and `U` as the hash function for MGF1.
|
||||
pub fn new_oaep_with_mgf_hash_with_label<
|
||||
T: 'static + Digest + DynDigest + Send + Sync,
|
||||
U: 'static + Digest + DynDigest + Send + Sync,
|
||||
S: AsRef<str>,
|
||||
>(
|
||||
label: S,
|
||||
) -> Self {
|
||||
PaddingScheme::OAEP {
|
||||
digest: Box::new(T::new()),
|
||||
mgf_digest: Box::new(U::new()),
|
||||
label: Some(label.as_ref().to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new OAEP `PaddingScheme` with an associated `label`, using `T` as the hash function for both the label and for MGF1.
|
||||
pub fn new_oaep_with_label<T: 'static + Digest + DynDigest + Send + Sync, S: AsRef<str>>(
|
||||
label: S,
|
||||
) -> Self {
|
||||
PaddingScheme::OAEP {
|
||||
digest: Box::new(T::new()),
|
||||
mgf_digest: Box::new(T::new()),
|
||||
label: Some(label.as_ref().to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// New PSS padding for the given digest.
|
||||
pub fn new_pss<T: 'static + Digest + DynDigest + Send + Sync>() -> 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<T: 'static + Digest + DynDigest + Send + Sync>(len: usize) -> Self {
|
||||
PaddingScheme::PSS {
|
||||
digest: Box::new(T::new()),
|
||||
salt_len: Some(len),
|
||||
}
|
||||
}
|
||||
/// Digital signature scheme.
|
||||
pub trait SignatureScheme {
|
||||
/// Sign the given digest.
|
||||
fn sign<Rng: CryptoRngCore, Priv: PrivateKey>(
|
||||
self,
|
||||
rng: Option<&mut Rng>,
|
||||
priv_key: &Priv,
|
||||
hashed: &[u8],
|
||||
) -> Result<Vec<u8>>;
|
||||
|
||||
/// 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, otherwise an `Err` indicating failure.
|
||||
fn verify<Pub: PublicKey>(self, pub_key: &Pub, hashed: &[u8], sig: &[u8]) -> Result<()>;
|
||||
}
|
||||
|
117
src/pkcs1v15.rs
117
src/pkcs1v15.rs
@ -24,8 +24,96 @@ use zeroize::Zeroizing;
|
||||
use crate::dummy_rng::DummyRng;
|
||||
use crate::errors::{Error, Result};
|
||||
use crate::key::{self, PrivateKey, PublicKey};
|
||||
use crate::padding::{PaddingScheme, SignatureScheme};
|
||||
use crate::{RsaPrivateKey, RsaPublicKey};
|
||||
|
||||
/// Encryption using PKCS#1 v1.5 padding.
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub struct Pkcs1v15Encrypt;
|
||||
|
||||
impl PaddingScheme for Pkcs1v15Encrypt {
|
||||
fn decrypt<Rng: CryptoRngCore, Priv: PrivateKey>(
|
||||
self,
|
||||
rng: Option<&mut Rng>,
|
||||
priv_key: &Priv,
|
||||
ciphertext: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
decrypt(rng, priv_key, ciphertext)
|
||||
}
|
||||
|
||||
fn encrypt<Rng: CryptoRngCore, Pub: PublicKey>(
|
||||
self,
|
||||
rng: &mut Rng,
|
||||
pub_key: &Pub,
|
||||
msg: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
encrypt(rng, pub_key, msg)
|
||||
}
|
||||
}
|
||||
|
||||
/// Digital signatures using PKCS#1 v1.5 padding.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Pkcs1v15Sign {
|
||||
/// Length of hash to use.
|
||||
pub hash_len: Option<usize>,
|
||||
|
||||
/// Prefix.
|
||||
pub prefix: Box<[u8]>,
|
||||
}
|
||||
|
||||
impl Pkcs1v15Sign {
|
||||
/// 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<D>() -> Self
|
||||
where
|
||||
D: Digest + AssociatedOid,
|
||||
{
|
||||
Self {
|
||||
hash_len: Some(<D as Digest>::output_size()),
|
||||
prefix: generate_prefix::<D>().into_boxed_slice(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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_raw() -> Self {
|
||||
Self {
|
||||
hash_len: None,
|
||||
prefix: Box::new([]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SignatureScheme for Pkcs1v15Sign {
|
||||
fn sign<Rng: CryptoRngCore, Priv: PrivateKey>(
|
||||
self,
|
||||
rng: Option<&mut Rng>,
|
||||
priv_key: &Priv,
|
||||
hashed: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
if let Some(hash_len) = self.hash_len {
|
||||
if hashed.len() != hash_len {
|
||||
return Err(Error::InputNotHashed);
|
||||
}
|
||||
}
|
||||
|
||||
sign(rng, priv_key, &self.prefix, hashed)
|
||||
}
|
||||
|
||||
fn verify<Pub: PublicKey>(self, pub_key: &Pub, hashed: &[u8], sig: &[u8]) -> Result<()> {
|
||||
if let Some(hash_len) = self.hash_len {
|
||||
if hashed.len() != hash_len {
|
||||
return Err(Error::InputNotHashed);
|
||||
}
|
||||
}
|
||||
|
||||
verify(pub_key, self.prefix.as_ref(), hashed, sig)
|
||||
}
|
||||
}
|
||||
|
||||
/// PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
|
||||
///
|
||||
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
|
||||
@ -44,10 +132,10 @@ impl From<Box<[u8]>> for Signature {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for Signature {
|
||||
impl TryFrom<&[u8]> for Signature {
|
||||
type Error = signature::Error;
|
||||
|
||||
fn try_from(bytes: &'a [u8]) -> signature::Result<Self> {
|
||||
fn try_from(bytes: &[u8]) -> signature::Result<Self> {
|
||||
Ok(Self {
|
||||
bytes: bytes.into(),
|
||||
})
|
||||
@ -629,7 +717,7 @@ mod tests {
|
||||
use sha3::Sha3_256;
|
||||
use signature::{RandomizedSigner, Signer, Verifier};
|
||||
|
||||
use crate::{PaddingScheme, PublicKey, PublicKeyParts, RsaPrivateKey, RsaPublicKey};
|
||||
use crate::{PublicKey, PublicKeyParts, RsaPrivateKey, RsaPublicKey};
|
||||
|
||||
#[test]
|
||||
fn test_non_zero_bytes() {
|
||||
@ -686,10 +774,7 @@ mod tests {
|
||||
|
||||
for test in &tests {
|
||||
let out = priv_key
|
||||
.decrypt(
|
||||
PaddingScheme::new_pkcs1v15_encrypt(),
|
||||
&Base64::decode_vec(test[0]).unwrap(),
|
||||
)
|
||||
.decrypt(Pkcs1v15Encrypt, &Base64::decode_vec(test[0]).unwrap())
|
||||
.unwrap();
|
||||
assert_eq!(out, test[1].as_bytes());
|
||||
}
|
||||
@ -734,19 +819,13 @@ mod tests {
|
||||
for (text, expected) in &tests {
|
||||
let digest = Sha1::digest(text.as_bytes()).to_vec();
|
||||
|
||||
let out = priv_key
|
||||
.sign(PaddingScheme::new_pkcs1v15_sign::<Sha1>(), &digest)
|
||||
.unwrap();
|
||||
let out = priv_key.sign(Pkcs1v15Sign::new::<Sha1>(), &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::<Sha1>(),
|
||||
&digest,
|
||||
)
|
||||
.sign_with_rng(&mut rng, Pkcs1v15Sign::new::<Sha1>(), &digest)
|
||||
.unwrap();
|
||||
assert_eq!(out2, expected);
|
||||
}
|
||||
@ -885,7 +964,7 @@ mod tests {
|
||||
for (text, sig, expected) in &tests {
|
||||
let digest = Sha1::digest(text.as_bytes()).to_vec();
|
||||
|
||||
let result = pub_key.verify(PaddingScheme::new_pkcs1v15_sign::<Sha1>(), &digest, sig);
|
||||
let result = pub_key.verify(Pkcs1v15Sign::new::<Sha1>(), &digest, sig);
|
||||
match expected {
|
||||
true => result.expect("failed to verify"),
|
||||
false => {
|
||||
@ -978,14 +1057,12 @@ mod tests {
|
||||
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();
|
||||
let sig = priv_key.sign(Pkcs1v15Sign::new_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)
|
||||
.verify(Pkcs1v15Sign::new_raw(), msg, &sig)
|
||||
.expect("failed to verify");
|
||||
}
|
||||
|
||||
|
108
src/pss.rs
108
src/pss.rs
@ -11,8 +11,8 @@
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt::{self, Debug, Display, Formatter, LowerHex, UpperHex};
|
||||
|
||||
use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex};
|
||||
use core::marker::PhantomData;
|
||||
use digest::{Digest, DynDigest, FixedOutputReset};
|
||||
use pkcs8::{Document, EncodePrivateKey, EncodePublicKey, SecretDocument};
|
||||
@ -26,8 +26,94 @@ use subtle::ConstantTimeEq;
|
||||
use crate::algorithms::{mgf1_xor, mgf1_xor_digest};
|
||||
use crate::errors::{Error, Result};
|
||||
use crate::key::{PrivateKey, PublicKey};
|
||||
use crate::padding::SignatureScheme;
|
||||
use crate::{RsaPrivateKey, RsaPublicKey};
|
||||
|
||||
/// Digital signatures using PSS padding.
|
||||
pub struct Pss {
|
||||
/// Create blinded signatures.
|
||||
pub blinded: bool,
|
||||
|
||||
/// Digest type to use.
|
||||
pub digest: Box<dyn DynDigest + Send + Sync>,
|
||||
|
||||
/// Salt length.
|
||||
pub salt_len: Option<usize>,
|
||||
}
|
||||
|
||||
impl Pss {
|
||||
/// New PSS padding for the given digest.
|
||||
pub fn new<T: 'static + Digest + DynDigest + Send + Sync>() -> Self {
|
||||
Self {
|
||||
blinded: false,
|
||||
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_with_salt<T: 'static + Digest + DynDigest + Send + Sync>(len: usize) -> Self {
|
||||
Self {
|
||||
blinded: false,
|
||||
digest: Box::new(T::new()),
|
||||
salt_len: Some(len),
|
||||
}
|
||||
}
|
||||
|
||||
/// New PSS padding for blinded signatures (RSA-BSSA) for the given digest.
|
||||
pub fn new_blinded<T: 'static + Digest + DynDigest + Send + Sync>() -> Self {
|
||||
Self {
|
||||
blinded: true,
|
||||
digest: Box::new(T::new()),
|
||||
salt_len: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// New PSS padding for blinded signatures (RSA-BSSA) for the given digest
|
||||
/// with a salt value of the given length.
|
||||
pub fn new_blinded_with_salt<T: 'static + Digest + DynDigest + Send + Sync>(
|
||||
len: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
blinded: true,
|
||||
digest: Box::new(T::new()),
|
||||
salt_len: Some(len),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SignatureScheme for Pss {
|
||||
fn sign<Rng: CryptoRngCore, Priv: PrivateKey>(
|
||||
mut self,
|
||||
rng: Option<&mut Rng>,
|
||||
priv_key: &Priv,
|
||||
hashed: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
sign(
|
||||
rng.ok_or(Error::InvalidPaddingScheme)?,
|
||||
self.blinded,
|
||||
priv_key,
|
||||
hashed,
|
||||
self.salt_len,
|
||||
&mut *self.digest,
|
||||
)
|
||||
}
|
||||
|
||||
fn verify<Pub: PublicKey>(mut self, pub_key: &Pub, hashed: &[u8], sig: &[u8]) -> Result<()> {
|
||||
verify(pub_key, hashed, sig, &mut *self.digest)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Pss {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("PSS")
|
||||
.field("blinded", &self.blinded)
|
||||
.field("digest", &"...")
|
||||
.field("salt_len", &self.salt_len)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// RSASSA-PSS signatures as described in [RFC8017 § 8.1].
|
||||
///
|
||||
/// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
|
||||
@ -46,10 +132,10 @@ impl From<Box<[u8]>> for Signature {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for Signature {
|
||||
impl TryFrom<&[u8]> for Signature {
|
||||
type Error = signature::Error;
|
||||
|
||||
fn try_from(bytes: &'a [u8]) -> signature::Result<Self> {
|
||||
fn try_from(bytes: &[u8]) -> signature::Result<Self> {
|
||||
Ok(Self {
|
||||
bytes: bytes.into(),
|
||||
})
|
||||
@ -329,7 +415,7 @@ where
|
||||
|
||||
let mut hash = D::new();
|
||||
|
||||
Digest::update(&mut hash, &prefix);
|
||||
Digest::update(&mut hash, prefix);
|
||||
Digest::update(&mut hash, m_hash);
|
||||
Digest::update(&mut hash, salt);
|
||||
|
||||
@ -920,8 +1006,8 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::pss::{BlindedSigningKey, Signature, SigningKey, VerifyingKey};
|
||||
use crate::{PaddingScheme, PublicKey, RsaPrivateKey, RsaPublicKey};
|
||||
use crate::pss::{BlindedSigningKey, Pss, Signature, SigningKey, VerifyingKey};
|
||||
use crate::{PublicKey, RsaPrivateKey, RsaPublicKey};
|
||||
|
||||
use hex_literal::hex;
|
||||
use num_bigint::BigUint;
|
||||
@ -980,7 +1066,7 @@ mod test {
|
||||
|
||||
for (text, sig, expected) in &tests {
|
||||
let digest = Sha1::digest(text.as_bytes()).to_vec();
|
||||
let result = pub_key.verify(PaddingScheme::new_pss::<Sha1>(), &digest, sig);
|
||||
let result = pub_key.verify(Pss::new::<Sha1>(), &digest, sig);
|
||||
match expected {
|
||||
true => result.expect("failed to verify"),
|
||||
false => {
|
||||
@ -1078,11 +1164,11 @@ mod test {
|
||||
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::<Sha1>(), &digest)
|
||||
.sign_with_rng(&mut rng.clone(), Pss::new::<Sha1>(), &digest)
|
||||
.expect("failed to sign");
|
||||
|
||||
priv_key
|
||||
.verify(PaddingScheme::new_pss::<Sha1>(), &digest, &sig)
|
||||
.verify(Pss::new::<Sha1>(), &digest, &sig)
|
||||
.expect("failed to verify");
|
||||
}
|
||||
}
|
||||
@ -1097,11 +1183,11 @@ mod test {
|
||||
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::<Sha1>(), &digest)
|
||||
.sign_with_rng(&mut rng.clone(), Pss::new_blinded::<Sha1>(), &digest)
|
||||
.expect("failed to sign");
|
||||
|
||||
priv_key
|
||||
.verify(PaddingScheme::new_pss::<Sha1>(), &digest, &sig)
|
||||
.verify(Pss::new::<Sha1>(), &digest, &sig)
|
||||
.expect("failed to verify");
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user