Page MenuHomePhorge

D71.1726893362.diff
No OneTemporary

D71.1726893362.diff

diff --git a/src/client/actions/encryption.cpp b/src/client/actions/encryption.cpp
--- a/src/client/actions/encryption.cpp
+++ b/src/client/actions/encryption.cpp
@@ -302,20 +302,33 @@
return;
} else {
auto messages = room.messages;
- room.messages = merge(
- room.messages,
- intoImmer(
- EventList{},
- zug::filter([](auto n) {
- auto e = n.second;
- return e.encrypted();
- })
- | zug::map([=](auto n) {
- auto event = n.second;
- return decryptFunc(event);
- }),
- std::move(messages)),
- keyOfTimeline);
+ auto undecryptedEvents = room.undecryptedEvents;
+ for (auto [sessionId, eventIds] : undecryptedEvents) {
+ if (m.constCrypto().hasInboundGroupSession({
+ room.roomId,
+ sessionId,
+ })) {
+ auto nextEventIds = intoImmer(
+ immer::flex_vector<std::string>{},
+ zug::filter([&](auto eventId) {
+ auto event = room.messages[eventId];
+ auto decrypted = decryptFunc(event);
+ if (decrypted.decrypted()) {
+ room.messages = std::move(room.messages)
+ .set(eventId, decrypted);
+ }
+ return !decrypted.decrypted();
+ }),
+ eventIds
+ );
+ if (nextEventIds.empty()) {
+ room.undecryptedEvents = std::move(room.undecryptedEvents).erase(sessionId);
+ } else {
+ room.undecryptedEvents = std::move(room.undecryptedEvents).set(sessionId, nextEventIds);
+ }
+ }
+ }
+
m.roomList.rooms = std::move(m.roomList.rooms).set(id, room);
}
};
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -82,6 +82,7 @@
client/device-list-tracker-benchmark-test.cpp
client/room/read-receipt-test.cpp
client/room/undecrypted-events-test.cpp
+ client/encryption-benchmark-test.cpp
EXTRA_LINK_LIBRARIES kazvclient kazveventemitter kazvjob client-test-lib kazvtestfixtures
EXTRA_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/client
)
diff --git a/src/tests/client/encryption-benchmark-test.cpp b/src/tests/client/encryption-benchmark-test.cpp
new file mode 100644
--- /dev/null
+++ b/src/tests/client/encryption-benchmark-test.cpp
@@ -0,0 +1,100 @@
+/*
+ * This file is part of libkazv.
+ * SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libkazv-config.hpp>
+#include <iostream>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/benchmark/catch_benchmark.hpp>
+#include <actions/encryption.hpp>
+#include <client-model.hpp>
+#include "factory.hpp"
+
+using namespace Kazv;
+using namespace Kazv::Factory;
+
+// The assumption here is that undecrypted events take only a small
+// part in the room timeline, and most undecrypted events are so because
+// we do not have the corresponding session key.
+// This should hold when the most of the
+// messages in the timeline is received after the session begins
+// (until we implement key sharing).
+static ClientModel data(
+ std::size_t roomCount,
+ std::size_t messagesPerRoom,
+ std::size_t undecryptableMessagesPerRoom,
+ std::size_t decryptableMessagesPerRoom
+)
+{
+ std::cerr << "Generating data" << std::endl;
+ auto client = makeClient(withCrypto(makeCrypto()));
+
+ std::size_t generated = 0;
+ auto makeNewEvent = [&](const std::string &roomId, std::size_t m) {
+ if (m < undecryptableMessagesPerRoom) {
+ return makeEvent(withEventType("m.room.encrypted")
+ | withEventContent(json{
+ {"algorithm", CryptoConstants::megOlmAlgo},
+ {"ciphertext", "does not matter"},
+ {"device_id", "somedevice"},
+ {"sender_key", "somekey"},
+ // rotating the session once per 100 messages
+ {"session_id", "some-session-id" + std::to_string(m / 100)},
+ })
+ | withEventKV("/room_id"_json_pointer, roomId));
+ } else if (m < undecryptableMessagesPerRoom + decryptableMessagesPerRoom) {
+ auto event = makeEvent();
+ auto [encrypted, sessionId] = client.megOlmEncrypt(
+ event, roomId, 1719196953000,
+ genRandomData(EncryptMegOlmEventAction::maxRandomSize()));
+ return encrypted;
+ } else {
+ return makeEvent();
+ }
+ };
+
+ for (std::size_t r = 0; r < roomCount; ++r) {
+ auto room = makeRoom(withRoomEncrypted(true));
+ withRoom(room)(client);
+ auto events = EventList{};
+ for (std::size_t m = 0; m < messagesPerRoom; ++m) {
+ auto event = makeNewEvent(room.roomId, m);
+ events = std::move(events).push_back(event);
+ ++generated;
+ if (generated % 10000 == 0) {
+ std::cerr << "Generated " << generated << " events" << std::endl;
+ }
+ }
+ withRoomTimeline(events)(room);
+ withRoom(room)(client);
+ }
+ return client;
+}
+
+// correspond to the case where the session is pretty new
+TEST_CASE("tryDecryptEvents() benchmark: small", "[client][encryption][!benchmark]")
+{
+ auto client = data(10, 200, 100, 5);
+ BENCHMARK("tryDecryptEvents()") {
+ return tryDecryptEvents(client);
+ };
+}
+
+TEST_CASE("tryDecryptEvents() benchmark: medium", "[client][encryption][!benchmark]")
+{
+ auto client = data(50, 1000, 200, 5);
+ BENCHMARK("tryDecryptEvents()") {
+ return tryDecryptEvents(client);
+ };
+}
+
+// A somewhat unrealistic case
+TEST_CASE("tryDecryptEvents() benchmark: large", "[client][encryption][!benchmark]")
+{
+ auto client = data(100, 4000, 400, 5);
+ BENCHMARK("tryDecryptEvents()") {
+ return tryDecryptEvents(client);
+ };
+}
diff --git a/src/tests/client/encryption-test.cpp b/src/tests/client/encryption-test.cpp
--- a/src/tests/client/encryption-test.cpp
+++ b/src/tests/client/encryption-test.cpp
@@ -186,4 +186,71 @@
REQUIRE(decryptedEvent2.decrypted());
REQUIRE(decryptedEvent2.type() == plainText2.type());
REQUIRE(decryptedEvent2.content() == plainText2.content());
+
+ REQUIRE(nextClient.roomList.rooms[roomId].undecryptedEvents
+ ==
+ immer::map<std::string, immer::flex_vector<std::string>>{});
+}
+
+TEST_CASE("tryDecryptEvents() will update room.undecryptedEvents", "[client][encryption]")
+{
+ auto roomId = "!someroom:example.com";
+ auto room = makeRoom(
+ withRoomEncrypted(true)
+ | withRoomId(roomId)
+ );
+ auto client = makeClient(
+ withCrypto(makeCrypto())
+ | withRoom(room)
+ );
+
+ auto plainText = makeEvent();
+ auto [encrypted, sessionId] = client.megOlmEncrypt(plainText, roomId, 1719196953000,
+ genRandomData(EncryptMegOlmEventAction::maxRandomSize()));
+ auto plainText2 = makeEvent();
+ auto [encrypted2, sessionId2] = client.megOlmEncrypt(plainText2, roomId, 1719196953000,
+ genRandomData(EncryptMegOlmEventAction::maxRandomSize()));
+ auto j = encrypted2.originalJson().get();
+ // simulate an undecryptable event with a known session id
+ j["content"]["/////"];
+ encrypted2 = Event(j);
+ // simulate an undecryptable event with an unknown session id
+ auto plainText3 = makeEvent();
+ auto [encrypted3, sessionId3] = client.megOlmEncrypt(plainText3, roomId, 1719196953000,
+ genRandomData(EncryptMegOlmEventAction::maxRandomSize()));
+ j["content"]["session_id"] = "some-session-id";
+ encrypted3 = Event(j);
+
+ auto events = EventList{
+ makeEvent(),
+ makeEvent(),
+ encrypted,
+ encrypted2,
+ encrypted3,
+ };
+
+ withRoomTimeline(events)(room);
+ withRoom(room)(client);
+
+ auto nextClient = tryDecryptEvents(client);
+ auto decryptedEvent = nextClient.roomList.rooms[roomId].messages[encrypted.id()];
+ REQUIRE(decryptedEvent.encrypted());
+ REQUIRE(decryptedEvent.decrypted());
+ REQUIRE(decryptedEvent.type() == plainText.type());
+ REQUIRE(decryptedEvent.content() == plainText.content());
+
+ auto decryptedEvent2 = nextClient.roomList.rooms[roomId].messages[encrypted2.id()];
+ REQUIRE(decryptedEvent2.encrypted());
+ REQUIRE(!decryptedEvent2.decrypted());
+
+ auto decryptedEvent3 = nextClient.roomList.rooms[roomId].messages[encrypted2.id()];
+ REQUIRE(decryptedEvent3.encrypted());
+ REQUIRE(!decryptedEvent3.decrypted());
+
+ REQUIRE(nextClient.roomList.rooms[roomId].undecryptedEvents
+ ==
+ immer::map<std::string, immer::flex_vector<std::string>>{
+ {encrypted.originalJson().get()["content"]["session_id"], {encrypted2.id()}},
+ {encrypted3.originalJson().get()["content"]["session_id"], {encrypted3.id()}},
+ });
}

File Metadata

Mime Type
text/plain
Expires
Fri, Sep 20, 9:36 PM (21 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16831
Default Alt Text
D71.1726893362.diff (9 KB)

Event Timeline