| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "api/api_cloud_password.h"
- #include "apiwrap.h"
- #include "base/random.h"
- #include "core/core_cloud_password.h"
- #include "passport/passport_encryption.h"
- #include "base/unixtime.h"
- #include "base/call_delayed.h"
- namespace Api {
- namespace {
- [[nodiscard]] Core::CloudPasswordState ProcessMtpState(
- const MTPaccount_password &state) {
- return state.match([&](const MTPDaccount_password &data) {
- base::RandomAddSeed(bytes::make_span(data.vsecure_random().v));
- return Core::ParseCloudPasswordState(data);
- });
- }
- } // namespace
- CloudPassword::CloudPassword(not_null<ApiWrap*> api)
- : _api(&api->instance()) {
- }
- void CloudPassword::apply(Core::CloudPasswordState state) {
- if (_state) {
- *_state = std::move(state);
- } else {
- _state = std::make_unique<Core::CloudPasswordState>(std::move(state));
- }
- _stateChanges.fire_copy(*_state);
- }
- void CloudPassword::reload() {
- if (_requestId) {
- return;
- }
- _requestId = _api.request(MTPaccount_GetPassword(
- )).done([=](const MTPaccount_Password &result) {
- _requestId = 0;
- apply(ProcessMtpState(result));
- }).fail([=] {
- _requestId = 0;
- }).send();
- }
- void CloudPassword::clearUnconfirmedPassword() {
- _requestId = _api.request(MTPaccount_CancelPasswordEmail(
- )).done([=] {
- _requestId = 0;
- reload();
- }).fail([=] {
- _requestId = 0;
- reload();
- }).send();
- }
- rpl::producer<Core::CloudPasswordState> CloudPassword::state() const {
- return _state
- ? _stateChanges.events_starting_with_copy(*_state)
- : (_stateChanges.events() | rpl::type_erased());
- }
- auto CloudPassword::stateCurrent() const
- -> std::optional<Core::CloudPasswordState> {
- return _state
- ? base::make_optional(*_state)
- : std::nullopt;
- }
- auto CloudPassword::resetPassword()
- -> rpl::producer<CloudPassword::ResetRetryDate, QString> {
- return [=](auto consumer) {
- _api.request(MTPaccount_ResetPassword(
- )).done([=](const MTPaccount_ResetPasswordResult &result) {
- result.match([&](const MTPDaccount_resetPasswordOk &data) {
- reload();
- }, [&](const MTPDaccount_resetPasswordRequestedWait &data) {
- if (!_state) {
- reload();
- return;
- }
- const auto until = data.vuntil_date().v;
- if (_state->pendingResetDate != until) {
- _state->pendingResetDate = until;
- _stateChanges.fire_copy(*_state);
- }
- }, [&](const MTPDaccount_resetPasswordFailedWait &data) {
- consumer.put_next_copy(data.vretry_date().v);
- });
- consumer.put_done();
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).send();
- return rpl::lifetime();
- };
- }
- auto CloudPassword::cancelResetPassword()
- -> rpl::producer<rpl::no_value, QString> {
- return [=](auto consumer) {
- _api.request(MTPaccount_DeclinePasswordReset(
- )).done([=] {
- reload();
- consumer.put_done();
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).send();
- return rpl::lifetime();
- };
- }
- rpl::producer<CloudPassword::SetOk, QString> CloudPassword::set(
- const QString &oldPassword,
- const QString &newPassword,
- const QString &hint,
- bool hasRecoveryEmail,
- const QString &recoveryEmail) {
- const auto generatePasswordCheck = [=](
- const Core::CloudPasswordState &latestState) {
- if (oldPassword.isEmpty() || !latestState.hasPassword) {
- return Core::CloudPasswordResult{
- MTP_inputCheckPasswordEmpty()
- };
- }
- const auto hash = Core::ComputeCloudPasswordHash(
- latestState.mtp.request.algo,
- bytes::make_span(oldPassword.toUtf8()));
- return Core::ComputeCloudPasswordCheck(
- latestState.mtp.request,
- hash);
- };
- const auto finish = [=](auto consumer, int unconfirmedEmailLengthCode) {
- _api.request(MTPaccount_GetPassword(
- )).done([=](const MTPaccount_Password &result) {
- apply(ProcessMtpState(result));
- if (unconfirmedEmailLengthCode) {
- consumer.put_next(SetOk{ unconfirmedEmailLengthCode });
- } else {
- consumer.put_done();
- }
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).handleFloodErrors().send();
- };
- const auto sendMTPaccountUpdatePasswordSettings = [=](
- const Core::CloudPasswordState &latestState,
- const QByteArray &secureSecret,
- auto consumer) {
- const auto newPasswordBytes = newPassword.toUtf8();
- const auto newPasswordHash = Core::ComputeCloudPasswordDigest(
- latestState.mtp.newPassword,
- bytes::make_span(newPasswordBytes));
- if (!newPassword.isEmpty() && newPasswordHash.modpow.empty()) {
- consumer.put_error("INTERNAL_SERVER_ERROR");
- return;
- }
- using Flag = MTPDaccount_passwordInputSettings::Flag;
- const auto flags = Flag::f_new_algo
- | Flag::f_new_password_hash
- | Flag::f_hint
- | (secureSecret.isEmpty() ? Flag(0) : Flag::f_new_secure_settings)
- | ((!hasRecoveryEmail) ? Flag(0) : Flag::f_email);
- auto newSecureSecret = bytes::vector();
- auto newSecureSecretId = 0ULL;
- if (!secureSecret.isEmpty()) {
- newSecureSecretId = Passport::CountSecureSecretId(
- bytes::make_span(secureSecret));
- newSecureSecret = Passport::EncryptSecureSecret(
- bytes::make_span(secureSecret),
- Core::ComputeSecureSecretHash(
- latestState.mtp.newSecureSecret,
- bytes::make_span(newPasswordBytes)));
- }
- const auto settings = MTP_account_passwordInputSettings(
- MTP_flags(flags),
- Core::PrepareCloudPasswordAlgo(newPassword.isEmpty()
- ? v::null
- : latestState.mtp.newPassword),
- newPassword.isEmpty()
- ? MTP_bytes()
- : MTP_bytes(newPasswordHash.modpow),
- MTP_string(hint),
- MTP_string(recoveryEmail),
- MTP_secureSecretSettings(
- Core::PrepareSecureSecretAlgo(
- latestState.mtp.newSecureSecret),
- MTP_bytes(newSecureSecret),
- MTP_long(newSecureSecretId)));
- _api.request(MTPaccount_UpdatePasswordSettings(
- generatePasswordCheck(latestState).result,
- settings
- )).done([=] {
- finish(consumer, 0);
- }).fail([=](const MTP::Error &error) {
- const auto &type = error.type();
- const auto prefix = u"EMAIL_UNCONFIRMED_"_q;
- if (type.startsWith(prefix)) {
- const auto codeLength = base::StringViewMid(
- type,
- prefix.size()).toInt();
- finish(consumer, codeLength);
- } else {
- consumer.put_error_copy(type);
- }
- }).handleFloodErrors().send();
- };
- return [=](auto consumer) {
- _api.request(MTPaccount_GetPassword(
- )).done([=](const MTPaccount_Password &result) {
- const auto latestState = ProcessMtpState(result);
- if (latestState.hasPassword
- && !oldPassword.isEmpty()
- && !newPassword.isEmpty()) {
- _api.request(MTPaccount_GetPasswordSettings(
- generatePasswordCheck(latestState).result
- )).done([=](const MTPaccount_PasswordSettings &result) {
- using Settings = MTPDaccount_passwordSettings;
- const auto &data = result.match([&](
- const Settings &data) -> const Settings & {
- return data;
- });
- auto secureSecret = QByteArray();
- if (const auto wrapped = data.vsecure_settings()) {
- using Secure = MTPDsecureSecretSettings;
- const auto &settings = wrapped->match([](
- const Secure &data) -> const Secure & {
- return data;
- });
- const auto passwordUtf = oldPassword.toUtf8();
- const auto secret = Passport::DecryptSecureSecret(
- bytes::make_span(settings.vsecure_secret().v),
- Core::ComputeSecureSecretHash(
- Core::ParseSecureSecretAlgo(
- settings.vsecure_algo()),
- bytes::make_span(passwordUtf)));
- if (secret.empty()) {
- LOG(("API Error: "
- "Failed to decrypt secure secret."));
- consumer.put_error("SUGGEST_SECRET_RESET");
- return;
- } else if (Passport::CountSecureSecretId(secret)
- != settings.vsecure_secret_id().v) {
- LOG(("API Error: Wrong secure secret id."));
- consumer.put_error("SUGGEST_SECRET_RESET");
- return;
- } else {
- secureSecret = QByteArray(
- reinterpret_cast<const char*>(secret.data()),
- secret.size());
- }
- }
- _api.request(MTPaccount_GetPassword(
- )).done([=](const MTPaccount_Password &result) {
- const auto latestState = ProcessMtpState(result);
- sendMTPaccountUpdatePasswordSettings(
- latestState,
- secureSecret,
- consumer);
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).send();
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).send();
- } else {
- sendMTPaccountUpdatePasswordSettings(
- latestState,
- QByteArray(),
- consumer);
- }
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).send();
- return rpl::lifetime();
- };
- }
- rpl::producer<rpl::no_value, QString> CloudPassword::check(
- const QString &password) {
- return [=](auto consumer) {
- _api.request(MTPaccount_GetPassword(
- )).done([=](const MTPaccount_Password &result) {
- const auto latestState = ProcessMtpState(result);
- const auto input = [&] {
- if (password.isEmpty()) {
- return Core::CloudPasswordResult{
- MTP_inputCheckPasswordEmpty()
- };
- }
- const auto hash = Core::ComputeCloudPasswordHash(
- latestState.mtp.request.algo,
- bytes::make_span(password.toUtf8()));
- return Core::ComputeCloudPasswordCheck(
- latestState.mtp.request,
- hash);
- }();
- _api.request(MTPaccount_GetPasswordSettings(
- input.result
- )).done([=](const MTPaccount_PasswordSettings &result) {
- consumer.put_done();
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).send();
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).send();
- return rpl::lifetime();
- };
- }
- rpl::producer<rpl::no_value, QString> CloudPassword::confirmEmail(
- const QString &code) {
- return [=](auto consumer) {
- _api.request(MTPaccount_ConfirmPasswordEmail(
- MTP_string(code)
- )).done([=] {
- _api.request(MTPaccount_GetPassword(
- )).done([=](const MTPaccount_Password &result) {
- apply(ProcessMtpState(result));
- consumer.put_done();
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).send();
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).handleFloodErrors().send();
- return rpl::lifetime();
- };
- }
- rpl::producer<rpl::no_value, QString> CloudPassword::resendEmailCode() {
- return [=](auto consumer) {
- _api.request(MTPaccount_ResendPasswordEmail(
- )).done([=] {
- _api.request(MTPaccount_GetPassword(
- )).done([=](const MTPaccount_Password &result) {
- apply(ProcessMtpState(result));
- consumer.put_done();
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).send();
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).handleFloodErrors().send();
- return rpl::lifetime();
- };
- }
- rpl::producer<CloudPassword::SetOk, QString> CloudPassword::setEmail(
- const QString &oldPassword,
- const QString &recoveryEmail) {
- const auto generatePasswordCheck = [=](
- const Core::CloudPasswordState &latestState) {
- if (oldPassword.isEmpty() || !latestState.hasPassword) {
- return Core::CloudPasswordResult{
- MTP_inputCheckPasswordEmpty()
- };
- }
- const auto hash = Core::ComputeCloudPasswordHash(
- latestState.mtp.request.algo,
- bytes::make_span(oldPassword.toUtf8()));
- return Core::ComputeCloudPasswordCheck(
- latestState.mtp.request,
- hash);
- };
- const auto finish = [=](auto consumer, int unconfirmedEmailLengthCode) {
- _api.request(MTPaccount_GetPassword(
- )).done([=](const MTPaccount_Password &result) {
- apply(ProcessMtpState(result));
- if (unconfirmedEmailLengthCode) {
- consumer.put_next(SetOk{ unconfirmedEmailLengthCode });
- } else {
- consumer.put_done();
- }
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).handleFloodErrors().send();
- };
- const auto sendMTPaccountUpdatePasswordSettings = [=](
- const Core::CloudPasswordState &latestState,
- auto consumer) {
- const auto settings = MTP_account_passwordInputSettings(
- MTP_flags(MTPDaccount_passwordInputSettings::Flag::f_email),
- MTP_passwordKdfAlgoUnknown(),
- MTP_bytes(),
- MTP_string(),
- MTP_string(recoveryEmail),
- MTPSecureSecretSettings());
- _api.request(MTPaccount_UpdatePasswordSettings(
- generatePasswordCheck(latestState).result,
- settings
- )).done([=] {
- finish(consumer, 0);
- }).fail([=](const MTP::Error &error) {
- const auto &type = error.type();
- const auto prefix = u"EMAIL_UNCONFIRMED_"_q;
- if (type.startsWith(prefix)) {
- const auto codeLength = base::StringViewMid(
- type,
- prefix.size()).toInt();
- finish(consumer, codeLength);
- } else {
- consumer.put_error_copy(type);
- }
- }).handleFloodErrors().send();
- };
- return [=](auto consumer) {
- _api.request(MTPaccount_GetPassword(
- )).done([=](const MTPaccount_Password &result) {
- const auto latestState = ProcessMtpState(result);
- sendMTPaccountUpdatePasswordSettings(latestState, consumer);
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).send();
- return rpl::lifetime();
- };
- }
- rpl::producer<rpl::no_value, QString> CloudPassword::recoverPassword(
- const QString &code,
- const QString &newPassword,
- const QString &newHint) {
- const auto finish = [=](auto consumer) {
- _api.request(MTPaccount_GetPassword(
- )).done([=](const MTPaccount_Password &result) {
- apply(ProcessMtpState(result));
- consumer.put_done();
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).handleFloodErrors().send();
- };
- const auto sendMTPaccountUpdatePasswordSettings = [=](
- const Core::CloudPasswordState &latestState,
- auto consumer) {
- const auto newPasswordBytes = newPassword.toUtf8();
- const auto newPasswordHash = Core::ComputeCloudPasswordDigest(
- latestState.mtp.newPassword,
- bytes::make_span(newPasswordBytes));
- if (!newPassword.isEmpty() && newPasswordHash.modpow.empty()) {
- consumer.put_error("INTERNAL_SERVER_ERROR");
- return;
- }
- using Flag = MTPDaccount_passwordInputSettings::Flag;
- const auto flags = Flag::f_new_algo
- | Flag::f_new_password_hash
- | Flag::f_hint;
- const auto settings = MTP_account_passwordInputSettings(
- MTP_flags(flags),
- Core::PrepareCloudPasswordAlgo(newPassword.isEmpty()
- ? v::null
- : latestState.mtp.newPassword),
- newPassword.isEmpty()
- ? MTP_bytes()
- : MTP_bytes(newPasswordHash.modpow),
- MTP_string(newHint),
- MTP_string(),
- MTPSecureSecretSettings());
- _api.request(MTPauth_RecoverPassword(
- MTP_flags(newPassword.isEmpty()
- ? MTPauth_RecoverPassword::Flags(0)
- : MTPauth_RecoverPassword::Flag::f_new_settings),
- MTP_string(code),
- settings
- )).done([=](const MTPauth_Authorization &result) {
- finish(consumer);
- }).fail([=](const MTP::Error &error) {
- const auto &type = error.type();
- consumer.put_error_copy(type);
- }).handleFloodErrors().send();
- };
- return [=](auto consumer) {
- _api.request(MTPaccount_GetPassword(
- )).done([=](const MTPaccount_Password &result) {
- const auto latestState = ProcessMtpState(result);
- sendMTPaccountUpdatePasswordSettings(latestState, consumer);
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).send();
- return rpl::lifetime();
- };
- }
- rpl::producer<QString, QString> CloudPassword::requestPasswordRecovery() {
- return [=](auto consumer) {
- _api.request(MTPauth_RequestPasswordRecovery(
- )).done([=](const MTPauth_PasswordRecovery &result) {
- result.match([&](const MTPDauth_passwordRecovery &data) {
- consumer.put_next(qs(data.vemail_pattern().v));
- });
- consumer.put_done();
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).send();
- return rpl::lifetime();
- };
- }
- auto CloudPassword::checkRecoveryEmailAddressCode(const QString &code)
- -> rpl::producer<rpl::no_value, QString> {
- return [=](auto consumer) {
- _api.request(MTPauth_CheckRecoveryPassword(
- MTP_string(code)
- )).done([=] {
- consumer.put_done();
- }).fail([=](const MTP::Error &error) {
- consumer.put_error_copy(error.type());
- }).handleFloodErrors().send();
- return rpl::lifetime();
- };
- }
- } // namespace Api
|