diff --git a/src/pgp.rs b/src/pgp.rs index fd0625a41..eba26f98f 100644 --- a/src/pgp.rs +++ b/src/pgp.rs @@ -128,13 +128,7 @@ pub async fn pk_encrypt( hashed.push(Subpacket::critical(SubpacketData::SignatureCreationTime( pgp::types::Timestamp::now(), ))?); - // Test "elena" uses old Delta Chat. - let skip = private_key_for_signing.dc_fingerprint().hex() - == "B86586B6DEF437D674BFAFC02A6B2EBC633B9E82"; for key in &public_keys_for_encryption { - if skip { - break; - } let data = SubpacketData::IntendedRecipientFingerprint(key.fingerprint()); let subpkt = match private_key_for_signing.version() < KeyVersion::V6 { true => Subpacket::regular(data)?, diff --git a/src/receive_imf/receive_imf_tests.rs b/src/receive_imf/receive_imf_tests.rs index 8209de31b..c0234063c 100644 --- a/src/receive_imf/receive_imf_tests.rs +++ b/src/receive_imf/receive_imf_tests.rs @@ -5108,97 +5108,75 @@ async fn test_dont_verify_by_verified_by_unknown() -> Result<()> { Ok(()) } +/// Tests that second device assigns outgoing encrypted messages +/// to 1:1 chat with key-contact even if the key of the contact is unknown. #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_recv_outgoing_msg_before_securejoin() -> Result<()> { let mut tcm = TestContextManager::new(); let bob = &tcm.bob().await; - let a0 = &tcm.elena().await; - let a1 = &tcm.elena().await; - - tcm.execute_securejoin(bob, a0).await; - let chat_id_a0_bob = a0.create_chat_id(bob).await; - let sent_msg = a0.send_text(chat_id_a0_bob, "Hi").await; - bob.recv_msg(&sent_msg).await; - let msg_a1 = a1.recv_msg(&sent_msg).await; - assert!(msg_a1.get_showpadlock()); - let chat_a1 = Chat::load_from_db(a1, msg_a1.chat_id).await?; - assert_eq!(chat_a1.typ, Chattype::Group); - assert!(!chat_a1.is_encrypted(a1).await?); - assert_eq!( - chat::get_chat_contacts(a1, chat_a1.id).await?, - [a1.add_or_lookup_address_contact_id(bob).await] - ); - assert_eq!( - chat_a1.why_cant_send(a1).await?, - Some(CantSendReason::NotAMember) - ); - - let sent_msg = a0.send_text(chat_id_a0_bob, "Hi again").await; - bob.recv_msg(&sent_msg).await; - let msg_a1 = a1.recv_msg(&sent_msg).await; - assert!(msg_a1.get_showpadlock()); - assert_eq!(msg_a1.chat_id, chat_a1.id); - let chat_a1 = Chat::load_from_db(a1, msg_a1.chat_id).await?; - assert_eq!( - chat_a1.why_cant_send(a1).await?, - Some(CantSendReason::NotAMember) - ); - - let msg_a1 = tcm.send_recv(bob, a1, "Hi back").await; - assert!(msg_a1.get_showpadlock()); - let chat_a1 = Chat::load_from_db(a1, msg_a1.chat_id).await?; - assert_eq!(chat_a1.typ, Chattype::Single); - assert!(chat_a1.is_encrypted(a1).await?); - // Weird, but fine, anyway the bigger problem is the conversation split into two chats. - assert_eq!( - chat_a1.why_cant_send(a1).await?, - Some(CantSendReason::ContactRequest) - ); - let a0 = &tcm.alice().await; let a1 = &tcm.alice().await; + tcm.execute_securejoin(bob, a0).await; let chat_id_a0_bob = a0.create_chat_id(bob).await; let sent_msg = a0.send_text(chat_id_a0_bob, "Hi").await; - bob.recv_msg(&sent_msg).await; + + // Device a1 does not have Bob's key. + // Message is still received in an encrypted 1:1 chat with Bob. + // a1 learns the fingerprint of Bob from the Intended Recipient Fingerprint packet, + // but not the key. let msg_a1 = a1.recv_msg(&sent_msg).await; assert!(msg_a1.get_showpadlock()); let chat_a1 = Chat::load_from_db(a1, msg_a1.chat_id).await?; assert_eq!(chat_a1.typ, Chattype::Single); assert!(chat_a1.is_encrypted(a1).await?); + + // Cannot send because a1 does not have Bob's key. + assert!(!chat_a1.can_send(a1).await?); + assert_eq!( + chat_a1.why_cant_send(a1).await?, + Some(CantSendReason::MissingKey) + ); + assert_eq!( chat::get_chat_contacts(a1, chat_a1.id).await?, - [a1.add_or_lookup_contact_id(bob).await] + [a1.add_or_lookup_contact_id_no_key(bob).await] ); - assert!(chat_a1.can_send(a1).await?); + assert!(!chat_a1.can_send(a1).await?); + + let a1_chat_id = a1.create_chat_id(bob).await; + assert_eq!(a1_chat_id, msg_a1.chat_id); Ok(()) } +/// Tests that outgoing message cannot be assigned to 1:1 chat +/// without the intended recipient fingerprint. #[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn test_recv_outgoing_msg_before_having_key_and_after() -> Result<()> { +async fn test_recv_outgoing_msg_no_intended_recipient_fingerprint() -> Result<()> { let mut tcm = TestContextManager::new(); - let a0 = &tcm.elena().await; - let a1 = &tcm.elena().await; - let bob = &tcm.bob().await; + let alice = &tcm.alice().await; - tcm.execute_securejoin(bob, a0).await; - let chat_id_a0_bob = a0.create_chat_id(bob).await; - let sent_msg = a0.send_text(chat_id_a0_bob, "Hi").await; - let msg_a1 = a1.recv_msg(&sent_msg).await; - assert!(msg_a1.get_showpadlock()); - let chat_a1 = Chat::load_from_db(a1, msg_a1.chat_id).await?; - assert_eq!(chat_a1.typ, Chattype::Group); - assert!(!chat_a1.is_encrypted(a1).await?); + let payload = include_bytes!( + "../../test-data/message/alice_to_bob_no_intended_recipient_fingerprint.eml" + ); + + // Alice does not have Bob's key. + // Message is encrypted, but is received in ad hoc group with Bob's address. + let rcvd_msg = receive_imf(alice, payload, false).await?.unwrap(); + let msg_alice = Message::load_from_db(alice, rcvd_msg.msg_ids[0]).await?; + + assert!(msg_alice.get_showpadlock()); + let chat_alice = Chat::load_from_db(alice, msg_alice.chat_id).await?; + assert_eq!(chat_alice.typ, Chattype::Group); + assert!(!chat_alice.is_encrypted(alice).await?); + + // Cannot send because Bob's key is unknown. + assert!(!chat_alice.can_send(alice).await?); + assert_eq!( + chat_alice.why_cant_send(alice).await?, + Some(CantSendReason::NotAMember) + ); - // Device a1 somehow learns Bob's key and creates the corresponding chat. However, this doesn't - // help because we only look up key contacts by address in a particular chat and the new chat - // isn't referenced by the received message. This is fixed by sending and receiving Intended - // Recipient Fingerprint subpackets which elena doesn't send. - a1.create_chat_id(bob).await; - let sent_msg = a0.send_text(chat_id_a0_bob, "Hi again").await; - let msg_a1 = a1.recv_msg(&sent_msg).await; - assert!(msg_a1.get_showpadlock()); - assert_eq!(msg_a1.chat_id, chat_a1.id); Ok(()) } diff --git a/src/test_utils.rs b/src/test_utils.rs index cc7b7dcc8..f687bd3ca 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -118,7 +118,6 @@ impl TestContextManager { } /// Returns new elena's "device". - /// Elena doesn't send Intended Recipient Fingerprint subpackets to simulate old Delta Chat. pub async fn elena(&mut self) -> TestContext { TestContext::builder() .configure_elena() diff --git a/test-data/message/alice_to_bob_no_intended_recipient_fingerprint.eml b/test-data/message/alice_to_bob_no_intended_recipient_fingerprint.eml new file mode 100644 index 000000000..59031caa6 --- /dev/null +++ b/test-data/message/alice_to_bob_no_intended_recipient_fingerprint.eml @@ -0,0 +1,66 @@ +Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; + boundary="189f5bdd65baabe1_1081b3a339e9a68e_ec024374690079d2" +MIME-Version: 1.0 +From: +To: "hidden-recipients": ; +Subject: [...] +Date: Wed, 18 Mar 2026 14:03:13 +0000 +Message-ID: <4bc40798-0029-42ef-b34a-77866db439a5@localhost> +Chat-Version: 1.0 + + +--189f5bdd65baabe1_1081b3a339e9a68e_ec024374690079d2 +Content-Type: application/pgp-encrypted; charset="utf-8" +Content-Description: PGP/MIME version identification +Content-Transfer-Encoding: 7bit + +Version: 1 + +--189f5bdd65baabe1_1081b3a339e9a68e_ec024374690079d2 +Content-Type: application/octet-stream; name="encrypted.asc"; + charset="utf-8" +Content-Description: OpenPGP encrypted message +Content-Disposition: inline; filename="encrypted.asc"; +Content-Transfer-Encoding: 7bit + +-----BEGIN PGP MESSAGE----- + +wU4DAAAAAAAAAAASAQdA297achK/ltriEs4OZi2IH+z7qzCFohr/zIzQP56gAlUg +M+1o/VqAjq/vo1iWluO+q7OkQZ71F3svvCb8I8bEtijBwEwDAAAAAAAAAAABB/9z +ZkrDMm+3gb6VeDe3QV5yLp8GcFHtOqPaIR2FsElDR1TmMJdCnQhZ7TvzYbeGbZ3M +sqjYHtZLGLfrBiFwaygOFJRpS9mvtyb0q/PB2GteLWysJaBy3nZtk9ZmYPs2vqlw +eVldQsNFkgAKVG6kaGTElXGGfraETvlCIyK2dglHXQJKmFEMno5OaFk/E7RP04CH +hsnuDlTrKeppzgjmp6NFtV9vOkb5cCrK5L1Qo7BQgAFxEw5BgPBryxt2VkMdKMMZ +BscDYnrrqFk8JLzlqrFxPvMWuwwyL0rVjau2rDuGMFEZQWfWkaJ2nPhicc4lpg4s +qaRwFcbdtZ0jEqHBuHAS0sQmAWStScwsUHghlsk+93sRsRxP1+dqj3+kSC4Gzz79 ++ZSeStvk0Aa2khwmeFYNIzJIQUQpfwRDm7tnghnMjy7jDpn08yOpy3V1LR2PNGSz +KtT7Q9F7rwNn2j/xs1hRAtp9n37fnlLZXyDQvOtsucKtYUiYydkWRfQdcVYem2Rt +K7iT2kkgHbkbNP8Mk53VzF1sIE/0rnjtoDOTBQ/GAG7I9xxBjJ8bNoF0250E0cFL +iHvsQRkIbLRip0qYuygsJ1zQgN4xkHYIY71iiiYUroK8PKDbAgyE/jz0kvfXqOdy +6zjr+HIYb7jD2e7zNiI9pcHr3Or1mgZj5cJUktus2Kpnbz7lBhUMh2hBLCwaVUNv +Pi0CIc+GMuwuLUOfvrT13E3gzY3a9NMQYORIrfE/I+4sy6urVWQkZarJol0xEJww +BtKDyOJjdYSK59pT30wKLz0jy1G+XJ4yfqjf9kJlUwwHvWKpj0u8bAa0VrFlCjA2 +sLOgkKCYPHvaImGG0z4wrn4kbBtaiVcTGCF3fPOafkYqdKR9TaN3FBOiUz80ezli +FLiyaH1/+VvzOmVHj8QWTViwkkO6Psvh/6m3eShqp2xekc82yWPer80xgIGipOxm +wTeIwBb6x8nB5PuQloJF9rj6O7kYqw4R8bMRS8hs0DxuVACTYxpnDMPsQ65kFAmk +6Qqzj5xpRGF10IOlMWtVYJIVvTKgnHwDvuWwLEEdgfGv0Edek3+VXIRAhySVJM01 +HJQ1cAHPUdQnksFr5cx5fZfTEdLsQ8onZDtwStptw38NVm3DxQiT0HYyjiilBzOU +xFN8+Mm1hCYF69DdSD3xBK4fLHQ2DdKwHIdz1UxI5KyBBVek4rfhJsx2jGKoz68o +l2ziNXgijwDlpZTqwxz+xHQWvw1L+GvP2HRfXVxLwJ61kZzKpEq8L01ZZlg+KI7C +JQFHbdJOA2NRXZ1WoagJObPuGqLsRqHnZ3oDtpqJ1LvOp5KAvlq138+lkKPXo66s +NPyExfm2+hjdKI6WqLNqa7sDBEsxqFkvfC84VhrKEvXn7oqmgtWBjmBcHA1O7haC +xspncUcyLiksOeVdwJLRnCyZDtVB9VtXaeyJT6v2sCCU7gWaoMXFwX/68oKPkz6S +xjWPkXPsBCBdXMS2ovINBwzhWeU9utluCLgk0g1rgAwZgPNhwpAZr9D74BSHohjD +EYWGmBdz86ly6DB7UwYYheSzTwWSOb5qYuPfo1MxEVzrAgal5795zYAIv/dcofmc +ahZ5JPXZgtywqMTAliKV24ENFZWSylE125zICX0vIKEr7zOowbENKBMoXFRqTMCh +D8tdFdSlSLxMH2Enc3ndC4i5G7W/hZhFT7Lnpab9vWj2suVAvjrgOUCHPzXNY9Un +RLCN4YfQelE9RAbaFYRVIE5XQUZJYMAf8kJvO3pWZ232a1m2Jp1GhfVo+/T/Y9qM +3yxv8+rU62B0Eta8OBtcPweE/4X+vwV6GEMCGI4rhcaTohvXFp/XgOqAkXS3weaZ +QwnmZQ66GsnjBlSbJsTmE3TgqzUrEFsVOOZ0Hc1elyZ6XvYdBR8XgDgaMkzu5M1r +0JVJN5eMassObqhvZBa3uHlUEvoT2ufA3ue5iRapWqkfAafOzmrDLEH/OBkjff4F +VEJ9Mqz+YT5A4e+3inYfrmfVdNHNmF4y +=2p4+ +-----END PGP MESSAGE----- + + +--189f5bdd65baabe1_1081b3a339e9a68e_ec024374690079d2--