diff --git a/src/Cache.cpp b/src/Cache.cpp index 7284ffaa..cc8516ad 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -635,6 +635,9 @@ Cache::exportSessionKeys() void Cache::importSessionKeys(const mtx::crypto::ExportedSessionKeys &keys) { + std::size_t importCount = 0; + + auto txn = lmdb::txn::begin(env_); for (const auto &s : keys.sessions) { MegolmSessionIndex index; index.room_id = s.room_id; @@ -643,14 +646,49 @@ Cache::importSessionKeys(const mtx::crypto::ExportedSessionKeys &keys) GroupSessionData data{}; data.sender_key = s.sender_key; data.forwarding_curve25519_key_chain = s.forwarding_curve25519_key_chain; + data.trusted = false; + if (s.sender_claimed_keys.count("ed25519")) data.sender_claimed_ed25519_key = s.sender_claimed_keys.at("ed25519"); - auto exported_session = mtx::crypto::import_session(s.session_key); + try { + auto exported_session = mtx::crypto::import_session(s.session_key); - saveInboundMegolmSession(index, std::move(exported_session), data); - ChatPage::instance()->receivedSessionKey(index.room_id, index.session_id); + using namespace mtx::crypto; + const auto key = nlohmann::json(index).dump(); + const auto pickled = + pickle(exported_session.get(), pickle_secret_); + + std::string_view value; + if (inboundMegolmSessionDb_.get(txn, key, value)) { + auto oldSession = + unpickle(std::string(value), pickle_secret_); + if (olm_inbound_group_session_first_known_index(exported_session.get()) >= + olm_inbound_group_session_first_known_index(oldSession.get())) { + nhlog::crypto()->warn( + "Not storing inbound session with newer or equal first known index"); + continue; + } + } + + inboundMegolmSessionDb_.put(txn, key, pickled); + megolmSessionDataDb_.put(txn, key, nlohmann::json(data).dump()); + + ChatPage::instance()->receivedSessionKey(index.room_id, index.session_id); + importCount++; + } catch (const mtx::crypto::olm_exception &e) { + nhlog::crypto()->critical( + "failed to import inbound megolm session {}: {}", index.session_id, e.what()); + continue; + } catch (const lmdb::error &e) { + nhlog::crypto()->critical( + "failed to save inbound megolm session {}: {}", index.session_id, e.what()); + continue; + } } + txn.commit(); + + nhlog::crypto()->info("Imported {} out of {} keys", importCount, keys.sessions.size()); } // diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp index dc8e87fc..666a03b4 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp @@ -548,6 +548,9 @@ UserSettings::setUseOnlineKeyBackup(bool useBackup) useOnlineKeyBackup_ = useBackup; emit useOnlineKeyBackupChanged(useBackup); save(); + + if (useBackup) + olm::download_full_keybackup(); } void diff --git a/src/encryption/Olm.cpp b/src/encryption/Olm.cpp index 8bf2222f..ed84a275 100644 --- a/src/encryption/Olm.cpp +++ b/src/encryption/Olm.cpp @@ -873,6 +873,73 @@ mark_keys_as_published() cache::saveOlmAccount(olm::client()->save(cache::client()->pickleSecret())); } +void +download_full_keybackup() +{ + if (!UserSettings::instance()->useOnlineKeyBackup()) { + // Online key backup disabled + return; + } + + auto backupVersion = cache::client()->backupVersion(); + if (!backupVersion) { + // no trusted OKB + return; + } + + using namespace mtx::crypto; + + auto decryptedSecret = cache::secret(mtx::secret_storage::secrets::megolm_backup_v1); + if (!decryptedSecret) { + // no backup key available + return; + } + auto sessionDecryptionKey = to_binary_buf(base642bin(*decryptedSecret)); + + http::client()->room_keys( + backupVersion->version, + [sessionDecryptionKey](const mtx::responses::backup::KeysBackup &bk, + mtx::http::RequestErr err) { + if (err) { + if (err->status_code != 404) + nhlog::crypto()->error("Failed to dowload backup {}:{}: {} - {}", + mtx::errors::to_string(err->matrix_error.errcode), + err->matrix_error.error); + return; + } + + mtx::crypto::ExportedSessionKeys allKeys; + try { + for (const auto &[room, roomKey] : bk.rooms) { + for (const auto &[session_id, encSession] : roomKey.sessions) { + auto session = decrypt_session(encSession.session_data, sessionDecryptionKey); + + if (session.algorithm != mtx::crypto::MEGOLM_ALGO) + // don't know this algorithm + return; + + ExportedSession sess{}; + sess.session_id = session_id; + sess.room_id = room; + sess.algorithm = mtx::crypto::MEGOLM_ALGO; + sess.forwarding_curve25519_key_chain = + std::move(session.forwarding_curve25519_key_chain); + sess.sender_claimed_keys = std::move(session.sender_claimed_keys); + sess.sender_key = std::move(session.sender_key); + sess.session_key = std::move(session.session_key); + allKeys.sessions.push_back(std::move(sess)); + } + } + + // 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()); + } + }); +} void lookup_keybackup(const std::string room, const std::string session_id) { diff --git a/src/encryption/Olm.h b/src/encryption/Olm.h index 9d99bcf4..1189fffa 100644 --- a/src/encryption/Olm.h +++ b/src/encryption/Olm.h @@ -73,6 +73,8 @@ import_inbound_megolm_session( const mtx::events::DeviceEvent &roomKey); void lookup_keybackup(const std::string room, const std::string session_id); +void +download_full_keybackup(); nlohmann::json handle_pre_key_olm_message(const std::string &sender, diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp index 122a6f8e..019cf78b 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp @@ -754,7 +754,6 @@ EventStore::requestSession(const mtx::events::EncryptedEvent