From 9fadd148715790743cb4e87bfe1854923e59c06b Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sat, 17 Jul 2021 01:27:37 +0200 Subject: Store megolm session data in separate database --- src/Cache.cpp | 102 +++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 72 insertions(+), 30 deletions(-) (limited to 'src/Cache.cpp') diff --git a/src/Cache.cpp b/src/Cache.cpp index 9304db0e..1c156104 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -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>; using Receipts = std::map>; @@ -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(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(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(); ref.session = unpickle(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(); + } + 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{}); info.seen_device_keys = j.value("seen_device_keys", std::set{}); + info.seen_device_ids = j.value("seen_device_ids", std::set{}); 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(); - msg.master_keys = obj.at("master_keys").get(); + msg.deviceids = obj.at("deviceids").get(); } 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>(); + msg.keys = obj.at("keys").get>(); } 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{}); + 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) -- cgit 1.5.1 From 3f0aa13cb6f83c8768dc30b1a1872b9696992fa7 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sat, 17 Jul 2021 02:14:44 +0200 Subject: Share historical keys We share all keys with our devices and ones created by us to other users. --- src/Cache.cpp | 24 ++++++++++++++++++++++++ src/Cache.h | 2 ++ src/Cache_p.h | 1 + src/Olm.cpp | 50 ++++++++++++++++++++++++++++---------------------- 4 files changed, 55 insertions(+), 22 deletions(-) (limited to 'src/Cache.cpp') diff --git a/src/Cache.cpp b/src/Cache.cpp index 1c156104..7b6a6135 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -582,6 +582,25 @@ Cache::getOutboundMegolmSession(const std::string &room_id) } } +std::optional +Cache::getMegolmSessionData(const MegolmSessionIndex &index) +{ + try { + using namespace mtx::crypto; + + auto txn = ro_txn(env_); + + std::string_view value; + if (megolmSessionDataDb_.get(txn, json(index).dump(), value)) { + return nlohmann::json::parse(value).get(); + } + + return std::nullopt; + } catch (std::exception &e) { + nhlog::db()->error("Failed to retrieve Megolm Session Data: {}", e.what()); + return std::nullopt; + } +} // // OLM sessions. // @@ -4622,6 +4641,11 @@ inboundMegolmSessionExists(const MegolmSessionIndex &index) { return instance_->inboundMegolmSessionExists(index); } +std::optional +getMegolmSessionData(const MegolmSessionIndex &index) +{ + return instance_->getMegolmSessionData(index); +} // // Olm Sessions diff --git a/src/Cache.h b/src/Cache.h index 2b547876..57a36d73 100644 --- a/src/Cache.h +++ b/src/Cache.h @@ -229,6 +229,8 @@ mtx::crypto::InboundGroupSessionPtr getInboundMegolmSession(const MegolmSessionIndex &index); bool inboundMegolmSessionExists(const MegolmSessionIndex &index); +std::optional +getMegolmSessionData(const MegolmSessionIndex &index); // // Olm Sessions diff --git a/src/Cache_p.h b/src/Cache_p.h index 6f208925..d1f6307d 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -259,6 +259,7 @@ public: mtx::crypto::InboundGroupSessionPtr getInboundMegolmSession( const MegolmSessionIndex &index); bool inboundMegolmSessionExists(const MegolmSessionIndex &index); + std::optional getMegolmSessionData(const MegolmSessionIndex &index); // // Olm Sessions diff --git a/src/Olm.cpp b/src/Olm.cpp index d9447031..18e2ddcf 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -631,8 +631,9 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id, // Saving the new megolm session. GroupSessionData session_data{}; - session_data.message_index = 0; - session_data.timestamp = QDateTime::currentMSecsSinceEpoch(); + session_data.message_index = 0; + session_data.timestamp = QDateTime::currentMSecsSinceEpoch(); + session_data.sender_claimed_ed25519_key = olm::client()->identity_keys().ed25519; sendSessionTo.clear(); @@ -886,30 +887,33 @@ handle_key_request_message(const mtx::events::DeviceEventidentity_keys().curve25519) { - nhlog::crypto()->debug("ignoring key request {} because we were not the sender: " - "\nrequested({}) ours({})", - req.content.request_id, - req.content.sender_key, - olm::client()->identity_keys().curve25519); + // Check if we were the sender of the session being requested (unless it is actually us + // requesting the session). + if (req.sender != http::client()->user_id().to_string() && + req.content.sender_key != olm::client()->identity_keys().curve25519) { + nhlog::crypto()->debug( + "ignoring key request {} because we did not create the requested session: " + "\nrequested({}) ours({})", + req.content.request_id, + req.content.sender_key, + olm::client()->identity_keys().curve25519); return; } + // Check that the requested session_id and the one we have saved match. + MegolmSessionIndex index{}; + index.room_id = req.content.room_id; + index.session_id = req.content.session_id; + index.sender_key = req.content.sender_key; + // Check if we have the keys for the requested session. - auto outboundSession = cache::getOutboundMegolmSession(req.content.room_id); - if (!outboundSession.session) { + auto sessionData = cache::getMegolmSessionData(index); + if (!sessionData) { nhlog::crypto()->warn("requested session not found in room: {}", req.content.room_id); return; } - // Check that the requested session_id and the one we have saved match. - MegolmSessionIndex index{}; - index.room_id = req.content.room_id; - index.session_id = req.content.session_id; - index.sender_key = olm::client()->identity_keys().curve25519; - const auto session = cache::getInboundMegolmSession(index); if (!session) { nhlog::crypto()->warn("No session with id {} in db", req.content.session_id); @@ -942,11 +946,11 @@ handle_key_request_message(const mtx::events::DeviceEventcurrently.keys.count(req.sender)) { + if (sessionData->currently.keys.at(req.sender) .deviceids.count(req.content.requesting_device_id)) { shouldSeeKeys = true; - minimumIndex = outboundSession.data.currently.keys.at(req.sender) + minimumIndex = sessionData->currently.keys.at(req.sender) .deviceids.at(req.content.requesting_device_id); } } @@ -976,8 +980,9 @@ handle_key_request_message(const mtx::events::DeviceEventidentity_keys().ed25519; - forward_key.forwarding_curve25519_key_chain = {}; + forward_key.sender_claimed_ed25519_key = sessionData->sender_claimed_ed25519_key; + forward_key.forwarding_curve25519_key_chain = + sessionData->forwarding_curve25519_key_chain; send_megolm_key_to_device( req.sender, req.content.requesting_device_id, forward_key); @@ -998,6 +1003,7 @@ send_megolm_key_to_device(const std::string &user_id, std::map> targets; targets[user_id] = {device_id}; send_encrypted_to_device_messages(targets, room_key); + nhlog::crypto()->debug("Forwarded key to {}:{}", user_id, device_id); } DecryptionResult -- cgit 1.5.1