summary refs log tree commit diff
path: root/src/timeline/TimelineModel.cpp
diff options
context:
space:
mode:
authorDeepBlueV7.X <nicolas.werner@hotmail.de>2020-10-08 20:08:38 +0200
committerGitHub <noreply@github.com>2020-10-08 20:08:38 +0200
commit517a126a4427972668a97b21fc8684f7160976b9 (patch)
tree27142d719f848abe685897cd367e8d69a9bed719 /src/timeline/TimelineModel.cpp
parentMerge pull request #294 from trilene/master (diff)
parentTry to fix windows build (diff)
downloadnheko-517a126a4427972668a97b21fc8684f7160976b9.tar.xz
Merge pull request #270 from Chethan2k1/device-verification
Device verification and Cross-Signing
Diffstat (limited to 'src/timeline/TimelineModel.cpp')
-rw-r--r--src/timeline/TimelineModel.cpp333
1 files changed, 210 insertions, 123 deletions
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp

index 32e9f92c..359e95bc 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp
@@ -116,7 +116,46 @@ struct RoomEventType { return qml_mtx_events::EventType::VideoMessage; } - + qml_mtx_events::EventType operator()( + const mtx::events::Event<mtx::events::msg::KeyVerificationRequest> &) + { + return qml_mtx_events::EventType::KeyVerificationRequest; + } + qml_mtx_events::EventType operator()( + const mtx::events::Event<mtx::events::msg::KeyVerificationStart> &) + { + return qml_mtx_events::EventType::KeyVerificationStart; + } + qml_mtx_events::EventType operator()( + const mtx::events::Event<mtx::events::msg::KeyVerificationMac> &) + { + return qml_mtx_events::EventType::KeyVerificationMac; + } + qml_mtx_events::EventType operator()( + const mtx::events::Event<mtx::events::msg::KeyVerificationAccept> &) + { + return qml_mtx_events::EventType::KeyVerificationAccept; + } + qml_mtx_events::EventType operator()( + const mtx::events::Event<mtx::events::msg::KeyVerificationReady> &) + { + return qml_mtx_events::EventType::KeyVerificationReady; + } + qml_mtx_events::EventType operator()( + const mtx::events::Event<mtx::events::msg::KeyVerificationCancel> &) + { + return qml_mtx_events::EventType::KeyVerificationCancel; + } + qml_mtx_events::EventType operator()( + const mtx::events::Event<mtx::events::msg::KeyVerificationKey> &) + { + return qml_mtx_events::EventType::KeyVerificationKey; + } + qml_mtx_events::EventType operator()( + const mtx::events::Event<mtx::events::msg::KeyVerificationDone> &) + { + return qml_mtx_events::EventType::KeyVerificationDone; + } qml_mtx_events::EventType operator()(const mtx::events::Event<mtx::events::msg::Redacted> &) { return qml_mtx_events::EventType::Redacted; @@ -211,6 +250,15 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj connect(&events, &EventStore::newEncryptedImage, this, &TimelineModel::newEncryptedImage); connect( &events, &EventStore::fetchedMore, this, [this]() { setPaginationInProgress(false); }); + connect(&events, + &EventStore::startDMVerification, + this, + [this](mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> msg) { + ChatPage::instance()->receivedRoomDeviceVerificationRequest(msg, this); + }); + connect(&events, &EventStore::updateFlowEventId, this, [this](std::string event_id) { + this->updateFlowEventId(event_id); + }); } QHash<int, QByteArray> @@ -743,9 +791,9 @@ TimelineModel::viewDecryptedRawMessage(QString id) const } void -TimelineModel::openUserProfile(QString userid) const +TimelineModel::openUserProfile(QString userid) { - MainWindow::instance()->openUserProfile(userid, room_id_); + emit openProfile(new UserProfile(room_id_, userid, manager_, this)); } void @@ -846,18 +894,18 @@ TimelineModel::markEventsAsRead(const std::vector<QString> &event_ids) } } +template<typename T> void -TimelineModel::sendEncryptedMessageEvent(const std::string &txn_id, - nlohmann::json content, - mtx::events::EventType eventType) +TimelineModel::sendEncryptedMessage(mtx::events::RoomEvent<T> msg, mtx::events::EventType eventType) { const auto room_id = room_id_.toStdString(); using namespace mtx::events; using namespace mtx::identifiers; - json doc = { - {"type", mtx::events::to_string(eventType)}, {"content", content}, {"room_id", room_id}}; + json doc = {{"type", mtx::events::to_string(eventType)}, + {"content", json(msg.content)}, + {"room_id", room_id}}; try { // Check if we have already an outbound megolm session then we can use. @@ -865,7 +913,7 @@ TimelineModel::sendEncryptedMessageEvent(const std::string &txn_id, mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> event; event.content = olm::encrypt_group_message(room_id, http::client()->device_id(), doc); - event.event_id = txn_id; + event.event_id = msg.event_id; event.room_id = room_id; event.sender = http::client()->user_id().to_string(); event.type = mtx::events::EventType::RoomEncrypted; @@ -893,32 +941,43 @@ TimelineModel::sendEncryptedMessageEvent(const std::string &txn_id, OutboundGroupSessionData session_data; session_data.session_id = session_id; session_data.session_key = session_key; - session_data.message_index = 0; // TODO Update me + session_data.message_index = 0; cache::saveOutboundMegolmSession( room_id, session_data, std::move(outbound_session)); + { + MegolmSessionIndex index; + index.room_id = room_id; + index.session_id = session_id; + index.sender_key = olm::client()->identity_keys().curve25519; + auto megolm_session = + olm::client()->init_inbound_group_session(session_key); + cache::saveInboundMegolmSession(index, std::move(megolm_session)); + } + const auto members = cache::roomMembers(room_id); nhlog::ui()->info("retrieved {} members for {}", members.size(), room_id); - auto keeper = std::make_shared<StateKeeper>([room_id, doc, txn_id, this]() { - try { - mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> event; - event.content = olm::encrypt_group_message( - room_id, http::client()->device_id(), doc); - event.event_id = txn_id; - event.room_id = room_id; - event.sender = http::client()->user_id().to_string(); - event.type = mtx::events::EventType::RoomEncrypted; - event.origin_server_ts = QDateTime::currentMSecsSinceEpoch(); + auto keeper = + std::make_shared<StateKeeper>([room_id, doc, txn_id = msg.event_id, this]() { + try { + mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> event; + event.content = olm::encrypt_group_message( + room_id, http::client()->device_id(), doc); + event.event_id = txn_id; + event.room_id = room_id; + event.sender = http::client()->user_id().to_string(); + event.type = mtx::events::EventType::RoomEncrypted; + event.origin_server_ts = QDateTime::currentMSecsSinceEpoch(); - emit this->addPendingMessageToStore(event); - } catch (const lmdb::error &e) { - nhlog::db()->critical("failed to save megolm outbound session: {}", - e.what()); - emit ChatPage::instance()->showNotification( - tr("Failed to encrypt event, sending aborted!")); - } - }); + emit this->addPendingMessageToStore(event); + } catch (const lmdb::error &e) { + nhlog::db()->critical( + "failed to save megolm outbound session: {}", e.what()); + emit ChatPage::instance()->showNotification( + tr("Failed to encrypt event, sending aborted!")); + } + }); mtx::requests::QueryKeys req; for (const auto &member : members) @@ -926,7 +985,7 @@ TimelineModel::sendEncryptedMessageEvent(const std::string &txn_id, http::client()->query_keys( req, - [keeper = std::move(keeper), megolm_payload, txn_id, this]( + [keeper = std::move(keeper), megolm_payload, txn_id = msg.event_id, this]( const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("failed to query device keys: {} {}", @@ -937,19 +996,23 @@ TimelineModel::sendEncryptedMessageEvent(const std::string &txn_id, return; } - 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<std::string, std::string> room_key_msgs; - std::map<std::string, DevicePublicKeys> deviceKeys; + mtx::requests::ClaimKeys claim_keys; - room_key_msgs.clear(); - deviceKeys.clear(); + // Mapping from user id to a device_id with valid identity keys to the + // generated room_key event used for sharing the megolm session. + std::map<std::string, std::map<std::string, std::string>> room_key_msgs; + std::map<std::string, std::map<std::string, DevicePublicKeys>> deviceKeys; + for (const auto &user : res.device_keys) { for (const auto &dev : user.second) { const auto user_id = ::UserId(dev.second.user_id); const auto device_id = DeviceId(dev.second.device_id); + if (user_id.get() == + http::client()->user_id().to_string() && + device_id.get() == http::client()->device_id()) + continue; + const auto device_keys = dev.second.keys; const auto curveKey = "curve25519:" + device_id.get(); const auto edKey = "ed25519:" + device_id.get(); @@ -968,7 +1031,7 @@ TimelineModel::sendEncryptedMessageEvent(const std::string &txn_id, try { if (!mtx::crypto::verify_identity_signature( - json(dev.second), device_id, user_id)) { + dev.second, device_id, user_id)) { nhlog::crypto()->warn( "failed to verify identity keys: {}", json(dev.second).dump(2)); @@ -991,42 +1054,25 @@ TimelineModel::sendEncryptedMessageEvent(const std::string &txn_id, user_id, pks.ed25519, megolm_payload) .dump(); - room_key_msgs.emplace(device_id, room_key); - deviceKeys.emplace(device_id, pks); - } - - std::vector<std::string> valid_devices; - valid_devices.reserve(room_key_msgs.size()); - for (auto const &d : room_key_msgs) { - valid_devices.push_back(d.first); + room_key_msgs[user_id].emplace(device_id, room_key); + deviceKeys[user_id].emplace(device_id, pks); + claim_keys.one_time_keys[user.first][device_id] = + mtx::crypto::SIGNED_CURVE25519; - 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("{}", device_id.get()); + nhlog::net()->info(" curve25519 {}", pks.curve25519); + nhlog::net()->info(" ed25519 {}", pks.ed25519); } - - nhlog::net()->info( - "sending claim request for user {} with {} devices", - user.first, - valid_devices.size()); - - http::client()->claim_keys( - user.first, - valid_devices, - std::bind(&TimelineModel::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)); } + + http::client()->claim_keys(claim_keys, + std::bind(&TimelineModel::handleClaimedKeys, + this, + keeper, + room_key_msgs, + deviceKeys, + std::placeholders::_1, + std::placeholders::_2)); }); // TODO: Let the user know about the errors. @@ -1044,12 +1090,12 @@ TimelineModel::sendEncryptedMessageEvent(const std::string &txn_id, } void -TimelineModel::handleClaimedKeys(std::shared_ptr<StateKeeper> keeper, - const std::map<std::string, std::string> &room_keys, - const std::map<std::string, DevicePublicKeys> &pks, - const std::string &user_id, - const mtx::responses::ClaimKeys &res, - mtx::http::RequestErr err) +TimelineModel::handleClaimedKeys( + std::shared_ptr<StateKeeper> keeper, + const std::map<std::string, std::map<std::string, std::string>> &room_keys, + const std::map<std::string, std::map<std::string, DevicePublicKeys>> &pks, + const mtx::responses::ClaimKeys &res, + mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("claim keys error: {} {} {}", @@ -1059,65 +1105,59 @@ TimelineModel::handleClaimedKeys(std::shared_ptr<StateKeeper> keeper, return; } - nhlog::net()->debug("claimed keys for {}", user_id); - - if (res.one_time_keys.size() == 0) { - nhlog::net()->debug("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()->debug("no one-time keys found for user_id: {}", user_id); - return; - } + // Payload with all the to_device message to be sent. + nlohmann::json body; - auto retrieved_devices = res.one_time_keys.at(user_id); + for (const auto &[user_id, retrieved_devices] : res.one_time_keys) { + nhlog::net()->debug("claimed keys for {}", user_id); + if (retrieved_devices.size() == 0) { + nhlog::net()->debug("no one-time keys found for user_id: {}", user_id); + return; + } - // Payload with all the to_device message to be sent. - json body; - body["messages"][user_id] = json::object(); + for (const auto &rd : retrieved_devices) { + const auto device_id = rd.first; - for (const auto &rd : retrieved_devices) { - const auto device_id = rd.first; - nhlog::net()->debug("{} : \n {}", device_id, rd.second.dump(2)); + nhlog::net()->debug("{} : \n {}", device_id, rd.second.dump(2)); - // TODO: Verify signatures - auto otk = rd.second.begin()->at("key"); + if (rd.second.empty() || !rd.second.begin()->contains("key")) { + nhlog::net()->warn("Skipping device {} as it has no key.", + device_id); + continue; + } - if (pks.find(device_id) == pks.end()) { - nhlog::net()->critical("couldn't find public key for device: {}", - device_id); - continue; - } + // TODO: Verify signatures + auto otk = rd.second.begin()->at("key"); - auto id_key = pks.at(device_id).curve25519; - auto s = olm::client()->create_outbound_session(id_key, otk); + auto id_key = pks.at(user_id).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(user_id).at(device_id), + pks.at(user_id).at(device_id).curve25519); - auto device_msg = olm::client()->create_olm_encrypted_content( - s.get(), room_keys.at(device_id), pks.at(device_id).curve25519); + try { + cache::saveOlmSession(id_key, std::move(s)); + } catch (const lmdb::error &e) { + nhlog::db()->critical("failed to save outbound olm session: {}", + e.what()); + } catch (const mtx::crypto::olm_exception &e) { + nhlog::crypto()->critical( + "failed to pickle outbound olm session: {}", e.what()); + } - try { - cache::saveOlmSession(id_key, std::move(s)); - } catch (const lmdb::error &e) { - nhlog::db()->critical("failed to save outbound olm session: {}", e.what()); - } catch (const mtx::crypto::olm_exception &e) { - nhlog::crypto()->critical("failed to pickle outbound olm session: {}", - e.what()); + body["messages"][user_id][device_id] = device_msg; } - body["messages"][user_id][device_id] = device_msg; + nhlog::net()->info("send_to_device: {}", user_id); } - nhlog::net()->info("send_to_device: {}", user_id); - http::client()->send_to_device( - "m.room.encrypted", body, [keeper](mtx::http::RequestErr err) { + mtx::events::to_string(mtx::events::EventType::RoomEncrypted), + http::client()->generate_txn_id(), + body, + [keeper](mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("failed to send " "send_to_device " @@ -1143,8 +1183,7 @@ struct SendMessageVisitor if (encInfo) emit model_->newEncryptedImage(encInfo.value()); - model_->sendEncryptedMessageEvent( - msg.event_id, nlohmann::json(msg.content), Event); + model_->sendEncryptedMessage(msg, Event); } else { msg.type = Event; emit model_->addPendingMessageToStore(msg); @@ -1199,6 +1238,54 @@ struct SendMessageVisitor event); } + void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> &msg) + { + sendRoomEvent<mtx::events::msg::KeyVerificationRequest, + mtx::events::EventType::RoomMessage>(msg); + } + + void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationReady> &msg) + { + sendRoomEvent<mtx::events::msg::KeyVerificationReady, + mtx::events::EventType::KeyVerificationReady>(msg); + } + + void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationStart> &msg) + { + sendRoomEvent<mtx::events::msg::KeyVerificationStart, + mtx::events::EventType::KeyVerificationStart>(msg); + } + + void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationAccept> &msg) + { + sendRoomEvent<mtx::events::msg::KeyVerificationAccept, + mtx::events::EventType::KeyVerificationAccept>(msg); + } + + void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationMac> &msg) + { + sendRoomEvent<mtx::events::msg::KeyVerificationMac, + mtx::events::EventType::KeyVerificationMac>(msg); + } + + void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationKey> &msg) + { + sendRoomEvent<mtx::events::msg::KeyVerificationKey, + mtx::events::EventType::KeyVerificationKey>(msg); + } + + void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationDone> &msg) + { + sendRoomEvent<mtx::events::msg::KeyVerificationDone, + mtx::events::EventType::KeyVerificationDone>(msg); + } + + void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationCancel> &msg) + { + sendRoomEvent<mtx::events::msg::KeyVerificationCancel, + mtx::events::EventType::KeyVerificationCancel>(msg); + } + TimelineModel *model_; };