mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-25 04:38:23 +00:00
Update API scheme to layer 84.
This commit is contained in:
parent
550c159ca8
commit
bdab477040
@ -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.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;
|
||||
|
||||
@ -1022,7 +1022,7 @@ savedPhoneContact#1142bd56 phone:string first_name:string last_name:string date:
|
||||
account.takeout#4dba4501 id:long = account.Takeout;
|
||||
|
||||
passwordKdfAlgoUnknown#d45ab096 = PasswordKdfAlgo;
|
||||
passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000#b6425eaa salt1:bytes salt2:bytes = PasswordKdfAlgo;
|
||||
passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow#3a912d4a salt1:bytes salt2:bytes g:int p:bytes = PasswordKdfAlgo;
|
||||
|
||||
securePasswordKdfAlgoUnknown#4a8537 = 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;
|
||||
|
||||
inputCheckPasswordEmpty#9880f658 = InputCheckPasswordSRP;
|
||||
inputCheckPasswordSRP#d27ff082 srp_id:long A:bytes M1:bytes = InputCheckPasswordSRP;
|
||||
|
||||
---functions---
|
||||
|
||||
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.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.checkPassword#a63011e password_hash:bytes = auth.Authorization;
|
||||
auth.checkPassword#d18b4d16 password:InputCheckPasswordSRP = auth.Authorization;
|
||||
auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery;
|
||||
auth.recoverPassword#4ea56e92 code:string = auth.Authorization;
|
||||
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.resetAuthorization#df77f3bc hash:long = Bool;
|
||||
account.getPassword#548a30f5 = account.Password;
|
||||
account.getPasswordSettings#bc8d11bb current_password_hash:bytes = account.PasswordSettings;
|
||||
account.updatePasswordSettings#fa7c4b86 current_password_hash:bytes new_settings:account.PasswordInputSettings = Bool;
|
||||
account.getPasswordSettings#9cd4eaf9 password:InputCheckPasswordSRP = account.PasswordSettings;
|
||||
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.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.resetWebAuthorization#2d01b9ef hash:long = 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.getLanguages#800fd57d = Vector<LangPackLanguage>;
|
||||
|
||||
// LAYER 83
|
||||
// LAYER 84
|
||||
|
@ -12,9 +12,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace bytes {
|
||||
|
||||
using span = gsl::span<gsl::byte>;
|
||||
using const_span = gsl::span<const gsl::byte>;
|
||||
using vector = std::vector<gsl::byte>;
|
||||
using type = gsl::byte;
|
||||
using span = gsl::span<type>;
|
||||
using const_span = gsl::span<const type>;
|
||||
using vector = std::vector<type>;
|
||||
|
||||
template <gsl::index Size>
|
||||
using array = std::array<type, Size>;
|
||||
|
||||
template <
|
||||
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));
|
||||
}
|
||||
|
||||
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>
|
||||
inline vector make_vector(const Container &container) {
|
||||
const auto buffer = bytes::make_span(container);
|
||||
|
@ -87,18 +87,11 @@ public:
|
||||
_failed = true;
|
||||
}
|
||||
}
|
||||
void setModExp(
|
||||
const BigNum &a,
|
||||
const BigNum &p,
|
||||
const BigNum &m,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || p.failed() || m.failed()) {
|
||||
|
||||
void setAdd(const BigNum &a, const BigNum &b) {
|
||||
if (a.failed() || b.failed()) {
|
||||
_failed = true;
|
||||
} else if (a.isNegative() || p.isNegative() || m.isNegative()) {
|
||||
_failed = true;
|
||||
} else if (!BN_mod_exp(raw(), a.raw(), p.raw(), m.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
} else if (!BN_add(raw(), a.raw(), b.raw())) {
|
||||
_failed = true;
|
||||
}
|
||||
}
|
||||
@ -116,6 +109,16 @@ public:
|
||||
_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) {
|
||||
Expects(word != 0);
|
||||
if (failed()) {
|
||||
@ -128,6 +131,51 @@ public:
|
||||
}
|
||||
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 {
|
||||
return failed() ? false : BN_is_negative(raw());
|
||||
@ -198,9 +246,54 @@ public:
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -210,12 +303,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
inline BigNum operator-(const BigNum &a, const BigNum &b) {
|
||||
BigNum result;
|
||||
result.setSub(a, b);
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace details {
|
||||
|
||||
template <typename Context, typename Method, typename Arg>
|
||||
|
@ -30,7 +30,7 @@ PasscodeBox::PasscodeBox(QWidget*, bool turningOff)
|
||||
|
||||
PasscodeBox::PasscodeBox(
|
||||
QWidget*,
|
||||
const Core::CloudPasswordAlgo &curAlgo,
|
||||
const Core::CloudPasswordCheckRequest &curRequest,
|
||||
const Core::CloudPasswordAlgo &newAlgo,
|
||||
bool hasRecovery,
|
||||
bool notEmptyPassport,
|
||||
@ -39,19 +39,19 @@ PasscodeBox::PasscodeBox(
|
||||
bool turningOff)
|
||||
: _turningOff(turningOff)
|
||||
, _cloudPwd(true)
|
||||
, _curAlgo(curAlgo)
|
||||
, _curRequest(curRequest)
|
||||
, _newAlgo(newAlgo)
|
||||
, _newSecureSecretAlgo(newSecureSecretAlgo)
|
||||
, _hasRecovery(hasRecovery)
|
||||
, _notEmptyPassport(notEmptyPassport)
|
||||
, _about(st::boxWidth - st::boxPadding.left() * 1.5)
|
||||
, _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))
|
||||
, _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))
|
||||
, _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));
|
||||
}
|
||||
@ -65,7 +65,7 @@ rpl::producer<> PasscodeBox::passwordReloadNeeded() const {
|
||||
}
|
||||
|
||||
bool PasscodeBox::currentlyHave() const {
|
||||
return _cloudPwd ? _curAlgo.has_value() : Global::LocalPasscode();
|
||||
return _cloudPwd ? (!!_curRequest) : Global::LocalPasscode();
|
||||
}
|
||||
|
||||
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 (_oldPasscode->isHidden()) return false;
|
||||
|
||||
closeReplacedBy();
|
||||
_setRequest = 0;
|
||||
|
||||
@ -240,45 +238,36 @@ bool PasscodeBox::setPasswordFail(const RPCError &error) {
|
||||
_recover->hide();
|
||||
}
|
||||
update();
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
if (MTP::isDefaultHandledError(error)) return false;
|
||||
|
||||
closeReplacedBy();
|
||||
_setRequest = 0;
|
||||
QString err = error.type();
|
||||
if (err == qstr("PASSWORD_HASH_INVALID")) {
|
||||
const auto err = error.type();
|
||||
if (err == qstr("PASSWORD_HASH_INVALID")
|
||||
|| err == qstr("SRP_PASSWORD_CHANGED")) {
|
||||
if (_oldPasscode->isHidden()) {
|
||||
_passwordReloadNeeded.fire({});
|
||||
closeBox();
|
||||
} else {
|
||||
badOldPasscode();
|
||||
}
|
||||
} else if (err == qstr("NEW_PASSWORD_BAD")) {
|
||||
_newPasscode->setFocus();
|
||||
_newPasscode->showError();
|
||||
_newError = lang(lng_cloud_password_bad);
|
||||
update();
|
||||
} else if (err == qstr("NEW_SALT_INVALID")) {
|
||||
_passwordReloadNeeded.fire({});
|
||||
closeBox();
|
||||
} else if (error.type() == qstr("SRP_ID_INVALID")) {
|
||||
handleSrpIdInvalid();
|
||||
//} else if (err == qstr("NEW_PASSWORD_BAD")) {
|
||||
//} else if (err == qstr("NEW_SALT_INVALID")) {
|
||||
} else if (err == qstr("EMAIL_INVALID")) {
|
||||
_emailError = lang(lng_cloud_password_bad_email);
|
||||
_recoverEmail->setFocus();
|
||||
_recoverEmail->showError();
|
||||
update();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PasscodeBox::setPasswordFail(
|
||||
void PasscodeBox::setPasswordFail(
|
||||
const QByteArray &newPasswordBytes,
|
||||
const RPCError &error) {
|
||||
if (MTP::isFloodError(error)) {
|
||||
return setPasswordFail(error);
|
||||
} else if (MTP::isDefaultHandledError(error)) {
|
||||
return setPasswordFail(error);
|
||||
} else if (error.type() == qstr("EMAIL_UNCONFIRMED")) {
|
||||
if (error.type() == qstr("EMAIL_UNCONFIRMED")) {
|
||||
closeReplacedBy();
|
||||
_setRequest = 0;
|
||||
|
||||
@ -286,9 +275,21 @@ bool PasscodeBox::setPasswordFail(
|
||||
getDelegate()->show(
|
||||
Box<InformBox>(lang(lng_cloud_password_almost)),
|
||||
LayerOption::CloseOther);
|
||||
return true;
|
||||
} 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) {
|
||||
checkPassword(oldPassword, [=](const Core::CloudPasswordResult &check) {
|
||||
sendClearCloudPassword(check);
|
||||
});
|
||||
}
|
||||
|
||||
void PasscodeBox::checkPassword(
|
||||
const QString &oldPassword,
|
||||
CheckPasswordCallback callback) {
|
||||
const auto passwordUtf = oldPassword.toUtf8();
|
||||
const auto oldPasswordHash = Core::ComputeCloudPasswordHash(
|
||||
_curAlgo,
|
||||
_checkPasswordHash = Core::ComputeCloudPasswordHash(
|
||||
_curRequest.algo,
|
||||
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 newPasswordHash = QByteArray();
|
||||
const auto hint = QString();
|
||||
@ -404,40 +463,42 @@ void PasscodeBox::sendClearCloudPassword(const QString &oldPassword) {
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_hint
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_email;
|
||||
_setRequest = request(MTPaccount_UpdatePasswordSettings(
|
||||
MTP_bytes(oldPasswordHash),
|
||||
check.result,
|
||||
MTP_account_passwordInputSettings(
|
||||
MTP_flags(flags),
|
||||
Core::PrepareCloudPasswordAlgo(_newAlgo),
|
||||
MTP_bytes(newPasswordHash),
|
||||
MTP_bytes(QByteArray()), // new_password_hash
|
||||
MTP_string(hint),
|
||||
MTP_string(email),
|
||||
MTPSecureSecretSettings())
|
||||
)).done([=](const MTPBool &result) {
|
||||
setPasswordDone({});
|
||||
}).fail([=](const RPCError &error) {
|
||||
}).handleFloodErrors().fail([=](const RPCError &error) mutable {
|
||||
setPasswordFail({}, error);
|
||||
}).send();
|
||||
}
|
||||
|
||||
void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
|
||||
const auto newPasswordBytes = newPassword.toUtf8();
|
||||
const auto newPasswordHash = Core::ComputeCloudPasswordHash(
|
||||
const auto newPasswordHash = Core::ComputeCloudPasswordDigest(
|
||||
_newAlgo,
|
||||
bytes::make_span(newPasswordBytes));
|
||||
const auto oldPasswordData = QByteArray();
|
||||
const auto oldPasswordHash = QByteArray();
|
||||
if (newPasswordHash.modpow.empty()) {
|
||||
return serverError();
|
||||
}
|
||||
const auto hint = _passwordHint->getLastText();
|
||||
const auto email = _recoverEmail->getLastText().trimmed();
|
||||
const auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_hint
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_email;
|
||||
_checkPasswordCallback = nullptr;
|
||||
_setRequest = request(MTPaccount_UpdatePasswordSettings(
|
||||
MTP_bytes(oldPasswordHash),
|
||||
MTP_inputCheckPasswordEmpty(),
|
||||
MTP_account_passwordInputSettings(
|
||||
MTP_flags(flags),
|
||||
Core::PrepareCloudPasswordAlgo(_newAlgo),
|
||||
MTP_bytes(newPasswordHash),
|
||||
MTP_bytes(newPasswordHash.modpow),
|
||||
MTP_string(hint),
|
||||
MTP_string(email),
|
||||
MTPSecureSecretSettings())
|
||||
@ -451,12 +512,17 @@ void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
|
||||
void PasscodeBox::changeCloudPassword(
|
||||
const QString &oldPassword,
|
||||
const QString &newPassword) {
|
||||
const auto passwordUtf = oldPassword.toUtf8();
|
||||
const auto oldPasswordHash = Core::ComputeCloudPasswordHash(
|
||||
_curAlgo,
|
||||
bytes::make_span(passwordUtf));
|
||||
checkPassword(oldPassword, [=](const Core::CloudPasswordResult &check) {
|
||||
changeCloudPassword(oldPassword, check, newPassword);
|
||||
});
|
||||
}
|
||||
|
||||
void PasscodeBox::changeCloudPassword(
|
||||
const QString &oldPassword,
|
||||
const Core::CloudPasswordResult &check,
|
||||
const QString &newPassword) {
|
||||
_setRequest = request(MTPaccount_GetPasswordSettings(
|
||||
MTP_bytes(oldPasswordHash)
|
||||
check.result
|
||||
)).done([=](const MTPaccount_PasswordSettings &result) {
|
||||
_setRequest = 0;
|
||||
|
||||
@ -464,12 +530,15 @@ void PasscodeBox::changeCloudPassword(
|
||||
const auto &data = result.c_account_passwordSettings();
|
||||
|
||||
if (!data.has_secure_settings()) {
|
||||
const auto empty = QByteArray();
|
||||
sendChangeCloudPassword(oldPasswordHash, newPassword, empty);
|
||||
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
|
||||
const auto empty = QByteArray();
|
||||
sendChangeCloudPassword(check, newPassword, empty);
|
||||
});
|
||||
return;
|
||||
}
|
||||
const auto &wrapped = data.vsecure_settings;
|
||||
const auto &settings = wrapped.c_secureSecretSettings();
|
||||
const auto passwordUtf = oldPassword.toUtf8();
|
||||
const auto secret = Passport::DecryptSecureSecret(
|
||||
bytes::make_span(settings.vsecure_secret.v),
|
||||
Core::ComputeSecureSecretHash(
|
||||
@ -477,67 +546,84 @@ void PasscodeBox::changeCloudPassword(
|
||||
bytes::make_span(passwordUtf)));
|
||||
if (secret.empty()) {
|
||||
LOG(("API Error: Failed to decrypt secure secret."));
|
||||
suggestSecretReset(oldPasswordHash, newPassword);
|
||||
suggestSecretReset(newPassword);
|
||||
} else if (Passport::CountSecureSecretId(secret)
|
||||
!= settings.vsecure_secret_id.v) {
|
||||
LOG(("API Error: Wrong secure secret id."));
|
||||
suggestSecretReset(oldPasswordHash, newPassword);
|
||||
suggestSecretReset(newPassword);
|
||||
} else {
|
||||
sendChangeCloudPassword(
|
||||
oldPasswordHash,
|
||||
newPassword,
|
||||
QByteArray::fromRawData(
|
||||
reinterpret_cast<const char*>(secret.data()),
|
||||
secret.size()));
|
||||
const auto secureSecret = QByteArray(
|
||||
reinterpret_cast<const char*>(secret.data()),
|
||||
secret.size());
|
||||
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
|
||||
sendChangeCloudPassword(check, newPassword, secureSecret);
|
||||
});
|
||||
}
|
||||
}).fail([=](const RPCError &error) {
|
||||
}).handleFloodErrors().fail([=](const RPCError &error) {
|
||||
setPasswordFail(error);
|
||||
}).send();
|
||||
}
|
||||
|
||||
void PasscodeBox::suggestSecretReset(
|
||||
const bytes::vector &oldPasswordHash,
|
||||
const QString &newPassword) {
|
||||
void PasscodeBox::suggestSecretReset(const QString &newPassword) {
|
||||
const auto box = std::make_shared<QPointer<BoxContent>>();
|
||||
const auto resetSecretAndSave = [=] {
|
||||
using Flag = MTPDaccount_passwordInputSettings::Flag;
|
||||
_setRequest = request(MTPaccount_UpdatePasswordSettings(
|
||||
MTP_bytes(oldPasswordHash),
|
||||
MTP_account_passwordInputSettings(
|
||||
MTP_flags(Flag::f_new_secure_settings),
|
||||
MTPPasswordKdfAlgo(), // new_algo
|
||||
MTPbytes(), // new_password_hash
|
||||
MTPstring(), // hint
|
||||
MTPstring(), // email
|
||||
MTP_secureSecretSettings(
|
||||
MTP_securePasswordKdfAlgoUnknown(), // secure_algo
|
||||
MTP_bytes(QByteArray()), // secure_secret
|
||||
MTP_long(0))) // secure_secret_id
|
||||
)).done([=](const MTPBool &result) {
|
||||
_setRequest = 0;
|
||||
const auto empty = QByteArray();
|
||||
if (*box) {
|
||||
(*box)->closeBox();
|
||||
}
|
||||
sendChangeCloudPassword(oldPasswordHash, newPassword, empty);
|
||||
}).fail([=](const RPCError &error) {
|
||||
_setRequest = 0;
|
||||
}).send();
|
||||
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
|
||||
resetSecret(check, newPassword, [=] {
|
||||
if (*box) {
|
||||
(*box)->closeBox();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
*box = getDelegate()->show(Box<ConfirmBox>(
|
||||
Lang::Hard::PassportCorrupted(),
|
||||
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;
|
||||
_setRequest = request(MTPaccount_UpdatePasswordSettings(
|
||||
check.result,
|
||||
MTP_account_passwordInputSettings(
|
||||
MTP_flags(Flag::f_new_secure_settings),
|
||||
MTPPasswordKdfAlgo(), // new_algo
|
||||
MTPbytes(), // new_password_hash
|
||||
MTPstring(), // hint
|
||||
MTPstring(), // email
|
||||
MTP_secureSecretSettings(
|
||||
MTP_securePasswordKdfAlgoUnknown(), // secure_algo
|
||||
MTP_bytes(QByteArray()), // secure_secret
|
||||
MTP_long(0))) // secure_secret_id
|
||||
)).done([=](const MTPBool &result) {
|
||||
_setRequest = 0;
|
||||
callback();
|
||||
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
|
||||
const auto empty = QByteArray();
|
||||
sendChangeCloudPassword(check, newPassword, empty);
|
||||
});
|
||||
}).fail([=](const RPCError &error) {
|
||||
_setRequest = 0;
|
||||
if (error.type() == qstr("SRP_ID_INVALID")) {
|
||||
handleSrpIdInvalid();
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
void PasscodeBox::sendChangeCloudPassword(
|
||||
const bytes::vector &oldPasswordHash,
|
||||
const Core::CloudPasswordResult &check,
|
||||
const QString &newPassword,
|
||||
const QByteArray &secureSecret) {
|
||||
const auto newPasswordBytes = newPassword.toUtf8();
|
||||
const auto newPasswordHash = Core::ComputeCloudPasswordHash(
|
||||
const auto newPasswordHash = Core::ComputeCloudPasswordDigest(
|
||||
_newAlgo,
|
||||
bytes::make_span(newPasswordBytes));
|
||||
if (newPasswordHash.modpow.empty()) {
|
||||
return serverError();
|
||||
}
|
||||
const auto hint = _passwordHint->getLastText();
|
||||
auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
|
||||
@ -555,11 +641,11 @@ void PasscodeBox::sendChangeCloudPassword(
|
||||
bytes::make_span(newPasswordBytes)));
|
||||
}
|
||||
_setRequest = request(MTPaccount_UpdatePasswordSettings(
|
||||
MTP_bytes(oldPasswordHash),
|
||||
check.result,
|
||||
MTP_account_passwordInputSettings(
|
||||
MTP_flags(flags),
|
||||
Core::PrepareCloudPasswordAlgo(_newAlgo),
|
||||
MTP_bytes(newPasswordHash),
|
||||
MTP_bytes(newPasswordHash.modpow),
|
||||
MTP_string(hint),
|
||||
MTPstring(), // email is not changing
|
||||
MTP_secureSecretSettings(
|
||||
@ -568,7 +654,7 @@ void PasscodeBox::sendChangeCloudPassword(
|
||||
MTP_long(newSecureSecretId)))
|
||||
)).done([=](const MTPBool &result) {
|
||||
setPasswordDone(newPasswordBytes);
|
||||
}).fail([=](const RPCError &error) {
|
||||
}).handleFloodErrors().fail([=](const RPCError &error) {
|
||||
setPasswordFail(newPasswordBytes, error);
|
||||
}).send();
|
||||
}
|
||||
@ -651,12 +737,9 @@ void PasscodeBox::recoverStarted(const MTPauth_PasswordRecovery &result) {
|
||||
recover();
|
||||
}
|
||||
|
||||
bool PasscodeBox::recoverStartFail(const RPCError &error) {
|
||||
if (MTP::isDefaultHandledError(error)) return false;
|
||||
|
||||
void PasscodeBox::recoverStartFail(const RPCError &error) {
|
||||
_pattern = QString();
|
||||
closeBox();
|
||||
return true;
|
||||
}
|
||||
|
||||
RecoverBox::RecoverBox(
|
||||
|
@ -22,7 +22,7 @@ public:
|
||||
PasscodeBox(QWidget*, bool turningOff);
|
||||
PasscodeBox(
|
||||
QWidget*,
|
||||
const Core::CloudPasswordAlgo &curAlgo,
|
||||
const Core::CloudPasswordCheckRequest &curRequest,
|
||||
const Core::CloudPasswordAlgo &newAlgo,
|
||||
bool hasRecovery,
|
||||
bool notEmptyPassport,
|
||||
@ -41,6 +41,9 @@ protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
private:
|
||||
using CheckPasswordCallback = Fn<void(
|
||||
const Core::CloudPasswordResult &check)>;
|
||||
|
||||
void submit();
|
||||
void closeReplacedBy();
|
||||
void oldChanged();
|
||||
@ -53,31 +56,51 @@ private:
|
||||
bool currentlyHave() const;
|
||||
|
||||
void setPasswordDone(const QByteArray &newPasswordBytes);
|
||||
bool setPasswordFail(const RPCError &error);
|
||||
bool setPasswordFail(
|
||||
void setPasswordFail(const RPCError &error);
|
||||
void setPasswordFail(
|
||||
const QByteArray &newPasswordBytes,
|
||||
const RPCError &error);
|
||||
|
||||
void recoverStarted(const MTPauth_PasswordRecovery &result);
|
||||
bool recoverStartFail(const RPCError &error);
|
||||
void recoverStartFail(const RPCError &error);
|
||||
|
||||
void recover();
|
||||
void clearCloudPassword(const QString &oldPassword);
|
||||
void setNewCloudPassword(const QString &newPassword);
|
||||
|
||||
void checkPassword(
|
||||
const QString &oldPassword,
|
||||
CheckPasswordCallback callback);
|
||||
void checkPasswordHash(CheckPasswordCallback callback);
|
||||
|
||||
void changeCloudPassword(
|
||||
const QString &oldPassword,
|
||||
const QString &newPassword);
|
||||
void changeCloudPassword(
|
||||
const QString &oldPassword,
|
||||
const Core::CloudPasswordResult &check,
|
||||
const QString &newPassword);
|
||||
|
||||
void sendChangeCloudPassword(
|
||||
const bytes::vector &oldPasswordHash,
|
||||
const Core::CloudPasswordResult &check,
|
||||
const QString &newPassword,
|
||||
const QByteArray &secureSecret);
|
||||
void suggestSecretReset(
|
||||
const bytes::vector &oldPasswordHash,
|
||||
const QString &newPassword);
|
||||
void suggestSecretReset(const QString &newPassword);
|
||||
void resetSecret(
|
||||
const Core::CloudPasswordResult &check,
|
||||
const QString &newPassword,
|
||||
Fn<void()> callback);
|
||||
void resetSecretAndChangePassword(
|
||||
const bytes::vector &oldPasswordHash,
|
||||
const QString &newPassword);
|
||||
|
||||
void sendClearCloudPassword(const QString &oldPassword);
|
||||
void sendClearCloudPassword(const Core::CloudPasswordResult &check);
|
||||
|
||||
void handleSrpIdInvalid();
|
||||
void requestPasswordData();
|
||||
void passwordChecked();
|
||||
void serverError();
|
||||
|
||||
QString _pattern;
|
||||
|
||||
@ -86,12 +109,15 @@ private:
|
||||
bool _cloudPwd = false;
|
||||
mtpRequestId _setRequest = 0;
|
||||
|
||||
Core::CloudPasswordAlgo _curAlgo;
|
||||
Core::CloudPasswordCheckRequest _curRequest;
|
||||
TimeMs _lastSrpIdInvalidTime = 0;
|
||||
Core::CloudPasswordAlgo _newAlgo;
|
||||
Core::SecureSecretAlgo _newSecureSecretAlgo;
|
||||
bool _hasRecovery = false;
|
||||
bool _notEmptyPassport = false;
|
||||
bool _skipEmailWarning = false;
|
||||
CheckPasswordCallback _checkPasswordCallback;
|
||||
bytes::vector _checkPasswordHash;
|
||||
|
||||
int _aboutHeight = 0;
|
||||
|
||||
|
@ -55,12 +55,13 @@ addChildParentFlags('MTPDchannelForbidden', 'MTPDchannel');
|
||||
parentFlagsCheck = {};
|
||||
|
||||
countedTypeIdExceptions = {};
|
||||
for i in range(77, 84):
|
||||
countedTypeIdExceptions[i] = {}
|
||||
countedTypeIdExceptions[i]['channel'] = True
|
||||
countedTypeIdExceptions['ipPortSecret'] = True
|
||||
countedTypeIdExceptions['accessPointRule'] = True
|
||||
countedTypeIdExceptions['help_configSimple'] = True
|
||||
countedTypeIdExceptions['channel#c88974ac'] = True
|
||||
countedTypeIdExceptions['ipPortSecret#37982646'] = True
|
||||
countedTypeIdExceptions['accessPointRule#4679b65f'] = True
|
||||
countedTypeIdExceptions['help.configSimple#5a592a6c'] = True
|
||||
|
||||
renamedTypes = {};
|
||||
renamedTypes['passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow'] = 'passwordKdfAlgoModPow';
|
||||
|
||||
lines = [];
|
||||
layer = '';
|
||||
@ -119,7 +120,10 @@ for line in lines:
|
||||
sys.exit(1);
|
||||
continue;
|
||||
|
||||
name = nametype.group(1);
|
||||
originalname = nametype.group(1);
|
||||
name = originalname;
|
||||
if (name in renamedTypes):
|
||||
name = renamedTypes[name];
|
||||
nameInd = name.find('.');
|
||||
if (nameInd >= 0):
|
||||
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));
|
||||
if (countTypeId < 0):
|
||||
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):
|
||||
typeid = '0x' + typeid;
|
||||
typeid = typeid;
|
||||
if (typeid != countTypeId):
|
||||
if (not layerIndex in countedTypeIdExceptions or not name in countedTypeIdExceptions[layerIndex]):
|
||||
if (not name in countedTypeIdExceptions):
|
||||
print('Warning: counted ' + countTypeId + ' mismatch with provided ' + typeid + ' (' + cleanline + ')');
|
||||
key = originalname + '#' + typeid;
|
||||
if (not key in countedTypeIdExceptions):
|
||||
print('Warning: counted ' + countTypeId + ' mismatch with provided ' + typeid + ' (' + key + ', ' + cleanline + ')');
|
||||
continue;
|
||||
else:
|
||||
typeid = countTypeId;
|
||||
|
||||
typeid = '0x' + typeid;
|
||||
|
||||
params = nametype.group(3);
|
||||
restype = nametype.group(4);
|
||||
if (restype.find('<') >= 0):
|
||||
@ -216,7 +222,7 @@ for line in lines:
|
||||
if (templ):
|
||||
hasTemplate = templ.group(1);
|
||||
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):
|
||||
print('Bad param found: "' + param + '" in line: ' + line);
|
||||
sys.exit(1);
|
||||
|
@ -8,59 +8,204 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/core_cloud_password.h"
|
||||
|
||||
#include "base/openssl_help.h"
|
||||
#include "mtproto/connection.h"
|
||||
|
||||
namespace Core {
|
||||
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(
|
||||
base::none_type,
|
||||
bytes::const_span password) {
|
||||
Unexpected("Bad cloud password 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);
|
||||
Unexpected("Bad secure secret algorithm.");
|
||||
}
|
||||
|
||||
bytes::vector ComputeHash(
|
||||
const SecureSecretAlgoSHA512 &algo,
|
||||
bytes::const_span password) {
|
||||
return openssl::Sha512(algo.salt, password, algo.salt);
|
||||
return Sha512(algo.salt, password, algo.salt);
|
||||
}
|
||||
|
||||
bytes::vector ComputeHash(
|
||||
const SecureSecretAlgoPBKDF2 &algo,
|
||||
bytes::const_span password) {
|
||||
return openssl::Pbkdf2Sha512(password, algo.salt, algo.kIterations);
|
||||
return Pbkdf2Sha512(password, algo.salt, algo.kIterations);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CloudPasswordAlgo ParseCloudPasswordAlgo(const MTPPasswordKdfAlgo &data) {
|
||||
return data.match([](
|
||||
const MTPDpasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000 &data) {
|
||||
return CloudPasswordAlgo(CloudPasswordAlgoPBKDF2{
|
||||
return data.match([](const MTPDpasswordKdfAlgoModPow &data) {
|
||||
return CloudPasswordAlgo(CloudPasswordAlgoModPow{
|
||||
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) {
|
||||
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) {
|
||||
if (!parsed.is<CloudPasswordAlgoPBKDF2>()) {
|
||||
if (!parsed.is<CloudPasswordAlgoModPow>()) {
|
||||
return base::none;
|
||||
}
|
||||
auto &value = parsed.get_unchecked<CloudPasswordAlgoPBKDF2>();
|
||||
auto &value = parsed.get_unchecked<CloudPasswordAlgoModPow>();
|
||||
const auto already = value.salt1.size();
|
||||
value.salt1.resize(already + kAdditionalSalt);
|
||||
bytes::set_random(bytes::make_span(value.salt1).subspan(already));
|
||||
@ -68,20 +213,48 @@ CloudPasswordAlgo ValidateNewCloudPasswordAlgo(CloudPasswordAlgo &&parsed) {
|
||||
}
|
||||
|
||||
MTPPasswordKdfAlgo PrepareCloudPasswordAlgo(const CloudPasswordAlgo &data) {
|
||||
return data.match([](const CloudPasswordAlgoPBKDF2 &data) {
|
||||
return MTP_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000(
|
||||
return data.match([](const CloudPasswordAlgoModPow &data) {
|
||||
return MTP_passwordKdfAlgoModPow(
|
||||
MTP_bytes(data.salt1),
|
||||
MTP_bytes(data.salt2));
|
||||
MTP_bytes(data.salt2),
|
||||
MTP_int(data.g),
|
||||
MTP_bytes(data.p));
|
||||
}, [](base::none_type) {
|
||||
return MTP_passwordKdfAlgoUnknown();
|
||||
});
|
||||
}
|
||||
|
||||
CloudPasswordResult::operator bool() const {
|
||||
return (result.type() != mtpc_inputCheckPasswordEmpty);
|
||||
}
|
||||
|
||||
bytes::vector ComputeCloudPasswordHash(
|
||||
const CloudPasswordAlgo &algo,
|
||||
bytes::const_span password) {
|
||||
return algo.match([&](const auto &data) {
|
||||
return algo.match([&](const CloudPasswordAlgoModPow &data) {
|
||||
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.");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -11,29 +11,79 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace Core {
|
||||
|
||||
struct CloudPasswordAlgoPBKDF2 {
|
||||
constexpr auto kHandleSrpIdInvalidTimeout = 60 * TimeMs(1000);
|
||||
|
||||
struct CloudPasswordAlgoModPow {
|
||||
static constexpr auto kIterations = 100000;
|
||||
|
||||
bytes::vector salt1;
|
||||
bytes::vector salt2;
|
||||
int g = 0;
|
||||
bytes::vector p;
|
||||
};
|
||||
|
||||
inline bool operator==(
|
||||
const CloudPasswordAlgoPBKDF2 &a,
|
||||
const CloudPasswordAlgoPBKDF2 &b) {
|
||||
return (a.salt1 == b.salt1) && (a.salt2 == b.salt2);
|
||||
const CloudPasswordAlgoModPow &a,
|
||||
const CloudPasswordAlgoModPow &b) {
|
||||
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 ValidateNewCloudPasswordAlgo(CloudPasswordAlgo &&parsed);
|
||||
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(
|
||||
const CloudPasswordAlgo &algo,
|
||||
bytes::const_span password);
|
||||
|
||||
CloudPasswordDigest ComputeCloudPasswordDigest(
|
||||
const CloudPasswordAlgo &algo,
|
||||
bytes::const_span password);
|
||||
|
||||
CloudPasswordResult ComputeCloudPasswordCheck(
|
||||
const CloudPasswordCheckRequest &request,
|
||||
bytes::const_span hash);
|
||||
|
||||
struct SecureSecretAlgoSHA512 {
|
||||
bytes::vector salt;
|
||||
};
|
||||
|
@ -293,14 +293,12 @@ void CodeWidget::gotPassword(const MTPaccount_Password &result) {
|
||||
stopCheck();
|
||||
_sentRequest = 0;
|
||||
const auto &d = result.c_account_password();
|
||||
getData()->pwdAlgo = d.has_current_algo()
|
||||
? Core::ParseCloudPasswordAlgo(d.vcurrent_algo)
|
||||
: Core::CloudPasswordAlgo();
|
||||
if (!d.has_current_algo()) {
|
||||
getData()->pwdRequest = Core::ParseCloudPasswordCheckRequest(d);
|
||||
if (!d.has_current_algo() || !d.has_srp_id() || !d.has_srp_B()) {
|
||||
LOG(("API Error: No current password received on login."));
|
||||
_code->setFocus();
|
||||
return;
|
||||
} else if (!getData()->pwdAlgo) {
|
||||
} else if (!getData()->pwdRequest) {
|
||||
const auto box = std::make_shared<QPointer<BoxContent>>();
|
||||
const auto callback = [=] {
|
||||
Core::UpdateApplication();
|
||||
@ -326,7 +324,7 @@ void CodeWidget::submit() {
|
||||
_checkRequest->start(1000);
|
||||
|
||||
_sentCode = _code->getLastText();
|
||||
getData()->pwdAlgo = Core::CloudPasswordAlgo();
|
||||
getData()->pwdRequest = Core::CloudPasswordCheckRequest();
|
||||
getData()->hasRecovery = false;
|
||||
getData()->pwdHint = QString();
|
||||
getData()->pwdNotEmptyPassport = false;
|
||||
|
@ -26,7 +26,7 @@ PwdCheckWidget::PwdCheckWidget(
|
||||
QWidget *parent,
|
||||
Widget::Data *data)
|
||||
: Step(parent, data)
|
||||
, _algo(getData()->pwdAlgo)
|
||||
, _request(getData()->pwdRequest)
|
||||
, _hasRecovery(getData()->hasRecovery)
|
||||
, _notEmptyPassport(getData()->pwdNotEmptyPassport)
|
||||
, _hint(getData()->pwdHint)
|
||||
@ -36,7 +36,7 @@ PwdCheckWidget::PwdCheckWidget(
|
||||
, _toRecover(this, lang(lng_signin_recover))
|
||||
, _toPassword(this, lang(lng_signin_try_password))
|
||||
, _checkRequest(this) {
|
||||
Expects(_algo.has_value());
|
||||
Expects(!!_request);
|
||||
|
||||
subscribe(Lang::Current().updated(), [this] { refreshLang(); });
|
||||
|
||||
@ -103,7 +103,7 @@ void PwdCheckWidget::activate() {
|
||||
}
|
||||
|
||||
void PwdCheckWidget::cancelled() {
|
||||
MTP::cancel(base::take(_sentRequest));
|
||||
request(base::take(_sentRequest)).cancel();
|
||||
}
|
||||
|
||||
void PwdCheckWidget::stopCheck() {
|
||||
@ -115,7 +115,7 @@ void PwdCheckWidget::onCheckRequest() {
|
||||
if (status < 0) {
|
||||
auto leftms = -status;
|
||||
if (leftms >= 1000) {
|
||||
MTP::cancel(base::take(_sentRequest));
|
||||
request(base::take(_sentRequest)).cancel();
|
||||
}
|
||||
}
|
||||
if (!_sentRequest && status == MTP::RequestSent) {
|
||||
@ -137,72 +137,125 @@ void PwdCheckWidget::pwdSubmitDone(bool recover, const MTPauth_Authorization &re
|
||||
finish(d.vuser);
|
||||
}
|
||||
|
||||
bool PwdCheckWidget::pwdSubmitFail(const RPCError &error) {
|
||||
void PwdCheckWidget::pwdSubmitFail(const RPCError &error) {
|
||||
if (MTP::isFloodError(error)) {
|
||||
_sentRequest = 0;
|
||||
stopCheck();
|
||||
showError(langFactory(lng_flood_error));
|
||||
_pwdField->showError();
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
if (MTP::isDefaultHandledError(error)) return false;
|
||||
|
||||
_sentRequest = 0;
|
||||
stopCheck();
|
||||
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));
|
||||
_pwdField->selectAll();
|
||||
_pwdField->showError();
|
||||
return true;
|
||||
} else if (err == qstr("PASSWORD_EMPTY")) {
|
||||
goBack();
|
||||
}
|
||||
if (Logs::DebugEnabled()) { // internal server error
|
||||
auto text = err + ": " + error.description();
|
||||
showError([text] { return text; });
|
||||
} else if (err == qstr("SRP_ID_INVALID")) {
|
||||
handleSrpIdInvalid();
|
||||
} else {
|
||||
showError(&Lang::Hard::ServerError);
|
||||
if (Logs::DebugEnabled()) { // internal server error
|
||||
auto text = err + ": " + error.description();
|
||||
showError([text] { return text; });
|
||||
} else {
|
||||
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)) {
|
||||
showError(langFactory(lng_flood_error));
|
||||
_codeField->showError();
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
if (MTP::isDefaultHandledError(error)) return false;
|
||||
|
||||
_sentRequest = 0;
|
||||
stopCheck();
|
||||
const QString &err = error.type();
|
||||
if (err == qstr("PASSWORD_EMPTY")) {
|
||||
goBack();
|
||||
return true;
|
||||
} else if (err == qstr("PASSWORD_RECOVERY_NA")) {
|
||||
recoverStartFail(error);
|
||||
return true;
|
||||
} else if (err == qstr("PASSWORD_RECOVERY_EXPIRED")) {
|
||||
_emailPattern = QString();
|
||||
onToPassword();
|
||||
return true;
|
||||
} else if (err == qstr("CODE_INVALID")) {
|
||||
showError(langFactory(lng_signin_wrong_code));
|
||||
_codeField->selectAll();
|
||||
_codeField->showError();
|
||||
return true;
|
||||
}
|
||||
if (Logs::DebugEnabled()) { // internal server error
|
||||
auto text = err + ": " + error.description();
|
||||
showError([text] { return text; });
|
||||
} else {
|
||||
showError(&Lang::Hard::ServerError);
|
||||
if (Logs::DebugEnabled()) { // internal server error
|
||||
auto text = err + ": " + error.description();
|
||||
showError([text] { return text; });
|
||||
} else {
|
||||
showError(&Lang::Hard::ServerError);
|
||||
}
|
||||
_codeField->setFocus();
|
||||
}
|
||||
_codeField->setFocus();
|
||||
return false;
|
||||
}
|
||||
|
||||
void PwdCheckWidget::recoverStarted(const MTPauth_PasswordRecovery &result) {
|
||||
@ -210,7 +263,7 @@ void PwdCheckWidget::recoverStarted(const MTPauth_PasswordRecovery &result) {
|
||||
updateDescriptionText();
|
||||
}
|
||||
|
||||
bool PwdCheckWidget::recoverStartFail(const RPCError &error) {
|
||||
void PwdCheckWidget::recoverStartFail(const RPCError &error) {
|
||||
stopCheck();
|
||||
_pwdField->show();
|
||||
_pwdHint->show();
|
||||
@ -219,13 +272,12 @@ bool PwdCheckWidget::recoverStartFail(const RPCError &error) {
|
||||
updateDescriptionText();
|
||||
update();
|
||||
hideError();
|
||||
return true;
|
||||
}
|
||||
|
||||
void PwdCheckWidget::onToRecover() {
|
||||
if (_hasRecovery) {
|
||||
if (_sentRequest) {
|
||||
MTP::cancel(base::take(_sentRequest));
|
||||
request(base::take(_sentRequest)).cancel();
|
||||
}
|
||||
hideError();
|
||||
_toRecover->hide();
|
||||
@ -237,7 +289,13 @@ void PwdCheckWidget::onToRecover() {
|
||||
_codeField->setFocus();
|
||||
updateDescriptionText();
|
||||
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 {
|
||||
Ui::show(Box<InformBox>(lang(lng_signin_no_email_forgot), [this] { showReset(); }));
|
||||
@ -250,7 +308,7 @@ void PwdCheckWidget::onToPassword() {
|
||||
|
||||
void PwdCheckWidget::showReset() {
|
||||
if (_sentRequest) {
|
||||
MTP::cancel(base::take(_sentRequest));
|
||||
request(base::take(_sentRequest)).cancel();
|
||||
}
|
||||
_toRecover->show();
|
||||
_toPassword->hide();
|
||||
@ -285,10 +343,13 @@ void PwdCheckWidget::submit() {
|
||||
return;
|
||||
}
|
||||
const auto send = crl::guard(this, [=] {
|
||||
_sentRequest = MTP::send(
|
||||
MTPauth_RecoverPassword(MTP_string(code)),
|
||||
rpcDone(&PwdCheckWidget::pwdSubmitDone, true),
|
||||
rpcFail(&PwdCheckWidget::codeSubmitFail));
|
||||
_sentRequest = request(
|
||||
MTPauth_RecoverPassword(MTP_string(code))
|
||||
).done([=](const MTPauth_Authorization &result) {
|
||||
pwdSubmitDone(true, result);
|
||||
}).handleFloodErrors().fail([=](const RPCError &error) {
|
||||
codeSubmitFail(error);
|
||||
}).send();
|
||||
});
|
||||
|
||||
if (_notEmptyPassport) {
|
||||
@ -310,13 +371,10 @@ void PwdCheckWidget::submit() {
|
||||
hideError();
|
||||
|
||||
const auto password = _pwdField->getLastText().toUtf8();
|
||||
const auto hash = Core::ComputeCloudPasswordHash(
|
||||
_algo,
|
||||
_passwordHash = Core::ComputeCloudPasswordHash(
|
||||
_request.algo,
|
||||
bytes::make_span(password));
|
||||
_sentRequest = MTP::send(
|
||||
MTPauth_CheckPassword(MTP_bytes(hash)),
|
||||
rpcDone(&PwdCheckWidget::pwdSubmitDone, false),
|
||||
rpcFail(&PwdCheckWidget::pwdSubmitFail));
|
||||
checkPasswordHash();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#pragma once
|
||||
|
||||
#include "intro/introwidget.h"
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
namespace Ui {
|
||||
class InputField;
|
||||
@ -18,7 +19,7 @@ class LinkButton;
|
||||
|
||||
namespace Intro {
|
||||
|
||||
class PwdCheckWidget : public Widget::Step {
|
||||
class PwdCheckWidget : public Widget::Step, private MTP::Sender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
@ -45,16 +46,23 @@ private:
|
||||
void updateControlsGeometry();
|
||||
|
||||
void pwdSubmitDone(bool recover, const MTPauth_Authorization &result);
|
||||
bool pwdSubmitFail(const RPCError &error);
|
||||
bool codeSubmitFail(const RPCError &error);
|
||||
bool recoverStartFail(const RPCError &error);
|
||||
void pwdSubmitFail(const RPCError &error);
|
||||
void codeSubmitFail(const RPCError &error);
|
||||
void recoverStartFail(const RPCError &error);
|
||||
|
||||
void recoverStarted(const MTPauth_PasswordRecovery &result);
|
||||
|
||||
void updateDescriptionText();
|
||||
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 _notEmptyPassport = false;
|
||||
QString _hint, _emailPattern;
|
||||
|
@ -73,7 +73,7 @@ public:
|
||||
int codeLength = 5;
|
||||
bool codeByTelegram = false;
|
||||
|
||||
Core::CloudPasswordAlgo pwdAlgo;
|
||||
Core::CloudPasswordCheckRequest pwdRequest;
|
||||
bool hasRecovery = false;
|
||||
QString pwdHint;
|
||||
bool pwdNotEmptyPassport = false;
|
||||
|
@ -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.");
|
||||
}
|
||||
|
||||
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() {
|
||||
return qsl("Reset");
|
||||
}
|
||||
|
@ -61,16 +61,20 @@ QString LogIdsVector(const QVector<MTPlong> &ids) {
|
||||
return idsStr + "]";
|
||||
}
|
||||
|
||||
bool IsGoodModExpFirst(const openssl::BigNum &modexp, const openssl::BigNum &prime) {
|
||||
auto diff = prime - modexp;
|
||||
bool IsGoodModExpFirst(
|
||||
const openssl::BigNum &modexp,
|
||||
const openssl::BigNum &prime) {
|
||||
const auto diff = openssl::BigNum::Sub(prime, modexp);
|
||||
if (modexp.failed() || prime.failed() || diff.failed()) {
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
Assert(modexp.bytesSize() <= kMaxModExpSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -194,20 +198,21 @@ ModExpFirst CreateModExp(
|
||||
|
||||
BigNum prime(primeBytes);
|
||||
auto result = ModExpFirst();
|
||||
constexpr auto kMaxModExpFirstTries = 5;
|
||||
result.randomPower.resize(ModExpFirst::kRandomPowerSize);
|
||||
for (auto tries = 0; tries != kMaxModExpFirstTries; ++tries) {
|
||||
while (true) {
|
||||
bytes::set_random(result.randomPower);
|
||||
for (auto i = 0; i != ModExpFirst::kRandomPowerSize; ++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)) {
|
||||
result.modexp = modexp.getBytes();
|
||||
break;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void wrapInvokeAfter(SecureRequest &to, const SecureRequest &from, const RequestMap &haveSent, int32 skipBeforeRequest = 0) {
|
||||
@ -3243,6 +3248,10 @@ bool IsPrimeAndGood(bytes::const_span primeBytes, int 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) {
|
||||
return internal::CreateModExp(g, primeBytes, randomSeed);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mtproto/auth_key.h"
|
||||
#include "mtproto/dc_options.h"
|
||||
#include "mtproto/connection_abstract.h"
|
||||
#include "base/openssl_help.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
namespace MTP {
|
||||
@ -23,6 +24,7 @@ struct ModExpFirst {
|
||||
bytes::vector modexp;
|
||||
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);
|
||||
bytes::vector CreateAuthKey(bytes::const_span firstBytes, bytes::const_span randomBytes, bytes::const_span primeBytes);
|
||||
|
||||
|
@ -321,7 +321,7 @@ QString FormController::privacyPolicyUrl() const {
|
||||
|
||||
bytes::vector FormController::passwordHashForAuth(
|
||||
bytes::const_span password) const {
|
||||
return Core::ComputeCloudPasswordHash(_password.algo, password);
|
||||
return Core::ComputeCloudPasswordHash(_password.request.algo, password);
|
||||
}
|
||||
|
||||
auto FormController::prepareFinalData() -> FinalData {
|
||||
@ -442,8 +442,52 @@ std::vector<not_null<const Value*>> FormController::submitGetErrors() {
|
||||
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) {
|
||||
Expects(_password.algo.has_value());
|
||||
Expects(!!_password.request);
|
||||
|
||||
const auto submitSaved = !base::take(_savedPasswordValue).isEmpty();
|
||||
if (_passwordCheckRequestId) {
|
||||
@ -452,16 +496,27 @@ void FormController::submitPassword(const QByteArray &password) {
|
||||
_passwordError.fire(QString());
|
||||
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(
|
||||
MTP_bytes(passwordHashForAuth(bytes::make_span(password)))
|
||||
check.result
|
||||
)).handleFloodErrors(
|
||||
).done([=](const MTPaccount_PasswordSettings &result) {
|
||||
Expects(result.type() == mtpc_account_passwordSettings);
|
||||
|
||||
_passwordCheckRequestId = 0;
|
||||
_savedPasswordValue = QByteArray();
|
||||
const auto hashForAuth = passwordHashForAuth(
|
||||
bytes::make_span(password));
|
||||
const auto &data = result.c_account_passwordSettings();
|
||||
_password.confirmedEmail = qs(data.vemail);
|
||||
if (data.has_secure_settings()) {
|
||||
@ -483,7 +538,7 @@ void FormController::submitPassword(const QByteArray &password) {
|
||||
settings.vsecure_secret_id.v);
|
||||
if (!_secret.empty()) {
|
||||
auto saved = SavedCredentials();
|
||||
saved.hashForAuth = hashForAuth;
|
||||
saved.hashForAuth = base::take(_passwordCheckHash);
|
||||
saved.hashForSecret = hashForSecret;
|
||||
saved.secretId = _secretId;
|
||||
Auth().data().rememberPassportCredentials(
|
||||
@ -499,13 +554,16 @@ void FormController::submitPassword(const QByteArray &password) {
|
||||
}
|
||||
}).fail([=](const RPCError &error) {
|
||||
_passwordCheckRequestId = 0;
|
||||
if (submitSaved) {
|
||||
if (error.type() == qstr("SRP_ID_INVALID")) {
|
||||
handleSrpIdInvalid(_passwordCheckRequestId);
|
||||
} else if (submitSaved) {
|
||||
// Force reload and show form.
|
||||
_password = PasswordSettings();
|
||||
reloadPassword();
|
||||
} else if (MTP::isFloodError(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));
|
||||
} else {
|
||||
_passwordError.fire_copy(error.type());
|
||||
@ -513,10 +571,40 @@ void FormController::submitPassword(const QByteArray &password) {
|
||||
}).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(
|
||||
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(
|
||||
MTP_bytes(credentials.hashForAuth)
|
||||
check.result
|
||||
)).done([=](const MTPaccount_PasswordSettings &result) {
|
||||
Expects(result.type() == mtpc_account_passwordSettings);
|
||||
|
||||
@ -546,8 +634,12 @@ void FormController::checkSavedPasswordSettings(
|
||||
}
|
||||
}).fail([=](const RPCError &error) {
|
||||
_passwordCheckRequestId = 0;
|
||||
Auth().data().forgetPassportCredentials();
|
||||
showForm();
|
||||
if (error.type() != qstr("SRP_ID_INVALID")
|
||||
|| !handleSrpIdInvalid(_passwordCheckRequestId)) {
|
||||
} else {
|
||||
Auth().data().forgetPassportCredentials();
|
||||
showForm();
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
@ -601,7 +693,7 @@ void FormController::cancelPassword() {
|
||||
return;
|
||||
}
|
||||
_passwordRequestId = request(MTPaccount_UpdatePasswordSettings(
|
||||
MTP_bytes(QByteArray()),
|
||||
MTP_inputCheckPasswordEmpty(),
|
||||
MTP_account_passwordInputSettings(
|
||||
MTP_flags(MTPDaccount_passwordInputSettings::Flag::f_email),
|
||||
MTP_passwordKdfAlgoUnknown(), // new_algo
|
||||
@ -662,30 +754,45 @@ void FormController::suggestReset(bytes::vector password) {
|
||||
// }
|
||||
}
|
||||
_view->suggestReset([=] {
|
||||
using Flag = MTPDaccount_passwordInputSettings::Flag;
|
||||
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
|
||||
MTP_bytes(passwordHashForAuth(password)),
|
||||
MTP_account_passwordInputSettings(
|
||||
MTP_flags(Flag::f_new_secure_settings),
|
||||
MTPPasswordKdfAlgo(), // new_algo
|
||||
MTPbytes(), // new_password_hash
|
||||
MTPstring(), // hint
|
||||
MTPstring(), // email
|
||||
MTP_secureSecretSettings(
|
||||
MTP_securePasswordKdfAlgoUnknown(), // secure_algo
|
||||
MTP_bytes(QByteArray()), // secure_secret
|
||||
MTP_long(0))) // secure_secret_id
|
||||
)).done([=](const MTPBool &result) {
|
||||
_saveSecretRequestId = 0;
|
||||
generateSecret(password);
|
||||
}).fail([=](const RPCError &error) {
|
||||
_saveSecretRequestId = 0;
|
||||
formFail(error.type());
|
||||
}).send();
|
||||
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;
|
||||
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
|
||||
check.result,
|
||||
MTP_account_passwordInputSettings(
|
||||
MTP_flags(Flag::f_new_secure_settings),
|
||||
MTPPasswordKdfAlgo(), // new_algo
|
||||
MTPbytes(), // new_password_hash
|
||||
MTPstring(), // hint
|
||||
MTPstring(), // email
|
||||
MTP_secureSecretSettings(
|
||||
MTP_securePasswordKdfAlgoUnknown(), // secure_algo
|
||||
MTP_bytes(QByteArray()), // secure_secret
|
||||
MTP_long(0))) // secure_secret_id
|
||||
)).done([=](const MTPBool &result) {
|
||||
_saveSecretRequestId = 0;
|
||||
generateSecret(password);
|
||||
}).fail([=](const RPCError &error) {
|
||||
_saveSecretRequestId = 0;
|
||||
if (error.type() != qstr("SRP_ID_INVALID")
|
||||
|| !handleSrpIdInvalid(_saveSecretRequestId)) {
|
||||
formFail(error.type());
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
void FormController::decryptValues() {
|
||||
Expects(!_secret.empty());
|
||||
|
||||
@ -1792,19 +1899,29 @@ void FormController::generateSecret(bytes::const_span password) {
|
||||
auto secret = GenerateSecretBytes();
|
||||
|
||||
auto saved = SavedCredentials();
|
||||
saved.hashForAuth = passwordHashForAuth(password);
|
||||
saved.hashForAuth = _passwordCheckHash;
|
||||
saved.hashForSecret = Core::ComputeSecureSecretHash(
|
||||
_password.newSecureAlgo,
|
||||
password);
|
||||
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,
|
||||
saved.hashForSecret);
|
||||
|
||||
using Flag = MTPDaccount_passwordInputSettings::Flag;
|
||||
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
|
||||
MTP_bytes(saved.hashForAuth),
|
||||
check.result,
|
||||
MTP_account_passwordInputSettings(
|
||||
MTP_flags(Flag::f_new_secure_settings),
|
||||
MTPPasswordKdfAlgo(), // new_algo
|
||||
@ -1829,7 +1946,10 @@ void FormController::generateSecret(bytes::const_span password) {
|
||||
}
|
||||
}).fail([=](const RPCError &error) {
|
||||
_saveSecretRequestId = 0;
|
||||
suggestRestart();
|
||||
if (error.type() != qstr("SRP_ID_INVALID")
|
||||
|| !handleSrpIdInvalid(_saveSecretRequestId)) {
|
||||
suggestRestart();
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
@ -2126,7 +2246,7 @@ void FormController::showForm() {
|
||||
|| !_password.newSecureAlgo) {
|
||||
_view->showUpdateAppBox();
|
||||
return;
|
||||
} else if (_password.algo) {
|
||||
} else if (_password.request) {
|
||||
if (!_savedPasswordValue.isEmpty()) {
|
||||
submitPassword(base::duplicate(_savedPasswordValue));
|
||||
} 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.hasRecovery = result.is_has_recovery();
|
||||
settings.notEmptyPassport = result.is_has_secure_values();
|
||||
settings.algo = result.has_current_algo()
|
||||
? Core::ParseCloudPasswordAlgo(result.vcurrent_algo)
|
||||
: Core::CloudPasswordAlgo();
|
||||
settings.request = Core::ParseCloudPasswordCheckRequest(result);
|
||||
settings.unknownAlgo = result.has_current_algo()
|
||||
&& !settings.algo;
|
||||
&& !settings.request;
|
||||
settings.unconfirmedPattern = result.has_email_unconfirmed_pattern()
|
||||
? qs(result.vemail_unconfirmed_pattern)
|
||||
: QString();
|
||||
|
@ -196,7 +196,7 @@ struct Form {
|
||||
};
|
||||
|
||||
struct PasswordSettings {
|
||||
Core::CloudPasswordAlgo algo;
|
||||
Core::CloudPasswordCheckRequest request;
|
||||
Core::CloudPasswordAlgo newAlgo;
|
||||
Core::SecureSecretAlgo newSecureAlgo;
|
||||
QString hint;
|
||||
@ -207,9 +207,14 @@ struct PasswordSettings {
|
||||
bool unknownAlgo = false;
|
||||
|
||||
bool operator==(const PasswordSettings &other) const {
|
||||
return (algo == other.algo)
|
||||
&& (newAlgo == other.newAlgo)
|
||||
&& (newSecureAlgo == other.newSecureAlgo)
|
||||
return (request == other.request)
|
||||
// newAlgo and newSecureAlgo are always different, because they have
|
||||
// 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)
|
||||
&& (unconfirmedPattern == other.unconfirmedPattern)
|
||||
&& (confirmedEmail == other.confirmedEmail)
|
||||
@ -310,6 +315,9 @@ public:
|
||||
~FormController();
|
||||
|
||||
private:
|
||||
using PasswordCheckCallback = Fn<void(
|
||||
const Core::CloudPasswordResult &check)>;
|
||||
|
||||
struct FinalData {
|
||||
QVector<MTPSecureValueHash> hashes;
|
||||
QByteArray credentials;
|
||||
@ -340,11 +348,26 @@ private:
|
||||
File &destination,
|
||||
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);
|
||||
bool applyPassword(const MTPDaccount_password &settings);
|
||||
bool applyPassword(PasswordSettings &&settings);
|
||||
bytes::vector passwordHashForAuth(bytes::const_span password) const;
|
||||
void checkSavedPasswordSettings(const SavedCredentials &credentials);
|
||||
void checkSavedPasswordSettings(
|
||||
const Core::CloudPasswordResult &check,
|
||||
const SavedCredentials &credentials);
|
||||
void validateSecureSecret(
|
||||
bytes::const_span encryptedSecret,
|
||||
bytes::const_span passwordHashForSecret,
|
||||
@ -361,6 +384,10 @@ private:
|
||||
void fileLoadProgress(FileKey key, int offset);
|
||||
void fileLoadFail(FileKey key);
|
||||
void generateSecret(bytes::const_span password);
|
||||
void saveSecret(
|
||||
const Core::CloudPasswordResult &check,
|
||||
const SavedCredentials &saved,
|
||||
const bytes::vector &secret);
|
||||
|
||||
void subscribeToUploader();
|
||||
void encryptFile(
|
||||
@ -410,6 +437,9 @@ private:
|
||||
FinalData prepareFinalData();
|
||||
|
||||
void suggestReset(bytes::vector password);
|
||||
void resetSecret(
|
||||
const Core::CloudPasswordResult &check,
|
||||
const bytes::vector &password);
|
||||
void suggestRestart();
|
||||
void cancelAbort();
|
||||
void shortPollEmailConfirmation();
|
||||
@ -423,6 +453,9 @@ private:
|
||||
mtpRequestId _passwordCheckRequestId = 0;
|
||||
|
||||
PasswordSettings _password;
|
||||
TimeMs _lastSrpIdInvalidTime = 0;
|
||||
bytes::vector _passwordCheckHash;
|
||||
PasswordCheckCallback _passwordCheckCallback;
|
||||
QByteArray _savedPasswordValue;
|
||||
Form _form;
|
||||
bool _cancelled = false;
|
||||
|
@ -472,7 +472,7 @@ void PanelController::setupPassword() {
|
||||
|| !settings.newSecureAlgo) {
|
||||
showUpdateAppBox();
|
||||
return;
|
||||
} else if (settings.algo) {
|
||||
} else if (settings.request) {
|
||||
showAskPassword();
|
||||
return;
|
||||
}
|
||||
@ -481,7 +481,7 @@ void PanelController::setupPassword() {
|
||||
const auto notEmptyPassport = false;
|
||||
const auto hint = QString();
|
||||
auto box = show(Box<PasscodeBox>(
|
||||
Core::CloudPasswordAlgo(), // current algo
|
||||
Core::CloudPasswordCheckRequest(), // current
|
||||
settings.newAlgo,
|
||||
hasRecovery,
|
||||
notEmptyPassport,
|
||||
|
@ -93,7 +93,7 @@ void CloudPasswordState::onEdit() {
|
||||
return;
|
||||
}
|
||||
auto box = Ui::show(Box<PasscodeBox>(
|
||||
_curPasswordAlgo,
|
||||
_curPasswordRequest,
|
||||
_newPasswordAlgo,
|
||||
_hasPasswordRecovery,
|
||||
_notEmptyPassport,
|
||||
@ -122,12 +122,12 @@ void CloudPasswordState::onTurnOff() {
|
||||
callback));
|
||||
return;
|
||||
}
|
||||
if (!_curPasswordAlgo) {
|
||||
if (!_curPasswordRequest) {
|
||||
_turnOff->hide();
|
||||
|
||||
MTP::send(
|
||||
MTPaccount_UpdatePasswordSettings(
|
||||
MTP_bytes(QByteArray()),
|
||||
MTP_inputCheckPasswordEmpty(),
|
||||
MTP_account_passwordInputSettings(
|
||||
MTP_flags(
|
||||
MTPDaccount_passwordInputSettings::Flag::f_email),
|
||||
@ -140,7 +140,7 @@ void CloudPasswordState::onTurnOff() {
|
||||
rpcFail(&CloudPasswordState::offPasswordFail));
|
||||
} else {
|
||||
auto box = Ui::show(Box<PasscodeBox>(
|
||||
_curPasswordAlgo,
|
||||
_curPasswordRequest,
|
||||
_newPasswordAlgo,
|
||||
_hasPasswordRecovery,
|
||||
_notEmptyPassport,
|
||||
@ -177,10 +177,8 @@ void CloudPasswordState::getPasswordDone(const MTPaccount_Password &result) {
|
||||
_waitingConfirm = QString();
|
||||
|
||||
const auto &d = result.c_account_password();
|
||||
_curPasswordAlgo = d.has_current_algo()
|
||||
? Core::ParseCloudPasswordAlgo(d.vcurrent_algo)
|
||||
: Core::CloudPasswordAlgo();
|
||||
_unknownPasswordAlgo = d.has_current_algo() && !_curPasswordAlgo;
|
||||
_curPasswordRequest = Core::ParseCloudPasswordCheckRequest(d);
|
||||
_unknownPasswordAlgo = d.has_current_algo() && !_curPasswordRequest;
|
||||
_hasPasswordRecovery = d.is_has_recovery();
|
||||
_notEmptyPassport = d.is_has_secure_values();
|
||||
_curPasswordHint = qs(d.vhint);
|
||||
@ -205,7 +203,7 @@ void CloudPasswordState::getPasswordDone(const MTPaccount_Password &result) {
|
||||
}
|
||||
|
||||
bool CloudPasswordState::hasCloudPassword() const {
|
||||
return (_curPasswordAlgo || _unknownPasswordAlgo);
|
||||
return (_curPasswordRequest || _unknownPasswordAlgo);
|
||||
}
|
||||
|
||||
bool CloudPasswordState::getPasswordFail(const RPCError &error) {
|
||||
|
@ -66,7 +66,7 @@ private:
|
||||
object_ptr<Ui::LinkButton> _turnOff;
|
||||
|
||||
QString _waitingConfirm;
|
||||
Core::CloudPasswordAlgo _curPasswordAlgo;
|
||||
Core::CloudPasswordCheckRequest _curPasswordRequest;
|
||||
bool _unknownPasswordAlgo = false;
|
||||
bool _hasPasswordRecovery = false;
|
||||
bool _notEmptyPassport = false;
|
||||
|
Loading…
Reference in New Issue
Block a user