Store megolm session data in separate database

This commit is contained in:
Nicolas Werner 2021-07-17 01:27:37 +02:00
parent 0b864d9485
commit 9fadd14871
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
7 changed files with 210 additions and 86 deletions

View file

@ -78,6 +78,8 @@ constexpr auto ENCRYPTED_ROOMS_DB("encrypted_rooms");
constexpr auto INBOUND_MEGOLM_SESSIONS_DB("inbound_megolm_sessions");
//! MegolmSessionIndex -> pickled OlmOutboundGroupSession
constexpr auto OUTBOUND_MEGOLM_SESSIONS_DB("outbound_megolm_sessions");
//! MegolmSessionIndex -> session data about which devices have access to this
constexpr auto MEGOLM_SESSIONS_DATA_DB("megolm_sessions_data_db");
using CachedReceipts = std::multimap<uint64_t, std::string, std::greater<uint64_t>>;
using Receipts = std::map<std::string, std::map<std::string, uint64_t>>;
@ -284,6 +286,7 @@ Cache::setup()
// Session management
inboundMegolmSessionDb_ = lmdb::dbi::open(txn, INBOUND_MEGOLM_SESSIONS_DB, MDB_CREATE);
outboundMegolmSessionDb_ = lmdb::dbi::open(txn, OUTBOUND_MEGOLM_SESSIONS_DB, MDB_CREATE);
megolmSessionDataDb_ = lmdb::dbi::open(txn, MEGOLM_SESSIONS_DATA_DB, MDB_CREATE);
txn.commit();
@ -387,9 +390,14 @@ Cache::importSessionKeys(const mtx::crypto::ExportedSessionKeys &keys)
index.session_id = s.session_id;
index.sender_key = s.sender_key;
GroupSessionData data{};
data.forwarding_curve25519_key_chain = s.forwarding_curve25519_key_chain;
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);
saveInboundMegolmSession(index, std::move(exported_session));
saveInboundMegolmSession(index, std::move(exported_session), data);
ChatPage::instance()->receivedSessionKey(index.room_id, index.session_id);
}
}
@ -400,7 +408,8 @@ Cache::importSessionKeys(const mtx::crypto::ExportedSessionKeys &keys)
void
Cache::saveInboundMegolmSession(const MegolmSessionIndex &index,
mtx::crypto::InboundGroupSessionPtr session)
mtx::crypto::InboundGroupSessionPtr session,
const GroupSessionData &data)
{
using namespace mtx::crypto;
const auto key = json(index).dump();
@ -420,6 +429,7 @@ Cache::saveInboundMegolmSession(const MegolmSessionIndex &index,
}
inboundMegolmSessionDb_.put(txn, key, pickled);
megolmSessionDataDb_.put(txn, key, json(data).dump());
txn.commit();
}
@ -464,7 +474,7 @@ Cache::inboundMegolmSessionExists(const MegolmSessionIndex &index)
void
Cache::updateOutboundMegolmSession(const std::string &room_id,
const OutboundGroupSessionData &data_,
const GroupSessionData &data_,
mtx::crypto::OutboundGroupSessionPtr &ptr)
{
using namespace mtx::crypto;
@ -472,18 +482,20 @@ Cache::updateOutboundMegolmSession(const std::string &room_id,
if (!outboundMegolmSessionExists(room_id))
return;
OutboundGroupSessionData data = data_;
data.message_index = olm_outbound_group_session_message_index(ptr.get());
data.session_id = mtx::crypto::session_id(ptr.get());
data.session_key = mtx::crypto::session_key(ptr.get());
GroupSessionData data = data_;
data.message_index = olm_outbound_group_session_message_index(ptr.get());
MegolmSessionIndex index;
index.room_id = room_id;
index.sender_key = olm::client()->identity_keys().ed25519;
index.session_id = mtx::crypto::session_id(ptr.get());
// Save the updated pickled data for the session.
json j;
j["data"] = data;
j["session"] = pickle<OutboundSessionObject>(ptr.get(), SECRET);
auto txn = lmdb::txn::begin(env_);
outboundMegolmSessionDb_.put(txn, room_id, j.dump());
megolmSessionDataDb_.put(txn, json(index).dump(), json(data).dump());
txn.commit();
}
@ -498,24 +510,32 @@ Cache::dropOutboundMegolmSession(const std::string &room_id)
{
auto txn = lmdb::txn::begin(env_);
outboundMegolmSessionDb_.del(txn, room_id);
// don't delete session data, so that we can still share the session.
txn.commit();
}
}
void
Cache::saveOutboundMegolmSession(const std::string &room_id,
const OutboundGroupSessionData &data,
const GroupSessionData &data_,
mtx::crypto::OutboundGroupSessionPtr &session)
{
using namespace mtx::crypto;
const auto pickled = pickle<OutboundSessionObject>(session.get(), SECRET);
GroupSessionData data = data_;
data.message_index = olm_outbound_group_session_message_index(session.get());
MegolmSessionIndex index;
index.room_id = room_id;
index.sender_key = olm::client()->identity_keys().ed25519;
index.session_id = mtx::crypto::session_id(session.get());
json j;
j["data"] = data;
j["session"] = pickled;
auto txn = lmdb::txn::begin(env_);
outboundMegolmSessionDb_.put(txn, room_id, j.dump());
megolmSessionDataDb_.put(txn, json(index).dump(), json(data).dump());
txn.commit();
}
@ -544,8 +564,17 @@ Cache::getOutboundMegolmSession(const std::string &room_id)
auto obj = json::parse(value);
OutboundGroupSessionDataRef ref{};
ref.data = obj.at("data").get<OutboundGroupSessionData>();
ref.session = unpickle<OutboundSessionObject>(obj.at("session"), SECRET);
MegolmSessionIndex index;
index.room_id = room_id;
index.sender_key = olm::client()->identity_keys().ed25519;
index.session_id = mtx::crypto::session_id(ref.session.get());
if (megolmSessionDataDb_.get(txn, json(index).dump(), value)) {
ref.data = nlohmann::json::parse(value).get<GroupSessionData>();
}
return ref;
} catch (std::exception &e) {
nhlog::db()->error("Failed to retrieve outbound Megolm Session: {}", e.what());
@ -829,6 +858,7 @@ Cache::deleteData()
lmdb::dbi_close(env_, inboundMegolmSessionDb_);
lmdb::dbi_close(env_, outboundMegolmSessionDb_);
lmdb::dbi_close(env_, megolmSessionDataDb_);
env_.close();
@ -3525,6 +3555,7 @@ to_json(json &j, const UserKeyCache &info)
{
j["device_keys"] = info.device_keys;
j["seen_device_keys"] = info.seen_device_keys;
j["seen_device_ids"] = info.seen_device_ids;
j["master_keys"] = info.master_keys;
j["master_key_changed"] = info.master_key_changed;
j["user_signing_keys"] = info.user_signing_keys;
@ -3538,6 +3569,7 @@ from_json(const json &j, UserKeyCache &info)
{
info.device_keys = j.value("device_keys", std::map<std::string, mtx::crypto::DeviceKeys>{});
info.seen_device_keys = j.value("seen_device_keys", std::set<std::string>{});
info.seen_device_ids = j.value("seen_device_ids", std::set<std::string>{});
info.master_keys = j.value("master_keys", mtx::crypto::CrossSigningKeys{});
info.master_key_changed = j.value("master_key_changed", false);
info.user_signing_keys = j.value("user_signing_keys", mtx::crypto::CrossSigningKeys{});
@ -3634,6 +3666,15 @@ Cache::updateUserKeys(const std::string &sync_token, const mtx::responses::Query
keyReused = true;
break;
}
if (updateToWrite.seen_device_ids.count(
device_id)) {
nhlog::crypto()->warn(
"device_id '{}' reused by ({})",
device_id,
user);
keyReused = true;
break;
}
}
if (!keyReused && !oldDeviceKeys.count(device_id))
@ -3644,6 +3685,7 @@ Cache::updateUserKeys(const std::string &sync_token, const mtx::responses::Query
(void)key_id;
updateToWrite.seen_device_keys.insert(key);
}
updateToWrite.seen_device_ids.insert(device_id);
}
}
db.put(txn, user, json(updateToWrite).dump());
@ -4077,17 +4119,15 @@ from_json(const json &j, MemberInfo &info)
}
void
to_json(nlohmann::json &obj, const DeviceAndMasterKeys &msg)
to_json(nlohmann::json &obj, const DeviceKeysToMsgIndex &msg)
{
obj["devices"] = msg.devices;
obj["master_keys"] = msg.master_keys;
obj["deviceids"] = msg.deviceids;
}
void
from_json(const nlohmann::json &obj, DeviceAndMasterKeys &msg)
from_json(const nlohmann::json &obj, DeviceKeysToMsgIndex &msg)
{
msg.devices = obj.at("devices").get<decltype(msg.devices)>();
msg.master_keys = obj.at("master_keys").get<decltype(msg.master_keys)>();
msg.deviceids = obj.at("deviceids").get<decltype(msg.deviceids)>();
}
void
@ -4099,30 +4139,31 @@ to_json(nlohmann::json &obj, const SharedWithUsers &msg)
void
from_json(const nlohmann::json &obj, SharedWithUsers &msg)
{
msg.keys = obj.at("keys").get<std::map<std::string, DeviceAndMasterKeys>>();
msg.keys = obj.at("keys").get<std::map<std::string, DeviceKeysToMsgIndex>>();
}
void
to_json(nlohmann::json &obj, const OutboundGroupSessionData &msg)
to_json(nlohmann::json &obj, const GroupSessionData &msg)
{
obj["session_id"] = msg.session_id;
obj["session_key"] = msg.session_key;
obj["message_index"] = msg.message_index;
obj["ts"] = msg.timestamp;
obj["initially"] = msg.initially;
obj["sender_claimed_ed25519_key"] = msg.sender_claimed_ed25519_key;
obj["forwarding_curve25519_key_chain"] = msg.forwarding_curve25519_key_chain;
obj["currently"] = msg.currently;
}
void
from_json(const nlohmann::json &obj, OutboundGroupSessionData &msg)
from_json(const nlohmann::json &obj, GroupSessionData &msg)
{
msg.session_id = obj.at("session_id");
msg.session_key = obj.at("session_key");
msg.message_index = obj.at("message_index");
msg.timestamp = obj.value("ts", 0ULL);
msg.initially = obj.value("initially", SharedWithUsers{});
msg.sender_claimed_ed25519_key = obj.value("sender_claimed_ed25519_key", "");
msg.forwarding_curve25519_key_chain =
obj.value("forwarding_curve25519_key_chain", std::vector<std::string>{});
msg.currently = obj.value("currently", SharedWithUsers{});
}
@ -4522,7 +4563,7 @@ isRoomMember(const std::string &user_id, const std::string &room_id)
//
void
saveOutboundMegolmSession(const std::string &room_id,
const OutboundGroupSessionData &data,
const GroupSessionData &data,
mtx::crypto::OutboundGroupSessionPtr &session)
{
instance_->saveOutboundMegolmSession(room_id, data, session);
@ -4539,7 +4580,7 @@ outboundMegolmSessionExists(const std::string &room_id) noexcept
}
void
updateOutboundMegolmSession(const std::string &room_id,
const OutboundGroupSessionData &data,
const GroupSessionData &data,
mtx::crypto::OutboundGroupSessionPtr &session)
{
instance_->updateOutboundMegolmSession(room_id, data, session);
@ -4566,9 +4607,10 @@ exportSessionKeys()
//
void
saveInboundMegolmSession(const MegolmSessionIndex &index,
mtx::crypto::InboundGroupSessionPtr session)
mtx::crypto::InboundGroupSessionPtr session,
const GroupSessionData &data)
{
instance_->saveInboundMegolmSession(index, std::move(session));
instance_->saveInboundMegolmSession(index, std::move(session), data);
}
mtx::crypto::InboundGroupSessionPtr
getInboundMegolmSession(const MegolmSessionIndex &index)