aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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.cpp22
-rw-r--r--lib/e2ee/qolmoutboundsession.h9
-rw-r--r--lib/room.cpp23
7 files changed, 99 insertions, 15 deletions
diff --git a/lib/connection.cpp b/lib/connection.cpp
index a66a4168..b11ec731 100644
--- a/lib/connection.cpp
+++ b/lib/connection.cpp
@@ -2247,7 +2247,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);
@@ -2266,4 +2265,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 afa4a657..8bed55da 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)
@@ -324,6 +325,10 @@ public:
const QOlmInboundGroupSession& session);
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 e2e7acc9..8cb3a9d1 100644
--- a/lib/database.cpp
+++ b/lib/database.cpp
@@ -86,7 +86,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);"));
@@ -292,3 +292,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 08fe49f3..751ebd1d 100644
--- a/lib/database.h
+++ b/lib/database.h
@@ -8,6 +8,7 @@
#include <QtCore/QVector>
#include "e2ee/e2ee.h"
+
namespace Quotient {
class QUOTIENT_API Database : public QObject
{
@@ -34,6 +35,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 96bad344..10b0c4de 100644
--- a/lib/e2ee/qolmoutboundsession.cpp
+++ b/lib/e2ee/qolmoutboundsession.cpp
@@ -66,7 +66,7 @@ QOlmExpected<QOlmOutboundGroupSessionPtr> QOlmOutboundGroupSession::unpickle(QBy
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 @@ QOlmExpected<QByteArray> 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 8058bbb1..56b25974 100644
--- a/lib/e2ee/qolmoutboundsession.h
+++ b/lib/e2ee/qolmoutboundsession.h
@@ -26,6 +26,7 @@ public:
//! pickling a `QOlmOutboundGroupSession`.
static QOlmExpected<QOlmOutboundGroupSessionPtr> unpickle(
QByteArray& pickled, const PicklingMode& mode);
+
//! Encrypts a plaintext message using the session.
QOlmExpected<QByteArray> encrypt(const QString& plaintext);
@@ -44,8 +45,16 @@ public:
//! ratchet key that will be used for the next message.
QOlmExpected<QByteArray> 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();
};
} // namespace Quotient
diff --git a/lib/room.cpp b/lib/room.cpp
index e8b63235..9e2bd7dd 100644
--- a/lib/room.cpp
+++ b/lib/room.cpp
@@ -344,7 +344,7 @@ public:
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 sessionId, QByteArray sessionKey,
const QString& senderId,
@@ -415,7 +415,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
@@ -446,9 +446,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)
@@ -476,7 +474,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;
@@ -493,7 +491,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);
@@ -527,8 +525,6 @@ public:
});
}
- //TODO load outbound megolm sessions from database
-
void sendMegolmSession() {
// Save the session to this device
const auto sessionId = currentOutboundMegolmSession->sessionId();
@@ -577,14 +573,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
});
@@ -2074,6 +2072,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);
@@ -2084,7 +2084,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);