Re-encrypt secure secret with password change.

This commit is contained in:
John Preston 2018-04-17 19:19:24 +04:00
parent 395f22063b
commit 6c2a39f1fc
7 changed files with 318 additions and 54 deletions

View File

@ -406,6 +406,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_cloud_password_differ" = "Passwords do not match";
"lng_cloud_password_wrong" = "Wrong cloud password";
"lng_cloud_password_is_same" = "Password was not changed";
"lng_cloud_password_passport_losing" = "Warning! All data saved in your Telegram Passport will be lost!";
"lng_connection_type" = "Connection type:";
"lng_connection_auto_connecting" = "Default (connecting...)";

View File

@ -12,9 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/confirm_box.h"
#include "mainwindow.h"
#include "storage/localstorage.h"
#include "styles/style_boxes.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "passport/passport_encryption.h"
#include "styles/style_boxes.h"
PasscodeBox::PasscodeBox(QWidget*, bool turningOff)
: _turningOff(turningOff)
@ -27,11 +28,19 @@ PasscodeBox::PasscodeBox(QWidget*, bool turningOff)
, _recover(this, lang(lng_signin_recover)) {
}
PasscodeBox::PasscodeBox(QWidget*, const QByteArray &newSalt, const QByteArray &curSalt, bool hasRecovery, const QString &hint, bool turningOff)
PasscodeBox::PasscodeBox(
QWidget*,
const QByteArray &newSalt,
const QByteArray &curSalt,
bool hasRecovery,
const QString &hint,
const QByteArray &newSecureSecretSalt,
bool turningOff)
: _turningOff(turningOff)
, _cloudPwd(true)
, _newSalt(newSalt)
, _curSalt(curSalt)
, _newSecureSecretSalt(newSecureSecretSalt)
, _hasRecovery(hasRecovery)
, _about(st::boxWidth - st::boxPadding.left() * 1.5)
, _oldPasscode(this, st::defaultInputField, langFactory(lng_cloud_password_enter_old))
@ -186,7 +195,7 @@ void PasscodeBox::setInnerFocus() {
}
}
void PasscodeBox::setPasswordDone(const MTPBool &result) {
void PasscodeBox::setPasswordDone() {
_setRequest = 0;
emit reloadPassword();
auto text = lang(_reenterPasscode->isHidden() ? lng_cloud_password_removed : (_oldPasscode->isHidden() ? lng_cloud_password_was_set : lng_cloud_password_updated));
@ -312,43 +321,12 @@ void PasscodeBox::save(bool force) {
_replacedBy = getDelegate()->show(Box<ConfirmBox>(lang(lng_cloud_password_about_recover), lang(lng_cloud_password_skip_email), st::attentionBoxButton, base::lambda_guarded(this, [this] {
save(true);
})));
} else if (_newPasscode->isHidden()) {
clearCloudPassword(old);
} else if (_oldPasscode->isHidden()) {
setNewCloudPassword(pwd);
} else {
QByteArray newPasswordData = pwd.isEmpty() ? QByteArray() : (_newSalt + pwd.toUtf8() + _newSalt);
QByteArray newPasswordHash = pwd.isEmpty() ? QByteArray() : QByteArray(32, Qt::Uninitialized);
if (pwd.isEmpty()) {
hint = QString();
email = QString();
} else {
hashSha256(newPasswordData.constData(), newPasswordData.size(), newPasswordHash.data());
}
QByteArray oldPasswordData = _oldPasscode->isHidden() ? QByteArray() : (_curSalt + old.toUtf8() + _curSalt);
QByteArray oldPasswordHash = _oldPasscode->isHidden() ? QByteArray() : QByteArray(32, Qt::Uninitialized);
if (!_oldPasscode->isHidden()) {
hashSha256(oldPasswordData.constData(), oldPasswordData.size(), oldPasswordHash.data());
}
auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_salt
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
| MTPDaccount_passwordInputSettings::Flag::f_hint;
if (_oldPasscode->isHidden() || _newPasscode->isHidden()) {
flags |= MTPDaccount_passwordInputSettings::Flag::f_email;
}
const auto newSecureSecret = bytes::vector();
const auto newSecureSalt = bytes::vector();
const auto newSecureSecretHash = 0ULL;
_setRequest = MTP::send(
MTPaccount_UpdatePasswordSettings(
MTP_bytes(oldPasswordHash),
MTP_account_passwordInputSettings(
MTP_flags(flags),
MTP_bytes(_newSalt),
MTP_bytes(newPasswordHash),
MTP_string(hint),
MTP_string(email),
MTP_bytes(newSecureSalt),
MTP_bytes(newSecureSecret),
MTP_long(newSecureSecretHash))),
rpcDone(&PasscodeBox::setPasswordDone),
rpcFail(&PasscodeBox::setPasswordFail));
changeCloudPassword(old, pwd);
}
} else {
cSetPasscodeBadTries(0);
@ -358,6 +336,225 @@ void PasscodeBox::save(bool force) {
}
}
void PasscodeBox::clearCloudPassword(const QString &oldPassword) {
Expects(!_oldPasscode->isHidden());
const auto passwordUtf = oldPassword.toUtf8();
const auto oldPasswordData = (_curSalt + passwordUtf + _curSalt);
auto oldPasswordHash = QByteArray(32, Qt::Uninitialized);
hashSha256(oldPasswordData.constData(), oldPasswordData.size(), oldPasswordHash.data());
_setRequest = request(MTPaccount_GetPasswordSettings(
MTP_bytes(oldPasswordHash)
)).done([=](const MTPaccount_PasswordSettings &result) {
_setRequest = 0;
Expects(result.type() == mtpc_account_passwordSettings);
const auto &data = result.c_account_passwordSettings();
if (data.vsecure_secret.v.isEmpty()) {
sendClearCloudPassword(oldPasswordHash);
return;
}
warnPassportLoss(oldPasswordHash);
}).fail([=](const RPCError &error) {
setPasswordFail(error);
}).send();
}
void PasscodeBox::sendClearCloudPassword(
const QByteArray &oldPasswordHash) {
const auto newPasswordData = QByteArray();
const auto newPasswordHash = QByteArray();
const auto hint = QString();
const auto email = QString();
const auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_salt
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
| MTPDaccount_passwordInputSettings::Flag::f_hint
| MTPDaccount_passwordInputSettings::Flag::f_email;
_setRequest = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(oldPasswordHash),
MTP_account_passwordInputSettings(
MTP_flags(flags),
MTP_bytes(_newSalt),
MTP_bytes(newPasswordHash),
MTP_string(hint),
MTP_string(email),
MTPbytes(), // new_secure_salt
MTPbytes(), // new_secure_secret
MTPlong()) // new_secure_secret_id
)).done([=](const MTPBool &result) {
setPasswordDone();
}).fail([=](const RPCError &error) {
setPasswordFail(error);
}).send();
}
void PasscodeBox::warnPassportLoss(const QByteArray &oldPasswordHash) {
const auto box = std::make_shared<QPointer<BoxContent>>();
const auto sendAndClose = [=] {
sendClearCloudPassword(oldPasswordHash);
if (*box) {
(*box)->closeBox();
}
};
*box = getDelegate()->show(Box<ConfirmBox>(
lang(lng_cloud_password_passport_losing),
lang(lng_continue),
sendAndClose));
}
void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
const auto newPasswordData = (_newSalt + newPassword.toUtf8() + _newSalt);
auto newPasswordHash = QByteArray(32, Qt::Uninitialized);
hashSha256(newPasswordData.constData(), newPasswordData.size(), newPasswordHash.data());
const auto oldPasswordData = QByteArray();
const auto oldPasswordHash = QByteArray();
const auto hint = _passwordHint->getLastText();
const auto email = _recoverEmail->getLastText().trimmed();
const auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_salt
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
| MTPDaccount_passwordInputSettings::Flag::f_hint
| MTPDaccount_passwordInputSettings::Flag::f_email;
_setRequest = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(oldPasswordHash),
MTP_account_passwordInputSettings(
MTP_flags(flags),
MTP_bytes(_newSalt),
MTP_bytes(newPasswordHash),
MTP_string(hint),
MTP_string(email),
MTPbytes(), // new_secure_salt
MTPbytes(), // new_secure_secret
MTPlong()) // new_secure_secret_id
)).done([=](const MTPBool &result) {
setPasswordDone();
}).fail([=](const RPCError &error) {
setPasswordFail(error);
}).send();
}
void PasscodeBox::changeCloudPassword(
const QString &oldPassword,
const QString &newPassword) {
const auto passwordUtf = oldPassword.toUtf8();
const auto oldPasswordData = (_curSalt + passwordUtf + _curSalt);
auto oldPasswordHash = QByteArray(32, Qt::Uninitialized);
hashSha256(oldPasswordData.constData(), oldPasswordData.size(), oldPasswordHash.data());
_setRequest = request(MTPaccount_GetPasswordSettings(
MTP_bytes(oldPasswordHash)
)).done([=](const MTPaccount_PasswordSettings &result) {
_setRequest = 0;
Expects(result.type() == mtpc_account_passwordSettings);
const auto &data = result.c_account_passwordSettings();
if (data.vsecure_secret.v.isEmpty()) {
const auto empty = QByteArray();
sendChangeCloudPassword(oldPasswordHash, newPassword, empty);
return;
}
const auto secret = Passport::DecryptSecureSecret(
bytes::make_span(data.vsecure_salt.v),
bytes::make_span(data.vsecure_secret.v),
bytes::make_span(passwordUtf));
if (secret.empty()) {
LOG(("API Error: Failed to decrypt secure secret."));
suggestSecretReset(oldPasswordHash, newPassword);
} else if (Passport::CountSecureSecretId(secret) != data.vsecure_secret_id.v) {
LOG(("API Error: Wrong secure secret id."));
suggestSecretReset(oldPasswordHash, newPassword);
} else {
sendChangeCloudPassword(
oldPasswordHash,
newPassword,
QByteArray::fromRawData(
reinterpret_cast<const char*>(secret.data()),
secret.size()));
}
}).fail([=](const RPCError &error) {
setPasswordFail(error);
}).send();
}
void PasscodeBox::suggestSecretReset(
const QByteArray &oldPasswordHash,
const QString &newPassword) {
const auto box = std::make_shared<QPointer<BoxContent>>();
const auto resetSecretAndSave = [=] {
using Flag = MTPDaccount_passwordInputSettings::Flag;
_setRequest = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(oldPasswordHash),
MTP_account_passwordInputSettings(
MTP_flags(Flag::f_new_secure_salt
| Flag::f_new_secure_secret
| Flag::f_new_secure_secret_id),
MTPbytes(), // new_salt
MTPbytes(), // new_password_hash
MTPstring(), // hint
MTPstring(), // email
MTP_bytes(QByteArray()), // new_secure_salt
MTP_bytes(QByteArray()), // new_secure_secret
MTP_long(0)) // new_secure_secret_id
)).done([=](const MTPBool &result) {
_setRequest = 0;
const auto empty = QByteArray();
if (*box) {
(*box)->closeBox();
}
sendChangeCloudPassword(oldPasswordHash, newPassword, empty);
}).fail([=](const RPCError &error) {
_setRequest = 0;
}).send();
};
*box = getDelegate()->show(Box<ConfirmBox>(
Lang::Hard::PassportCorrupted(),
Lang::Hard::PassportCorruptedReset(),
[=] { resetSecretAndSave(); }));
}
void PasscodeBox::sendChangeCloudPassword(
const QByteArray &oldPasswordHash,
const QString &newPassword,
const QByteArray &secureSecret) {
const auto passwordUtf = newPassword.toUtf8();
const auto newPasswordData = (_newSalt + passwordUtf + _newSalt);
auto newPasswordHash = QByteArray(32, Qt::Uninitialized);
hashSha256(newPasswordData.constData(), newPasswordData.size(), newPasswordHash.data());
const auto hint = _passwordHint->getLastText();
auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_salt
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
| MTPDaccount_passwordInputSettings::Flag::f_hint;
auto newSecureSecret = bytes::vector();
auto newSecureSecretId = 0ULL;
if (!secureSecret.isEmpty()) {
flags |= MTPDaccount_passwordInputSettings::Flag::f_new_secure_salt
| MTPDaccount_passwordInputSettings::Flag::f_new_secure_secret
| MTPDaccount_passwordInputSettings::Flag::f_new_secure_secret_id;
newSecureSecretId = Passport::CountSecureSecretId(
bytes::make_span(secureSecret));
newSecureSecret = Passport::EncryptSecureSecret(
bytes::make_span(_newSecureSecretSalt),
bytes::make_span(secureSecret),
bytes::make_span(passwordUtf));
}
_setRequest = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(oldPasswordHash),
MTP_account_passwordInputSettings(
MTP_flags(flags),
MTP_bytes(_newSalt),
MTP_bytes(newPasswordHash),
MTP_string(hint),
MTPstring(), // email is not changing
MTP_bytes(_newSecureSecretSalt),
MTP_bytes(newSecureSecret),
MTP_long(newSecureSecretId))
)).done([=](const MTPBool &result) {
setPasswordDone();
}).fail([=](const RPCError &error) {
setPasswordFail(error);
}).send();
}
void PasscodeBox::badOldPasscode() {
_oldPasscode->selectAll();
_oldPasscode->setFocus();
@ -396,7 +593,12 @@ void PasscodeBox::emailChanged() {
void PasscodeBox::recoverByEmail() {
if (_pattern.isEmpty()) {
_pattern = "-";
MTP::send(MTPauth_RequestPasswordRecovery(), rpcDone(&PasscodeBox::recoverStarted), rpcFail(&PasscodeBox::recoverStartFail));
request(MTPauth_RequestPasswordRecovery(
)).done([=](const MTPauth_PasswordRecovery &result) {
recoverStarted(result);
}).fail([=](const RPCError &error) {
recoverStartFail(error);
}).send();
} else {
recover();
}

View File

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "boxes/abstract_box.h"
#include "mtproto/sender.h"
namespace Ui {
class InputField;
@ -15,12 +16,19 @@ class PasswordInput;
class LinkButton;
} // namespace Ui
class PasscodeBox : public BoxContent, public RPCSender {
class PasscodeBox : public BoxContent, private MTP::Sender {
Q_OBJECT
public:
PasscodeBox(QWidget*, bool turningOff);
PasscodeBox(QWidget*, const QByteArray &newSalt, const QByteArray &curSalt, bool hasRecovery, const QString &hint, bool turningOff = false);
PasscodeBox(
QWidget*,
const QByteArray &newSalt,
const QByteArray &curSalt,
bool hasRecovery,
const QString &hint,
const QByteArray &newSecureSecretSalt,
bool turningOff = false);
signals:
void reloadPassword();
@ -43,13 +51,31 @@ private:
void recoverByEmail();
void recoverExpired();
void setPasswordDone(const MTPBool &result);
void setPasswordDone();
bool setPasswordFail(const RPCError &error);
void recoverStarted(const MTPauth_PasswordRecovery &result);
bool recoverStartFail(const RPCError &error);
void recover();
void clearCloudPassword(const QString &oldPassword);
void setNewCloudPassword(const QString &newPassword);
void changeCloudPassword(
const QString &oldPassword,
const QString &newPassword);
void sendChangeCloudPassword(
const QByteArray &oldPasswordHash,
const QString &newPassword,
const QByteArray &secureSecret);
void suggestSecretReset(
const QByteArray &oldPasswordHash,
const QString &newPassword);
void resetSecretAndChangePassword(
const QByteArray &oldPasswordHash,
const QString &newPassword);
void sendClearCloudPassword(const QByteArray &oldPasswordHash);
void warnPassportLoss(const QByteArray &oldPasswordHash);
QString _pattern;
QPointer<BoxContent> _replacedBy;
@ -57,7 +83,7 @@ private:
bool _cloudPwd = false;
mtpRequestId _setRequest = 0;
QByteArray _newSalt, _curSalt;
QByteArray _newSalt, _curSalt, _newSecureSecretSalt;
bool _hasRecovery = false;
bool _skipEmailWarning = false;

View File

@ -462,7 +462,9 @@ void FormController::suggestReset(bytes::vector password) {
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(hashForAuth),
MTP_account_passwordInputSettings(
MTP_flags(Flag::f_new_secure_secret),
MTP_flags(Flag::f_new_secure_salt
| Flag::f_new_secure_secret
| Flag::f_new_secure_secret_id),
MTPbytes(), // new_salt
MTPbytes(), // new_password_hash
MTPstring(), // hint
@ -1482,7 +1484,9 @@ void FormController::generateSecret(bytes::const_span password) {
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(hashForAuth),
MTP_account_passwordInputSettings(
MTP_flags(Flag::f_new_secure_secret),
MTP_flags(Flag::f_new_secure_salt
| Flag::f_new_secure_secret
| Flag::f_new_secure_secret_id),
MTPbytes(), // new_salt
MTPbytes(), // new_password_hash
MTPstring(), // hint

View File

@ -427,20 +427,28 @@ void PanelController::setupPassword() {
Assert(settings.salt.empty());
constexpr auto kRandomPart = 8;
auto newSalt = QByteArray(
auto newPasswordSalt = QByteArray(
reinterpret_cast<const char*>(settings.newSalt.data()),
settings.newSalt.size());
newSalt.resize(newSalt.size() + kRandomPart);
newPasswordSalt.resize(newPasswordSalt.size() + kRandomPart);
bytes::set_random(
bytes::make_span(newSalt).subspan(settings.newSalt.size()));
bytes::make_span(newPasswordSalt).subspan(settings.newSalt.size()));
auto newSecureSecretSalt = QByteArray(
reinterpret_cast<const char*>(settings.newSecureSalt.data()),
settings.newSecureSalt.size());
newSecureSecretSalt.resize(newSecureSecretSalt.size() + kRandomPart);
bytes::set_random(
bytes::make_span(
newSecureSecretSalt).subspan(settings.newSecureSalt.size()));
const auto currentSalt = QByteArray();
const auto hasRecovery = false;
const auto hint = QString();
auto box = show(Box<PasscodeBox>(
newSalt,
newPasswordSalt,
currentSalt,
hasRecovery,
hint));
hint,
newSecureSecretSalt));
box->connect(box, &PasscodeBox::reloadPassword, _panel.get(), [=] {
_form->reloadPassword();
});

View File

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "application.h"
#include "platform/platform_specific.h"
#include "base/openssl_help.h"
#include "boxes/sessions_box.h"
#include "boxes/passcode_box.h"
#include "boxes/autolock_box.h"
@ -73,7 +74,12 @@ int CloudPasswordState::resizeGetHeight(int newWidth) {
}
void CloudPasswordState::onEdit() {
auto box = Ui::show(Box<PasscodeBox>(_newPasswordSalt, _curPasswordSalt, _hasPasswordRecovery, _curPasswordHint));
auto box = Ui::show(Box<PasscodeBox>(
_newPasswordSalt,
_curPasswordSalt,
_hasPasswordRecovery,
_curPasswordHint,
_newSecureSecretSalt));
connect(box, SIGNAL(reloadPassword()), this, SLOT(onReloadPassword()));
}
@ -96,7 +102,13 @@ void CloudPasswordState::onTurnOff() {
rpcDone(&CloudPasswordState::offPasswordDone),
rpcFail(&CloudPasswordState::offPasswordFail));
} else {
auto box = Ui::show(Box<PasscodeBox>(_newPasswordSalt, _curPasswordSalt, _hasPasswordRecovery, _curPasswordHint, true));
auto box = Ui::show(Box<PasscodeBox>(
_newPasswordSalt,
_curPasswordSalt,
_hasPasswordRecovery,
_curPasswordHint,
_newSecureSecretSalt,
true));
connect(box, SIGNAL(reloadPassword()), this, SLOT(onReloadPassword()));
}
}
@ -117,10 +129,12 @@ void CloudPasswordState::getPasswordDone(const MTPaccount_Password &result) {
_hasPasswordRecovery = false;
_curPasswordHint = QString();
_newPasswordSalt = qba(d.vnew_salt);
_newSecureSecretSalt = qba(d.vnew_secure_salt);
auto pattern = qs(d.vemail_unconfirmed_pattern);
if (!pattern.isEmpty()) {
_waitingConfirm = lng_cloud_password_waiting(lt_email, pattern);
}
openssl::AddRandomSeed(bytes::make_span(d.vsecure_random.v));
} break;
case mtpc_account_password: {
@ -129,10 +143,12 @@ void CloudPasswordState::getPasswordDone(const MTPaccount_Password &result) {
_hasPasswordRecovery = mtpIsTrue(d.vhas_recovery);
_curPasswordHint = qs(d.vhint);
_newPasswordSalt = qba(d.vnew_salt);
_newSecureSecretSalt = qba(d.vnew_secure_salt);
auto pattern = qs(d.vemail_unconfirmed_pattern);
if (!pattern.isEmpty()) {
_waitingConfirm = lng_cloud_password_waiting(lt_email, pattern);
}
openssl::AddRandomSeed(bytes::make_span(d.vsecure_random.v));
} break;
}
_edit->setText(lang(_curPasswordSalt.isEmpty() ? lng_cloud_password_set : lng_cloud_password_edit));
@ -141,7 +157,13 @@ void CloudPasswordState::getPasswordDone(const MTPaccount_Password &result) {
update();
_newPasswordSalt.resize(_newPasswordSalt.size() + 8);
memset_rand(_newPasswordSalt.data() + _newPasswordSalt.size() - 8, 8);
memset_rand(
_newPasswordSalt.data() + _newPasswordSalt.size() - 8,
8);
_newSecureSecretSalt.resize(_newSecureSecretSalt.size() + 8);
memset_rand(
_newSecureSecretSalt.data() + _newSecureSecretSalt.size() - 8,
8);
}
void CloudPasswordState::paintEvent(QPaintEvent *e) {

View File

@ -65,6 +65,7 @@ private:
bool _hasPasswordRecovery = false;
QString _curPasswordHint;
QByteArray _newPasswordSalt;
QByteArray _newSecureSecretSalt;
};