Added ability to set cloud password from Api::CloudPassword.

This commit is contained in:
23rd 2022-05-07 00:17:41 +03:00
parent 7e3c54f8d0
commit 3d6f6cdd8f
2 changed files with 210 additions and 11 deletions

View File

@ -7,11 +7,23 @@ 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 "apiwrap.h"
#include "passport/passport_encryption.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
// #TODO Add ability to set recovery email separately.
@ -19,6 +31,15 @@ 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;
@ -26,16 +47,7 @@ void CloudPassword::reload() {
_requestId = _api.request(MTPaccount_GetPassword(
)).done([=](const MTPaccount_Password &result) {
_requestId = 0;
result.match([&](const MTPDaccount_password &data) {
base::RandomAddSeed(bytes::make_span(data.vsecure_random().v));
if (_state) {
*_state = Core::ParseCloudPasswordState(data);
} else {
_state = std::make_unique<Core::CloudPasswordState>(
Core::ParseCloudPasswordState(data));
}
_stateChanges.fire_copy(*_state);
});
apply(ProcessMtpState(result));
}).fail([=] {
_requestId = 0;
}).send();
@ -109,4 +121,178 @@ auto CloudPassword::cancelResetPassword()
};
}
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();
};
}
} // namespace Api

View File

@ -23,6 +23,10 @@ namespace Api {
class CloudPassword final {
public:
struct SetOk {
int unconfirmedEmailLengthCode = 0;
};
using ResetRetryDate = int;
explicit CloudPassword(not_null<ApiWrap*> api);
@ -34,7 +38,16 @@ public:
rpl::producer<ResetRetryDate, QString> resetPassword();
rpl::producer<rpl::no_value, QString> cancelResetPassword();
rpl::producer<SetOk, QString> set(
const QString &oldPassword,
const QString &newPassword,
const QString &hint,
bool hasRecoveryEmail,
const QString &recoveryEmail);
private:
void apply(Core::CloudPasswordState state);
MTP::Sender _api;
mtpRequestId _requestId = 0;
std::unique_ptr<Core::CloudPasswordState> _state;