Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F114525
D26.1732558651.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
6 KB
Referenced Files
None
Subscribers
None
D26.1732558651.diff
View Options
diff --git a/src/client/client-model.hpp b/src/client/client-model.hpp
--- a/src/client/client-model.hpp
+++ b/src/client/client-model.hpp
@@ -97,8 +97,13 @@
std::pair<Event, std::optional<std::string> /* sessionKey */>
megOlmEncrypt(Event e, std::string roomId, Timestamp timeMs, RandomData random);
/// precondition: the one-time keys for those devices must already be claimed
+ [[deprecated("Use olmEncryptSplit instead")]]
Event olmEncrypt(Event e, immer::map<std::string, immer::flex_vector<std::string>> userIdToDeviceIdMap, RandomData random);
+ /// precondition: the one-time keys for those devices must already be claimed
+ /// @return A map from user id to device id to encrypted event for that device
+ immer::map<std::string, immer::map<std::string, Event>> olmEncryptSplit(Event e, immer::map<std::string, immer::flex_vector<std::string>> userIdToDeviceIdMap, RandomData random);
+
/// @return number of one-time keys we need to generate
std::size_t numOneTimeKeysNeeded() const;
diff --git a/src/client/client-model.cpp b/src/client/client-model.cpp
--- a/src/client/client-model.cpp
+++ b/src/client/client-model.cpp
@@ -225,6 +225,62 @@
return Event(JsonWrap(encJson));
}
+ immer::map<std::string, immer::map<std::string, Event>> ClientModel::olmEncryptSplit(
+ Event e,
+ immer::map<std::string, immer::flex_vector<std::string>> userIdToDeviceIdMap, RandomData random)
+ {
+ using ResT = immer::map<std::string, immer::map<std::string, Event>>;
+ if (!crypto) {
+ kzo.client.dbg() << "We do not have e2ee, so do not encrypt events" << std::endl;
+ return ResT{};
+ }
+
+ if (e.encrypted()) {
+ kzo.client.dbg() << "The event is already encrypted. Ignoring it." << std::endl;
+ return ResT{};
+ }
+
+ auto origJson = e.originalJson().get();
+
+ auto encJson = json::object();
+ encJson["content"] = json{
+ {"algorithm", CryptoConstants::olmAlgo},
+ {"ciphertext", json::object()},
+ {"sender_key", constCrypto().curve25519IdentityKey()},
+ };
+
+ encJson["type"] = "m.room.encrypted";
+
+ ResT messages;
+
+ for (auto [userId, devices] : userIdToDeviceIdMap) {
+ messages = std::move(messages).set(userId, immer::map<std::string, Event>());
+ for (auto dev : devices) {
+ auto devInfoOpt = deviceLists.get(userId, dev);
+ if (! devInfoOpt) {
+ continue;
+ }
+ auto devInfo = devInfoOpt.value();
+ auto jsonForThisDevice = origJson;
+ jsonForThisDevice["sender"] = this->userId;
+ jsonForThisDevice["recipient"] = userId;
+ jsonForThisDevice["recipient_keys"] = json{
+ {CryptoConstants::ed25519, devInfo.ed25519Key}
+ };
+ jsonForThisDevice["keys"] = json{
+ {CryptoConstants::ed25519, constCrypto().ed25519IdentityKey()}
+ };
+ auto thisEventJson = encJson;
+ thisEventJson["content"]["ciphertext"]
+ .merge_patch(withCrypto([&](auto &c) { return c.encryptOlmWithRandom(random, jsonForThisDevice, devInfo.curve25519Key); }));
+ random.erase(0, Crypto::encryptOlmMaxRandomSize());
+ messages = setIn(std::move(messages), Event(thisEventJson), userId, dev);
+ }
+ }
+
+ return messages;
+ }
+
immer::flex_vector<std::string /* deviceId */> ClientModel::devicesToSendKeys(std::string userId) const
{
auto trustLevelNeeded = this->trustLevelNeededToSendKeys;
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
@@ -7,6 +7,7 @@
#include <libkazv-config.hpp>
#include <catch2/catch_all.hpp>
+#include <client/actions/encryption.hpp>
#include <client-model.hpp>
#include "client-test-util.hpp"
@@ -91,3 +92,51 @@
REQUIRE(!encryptedEvent.originalJson().get()["content"].contains("m.relates_to"));
}
+
+TEST_CASE("ClientModel::olmEncryptSplit()", "[client][encryption]")
+{
+ auto makeDeviceInfo = [](const Crypto &crypto, std::string userId, std::string deviceId) {
+ auto client = makeClient(withCrypto(crypto));
+ client.userId = userId;
+ client.deviceId = deviceId;
+ auto [next, _] = updateClient(client, UploadIdentityKeysAction{});
+ return json::parse(std::get<Bytes>(next.nextJobs[0].requestBody()))["device_keys"];
+ };
+
+ auto receiver1 = makeCrypto();
+ receiver1.genOneTimeKeysWithRandom(genRandomData(Crypto::genOneTimeKeysRandomSize(1)), 1);
+ auto receiver2 = makeCrypto();
+ receiver2.genOneTimeKeysWithRandom(genRandomData(Crypto::genOneTimeKeysRandomSize(1)), 1);
+
+ auto oneTimeKeys1 = receiver1.unpublishedOneTimeKeys();
+ auto cv25519Key1 = oneTimeKeys1["curve25519"].items().begin().value().template get<std::string>();
+ auto oneTimeKeys2 = receiver2.unpublishedOneTimeKeys();
+ auto cv25519Key2 = oneTimeKeys2["curve25519"].items().begin().value().template get<std::string>();
+
+ auto queryKeysRespJson = json{
+ {"device_keys", {{"@receiver:example.com", {
+ {"device1", makeDeviceInfo(receiver1, "@receiver:example.com", "device1")},
+ {"device2", makeDeviceInfo(receiver2, "@receiver:example.com", "device2")},
+ }}}},
+ };
+
+ // Query keys
+ auto client = makeClient(withCrypto(makeCrypto()));
+ std::tie(client, std::ignore) = processResponse(client, QueryKeysResponse(
+ makeResponse("QueryKeys", withResponseJsonBody(queryKeysRespJson))
+ ));
+
+ // Claim keys
+ client.withCrypto([&](auto &c) { c.createOutboundSessionWithRandom(genRandomData(Crypto::createOutboundSessionRandomSize()), receiver1.curve25519IdentityKey(), cv25519Key1); });
+ client.withCrypto([&](auto &c) { c.createOutboundSessionWithRandom(genRandomData(Crypto::createOutboundSessionRandomSize()), receiver2.curve25519IdentityKey(), cv25519Key2); });
+
+ // encrypt
+ auto res = client.olmEncryptSplit(Event(json::object()),
+ {{"@receiver:example.com", {"device1", "device2"}}},
+ genRandomData(Crypto::encryptOlmMaxRandomSize() * 2));
+
+ REQUIRE(res["@receiver:example.com"]["device1"].originalJson().get().at("content").at("ciphertext").size() == 1);
+ REQUIRE(res["@receiver:example.com"]["device1"].originalJson().get().at("content").at("ciphertext").contains(receiver1.curve25519IdentityKey()));
+ REQUIRE(res["@receiver:example.com"]["device2"].originalJson().get().at("content").at("ciphertext").size() == 1);
+ REQUIRE(res["@receiver:example.com"]["device2"].originalJson().get().at("content").at("ciphertext").contains(receiver2.curve25519IdentityKey()));
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Nov 25, 10:17 AM (13 h, 47 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39862
Default Alt Text
D26.1732558651.diff (6 KB)
Attached To
Mode
D26: Support encrypting olm separately for each device
Attached
Detach File
Event Timeline
Log In to Comment