diff --git a/.gitignore b/.gitignore index 084df1ff85833385f1422cb775f2cdde66a426f4..4e360c54a99d0b890e1af422ed1bb7b05b8d075b 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ compile_commands.json .ccls-cache/ .clangd/ .*.swp +.vscode # Synapse data data/ diff --git a/examples/crypto_bot.cpp b/examples/crypto_bot.cpp index a6a817e210ca1e44f7ba61ef6746e8cb624efbe9..44ec7782f85f84da1af3286fb9e86d7531129dd7 100644 --- a/examples/crypto_bot.cpp +++ b/examples/crypto_bot.cpp @@ -94,7 +94,7 @@ void mark_encrypted_room(const RoomId &id); void -handle_to_device_msgs(const std::vector<nlohmann::json> &to_device); +handle_to_device_msgs(const mtx::responses::ToDevice &to_device); struct OutboundSessionData { @@ -895,16 +895,16 @@ get_device_keys(const UserId &user) } void -handle_to_device_msgs(const std::vector<nlohmann::json> &msgs) +handle_to_device_msgs(const mtx::responses::ToDevice &msgs) { - if (!msgs.empty()) - console->info("inspecting {} to_device messages", msgs.size()); + if (!msgs.events.empty()) + console->info("inspecting {} to_device messages", msgs.events.size()); - for (const auto &msg : msgs) { - console->info(msg.dump(2)); + for (const auto &msg : msgs.events) { + console->info(std::visit(mtx::events::DeviceEventVisitor{}, msg).dump(2)); try { - OlmMessage olm_msg = msg; + OlmMessage olm_msg = std::visit(DeviceEventVisitor{}, msg); decrypt_olm_message(std::move(olm_msg)); } catch (const nlohmann::json::exception &e) { console->warn("parsing error for olm message: {}", e.what()); diff --git a/include/mtx/events.hpp b/include/mtx/events.hpp index 692540dc5d19528015f7224f16121ccbffb0e52d..881c5c4284500eaba06b492e5512537493b414e6 100644 --- a/include/mtx/events.hpp +++ b/include/mtx/events.hpp @@ -6,7 +6,6 @@ #include "mtx/identifiers.hpp" using json = nlohmann::json; - namespace mtx { namespace events { @@ -26,6 +25,8 @@ enum class EventType KeyVerificationMac, /// m.reaction, Reaction, + /// m.room_key + RoomKey, /// m.room_key_request RoomKeyRequest, /// m.room.aliases @@ -122,6 +123,9 @@ to_json(json &obj, const Event<Content> &event) case EventType::Reaction: obj["type"] = "m.reaction"; break; + case EventType::RoomKey: + obj["type"] = "m.room_key"; + break; case EventType::RoomKeyRequest: obj["type"] = "m.room_key_request"; break; @@ -199,6 +203,34 @@ from_json(const json &obj, Event<Content> &event) event.type = getEventType(obj.at("type").get<std::string>()); } +//! Extension of the Event type for device events. +template<class Content> +struct DeviceEvent : public Event<Content> +{ + std::string sender; +}; + +template<class Content> +void +from_json(const json &obj, DeviceEvent<Content> &event) +{ + Event<Content> base_event = event; + from_json(obj, base_event); + event.content = base_event.content; + event.type = base_event.type; + event.sender = obj.at("sender"); +} + +template<class Content> +void +to_json(json &obj, const DeviceEvent<Content> &event) +{ + Event<Content> base_event = event; + to_json(obj, base_event); + + obj["sender"] = event.sender; +} + struct UnsignedData { //! The time in milliseconds that has elapsed since the event was sent. diff --git a/include/mtx/events/collections.hpp b/include/mtx/events/collections.hpp index 1af77cb1bc6853a52ee5deddcfc1650adef24685..f9cbd735fbd45bc4fe0f5a819fd0a7dead1d7f9f 100644 --- a/include/mtx/events/collections.hpp +++ b/include/mtx/events/collections.hpp @@ -41,6 +41,18 @@ namespace account_data = mtx::events::account_data; namespace states = mtx::events::state; namespace msgs = mtx::events::msg; +//! Collection of key verification events +using DeviceEvents = std::variant<events::DeviceEvent<msgs::RoomKey>, + events::DeviceEvent<msgs::KeyRequest>, + events::DeviceEvent<msgs::OlmEncrypted>, + events::DeviceEvent<msgs::Encrypted>, + events::DeviceEvent<msgs::KeyVerificationRequest>, + events::DeviceEvent<msgs::KeyVerificationStart>, + events::DeviceEvent<msgs::KeyVerificationAccept>, + events::DeviceEvent<msgs::KeyVerificationCancel>, + events::DeviceEvent<msgs::KeyVerificationKey>, + events::DeviceEvent<msgs::KeyVerificationMac>>; + //! Collection of room specific account data using RoomAccountDataEvents = std::variant<events::Event<account_data::Tag>, events::Event<pushrules::GlobalRuleset>>; diff --git a/include/mtx/events/encrypted.hpp b/include/mtx/events/encrypted.hpp index 5001da4726a26abbe3ddfed8567f587a7714bdf2..55306c773cd5c05aa1625f0fbc031501b9ca5f44 100644 --- a/include/mtx/events/encrypted.hpp +++ b/include/mtx/events/encrypted.hpp @@ -141,11 +141,6 @@ struct KeyRequest std::string request_id; //! The device requesting the keys. std::string requesting_device_id; - - //! The user that send this event. - std::string sender; - //! The type of the event. - mtx::events::EventType type; }; void @@ -323,5 +318,74 @@ void to_json(nlohmann::json &obj, const KeyVerificationMac &event); } // namespace msg +struct DeviceEventVisitor +{ + nlohmann::json operator()(const DeviceEvent<mtx::events::msg::RoomKey> &roomKey) + { + json j; + mtx::events::to_json(j, roomKey); + return j; + } + nlohmann::json operator()(const DeviceEvent<mtx::events::msg::KeyRequest> &keyReq) + { + json j; + mtx::events::to_json(j, keyReq); + return j; + } + nlohmann::json operator()(const DeviceEvent<mtx::events::msg::OlmEncrypted> &olmEnc) + { + json j; + mtx::events::to_json(j, olmEnc); + return j; + } + nlohmann::json operator()(const DeviceEvent<mtx::events::msg::Encrypted> &enc) + { + json j; + mtx::events::to_json(j, enc); + return j; + } + nlohmann::json operator()( + const DeviceEvent<mtx::events::msg::KeyVerificationRequest> &keyVerificationRequest) + { + json j; + mtx::events::to_json(j, keyVerificationRequest); + return j; + } + nlohmann::json operator()( + const DeviceEvent<mtx::events::msg::KeyVerificationAccept> &keyVerificationAccept) + { + json j; + mtx::events::to_json(j, keyVerificationAccept); + return j; + } + nlohmann::json operator()( + const DeviceEvent<mtx::events::msg::KeyVerificationStart> &keyVerificationStart) + { + json j; + mtx::events::to_json(j, keyVerificationStart); + return j; + } + nlohmann::json operator()( + const DeviceEvent<mtx::events::msg::KeyVerificationCancel> &KeyVerificationCancel) + { + json j; + mtx::events::to_json(j, KeyVerificationCancel); + return j; + } + nlohmann::json operator()( + const DeviceEvent<mtx::events::msg::KeyVerificationKey> &keyVerificationKey) + { + json j; + mtx::events::to_json(j, keyVerificationKey); + return j; + } + nlohmann::json operator()( + const DeviceEvent<mtx::events::msg::KeyVerificationMac> &keyVerificationMac) + { + json j; + mtx::events::to_json(j, keyVerificationMac); + return j; + } +}; } // namespace events } // namespace mtx diff --git a/include/mtx/identifiers.hpp b/include/mtx/identifiers.hpp index 077099c42b6e0008a5887254716bb2c5bf052003..b6ef59e576dd2d56055b2f4db7b0c666095708be 100644 --- a/include/mtx/identifiers.hpp +++ b/include/mtx/identifiers.hpp @@ -59,6 +59,7 @@ class User : public ID public: template<typename Identifier> friend Identifier parse(const std::string &id); + friend bool operator<(const User &a, const User &b) { return a.id_ < b.id_; } private: std::string sigil = "@"; diff --git a/include/mtx/requests.hpp b/include/mtx/requests.hpp index 8836497a1e014b25f762e60001d0d764d3cddef6..cd636468ae2ce2824fb9952f1d5159d59fbbed8b 100644 --- a/include/mtx/requests.hpp +++ b/include/mtx/requests.hpp @@ -3,6 +3,7 @@ #include <string> #include <mtx/common.hpp> +#include <mtx/events/collections.hpp> #include <nlohmann/json.hpp> using json = nlohmann::json; @@ -84,6 +85,10 @@ struct Login void to_json(json &obj, const Login &request); +//! Request payload for the `PUT /_matrix/client/r0/sendToDevice/{eventType}/{transcationID}` +template<typename EventContent> +using ToDeviceMessages = std::map<mtx::identifiers::User, std::map<std::string, EventContent>>; + //! Request payload for the `POST /_matrix/client/r0/profile/{userId}/avatar_url` endpoint. struct AvatarUrl { diff --git a/include/mtx/responses/common.hpp b/include/mtx/responses/common.hpp index 6d8a36ccdd01a3c1d60e0b53df558557258efef0..06deb28f550c082bf61bb6bec2349ceab3832c84 100644 --- a/include/mtx/responses/common.hpp +++ b/include/mtx/responses/common.hpp @@ -44,6 +44,7 @@ using RoomAccountDataEvents = std::vector<mtx::events::collections::RoomAccountD using TimelineEvents = std::vector<mtx::events::collections::TimelineEvents>; using StateEvents = std::vector<mtx::events::collections::StateEvents>; using StrippedEvents = std::vector<mtx::events::collections::StrippedEvents>; +using DeviceEvents = std::vector<mtx::events::collections::DeviceEvents>; namespace states = mtx::events::state; namespace msgs = mtx::events::msg; @@ -68,6 +69,9 @@ parse_state_events(const nlohmann::json &events, StateEvents &container); void parse_stripped_events(const nlohmann::json &events, StrippedEvents &container); + +void +parse_device_events(const nlohmann::json &events, DeviceEvents &container); } } } diff --git a/include/mtx/responses/sync.hpp b/include/mtx/responses/sync.hpp index 151c240cc0d04ec1220158a69ec08bfbb50c1c7e..0d7f04b93e131fe9d16f6d9611f9e079b44ab5cc 100644 --- a/include/mtx/responses/sync.hpp +++ b/include/mtx/responses/sync.hpp @@ -151,6 +151,16 @@ struct DeviceLists void from_json(const nlohmann::json &obj, DeviceLists &device_lists); +//! Information on to_device events in sync. +struct ToDevice +{ + //! Information on the send-to-device messages for the client device. + std::vector<events::collections::DeviceEvents> events; +}; + +void +from_json(const nlohmann::json &obj, ToDevice &to_device); + //! Response from the `GET /_matrix/client/r0/sync` endpoint. struct Sync { @@ -159,7 +169,7 @@ struct Sync //! Updates to rooms. Rooms rooms; //! Information on the send-to-device messages for the client device. - std::vector<nlohmann::json> to_device; + ToDevice to_device; /* Presence presence; */ /* Groups groups; */ //! Information on end-to-end device updates, diff --git a/include/mtxclient/http/client.hpp b/include/mtxclient/http/client.hpp index 5f02ee0919f426b26d84431f3b785e58c6fdcbd4..0e4cc2f06d679121c88a90a4d2a496baf7e1679b 100644 --- a/include/mtxclient/http/client.hpp +++ b/include/mtxclient/http/client.hpp @@ -10,7 +10,9 @@ #include "mtx/events.hpp" // for EventType, to_string, json #include "mtx/events/collections.hpp" // for TimelineEvents #include "mtx/identifiers.hpp" // for User +#include "mtx/identifiers.hpp" // for Class user #include "mtx/pushrules.hpp" +#include "mtx/requests.hpp" #include "mtx/responses/empty.hpp" // for Empty, Logout, RoomInvite #include "mtxclient/http/errors.hpp" // for ClientError #include "mtxclient/utils.hpp" // for random_token, url_encode, des... @@ -403,6 +405,20 @@ public: { send_to_device(event_type, generate_txn_id(), body, cb); } + //! Send send-to-device events to a set of client devices with a specified transaction id. + template<typename EventContent, mtx::events::EventType Event> + void send_to_device( + const std::string &txid, + const std::map<mtx::identifiers::User, std::map<std::string, EventContent>> &messages, + ErrCallback callback) + { + json j; + for (const auto &[user, deviceToMessage] : messages) + for (const auto &[deviceid, message] : deviceToMessage) + j["messages"][user.to_string()][deviceid] = message; + + send_to_device(mtx::events::to_string(Event), txid, j, callback); + } // // Group related endpoints. diff --git a/lib/http/client.cpp b/lib/http/client.cpp index deaed1c4eb0fa69de69fb759baa7f9149a9598e0..8ce637a208c9fd2e8411876b60e03d8a2da099b9 100644 --- a/lib/http/client.cpp +++ b/lib/http/client.cpp @@ -18,8 +18,10 @@ #include "mtxclient/http/session.hpp" #include "mtxclient/utils.hpp" +#include "mtx/identifiers.hpp" #include "mtx/requests.hpp" #include "mtx/responses.hpp" +#include <iostream> using namespace mtx::http; using namespace boost::beast; diff --git a/lib/structs/events.cpp b/lib/structs/events.cpp index b0f05cf77f6ae60ae4dda0763eeaee0b42ec086b..95f4de5fbe103199b6cdb6fdde6b3c7f8aabee61 100644 --- a/lib/structs/events.cpp +++ b/lib/structs/events.cpp @@ -86,6 +86,8 @@ to_string(EventType type) return "m.key.verification.mac"; case EventType::Reaction: return "m.reaction"; + case EventType::RoomKey: + return "m.room_key"; case EventType::RoomKeyRequest: return "m.room_key_request"; case EventType::RoomAliases: @@ -178,5 +180,6 @@ getMessageType(const json &obj) return getMessageType(obj.at("msgtype").get<std::string>()); } + } -} +} \ No newline at end of file diff --git a/lib/structs/events/collections.cpp b/lib/structs/events/collections.cpp index de087f5e16f7894e67bd87a93e541c6bc1be8f7e..d1babefc432cb6c2012a9ec4208d3c905d354a5a 100644 --- a/lib/structs/events/collections.cpp +++ b/lib/structs/events/collections.cpp @@ -139,6 +139,7 @@ from_json(const json &obj, TimelineEvent &e) break; } case events::EventType::RoomPinnedEvents: + case events::EventType::RoomKey: // not part of the timeline case events::EventType::RoomKeyRequest: // Not part of the timeline case events::EventType::Tag: // Not part of the timeline case events::EventType::PushRules: // Not part of the timeline diff --git a/lib/structs/events/encrypted.cpp b/lib/structs/events/encrypted.cpp index bda3466e7cf1b4c7720858bf0e2e25c18b532f80..a8dfda47079327b510a8ba993e6e39120b5e933b 100644 --- a/lib/structs/events/encrypted.cpp +++ b/lib/structs/events/encrypted.cpp @@ -137,18 +137,16 @@ to_json(json &obj, const RoomKey &event) void from_json(const json &obj, KeyRequest &event) { - event.sender = obj.at("sender"); - event.type = mtx::events::getEventType(obj.at("type").get<std::string>()); - event.request_id = obj.at("content").at("request_id"); - event.requesting_device_id = obj.at("content").at("requesting_device_id"); + event.request_id = obj.at("request_id"); + event.requesting_device_id = obj.at("requesting_device_id"); - auto action = obj.at("content").at("action").get<std::string>(); + auto action = obj.at("action").get<std::string>(); if (action == "request") { event.action = RequestAction::Request; - event.room_id = obj.at("content").at("body").at("room_id"); - event.sender_key = obj.at("content").at("body").at("sender_key"); - event.session_id = obj.at("content").at("body").at("session_id"); - event.algorithm = obj.at("content").at("body").at("algorithm"); + event.room_id = obj.at("body").at("room_id"); + event.sender_key = obj.at("body").at("sender_key"); + event.session_id = obj.at("body").at("session_id"); + event.algorithm = obj.at("body").at("algorithm"); } else if (action == "request_cancellation") { event.action = RequestAction::Cancellation; } @@ -159,27 +157,25 @@ to_json(json &obj, const KeyRequest &event) { obj = json::object(); - obj["sender"] = event.sender; - obj["type"] = to_string(event.type); - obj["content"] = json::object(); + obj = json::object(); - obj["content"]["request_id"] = event.request_id; - obj["content"]["requesting_device_id"] = event.requesting_device_id; + obj["request_id"] = event.request_id; + obj["requesting_device_id"] = event.requesting_device_id; switch (event.action) { case RequestAction::Request: { - obj["content"]["body"] = json::object(); + obj["body"] = json::object(); - obj["content"]["body"]["room_id"] = event.room_id; - obj["content"]["body"]["sender_key"] = event.sender_key; - obj["content"]["body"]["session_id"] = event.session_id; - obj["content"]["body"]["algorithm"] = "m.megolm.v1.aes-sha2"; + obj["body"]["room_id"] = event.room_id; + obj["body"]["sender_key"] = event.sender_key; + obj["body"]["session_id"] = event.session_id; + obj["body"]["algorithm"] = "m.megolm.v1.aes-sha2"; - obj["content"]["action"] = "request"; + obj["action"] = "request"; break; } case RequestAction::Cancellation: { - obj["content"]["action"] = "request_cancellation"; + obj["action"] = "request_cancellation"; break; } default: diff --git a/lib/structs/requests.cpp b/lib/structs/requests.cpp index 0ce5c75c0e0570f0662c850b48b3a6372a01a118..02386360abfc3596fca4eac08071fcce39682500 100644 --- a/lib/structs/requests.cpp +++ b/lib/structs/requests.cpp @@ -1,6 +1,10 @@ #include "mtx/requests.hpp" +#include "mtx/events/collections.hpp" +#include "mtx/events/encrypted.hpp" +#include <iostream> using json = nlohmann::json; +using namespace mtx::events::collections; namespace mtx { namespace requests { diff --git a/lib/structs/responses/common.cpp b/lib/structs/responses/common.cpp index d067f6f882a24757a190f89bdd0eff0dfd3ba339..1b42c9571fc55f9754caca0cc78db14770dd7177 100644 --- a/lib/structs/responses/common.cpp +++ b/lib/structs/responses/common.cpp @@ -22,6 +22,7 @@ using json = nlohmann::json; using namespace mtx::events::account_data; using namespace mtx::events::state; +using namespace mtx::events::msg; namespace mtx { namespace responses { @@ -257,6 +258,7 @@ parse_room_account_data_events( case events::EventType::KeyVerificationKey: case events::EventType::KeyVerificationMac: case events::EventType::Reaction: + case events::EventType::RoomKey: // Not part of timeline or state case events::EventType::RoomKeyRequest: case events::EventType::RoomAliases: case events::EventType::RoomAvatar: @@ -561,6 +563,7 @@ parse_timeline_events(const json &events, break; } case events::EventType::RoomPinnedEvents: + case events::EventType::RoomKey: // Not part of timeline or state case events::EventType::RoomKeyRequest: // Not part of the timeline case events::EventType::Tag: // Not part of the timeline or state case events::EventType::PushRules: // Not part of the timeline or state @@ -576,6 +579,113 @@ parse_timeline_events(const json &events, } } +void +parse_device_events(const json &events, + std::vector<mtx::events::collections::DeviceEvents> &container) +{ + container.clear(); + container.reserve(events.size()); + for (const auto &e : events) { + const auto type = mtx::events::getEventType(e); + + switch (type) { + case events::EventType::RoomEncrypted: { + try { + const auto algo = + e.at("content").at("algorithm").get<std::string>(); + // Algorithm determines whether it's an olm or megolm event + if (algo == "m.olm.v1.curve25519-aes-sha2") { + container.emplace_back( + events::DeviceEvent<OlmEncrypted>(e)); + } else if (algo == "m.megolm.v1.aes-sha2") { + container.emplace_back(events::DeviceEvent<Encrypted>(e)); + } else { + log_error("Invalid m.room.encrypted algorithm", e); + continue; + } + } catch (json::exception &err) { + log_error(err, e); + } + + break; + } + case events::EventType::RoomKey: { + try { + container.emplace_back(events::DeviceEvent<RoomKey>(e)); + } catch (json::exception &err) { + log_error(err, e); + } + + break; + } + case events::EventType::RoomKeyRequest: { + try { + container.emplace_back(events::DeviceEvent<KeyRequest>(e)); + } catch (json::exception &err) { + log_error(err, e); + } + + break; + } + case events::EventType::KeyVerificationCancel: { + try { + container.emplace_back( + events::DeviceEvent<KeyVerificationCancel>(e)); + } catch (json::exception &err) { + log_error(err, e); + } + + break; + } + case events::EventType::KeyVerificationRequest: + try { + container.emplace_back( + events::DeviceEvent<KeyVerificationRequest>(e)); + } catch (json::exception &err) { + log_error(err, e); + } + + break; + case events::EventType::KeyVerificationStart: + try { + container.emplace_back( + events::DeviceEvent<KeyVerificationStart>(e)); + } catch (json::exception &err) { + log_error(err, e); + } + + break; + case events::EventType::KeyVerificationAccept: + try { + container.emplace_back( + events::DeviceEvent<KeyVerificationAccept>(e)); + } catch (json::exception &err) { + log_error(err, e); + } + + break; + case events::EventType::KeyVerificationKey: + try { + container.emplace_back(events::DeviceEvent<KeyVerificationKey>(e)); + } catch (json::exception &err) { + log_error(err, e); + } + + break; + case events::EventType::KeyVerificationMac: + try { + container.emplace_back(events::DeviceEvent<KeyVerificationMac>(e)); + } catch (json::exception &err) { + log_error(err, e); + } + + break; + default: + continue; + } + } +} + void parse_state_events(const json &events, std::vector<mtx::events::collections::StateEvents> &container) @@ -707,6 +817,7 @@ parse_state_events(const json &events, case events::EventType::Sticker: case events::EventType::Reaction: case events::EventType::RoomEncrypted: /* Does this need to be here? */ + case events::EventType::RoomKey: // Not part of timeline or state case events::EventType::RoomKeyRequest: // Not part of the timeline or state case events::EventType::RoomMessage: case events::EventType::RoomPinnedEvents: @@ -850,6 +961,7 @@ parse_stripped_events(const json &events, case events::EventType::RoomEncryption: case events::EventType::RoomMessage: case events::EventType::RoomRedaction: + case events::EventType::RoomKey: // Not part of timeline or state case events::EventType::RoomKeyRequest: // Not part of the timeline or state case events::EventType::RoomPinnedEvents: case events::EventType::Tag: // Not part of the timeline or state diff --git a/lib/structs/responses/sync.cpp b/lib/structs/responses/sync.cpp index 3bf7e6329697de6130064ef281143517c5e8d19a..26b40ce332f223ca98aedd527cfed6131a44ce6a 100644 --- a/lib/structs/responses/sync.cpp +++ b/lib/structs/responses/sync.cpp @@ -211,6 +211,13 @@ from_json(const json &obj, DeviceLists &device_lists) device_lists.left = obj.at("left").get<std::vector<std::string>>(); } +void +from_json(const json &obj, ToDevice &to_device) +{ + if (obj.count("events") != 0) + utils::parse_device_events(obj.at("events"), to_device.events); +} + void from_json(const json &obj, Sync &response) { @@ -221,9 +228,7 @@ from_json(const json &obj, Sync &response) response.device_lists = obj.at("device_lists").get<DeviceLists>(); if (obj.count("to_device") != 0) { - if (obj.at("to_device").count("events") != 0) - response.to_device = - obj.at("to_device").at("events").get<std::vector<json>>(); + response.to_device = obj.at("to_device").get<ToDevice>(); } if (obj.count("device_one_time_keys_count") != 0) diff --git a/tests/client_api.cpp b/tests/client_api.cpp index 71284edb25dec7695bb3dcd39554fb23577ae174..1e56f987c1e6aaa4fcc29c8945bb5dda3781d665 100644 --- a/tests/client_api.cpp +++ b/tests/client_api.cpp @@ -6,6 +6,8 @@ #include <gtest/gtest.h> +#include "mtx/events/collections.hpp" +#include "mtx/events/encrypted.hpp" #include "mtx/requests.hpp" #include "mtx/responses.hpp" #include "mtxclient/http/client.hpp" @@ -16,6 +18,7 @@ using namespace mtx::client; using namespace mtx::http; using namespace mtx::identifiers; using namespace mtx::events::collections; +using namespace mtx::requests; using namespace std; @@ -1132,9 +1135,19 @@ TEST(ClientAPI, SendToDevice) json body{{"messages", {{bob->user_id().to_string(), - {{bob->device_id(), {{"example_content_key", "test"}}}}}}}}; - - alice->send_to_device("m.test", body, [bob](RequestErr err) { + {{bob->device_id(), + { + {"action", "request"}, + {"body", + {{"sender_key", "test"}, + {"algorithm", "test_algo"}, + {"room_id", "test_room_id"}, + {"session_id", "test_session_id"}}}, + {"request_id", "test_request_id"}, + {"requesting_device_id", "test_req_id"}, + }}}}}}}; + + alice->send_to_device("m.room_key_request", body, [bob](RequestErr err) { check_error(err); SyncOpts opts; @@ -1142,12 +1155,19 @@ TEST(ClientAPI, SendToDevice) bob->sync(opts, [](const mtx::responses::Sync &res, RequestErr err) { check_error(err); - EXPECT_EQ(res.to_device.size(), 1); - - auto msg = res.to_device.at(0); - EXPECT_EQ(msg.at("content").at("example_content_key"), "test"); - EXPECT_EQ(msg.at("type"), "m.test"); - EXPECT_EQ(msg.at("sender"), "@alice:localhost"); + EXPECT_EQ(res.to_device.events.size(), 1); + + auto event = std::get<mtx::events::DeviceEvent<msgs::KeyRequest>>( + res.to_device.events[0]); + EXPECT_EQ(event.content.action, mtx::events::msg::RequestAction::Request); + EXPECT_EQ(event.content.sender_key, "test"); + EXPECT_EQ(event.content.algorithm, "test_algo"); + EXPECT_EQ(event.content.room_id, "test_room_id"); + EXPECT_EQ(event.content.session_id, "test_session_id"); + EXPECT_EQ(event.content.request_id, "test_request_id"); + EXPECT_EQ(event.content.requesting_device_id, "test_req_id"); + EXPECT_EQ(event.type, mtx::events::EventType::RoomKeyRequest); + EXPECT_EQ(event.sender, "@alice:localhost"); }); }); @@ -1155,6 +1175,66 @@ TEST(ClientAPI, SendToDevice) bob->close(); } +TEST(ClientAPI, NewSendToDevice) +{ + auto alice = std::make_shared<Client>("localhost"); + auto bob = std::make_shared<Client>("localhost"); + auto carl = std::make_shared<Client>("localhost"); + + alice->login("alice", "secret", &check_login); + bob->login("bob", "secret", &check_login); + carl->login("carl", "secret", &check_login); + + while (alice->access_token().empty() || bob->access_token().empty() || + carl->access_token().empty()) + sleep(); + + ToDeviceMessages<msgs::KeyRequest> body1; + ToDeviceMessages<msgs::KeyRequest> body2; + + msgs::KeyRequest request1; + + request1.action = mtx::events::msg::RequestAction::Request; + request1.sender_key = "test"; + request1.algorithm = "m.megolm.v1.aes-sha2"; + request1.room_id = "test_room_id"; + request1.session_id = "test_session_id"; + request1.request_id = "test_request_id"; + request1.requesting_device_id = "test_req_id"; + + body1[bob->user_id()][bob->device_id()] = request1; + + msgs::KeyRequest request2; + + request2.action = mtx::events::msg::RequestAction::Cancellation; + request2.request_id = "test_request_id_1"; + request2.requesting_device_id = "test_req_id_1"; + + body2[bob->user_id()][bob->device_id()] = request2; + + carl->send_to_device<msgs::KeyRequest, mtx::events::EventType::RoomKeyRequest>( + "m.room.key_request", body1, [bob](RequestErr err) { check_error(err); }); + + alice->send_to_device<msgs::KeyRequest, mtx::events::EventType::RoomKeyRequest>( + "m.room_key_request", body2, [bob](RequestErr err) { + check_error(err); + + SyncOpts opts; + opts.timeout = 0; + bob->sync(opts, [](const mtx::responses::Sync &res, RequestErr err) { + check_error(err); + + EXPECT_EQ(res.to_device.events.size(), 2); + auto event = std::get<mtx::events::DeviceEvent<msgs::KeyRequest>>( + res.to_device.events[0]); + }); + }); + + alice->close(); + bob->close(); + carl->close(); +} + TEST(ClientAPI, RetrieveSingleEvent) { auto bob = std::make_shared<Client>("localhost"); diff --git a/tests/e2ee.cpp b/tests/e2ee.cpp index 5d522a48c3fd365c101aafa411e6795082104233..f131f75d9b16ad309394fcc84b2c39bb71dfc10a 100644 --- a/tests/e2ee.cpp +++ b/tests/e2ee.cpp @@ -18,6 +18,8 @@ using namespace mtx::http; using namespace mtx::crypto; using namespace mtx::identifiers; +using namespace mtx::events; +using namespace mtx::events::msg; using namespace mtx::events::collections; using namespace mtx::responses; @@ -25,19 +27,6 @@ using namespace std; using namespace nlohmann; -struct OlmCipherContent -{ - std::string body; - uint8_t type; -}; - -inline void -from_json(const nlohmann::json &obj, OlmCipherContent &msg) -{ - msg.body = obj.at("body"); - msg.type = obj.at("type"); -} - struct OlmMessage { std::string sender_key; @@ -875,11 +864,11 @@ TEST(Encryption, OlmRoomKeyEncryption) opts, [bob = bob_olm, SECRET_TEXT](const mtx::responses::Sync &res, RequestErr err) { check_error(err); - assert(!res.to_device.empty()); - assert(res.to_device.size() == 1); + EXPECT_EQ(res.to_device.events.size(), 1); - OlmMessage olm_msg = res.to_device[0]; - auto cipher = olm_msg.ciphertext.begin(); + auto olm_msg = + std::get<DeviceEvent<msgs::OlmEncrypted>>(res.to_device.events[0]).content; + auto cipher = olm_msg.ciphertext.begin(); EXPECT_EQ(cipher->first, bob->identity_keys().curve25519); diff --git a/tests/events.cpp b/tests/events.cpp index ef06795be43592b6d67e8cb4a269b659a756983c..288f5289fa86b61b1b96ead9867a49cf8caf1208 100644 --- a/tests/events.cpp +++ b/tests/events.cpp @@ -959,18 +959,16 @@ TEST(ToDevice, KeyRequest) "sender": "@mujx:matrix.org", "type": "m.room_key_request" })"_json; - - ns::msg::KeyRequest event = request_data; + mtx::events::DeviceEvent<ns::msg::KeyRequest> event(request_data); EXPECT_EQ(event.sender, "@mujx:matrix.org"); EXPECT_EQ(event.type, mtx::events::EventType::RoomKeyRequest); - EXPECT_EQ(event.action, ns::msg::RequestAction::Request); - EXPECT_EQ(event.algorithm, "m.megolm.v1.aes-sha2"); - EXPECT_EQ(event.room_id, "!iapLxlpZgOzqGnWkXR:matrix.org"); - EXPECT_EQ(event.sender_key, "9im1n0bSYQpnF700sXJqAAYiqGgkyRqMZRdobj0kymY"); - EXPECT_EQ(event.session_id, "oGj6sEDraRDf+NdmvZTI7urDJk/Z+i7TX2KFLbfMGlE"); - EXPECT_EQ(event.request_id, "m1529936829480.0"); - EXPECT_EQ(event.requesting_device_id, "GGUBYESVPI"); - + EXPECT_EQ(event.content.action, ns::msg::RequestAction::Request); + EXPECT_EQ(event.content.algorithm, "m.megolm.v1.aes-sha2"); + EXPECT_EQ(event.content.room_id, "!iapLxlpZgOzqGnWkXR:matrix.org"); + EXPECT_EQ(event.content.sender_key, "9im1n0bSYQpnF700sXJqAAYiqGgkyRqMZRdobj0kymY"); + EXPECT_EQ(event.content.session_id, "oGj6sEDraRDf+NdmvZTI7urDJk/Z+i7TX2KFLbfMGlE"); + EXPECT_EQ(event.content.request_id, "m1529936829480.0"); + EXPECT_EQ(event.content.requesting_device_id, "GGUBYESVPI"); EXPECT_EQ(request_data.dump(), json(event).dump()); } @@ -986,12 +984,12 @@ TEST(ToDevice, KeyCancellation) "type": "m.room_key_request" })"_json; - ns::msg::KeyRequest event = cancellation_data; + mtx::events::DeviceEvent<ns::msg::KeyRequest> event(cancellation_data); EXPECT_EQ(event.sender, "@mujx:matrix.org"); EXPECT_EQ(event.type, mtx::events::EventType::RoomKeyRequest); - EXPECT_EQ(event.action, ns::msg::RequestAction::Cancellation); - EXPECT_EQ(event.request_id, "m1529936829480.0"); - EXPECT_EQ(event.requesting_device_id, "GGUBYESVPI"); + EXPECT_EQ(event.content.action, ns::msg::RequestAction::Cancellation); + EXPECT_EQ(event.content.request_id, "m1529936829480.0"); + EXPECT_EQ(event.content.requesting_device_id, "GGUBYESVPI"); EXPECT_EQ(cancellation_data.dump(), json(event).dump()); }