aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/connection.cpp12
-rw-r--r--lib/connection.h3
-rw-r--r--lib/database.cpp18
-rw-r--r--lib/database.h3
-rw-r--r--lib/events/encryptedfile.cpp4
-rw-r--r--lib/room.cpp27
6 files changed, 46 insertions, 21 deletions
diff --git a/lib/connection.cpp b/lib/connection.cpp
index 2a1b39f9..82046d53 100644
--- a/lib/connection.cpp
+++ b/lib/connection.cpp
@@ -33,6 +33,7 @@
#include "jobs/downloadfilejob.h"
#include "jobs/mediathumbnailjob.h"
#include "jobs/syncjob.h"
+#include <variant>
#ifdef Quotient_E2EE_ENABLED
# include "database.h"
@@ -2246,21 +2247,24 @@ 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 do we need to save the olm session after sending a message?
const auto& curveKey = curveKeyForUserDevice(user->id(), device);
QOlmMessage::Type type = d->olmSessions[curveKey][0]->encryptMessageType();
auto result = d->olmSessions[curveKey][0]->encrypt(message);
+ auto pickle = d->olmSessions[curveKey][0]->pickle(picklingMode());
+ if (std::holds_alternative<QByteArray>(pickle)) {
+ database()->updateOlmSession(curveKey, d->olmSessions[curveKey][0]->sessionId(), std::get<QByteArray>(pickle));
+ } else {
+ qCWarning(E2EE) << "Failed to pickle olm session.";
+ }
return qMakePair(type, result.toCiphertext());
}
-//TODO be more consistent with curveKey and identityKey
void Connection::createOlmSession(const QString& theirIdentityKey, const QString& theirOneTimeKey)
{
auto session = QOlmSession::createOutboundSession(olmAccount(), theirIdentityKey, theirOneTimeKey);
if (std::holds_alternative<QOlmError>(session)) {
- //TODO something
qCWarning(E2EE) << "Failed to create olm session for " << theirIdentityKey << std::get<QOlmError>(session);
+ return;
}
d->saveSession(std::get<std::unique_ptr<QOlmSession>>(session), theirIdentityKey);
d->olmSessions[theirIdentityKey].push_back(std::move(std::get<std::unique_ptr<QOlmSession>>(session)));
diff --git a/lib/connection.h b/lib/connection.h
index 8bed55da..5a1f1e5c 100644
--- a/lib/connection.h
+++ b/lib/connection.h
@@ -329,8 +329,7 @@ public:
void saveCurrentOutboundMegolmSession(Room *room, const QOlmOutboundGroupSessionPtr& data);
- //This currently assumes that an olm session with (user, device) exists
- //TODO make this return an event?
+ //This assumes that an olm session with (user, device) exists
QPair<QOlmMessage::Type, QByteArray> olmEncryptMessage(User* user, const QString& device, const QByteArray& message);
void createOlmSession(const QString& theirIdentityKey, const QString& theirOneTimeKey);
#endif // Quotient_E2EE_ENABLED
diff --git a/lib/database.cpp b/lib/database.cpp
index 4a28fd4c..74b56a02 100644
--- a/lib/database.cpp
+++ b/lib/database.cpp
@@ -9,6 +9,7 @@
#include <QtCore/QStandardPaths>
#include <QtCore/QDebug>
#include <QtCore/QDir>
+#include <ctime>
#include "e2ee/e2ee.h"
#include "e2ee/qolmsession.h"
@@ -182,7 +183,7 @@ void Database::saveOlmSession(const QString& senderKey, const QString& sessionId
UnorderedMap<QString, std::vector<QOlmSessionPtr>> Database::loadOlmSessions(const PicklingMode& picklingMode)
{
- auto query = prepareQuery(QStringLiteral("SELECT * FROM olm_sessions;"));
+ auto query = prepareQuery(QStringLiteral("SELECT * FROM olm_sessions ORDER BY lastReceived DESC;"));
transaction();
execute(query);
commit();
@@ -338,7 +339,6 @@ QOlmOutboundGroupSessionPtr Database::loadCurrentOutboundMegolmSession(const QSt
void Database::setDevicesReceivedKey(const QString& roomId, QHash<User *, QStringList> devices, const QString& sessionId, int index)
{
- //TODO this better
auto connection = dynamic_cast<Connection *>(parent());
transaction();
for (const auto& user : devices.keys()) {
@@ -360,7 +360,7 @@ QHash<QString, QStringList> Database::devicesWithoutKey(Room* room, const QStrin
{
auto connection = dynamic_cast<Connection *>(parent());
QHash<QString, QStringList> devices;
- for (const auto& user : room->users()) {//TODO does this include invited & left?
+ for (const auto& user : room->users()) {
devices[user->id()] = connection->devicesForUser(user);
}
@@ -375,3 +375,15 @@ QHash<QString, QStringList> Database::devicesWithoutKey(Room* room, const QStrin
}
return devices;
}
+
+void Database::updateOlmSession(const QString& senderKey, const QString& sessionId, const QByteArray& pickle)
+{
+ auto query = prepareQuery(QStringLiteral("UPDATE olm_sessions SET pickle=:pickle WHERE senderKey=:senderKey AND sessionId=:sessionId;"));
+ query.bindValue(":pickle", pickle);
+ query.bindValue(":senderKey", senderKey);
+ query.bindValue(":sessionId", sessionId);
+ transaction();
+ execute(query);
+ commit();
+}
+
diff --git a/lib/database.h b/lib/database.h
index 30f2f203..8ddd7b6d 100644
--- a/lib/database.h
+++ b/lib/database.h
@@ -32,7 +32,7 @@ public:
QByteArray accountPickle();
void setAccountPickle(const QByteArray &pickle);
void clear();
- void saveOlmSession(const QString& senderKey, const QString& sessionId, const QByteArray &pickle, const QDateTime& timestamp);
+ void saveOlmSession(const QString& senderKey, const QString& sessionId, const QByteArray& pickle, const QDateTime& timestamp);
UnorderedMap<QString, std::vector<QOlmSessionPtr>> loadOlmSessions(const PicklingMode& picklingMode);
UnorderedMap<QString, QOlmInboundGroupSessionPtr> loadMegolmSessions(const QString& roomId, const PicklingMode& picklingMode);
void saveMegolmSession(const QString& roomId, const QString& sessionId, const QByteArray& pickle, const QString& senderId, const QString& olmSessionId);
@@ -42,6 +42,7 @@ public:
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);
+ void updateOlmSession(const QString& senderKey, const QString& sessionId, const QByteArray& pickle);
// Returns a map User -> [Device] that have not received key yet
QHash<QString, QStringList> devicesWithoutKey(Room* room, const QString &sessionId);
diff --git a/lib/events/encryptedfile.cpp b/lib/events/encryptedfile.cpp
index e90be428..bb4e26c7 100644
--- a/lib/events/encryptedfile.cpp
+++ b/lib/events/encryptedfile.cpp
@@ -56,6 +56,7 @@ QByteArray EncryptedFile::decryptFile(const QByteArray& ciphertext) const
std::pair<EncryptedFile, QByteArray> EncryptedFile::encryptFile(const QByteArray &plainText)
{
+#ifdef Quotient_E2EE_ENABLED
QByteArray k = getRandom(32);
auto kBase64 = k.toBase64();
QByteArray iv = getRandom(16);
@@ -73,6 +74,9 @@ std::pair<EncryptedFile, QByteArray> EncryptedFile::encryptFile(const QByteArray
auto ivBase64 = iv.toBase64();
EncryptedFile file = {{}, key, ivBase64.left(ivBase64.indexOf('=')), {{QStringLiteral("sha256"), hash.left(hash.indexOf('='))}}, "v2"_ls};
return {file, cipherText};
+#else
+ return {{}, {}};
+#endif
}
void JsonObjectConverter<EncryptedFile>::dumpTo(QJsonObject& jo,
diff --git a/lib/room.cpp b/lib/room.cpp
index a42b7184..0ca8f648 100644
--- a/lib/room.cpp
+++ b/lib/room.cpp
@@ -449,8 +449,8 @@ public:
const auto sessionKey = currentOutboundMegolmSession->sessionKey();
if(std::holds_alternative<QOlmError>(sessionKey)) {
- qCWarning(E2EE) << "Session error";
- //TODO something
+ qCWarning(E2EE) << "Failed to load key for new megolm session";
+ return;
}
addInboundGroupSession(q->connection()->olmAccount()->identityKeys().curve25519, currentOutboundMegolmSession->sessionId(), std::get<QByteArray>(sessionKey), QString(connection->olmAccount()->identityKeys().ed25519));
}
@@ -459,7 +459,6 @@ public:
{
// Noisy but nice for debugging
//qCDebug(E2EE) << "Creating the payload for" << user->id() << device << sessionId << sessionKey.toHex();
- //TODO: store {user->id(), device, sessionId, theirIdentityKey}; required for key requests
const auto event = makeEvent<RoomKeyEvent>("m.megolm.v1.aes-sha2", q->id(), sessionId, sessionKey, q->localUser()->id());
QJsonObject payloadJson = event->fullJson();
payloadJson["recipient"] = user->id();
@@ -504,6 +503,9 @@ public:
hash[user->id()] = u;
}
}
+ if (hash.isEmpty()) {
+ return;
+ }
auto job = connection->callApi<ClaimKeysJob>(hash);
connect(job, &BaseJob::success, q, [job, this, sessionId, sessionKey, devices, index](){
Connection::UsersToDevicesToEvents usersToDevicesToEvents;
@@ -525,7 +527,6 @@ public:
signedData.remove("signatures");
auto signatureMatch = QOlmUtility().ed25519Verify(connection->edKeyForUserDevice(user->id(), device).toLatin1(), QJsonDocument(signedData).toJson(QJsonDocument::Compact), signature);
if (std::holds_alternative<QOlmError>(signatureMatch)) {
- //TODO i think there are more failed signature checks than expected. Investigate
qCWarning(E2EE) << "Failed to verify one-time-key signature for" << user->id() << device << ". Skipping this device.";
continue;
} else {
@@ -535,8 +536,10 @@ public:
usersToDevicesToEvents[user->id()][device] = payloadForUserDevice(user, device, sessionId, sessionKey);
}
}
- connection->sendToDevices("m.room.encrypted", usersToDevicesToEvents);
- connection->database()->setDevicesReceivedKey(q->id(), devices, sessionId, index);
+ if (!usersToDevicesToEvents.empty()) {
+ connection->sendToDevices("m.room.encrypted", usersToDevicesToEvents);
+ connection->database()->setDevicesReceivedKey(q->id(), devices, sessionId, index);
+ }
});
}
@@ -545,8 +548,8 @@ public:
const auto sessionId = currentOutboundMegolmSession->sessionId();
const auto _sessionKey = currentOutboundMegolmSession->sessionKey();
if(std::holds_alternative<QOlmError>(_sessionKey)) {
- qCWarning(E2EE) << "Session error";
- //TODO something
+ qCWarning(E2EE) << "Error loading session key";
+ return;
}
const auto sessionKey = std::get<QByteArray>(_sessionKey);
const auto senderKey = q->connection()->olmAccount()->identityKeys().curve25519;
@@ -581,7 +584,6 @@ Room::Room(Connection* connection, QString id, JoinState initialJoinState)
connect(this, &Room::userAdded, this, [this, connection](){
if(usesEncryption()) {
connection->encryptionUpdate(this);
- //TODO key at currentIndex to all user devices
}
});
d->groupSessions = connection->loadRoomMegolmSessions(this);
@@ -2086,18 +2088,20 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent)
const RoomEvent* _event = pEvent;
if (q->usesEncryption()) {
+#ifndef Quotient_E2EE_ENABLED
+ qWarning() << "This build of libQuotient does not support E2EE.";
+ return {};
+#else
if (!hasValidMegolmSession() || shouldRotateMegolmSession()) {
createMegolmSession();
}
const auto devicesWithoutKey = getDevicesWithoutKey();
sendMegolmSession(devicesWithoutKey);
- //TODO check if we increment the sent message count
const auto encrypted = currentOutboundMegolmSession->encrypt(QJsonDocument(pEvent->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);
return {};
}
@@ -2110,6 +2114,7 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent)
}
// We show the unencrypted event locally while pending. The echo check will throw the encrypted version out
_event = encryptedEvent;
+#endif
}
if (auto call =