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
delegate: RowLayout{
width: parent.width
width: devicelist.width
spacing: 4
ColumnLayout{

View file

@ -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<Cache> 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<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);
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<mtx::responses::QueryKeys>();
info.isUpdated = j.at("isUpdated").get<bool>();
info.device_keys = j.value("device_keys", std::map<std::string, mtx::crypto::DeviceKeys>{});
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>
Cache::getUserCache(const std::string &user_id)
std::optional<UserKeyCache>
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<UserKeyCache>();
} 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<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
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<UserKeyCache>()
.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<std::string> &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<std::string> &user_ids,
const std::string &sync_token)
{
info.is_user_verified = j.at("is_user_verified");
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>>();
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<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>
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<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;
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<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.
void
@ -3444,6 +3516,43 @@ 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>
joinedRooms()
{

View file

@ -60,25 +60,21 @@ presenceState(const std::string &user_id);
std::string
statusMessage(const std::string &user_id);
//! user Cache
std::optional<UserCache>
getUserCache(const std::string &user_id);
// user cache stores user keys
std::optional<UserKeyCache>
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<DeviceVerifiedCache>
getVerifiedCache(const std::string &user_id);
int
setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body);
// device & user verification cache
std::optional<VerificationCache>
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

View file

@ -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<std::string, mtx::crypto::DeviceKeys> 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<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;
//! list of devices the user blocks
std::vector<std::string> device_blocked;
//! this stores if the user is verified (with cross-signing)
bool is_user_verified = false;
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() {}
//! 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);

View file

@ -55,14 +55,22 @@ public:
std::string statusMessage(const std::string &user_id);
// user cache stores user keys
std::optional<UserCache> 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<UserKeyCache> 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<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
std::optional<DeviceVerifiedCache> getVerifiedCache(const std::string &user_id);
int setVerifiedCache(const std::string &user_id, const DeviceVerifiedCache &body);
// device & user verification cache
std::optional<VerificationCache> 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<QString> &event_ids);
void roomReadStatus(const std::map<QString, bool> &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);
}

View file

@ -1466,35 +1466,43 @@ ChatPage::initiateLogout()
}
void
ChatPage::query_keys(
const mtx::requests::QueryKeys &req,
std::function<void(const mtx::responses::QueryKeys &, mtx::http::RequestErr)> cb)
ChatPage::query_keys(const std::string &user_id,
std::function<void(const UserKeyCache &, mtx::http::RequestErr)> 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<int>(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<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>

View file

@ -35,6 +35,7 @@
#include <QTimer>
#include <QWidget>
#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<void(const mtx::responses::QueryKeys &, mtx::http::RequestErr)> cb);
void query_keys(const std::string &req,
std::function<void(const UserKeyCache &, mtx::http::RequestErr)> cb);
void focusMessageInput();
QString status() const;

View file

@ -328,22 +328,14 @@ DeviceVerificationFlow::setTransactionId(QString transaction_id_)
void
DeviceVerificationFlow::setUserId(QString userID)
{
this->userId = userID;
this->toClient = mtx::identifiers::parse<mtx::identifiers::User>(userID.toStdString());
auto user_cache = cache::getUserCache(userID.toStdString());
this->userId = userID;
this->toClient = mtx::identifiers::parse<mtx::identifiers::User>(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<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
void
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() +
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<std::pair<std::string, std::string>> 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<mtx::events::msg::KeyVerificationMac> 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();
}

View file

@ -1,11 +1,13 @@
#pragma once
#include "Olm.h"
#include "MatrixClient.h"
#include "mtx/responses/crypto.hpp"
#include <QObject>
#include <mtx/responses/crypto.hpp>
#include "CacheCryptoStructs.h"
#include "MatrixClient.h"
#include "Olm.h"
class QTimer;
using sas_ptr = std::unique_ptr<mtx::crypto::SAS>;
@ -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;

Binary file not shown.

View file

@ -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> 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<crypto::CrossSigningKeys> 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),