Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85636557
D348.1786689124.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
9 KB
Referenced Files
None
Subscribers
None
D348.1786689124.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
@@ -315,7 +315,7 @@
auto takeOutRoomKeyEvents =
[&](auto e) {
- if (e.type() != "m.room_key") {
+ if (e.type() != "m.room_key" && e.type() != "m.forwarded_room_key") {
// Leave it as it is
return true;
}
@@ -330,6 +330,17 @@
return false;
}
+ if (e.type() == "m.forwarded_room_key") {
+ // only accept forwarded room keys of verified devices of the same user
+ auto [st, sendingDev] = m.deviceLists.findByOlmEvent(e);
+ if (!(st == DeviceListTracker::InTracker
+ && e.sender() == m.userId
+ && sendingDev.trustLevel >= Verified)) {
+ kzo.client.info() << "Forwarded key is not from verified devices of the same user. Ignoring." << std::endl;
+ return false;
+ }
+ }
+
try {
auto content = e.content();
std::string roomId = content.get().at("room_id");
@@ -340,7 +351,7 @@
auto k = KeyOfGroupSession{roomId, sessionId};
- std::string ed25519Key = e.decryptedJson().get().at("keys").at(ed25519);
+ std::string ed25519Key = e.type() == "m.forwarded_room_key" ? e.decryptedJson().get().at("content").at("sender_claimed_ed25519_key") : e.decryptedJson().get().at("keys").at(ed25519);
if (m.withCrypto([&](auto &c) { return c.createInboundGroupSession(k, sessionKey, ed25519Key); })) {
return false; // such that this event is removed
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
@@ -40,24 +40,52 @@
return json::parse(std::get<Bytes>(next.nextJobs[0].requestBody()))["device_keys"];
}
+static void queryEachOtherKeys(ClientModel &m1, ClientModel &m2)
+{
+ auto queryOne = [](ClientModel &a, ClientModel &b) {
+ auto queryKeysRespJson = json{
+ {"device_keys", {{a.userId, {
+ {a.deviceId, makeDeviceInfo(a)}
+ }}}},
+ };
+ std::tie(b, std::ignore) = processResponse(b, QueryKeysResponse(
+ makeResponse("QueryKeys", withResponseJsonBody(queryKeysRespJson)
+ | withResponseDataKV("deviceKeys", json::object({
+ {a.userId, {a.deviceId}}
+ })))
+ ));
+ };
+ queryOne(m1, m2);
+ queryOne(m2, m1);
+}
+
+static void createAndClaimOneTimeKey(ClientModel &sender, ClientModel &receiver)
+{
+ auto oneTimeKeys = receiver.withCrypto([](Crypto &c) {
+ c.genOneTimeKeysWithRandom(genRandomData(Crypto::genOneTimeKeysRandomSize(1)), 1);
+ auto k = c.unpublishedOneTimeKeys();
+ c.markOneTimeKeysAsPublished();
+ return k;
+ });
+ auto cv25519Key = oneTimeKeys["curve25519"].items().begin().value().template get<std::string>();
+ sender.withCrypto([&](auto &c) {
+ c.createOutboundSessionWithRandom(
+ genRandomData(Crypto::createOutboundSessionRandomSize()),
+ receiver.constCrypto().curve25519IdentityKey(), cv25519Key);
+ });
+}
+
static CreateE2EESessionResult createE2EESession()
{
auto r1Crypto = makeCrypto();
- r1Crypto.genOneTimeKeysWithRandom(genRandomData(Crypto::genOneTimeKeysRandomSize(1)), 1);
auto r1 = makeClient(withCrypto(r1Crypto));
r1.userId = "@receiver:example.com";
r1.deviceId = "device1";
auto r2Crypto = makeCrypto();
- r2Crypto.genOneTimeKeysWithRandom(genRandomData(Crypto::genOneTimeKeysRandomSize(1)), 1);
auto r2 = makeClient(withCrypto(r2Crypto));
r2.userId = "@receiver:example.com";
r2.deviceId = "device2";
- auto oneTimeKeys1 = r1Crypto.unpublishedOneTimeKeys();
- auto cv25519Key1 = oneTimeKeys1["curve25519"].items().begin().value().template get<std::string>();
- auto oneTimeKeys2 = r2Crypto.unpublishedOneTimeKeys();
- auto cv25519Key2 = oneTimeKeys2["curve25519"].items().begin().value().template get<std::string>();
-
auto queryKeysRespJsonSender = json{
{"device_keys", {{"@receiver:example.com", {
{"device1", makeDeviceInfo(r1)},
@@ -69,28 +97,12 @@
auto client = makeClient(withCrypto(makeCrypto()));
client.userId = "@sender:example.com";
client.deviceId = "device1";
- auto queryKeysRespJsonReceiver = json{
- {"device_keys", {{"@sender:example.com", {
- {"device1", makeDeviceInfo(client)}
- }}}},
- };
- std::tie(client, std::ignore) = processResponse(client, QueryKeysResponse(
- makeResponse("QueryKeys", withResponseJsonBody(queryKeysRespJsonSender)
- | withResponseDataKV("deviceKeys", json::object({{"@receiver:example.com", json::array()}})))
- ));
- std::tie(r1, std::ignore) = processResponse(r1, QueryKeysResponse(
- makeResponse("QueryKeys", withResponseJsonBody(queryKeysRespJsonReceiver)
- | withResponseDataKV("deviceKeys", json::object({{"@sender:example.com", json::array()}})))
- ));
- std::tie(r2, std::ignore) = processResponse(r2, QueryKeysResponse(
- makeResponse("QueryKeys", withResponseJsonBody(queryKeysRespJsonReceiver)
- | withResponseDataKV("deviceKeys", json::object({{"@sender:example.com", json::array()}})))
- ));
+ queryEachOtherKeys(client, r1);
+ queryEachOtherKeys(client, r2);
- // Claim keys
- client.withCrypto([&](auto &c) { c.createOutboundSessionWithRandom(genRandomData(Crypto::createOutboundSessionRandomSize()), r1Crypto.curve25519IdentityKey(), cv25519Key1); });
- client.withCrypto([&](auto &c) { c.createOutboundSessionWithRandom(genRandomData(Crypto::createOutboundSessionRandomSize()), r2Crypto.curve25519IdentityKey(), cv25519Key2); });
+ createAndClaimOneTimeKey(client, r1);
+ createAndClaimOneTimeKey(client, r2);
return {
r1,
@@ -335,6 +347,86 @@
}
}
+TEST_CASE("tryDecryptEvents() will process forwarded room keys", "[client][encryption]")
+{
+ auto r = createE2EESession();
+ auto sender = r.sender;
+ auto receiver1 = r.receiver1;
+ auto receiver2 = r.receiver2;
+ std::string roomId = "!someroom:example.com";
+
+ Event plainText = json{
+ {"type", "m.room.message"},
+ {"content", {
+ {"body", "mew"},
+ }},
+ {"room_id", roomId},
+ };
+
+ queryEachOtherKeys(receiver1, receiver2);
+ createAndClaimOneTimeKey(receiver1, receiver2);
+
+ auto [encryptedRoomEvent, encryptedKeyEvent, unencryptedKeyEvent] = createMegOlmSession(sender, receiver1, roomId, plainText);
+
+ auto sessionId = unencryptedKeyEvent.content().get().at("session_id").template get<std::string>();
+ auto sessionKey = unencryptedKeyEvent.content().get().at("session_key").template get<std::string>();
+
+ auto forwardKeyEventJson = json{
+ {"content", {{"algorithm", CryptoConstants::megOlmAlgo},
+ {"forwarding_curve25519_key_chain", json::array()},
+ {"sender_claimed_ed25519_key", sender.constCrypto().ed25519IdentityKey()},
+ {"sender_key", sender.constCrypto().curve25519IdentityKey()},
+ {"room_id", roomId},
+ {"session_id", sessionId},
+ {"session_key", sessionKey}}},
+ {"type", "m.forwarded_room_key"}
+ };
+
+ auto encryptToReceiver = [receiver2](ClientModel &sender, const json &plain) {
+ auto res = sender.olmEncryptSplit(Event(plain),
+ {{receiver2.userId, {receiver2.deviceId}}},
+ genRandomData(Crypto::encryptOlmMaxRandomSize()));
+
+ auto e = res[receiver2.userId][receiver2.deviceId].originalJson().get();
+ e["sender"] = sender.userId;
+ return e;
+ };
+
+ SECTION("Process forwarded key") {
+ std::tie(receiver2, std::ignore) = updateClient(receiver2, SetDeviceTrustLevelAction{receiver1.userId, receiver1.deviceId, Verified});
+ auto resp = syncResponseFromToDevice(encryptToReceiver(receiver1, forwardKeyEventJson));
+ auto [next, _dontCareEffect] = ClientModel::update(receiver2, ProcessResponseAction{resp});
+
+ REQUIRE(next.constCrypto().hasInboundGroupSession(KeyOfGroupSession{roomId, sessionId}));
+ REQUIRE(next.toDevice.size() == 0);
+ }
+
+ SECTION("Ignore forwarded key from other users") {
+ std::tie(receiver2, std::ignore) = updateClient(receiver2, SetDeviceTrustLevelAction{sender.userId, sender.deviceId, Verified});
+ auto resp = syncResponseFromToDevice(encryptToReceiver(sender, forwardKeyEventJson));
+ auto [next, _dontCareEffect] = ClientModel::update(receiver2, ProcessResponseAction{resp});
+
+ REQUIRE(!next.constCrypto().hasInboundGroupSession(KeyOfGroupSession{roomId, sessionId}));
+ REQUIRE(next.toDevice.size() == 0);
+ }
+
+ SECTION("Ignore forwarded key from unverified devices") {
+ auto resp = syncResponseFromToDevice(encryptToReceiver(receiver1, forwardKeyEventJson));
+ auto [next, _dontCareEffect] = ClientModel::update(receiver2, ProcessResponseAction{resp});
+
+ REQUIRE(!next.constCrypto().hasInboundGroupSession(KeyOfGroupSession{roomId, sessionId}));
+ REQUIRE(next.toDevice.size() == 0);
+ }
+
+ SECTION("Reject unencrypted key event") {
+ auto resp = syncResponseFromToDevice(forwardKeyEventJson);
+ auto [next, _dontCareEffect] = ClientModel::update(receiver2, ProcessResponseAction{resp});
+
+ REQUIRE(!next.constCrypto().hasInboundGroupSession(KeyOfGroupSession{roomId, sessionId}));
+ REQUIRE(next.toDevice.size() == 0);
+ }
+}
+
TEST_CASE("tryDecryptEvents() will update room.undecryptedEvents", "[client][encryption]")
{
auto roomId = "!someroom:example.com";
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Aug 13, 11:32 PM (22 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1728881
Default Alt Text
D348.1786689124.diff (9 KB)
Attached To
Mode
D348: Process incoming forwarded room key events
Attached
Detach File
Event Timeline
Log In to Comment