diff --git a/include/timeline/TimelineView.h b/include/timeline/TimelineView.h index a86c0286..bbe1dcad 100644 --- a/include/timeline/TimelineView.h +++ b/include/timeline/TimelineView.h @@ -195,10 +195,9 @@ private: const mtx::events::EncryptedEvent &e); void handleClaimedKeys(std::shared_ptr keeper, - const std::string &room_key, - const DevicePublicKeys &pks, + const std::map &room_key, + const std::map &pks, const std::string &user_id, - const std::string &device_id, const mtx::responses::ClaimKeys &res, mtx::http::RequestErr err); diff --git a/src/Olm.cpp b/src/Olm.cpp index c426968f..4d144a2d 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -6,6 +6,7 @@ using namespace mtx::crypto; static const std::string STORAGE_SECRET_KEY("secret"); +constexpr auto MEGOLM_ALGO = "m.megolm.v1.aes-sha2"; namespace { auto client_ = std::make_unique(); @@ -142,6 +143,7 @@ encrypt_group_message(const std::string &room_id, data.sender_key = olm::client()->identity_keys().curve25519; data.session_id = res.data.session_id; data.device_id = device_id; + data.algorithm = MEGOLM_ALGO; auto message_index = olm_outbound_group_session_message_index(res.session); nhlog::crypto()->info("next message_index {}", message_index); diff --git a/src/timeline/TimelineView.cc b/src/timeline/TimelineView.cc index 9a69c58a..1a329594 100644 --- a/src/timeline/TimelineView.cc +++ b/src/timeline/TimelineView.cc @@ -185,7 +185,6 @@ TimelineView::sliderMoved(int position) // Prevent user from moving up when there is pagination in // progress. - // TODO: Keep a map of the event ids to filter out duplicates. if (isPaginationInProgress_) return; @@ -1278,12 +1277,18 @@ TimelineView::prepareEncryptedMessage(const PendingMessage &msg) } for (const auto &user : res.device_keys) { + // Mapping from a device_id with valid identity keys to the + // generated room_key event used for sharing the megolm session. + std::map room_key_msgs; + std::map deviceKeys; + + room_key_msgs.clear(); + deviceKeys.clear(); + for (const auto &dev : user.second) { const auto user_id = UserId(dev.second.user_id); const auto device_id = DeviceId(dev.second.device_id); - nhlog::net()->info("device_id {}", device_id.get()); - const auto device_keys = dev.second.keys; const auto curveKey = "curve25519:" + device_id.get(); const auto edKey = "ed25519:" + device_id.get(); @@ -1300,12 +1305,6 @@ TimelineView::prepareEncryptedMessage(const PendingMessage &msg) pks.ed25519 = device_keys.at(edKey); pks.curve25519 = device_keys.at(curveKey); - // Validate signatures - for (const auto &algo : dev.second.keys) { - nhlog::net()->info( - "dev keys {} {}", algo.first, algo.second); - } - try { if (!mtx::crypto::verify_identity_signature( json(dev.second), device_id, user_id)) { @@ -1319,6 +1318,11 @@ TimelineView::prepareEncryptedMessage(const PendingMessage &msg) "failed to parse device key json: {}", e.what()); continue; + } catch (const mtx::crypto::olm_exception &e) { + nhlog::crypto()->warn( + "failed to verify device key json: {}", + e.what()); + continue; } auto room_key = olm::client() @@ -1326,19 +1330,41 @@ TimelineView::prepareEncryptedMessage(const PendingMessage &msg) user_id, pks.ed25519, megolm_payload) .dump(); - http::v2::client()->claim_keys( - user_id, - {device_id}, - std::bind(&TimelineView::handleClaimedKeys, - this, - keeper, - room_key, - pks, - user_id, - device_id, - std::placeholders::_1, - std::placeholders::_2)); + room_key_msgs.emplace(device_id, room_key); + deviceKeys.emplace(device_id, pks); } + + std::vector valid_devices; + valid_devices.reserve(room_key_msgs.size()); + for (auto const &d : room_key_msgs) { + valid_devices.push_back(d.first); + + nhlog::net()->info("{}", d.first); + nhlog::net()->info(" curve25519 {}", + deviceKeys.at(d.first).curve25519); + nhlog::net()->info(" ed25519 {}", + deviceKeys.at(d.first).ed25519); + } + + nhlog::net()->info( + "sending claim request for user {} with {} devices", + user.first, + valid_devices.size()); + + http::v2::client()->claim_keys( + user.first, + valid_devices, + std::bind(&TimelineView::handleClaimedKeys, + this, + keeper, + room_key_msgs, + deviceKeys, + user.first, + std::placeholders::_1, + std::placeholders::_2)); + + // TODO: Wait before sending the next batch of requests. + std::this_thread::sleep_for(std::chrono::milliseconds(500)); } }); @@ -1354,44 +1380,62 @@ TimelineView::prepareEncryptedMessage(const PendingMessage &msg) void TimelineView::handleClaimedKeys(std::shared_ptr keeper, - const std::string &room_key, - const DevicePublicKeys &pks, + const std::map &room_keys, + const std::map &pks, const std::string &user_id, - const std::string &device_id, const mtx::responses::ClaimKeys &res, mtx::http::RequestErr err) { if (err) { - nhlog::net()->warn("claim keys error: {}", err->matrix_error.error); + nhlog::net()->warn("claim keys error: {} {} {}", + err->matrix_error.error, + err->parse_error, + static_cast(err->status_code)); return; } - nhlog::net()->info("claimed keys for {} - {}", user_id, device_id); + nhlog::net()->info("claimed keys for {}", user_id); if (res.one_time_keys.size() == 0) { - nhlog::net()->info("no one-time keys found for device_id: {}", device_id); + nhlog::net()->info("no one-time keys found for user_id: {}", user_id); return; } if (res.one_time_keys.find(user_id) == res.one_time_keys.end()) { - nhlog::net()->info( - "no one-time keys found in device_id {} for the user {}", device_id, user_id); + nhlog::net()->info("no one-time keys found for user_id: {}", user_id); return; } auto retrieved_devices = res.one_time_keys.at(user_id); + // Payload with all the to_device message to be sent. + json body; + body["messages"][user_id] = json::object(); + for (const auto &rd : retrieved_devices) { - nhlog::net()->info("{} : \n {}", rd.first, rd.second.dump(2)); + const auto device_id = rd.first; + nhlog::net()->info("{} : \n {}", device_id, rd.second.dump(2)); // TODO: Verify signatures - auto otk = rd.second.begin()->at("key"); - auto id_key = pks.curve25519; + auto otk = rd.second.begin()->at("key"); - auto s = olm::client()->create_outbound_session(id_key, otk); + if (pks.find(device_id) == pks.end()) { + nhlog::net()->critical("couldn't find public key for device: {}", + device_id); + continue; + } - auto device_msg = - olm::client()->create_olm_encrypted_content(s.get(), room_key, pks.curve25519); + auto id_key = pks.at(device_id).curve25519; + auto s = olm::client()->create_outbound_session(id_key, otk); + + if (room_keys.find(device_id) == room_keys.end()) { + nhlog::net()->critical("couldn't find m.room_key for device: {}", + device_id); + continue; + } + + auto device_msg = olm::client()->create_olm_encrypted_content( + s.get(), room_keys.at(device_id), pks.at(device_id).curve25519); try { cache::client()->saveOlmSession(id_key, std::move(s)); @@ -1402,16 +1446,18 @@ TimelineView::handleClaimedKeys(std::shared_ptr keeper, e.what()); } - json body{{"messages", {{user_id, {{device_id, device_msg}}}}}}; - - http::v2::client()->send_to_device( - "m.room.encrypted", body, [keeper](mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to send " - "send_to_device " - "message: {}", - err->matrix_error.error); - } - }); + body["messages"][user_id][device_id] = device_msg; } + + nhlog::net()->info("send_to_device: {}", user_id); + + http::v2::client()->send_to_device( + "m.room.encrypted", body, [keeper](mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to send " + "send_to_device " + "message: {}", + err->matrix_error.error); + } + }); }