Stop encrypting all sessions with secret

This commit is contained in:
Nicolas Werner 2021-08-31 04:06:51 +02:00
parent 0f361151d7
commit c80e253a24
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
4 changed files with 61 additions and 31 deletions

View file

@ -36,8 +36,7 @@
//! Should be changed when a breaking change occurs in the cache format. //! Should be changed when a breaking change occurs in the cache format.
//! This will reset client's data. //! This will reset client's data.
static const std::string CURRENT_CACHE_FORMAT_VERSION("2021.08.22"); static const std::string CURRENT_CACHE_FORMAT_VERSION("2021.08.31");
static const std::string SECRET("secret");
//! Keys used for the DB //! Keys used for the DB
static const std::string_view NEXT_BATCH_KEY("next_batch"); static const std::string_view NEXT_BATCH_KEY("next_batch");
@ -370,7 +369,8 @@ Cache::exportSessionKeys()
ExportedSession exported; ExportedSession exported;
MegolmSessionIndex index; MegolmSessionIndex index;
auto saved_session = unpickle<InboundSessionObject>(std::string(value), SECRET); auto saved_session =
unpickle<InboundSessionObject>(std::string(value), pickle_secret_);
try { try {
index = nlohmann::json::parse(key).get<MegolmSessionIndex>(); index = nlohmann::json::parse(key).get<MegolmSessionIndex>();
@ -424,13 +424,14 @@ Cache::saveInboundMegolmSession(const MegolmSessionIndex &index,
{ {
using namespace mtx::crypto; using namespace mtx::crypto;
const auto key = json(index).dump(); const auto key = json(index).dump();
const auto pickled = pickle<InboundSessionObject>(session.get(), SECRET); const auto pickled = pickle<InboundSessionObject>(session.get(), pickle_secret_);
auto txn = lmdb::txn::begin(env_); auto txn = lmdb::txn::begin(env_);
std::string_view value; std::string_view value;
if (inboundMegolmSessionDb_.get(txn, key, value)) { if (inboundMegolmSessionDb_.get(txn, key, value)) {
auto oldSession = unpickle<InboundSessionObject>(std::string(value), SECRET); auto oldSession =
unpickle<InboundSessionObject>(std::string(value), pickle_secret_);
if (olm_inbound_group_session_first_known_index(session.get()) > if (olm_inbound_group_session_first_known_index(session.get()) >
olm_inbound_group_session_first_known_index(oldSession.get())) { olm_inbound_group_session_first_known_index(oldSession.get())) {
nhlog::crypto()->warn( nhlog::crypto()->warn(
@ -455,7 +456,8 @@ Cache::getInboundMegolmSession(const MegolmSessionIndex &index)
std::string_view value; std::string_view value;
if (inboundMegolmSessionDb_.get(txn, key, value)) { if (inboundMegolmSessionDb_.get(txn, key, value)) {
auto session = unpickle<InboundSessionObject>(std::string(value), SECRET); auto session =
unpickle<InboundSessionObject>(std::string(value), pickle_secret_);
return session; return session;
} }
} catch (std::exception &e) { } catch (std::exception &e) {
@ -502,7 +504,7 @@ Cache::updateOutboundMegolmSession(const std::string &room_id,
// Save the updated pickled data for the session. // Save the updated pickled data for the session.
json j; json j;
j["session"] = pickle<OutboundSessionObject>(ptr.get(), SECRET); j["session"] = pickle<OutboundSessionObject>(ptr.get(), pickle_secret_);
auto txn = lmdb::txn::begin(env_); auto txn = lmdb::txn::begin(env_);
outboundMegolmSessionDb_.put(txn, room_id, j.dump()); outboundMegolmSessionDb_.put(txn, room_id, j.dump());
@ -532,7 +534,7 @@ Cache::saveOutboundMegolmSession(const std::string &room_id,
mtx::crypto::OutboundGroupSessionPtr &session) mtx::crypto::OutboundGroupSessionPtr &session)
{ {
using namespace mtx::crypto; using namespace mtx::crypto;
const auto pickled = pickle<OutboundSessionObject>(session.get(), SECRET); const auto pickled = pickle<OutboundSessionObject>(session.get(), pickle_secret_);
GroupSessionData data = data_; GroupSessionData data = data_;
data.message_index = olm_outbound_group_session_message_index(session.get()); data.message_index = olm_outbound_group_session_message_index(session.get());
@ -575,7 +577,7 @@ Cache::getOutboundMegolmSession(const std::string &room_id)
auto obj = json::parse(value); auto obj = json::parse(value);
OutboundGroupSessionDataRef ref{}; OutboundGroupSessionDataRef ref{};
ref.session = unpickle<OutboundSessionObject>(obj.at("session"), SECRET); ref.session = unpickle<OutboundSessionObject>(obj.at("session"), pickle_secret_);
MegolmSessionIndex index; MegolmSessionIndex index;
index.room_id = room_id; index.room_id = room_id;
@ -626,7 +628,7 @@ Cache::saveOlmSession(const std::string &curve25519,
auto txn = lmdb::txn::begin(env_); auto txn = lmdb::txn::begin(env_);
auto db = getOlmSessionsDb(txn, curve25519); auto db = getOlmSessionsDb(txn, curve25519);
const auto pickled = pickle<SessionObject>(session.get(), SECRET); const auto pickled = pickle<SessionObject>(session.get(), pickle_secret_);
const auto session_id = mtx::crypto::session_id(session.get()); const auto session_id = mtx::crypto::session_id(session.get());
StoredOlmSession stored_session; StoredOlmSession stored_session;
@ -653,7 +655,7 @@ Cache::getOlmSession(const std::string &curve25519, const std::string &session_i
if (found) { if (found) {
auto data = json::parse(pickled).get<StoredOlmSession>(); auto data = json::parse(pickled).get<StoredOlmSession>();
return unpickle<SessionObject>(data.pickled_session, SECRET); return unpickle<SessionObject>(data.pickled_session, pickle_secret_);
} }
return std::nullopt; return std::nullopt;
@ -681,8 +683,8 @@ Cache::getLatestOlmSession(const std::string &curve25519)
txn.commit(); txn.commit();
return currentNewest return currentNewest ? std::optional(unpickle<SessionObject>(currentNewest->pickled_session,
? std::optional(unpickle<SessionObject>(currentNewest->pickled_session, SECRET)) pickle_secret_))
: std::nullopt; : std::nullopt;
} }
@ -719,6 +721,7 @@ std::string
Cache::restoreOlmAccount() Cache::restoreOlmAccount()
{ {
auto txn = ro_txn(env_); auto txn = ro_txn(env_);
std::string_view pickled; std::string_view pickled;
syncStateDb_.get(txn, OLM_ACCOUNT_KEY, pickled); syncStateDb_.get(txn, OLM_ACCOUNT_KEY, pickled);
@ -774,7 +777,7 @@ fatalSecretError()
} }
void void
Cache::storeSecret(const std::string name, const std::string secret) Cache::storeSecret(const std::string name, const std::string secret, bool internal)
{ {
auto settings = UserSettings::instance(); auto settings = UserSettings::instance();
auto job = new QKeychain::WritePasswordJob(QCoreApplication::applicationName()); auto job = new QKeychain::WritePasswordJob(QCoreApplication::applicationName());
@ -783,7 +786,7 @@ Cache::storeSecret(const std::string name, const std::string secret)
job->setSettings(UserSettings::instance()->qsettings()); job->setSettings(UserSettings::instance()->qsettings());
job->setKey( job->setKey(
"matrix." + (internal ? "nheko." : "matrix.") +
QString(QCryptographicHash::hash(settings->profile().toUtf8(), QCryptographicHash::Sha256) QString(QCryptographicHash::hash(settings->profile().toUtf8(), QCryptographicHash::Sha256)
.toBase64()) + .toBase64()) +
"." + QString::fromStdString(name)); "." + QString::fromStdString(name));
@ -812,7 +815,7 @@ Cache::storeSecret(const std::string name, const std::string secret)
} }
void void
Cache::deleteSecret(const std::string name) Cache::deleteSecret(const std::string name, bool internal)
{ {
auto settings = UserSettings::instance(); auto settings = UserSettings::instance();
QKeychain::DeletePasswordJob job(QCoreApplication::applicationName()); QKeychain::DeletePasswordJob job(QCoreApplication::applicationName());
@ -821,7 +824,7 @@ Cache::deleteSecret(const std::string name)
job.setSettings(UserSettings::instance()->qsettings()); job.setSettings(UserSettings::instance()->qsettings());
job.setKey( job.setKey(
"matrix." + (internal ? "nheko." : "matrix.") +
QString(QCryptographicHash::hash(settings->profile().toUtf8(), QCryptographicHash::Sha256) QString(QCryptographicHash::hash(settings->profile().toUtf8(), QCryptographicHash::Sha256)
.toBase64()) + .toBase64()) +
"." + QString::fromStdString(name)); "." + QString::fromStdString(name));
@ -837,7 +840,7 @@ Cache::deleteSecret(const std::string name)
} }
std::optional<std::string> std::optional<std::string>
Cache::secret(const std::string name) Cache::secret(const std::string name, bool internal)
{ {
auto settings = UserSettings::instance(); auto settings = UserSettings::instance();
QKeychain::ReadPasswordJob job(QCoreApplication::applicationName()); QKeychain::ReadPasswordJob job(QCoreApplication::applicationName());
@ -846,7 +849,7 @@ Cache::secret(const std::string name)
job.setSettings(UserSettings::instance()->qsettings()); job.setSettings(UserSettings::instance()->qsettings());
job.setKey( job.setKey(
"matrix." + (internal ? "nheko." : "matrix.") +
QString(QCryptographicHash::hash(settings->profile().toUtf8(), QCryptographicHash::Sha256) QString(QCryptographicHash::hash(settings->profile().toUtf8(), QCryptographicHash::Sha256)
.toBase64()) + .toBase64()) +
"." + QString::fromStdString(name)); "." + QString::fromStdString(name));
@ -878,6 +881,22 @@ Cache::secret(const std::string name)
return secret.toStdString(); return secret.toStdString();
} }
std::string
Cache::pickleSecret()
{
if (pickle_secret_.empty()) {
auto s = secret("pickle_secret", true);
if (!s) {
this->pickle_secret_ = mtx::client::utils::random_token(64, true);
storeSecret("pickle_secret", pickle_secret_, true);
} else {
this->pickle_secret_ = *s;
}
}
return pickle_secret_;
};
void void
Cache::removeInvite(lmdb::txn &txn, const std::string &room_id) Cache::removeInvite(lmdb::txn &txn, const std::string &room_id)
{ {
@ -979,6 +998,7 @@ Cache::deleteData()
deleteSecret(mtx::secret_storage::secrets::cross_signing_master); deleteSecret(mtx::secret_storage::secrets::cross_signing_master);
deleteSecret(mtx::secret_storage::secrets::cross_signing_user_signing); deleteSecret(mtx::secret_storage::secrets::cross_signing_user_signing);
deleteSecret(mtx::secret_storage::secrets::cross_signing_self_signing); deleteSecret(mtx::secret_storage::secrets::cross_signing_self_signing);
deleteSecret("pickle_secret", true);
} }
//! migrates db to the current format //! migrates db to the current format
@ -1202,6 +1222,11 @@ Cache::runMigrations()
"Successfully cleared the cache. Will do a clean sync after startup."); "Successfully cleared the cache. Will do a clean sync after startup.");
return true; return true;
}}, }},
{"2021.08.31",
[this]() {
storeSecret("pickle_secret", "secret", true);
return true;
}},
}; };
nhlog::db()->info("Running migrations, this may take a while!"); nhlog::db()->info("Running migrations, this may take a while!");

View file

@ -291,9 +291,11 @@ public:
void deleteBackupVersion(); void deleteBackupVersion();
std::optional<OnlineBackupVersion> backupVersion(); std::optional<OnlineBackupVersion> backupVersion();
void storeSecret(const std::string name, const std::string secret); void storeSecret(const std::string name, const std::string secret, bool internal = false);
void deleteSecret(const std::string name); void deleteSecret(const std::string name, bool internal = false);
std::optional<std::string> secret(const std::string name); std::optional<std::string> secret(const std::string name, bool internal = false);
std::string pickleSecret();
template<class T> template<class T>
constexpr static bool isStateEvent_ = constexpr static bool isStateEvent_ =
@ -713,6 +715,8 @@ private:
QString localUserId_; QString localUserId_;
QString cacheDirectory_; QString cacheDirectory_;
std::string pickle_secret_;
VerificationStorage verification_storage; VerificationStorage verification_storage;
bool databaseReady_ = false; bool databaseReady_ = false;

View file

@ -32,9 +32,6 @@
#include "blurhash.hpp" #include "blurhash.hpp"
// TODO: Needs to be updated with an actual secret.
static const std::string STORAGE_SECRET_KEY("secret");
ChatPage *ChatPage::instance_ = nullptr; ChatPage *ChatPage::instance_ = nullptr;
constexpr int CHECK_CONNECTIVITY_INTERVAL = 15'000; constexpr int CHECK_CONNECTIVITY_INTERVAL = 15'000;
constexpr int RETRY_TIMEOUT = 5'000; constexpr int RETRY_TIMEOUT = 5'000;
@ -372,7 +369,7 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token)
// There isn't a saved olm account to restore. // There isn't a saved olm account to restore.
nhlog::crypto()->info("creating new olm account"); nhlog::crypto()->info("creating new olm account");
olm::client()->create_new_account(); olm::client()->create_new_account();
cache::saveOlmAccount(olm::client()->save(STORAGE_SECRET_KEY)); cache::saveOlmAccount(olm::client()->save(cache::client()->pickleSecret()));
} catch (const lmdb::error &e) { } catch (const lmdb::error &e) {
nhlog::crypto()->critical("failed to save olm account {}", e.what()); nhlog::crypto()->critical("failed to save olm account {}", e.what());
emit dropToLoginPageCb(QString::fromStdString(e.what())); emit dropToLoginPageCb(QString::fromStdString(e.what()));
@ -394,7 +391,7 @@ ChatPage::loadStateFromCache()
nhlog::db()->info("restoring state from cache"); nhlog::db()->info("restoring state from cache");
try { try {
olm::client()->load(cache::restoreOlmAccount(), STORAGE_SECRET_KEY); olm::client()->load(cache::restoreOlmAccount(), cache::client()->pickleSecret());
emit initializeEmptyViews(); emit initializeEmptyViews();
emit initializeMentions(cache::getTimelineMentions()); emit initializeMentions(cache::getTimelineMentions());
@ -411,6 +408,11 @@ ChatPage::loadStateFromCache()
return; return;
} catch (const json::exception &e) { } catch (const json::exception &e) {
nhlog::db()->critical("failed to parse cache data: {}", e.what()); nhlog::db()->critical("failed to parse cache data: {}", e.what());
emit dropToLoginPageCb(tr("Failed to restore save data. Please login again."));
return;
} catch (const std::exception &e) {
nhlog::db()->critical("failed to load cache data: {}", e.what());
emit dropToLoginPageCb(tr("Failed to restore save data. Please login again."));
return; return;
} }

View file

@ -27,7 +27,6 @@ auto client_ = std::make_unique<mtx::crypto::OlmClient>();
std::map<std::string, std::string> request_id_to_secret_name; std::map<std::string, std::string> request_id_to_secret_name;
const std::string STORAGE_SECRET_KEY("secret");
constexpr auto MEGOLM_ALGO = "m.megolm.v1.aes-sha2"; constexpr auto MEGOLM_ALGO = "m.megolm.v1.aes-sha2";
} }
@ -483,7 +482,7 @@ handle_pre_key_olm_message(const std::string &sender,
// We also remove the one time key used to establish that // We also remove the one time key used to establish that
// session so we'll have to update our copy of the account object. // session so we'll have to update our copy of the account object.
cache::saveOlmAccount(olm::client()->save("secret")); cache::saveOlmAccount(olm::client()->save(cache::client()->pickleSecret()));
} catch (const mtx::crypto::olm_exception &e) { } catch (const mtx::crypto::olm_exception &e) {
nhlog::crypto()->critical( nhlog::crypto()->critical(
"failed to create inbound session with {}: {}", sender, e.what()); "failed to create inbound session with {}: {}", sender, e.what());
@ -938,7 +937,7 @@ void
mark_keys_as_published() mark_keys_as_published()
{ {
olm::client()->mark_keys_as_published(); olm::client()->mark_keys_as_published();
cache::saveOlmAccount(olm::client()->save(STORAGE_SECRET_KEY)); cache::saveOlmAccount(olm::client()->save(cache::client()->pickleSecret()));
} }
void void