1#[cfg(feature = "alloc")]
8mod decrypting_key;
9mod encrypting_key;
10#[cfg(not(feature = "alloc"))]
11mod label;
12
13#[cfg(feature = "alloc")]
14pub use self::decrypting_key::DecryptingKey;
15#[cfg(feature = "alloc")]
16pub use self::encrypting_key::EncryptingKey;
17pub use self::encrypting_key::GenericEncryptingKey;
18#[cfg(not(feature = "alloc"))]
19pub use self::label::{Label, MAX_LABEL_LEN};
20
21#[cfg(feature = "alloc")]
22use alloc::boxed::Box;
23#[cfg(feature = "alloc")]
24use alloc::{vec, vec::Vec};
25use core::fmt;
26#[cfg(feature = "alloc")]
27use crypto_bigint::BoxedUint;
28
29use digest::{Digest, FixedOutputReset};
30use rand_core::TryCryptoRng;
31
32use crate::algorithms::oaep::*;
33#[cfg(feature = "alloc")]
34use crate::algorithms::pad::{uint_to_be_pad, uint_to_be_pad_into, uint_to_zeroizing_be_pad};
35#[cfg(feature = "alloc")]
36use crate::algorithms::rsa::rsa_decrypt_and_check;
37#[cfg(feature = "alloc")]
38use crate::algorithms::rsa::rsa_encrypt;
39use crate::errors::{Error, Result};
40#[cfg(feature = "alloc")]
41use crate::key::RsaPrivateKey;
42#[cfg(feature = "alloc")]
43use crate::key::{self, RsaPublicKey};
44use crate::traits::{PaddingScheme, PublicKeyParts, UnsignedModularInt};
45
46#[cfg(feature = "alloc")]
58pub struct Oaep<D, MGD = D> {
59 pub digest: D,
61
62 pub mgf_digest: MGD,
64
65 pub label: Option<Box<[u8]>>,
67}
68
69#[cfg(feature = "alloc")]
70impl<D> Default for Oaep<D>
71where
72 D: Digest + FixedOutputReset,
73{
74 fn default() -> Self {
75 Self::new()
76 }
77}
78
79#[cfg(feature = "alloc")]
80impl<D> Oaep<D>
81where
82 D: Digest + FixedOutputReset,
83{
84 pub fn new() -> Self {
105 Self {
106 digest: D::new(),
107 mgf_digest: D::new(),
108 label: None,
109 }
110 }
111
112 pub fn new_with_label<S: Into<Box<[u8]>>>(label: S) -> Self {
114 Self {
115 digest: D::new(),
116 mgf_digest: D::new(),
117 label: Some(label.into()),
118 }
119 }
120}
121
122#[cfg(feature = "alloc")]
123impl<D, MGD> Oaep<D, MGD>
124where
125 D: Digest + FixedOutputReset,
126 MGD: Digest + FixedOutputReset,
127{
128 pub fn new_with_mgf_hash() -> Self {
150 Self {
151 digest: D::new(),
152 mgf_digest: MGD::new(),
153 label: None,
154 }
155 }
156
157 pub fn new_with_mgf_hash_and_label<S: Into<Box<[u8]>>>(label: S) -> Self {
159 Self {
160 digest: D::new(),
161 mgf_digest: MGD::new(),
162 label: Some(label.into()),
163 }
164 }
165}
166
167#[cfg(feature = "alloc")]
168impl<D, MGD> PaddingScheme for Oaep<D, MGD>
169where
170 D: Digest + FixedOutputReset,
171 MGD: Digest + FixedOutputReset,
172{
173 #[cfg(feature = "alloc")]
174 fn decrypt<Rng: TryCryptoRng + ?Sized>(
175 mut self,
176 rng: Option<&mut Rng>,
177 priv_key: &RsaPrivateKey,
178 ciphertext: &[u8],
179 ) -> Result<Vec<u8>> {
180 decrypt(
181 rng,
182 priv_key,
183 ciphertext,
184 &mut self.digest,
185 &mut self.mgf_digest,
186 self.label,
187 )
188 }
189
190 fn encrypt<Rng, K, T>(mut self, rng: &mut Rng, pub_key: &K, msg: &[u8]) -> Result<Vec<u8>>
191 where
192 Rng: TryCryptoRng + ?Sized,
193 T: UnsignedModularInt,
194 K: PublicKeyParts<T>,
195 K::MontyParams: crate::traits::modular::CtModulusParams,
196 {
197 let em = oaep_encrypt(
198 rng,
199 msg,
200 &mut self.digest,
201 &mut self.mgf_digest,
202 self.label,
203 pub_key.size(),
204 )?;
205 let int = T::try_from_be_bytes_vartime(&em)?;
206 let mut storage = vec![0u8; pub_key.size()];
207 let ciphertext =
208 uint_to_be_pad_into(rsa_encrypt(pub_key, &int)?, pub_key.size(), &mut storage)?;
209 Ok(ciphertext.to_vec())
210 }
211}
212
213#[cfg(feature = "alloc")]
214impl<D, MGD> fmt::Debug for Oaep<D, MGD> {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 f.debug_struct("OAEP")
217 .field("digest", &"...")
218 .field("mgf_digest", &"...")
219 .field("label", &self.label)
220 .finish()
221 }
222}
223
224#[cfg(feature = "alloc")]
232#[inline]
233#[allow(dead_code)]
234fn encrypt<R, D, MGD>(
235 rng: &mut R,
236 pub_key: &RsaPublicKey,
237 msg: &[u8],
238 digest: &mut D,
239 mgf_digest: &mut MGD,
240 label: Option<Box<[u8]>>,
241) -> Result<Vec<u8>>
242where
243 R: TryCryptoRng + ?Sized,
244 D: Digest + FixedOutputReset,
245 MGD: Digest + FixedOutputReset,
246{
247 key::check_public(pub_key)?;
248
249 let em = oaep_encrypt(rng, msg, digest, mgf_digest, label, pub_key.size())?;
250
251 let int = BoxedUint::from_be_slice(&em, pub_key.n_bits_precision())?;
252 uint_to_be_pad(rsa_encrypt(pub_key, &int)?, pub_key.size())
253}
254
255#[cfg(feature = "alloc")]
263#[allow(dead_code)]
264fn encrypt_digest<R, D, MGD>(
265 rng: &mut R,
266 pub_key: &RsaPublicKey,
267 msg: &[u8],
268 label: Option<Box<[u8]>>,
269) -> Result<Vec<u8>>
270where
271 R: TryCryptoRng + ?Sized,
272 D: Digest,
273 MGD: Digest + FixedOutputReset,
274{
275 key::check_public(pub_key)?;
276
277 let em = oaep_encrypt_digest::<_, D, MGD>(rng, msg, label, pub_key.size())?;
278
279 let int = BoxedUint::from_be_slice(&em, pub_key.n_bits_precision())?;
280 uint_to_be_pad(rsa_encrypt(pub_key, &int)?, pub_key.size())
281}
282
283pub fn encrypt_digest_into<'a, R, D, MGD, K, T>(
285 rng: &mut R,
286 pub_key: &K,
287 msg: &[u8],
288 label: Option<&[u8]>,
289 storage: &'a mut [u8],
290) -> crate::Result<&'a [u8]>
291where
292 R: rand_core::TryCryptoRng + ?Sized,
293 D: digest::Digest,
294 MGD: digest::Digest + digest::FixedOutputReset,
295 K: crate::traits::PublicKeyParts<T>,
296 K::MontyParams: crate::traits::modular::CtModulusParams,
297 T: crate::traits::UnsignedModularInt,
298{
299 let padded_len = pub_key.size();
300 let em = crate::algorithms::oaep::oaep_encrypt_digest_into::<_, D, MGD>(
301 rng, msg, label, padded_len, storage,
302 )?;
303 let int = T::try_from_be_bytes_vartime(em)?;
304 crate::algorithms::pad::uint_to_be_pad_into(
305 crate::algorithms::rsa::rsa_encrypt(pub_key, &int)?,
306 padded_len,
307 storage,
308 )
309}
310
311#[cfg(feature = "alloc")]
324#[inline]
325fn decrypt<R, D, MGD>(
326 rng: Option<&mut R>,
327 priv_key: &RsaPrivateKey,
328 ciphertext: &[u8],
329 digest: &mut D,
330 mgf_digest: &mut MGD,
331 label: Option<Box<[u8]>>,
332) -> Result<Vec<u8>>
333where
334 R: TryCryptoRng + ?Sized,
335 D: Digest + FixedOutputReset,
336 MGD: Digest + FixedOutputReset,
337{
338 if ciphertext.len() != priv_key.size() {
339 return Err(Error::Decryption);
340 }
341
342 let ciphertext = BoxedUint::from_be_slice(ciphertext, priv_key.n_bits_precision())?;
343
344 let em = rsa_decrypt_and_check(priv_key, rng, &ciphertext)?;
345 let mut em = uint_to_zeroizing_be_pad(em, priv_key.size())?;
346
347 oaep_decrypt(&mut em, digest, mgf_digest, label, priv_key.size())
348}
349
350#[cfg(feature = "alloc")]
363#[inline]
364fn decrypt_digest<R, D, MGD>(
365 rng: Option<&mut R>,
366 priv_key: &RsaPrivateKey,
367 ciphertext: &[u8],
368 label: Option<Box<[u8]>>,
369) -> Result<Vec<u8>>
370where
371 R: TryCryptoRng + ?Sized,
372 D: Digest,
373 MGD: Digest + FixedOutputReset,
374{
375 key::check_public(priv_key)?;
376
377 if ciphertext.len() != priv_key.size() {
378 return Err(Error::Decryption);
379 }
380
381 let ciphertext = BoxedUint::from_be_slice(ciphertext, priv_key.n_bits_precision())?;
382 let em = rsa_decrypt_and_check(priv_key, rng, &ciphertext)?;
383 let mut em = uint_to_zeroizing_be_pad(em, priv_key.size())?;
384
385 oaep_decrypt_digest::<D, MGD>(&mut em, label, priv_key.size())
386}
387
388#[cfg(test)]
389#[cfg(feature = "alloc")]
390mod tests {
391 use crate::key::{RsaPrivateKey, RsaPublicKey};
392 use crate::oaep::{DecryptingKey, EncryptingKey, Oaep};
393 use crate::traits::PublicKeyParts;
394 use crate::traits::{Decryptor, RandomizedDecryptor, RandomizedEncryptor};
395
396 use crypto_bigint::BoxedUint;
397 use digest::{Digest, FixedOutputReset};
398 use rand::rngs::ChaCha8Rng;
399 use rand_core::{Rng, SeedableRng};
400 use sha1::Sha1;
401 use sha2::{Sha224, Sha256, Sha384, Sha512};
402 use sha3::{Sha3_256, Sha3_384, Sha3_512};
403
404 fn get_private_key() -> RsaPrivateKey {
405 RsaPrivateKey::from_components(
434 BoxedUint::from_be_hex("d397b84d98a4c26138ed1b695a8106ead91d553bf06041b62d3fdc50a041e222b8f4529689c1b82c5e71554f5dd69fa2f4b6158cf0dbeb57811a0fc327e1f28e74fe74d3bc166c1eabdc1b8b57b934ca8be5b00b4f29975bcc99acaf415b59bb28a6782bb41a2c3c2976b3c18dbadef62f00c6bb226640095096c0cc60d22fe7ef987d75c6a81b10d96bf292028af110dc7cc1bbc43d22adab379a0cd5d8078cc780ff5cd6209dea34c922cf784f7717e428d75b5aec8ff30e5f0141510766e2e0ab8d473c84e8710b2b98227c3db095337ad3452f19e2b9bfbccdd8148abf6776fa552775e6e75956e45229ae5a9c46949bab1e622f0e48f56524a84ed3483b", 2048).unwrap(),
435 BoxedUint::from(65_537u64),
436 BoxedUint::from_be_hex("c4e70c689162c94c660828191b52b4d8392115df486a9adbe831e458d73958320dc1b755456e93701e9702d76fb0b92f90e01d1fe248153281fe79aa9763a92fae69d8d7ecd144de29fa135bd14f9573e349e45031e3b76982f583003826c552e89a397c1a06bd2163488630d92e8c2bb643d7abef700da95d685c941489a46f54b5316f62b5d2c3a7f1bbd134cb37353a44683fdc9d95d36458de22f6c44057fe74a0a436c4308f73f4da42f35c47ac16a7138d483afc91e41dc3a1127382e0c0f5119b0221b4fc639d6b9c38177a6de9b526ebd88c38d7982c07f98a0efd877d508aae275b946915c02e2e1106d175d74ec6777f5e80d12c053d9c7be1e341", 2048).unwrap(),
437 vec![
438 BoxedUint::from_be_hex("f827bbf3a41877c7cc59aebf42ed4b29c32defcb8ed96863d5b090a05a8930dd624a21c9dcf9838568fdfa0df65b8462a5f2ac913d6c56f975532bd8e78fb07bd405ca99a484bcf59f019bbddcb3933f2bce706300b4f7b110120c5df9018159067c35da3061a56c8635a52b54273b31271b4311f0795df6021e6355e1a42e61", 1024).unwrap(),
439 BoxedUint::from_be_hex("da4817ce0089dd36f2ade6a3ff410c73ec34bf1b4f6bda38431bfede11cef1f7f6efa70e5f8063a3b1f6e17296ffb15feefa0912a0325b8d1fd65a559e717b5b961ec345072e0ec5203d03441d29af4d64054a04507410cf1da78e7b6119d909ec66e6ad625bf995b279a4b3c5be7d895cd7c5b9c4c497fde730916fcdb4e41b", 1024).unwrap()
440 ],
441 ).unwrap()
442 }
443
444 #[test]
445 fn test_encrypt_decrypt_oaep() {
446 let priv_key = get_private_key();
447 do_test_encrypt_decrypt_oaep::<Sha1>(&priv_key);
448 do_test_encrypt_decrypt_oaep::<Sha224>(&priv_key);
449 do_test_encrypt_decrypt_oaep::<Sha256>(&priv_key);
450 do_test_encrypt_decrypt_oaep::<Sha384>(&priv_key);
451 do_test_encrypt_decrypt_oaep::<Sha512>(&priv_key);
452 do_test_encrypt_decrypt_oaep::<Sha3_256>(&priv_key);
453 do_test_encrypt_decrypt_oaep::<Sha3_384>(&priv_key);
454 do_test_encrypt_decrypt_oaep::<Sha3_512>(&priv_key);
455
456 do_test_oaep_with_different_hashes::<Sha1, Sha1>(&priv_key);
457 do_test_oaep_with_different_hashes::<Sha224, Sha1>(&priv_key);
458 do_test_oaep_with_different_hashes::<Sha256, Sha1>(&priv_key);
459 do_test_oaep_with_different_hashes::<Sha384, Sha1>(&priv_key);
460 do_test_oaep_with_different_hashes::<Sha512, Sha1>(&priv_key);
461 do_test_oaep_with_different_hashes::<Sha3_256, Sha1>(&priv_key);
462 do_test_oaep_with_different_hashes::<Sha3_384, Sha1>(&priv_key);
463 do_test_oaep_with_different_hashes::<Sha3_512, Sha1>(&priv_key);
464 }
465
466 fn get_label(rng: &mut ChaCha8Rng) -> Option<Box<[u8]>> {
467 let mut buf = [0u8; 32];
468 rng.fill_bytes(&mut buf);
469
470 if rng.next_u32() % 2 == 0 {
471 Some(buf.into())
472 } else {
473 None
474 }
475 }
476
477 fn do_test_encrypt_decrypt_oaep<D: Digest + FixedOutputReset>(prk: &RsaPrivateKey) {
478 let mut rng = ChaCha8Rng::from_seed([42; 32]);
479
480 let k = prk.size();
481
482 for i in 1..8 {
483 let mut input = vec![0u8; i * 8];
484 rng.fill_bytes(&mut input);
485
486 if input.len() > k - 11 {
487 input = input[0..k - 11].to_vec();
488 }
489 let label = get_label(&mut rng);
490
491 let pub_key: RsaPublicKey = prk.into();
492
493 let ciphertext = if let Some(ref label) = label {
494 let padding = Oaep::<D>::new_with_label(label.clone());
495 pub_key.encrypt(&mut rng, padding, &input).unwrap()
496 } else {
497 let padding = Oaep::<D>::new();
498 pub_key.encrypt(&mut rng, padding, &input).unwrap()
499 };
500
501 assert_ne!(input, ciphertext);
502 let blind: bool = rng.next_u32() < (1 << 31);
503
504 let padding = if let Some(label) = label {
505 Oaep::<D>::new_with_label::<Box<[u8]>>(label)
506 } else {
507 Oaep::<D>::new()
508 };
509
510 let plaintext = if blind {
511 prk.decrypt(padding, &ciphertext).unwrap()
512 } else {
513 prk.decrypt_blinded(&mut rng, padding, &ciphertext).unwrap()
514 };
515
516 assert_eq!(input, plaintext);
517 }
518 }
519
520 fn do_test_oaep_with_different_hashes<
521 D: Digest + FixedOutputReset,
522 U: Digest + FixedOutputReset,
523 >(
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 padding = Oaep::<D, U>::new_with_mgf_hash_and_label::<_>(label.clone());
543 pub_key.encrypt(&mut rng, padding, &input).unwrap()
544 } else {
545 let padding = Oaep::<D, U>::new_with_mgf_hash();
546 pub_key.encrypt(&mut rng, padding, &input).unwrap()
547 };
548
549 assert_ne!(input, ciphertext);
550 let blind: bool = rng.next_u32() < (1 << 31);
551
552 let padding = if let Some(label) = label {
553 Oaep::<D, U>::new_with_mgf_hash_and_label::<_>(label)
554 } else {
555 Oaep::<D, U>::new_with_mgf_hash()
556 };
557
558 let plaintext = if blind {
559 prk.decrypt(padding, &ciphertext).unwrap()
560 } else {
561 prk.decrypt_blinded(&mut rng, padding, &ciphertext).unwrap()
562 };
563
564 assert_eq!(input, plaintext);
565 }
566 }
567
568 #[test]
569 fn test_decrypt_oaep_invalid_hash() {
570 let mut rng = ChaCha8Rng::from_seed([42; 32]);
571 let priv_key = get_private_key();
572 let pub_key: RsaPublicKey = (&priv_key).into();
573 let ciphertext = pub_key
574 .encrypt(&mut rng, Oaep::<Sha1>::new(), "a_plain_text".as_bytes())
575 .unwrap();
576 assert!(
577 priv_key
578 .decrypt_blinded(
579 &mut rng,
580 Oaep::<Sha1>::new_with_label::<_>("label".as_bytes()),
581 &ciphertext,
582 )
583 .is_err(),
584 "decrypt should have failed on hash verification"
585 );
586 }
587
588 #[test]
589 fn test_encrypt_decrypt_oaep_traits() {
590 let priv_key = get_private_key();
591 do_test_encrypt_decrypt_oaep_traits::<Sha1>(&priv_key);
592 do_test_encrypt_decrypt_oaep_traits::<Sha224>(&priv_key);
593 do_test_encrypt_decrypt_oaep_traits::<Sha256>(&priv_key);
594 do_test_encrypt_decrypt_oaep_traits::<Sha384>(&priv_key);
595 do_test_encrypt_decrypt_oaep_traits::<Sha512>(&priv_key);
596 do_test_encrypt_decrypt_oaep_traits::<Sha3_256>(&priv_key);
597 do_test_encrypt_decrypt_oaep_traits::<Sha3_384>(&priv_key);
598 do_test_encrypt_decrypt_oaep_traits::<Sha3_512>(&priv_key);
599
600 do_test_oaep_with_different_hashes_traits::<Sha1, Sha1>(&priv_key);
601 do_test_oaep_with_different_hashes_traits::<Sha224, Sha1>(&priv_key);
602 do_test_oaep_with_different_hashes_traits::<Sha256, Sha1>(&priv_key);
603 do_test_oaep_with_different_hashes_traits::<Sha384, Sha1>(&priv_key);
604 do_test_oaep_with_different_hashes_traits::<Sha512, Sha1>(&priv_key);
605 do_test_oaep_with_different_hashes_traits::<Sha3_256, Sha1>(&priv_key);
606 do_test_oaep_with_different_hashes_traits::<Sha3_384, Sha1>(&priv_key);
607 do_test_oaep_with_different_hashes_traits::<Sha3_512, Sha1>(&priv_key);
608 }
609
610 fn do_test_encrypt_decrypt_oaep_traits<D: Digest + FixedOutputReset>(prk: &RsaPrivateKey) {
611 do_test_oaep_with_different_hashes_traits::<D, D>(prk);
612 }
613
614 fn do_test_oaep_with_different_hashes_traits<D: Digest, MGD: Digest + FixedOutputReset>(
615 prk: &RsaPrivateKey,
616 ) {
617 let mut rng = ChaCha8Rng::from_seed([42; 32]);
618
619 let k = prk.size();
620
621 for i in 1..8 {
622 let mut input = vec![0u8; i * 8];
623 rng.fill_bytes(&mut input);
624
625 if input.len() > k - 11 {
626 input = input[0..k - 11].to_vec();
627 }
628 let label = get_label(&mut rng);
629
630 let pub_key: RsaPublicKey = prk.into();
631
632 let ciphertext = if let Some(ref label) = label {
633 let encrypting_key =
634 EncryptingKey::<D, MGD>::new_with_label(pub_key, label.clone());
635 encrypting_key.encrypt_with_rng(&mut rng, &input).unwrap()
636 } else {
637 let encrypting_key = EncryptingKey::<D, MGD>::new(pub_key);
638 encrypting_key.encrypt_with_rng(&mut rng, &input).unwrap()
639 };
640
641 assert_ne!(input, ciphertext);
642 let blind: bool = rng.next_u32() < (1 << 31);
643
644 let decrypting_key = if let Some(ref label) = label {
645 DecryptingKey::<D, MGD>::new_with_label(prk.clone(), label.clone())
646 } else {
647 DecryptingKey::<D, MGD>::new(prk.clone())
648 };
649
650 let plaintext = if blind {
651 decrypting_key.decrypt(&ciphertext).unwrap()
652 } else {
653 decrypting_key
654 .decrypt_with_rng(&mut rng, &ciphertext)
655 .unwrap()
656 };
657
658 assert_eq!(input, plaintext);
659 }
660 }
661
662 #[test]
663 fn test_decrypt_oaep_invalid_hash_traits() {
664 let mut rng = ChaCha8Rng::from_seed([42; 32]);
665 let priv_key = get_private_key();
666 let pub_key: RsaPublicKey = (&priv_key).into();
667 let encrypting_key = EncryptingKey::<Sha1>::new(pub_key);
668 let decrypting_key = DecryptingKey::<Sha1>::new_with_label(priv_key, "label".as_bytes());
669 let ciphertext = encrypting_key
670 .encrypt_with_rng(&mut rng, "a_plain_text".as_bytes())
671 .unwrap();
672 assert!(
673 decrypting_key
674 .decrypt_with_rng(&mut rng, &ciphertext)
675 .is_err(),
676 "decrypt should have failed on hash verification"
677 );
678 }
679}