1mod 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
29pub struct Oaep {
41 pub digest: Box<dyn DynDigest + Send + Sync>,
43
44 pub mgf_digest: Box<dyn DynDigest + Send + Sync>,
46
47 pub label: Option<String>,
49}
50
51impl Oaep {
52 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 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 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 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#[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
201fn 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#[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#[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 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}