diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 2d2235844d387d719c7ead432b0a41b6a1740b9c..e3325c05c2eb406b4f1de53b61d94d1afaa78cec 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -17,6 +17,7 @@ #include <QApplication> #include <QImageReader> +#include <QInputDialog> #include <QMessageBox> #include <QSettings> #include <QShortcut> @@ -64,6 +65,8 @@ constexpr size_t MAX_ONETIME_KEYS = 50; Q_DECLARE_METATYPE(std::optional<mtx::crypto::EncryptedFile>) Q_DECLARE_METATYPE(std::optional<RelatedInfo>) Q_DECLARE_METATYPE(mtx::presence::PresenceState) +Q_DECLARE_METATYPE(mtx::secret_storage::AesHmacSha2KeyDescription) +Q_DECLARE_METATYPE(SecretsToDecrypt) ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent) : QWidget(parent) @@ -79,6 +82,8 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent) qRegisterMetaType<std::optional<mtx::crypto::EncryptedFile>>(); qRegisterMetaType<std::optional<RelatedInfo>>(); qRegisterMetaType<mtx::presence::PresenceState>(); + qRegisterMetaType<mtx::secret_storage::AesHmacSha2KeyDescription>(); + qRegisterMetaType<SecretsToDecrypt>(); topLayout_ = new QHBoxLayout(this); topLayout_->setSpacing(0); @@ -136,6 +141,12 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent) splitter->addWidget(content_); splitter->restoreSizes(parent->width()); + connect(this, + &ChatPage::downloadedSecrets, + this, + &ChatPage::decryptDownloadedSecrets, + Qt::QueuedConnection); + connect(this, &ChatPage::connectionLost, this, [this]() { nhlog::net()->info("connectivity lost"); isConnected_ = false; @@ -1208,3 +1219,45 @@ ChatPage::connectCallMessage() view_manager_, qOverload<const QString &, const T &>(&TimelineViewManager::queueCallMessage)); } + +void +ChatPage::decryptDownloadedSecrets(mtx::secret_storage::AesHmacSha2KeyDescription keyDesc, + const SecretsToDecrypt &secrets) +{ + QString text = QInputDialog::getText( + ChatPage::instance(), + QCoreApplication::translate("CrossSigningSecrets", "Decrypt secrets"), + keyDesc.name.empty() + ? QCoreApplication::translate( + "CrossSigningSecrets", + "Enter your recovery key or passphrase to decrypt your secrets:") + : QCoreApplication::translate( + "CrossSigningSecrets", + "Enter your recovery key or passphrase called %1 to decrypt your secrets:") + .arg(QString::fromStdString(keyDesc.name)), + QLineEdit::Password); + + if (text.isEmpty()) + return; + + auto decryptionKey = mtx::crypto::key_from_recoverykey(text.toStdString(), keyDesc); + + if (!decryptionKey) + decryptionKey = mtx::crypto::key_from_passphrase(text.toStdString(), keyDesc); + + if (!decryptionKey) { + QMessageBox::information( + ChatPage::instance(), + QCoreApplication::translate("CrossSigningSecrets", "Decrytion failed"), + QCoreApplication::translate("CrossSigningSecrets", + "Failed to decrypt secrets with the " + "provided recovery key or passphrase")); + return; + } + + for (const auto &[secretName, encryptedSecret] : secrets) { + auto decrypted = mtx::crypto::decrypt(encryptedSecret, *decryptionKey, secretName); + if (!decrypted.empty()) + cache::storeSecret(secretName, decrypted); + } +} diff --git a/src/ChatPage.h b/src/ChatPage.h index 5b336cbb28fdfd9e9a08be9b11fc6c14ce31f5f5..45a4ff63105287133e76c2386c45fe445d1a8a64 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -27,6 +27,7 @@ #include <mtx/events/encrypted.hpp> #include <mtx/events/member.hpp> #include <mtx/events/presence.hpp> +#include <mtx/secret_storage.hpp> #include <QFrame> #include <QHBoxLayout> @@ -72,6 +73,8 @@ namespace popups { class UserMentions; } +using SecretsToDecrypt = std::map<std::string, mtx::secret_storage::AesHmacSha2EncryptedData>; + class ChatPage : public QWidget { Q_OBJECT @@ -117,6 +120,8 @@ public slots: void unbanUser(QString userid, QString reason); void receivedSessionKey(const std::string &room_id, const std::string &session_id); + void decryptDownloadedSecrets(mtx::secret_storage::AesHmacSha2KeyDescription keyDesc, + const SecretsToDecrypt &secrets); signals: void connectionLost(); @@ -185,6 +190,9 @@ signals: void receivedDeviceVerificationReady(const mtx::events::msg::KeyVerificationReady &message); void receivedDeviceVerificationDone(const mtx::events::msg::KeyVerificationDone &message); + void downloadedSecrets(mtx::secret_storage::AesHmacSha2KeyDescription keyDesc, + const SecretsToDecrypt &secrets); + private slots: void logout(); void removeRoom(const QString &room_id); diff --git a/src/Olm.cpp b/src/Olm.cpp index 82a61fba43abd3014e28f61801f1d550e7f54db3..0dbd51249ca6d2a2171217e231902c813dfd28dc 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -1243,8 +1243,73 @@ request_cross_signing_keys() request(mtx::secret_storage::secrets::cross_signing_user_signing); request(mtx::secret_storage::secrets::megolm_backup_v1); } + +namespace { +void +unlock_secrets(const std::string &key, + const std::map<std::string, mtx::secret_storage::AesHmacSha2EncryptedData> &secrets) +{ + http::client()->secret_storage_key( + key, + [secrets](mtx::secret_storage::AesHmacSha2KeyDescription keyDesc, + mtx::http::RequestErr err) { + if (err) { + nhlog::net()->error("Failed to download secret storage key"); + return; + } + + emit ChatPage::instance()->downloadedSecrets(keyDesc, secrets); + }); +} +} + void download_cross_signing_keys() -{} +{ + using namespace mtx::secret_storage; + http::client()->secret_storage_secret( + secrets::megolm_backup_v1, [](Secret secret, mtx::http::RequestErr err) { + std::optional<Secret> backup_key; + if (!err) + backup_key = secret; + + http::client()->secret_storage_secret( + secrets::cross_signing_self_signing, + [backup_key](Secret secret, mtx::http::RequestErr err) { + std::optional<Secret> self_signing_key; + if (!err) + self_signing_key = secret; + + http::client()->secret_storage_secret( + secrets::cross_signing_user_signing, + [backup_key, self_signing_key](Secret secret, + mtx::http::RequestErr err) { + std::optional<Secret> user_signing_key; + if (!err) + user_signing_key = secret; + + std::map<std::string, + std::map<std::string, AesHmacSha2EncryptedData>> + secrets; + + if (backup_key && !backup_key->encrypted.empty()) + secrets[backup_key->encrypted.begin()->first] + [secrets::megolm_backup_v1] = + backup_key->encrypted.begin()->second; + if (self_signing_key && !self_signing_key->encrypted.empty()) + secrets[self_signing_key->encrypted.begin()->first] + [secrets::cross_signing_self_signing] = + self_signing_key->encrypted.begin()->second; + if (user_signing_key && !user_signing_key->encrypted.empty()) + secrets[user_signing_key->encrypted.begin()->first] + [secrets::cross_signing_user_signing] = + user_signing_key->encrypted.begin()->second; + + for (const auto &[key, secrets] : secrets) + unlock_secrets(key, secrets); + }); + }); + }); +} } // namespace olm diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp index fe0145fe852f05b14e8844df99b8b16716d403bb..9fd76bdada303b6a33e29dc68d4f8141739a6979 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp @@ -1029,6 +1029,10 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge olm::request_cross_signing_keys(); }); + connect(crossSigningDownloadBtn, &QPushButton::clicked, this, []() { + olm::download_cross_signing_keys(); + }); + connect(backBtn_, &QPushButton::clicked, this, [this]() { settings_->save(); emit moveBack();