Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions src/pgp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Main change, removing test code from releases so we don't need to maintain the test code when changing pk_encrypt.

== "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)?,
Expand Down
106 changes: 42 additions & 64 deletions src/receive_imf/receive_imf_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
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;
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;
// 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());
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.

// 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::ContactRequest)
Some(CantSendReason::MissingKey)
);

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;
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?);
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;

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)
);

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?);

// 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(())
}

Expand Down
3 changes: 1 addition & 2 deletions src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -894,7 +893,7 @@ impl TestContext {
///
/// If the contact does not exist yet, a new contact will be created
/// with the correct fingerprint, but without the public key.
async fn add_or_lookup_contact_id_no_key(&self, other: &TestContext) -> ContactId {
pub async fn add_or_lookup_contact_id_no_key(&self, other: &TestContext) -> ContactId {
let primary_self_addr = other.ctx.get_primary_self_addr().await.unwrap();
let addr = ContactAddress::new(&primary_self_addr).unwrap();
let fingerprint = self_fingerprint(other).await.unwrap();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
Content-Type: multipart/encrypted; protocol="application/pgp-encrypted";
boundary="189f5bdd65baabe1_1081b3a339e9a68e_ec024374690079d2"
MIME-Version: 1.0
From: <alice@example.org>
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-----

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
VEJ9Mqz+YT5A4e+3inYfrmfVdNHNmF4y
=2p4+
-----END PGP MESSAGE-----


--189f5bdd65baabe1_1081b3a339e9a68e_ec024374690079d2--
Loading