diff options
-rw-r--r-- | lib/connection.cpp | 11 | ||||
-rw-r--r-- | lib/connection.h | 5 | ||||
-rw-r--r-- | lib/database.cpp | 41 | ||||
-rw-r--r-- | lib/database.h | 3 | ||||
-rw-r--r-- | lib/e2ee/qolmoutboundsession.cpp | 22 | ||||
-rw-r--r-- | lib/e2ee/qolmoutboundsession.h | 9 | ||||
-rw-r--r-- | lib/room.cpp | 23 |
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); |