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 for key in &public_keys_for_encryption {
132 let data = SubpacketData::IntendedRecipientFingerprint(key.fingerprint());
133 let subpkt = match private_key_for_signing.version() < KeyVersion::V6 {
134 true => Subpacket::regular(data)?,
135 false => Subpacket::critical(data)?,
136 };
137 hashed.push(subpkt);
138 }
139 hashed.push(Subpacket::regular(SubpacketData::IssuerFingerprint(
140 private_key_for_signing.fingerprint(),
141 ))?);
142 let mut unhashed = vec![];
143 if private_key_for_signing.version() <= KeyVersion::V4 {
144 unhashed.push(Subpacket::regular(SubpacketData::IssuerKeyId(
145 private_key_for_signing.legacy_key_id(),
146 ))?);
147 }
148 SubpacketConfig::UserDefined { hashed, unhashed }
149 };
150
151 let msg = MessageBuilder::from_bytes("", plain);
152 let encoded_msg = match seipd_version {
153 SeipdVersion::V1 => {
154 let mut msg = msg.seipd_v1(&mut rng, SYMMETRIC_KEY_ALGORITHM);
155
156 for pkey in pkeys {
157 msg.encrypt_to_key_anonymous(&mut rng, &pkey)?;
158 }
159
160 let hash_algorithm = private_key_for_signing.hash_alg();
161 msg.sign_with_subpackets(
162 &*private_key_for_signing,
163 Password::empty(),
164 hash_algorithm,
165 subpkts,
166 );
167 if compress {
168 msg.compression(CompressionAlgorithm::ZLIB);
169 }
170
171 msg.to_armored_string(&mut rng, Default::default())?
172 }
173 SeipdVersion::V2 => {
174 let mut msg = msg.seipd_v2(
175 &mut rng,
176 SYMMETRIC_KEY_ALGORITHM,
177 AeadAlgorithm::Ocb,
178 ChunkSize::C8KiB,
179 );
180
181 for pkey in pkeys {
182 msg.encrypt_to_key_anonymous(&mut rng, &pkey)?;
183 }
184
185 let hash_algorithm = private_key_for_signing.hash_alg();
186 msg.sign_with_subpackets(
187 &*private_key_for_signing,
188 Password::empty(),
189 hash_algorithm,
190 subpkts,
191 );
192 if compress {
193 msg.compression(CompressionAlgorithm::ZLIB);
194 }
195
196 msg.to_armored_string(&mut rng, Default::default())?
197 }
198 };
199
200 Ok(encoded_msg)
201 })
202 .await?
203}
204
205pub fn pk_calc_signature(
207 plain: Vec<u8>,
208 private_key_for_signing: &SignedSecretKey,
209) -> Result<String> {
210 let rng = thread_rng();
211
212 let mut config = SignatureConfig::from_key(
213 rng,
214 &private_key_for_signing.primary_key,
215 SignatureType::Binary,
216 )?;
217
218 config.hashed_subpackets = vec![
219 Subpacket::regular(SubpacketData::IssuerFingerprint(
220 private_key_for_signing.fingerprint(),
221 ))?,
222 Subpacket::critical(SubpacketData::SignatureCreationTime(
223 pgp::types::Timestamp::now(),
224 ))?,
225 ];
226 config.unhashed_subpackets = vec![];
227 if private_key_for_signing.version() <= KeyVersion::V4 {
228 config
229 .unhashed_subpackets
230 .push(Subpacket::regular(SubpacketData::IssuerKeyId(
231 private_key_for_signing.legacy_key_id(),
232 ))?);
233 }
234
235 let signature = config.sign(
236 &private_key_for_signing.primary_key,
237 &Password::empty(),
238 plain.as_slice(),
239 )?;
240
241 let sig = DetachedSignature::new(signature);
242
243 Ok(sig.to_armored_string(ArmorOptions::default())?)
244}
245
246pub fn valid_signature_fingerprints(
253 msg: &pgp::composed::Message,
254 public_keys_for_validation: &[SignedPublicKey],
255) -> HashMap<Fingerprint, Vec<Fingerprint>> {
256 let mut ret_signature_fingerprints = HashMap::new();
257 if msg.is_signed() {
258 for pkey in public_keys_for_validation {
259 if let Ok(signature) = msg.verify(&pkey.primary_key) {
260 let fp = pkey.dc_fingerprint();
261 let mut recipient_fps = Vec::new();
262 if let Some(cfg) = signature.config() {
263 for subpkt in &cfg.hashed_subpackets {
264 if let SubpacketData::IntendedRecipientFingerprint(fp) = &subpkt.data {
265 recipient_fps.push(fp.clone().into());
266 }
267 }
268 }
269 ret_signature_fingerprints.insert(fp, recipient_fps);
270 }
271 }
272 }
273 ret_signature_fingerprints
274}
275
276pub fn pk_validate(
278 content: &[u8],
279 signature: &[u8],
280 public_keys_for_validation: &[SignedPublicKey],
281) -> Result<HashSet<Fingerprint>> {
282 let mut ret: HashSet<Fingerprint> = Default::default();
283
284 let detached_signature = DetachedSignature::from_armor_single(Cursor::new(signature))?.0;
285
286 for pkey in public_keys_for_validation {
287 if detached_signature.verify(pkey, content).is_ok() {
288 let fp = pkey.dc_fingerprint();
289 ret.insert(fp);
290 }
291 }
292 Ok(ret)
293}
294
295pub async fn symm_encrypt_message(
299 plain: Vec<u8>,
300 private_key_for_signing: Option<SignedSecretKey>,
301 shared_secret: &str,
302 compress: bool,
303) -> Result<String> {
304 let shared_secret = Password::from(shared_secret.to_string());
305
306 tokio::task::spawn_blocking(move || {
307 let msg = MessageBuilder::from_bytes("", plain);
308 let mut rng = thread_rng();
309 let mut salt = [0u8; 8];
310 rng.fill(&mut salt[..]);
311 let s2k = StringToKey::Salted {
312 hash_alg: HashAlgorithm::default(),
313 salt,
314 };
315 let mut msg = msg.seipd_v2(
316 &mut rng,
317 SYMMETRIC_KEY_ALGORITHM,
318 AeadAlgorithm::Ocb,
319 ChunkSize::C8KiB,
320 );
321 msg.encrypt_with_password(&mut rng, s2k, &shared_secret)?;
322
323 if let Some(private_key_for_signing) = private_key_for_signing.as_deref() {
324 let hash_algorithm = private_key_for_signing.hash_alg();
325 msg.sign(private_key_for_signing, Password::empty(), hash_algorithm);
326 }
327 if compress {
328 msg.compression(CompressionAlgorithm::ZLIB);
329 }
330
331 let encoded_msg = msg.to_armored_string(&mut rng, Default::default())?;
332
333 Ok(encoded_msg)
334 })
335 .await?
336}
337
338pub fn merge_openpgp_certificates(
355 old_certificate: SignedPublicKey,
356 new_certificate: SignedPublicKey,
357) -> Result<SignedPublicKey> {
358 old_certificate
359 .verify_bindings()
360 .context("First key cannot be verified")?;
361 new_certificate
362 .verify_bindings()
363 .context("Second key cannot be verified")?;
364
365 let SignedPublicKey {
367 primary_key: old_primary_key,
368 details: old_details,
369 public_subkeys: old_public_subkeys,
370 } = old_certificate;
371 let SignedPublicKey {
372 primary_key: new_primary_key,
373 details: new_details,
374 public_subkeys: _new_public_subkeys,
375 } = new_certificate;
376
377 let old_imprint = old_primary_key.imprint::<Sha256>()?;
384 let new_imprint = new_primary_key.imprint::<Sha256>()?;
385 ensure!(
386 old_imprint == new_imprint,
387 "Cannot merge certificates with different primary keys {} and {}",
388 old_primary_key.fingerprint(),
389 new_primary_key.fingerprint()
390 );
391
392 let SignedKeyDetails {
401 revocation_signatures: _old_revocation_signatures,
402 direct_signatures: old_direct_signatures,
403 users: old_users,
404 user_attributes: _old_user_attributes,
405 } = old_details;
406 let SignedKeyDetails {
407 revocation_signatures: _new_revocation_signatures,
408 direct_signatures: new_direct_signatures,
409 users: new_users,
410 user_attributes: _new_user_attributes,
411 } = new_details;
412
413 let best_direct_key_signature: Option<Signature> = old_direct_signatures
415 .into_iter()
416 .chain(new_direct_signatures)
417 .filter(|x: &Signature| x.verify_key(&old_primary_key).is_ok())
418 .max_by_key(|x: &Signature|
419 x.created().map_or(0, |ts| ts.as_secs()));
422 let direct_signatures: Vec<Signature> = best_direct_key_signature.into_iter().collect();
423
424 let best_user: Option<SignedUser> = old_users
431 .into_iter()
432 .chain(new_users.clone())
433 .filter_map(|SignedUser { id, signatures }| {
434 let best_user_signature: Option<Signature> = signatures
437 .into_iter()
438 .filter(|signature: &Signature| {
439 signature
440 .verify_certification(&old_primary_key, pgp::types::Tag::UserId, &id)
441 .is_ok()
442 })
443 .max_by_key(|signature: &Signature| {
444 signature.created().map_or(0, |ts| ts.as_secs())
445 });
446 best_user_signature.map(|signature| (id, signature))
447 })
448 .max_by_key(|(_id, signature)| signature.created().map_or(0, |ts| ts.as_secs()))
449 .map(|(id, signature)| SignedUser {
450 id,
451 signatures: vec![signature],
452 });
453 let users: Vec<SignedUser> = best_user.into_iter().collect();
454
455 let public_subkeys = old_public_subkeys;
456
457 Ok(SignedPublicKey {
458 primary_key: old_primary_key,
459 details: SignedKeyDetails {
460 revocation_signatures: vec![],
461 direct_signatures,
462 users,
463 user_attributes: vec![],
464 },
465 public_subkeys,
466 })
467}
468
469pub(crate) fn addresses_from_public_key(public_key: &SignedPublicKey) -> Option<Vec<String>> {
473 for signature in &public_key.details.direct_signatures {
474 let signature_is_valid = signature.verify_key(&public_key.primary_key).is_ok();
477 debug_assert!(signature_is_valid);
478 if signature_is_valid {
479 for notation in signature.notations() {
480 if notation.name == "relays@chatmail.at"
481 && let Ok(value) = str::from_utf8(¬ation.value)
482 {
483 return Some(
484 value
485 .split(",")
486 .map(|s| s.to_string())
487 .filter(|s| may_be_valid_addr(s))
488 .take(3)
489 .collect(),
490 );
491 }
492 }
493 }
494 }
495 None
496}
497
498pub(crate) fn pubkey_supports_seipdv2(public_key: &SignedPublicKey) -> bool {
500 public_key
510 .details
511 .direct_signatures
512 .iter()
513 .chain(
514 public_key
515 .details
516 .users
517 .iter()
518 .flat_map(|user| user.signatures.iter()),
519 )
520 .any(|signature| {
521 signature
522 .features()
523 .is_some_and(|features| features.seipd_v2())
524 })
525}
526
527#[cfg(test)]
528mod tests {
529 use std::sync::LazyLock;
530 use tokio::sync::OnceCell;
531
532 use super::*;
533 use crate::{
534 config::Config,
535 decrypt,
536 key::{load_self_public_key, self_fingerprint, store_self_keypair},
537 mimefactory::{render_outer_message, wrap_encrypted_part},
538 test_utils::{TestContext, TestContextManager, alice_keypair, bob_keypair},
539 token,
540 };
541 use pgp::composed::{Esk, Message};
542 use pgp::packet::PublicKeyEncryptedSessionKey;
543
544 async fn decrypt_bytes(
545 bytes: Vec<u8>,
546 private_keys_for_decryption: &[SignedSecretKey],
547 auth_tokens_for_decryption: &[String],
548 ) -> Result<pgp::composed::Message<'static>> {
549 let t = &TestContext::new().await;
550 t.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
551 .await
552 .expect("Failed to configure address");
553
554 for secret in auth_tokens_for_decryption {
555 token::save(t, token::Namespace::Auth, None, secret, 0).await?;
556 }
557 let [secret_key] = private_keys_for_decryption else {
558 panic!("Only one private key is allowed anymore");
559 };
560 store_self_keypair(t, secret_key).await?;
561
562 let mime_message = wrap_encrypted_part(bytes.try_into().unwrap());
563 let rendered = render_outer_message(vec![], mime_message);
564 let parsed = mailparse::parse_mail(rendered.as_bytes())?;
565 let (decrypted, _fp) = decrypt::decrypt(t, &parsed).await?.unwrap();
566 Ok(decrypted)
567 }
568
569 async fn pk_decrypt_and_validate<'a>(
570 ctext: &'a [u8],
571 private_keys_for_decryption: &'a [SignedSecretKey],
572 public_keys_for_validation: &[SignedPublicKey],
573 ) -> Result<(
574 pgp::composed::Message<'static>,
575 HashMap<Fingerprint, Vec<Fingerprint>>,
576 Vec<u8>,
577 )> {
578 let mut msg = decrypt_bytes(ctext.to_vec(), private_keys_for_decryption, &[]).await?;
579 let content = msg.as_data_vec()?;
580 let ret_signature_fingerprints =
581 valid_signature_fingerprints(&msg, public_keys_for_validation);
582
583 Ok((msg, ret_signature_fingerprints, content))
584 }
585
586 #[test]
587 fn test_create_keypair() {
588 let keypair0 = create_keypair(EmailAddress::new("foo@bar.de").unwrap()).unwrap();
589 let keypair1 = create_keypair(EmailAddress::new("two@zwo.de").unwrap()).unwrap();
590 assert_ne!(keypair0.public_key(), keypair1.public_key());
591 }
592
593 struct TestKeys {
596 alice_secret: SignedSecretKey,
597 alice_public: SignedPublicKey,
598 bob_secret: SignedSecretKey,
599 bob_public: SignedPublicKey,
600 }
601
602 impl TestKeys {
603 fn new() -> TestKeys {
604 let alice = alice_keypair();
605 let bob = bob_keypair();
606 TestKeys {
607 alice_secret: alice.clone(),
608 alice_public: alice.to_public_key(),
609 bob_secret: bob.clone(),
610 bob_public: bob.to_public_key(),
611 }
612 }
613 }
614
615 static CLEARTEXT: &[u8] = b"This is a test";
617
618 static KEYS: LazyLock<TestKeys> = LazyLock::new(TestKeys::new);
620
621 static CTEXT_SIGNED: OnceCell<String> = OnceCell::const_new();
622
623 async fn ctext_signed() -> &'static String {
625 CTEXT_SIGNED
626 .get_or_init(|| async {
627 let keyring = vec![KEYS.alice_public.clone(), KEYS.bob_public.clone()];
628 let compress = true;
629
630 pk_encrypt(
631 CLEARTEXT.to_vec(),
632 keyring,
633 KEYS.alice_secret.clone(),
634 compress,
635 SeipdVersion::V2,
636 )
637 .await
638 .unwrap()
639 })
640 .await
641 }
642
643 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
644 async fn test_encrypt_signed() {
645 assert!(!ctext_signed().await.is_empty());
646 assert!(
647 ctext_signed()
648 .await
649 .starts_with("-----BEGIN PGP MESSAGE-----")
650 );
651 }
652
653 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
654 async fn test_decrypt_signed() {
655 let decrypt_keyring = vec![KEYS.alice_secret.clone()];
657 let sig_check_keyring = vec![KEYS.alice_public.clone()];
658 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
659 ctext_signed().await.as_bytes(),
660 &decrypt_keyring,
661 &sig_check_keyring,
662 )
663 .await
664 .unwrap();
665 assert_eq!(content, CLEARTEXT);
666 assert_eq!(valid_signatures.len(), 1);
667 for recipient_fps in valid_signatures.values() {
668 assert_eq!(recipient_fps.len(), 2);
669 }
670
671 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
673 let sig_check_keyring = vec![KEYS.alice_public.clone()];
674 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
675 ctext_signed().await.as_bytes(),
676 &decrypt_keyring,
677 &sig_check_keyring,
678 )
679 .await
680 .unwrap();
681 assert_eq!(content, CLEARTEXT);
682 assert_eq!(valid_signatures.len(), 1);
683 for recipient_fps in valid_signatures.values() {
684 assert_eq!(recipient_fps.len(), 2);
685 }
686 }
687
688 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
689 async fn test_decrypt_no_sig_check() {
690 let keyring = vec![KEYS.alice_secret.clone()];
691 let (_msg, valid_signatures, content) =
692 pk_decrypt_and_validate(ctext_signed().await.as_bytes(), &keyring, &[])
693 .await
694 .unwrap();
695 assert_eq!(content, CLEARTEXT);
696 assert_eq!(valid_signatures.len(), 0);
697 }
698
699 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
700 async fn test_decrypt_signed_no_key() {
701 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
703 let sig_check_keyring = vec![KEYS.bob_public.clone()];
704 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
705 ctext_signed().await.as_bytes(),
706 &decrypt_keyring,
707 &sig_check_keyring,
708 )
709 .await
710 .unwrap();
711 assert_eq!(content, CLEARTEXT);
712 assert_eq!(valid_signatures.len(), 0);
713 }
714
715 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
716 async fn test_decrypt_unsigned() {
717 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
718 let ctext_unsigned = include_bytes!("../test-data/message/ctext_unsigned.asc");
719 let (_msg, valid_signatures, content) =
720 pk_decrypt_and_validate(ctext_unsigned, &decrypt_keyring, &[])
721 .await
722 .unwrap();
723 assert_eq!(content, CLEARTEXT);
724 assert_eq!(valid_signatures.len(), 0);
725 }
726
727 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
728 async fn test_dont_decrypt_expensive_message_happy_path() -> Result<()> {
729 let s2k = StringToKey::Salted {
730 hash_alg: HashAlgorithm::default(),
731 salt: [1; 8],
732 };
733
734 test_dont_decrypt_expensive_message_ex(s2k, false, None).await
735 }
736
737 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
738 async fn test_dont_decrypt_expensive_message_bad_s2k() -> Result<()> {
739 let s2k = StringToKey::new_default(&mut thread_rng()); test_dont_decrypt_expensive_message_ex(s2k, false, Some("unsupported string2key algorithm"))
742 .await
743 }
744
745 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
746 async fn test_dont_decrypt_expensive_message_multiple_secrets() -> Result<()> {
747 let s2k = StringToKey::Salted {
748 hash_alg: HashAlgorithm::default(),
749 salt: [1; 8],
750 };
751
752 test_dont_decrypt_expensive_message_ex(s2k, true, Some("decrypt_with_keys: missing key"))
755 .await
756 }
757
758 async fn test_dont_decrypt_expensive_message_ex(
764 s2k: StringToKey,
765 encrypt_twice: bool,
766 expected_error_msg: Option<&str>,
767 ) -> Result<()> {
768 let mut tcm = TestContextManager::new();
769 let bob = &tcm.bob().await;
770
771 let plain = Vec::from(b"this is the secret message");
772 let shared_secret = "shared secret";
773 let bob_fp = self_fingerprint(bob).await?;
774
775 let shared_secret_pw = Password::from(format!("securejoin/{bob_fp}/{shared_secret}"));
776 let msg = MessageBuilder::from_bytes("", plain);
777 let mut rng = thread_rng();
778
779 let mut msg = msg.seipd_v2(
780 &mut rng,
781 SymmetricKeyAlgorithm::AES128,
782 AeadAlgorithm::Ocb,
783 ChunkSize::C8KiB,
784 );
785 msg.encrypt_with_password(&mut rng, s2k.clone(), &shared_secret_pw)?;
786 if encrypt_twice {
787 msg.encrypt_with_password(&mut rng, s2k, &shared_secret_pw)?;
788 }
789
790 let ctext = msg.to_armored_string(&mut rng, Default::default())?;
791
792 let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
795 let res = decrypt_bytes(
796 ctext.into(),
797 &bob_private_keyring,
798 &[shared_secret.to_string()],
799 )
800 .await;
801
802 if let Some(expected_error_msg) = expected_error_msg {
803 assert_eq!(format!("{:#}", res.unwrap_err()), expected_error_msg);
804 } else {
805 res.unwrap();
806 }
807
808 Ok(())
809 }
810
811 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
812 async fn test_decryption_error_msg() -> Result<()> {
813 let mut tcm = TestContextManager::new();
814 let alice = &tcm.alice().await;
815 let bob = &tcm.bob().await;
816
817 let plain = Vec::from(b"this is the secret message");
818 let pk_for_encryption = load_self_public_key(alice).await?;
819
820 let compress = true;
822 let ctext = pk_encrypt(
823 plain,
824 vec![pk_for_encryption],
825 KEYS.alice_secret.clone(),
826 compress,
827 SeipdVersion::V2,
828 )
829 .await?;
830
831 let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
833 let error = decrypt_bytes(ctext.into(), &bob_private_keyring, &[])
834 .await
835 .unwrap_err();
836
837 assert_eq!(format!("{error:#}"), "decrypt_with_keys: missing key");
838
839 Ok(())
840 }
841
842 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
845 async fn test_anonymous_recipients() -> Result<()> {
846 let ctext = ctext_signed().await.as_bytes();
847 let cursor = Cursor::new(ctext);
848 let (msg, _headers) = Message::from_armor(cursor)?;
849
850 let Message::Encrypted { esk, .. } = msg else {
851 unreachable!();
852 };
853
854 for encrypted_session_key in esk {
855 let Esk::PublicKeyEncryptedSessionKey(pkesk) = encrypted_session_key else {
856 unreachable!()
857 };
858
859 match pkesk {
860 PublicKeyEncryptedSessionKey::V3 { id, .. } => {
861 assert!(id.is_wildcard());
862 }
863 PublicKeyEncryptedSessionKey::V6 { fingerprint, .. } => {
864 assert!(fingerprint.is_none());
865 }
866 PublicKeyEncryptedSessionKey::Other { .. } => unreachable!(),
867 }
868 }
869 Ok(())
870 }
871
872 #[test]
873 fn test_merge_openpgp_certificates() {
874 let alice = alice_keypair().to_public_key();
875 let bob = bob_keypair().to_public_key();
876
877 assert_eq!(
879 merge_openpgp_certificates(alice.clone(), alice.clone()).unwrap(),
880 alice
881 );
882 assert_eq!(
883 merge_openpgp_certificates(bob.clone(), bob.clone()).unwrap(),
884 bob
885 );
886
887 assert!(merge_openpgp_certificates(alice.clone(), bob.clone()).is_err());
889 assert!(merge_openpgp_certificates(bob.clone(), alice.clone()).is_err());
890 }
891}