Add Encryption-related traits (#259)

* feat: relax Sized requirement for random source parameters

* oaep: move OAEP test cases to src/oaep.rs

There is little point in having only OAEP test cases in src/key.rs. Move
them to proper module, oaep.rs.

* oaep: mark two functions as private

Currently the crate doesn't mark the oaep module as public. Thus it
makes little sense to mark top-level functions as public. Drop the
modifier.

* feat: traits: add traits for encryption and decryption

Add traits following the signature design for encryption and decryption.

* oaep: add support for new encryption API

Add new EncryptingKey and DecryptingKey structs implementing Encryptor /
Decryptor traits.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
This commit is contained in:
Dmitry Baryshkov 2023-02-13 18:35:20 +03:00 committed by GitHub
parent 6f8d112cea
commit dacabfc5ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 782 additions and 251 deletions

View File

@ -504,19 +504,10 @@ 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};
use hex_literal::hex;
use num_traits::{FromPrimitive, ToPrimitive};
use rand_chacha::{
rand_core::{RngCore, SeedableRng},
ChaCha8Rng,
};
use sha1::Sha1;
use sha2::{Sha224, Sha256, Sha384, Sha512};
use sha3::{Sha3_256, Sha3_384, Sha3_512};
use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng};
#[test]
fn test_from_into() {
@ -780,195 +771,4 @@ mod tests {
Error::ModulusTooLarge
);
}
fn get_private_key() -> RsaPrivateKey {
// -----BEGIN RSA PRIVATE KEY-----
// MIIEpAIBAAKCAQEA05e4TZikwmE47RtpWoEG6tkdVTvwYEG2LT/cUKBB4iK49FKW
// icG4LF5xVU9d1p+i9LYVjPDb61eBGg/DJ+HyjnT+dNO8Fmweq9wbi1e5NMqL5bAL
// TymXW8yZrK9BW1m7KKZ4K7QaLDwpdrPBjbre9i8AxrsiZkAJUJbAzGDSL+fvmH11
// xqgbENlr8pICivEQ3HzBu8Q9Iq2rN5oM1dgHjMeA/1zWIJ3qNMkiz3hPdxfkKNdb
// WuyP8w5fAUFRB2bi4KuNRzyE6HELK5gifD2wlTN600UvGeK5v7zN2BSKv2d2+lUn
// debnWVbkUimuWpxGlJurHmIvDkj1ZSSoTtNIOwIDAQABAoIBAQDE5wxokWLJTGYI
// KBkbUrTYOSEV30hqmtvoMeRY1zlYMg3Bt1VFbpNwHpcC12+wuS+Q4B0f4kgVMoH+
// eaqXY6kvrmnY1+zRRN4p+hNb0U+Vc+NJ5FAx47dpgvWDADgmxVLomjl8Gga9IWNI
// hjDZLowrtkPXq+9wDaldaFyUFImkb1S1MW9itdLDp/G70TTLNzU6RGg/3J2V02RY
// 3iL2xEBX/nSgpDbEMI9z9NpC81xHrBanE41IOvyR5B3DoRJzguDA9RGbAiG0/GOd
// a5w4F3pt6bUm69iMONeYLAf5ig79h31Qiq4nW5RpFcAuLhEG0XXXTsZ3f16A0SwF
// PZx74eNBAoGBAPgnu/OkGHfHzFmuv0LtSynDLe/LjtloY9WwkKBaiTDdYkohydz5
// g4Vo/foN9luEYqXyrJE9bFb5dVMr2OePsHvUBcqZpIS89Z8Bm73cs5M/K85wYwC0
// 97EQEgxd+QGBWQZ8NdowYaVshjWlK1QnOzEnG0MR8Hld9gIeY1XhpC5hAoGBANpI
// F84Aid028q3mo/9BDHPsNL8bT2vaOEMb/t4RzvH39u+nDl+AY6Ox9uFylv+xX+76
// CRKgMluNH9ZaVZ5xe1uWHsNFBy4OxSA9A0QdKa9NZAVKBFB0EM8dp457YRnZCexm
// 5q1iW/mVsnmks8W+fYlc18W5xMSX/ecwkW/NtOQbAoGAHabpz4AhKFbodSLrWbzv
// CUt4NroVFKdjnoodjfujfwJFF2SYMV5jN9LG3lVCxca43ulzc1tqka33Nfv8TBcg
// WHuKQZ5ASVgm5VwU1wgDMSoQOve07MWy/yZTccTc1zA0ihDXgn3bfR/NnaVh2wlh
// CkuI92eyW1494hztc7qlmqECgYEA1zenyOQ9ChDIW/ABGIahaZamNxsNRrDFMl3j
// AD+cxHSRU59qC32CQH8ShRy/huHzTaPX2DZ9EEln76fnrS4Ey7uLH0rrFl1XvT6K
// /timJgLvMEvXTx/xBtUdRN2fUqXtI9odbSyCtOYFL+zVl44HJq2UzY4pVRDrNcxs
// SUkQJqsCgYBSaNfPBzR5rrstLtTdZrjImRW1LRQeDEky9WsMDtCTYUGJTsTSfVO8
// hkU82MpbRVBFIYx+GWIJwcZRcC7OCQoV48vMJllxMAAjqG/p00rVJ+nvA7et/nNu
// BoB0er/UmDm4Ly/97EO9A0PKMOE5YbMq9s3t3RlWcsdrU7dvw+p2+A==
// -----END RSA PRIVATE KEY-----
RsaPrivateKey::from_components(
BigUint::parse_bytes(b"00d397b84d98a4c26138ed1b695a8106ead91d553bf06041b62d3fdc50a041e222b8f4529689c1b82c5e71554f5dd69fa2f4b6158cf0dbeb57811a0fc327e1f28e74fe74d3bc166c1eabdc1b8b57b934ca8be5b00b4f29975bcc99acaf415b59bb28a6782bb41a2c3c2976b3c18dbadef62f00c6bb226640095096c0cc60d22fe7ef987d75c6a81b10d96bf292028af110dc7cc1bbc43d22adab379a0cd5d8078cc780ff5cd6209dea34c922cf784f7717e428d75b5aec8ff30e5f0141510766e2e0ab8d473c84e8710b2b98227c3db095337ad3452f19e2b9bfbccdd8148abf6776fa552775e6e75956e45229ae5a9c46949bab1e622f0e48f56524a84ed3483b", 16).unwrap(),
BigUint::from_u64(65537).unwrap(),
BigUint::parse_bytes(b"00c4e70c689162c94c660828191b52b4d8392115df486a9adbe831e458d73958320dc1b755456e93701e9702d76fb0b92f90e01d1fe248153281fe79aa9763a92fae69d8d7ecd144de29fa135bd14f9573e349e45031e3b76982f583003826c552e89a397c1a06bd2163488630d92e8c2bb643d7abef700da95d685c941489a46f54b5316f62b5d2c3a7f1bbd134cb37353a44683fdc9d95d36458de22f6c44057fe74a0a436c4308f73f4da42f35c47ac16a7138d483afc91e41dc3a1127382e0c0f5119b0221b4fc639d6b9c38177a6de9b526ebd88c38d7982c07f98a0efd877d508aae275b946915c02e2e1106d175d74ec6777f5e80d12c053d9c7be1e341", 16).unwrap(),
vec![
BigUint::parse_bytes(b"00f827bbf3a41877c7cc59aebf42ed4b29c32defcb8ed96863d5b090a05a8930dd624a21c9dcf9838568fdfa0df65b8462a5f2ac913d6c56f975532bd8e78fb07bd405ca99a484bcf59f019bbddcb3933f2bce706300b4f7b110120c5df9018159067c35da3061a56c8635a52b54273b31271b4311f0795df6021e6355e1a42e61",16).unwrap(),
BigUint::parse_bytes(b"00da4817ce0089dd36f2ade6a3ff410c73ec34bf1b4f6bda38431bfede11cef1f7f6efa70e5f8063a3b1f6e17296ffb15feefa0912a0325b8d1fd65a559e717b5b961ec345072e0ec5203d03441d29af4d64054a04507410cf1da78e7b6119d909ec66e6ad625bf995b279a4b3c5be7d895cd7c5b9c4c497fde730916fcdb4e41b", 16).unwrap()
],
).unwrap()
}
#[test]
fn test_encrypt_decrypt_oaep() {
let priv_key = get_private_key();
do_test_encrypt_decrypt_oaep::<Sha1>(&priv_key);
do_test_encrypt_decrypt_oaep::<Sha224>(&priv_key);
do_test_encrypt_decrypt_oaep::<Sha256>(&priv_key);
do_test_encrypt_decrypt_oaep::<Sha384>(&priv_key);
do_test_encrypt_decrypt_oaep::<Sha512>(&priv_key);
do_test_encrypt_decrypt_oaep::<Sha3_256>(&priv_key);
do_test_encrypt_decrypt_oaep::<Sha3_384>(&priv_key);
do_test_encrypt_decrypt_oaep::<Sha3_512>(&priv_key);
do_test_oaep_with_different_hashes::<Sha1, Sha1>(&priv_key);
do_test_oaep_with_different_hashes::<Sha224, Sha1>(&priv_key);
do_test_oaep_with_different_hashes::<Sha256, Sha1>(&priv_key);
do_test_oaep_with_different_hashes::<Sha384, Sha1>(&priv_key);
do_test_oaep_with_different_hashes::<Sha512, Sha1>(&priv_key);
do_test_oaep_with_different_hashes::<Sha3_256, Sha1>(&priv_key);
do_test_oaep_with_different_hashes::<Sha3_384, Sha1>(&priv_key);
do_test_oaep_with_different_hashes::<Sha3_512, Sha1>(&priv_key);
}
fn get_label(rng: &mut ChaCha8Rng) -> Option<String> {
const GEN_ASCII_STR_CHARSET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789=+";
let mut buf = [0u8; 32];
rng.fill_bytes(&mut buf);
if buf[0] < (1 << 7) {
for v in buf.iter_mut() {
*v = GEN_ASCII_STR_CHARSET[(*v >> 2) as usize];
}
Some(core::str::from_utf8(&buf).unwrap().to_string())
} else {
None
}
}
fn do_test_encrypt_decrypt_oaep<D: 'static + Digest + DynDigest + Send + Sync>(
prk: &RsaPrivateKey,
) {
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let k = prk.size();
for i in 1..8 {
let mut input = vec![0u8; i * 8];
rng.fill_bytes(&mut input);
if input.len() > k - 11 {
input = input[0..k - 11].to_vec();
}
let label = get_label(&mut rng);
let pub_key: RsaPublicKey = prk.into();
let ciphertext = if let Some(ref label) = label {
let padding = Oaep::new_with_label::<D, _>(label);
pub_key.encrypt(&mut rng, padding, &input).unwrap()
} else {
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 {
Oaep::new_with_label::<D, _>(label)
} else {
Oaep::new::<D>()
};
let plaintext = if blind {
prk.decrypt(padding, &ciphertext).unwrap()
} else {
prk.decrypt_blinded(&mut rng, padding, &ciphertext).unwrap()
};
assert_eq!(input, plaintext);
}
}
fn do_test_oaep_with_different_hashes<
D: 'static + Digest + DynDigest + Send + Sync,
U: 'static + Digest + DynDigest + Send + Sync,
>(
prk: &RsaPrivateKey,
) {
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let k = prk.size();
for i in 1..8 {
let mut input = vec![0u8; i * 8];
rng.fill_bytes(&mut input);
if input.len() > k - 11 {
input = input[0..k - 11].to_vec();
}
let label = get_label(&mut rng);
let pub_key: RsaPublicKey = prk.into();
let ciphertext = if let Some(ref label) = label {
let padding = Oaep::new_with_mgf_hash_and_label::<D, U, _>(label);
pub_key.encrypt(&mut rng, padding, &input).unwrap()
} else {
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 {
Oaep::new_with_mgf_hash_and_label::<D, U, _>(label)
} else {
Oaep::new_with_mgf_hash::<D, U>()
};
let plaintext = if blind {
prk.decrypt(padding, &ciphertext).unwrap()
} else {
prk.decrypt_blinded(&mut rng, padding, &ciphertext).unwrap()
};
assert_eq!(input, plaintext);
}
}
#[test]
fn test_decrypt_oaep_invalid_hash() {
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let priv_key = get_private_key();
let pub_key: RsaPublicKey = (&priv_key).into();
let ciphertext = pub_key
.encrypt(&mut rng, Oaep::new::<Sha1>(), "a_plain_text".as_bytes())
.unwrap();
assert!(
priv_key
.decrypt_blinded(
&mut rng,
Oaep::new_with_label::<Sha1, _>("label"),
&ciphertext,
)
.is_err(),
"decrypt should have failed on hash verification"
);
}
}

View File

@ -220,13 +220,14 @@ pub use signature;
pub mod algorithms;
pub mod errors;
pub mod oaep;
pub mod pkcs1v15;
pub mod pss;
pub mod traits;
mod dummy_rng;
mod encoding;
mod key;
mod oaep;
mod padding;
mod raw;

View File

@ -1,18 +1,26 @@
//! Encryption and Decryption using [OAEP padding](https://datatracker.ietf.org/doc/html/rfc8017#section-7.1).
//!
//! # Usage
//!
//! See [code example in the toplevel rustdoc](../index.html#oaep-encryption).
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
use core::fmt;
use core::marker::PhantomData;
use rand_core::CryptoRngCore;
use digest::{Digest, DynDigest};
use digest::{Digest, DynDigest, FixedOutputReset};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use zeroize::Zeroizing;
use crate::algorithms::mgf1_xor;
use crate::algorithms::{mgf1_xor, mgf1_xor_digest};
use crate::dummy_rng::DummyRng;
use crate::errors::{Error, Result};
use crate::key::{self, PrivateKey, PublicKey};
use crate::key::{self, PrivateKey, PublicKey, RsaPrivateKey, RsaPublicKey};
use crate::padding::PaddingScheme;
use crate::traits::{Decryptor, RandomizedDecryptor, RandomizedEncryptor};
// 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.
@ -166,37 +174,28 @@ impl fmt::Debug for Oaep {
}
}
/// Encrypts the given message with RSA and the padding scheme from
/// [PKCS#1 OAEP].
///
/// The message must be no longer than the length of the public modulus minus
/// `2 + (2 * hash.size())`.
///
/// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
#[inline]
pub fn encrypt<R: CryptoRngCore, K: PublicKey>(
fn encrypt_internal<
'a,
R: CryptoRngCore + ?Sized,
K: PublicKey,
MGF: FnMut(&mut [u8], &mut [u8]) -> (),
>(
rng: &mut R,
pub_key: &K,
msg: &[u8],
digest: &mut dyn DynDigest,
mgf_digest: &mut dyn DynDigest,
label: Option<String>,
p_hash: &[u8],
h_size: usize,
mut mgf: MGF,
) -> Result<Vec<u8>> {
key::check_public(pub_key)?;
let k = pub_key.size();
let h_size = digest.output_size();
if msg.len() + 2 * h_size + 2 > k {
return Err(Error::MessageTooLong);
}
let label = label.unwrap_or_default();
if label.len() as u64 > MAX_LABEL_LEN {
return Err(Error::LabelTooLong);
}
let mut em = Zeroizing::new(vec![0u8; k]);
let (_, payload) = em.split_at_mut(1);
@ -206,18 +205,82 @@ pub fn encrypt<R: CryptoRngCore, K: PublicKey>(
// Data block DB = pHash || PS || 01 || M
let db_len = k - h_size - 1;
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);
mgf1_xor(db, mgf_digest, seed);
mgf1_xor(seed, mgf_digest, db);
mgf(seed, db);
pub_key.raw_encryption_primitive(&em, pub_key.size())
}
/// Encrypts the given message with RSA and the padding scheme from
/// [PKCS#1 OAEP].
///
/// The message must be no longer than the length of the public modulus minus
/// `2 + (2 * hash.size())`.
///
/// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
#[inline]
fn encrypt<R: CryptoRngCore + ?Sized, K: PublicKey>(
rng: &mut R,
pub_key: &K,
msg: &[u8],
digest: &mut dyn DynDigest,
mgf_digest: &mut dyn DynDigest,
label: Option<String>,
) -> Result<Vec<u8>> {
let h_size = digest.output_size();
let label = label.unwrap_or_default();
if label.len() as u64 > MAX_LABEL_LEN {
return Err(Error::LabelTooLong);
}
digest.update(label.as_bytes());
let p_hash = digest.finalize_reset();
encrypt_internal(rng, pub_key, msg, &p_hash, h_size, |seed, db| {
mgf1_xor(db, mgf_digest, seed);
mgf1_xor(seed, mgf_digest, db);
})
}
/// Encrypts the given message with RSA and the padding scheme from
/// [PKCS#1 OAEP].
///
/// The message must be no longer than the length of the public modulus minus
/// `2 + (2 * hash.size())`.
///
/// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
#[inline]
fn encrypt_digest<
R: CryptoRngCore + ?Sized,
K: PublicKey,
D: Digest,
MGD: Digest + FixedOutputReset,
>(
rng: &mut R,
pub_key: &K,
msg: &[u8],
label: Option<String>,
) -> Result<Vec<u8>> {
let h_size = <D as Digest>::output_size();
let label = label.unwrap_or_default();
if label.len() as u64 > MAX_LABEL_LEN {
return Err(Error::LabelTooLong);
}
let p_hash = D::digest(label.as_bytes());
encrypt_internal(rng, pub_key, msg, &p_hash, h_size, |seed, db| {
let mut mgf_digest = MGD::new();
mgf1_xor_digest(db, &mut mgf_digest, seed);
mgf1_xor_digest(seed, &mut mgf_digest, db);
})
}
/// Decrypts a plaintext using RSA and the padding scheme from [PKCS#1 OAEP].
///
/// If an `rng` is passed, it uses RSA blinding to avoid timing side-channel attacks.
@ -231,7 +294,7 @@ pub fn encrypt<R: CryptoRngCore, K: PublicKey>(
///
/// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
#[inline]
pub fn decrypt<R: CryptoRngCore, SK: PrivateKey>(
fn decrypt<R: CryptoRngCore + ?Sized, SK: PrivateKey>(
rng: Option<&mut R>,
priv_key: &SK,
ciphertext: &[u8],
@ -241,7 +304,84 @@ pub fn decrypt<R: CryptoRngCore, SK: PrivateKey>(
) -> Result<Vec<u8>> {
key::check_public(priv_key)?;
let res = decrypt_inner(rng, priv_key, ciphertext, digest, mgf_digest, label)?;
let h_size = digest.output_size();
let label = label.unwrap_or_default();
if label.len() as u64 > MAX_LABEL_LEN {
return Err(Error::Decryption);
}
digest.update(label.as_bytes());
let expected_p_hash = digest.finalize_reset();
let res = decrypt_inner(
rng,
priv_key,
ciphertext,
h_size,
&expected_p_hash,
|seed, db| {
mgf1_xor(seed, mgf_digest, db);
mgf1_xor(db, mgf_digest, seed);
},
)?;
if res.is_none().into() {
return Err(Error::Decryption);
}
let (out, index) = res.unwrap();
Ok(out[index as usize..].to_vec())
}
/// Decrypts a plaintext using RSA and the padding scheme from [PKCS#1 OAEP].
///
/// If an `rng` is passed, it uses RSA blinding to avoid timing side-channel attacks.
///
/// Note that whether this function returns an error or not discloses secret
/// information. If an attacker can cause this function to run repeatedly and
/// learn whether each instance returned an error then they can decrypt and
/// forge signatures as if they had the private key.
///
/// See `decrypt_session_key` for a way of solving this problem.
///
/// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
#[inline]
fn decrypt_digest<
R: CryptoRngCore + ?Sized,
SK: PrivateKey,
D: Digest,
MGD: Digest + FixedOutputReset,
>(
rng: Option<&mut R>,
priv_key: &SK,
ciphertext: &[u8],
label: Option<String>,
) -> Result<Vec<u8>> {
key::check_public(priv_key)?;
let h_size = <D as Digest>::output_size();
let label = label.unwrap_or_default();
if label.len() as u64 > MAX_LABEL_LEN {
return Err(Error::LabelTooLong);
}
let expected_p_hash = D::digest(label.as_bytes());
let res = decrypt_inner(
rng,
priv_key,
ciphertext,
h_size,
&expected_p_hash,
|seed, db| {
let mut mgf_digest = MGD::new();
mgf1_xor_digest(seed, &mut mgf_digest, db);
mgf1_xor_digest(db, &mut mgf_digest, seed);
},
)?;
if res.is_none().into() {
return Err(Error::Decryption);
}
@ -255,43 +395,35 @@ pub fn decrypt<R: CryptoRngCore, SK: PrivateKey>(
/// `rng` is given. It returns one or zero in valid that indicates whether the
/// plaintext was correctly structured.
#[inline]
fn decrypt_inner<R: CryptoRngCore, SK: PrivateKey>(
fn decrypt_inner<
R: CryptoRngCore + ?Sized,
SK: PrivateKey,
MGF: FnMut(&mut [u8], &mut [u8]) -> (),
>(
rng: Option<&mut R>,
priv_key: &SK,
ciphertext: &[u8],
digest: &mut dyn DynDigest,
mgf_digest: &mut dyn DynDigest,
label: Option<String>,
h_size: usize,
expected_p_hash: &[u8],
mut mgf: MGF,
) -> Result<CtOption<(Vec<u8>, u32)>> {
let k = priv_key.size();
if k < 11 {
return Err(Error::Decryption);
}
let h_size = digest.output_size();
if ciphertext.len() != k || k < h_size * 2 + 2 {
return Err(Error::Decryption);
}
let mut em = priv_key.raw_decryption_primitive(rng, ciphertext, priv_key.size())?;
let label = label.unwrap_or_default();
if label.len() as u64 > MAX_LABEL_LEN {
return Err(Error::LabelTooLong);
}
digest.update(label.as_bytes());
let expected_p_hash = &*digest.finalize_reset();
let first_byte_is_zero = em[0].ct_eq(&0u8);
let (_, payload) = em.split_at_mut(1);
let (seed, db) = payload.split_at_mut(h_size);
mgf1_xor(seed, mgf_digest, db);
mgf1_xor(db, mgf_digest, seed);
mgf(seed, db);
let hash_are_equal = db[0..h_size].ct_eq(expected_p_hash);
@ -316,3 +448,436 @@ fn decrypt_inner<R: CryptoRngCore, SK: PrivateKey>(
Ok(CtOption::new((em, index + 2 + (h_size * 2) as u32), valid))
}
/// Encryption key for PKCS#1 v1.5 encryption as described in [RFC8017 § 7.1].
///
/// [RFC8017 § 7.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
#[derive(Debug, Clone)]
pub struct EncryptingKey<D, MGD = D>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
inner: RsaPublicKey,
label: Option<String>,
phantom: PhantomData<D>,
mg_phantom: PhantomData<MGD>,
}
impl<D, MGD> EncryptingKey<D, MGD>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
/// Create a new verifying key from an RSA public key.
pub fn new(key: RsaPublicKey) -> Self {
Self {
inner: key,
label: None,
phantom: Default::default(),
mg_phantom: Default::default(),
}
}
/// Create a new verifying key from an RSA public key using provided label
pub fn new_with_label<S: AsRef<str>>(key: RsaPublicKey, label: S) -> Self {
Self {
inner: key,
label: Some(label.as_ref().to_string()),
phantom: Default::default(),
mg_phantom: Default::default(),
}
}
}
impl<D, MGD> RandomizedEncryptor for EncryptingKey<D, MGD>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
fn encrypt_with_rng<R: CryptoRngCore + ?Sized>(
&self,
rng: &mut R,
msg: &[u8],
) -> Result<Vec<u8>> {
encrypt_digest::<_, _, D, MGD>(rng, &self.inner, msg, self.label.as_ref().cloned())
}
}
/// Decryption key for PKCS#1 v1.5 decryption as described in [RFC8017 § 7.1].
///
/// [RFC8017 § 7.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
#[derive(Debug, Clone)]
pub struct DecryptingKey<D, MGD = D>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
inner: RsaPrivateKey,
label: Option<String>,
phantom: PhantomData<D>,
mg_phantom: PhantomData<MGD>,
}
impl<D, MGD> DecryptingKey<D, MGD>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
/// Create a new verifying key from an RSA public key.
pub fn new(key: RsaPrivateKey) -> Self {
Self {
inner: key,
label: None,
phantom: Default::default(),
mg_phantom: Default::default(),
}
}
/// Create a new verifying key from an RSA public key using provided label
pub fn new_with_label<S: AsRef<str>>(key: RsaPrivateKey, label: S) -> Self {
Self {
inner: key,
label: Some(label.as_ref().to_string()),
phantom: Default::default(),
mg_phantom: Default::default(),
}
}
}
impl<D, MGD> Decryptor for DecryptingKey<D, MGD>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
decrypt_digest::<DummyRng, _, D, MGD>(
None,
&self.inner,
ciphertext,
self.label.as_ref().cloned(),
)
}
}
impl<D, MGD> RandomizedDecryptor for DecryptingKey<D, MGD>
where
D: Digest,
MGD: Digest + FixedOutputReset,
{
fn decrypt_with_rng<R: CryptoRngCore + ?Sized>(
&self,
rng: &mut R,
ciphertext: &[u8],
) -> Result<Vec<u8>> {
decrypt_digest::<_, _, D, MGD>(
Some(rng),
&self.inner,
ciphertext,
self.label.as_ref().cloned(),
)
}
}
#[cfg(test)]
mod tests {
use crate::key::{PublicKey, PublicKeyParts, RsaPrivateKey, RsaPublicKey};
use crate::oaep::{DecryptingKey, EncryptingKey, Oaep};
use crate::traits::{Decryptor, RandomizedDecryptor, RandomizedEncryptor};
use alloc::string::String;
use digest::{Digest, DynDigest, FixedOutputReset};
use num_bigint::BigUint;
use num_traits::FromPrimitive;
use rand_chacha::{
rand_core::{RngCore, SeedableRng},
ChaCha8Rng,
};
use sha1::Sha1;
use sha2::{Sha224, Sha256, Sha384, Sha512};
use sha3::{Sha3_256, Sha3_384, Sha3_512};
fn get_private_key() -> RsaPrivateKey {
// -----BEGIN RSA PRIVATE KEY-----
// MIIEpAIBAAKCAQEA05e4TZikwmE47RtpWoEG6tkdVTvwYEG2LT/cUKBB4iK49FKW
// icG4LF5xVU9d1p+i9LYVjPDb61eBGg/DJ+HyjnT+dNO8Fmweq9wbi1e5NMqL5bAL
// TymXW8yZrK9BW1m7KKZ4K7QaLDwpdrPBjbre9i8AxrsiZkAJUJbAzGDSL+fvmH11
// xqgbENlr8pICivEQ3HzBu8Q9Iq2rN5oM1dgHjMeA/1zWIJ3qNMkiz3hPdxfkKNdb
// WuyP8w5fAUFRB2bi4KuNRzyE6HELK5gifD2wlTN600UvGeK5v7zN2BSKv2d2+lUn
// debnWVbkUimuWpxGlJurHmIvDkj1ZSSoTtNIOwIDAQABAoIBAQDE5wxokWLJTGYI
// KBkbUrTYOSEV30hqmtvoMeRY1zlYMg3Bt1VFbpNwHpcC12+wuS+Q4B0f4kgVMoH+
// eaqXY6kvrmnY1+zRRN4p+hNb0U+Vc+NJ5FAx47dpgvWDADgmxVLomjl8Gga9IWNI
// hjDZLowrtkPXq+9wDaldaFyUFImkb1S1MW9itdLDp/G70TTLNzU6RGg/3J2V02RY
// 3iL2xEBX/nSgpDbEMI9z9NpC81xHrBanE41IOvyR5B3DoRJzguDA9RGbAiG0/GOd
// a5w4F3pt6bUm69iMONeYLAf5ig79h31Qiq4nW5RpFcAuLhEG0XXXTsZ3f16A0SwF
// PZx74eNBAoGBAPgnu/OkGHfHzFmuv0LtSynDLe/LjtloY9WwkKBaiTDdYkohydz5
// g4Vo/foN9luEYqXyrJE9bFb5dVMr2OePsHvUBcqZpIS89Z8Bm73cs5M/K85wYwC0
// 97EQEgxd+QGBWQZ8NdowYaVshjWlK1QnOzEnG0MR8Hld9gIeY1XhpC5hAoGBANpI
// F84Aid028q3mo/9BDHPsNL8bT2vaOEMb/t4RzvH39u+nDl+AY6Ox9uFylv+xX+76
// CRKgMluNH9ZaVZ5xe1uWHsNFBy4OxSA9A0QdKa9NZAVKBFB0EM8dp457YRnZCexm
// 5q1iW/mVsnmks8W+fYlc18W5xMSX/ecwkW/NtOQbAoGAHabpz4AhKFbodSLrWbzv
// CUt4NroVFKdjnoodjfujfwJFF2SYMV5jN9LG3lVCxca43ulzc1tqka33Nfv8TBcg
// WHuKQZ5ASVgm5VwU1wgDMSoQOve07MWy/yZTccTc1zA0ihDXgn3bfR/NnaVh2wlh
// CkuI92eyW1494hztc7qlmqECgYEA1zenyOQ9ChDIW/ABGIahaZamNxsNRrDFMl3j
// AD+cxHSRU59qC32CQH8ShRy/huHzTaPX2DZ9EEln76fnrS4Ey7uLH0rrFl1XvT6K
// /timJgLvMEvXTx/xBtUdRN2fUqXtI9odbSyCtOYFL+zVl44HJq2UzY4pVRDrNcxs
// SUkQJqsCgYBSaNfPBzR5rrstLtTdZrjImRW1LRQeDEky9WsMDtCTYUGJTsTSfVO8
// hkU82MpbRVBFIYx+GWIJwcZRcC7OCQoV48vMJllxMAAjqG/p00rVJ+nvA7et/nNu
// BoB0er/UmDm4Ly/97EO9A0PKMOE5YbMq9s3t3RlWcsdrU7dvw+p2+A==
// -----END RSA PRIVATE KEY-----
RsaPrivateKey::from_components(
BigUint::parse_bytes(b"00d397b84d98a4c26138ed1b695a8106ead91d553bf06041b62d3fdc50a041e222b8f4529689c1b82c5e71554f5dd69fa2f4b6158cf0dbeb57811a0fc327e1f28e74fe74d3bc166c1eabdc1b8b57b934ca8be5b00b4f29975bcc99acaf415b59bb28a6782bb41a2c3c2976b3c18dbadef62f00c6bb226640095096c0cc60d22fe7ef987d75c6a81b10d96bf292028af110dc7cc1bbc43d22adab379a0cd5d8078cc780ff5cd6209dea34c922cf784f7717e428d75b5aec8ff30e5f0141510766e2e0ab8d473c84e8710b2b98227c3db095337ad3452f19e2b9bfbccdd8148abf6776fa552775e6e75956e45229ae5a9c46949bab1e622f0e48f56524a84ed3483b", 16).unwrap(),
BigUint::from_u64(65537).unwrap(),
BigUint::parse_bytes(b"00c4e70c689162c94c660828191b52b4d8392115df486a9adbe831e458d73958320dc1b755456e93701e9702d76fb0b92f90e01d1fe248153281fe79aa9763a92fae69d8d7ecd144de29fa135bd14f9573e349e45031e3b76982f583003826c552e89a397c1a06bd2163488630d92e8c2bb643d7abef700da95d685c941489a46f54b5316f62b5d2c3a7f1bbd134cb37353a44683fdc9d95d36458de22f6c44057fe74a0a436c4308f73f4da42f35c47ac16a7138d483afc91e41dc3a1127382e0c0f5119b0221b4fc639d6b9c38177a6de9b526ebd88c38d7982c07f98a0efd877d508aae275b946915c02e2e1106d175d74ec6777f5e80d12c053d9c7be1e341", 16).unwrap(),
vec![
BigUint::parse_bytes(b"00f827bbf3a41877c7cc59aebf42ed4b29c32defcb8ed96863d5b090a05a8930dd624a21c9dcf9838568fdfa0df65b8462a5f2ac913d6c56f975532bd8e78fb07bd405ca99a484bcf59f019bbddcb3933f2bce706300b4f7b110120c5df9018159067c35da3061a56c8635a52b54273b31271b4311f0795df6021e6355e1a42e61",16).unwrap(),
BigUint::parse_bytes(b"00da4817ce0089dd36f2ade6a3ff410c73ec34bf1b4f6bda38431bfede11cef1f7f6efa70e5f8063a3b1f6e17296ffb15feefa0912a0325b8d1fd65a559e717b5b961ec345072e0ec5203d03441d29af4d64054a04507410cf1da78e7b6119d909ec66e6ad625bf995b279a4b3c5be7d895cd7c5b9c4c497fde730916fcdb4e41b", 16).unwrap()
],
).unwrap()
}
#[test]
fn test_encrypt_decrypt_oaep() {
let priv_key = get_private_key();
do_test_encrypt_decrypt_oaep::<Sha1>(&priv_key);
do_test_encrypt_decrypt_oaep::<Sha224>(&priv_key);
do_test_encrypt_decrypt_oaep::<Sha256>(&priv_key);
do_test_encrypt_decrypt_oaep::<Sha384>(&priv_key);
do_test_encrypt_decrypt_oaep::<Sha512>(&priv_key);
do_test_encrypt_decrypt_oaep::<Sha3_256>(&priv_key);
do_test_encrypt_decrypt_oaep::<Sha3_384>(&priv_key);
do_test_encrypt_decrypt_oaep::<Sha3_512>(&priv_key);
do_test_oaep_with_different_hashes::<Sha1, Sha1>(&priv_key);
do_test_oaep_with_different_hashes::<Sha224, Sha1>(&priv_key);
do_test_oaep_with_different_hashes::<Sha256, Sha1>(&priv_key);
do_test_oaep_with_different_hashes::<Sha384, Sha1>(&priv_key);
do_test_oaep_with_different_hashes::<Sha512, Sha1>(&priv_key);
do_test_oaep_with_different_hashes::<Sha3_256, Sha1>(&priv_key);
do_test_oaep_with_different_hashes::<Sha3_384, Sha1>(&priv_key);
do_test_oaep_with_different_hashes::<Sha3_512, Sha1>(&priv_key);
}
fn get_label(rng: &mut ChaCha8Rng) -> Option<String> {
const GEN_ASCII_STR_CHARSET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789=+";
let mut buf = [0u8; 32];
rng.fill_bytes(&mut buf);
if buf[0] < (1 << 7) {
for v in buf.iter_mut() {
*v = GEN_ASCII_STR_CHARSET[(*v >> 2) as usize];
}
Some(core::str::from_utf8(&buf).unwrap().to_string())
} else {
None
}
}
fn do_test_encrypt_decrypt_oaep<D: 'static + Digest + DynDigest + Send + Sync>(
prk: &RsaPrivateKey,
) {
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let k = prk.size();
for i in 1..8 {
let mut input = vec![0u8; i * 8];
rng.fill_bytes(&mut input);
if input.len() > k - 11 {
input = input[0..k - 11].to_vec();
}
let label = get_label(&mut rng);
let pub_key: RsaPublicKey = prk.into();
let ciphertext = if let Some(ref label) = label {
let padding = Oaep::new_with_label::<D, _>(label);
pub_key.encrypt(&mut rng, padding, &input).unwrap()
} else {
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 {
Oaep::new_with_label::<D, _>(label)
} else {
Oaep::new::<D>()
};
let plaintext = if blind {
prk.decrypt(padding, &ciphertext).unwrap()
} else {
prk.decrypt_blinded(&mut rng, padding, &ciphertext).unwrap()
};
assert_eq!(input, plaintext);
}
}
fn do_test_oaep_with_different_hashes<
D: 'static + Digest + DynDigest + Send + Sync,
U: 'static + Digest + DynDigest + Send + Sync,
>(
prk: &RsaPrivateKey,
) {
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let k = prk.size();
for i in 1..8 {
let mut input = vec![0u8; i * 8];
rng.fill_bytes(&mut input);
if input.len() > k - 11 {
input = input[0..k - 11].to_vec();
}
let label = get_label(&mut rng);
let pub_key: RsaPublicKey = prk.into();
let ciphertext = if let Some(ref label) = label {
let padding = Oaep::new_with_mgf_hash_and_label::<D, U, _>(label);
pub_key.encrypt(&mut rng, padding, &input).unwrap()
} else {
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 {
Oaep::new_with_mgf_hash_and_label::<D, U, _>(label)
} else {
Oaep::new_with_mgf_hash::<D, U>()
};
let plaintext = if blind {
prk.decrypt(padding, &ciphertext).unwrap()
} else {
prk.decrypt_blinded(&mut rng, padding, &ciphertext).unwrap()
};
assert_eq!(input, plaintext);
}
}
#[test]
fn test_decrypt_oaep_invalid_hash() {
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let priv_key = get_private_key();
let pub_key: RsaPublicKey = (&priv_key).into();
let ciphertext = pub_key
.encrypt(&mut rng, Oaep::new::<Sha1>(), "a_plain_text".as_bytes())
.unwrap();
assert!(
priv_key
.decrypt_blinded(
&mut rng,
Oaep::new_with_label::<Sha1, _>("label"),
&ciphertext,
)
.is_err(),
"decrypt should have failed on hash verification"
);
}
#[test]
fn test_encrypt_decrypt_oaep_traits() {
let priv_key = get_private_key();
do_test_encrypt_decrypt_oaep_traits::<Sha1>(&priv_key);
do_test_encrypt_decrypt_oaep_traits::<Sha224>(&priv_key);
do_test_encrypt_decrypt_oaep_traits::<Sha256>(&priv_key);
do_test_encrypt_decrypt_oaep_traits::<Sha384>(&priv_key);
do_test_encrypt_decrypt_oaep_traits::<Sha512>(&priv_key);
do_test_encrypt_decrypt_oaep_traits::<Sha3_256>(&priv_key);
do_test_encrypt_decrypt_oaep_traits::<Sha3_384>(&priv_key);
do_test_encrypt_decrypt_oaep_traits::<Sha3_512>(&priv_key);
do_test_oaep_with_different_hashes_traits::<Sha1, Sha1>(&priv_key);
do_test_oaep_with_different_hashes_traits::<Sha224, Sha1>(&priv_key);
do_test_oaep_with_different_hashes_traits::<Sha256, Sha1>(&priv_key);
do_test_oaep_with_different_hashes_traits::<Sha384, Sha1>(&priv_key);
do_test_oaep_with_different_hashes_traits::<Sha512, Sha1>(&priv_key);
do_test_oaep_with_different_hashes_traits::<Sha3_256, Sha1>(&priv_key);
do_test_oaep_with_different_hashes_traits::<Sha3_384, Sha1>(&priv_key);
do_test_oaep_with_different_hashes_traits::<Sha3_512, Sha1>(&priv_key);
}
fn do_test_encrypt_decrypt_oaep_traits<D: Digest + FixedOutputReset>(prk: &RsaPrivateKey) {
do_test_oaep_with_different_hashes_traits::<D, D>(prk);
}
fn do_test_oaep_with_different_hashes_traits<D: Digest, MGD: Digest + FixedOutputReset>(
prk: &RsaPrivateKey,
) {
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let k = prk.size();
for i in 1..8 {
let mut input = vec![0u8; i * 8];
rng.fill_bytes(&mut input);
if input.len() > k - 11 {
input = input[0..k - 11].to_vec();
}
let label = get_label(&mut rng);
let pub_key: RsaPublicKey = prk.into();
let ciphertext = if let Some(ref label) = label {
let encrypting_key =
EncryptingKey::<D, MGD>::new_with_label(pub_key, label.clone());
encrypting_key.encrypt_with_rng(&mut rng, &input).unwrap()
} else {
let encrypting_key = EncryptingKey::<D, MGD>::new(pub_key);
encrypting_key.encrypt_with_rng(&mut rng, &input).unwrap()
};
assert_ne!(input, ciphertext);
let blind: bool = rng.next_u32() < (1 << 31);
let decrypting_key = if let Some(ref label) = label {
DecryptingKey::<D, MGD>::new_with_label(prk.clone(), label.clone())
} else {
DecryptingKey::<D, MGD>::new(prk.clone())
};
let plaintext = if blind {
decrypting_key.decrypt(&ciphertext).unwrap()
} else {
decrypting_key
.decrypt_with_rng(&mut rng, &ciphertext)
.unwrap()
};
assert_eq!(input, plaintext);
}
}
#[test]
fn test_decrypt_oaep_invalid_hash_traits() {
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let priv_key = get_private_key();
let pub_key: RsaPublicKey = (&priv_key).into();
let encrypting_key = EncryptingKey::<Sha1>::new(pub_key);
let decrypting_key = DecryptingKey::<Sha1>::new_with_label(priv_key, "label");
let ciphertext = encrypting_key
.encrypt_with_rng(&mut rng, "a_plain_text".as_bytes())
.unwrap();
assert!(
decrypting_key
.decrypt_with_rng(&mut rng, &ciphertext)
.is_err(),
"decrypt should have failed on hash verification"
);
}
}

View File

@ -25,6 +25,7 @@ use crate::dummy_rng::DummyRng;
use crate::errors::{Error, Result};
use crate::key::{self, PrivateKey, PublicKey};
use crate::padding::{PaddingScheme, SignatureScheme};
use crate::traits::{Decryptor, EncryptingKeypair, RandomizedDecryptor, RandomizedEncryptor};
use crate::{RsaPrivateKey, RsaPublicKey};
/// Encryption using PKCS#1 v1.5 padding.
@ -188,7 +189,7 @@ impl Display for Signature {
/// scheme from PKCS#1 v1.5. The message must be no longer than the
/// length of the public modulus minus 11 bytes.
#[inline]
pub(crate) fn encrypt<R: CryptoRngCore, PK: PublicKey>(
pub(crate) fn encrypt<R: CryptoRngCore + ?Sized, PK: PublicKey>(
rng: &mut R,
pub_key: &PK,
msg: &[u8],
@ -220,7 +221,7 @@ pub(crate) fn encrypt<R: CryptoRngCore, PK: PublicKey>(
/// forge signatures as if they had the private key. See
/// `decrypt_session_key` for a way of solving this problem.
#[inline]
pub(crate) fn decrypt<R: CryptoRngCore, SK: PrivateKey>(
pub(crate) fn decrypt<R: CryptoRngCore + ?Sized, SK: PrivateKey>(
rng: Option<&mut R>,
priv_key: &SK,
ciphertext: &[u8],
@ -337,7 +338,7 @@ where
/// in order to maintain constant memory access patterns. If the plaintext was
/// valid then index contains the index of the original message in em.
#[inline]
fn decrypt_inner<R: CryptoRngCore, SK: PrivateKey>(
fn decrypt_inner<R: CryptoRngCore + ?Sized, SK: PrivateKey>(
rng: Option<&mut R>,
priv_key: &SK,
ciphertext: &[u8],
@ -382,7 +383,7 @@ fn decrypt_inner<R: CryptoRngCore, SK: PrivateKey>(
/// Fills the provided slice with random values, which are guaranteed
/// to not be zero.
#[inline]
fn non_zero_random_bytes<R: CryptoRngCore>(rng: &mut R, data: &mut [u8]) {
fn non_zero_random_bytes<R: CryptoRngCore + ?Sized>(rng: &mut R, data: &mut [u8]) {
rng.fill_bytes(data);
for el in data {
@ -708,6 +709,71 @@ where
}
}
/// Encryption key for PKCS#1 v1.5 encryption as described in [RFC8017 § 7.2].
///
/// [RFC8017 § 7.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.2
#[derive(Debug, Clone)]
pub struct EncryptingKey {
inner: RsaPublicKey,
}
impl EncryptingKey {
/// Create a new verifying key from an RSA public key.
pub fn new(key: RsaPublicKey) -> Self {
Self { inner: key }
}
}
impl RandomizedEncryptor for EncryptingKey {
fn encrypt_with_rng<R: CryptoRngCore + ?Sized>(
&self,
rng: &mut R,
msg: &[u8],
) -> Result<Vec<u8>> {
encrypt(rng, &self.inner, msg)
}
}
/// Decryption key for PKCS#1 v1.5 decryption as described in [RFC8017 § 7.2].
///
/// [RFC8017 § 7.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.2
#[derive(Debug, Clone)]
pub struct DecryptingKey {
inner: RsaPrivateKey,
}
impl DecryptingKey {
/// Create a new verifying key from an RSA public key.
pub fn new(key: RsaPrivateKey) -> Self {
Self { inner: key }
}
}
impl Decryptor for DecryptingKey {
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
decrypt::<DummyRng, _>(None, &self.inner, ciphertext)
}
}
impl RandomizedDecryptor for DecryptingKey {
fn decrypt_with_rng<R: CryptoRngCore + ?Sized>(
&self,
rng: &mut R,
ciphertext: &[u8],
) -> Result<Vec<u8>> {
decrypt(Some(rng), &self.inner, ciphertext)
}
}
impl EncryptingKeypair for DecryptingKey {
type EncryptingKey = EncryptingKey;
fn encrypting_key(&self) -> EncryptingKey {
EncryptingKey {
inner: self.inner.clone().into(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -812,6 +878,63 @@ mod tests {
}
}
#[test]
fn test_decrypt_pkcs1v15_traits() {
let priv_key = get_private_key();
let decrypting_key = DecryptingKey::new(priv_key);
let tests = [[
"gIcUIoVkD6ATMBk/u/nlCZCCWRKdkfjCgFdo35VpRXLduiKXhNz1XupLLzTXAybEq15juc+EgY5o0DHv/nt3yg==",
"x",
], [
"Y7TOCSqofGhkRb+jaVRLzK8xw2cSo1IVES19utzv6hwvx+M8kFsoWQm5DzBeJCZTCVDPkTpavUuEbgp8hnUGDw==",
"testing.",
], [
"arReP9DJtEVyV2Dg3dDp4c/PSk1O6lxkoJ8HcFupoRorBZG+7+1fDAwT1olNddFnQMjmkb8vxwmNMoTAT/BFjQ==",
"testing.\n",
], [
"WtaBXIoGC54+vH0NH0CHHE+dRDOsMc/6BrfFu2lEqcKL9+uDuWaf+Xj9mrbQCjjZcpQuX733zyok/jsnqe/Ftw==",
"01234567890123456789012345678901234567890123456789012",
]];
for test in &tests {
let out = decrypting_key
.decrypt(&Base64::decode_vec(test[0]).unwrap())
.unwrap();
assert_eq!(out, test[1].as_bytes());
}
}
#[test]
fn test_encrypt_decrypt_pkcs1v15_traits() {
let mut rng = ChaCha8Rng::from_seed([42; 32]);
let priv_key = get_private_key();
let k = priv_key.size();
let decrypting_key = DecryptingKey::new(priv_key);
for i in 1..100 {
let mut input = vec![0u8; i * 8];
rng.fill_bytes(&mut input);
if input.len() > k - 11 {
input = input[0..k - 11].to_vec();
}
let encrypting_key = decrypting_key.encrypting_key();
let ciphertext = encrypting_key.encrypt_with_rng(&mut rng, &input).unwrap();
assert_ne!(input, ciphertext);
let blind: bool = rng.next_u32() < (1u32 << 31);
let plaintext = if blind {
decrypting_key
.decrypt_with_rng(&mut rng, &ciphertext)
.unwrap()
} else {
decrypting_key.decrypt(&ciphertext).unwrap()
};
assert_eq!(input, plaintext);
}
}
#[test]
fn test_sign_pkcs1v15() {
let priv_key = get_private_key();

42
src/traits.rs Normal file
View File

@ -0,0 +1,42 @@
//! Generic traits for message encryption and decryption
use alloc::vec::Vec;
use rand_core::CryptoRngCore;
use crate::errors::Result;
/// Encrypt the message using provided random source
pub trait RandomizedEncryptor {
/// Encrypt the given message.
fn encrypt_with_rng<R: CryptoRngCore + ?Sized>(
&self,
rng: &mut R,
msg: &[u8],
) -> Result<Vec<u8>>;
}
/// Decrypt the given message
pub trait Decryptor {
/// Decrypt the given message.
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>>;
}
/// Decrypt the given message using provided random source
pub trait RandomizedDecryptor {
/// Decrypt the given message.
fn decrypt_with_rng<R: CryptoRngCore + ?Sized>(
&self,
rng: &mut R,
ciphertext: &[u8],
) -> Result<Vec<u8>>;
}
/// Encryption keypair with an associated encryption key.
pub trait EncryptingKeypair {
/// Encrypting key type for this keypair.
type EncryptingKey: Clone;
/// Get the encrypting key which can encrypt messages to be decrypted by
/// the decryption key portion of this keypair.
fn encrypting_key(&self) -> Self::EncryptingKey;
}