diff --git a/src/Cache.cpp b/src/Cache.cpp
index 9304db0eafe45ef2ec18a16b6bc19f06f2dee69e..7b6a6135a45345de3cc1481792a48ab75cf789fa 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<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());
@@ -553,6 +582,25 @@ Cache::getOutboundMegolmSession(const std::string &room_id)
         }
 }
 
+std::optional<GroupSessionData>
+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<GroupSessionData>();
+                }
+
+                return std::nullopt;
+        } catch (std::exception &e) {
+                nhlog::db()->error("Failed to retrieve Megolm Session Data: {}", e.what());
+                return std::nullopt;
+        }
+}
 //
 // OLM sessions.
 //
@@ -829,6 +877,7 @@ Cache::deleteData()
 
         lmdb::dbi_close(env_, inboundMegolmSessionDb_);
         lmdb::dbi_close(env_, outboundMegolmSessionDb_);
+        lmdb::dbi_close(env_, megolmSessionDataDb_);
 
         env_.close();
 
@@ -3525,6 +3574,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 +3588,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 +3685,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 +3704,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 +4138,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 +4158,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 +4582,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 +4599,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 +4626,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)
@@ -4580,6 +4641,11 @@ inboundMegolmSessionExists(const MegolmSessionIndex &index)
 {
         return instance_->inboundMegolmSessionExists(index);
 }
+std::optional<GroupSessionData>
+getMegolmSessionData(const MegolmSessionIndex &index)
+{
+        return instance_->getMegolmSessionData(index);
+}
 
 //
 // Olm Sessions
diff --git a/src/Cache.h b/src/Cache.h
index b0520f6b112489170576b3583fc4134e2d07c4f4..57a36d73aa81794cc0faef159781bf0876ebb2ee 100644
--- a/src/Cache.h
+++ b/src/Cache.h
@@ -200,7 +200,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);
 OutboundGroupSessionDataRef
 getOutboundMegolmSession(const std::string &room_id);
@@ -208,7 +208,7 @@ bool
 outboundMegolmSessionExists(const std::string &room_id) noexcept;
 void
 updateOutboundMegolmSession(const std::string &room_id,
-                            const OutboundGroupSessionData &data,
+                            const GroupSessionData &data,
                             mtx::crypto::OutboundGroupSessionPtr &session);
 void
 dropOutboundMegolmSession(const std::string &room_id);
@@ -223,11 +223,14 @@ exportSessionKeys();
 //
 void
 saveInboundMegolmSession(const MegolmSessionIndex &index,
-                         mtx::crypto::InboundGroupSessionPtr session);
+                         mtx::crypto::InboundGroupSessionPtr session,
+                         const GroupSessionData &data);
 mtx::crypto::InboundGroupSessionPtr
 getInboundMegolmSession(const MegolmSessionIndex &index);
 bool
 inboundMegolmSessionExists(const MegolmSessionIndex &index);
+std::optional<GroupSessionData>
+getMegolmSessionData(const MegolmSessionIndex &index);
 
 //
 // Olm Sessions
diff --git a/src/CacheCryptoStructs.h b/src/CacheCryptoStructs.h
index 07ca274e39642af9c387a54143defabdc617c002..409c9d67b41f6d97875d6d7f30ab4ccf4e4aa60c 100644
--- a/src/CacheCryptoStructs.h
+++ b/src/CacheCryptoStructs.h
@@ -27,40 +27,43 @@ enum Trust
 Q_ENUM_NS(Trust)
 }
 
-struct DeviceAndMasterKeys
+struct DeviceKeysToMsgIndex
 {
-        // map from device id or master key id to message_index
-        std::map<std::string, uint64_t> devices, master_keys;
+        // map from device key to message_index
+        // Using the device id is safe because we check for reuse on device list updates
+        // Using the device id makes our logic much easier to read.
+        std::map<std::string, uint64_t> deviceids;
 };
 
 struct SharedWithUsers
 {
         // userid to keys
-        std::map<std::string, DeviceAndMasterKeys> keys;
+        std::map<std::string, DeviceKeysToMsgIndex> keys;
 };
 
 // Extra information associated with an outbound megolm session.
-struct OutboundGroupSessionData
+struct GroupSessionData
 {
-        std::string session_id;
-        std::string session_key;
         uint64_t message_index = 0;
         uint64_t timestamp     = 0;
 
+        std::string sender_claimed_ed25519_key;
+        std::vector<std::string> forwarding_curve25519_key_chain;
+
         // who has access to this session.
         // Rotate, when a user leaves the room and share, when a user gets added.
-        SharedWithUsers initially, currently;
+        SharedWithUsers currently;
 };
 
 void
-to_json(nlohmann::json &obj, const OutboundGroupSessionData &msg);
+to_json(nlohmann::json &obj, const GroupSessionData &msg);
 void
-from_json(const nlohmann::json &obj, OutboundGroupSessionData &msg);
+from_json(const nlohmann::json &obj, GroupSessionData &msg);
 
 struct OutboundGroupSessionDataRef
 {
         mtx::crypto::OutboundGroupSessionPtr session;
-        OutboundGroupSessionData data;
+        GroupSessionData data;
 };
 
 struct DevicePublicKeys
@@ -134,6 +137,8 @@ struct UserKeyCache
         bool master_key_changed = false;
         //! Device keys that were already used at least once
         std::set<std::string> seen_device_keys;
+        //! Device ids that were already used at least once
+        std::set<std::string> seen_device_ids;
 };
 
 void
diff --git a/src/Cache_p.h b/src/Cache_p.h
index c76cc717a9c098be388e6821bf6bf177cda4c970..d1f6307d83742f94d11357574b923489f4830f8d 100644
--- a/src/Cache_p.h
+++ b/src/Cache_p.h
@@ -238,12 +238,12 @@ public:
         // Outbound Megolm Sessions
         //
         void saveOutboundMegolmSession(const std::string &room_id,
-                                       const OutboundGroupSessionData &data,
+                                       const GroupSessionData &data,
                                        mtx::crypto::OutboundGroupSessionPtr &session);
         OutboundGroupSessionDataRef getOutboundMegolmSession(const std::string &room_id);
         bool outboundMegolmSessionExists(const std::string &room_id) noexcept;
         void updateOutboundMegolmSession(const std::string &room_id,
-                                         const OutboundGroupSessionData &data,
+                                         const GroupSessionData &data,
                                          mtx::crypto::OutboundGroupSessionPtr &session);
         void dropOutboundMegolmSession(const std::string &room_id);
 
@@ -254,10 +254,12 @@ public:
         // Inbound Megolm Sessions
         //
         void saveInboundMegolmSession(const MegolmSessionIndex &index,
-                                      mtx::crypto::InboundGroupSessionPtr session);
+                                      mtx::crypto::InboundGroupSessionPtr session,
+                                      const GroupSessionData &data);
         mtx::crypto::InboundGroupSessionPtr getInboundMegolmSession(
           const MegolmSessionIndex &index);
         bool inboundMegolmSessionExists(const MegolmSessionIndex &index);
+        std::optional<GroupSessionData> getMegolmSessionData(const MegolmSessionIndex &index);
 
         //
         // Olm Sessions
@@ -676,6 +678,7 @@ private:
 
         lmdb::dbi inboundMegolmSessionDb_;
         lmdb::dbi outboundMegolmSessionDb_;
+        lmdb::dbi megolmSessionDataDb_;
 
         QString localUserId_;
         QString cacheDirectory_;
diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index 6003eb855cf05a28480a32be64f0359f27f8f685..10a91557e835447c7ddf694fe451f8f572e6408b 100644
--- a/src/ChatPage.cpp
+++ b/src/ChatPage.cpp
@@ -939,12 +939,16 @@ ChatPage::ensureOneTimeKeyCount(const std::map<std::string, uint16_t> &counts)
                           [](const mtx::responses::UploadKeys &, mtx::http::RequestErr err) {
                                   if (err) {
                                           nhlog::crypto()->warn(
-                                            "failed to update one-time keys: {} {}",
+                                            "failed to update one-time keys: {} {} {}",
                                             err->matrix_error.error,
-                                            static_cast<int>(err->status_code));
-                                          return;
+                                            static_cast<int>(err->status_code),
+                                            static_cast<int>(err->error_code));
+
+                                          if (err->status_code < 400 || err->status_code >= 500)
+                                                  return;
                                   }
 
+                                  // mark as published anyway, otherwise we may end up in a loop.
                                   olm::mark_keys_as_published();
                           });
                 }
diff --git a/src/Olm.cpp b/src/Olm.cpp
index ff4c883b0b9e6c0e8411c4891200e740dc0eb4e5..18e2ddcf0fe36ed0f84fe5ba85246ca83614bf1d 100644
--- a/src/Olm.cpp
+++ b/src/Olm.cpp
@@ -123,7 +123,17 @@ handle_to_device_messages(const std::vector<mtx::events::collections::DeviceEven
                 if (msg_type == to_string(mtx::events::EventType::RoomEncrypted)) {
                         try {
                                 olm::OlmMessage olm_msg = j_msg;
-                                handle_olm_message(std::move(olm_msg));
+                                cache::client()->query_keys(
+                                  olm_msg.sender,
+                                  [olm_msg](const UserKeyCache &userKeys, mtx::http::RequestErr e) {
+                                          if (e) {
+                                                  nhlog::crypto()->error(
+                                                    "Failed to query user keys, dropping olm "
+                                                    "message");
+                                                  return;
+                                          }
+                                          handle_olm_message(std::move(olm_msg), userKeys);
+                                  });
                         } catch (const nlohmann::json::exception &e) {
                                 nhlog::crypto()->warn(
                                   "parsing error for olm message: {} {}", e.what(), j_msg.dump(2));
@@ -197,7 +207,7 @@ handle_to_device_messages(const std::vector<mtx::events::collections::DeviceEven
 }
 
 void
-handle_olm_message(const OlmMessage &msg)
+handle_olm_message(const OlmMessage &msg, const UserKeyCache &otherUserDeviceKeys)
 {
         nhlog::crypto()->info("sender    : {}", msg.sender);
         nhlog::crypto()->info("sender_key: {}", msg.sender_key);
@@ -209,7 +219,7 @@ handle_olm_message(const OlmMessage &msg)
                 if (cipher.first != my_key) {
                         nhlog::crypto()->debug(
                           "Skipping message for {} since we are {}.", cipher.first, my_key);
-                        continue;
+                        return;
                 }
 
                 const auto type = cipher.second.type;
@@ -231,6 +241,57 @@ handle_olm_message(const OlmMessage &msg)
                 if (!payload.is_null()) {
                         mtx::events::collections::DeviceEvents device_event;
 
+                        // Other properties are included in order to prevent an attacker from
+                        // publishing someone else's curve25519 keys as their own and subsequently
+                        // claiming to have sent messages which they didn't. sender must correspond
+                        // to the user who sent the event, recipient to the local user, and
+                        // recipient_keys to the local ed25519 key.
+                        std::string receiver_ed25519 = payload["recipient_keys"]["ed25519"];
+                        if (receiver_ed25519.empty() ||
+                            receiver_ed25519 != olm::client()->identity_keys().ed25519) {
+                                nhlog::crypto()->warn(
+                                  "Decrypted event doesn't include our ed25519: {}",
+                                  payload.dump());
+                                return;
+                        }
+                        std::string receiver = payload["recipient"];
+                        if (receiver.empty() || receiver != http::client()->user_id().to_string()) {
+                                nhlog::crypto()->warn(
+                                  "Decrypted event doesn't include our user_id: {}",
+                                  payload.dump());
+                                return;
+                        }
+
+                        // Clients must confirm that the sender_key and the ed25519 field value
+                        // under the keys property match the keys returned by /keys/query for the
+                        // given user, and must also verify the signature of the payload. Without
+                        // this check, a client cannot be sure that the sender device owns the
+                        // private part of the ed25519 key it claims to have in the Olm payload.
+                        // This is crucial when the ed25519 key corresponds to a verified device.
+                        std::string sender_ed25519 = payload["keys"]["ed25519"];
+                        if (sender_ed25519.empty()) {
+                                nhlog::crypto()->warn(
+                                  "Decrypted event doesn't include sender ed25519: {}",
+                                  payload.dump());
+                                return;
+                        }
+
+                        bool from_their_device = false;
+                        for (auto [device_id, key] : otherUserDeviceKeys.device_keys) {
+                                if (key.keys.at("curve25519:" + device_id) == msg.sender_key) {
+                                        if (key.keys.at("ed25519:" + device_id) == sender_ed25519) {
+                                                from_their_device = true;
+                                                break;
+                                        }
+                                }
+                        }
+                        if (!from_their_device) {
+                                nhlog::crypto()->warn("Decrypted event isn't sent from a device "
+                                                      "listed by that user! {}",
+                                                      payload.dump());
+                                return;
+                        }
+
                         {
                                 std::string msg_type = payload["type"];
                                 json event_array     = json::array();
@@ -242,7 +303,7 @@ handle_olm_message(const OlmMessage &msg)
                                 if (temp_events.empty()) {
                                         nhlog::crypto()->warn("Decrypted unknown event: {}",
                                                               payload.dump());
-                                        continue;
+                                        return;
                                 }
                                 device_event = temp_events.at(0);
                         }
@@ -276,17 +337,20 @@ handle_olm_message(const OlmMessage &msg)
                                 ChatPage::instance()->receivedDeviceVerificationDone(e8->content);
                         } else if (auto roomKey =
                                      std::get_if<DeviceEvent<msg::RoomKey>>(&device_event)) {
-                                create_inbound_megolm_session(*roomKey, msg.sender_key);
+                                create_inbound_megolm_session(
+                                  *roomKey, msg.sender_key, sender_ed25519);
                         } else if (auto forwardedRoomKey =
                                      std::get_if<DeviceEvent<msg::ForwardedRoomKey>>(
                                        &device_event)) {
+                                forwardedRoomKey->content.forwarding_curve25519_key_chain.push_back(
+                                  msg.sender_key);
                                 import_inbound_megolm_session(*forwardedRoomKey);
                         } else if (auto e =
                                      std::get_if<DeviceEvent<msg::SecretSend>>(&device_event)) {
                                 auto local_user = http::client()->user_id();
 
                                 if (msg.sender != local_user.to_string())
-                                        continue;
+                                        return;
 
                                 auto secret_name =
                                   request_id_to_secret_name.find(e->content.request_id);
@@ -306,7 +370,7 @@ handle_olm_message(const OlmMessage &msg)
                                           cache::verificationStatus(local_user.to_string());
 
                                         if (!verificationStatus)
-                                                continue;
+                                                return;
 
                                         auto deviceKeys = cache::userKeys(local_user.to_string());
                                         std::string sender_device_id;
@@ -344,7 +408,6 @@ handle_olm_message(const OlmMessage &msg)
                                                               "for secrect "
                                                               "'{}'",
                                                               name);
-                                                            return;
                                                     }
                                             });
 
@@ -364,13 +427,8 @@ handle_olm_message(const OlmMessage &msg)
         }
 
         try {
-                auto otherUserDeviceKeys = cache::userKeys(msg.sender);
-
-                if (!otherUserDeviceKeys)
-                        return;
-
                 std::map<std::string, std::vector<std::string>> targets;
-                for (auto [device_id, key] : otherUserDeviceKeys->device_keys) {
+                for (auto [device_id, key] : otherUserDeviceKeys.device_keys) {
                         if (key.keys.at("curve25519:" + device_id) == msg.sender_key)
                                 targets[msg.sender].push_back(device_id);
                 }
@@ -450,7 +508,7 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id,
 
         std::map<std::string, std::vector<std::string>> sendSessionTo;
         mtx::crypto::OutboundGroupSessionPtr session = nullptr;
-        OutboundGroupSessionData group_session_data;
+        GroupSessionData group_session_data;
 
         if (cache::outboundMegolmSessionExists(room_id)) {
                 auto res                = cache::getOutboundMegolmSession(room_id);
@@ -519,7 +577,8 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id,
                                 } else {
                                         // compare devices
                                         bool device_removed = false;
-                                        for (const auto &dev : session_member_it->second.devices) {
+                                        for (const auto &dev :
+                                             session_member_it->second.deviceids) {
                                                 if (!member_it->second ||
                                                     !member_it->second->device_keys.count(
                                                       dev.first)) {
@@ -541,7 +600,7 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id,
                                         if (member_it->second)
                                                 for (const auto &dev :
                                                      member_it->second->device_keys)
-                                                        if (!session_member_it->second.devices
+                                                        if (!session_member_it->second.deviceids
                                                                .count(dev.first) &&
                                                             (member_it->first != own_user_id ||
                                                              dev.first != device_id))
@@ -571,32 +630,28 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id,
                 const auto session_key = mtx::crypto::session_key(session.get());
 
                 // Saving the new megolm session.
-                OutboundGroupSessionData session_data{};
-                session_data.session_id    = mtx::crypto::session_id(session.get());
-                session_data.session_key   = mtx::crypto::session_key(session.get());
-                session_data.message_index = 0;
-                session_data.timestamp     = QDateTime::currentMSecsSinceEpoch();
+                GroupSessionData session_data{};
+                session_data.message_index              = 0;
+                session_data.timestamp                  = QDateTime::currentMSecsSinceEpoch();
+                session_data.sender_claimed_ed25519_key = olm::client()->identity_keys().ed25519;
 
                 sendSessionTo.clear();
 
                 for (const auto &[user, devices] : members) {
                         sendSessionTo[user]               = {};
-                        session_data.initially.keys[user] = {};
+                        session_data.currently.keys[user] = {};
                         if (devices) {
                                 for (const auto &[device_id_, key] : devices->device_keys) {
                                         (void)key;
                                         if (device_id != device_id_ || user != own_user_id) {
                                                 sendSessionTo[user].push_back(device_id_);
-                                                session_data.initially.keys[user]
-                                                  .devices[device_id_] = 0;
+                                                session_data.currently.keys[user]
+                                                  .deviceids[device_id_] = 0;
                                         }
                                 }
                         }
                 }
 
-                cache::saveOutboundMegolmSession(room_id, session_data, session);
-                group_session_data = std::move(session_data);
-
                 {
                         MegolmSessionIndex index;
                         index.room_id    = room_id;
@@ -604,8 +659,12 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id,
                         index.sender_key = olm::client()->identity_keys().curve25519;
                         auto megolm_session =
                           olm::client()->init_inbound_group_session(session_key);
-                        cache::saveInboundMegolmSession(index, std::move(megolm_session));
+                        cache::saveInboundMegolmSession(
+                          index, std::move(megolm_session), session_data);
                 }
+
+                cache::saveOutboundMegolmSession(room_id, session_data, session);
+                group_session_data = std::move(session_data);
         }
 
         mtx::events::DeviceEvent<mtx::events::msg::RoomKey> megolm_payload{};
@@ -641,8 +700,8 @@ encrypt_group_message(const std::string &room_id, const std::string &device_id,
                         group_session_data.currently.keys[user] = {};
 
                 for (const auto &device_id_ : devices) {
-                        if (!group_session_data.currently.keys[user].devices.count(device_id_))
-                                group_session_data.currently.keys[user].devices[device_id_] =
+                        if (!group_session_data.currently.keys[user].deviceids.count(device_id_))
+                                group_session_data.currently.keys[user].deviceids[device_id_] =
                                   group_session_data.message_index;
                 }
         }
@@ -704,7 +763,8 @@ try_olm_decryption(const std::string &sender_key, const mtx::events::msg::OlmCip
 
 void
 create_inbound_megolm_session(const mtx::events::DeviceEvent<mtx::events::msg::RoomKey> &roomKey,
-                              const std::string &sender_key)
+                              const std::string &sender_key,
+                              const std::string &sender_ed25519)
 {
         MegolmSessionIndex index;
         index.room_id    = roomKey.content.room_id;
@@ -712,9 +772,13 @@ create_inbound_megolm_session(const mtx::events::DeviceEvent<mtx::events::msg::R
         index.sender_key = sender_key;
 
         try {
+                GroupSessionData data{};
+                data.forwarding_curve25519_key_chain = {sender_key};
+                data.sender_claimed_ed25519_key      = sender_ed25519;
+
                 auto megolm_session =
                   olm::client()->init_inbound_group_session(roomKey.content.session_key);
-                cache::saveInboundMegolmSession(index, std::move(megolm_session));
+                cache::saveInboundMegolmSession(index, std::move(megolm_session), data);
         } catch (const lmdb::error &e) {
                 nhlog::crypto()->critical("failed to save inbound megolm session: {}", e.what());
                 return;
@@ -741,7 +805,13 @@ import_inbound_megolm_session(
         try {
                 auto megolm_session =
                   olm::client()->import_inbound_group_session(roomKey.content.session_key);
-                cache::saveInboundMegolmSession(index, std::move(megolm_session));
+
+                GroupSessionData data{};
+                data.forwarding_curve25519_key_chain =
+                  roomKey.content.forwarding_curve25519_key_chain;
+                data.sender_claimed_ed25519_key = roomKey.content.sender_claimed_ed25519_key;
+
+                cache::saveInboundMegolmSession(index, std::move(megolm_session), data);
         } catch (const lmdb::error &e) {
                 nhlog::crypto()->critical("failed to save inbound megolm session: {}", e.what());
                 return;
@@ -817,30 +887,33 @@ handle_key_request_message(const mtx::events::DeviceEvent<mtx::events::msg::KeyR
                 return;
         }
 
-        // Check if we were the sender of the session being requested.
-        if (req.content.sender_key != olm::client()->identity_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);
@@ -873,12 +946,12 @@ handle_key_request_message(const mtx::events::DeviceEvent<mtx::events::msg::KeyR
 
         bool shouldSeeKeys    = false;
         uint64_t minimumIndex = -1;
-        if (outboundSession.data.currently.keys.count(req.sender)) {
-                if (outboundSession.data.currently.keys.at(req.sender)
-                      .devices.count(req.content.requesting_device_id)) {
+        if (sessionData->currently.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)
-                                         .devices.at(req.content.requesting_device_id);
+                        minimumIndex  = sessionData->currently.keys.at(req.sender)
+                                         .deviceids.at(req.content.requesting_device_id);
                 }
         }
 
@@ -907,8 +980,9 @@ handle_key_request_message(const mtx::events::DeviceEvent<mtx::events::msg::KeyR
                 forward_key.sender_key  = index.sender_key;
 
                 // TODO(Nico): Figure out if this is correct
-                forward_key.sender_claimed_ed25519_key = olm::client()->identity_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);
@@ -929,6 +1003,7 @@ send_megolm_key_to_device(const std::string &user_id,
         std::map<std::string, std::vector<std::string>> 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
diff --git a/src/Olm.h b/src/Olm.h
index 8479f4f2967e9fe08b51f89d258899a504c4682a..a18cbbfb2d7a146d042ef3e79da3ff83966afcf6 100644
--- a/src/Olm.h
+++ b/src/Olm.h
@@ -59,12 +59,13 @@ try_olm_decryption(const std::string &sender_key,
                    const mtx::events::msg::OlmCipherContent &content);
 
 void
-handle_olm_message(const OlmMessage &msg);
+handle_olm_message(const OlmMessage &msg, const UserKeyCache &otherUserDeviceKeys);
 
 //! Establish a new inbound megolm session with the decrypted payload from olm.
 void
 create_inbound_megolm_session(const mtx::events::DeviceEvent<mtx::events::msg::RoomKey> &roomKey,
-                              const std::string &sender_key);
+                              const std::string &sender_key,
+                              const std::string &sender_ed25519);
 void
 import_inbound_megolm_session(
   const mtx::events::DeviceEvent<mtx::events::msg::ForwardedRoomKey> &roomKey);
diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp
index 740b89790e868cbbfee013230cfce0eaad8fe812..ffaebe617b48ff109f9cfd4b1d1d97dec54397fe 100644
--- a/src/UserSettingsPage.cpp
+++ b/src/UserSettingsPage.cpp
@@ -91,7 +91,7 @@ UserSettings::load(std::optional<QString> profile)
         privacyScreen_        = settings.value("user/privacy_screen", false).toBool();
         privacyScreenTimeout_ = settings.value("user/privacy_screen_timeout", 0).toInt();
         shareKeysWithTrustedUsers_ =
-          settings.value("user/share_keys_with_trusted_users", true).toBool();
+          settings.value("user/automatically_share_keys_with_trusted_users", false).toBool();
         mobileMode_        = settings.value("user/mobile_mode", false).toBool();
         emojiFont_         = settings.value("user/emoji_font_family", "default").toString();
         baseFontSize_      = settings.value("user/font_size", QFont().pointSizeF()).toDouble();
@@ -610,7 +610,8 @@ UserSettings::save()
         settings.setValue("decrypt_sidebar", decryptSidebar_);
         settings.setValue("privacy_screen", privacyScreen_);
         settings.setValue("privacy_screen_timeout", privacyScreenTimeout_);
-        settings.setValue("share_keys_with_trusted_users", shareKeysWithTrustedUsers_);
+        settings.setValue("automatically_share_keys_with_trusted_users",
+                          shareKeysWithTrustedUsers_);
         settings.setValue("mobile_mode", mobileMode_);
         settings.setValue("font_size", baseFontSize_);
         settings.setValue("typing_notifications", typingNotifications_);