Update API scheme to layer 84.

This commit is contained in:
John Preston 2018-08-10 22:19:46 +03:00
parent 550c159ca8
commit bdab477040
20 changed files with 966 additions and 296 deletions

View File

@ -590,7 +590,7 @@ authorization#7bf2e6f6 hash:long flags:int device_model:string platform:string s
account.authorizations#1250abde authorizations:Vector<Authorization> = account.Authorizations; account.authorizations#1250abde authorizations:Vector<Authorization> = account.Authorizations;
account.password#68873ba5 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes = account.Password; account.password#ad2641f8 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes = account.Password;
account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings; account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings;
@ -1022,7 +1022,7 @@ savedPhoneContact#1142bd56 phone:string first_name:string last_name:string date:
account.takeout#4dba4501 id:long = account.Takeout; account.takeout#4dba4501 id:long = account.Takeout;
passwordKdfAlgoUnknown#d45ab096 = PasswordKdfAlgo; passwordKdfAlgoUnknown#d45ab096 = PasswordKdfAlgo;
passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000#b6425eaa salt1:bytes salt2:bytes = PasswordKdfAlgo; passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow#3a912d4a salt1:bytes salt2:bytes g:int p:bytes = PasswordKdfAlgo;
securePasswordKdfAlgoUnknown#4a8537 = SecurePasswordKdfAlgo; securePasswordKdfAlgoUnknown#4a8537 = SecurePasswordKdfAlgo;
securePasswordKdfAlgoPBKDF2HMACSHA512iter100000#bbf2dda0 salt:bytes = SecurePasswordKdfAlgo; securePasswordKdfAlgoPBKDF2HMACSHA512iter100000#bbf2dda0 salt:bytes = SecurePasswordKdfAlgo;
@ -1030,6 +1030,9 @@ securePasswordKdfAlgoSHA512#86471d92 salt:bytes = SecurePasswordKdfAlgo;
secureSecretSettings#1527bcac secure_algo:SecurePasswordKdfAlgo secure_secret:bytes secure_secret_id:long = SecureSecretSettings; secureSecretSettings#1527bcac secure_algo:SecurePasswordKdfAlgo secure_secret:bytes secure_secret_id:long = SecureSecretSettings;
inputCheckPasswordEmpty#9880f658 = InputCheckPasswordSRP;
inputCheckPasswordSRP#d27ff082 srp_id:long A:bytes M1:bytes = InputCheckPasswordSRP;
---functions--- ---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1049,7 +1052,7 @@ auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization;
auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization; auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization;
auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool; auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool;
auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization; auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization;
auth.checkPassword#a63011e password_hash:bytes = auth.Authorization; auth.checkPassword#d18b4d16 password:InputCheckPasswordSRP = auth.Authorization;
auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery; auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery;
auth.recoverPassword#4ea56e92 code:string = auth.Authorization; auth.recoverPassword#4ea56e92 code:string = auth.Authorization;
auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode; auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode;
@ -1078,11 +1081,11 @@ account.updateDeviceLocked#38df3532 period:int = Bool;
account.getAuthorizations#e320c158 = account.Authorizations; account.getAuthorizations#e320c158 = account.Authorizations;
account.resetAuthorization#df77f3bc hash:long = Bool; account.resetAuthorization#df77f3bc hash:long = Bool;
account.getPassword#548a30f5 = account.Password; account.getPassword#548a30f5 = account.Password;
account.getPasswordSettings#bc8d11bb current_password_hash:bytes = account.PasswordSettings; account.getPasswordSettings#9cd4eaf9 password:InputCheckPasswordSRP = account.PasswordSettings;
account.updatePasswordSettings#fa7c4b86 current_password_hash:bytes new_settings:account.PasswordInputSettings = Bool; account.updatePasswordSettings#a59b102f password:InputCheckPasswordSRP new_settings:account.PasswordInputSettings = Bool;
account.sendConfirmPhoneCode#1516d7bd flags:# allow_flashcall:flags.0?true hash:string current_number:flags.0?Bool = auth.SentCode; account.sendConfirmPhoneCode#1516d7bd flags:# allow_flashcall:flags.0?true hash:string current_number:flags.0?Bool = auth.SentCode;
account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool; account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool;
account.getTmpPassword#4a82327e password_hash:bytes period:int = account.TmpPassword; account.getTmpPassword#449e0b51 password:InputCheckPasswordSRP period:int = account.TmpPassword;
account.getWebAuthorizations#182e6d6f = account.WebAuthorizations; account.getWebAuthorizations#182e6d6f = account.WebAuthorizations;
account.resetWebAuthorization#2d01b9ef hash:long = Bool; account.resetWebAuthorization#2d01b9ef hash:long = Bool;
account.resetWebAuthorizations#682d2594 = Bool; account.resetWebAuthorizations#682d2594 = Bool;
@ -1318,4 +1321,4 @@ langpack.getStrings#2e1ee318 lang_code:string keys:Vector<string> = Vector<LangP
langpack.getDifference#b2e4d7d from_version:int = LangPackDifference; langpack.getDifference#b2e4d7d from_version:int = LangPackDifference;
langpack.getLanguages#800fd57d = Vector<LangPackLanguage>; langpack.getLanguages#800fd57d = Vector<LangPackLanguage>;
// LAYER 83 // LAYER 84

View File

@ -12,9 +12,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace bytes { namespace bytes {
using span = gsl::span<gsl::byte>; using type = gsl::byte;
using const_span = gsl::span<const gsl::byte>; using span = gsl::span<type>;
using vector = std::vector<gsl::byte>; using const_span = gsl::span<const type>;
using vector = std::vector<type>;
template <gsl::index Size>
using array = std::array<type, Size>;
template < template <
typename Container, typename Container,
@ -48,6 +52,16 @@ inline const_span make_span(const Type *value, std::size_t count) {
return gsl::as_bytes(gsl::make_span(value, count)); return gsl::as_bytes(gsl::make_span(value, count));
} }
template <typename Type>
inline span object_as_span(Type *value) {
return bytes::make_span(value, 1);
}
template <typename Type>
inline const_span object_as_span(const Type *value) {
return bytes::make_span(value, 1);
}
template <typename Container> template <typename Container>
inline vector make_vector(const Container &container) { inline vector make_vector(const Container &container) {
const auto buffer = bytes::make_span(container); const auto buffer = bytes::make_span(container);

View File

@ -87,18 +87,11 @@ public:
_failed = true; _failed = true;
} }
} }
void setModExp(
const BigNum &a, void setAdd(const BigNum &a, const BigNum &b) {
const BigNum &p, if (a.failed() || b.failed()) {
const BigNum &m,
const Context &context = Context()) {
if (a.failed() || p.failed() || m.failed()) {
_failed = true; _failed = true;
} else if (a.isNegative() || p.isNegative() || m.isNegative()) { } else if (!BN_add(raw(), a.raw(), b.raw())) {
_failed = true;
} else if (!BN_mod_exp(raw(), a.raw(), p.raw(), m.raw(), context.raw())) {
_failed = true;
} else if (isNegative()) {
_failed = true; _failed = true;
} }
} }
@ -116,6 +109,16 @@ public:
_failed = true; _failed = true;
} }
} }
void setMul(
const BigNum &a,
const BigNum &b,
const Context &context = Context()) {
if (a.failed() || b.failed()) {
_failed = true;
} else if (!BN_mul(raw(), a.raw(), b.raw(), context.raw())) {
_failed = true;
}
}
BN_ULONG setDivWord(BN_ULONG word) { BN_ULONG setDivWord(BN_ULONG word) {
Expects(word != 0); Expects(word != 0);
if (failed()) { if (failed()) {
@ -128,6 +131,51 @@ public:
} }
return result; return result;
} }
void setModSub(
const BigNum &a,
const BigNum &b,
const BigNum &m,
const Context &context = Context()) {
if (a.failed() || b.failed() || m.failed()) {
_failed = true;
} else if (a.isNegative() || b.isNegative() || m.isNegative()) {
_failed = true;
} else if (!BN_mod_sub(raw(), a.raw(), b.raw(), m.raw(), context.raw())) {
_failed = true;
} else if (isNegative()) {
_failed = true;
}
}
void setModMul(
const BigNum &a,
const BigNum &b,
const BigNum &m,
const Context &context = Context()) {
if (a.failed() || b.failed() || m.failed()) {
_failed = true;
} else if (a.isNegative() || b.isNegative() || m.isNegative()) {
_failed = true;
} else if (!BN_mod_mul(raw(), a.raw(), b.raw(), m.raw(), context.raw())) {
_failed = true;
} else if (isNegative()) {
_failed = true;
}
}
void setModExp(
const BigNum &base,
const BigNum &power,
const BigNum &m,
const Context &context = Context()) {
if (base.failed() || power.failed() || m.failed()) {
_failed = true;
} else if (base.isNegative() || power.isNegative() || m.isNegative()) {
_failed = true;
} else if (!BN_mod_exp(raw(), base.raw(), power.raw(), m.raw(), context.raw())) {
_failed = true;
} else if (isNegative()) {
_failed = true;
}
}
bool isNegative() const { bool isNegative() const {
return failed() ? false : BN_is_negative(raw()); return failed() ? false : BN_is_negative(raw());
@ -198,9 +246,54 @@ public:
return _failed; return _failed;
} }
static BigNum ModExp(const BigNum &base, const BigNum &power, const openssl::BigNum &mod) { static BigNum Add(const BigNum &a, const BigNum &b) {
BigNum result; BigNum result;
result.setModExp(base, power, mod); result.setAdd(a, b);
return result;
}
static BigNum Sub(const BigNum &a, const BigNum &b) {
BigNum result;
result.setSub(a, b);
return result;
}
static BigNum Mul(
const BigNum &a,
const BigNum &b,
const Context &context = Context()) {
BigNum result;
result.setMul(a, b, context);
return result;
}
static BigNum ModSub(
const BigNum &a,
const BigNum &b,
const BigNum &mod,
const Context &context = Context()) {
BigNum result;
result.setModSub(a, b, mod, context);
return result;
}
static BigNum ModMul(
const BigNum &a,
const BigNum &b,
const BigNum &mod,
const Context &context = Context()) {
BigNum result;
result.setModMul(a, b, mod, context);
return result;
}
static BigNum ModExp(
const BigNum &base,
const BigNum &power,
const BigNum &mod,
const Context &context = Context()) {
BigNum result;
result.setModExp(base, power, mod, context);
return result;
}
static BigNum Failed() {
BigNum result;
result._failed = true;
return result; return result;
} }
@ -210,12 +303,6 @@ private:
}; };
inline BigNum operator-(const BigNum &a, const BigNum &b) {
BigNum result;
result.setSub(a, b);
return result;
}
namespace details { namespace details {
template <typename Context, typename Method, typename Arg> template <typename Context, typename Method, typename Arg>

View File

@ -30,7 +30,7 @@ PasscodeBox::PasscodeBox(QWidget*, bool turningOff)
PasscodeBox::PasscodeBox( PasscodeBox::PasscodeBox(
QWidget*, QWidget*,
const Core::CloudPasswordAlgo &curAlgo, const Core::CloudPasswordCheckRequest &curRequest,
const Core::CloudPasswordAlgo &newAlgo, const Core::CloudPasswordAlgo &newAlgo,
bool hasRecovery, bool hasRecovery,
bool notEmptyPassport, bool notEmptyPassport,
@ -39,19 +39,19 @@ PasscodeBox::PasscodeBox(
bool turningOff) bool turningOff)
: _turningOff(turningOff) : _turningOff(turningOff)
, _cloudPwd(true) , _cloudPwd(true)
, _curAlgo(curAlgo) , _curRequest(curRequest)
, _newAlgo(newAlgo) , _newAlgo(newAlgo)
, _newSecureSecretAlgo(newSecureSecretAlgo) , _newSecureSecretAlgo(newSecureSecretAlgo)
, _hasRecovery(hasRecovery) , _hasRecovery(hasRecovery)
, _notEmptyPassport(notEmptyPassport) , _notEmptyPassport(notEmptyPassport)
, _about(st::boxWidth - st::boxPadding.left() * 1.5) , _about(st::boxWidth - st::boxPadding.left() * 1.5)
, _oldPasscode(this, st::defaultInputField, langFactory(lng_cloud_password_enter_old)) , _oldPasscode(this, st::defaultInputField, langFactory(lng_cloud_password_enter_old))
, _newPasscode(this, st::defaultInputField, langFactory(curAlgo ? lng_cloud_password_enter_new : lng_cloud_password_enter_first)) , _newPasscode(this, st::defaultInputField, langFactory(curRequest ? lng_cloud_password_enter_new : lng_cloud_password_enter_first))
, _reenterPasscode(this, st::defaultInputField, langFactory(lng_cloud_password_confirm_new)) , _reenterPasscode(this, st::defaultInputField, langFactory(lng_cloud_password_confirm_new))
, _passwordHint(this, st::defaultInputField, langFactory(curAlgo ? lng_cloud_password_change_hint : lng_cloud_password_hint)) , _passwordHint(this, st::defaultInputField, langFactory(curRequest ? lng_cloud_password_change_hint : lng_cloud_password_hint))
, _recoverEmail(this, st::defaultInputField, langFactory(lng_cloud_password_email)) , _recoverEmail(this, st::defaultInputField, langFactory(lng_cloud_password_email))
, _recover(this, lang(lng_signin_recover)) { , _recover(this, lang(lng_signin_recover)) {
Expects(!_turningOff || curAlgo.has_value()); Expects(!_turningOff || curRequest);
if (!hint.isEmpty()) _hintText.setText(st::passcodeTextStyle, lng_signin_hint(lt_password_hint, hint)); if (!hint.isEmpty()) _hintText.setText(st::passcodeTextStyle, lng_signin_hint(lt_password_hint, hint));
} }
@ -65,7 +65,7 @@ rpl::producer<> PasscodeBox::passwordReloadNeeded() const {
} }
bool PasscodeBox::currentlyHave() const { bool PasscodeBox::currentlyHave() const {
return _cloudPwd ? _curAlgo.has_value() : Global::LocalPasscode(); return _cloudPwd ? (!!_curRequest) : Global::LocalPasscode();
} }
void PasscodeBox::prepare() { void PasscodeBox::prepare() {
@ -225,10 +225,8 @@ void PasscodeBox::closeReplacedBy() {
} }
} }
bool PasscodeBox::setPasswordFail(const RPCError &error) { void PasscodeBox::setPasswordFail(const RPCError &error) {
if (MTP::isFloodError(error)) { if (MTP::isFloodError(error)) {
if (_oldPasscode->isHidden()) return false;
closeReplacedBy(); closeReplacedBy();
_setRequest = 0; _setRequest = 0;
@ -240,45 +238,36 @@ bool PasscodeBox::setPasswordFail(const RPCError &error) {
_recover->hide(); _recover->hide();
} }
update(); update();
return true; return;
} }
if (MTP::isDefaultHandledError(error)) return false;
closeReplacedBy(); closeReplacedBy();
_setRequest = 0; _setRequest = 0;
QString err = error.type(); const auto err = error.type();
if (err == qstr("PASSWORD_HASH_INVALID")) { if (err == qstr("PASSWORD_HASH_INVALID")
|| err == qstr("SRP_PASSWORD_CHANGED")) {
if (_oldPasscode->isHidden()) { if (_oldPasscode->isHidden()) {
_passwordReloadNeeded.fire({}); _passwordReloadNeeded.fire({});
closeBox(); closeBox();
} else { } else {
badOldPasscode(); badOldPasscode();
} }
} else if (err == qstr("NEW_PASSWORD_BAD")) { } else if (error.type() == qstr("SRP_ID_INVALID")) {
_newPasscode->setFocus(); handleSrpIdInvalid();
_newPasscode->showError(); //} else if (err == qstr("NEW_PASSWORD_BAD")) {
_newError = lang(lng_cloud_password_bad); //} else if (err == qstr("NEW_SALT_INVALID")) {
update();
} else if (err == qstr("NEW_SALT_INVALID")) {
_passwordReloadNeeded.fire({});
closeBox();
} else if (err == qstr("EMAIL_INVALID")) { } else if (err == qstr("EMAIL_INVALID")) {
_emailError = lang(lng_cloud_password_bad_email); _emailError = lang(lng_cloud_password_bad_email);
_recoverEmail->setFocus(); _recoverEmail->setFocus();
_recoverEmail->showError(); _recoverEmail->showError();
update(); update();
} }
return true;
} }
bool PasscodeBox::setPasswordFail( void PasscodeBox::setPasswordFail(
const QByteArray &newPasswordBytes, const QByteArray &newPasswordBytes,
const RPCError &error) { const RPCError &error) {
if (MTP::isFloodError(error)) { if (error.type() == qstr("EMAIL_UNCONFIRMED")) {
return setPasswordFail(error);
} else if (MTP::isDefaultHandledError(error)) {
return setPasswordFail(error);
} else if (error.type() == qstr("EMAIL_UNCONFIRMED")) {
closeReplacedBy(); closeReplacedBy();
_setRequest = 0; _setRequest = 0;
@ -286,9 +275,21 @@ bool PasscodeBox::setPasswordFail(
getDelegate()->show( getDelegate()->show(
Box<InformBox>(lang(lng_cloud_password_almost)), Box<InformBox>(lang(lng_cloud_password_almost)),
LayerOption::CloseOther); LayerOption::CloseOther);
return true;
} else { } else {
return setPasswordFail(error); setPasswordFail(error);
}
}
void PasscodeBox::handleSrpIdInvalid() {
const auto now = getms(true);
if (_lastSrpIdInvalidTime > 0
&& now - _lastSrpIdInvalidTime < Core::kHandleSrpIdInvalidTimeout) {
_curRequest.id = 0;
_oldError = Lang::Hard::ServerError();
update();
} else {
_lastSrpIdInvalidTime = now;
requestPasswordData();
} }
} }
@ -391,10 +392,68 @@ void PasscodeBox::clearCloudPassword(const QString &oldPassword) {
} }
void PasscodeBox::sendClearCloudPassword(const QString &oldPassword) { void PasscodeBox::sendClearCloudPassword(const QString &oldPassword) {
checkPassword(oldPassword, [=](const Core::CloudPasswordResult &check) {
sendClearCloudPassword(check);
});
}
void PasscodeBox::checkPassword(
const QString &oldPassword,
CheckPasswordCallback callback) {
const auto passwordUtf = oldPassword.toUtf8(); const auto passwordUtf = oldPassword.toUtf8();
const auto oldPasswordHash = Core::ComputeCloudPasswordHash( _checkPasswordHash = Core::ComputeCloudPasswordHash(
_curAlgo, _curRequest.algo,
bytes::make_span(passwordUtf)); bytes::make_span(passwordUtf));
checkPasswordHash(std::move(callback));
}
void PasscodeBox::checkPasswordHash(CheckPasswordCallback callback) {
_checkPasswordCallback = std::move(callback);
if (_curRequest.id) {
passwordChecked();
} else {
requestPasswordData();
}
}
void PasscodeBox::passwordChecked() {
if (!_curRequest || !_curRequest.id || !_checkPasswordCallback) {
return serverError();
}
const auto check = Core::ComputeCloudPasswordCheck(
_curRequest,
_checkPasswordHash);
if (!check) {
return serverError();
}
_curRequest.id = 0;
_checkPasswordCallback(check);
}
void PasscodeBox::requestPasswordData() {
if (!_checkPasswordCallback) {
return serverError();
}
request(base::take(_setRequest)).cancel();
_setRequest = request(
MTPaccount_GetPassword()
).done([=](const MTPaccount_Password &result) {
_setRequest = 0;
result.match([&](const MTPDaccount_password &data) {
_curRequest = Core::ParseCloudPasswordCheckRequest(data);
passwordChecked();
});
}).send();
}
void PasscodeBox::serverError() {
getDelegate()->show(Box<InformBox>(Lang::Hard::ServerError()));
closeBox();
}
void PasscodeBox::sendClearCloudPassword(
const Core::CloudPasswordResult &check) {
const auto newPasswordData = QByteArray(); const auto newPasswordData = QByteArray();
const auto newPasswordHash = QByteArray(); const auto newPasswordHash = QByteArray();
const auto hint = QString(); const auto hint = QString();
@ -404,40 +463,42 @@ void PasscodeBox::sendClearCloudPassword(const QString &oldPassword) {
| MTPDaccount_passwordInputSettings::Flag::f_hint | MTPDaccount_passwordInputSettings::Flag::f_hint
| MTPDaccount_passwordInputSettings::Flag::f_email; | MTPDaccount_passwordInputSettings::Flag::f_email;
_setRequest = request(MTPaccount_UpdatePasswordSettings( _setRequest = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(oldPasswordHash), check.result,
MTP_account_passwordInputSettings( MTP_account_passwordInputSettings(
MTP_flags(flags), MTP_flags(flags),
Core::PrepareCloudPasswordAlgo(_newAlgo), Core::PrepareCloudPasswordAlgo(_newAlgo),
MTP_bytes(newPasswordHash), MTP_bytes(QByteArray()), // new_password_hash
MTP_string(hint), MTP_string(hint),
MTP_string(email), MTP_string(email),
MTPSecureSecretSettings()) MTPSecureSecretSettings())
)).done([=](const MTPBool &result) { )).done([=](const MTPBool &result) {
setPasswordDone({}); setPasswordDone({});
}).fail([=](const RPCError &error) { }).handleFloodErrors().fail([=](const RPCError &error) mutable {
setPasswordFail({}, error); setPasswordFail({}, error);
}).send(); }).send();
} }
void PasscodeBox::setNewCloudPassword(const QString &newPassword) { void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
const auto newPasswordBytes = newPassword.toUtf8(); const auto newPasswordBytes = newPassword.toUtf8();
const auto newPasswordHash = Core::ComputeCloudPasswordHash( const auto newPasswordHash = Core::ComputeCloudPasswordDigest(
_newAlgo, _newAlgo,
bytes::make_span(newPasswordBytes)); bytes::make_span(newPasswordBytes));
const auto oldPasswordData = QByteArray(); if (newPasswordHash.modpow.empty()) {
const auto oldPasswordHash = QByteArray(); return serverError();
}
const auto hint = _passwordHint->getLastText(); const auto hint = _passwordHint->getLastText();
const auto email = _recoverEmail->getLastText().trimmed(); const auto email = _recoverEmail->getLastText().trimmed();
const auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo const auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash | MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
| MTPDaccount_passwordInputSettings::Flag::f_hint | MTPDaccount_passwordInputSettings::Flag::f_hint
| MTPDaccount_passwordInputSettings::Flag::f_email; | MTPDaccount_passwordInputSettings::Flag::f_email;
_checkPasswordCallback = nullptr;
_setRequest = request(MTPaccount_UpdatePasswordSettings( _setRequest = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(oldPasswordHash), MTP_inputCheckPasswordEmpty(),
MTP_account_passwordInputSettings( MTP_account_passwordInputSettings(
MTP_flags(flags), MTP_flags(flags),
Core::PrepareCloudPasswordAlgo(_newAlgo), Core::PrepareCloudPasswordAlgo(_newAlgo),
MTP_bytes(newPasswordHash), MTP_bytes(newPasswordHash.modpow),
MTP_string(hint), MTP_string(hint),
MTP_string(email), MTP_string(email),
MTPSecureSecretSettings()) MTPSecureSecretSettings())
@ -451,12 +512,17 @@ void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
void PasscodeBox::changeCloudPassword( void PasscodeBox::changeCloudPassword(
const QString &oldPassword, const QString &oldPassword,
const QString &newPassword) { const QString &newPassword) {
const auto passwordUtf = oldPassword.toUtf8(); checkPassword(oldPassword, [=](const Core::CloudPasswordResult &check) {
const auto oldPasswordHash = Core::ComputeCloudPasswordHash( changeCloudPassword(oldPassword, check, newPassword);
_curAlgo, });
bytes::make_span(passwordUtf)); }
void PasscodeBox::changeCloudPassword(
const QString &oldPassword,
const Core::CloudPasswordResult &check,
const QString &newPassword) {
_setRequest = request(MTPaccount_GetPasswordSettings( _setRequest = request(MTPaccount_GetPasswordSettings(
MTP_bytes(oldPasswordHash) check.result
)).done([=](const MTPaccount_PasswordSettings &result) { )).done([=](const MTPaccount_PasswordSettings &result) {
_setRequest = 0; _setRequest = 0;
@ -464,12 +530,15 @@ void PasscodeBox::changeCloudPassword(
const auto &data = result.c_account_passwordSettings(); const auto &data = result.c_account_passwordSettings();
if (!data.has_secure_settings()) { if (!data.has_secure_settings()) {
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
const auto empty = QByteArray(); const auto empty = QByteArray();
sendChangeCloudPassword(oldPasswordHash, newPassword, empty); sendChangeCloudPassword(check, newPassword, empty);
});
return; return;
} }
const auto &wrapped = data.vsecure_settings; const auto &wrapped = data.vsecure_settings;
const auto &settings = wrapped.c_secureSecretSettings(); const auto &settings = wrapped.c_secureSecretSettings();
const auto passwordUtf = oldPassword.toUtf8();
const auto secret = Passport::DecryptSecureSecret( const auto secret = Passport::DecryptSecureSecret(
bytes::make_span(settings.vsecure_secret.v), bytes::make_span(settings.vsecure_secret.v),
Core::ComputeSecureSecretHash( Core::ComputeSecureSecretHash(
@ -477,32 +546,48 @@ void PasscodeBox::changeCloudPassword(
bytes::make_span(passwordUtf))); bytes::make_span(passwordUtf)));
if (secret.empty()) { if (secret.empty()) {
LOG(("API Error: Failed to decrypt secure secret.")); LOG(("API Error: Failed to decrypt secure secret."));
suggestSecretReset(oldPasswordHash, newPassword); suggestSecretReset(newPassword);
} else if (Passport::CountSecureSecretId(secret) } else if (Passport::CountSecureSecretId(secret)
!= settings.vsecure_secret_id.v) { != settings.vsecure_secret_id.v) {
LOG(("API Error: Wrong secure secret id.")); LOG(("API Error: Wrong secure secret id."));
suggestSecretReset(oldPasswordHash, newPassword); suggestSecretReset(newPassword);
} else { } else {
sendChangeCloudPassword( const auto secureSecret = QByteArray(
oldPasswordHash,
newPassword,
QByteArray::fromRawData(
reinterpret_cast<const char*>(secret.data()), reinterpret_cast<const char*>(secret.data()),
secret.size())); secret.size());
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
sendChangeCloudPassword(check, newPassword, secureSecret);
});
} }
}).fail([=](const RPCError &error) { }).handleFloodErrors().fail([=](const RPCError &error) {
setPasswordFail(error); setPasswordFail(error);
}).send(); }).send();
} }
void PasscodeBox::suggestSecretReset( void PasscodeBox::suggestSecretReset(const QString &newPassword) {
const bytes::vector &oldPasswordHash,
const QString &newPassword) {
const auto box = std::make_shared<QPointer<BoxContent>>(); const auto box = std::make_shared<QPointer<BoxContent>>();
const auto resetSecretAndSave = [=] { const auto resetSecretAndSave = [=] {
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
resetSecret(check, newPassword, [=] {
if (*box) {
(*box)->closeBox();
}
});
});
};
*box = getDelegate()->show(Box<ConfirmBox>(
Lang::Hard::PassportCorruptedChange(),
Lang::Hard::PassportCorruptedReset(),
[=] { resetSecretAndSave(); }));
}
void PasscodeBox::resetSecret(
const Core::CloudPasswordResult &check,
const QString &newPassword,
Fn<void()> callback) {
using Flag = MTPDaccount_passwordInputSettings::Flag; using Flag = MTPDaccount_passwordInputSettings::Flag;
_setRequest = request(MTPaccount_UpdatePasswordSettings( _setRequest = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(oldPasswordHash), check.result,
MTP_account_passwordInputSettings( MTP_account_passwordInputSettings(
MTP_flags(Flag::f_new_secure_settings), MTP_flags(Flag::f_new_secure_settings),
MTPPasswordKdfAlgo(), // new_algo MTPPasswordKdfAlgo(), // new_algo
@ -515,29 +600,30 @@ void PasscodeBox::suggestSecretReset(
MTP_long(0))) // secure_secret_id MTP_long(0))) // secure_secret_id
)).done([=](const MTPBool &result) { )).done([=](const MTPBool &result) {
_setRequest = 0; _setRequest = 0;
callback();
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
const auto empty = QByteArray(); const auto empty = QByteArray();
if (*box) { sendChangeCloudPassword(check, newPassword, empty);
(*box)->closeBox(); });
}
sendChangeCloudPassword(oldPasswordHash, newPassword, empty);
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {
_setRequest = 0; _setRequest = 0;
if (error.type() == qstr("SRP_ID_INVALID")) {
handleSrpIdInvalid();
}
}).send(); }).send();
};
*box = getDelegate()->show(Box<ConfirmBox>(
Lang::Hard::PassportCorrupted(),
Lang::Hard::PassportCorruptedReset(),
[=] { resetSecretAndSave(); }));
} }
void PasscodeBox::sendChangeCloudPassword( void PasscodeBox::sendChangeCloudPassword(
const bytes::vector &oldPasswordHash, const Core::CloudPasswordResult &check,
const QString &newPassword, const QString &newPassword,
const QByteArray &secureSecret) { const QByteArray &secureSecret) {
const auto newPasswordBytes = newPassword.toUtf8(); const auto newPasswordBytes = newPassword.toUtf8();
const auto newPasswordHash = Core::ComputeCloudPasswordHash( const auto newPasswordHash = Core::ComputeCloudPasswordDigest(
_newAlgo, _newAlgo,
bytes::make_span(newPasswordBytes)); bytes::make_span(newPasswordBytes));
if (newPasswordHash.modpow.empty()) {
return serverError();
}
const auto hint = _passwordHint->getLastText(); const auto hint = _passwordHint->getLastText();
auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash | MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
@ -555,11 +641,11 @@ void PasscodeBox::sendChangeCloudPassword(
bytes::make_span(newPasswordBytes))); bytes::make_span(newPasswordBytes)));
} }
_setRequest = request(MTPaccount_UpdatePasswordSettings( _setRequest = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(oldPasswordHash), check.result,
MTP_account_passwordInputSettings( MTP_account_passwordInputSettings(
MTP_flags(flags), MTP_flags(flags),
Core::PrepareCloudPasswordAlgo(_newAlgo), Core::PrepareCloudPasswordAlgo(_newAlgo),
MTP_bytes(newPasswordHash), MTP_bytes(newPasswordHash.modpow),
MTP_string(hint), MTP_string(hint),
MTPstring(), // email is not changing MTPstring(), // email is not changing
MTP_secureSecretSettings( MTP_secureSecretSettings(
@ -568,7 +654,7 @@ void PasscodeBox::sendChangeCloudPassword(
MTP_long(newSecureSecretId))) MTP_long(newSecureSecretId)))
)).done([=](const MTPBool &result) { )).done([=](const MTPBool &result) {
setPasswordDone(newPasswordBytes); setPasswordDone(newPasswordBytes);
}).fail([=](const RPCError &error) { }).handleFloodErrors().fail([=](const RPCError &error) {
setPasswordFail(newPasswordBytes, error); setPasswordFail(newPasswordBytes, error);
}).send(); }).send();
} }
@ -651,12 +737,9 @@ void PasscodeBox::recoverStarted(const MTPauth_PasswordRecovery &result) {
recover(); recover();
} }
bool PasscodeBox::recoverStartFail(const RPCError &error) { void PasscodeBox::recoverStartFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
_pattern = QString(); _pattern = QString();
closeBox(); closeBox();
return true;
} }
RecoverBox::RecoverBox( RecoverBox::RecoverBox(

View File

@ -22,7 +22,7 @@ public:
PasscodeBox(QWidget*, bool turningOff); PasscodeBox(QWidget*, bool turningOff);
PasscodeBox( PasscodeBox(
QWidget*, QWidget*,
const Core::CloudPasswordAlgo &curAlgo, const Core::CloudPasswordCheckRequest &curRequest,
const Core::CloudPasswordAlgo &newAlgo, const Core::CloudPasswordAlgo &newAlgo,
bool hasRecovery, bool hasRecovery,
bool notEmptyPassport, bool notEmptyPassport,
@ -41,6 +41,9 @@ protected:
void resizeEvent(QResizeEvent *e) override; void resizeEvent(QResizeEvent *e) override;
private: private:
using CheckPasswordCallback = Fn<void(
const Core::CloudPasswordResult &check)>;
void submit(); void submit();
void closeReplacedBy(); void closeReplacedBy();
void oldChanged(); void oldChanged();
@ -53,31 +56,51 @@ private:
bool currentlyHave() const; bool currentlyHave() const;
void setPasswordDone(const QByteArray &newPasswordBytes); void setPasswordDone(const QByteArray &newPasswordBytes);
bool setPasswordFail(const RPCError &error); void setPasswordFail(const RPCError &error);
bool setPasswordFail( void setPasswordFail(
const QByteArray &newPasswordBytes, const QByteArray &newPasswordBytes,
const RPCError &error); const RPCError &error);
void recoverStarted(const MTPauth_PasswordRecovery &result); void recoverStarted(const MTPauth_PasswordRecovery &result);
bool recoverStartFail(const RPCError &error); void recoverStartFail(const RPCError &error);
void recover(); void recover();
void clearCloudPassword(const QString &oldPassword); void clearCloudPassword(const QString &oldPassword);
void setNewCloudPassword(const QString &newPassword); void setNewCloudPassword(const QString &newPassword);
void checkPassword(
const QString &oldPassword,
CheckPasswordCallback callback);
void checkPasswordHash(CheckPasswordCallback callback);
void changeCloudPassword( void changeCloudPassword(
const QString &oldPassword, const QString &oldPassword,
const QString &newPassword); const QString &newPassword);
void changeCloudPassword(
const QString &oldPassword,
const Core::CloudPasswordResult &check,
const QString &newPassword);
void sendChangeCloudPassword( void sendChangeCloudPassword(
const bytes::vector &oldPasswordHash, const Core::CloudPasswordResult &check,
const QString &newPassword, const QString &newPassword,
const QByteArray &secureSecret); const QByteArray &secureSecret);
void suggestSecretReset( void suggestSecretReset(const QString &newPassword);
const bytes::vector &oldPasswordHash, void resetSecret(
const QString &newPassword); const Core::CloudPasswordResult &check,
const QString &newPassword,
Fn<void()> callback);
void resetSecretAndChangePassword( void resetSecretAndChangePassword(
const bytes::vector &oldPasswordHash, const bytes::vector &oldPasswordHash,
const QString &newPassword); const QString &newPassword);
void sendClearCloudPassword(const QString &oldPassword); void sendClearCloudPassword(const QString &oldPassword);
void sendClearCloudPassword(const Core::CloudPasswordResult &check);
void handleSrpIdInvalid();
void requestPasswordData();
void passwordChecked();
void serverError();
QString _pattern; QString _pattern;
@ -86,12 +109,15 @@ private:
bool _cloudPwd = false; bool _cloudPwd = false;
mtpRequestId _setRequest = 0; mtpRequestId _setRequest = 0;
Core::CloudPasswordAlgo _curAlgo; Core::CloudPasswordCheckRequest _curRequest;
TimeMs _lastSrpIdInvalidTime = 0;
Core::CloudPasswordAlgo _newAlgo; Core::CloudPasswordAlgo _newAlgo;
Core::SecureSecretAlgo _newSecureSecretAlgo; Core::SecureSecretAlgo _newSecureSecretAlgo;
bool _hasRecovery = false; bool _hasRecovery = false;
bool _notEmptyPassport = false; bool _notEmptyPassport = false;
bool _skipEmailWarning = false; bool _skipEmailWarning = false;
CheckPasswordCallback _checkPasswordCallback;
bytes::vector _checkPasswordHash;
int _aboutHeight = 0; int _aboutHeight = 0;

View File

@ -55,12 +55,13 @@ addChildParentFlags('MTPDchannelForbidden', 'MTPDchannel');
parentFlagsCheck = {}; parentFlagsCheck = {};
countedTypeIdExceptions = {}; countedTypeIdExceptions = {};
for i in range(77, 84): countedTypeIdExceptions['channel#c88974ac'] = True
countedTypeIdExceptions[i] = {} countedTypeIdExceptions['ipPortSecret#37982646'] = True
countedTypeIdExceptions[i]['channel'] = True countedTypeIdExceptions['accessPointRule#4679b65f'] = True
countedTypeIdExceptions['ipPortSecret'] = True countedTypeIdExceptions['help.configSimple#5a592a6c'] = True
countedTypeIdExceptions['accessPointRule'] = True
countedTypeIdExceptions['help_configSimple'] = True renamedTypes = {};
renamedTypes['passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow'] = 'passwordKdfAlgoModPow';
lines = []; lines = [];
layer = ''; layer = '';
@ -119,7 +120,10 @@ for line in lines:
sys.exit(1); sys.exit(1);
continue; continue;
name = nametype.group(1); originalname = nametype.group(1);
name = originalname;
if (name in renamedTypes):
name = renamedTypes[name];
nameInd = name.find('.'); nameInd = name.find('.');
if (nameInd >= 0): if (nameInd >= 0):
Name = name[0:nameInd] + '_' + name[nameInd + 1:nameInd + 2].upper() + name[nameInd + 2:]; Name = name[0:nameInd] + '_' + name[nameInd + 1:nameInd + 2].upper() + name[nameInd + 2:];
@ -144,17 +148,19 @@ for line in lines:
countTypeId = binascii.crc32(binascii.a2b_qp(cleanline)); countTypeId = binascii.crc32(binascii.a2b_qp(cleanline));
if (countTypeId < 0): if (countTypeId < 0):
countTypeId += 2 ** 32; countTypeId += 2 ** 32;
countTypeId = '0x' + re.sub(r'^0x|L$', '', hex(countTypeId)); countTypeId = re.sub(r'^0x|L$', '', hex(countTypeId));
if (typeid and len(typeid) > 0): if (typeid and len(typeid) > 0):
typeid = '0x' + typeid; typeid = typeid;
if (typeid != countTypeId): if (typeid != countTypeId):
if (not layerIndex in countedTypeIdExceptions or not name in countedTypeIdExceptions[layerIndex]): key = originalname + '#' + typeid;
if (not name in countedTypeIdExceptions): if (not key in countedTypeIdExceptions):
print('Warning: counted ' + countTypeId + ' mismatch with provided ' + typeid + ' (' + cleanline + ')'); print('Warning: counted ' + countTypeId + ' mismatch with provided ' + typeid + ' (' + key + ', ' + cleanline + ')');
continue; continue;
else: else:
typeid = countTypeId; typeid = countTypeId;
typeid = '0x' + typeid;
params = nametype.group(3); params = nametype.group(3);
restype = nametype.group(4); restype = nametype.group(4);
if (restype.find('<') >= 0): if (restype.find('<') >= 0):
@ -216,7 +222,7 @@ for line in lines:
if (templ): if (templ):
hasTemplate = templ.group(1); hasTemplate = templ.group(1);
continue; continue;
pnametype = re.match(r'([a-z_][a-z0-9_]*):([A-Za-z0-9<>\._]+|![a-zA-Z]+|\#|[a-z_][a-z0-9_]*\.[0-9]+\?[A-Za-z0-9<>\._]+)$', param); pnametype = re.match(r'([a-zA-Z_][a-zA-Z0-9_]*):([A-Za-z0-9<>\._]+|![a-zA-Z]+|\#|[a-z_][a-z0-9_]*\.[0-9]+\?[A-Za-z0-9<>\._]+)$', param);
if (not pnametype): if (not pnametype):
print('Bad param found: "' + param + '" in line: ' + line); print('Bad param found: "' + param + '" in line: ' + line);
sys.exit(1); sys.exit(1);

View File

@ -8,59 +8,204 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/core_cloud_password.h" #include "core/core_cloud_password.h"
#include "base/openssl_help.h" #include "base/openssl_help.h"
#include "mtproto/connection.h"
namespace Core { namespace Core {
namespace { namespace {
constexpr auto kAdditionalSalt = size_type(8); using namespace openssl;
constexpr auto kAdditionalSalt = size_type(32);
constexpr auto kSizeForHash = 256;
bytes::vector NumBytesForHash(bytes::const_span number) {
const auto fill = kSizeForHash - number.size();
if (!fill) {
return bytes::make_vector(number);
}
auto result = bytes::vector(kSizeForHash);
const auto storage = bytes::make_span(result);
bytes::set_with_const(storage.subspan(0, fill), bytes::type(0));
bytes::copy(storage.subspan(fill), number);
return result;
}
bytes::vector BigNumForHash(const BigNum &number) {
auto result = number.getBytes();
if (result.size() == kSizeForHash) {
return result;
}
return NumBytesForHash(result);
}
bool IsPositive(const BigNum &number) {
return !number.isNegative() && (number.bitsSize() > 0);
}
bool IsGoodLarge(const BigNum &number, const BigNum &p) {
return IsPositive(number) && IsPositive(BigNum::Sub(p, number));
}
bytes::vector Xor(bytes::const_span a, bytes::const_span b) {
Expects(a.size() == b.size());
auto result = bytes::vector(a.size());
for (auto i = index_type(); i != a.size(); ++i) {
result[i] = a[i] ^ b[i];
}
return result;
};
bytes::vector ComputeHash(
const CloudPasswordAlgoModPow &algo,
bytes::const_span password) {
const auto hash1 = Sha256(algo.salt1, password, algo.salt1);
const auto hash2 = Sha256(algo.salt2, hash1, algo.salt2);
const auto hash3 = Pbkdf2Sha512(hash2, algo.salt1, algo.kIterations);
return Sha256(algo.salt2, hash3, algo.salt2);
}
CloudPasswordDigest ComputeDigest(
const CloudPasswordAlgoModPow &algo,
bytes::const_span password) {
if (!MTP::IsPrimeAndGood(algo.p, algo.g)) {
LOG(("API Error: Bad p/g in cloud password creation!"));
return {};
}
const auto value = BigNum::ModExp(
BigNum(algo.g),
BigNum(ComputeHash(algo, password)),
BigNum(algo.p));
if (value.failed()) {
LOG(("API Error: Failed to count g_x in cloud password creation!"));
return {};
}
return { BigNumForHash(value) };
}
CloudPasswordResult ComputeCheck(
const CloudPasswordCheckRequest &request,
const CloudPasswordAlgoModPow &algo,
bytes::const_span hash) {
const auto failed = [] {
return CloudPasswordResult{ MTP_inputCheckPasswordEmpty() };
};
const auto p = BigNum(algo.p);
const auto g = BigNum(algo.g);
const auto B = BigNum(request.B);
if (!MTP::IsPrimeAndGood(algo.p, algo.g)) {
LOG(("API Error: Bad p/g in cloud password creation!"));
return failed();
} else if (!IsGoodLarge(B, p)) {
LOG(("API Error: Bad B in cloud password check!"));
return failed();
}
const auto context = Context();
const auto x = BigNum(hash);
const auto pForHash = NumBytesForHash(algo.p);
const auto gForHash = BigNumForHash(g);
const auto BForHash = NumBytesForHash(request.B);
const auto g_x = BigNum::ModExp(g, x, p, context);
const auto k = BigNum(Sha256(pForHash, gForHash));
const auto kg_x = BigNum::ModMul(k, g_x, p, context);
const auto GenerateAndCheckRandom = [&] {
constexpr auto kRandomSize = 256;
while (true) {
auto random = bytes::vector(kRandomSize);
bytes::set_random(random);
const auto a = BigNum(random);
const auto A = BigNum::ModExp(g, a, p, context);
if (MTP::IsGoodModExpFirst(A, p)) {
auto AForHash = BigNumForHash(A);
const auto u = BigNum(Sha256(AForHash, BForHash));
if (IsPositive(u)) {
return std::make_tuple(a, std::move(AForHash), u);
}
}
}
};
const auto [a, AForHash, u] = GenerateAndCheckRandom();
const auto g_b = BigNum::ModSub(B, kg_x, p, context);
if (!MTP::IsGoodModExpFirst(g_b, p)) {
LOG(("API Error: Bad g_b in cloud password check!"));
return failed();
}
const auto ux = BigNum::Mul(u, x, context);
const auto a_ux = BigNum::Add(a, ux);
const auto S = BigNum::ModExp(g_b, a_ux, p, context);
if (S.failed()) {
LOG(("API Error: Failed to count S in cloud password check!"));
return failed();
}
const auto K = Sha256(BigNumForHash(S));
const auto M1 = Sha256(
Xor(Sha256(pForHash), Sha256(gForHash)),
Sha256(algo.salt1),
Sha256(algo.salt2),
AForHash,
BForHash,
K);
return CloudPasswordResult{ MTP_inputCheckPasswordSRP(
MTP_long(request.id),
MTP_bytes(AForHash),
MTP_bytes(M1))
};
}
bytes::vector ComputeHash( bytes::vector ComputeHash(
base::none_type, base::none_type,
bytes::const_span password) { bytes::const_span password) {
Unexpected("Bad cloud password algorithm."); Unexpected("Bad secure secret algorithm.");
}
bytes::vector ComputeHash(
const CloudPasswordAlgoPBKDF2 &algo,
bytes::const_span password) {
const auto hash1 = openssl::Sha256(algo.salt1, password, algo.salt1);
const auto hash2 = openssl::Sha256(algo.salt2, hash1, algo.salt2);
return openssl::Pbkdf2Sha512(
hash2,
bytes::make_span(algo.salt1),
algo.kIterations);
} }
bytes::vector ComputeHash( bytes::vector ComputeHash(
const SecureSecretAlgoSHA512 &algo, const SecureSecretAlgoSHA512 &algo,
bytes::const_span password) { bytes::const_span password) {
return openssl::Sha512(algo.salt, password, algo.salt); return Sha512(algo.salt, password, algo.salt);
} }
bytes::vector ComputeHash( bytes::vector ComputeHash(
const SecureSecretAlgoPBKDF2 &algo, const SecureSecretAlgoPBKDF2 &algo,
bytes::const_span password) { bytes::const_span password) {
return openssl::Pbkdf2Sha512(password, algo.salt, algo.kIterations); return Pbkdf2Sha512(password, algo.salt, algo.kIterations);
} }
} // namespace } // namespace
CloudPasswordAlgo ParseCloudPasswordAlgo(const MTPPasswordKdfAlgo &data) { CloudPasswordAlgo ParseCloudPasswordAlgo(const MTPPasswordKdfAlgo &data) {
return data.match([]( return data.match([](const MTPDpasswordKdfAlgoModPow &data) {
const MTPDpasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000 &data) { return CloudPasswordAlgo(CloudPasswordAlgoModPow{
return CloudPasswordAlgo(CloudPasswordAlgoPBKDF2{
bytes::make_vector(data.vsalt1.v), bytes::make_vector(data.vsalt1.v),
bytes::make_vector(data.vsalt2.v) }); bytes::make_vector(data.vsalt2.v),
data.vg.v,
bytes::make_vector(data.vp.v) });
}, [](const MTPDpasswordKdfAlgoUnknown &data) { }, [](const MTPDpasswordKdfAlgoUnknown &data) {
return CloudPasswordAlgo(); return CloudPasswordAlgo();
}); });
} }
CloudPasswordCheckRequest ParseCloudPasswordCheckRequest(
const MTPDaccount_password &data) {
return CloudPasswordCheckRequest{
(data.has_srp_id() ? data.vsrp_id.v : uint64()),
(data.has_srp_B()
? bytes::make_vector(data.vsrp_B.v)
: bytes::vector()),
(data.has_current_algo()
? ParseCloudPasswordAlgo(data.vcurrent_algo)
: CloudPasswordAlgo())
};
}
CloudPasswordAlgo ValidateNewCloudPasswordAlgo(CloudPasswordAlgo &&parsed) { CloudPasswordAlgo ValidateNewCloudPasswordAlgo(CloudPasswordAlgo &&parsed) {
if (!parsed.is<CloudPasswordAlgoPBKDF2>()) { if (!parsed.is<CloudPasswordAlgoModPow>()) {
return base::none; return base::none;
} }
auto &value = parsed.get_unchecked<CloudPasswordAlgoPBKDF2>(); auto &value = parsed.get_unchecked<CloudPasswordAlgoModPow>();
const auto already = value.salt1.size(); const auto already = value.salt1.size();
value.salt1.resize(already + kAdditionalSalt); value.salt1.resize(already + kAdditionalSalt);
bytes::set_random(bytes::make_span(value.salt1).subspan(already)); bytes::set_random(bytes::make_span(value.salt1).subspan(already));
@ -68,20 +213,48 @@ CloudPasswordAlgo ValidateNewCloudPasswordAlgo(CloudPasswordAlgo &&parsed) {
} }
MTPPasswordKdfAlgo PrepareCloudPasswordAlgo(const CloudPasswordAlgo &data) { MTPPasswordKdfAlgo PrepareCloudPasswordAlgo(const CloudPasswordAlgo &data) {
return data.match([](const CloudPasswordAlgoPBKDF2 &data) { return data.match([](const CloudPasswordAlgoModPow &data) {
return MTP_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000( return MTP_passwordKdfAlgoModPow(
MTP_bytes(data.salt1), MTP_bytes(data.salt1),
MTP_bytes(data.salt2)); MTP_bytes(data.salt2),
MTP_int(data.g),
MTP_bytes(data.p));
}, [](base::none_type) { }, [](base::none_type) {
return MTP_passwordKdfAlgoUnknown(); return MTP_passwordKdfAlgoUnknown();
}); });
} }
CloudPasswordResult::operator bool() const {
return (result.type() != mtpc_inputCheckPasswordEmpty);
}
bytes::vector ComputeCloudPasswordHash( bytes::vector ComputeCloudPasswordHash(
const CloudPasswordAlgo &algo, const CloudPasswordAlgo &algo,
bytes::const_span password) { bytes::const_span password) {
return algo.match([&](const auto &data) { return algo.match([&](const CloudPasswordAlgoModPow &data) {
return ComputeHash(data, password); return ComputeHash(data, password);
}, [](base::none_type) -> bytes::vector {
Unexpected("Bad cloud password algorithm.");
});
}
CloudPasswordDigest ComputeCloudPasswordDigest(
const CloudPasswordAlgo &algo,
bytes::const_span password) {
return algo.match([&](const CloudPasswordAlgoModPow &data) {
return ComputeDigest(data, password);
}, [](base::none_type) -> CloudPasswordDigest {
Unexpected("Bad cloud password algorithm.");
});
}
CloudPasswordResult ComputeCloudPasswordCheck(
const CloudPasswordCheckRequest &request,
bytes::const_span hash) {
return request.algo.match([&](const CloudPasswordAlgoModPow &data) {
return ComputeCheck(request, data, hash);
}, [](base::none_type) -> CloudPasswordResult {
Unexpected("Bad cloud password algorithm.");
}); });
} }

View File

@ -11,29 +11,79 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Core { namespace Core {
struct CloudPasswordAlgoPBKDF2 { constexpr auto kHandleSrpIdInvalidTimeout = 60 * TimeMs(1000);
struct CloudPasswordAlgoModPow {
static constexpr auto kIterations = 100000; static constexpr auto kIterations = 100000;
bytes::vector salt1; bytes::vector salt1;
bytes::vector salt2; bytes::vector salt2;
int g = 0;
bytes::vector p;
}; };
inline bool operator==( inline bool operator==(
const CloudPasswordAlgoPBKDF2 &a, const CloudPasswordAlgoModPow &a,
const CloudPasswordAlgoPBKDF2 &b) { const CloudPasswordAlgoModPow &b) {
return (a.salt1 == b.salt1) && (a.salt2 == b.salt2); return (a.salt1 == b.salt1)
&& (a.salt2 == b.salt2)
&& (a.g == b.g)
&& (a.p == b.p);
} }
using CloudPasswordAlgo = base::optional_variant<CloudPasswordAlgoPBKDF2>; using CloudPasswordAlgo = base::optional_variant<CloudPasswordAlgoModPow>;
CloudPasswordAlgo ParseCloudPasswordAlgo(const MTPPasswordKdfAlgo &data); CloudPasswordAlgo ParseCloudPasswordAlgo(const MTPPasswordKdfAlgo &data);
CloudPasswordAlgo ValidateNewCloudPasswordAlgo(CloudPasswordAlgo &&parsed); CloudPasswordAlgo ValidateNewCloudPasswordAlgo(CloudPasswordAlgo &&parsed);
MTPPasswordKdfAlgo PrepareCloudPasswordAlgo(const CloudPasswordAlgo &data); MTPPasswordKdfAlgo PrepareCloudPasswordAlgo(const CloudPasswordAlgo &data);
struct CloudPasswordCheckRequest {
uint64 id = 0;
bytes::vector B;
CloudPasswordAlgo algo;
explicit operator bool() const {
return !!algo;
}
};
inline bool operator==(
const CloudPasswordCheckRequest &a,
const CloudPasswordCheckRequest &b) {
return (a.id == b.id) && (a.B == b.B) && (a.algo == b.algo);
}
inline bool operator!=(
const CloudPasswordCheckRequest &a,
const CloudPasswordCheckRequest &b) {
return !(a == b);
}
CloudPasswordCheckRequest ParseCloudPasswordCheckRequest(
const MTPDaccount_password &data);
struct CloudPasswordResult {
MTPInputCheckPasswordSRP result;
explicit operator bool() const;
};
struct CloudPasswordDigest {
bytes::vector modpow;
};
bytes::vector ComputeCloudPasswordHash( bytes::vector ComputeCloudPasswordHash(
const CloudPasswordAlgo &algo, const CloudPasswordAlgo &algo,
bytes::const_span password); bytes::const_span password);
CloudPasswordDigest ComputeCloudPasswordDigest(
const CloudPasswordAlgo &algo,
bytes::const_span password);
CloudPasswordResult ComputeCloudPasswordCheck(
const CloudPasswordCheckRequest &request,
bytes::const_span hash);
struct SecureSecretAlgoSHA512 { struct SecureSecretAlgoSHA512 {
bytes::vector salt; bytes::vector salt;
}; };

View File

@ -293,14 +293,12 @@ void CodeWidget::gotPassword(const MTPaccount_Password &result) {
stopCheck(); stopCheck();
_sentRequest = 0; _sentRequest = 0;
const auto &d = result.c_account_password(); const auto &d = result.c_account_password();
getData()->pwdAlgo = d.has_current_algo() getData()->pwdRequest = Core::ParseCloudPasswordCheckRequest(d);
? Core::ParseCloudPasswordAlgo(d.vcurrent_algo) if (!d.has_current_algo() || !d.has_srp_id() || !d.has_srp_B()) {
: Core::CloudPasswordAlgo();
if (!d.has_current_algo()) {
LOG(("API Error: No current password received on login.")); LOG(("API Error: No current password received on login."));
_code->setFocus(); _code->setFocus();
return; return;
} else if (!getData()->pwdAlgo) { } else if (!getData()->pwdRequest) {
const auto box = std::make_shared<QPointer<BoxContent>>(); const auto box = std::make_shared<QPointer<BoxContent>>();
const auto callback = [=] { const auto callback = [=] {
Core::UpdateApplication(); Core::UpdateApplication();
@ -326,7 +324,7 @@ void CodeWidget::submit() {
_checkRequest->start(1000); _checkRequest->start(1000);
_sentCode = _code->getLastText(); _sentCode = _code->getLastText();
getData()->pwdAlgo = Core::CloudPasswordAlgo(); getData()->pwdRequest = Core::CloudPasswordCheckRequest();
getData()->hasRecovery = false; getData()->hasRecovery = false;
getData()->pwdHint = QString(); getData()->pwdHint = QString();
getData()->pwdNotEmptyPassport = false; getData()->pwdNotEmptyPassport = false;

View File

@ -26,7 +26,7 @@ PwdCheckWidget::PwdCheckWidget(
QWidget *parent, QWidget *parent,
Widget::Data *data) Widget::Data *data)
: Step(parent, data) : Step(parent, data)
, _algo(getData()->pwdAlgo) , _request(getData()->pwdRequest)
, _hasRecovery(getData()->hasRecovery) , _hasRecovery(getData()->hasRecovery)
, _notEmptyPassport(getData()->pwdNotEmptyPassport) , _notEmptyPassport(getData()->pwdNotEmptyPassport)
, _hint(getData()->pwdHint) , _hint(getData()->pwdHint)
@ -36,7 +36,7 @@ PwdCheckWidget::PwdCheckWidget(
, _toRecover(this, lang(lng_signin_recover)) , _toRecover(this, lang(lng_signin_recover))
, _toPassword(this, lang(lng_signin_try_password)) , _toPassword(this, lang(lng_signin_try_password))
, _checkRequest(this) { , _checkRequest(this) {
Expects(_algo.has_value()); Expects(!!_request);
subscribe(Lang::Current().updated(), [this] { refreshLang(); }); subscribe(Lang::Current().updated(), [this] { refreshLang(); });
@ -103,7 +103,7 @@ void PwdCheckWidget::activate() {
} }
void PwdCheckWidget::cancelled() { void PwdCheckWidget::cancelled() {
MTP::cancel(base::take(_sentRequest)); request(base::take(_sentRequest)).cancel();
} }
void PwdCheckWidget::stopCheck() { void PwdCheckWidget::stopCheck() {
@ -115,7 +115,7 @@ void PwdCheckWidget::onCheckRequest() {
if (status < 0) { if (status < 0) {
auto leftms = -status; auto leftms = -status;
if (leftms >= 1000) { if (leftms >= 1000) {
MTP::cancel(base::take(_sentRequest)); request(base::take(_sentRequest)).cancel();
} }
} }
if (!_sentRequest && status == MTP::RequestSent) { if (!_sentRequest && status == MTP::RequestSent) {
@ -137,27 +137,28 @@ void PwdCheckWidget::pwdSubmitDone(bool recover, const MTPauth_Authorization &re
finish(d.vuser); finish(d.vuser);
} }
bool PwdCheckWidget::pwdSubmitFail(const RPCError &error) { void PwdCheckWidget::pwdSubmitFail(const RPCError &error) {
if (MTP::isFloodError(error)) { if (MTP::isFloodError(error)) {
_sentRequest = 0; _sentRequest = 0;
stopCheck(); stopCheck();
showError(langFactory(lng_flood_error)); showError(langFactory(lng_flood_error));
_pwdField->showError(); _pwdField->showError();
return true; return;
} }
if (MTP::isDefaultHandledError(error)) return false;
_sentRequest = 0; _sentRequest = 0;
stopCheck(); stopCheck();
auto &err = error.type(); auto &err = error.type();
if (err == qstr("PASSWORD_HASH_INVALID")) { if (err == qstr("PASSWORD_HASH_INVALID")
|| err == qstr("SRP_PASSWORD_CHANGED")) {
showError(langFactory(lng_signin_bad_password)); showError(langFactory(lng_signin_bad_password));
_pwdField->selectAll(); _pwdField->selectAll();
_pwdField->showError(); _pwdField->showError();
return true;
} else if (err == qstr("PASSWORD_EMPTY")) { } else if (err == qstr("PASSWORD_EMPTY")) {
goBack(); goBack();
} } else if (err == qstr("SRP_ID_INVALID")) {
handleSrpIdInvalid();
} else {
if (Logs::DebugEnabled()) { // internal server error if (Logs::DebugEnabled()) { // internal server error
auto text = err + ": " + error.description(); auto text = err + ": " + error.description();
showError([text] { return text; }); showError([text] { return text; });
@ -165,36 +166,88 @@ bool PwdCheckWidget::pwdSubmitFail(const RPCError &error) {
showError(&Lang::Hard::ServerError); showError(&Lang::Hard::ServerError);
} }
_pwdField->setFocus(); _pwdField->setFocus();
return false; }
} }
bool PwdCheckWidget::codeSubmitFail(const RPCError &error) { void PwdCheckWidget::handleSrpIdInvalid() {
const auto now = getms(true);
if (_lastSrpIdInvalidTime > 0
&& now - _lastSrpIdInvalidTime < Core::kHandleSrpIdInvalidTimeout) {
_request.id = 0;
showError(&Lang::Hard::ServerError);
} else {
_lastSrpIdInvalidTime = now;
requestPasswordData();
}
}
void PwdCheckWidget::checkPasswordHash() {
if (_request.id) {
passwordChecked();
} else {
requestPasswordData();
}
}
void PwdCheckWidget::requestPasswordData() {
request(base::take(_sentRequest)).cancel();
_sentRequest = request(
MTPaccount_GetPassword()
).done([=](const MTPaccount_Password &result) {
_sentRequest = 0;
result.match([&](const MTPDaccount_password &data) {
_request = Core::ParseCloudPasswordCheckRequest(data);
passwordChecked();
});
}).send();
}
void PwdCheckWidget::passwordChecked() {
if (!_request || !_request.id) {
return serverError();
}
const auto check = Core::ComputeCloudPasswordCheck(
_request,
_passwordHash);
if (!check) {
return serverError();
}
_request.id = 0;
_sentRequest = request(
MTPauth_CheckPassword(check.result)
).done([=](const MTPauth_Authorization &result) {
pwdSubmitDone(false, result);
}).handleFloodErrors().fail([=](const RPCError &error) {
pwdSubmitFail(error);
}).send();
}
void PwdCheckWidget::serverError() {
showError(&Lang::Hard::ServerError);
}
void PwdCheckWidget::codeSubmitFail(const RPCError &error) {
if (MTP::isFloodError(error)) { if (MTP::isFloodError(error)) {
showError(langFactory(lng_flood_error)); showError(langFactory(lng_flood_error));
_codeField->showError(); _codeField->showError();
return true; return;
} }
if (MTP::isDefaultHandledError(error)) return false;
_sentRequest = 0; _sentRequest = 0;
stopCheck(); stopCheck();
const QString &err = error.type(); const QString &err = error.type();
if (err == qstr("PASSWORD_EMPTY")) { if (err == qstr("PASSWORD_EMPTY")) {
goBack(); goBack();
return true;
} else if (err == qstr("PASSWORD_RECOVERY_NA")) { } else if (err == qstr("PASSWORD_RECOVERY_NA")) {
recoverStartFail(error); recoverStartFail(error);
return true;
} else if (err == qstr("PASSWORD_RECOVERY_EXPIRED")) { } else if (err == qstr("PASSWORD_RECOVERY_EXPIRED")) {
_emailPattern = QString(); _emailPattern = QString();
onToPassword(); onToPassword();
return true;
} else if (err == qstr("CODE_INVALID")) { } else if (err == qstr("CODE_INVALID")) {
showError(langFactory(lng_signin_wrong_code)); showError(langFactory(lng_signin_wrong_code));
_codeField->selectAll(); _codeField->selectAll();
_codeField->showError(); _codeField->showError();
return true; } else {
}
if (Logs::DebugEnabled()) { // internal server error if (Logs::DebugEnabled()) { // internal server error
auto text = err + ": " + error.description(); auto text = err + ": " + error.description();
showError([text] { return text; }); showError([text] { return text; });
@ -202,7 +255,7 @@ bool PwdCheckWidget::codeSubmitFail(const RPCError &error) {
showError(&Lang::Hard::ServerError); showError(&Lang::Hard::ServerError);
} }
_codeField->setFocus(); _codeField->setFocus();
return false; }
} }
void PwdCheckWidget::recoverStarted(const MTPauth_PasswordRecovery &result) { void PwdCheckWidget::recoverStarted(const MTPauth_PasswordRecovery &result) {
@ -210,7 +263,7 @@ void PwdCheckWidget::recoverStarted(const MTPauth_PasswordRecovery &result) {
updateDescriptionText(); updateDescriptionText();
} }
bool PwdCheckWidget::recoverStartFail(const RPCError &error) { void PwdCheckWidget::recoverStartFail(const RPCError &error) {
stopCheck(); stopCheck();
_pwdField->show(); _pwdField->show();
_pwdHint->show(); _pwdHint->show();
@ -219,13 +272,12 @@ bool PwdCheckWidget::recoverStartFail(const RPCError &error) {
updateDescriptionText(); updateDescriptionText();
update(); update();
hideError(); hideError();
return true;
} }
void PwdCheckWidget::onToRecover() { void PwdCheckWidget::onToRecover() {
if (_hasRecovery) { if (_hasRecovery) {
if (_sentRequest) { if (_sentRequest) {
MTP::cancel(base::take(_sentRequest)); request(base::take(_sentRequest)).cancel();
} }
hideError(); hideError();
_toRecover->hide(); _toRecover->hide();
@ -237,7 +289,13 @@ void PwdCheckWidget::onToRecover() {
_codeField->setFocus(); _codeField->setFocus();
updateDescriptionText(); updateDescriptionText();
if (_emailPattern.isEmpty()) { if (_emailPattern.isEmpty()) {
MTP::send(MTPauth_RequestPasswordRecovery(), rpcDone(&PwdCheckWidget::recoverStarted), rpcFail(&PwdCheckWidget::recoverStartFail)); request(
MTPauth_RequestPasswordRecovery()
).done([=](const MTPauth_PasswordRecovery &result) {
recoverStarted(result);
}).fail([=](const RPCError &error) {
recoverStartFail(error);
}).send();
} }
} else { } else {
Ui::show(Box<InformBox>(lang(lng_signin_no_email_forgot), [this] { showReset(); })); Ui::show(Box<InformBox>(lang(lng_signin_no_email_forgot), [this] { showReset(); }));
@ -250,7 +308,7 @@ void PwdCheckWidget::onToPassword() {
void PwdCheckWidget::showReset() { void PwdCheckWidget::showReset() {
if (_sentRequest) { if (_sentRequest) {
MTP::cancel(base::take(_sentRequest)); request(base::take(_sentRequest)).cancel();
} }
_toRecover->show(); _toRecover->show();
_toPassword->hide(); _toPassword->hide();
@ -285,10 +343,13 @@ void PwdCheckWidget::submit() {
return; return;
} }
const auto send = crl::guard(this, [=] { const auto send = crl::guard(this, [=] {
_sentRequest = MTP::send( _sentRequest = request(
MTPauth_RecoverPassword(MTP_string(code)), MTPauth_RecoverPassword(MTP_string(code))
rpcDone(&PwdCheckWidget::pwdSubmitDone, true), ).done([=](const MTPauth_Authorization &result) {
rpcFail(&PwdCheckWidget::codeSubmitFail)); pwdSubmitDone(true, result);
}).handleFloodErrors().fail([=](const RPCError &error) {
codeSubmitFail(error);
}).send();
}); });
if (_notEmptyPassport) { if (_notEmptyPassport) {
@ -310,13 +371,10 @@ void PwdCheckWidget::submit() {
hideError(); hideError();
const auto password = _pwdField->getLastText().toUtf8(); const auto password = _pwdField->getLastText().toUtf8();
const auto hash = Core::ComputeCloudPasswordHash( _passwordHash = Core::ComputeCloudPasswordHash(
_algo, _request.algo,
bytes::make_span(password)); bytes::make_span(password));
_sentRequest = MTP::send( checkPasswordHash();
MTPauth_CheckPassword(MTP_bytes(hash)),
rpcDone(&PwdCheckWidget::pwdSubmitDone, false),
rpcFail(&PwdCheckWidget::pwdSubmitFail));
} }
} }

View File

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "intro/introwidget.h" #include "intro/introwidget.h"
#include "mtproto/sender.h"
namespace Ui { namespace Ui {
class InputField; class InputField;
@ -18,7 +19,7 @@ class LinkButton;
namespace Intro { namespace Intro {
class PwdCheckWidget : public Widget::Step { class PwdCheckWidget : public Widget::Step, private MTP::Sender {
Q_OBJECT Q_OBJECT
public: public:
@ -45,16 +46,23 @@ private:
void updateControlsGeometry(); void updateControlsGeometry();
void pwdSubmitDone(bool recover, const MTPauth_Authorization &result); void pwdSubmitDone(bool recover, const MTPauth_Authorization &result);
bool pwdSubmitFail(const RPCError &error); void pwdSubmitFail(const RPCError &error);
bool codeSubmitFail(const RPCError &error); void codeSubmitFail(const RPCError &error);
bool recoverStartFail(const RPCError &error); void recoverStartFail(const RPCError &error);
void recoverStarted(const MTPauth_PasswordRecovery &result); void recoverStarted(const MTPauth_PasswordRecovery &result);
void updateDescriptionText(); void updateDescriptionText();
void stopCheck(); void stopCheck();
void handleSrpIdInvalid();
void requestPasswordData();
void checkPasswordHash();
void passwordChecked();
void serverError();
Core::CloudPasswordAlgo _algo; Core::CloudPasswordCheckRequest _request;
TimeMs _lastSrpIdInvalidTime = 0;
bytes::vector _passwordHash;
bool _hasRecovery = false; bool _hasRecovery = false;
bool _notEmptyPassport = false; bool _notEmptyPassport = false;
QString _hint, _emailPattern; QString _hint, _emailPattern;

View File

@ -73,7 +73,7 @@ public:
int codeLength = 5; int codeLength = 5;
bool codeByTelegram = false; bool codeByTelegram = false;
Core::CloudPasswordAlgo pwdAlgo; Core::CloudPasswordCheckRequest pwdRequest;
bool hasRecovery = false; bool hasRecovery = false;
QString pwdHint; QString pwdHint;
bool pwdNotEmptyPassport = false; bool pwdNotEmptyPassport = false;

View File

@ -46,6 +46,10 @@ inline QString PassportCorrupted() {
return qsl("It seems your Telegram Passport data was corrupted.\n\nYou can reset your Telegram Passport and restart this authorization."); return qsl("It seems your Telegram Passport data was corrupted.\n\nYou can reset your Telegram Passport and restart this authorization.");
} }
inline QString PassportCorruptedChange() {
return qsl("It seems your Telegram Passport data was corrupted.\n\nYou can reset your Telegram Passport and change your cloud password.");
}
inline QString PassportCorruptedReset() { inline QString PassportCorruptedReset() {
return qsl("Reset"); return qsl("Reset");
} }

View File

@ -61,16 +61,20 @@ QString LogIdsVector(const QVector<MTPlong> &ids) {
return idsStr + "]"; return idsStr + "]";
} }
bool IsGoodModExpFirst(const openssl::BigNum &modexp, const openssl::BigNum &prime) { bool IsGoodModExpFirst(
auto diff = prime - modexp; const openssl::BigNum &modexp,
const openssl::BigNum &prime) {
const auto diff = openssl::BigNum::Sub(prime, modexp);
if (modexp.failed() || prime.failed() || diff.failed()) { if (modexp.failed() || prime.failed() || diff.failed()) {
return false; return false;
} }
constexpr auto kMinDiffBitsCount = 2048 - 64; constexpr auto kMinDiffBitsCount = 2048 - 64;
if (diff.isNegative() || diff.bitsSize() < kMinDiffBitsCount || modexp.bitsSize() < kMinDiffBitsCount) { if (diff.isNegative()
|| diff.bitsSize() < kMinDiffBitsCount
|| modexp.bitsSize() < kMinDiffBitsCount
|| modexp.bytesSize() > kMaxModExpSize) {
return false; return false;
} }
Assert(modexp.bytesSize() <= kMaxModExpSize);
return true; return true;
} }
@ -194,21 +198,22 @@ ModExpFirst CreateModExp(
BigNum prime(primeBytes); BigNum prime(primeBytes);
auto result = ModExpFirst(); auto result = ModExpFirst();
constexpr auto kMaxModExpFirstTries = 5;
result.randomPower.resize(ModExpFirst::kRandomPowerSize); result.randomPower.resize(ModExpFirst::kRandomPowerSize);
for (auto tries = 0; tries != kMaxModExpFirstTries; ++tries) { while (true) {
bytes::set_random(result.randomPower); bytes::set_random(result.randomPower);
for (auto i = 0; i != ModExpFirst::kRandomPowerSize; ++i) { for (auto i = 0; i != ModExpFirst::kRandomPowerSize; ++i) {
result.randomPower[i] ^= randomSeed[i]; result.randomPower[i] ^= randomSeed[i];
} }
auto modexp = BigNum::ModExp(BigNum(g), BigNum(result.randomPower), prime); const auto modexp = BigNum::ModExp(
BigNum(g),
BigNum(result.randomPower),
prime);
if (IsGoodModExpFirst(modexp, prime)) { if (IsGoodModExpFirst(modexp, prime)) {
result.modexp = modexp.getBytes(); result.modexp = modexp.getBytes();
break;
}
}
return result; return result;
} }
}
}
void wrapInvokeAfter(SecureRequest &to, const SecureRequest &from, const RequestMap &haveSent, int32 skipBeforeRequest = 0) { void wrapInvokeAfter(SecureRequest &to, const SecureRequest &from, const RequestMap &haveSent, int32 skipBeforeRequest = 0) {
const auto afterId = *(mtpMsgId*)(from->after->data() + 4); const auto afterId = *(mtpMsgId*)(from->after->data() + 4);
@ -3243,6 +3248,10 @@ bool IsPrimeAndGood(bytes::const_span primeBytes, int g) {
return internal::IsPrimeAndGood(primeBytes, g); return internal::IsPrimeAndGood(primeBytes, g);
} }
bool IsGoodModExpFirst(const openssl::BigNum &modexp, const openssl::BigNum &prime) {
return internal::IsGoodModExpFirst(modexp, prime);
}
ModExpFirst CreateModExp(int g, bytes::const_span primeBytes, bytes::const_span randomSeed) { ModExpFirst CreateModExp(int g, bytes::const_span primeBytes, bytes::const_span randomSeed) {
return internal::CreateModExp(g, primeBytes, randomSeed); return internal::CreateModExp(g, primeBytes, randomSeed);
} }

View File

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/auth_key.h" #include "mtproto/auth_key.h"
#include "mtproto/dc_options.h" #include "mtproto/dc_options.h"
#include "mtproto/connection_abstract.h" #include "mtproto/connection_abstract.h"
#include "base/openssl_help.h"
#include "base/timer.h" #include "base/timer.h"
namespace MTP { namespace MTP {
@ -23,6 +24,7 @@ struct ModExpFirst {
bytes::vector modexp; bytes::vector modexp;
bytes::vector randomPower; bytes::vector randomPower;
}; };
bool IsGoodModExpFirst(const openssl::BigNum &modexp, const openssl::BigNum &prime);
ModExpFirst CreateModExp(int g, bytes::const_span primeBytes, bytes::const_span randomSeed); ModExpFirst CreateModExp(int g, bytes::const_span primeBytes, bytes::const_span randomSeed);
bytes::vector CreateAuthKey(bytes::const_span firstBytes, bytes::const_span randomBytes, bytes::const_span primeBytes); bytes::vector CreateAuthKey(bytes::const_span firstBytes, bytes::const_span randomBytes, bytes::const_span primeBytes);

View File

@ -321,7 +321,7 @@ QString FormController::privacyPolicyUrl() const {
bytes::vector FormController::passwordHashForAuth( bytes::vector FormController::passwordHashForAuth(
bytes::const_span password) const { bytes::const_span password) const {
return Core::ComputeCloudPasswordHash(_password.algo, password); return Core::ComputeCloudPasswordHash(_password.request.algo, password);
} }
auto FormController::prepareFinalData() -> FinalData { auto FormController::prepareFinalData() -> FinalData {
@ -442,8 +442,52 @@ std::vector<not_null<const Value*>> FormController::submitGetErrors() {
return {}; return {};
} }
void FormController::checkPasswordHash(
mtpRequestId &guard,
bytes::vector hash,
PasswordCheckCallback callback) {
_passwordCheckHash = std::move(hash);
_passwordCheckCallback = std::move(callback);
if (_password.request.id) {
passwordChecked();
} else {
requestPasswordData(guard);
}
}
void FormController::passwordChecked() {
if (!_password.request || !_password.request.id) {
return passwordServerError();
}
const auto check = Core::ComputeCloudPasswordCheck(
_password.request,
_passwordCheckHash);
if (!check) {
return passwordServerError();
}
_password.request.id = 0;
_passwordCheckCallback(check);
}
void FormController::requestPasswordData(mtpRequestId &guard) {
if (!_passwordCheckCallback) {
return passwordServerError();
}
request(base::take(guard)).cancel();
guard = request(
MTPaccount_GetPassword()
).done([=, &guard](const MTPaccount_Password &result) {
guard = 0;
result.match([&](const MTPDaccount_password &data) {
_password.request = Core::ParseCloudPasswordCheckRequest(data);
passwordChecked();
});
}).send();
}
void FormController::submitPassword(const QByteArray &password) { void FormController::submitPassword(const QByteArray &password) {
Expects(_password.algo.has_value()); Expects(!!_password.request);
const auto submitSaved = !base::take(_savedPasswordValue).isEmpty(); const auto submitSaved = !base::take(_savedPasswordValue).isEmpty();
if (_passwordCheckRequestId) { if (_passwordCheckRequestId) {
@ -452,16 +496,27 @@ void FormController::submitPassword(const QByteArray &password) {
_passwordError.fire(QString()); _passwordError.fire(QString());
return; return;
} }
const auto callback = [=](const Core::CloudPasswordResult &check) {
submitPassword(check, password, submitSaved);
};
checkPasswordHash(
_passwordCheckRequestId,
passwordHashForAuth(bytes::make_span(password)),
callback);
}
void FormController::submitPassword(
const Core::CloudPasswordResult &check,
const QByteArray &password,
bool submitSaved) {
_passwordCheckRequestId = request(MTPaccount_GetPasswordSettings( _passwordCheckRequestId = request(MTPaccount_GetPasswordSettings(
MTP_bytes(passwordHashForAuth(bytes::make_span(password))) check.result
)).handleFloodErrors( )).handleFloodErrors(
).done([=](const MTPaccount_PasswordSettings &result) { ).done([=](const MTPaccount_PasswordSettings &result) {
Expects(result.type() == mtpc_account_passwordSettings); Expects(result.type() == mtpc_account_passwordSettings);
_passwordCheckRequestId = 0; _passwordCheckRequestId = 0;
_savedPasswordValue = QByteArray(); _savedPasswordValue = QByteArray();
const auto hashForAuth = passwordHashForAuth(
bytes::make_span(password));
const auto &data = result.c_account_passwordSettings(); const auto &data = result.c_account_passwordSettings();
_password.confirmedEmail = qs(data.vemail); _password.confirmedEmail = qs(data.vemail);
if (data.has_secure_settings()) { if (data.has_secure_settings()) {
@ -483,7 +538,7 @@ void FormController::submitPassword(const QByteArray &password) {
settings.vsecure_secret_id.v); settings.vsecure_secret_id.v);
if (!_secret.empty()) { if (!_secret.empty()) {
auto saved = SavedCredentials(); auto saved = SavedCredentials();
saved.hashForAuth = hashForAuth; saved.hashForAuth = base::take(_passwordCheckHash);
saved.hashForSecret = hashForSecret; saved.hashForSecret = hashForSecret;
saved.secretId = _secretId; saved.secretId = _secretId;
Auth().data().rememberPassportCredentials( Auth().data().rememberPassportCredentials(
@ -499,13 +554,16 @@ void FormController::submitPassword(const QByteArray &password) {
} }
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {
_passwordCheckRequestId = 0; _passwordCheckRequestId = 0;
if (submitSaved) { if (error.type() == qstr("SRP_ID_INVALID")) {
handleSrpIdInvalid(_passwordCheckRequestId);
} else if (submitSaved) {
// Force reload and show form. // Force reload and show form.
_password = PasswordSettings(); _password = PasswordSettings();
reloadPassword(); reloadPassword();
} else if (MTP::isFloodError(error)) { } else if (MTP::isFloodError(error)) {
_passwordError.fire(lang(lng_flood_error)); _passwordError.fire(lang(lng_flood_error));
} else if (error.type() == qstr("PASSWORD_HASH_INVALID")) { } else if (error.type() == qstr("PASSWORD_HASH_INVALID")
|| error.type() == qstr("SRP_PASSWORD_CHANGED")) {
_passwordError.fire(lang(lng_passport_password_wrong)); _passwordError.fire(lang(lng_passport_password_wrong));
} else { } else {
_passwordError.fire_copy(error.type()); _passwordError.fire_copy(error.type());
@ -513,10 +571,40 @@ void FormController::submitPassword(const QByteArray &password) {
}).send(); }).send();
} }
bool FormController::handleSrpIdInvalid(mtpRequestId &guard) {
const auto now = getms(true);
if (_lastSrpIdInvalidTime > 0
&& now - _lastSrpIdInvalidTime < Core::kHandleSrpIdInvalidTimeout) {
_password.request.id = 0;
_passwordError.fire(Lang::Hard::ServerError());
return false;
} else {
_lastSrpIdInvalidTime = now;
requestPasswordData(guard);
return true;
}
}
void FormController::passwordServerError() {
_view->showCriticalError(Lang::Hard::ServerError());
}
void FormController::checkSavedPasswordSettings( void FormController::checkSavedPasswordSettings(
const SavedCredentials &credentials) { const SavedCredentials &credentials) {
const auto callback = [=](const Core::CloudPasswordResult &check) {
checkSavedPasswordSettings(check, credentials);
};
checkPasswordHash(
_passwordCheckRequestId,
credentials.hashForAuth,
callback);
}
void FormController::checkSavedPasswordSettings(
const Core::CloudPasswordResult &check,
const SavedCredentials &credentials) {
_passwordCheckRequestId = request(MTPaccount_GetPasswordSettings( _passwordCheckRequestId = request(MTPaccount_GetPasswordSettings(
MTP_bytes(credentials.hashForAuth) check.result
)).done([=](const MTPaccount_PasswordSettings &result) { )).done([=](const MTPaccount_PasswordSettings &result) {
Expects(result.type() == mtpc_account_passwordSettings); Expects(result.type() == mtpc_account_passwordSettings);
@ -546,8 +634,12 @@ void FormController::checkSavedPasswordSettings(
} }
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {
_passwordCheckRequestId = 0; _passwordCheckRequestId = 0;
if (error.type() != qstr("SRP_ID_INVALID")
|| !handleSrpIdInvalid(_passwordCheckRequestId)) {
} else {
Auth().data().forgetPassportCredentials(); Auth().data().forgetPassportCredentials();
showForm(); showForm();
}
}).send(); }).send();
} }
@ -601,7 +693,7 @@ void FormController::cancelPassword() {
return; return;
} }
_passwordRequestId = request(MTPaccount_UpdatePasswordSettings( _passwordRequestId = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(QByteArray()), MTP_inputCheckPasswordEmpty(),
MTP_account_passwordInputSettings( MTP_account_passwordInputSettings(
MTP_flags(MTPDaccount_passwordInputSettings::Flag::f_email), MTP_flags(MTPDaccount_passwordInputSettings::Flag::f_email),
MTP_passwordKdfAlgoUnknown(), // new_algo MTP_passwordKdfAlgoUnknown(), // new_algo
@ -662,9 +754,23 @@ void FormController::suggestReset(bytes::vector password) {
// } // }
} }
_view->suggestReset([=] { _view->suggestReset([=] {
const auto callback = [=](const Core::CloudPasswordResult &check) {
resetSecret(check, password);
};
checkPasswordHash(
_saveSecretRequestId,
passwordHashForAuth(bytes::make_span(password)),
callback);
_secretReady.fire({});
});
}
void FormController::resetSecret(
const Core::CloudPasswordResult &check,
const bytes::vector &password) {
using Flag = MTPDaccount_passwordInputSettings::Flag; using Flag = MTPDaccount_passwordInputSettings::Flag;
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings( _saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(passwordHashForAuth(password)), check.result,
MTP_account_passwordInputSettings( MTP_account_passwordInputSettings(
MTP_flags(Flag::f_new_secure_settings), MTP_flags(Flag::f_new_secure_settings),
MTPPasswordKdfAlgo(), // new_algo MTPPasswordKdfAlgo(), // new_algo
@ -680,10 +786,11 @@ void FormController::suggestReset(bytes::vector password) {
generateSecret(password); generateSecret(password);
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {
_saveSecretRequestId = 0; _saveSecretRequestId = 0;
if (error.type() != qstr("SRP_ID_INVALID")
|| !handleSrpIdInvalid(_saveSecretRequestId)) {
formFail(error.type()); formFail(error.type());
}
}).send(); }).send();
_secretReady.fire({});
});
} }
void FormController::decryptValues() { void FormController::decryptValues() {
@ -1792,19 +1899,29 @@ void FormController::generateSecret(bytes::const_span password) {
auto secret = GenerateSecretBytes(); auto secret = GenerateSecretBytes();
auto saved = SavedCredentials(); auto saved = SavedCredentials();
saved.hashForAuth = passwordHashForAuth(password); saved.hashForAuth = _passwordCheckHash;
saved.hashForSecret = Core::ComputeSecureSecretHash( saved.hashForSecret = Core::ComputeSecureSecretHash(
_password.newSecureAlgo, _password.newSecureAlgo,
password); password);
saved.secretId = CountSecureSecretId(secret); saved.secretId = CountSecureSecretId(secret);
auto encryptedSecret = EncryptSecureSecret( const auto callback = [=](const Core::CloudPasswordResult &check) {
saveSecret(check, saved, secret);
};
checkPasswordHash(_saveSecretRequestId, saved.hashForAuth, callback);
}
void FormController::saveSecret(
const Core::CloudPasswordResult &check,
const SavedCredentials &saved,
const bytes::vector &secret) {
const auto encryptedSecret = EncryptSecureSecret(
secret, secret,
saved.hashForSecret); saved.hashForSecret);
using Flag = MTPDaccount_passwordInputSettings::Flag; using Flag = MTPDaccount_passwordInputSettings::Flag;
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings( _saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(saved.hashForAuth), check.result,
MTP_account_passwordInputSettings( MTP_account_passwordInputSettings(
MTP_flags(Flag::f_new_secure_settings), MTP_flags(Flag::f_new_secure_settings),
MTPPasswordKdfAlgo(), // new_algo MTPPasswordKdfAlgo(), // new_algo
@ -1829,7 +1946,10 @@ void FormController::generateSecret(bytes::const_span password) {
} }
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {
_saveSecretRequestId = 0; _saveSecretRequestId = 0;
if (error.type() != qstr("SRP_ID_INVALID")
|| !handleSrpIdInvalid(_saveSecretRequestId)) {
suggestRestart(); suggestRestart();
}
}).send(); }).send();
} }
@ -2126,7 +2246,7 @@ void FormController::showForm() {
|| !_password.newSecureAlgo) { || !_password.newSecureAlgo) {
_view->showUpdateAppBox(); _view->showUpdateAppBox();
return; return;
} else if (_password.algo) { } else if (_password.request) {
if (!_savedPasswordValue.isEmpty()) { if (!_savedPasswordValue.isEmpty()) {
submitPassword(base::duplicate(_savedPasswordValue)); submitPassword(base::duplicate(_savedPasswordValue));
} else if (const auto saved = Auth().data().passportCredentials()) { } else if (const auto saved = Auth().data().passportCredentials()) {
@ -2144,11 +2264,9 @@ bool FormController::applyPassword(const MTPDaccount_password &result) {
settings.hint = qs(result.vhint); settings.hint = qs(result.vhint);
settings.hasRecovery = result.is_has_recovery(); settings.hasRecovery = result.is_has_recovery();
settings.notEmptyPassport = result.is_has_secure_values(); settings.notEmptyPassport = result.is_has_secure_values();
settings.algo = result.has_current_algo() settings.request = Core::ParseCloudPasswordCheckRequest(result);
? Core::ParseCloudPasswordAlgo(result.vcurrent_algo)
: Core::CloudPasswordAlgo();
settings.unknownAlgo = result.has_current_algo() settings.unknownAlgo = result.has_current_algo()
&& !settings.algo; && !settings.request;
settings.unconfirmedPattern = result.has_email_unconfirmed_pattern() settings.unconfirmedPattern = result.has_email_unconfirmed_pattern()
? qs(result.vemail_unconfirmed_pattern) ? qs(result.vemail_unconfirmed_pattern)
: QString(); : QString();

View File

@ -196,7 +196,7 @@ struct Form {
}; };
struct PasswordSettings { struct PasswordSettings {
Core::CloudPasswordAlgo algo; Core::CloudPasswordCheckRequest request;
Core::CloudPasswordAlgo newAlgo; Core::CloudPasswordAlgo newAlgo;
Core::SecureSecretAlgo newSecureAlgo; Core::SecureSecretAlgo newSecureAlgo;
QString hint; QString hint;
@ -207,9 +207,14 @@ struct PasswordSettings {
bool unknownAlgo = false; bool unknownAlgo = false;
bool operator==(const PasswordSettings &other) const { bool operator==(const PasswordSettings &other) const {
return (algo == other.algo) return (request == other.request)
&& (newAlgo == other.newAlgo) // newAlgo and newSecureAlgo are always different, because they have
&& (newSecureAlgo == other.newSecureAlgo) // different random parts added on the client to the server salts.
// && (newAlgo == other.newAlgo)
// && (newSecureAlgo == other.newSecureAlgo)
&& ((!newAlgo && !other.newAlgo) || (newAlgo && other.newAlgo))
&& ((!newSecureAlgo && !other.newSecureAlgo)
|| (newSecureAlgo && other.newSecureAlgo))
&& (hint == other.hint) && (hint == other.hint)
&& (unconfirmedPattern == other.unconfirmedPattern) && (unconfirmedPattern == other.unconfirmedPattern)
&& (confirmedEmail == other.confirmedEmail) && (confirmedEmail == other.confirmedEmail)
@ -310,6 +315,9 @@ public:
~FormController(); ~FormController();
private: private:
using PasswordCheckCallback = Fn<void(
const Core::CloudPasswordResult &check)>;
struct FinalData { struct FinalData {
QVector<MTPSecureValueHash> hashes; QVector<MTPSecureValueHash> hashes;
QByteArray credentials; QByteArray credentials;
@ -340,11 +348,26 @@ private:
File &destination, File &destination,
const std::vector<EditFile> &source) const; const std::vector<EditFile> &source) const;
void submitPassword(
const Core::CloudPasswordResult &check,
const QByteArray &password,
bool submitSaved);
void checkPasswordHash(
mtpRequestId &guard,
bytes::vector hash,
PasswordCheckCallback callback);
bool handleSrpIdInvalid(mtpRequestId &guard);
void requestPasswordData(mtpRequestId &guard);
void passwordChecked();
void passwordServerError();
void passwordDone(const MTPaccount_Password &result); void passwordDone(const MTPaccount_Password &result);
bool applyPassword(const MTPDaccount_password &settings); bool applyPassword(const MTPDaccount_password &settings);
bool applyPassword(PasswordSettings &&settings); bool applyPassword(PasswordSettings &&settings);
bytes::vector passwordHashForAuth(bytes::const_span password) const; bytes::vector passwordHashForAuth(bytes::const_span password) const;
void checkSavedPasswordSettings(const SavedCredentials &credentials); void checkSavedPasswordSettings(const SavedCredentials &credentials);
void checkSavedPasswordSettings(
const Core::CloudPasswordResult &check,
const SavedCredentials &credentials);
void validateSecureSecret( void validateSecureSecret(
bytes::const_span encryptedSecret, bytes::const_span encryptedSecret,
bytes::const_span passwordHashForSecret, bytes::const_span passwordHashForSecret,
@ -361,6 +384,10 @@ private:
void fileLoadProgress(FileKey key, int offset); void fileLoadProgress(FileKey key, int offset);
void fileLoadFail(FileKey key); void fileLoadFail(FileKey key);
void generateSecret(bytes::const_span password); void generateSecret(bytes::const_span password);
void saveSecret(
const Core::CloudPasswordResult &check,
const SavedCredentials &saved,
const bytes::vector &secret);
void subscribeToUploader(); void subscribeToUploader();
void encryptFile( void encryptFile(
@ -410,6 +437,9 @@ private:
FinalData prepareFinalData(); FinalData prepareFinalData();
void suggestReset(bytes::vector password); void suggestReset(bytes::vector password);
void resetSecret(
const Core::CloudPasswordResult &check,
const bytes::vector &password);
void suggestRestart(); void suggestRestart();
void cancelAbort(); void cancelAbort();
void shortPollEmailConfirmation(); void shortPollEmailConfirmation();
@ -423,6 +453,9 @@ private:
mtpRequestId _passwordCheckRequestId = 0; mtpRequestId _passwordCheckRequestId = 0;
PasswordSettings _password; PasswordSettings _password;
TimeMs _lastSrpIdInvalidTime = 0;
bytes::vector _passwordCheckHash;
PasswordCheckCallback _passwordCheckCallback;
QByteArray _savedPasswordValue; QByteArray _savedPasswordValue;
Form _form; Form _form;
bool _cancelled = false; bool _cancelled = false;

View File

@ -472,7 +472,7 @@ void PanelController::setupPassword() {
|| !settings.newSecureAlgo) { || !settings.newSecureAlgo) {
showUpdateAppBox(); showUpdateAppBox();
return; return;
} else if (settings.algo) { } else if (settings.request) {
showAskPassword(); showAskPassword();
return; return;
} }
@ -481,7 +481,7 @@ void PanelController::setupPassword() {
const auto notEmptyPassport = false; const auto notEmptyPassport = false;
const auto hint = QString(); const auto hint = QString();
auto box = show(Box<PasscodeBox>( auto box = show(Box<PasscodeBox>(
Core::CloudPasswordAlgo(), // current algo Core::CloudPasswordCheckRequest(), // current
settings.newAlgo, settings.newAlgo,
hasRecovery, hasRecovery,
notEmptyPassport, notEmptyPassport,

View File

@ -93,7 +93,7 @@ void CloudPasswordState::onEdit() {
return; return;
} }
auto box = Ui::show(Box<PasscodeBox>( auto box = Ui::show(Box<PasscodeBox>(
_curPasswordAlgo, _curPasswordRequest,
_newPasswordAlgo, _newPasswordAlgo,
_hasPasswordRecovery, _hasPasswordRecovery,
_notEmptyPassport, _notEmptyPassport,
@ -122,12 +122,12 @@ void CloudPasswordState::onTurnOff() {
callback)); callback));
return; return;
} }
if (!_curPasswordAlgo) { if (!_curPasswordRequest) {
_turnOff->hide(); _turnOff->hide();
MTP::send( MTP::send(
MTPaccount_UpdatePasswordSettings( MTPaccount_UpdatePasswordSettings(
MTP_bytes(QByteArray()), MTP_inputCheckPasswordEmpty(),
MTP_account_passwordInputSettings( MTP_account_passwordInputSettings(
MTP_flags( MTP_flags(
MTPDaccount_passwordInputSettings::Flag::f_email), MTPDaccount_passwordInputSettings::Flag::f_email),
@ -140,7 +140,7 @@ void CloudPasswordState::onTurnOff() {
rpcFail(&CloudPasswordState::offPasswordFail)); rpcFail(&CloudPasswordState::offPasswordFail));
} else { } else {
auto box = Ui::show(Box<PasscodeBox>( auto box = Ui::show(Box<PasscodeBox>(
_curPasswordAlgo, _curPasswordRequest,
_newPasswordAlgo, _newPasswordAlgo,
_hasPasswordRecovery, _hasPasswordRecovery,
_notEmptyPassport, _notEmptyPassport,
@ -177,10 +177,8 @@ void CloudPasswordState::getPasswordDone(const MTPaccount_Password &result) {
_waitingConfirm = QString(); _waitingConfirm = QString();
const auto &d = result.c_account_password(); const auto &d = result.c_account_password();
_curPasswordAlgo = d.has_current_algo() _curPasswordRequest = Core::ParseCloudPasswordCheckRequest(d);
? Core::ParseCloudPasswordAlgo(d.vcurrent_algo) _unknownPasswordAlgo = d.has_current_algo() && !_curPasswordRequest;
: Core::CloudPasswordAlgo();
_unknownPasswordAlgo = d.has_current_algo() && !_curPasswordAlgo;
_hasPasswordRecovery = d.is_has_recovery(); _hasPasswordRecovery = d.is_has_recovery();
_notEmptyPassport = d.is_has_secure_values(); _notEmptyPassport = d.is_has_secure_values();
_curPasswordHint = qs(d.vhint); _curPasswordHint = qs(d.vhint);
@ -205,7 +203,7 @@ void CloudPasswordState::getPasswordDone(const MTPaccount_Password &result) {
} }
bool CloudPasswordState::hasCloudPassword() const { bool CloudPasswordState::hasCloudPassword() const {
return (_curPasswordAlgo || _unknownPasswordAlgo); return (_curPasswordRequest || _unknownPasswordAlgo);
} }
bool CloudPasswordState::getPasswordFail(const RPCError &error) { bool CloudPasswordState::getPasswordFail(const RPCError &error) {

View File

@ -66,7 +66,7 @@ private:
object_ptr<Ui::LinkButton> _turnOff; object_ptr<Ui::LinkButton> _turnOff;
QString _waitingConfirm; QString _waitingConfirm;
Core::CloudPasswordAlgo _curPasswordAlgo; Core::CloudPasswordCheckRequest _curPasswordRequest;
bool _unknownPasswordAlgo = false; bool _unknownPasswordAlgo = false;
bool _hasPasswordRecovery = false; bool _hasPasswordRecovery = false;
bool _notEmptyPassport = false; bool _notEmptyPassport = false;