Make steps in verification flow explicit

This commit is contained in:
Nicolas Werner 2020-10-05 22:12:10 +02:00
parent 2a79cd2b6b
commit bca29a4227
23 changed files with 707 additions and 804 deletions

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}
}
}
}

View File

@ -27,9 +27,8 @@ Pane {
text: qsTr("Cancel")
onClicked: {
flow.cancelVerification(DeviceVerificationFlow.User);
deviceVerificationList.remove(tran_id);
dialog.destroy();
flow.cancel();
dialog.close();
}
}
Item {

View File

@ -18,36 +18,31 @@ ApplicationWindow {
height: stack.implicitHeight
width: stack.implicitWidth
StackView {
id: stack
initialItem: newVerificationRequest
implicitWidth: currentItem.implicitWidth
implicitHeight: currentItem.implicitHeight
}
Component{
id: newVerificationRequest
NewVerificationRequest {}
}
Component{
id: acceptNewVerificationRequest
AcceptNewVerificationRequest {}
}
StackView {
id: stack
initialItem: flow.sender == true?newVerificationRequest:acceptNewVerificationRequest
implicitWidth: currentItem.implicitWidth
implicitHeight: currentItem.implicitHeight
Component {
id: waiting
Waiting {}
}
Component {
id: partnerAborted
PartnerAborted {}
id: success
Success {}
}
Component {
id: timedout
TimedOut {}
}
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)
Item {
state: flow.state
onVerificationRequestAccepted: switch(method) {
case DeviceVerificationFlow.Decimal: stack.replace(digitVerification); break;
case DeviceVerificationFlow.Emoji: stack.replace(emojiVerification); break;
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);
}
}
]
}
}

View File

@ -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();
}
}
}

View File

@ -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()
}
}
}

View File

@ -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()
}
}
}

View File

@ -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();
}
}
}
}

View File

@ -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();
}
}
}
}
}

View File

@ -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();
}
}
}

View File

@ -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 {

View File

@ -141,16 +141,13 @@
<file>qml/delegates/Pill.qml</file>
<file>qml/delegates/Placeholder.qml</file>
<file>qml/delegates/Reply.qml</file>
<file>qml/device-verification/AcceptNewVerificationRequest.qml</file>
<file>qml/device-verification/AwaitingVerificationConfirmation.qml</file>
<file>qml/device-verification/AwaitingVerificationRequest.qml</file>
<file>qml/device-verification/Waiting.qml</file>
<file>qml/device-verification/DeviceVerification.qml</file>
<file>qml/device-verification/DigitVerification.qml</file>
<file>qml/device-verification/EmojiVerification.qml</file>
<file>qml/device-verification/NewVerificationRequest.qml</file>
<file>qml/device-verification/PartnerAborted.qml</file>
<file>qml/device-verification/TimedOut.qml</file>
<file>qml/device-verification/VerificationSuccess.qml</file>
<file>qml/device-verification/Failed.qml</file>
<file>qml/device-verification/Success.qml</file>
</qresource>
<qresource prefix="/media">
<file>media/ring.ogg</file>

View File

@ -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<mtx::events::msg::KeyVerificationRequest> &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);

View File

@ -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<mtx::identifiers::User>(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<int>(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()
{
@ -319,51 +358,6 @@ DeviceVerificationFlow::getSasList()
return this->sasList;
}
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<mtx::identifiers::User>(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_)
{
this->type = type_;
}
void
DeviceVerificationFlow::setSender(bool sender_)
{
this->sender = sender_;
if (this->sender)
this->transaction_id = http::client()->generate_txn_id();
}
void
DeviceVerificationFlow::setEventId(std::string event_id_)
{
@ -372,6 +366,53 @@ DeviceVerificationFlow::setEventId(std::string event_id_)
this->transaction_id = event_id_;
}
void
DeviceVerificationFlow::handleStartMessage(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::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);
}
}
if (state_ != PromptStartVerification)
this->acceptVerificationRequest();
} else {
this->cancelVerification(DeviceVerificationFlow::Error::UnknownMethod);
}
}
//! accepts a verification
void
DeviceVerificationFlow::acceptVerificationRequest()
@ -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<mtx::events::msg::KeyVerificationAccept> body;
req.transaction_id = this->transaction_id;
body[this->toClient][this->deviceId.toStdString()] = req;
http::client()->send_to_device<mtx::events::msg::KeyVerificationAccept>(
this->transaction_id, body, [](mtx::http::RequestErr err) {
if (err)
nhlog::net()->warn("failed to accept verification request: {} {}",
err->matrix_error.error,
static_cast<int>(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<mtx::events::msg::KeyVerificationReady> body;
body[this->toClient][this->deviceId.toStdString()] = req;
http::client()->send_to_device<mtx::events::msg::KeyVerificationReady>(
this->transaction_id, body, [](mtx::http::RequestErr err) {
if (err)
nhlog::net()->warn("failed to send verification ready: {} {}",
err->matrix_error.error,
static_cast<int>(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<mtx::events::msg::KeyVerificationDone> body;
req.transaction_id = this->transaction_id;
body[this->toClient][this->deviceId.toStdString()] = req;
http::client()->send_to_device<mtx::events::msg::KeyVerificationDone>(
this->transaction_id, body, [](mtx::http::RequestErr err) {
if (err)
nhlog::net()->warn("failed to send verification done: {} {}",
err->matrix_error.error,
static_cast<int>(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<mtx::events::msg::KeyVerificationStart> 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<mtx::events::msg::KeyVerificationStart>(
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<int>(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();
req.timestamp = (uint64_t)currentTime.toMSecsSinceEpoch();
mtx::requests::ToDeviceMessages<mtx::events::msg::KeyVerificationRequest> body;
body[this->toClient][this->deviceId.toStdString()] = req;
http::client()->send_to_device<mtx::events::msg::KeyVerificationRequest>(
this->transaction_id, body, [](mtx::http::RequestErr err) {
if (err)
nhlog::net()->warn("failed to send verification request: {} {}",
err->matrix_error.error,
static_cast<int>(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<mtx::events::msg::KeyVerificationCancel> body;
body[this->toClient][deviceId.toStdString()] = req;
http::client()->send_to_device<mtx::events::msg::KeyVerificationCancel>(
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<int>(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<mtx::events::msg::KeyVerificationKey> body;
req.transaction_id = this->transaction_id;
body[this->toClient][deviceId.toStdString()] = req;
http::client()->send_to_device<mtx::events::msg::KeyVerificationKey>(
this->transaction_id, body, [](mtx::http::RequestErr err) {
if (err)
nhlog::net()->warn("failed to send verification key: {} {}",
err->matrix_error.error,
static_cast<int>(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<mtx::events::msg::KeyVerificationMac> body;
req.transaction_id = this->transaction_id;
body[this->toClient][deviceId.toStdString()] = req;
send(req);
http::client()->send_to_device<mtx::events::msg::KeyVerificationMac>(
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<int>(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);
}
setState(WaitingForMac);
acceptDevice();
}
//! Completes the verification flow
void
DeviceVerificationFlow::acceptDevice()
{
cache::markDeviceVerified(this->userId.toStdString(), 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)
{
if (err) {
nhlog::net()->warn("failed to query device keys: {},{}",
err->matrix_error.errcode,
static_cast<int>(err->status_code));
return;
}
if (res.device_keys.empty() ||
(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 (!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->userId.toStdString(), this->deviceId.toStdString());
cache::markDeviceUnverified(this->toClient.to_string(), this->deviceId.toStdString());
emit refreshProfile();
}
QSharedPointer<DeviceVerificationFlow>
DeviceVerificationFlow::NewInRoomVerification(QObject *parent_,
TimelineModel *timelineModel_,
const mtx::events::msg::KeyVerificationRequest &msg,
QString other_user_,
QString event_id_)
{
QSharedPointer<DeviceVerificationFlow> flow(
new DeviceVerificationFlow(parent_, Type::RoomMsg, timelineModel_, other_user_, ""));
flow->event_id = event_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>
DeviceVerificationFlow::NewToDeviceVerification(QObject *parent_,
const mtx::events::msg::KeyVerificationRequest &msg,
QString other_user_,
QString txn_id_)
{
QSharedPointer<DeviceVerificationFlow> 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>
DeviceVerificationFlow::NewToDeviceVerification(QObject *parent_,
const mtx::events::msg::KeyVerificationStart &msg,
QString other_user_,
QString txn_id_)
{
QSharedPointer<DeviceVerificationFlow> flow(new DeviceVerificationFlow(
parent_, Type::ToDevice, nullptr, other_user_, QString::fromStdString(msg.from_device)));
flow->transaction_id = txn_id_.toStdString();
flow->handleStartMessage(msg, "");
return flow;
}
QSharedPointer<DeviceVerificationFlow>
DeviceVerificationFlow::InitiateUserVerification(QObject *parent_,
TimelineModel *timelineModel_,
QString userid)
{
QSharedPointer<DeviceVerificationFlow> flow(
new DeviceVerificationFlow(parent_, Type::RoomMsg, timelineModel_, userid, ""));
flow->sender = true;
return flow;
}
QSharedPointer<DeviceVerificationFlow>
DeviceVerificationFlow::InitiateDeviceVerification(QObject *parent_, QString userid, QString device)
{
QSharedPointer<DeviceVerificationFlow> flow(
new DeviceVerificationFlow(parent_, Type::ToDevice, nullptr, userid, device));
flow->sender = true;
flow->transaction_id = http::client()->generate_txn_id();
return flow;
}

View File

@ -5,41 +5,84 @@
#include <mtx/responses/crypto.hpp>
#include "CacheCryptoStructs.h"
#include "Logging.h"
#include "MatrixClient.h"
#include "Olm.h"
#include "timeline/TimelineModel.h"
class QTimer;
using sas_ptr = std::unique_ptr<mtx::crypto::SAS>;
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<int> sasList READ getSasList CONSTANT)
public:
enum State
{
PromptStartVerification,
WaitingForOtherToAccept,
WaitingForKeys,
CompareEmoji,
CompareNumber,
WaitingForMac,
Success,
Failed,
};
Q_ENUM(State)
enum Type
{
ToDevice,
RoomMsg
};
Q_ENUM(Type)
enum Method
{
Decimal,
Emoji
};
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<DeviceVerificationFlow> NewInRoomVerification(
QObject *parent_,
TimelineModel *timelineModel_,
const mtx::events::msg::KeyVerificationRequest &msg,
QString other_user_,
QString event_id_);
static QSharedPointer<DeviceVerificationFlow> NewToDeviceVerification(
QObject *parent_,
const mtx::events::msg::KeyVerificationRequest &msg,
QString other_user_,
QString txn_id_);
static QSharedPointer<DeviceVerificationFlow> NewToDeviceVerification(
QObject *parent_,
const mtx::events::msg::KeyVerificationStart &msg,
QString other_user_,
QString txn_id_);
static QSharedPointer<DeviceVerificationFlow>
InitiateUserVerification(QObject *parent_, TimelineModel *timelineModel_, QString userid);
static QSharedPointer<DeviceVerificationFlow> 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<int> getSasList();
bool getSender();
std::vector<int> 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();
private:
// general
QString userId;
QString deviceId;
Method method = Method::Emoji;
Type type;
bool sender;
QTimer *timeout = nullptr;
sas_ptr sas;
bool isMacVerified = false;
std::string mac_method;
std::string commitment;
mtx::identifiers::User toClient;
std::vector<int> sasList;
std::map<std::string, std::string> device_keys;
// for to_device messages
std::string transaction_id;
// for room messages
std::optional<std::string> room_id;
std::optional<std::string> event_id;
bool sender;
Type type;
mtx::identifiers::User toClient;
QString deviceId;
mtx::events::msg::SASMethods method = mtx::events::msg::SASMethods::Emoji;
QTimer *timeout = nullptr;
sas_ptr sas;
std::string mac_method;
std::string commitment;
nlohmann::json canonical_json;
std::vector<int> sasList;
std::map<std::string, std::string> device_keys;
TimelineModel *model_;
mtx::common::RelatesTo relation;
State state_ = PromptStartVerification;
Error error_;
bool isMacVerified = false;
template<typename T>
void send(T msg)
{
if (this->type == DeviceVerificationFlow::Type::ToDevice) {
mtx::requests::ToDeviceMessages<T> body;
msg.transaction_id = this->transaction_id;
body[this->toClient][deviceId.toStdString()] = msg;
http::client()->send_to_device<T>(
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<int>(err->status_code));
});
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
if constexpr (!std::is_same_v<T, mtx::events::msg::KeyVerificationRequest>)
msg.relates_to = this->relation;
(model_)->sendMessageEvent(msg, mtx::events::to_device_content_to_type<T>);
}
nhlog::net()->debug(
"Sent verification step: {} in state: {}",
mtx::events::to_string(mtx::events::to_device_content_to_type<T>),
state().toStdString());
}
};

View File

@ -80,40 +80,40 @@ handle_to_device_messages(const std::vector<mtx::events::collections::DeviceEven
} else if (msg_type == to_string(mtx::events::EventType::KeyVerificationAccept)) {
auto message = std::get<
mtx::events::DeviceEvent<mtx::events::msg::KeyVerificationAccept>>(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<mtx::events::msg::KeyVerificationRequest>>(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<mtx::events::msg::KeyVerificationCancel>>(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<mtx::events::DeviceEvent<mtx::events::msg::KeyVerificationKey>>(
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<mtx::events::DeviceEvent<mtx::events::msg::KeyVerificationMac>>(
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<mtx::events::msg::KeyVerificationStart>>(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<mtx::events::msg::KeyVerificationReady>>(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<mtx::events::DeviceEvent<mtx::events::msg::KeyVerificationDone>>(
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)) {

View File

@ -299,7 +299,7 @@ EventStore::handleSync(const mtx::responses::Timeline &events)
mtx::events::msg::KeyVerificationReady>>(d_event)) {
auto msg = std::get_if<mtx::events::RoomEvent<
mtx::events::msg::KeyVerificationReady>>(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<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationCancel>>(event);
last_verification_cancel_event = msg;
ChatPage::instance()->recievedDeviceVerificationCancel(msg.content);
ChatPage::instance()->receivedDeviceVerificationCancel(msg.content);
return;
} else if (std::get_if<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationAccept>>(
&event)) {
auto msg =
std::get<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationAccept>>(event);
ChatPage::instance()->recievedDeviceVerificationAccept(msg.content);
ChatPage::instance()->receivedDeviceVerificationAccept(msg.content);
return;
} else if (std::get_if<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationKey>>(
&event)) {
auto msg =
std::get<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationKey>>(event);
ChatPage::instance()->recievedDeviceVerificationKey(msg.content);
ChatPage::instance()->receivedDeviceVerificationKey(msg.content);
return;
} else if (std::get_if<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationMac>>(
&event)) {
auto msg =
std::get<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationMac>>(event);
ChatPage::instance()->recievedDeviceVerificationMac(msg.content);
ChatPage::instance()->receivedDeviceVerificationMac(msg.content);
return;
} else if (std::get_if<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationReady>>(
&event)) {
auto msg =
std::get<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationReady>>(event);
ChatPage::instance()->recievedDeviceVerificationReady(msg.content);
ChatPage::instance()->receivedDeviceVerificationReady(msg.content);
return;
} else if (std::get_if<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationDone>>(
&event)) {
auto msg =
std::get<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationDone>>(event);
ChatPage::instance()->recievedDeviceVerificationDone(msg.content);
ChatPage::instance()->receivedDeviceVerificationDone(msg.content);
return;
} else if (std::get_if<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationStart>>(
&event)) {
auto msg =
std::get<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationStart>>(event);
ChatPage::instance()->recievedDeviceVerificationStart(msg.content, msg.sender);
ChatPage::instance()->receivedDeviceVerificationStart(msg.content, msg.sender);
return;
}
}

View File

@ -254,7 +254,7 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj
&EventStore::startDMVerification,
this,
[this](mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> 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

View File

@ -28,22 +28,6 @@ Q_DECLARE_METATYPE(std::vector<DeviceInfo>)
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<UserSettings> userSettin
qmlRegisterType<DelegateChoice>("im.nheko", 1, 0, "DelegateChoice");
qmlRegisterType<DelegateChooser>("im.nheko", 1, 0, "DelegateChooser");
qmlRegisterType<DeviceVerificationFlow>("im.nheko", 1, 0, "DeviceVerificationFlow");
qmlRegisterUncreatableType<DeviceVerificationFlow>(
"im.nheko", 1, 0, "DeviceVerificationFlow", "Can't create verification flow from QML!");
qmlRegisterUncreatableType<UserProfile>(
"im.nheko",
1,
@ -163,7 +148,6 @@ TimelineViewManager::TimelineViewManager(QSharedPointer<UserSettings> userSettin
0,
"EmojiCategory",
"Error: Only enums");
this->dvList = new DeviceVerificationList;
#ifdef USE_QUICK_VIEW
view = new QQuickView();
@ -183,7 +167,6 @@ TimelineViewManager::TimelineViewManager(QSharedPointer<UserSettings> 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<UserSettings> userSettin
&TimelineViewManager::updateEncryptedDescriptions);
connect(
dynamic_cast<ChatPage *>(parent),
&ChatPage::recievedRoomDeviceVerificationRequest,
&ChatPage::receivedRoomDeviceVerificationRequest,
this,
[this](const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> &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<ChatPage *>(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<ChatPage *>(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<ChatPage *>(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<ChatPage *>(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<QString> &event_ids)

View File

@ -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<QString> 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<UserSettings> settings;
QHash<QString, QColor> userColors;
DeviceVerificationList *dvList;
QHash<QString, QSharedPointer<DeviceVerificationFlow>> dvList;
};
Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationAccept)
Q_DECLARE_METATYPE(mtx::events::msg::KeyVerificationCancel)

View File

@ -6,13 +6,18 @@
#include "Utils.h"
#include "mtx/responses/crypto.hpp"
#include "timeline/TimelineModel.h"
#include "timeline/TimelineViewManager.h"
#include <iostream> // 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());
}

View File

@ -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<std::string> cross_verified;
DeviceInfoModel deviceList_;
bool isUserVerified = false;
TimelineViewManager *manager;
TimelineModel *model;
void callback_fn(const mtx::responses::QueryKeys &res,