Page MenuHomePhorge

D348.1786689124.diff
No OneTemporary

Size
9 KB
Referenced Files
None
Subscribers
None

D348.1786689124.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
@@ -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

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)

Event Timeline