diff options
author | Kitsune Ral <Kitsune-Ral@users.sf.net> | 2019-07-07 14:47:41 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-07-07 14:47:41 +0900 |
commit | 6c9d895c03b9d0e7ffbf5bace3994536ff3215be (patch) | |
tree | d80e9fe022e631d76fae641eddc264f7306996de | |
parent | f58819e4e930ee66e790eccaedf551f807956d72 (diff) | |
parent | d5b4e6440dae82eebc86657dd2f828edaf81b180 (diff) | |
download | libquotient-6c9d895c03b9d0e7ffbf5bace3994536ff3215be.tar.gz libquotient-6c9d895c03b9d0e7ffbf5bace3994536ff3215be.zip |
Merge pull request #329 from a-andreyev/aa13q-e2ee-enc-mng
E2EE: Introduce EncryptionManager with uploadIdentityKeys and uploadOneTimeKeys API.
-rw-r--r-- | .appveyor.yml | 17 | ||||
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | .travis.yml | 7 | ||||
m--------- | 3rdparty/libQtOlm | 0 | ||||
-rw-r--r-- | CMakeLists.txt | 37 | ||||
-rw-r--r-- | cmake/QMatrixClientConfig.cmake | 3 | ||||
-rw-r--r-- | lib/connection.cpp | 10 | ||||
-rw-r--r-- | lib/encryptionmanager.cpp | 206 | ||||
-rw-r--r-- | lib/encryptionmanager.h | 31 | ||||
-rw-r--r-- | lib/events/encryptionevent.cpp | 53 | ||||
-rw-r--r-- | lib/events/encryptionevent.h | 78 | ||||
-rw-r--r-- | lib/events/simplestateevents.h | 2 | ||||
-rw-r--r-- | lib/room.cpp | 1 | ||||
-rw-r--r-- | lib/settings.cpp | 22 | ||||
-rw-r--r-- | lib/settings.h | 5 | ||||
-rw-r--r-- | libqmatrixclient.pri | 6 |
16 files changed, 465 insertions, 16 deletions
diff --git a/.appveyor.yml b/.appveyor.yml index 410ad12e..4e2d4b5d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -7,16 +7,9 @@ environment: QTDIR: C:\Qt\5.9\msvc2017_64 VCVARS: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat" PLATFORM: - MAKETOOL: cmake - - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - QTDIR: C:\Qt\5.9\msvc2017_64 - VCVARS: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat" - PLATFORM: - MAKETOOL: qmake - QTDIR: C:\Qt\5.9\msvc2015 VCVARS: "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\vcvarsall.bat" PLATFORM: x86 - MAKETOOL: cmake init: - call "%QTDIR%\bin\qtenv2.bat" @@ -26,11 +19,15 @@ init: before_build: - git submodule update --init --recursive -- if %MAKETOOL% == cmake cmake -G "NMake Makefiles JOM" -H. -Bbuild -DCMAKE_CXX_FLAGS="/EHsc /W3" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX="%DEPLOY_DIR%" +- cd 3rdparty/libQtOlm +- git clone https://gitlab.matrix.org/matrix-org/olm.git +- cd ../.. +- cmake -G "NMake Makefiles JOM" -H. -Bbuild -DCMAKE_CXX_FLAGS="/EHsc /W3" -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX="%DEPLOY_DIR%" build_script: -- if %MAKETOOL% == cmake cmake --build build -- if %MAKETOOL% == qmake qmake && jom +- cmake --build build +# qmake uses olm just built by CMake - it can't build olm on its own. +- qmake "INCLUDEPATH += 3rdparty/libQtOlm/olm/include" "LIBS += -Lbuild" && jom #after_build: #- cmake --build build --target install diff --git a/.gitmodules b/.gitmodules index e69de29b..eb4c1815 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "3rdparty/libQtOlm"] + path = 3rdparty/libQtOlm + url = https://gitlab.com/b0/libqtolm.git diff --git a/.travis.yml b/.travis.yml index e0b10ce8..79d5d0e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,9 @@ before_install: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then USE_NINJA="-GNinja"; VALGRIND="valgrind $VALGRIND_OPTIONS"; . /opt/qt57/bin/qt57-env.sh; fi install: +- pushd 3rdparty/libQtOlm +- git clone https://gitlab.matrix.org/matrix-org/olm.git +- popd - git clone https://github.com/QMatrixClient/matrix-doc.git - git clone --recursive https://github.com/KitsuneRal/gtad.git - pushd gtad @@ -54,8 +57,8 @@ script: - cmake -DCMAKE_PREFIX_PATH=../install ../examples - cmake --build . --target all - popd -# Build and install with qmake -- qmake qmc-example.pro "CONFIG += debug" "CONFIG -= app_bundle" "QMAKE_CC = $CC" "QMAKE_CXX = $CXX" +# Build with qmake +- qmake qmc-example.pro "CONFIG += debug" "CONFIG -= app_bundle" "QMAKE_CC = $CC" "QMAKE_CXX = $CXX" "INCLUDEPATH += 3rdparty/libQtOlm/olm/include" "LIBS += -Lbuild/lib" - make all # Run the qmake-compiled qmc-example under valgrind - if [ "$QMC_TEST_USER" != "" ]; then $VALGRIND ./qmc-example "$QMC_TEST_USER" "$QMC_TEST_PWD" qmc-example-travis '#qmc-test:matrix.org' "Travis CI job $TRAVIS_JOB_NUMBER"; fi diff --git a/3rdparty/libQtOlm b/3rdparty/libQtOlm new file mode 160000 +Subproject f610197ba38ef87bbab8bcff1053bda684a5994 diff --git a/CMakeLists.txt b/CMakeLists.txt index ca597469..19fbdcbe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,24 @@ endforeach () find_package(Qt5 5.4.1 REQUIRED Network Gui Multimedia) get_filename_component(Qt5_Prefix "${Qt5_DIR}/../../../.." ABSOLUTE) +if ((NOT DEFINED USE_INTREE_LIBQOLM OR USE_INTREE_LIBQOLM) + AND EXISTS ${PROJECT_SOURCE_DIR}/3rdparty/libQtOlm/lib/utils.h) + add_subdirectory(3rdparty/libQtOlm EXCLUDE_FROM_ALL) + include_directories(3rdparty/libQtOlm) + if (NOT DEFINED USE_INTREE_LIBQOLM) + set (USE_INTREE_LIBQOLM 1) + endif () +endif () +if (NOT USE_INTREE_LIBQOLM) + find_package(QtOlm 0.1.0 REQUIRED) + if (NOT QtOlm_FOUND) + message( WARNING "libQtOlm not found; configuration will most likely fail.") + message( WARNING "Make sure you have installed libQtOlm development files") + message( WARNING "as a package or checked out the library sources in lib/.") + message( WARNING "See also BUILDING.md") + endif () +endif () + if (GTAD_PATH) get_filename_component(ABS_GTAD_PATH "${GTAD_PATH}" ABSOLUTE) endif () @@ -64,12 +82,27 @@ if (CMAKE_BUILD_TYPE) message( STATUS "Build type: ${CMAKE_BUILD_TYPE}") endif(CMAKE_BUILD_TYPE) message( STATUS "Using compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}" ) +message( STATUS "Install Prefix: ${CMAKE_INSTALL_PREFIX}" ) message( STATUS "Using Qt ${Qt5_VERSION} at ${Qt5_Prefix}" ) if (MATRIX_DOC_PATH AND GTAD_PATH) message( STATUS "Generating API stubs enabled" ) message( STATUS " Using GTAD at ${ABS_GTAD_PATH}" ) message( STATUS " Using API files at ${ABS_API_DEF_PATH}" ) endif () +find_package(Git) +if (USE_INTREE_LIBQOLM) + message( STATUS "Using in-tree libQtOlm") + if (GIT_FOUND) + execute_process(COMMAND + "${GIT_EXECUTABLE}" rev-parse -q HEAD + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/3rdparty/libQtOlm + OUTPUT_VARIABLE QTOLM_GIT_SHA1 + OUTPUT_STRIP_TRAILING_WHITESPACE) + message( STATUS " Library git SHA1: ${QTOLM_GIT_SHA1}") + endif (GIT_FOUND) +else () + message( STATUS "Using libQtOlm ${QtOlm_VERSION} at ${QtOlm_DIR}") +endif () message( STATUS "=============================================================================" ) message( STATUS ) @@ -87,6 +120,7 @@ set(libqmatrixclient_SRCS lib/networksettings.cpp lib/converters.cpp lib/util.cpp + lib/encryptionmanager.cpp lib/eventitem.cpp lib/events/event.cpp lib/events/roomevent.cpp @@ -103,6 +137,7 @@ set(libqmatrixclient_SRCS lib/events/callhangupevent.cpp lib/events/callinviteevent.cpp lib/events/directchatevent.cpp + lib/events/encryptionevent.cpp lib/jobs/requestdata.cpp lib/jobs/basejob.cpp lib/jobs/syncjob.cpp @@ -158,7 +193,7 @@ target_include_directories(QMatrixClient PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/lib> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> ) -target_link_libraries(QMatrixClient Qt5::Core Qt5::Network Qt5::Gui Qt5::Multimedia) +target_link_libraries(QMatrixClient QtOlm Qt5::Core Qt5::Network Qt5::Gui Qt5::Multimedia) add_executable(qmc-example ${example_SRCS}) target_link_libraries(qmc-example Qt5::Core QMatrixClient) diff --git a/cmake/QMatrixClientConfig.cmake b/cmake/QMatrixClientConfig.cmake index 900038a5..64180cca 100644 --- a/cmake/QMatrixClientConfig.cmake +++ b/cmake/QMatrixClientConfig.cmake @@ -1 +1,4 @@ +include(CMakeFindDependencyMacro) + +find_dependency(QtOlm) include("${CMAKE_CURRENT_LIST_DIR}/QMatrixClientTargets.cmake") diff --git a/lib/connection.cpp b/lib/connection.cpp index 5d377173..20fb367c 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -23,6 +23,7 @@ #include "events/eventloader.h" #include "room.h" #include "settings.h" +#include "encryptionmanager.h" #include "csapi/login.h" #include "csapi/capabilities.h" #include "csapi/logout.h" @@ -102,6 +103,8 @@ class Connection::Private GetCapabilitiesJob* capabilitiesJob = nullptr; GetCapabilitiesJob::Capabilities capabilities; + QScopedPointer<EncryptionManager> encryptionManager; + SyncJob* syncJob = nullptr; bool cacheState = true; @@ -248,6 +251,13 @@ void Connection::doConnectToServer(const QString& user, const QString& password, [this, loginJob] { d->connectWithToken(loginJob->userId(), loginJob->accessToken(), loginJob->deviceId()); + + AccountSettings accountSettings(loginJob->userId()); + d->encryptionManager.reset(new EncryptionManager(accountSettings.encryptionAccountPickle())); + + d->encryptionManager->uploadIdentityKeys(this); + d->encryptionManager->uploadOneTimeKeys(this); + }); connect(loginJob, &BaseJob::failure, this, [this, loginJob] { diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp new file mode 100644 index 00000000..1e1fc669 --- /dev/null +++ b/lib/encryptionmanager.cpp @@ -0,0 +1,206 @@ +#include "encryptionmanager.h" + +#include <functional> +#include <memory> +#include <QtCore/QStringBuilder> +#include <QtCore/QHash> +#include <account.h> // QtOlm + +#include "csapi/keys.h" +#include "connection.h" + +using namespace QMatrixClient; +using namespace QtOlm; +using std::move; + +static const auto ed25519Name = QStringLiteral("ed25519"); +static const auto Curve25519Name = QStringLiteral("curve25519"); +static const auto SignedCurve25519Name = QStringLiteral("signed_curve25519"); +static const auto OlmCurve25519AesSha256AlgoName = QStringLiteral("m.olm.curve25519-aes-sha256"); +static const auto MegolmV1AesShaAlgoName = QStringLiteral("m.megolm.v1.aes-sha"); +static const QStringList SupportedAlgorithms = { OlmCurve25519AesSha256AlgoName, MegolmV1AesShaAlgoName }; + +class EncryptionManager::Private +{ + public: + explicit Private(const QByteArray& encryptionAccountPickle, float signedKeysProportion, float oneTimeKeyThreshold) + : olmAccount(new Account(encryptionAccountPickle)), // TODO: passphrase even with qtkeychain? + signedKeysProportion(move(signedKeysProportion)), + oneTimeKeyThreshold(move(oneTimeKeyThreshold)), + targetKeysNumber(olmAccount->maxOneTimeKeys()) // 2 // see note below + { + Q_ASSERT((0 <= signedKeysProportion) && (signedKeysProportion <= 1)); + Q_ASSERT((0 <= oneTimeKeyThreshold) && (oneTimeKeyThreshold <= 1)); + /* + * Note about targetKeysNumber: + * + * From: https://github.com/Zil0/matrix-python-sdk/ + * File: matrix_client/crypto/olm_device.py + * + * Try to maintain half the number of one-time keys libolm can hold uploaded + * on the HS. This is because some keys will be claimed by peers but not + * used instantly, and we want them to stay in libolm, until the limit is reached + * and it starts discarding keys, starting by the oldest. + */ + } + ~Private() + { + delete olmAccount; + } + + UploadKeysJob* uploadIdentityKeysJob = nullptr; + UploadKeysJob* uploadOneTimeKeysJob = nullptr; + + Account* olmAccount; + const QByteArray encryptionAccountPickle; + + float signedKeysProportion; + float oneTimeKeyThreshold; + int targetKeysNumber; + + void updateKeysToUpload(); + bool oneTimeKeyShouldUpload(); + + QHash<QString, int> oneTimeKeyCounts; + void setOneTimeKeyCounts(const QHash<QString, int> oneTimeKeyCountsNewValue) + { + oneTimeKeyCounts = oneTimeKeyCountsNewValue; + updateKeysToUpload(); + } + QHash<QString, int> oneTimeKeysToUploadCounts; + QHash<QString, int> targetOneTimeKeyCounts + { + {SignedCurve25519Name, qRound(signedKeysProportion * targetKeysNumber)}, + {Curve25519Name, qRound((1-signedKeysProportion) * targetKeysNumber)} + }; +}; + +EncryptionManager::EncryptionManager(const QByteArray &encryptionAccountPickle, float signedKeysProportion, float oneTimeKeyThreshold, + QObject* parent) + : QObject(parent), + d(std::make_unique<Private>(std::move(encryptionAccountPickle), std::move(signedKeysProportion), std::move(oneTimeKeyThreshold))) +{ + +} + +EncryptionManager::~EncryptionManager() = default; + +void EncryptionManager::uploadIdentityKeys(Connection* connection) +{ + // https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-keys-upload + DeviceKeys deviceKeys + { + /* + * The ID of the user the device belongs to. Must match the user ID used when logging in. + * The ID of the device these keys belong to. Must match the device ID used when logging in. + * The encryption algorithms supported by this device. + */ + connection->userId(), connection->deviceId(), SupportedAlgorithms, + /* + * Public identity keys. The names of the properties should be in the format <algorithm>:<device_id>. + * The keys themselves should be encoded as specified by the key algorithm. + */ + { + { + Curve25519Name + QStringLiteral(":") + connection->deviceId(), + d->olmAccount->curve25519IdentityKey() + }, + { + ed25519Name + QStringLiteral(":") + connection->deviceId(), + d->olmAccount->ed25519IdentityKey() + } + }, + /* + * Signatures for the device key object. + * A map from user ID, to a map from <algorithm>:<device_id> to the signature. + * The signature is calculated using the process called Signing JSON. + */ + { + { + connection->userId(), + { + { + ed25519Name + QStringLiteral(":") + connection->deviceId(), + d->olmAccount->sign(toJson(deviceKeys)) + } + } + } + } + }; + + connect(d->uploadIdentityKeysJob, &BaseJob::success, this, [this] { + d->setOneTimeKeyCounts(d->uploadIdentityKeysJob->oneTimeKeyCounts()); + qDebug() << QString("Uploaded identity keys."); + }); + d->uploadIdentityKeysJob = connection->callApi<UploadKeysJob>(deviceKeys); +} + +void EncryptionManager::uploadOneTimeKeys(Connection* connection, bool forceUpdate) +{ + if (forceUpdate || d->oneTimeKeyCounts.isEmpty()) + { + auto job = connection->callApi<UploadKeysJob>(); + connect(job, &BaseJob::success, this, [job,this] { + d->setOneTimeKeyCounts(job->oneTimeKeyCounts()); + }); + + } + + int signedKeysToUploadCount = d->oneTimeKeysToUploadCounts.value(SignedCurve25519Name, 0); + int unsignedKeysToUploadCount = d->oneTimeKeysToUploadCounts.value(Curve25519Name, 0); + + d->olmAccount->generateOneTimeKeys(signedKeysToUploadCount + unsignedKeysToUploadCount); + + QHash<QString, QVariant> oneTimeKeys = {}; + const auto& olmAccountCurve25519OneTimeKeys = d->olmAccount->curve25519OneTimeKeys(); + + int oneTimeKeysCounter = 0; + for (auto it = olmAccountCurve25519OneTimeKeys.cbegin(); it != olmAccountCurve25519OneTimeKeys.cend(); ++it) + { + QString keyId = it.key(); + QString keyType; + QVariant key; + if (oneTimeKeysCounter < signedKeysToUploadCount) + { + QJsonObject message + { + {QStringLiteral("key"), it.value().toString()} + }; + key = d->olmAccount->sign(message); + keyType = SignedCurve25519Name; + + } else { + key = it.value(); + keyType = Curve25519Name; + } + ++oneTimeKeysCounter; + oneTimeKeys.insert(QString("%1:%2").arg(keyType).arg(keyId), key); + } + + d->uploadOneTimeKeysJob = connection->callApi<UploadKeysJob>(none, oneTimeKeys); + d->olmAccount->markKeysAsPublished(); + qDebug() << QString("Uploaded new one-time keys: %1 signed, %2 unsigned.") + .arg(signedKeysToUploadCount).arg(unsignedKeysToUploadCount); +} + +void EncryptionManager::Private::updateKeysToUpload() +{ + for (auto it = targetOneTimeKeyCounts.cbegin(); it != targetOneTimeKeyCounts.cend(); ++it) + { + int numKeys = oneTimeKeyCounts.value(it.key(), 0); + int numToCreate = qMax(it.value() - numKeys, 0); + oneTimeKeysToUploadCounts.insert(it.key(), numToCreate); + } +} + +bool EncryptionManager::Private::oneTimeKeyShouldUpload() +{ + if (oneTimeKeyCounts.empty()) + return true; + for (auto it = targetOneTimeKeyCounts.cbegin(); it != targetOneTimeKeyCounts.cend(); ++it) + { + if (oneTimeKeyCounts.value(it.key(), 0) < it.value() * oneTimeKeyThreshold) + return true; + } + return false; +} diff --git a/lib/encryptionmanager.h b/lib/encryptionmanager.h new file mode 100644 index 00000000..0bd05432 --- /dev/null +++ b/lib/encryptionmanager.h @@ -0,0 +1,31 @@ +#pragma once + +#include <functional> +#include <memory> +#include <QtCore/QObject> + +namespace QMatrixClient +{ + class Connection; + + class EncryptionManager: public QObject + { + Q_OBJECT + + public: + // TODO: store constats separately? + // TODO: 0.5 oneTimeKeyThreshold instead of 0.1? + explicit EncryptionManager(const QByteArray& encryptionAccountPickle, float signedKeysProportion = 1, float oneTimeKeyThreshold = float(0.1), + QObject* parent = nullptr); + ~EncryptionManager(); + + void uploadIdentityKeys(Connection* connection); + void uploadOneTimeKeys(Connection* connection, bool forceUpdate = false); + + private: + class Private; + std::unique_ptr<Private> d; + + }; + +} // namespace QMatrixClient diff --git a/lib/events/encryptionevent.cpp b/lib/events/encryptionevent.cpp new file mode 100644 index 00000000..b8e2b575 --- /dev/null +++ b/lib/events/encryptionevent.cpp @@ -0,0 +1,53 @@ +// +// Created by rusakov on 26/09/2017. +// Contributed by andreev on 27/06/2019. +// + +#include "encryptionevent.h" + +#include "converters.h" +#include "logging.h" + +#include <array> + +static const std::array<QString, 1> encryptionStrings = { { + QStringLiteral("m.megolm.v1.aes-sha2") +} }; + +namespace QMatrixClient { + template <> + struct JsonConverter<EncryptionType> + { + static EncryptionType load(const QJsonValue& jv) + { + const auto& encryptionString = jv.toString(); + for (auto it = encryptionStrings.begin(); + it != encryptionStrings.end(); ++it) + if (encryptionString == *it) + return EncryptionType(it - encryptionStrings.begin()); + + qCWarning(EVENTS) << "Unknown EncryptionType: " << encryptionString; + return EncryptionType::Undefined; + } + }; +} + +using namespace QMatrixClient; + +EncryptionEventContent::EncryptionEventContent(const QJsonObject& json) + : encryption(fromJson<EncryptionType>(json["algorithm"_ls])) + , algorithm(sanitized(json["algorithm"_ls].toString())) + , rotationPeriodMs(json["rotation_period_ms"_ls].toInt(604800000)) + , rotationPeriodMsgs(json["rotation_period_msgs"_ls].toInt(100)) +{ } + +void EncryptionEventContent::fillJson(QJsonObject* o) const +{ + Q_ASSERT(o); + Q_ASSERT_X(encryption != EncryptionType::Undefined, __FUNCTION__, + "The key 'algorithm' must be explicit in EncryptionEventContent"); + if (encryption != EncryptionType::Undefined) + o->insert(QStringLiteral("algorithm"), algorithm); + o->insert(QStringLiteral("rotation_period_ms"), rotationPeriodMs); + o->insert(QStringLiteral("rotation_period_msgs"), rotationPeriodMsgs); +} diff --git a/lib/events/encryptionevent.h b/lib/events/encryptionevent.h new file mode 100644 index 00000000..b9e108f0 --- /dev/null +++ b/lib/events/encryptionevent.h @@ -0,0 +1,78 @@ +/****************************************************************************** + * Copyright (C) 2017 Kitsune Ral <kitsune-ral@users.sf.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include "stateevent.h" +#include "eventcontent.h" + +namespace QMatrixClient +{ + class EncryptionEventContent: public EventContent::Base + { + public: + enum EncryptionType : size_t { MegolmV1AesSha2 = 0, + Undefined }; + + explicit EncryptionEventContent(EncryptionType et = Undefined) + : encryption(et) + { } + explicit EncryptionEventContent(const QJsonObject& json); + + EncryptionType encryption; + QString algorithm; + int rotationPeriodMs; + int rotationPeriodMsgs; + + protected: + void fillJson(QJsonObject* o) const override; + }; + + using EncryptionType = EncryptionEventContent::EncryptionType; + + class EncryptionEvent : public StateEvent<EncryptionEventContent> + { + Q_GADGET + public: + DEFINE_EVENT_TYPEID("m.room.encryption", EncryptionEvent) + + using EncryptionType = EncryptionEventContent::EncryptionType; + + explicit EncryptionEvent(const QJsonObject& obj = {}) // TODO: apropriate default value + : StateEvent(typeId(), obj) + { } + template <typename... ArgTs> + EncryptionEvent(ArgTs&&... contentArgs) + : StateEvent(typeId(), matrixTypeId(), QString(), + std::forward<ArgTs>(contentArgs)...) + { } + + EncryptionType encryption() const { return content().encryption; } + + QString algorithm() const { return content().algorithm; } + int rotationPeriodMs() const { return content().rotationPeriodMs; } + int rotationPeriodMsgs() const { return content().rotationPeriodMsgs; } + + private: + REGISTER_ENUM(EncryptionType) + }; + + REGISTER_EVENT_TYPE(EncryptionEvent) + DEFINE_EVENTTYPE_ALIAS(Encryption, EncryptionEvent) +} // namespace QMatrixClient + diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h index dc6a0868..ef56c7b2 100644 --- a/lib/events/simplestateevents.h +++ b/lib/events/simplestateevents.h @@ -81,8 +81,6 @@ namespace QMatrixClient DEFINE_EVENTTYPE_ALIAS(RoomCanonicalAlias, RoomCanonicalAliasEvent) DEFINE_SIMPLE_STATE_EVENT(RoomTopicEvent, "m.room.topic", QString, topic) DEFINE_EVENTTYPE_ALIAS(RoomTopic, RoomTopicEvent) - DEFINE_SIMPLE_STATE_EVENT(EncryptionEvent, "m.room.encryption", - QString, algorithm) DEFINE_EVENTTYPE_ALIAS(RoomEncryption, EncryptionEvent) class RoomAliasesEvent diff --git a/lib/room.cpp b/lib/room.cpp index 44cd0d06..dea21082 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -31,6 +31,7 @@ #include "csapi/tags.h" #include "csapi/room_upgrades.h" #include "events/simplestateevents.h" +#include "events/encryptionevent.h" #include "events/roomcreateevent.h" #include "events/roomtombstoneevent.h" #include "events/roomavatarevent.h" diff --git a/lib/settings.cpp b/lib/settings.cpp index 124d7042..5f10299c 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -90,6 +90,7 @@ QMC_DEFINE_SETTING(AccountSettings, bool, keepLoggedIn, "keep_logged_in", false, static const auto HomeserverKey = QStringLiteral("homeserver"); static const auto AccessTokenKey = QStringLiteral("access_token"); +static const auto EncryptionAccountPickleKey = QStringLiteral("encryption_account_pickle"); QUrl AccountSettings::homeserver() const { @@ -114,7 +115,7 @@ QString AccountSettings::accessToken() const void AccountSettings::setAccessToken(const QString& accessToken) { qCWarning(MAIN) << "Saving access_token to QSettings is insecure." - " Developers, please save access_token separately."; + " Developers, do it manually or contribute to share QtKeychain logic to libQuotient."; setValue(AccessTokenKey, accessToken); } @@ -124,3 +125,22 @@ void AccountSettings::clearAccessToken() legacySettings.remove(QStringLiteral("device_id")); // Force the server to re-issue it remove(AccessTokenKey); } + +QByteArray AccountSettings::encryptionAccountPickle() +{ + QString passphrase = ""; // FIXME: add QtKeychain + return value("encryption_account_pickle", "").toByteArray(); +} + +void AccountSettings::setEncryptionAccountPickle(const QByteArray& encryptionAccountPickle) +{ + qCWarning(MAIN) << "Saving encryption_account_pickle to QSettings is insecure." + " Developers, do it manually or contribute to share QtKeychain logic to libQuotient."; + QString passphrase = ""; // FIXME: add QtKeychain + setValue("encryption_account_pickle", encryptionAccountPickle); +} + +void AccountSettings::clearEncryptionAccountPickle() +{ + remove(EncryptionAccountPickleKey); // TODO: Force to re-issue it? +} diff --git a/lib/settings.h b/lib/settings.h index 759bda35..61e5232a 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -131,6 +131,7 @@ void classname::setter(type newValue) \ QMC_DECLARE_SETTING(bool, keepLoggedIn, setKeepLoggedIn) /** \deprecated \sa setAccessToken */ Q_PROPERTY(QString accessToken READ accessToken WRITE setAccessToken) + Q_PROPERTY(QByteArray encryptionAccountPickle READ encryptionAccountPickle WRITE setEncryptionAccountPickle) public: template <typename... ArgTs> explicit AccountSettings(const QString& accountId, ArgTs... qsettingsArgs) @@ -148,5 +149,9 @@ void classname::setter(type newValue) \ * see QMatrixClient/Quaternion#181 */ void setAccessToken(const QString& accessToken); Q_INVOKABLE void clearAccessToken(); + + QByteArray encryptionAccountPickle(); + void setEncryptionAccountPickle(const QByteArray& encryptionAccountPickle); + Q_INVOKABLE void clearEncryptionAccountPickle(); }; } // namespace QMatrixClient diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri index be568bd2..c561a415 100644 --- a/libqmatrixclient.pri +++ b/libqmatrixclient.pri @@ -7,12 +7,15 @@ win32-msvc* { QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter } +include(3rdparty/libQtOlm/libQtOlm.pri) + SRCPATH = $$PWD/lib INCLUDEPATH += $$SRCPATH HEADERS += \ $$SRCPATH/connectiondata.h \ $$SRCPATH/connection.h \ + $$SRCPATH/encryptionmanager.h \ $$SRCPATH/eventitem.h \ $$SRCPATH/room.h \ $$SRCPATH/user.h \ @@ -38,6 +41,7 @@ HEADERS += \ $$SRCPATH/events/callinviteevent.h \ $$SRCPATH/events/accountdataevents.h \ $$SRCPATH/events/directchatevent.h \ + $$SRCPATH/events/encryptionevent.h \ $$SRCPATH/events/redactionevent.h \ $$SRCPATH/events/eventloader.h \ $$SRCPATH/jobs/requestdata.h \ @@ -60,6 +64,7 @@ HEADERS += \ SOURCES += \ $$SRCPATH/connectiondata.cpp \ $$SRCPATH/connection.cpp \ + $$SRCPATH/encryptionmanager.cpp \ $$SRCPATH/eventitem.cpp \ $$SRCPATH/room.cpp \ $$SRCPATH/user.cpp \ @@ -81,6 +86,7 @@ SOURCES += \ $$SRCPATH/events/callinviteevent.cpp \ $$SRCPATH/events/receiptevent.cpp \ $$SRCPATH/events/directchatevent.cpp \ + $$SRCPATH/events/encryptionevent.cpp \ $$SRCPATH/jobs/requestdata.cpp \ $$SRCPATH/jobs/basejob.cpp \ $$SRCPATH/jobs/syncjob.cpp \ |