Clean up verification and key cache a bit

This commit is contained in:
Nicolas Werner 2020-10-02 01:14:42 +02:00
parent 4802c34009
commit 94690ebd4c
11 changed files with 416 additions and 335 deletions

View File

@ -136,7 +136,7 @@ ApplicationWindow{
model: profile.deviceList model: profile.deviceList
delegate: RowLayout{ delegate: RowLayout{
width: parent.width width: devicelist.width
spacing: 4 spacing: 4
ColumnLayout{ ColumnLayout{

View File

@ -91,6 +91,7 @@ Q_DECLARE_METATYPE(RoomMember)
Q_DECLARE_METATYPE(mtx::responses::Timeline) Q_DECLARE_METATYPE(mtx::responses::Timeline)
Q_DECLARE_METATYPE(RoomSearchResult) Q_DECLARE_METATYPE(RoomSearchResult)
Q_DECLARE_METATYPE(RoomInfo) Q_DECLARE_METATYPE(RoomInfo)
Q_DECLARE_METATYPE(mtx::responses::QueryKeys)
namespace { namespace {
std::unique_ptr<Cache> instance_ = nullptr; std::unique_ptr<Cache> instance_ = nullptr;
@ -155,26 +156,7 @@ Cache::Cache(const QString &userId, QObject *parent)
, localUserId_{userId} , localUserId_{userId}
{ {
setup(); setup();
connect( connect(this, &Cache::userKeysUpdate, this, &Cache::updateUserKeys, Qt::QueuedConnection);
this,
&Cache::updateUserCacheFlag,
this,
[this](const std::string &user_id) {
std::optional<UserCache> 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);
} }
void void
@ -1017,6 +999,8 @@ Cache::saveState(const mtx::responses::Sync &res)
using namespace mtx::events; using namespace mtx::events;
auto user_id = this->localUserId_.toStdString(); auto user_id = this->localUserId_.toStdString();
auto currentBatchToken = nextBatchToken();
auto txn = lmdb::txn::begin(env_); auto txn = lmdb::txn::begin(env_);
setNextBatchToken(txn, res.next_batch); setNextBatchToken(txn, res.next_batch);
@ -1034,6 +1018,8 @@ Cache::saveState(const mtx::responses::Sync &res)
ev); ev);
} }
auto userKeyCacheDb = getUserKeysDb(txn);
// Save joined rooms // Save joined rooms
for (const auto &room : res.rooms.join) { for (const auto &room : res.rooms.join) {
auto statesdb = getStatesDb(txn, room.first); auto statesdb = getStatesDb(txn, room.first);
@ -1107,7 +1093,8 @@ Cache::saveState(const mtx::responses::Sync &res)
savePresence(txn, res.presence); 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); removeLeftRooms(txn, res.rooms.leave);
@ -3098,126 +3085,246 @@ Cache::statusMessage(const std::string &user_id)
} }
void void
to_json(json &j, const UserCache &info) to_json(json &j, const UserKeyCache &info)
{ {
j["keys"] = info.keys; j["device_keys"] = info.device_keys;
j["isUpdated"] = info.isUpdated; 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 void
from_json(const json &j, UserCache &info) from_json(const json &j, UserKeyCache &info)
{ {
info.keys = j.at("keys").get<mtx::responses::QueryKeys>(); info.device_keys = j.value("device_keys", std::map<std::string, mtx::crypto::DeviceKeys>{});
info.isUpdated = j.at("isUpdated").get<bool>(); 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<UserCache> std::optional<UserKeyCache>
Cache::getUserCache(const std::string &user_id) Cache::userKeys(const std::string &user_id)
{ {
lmdb::val verifiedVal; lmdb::val keys;
auto txn = lmdb::txn::begin(env_); try {
auto db = getUserCacheDb(txn); auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY);
auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal); auto db = getUserKeysDb(txn);
auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), keys);
txn.commit(); if (res) {
return json::parse(std::string_view(keys.data(), keys.size()))
UserCache verified_state; .get<UserKeyCache>();
if (res) { } else {
verified_state = json::parse(std::string(verifiedVal.data(), verifiedVal.size())); return {};
return verified_state; }
} else { } catch (std::exception &) {
return {}; return {};
} }
} }
//! be careful when using make sure is_user_verified is not changed void
int Cache::updateUserKeys(const std::string &sync_token, const mtx::responses::QueryKeys &keyQuery)
Cache::setUserCache(const std::string &user_id, const UserCache &body)
{ {
auto txn = lmdb::txn::begin(env_); 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<std::string, UserKeyCache> 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 if (res) {
Cache::updateUserCache(const mtx::responses::DeviceLists body) auto last_changed =
{ json::parse(std::string_view(oldKeys.data(), oldKeys.size()))
for (std::string user_id : body.changed) { .get<UserKeyCache>()
emit updateUserCacheFlag(user_id); .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(); txn.commit();
return res;
} }
void void
to_json(json &j, const DeviceVerifiedCache &info) Cache::deleteUserKeys(lmdb::txn &txn, lmdb::dbi &db, const std::vector<std::string> &user_ids)
{ {
j["is_user_verified"] = info.is_user_verified; for (const auto &user_id : user_ids)
j["cross_verified"] = info.cross_verified; lmdb::dbi_del(txn, db, lmdb::val(user_id), nullptr);
j["device_verified"] = info.device_verified;
j["device_blocked"] = info.device_blocked;
} }
void void
from_json(const json &j, DeviceVerifiedCache &info) Cache::markUserKeysOutOfDate(lmdb::txn &txn,
lmdb::dbi &db,
const std::vector<std::string> &user_ids,
const std::string &sync_token)
{ {
info.is_user_verified = j.at("is_user_verified"); mtx::requests::QueryKeys query;
info.cross_verified = j.at("cross_verified").get<std::vector<std::string>>(); query.token = sync_token;
info.device_verified = j.at("device_verified").get<std::vector<std::string>>();
info.device_blocked = j.at("device_blocked").get<std::vector<std::string>>(); 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<UserKeyCache>();
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<int>(err->status_code));
return;
}
emit userKeysUpdate(sync_token, keys);
});
} }
std::optional<DeviceVerifiedCache> void
Cache::getVerifiedCache(const std::string &user_id) 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<std::vector<std::string>>();
info.device_verified = j.at("device_verified").get<std::vector<std::string>>();
info.device_blocked = j.at("device_blocked").get<std::vector<std::string>>();
}
std::optional<VerificationCache>
Cache::verificationStatus(const std::string &user_id)
{ {
lmdb::val verifiedVal; lmdb::val verifiedVal;
auto txn = lmdb::txn::begin(env_); auto txn = lmdb::txn::begin(env_);
auto db = getDeviceVerifiedDb(txn); auto db = getVerificationDb(txn);
auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal);
txn.commit(); try {
VerificationCache verified_state;
DeviceVerifiedCache verified_state; auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal);
if (res) { if (res) {
verified_state = json::parse(std::string(verifiedVal.data(), verifiedVal.size())); verified_state =
return verified_state; json::parse(std::string_view(verifiedVal.data(), verifiedVal.size()));
} else { return verified_state;
} else {
return {};
}
} catch (std::exception &) {
return {}; return {};
} }
} }
int void
Cache::setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body) Cache::markDeviceVerified(const std::string &user_id, const std::string &key)
{ {
lmdb::val val;
auto txn = lmdb::txn::begin(env_); 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 void
@ -3401,41 +3508,6 @@ statusMessage(const std::string &user_id)
{ {
return instance_->statusMessage(user_id); return instance_->statusMessage(user_id);
} }
std::optional<UserCache>
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<DeviceVerifiedCache>
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. //! Load saved data for the display names & avatars.
void void
@ -3444,6 +3516,43 @@ populateMembers()
instance_->populateMembers(); instance_->populateMembers();
} }
// user cache stores user keys
std::optional<UserKeyCache>
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<VerificationCache>
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<std::string> std::vector<std::string>
joinedRooms() joinedRooms()
{ {

View File

@ -60,25 +60,21 @@ presenceState(const std::string &user_id);
std::string std::string
statusMessage(const std::string &user_id); statusMessage(const std::string &user_id);
//! user Cache // user cache stores user keys
std::optional<UserCache> std::optional<UserKeyCache>
getUserCache(const std::string &user_id); userKeys(const std::string &user_id);
void void
updateUserCache(const mtx::responses::DeviceLists body); updateUserKeys(const std::string &sync_token, const mtx::responses::QueryKeys &keyQuery);
int // device & user verification cache
setUserCache(const std::string &user_id, const UserCache &body); std::optional<VerificationCache>
verificationStatus(const std::string &user_id);
int void
deleteUserCache(const std::string &user_id); markDeviceVerified(const std::string &user_id, const std::string &key);
void
//! verified Cache markDeviceUnverified(const std::string &user_id, const std::string &key);
std::optional<DeviceVerifiedCache> void
getVerifiedCache(const std::string &user_id); markMasterKeyVerified(const std::string &user_id, const std::string &key);
int
setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body);
//! Load saved data for the display names & avatars. //! Load saved data for the display names & avatars.
void void

View File

@ -67,52 +67,38 @@ struct OlmSessionStorage
}; };
// this will store the keys of the user with whom a encrypted room is shared with // 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 //! Device id to device keys
mtx::responses::QueryKeys keys; std::map<std::string, mtx::crypto::DeviceKeys> device_keys;
//! if the current cache is updated or not //! corss signing keys
bool isUpdated = false; mtx::crypto::CrossSigningKeys master_keys, user_signing_keys, self_signing_keys;
//! Sync token when nheko last fetched the keys
UserCache(mtx::responses::QueryKeys res, bool isUpdated_ = false) std::string updated_at;
: keys(res) //! Sync token when the keys last changed. updated != last_changed means they are outdated.
, isUpdated(isUpdated_) std::string last_changed;
{}
UserCache() {}
}; };
void void
to_json(nlohmann::json &j, const UserCache &info); to_json(nlohmann::json &j, const UserKeyCache &info);
void 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 // 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 // UserKeyCache stores only keys of users with which encrypted room is shared
struct DeviceVerifiedCache struct VerificationCache
{ {
//! list of verified device_ids with device-verification //! list of verified device_ids with device-verification
std::vector<std::string> device_verified; std::vector<std::string> device_verified;
//! list of verified device_ids with cross-signing //! list of verified device_ids with cross-signing, calculated from master key
std::vector<std::string> cross_verified; std::vector<std::string> cross_verified;
//! list of devices the user blocks //! list of devices the user blocks
std::vector<std::string> device_blocked; std::vector<std::string> device_blocked;
//! this stores if the user is verified (with cross-signing) //! The verified master key.
bool is_user_verified = false; std::string verified_master_key;
DeviceVerifiedCache(std::vector<std::string> device_verified_,
std::vector<std::string> cross_verified_,
std::vector<std::string> 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() {}
}; };
void void
to_json(nlohmann::json &j, const DeviceVerifiedCache &info); to_json(nlohmann::json &j, const VerificationCache &info);
void void
from_json(const nlohmann::json &j, DeviceVerifiedCache &info); from_json(const nlohmann::json &j, VerificationCache &info);

View File

@ -55,14 +55,22 @@ public:
std::string statusMessage(const std::string &user_id); std::string statusMessage(const std::string &user_id);
// user cache stores user keys // user cache stores user keys
std::optional<UserCache> getUserCache(const std::string &user_id); std::optional<UserKeyCache> userKeys(const std::string &user_id);
void updateUserCache(const mtx::responses::DeviceLists body); void updateUserKeys(const std::string &sync_token,
int setUserCache(const std::string &user_id, const UserCache &body); const mtx::responses::QueryKeys &keyQuery);
int deleteUserCache(const std::string &user_id); void markUserKeysOutOfDate(lmdb::txn &txn,
lmdb::dbi &db,
const std::vector<std::string> &user_ids,
const std::string &sync_token);
void deleteUserKeys(lmdb::txn &txn,
lmdb::dbi &db,
const std::vector<std::string> &user_ids);
// device verified cache // device & user verification cache
std::optional<DeviceVerifiedCache> getVerifiedCache(const std::string &user_id); std::optional<VerificationCache> verificationStatus(const std::string &user_id);
int setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body); 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 removeDisplayName(const QString &room_id, const QString &user_id);
static void removeAvatarUrl(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<QString> &event_ids); void newReadReceipts(const QString &room_id, const std::vector<QString> &event_ids);
void roomReadStatus(const std::map<QString, bool> &status); void roomReadStatus(const std::map<QString, bool> &status);
void removeNotification(const QString &room_id, const QString &event_id); void removeNotification(const QString &room_id, const QString &event_id);
void updateUserCacheFlag(const std::string &user_id); void userKeysUpdate(const std::string &sync_token,
void deleteLeftUsers(const std::string &user_id); const mtx::responses::QueryKeys &keyQuery);
private: private:
//! Save an invited room. //! Save an invited room.
@ -539,12 +547,12 @@ private:
return lmdb::dbi::open(txn, "presence", MDB_CREATE); 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); return lmdb::dbi::open(txn, "verified", MDB_CREATE);
} }

View File

@ -1466,35 +1466,43 @@ ChatPage::initiateLogout()
} }
void void
ChatPage::query_keys( ChatPage::query_keys(const std::string &user_id,
const mtx::requests::QueryKeys &req, std::function<void(const UserKeyCache &, mtx::http::RequestErr)> cb)
std::function<void(const mtx::responses::QueryKeys &, mtx::http::RequestErr)> cb)
{ {
std::string user_id = req.device_keys.begin()->first; auto cache_ = cache::userKeys(user_id);
auto cache_ = cache::getUserCache(user_id);
if (cache_.has_value()) { if (cache_.has_value()) {
if (cache_.value().isUpdated) { if (!cache_->updated_at.empty() && cache_->updated_at == cache_->last_changed) {
cb(cache_.value().keys, {}); cb(cache_.value(), {});
} else { return;
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<int>(err->status_code));
return;
}
cache::setUserCache(std::move(user_id),
std::move(UserCache{res, true}));
cb(res, err);
});
} }
} 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<int>(err->status_code));
cb({}, err);
return;
}
cache::updateUserKeys(last_changed, res);
auto keys = cache::userKeys(user_id);
cb(keys.value_or(UserKeyCache{}), err);
});
} }
template<typename T> template<typename T>

View File

@ -35,6 +35,7 @@
#include <QTimer> #include <QTimer>
#include <QWidget> #include <QWidget>
#include "CacheCryptoStructs.h"
#include "CacheStructs.h" #include "CacheStructs.h"
#include "CallManager.h" #include "CallManager.h"
#include "CommunitiesList.h" #include "CommunitiesList.h"
@ -89,9 +90,8 @@ public:
//! Show the room/group list (if it was visible). //! Show the room/group list (if it was visible).
void showSideBars(); void showSideBars();
void initiateLogout(); void initiateLogout();
void query_keys( void query_keys(const std::string &req,
const mtx::requests::QueryKeys &req, std::function<void(const UserKeyCache &, mtx::http::RequestErr)> cb);
std::function<void(const mtx::responses::QueryKeys &, mtx::http::RequestErr)> cb);
void focusMessageInput(); void focusMessageInput();
QString status() const; QString status() const;

View File

@ -328,22 +328,14 @@ DeviceVerificationFlow::setTransactionId(QString transaction_id_)
void void
DeviceVerificationFlow::setUserId(QString userID) DeviceVerificationFlow::setUserId(QString userID)
{ {
this->userId = userID; this->userId = userID;
this->toClient = mtx::identifiers::parse<mtx::identifiers::User>(userID.toStdString()); this->toClient = mtx::identifiers::parse<mtx::identifiers::User>(userID.toStdString());
auto user_cache = cache::getUserCache(userID.toStdString());
if (user_cache.has_value()) { auto user_id = userID.toStdString();
this->callback_fn(user_cache->keys, {}, userID.toStdString()); ChatPage::instance()->query_keys(
} else { user_id, [user_id, this](const UserKeyCache &res, mtx::http::RequestErr err) {
mtx::requests::QueryKeys req; this->callback_fn(res, err, user_id);
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);
});
}
} }
void void
@ -622,30 +614,52 @@ DeviceVerificationFlow::sendVerificationKey()
(model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationKey); (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<std::string, std::string> 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 //! sends the mac of the keys
void void
DeviceVerificationFlow::sendVerificationMac() DeviceVerificationFlow::sendVerificationMac()
{ {
mtx::events::msg::KeyVerificationMac req; std::map<std::string, std::string> 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() + mtx::events::msg::KeyVerificationMac req =
http::client()->device_id() + this->toClient.to_string() + key_verification_mac(sas.get(),
this->deviceId.toStdString() + this->transaction_id; http::client()->user_id(),
http::client()->device_id(),
//! this vector stores the type of the key and the key this->toClient,
std::vector<std::pair<std::string, std::string>> key_list; this->deviceId.toStdString(),
key_list.push_back(make_pair("ed25519", olm::client()->identity_keys().ed25519)); this->transaction_id,
std::sort(key_list.begin(), key_list.end()); key_list);
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");
if (this->type == DeviceVerificationFlow::Type::ToDevice) { if (this->type == DeviceVerificationFlow::Type::ToDevice) {
mtx::requests::ToDeviceMessages<mtx::events::msg::KeyVerificationMac> body; mtx::requests::ToDeviceMessages<mtx::events::msg::KeyVerificationMac> body;
@ -673,27 +687,16 @@ DeviceVerificationFlow::sendVerificationMac()
void void
DeviceVerificationFlow::acceptDevice() DeviceVerificationFlow::acceptDevice()
{ {
auto verified_cache = cache::getVerifiedCache(this->userId.toStdString()); cache::markDeviceVerified(this->userId.toStdString(), this->deviceId.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()}, {}, {}});
}
emit deviceVerified(); emit deviceVerified();
emit refreshProfile(); emit refreshProfile();
this->deleteLater(); this->deleteLater();
} }
//! callback function to keep track of devices //! callback function to keep track of devices
void void
DeviceVerificationFlow::callback_fn(const mtx::responses::QueryKeys &res, DeviceVerificationFlow::callback_fn(const UserKeyCache &res,
mtx::http::RequestErr err, mtx::http::RequestErr err,
std::string user_id) std::string user_id)
{ {
@ -704,35 +707,22 @@ DeviceVerificationFlow::callback_fn(const mtx::responses::QueryKeys &res,
return; 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); nhlog::net()->warn("no devices retrieved {}", user_id);
return; return;
} }
for (auto x : res.device_keys) { for (const auto &[algorithm, key] : res.device_keys.at(deviceId.toStdString()).keys) {
for (auto y : x.second) { // TODO: Verify Signatures
auto z = y.second; this->device_keys[algorithm] = key;
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;
}
}
}
} }
} }
void void
DeviceVerificationFlow::unverify() DeviceVerificationFlow::unverify()
{ {
auto verified_cache = cache::getVerifiedCache(this->userId.toStdString()); cache::markDeviceUnverified(this->userId.toStdString(), this->deviceId.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());
}
emit refreshProfile(); emit refreshProfile();
} }

View File

@ -1,11 +1,13 @@
#pragma once #pragma once
#include "Olm.h"
#include "MatrixClient.h"
#include "mtx/responses/crypto.hpp"
#include <QObject> #include <QObject>
#include <mtx/responses/crypto.hpp>
#include "CacheCryptoStructs.h"
#include "MatrixClient.h"
#include "Olm.h"
class QTimer; class QTimer;
using sas_ptr = std::unique_ptr<mtx::crypto::SAS>; using sas_ptr = std::unique_ptr<mtx::crypto::SAS>;
@ -71,9 +73,7 @@ public:
void setSender(bool sender_); void setSender(bool sender_);
void setEventId(std::string event_id); void setEventId(std::string event_id);
void callback_fn(const mtx::responses::QueryKeys &res, void callback_fn(const UserKeyCache &res, mtx::http::RequestErr err, std::string user_id);
mtx::http::RequestErr err,
std::string user_id);
nlohmann::json canonical_json; nlohmann::json canonical_json;

Binary file not shown.

View File

@ -89,12 +89,10 @@ UserProfile::fetchDeviceList(const QString &userID)
{ {
auto localUser = utils::localUser(); auto localUser = utils::localUser();
mtx::requests::QueryKeys req;
req.device_keys[userID.toStdString()] = {};
ChatPage::instance()->query_keys( ChatPage::instance()->query_keys(
req, userID.toStdString(),
[user_id = userID.toStdString(), this](const mtx::responses::QueryKeys &res, [other_user_id = userID.toStdString(), this](const UserKeyCache &other_user_keys,
mtx::http::RequestErr err) { mtx::http::RequestErr err) {
if (err) { if (err) {
nhlog::net()->warn("failed to query device keys: {},{}", nhlog::net()->warn("failed to query device keys: {},{}",
err->matrix_error.errcode, err->matrix_error.errcode,
@ -102,20 +100,11 @@ UserProfile::fetchDeviceList(const QString &userID)
return; 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 // 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( ChatPage::instance()->query_keys(
req, utils::localUser().toStdString(),
[user_id, other_res = res, this](const mtx::responses::QueryKeys &res, [other_user_id, other_user_keys, this](const UserKeyCache &res,
mtx::http::RequestErr err) { mtx::http::RequestErr err) {
using namespace mtx; using namespace mtx;
std::string local_user_id = utils::localUser().toStdString(); std::string local_user_id = utils::localUser().toStdString();
@ -126,34 +115,28 @@ UserProfile::fetchDeviceList(const QString &userID)
return; return;
} }
if (res.device_keys.empty() || if (res.device_keys.empty()) {
(res.device_keys.find(local_user_id) == res.device_keys.end())) { nhlog::net()->warn("no devices retrieved {}", local_user_id);
nhlog::net()->warn("no devices retrieved {}", user_id);
return; return;
} }
std::vector<DeviceInfo> deviceInfo; std::vector<DeviceInfo> deviceInfo;
auto devices = other_res.device_keys.at(user_id); auto devices = other_user_keys.device_keys;
auto device_verified = cache::getVerifiedCache(user_id); auto device_verified = cache::verificationStatus(other_user_id);
if (device_verified.has_value()) { 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<crypto::CrossSigningKeys> lmk, lsk, luk, mk, sk, uk; std::optional<crypto::CrossSigningKeys> lmk, lsk, luk, mk, sk, uk;
if (!res.master_keys.empty()) lmk = res.master_keys;
lmk = res.master_keys.at(local_user_id); luk = res.user_signing_keys;
if (!res.user_signing_keys.empty()) lsk = res.self_signing_keys;
luk = res.user_signing_keys.at(local_user_id); mk = other_user_keys.master_keys;
if (!res.self_signing_keys.empty()) uk = other_user_keys.user_signing_keys;
lsk = res.self_signing_keys.at(local_user_id); sk = other_user_keys.self_signing_keys;
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);
// First checking if the user is verified // First checking if the user is verified
if (luk.has_value() && mk.has_value()) { if (luk.has_value() && mk.has_value()) {
@ -202,7 +185,7 @@ UserProfile::fetchDeviceList(const QString &userID)
device_verified->device_blocked.end()) device_verified->device_blocked.end())
verified = verification::Status::BLOCKED; verified = verification::Status::BLOCKED;
} else if (isUserVerified) { } else if (isUserVerified) {
device_verified = DeviceVerifiedCache{}; device_verified = VerificationCache{};
} }
// won't check for already verified devices // won't check for already verified devices
@ -211,7 +194,7 @@ UserProfile::fetchDeviceList(const QString &userID)
if ((sk.has_value()) && (!device.signatures.empty())) { if ((sk.has_value()) && (!device.signatures.empty())) {
for (auto sign_key : sk.value().keys) { for (auto sign_key : sk.value().keys) {
auto signs = auto signs =
device.signatures.at(user_id); device.signatures.at(other_user_id);
try { try {
if (olm::client() if (olm::client()
->ed25519_verify_sig( ->ed25519_verify_sig(
@ -232,12 +215,13 @@ UserProfile::fetchDeviceList(const QString &userID)
} }
} }
if (device_verified.has_value()) { // TODO(Nico): properly show cross-signing
device_verified.value().is_user_verified = // if (device_verified.has_value()) {
isUserVerified; // device_verified.value().is_user_verified =
cache::setVerifiedCache(user_id, // isUserVerified;
device_verified.value()); // cache::setVerifiedCache(user_id,
} // device_verified.value());
//}
deviceInfo.push_back( deviceInfo.push_back(
{QString::fromStdString(d.first), {QString::fromStdString(d.first),