diff options
Diffstat (limited to 'src/DeviceVerificationFlow.cpp')
-rw-r--r-- | src/DeviceVerificationFlow.cpp | 1279 |
1 files changed, 623 insertions, 656 deletions
diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp index 1760ea9a..d4e27022 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp @@ -38,685 +38,653 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, , deviceId(deviceId_) , model_(model) { - timeout = new QTimer(this); - timeout->setSingleShot(true); - this->sas = olm::client()->sas_init(); - this->isMacVerified = false; - - auto user_id = userID.toStdString(); - this->toClient = mtx::identifiers::parse<mtx::identifiers::User>(user_id); - cache::client()->query_keys( - user_id, [user_id, this](const UserKeyCache &res, mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to query device keys: {},{}", - mtx::errors::to_string(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; - } - - this->their_keys = res; - }); - - cache::client()->query_keys( - http::client()->user_id().to_string(), - [this](const UserKeyCache &res, mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to query device keys: {},{}", - mtx::errors::to_string(err->matrix_error.errcode), - static_cast<int>(err->status_code)); - return; - } - - if (res.master_keys.keys.empty()) - return; - - if (auto status = - cache::verificationStatus(http::client()->user_id().to_string()); - status && status->user_verified == crypto::Trust::Verified) - this->our_trusted_master_key = res.master_keys.keys.begin()->second; - }); - - if (model) { - connect(this->model_, - &TimelineModel::updateFlowEventId, - this, - [this](std::string event_id_) { - this->relation.rel_type = mtx::common::RelationType::Reference; - this->relation.event_id = event_id_; - this->transaction_id = event_id_; - }); - } - - connect(timeout, &QTimer::timeout, this, [this]() { - nhlog::crypto()->info("verification: timeout"); - if (state_ != Success && state_ != Failed) - this->cancelVerification(DeviceVerificationFlow::Error::Timeout); - }); - - connect(ChatPage::instance(), - &ChatPage::receivedDeviceVerificationStart, - this, - &DeviceVerificationFlow::handleStartMessage); - connect(ChatPage::instance(), - &ChatPage::receivedDeviceVerificationAccept, - this, - [this](const mtx::events::msg::KeyVerificationAccept &msg) { - nhlog::crypto()->info("verification: received accept"); - if (msg.transaction_id.has_value()) { - if (msg.transaction_id.value() != this->transaction_id) - return; - } else if (msg.relations.references()) { - if (msg.relations.references() != this->relation.event_id) - return; - } - if ((msg.key_agreement_protocol == "curve25519-hkdf-sha256") && - (msg.hash == "sha256") && - (msg.message_authentication_code == "hkdf-hmac-sha256")) { - this->commitment = msg.commitment; - 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 { - this->method = mtx::events::msg::SASMethods::Decimal; - } - this->mac_method = msg.message_authentication_code; - this->sendVerificationKey(); - } else { - this->cancelVerification( - DeviceVerificationFlow::Error::UnknownMethod); - } - }); - - connect(ChatPage::instance(), - &ChatPage::receivedDeviceVerificationCancel, - this, - [this](const mtx::events::msg::KeyVerificationCancel &msg) { - nhlog::crypto()->info("verification: received cancel"); - if (msg.transaction_id.has_value()) { - if (msg.transaction_id.value() != this->transaction_id) - return; - } else if (msg.relations.references()) { - if (msg.relations.references() != this->relation.event_id) - return; - } - error_ = User; - emit errorChanged(); - setState(Failed); - }); - - connect(ChatPage::instance(), - &ChatPage::receivedDeviceVerificationKey, - this, - [this](const mtx::events::msg::KeyVerificationKey &msg) { - nhlog::crypto()->info("verification: received key"); - if (msg.transaction_id.has_value()) { - if (msg.transaction_id.value() != this->transaction_id) - return; - } else if (msg.relations.references()) { - if (msg.relations.references() != 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) { - info = "MATRIX_KEY_VERIFICATION_SAS|" + - http::client()->user_id().to_string() + "|" + - http::client()->device_id() + "|" + this->sas->public_key() + - "|" + this->toClient.to_string() + "|" + - this->deviceId.toStdString() + "|" + msg.key + "|" + - this->transaction_id; - } else { - info = "MATRIX_KEY_VERIFICATION_SAS|" + this->toClient.to_string() + - "|" + this->deviceId.toStdString() + "|" + msg.key + "|" + - http::client()->user_id().to_string() + "|" + - http::client()->device_id() + "|" + this->sas->public_key() + - "|" + this->transaction_id; - } - - nhlog::ui()->info("Info is: '{}'", info); - - if (this->sender == false) { - this->sendVerificationKey(); - } else { - if (this->commitment != - mtx::crypto::bin2base64_unpadded( - mtx::crypto::sha256(msg.key + this->canonical_json.dump()))) { - 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); - } - }); - + timeout = new QTimer(this); + timeout->setSingleShot(true); + this->sas = olm::client()->sas_init(); + this->isMacVerified = false; + + auto user_id = userID.toStdString(); + this->toClient = mtx::identifiers::parse<mtx::identifiers::User>(user_id); + cache::client()->query_keys( + user_id, [user_id, this](const UserKeyCache &res, mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to query device keys: {},{}", + mtx::errors::to_string(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; + } + + this->their_keys = res; + }); + + cache::client()->query_keys( + http::client()->user_id().to_string(), + [this](const UserKeyCache &res, mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to query device keys: {},{}", + mtx::errors::to_string(err->matrix_error.errcode), + static_cast<int>(err->status_code)); + return; + } + + if (res.master_keys.keys.empty()) + return; + + if (auto status = cache::verificationStatus(http::client()->user_id().to_string()); + status && status->user_verified == crypto::Trust::Verified) + this->our_trusted_master_key = res.master_keys.keys.begin()->second; + }); + + if (model) { connect( - ChatPage::instance(), - &ChatPage::receivedDeviceVerificationMac, - this, - [this](const mtx::events::msg::KeyVerificationMac &msg) { - nhlog::crypto()->info("verification: received mac"); - if (msg.transaction_id.has_value()) { - if (msg.transaction_id.value() != this->transaction_id) - return; - } else if (msg.relations.references()) { - if (msg.relations.references() != this->relation.event_id) - return; - } - - std::map<std::string, std::string> key_list; - std::string key_string; + this->model_, &TimelineModel::updateFlowEventId, this, [this](std::string event_id_) { + this->relation.rel_type = mtx::common::RelationType::Reference; + this->relation.event_id = event_id_; + this->transaction_id = event_id_; + }); + } + + connect(timeout, &QTimer::timeout, this, [this]() { + nhlog::crypto()->info("verification: timeout"); + if (state_ != Success && state_ != Failed) + this->cancelVerification(DeviceVerificationFlow::Error::Timeout); + }); + + connect(ChatPage::instance(), + &ChatPage::receivedDeviceVerificationStart, + this, + &DeviceVerificationFlow::handleStartMessage); + connect(ChatPage::instance(), + &ChatPage::receivedDeviceVerificationAccept, + this, + [this](const mtx::events::msg::KeyVerificationAccept &msg) { + nhlog::crypto()->info("verification: received accept"); + if (msg.transaction_id.has_value()) { + if (msg.transaction_id.value() != this->transaction_id) + return; + } else if (msg.relations.references()) { + if (msg.relations.references() != this->relation.event_id) + return; + } + if ((msg.key_agreement_protocol == "curve25519-hkdf-sha256") && + (msg.hash == "sha256") && + (msg.message_authentication_code == "hkdf-hmac-sha256")) { + this->commitment = msg.commitment; + 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 { + this->method = mtx::events::msg::SASMethods::Decimal; + } + this->mac_method = msg.message_authentication_code; + this->sendVerificationKey(); + } else { + this->cancelVerification(DeviceVerificationFlow::Error::UnknownMethod); + } + }); + + connect(ChatPage::instance(), + &ChatPage::receivedDeviceVerificationCancel, + this, + [this](const mtx::events::msg::KeyVerificationCancel &msg) { + nhlog::crypto()->info("verification: received cancel"); + if (msg.transaction_id.has_value()) { + if (msg.transaction_id.value() != this->transaction_id) + return; + } else if (msg.relations.references()) { + if (msg.relations.references() != this->relation.event_id) + return; + } + error_ = User; + emit errorChanged(); + setState(Failed); + }); + + connect( + ChatPage::instance(), + &ChatPage::receivedDeviceVerificationKey, + this, + [this](const mtx::events::msg::KeyVerificationKey &msg) { + nhlog::crypto()->info("verification: received key"); + if (msg.transaction_id.has_value()) { + if (msg.transaction_id.value() != this->transaction_id) + return; + } else if (msg.relations.references()) { + if (msg.relations.references() != 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) { + info = "MATRIX_KEY_VERIFICATION_SAS|" + http::client()->user_id().to_string() + "|" + + http::client()->device_id() + "|" + this->sas->public_key() + "|" + + this->toClient.to_string() + "|" + this->deviceId.toStdString() + "|" + + msg.key + "|" + this->transaction_id; + } else { + info = "MATRIX_KEY_VERIFICATION_SAS|" + this->toClient.to_string() + "|" + + this->deviceId.toStdString() + "|" + msg.key + "|" + + http::client()->user_id().to_string() + "|" + http::client()->device_id() + + "|" + this->sas->public_key() + "|" + this->transaction_id; + } + + nhlog::ui()->info("Info is: '{}'", info); + + if (this->sender == false) { + this->sendVerificationKey(); + } else { + if (this->commitment != mtx::crypto::bin2base64_unpadded(mtx::crypto::sha256( + msg.key + this->canonical_json.dump()))) { + 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::receivedDeviceVerificationMac, + this, + [this](const mtx::events::msg::KeyVerificationMac &msg) { + nhlog::crypto()->info("verification: received mac"); + if (msg.transaction_id.has_value()) { + if (msg.transaction_id.value() != this->transaction_id) + return; + } else if (msg.relations.references()) { + if (msg.relations.references() != this->relation.event_id) + return; + } + + std::map<std::string, std::string> key_list; + std::string key_string; + for (const auto &mac : msg.mac) { + for (const auto &[deviceid, key] : their_keys.device_keys) { + (void)deviceid; + if (key.keys.count(mac.first)) + key_list[mac.first] = key.keys.at(mac.first); + } + + if (their_keys.master_keys.keys.count(mac.first)) + key_list[mac.first] = their_keys.master_keys.keys[mac.first]; + if (their_keys.user_signing_keys.keys.count(mac.first)) + key_list[mac.first] = their_keys.user_signing_keys.keys[mac.first]; + if (their_keys.self_signing_keys.keys.count(mac.first)) + key_list[mac.first] = their_keys.self_signing_keys.keys[mac.first]; + } + auto macs = key_verification_mac(sas.get(), + toClient, + this->deviceId.toStdString(), + http::client()->user_id(), + http::client()->device_id(), + this->transaction_id, + key_list); + + for (const auto &[key, mac] : macs.mac) { + if (mac != msg.mac.at(key)) { + this->cancelVerification(DeviceVerificationFlow::Error::KeyMismatch); + return; + } + } + + if (msg.keys == macs.keys) { + mtx::requests::KeySignaturesUpload req; + if (utils::localUser().toStdString() == this->toClient.to_string()) { + // self verification, sign master key with device key, if we + // verified it for (const auto &mac : msg.mac) { - for (const auto &[deviceid, key] : their_keys.device_keys) { - (void)deviceid; - if (key.keys.count(mac.first)) - key_list[mac.first] = key.keys.at(mac.first); + if (their_keys.master_keys.keys.count(mac.first)) { + json j = their_keys.master_keys; + j.erase("signatures"); + j.erase("unsigned"); + mtx::crypto::CrossSigningKeys master_key = j; + master_key.signatures[utils::localUser().toStdString()] + ["ed25519:" + http::client()->device_id()] = + olm::client()->sign_message(j.dump()); + req.signatures[utils::localUser().toStdString()] + [master_key.keys.at(mac.first)] = master_key; + } else if (mac.first == "ed25519:" + this->deviceId.toStdString()) { + // Sign their device key with self signing key + + auto device_id = this->deviceId.toStdString(); + + if (their_keys.device_keys.count(device_id)) { + json j = their_keys.device_keys.at(device_id); + j.erase("signatures"); + j.erase("unsigned"); + + auto secret = cache::secret( + mtx::secret_storage::secrets::cross_signing_self_signing); + if (!secret) + continue; + auto ssk = mtx::crypto::PkSigning::from_seed(*secret); + + mtx::crypto::DeviceKeys dev = j; + dev.signatures[utils::localUser().toStdString()] + ["ed25519:" + ssk.public_key()] = ssk.sign(j.dump()); + + req.signatures[utils::localUser().toStdString()][device_id] = dev; } - - if (their_keys.master_keys.keys.count(mac.first)) - key_list[mac.first] = their_keys.master_keys.keys[mac.first]; - if (their_keys.user_signing_keys.keys.count(mac.first)) - key_list[mac.first] = - their_keys.user_signing_keys.keys[mac.first]; - if (their_keys.self_signing_keys.keys.count(mac.first)) - key_list[mac.first] = - their_keys.self_signing_keys.keys[mac.first]; + } } - auto macs = key_verification_mac(sas.get(), - toClient, - this->deviceId.toStdString(), - http::client()->user_id(), - http::client()->device_id(), - this->transaction_id, - key_list); - - for (const auto &[key, mac] : macs.mac) { - if (mac != msg.mac.at(key)) { - this->cancelVerification( - DeviceVerificationFlow::Error::KeyMismatch); - return; - } + } else { + // Sign their master key with user signing key + for (const auto &mac : msg.mac) { + if (their_keys.master_keys.keys.count(mac.first)) { + json j = their_keys.master_keys; + j.erase("signatures"); + j.erase("unsigned"); + + auto secret = + cache::secret(mtx::secret_storage::secrets::cross_signing_user_signing); + if (!secret) + continue; + auto usk = mtx::crypto::PkSigning::from_seed(*secret); + + mtx::crypto::CrossSigningKeys master_key = j; + master_key.signatures[utils::localUser().toStdString()] + ["ed25519:" + usk.public_key()] = usk.sign(j.dump()); + + req.signatures[toClient.to_string()][master_key.keys.at(mac.first)] = + master_key; + } } + } + + if (!req.signatures.empty()) { + http::client()->keys_signatures_upload( + req, + [](const mtx::responses::KeySignaturesUpload &res, mtx::http::RequestErr err) { + if (err) { + nhlog::net()->error("failed to upload signatures: {},{}", + mtx::errors::to_string(err->matrix_error.errcode), + static_cast<int>(err->status_code)); + } - if (msg.keys == macs.keys) { - mtx::requests::KeySignaturesUpload req; - if (utils::localUser().toStdString() == this->toClient.to_string()) { - // self verification, sign master key with device key, if we - // verified it - for (const auto &mac : msg.mac) { - if (their_keys.master_keys.keys.count(mac.first)) { - json j = their_keys.master_keys; - j.erase("signatures"); - j.erase("unsigned"); - mtx::crypto::CrossSigningKeys master_key = j; - master_key - .signatures[utils::localUser().toStdString()] - ["ed25519:" + - http::client()->device_id()] = - olm::client()->sign_message(j.dump()); - req.signatures[utils::localUser().toStdString()] - [master_key.keys.at(mac.first)] = - master_key; - } else if (mac.first == - "ed25519:" + this->deviceId.toStdString()) { - // Sign their device key with self signing key - - auto device_id = this->deviceId.toStdString(); - - if (their_keys.device_keys.count(device_id)) { - json j = - their_keys.device_keys.at(device_id); - j.erase("signatures"); - j.erase("unsigned"); - - auto secret = cache::secret( - mtx::secret_storage::secrets:: - cross_signing_self_signing); - if (!secret) - continue; - auto ssk = - mtx::crypto::PkSigning::from_seed( - *secret); - - mtx::crypto::DeviceKeys dev = j; - dev.signatures - [utils::localUser().toStdString()] - ["ed25519:" + ssk.public_key()] = - ssk.sign(j.dump()); - - req.signatures[utils::localUser() - .toStdString()] - [device_id] = dev; - } - } - } - } else { - // Sign their master key with user signing key - for (const auto &mac : msg.mac) { - if (their_keys.master_keys.keys.count(mac.first)) { - json j = their_keys.master_keys; - j.erase("signatures"); - j.erase("unsigned"); - - auto secret = - cache::secret(mtx::secret_storage::secrets:: - cross_signing_user_signing); - if (!secret) - continue; - auto usk = - mtx::crypto::PkSigning::from_seed(*secret); - - mtx::crypto::CrossSigningKeys master_key = j; - master_key - .signatures[utils::localUser().toStdString()] - ["ed25519:" + usk.public_key()] = - usk.sign(j.dump()); - - req.signatures[toClient.to_string()] - [master_key.keys.at(mac.first)] = - master_key; - } - } - } - - if (!req.signatures.empty()) { - http::client()->keys_signatures_upload( - req, - [](const mtx::responses::KeySignaturesUpload &res, - mtx::http::RequestErr err) { - if (err) { - nhlog::net()->error( - "failed to upload signatures: {},{}", - mtx::errors::to_string( - err->matrix_error.errcode), - static_cast<int>(err->status_code)); - } - - for (const auto &[user_id, tmp] : res.errors) - for (const auto &[key_id, e] : tmp) - nhlog::net()->error( - "signature error for user {} and key " - "id {}: {}, {}", - user_id, - key_id, - mtx::errors::to_string(e.errcode), - e.error); - }); - } - - this->isMacVerified = true; - this->acceptDevice(); - } else { - this->cancelVerification(DeviceVerificationFlow::Error::KeyMismatch); - } - }); + for (const auto &[user_id, tmp] : res.errors) + for (const auto &[key_id, e] : tmp) + nhlog::net()->error("signature error for user {} and key " + "id {}: {}, {}", + user_id, + key_id, + mtx::errors::to_string(e.errcode), + e.error); + }); + } + + this->isMacVerified = true; + this->acceptDevice(); + } else { + this->cancelVerification(DeviceVerificationFlow::Error::KeyMismatch); + } + }); + + connect(ChatPage::instance(), + &ChatPage::receivedDeviceVerificationReady, + this, + [this](const mtx::events::msg::KeyVerificationReady &msg) { + nhlog::crypto()->info("verification: received ready"); + if (!sender) { + if (msg.from_device != http::client()->device_id()) { + error_ = User; + emit errorChanged(); + setState(Failed); + } - connect(ChatPage::instance(), - &ChatPage::receivedDeviceVerificationReady, - this, - [this](const mtx::events::msg::KeyVerificationReady &msg) { - nhlog::crypto()->info("verification: received ready"); - if (!sender) { - if (msg.from_device != http::client()->device_id()) { - error_ = User; - emit errorChanged(); - setState(Failed); - } - - return; - } + return; + } - if (msg.transaction_id.has_value()) { - if (msg.transaction_id.value() != this->transaction_id) - return; - } else if (msg.relations.references()) { - if (msg.relations.references() != this->relation.event_id) - return; - else { - this->deviceId = QString::fromStdString(msg.from_device); - } - } - this->startVerificationRequest(); - }); - - connect(ChatPage::instance(), - &ChatPage::receivedDeviceVerificationDone, - this, - [this](const mtx::events::msg::KeyVerificationDone &msg) { - nhlog::crypto()->info("verification: receoved done"); - if (msg.transaction_id.has_value()) { - if (msg.transaction_id.value() != this->transaction_id) - return; - } else if (msg.relations.references()) { - if (msg.relations.references() != this->relation.event_id) - return; - } - nhlog::ui()->info("Flow done on other side"); - }); + if (msg.transaction_id.has_value()) { + if (msg.transaction_id.value() != this->transaction_id) + return; + } else if (msg.relations.references()) { + if (msg.relations.references() != this->relation.event_id) + return; + else { + this->deviceId = QString::fromStdString(msg.from_device); + } + } + this->startVerificationRequest(); + }); + + connect(ChatPage::instance(), + &ChatPage::receivedDeviceVerificationDone, + this, + [this](const mtx::events::msg::KeyVerificationDone &msg) { + nhlog::crypto()->info("verification: receoved done"); + if (msg.transaction_id.has_value()) { + if (msg.transaction_id.value() != this->transaction_id) + return; + } else if (msg.relations.references()) { + if (msg.relations.references() != this->relation.event_id) + return; + } + nhlog::ui()->info("Flow done on other side"); + }); - timeout->start(TIMEOUT); + timeout->start(TIMEOUT); } QString DeviceVerificationFlow::state() { + 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: - return "PromptStartVerification"; + sendVerificationRequest(); + break; case CompareEmoji: - return "CompareEmoji"; case CompareNumber: - return "CompareNumber"; + sendVerificationMac(); + break; case WaitingForKeys: - return "WaitingForKeys"; case WaitingForOtherToAccept: - return "WaitingForOtherToAccept"; case WaitingForMac: - return "WaitingForMac"; case Success: - return "Success"; case Failed: - return "Failed"; - default: - return ""; + nhlog::db()->error("verification: Invalid state transition!"); + break; } -} - -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; - } + } 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 QString::fromStdString(this->toClient.to_string()); + return QString::fromStdString(this->toClient.to_string()); } QString DeviceVerificationFlow::getDeviceId() { - return this->deviceId; + return this->deviceId; } bool DeviceVerificationFlow::getSender() { - return this->sender; + return this->sender; } std::vector<int> DeviceVerificationFlow::getSasList() { - return this->sasList; + return this->sasList; } bool DeviceVerificationFlow::isSelfVerification() const { - return this->toClient.to_string() == http::client()->user_id().to_string(); + return this->toClient.to_string() == http::client()->user_id().to_string(); } 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_; + this->relation.rel_type = mtx::common::RelationType::Reference; + this->relation.event_id = 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.relations.references()) { - if (msg.relations.references() != 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(); + if (msg.transaction_id.has_value()) { + if (msg.transaction_id.value() != this->transaction_id) + return; + } else if (msg.relations.references()) { + if (msg.relations.references() != 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); + 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() { - mtx::events::msg::KeyVerificationAccept req; - - req.method = mtx::events::msg::VerificationMethods::SASv1; - req.key_agreement_protocol = "curve25519-hkdf-sha256"; - req.hash = "sha256"; - req.message_authentication_code = "hkdf-hmac-sha256"; - if (this->method == mtx::events::msg::SASMethods::Emoji) - req.short_authentication_string = {mtx::events::msg::SASMethods::Emoji}; - 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())); - - send(req); - setState(WaitingForKeys); + mtx::events::msg::KeyVerificationAccept req; + + req.method = mtx::events::msg::VerificationMethods::SASv1; + req.key_agreement_protocol = "curve25519-hkdf-sha256"; + req.hash = "sha256"; + req.message_authentication_code = "hkdf-hmac-sha256"; + if (this->method == mtx::events::msg::SASMethods::Emoji) + req.short_authentication_string = {mtx::events::msg::SASMethods::Emoji}; + 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())); + + send(req); + setState(WaitingForKeys); } //! responds verification request void DeviceVerificationFlow::sendVerificationReady() { - mtx::events::msg::KeyVerificationReady req; + mtx::events::msg::KeyVerificationReady req; - req.from_device = http::client()->device_id(); - req.methods = {mtx::events::msg::VerificationMethods::SASv1}; + req.from_device = http::client()->device_id(); + req.methods = {mtx::events::msg::VerificationMethods::SASv1}; - send(req); - setState(WaitingForKeys); + send(req); + setState(WaitingForKeys); } //! accepts a verification void DeviceVerificationFlow::sendVerificationDone() { - mtx::events::msg::KeyVerificationDone req; + mtx::events::msg::KeyVerificationDone req; - send(req); + send(req); } //! starts the verification flow void DeviceVerificationFlow::startVerificationRequest() { - mtx::events::msg::KeyVerificationStart req; - - req.from_device = http::client()->device_id(); - req.method = mtx::events::msg::VerificationMethods::SASv1; - req.key_agreement_protocols = {"curve25519-hkdf-sha256"}; - req.hashes = {"sha256"}; - req.message_authentication_codes = {"hkdf-hmac-sha256"}; - req.short_authentication_string = {mtx::events::msg::SASMethods::Decimal, - mtx::events::msg::SASMethods::Emoji}; - - 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); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - req.relations.relations.push_back(this->relation); - // Set synthesized to surpress the nheko relation extensions - req.relations.synthesized = true; - this->canonical_json = nlohmann::json(req); - } - send(req); - setState(WaitingForOtherToAccept); + mtx::events::msg::KeyVerificationStart req; + + req.from_device = http::client()->device_id(); + req.method = mtx::events::msg::VerificationMethods::SASv1; + req.key_agreement_protocols = {"curve25519-hkdf-sha256"}; + req.hashes = {"sha256"}; + req.message_authentication_codes = {"hkdf-hmac-sha256"}; + req.short_authentication_string = {mtx::events::msg::SASMethods::Decimal, + mtx::events::msg::SASMethods::Emoji}; + + 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); + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { + req.relations.relations.push_back(this->relation); + // Set synthesized to surpress the nheko relation extensions + req.relations.synthesized = true; + this->canonical_json = nlohmann::json(req); + } + send(req); + setState(WaitingForOtherToAccept); } //! sends a verification request void DeviceVerificationFlow::sendVerificationRequest() { - mtx::events::msg::KeyVerificationRequest req; + mtx::events::msg::KeyVerificationRequest req; - req.from_device = http::client()->device_id(); - req.methods = {mtx::events::msg::VerificationMethods::SASv1}; + req.from_device = http::client()->device_id(); + req.methods = {mtx::events::msg::VerificationMethods::SASv1}; - if (this->type == DeviceVerificationFlow::Type::ToDevice) { - QDateTime currentTime = QDateTime::currentDateTimeUtc(); + if (this->type == DeviceVerificationFlow::Type::ToDevice) { + QDateTime currentTime = QDateTime::currentDateTimeUtc(); - req.timestamp = (uint64_t)currentTime.toMSecsSinceEpoch(); + req.timestamp = (uint64_t)currentTime.toMSecsSinceEpoch(); - } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { - 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."; - } + } else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) { + 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."; + } - send(req); - setState(WaitingForOtherToAccept); + send(req); + setState(WaitingForOtherToAccept); } //! cancels a verification flow void DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_code) { - if (state_ == State::Success || state_ == State::Failed) - return; - - mtx::events::msg::KeyVerificationCancel req; - - if (error_code == DeviceVerificationFlow::Error::UnknownMethod) { - req.code = "m.unknown_method"; - req.reason = "unknown method received"; - } else if (error_code == DeviceVerificationFlow::Error::MismatchedCommitment) { - req.code = "m.mismatched_commitment"; - req.reason = "commitment didn't match"; - } else if (error_code == DeviceVerificationFlow::Error::MismatchedSAS) { - req.code = "m.mismatched_sas"; - req.reason = "sas didn't match"; - } else if (error_code == DeviceVerificationFlow::Error::KeyMismatch) { - req.code = "m.key_match"; - req.reason = "keys did not match"; - } else if (error_code == DeviceVerificationFlow::Error::Timeout) { - req.code = "m.timeout"; - req.reason = "timed out"; - } 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"; - } - - this->error_ = error_code; - emit errorChanged(); - this->setState(Failed); - - send(req); + if (state_ == State::Success || state_ == State::Failed) + return; + + mtx::events::msg::KeyVerificationCancel req; + + if (error_code == DeviceVerificationFlow::Error::UnknownMethod) { + req.code = "m.unknown_method"; + req.reason = "unknown method received"; + } else if (error_code == DeviceVerificationFlow::Error::MismatchedCommitment) { + req.code = "m.mismatched_commitment"; + req.reason = "commitment didn't match"; + } else if (error_code == DeviceVerificationFlow::Error::MismatchedSAS) { + req.code = "m.mismatched_sas"; + req.reason = "sas didn't match"; + } else if (error_code == DeviceVerificationFlow::Error::KeyMismatch) { + req.code = "m.key_match"; + req.reason = "keys did not match"; + } else if (error_code == DeviceVerificationFlow::Error::Timeout) { + req.code = "m.timeout"; + req.reason = "timed out"; + } 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"; + } + + this->error_ = error_code; + emit errorChanged(); + this->setState(Failed); + + send(req); } //! sends the verification key void DeviceVerificationFlow::sendVerificationKey() { - mtx::events::msg::KeyVerificationKey req; + mtx::events::msg::KeyVerificationKey req; - req.key = this->sas->public_key(); + req.key = this->sas->public_key(); - send(req); + send(req); } mtx::events::msg::KeyVerificationMac @@ -728,79 +696,78 @@ key_verification_mac(mtx::crypto::SAS *sas, const std::string &transactionId, std::map<std::string, std::string> keys) { - mtx::events::msg::KeyVerificationMac req; + mtx::events::msg::KeyVerificationMac req; - std::string info = "MATRIX_KEY_VERIFICATION_MAC" + sender.to_string() + senderDevice + - receiver.to_string() + receiverDevice + transactionId; + std::string info = "MATRIX_KEY_VERIFICATION_MAC" + sender.to_string() + senderDevice + + receiver.to_string() + receiverDevice + transactionId; - std::string key_list; - bool first = true; - for (const auto &[key_id, key] : keys) { - req.mac[key_id] = sas->calculate_mac(key, info + key_id); + std::string key_list; + bool first = true; + for (const auto &[key_id, key] : keys) { + req.mac[key_id] = sas->calculate_mac(key, info + key_id); - if (!first) - key_list += ","; - key_list += key_id; - first = false; - } + if (!first) + key_list += ","; + key_list += key_id; + first = false; + } - req.keys = sas->calculate_mac(key_list, info + "KEY_IDS"); + req.keys = sas->calculate_mac(key_list, info + "KEY_IDS"); - return req; + return req; } //! sends the mac of the keys void DeviceVerificationFlow::sendVerificationMac() { - std::map<std::string, std::string> key_list; - key_list["ed25519:" + http::client()->device_id()] = olm::client()->identity_keys().ed25519; + std::map<std::string, std::string> key_list; + key_list["ed25519:" + http::client()->device_id()] = olm::client()->identity_keys().ed25519; - // send our master key, if we trust it - if (!this->our_trusted_master_key.empty()) - key_list["ed25519:" + our_trusted_master_key] = our_trusted_master_key; + // send our master key, if we trust it + if (!this->our_trusted_master_key.empty()) + key_list["ed25519:" + our_trusted_master_key] = our_trusted_master_key; - mtx::events::msg::KeyVerificationMac req = - key_verification_mac(sas.get(), - http::client()->user_id(), - http::client()->device_id(), - this->toClient, - this->deviceId.toStdString(), - this->transaction_id, - key_list); + mtx::events::msg::KeyVerificationMac req = key_verification_mac(sas.get(), + http::client()->user_id(), + http::client()->device_id(), + this->toClient, + this->deviceId.toStdString(), + this->transaction_id, + key_list); - send(req); + send(req); - setState(WaitingForMac); - acceptDevice(); + setState(WaitingForMac); + acceptDevice(); } //! Completes the verification flow void DeviceVerificationFlow::acceptDevice() { - if (!isMacVerified) { - setState(WaitingForMac); - } else if (state_ == WaitingForMac) { - cache::markDeviceVerified(this->toClient.to_string(), this->deviceId.toStdString()); - this->sendVerificationDone(); - setState(Success); - - // Request secrets. We should probably check somehow, if a device knowns about the - // secrets. - if (utils::localUser().toStdString() == this->toClient.to_string() && - (!cache::secret(mtx::secret_storage::secrets::cross_signing_self_signing) || - !cache::secret(mtx::secret_storage::secrets::cross_signing_user_signing))) { - olm::request_cross_signing_keys(); - } + if (!isMacVerified) { + setState(WaitingForMac); + } else if (state_ == WaitingForMac) { + cache::markDeviceVerified(this->toClient.to_string(), this->deviceId.toStdString()); + this->sendVerificationDone(); + setState(Success); + + // Request secrets. We should probably check somehow, if a device knowns about the + // secrets. + if (utils::localUser().toStdString() == this->toClient.to_string() && + (!cache::secret(mtx::secret_storage::secrets::cross_signing_self_signing) || + !cache::secret(mtx::secret_storage::secrets::cross_signing_user_signing))) { + olm::request_cross_signing_keys(); } + } } void DeviceVerificationFlow::unverify() { - cache::markDeviceUnverified(this->toClient.to_string(), this->deviceId.toStdString()); + cache::markDeviceUnverified(this->toClient.to_string(), this->deviceId.toStdString()); - emit refreshProfile(); + emit refreshProfile(); } QSharedPointer<DeviceVerificationFlow> @@ -810,22 +777,22 @@ DeviceVerificationFlow::NewInRoomVerification(QObject *parent_, QString other_user_, QString event_id_) { - QSharedPointer<DeviceVerificationFlow> flow( - new DeviceVerificationFlow(parent_, - Type::RoomMsg, - timelineModel_, - other_user_, - QString::fromStdString(msg.from_device))); - - flow->setEventId(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> flow( + new DeviceVerificationFlow(parent_, + Type::RoomMsg, + timelineModel_, + other_user_, + QString::fromStdString(msg.from_device))); + + flow->setEventId(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_, @@ -833,17 +800,17 @@ DeviceVerificationFlow::NewToDeviceVerification(QObject *parent_, 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); - } + 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; + return flow; } QSharedPointer<DeviceVerificationFlow> DeviceVerificationFlow::NewToDeviceVerification(QObject *parent_, @@ -851,32 +818,32 @@ DeviceVerificationFlow::NewToDeviceVerification(QObject *parent_, 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(); + 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, ""); + flow->handleStartMessage(msg, ""); - return flow; + 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> 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)); + QSharedPointer<DeviceVerificationFlow> flow( + new DeviceVerificationFlow(parent_, Type::ToDevice, nullptr, userid, device)); - flow->sender = true; - flow->transaction_id = http::client()->generate_txn_id(); + flow->sender = true; + flow->transaction_id = http::client()->generate_txn_id(); - return flow; + return flow; } |