mirror of https://github.com/procxx/kepka.git
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.authorizations#1250abde authorizations:Vector<Authorization> = account.Authorizations;
|
||||||
|
|
||||||
account.password#68873ba5 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes = account.Password;
|
account.password#ad2641f8 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes = account.Password;
|
||||||
|
|
||||||
account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings;
|
account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings;
|
||||||
|
|
||||||
|
@ -1022,7 +1022,7 @@ savedPhoneContact#1142bd56 phone:string first_name:string last_name:string date:
|
||||||
account.takeout#4dba4501 id:long = account.Takeout;
|
account.takeout#4dba4501 id:long = account.Takeout;
|
||||||
|
|
||||||
passwordKdfAlgoUnknown#d45ab096 = PasswordKdfAlgo;
|
passwordKdfAlgoUnknown#d45ab096 = PasswordKdfAlgo;
|
||||||
passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000#b6425eaa salt1:bytes salt2:bytes = PasswordKdfAlgo;
|
passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow#3a912d4a salt1:bytes salt2:bytes g:int p:bytes = PasswordKdfAlgo;
|
||||||
|
|
||||||
securePasswordKdfAlgoUnknown#4a8537 = SecurePasswordKdfAlgo;
|
securePasswordKdfAlgoUnknown#4a8537 = SecurePasswordKdfAlgo;
|
||||||
securePasswordKdfAlgoPBKDF2HMACSHA512iter100000#bbf2dda0 salt:bytes = SecurePasswordKdfAlgo;
|
securePasswordKdfAlgoPBKDF2HMACSHA512iter100000#bbf2dda0 salt:bytes = SecurePasswordKdfAlgo;
|
||||||
|
@ -1030,6 +1030,9 @@ securePasswordKdfAlgoSHA512#86471d92 salt:bytes = SecurePasswordKdfAlgo;
|
||||||
|
|
||||||
secureSecretSettings#1527bcac secure_algo:SecurePasswordKdfAlgo secure_secret:bytes secure_secret_id:long = SecureSecretSettings;
|
secureSecretSettings#1527bcac secure_algo:SecurePasswordKdfAlgo secure_secret:bytes secure_secret_id:long = SecureSecretSettings;
|
||||||
|
|
||||||
|
inputCheckPasswordEmpty#9880f658 = InputCheckPasswordSRP;
|
||||||
|
inputCheckPasswordSRP#d27ff082 srp_id:long A:bytes M1:bytes = InputCheckPasswordSRP;
|
||||||
|
|
||||||
---functions---
|
---functions---
|
||||||
|
|
||||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||||
|
@ -1049,7 +1052,7 @@ auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization;
|
||||||
auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization;
|
auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization;
|
||||||
auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool;
|
auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool;
|
||||||
auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization;
|
auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization;
|
||||||
auth.checkPassword#a63011e password_hash:bytes = auth.Authorization;
|
auth.checkPassword#d18b4d16 password:InputCheckPasswordSRP = auth.Authorization;
|
||||||
auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery;
|
auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery;
|
||||||
auth.recoverPassword#4ea56e92 code:string = auth.Authorization;
|
auth.recoverPassword#4ea56e92 code:string = auth.Authorization;
|
||||||
auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode;
|
auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode;
|
||||||
|
@ -1078,11 +1081,11 @@ account.updateDeviceLocked#38df3532 period:int = Bool;
|
||||||
account.getAuthorizations#e320c158 = account.Authorizations;
|
account.getAuthorizations#e320c158 = account.Authorizations;
|
||||||
account.resetAuthorization#df77f3bc hash:long = Bool;
|
account.resetAuthorization#df77f3bc hash:long = Bool;
|
||||||
account.getPassword#548a30f5 = account.Password;
|
account.getPassword#548a30f5 = account.Password;
|
||||||
account.getPasswordSettings#bc8d11bb current_password_hash:bytes = account.PasswordSettings;
|
account.getPasswordSettings#9cd4eaf9 password:InputCheckPasswordSRP = account.PasswordSettings;
|
||||||
account.updatePasswordSettings#fa7c4b86 current_password_hash:bytes new_settings:account.PasswordInputSettings = Bool;
|
account.updatePasswordSettings#a59b102f password:InputCheckPasswordSRP new_settings:account.PasswordInputSettings = Bool;
|
||||||
account.sendConfirmPhoneCode#1516d7bd flags:# allow_flashcall:flags.0?true hash:string current_number:flags.0?Bool = auth.SentCode;
|
account.sendConfirmPhoneCode#1516d7bd flags:# allow_flashcall:flags.0?true hash:string current_number:flags.0?Bool = auth.SentCode;
|
||||||
account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool;
|
account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool;
|
||||||
account.getTmpPassword#4a82327e password_hash:bytes period:int = account.TmpPassword;
|
account.getTmpPassword#449e0b51 password:InputCheckPasswordSRP period:int = account.TmpPassword;
|
||||||
account.getWebAuthorizations#182e6d6f = account.WebAuthorizations;
|
account.getWebAuthorizations#182e6d6f = account.WebAuthorizations;
|
||||||
account.resetWebAuthorization#2d01b9ef hash:long = Bool;
|
account.resetWebAuthorization#2d01b9ef hash:long = Bool;
|
||||||
account.resetWebAuthorizations#682d2594 = Bool;
|
account.resetWebAuthorizations#682d2594 = Bool;
|
||||||
|
@ -1318,4 +1321,4 @@ langpack.getStrings#2e1ee318 lang_code:string keys:Vector<string> = Vector<LangP
|
||||||
langpack.getDifference#b2e4d7d from_version:int = LangPackDifference;
|
langpack.getDifference#b2e4d7d from_version:int = LangPackDifference;
|
||||||
langpack.getLanguages#800fd57d = Vector<LangPackLanguage>;
|
langpack.getLanguages#800fd57d = Vector<LangPackLanguage>;
|
||||||
|
|
||||||
// LAYER 83
|
// LAYER 84
|
||||||
|
|
|
@ -12,9 +12,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace bytes {
|
namespace bytes {
|
||||||
|
|
||||||
using span = gsl::span<gsl::byte>;
|
using type = gsl::byte;
|
||||||
using const_span = gsl::span<const gsl::byte>;
|
using span = gsl::span<type>;
|
||||||
using vector = std::vector<gsl::byte>;
|
using const_span = gsl::span<const type>;
|
||||||
|
using vector = std::vector<type>;
|
||||||
|
|
||||||
|
template <gsl::index Size>
|
||||||
|
using array = std::array<type, Size>;
|
||||||
|
|
||||||
template <
|
template <
|
||||||
typename Container,
|
typename Container,
|
||||||
|
@ -48,6 +52,16 @@ inline const_span make_span(const Type *value, std::size_t count) {
|
||||||
return gsl::as_bytes(gsl::make_span(value, count));
|
return gsl::as_bytes(gsl::make_span(value, count));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Type>
|
||||||
|
inline span object_as_span(Type *value) {
|
||||||
|
return bytes::make_span(value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Type>
|
||||||
|
inline const_span object_as_span(const Type *value) {
|
||||||
|
return bytes::make_span(value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Container>
|
template <typename Container>
|
||||||
inline vector make_vector(const Container &container) {
|
inline vector make_vector(const Container &container) {
|
||||||
const auto buffer = bytes::make_span(container);
|
const auto buffer = bytes::make_span(container);
|
||||||
|
|
|
@ -87,18 +87,11 @@ public:
|
||||||
_failed = true;
|
_failed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void setModExp(
|
|
||||||
const BigNum &a,
|
void setAdd(const BigNum &a, const BigNum &b) {
|
||||||
const BigNum &p,
|
if (a.failed() || b.failed()) {
|
||||||
const BigNum &m,
|
|
||||||
const Context &context = Context()) {
|
|
||||||
if (a.failed() || p.failed() || m.failed()) {
|
|
||||||
_failed = true;
|
_failed = true;
|
||||||
} else if (a.isNegative() || p.isNegative() || m.isNegative()) {
|
} else if (!BN_add(raw(), a.raw(), b.raw())) {
|
||||||
_failed = true;
|
|
||||||
} else if (!BN_mod_exp(raw(), a.raw(), p.raw(), m.raw(), context.raw())) {
|
|
||||||
_failed = true;
|
|
||||||
} else if (isNegative()) {
|
|
||||||
_failed = true;
|
_failed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,6 +109,16 @@ public:
|
||||||
_failed = true;
|
_failed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void setMul(
|
||||||
|
const BigNum &a,
|
||||||
|
const BigNum &b,
|
||||||
|
const Context &context = Context()) {
|
||||||
|
if (a.failed() || b.failed()) {
|
||||||
|
_failed = true;
|
||||||
|
} else if (!BN_mul(raw(), a.raw(), b.raw(), context.raw())) {
|
||||||
|
_failed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
BN_ULONG setDivWord(BN_ULONG word) {
|
BN_ULONG setDivWord(BN_ULONG word) {
|
||||||
Expects(word != 0);
|
Expects(word != 0);
|
||||||
if (failed()) {
|
if (failed()) {
|
||||||
|
@ -128,6 +131,51 @@ public:
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
void setModSub(
|
||||||
|
const BigNum &a,
|
||||||
|
const BigNum &b,
|
||||||
|
const BigNum &m,
|
||||||
|
const Context &context = Context()) {
|
||||||
|
if (a.failed() || b.failed() || m.failed()) {
|
||||||
|
_failed = true;
|
||||||
|
} else if (a.isNegative() || b.isNegative() || m.isNegative()) {
|
||||||
|
_failed = true;
|
||||||
|
} else if (!BN_mod_sub(raw(), a.raw(), b.raw(), m.raw(), context.raw())) {
|
||||||
|
_failed = true;
|
||||||
|
} else if (isNegative()) {
|
||||||
|
_failed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void setModMul(
|
||||||
|
const BigNum &a,
|
||||||
|
const BigNum &b,
|
||||||
|
const BigNum &m,
|
||||||
|
const Context &context = Context()) {
|
||||||
|
if (a.failed() || b.failed() || m.failed()) {
|
||||||
|
_failed = true;
|
||||||
|
} else if (a.isNegative() || b.isNegative() || m.isNegative()) {
|
||||||
|
_failed = true;
|
||||||
|
} else if (!BN_mod_mul(raw(), a.raw(), b.raw(), m.raw(), context.raw())) {
|
||||||
|
_failed = true;
|
||||||
|
} else if (isNegative()) {
|
||||||
|
_failed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void setModExp(
|
||||||
|
const BigNum &base,
|
||||||
|
const BigNum &power,
|
||||||
|
const BigNum &m,
|
||||||
|
const Context &context = Context()) {
|
||||||
|
if (base.failed() || power.failed() || m.failed()) {
|
||||||
|
_failed = true;
|
||||||
|
} else if (base.isNegative() || power.isNegative() || m.isNegative()) {
|
||||||
|
_failed = true;
|
||||||
|
} else if (!BN_mod_exp(raw(), base.raw(), power.raw(), m.raw(), context.raw())) {
|
||||||
|
_failed = true;
|
||||||
|
} else if (isNegative()) {
|
||||||
|
_failed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool isNegative() const {
|
bool isNegative() const {
|
||||||
return failed() ? false : BN_is_negative(raw());
|
return failed() ? false : BN_is_negative(raw());
|
||||||
|
@ -198,9 +246,54 @@ public:
|
||||||
return _failed;
|
return _failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BigNum ModExp(const BigNum &base, const BigNum &power, const openssl::BigNum &mod) {
|
static BigNum Add(const BigNum &a, const BigNum &b) {
|
||||||
BigNum result;
|
BigNum result;
|
||||||
result.setModExp(base, power, mod);
|
result.setAdd(a, b);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
static BigNum Sub(const BigNum &a, const BigNum &b) {
|
||||||
|
BigNum result;
|
||||||
|
result.setSub(a, b);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
static BigNum Mul(
|
||||||
|
const BigNum &a,
|
||||||
|
const BigNum &b,
|
||||||
|
const Context &context = Context()) {
|
||||||
|
BigNum result;
|
||||||
|
result.setMul(a, b, context);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
static BigNum ModSub(
|
||||||
|
const BigNum &a,
|
||||||
|
const BigNum &b,
|
||||||
|
const BigNum &mod,
|
||||||
|
const Context &context = Context()) {
|
||||||
|
BigNum result;
|
||||||
|
result.setModSub(a, b, mod, context);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
static BigNum ModMul(
|
||||||
|
const BigNum &a,
|
||||||
|
const BigNum &b,
|
||||||
|
const BigNum &mod,
|
||||||
|
const Context &context = Context()) {
|
||||||
|
BigNum result;
|
||||||
|
result.setModMul(a, b, mod, context);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
static BigNum ModExp(
|
||||||
|
const BigNum &base,
|
||||||
|
const BigNum &power,
|
||||||
|
const BigNum &mod,
|
||||||
|
const Context &context = Context()) {
|
||||||
|
BigNum result;
|
||||||
|
result.setModExp(base, power, mod, context);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
static BigNum Failed() {
|
||||||
|
BigNum result;
|
||||||
|
result._failed = true;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,12 +303,6 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline BigNum operator-(const BigNum &a, const BigNum &b) {
|
|
||||||
BigNum result;
|
|
||||||
result.setSub(a, b);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace details {
|
namespace details {
|
||||||
|
|
||||||
template <typename Context, typename Method, typename Arg>
|
template <typename Context, typename Method, typename Arg>
|
||||||
|
|
|
@ -30,7 +30,7 @@ PasscodeBox::PasscodeBox(QWidget*, bool turningOff)
|
||||||
|
|
||||||
PasscodeBox::PasscodeBox(
|
PasscodeBox::PasscodeBox(
|
||||||
QWidget*,
|
QWidget*,
|
||||||
const Core::CloudPasswordAlgo &curAlgo,
|
const Core::CloudPasswordCheckRequest &curRequest,
|
||||||
const Core::CloudPasswordAlgo &newAlgo,
|
const Core::CloudPasswordAlgo &newAlgo,
|
||||||
bool hasRecovery,
|
bool hasRecovery,
|
||||||
bool notEmptyPassport,
|
bool notEmptyPassport,
|
||||||
|
@ -39,19 +39,19 @@ PasscodeBox::PasscodeBox(
|
||||||
bool turningOff)
|
bool turningOff)
|
||||||
: _turningOff(turningOff)
|
: _turningOff(turningOff)
|
||||||
, _cloudPwd(true)
|
, _cloudPwd(true)
|
||||||
, _curAlgo(curAlgo)
|
, _curRequest(curRequest)
|
||||||
, _newAlgo(newAlgo)
|
, _newAlgo(newAlgo)
|
||||||
, _newSecureSecretAlgo(newSecureSecretAlgo)
|
, _newSecureSecretAlgo(newSecureSecretAlgo)
|
||||||
, _hasRecovery(hasRecovery)
|
, _hasRecovery(hasRecovery)
|
||||||
, _notEmptyPassport(notEmptyPassport)
|
, _notEmptyPassport(notEmptyPassport)
|
||||||
, _about(st::boxWidth - st::boxPadding.left() * 1.5)
|
, _about(st::boxWidth - st::boxPadding.left() * 1.5)
|
||||||
, _oldPasscode(this, st::defaultInputField, langFactory(lng_cloud_password_enter_old))
|
, _oldPasscode(this, st::defaultInputField, langFactory(lng_cloud_password_enter_old))
|
||||||
, _newPasscode(this, st::defaultInputField, langFactory(curAlgo ? lng_cloud_password_enter_new : lng_cloud_password_enter_first))
|
, _newPasscode(this, st::defaultInputField, langFactory(curRequest ? lng_cloud_password_enter_new : lng_cloud_password_enter_first))
|
||||||
, _reenterPasscode(this, st::defaultInputField, langFactory(lng_cloud_password_confirm_new))
|
, _reenterPasscode(this, st::defaultInputField, langFactory(lng_cloud_password_confirm_new))
|
||||||
, _passwordHint(this, st::defaultInputField, langFactory(curAlgo ? lng_cloud_password_change_hint : lng_cloud_password_hint))
|
, _passwordHint(this, st::defaultInputField, langFactory(curRequest ? lng_cloud_password_change_hint : lng_cloud_password_hint))
|
||||||
, _recoverEmail(this, st::defaultInputField, langFactory(lng_cloud_password_email))
|
, _recoverEmail(this, st::defaultInputField, langFactory(lng_cloud_password_email))
|
||||||
, _recover(this, lang(lng_signin_recover)) {
|
, _recover(this, lang(lng_signin_recover)) {
|
||||||
Expects(!_turningOff || curAlgo.has_value());
|
Expects(!_turningOff || curRequest);
|
||||||
|
|
||||||
if (!hint.isEmpty()) _hintText.setText(st::passcodeTextStyle, lng_signin_hint(lt_password_hint, hint));
|
if (!hint.isEmpty()) _hintText.setText(st::passcodeTextStyle, lng_signin_hint(lt_password_hint, hint));
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ rpl::producer<> PasscodeBox::passwordReloadNeeded() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PasscodeBox::currentlyHave() const {
|
bool PasscodeBox::currentlyHave() const {
|
||||||
return _cloudPwd ? _curAlgo.has_value() : Global::LocalPasscode();
|
return _cloudPwd ? (!!_curRequest) : Global::LocalPasscode();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PasscodeBox::prepare() {
|
void PasscodeBox::prepare() {
|
||||||
|
@ -225,10 +225,8 @@ void PasscodeBox::closeReplacedBy() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PasscodeBox::setPasswordFail(const RPCError &error) {
|
void PasscodeBox::setPasswordFail(const RPCError &error) {
|
||||||
if (MTP::isFloodError(error)) {
|
if (MTP::isFloodError(error)) {
|
||||||
if (_oldPasscode->isHidden()) return false;
|
|
||||||
|
|
||||||
closeReplacedBy();
|
closeReplacedBy();
|
||||||
_setRequest = 0;
|
_setRequest = 0;
|
||||||
|
|
||||||
|
@ -240,45 +238,36 @@ bool PasscodeBox::setPasswordFail(const RPCError &error) {
|
||||||
_recover->hide();
|
_recover->hide();
|
||||||
}
|
}
|
||||||
update();
|
update();
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
if (MTP::isDefaultHandledError(error)) return false;
|
|
||||||
|
|
||||||
closeReplacedBy();
|
closeReplacedBy();
|
||||||
_setRequest = 0;
|
_setRequest = 0;
|
||||||
QString err = error.type();
|
const auto err = error.type();
|
||||||
if (err == qstr("PASSWORD_HASH_INVALID")) {
|
if (err == qstr("PASSWORD_HASH_INVALID")
|
||||||
|
|| err == qstr("SRP_PASSWORD_CHANGED")) {
|
||||||
if (_oldPasscode->isHidden()) {
|
if (_oldPasscode->isHidden()) {
|
||||||
_passwordReloadNeeded.fire({});
|
_passwordReloadNeeded.fire({});
|
||||||
closeBox();
|
closeBox();
|
||||||
} else {
|
} else {
|
||||||
badOldPasscode();
|
badOldPasscode();
|
||||||
}
|
}
|
||||||
} else if (err == qstr("NEW_PASSWORD_BAD")) {
|
} else if (error.type() == qstr("SRP_ID_INVALID")) {
|
||||||
_newPasscode->setFocus();
|
handleSrpIdInvalid();
|
||||||
_newPasscode->showError();
|
//} else if (err == qstr("NEW_PASSWORD_BAD")) {
|
||||||
_newError = lang(lng_cloud_password_bad);
|
//} else if (err == qstr("NEW_SALT_INVALID")) {
|
||||||
update();
|
|
||||||
} else if (err == qstr("NEW_SALT_INVALID")) {
|
|
||||||
_passwordReloadNeeded.fire({});
|
|
||||||
closeBox();
|
|
||||||
} else if (err == qstr("EMAIL_INVALID")) {
|
} else if (err == qstr("EMAIL_INVALID")) {
|
||||||
_emailError = lang(lng_cloud_password_bad_email);
|
_emailError = lang(lng_cloud_password_bad_email);
|
||||||
_recoverEmail->setFocus();
|
_recoverEmail->setFocus();
|
||||||
_recoverEmail->showError();
|
_recoverEmail->showError();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PasscodeBox::setPasswordFail(
|
void PasscodeBox::setPasswordFail(
|
||||||
const QByteArray &newPasswordBytes,
|
const QByteArray &newPasswordBytes,
|
||||||
const RPCError &error) {
|
const RPCError &error) {
|
||||||
if (MTP::isFloodError(error)) {
|
if (error.type() == qstr("EMAIL_UNCONFIRMED")) {
|
||||||
return setPasswordFail(error);
|
|
||||||
} else if (MTP::isDefaultHandledError(error)) {
|
|
||||||
return setPasswordFail(error);
|
|
||||||
} else if (error.type() == qstr("EMAIL_UNCONFIRMED")) {
|
|
||||||
closeReplacedBy();
|
closeReplacedBy();
|
||||||
_setRequest = 0;
|
_setRequest = 0;
|
||||||
|
|
||||||
|
@ -286,9 +275,21 @@ bool PasscodeBox::setPasswordFail(
|
||||||
getDelegate()->show(
|
getDelegate()->show(
|
||||||
Box<InformBox>(lang(lng_cloud_password_almost)),
|
Box<InformBox>(lang(lng_cloud_password_almost)),
|
||||||
LayerOption::CloseOther);
|
LayerOption::CloseOther);
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
return setPasswordFail(error);
|
setPasswordFail(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PasscodeBox::handleSrpIdInvalid() {
|
||||||
|
const auto now = getms(true);
|
||||||
|
if (_lastSrpIdInvalidTime > 0
|
||||||
|
&& now - _lastSrpIdInvalidTime < Core::kHandleSrpIdInvalidTimeout) {
|
||||||
|
_curRequest.id = 0;
|
||||||
|
_oldError = Lang::Hard::ServerError();
|
||||||
|
update();
|
||||||
|
} else {
|
||||||
|
_lastSrpIdInvalidTime = now;
|
||||||
|
requestPasswordData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,10 +392,68 @@ void PasscodeBox::clearCloudPassword(const QString &oldPassword) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PasscodeBox::sendClearCloudPassword(const QString &oldPassword) {
|
void PasscodeBox::sendClearCloudPassword(const QString &oldPassword) {
|
||||||
|
checkPassword(oldPassword, [=](const Core::CloudPasswordResult &check) {
|
||||||
|
sendClearCloudPassword(check);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PasscodeBox::checkPassword(
|
||||||
|
const QString &oldPassword,
|
||||||
|
CheckPasswordCallback callback) {
|
||||||
const auto passwordUtf = oldPassword.toUtf8();
|
const auto passwordUtf = oldPassword.toUtf8();
|
||||||
const auto oldPasswordHash = Core::ComputeCloudPasswordHash(
|
_checkPasswordHash = Core::ComputeCloudPasswordHash(
|
||||||
_curAlgo,
|
_curRequest.algo,
|
||||||
bytes::make_span(passwordUtf));
|
bytes::make_span(passwordUtf));
|
||||||
|
checkPasswordHash(std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PasscodeBox::checkPasswordHash(CheckPasswordCallback callback) {
|
||||||
|
_checkPasswordCallback = std::move(callback);
|
||||||
|
if (_curRequest.id) {
|
||||||
|
passwordChecked();
|
||||||
|
} else {
|
||||||
|
requestPasswordData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PasscodeBox::passwordChecked() {
|
||||||
|
if (!_curRequest || !_curRequest.id || !_checkPasswordCallback) {
|
||||||
|
return serverError();
|
||||||
|
}
|
||||||
|
const auto check = Core::ComputeCloudPasswordCheck(
|
||||||
|
_curRequest,
|
||||||
|
_checkPasswordHash);
|
||||||
|
if (!check) {
|
||||||
|
return serverError();
|
||||||
|
}
|
||||||
|
_curRequest.id = 0;
|
||||||
|
_checkPasswordCallback(check);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PasscodeBox::requestPasswordData() {
|
||||||
|
if (!_checkPasswordCallback) {
|
||||||
|
return serverError();
|
||||||
|
}
|
||||||
|
|
||||||
|
request(base::take(_setRequest)).cancel();
|
||||||
|
_setRequest = request(
|
||||||
|
MTPaccount_GetPassword()
|
||||||
|
).done([=](const MTPaccount_Password &result) {
|
||||||
|
_setRequest = 0;
|
||||||
|
result.match([&](const MTPDaccount_password &data) {
|
||||||
|
_curRequest = Core::ParseCloudPasswordCheckRequest(data);
|
||||||
|
passwordChecked();
|
||||||
|
});
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PasscodeBox::serverError() {
|
||||||
|
getDelegate()->show(Box<InformBox>(Lang::Hard::ServerError()));
|
||||||
|
closeBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PasscodeBox::sendClearCloudPassword(
|
||||||
|
const Core::CloudPasswordResult &check) {
|
||||||
const auto newPasswordData = QByteArray();
|
const auto newPasswordData = QByteArray();
|
||||||
const auto newPasswordHash = QByteArray();
|
const auto newPasswordHash = QByteArray();
|
||||||
const auto hint = QString();
|
const auto hint = QString();
|
||||||
|
@ -404,40 +463,42 @@ void PasscodeBox::sendClearCloudPassword(const QString &oldPassword) {
|
||||||
| MTPDaccount_passwordInputSettings::Flag::f_hint
|
| MTPDaccount_passwordInputSettings::Flag::f_hint
|
||||||
| MTPDaccount_passwordInputSettings::Flag::f_email;
|
| MTPDaccount_passwordInputSettings::Flag::f_email;
|
||||||
_setRequest = request(MTPaccount_UpdatePasswordSettings(
|
_setRequest = request(MTPaccount_UpdatePasswordSettings(
|
||||||
MTP_bytes(oldPasswordHash),
|
check.result,
|
||||||
MTP_account_passwordInputSettings(
|
MTP_account_passwordInputSettings(
|
||||||
MTP_flags(flags),
|
MTP_flags(flags),
|
||||||
Core::PrepareCloudPasswordAlgo(_newAlgo),
|
Core::PrepareCloudPasswordAlgo(_newAlgo),
|
||||||
MTP_bytes(newPasswordHash),
|
MTP_bytes(QByteArray()), // new_password_hash
|
||||||
MTP_string(hint),
|
MTP_string(hint),
|
||||||
MTP_string(email),
|
MTP_string(email),
|
||||||
MTPSecureSecretSettings())
|
MTPSecureSecretSettings())
|
||||||
)).done([=](const MTPBool &result) {
|
)).done([=](const MTPBool &result) {
|
||||||
setPasswordDone({});
|
setPasswordDone({});
|
||||||
}).fail([=](const RPCError &error) {
|
}).handleFloodErrors().fail([=](const RPCError &error) mutable {
|
||||||
setPasswordFail({}, error);
|
setPasswordFail({}, error);
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
|
void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
|
||||||
const auto newPasswordBytes = newPassword.toUtf8();
|
const auto newPasswordBytes = newPassword.toUtf8();
|
||||||
const auto newPasswordHash = Core::ComputeCloudPasswordHash(
|
const auto newPasswordHash = Core::ComputeCloudPasswordDigest(
|
||||||
_newAlgo,
|
_newAlgo,
|
||||||
bytes::make_span(newPasswordBytes));
|
bytes::make_span(newPasswordBytes));
|
||||||
const auto oldPasswordData = QByteArray();
|
if (newPasswordHash.modpow.empty()) {
|
||||||
const auto oldPasswordHash = QByteArray();
|
return serverError();
|
||||||
|
}
|
||||||
const auto hint = _passwordHint->getLastText();
|
const auto hint = _passwordHint->getLastText();
|
||||||
const auto email = _recoverEmail->getLastText().trimmed();
|
const auto email = _recoverEmail->getLastText().trimmed();
|
||||||
const auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo
|
const auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo
|
||||||
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
|
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
|
||||||
| MTPDaccount_passwordInputSettings::Flag::f_hint
|
| MTPDaccount_passwordInputSettings::Flag::f_hint
|
||||||
| MTPDaccount_passwordInputSettings::Flag::f_email;
|
| MTPDaccount_passwordInputSettings::Flag::f_email;
|
||||||
|
_checkPasswordCallback = nullptr;
|
||||||
_setRequest = request(MTPaccount_UpdatePasswordSettings(
|
_setRequest = request(MTPaccount_UpdatePasswordSettings(
|
||||||
MTP_bytes(oldPasswordHash),
|
MTP_inputCheckPasswordEmpty(),
|
||||||
MTP_account_passwordInputSettings(
|
MTP_account_passwordInputSettings(
|
||||||
MTP_flags(flags),
|
MTP_flags(flags),
|
||||||
Core::PrepareCloudPasswordAlgo(_newAlgo),
|
Core::PrepareCloudPasswordAlgo(_newAlgo),
|
||||||
MTP_bytes(newPasswordHash),
|
MTP_bytes(newPasswordHash.modpow),
|
||||||
MTP_string(hint),
|
MTP_string(hint),
|
||||||
MTP_string(email),
|
MTP_string(email),
|
||||||
MTPSecureSecretSettings())
|
MTPSecureSecretSettings())
|
||||||
|
@ -451,12 +512,17 @@ void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
|
||||||
void PasscodeBox::changeCloudPassword(
|
void PasscodeBox::changeCloudPassword(
|
||||||
const QString &oldPassword,
|
const QString &oldPassword,
|
||||||
const QString &newPassword) {
|
const QString &newPassword) {
|
||||||
const auto passwordUtf = oldPassword.toUtf8();
|
checkPassword(oldPassword, [=](const Core::CloudPasswordResult &check) {
|
||||||
const auto oldPasswordHash = Core::ComputeCloudPasswordHash(
|
changeCloudPassword(oldPassword, check, newPassword);
|
||||||
_curAlgo,
|
});
|
||||||
bytes::make_span(passwordUtf));
|
}
|
||||||
|
|
||||||
|
void PasscodeBox::changeCloudPassword(
|
||||||
|
const QString &oldPassword,
|
||||||
|
const Core::CloudPasswordResult &check,
|
||||||
|
const QString &newPassword) {
|
||||||
_setRequest = request(MTPaccount_GetPasswordSettings(
|
_setRequest = request(MTPaccount_GetPasswordSettings(
|
||||||
MTP_bytes(oldPasswordHash)
|
check.result
|
||||||
)).done([=](const MTPaccount_PasswordSettings &result) {
|
)).done([=](const MTPaccount_PasswordSettings &result) {
|
||||||
_setRequest = 0;
|
_setRequest = 0;
|
||||||
|
|
||||||
|
@ -464,12 +530,15 @@ void PasscodeBox::changeCloudPassword(
|
||||||
const auto &data = result.c_account_passwordSettings();
|
const auto &data = result.c_account_passwordSettings();
|
||||||
|
|
||||||
if (!data.has_secure_settings()) {
|
if (!data.has_secure_settings()) {
|
||||||
|
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
|
||||||
const auto empty = QByteArray();
|
const auto empty = QByteArray();
|
||||||
sendChangeCloudPassword(oldPasswordHash, newPassword, empty);
|
sendChangeCloudPassword(check, newPassword, empty);
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto &wrapped = data.vsecure_settings;
|
const auto &wrapped = data.vsecure_settings;
|
||||||
const auto &settings = wrapped.c_secureSecretSettings();
|
const auto &settings = wrapped.c_secureSecretSettings();
|
||||||
|
const auto passwordUtf = oldPassword.toUtf8();
|
||||||
const auto secret = Passport::DecryptSecureSecret(
|
const auto secret = Passport::DecryptSecureSecret(
|
||||||
bytes::make_span(settings.vsecure_secret.v),
|
bytes::make_span(settings.vsecure_secret.v),
|
||||||
Core::ComputeSecureSecretHash(
|
Core::ComputeSecureSecretHash(
|
||||||
|
@ -477,32 +546,48 @@ void PasscodeBox::changeCloudPassword(
|
||||||
bytes::make_span(passwordUtf)));
|
bytes::make_span(passwordUtf)));
|
||||||
if (secret.empty()) {
|
if (secret.empty()) {
|
||||||
LOG(("API Error: Failed to decrypt secure secret."));
|
LOG(("API Error: Failed to decrypt secure secret."));
|
||||||
suggestSecretReset(oldPasswordHash, newPassword);
|
suggestSecretReset(newPassword);
|
||||||
} else if (Passport::CountSecureSecretId(secret)
|
} else if (Passport::CountSecureSecretId(secret)
|
||||||
!= settings.vsecure_secret_id.v) {
|
!= settings.vsecure_secret_id.v) {
|
||||||
LOG(("API Error: Wrong secure secret id."));
|
LOG(("API Error: Wrong secure secret id."));
|
||||||
suggestSecretReset(oldPasswordHash, newPassword);
|
suggestSecretReset(newPassword);
|
||||||
} else {
|
} else {
|
||||||
sendChangeCloudPassword(
|
const auto secureSecret = QByteArray(
|
||||||
oldPasswordHash,
|
|
||||||
newPassword,
|
|
||||||
QByteArray::fromRawData(
|
|
||||||
reinterpret_cast<const char*>(secret.data()),
|
reinterpret_cast<const char*>(secret.data()),
|
||||||
secret.size()));
|
secret.size());
|
||||||
|
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
|
||||||
|
sendChangeCloudPassword(check, newPassword, secureSecret);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}).fail([=](const RPCError &error) {
|
}).handleFloodErrors().fail([=](const RPCError &error) {
|
||||||
setPasswordFail(error);
|
setPasswordFail(error);
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PasscodeBox::suggestSecretReset(
|
void PasscodeBox::suggestSecretReset(const QString &newPassword) {
|
||||||
const bytes::vector &oldPasswordHash,
|
|
||||||
const QString &newPassword) {
|
|
||||||
const auto box = std::make_shared<QPointer<BoxContent>>();
|
const auto box = std::make_shared<QPointer<BoxContent>>();
|
||||||
const auto resetSecretAndSave = [=] {
|
const auto resetSecretAndSave = [=] {
|
||||||
|
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
|
||||||
|
resetSecret(check, newPassword, [=] {
|
||||||
|
if (*box) {
|
||||||
|
(*box)->closeBox();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
*box = getDelegate()->show(Box<ConfirmBox>(
|
||||||
|
Lang::Hard::PassportCorruptedChange(),
|
||||||
|
Lang::Hard::PassportCorruptedReset(),
|
||||||
|
[=] { resetSecretAndSave(); }));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PasscodeBox::resetSecret(
|
||||||
|
const Core::CloudPasswordResult &check,
|
||||||
|
const QString &newPassword,
|
||||||
|
Fn<void()> callback) {
|
||||||
using Flag = MTPDaccount_passwordInputSettings::Flag;
|
using Flag = MTPDaccount_passwordInputSettings::Flag;
|
||||||
_setRequest = request(MTPaccount_UpdatePasswordSettings(
|
_setRequest = request(MTPaccount_UpdatePasswordSettings(
|
||||||
MTP_bytes(oldPasswordHash),
|
check.result,
|
||||||
MTP_account_passwordInputSettings(
|
MTP_account_passwordInputSettings(
|
||||||
MTP_flags(Flag::f_new_secure_settings),
|
MTP_flags(Flag::f_new_secure_settings),
|
||||||
MTPPasswordKdfAlgo(), // new_algo
|
MTPPasswordKdfAlgo(), // new_algo
|
||||||
|
@ -515,29 +600,30 @@ void PasscodeBox::suggestSecretReset(
|
||||||
MTP_long(0))) // secure_secret_id
|
MTP_long(0))) // secure_secret_id
|
||||||
)).done([=](const MTPBool &result) {
|
)).done([=](const MTPBool &result) {
|
||||||
_setRequest = 0;
|
_setRequest = 0;
|
||||||
|
callback();
|
||||||
|
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
|
||||||
const auto empty = QByteArray();
|
const auto empty = QByteArray();
|
||||||
if (*box) {
|
sendChangeCloudPassword(check, newPassword, empty);
|
||||||
(*box)->closeBox();
|
});
|
||||||
}
|
|
||||||
sendChangeCloudPassword(oldPasswordHash, newPassword, empty);
|
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
_setRequest = 0;
|
_setRequest = 0;
|
||||||
|
if (error.type() == qstr("SRP_ID_INVALID")) {
|
||||||
|
handleSrpIdInvalid();
|
||||||
|
}
|
||||||
}).send();
|
}).send();
|
||||||
};
|
|
||||||
*box = getDelegate()->show(Box<ConfirmBox>(
|
|
||||||
Lang::Hard::PassportCorrupted(),
|
|
||||||
Lang::Hard::PassportCorruptedReset(),
|
|
||||||
[=] { resetSecretAndSave(); }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PasscodeBox::sendChangeCloudPassword(
|
void PasscodeBox::sendChangeCloudPassword(
|
||||||
const bytes::vector &oldPasswordHash,
|
const Core::CloudPasswordResult &check,
|
||||||
const QString &newPassword,
|
const QString &newPassword,
|
||||||
const QByteArray &secureSecret) {
|
const QByteArray &secureSecret) {
|
||||||
const auto newPasswordBytes = newPassword.toUtf8();
|
const auto newPasswordBytes = newPassword.toUtf8();
|
||||||
const auto newPasswordHash = Core::ComputeCloudPasswordHash(
|
const auto newPasswordHash = Core::ComputeCloudPasswordDigest(
|
||||||
_newAlgo,
|
_newAlgo,
|
||||||
bytes::make_span(newPasswordBytes));
|
bytes::make_span(newPasswordBytes));
|
||||||
|
if (newPasswordHash.modpow.empty()) {
|
||||||
|
return serverError();
|
||||||
|
}
|
||||||
const auto hint = _passwordHint->getLastText();
|
const auto hint = _passwordHint->getLastText();
|
||||||
auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo
|
auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo
|
||||||
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
|
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
|
||||||
|
@ -555,11 +641,11 @@ void PasscodeBox::sendChangeCloudPassword(
|
||||||
bytes::make_span(newPasswordBytes)));
|
bytes::make_span(newPasswordBytes)));
|
||||||
}
|
}
|
||||||
_setRequest = request(MTPaccount_UpdatePasswordSettings(
|
_setRequest = request(MTPaccount_UpdatePasswordSettings(
|
||||||
MTP_bytes(oldPasswordHash),
|
check.result,
|
||||||
MTP_account_passwordInputSettings(
|
MTP_account_passwordInputSettings(
|
||||||
MTP_flags(flags),
|
MTP_flags(flags),
|
||||||
Core::PrepareCloudPasswordAlgo(_newAlgo),
|
Core::PrepareCloudPasswordAlgo(_newAlgo),
|
||||||
MTP_bytes(newPasswordHash),
|
MTP_bytes(newPasswordHash.modpow),
|
||||||
MTP_string(hint),
|
MTP_string(hint),
|
||||||
MTPstring(), // email is not changing
|
MTPstring(), // email is not changing
|
||||||
MTP_secureSecretSettings(
|
MTP_secureSecretSettings(
|
||||||
|
@ -568,7 +654,7 @@ void PasscodeBox::sendChangeCloudPassword(
|
||||||
MTP_long(newSecureSecretId)))
|
MTP_long(newSecureSecretId)))
|
||||||
)).done([=](const MTPBool &result) {
|
)).done([=](const MTPBool &result) {
|
||||||
setPasswordDone(newPasswordBytes);
|
setPasswordDone(newPasswordBytes);
|
||||||
}).fail([=](const RPCError &error) {
|
}).handleFloodErrors().fail([=](const RPCError &error) {
|
||||||
setPasswordFail(newPasswordBytes, error);
|
setPasswordFail(newPasswordBytes, error);
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
@ -651,12 +737,9 @@ void PasscodeBox::recoverStarted(const MTPauth_PasswordRecovery &result) {
|
||||||
recover();
|
recover();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PasscodeBox::recoverStartFail(const RPCError &error) {
|
void PasscodeBox::recoverStartFail(const RPCError &error) {
|
||||||
if (MTP::isDefaultHandledError(error)) return false;
|
|
||||||
|
|
||||||
_pattern = QString();
|
_pattern = QString();
|
||||||
closeBox();
|
closeBox();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RecoverBox::RecoverBox(
|
RecoverBox::RecoverBox(
|
||||||
|
|
|
@ -22,7 +22,7 @@ public:
|
||||||
PasscodeBox(QWidget*, bool turningOff);
|
PasscodeBox(QWidget*, bool turningOff);
|
||||||
PasscodeBox(
|
PasscodeBox(
|
||||||
QWidget*,
|
QWidget*,
|
||||||
const Core::CloudPasswordAlgo &curAlgo,
|
const Core::CloudPasswordCheckRequest &curRequest,
|
||||||
const Core::CloudPasswordAlgo &newAlgo,
|
const Core::CloudPasswordAlgo &newAlgo,
|
||||||
bool hasRecovery,
|
bool hasRecovery,
|
||||||
bool notEmptyPassport,
|
bool notEmptyPassport,
|
||||||
|
@ -41,6 +41,9 @@ protected:
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
using CheckPasswordCallback = Fn<void(
|
||||||
|
const Core::CloudPasswordResult &check)>;
|
||||||
|
|
||||||
void submit();
|
void submit();
|
||||||
void closeReplacedBy();
|
void closeReplacedBy();
|
||||||
void oldChanged();
|
void oldChanged();
|
||||||
|
@ -53,31 +56,51 @@ private:
|
||||||
bool currentlyHave() const;
|
bool currentlyHave() const;
|
||||||
|
|
||||||
void setPasswordDone(const QByteArray &newPasswordBytes);
|
void setPasswordDone(const QByteArray &newPasswordBytes);
|
||||||
bool setPasswordFail(const RPCError &error);
|
void setPasswordFail(const RPCError &error);
|
||||||
bool setPasswordFail(
|
void setPasswordFail(
|
||||||
const QByteArray &newPasswordBytes,
|
const QByteArray &newPasswordBytes,
|
||||||
const RPCError &error);
|
const RPCError &error);
|
||||||
|
|
||||||
void recoverStarted(const MTPauth_PasswordRecovery &result);
|
void recoverStarted(const MTPauth_PasswordRecovery &result);
|
||||||
bool recoverStartFail(const RPCError &error);
|
void recoverStartFail(const RPCError &error);
|
||||||
|
|
||||||
void recover();
|
void recover();
|
||||||
void clearCloudPassword(const QString &oldPassword);
|
void clearCloudPassword(const QString &oldPassword);
|
||||||
void setNewCloudPassword(const QString &newPassword);
|
void setNewCloudPassword(const QString &newPassword);
|
||||||
|
|
||||||
|
void checkPassword(
|
||||||
|
const QString &oldPassword,
|
||||||
|
CheckPasswordCallback callback);
|
||||||
|
void checkPasswordHash(CheckPasswordCallback callback);
|
||||||
|
|
||||||
void changeCloudPassword(
|
void changeCloudPassword(
|
||||||
const QString &oldPassword,
|
const QString &oldPassword,
|
||||||
const QString &newPassword);
|
const QString &newPassword);
|
||||||
|
void changeCloudPassword(
|
||||||
|
const QString &oldPassword,
|
||||||
|
const Core::CloudPasswordResult &check,
|
||||||
|
const QString &newPassword);
|
||||||
|
|
||||||
void sendChangeCloudPassword(
|
void sendChangeCloudPassword(
|
||||||
const bytes::vector &oldPasswordHash,
|
const Core::CloudPasswordResult &check,
|
||||||
const QString &newPassword,
|
const QString &newPassword,
|
||||||
const QByteArray &secureSecret);
|
const QByteArray &secureSecret);
|
||||||
void suggestSecretReset(
|
void suggestSecretReset(const QString &newPassword);
|
||||||
const bytes::vector &oldPasswordHash,
|
void resetSecret(
|
||||||
const QString &newPassword);
|
const Core::CloudPasswordResult &check,
|
||||||
|
const QString &newPassword,
|
||||||
|
Fn<void()> callback);
|
||||||
void resetSecretAndChangePassword(
|
void resetSecretAndChangePassword(
|
||||||
const bytes::vector &oldPasswordHash,
|
const bytes::vector &oldPasswordHash,
|
||||||
const QString &newPassword);
|
const QString &newPassword);
|
||||||
|
|
||||||
void sendClearCloudPassword(const QString &oldPassword);
|
void sendClearCloudPassword(const QString &oldPassword);
|
||||||
|
void sendClearCloudPassword(const Core::CloudPasswordResult &check);
|
||||||
|
|
||||||
|
void handleSrpIdInvalid();
|
||||||
|
void requestPasswordData();
|
||||||
|
void passwordChecked();
|
||||||
|
void serverError();
|
||||||
|
|
||||||
QString _pattern;
|
QString _pattern;
|
||||||
|
|
||||||
|
@ -86,12 +109,15 @@ private:
|
||||||
bool _cloudPwd = false;
|
bool _cloudPwd = false;
|
||||||
mtpRequestId _setRequest = 0;
|
mtpRequestId _setRequest = 0;
|
||||||
|
|
||||||
Core::CloudPasswordAlgo _curAlgo;
|
Core::CloudPasswordCheckRequest _curRequest;
|
||||||
|
TimeMs _lastSrpIdInvalidTime = 0;
|
||||||
Core::CloudPasswordAlgo _newAlgo;
|
Core::CloudPasswordAlgo _newAlgo;
|
||||||
Core::SecureSecretAlgo _newSecureSecretAlgo;
|
Core::SecureSecretAlgo _newSecureSecretAlgo;
|
||||||
bool _hasRecovery = false;
|
bool _hasRecovery = false;
|
||||||
bool _notEmptyPassport = false;
|
bool _notEmptyPassport = false;
|
||||||
bool _skipEmailWarning = false;
|
bool _skipEmailWarning = false;
|
||||||
|
CheckPasswordCallback _checkPasswordCallback;
|
||||||
|
bytes::vector _checkPasswordHash;
|
||||||
|
|
||||||
int _aboutHeight = 0;
|
int _aboutHeight = 0;
|
||||||
|
|
||||||
|
|
|
@ -55,12 +55,13 @@ addChildParentFlags('MTPDchannelForbidden', 'MTPDchannel');
|
||||||
parentFlagsCheck = {};
|
parentFlagsCheck = {};
|
||||||
|
|
||||||
countedTypeIdExceptions = {};
|
countedTypeIdExceptions = {};
|
||||||
for i in range(77, 84):
|
countedTypeIdExceptions['channel#c88974ac'] = True
|
||||||
countedTypeIdExceptions[i] = {}
|
countedTypeIdExceptions['ipPortSecret#37982646'] = True
|
||||||
countedTypeIdExceptions[i]['channel'] = True
|
countedTypeIdExceptions['accessPointRule#4679b65f'] = True
|
||||||
countedTypeIdExceptions['ipPortSecret'] = True
|
countedTypeIdExceptions['help.configSimple#5a592a6c'] = True
|
||||||
countedTypeIdExceptions['accessPointRule'] = True
|
|
||||||
countedTypeIdExceptions['help_configSimple'] = True
|
renamedTypes = {};
|
||||||
|
renamedTypes['passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow'] = 'passwordKdfAlgoModPow';
|
||||||
|
|
||||||
lines = [];
|
lines = [];
|
||||||
layer = '';
|
layer = '';
|
||||||
|
@ -119,7 +120,10 @@ for line in lines:
|
||||||
sys.exit(1);
|
sys.exit(1);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
name = nametype.group(1);
|
originalname = nametype.group(1);
|
||||||
|
name = originalname;
|
||||||
|
if (name in renamedTypes):
|
||||||
|
name = renamedTypes[name];
|
||||||
nameInd = name.find('.');
|
nameInd = name.find('.');
|
||||||
if (nameInd >= 0):
|
if (nameInd >= 0):
|
||||||
Name = name[0:nameInd] + '_' + name[nameInd + 1:nameInd + 2].upper() + name[nameInd + 2:];
|
Name = name[0:nameInd] + '_' + name[nameInd + 1:nameInd + 2].upper() + name[nameInd + 2:];
|
||||||
|
@ -144,17 +148,19 @@ for line in lines:
|
||||||
countTypeId = binascii.crc32(binascii.a2b_qp(cleanline));
|
countTypeId = binascii.crc32(binascii.a2b_qp(cleanline));
|
||||||
if (countTypeId < 0):
|
if (countTypeId < 0):
|
||||||
countTypeId += 2 ** 32;
|
countTypeId += 2 ** 32;
|
||||||
countTypeId = '0x' + re.sub(r'^0x|L$', '', hex(countTypeId));
|
countTypeId = re.sub(r'^0x|L$', '', hex(countTypeId));
|
||||||
if (typeid and len(typeid) > 0):
|
if (typeid and len(typeid) > 0):
|
||||||
typeid = '0x' + typeid;
|
typeid = typeid;
|
||||||
if (typeid != countTypeId):
|
if (typeid != countTypeId):
|
||||||
if (not layerIndex in countedTypeIdExceptions or not name in countedTypeIdExceptions[layerIndex]):
|
key = originalname + '#' + typeid;
|
||||||
if (not name in countedTypeIdExceptions):
|
if (not key in countedTypeIdExceptions):
|
||||||
print('Warning: counted ' + countTypeId + ' mismatch with provided ' + typeid + ' (' + cleanline + ')');
|
print('Warning: counted ' + countTypeId + ' mismatch with provided ' + typeid + ' (' + key + ', ' + cleanline + ')');
|
||||||
continue;
|
continue;
|
||||||
else:
|
else:
|
||||||
typeid = countTypeId;
|
typeid = countTypeId;
|
||||||
|
|
||||||
|
typeid = '0x' + typeid;
|
||||||
|
|
||||||
params = nametype.group(3);
|
params = nametype.group(3);
|
||||||
restype = nametype.group(4);
|
restype = nametype.group(4);
|
||||||
if (restype.find('<') >= 0):
|
if (restype.find('<') >= 0):
|
||||||
|
@ -216,7 +222,7 @@ for line in lines:
|
||||||
if (templ):
|
if (templ):
|
||||||
hasTemplate = templ.group(1);
|
hasTemplate = templ.group(1);
|
||||||
continue;
|
continue;
|
||||||
pnametype = re.match(r'([a-z_][a-z0-9_]*):([A-Za-z0-9<>\._]+|![a-zA-Z]+|\#|[a-z_][a-z0-9_]*\.[0-9]+\?[A-Za-z0-9<>\._]+)$', param);
|
pnametype = re.match(r'([a-zA-Z_][a-zA-Z0-9_]*):([A-Za-z0-9<>\._]+|![a-zA-Z]+|\#|[a-z_][a-z0-9_]*\.[0-9]+\?[A-Za-z0-9<>\._]+)$', param);
|
||||||
if (not pnametype):
|
if (not pnametype):
|
||||||
print('Bad param found: "' + param + '" in line: ' + line);
|
print('Bad param found: "' + param + '" in line: ' + line);
|
||||||
sys.exit(1);
|
sys.exit(1);
|
||||||
|
|
|
@ -8,59 +8,204 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "core/core_cloud_password.h"
|
#include "core/core_cloud_password.h"
|
||||||
|
|
||||||
#include "base/openssl_help.h"
|
#include "base/openssl_help.h"
|
||||||
|
#include "mtproto/connection.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kAdditionalSalt = size_type(8);
|
using namespace openssl;
|
||||||
|
|
||||||
|
constexpr auto kAdditionalSalt = size_type(32);
|
||||||
|
|
||||||
|
constexpr auto kSizeForHash = 256;
|
||||||
|
bytes::vector NumBytesForHash(bytes::const_span number) {
|
||||||
|
const auto fill = kSizeForHash - number.size();
|
||||||
|
if (!fill) {
|
||||||
|
return bytes::make_vector(number);
|
||||||
|
}
|
||||||
|
auto result = bytes::vector(kSizeForHash);
|
||||||
|
const auto storage = bytes::make_span(result);
|
||||||
|
bytes::set_with_const(storage.subspan(0, fill), bytes::type(0));
|
||||||
|
bytes::copy(storage.subspan(fill), number);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes::vector BigNumForHash(const BigNum &number) {
|
||||||
|
auto result = number.getBytes();
|
||||||
|
if (result.size() == kSizeForHash) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return NumBytesForHash(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPositive(const BigNum &number) {
|
||||||
|
return !number.isNegative() && (number.bitsSize() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsGoodLarge(const BigNum &number, const BigNum &p) {
|
||||||
|
return IsPositive(number) && IsPositive(BigNum::Sub(p, number));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes::vector Xor(bytes::const_span a, bytes::const_span b) {
|
||||||
|
Expects(a.size() == b.size());
|
||||||
|
|
||||||
|
auto result = bytes::vector(a.size());
|
||||||
|
for (auto i = index_type(); i != a.size(); ++i) {
|
||||||
|
result[i] = a[i] ^ b[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
bytes::vector ComputeHash(
|
||||||
|
const CloudPasswordAlgoModPow &algo,
|
||||||
|
bytes::const_span password) {
|
||||||
|
const auto hash1 = Sha256(algo.salt1, password, algo.salt1);
|
||||||
|
const auto hash2 = Sha256(algo.salt2, hash1, algo.salt2);
|
||||||
|
const auto hash3 = Pbkdf2Sha512(hash2, algo.salt1, algo.kIterations);
|
||||||
|
return Sha256(algo.salt2, hash3, algo.salt2);
|
||||||
|
}
|
||||||
|
|
||||||
|
CloudPasswordDigest ComputeDigest(
|
||||||
|
const CloudPasswordAlgoModPow &algo,
|
||||||
|
bytes::const_span password) {
|
||||||
|
if (!MTP::IsPrimeAndGood(algo.p, algo.g)) {
|
||||||
|
LOG(("API Error: Bad p/g in cloud password creation!"));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const auto value = BigNum::ModExp(
|
||||||
|
BigNum(algo.g),
|
||||||
|
BigNum(ComputeHash(algo, password)),
|
||||||
|
BigNum(algo.p));
|
||||||
|
if (value.failed()) {
|
||||||
|
LOG(("API Error: Failed to count g_x in cloud password creation!"));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return { BigNumForHash(value) };
|
||||||
|
}
|
||||||
|
|
||||||
|
CloudPasswordResult ComputeCheck(
|
||||||
|
const CloudPasswordCheckRequest &request,
|
||||||
|
const CloudPasswordAlgoModPow &algo,
|
||||||
|
bytes::const_span hash) {
|
||||||
|
const auto failed = [] {
|
||||||
|
return CloudPasswordResult{ MTP_inputCheckPasswordEmpty() };
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto p = BigNum(algo.p);
|
||||||
|
const auto g = BigNum(algo.g);
|
||||||
|
const auto B = BigNum(request.B);
|
||||||
|
if (!MTP::IsPrimeAndGood(algo.p, algo.g)) {
|
||||||
|
LOG(("API Error: Bad p/g in cloud password creation!"));
|
||||||
|
return failed();
|
||||||
|
} else if (!IsGoodLarge(B, p)) {
|
||||||
|
LOG(("API Error: Bad B in cloud password check!"));
|
||||||
|
return failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto context = Context();
|
||||||
|
const auto x = BigNum(hash);
|
||||||
|
const auto pForHash = NumBytesForHash(algo.p);
|
||||||
|
const auto gForHash = BigNumForHash(g);
|
||||||
|
const auto BForHash = NumBytesForHash(request.B);
|
||||||
|
const auto g_x = BigNum::ModExp(g, x, p, context);
|
||||||
|
const auto k = BigNum(Sha256(pForHash, gForHash));
|
||||||
|
const auto kg_x = BigNum::ModMul(k, g_x, p, context);
|
||||||
|
|
||||||
|
const auto GenerateAndCheckRandom = [&] {
|
||||||
|
constexpr auto kRandomSize = 256;
|
||||||
|
while (true) {
|
||||||
|
auto random = bytes::vector(kRandomSize);
|
||||||
|
bytes::set_random(random);
|
||||||
|
const auto a = BigNum(random);
|
||||||
|
const auto A = BigNum::ModExp(g, a, p, context);
|
||||||
|
if (MTP::IsGoodModExpFirst(A, p)) {
|
||||||
|
auto AForHash = BigNumForHash(A);
|
||||||
|
const auto u = BigNum(Sha256(AForHash, BForHash));
|
||||||
|
if (IsPositive(u)) {
|
||||||
|
return std::make_tuple(a, std::move(AForHash), u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto [a, AForHash, u] = GenerateAndCheckRandom();
|
||||||
|
const auto g_b = BigNum::ModSub(B, kg_x, p, context);
|
||||||
|
if (!MTP::IsGoodModExpFirst(g_b, p)) {
|
||||||
|
LOG(("API Error: Bad g_b in cloud password check!"));
|
||||||
|
return failed();
|
||||||
|
}
|
||||||
|
const auto ux = BigNum::Mul(u, x, context);
|
||||||
|
const auto a_ux = BigNum::Add(a, ux);
|
||||||
|
const auto S = BigNum::ModExp(g_b, a_ux, p, context);
|
||||||
|
if (S.failed()) {
|
||||||
|
LOG(("API Error: Failed to count S in cloud password check!"));
|
||||||
|
return failed();
|
||||||
|
}
|
||||||
|
const auto K = Sha256(BigNumForHash(S));
|
||||||
|
const auto M1 = Sha256(
|
||||||
|
Xor(Sha256(pForHash), Sha256(gForHash)),
|
||||||
|
Sha256(algo.salt1),
|
||||||
|
Sha256(algo.salt2),
|
||||||
|
AForHash,
|
||||||
|
BForHash,
|
||||||
|
K);
|
||||||
|
return CloudPasswordResult{ MTP_inputCheckPasswordSRP(
|
||||||
|
MTP_long(request.id),
|
||||||
|
MTP_bytes(AForHash),
|
||||||
|
MTP_bytes(M1))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
bytes::vector ComputeHash(
|
bytes::vector ComputeHash(
|
||||||
base::none_type,
|
base::none_type,
|
||||||
bytes::const_span password) {
|
bytes::const_span password) {
|
||||||
Unexpected("Bad cloud password algorithm.");
|
Unexpected("Bad secure secret algorithm.");
|
||||||
}
|
|
||||||
|
|
||||||
bytes::vector ComputeHash(
|
|
||||||
const CloudPasswordAlgoPBKDF2 &algo,
|
|
||||||
bytes::const_span password) {
|
|
||||||
const auto hash1 = openssl::Sha256(algo.salt1, password, algo.salt1);
|
|
||||||
const auto hash2 = openssl::Sha256(algo.salt2, hash1, algo.salt2);
|
|
||||||
return openssl::Pbkdf2Sha512(
|
|
||||||
hash2,
|
|
||||||
bytes::make_span(algo.salt1),
|
|
||||||
algo.kIterations);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes::vector ComputeHash(
|
bytes::vector ComputeHash(
|
||||||
const SecureSecretAlgoSHA512 &algo,
|
const SecureSecretAlgoSHA512 &algo,
|
||||||
bytes::const_span password) {
|
bytes::const_span password) {
|
||||||
return openssl::Sha512(algo.salt, password, algo.salt);
|
return Sha512(algo.salt, password, algo.salt);
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes::vector ComputeHash(
|
bytes::vector ComputeHash(
|
||||||
const SecureSecretAlgoPBKDF2 &algo,
|
const SecureSecretAlgoPBKDF2 &algo,
|
||||||
bytes::const_span password) {
|
bytes::const_span password) {
|
||||||
return openssl::Pbkdf2Sha512(password, algo.salt, algo.kIterations);
|
return Pbkdf2Sha512(password, algo.salt, algo.kIterations);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
CloudPasswordAlgo ParseCloudPasswordAlgo(const MTPPasswordKdfAlgo &data) {
|
CloudPasswordAlgo ParseCloudPasswordAlgo(const MTPPasswordKdfAlgo &data) {
|
||||||
return data.match([](
|
return data.match([](const MTPDpasswordKdfAlgoModPow &data) {
|
||||||
const MTPDpasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000 &data) {
|
return CloudPasswordAlgo(CloudPasswordAlgoModPow{
|
||||||
return CloudPasswordAlgo(CloudPasswordAlgoPBKDF2{
|
|
||||||
bytes::make_vector(data.vsalt1.v),
|
bytes::make_vector(data.vsalt1.v),
|
||||||
bytes::make_vector(data.vsalt2.v) });
|
bytes::make_vector(data.vsalt2.v),
|
||||||
|
data.vg.v,
|
||||||
|
bytes::make_vector(data.vp.v) });
|
||||||
}, [](const MTPDpasswordKdfAlgoUnknown &data) {
|
}, [](const MTPDpasswordKdfAlgoUnknown &data) {
|
||||||
return CloudPasswordAlgo();
|
return CloudPasswordAlgo();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CloudPasswordCheckRequest ParseCloudPasswordCheckRequest(
|
||||||
|
const MTPDaccount_password &data) {
|
||||||
|
return CloudPasswordCheckRequest{
|
||||||
|
(data.has_srp_id() ? data.vsrp_id.v : uint64()),
|
||||||
|
(data.has_srp_B()
|
||||||
|
? bytes::make_vector(data.vsrp_B.v)
|
||||||
|
: bytes::vector()),
|
||||||
|
(data.has_current_algo()
|
||||||
|
? ParseCloudPasswordAlgo(data.vcurrent_algo)
|
||||||
|
: CloudPasswordAlgo())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
CloudPasswordAlgo ValidateNewCloudPasswordAlgo(CloudPasswordAlgo &&parsed) {
|
CloudPasswordAlgo ValidateNewCloudPasswordAlgo(CloudPasswordAlgo &&parsed) {
|
||||||
if (!parsed.is<CloudPasswordAlgoPBKDF2>()) {
|
if (!parsed.is<CloudPasswordAlgoModPow>()) {
|
||||||
return base::none;
|
return base::none;
|
||||||
}
|
}
|
||||||
auto &value = parsed.get_unchecked<CloudPasswordAlgoPBKDF2>();
|
auto &value = parsed.get_unchecked<CloudPasswordAlgoModPow>();
|
||||||
const auto already = value.salt1.size();
|
const auto already = value.salt1.size();
|
||||||
value.salt1.resize(already + kAdditionalSalt);
|
value.salt1.resize(already + kAdditionalSalt);
|
||||||
bytes::set_random(bytes::make_span(value.salt1).subspan(already));
|
bytes::set_random(bytes::make_span(value.salt1).subspan(already));
|
||||||
|
@ -68,20 +213,48 @@ CloudPasswordAlgo ValidateNewCloudPasswordAlgo(CloudPasswordAlgo &&parsed) {
|
||||||
}
|
}
|
||||||
|
|
||||||
MTPPasswordKdfAlgo PrepareCloudPasswordAlgo(const CloudPasswordAlgo &data) {
|
MTPPasswordKdfAlgo PrepareCloudPasswordAlgo(const CloudPasswordAlgo &data) {
|
||||||
return data.match([](const CloudPasswordAlgoPBKDF2 &data) {
|
return data.match([](const CloudPasswordAlgoModPow &data) {
|
||||||
return MTP_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000(
|
return MTP_passwordKdfAlgoModPow(
|
||||||
MTP_bytes(data.salt1),
|
MTP_bytes(data.salt1),
|
||||||
MTP_bytes(data.salt2));
|
MTP_bytes(data.salt2),
|
||||||
|
MTP_int(data.g),
|
||||||
|
MTP_bytes(data.p));
|
||||||
}, [](base::none_type) {
|
}, [](base::none_type) {
|
||||||
return MTP_passwordKdfAlgoUnknown();
|
return MTP_passwordKdfAlgoUnknown();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CloudPasswordResult::operator bool() const {
|
||||||
|
return (result.type() != mtpc_inputCheckPasswordEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
bytes::vector ComputeCloudPasswordHash(
|
bytes::vector ComputeCloudPasswordHash(
|
||||||
const CloudPasswordAlgo &algo,
|
const CloudPasswordAlgo &algo,
|
||||||
bytes::const_span password) {
|
bytes::const_span password) {
|
||||||
return algo.match([&](const auto &data) {
|
return algo.match([&](const CloudPasswordAlgoModPow &data) {
|
||||||
return ComputeHash(data, password);
|
return ComputeHash(data, password);
|
||||||
|
}, [](base::none_type) -> bytes::vector {
|
||||||
|
Unexpected("Bad cloud password algorithm.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
CloudPasswordDigest ComputeCloudPasswordDigest(
|
||||||
|
const CloudPasswordAlgo &algo,
|
||||||
|
bytes::const_span password) {
|
||||||
|
return algo.match([&](const CloudPasswordAlgoModPow &data) {
|
||||||
|
return ComputeDigest(data, password);
|
||||||
|
}, [](base::none_type) -> CloudPasswordDigest {
|
||||||
|
Unexpected("Bad cloud password algorithm.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
CloudPasswordResult ComputeCloudPasswordCheck(
|
||||||
|
const CloudPasswordCheckRequest &request,
|
||||||
|
bytes::const_span hash) {
|
||||||
|
return request.algo.match([&](const CloudPasswordAlgoModPow &data) {
|
||||||
|
return ComputeCheck(request, data, hash);
|
||||||
|
}, [](base::none_type) -> CloudPasswordResult {
|
||||||
|
Unexpected("Bad cloud password algorithm.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,29 +11,79 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
struct CloudPasswordAlgoPBKDF2 {
|
constexpr auto kHandleSrpIdInvalidTimeout = 60 * TimeMs(1000);
|
||||||
|
|
||||||
|
struct CloudPasswordAlgoModPow {
|
||||||
static constexpr auto kIterations = 100000;
|
static constexpr auto kIterations = 100000;
|
||||||
|
|
||||||
bytes::vector salt1;
|
bytes::vector salt1;
|
||||||
bytes::vector salt2;
|
bytes::vector salt2;
|
||||||
|
int g = 0;
|
||||||
|
bytes::vector p;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool operator==(
|
inline bool operator==(
|
||||||
const CloudPasswordAlgoPBKDF2 &a,
|
const CloudPasswordAlgoModPow &a,
|
||||||
const CloudPasswordAlgoPBKDF2 &b) {
|
const CloudPasswordAlgoModPow &b) {
|
||||||
return (a.salt1 == b.salt1) && (a.salt2 == b.salt2);
|
return (a.salt1 == b.salt1)
|
||||||
|
&& (a.salt2 == b.salt2)
|
||||||
|
&& (a.g == b.g)
|
||||||
|
&& (a.p == b.p);
|
||||||
}
|
}
|
||||||
|
|
||||||
using CloudPasswordAlgo = base::optional_variant<CloudPasswordAlgoPBKDF2>;
|
using CloudPasswordAlgo = base::optional_variant<CloudPasswordAlgoModPow>;
|
||||||
|
|
||||||
CloudPasswordAlgo ParseCloudPasswordAlgo(const MTPPasswordKdfAlgo &data);
|
CloudPasswordAlgo ParseCloudPasswordAlgo(const MTPPasswordKdfAlgo &data);
|
||||||
CloudPasswordAlgo ValidateNewCloudPasswordAlgo(CloudPasswordAlgo &&parsed);
|
CloudPasswordAlgo ValidateNewCloudPasswordAlgo(CloudPasswordAlgo &&parsed);
|
||||||
MTPPasswordKdfAlgo PrepareCloudPasswordAlgo(const CloudPasswordAlgo &data);
|
MTPPasswordKdfAlgo PrepareCloudPasswordAlgo(const CloudPasswordAlgo &data);
|
||||||
|
|
||||||
|
struct CloudPasswordCheckRequest {
|
||||||
|
uint64 id = 0;
|
||||||
|
bytes::vector B;
|
||||||
|
CloudPasswordAlgo algo;
|
||||||
|
|
||||||
|
explicit operator bool() const {
|
||||||
|
return !!algo;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool operator==(
|
||||||
|
const CloudPasswordCheckRequest &a,
|
||||||
|
const CloudPasswordCheckRequest &b) {
|
||||||
|
return (a.id == b.id) && (a.B == b.B) && (a.algo == b.algo);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator!=(
|
||||||
|
const CloudPasswordCheckRequest &a,
|
||||||
|
const CloudPasswordCheckRequest &b) {
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
CloudPasswordCheckRequest ParseCloudPasswordCheckRequest(
|
||||||
|
const MTPDaccount_password &data);
|
||||||
|
|
||||||
|
struct CloudPasswordResult {
|
||||||
|
MTPInputCheckPasswordSRP result;
|
||||||
|
|
||||||
|
explicit operator bool() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CloudPasswordDigest {
|
||||||
|
bytes::vector modpow;
|
||||||
|
};
|
||||||
|
|
||||||
bytes::vector ComputeCloudPasswordHash(
|
bytes::vector ComputeCloudPasswordHash(
|
||||||
const CloudPasswordAlgo &algo,
|
const CloudPasswordAlgo &algo,
|
||||||
bytes::const_span password);
|
bytes::const_span password);
|
||||||
|
|
||||||
|
CloudPasswordDigest ComputeCloudPasswordDigest(
|
||||||
|
const CloudPasswordAlgo &algo,
|
||||||
|
bytes::const_span password);
|
||||||
|
|
||||||
|
CloudPasswordResult ComputeCloudPasswordCheck(
|
||||||
|
const CloudPasswordCheckRequest &request,
|
||||||
|
bytes::const_span hash);
|
||||||
|
|
||||||
struct SecureSecretAlgoSHA512 {
|
struct SecureSecretAlgoSHA512 {
|
||||||
bytes::vector salt;
|
bytes::vector salt;
|
||||||
};
|
};
|
||||||
|
|
|
@ -293,14 +293,12 @@ void CodeWidget::gotPassword(const MTPaccount_Password &result) {
|
||||||
stopCheck();
|
stopCheck();
|
||||||
_sentRequest = 0;
|
_sentRequest = 0;
|
||||||
const auto &d = result.c_account_password();
|
const auto &d = result.c_account_password();
|
||||||
getData()->pwdAlgo = d.has_current_algo()
|
getData()->pwdRequest = Core::ParseCloudPasswordCheckRequest(d);
|
||||||
? Core::ParseCloudPasswordAlgo(d.vcurrent_algo)
|
if (!d.has_current_algo() || !d.has_srp_id() || !d.has_srp_B()) {
|
||||||
: Core::CloudPasswordAlgo();
|
|
||||||
if (!d.has_current_algo()) {
|
|
||||||
LOG(("API Error: No current password received on login."));
|
LOG(("API Error: No current password received on login."));
|
||||||
_code->setFocus();
|
_code->setFocus();
|
||||||
return;
|
return;
|
||||||
} else if (!getData()->pwdAlgo) {
|
} else if (!getData()->pwdRequest) {
|
||||||
const auto box = std::make_shared<QPointer<BoxContent>>();
|
const auto box = std::make_shared<QPointer<BoxContent>>();
|
||||||
const auto callback = [=] {
|
const auto callback = [=] {
|
||||||
Core::UpdateApplication();
|
Core::UpdateApplication();
|
||||||
|
@ -326,7 +324,7 @@ void CodeWidget::submit() {
|
||||||
_checkRequest->start(1000);
|
_checkRequest->start(1000);
|
||||||
|
|
||||||
_sentCode = _code->getLastText();
|
_sentCode = _code->getLastText();
|
||||||
getData()->pwdAlgo = Core::CloudPasswordAlgo();
|
getData()->pwdRequest = Core::CloudPasswordCheckRequest();
|
||||||
getData()->hasRecovery = false;
|
getData()->hasRecovery = false;
|
||||||
getData()->pwdHint = QString();
|
getData()->pwdHint = QString();
|
||||||
getData()->pwdNotEmptyPassport = false;
|
getData()->pwdNotEmptyPassport = false;
|
||||||
|
|
|
@ -26,7 +26,7 @@ PwdCheckWidget::PwdCheckWidget(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
Widget::Data *data)
|
Widget::Data *data)
|
||||||
: Step(parent, data)
|
: Step(parent, data)
|
||||||
, _algo(getData()->pwdAlgo)
|
, _request(getData()->pwdRequest)
|
||||||
, _hasRecovery(getData()->hasRecovery)
|
, _hasRecovery(getData()->hasRecovery)
|
||||||
, _notEmptyPassport(getData()->pwdNotEmptyPassport)
|
, _notEmptyPassport(getData()->pwdNotEmptyPassport)
|
||||||
, _hint(getData()->pwdHint)
|
, _hint(getData()->pwdHint)
|
||||||
|
@ -36,7 +36,7 @@ PwdCheckWidget::PwdCheckWidget(
|
||||||
, _toRecover(this, lang(lng_signin_recover))
|
, _toRecover(this, lang(lng_signin_recover))
|
||||||
, _toPassword(this, lang(lng_signin_try_password))
|
, _toPassword(this, lang(lng_signin_try_password))
|
||||||
, _checkRequest(this) {
|
, _checkRequest(this) {
|
||||||
Expects(_algo.has_value());
|
Expects(!!_request);
|
||||||
|
|
||||||
subscribe(Lang::Current().updated(), [this] { refreshLang(); });
|
subscribe(Lang::Current().updated(), [this] { refreshLang(); });
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ void PwdCheckWidget::activate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PwdCheckWidget::cancelled() {
|
void PwdCheckWidget::cancelled() {
|
||||||
MTP::cancel(base::take(_sentRequest));
|
request(base::take(_sentRequest)).cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PwdCheckWidget::stopCheck() {
|
void PwdCheckWidget::stopCheck() {
|
||||||
|
@ -115,7 +115,7 @@ void PwdCheckWidget::onCheckRequest() {
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
auto leftms = -status;
|
auto leftms = -status;
|
||||||
if (leftms >= 1000) {
|
if (leftms >= 1000) {
|
||||||
MTP::cancel(base::take(_sentRequest));
|
request(base::take(_sentRequest)).cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!_sentRequest && status == MTP::RequestSent) {
|
if (!_sentRequest && status == MTP::RequestSent) {
|
||||||
|
@ -137,27 +137,28 @@ void PwdCheckWidget::pwdSubmitDone(bool recover, const MTPauth_Authorization &re
|
||||||
finish(d.vuser);
|
finish(d.vuser);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PwdCheckWidget::pwdSubmitFail(const RPCError &error) {
|
void PwdCheckWidget::pwdSubmitFail(const RPCError &error) {
|
||||||
if (MTP::isFloodError(error)) {
|
if (MTP::isFloodError(error)) {
|
||||||
_sentRequest = 0;
|
_sentRequest = 0;
|
||||||
stopCheck();
|
stopCheck();
|
||||||
showError(langFactory(lng_flood_error));
|
showError(langFactory(lng_flood_error));
|
||||||
_pwdField->showError();
|
_pwdField->showError();
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
if (MTP::isDefaultHandledError(error)) return false;
|
|
||||||
|
|
||||||
_sentRequest = 0;
|
_sentRequest = 0;
|
||||||
stopCheck();
|
stopCheck();
|
||||||
auto &err = error.type();
|
auto &err = error.type();
|
||||||
if (err == qstr("PASSWORD_HASH_INVALID")) {
|
if (err == qstr("PASSWORD_HASH_INVALID")
|
||||||
|
|| err == qstr("SRP_PASSWORD_CHANGED")) {
|
||||||
showError(langFactory(lng_signin_bad_password));
|
showError(langFactory(lng_signin_bad_password));
|
||||||
_pwdField->selectAll();
|
_pwdField->selectAll();
|
||||||
_pwdField->showError();
|
_pwdField->showError();
|
||||||
return true;
|
|
||||||
} else if (err == qstr("PASSWORD_EMPTY")) {
|
} else if (err == qstr("PASSWORD_EMPTY")) {
|
||||||
goBack();
|
goBack();
|
||||||
}
|
} else if (err == qstr("SRP_ID_INVALID")) {
|
||||||
|
handleSrpIdInvalid();
|
||||||
|
} else {
|
||||||
if (Logs::DebugEnabled()) { // internal server error
|
if (Logs::DebugEnabled()) { // internal server error
|
||||||
auto text = err + ": " + error.description();
|
auto text = err + ": " + error.description();
|
||||||
showError([text] { return text; });
|
showError([text] { return text; });
|
||||||
|
@ -165,36 +166,88 @@ bool PwdCheckWidget::pwdSubmitFail(const RPCError &error) {
|
||||||
showError(&Lang::Hard::ServerError);
|
showError(&Lang::Hard::ServerError);
|
||||||
}
|
}
|
||||||
_pwdField->setFocus();
|
_pwdField->setFocus();
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PwdCheckWidget::codeSubmitFail(const RPCError &error) {
|
void PwdCheckWidget::handleSrpIdInvalid() {
|
||||||
|
const auto now = getms(true);
|
||||||
|
if (_lastSrpIdInvalidTime > 0
|
||||||
|
&& now - _lastSrpIdInvalidTime < Core::kHandleSrpIdInvalidTimeout) {
|
||||||
|
_request.id = 0;
|
||||||
|
showError(&Lang::Hard::ServerError);
|
||||||
|
} else {
|
||||||
|
_lastSrpIdInvalidTime = now;
|
||||||
|
requestPasswordData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PwdCheckWidget::checkPasswordHash() {
|
||||||
|
if (_request.id) {
|
||||||
|
passwordChecked();
|
||||||
|
} else {
|
||||||
|
requestPasswordData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PwdCheckWidget::requestPasswordData() {
|
||||||
|
request(base::take(_sentRequest)).cancel();
|
||||||
|
_sentRequest = request(
|
||||||
|
MTPaccount_GetPassword()
|
||||||
|
).done([=](const MTPaccount_Password &result) {
|
||||||
|
_sentRequest = 0;
|
||||||
|
result.match([&](const MTPDaccount_password &data) {
|
||||||
|
_request = Core::ParseCloudPasswordCheckRequest(data);
|
||||||
|
passwordChecked();
|
||||||
|
});
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PwdCheckWidget::passwordChecked() {
|
||||||
|
if (!_request || !_request.id) {
|
||||||
|
return serverError();
|
||||||
|
}
|
||||||
|
const auto check = Core::ComputeCloudPasswordCheck(
|
||||||
|
_request,
|
||||||
|
_passwordHash);
|
||||||
|
if (!check) {
|
||||||
|
return serverError();
|
||||||
|
}
|
||||||
|
_request.id = 0;
|
||||||
|
_sentRequest = request(
|
||||||
|
MTPauth_CheckPassword(check.result)
|
||||||
|
).done([=](const MTPauth_Authorization &result) {
|
||||||
|
pwdSubmitDone(false, result);
|
||||||
|
}).handleFloodErrors().fail([=](const RPCError &error) {
|
||||||
|
pwdSubmitFail(error);
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PwdCheckWidget::serverError() {
|
||||||
|
showError(&Lang::Hard::ServerError);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PwdCheckWidget::codeSubmitFail(const RPCError &error) {
|
||||||
if (MTP::isFloodError(error)) {
|
if (MTP::isFloodError(error)) {
|
||||||
showError(langFactory(lng_flood_error));
|
showError(langFactory(lng_flood_error));
|
||||||
_codeField->showError();
|
_codeField->showError();
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
if (MTP::isDefaultHandledError(error)) return false;
|
|
||||||
|
|
||||||
_sentRequest = 0;
|
_sentRequest = 0;
|
||||||
stopCheck();
|
stopCheck();
|
||||||
const QString &err = error.type();
|
const QString &err = error.type();
|
||||||
if (err == qstr("PASSWORD_EMPTY")) {
|
if (err == qstr("PASSWORD_EMPTY")) {
|
||||||
goBack();
|
goBack();
|
||||||
return true;
|
|
||||||
} else if (err == qstr("PASSWORD_RECOVERY_NA")) {
|
} else if (err == qstr("PASSWORD_RECOVERY_NA")) {
|
||||||
recoverStartFail(error);
|
recoverStartFail(error);
|
||||||
return true;
|
|
||||||
} else if (err == qstr("PASSWORD_RECOVERY_EXPIRED")) {
|
} else if (err == qstr("PASSWORD_RECOVERY_EXPIRED")) {
|
||||||
_emailPattern = QString();
|
_emailPattern = QString();
|
||||||
onToPassword();
|
onToPassword();
|
||||||
return true;
|
|
||||||
} else if (err == qstr("CODE_INVALID")) {
|
} else if (err == qstr("CODE_INVALID")) {
|
||||||
showError(langFactory(lng_signin_wrong_code));
|
showError(langFactory(lng_signin_wrong_code));
|
||||||
_codeField->selectAll();
|
_codeField->selectAll();
|
||||||
_codeField->showError();
|
_codeField->showError();
|
||||||
return true;
|
} else {
|
||||||
}
|
|
||||||
if (Logs::DebugEnabled()) { // internal server error
|
if (Logs::DebugEnabled()) { // internal server error
|
||||||
auto text = err + ": " + error.description();
|
auto text = err + ": " + error.description();
|
||||||
showError([text] { return text; });
|
showError([text] { return text; });
|
||||||
|
@ -202,7 +255,7 @@ bool PwdCheckWidget::codeSubmitFail(const RPCError &error) {
|
||||||
showError(&Lang::Hard::ServerError);
|
showError(&Lang::Hard::ServerError);
|
||||||
}
|
}
|
||||||
_codeField->setFocus();
|
_codeField->setFocus();
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PwdCheckWidget::recoverStarted(const MTPauth_PasswordRecovery &result) {
|
void PwdCheckWidget::recoverStarted(const MTPauth_PasswordRecovery &result) {
|
||||||
|
@ -210,7 +263,7 @@ void PwdCheckWidget::recoverStarted(const MTPauth_PasswordRecovery &result) {
|
||||||
updateDescriptionText();
|
updateDescriptionText();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PwdCheckWidget::recoverStartFail(const RPCError &error) {
|
void PwdCheckWidget::recoverStartFail(const RPCError &error) {
|
||||||
stopCheck();
|
stopCheck();
|
||||||
_pwdField->show();
|
_pwdField->show();
|
||||||
_pwdHint->show();
|
_pwdHint->show();
|
||||||
|
@ -219,13 +272,12 @@ bool PwdCheckWidget::recoverStartFail(const RPCError &error) {
|
||||||
updateDescriptionText();
|
updateDescriptionText();
|
||||||
update();
|
update();
|
||||||
hideError();
|
hideError();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PwdCheckWidget::onToRecover() {
|
void PwdCheckWidget::onToRecover() {
|
||||||
if (_hasRecovery) {
|
if (_hasRecovery) {
|
||||||
if (_sentRequest) {
|
if (_sentRequest) {
|
||||||
MTP::cancel(base::take(_sentRequest));
|
request(base::take(_sentRequest)).cancel();
|
||||||
}
|
}
|
||||||
hideError();
|
hideError();
|
||||||
_toRecover->hide();
|
_toRecover->hide();
|
||||||
|
@ -237,7 +289,13 @@ void PwdCheckWidget::onToRecover() {
|
||||||
_codeField->setFocus();
|
_codeField->setFocus();
|
||||||
updateDescriptionText();
|
updateDescriptionText();
|
||||||
if (_emailPattern.isEmpty()) {
|
if (_emailPattern.isEmpty()) {
|
||||||
MTP::send(MTPauth_RequestPasswordRecovery(), rpcDone(&PwdCheckWidget::recoverStarted), rpcFail(&PwdCheckWidget::recoverStartFail));
|
request(
|
||||||
|
MTPauth_RequestPasswordRecovery()
|
||||||
|
).done([=](const MTPauth_PasswordRecovery &result) {
|
||||||
|
recoverStarted(result);
|
||||||
|
}).fail([=](const RPCError &error) {
|
||||||
|
recoverStartFail(error);
|
||||||
|
}).send();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ui::show(Box<InformBox>(lang(lng_signin_no_email_forgot), [this] { showReset(); }));
|
Ui::show(Box<InformBox>(lang(lng_signin_no_email_forgot), [this] { showReset(); }));
|
||||||
|
@ -250,7 +308,7 @@ void PwdCheckWidget::onToPassword() {
|
||||||
|
|
||||||
void PwdCheckWidget::showReset() {
|
void PwdCheckWidget::showReset() {
|
||||||
if (_sentRequest) {
|
if (_sentRequest) {
|
||||||
MTP::cancel(base::take(_sentRequest));
|
request(base::take(_sentRequest)).cancel();
|
||||||
}
|
}
|
||||||
_toRecover->show();
|
_toRecover->show();
|
||||||
_toPassword->hide();
|
_toPassword->hide();
|
||||||
|
@ -285,10 +343,13 @@ void PwdCheckWidget::submit() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto send = crl::guard(this, [=] {
|
const auto send = crl::guard(this, [=] {
|
||||||
_sentRequest = MTP::send(
|
_sentRequest = request(
|
||||||
MTPauth_RecoverPassword(MTP_string(code)),
|
MTPauth_RecoverPassword(MTP_string(code))
|
||||||
rpcDone(&PwdCheckWidget::pwdSubmitDone, true),
|
).done([=](const MTPauth_Authorization &result) {
|
||||||
rpcFail(&PwdCheckWidget::codeSubmitFail));
|
pwdSubmitDone(true, result);
|
||||||
|
}).handleFloodErrors().fail([=](const RPCError &error) {
|
||||||
|
codeSubmitFail(error);
|
||||||
|
}).send();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (_notEmptyPassport) {
|
if (_notEmptyPassport) {
|
||||||
|
@ -310,13 +371,10 @@ void PwdCheckWidget::submit() {
|
||||||
hideError();
|
hideError();
|
||||||
|
|
||||||
const auto password = _pwdField->getLastText().toUtf8();
|
const auto password = _pwdField->getLastText().toUtf8();
|
||||||
const auto hash = Core::ComputeCloudPasswordHash(
|
_passwordHash = Core::ComputeCloudPasswordHash(
|
||||||
_algo,
|
_request.algo,
|
||||||
bytes::make_span(password));
|
bytes::make_span(password));
|
||||||
_sentRequest = MTP::send(
|
checkPasswordHash();
|
||||||
MTPauth_CheckPassword(MTP_bytes(hash)),
|
|
||||||
rpcDone(&PwdCheckWidget::pwdSubmitDone, false),
|
|
||||||
rpcFail(&PwdCheckWidget::pwdSubmitFail));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "intro/introwidget.h"
|
#include "intro/introwidget.h"
|
||||||
|
#include "mtproto/sender.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class InputField;
|
class InputField;
|
||||||
|
@ -18,7 +19,7 @@ class LinkButton;
|
||||||
|
|
||||||
namespace Intro {
|
namespace Intro {
|
||||||
|
|
||||||
class PwdCheckWidget : public Widget::Step {
|
class PwdCheckWidget : public Widget::Step, private MTP::Sender {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -45,16 +46,23 @@ private:
|
||||||
void updateControlsGeometry();
|
void updateControlsGeometry();
|
||||||
|
|
||||||
void pwdSubmitDone(bool recover, const MTPauth_Authorization &result);
|
void pwdSubmitDone(bool recover, const MTPauth_Authorization &result);
|
||||||
bool pwdSubmitFail(const RPCError &error);
|
void pwdSubmitFail(const RPCError &error);
|
||||||
bool codeSubmitFail(const RPCError &error);
|
void codeSubmitFail(const RPCError &error);
|
||||||
bool recoverStartFail(const RPCError &error);
|
void recoverStartFail(const RPCError &error);
|
||||||
|
|
||||||
void recoverStarted(const MTPauth_PasswordRecovery &result);
|
void recoverStarted(const MTPauth_PasswordRecovery &result);
|
||||||
|
|
||||||
void updateDescriptionText();
|
void updateDescriptionText();
|
||||||
void stopCheck();
|
void stopCheck();
|
||||||
|
void handleSrpIdInvalid();
|
||||||
|
void requestPasswordData();
|
||||||
|
void checkPasswordHash();
|
||||||
|
void passwordChecked();
|
||||||
|
void serverError();
|
||||||
|
|
||||||
Core::CloudPasswordAlgo _algo;
|
Core::CloudPasswordCheckRequest _request;
|
||||||
|
TimeMs _lastSrpIdInvalidTime = 0;
|
||||||
|
bytes::vector _passwordHash;
|
||||||
bool _hasRecovery = false;
|
bool _hasRecovery = false;
|
||||||
bool _notEmptyPassport = false;
|
bool _notEmptyPassport = false;
|
||||||
QString _hint, _emailPattern;
|
QString _hint, _emailPattern;
|
||||||
|
|
|
@ -73,7 +73,7 @@ public:
|
||||||
int codeLength = 5;
|
int codeLength = 5;
|
||||||
bool codeByTelegram = false;
|
bool codeByTelegram = false;
|
||||||
|
|
||||||
Core::CloudPasswordAlgo pwdAlgo;
|
Core::CloudPasswordCheckRequest pwdRequest;
|
||||||
bool hasRecovery = false;
|
bool hasRecovery = false;
|
||||||
QString pwdHint;
|
QString pwdHint;
|
||||||
bool pwdNotEmptyPassport = false;
|
bool pwdNotEmptyPassport = false;
|
||||||
|
|
|
@ -46,6 +46,10 @@ inline QString PassportCorrupted() {
|
||||||
return qsl("It seems your Telegram Passport data was corrupted.\n\nYou can reset your Telegram Passport and restart this authorization.");
|
return qsl("It seems your Telegram Passport data was corrupted.\n\nYou can reset your Telegram Passport and restart this authorization.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline QString PassportCorruptedChange() {
|
||||||
|
return qsl("It seems your Telegram Passport data was corrupted.\n\nYou can reset your Telegram Passport and change your cloud password.");
|
||||||
|
}
|
||||||
|
|
||||||
inline QString PassportCorruptedReset() {
|
inline QString PassportCorruptedReset() {
|
||||||
return qsl("Reset");
|
return qsl("Reset");
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,16 +61,20 @@ QString LogIdsVector(const QVector<MTPlong> &ids) {
|
||||||
return idsStr + "]";
|
return idsStr + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsGoodModExpFirst(const openssl::BigNum &modexp, const openssl::BigNum &prime) {
|
bool IsGoodModExpFirst(
|
||||||
auto diff = prime - modexp;
|
const openssl::BigNum &modexp,
|
||||||
|
const openssl::BigNum &prime) {
|
||||||
|
const auto diff = openssl::BigNum::Sub(prime, modexp);
|
||||||
if (modexp.failed() || prime.failed() || diff.failed()) {
|
if (modexp.failed() || prime.failed() || diff.failed()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
constexpr auto kMinDiffBitsCount = 2048 - 64;
|
constexpr auto kMinDiffBitsCount = 2048 - 64;
|
||||||
if (diff.isNegative() || diff.bitsSize() < kMinDiffBitsCount || modexp.bitsSize() < kMinDiffBitsCount) {
|
if (diff.isNegative()
|
||||||
|
|| diff.bitsSize() < kMinDiffBitsCount
|
||||||
|
|| modexp.bitsSize() < kMinDiffBitsCount
|
||||||
|
|| modexp.bytesSize() > kMaxModExpSize) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Assert(modexp.bytesSize() <= kMaxModExpSize);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,21 +198,22 @@ ModExpFirst CreateModExp(
|
||||||
|
|
||||||
BigNum prime(primeBytes);
|
BigNum prime(primeBytes);
|
||||||
auto result = ModExpFirst();
|
auto result = ModExpFirst();
|
||||||
constexpr auto kMaxModExpFirstTries = 5;
|
|
||||||
result.randomPower.resize(ModExpFirst::kRandomPowerSize);
|
result.randomPower.resize(ModExpFirst::kRandomPowerSize);
|
||||||
for (auto tries = 0; tries != kMaxModExpFirstTries; ++tries) {
|
while (true) {
|
||||||
bytes::set_random(result.randomPower);
|
bytes::set_random(result.randomPower);
|
||||||
for (auto i = 0; i != ModExpFirst::kRandomPowerSize; ++i) {
|
for (auto i = 0; i != ModExpFirst::kRandomPowerSize; ++i) {
|
||||||
result.randomPower[i] ^= randomSeed[i];
|
result.randomPower[i] ^= randomSeed[i];
|
||||||
}
|
}
|
||||||
auto modexp = BigNum::ModExp(BigNum(g), BigNum(result.randomPower), prime);
|
const auto modexp = BigNum::ModExp(
|
||||||
|
BigNum(g),
|
||||||
|
BigNum(result.randomPower),
|
||||||
|
prime);
|
||||||
if (IsGoodModExpFirst(modexp, prime)) {
|
if (IsGoodModExpFirst(modexp, prime)) {
|
||||||
result.modexp = modexp.getBytes();
|
result.modexp = modexp.getBytes();
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void wrapInvokeAfter(SecureRequest &to, const SecureRequest &from, const RequestMap &haveSent, int32 skipBeforeRequest = 0) {
|
void wrapInvokeAfter(SecureRequest &to, const SecureRequest &from, const RequestMap &haveSent, int32 skipBeforeRequest = 0) {
|
||||||
const auto afterId = *(mtpMsgId*)(from->after->data() + 4);
|
const auto afterId = *(mtpMsgId*)(from->after->data() + 4);
|
||||||
|
@ -3243,6 +3248,10 @@ bool IsPrimeAndGood(bytes::const_span primeBytes, int g) {
|
||||||
return internal::IsPrimeAndGood(primeBytes, g);
|
return internal::IsPrimeAndGood(primeBytes, g);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsGoodModExpFirst(const openssl::BigNum &modexp, const openssl::BigNum &prime) {
|
||||||
|
return internal::IsGoodModExpFirst(modexp, prime);
|
||||||
|
}
|
||||||
|
|
||||||
ModExpFirst CreateModExp(int g, bytes::const_span primeBytes, bytes::const_span randomSeed) {
|
ModExpFirst CreateModExp(int g, bytes::const_span primeBytes, bytes::const_span randomSeed) {
|
||||||
return internal::CreateModExp(g, primeBytes, randomSeed);
|
return internal::CreateModExp(g, primeBytes, randomSeed);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "mtproto/auth_key.h"
|
#include "mtproto/auth_key.h"
|
||||||
#include "mtproto/dc_options.h"
|
#include "mtproto/dc_options.h"
|
||||||
#include "mtproto/connection_abstract.h"
|
#include "mtproto/connection_abstract.h"
|
||||||
|
#include "base/openssl_help.h"
|
||||||
#include "base/timer.h"
|
#include "base/timer.h"
|
||||||
|
|
||||||
namespace MTP {
|
namespace MTP {
|
||||||
|
@ -23,6 +24,7 @@ struct ModExpFirst {
|
||||||
bytes::vector modexp;
|
bytes::vector modexp;
|
||||||
bytes::vector randomPower;
|
bytes::vector randomPower;
|
||||||
};
|
};
|
||||||
|
bool IsGoodModExpFirst(const openssl::BigNum &modexp, const openssl::BigNum &prime);
|
||||||
ModExpFirst CreateModExp(int g, bytes::const_span primeBytes, bytes::const_span randomSeed);
|
ModExpFirst CreateModExp(int g, bytes::const_span primeBytes, bytes::const_span randomSeed);
|
||||||
bytes::vector CreateAuthKey(bytes::const_span firstBytes, bytes::const_span randomBytes, bytes::const_span primeBytes);
|
bytes::vector CreateAuthKey(bytes::const_span firstBytes, bytes::const_span randomBytes, bytes::const_span primeBytes);
|
||||||
|
|
||||||
|
|
|
@ -321,7 +321,7 @@ QString FormController::privacyPolicyUrl() const {
|
||||||
|
|
||||||
bytes::vector FormController::passwordHashForAuth(
|
bytes::vector FormController::passwordHashForAuth(
|
||||||
bytes::const_span password) const {
|
bytes::const_span password) const {
|
||||||
return Core::ComputeCloudPasswordHash(_password.algo, password);
|
return Core::ComputeCloudPasswordHash(_password.request.algo, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto FormController::prepareFinalData() -> FinalData {
|
auto FormController::prepareFinalData() -> FinalData {
|
||||||
|
@ -442,8 +442,52 @@ std::vector<not_null<const Value*>> FormController::submitGetErrors() {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FormController::checkPasswordHash(
|
||||||
|
mtpRequestId &guard,
|
||||||
|
bytes::vector hash,
|
||||||
|
PasswordCheckCallback callback) {
|
||||||
|
_passwordCheckHash = std::move(hash);
|
||||||
|
_passwordCheckCallback = std::move(callback);
|
||||||
|
if (_password.request.id) {
|
||||||
|
passwordChecked();
|
||||||
|
} else {
|
||||||
|
requestPasswordData(guard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormController::passwordChecked() {
|
||||||
|
if (!_password.request || !_password.request.id) {
|
||||||
|
return passwordServerError();
|
||||||
|
}
|
||||||
|
const auto check = Core::ComputeCloudPasswordCheck(
|
||||||
|
_password.request,
|
||||||
|
_passwordCheckHash);
|
||||||
|
if (!check) {
|
||||||
|
return passwordServerError();
|
||||||
|
}
|
||||||
|
_password.request.id = 0;
|
||||||
|
_passwordCheckCallback(check);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormController::requestPasswordData(mtpRequestId &guard) {
|
||||||
|
if (!_passwordCheckCallback) {
|
||||||
|
return passwordServerError();
|
||||||
|
}
|
||||||
|
|
||||||
|
request(base::take(guard)).cancel();
|
||||||
|
guard = request(
|
||||||
|
MTPaccount_GetPassword()
|
||||||
|
).done([=, &guard](const MTPaccount_Password &result) {
|
||||||
|
guard = 0;
|
||||||
|
result.match([&](const MTPDaccount_password &data) {
|
||||||
|
_password.request = Core::ParseCloudPasswordCheckRequest(data);
|
||||||
|
passwordChecked();
|
||||||
|
});
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
void FormController::submitPassword(const QByteArray &password) {
|
void FormController::submitPassword(const QByteArray &password) {
|
||||||
Expects(_password.algo.has_value());
|
Expects(!!_password.request);
|
||||||
|
|
||||||
const auto submitSaved = !base::take(_savedPasswordValue).isEmpty();
|
const auto submitSaved = !base::take(_savedPasswordValue).isEmpty();
|
||||||
if (_passwordCheckRequestId) {
|
if (_passwordCheckRequestId) {
|
||||||
|
@ -452,16 +496,27 @@ void FormController::submitPassword(const QByteArray &password) {
|
||||||
_passwordError.fire(QString());
|
_passwordError.fire(QString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const auto callback = [=](const Core::CloudPasswordResult &check) {
|
||||||
|
submitPassword(check, password, submitSaved);
|
||||||
|
};
|
||||||
|
checkPasswordHash(
|
||||||
|
_passwordCheckRequestId,
|
||||||
|
passwordHashForAuth(bytes::make_span(password)),
|
||||||
|
callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormController::submitPassword(
|
||||||
|
const Core::CloudPasswordResult &check,
|
||||||
|
const QByteArray &password,
|
||||||
|
bool submitSaved) {
|
||||||
_passwordCheckRequestId = request(MTPaccount_GetPasswordSettings(
|
_passwordCheckRequestId = request(MTPaccount_GetPasswordSettings(
|
||||||
MTP_bytes(passwordHashForAuth(bytes::make_span(password)))
|
check.result
|
||||||
)).handleFloodErrors(
|
)).handleFloodErrors(
|
||||||
).done([=](const MTPaccount_PasswordSettings &result) {
|
).done([=](const MTPaccount_PasswordSettings &result) {
|
||||||
Expects(result.type() == mtpc_account_passwordSettings);
|
Expects(result.type() == mtpc_account_passwordSettings);
|
||||||
|
|
||||||
_passwordCheckRequestId = 0;
|
_passwordCheckRequestId = 0;
|
||||||
_savedPasswordValue = QByteArray();
|
_savedPasswordValue = QByteArray();
|
||||||
const auto hashForAuth = passwordHashForAuth(
|
|
||||||
bytes::make_span(password));
|
|
||||||
const auto &data = result.c_account_passwordSettings();
|
const auto &data = result.c_account_passwordSettings();
|
||||||
_password.confirmedEmail = qs(data.vemail);
|
_password.confirmedEmail = qs(data.vemail);
|
||||||
if (data.has_secure_settings()) {
|
if (data.has_secure_settings()) {
|
||||||
|
@ -483,7 +538,7 @@ void FormController::submitPassword(const QByteArray &password) {
|
||||||
settings.vsecure_secret_id.v);
|
settings.vsecure_secret_id.v);
|
||||||
if (!_secret.empty()) {
|
if (!_secret.empty()) {
|
||||||
auto saved = SavedCredentials();
|
auto saved = SavedCredentials();
|
||||||
saved.hashForAuth = hashForAuth;
|
saved.hashForAuth = base::take(_passwordCheckHash);
|
||||||
saved.hashForSecret = hashForSecret;
|
saved.hashForSecret = hashForSecret;
|
||||||
saved.secretId = _secretId;
|
saved.secretId = _secretId;
|
||||||
Auth().data().rememberPassportCredentials(
|
Auth().data().rememberPassportCredentials(
|
||||||
|
@ -499,13 +554,16 @@ void FormController::submitPassword(const QByteArray &password) {
|
||||||
}
|
}
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
_passwordCheckRequestId = 0;
|
_passwordCheckRequestId = 0;
|
||||||
if (submitSaved) {
|
if (error.type() == qstr("SRP_ID_INVALID")) {
|
||||||
|
handleSrpIdInvalid(_passwordCheckRequestId);
|
||||||
|
} else if (submitSaved) {
|
||||||
// Force reload and show form.
|
// Force reload and show form.
|
||||||
_password = PasswordSettings();
|
_password = PasswordSettings();
|
||||||
reloadPassword();
|
reloadPassword();
|
||||||
} else if (MTP::isFloodError(error)) {
|
} else if (MTP::isFloodError(error)) {
|
||||||
_passwordError.fire(lang(lng_flood_error));
|
_passwordError.fire(lang(lng_flood_error));
|
||||||
} else if (error.type() == qstr("PASSWORD_HASH_INVALID")) {
|
} else if (error.type() == qstr("PASSWORD_HASH_INVALID")
|
||||||
|
|| error.type() == qstr("SRP_PASSWORD_CHANGED")) {
|
||||||
_passwordError.fire(lang(lng_passport_password_wrong));
|
_passwordError.fire(lang(lng_passport_password_wrong));
|
||||||
} else {
|
} else {
|
||||||
_passwordError.fire_copy(error.type());
|
_passwordError.fire_copy(error.type());
|
||||||
|
@ -513,10 +571,40 @@ void FormController::submitPassword(const QByteArray &password) {
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FormController::handleSrpIdInvalid(mtpRequestId &guard) {
|
||||||
|
const auto now = getms(true);
|
||||||
|
if (_lastSrpIdInvalidTime > 0
|
||||||
|
&& now - _lastSrpIdInvalidTime < Core::kHandleSrpIdInvalidTimeout) {
|
||||||
|
_password.request.id = 0;
|
||||||
|
_passwordError.fire(Lang::Hard::ServerError());
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
_lastSrpIdInvalidTime = now;
|
||||||
|
requestPasswordData(guard);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormController::passwordServerError() {
|
||||||
|
_view->showCriticalError(Lang::Hard::ServerError());
|
||||||
|
}
|
||||||
|
|
||||||
void FormController::checkSavedPasswordSettings(
|
void FormController::checkSavedPasswordSettings(
|
||||||
const SavedCredentials &credentials) {
|
const SavedCredentials &credentials) {
|
||||||
|
const auto callback = [=](const Core::CloudPasswordResult &check) {
|
||||||
|
checkSavedPasswordSettings(check, credentials);
|
||||||
|
};
|
||||||
|
checkPasswordHash(
|
||||||
|
_passwordCheckRequestId,
|
||||||
|
credentials.hashForAuth,
|
||||||
|
callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormController::checkSavedPasswordSettings(
|
||||||
|
const Core::CloudPasswordResult &check,
|
||||||
|
const SavedCredentials &credentials) {
|
||||||
_passwordCheckRequestId = request(MTPaccount_GetPasswordSettings(
|
_passwordCheckRequestId = request(MTPaccount_GetPasswordSettings(
|
||||||
MTP_bytes(credentials.hashForAuth)
|
check.result
|
||||||
)).done([=](const MTPaccount_PasswordSettings &result) {
|
)).done([=](const MTPaccount_PasswordSettings &result) {
|
||||||
Expects(result.type() == mtpc_account_passwordSettings);
|
Expects(result.type() == mtpc_account_passwordSettings);
|
||||||
|
|
||||||
|
@ -546,8 +634,12 @@ void FormController::checkSavedPasswordSettings(
|
||||||
}
|
}
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
_passwordCheckRequestId = 0;
|
_passwordCheckRequestId = 0;
|
||||||
|
if (error.type() != qstr("SRP_ID_INVALID")
|
||||||
|
|| !handleSrpIdInvalid(_passwordCheckRequestId)) {
|
||||||
|
} else {
|
||||||
Auth().data().forgetPassportCredentials();
|
Auth().data().forgetPassportCredentials();
|
||||||
showForm();
|
showForm();
|
||||||
|
}
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -601,7 +693,7 @@ void FormController::cancelPassword() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_passwordRequestId = request(MTPaccount_UpdatePasswordSettings(
|
_passwordRequestId = request(MTPaccount_UpdatePasswordSettings(
|
||||||
MTP_bytes(QByteArray()),
|
MTP_inputCheckPasswordEmpty(),
|
||||||
MTP_account_passwordInputSettings(
|
MTP_account_passwordInputSettings(
|
||||||
MTP_flags(MTPDaccount_passwordInputSettings::Flag::f_email),
|
MTP_flags(MTPDaccount_passwordInputSettings::Flag::f_email),
|
||||||
MTP_passwordKdfAlgoUnknown(), // new_algo
|
MTP_passwordKdfAlgoUnknown(), // new_algo
|
||||||
|
@ -662,9 +754,23 @@ void FormController::suggestReset(bytes::vector password) {
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
_view->suggestReset([=] {
|
_view->suggestReset([=] {
|
||||||
|
const auto callback = [=](const Core::CloudPasswordResult &check) {
|
||||||
|
resetSecret(check, password);
|
||||||
|
};
|
||||||
|
checkPasswordHash(
|
||||||
|
_saveSecretRequestId,
|
||||||
|
passwordHashForAuth(bytes::make_span(password)),
|
||||||
|
callback);
|
||||||
|
_secretReady.fire({});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormController::resetSecret(
|
||||||
|
const Core::CloudPasswordResult &check,
|
||||||
|
const bytes::vector &password) {
|
||||||
using Flag = MTPDaccount_passwordInputSettings::Flag;
|
using Flag = MTPDaccount_passwordInputSettings::Flag;
|
||||||
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
|
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
|
||||||
MTP_bytes(passwordHashForAuth(password)),
|
check.result,
|
||||||
MTP_account_passwordInputSettings(
|
MTP_account_passwordInputSettings(
|
||||||
MTP_flags(Flag::f_new_secure_settings),
|
MTP_flags(Flag::f_new_secure_settings),
|
||||||
MTPPasswordKdfAlgo(), // new_algo
|
MTPPasswordKdfAlgo(), // new_algo
|
||||||
|
@ -680,10 +786,11 @@ void FormController::suggestReset(bytes::vector password) {
|
||||||
generateSecret(password);
|
generateSecret(password);
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
_saveSecretRequestId = 0;
|
_saveSecretRequestId = 0;
|
||||||
|
if (error.type() != qstr("SRP_ID_INVALID")
|
||||||
|
|| !handleSrpIdInvalid(_saveSecretRequestId)) {
|
||||||
formFail(error.type());
|
formFail(error.type());
|
||||||
|
}
|
||||||
}).send();
|
}).send();
|
||||||
_secretReady.fire({});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::decryptValues() {
|
void FormController::decryptValues() {
|
||||||
|
@ -1792,19 +1899,29 @@ void FormController::generateSecret(bytes::const_span password) {
|
||||||
auto secret = GenerateSecretBytes();
|
auto secret = GenerateSecretBytes();
|
||||||
|
|
||||||
auto saved = SavedCredentials();
|
auto saved = SavedCredentials();
|
||||||
saved.hashForAuth = passwordHashForAuth(password);
|
saved.hashForAuth = _passwordCheckHash;
|
||||||
saved.hashForSecret = Core::ComputeSecureSecretHash(
|
saved.hashForSecret = Core::ComputeSecureSecretHash(
|
||||||
_password.newSecureAlgo,
|
_password.newSecureAlgo,
|
||||||
password);
|
password);
|
||||||
saved.secretId = CountSecureSecretId(secret);
|
saved.secretId = CountSecureSecretId(secret);
|
||||||
|
|
||||||
auto encryptedSecret = EncryptSecureSecret(
|
const auto callback = [=](const Core::CloudPasswordResult &check) {
|
||||||
|
saveSecret(check, saved, secret);
|
||||||
|
};
|
||||||
|
checkPasswordHash(_saveSecretRequestId, saved.hashForAuth, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormController::saveSecret(
|
||||||
|
const Core::CloudPasswordResult &check,
|
||||||
|
const SavedCredentials &saved,
|
||||||
|
const bytes::vector &secret) {
|
||||||
|
const auto encryptedSecret = EncryptSecureSecret(
|
||||||
secret,
|
secret,
|
||||||
saved.hashForSecret);
|
saved.hashForSecret);
|
||||||
|
|
||||||
using Flag = MTPDaccount_passwordInputSettings::Flag;
|
using Flag = MTPDaccount_passwordInputSettings::Flag;
|
||||||
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
|
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
|
||||||
MTP_bytes(saved.hashForAuth),
|
check.result,
|
||||||
MTP_account_passwordInputSettings(
|
MTP_account_passwordInputSettings(
|
||||||
MTP_flags(Flag::f_new_secure_settings),
|
MTP_flags(Flag::f_new_secure_settings),
|
||||||
MTPPasswordKdfAlgo(), // new_algo
|
MTPPasswordKdfAlgo(), // new_algo
|
||||||
|
@ -1829,7 +1946,10 @@ void FormController::generateSecret(bytes::const_span password) {
|
||||||
}
|
}
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
_saveSecretRequestId = 0;
|
_saveSecretRequestId = 0;
|
||||||
|
if (error.type() != qstr("SRP_ID_INVALID")
|
||||||
|
|| !handleSrpIdInvalid(_saveSecretRequestId)) {
|
||||||
suggestRestart();
|
suggestRestart();
|
||||||
|
}
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2126,7 +2246,7 @@ void FormController::showForm() {
|
||||||
|| !_password.newSecureAlgo) {
|
|| !_password.newSecureAlgo) {
|
||||||
_view->showUpdateAppBox();
|
_view->showUpdateAppBox();
|
||||||
return;
|
return;
|
||||||
} else if (_password.algo) {
|
} else if (_password.request) {
|
||||||
if (!_savedPasswordValue.isEmpty()) {
|
if (!_savedPasswordValue.isEmpty()) {
|
||||||
submitPassword(base::duplicate(_savedPasswordValue));
|
submitPassword(base::duplicate(_savedPasswordValue));
|
||||||
} else if (const auto saved = Auth().data().passportCredentials()) {
|
} else if (const auto saved = Auth().data().passportCredentials()) {
|
||||||
|
@ -2144,11 +2264,9 @@ bool FormController::applyPassword(const MTPDaccount_password &result) {
|
||||||
settings.hint = qs(result.vhint);
|
settings.hint = qs(result.vhint);
|
||||||
settings.hasRecovery = result.is_has_recovery();
|
settings.hasRecovery = result.is_has_recovery();
|
||||||
settings.notEmptyPassport = result.is_has_secure_values();
|
settings.notEmptyPassport = result.is_has_secure_values();
|
||||||
settings.algo = result.has_current_algo()
|
settings.request = Core::ParseCloudPasswordCheckRequest(result);
|
||||||
? Core::ParseCloudPasswordAlgo(result.vcurrent_algo)
|
|
||||||
: Core::CloudPasswordAlgo();
|
|
||||||
settings.unknownAlgo = result.has_current_algo()
|
settings.unknownAlgo = result.has_current_algo()
|
||||||
&& !settings.algo;
|
&& !settings.request;
|
||||||
settings.unconfirmedPattern = result.has_email_unconfirmed_pattern()
|
settings.unconfirmedPattern = result.has_email_unconfirmed_pattern()
|
||||||
? qs(result.vemail_unconfirmed_pattern)
|
? qs(result.vemail_unconfirmed_pattern)
|
||||||
: QString();
|
: QString();
|
||||||
|
|
|
@ -196,7 +196,7 @@ struct Form {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PasswordSettings {
|
struct PasswordSettings {
|
||||||
Core::CloudPasswordAlgo algo;
|
Core::CloudPasswordCheckRequest request;
|
||||||
Core::CloudPasswordAlgo newAlgo;
|
Core::CloudPasswordAlgo newAlgo;
|
||||||
Core::SecureSecretAlgo newSecureAlgo;
|
Core::SecureSecretAlgo newSecureAlgo;
|
||||||
QString hint;
|
QString hint;
|
||||||
|
@ -207,9 +207,14 @@ struct PasswordSettings {
|
||||||
bool unknownAlgo = false;
|
bool unknownAlgo = false;
|
||||||
|
|
||||||
bool operator==(const PasswordSettings &other) const {
|
bool operator==(const PasswordSettings &other) const {
|
||||||
return (algo == other.algo)
|
return (request == other.request)
|
||||||
&& (newAlgo == other.newAlgo)
|
// newAlgo and newSecureAlgo are always different, because they have
|
||||||
&& (newSecureAlgo == other.newSecureAlgo)
|
// different random parts added on the client to the server salts.
|
||||||
|
// && (newAlgo == other.newAlgo)
|
||||||
|
// && (newSecureAlgo == other.newSecureAlgo)
|
||||||
|
&& ((!newAlgo && !other.newAlgo) || (newAlgo && other.newAlgo))
|
||||||
|
&& ((!newSecureAlgo && !other.newSecureAlgo)
|
||||||
|
|| (newSecureAlgo && other.newSecureAlgo))
|
||||||
&& (hint == other.hint)
|
&& (hint == other.hint)
|
||||||
&& (unconfirmedPattern == other.unconfirmedPattern)
|
&& (unconfirmedPattern == other.unconfirmedPattern)
|
||||||
&& (confirmedEmail == other.confirmedEmail)
|
&& (confirmedEmail == other.confirmedEmail)
|
||||||
|
@ -310,6 +315,9 @@ public:
|
||||||
~FormController();
|
~FormController();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
using PasswordCheckCallback = Fn<void(
|
||||||
|
const Core::CloudPasswordResult &check)>;
|
||||||
|
|
||||||
struct FinalData {
|
struct FinalData {
|
||||||
QVector<MTPSecureValueHash> hashes;
|
QVector<MTPSecureValueHash> hashes;
|
||||||
QByteArray credentials;
|
QByteArray credentials;
|
||||||
|
@ -340,11 +348,26 @@ private:
|
||||||
File &destination,
|
File &destination,
|
||||||
const std::vector<EditFile> &source) const;
|
const std::vector<EditFile> &source) const;
|
||||||
|
|
||||||
|
void submitPassword(
|
||||||
|
const Core::CloudPasswordResult &check,
|
||||||
|
const QByteArray &password,
|
||||||
|
bool submitSaved);
|
||||||
|
void checkPasswordHash(
|
||||||
|
mtpRequestId &guard,
|
||||||
|
bytes::vector hash,
|
||||||
|
PasswordCheckCallback callback);
|
||||||
|
bool handleSrpIdInvalid(mtpRequestId &guard);
|
||||||
|
void requestPasswordData(mtpRequestId &guard);
|
||||||
|
void passwordChecked();
|
||||||
|
void passwordServerError();
|
||||||
void passwordDone(const MTPaccount_Password &result);
|
void passwordDone(const MTPaccount_Password &result);
|
||||||
bool applyPassword(const MTPDaccount_password &settings);
|
bool applyPassword(const MTPDaccount_password &settings);
|
||||||
bool applyPassword(PasswordSettings &&settings);
|
bool applyPassword(PasswordSettings &&settings);
|
||||||
bytes::vector passwordHashForAuth(bytes::const_span password) const;
|
bytes::vector passwordHashForAuth(bytes::const_span password) const;
|
||||||
void checkSavedPasswordSettings(const SavedCredentials &credentials);
|
void checkSavedPasswordSettings(const SavedCredentials &credentials);
|
||||||
|
void checkSavedPasswordSettings(
|
||||||
|
const Core::CloudPasswordResult &check,
|
||||||
|
const SavedCredentials &credentials);
|
||||||
void validateSecureSecret(
|
void validateSecureSecret(
|
||||||
bytes::const_span encryptedSecret,
|
bytes::const_span encryptedSecret,
|
||||||
bytes::const_span passwordHashForSecret,
|
bytes::const_span passwordHashForSecret,
|
||||||
|
@ -361,6 +384,10 @@ private:
|
||||||
void fileLoadProgress(FileKey key, int offset);
|
void fileLoadProgress(FileKey key, int offset);
|
||||||
void fileLoadFail(FileKey key);
|
void fileLoadFail(FileKey key);
|
||||||
void generateSecret(bytes::const_span password);
|
void generateSecret(bytes::const_span password);
|
||||||
|
void saveSecret(
|
||||||
|
const Core::CloudPasswordResult &check,
|
||||||
|
const SavedCredentials &saved,
|
||||||
|
const bytes::vector &secret);
|
||||||
|
|
||||||
void subscribeToUploader();
|
void subscribeToUploader();
|
||||||
void encryptFile(
|
void encryptFile(
|
||||||
|
@ -410,6 +437,9 @@ private:
|
||||||
FinalData prepareFinalData();
|
FinalData prepareFinalData();
|
||||||
|
|
||||||
void suggestReset(bytes::vector password);
|
void suggestReset(bytes::vector password);
|
||||||
|
void resetSecret(
|
||||||
|
const Core::CloudPasswordResult &check,
|
||||||
|
const bytes::vector &password);
|
||||||
void suggestRestart();
|
void suggestRestart();
|
||||||
void cancelAbort();
|
void cancelAbort();
|
||||||
void shortPollEmailConfirmation();
|
void shortPollEmailConfirmation();
|
||||||
|
@ -423,6 +453,9 @@ private:
|
||||||
mtpRequestId _passwordCheckRequestId = 0;
|
mtpRequestId _passwordCheckRequestId = 0;
|
||||||
|
|
||||||
PasswordSettings _password;
|
PasswordSettings _password;
|
||||||
|
TimeMs _lastSrpIdInvalidTime = 0;
|
||||||
|
bytes::vector _passwordCheckHash;
|
||||||
|
PasswordCheckCallback _passwordCheckCallback;
|
||||||
QByteArray _savedPasswordValue;
|
QByteArray _savedPasswordValue;
|
||||||
Form _form;
|
Form _form;
|
||||||
bool _cancelled = false;
|
bool _cancelled = false;
|
||||||
|
|
|
@ -472,7 +472,7 @@ void PanelController::setupPassword() {
|
||||||
|| !settings.newSecureAlgo) {
|
|| !settings.newSecureAlgo) {
|
||||||
showUpdateAppBox();
|
showUpdateAppBox();
|
||||||
return;
|
return;
|
||||||
} else if (settings.algo) {
|
} else if (settings.request) {
|
||||||
showAskPassword();
|
showAskPassword();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -481,7 +481,7 @@ void PanelController::setupPassword() {
|
||||||
const auto notEmptyPassport = false;
|
const auto notEmptyPassport = false;
|
||||||
const auto hint = QString();
|
const auto hint = QString();
|
||||||
auto box = show(Box<PasscodeBox>(
|
auto box = show(Box<PasscodeBox>(
|
||||||
Core::CloudPasswordAlgo(), // current algo
|
Core::CloudPasswordCheckRequest(), // current
|
||||||
settings.newAlgo,
|
settings.newAlgo,
|
||||||
hasRecovery,
|
hasRecovery,
|
||||||
notEmptyPassport,
|
notEmptyPassport,
|
||||||
|
|
|
@ -93,7 +93,7 @@ void CloudPasswordState::onEdit() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto box = Ui::show(Box<PasscodeBox>(
|
auto box = Ui::show(Box<PasscodeBox>(
|
||||||
_curPasswordAlgo,
|
_curPasswordRequest,
|
||||||
_newPasswordAlgo,
|
_newPasswordAlgo,
|
||||||
_hasPasswordRecovery,
|
_hasPasswordRecovery,
|
||||||
_notEmptyPassport,
|
_notEmptyPassport,
|
||||||
|
@ -122,12 +122,12 @@ void CloudPasswordState::onTurnOff() {
|
||||||
callback));
|
callback));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!_curPasswordAlgo) {
|
if (!_curPasswordRequest) {
|
||||||
_turnOff->hide();
|
_turnOff->hide();
|
||||||
|
|
||||||
MTP::send(
|
MTP::send(
|
||||||
MTPaccount_UpdatePasswordSettings(
|
MTPaccount_UpdatePasswordSettings(
|
||||||
MTP_bytes(QByteArray()),
|
MTP_inputCheckPasswordEmpty(),
|
||||||
MTP_account_passwordInputSettings(
|
MTP_account_passwordInputSettings(
|
||||||
MTP_flags(
|
MTP_flags(
|
||||||
MTPDaccount_passwordInputSettings::Flag::f_email),
|
MTPDaccount_passwordInputSettings::Flag::f_email),
|
||||||
|
@ -140,7 +140,7 @@ void CloudPasswordState::onTurnOff() {
|
||||||
rpcFail(&CloudPasswordState::offPasswordFail));
|
rpcFail(&CloudPasswordState::offPasswordFail));
|
||||||
} else {
|
} else {
|
||||||
auto box = Ui::show(Box<PasscodeBox>(
|
auto box = Ui::show(Box<PasscodeBox>(
|
||||||
_curPasswordAlgo,
|
_curPasswordRequest,
|
||||||
_newPasswordAlgo,
|
_newPasswordAlgo,
|
||||||
_hasPasswordRecovery,
|
_hasPasswordRecovery,
|
||||||
_notEmptyPassport,
|
_notEmptyPassport,
|
||||||
|
@ -177,10 +177,8 @@ void CloudPasswordState::getPasswordDone(const MTPaccount_Password &result) {
|
||||||
_waitingConfirm = QString();
|
_waitingConfirm = QString();
|
||||||
|
|
||||||
const auto &d = result.c_account_password();
|
const auto &d = result.c_account_password();
|
||||||
_curPasswordAlgo = d.has_current_algo()
|
_curPasswordRequest = Core::ParseCloudPasswordCheckRequest(d);
|
||||||
? Core::ParseCloudPasswordAlgo(d.vcurrent_algo)
|
_unknownPasswordAlgo = d.has_current_algo() && !_curPasswordRequest;
|
||||||
: Core::CloudPasswordAlgo();
|
|
||||||
_unknownPasswordAlgo = d.has_current_algo() && !_curPasswordAlgo;
|
|
||||||
_hasPasswordRecovery = d.is_has_recovery();
|
_hasPasswordRecovery = d.is_has_recovery();
|
||||||
_notEmptyPassport = d.is_has_secure_values();
|
_notEmptyPassport = d.is_has_secure_values();
|
||||||
_curPasswordHint = qs(d.vhint);
|
_curPasswordHint = qs(d.vhint);
|
||||||
|
@ -205,7 +203,7 @@ void CloudPasswordState::getPasswordDone(const MTPaccount_Password &result) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CloudPasswordState::hasCloudPassword() const {
|
bool CloudPasswordState::hasCloudPassword() const {
|
||||||
return (_curPasswordAlgo || _unknownPasswordAlgo);
|
return (_curPasswordRequest || _unknownPasswordAlgo);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CloudPasswordState::getPasswordFail(const RPCError &error) {
|
bool CloudPasswordState::getPasswordFail(const RPCError &error) {
|
||||||
|
|
|
@ -66,7 +66,7 @@ private:
|
||||||
object_ptr<Ui::LinkButton> _turnOff;
|
object_ptr<Ui::LinkButton> _turnOff;
|
||||||
|
|
||||||
QString _waitingConfirm;
|
QString _waitingConfirm;
|
||||||
Core::CloudPasswordAlgo _curPasswordAlgo;
|
Core::CloudPasswordCheckRequest _curPasswordRequest;
|
||||||
bool _unknownPasswordAlgo = false;
|
bool _unknownPasswordAlgo = false;
|
||||||
bool _hasPasswordRecovery = false;
|
bool _hasPasswordRecovery = false;
|
||||||
bool _notEmptyPassport = false;
|
bool _notEmptyPassport = false;
|
||||||
|
|
Loading…
Reference in New Issue