1use std::collections::{BTreeMap, HashMap, HashSet};
4use std::io::{BufRead, Cursor};
5
6use anyhow::{Context as _, Result};
7use deltachat_contact_tools::EmailAddress;
8use pgp::armor::BlockType;
9use pgp::composed::{
10 ArmorOptions, Deserializable, DetachedSignature, EncryptionCaps, KeyType as PgpKeyType,
11 Message, MessageBuilder, SecretKeyParamsBuilder, SignedPublicKey, SignedPublicSubKey,
12 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::{SignatureConfig, SignatureType, Subpacket, SubpacketData};
19use pgp::types::{
20 CompressionAlgorithm, KeyDetails, KeyVersion, Password, SigningKey as _, StringToKey,
21};
22use rand_old::{Rng as _, thread_rng};
23use tokio::runtime::Handle;
24
25use crate::key::{DcKey, Fingerprint};
26
27#[cfg(test)]
28pub(crate) const HEADER_AUTOCRYPT: &str = "autocrypt-prefer-encrypt";
29
30pub(crate) const HEADER_SETUPCODE: &str = "passphrase-begin";
31
32const SYMMETRIC_KEY_ALGORITHM: SymmetricKeyAlgorithm = SymmetricKeyAlgorithm::AES128;
34
35pub fn split_armored_data(buf: &[u8]) -> Result<(BlockType, BTreeMap<String, String>, Vec<u8>)> {
39 use std::io::Read;
40
41 let cursor = Cursor::new(buf);
42 let mut dearmor = pgp::armor::Dearmor::new(cursor);
43
44 let mut bytes = Vec::with_capacity(buf.len());
45
46 dearmor.read_to_end(&mut bytes)?;
47 let typ = dearmor.typ.context("failed to parse type")?;
48
49 let headers = dearmor
51 .headers
52 .into_iter()
53 .map(|(key, values)| {
54 (
55 key.trim().to_lowercase(),
56 values
57 .last()
58 .map_or_else(String::new, |s| s.trim().to_string()),
59 )
60 })
61 .collect();
62
63 Ok((typ, headers, bytes))
64}
65
66pub(crate) fn create_keypair(addr: EmailAddress) -> Result<SignedSecretKey> {
71 let signing_key_type = PgpKeyType::Ed25519Legacy;
72 let encryption_key_type = PgpKeyType::ECDH(ECCCurve::Curve25519);
73
74 let user_id = format!("<{addr}>");
75 let key_params = SecretKeyParamsBuilder::default()
76 .key_type(signing_key_type)
77 .can_certify(true)
78 .can_sign(true)
79 .feature_seipd_v2(true)
80 .primary_user_id(user_id)
81 .passphrase(None)
82 .preferred_symmetric_algorithms(smallvec![
83 SymmetricKeyAlgorithm::AES256,
84 SymmetricKeyAlgorithm::AES192,
85 SymmetricKeyAlgorithm::AES128,
86 ])
87 .preferred_hash_algorithms(smallvec![
88 HashAlgorithm::Sha256,
89 HashAlgorithm::Sha384,
90 HashAlgorithm::Sha512,
91 HashAlgorithm::Sha224,
92 ])
93 .preferred_compression_algorithms(smallvec![
94 CompressionAlgorithm::ZLIB,
95 CompressionAlgorithm::ZIP,
96 ])
97 .subkey(
98 SubkeyParamsBuilder::default()
99 .key_type(encryption_key_type)
100 .can_encrypt(EncryptionCaps::All)
101 .passphrase(None)
102 .build()
103 .context("failed to build subkey parameters")?,
104 )
105 .build()
106 .context("failed to build key parameters")?;
107
108 let mut rng = thread_rng();
109 let secret_key = key_params
110 .generate(&mut rng)
111 .context("Failed to generate the key")?;
112 secret_key
113 .verify_bindings()
114 .context("Invalid secret key generated")?;
115
116 Ok(secret_key)
117}
118
119fn select_pk_for_encryption(key: &SignedPublicKey) -> Option<&SignedPublicSubKey> {
125 key.public_subkeys
126 .iter()
127 .find(|subkey| subkey.algorithm().can_encrypt())
128}
129
130#[derive(Debug)]
136pub enum SeipdVersion {
137 V1,
139
140 V2,
142}
143
144#[expect(clippy::arithmetic_side_effects)]
147pub async fn pk_encrypt(
148 plain: Vec<u8>,
149 public_keys_for_encryption: Vec<SignedPublicKey>,
150 private_key_for_signing: SignedSecretKey,
151 compress: bool,
152 anonymous_recipients: bool,
153 seipd_version: SeipdVersion,
154) -> Result<String> {
155 Handle::current()
156 .spawn_blocking(move || {
157 let mut rng = thread_rng();
158
159 let pkeys = public_keys_for_encryption
160 .iter()
161 .filter_map(select_pk_for_encryption);
162 let subpkts = {
163 let mut hashed = Vec::with_capacity(1 + public_keys_for_encryption.len() + 1);
164 hashed.push(Subpacket::critical(SubpacketData::SignatureCreationTime(
165 pgp::types::Timestamp::now(),
166 ))?);
167 let skip = private_key_for_signing.dc_fingerprint().hex()
169 == "B86586B6DEF437D674BFAFC02A6B2EBC633B9E82";
170 for key in &public_keys_for_encryption {
171 if skip {
172 break;
173 }
174 let data = SubpacketData::IntendedRecipientFingerprint(key.fingerprint());
175 let subpkt = match private_key_for_signing.version() < KeyVersion::V6 {
176 true => Subpacket::regular(data)?,
177 false => Subpacket::critical(data)?,
178 };
179 hashed.push(subpkt);
180 }
181 hashed.push(Subpacket::regular(SubpacketData::IssuerFingerprint(
182 private_key_for_signing.fingerprint(),
183 ))?);
184 let mut unhashed = vec![];
185 if private_key_for_signing.version() <= KeyVersion::V4 {
186 unhashed.push(Subpacket::regular(SubpacketData::IssuerKeyId(
187 private_key_for_signing.legacy_key_id(),
188 ))?);
189 }
190 SubpacketConfig::UserDefined { hashed, unhashed }
191 };
192
193 let msg = MessageBuilder::from_bytes("", plain);
194 let encoded_msg = match seipd_version {
195 SeipdVersion::V1 => {
196 let mut msg = msg.seipd_v1(&mut rng, SYMMETRIC_KEY_ALGORITHM);
197
198 for pkey in pkeys {
199 if anonymous_recipients {
200 msg.encrypt_to_key_anonymous(&mut rng, &pkey)?;
201 } else {
202 msg.encrypt_to_key(&mut rng, &pkey)?;
203 }
204 }
205
206 let hash_algorithm = private_key_for_signing.hash_alg();
207 msg.sign_with_subpackets(
208 &*private_key_for_signing,
209 Password::empty(),
210 hash_algorithm,
211 subpkts,
212 );
213 if compress {
214 msg.compression(CompressionAlgorithm::ZLIB);
215 }
216
217 msg.to_armored_string(&mut rng, Default::default())?
218 }
219 SeipdVersion::V2 => {
220 let mut msg = msg.seipd_v2(
221 &mut rng,
222 SYMMETRIC_KEY_ALGORITHM,
223 AeadAlgorithm::Ocb,
224 ChunkSize::C8KiB,
225 );
226
227 for pkey in pkeys {
228 if anonymous_recipients {
229 msg.encrypt_to_key_anonymous(&mut rng, &pkey)?;
230 } else {
231 msg.encrypt_to_key(&mut rng, &pkey)?;
232 }
233 }
234
235 let hash_algorithm = private_key_for_signing.hash_alg();
236 msg.sign_with_subpackets(
237 &*private_key_for_signing,
238 Password::empty(),
239 hash_algorithm,
240 subpkts,
241 );
242 if compress {
243 msg.compression(CompressionAlgorithm::ZLIB);
244 }
245
246 msg.to_armored_string(&mut rng, Default::default())?
247 }
248 };
249
250 Ok(encoded_msg)
251 })
252 .await?
253}
254
255pub fn pk_calc_signature(
257 plain: Vec<u8>,
258 private_key_for_signing: &SignedSecretKey,
259) -> Result<String> {
260 let rng = thread_rng();
261
262 let mut config = SignatureConfig::from_key(
263 rng,
264 &private_key_for_signing.primary_key,
265 SignatureType::Binary,
266 )?;
267
268 config.hashed_subpackets = vec![
269 Subpacket::regular(SubpacketData::IssuerFingerprint(
270 private_key_for_signing.fingerprint(),
271 ))?,
272 Subpacket::critical(SubpacketData::SignatureCreationTime(
273 pgp::types::Timestamp::now(),
274 ))?,
275 ];
276 config.unhashed_subpackets = vec![];
277 if private_key_for_signing.version() <= KeyVersion::V4 {
278 config
279 .unhashed_subpackets
280 .push(Subpacket::regular(SubpacketData::IssuerKeyId(
281 private_key_for_signing.legacy_key_id(),
282 ))?);
283 }
284
285 let signature = config.sign(
286 &private_key_for_signing.primary_key,
287 &Password::empty(),
288 plain.as_slice(),
289 )?;
290
291 let sig = DetachedSignature::new(signature);
292
293 Ok(sig.to_armored_string(ArmorOptions::default())?)
294}
295
296pub fn valid_signature_fingerprints(
303 msg: &pgp::composed::Message,
304 public_keys_for_validation: &[SignedPublicKey],
305) -> HashMap<Fingerprint, Vec<Fingerprint>> {
306 let mut ret_signature_fingerprints = HashMap::new();
307 if msg.is_signed() {
308 for pkey in public_keys_for_validation {
309 if let Ok(signature) = msg.verify(&pkey.primary_key) {
310 let fp = pkey.dc_fingerprint();
311 let mut recipient_fps = Vec::new();
312 if let Some(cfg) = signature.config() {
313 for subpkt in &cfg.hashed_subpackets {
314 if let SubpacketData::IntendedRecipientFingerprint(fp) = &subpkt.data {
315 recipient_fps.push(fp.clone().into());
316 }
317 }
318 }
319 ret_signature_fingerprints.insert(fp, recipient_fps);
320 }
321 }
322 }
323 ret_signature_fingerprints
324}
325
326pub fn pk_validate(
328 content: &[u8],
329 signature: &[u8],
330 public_keys_for_validation: &[SignedPublicKey],
331) -> Result<HashSet<Fingerprint>> {
332 let mut ret: HashSet<Fingerprint> = Default::default();
333
334 let detached_signature = DetachedSignature::from_armor_single(Cursor::new(signature))?.0;
335
336 for pkey in public_keys_for_validation {
337 if detached_signature.verify(pkey, content).is_ok() {
338 let fp = pkey.dc_fingerprint();
339 ret.insert(fp);
340 }
341 }
342 Ok(ret)
343}
344
345pub async fn symm_encrypt_autocrypt_setup(passphrase: &str, plain: Vec<u8>) -> Result<String> {
347 let passphrase = Password::from(passphrase.to_string());
348
349 tokio::task::spawn_blocking(move || {
350 let mut rng = thread_rng();
351 let s2k = StringToKey::new_default(&mut rng);
352 let builder = MessageBuilder::from_bytes("", plain);
353 let mut builder = builder.seipd_v1(&mut rng, SYMMETRIC_KEY_ALGORITHM);
354 builder.encrypt_with_password(s2k, &passphrase)?;
355
356 let encoded_msg = builder.to_armored_string(&mut rng, Default::default())?;
357
358 Ok(encoded_msg)
359 })
360 .await?
361}
362
363pub async fn symm_encrypt_message(
367 plain: Vec<u8>,
368 private_key_for_signing: Option<SignedSecretKey>,
369 shared_secret: &str,
370 compress: bool,
371) -> Result<String> {
372 let shared_secret = Password::from(shared_secret.to_string());
373
374 tokio::task::spawn_blocking(move || {
375 let msg = MessageBuilder::from_bytes("", plain);
376 let mut rng = thread_rng();
377 let mut salt = [0u8; 8];
378 rng.fill(&mut salt[..]);
379 let s2k = StringToKey::Salted {
380 hash_alg: HashAlgorithm::default(),
381 salt,
382 };
383 let mut msg = msg.seipd_v2(
384 &mut rng,
385 SYMMETRIC_KEY_ALGORITHM,
386 AeadAlgorithm::Ocb,
387 ChunkSize::C8KiB,
388 );
389 msg.encrypt_with_password(&mut rng, s2k, &shared_secret)?;
390
391 if let Some(private_key_for_signing) = private_key_for_signing.as_deref() {
392 let hash_algorithm = private_key_for_signing.hash_alg();
393 msg.sign(private_key_for_signing, Password::empty(), hash_algorithm);
394 }
395 if compress {
396 msg.compression(CompressionAlgorithm::ZLIB);
397 }
398
399 let encoded_msg = msg.to_armored_string(&mut rng, Default::default())?;
400
401 Ok(encoded_msg)
402 })
403 .await?
404}
405
406pub async fn symm_decrypt<T: BufRead + std::fmt::Debug + 'static + Send>(
408 passphrase: &str,
409 ctext: T,
410) -> Result<Vec<u8>> {
411 let passphrase = passphrase.to_string();
412 tokio::task::spawn_blocking(move || {
413 let (enc_msg, _) = Message::from_armor(ctext)?;
414 let password = Password::from(passphrase);
415
416 let msg = enc_msg.decrypt_with_password(&password)?;
417 let res = msg.decompress()?.as_data_vec()?;
418 Ok(res)
419 })
420 .await?
421}
422
423#[cfg(test)]
424mod tests {
425 use std::sync::LazyLock;
426 use tokio::sync::OnceCell;
427
428 use super::*;
429 use crate::{
430 decrypt,
431 key::{load_self_public_key, load_self_secret_key, store_self_keypair},
432 mimefactory::{render_outer_message, wrap_encrypted_part},
433 test_utils::{TestContext, TestContextManager, alice_keypair, bob_keypair},
434 token,
435 };
436 use pgp::composed::Esk;
437 use pgp::packet::PublicKeyEncryptedSessionKey;
438
439 async fn decrypt_bytes(
440 bytes: Vec<u8>,
441 private_keys_for_decryption: &[SignedSecretKey],
442 shared_secrets: &[String],
443 ) -> Result<pgp::composed::Message<'static>> {
444 let t = &TestContext::new().await;
445
446 for secret in shared_secrets {
447 token::save(t, token::Namespace::Auth, None, secret, 0).await?;
448 }
449 let [secret_key] = private_keys_for_decryption else {
450 panic!("Only one private key is allowed anymore");
451 };
452 store_self_keypair(t, secret_key).await?;
453
454 let mime_message = wrap_encrypted_part(bytes.try_into().unwrap());
455 let rendered = render_outer_message(vec![], mime_message);
456 let parsed = mailparse::parse_mail(rendered.as_bytes())?;
457 let (decrypted, _fp) = decrypt::decrypt(t, &parsed).await?.unwrap();
458 Ok(decrypted)
459 }
460
461 async fn pk_decrypt_and_validate<'a>(
462 ctext: &'a [u8],
463 private_keys_for_decryption: &'a [SignedSecretKey],
464 public_keys_for_validation: &[SignedPublicKey],
465 ) -> Result<(
466 pgp::composed::Message<'static>,
467 HashMap<Fingerprint, Vec<Fingerprint>>,
468 Vec<u8>,
469 )> {
470 let mut msg = decrypt_bytes(ctext.to_vec(), private_keys_for_decryption, &[]).await?;
471 let content = msg.as_data_vec()?;
472 let ret_signature_fingerprints =
473 valid_signature_fingerprints(&msg, public_keys_for_validation);
474
475 Ok((msg, ret_signature_fingerprints, content))
476 }
477
478 #[test]
479 fn test_split_armored_data_1() {
480 let (typ, _headers, base64) = split_armored_data(
481 b"-----BEGIN PGP MESSAGE-----\nNoVal:\n\naGVsbG8gd29ybGQ=\n-----END PGP MESSAGE-----",
482 )
483 .unwrap();
484
485 assert_eq!(typ, BlockType::Message);
486 assert!(!base64.is_empty());
487 assert_eq!(
488 std::string::String::from_utf8(base64).unwrap(),
489 "hello world"
490 );
491 }
492
493 #[test]
494 fn test_split_armored_data_2() {
495 let (typ, headers, base64) = split_armored_data(
496 b"-----BEGIN PGP PRIVATE KEY BLOCK-----\nAutocrypt-Prefer-Encrypt: mutual \n\naGVsbG8gd29ybGQ=\n-----END PGP PRIVATE KEY BLOCK-----"
497 )
498 .unwrap();
499
500 assert_eq!(typ, BlockType::PrivateKey);
501 assert!(!base64.is_empty());
502 assert_eq!(headers.get(HEADER_AUTOCRYPT), Some(&"mutual".to_string()));
503 }
504
505 #[test]
506 fn test_create_keypair() {
507 let keypair0 = create_keypair(EmailAddress::new("foo@bar.de").unwrap()).unwrap();
508 let keypair1 = create_keypair(EmailAddress::new("two@zwo.de").unwrap()).unwrap();
509 assert_ne!(keypair0.public_key(), keypair1.public_key());
510 }
511
512 struct TestKeys {
515 alice_secret: SignedSecretKey,
516 alice_public: SignedPublicKey,
517 bob_secret: SignedSecretKey,
518 bob_public: SignedPublicKey,
519 }
520
521 impl TestKeys {
522 fn new() -> TestKeys {
523 let alice = alice_keypair();
524 let bob = bob_keypair();
525 TestKeys {
526 alice_secret: alice.clone(),
527 alice_public: alice.to_public_key(),
528 bob_secret: bob.clone(),
529 bob_public: bob.to_public_key(),
530 }
531 }
532 }
533
534 static CLEARTEXT: &[u8] = b"This is a test";
536
537 static KEYS: LazyLock<TestKeys> = LazyLock::new(TestKeys::new);
539
540 static CTEXT_SIGNED: OnceCell<String> = OnceCell::const_new();
541
542 async fn ctext_signed() -> &'static String {
544 let anonymous_recipients = true;
545 CTEXT_SIGNED
546 .get_or_init(|| async {
547 let keyring = vec![KEYS.alice_public.clone(), KEYS.bob_public.clone()];
548 let compress = true;
549
550 pk_encrypt(
551 CLEARTEXT.to_vec(),
552 keyring,
553 KEYS.alice_secret.clone(),
554 compress,
555 anonymous_recipients,
556 SeipdVersion::V2,
557 )
558 .await
559 .unwrap()
560 })
561 .await
562 }
563
564 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
565 async fn test_encrypt_signed() {
566 assert!(!ctext_signed().await.is_empty());
567 assert!(
568 ctext_signed()
569 .await
570 .starts_with("-----BEGIN PGP MESSAGE-----")
571 );
572 }
573
574 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
575 async fn test_decrypt_signed() {
576 let decrypt_keyring = vec![KEYS.alice_secret.clone()];
578 let sig_check_keyring = vec![KEYS.alice_public.clone()];
579 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
580 ctext_signed().await.as_bytes(),
581 &decrypt_keyring,
582 &sig_check_keyring,
583 )
584 .await
585 .unwrap();
586 assert_eq!(content, CLEARTEXT);
587 assert_eq!(valid_signatures.len(), 1);
588 for recipient_fps in valid_signatures.values() {
589 assert_eq!(recipient_fps.len(), 2);
590 }
591
592 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
594 let sig_check_keyring = vec![KEYS.alice_public.clone()];
595 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
596 ctext_signed().await.as_bytes(),
597 &decrypt_keyring,
598 &sig_check_keyring,
599 )
600 .await
601 .unwrap();
602 assert_eq!(content, CLEARTEXT);
603 assert_eq!(valid_signatures.len(), 1);
604 for recipient_fps in valid_signatures.values() {
605 assert_eq!(recipient_fps.len(), 2);
606 }
607 }
608
609 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
610 async fn test_decrypt_no_sig_check() {
611 let keyring = vec![KEYS.alice_secret.clone()];
612 let (_msg, valid_signatures, content) =
613 pk_decrypt_and_validate(ctext_signed().await.as_bytes(), &keyring, &[])
614 .await
615 .unwrap();
616 assert_eq!(content, CLEARTEXT);
617 assert_eq!(valid_signatures.len(), 0);
618 }
619
620 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
621 async fn test_decrypt_signed_no_key() {
622 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
624 let sig_check_keyring = vec![KEYS.bob_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(), 0);
634 }
635
636 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
637 async fn test_decrypt_unsigned() {
638 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
639 let ctext_unsigned = include_bytes!("../test-data/message/ctext_unsigned.asc");
640 let (_msg, valid_signatures, content) =
641 pk_decrypt_and_validate(ctext_unsigned, &decrypt_keyring, &[])
642 .await
643 .unwrap();
644 assert_eq!(content, CLEARTEXT);
645 assert_eq!(valid_signatures.len(), 0);
646 }
647
648 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
649 async fn test_encrypt_decrypt_broadcast() -> Result<()> {
650 let mut tcm = TestContextManager::new();
651 let alice = &tcm.alice().await;
652 let bob = &tcm.bob().await;
653
654 let plain = Vec::from(b"this is the secret message");
655 let shared_secret = "shared secret";
656 let ctext = symm_encrypt_message(
657 plain.clone(),
658 Some(load_self_secret_key(alice).await?),
659 shared_secret,
660 true,
661 )
662 .await?;
663
664 let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
665 let mut decrypted = decrypt_bytes(
666 ctext.into(),
667 &bob_private_keyring,
668 &[shared_secret.to_string()],
669 )
670 .await
671 .unwrap();
672
673 assert_eq!(decrypted.as_data_vec()?, plain);
674
675 Ok(())
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_with_keys: 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
725 let shared_secret_pw = Password::from(shared_secret.to_string());
726 let msg = MessageBuilder::from_bytes("", plain);
727 let mut rng = thread_rng();
728
729 let mut msg = msg.seipd_v2(
730 &mut rng,
731 SymmetricKeyAlgorithm::AES128,
732 AeadAlgorithm::Ocb,
733 ChunkSize::C8KiB,
734 );
735 msg.encrypt_with_password(&mut rng, s2k.clone(), &shared_secret_pw)?;
736 if encrypt_twice {
737 msg.encrypt_with_password(&mut rng, s2k, &shared_secret_pw)?;
738 }
739
740 let ctext = msg.to_armored_string(&mut rng, Default::default())?;
741
742 let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
745 let res = decrypt_bytes(
746 ctext.into(),
747 &bob_private_keyring,
748 &[shared_secret.to_string()],
749 )
750 .await;
751
752 if let Some(expected_error_msg) = expected_error_msg {
753 assert_eq!(format!("{:#}", res.unwrap_err()), expected_error_msg);
754 } else {
755 res.unwrap();
756 }
757
758 Ok(())
759 }
760
761 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
762 async fn test_decryption_error_msg() -> Result<()> {
763 let mut tcm = TestContextManager::new();
764 let alice = &tcm.alice().await;
765 let bob = &tcm.bob().await;
766
767 let plain = Vec::from(b"this is the secret message");
768 let pk_for_encryption = load_self_public_key(alice).await?;
769
770 let ctext = pk_encrypt(
772 plain,
773 vec![pk_for_encryption],
774 KEYS.alice_secret.clone(),
775 true,
776 true,
777 SeipdVersion::V2,
778 )
779 .await?;
780
781 let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
783 let error = decrypt_bytes(ctext.into(), &bob_private_keyring, &[])
784 .await
785 .unwrap_err();
786
787 assert_eq!(format!("{error:#}"), "decrypt_with_keys: missing key");
788
789 Ok(())
790 }
791
792 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
795 async fn test_anonymous_recipients() -> Result<()> {
796 let ctext = ctext_signed().await.as_bytes();
797 let cursor = Cursor::new(ctext);
798 let (msg, _headers) = Message::from_armor(cursor)?;
799
800 let Message::Encrypted { esk, .. } = msg else {
801 unreachable!();
802 };
803
804 for encrypted_session_key in esk {
805 let Esk::PublicKeyEncryptedSessionKey(pkesk) = encrypted_session_key else {
806 unreachable!()
807 };
808
809 match pkesk {
810 PublicKeyEncryptedSessionKey::V3 { id, .. } => {
811 assert!(id.is_wildcard());
812 }
813 PublicKeyEncryptedSessionKey::V6 { fingerprint, .. } => {
814 assert!(fingerprint.is_none());
815 }
816 PublicKeyEncryptedSessionKey::Other { .. } => unreachable!(),
817 }
818 }
819 Ok(())
820 }
821}