aboutsummaryrefslogtreecommitdiff
path: root/lib/encryptionmanager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/encryptionmanager.cpp')
-rw-r--r--lib/encryptionmanager.cpp177
1 files changed, 162 insertions, 15 deletions
diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp
index 22387cf9..0895fae9 100644
--- a/lib/encryptionmanager.cpp
+++ b/lib/encryptionmanager.cpp
@@ -1,3 +1,4 @@
+#ifdef Quotient_E2EE_ENABLED
#include "encryptionmanager.h"
#include "connection.h"
@@ -9,6 +10,10 @@
#include <QtCore/QStringBuilder>
#include <account.h> // QtOlm
+#include <session.h> // QtOlm
+#include <message.h> // QtOlm
+#include <errors.h> // QtOlm
+#include <utils.h> // QtOlm
#include <functional>
#include <memory>
@@ -20,7 +25,8 @@ class EncryptionManager::Private {
public:
explicit Private(const QByteArray& encryptionAccountPickle,
float signedKeysProportion, float oneTimeKeyThreshold)
- : signedKeysProportion(move(signedKeysProportion))
+ : q(nullptr)
+ , signedKeysProportion(move(signedKeysProportion))
, oneTimeKeyThreshold(move(oneTimeKeyThreshold))
{
Q_ASSERT((0 <= signedKeysProportion) && (signedKeysProportion <= 1));
@@ -44,18 +50,23 @@ public:
* until the limit is reached and it starts discarding keys, starting by
* the oldest.
*/
- targetKeysNumber = olmAccount->maxOneTimeKeys(); // 2 // see note below
+ targetKeysNumber = olmAccount->maxOneTimeKeys() / 2;
targetOneTimeKeyCounts = {
{ SignedCurve25519Key,
qRound(signedKeysProportion * targetKeysNumber) },
{ Curve25519Key,
qRound((1 - signedKeysProportion) * targetKeysNumber) }
};
+ updateKeysToUpload();
}
~Private() = default;
+ EncryptionManager* q;
+
UploadKeysJob* uploadIdentityKeysJob = nullptr;
+ UploadKeysJob* uploadOneTimeKeysInitJob = nullptr;
UploadKeysJob* uploadOneTimeKeysJob = nullptr;
+ QueryKeysJob* queryKeysJob = nullptr;
QScopedPointer<Account> olmAccount;
@@ -74,6 +85,95 @@ public:
}
QHash<QString, int> oneTimeKeysToUploadCounts;
QHash<QString, int> targetOneTimeKeyCounts;
+
+ // A map from senderKey to InboundSession
+ QMap<QString, InboundSession*> sessions; // TODO: cache
+ void updateDeviceKeys(
+ const QHash<QString, QHash<QString, QueryKeysJob::DeviceInformation>>&
+ deviceKeys)
+ {
+ for (auto userId : deviceKeys.keys()) {
+ for (auto deviceId : deviceKeys.value(userId).keys()) {
+ QueryKeysJob::DeviceInformation info =
+ deviceKeys.value(userId).value(deviceId);
+ // TODO: ed25519Verify, etc
+ }
+ }
+ }
+ QString sessionDecrypt(Message* message, const QString& senderKey)
+ {
+ QString decrypted;
+ QList<InboundSession*> senderSessions = sessions.values(senderKey);
+ // Try to decrypt message body using one of the known sessions for that
+ // device
+ bool sessionsPassed = false;
+ for (auto senderSession : senderSessions) {
+ if (senderSession == senderSessions.last()) {
+ sessionsPassed = true;
+ }
+ try {
+ decrypted = senderSession->decrypt(message);
+ qCDebug(E2EE)
+ << "Success decrypting Olm event using existing session"
+ << senderSession->id();
+ break;
+ } catch (OlmError* e) {
+ if (message->messageType() == 0) {
+ PreKeyMessage preKeyMessage =
+ PreKeyMessage(message->cipherText());
+ if (senderSession->matches(&preKeyMessage, senderKey)) {
+ // We had a matching session for a pre-key message, but
+ // it didn't work. This means something is wrong, so we
+ // fail now.
+ qCDebug(E2EE)
+ << "Error decrypting pre-key message with existing "
+ "Olm session"
+ << senderSession->id() << "reason:" << e->what();
+ return QString();
+ }
+ }
+ // Simply keep trying otherwise
+ }
+ }
+ if (sessionsPassed || senderSessions.empty()) {
+ if (message->messageType() > 0) {
+ // Not a pre-key message, we should have had a matching session
+ if (!sessions.empty()) {
+ qCDebug(E2EE) << "Error decrypting with existing sessions";
+ return QString();
+ }
+ qCDebug(E2EE) << "No existing sessions";
+ return QString();
+ }
+ // We have a pre-key message without any matching session, in this
+ // case we should try to create one.
+ InboundSession* newSession;
+ qCDebug(E2EE) << "try to establish new InboundSession with" << senderKey;
+ PreKeyMessage preKeyMessage = PreKeyMessage(message->cipherText());
+ try {
+ newSession = new InboundSession(olmAccount.data(),
+ &preKeyMessage,
+ senderKey.toLatin1(), q);
+ } catch (OlmError* e) {
+ qCDebug(E2EE) << "Error decrypting pre-key message when trying "
+ "to establish a new session:"
+ << e->what();
+ return QString();
+ }
+ qCDebug(E2EE) << "Created new Olm session" << newSession->id();
+ try {
+ decrypted = newSession->decrypt(message);
+ } catch (OlmError* e) {
+ qCDebug(E2EE)
+ << "Error decrypting pre-key message with new session"
+ << e->what();
+ return QString();
+ }
+ olmAccount->removeOneTimeKeys(newSession);
+ sessions.insert(senderKey, newSession);
+ }
+ return decrypted;
+ }
};
EncryptionManager::EncryptionManager(const QByteArray& encryptionAccountPickle,
@@ -83,7 +183,9 @@ EncryptionManager::EncryptionManager(const QByteArray& encryptionAccountPickle,
, d(std::make_unique<Private>(std::move(encryptionAccountPickle),
std::move(signedKeysProportion),
std::move(oneTimeKeyThreshold)))
-{}
+{
+ d->q = this;
+}
EncryptionManager::~EncryptionManager() = default;
@@ -132,20 +234,19 @@ void EncryptionManager::uploadIdentityKeys(Connection* connection)
d->olmAccount->sign(deviceKeysJsonObject) } } }
};
+ d->uploadIdentityKeysJob = connection->callApi<UploadKeysJob>(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());
+ d->uploadOneTimeKeysInitJob = connection->callApi<UploadKeysJob>();
+ connect(d->uploadOneTimeKeysInitJob, &BaseJob::success, this, [this] {
+ d->setOneTimeKeyCounts(d->uploadIdentityKeysJob->oneTimeKeyCounts());
});
}
@@ -170,9 +271,17 @@ void EncryptionManager::uploadOneTimeKeys(Connection* connection,
if (oneTimeKeysCounter < signedKeysToUploadCount) {
QJsonObject message { { QStringLiteral("key"),
it.value().toString() } };
- key = d->olmAccount->sign(message);
- keyType = SignedCurve25519Key;
+ QByteArray signedMessage = d->olmAccount->sign(message);
+ QJsonObject signatures {
+ { connection->userId(),
+ QJsonObject { { Ed25519Key + QStringLiteral(":")
+ + connection->deviceId(),
+ QString::fromUtf8(signedMessage) } } }
+ };
+ message.insert(QStringLiteral("signatures"), signatures);
+ key = message;
+ keyType = SignedCurve25519Key;
} else {
key = it.value();
keyType = Curve25519Key;
@@ -180,13 +289,50 @@ void EncryptionManager::uploadOneTimeKeys(Connection* connection,
++oneTimeKeysCounter;
oneTimeKeys.insert(QString("%1:%2").arg(keyType).arg(keyId), key);
}
-
- d->uploadOneTimeKeysJob = connection->callApi<UploadKeysJob>(none,
- oneTimeKeys);
+ d->uploadOneTimeKeysJob =
+ connection->callApi<UploadKeysJob>(none, oneTimeKeys);
+ connect(d->uploadOneTimeKeysJob, &BaseJob::success, this, [this] {
+ d->setOneTimeKeyCounts(d->uploadOneTimeKeysJob->oneTimeKeyCounts());
+ });
d->olmAccount->markKeysAsPublished();
- qDebug() << QString("Uploaded new one-time keys: %1 signed, %2 unsigned.")
+ qCDebug(E2EE) << QString("Uploaded new one-time keys: %1 signed, %2 unsigned.")
.arg(signedKeysToUploadCount)
- .arg(unsignedKeysToUploadCount);
+ .arg(unsignedKeysToUploadCount);
+}
+
+void EncryptionManager::updateOneTimeKeyCounts(
+ Connection* connection, const QHash<QString, int>& deviceOneTimeKeysCount)
+{
+ d->oneTimeKeyCounts = deviceOneTimeKeysCount;
+ if (d->oneTimeKeyShouldUpload()) {
+ qCDebug(E2EE) << "Uploading new one-time keys.";
+ uploadOneTimeKeys(connection);
+ }
+}
+
+void Quotient::EncryptionManager::updateDeviceKeys(
+ Connection* connection, const QHash<QString, QStringList>& deviceKeys)
+{
+ d->queryKeysJob = connection->callApi<QueryKeysJob>(deviceKeys);
+ connect(d->queryKeysJob, &BaseJob::success, this,
+ [this] { d->updateDeviceKeys(d->queryKeysJob->deviceKeys()); });
+}
+
+QString EncryptionManager::sessionDecryptMessage(
+ const QJsonObject& personalCipherObject, const QByteArray& senderKey)
+{
+ QString decrypted;
+ int type = personalCipherObject.value(TypeKeyL).toInt(-1);
+ QByteArray body = personalCipherObject.value(BodyKeyL).toString().toLatin1();
+ if (type == 0) {
+ PreKeyMessage preKeyMessage { body };
+ decrypted = d->sessionDecrypt(reinterpret_cast<Message*>(&preKeyMessage),
+ senderKey);
+ } else if (type == 1) {
+ Message message { body };
+ decrypted = d->sessionDecrypt(&message, senderKey);
+ }
+ return decrypted;
}
QByteArray EncryptionManager::olmAccountPickle()
@@ -221,3 +367,4 @@ bool EncryptionManager::Private::oneTimeKeyShouldUpload()
}
return false;
}
+#endif // Quotient_E2EE_ENABLED