diff --git a/Quotient/connection.cpp b/Quotient/connection.cpp index 8a46c2054..4a0669e24 100644 --- a/Quotient/connection.cpp +++ b/Quotient/connection.cpp @@ -1913,38 +1913,17 @@ bool Connection::isKnownE2eeCapableDevice(const QString& userId, const QString& bool Connection::hasConflictingDeviceIdsAndCrossSigningKeys(const QString& userId) { - auto devices = devicesForUser(userId); - - auto selfQuery = database()->prepareQuery("SELECT key FROM self_signing_keys WHERE userId=:userId;"_ls); - selfQuery.bindValue(":userId"_ls, userId); - database()->execute(selfQuery); - if (!selfQuery.next()) { - return false; - } - if (devices.contains(selfQuery.value("key"_ls).toString())) { - return true; - } - - auto masterQuery = database()->prepareQuery("SELECT key FROM master_keys WHERE userId=:userId;"_ls); - masterQuery.bindValue(":userId"_ls, userId); - database()->execute(masterQuery); - if (!masterQuery.next()) { - return false; - } - if (devices.contains(masterQuery.value("key"_ls).toString())) { - return true; + if (d->encryptionData) { + d->encryptionData->hasConflictingDeviceIdsAndCrossSigningKeys(userId); } + return true; +} - auto userQuery = database()->prepareQuery("SELECT key FROM user_signing_keys WHERE userId=:userId;"_ls); - userQuery.bindValue(":userId"_ls, userId); - database()->execute(userQuery); - if (!userQuery.next()) { - return false; - } - if (devices.contains(userQuery.value("key"_ls).toString())) { - return true; +void Connection::reloadDevices() +{ + if (d->encryptionData) { + reloadDevices(); } - return false; } #endif @@ -1956,9 +1935,3 @@ Connection* Connection::makeMockConnection(const QString& mxId, c->d->completeSetup(mxId, true); return c; } - -void Connection::reloadDevices() -{ - d->outdatedUsers = d->trackedUsers; - d->loadOutdatedUserDevices(); -} diff --git a/Quotient/connection.h b/Quotient/connection.h index 42c3d1d75..0e0eb01ab 100644 --- a/Quotient/connection.h +++ b/Quotient/connection.h @@ -353,9 +353,9 @@ class QUOTIENT_API Connection : public QObject { Q_INVOKABLE bool isQueryingKeys() const; QString masterKeyForUser(const QString& userId) const; bool isUserVerified(const QString& userId) const; + bool hasConflictingDeviceIdsAndCrossSigningKeys(const QString& userId); void reloadDevices(); - bool hasConflictingDeviceIdsAndCrossSigningKeys(const QString& userId); #endif // Quotient_E2EE_ENABLED Q_INVOKABLE Quotient::SyncJob* syncJob() const; diff --git a/Quotient/connectionencryptiondata_p.cpp b/Quotient/connectionencryptiondata_p.cpp index d3230a9f8..891fb3b18 100644 --- a/Quotient/connectionencryptiondata_p.cpp +++ b/Quotient/connectionencryptiondata_p.cpp @@ -140,14 +140,14 @@ void ConnectionEncryptionData::saveDevicesList() query.prepare(QStringLiteral( "INSERT INTO tracked_devices" "(matrixId, deviceId, curveKeyId, curveKey, edKeyId, edKey, verified, selfVerified) " - "VALUES (:matrixId, :deviceId, :curveKeyId, :curveKey, :edKeyId, :edKey, :verified, :selfVerified);" + "VALUES (:matrixId, :deviceId, :curveKeyId, :curveKey, :edKeyId, :edKey, :verified, :selfVerified);")); for (const auto& [user, devices] : asKeyValueRange(deviceKeys)) { for (const auto& device : devices) { auto keys = device.keys.keys(); - auto deleteQuery = database->prepareQuery("DELETE FROM tracked_devices WHERE matrixId=:matrixId AND deviceId=:deviceId;"_ls); + auto deleteQuery = database.prepareQuery("DELETE FROM tracked_devices WHERE matrixId=:matrixId AND deviceId=:deviceId;"_ls); deleteQuery.bindValue(":matrixId"_ls, user); deleteQuery.bindValue(":deviceId"_ls, device.deviceId); - database->execute(deleteQuery); + database.execute(deleteQuery); auto curveKeyId = keys[0].startsWith("curve"_ls) ? keys[0] : keys[1]; @@ -381,8 +381,8 @@ void ConnectionEncryptionData::handleQueryKeys(const QHash& masterKeys, const QHash& selfSigningKeys, const QHash& userSigningKeys) { - database->transaction(); - for (const auto &[userId, key] : asKeyValueRange(job->masterKeys())) { + database.transaction(); + for (const auto &[userId, key] : asKeyValueRange(masterKeys)) { if (key.userId != userId) { qCWarning(E2EE) << "Master key: userId mismatch"; continue; @@ -391,35 +391,35 @@ void ConnectionEncryptionData::handleQueryKeys(const QHashprepareQuery("SELECT * FROM master_keys WHERE userId=:userId"_ls); + auto checkQuery = database.prepareQuery("SELECT * FROM master_keys WHERE userId=:userId"_ls); checkQuery.bindValue(":userId"_ls, key.userId); - database->execute(checkQuery); + database.execute(checkQuery); if (checkQuery.next()) { if (checkQuery.value("key"_ls).toString() != key.keys.values()[0]) { qCWarning(E2EE) << "New master key for" << key.userId; - database->transaction(); - auto query = database->prepareQuery( + database.transaction(); + auto query = database.prepareQuery( "UPDATE tracked_devices SET verified=0, selfVerified=0 WHERE matrixId=:matrixId;"_ls); query.bindValue(":matrixId"_ls, userId); - database->execute(query); - query = database->prepareQuery("DELETE FROM self_signing_keys WHERE userId=:userId;"_ls); + database.execute(query); + query = database.prepareQuery("DELETE FROM self_signing_keys WHERE userId=:userId;"_ls); query.bindValue(":userId"_ls, userId); - database->execute(query); - database->commit(); + database.execute(query); + database.commit(); } else { continue; } } - auto query = database->prepareQuery("DELETE FROM master_keys WHERE userId=:userId;"_ls); + auto query = database.prepareQuery("DELETE FROM master_keys WHERE userId=:userId;"_ls); query.bindValue(":userId"_ls, userId); - database->execute(query); - query = database->prepareQuery("INSERT INTO master_keys(userId, key) VALUES(:userId, :key);"_ls); + database.execute(query); + query = database.prepareQuery("INSERT INTO master_keys(userId, key) VALUES(:userId, :key);"_ls); query.bindValue(":userId"_ls, userId); query.bindValue(":key"_ls, key.keys.values()[0]); - database->execute(query); + database.execute(query); } - for (const auto &[userId, key] : asKeyValueRange(job->selfSigningKeys())) { + for (const auto &[userId, key] : asKeyValueRange(selfSigningKeys)) { if (key.userId != userId) { qCWarning(E2EE) << "Self signing key: userId mismatch"; continue; @@ -428,27 +428,27 @@ void ConnectionEncryptionData::handleQueryKeys(const QHashprepareQuery("SELECT key FROM master_keys WHERE userId=:userId"_ls); + auto masterKeyQuery = database.prepareQuery("SELECT key FROM master_keys WHERE userId=:userId"_ls); masterKeyQuery.bindValue(":userId"_ls, userId); - database->execute(masterKeyQuery); + database.execute(masterKeyQuery); if (!masterKeyQuery.next()) { continue; } auto masterKey = masterKeyQuery.value("key"_ls).toString(); - auto checkQuery = database->prepareQuery("SELECT key FROM self_signing_keys WHERE userId=:userId;"_ls); + auto checkQuery = database.prepareQuery("SELECT key FROM self_signing_keys WHERE userId=:userId;"_ls); checkQuery.bindValue(":userId"_ls, userId); - database->execute(checkQuery); + database.execute(checkQuery); if (checkQuery.next()) { auto oldKey = checkQuery.value("key"_ls).toString(); if (oldKey != key.keys.values()[0]) { qCWarning(E2EE) << "New self-signing key for" << userId << ". Marking all devices as unverified."; - database->transaction(); - auto query = database->prepareQuery( + database.transaction(); + auto query = database.prepareQuery( "UPDATE tracked_devices SET verified=0, selfVerified=0 WHERE matrixId=:matrixId;"_ls); query.bindValue(":matrixId"_ls, userId); - database->execute(query); - database->commit(); + database.execute(query); + database.commit(); } } @@ -457,15 +457,15 @@ void ConnectionEncryptionData::handleQueryKeys(const QHashprepareQuery("DELETE FROM self_signing_keys WHERE userId=:userId;"_ls); + auto query = database.prepareQuery("DELETE FROM self_signing_keys WHERE userId=:userId;"_ls); query.bindValue(":userId"_ls, userId); - database->execute(query); - query = database->prepareQuery("INSERT INTO self_signing_keys(userId, key) VALUES(:userId, :key);"_ls); + database.execute(query); + query = database.prepareQuery("INSERT INTO self_signing_keys(userId, key) VALUES(:userId, :key);"_ls); query.bindValue(":userId"_ls, userId); query.bindValue(":key"_ls, key.keys.values()[0]); - database->execute(query); + database.execute(query); } - for (const auto &[userId, key] : asKeyValueRange(job->userSigningKeys())) { + for (const auto &[userId, key] : asKeyValueRange(userSigningKeys)) { if (key.userId != userId) { qWarning() << "User signing key: userId mismatch"; continue; @@ -474,25 +474,25 @@ void ConnectionEncryptionData::handleQueryKeys(const QHashprepareQuery("SELECT key FROM master_keys WHERE userId=:userId"_ls); + auto masterKeyQuery = database.prepareQuery("SELECT key FROM master_keys WHERE userId=:userId"_ls); masterKeyQuery.bindValue(":userId"_ls, userId); - database->execute(masterKeyQuery); + database.execute(masterKeyQuery); if (!masterKeyQuery.next()) { continue; } - auto checkQuery = database->prepareQuery("SELECT key FROM user_signing_keys WHERE userId=:userId"_ls); + auto checkQuery = database.prepareQuery("SELECT key FROM user_signing_keys WHERE userId=:userId"_ls); checkQuery.bindValue(":userId"_ls, userId); - database->execute(checkQuery); + database.execute(checkQuery); if (checkQuery.next()) { auto oldKey = checkQuery.value("key"_ls).toString(); if (oldKey != key.keys.values()[0]) { qCWarning(E2EE) << "New user signing key; marking all master signing keys as unverified"; - database->transaction(); - auto query = database->prepareQuery( + database.transaction(); + auto query = database.prepareQuery( "UPDATE master_keys SET verified=0;"_ls); - database->execute(query); - database->commit(); + database.execute(query); + database.commit(); } } @@ -502,26 +502,26 @@ void ConnectionEncryptionData::handleQueryKeys(const QHashprepareQuery("DELETE FROM user_signing_keys WHERE userId=:userId;"_ls); + auto query = database.prepareQuery("DELETE FROM user_signing_keys WHERE userId=:userId;"_ls); query.bindValue(":userId"_ls, userId); - database->execute(query); - query = database->prepareQuery("INSERT INTO user_signing_keys(userId, key) VALUES(:userId, :key);"_ls); + database.execute(query); + query = database.prepareQuery("INSERT INTO user_signing_keys(userId, key) VALUES(:userId, :key);"_ls); query.bindValue(":userId"_ls, userId); query.bindValue(":key"_ls, key.keys.values()[0]); - database->execute(query); + database.execute(query); } - database->commit(); + database.commit(); if (q->isUserVerified(q->userId())) { - auto query = database->prepareQuery("SELECT key FROM user_signing_keys WHERE userId=:userId;"_ls); + auto query = database.prepareQuery("SELECT key FROM user_signing_keys WHERE userId=:userId;"_ls); query.bindValue(":userId"_ls, q->userId()); - database->execute(query); + database.execute(query); query.next(); auto userSigningKey = query.value("key"_ls).toString(); - for (const auto& masterKey : job->masterKeys()) { + for (const auto& masterKey : masterKeys) { auto signature = masterKey.signatures[q->userId()]["ed25519:"_ls % userSigningKey].toString(); if (!signature.isEmpty()) { if (ed25519VerifySignature(userSigningKey, toJson(masterKey), signature)) { - database->setMasterKeyVerified(masterKey.keys.values()[0]); + database.setMasterKeyVerified(masterKey.keys.values()[0]); emit q->userVerified(masterKey.userId); } else { qCWarning(E2EE) << "Master key signature verification failed"; @@ -529,14 +529,13 @@ void ConnectionEncryptionData::handleQueryKeys(const QHashdeviceKeys(); - for(const auto &[user, keys] : asKeyValueRange(data)) { - QHash oldDevices = deviceKeys[user]; - auto query = database->prepareQuery("SELECT * FROM self_signing_keys WHERE userId=:userId;"_ls); + for(const auto &[user, keys] : asKeyValueRange(deviceKeys)) { + QHash oldDevices = this->deviceKeys[user]; + auto query = database.prepareQuery("SELECT * FROM self_signing_keys WHERE userId=:userId;"_ls); query.bindValue(":userId"_ls, user); - database->execute(query); + database.execute(query); auto selfSigningKey = query.next() ? query.value("key"_ls).toString() : QString(); - deviceKeys[user].clear(); + this->deviceKeys[user].clear(); selfVerifiedDevices[user].clear(); for(const auto &device : keys) { if(device.userId != user) { @@ -578,7 +577,7 @@ void ConnectionEncryptionData::handleQueryKeys(const QHashdeviceKeys[user][device.deviceId] = SLICE(device, DeviceKeys); } outdatedUsers -= user; } @@ -593,7 +592,7 @@ void ConnectionEncryptionData::handleQueryKeys(const QHash& pendingEvent) { if (!isKnownCurveKey( pendingEvent->fullJson()[SenderKey].toString(), - pendingEvent->contentPart(SenderKeyKeyL))) + pendingEvent->contentPart(SenderKey))) return false; handleEncryptedToDeviceEvent(*pendingEvent); return true; @@ -955,3 +954,45 @@ void ConnectionEncryptionData::saveOlmAccount() qCDebug(E2EE) << "Saving olm account"; database.storeOlmAccount(olmAccount); } + +void ConnectionEncryptionData::reloadDevices() +{ + outdatedUsers = trackedUsers; + loadOutdatedUserDevices(); +} + +bool ConnectionEncryptionData::hasConflictingDeviceIdsAndCrossSigningKeys(const QString& userId) +{ + auto devices = q->devicesForUser(userId); + + auto selfQuery = database.prepareQuery("SELECT key FROM self_signing_keys WHERE userId=:userId;"_ls); + selfQuery.bindValue(":userId"_ls, userId); + database.execute(selfQuery); + if (!selfQuery.next()) { + return false; + } + if (devices.contains(selfQuery.value("key"_ls).toString())) { + return true; + } + + auto masterQuery = database.prepareQuery("SELECT key FROM master_keys WHERE userId=:userId;"_ls); + masterQuery.bindValue(":userId"_ls, userId); + database.execute(masterQuery); + if (!masterQuery.next()) { + return false; + } + if (devices.contains(masterQuery.value("key"_ls).toString())) { + return true; + } + + auto userQuery = database.prepareQuery("SELECT key FROM user_signing_keys WHERE userId=:userId;"_ls); + userQuery.bindValue(":userId"_ls, userId); + database.execute(userQuery); + if (!userQuery.next()) { + return false; + } + if (devices.contains(userQuery.value("key"_ls).toString())) { + return true; + } + return false; +} diff --git a/Quotient/connectionencryptiondata_p.h b/Quotient/connectionencryptiondata_p.h index 6cfe7c920..267678f5c 100644 --- a/Quotient/connectionencryptiondata_p.h +++ b/Quotient/connectionencryptiondata_p.h @@ -6,6 +6,8 @@ #include "e2ee/qolmaccount.h" #include "e2ee/qolmsession.h" +#include "events/encryptedevent.h" + namespace Quotient { struct DevicesList; @@ -36,6 +38,7 @@ namespace _impl { std::vector> pendingEncryptedEvents{}; bool isUploadingKeys = false; bool firstSync = true; + QHash> selfVerifiedDevices; void saveDevicesList(); void loadDevicesList(); @@ -59,6 +62,7 @@ namespace _impl { QDateTime::currentDateTime()); } void saveOlmAccount(); + void reloadDevices(); std::pair sessionDecryptMessage( const QJsonObject& personalCipherObject, @@ -93,6 +97,7 @@ namespace _impl { // get an instance from setup() instead ConnectionEncryptionData(Connection* connection, PicklingKey&& picklingKey); + bool hasConflictingDeviceIdsAndCrossSigningKeys(const QString& userId); private: void consumeDevicesList(const DevicesList &devicesList); diff --git a/autotests/testcrosssigning.cpp b/autotests/testcrosssigning.cpp index d21a1c867..0cb80903b 100644 --- a/autotests/testcrosssigning.cpp +++ b/autotests/testcrosssigning.cpp @@ -9,6 +9,8 @@ #undef private #include "testutils.h" +using namespace Quotient; + class TestCrossSigning : public QObject { Q_OBJECT @@ -25,8 +27,8 @@ private Q_SLOTS: QVERIFY(!data.isEmpty()); auto json = QJsonDocument::fromJson(data).object(); - auto connection = Connection::makeMockConnection("@tobiasfella:kde.org"_ls); - connection->d->handleQueryKeys(fromJson>>(json["device_keys"_ls]), + auto connection = Connection::makeMockConnection("@tobiasfella:kde.org"_ls, true); + connection->d->encryptionData->handleQueryKeys(fromJson>>(json["device_keys"_ls]), fromJson>(json["master_keys"_ls]), fromJson>(json["self_signing_keys"_ls]), fromJson>(json["user_signing_keys"_ls]));