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