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