diff options
Diffstat (limited to 'lib')
-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 | 24 | ||||
-rw-r--r-- | lib/e2ee/qolmoutboundsession.h | 10 | ||||
-rw-r--r-- | lib/room.cpp | 33 |
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); |