From bca29a4227a871caac21236c29430b69264018ce Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Mon, 5 Oct 2020 22:12:10 +0200 Subject: [PATCH] Make steps in verification flow explicit --- resources/qml/TimelineView.qml | 14 +- resources/qml/UserProfile.qml | 31 +- .../AcceptNewVerificationRequest.qml | 49 -- .../AwaitingVerificationConfirmation.qml | 5 +- .../DeviceVerification.qml | 80 ++- .../device-verification/DigitVerification.qml | 12 +- .../device-verification/EmojiVerification.qml | 12 +- .../{TimedOut.qml => Failed.qml} | 11 +- .../NewVerificationRequest.qml | 37 +- .../device-verification/PartnerAborted.qml | 34 - .../{VerificationSuccess.qml => Success.qml} | 6 +- ...ingVerificationRequest.qml => Waiting.qml} | 13 +- resources/res.qrc | 9 +- src/ChatPage.h | 18 +- src/DeviceVerificationFlow.cpp | 613 +++++++++--------- src/DeviceVerificationFlow.h | 215 ++++-- src/Olm.cpp | 32 +- src/timeline/EventStore.cpp | 16 +- src/timeline/TimelineModel.cpp | 4 +- src/timeline/TimelineViewManager.cpp | 194 +++--- src/timeline/TimelineViewManager.h | 25 +- src/ui/UserProfile.cpp | 55 +- src/ui/UserProfile.h | 10 +- 23 files changed, 699 insertions(+), 796 deletions(-) delete mode 100644 resources/qml/device-verification/AcceptNewVerificationRequest.qml rename resources/qml/device-verification/{TimedOut.qml => Failed.qml} (50%) delete mode 100644 resources/qml/device-verification/PartnerAborted.qml rename resources/qml/device-verification/{VerificationSuccess.qml => Success.qml} (82%) rename resources/qml/device-verification/{AwaitingVerificationRequest.qml => Waiting.qml} (59%) diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 3f72a7d..1dbe7c1 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -106,19 +106,7 @@ Page { Connections { target: TimelineManager function onNewDeviceVerificationRequest(flow,transactionId,userId,deviceId,isRequest) { - flow.userId = userId; - flow.sender = false; - flow.deviceId = deviceId; - switch(flow.type){ - case DeviceVerificationFlow.ToDevice: - flow.tranId = transactionId; - deviceVerificationList.add(flow.tranId); - break; - case DeviceVerificationFlow.RoomMsg: - deviceVerificationList.add(flow.tranId); - break; - } - var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: flow,isRequest: isRequest,tran_id: flow.tranId}); + var dialog = deviceVerificationDialog.createObject(timelineRoot, {flow: flow}); dialog.show(); } } diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index dc6bc16..e7dcc77 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -15,24 +15,12 @@ ApplicationWindow{ width: 420 minimumHeight: 420 - modality: Qt.WindowModal palette: colors - Connections{ - target: deviceVerificationList - function onUpdateProfile() { - profile.fetchDeviceList(profile.userid) - } - } - Component { id: deviceVerificationDialog DeviceVerification {} } - Component{ - id: deviceVerificationFlow - DeviceVerificationFlow {} - } ColumnLayout{ id: contentL @@ -73,14 +61,7 @@ ApplicationWindow{ enabled: !profile.isUserVerified visible: !profile.isUserVerified - onClicked: { - var newFlow = profile.createFlow(true); - newFlow.userId = profile.userid; - newFlow.sender = true; - deviceVerificationList.add(newFlow.tranId); - var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow,isRequest: true,tran_id: newFlow.tranId}); - dialog.show(); - } + onClicked: profile.verify() } RowLayout { @@ -172,17 +153,11 @@ ApplicationWindow{ id: verifyButton text: (model.verificationStatus != VerificationStatus.VERIFIED)?"Verify":"Unverify" onClicked: { - var newFlow = profile.createFlow(false); - newFlow.userId = profile.userid; - newFlow.sender = true; - newFlow.deviceId = model.deviceId; if(model.verificationStatus == VerificationStatus.VERIFIED){ - newFlow.unverify(); + profile.unverify(model.deviceId) deviceVerificationList.updateProfile(newFlow.userId); }else{ - deviceVerificationList.add(newFlow.tranId); - var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow,isRequest:false,tran_id: newFlow.tranId}); - dialog.show(); + profile.verify(model.deviceId); } } } diff --git a/resources/qml/device-verification/AcceptNewVerificationRequest.qml b/resources/qml/device-verification/AcceptNewVerificationRequest.qml deleted file mode 100644 index 5bdbc4a..0000000 --- a/resources/qml/device-verification/AcceptNewVerificationRequest.qml +++ /dev/null @@ -1,49 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 2.10 -import QtQuick.Layouts 1.10 - -import im.nheko 1.0 - -Pane { - property string title: qsTr("Recieving Device Verification Request") - Component { - id: awaitingVerificationRequestAccept - AwaitingVerificationRequest {} - } - ColumnLayout { - spacing: 16 - Label { - Layout.maximumWidth: 400 - Layout.fillHeight: true - Layout.fillWidth: true - wrapMode: Text.Wrap - text: qsTr("The device was requested to be verified") - color:colors.text - verticalAlignment: Text.AlignVCenter - } - RowLayout { - Button { - Layout.alignment: Qt.AlignLeft - text: qsTr("Deny") - - onClicked: { - flow.cancelVerification(DeviceVerificationFlow.User); - deviceVerificationList.remove(tran_id); - dialog.destroy(); - } - } - Item { - Layout.fillWidth: true - } - Button { - Layout.alignment: Qt.AlignRight - text: qsTr("Accept") - - onClicked: { - stack.replace(awaitingVerificationRequestAccept); - flow.sender ?flow.sendVerificationReady():flow.acceptVerificationRequest(); - } - } - } - } -} diff --git a/resources/qml/device-verification/AwaitingVerificationConfirmation.qml b/resources/qml/device-verification/AwaitingVerificationConfirmation.qml index aaebba6..cd8ccfd 100644 --- a/resources/qml/device-verification/AwaitingVerificationConfirmation.qml +++ b/resources/qml/device-verification/AwaitingVerificationConfirmation.qml @@ -27,9 +27,8 @@ Pane { text: qsTr("Cancel") onClicked: { - flow.cancelVerification(DeviceVerificationFlow.User); - deviceVerificationList.remove(tran_id); - dialog.destroy(); + flow.cancel(); + dialog.close(); } } Item { diff --git a/resources/qml/device-verification/DeviceVerification.qml b/resources/qml/device-verification/DeviceVerification.qml index ca98098..4e93df0 100644 --- a/resources/qml/device-verification/DeviceVerification.qml +++ b/resources/qml/device-verification/DeviceVerification.qml @@ -18,36 +18,31 @@ ApplicationWindow { height: stack.implicitHeight width: stack.implicitWidth - Component{ - id: newVerificationRequest - NewVerificationRequest {} - } - - Component{ - id: acceptNewVerificationRequest - AcceptNewVerificationRequest {} - } - StackView { id: stack - initialItem: flow.sender == true?newVerificationRequest:acceptNewVerificationRequest + initialItem: newVerificationRequest implicitWidth: currentItem.implicitWidth implicitHeight: currentItem.implicitHeight } + Component{ + id: newVerificationRequest + NewVerificationRequest {} + } + Component { - id: partnerAborted - PartnerAborted {} + id: waiting + Waiting {} } Component { - id: timedout - TimedOut {} + id: success + Success {} } Component { - id: verificationSuccess - VerificationSuccess {} + id: failed + Failed {} } Component { @@ -60,19 +55,42 @@ ApplicationWindow { EmojiVerification {} } - Connections { - target: flow - onVerificationCanceled: stack.replace(partnerAborted) - onTimedout: stack.replace(timedout) - onDeviceVerified: stack.replace(verificationSuccess) - - onVerificationRequestAccepted: switch(method) { - case DeviceVerificationFlow.Decimal: stack.replace(digitVerification); break; - case DeviceVerificationFlow.Emoji: stack.replace(emojiVerification); break; + Item { + state: flow.state + + states: [ + State { + name: "PromptStartVerification" + StateChangeScript { script: stack.replace(newVerificationRequest) } + }, + State { + name: "CompareEmoji" + StateChangeScript { script: stack.replace(emojiVerification) } + }, + State { + name: "CompareNumber" + StateChangeScript { script: stack.replace(digitVerification) } + }, + State { + name: "WaitingForKeys" + StateChangeScript { script: stack.replace(waiting) } + }, + State { + name: "WaitingForOtherToAccept" + StateChangeScript { script: stack.replace(waiting) } + }, + State { + name: "WaitingForMac" + StateChangeScript { script: stack.replace(waiting) } + }, + State { + name: "Success" + StateChangeScript { script: stack.replace(success) } + }, + State { + name: "Failed" + StateChangeScript { script: stack.replace(failed); } } - - onRefreshProfile: { - deviceVerificationList.updateProfile(flow.userId); - } - } + ] +} } diff --git a/resources/qml/device-verification/DigitVerification.qml b/resources/qml/device-verification/DigitVerification.qml index f3b1f5c..ff878a5 100644 --- a/resources/qml/device-verification/DigitVerification.qml +++ b/resources/qml/device-verification/DigitVerification.qml @@ -6,10 +6,7 @@ import im.nheko 1.0 Pane { property string title: qsTr("Verification Code") - Component { - id: awaitingVerificationConfirmation - AwaitingVerificationConfirmation {} - } + ColumnLayout { spacing: 16 Label { @@ -45,9 +42,8 @@ Pane { text: qsTr("They do not match!") onClicked: { - flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS); - deviceVerificationList.remove(tran_id); - dialog.destroy(); + flow.cancel(); + dialog.close(); } } Item { @@ -57,7 +53,7 @@ Pane { Layout.alignment: Qt.AlignRight text: qsTr("They match!") - onClicked: { stack.replace(awaitingVerificationConfirmation); flow.sendVerificationMac(); } + onClicked: flow.next(); } } } diff --git a/resources/qml/device-verification/EmojiVerification.qml b/resources/qml/device-verification/EmojiVerification.qml index 19faf1b..ed7727a 100644 --- a/resources/qml/device-verification/EmojiVerification.qml +++ b/resources/qml/device-verification/EmojiVerification.qml @@ -6,10 +6,7 @@ import im.nheko 1.0 Pane { property string title: qsTr("Verification Code") - Component { - id: awaitingVerificationConfirmation - AwaitingVerificationConfirmation{} - } + ColumnLayout { spacing: 16 Label { @@ -125,9 +122,8 @@ Pane { text: qsTr("They do not match!") onClicked: { - flow.cancelVerification(DeviceVerificationFlow.MismatchedSAS); - deviceVerificationList.remove(tran_id); - dialog.destroy(); + flow.cancel(); + dialog.close(); } } Item { @@ -137,7 +133,7 @@ Pane { Layout.alignment: Qt.AlignRight text: qsTr("They match!") - onClicked: { stack.replace(awaitingVerificationConfirmation); flow.sendVerificationMac(); } + onClicked: flow.next() } } } diff --git a/resources/qml/device-verification/TimedOut.qml b/resources/qml/device-verification/Failed.qml similarity index 50% rename from resources/qml/device-verification/TimedOut.qml rename to resources/qml/device-verification/Failed.qml index 7dd0ab6..6b5d57e 100644 --- a/resources/qml/device-verification/TimedOut.qml +++ b/resources/qml/device-verification/Failed.qml @@ -12,7 +12,14 @@ Pane { Layout.fillWidth: true wrapMode: Text.Wrap id: content - text: qsTr("Device verification timed out.") + text: switch (flow.error) { + case VerificationStatus.UnknownMethod: return qsTr("Device verification timed out.") + case VerificationStatus.MismatchedCommitment: return qsTr("Device verification timed out.") + case VerificationStatus.MismatchedSAS: return qsTr("Device verification timed out.") + case VerificationStatus.KeyMismatch: return qsTr("Device verification timed out.") + case VerificationStatus.Timeout: return qsTr("Device verification timed out.") + case VerificationStatus.OutOfOrder: return qsTr("Device verification timed out.") + } color:colors.text verticalAlignment: Text.AlignVCenter } @@ -27,7 +34,7 @@ Pane { onClicked: { deviceVerificationList.remove(tran_id); flow.deleteFlow(); - dialog.destroy() + dialog.close() } } } diff --git a/resources/qml/device-verification/NewVerificationRequest.qml b/resources/qml/device-verification/NewVerificationRequest.qml index ef730b1..bd25bb9 100644 --- a/resources/qml/device-verification/NewVerificationRequest.qml +++ b/resources/qml/device-verification/NewVerificationRequest.qml @@ -2,12 +2,11 @@ import QtQuick 2.3 import QtQuick.Controls 2.10 import QtQuick.Layouts 1.10 +import im.nheko 1.0 + Pane { - property string title: qsTr("Sending Device Verification Request") - Component { - id: awaitingVerificationRequestAccept - AwaitingVerificationRequest {} - } + property string title: flow.sender ? qsTr("Send Device Verification Request") : qsTr("Recieved Device Verification Request") + ColumnLayout { spacing: 16 Label { @@ -15,28 +14,20 @@ Pane { Layout.fillHeight: true Layout.fillWidth: true wrapMode: Text.Wrap - text: qsTr("A new device was added.") - color:colors.text - verticalAlignment: Text.AlignVCenter - } - Label { - Layout.maximumWidth: 400 - Layout.fillHeight: true - Layout.fillWidth: true - wrapMode: Text.Wrap - text: qsTr("The device may have been added by you signing in from another client or physical device. To ensure that no malicious user can eavesdrop on your encrypted communications, you should verify the new device.") + text: flow.sender ? + qsTr("To ensure that no malicious user can eavesdrop on your encrypted communications, you can verify this device.") + : qsTr("The device was requested to be verified") color:colors.text verticalAlignment: Text.AlignVCenter } RowLayout { Button { Layout.alignment: Qt.AlignLeft - text: qsTr("Cancel") + text: flow.sender ? qsTr("Cancel") : qsTr("Deny") onClicked: { - deviceVerificationList.remove(tran_id); - flow.deleteFlow(); - dialog.destroy(); + flow.cancel(); + dialog.close(); } } Item { @@ -44,12 +35,10 @@ Pane { } Button { Layout.alignment: Qt.AlignRight - text: qsTr("Start verification") + text: flow.sender ? qsTr("Start verification") : qsTr("Accept") - onClicked: { - stack.replace(awaitingVerificationRequestAccept); - flow.sender ?flow.sendVerificationRequest():flow.startVerificationRequest(); } - } + onClicked: flow.next(); } } } +} diff --git a/resources/qml/device-verification/PartnerAborted.qml b/resources/qml/device-verification/PartnerAborted.qml deleted file mode 100644 index 6174477..0000000 --- a/resources/qml/device-verification/PartnerAborted.qml +++ /dev/null @@ -1,34 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 2.10 -import QtQuick.Layouts 1.10 - -Pane { - property string title: qsTr("Verification aborted!") - ColumnLayout { - spacing: 16 - Label { - Layout.maximumWidth: 400 - Layout.fillHeight: true - Layout.fillWidth: true - wrapMode: Text.Wrap - id: content - text: qsTr("Verification canceled by the other party!") - color:colors.text - verticalAlignment: Text.AlignVCenter - } - RowLayout { - Item { - Layout.fillWidth: true - } - Button { - Layout.alignment: Qt.AlignRight - text: qsTr("Close") - - onClicked: { - deviceVerificationList.remove(tran_id); - dialog.destroy(); - } - } - } - } -} diff --git a/resources/qml/device-verification/VerificationSuccess.qml b/resources/qml/device-verification/Success.qml similarity index 82% rename from resources/qml/device-verification/VerificationSuccess.qml rename to resources/qml/device-verification/Success.qml index bc1e64f..b17b293 100644 --- a/resources/qml/device-verification/VerificationSuccess.qml +++ b/resources/qml/device-verification/Success.qml @@ -24,11 +24,7 @@ Pane { Layout.alignment: Qt.AlignRight text: qsTr("Close") - onClicked: { - deviceVerificationList.remove(tran_id); - if(flow) flow.deleteFlow(); - dialog.destroy(); - } + onClicked: dialog.close(); } } } diff --git a/resources/qml/device-verification/AwaitingVerificationRequest.qml b/resources/qml/device-verification/Waiting.qml similarity index 59% rename from resources/qml/device-verification/AwaitingVerificationRequest.qml rename to resources/qml/device-verification/Waiting.qml index b4b9178..f36910e 100644 --- a/resources/qml/device-verification/AwaitingVerificationRequest.qml +++ b/resources/qml/device-verification/Waiting.qml @@ -14,12 +14,18 @@ Pane { Layout.fillWidth: true wrapMode: Text.Wrap id: content - text: qsTr("Waiting for other side to accept the verification request.") + text: switch (flow.state) { + case "WaitingForOtherToAccept": return qsTr("Waiting for other side to accept the verification request.") + case "WaitingForKeys": return qsTr("Waiting for other side to continue the verification request.") + case "WaitingForMac": return qsTr("Waiting for other side to complete the verification request.") + } + color:colors.text verticalAlignment: Text.AlignVCenter } BusyIndicator { Layout.alignment: Qt.AlignHCenter + palette: color } RowLayout { Button { @@ -27,9 +33,8 @@ Pane { text: qsTr("Cancel") onClicked: { - flow.cancelVerification(DeviceVerificationFlow.User); - deviceVerificationList.remove(tran_id); - dialog.destroy(); + flow.cancel(); + dialog.close(); } } Item { diff --git a/resources/res.qrc b/resources/res.qrc index 7ef7ecf..64e5b08 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -141,16 +141,13 @@ qml/delegates/Pill.qml qml/delegates/Placeholder.qml qml/delegates/Reply.qml - qml/device-verification/AcceptNewVerificationRequest.qml - qml/device-verification/AwaitingVerificationConfirmation.qml - qml/device-verification/AwaitingVerificationRequest.qml + qml/device-verification/Waiting.qml qml/device-verification/DeviceVerification.qml qml/device-verification/DigitVerification.qml qml/device-verification/EmojiVerification.qml qml/device-verification/NewVerificationRequest.qml - qml/device-verification/PartnerAborted.qml - qml/device-verification/TimedOut.qml - qml/device-verification/VerificationSuccess.qml + qml/device-verification/Failed.qml + qml/device-verification/Success.qml media/ring.ogg diff --git a/src/ChatPage.h b/src/ChatPage.h index f363c4f..c37aa91 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -169,22 +169,22 @@ signals: void decryptSidebarChanged(); //! Signals for device verificaiton - void recievedDeviceVerificationAccept( + void receivedDeviceVerificationAccept( const mtx::events::msg::KeyVerificationAccept &message); - void recievedDeviceVerificationRequest( + void receivedDeviceVerificationRequest( const mtx::events::msg::KeyVerificationRequest &message, std::string sender); - void recievedRoomDeviceVerificationRequest( + void receivedRoomDeviceVerificationRequest( const mtx::events::RoomEvent &message, TimelineModel *model); - void recievedDeviceVerificationCancel( + void receivedDeviceVerificationCancel( const mtx::events::msg::KeyVerificationCancel &message); - void recievedDeviceVerificationKey(const mtx::events::msg::KeyVerificationKey &message); - void recievedDeviceVerificationMac(const mtx::events::msg::KeyVerificationMac &message); - void recievedDeviceVerificationStart(const mtx::events::msg::KeyVerificationStart &message, + void receivedDeviceVerificationKey(const mtx::events::msg::KeyVerificationKey &message); + void receivedDeviceVerificationMac(const mtx::events::msg::KeyVerificationMac &message); + void receivedDeviceVerificationStart(const mtx::events::msg::KeyVerificationStart &message, std::string sender); - void recievedDeviceVerificationReady(const mtx::events::msg::KeyVerificationReady &message); - void recievedDeviceVerificationDone(const mtx::events::msg::KeyVerificationDone &message); + void receivedDeviceVerificationReady(const mtx::events::msg::KeyVerificationReady &message); + void receivedDeviceVerificationDone(const mtx::events::msg::KeyVerificationDone &message); private slots: void showUnreadMessageNotification(int count); diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 7b367de..99fd7be 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -15,8 +15,12 @@ namespace msgs = mtx::events::msg; DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow::Type flow_type, - TimelineModel *model) - : type(flow_type) + TimelineModel *model, + QString userID, + QString deviceId_) + : sender(false) + , type(flow_type) + , deviceId(deviceId_) , model_(model) { timeout = new QTimer(this); @@ -24,6 +28,30 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, this->sas = olm::client()->sas_init(); this->isMacVerified = false; + auto user_id = userID.toStdString(); + this->toClient = mtx::identifiers::parse(user_id); + ChatPage::instance()->query_keys( + user_id, [user_id, this](const UserKeyCache &res, mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to query device keys: {},{}", + err->matrix_error.errcode, + static_cast(err->status_code)); + return; + } + + if (!this->deviceId.isEmpty() && + (res.device_keys.find(deviceId.toStdString()) == res.device_keys.end())) { + nhlog::net()->warn("no devices retrieved {}", user_id); + return; + } + + for (const auto &[algorithm, key] : + res.device_keys.at(deviceId.toStdString()).keys) { + // TODO: Verify Signatures + this->device_keys[algorithm] = key; + } + }); + if (model) { connect(this->model_, &TimelineModel::updateFlowEventId, @@ -36,64 +64,15 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, } connect(timeout, &QTimer::timeout, this, [this]() { - emit timedout(); this->cancelVerification(DeviceVerificationFlow::Error::Timeout); - this->deleteLater(); }); - connect(this, &DeviceVerificationFlow::deleteFlow, this, [this]() { this->deleteLater(); }); - - connect( - ChatPage::instance(), - &ChatPage::recievedDeviceVerificationStart, - this, - [this](const mtx::events::msg::KeyVerificationStart &msg, std::string) { - if (msg.transaction_id.has_value()) { - if (msg.transaction_id.value() != this->transaction_id) - return; - } else if (msg.relates_to.has_value()) { - if (msg.relates_to.value().event_id != this->relation.event_id) - return; - } - if ((std::find(msg.key_agreement_protocols.begin(), - msg.key_agreement_protocols.end(), - "curve25519-hkdf-sha256") != msg.key_agreement_protocols.end()) && - (std::find(msg.hashes.begin(), msg.hashes.end(), "sha256") != - msg.hashes.end()) && - (std::find(msg.message_authentication_codes.begin(), - msg.message_authentication_codes.end(), - "hkdf-hmac-sha256") != msg.message_authentication_codes.end())) { - if (std::find(msg.short_authentication_string.begin(), - msg.short_authentication_string.end(), - mtx::events::msg::SASMethods::Decimal) != - msg.short_authentication_string.end()) { - this->method = DeviceVerificationFlow::Method::Emoji; - } else if (std::find(msg.short_authentication_string.begin(), - msg.short_authentication_string.end(), - mtx::events::msg::SASMethods::Emoji) != - msg.short_authentication_string.end()) { - this->method = DeviceVerificationFlow::Method::Decimal; - } else { - this->cancelVerification( - DeviceVerificationFlow::Error::UnknownMethod); - return; - } - if (!sender) - this->canonical_json = nlohmann::json(msg); - else { - if (utils::localUser().toStdString() < - this->toClient.to_string()) { - this->canonical_json = nlohmann::json(msg); - } - } - this->acceptVerificationRequest(); - } else { - this->cancelVerification(DeviceVerificationFlow::Error::UnknownMethod); - } - }); - connect(ChatPage::instance(), - &ChatPage::recievedDeviceVerificationAccept, + &ChatPage::receivedDeviceVerificationStart, + this, + &DeviceVerificationFlow::handleStartMessage); + connect(ChatPage::instance(), + &ChatPage::receivedDeviceVerificationAccept, this, [this](const mtx::events::msg::KeyVerificationAccept &msg) { if (msg.transaction_id.has_value()) { @@ -111,9 +90,9 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, msg.short_authentication_string.end(), mtx::events::msg::SASMethods::Emoji) != msg.short_authentication_string.end()) { - this->method = DeviceVerificationFlow::Method::Emoji; + this->method = mtx::events::msg::SASMethods::Emoji; } else { - this->method = DeviceVerificationFlow::Method::Decimal; + this->method = mtx::events::msg::SASMethods::Decimal; } this->mac_method = msg.message_authentication_code; this->sendVerificationKey(); @@ -124,7 +103,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, }); connect(ChatPage::instance(), - &ChatPage::recievedDeviceVerificationCancel, + &ChatPage::receivedDeviceVerificationCancel, this, [this](const mtx::events::msg::KeyVerificationCancel &msg) { if (msg.transaction_id.has_value()) { @@ -134,12 +113,13 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, if (msg.relates_to.value().event_id != this->relation.event_id) return; } - this->deleteLater(); - emit verificationCanceled(); + error_ = User; + emit errorChanged(); + setState(Failed); }); connect(ChatPage::instance(), - &ChatPage::recievedDeviceVerificationKey, + &ChatPage::receivedDeviceVerificationKey, this, [this](const mtx::events::msg::KeyVerificationKey &msg) { if (msg.transaction_id.has_value()) { @@ -149,6 +129,19 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, if (msg.relates_to.value().event_id != this->relation.event_id) return; } + + if (sender) { + if (state_ != WaitingForOtherToAccept) { + this->cancelVerification(OutOfOrder); + return; + } + } else { + if (state_ != WaitingForKeys) { + this->cancelVerification(OutOfOrder); + return; + } + } + this->sas->set_their_key(msg.key); std::string info; if (this->sender == true) { @@ -166,31 +159,30 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, "|" + this->transaction_id; } - if (this->method == DeviceVerificationFlow::Method::Emoji) { - std::cout << info << std::endl; - this->sasList = this->sas->generate_bytes_emoji(info); - } else if (this->method == DeviceVerificationFlow::Method::Decimal) { - this->sasList = this->sas->generate_bytes_decimal(info); - } - if (this->sender == false) { - emit this->verificationRequestAccepted(this->method); this->sendVerificationKey(); } else { - if (this->commitment == + if (this->commitment != mtx::crypto::bin2base64_unpadded( mtx::crypto::sha256(msg.key + this->canonical_json.dump()))) { - emit this->verificationRequestAccepted(this->method); - } else { this->cancelVerification( DeviceVerificationFlow::Error::MismatchedCommitment); + return; } } + + if (this->method == mtx::events::msg::SASMethods::Emoji) { + this->sasList = this->sas->generate_bytes_emoji(info); + setState(CompareEmoji); + } else if (this->method == mtx::events::msg::SASMethods::Decimal) { + this->sasList = this->sas->generate_bytes_decimal(info); + setState(CompareNumber); + } }); connect( ChatPage::instance(), - &ChatPage::recievedDeviceVerificationMac, + &ChatPage::receivedDeviceVerificationMac, this, [this](const mtx::events::msg::KeyVerificationMac &msg) { if (msg.transaction_id.has_value()) { @@ -222,26 +214,22 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, } key_string = key_string.substr(0, key_string.length() - 1); if (msg.keys == this->sas->calculate_mac(key_string, info + "KEY_IDS")) { - // uncomment this in future to be compatible with the - // MSC2366 this->sendVerificationDone(); and remove the - // below line - if (this->isMacVerified == true) { - this->acceptDevice(); - } else - this->isMacVerified = true; + this->isMacVerified = true; + this->acceptDevice(); } else { this->cancelVerification(DeviceVerificationFlow::Error::KeyMismatch); } }); connect(ChatPage::instance(), - &ChatPage::recievedDeviceVerificationReady, + &ChatPage::receivedDeviceVerificationReady, this, [this](const mtx::events::msg::KeyVerificationReady &msg) { if (!sender) { if (msg.from_device != http::client()->device_id()) { - this->deleteLater(); - emit verificationCanceled(); + error_ = User; + emit errorChanged(); + setState(Failed); } return; @@ -261,7 +249,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, }); connect(ChatPage::instance(), - &ChatPage::recievedDeviceVerificationDone, + &ChatPage::receivedDeviceVerificationDone, this, [this](const mtx::events::msg::KeyVerificationDone &msg) { if (msg.transaction_id.has_value()) { @@ -271,22 +259,85 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, if (msg.relates_to.value().event_id != this->relation.event_id) return; } - this->acceptDevice(); + nhlog::ui()->info("Flow done on other side"); }); timeout->start(TIMEOUT); } QString -DeviceVerificationFlow::getTransactionId() +DeviceVerificationFlow::state() { - return QString::fromStdString(this->transaction_id); + switch (state_) { + case PromptStartVerification: + return "PromptStartVerification"; + case CompareEmoji: + return "CompareEmoji"; + case CompareNumber: + return "CompareNumber"; + case WaitingForKeys: + return "WaitingForKeys"; + case WaitingForOtherToAccept: + return "WaitingForOtherToAccept"; + case WaitingForMac: + return "WaitingForMac"; + case Success: + return "Success"; + case Failed: + return "Failed"; + default: + return ""; + } +} + +void +DeviceVerificationFlow::next() +{ + if (sender) { + switch (state_) { + case PromptStartVerification: + sendVerificationRequest(); + break; + case CompareEmoji: + case CompareNumber: + sendVerificationMac(); + break; + case WaitingForKeys: + case WaitingForOtherToAccept: + case WaitingForMac: + case Success: + case Failed: + nhlog::db()->error("verification: Invalid state transition!"); + break; + } + } else { + switch (state_) { + case PromptStartVerification: + if (canonical_json.is_null()) + sendVerificationReady(); + else // legacy path without request and ready + acceptVerificationRequest(); + break; + case CompareEmoji: + [[fallthrough]]; + case CompareNumber: + sendVerificationMac(); + break; + case WaitingForKeys: + case WaitingForOtherToAccept: + case WaitingForMac: + case Success: + case Failed: + nhlog::db()->error("verification: Invalid state transition!"); + break; + } + } } QString DeviceVerificationFlow::getUserId() { - return this->userId; + return QString::fromStdString(this->toClient.to_string()); } QString @@ -295,18 +346,6 @@ DeviceVerificationFlow::getDeviceId() return this->deviceId; } -DeviceVerificationFlow::Method -DeviceVerificationFlow::getMethod() -{ - return this->method; -} - -DeviceVerificationFlow::Type -DeviceVerificationFlow::getType() -{ - return this->type; -} - bool DeviceVerificationFlow::getSender() { @@ -320,56 +359,58 @@ DeviceVerificationFlow::getSasList() } void -DeviceVerificationFlow::setTransactionId(QString transaction_id_) -{ - this->transaction_id = transaction_id_.toStdString(); -} - -void -DeviceVerificationFlow::setUserId(QString userID) -{ - this->userId = userID; - this->toClient = mtx::identifiers::parse(userID.toStdString()); - - auto user_id = userID.toStdString(); - ChatPage::instance()->query_keys( - user_id, [user_id, this](const UserKeyCache &res, mtx::http::RequestErr err) { - this->callback_fn(res, err, user_id); - }); -} - -void -DeviceVerificationFlow::setDeviceId(QString deviceID) -{ - this->deviceId = deviceID; -} - -void -DeviceVerificationFlow::setMethod(DeviceVerificationFlow::Method method_) -{ - this->method = method_; -} - -void -DeviceVerificationFlow::setType(Type type_) +DeviceVerificationFlow::setEventId(std::string event_id_) { - this->type = type_; + this->relation.rel_type = mtx::common::RelationType::Reference; + this->relation.event_id = event_id_; + this->transaction_id = event_id_; } void -DeviceVerificationFlow::setSender(bool sender_) +DeviceVerificationFlow::handleStartMessage(const mtx::events::msg::KeyVerificationStart &msg, + std::string) { - this->sender = sender_; - if (this->sender) - this->transaction_id = http::client()->generate_txn_id(); -} + if (msg.transaction_id.has_value()) { + if (msg.transaction_id.value() != this->transaction_id) + return; + } else if (msg.relates_to.has_value()) { + if (msg.relates_to.value().event_id != this->relation.event_id) + return; + } + if ((std::find(msg.key_agreement_protocols.begin(), + msg.key_agreement_protocols.end(), + "curve25519-hkdf-sha256") != msg.key_agreement_protocols.end()) && + (std::find(msg.hashes.begin(), msg.hashes.end(), "sha256") != msg.hashes.end()) && + (std::find(msg.message_authentication_codes.begin(), + msg.message_authentication_codes.end(), + "hkdf-hmac-sha256") != msg.message_authentication_codes.end())) { + if (std::find(msg.short_authentication_string.begin(), + msg.short_authentication_string.end(), + mtx::events::msg::SASMethods::Emoji) != + msg.short_authentication_string.end()) { + this->method = mtx::events::msg::SASMethods::Emoji; + } else if (std::find(msg.short_authentication_string.begin(), + msg.short_authentication_string.end(), + mtx::events::msg::SASMethods::Decimal) != + msg.short_authentication_string.end()) { + this->method = mtx::events::msg::SASMethods::Decimal; + } else { + this->cancelVerification(DeviceVerificationFlow::Error::UnknownMethod); + return; + } + if (!sender) + this->canonical_json = nlohmann::json(msg); + else { + if (utils::localUser().toStdString() < this->toClient.to_string()) { + this->canonical_json = nlohmann::json(msg); + } + } -void -DeviceVerificationFlow::setEventId(std::string event_id_) -{ - this->relation.rel_type = mtx::common::RelationType::Reference; - this->relation.event_id = event_id_; - this->transaction_id = event_id_; + if (state_ != PromptStartVerification) + this->acceptVerificationRequest(); + } else { + this->cancelVerification(DeviceVerificationFlow::Error::UnknownMethod); + } } //! accepts a verification @@ -382,30 +423,15 @@ DeviceVerificationFlow::acceptVerificationRequest() req.key_agreement_protocol = "curve25519-hkdf-sha256"; req.hash = "sha256"; req.message_authentication_code = "hkdf-hmac-sha256"; - if (this->method == DeviceVerificationFlow::Method::Emoji) + if (this->method == mtx::events::msg::SASMethods::Emoji) req.short_authentication_string = {mtx::events::msg::SASMethods::Emoji}; - else if (this->method == DeviceVerificationFlow::Method::Decimal) + else if (this->method == mtx::events::msg::SASMethods::Decimal) req.short_authentication_string = {mtx::events::msg::SASMethods::Decimal}; req.commitment = mtx::crypto::bin2base64_unpadded( mtx::crypto::sha256(this->sas->public_key() + this->canonical_json.dump())); - if (this->type == DeviceVerificationFlow::Type::ToDevice) { - mtx::requests::ToDeviceMessages body; - req.transaction_id = this->transaction_id; - - body[this->toClient][this->deviceId.toStdString()] = req; - - http::client()->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to accept verification request: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - req.relates_to = this->relation; - (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationAccept); - } + send(req); + setState(WaitingForKeys); } //! responds verification request void @@ -416,23 +442,8 @@ DeviceVerificationFlow::sendVerificationReady() req.from_device = http::client()->device_id(); req.methods = {mtx::events::msg::VerificationMethods::SASv1}; - if (this->type == DeviceVerificationFlow::Type::ToDevice) { - req.transaction_id = this->transaction_id; - mtx::requests::ToDeviceMessages body; - - body[this->toClient][this->deviceId.toStdString()] = req; - - http::client()->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification ready: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - req.relates_to = this->relation; - (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationReady); - } + send(req); + setState(WaitingForKeys); } //! accepts a verification void @@ -440,23 +451,7 @@ DeviceVerificationFlow::sendVerificationDone() { mtx::events::msg::KeyVerificationDone req; - if (this->type == DeviceVerificationFlow::Type::ToDevice) { - mtx::requests::ToDeviceMessages body; - req.transaction_id = this->transaction_id; - - body[this->toClient][this->deviceId.toStdString()] = req; - - http::client()->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification done: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - req.relates_to = this->relation; - (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationDone); - } + send(req); } //! starts the verification flow void @@ -474,22 +469,14 @@ DeviceVerificationFlow::startVerificationRequest() if (this->type == DeviceVerificationFlow::Type::ToDevice) { mtx::requests::ToDeviceMessages body; - req.transaction_id = this->transaction_id; - this->canonical_json = nlohmann::json(req); - body[this->toClient][this->deviceId.toStdString()] = req; - - http::client()->send_to_device( - this->transaction_id, body, [body](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to start verification request: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); + req.transaction_id = this->transaction_id; + this->canonical_json = nlohmann::json(req); } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { req.relates_to = this->relation; this->canonical_json = nlohmann::json(req); - (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationStart); } + send(req); + setState(WaitingForOtherToAccept); } //! sends a verification request void @@ -503,28 +490,18 @@ DeviceVerificationFlow::sendVerificationRequest() if (this->type == DeviceVerificationFlow::Type::ToDevice) { QDateTime currentTime = QDateTime::currentDateTimeUtc(); - req.transaction_id = this->transaction_id; - req.timestamp = (uint64_t)currentTime.toMSecsSinceEpoch(); - - mtx::requests::ToDeviceMessages body; + req.timestamp = (uint64_t)currentTime.toMSecsSinceEpoch(); - body[this->toClient][this->deviceId.toStdString()] = req; - - http::client()->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification request: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - req.to = this->userId.toStdString(); + req.to = this->toClient.to_string(); req.msgtype = "m.key.verification.request"; req.body = "User is requesting to verify keys with you. However, your client does " "not support this method, so you will need to use the legacy method of " "key verification."; - (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationRequest); } + + send(req); + setState(WaitingForOtherToAccept); } //! cancels a verification flow void @@ -534,7 +511,7 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c if (error_code == DeviceVerificationFlow::Error::UnknownMethod) { req.code = "m.unknown_method"; - req.reason = "unknown method recieved"; + req.reason = "unknown method received"; } else if (error_code == DeviceVerificationFlow::Error::MismatchedCommitment) { req.code = "m.mismatched_commitment"; req.reason = "commitment didn't match"; @@ -550,42 +527,16 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c } else if (error_code == DeviceVerificationFlow::Error::User) { req.code = "m.user"; req.reason = "user cancelled the verification"; + } else if (error_code == DeviceVerificationFlow::Error::OutOfOrder) { + req.code = "m.unexpected_message"; + req.reason = "received messages out of order"; } - emit this->verificationCanceled(); + this->error_ = error_code; + emit errorChanged(); + this->setState(Failed); - if (this->type == DeviceVerificationFlow::Type::ToDevice) { - req.transaction_id = this->transaction_id; - mtx::requests::ToDeviceMessages body; - - body[this->toClient][deviceId.toStdString()] = req; - - http::client()->send_to_device( - this->transaction_id, body, [this](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to cancel verification request: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - - this->deleteLater(); - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - req.relates_to = this->relation; - (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationCancel); - this->deleteLater(); - } - - // TODO : Handle Blocking user better - // auto verified_cache = cache::getVerifiedCache(this->userId.toStdString()); - // if (verified_cache.has_value()) { - // verified_cache->device_blocked.push_back(this->deviceId.toStdString()); - // cache::setVerifiedCache(this->userId.toStdString(), - // verified_cache.value()); - // } else { - // cache::setVerifiedCache( - // this->userId.toStdString(), - // DeviceVerifiedCache{{}, {}, {this->deviceId.toStdString()}}); - // } + send(req); } //! sends the verification key void @@ -595,23 +546,7 @@ DeviceVerificationFlow::sendVerificationKey() req.key = this->sas->public_key(); - if (this->type == DeviceVerificationFlow::Type::ToDevice) { - mtx::requests::ToDeviceMessages body; - req.transaction_id = this->transaction_id; - - body[this->toClient][deviceId.toStdString()] = req; - - http::client()->send_to_device( - this->transaction_id, body, [](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification key: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - req.relates_to = this->relation; - (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationKey); - } + send(req); } mtx::events::msg::KeyVerificationMac @@ -660,68 +595,102 @@ DeviceVerificationFlow::sendVerificationMac() this->transaction_id, key_list); - if (this->type == DeviceVerificationFlow::Type::ToDevice) { - mtx::requests::ToDeviceMessages body; - req.transaction_id = this->transaction_id; - body[this->toClient][deviceId.toStdString()] = req; - - http::client()->send_to_device( - this->transaction_id, body, [this](mtx::http::RequestErr err) { - if (err) - nhlog::net()->warn("failed to send verification MAC: {} {}", - err->matrix_error.error, - static_cast(err->status_code)); - - if (this->isMacVerified == true) - this->acceptDevice(); - else - this->isMacVerified = true; - }); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - req.relates_to = this->relation; - (model_)->sendMessageEvent(req, mtx::events::EventType::KeyVerificationMac); - } + send(req); + + setState(WaitingForMac); + acceptDevice(); } //! Completes the verification flow void DeviceVerificationFlow::acceptDevice() { - cache::markDeviceVerified(this->userId.toStdString(), this->deviceId.toStdString()); + if (!isMacVerified) { + setState(WaitingForMac); + } else if (state_ == WaitingForMac) { + cache::markDeviceVerified(this->toClient.to_string(), this->deviceId.toStdString()); + this->sendVerificationDone(); + setState(Success); + } +} + +void +DeviceVerificationFlow::unverify() +{ + cache::markDeviceUnverified(this->toClient.to_string(), this->deviceId.toStdString()); - emit deviceVerified(); emit refreshProfile(); - this->deleteLater(); } -//! callback function to keep track of devices -void -DeviceVerificationFlow::callback_fn(const UserKeyCache &res, - mtx::http::RequestErr err, - std::string user_id) +QSharedPointer +DeviceVerificationFlow::NewInRoomVerification(QObject *parent_, + TimelineModel *timelineModel_, + const mtx::events::msg::KeyVerificationRequest &msg, + QString other_user_, + QString event_id_) { - if (err) { - nhlog::net()->warn("failed to query device keys: {},{}", - err->matrix_error.errcode, - static_cast(err->status_code)); - return; - } + QSharedPointer flow( + new DeviceVerificationFlow(parent_, Type::RoomMsg, timelineModel_, other_user_, "")); + + flow->event_id = event_id_.toStdString(); - if (res.device_keys.empty() || - (res.device_keys.find(deviceId.toStdString()) == res.device_keys.end())) { - nhlog::net()->warn("no devices retrieved {}", user_id); - return; + if (std::find(msg.methods.begin(), + msg.methods.end(), + mtx::events::msg::VerificationMethods::SASv1) == msg.methods.end()) { + flow->cancelVerification(UnknownMethod); } - for (const auto &[algorithm, key] : res.device_keys.at(deviceId.toStdString()).keys) { - // TODO: Verify Signatures - this->device_keys[algorithm] = key; + return flow; +} +QSharedPointer +DeviceVerificationFlow::NewToDeviceVerification(QObject *parent_, + const mtx::events::msg::KeyVerificationRequest &msg, + QString other_user_, + QString txn_id_) +{ + QSharedPointer flow(new DeviceVerificationFlow( + parent_, Type::ToDevice, nullptr, other_user_, QString::fromStdString(msg.from_device))); + flow->transaction_id = txn_id_.toStdString(); + + if (std::find(msg.methods.begin(), + msg.methods.end(), + mtx::events::msg::VerificationMethods::SASv1) == msg.methods.end()) { + flow->cancelVerification(UnknownMethod); } + + return flow; } +QSharedPointer +DeviceVerificationFlow::NewToDeviceVerification(QObject *parent_, + const mtx::events::msg::KeyVerificationStart &msg, + QString other_user_, + QString txn_id_) +{ + QSharedPointer flow(new DeviceVerificationFlow( + parent_, Type::ToDevice, nullptr, other_user_, QString::fromStdString(msg.from_device))); + flow->transaction_id = txn_id_.toStdString(); -void -DeviceVerificationFlow::unverify() + flow->handleStartMessage(msg, ""); + + return flow; +} +QSharedPointer +DeviceVerificationFlow::InitiateUserVerification(QObject *parent_, + TimelineModel *timelineModel_, + QString userid) { - cache::markDeviceUnverified(this->userId.toStdString(), this->deviceId.toStdString()); + QSharedPointer flow( + new DeviceVerificationFlow(parent_, Type::RoomMsg, timelineModel_, userid, "")); + flow->sender = true; + return flow; +} +QSharedPointer +DeviceVerificationFlow::InitiateDeviceVerification(QObject *parent_, QString userid, QString device) +{ + QSharedPointer flow( + new DeviceVerificationFlow(parent_, Type::ToDevice, nullptr, userid, device)); - emit refreshProfile(); + flow->sender = true; + flow->transaction_id = http::client()->generate_txn_id(); + + return flow; } diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h index de7a456..1fe3919 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h @@ -5,41 +5,84 @@ #include #include "CacheCryptoStructs.h" +#include "Logging.h" #include "MatrixClient.h" #include "Olm.h" +#include "timeline/TimelineModel.h" class QTimer; using sas_ptr = std::unique_ptr; -class TimelineModel; - +// clang-format off +/* + * Stolen from fluffy chat :D + * + * State | +-------------+ +-----------+ | + * | | AliceDevice | | BobDevice | | + * | | (sender) | | | | + * | +-------------+ +-----------+ | + * promptStartVerify | | | | + * | o | (m.key.verification.request) | | + * | p |-------------------------------->| (ASK FOR VERIFICATION REQUEST) | + * waitForOtherAccept | t | | | promptStartVerify + * && | i | (m.key.verification.ready) | | + * no commitment | o |<--------------------------------| | + * && | n | | | + * no canonical_json | a | (m.key.verification.start) | | waitingForKeys + * | l |<--------------------------------| Not sending to prevent the glare resolve| && no commitment + * | | | | && no canonical_json + * | | m.key.verification.start | | + * waitForOtherAccept | |-------------------------------->| (IF NOT ALREADY ASKED, | + * && | | | ASK FOR VERIFICATION REQUEST) | promptStartVerify, if not accepted + * canonical_json | | m.key.verification.accept | | + * | |<--------------------------------| | + * waitForOtherAccept | | | | waitingForKeys + * && | | m.key.verification.key | | && canonical_json + * commitment | |-------------------------------->| | && commitment + * | | | | + * | | m.key.verification.key | | + * | |<--------------------------------| | + * compareEmoji/Number| | | | compareEmoji/Number + * | | COMPARE EMOJI / NUMBERS | | + * | | | | + * waitingForMac | | m.key.verification.mac | | waitingForMac + * | success |<------------------------------->| success | + * | | | | + * success/fail | | m.key.verification.done | | success/fail + * | |<------------------------------->| | + */ +// clang-format on class DeviceVerificationFlow : public QObject { Q_OBJECT // Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") - Q_PROPERTY(QString tranId READ getTransactionId WRITE setTransactionId) - Q_PROPERTY(bool sender READ getSender WRITE setSender) - Q_PROPERTY(QString userId READ getUserId WRITE setUserId) - Q_PROPERTY(QString deviceId READ getDeviceId WRITE setDeviceId) - Q_PROPERTY(Method method READ getMethod WRITE setMethod) - Q_PROPERTY(Type type READ getType WRITE setType) + Q_PROPERTY(QString state READ state NOTIFY stateChanged) + Q_PROPERTY(Error error READ error CONSTANT) + Q_PROPERTY(QString userId READ getUserId CONSTANT) + Q_PROPERTY(QString deviceId READ getDeviceId CONSTANT) + Q_PROPERTY(bool sender READ getSender CONSTANT) Q_PROPERTY(std::vector sasList READ getSasList CONSTANT) public: - enum Type + enum State { - ToDevice, - RoomMsg + PromptStartVerification, + WaitingForOtherToAccept, + WaitingForKeys, + CompareEmoji, + CompareNumber, + WaitingForMac, + Success, + Failed, }; - Q_ENUM(Type) + Q_ENUM(State) - enum Method + enum Type { - Decimal, - Emoji + ToDevice, + RoomMsg }; - Q_ENUM(Method) enum Error { @@ -48,36 +91,75 @@ public: MismatchedSAS, KeyMismatch, Timeout, - User + User, + OutOfOrder, }; Q_ENUM(Error) - DeviceVerificationFlow( - QObject *parent = nullptr, - DeviceVerificationFlow::Type = DeviceVerificationFlow::Type::ToDevice, - TimelineModel *model = nullptr); + static QSharedPointer NewInRoomVerification( + QObject *parent_, + TimelineModel *timelineModel_, + const mtx::events::msg::KeyVerificationRequest &msg, + QString other_user_, + QString event_id_); + static QSharedPointer NewToDeviceVerification( + QObject *parent_, + const mtx::events::msg::KeyVerificationRequest &msg, + QString other_user_, + QString txn_id_); + static QSharedPointer NewToDeviceVerification( + QObject *parent_, + const mtx::events::msg::KeyVerificationStart &msg, + QString other_user_, + QString txn_id_); + static QSharedPointer + InitiateUserVerification(QObject *parent_, TimelineModel *timelineModel_, QString userid); + static QSharedPointer InitiateDeviceVerification(QObject *parent, + QString userid, + QString device); + // getters - QString getTransactionId(); + QString state(); + Error error() { return error_; } QString getUserId(); QString getDeviceId(); - Method getMethod(); - Type getType(); - std::vector getSasList(); bool getSender(); + std::vector getSasList(); + QString transactionId() { return QString::fromStdString(this->transaction_id); } // setters - void setTransactionId(QString transaction_id_); - void setUserId(QString userID); void setDeviceId(QString deviceID); - void setMethod(Method method_); - void setType(Type type_); - void setSender(bool sender_); void setEventId(std::string event_id); void callback_fn(const UserKeyCache &res, mtx::http::RequestErr err, std::string user_id); - nlohmann::json canonical_json; - public slots: + //! unverifies a device + void unverify(); + //! Continues the flow + void next(); + //! Cancel the flow + void cancel() { cancelVerification(User); } + +signals: + void refreshProfile(); + void stateChanged(); + void errorChanged(); + +private: + DeviceVerificationFlow(QObject *, + DeviceVerificationFlow::Type flow_type, + TimelineModel *model, + QString userID, + QString deviceId_); + void setState(State state) + { + if (state != state_) { + state_ = state; + emit stateChanged(); + } + } + + void handleStartMessage(const mtx::events::msg::KeyVerificationStart &msg, std::string); //! sends a verification request void sendVerificationRequest(); //! accepts a verification request @@ -96,37 +178,60 @@ public slots: void sendVerificationMac(); //! Completes the verification flow void acceptDevice(); - //! unverifies a device - void unverify(); -signals: - void verificationRequestAccepted(Method method); - void deviceVerified(); - void timedout(); - void verificationCanceled(); - void refreshProfile(); - void deleteFlow(); + // for to_device messages + std::string transaction_id; + // for room messages + std::optional room_id; + std::optional event_id; -private: - // general - QString userId; - QString deviceId; - Method method = Method::Emoji; - Type type; bool sender; - QTimer *timeout = nullptr; + Type type; + mtx::identifiers::User toClient; + QString deviceId; + + mtx::events::msg::SASMethods method = mtx::events::msg::SASMethods::Emoji; + QTimer *timeout = nullptr; sas_ptr sas; - bool isMacVerified = false; std::string mac_method; std::string commitment; - mtx::identifiers::User toClient; + nlohmann::json canonical_json; + std::vector sasList; std::map device_keys; - // for to_device messages - std::string transaction_id; - // for room messages - std::optional room_id; - std::optional event_id; TimelineModel *model_; mtx::common::RelatesTo relation; + + State state_ = PromptStartVerification; + Error error_; + + bool isMacVerified = false; + + template + void send(T msg) + { + if (this->type == DeviceVerificationFlow::Type::ToDevice) { + mtx::requests::ToDeviceMessages body; + msg.transaction_id = this->transaction_id; + body[this->toClient][deviceId.toStdString()] = msg; + + http::client()->send_to_device( + this->transaction_id, body, [this](mtx::http::RequestErr err) { + if (err) + nhlog::net()->warn( + "failed to send verification to_device message: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + }); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { + if constexpr (!std::is_same_v) + msg.relates_to = this->relation; + (model_)->sendMessageEvent(msg, mtx::events::to_device_content_to_type); + } + + nhlog::net()->debug( + "Sent verification step: {} in state: {}", + mtx::events::to_string(mtx::events::to_device_content_to_type), + state().toStdString()); + } }; diff --git a/src/Olm.cpp b/src/Olm.cpp index 8219ce4..f4cb220 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp @@ -80,40 +80,40 @@ handle_to_device_messages(const std::vector>(msg); - ChatPage::instance()->recievedDeviceVerificationAccept(message.content); + ChatPage::instance()->receivedDeviceVerificationAccept(message.content); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationRequest)) { auto message = std::get< mtx::events::DeviceEvent>(msg); - ChatPage::instance()->recievedDeviceVerificationRequest(message.content, + ChatPage::instance()->receivedDeviceVerificationRequest(message.content, message.sender); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationCancel)) { auto message = std::get< mtx::events::DeviceEvent>(msg); - ChatPage::instance()->recievedDeviceVerificationCancel(message.content); + ChatPage::instance()->receivedDeviceVerificationCancel(message.content); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationKey)) { auto message = std::get>( msg); - ChatPage::instance()->recievedDeviceVerificationKey(message.content); + ChatPage::instance()->receivedDeviceVerificationKey(message.content); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationMac)) { auto message = std::get>( msg); - ChatPage::instance()->recievedDeviceVerificationMac(message.content); + ChatPage::instance()->receivedDeviceVerificationMac(message.content); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationStart)) { auto message = std::get< mtx::events::DeviceEvent>(msg); - ChatPage::instance()->recievedDeviceVerificationStart(message.content, + ChatPage::instance()->receivedDeviceVerificationStart(message.content, message.sender); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationReady)) { auto message = std::get< mtx::events::DeviceEvent>(msg); - ChatPage::instance()->recievedDeviceVerificationReady(message.content); + ChatPage::instance()->receivedDeviceVerificationReady(message.content); } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationDone)) { auto message = std::get>( msg); - ChatPage::instance()->recievedDeviceVerificationDone(message.content); + ChatPage::instance()->receivedDeviceVerificationDone(message.content); } else { nhlog::crypto()->warn("unhandled event: {}", j_msg.dump(2)); } @@ -153,42 +153,42 @@ handle_olm_message(const OlmMessage &msg) std::string msg_type = payload["type"]; if (msg_type == to_string(mtx::events::EventType::KeyVerificationAccept)) { - ChatPage::instance()->recievedDeviceVerificationAccept( + ChatPage::instance()->receivedDeviceVerificationAccept( payload["content"]); return; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationRequest)) { - ChatPage::instance()->recievedDeviceVerificationRequest( + ChatPage::instance()->receivedDeviceVerificationRequest( payload["content"], payload["sender"]); return; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationCancel)) { - ChatPage::instance()->recievedDeviceVerificationCancel( + ChatPage::instance()->receivedDeviceVerificationCancel( payload["content"]); return; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationKey)) { - ChatPage::instance()->recievedDeviceVerificationKey( + ChatPage::instance()->receivedDeviceVerificationKey( payload["content"]); return; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationMac)) { - ChatPage::instance()->recievedDeviceVerificationMac( + ChatPage::instance()->receivedDeviceVerificationMac( payload["content"]); return; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationStart)) { - ChatPage::instance()->recievedDeviceVerificationStart( + ChatPage::instance()->receivedDeviceVerificationStart( payload["content"], payload["sender"]); return; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationReady)) { - ChatPage::instance()->recievedDeviceVerificationReady( + ChatPage::instance()->receivedDeviceVerificationReady( payload["content"]); return; } else if (msg_type == to_string(mtx::events::EventType::KeyVerificationDone)) { - ChatPage::instance()->recievedDeviceVerificationDone( + ChatPage::instance()->receivedDeviceVerificationDone( payload["content"]); return; } else if (msg_type == to_string(mtx::events::EventType::RoomKey)) { diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp index 29b3c23..dc92a37 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp @@ -299,7 +299,7 @@ EventStore::handleSync(const mtx::responses::Timeline &events) mtx::events::msg::KeyVerificationReady>>(d_event)) { auto msg = std::get_if>(d_event); - ChatPage::instance()->recievedDeviceVerificationReady( + ChatPage::instance()->receivedDeviceVerificationReady( msg->content); } } @@ -328,43 +328,43 @@ EventStore::handle_room_verification(mtx::events::collections::TimelineEvents ev auto msg = std::get>(event); last_verification_cancel_event = msg; - ChatPage::instance()->recievedDeviceVerificationCancel(msg.content); + ChatPage::instance()->receivedDeviceVerificationCancel(msg.content); return; } else if (std::get_if>( &event)) { auto msg = std::get>(event); - ChatPage::instance()->recievedDeviceVerificationAccept(msg.content); + ChatPage::instance()->receivedDeviceVerificationAccept(msg.content); return; } else if (std::get_if>( &event)) { auto msg = std::get>(event); - ChatPage::instance()->recievedDeviceVerificationKey(msg.content); + ChatPage::instance()->receivedDeviceVerificationKey(msg.content); return; } else if (std::get_if>( &event)) { auto msg = std::get>(event); - ChatPage::instance()->recievedDeviceVerificationMac(msg.content); + ChatPage::instance()->receivedDeviceVerificationMac(msg.content); return; } else if (std::get_if>( &event)) { auto msg = std::get>(event); - ChatPage::instance()->recievedDeviceVerificationReady(msg.content); + ChatPage::instance()->receivedDeviceVerificationReady(msg.content); return; } else if (std::get_if>( &event)) { auto msg = std::get>(event); - ChatPage::instance()->recievedDeviceVerificationDone(msg.content); + ChatPage::instance()->receivedDeviceVerificationDone(msg.content); return; } else if (std::get_if>( &event)) { auto msg = std::get>(event); - ChatPage::instance()->recievedDeviceVerificationStart(msg.content, msg.sender); + ChatPage::instance()->receivedDeviceVerificationStart(msg.content, msg.sender); return; } } diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 5e8952f..e8d381d 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -254,7 +254,7 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj &EventStore::startDMVerification, this, [this](mtx::events::RoomEvent msg) { - ChatPage::instance()->recievedRoomDeviceVerificationRequest(msg, this); + ChatPage::instance()->receivedRoomDeviceVerificationRequest(msg, this); }); connect(&events, &EventStore::updateFlowEventId, this, [this](std::string event_id) { this->updateFlowEventId(event_id); @@ -793,7 +793,7 @@ TimelineModel::viewDecryptedRawMessage(QString id) const void TimelineModel::openUserProfile(QString userid) { - emit openProfile(new UserProfile(room_id_, userid, this)); + emit openProfile(new UserProfile(room_id_, userid, manager_, this)); } void diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 03dd477..250cd5f 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -28,22 +28,6 @@ Q_DECLARE_METATYPE(std::vector) namespace msgs = mtx::events::msg; -void -DeviceVerificationList::add(QString tran_id) -{ - this->deviceVerificationList.push_back(tran_id); -} -void -DeviceVerificationList::remove(QString tran_id) -{ - this->deviceVerificationList.removeOne(tran_id); -} -bool -DeviceVerificationList::exist(QString tran_id) -{ - return this->deviceVerificationList.contains(tran_id); -} - void TimelineViewManager::updateEncryptedDescriptions() { @@ -134,7 +118,8 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin qmlRegisterType("im.nheko", 1, 0, "DelegateChoice"); qmlRegisterType("im.nheko", 1, 0, "DelegateChooser"); - qmlRegisterType("im.nheko", 1, 0, "DeviceVerificationFlow"); + qmlRegisterUncreatableType( + "im.nheko", 1, 0, "DeviceVerificationFlow", "Can't create verification flow from QML!"); qmlRegisterUncreatableType( "im.nheko", 1, @@ -163,7 +148,6 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin 0, "EmojiCategory", "Error: Only enums"); - this->dvList = new DeviceVerificationList; #ifdef USE_QUICK_VIEW view = new QQuickView(); @@ -183,7 +167,6 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin }); #endif container->setMinimumSize(200, 200); - view->rootContext()->setContextProperty("deviceVerificationList", this->dvList); updateColorPalette(); view->engine()->addImageProvider("MxcImage", imgProvider); view->engine()->addImageProvider("colorimage", colorImgProvider); @@ -197,102 +180,55 @@ TimelineViewManager::TimelineViewManager(QSharedPointer userSettin &TimelineViewManager::updateEncryptedDescriptions); connect( dynamic_cast(parent), - &ChatPage::recievedRoomDeviceVerificationRequest, + &ChatPage::receivedRoomDeviceVerificationRequest, this, [this](const mtx::events::RoomEvent &message, TimelineModel *model) { - if (!(this->dvList->exist(QString::fromStdString(message.event_id)))) { - auto flow = new DeviceVerificationFlow( - this, DeviceVerificationFlow::Type::RoomMsg, model); - if (std::find(message.content.methods.begin(), - message.content.methods.end(), - mtx::events::msg::VerificationMethods::SASv1) != - message.content.methods.end()) { - flow->setEventId(message.event_id); - emit newDeviceVerificationRequest( - std::move(flow), - QString::fromStdString(message.event_id), - QString::fromStdString(message.sender), - QString::fromStdString(message.content.from_device), - true); - } else { - flow->cancelVerification( - DeviceVerificationFlow::Error::UnknownMethod); - } - } - }); - connect( - dynamic_cast(parent), - &ChatPage::recievedDeviceVerificationRequest, - this, - [this](const mtx::events::msg::KeyVerificationRequest &msg, std::string sender) { - if (!(this->dvList->exist(QString::fromStdString(msg.transaction_id.value())))) { - auto flow = new DeviceVerificationFlow(this); - if (std::find(msg.methods.begin(), - msg.methods.end(), - mtx::events::msg::VerificationMethods::SASv1) != - msg.methods.end()) { - emit newDeviceVerificationRequest( - std::move(flow), - QString::fromStdString(msg.transaction_id.value()), - QString::fromStdString(sender), - QString::fromStdString(msg.from_device)); - } else { - flow->cancelVerification( - DeviceVerificationFlow::Error::UnknownMethod); - } - } - }); - connect( - dynamic_cast(parent), - &ChatPage::recievedDeviceVerificationStart, - this, - [this](const mtx::events::msg::KeyVerificationStart &msg, std::string sender) { - if (msg.transaction_id.has_value()) { - if (!(this->dvList->exist( - QString::fromStdString(msg.transaction_id.value())))) { - auto flow = new DeviceVerificationFlow(this); - flow->canonical_json = nlohmann::json(msg); - if ((std::find(msg.key_agreement_protocols.begin(), - msg.key_agreement_protocols.end(), - "curve25519-hkdf-sha256") != - msg.key_agreement_protocols.end()) && - (std::find(msg.hashes.begin(), msg.hashes.end(), "sha256") != - msg.hashes.end()) && - (std::find(msg.message_authentication_codes.begin(), - msg.message_authentication_codes.end(), - "hmac-sha256") != - msg.message_authentication_codes.end())) { - if (std::find(msg.short_authentication_string.begin(), - msg.short_authentication_string.end(), - mtx::events::msg::SASMethods::Emoji) != - msg.short_authentication_string.end()) { - flow->setMethod( - DeviceVerificationFlow::Method::Emoji); - } else if (std::find( - msg.short_authentication_string.begin(), - msg.short_authentication_string.end(), - mtx::events::msg::SASMethods::Decimal) != - msg.short_authentication_string.end()) { - flow->setMethod( - DeviceVerificationFlow::Method::Decimal); - } else { - flow->cancelVerification( - DeviceVerificationFlow::Error::UnknownMethod); - return; - } - emit newDeviceVerificationRequest( - std::move(flow), - QString::fromStdString(msg.transaction_id.value()), - QString::fromStdString(sender), - QString::fromStdString(msg.from_device)); - } else { - flow->cancelVerification( - DeviceVerificationFlow::Error::UnknownMethod); - } + auto event_id = QString::fromStdString(message.event_id); + if (!this->dvList.contains(event_id)) { + if (auto flow = DeviceVerificationFlow::NewInRoomVerification( + this, + model, + message.content, + QString::fromStdString(message.sender), + event_id)) { + dvList[event_id] = flow; + emit newDeviceVerificationRequest(flow.data()); } } }); + connect(dynamic_cast(parent), + &ChatPage::receivedDeviceVerificationRequest, + this, + [this](const mtx::events::msg::KeyVerificationRequest &msg, std::string sender) { + if (!msg.transaction_id) + return; + + auto txnid = QString::fromStdString(msg.transaction_id.value()); + if (!this->dvList.contains(txnid)) { + if (auto flow = DeviceVerificationFlow::NewToDeviceVerification( + this, msg, QString::fromStdString(sender), txnid)) { + dvList[txnid] = flow; + emit newDeviceVerificationRequest(flow.data()); + } + } + }); + connect(dynamic_cast(parent), + &ChatPage::receivedDeviceVerificationStart, + this, + [this](const mtx::events::msg::KeyVerificationStart &msg, std::string sender) { + if (!msg.transaction_id) + return; + + auto txnid = QString::fromStdString(msg.transaction_id.value()); + if (!this->dvList.contains(txnid)) { + if (auto flow = DeviceVerificationFlow::NewToDeviceVerification( + this, msg, QString::fromStdString(sender), txnid)) { + dvList[txnid] = flow; + emit newDeviceVerificationRequest(flow.data()); + } + } + }); connect(parent, &ChatPage::loggedOut, this, [this]() { isInitialSync_ = true; emit initialSyncChanged(true); @@ -428,6 +364,46 @@ TimelineViewManager::openRoomSettings() const MainWindow::instance()->openRoomSettings(timeline_->roomId()); } +void +TimelineViewManager::verifyUser(QString userid) +{ + auto joined_rooms = cache::joinedRooms(); + auto room_infos = cache::getRoomInfo(joined_rooms); + + for (std::string room_id : joined_rooms) { + if ((room_infos[QString::fromStdString(room_id)].member_count == 2) && + cache::isRoomEncrypted(room_id)) { + auto room_members = cache::roomMembers(room_id); + if (std::find(room_members.begin(), + room_members.end(), + (userid).toStdString()) != room_members.end()) { + auto model = models.value(QString::fromStdString(room_id)); + auto flow = DeviceVerificationFlow::InitiateUserVerification( + this, model.data(), userid); + connect(model.data(), + &TimelineModel::updateFlowEventId, + this, + [this, flow](std::string eventId) { + dvList[QString::fromStdString(eventId)] = flow; + }); + emit newDeviceVerificationRequest(flow.data()); + return; + } + } + } + + emit ChatPage::instance()->showNotification( + tr("No share room with this user found. Create an " + "encrypted room with this user and try again.")); +} +void +TimelineViewManager::verifyDevice(QString userid, QString deviceid) +{ + auto flow = DeviceVerificationFlow::InitiateDeviceVerification(this, userid, deviceid); + this->dvList[flow->transactionId()] = flow; + emit newDeviceVerificationRequest(flow.data()); +} + void TimelineViewManager::updateReadReceipts(const QString &room_id, const std::vector &event_ids) diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 4779d3c..12e4908 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -24,20 +24,6 @@ class ColorImageProvider; class UserSettings; class ChatPage; -class DeviceVerificationList : public QObject -{ - Q_OBJECT -public: - Q_INVOKABLE void add(QString tran_id); - Q_INVOKABLE void remove(QString tran_id); - Q_INVOKABLE bool exist(QString tran_id); -signals: - void updateProfile(QString userId); - -private: - QVector deviceVerificationList; -}; - class TimelineViewManager : public QObject { Q_OBJECT @@ -77,6 +63,9 @@ public: Q_INVOKABLE void openLeaveRoomDialog() const; Q_INVOKABLE void openRoomSettings() const; + void verifyUser(QString userid); + void verifyDevice(QString userid, QString deviceid); + signals: void clearRoomMessageCount(QString roomid); void updateRoomsLastMessage(QString roomid, const DescInfo &info); @@ -84,11 +73,7 @@ signals: void initialSyncChanged(bool isInitialSync); void replyingEventChanged(QString replyingEvent); void replyClosed(); - void newDeviceVerificationRequest(DeviceVerificationFlow *flow, - QString transactionId, - QString userId, - QString deviceId, - bool isRequest = false); + void newDeviceVerificationRequest(DeviceVerificationFlow *flow); void inviteUsers(QStringList users); void showRoomList(); void narrowViewChanged(); @@ -180,7 +165,7 @@ private: QSharedPointer settings; QHash userColors; - DeviceVerificationList *dvList; + QHash> dvList; }; Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationAccept) Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationCancel) diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 2ea3f7b..2a1eecd 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -6,13 +6,18 @@ #include "Utils.h" #include "mtx/responses/crypto.hpp" #include "timeline/TimelineModel.h" +#include "timeline/TimelineViewManager.h" #include // only for debugging -UserProfile::UserProfile(QString roomid, QString userid, TimelineModel *parent) +UserProfile::UserProfile(QString roomid, + QString userid, + TimelineViewManager *manager_, + TimelineModel *parent) : QObject(parent) , roomid_(roomid) , userid_(userid) + , manager(manager_) , model(parent) { fetchDeviceList(this->userid_); @@ -270,44 +275,18 @@ UserProfile::startChat() emit ChatPage::instance()->createRoom(req); } -DeviceVerificationFlow * -UserProfile::createFlow(bool isVerifyUser) +void +UserProfile::verify(QString device) { - if (!isVerifyUser) - return (new DeviceVerificationFlow(this, DeviceVerificationFlow::Type::ToDevice)); + if (!device.isEmpty()) + manager->verifyDevice(userid_, device); else { - std::cout << "CHECKING IF IT TO START ROOM_VERIFICATION OR TO_DEVICE VERIFICATION" - << std::endl; - auto joined_rooms = cache::joinedRooms(); - auto room_infos = cache::getRoomInfo(joined_rooms); - - for (std::string room_id : joined_rooms) { - if ((room_infos[QString::fromStdString(room_id)].member_count == 2) && - cache::isRoomEncrypted(room_id)) { - auto room_members = cache::roomMembers(room_id); - if (std::find(room_members.begin(), - room_members.end(), - (this->userid()).toStdString()) != - room_members.end()) { - std::cout - << "FOUND A ENCRYPTED ROOM WITH THIS USER : " << room_id - << std::endl; - if (this->roomid_.toStdString() == room_id) { - auto newflow = new DeviceVerificationFlow( - this, - DeviceVerificationFlow::Type::RoomMsg, - this->model); - return (std::move(newflow)); - } else { - std::cout << "FOUND A ENCRYPTED ROOM BUT CURRENTLY " - "NOT IN THAT ROOM : " - << room_id << std::endl; - } - } - } - } - - std::cout << "DIDN'T FIND A ENCRYPTED ROOM WITH THIS USER" << std::endl; - return (new DeviceVerificationFlow(this, DeviceVerificationFlow::Type::ToDevice)); + manager->verifyUser(userid_); } } + +void +UserProfile::unverify(QString device) +{ + cache::markDeviceUnverified(userid_.toStdString(), device.toStdString()); +} diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index de55b6a..1893372 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -21,6 +21,7 @@ Q_ENUM_NS(Status) class DeviceVerificationFlow; class TimelineModel; +class TimelineViewManager; class DeviceInfo { @@ -84,7 +85,10 @@ class UserProfile : public QObject Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList CONSTANT) Q_PROPERTY(bool isUserVerified READ getUserStatus CONSTANT) public: - UserProfile(QString roomid, QString userid, TimelineModel *parent = nullptr); + UserProfile(QString roomid, + QString userid, + TimelineViewManager *manager_, + TimelineModel *parent = nullptr); DeviceInfoModel *deviceList(); @@ -93,7 +97,8 @@ public: QString avatarUrl(); bool getUserStatus(); - Q_INVOKABLE DeviceVerificationFlow *createFlow(bool isVerifyUser); + Q_INVOKABLE void verify(QString device = ""); + Q_INVOKABLE void unverify(QString device = ""); Q_INVOKABLE void fetchDeviceList(const QString &userID); Q_INVOKABLE void banUser(); // Q_INVOKABLE void ignoreUser(); @@ -105,6 +110,7 @@ private: std::optional cross_verified; DeviceInfoModel deviceList_; bool isUserVerified = false; + TimelineViewManager *manager; TimelineModel *model; void callback_fn(const mtx::responses::QueryKeys &res,