// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include "td/telegram/SecureManager.h" #include "td/telegram/ContactsManager.h" #include "td/telegram/DialogId.h" #include "td/telegram/files/FileId.h" #include "td/telegram/files/FileManager.h" #include "td/telegram/files/FileType.h" #include "td/telegram/Global.h" #include "td/telegram/misc.h" #include "td/telegram/net/NetQueryDispatcher.h" #include "td/telegram/PasswordManager.h" #include "td/telegram/Td.h" #include "td/utils/algorithm.h" #include "td/utils/buffer.h" #include "td/utils/logging.h" #include "td/utils/misc.h" #include "td/utils/optional.h" #include "td/utils/Slice.h" #include "td/utils/SliceBuilder.h" #include namespace td { class GetSecureValue final : public NetQueryCallback { public: GetSecureValue(ActorShared parent, std::string password, SecureValueType type, Promise promise); private: ActorShared parent_; string password_; SecureValueType type_; Promise promise_; optional encrypted_secure_value_; optional secret_; void on_error(Status error); void on_secret(Result r_secret, bool dummy); void loop() final; void start_up() final; void on_result(NetQueryPtr query) final; }; class GetAllSecureValues final : public NetQueryCallback { public: GetAllSecureValues(ActorShared parent, std::string password, Promise promise); private: ActorShared parent_; string password_; Promise promise_; optional> encrypted_secure_values_; optional secret_; void on_error(Status error); void on_secret(Result r_secret, bool dummy); void loop() final; void start_up() final; void on_result(NetQueryPtr query) final; }; class SetSecureValue final : public NetQueryCallback { public: SetSecureValue(ActorShared parent, string password, SecureValue secure_value, Promise promise); private: ActorShared parent_; string password_; SecureValue secure_value_; Promise promise_; optional secret_; size_t files_left_to_upload_ = 0; uint32 upload_generation_{0}; vector files_to_upload_; vector translations_to_upload_; optional front_side_; optional reverse_side_; optional selfie_; class UploadCallback; std::shared_ptr upload_callback_; enum class State : int32 { WaitSecret, WaitSetValue } state_ = State::WaitSecret; class UploadCallback final : public FileManager::UploadCallback { public: UploadCallback(ActorId actor_id, uint32 upload_generation); private: ActorId actor_id_; uint32 upload_generation_; void on_upload_ok(FileId file_id, tl_object_ptr input_file) final; void on_upload_encrypted_ok(FileId file_id, tl_object_ptr input_file) final; void on_upload_secure_ok(FileId file_id, tl_object_ptr input_file) final; void on_upload_error(FileId file_id, Status status) final; }; void on_upload_ok(FileId file_id, tl_object_ptr input_file, uint32 upload_generation); void on_upload_error(FileId file_id, Status status, uint32 upload_generation); void on_error(Status error); void on_secret(Result r_secret, bool x); void start_up() final; void hangup() final; void tear_down() final; void loop() final; void on_result(NetQueryPtr query) final; void load_secret(); void cancel_upload(); void start_upload_all(); void start_upload(FileManager *file_manager, FileId &file_id, SecureInputFile &info); void merge(FileManager *file_manager, FileId file_id, EncryptedSecureFile &encrypted_file); }; class SetSecureValueErrorsQuery final : public Td::ResultHandler { Promise promise_; public: explicit SetSecureValueErrorsQuery(Promise &&promise) : promise_(std::move(promise)) { } void send(tl_object_ptr input_user, vector> input_errors) { send_query(G()->net_query_creator().create( telegram_api::users_setSecureValueErrors(std::move(input_user), std::move(input_errors)))); } void on_result(uint64 id, BufferSlice packet) final { auto result_ptr = fetch_result(packet); if (result_ptr.is_error()) { return on_error(id, result_ptr.move_as_error()); } bool ptr = result_ptr.move_as_ok(); LOG(DEBUG) << "Receive result for SetSecureValueErrorsQuery: " << ptr; promise_.set_value(Unit()); } void on_error(uint64 id, Status status) final { if (status.code() != 0) { promise_.set_error(std::move(status)); } else { promise_.set_error(Status::Error(400, status.message())); } } }; GetSecureValue::GetSecureValue(ActorShared parent, std::string password, SecureValueType type, Promise promise) : parent_(std::move(parent)), password_(std::move(password)), type_(type), promise_(std::move(promise)) { } void GetSecureValue::on_error(Status error) { if (error.message() == "SECURE_SECRET_REQUIRED") { send_closure(G()->password_manager(), &PasswordManager::drop_cached_secret); } if (error.code() > 0) { promise_.set_error(std::move(error)); } else { promise_.set_error(Status::Error(400, error.message())); } stop(); } void GetSecureValue::on_secret(Result r_secret, bool dummy) { if (r_secret.is_error()) { if (!G()->is_expected_error(r_secret.error())) { LOG(ERROR) << "Receive error instead of secret: " << r_secret.error(); } return on_error(r_secret.move_as_error()); } secret_ = r_secret.move_as_ok(); loop(); } void GetSecureValue::loop() { if (!encrypted_secure_value_ || !secret_) { return; } auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get(); auto r_secure_value = decrypt_secure_value(file_manager, *secret_, *encrypted_secure_value_); if (r_secure_value.is_error()) { return on_error(r_secure_value.move_as_error()); } send_closure(parent_, &SecureManager::on_get_secure_value, r_secure_value.ok()); promise_.set_value(r_secure_value.move_as_ok()); stop(); } void GetSecureValue::start_up() { std::vector> types; types.push_back(get_input_secure_value_type(type_)); auto query = G()->net_query_creator().create(telegram_api::account_getSecureValue(std::move(types))); G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this)); send_closure(G()->password_manager(), &PasswordManager::get_secure_secret, password_, PromiseCreator::lambda([actor_id = actor_id(this)](Result r_secret) { send_closure(actor_id, &GetSecureValue::on_secret, std::move(r_secret), true); })); } void GetSecureValue::on_result(NetQueryPtr query) { auto r_result = fetch_result(std::move(query)); if (r_result.is_error()) { return on_error(r_result.move_as_error()); } auto result = r_result.move_as_ok(); if (result.empty()) { return on_error(Status::Error(404, "Not Found")); } if (result.size() != 1) { return on_error(Status::Error(PSLICE() << "Expected vector of size 1 got " << result.size())); } encrypted_secure_value_ = get_encrypted_secure_value(G()->td().get_actor_unsafe()->file_manager_.get(), std::move(result[0])); if (encrypted_secure_value_.value().type == SecureValueType::None) { return on_error(Status::Error(404, "Not Found")); } loop(); } GetAllSecureValues::GetAllSecureValues(ActorShared parent, std::string password, Promise promise) : parent_(std::move(parent)), password_(std::move(password)), promise_(std::move(promise)) { } void GetAllSecureValues::on_error(Status error) { if (error.message() == "SECURE_SECRET_REQUIRED") { send_closure(G()->password_manager(), &PasswordManager::drop_cached_secret); } if (error.code() > 0) { promise_.set_error(std::move(error)); } else { promise_.set_error(Status::Error(400, error.message())); } stop(); } void GetAllSecureValues::on_secret(Result r_secret, bool dummy) { if (r_secret.is_error()) { if (!G()->is_expected_error(r_secret.error())) { LOG(ERROR) << "Receive error instead of secret: " << r_secret.error(); } return on_error(r_secret.move_as_error()); } secret_ = r_secret.move_as_ok(); loop(); } void GetAllSecureValues::loop() { if (!encrypted_secure_values_ || !secret_) { return; } auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get(); auto r_secure_values = decrypt_secure_values(file_manager, *secret_, *encrypted_secure_values_); if (r_secure_values.is_error()) { return on_error(r_secure_values.move_as_error()); } for (auto &secure_value : r_secure_values.ok()) { send_closure(parent_, &SecureManager::on_get_secure_value, secure_value); } auto secure_values = transform(r_secure_values.move_as_ok(), [](SecureValueWithCredentials &&value) { return std::move(value.value); }); promise_.set_value(get_passport_elements_object(file_manager, std::move(secure_values))); stop(); } void GetAllSecureValues::start_up() { auto query = G()->net_query_creator().create(telegram_api::account_getAllSecureValues()); G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this)); send_closure(G()->password_manager(), &PasswordManager::get_secure_secret, password_, PromiseCreator::lambda([actor_id = actor_id(this)](Result r_secret) { send_closure(actor_id, &GetAllSecureValues::on_secret, std::move(r_secret), true); })); } void GetAllSecureValues::on_result(NetQueryPtr query) { auto r_result = fetch_result(std::move(query)); if (r_result.is_error()) { return on_error(r_result.move_as_error()); } encrypted_secure_values_ = get_encrypted_secure_values(G()->td().get_actor_unsafe()->file_manager_.get(), r_result.move_as_ok()); loop(); } SetSecureValue::SetSecureValue(ActorShared parent, string password, SecureValue secure_value, Promise promise) : parent_(std::move(parent)) , password_(std::move(password)) , secure_value_(std::move(secure_value)) , promise_(std::move(promise)) { } SetSecureValue::UploadCallback::UploadCallback(ActorId actor_id, uint32 upload_generation) : actor_id_(actor_id), upload_generation_(upload_generation) { } void SetSecureValue::UploadCallback::on_upload_ok(FileId file_id, tl_object_ptr input_file) { CHECK(input_file == nullptr); send_closure_later(actor_id_, &SetSecureValue::on_upload_ok, file_id, nullptr, upload_generation_); } void SetSecureValue::UploadCallback::on_upload_encrypted_ok( FileId file_id, tl_object_ptr input_file) { UNREACHABLE(); } void SetSecureValue::UploadCallback::on_upload_secure_ok(FileId file_id, tl_object_ptr input_file) { send_closure_later(actor_id_, &SetSecureValue::on_upload_ok, file_id, std::move(input_file), upload_generation_); } void SetSecureValue::UploadCallback::on_upload_error(FileId file_id, Status error) { send_closure_later(actor_id_, &SetSecureValue::on_upload_error, file_id, std::move(error), upload_generation_); } void SetSecureValue::on_upload_ok(FileId file_id, tl_object_ptr input_file, uint32 upload_generation) { if (upload_generation_ != upload_generation) { return; } SecureInputFile *info_ptr = nullptr; for (auto &info : files_to_upload_) { if (info.file_id == file_id) { info_ptr = &info; break; } } for (auto &info : translations_to_upload_) { if (info.file_id == file_id) { info_ptr = &info; break; } } if (front_side_ && front_side_.value().file_id == file_id) { info_ptr = &front_side_.value(); } if (reverse_side_ && reverse_side_.value().file_id == file_id) { info_ptr = &reverse_side_.value(); } if (selfie_ && selfie_.value().file_id == file_id) { info_ptr = &selfie_.value(); } CHECK(info_ptr); auto &info = *info_ptr; CHECK(!info.input_file); info.input_file = std::move(input_file); CHECK(files_left_to_upload_ != 0); files_left_to_upload_--; loop(); } void SetSecureValue::on_upload_error(FileId file_id, Status error, uint32 upload_generation) { if (upload_generation_ != upload_generation) { return; } on_error(std::move(error)); } void SetSecureValue::on_error(Status error) { if (error.code() > 0) { promise_.set_error(std::move(error)); } else { promise_.set_error(Status::Error(400, error.message())); } stop(); } void SetSecureValue::on_secret(Result r_secret, bool x) { if (r_secret.is_error()) { if (!G()->is_expected_error(r_secret.error())) { LOG(ERROR) << "Receive error instead of secret: " << r_secret.error(); } return on_error(r_secret.move_as_error()); } secret_ = r_secret.move_as_ok(); loop(); } void SetSecureValue::start_up() { load_secret(); auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get(); // Remove duplicate files FileId front_side_file_id; if (secure_value_.front_side.file_id.is_valid()) { front_side_file_id = file_manager->get_file_view(secure_value_.front_side.file_id).file_id(); front_side_ = SecureInputFile(); } FileId reverse_side_file_id; if (secure_value_.reverse_side.file_id.is_valid()) { reverse_side_file_id = file_manager->get_file_view(secure_value_.reverse_side.file_id).file_id(); reverse_side_ = SecureInputFile(); if (front_side_file_id == reverse_side_file_id) { return on_error(Status::Error(400, "Front side and reverse side must be different")); } } FileId selfie_file_id; if (secure_value_.selfie.file_id.is_valid()) { selfie_file_id = file_manager->get_file_view(secure_value_.selfie.file_id).file_id(); selfie_ = SecureInputFile(); if (front_side_file_id == selfie_file_id) { return on_error(Status::Error(400, "Front side and selfie must be different")); } if (reverse_side_file_id == selfie_file_id) { return on_error(Status::Error(400, "Reverse side and selfie must be different")); } } if (!secure_value_.files.empty()) { CHECK(!front_side_file_id.is_valid()); CHECK(!reverse_side_file_id.is_valid()); CHECK(!selfie_file_id.is_valid()); for (auto it = secure_value_.files.begin(); it != secure_value_.files.end();) { auto file_id = file_manager->get_file_view(it->file_id).file_id(); bool is_duplicate = false; for (auto other_it = secure_value_.files.begin(); other_it != it; ++other_it) { if (file_id == file_manager->get_file_view(other_it->file_id).file_id()) { is_duplicate = true; break; } } if (is_duplicate) { it = secure_value_.files.erase(it); } else { ++it; } } } if (!secure_value_.translations.empty()) { for (auto it = secure_value_.translations.begin(); it != secure_value_.translations.end();) { auto file_id = file_manager->get_file_view(it->file_id).file_id(); bool is_duplicate = file_id == front_side_file_id || file_id == reverse_side_file_id || file_id == selfie_file_id; for (auto other_it = secure_value_.translations.begin(); other_it != it; ++other_it) { if (file_id == file_manager->get_file_view(other_it->file_id).file_id()) { is_duplicate = true; break; } } for (auto &dated_file : secure_value_.files) { if (file_id == file_manager->get_file_view(dated_file.file_id).file_id()) { is_duplicate = true; break; } } if (is_duplicate) { it = secure_value_.translations.erase(it); } else { ++it; } } } start_upload_all(); } void SetSecureValue::load_secret() { secret_ = {}; send_closure(G()->password_manager(), &PasswordManager::get_secure_secret, password_, PromiseCreator::lambda([actor_id = actor_id(this)](Result r_secret) { send_closure(actor_id, &SetSecureValue::on_secret, std::move(r_secret), true); })); } void SetSecureValue::cancel_upload() { upload_generation_++; auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get(); if (file_manager == nullptr) { return; } for (auto &file_info : files_to_upload_) { file_manager->cancel_upload(file_info.file_id); } for (auto &file_info : translations_to_upload_) { file_manager->cancel_upload(file_info.file_id); } if (front_side_) { file_manager->cancel_upload(front_side_.value().file_id); } if (reverse_side_) { file_manager->cancel_upload(reverse_side_.value().file_id); } if (selfie_) { file_manager->cancel_upload(selfie_.value().file_id); } files_left_to_upload_ = 0; } void SetSecureValue::start_upload_all() { if (files_left_to_upload_ != 0) { cancel_upload(); } upload_generation_++; upload_callback_ = std::make_shared(actor_id(this), upload_generation_); auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get(); files_to_upload_.resize(secure_value_.files.size()); for (size_t i = 0; i < files_to_upload_.size(); i++) { start_upload(file_manager, secure_value_.files[i].file_id, files_to_upload_[i]); } translations_to_upload_.resize(secure_value_.translations.size()); for (size_t i = 0; i < translations_to_upload_.size(); i++) { start_upload(file_manager, secure_value_.translations[i].file_id, translations_to_upload_[i]); } if (front_side_) { start_upload(file_manager, secure_value_.front_side.file_id, front_side_.value()); } if (reverse_side_) { start_upload(file_manager, secure_value_.reverse_side.file_id, reverse_side_.value()); } if (selfie_) { start_upload(file_manager, secure_value_.selfie.file_id, selfie_.value()); } } void SetSecureValue::start_upload(FileManager *file_manager, FileId &file_id, SecureInputFile &info) { auto file_view = file_manager->get_file_view(file_id); bool force = false; if (info.file_id.empty()) { if (!file_view.is_encrypted_secure()) { auto download_file_id = file_manager->dup_file_id(file_id); file_id = file_manager ->register_generate(FileType::Secure, FileLocationSource::FromServer, file_view.suggested_path(), PSTRING() << "#file_id#" << download_file_id.get(), DialogId(), file_view.size()) .ok(); } info.file_id = file_manager->dup_file_id(file_id); } else { force = true; } file_manager->resume_upload(info.file_id, {}, upload_callback_, 1, 0, force); files_left_to_upload_++; } void SetSecureValue::loop() { if (state_ == State::WaitSecret) { if (!secret_) { return; } if (files_left_to_upload_ != 0) { return; } auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get(); auto input_secure_value = get_input_secure_value_object(file_manager, encrypt_secure_value(file_manager, *secret_, secure_value_), files_to_upload_, front_side_, reverse_side_, selfie_, translations_to_upload_); auto save_secure_value = telegram_api::account_saveSecureValue(std::move(input_secure_value), secret_.value().get_hash()); auto query = G()->net_query_creator().create(save_secure_value); G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this)); state_ = State::WaitSetValue; } } void SetSecureValue::hangup() { on_error(Status::Error(406, "Request canceled")); } void SetSecureValue::tear_down() { cancel_upload(); } void SetSecureValue::on_result(NetQueryPtr query) { auto r_result = fetch_result(std::move(query)); if (r_result.is_error()) { if (r_result.error().message() == "SECURE_SECRET_REQUIRED") { state_ = State::WaitSecret; send_closure(G()->password_manager(), &PasswordManager::drop_cached_secret); load_secret(); return loop(); } if (r_result.error().message() == "SECURE_SECRET_INVALID") { state_ = State::WaitSecret; start_upload_all(); return loop(); } return on_error(r_result.move_as_error()); } auto result = r_result.move_as_ok(); auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get(); auto encrypted_secure_value = get_encrypted_secure_value(file_manager, std::move(result)); if (encrypted_secure_value.type == SecureValueType::None) { return on_error(Status::Error(500, "Receive invalid Telegram Passport element")); } if (secure_value_.files.size() != encrypted_secure_value.files.size()) { return on_error(Status::Error(500, "Different file count")); } for (size_t i = 0; i < secure_value_.files.size(); i++) { merge(file_manager, secure_value_.files[i].file_id, encrypted_secure_value.files[i]); } if (secure_value_.front_side.file_id.is_valid() && encrypted_secure_value.front_side.file.file_id.is_valid()) { merge(file_manager, secure_value_.front_side.file_id, encrypted_secure_value.front_side); } if (secure_value_.reverse_side.file_id.is_valid() && encrypted_secure_value.reverse_side.file.file_id.is_valid()) { merge(file_manager, secure_value_.reverse_side.file_id, encrypted_secure_value.reverse_side); } if (secure_value_.selfie.file_id.is_valid() && encrypted_secure_value.selfie.file.file_id.is_valid()) { merge(file_manager, secure_value_.selfie.file_id, encrypted_secure_value.selfie); } for (size_t i = 0; i < secure_value_.translations.size(); i++) { merge(file_manager, secure_value_.translations[i].file_id, encrypted_secure_value.translations[i]); } auto r_secure_value = decrypt_secure_value(file_manager, *secret_, encrypted_secure_value); if (r_secure_value.is_error()) { return on_error(r_secure_value.move_as_error()); } send_closure(parent_, &SecureManager::on_get_secure_value, r_secure_value.ok()); promise_.set_value(r_secure_value.move_as_ok()); stop(); } void SetSecureValue::merge(FileManager *file_manager, FileId file_id, EncryptedSecureFile &encrypted_file) { auto file_view = file_manager->get_file_view(file_id); CHECK(!file_view.empty()); CHECK(file_view.encryption_key().has_value_hash()); if (file_view.encryption_key().value_hash().as_slice() != encrypted_file.file_hash) { LOG(ERROR) << "Hash mismatch"; return; } auto status = file_manager->merge(encrypted_file.file.file_id, file_id); LOG_IF(ERROR, status.is_error()) << status.error(); } class DeleteSecureValue final : public NetQueryCallback { public: DeleteSecureValue(ActorShared parent, SecureValueType type, Promise promise) : parent_(std::move(parent)), type_(std::move(type)), promise_(std::move(promise)) { } private: ActorShared parent_; SecureValueType type_; Promise promise_; void start_up() final { std::vector> types; types.push_back(get_input_secure_value_type(type_)); auto query = G()->net_query_creator().create(telegram_api::account_deleteSecureValue(std::move(types))); G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this)); } void on_result(NetQueryPtr query) final { auto r_result = fetch_result(std::move(query)); if (r_result.is_error()) { promise_.set_error(r_result.move_as_error()); } else { promise_.set_value(Unit()); } stop(); } }; class GetPassportAuthorizationForm final : public NetQueryCallback { public: GetPassportAuthorizationForm(ActorShared parent, UserId bot_user_id, string scope, string public_key, Promise> promise) : parent_(std::move(parent)) , bot_user_id_(bot_user_id) , scope_(std::move(scope)) , public_key_(std::move(public_key)) , promise_(std::move(promise)) { } private: ActorShared parent_; UserId bot_user_id_; string scope_; string public_key_; Promise> promise_; void on_error(Status error) { if (error.code() > 0) { promise_.set_error(std::move(error)); } else { promise_.set_error(Status::Error(400, error.message())); } stop(); } void start_up() final { auto account_get_authorization_form = telegram_api::account_getAuthorizationForm(bot_user_id_.get(), std::move(scope_), std::move(public_key_)); auto query = G()->net_query_creator().create(account_get_authorization_form); G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this)); } void on_result(NetQueryPtr query) final { auto r_result = fetch_result(std::move(query)); if (r_result.is_error()) { return on_error(r_result.move_as_error()); } promise_.set_value(r_result.move_as_ok()); stop(); } }; SecureManager::SecureManager(ActorShared<> parent) : parent_(std::move(parent)) { } void SecureManager::get_secure_value(std::string password, SecureValueType type, Promise promise) { auto new_promise = PromiseCreator::lambda([promise = std::move(promise)](Result r_secure_value) mutable { if (r_secure_value.is_error()) { return promise.set_error(r_secure_value.move_as_error()); } auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get(); if (file_manager == nullptr) { return promise.set_value(nullptr); } auto r_passport_element = get_passport_element_object(file_manager, r_secure_value.move_as_ok().value); if (r_passport_element.is_error()) { LOG(ERROR) << "Failed to get passport element object: " << r_passport_element.error(); return promise.set_value(nullptr); } promise.set_value(r_passport_element.move_as_ok()); }); refcnt_++; create_actor("GetSecureValue", actor_shared(this), std::move(password), type, std::move(new_promise)) .release(); } class GetPassportConfig final : public NetQueryCallback { public: GetPassportConfig(ActorShared parent, string country_code, Promise> promise) : parent_(std::move(parent)), country_code_(std::move(country_code)), promise_(std::move(promise)) { } private: ActorShared parent_; string country_code_; Promise> promise_; void start_up() final { auto query = G()->net_query_creator().create(telegram_api::help_getPassportConfig(0)); G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this)); } void on_result(NetQueryPtr query) final { auto r_result = fetch_result(std::move(query)); if (r_result.is_error()) { promise_.set_error(r_result.move_as_error()); stop(); return; } auto config = r_result.move_as_ok(); switch (config->get_id()) { case telegram_api::help_passportConfigNotModified::ID: promise_.set_error(Status::Error(500, "Wrong server response")); break; case telegram_api::help_passportConfig::ID: { const string &data = static_cast(config.get())->countries_langs_->data_; auto begin_pos = data.find((PSLICE() << '"' << country_code_ << "\":\"").c_str()); if (begin_pos == string::npos) { promise_.set_value(nullptr); break; } begin_pos += 4 + country_code_.size(); auto end_pos = data.find('"', begin_pos); if (end_pos == string::npos) { return promise_.set_error(Status::Error(500, "Wrong server response")); } promise_.set_value(td_api::make_object(data.substr(begin_pos, end_pos - begin_pos))); break; } default: UNREACHABLE(); } stop(); } }; void SecureManager::on_get_secure_value(SecureValueWithCredentials value) { auto type = value.value.type; secure_value_cache_[type] = std::move(value); } void SecureManager::get_all_secure_values(std::string password, Promise promise) { refcnt_++; create_actor("GetAllSecureValues", actor_shared(this), std::move(password), std::move(promise)) .release(); } void SecureManager::set_secure_value(string password, SecureValue secure_value, Promise promise) { refcnt_++; auto type = secure_value.type; auto new_promise = PromiseCreator::lambda([promise = std::move(promise)](Result r_secure_value) mutable { if (r_secure_value.is_error()) { return promise.set_error(r_secure_value.move_as_error()); } auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get(); auto r_passport_element = get_passport_element_object(file_manager, r_secure_value.move_as_ok().value); if (r_passport_element.is_error()) { LOG(ERROR) << "Failed to get passport element object: " << r_passport_element.error(); return promise.set_error(Status::Error(500, "Failed to get passport element object")); } promise.set_value(r_passport_element.move_as_ok()); }); set_secure_value_queries_[type] = create_actor( "SetSecureValue", actor_shared(this), std::move(password), std::move(secure_value), std::move(new_promise)); } void SecureManager::delete_secure_value(SecureValueType type, Promise promise) { refcnt_++; auto new_promise = PromiseCreator::lambda( [actor_id = actor_id(this), type, promise = std::move(promise)](Result result) mutable { send_closure(actor_id, &SecureManager::on_delete_secure_value, type, std::move(promise), std::move(result)); }); create_actor("DeleteSecureValue", actor_shared(this), type, std::move(new_promise)).release(); } void SecureManager::on_delete_secure_value(SecureValueType type, Promise promise, Result result) { if (result.is_error()) { return promise.set_error(result.move_as_error()); } secure_value_cache_.erase(type); promise.set_value(Unit()); } void SecureManager::set_secure_value_errors(Td *td, tl_object_ptr input_user, vector> errors, Promise promise) { CHECK(td != nullptr); CHECK(input_user != nullptr); vector> input_errors; for (auto &error : errors) { if (error == nullptr) { return promise.set_error(Status::Error(400, "Error must be non-empty")); } if (error->type_ == nullptr) { return promise.set_error(Status::Error(400, "Type must be non-empty")); } if (!clean_input_string(error->message_)) { return promise.set_error(Status::Error(400, "Error message must be encoded in UTF-8")); } if (error->source_ == nullptr) { return promise.set_error(Status::Error(400, "Error source must be non-empty")); } auto type = get_input_secure_value_type(get_secure_value_type_td_api(error->type_)); switch (error->source_->get_id()) { case td_api::inputPassportElementErrorSourceUnspecified::ID: { auto source = td_api::move_object_as(error->source_); input_errors.push_back(make_tl_object( std::move(type), BufferSlice(source->element_hash_), error->message_)); break; } case td_api::inputPassportElementErrorSourceDataField::ID: { auto source = td_api::move_object_as(error->source_); if (!clean_input_string(source->field_name_)) { return promise.set_error(Status::Error(400, "Field name must be encoded in UTF-8")); } input_errors.push_back(make_tl_object( std::move(type), BufferSlice(source->data_hash_), source->field_name_, error->message_)); break; } case td_api::inputPassportElementErrorSourceFrontSide::ID: { auto source = td_api::move_object_as(error->source_); input_errors.push_back(make_tl_object( std::move(type), BufferSlice(source->file_hash_), error->message_)); break; } case td_api::inputPassportElementErrorSourceReverseSide::ID: { auto source = td_api::move_object_as(error->source_); input_errors.push_back(make_tl_object( std::move(type), BufferSlice(source->file_hash_), error->message_)); break; } case td_api::inputPassportElementErrorSourceSelfie::ID: { auto source = td_api::move_object_as(error->source_); input_errors.push_back(make_tl_object( std::move(type), BufferSlice(source->file_hash_), error->message_)); break; } case td_api::inputPassportElementErrorSourceTranslationFile::ID: { auto source = td_api::move_object_as(error->source_); input_errors.push_back(make_tl_object( std::move(type), BufferSlice(source->file_hash_), error->message_)); break; } case td_api::inputPassportElementErrorSourceTranslationFiles::ID: { auto source = td_api::move_object_as(error->source_); if (source->file_hashes_.empty()) { return promise.set_error(Status::Error(400, "File hashes must be non-empty")); } auto file_hashes = transform(source->file_hashes_, [](Slice hash) { return BufferSlice(hash); }); input_errors.push_back(make_tl_object( std::move(type), std::move(file_hashes), error->message_)); break; } case td_api::inputPassportElementErrorSourceFile::ID: { auto source = td_api::move_object_as(error->source_); input_errors.push_back(make_tl_object( std::move(type), BufferSlice(source->file_hash_), error->message_)); break; } case td_api::inputPassportElementErrorSourceFiles::ID: { auto source = td_api::move_object_as(error->source_); if (source->file_hashes_.empty()) { return promise.set_error(Status::Error(400, "File hashes must be non-empty")); } auto file_hashes = transform(source->file_hashes_, [](Slice hash) { return BufferSlice(hash); }); input_errors.push_back(make_tl_object( std::move(type), std::move(file_hashes), error->message_)); break; } default: UNREACHABLE(); } } td->create_handler(std::move(promise)) ->send(std::move(input_user), std::move(input_errors)); } void SecureManager::get_passport_authorization_form(UserId bot_user_id, string scope, string public_key, string nonce, Promise promise) { refcnt_++; auto authorization_form_id = ++max_authorization_form_id_; auto &form = authorization_forms_[authorization_form_id]; form.bot_user_id = bot_user_id; form.scope = scope; form.public_key = public_key; form.nonce = nonce; auto new_promise = PromiseCreator::lambda( [actor_id = actor_id(this), authorization_form_id, promise = std::move(promise)]( Result> r_authorization_form) mutable { send_closure(actor_id, &SecureManager::on_get_passport_authorization_form, authorization_form_id, std::move(promise), std::move(r_authorization_form)); }); create_actor("GetPassportAuthorizationForm", actor_shared(this), bot_user_id, std::move(scope), std::move(public_key), std::move(new_promise)) .release(); } void SecureManager::on_get_passport_authorization_form( int32 authorization_form_id, Promise promise, Result> r_authorization_form) { auto it = authorization_forms_.find(authorization_form_id); CHECK(it != authorization_forms_.end()); CHECK(it->second.is_received == false); if (r_authorization_form.is_error()) { authorization_forms_.erase(it); return promise.set_error(r_authorization_form.move_as_error()); } auto authorization_form = r_authorization_form.move_as_ok(); LOG(INFO) << "Receive " << to_string(authorization_form); G()->td().get_actor_unsafe()->contacts_manager_->on_get_users(std::move(authorization_form->users_), "on_get_passport_authorization_form"); vector> required_types; std::map all_types; for (auto &type_ptr : authorization_form->required_types_) { CHECK(type_ptr != nullptr); vector required_type; switch (type_ptr->get_id()) { case telegram_api::secureRequiredType::ID: { auto value = get_suitable_secure_value(move_tl_object_as(type_ptr)); all_types.emplace(value.type, value); required_type.push_back(std::move(value)); break; } case telegram_api::secureRequiredTypeOneOf::ID: { auto type_one_of = move_tl_object_as(type_ptr); for (auto &type : type_one_of->types_) { if (type->get_id() == telegram_api::secureRequiredType::ID) { auto value = get_suitable_secure_value(move_tl_object_as(type)); all_types.emplace(value.type, value); required_type.push_back(std::move(value)); } else { LOG(ERROR) << to_string(type); } } break; } default: UNREACHABLE(); } if (!required_type.empty()) { required_types.push_back(required_type); } } it->second.options = std::move(all_types); it->second.values = std::move(authorization_form->values_); it->second.errors = std::move(authorization_form->errors_); it->second.is_received = true; promise.set_value(td_api::make_object( authorization_form_id, get_passport_required_elements_object(required_types), authorization_form->privacy_policy_url_)); } void SecureManager::get_passport_authorization_form_available_elements(int32 authorization_form_id, string password, Promise promise) { auto it = authorization_forms_.find(authorization_form_id); if (it == authorization_forms_.end()) { return promise.set_error(Status::Error(400, "Unknown authorization_form_id")); } if (!it->second.is_received) { return promise.set_error(Status::Error(400, "Authorization form isn't received yet")); } refcnt_++; send_closure(G()->password_manager(), &PasswordManager::get_secure_secret, password, PromiseCreator::lambda([actor_id = actor_shared(this), authorization_form_id, promise = std::move(promise)](Result r_secret) mutable { send_closure(actor_id, &SecureManager::on_get_passport_authorization_form_secret, authorization_form_id, std::move(promise), std::move(r_secret)); })); } void SecureManager::on_get_passport_authorization_form_secret(int32 authorization_form_id, Promise promise, Result r_secret) { auto it = authorization_forms_.find(authorization_form_id); if (it == authorization_forms_.end()) { return promise.set_error(Status::Error(400, "Authorization form has already been sent")); } CHECK(it->second.is_received); if (it->second.is_decrypted) { return promise.set_error(Status::Error(400, "Authorization form has already been decrypted")); } if (r_secret.is_error()) { auto error = r_secret.move_as_error(); if (!G()->is_expected_error(error)) { LOG(ERROR) << "Receive error instead of secret: " << error; } if (error.code() <= 0) { error = Status::Error(400, error.message()); // TODO error.set_code(400) ? } return promise.set_error(std::move(error)); } auto secret = r_secret.move_as_ok(); it->second.is_decrypted = true; auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get(); std::vector values; std::map all_credentials; for (auto suitable_type : it->second.options) { auto type = suitable_type.first; for (auto &value : it->second.values) { if (value == nullptr) { continue; } auto value_type = get_secure_value_type(value->type_); if (value_type != type) { continue; } auto r_secure_value = decrypt_secure_value(file_manager, secret, get_encrypted_secure_value(file_manager, std::move(value))); value = nullptr; if (r_secure_value.is_error()) { LOG(ERROR) << "Failed to decrypt secure value: " << r_secure_value.error(); break; } on_get_secure_value(r_secure_value.ok()); auto secure_value = r_secure_value.move_as_ok(); auto r_passport_element = get_passport_element_object(file_manager, std::move(secure_value.value)); if (r_passport_element.is_error()) { LOG(ERROR) << "Failed to get passport element object: " << r_passport_element.error(); break; } values.push_back(r_passport_element.move_as_ok()); all_credentials.emplace(type, std::move(secure_value.credentials)); break; } } auto get_file_index = [](const vector &file_credentials, Slice file_hash) -> int32 { for (size_t i = 0; i < file_credentials.size(); i++) { if (file_credentials[i].hash == file_hash) { return narrow_cast(i); } } return -1; }; vector> errors; for (auto &error_ptr : it->second.errors) { CHECK(error_ptr != nullptr); SecureValueType type = SecureValueType::None; td_api::object_ptr source; string message; switch (error_ptr->get_id()) { case telegram_api::secureValueError::ID: { auto error = move_tl_object_as(error_ptr); type = get_secure_value_type(error->type_); message = std::move(error->text_); source = td_api::make_object(); break; } case telegram_api::secureValueErrorData::ID: { auto error = move_tl_object_as(error_ptr); type = get_secure_value_type(error->type_); message = std::move(error->text_); string field_name = get_secure_value_data_field_name(type, error->field_); if (field_name.empty()) { break; } source = td_api::make_object(std::move(field_name)); break; } case telegram_api::secureValueErrorFile::ID: { auto error = move_tl_object_as(error_ptr); type = get_secure_value_type(error->type_); message = std::move(error->text_); int32 file_index = get_file_index(all_credentials[type].files, error->file_hash_.as_slice()); if (file_index == -1) { LOG(ERROR) << "Can't find file with error"; break; } source = td_api::make_object(file_index); break; } case telegram_api::secureValueErrorFiles::ID: { auto error = move_tl_object_as(error_ptr); type = get_secure_value_type(error->type_); message = std::move(error->text_); source = td_api::make_object(); break; } case telegram_api::secureValueErrorFrontSide::ID: { auto error = move_tl_object_as(error_ptr); type = get_secure_value_type(error->type_); message = std::move(error->text_); source = td_api::make_object(); break; } case telegram_api::secureValueErrorReverseSide::ID: { auto error = move_tl_object_as(error_ptr); type = get_secure_value_type(error->type_); message = std::move(error->text_); source = td_api::make_object(); break; } case telegram_api::secureValueErrorSelfie::ID: { auto error = move_tl_object_as(error_ptr); type = get_secure_value_type(error->type_); message = std::move(error->text_); source = td_api::make_object(); break; } case telegram_api::secureValueErrorTranslationFile::ID: { auto error = move_tl_object_as(error_ptr); type = get_secure_value_type(error->type_); message = std::move(error->text_); int32 file_index = get_file_index(all_credentials[type].translations, error->file_hash_.as_slice()); if (file_index == -1) { LOG(ERROR) << "Can't find translation file with error"; break; } source = td_api::make_object(file_index); break; } case telegram_api::secureValueErrorTranslationFiles::ID: { auto error = move_tl_object_as(error_ptr); type = get_secure_value_type(error->type_); message = std::move(error->text_); source = td_api::make_object(); break; } default: UNREACHABLE(); } if (source == nullptr) { continue; } errors.push_back(td_api::make_object(get_passport_element_type_object(type), message, std::move(source))); } promise.set_value(td_api::make_object(std::move(values), std::move(errors))); } void SecureManager::send_passport_authorization_form(int32 authorization_form_id, std::vector types, Promise<> promise) { auto it = authorization_forms_.find(authorization_form_id); if (it == authorization_forms_.end()) { return promise.set_error(Status::Error(400, "Unknown authorization_form_id")); } if (!it->second.is_received) { return promise.set_error(Status::Error(400, "Authorization form isn't received yet")); } // there is no need to check for is_decrypted if (types.empty()) { return promise.set_error(Status::Error(400, "Types must be non-empty")); } std::vector credentials; credentials.reserve(types.size()); for (auto type : types) { auto value_it = secure_value_cache_.find(type); if (value_it == secure_value_cache_.end()) { return promise.set_error(Status::Error(400, "Passport Element with the specified type is not found")); } credentials.push_back(value_it->second.credentials); } std::vector> hashes; for (auto &c : credentials) { hashes.push_back(telegram_api::make_object(get_input_secure_value_type(c.type), BufferSlice(c.hash))); auto options_it = it->second.options.find(c.type); if (options_it == it->second.options.end()) { return promise.set_error(Status::Error(400, "Passport Element with the specified type was not requested")); } auto &options = options_it->second; if (!options.is_selfie_required) { c.selfie = optional(); } if (!options.is_translation_required) { c.translations.clear(); } } auto r_encrypted_credentials = get_encrypted_credentials(credentials, it->second.nonce, it->second.public_key, it->second.scope[0] == '{' && it->second.scope.back() == '}'); if (r_encrypted_credentials.is_error()) { return promise.set_error(r_encrypted_credentials.move_as_error()); } auto td_query = telegram_api::account_acceptAuthorization( it->second.bot_user_id.get(), it->second.scope, it->second.public_key, std::move(hashes), get_secure_credentials_encrypted_object(r_encrypted_credentials.move_as_ok())); auto query = G()->net_query_creator().create(td_query); auto new_promise = PromiseCreator::lambda([promise = std::move(promise)](Result r_net_query_ptr) mutable { auto r_result = fetch_result(std::move(r_net_query_ptr)); if (r_result.is_error()) { return promise.set_error(r_result.move_as_error()); } promise.set_value(Unit()); }); send_with_promise(std::move(query), std::move(new_promise)); } void SecureManager::get_preferred_country_language(string country_code, Promise> promise) { refcnt_++; for (auto &c : country_code) { c = to_upper(c); } create_actor("GetPassportConfig", actor_shared(this), std::move(country_code), std::move(promise)) .release(); } void SecureManager::hangup() { container_.for_each( [](auto id, Promise &promise) { promise.set_error(Global::request_aborted_error()); }); dec_refcnt(); } void SecureManager::hangup_shared() { dec_refcnt(); } void SecureManager::dec_refcnt() { refcnt_--; if (refcnt_ == 0) { stop(); } } void SecureManager::on_result(NetQueryPtr query) { auto token = get_link_token(); container_.extract(token).set_value(std::move(query)); } void SecureManager::send_with_promise(NetQueryPtr query, Promise promise) { auto id = container_.create(std::move(promise)); G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this, id)); } } // namespace td