Remember passport credentials for 30 minutes.

This commit is contained in:
John Preston 2018-07-10 19:41:11 +03:00
parent 9929bfb281
commit e3e8d083ea
15 changed files with 342 additions and 138 deletions

View File

@ -54,6 +54,14 @@ PasscodeBox::PasscodeBox(
if (!hint.isEmpty()) _hintText.setText(st::passcodeTextStyle, lng_signin_hint(lt_password_hint, hint));
}
rpl::producer<QByteArray> PasscodeBox::newPasswordSet() const {
return _newPasswordSet.events();
}
rpl::producer<> PasscodeBox::passwordReloadNeeded() const {
return _passwordReloadNeeded.events();
}
void PasscodeBox::prepare() {
addButton(langFactory(_turningOff ? lng_passcode_remove_button : lng_settings_save), [=] { save(); });
addButton(langFactory(lng_cancel), [=] { closeBox(); });
@ -197,9 +205,9 @@ void PasscodeBox::setInnerFocus() {
}
}
void PasscodeBox::setPasswordDone() {
void PasscodeBox::setPasswordDone(const QByteArray &newPasswordBytes) {
_setRequest = 0;
emit reloadPassword();
_newPasswordSet.fire_copy(newPasswordBytes);
auto text = lang(_reenterPasscode->isHidden() ? lng_cloud_password_removed : (_oldPasscode->isHidden() ? lng_cloud_password_was_set : lng_cloud_password_updated));
getDelegate()->show(Box<InformBox>(text), LayerOption::CloseOther);
}
@ -236,7 +244,7 @@ bool PasscodeBox::setPasswordFail(const RPCError &error) {
QString err = error.type();
if (err == qstr("PASSWORD_HASH_INVALID")) {
if (_oldPasscode->isHidden()) {
emit reloadPassword();
_passwordReloadNeeded.fire({});
closeBox();
} else {
badOldPasscode();
@ -247,20 +255,36 @@ bool PasscodeBox::setPasswordFail(const RPCError &error) {
_newError = lang(lng_cloud_password_bad);
update();
} else if (err == qstr("NEW_SALT_INVALID")) {
emit reloadPassword();
_passwordReloadNeeded.fire({});
closeBox();
} else if (err == qstr("EMAIL_INVALID")) {
_emailError = lang(lng_cloud_password_bad_email);
_recoverEmail->setFocus();
_recoverEmail->showError();
update();
} else if (err == qstr("EMAIL_UNCONFIRMED")) {
}
return true;
}
bool PasscodeBox::setPasswordFail(
const QByteArray &newPasswordBytes,
const RPCError &error) {
if (MTP::isFloodError(error)) {
return setPasswordFail(error);
} else if (MTP::isDefaultHandledError(error)) {
return setPasswordFail(error);
} else if (error.type() == qstr("EMAIL_UNCONFIRMED")) {
closeReplacedBy();
_setRequest = 0;
_newPasswordSet.fire_copy(newPasswordBytes);
getDelegate()->show(
Box<InformBox>(lang(lng_cloud_password_almost)),
LayerOption::CloseOther);
emit reloadPassword();
return true;
} else {
return setPasswordFail(error);
}
return true;
}
void PasscodeBox::save(bool force) {
@ -386,14 +410,15 @@ void PasscodeBox::sendClearCloudPassword(const QString &oldPassword) {
MTPbytes(), // new_secure_secret
MTPlong()) // new_secure_secret_id
)).done([=](const MTPBool &result) {
setPasswordDone();
setPasswordDone({});
}).fail([=](const RPCError &error) {
setPasswordFail(error);
setPasswordFail({}, error);
}).send();
}
void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
const auto newPasswordData = (_newSalt + newPassword.toUtf8() + _newSalt);
const auto newPasswordBytes = newPassword.toUtf8();
const auto newPasswordData = (_newSalt + newPasswordBytes + _newSalt);
auto newPasswordHash = QByteArray(32, Qt::Uninitialized);
hashSha256(newPasswordData.constData(), newPasswordData.size(), newPasswordHash.data());
const auto oldPasswordData = QByteArray();
@ -416,9 +441,9 @@ void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
MTPbytes(), // new_secure_secret
MTPlong()) // new_secure_secret_id
)).done([=](const MTPBool &result) {
setPasswordDone();
setPasswordDone(newPasswordBytes);
}).fail([=](const RPCError &error) {
setPasswordFail(error);
setPasswordFail(newPasswordBytes, error);
}).send();
}
@ -443,9 +468,10 @@ void PasscodeBox::changeCloudPassword(
return;
}
const auto secret = Passport::DecryptSecureSecret(
bytes::make_span(data.vsecure_salt.v),
bytes::make_span(data.vsecure_secret.v),
bytes::make_span(passwordUtf));
Passport::CountPasswordHashForSecret(
bytes::make_span(data.vsecure_salt.v),
bytes::make_span(passwordUtf)));
if (secret.empty()) {
LOG(("API Error: Failed to decrypt secure secret."));
suggestSecretReset(oldPasswordHash, newPassword);
@ -505,8 +531,8 @@ void PasscodeBox::sendChangeCloudPassword(
const QByteArray &oldPasswordHash,
const QString &newPassword,
const QByteArray &secureSecret) {
const auto passwordUtf = newPassword.toUtf8();
const auto newPasswordData = (_newSalt + passwordUtf + _newSalt);
const auto newPasswordBytes = newPassword.toUtf8();
const auto newPasswordData = (_newSalt + newPasswordBytes + _newSalt);
auto newPasswordHash = QByteArray(32, Qt::Uninitialized);
hashSha256(newPasswordData.constData(), newPasswordData.size(), newPasswordHash.data());
const auto hint = _passwordHint->getLastText();
@ -522,9 +548,10 @@ void PasscodeBox::sendChangeCloudPassword(
newSecureSecretId = Passport::CountSecureSecretId(
bytes::make_span(secureSecret));
newSecureSecret = Passport::EncryptSecureSecret(
bytes::make_span(_newSecureSecretSalt),
bytes::make_span(secureSecret),
bytes::make_span(passwordUtf));
Passport::CountPasswordHashForSecret(
bytes::make_span(_newSecureSecretSalt),
bytes::make_span(newPasswordBytes)));
}
_setRequest = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(oldPasswordHash),
@ -538,9 +565,9 @@ void PasscodeBox::sendChangeCloudPassword(
MTP_bytes(newSecureSecret),
MTP_long(newSecureSecretId))
)).done([=](const MTPBool &result) {
setPasswordDone();
setPasswordDone(newPasswordBytes);
}).fail([=](const RPCError &error) {
setPasswordFail(error);
setPasswordFail(newPasswordBytes, error);
}).send();
}
@ -603,8 +630,17 @@ void PasscodeBox::recover() {
const auto box = getDelegate()->show(Box<RecoverBox>(
_pattern,
_notEmptyPassport));
connect(box, &RecoverBox::reloadPassword, this, &PasscodeBox::reloadPassword);
connect(box, &RecoverBox::recoveryExpired, this, &PasscodeBox::recoverExpired);
box->passwordCleared(
) | rpl::map([] {
return QByteArray();
}) | rpl::start_to_stream(_newPasswordSet, lifetime());
box->recoveryExpired(
) | rpl::start_with_next([=] {
recoverExpired();
}, lifetime());
_replacedBy = box;
}
@ -630,6 +666,14 @@ RecoverBox::RecoverBox(
, _recoverCode(this, st::defaultInputField, langFactory(lng_signin_code)) {
}
rpl::producer<> RecoverBox::passwordCleared() const {
return _passwordCleared.events();
}
rpl::producer<> RecoverBox::recoveryExpired() const {
return _recoveryExpired.events();
}
void RecoverBox::prepare() {
setTitle(langFactory(lng_signin_recover_title));
@ -707,10 +751,12 @@ void RecoverBox::codeChanged() {
update();
}
void RecoverBox::codeSubmitDone(bool recover, const MTPauth_Authorization &result) {
void RecoverBox::codeSubmitDone(
bool recover,
const MTPauth_Authorization &result) {
_submitRequest = 0;
emit reloadPassword();
_passwordCleared.fire({});
getDelegate()->show(
Box<InformBox>(lang(lng_cloud_password_removed)),
LayerOption::CloseOther);
@ -730,7 +776,7 @@ bool RecoverBox::codeSubmitFail(const RPCError &error) {
const QString &err = error.type();
if (err == qstr("PASSWORD_EMPTY")) {
emit reloadPassword();
_passwordCleared.fire({});
getDelegate()->show(
Box<InformBox>(lang(lng_cloud_password_removed)),
LayerOption::CloseOther);
@ -739,7 +785,7 @@ bool RecoverBox::codeSubmitFail(const RPCError &error) {
closeBox();
return true;
} else if (err == qstr("PASSWORD_RECOVERY_EXPIRED")) {
emit recoveryExpired();
_recoveryExpired.fire({});
closeBox();
return true;
} else if (err == qstr("CODE_INVALID")) {

View File

@ -17,8 +17,6 @@ class LinkButton;
} // namespace Ui
class PasscodeBox : public BoxContent, private MTP::Sender {
Q_OBJECT
public:
PasscodeBox(QWidget*, bool turningOff);
PasscodeBox(
@ -31,8 +29,8 @@ public:
const QByteArray &newSecureSecretSalt,
bool turningOff = false);
signals:
void reloadPassword();
rpl::producer<QByteArray> newPasswordSet() const;
rpl::producer<> passwordReloadNeeded() const;
protected:
void prepare() override;
@ -52,8 +50,11 @@ private:
void recoverByEmail();
void recoverExpired();
void setPasswordDone();
void setPasswordDone(const QByteArray &newPasswordBytes);
bool setPasswordFail(const RPCError &error);
bool setPasswordFail(
const QByteArray &newPasswordBytes,
const RPCError &error);
void recoverStarted(const MTPauth_PasswordRecovery &result);
bool recoverStartFail(const RPCError &error);
@ -101,17 +102,20 @@ private:
QString _oldError, _newError, _emailError;
rpl::event_stream<QByteArray> _newPasswordSet;
rpl::event_stream<> _passwordReloadNeeded;
};
class RecoverBox : public BoxContent, public RPCSender {
Q_OBJECT
public:
RecoverBox(QWidget*, const QString &pattern, bool notEmptyPassport);
signals:
void reloadPassword();
void recoveryExpired();
rpl::producer<> passwordCleared() const;
rpl::producer<> recoveryExpired() const;
//void reloadPassword();
//void recoveryExpired();
protected:
void prepare() override;
@ -135,4 +139,7 @@ private:
QString _error;
rpl::event_stream<> _passwordCleared;
rpl::event_stream<> _recoveryExpired;
};

View File

@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "inline_bots/inline_bot_layout_item.h"
#include "storage/localstorage.h"
#include "boxes/abstract_box.h"
#include "passport/passport_form_controller.h"
#include "data/data_media_types.h"
#include "data/data_feed.h"
#include "data/data_photo.h"
@ -156,6 +157,30 @@ void Session::stopExport() {
_export = nullptr;
}
const Passport::SavedCredentials *Session::passportCredentials() const {
return _passportCredentials ? &_passportCredentials->first : nullptr;
}
void Session::rememberPassportCredentials(
Passport::SavedCredentials data,
TimeMs rememberFor) {
Expects(rememberFor > 0);
static auto generation = 0;
_passportCredentials = std::make_unique<CredentialsWithGeneration>(
std::move(data),
++generation);
App::CallDelayed(rememberFor, _session, [=, check = generation] {
if (_passportCredentials && _passportCredentials->second == check) {
forgetPassportCredentials();
}
});
}
void Session::forgetPassportCredentials() {
_passportCredentials = nullptr;
}
void Session::setupContactViewsViewer() {
Notify::PeerUpdateViewer(
Notify::PeerUpdate::Flag::UserIsContact

View File

@ -35,6 +35,10 @@ class PanelController;
} // namespace View
} // namespace Export
namespace Passport {
struct SavedCredentials;
} // namespace Passport
namespace Data {
class Feed;
@ -60,6 +64,12 @@ public:
void stopExportWithConfirmation(FnMut<void()> callback);
void stopExport();
const Passport::SavedCredentials *passportCredentials() const;
void rememberPassportCredentials(
Passport::SavedCredentials data,
TimeMs rememberFor);
void forgetPassportCredentials();
[[nodiscard]] base::Variable<bool> &contactsLoaded() {
return _contactsLoaded;
}
@ -610,6 +620,11 @@ private:
MessageIdsList _mimeForwardIds;
using CredentialsWithGeneration = std::pair<
const Passport::SavedCredentials,
int>;
std::unique_ptr<CredentialsWithGeneration> _passportCredentials;
rpl::lifetime _lifetime;
};

View File

@ -1096,11 +1096,6 @@ void ApiWrap::appendChatsSlice(
auto filtered = ranges::view::all(
info.list
) | ranges::view::filter([&](const Data::DialogInfo &info) {
#ifdef _DEBUG
return (info.name == "Anta");
#else
#error "test"
#endif
return (types & SettingsFromDialogsType(info.type)) != 0;
});
auto &list = to.info.list;

View File

@ -19,10 +19,7 @@ namespace Export {
namespace Output {
namespace {
#ifndef _DEBUG
#error test
#endif
constexpr auto kMessagesInFile = 50;
constexpr auto kMessagesInFile = 1000;
constexpr auto kPersonalUserpicSize = 90;
constexpr auto kEntryUserpicSize = 48;
constexpr auto kServiceMessagePhotoSize = 60;

View File

@ -90,15 +90,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_history.h"
#include "styles/style_boxes.h"
#ifdef _DEBUG
#include "export/output/export_output_html.h"
#include "export/output/export_output_stats.h"
#include "export/view/export_view_panel_controller.h"
#include "platform/platform_specific.h"
#else
#error "test"
#endif
namespace {
bool IsForceLogoutNotification(const MTPDupdateServiceNotification &data) {
@ -261,10 +252,6 @@ MainWidget::MainWidget(
Messenger::Instance().mtp()->setUpdatesHandler(rpcDone(&MainWidget::updateReceived));
Messenger::Instance().mtp()->setGlobalFailHandler(rpcFail(&MainWidget::updateFail));
//Export::Output::HtmlWriter writer;
//writer.produceTestExample(psDownloadPath(), Export::View::PrepareEnvironment());
//crl::on_main([] { App::quit(); });
_ptsWaiter.setRequesting(true);
updateScrollColors();
setupConnectingWidget();

View File

@ -16,6 +16,7 @@ namespace {
constexpr auto kAesKeyLength = 32;
constexpr auto kAesIvLength = 16;
constexpr auto kSecretSize = 32;
constexpr auto kAesParamsHashSize = 64;
constexpr auto kMinPadding = 32;
constexpr auto kMaxPadding = 255;
constexpr auto kAlignTo = 16;
@ -27,18 +28,21 @@ struct AesParams {
bytes::vector iv;
};
AesParams PrepareAesParams(bytes::const_span bytesForEncryptionKey) {
const auto hash = openssl::Sha512(bytesForEncryptionKey);
const auto view = gsl::make_span(hash);
AesParams PrepareAesParamsWithHash(bytes::const_span hashForEncryptionKey) {
Expects(hashForEncryptionKey.size() == kAesParamsHashSize);
auto result = AesParams();
result.key = bytes::make_vector(
view.subspan(0, kAesKeyLength));
hashForEncryptionKey.subspan(0, kAesKeyLength));
result.iv = bytes::make_vector(
view.subspan(kAesKeyLength, kAesIvLength));
hashForEncryptionKey.subspan(kAesKeyLength, kAesIvLength));
return result;
}
AesParams PrepareAesParams(bytes::const_span bytesForEncryptionKey) {
return PrepareAesParamsWithHash(openssl::Sha512(bytesForEncryptionKey));
}
bytes::vector EncryptOrDecrypt(
bytes::const_span initial,
AesParams &&params,
@ -112,9 +116,9 @@ bytes::vector GenerateSecretBytes() {
return result;
}
bytes::vector DecryptSecretBytes(
bytes::vector DecryptSecretBytesWithHash(
bytes::const_span encryptedSecret,
bytes::const_span bytesForEncryptionKey) {
bytes::const_span hashForEncryptionKey) {
if (encryptedSecret.empty()) {
return {};
} else if (encryptedSecret.size() != kSecretSize) {
@ -122,7 +126,7 @@ bytes::vector DecryptSecretBytes(
).arg(encryptedSecret.size()));
return {};
}
auto params = PrepareAesParams(bytesForEncryptionKey);
auto params = PrepareAesParamsWithHash(hashForEncryptionKey);
auto result = Decrypt(encryptedSecret, std::move(params));
if (!CheckSecretBytes(result)) {
LOG(("API Error: Bad secret bytes."));
@ -131,6 +135,24 @@ bytes::vector DecryptSecretBytes(
return result;
}
bytes::vector DecryptSecretBytes(
bytes::const_span encryptedSecret,
bytes::const_span bytesForEncryptionKey) {
return DecryptSecretBytesWithHash(
encryptedSecret,
openssl::Sha512(bytesForEncryptionKey));
}
bytes::vector EncryptSecretBytesWithHash(
bytes::const_span secret,
bytes::const_span hashForEncryptionKey) {
Expects(secret.size() == kSecretSize);
Expects(CheckSecretBytes(secret) == true);
auto params = PrepareAesParamsWithHash(hashForEncryptionKey);
return Encrypt(secret, std::move(params));
}
bytes::vector EncryptSecretBytes(
bytes::const_span secret,
bytes::const_span bytesForEncryptionKey) {
@ -141,34 +163,31 @@ bytes::vector EncryptSecretBytes(
return Encrypt(secret, std::move(params));
}
bytes::vector DecryptSecureSecret(
bytes::vector CountPasswordHashForSecret(
bytes::const_span salt,
bytes::const_span encryptedSecret,
bytes::const_span password) {
Expects(!salt.empty());
Expects(!encryptedSecret.empty());
Expects(!password.empty());
const auto bytesForEncryptionKey = bytes::concatenate(
return openssl::Sha512(bytes::concatenate(
salt,
password,
salt);
return DecryptSecretBytes(encryptedSecret, bytesForEncryptionKey);
salt));
}
bytes::vector DecryptSecureSecret(
bytes::const_span encryptedSecret,
bytes::const_span passwordHashForSecret) {
Expects(!encryptedSecret.empty());
return DecryptSecretBytesWithHash(
encryptedSecret,
passwordHashForSecret);
}
bytes::vector EncryptSecureSecret(
bytes::const_span salt,
bytes::const_span secret,
bytes::const_span password) {
Expects(!salt.empty());
bytes::const_span passwordHashForSecret) {
Expects(secret.size() == kSecretSize);
Expects(!password.empty());
const auto bytesForEncryptionKey = bytes::concatenate(
salt,
password,
salt);
return EncryptSecretBytes(secret, bytesForEncryptionKey);
return EncryptSecretBytesWithHash(secret, passwordHashForSecret);
}
bytes::vector SerializeData(const std::map<QString, QString> &data) {

View File

@ -11,14 +11,15 @@ namespace Passport {
bytes::vector GenerateSecretBytes();
bytes::vector CountPasswordHashForSecret(
bytes::const_span salt,
bytes::const_span password);
bytes::vector EncryptSecureSecret(
bytes::const_span salt,
bytes::const_span secret,
bytes::const_span password);
bytes::const_span passwordHashForSecret);
bytes::vector DecryptSecureSecret(
bytes::const_span salt,
bytes::const_span encryptedSecret,
bytes::const_span password);
bytes::const_span passwordHashForSecret);
bytes::vector SerializeData(const std::map<QString, QString> &data);
std::map<QString, QString> DeserializeData(bytes::const_span bytes);

View File

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_hardcoded.h"
#include "base/openssl_help.h"
#include "base/qthelp_url.h"
#include "data/data_session.h"
#include "mainwindow.h"
#include "window/window_controller.h"
#include "core/click_handler_types.h"
@ -30,6 +31,7 @@ namespace {
constexpr auto kDocumentScansLimit = 20;
constexpr auto kShortPollTimeout = TimeMs(3000);
constexpr auto kRememberCredentialsDelay = TimeMs(1800 * 1000);
bool ForwardServiceErrorRequired(const QString &error) {
return (error == qstr("BOT_INVALID"))
@ -436,32 +438,54 @@ std::vector<not_null<const Value*>> FormController::submitGetErrors() {
return {};
}
void FormController::submitPassword(const QString &password) {
void FormController::submitPassword(const QByteArray &password) {
Expects(!_password.salt.empty());
const auto submitSaved = !base::take(_savedPasswordValue).isEmpty();
if (_passwordCheckRequestId) {
return;
} else if (password.isEmpty()) {
_passwordError.fire(QString());
return;
}
const auto passwordBytes = password.toUtf8();
_passwordCheckRequestId = request(MTPaccount_GetPasswordSettings(
MTP_bytes(passwordHashForAuth(bytes::make_span(passwordBytes)))
MTP_bytes(passwordHashForAuth(bytes::make_span(password)))
)).handleFloodErrors(
).done([=](const MTPaccount_PasswordSettings &result) {
Expects(result.type() == mtpc_account_passwordSettings);
_passwordCheckRequestId = 0;
_savedPasswordValue = QByteArray();
const auto &data = result.c_account_passwordSettings();
const auto hashForAuth = passwordHashForAuth(
bytes::make_span(password));
const auto hashForSecret = (data.vsecure_salt.v.isEmpty()
? bytes::vector()
: CountPasswordHashForSecret(
bytes::make_span(data.vsecure_salt.v),
bytes::make_span(password)));
_password.confirmedEmail = qs(data.vemail);
validateSecureSecret(
bytes::make_span(data.vsecure_salt.v),
bytes::make_span(data.vsecure_secret.v),
bytes::make_span(passwordBytes),
hashForSecret,
bytes::make_span(password),
data.vsecure_secret_id.v);
if (!_secret.empty()) {
auto saved = SavedCredentials();
saved.hashForAuth = hashForAuth;
saved.hashForSecret = hashForSecret;
saved.secretId = _secretId;
Auth().data().rememberPassportCredentials(
std::move(saved),
kRememberCredentialsDelay);
}
}).fail([=](const RPCError &error) {
_passwordCheckRequestId = 0;
if (MTP::isFloodError(error)) {
if (submitSaved) {
// Force reload and show form.
_password = PasswordSettings();
reloadPassword();
} else if (MTP::isFloodError(error)) {
_passwordError.fire(lang(lng_flood_error));
} else if (error.type() == qstr("PASSWORD_HASH_INVALID")) {
_passwordError.fire(lang(lng_passport_password_wrong));
@ -471,6 +495,35 @@ void FormController::submitPassword(const QString &password) {
}).send();
}
void FormController::checkSavedPasswordSettings(
const SavedCredentials &credentials) {
_passwordCheckRequestId = request(MTPaccount_GetPasswordSettings(
MTP_bytes(credentials.hashForAuth)
)).done([=](const MTPaccount_PasswordSettings &result) {
Expects(result.type() == mtpc_account_passwordSettings);
_passwordCheckRequestId = 0;
const auto &data = result.c_account_passwordSettings();
if (!data.vsecure_secret.v.isEmpty()
&& data.vsecure_secret_id.v == credentials.secretId) {
_password.confirmedEmail = qs(data.vemail);
validateSecureSecret(
bytes::make_span(data.vsecure_secret.v),
credentials.hashForSecret,
{},
data.vsecure_secret_id.v);
}
if (_secret.empty()) {
Auth().data().forgetPassportCredentials();
showForm();
}
}).fail([=](const RPCError &error) {
_passwordCheckRequestId = 0;
Auth().data().forgetPassportCredentials();
showForm();
}).send();
}
void FormController::recoverPassword() {
if (!_password.hasRecovery) {
_view->show(Box<InformBox>(lang(lng_signin_no_email_forgot)));
@ -489,14 +542,16 @@ void FormController::recoverPassword() {
const auto box = _view->show(Box<RecoverBox>(
pattern,
_password.notEmptyPassport));
box->connect(box, &RecoverBox::reloadPassword, [=] {
box->passwordCleared(
) | rpl::start_with_next([=] {
reloadPassword();
});
box->connect(box, &RecoverBox::recoveryExpired, [=] {
if (box) {
box->closeBox();
}
});
}, box->lifetime());
box->recoveryExpired(
) | rpl::start_with_next([=] {
box->closeBox();
}, box->lifetime());
}).fail([=](const RPCError &error) {
_recoverRequestId = 0;
_view->show(Box<InformBox>(Lang::Hard::ServerError()
@ -509,6 +564,11 @@ void FormController::reloadPassword() {
requestPassword();
}
void FormController::reloadAndSubmitPassword(const QByteArray &password) {
_savedPasswordValue = password;
requestPassword();
}
void FormController::cancelPassword() {
if (_passwordRequestId) {
return;
@ -534,22 +594,30 @@ void FormController::cancelPassword() {
}
void FormController::validateSecureSecret(
bytes::const_span salt,
bytes::const_span encryptedSecret,
bytes::const_span password,
bytes::const_span passwordHashForSecret,
bytes::const_span passwordBytes,
uint64 serverSecretId) {
if (!salt.empty() && !encryptedSecret.empty()) {
_secret = DecryptSecureSecret(salt, encryptedSecret, password);
Expects(!passwordBytes.empty() || !passwordHashForSecret.empty());
if (!passwordHashForSecret.empty() && !encryptedSecret.empty()) {
_secret = DecryptSecureSecret(
encryptedSecret,
passwordHashForSecret);
if (_secret.empty()) {
_secretId = 0;
LOG(("API Error: Failed to decrypt secure secret."));
suggestReset(bytes::make_vector(password));
if (!passwordBytes.empty()) {
suggestReset(bytes::make_vector(passwordBytes));
}
return;
} else if (CountSecureSecretId(_secret) != serverSecretId) {
_secret.clear();
_secretId = 0;
LOG(("API Error: Wrong secure secret id."));
suggestReset(bytes::make_vector(password));
if (!passwordBytes.empty()) {
suggestReset(bytes::make_vector(passwordBytes));
}
return;
} else {
_secretId = serverSecretId;
@ -557,7 +625,7 @@ void FormController::validateSecureSecret(
}
}
if (_secret.empty()) {
generateSecret(password);
generateSecret(passwordBytes);
}
_secretReady.fire({});
}
@ -569,13 +637,9 @@ void FormController::suggestReset(bytes::vector password) {
// }
}
_view->suggestReset([=] {
const auto hashForAuth = openssl::Sha256(bytes::concatenate(
_password.salt,
password,
_password.salt));
using Flag = MTPDaccount_passwordInputSettings::Flag;
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(hashForAuth),
MTP_bytes(passwordHashForAuth(password)),
MTP_account_passwordInputSettings(
MTP_flags(Flag::f_new_secure_salt
| Flag::f_new_secure_secret
@ -1696,6 +1760,8 @@ void FormController::valueSaveFailed(not_null<Value*> value) {
}
void FormController::generateSecret(bytes::const_span password) {
Expects(!password.empty());
if (_saveSecretRequestId) {
return;
}
@ -1707,20 +1773,20 @@ void FormController::generateSecret(bytes::const_span password) {
_password.newSecureSalt,
randomSaltPart);
auto secureSecretId = CountSecureSecretId(secret);
auto encryptedSecret = EncryptSecureSecret(
auto saved = SavedCredentials();
saved.hashForAuth = passwordHashForAuth(password);
saved.hashForSecret = CountPasswordHashForSecret(
newSecureSaltFull,
secret,
password);
saved.secretId = CountSecureSecretId(secret);
const auto hashForAuth = openssl::Sha256(bytes::concatenate(
_password.salt,
password,
_password.salt));
auto encryptedSecret = EncryptSecureSecret(
secret,
saved.hashForSecret);
using Flag = MTPDaccount_passwordInputSettings::Flag;
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(hashForAuth),
MTP_bytes(saved.hashForAuth),
MTP_account_passwordInputSettings(
MTP_flags(Flag::f_new_secure_salt
| Flag::f_new_secure_secret
@ -1731,11 +1797,15 @@ void FormController::generateSecret(bytes::const_span password) {
MTPstring(), // email
MTP_bytes(newSecureSaltFull),
MTP_bytes(encryptedSecret),
MTP_long(secureSecretId))
MTP_long(saved.secretId))
)).done([=](const MTPBool &result) {
Auth().data().rememberPassportCredentials(
std::move(saved),
kRememberCredentialsDelay);
_saveSecretRequestId = 0;
_secret = secret;
_secretId = secureSecretId;
_secretId = saved.secretId;
//_password.salt = newPasswordSaltFull;
for (const auto &callback : base::take(_secretCallbacks)) {
callback();
@ -1988,6 +2058,7 @@ void FormController::parseForm(const MTPaccount_AuthorizationForm &result) {
}
void FormController::formFail(const QString &error) {
_savedPasswordValue = QByteArray();
_serviceErrorText = error;
_view->showCriticalError(
lang(lng_passport_form_error) + "\n" + error);
@ -2036,7 +2107,13 @@ void FormController::showForm() {
return;
}
if (!_password.salt.empty()) {
_view->showAskPassword();
if (!_savedPasswordValue.isEmpty()) {
submitPassword(base::duplicate(_savedPasswordValue));
} else if (const auto saved = Auth().data().passportCredentials()) {
checkSavedPasswordSettings(*saved);
} else {
_view->showAskPassword();
}
} else {
_view->showNoPassword();
}

View File

@ -24,6 +24,12 @@ class Controller;
namespace Passport {
struct SavedCredentials {
bytes::vector hashForAuth;
bytes::vector hashForSecret;
uint64 secretId = 0;
};
class ViewController;
struct FormRequest {
@ -247,11 +253,12 @@ public:
UserData *bot() const;
QString privacyPolicyUrl() const;
std::vector<not_null<const Value*>> submitGetErrors();
void submitPassword(const QString &password);
void submitPassword(const QByteArray &password);
void recoverPassword();
rpl::producer<QString> passwordError() const;
const PasswordSettings &passwordSettings() const;
void reloadPassword();
void reloadAndSubmitPassword(const QByteArray &password);
void cancelPassword();
bool canAddScan(not_null<const Value*> value) const;
@ -335,10 +342,11 @@ private:
bool applyPassword(const MTPDaccount_password &settings);
bool applyPassword(PasswordSettings &&settings);
bytes::vector passwordHashForAuth(bytes::const_span password) const;
void checkSavedPasswordSettings(const SavedCredentials &credentials);
void validateSecureSecret(
bytes::const_span salt,
bytes::const_span encryptedSecret,
bytes::const_span password,
bytes::const_span passwordHashForSecret,
bytes::const_span passwordBytes,
uint64 serverSecretId);
void decryptValues();
void decryptValue(Value &value);
@ -413,6 +421,7 @@ private:
mtpRequestId _passwordCheckRequestId = 0;
PasswordSettings _password;
QByteArray _savedPasswordValue;
Form _form;
bool _cancelled = false;
mtpRequestId _recoverRequestId = 0;

View File

@ -362,9 +362,8 @@ PanelController::PanelController(not_null<FormController*> form)
, _scopes(ComputeScopes(_form)) {
_form->secretReadyEvents(
) | rpl::start_with_next([=] {
if (_panel) {
_panel->showForm();
}
ensurePanelCreated();
_panel->showForm();
}, lifetime());
_form->verificationNeeded(
@ -435,7 +434,7 @@ void PanelController::submitForm() {
}
}
void PanelController::submitPassword(const QString &password) {
void PanelController::submitPassword(const QByteArray &password) {
_form->submitPassword(password);
}
@ -467,7 +466,10 @@ void PanelController::setupPassword() {
Expects(_panel != nullptr);
const auto &settings = _form->passwordSettings();
Assert(settings.salt.empty());
if (!settings.salt.empty()) {
showAskPassword();
return;
}
constexpr auto kRandomPart = 8;
auto newPasswordSalt = QByteArray(
@ -494,9 +496,22 @@ void PanelController::setupPassword() {
notEmptyPassport,
hint,
newSecureSecretSalt));
box->connect(box, &PasscodeBox::reloadPassword, [=] {
box->newPasswordSet(
) | rpl::filter([=](const QByteArray &password) {
return !password.isEmpty();
}) | rpl::start_with_next([=](const QByteArray &password) {
_form->reloadAndSubmitPassword(password);
}, box->lifetime());
rpl::merge(
box->passwordReloadNeeded(),
box->newPasswordSet(
) | rpl::filter([=](const QByteArray &password) {
return password.isEmpty();
}) | rpl::map([] { return rpl::empty_value(); })
) | rpl::start_with_next([=] {
_form->reloadPassword();
});
}, box->lifetime());
}
void PanelController::cancelPasswordSubmit() {

View File

@ -68,7 +68,7 @@ public:
not_null<UserData*> bot() const;
QString privacyPolicyUrl() const;
void submitForm();
void submitPassword(const QString &password);
void submitPassword(const QByteArray &password);
void recoverPassword();
rpl::producer<QString> passwordError() const;
QString passwordHint() const;

View File

@ -93,7 +93,7 @@ void PanelAskPassword::hideError() {
}
void PanelAskPassword::submit() {
_controller->submitPassword(_password->getLastText());
_controller->submitPassword(_password->getLastText().toUtf8());
}
void PanelAskPassword::recover() {

View File

@ -83,7 +83,12 @@ void CloudPasswordState::onEdit() {
_notEmptyPassport,
_curPasswordHint,
_newSecureSecretSalt));
connect(box, SIGNAL(reloadPassword()), this, SLOT(onReloadPassword()));
rpl::merge(
box->newPasswordSet() | rpl::map([] { return rpl::empty_value(); }),
box->passwordReloadNeeded()
) | rpl::start_with_next([=] {
onReloadPassword();
}, box->lifetime());
}
void CloudPasswordState::onTurnOff() {
@ -113,7 +118,13 @@ void CloudPasswordState::onTurnOff() {
_curPasswordHint,
_newSecureSecretSalt,
true));
connect(box, SIGNAL(reloadPassword()), this, SLOT(onReloadPassword()));
rpl::merge(
box->newPasswordSet(
) | rpl::map([] { return rpl::empty_value(); }),
box->passwordReloadNeeded()
) | rpl::start_with_next([=] {
onReloadPassword();
}, box->lifetime());
}
}