aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKitsune Ral <Kitsune-Ral@users.sf.net>2019-07-07 14:47:41 +0900
committerGitHub <noreply@github.com>2019-07-07 14:47:41 +0900
commit6c9d895c03b9d0e7ffbf5bace3994536ff3215be (patch)
treed80e9fe022e631d76fae641eddc264f7306996de
parentf58819e4e930ee66e790eccaedf551f807956d72 (diff)
parentd5b4e6440dae82eebc86657dd2f828edaf81b180 (diff)
downloadlibquotient-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.yml17
-rw-r--r--.gitmodules3
-rw-r--r--.travis.yml7
m---------3rdparty/libQtOlm0
-rw-r--r--CMakeLists.txt37
-rw-r--r--cmake/QMatrixClientConfig.cmake3
-rw-r--r--lib/connection.cpp10
-rw-r--r--lib/encryptionmanager.cpp206
-rw-r--r--lib/encryptionmanager.h31
-rw-r--r--lib/events/encryptionevent.cpp53
-rw-r--r--lib/events/encryptionevent.h78
-rw-r--r--lib/events/simplestateevents.h2
-rw-r--r--lib/room.cpp1
-rw-r--r--lib/settings.cpp22
-rw-r--r--lib/settings.h5
-rw-r--r--libqmatrixclient.pri6
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 \