diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index 1ca9dcc8..dc6bc165 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -136,7 +136,7 @@ ApplicationWindow{ model: profile.deviceList delegate: RowLayout{ - width: parent.width + width: devicelist.width spacing: 4 ColumnLayout{ diff --git a/src/Cache.cpp b/src/Cache.cpp index 667506c5..8b47c357 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -91,6 +91,7 @@ Q_DECLARE_METATYPE(RoomMember) Q_DECLARE_METATYPE(mtx::responses::Timeline) Q_DECLARE_METATYPE(RoomSearchResult) Q_DECLARE_METATYPE(RoomInfo) +Q_DECLARE_METATYPE(mtx::responses::QueryKeys) namespace { std::unique_ptr instance_ = nullptr; @@ -155,26 +156,7 @@ Cache::Cache(const QString &userId, QObject *parent) , localUserId_{userId} { setup(); - connect( - this, - &Cache::updateUserCacheFlag, - this, - [this](const std::string &user_id) { - std::optional cache_ = getUserCache(user_id); - if (cache_.has_value()) { - cache_.value().isUpdated = false; - setUserCache(user_id, cache_.value()); - } else { - setUserCache(user_id, UserCache{}); - } - }, - Qt::QueuedConnection); - connect( - this, - &Cache::deleteLeftUsers, - this, - [this](const std::string &user_id) { deleteUserCache(user_id); }, - Qt::QueuedConnection); + connect(this, &Cache::userKeysUpdate, this, &Cache::updateUserKeys, Qt::QueuedConnection); } void @@ -1017,6 +999,8 @@ Cache::saveState(const mtx::responses::Sync &res) using namespace mtx::events; auto user_id = this->localUserId_.toStdString(); + auto currentBatchToken = nextBatchToken(); + auto txn = lmdb::txn::begin(env_); setNextBatchToken(txn, res.next_batch); @@ -1034,6 +1018,8 @@ Cache::saveState(const mtx::responses::Sync &res) ev); } + auto userKeyCacheDb = getUserKeysDb(txn); + // Save joined rooms for (const auto &room : res.rooms.join) { auto statesdb = getStatesDb(txn, room.first); @@ -1107,7 +1093,8 @@ Cache::saveState(const mtx::responses::Sync &res) savePresence(txn, res.presence); - updateUserCache(res.device_lists); + markUserKeysOutOfDate(txn, userKeyCacheDb, res.device_lists.changed, currentBatchToken); + deleteUserKeys(txn, userKeyCacheDb, res.device_lists.left); removeLeftRooms(txn, res.rooms.leave); @@ -3098,126 +3085,246 @@ Cache::statusMessage(const std::string &user_id) } void -to_json(json &j, const UserCache &info) +to_json(json &j, const UserKeyCache &info) { - j["keys"] = info.keys; - j["isUpdated"] = info.isUpdated; + j["device_keys"] = info.device_keys; + j["master_keys"] = info.master_keys; + j["user_signing_keys"] = info.user_signing_keys; + j["self_signing_keys"] = info.self_signing_keys; + j["updated_at"] = info.updated_at; + j["last_changed"] = info.last_changed; } void -from_json(const json &j, UserCache &info) +from_json(const json &j, UserKeyCache &info) { - info.keys = j.at("keys").get(); - info.isUpdated = j.at("isUpdated").get(); + info.device_keys = j.value("device_keys", std::map{}); + info.master_keys = j.value("master_keys", mtx::crypto::CrossSigningKeys{}); + info.user_signing_keys = j.value("user_signing_keys", mtx::crypto::CrossSigningKeys{}); + info.self_signing_keys = j.value("self_signing_keys", mtx::crypto::CrossSigningKeys{}); + info.updated_at = j.value("updated_at", ""); + info.last_changed = j.value("last_changed", ""); } -std::optional -Cache::getUserCache(const std::string &user_id) +std::optional +Cache::userKeys(const std::string &user_id) { - lmdb::val verifiedVal; + lmdb::val keys; - auto txn = lmdb::txn::begin(env_); - auto db = getUserCacheDb(txn); - auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal); + try { + auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); + auto db = getUserKeysDb(txn); + auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), keys); - txn.commit(); - - UserCache verified_state; - if (res) { - verified_state = json::parse(std::string(verifiedVal.data(), verifiedVal.size())); - return verified_state; - } else { + if (res) { + return json::parse(std::string_view(keys.data(), keys.size())) + .get(); + } else { + return {}; + } + } catch (std::exception &) { return {}; } } -//! be careful when using make sure is_user_verified is not changed -int -Cache::setUserCache(const std::string &user_id, const UserCache &body) +void +Cache::updateUserKeys(const std::string &sync_token, const mtx::responses::QueryKeys &keyQuery) { auto txn = lmdb::txn::begin(env_); - auto db = getUserCacheDb(txn); + auto db = getUserKeysDb(txn); - auto res = lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(body).dump())); + std::map updates; - txn.commit(); + for (const auto &[user, keys] : keyQuery.device_keys) + updates[user].device_keys = keys; + for (const auto &[user, keys] : keyQuery.master_keys) + updates[user].master_keys = keys; + for (const auto &[user, keys] : keyQuery.user_signing_keys) + updates[user].user_signing_keys = keys; + for (const auto &[user, keys] : keyQuery.self_signing_keys) + updates[user].self_signing_keys = keys; - return res; -} + for (auto &[user, update] : updates) { + lmdb::val oldKeys; + auto res = lmdb::dbi_get(txn, db, lmdb::val(user), oldKeys); -void -Cache::updateUserCache(const mtx::responses::DeviceLists body) -{ - for (std::string user_id : body.changed) { - emit updateUserCacheFlag(user_id); + if (res) { + auto last_changed = + json::parse(std::string_view(oldKeys.data(), oldKeys.size())) + .get() + .last_changed; + // skip if we are tracking this and expect it to be up to date with the last + // sync token + if (!last_changed.empty() && last_changed != sync_token) + continue; + } + lmdb::dbi_put(txn, db, lmdb::val(user), lmdb::val(json(update).dump())); } - for (std::string user_id : body.left) { - emit deleteLeftUsers(user_id); - } -} - -int -Cache::deleteUserCache(const std::string &user_id) -{ - auto txn = lmdb::txn::begin(env_); - auto db = getUserCacheDb(txn); - auto res = lmdb::dbi_del(txn, db, lmdb::val(user_id), nullptr); - txn.commit(); - - return res; } void -to_json(json &j, const DeviceVerifiedCache &info) +Cache::deleteUserKeys(lmdb::txn &txn, lmdb::dbi &db, const std::vector &user_ids) { - j["is_user_verified"] = info.is_user_verified; - j["cross_verified"] = info.cross_verified; - j["device_verified"] = info.device_verified; - j["device_blocked"] = info.device_blocked; + for (const auto &user_id : user_ids) + lmdb::dbi_del(txn, db, lmdb::val(user_id), nullptr); } void -from_json(const json &j, DeviceVerifiedCache &info) +Cache::markUserKeysOutOfDate(lmdb::txn &txn, + lmdb::dbi &db, + const std::vector &user_ids, + const std::string &sync_token) { - info.is_user_verified = j.at("is_user_verified"); - info.cross_verified = j.at("cross_verified").get>(); - info.device_verified = j.at("device_verified").get>(); - info.device_blocked = j.at("device_blocked").get>(); + mtx::requests::QueryKeys query; + query.token = sync_token; + + for (const auto &user : user_ids) { + lmdb::val oldKeys; + auto res = lmdb::dbi_get(txn, db, lmdb::val(user), oldKeys); + + if (!res) + continue; + + auto cacheEntry = + json::parse(std::string_view(oldKeys.data(), oldKeys.size())).get(); + cacheEntry.last_changed = sync_token; + lmdb::dbi_put(txn, db, lmdb::val(user), lmdb::val(json(cacheEntry).dump())); + + query.device_keys[user] = {}; + } + + if (!query.device_keys.empty()) + http::client()->query_keys(query, + [this, sync_token](const mtx::responses::QueryKeys &keys, + mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn( + "failed to query device keys: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + return; + } + + emit userKeysUpdate(sync_token, keys); + }); } -std::optional -Cache::getVerifiedCache(const std::string &user_id) +void +to_json(json &j, const VerificationCache &info) +{ + j["verified_master_key"] = info.verified_master_key; + j["cross_verified"] = info.cross_verified; + j["device_verified"] = info.device_verified; + j["device_blocked"] = info.device_blocked; +} + +void +from_json(const json &j, VerificationCache &info) +{ + info.verified_master_key = j.at("verified_master_key"); + info.cross_verified = j.at("cross_verified").get>(); + info.device_verified = j.at("device_verified").get>(); + info.device_blocked = j.at("device_blocked").get>(); +} + +std::optional +Cache::verificationStatus(const std::string &user_id) { lmdb::val verifiedVal; auto txn = lmdb::txn::begin(env_); - auto db = getDeviceVerifiedDb(txn); - auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal); + auto db = getVerificationDb(txn); - txn.commit(); - - DeviceVerifiedCache verified_state; - if (res) { - verified_state = json::parse(std::string(verifiedVal.data(), verifiedVal.size())); - return verified_state; - } else { + try { + VerificationCache verified_state; + auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal); + if (res) { + verified_state = + json::parse(std::string_view(verifiedVal.data(), verifiedVal.size())); + return verified_state; + } else { + return {}; + } + } catch (std::exception &) { return {}; } } -int -Cache::setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body) +void +Cache::markDeviceVerified(const std::string &user_id, const std::string &key) { + lmdb::val val; + auto txn = lmdb::txn::begin(env_); - auto db = getDeviceVerifiedDb(txn); + auto db = getVerificationDb(txn); - auto res = lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(body).dump())); + try { + VerificationCache verified_state; + auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), val); + if (res) { + verified_state = json::parse(std::string_view(val.data(), val.size())); + } - txn.commit(); + for (const auto &device : verified_state.device_verified) + if (device == key) + return; - return res; + verified_state.device_verified.push_back(key); + lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(verified_state).dump())); + txn.commit(); + } catch (std::exception &) { + } +} + +void +Cache::markDeviceUnverified(const std::string &user_id, const std::string &key) +{ + lmdb::val val; + + auto txn = lmdb::txn::begin(env_); + auto db = getVerificationDb(txn); + + try { + VerificationCache verified_state; + auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), val); + if (res) { + verified_state = json::parse(std::string_view(val.data(), val.size())); + } + + verified_state.device_verified.erase( + std::remove(verified_state.device_verified.begin(), + verified_state.device_verified.end(), + key), + verified_state.device_verified.end()); + + lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(verified_state).dump())); + txn.commit(); + } catch (std::exception &) { + } +} + +void +Cache::markMasterKeyVerified(const std::string &user_id, const std::string &key) +{ + lmdb::val val; + + auto txn = lmdb::txn::begin(env_); + auto db = getVerificationDb(txn); + + try { + VerificationCache verified_state; + auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), val); + if (res) { + verified_state = json::parse(std::string_view(val.data(), val.size())); + } + + verified_state.verified_master_key = key; + lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(verified_state).dump())); + txn.commit(); + } catch (std::exception &) { + } } void @@ -3401,41 +3508,6 @@ statusMessage(const std::string &user_id) { return instance_->statusMessage(user_id); } -std::optional -getUserCache(const std::string &user_id) -{ - return instance_->getUserCache(user_id); -} - -void -updateUserCache(const mtx::responses::DeviceLists body) -{ - instance_->updateUserCache(body); -} - -int -setUserCache(const std::string &user_id, const UserCache &body) -{ - return instance_->setUserCache(user_id, body); -} - -int -deleteUserCache(const std::string &user_id) -{ - return instance_->deleteUserCache(user_id); -} - -std::optional -getVerifiedCache(const std::string &user_id) -{ - return instance_->getVerifiedCache(user_id); -} - -int -setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body) -{ - return instance_->setVerifiedCache(user_id, body); -} //! Load saved data for the display names & avatars. void @@ -3444,6 +3516,43 @@ populateMembers() instance_->populateMembers(); } +// user cache stores user keys +std::optional +userKeys(const std::string &user_id) +{ + return instance_->userKeys(user_id); +} +void +updateUserKeys(const std::string &sync_token, const mtx::responses::QueryKeys &keyQuery) +{ + instance_->updateUserKeys(sync_token, keyQuery); +} + +// device & user verification cache +std::optional +verificationStatus(const std::string &user_id) +{ + return instance_->verificationStatus(user_id); +} + +void +markDeviceVerified(const std::string &user_id, const std::string &key) +{ + instance_->markDeviceVerified(user_id, key); +} + +void +markDeviceUnverified(const std::string &user_id, const std::string &key) +{ + instance_->markDeviceUnverified(user_id, key); +} + +void +markMasterKeyVerified(const std::string &user_id, const std::string &key) +{ + instance_->markMasterKeyVerified(user_id, key); +} + std::vector joinedRooms() { diff --git a/src/Cache.h b/src/Cache.h index 82d909ae..edad5993 100644 --- a/src/Cache.h +++ b/src/Cache.h @@ -60,25 +60,21 @@ presenceState(const std::string &user_id); std::string statusMessage(const std::string &user_id); -//! user Cache -std::optional -getUserCache(const std::string &user_id); - +// user cache stores user keys +std::optional +userKeys(const std::string &user_id); void -updateUserCache(const mtx::responses::DeviceLists body); +updateUserKeys(const std::string &sync_token, const mtx::responses::QueryKeys &keyQuery); -int -setUserCache(const std::string &user_id, const UserCache &body); - -int -deleteUserCache(const std::string &user_id); - -//! verified Cache -std::optional -getVerifiedCache(const std::string &user_id); - -int -setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body); +// device & user verification cache +std::optional +verificationStatus(const std::string &user_id); +void +markDeviceVerified(const std::string &user_id, const std::string &key); +void +markDeviceUnverified(const std::string &user_id, const std::string &key); +void +markMasterKeyVerified(const std::string &user_id, const std::string &key); //! Load saved data for the display names & avatars. void diff --git a/src/CacheCryptoStructs.h b/src/CacheCryptoStructs.h index 1dde21ce..10636ac6 100644 --- a/src/CacheCryptoStructs.h +++ b/src/CacheCryptoStructs.h @@ -67,52 +67,38 @@ struct OlmSessionStorage }; // this will store the keys of the user with whom a encrypted room is shared with -struct UserCache +struct UserKeyCache { - //! map of public key key_ids and their public_key - mtx::responses::QueryKeys keys; - //! if the current cache is updated or not - bool isUpdated = false; - - UserCache(mtx::responses::QueryKeys res, bool isUpdated_ = false) - : keys(res) - , isUpdated(isUpdated_) - {} - UserCache() {} + //! Device id to device keys + std::map device_keys; + //! corss signing keys + mtx::crypto::CrossSigningKeys master_keys, user_signing_keys, self_signing_keys; + //! Sync token when nheko last fetched the keys + std::string updated_at; + //! Sync token when the keys last changed. updated != last_changed means they are outdated. + std::string last_changed; }; void -to_json(nlohmann::json &j, const UserCache &info); +to_json(nlohmann::json &j, const UserKeyCache &info); void -from_json(const nlohmann::json &j, UserCache &info); +from_json(const nlohmann::json &j, UserKeyCache &info); // the reason these are stored in a seperate cache rather than storing it in the user cache is -// UserCache stores only keys of users with which encrypted room is shared -struct DeviceVerifiedCache +// UserKeyCache stores only keys of users with which encrypted room is shared +struct VerificationCache { //! list of verified device_ids with device-verification std::vector device_verified; - //! list of verified device_ids with cross-signing + //! list of verified device_ids with cross-signing, calculated from master key std::vector cross_verified; //! list of devices the user blocks std::vector device_blocked; - //! this stores if the user is verified (with cross-signing) - bool is_user_verified = false; - - DeviceVerifiedCache(std::vector device_verified_, - std::vector cross_verified_, - std::vector device_blocked_, - bool is_user_verified_ = false) - : device_verified(device_verified_) - , cross_verified(cross_verified_) - , device_blocked(device_blocked_) - , is_user_verified(is_user_verified_) - {} - - DeviceVerifiedCache() {} + //! The verified master key. + std::string verified_master_key; }; void -to_json(nlohmann::json &j, const DeviceVerifiedCache &info); +to_json(nlohmann::json &j, const VerificationCache &info); void -from_json(const nlohmann::json &j, DeviceVerifiedCache &info); +from_json(const nlohmann::json &j, VerificationCache &info); diff --git a/src/Cache_p.h b/src/Cache_p.h index ce6414ab..034c6d76 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -55,14 +55,22 @@ public: std::string statusMessage(const std::string &user_id); // user cache stores user keys - std::optional getUserCache(const std::string &user_id); - void updateUserCache(const mtx::responses::DeviceLists body); - int setUserCache(const std::string &user_id, const UserCache &body); - int deleteUserCache(const std::string &user_id); + std::optional userKeys(const std::string &user_id); + void updateUserKeys(const std::string &sync_token, + const mtx::responses::QueryKeys &keyQuery); + void markUserKeysOutOfDate(lmdb::txn &txn, + lmdb::dbi &db, + const std::vector &user_ids, + const std::string &sync_token); + void deleteUserKeys(lmdb::txn &txn, + lmdb::dbi &db, + const std::vector &user_ids); - // device verified cache - std::optional getVerifiedCache(const std::string &user_id); - int setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body); + // device & user verification cache + std::optional verificationStatus(const std::string &user_id); + void markDeviceVerified(const std::string &user_id, const std::string &key); + void markDeviceUnverified(const std::string &user_id, const std::string &key); + void markMasterKeyVerified(const std::string &user_id, const std::string &key); static void removeDisplayName(const QString &room_id, const QString &user_id); static void removeAvatarUrl(const QString &room_id, const QString &user_id); @@ -272,8 +280,8 @@ signals: void newReadReceipts(const QString &room_id, const std::vector &event_ids); void roomReadStatus(const std::map &status); void removeNotification(const QString &room_id, const QString &event_id); - void updateUserCacheFlag(const std::string &user_id); - void deleteLeftUsers(const std::string &user_id); + void userKeysUpdate(const std::string &sync_token, + const mtx::responses::QueryKeys &keyQuery); private: //! Save an invited room. @@ -539,12 +547,12 @@ private: return lmdb::dbi::open(txn, "presence", MDB_CREATE); } - lmdb::dbi getUserCacheDb(lmdb::txn &txn) + lmdb::dbi getUserKeysDb(lmdb::txn &txn) { - return lmdb::dbi::open(txn, "user_cache", MDB_CREATE); + return lmdb::dbi::open(txn, "user_key", MDB_CREATE); } - lmdb::dbi getDeviceVerifiedDb(lmdb::txn &txn) + lmdb::dbi getVerificationDb(lmdb::txn &txn) { return lmdb::dbi::open(txn, "verified", MDB_CREATE); } diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index c6978a59..6abe4078 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -1466,35 +1466,43 @@ ChatPage::initiateLogout() } void -ChatPage::query_keys( - const mtx::requests::QueryKeys &req, - std::function cb) +ChatPage::query_keys(const std::string &user_id, + std::function cb) { - std::string user_id = req.device_keys.begin()->first; - auto cache_ = cache::getUserCache(user_id); + auto cache_ = cache::userKeys(user_id); if (cache_.has_value()) { - if (cache_.value().isUpdated) { - cb(cache_.value().keys, {}); - } else { - http::client()->query_keys( - req, - [cb, user_id](const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to query device keys: {},{}", - err->matrix_error.errcode, - static_cast(err->status_code)); - return; - } - cache::setUserCache(std::move(user_id), - std::move(UserCache{res, true})); - cb(res, err); - }); + if (!cache_->updated_at.empty() && cache_->updated_at == cache_->last_changed) { + cb(cache_.value(), {}); + return; } - } else { - http::client()->query_keys(req, cb); } + + mtx::requests::QueryKeys req; + req.device_keys[user_id] = {}; + + std::string last_changed; + if (cache_) + last_changed = cache_->last_changed; + req.token = last_changed; + + http::client()->query_keys(req, + [cb, user_id, last_changed](const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn( + "failed to query device keys: {},{}", + err->matrix_error.errcode, + static_cast(err->status_code)); + cb({}, err); + return; + } + + cache::updateUserKeys(last_changed, res); + + auto keys = cache::userKeys(user_id); + cb(keys.value_or(UserKeyCache{}), err); + }); } template diff --git a/src/ChatPage.h b/src/ChatPage.h index 9d8abb24..f363c4fe 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -35,6 +35,7 @@ #include #include +#include "CacheCryptoStructs.h" #include "CacheStructs.h" #include "CallManager.h" #include "CommunitiesList.h" @@ -89,9 +90,8 @@ public: //! Show the room/group list (if it was visible). void showSideBars(); void initiateLogout(); - void query_keys( - const mtx::requests::QueryKeys &req, - std::function cb); + void query_keys(const std::string &req, + std::function cb); void focusMessageInput(); QString status() const; diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 96fed55a..aa8b5b44 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -328,22 +328,14 @@ DeviceVerificationFlow::setTransactionId(QString transaction_id_) void DeviceVerificationFlow::setUserId(QString userID) { - this->userId = userID; - this->toClient = mtx::identifiers::parse(userID.toStdString()); - auto user_cache = cache::getUserCache(userID.toStdString()); + this->userId = userID; + this->toClient = mtx::identifiers::parse(userID.toStdString()); - if (user_cache.has_value()) { - this->callback_fn(user_cache->keys, {}, userID.toStdString()); - } else { - mtx::requests::QueryKeys req; - req.device_keys[userID.toStdString()] = {}; - http::client()->query_keys( - req, - [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err) { - this->callback_fn(res, err, user_id); - }); - } + auto user_id = userID.toStdString(); + ChatPage::instance()->query_keys( + user_id, [user_id, this](const UserKeyCache &res, mtx::http::RequestErr err) { + this->callback_fn(res, err, user_id); + }); } void @@ -622,30 +614,52 @@ DeviceVerificationFlow::sendVerificationKey() (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationKey); } } + +mtx::events::msg::KeyVerificationMac +key_verification_mac(mtx::crypto::SAS *sas, + mtx::identifiers::User sender, + const std::string &senderDevice, + mtx::identifiers::User receiver, + const std::string &receiverDevice, + const std::string &transactionId, + std::map keys) +{ + mtx::events::msg::KeyVerificationMac req; + + std::string info = "MATRIX_KEY_VERIFICATION_MAC" + sender.to_string() + senderDevice + + receiver.to_string() + receiverDevice + transactionId; + + std::string key_list; + bool first = true; + for (const auto &[key_id, key] : keys) { + req.mac[key_id] = sas->calculate_mac(key, info + key_id); + + if (!first) + key_list += ","; + key_list += key_id; + first = false; + } + + req.keys = sas->calculate_mac(key_list, info + "KEY_IDS"); + + return req; +} + //! sends the mac of the keys void DeviceVerificationFlow::sendVerificationMac() { - mtx::events::msg::KeyVerificationMac req; + std::map key_list; + key_list["ed25519:" + http::client()->device_id()] = olm::client()->identity_keys().ed25519; - std::string info = "MATRIX_KEY_VERIFICATION_MAC" + http::client()->user_id().to_string() + - http::client()->device_id() + this->toClient.to_string() + - this->deviceId.toStdString() + this->transaction_id; - - //! this vector stores the type of the key and the key - std::vector> key_list; - key_list.push_back(make_pair("ed25519", olm::client()->identity_keys().ed25519)); - std::sort(key_list.begin(), key_list.end()); - for (auto x : key_list) { - req.mac.insert( - std::make_pair(x.first + ":" + http::client()->device_id(), - this->sas->calculate_mac( - x.second, info + x.first + ":" + http::client()->device_id()))); - req.keys += x.first + ":" + http::client()->device_id() + ","; - } - - req.keys = - this->sas->calculate_mac(req.keys.substr(0, req.keys.size() - 1), info + "KEY_IDS"); + mtx::events::msg::KeyVerificationMac req = + key_verification_mac(sas.get(), + http::client()->user_id(), + http::client()->device_id(), + this->toClient, + this->deviceId.toStdString(), + this->transaction_id, + key_list); if (this->type == DeviceVerificationFlow::Type::ToDevice) { mtx::requests::ToDeviceMessages body; @@ -673,27 +687,16 @@ DeviceVerificationFlow::sendVerificationMac() void DeviceVerificationFlow::acceptDevice() { - auto verified_cache = cache::getVerifiedCache(this->userId.toStdString()); - if (verified_cache.has_value()) { - verified_cache->device_verified.push_back(this->deviceId.toStdString()); - verified_cache->device_blocked.erase( - std::remove(verified_cache->device_blocked.begin(), - verified_cache->device_blocked.end(), - this->deviceId.toStdString()), - verified_cache->device_blocked.end()); - } else { - cache::setVerifiedCache( - this->userId.toStdString(), - DeviceVerifiedCache{{this->deviceId.toStdString()}, {}, {}}); - } + cache::markDeviceVerified(this->userId.toStdString(), this->deviceId.toStdString()); emit deviceVerified(); emit refreshProfile(); this->deleteLater(); } + //! callback function to keep track of devices void -DeviceVerificationFlow::callback_fn(const mtx::responses::QueryKeys &res, +DeviceVerificationFlow::callback_fn(const UserKeyCache &res, mtx::http::RequestErr err, std::string user_id) { @@ -704,35 +707,22 @@ DeviceVerificationFlow::callback_fn(const mtx::responses::QueryKeys &res, return; } - if (res.device_keys.empty() || (res.device_keys.find(user_id) == res.device_keys.end())) { + if (res.device_keys.empty() || + (res.device_keys.find(deviceId.toStdString()) == res.device_keys.end())) { nhlog::net()->warn("no devices retrieved {}", user_id); return; } - for (auto x : res.device_keys) { - for (auto y : x.second) { - auto z = y.second; - if (z.user_id == user_id && z.device_id == this->deviceId.toStdString()) { - for (auto a : z.keys) { - // TODO: Verify Signatures - this->device_keys[a.first] = a.second; - } - } - } + for (const auto &[algorithm, key] : res.device_keys.at(deviceId.toStdString()).keys) { + // TODO: Verify Signatures + this->device_keys[algorithm] = key; } } void DeviceVerificationFlow::unverify() { - auto verified_cache = cache::getVerifiedCache(this->userId.toStdString()); - if (verified_cache.has_value()) { - auto it = std::remove(verified_cache->device_verified.begin(), - verified_cache->device_verified.end(), - this->deviceId.toStdString()); - verified_cache->device_verified.erase(it); - cache::setVerifiedCache(this->userId.toStdString(), verified_cache.value()); - } + cache::markDeviceUnverified(this->userId.toStdString(), this->deviceId.toStdString()); emit refreshProfile(); } diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index b85cbec2..31d2facc 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -1,11 +1,13 @@ #pragma once -#include "Olm.h" - -#include "MatrixClient.h" -#include "mtx/responses/crypto.hpp" #include +#include + +#include "CacheCryptoStructs.h" +#include "MatrixClient.h" +#include "Olm.h" + class QTimer; using sas_ptr = std::unique_ptr; @@ -71,9 +73,7 @@ public: void setSender(bool sender_); void setEventId(std::string event_id); - void callback_fn(const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err, - std::string user_id); + void callback_fn(const UserKeyCache &res, mtx::http::RequestErr err, std::string user_id); nlohmann::json canonical_json; diff --git a/src/timeline/.TimelineModel.cpp.swn b/src/timeline/.TimelineModel.cpp.swn deleted file mode 100644 index 9e965702..00000000 Binary files a/src/timeline/.TimelineModel.cpp.swn and /dev/null differ diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 08c30097..2ea3f7b6 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -89,12 +89,10 @@ UserProfile::fetchDeviceList(const QString &userID) { auto localUser = utils::localUser(); - mtx::requests::QueryKeys req; - req.device_keys[userID.toStdString()] = {}; ChatPage::instance()->query_keys( - req, - [user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err) { + userID.toStdString(), + [other_user_id = userID.toStdString(), this](const UserKeyCache &other_user_keys, + mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("failed to query device keys: {},{}", err->matrix_error.errcode, @@ -102,20 +100,11 @@ UserProfile::fetchDeviceList(const QString &userID) return; } - if (res.device_keys.empty() || - (res.device_keys.find(user_id) == res.device_keys.end())) { - nhlog::net()->warn("no devices retrieved {}", user_id); - return; - } - // Finding if the User is Verified or not based on the Signatures - mtx::requests::QueryKeys req; - req.device_keys[utils::localUser().toStdString()] = {}; - ChatPage::instance()->query_keys( - req, - [user_id, other_res = res, this](const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err) { + utils::localUser().toStdString(), + [other_user_id, other_user_keys, this](const UserKeyCache &res, + mtx::http::RequestErr err) { using namespace mtx; std::string local_user_id = utils::localUser().toStdString(); @@ -126,34 +115,28 @@ UserProfile::fetchDeviceList(const QString &userID) return; } - if (res.device_keys.empty() || - (res.device_keys.find(local_user_id) == res.device_keys.end())) { - nhlog::net()->warn("no devices retrieved {}", user_id); + if (res.device_keys.empty()) { + nhlog::net()->warn("no devices retrieved {}", local_user_id); return; } std::vector deviceInfo; - auto devices = other_res.device_keys.at(user_id); - auto device_verified = cache::getVerifiedCache(user_id); + auto devices = other_user_keys.device_keys; + auto device_verified = cache::verificationStatus(other_user_id); if (device_verified.has_value()) { - isUserVerified = device_verified.value().is_user_verified; + // TODO: properly check cross-signing signatures here + isUserVerified = !device_verified->verified_master_key.empty(); } std::optional lmk, lsk, luk, mk, sk, uk; - if (!res.master_keys.empty()) - lmk = res.master_keys.at(local_user_id); - if (!res.user_signing_keys.empty()) - luk = res.user_signing_keys.at(local_user_id); - if (!res.self_signing_keys.empty()) - lsk = res.self_signing_keys.at(local_user_id); - if (!other_res.master_keys.empty()) - mk = other_res.master_keys.at(user_id); - if (!other_res.user_signing_keys.empty()) - uk = other_res.user_signing_keys.at(user_id); - if (!other_res.self_signing_keys.empty()) - sk = other_res.self_signing_keys.at(user_id); + lmk = res.master_keys; + luk = res.user_signing_keys; + lsk = res.self_signing_keys; + mk = other_user_keys.master_keys; + uk = other_user_keys.user_signing_keys; + sk = other_user_keys.self_signing_keys; // First checking if the user is verified if (luk.has_value() && mk.has_value()) { @@ -202,7 +185,7 @@ UserProfile::fetchDeviceList(const QString &userID) device_verified->device_blocked.end()) verified = verification::Status::BLOCKED; } else if (isUserVerified) { - device_verified = DeviceVerifiedCache{}; + device_verified = VerificationCache{}; } // won't check for already verified devices @@ -211,7 +194,7 @@ UserProfile::fetchDeviceList(const QString &userID) if ((sk.has_value()) && (!device.signatures.empty())) { for (auto sign_key : sk.value().keys) { auto signs = - device.signatures.at(user_id); + device.signatures.at(other_user_id); try { if (olm::client() ->ed25519_verify_sig( @@ -232,12 +215,13 @@ UserProfile::fetchDeviceList(const QString &userID) } } - if (device_verified.has_value()) { - device_verified.value().is_user_verified = - isUserVerified; - cache::setVerifiedCache(user_id, - device_verified.value()); - } + // TODO(Nico): properly show cross-signing + // if (device_verified.has_value()) { + // device_verified.value().is_user_verified = + // isUserVerified; + // cache::setVerifiedCache(user_id, + // device_verified.value()); + //} deviceInfo.push_back( {QString::fromStdString(d.first),