From 2505bc6667ad09f46d46245f9de340fda5b3e1f9 Mon Sep 17 00:00:00 2001
From: johan pascal <johan.pascal@belledonne-communications.com>
Date: Thu, 27 Feb 2025 13:06:48 +0100
Subject: [PATCH] Lime message deflate condition improved + better migration
 testing

---
 .../lime-x3dh-encryption-engine.cpp           |  12 +-
 tester/group_chat_secure_multialgo_tester.cpp | 122 ++++++++++++++++++
 2 files changed, 132 insertions(+), 2 deletions(-)

diff --git a/src/chat/encryption/lime-x3dh-encryption-engine.cpp b/src/chat/encryption/lime-x3dh-encryption-engine.cpp
index f36025eafe..d53f3c9d8e 100644
--- a/src/chat/encryption/lime-x3dh-encryption-engine.cpp
+++ b/src/chat/encryption/lime-x3dh-encryption-engine.cpp
@@ -360,12 +360,20 @@ ChatMessageModifier::Result LimeX3dhEncryptionEngine::processOutgoingMessage(con
 
 	// Compress plain text message - compression after encryption is much less efficient
 	// To keep compatibility with version not supporting the encryption at this stage, do it (for now: may 2024)
-	// only when the only supported algorithm is c25519k512
+	// only when all the supported algorithm are in c25519k512, c25519mlk512, c448mlk1024
 	bool compressedPlain = false;
 	Content plainContent(message->getInternalContent());
 	const string localDeviceId = localDevice->asStringUriOnly();
 	if (usersAlgos.find(localDeviceId) != usersAlgos.end()) {
-		if (usersAlgos[localDeviceId].size() == 1 && usersAlgos[localDeviceId][0] == lime::CurveId::c25519k512) {
+		bool skipDeflate = false;
+		for (const auto &algo : usersAlgos[localDeviceId]) {
+			if (algo <
+			    lime::CurveId::c25519k512) { // enum class is ordered, values under c2519k512 are the older base algo
+				skipDeflate = true;
+				break;
+			}
+		}
+		if (!skipDeflate) {
 			compressedPlain = plainContent.deflateBody();
 		}
 	}
diff --git a/tester/group_chat_secure_multialgo_tester.cpp b/tester/group_chat_secure_multialgo_tester.cpp
index 0aa48be404..f9f35e0017 100644
--- a/tester/group_chat_secure_multialgo_tester.cpp
+++ b/tester/group_chat_secure_multialgo_tester.cpp
@@ -609,6 +609,128 @@ static void group_chat_lime_x3dh_soft_migration(void) {
 
 	// wait bit more to allow imdn exchanges
 	Linphone::Tester::CoreManagerAssert(allCoreMgrs).waitUntil(std::chrono::seconds(3), [] { return false; });
+
+	// ReStart Marie, Laure and Chloe core with c25519mlk512,c25519k512 (drop c25519)
+	laure.set_limeAlgoList({lime::CurveId::c25519mlk512, lime::CurveId::c25519k512});
+	coresList = bctbx_list_remove(coresList, laure.getLc());
+	laure.reStart();
+	coresList = bctbx_list_append(coresList, laure.getLc());
+	laureStats = laure.getStats();
+	BC_ASSERT_TRUE(laure.assertPtrValue(&(laureStats.number_of_X3dhUserCreationSuccess), 1));
+
+	chloe.set_limeAlgoList({lime::CurveId::c25519mlk512, lime::CurveId::c25519k512});
+	coresList = bctbx_list_remove(coresList, chloe.getLc());
+	chloe.reStart();
+	coresList = bctbx_list_append(coresList, chloe.getLc());
+	chloeStats = chloe.getStats();
+	BC_ASSERT_TRUE(chloe.assertPtrValue(&(chloeStats.number_of_X3dhUserCreationSuccess), 1));
+
+	marie.set_limeAlgoList({lime::CurveId::c25519mlk512, lime::CurveId::c25519k512});
+	coresList = bctbx_list_remove(coresList, marie.getLc());
+	marie.reStart();
+	coresList = bctbx_list_append(coresList, marie.getLc());
+	marieStats = marie.getStats();
+	BC_ASSERT_TRUE(marie.assertPtrValue(&(marieStats.number_of_X3dhUserCreationSuccess), 1));
+
+	// get back marieCr
+	marieDeviceAddr = linphone_address_clone(linphone_proxy_config_get_contact(marie.getDefaultProxyConfig()));
+	marieCr = marie.searchChatRoom(marieDeviceAddr, confAddr);
+	BC_ASSERT_PTR_NOT_NULL(marieCr);
+	linphone_address_unref(marieDeviceAddr);
+
+	// Marie send a message to the group
+	msgText = std::string("To infinity and beyond");
+	msg = Linphone::Tester::ConfCoreManager::sendTextMsg(marieCr, msgText);
+
+	// Message is delivered
+	BC_ASSERT_TRUE(Linphone::Tester::CoreManagerAssert(allCoreMgrs).wait([msg] {
+		return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered);
+	}));
+
+	linphone_chat_message_unref(msg);
+	msg = nullptr;
+
+	// everyone get it
+	for (Linphone::Tester::CoreManager &client : recipients) {
+		for (auto chatRoom : client.getCore().getChatRooms()) {
+			BC_ASSERT_TRUE(Linphone::Tester::CoreManagerAssert(allCoreMgrs).wait([chatRoom] {
+				return chatRoom->getUnreadChatMessageCount() == 1;
+			}));
+			LinphoneChatMessage *lastMsg = client.getStats().last_received_chat_message;
+			BC_ASSERT_PTR_NOT_NULL(lastMsg);
+			if (lastMsg) {
+				BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(lastMsg), msgText.c_str());
+			}
+			chatRoom->markAsRead();
+		}
+	}
+
+	// wait bit more to allow imdn exchanges
+	Linphone::Tester::CoreManagerAssert(allCoreMgrs).waitUntil(std::chrono::seconds(3), [] { return false; });
+
+	// ReStart Marie, Laure and Chloe core with c25519mlk512 only (drop c25519k512) -> pauline won't be able to get the
+	// message
+	laure.set_limeAlgoList({lime::CurveId::c25519mlk512});
+	coresList = bctbx_list_remove(coresList, laure.getLc());
+	laure.reStart();
+	coresList = bctbx_list_append(coresList, laure.getLc());
+	laureStats = laure.getStats();
+	BC_ASSERT_TRUE(laure.assertPtrValue(&(laureStats.number_of_X3dhUserCreationSuccess), 1));
+
+	chloe.set_limeAlgoList({lime::CurveId::c25519mlk512});
+	coresList = bctbx_list_remove(coresList, chloe.getLc());
+	chloe.reStart();
+	coresList = bctbx_list_append(coresList, chloe.getLc());
+	chloeStats = chloe.getStats();
+	BC_ASSERT_TRUE(chloe.assertPtrValue(&(chloeStats.number_of_X3dhUserCreationSuccess), 1));
+
+	marie.set_limeAlgoList({lime::CurveId::c25519mlk512});
+	coresList = bctbx_list_remove(coresList, marie.getLc());
+	marie.reStart();
+	coresList = bctbx_list_append(coresList, marie.getLc());
+	marieStats = marie.getStats();
+	BC_ASSERT_TRUE(marie.assertPtrValue(&(marieStats.number_of_X3dhUserCreationSuccess), 1));
+
+	// get back marieCr
+	marieDeviceAddr = linphone_address_clone(linphone_proxy_config_get_contact(marie.getDefaultProxyConfig()));
+	marieCr = marie.searchChatRoom(marieDeviceAddr, confAddr);
+	BC_ASSERT_PTR_NOT_NULL(marieCr);
+	linphone_address_unref(marieDeviceAddr);
+
+	// Marie send a message to the group
+	msgText = std::string("Bye Pauline");
+	msg = Linphone::Tester::ConfCoreManager::sendTextMsg(marieCr, msgText);
+
+	// Message is delivered
+	BC_ASSERT_TRUE(Linphone::Tester::CoreManagerAssert(allCoreMgrs).wait([msg] {
+		return (linphone_chat_message_get_state(msg) == LinphoneChatMessageStateDelivered);
+	}));
+
+	linphone_chat_message_unref(msg);
+	msg = nullptr;
+
+	// Laure and Chloe get it
+	for (Linphone::Tester::CoreManager &client :
+	     std::list<std::reference_wrapper<Linphone::Tester::CoreManager>>{laure, chloe}) {
+		for (auto chatRoom : client.getCore().getChatRooms()) {
+			BC_ASSERT_TRUE(Linphone::Tester::CoreManagerAssert(allCoreMgrs).wait([chatRoom] {
+				return chatRoom->getUnreadChatMessageCount() == 1;
+			}));
+			LinphoneChatMessage *lastMsg = client.getStats().last_received_chat_message;
+			BC_ASSERT_PTR_NOT_NULL(lastMsg);
+			if (lastMsg) {
+				BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_utf8_text(lastMsg), msgText.c_str());
+			}
+			chatRoom->markAsRead();
+		}
+	}
+
+	// wait bit more to allow imdn exchanges
+	Linphone::Tester::CoreManagerAssert(allCoreMgrs).waitUntil(std::chrono::seconds(3), [] { return false; });
+
+	// Pauline does not
+	BC_ASSERT_EQUAL(pauline.getCore().getChatRooms().front()->getUnreadChatMessageCount(), 0, int, "%d");
+
 	linphone_address_unref(confAddr);
 	bctbx_list_free(coresList);
 }
-- 
GitLab