1use std::collections::{BTreeMap, HashMap, HashSet};
4use std::io::{BufRead, Cursor};
5
6use anyhow::{Context as _, Result, ensure};
7use deltachat_contact_tools::{EmailAddress, may_be_valid_addr};
8use pgp::armor::BlockType;
9use pgp::composed::{
10 ArmorOptions, Deserializable, DetachedSignature, EncryptionCaps, KeyType as PgpKeyType,
11 Message, MessageBuilder, SecretKeyParamsBuilder, SignedKeyDetails, SignedPublicKey,
12 SignedPublicSubKey, SignedSecretKey, SubkeyParamsBuilder, SubpacketConfig,
13};
14use pgp::crypto::aead::{AeadAlgorithm, ChunkSize};
15use pgp::crypto::ecc_curve::ECCCurve;
16use pgp::crypto::hash::HashAlgorithm;
17use pgp::crypto::sym::SymmetricKeyAlgorithm;
18use pgp::packet::{Signature, SignatureConfig, SignatureType, Subpacket, SubpacketData};
19use pgp::types::{
20 CompressionAlgorithm, Imprint, KeyDetails, KeyVersion, Password, SignedUser, SigningKey as _,
21 StringToKey,
22};
23use rand_old::{Rng as _, thread_rng};
24use sha2::Sha256;
25use tokio::runtime::Handle;
26
27use crate::key::{DcKey, Fingerprint};
28
29#[cfg(test)]
30pub(crate) const HEADER_AUTOCRYPT: &str = "autocrypt-prefer-encrypt";
31
32pub(crate) const HEADER_SETUPCODE: &str = "passphrase-begin";
33
34const SYMMETRIC_KEY_ALGORITHM: SymmetricKeyAlgorithm = SymmetricKeyAlgorithm::AES128;
36
37pub fn split_armored_data(buf: &[u8]) -> Result<(BlockType, BTreeMap<String, String>, Vec<u8>)> {
41 use std::io::Read;
42
43 let cursor = Cursor::new(buf);
44 let mut dearmor = pgp::armor::Dearmor::new(cursor);
45
46 let mut bytes = Vec::with_capacity(buf.len());
47
48 dearmor.read_to_end(&mut bytes)?;
49 let typ = dearmor.typ.context("failed to parse type")?;
50
51 let headers = dearmor
53 .headers
54 .into_iter()
55 .map(|(key, values)| {
56 (
57 key.trim().to_lowercase(),
58 values
59 .last()
60 .map_or_else(String::new, |s| s.trim().to_string()),
61 )
62 })
63 .collect();
64
65 Ok((typ, headers, bytes))
66}
67
68pub(crate) fn create_keypair(addr: EmailAddress) -> Result<SignedSecretKey> {
73 let signing_key_type = PgpKeyType::Ed25519Legacy;
74 let encryption_key_type = PgpKeyType::ECDH(ECCCurve::Curve25519);
75
76 let user_id = format!("<{addr}>");
77 let key_params = SecretKeyParamsBuilder::default()
78 .key_type(signing_key_type)
79 .can_certify(true)
80 .can_sign(true)
81 .feature_seipd_v2(true)
82 .primary_user_id(user_id)
83 .passphrase(None)
84 .preferred_symmetric_algorithms(smallvec![
85 SymmetricKeyAlgorithm::AES256,
86 SymmetricKeyAlgorithm::AES192,
87 SymmetricKeyAlgorithm::AES128,
88 ])
89 .preferred_hash_algorithms(smallvec![
90 HashAlgorithm::Sha256,
91 HashAlgorithm::Sha384,
92 HashAlgorithm::Sha512,
93 HashAlgorithm::Sha224,
94 ])
95 .preferred_compression_algorithms(smallvec![
96 CompressionAlgorithm::ZLIB,
97 CompressionAlgorithm::ZIP,
98 ])
99 .subkey(
100 SubkeyParamsBuilder::default()
101 .key_type(encryption_key_type)
102 .can_encrypt(EncryptionCaps::All)
103 .passphrase(None)
104 .build()
105 .context("failed to build subkey parameters")?,
106 )
107 .build()
108 .context("failed to build key parameters")?;
109
110 let mut rng = thread_rng();
111 let secret_key = key_params
112 .generate(&mut rng)
113 .context("Failed to generate the key")?;
114 secret_key
115 .verify_bindings()
116 .context("Invalid secret key generated")?;
117
118 Ok(secret_key)
119}
120
121fn select_pk_for_encryption(key: &SignedPublicKey) -> Option<&SignedPublicSubKey> {
127 key.public_subkeys
128 .iter()
129 .find(|subkey| subkey.algorithm().can_encrypt())
130}
131
132#[derive(Debug)]
138pub enum SeipdVersion {
139 V1,
141
142 V2,
144}
145
146#[expect(clippy::arithmetic_side_effects)]
149pub async fn pk_encrypt(
150 plain: Vec<u8>,
151 public_keys_for_encryption: Vec<SignedPublicKey>,
152 private_key_for_signing: SignedSecretKey,
153 compress: bool,
154 anonymous_recipients: bool,
155 seipd_version: SeipdVersion,
156) -> Result<String> {
157 Handle::current()
158 .spawn_blocking(move || {
159 let mut rng = thread_rng();
160
161 let pkeys = public_keys_for_encryption
162 .iter()
163 .filter_map(select_pk_for_encryption);
164 let subpkts = {
165 let mut hashed = Vec::with_capacity(1 + public_keys_for_encryption.len() + 1);
166 hashed.push(Subpacket::critical(SubpacketData::SignatureCreationTime(
167 pgp::types::Timestamp::now(),
168 ))?);
169 let skip = private_key_for_signing.dc_fingerprint().hex()
171 == "B86586B6DEF437D674BFAFC02A6B2EBC633B9E82";
172 for key in &public_keys_for_encryption {
173 if skip {
174 break;
175 }
176 let data = SubpacketData::IntendedRecipientFingerprint(key.fingerprint());
177 let subpkt = match private_key_for_signing.version() < KeyVersion::V6 {
178 true => Subpacket::regular(data)?,
179 false => Subpacket::critical(data)?,
180 };
181 hashed.push(subpkt);
182 }
183 hashed.push(Subpacket::regular(SubpacketData::IssuerFingerprint(
184 private_key_for_signing.fingerprint(),
185 ))?);
186 let mut unhashed = vec![];
187 if private_key_for_signing.version() <= KeyVersion::V4 {
188 unhashed.push(Subpacket::regular(SubpacketData::IssuerKeyId(
189 private_key_for_signing.legacy_key_id(),
190 ))?);
191 }
192 SubpacketConfig::UserDefined { hashed, unhashed }
193 };
194
195 let msg = MessageBuilder::from_bytes("", plain);
196 let encoded_msg = match seipd_version {
197 SeipdVersion::V1 => {
198 let mut msg = msg.seipd_v1(&mut rng, SYMMETRIC_KEY_ALGORITHM);
199
200 for pkey in pkeys {
201 if anonymous_recipients {
202 msg.encrypt_to_key_anonymous(&mut rng, &pkey)?;
203 } else {
204 msg.encrypt_to_key(&mut rng, &pkey)?;
205 }
206 }
207
208 let hash_algorithm = private_key_for_signing.hash_alg();
209 msg.sign_with_subpackets(
210 &*private_key_for_signing,
211 Password::empty(),
212 hash_algorithm,
213 subpkts,
214 );
215 if compress {
216 msg.compression(CompressionAlgorithm::ZLIB);
217 }
218
219 msg.to_armored_string(&mut rng, Default::default())?
220 }
221 SeipdVersion::V2 => {
222 let mut msg = msg.seipd_v2(
223 &mut rng,
224 SYMMETRIC_KEY_ALGORITHM,
225 AeadAlgorithm::Ocb,
226 ChunkSize::C8KiB,
227 );
228
229 for pkey in pkeys {
230 if anonymous_recipients {
231 msg.encrypt_to_key_anonymous(&mut rng, &pkey)?;
232 } else {
233 msg.encrypt_to_key(&mut rng, &pkey)?;
234 }
235 }
236
237 let hash_algorithm = private_key_for_signing.hash_alg();
238 msg.sign_with_subpackets(
239 &*private_key_for_signing,
240 Password::empty(),
241 hash_algorithm,
242 subpkts,
243 );
244 if compress {
245 msg.compression(CompressionAlgorithm::ZLIB);
246 }
247
248 msg.to_armored_string(&mut rng, Default::default())?
249 }
250 };
251
252 Ok(encoded_msg)
253 })
254 .await?
255}
256
257pub fn pk_calc_signature(
259 plain: Vec<u8>,
260 private_key_for_signing: &SignedSecretKey,
261) -> Result<String> {
262 let rng = thread_rng();
263
264 let mut config = SignatureConfig::from_key(
265 rng,
266 &private_key_for_signing.primary_key,
267 SignatureType::Binary,
268 )?;
269
270 config.hashed_subpackets = vec![
271 Subpacket::regular(SubpacketData::IssuerFingerprint(
272 private_key_for_signing.fingerprint(),
273 ))?,
274 Subpacket::critical(SubpacketData::SignatureCreationTime(
275 pgp::types::Timestamp::now(),
276 ))?,
277 ];
278 config.unhashed_subpackets = vec![];
279 if private_key_for_signing.version() <= KeyVersion::V4 {
280 config
281 .unhashed_subpackets
282 .push(Subpacket::regular(SubpacketData::IssuerKeyId(
283 private_key_for_signing.legacy_key_id(),
284 ))?);
285 }
286
287 let signature = config.sign(
288 &private_key_for_signing.primary_key,
289 &Password::empty(),
290 plain.as_slice(),
291 )?;
292
293 let sig = DetachedSignature::new(signature);
294
295 Ok(sig.to_armored_string(ArmorOptions::default())?)
296}
297
298pub fn valid_signature_fingerprints(
305 msg: &pgp::composed::Message,
306 public_keys_for_validation: &[SignedPublicKey],
307) -> HashMap<Fingerprint, Vec<Fingerprint>> {
308 let mut ret_signature_fingerprints = HashMap::new();
309 if msg.is_signed() {
310 for pkey in public_keys_for_validation {
311 if let Ok(signature) = msg.verify(&pkey.primary_key) {
312 let fp = pkey.dc_fingerprint();
313 let mut recipient_fps = Vec::new();
314 if let Some(cfg) = signature.config() {
315 for subpkt in &cfg.hashed_subpackets {
316 if let SubpacketData::IntendedRecipientFingerprint(fp) = &subpkt.data {
317 recipient_fps.push(fp.clone().into());
318 }
319 }
320 }
321 ret_signature_fingerprints.insert(fp, recipient_fps);
322 }
323 }
324 }
325 ret_signature_fingerprints
326}
327
328pub fn pk_validate(
330 content: &[u8],
331 signature: &[u8],
332 public_keys_for_validation: &[SignedPublicKey],
333) -> Result<HashSet<Fingerprint>> {
334 let mut ret: HashSet<Fingerprint> = Default::default();
335
336 let detached_signature = DetachedSignature::from_armor_single(Cursor::new(signature))?.0;
337
338 for pkey in public_keys_for_validation {
339 if detached_signature.verify(pkey, content).is_ok() {
340 let fp = pkey.dc_fingerprint();
341 ret.insert(fp);
342 }
343 }
344 Ok(ret)
345}
346
347pub async fn symm_encrypt_autocrypt_setup(passphrase: &str, plain: Vec<u8>) -> Result<String> {
349 let passphrase = Password::from(passphrase.to_string());
350
351 tokio::task::spawn_blocking(move || {
352 let mut rng = thread_rng();
353 let s2k = StringToKey::new_default(&mut rng);
354 let builder = MessageBuilder::from_bytes("", plain);
355 let mut builder = builder.seipd_v1(&mut rng, SYMMETRIC_KEY_ALGORITHM);
356 builder.encrypt_with_password(s2k, &passphrase)?;
357
358 let encoded_msg = builder.to_armored_string(&mut rng, Default::default())?;
359
360 Ok(encoded_msg)
361 })
362 .await?
363}
364
365pub async fn symm_encrypt_message(
369 plain: Vec<u8>,
370 private_key_for_signing: Option<SignedSecretKey>,
371 shared_secret: &str,
372 compress: bool,
373) -> Result<String> {
374 let shared_secret = Password::from(shared_secret.to_string());
375
376 tokio::task::spawn_blocking(move || {
377 let msg = MessageBuilder::from_bytes("", plain);
378 let mut rng = thread_rng();
379 let mut salt = [0u8; 8];
380 rng.fill(&mut salt[..]);
381 let s2k = StringToKey::Salted {
382 hash_alg: HashAlgorithm::default(),
383 salt,
384 };
385 let mut msg = msg.seipd_v2(
386 &mut rng,
387 SYMMETRIC_KEY_ALGORITHM,
388 AeadAlgorithm::Ocb,
389 ChunkSize::C8KiB,
390 );
391 msg.encrypt_with_password(&mut rng, s2k, &shared_secret)?;
392
393 if let Some(private_key_for_signing) = private_key_for_signing.as_deref() {
394 let hash_algorithm = private_key_for_signing.hash_alg();
395 msg.sign(private_key_for_signing, Password::empty(), hash_algorithm);
396 }
397 if compress {
398 msg.compression(CompressionAlgorithm::ZLIB);
399 }
400
401 let encoded_msg = msg.to_armored_string(&mut rng, Default::default())?;
402
403 Ok(encoded_msg)
404 })
405 .await?
406}
407
408pub async fn symm_decrypt<T: BufRead + std::fmt::Debug + 'static + Send>(
410 passphrase: &str,
411 ctext: T,
412) -> Result<Vec<u8>> {
413 let passphrase = passphrase.to_string();
414 tokio::task::spawn_blocking(move || {
415 let (enc_msg, _) = Message::from_armor(ctext)?;
416 let password = Password::from(passphrase);
417
418 let msg = enc_msg.decrypt_with_password(&password)?;
419 let res = msg.decompress()?.as_data_vec()?;
420 Ok(res)
421 })
422 .await?
423}
424
425pub fn merge_openpgp_certificates(
442 old_certificate: SignedPublicKey,
443 new_certificate: SignedPublicKey,
444) -> Result<SignedPublicKey> {
445 old_certificate
446 .verify_bindings()
447 .context("First key cannot be verified")?;
448 new_certificate
449 .verify_bindings()
450 .context("Second key cannot be verified")?;
451
452 let SignedPublicKey {
454 primary_key: old_primary_key,
455 details: old_details,
456 public_subkeys: old_public_subkeys,
457 } = old_certificate;
458 let SignedPublicKey {
459 primary_key: new_primary_key,
460 details: new_details,
461 public_subkeys: _new_public_subkeys,
462 } = new_certificate;
463
464 let old_imprint = old_primary_key.imprint::<Sha256>()?;
471 let new_imprint = new_primary_key.imprint::<Sha256>()?;
472 ensure!(
473 old_imprint == new_imprint,
474 "Cannot merge certificates with different primary keys {} and {}",
475 old_primary_key.fingerprint(),
476 new_primary_key.fingerprint()
477 );
478
479 let SignedKeyDetails {
488 revocation_signatures: _old_revocation_signatures,
489 direct_signatures: old_direct_signatures,
490 users: old_users,
491 user_attributes: _old_user_attributes,
492 } = old_details;
493 let SignedKeyDetails {
494 revocation_signatures: _new_revocation_signatures,
495 direct_signatures: new_direct_signatures,
496 users: new_users,
497 user_attributes: _new_user_attributes,
498 } = new_details;
499
500 let best_direct_key_signature: Option<Signature> = old_direct_signatures
502 .into_iter()
503 .chain(new_direct_signatures)
504 .filter(|x: &Signature| x.verify_key(&old_primary_key).is_ok())
505 .max_by_key(|x: &Signature|
506 x.created().map_or(0, |ts| ts.as_secs()));
509 let direct_signatures: Vec<Signature> = best_direct_key_signature.into_iter().collect();
510
511 let best_user: Option<SignedUser> = old_users
518 .into_iter()
519 .chain(new_users.clone())
520 .filter_map(|SignedUser { id, signatures }| {
521 let best_user_signature: Option<Signature> = signatures
524 .into_iter()
525 .filter(|signature: &Signature| {
526 signature
527 .verify_certification(&old_primary_key, pgp::types::Tag::UserId, &id)
528 .is_ok()
529 })
530 .max_by_key(|signature: &Signature| {
531 signature.created().map_or(0, |ts| ts.as_secs())
532 });
533 best_user_signature.map(|signature| (id, signature))
534 })
535 .max_by_key(|(_id, signature)| signature.created().map_or(0, |ts| ts.as_secs()))
536 .map(|(id, signature)| SignedUser {
537 id,
538 signatures: vec![signature],
539 });
540 let users: Vec<SignedUser> = best_user.into_iter().collect();
541
542 let public_subkeys = old_public_subkeys;
543
544 Ok(SignedPublicKey {
545 primary_key: old_primary_key,
546 details: SignedKeyDetails {
547 revocation_signatures: vec![],
548 direct_signatures,
549 users,
550 user_attributes: vec![],
551 },
552 public_subkeys,
553 })
554}
555
556pub(crate) fn addresses_from_public_key(public_key: &SignedPublicKey) -> Option<Vec<String>> {
560 for signature in &public_key.details.direct_signatures {
561 let signature_is_valid = signature.verify_key(&public_key.primary_key).is_ok();
564 debug_assert!(signature_is_valid);
565 if signature_is_valid {
566 for notation in signature.notations() {
567 if notation.name == "relays@chatmail.at"
568 && let Ok(value) = str::from_utf8(¬ation.value)
569 {
570 return Some(
571 value
572 .split(",")
573 .map(|s| s.to_string())
574 .filter(|s| may_be_valid_addr(s))
575 .take(3)
576 .collect(),
577 );
578 }
579 }
580 }
581 }
582 None
583}
584
585#[cfg(test)]
586mod tests {
587 use std::sync::LazyLock;
588 use tokio::sync::OnceCell;
589
590 use super::*;
591 use crate::{
592 config::Config,
593 decrypt,
594 key::{load_self_public_key, self_fingerprint, store_self_keypair},
595 mimefactory::{render_outer_message, wrap_encrypted_part},
596 test_utils::{TestContext, TestContextManager, alice_keypair, bob_keypair},
597 token,
598 };
599 use pgp::composed::Esk;
600 use pgp::packet::PublicKeyEncryptedSessionKey;
601
602 async fn decrypt_bytes(
603 bytes: Vec<u8>,
604 private_keys_for_decryption: &[SignedSecretKey],
605 auth_tokens_for_decryption: &[String],
606 ) -> Result<pgp::composed::Message<'static>> {
607 let t = &TestContext::new().await;
608 t.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
609 .await
610 .expect("Failed to configure address");
611
612 for secret in auth_tokens_for_decryption {
613 token::save(t, token::Namespace::Auth, None, secret, 0).await?;
614 }
615 let [secret_key] = private_keys_for_decryption else {
616 panic!("Only one private key is allowed anymore");
617 };
618 store_self_keypair(t, secret_key).await?;
619
620 let mime_message = wrap_encrypted_part(bytes.try_into().unwrap());
621 let rendered = render_outer_message(vec![], mime_message);
622 let parsed = mailparse::parse_mail(rendered.as_bytes())?;
623 let (decrypted, _fp) = decrypt::decrypt(t, &parsed).await?.unwrap();
624 Ok(decrypted)
625 }
626
627 async fn pk_decrypt_and_validate<'a>(
628 ctext: &'a [u8],
629 private_keys_for_decryption: &'a [SignedSecretKey],
630 public_keys_for_validation: &[SignedPublicKey],
631 ) -> Result<(
632 pgp::composed::Message<'static>,
633 HashMap<Fingerprint, Vec<Fingerprint>>,
634 Vec<u8>,
635 )> {
636 let mut msg = decrypt_bytes(ctext.to_vec(), private_keys_for_decryption, &[]).await?;
637 let content = msg.as_data_vec()?;
638 let ret_signature_fingerprints =
639 valid_signature_fingerprints(&msg, public_keys_for_validation);
640
641 Ok((msg, ret_signature_fingerprints, content))
642 }
643
644 #[test]
645 fn test_split_armored_data_1() {
646 let (typ, _headers, base64) = split_armored_data(
647 b"-----BEGIN PGP MESSAGE-----\nNoVal:\n\naGVsbG8gd29ybGQ=\n-----END PGP MESSAGE-----",
648 )
649 .unwrap();
650
651 assert_eq!(typ, BlockType::Message);
652 assert!(!base64.is_empty());
653 assert_eq!(
654 std::string::String::from_utf8(base64).unwrap(),
655 "hello world"
656 );
657 }
658
659 #[test]
660 fn test_split_armored_data_2() {
661 let (typ, headers, base64) = split_armored_data(
662 b"-----BEGIN PGP PRIVATE KEY BLOCK-----\nAutocrypt-Prefer-Encrypt: mutual \n\naGVsbG8gd29ybGQ=\n-----END PGP PRIVATE KEY BLOCK-----"
663 )
664 .unwrap();
665
666 assert_eq!(typ, BlockType::PrivateKey);
667 assert!(!base64.is_empty());
668 assert_eq!(headers.get(HEADER_AUTOCRYPT), Some(&"mutual".to_string()));
669 }
670
671 #[test]
672 fn test_create_keypair() {
673 let keypair0 = create_keypair(EmailAddress::new("foo@bar.de").unwrap()).unwrap();
674 let keypair1 = create_keypair(EmailAddress::new("two@zwo.de").unwrap()).unwrap();
675 assert_ne!(keypair0.public_key(), keypair1.public_key());
676 }
677
678 struct TestKeys {
681 alice_secret: SignedSecretKey,
682 alice_public: SignedPublicKey,
683 bob_secret: SignedSecretKey,
684 bob_public: SignedPublicKey,
685 }
686
687 impl TestKeys {
688 fn new() -> TestKeys {
689 let alice = alice_keypair();
690 let bob = bob_keypair();
691 TestKeys {
692 alice_secret: alice.clone(),
693 alice_public: alice.to_public_key(),
694 bob_secret: bob.clone(),
695 bob_public: bob.to_public_key(),
696 }
697 }
698 }
699
700 static CLEARTEXT: &[u8] = b"This is a test";
702
703 static KEYS: LazyLock<TestKeys> = LazyLock::new(TestKeys::new);
705
706 static CTEXT_SIGNED: OnceCell<String> = OnceCell::const_new();
707
708 async fn ctext_signed() -> &'static String {
710 let anonymous_recipients = true;
711 CTEXT_SIGNED
712 .get_or_init(|| async {
713 let keyring = vec![KEYS.alice_public.clone(), KEYS.bob_public.clone()];
714 let compress = true;
715
716 pk_encrypt(
717 CLEARTEXT.to_vec(),
718 keyring,
719 KEYS.alice_secret.clone(),
720 compress,
721 anonymous_recipients,
722 SeipdVersion::V2,
723 )
724 .await
725 .unwrap()
726 })
727 .await
728 }
729
730 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
731 async fn test_encrypt_signed() {
732 assert!(!ctext_signed().await.is_empty());
733 assert!(
734 ctext_signed()
735 .await
736 .starts_with("-----BEGIN PGP MESSAGE-----")
737 );
738 }
739
740 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
741 async fn test_decrypt_signed() {
742 let decrypt_keyring = vec![KEYS.alice_secret.clone()];
744 let sig_check_keyring = vec![KEYS.alice_public.clone()];
745 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
746 ctext_signed().await.as_bytes(),
747 &decrypt_keyring,
748 &sig_check_keyring,
749 )
750 .await
751 .unwrap();
752 assert_eq!(content, CLEARTEXT);
753 assert_eq!(valid_signatures.len(), 1);
754 for recipient_fps in valid_signatures.values() {
755 assert_eq!(recipient_fps.len(), 2);
756 }
757
758 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
760 let sig_check_keyring = vec![KEYS.alice_public.clone()];
761 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
762 ctext_signed().await.as_bytes(),
763 &decrypt_keyring,
764 &sig_check_keyring,
765 )
766 .await
767 .unwrap();
768 assert_eq!(content, CLEARTEXT);
769 assert_eq!(valid_signatures.len(), 1);
770 for recipient_fps in valid_signatures.values() {
771 assert_eq!(recipient_fps.len(), 2);
772 }
773 }
774
775 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
776 async fn test_decrypt_no_sig_check() {
777 let keyring = vec![KEYS.alice_secret.clone()];
778 let (_msg, valid_signatures, content) =
779 pk_decrypt_and_validate(ctext_signed().await.as_bytes(), &keyring, &[])
780 .await
781 .unwrap();
782 assert_eq!(content, CLEARTEXT);
783 assert_eq!(valid_signatures.len(), 0);
784 }
785
786 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
787 async fn test_decrypt_signed_no_key() {
788 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
790 let sig_check_keyring = vec![KEYS.bob_public.clone()];
791 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
792 ctext_signed().await.as_bytes(),
793 &decrypt_keyring,
794 &sig_check_keyring,
795 )
796 .await
797 .unwrap();
798 assert_eq!(content, CLEARTEXT);
799 assert_eq!(valid_signatures.len(), 0);
800 }
801
802 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
803 async fn test_decrypt_unsigned() {
804 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
805 let ctext_unsigned = include_bytes!("../test-data/message/ctext_unsigned.asc");
806 let (_msg, valid_signatures, content) =
807 pk_decrypt_and_validate(ctext_unsigned, &decrypt_keyring, &[])
808 .await
809 .unwrap();
810 assert_eq!(content, CLEARTEXT);
811 assert_eq!(valid_signatures.len(), 0);
812 }
813
814 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
815 async fn test_dont_decrypt_expensive_message_happy_path() -> Result<()> {
816 let s2k = StringToKey::Salted {
817 hash_alg: HashAlgorithm::default(),
818 salt: [1; 8],
819 };
820
821 test_dont_decrypt_expensive_message_ex(s2k, false, None).await
822 }
823
824 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
825 async fn test_dont_decrypt_expensive_message_bad_s2k() -> Result<()> {
826 let s2k = StringToKey::new_default(&mut thread_rng()); test_dont_decrypt_expensive_message_ex(s2k, false, Some("unsupported string2key algorithm"))
829 .await
830 }
831
832 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
833 async fn test_dont_decrypt_expensive_message_multiple_secrets() -> Result<()> {
834 let s2k = StringToKey::Salted {
835 hash_alg: HashAlgorithm::default(),
836 salt: [1; 8],
837 };
838
839 test_dont_decrypt_expensive_message_ex(s2k, true, Some("decrypt_with_keys: missing key"))
842 .await
843 }
844
845 async fn test_dont_decrypt_expensive_message_ex(
851 s2k: StringToKey,
852 encrypt_twice: bool,
853 expected_error_msg: Option<&str>,
854 ) -> Result<()> {
855 let mut tcm = TestContextManager::new();
856 let bob = &tcm.bob().await;
857
858 let plain = Vec::from(b"this is the secret message");
859 let shared_secret = "shared secret";
860 let bob_fp = self_fingerprint(bob).await?;
861
862 let shared_secret_pw = Password::from(format!("securejoin/{bob_fp}/{shared_secret}"));
863 let msg = MessageBuilder::from_bytes("", plain);
864 let mut rng = thread_rng();
865
866 let mut msg = msg.seipd_v2(
867 &mut rng,
868 SymmetricKeyAlgorithm::AES128,
869 AeadAlgorithm::Ocb,
870 ChunkSize::C8KiB,
871 );
872 msg.encrypt_with_password(&mut rng, s2k.clone(), &shared_secret_pw)?;
873 if encrypt_twice {
874 msg.encrypt_with_password(&mut rng, s2k, &shared_secret_pw)?;
875 }
876
877 let ctext = msg.to_armored_string(&mut rng, Default::default())?;
878
879 let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
882 let res = decrypt_bytes(
883 ctext.into(),
884 &bob_private_keyring,
885 &[shared_secret.to_string()],
886 )
887 .await;
888
889 if let Some(expected_error_msg) = expected_error_msg {
890 assert_eq!(format!("{:#}", res.unwrap_err()), expected_error_msg);
891 } else {
892 res.unwrap();
893 }
894
895 Ok(())
896 }
897
898 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
899 async fn test_decryption_error_msg() -> Result<()> {
900 let mut tcm = TestContextManager::new();
901 let alice = &tcm.alice().await;
902 let bob = &tcm.bob().await;
903
904 let plain = Vec::from(b"this is the secret message");
905 let pk_for_encryption = load_self_public_key(alice).await?;
906
907 let ctext = pk_encrypt(
909 plain,
910 vec![pk_for_encryption],
911 KEYS.alice_secret.clone(),
912 true,
913 true,
914 SeipdVersion::V2,
915 )
916 .await?;
917
918 let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
920 let error = decrypt_bytes(ctext.into(), &bob_private_keyring, &[])
921 .await
922 .unwrap_err();
923
924 assert_eq!(format!("{error:#}"), "decrypt_with_keys: missing key");
925
926 Ok(())
927 }
928
929 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
932 async fn test_anonymous_recipients() -> Result<()> {
933 let ctext = ctext_signed().await.as_bytes();
934 let cursor = Cursor::new(ctext);
935 let (msg, _headers) = Message::from_armor(cursor)?;
936
937 let Message::Encrypted { esk, .. } = msg else {
938 unreachable!();
939 };
940
941 for encrypted_session_key in esk {
942 let Esk::PublicKeyEncryptedSessionKey(pkesk) = encrypted_session_key else {
943 unreachable!()
944 };
945
946 match pkesk {
947 PublicKeyEncryptedSessionKey::V3 { id, .. } => {
948 assert!(id.is_wildcard());
949 }
950 PublicKeyEncryptedSessionKey::V6 { fingerprint, .. } => {
951 assert!(fingerprint.is_none());
952 }
953 PublicKeyEncryptedSessionKey::Other { .. } => unreachable!(),
954 }
955 }
956 Ok(())
957 }
958
959 #[test]
960 fn test_merge_openpgp_certificates() {
961 let alice = alice_keypair().to_public_key();
962 let bob = bob_keypair().to_public_key();
963
964 assert_eq!(
966 merge_openpgp_certificates(alice.clone(), alice.clone()).unwrap(),
967 alice
968 );
969 assert_eq!(
970 merge_openpgp_certificates(bob.clone(), bob.clone()).unwrap(),
971 bob
972 );
973
974 assert!(merge_openpgp_certificates(alice.clone(), bob.clone()).is_err());
976 assert!(merge_openpgp_certificates(bob.clone(), alice.clone()).is_err());
977 }
978}