aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/connection.cpp11
-rw-r--r--lib/connection.h5
-rw-r--r--lib/database.cpp41
-rw-r--r--lib/database.h3
-rw-r--r--lib/e2ee/qolmoutboundsession.cpp24
-rw-r--r--lib/e2ee/qolmoutboundsession.h10
-rw-r--r--lib/room.cpp33
7 files changed, 104 insertions, 23 deletions
diff --git a/lib/connection.cpp b/lib/connection.cpp
index 88c61530..d01b8ac4 100644
--- a/lib/connection.cpp
+++ b/lib/connection.cpp
@@ -2193,7 +2193,6 @@ bool Connection::hasOlmSession(User* user, const QString& deviceId) const
QPair<QOlmMessage::Type, QByteArray> Connection::olmEncryptMessage(User* user, const QString& device, const QByteArray& message)
{
//TODO be smarter about choosing a session; see e2ee impl guide
- //TODO create session?
const auto& curveKey = curveKeyForUserDevice(user->id(), device);
QOlmMessage::Type type = d->olmSessions[curveKey][0]->encryptMessageType();
auto result = d->olmSessions[curveKey][0]->encrypt(message);
@@ -2212,4 +2211,14 @@ void Connection::createOlmSession(const QString& theirIdentityKey, const QString
d->olmSessions[theirIdentityKey].push_back(std::move(std::get<std::unique_ptr<QOlmSession>>(session)));
}
+QOlmOutboundGroupSessionPtr Connection::loadCurrentOutboundMegolmSession(Room* room)
+{
+ return d->database->loadCurrentOutboundMegolmSession(room->id(), d->picklingMode);
+}
+
+void Connection::saveCurrentOutboundMegolmSession(Room *room, const QOlmOutboundGroupSessionPtr& data)
+{
+ d->database->saveCurrentOutboundMegolmSession(room->id(), d->picklingMode, data);
+}
+
#endif
diff --git a/lib/connection.h b/lib/connection.h
index 5ea7c5f1..6f75b068 100644
--- a/lib/connection.h
+++ b/lib/connection.h
@@ -25,6 +25,7 @@
#ifdef Quotient_E2EE_ENABLED
#include "e2ee/e2ee.h"
#include "e2ee/qolmmessage.h"
+#include "e2ee/qolmoutboundsession.h"
#endif
Q_DECLARE_METATYPE(Quotient::GetLoginFlowsJob::LoginFlow)
@@ -322,6 +323,10 @@ public:
void saveMegolmSession(Room* room, const QString& senderKey, QOlmInboundGroupSession* session, const QString& ed25519Key);
bool hasOlmSession(User* user, const QString& deviceId) const;
+ QOlmOutboundGroupSessionPtr loadCurrentOutboundMegolmSession(Room* room);
+ void saveCurrentOutboundMegolmSession(Room *room, const QOlmOutboundGroupSessionPtr& data);
+
+
//This currently assumes that an olm session with (user, device) exists
//TODO make this return an event?
QPair<QOlmMessage::Type, QByteArray> olmEncryptMessage(User* user, const QString& device, const QByteArray& message);
diff --git a/lib/database.cpp b/lib/database.cpp
index 70dc1b9b..ad653073 100644
--- a/lib/database.cpp
+++ b/lib/database.cpp
@@ -85,7 +85,7 @@ void Database::migrateTo1()
execute(QStringLiteral("CREATE TABLE accounts (pickle TEXT);"));
execute(QStringLiteral("CREATE TABLE olm_sessions (senderKey TEXT, sessionId TEXT, pickle TEXT);"));
execute(QStringLiteral("CREATE TABLE inbound_megolm_sessions (roomId TEXT, senderKey TEXT, sessionId TEXT, pickle TEXT);"));
- execute(QStringLiteral("CREATE TABLE outbound_megolm_sessions (roomId TEXT, senderKey TEXT, sessionId TEXT, pickle TEXT);"));
+ execute(QStringLiteral("CREATE TABLE outbound_megolm_sessions (roomId TEXT, sessionId TEXT, pickle TEXT, creationTime TEXT, messageCount INTEGER);"));
execute(QStringLiteral("CREATE TABLE group_session_record_index (roomId TEXT, sessionId TEXT, i INTEGER, eventId TEXT, ts INTEGER);"));
execute(QStringLiteral("CREATE TABLE tracked_users (matrixId TEXT);"));
execute(QStringLiteral("CREATE TABLE outdated_users (matrixId TEXT);"));
@@ -271,3 +271,42 @@ void Database::setOlmSessionLastReceived(const QString& sessionId, const QDateTi
execute(query);
commit();
}
+
+void Database::saveCurrentOutboundMegolmSession(const QString& roomId, const PicklingMode& picklingMode, const QOlmOutboundGroupSessionPtr& session)
+{
+ const auto pickle = session->pickle(picklingMode);
+ if (std::holds_alternative<QByteArray>(pickle)) {
+ auto deleteQuery = prepareQuery(QStringLiteral("DELETE FROM outbound_megolm_sessions WHERE roomId=:roomId AND sessionId=:sessionId;"));
+ deleteQuery.bindValue(":roomId", roomId);
+ deleteQuery.bindValue(":sessionId", session->sessionId());
+
+ auto insertQuery = prepareQuery(QStringLiteral("INSERT INTO outbound_megolm_sessions(roomId, sessionId, pickle, creationTime, messageCount) VALUES(:roomId, :sessionId, :pickle, :creationTime, :messageCount);"));
+ insertQuery.bindValue(":roomId", roomId);
+ insertQuery.bindValue(":sessionId", session->sessionId());
+ insertQuery.bindValue(":pickle", std::get<QByteArray>(pickle));
+ insertQuery.bindValue(":creationTime", session->creationTime());
+ insertQuery.bindValue(":messageCount", session->messageCount());
+
+ transaction();
+ execute(deleteQuery);
+ execute(insertQuery);
+ commit();
+ }
+}
+
+QOlmOutboundGroupSessionPtr Database::loadCurrentOutboundMegolmSession(const QString& roomId, const PicklingMode& picklingMode)
+{
+ auto query = prepareQuery(QStringLiteral("SELECT * FROM outbound_megolm_sessions WHERE roomId=:roomId ORDER BY creationTime DESC;"));
+ query.bindValue(":roomId", roomId);
+ execute(query);
+ if (query.next()) {
+ auto sessionResult = QOlmOutboundGroupSession::unpickle(query.value("pickle").toByteArray(), picklingMode);
+ if (std::holds_alternative<QOlmOutboundGroupSessionPtr>(sessionResult)) {
+ auto session = std::move(std::get<QOlmOutboundGroupSessionPtr>(sessionResult));
+ session->setCreationTime(query.value("creationTime").toDateTime());
+ session->setMessageCount(query.value("messageCount").toInt());
+ return session;
+ }
+ }
+ return nullptr;
+}
diff --git a/lib/database.h b/lib/database.h
index cf241dbc..dccfb770 100644
--- a/lib/database.h
+++ b/lib/database.h
@@ -8,6 +8,7 @@
#include <QtCore/QVector>
#include "e2ee/e2ee.h"
+#include "e2ee/qolmoutboundsession.h"
namespace Quotient {
class QUOTIENT_API Database : public QObject
@@ -35,6 +36,8 @@ public:
std::pair<QString, qint64> groupSessionIndexRecord(const QString& roomId, const QString& sessionId, qint64 index);
void clearRoomData(const QString& roomId);
void setOlmSessionLastReceived(const QString& sessionId, const QDateTime& timestamp);
+ QOlmOutboundGroupSessionPtr loadCurrentOutboundMegolmSession(const QString& roomId, const PicklingMode& picklingMode);
+ void saveCurrentOutboundMegolmSession(const QString& roomId, const PicklingMode& picklingMode, const QOlmOutboundGroupSessionPtr& data);
private:
void migrateTo1();
diff --git a/lib/e2ee/qolmoutboundsession.cpp b/lib/e2ee/qolmoutboundsession.cpp
index da32417b..8852bcf3 100644
--- a/lib/e2ee/qolmoutboundsession.cpp
+++ b/lib/e2ee/qolmoutboundsession.cpp
@@ -61,13 +61,13 @@ std::variant<QByteArray, QOlmError> QOlmOutboundGroupSession::pickle(const Pickl
return pickledBuf;
}
-std::variant<std::unique_ptr<QOlmOutboundGroupSession>, QOlmError> QOlmOutboundGroupSession::unpickle(QByteArray &pickled, const PicklingMode &mode)
+std::variant<std::unique_ptr<QOlmOutboundGroupSession>, QOlmError> QOlmOutboundGroupSession::unpickle(const QByteArray &pickled, const PicklingMode &mode)
{
QByteArray pickledBuf = pickled;
auto *olmOutboundGroupSession = olm_outbound_group_session(new uint8_t[olm_outbound_group_session_size()]);
QByteArray key = toKey(mode);
const auto error = olm_unpickle_outbound_group_session(olmOutboundGroupSession, key.data(), key.length(),
- pickled.data(), pickled.length());
+ pickledBuf.data(), pickledBuf.length());
if (error == olm_error()) {
return lastError(olmOutboundGroupSession);
}
@@ -123,3 +123,23 @@ std::variant<QByteArray, QOlmError> QOlmOutboundGroupSession::sessionKey() const
}
return keyBuffer;
}
+
+int QOlmOutboundGroupSession::messageCount() const
+{
+ return m_messageCount;
+}
+
+void QOlmOutboundGroupSession::setMessageCount(int messageCount)
+{
+ m_messageCount = messageCount;
+}
+
+QDateTime QOlmOutboundGroupSession::creationTime() const
+{
+ return m_creationTime;
+}
+
+void QOlmOutboundGroupSession::setCreationTime(const QDateTime& creationTime)
+{
+ m_creationTime = creationTime;
+}
diff --git a/lib/e2ee/qolmoutboundsession.h b/lib/e2ee/qolmoutboundsession.h
index 32ba2b3b..10ca35c0 100644
--- a/lib/e2ee/qolmoutboundsession.h
+++ b/lib/e2ee/qolmoutboundsession.h
@@ -25,7 +25,7 @@ public:
//! Deserialises from encrypted Base64 that was previously obtained by
//! pickling a `QOlmOutboundGroupSession`.
static std::variant<std::unique_ptr<QOlmOutboundGroupSession>, QOlmError>
- unpickle(QByteArray& pickled, const PicklingMode& mode);
+ unpickle(const QByteArray& pickled, const PicklingMode& mode);
//! Encrypts a plaintext message using the session.
std::variant<QByteArray, QOlmError> encrypt(const QString &plaintext);
@@ -44,8 +44,16 @@ public:
//! ratchet key that will be used for the next message.
std::variant<QByteArray, QOlmError> sessionKey() const;
QOlmOutboundGroupSession(OlmOutboundGroupSession *groupSession);
+
+ int messageCount() const;
+ void setMessageCount(int messageCount);
+
+ QDateTime creationTime() const;
+ void setCreationTime(const QDateTime& creationTime);
private:
OlmOutboundGroupSession *m_groupSession;
+ int m_messageCount = 0;
+ QDateTime m_creationTime = QDateTime::currentDateTime();
};
using QOlmOutboundGroupSessionPtr = std::unique_ptr<QOlmOutboundGroupSession>;
diff --git a/lib/room.cpp b/lib/room.cpp
index 125eae9f..e4f6afef 100644
--- a/lib/room.cpp
+++ b/lib/room.cpp
@@ -342,10 +342,8 @@ public:
#ifdef Quotient_E2EE_ENABLED
// A map from (senderKey, sessionId) to InboundGroupSession
UnorderedMap<std::pair<QString, QString>, QOlmInboundGroupSessionPtr> groupSessions;
- int currentMegolmSessionMessageCount = 0;
- //TODO save this to database
- unsigned long long currentMegolmSessionCreationTimestamp = 0;
- std::unique_ptr<QOlmOutboundGroupSession> currentOutboundMegolmSession = nullptr;
+
+ QOlmOutboundGroupSessionPtr currentOutboundMegolmSession = nullptr;
bool addInboundGroupSession(QString senderKey, QString sessionId,
QString sessionKey, QString ed25519Key)
@@ -376,9 +374,9 @@ public:
{
auto groupSessionIt = groupSessions.find({ senderKey, sessionId });
if (groupSessionIt == groupSessions.end()) {
- // qCWarning(E2EE) << "Unable to decrypt event" << eventId
- // << "The sender's device has not sent us the keys for "
- // "this message";
+ qCWarning(E2EE) << "Unable to decrypt event" << eventId
+ << "The sender's device has not sent us the keys for "
+ "this message" << senderKey << sessionId;
return QString();
}
auto& senderSession = groupSessionIt->second;
@@ -406,7 +404,7 @@ public:
if (!q->usesEncryption()) {
return false;
}
- return currentMegolmSessionMessageCount >= rotationMessageCount() || (currentMegolmSessionCreationTimestamp + rotationInterval()) < QDateTime::currentMSecsSinceEpoch();
+ return currentOutboundMegolmSession->messageCount() >= rotationMessageCount() || currentOutboundMegolmSession->creationTime().addMSecs(rotationInterval()) < QDateTime::currentDateTime();
}
bool hasValidMegolmSession() const
@@ -437,9 +435,7 @@ public:
void createMegolmSession() {
qCDebug(E2EE) << "Creating new outbound megolm session for room " << q->id();
currentOutboundMegolmSession = QOlmOutboundGroupSession::create();
- currentMegolmSessionMessageCount = 0;
- currentMegolmSessionCreationTimestamp = QDateTime::currentMSecsSinceEpoch();
- //TODO store megolm session to database
+ connection->saveCurrentOutboundMegolmSession(q, currentOutboundMegolmSession);
}
std::unique_ptr<EncryptedEvent> payloadForUserDevice(User* user, const QString& device, const QByteArray& sessionId, const QByteArray& sessionKey)
@@ -467,7 +463,7 @@ public:
void sendRoomKeyToDevices(const QByteArray& sessionId, const QByteArray& sessionKey)
{
- qWarning() << "Sending room key to devices" << sessionId, sessionKey.toHex();
+ qCDebug(E2EE) << "Sending room key to devices" << sessionId, sessionKey.toHex();
QHash<QString, QHash<QString, QString>> hash;
for (const auto& user : q->users()) {
QHash<QString, QString> u;
@@ -484,7 +480,7 @@ public:
auto job = connection->callApi<ClaimKeysJob>(hash);
connect(job, &BaseJob::success, q, [job, this, sessionId, sessionKey](){
Connection::UsersToDevicesToEvents usersToDevicesToEvents;
- auto data = job->jsonData();
+ const auto data = job->jsonData();
for(const auto &user : q->users()) {
for(const auto &device : connection->devicesForUser(user)) {
const auto recipientCurveKey = connection->curveKeyForUserDevice(user->id(), device);
@@ -518,8 +514,6 @@ public:
});
}
- //TODO load outbound megolm sessions from database
-
void sendMegolmSession() {
// Save the session to this device
const auto sessionId = currentOutboundMegolmSession->sessionId();
@@ -573,14 +567,16 @@ Room::Room(Connection* connection, QString id, JoinState initialJoinState)
}
});
d->groupSessions = connection->loadRoomMegolmSessions(this);
- //TODO load outbound session
+ d->currentOutboundMegolmSession = connection->loadCurrentOutboundMegolmSession(this);
+ if (d->shouldRotateMegolmSession()) {
+ d->currentOutboundMegolmSession = nullptr;
+ }
connect(this, &Room::userRemoved, this, [this](){
if (!usesEncryption()) {
return;
}
d->currentOutboundMegolmSession = nullptr;
qCDebug(E2EE) << "Invalidating current megolm session because user left";
- //TODO save old session probably
});
@@ -2060,6 +2056,8 @@ QString Room::Private::sendEvent(RoomEventPtr&& event)
//TODO check if we increment the sent message count
event->setRoomId(id);
const auto encrypted = currentOutboundMegolmSession->encrypt(QJsonDocument(event->fullJson()).toJson());
+ currentOutboundMegolmSession->setMessageCount(currentOutboundMegolmSession->messageCount() + 1);
+ connection->saveCurrentOutboundMegolmSession(q, currentOutboundMegolmSession);
if(std::holds_alternative<QOlmError>(encrypted)) {
//TODO something
qWarning(E2EE) << "Error encrypting message" << std::get<QOlmError>(encrypted);
@@ -2070,7 +2068,6 @@ QString Room::Private::sendEvent(RoomEventPtr&& event)
encryptedEvent->setRoomId(id);
encryptedEvent->setSender(connection->userId());
event->setTransactionId(encryptedEvent->transactionId());
- currentMegolmSessionMessageCount++;
// We show the unencrypted event locally while pending. The echo check will throw the encrypted version out
addAsPending(std::move(event));
return doSendEvent(encryptedEvent, true);