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
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 decrypt(
304 msg: Message<'static>,
305 private_keys_for_decryption: &[SignedSecretKey],
306 mut shared_secrets: &[String],
307) -> Result<pgp::composed::Message<'static>> {
308 let skeys: Vec<&SignedSecretKey> = private_keys_for_decryption.iter().collect();
309 let empty_pw = Password::empty();
310
311 let decrypt_options = DecryptionOptions::new();
312 let symmetric_encryption_res = check_symmetric_encryption(&msg);
313 if symmetric_encryption_res.is_err() {
314 shared_secrets = &[];
315 }
316
317 let message_password: Vec<Password> = shared_secrets
322 .iter()
323 .map(|p| Password::from(p.as_str()))
324 .collect();
325 let message_password: Vec<&Password> = message_password.iter().collect();
326
327 let ring = TheRing {
328 secret_keys: skeys,
329 key_passwords: vec![&empty_pw],
330 message_password,
331 session_keys: vec![],
332 decrypt_options,
333 };
334
335 let res = msg.decrypt_the_ring(ring, true);
336
337 let (msg, _ring_result) = match res {
338 Ok(it) => it,
339 Err(err) => {
340 if let Err(reason) = symmetric_encryption_res {
341 bail!("{err:#} (Note: symmetric decryption was not tried: {reason})")
342 } else {
343 bail!("{err:#}");
344 }
345 }
346 };
347
348 let msg = msg.decompress()?;
350
351 Ok(msg)
352}
353
354pub(crate) fn check_symmetric_encryption(
364 msg: &Message<'_>,
365) -> std::result::Result<(), &'static str> {
366 let Message::Encrypted { esk, .. } = msg else {
367 return Err("not encrypted");
368 };
369
370 if esk.len() > 1 {
371 return Err("too many esks");
372 }
373
374 let [pgp::composed::Esk::SymKeyEncryptedSessionKey(esk)] = &esk[..] else {
375 return Err("not symmetrically encrypted");
376 };
377
378 match esk.s2k() {
379 Some(StringToKey::Salted { .. }) => Ok(()),
380 _ => Err("unsupported string2key algorithm"),
381 }
382}
383
384pub fn valid_signature_fingerprints(
391 msg: &pgp::composed::Message,
392 public_keys_for_validation: &[SignedPublicKey],
393) -> HashMap<Fingerprint, Vec<Fingerprint>> {
394 let mut ret_signature_fingerprints = HashMap::new();
395 if msg.is_signed() {
396 for pkey in public_keys_for_validation {
397 if let Ok(signature) = msg.verify(&pkey.primary_key) {
398 let fp = pkey.dc_fingerprint();
399 let mut recipient_fps = Vec::new();
400 if let Some(cfg) = signature.config() {
401 for subpkt in &cfg.hashed_subpackets {
402 if let SubpacketData::IntendedRecipientFingerprint(fp) = &subpkt.data {
403 recipient_fps.push(fp.clone().into());
404 }
405 }
406 }
407 ret_signature_fingerprints.insert(fp, recipient_fps);
408 }
409 }
410 }
411 ret_signature_fingerprints
412}
413
414pub fn pk_validate(
416 content: &[u8],
417 signature: &[u8],
418 public_keys_for_validation: &[SignedPublicKey],
419) -> Result<HashSet<Fingerprint>> {
420 let mut ret: HashSet<Fingerprint> = Default::default();
421
422 let detached_signature = DetachedSignature::from_armor_single(Cursor::new(signature))?.0;
423
424 for pkey in public_keys_for_validation {
425 if detached_signature.verify(pkey, content).is_ok() {
426 let fp = pkey.dc_fingerprint();
427 ret.insert(fp);
428 }
429 }
430 Ok(ret)
431}
432
433pub async fn symm_encrypt_autocrypt_setup(passphrase: &str, plain: Vec<u8>) -> Result<String> {
435 let passphrase = Password::from(passphrase.to_string());
436
437 tokio::task::spawn_blocking(move || {
438 let mut rng = thread_rng();
439 let s2k = StringToKey::new_default(&mut rng);
440 let builder = MessageBuilder::from_bytes("", plain);
441 let mut builder = builder.seipd_v1(&mut rng, SYMMETRIC_KEY_ALGORITHM);
442 builder.encrypt_with_password(s2k, &passphrase)?;
443
444 let encoded_msg = builder.to_armored_string(&mut rng, Default::default())?;
445
446 Ok(encoded_msg)
447 })
448 .await?
449}
450
451pub async fn symm_encrypt_message(
455 plain: Vec<u8>,
456 private_key_for_signing: Option<SignedSecretKey>,
457 shared_secret: &str,
458 compress: bool,
459) -> Result<String> {
460 let shared_secret = Password::from(shared_secret.to_string());
461
462 tokio::task::spawn_blocking(move || {
463 let msg = MessageBuilder::from_bytes("", plain);
464 let mut rng = thread_rng();
465 let mut salt = [0u8; 8];
466 rng.fill(&mut salt[..]);
467 let s2k = StringToKey::Salted {
468 hash_alg: HashAlgorithm::default(),
469 salt,
470 };
471 let mut msg = msg.seipd_v2(
472 &mut rng,
473 SYMMETRIC_KEY_ALGORITHM,
474 AeadAlgorithm::Ocb,
475 ChunkSize::C8KiB,
476 );
477 msg.encrypt_with_password(&mut rng, s2k, &shared_secret)?;
478
479 if let Some(private_key_for_signing) = private_key_for_signing.as_deref() {
480 let hash_algorithm = private_key_for_signing.hash_alg();
481 msg.sign(private_key_for_signing, Password::empty(), hash_algorithm);
482 }
483 if compress {
484 msg.compression(CompressionAlgorithm::ZLIB);
485 }
486
487 let encoded_msg = msg.to_armored_string(&mut rng, Default::default())?;
488
489 Ok(encoded_msg)
490 })
491 .await?
492}
493
494pub async fn symm_decrypt<T: BufRead + std::fmt::Debug + 'static + Send>(
496 passphrase: &str,
497 ctext: T,
498) -> Result<Vec<u8>> {
499 let passphrase = passphrase.to_string();
500 tokio::task::spawn_blocking(move || {
501 let (enc_msg, _) = Message::from_armor(ctext)?;
502 let password = Password::from(passphrase);
503
504 let msg = enc_msg.decrypt_with_password(&password)?;
505 let res = msg.decompress()?.as_data_vec()?;
506 Ok(res)
507 })
508 .await?
509}
510
511#[cfg(test)]
512mod tests {
513 use std::sync::LazyLock;
514 use tokio::sync::OnceCell;
515
516 use super::*;
517 use crate::{
518 key::{load_self_public_key, load_self_secret_key},
519 test_utils::{TestContextManager, alice_keypair, bob_keypair},
520 };
521 use pgp::composed::Esk;
522 use pgp::packet::PublicKeyEncryptedSessionKey;
523
524 fn decrypt_bytes(
525 bytes: Vec<u8>,
526 private_keys_for_decryption: &[SignedSecretKey],
527 shared_secrets: &[String],
528 ) -> Result<pgp::composed::Message<'static>> {
529 let cursor = Cursor::new(bytes);
530 let (msg, _headers) = Message::from_armor(cursor).unwrap();
531 decrypt(msg, private_keys_for_decryption, shared_secrets)
532 }
533
534 #[expect(clippy::type_complexity)]
535 fn pk_decrypt_and_validate<'a>(
536 ctext: &'a [u8],
537 private_keys_for_decryption: &'a [SignedSecretKey],
538 public_keys_for_validation: &[SignedPublicKey],
539 ) -> Result<(
540 pgp::composed::Message<'static>,
541 HashMap<Fingerprint, Vec<Fingerprint>>,
542 Vec<u8>,
543 )> {
544 let mut msg = decrypt_bytes(ctext.to_vec(), private_keys_for_decryption, &[])?;
545 let content = msg.as_data_vec()?;
546 let ret_signature_fingerprints =
547 valid_signature_fingerprints(&msg, public_keys_for_validation);
548
549 Ok((msg, ret_signature_fingerprints, content))
550 }
551
552 #[test]
553 fn test_split_armored_data_1() {
554 let (typ, _headers, base64) = split_armored_data(
555 b"-----BEGIN PGP MESSAGE-----\nNoVal:\n\naGVsbG8gd29ybGQ=\n-----END PGP MESSAGE-----",
556 )
557 .unwrap();
558
559 assert_eq!(typ, BlockType::Message);
560 assert!(!base64.is_empty());
561 assert_eq!(
562 std::string::String::from_utf8(base64).unwrap(),
563 "hello world"
564 );
565 }
566
567 #[test]
568 fn test_split_armored_data_2() {
569 let (typ, headers, base64) = split_armored_data(
570 b"-----BEGIN PGP PRIVATE KEY BLOCK-----\nAutocrypt-Prefer-Encrypt: mutual \n\naGVsbG8gd29ybGQ=\n-----END PGP PRIVATE KEY BLOCK-----"
571 )
572 .unwrap();
573
574 assert_eq!(typ, BlockType::PrivateKey);
575 assert!(!base64.is_empty());
576 assert_eq!(headers.get(HEADER_AUTOCRYPT), Some(&"mutual".to_string()));
577 }
578
579 #[test]
580 fn test_create_keypair() {
581 let keypair0 = create_keypair(EmailAddress::new("foo@bar.de").unwrap()).unwrap();
582 let keypair1 = create_keypair(EmailAddress::new("two@zwo.de").unwrap()).unwrap();
583 assert_ne!(keypair0.public_key(), keypair1.public_key());
584 }
585
586 struct TestKeys {
589 alice_secret: SignedSecretKey,
590 alice_public: SignedPublicKey,
591 bob_secret: SignedSecretKey,
592 bob_public: SignedPublicKey,
593 }
594
595 impl TestKeys {
596 fn new() -> TestKeys {
597 let alice = alice_keypair();
598 let bob = bob_keypair();
599 TestKeys {
600 alice_secret: alice.clone(),
601 alice_public: alice.to_public_key(),
602 bob_secret: bob.clone(),
603 bob_public: bob.to_public_key(),
604 }
605 }
606 }
607
608 static CLEARTEXT: &[u8] = b"This is a test";
610
611 static KEYS: LazyLock<TestKeys> = LazyLock::new(TestKeys::new);
613
614 static CTEXT_SIGNED: OnceCell<String> = OnceCell::const_new();
615
616 async fn ctext_signed() -> &'static String {
618 let anonymous_recipients = true;
619 CTEXT_SIGNED
620 .get_or_init(|| async {
621 let keyring = vec![KEYS.alice_public.clone(), KEYS.bob_public.clone()];
622 let compress = true;
623
624 pk_encrypt(
625 CLEARTEXT.to_vec(),
626 keyring,
627 KEYS.alice_secret.clone(),
628 compress,
629 anonymous_recipients,
630 SeipdVersion::V2,
631 )
632 .await
633 .unwrap()
634 })
635 .await
636 }
637
638 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
639 async fn test_encrypt_signed() {
640 assert!(!ctext_signed().await.is_empty());
641 assert!(
642 ctext_signed()
643 .await
644 .starts_with("-----BEGIN PGP MESSAGE-----")
645 );
646 }
647
648 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
649 async fn test_decrypt_signed() {
650 let decrypt_keyring = vec![KEYS.alice_secret.clone()];
652 let sig_check_keyring = vec![KEYS.alice_public.clone()];
653 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
654 ctext_signed().await.as_bytes(),
655 &decrypt_keyring,
656 &sig_check_keyring,
657 )
658 .unwrap();
659 assert_eq!(content, CLEARTEXT);
660 assert_eq!(valid_signatures.len(), 1);
661 for recipient_fps in valid_signatures.values() {
662 assert_eq!(recipient_fps.len(), 2);
663 }
664
665 let decrypt_keyring = vec![KEYS.bob_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
681 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
682 async fn test_decrypt_no_sig_check() {
683 let keyring = vec![KEYS.alice_secret.clone()];
684 let (_msg, valid_signatures, content) =
685 pk_decrypt_and_validate(ctext_signed().await.as_bytes(), &keyring, &[]).unwrap();
686 assert_eq!(content, CLEARTEXT);
687 assert_eq!(valid_signatures.len(), 0);
688 }
689
690 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
691 async fn test_decrypt_signed_no_key() {
692 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
694 let sig_check_keyring = vec![KEYS.bob_public.clone()];
695 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
696 ctext_signed().await.as_bytes(),
697 &decrypt_keyring,
698 &sig_check_keyring,
699 )
700 .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_unsigned() {
707 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
708 let ctext_unsigned = include_bytes!("../test-data/message/ctext_unsigned.asc");
709 let (_msg, valid_signatures, content) =
710 pk_decrypt_and_validate(ctext_unsigned, &decrypt_keyring, &[]).unwrap();
711 assert_eq!(content, CLEARTEXT);
712 assert_eq!(valid_signatures.len(), 0);
713 }
714
715 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
716 async fn test_encrypt_decrypt_broadcast() -> Result<()> {
717 let mut tcm = TestContextManager::new();
718 let alice = &tcm.alice().await;
719 let bob = &tcm.bob().await;
720
721 let plain = Vec::from(b"this is the secret message");
722 let shared_secret = "shared secret";
723 let ctext = symm_encrypt_message(
724 plain.clone(),
725 Some(load_self_secret_key(alice).await?),
726 shared_secret,
727 true,
728 )
729 .await?;
730
731 let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
732 let mut decrypted = decrypt_bytes(
733 ctext.into(),
734 &bob_private_keyring,
735 &[shared_secret.to_string()],
736 )?;
737
738 assert_eq!(decrypted.as_data_vec()?, plain);
739
740 Ok(())
741 }
742
743 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
747 async fn test_dont_decrypt_expensive_message() -> Result<()> {
748 let mut tcm = TestContextManager::new();
749 let bob = &tcm.bob().await;
750
751 let plain = Vec::from(b"this is the secret message");
752 let shared_secret = "shared secret";
753
754 let shared_secret_pw = Password::from(shared_secret.to_string());
758 let msg = MessageBuilder::from_bytes("", plain);
759 let mut rng = thread_rng();
760 let s2k = StringToKey::new_default(&mut rng); let mut msg = msg.seipd_v2(
763 &mut rng,
764 SymmetricKeyAlgorithm::AES128,
765 AeadAlgorithm::Ocb,
766 ChunkSize::C8KiB,
767 );
768 msg.encrypt_with_password(&mut rng, s2k, &shared_secret_pw)?;
769
770 let ctext = msg.to_armored_string(&mut rng, Default::default())?;
771
772 let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
775 let error = decrypt_bytes(
776 ctext.into(),
777 &bob_private_keyring,
778 &[shared_secret.to_string()],
779 )
780 .unwrap_err();
781
782 assert_eq!(
783 error.to_string(),
784 "missing key (Note: symmetric decryption was not tried: unsupported string2key algorithm)"
785 );
786
787 Ok(())
788 }
789
790 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
791 async fn test_decryption_error_msg() -> Result<()> {
792 let mut tcm = TestContextManager::new();
793 let alice = &tcm.alice().await;
794 let bob = &tcm.bob().await;
795
796 let plain = Vec::from(b"this is the secret message");
797 let pk_for_encryption = load_self_public_key(alice).await?;
798
799 let ctext = pk_encrypt(
801 plain,
802 vec![pk_for_encryption],
803 KEYS.alice_secret.clone(),
804 true,
805 true,
806 SeipdVersion::V2,
807 )
808 .await?;
809
810 let bob_private_keyring = crate::key::load_self_secret_keyring(bob).await?;
812 let error = decrypt_bytes(ctext.into(), &bob_private_keyring, &[]).unwrap_err();
813
814 assert_eq!(
815 error.to_string(),
816 "missing key (Note: symmetric decryption was not tried: not symmetrically encrypted)"
817 );
818
819 Ok(())
820 }
821
822 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
825 async fn test_anonymous_recipients() -> Result<()> {
826 let ctext = ctext_signed().await.as_bytes();
827 let cursor = Cursor::new(ctext);
828 let (msg, _headers) = Message::from_armor(cursor)?;
829
830 let Message::Encrypted { esk, .. } = msg else {
831 unreachable!();
832 };
833
834 for encrypted_session_key in esk {
835 let Esk::PublicKeyEncryptedSessionKey(pkesk) = encrypted_session_key else {
836 unreachable!()
837 };
838
839 match pkesk {
840 PublicKeyEncryptedSessionKey::V3 { id, .. } => {
841 assert!(id.is_wildcard());
842 }
843 PublicKeyEncryptedSessionKey::V6 { fingerprint, .. } => {
844 assert!(fingerprint.is_none());
845 }
846 PublicKeyEncryptedSessionKey::Other { .. } => unreachable!(),
847 }
848 }
849 Ok(())
850 }
851}