Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F116545
D71.1733020985.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
10 KB
Referenced Files
None
Subscribers
None
D71.1733020985.diff
View Options
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/client-test.cpp b/src/tests/client/client-test.cpp
--- a/src/tests/client/client-test.cpp
+++ b/src/tests/client/client-test.cpp
@@ -27,17 +27,27 @@
TEST_CASE("Modifying Crypto in Client when it is not unique should invoke Crypto's copy constructor", "[client][copy]")
{
- ClientModel m = makeClient(
+ auto roomId = "!someroom:example.com";
+ auto room = makeRoom(
+ withRoomEncrypted(true)
+ | withRoomId(roomId)
+ );
+ auto client = makeClient(
withCrypto(makeCrypto())
- | withRoom(makeRoom(
- withRoomEncrypted(true)
- | withRoomTimeline({
- makeEvent(withEventType("m.room.encrypted"))
- })))
+ | withRoom(room)
);
- auto m2 = m;
+ auto plainText = makeEvent();
+ auto [encrypted, sessionId] = client.megOlmEncrypt(plainText, roomId, 1719196953000,
+ genRandomData(EncryptMegOlmEventAction::maxRandomSize()));
+
+ withRoomTimeline({
+ encrypted
+ })(room);
+ withRoom(room)(client);
+
+ auto m2 = client;
- auto m3 = tryDecryptEvents(std::move(m));
+ auto m3 = tryDecryptEvents(std::move(client));
REQUIRE(!(m3.crypto.value().get() == m2.crypto.value().get()));
}
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
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 30, 6:43 PM (15 h, 55 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41802
Default Alt Text
D71.1733020985.diff (10 KB)
Attached To
Mode
D71: Make tryDecryptEvents() use and update Room::undecryptedEvents
Attached
Detach File
Event Timeline
Log In to Comment