From 69ba71cca36b4a90328d169c845195f39c041a3a Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Sat, 22 Jun 2019 19:57:18 +0300 Subject: Add EncryptionManager class. Add AccountSettings::encryptionAccountPickle logic. --- lib/encryptionmanager.cpp | 117 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 lib/encryptionmanager.cpp (limited to 'lib/encryptionmanager.cpp') 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 +#include +#include +#include +#include // 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 oneTimeKeyCounts; + void setOneTimeKeyCounts(const QHash oneTimeKeyCountsNewValue) + { + oneTimeKeyCounts = oneTimeKeyCountsNewValue; + updateKeysToUpload(); + } + QHash oneTimeKeysToUploadCounts; + QHash 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(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; +} -- cgit v1.2.3 From 79f6f33bbbd60eafe92b5187a3bf5fd8966f8bf5 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Sat, 22 Jun 2019 19:57:18 +0300 Subject: Upload device public keys. Issue #87 --- lib/encryptionmanager.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) (limited to 'lib/encryptionmanager.cpp') diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index b40305ea..80fdcebd 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -18,6 +18,7 @@ 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 { @@ -86,7 +87,52 @@ EncryptionManager::~EncryptionManager() = default; void EncryptionManager::uploadIdentityKeys(Connection* connection) { - // TODO + // 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 :. + * 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 : 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(deviceKeys); } void EncryptionManager::uploadOneTimeKeys(Connection* connection, bool forceUpdate) -- cgit v1.2.3 From e1bee9d71d88d6500d2c6124689a0e8685aeab90 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Sat, 22 Jun 2019 19:57:18 +0300 Subject: Upload one-time keys. Issue #88 --- lib/encryptionmanager.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) (limited to 'lib/encryptionmanager.cpp') diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index 80fdcebd..1e1fc669 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -137,7 +137,50 @@ void EncryptionManager::uploadIdentityKeys(Connection* connection) void EncryptionManager::uploadOneTimeKeys(Connection* connection, bool forceUpdate) { - // TODO + if (forceUpdate || d->oneTimeKeyCounts.isEmpty()) + { + auto job = connection->callApi(); + 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 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(none, oneTimeKeys); + d->olmAccount->markKeysAsPublished(); + qDebug() << QString("Uploaded new one-time keys: %1 signed, %2 unsigned.") + .arg(signedKeysToUploadCount).arg(unsignedKeysToUploadCount); } void EncryptionManager::Private::updateKeysToUpload() -- cgit v1.2.3