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