summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ChatPage.h18
-rw-r--r--src/DeviceVerificationFlow.cpp613
-rw-r--r--src/DeviceVerificationFlow.h215
-rw-r--r--src/Olm.cpp32
-rw-r--r--src/timeline/EventStore.cpp16
-rw-r--r--src/timeline/TimelineModel.cpp4
-rw-r--r--src/timeline/TimelineViewManager.cpp194
-rw-r--r--src/timeline/TimelineViewManager.h25
-rw-r--r--src/ui/UserProfile.cpp55
-rw-r--r--src/ui/UserProfile.h10
10 files changed, 601 insertions, 581 deletions
diff --git a/src/ChatPage.h b/src/ChatPage.h

index f363c4fe..c37aa915 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<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); diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index 7b367de9..99fd7bed 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<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() { @@ -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<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_) +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<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(); - - mtx::requests::ToDeviceMessages<mtx::events::msg::KeyVerificationRequest> body; + req.timestamp = (uint64_t)currentTime.toMSecsSinceEpoch(); - 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; - - 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); - } + 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> +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<int>(err->status_code)); - return; - } + QSharedPointer<DeviceVerificationFlow> 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> +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(); -void -DeviceVerificationFlow::unverify() + flow->handleStartMessage(msg, ""); + + return flow; +} +QSharedPointer<DeviceVerificationFlow> +DeviceVerificationFlow::InitiateUserVerification(QObject *parent_, + TimelineModel *timelineModel_, + QString userid) { - cache::markDeviceUnverified(this->userId.toStdString(), this->deviceId.toStdString()); + 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)); - 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 de7a4567..1fe3919b 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h
@@ -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 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<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(); + // for to_device messages + std::string transaction_id; + // for room messages + std::optional<std::string> room_id; + std::optional<std::string> 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<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; 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()); + } }; diff --git a/src/Olm.cpp b/src/Olm.cpp
index 8219ce4c..f4cb2209 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp
@@ -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)) { diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp
index 29b3c239..dc92a37f 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<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; } } diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 5e8952fc..e8d381df 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<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 diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 03dd4773..250cd5f0 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp
@@ -29,22 +29,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() { auto decrypt = settings->decryptSidebar(); @@ -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); @@ -429,6 +365,46 @@ TimelineViewManager::openRoomSettings() const } 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) { diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 4779d3cd..12e49080 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<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) diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp
index 2ea3f7b6..2a1eecdf 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 <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()); +} diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h
index de55b6ab..18933727 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<std::string> cross_verified; DeviceInfoModel deviceList_; bool isUserVerified = false; + TimelineViewManager *manager; TimelineModel *model; void callback_fn(const mtx::responses::QueryKeys &res,