diff options
author | Alexey Andreyev <aa13q@ya.ru> | 2019-06-22 19:57:18 +0300 |
---|---|---|
committer | Alexey Andreyev <aa13q@ya.ru> | 2019-07-04 11:21:06 +0300 |
commit | 69ba71cca36b4a90328d169c845195f39c041a3a (patch) | |
tree | dfcb78bc916b03e13a54c4f8cd707e3324cb2894 | |
parent | 46a7350086e42ea3960fe54ef5c68b2207527899 (diff) | |
download | libquotient-69ba71cca36b4a90328d169c845195f39c041a3a.tar.gz libquotient-69ba71cca36b4a90328d169c845195f39c041a3a.zip |
Add EncryptionManager class. Add AccountSettings::encryptionAccountPickle logic.
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/connection.cpp | 10 | ||||
-rw-r--r-- | lib/encryptionmanager.cpp | 117 | ||||
-rw-r--r-- | lib/encryptionmanager.h | 31 | ||||
-rw-r--r-- | lib/settings.cpp | 22 | ||||
-rw-r--r-- | lib/settings.h | 5 | ||||
-rw-r--r-- | libqmatrixclient.pri | 2 |
7 files changed, 187 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b6cd0f3..d3906ffb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,6 +125,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 diff --git a/lib/connection.cpp b/lib/connection.cpp index a7eae30f..d7c3d78f 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" @@ -101,6 +102,8 @@ class Connection::Private GetCapabilitiesJob* capabilitiesJob = nullptr; GetCapabilitiesJob::Capabilities capabilities; + QScopedPointer<EncryptionManager> encryptionManager; + SyncJob* syncJob = nullptr; bool cacheState = true; @@ -235,6 +238,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..b40305ea --- /dev/null +++ b/lib/encryptionmanager.cpp @@ -0,0 +1,117 @@ +#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"); + +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) +{ + // TODO +} + +void EncryptionManager::uploadOneTimeKeys(Connection* connection, bool forceUpdate) +{ + // TODO +} + +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/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 4387af29..dfd45c04 100644 --- a/libqmatrixclient.pri +++ b/libqmatrixclient.pri @@ -13,6 +13,7 @@ INCLUDEPATH += $$SRCPATH HEADERS += \ $$SRCPATH/connectiondata.h \ $$SRCPATH/connection.h \ + $$SRCPATH/encryptionmanager.h \ $$SRCPATH/eventitem.h \ $$SRCPATH/room.h \ $$SRCPATH/user.h \ @@ -61,6 +62,7 @@ HEADERS += \ SOURCES += \ $$SRCPATH/connectiondata.cpp \ $$SRCPATH/connection.cpp \ + $$SRCPATH/encryptionmanager.cpp \ $$SRCPATH/eventitem.cpp \ $$SRCPATH/room.cpp \ $$SRCPATH/user.cpp \ |