mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 11:00:48 +03:00
Automatically fetch keys for undecrypted messages after verification
Also fix rerendering edited messages after keys are received. fixes #1375 fixes #770 fixes #888
This commit is contained in:
parent
05727b8a45
commit
20740c9976
8 changed files with 81 additions and 16 deletions
|
@ -547,6 +547,12 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token)
|
||||||
&Cache::newReadReceipts,
|
&Cache::newReadReceipts,
|
||||||
view_manager_,
|
view_manager_,
|
||||||
&TimelineViewManager::updateReadReceipts);
|
&TimelineViewManager::updateReadReceipts);
|
||||||
|
|
||||||
|
connect(cache::client(), &Cache::secretChanged, this, [this](const std::string &secret) {
|
||||||
|
if (secret == mtx::secret_storage::secrets::megolm_backup_v1) {
|
||||||
|
getBackupVersion();
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (const lmdb::error &e) {
|
} catch (const lmdb::error &e) {
|
||||||
nhlog::db()->critical("failure during boot: {}", e.what());
|
nhlog::db()->critical("failure during boot: {}", e.what());
|
||||||
emit dropToLoginPageCb(tr("Failed to open database, logging out!"));
|
emit dropToLoginPageCb(tr("Failed to open database, logging out!"));
|
||||||
|
@ -1224,7 +1230,7 @@ ChatPage::getBackupVersion()
|
||||||
}
|
}
|
||||||
|
|
||||||
// switch to UI thread for secrets stuff
|
// switch to UI thread for secrets stuff
|
||||||
QTimer::singleShot(0, this, [res] {
|
QTimer::singleShot(0, this, [this, res] {
|
||||||
auto auth_data = nlohmann::json::parse(res.auth_data);
|
auto auth_data = nlohmann::json::parse(res.auth_data);
|
||||||
|
|
||||||
if (res.algorithm == "m.megolm_backup.v1.curve25519-aes-sha2") {
|
if (res.algorithm == "m.megolm_backup.v1.curve25519-aes-sha2") {
|
||||||
|
@ -1247,11 +1253,17 @@ ChatPage::getBackupVersion()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto oldBackupVersion = cache::client()->backupVersion();
|
||||||
|
|
||||||
nhlog::crypto()->info("Using online key backup.");
|
nhlog::crypto()->info("Using online key backup.");
|
||||||
OnlineBackupVersion data{};
|
OnlineBackupVersion data{};
|
||||||
data.algorithm = res.algorithm;
|
data.algorithm = res.algorithm;
|
||||||
data.version = res.version;
|
data.version = res.version;
|
||||||
cache::client()->saveBackupVersion(data);
|
cache::client()->saveBackupVersion(data);
|
||||||
|
|
||||||
|
if (!oldBackupVersion || oldBackupVersion->version != data.version) {
|
||||||
|
view_manager_->rooms()->refetchOnlineKeyBackupKeys();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
nhlog::crypto()->info("Unsupported key backup algorithm: {}", res.algorithm);
|
nhlog::crypto()->info("Unsupported key backup algorithm: {}", res.algorithm);
|
||||||
cache::client()->deleteBackupVersion();
|
cache::client()->deleteBackupVersion();
|
||||||
|
|
|
@ -180,6 +180,7 @@ signals:
|
||||||
QString reason = "",
|
QString reason = "",
|
||||||
bool failedJoin = false,
|
bool failedJoin = false,
|
||||||
bool promptForConfirmation = true);
|
bool promptForConfirmation = true);
|
||||||
|
void newOnlineKeyBackupAvailable();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void logout();
|
void logout();
|
||||||
|
|
|
@ -896,12 +896,15 @@ download_full_keybackup()
|
||||||
{
|
{
|
||||||
if (!UserSettings::instance()->useOnlineKeyBackup()) {
|
if (!UserSettings::instance()->useOnlineKeyBackup()) {
|
||||||
// Online key backup disabled
|
// Online key backup disabled
|
||||||
|
nhlog::crypto()->debug("Not downloading full online key backup, because it is disabled.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto backupVersion = cache::client()->backupVersion();
|
auto backupVersion = cache::client()->backupVersion();
|
||||||
if (!backupVersion) {
|
if (!backupVersion) {
|
||||||
// no trusted OKB
|
// no trusted OKB
|
||||||
|
nhlog::crypto()->debug(
|
||||||
|
"Not downloading full online key backup, because we don't have a version for it.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -910,10 +913,14 @@ download_full_keybackup()
|
||||||
auto decryptedSecret = cache::secret(mtx::secret_storage::secrets::megolm_backup_v1);
|
auto decryptedSecret = cache::secret(mtx::secret_storage::secrets::megolm_backup_v1);
|
||||||
if (!decryptedSecret) {
|
if (!decryptedSecret) {
|
||||||
// no backup key available
|
// no backup key available
|
||||||
|
nhlog::crypto()->debug(
|
||||||
|
"Not downloading full online key backup, because we don't have a key for it.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto sessionDecryptionKey = to_binary_buf(base642bin(*decryptedSecret));
|
auto sessionDecryptionKey = to_binary_buf(base642bin(*decryptedSecret));
|
||||||
|
|
||||||
|
nhlog::crypto()->debug("Downloading full online key backup.");
|
||||||
|
|
||||||
http::client()->room_keys(
|
http::client()->room_keys(
|
||||||
backupVersion->version,
|
backupVersion->version,
|
||||||
[sessionDecryptionKey](const mtx::responses::backup::KeysBackup &bk,
|
[sessionDecryptionKey](const mtx::responses::backup::KeysBackup &bk,
|
||||||
|
@ -925,11 +932,12 @@ download_full_keybackup()
|
||||||
err->matrix_error.error);
|
err->matrix_error.error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
nhlog::crypto()->debug("Storing full online key backup.");
|
||||||
|
|
||||||
mtx::crypto::ExportedSessionKeys allKeys;
|
mtx::crypto::ExportedSessionKeys allKeys;
|
||||||
try {
|
for (const auto &[room, roomKey] : bk.rooms) {
|
||||||
for (const auto &[room, roomKey] : bk.rooms) {
|
for (const auto &[session_id, encSession] : roomKey.sessions) {
|
||||||
for (const auto &[session_id, encSession] : roomKey.sessions) {
|
try {
|
||||||
auto session = decrypt_session(encSession.session_data, sessionDecryptionKey);
|
auto session = decrypt_session(encSession.session_data, sessionDecryptionKey);
|
||||||
|
|
||||||
if (session.algorithm != mtx::crypto::MEGOLM_ALGO)
|
if (session.algorithm != mtx::crypto::MEGOLM_ALGO)
|
||||||
|
@ -946,16 +954,22 @@ download_full_keybackup()
|
||||||
sess.sender_key = std::move(session.sender_key);
|
sess.sender_key = std::move(session.sender_key);
|
||||||
sess.session_key = std::move(session.session_key);
|
sess.session_key = std::move(session.session_key);
|
||||||
allKeys.sessions.push_back(std::move(sess));
|
allKeys.sessions.push_back(std::move(sess));
|
||||||
|
} catch (const olm_exception &e) {
|
||||||
|
nhlog::crypto()->critical("failed to decrypt inbound megolm session: {}",
|
||||||
|
e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// call on UI thread
|
|
||||||
QTimer::singleShot(0, ChatPage::instance(), [keys = std::move(allKeys)] {
|
|
||||||
cache::importSessionKeys(keys);
|
|
||||||
});
|
|
||||||
} catch (const lmdb::error &e) {
|
|
||||||
nhlog::crypto()->critical("failed to save inbound megolm session: {}", e.what());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// call on UI thread
|
||||||
|
QTimer::singleShot(0, ChatPage::instance(), [keys = std::move(allKeys)] {
|
||||||
|
try {
|
||||||
|
cache::importSessionKeys(keys);
|
||||||
|
nhlog::crypto()->debug("Storing full online key backup completed.");
|
||||||
|
} catch (const lmdb::error &e) {
|
||||||
|
nhlog::crypto()->critical("failed to save inbound megolm session: {}", e.what());
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
|
@ -1083,8 +1097,6 @@ send_key_request_for(mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> e,
|
||||||
nhlog::net()->info(
|
nhlog::net()->info(
|
||||||
"m.room_key_request sent to {}:{} and your own devices", e.sender, e.content.device_id);
|
"m.room_key_request sent to {}:{} and your own devices", e.sender, e.content.device_id);
|
||||||
});
|
});
|
||||||
|
|
||||||
// http::client()->room_keys
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "EventAccessors.h"
|
#include "EventAccessors.h"
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
#include "MatrixClient.h"
|
#include "MatrixClient.h"
|
||||||
|
#include "TimelineModel.h"
|
||||||
#include "UserSettingsPage.h"
|
#include "UserSettingsPage.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
|
@ -353,6 +354,16 @@ EventStore::receivedSessionKey(const std::string &session_id)
|
||||||
events_.remove({room_id_, toInternalIdx(*idx)});
|
events_.remove({room_id_, toInternalIdx(*idx)});
|
||||||
emit dataChanged(*idx, *idx);
|
emit dataChanged(*idx, *idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auto edit = e.content.relations.replaces()) {
|
||||||
|
auto edit_idx = idToIndex(edit.value());
|
||||||
|
if (edit_idx) {
|
||||||
|
decryptedEvents_.remove({room_id_, e.event_id});
|
||||||
|
events_by_id_.remove({room_id_, e.event_id});
|
||||||
|
events_.remove({room_id_, toInternalIdx(*edit_idx)});
|
||||||
|
emit dataChanged(*edit_idx, *edit_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -538,7 +549,7 @@ EventStore::edits(const std::string &event_id)
|
||||||
// spec does not allow changing relatings in an edit. So if we are not using the multi
|
// spec does not allow changing relatings in an edit. So if we are not using the multi
|
||||||
// relation format specific to Nheko, just use the original relations + the edit...
|
// relation format specific to Nheko, just use the original relations + the edit...
|
||||||
if (edit_rel.synthesized) {
|
if (edit_rel.synthesized) {
|
||||||
auto merged_relations = original_relations;
|
auto merged_relations = original_relations;
|
||||||
merged_relations.synthesized = true;
|
merged_relations.synthesized = true;
|
||||||
merged_relations.relations.push_back(
|
merged_relations.relations.push_back(
|
||||||
{mtx::common::RelationType::Replace, event_id});
|
{mtx::common::RelationType::Replace, event_id});
|
||||||
|
@ -753,6 +764,15 @@ EventStore::decryptEvent(const IdIndex &idx,
|
||||||
return asCacheEntry(std::move(decryptionResult));
|
return asCacheEntry(std::move(decryptionResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EventStore::refetchOnlineKeyBackupKeys(TimelineModel *room)
|
||||||
|
{
|
||||||
|
for (const auto &[session_id, request] : room->events.pending_key_requests) {
|
||||||
|
(void)request;
|
||||||
|
olm::lookup_keybackup(room->events.room_id_, session_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
EventStore::requestSession(const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &ev,
|
EventStore::requestSession(const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &ev,
|
||||||
bool manual)
|
bool manual)
|
||||||
|
@ -767,8 +787,8 @@ EventStore::requestSession(const mtx::events::EncryptedEvent<mtx::events::msg::E
|
||||||
auto &r = pending_key_requests.at(ev.content.session_id);
|
auto &r = pending_key_requests.at(ev.content.session_id);
|
||||||
r.events.push_back(copy);
|
r.events.push_back(copy);
|
||||||
|
|
||||||
// automatically request once every 10 min, manually every 1 min
|
// automatically request once every 2 min, manually every 30 s
|
||||||
qint64 delay = manual ? 60 : (60 * 10);
|
qint64 delay = manual ? 30 : (60 * 2);
|
||||||
if (r.requested_at + delay < QDateTime::currentSecsSinceEpoch()) {
|
if (r.requested_at + delay < QDateTime::currentSecsSinceEpoch()) {
|
||||||
r.requested_at = QDateTime::currentSecsSinceEpoch();
|
r.requested_at = QDateTime::currentSecsSinceEpoch();
|
||||||
olm::lookup_keybackup(room_id_, ev.content.session_id);
|
olm::lookup_keybackup(room_id_, ev.content.session_id);
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
#include "Reaction.h"
|
#include "Reaction.h"
|
||||||
#include "encryption/Olm.h"
|
#include "encryption/Olm.h"
|
||||||
|
|
||||||
|
class TimelineModel;
|
||||||
|
|
||||||
class EventStore final : public QObject
|
class EventStore final : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -27,6 +29,8 @@ class EventStore final : public QObject
|
||||||
public:
|
public:
|
||||||
EventStore(std::string room_id, QObject *parent);
|
EventStore(std::string room_id, QObject *parent);
|
||||||
|
|
||||||
|
static void refetchOnlineKeyBackupKeys(TimelineModel *room);
|
||||||
|
|
||||||
// taken from QtPrivate::QHashCombine
|
// taken from QtPrivate::QHashCombine
|
||||||
static uint hashCombine(uint hash, uint seed)
|
static uint hashCombine(uint hash, uint seed)
|
||||||
{
|
{
|
||||||
|
|
|
@ -812,6 +812,18 @@ RoomlistModel::setCurrentRoom(const QString &roomid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
RoomlistModel::refetchOnlineKeyBackupKeys()
|
||||||
|
{
|
||||||
|
for (auto i = models.begin(); i != models.end(); ++i) {
|
||||||
|
auto ptr = i.value();
|
||||||
|
|
||||||
|
if (!ptr.isNull()) {
|
||||||
|
EventStore::refetchOnlineKeyBackupKeys(ptr.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
enum NotificationImportance : short
|
enum NotificationImportance : short
|
||||||
{
|
{
|
||||||
|
|
|
@ -95,6 +95,8 @@ public:
|
||||||
}
|
}
|
||||||
RoomPreview getRoomPreviewById(QString roomid) const;
|
RoomPreview getRoomPreviewById(QString roomid) const;
|
||||||
|
|
||||||
|
void refetchOnlineKeyBackupKeys();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void initializeRooms();
|
void initializeRooms();
|
||||||
void sync(const mtx::responses::Sync &sync_);
|
void sync(const mtx::responses::Sync &sync_);
|
||||||
|
|
|
@ -528,6 +528,8 @@ private:
|
||||||
|
|
||||||
std::unique_ptr<RoomSummary, DeleteLaterDeleter> parentSummary = nullptr;
|
std::unique_ptr<RoomSummary, DeleteLaterDeleter> parentSummary = nullptr;
|
||||||
bool parentChecked = false;
|
bool parentChecked = false;
|
||||||
|
|
||||||
|
friend void EventStore::refetchOnlineKeyBackupKeys(TimelineModel *room);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
|
|
Loading…
Reference in a new issue