From 550c159ca8ee1c39bdb3d86f706357b4e4c2fb12 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 4 Aug 2018 00:48:00 +0300 Subject: [PATCH] Update API scheme to layer 83. --- Telegram/Resources/scheme.tl | 24 ++- Telegram/SourceFiles/base/openssl_help.h | 143 +++++++++++++-- Telegram/SourceFiles/boxes/passcode_box.cpp | 124 ++++++------- Telegram/SourceFiles/boxes/passcode_box.h | 18 +- .../codegen/scheme/codegen_scheme.py | 2 +- Telegram/SourceFiles/core/basic_types.h | 2 + .../SourceFiles/core/core_cloud_password.cpp | 133 ++++++++++++++ .../SourceFiles/core/core_cloud_password.h | 73 ++++++++ Telegram/SourceFiles/intro/introcode.cpp | 40 +++-- Telegram/SourceFiles/intro/intropwdcheck.cpp | 22 ++- Telegram/SourceFiles/intro/intropwdcheck.h | 4 +- Telegram/SourceFiles/intro/introwidget.h | 3 +- Telegram/SourceFiles/messenger.cpp | 6 +- .../passport/passport_encryption.cpp | 11 +- .../passport/passport_encryption.h | 3 - .../passport/passport_form_controller.cpp | 170 +++++++++--------- .../passport/passport_form_controller.h | 18 +- .../passport_form_view_controller.cpp | 12 +- .../passport/passport_panel_controller.cpp | 34 ++-- .../settings/settings_privacy_widget.cpp | 125 +++++++------ .../settings/settings_privacy_widget.h | 10 +- Telegram/gyp/telegram_sources.txt | 2 + 22 files changed, 683 insertions(+), 296 deletions(-) create mode 100644 Telegram/SourceFiles/core/core_cloud_password.cpp create mode 100644 Telegram/SourceFiles/core/core_cloud_password.h diff --git a/Telegram/Resources/scheme.tl b/Telegram/Resources/scheme.tl index 92b32b113f..b570aa79eb 100644 --- a/Telegram/Resources/scheme.tl +++ b/Telegram/Resources/scheme.tl @@ -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 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 = 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; messages.markDialogUnread#c286d98f flags:# unread:flags.0?true peer:InputDialogPeer = Bool; messages.getDialogUnreadMarks#22e24e22 = Vector; +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 = Bool; help.getInviteText#4d392343 = help.InviteText; help.getSupport#9cdf08cd = help.Support; @@ -1308,4 +1318,4 @@ langpack.getStrings#2e1ee318 lang_code:string keys:Vector = Vector; -// LAYER 82 +// LAYER 83 diff --git a/Telegram/SourceFiles/base/openssl_help.h b/Telegram/SourceFiles/base/openssl_help.h index 132ecb7b11..3ca53719e6 100644 --- a/Telegram/SourceFiles/base/openssl_help.h +++ b/Telegram/SourceFiles/base/openssl_help.h @@ -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 #include #include #include +#include #include +#include } // 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 +inline void ShaUpdate(Context context, Method method, Arg &&arg) { + const auto span = bytes::make_span(arg); + method(context, span.data(), span.size()); +} + +template +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 +inline bytes::vector Sha(Method method, bytes::const_span data) { + auto result = bytes::vector(Size); + method( reinterpret_cast(data.data()), data.size(), reinterpret_cast(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(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(password.data()), + password.size(), + reinterpret_cast(salt.data()), + salt.size(), + iterations, + evp, + result.size(), + reinterpret_cast(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(SHA1, data); +} + +template < + typename ...Args, + typename = std::enable_if_t<(sizeof...(Args) > 1)>> +inline bytes::vector Sha1(Args &&...args) { + return details::Sha( + 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(data.data()), - data.size(), - reinterpret_cast(result.data())); - return result; + return details::Sha(SHA256, data); } -inline bytes::vector Sha1(bytes::const_span data) { - auto result = bytes::vector(SHA_DIGEST_LENGTH); - SHA1( - reinterpret_cast(data.data()), - data.size(), - reinterpret_cast(result.data())); - return result; +template < + typename ...Args, + typename = std::enable_if_t<(sizeof...(Args) > 1)>> +inline bytes::vector Sha256(Args &&...args) { + return details::Sha( + SHA256_CTX(), + SHA256_Init, + SHA256_Update, + SHA256_Final, + args...); +} + +inline bytes::vector Sha512(bytes::const_span data) { + return details::Sha(SHA512, data); +} + +template < + typename ...Args, + typename = std::enable_if_t<(sizeof...(Args) > 1)>> +inline bytes::vector Sha512(Args &&...args) { + return details::Sha( + 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( + password, + salt, + iterations, + EVP_sha512()); +} + } // namespace openssl namespace bytes { diff --git a/Telegram/SourceFiles/boxes/passcode_box.cpp b/Telegram/SourceFiles/boxes/passcode_box.cpp index 0c28fbd64f..64c15bebfe 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.cpp +++ b/Telegram/SourceFiles/boxes/passcode_box.cpp @@ -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>(); 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) { diff --git a/Telegram/SourceFiles/boxes/passcode_box.h b/Telegram/SourceFiles/boxes/passcode_box.h index 1ce793c2b0..78f43d6d8d 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.h +++ b/Telegram/SourceFiles/boxes/passcode_box.h @@ -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 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; diff --git a/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py b/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py index fd3923123b..1cb7e6df71 100644 --- a/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py +++ b/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py @@ -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 diff --git a/Telegram/SourceFiles/core/basic_types.h b/Telegram/SourceFiles/core/basic_types.h index 309ff41b4a..4345bebfbe 100644 --- a/Telegram/SourceFiles/core/basic_types.h +++ b/Telegram/SourceFiles/core/basic_types.h @@ -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 using Fn = std::function; diff --git a/Telegram/SourceFiles/core/core_cloud_password.cpp b/Telegram/SourceFiles/core/core_cloud_password.cpp new file mode 100644 index 0000000000..b8da67f69c --- /dev/null +++ b/Telegram/SourceFiles/core/core_cloud_password.cpp @@ -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()) { + return base::none; + } + auto &value = parsed.get_unchecked(); + 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()) { + return base::none; + } + auto &value = parsed.get_unchecked(); + 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 \ No newline at end of file diff --git a/Telegram/SourceFiles/core/core_cloud_password.h b/Telegram/SourceFiles/core/core_cloud_password.h new file mode 100644 index 0000000000..6856eeff1e --- /dev/null +++ b/Telegram/SourceFiles/core/core_cloud_password.h @@ -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; + +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 diff --git a/Telegram/SourceFiles/intro/introcode.cpp b/Telegram/SourceFiles/intro/introcode.cpp index b071d2a849..7b62e3322d 100644 --- a/Telegram/SourceFiles/intro/introcode.cpp +++ b/Telegram/SourceFiles/intro/introcode.cpp @@ -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>(); + const auto callback = [=] { + Core::UpdateApplication(); + if (*box) (*box)->closeBox(); + }; + *box = Ui::show(Box( + 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; diff --git a/Telegram/SourceFiles/intro/intropwdcheck.cpp b/Telegram/SourceFiles/intro/intropwdcheck.cpp index dfa910ce2c..eb6bd446be 100644 --- a/Telegram/SourceFiles/intro/intropwdcheck.cpp +++ b/Telegram/SourceFiles/intro/intropwdcheck.cpp @@ -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)); } } diff --git a/Telegram/SourceFiles/intro/intropwdcheck.h b/Telegram/SourceFiles/intro/intropwdcheck.h index 3f5cc3b7fe..8ca541b17b 100644 --- a/Telegram/SourceFiles/intro/intropwdcheck.h +++ b/Telegram/SourceFiles/intro/intropwdcheck.h @@ -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 _toPassword; mtpRequestId _sentRequest = 0; - QByteArray _pwdSalt; - object_ptr _checkRequest; }; diff --git a/Telegram/SourceFiles/intro/introwidget.h b/Telegram/SourceFiles/intro/introwidget.h index aa20459c64..7effa12071 100644 --- a/Telegram/SourceFiles/intro/introwidget.h +++ b/Telegram/SourceFiles/intro/introwidget.h @@ -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; diff --git a/Telegram/SourceFiles/messenger.cpp b/Telegram/SourceFiles/messenger.cpp index 29e81540d2..11cedf357f 100644 --- a/Telegram/SourceFiles/messenger.cpp +++ b/Telegram/SourceFiles/messenger.cpp @@ -975,10 +975,14 @@ bool Messenger::openLocalUrl(const QString &url, QVariant context) { }; if (result.is_update_app()) { const auto box = std::make_shared>(); + const auto callback = [=] { + Core::UpdateApplication(); + if (*box) (*box)->closeBox(); + }; *box = Ui::show(Box( text, lang(lng_menu_update), - [=] { Core::UpdateApplication(); if (*box) (*box)->closeBox(); })); + callback)); } else { Ui::show(Box(text)); } diff --git a/Telegram/SourceFiles/passport/passport_encryption.cpp b/Telegram/SourceFiles/passport/passport_encryption.cpp index 57fb485ebe..5e743b4fed 100644 --- a/Telegram/SourceFiles/passport/passport_encryption.cpp +++ b/Telegram/SourceFiles/passport/passport_encryption.cpp @@ -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( diff --git a/Telegram/SourceFiles/passport/passport_encryption.h b/Telegram/SourceFiles/passport/passport_encryption.h index 2c54d3ef26..2c5e653fe7 100644 --- a/Telegram/SourceFiles/passport/passport_encryption.h +++ b/Telegram/SourceFiles/passport/passport_encryption.h @@ -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); diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index 10ad1762f8..22ca4cf9d9 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -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> 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)); } diff --git a/Telegram/SourceFiles/passport/passport_form_controller.h b/Telegram/SourceFiles/passport/passport_form_controller.h index d461ce09a1..ec76264433 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.h +++ b/Telegram/SourceFiles/passport/passport_form_controller.h @@ -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 &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; diff --git a/Telegram/SourceFiles/passport/passport_form_view_controller.cpp b/Telegram/SourceFiles/passport/passport_form_view_controller.cpp index e566cb8623..d83ca197bb 100644 --- a/Telegram/SourceFiles/passport/passport_form_view_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_view_controller.cpp @@ -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'); diff --git a/Telegram/SourceFiles/passport/passport_panel_controller.cpp b/Telegram/SourceFiles/passport/passport_panel_controller.cpp index 56b9ad1b81..43123e6c7b 100644 --- a/Telegram/SourceFiles/passport/passport_panel_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_controller.cpp @@ -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(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(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( - 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>(); const auto callback = [=] { _form->cancelSure(); Core::UpdateApplication(); }; - *box = show( + show( Box( 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); } diff --git a/Telegram/SourceFiles/settings/settings_privacy_widget.cpp b/Telegram/SourceFiles/settings/settings_privacy_widget.cpp index 5b5ee2b10a..5ad4385de8 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_widget.cpp @@ -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>(); + const auto callback = [=] { + Core::UpdateApplication(); + if (*box) (*box)->closeBox(); + }; + *box = Ui::show(Box( + lang(lng_passport_app_out_of_date), + lang(lng_menu_update), + callback)); + return; + } auto box = Ui::show(Box( - _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>(); + const auto callback = [=] { + Core::UpdateApplication(); + if (*box) (*box)->closeBox(); + }; + *box = Ui::show(Box( + 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( - _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) { diff --git a/Telegram/SourceFiles/settings/settings_privacy_widget.h b/Telegram/SourceFiles/settings/settings_privacy_widget.h index 59e840e988..15c42fea8c 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_widget.h +++ b/Telegram/SourceFiles/settings/settings_privacy_widget.h @@ -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 _edit; object_ptr _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; }; diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 7a103286bd..59ab00e8ee 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -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