1use std::collections::{BTreeMap, HashMap, HashSet};
4use std::io::{BufRead, Cursor};
5
6use anyhow::{Context as _, Result, bail};
7use chrono::SubsecRound;
8use deltachat_contact_tools::EmailAddress;
9use pgp::armor::BlockType;
10use pgp::composed::{
11 ArmorOptions, DecryptionOptions, Deserializable, DetachedSignature, KeyType as PgpKeyType,
12 Message, MessageBuilder, SecretKeyParamsBuilder, SignedPublicKey, SignedPublicSubKey,
13 SignedSecretKey, SubkeyParamsBuilder, SubpacketConfig, TheRing,
14};
15use pgp::crypto::aead::{AeadAlgorithm, ChunkSize};
16use pgp::crypto::ecc_curve::ECCCurve;
17use pgp::crypto::hash::HashAlgorithm;
18use pgp::crypto::sym::SymmetricKeyAlgorithm;
19use pgp::packet::{SignatureConfig, SignatureType, Subpacket, SubpacketData};
20use pgp::types::{
21 CompressionAlgorithm, KeyDetails, KeyVersion, Password, PublicKeyTrait, SecretKeyTrait as _,
22 StringToKey,
23};
24use rand_old::{Rng as _, thread_rng};
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
68#[derive(Debug, Clone, Eq, PartialEq)]
73pub struct KeyPair {
74 pub public: SignedPublicKey,
76
77 pub secret: SignedSecretKey,
79}
80
81impl KeyPair {
82 pub fn new(secret: SignedSecretKey) -> Result<Self> {
86 use crate::key::DcSecretKey;
87
88 let public = secret.split_public_key()?;
89 Ok(Self { public, secret })
90 }
91}
92
93pub(crate) fn create_keypair(addr: EmailAddress) -> Result<KeyPair> {
98 let signing_key_type = PgpKeyType::Ed25519Legacy;
99 let encryption_key_type = PgpKeyType::ECDH(ECCCurve::Curve25519);
100
101 let user_id = format!("<{addr}>");
102 let key_params = SecretKeyParamsBuilder::default()
103 .key_type(signing_key_type)
104 .can_certify(true)
105 .can_sign(true)
106 .primary_user_id(user_id)
107 .passphrase(None)
108 .preferred_symmetric_algorithms(smallvec![
109 SymmetricKeyAlgorithm::AES256,
110 SymmetricKeyAlgorithm::AES192,
111 SymmetricKeyAlgorithm::AES128,
112 ])
113 .preferred_hash_algorithms(smallvec![
114 HashAlgorithm::Sha256,
115 HashAlgorithm::Sha384,
116 HashAlgorithm::Sha512,
117 HashAlgorithm::Sha224,
118 ])
119 .preferred_compression_algorithms(smallvec![
120 CompressionAlgorithm::ZLIB,
121 CompressionAlgorithm::ZIP,
122 ])
123 .subkey(
124 SubkeyParamsBuilder::default()
125 .key_type(encryption_key_type)
126 .can_encrypt(true)
127 .passphrase(None)
128 .build()
129 .context("failed to build subkey parameters")?,
130 )
131 .build()
132 .context("failed to build key parameters")?;
133
134 let mut rng = thread_rng();
135 let secret_key = key_params
136 .generate(&mut rng)
137 .context("failed to generate the key")?
138 .sign(&mut rng, &Password::empty())
139 .context("failed to sign secret key")?;
140 secret_key
141 .verify()
142 .context("invalid secret key generated")?;
143
144 let key_pair = KeyPair::new(secret_key)?;
145 key_pair
146 .public
147 .verify()
148 .context("invalid public key generated")?;
149 Ok(key_pair)
150}
151
152fn select_pk_for_encryption(key: &SignedPublicKey) -> Option<&SignedPublicSubKey> {
158 key.public_subkeys
159 .iter()
160 .find(|subkey| subkey.is_encryption_key())
161}
162
163#[derive(Debug)]
169pub enum SeipdVersion {
170 V1,
172
173 V2,
175}
176
177pub async fn pk_encrypt(
180 plain: Vec<u8>,
181 public_keys_for_encryption: Vec<SignedPublicKey>,
182 private_key_for_signing: SignedSecretKey,
183 compress: bool,
184 anonymous_recipients: bool,
185 seipd_version: SeipdVersion,
186) -> Result<String> {
187 Handle::current()
188 .spawn_blocking(move || {
189 let mut rng = thread_rng();
190
191 let pkeys = public_keys_for_encryption
192 .iter()
193 .filter_map(select_pk_for_encryption);
194 let subpkts = {
195 let mut hashed = Vec::with_capacity(1 + public_keys_for_encryption.len() + 1);
196 hashed.push(Subpacket::critical(SubpacketData::SignatureCreationTime(
197 chrono::Utc::now().trunc_subsecs(0),
198 ))?);
199 let skip = private_key_for_signing.dc_fingerprint().hex()
201 == "B86586B6DEF437D674BFAFC02A6B2EBC633B9E82";
202 for key in &public_keys_for_encryption {
203 if skip {
204 break;
205 }
206 let data = SubpacketData::IntendedRecipientFingerprint(key.fingerprint());
207 let subpkt = match private_key_for_signing.version() < KeyVersion::V6 {
208 true => Subpacket::regular(data)?,
209 false => Subpacket::critical(data)?,
210 };
211 hashed.push(subpkt);
212 }
213 hashed.push(Subpacket::regular(SubpacketData::IssuerFingerprint(
214 private_key_for_signing.fingerprint(),
215 ))?);
216 let mut unhashed = vec![];
217 if private_key_for_signing.version() <= KeyVersion::V4 {
218 unhashed.push(Subpacket::regular(SubpacketData::Issuer(
219 private_key_for_signing.key_id(),
220 ))?);
221 }
222 SubpacketConfig::UserDefined { hashed, unhashed }
223 };
224
225 let msg = MessageBuilder::from_bytes("", plain);
226 let encoded_msg = match seipd_version {
227 SeipdVersion::V1 => {
228 let mut msg = msg.seipd_v1(&mut rng, SYMMETRIC_KEY_ALGORITHM);
229
230 for pkey in pkeys {
231 if anonymous_recipients {
232 msg.encrypt_to_key_anonymous(&mut rng, &pkey)?;
233 } else {
234 msg.encrypt_to_key(&mut rng, &pkey)?;
235 }
236 }
237
238 let hash_algorithm = private_key_for_signing.hash_alg();
239 msg.sign_with_subpackets(
240 &*private_key_for_signing,
241 Password::empty(),
242 hash_algorithm,
243 subpkts,
244 );
245 if compress {
246 msg.compression(CompressionAlgorithm::ZLIB);
247 }
248
249 msg.to_armored_string(&mut rng, Default::default())?
250 }
251 SeipdVersion::V2 => {
252 let mut msg = msg.seipd_v2(
253 &mut rng,
254 SYMMETRIC_KEY_ALGORITHM,
255 AeadAlgorithm::Ocb,
256 ChunkSize::C8KiB,
257 );
258
259 for pkey in pkeys {
260 if anonymous_recipients {
261 msg.encrypt_to_key_anonymous(&mut rng, &pkey)?;
262 } else {
263 msg.encrypt_to_key(&mut rng, &pkey)?;
264 }
265 }
266
267 let hash_algorithm = private_key_for_signing.hash_alg();
268 msg.sign_with_subpackets(
269 &*private_key_for_signing,
270 Password::empty(),
271 hash_algorithm,
272 subpkts,
273 );
274 if compress {
275 msg.compression(CompressionAlgorithm::ZLIB);
276 }
277
278 msg.to_armored_string(&mut rng, Default::default())?
279 }
280 };
281
282 Ok(encoded_msg)
283 })
284 .await?
285}
286
287pub fn pk_calc_signature(
289 plain: Vec<u8>,
290 private_key_for_signing: &SignedSecretKey,
291) -> Result<String> {
292 let rng = thread_rng();
293
294 let mut config = SignatureConfig::from_key(
295 rng,
296 &private_key_for_signing.primary_key,
297 SignatureType::Binary,
298 )?;
299
300 config.hashed_subpackets = vec![
301 Subpacket::regular(SubpacketData::IssuerFingerprint(
302 private_key_for_signing.fingerprint(),
303 ))?,
304 Subpacket::critical(SubpacketData::SignatureCreationTime(
305 chrono::Utc::now().trunc_subsecs(0),
306 ))?,
307 ];
308 config.unhashed_subpackets = vec![];
309 if private_key_for_signing.version() <= KeyVersion::V4 {
310 config
311 .unhashed_subpackets
312 .push(Subpacket::regular(SubpacketData::Issuer(
313 private_key_for_signing.key_id(),
314 ))?);
315 }
316
317 let signature = config.sign(
318 &private_key_for_signing.primary_key,
319 &Password::empty(),
320 plain.as_slice(),
321 )?;
322
323 let sig = DetachedSignature::new(signature);
324
325 Ok(sig.to_armored_string(ArmorOptions::default())?)
326}
327
328pub fn decrypt(
336 ctext: Vec<u8>,
337 private_keys_for_decryption: &[SignedSecretKey],
338 mut shared_secrets: &[String],
339) -> Result<pgp::composed::Message<'static>> {
340 let cursor = Cursor::new(ctext);
341 let (msg, _headers) = Message::from_armor(cursor)?;
342
343 let skeys: Vec<&SignedSecretKey> = private_keys_for_decryption.iter().collect();
344 let empty_pw = Password::empty();
345
346 let decrypt_options = DecryptionOptions::new();
347 let symmetric_encryption_res = check_symmetric_encryption(&msg);
348 if symmetric_encryption_res.is_err() {
349 shared_secrets = &[];
350 }
351
352 let message_password: Vec<Password> = shared_secrets
357 .iter()
358 .map(|p| Password::from(p.as_str()))
359 .collect();
360 let message_password: Vec<&Password> = message_password.iter().collect();
361
362 let ring = TheRing {
363 secret_keys: skeys,
364 key_passwords: vec![&empty_pw],
365 message_password,
366 session_keys: vec![],
367 decrypt_options,
368 };
369
370 let res = msg.decrypt_the_ring(ring, true);
371
372 let (msg, _ring_result) = match res {
373 Ok(it) => it,
374 Err(err) => {
375 if let Err(reason) = symmetric_encryption_res {
376 bail!("{err:#} (Note: symmetric decryption was not tried: {reason})")
377 } else {
378 bail!("{err:#}");
379 }
380 }
381 };
382
383 let msg = msg.decompress()?;
385
386 Ok(msg)
387}
388
389fn check_symmetric_encryption(msg: &Message<'_>) -> std::result::Result<(), &'static str> {
399 let Message::Encrypted { esk, .. } = msg else {
400 return Err("not encrypted");
401 };
402
403 if esk.len() > 1 {
404 return Err("too many esks");
405 }
406
407 let [pgp::composed::Esk::SymKeyEncryptedSessionKey(esk)] = &esk[..] else {
408 return Err("not symmetrically encrypted");
409 };
410
411 match esk.s2k() {
412 Some(StringToKey::Salted { .. }) => Ok(()),
413 _ => Err("unsupported string2key algorithm"),
414 }
415}
416
417pub fn valid_signature_fingerprints(
424 msg: &pgp::composed::Message,
425 public_keys_for_validation: &[SignedPublicKey],
426) -> HashMap<Fingerprint, Vec<Fingerprint>> {
427 let mut ret_signature_fingerprints = HashMap::new();
428 if msg.is_signed() {
429 for pkey in public_keys_for_validation {
430 if let Ok(signature) = msg.verify(&pkey.primary_key) {
431 let fp = pkey.dc_fingerprint();
432 let mut recipient_fps = Vec::new();
433 if let Some(cfg) = signature.config() {
434 for subpkt in &cfg.hashed_subpackets {
435 if let SubpacketData::IntendedRecipientFingerprint(fp) = &subpkt.data {
436 recipient_fps.push(fp.clone().into());
437 }
438 }
439 }
440 ret_signature_fingerprints.insert(fp, recipient_fps);
441 }
442 }
443 }
444 ret_signature_fingerprints
445}
446
447pub fn pk_validate(
449 content: &[u8],
450 signature: &[u8],
451 public_keys_for_validation: &[SignedPublicKey],
452) -> Result<HashSet<Fingerprint>> {
453 let mut ret: HashSet<Fingerprint> = Default::default();
454
455 let detached_signature = DetachedSignature::from_armor_single(Cursor::new(signature))?.0;
456
457 for pkey in public_keys_for_validation {
458 if detached_signature.verify(pkey, content).is_ok() {
459 let fp = pkey.dc_fingerprint();
460 ret.insert(fp);
461 }
462 }
463 Ok(ret)
464}
465
466pub async fn symm_encrypt_autocrypt_setup(passphrase: &str, plain: Vec<u8>) -> Result<String> {
468 let passphrase = Password::from(passphrase.to_string());
469
470 tokio::task::spawn_blocking(move || {
471 let mut rng = thread_rng();
472 let s2k = StringToKey::new_default(&mut rng);
473 let builder = MessageBuilder::from_bytes("", plain);
474 let mut builder = builder.seipd_v1(&mut rng, SYMMETRIC_KEY_ALGORITHM);
475 builder.encrypt_with_password(s2k, &passphrase)?;
476
477 let encoded_msg = builder.to_armored_string(&mut rng, Default::default())?;
478
479 Ok(encoded_msg)
480 })
481 .await?
482}
483
484pub async fn symm_encrypt_message(
488 plain: Vec<u8>,
489 private_key_for_signing: SignedSecretKey,
490 shared_secret: &str,
491 compress: bool,
492) -> Result<String> {
493 let shared_secret = Password::from(shared_secret.to_string());
494
495 tokio::task::spawn_blocking(move || {
496 let msg = MessageBuilder::from_bytes("", plain);
497 let mut rng = thread_rng();
498 let mut salt = [0u8; 8];
499 rng.fill(&mut salt[..]);
500 let s2k = StringToKey::Salted {
501 hash_alg: HashAlgorithm::default(),
502 salt,
503 };
504 let mut msg = msg.seipd_v2(
505 &mut rng,
506 SYMMETRIC_KEY_ALGORITHM,
507 AeadAlgorithm::Ocb,
508 ChunkSize::C8KiB,
509 );
510 msg.encrypt_with_password(&mut rng, s2k, &shared_secret)?;
511
512 let hash_algorithm = private_key_for_signing.hash_alg();
513 msg.sign(&*private_key_for_signing, Password::empty(), hash_algorithm);
514 if compress {
515 msg.compression(CompressionAlgorithm::ZLIB);
516 }
517
518 let encoded_msg = msg.to_armored_string(&mut rng, Default::default())?;
519
520 Ok(encoded_msg)
521 })
522 .await?
523}
524
525pub async fn symm_decrypt<T: BufRead + std::fmt::Debug + 'static + Send>(
527 passphrase: &str,
528 ctext: T,
529) -> Result<Vec<u8>> {
530 let passphrase = passphrase.to_string();
531 tokio::task::spawn_blocking(move || {
532 let (enc_msg, _) = Message::from_armor(ctext)?;
533 let password = Password::from(passphrase);
534
535 let msg = enc_msg.decrypt_with_password(&password)?;
536 let res = msg.decompress()?.as_data_vec()?;
537 Ok(res)
538 })
539 .await?
540}
541
542#[cfg(test)]
543mod tests {
544 use std::sync::LazyLock;
545 use tokio::sync::OnceCell;
546
547 use super::*;
548 use crate::{
549 key::{load_self_public_key, load_self_secret_key},
550 test_utils::{TestContextManager, alice_keypair, bob_keypair},
551 };
552 use pgp::composed::Esk;
553 use pgp::packet::PublicKeyEncryptedSessionKey;
554
555 #[expect(clippy::type_complexity)]
556 fn pk_decrypt_and_validate<'a>(
557 ctext: &'a [u8],
558 private_keys_for_decryption: &'a [SignedSecretKey],
559 public_keys_for_validation: &[SignedPublicKey],
560 ) -> Result<(
561 pgp::composed::Message<'static>,
562 HashMap<Fingerprint, Vec<Fingerprint>>,
563 Vec<u8>,
564 )> {
565 let mut msg = decrypt(ctext.to_vec(), private_keys_for_decryption, &[])?;
566 let content = msg.as_data_vec()?;
567 let ret_signature_fingerprints =
568 valid_signature_fingerprints(&msg, public_keys_for_validation);
569
570 Ok((msg, ret_signature_fingerprints, content))
571 }
572
573 #[test]
574 fn test_split_armored_data_1() {
575 let (typ, _headers, base64) = split_armored_data(
576 b"-----BEGIN PGP MESSAGE-----\nNoVal:\n\naGVsbG8gd29ybGQ=\n-----END PGP MESSAGE-----",
577 )
578 .unwrap();
579
580 assert_eq!(typ, BlockType::Message);
581 assert!(!base64.is_empty());
582 assert_eq!(
583 std::string::String::from_utf8(base64).unwrap(),
584 "hello world"
585 );
586 }
587
588 #[test]
589 fn test_split_armored_data_2() {
590 let (typ, headers, base64) = split_armored_data(
591 b"-----BEGIN PGP PRIVATE KEY BLOCK-----\nAutocrypt-Prefer-Encrypt: mutual \n\naGVsbG8gd29ybGQ=\n-----END PGP PRIVATE KEY BLOCK-----"
592 )
593 .unwrap();
594
595 assert_eq!(typ, BlockType::PrivateKey);
596 assert!(!base64.is_empty());
597 assert_eq!(headers.get(HEADER_AUTOCRYPT), Some(&"mutual".to_string()));
598 }
599
600 #[test]
601 fn test_create_keypair() {
602 let keypair0 = create_keypair(EmailAddress::new("foo@bar.de").unwrap()).unwrap();
603 let keypair1 = create_keypair(EmailAddress::new("two@zwo.de").unwrap()).unwrap();
604 assert_ne!(keypair0.public, keypair1.public);
605 }
606
607 struct TestKeys {
610 alice_secret: SignedSecretKey,
611 alice_public: SignedPublicKey,
612 bob_secret: SignedSecretKey,
613 bob_public: SignedPublicKey,
614 }
615
616 impl TestKeys {
617 fn new() -> TestKeys {
618 let alice = alice_keypair();
619 let bob = bob_keypair();
620 TestKeys {
621 alice_secret: alice.secret.clone(),
622 alice_public: alice.public,
623 bob_secret: bob.secret.clone(),
624 bob_public: bob.public,
625 }
626 }
627 }
628
629 static CLEARTEXT: &[u8] = b"This is a test";
631
632 static KEYS: LazyLock<TestKeys> = LazyLock::new(TestKeys::new);
634
635 static CTEXT_SIGNED: OnceCell<String> = OnceCell::const_new();
636
637 async fn ctext_signed() -> &'static String {
639 let anonymous_recipients = true;
640 CTEXT_SIGNED
641 .get_or_init(|| async {
642 let keyring = vec![KEYS.alice_public.clone(), KEYS.bob_public.clone()];
643 let compress = true;
644
645 pk_encrypt(
646 CLEARTEXT.to_vec(),
647 keyring,
648 KEYS.alice_secret.clone(),
649 compress,
650 anonymous_recipients,
651 SeipdVersion::V2,
652 )
653 .await
654 .unwrap()
655 })
656 .await
657 }
658
659 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
660 async fn test_encrypt_signed() {
661 assert!(!ctext_signed().await.is_empty());
662 assert!(
663 ctext_signed()
664 .await
665 .starts_with("-----BEGIN PGP MESSAGE-----")
666 );
667 }
668
669 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
670 async fn test_decrypt_signed() {
671 let decrypt_keyring = vec![KEYS.alice_secret.clone()];
673 let sig_check_keyring = vec![KEYS.alice_public.clone()];
674 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
675 ctext_signed().await.as_bytes(),
676 &decrypt_keyring,
677 &sig_check_keyring,
678 )
679 .unwrap();
680 assert_eq!(content, CLEARTEXT);
681 assert_eq!(valid_signatures.len(), 1);
682 for recipient_fps in valid_signatures.values() {
683 assert_eq!(recipient_fps.len(), 2);
684 }
685
686 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
688 let sig_check_keyring = vec![KEYS.alice_public.clone()];
689 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
690 ctext_signed().await.as_bytes(),
691 &decrypt_keyring,
692 &sig_check_keyring,
693 )
694 .unwrap();
695 assert_eq!(content, CLEARTEXT);
696 assert_eq!(valid_signatures.len(), 1);
697 for recipient_fps in valid_signatures.values() {
698 assert_eq!(recipient_fps.len(), 2);
699 }
700 }
701
702 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
703 async fn test_decrypt_no_sig_check() {
704 let keyring = vec![KEYS.alice_secret.clone()];
705 let (_msg, valid_signatures, content) =
706 pk_decrypt_and_validate(ctext_signed().await.as_bytes(), &keyring, &[]).unwrap();
707 assert_eq!(content, CLEARTEXT);
708 assert_eq!(valid_signatures.len(), 0);
709 }
710
711 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
712 async fn test_decrypt_signed_no_key() {
713 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
715 let sig_check_keyring = vec![KEYS.bob_public.clone()];
716 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
717 ctext_signed().await.as_bytes(),
718 &decrypt_keyring,
719 &sig_check_keyring,
720 )
721 .unwrap();
722 assert_eq!(content, CLEARTEXT);
723 assert_eq!(valid_signatures.len(), 0);
724 }
725
726 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
727 async fn test_decrypt_unsigned() {
728 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
729 let ctext_unsigned = include_bytes!("../test-data/message/ctext_unsigned.asc");
730 let (_msg, valid_signatures, content) =
731 pk_decrypt_and_validate(ctext_unsigned, &decrypt_keyring, &[]).unwrap();
732 assert_eq!(content, CLEARTEXT);
733 assert_eq!(valid_signatures.len(), 0);
734 }
735
736 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
737 async fn test_encrypt_decrypt_broadcast() -> Result<()> {
738 let mut tcm = TestContextManager::new();
739 let alice = &tcm.alice().await;
740 let bob = &tcm.bob().await;
741
742 let plain = Vec::from(b"this is the secret message");
743 let shared_secret = "shared secret";
744 let ctext = symm_encrypt_message(
745 plain.clone(),
746 load_self_secret_key(alice).await?,
747 shared_secret,
748 true,
749 )
750 .await?;
751
752 let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
753 let mut decrypted = decrypt(
754 ctext.into(),
755 &bob_private_keyring,
756 &[shared_secret.to_string()],
757 )?;
758
759 assert_eq!(decrypted.as_data_vec()?, plain);
760
761 Ok(())
762 }
763
764 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
768 async fn test_dont_decrypt_expensive_message() -> Result<()> {
769 let mut tcm = TestContextManager::new();
770 let bob = &tcm.bob().await;
771
772 let plain = Vec::from(b"this is the secret message");
773 let shared_secret = "shared secret";
774
775 let shared_secret_pw = Password::from(shared_secret.to_string());
779 let msg = MessageBuilder::from_bytes("", plain);
780 let mut rng = thread_rng();
781 let s2k = StringToKey::new_default(&mut rng); let mut msg = msg.seipd_v2(
784 &mut rng,
785 SymmetricKeyAlgorithm::AES128,
786 AeadAlgorithm::Ocb,
787 ChunkSize::C8KiB,
788 );
789 msg.encrypt_with_password(&mut rng, s2k, &shared_secret_pw)?;
790
791 let ctext = msg.to_armored_string(&mut rng, Default::default())?;
792
793 let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
796 let error = decrypt(
797 ctext.into(),
798 &bob_private_keyring,
799 &[shared_secret.to_string()],
800 )
801 .unwrap_err();
802
803 assert_eq!(
804 error.to_string(),
805 "missing key (Note: symmetric decryption was not tried: unsupported string2key algorithm)"
806 );
807
808 Ok(())
809 }
810
811 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
812 async fn test_decryption_error_msg() -> Result<()> {
813 let mut tcm = TestContextManager::new();
814 let alice = &tcm.alice().await;
815 let bob = &tcm.bob().await;
816
817 let plain = Vec::from(b"this is the secret message");
818 let pk_for_encryption = load_self_public_key(alice).await?;
819
820 let ctext = pk_encrypt(
822 plain,
823 vec![pk_for_encryption],
824 KEYS.alice_secret.clone(),
825 true,
826 true,
827 SeipdVersion::V2,
828 )
829 .await?;
830
831 let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
833 let error = decrypt(ctext.into(), &bob_private_keyring, &[]).unwrap_err();
834
835 assert_eq!(
836 error.to_string(),
837 "missing key (Note: symmetric decryption was not tried: not symmetrically encrypted)"
838 );
839
840 Ok(())
841 }
842
843 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
846 async fn test_anonymous_recipients() -> Result<()> {
847 let ctext = ctext_signed().await.as_bytes();
848 let cursor = Cursor::new(ctext);
849 let (msg, _headers) = Message::from_armor(cursor)?;
850
851 let Message::Encrypted { esk, .. } = msg else {
852 unreachable!();
853 };
854
855 for encrypted_session_key in esk {
856 let Esk::PublicKeyEncryptedSessionKey(pkesk) = encrypted_session_key else {
857 unreachable!()
858 };
859
860 match pkesk {
861 PublicKeyEncryptedSessionKey::V3 { id, .. } => {
862 assert!(id.is_wildcard());
863 }
864 PublicKeyEncryptedSessionKey::V6 { fingerprint, .. } => {
865 assert!(fingerprint.is_none());
866 }
867 PublicKeyEncryptedSessionKey::Other { .. } => unreachable!(),
868 }
869 }
870 Ok(())
871 }
872}