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, FixedOutputReset};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use zeroize::Zeroizing;
use crate::algorithms::{mgf1_xor, mgf1_xor_digest};
use crate::dummy_rng::DummyRng;
use crate::errors::{Error, Result};
use crate::key::{self, PrivateKey, PublicKey, RsaPrivateKey, RsaPublicKey};
use crate::padding::PaddingScheme;
use crate::traits::{Decryptor, RandomizedDecryptor, RandomizedEncryptor};
const MAX_LABEL_LEN: u64 = 2_305_843_009_213_693_951;
pub struct Oaep {
    pub digest: Box<dyn DynDigest + Send + Sync>,
    pub mgf_digest: Box<dyn DynDigest + Send + Sync>,
    pub label: Option<String>,
}
impl Oaep {
    pub fn new<T: 'static + Digest + DynDigest + Send + Sync>() -> Self {
        Self {
            digest: Box::new(T::new()),
            mgf_digest: Box::new(T::new()),
            label: None,
        }
    }
    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()),
        }
    }
    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,
        }
    }
    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()
    }
}
#[inline]
fn encrypt_internal<
    'a,
    R: CryptoRngCore + ?Sized,
    K: PublicKey,
    MGF: FnMut(&mut [u8], &mut [u8]) -> (),
>(
    rng: &mut R,
    pub_key: &K,
    msg: &[u8],
    p_hash: &[u8],
    h_size: usize,
    mut mgf: MGF,
) -> Result<Vec<u8>> {
    key::check_public(pub_key)?;
    let k = pub_key.size();
    if msg.len() + 2 * h_size + 2 > k {
        return Err(Error::MessageTooLong);
    }
    let mut em = Zeroizing::new(vec![0u8; k]);
    let (_, payload) = em.split_at_mut(1);
    let (seed, db) = payload.split_at_mut(h_size);
    rng.fill_bytes(seed);
    let db_len = k - h_size - 1;
    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);
    mgf(seed, db);
    pub_key.raw_encryption_primitive(&em, pub_key.size())
}
#[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);
    })
}
#[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);
    })
}
#[inline]
fn decrypt<R: CryptoRngCore + ?Sized, SK: PrivateKey>(
    rng: Option<&mut R>,
    priv_key: &SK,
    ciphertext: &[u8],
    digest: &mut dyn DynDigest,
    mgf_digest: &mut dyn DynDigest,
    label: Option<String>,
) -> Result<Vec<u8>> {
    key::check_public(priv_key)?;
    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())
}
#[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);
    }
    let (out, index) = res.unwrap();
    Ok(out[index as usize..].to_vec())
}
#[inline]
fn decrypt_inner<
    R: CryptoRngCore + ?Sized,
    SK: PrivateKey,
    MGF: FnMut(&mut [u8], &mut [u8]) -> (),
>(
    rng: Option<&mut R>,
    priv_key: &SK,
    ciphertext: &[u8],
    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);
    }
    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 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);
    mgf(seed, db);
    let hash_are_equal = db[0..h_size].ct_eq(expected_p_hash);
    let mut looking_for_index = Choice::from(1u8);
    let mut index = 0u32;
    let mut nonzero_before_one = Choice::from(0u8);
    for (i, el) in db.iter().skip(h_size).enumerate() {
        let equals0 = el.ct_eq(&0u8);
        let equals1 = el.ct_eq(&1u8);
        index.conditional_assign(&(i as u32), looking_for_index & equals1);
        looking_for_index &= !equals1;
        nonzero_before_one |= looking_for_index & !equals0;
    }
    let valid = first_byte_is_zero & hash_are_equal & !nonzero_before_one & !looking_for_index;
    Ok(CtOption::new((em, index + 2 + (h_size * 2) as u32), valid))
}
#[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,
{
    pub fn new(key: RsaPublicKey) -> Self {
        Self {
            inner: key,
            label: None,
            phantom: Default::default(),
            mg_phantom: Default::default(),
        }
    }
    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())
    }
}
#[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,
{
    pub fn new(key: RsaPrivateKey) -> Self {
        Self {
            inner: key,
            label: None,
            phantom: Default::default(),
            mg_phantom: Default::default(),
        }
    }
    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 {
        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"
        );
    }
}