Create Cross-signing keys when required
This should be almost entirely in libquotient, but that's not prepared to actually use user-interactive authentication...
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -16,10 +16,14 @@
|
||||
#include "spacehierarchycache.h"
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
#include <Quotient/csapi/cross_signing.h>
|
||||
#include <Quotient/e2ee/cryptoutils.h>
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
#include <qt6keychain/keychain.h>
|
||||
|
||||
#include <olm/pk.h>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <Quotient/csapi/content-repo.h>
|
||||
@@ -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<UploadCrossSigningKeysJob>(masterKey, selfSigningKey, userSigningKey, std::nullopt);
|
||||
connect(job, &BaseJob::failure, this, [this, masterKey, selfSigningKey, userSigningKey, password](const auto &job) {
|
||||
callApi<UploadCrossSigningKeysJob>(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"
|
||||
|
||||
@@ -184,6 +184,8 @@ public:
|
||||
|
||||
LinkPreviewer *previewerForLink(const QUrl &link);
|
||||
|
||||
Q_INVOKABLE void setupCrossSigningKeys(const QString &password);
|
||||
|
||||
Q_SIGNALS:
|
||||
void labelChanged();
|
||||
void identityServerChanged();
|
||||
|
||||
33
src/qml/CrossSigningSetupDialog.qml
Normal file
33
src/qml/CrossSigningSetupDialog.qml
Normal file
@@ -0,0 +1,33 @@
|
||||
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -309,6 +309,12 @@ Kirigami.ApplicationWindow {
|
||||
url: url
|
||||
}).open();
|
||||
}
|
||||
|
||||
function onCrossSigningSetupRequired() {
|
||||
Qt.createComponent("org.kde.neochat", "CrossSigningSetupDialog").createObject(this, {
|
||||
connection: root.connection
|
||||
}).open();
|
||||
}
|
||||
}
|
||||
|
||||
HoverLinkIndicator {
|
||||
|
||||
Reference in New Issue
Block a user