Update API scheme to layer 83.

This commit is contained in:
John Preston 2018-08-04 00:48:00 +03:00
parent aeec5d1542
commit 550c159ca8
22 changed files with 683 additions and 296 deletions

View File

@ -322,6 +322,7 @@ inputReportReasonSpam#58dbcab8 = ReportReason;
inputReportReasonViolence#1e22c78d = ReportReason;
inputReportReasonPornography#2e59d922 = ReportReason;
inputReportReasonOther#e1746d0a text:string = ReportReason;
inputReportReasonCopyright#9b89f93a = ReportReason;
userFull#f220f3f flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true user:User about:flags.1?string link:contacts.Link profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo common_chats_count:int = UserFull;
@ -473,7 +474,7 @@ config#3213dbba flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:fla
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
help.appUpdate#8987f311 id:int critical:Bool url:string text:string = help.AppUpdate;
help.appUpdate#1da7158f flags:# popup:flags.0?true id:int version:string text:string entities:Vector<MessageEntity> document:flags.1?Document url:flags.2?string = help.AppUpdate;
help.noAppUpdate#c45a6536 = help.AppUpdate;
help.inviteText#18cb9f78 message:string = help.InviteText;
@ -589,12 +590,11 @@ authorization#7bf2e6f6 hash:long flags:int device_model:string platform:string s
account.authorizations#1250abde authorizations:Vector<Authorization> = account.Authorizations;
account.noPassword#5ea182f6 new_salt:bytes new_secure_salt:bytes secure_random:bytes email_unconfirmed_pattern:string = account.Password;
account.password#ca39b447 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true current_salt:bytes new_salt:bytes new_secure_salt:bytes secure_random:bytes hint:string email_unconfirmed_pattern:string = account.Password;
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.passwordSettings#7bd9c3f1 email:string secure_salt:bytes secure_secret:bytes secure_secret_id:long = account.PasswordSettings;
account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings;
account.passwordInputSettings#21ffa60d flags:# new_salt:flags.0?bytes new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_salt:flags.2?bytes new_secure_secret:flags.2?bytes new_secure_secret_id:flags.2?long = account.PasswordInputSettings;
account.passwordInputSettings#c23727c9 flags:# new_algo:flags.0?PasswordKdfAlgo new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_settings:flags.2?SecureSecretSettings = account.PasswordInputSettings;
auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery;
@ -1021,6 +1021,15 @@ savedPhoneContact#1142bd56 phone:string first_name:string last_name:string date:
account.takeout#4dba4501 id:long = account.Takeout;
passwordKdfAlgoUnknown#d45ab096 = PasswordKdfAlgo;
passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000#b6425eaa salt1:bytes salt2:bytes = PasswordKdfAlgo;
securePasswordKdfAlgoUnknown#4a8537 = SecurePasswordKdfAlgo;
securePasswordKdfAlgoPBKDF2HMACSHA512iter100000#bbf2dda0 salt:bytes = SecurePasswordKdfAlgo;
securePasswordKdfAlgoSHA512#86471d92 salt:bytes = SecurePasswordKdfAlgo;
secureSecretSettings#1527bcac secure_algo:SecurePasswordKdfAlgo secure_secret:bytes secure_secret_id:long = SecureSecretSettings;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1211,6 +1220,7 @@ messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:stri
messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
messages.markDialogUnread#c286d98f flags:# unread:flags.0?true peer:InputDialogPeer = Bool;
messages.getDialogUnreadMarks#22e24e22 = Vector<DialogPeer>;
messages.clearAllDrafts#7e58ee9c = Bool;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@ -1232,7 +1242,7 @@ upload.getFileHashes#c7025931 location:InputFileLocation offset:int = Vector<Fil
help.getConfig#c4f9186b = Config;
help.getNearestDc#1fb33026 = NearestDc;
help.getAppUpdate#ae2de196 = help.AppUpdate;
help.getAppUpdate#522d5a7d source:string = help.AppUpdate;
help.saveAppLog#6f02f748 events:Vector<InputAppEvent> = Bool;
help.getInviteText#4d392343 = help.InviteText;
help.getSupport#9cdf08cd = help.Support;
@ -1308,4 +1318,4 @@ langpack.getStrings#2e1ee318 lang_code:string keys:Vector<string> = Vector<LangP
langpack.getDifference#b2e4d7d from_version:int = LangPackDifference;
langpack.getLanguages#800fd57d = Vector<LangPackLanguage>;
// LAYER 82
// LAYER 83

View File

@ -8,13 +8,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "base/bytes.h"
#include "base/algorithm.h"
#include "core/basic_types.h"
extern "C" {
#include <openssl/bn.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include <openssl/aes.h>
#include <openssl/modes.h>
#include <openssl/crypto.h>
#include <openssl/evp.h>
} // extern "C"
namespace openssl {
@ -212,37 +216,144 @@ inline BigNum operator-(const BigNum &a, const BigNum &b) {
return result;
}
inline bytes::vector Sha512(bytes::const_span data) {
auto result = bytes::vector(SHA512_DIGEST_LENGTH);
SHA512(
namespace details {
template <typename Context, typename Method, typename Arg>
inline void ShaUpdate(Context context, Method method, Arg &&arg) {
const auto span = bytes::make_span(arg);
method(context, span.data(), span.size());
}
template <typename Context, typename Method, typename Arg, typename ...Args>
inline void ShaUpdate(Context context, Method method, Arg &&arg, Args &&...args) {
const auto span = bytes::make_span(arg);
method(context, span.data(), span.size());
ShaUpdate(context, method, args...);
}
template <size_type Size, typename Method>
inline bytes::vector Sha(Method method, bytes::const_span data) {
auto result = bytes::vector(Size);
method(
reinterpret_cast<const unsigned char*>(data.data()),
data.size(),
reinterpret_cast<unsigned char*>(result.data()));
return result;
}
template <
size_type Size,
typename Context,
typename Init,
typename Update,
typename Finalize,
typename ...Args,
typename = std::enable_if_t<(sizeof...(Args) > 1)>>
bytes::vector Sha(
Context context,
Init init,
Update update,
Finalize finalize,
Args &&...args) {
auto result = bytes::vector(Size);
init(&context);
ShaUpdate(&context, update, args...);
finalize(reinterpret_cast<unsigned char*>(result.data()), &context);
return result;
}
template <
size_type Size,
typename Evp>
bytes::vector Pbkdf2(
bytes::const_span password,
bytes::const_span salt,
int iterations,
Evp evp) {
auto result = bytes::vector(Size);
PKCS5_PBKDF2_HMAC(
reinterpret_cast<const char*>(password.data()),
password.size(),
reinterpret_cast<const unsigned char*>(salt.data()),
salt.size(),
iterations,
evp,
result.size(),
reinterpret_cast<unsigned char*>(result.data()));
return result;
}
} // namespace details
constexpr auto kSha1Size = size_type(SHA_DIGEST_LENGTH);
constexpr auto kSha256Size = size_type(SHA256_DIGEST_LENGTH);
constexpr auto kSha512Size = size_type(SHA512_DIGEST_LENGTH);
inline bytes::vector Sha1(bytes::const_span data) {
return details::Sha<kSha1Size>(SHA1, data);
}
template <
typename ...Args,
typename = std::enable_if_t<(sizeof...(Args) > 1)>>
inline bytes::vector Sha1(Args &&...args) {
return details::Sha<kSha1Size>(
SHA_CTX(),
SHA_Init,
SHA_Update,
SHA_Final,
args...);
}
inline bytes::vector Sha256(bytes::const_span data) {
auto result = bytes::vector(SHA256_DIGEST_LENGTH);
SHA256(
reinterpret_cast<const unsigned char*>(data.data()),
data.size(),
reinterpret_cast<unsigned char*>(result.data()));
return result;
return details::Sha<kSha256Size>(SHA256, data);
}
inline bytes::vector Sha1(bytes::const_span data) {
auto result = bytes::vector(SHA_DIGEST_LENGTH);
SHA1(
reinterpret_cast<const unsigned char*>(data.data()),
data.size(),
reinterpret_cast<unsigned char*>(result.data()));
return result;
template <
typename ...Args,
typename = std::enable_if_t<(sizeof...(Args) > 1)>>
inline bytes::vector Sha256(Args &&...args) {
return details::Sha<kSha256Size>(
SHA256_CTX(),
SHA256_Init,
SHA256_Update,
SHA256_Final,
args...);
}
inline bytes::vector Sha512(bytes::const_span data) {
return details::Sha<kSha512Size>(SHA512, data);
}
template <
typename ...Args,
typename = std::enable_if_t<(sizeof...(Args) > 1)>>
inline bytes::vector Sha512(Args &&...args) {
return details::Sha<kSha512Size>(
SHA512_CTX(),
SHA512_Init,
SHA512_Update,
SHA512_Final,
args...);
}
inline void AddRandomSeed(bytes::const_span data) {
RAND_seed(data.data(), data.size());
}
inline bytes::vector Pbkdf2Sha512(
bytes::const_span password,
bytes::const_span salt,
int iterations) {
return details::Pbkdf2<kSha512Size>(
password,
salt,
iterations,
EVP_sha512());
}
} // namespace openssl
namespace bytes {

View File

@ -30,27 +30,29 @@ PasscodeBox::PasscodeBox(QWidget*, bool turningOff)
PasscodeBox::PasscodeBox(
QWidget*,
const QByteArray &newSalt,
const QByteArray &curSalt,
const Core::CloudPasswordAlgo &curAlgo,
const Core::CloudPasswordAlgo &newAlgo,
bool hasRecovery,
bool notEmptyPassport,
const QString &hint,
const QByteArray &newSecureSecretSalt,
const Core::SecureSecretAlgo &newSecureSecretAlgo,
bool turningOff)
: _turningOff(turningOff)
, _cloudPwd(true)
, _newSalt(newSalt)
, _curSalt(curSalt)
, _newSecureSecretSalt(newSecureSecretSalt)
, _curAlgo(curAlgo)
, _newAlgo(newAlgo)
, _newSecureSecretAlgo(newSecureSecretAlgo)
, _hasRecovery(hasRecovery)
, _notEmptyPassport(notEmptyPassport)
, _about(st::boxWidth - st::boxPadding.left() * 1.5)
, _oldPasscode(this, st::defaultInputField, langFactory(lng_cloud_password_enter_old))
, _newPasscode(this, st::defaultInputField, langFactory(curSalt.isEmpty() ? lng_cloud_password_enter_first : lng_cloud_password_enter_new))
, _newPasscode(this, st::defaultInputField, langFactory(curAlgo ? lng_cloud_password_enter_new : lng_cloud_password_enter_first))
, _reenterPasscode(this, st::defaultInputField, langFactory(lng_cloud_password_confirm_new))
, _passwordHint(this, st::defaultInputField, langFactory(curSalt.isEmpty() ? lng_cloud_password_hint : lng_cloud_password_change_hint))
, _passwordHint(this, st::defaultInputField, langFactory(curAlgo ? lng_cloud_password_change_hint : lng_cloud_password_hint))
, _recoverEmail(this, st::defaultInputField, langFactory(lng_cloud_password_email))
, _recover(this, lang(lng_signin_recover)) {
Expects(!_turningOff || curAlgo.has_value());
if (!hint.isEmpty()) _hintText.setText(st::passcodeTextStyle, lng_signin_hint(lt_password_hint, hint));
}
@ -62,6 +64,10 @@ rpl::producer<> PasscodeBox::passwordReloadNeeded() const {
return _passwordReloadNeeded.events();
}
bool PasscodeBox::currentlyHave() const {
return _cloudPwd ? _curAlgo.has_value() : Global::LocalPasscode();
}
void PasscodeBox::prepare() {
addButton(langFactory(_turningOff ? lng_passcode_remove_button : lng_settings_save), [=] { save(); });
addButton(langFactory(lng_cancel), [=] { closeBox(); });
@ -73,8 +79,7 @@ void PasscodeBox::prepare() {
setTitle(langFactory(_cloudPwd ? lng_cloud_password_remove : lng_passcode_remove));
setDimensions(st::boxWidth, st::passcodePadding.top() + _oldPasscode->height() + st::passcodeTextLine + ((_hasRecovery && !_hintText.isEmpty()) ? st::passcodeTextLine : 0) + st::passcodeAboutSkip + _aboutHeight + st::passcodePadding.bottom());
} else {
auto has = _cloudPwd ? (!_curSalt.isEmpty()) : Global::LocalPasscode();
if (has) {
if (currentlyHave()) {
_oldPasscode->show();
setTitle(langFactory(_cloudPwd ? lng_cloud_password_change : lng_passcode_change));
setDimensions(st::boxWidth, st::passcodePadding.top() + _oldPasscode->height() + st::passcodeTextLine + ((_hasRecovery && !_hintText.isEmpty()) ? st::passcodeTextLine : 0) + _newPasscode->height() + st::passcodeLittleSkip + _reenterPasscode->height() + st::passcodeSkip + (_cloudPwd ? _passwordHint->height() + st::passcodeLittleSkip : 0) + st::passcodeAboutSkip + _aboutHeight + st::passcodePadding.bottom());
@ -100,17 +105,17 @@ void PasscodeBox::prepare() {
_recover->addClickHandler([=] { recoverByEmail(); });
bool has = _cloudPwd ? (!_curSalt.isEmpty()) : Global::LocalPasscode();
const auto has = currentlyHave();
_oldPasscode->setVisible(_turningOff || has);
_recover->setVisible((_turningOff || has) && _cloudPwd && _hasRecovery);
_newPasscode->setVisible(!_turningOff);
_reenterPasscode->setVisible(!_turningOff);
_passwordHint->setVisible(!_turningOff && _cloudPwd);
_recoverEmail->setVisible(!_turningOff && _cloudPwd && _curSalt.isEmpty());
_recoverEmail->setVisible(!_turningOff && _cloudPwd && !has);
}
void PasscodeBox::submit() {
bool has = _cloudPwd ? (!_curSalt.isEmpty()) : Global::LocalPasscode();
const auto has = currentlyHave();
if (_oldPasscode->hasFocus()) {
if (_turningOff) {
save();
@ -177,7 +182,7 @@ void PasscodeBox::paintEvent(QPaintEvent *e) {
void PasscodeBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
bool has = _cloudPwd ? (!_curSalt.isEmpty()) : Global::LocalPasscode();
const auto has = currentlyHave();
int32 w = st::boxWidth - st::boxPadding.left() - st::boxPadding.right();
_oldPasscode->resize(w, _oldPasscode->height());
_oldPasscode->moveToLeft(st::boxPadding.left(), st::passcodePadding.top());
@ -291,7 +296,7 @@ void PasscodeBox::save(bool force) {
if (_setRequest) return;
QString old = _oldPasscode->text(), pwd = _newPasscode->text(), conf = _reenterPasscode->text();
bool has = _cloudPwd ? (!_curSalt.isEmpty()) : Global::LocalPasscode();
const auto has = currentlyHave();
if (!_cloudPwd && (_turningOff || has)) {
if (!passcodeCanTry()) {
_oldError = lang(lng_flood_error);
@ -387,14 +392,14 @@ void PasscodeBox::clearCloudPassword(const QString &oldPassword) {
void PasscodeBox::sendClearCloudPassword(const QString &oldPassword) {
const auto passwordUtf = oldPassword.toUtf8();
const auto oldPasswordData = (_curSalt + passwordUtf + _curSalt);
auto oldPasswordHash = QByteArray(32, Qt::Uninitialized);
hashSha256(oldPasswordData.constData(), oldPasswordData.size(), oldPasswordHash.data());
const auto oldPasswordHash = Core::ComputeCloudPasswordHash(
_curAlgo,
bytes::make_span(passwordUtf));
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
const auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
| MTPDaccount_passwordInputSettings::Flag::f_hint
| MTPDaccount_passwordInputSettings::Flag::f_email;
@ -402,13 +407,11 @@ void PasscodeBox::sendClearCloudPassword(const QString &oldPassword) {
MTP_bytes(oldPasswordHash),
MTP_account_passwordInputSettings(
MTP_flags(flags),
MTP_bytes(_newSalt),
Core::PrepareCloudPasswordAlgo(_newAlgo),
MTP_bytes(newPasswordHash),
MTP_string(hint),
MTP_string(email),
MTPbytes(), // new_secure_salt
MTPbytes(), // new_secure_secret
MTPlong()) // new_secure_secret_id
MTPSecureSecretSettings())
)).done([=](const MTPBool &result) {
setPasswordDone({});
}).fail([=](const RPCError &error) {
@ -418,14 +421,14 @@ void PasscodeBox::sendClearCloudPassword(const QString &oldPassword) {
void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
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 newPasswordHash = Core::ComputeCloudPasswordHash(
_newAlgo,
bytes::make_span(newPasswordBytes));
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
const auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
| MTPDaccount_passwordInputSettings::Flag::f_hint
| MTPDaccount_passwordInputSettings::Flag::f_email;
@ -433,13 +436,11 @@ void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
MTP_bytes(oldPasswordHash),
MTP_account_passwordInputSettings(
MTP_flags(flags),
MTP_bytes(_newSalt),
Core::PrepareCloudPasswordAlgo(_newAlgo),
MTP_bytes(newPasswordHash),
MTP_string(hint),
MTP_string(email),
MTPbytes(), // new_secure_salt
MTPbytes(), // new_secure_secret
MTPlong()) // new_secure_secret_id
MTPSecureSecretSettings())
)).done([=](const MTPBool &result) {
setPasswordDone(newPasswordBytes);
}).fail([=](const RPCError &error) {
@ -451,9 +452,9 @@ 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());
const auto oldPasswordHash = Core::ComputeCloudPasswordHash(
_curAlgo,
bytes::make_span(passwordUtf));
_setRequest = request(MTPaccount_GetPasswordSettings(
MTP_bytes(oldPasswordHash)
)).done([=](const MTPaccount_PasswordSettings &result) {
@ -462,20 +463,23 @@ void PasscodeBox::changeCloudPassword(
Expects(result.type() == mtpc_account_passwordSettings);
const auto &data = result.c_account_passwordSettings();
if (data.vsecure_secret.v.isEmpty()) {
if (!data.has_secure_settings()) {
const auto empty = QByteArray();
sendChangeCloudPassword(oldPasswordHash, newPassword, empty);
return;
}
const auto &wrapped = data.vsecure_settings;
const auto &settings = wrapped.c_secureSecretSettings();
const auto secret = Passport::DecryptSecureSecret(
bytes::make_span(data.vsecure_secret.v),
Passport::CountPasswordHashForSecret(
bytes::make_span(data.vsecure_salt.v),
bytes::make_span(settings.vsecure_secret.v),
Core::ComputeSecureSecretHash(
Core::ParseSecureSecretAlgo(settings.vsecure_algo),
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) {
} else if (Passport::CountSecureSecretId(secret)
!= settings.vsecure_secret_id.v) {
LOG(("API Error: Wrong secure secret id."));
suggestSecretReset(oldPasswordHash, newPassword);
} else {
@ -492,7 +496,7 @@ void PasscodeBox::changeCloudPassword(
}
void PasscodeBox::suggestSecretReset(
const QByteArray &oldPasswordHash,
const bytes::vector &oldPasswordHash,
const QString &newPassword) {
const auto box = std::make_shared<QPointer<BoxContent>>();
const auto resetSecretAndSave = [=] {
@ -500,16 +504,15 @@ void PasscodeBox::suggestSecretReset(
_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
MTP_flags(Flag::f_new_secure_settings),
MTPPasswordKdfAlgo(), // new_algo
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
MTP_secureSecretSettings(
MTP_securePasswordKdfAlgoUnknown(), // secure_algo
MTP_bytes(QByteArray()), // secure_secret
MTP_long(0))) // secure_secret_id
)).done([=](const MTPBool &result) {
_setRequest = 0;
const auto empty = QByteArray();
@ -528,42 +531,41 @@ void PasscodeBox::suggestSecretReset(
}
void PasscodeBox::sendChangeCloudPassword(
const QByteArray &oldPasswordHash,
const bytes::vector &oldPasswordHash,
const QString &newPassword,
const QByteArray &secureSecret) {
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 newPasswordHash = Core::ComputeCloudPasswordHash(
_newAlgo,
bytes::make_span(newPasswordBytes));
const auto hint = _passwordHint->getLastText();
auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_salt
auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo
| 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;
flags |= MTPDaccount_passwordInputSettings::Flag::f_new_secure_settings;
newSecureSecretId = Passport::CountSecureSecretId(
bytes::make_span(secureSecret));
newSecureSecret = Passport::EncryptSecureSecret(
bytes::make_span(secureSecret),
Passport::CountPasswordHashForSecret(
bytes::make_span(_newSecureSecretSalt),
Core::ComputeSecureSecretHash(
_newSecureSecretAlgo,
bytes::make_span(newPasswordBytes)));
}
_setRequest = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(oldPasswordHash),
MTP_account_passwordInputSettings(
MTP_flags(flags),
MTP_bytes(_newSalt),
Core::PrepareCloudPasswordAlgo(_newAlgo),
MTP_bytes(newPasswordHash),
MTP_string(hint),
MTPstring(), // email is not changing
MTP_bytes(_newSecureSecretSalt),
MTP_bytes(newSecureSecret),
MTP_long(newSecureSecretId))
MTP_secureSecretSettings(
Core::PrepareSecureSecretAlgo(_newSecureSecretAlgo),
MTP_bytes(newSecureSecret),
MTP_long(newSecureSecretId)))
)).done([=](const MTPBool &result) {
setPasswordDone(newPasswordBytes);
}).fail([=](const RPCError &error) {

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
#include "mtproto/sender.h"
#include "core/core_cloud_password.h"
namespace Ui {
class InputField;
@ -21,12 +22,12 @@ public:
PasscodeBox(QWidget*, bool turningOff);
PasscodeBox(
QWidget*,
const QByteArray &newSalt,
const QByteArray &curSalt,
const Core::CloudPasswordAlgo &curAlgo,
const Core::CloudPasswordAlgo &newAlgo,
bool hasRecovery,
bool notEmptyPassport,
const QString &hint,
const QByteArray &newSecureSecretSalt,
const Core::SecureSecretAlgo &newSecureSecretAlgo,
bool turningOff = false);
rpl::producer<QByteArray> newPasswordSet() const;
@ -49,6 +50,7 @@ private:
void badOldPasscode();
void recoverByEmail();
void recoverExpired();
bool currentlyHave() const;
void setPasswordDone(const QByteArray &newPasswordBytes);
bool setPasswordFail(const RPCError &error);
@ -66,14 +68,14 @@ private:
const QString &oldPassword,
const QString &newPassword);
void sendChangeCloudPassword(
const QByteArray &oldPasswordHash,
const bytes::vector &oldPasswordHash,
const QString &newPassword,
const QByteArray &secureSecret);
void suggestSecretReset(
const QByteArray &oldPasswordHash,
const bytes::vector &oldPasswordHash,
const QString &newPassword);
void resetSecretAndChangePassword(
const QByteArray &oldPasswordHash,
const bytes::vector &oldPasswordHash,
const QString &newPassword);
void sendClearCloudPassword(const QString &oldPassword);
@ -84,7 +86,9 @@ private:
bool _cloudPwd = false;
mtpRequestId _setRequest = 0;
QByteArray _newSalt, _curSalt, _newSecureSecretSalt;
Core::CloudPasswordAlgo _curAlgo;
Core::CloudPasswordAlgo _newAlgo;
Core::SecureSecretAlgo _newSecureSecretAlgo;
bool _hasRecovery = false;
bool _notEmptyPassport = false;
bool _skipEmailWarning = false;

View File

@ -55,7 +55,7 @@ addChildParentFlags('MTPDchannelForbidden', 'MTPDchannel');
parentFlagsCheck = {};
countedTypeIdExceptions = {};
for i in range(77, 83):
for i in range(77, 84):
countedTypeIdExceptions[i] = {}
countedTypeIdExceptions[i]['channel'] = True
countedTypeIdExceptions['ipPortSecret'] = True

View File

@ -22,6 +22,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace func = base::functors;
using gsl::not_null;
using index_type = gsl::index;
using size_type = gsl::index;
template <typename Signature>
using Fn = std::function<Signature>;

View File

@ -0,0 +1,133 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "core/core_cloud_password.h"
#include "base/openssl_help.h"
namespace Core {
namespace {
constexpr auto kAdditionalSalt = size_type(8);
bytes::vector ComputeHash(
base::none_type,
bytes::const_span password) {
Unexpected("Bad cloud password algorithm.");
}
bytes::vector ComputeHash(
const CloudPasswordAlgoPBKDF2 &algo,
bytes::const_span password) {
const auto hash1 = openssl::Sha256(algo.salt1, password, algo.salt1);
const auto hash2 = openssl::Sha256(algo.salt2, hash1, algo.salt2);
return openssl::Pbkdf2Sha512(
hash2,
bytes::make_span(algo.salt1),
algo.kIterations);
}
bytes::vector ComputeHash(
const SecureSecretAlgoSHA512 &algo,
bytes::const_span password) {
return openssl::Sha512(algo.salt, password, algo.salt);
}
bytes::vector ComputeHash(
const SecureSecretAlgoPBKDF2 &algo,
bytes::const_span password) {
return openssl::Pbkdf2Sha512(password, algo.salt, algo.kIterations);
}
} // namespace
CloudPasswordAlgo ParseCloudPasswordAlgo(const MTPPasswordKdfAlgo &data) {
return data.match([](
const MTPDpasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000 &data) {
return CloudPasswordAlgo(CloudPasswordAlgoPBKDF2{
bytes::make_vector(data.vsalt1.v),
bytes::make_vector(data.vsalt2.v) });
}, [](const MTPDpasswordKdfAlgoUnknown &data) {
return CloudPasswordAlgo();
});
}
CloudPasswordAlgo ValidateNewCloudPasswordAlgo(CloudPasswordAlgo &&parsed) {
if (!parsed.is<CloudPasswordAlgoPBKDF2>()) {
return base::none;
}
auto &value = parsed.get_unchecked<CloudPasswordAlgoPBKDF2>();
const auto already = value.salt1.size();
value.salt1.resize(already + kAdditionalSalt);
bytes::set_random(bytes::make_span(value.salt1).subspan(already));
return std::move(parsed);
}
MTPPasswordKdfAlgo PrepareCloudPasswordAlgo(const CloudPasswordAlgo &data) {
return data.match([](const CloudPasswordAlgoPBKDF2 &data) {
return MTP_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000(
MTP_bytes(data.salt1),
MTP_bytes(data.salt2));
}, [](base::none_type) {
return MTP_passwordKdfAlgoUnknown();
});
}
bytes::vector ComputeCloudPasswordHash(
const CloudPasswordAlgo &algo,
bytes::const_span password) {
return algo.match([&](const auto &data) {
return ComputeHash(data, password);
});
}
SecureSecretAlgo ParseSecureSecretAlgo(
const MTPSecurePasswordKdfAlgo &data) {
return data.match([](
const MTPDsecurePasswordKdfAlgoPBKDF2HMACSHA512iter100000 &data) {
return SecureSecretAlgo(SecureSecretAlgoPBKDF2{
bytes::make_vector(data.vsalt.v) });
}, [](const MTPDsecurePasswordKdfAlgoSHA512 &data) {
return SecureSecretAlgo(SecureSecretAlgoSHA512{
bytes::make_vector(data.vsalt.v) });
}, [](const MTPDsecurePasswordKdfAlgoUnknown &data) {
return SecureSecretAlgo();
});
}
SecureSecretAlgo ValidateNewSecureSecretAlgo(SecureSecretAlgo &&parsed) {
if (!parsed.is<SecureSecretAlgoPBKDF2>()) {
return base::none;
}
auto &value = parsed.get_unchecked<SecureSecretAlgoPBKDF2>();
const auto already = value.salt.size();
value.salt.resize(already + kAdditionalSalt);
bytes::set_random(bytes::make_span(value.salt).subspan(already));
return std::move(parsed);
}
MTPSecurePasswordKdfAlgo PrepareSecureSecretAlgo(
const SecureSecretAlgo &data) {
return data.match([](const SecureSecretAlgoPBKDF2 &data) {
return MTP_securePasswordKdfAlgoPBKDF2HMACSHA512iter100000(
MTP_bytes(data.salt));
}, [](const SecureSecretAlgoSHA512 &data) {
return MTP_securePasswordKdfAlgoSHA512(MTP_bytes(data.salt));
}, [](base::none_type) {
return MTP_securePasswordKdfAlgoUnknown();
});
}
bytes::vector ComputeSecureSecretHash(
const SecureSecretAlgo &algo,
bytes::const_span password) {
return algo.match([&](const auto &data) {
return ComputeHash(data, password);
});
}
} // namespace Core

View File

@ -0,0 +1,73 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/bytes.h"
namespace Core {
struct CloudPasswordAlgoPBKDF2 {
static constexpr auto kIterations = 100000;
bytes::vector salt1;
bytes::vector salt2;
};
inline bool operator==(
const CloudPasswordAlgoPBKDF2 &a,
const CloudPasswordAlgoPBKDF2 &b) {
return (a.salt1 == b.salt1) && (a.salt2 == b.salt2);
}
using CloudPasswordAlgo = base::optional_variant<CloudPasswordAlgoPBKDF2>;
CloudPasswordAlgo ParseCloudPasswordAlgo(const MTPPasswordKdfAlgo &data);
CloudPasswordAlgo ValidateNewCloudPasswordAlgo(CloudPasswordAlgo &&parsed);
MTPPasswordKdfAlgo PrepareCloudPasswordAlgo(const CloudPasswordAlgo &data);
bytes::vector ComputeCloudPasswordHash(
const CloudPasswordAlgo &algo,
bytes::const_span password);
struct SecureSecretAlgoSHA512 {
bytes::vector salt;
};
inline bool operator==(
const SecureSecretAlgoSHA512 &a,
const SecureSecretAlgoSHA512 &b) {
return (a.salt == b.salt);
}
struct SecureSecretAlgoPBKDF2 {
static constexpr auto kIterations = 100000;
bytes::vector salt;
};
inline bool operator==(
const SecureSecretAlgoPBKDF2 &a,
const SecureSecretAlgoPBKDF2 &b) {
return (a.salt == b.salt);
}
using SecureSecretAlgo = base::optional_variant<
SecureSecretAlgoSHA512,
SecureSecretAlgoPBKDF2>;
SecureSecretAlgo ParseSecureSecretAlgo(
const MTPSecurePasswordKdfAlgo &data);
SecureSecretAlgo ValidateNewSecureSecretAlgo(SecureSecretAlgo &&parsed);
MTPSecurePasswordKdfAlgo PrepareSecureSecretAlgo(
const SecureSecretAlgo &data);
bytes::vector ComputeSecureSecretHash(
const SecureSecretAlgo &algo,
bytes::const_span password);
} // namespace Core

View File

@ -11,8 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "application.h"
#include "intro/introsignup.h"
#include "intro/intropwdcheck.h"
#include "core/update_checker.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h"
#include "boxes/confirm_box.h"
#include "styles/style_intro.h"
namespace Intro {
@ -286,22 +288,34 @@ void CodeWidget::callDone(const MTPauth_SentCode &v) {
}
void CodeWidget::gotPassword(const MTPaccount_Password &result) {
Expects(result.type() == mtpc_account_password);
stopCheck();
_sentRequest = 0;
switch (result.type()) {
case mtpc_account_noPassword: { // should not happen
const auto &d = result.c_account_password();
getData()->pwdAlgo = d.has_current_algo()
? Core::ParseCloudPasswordAlgo(d.vcurrent_algo)
: Core::CloudPasswordAlgo();
if (!d.has_current_algo()) {
LOG(("API Error: No current password received on login."));
_code->setFocus();
} break;
case mtpc_account_password: {
auto &d = result.c_account_password();
getData()->pwdSalt = qba(d.vcurrent_salt);
getData()->hasRecovery = d.is_has_recovery();
getData()->pwdHint = qs(d.vhint);
getData()->pwdNotEmptyPassport = d.is_has_secure_values();
goReplace(new Intro::PwdCheckWidget(parentWidget(), getData()));
} break;
return;
} else if (!getData()->pwdAlgo) {
const auto box = std::make_shared<QPointer<BoxContent>>();
const auto callback = [=] {
Core::UpdateApplication();
if (*box) (*box)->closeBox();
};
*box = Ui::show(Box<ConfirmBox>(
lang(lng_passport_app_out_of_date),
lang(lng_menu_update),
callback));
return;
}
getData()->hasRecovery = d.is_has_recovery();
getData()->pwdHint = qs(d.vhint);
getData()->pwdNotEmptyPassport = d.is_has_secure_values();
goReplace(new Intro::PwdCheckWidget(parentWidget(), getData()));
}
void CodeWidget::submit() {
@ -312,7 +326,7 @@ void CodeWidget::submit() {
_checkRequest->start(1000);
_sentCode = _code->getLastText();
getData()->pwdSalt = QByteArray();
getData()->pwdAlgo = Core::CloudPasswordAlgo();
getData()->hasRecovery = false;
getData()->pwdHint = QString();
getData()->pwdNotEmptyPassport = false;

View File

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_intro.h"
#include "styles/style_boxes.h"
#include "core/file_utilities.h"
#include "core/core_cloud_password.h"
#include "boxes/confirm_box.h"
#include "lang/lang_keys.h"
#include "application.h"
@ -17,11 +18,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/labels.h"
#include "base/openssl_help.h"
namespace Intro {
PwdCheckWidget::PwdCheckWidget(QWidget *parent, Widget::Data *data) : Step(parent, data)
, _salt(getData()->pwdSalt)
PwdCheckWidget::PwdCheckWidget(
QWidget *parent,
Widget::Data *data)
: Step(parent, data)
, _algo(getData()->pwdAlgo)
, _hasRecovery(getData()->hasRecovery)
, _notEmptyPassport(getData()->pwdNotEmptyPassport)
, _hint(getData()->pwdHint)
@ -31,6 +36,8 @@ PwdCheckWidget::PwdCheckWidget(QWidget *parent, Widget::Data *data) : Step(paren
, _toRecover(this, lang(lng_signin_recover))
, _toPassword(this, lang(lng_signin_try_password))
, _checkRequest(this) {
Expects(_algo.has_value());
subscribe(Lang::Current().updated(), [this] { refreshLang(); });
connect(_checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest()));
@ -302,9 +309,14 @@ void PwdCheckWidget::submit() {
} else {
hideError();
QByteArray pwdData = _salt + _pwdField->getLastText().toUtf8() + _salt, pwdHash(32, Qt::Uninitialized);
hashSha256(pwdData.constData(), pwdData.size(), pwdHash.data());
_sentRequest = MTP::send(MTPauth_CheckPassword(MTP_bytes(pwdHash)), rpcDone(&PwdCheckWidget::pwdSubmitDone, false), rpcFail(&PwdCheckWidget::pwdSubmitFail));
const auto password = _pwdField->getLastText().toUtf8();
const auto hash = Core::ComputeCloudPasswordHash(
_algo,
bytes::make_span(password));
_sentRequest = MTP::send(
MTPauth_CheckPassword(MTP_bytes(hash)),
rpcDone(&PwdCheckWidget::pwdSubmitDone, false),
rpcFail(&PwdCheckWidget::pwdSubmitFail));
}
}

View File

@ -54,7 +54,7 @@ private:
void updateDescriptionText();
void stopCheck();
QByteArray _salt;
Core::CloudPasswordAlgo _algo;
bool _hasRecovery = false;
bool _notEmptyPassport = false;
QString _hint, _emailPattern;
@ -66,8 +66,6 @@ private:
object_ptr<Ui::LinkButton> _toPassword;
mtpRequestId _sentRequest = 0;
QByteArray _pwdSalt;
object_ptr<QTimer> _checkRequest;
};

View File

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/sender.h"
#include "ui/rp_widget.h"
#include "window/window_lock_widgets.h"
#include "core/core_cloud_password.h"
namespace Ui {
class IconButton;
@ -72,7 +73,7 @@ public:
int codeLength = 5;
bool codeByTelegram = false;
QByteArray pwdSalt;
Core::CloudPasswordAlgo pwdAlgo;
bool hasRecovery = false;
QString pwdHint;
bool pwdNotEmptyPassport = false;

View File

@ -975,10 +975,14 @@ bool Messenger::openLocalUrl(const QString &url, QVariant context) {
};
if (result.is_update_app()) {
const auto box = std::make_shared<QPointer<BoxContent>>();
const auto callback = [=] {
Core::UpdateApplication();
if (*box) (*box)->closeBox();
};
*box = Ui::show(Box<ConfirmBox>(
text,
lang(lng_menu_update),
[=] { Core::UpdateApplication(); if (*box) (*box)->closeBox(); }));
callback));
} else {
Ui::show(Box<InformBox>(text));
}

View File

@ -163,15 +163,6 @@ bytes::vector EncryptSecretBytes(
return Encrypt(secret, std::move(params));
}
bytes::vector CountPasswordHashForSecret(
bytes::const_span salt,
bytes::const_span password) {
return openssl::Sha512(bytes::concatenate(
salt,
password,
salt));
}
bytes::vector DecryptSecureSecret(
bytes::const_span encryptedSecret,
bytes::const_span passwordHashForSecret) {
@ -403,7 +394,7 @@ bytes::vector DecryptData(
bytes::vector PrepareValueHash(
bytes::const_span dataHash,
bytes::const_span valueSecret) {
return openssl::Sha256(bytes::concatenate(dataHash, valueSecret));
return openssl::Sha256(dataHash, valueSecret);
}
bytes::vector EncryptValueSecret(

View File

@ -11,9 +11,6 @@ namespace Passport {
bytes::vector GenerateSecretBytes();
bytes::vector CountPasswordHashForSecret(
bytes::const_span salt,
bytes::const_span password);
bytes::vector EncryptSecureSecret(
bytes::const_span secret,
bytes::const_span passwordHashForSecret);

View File

@ -321,10 +321,7 @@ QString FormController::privacyPolicyUrl() const {
bytes::vector FormController::passwordHashForAuth(
bytes::const_span password) const {
return openssl::Sha256(bytes::concatenate(
_password.salt,
password,
_password.salt));
return Core::ComputeCloudPasswordHash(_password.algo, password);
}
auto FormController::prepareFinalData() -> FinalData {
@ -446,7 +443,7 @@ std::vector<not_null<const Value*>> FormController::submitGetErrors() {
}
void FormController::submitPassword(const QByteArray &password) {
Expects(!_password.salt.empty());
Expects(_password.algo.has_value());
const auto submitSaved = !base::take(_savedPasswordValue).isEmpty();
if (_passwordCheckRequestId) {
@ -463,28 +460,42 @@ void FormController::submitPassword(const QByteArray &password) {
_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)));
const auto &data = result.c_account_passwordSettings();
_password.confirmedEmail = qs(data.vemail);
validateSecureSecret(
bytes::make_span(data.vsecure_secret.v),
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);
if (data.has_secure_settings()) {
const auto &wrapped = data.vsecure_settings;
const auto &settings = wrapped.c_secureSecretSettings();
const auto algo = Core::ParseSecureSecretAlgo(
settings.vsecure_algo);
if (!algo) {
_view->showUpdateAppBox();
return;
}
const auto hashForSecret = Core::ComputeSecureSecretHash(
algo,
bytes::make_span(password));
validateSecureSecret(
bytes::make_span(settings.vsecure_secret.v),
hashForSecret,
bytes::make_span(password),
settings.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);
}
} else {
validateSecureSecret(
bytes::const_span(), // secure_secret
bytes::const_span(), // hash for secret
bytes::make_span(password),
0); // secure_secret_id
}
}).fail([=](const RPCError &error) {
_passwordCheckRequestId = 0;
@ -511,14 +522,23 @@ void FormController::checkSavedPasswordSettings(
_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 (data.has_secure_settings()) {
const auto &wrapped = data.vsecure_settings;
const auto &settings = wrapped.c_secureSecretSettings();
const auto algo = Core::ParseSecureSecretAlgo(
settings.vsecure_algo);
if (!algo) {
_view->showUpdateAppBox();
return;
} else if (!settings.vsecure_secret.v.isEmpty()
&& settings.vsecure_secret_id.v == credentials.secretId) {
_password.confirmedEmail = qs(data.vemail);
validateSecureSecret(
bytes::make_span(settings.vsecure_secret.v),
credentials.hashForSecret,
{},
settings.vsecure_secret_id.v);
}
}
if (_secret.empty()) {
Auth().data().forgetPassportCredentials();
@ -584,13 +604,11 @@ void FormController::cancelPassword() {
MTP_bytes(QByteArray()),
MTP_account_passwordInputSettings(
MTP_flags(MTPDaccount_passwordInputSettings::Flag::f_email),
MTP_bytes(QByteArray()), // new_salt
MTP_passwordKdfAlgoUnknown(), // new_algo
MTP_bytes(QByteArray()), // new_password_hash
MTP_string(QString()), // hint
MTP_string(QString()), // email
MTP_bytes(QByteArray()), // new_secure_salt
MTP_bytes(QByteArray()), // new_secure_secret
MTP_long(0)) // new_secure_secret_hash
MTPSecureSecretSettings())
)).done([=](const MTPBool &result) {
_passwordRequestId = 0;
reloadPassword();
@ -648,16 +666,15 @@ void FormController::suggestReset(bytes::vector password) {
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(passwordHashForAuth(password)),
MTP_account_passwordInputSettings(
MTP_flags(Flag::f_new_secure_salt
| Flag::f_new_secure_secret
| Flag::f_new_secure_secret_id),
MTPbytes(), // new_salt
MTP_flags(Flag::f_new_secure_settings),
MTPPasswordKdfAlgo(), // new_algo
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
MTP_secureSecretSettings(
MTP_securePasswordKdfAlgoUnknown(), // secure_algo
MTP_bytes(QByteArray()), // secure_secret
MTP_long(0))) // secure_secret_id
)).done([=](const MTPBool &result) {
_saveSecretRequestId = 0;
generateSecret(password);
@ -1774,16 +1791,10 @@ void FormController::generateSecret(bytes::const_span password) {
}
auto secret = GenerateSecretBytes();
auto randomSaltPart = bytes::vector(8);
bytes::set_random(randomSaltPart);
auto newSecureSaltFull = bytes::concatenate(
_password.newSecureSalt,
randomSaltPart);
auto saved = SavedCredentials();
saved.hashForAuth = passwordHashForAuth(password);
saved.hashForSecret = CountPasswordHashForSecret(
newSecureSaltFull,
saved.hashForSecret = Core::ComputeSecureSecretHash(
_password.newSecureAlgo,
password);
saved.secretId = CountSecureSecretId(secret);
@ -1795,16 +1806,15 @@ void FormController::generateSecret(bytes::const_span password) {
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(saved.hashForAuth),
MTP_account_passwordInputSettings(
MTP_flags(Flag::f_new_secure_salt
| Flag::f_new_secure_secret
| Flag::f_new_secure_secret_id),
MTPbytes(), // new_salt
MTP_flags(Flag::f_new_secure_settings),
MTPPasswordKdfAlgo(), // new_algo
MTPbytes(), // new_password_hash
MTPstring(), // hint
MTPstring(), // email
MTP_bytes(newSecureSaltFull),
MTP_bytes(encryptedSecret),
MTP_long(saved.secretId))
MTP_secureSecretSettings(
Core::PrepareSecureSecretAlgo(_password.newSecureAlgo),
MTP_bytes(encryptedSecret),
MTP_long(saved.secretId)))
)).done([=](const MTPBool &result) {
Auth().data().rememberPassportCredentials(
std::move(saved),
@ -2089,15 +2099,9 @@ void FormController::requestPassword() {
}
void FormController::passwordDone(const MTPaccount_Password &result) {
const auto changed = [&] {
switch (result.type()) {
case mtpc_account_noPassword:
return applyPassword(result.c_account_noPassword());
case mtpc_account_password:
return applyPassword(result.c_account_password());
}
Unexpected("Type in FormController::passwordDone.");
}();
Expects(result.type() == mtpc_account_password);
const auto changed = applyPassword(result.c_account_password());
if (changed && !_formRequestId) {
showForm();
}
@ -2117,7 +2121,12 @@ void FormController::showForm() {
formFail(Lang::Hard::NoAuthorizationBot());
return;
}
if (!_password.salt.empty()) {
if (_password.unknownAlgo
|| !_password.newAlgo
|| !_password.newSecureAlgo) {
_view->showUpdateAppBox();
return;
} else if (_password.algo) {
if (!_savedPasswordValue.isEmpty()) {
submitPassword(base::duplicate(_savedPasswordValue));
} else if (const auto saved = Auth().data().passportCredentials()) {
@ -2130,24 +2139,23 @@ void FormController::showForm() {
}
}
bool FormController::applyPassword(const MTPDaccount_noPassword &result) {
auto settings = PasswordSettings();
settings.unconfirmedPattern = qs(result.vemail_unconfirmed_pattern);
settings.newSalt = bytes::make_vector(result.vnew_salt.v);
settings.newSecureSalt = bytes::make_vector(result.vnew_secure_salt.v);
openssl::AddRandomSeed(bytes::make_span(result.vsecure_random.v));
return applyPassword(std::move(settings));
}
bool FormController::applyPassword(const MTPDaccount_password &result) {
auto settings = PasswordSettings();
settings.hint = qs(result.vhint);
settings.hasRecovery = result.is_has_recovery();
settings.notEmptyPassport = result.is_has_secure_values();
settings.salt = bytes::make_vector(result.vcurrent_salt.v);
settings.unconfirmedPattern = qs(result.vemail_unconfirmed_pattern);
settings.newSalt = bytes::make_vector(result.vnew_salt.v);
settings.newSecureSalt = bytes::make_vector(result.vnew_secure_salt.v);
settings.algo = result.has_current_algo()
? Core::ParseCloudPasswordAlgo(result.vcurrent_algo)
: Core::CloudPasswordAlgo();
settings.unknownAlgo = result.has_current_algo()
&& !settings.algo;
settings.unconfirmedPattern = result.has_email_unconfirmed_pattern()
? qs(result.vemail_unconfirmed_pattern)
: QString();
settings.newAlgo = Core::ValidateNewCloudPasswordAlgo(
Core::ParseCloudPasswordAlgo(result.vnew_algo));
settings.newSecureAlgo = Core::ValidateNewSecureSecretAlgo(
Core::ParseSecureSecretAlgo(result.vnew_secure_algo));
openssl::AddRandomSeed(bytes::make_span(result.vsecure_random.v));
return applyPassword(std::move(settings));
}

View File

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/sender.h"
#include "boxes/confirm_phone_box.h"
#include "base/weak_ptr.h"
#include "core/core_cloud_password.h"
class BoxContent;
@ -195,23 +196,25 @@ struct Form {
};
struct PasswordSettings {
bytes::vector salt;
bytes::vector newSalt;
bytes::vector newSecureSalt;
Core::CloudPasswordAlgo algo;
Core::CloudPasswordAlgo newAlgo;
Core::SecureSecretAlgo newSecureAlgo;
QString hint;
QString unconfirmedPattern;
QString confirmedEmail;
bool hasRecovery = false;
bool notEmptyPassport = false;
bool unknownAlgo = false;
bool operator==(const PasswordSettings &other) const {
return (salt == other.salt)
&& (newSalt == other.newSalt)
&& (newSecureSalt == other.newSecureSalt)
return (algo == other.algo)
&& (newAlgo == other.newAlgo)
&& (newSecureAlgo == other.newSecureAlgo)
&& (hint == other.hint)
&& (unconfirmedPattern == other.unconfirmedPattern)
&& (confirmedEmail == other.confirmedEmail)
&& (hasRecovery == other.hasRecovery);
&& (hasRecovery == other.hasRecovery)
&& (unknownAlgo == other.unknownAlgo);
}
bool operator!=(const PasswordSettings &other) const {
return !(*this == other);
@ -338,7 +341,6 @@ private:
const std::vector<EditFile> &source) const;
void passwordDone(const MTPaccount_Password &result);
bool applyPassword(const MTPDaccount_noPassword &settings);
bool applyPassword(const MTPDaccount_password &settings);
bool applyPassword(PasswordSettings &&settings);
bytes::vector passwordHashForAuth(bytes::const_span password) const;

View File

@ -261,7 +261,17 @@ ScopeRow ComputeScopeRow(const Scope &scope) {
}
}
};
ranges::for_each(scope.documents, addValueErrors);
const auto document = [&]() -> const Value* {
for (const auto &document : scope.documents) {
if (document->scansAreFilled(scope.selfieRequired)) {
return document;
}
}
return nullptr;
}();
if (document) {
addValueErrors(document);
}
addValueErrors(scope.fields);
if (!errors.isEmpty()) {
row.error = lang(lng_passport_fix_errors);// errors.join('\n');

View File

@ -467,36 +467,26 @@ void PanelController::setupPassword() {
Expects(_panel != nullptr);
const auto &settings = _form->passwordSettings();
if (!settings.salt.empty()) {
if (settings.unknownAlgo
|| !settings.newAlgo
|| !settings.newSecureAlgo) {
showUpdateAppBox();
return;
} else if (settings.algo) {
showAskPassword();
return;
}
constexpr auto kRandomPart = 8;
auto newPasswordSalt = QByteArray(
reinterpret_cast<const char*>(settings.newSalt.data()),
settings.newSalt.size());
newPasswordSalt.resize(newPasswordSalt.size() + kRandomPart);
bytes::set_random(
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 notEmptyPassport = false;
const auto hint = QString();
auto box = show(Box<PasscodeBox>(
newPasswordSalt,
currentSalt,
Core::CloudPasswordAlgo(), // current algo
settings.newAlgo,
hasRecovery,
notEmptyPassport,
hint,
newSecureSecretSalt));
settings.newSecureAlgo));
box->newPasswordSet(
) | rpl::filter([=](const QByteArray &password) {
return !password.isEmpty();
@ -816,12 +806,11 @@ void PanelController::showCriticalError(const QString &error) {
void PanelController::showUpdateAppBox() {
ensurePanelCreated();
const auto box = std::make_shared<QPointer<BoxContent>>();
const auto callback = [=] {
_form->cancelSure();
Core::UpdateApplication();
};
*box = show(
show(
Box<ConfirmBox>(
lang(lng_passport_app_out_of_date),
lang(lng_menu_update),
@ -952,6 +941,9 @@ void PanelController::editWithUpload(int index, int documentIndex) {
const auto allowMany = !requiresSpecialScan;
const auto widget = _panel->widget();
EditScans::ChooseScan(widget.get(), [=](QByteArray &&content) {
if (_scopeDocumentTypeBox) {
_scopeDocumentTypeBox = BoxPointer();
}
if (!_editScope || !_editDocument) {
editScope(index, documentIndex);
}

View File

@ -15,7 +15,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "auth_session.h"
#include "data/data_session.h"
#include "platform/platform_specific.h"
#include "core/update_checker.h"
#include "base/openssl_help.h"
#include "boxes/confirm_box.h"
#include "boxes/sessions_box.h"
#include "boxes/passcode_box.h"
#include "boxes/autolock_box.h"
@ -76,13 +78,27 @@ int CloudPasswordState::resizeGetHeight(int newWidth) {
}
void CloudPasswordState::onEdit() {
if (_unknownPasswordAlgo
|| !_newPasswordAlgo
|| !_newSecureSecretAlgo) {
const auto box = std::make_shared<QPointer<BoxContent>>();
const auto callback = [=] {
Core::UpdateApplication();
if (*box) (*box)->closeBox();
};
*box = Ui::show(Box<ConfirmBox>(
lang(lng_passport_app_out_of_date),
lang(lng_menu_update),
callback));
return;
}
auto box = Ui::show(Box<PasscodeBox>(
_newPasswordSalt,
_curPasswordSalt,
_curPasswordAlgo,
_newPasswordAlgo,
_hasPasswordRecovery,
_notEmptyPassport,
_curPasswordHint,
_newSecureSecretSalt));
_newSecureSecretAlgo));
rpl::merge(
box->newPasswordSet() | rpl::map([] { return rpl::empty_value(); }),
box->passwordReloadNeeded()
@ -92,31 +108,44 @@ void CloudPasswordState::onEdit() {
}
void CloudPasswordState::onTurnOff() {
if (_curPasswordSalt.isEmpty()) {
if (_unknownPasswordAlgo
|| !_newPasswordAlgo
|| !_newSecureSecretAlgo) {
const auto box = std::make_shared<QPointer<BoxContent>>();
const auto callback = [=] {
Core::UpdateApplication();
if (*box) (*box)->closeBox();
};
*box = Ui::show(Box<ConfirmBox>(
lang(lng_passport_app_out_of_date),
lang(lng_menu_update),
callback));
return;
}
if (!_curPasswordAlgo) {
_turnOff->hide();
MTP::send(
MTPaccount_UpdatePasswordSettings(
MTP_bytes(QByteArray()),
MTP_account_passwordInputSettings(
MTP_flags(MTPDaccount_passwordInputSettings::Flag::f_email),
MTP_bytes(QByteArray()), // new_salt
MTP_flags(
MTPDaccount_passwordInputSettings::Flag::f_email),
MTP_passwordKdfAlgoUnknown(), // new_algo
MTP_bytes(QByteArray()), // new_password_hash
MTP_string(QString()), // hint
MTP_string(QString()), // email
MTP_bytes(QByteArray()), // new_secure_salt
MTP_bytes(QByteArray()), // new_secure_secret
MTP_long(0))), // new_secure_secret_hash
MTPSecureSecretSettings())),
rpcDone(&CloudPasswordState::offPasswordDone),
rpcFail(&CloudPasswordState::offPasswordFail));
} else {
auto box = Ui::show(Box<PasscodeBox>(
_newPasswordSalt,
_curPasswordSalt,
_curPasswordAlgo,
_newPasswordAlgo,
_hasPasswordRecovery,
_notEmptyPassport,
_curPasswordHint,
_newSecureSecretSalt,
_newSecureSecretAlgo,
true));
rpl::merge(
box->newPasswordSet(
@ -142,53 +171,41 @@ void CloudPasswordState::onReloadPassword(Qt::ApplicationState state) {
}
void CloudPasswordState::getPasswordDone(const MTPaccount_Password &result) {
Expects(result.type() == mtpc_account_password);
_reloadRequestId = 0;
_waitingConfirm = QString();
switch (result.type()) {
case mtpc_account_noPassword: {
auto &d = result.c_account_noPassword();
_curPasswordSalt = QByteArray();
_hasPasswordRecovery = false;
_notEmptyPassport = 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: {
auto &d = result.c_account_password();
_curPasswordSalt = qba(d.vcurrent_salt);
_hasPasswordRecovery = d.is_has_recovery();
_notEmptyPassport = d.is_has_secure_values();
_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;
const auto &d = result.c_account_password();
_curPasswordAlgo = d.has_current_algo()
? Core::ParseCloudPasswordAlgo(d.vcurrent_algo)
: Core::CloudPasswordAlgo();
_unknownPasswordAlgo = d.has_current_algo() && !_curPasswordAlgo;
_hasPasswordRecovery = d.is_has_recovery();
_notEmptyPassport = d.is_has_secure_values();
_curPasswordHint = qs(d.vhint);
_newPasswordAlgo = Core::ValidateNewCloudPasswordAlgo(
Core::ParseCloudPasswordAlgo(d.vnew_algo));
_newSecureSecretAlgo = Core::ValidateNewSecureSecretAlgo(
Core::ParseSecureSecretAlgo(d.vnew_secure_algo));
const auto pattern = d.has_email_unconfirmed_pattern()
? qs(d.vemail_unconfirmed_pattern)
: QString();
if (!pattern.isEmpty()) {
_waitingConfirm = lng_cloud_password_waiting(lt_email, pattern);
}
_edit->setText(lang(_curPasswordSalt.isEmpty() ? lng_cloud_password_set : lng_cloud_password_edit));
_edit->setVisible(_waitingConfirm.isEmpty());
_turnOff->setVisible(!_waitingConfirm.isEmpty() || !_curPasswordSalt.isEmpty());
update();
openssl::AddRandomSeed(bytes::make_span(d.vsecure_random.v));
_newPasswordSalt.resize(_newPasswordSalt.size() + 8);
memset_rand(
_newPasswordSalt.data() + _newPasswordSalt.size() - 8,
8);
_newSecureSecretSalt.resize(_newSecureSecretSalt.size() + 8);
memset_rand(
_newSecureSecretSalt.data() + _newSecureSecretSalt.size() - 8,
8);
_edit->setText(lang(hasCloudPassword()
? lng_cloud_password_edit
: lng_cloud_password_set));
_edit->setVisible(_waitingConfirm.isEmpty());
_turnOff->setVisible(!_waitingConfirm.isEmpty() || hasCloudPassword());
update();
}
bool CloudPasswordState::hasCloudPassword() const {
return (_curPasswordAlgo || _unknownPasswordAlgo);
}
bool CloudPasswordState::getPasswordFail(const RPCError &error) {

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_block_widget.h"
#include "settings/settings_chat_settings_widget.h"
#include "core/core_cloud_password.h"
#include "ui/rp_widget.h"
namespace Settings {
@ -59,16 +60,19 @@ private:
void offPasswordDone(const MTPBool &result);
bool offPasswordFail(const RPCError &error);
bool hasCloudPassword() const;
object_ptr<Ui::LinkButton> _edit;
object_ptr<Ui::LinkButton> _turnOff;
QString _waitingConfirm;
QByteArray _curPasswordSalt;
Core::CloudPasswordAlgo _curPasswordAlgo;
bool _unknownPasswordAlgo = false;
bool _hasPasswordRecovery = false;
bool _notEmptyPassport = false;
QString _curPasswordHint;
QByteArray _newPasswordSalt;
QByteArray _newSecureSecretSalt;
Core::CloudPasswordAlgo _newPasswordAlgo;
Core::SecureSecretAlgo _newSecureSecretAlgo;
mtpRequestId _reloadRequestId = 0;
};

View File

@ -143,6 +143,8 @@
<(src_loc)/core/click_handler.h
<(src_loc)/core/click_handler_types.cpp
<(src_loc)/core/click_handler_types.h
<(src_loc)/core/core_cloud_password.cpp
<(src_loc)/core/core_cloud_password.h
<(src_loc)/core/crash_report_window.cpp
<(src_loc)/core/crash_report_window.h
<(src_loc)/core/crash_reports.cpp