diff options
-rw-r--r-- | CMakeLists.txt | 4 | ||||
-rw-r--r-- | lib/connection.cpp | 48 | ||||
-rw-r--r-- | lib/connection.h | 5 | ||||
-rw-r--r-- | lib/encryptionmanager.cpp | 4 | ||||
-rw-r--r-- | lib/events/eventcontent.cpp | 1 | ||||
-rw-r--r-- | lib/room.cpp | 5 |
6 files changed, 59 insertions, 8 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d5f08af..3977a9d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,6 +109,8 @@ if (${PROJECT_NAME}_ENABLE_E2EE) endif() endif() +find_package(Qt${QT_MAJOR_VERSION}Keychain REQUIRED) + # Set up source files list(APPEND lib_SRCS lib/quotient_common.h @@ -325,7 +327,7 @@ if (${PROJECT_NAME}_ENABLE_E2EE) find_dependency(OpenSSL)") # For QuotientConfig.cmake.in endif() -target_link_libraries(${PROJECT_NAME} ${Qt}::Core ${Qt}::Network ${Qt}::Gui) +target_link_libraries(${PROJECT_NAME} ${Qt}::Core ${Qt}::Network ${Qt}::Gui ${QTKEYCHAIN_LIBRARIES}) if (Qt STREQUAL Qt5) # See #483 target_link_libraries(${PROJECT_NAME} ${Qt}::Multimedia) diff --git a/lib/connection.cpp b/lib/connection.cpp index 7a96bc50..77ab3b72 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -39,6 +39,7 @@ #ifdef Quotient_E2EE_ENABLED # include "crypto/qolmaccount.h" +# include "crypto/qolmutils.h" #endif // Quotient_E2EE_ENABLED #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) @@ -55,6 +56,13 @@ #include <QtCore/QStringBuilder> #include <QtNetwork/QDnsLookup> + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +# include <qt6keychain/keychain.h> +#else +# include <qt5keychain/keychain.h> +#endif + using namespace Quotient; // This is very much Qt-specific; STL iterators don't have key() and value() @@ -108,6 +116,7 @@ public: QHash<QString, QHash<QString, QueryKeysJob::DeviceInformation>> deviceKeys; QueryKeysJob *currentQueryKeysJob = nullptr; bool encryptionUpdateRequired = false; + PicklingMode picklingMode = Unencrypted {}; #endif GetCapabilitiesJob* capabilitiesJob = nullptr; @@ -457,10 +466,40 @@ void Connection::Private::completeSetup(const QString& mxId) #else // Quotient_E2EE_ENABLED AccountSettings accountSettings(data->userId()); + QKeychain::ReadPasswordJob job(qAppName()); + job.setAutoDelete(false); + job.setKey(accountSettings.userId() + QStringLiteral("-Pickle")); + QEventLoop loop; + QKeychain::ReadPasswordJob::connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit); + job.start(); + loop.exec(); + + if (job.error() == QKeychain::Error::EntryNotFound) { + picklingMode = Encrypted { getRandom(128) }; + QKeychain::WritePasswordJob job(qAppName()); + job.setAutoDelete(false); + job.setKey(accountSettings.userId() + QStringLiteral("-Pickle")); + job.setBinaryData(std::get<Encrypted>(picklingMode).key); + QEventLoop loop; + QKeychain::WritePasswordJob::connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit); + job.start(); + loop.exec(); + + if (job.error()) { + qCWarning(E2EE) << "Could not save pickling key to keychain: " << job.errorString(); + } + } else if(job.error() != QKeychain::Error::NoError) { + //TODO Error, do something + qCWarning(E2EE) << "Error loading pickling key from keychain:" << job.error(); + } else { + qCDebug(E2EE) << "Successfully loaded pickling key from keychain"; + picklingMode = Encrypted { job.binaryData() }; + } + // init olmAccount olmAccount = std::make_unique<QOlmAccount>(data->userId(), data->deviceId(), q); connect(olmAccount.get(), &QOlmAccount::needsSave, q, [=](){ - auto pickle = olmAccount->pickle(Unencrypted{}); + auto pickle = olmAccount->pickle(picklingMode); AccountSettings(data->userId()).setEncryptionAccountPickle(std::get<QByteArray>(pickle)); //TODO handle errors }); @@ -476,7 +515,7 @@ void Connection::Private::completeSetup(const QString& mxId) } else { // account already existing auto pickle = accountSettings.encryptionAccountPickle(); - olmAccount->unpickle(pickle, Unencrypted{}); + olmAccount->unpickle(pickle, picklingMode); } #endif // Quotient_E2EE_ENABLED emit q->stateChanged(); @@ -1982,4 +2021,9 @@ void Connection::Private::loadDevicesList() } }); } + +PicklingMode Connection::picklingMode() const +{ + return d->picklingMode; +} #endif diff --git a/lib/connection.h b/lib/connection.h index c351f93e..e5cec34b 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -21,6 +21,10 @@ #include <functional> +#ifdef Quotient_E2EE_ENABLED +#include "crypto/e2ee.h" +#endif + Q_DECLARE_METATYPE(Quotient::GetLoginFlowsJob::LoginFlow) namespace Quotient { @@ -650,6 +654,7 @@ public Q_SLOTS: #ifdef Quotient_E2EE_ENABLED void encryptionUpdate(Room *room); + PicklingMode picklingMode() const; #endif Q_SIGNALS: /// \brief Initial server resolution has failed diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index e8cc7b3a..5c1750c9 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -71,7 +71,7 @@ public: } for(const auto &senderKey : json["sessions"].toObject().keys()) { auto pickle = json["sessions"].toObject()[senderKey].toString(); - auto sessionResult = QOlmSession::unpickle(pickle.toLatin1(), Unencrypted{}); + auto sessionResult = QOlmSession::unpickle(pickle.toLatin1(), static_cast<Connection *>(q->parent())->picklingMode()); if(std::holds_alternative<QOlmError>(sessionResult)) { qCWarning(E2EE) << "Failed to unpickle olm session"; continue; @@ -97,7 +97,7 @@ public: { QJsonObject sessionsJson; for (const auto &session : sessions) { - auto pickleResult = session.second->pickle(Unencrypted{}); + auto pickleResult = session.second->pickle(static_cast<Connection *>(q->parent())->picklingMode()); if(std::holds_alternative<QOlmError>(pickleResult)) { qCWarning(E2EE) << "Failed to pickle session"; continue; diff --git a/lib/events/eventcontent.cpp b/lib/events/eventcontent.cpp index 22878d4c..d4cb43ff 100644 --- a/lib/events/eventcontent.cpp +++ b/lib/events/eventcontent.cpp @@ -75,6 +75,7 @@ void FileInfo::fillInfoJson(QJsonObject* infoJson) const infoJson->insert(QStringLiteral("size"), payloadSize); if (mimeType.isValid()) infoJson->insert(QStringLiteral("mimetype"), mimeType.name()); + //TODO add encryptedfile } ImageInfo::ImageInfo(const QFileInfo& fi, QSize imageSize) diff --git a/lib/room.cpp b/lib/room.cpp index a1354fc5..b60a23f2 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -394,7 +394,7 @@ public: auto pickle = s.toObject()["pickle"].toString().toLatin1(); auto senderKey = s.toObject()["sender_key"].toString(); auto sessionId = s.toObject()["session_id"].toString(); - auto sessionResult = QOlmInboundGroupSession::unpickle(pickle, Unencrypted{}); + auto sessionResult = QOlmInboundGroupSession::unpickle(pickle, connection->picklingMode()); if(std::holds_alternative<QOlmError>(sessionResult)) { qCWarning(E2EE) << "Failed to unpickle olm session"; continue; @@ -421,7 +421,7 @@ public: { QJsonArray sessionsJson; for (const auto &session : groupSessions) { - auto pickleResult = session.second->pickle(Unencrypted{}); + auto pickleResult = session.second->pickle(connection->picklingMode()); sessionsJson += QJsonObject { {QStringLiteral("sender_key"), session.first.first}, {QStringLiteral("session_id"), session.first.second}, @@ -2659,7 +2659,6 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) //TODO should this be done before dropDuplicateEvents? for(long unsigned int i = 0; i < events.size(); i++) { if(auto* encrypted = eventCast<EncryptedEvent>(events[i])) { - qDebug() << "Encrypted Event"; auto decrypted = q->decryptMessage(*encrypted); if(decrypted) { events[i] = std::move(decrypted); |