aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/connection.cpp108
-rw-r--r--lib/connection.h2
-rw-r--r--lib/e2ee/qolminboundsession.cpp2
-rw-r--r--lib/encryptionmanager.cpp149
-rw-r--r--lib/encryptionmanager.h33
-rw-r--r--lib/events/encryptedfile.cpp27
-rw-r--r--lib/events/encryptedfile.h6
-rw-r--r--lib/jobs/downloadfilejob.cpp5
-rw-r--r--lib/mxcreply.cpp3
9 files changed, 128 insertions, 207 deletions
diff --git a/lib/connection.cpp b/lib/connection.cpp
index 58e3a9f8..1a1b284d 100644
--- a/lib/connection.cpp
+++ b/lib/connection.cpp
@@ -37,7 +37,6 @@
#ifdef Quotient_E2EE_ENABLED
# include "e2ee/qolmaccount.h"
# include "e2ee/qolmutils.h"
-# include "encryptionmanager.h"
# include "database.h"
#if QT_VERSION_MAJOR >= 6
@@ -117,6 +116,10 @@ public:
bool encryptionUpdateRequired = false;
PicklingMode picklingMode = Unencrypted {};
Database *database = nullptr;
+
+ // A map from SenderKey to vector of InboundSession
+ UnorderedMap<QString, std::vector<QOlmSessionPtr>> olmSessions;
+
#endif
GetCapabilitiesJob* capabilitiesJob = nullptr;
@@ -127,7 +130,6 @@ public:
#ifdef Quotient_E2EE_ENABLED
std::unique_ptr<QOlmAccount> olmAccount;
bool isUploadingKeys = false;
- EncryptionManager *encryptionManager;
#endif // Quotient_E2EE_ENABLED
QPointer<GetWellknownJob> resolverJob = nullptr;
@@ -201,6 +203,85 @@ public:
return q->stateCacheDir().filePath("state.json");
}
+#ifdef Quotient_E2EE_ENABLED
+ void loadSessions() {
+ olmSessions = q->database()->loadOlmSessions(q->picklingMode());
+ }
+ void saveSession(QOlmSessionPtr& session, const QString &senderKey) {
+ auto pickleResult = session->pickle(q->picklingMode());
+ if (std::holds_alternative<QOlmError>(pickleResult)) {
+ qCWarning(E2EE) << "Failed to pickle olm session. Error" << std::get<QOlmError>(pickleResult);
+ return;
+ }
+ q->database()->saveOlmSession(senderKey, session->sessionId(), std::get<QByteArray>(pickleResult));
+ }
+ QString sessionDecryptPrekey(const QOlmMessage& message, const QString &senderKey, std::unique_ptr<QOlmAccount>& olmAccount)
+ {
+ Q_ASSERT(message.type() == QOlmMessage::PreKey);
+ for(auto& session : olmSessions[senderKey]) {
+ const auto matches = session->matchesInboundSessionFrom(senderKey, message);
+ if(std::holds_alternative<bool>(matches) && std::get<bool>(matches)) {
+ qCDebug(E2EE) << "Found inbound session";
+ const auto result = session->decrypt(message);
+ if(std::holds_alternative<QString>(result)) {
+ return std::get<QString>(result);
+ } else {
+ qCDebug(E2EE) << "Failed to decrypt prekey message";
+ return {};
+ }
+ }
+ }
+ qCDebug(E2EE) << "Creating new inbound session";
+ auto newSessionResult = olmAccount->createInboundSessionFrom(senderKey.toUtf8(), message);
+ if(std::holds_alternative<QOlmError>(newSessionResult)) {
+ qCWarning(E2EE) << "Failed to create inbound session for" << senderKey << std::get<QOlmError>(newSessionResult);
+ return {};
+ }
+ auto newSession = std::move(std::get<QOlmSessionPtr>(newSessionResult));
+ auto error = olmAccount->removeOneTimeKeys(newSession);
+ if (error) {
+ qWarning(E2EE) << "Failed to remove one time key for session" << newSession->sessionId();
+ }
+ const auto result = newSession->decrypt(message);
+ saveSession(newSession, senderKey);
+ olmSessions[senderKey].push_back(std::move(newSession));
+ if(std::holds_alternative<QString>(result)) {
+ return std::get<QString>(result);
+ } else {
+ qCDebug(E2EE) << "Failed to decrypt prekey message with new session";
+ return {};
+ }
+ }
+ QString sessionDecryptGeneral(const QOlmMessage& message, const QString &senderKey)
+ {
+ Q_ASSERT(message.type() == QOlmMessage::General);
+ for(auto& session : olmSessions[senderKey]) {
+ const auto result = session->decrypt(message);
+ if(std::holds_alternative<QString>(result)) {
+ return std::get<QString>(result);
+ }
+ }
+ qCWarning(E2EE) << "Failed to decrypt message";
+ return {};
+ }
+
+ QString sessionDecryptMessage(
+ const QJsonObject& personalCipherObject, const QByteArray& senderKey, std::unique_ptr<QOlmAccount>& account)
+ {
+ QString decrypted;
+ int type = personalCipherObject.value(TypeKeyL).toInt(-1);
+ QByteArray body = personalCipherObject.value(BodyKeyL).toString().toLatin1();
+ if (type == 0) {
+ QOlmMessage preKeyMessage(body, QOlmMessage::PreKey);
+ decrypted = sessionDecryptPrekey(preKeyMessage, senderKey, account);
+ } else if (type == 1) {
+ QOlmMessage message(body, QOlmMessage::General);
+ decrypted = sessionDecryptGeneral(message, senderKey);
+ }
+ return decrypted;
+ }
+#endif
+
EventPtr sessionDecryptMessage(const EncryptedEvent& encryptedEvent)
{
#ifndef Quotient_E2EE_ENABLED
@@ -217,7 +298,7 @@ public:
qCDebug(E2EE) << "Encrypted event is not for the current device";
return {};
}
- const auto decrypted = encryptionManager->sessionDecryptMessage(
+ const auto decrypted = sessionDecryptMessage(
personalCipherObject, encryptedEvent.senderKey().toLatin1(), olmAccount);
if (decrypted.isEmpty()) {
qCDebug(E2EE) << "Problem with new session from senderKey:"
@@ -443,6 +524,7 @@ void Connection::Private::loginToServer(LoginArgTs&&... loginArgs)
#ifndef Quotient_E2EE_ENABLED
qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off.";
#endif // Quotient_E2EE_ENABLED
+ database->clear();
});
connect(loginJob, &BaseJob::failure, q, [this, loginJob] {
emit q->loginError(loginJob->errorString(), loginJob->rawDataSample());
@@ -493,13 +575,15 @@ void Connection::Private::completeSetup(const QString& mxId)
picklingMode = Encrypted { job.binaryData() };
}
+ database = new Database(data->userId(), q);
+
// init olmAccount
olmAccount = std::make_unique<QOlmAccount>(data->userId(), data->deviceId(), q);
connect(olmAccount.get(), &QOlmAccount::needsSave, q, &Connection::saveOlmAccount);
- database = new Database(data->userId(), q);
-
- encryptionManager = new EncryptionManager(q);
+#ifdef Quotient_E2EE_ENABLED
+ loadSessions();
+#endif
if (database->accountPickle().isEmpty()) {
// create new account and save unpickle data
@@ -2019,18 +2103,6 @@ void Connection::saveOlmAccount()
#endif
}
-QString Connection::e2eeDataDir() const
-{
- auto safeUserId = userId();
- safeUserId.replace(':', '_');
- const QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) % '/'
- % safeUserId % '/';
- QDir dir;
- if (!dir.exists(path))
- dir.mkpath(path);
- return path;
-}
-
#ifdef Quotient_E2EE_ENABLED
QJsonObject Connection::decryptNotification(const QJsonObject &notification)
{
diff --git a/lib/connection.h b/lib/connection.h
index 93ee496e..8dec2a0c 100644
--- a/lib/connection.h
+++ b/lib/connection.h
@@ -401,8 +401,6 @@ public:
bool lazyLoading() const;
void setLazyLoading(bool newValue);
- QString e2eeDataDir() const;
-
/*! Start a pre-created job object on this connection */
Q_INVOKABLE BaseJob* run(BaseJob* job,
RunningPolicy runningPolicy = ForegroundRequest);
diff --git a/lib/e2ee/qolminboundsession.cpp b/lib/e2ee/qolminboundsession.cpp
index 9bc80eef..2e9cc716 100644
--- a/lib/e2ee/qolminboundsession.cpp
+++ b/lib/e2ee/qolminboundsession.cpp
@@ -4,6 +4,8 @@
#include "e2ee/qolminboundsession.h"
#include <iostream>
+#include <cstring>
+
using namespace Quotient;
QOlmError lastError(OlmInboundGroupSession *session) {
diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp
deleted file mode 100644
index abdcdcee..00000000
--- a/lib/encryptionmanager.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-// SPDX-FileCopyrightText: 2019 Alexey Andreyev <aa13q@ya.ru>
-// SPDX-FileCopyrightText: 2019 Kitsune Ral <Kitsune-Ral@users.sf.net>
-// SPDX-License-Identifier: LGPL-2.1-or-later
-
-#ifdef Quotient_E2EE_ENABLED
-#include "encryptionmanager.h"
-
-#include "connection.h"
-#include "events/encryptedfile.h"
-#include "database.h"
-
-#include "csapi/keys.h"
-
-#include <QtCore/QHash>
-#include <QtCore/QStringBuilder>
-#include <QtCore/QCryptographicHash>
-
-#include "e2ee/e2ee.h"
-#include "e2ee/qolmaccount.h"
-#include "e2ee/qolmsession.h"
-#include "e2ee/qolmmessage.h"
-#include "e2ee/qolmerrors.h"
-#include "e2ee/qolmutils.h"
-#include <functional>
-#include <memory>
-
-#include <openssl/evp.h>
-
-using namespace Quotient;
-using std::move;
-
-class EncryptionManager::Private {
-public:
- EncryptionManager* q;
-
- Connection* connection;
-
- // A map from SenderKey to vector of InboundSession
- UnorderedMap<QString, std::vector<QOlmSessionPtr>> sessions;
-
- void loadSessions() {
- sessions = connection->database()->loadOlmSessions(connection->picklingMode());
- }
- void saveSession(QOlmSessionPtr& session, const QString &senderKey) {
- auto pickleResult = session->pickle(connection->picklingMode());
- if (std::holds_alternative<QOlmError>(pickleResult)) {
- qCWarning(E2EE) << "Failed to pickle olm session. Error" << std::get<QOlmError>(pickleResult);
- return;
- }
- connection->database()->saveOlmSession(senderKey, session->sessionId(), std::get<QByteArray>(pickleResult));
- }
- QString sessionDecryptPrekey(const QOlmMessage& message, const QString &senderKey, std::unique_ptr<QOlmAccount>& olmAccount)
- {
- Q_ASSERT(message.type() == QOlmMessage::PreKey);
- for(auto& session : sessions[senderKey]) {
- const auto matches = session->matchesInboundSessionFrom(senderKey, message);
- if(std::holds_alternative<bool>(matches) && std::get<bool>(matches)) {
- qCDebug(E2EE) << "Found inbound session";
- const auto result = session->decrypt(message);
- if(std::holds_alternative<QString>(result)) {
- return std::get<QString>(result);
- } else {
- qCDebug(E2EE) << "Failed to decrypt prekey message";
- return {};
- }
- }
- }
- qCDebug(E2EE) << "Creating new inbound session";
- auto newSessionResult = olmAccount->createInboundSessionFrom(senderKey.toUtf8(), message);
- if(std::holds_alternative<QOlmError>(newSessionResult)) {
- qCWarning(E2EE) << "Failed to create inbound session for" << senderKey << std::get<QOlmError>(newSessionResult);
- return {};
- }
- auto newSession = std::move(std::get<QOlmSessionPtr>(newSessionResult));
- auto error = olmAccount->removeOneTimeKeys(newSession);
- if (error) {
- qWarning(E2EE) << "Failed to remove one time key for session" << newSession->sessionId();
- }
- const auto result = newSession->decrypt(message);
- saveSession(newSession, senderKey);
- sessions[senderKey].push_back(std::move(newSession));
- if(std::holds_alternative<QString>(result)) {
- return std::get<QString>(result);
- } else {
- qCDebug(E2EE) << "Failed to decrypt prekey message with new session";
- return {};
- }
- }
- QString sessionDecryptGeneral(const QOlmMessage& message, const QString &senderKey)
- {
- Q_ASSERT(message.type() == QOlmMessage::General);
- for(auto& session : sessions[senderKey]) {
- const auto result = session->decrypt(message);
- if(std::holds_alternative<QString>(result)) {
- return std::get<QString>(result);
- }
- }
- qCWarning(E2EE) << "Failed to decrypt message";
- return {};
- }
-};
-
-EncryptionManager::EncryptionManager(QObject* parent)
- : QObject(parent)
- , d(std::make_unique<Private>())
-{
- d->q = this;
- d->connection = static_cast<Connection *>(parent);
- d->loadSessions();
-}
-
-EncryptionManager::~EncryptionManager() = default;
-
-QString EncryptionManager::sessionDecryptMessage(
- const QJsonObject& personalCipherObject, const QByteArray& senderKey, std::unique_ptr<QOlmAccount>& account)
-{
- QString decrypted;
- int type = personalCipherObject.value(TypeKeyL).toInt(-1);
- QByteArray body = personalCipherObject.value(BodyKeyL).toString().toLatin1();
- if (type == 0) {
- QOlmMessage preKeyMessage(body, QOlmMessage::PreKey);
- decrypted = d->sessionDecryptPrekey(preKeyMessage, senderKey, account);
- } else if (type == 1) {
- QOlmMessage message(body, QOlmMessage::General);
- decrypted = d->sessionDecryptGeneral(message, senderKey);
- }
- return decrypted;
-}
-
-QByteArray EncryptionManager::decryptFile(const QByteArray &ciphertext, EncryptedFile* file)
-{
- const auto key = QByteArray::fromBase64(file->key.k.replace(QLatin1Char('_'), QLatin1Char('/')).replace(QLatin1Char('-'), QLatin1Char('+')).toLatin1());
- const auto iv = QByteArray::fromBase64(file->iv.toLatin1());
- const auto sha256 = QByteArray::fromBase64(file->hashes["sha256"].toLatin1());
- if(sha256 != QCryptographicHash::hash(ciphertext, QCryptographicHash::Sha256)) {
- qCWarning(E2EE) << "Hash verification failed for file";
- return QByteArray();
- }
- QByteArray plaintext(ciphertext.size(), 0);
- EVP_CIPHER_CTX *ctx;
- int length;
- ctx = EVP_CIPHER_CTX_new();
- EVP_DecryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, (const unsigned char *)key.data(), (const unsigned char *)iv.data());
- EVP_DecryptUpdate(ctx, (unsigned char *)plaintext.data(), &length, (const unsigned char *)ciphertext.data(), ciphertext.size());
- EVP_DecryptFinal_ex(ctx, (unsigned char *)plaintext.data() + length, &length);
- EVP_CIPHER_CTX_free(ctx);
- return plaintext;
-}
-#endif // Quotient_E2EE_ENABLED
diff --git a/lib/encryptionmanager.h b/lib/encryptionmanager.h
deleted file mode 100644
index 96569980..00000000
--- a/lib/encryptionmanager.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// SPDX-FileCopyrightText: 2019 Alexey Andreyev <aa13q@ya.ru>
-// SPDX-License-Identifier: LGPL-2.1-or-later
-
-#ifdef Quotient_E2EE_ENABLED
-#pragma once
-
-#include <QtCore/QObject>
-
-#include <functional>
-#include <memory>
-
-namespace Quotient {
-class Connection;
-class QOlmAccount;
-struct EncryptedFile;
-
-class EncryptionManager : public QObject {
- Q_OBJECT
-
-public:
- explicit EncryptionManager(QObject* parent = nullptr);
- ~EncryptionManager();
- QString sessionDecryptMessage(const QJsonObject& personalCipherObject,
- const QByteArray& senderKey, std::unique_ptr<QOlmAccount>& account);
- static QByteArray decryptFile(const QByteArray &ciphertext, EncryptedFile* encryptedFile);
-
-private:
- class Private;
- std::unique_ptr<Private> d;
-};
-
-} // namespace Quotient
-#endif // Quotient_E2EE_ENABLED
diff --git a/lib/events/encryptedfile.cpp b/lib/events/encryptedfile.cpp
new file mode 100644
index 00000000..5ec344bb
--- /dev/null
+++ b/lib/events/encryptedfile.cpp
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
+//
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#include "encryptedfile.h"
+
+using namespace Quotient;
+
+QByteArray EncryptedFile::decryptFile(const QByteArray &ciphertext) const
+{
+ QString _key = key.k;
+ _key = QByteArray::fromBase64(_key.replace(QLatin1Char('_'), QLatin1Char('/')).replace(QLatin1Char('-'), QLatin1Char('+')).toLatin1());
+ const auto sha256 = QByteArray::fromBase64(hashes["sha256"].toLatin1());
+ if(sha256 != QCryptographicHash::hash(ciphertext, QCryptographicHash::Sha256)) {
+ qCWarning(E2EE) << "Hash verification failed for file";
+ return QByteArray();
+ }
+ QByteArray plaintext(ciphertext.size(), 0);
+ EVP_CIPHER_CTX *ctx;
+ int length;
+ ctx = EVP_CIPHER_CTX_new();
+ EVP_DecryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, (const unsigned char *)_key.data(), (const unsigned char *)iv.toLatin1().data());
+ EVP_DecryptUpdate(ctx, (unsigned char *)plaintext.data(), &length, (const unsigned char *)ciphertext.data(), ciphertext.size());
+ EVP_DecryptFinal_ex(ctx, (unsigned char *)plaintext.data() + length, &length);
+ EVP_CIPHER_CTX_free(ctx);
+ return plaintext;
+}
diff --git a/lib/events/encryptedfile.h b/lib/events/encryptedfile.h
index 24ac9de1..f271d345 100644
--- a/lib/events/encryptedfile.h
+++ b/lib/events/encryptedfile.h
@@ -5,6 +5,10 @@
#pragma once
#include "converters.h"
+#include "logging.h"
+
+#include <openssl/evp.h>
+#include <QtCore/QCryptographicHash>
namespace Quotient {
/**
@@ -44,6 +48,8 @@ public:
QString iv;
QHash<QString, QString> hashes;
QString v;
+
+ QByteArray decryptFile(const QByteArray &ciphertext) const;
};
template <>
diff --git a/lib/jobs/downloadfilejob.cpp b/lib/jobs/downloadfilejob.cpp
index 2eea9d59..c5280770 100644
--- a/lib/jobs/downloadfilejob.cpp
+++ b/lib/jobs/downloadfilejob.cpp
@@ -9,7 +9,6 @@
#ifdef Quotient_E2EE_ENABLED
# include <QtCore/QCryptographicHash>
-# include "encryptionmanager.h"
# include "events/encryptedfile.h"
#endif
@@ -126,7 +125,7 @@ BaseJob::Status DownloadFileJob::prepareResult()
QByteArray encrypted = d->tempFile->readAll();
EncryptedFile file = *d->encryptedFile;
- auto decrypted = EncryptionManager::decryptFile(encrypted, &file);
+ auto decrypted = file.decryptFile(encrypted);
d->targetFile->write(decrypted);
d->tempFile->remove();
} else {
@@ -151,7 +150,7 @@ BaseJob::Status DownloadFileJob::prepareResult()
auto encrypted = d->tempFile->readAll();
EncryptedFile file = *d->encryptedFile;
- auto decrypted = EncryptionManager::decryptFile(encrypted, &file);
+ auto decrypted = file.decryptFile(encrypted);
d->tempFile->write(decrypted);
} else {
#endif
diff --git a/lib/mxcreply.cpp b/lib/mxcreply.cpp
index c7f27b0c..c666cce3 100644
--- a/lib/mxcreply.cpp
+++ b/lib/mxcreply.cpp
@@ -9,7 +9,6 @@
#include "room.h"
#ifdef Quotient_E2EE_ENABLED
-#include "encryptionmanager.h"
#include "events/encryptedfile.h"
#endif
@@ -51,7 +50,7 @@ MxcReply::MxcReply(QNetworkReply* reply, Room* room, const QString &eventId)
} else {
EncryptedFile file = *d->m_encryptedFile;
auto buffer = new QBuffer(this);
- buffer->setData(EncryptionManager::decryptFile(d->m_reply->readAll(), &file));
+ buffer->setData(file.decryptFile(d->m_reply->readAll()));
buffer->open(ReadOnly);
d->m_device = buffer;
}