Skip to main content

rsa/
oaep.rs

1//! Encryption and Decryption using [OAEP padding](https://datatracker.ietf.org/doc/html/rfc8017#section-7.1).
2//!
3//! # Usage
4//!
5//! See [code example in the toplevel rustdoc](../index.html#oaep-encryption).
6
7mod decrypting_key;
8mod encrypting_key;
9
10pub use self::{decrypting_key::DecryptingKey, encrypting_key::EncryptingKey};
11
12use alloc::boxed::Box;
13use alloc::vec::Vec;
14use core::fmt;
15use crypto_bigint::BoxedUint;
16
17use digest::{Digest, FixedOutputReset};
18use rand_core::TryCryptoRng;
19
20use crate::algorithms::oaep::*;
21use crate::algorithms::pad::{uint_to_be_pad, uint_to_zeroizing_be_pad};
22use crate::algorithms::rsa::{rsa_decrypt_and_check, rsa_encrypt};
23use crate::errors::{Error, Result};
24use crate::key::{self, RsaPrivateKey, RsaPublicKey};
25use crate::traits::{PaddingScheme, PublicKeyParts};
26
27/// Encryption and Decryption using [OAEP padding](https://datatracker.ietf.org/doc/html/rfc8017#section-7.1).
28///
29/// - `digest` is used to hash the label. The maximum possible plaintext length is `m = k - 2 * h_len - 2`,
30///   where `k` is the size of the RSA modulus.
31/// - `mgf_digest` specifies the hash function that is used in the [MGF1](https://datatracker.ietf.org/doc/html/rfc8017#appendix-B.2).
32/// - `label` is optional data that can be associated with the message.
33///
34/// The two hash functions can, but don't need to be the same.
35///
36/// A prominent example is the [`AndroidKeyStore`](https://developer.android.com/guide/topics/security/cryptography#oaep-mgf1-digest).
37/// It uses SHA-1 for `mgf_digest` and a user-chosen SHA flavour for `digest`.
38pub struct Oaep<D, MGD = D> {
39    /// Digest type to use.
40    pub digest: D,
41
42    /// Digest to use for Mask Generation Function (MGF).
43    pub mgf_digest: MGD,
44
45    /// Optional label.
46    pub label: Option<Box<[u8]>>,
47}
48
49impl<D> Default for Oaep<D>
50where
51    D: Digest + FixedOutputReset,
52{
53    fn default() -> Self {
54        Self::new()
55    }
56}
57
58impl<D> Oaep<D>
59where
60    D: Digest + FixedOutputReset,
61{
62    /// Create a new OAEP `PaddingScheme`, using `T` as the hash function for both the default (empty) label and for MGF1.
63    ///
64    /// # Example
65    /// ```
66    /// use sha1::Sha1;
67    /// use sha2::Sha256;
68    /// use rsa::{RsaPublicKey, Oaep};
69    /// use base64ct::{Base64, Encoding};
70    /// use crypto_bigint::BoxedUint;
71    ///
72    /// let n_bytes = Base64::decode_vec("seAOhmYFAjH6NOaB54dboqw86uPXV/oK9ayJGV4mVClbvsDBJmF3bVkOaVMp9ogcFJTFFSy5g2HsTZIfHyuQVUJADb+BeRnkYrYhRvNJOKj2pcDbkxYe9XGMx8pIvxkDFnIpusb3gUsuzMUAU5qIstjwQKzuD51c6uJi0HAtQkr6Wmlt34SX7xkD/MfRuTu9uqmHmkiiJaCDHB2reYTPguetSWfuvp1qBJDNgSsp7BjwYANWldyrmZ8cLXEXYMUG5vtsWMxUzl8ertEr6kbnGM0OJghNuEtittW/dfTPvk683R1jj0hNaMzvHK8xYldUlLuwmWCYIIvpHBaA/w+FwQ==").unwrap();
73    /// let e_bytes = Base64::decode_vec("AQAB").unwrap();
74    /// let n = BoxedUint::from_be_slice(&n_bytes, 2048).unwrap();
75    /// let e = BoxedUint::from_be_slice(&e_bytes, 32).unwrap();
76    ///
77    /// let mut rng = rand::rng();
78    /// let key = RsaPublicKey::new(n, e).unwrap();
79    /// let padding = Oaep::<Sha256>::new();
80    /// let encrypted_data = key.encrypt(&mut rng, padding, b"secret").unwrap();
81    /// ```
82    pub fn new() -> Self {
83        Self {
84            digest: D::new(),
85            mgf_digest: D::new(),
86            label: None,
87        }
88    }
89
90    /// Create a new OAEP `PaddingScheme` with an associated `label`, using `T` as the hash function for both the label and for MGF1.
91    pub fn new_with_label<S: Into<Box<[u8]>>>(label: S) -> Self {
92        Self {
93            digest: D::new(),
94            mgf_digest: D::new(),
95            label: Some(label.into()),
96        }
97    }
98}
99
100impl<D, MGD> Oaep<D, MGD>
101where
102    D: Digest + FixedOutputReset,
103    MGD: Digest + FixedOutputReset,
104{
105    /// Create a new OAEP `PaddingScheme`, using `T` as the hash function for the default (empty) label, and `U` as the hash function for MGF1.
106    /// If a label is needed use `PaddingScheme::new_oaep_with_label` or `PaddingScheme::new_oaep_with_mgf_hash_with_label`.
107    ///
108    /// # Example
109    /// ```
110    /// use sha1::Sha1;
111    /// use sha2::Sha256;
112    /// use rsa::{RsaPublicKey, Oaep};
113    /// use base64ct::{Base64, Encoding};
114    /// use crypto_bigint::BoxedUint;
115    ///
116    /// let n_bytes = Base64::decode_vec("seAOhmYFAjH6NOaB54dboqw86uPXV/oK9ayJGV4mVClbvsDBJmF3bVkOaVMp9ogcFJTFFSy5g2HsTZIfHyuQVUJADb+BeRnkYrYhRvNJOKj2pcDbkxYe9XGMx8pIvxkDFnIpusb3gUsuzMUAU5qIstjwQKzuD51c6uJi0HAtQkr6Wmlt34SX7xkD/MfRuTu9uqmHmkiiJaCDHB2reYTPguetSWfuvp1qBJDNgSsp7BjwYANWldyrmZ8cLXEXYMUG5vtsWMxUzl8ertEr6kbnGM0OJghNuEtittW/dfTPvk683R1jj0hNaMzvHK8xYldUlLuwmWCYIIvpHBaA/w+FwQ==").unwrap();
117    /// let e_bytes = Base64::decode_vec("AQAB").unwrap();
118    /// let n = BoxedUint::from_be_slice(&n_bytes, 2048).unwrap();
119    /// let e = BoxedUint::from_be_slice(&e_bytes, 32).unwrap();
120    ///
121    /// let mut rng = rand::rng();
122    /// let key = RsaPublicKey::new(n, e).unwrap();
123    /// let padding = Oaep::<Sha256, Sha1>::new_with_mgf_hash();
124    /// let encrypted_data = key.encrypt(&mut rng, padding, b"secret").unwrap();
125    /// ```
126    pub fn new_with_mgf_hash() -> Self {
127        Self {
128            digest: D::new(),
129            mgf_digest: MGD::new(),
130            label: None,
131        }
132    }
133
134    /// 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.
135    pub fn new_with_mgf_hash_and_label<S: Into<Box<[u8]>>>(label: S) -> Self {
136        Self {
137            digest: D::new(),
138            mgf_digest: MGD::new(),
139            label: Some(label.into()),
140        }
141    }
142}
143
144impl<D, MGD> PaddingScheme for Oaep<D, MGD>
145where
146    D: Digest + FixedOutputReset,
147    MGD: Digest + FixedOutputReset,
148{
149    fn decrypt<Rng: TryCryptoRng + ?Sized>(
150        mut self,
151        rng: Option<&mut Rng>,
152        priv_key: &RsaPrivateKey,
153        ciphertext: &[u8],
154    ) -> Result<Vec<u8>> {
155        decrypt(
156            rng,
157            priv_key,
158            ciphertext,
159            &mut self.digest,
160            &mut self.mgf_digest,
161            self.label,
162        )
163    }
164
165    fn encrypt<Rng: TryCryptoRng + ?Sized>(
166        mut self,
167        rng: &mut Rng,
168        pub_key: &RsaPublicKey,
169        msg: &[u8],
170    ) -> Result<Vec<u8>> {
171        encrypt(
172            rng,
173            pub_key,
174            msg,
175            &mut self.digest,
176            &mut self.mgf_digest,
177            self.label,
178        )
179    }
180}
181
182impl<D, MGD> fmt::Debug for Oaep<D, MGD> {
183    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184        f.debug_struct("OAEP")
185            .field("digest", &"...")
186            .field("mgf_digest", &"...")
187            .field("label", &self.label)
188            .finish()
189    }
190}
191
192/// Encrypts the given message with RSA and the padding scheme from
193/// [PKCS#1 OAEP].
194///
195/// The message must be no longer than the length of the public modulus minus
196/// `2 + (2 * hash.size())`.
197///
198/// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
199#[inline]
200fn encrypt<R, D, MGD>(
201    rng: &mut R,
202    pub_key: &RsaPublicKey,
203    msg: &[u8],
204    digest: &mut D,
205    mgf_digest: &mut MGD,
206    label: Option<Box<[u8]>>,
207) -> Result<Vec<u8>>
208where
209    R: TryCryptoRng + ?Sized,
210    D: Digest + FixedOutputReset,
211    MGD: Digest + FixedOutputReset,
212{
213    key::check_public(pub_key)?;
214
215    let em = oaep_encrypt(rng, msg, digest, mgf_digest, label, pub_key.size())?;
216
217    let int = BoxedUint::from_be_slice(&em, pub_key.n_bits_precision())?;
218    uint_to_be_pad(rsa_encrypt(pub_key, &int)?, pub_key.size())
219}
220
221/// Encrypts the given message with RSA and the padding scheme from
222/// [PKCS#1 OAEP].
223///
224/// The message must be no longer than the length of the public modulus minus
225/// `2 + (2 * hash.size())`.
226///
227/// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
228fn encrypt_digest<R, D, MGD>(
229    rng: &mut R,
230    pub_key: &RsaPublicKey,
231    msg: &[u8],
232    label: Option<Box<[u8]>>,
233) -> Result<Vec<u8>>
234where
235    R: TryCryptoRng + ?Sized,
236    D: Digest,
237    MGD: Digest + FixedOutputReset,
238{
239    key::check_public(pub_key)?;
240
241    let em = oaep_encrypt_digest::<_, D, MGD>(rng, msg, label, pub_key.size())?;
242
243    let int = BoxedUint::from_be_slice(&em, pub_key.n_bits_precision())?;
244    uint_to_be_pad(rsa_encrypt(pub_key, &int)?, pub_key.size())
245}
246
247/// Decrypts a plaintext using RSA and the padding scheme from [PKCS#1 OAEP].
248///
249/// If an `rng` is passed, it uses RSA blinding to avoid timing side-channel attacks.
250///
251/// Note that whether this function returns an error or not discloses secret
252/// information. If an attacker can cause this function to run repeatedly and
253/// learn whether each instance returned an error then they can decrypt and
254/// forge signatures as if they had the private key.
255///
256/// See `decrypt_session_key` for a way of solving this problem.
257///
258/// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
259#[inline]
260fn decrypt<R, D, MGD>(
261    rng: Option<&mut R>,
262    priv_key: &RsaPrivateKey,
263    ciphertext: &[u8],
264    digest: &mut D,
265    mgf_digest: &mut MGD,
266    label: Option<Box<[u8]>>,
267) -> Result<Vec<u8>>
268where
269    R: TryCryptoRng + ?Sized,
270    D: Digest + FixedOutputReset,
271    MGD: Digest + FixedOutputReset,
272{
273    if ciphertext.len() != priv_key.size() {
274        return Err(Error::Decryption);
275    }
276
277    let ciphertext = BoxedUint::from_be_slice(ciphertext, priv_key.n_bits_precision())?;
278
279    let em = rsa_decrypt_and_check(priv_key, rng, &ciphertext)?;
280    let mut em = uint_to_zeroizing_be_pad(em, priv_key.size())?;
281
282    oaep_decrypt(&mut em, digest, mgf_digest, label, priv_key.size())
283}
284
285/// Decrypts a plaintext using RSA and the padding scheme from [PKCS#1 OAEP].
286///
287/// If an `rng` is passed, it uses RSA blinding to avoid timing side-channel attacks.
288///
289/// Note that whether this function returns an error or not discloses secret
290/// information. If an attacker can cause this function to run repeatedly and
291/// learn whether each instance returned an error then they can decrypt and
292/// forge signatures as if they had the private key.
293///
294/// See `decrypt_session_key` for a way of solving this problem.
295///
296/// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
297#[inline]
298fn decrypt_digest<R, D, MGD>(
299    rng: Option<&mut R>,
300    priv_key: &RsaPrivateKey,
301    ciphertext: &[u8],
302    label: Option<Box<[u8]>>,
303) -> Result<Vec<u8>>
304where
305    R: TryCryptoRng + ?Sized,
306    D: Digest,
307    MGD: Digest + FixedOutputReset,
308{
309    key::check_public(priv_key)?;
310
311    if ciphertext.len() != priv_key.size() {
312        return Err(Error::Decryption);
313    }
314
315    let ciphertext = BoxedUint::from_be_slice(ciphertext, priv_key.n_bits_precision())?;
316    let em = rsa_decrypt_and_check(priv_key, rng, &ciphertext)?;
317    let mut em = uint_to_zeroizing_be_pad(em, priv_key.size())?;
318
319    oaep_decrypt_digest::<D, MGD>(&mut em, label, priv_key.size())
320}
321
322#[cfg(test)]
323mod tests {
324    use crate::key::{RsaPrivateKey, RsaPublicKey};
325    use crate::oaep::{DecryptingKey, EncryptingKey, Oaep};
326    use crate::traits::PublicKeyParts;
327    use crate::traits::{Decryptor, RandomizedDecryptor, RandomizedEncryptor};
328
329    use crypto_bigint::BoxedUint;
330    use digest::{Digest, FixedOutputReset};
331    use rand::rngs::ChaCha8Rng;
332    use rand_core::{Rng, SeedableRng};
333    use sha1::Sha1;
334    use sha2::{Sha224, Sha256, Sha384, Sha512};
335    use sha3::{Sha3_256, Sha3_384, Sha3_512};
336
337    fn get_private_key() -> RsaPrivateKey {
338        // -----BEGIN RSA PRIVATE KEY-----
339        // MIIEpAIBAAKCAQEA05e4TZikwmE47RtpWoEG6tkdVTvwYEG2LT/cUKBB4iK49FKW
340        // icG4LF5xVU9d1p+i9LYVjPDb61eBGg/DJ+HyjnT+dNO8Fmweq9wbi1e5NMqL5bAL
341        // TymXW8yZrK9BW1m7KKZ4K7QaLDwpdrPBjbre9i8AxrsiZkAJUJbAzGDSL+fvmH11
342        // xqgbENlr8pICivEQ3HzBu8Q9Iq2rN5oM1dgHjMeA/1zWIJ3qNMkiz3hPdxfkKNdb
343        // WuyP8w5fAUFRB2bi4KuNRzyE6HELK5gifD2wlTN600UvGeK5v7zN2BSKv2d2+lUn
344        // debnWVbkUimuWpxGlJurHmIvDkj1ZSSoTtNIOwIDAQABAoIBAQDE5wxokWLJTGYI
345        // KBkbUrTYOSEV30hqmtvoMeRY1zlYMg3Bt1VFbpNwHpcC12+wuS+Q4B0f4kgVMoH+
346        // eaqXY6kvrmnY1+zRRN4p+hNb0U+Vc+NJ5FAx47dpgvWDADgmxVLomjl8Gga9IWNI
347        // hjDZLowrtkPXq+9wDaldaFyUFImkb1S1MW9itdLDp/G70TTLNzU6RGg/3J2V02RY
348        // 3iL2xEBX/nSgpDbEMI9z9NpC81xHrBanE41IOvyR5B3DoRJzguDA9RGbAiG0/GOd
349        // a5w4F3pt6bUm69iMONeYLAf5ig79h31Qiq4nW5RpFcAuLhEG0XXXTsZ3f16A0SwF
350        // PZx74eNBAoGBAPgnu/OkGHfHzFmuv0LtSynDLe/LjtloY9WwkKBaiTDdYkohydz5
351        // g4Vo/foN9luEYqXyrJE9bFb5dVMr2OePsHvUBcqZpIS89Z8Bm73cs5M/K85wYwC0
352        // 97EQEgxd+QGBWQZ8NdowYaVshjWlK1QnOzEnG0MR8Hld9gIeY1XhpC5hAoGBANpI
353        // F84Aid028q3mo/9BDHPsNL8bT2vaOEMb/t4RzvH39u+nDl+AY6Ox9uFylv+xX+76
354        // CRKgMluNH9ZaVZ5xe1uWHsNFBy4OxSA9A0QdKa9NZAVKBFB0EM8dp457YRnZCexm
355        // 5q1iW/mVsnmks8W+fYlc18W5xMSX/ecwkW/NtOQbAoGAHabpz4AhKFbodSLrWbzv
356        // CUt4NroVFKdjnoodjfujfwJFF2SYMV5jN9LG3lVCxca43ulzc1tqka33Nfv8TBcg
357        // WHuKQZ5ASVgm5VwU1wgDMSoQOve07MWy/yZTccTc1zA0ihDXgn3bfR/NnaVh2wlh
358        // CkuI92eyW1494hztc7qlmqECgYEA1zenyOQ9ChDIW/ABGIahaZamNxsNRrDFMl3j
359        // AD+cxHSRU59qC32CQH8ShRy/huHzTaPX2DZ9EEln76fnrS4Ey7uLH0rrFl1XvT6K
360        // /timJgLvMEvXTx/xBtUdRN2fUqXtI9odbSyCtOYFL+zVl44HJq2UzY4pVRDrNcxs
361        // SUkQJqsCgYBSaNfPBzR5rrstLtTdZrjImRW1LRQeDEky9WsMDtCTYUGJTsTSfVO8
362        // hkU82MpbRVBFIYx+GWIJwcZRcC7OCQoV48vMJllxMAAjqG/p00rVJ+nvA7et/nNu
363        // BoB0er/UmDm4Ly/97EO9A0PKMOE5YbMq9s3t3RlWcsdrU7dvw+p2+A==
364        // -----END RSA PRIVATE KEY-----
365
366        RsaPrivateKey::from_components(
367            BoxedUint::from_be_hex("d397b84d98a4c26138ed1b695a8106ead91d553bf06041b62d3fdc50a041e222b8f4529689c1b82c5e71554f5dd69fa2f4b6158cf0dbeb57811a0fc327e1f28e74fe74d3bc166c1eabdc1b8b57b934ca8be5b00b4f29975bcc99acaf415b59bb28a6782bb41a2c3c2976b3c18dbadef62f00c6bb226640095096c0cc60d22fe7ef987d75c6a81b10d96bf292028af110dc7cc1bbc43d22adab379a0cd5d8078cc780ff5cd6209dea34c922cf784f7717e428d75b5aec8ff30e5f0141510766e2e0ab8d473c84e8710b2b98227c3db095337ad3452f19e2b9bfbccdd8148abf6776fa552775e6e75956e45229ae5a9c46949bab1e622f0e48f56524a84ed3483b", 2048).unwrap(),
368            BoxedUint::from(65_537u64),
369            BoxedUint::from_be_hex("c4e70c689162c94c660828191b52b4d8392115df486a9adbe831e458d73958320dc1b755456e93701e9702d76fb0b92f90e01d1fe248153281fe79aa9763a92fae69d8d7ecd144de29fa135bd14f9573e349e45031e3b76982f583003826c552e89a397c1a06bd2163488630d92e8c2bb643d7abef700da95d685c941489a46f54b5316f62b5d2c3a7f1bbd134cb37353a44683fdc9d95d36458de22f6c44057fe74a0a436c4308f73f4da42f35c47ac16a7138d483afc91e41dc3a1127382e0c0f5119b0221b4fc639d6b9c38177a6de9b526ebd88c38d7982c07f98a0efd877d508aae275b946915c02e2e1106d175d74ec6777f5e80d12c053d9c7be1e341", 2048).unwrap(),
370            vec![
371                BoxedUint::from_be_hex("f827bbf3a41877c7cc59aebf42ed4b29c32defcb8ed96863d5b090a05a8930dd624a21c9dcf9838568fdfa0df65b8462a5f2ac913d6c56f975532bd8e78fb07bd405ca99a484bcf59f019bbddcb3933f2bce706300b4f7b110120c5df9018159067c35da3061a56c8635a52b54273b31271b4311f0795df6021e6355e1a42e61", 1024).unwrap(),
372                BoxedUint::from_be_hex("da4817ce0089dd36f2ade6a3ff410c73ec34bf1b4f6bda38431bfede11cef1f7f6efa70e5f8063a3b1f6e17296ffb15feefa0912a0325b8d1fd65a559e717b5b961ec345072e0ec5203d03441d29af4d64054a04507410cf1da78e7b6119d909ec66e6ad625bf995b279a4b3c5be7d895cd7c5b9c4c497fde730916fcdb4e41b", 1024).unwrap()
373            ],
374        ).unwrap()
375    }
376
377    #[test]
378    fn test_encrypt_decrypt_oaep() {
379        let priv_key = get_private_key();
380        do_test_encrypt_decrypt_oaep::<Sha1>(&priv_key);
381        do_test_encrypt_decrypt_oaep::<Sha224>(&priv_key);
382        do_test_encrypt_decrypt_oaep::<Sha256>(&priv_key);
383        do_test_encrypt_decrypt_oaep::<Sha384>(&priv_key);
384        do_test_encrypt_decrypt_oaep::<Sha512>(&priv_key);
385        do_test_encrypt_decrypt_oaep::<Sha3_256>(&priv_key);
386        do_test_encrypt_decrypt_oaep::<Sha3_384>(&priv_key);
387        do_test_encrypt_decrypt_oaep::<Sha3_512>(&priv_key);
388
389        do_test_oaep_with_different_hashes::<Sha1, Sha1>(&priv_key);
390        do_test_oaep_with_different_hashes::<Sha224, Sha1>(&priv_key);
391        do_test_oaep_with_different_hashes::<Sha256, Sha1>(&priv_key);
392        do_test_oaep_with_different_hashes::<Sha384, Sha1>(&priv_key);
393        do_test_oaep_with_different_hashes::<Sha512, Sha1>(&priv_key);
394        do_test_oaep_with_different_hashes::<Sha3_256, Sha1>(&priv_key);
395        do_test_oaep_with_different_hashes::<Sha3_384, Sha1>(&priv_key);
396        do_test_oaep_with_different_hashes::<Sha3_512, Sha1>(&priv_key);
397    }
398
399    fn get_label(rng: &mut ChaCha8Rng) -> Option<Box<[u8]>> {
400        let mut buf = [0u8; 32];
401        rng.fill_bytes(&mut buf);
402
403        if rng.next_u32() % 2 == 0 {
404            Some(buf.into())
405        } else {
406            None
407        }
408    }
409
410    fn do_test_encrypt_decrypt_oaep<D: Digest + FixedOutputReset>(prk: &RsaPrivateKey) {
411        let mut rng = ChaCha8Rng::from_seed([42; 32]);
412
413        let k = prk.size();
414
415        for i in 1..8 {
416            let mut input = vec![0u8; i * 8];
417            rng.fill_bytes(&mut input);
418
419            if input.len() > k - 11 {
420                input = input[0..k - 11].to_vec();
421            }
422            let label = get_label(&mut rng);
423
424            let pub_key: RsaPublicKey = prk.into();
425
426            let ciphertext = if let Some(ref label) = label {
427                let padding = Oaep::<D>::new_with_label(label.clone());
428                pub_key.encrypt(&mut rng, padding, &input).unwrap()
429            } else {
430                let padding = Oaep::<D>::new();
431                pub_key.encrypt(&mut rng, padding, &input).unwrap()
432            };
433
434            assert_ne!(input, ciphertext);
435            let blind: bool = rng.next_u32() < (1 << 31);
436
437            let padding = if let Some(label) = label {
438                Oaep::<D>::new_with_label::<Box<[u8]>>(label)
439            } else {
440                Oaep::<D>::new()
441            };
442
443            let plaintext = if blind {
444                prk.decrypt(padding, &ciphertext).unwrap()
445            } else {
446                prk.decrypt_blinded(&mut rng, padding, &ciphertext).unwrap()
447            };
448
449            assert_eq!(input, plaintext);
450        }
451    }
452
453    fn do_test_oaep_with_different_hashes<
454        D: Digest + FixedOutputReset,
455        U: Digest + FixedOutputReset,
456    >(
457        prk: &RsaPrivateKey,
458    ) {
459        let mut rng = ChaCha8Rng::from_seed([42; 32]);
460
461        let k = prk.size();
462
463        for i in 1..8 {
464            let mut input = vec![0u8; i * 8];
465            rng.fill_bytes(&mut input);
466
467            if input.len() > k - 11 {
468                input = input[0..k - 11].to_vec();
469            }
470            let label = get_label(&mut rng);
471
472            let pub_key: RsaPublicKey = prk.into();
473
474            let ciphertext = if let Some(ref label) = label {
475                let padding = Oaep::<D, U>::new_with_mgf_hash_and_label::<_>(label.clone());
476                pub_key.encrypt(&mut rng, padding, &input).unwrap()
477            } else {
478                let padding = Oaep::<D, U>::new_with_mgf_hash();
479                pub_key.encrypt(&mut rng, padding, &input).unwrap()
480            };
481
482            assert_ne!(input, ciphertext);
483            let blind: bool = rng.next_u32() < (1 << 31);
484
485            let padding = if let Some(label) = label {
486                Oaep::<D, U>::new_with_mgf_hash_and_label::<_>(label)
487            } else {
488                Oaep::<D, U>::new_with_mgf_hash()
489            };
490
491            let plaintext = if blind {
492                prk.decrypt(padding, &ciphertext).unwrap()
493            } else {
494                prk.decrypt_blinded(&mut rng, padding, &ciphertext).unwrap()
495            };
496
497            assert_eq!(input, plaintext);
498        }
499    }
500
501    #[test]
502    fn test_decrypt_oaep_invalid_hash() {
503        let mut rng = ChaCha8Rng::from_seed([42; 32]);
504        let priv_key = get_private_key();
505        let pub_key: RsaPublicKey = (&priv_key).into();
506        let ciphertext = pub_key
507            .encrypt(&mut rng, Oaep::<Sha1>::new(), "a_plain_text".as_bytes())
508            .unwrap();
509        assert!(
510            priv_key
511                .decrypt_blinded(
512                    &mut rng,
513                    Oaep::<Sha1>::new_with_label::<_>("label".as_bytes()),
514                    &ciphertext,
515                )
516                .is_err(),
517            "decrypt should have failed on hash verification"
518        );
519    }
520
521    #[test]
522    fn test_encrypt_decrypt_oaep_traits() {
523        let priv_key = get_private_key();
524        do_test_encrypt_decrypt_oaep_traits::<Sha1>(&priv_key);
525        do_test_encrypt_decrypt_oaep_traits::<Sha224>(&priv_key);
526        do_test_encrypt_decrypt_oaep_traits::<Sha256>(&priv_key);
527        do_test_encrypt_decrypt_oaep_traits::<Sha384>(&priv_key);
528        do_test_encrypt_decrypt_oaep_traits::<Sha512>(&priv_key);
529        do_test_encrypt_decrypt_oaep_traits::<Sha3_256>(&priv_key);
530        do_test_encrypt_decrypt_oaep_traits::<Sha3_384>(&priv_key);
531        do_test_encrypt_decrypt_oaep_traits::<Sha3_512>(&priv_key);
532
533        do_test_oaep_with_different_hashes_traits::<Sha1, Sha1>(&priv_key);
534        do_test_oaep_with_different_hashes_traits::<Sha224, Sha1>(&priv_key);
535        do_test_oaep_with_different_hashes_traits::<Sha256, Sha1>(&priv_key);
536        do_test_oaep_with_different_hashes_traits::<Sha384, Sha1>(&priv_key);
537        do_test_oaep_with_different_hashes_traits::<Sha512, Sha1>(&priv_key);
538        do_test_oaep_with_different_hashes_traits::<Sha3_256, Sha1>(&priv_key);
539        do_test_oaep_with_different_hashes_traits::<Sha3_384, Sha1>(&priv_key);
540        do_test_oaep_with_different_hashes_traits::<Sha3_512, Sha1>(&priv_key);
541    }
542
543    fn do_test_encrypt_decrypt_oaep_traits<D: Digest + FixedOutputReset>(prk: &RsaPrivateKey) {
544        do_test_oaep_with_different_hashes_traits::<D, D>(prk);
545    }
546
547    fn do_test_oaep_with_different_hashes_traits<D: Digest, MGD: Digest + FixedOutputReset>(
548        prk: &RsaPrivateKey,
549    ) {
550        let mut rng = ChaCha8Rng::from_seed([42; 32]);
551
552        let k = prk.size();
553
554        for i in 1..8 {
555            let mut input = vec![0u8; i * 8];
556            rng.fill_bytes(&mut input);
557
558            if input.len() > k - 11 {
559                input = input[0..k - 11].to_vec();
560            }
561            let label = get_label(&mut rng);
562
563            let pub_key: RsaPublicKey = prk.into();
564
565            let ciphertext = if let Some(ref label) = label {
566                let encrypting_key =
567                    EncryptingKey::<D, MGD>::new_with_label(pub_key, label.clone());
568                encrypting_key.encrypt_with_rng(&mut rng, &input).unwrap()
569            } else {
570                let encrypting_key = EncryptingKey::<D, MGD>::new(pub_key);
571                encrypting_key.encrypt_with_rng(&mut rng, &input).unwrap()
572            };
573
574            assert_ne!(input, ciphertext);
575            let blind: bool = rng.next_u32() < (1 << 31);
576
577            let decrypting_key = if let Some(ref label) = label {
578                DecryptingKey::<D, MGD>::new_with_label(prk.clone(), label.clone())
579            } else {
580                DecryptingKey::<D, MGD>::new(prk.clone())
581            };
582
583            let plaintext = if blind {
584                decrypting_key.decrypt(&ciphertext).unwrap()
585            } else {
586                decrypting_key
587                    .decrypt_with_rng(&mut rng, &ciphertext)
588                    .unwrap()
589            };
590
591            assert_eq!(input, plaintext);
592        }
593    }
594
595    #[test]
596    fn test_decrypt_oaep_invalid_hash_traits() {
597        let mut rng = ChaCha8Rng::from_seed([42; 32]);
598        let priv_key = get_private_key();
599        let pub_key: RsaPublicKey = (&priv_key).into();
600        let encrypting_key = EncryptingKey::<Sha1>::new(pub_key);
601        let decrypting_key = DecryptingKey::<Sha1>::new_with_label(priv_key, "label".as_bytes());
602        let ciphertext = encrypting_key
603            .encrypt_with_rng(&mut rng, "a_plain_text".as_bytes())
604            .unwrap();
605        assert!(
606            decrypting_key
607                .decrypt_with_rng(&mut rng, &ciphertext)
608                .is_err(),
609            "decrypt should have failed on hash verification"
610        );
611    }
612}