diff --git a/include/Cache.h b/include/Cache.h
index afc7a148..994a6da7 100644
--- a/include/Cache.h
+++ b/include/Cache.h
@@ -17,13 +17,16 @@
#pragma once
-#include <QDebug>
#include <QDir>
#include <QImage>
+
#include <json.hpp>
#include <lmdb++.h>
#include <mtx/events/join_rules.hpp>
#include <mtx/responses.hpp>
+#include <mtxclient/crypto/client.hpp>
+#include <mutex>
+
using mtx::events::state::JoinRule;
struct RoomMember
@@ -140,6 +143,83 @@ struct RoomSearchResult
Q_DECLARE_METATYPE(RoomSearchResult)
Q_DECLARE_METATYPE(RoomInfo)
+// Extra information associated with an outbound megolm session.
+struct OutboundGroupSessionData
+{
+ std::string session_id;
+ std::string session_key;
+ uint64_t message_index = 0;
+};
+
+inline void
+to_json(nlohmann::json &obj, const OutboundGroupSessionData &msg)
+{
+ obj["session_id"] = msg.session_id;
+ obj["session_key"] = msg.session_key;
+ obj["message_index"] = msg.message_index;
+}
+
+inline void
+from_json(const nlohmann::json &obj, OutboundGroupSessionData &msg)
+{
+ msg.session_id = obj.at("session_id");
+ msg.session_key = obj.at("session_key");
+ msg.message_index = obj.at("message_index");
+}
+
+struct OutboundGroupSessionDataRef
+{
+ OlmOutboundGroupSession *session;
+ OutboundGroupSessionData data;
+};
+
+struct DevicePublicKeys
+{
+ std::string ed25519;
+ std::string curve25519;
+};
+
+inline void
+to_json(nlohmann::json &obj, const DevicePublicKeys &msg)
+{
+ obj["ed25519"] = msg.ed25519;
+ obj["curve25519"] = msg.curve25519;
+}
+
+inline void
+from_json(const nlohmann::json &obj, DevicePublicKeys &msg)
+{
+ msg.ed25519 = obj.at("ed25519");
+ msg.curve25519 = obj.at("curve25519");
+}
+
+//! Represents a unique megolm session identifier.
+struct MegolmSessionIndex
+{
+ //! The room in which this session exists.
+ std::string room_id;
+ //! The session_id of the megolm session.
+ std::string session_id;
+ //! The curve25519 public key of the sender.
+ std::string sender_key;
+
+ //! Representation to be used in a hash map.
+ std::string to_hash() const { return room_id + session_id + sender_key; }
+};
+
+struct OlmSessionStorage
+{
+ std::map<std::string, mtx::crypto::OlmSessionPtr> outbound_sessions;
+ std::map<std::string, mtx::crypto::InboundGroupSessionPtr> group_inbound_sessions;
+ std::map<std::string, mtx::crypto::OutboundGroupSessionPtr> group_outbound_sessions;
+ std::map<std::string, OutboundGroupSessionData> group_outbound_session_data;
+
+ // Guards for accessing critical data.
+ std::mutex outbound_mtx;
+ std::mutex group_outbound_mtx;
+ std::mutex group_inbound_mtx;
+};
+
class Cache : public QObject
{
Q_OBJECT
@@ -260,6 +340,48 @@ public:
//! Check if we have sent a desktop notification for the given event id.
bool isNotificationSent(const std::string &event_id);
+ //! Mark a room that uses e2e encryption.
+ void setEncryptedRoom(const std::string &room_id);
+ //! Save the public keys for a device.
+ void saveDeviceKeys(const std::string &device_id);
+ void getDeviceKeys(const std::string &device_id);
+
+ //! Save the device list for a user.
+ void setDeviceList(const std::string &user_id, const std::vector<std::string> &devices);
+ std::vector<std::string> getDeviceList(const std::string &user_id);
+
+ //
+ // Outbound Megolm Sessions
+ //
+ void saveOutboundMegolmSession(const MegolmSessionIndex &index,
+ const OutboundGroupSessionData &data,
+ mtx::crypto::OutboundGroupSessionPtr session);
+ OutboundGroupSessionDataRef getOutboundMegolmSession(const MegolmSessionIndex &index);
+ bool outboundMegolmSessionExists(const MegolmSessionIndex &index) noexcept;
+
+ //
+ // Inbound Megolm Sessions
+ //
+ void saveInboundMegolmSession(const MegolmSessionIndex &index,
+ mtx::crypto::InboundGroupSessionPtr session);
+ OlmInboundGroupSession *getInboundMegolmSession(const MegolmSessionIndex &index);
+ bool inboundMegolmSessionExists(const MegolmSessionIndex &index) noexcept;
+
+ //
+ // Outbound Olm Sessions
+ //
+ void saveOutboundOlmSession(const std::string &curve25519,
+ mtx::crypto::OlmSessionPtr session);
+ OlmSession *getOutboundOlmSession(const std::string &curve25519);
+ bool outboundOlmSessionsExists(const std::string &curve25519) noexcept;
+
+ void saveOlmAccount(const std::string &pickled);
+ std::string restoreOlmAccount();
+
+ void restoreSessions();
+
+ OlmSessionStorage session_storage;
+
private:
//! Save an invited room.
void saveInvite(lmdb::txn &txn,
@@ -451,6 +573,13 @@ private:
lmdb::dbi readReceiptsDb_;
lmdb::dbi notificationsDb_;
+ lmdb::dbi devicesDb_;
+ lmdb::dbi deviceKeysDb_;
+
+ lmdb::dbi inboundMegolmSessionDb_;
+ lmdb::dbi outboundMegolmSessionDb_;
+ lmdb::dbi outboundOlmSessionDb_;
+
QString localUserId_;
QString cacheDirectory_;
};
diff --git a/include/ChatPage.h b/include/ChatPage.h
index e99e94ba..d8582993 100644
--- a/include/ChatPage.h
+++ b/include/ChatPage.h
@@ -29,8 +29,7 @@
#include "Cache.h"
#include "CommunitiesList.h"
#include "Community.h"
-
-#include <mtx.hpp>
+#include "MatrixClient.h"
class OverlayModal;
class QuickSwitcher;
@@ -119,6 +118,7 @@ signals:
void loggedOut();
void trySyncCb();
+ void tryDelayedSyncCb();
void tryInitialSyncCb();
void leftRoom(const QString &room_id);
@@ -146,8 +146,12 @@ private slots:
private:
static ChatPage *instance_;
+ //! Handler callback for initial sync. It doesn't run on the main thread so all
+ //! communication with the GUI should be done through signals.
+ void initialSyncHandler(const mtx::responses::Sync &res, mtx::http::RequestErr err);
void tryInitialSync();
void trySync();
+ void ensureOneTimeKeyCount(const std::map<std::string, uint16_t> &counts);
//! Check if the given room is currently open.
bool isRoomActive(const QString &room_id)
diff --git a/include/Logging.hpp b/include/Logging.hpp
index c301d80d..bdbd3e2c 100644
--- a/include/Logging.hpp
+++ b/include/Logging.hpp
@@ -15,4 +15,7 @@ net();
std::shared_ptr<spdlog::logger>
db();
+
+std::shared_ptr<spdlog::logger>
+crypto();
}
diff --git a/include/MainWindow.h b/include/MainWindow.h
index f0fa9a08..b068e8f6 100644
--- a/include/MainWindow.h
+++ b/include/MainWindow.h
@@ -59,7 +59,6 @@ class MainWindow : public QMainWindow
public:
explicit MainWindow(QWidget *parent = 0);
- ~MainWindow();
static MainWindow *instance() { return instance_; };
void saveCurrentWindowSize();
diff --git a/include/MatrixClient.h b/include/MatrixClient.h
index 832d6cad..7ea5e0b7 100644
--- a/include/MatrixClient.h
+++ b/include/MatrixClient.h
@@ -11,12 +11,15 @@ Q_DECLARE_METATYPE(mtx::responses::Notifications)
Q_DECLARE_METATYPE(mtx::responses::Rooms)
Q_DECLARE_METATYPE(mtx::responses::Sync)
Q_DECLARE_METATYPE(std::string)
-Q_DECLARE_METATYPE(std::vector<std::string>);
+Q_DECLARE_METATYPE(std::vector<std::string>)
namespace http {
namespace v2 {
mtx::http::Client *
client();
+
+bool
+is_logged_in();
}
//! Initialize the http module
diff --git a/include/Olm.hpp b/include/Olm.hpp
new file mode 100644
index 00000000..2f7b1d64
--- /dev/null
+++ b/include/Olm.hpp
@@ -0,0 +1,65 @@
+#pragma once
+
+#include <memory>
+#include <mtxclient/crypto/client.hpp>
+
+constexpr auto OLM_ALGO = "m.olm.v1.curve25519-aes-sha2";
+
+namespace olm {
+
+struct OlmCipherContent
+{
+ std::string body;
+ uint8_t type;
+};
+
+inline void
+from_json(const nlohmann::json &obj, OlmCipherContent &msg)
+{
+ msg.body = obj.at("body");
+ msg.type = obj.at("type");
+}
+
+struct OlmMessage
+{
+ std::string sender_key;
+ std::string sender;
+
+ using RecipientKey = std::string;
+ std::map<RecipientKey, OlmCipherContent> ciphertext;
+};
+
+inline void
+from_json(const nlohmann::json &obj, OlmMessage &msg)
+{
+ if (obj.at("type") != "m.room.encrypted")
+ throw std::invalid_argument("invalid type for olm message");
+
+ if (obj.at("content").at("algorithm") != OLM_ALGO)
+ throw std::invalid_argument("invalid algorithm for olm message");
+
+ msg.sender = obj.at("sender");
+ msg.sender_key = obj.at("content").at("sender_key");
+ msg.ciphertext =
+ obj.at("content").at("ciphertext").get<std::map<std::string, OlmCipherContent>>();
+}
+
+mtx::crypto::OlmClient *
+client();
+
+void
+handle_to_device_messages(const std::vector<nlohmann::json> &msgs);
+
+void
+handle_olm_message(const OlmMessage &msg);
+
+void
+handle_olm_normal_message(const std::string &sender,
+ const std::string &sender_key,
+ const OlmCipherContent &content);
+
+void
+handle_pre_key_olm_message(const std::string &sender,
+ const std::string &sender_key,
+ const OlmCipherContent &content);
+} // namespace olm
diff --git a/include/timeline/TimelineView.h b/include/timeline/TimelineView.h
index 30af97fb..88857222 100644
--- a/include/timeline/TimelineView.h
+++ b/include/timeline/TimelineView.h
@@ -149,6 +149,9 @@ private:
QWidget *relativeWidget(TimelineItem *item, int dt) const;
+ TimelineEvent parseEncryptedEvent(
+ const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &e);
+
//! Callback for all message sending.
void sendRoomMessageHandler(const std::string &txn_id,
const mtx::responses::EventId &res,
|