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 Deserializable, DetachedSignature, EncryptionCaps, KeyType as PgpKeyType, MessageBuilder,
10 SecretKeyParamsBuilder, SignedKeyDetails, SignedPublicKey, SignedPublicSubKey, SignedSecretKey,
11 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, 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 valid_signature_fingerprints(
212 msg: &pgp::composed::Message,
213 public_keys_for_validation: &[SignedPublicKey],
214) -> HashMap<Fingerprint, Vec<Fingerprint>> {
215 let mut ret_signature_fingerprints = HashMap::new();
216 if msg.is_signed() {
217 for pkey in public_keys_for_validation {
218 if let Ok(signature) = msg.verify(&pkey.primary_key) {
219 let fp = pkey.dc_fingerprint();
220 let mut recipient_fps = Vec::new();
221 if let Some(cfg) = signature.config() {
222 for subpkt in &cfg.hashed_subpackets {
223 if let SubpacketData::IntendedRecipientFingerprint(fp) = &subpkt.data {
224 recipient_fps.push(fp.clone().into());
225 }
226 }
227 }
228 ret_signature_fingerprints.insert(fp, recipient_fps);
229 }
230 }
231 }
232 ret_signature_fingerprints
233}
234
235pub fn pk_validate(
237 content: &[u8],
238 signature: &[u8],
239 public_keys_for_validation: &[SignedPublicKey],
240) -> Result<HashSet<Fingerprint>> {
241 let mut ret: HashSet<Fingerprint> = Default::default();
242
243 let detached_signature = DetachedSignature::from_armor_single(Cursor::new(signature))?.0;
244
245 for pkey in public_keys_for_validation {
246 if detached_signature.verify(pkey, content).is_ok() {
247 let fp = pkey.dc_fingerprint();
248 ret.insert(fp);
249 }
250 }
251 Ok(ret)
252}
253
254pub async fn symm_encrypt_message(
258 plain: Vec<u8>,
259 private_key_for_signing: Option<SignedSecretKey>,
260 shared_secret: &str,
261 compress: bool,
262) -> Result<String> {
263 let shared_secret = Password::from(shared_secret.to_string());
264
265 tokio::task::spawn_blocking(move || {
266 let msg = MessageBuilder::from_bytes("", plain);
267 let mut rng = thread_rng();
268 let mut salt = [0u8; 8];
269 rng.fill(&mut salt[..]);
270 let s2k = StringToKey::Salted {
271 hash_alg: HashAlgorithm::default(),
272 salt,
273 };
274 let mut msg = msg.seipd_v2(
275 &mut rng,
276 SYMMETRIC_KEY_ALGORITHM,
277 AeadAlgorithm::Ocb,
278 ChunkSize::C8KiB,
279 );
280 msg.encrypt_with_password(&mut rng, s2k, &shared_secret)?;
281
282 if let Some(private_key_for_signing) = private_key_for_signing.as_deref() {
283 let hash_algorithm = private_key_for_signing.hash_alg();
284 msg.sign(private_key_for_signing, Password::empty(), hash_algorithm);
285 }
286 if compress {
287 msg.compression(CompressionAlgorithm::ZLIB);
288 }
289
290 let encoded_msg = msg.to_armored_string(&mut rng, Default::default())?;
291
292 Ok(encoded_msg)
293 })
294 .await?
295}
296
297pub fn merge_openpgp_certificates(
314 old_certificate: SignedPublicKey,
315 new_certificate: SignedPublicKey,
316) -> Result<SignedPublicKey> {
317 old_certificate
318 .verify_bindings()
319 .context("First key cannot be verified")?;
320 new_certificate
321 .verify_bindings()
322 .context("Second key cannot be verified")?;
323
324 let SignedPublicKey {
326 primary_key: old_primary_key,
327 details: old_details,
328 public_subkeys: old_public_subkeys,
329 } = old_certificate;
330 let SignedPublicKey {
331 primary_key: new_primary_key,
332 details: new_details,
333 public_subkeys: _new_public_subkeys,
334 } = new_certificate;
335
336 let old_imprint = old_primary_key.imprint::<Sha256>()?;
343 let new_imprint = new_primary_key.imprint::<Sha256>()?;
344 ensure!(
345 old_imprint == new_imprint,
346 "Cannot merge certificates with different primary keys {} and {}",
347 old_primary_key.fingerprint(),
348 new_primary_key.fingerprint()
349 );
350
351 let SignedKeyDetails {
360 revocation_signatures: _old_revocation_signatures,
361 direct_signatures: old_direct_signatures,
362 users: old_users,
363 user_attributes: _old_user_attributes,
364 } = old_details;
365 let SignedKeyDetails {
366 revocation_signatures: _new_revocation_signatures,
367 direct_signatures: new_direct_signatures,
368 users: new_users,
369 user_attributes: _new_user_attributes,
370 } = new_details;
371
372 let best_direct_key_signature: Option<Signature> = old_direct_signatures
374 .into_iter()
375 .chain(new_direct_signatures)
376 .filter(|x: &Signature| x.verify_key(&old_primary_key).is_ok())
377 .max_by_key(|x: &Signature|
378 x.created().map_or(0, |ts| ts.as_secs()));
381 let direct_signatures: Vec<Signature> = best_direct_key_signature.into_iter().collect();
382
383 let best_user: Option<SignedUser> = old_users
390 .into_iter()
391 .chain(new_users.clone())
392 .filter_map(|SignedUser { id, signatures }| {
393 let best_user_signature: Option<Signature> = signatures
396 .into_iter()
397 .filter(|signature: &Signature| {
398 signature
399 .verify_certification(&old_primary_key, pgp::types::Tag::UserId, &id)
400 .is_ok()
401 })
402 .max_by_key(|signature: &Signature| {
403 signature.created().map_or(0, |ts| ts.as_secs())
404 });
405 best_user_signature.map(|signature| (id, signature))
406 })
407 .max_by_key(|(_id, signature)| signature.created().map_or(0, |ts| ts.as_secs()))
408 .map(|(id, signature)| SignedUser {
409 id,
410 signatures: vec![signature],
411 });
412 let users: Vec<SignedUser> = best_user.into_iter().collect();
413
414 let public_subkeys = old_public_subkeys;
415
416 Ok(SignedPublicKey {
417 primary_key: old_primary_key,
418 details: SignedKeyDetails {
419 revocation_signatures: vec![],
420 direct_signatures,
421 users,
422 user_attributes: vec![],
423 },
424 public_subkeys,
425 })
426}
427
428pub(crate) fn addresses_from_public_key(public_key: &SignedPublicKey) -> Option<Vec<String>> {
432 for signature in &public_key.details.direct_signatures {
433 let signature_is_valid = signature.verify_key(&public_key.primary_key).is_ok();
436 debug_assert!(signature_is_valid);
437 if signature_is_valid {
438 for notation in signature.notations() {
439 if notation.name == "relays@chatmail.at"
440 && let Ok(value) = str::from_utf8(¬ation.value)
441 {
442 return Some(
443 value
444 .split(",")
445 .map(|s| s.to_string())
446 .filter(|s| may_be_valid_addr(s))
447 .take(3)
448 .collect(),
449 );
450 }
451 }
452 }
453 }
454 None
455}
456
457pub(crate) fn pubkey_supports_seipdv2(public_key: &SignedPublicKey) -> bool {
459 public_key
469 .details
470 .direct_signatures
471 .iter()
472 .chain(
473 public_key
474 .details
475 .users
476 .iter()
477 .flat_map(|user| user.signatures.iter()),
478 )
479 .any(|signature| {
480 signature
481 .features()
482 .is_some_and(|features| features.seipd_v2())
483 })
484}
485
486#[cfg(test)]
487mod tests {
488 use std::sync::LazyLock;
489 use tokio::sync::OnceCell;
490
491 use super::*;
492 use crate::{
493 config::Config,
494 decrypt,
495 key::{load_self_public_key, self_fingerprint, store_self_keypair},
496 mimefactory::{render_outer_message, wrap_encrypted_part},
497 test_utils::{TestContext, TestContextManager, alice_keypair, bob_keypair},
498 token,
499 };
500 use pgp::composed::{Esk, Message};
501 use pgp::packet::PublicKeyEncryptedSessionKey;
502
503 async fn decrypt_bytes(
504 bytes: Vec<u8>,
505 private_keys_for_decryption: &[SignedSecretKey],
506 auth_tokens_for_decryption: &[String],
507 ) -> Result<pgp::composed::Message<'static>> {
508 let t = &TestContext::new().await;
509 t.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
510 .await
511 .expect("Failed to configure address");
512
513 for secret in auth_tokens_for_decryption {
514 token::save(t, token::Namespace::Auth, None, secret, 0).await?;
515 }
516 let [secret_key] = private_keys_for_decryption else {
517 panic!("Only one private key is allowed anymore");
518 };
519 store_self_keypair(t, secret_key).await?;
520
521 let mime_message = wrap_encrypted_part(bytes.try_into().unwrap());
522 let rendered = render_outer_message(vec![], mime_message);
523 let parsed = mailparse::parse_mail(rendered.as_bytes())?;
524 let (decrypted, _fp) = decrypt::decrypt(t, &parsed).await?.unwrap();
525 Ok(decrypted)
526 }
527
528 async fn pk_decrypt_and_validate<'a>(
529 ctext: &'a [u8],
530 private_keys_for_decryption: &'a [SignedSecretKey],
531 public_keys_for_validation: &[SignedPublicKey],
532 ) -> Result<(
533 pgp::composed::Message<'static>,
534 HashMap<Fingerprint, Vec<Fingerprint>>,
535 Vec<u8>,
536 )> {
537 let mut msg = decrypt_bytes(ctext.to_vec(), private_keys_for_decryption, &[]).await?;
538 let content = msg.as_data_vec()?;
539 let ret_signature_fingerprints =
540 valid_signature_fingerprints(&msg, public_keys_for_validation);
541
542 Ok((msg, ret_signature_fingerprints, content))
543 }
544
545 #[test]
546 fn test_create_keypair() {
547 let keypair0 = create_keypair(EmailAddress::new("foo@bar.de").unwrap()).unwrap();
548 let keypair1 = create_keypair(EmailAddress::new("two@zwo.de").unwrap()).unwrap();
549 assert_ne!(keypair0.public_key(), keypair1.public_key());
550 }
551
552 struct TestKeys {
555 alice_secret: SignedSecretKey,
556 alice_public: SignedPublicKey,
557 bob_secret: SignedSecretKey,
558 bob_public: SignedPublicKey,
559 }
560
561 impl TestKeys {
562 fn new() -> TestKeys {
563 let alice = alice_keypair();
564 let bob = bob_keypair();
565 TestKeys {
566 alice_secret: alice.clone(),
567 alice_public: alice.to_public_key(),
568 bob_secret: bob.clone(),
569 bob_public: bob.to_public_key(),
570 }
571 }
572 }
573
574 static CLEARTEXT: &[u8] = b"This is a test";
576
577 static KEYS: LazyLock<TestKeys> = LazyLock::new(TestKeys::new);
579
580 static CTEXT_SIGNED: OnceCell<String> = OnceCell::const_new();
581
582 async fn ctext_signed() -> &'static String {
584 CTEXT_SIGNED
585 .get_or_init(|| async {
586 let keyring = vec![KEYS.alice_public.clone(), KEYS.bob_public.clone()];
587 let compress = true;
588
589 pk_encrypt(
590 CLEARTEXT.to_vec(),
591 keyring,
592 KEYS.alice_secret.clone(),
593 compress,
594 SeipdVersion::V2,
595 )
596 .await
597 .unwrap()
598 })
599 .await
600 }
601
602 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
603 async fn test_encrypt_signed() {
604 assert!(!ctext_signed().await.is_empty());
605 assert!(
606 ctext_signed()
607 .await
608 .starts_with("-----BEGIN PGP MESSAGE-----")
609 );
610 }
611
612 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
613 async fn test_decrypt_signed() {
614 let decrypt_keyring = vec![KEYS.alice_secret.clone()];
616 let sig_check_keyring = vec![KEYS.alice_public.clone()];
617 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
618 ctext_signed().await.as_bytes(),
619 &decrypt_keyring,
620 &sig_check_keyring,
621 )
622 .await
623 .unwrap();
624 assert_eq!(content, CLEARTEXT);
625 assert_eq!(valid_signatures.len(), 1);
626 for recipient_fps in valid_signatures.values() {
627 assert_eq!(recipient_fps.len(), 2);
628 }
629
630 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
632 let sig_check_keyring = vec![KEYS.alice_public.clone()];
633 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
634 ctext_signed().await.as_bytes(),
635 &decrypt_keyring,
636 &sig_check_keyring,
637 )
638 .await
639 .unwrap();
640 assert_eq!(content, CLEARTEXT);
641 assert_eq!(valid_signatures.len(), 1);
642 for recipient_fps in valid_signatures.values() {
643 assert_eq!(recipient_fps.len(), 2);
644 }
645 }
646
647 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
648 async fn test_decrypt_no_sig_check() {
649 let keyring = vec![KEYS.alice_secret.clone()];
650 let (_msg, valid_signatures, content) =
651 pk_decrypt_and_validate(ctext_signed().await.as_bytes(), &keyring, &[])
652 .await
653 .unwrap();
654 assert_eq!(content, CLEARTEXT);
655 assert_eq!(valid_signatures.len(), 0);
656 }
657
658 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
659 async fn test_decrypt_signed_no_key() {
660 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
662 let sig_check_keyring = vec![KEYS.bob_public.clone()];
663 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
664 ctext_signed().await.as_bytes(),
665 &decrypt_keyring,
666 &sig_check_keyring,
667 )
668 .await
669 .unwrap();
670 assert_eq!(content, CLEARTEXT);
671 assert_eq!(valid_signatures.len(), 0);
672 }
673
674 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
675 async fn test_decrypt_unsigned() {
676 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
677 let ctext_unsigned = include_bytes!("../test-data/message/ctext_unsigned.asc");
678 let (_msg, valid_signatures, content) =
679 pk_decrypt_and_validate(ctext_unsigned, &decrypt_keyring, &[])
680 .await
681 .unwrap();
682 assert_eq!(content, CLEARTEXT);
683 assert_eq!(valid_signatures.len(), 0);
684 }
685
686 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
687 async fn test_dont_decrypt_expensive_message_happy_path() -> Result<()> {
688 let s2k = StringToKey::Salted {
689 hash_alg: HashAlgorithm::default(),
690 salt: [1; 8],
691 };
692
693 test_dont_decrypt_expensive_message_ex(s2k, false, None).await
694 }
695
696 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
697 async fn test_dont_decrypt_expensive_message_bad_s2k() -> Result<()> {
698 let s2k = StringToKey::new_default(&mut thread_rng()); test_dont_decrypt_expensive_message_ex(s2k, false, Some("unsupported string2key algorithm"))
701 .await
702 }
703
704 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
705 async fn test_dont_decrypt_expensive_message_multiple_secrets() -> 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, true, Some("decrypt_with_keys: missing key"))
714 .await
715 }
716
717 async fn test_dont_decrypt_expensive_message_ex(
723 s2k: StringToKey,
724 encrypt_twice: bool,
725 expected_error_msg: Option<&str>,
726 ) -> Result<()> {
727 let mut tcm = TestContextManager::new();
728 let bob = &tcm.bob().await;
729
730 let plain = Vec::from(b"this is the secret message");
731 let shared_secret = "shared secret";
732 let bob_fp = self_fingerprint(bob).await?;
733
734 let shared_secret_pw = Password::from(format!("securejoin/{bob_fp}/{shared_secret}"));
735 let msg = MessageBuilder::from_bytes("", plain);
736 let mut rng = thread_rng();
737
738 let mut msg = msg.seipd_v2(
739 &mut rng,
740 SymmetricKeyAlgorithm::AES128,
741 AeadAlgorithm::Ocb,
742 ChunkSize::C8KiB,
743 );
744 msg.encrypt_with_password(&mut rng, s2k.clone(), &shared_secret_pw)?;
745 if encrypt_twice {
746 msg.encrypt_with_password(&mut rng, s2k, &shared_secret_pw)?;
747 }
748
749 let ctext = msg.to_armored_string(&mut rng, Default::default())?;
750
751 let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
754 let res = decrypt_bytes(
755 ctext.into(),
756 &bob_private_keyring,
757 &[shared_secret.to_string()],
758 )
759 .await;
760
761 if let Some(expected_error_msg) = expected_error_msg {
762 assert_eq!(format!("{:#}", res.unwrap_err()), expected_error_msg);
763 } else {
764 res.unwrap();
765 }
766
767 Ok(())
768 }
769
770 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
771 async fn test_decryption_error_msg() -> Result<()> {
772 let mut tcm = TestContextManager::new();
773 let alice = &tcm.alice().await;
774 let bob = &tcm.bob().await;
775
776 let plain = Vec::from(b"this is the secret message");
777 let pk_for_encryption = load_self_public_key(alice).await?;
778
779 let compress = true;
781 let ctext = pk_encrypt(
782 plain,
783 vec![pk_for_encryption],
784 KEYS.alice_secret.clone(),
785 compress,
786 SeipdVersion::V2,
787 )
788 .await?;
789
790 let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
792 let error = decrypt_bytes(ctext.into(), &bob_private_keyring, &[])
793 .await
794 .unwrap_err();
795
796 assert_eq!(format!("{error:#}"), "decrypt_with_keys: missing key");
797
798 Ok(())
799 }
800
801 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
804 async fn test_anonymous_recipients() -> Result<()> {
805 let ctext = ctext_signed().await.as_bytes();
806 let cursor = Cursor::new(ctext);
807 let (msg, _headers) = Message::from_armor(cursor)?;
808
809 let Message::Encrypted { esk, .. } = msg else {
810 unreachable!();
811 };
812
813 for encrypted_session_key in esk {
814 let Esk::PublicKeyEncryptedSessionKey(pkesk) = encrypted_session_key else {
815 unreachable!()
816 };
817
818 match pkesk {
819 PublicKeyEncryptedSessionKey::V3 { id, .. } => {
820 assert!(id.is_wildcard());
821 }
822 PublicKeyEncryptedSessionKey::V6 { fingerprint, .. } => {
823 assert!(fingerprint.is_none());
824 }
825 PublicKeyEncryptedSessionKey::Other { .. } => unreachable!(),
826 }
827 }
828 Ok(())
829 }
830
831 #[test]
832 fn test_merge_openpgp_certificates() {
833 let alice = alice_keypair().to_public_key();
834 let bob = bob_keypair().to_public_key();
835
836 assert_eq!(
838 merge_openpgp_certificates(alice.clone(), alice.clone()).unwrap(),
839 alice
840 );
841 assert_eq!(
842 merge_openpgp_certificates(bob.clone(), bob.clone()).unwrap(),
843 bob
844 );
845
846 assert!(merge_openpgp_certificates(alice.clone(), bob.clone()).is_err());
848 assert!(merge_openpgp_certificates(bob.clone(), alice.clone()).is_err());
849 }
850
851 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
853 async fn test_pqc() -> Result<()> {
854 let mut tcm = TestContextManager::new();
855 let alice = &tcm.alice().await;
856 let pqc = &tcm.pqc().await;
857
858 let pqc_received_message = tcm.send_recv_accept(alice, pqc, "Hi!").await;
859 let pqc_chat_id = pqc_received_message.chat_id;
860 let pqc_sent = pqc.send_text(pqc_chat_id, "Hello back!").await;
861
862 let alice_rcvd = alice.recv_msg(&pqc_sent).await;
863 assert_eq!(alice_rcvd.text, "Hello back!");
864
865 Ok(())
866 }
867
868 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
870 async fn test_securejoin_pqc_inviter() {
871 let mut tcm = TestContextManager::new();
872 let alice = &tcm.alice().await;
873 let pqc = &tcm.pqc().await;
874
875 tcm.execute_securejoin(pqc, alice).await;
876 }
877
878 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
880 async fn test_securejoin_pqc_joiner() {
881 let mut tcm = TestContextManager::new();
882 let pqc = &tcm.pqc().await;
883 let bob = &tcm.bob().await;
884
885 tcm.execute_securejoin(bob, pqc).await;
886 }
887}