1use std::collections::{HashMap, HashSet};
4use std::io::Cursor;
5
6use anyhow::{Context as _, Result, ensure};
7use deltachat_contact_tools::{EmailAddress, may_be_valid_addr};
8use pgp::composed::{
9 ArmorOptions, Deserializable, DetachedSignature, EncryptionCaps, KeyType as PgpKeyType,
10 MessageBuilder, SecretKeyParamsBuilder, SignedKeyDetails, SignedPublicKey, SignedPublicSubKey,
11 SignedSecretKey, SubkeyParamsBuilder, SubpacketConfig,
12};
13use pgp::crypto::aead::{AeadAlgorithm, ChunkSize};
14use pgp::crypto::ecc_curve::ECCCurve;
15use pgp::crypto::hash::HashAlgorithm;
16use pgp::crypto::sym::SymmetricKeyAlgorithm;
17use pgp::packet::{Signature, SignatureConfig, SignatureType, Subpacket, SubpacketData};
18use pgp::types::{
19 CompressionAlgorithm, Imprint, KeyDetails, KeyVersion, Password, SignedUser, SigningKey as _,
20 StringToKey,
21};
22use rand_old::{Rng as _, thread_rng};
23use sha2::Sha256;
24use tokio::runtime::Handle;
25
26use crate::key::{DcKey, Fingerprint};
27
28const SYMMETRIC_KEY_ALGORITHM: SymmetricKeyAlgorithm = SymmetricKeyAlgorithm::AES128;
30
31pub(crate) fn create_keypair(addr: EmailAddress) -> Result<SignedSecretKey> {
36 let signing_key_type = PgpKeyType::Ed25519Legacy;
37 let encryption_key_type = PgpKeyType::ECDH(ECCCurve::Curve25519);
38
39 let user_id = format!("<{addr}>");
40 let key_params = SecretKeyParamsBuilder::default()
41 .key_type(signing_key_type)
42 .can_certify(true)
43 .can_sign(true)
44 .feature_seipd_v2(true)
45 .primary_user_id(user_id)
46 .passphrase(None)
47 .preferred_symmetric_algorithms(smallvec![
48 SymmetricKeyAlgorithm::AES256,
49 SymmetricKeyAlgorithm::AES192,
50 SymmetricKeyAlgorithm::AES128,
51 ])
52 .preferred_hash_algorithms(smallvec![
53 HashAlgorithm::Sha256,
54 HashAlgorithm::Sha384,
55 HashAlgorithm::Sha512,
56 HashAlgorithm::Sha224,
57 ])
58 .preferred_compression_algorithms(smallvec![
59 CompressionAlgorithm::ZLIB,
60 CompressionAlgorithm::ZIP,
61 ])
62 .subkey(
63 SubkeyParamsBuilder::default()
64 .key_type(encryption_key_type)
65 .can_encrypt(EncryptionCaps::All)
66 .passphrase(None)
67 .build()
68 .context("failed to build subkey parameters")?,
69 )
70 .build()
71 .context("failed to build key parameters")?;
72
73 let mut rng = thread_rng();
74 let secret_key = key_params
75 .generate(&mut rng)
76 .context("Failed to generate the key")?;
77 secret_key
78 .verify_bindings()
79 .context("Invalid secret key generated")?;
80
81 Ok(secret_key)
82}
83
84fn select_pk_for_encryption(key: &SignedPublicKey) -> Option<&SignedPublicSubKey> {
90 key.public_subkeys
91 .iter()
92 .find(|subkey| subkey.algorithm().can_encrypt())
93}
94
95#[derive(Debug)]
101pub enum SeipdVersion {
102 V1,
104
105 V2,
107}
108
109#[expect(clippy::arithmetic_side_effects)]
112pub async fn pk_encrypt(
113 plain: Vec<u8>,
114 public_keys_for_encryption: Vec<SignedPublicKey>,
115 private_key_for_signing: SignedSecretKey,
116 compress: bool,
117 seipd_version: SeipdVersion,
118) -> Result<String> {
119 Handle::current()
120 .spawn_blocking(move || {
121 let mut rng = thread_rng();
122
123 let pkeys = public_keys_for_encryption
124 .iter()
125 .filter_map(select_pk_for_encryption);
126 let subpkts = {
127 let mut hashed = Vec::with_capacity(1 + public_keys_for_encryption.len() + 1);
128 hashed.push(Subpacket::critical(SubpacketData::SignatureCreationTime(
129 pgp::types::Timestamp::now(),
130 ))?);
131 let skip = private_key_for_signing.dc_fingerprint().hex()
133 == "B86586B6DEF437D674BFAFC02A6B2EBC633B9E82";
134 for key in &public_keys_for_encryption {
135 if skip {
136 break;
137 }
138 let data = SubpacketData::IntendedRecipientFingerprint(key.fingerprint());
139 let subpkt = match private_key_for_signing.version() < KeyVersion::V6 {
140 true => Subpacket::regular(data)?,
141 false => Subpacket::critical(data)?,
142 };
143 hashed.push(subpkt);
144 }
145 hashed.push(Subpacket::regular(SubpacketData::IssuerFingerprint(
146 private_key_for_signing.fingerprint(),
147 ))?);
148 let mut unhashed = vec![];
149 if private_key_for_signing.version() <= KeyVersion::V4 {
150 unhashed.push(Subpacket::regular(SubpacketData::IssuerKeyId(
151 private_key_for_signing.legacy_key_id(),
152 ))?);
153 }
154 SubpacketConfig::UserDefined { hashed, unhashed }
155 };
156
157 let msg = MessageBuilder::from_bytes("", plain);
158 let encoded_msg = match seipd_version {
159 SeipdVersion::V1 => {
160 let mut msg = msg.seipd_v1(&mut rng, SYMMETRIC_KEY_ALGORITHM);
161
162 for pkey in pkeys {
163 msg.encrypt_to_key_anonymous(&mut rng, &pkey)?;
164 }
165
166 let hash_algorithm = private_key_for_signing.hash_alg();
167 msg.sign_with_subpackets(
168 &*private_key_for_signing,
169 Password::empty(),
170 hash_algorithm,
171 subpkts,
172 );
173 if compress {
174 msg.compression(CompressionAlgorithm::ZLIB);
175 }
176
177 msg.to_armored_string(&mut rng, Default::default())?
178 }
179 SeipdVersion::V2 => {
180 let mut msg = msg.seipd_v2(
181 &mut rng,
182 SYMMETRIC_KEY_ALGORITHM,
183 AeadAlgorithm::Ocb,
184 ChunkSize::C8KiB,
185 );
186
187 for pkey in pkeys {
188 msg.encrypt_to_key_anonymous(&mut rng, &pkey)?;
189 }
190
191 let hash_algorithm = private_key_for_signing.hash_alg();
192 msg.sign_with_subpackets(
193 &*private_key_for_signing,
194 Password::empty(),
195 hash_algorithm,
196 subpkts,
197 );
198 if compress {
199 msg.compression(CompressionAlgorithm::ZLIB);
200 }
201
202 msg.to_armored_string(&mut rng, Default::default())?
203 }
204 };
205
206 Ok(encoded_msg)
207 })
208 .await?
209}
210
211pub fn pk_calc_signature(
213 plain: Vec<u8>,
214 private_key_for_signing: &SignedSecretKey,
215) -> Result<String> {
216 let rng = thread_rng();
217
218 let mut config = SignatureConfig::from_key(
219 rng,
220 &private_key_for_signing.primary_key,
221 SignatureType::Binary,
222 )?;
223
224 config.hashed_subpackets = vec![
225 Subpacket::regular(SubpacketData::IssuerFingerprint(
226 private_key_for_signing.fingerprint(),
227 ))?,
228 Subpacket::critical(SubpacketData::SignatureCreationTime(
229 pgp::types::Timestamp::now(),
230 ))?,
231 ];
232 config.unhashed_subpackets = vec![];
233 if private_key_for_signing.version() <= KeyVersion::V4 {
234 config
235 .unhashed_subpackets
236 .push(Subpacket::regular(SubpacketData::IssuerKeyId(
237 private_key_for_signing.legacy_key_id(),
238 ))?);
239 }
240
241 let signature = config.sign(
242 &private_key_for_signing.primary_key,
243 &Password::empty(),
244 plain.as_slice(),
245 )?;
246
247 let sig = DetachedSignature::new(signature);
248
249 Ok(sig.to_armored_string(ArmorOptions::default())?)
250}
251
252pub fn valid_signature_fingerprints(
259 msg: &pgp::composed::Message,
260 public_keys_for_validation: &[SignedPublicKey],
261) -> HashMap<Fingerprint, Vec<Fingerprint>> {
262 let mut ret_signature_fingerprints = HashMap::new();
263 if msg.is_signed() {
264 for pkey in public_keys_for_validation {
265 if let Ok(signature) = msg.verify(&pkey.primary_key) {
266 let fp = pkey.dc_fingerprint();
267 let mut recipient_fps = Vec::new();
268 if let Some(cfg) = signature.config() {
269 for subpkt in &cfg.hashed_subpackets {
270 if let SubpacketData::IntendedRecipientFingerprint(fp) = &subpkt.data {
271 recipient_fps.push(fp.clone().into());
272 }
273 }
274 }
275 ret_signature_fingerprints.insert(fp, recipient_fps);
276 }
277 }
278 }
279 ret_signature_fingerprints
280}
281
282pub fn pk_validate(
284 content: &[u8],
285 signature: &[u8],
286 public_keys_for_validation: &[SignedPublicKey],
287) -> Result<HashSet<Fingerprint>> {
288 let mut ret: HashSet<Fingerprint> = Default::default();
289
290 let detached_signature = DetachedSignature::from_armor_single(Cursor::new(signature))?.0;
291
292 for pkey in public_keys_for_validation {
293 if detached_signature.verify(pkey, content).is_ok() {
294 let fp = pkey.dc_fingerprint();
295 ret.insert(fp);
296 }
297 }
298 Ok(ret)
299}
300
301pub async fn symm_encrypt_message(
305 plain: Vec<u8>,
306 private_key_for_signing: Option<SignedSecretKey>,
307 shared_secret: &str,
308 compress: bool,
309) -> Result<String> {
310 let shared_secret = Password::from(shared_secret.to_string());
311
312 tokio::task::spawn_blocking(move || {
313 let msg = MessageBuilder::from_bytes("", plain);
314 let mut rng = thread_rng();
315 let mut salt = [0u8; 8];
316 rng.fill(&mut salt[..]);
317 let s2k = StringToKey::Salted {
318 hash_alg: HashAlgorithm::default(),
319 salt,
320 };
321 let mut msg = msg.seipd_v2(
322 &mut rng,
323 SYMMETRIC_KEY_ALGORITHM,
324 AeadAlgorithm::Ocb,
325 ChunkSize::C8KiB,
326 );
327 msg.encrypt_with_password(&mut rng, s2k, &shared_secret)?;
328
329 if let Some(private_key_for_signing) = private_key_for_signing.as_deref() {
330 let hash_algorithm = private_key_for_signing.hash_alg();
331 msg.sign(private_key_for_signing, Password::empty(), hash_algorithm);
332 }
333 if compress {
334 msg.compression(CompressionAlgorithm::ZLIB);
335 }
336
337 let encoded_msg = msg.to_armored_string(&mut rng, Default::default())?;
338
339 Ok(encoded_msg)
340 })
341 .await?
342}
343
344pub fn merge_openpgp_certificates(
361 old_certificate: SignedPublicKey,
362 new_certificate: SignedPublicKey,
363) -> Result<SignedPublicKey> {
364 old_certificate
365 .verify_bindings()
366 .context("First key cannot be verified")?;
367 new_certificate
368 .verify_bindings()
369 .context("Second key cannot be verified")?;
370
371 let SignedPublicKey {
373 primary_key: old_primary_key,
374 details: old_details,
375 public_subkeys: old_public_subkeys,
376 } = old_certificate;
377 let SignedPublicKey {
378 primary_key: new_primary_key,
379 details: new_details,
380 public_subkeys: _new_public_subkeys,
381 } = new_certificate;
382
383 let old_imprint = old_primary_key.imprint::<Sha256>()?;
390 let new_imprint = new_primary_key.imprint::<Sha256>()?;
391 ensure!(
392 old_imprint == new_imprint,
393 "Cannot merge certificates with different primary keys {} and {}",
394 old_primary_key.fingerprint(),
395 new_primary_key.fingerprint()
396 );
397
398 let SignedKeyDetails {
407 revocation_signatures: _old_revocation_signatures,
408 direct_signatures: old_direct_signatures,
409 users: old_users,
410 user_attributes: _old_user_attributes,
411 } = old_details;
412 let SignedKeyDetails {
413 revocation_signatures: _new_revocation_signatures,
414 direct_signatures: new_direct_signatures,
415 users: new_users,
416 user_attributes: _new_user_attributes,
417 } = new_details;
418
419 let best_direct_key_signature: Option<Signature> = old_direct_signatures
421 .into_iter()
422 .chain(new_direct_signatures)
423 .filter(|x: &Signature| x.verify_key(&old_primary_key).is_ok())
424 .max_by_key(|x: &Signature|
425 x.created().map_or(0, |ts| ts.as_secs()));
428 let direct_signatures: Vec<Signature> = best_direct_key_signature.into_iter().collect();
429
430 let best_user: Option<SignedUser> = old_users
437 .into_iter()
438 .chain(new_users.clone())
439 .filter_map(|SignedUser { id, signatures }| {
440 let best_user_signature: Option<Signature> = signatures
443 .into_iter()
444 .filter(|signature: &Signature| {
445 signature
446 .verify_certification(&old_primary_key, pgp::types::Tag::UserId, &id)
447 .is_ok()
448 })
449 .max_by_key(|signature: &Signature| {
450 signature.created().map_or(0, |ts| ts.as_secs())
451 });
452 best_user_signature.map(|signature| (id, signature))
453 })
454 .max_by_key(|(_id, signature)| signature.created().map_or(0, |ts| ts.as_secs()))
455 .map(|(id, signature)| SignedUser {
456 id,
457 signatures: vec![signature],
458 });
459 let users: Vec<SignedUser> = best_user.into_iter().collect();
460
461 let public_subkeys = old_public_subkeys;
462
463 Ok(SignedPublicKey {
464 primary_key: old_primary_key,
465 details: SignedKeyDetails {
466 revocation_signatures: vec![],
467 direct_signatures,
468 users,
469 user_attributes: vec![],
470 },
471 public_subkeys,
472 })
473}
474
475pub(crate) fn addresses_from_public_key(public_key: &SignedPublicKey) -> Option<Vec<String>> {
479 for signature in &public_key.details.direct_signatures {
480 let signature_is_valid = signature.verify_key(&public_key.primary_key).is_ok();
483 debug_assert!(signature_is_valid);
484 if signature_is_valid {
485 for notation in signature.notations() {
486 if notation.name == "relays@chatmail.at"
487 && let Ok(value) = str::from_utf8(¬ation.value)
488 {
489 return Some(
490 value
491 .split(",")
492 .map(|s| s.to_string())
493 .filter(|s| may_be_valid_addr(s))
494 .take(3)
495 .collect(),
496 );
497 }
498 }
499 }
500 }
501 None
502}
503
504#[cfg(test)]
505mod tests {
506 use std::sync::LazyLock;
507 use tokio::sync::OnceCell;
508
509 use super::*;
510 use crate::{
511 config::Config,
512 decrypt,
513 key::{load_self_public_key, self_fingerprint, store_self_keypair},
514 mimefactory::{render_outer_message, wrap_encrypted_part},
515 test_utils::{TestContext, TestContextManager, alice_keypair, bob_keypair},
516 token,
517 };
518 use pgp::composed::{Esk, Message};
519 use pgp::packet::PublicKeyEncryptedSessionKey;
520
521 async fn decrypt_bytes(
522 bytes: Vec<u8>,
523 private_keys_for_decryption: &[SignedSecretKey],
524 auth_tokens_for_decryption: &[String],
525 ) -> Result<pgp::composed::Message<'static>> {
526 let t = &TestContext::new().await;
527 t.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
528 .await
529 .expect("Failed to configure address");
530
531 for secret in auth_tokens_for_decryption {
532 token::save(t, token::Namespace::Auth, None, secret, 0).await?;
533 }
534 let [secret_key] = private_keys_for_decryption else {
535 panic!("Only one private key is allowed anymore");
536 };
537 store_self_keypair(t, secret_key).await?;
538
539 let mime_message = wrap_encrypted_part(bytes.try_into().unwrap());
540 let rendered = render_outer_message(vec![], mime_message);
541 let parsed = mailparse::parse_mail(rendered.as_bytes())?;
542 let (decrypted, _fp) = decrypt::decrypt(t, &parsed).await?.unwrap();
543 Ok(decrypted)
544 }
545
546 async fn pk_decrypt_and_validate<'a>(
547 ctext: &'a [u8],
548 private_keys_for_decryption: &'a [SignedSecretKey],
549 public_keys_for_validation: &[SignedPublicKey],
550 ) -> Result<(
551 pgp::composed::Message<'static>,
552 HashMap<Fingerprint, Vec<Fingerprint>>,
553 Vec<u8>,
554 )> {
555 let mut msg = decrypt_bytes(ctext.to_vec(), private_keys_for_decryption, &[]).await?;
556 let content = msg.as_data_vec()?;
557 let ret_signature_fingerprints =
558 valid_signature_fingerprints(&msg, public_keys_for_validation);
559
560 Ok((msg, ret_signature_fingerprints, content))
561 }
562
563 #[test]
564 fn test_create_keypair() {
565 let keypair0 = create_keypair(EmailAddress::new("foo@bar.de").unwrap()).unwrap();
566 let keypair1 = create_keypair(EmailAddress::new("two@zwo.de").unwrap()).unwrap();
567 assert_ne!(keypair0.public_key(), keypair1.public_key());
568 }
569
570 struct TestKeys {
573 alice_secret: SignedSecretKey,
574 alice_public: SignedPublicKey,
575 bob_secret: SignedSecretKey,
576 bob_public: SignedPublicKey,
577 }
578
579 impl TestKeys {
580 fn new() -> TestKeys {
581 let alice = alice_keypair();
582 let bob = bob_keypair();
583 TestKeys {
584 alice_secret: alice.clone(),
585 alice_public: alice.to_public_key(),
586 bob_secret: bob.clone(),
587 bob_public: bob.to_public_key(),
588 }
589 }
590 }
591
592 static CLEARTEXT: &[u8] = b"This is a test";
594
595 static KEYS: LazyLock<TestKeys> = LazyLock::new(TestKeys::new);
597
598 static CTEXT_SIGNED: OnceCell<String> = OnceCell::const_new();
599
600 async fn ctext_signed() -> &'static String {
602 CTEXT_SIGNED
603 .get_or_init(|| async {
604 let keyring = vec![KEYS.alice_public.clone(), KEYS.bob_public.clone()];
605 let compress = true;
606
607 pk_encrypt(
608 CLEARTEXT.to_vec(),
609 keyring,
610 KEYS.alice_secret.clone(),
611 compress,
612 SeipdVersion::V2,
613 )
614 .await
615 .unwrap()
616 })
617 .await
618 }
619
620 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
621 async fn test_encrypt_signed() {
622 assert!(!ctext_signed().await.is_empty());
623 assert!(
624 ctext_signed()
625 .await
626 .starts_with("-----BEGIN PGP MESSAGE-----")
627 );
628 }
629
630 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
631 async fn test_decrypt_signed() {
632 let decrypt_keyring = vec![KEYS.alice_secret.clone()];
634 let sig_check_keyring = vec![KEYS.alice_public.clone()];
635 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
636 ctext_signed().await.as_bytes(),
637 &decrypt_keyring,
638 &sig_check_keyring,
639 )
640 .await
641 .unwrap();
642 assert_eq!(content, CLEARTEXT);
643 assert_eq!(valid_signatures.len(), 1);
644 for recipient_fps in valid_signatures.values() {
645 assert_eq!(recipient_fps.len(), 2);
646 }
647
648 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
650 let sig_check_keyring = vec![KEYS.alice_public.clone()];
651 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
652 ctext_signed().await.as_bytes(),
653 &decrypt_keyring,
654 &sig_check_keyring,
655 )
656 .await
657 .unwrap();
658 assert_eq!(content, CLEARTEXT);
659 assert_eq!(valid_signatures.len(), 1);
660 for recipient_fps in valid_signatures.values() {
661 assert_eq!(recipient_fps.len(), 2);
662 }
663 }
664
665 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
666 async fn test_decrypt_no_sig_check() {
667 let keyring = vec![KEYS.alice_secret.clone()];
668 let (_msg, valid_signatures, content) =
669 pk_decrypt_and_validate(ctext_signed().await.as_bytes(), &keyring, &[])
670 .await
671 .unwrap();
672 assert_eq!(content, CLEARTEXT);
673 assert_eq!(valid_signatures.len(), 0);
674 }
675
676 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
677 async fn test_decrypt_signed_no_key() {
678 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
680 let sig_check_keyring = vec![KEYS.bob_public.clone()];
681 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
682 ctext_signed().await.as_bytes(),
683 &decrypt_keyring,
684 &sig_check_keyring,
685 )
686 .await
687 .unwrap();
688 assert_eq!(content, CLEARTEXT);
689 assert_eq!(valid_signatures.len(), 0);
690 }
691
692 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
693 async fn test_decrypt_unsigned() {
694 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
695 let ctext_unsigned = include_bytes!("../test-data/message/ctext_unsigned.asc");
696 let (_msg, valid_signatures, content) =
697 pk_decrypt_and_validate(ctext_unsigned, &decrypt_keyring, &[])
698 .await
699 .unwrap();
700 assert_eq!(content, CLEARTEXT);
701 assert_eq!(valid_signatures.len(), 0);
702 }
703
704 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
705 async fn test_dont_decrypt_expensive_message_happy_path() -> Result<()> {
706 let s2k = StringToKey::Salted {
707 hash_alg: HashAlgorithm::default(),
708 salt: [1; 8],
709 };
710
711 test_dont_decrypt_expensive_message_ex(s2k, false, None).await
712 }
713
714 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
715 async fn test_dont_decrypt_expensive_message_bad_s2k() -> Result<()> {
716 let s2k = StringToKey::new_default(&mut thread_rng()); test_dont_decrypt_expensive_message_ex(s2k, false, Some("unsupported string2key algorithm"))
719 .await
720 }
721
722 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
723 async fn test_dont_decrypt_expensive_message_multiple_secrets() -> Result<()> {
724 let s2k = StringToKey::Salted {
725 hash_alg: HashAlgorithm::default(),
726 salt: [1; 8],
727 };
728
729 test_dont_decrypt_expensive_message_ex(s2k, true, Some("decrypt_with_keys: missing key"))
732 .await
733 }
734
735 async fn test_dont_decrypt_expensive_message_ex(
741 s2k: StringToKey,
742 encrypt_twice: bool,
743 expected_error_msg: Option<&str>,
744 ) -> Result<()> {
745 let mut tcm = TestContextManager::new();
746 let bob = &tcm.bob().await;
747
748 let plain = Vec::from(b"this is the secret message");
749 let shared_secret = "shared secret";
750 let bob_fp = self_fingerprint(bob).await?;
751
752 let shared_secret_pw = Password::from(format!("securejoin/{bob_fp}/{shared_secret}"));
753 let msg = MessageBuilder::from_bytes("", plain);
754 let mut rng = thread_rng();
755
756 let mut msg = msg.seipd_v2(
757 &mut rng,
758 SymmetricKeyAlgorithm::AES128,
759 AeadAlgorithm::Ocb,
760 ChunkSize::C8KiB,
761 );
762 msg.encrypt_with_password(&mut rng, s2k.clone(), &shared_secret_pw)?;
763 if encrypt_twice {
764 msg.encrypt_with_password(&mut rng, s2k, &shared_secret_pw)?;
765 }
766
767 let ctext = msg.to_armored_string(&mut rng, Default::default())?;
768
769 let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
772 let res = decrypt_bytes(
773 ctext.into(),
774 &bob_private_keyring,
775 &[shared_secret.to_string()],
776 )
777 .await;
778
779 if let Some(expected_error_msg) = expected_error_msg {
780 assert_eq!(format!("{:#}", res.unwrap_err()), expected_error_msg);
781 } else {
782 res.unwrap();
783 }
784
785 Ok(())
786 }
787
788 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
789 async fn test_decryption_error_msg() -> Result<()> {
790 let mut tcm = TestContextManager::new();
791 let alice = &tcm.alice().await;
792 let bob = &tcm.bob().await;
793
794 let plain = Vec::from(b"this is the secret message");
795 let pk_for_encryption = load_self_public_key(alice).await?;
796
797 let compress = true;
799 let ctext = pk_encrypt(
800 plain,
801 vec![pk_for_encryption],
802 KEYS.alice_secret.clone(),
803 compress,
804 SeipdVersion::V2,
805 )
806 .await?;
807
808 let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
810 let error = decrypt_bytes(ctext.into(), &bob_private_keyring, &[])
811 .await
812 .unwrap_err();
813
814 assert_eq!(format!("{error:#}"), "decrypt_with_keys: missing key");
815
816 Ok(())
817 }
818
819 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
822 async fn test_anonymous_recipients() -> Result<()> {
823 let ctext = ctext_signed().await.as_bytes();
824 let cursor = Cursor::new(ctext);
825 let (msg, _headers) = Message::from_armor(cursor)?;
826
827 let Message::Encrypted { esk, .. } = msg else {
828 unreachable!();
829 };
830
831 for encrypted_session_key in esk {
832 let Esk::PublicKeyEncryptedSessionKey(pkesk) = encrypted_session_key else {
833 unreachable!()
834 };
835
836 match pkesk {
837 PublicKeyEncryptedSessionKey::V3 { id, .. } => {
838 assert!(id.is_wildcard());
839 }
840 PublicKeyEncryptedSessionKey::V6 { fingerprint, .. } => {
841 assert!(fingerprint.is_none());
842 }
843 PublicKeyEncryptedSessionKey::Other { .. } => unreachable!(),
844 }
845 }
846 Ok(())
847 }
848
849 #[test]
850 fn test_merge_openpgp_certificates() {
851 let alice = alice_keypair().to_public_key();
852 let bob = bob_keypair().to_public_key();
853
854 assert_eq!(
856 merge_openpgp_certificates(alice.clone(), alice.clone()).unwrap(),
857 alice
858 );
859 assert_eq!(
860 merge_openpgp_certificates(bob.clone(), bob.clone()).unwrap(),
861 bob
862 );
863
864 assert!(merge_openpgp_certificates(alice.clone(), bob.clone()).is_err());
866 assert!(merge_openpgp_certificates(bob.clone(), alice.clone()).is_err());
867 }
868}