diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4b2418f1d..d0cf8904a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -282,6 +282,7 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN qml/ConsentDialog.qml qml/AskDirectChatConfirmation.qml qml/HoverLinkIndicator.qml + qml/CrossSigningSetupDialog.qml DEPENDENCIES QtCore QtQuick diff --git a/src/neochatconnection.cpp b/src/neochatconnection.cpp index edec55456..a3860486a 100644 --- a/src/neochatconnection.cpp +++ b/src/neochatconnection.cpp @@ -16,10 +16,14 @@ #include "spacehierarchycache.h" #include +#include +#include #include #include #include +#include + #include #include @@ -538,4 +542,95 @@ LinkPreviewer *NeoChatConnection::previewerForLink(const QUrl &link) return previewer; } +void NeoChatConnection::setupCrossSigningKeys(const QString &password) +{ + auto masterKeyPrivate = getRandom<32>(); + auto masterKeyContext = makeCStruct(olm_pk_signing, olm_pk_signing_size, olm_clear_pk_signing); + QByteArray masterKeyPublic(olm_pk_signing_public_key_length(), 0); + olm_pk_signing_key_from_seed(masterKeyContext.get(), + masterKeyPublic.data(), + masterKeyPublic.length(), + masterKeyPrivate.data(), + masterKeyPrivate.viewAsByteArray().length()); + + auto selfSigningKeyPrivate = getRandom<32>(); + auto selfSigningKeyContext = makeCStruct(olm_pk_signing, olm_pk_signing_size, olm_clear_pk_signing); + QByteArray selfSigningKeyPublic(olm_pk_signing_public_key_length(), 0); + olm_pk_signing_key_from_seed(selfSigningKeyContext.get(), + selfSigningKeyPublic.data(), + selfSigningKeyPublic.length(), + selfSigningKeyPrivate.data(), + selfSigningKeyPrivate.viewAsByteArray().length()); + + auto userSigningKeyPrivate = getRandom<32>(); + auto userSigningKeyContext = makeCStruct(olm_pk_signing, olm_pk_signing_size, olm_clear_pk_signing); + QByteArray userSigningKeyPublic(olm_pk_signing_public_key_length(), 0); + olm_pk_signing_key_from_seed(userSigningKeyContext.get(), + userSigningKeyPublic.data(), + userSigningKeyPublic.length(), + userSigningKeyPrivate.data(), + userSigningKeyPrivate.viewAsByteArray().length()); + + database()->storeEncrypted("m.cross_signing.master"_ls, masterKeyPrivate.viewAsByteArray()); + database()->storeEncrypted("m.cross_signing.self_signing"_ls, selfSigningKeyPrivate.viewAsByteArray()); + database()->storeEncrypted("m.cross_signing.user_signing"_ls, userSigningKeyPrivate.viewAsByteArray()); + + auto masterKey = CrossSigningKey{ + .userId = userId(), + .usage = {"master"_ls}, + .keys = {{"ed25519:"_ls + QString::fromLatin1(masterKeyPublic), QString::fromLatin1(masterKeyPublic)}}, + .signatures = {}, + }; + auto selfSigningKey = CrossSigningKey{ + .userId = userId(), + .usage = {"self_signing"_ls}, + .keys = {{"ed25519:"_ls + QString::fromLatin1(selfSigningKeyPublic), QString::fromLatin1(selfSigningKeyPublic)}}, + }; + auto userSigningKey = CrossSigningKey{ + .userId = userId(), + .usage = {"user_signing"_ls}, + .keys = {{"ed25519:"_ls + QString::fromLatin1(userSigningKeyPublic), QString::fromLatin1(userSigningKeyPublic)}}, + + }; + + auto selfSigningKeyJson = toJson(selfSigningKey); + selfSigningKeyJson.remove("signatures"_ls); + selfSigningKey.signatures = QJsonObject{ + {userId(), + QJsonObject{{"ed25519:"_ls + QString::fromLatin1(masterKeyPublic), + QString::fromLatin1(sign(masterKeyPrivate.viewAsByteArray(), QJsonDocument(selfSigningKeyJson).toJson(QJsonDocument::Compact)))}}}}; + auto userSigningKeyJson = toJson(userSigningKey); + userSigningKeyJson.remove("signatures"_ls); + userSigningKey.signatures = QJsonObject{ + {userId(), + QJsonObject{{"ed25519:"_ls + QString::fromLatin1(masterKeyPublic), + QString::fromLatin1(sign(masterKeyPrivate.viewAsByteArray(), QJsonDocument(userSigningKeyJson).toJson(QJsonDocument::Compact)))}}}}; + + auto job = callApi(masterKey, selfSigningKey, userSigningKey, std::nullopt); + connect(job, &BaseJob::failure, this, [this, masterKey, selfSigningKey, userSigningKey, password](const auto &job) { + callApi(masterKey, + selfSigningKey, + userSigningKey, + AuthenticationData{ + .type = "m.login.password"_ls, + .session = job->jsonData()["session"_ls].toString(), + .authInfo = + QVariantHash{ + {"password"_ls, password}, + {"identifier"_ls, + QJsonObject{ + {"type"_ls, "m.id.user"_ls}, + {"user"_ls, userId()}, + }}, + }, + + }) + .then([](const auto &job) { + // TODO mark key as verified + // TODO store keys in accountdata + qWarning() << "finished uploading cs keys" << job->jsonData() << job->errorString(); + }); + }); +} + #include "moc_neochatconnection.cpp" diff --git a/src/neochatconnection.h b/src/neochatconnection.h index 4c57766ee..16a78955a 100644 --- a/src/neochatconnection.h +++ b/src/neochatconnection.h @@ -184,6 +184,8 @@ public: LinkPreviewer *previewerForLink(const QUrl &link); + Q_INVOKABLE void setupCrossSigningKeys(const QString &password); + Q_SIGNALS: void labelChanged(); void identityServerChanged(); diff --git a/src/qml/CrossSigningSetupDialog.qml b/src/qml/CrossSigningSetupDialog.qml new file mode 100644 index 000000000..5909cd5ff --- /dev/null +++ b/src/qml/CrossSigningSetupDialog.qml @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2024 Tobias Fella +// SPDX-License-Identifier: GPL-2.0-or-later + +import QtQuick +import QtQuick.Controls as QQC2 +import org.kde.kirigami as Kirigami + +import org.kde.neochat + +Kirigami.PromptDialog { + id: root + + required property NeoChatConnection connection + + title: i18nc("@title:dialog", "Setup Encryption Keys") + dialogType: Kirigami.PromptDialog.Information + + onRejected: { + root.close(); + } + + footer: QQC2.DialogButtonBox { + standardButtons: QQC2.Dialog.Cancel + + QQC2.Button { + text: i18nc("@action:button", "Setup Keys") + QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole + onClicked: { + root.connection.setupCrossSigningKeys("password123"); + } + } + } +} diff --git a/src/qml/Main.qml b/src/qml/Main.qml index 318446053..9a5742d55 100644 --- a/src/qml/Main.qml +++ b/src/qml/Main.qml @@ -309,6 +309,12 @@ Kirigami.ApplicationWindow { url: url }).open(); } + + function onCrossSigningSetupRequired() { + Qt.createComponent("org.kde.neochat", "CrossSigningSetupDialog").createObject(this, { + connection: root.connection + }).open(); + } } HoverLinkIndicator {