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.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

View File

@ -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);

View File

@ -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>

View File

@ -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(

View File

@ -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;

View File

@ -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);

View File

@ -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.");
});
}

View File

@ -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;
};

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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;

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.");
}
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");
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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();

View File

@ -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;

View File

@ -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,

View File

@ -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) {

View File

@ -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;