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