diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index b33afa76a..df32f7241 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -280,11 +280,11 @@ void ApiWrap::refreshProxyPromotion() { return; } const auto key = [&]() -> std::pair { - if (Global::ProxySettings() != ProxyData::Settings::Enabled) { + if (Global::ProxySettings() != MTP::ProxyData::Settings::Enabled) { return {}; } const auto &proxy = Global::SelectedProxy(); - if (proxy.type != ProxyData::Type::Mtproto) { + if (proxy.type != MTP::ProxyData::Type::Mtproto) { return {}; } return { proxy.host, proxy.port }; diff --git a/Telegram/SourceFiles/boxes/connection_box.cpp b/Telegram/SourceFiles/boxes/connection_box.cpp index 663739f26..2b9cd94cb 100644 --- a/Telegram/SourceFiles/boxes/connection_box.cpp +++ b/Telegram/SourceFiles/boxes/connection_box.cpp @@ -38,6 +38,8 @@ namespace { constexpr auto kSaveSettingsDelayedTimeout = crl::time(1000); +using ProxyData = MTP::ProxyData; + class Base64UrlInput : public Ui::MaskedInputField { public: Base64UrlInput( diff --git a/Telegram/SourceFiles/boxes/connection_box.h b/Telegram/SourceFiles/boxes/connection_box.h index 37e7040f2..21d79f9ad 100644 --- a/Telegram/SourceFiles/boxes/connection_box.h +++ b/Telegram/SourceFiles/boxes/connection_box.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/timer.h" #include "base/object_ptr.h" #include "mtproto/connection_abstract.h" +#include "mtproto/mtproto_proxy_data.h" namespace Ui { class BoxContent; @@ -25,6 +26,7 @@ class Radioenum; class ProxiesBoxController : public base::Subscriber { public: + using ProxyData = MTP::ProxyData; using Type = ProxyData::Type; ProxiesBoxController(); diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index 428ebf94f..7539f1544 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/rate_call_box.h" #include "calls/calls_instance.h" #include "base/openssl_help.h" -#include "mtproto/connection.h" +#include "mtproto/mtproto_dh_utils.h" #include "media/audio/media_audio_track.h" #include "base/platform/base_platform_info.h" #include "calls/calls_panel.h" @@ -623,10 +623,10 @@ void Call::createAndStartController(const MTPDphoneCall &call) { _controller->SetEncryptionKey(reinterpret_cast(_authKey.data()), (_type == Type::Outgoing)); _controller->SetCallbacks(callbacks); if (Global::UseProxyForCalls() - && (Global::ProxySettings() == ProxyData::Settings::Enabled)) { + && (Global::ProxySettings() == MTP::ProxyData::Settings::Enabled)) { const auto &proxy = Global::SelectedProxy(); if (proxy.supportsCalls()) { - Assert(proxy.type == ProxyData::Type::Socks5); + Assert(proxy.type == MTP::ProxyData::Type::Socks5); _controller->SetProxy( tgvoip::PROXY_SOCKS5, proxy.host.toStdString(), diff --git a/Telegram/SourceFiles/calls/calls_instance.cpp b/Telegram/SourceFiles/calls/calls_instance.cpp index 5154c8019..7da4cef5e 100644 --- a/Telegram/SourceFiles/calls/calls_instance.cpp +++ b/Telegram/SourceFiles/calls/calls_instance.cpp @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "calls/calls_instance.h" -#include "mtproto/connection.h" +#include "mtproto/mtproto_dh_utils.h" #include "core/application.h" #include "main/main_session.h" #include "apiwrap.h" diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index bd74ebb48..916af7b22 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -370,12 +370,12 @@ void Application::saveSettingsDelayed(crl::time delay) { } void Application::setCurrentProxy( - const ProxyData &proxy, - ProxyData::Settings settings) { + const MTP::ProxyData &proxy, + MTP::ProxyData::Settings settings) { const auto current = [&] { - return (Global::ProxySettings() == ProxyData::Settings::Enabled) + return (Global::ProxySettings() == MTP::ProxyData::Settings::Enabled) ? Global::SelectedProxy() - : ProxyData(); + : MTP::ProxyData(); }; const auto was = current(); Global::SetSelectedProxy(proxy); @@ -391,12 +391,12 @@ auto Application::proxyChanges() const -> rpl::producer { } void Application::badMtprotoConfigurationError() { - if (Global::ProxySettings() == ProxyData::Settings::Enabled + if (Global::ProxySettings() == MTP::ProxyData::Settings::Enabled && !_badProxyDisableBox) { const auto disableCallback = [=] { setCurrentProxy( Global::SelectedProxy(), - ProxyData::Settings::System); + MTP::ProxyData::Settings::System); }; _badProxyDisableBox = Ui::show(Box( Lang::Hard::ProxyConfigError(), diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h index 6129e71bd..57f3ace71 100644 --- a/Telegram/SourceFiles/core/application.h +++ b/Telegram/SourceFiles/core/application.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/core_settings.h" #include "mtproto/auth_key.h" +#include "mtproto/mtproto_proxy_data.h" #include "base/observer.h" #include "base/timer.h" @@ -131,12 +132,12 @@ public: return _dcOptions.get(); } struct ProxyChange { - ProxyData was; - ProxyData now; + MTP::ProxyData was; + MTP::ProxyData now; }; void setCurrentProxy( - const ProxyData &proxy, - ProxyData::Settings settings); + const MTP::ProxyData &proxy, + MTP::ProxyData::Settings settings); [[nodiscard]] rpl::producer proxyChanges() const; void badMtprotoConfigurationError(); diff --git a/Telegram/SourceFiles/core/core_cloud_password.cpp b/Telegram/SourceFiles/core/core_cloud_password.cpp index 982154a96..22b14a717 100644 --- a/Telegram/SourceFiles/core/core_cloud_password.cpp +++ b/Telegram/SourceFiles/core/core_cloud_password.cpp @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/core_cloud_password.h" #include "base/openssl_help.h" -#include "mtproto/connection.h" +#include "mtproto/mtproto_dh_utils.h" namespace Core { namespace { diff --git a/Telegram/SourceFiles/core/crash_report_window.cpp b/Telegram/SourceFiles/core/crash_report_window.cpp index 68ba1d90a..44ee32d85 100644 --- a/Telegram/SourceFiles/core/crash_report_window.cpp +++ b/Telegram/SourceFiles/core/crash_report_window.cpp @@ -814,10 +814,10 @@ void LastCrashedWindow::onNetworkSettingsSaved( QString password) { Expects(host.isEmpty() || port != 0); - auto proxy = ProxyData(); + auto proxy = MTP::ProxyData(); proxy.type = host.isEmpty() - ? ProxyData::Type::None - : ProxyData::Type::Http; + ? MTP::ProxyData::Type::None + : MTP::ProxyData::Type::Http; proxy.host = host; proxy.port = port; proxy.user = username; @@ -843,7 +843,7 @@ void LastCrashedWindow::proxyUpdated() { activate(); } -rpl::producer LastCrashedWindow::proxyChanges() const { +rpl::producer LastCrashedWindow::proxyChanges() const { return _proxyChanges.events(); } diff --git a/Telegram/SourceFiles/core/crash_report_window.h b/Telegram/SourceFiles/core/crash_report_window.h index 59b4ac72f..16507c744 100644 --- a/Telegram/SourceFiles/core/crash_report_window.h +++ b/Telegram/SourceFiles/core/crash_report_window.h @@ -16,6 +16,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include +namespace MTP { +struct ProxyData; +} // namespace MTP + namespace Core { class Launcher; } // namespace Core @@ -96,7 +100,7 @@ public: const QByteArray &crashdump, Fn launch); - rpl::producer proxyChanges() const; + rpl::producer proxyChanges() const; rpl::lifetime &lifetime() { return _lifetime; @@ -199,7 +203,7 @@ private: void setDownloadProgress(qint64 ready, qint64 total); Fn _launch; - rpl::event_stream _proxyChanges; + rpl::event_stream _proxyChanges; rpl::lifetime _lifetime; }; diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index f19f54a24..b72822231 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -163,7 +163,9 @@ bool ApplySocksProxy( auto params = url_parse_params( match->captured(1), qthelp::UrlParamNameTransform::ToLower); - ProxiesBoxController::ShowApplyConfirmation(ProxyData::Type::Socks5, params); + ProxiesBoxController::ShowApplyConfirmation( + MTP::ProxyData::Type::Socks5, + params); return true; } @@ -174,7 +176,9 @@ bool ApplyMtprotoProxy( auto params = url_parse_params( match->captured(1), qthelp::UrlParamNameTransform::ToLower); - ProxiesBoxController::ShowApplyConfirmation(ProxyData::Type::Mtproto, params); + ProxiesBoxController::ShowApplyConfirmation( + MTP::ProxyData::Type::Mtproto, + params); return true; } diff --git a/Telegram/SourceFiles/core/sandbox.cpp b/Telegram/SourceFiles/core/sandbox.cpp index 6f255a94c..762fd66d6 100644 --- a/Telegram/SourceFiles/core/sandbox.cpp +++ b/Telegram/SourceFiles/core/sandbox.cpp @@ -329,7 +329,7 @@ void Sandbox::singleInstanceChecked() { _lastCrashDump, [=] { launchApplication(); }); window->proxyChanges( - ) | rpl::start_with_next([=](ProxyData &&proxy) { + ) | rpl::start_with_next([=](MTP::ProxyData &&proxy) { _sandboxProxy = std::move(proxy); refreshGlobalProxy(); }, window->lifetime()); @@ -443,15 +443,15 @@ void Sandbox::refreshGlobalProxy() { #ifndef TDESKTOP_DISABLE_NETWORK_PROXY const auto proxy = !Global::started() ? _sandboxProxy - : (Global::ProxySettings() == ProxyData::Settings::Enabled) + : (Global::ProxySettings() == MTP::ProxyData::Settings::Enabled) ? Global::SelectedProxy() - : ProxyData(); - if (proxy.type == ProxyData::Type::Socks5 - || proxy.type == ProxyData::Type::Http) { + : MTP::ProxyData(); + if (proxy.type == MTP::ProxyData::Type::Socks5 + || proxy.type == MTP::ProxyData::Type::Http) { QNetworkProxy::setApplicationProxy( - ToNetworkProxy(ToDirectIpProxy(proxy))); + MTP::ToNetworkProxy(MTP::ToDirectIpProxy(proxy))); } else if (!Global::started() - || Global::ProxySettings() == ProxyData::Settings::System) { + || Global::ProxySettings() == MTP::ProxyData::Settings::System) { QNetworkProxyFactory::setUseSystemConfiguration(true); } else { QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy); @@ -555,7 +555,7 @@ rpl::producer<> Sandbox::widgetUpdateRequests() const { return _widgetUpdateRequests.events(); } -ProxyData Sandbox::sandboxProxy() const { +MTP::ProxyData Sandbox::sandboxProxy() const { return _sandboxProxy; } diff --git a/Telegram/SourceFiles/core/sandbox.h b/Telegram/SourceFiles/core/sandbox.h index 7719f8419..f2b591706 100644 --- a/Telegram/SourceFiles/core/sandbox.h +++ b/Telegram/SourceFiles/core/sandbox.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "mtproto/mtproto_proxy_data.h" + #include #include #include @@ -50,7 +52,7 @@ public: rpl::producer<> widgetUpdateRequests() const; - ProxyData sandboxProxy() const; + MTP::ProxyData sandboxProxy() const; static Sandbox &Instance() { Expects(QCoreApplication::instance() != nullptr); @@ -119,7 +121,7 @@ private: std::unique_ptr _updateChecker; QByteArray _lastCrashDump; - ProxyData _sandboxProxy; + MTP::ProxyData _sandboxProxy; rpl::event_stream<> _widgetUpdateRequests; diff --git a/Telegram/SourceFiles/core/utils.cpp b/Telegram/SourceFiles/core/utils.cpp index 54ded4de8..fd62a1716 100644 --- a/Telegram/SourceFiles/core/utils.cpp +++ b/Telegram/SourceFiles/core/utils.cpp @@ -62,128 +62,6 @@ namespace { std::atomic GlobalAtomicRequestId = 0; -[[nodiscard]] bool IsHexMtprotoPassword(const QString &password) { - const auto size = password.size(); - if (size < 32 || size % 2 == 1) { - return false; - } - const auto bad = [](QChar ch) { - const auto code = ch.unicode(); - return (code < 'a' || code > 'f') - && (code < 'A' || code > 'F') - && (code < '0' || code > '9'); - }; - const auto i = std::find_if(password.begin(), password.end(), bad); - return (i == password.end()); -} - -[[nodiscard]] ProxyData::Status HexMtprotoPasswordStatus( - const QString &password) { - const auto size = password.size() / 2; - const auto valid = (size == 16) - || (size == 17 && (password[0] == 'd') && (password[1] == 'd')) - || (size >= 21 && (password[0] == 'e') && (password[1] == 'e')); - if (valid) { - return ProxyData::Status::Valid; - } else if (size < 16) { - return ProxyData::Status::Invalid; - } - return ProxyData::Status::Unsupported; -} - -[[nodiscard]] bytes::vector SecretFromHexMtprotoPassword( - const QString &password) { - Expects(password.size() % 2 == 0); - - const auto size = password.size() / 2; - const auto fromHex = [](QChar ch) -> int { - const auto code = int(ch.unicode()); - if (code >= '0' && code <= '9') { - return (code - '0'); - } else if (code >= 'A' && code <= 'F') { - return 10 + (code - 'A'); - } else if (ch >= 'a' && ch <= 'f') { - return 10 + (code - 'a'); - } - Unexpected("Code in ProxyData fromHex."); - }; - auto result = bytes::vector(size); - for (auto i = 0; i != size; ++i) { - const auto high = fromHex(password[2 * i]); - const auto low = fromHex(password[2 * i + 1]); - if (high < 0 || low < 0) { - return {}; - } - result[i] = static_cast(high * 16 + low); - } - return result; -} - -[[nodiscard]] QStringRef Base64UrlInner(const QString &password) { - Expects(password.size() > 2); - - // Skip one or two '=' at the end of the string. - return password.midRef(0, [&] { - auto result = password.size(); - for (auto i = 0; i != 2; ++i) { - const auto prev = result - 1; - if (password[prev] != '=') { - break; - } - result = prev; - } - return result; - }()); -} - -[[nodiscard]] bool IsBase64UrlMtprotoPassword(const QString &password) { - const auto size = password.size(); - if (size < 22 || size % 4 == 1) { - return false; - } - const auto bad = [](QChar ch) { - const auto code = ch.unicode(); - return (code < 'a' || code > 'z') - && (code < 'A' || code > 'Z') - && (code < '0' || code > '9') - && (code != '_') - && (code != '-'); - }; - const auto inner = Base64UrlInner(password); - const auto begin = inner.data(); - const auto end = begin + inner.size(); - return (std::find_if(begin, end, bad) == end); -} - -[[nodiscard]] ProxyData::Status Base64UrlMtprotoPasswordStatus( - const QString &password) { - const auto inner = Base64UrlInner(password); - const auto size = (inner.size() * 3) / 4; - const auto valid = (size == 16) - || (size == 17 - && (password[0] == '3') - && ((password[1] >= 'Q' && password[1] <= 'Z') - || (password[1] >= 'a' && password[1] <= 'f'))) - || (size >= 21 - && (password[0] == '7') - && (password[1] >= 'g') - && (password[1] <= 'v')); - if (size < 16) { - return ProxyData::Status::Invalid; - } else if (valid) { - return ProxyData::Status::Valid; - } - return ProxyData::Status::Unsupported; -} - -[[nodiscard]] bytes::vector SecretFromBase64UrlMtprotoPassword( - const QString &password) { - const auto result = QByteArray::fromBase64( - password.toLatin1(), - QByteArray::Base64UrlEncoding); - return bytes::make_vector(bytes::make_span(result)); -} - } // namespace // Precise timing functions / rand init @@ -248,105 +126,6 @@ namespace { } } -bool ProxyData::valid() const { - return status() == Status::Valid; -} - -ProxyData::Status ProxyData::status() const { - if (type == Type::None || host.isEmpty() || !port) { - return Status::Invalid; - } else if (type == Type::Mtproto) { - return MtprotoPasswordStatus(password); - } - return Status::Valid; -} - -bool ProxyData::supportsCalls() const { - return (type == Type::Socks5); -} - -bool ProxyData::tryCustomResolve() const { - return (type == Type::Socks5 || type == Type::Mtproto) - && !qthelp::is_ipv6(host) - && !QRegularExpression( - qsl("^\\d+\\.\\d+\\.\\d+\\.\\d+$") - ).match(host).hasMatch(); -} - -bytes::vector ProxyData::secretFromMtprotoPassword() const { - Expects(type == Type::Mtproto); - - if (IsHexMtprotoPassword(password)) { - return SecretFromHexMtprotoPassword(password); - } else if (IsBase64UrlMtprotoPassword(password)) { - return SecretFromBase64UrlMtprotoPassword(password); - } - return {}; -} - -ProxyData::operator bool() const { - return valid(); -} - -bool ProxyData::operator==(const ProxyData &other) const { - if (!valid()) { - return !other.valid(); - } - return (type == other.type) - && (host == other.host) - && (port == other.port) - && (user == other.user) - && (password == other.password); -} - -bool ProxyData::operator!=(const ProxyData &other) const { - return !(*this == other); -} - -bool ProxyData::ValidMtprotoPassword(const QString &password) { - return MtprotoPasswordStatus(password) == Status::Valid; -} - -ProxyData::Status ProxyData::MtprotoPasswordStatus(const QString &password) { - if (IsHexMtprotoPassword(password)) { - return HexMtprotoPasswordStatus(password); - } else if (IsBase64UrlMtprotoPassword(password)) { - return Base64UrlMtprotoPasswordStatus(password); - } - return Status::Invalid; -} - -ProxyData ToDirectIpProxy(const ProxyData &proxy, int ipIndex) { - if (!proxy.tryCustomResolve() - || ipIndex < 0 - || ipIndex >= proxy.resolvedIPs.size()) { - return proxy; - } - return { - proxy.type, - proxy.resolvedIPs[ipIndex], - proxy.port, - proxy.user, - proxy.password - }; -} - -QNetworkProxy ToNetworkProxy(const ProxyData &proxy) { - if (proxy.type == ProxyData::Type::None) { - return QNetworkProxy::DefaultProxy; - } else if (proxy.type == ProxyData::Type::Mtproto) { - return QNetworkProxy::NoProxy; - } - return QNetworkProxy( - (proxy.type == ProxyData::Type::Socks5 - ? QNetworkProxy::Socks5Proxy - : QNetworkProxy::HttpProxy), - proxy.host, - proxy.port, - proxy.user, - proxy.password); -} - namespace ThirdParty { void start() { diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index c219f036e..9a21cabce 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -218,50 +218,6 @@ enum DBIWorkMode { dbiwmWindowOnly = 2, }; -struct ProxyData { - enum class Settings { - System, - Enabled, - Disabled, - }; - enum class Type { - None, - Socks5, - Http, - Mtproto, - }; - enum class Status { - Valid, - Unsupported, - Invalid, - }; - - Type type = Type::None; - QString host; - uint32 port = 0; - QString user, password; - - std::vector resolvedIPs; - crl::time resolvedExpireAt = 0; - - [[nodiscard]] bool valid() const; - [[nodiscard]] Status status() const; - [[nodiscard]] bool supportsCalls() const; - [[nodiscard]] bool tryCustomResolve() const; - [[nodiscard]] bytes::vector secretFromMtprotoPassword() const; - [[nodiscard]] explicit operator bool() const; - [[nodiscard]] bool operator==(const ProxyData &other) const; - [[nodiscard]] bool operator!=(const ProxyData &other) const; - - [[nodiscard]] static bool ValidMtprotoPassword(const QString &password); - [[nodiscard]] static Status MtprotoPasswordStatus( - const QString &password); - -}; - -ProxyData ToDirectIpProxy(const ProxyData &proxy, int ipIndex = 0); -QNetworkProxy ToNetworkProxy(const ProxyData &proxy); - static const int MatrixRowShift = 40000; inline int rowscount(int fullCount, int countPerRow) { diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 19419045a..725130654 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -373,9 +373,9 @@ struct Data { bool NotificationsDemoIsShown = false; bool TryIPv6 = !Platform::IsWindows(); - std::vector ProxiesList; - ProxyData SelectedProxy; - ProxyData::Settings ProxySettings = ProxyData::Settings::System; + std::vector ProxiesList; + MTP::ProxyData SelectedProxy; + MTP::ProxyData::Settings ProxySettings = MTP::ProxyData::Settings::System; bool UseProxyForCalls = false; base::Observable ConnectionTypeChanged; @@ -501,9 +501,9 @@ DefineVar(Global, Notify::ScreenCorner, NotificationsCorner); DefineVar(Global, bool, NotificationsDemoIsShown); DefineVar(Global, bool, TryIPv6); -DefineVar(Global, std::vector, ProxiesList); -DefineVar(Global, ProxyData, SelectedProxy); -DefineVar(Global, ProxyData::Settings, ProxySettings); +DefineVar(Global, std::vector, ProxiesList); +DefineVar(Global, MTP::ProxyData, SelectedProxy); +DefineVar(Global, MTP::ProxyData::Settings, ProxySettings); DefineVar(Global, bool, UseProxyForCalls); DefineRefVar(Global, base::Observable, ConnectionTypeChanged); diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 951764a2a..bfa29df18 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/observer.h" #include "base/call_delayed.h" #include "ui/effects/animation_value.h" +#include "mtproto/mtproto_proxy_data.h" class History; @@ -224,9 +225,9 @@ DeclareVar(Notify::ScreenCorner, NotificationsCorner); DeclareVar(bool, NotificationsDemoIsShown); DeclareVar(bool, TryIPv6); -DeclareVar(std::vector, ProxiesList); -DeclareVar(ProxyData, SelectedProxy); -DeclareVar(ProxyData::Settings, ProxySettings); +DeclareVar(std::vector, ProxiesList); +DeclareVar(MTP::ProxyData, SelectedProxy); +DeclareVar(MTP::ProxyData::Settings, ProxySettings); DeclareVar(bool, UseProxyForCalls); DeclareRefVar(base::Observable, ConnectionTypeChanged); diff --git a/Telegram/SourceFiles/main/main_account.cpp b/Telegram/SourceFiles/main/main_account.cpp index c0185388c..0f98bb2ea 100644 --- a/Telegram/SourceFiles/main/main_account.cpp +++ b/Telegram/SourceFiles/main/main_account.cpp @@ -35,8 +35,8 @@ void Account::watchProxyChanges() { Core::App().proxyChanges( ) | rpl::start_with_next([=](const ProxyChange &change) { - const auto key = [&](const ProxyData &proxy) { - return (proxy.type == ProxyData::Type::Mtproto) + const auto key = [&](const MTP::ProxyData &proxy) { + return (proxy.type == MTP::ProxyData::Type::Mtproto) ? std::make_pair(proxy.host, proxy.port) : std::make_pair(QString(), uint32(0)); }; diff --git a/Telegram/SourceFiles/mtproto/connection.cpp b/Telegram/SourceFiles/mtproto/connection.cpp index c52b3aa7b..f48b90189 100644 --- a/Telegram/SourceFiles/mtproto/connection.cpp +++ b/Telegram/SourceFiles/mtproto/connection.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "mtproto/connection.h" +#include "mtproto/details/mtproto_dc_key_creator.h" #include "mtproto/session.h" #include "mtproto/rsa_public_key.h" #include "mtproto/rpc_sender.h" @@ -39,7 +40,6 @@ namespace { constexpr auto kRecreateKeyId = AuthKey::KeyId(0xFFFFFFFFFFFFFFFFULL); constexpr auto kIntSize = static_cast(sizeof(mtpPrime)); -constexpr auto kMaxModExpSize = 256; constexpr auto kWaitForBetterTimeout = crl::time(2000); constexpr auto kMinConnectedTimeout = crl::time(1000); constexpr auto kMaxConnectedTimeout = crl::time(8000); @@ -57,6 +57,8 @@ constexpr auto kRequestConfigTimeout = crl::time(8000); // Don't try to handle messages larger than this size. constexpr auto kMaxMessageLength = 16 * 1024 * 1024; +using namespace details; + QString LogIdsVector(const QVector &ids) { if (!ids.size()) return "[]"; auto idsStr = QString("[%1").arg(ids.cbegin()->v); @@ -66,162 +68,6 @@ QString LogIdsVector(const QVector &ids) { return idsStr + "]"; } -bool IsGoodModExpFirst( - const openssl::BigNum &modexp, - const openssl::BigNum &prime) { - const auto diff = openssl::BigNum::Sub(prime, modexp); - if (modexp.failed() || prime.failed() || diff.failed()) { - return false; - } - constexpr auto kMinDiffBitsCount = 2048 - 64; - if (diff.isNegative() - || diff.bitsSize() < kMinDiffBitsCount - || modexp.bitsSize() < kMinDiffBitsCount - || modexp.bytesSize() > kMaxModExpSize) { - return false; - } - return true; -} - -bool IsPrimeAndGoodCheck(const openssl::BigNum &prime, int g) { - constexpr auto kGoodPrimeBitsCount = 2048; - - if (prime.failed() - || prime.isNegative() - || prime.bitsSize() != kGoodPrimeBitsCount) { - LOG(("MTP Error: Bad prime bits count %1, expected %2." - ).arg(prime.bitsSize() - ).arg(kGoodPrimeBitsCount)); - return false; - } - - const auto context = openssl::Context(); - if (!prime.isPrime(context)) { - LOG(("MTP Error: Bad prime.")); - return false; - } - - switch (g) { - case 2: { - const auto mod8 = prime.countModWord(8); - if (mod8 != 7) { - LOG(("BigNum PT Error: bad g value: %1, mod8: %2").arg(g).arg(mod8)); - return false; - } - } break; - case 3: { - const auto mod3 = prime.countModWord(3); - if (mod3 != 2) { - LOG(("BigNum PT Error: bad g value: %1, mod3: %2").arg(g).arg(mod3)); - return false; - } - } break; - case 4: break; - case 5: { - const auto mod5 = prime.countModWord(5); - if (mod5 != 1 && mod5 != 4) { - LOG(("BigNum PT Error: bad g value: %1, mod5: %2").arg(g).arg(mod5)); - return false; - } - } break; - case 6: { - const auto mod24 = prime.countModWord(24); - if (mod24 != 19 && mod24 != 23) { - LOG(("BigNum PT Error: bad g value: %1, mod24: %2").arg(g).arg(mod24)); - return false; - } - } break; - case 7: { - const auto mod7 = prime.countModWord(7); - if (mod7 != 3 && mod7 != 5 && mod7 != 6) { - LOG(("BigNum PT Error: bad g value: %1, mod7: %2").arg(g).arg(mod7)); - return false; - } - } break; - default: { - LOG(("BigNum PT Error: bad g value: %1").arg(g)); - return false; - } break; - } - - if (!openssl::BigNum(prime).subWord(1).divWord(2).isPrime(context)) { - LOG(("MTP Error: Bad (prime - 1) / 2.")); - return false; - } - - return true; -} - -bool IsPrimeAndGood(bytes::const_span primeBytes, int g) { - static constexpr unsigned char GoodPrime[] = { - 0xC7, 0x1C, 0xAE, 0xB9, 0xC6, 0xB1, 0xC9, 0x04, 0x8E, 0x6C, 0x52, 0x2F, 0x70, 0xF1, 0x3F, 0x73, - 0x98, 0x0D, 0x40, 0x23, 0x8E, 0x3E, 0x21, 0xC1, 0x49, 0x34, 0xD0, 0x37, 0x56, 0x3D, 0x93, 0x0F, - 0x48, 0x19, 0x8A, 0x0A, 0xA7, 0xC1, 0x40, 0x58, 0x22, 0x94, 0x93, 0xD2, 0x25, 0x30, 0xF4, 0xDB, - 0xFA, 0x33, 0x6F, 0x6E, 0x0A, 0xC9, 0x25, 0x13, 0x95, 0x43, 0xAE, 0xD4, 0x4C, 0xCE, 0x7C, 0x37, - 0x20, 0xFD, 0x51, 0xF6, 0x94, 0x58, 0x70, 0x5A, 0xC6, 0x8C, 0xD4, 0xFE, 0x6B, 0x6B, 0x13, 0xAB, - 0xDC, 0x97, 0x46, 0x51, 0x29, 0x69, 0x32, 0x84, 0x54, 0xF1, 0x8F, 0xAF, 0x8C, 0x59, 0x5F, 0x64, - 0x24, 0x77, 0xFE, 0x96, 0xBB, 0x2A, 0x94, 0x1D, 0x5B, 0xCD, 0x1D, 0x4A, 0xC8, 0xCC, 0x49, 0x88, - 0x07, 0x08, 0xFA, 0x9B, 0x37, 0x8E, 0x3C, 0x4F, 0x3A, 0x90, 0x60, 0xBE, 0xE6, 0x7C, 0xF9, 0xA4, - 0xA4, 0xA6, 0x95, 0x81, 0x10, 0x51, 0x90, 0x7E, 0x16, 0x27, 0x53, 0xB5, 0x6B, 0x0F, 0x6B, 0x41, - 0x0D, 0xBA, 0x74, 0xD8, 0xA8, 0x4B, 0x2A, 0x14, 0xB3, 0x14, 0x4E, 0x0E, 0xF1, 0x28, 0x47, 0x54, - 0xFD, 0x17, 0xED, 0x95, 0x0D, 0x59, 0x65, 0xB4, 0xB9, 0xDD, 0x46, 0x58, 0x2D, 0xB1, 0x17, 0x8D, - 0x16, 0x9C, 0x6B, 0xC4, 0x65, 0xB0, 0xD6, 0xFF, 0x9C, 0xA3, 0x92, 0x8F, 0xEF, 0x5B, 0x9A, 0xE4, - 0xE4, 0x18, 0xFC, 0x15, 0xE8, 0x3E, 0xBE, 0xA0, 0xF8, 0x7F, 0xA9, 0xFF, 0x5E, 0xED, 0x70, 0x05, - 0x0D, 0xED, 0x28, 0x49, 0xF4, 0x7B, 0xF9, 0x59, 0xD9, 0x56, 0x85, 0x0C, 0xE9, 0x29, 0x85, 0x1F, - 0x0D, 0x81, 0x15, 0xF6, 0x35, 0xB1, 0x05, 0xEE, 0x2E, 0x4E, 0x15, 0xD0, 0x4B, 0x24, 0x54, 0xBF, - 0x6F, 0x4F, 0xAD, 0xF0, 0x34, 0xB1, 0x04, 0x03, 0x11, 0x9C, 0xD8, 0xE3, 0xB9, 0x2F, 0xCC, 0x5B }; - - if (!bytes::compare(bytes::make_span(GoodPrime), primeBytes)) { - if (g == 3 || g == 4 || g == 5 || g == 7) { - return true; - } - } - - return IsPrimeAndGoodCheck(openssl::BigNum(primeBytes), g); -} - -bytes::vector CreateAuthKey( - bytes::const_span firstBytes, - bytes::const_span randomBytes, - bytes::const_span primeBytes) { - using openssl::BigNum; - - const auto first = BigNum(firstBytes); - const auto prime = BigNum(primeBytes); - if (!IsGoodModExpFirst(first, prime)) { - LOG(("AuthKey Error: Bad first prime in CreateAuthKey().")); - return {}; - } - return BigNum::ModExp(first, BigNum(randomBytes), prime).getBytes(); -} - -ModExpFirst CreateModExp( - int g, - bytes::const_span primeBytes, - bytes::const_span randomSeed) { - Expects(randomSeed.size() == ModExpFirst::kRandomPowerSize); - - using namespace openssl; - - BigNum prime(primeBytes); - auto result = ModExpFirst(); - result.randomPower.resize(ModExpFirst::kRandomPowerSize); - while (true) { - bytes::set_random(result.randomPower); - for (auto i = 0; i != ModExpFirst::kRandomPowerSize; ++i) { - result.randomPower[i] ^= randomSeed[i]; - } - const auto modexp = BigNum::ModExp( - BigNum(g), - BigNum(result.randomPower), - prime); - if (IsGoodModExpFirst(modexp, prime)) { - result.modexp = modexp.getBytes(); - return result; - } - } -} - void wrapInvokeAfter(SecureRequest &to, const SecureRequest &from, const RequestMap &haveSent, int32 skipBeforeRequest = 0) { const auto afterId = *(mtpMsgId*)(from->after->data() + 4); const auto i = afterId ? haveSent.constFind(afterId) : haveSent.cend(); @@ -245,48 +91,6 @@ void wrapInvokeAfter(SecureRequest &to, const SecureRequest &from, const Request } } -bool parsePQ(const QByteArray &pqStr, QByteArray &pStr, QByteArray &qStr) { - if (pqStr.length() > 8) return false; // more than 64 bit pq - - uint64 pq = 0, p, q; - const uchar *pqChars = (const uchar*)pqStr.constData(); - for (uint32 i = 0, l = pqStr.length(); i < l; ++i) { - pq <<= 8; - pq |= (uint64)pqChars[i]; - } - uint64 pqSqrt = (uint64)sqrtl((long double)pq), ySqr, y; - while (pqSqrt * pqSqrt > pq) --pqSqrt; - while (pqSqrt * pqSqrt < pq) ++pqSqrt; - for (ySqr = pqSqrt * pqSqrt - pq; ; ++pqSqrt, ySqr = pqSqrt * pqSqrt - pq) { - y = (uint64)sqrtl((long double)ySqr); - while (y * y > ySqr) --y; - while (y * y < ySqr) ++y; - if (!ySqr || y + pqSqrt >= pq) return false; - if (y * y == ySqr) { - p = pqSqrt + y; - q = (pqSqrt > y) ? (pqSqrt - y) : (y - pqSqrt); - break; - } - } - if (p > q) std::swap(p, q); - - pStr.resize(4); - uchar *pChars = (uchar*)pStr.data(); - for (uint32 i = 0; i < 4; ++i) { - *(pChars + 3 - i) = (uchar)(p & 0xFF); - p >>= 8; - } - - qStr.resize(4); - uchar *qChars = (uchar*)qStr.data(); - for (uint32 i = 0; i < 4; ++i) { - *(qChars + 3 - i) = (uchar)(q & 0xFF); - q >>= 8; - } - - return true; -} - } // namespace Connection::Connection(not_null instance) : _instance(instance) { @@ -411,6 +215,7 @@ void ConnectionPrivate::destroyAllConnections() { _waitForReceivedTimer.cancel(); _waitForConnectedTimer.cancel(); _testConnections.clear(); + _keyCreator = nullptr; _connection = nullptr; } @@ -1392,8 +1197,6 @@ void ConnectionPrivate::doDisconnect() { } } - clearAuthKeyData(); - setState(DisconnectedState); restarted = false; } @@ -2568,7 +2371,7 @@ void ConnectionPrivate::removeTestConnection( end(_testConnections)); } -void ConnectionPrivate::updateAuthKey() { +void ConnectionPrivate::updateAuthKey() { QReadLocker lockFinished(&sessionDataMutex); if (!sessionData || !_connection) return; @@ -2597,7 +2400,7 @@ void ConnectionPrivate::updateAuthKey() { DEBUG_LOG(("AuthKey Info: No key in updateAuthKey(), will be creating auth_key")); lockKey(); - auto &key = sessionData->getKey(); + const auto &key = sessionData->getKey(); if (key) { if (keyId != key->keyId()) clearMessages(); keyId = key->keyId(); @@ -2609,19 +2412,49 @@ void ConnectionPrivate::updateAuthKey() { _instance->checkIfKeyWasDestroyed(_shiftedDcId); return; } - - _authKeyData = std::make_unique(); - _authKeyStrings = std::make_unique(); - const auto nonce = _authKeyData->nonce = rand_value(); - - connect(_connection, &AbstractConnection::receivedData, [=] { - pqAnswered(); - }); - - DEBUG_LOG(("AuthKey Info: sending Req_pq...")); lockFinished.unlock(); - sendNotSecureRequest(MTPReq_pq_multi(nonce)); + createDcKey(); +} + +void ConnectionPrivate::createDcKey() { + using Result = DcKeyCreator::Result; + using Error = DcKeyCreator::Error; + auto delegate = DcKeyCreator::Delegate(); + delegate.done = [=](base::expected result) { + _keyCreator = nullptr; + + if (result) { + QReadLocker lockFinished(&sessionDataMutex); + if (!sessionData) return; + + sessionData->setSalt(result->serverSalt); + + auto authKey = std::move(result->key); + + DEBUG_LOG(("AuthKey Info: auth key gen succeed, id: %1, server salt: %2").arg(authKey->keyId()).arg(result->serverSalt)); + + sessionData->owner()->notifyKeyCreated(std::move(authKey)); // slot will call authKeyCreated() + sessionData->clear(_instance); + unlockKey(); + } else if (result.error() == Error::UnknownPublicKey) { + if (_dcType == DcType::Cdn) { + LOG(("Warning: CDN public RSA key not found")); + requestCDNConfig(); + } else { + LOG(("AuthKey Error: could not choose public RSA key")); + restart(); + } + } else { + restart(); + } + }; + _keyCreator = std::make_unique( + BareDcId(_shiftedDcId), + getProtocolDcId(), + _connection.get(), + _instance->dcOptions(), + std::move(delegate)); } void ConnectionPrivate::clearMessages() { @@ -2630,410 +2463,8 @@ void ConnectionPrivate::clearMessages() { } } -void ConnectionPrivate::pqAnswered() { - disconnect(_connection, &AbstractConnection::receivedData, nullptr, nullptr); - DEBUG_LOG(("AuthKey Info: receiving Req_pq answer...")); - - MTPReq_pq::ResponseType res_pq; - if (!readNotSecureResponse(res_pq)) { - return restart(); - } - - auto &res_pq_data = res_pq.c_resPQ(); - if (res_pq_data.vnonce() != _authKeyData->nonce) { - LOG(("AuthKey Error: received nonce <> sent nonce (in res_pq)!")); - DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&res_pq_data.vnonce(), 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str())); - return restart(); - } - - auto rsaKey = internal::RSAPublicKey(); - if (!_instance->dcOptions()->getDcRSAKey(BareDcId(_shiftedDcId), res_pq.c_resPQ().vserver_public_key_fingerprints().v, &rsaKey)) { - if (_dcType == DcType::Cdn) { - LOG(("Warning: CDN public RSA key not found")); - requestCDNConfig(); - return; - } - LOG(("AuthKey Error: could not choose public RSA key")); - return restart(); - } - Assert(rsaKey.isValid()); - - _authKeyData->server_nonce = res_pq_data.vserver_nonce(); - _authKeyData->new_nonce = rand_value(); - - auto &pq = res_pq_data.vpq().v; - auto p = QByteArray(); - auto q = QByteArray(); - if (!internal::parsePQ(pq, p, q)) { - LOG(("AuthKey Error: could not factor pq!")); - DEBUG_LOG(("AuthKey Error: problematic pq: %1").arg(Logs::mb(pq.constData(), pq.length()).str())); - return restart(); - } - - auto p_q_inner = MTP_p_q_inner_data_dc( - res_pq_data.vpq(), - MTP_bytes(std::move(p)), - MTP_bytes(std::move(q)), - _authKeyData->nonce, - _authKeyData->server_nonce, - _authKeyData->new_nonce, - MTP_int(getProtocolDcId())); - auto dhEncString = encryptPQInnerRSA(p_q_inner, rsaKey); - if (dhEncString.empty()) { - return restart(); - } - - connect(_connection, &AbstractConnection::receivedData, [=] { - dhParamsAnswered(); - }); - - DEBUG_LOG(("AuthKey Info: sending Req_DH_params...")); - - sendNotSecureRequest(MTPReq_DH_params( - _authKeyData->nonce, - _authKeyData->server_nonce, - p_q_inner.c_p_q_inner_data_dc().vp(), - p_q_inner.c_p_q_inner_data_dc().vq(), - MTP_long(rsaKey.getFingerPrint()), - MTP_bytes(dhEncString))); -} - -bytes::vector ConnectionPrivate::encryptPQInnerRSA( - const MTPP_Q_inner_data &data, - const internal::RSAPublicKey &key) { - auto p_q_inner_size = tl::count_length(data); - auto encSize = (p_q_inner_size >> 2) + 6; - if (encSize >= 65) { - auto tmp = mtpBuffer(); - tmp.reserve(encSize); - data.write(tmp); - LOG(("AuthKey Error: too large data for RSA encrypt, size %1").arg(encSize * sizeof(mtpPrime))); - DEBUG_LOG(("AuthKey Error: bad data for RSA encrypt %1").arg(Logs::mb(&tmp[0], tmp.size() * 4).str())); - return {}; // can't be 255-byte string - } - - auto encBuffer = mtpBuffer(); - encBuffer.reserve(65); // 260 bytes - encBuffer.resize(6); - encBuffer[0] = 0; - data.write(encBuffer); - - hashSha1(&encBuffer[6], p_q_inner_size, &encBuffer[1]); - if (encSize < 65) { - encBuffer.resize(65); - memset_rand(&encBuffer[encSize], (65 - encSize) * sizeof(mtpPrime)); - } - - auto bytes = bytes::make_span(encBuffer); - auto bytesToEncrypt = bytes.subspan(3, 256); - return key.encrypt(bytesToEncrypt); -} - -void ConnectionPrivate::dhParamsAnswered() { - disconnect(_connection, &AbstractConnection::receivedData, nullptr, nullptr); - DEBUG_LOG(("AuthKey Info: receiving Req_DH_params answer...")); - - MTPReq_DH_params::ResponseType res_DH_params; - if (!readNotSecureResponse(res_DH_params)) { - return restart(); - } - - switch (res_DH_params.type()) { - case mtpc_server_DH_params_ok: { - const auto &encDH(res_DH_params.c_server_DH_params_ok()); - if (encDH.vnonce() != _authKeyData->nonce) { - LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_params_ok)!")); - DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&encDH.vnonce(), 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str())); - return restart(); - } - if (encDH.vserver_nonce() != _authKeyData->server_nonce) { - LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_params_ok)!")); - DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&encDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str())); - return restart(); - } - - auto &encDHStr = encDH.vencrypted_answer().v; - uint32 encDHLen = encDHStr.length(), encDHBufLen = encDHLen >> 2; - if ((encDHLen & 0x03) || encDHBufLen < 6) { - LOG(("AuthKey Error: bad encrypted data length %1 (in server_DH_params_ok)!").arg(encDHLen)); - DEBUG_LOG(("AuthKey Error: received encrypted data %1").arg(Logs::mb(encDHStr.constData(), encDHLen).str())); - return restart(); - } - - uint32 nlen = tl::count_length(_authKeyData->new_nonce), slen = tl::count_length(_authKeyData->server_nonce); - uchar tmp_aes[1024], sha1ns[20], sha1sn[20], sha1nn[20]; - memcpy(tmp_aes, &_authKeyData->new_nonce, nlen); - memcpy(tmp_aes + nlen, &_authKeyData->server_nonce, slen); - memcpy(tmp_aes + nlen + slen, &_authKeyData->new_nonce, nlen); - memcpy(tmp_aes + nlen + slen + nlen, &_authKeyData->new_nonce, nlen); - hashSha1(tmp_aes, nlen + slen, sha1ns); - hashSha1(tmp_aes + nlen, nlen + slen, sha1sn); - hashSha1(tmp_aes + nlen + slen, nlen + nlen, sha1nn); - - mtpBuffer decBuffer; - decBuffer.resize(encDHBufLen); - - memcpy(_authKeyData->aesKey, sha1ns, 20); - memcpy(_authKeyData->aesKey + 20, sha1sn, 12); - memcpy(_authKeyData->aesIV, sha1sn + 12, 8); - memcpy(_authKeyData->aesIV + 8, sha1nn, 20); - memcpy(_authKeyData->aesIV + 28, &_authKeyData->new_nonce, 4); - - aesIgeDecryptRaw(encDHStr.constData(), &decBuffer[0], encDHLen, _authKeyData->aesKey, _authKeyData->aesIV); - - const mtpPrime *from(&decBuffer[5]), *to(from), *end(from + (encDHBufLen - 5)); - MTPServer_DH_inner_data dh_inner; - if (!dh_inner.read(to, end)) { - LOG(("AuthKey Error: could not decrypt server_DH_inner_data!")); - return restart(); - } - const auto &dh_inner_data(dh_inner.c_server_DH_inner_data()); - if (dh_inner_data.vnonce() != _authKeyData->nonce) { - LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_inner_data)!")); - DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&dh_inner_data.vnonce(), 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str())); - return restart(); - } - if (dh_inner_data.vserver_nonce() != _authKeyData->server_nonce) { - LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_inner_data)!")); - DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&dh_inner_data.vserver_nonce(), 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str())); - return restart(); - } - uchar sha1Buffer[20]; - if (memcmp(&decBuffer[0], hashSha1(&decBuffer[5], (to - from) * sizeof(mtpPrime), sha1Buffer), 20)) { - LOG(("AuthKey Error: sha1 hash of encrypted part did not match!")); - DEBUG_LOG(("AuthKey Error: sha1 did not match, server_nonce: %1, new_nonce %2, encrypted data %3").arg(Logs::mb(&_authKeyData->server_nonce, 16).str()).arg(Logs::mb(&_authKeyData->new_nonce, 16).str()).arg(Logs::mb(encDHStr.constData(), encDHLen).str())); - return restart(); - } - base::unixtime::update(dh_inner_data.vserver_time().v); - - // check that dhPrime and (dhPrime - 1) / 2 are really prime - if (!IsPrimeAndGood(bytes::make_span(dh_inner_data.vdh_prime().v), dh_inner_data.vg().v)) { - LOG(("AuthKey Error: bad dh_prime primality!")); - return restart(); - } - - _authKeyStrings->dh_prime = bytes::make_vector( - dh_inner_data.vdh_prime().v); - _authKeyData->g = dh_inner_data.vg().v; - _authKeyStrings->g_a = bytes::make_vector(dh_inner_data.vg_a().v); - _authKeyData->retry_id = MTP_long(0); - _authKeyData->retries = 0; - } return dhClientParamsSend(); - - case mtpc_server_DH_params_fail: { - const auto &encDH(res_DH_params.c_server_DH_params_fail()); - if (encDH.vnonce() != _authKeyData->nonce) { - LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_params_fail)!")); - DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&encDH.vnonce(), 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str())); - return restart(); - } - if (encDH.vserver_nonce() != _authKeyData->server_nonce) { - LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_params_fail)!")); - DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&encDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str())); - return restart(); - } - uchar sha1Buffer[20]; - if (encDH.vnew_nonce_hash() != *(MTPint128*)(hashSha1(&_authKeyData->new_nonce, 32, sha1Buffer) + 1)) { - LOG(("AuthKey Error: received new_nonce_hash did not match!")); - DEBUG_LOG(("AuthKey Error: received new_nonce_hash: %1, new_nonce: %2").arg(Logs::mb(&encDH.vnew_nonce_hash(), 16).str()).arg(Logs::mb(&_authKeyData->new_nonce, 32).str())); - return restart(); - } - LOG(("AuthKey Error: server_DH_params_fail received!")); - } return restart(); - - } - LOG(("AuthKey Error: unknown server_DH_params received, typeId = %1").arg(res_DH_params.type())); - return restart(); -} - -void ConnectionPrivate::dhClientParamsSend() { - if (++_authKeyData->retries > 5) { - LOG(("AuthKey Error: could not create auth_key for %1 retries").arg(_authKeyData->retries - 1)); - return restart(); - } - - // gen rand 'b' - auto randomSeed = bytes::vector(ModExpFirst::kRandomPowerSize); - bytes::set_random(randomSeed); - auto g_b_data = CreateModExp(_authKeyData->g, _authKeyStrings->dh_prime, randomSeed); - if (g_b_data.modexp.empty()) { - LOG(("AuthKey Error: could not generate good g_b.")); - return restart(); - } - - auto computedAuthKey = CreateAuthKey(_authKeyStrings->g_a, g_b_data.randomPower, _authKeyStrings->dh_prime); - if (computedAuthKey.empty()) { - LOG(("AuthKey Error: could not generate auth_key.")); - return restart(); - } - AuthKey::FillData(_authKeyStrings->auth_key, computedAuthKey); - - // count auth_key hashes - parts of sha1(auth_key) - auto auth_key_sha = hashSha1(_authKeyStrings->auth_key.data(), _authKeyStrings->auth_key.size()); - memcpy(&_authKeyData->auth_key_aux_hash, auth_key_sha.data(), 8); - memcpy(&_authKeyData->auth_key_hash, auth_key_sha.data() + 12, 8); - - auto client_dh_inner = MTP_client_DH_inner_data(_authKeyData->nonce, _authKeyData->server_nonce, _authKeyData->retry_id, MTP_bytes(g_b_data.modexp)); - - auto sdhEncString = encryptClientDHInner(client_dh_inner); - - connect(_connection, &AbstractConnection::receivedData, [=] { - dhClientParamsAnswered(); - }); - - DEBUG_LOG(("AuthKey Info: sending Req_client_DH_params...")); - sendNotSecureRequest(MTPSet_client_DH_params( - _authKeyData->nonce, - _authKeyData->server_nonce, - MTP_string(std::move(sdhEncString)))); -} - -std::string ConnectionPrivate::encryptClientDHInner(const MTPClient_DH_Inner_Data &data) { - auto client_dh_inner_size = tl::count_length(data); - auto encSize = (client_dh_inner_size >> 2) + 5; - auto encFullSize = encSize; - if (encSize & 0x03) { - encFullSize += 4 - (encSize & 0x03); - } - - auto encBuffer = mtpBuffer(); - encBuffer.reserve(encFullSize); - encBuffer.resize(5); - data.write(encBuffer); - - hashSha1(&encBuffer[5], client_dh_inner_size, &encBuffer[0]); - if (encSize < encFullSize) { - encBuffer.resize(encFullSize); - memset_rand(&encBuffer[encSize], (encFullSize - encSize) * sizeof(mtpPrime)); - } - - auto sdhEncString = std::string(encFullSize * 4, ' '); - - aesIgeEncryptRaw(&encBuffer[0], &sdhEncString[0], encFullSize * sizeof(mtpPrime), _authKeyData->aesKey, _authKeyData->aesIV); - - return sdhEncString; -} - -void ConnectionPrivate::dhClientParamsAnswered() { - QReadLocker lockFinished(&sessionDataMutex); - if (!sessionData) return; - - disconnect(_connection, &AbstractConnection::receivedData, nullptr, nullptr); - DEBUG_LOG(("AuthKey Info: receiving Req_client_DH_params answer...")); - - MTPSet_client_DH_params::ResponseType res_client_DH_params; - if (!readNotSecureResponse(res_client_DH_params)) { - lockFinished.unlock(); - return restart(); - } - - switch (res_client_DH_params.type()) { - case mtpc_dh_gen_ok: { - const auto &resDH(res_client_DH_params.c_dh_gen_ok()); - if (resDH.vnonce() != _authKeyData->nonce) { - LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_ok)!")); - DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce(), 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str())); - - lockFinished.unlock(); - return restart(); - } - if (resDH.vserver_nonce() != _authKeyData->server_nonce) { - LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_ok)!")); - DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str())); - - lockFinished.unlock(); - return restart(); - } - _authKeyData->new_nonce_buf[32] = 1; - uchar sha1Buffer[20]; - if (resDH.vnew_nonce_hash1() != *(MTPint128*)(hashSha1(_authKeyData->new_nonce_buf, 41, sha1Buffer) + 1)) { - LOG(("AuthKey Error: received new_nonce_hash1 did not match!")); - DEBUG_LOG(("AuthKey Error: received new_nonce_hash1: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash1(), 16).str()).arg(Logs::mb(_authKeyData->new_nonce_buf, 41).str())); - - lockFinished.unlock(); - return restart(); - } - - uint64 salt1 = _authKeyData->new_nonce.l.l, salt2 = _authKeyData->server_nonce.l, serverSalt = salt1 ^ salt2; - sessionData->setSalt(serverSalt); - - auto authKey = std::make_shared(AuthKey::Type::Generated, BareDcId(_shiftedDcId), _authKeyStrings->auth_key); - - DEBUG_LOG(("AuthKey Info: auth key gen succeed, id: %1, server salt: %2").arg(authKey->keyId()).arg(serverSalt)); - - sessionData->owner()->notifyKeyCreated(std::move(authKey)); // slot will call authKeyCreated() - sessionData->clear(_instance); - unlockKey(); - } return; - - case mtpc_dh_gen_retry: { - const auto &resDH(res_client_DH_params.c_dh_gen_retry()); - if (resDH.vnonce() != _authKeyData->nonce) { - LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_retry)!")); - DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce(), 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str())); - - lockFinished.unlock(); - return restart(); - } - if (resDH.vserver_nonce() != _authKeyData->server_nonce) { - LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_retry)!")); - DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str())); - - lockFinished.unlock(); - return restart(); - } - _authKeyData->new_nonce_buf[32] = 2; - uchar sha1Buffer[20]; - if (resDH.vnew_nonce_hash2() != *(MTPint128*)(hashSha1(_authKeyData->new_nonce_buf, 41, sha1Buffer) + 1)) { - LOG(("AuthKey Error: received new_nonce_hash2 did not match!")); - DEBUG_LOG(("AuthKey Error: received new_nonce_hash2: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash2(), 16).str()).arg(Logs::mb(_authKeyData->new_nonce_buf, 41).str())); - - lockFinished.unlock(); - return restart(); - } - _authKeyData->retry_id = _authKeyData->auth_key_aux_hash; - } return dhClientParamsSend(); - - case mtpc_dh_gen_fail: { - const auto &resDH(res_client_DH_params.c_dh_gen_fail()); - if (resDH.vnonce() != _authKeyData->nonce) { - LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_fail)!")); - DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce(), 16).str()).arg(Logs::mb(&_authKeyData->nonce, 16).str())); - - lockFinished.unlock(); - return restart(); - } - if (resDH.vserver_nonce() != _authKeyData->server_nonce) { - LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_fail)!")); - DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_authKeyData->server_nonce, 16).str())); - - lockFinished.unlock(); - return restart(); - } - _authKeyData->new_nonce_buf[32] = 3; - uchar sha1Buffer[20]; - if (resDH.vnew_nonce_hash3() != *(MTPint128*)(hashSha1(_authKeyData->new_nonce_buf, 41, sha1Buffer) + 1)) { - LOG(("AuthKey Error: received new_nonce_hash3 did not match!")); - DEBUG_LOG(("AuthKey Error: received new_nonce_hash3: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash3(), 16).str()).arg(Logs::mb(_authKeyData->new_nonce_buf, 41).str())); - - lockFinished.unlock(); - return restart(); - } - LOG(("AuthKey Error: dh_gen_fail received!")); - } - - lockFinished.unlock(); - return restart(); - - } - LOG(("AuthKey Error: unknown set_client_DH_params_answer received, typeId = %1").arg(res_client_DH_params.type())); - - lockFinished.unlock(); - return restart(); -} - void ConnectionPrivate::authKeyCreated() { - clearAuthKeyData(); + _keyCreator = nullptr; connect(_connection, &AbstractConnection::receivedData, [=] { handleReceived(); @@ -3052,33 +2483,6 @@ void ConnectionPrivate::authKeyCreated() { emit needToSendAsync(); } -void ConnectionPrivate::clearAuthKeyData() { - auto zeroMemory = [](bytes::span bytes) { -#ifdef Q_OS_WIN2 - SecureZeroMemory(bytes.data(), bytes.size()); -#else // Q_OS_WIN - auto end = reinterpret_cast(bytes.data()) + bytes.size(); - for (volatile auto p = reinterpret_cast(bytes.data()); p != end; ++p) { - *p = 0; - } -#endif // Q_OS_WIN - }; - if (_authKeyData) { - zeroMemory(gsl::make_span(reinterpret_cast(_authKeyData.get()), sizeof(AuthKeyCreateData))); - _authKeyData.reset(); - } - if (_authKeyStrings) { - if (!_authKeyStrings->dh_prime.empty()) { - zeroMemory(_authKeyStrings->dh_prime); - } - if (!_authKeyStrings->g_a.empty()) { - zeroMemory(_authKeyStrings->g_a); - } - zeroMemory(_authKeyStrings->auth_key); - _authKeyStrings.reset(); - } -} - void ConnectionPrivate::onError( not_null connection, qint32 errorCode) { @@ -3104,7 +2508,7 @@ void ConnectionPrivate::handleError(int errorCode) { _waitForConnectedTimer.cancel(); if (errorCode == -404) { - if (_dcType == DcType::Cdn) { + if (_dcType == DcType::Cdn && !_instance->isKeysDestroyer()) { LOG(("MTP Info: -404 error received in CDN dc %1, assuming it was destroyed, recreating.").arg(_shiftedDcId)); clearMessages(); keyId = kRecreateKeyId; @@ -3124,44 +2528,6 @@ void ConnectionPrivate::handleError(int errorCode) { void ConnectionPrivate::onReadyData() { } -template -void ConnectionPrivate::sendNotSecureRequest(const Request &request) { - auto packet = _connection->prepareNotSecurePacket( - request, - base::unixtime::mtproto_msg_id()); - - DEBUG_LOG(("AuthKey Info: sending request, size: %1, time: %3" - ).arg(packet.size() - 8 - ).arg(packet[5])); - - const auto bytesSize = packet.size() * sizeof(mtpPrime); - - _connection->sendData(std::move(packet)); - - onSentSome(bytesSize); -} - -template -bool ConnectionPrivate::readNotSecureResponse(Response &response) { - onReceivedSome(); - - if (_connection->received().empty()) { - LOG(("AuthKey Error: " - "trying to read response from empty received list")); - return false; - } - - const auto buffer = std::move(_connection->received().front()); - _connection->received().pop_front(); - - const auto answer = _connection->parseNotSecureResponse(buffer); - if (answer.empty()) { - return false; - } - auto from = answer.data(); - return response.read(from, from + answer.size()); -} - bool ConnectionPrivate::sendSecureRequest( SecureRequest &&request, bool needAnyResponse, @@ -3299,8 +2665,10 @@ void ConnectionPrivate::unlockKey() { } ConnectionPrivate::~ConnectionPrivate() { - clearAuthKeyData(); - Assert(_finished && _connection == nullptr && _testConnections.empty()); + Expects(_finished); + Expects(!_connection); + Expects(_testConnections.empty()); + Expects(!_keyCreator); } void ConnectionPrivate::stop() { @@ -3316,29 +2684,4 @@ void ConnectionPrivate::stop() { } } // namespace internal - -bool IsPrimeAndGood(bytes::const_span primeBytes, int g) { - return internal::IsPrimeAndGood(primeBytes, g); -} - -bool IsGoodModExpFirst( - const openssl::BigNum &modexp, - const openssl::BigNum &prime) { - return internal::IsGoodModExpFirst(modexp, prime); -} - -ModExpFirst CreateModExp( - int g, - bytes::const_span primeBytes, - bytes::const_span randomSeed) { - return internal::CreateModExp(g, primeBytes, randomSeed); -} - -bytes::vector CreateAuthKey( - bytes::const_span firstBytes, - bytes::const_span randomBytes, - bytes::const_span primeBytes) { - return internal::CreateAuthKey(firstBytes, randomBytes, primeBytes); -} - } // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/connection.h b/Telegram/SourceFiles/mtproto/connection.h index d947e537d..e03a1815d 100644 --- a/Telegram/SourceFiles/mtproto/connection.h +++ b/Telegram/SourceFiles/mtproto/connection.h @@ -15,31 +15,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/timer.h" namespace MTP { +namespace details { +class DcKeyCreator; +} // namespace details // How much time to wait for some more requests, when sending msg acks. constexpr auto kAckSendWaiting = crl::time(10000); class Instance; -[[nodiscard]] bool IsPrimeAndGood(bytes::const_span primeBytes, int g); -struct ModExpFirst { - static constexpr auto kRandomPowerSize = 256; - - bytes::vector modexp; - bytes::vector randomPower; -}; -[[nodiscard]] bool IsGoodModExpFirst( - const openssl::BigNum &modexp, - const openssl::BigNum &prime); -[[nodiscard]] ModExpFirst CreateModExp( - int g, - bytes::const_span primeBytes, - bytes::const_span randomSeed); -[[nodiscard]] bytes::vector CreateAuthKey( - bytes::const_span firstBytes, - bytes::const_span randomBytes, - bytes::const_span primeBytes); - namespace internal { class AbstractConnection; @@ -139,11 +123,6 @@ public slots: void onReadyData(); - // Auth key creation packet receive slots - void pqAnswered(); - void dhParamsAnswered(); - void dhClientParamsAnswered(); - // General packet receive slot, connected to conn->receivedData signal void handleReceived(); @@ -214,8 +193,6 @@ private: bool setState(int32 state, int32 ifState = Connection::UpdateAlways); - bytes::vector encryptPQInnerRSA(const MTPP_Q_inner_data &data, const internal::RSAPublicKey &key); - std::string encryptClientDHInner(const MTPClient_DH_Inner_Data &data); void appendTestConnection( DcOptions::Variants::Protocol protocol, const QString &ip, @@ -231,11 +208,11 @@ private: void resend(quint64 msgId, qint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false); void resendMany(QVector msgIds, qint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false); - template - void sendNotSecureRequest(const Request &request); - - template - [[nodiscard]] bool readNotSecureResponse(Response &response); + void createDcKey(); + void resetSession(); + void lockKey(); + void unlockKey(); + void authKeyCreated(); not_null _instance; DcType _dcType = DcType::Regular; @@ -244,7 +221,6 @@ private: int32 _state = DisconnectedState; bool _needSessionReset = false; - void resetSession(); ShiftedDcId _shiftedDcId = 0; not_null _owner; @@ -283,40 +259,8 @@ private: std::unique_ptr _connectionOptions; bool myKeyLock = false; - void lockKey(); - void unlockKey(); - // Auth key creation fields and methods - struct AuthKeyCreateData { - AuthKeyCreateData() - : new_nonce(*(MTPint256*)((uchar*)new_nonce_buf)) - , auth_key_aux_hash(*(MTPlong*)((uchar*)new_nonce_buf + 33)) { - } - MTPint128 nonce, server_nonce; - uchar new_nonce_buf[41] = { 0 }; // 32 bytes new_nonce + 1 check byte + 8 bytes of auth_key_aux_hash - MTPint256 &new_nonce; - MTPlong &auth_key_aux_hash; - - uint32 retries = 0; - MTPlong retry_id; - - int32 g = 0; - - uchar aesKey[32] = { 0 }; - uchar aesIV[32] = { 0 }; - MTPlong auth_key_hash; - }; - struct AuthKeyCreateStrings { - bytes::vector dh_prime; - bytes::vector g_a; - AuthKey::Data auth_key = { { gsl::byte{} } }; - }; - std::unique_ptr _authKeyData; - std::unique_ptr _authKeyStrings; - - void dhClientParamsSend(); - void authKeyCreated(); - void clearAuthKeyData(); + std::unique_ptr _keyCreator; }; diff --git a/Telegram/SourceFiles/mtproto/connection_abstract.cpp b/Telegram/SourceFiles/mtproto/connection_abstract.cpp index 48974856d..831141c29 100644 --- a/Telegram/SourceFiles/mtproto/connection_abstract.cpp +++ b/Telegram/SourceFiles/mtproto/connection_abstract.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/connection_resolving.h" #include "mtproto/session.h" #include "base/unixtime.h" +#include "base/openssl_help.h" namespace MTP { namespace internal { @@ -187,5 +188,11 @@ ConnectionPointer AbstractConnection::Create( return result; } +uint32 AbstractConnection::extendedNotSecurePadding() const { + return requiresExtendedPadding() + ? uint32(openssl::RandomValue() & 0x3F) + : 0; +} + } // namespace internal } // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/connection_abstract.h b/Telegram/SourceFiles/mtproto/connection_abstract.h index 889fa831e..802f9fbd9 100644 --- a/Telegram/SourceFiles/mtproto/connection_abstract.h +++ b/Telegram/SourceFiles/mtproto/connection_abstract.h @@ -8,8 +8,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "mtproto/dc_options.h" +#include "mtproto/mtproto_proxy_data.h" #include "base/bytes.h" +#include +#include + namespace MTP { class Instance; @@ -60,17 +64,17 @@ public: virtual ~AbstractConnection() = default; // virtual constructor - static ConnectionPointer Create( + [[nodiscard]] static ConnectionPointer Create( not_null instance, DcOptions::Variants::Protocol protocol, QThread *thread, const bytes::vector &secret, const ProxyData &proxy); - virtual ConnectionPointer clone(const ProxyData &proxy) = 0; + [[nodiscard]] virtual ConnectionPointer clone(const ProxyData &proxy) = 0; - virtual crl::time pingTime() const = 0; - virtual crl::time fullConnectTimeout() const = 0; + [[nodiscard]] virtual crl::time pingTime() const = 0; + [[nodiscard]] virtual crl::time fullConnectTimeout() const = 0; virtual void sendData(mtpBuffer &&buffer) = 0; virtual void disconnectFromServer() = 0; virtual void connectToServer( @@ -80,41 +84,41 @@ public: int16 protocolDcId) = 0; virtual void timedOut() { } - virtual bool isConnected() const = 0; - virtual bool usingHttpWait() { + [[nodiscard]] virtual bool isConnected() const = 0; + [[nodiscard]] virtual bool usingHttpWait() { return false; } - virtual bool needHttpWait() { + [[nodiscard]] virtual bool needHttpWait() { return false; } - virtual bool requiresExtendedPadding() const { + [[nodiscard]] virtual bool requiresExtendedPadding() const { return false; } - virtual int32 debugState() const = 0; + [[nodiscard]] virtual int32 debugState() const = 0; - virtual QString transport() const = 0; - virtual QString tag() const = 0; + [[nodiscard]] virtual QString transport() const = 0; + [[nodiscard]] virtual QString tag() const = 0; void setSentEncrypted() { _sentEncrypted = true; } using BuffersQueue = std::deque; - BuffersQueue &received() { + [[nodiscard]] BuffersQueue &received() { return _receivedQueue; } template - mtpBuffer prepareNotSecurePacket( + [[nodiscard]] mtpBuffer prepareNotSecurePacket( const Request &request, mtpMsgId newId) const; - mtpBuffer prepareSecurePacket( + [[nodiscard]] mtpBuffer prepareSecurePacket( uint64 keyId, MTPint128 msgKey, uint32 size) const; - gsl::span parseNotSecureResponse( + [[nodiscard]] gsl::span parseNotSecureResponse( const mtpBuffer &buffer) const; // Used to emit error(...) with no real code from the server. @@ -139,8 +143,12 @@ protected: // first we always send fake MTPReq_pq to see if connection works at all // we send them simultaneously through TCP/HTTP/IPv4/IPv6 to choose the working one - mtpBuffer preparePQFake(const MTPint128 &nonce) const; - std::optional readPQFakeReply(const mtpBuffer &buffer) const; + [[nodiscard]] mtpBuffer preparePQFake(const MTPint128 &nonce) const; + [[nodiscard]] std::optional readPQFakeReply( + const mtpBuffer &buffer) const; + +private: + [[nodiscard]] uint32 extendedNotSecurePadding() const; }; @@ -149,9 +157,7 @@ mtpBuffer AbstractConnection::prepareNotSecurePacket( const Request &request, mtpMsgId newId) const { const auto intsSize = tl::count_length(request) >> 2; - const auto intsPadding = requiresExtendedPadding() - ? uint32(rand_value() & 0x3F) - : 0; + const auto intsPadding = extendedNotSecurePadding(); auto result = mtpBuffer(); constexpr auto kTcpPrefixInts = 2; @@ -176,10 +182,10 @@ mtpBuffer AbstractConnection::prepareNotSecurePacket( *messageLength = (result.size() - kPrefixInts + intsPadding) << 2; if (intsPadding > 0) { - result.resize(result.size() + intsPadding); - memset_rand( - result.data() + result.size() - intsPadding, - intsPadding * sizeof(mtpPrime)); + const auto skipPrimes = result.size(); + result.resize(skipPrimes + intsPadding); + const auto skipBytes = skipPrimes * sizeof(mtpPrime); + bytes::set_random(bytes::make_span(result).subspan(skipBytes)); } return result; diff --git a/Telegram/SourceFiles/mtproto/dc_options.cpp b/Telegram/SourceFiles/mtproto/dc_options.cpp index 54d15ca1c..a072048d4 100644 --- a/Telegram/SourceFiles/mtproto/dc_options.cpp +++ b/Telegram/SourceFiles/mtproto/dc_options.cpp @@ -105,8 +105,8 @@ void DcOptions::readBuiltInPublicKeys() { for (const auto key : PublicRSAKeys) { const auto keyBytes = bytes::make_span(key, strlen(key)); auto parsed = internal::RSAPublicKey(keyBytes); - if (parsed.isValid()) { - _publicKeys.emplace(parsed.getFingerPrint(), std::move(parsed)); + if (parsed.valid()) { + _publicKeys.emplace(parsed.fingerprint(), std::move(parsed)); } else { LOG(("MTP Error: could not read this public RSA key:")); LOG((key)); @@ -505,8 +505,8 @@ void DcOptions::constructFromSerialized(const QByteArray &serialized) { } auto key = internal::RSAPublicKey(n, e); - if (key.isValid()) { - _cdnPublicKeys[dcId].emplace(key.getFingerPrint(), std::move(key)); + if (key.valid()) { + _cdnPublicKeys[dcId].emplace(key.fingerprint(), std::move(key)); } else { LOG(("MTP Error: Could not read valid CDN public key.")); } @@ -554,9 +554,9 @@ void DcOptions::setCDNConfig(const MTPDcdnConfig &config) { const auto &keyData = publicKey.c_cdnPublicKey(); const auto keyBytes = bytes::make_span(keyData.vpublic_key().v); auto key = internal::RSAPublicKey(keyBytes); - if (key.isValid()) { + if (key.valid()) { _cdnPublicKeys[keyData.vdc_id().v].emplace( - key.getFingerPrint(), + key.fingerprint(), std::move(key)); } else { LOG(("MTP Error: could not read this public RSA key:")); @@ -570,20 +570,22 @@ bool DcOptions::hasCDNKeysForDc(DcId dcId) const { return _cdnPublicKeys.find(dcId) != _cdnPublicKeys.cend(); } -bool DcOptions::getDcRSAKey(DcId dcId, const QVector &fingerprints, internal::RSAPublicKey *result) const { - auto findKey = [&fingerprints, &result](const std::map &keys) { - for_const (auto &fingerprint, fingerprints) { - auto it = keys.find(static_cast(fingerprint.v)); +internal::RSAPublicKey DcOptions::getDcRSAKey( + DcId dcId, + const QVector &fingerprints) const { + const auto findKey = [&]( + const std::map &keys) { + for (const auto &fingerprint : fingerprints) { + const auto it = keys.find(static_cast(fingerprint.v)); if (it != keys.cend()) { - *result = it->second; - return true; + return it->second; } } - return false; + return internal::RSAPublicKey(); }; { ReadLocker lock(this); - auto it = _cdnPublicKeys.find(dcId); + const auto it = _cdnPublicKeys.find(dcId); if (it != _cdnPublicKeys.cend()) { return findKey(it->second); } diff --git a/Telegram/SourceFiles/mtproto/dc_options.h b/Telegram/SourceFiles/mtproto/dc_options.h index cbd20d68a..71d3dec85 100644 --- a/Telegram/SourceFiles/mtproto/dc_options.h +++ b/Telegram/SourceFiles/mtproto/dc_options.h @@ -10,9 +10,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/observer.h" #include "base/bytes.h" #include "mtproto/rsa_public_key.h" + +#include #include #include #include +#include namespace MTP { @@ -90,8 +93,10 @@ public: DcType dcType(ShiftedDcId shiftedDcId) const; void setCDNConfig(const MTPDcdnConfig &config); - bool hasCDNKeysForDc(DcId dcId) const; - bool getDcRSAKey(DcId dcId, const QVector &fingerprints, internal::RSAPublicKey *result) const; + [[nodiscard]] bool hasCDNKeysForDc(DcId dcId) const; + [[nodiscard]] internal::RSAPublicKey getDcRSAKey( + DcId dcId, + const QVector &fingerprints) const; // Debug feature for now. bool loadFromFile(const QString &path); diff --git a/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_checker.cpp b/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_checker.cpp new file mode 100644 index 000000000..54f713a20 --- /dev/null +++ b/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_checker.cpp @@ -0,0 +1,31 @@ +/* +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 "mtproto/details/mtproto_dc_key_checker.h" + +#include "mtproto/mtp_instance.h" + +#include + +namespace MTP::details { + +DcKeyChecker::DcKeyChecker( + not_null instance, + DcId dcId, + const AuthKeyPtr &key, + FnMut destroyMe) +: _instance(instance) +, _dcId(dcId) +, _key(key) +, _destroyMe(std::move(destroyMe)) { + crl::on_main(instance, [=] { + auto destroy = std::move(_destroyMe); + destroy(); + }); +} + +} // namespace MTP::details diff --git a/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_checker.h b/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_checker.h new file mode 100644 index 000000000..08f631277 --- /dev/null +++ b/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_checker.h @@ -0,0 +1,35 @@ +/* +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 "mtproto/core_types.h" +#include "mtproto/auth_key.h" + +namespace MTP { +class Instance; +} // namespace MTP + +namespace MTP::details { + +class DcKeyChecker final { +public: + DcKeyChecker( + not_null instance, + DcId dcId, + const AuthKeyPtr &key, + FnMut destroyMe); + +private: + not_null _instance; + DcId _dcId = 0; + AuthKeyPtr _key; + FnMut _destroyMe; + +}; + +} // namespace MTP::details diff --git a/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.cpp b/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.cpp new file mode 100644 index 000000000..1f12d81ac --- /dev/null +++ b/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.cpp @@ -0,0 +1,572 @@ +/* +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 "mtproto/details/mtproto_dc_key_creator.h" + +#include "mtproto/connection_abstract.h" +#include "mtproto/mtproto_dh_utils.h" +#include "base/openssl_help.h" +#include "base/unixtime.h" +#include "scheme.h" +#include "logs.h" + +namespace MTP::details { +namespace { + +struct ParsedPQ { + QByteArray p; + QByteArray q; +}; + +[[nodiscard]] ParsedPQ ParsePQ(const QByteArray &pqStr) { + if (pqStr.length() > 8) { + // More than 64 bit pq. + return ParsedPQ(); + } + + uint64 pq = 0, p, q; + const uchar *pqChars = (const uchar*)pqStr.constData(); + for (uint32 i = 0, l = pqStr.length(); i < l; ++i) { + pq <<= 8; + pq |= (uint64)pqChars[i]; + } + uint64 pqSqrt = (uint64)sqrtl((long double)pq), ySqr, y; + while (pqSqrt * pqSqrt > pq) --pqSqrt; + while (pqSqrt * pqSqrt < pq) ++pqSqrt; + for (ySqr = pqSqrt * pqSqrt - pq; ; ++pqSqrt, ySqr = pqSqrt * pqSqrt - pq) { + y = (uint64)sqrtl((long double)ySqr); + while (y * y > ySqr) --y; + while (y * y < ySqr) ++y; + if (!ySqr || y + pqSqrt >= pq) { + return ParsedPQ(); + } + if (y * y == ySqr) { + p = pqSqrt + y; + q = (pqSqrt > y) ? (pqSqrt - y) : (y - pqSqrt); + break; + } + } + if (p > q) std::swap(p, q); + + auto pStr = QByteArray(4, Qt::Uninitialized); + uchar *pChars = (uchar*)pStr.data(); + for (uint32 i = 0; i < 4; ++i) { + *(pChars + 3 - i) = (uchar)(p & 0xFF); + p >>= 8; + } + + auto qStr = QByteArray(4, Qt::Uninitialized); + uchar *qChars = (uchar*)qStr.data(); + for (uint32 i = 0; i < 4; ++i) { + *(qChars + 3 - i) = (uchar)(q & 0xFF); + q >>= 8; + } + + return { pStr, qStr }; +} + +[[nodiscard]] bytes::vector EncryptPQInnerRSA( + const MTPP_Q_inner_data &data, + const RSAPublicKey &key) { + constexpr auto kSkipPrimes = 6; + constexpr auto kMaxPrimes = 65; // 260 bytes + + const auto p_q_inner_size = tl::count_length(data); + const auto sizeInPrimes = (p_q_inner_size >> 2) + kSkipPrimes; + if (sizeInPrimes >= kMaxPrimes) { + auto tmp = mtpBuffer(); + tmp.reserve(sizeInPrimes); + data.write(tmp); + LOG(("AuthKey Error: too large data for RSA encrypt, size %1").arg(sizeInPrimes * sizeof(mtpPrime))); + DEBUG_LOG(("AuthKey Error: bad data for RSA encrypt %1").arg(Logs::mb(&tmp[0], tmp.size() * 4).str())); + return {}; // can't be 255-byte string + } + + auto encBuffer = mtpBuffer(); + encBuffer.reserve(kMaxPrimes); + encBuffer.resize(kSkipPrimes); + data.write(encBuffer); + encBuffer.resize(kMaxPrimes); + const auto bytes = bytes::make_span(encBuffer); + + const auto hashSrc = bytes.subspan( + kSkipPrimes * sizeof(mtpPrime), + p_q_inner_size); + bytes::copy(bytes.subspan(sizeof(mtpPrime)), openssl::Sha1(hashSrc)); + bytes::set_random(bytes.subspan(sizeInPrimes * sizeof(mtpPrime))); + + const auto bytesToEncrypt = bytes.subspan(3, 256); + return key.encrypt(bytesToEncrypt); +} + +[[nodiscard]] std::string EncryptClientDHInner( + const MTPClient_DH_Inner_Data &data, + const void *aesKey, + const void *aesIV) { + constexpr auto kSkipPrimes = openssl::kSha1Size / sizeof(mtpPrime); + + auto client_dh_inner_size = tl::count_length(data); + auto encSize = (client_dh_inner_size >> 2) + kSkipPrimes; + auto encFullSize = encSize; + if (encSize & 0x03) { + encFullSize += 4 - (encSize & 0x03); + } + + auto encBuffer = mtpBuffer(); + encBuffer.reserve(encFullSize); + encBuffer.resize(kSkipPrimes); + data.write(encBuffer); + encBuffer.resize(encFullSize); + + const auto bytes = bytes::make_span(encBuffer); + + const auto hash = openssl::Sha1(bytes.subspan( + kSkipPrimes * sizeof(mtpPrime), + client_dh_inner_size)); + bytes::copy(bytes, hash); + bytes::set_random(bytes.subspan(encSize * sizeof(mtpPrime))); + + auto sdhEncString = std::string(encFullSize * 4, ' '); + + aesIgeEncryptRaw(&encBuffer[0], &sdhEncString[0], encFullSize * sizeof(mtpPrime), aesKey, aesIV); + + return sdhEncString; +} + +// 128 lower-order bits of SHA1. +MTPint128 NonceDigest(bytes::const_span data) { + const auto hash = openssl::Sha1(data); + return *(MTPint128*)(hash.data() + 4); +} + +} // namespace + +DcKeyCreator::DcKeyCreator( + DcId dcId, + int16 protocolDcId, + not_null connection, + not_null dcOptions, + Delegate delegate) +: _connection(connection) +, _dcOptions(dcOptions) +, _dcId(dcId) +, _protocolDcId(protocolDcId) +, _delegate(std::move(delegate)) { + Expects(_delegate.done != nullptr); + + _data.nonce = openssl::RandomValue(); + pqSend(); +} + +DcKeyCreator::~DcKeyCreator() { + const auto clearBytes = [](bytes::span bytes) { + OPENSSL_cleanse(bytes.data(), bytes.size()); + }; + OPENSSL_cleanse(&_data, sizeof(_data)); + clearBytes(_dhPrime); + clearBytes(_g_a); + clearBytes(_authKey); +} + +void DcKeyCreator::pqSend() { + QObject::connect(_connection, &AbstractConnection::receivedData, [=] { + pqAnswered(); + }); + + DEBUG_LOG(("AuthKey Info: sending Req_pq...")); + sendNotSecureRequest(MTPReq_pq_multi(_data.nonce)); +} + +void DcKeyCreator::pqAnswered() { + QObject::disconnect( + _connection, + &AbstractConnection::receivedData, + nullptr, + nullptr); + DEBUG_LOG(("AuthKey Info: receiving Req_pq answer...")); + + MTPReq_pq::ResponseType res_pq; + if (!readNotSecureResponse(res_pq)) { + return failed(); + } + + auto &res_pq_data = res_pq.c_resPQ(); + if (res_pq_data.vnonce() != _data.nonce) { + LOG(("AuthKey Error: received nonce <> sent nonce (in res_pq)!")); + DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&res_pq_data.vnonce(), 16).str()).arg(Logs::mb(&_data.nonce, 16).str())); + return failed(); + } + + const auto rsaKey = _dcOptions->getDcRSAKey( + _dcId, + res_pq.c_resPQ().vserver_public_key_fingerprints().v); + if (!rsaKey.valid()) { + return failed(Error::UnknownPublicKey); + } + + _data.server_nonce = res_pq_data.vserver_nonce(); + _data.new_nonce = openssl::RandomValue(); + + const auto &pq = res_pq_data.vpq().v; + const auto parsed = ParsePQ(res_pq_data.vpq().v); + if (parsed.p.isEmpty() || parsed.q.isEmpty()) { + LOG(("AuthKey Error: could not factor pq!")); + DEBUG_LOG(("AuthKey Error: problematic pq: %1").arg(Logs::mb(pq.constData(), pq.length()).str())); + return failed(); + } + + auto p_q_inner = MTP_p_q_inner_data_dc( + res_pq_data.vpq(), + MTP_bytes(parsed.p), + MTP_bytes(parsed.q), + _data.nonce, + _data.server_nonce, + _data.new_nonce, + MTP_int(_protocolDcId)); + const auto dhEncString = EncryptPQInnerRSA(p_q_inner, rsaKey); + if (dhEncString.empty()) { + return failed(); + } + + QObject::connect(_connection, &AbstractConnection::receivedData, [=] { + dhParamsAnswered(); + }); + + DEBUG_LOG(("AuthKey Info: sending Req_DH_params...")); + + sendNotSecureRequest(MTPReq_DH_params( + _data.nonce, + _data.server_nonce, + p_q_inner.c_p_q_inner_data_dc().vp(), + p_q_inner.c_p_q_inner_data_dc().vq(), + MTP_long(rsaKey.fingerprint()), + MTP_bytes(dhEncString))); +} + +void DcKeyCreator::dhParamsAnswered() { + QObject::disconnect( + _connection, + &AbstractConnection::receivedData, + nullptr, + nullptr); + DEBUG_LOG(("AuthKey Info: receiving Req_DH_params answer...")); + + MTPReq_DH_params::ResponseType res_DH_params; + if (!readNotSecureResponse(res_DH_params)) { + return failed(); + } + + switch (res_DH_params.type()) { + case mtpc_server_DH_params_ok: { + const auto &encDH(res_DH_params.c_server_DH_params_ok()); + if (encDH.vnonce() != _data.nonce) { + LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_params_ok)!")); + DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&encDH.vnonce(), 16).str()).arg(Logs::mb(&_data.nonce, 16).str())); + return failed(); + } + if (encDH.vserver_nonce() != _data.server_nonce) { + LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_params_ok)!")); + DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&encDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_data.server_nonce, 16).str())); + return failed(); + } + + auto &encDHStr = encDH.vencrypted_answer().v; + uint32 encDHLen = encDHStr.length(), encDHBufLen = encDHLen >> 2; + if ((encDHLen & 0x03) || encDHBufLen < 6) { + LOG(("AuthKey Error: bad encrypted data length %1 (in server_DH_params_ok)!").arg(encDHLen)); + DEBUG_LOG(("AuthKey Error: received encrypted data %1").arg(Logs::mb(encDHStr.constData(), encDHLen).str())); + return failed(); + } + + const auto nlen = sizeof(_data.new_nonce); + const auto slen = sizeof(_data.server_nonce); + auto tmp_aes_buffer = bytes::array<1024>(); + const auto tmp_aes = bytes::make_span(tmp_aes_buffer); + bytes::copy(tmp_aes, bytes::object_as_span(&_data.new_nonce)); + bytes::copy(tmp_aes.subspan(nlen), bytes::object_as_span(&_data.server_nonce)); + bytes::copy(tmp_aes.subspan(nlen + slen), bytes::object_as_span(&_data.new_nonce)); + bytes::copy(tmp_aes.subspan(nlen + slen + nlen), bytes::object_as_span(&_data.new_nonce)); + const auto sha1ns = openssl::Sha1(tmp_aes.subspan(0, nlen + slen)); + const auto sha1sn = openssl::Sha1(tmp_aes.subspan(nlen, nlen + slen)); + const auto sha1nn = openssl::Sha1(tmp_aes.subspan(nlen + slen, nlen + nlen)); + + mtpBuffer decBuffer; + decBuffer.resize(encDHBufLen); + + const auto aesKey = bytes::make_span(_data.aesKey); + const auto aesIV = bytes::make_span(_data.aesIV); + bytes::copy(aesKey, bytes::make_span(sha1ns).subspan(0, 20)); + bytes::copy(aesKey.subspan(20), bytes::make_span(sha1sn).subspan(0, 12)); + bytes::copy(aesIV, bytes::make_span(sha1sn).subspan(12, 8)); + bytes::copy(aesIV.subspan(8), bytes::make_span(sha1nn).subspan(0, 20)); + bytes::copy(aesIV.subspan(28), bytes::object_as_span(&_data.new_nonce).subspan(0, 4)); + + aesIgeDecryptRaw(encDHStr.constData(), &decBuffer[0], encDHLen, aesKey.data(), aesIV.data()); + + const mtpPrime *from(&decBuffer[5]), *to(from), *end(from + (encDHBufLen - 5)); + MTPServer_DH_inner_data dh_inner; + if (!dh_inner.read(to, end)) { + LOG(("AuthKey Error: could not decrypt server_DH_inner_data!")); + return failed(); + } + const auto &dh_inner_data(dh_inner.c_server_DH_inner_data()); + if (dh_inner_data.vnonce() != _data.nonce) { + LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_inner_data)!")); + DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&dh_inner_data.vnonce(), 16).str()).arg(Logs::mb(&_data.nonce, 16).str())); + return failed(); + } + if (dh_inner_data.vserver_nonce() != _data.server_nonce) { + LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_inner_data)!")); + DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&dh_inner_data.vserver_nonce(), 16).str()).arg(Logs::mb(&_data.server_nonce, 16).str())); + return failed(); + } + const auto sha1Buffer = openssl::Sha1( + bytes::make_span(decBuffer).subspan( + 5 * sizeof(mtpPrime), + (to - from) * sizeof(mtpPrime))); + const auto sha1Dec = bytes::make_span(decBuffer).subspan( + 0, + openssl::kSha1Size); + if (bytes::compare(sha1Dec, sha1Buffer)) { + LOG(("AuthKey Error: sha1 hash of encrypted part did not match!")); + DEBUG_LOG(("AuthKey Error: sha1 did not match, server_nonce: %1, new_nonce %2, encrypted data %3").arg(Logs::mb(&_data.server_nonce, 16).str()).arg(Logs::mb(&_data.new_nonce, 16).str()).arg(Logs::mb(encDHStr.constData(), encDHLen).str())); + return failed(); + } + base::unixtime::update(dh_inner_data.vserver_time().v); + + // check that dhPrime and (dhPrime - 1) / 2 are really prime + if (!IsPrimeAndGood(bytes::make_span(dh_inner_data.vdh_prime().v), dh_inner_data.vg().v)) { + LOG(("AuthKey Error: bad dh_prime primality!")); + return failed(); + } + + _dhPrime = bytes::make_vector( + dh_inner_data.vdh_prime().v); + _data.g = dh_inner_data.vg().v; + _g_a = bytes::make_vector(dh_inner_data.vg_a().v); + _data.retry_id = MTP_long(0); + _data.retries = 0; + } return dhClientParamsSend(); + + case mtpc_server_DH_params_fail: { + const auto &encDH(res_DH_params.c_server_DH_params_fail()); + if (encDH.vnonce() != _data.nonce) { + LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_params_fail)!")); + DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&encDH.vnonce(), 16).str()).arg(Logs::mb(&_data.nonce, 16).str())); + return failed(); + } + if (encDH.vserver_nonce() != _data.server_nonce) { + LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_params_fail)!")); + DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&encDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_data.server_nonce, 16).str())); + return failed(); + } + if (encDH.vnew_nonce_hash() != NonceDigest(bytes::object_as_span(&_data.new_nonce))) { + LOG(("AuthKey Error: received new_nonce_hash did not match!")); + DEBUG_LOG(("AuthKey Error: received new_nonce_hash: %1, new_nonce: %2").arg(Logs::mb(&encDH.vnew_nonce_hash(), 16).str()).arg(Logs::mb(&_data.new_nonce, 32).str())); + return failed(); + } + LOG(("AuthKey Error: server_DH_params_fail received!")); + } return failed(); + + } + LOG(("AuthKey Error: unknown server_DH_params received, typeId = %1").arg(res_DH_params.type())); + return failed(); +} + +void DcKeyCreator::dhClientParamsSend() { + if (++_data.retries > 5) { + LOG(("AuthKey Error: could not create auth_key for %1 retries").arg(_data.retries - 1)); + return failed(); + } + + // gen rand 'b' + auto randomSeed = bytes::vector(ModExpFirst::kRandomPowerSize); + bytes::set_random(randomSeed); + auto g_b_data = CreateModExp(_data.g, _dhPrime, randomSeed); + if (g_b_data.modexp.empty()) { + LOG(("AuthKey Error: could not generate good g_b.")); + return failed(); + } + + auto computedAuthKey = CreateAuthKey(_g_a, g_b_data.randomPower, _dhPrime); + if (computedAuthKey.empty()) { + LOG(("AuthKey Error: could not generate auth_key.")); + return failed(); + } + AuthKey::FillData(_authKey, computedAuthKey); + + auto auth_key_sha = openssl::Sha1(_authKey); + memcpy(&_data.auth_key_aux_hash, auth_key_sha.data(), 8); + memcpy(&_data.auth_key_hash, auth_key_sha.data() + 12, 8); + + const auto client_dh_inner = MTP_client_DH_inner_data( + _data.nonce, + _data.server_nonce, + _data.retry_id, + MTP_bytes(g_b_data.modexp)); + + auto sdhEncString = EncryptClientDHInner( + client_dh_inner, + _data.aesKey.data(), + _data.aesIV.data()); + + QObject::connect(_connection, &AbstractConnection::receivedData, [=] { + dhClientParamsAnswered(); + }); + + DEBUG_LOG(("AuthKey Info: sending Req_client_DH_params...")); + sendNotSecureRequest(MTPSet_client_DH_params( + _data.nonce, + _data.server_nonce, + MTP_string(std::move(sdhEncString)))); +} + +void DcKeyCreator::dhClientParamsAnswered() { + QObject::disconnect( + _connection, + &AbstractConnection::receivedData, + nullptr, + nullptr); + DEBUG_LOG(("AuthKey Info: receiving Req_client_DH_params answer...")); + + MTPSet_client_DH_params::ResponseType res_client_DH_params; + if (!readNotSecureResponse(res_client_DH_params)) { + return failed(); + } + + switch (res_client_DH_params.type()) { + case mtpc_dh_gen_ok: { + const auto &resDH(res_client_DH_params.c_dh_gen_ok()); + if (resDH.vnonce() != _data.nonce) { + LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_ok)!")); + DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce(), 16).str()).arg(Logs::mb(&_data.nonce, 16).str())); + return failed(); + } + if (resDH.vserver_nonce() != _data.server_nonce) { + LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_ok)!")); + DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_data.server_nonce, 16).str())); + return failed(); + } + _data.new_nonce_buf[32] = bytes::type(1); + if (resDH.vnew_nonce_hash1() != NonceDigest(_data.new_nonce_buf)) { + LOG(("AuthKey Error: received new_nonce_hash1 did not match!")); + DEBUG_LOG(("AuthKey Error: received new_nonce_hash1: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash1(), 16).str()).arg(Logs::mb(_data.new_nonce_buf.data(), 41).str())); + return failed(); + } + + uint64 salt1 = _data.new_nonce.l.l, salt2 = _data.server_nonce.l; + done(salt1 ^ salt2); + } return; + + case mtpc_dh_gen_retry: { + const auto &resDH(res_client_DH_params.c_dh_gen_retry()); + if (resDH.vnonce() != _data.nonce) { + LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_retry)!")); + DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce(), 16).str()).arg(Logs::mb(&_data.nonce, 16).str())); + return failed(); + } + if (resDH.vserver_nonce() != _data.server_nonce) { + LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_retry)!")); + DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_data.server_nonce, 16).str())); + return failed(); + } + _data.new_nonce_buf[32] = bytes::type(2); + uchar sha1Buffer[20]; + if (resDH.vnew_nonce_hash2() != NonceDigest(_data.new_nonce_buf)) { + LOG(("AuthKey Error: received new_nonce_hash2 did not match!")); + DEBUG_LOG(("AuthKey Error: received new_nonce_hash2: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash2(), 16).str()).arg(Logs::mb(_data.new_nonce_buf.data(), 41).str())); + return failed(); + } + _data.retry_id = _data.auth_key_aux_hash; + } return dhClientParamsSend(); + + case mtpc_dh_gen_fail: { + const auto &resDH(res_client_DH_params.c_dh_gen_fail()); + if (resDH.vnonce() != _data.nonce) { + LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_fail)!")); + DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce(), 16).str()).arg(Logs::mb(&_data.nonce, 16).str())); + return failed(); + } + if (resDH.vserver_nonce() != _data.server_nonce) { + LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_fail)!")); + DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_data.server_nonce, 16).str())); + return failed(); + } + _data.new_nonce_buf[32] = bytes::type(3); + uchar sha1Buffer[20]; + if (resDH.vnew_nonce_hash3() != NonceDigest(_data.new_nonce_buf)) { + LOG(("AuthKey Error: received new_nonce_hash3 did not match!")); + DEBUG_LOG(("AuthKey Error: received new_nonce_hash3: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash3(), 16).str()).arg(Logs::mb(_data.new_nonce_buf.data(), 41).str())); + return failed(); + } + LOG(("AuthKey Error: dh_gen_fail received!")); + } return failed(); + } + + LOG(("AuthKey Error: unknown set_client_DH_params_answer received, typeId = %1").arg(res_client_DH_params.type())); + return failed(); +} + +template +void DcKeyCreator::sendNotSecureRequest(const Request &request) { + auto packet = _connection->prepareNotSecurePacket( + request, + base::unixtime::mtproto_msg_id()); + + DEBUG_LOG(("AuthKey Info: sending request, size: %1, time: %3" + ).arg(packet.size() - 8 + ).arg(packet[5])); + + const auto bytesSize = packet.size() * sizeof(mtpPrime); + + _connection->sendData(std::move(packet)); + + if (_delegate.sentSome) { + _delegate.sentSome(bytesSize); + } +} + +template +bool DcKeyCreator::readNotSecureResponse(Response &response) { + if (_delegate.receivedSome) { + _delegate.receivedSome(); + } + + if (_connection->received().empty()) { + LOG(("AuthKey Error: " + "trying to read response from empty received list")); + return false; + } + + const auto buffer = std::move(_connection->received().front()); + _connection->received().pop_front(); + + const auto answer = _connection->parseNotSecureResponse(buffer); + if (answer.empty()) { + return false; + } + auto from = answer.data(); + return response.read(from, from + answer.size()); +} + +void DcKeyCreator::failed(Error error) { + auto onstack = std::move(_delegate.done); + onstack(tl::unexpected(error)); +} + +void DcKeyCreator::done(uint64 serverSalt) { + auto result = Result(); + result.key = std::make_shared( + AuthKey::Type::Generated, + _dcId, + _authKey); + result.serverSalt = serverSalt; + auto onstack = std::move(_delegate.done); + onstack(std::move(result)); +} + +} // namespace MTP::details diff --git a/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.h b/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.h new file mode 100644 index 000000000..6d774143b --- /dev/null +++ b/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.h @@ -0,0 +1,102 @@ +/* +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 "mtproto/core_types.h" +#include "mtproto/auth_key.h" +#include "mtproto/connection_abstract.h" +#include "base/basic_types.h" +#include "base/expected.h" + +namespace MTP { +class DcOptions; +} // namespace MTP + +namespace MTP::details { + +using namespace ::MTP::internal; + +class DcKeyCreator final { +public: + enum class Error { + UnknownPublicKey, + Other, + }; + struct Result { + AuthKeyPtr key; + uint64 serverSalt = 0; + }; + struct Delegate { + FnMut)> done; + Fn sentSome; + Fn receivedSome; + }; + + DcKeyCreator( + DcId dcId, + int16 protocolDcId, + not_null connection, + not_null dcOptions, + Delegate delegate); + ~DcKeyCreator(); + +private: + // Auth key creation fields and methods + struct Data { + Data() + : new_nonce(*(MTPint256*)((uchar*)new_nonce_buf.data())) + , auth_key_aux_hash(*(MTPlong*)((uchar*)new_nonce_buf.data() + 33)) { + } + MTPint128 nonce, server_nonce; + + // 32 bytes new_nonce + 1 check byte + 8 bytes of auth_key_aux_hash. + bytes::array<41> new_nonce_buf; + + MTPint256 &new_nonce; + MTPlong &auth_key_aux_hash; + + uint32 retries = 0; + MTPlong retry_id; + + int32 g = 0; + + bytes::array<32> aesKey; + bytes::array<32> aesIV; + MTPlong auth_key_hash; + }; + + template + void sendNotSecureRequest(const Request &request); + + template + [[nodiscard]] bool readNotSecureResponse(Response &response); + + void pqSend(); + void pqAnswered(); + void dhParamsAnswered(); + void dhClientParamsSend(); + void dhClientParamsAnswered(); + + void failed(Error error = Error::Other); + void done(uint64 serverSalt); + + const not_null _connection; + const not_null _dcOptions; + const DcId _dcId; + const int16 _protocolDcId = 0; + Delegate _delegate; + + Data _data; + bytes::vector _dhPrime; + bytes::vector _g_a; + AuthKey::Data _authKey = { { gsl::byte{} } }; + FnMut)> _done; + +}; + +} // namespace MTP::details diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.cpp b/Telegram/SourceFiles/mtproto/mtp_instance.cpp index 14ea8fae7..64881911e 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_instance.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "mtproto/mtp_instance.h" +#include "mtproto/details/mtproto_dc_key_checker.h" #include "mtproto/session.h" #include "mtproto/dc_options.h" #include "mtproto/dcenter.h" @@ -74,7 +75,6 @@ public: void cancel(mtpRequestId requestId); [[nodiscard]] int32 state(mtpRequestId requestId); // < 0 means waiting for such count of ms void killSession(ShiftedDcId shiftedDcId); - void killSession(std::unique_ptr session); void stopSession(ShiftedDcId shiftedDcId); void reInitConnection(DcId dcId); void logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail); @@ -133,6 +133,7 @@ public: void scheduleKeyDestroy(ShiftedDcId shiftedDcId); void performKeyDestroy(ShiftedDcId shiftedDcId); void completedKeyDestroy(ShiftedDcId shiftedDcId); + void checkMainDcKey(); void clearKilledSessions(); void prepareToDestroy(); @@ -227,6 +228,8 @@ private: base::Timer _checkDelayedTimer; + std::unique_ptr _mainDcKeyChecker; + // Debug flag to find out how we end up crashing. bool MustNotCreateSessions = false; @@ -1102,13 +1105,13 @@ void Instance::Private::globalCallback(const mtpPrime *from, const mtpPrime *end [[maybe_unused]] bool result = (*_globalHandler.onDone)(0, from, end); } -void Instance::Private::onStateChange(int32 dcWithShift, int32 state) { +void Instance::Private::onStateChange(ShiftedDcId dcWithShift, int32 state) { if (_stateChangedHandler) { _stateChangedHandler(dcWithShift, state); } } -void Instance::Private::onSessionReset(int32 dcWithShift) { +void Instance::Private::onSessionReset(ShiftedDcId dcWithShift) { if (_sessionResetHandler) { _sessionResetHandler(dcWithShift); } @@ -1505,6 +1508,26 @@ void Instance::Private::completedKeyDestroy(ShiftedDcId shiftedDcId) { } } +void Instance::Private::checkMainDcKey() { + if (_mainDcKeyChecker) { + return; + } + const auto id = mainDcId(); + const auto key = [&] { + QReadLocker lock(&_keysForWriteLock); + const auto i = _keysForWrite.find(id); + return (i != end(_keysForWrite)) ? i->second : AuthKeyPtr(); + }(); + if (!key) { + return; + } + _mainDcKeyChecker = std::make_unique( + _instance, + id, + key, + [=] { _mainDcKeyChecker = nullptr; }); +} + void Instance::Private::setUpdatesHandler(RPCDoneHandlerPtr onDone) { _globalHandler.onDone = onDone; } @@ -1751,6 +1774,10 @@ void Instance::checkIfKeyWasDestroyed(ShiftedDcId shiftedDcId) { LOG(("MTP Info: checkIfKeyWasDestroyed on destroying key %1, " "assuming it is destroyed.").arg(shiftedDcId)); _private->completedKeyDestroy(shiftedDcId); + } else if (BareDcId(shiftedDcId) == mainDcId()) { + LOG(("MTP Info: checkIfKeyWasDestroyed for main dc %1, " + "checking.").arg(shiftedDcId)); + _private->checkMainDcKey(); } }); } diff --git a/Telegram/SourceFiles/mtproto/mtproto_dh_utils.cpp b/Telegram/SourceFiles/mtproto/mtproto_dh_utils.cpp new file mode 100644 index 000000000..9c136eb1d --- /dev/null +++ b/Telegram/SourceFiles/mtproto/mtproto_dh_utils.cpp @@ -0,0 +1,173 @@ +/* +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 "mtproto/mtproto_dh_utils.h" + +namespace MTP { +namespace { + +constexpr auto kMaxModExpSize = 256; + +bool IsPrimeAndGoodCheck(const openssl::BigNum &prime, int g) { + constexpr auto kGoodPrimeBitsCount = 2048; + + if (prime.failed() + || prime.isNegative() + || prime.bitsSize() != kGoodPrimeBitsCount) { + LOG(("MTP Error: Bad prime bits count %1, expected %2." + ).arg(prime.bitsSize() + ).arg(kGoodPrimeBitsCount)); + return false; + } + + const auto context = openssl::Context(); + if (!prime.isPrime(context)) { + LOG(("MTP Error: Bad prime.")); + return false; + } + + switch (g) { + case 2: { + const auto mod8 = prime.countModWord(8); + if (mod8 != 7) { + LOG(("BigNum PT Error: bad g value: %1, mod8: %2").arg(g).arg(mod8)); + return false; + } + } break; + case 3: { + const auto mod3 = prime.countModWord(3); + if (mod3 != 2) { + LOG(("BigNum PT Error: bad g value: %1, mod3: %2").arg(g).arg(mod3)); + return false; + } + } break; + case 4: break; + case 5: { + const auto mod5 = prime.countModWord(5); + if (mod5 != 1 && mod5 != 4) { + LOG(("BigNum PT Error: bad g value: %1, mod5: %2").arg(g).arg(mod5)); + return false; + } + } break; + case 6: { + const auto mod24 = prime.countModWord(24); + if (mod24 != 19 && mod24 != 23) { + LOG(("BigNum PT Error: bad g value: %1, mod24: %2").arg(g).arg(mod24)); + return false; + } + } break; + case 7: { + const auto mod7 = prime.countModWord(7); + if (mod7 != 3 && mod7 != 5 && mod7 != 6) { + LOG(("BigNum PT Error: bad g value: %1, mod7: %2").arg(g).arg(mod7)); + return false; + } + } break; + default: { + LOG(("BigNum PT Error: bad g value: %1").arg(g)); + return false; + } break; + } + + if (!openssl::BigNum(prime).subWord(1).divWord(2).isPrime(context)) { + LOG(("MTP Error: Bad (prime - 1) / 2.")); + return false; + } + + return true; +} + +} // namespace + +bool IsGoodModExpFirst( + const openssl::BigNum &modexp, + const openssl::BigNum &prime) { + const auto diff = openssl::BigNum::Sub(prime, modexp); + if (modexp.failed() || prime.failed() || diff.failed()) { + return false; + } + constexpr auto kMinDiffBitsCount = 2048 - 64; + if (diff.isNegative() + || diff.bitsSize() < kMinDiffBitsCount + || modexp.bitsSize() < kMinDiffBitsCount + || modexp.bytesSize() > kMaxModExpSize) { + return false; + } + return true; +} + +bool IsPrimeAndGood(bytes::const_span primeBytes, int g) { + static constexpr unsigned char GoodPrime[] = { + 0xC7, 0x1C, 0xAE, 0xB9, 0xC6, 0xB1, 0xC9, 0x04, 0x8E, 0x6C, 0x52, 0x2F, 0x70, 0xF1, 0x3F, 0x73, + 0x98, 0x0D, 0x40, 0x23, 0x8E, 0x3E, 0x21, 0xC1, 0x49, 0x34, 0xD0, 0x37, 0x56, 0x3D, 0x93, 0x0F, + 0x48, 0x19, 0x8A, 0x0A, 0xA7, 0xC1, 0x40, 0x58, 0x22, 0x94, 0x93, 0xD2, 0x25, 0x30, 0xF4, 0xDB, + 0xFA, 0x33, 0x6F, 0x6E, 0x0A, 0xC9, 0x25, 0x13, 0x95, 0x43, 0xAE, 0xD4, 0x4C, 0xCE, 0x7C, 0x37, + 0x20, 0xFD, 0x51, 0xF6, 0x94, 0x58, 0x70, 0x5A, 0xC6, 0x8C, 0xD4, 0xFE, 0x6B, 0x6B, 0x13, 0xAB, + 0xDC, 0x97, 0x46, 0x51, 0x29, 0x69, 0x32, 0x84, 0x54, 0xF1, 0x8F, 0xAF, 0x8C, 0x59, 0x5F, 0x64, + 0x24, 0x77, 0xFE, 0x96, 0xBB, 0x2A, 0x94, 0x1D, 0x5B, 0xCD, 0x1D, 0x4A, 0xC8, 0xCC, 0x49, 0x88, + 0x07, 0x08, 0xFA, 0x9B, 0x37, 0x8E, 0x3C, 0x4F, 0x3A, 0x90, 0x60, 0xBE, 0xE6, 0x7C, 0xF9, 0xA4, + 0xA4, 0xA6, 0x95, 0x81, 0x10, 0x51, 0x90, 0x7E, 0x16, 0x27, 0x53, 0xB5, 0x6B, 0x0F, 0x6B, 0x41, + 0x0D, 0xBA, 0x74, 0xD8, 0xA8, 0x4B, 0x2A, 0x14, 0xB3, 0x14, 0x4E, 0x0E, 0xF1, 0x28, 0x47, 0x54, + 0xFD, 0x17, 0xED, 0x95, 0x0D, 0x59, 0x65, 0xB4, 0xB9, 0xDD, 0x46, 0x58, 0x2D, 0xB1, 0x17, 0x8D, + 0x16, 0x9C, 0x6B, 0xC4, 0x65, 0xB0, 0xD6, 0xFF, 0x9C, 0xA3, 0x92, 0x8F, 0xEF, 0x5B, 0x9A, 0xE4, + 0xE4, 0x18, 0xFC, 0x15, 0xE8, 0x3E, 0xBE, 0xA0, 0xF8, 0x7F, 0xA9, 0xFF, 0x5E, 0xED, 0x70, 0x05, + 0x0D, 0xED, 0x28, 0x49, 0xF4, 0x7B, 0xF9, 0x59, 0xD9, 0x56, 0x85, 0x0C, 0xE9, 0x29, 0x85, 0x1F, + 0x0D, 0x81, 0x15, 0xF6, 0x35, 0xB1, 0x05, 0xEE, 0x2E, 0x4E, 0x15, 0xD0, 0x4B, 0x24, 0x54, 0xBF, + 0x6F, 0x4F, 0xAD, 0xF0, 0x34, 0xB1, 0x04, 0x03, 0x11, 0x9C, 0xD8, 0xE3, 0xB9, 0x2F, 0xCC, 0x5B }; + + if (!bytes::compare(bytes::make_span(GoodPrime), primeBytes)) { + if (g == 3 || g == 4 || g == 5 || g == 7) { + return true; + } + } + + return IsPrimeAndGoodCheck(openssl::BigNum(primeBytes), g); +} + +ModExpFirst CreateModExp( + int g, + bytes::const_span primeBytes, + bytes::const_span randomSeed) { + Expects(randomSeed.size() == ModExpFirst::kRandomPowerSize); + + using namespace openssl; + + BigNum prime(primeBytes); + auto result = ModExpFirst(); + result.randomPower.resize(ModExpFirst::kRandomPowerSize); + while (true) { + bytes::set_random(result.randomPower); + for (auto i = 0; i != ModExpFirst::kRandomPowerSize; ++i) { + result.randomPower[i] ^= randomSeed[i]; + } + const auto modexp = BigNum::ModExp( + BigNum(g), + BigNum(result.randomPower), + prime); + if (IsGoodModExpFirst(modexp, prime)) { + result.modexp = modexp.getBytes(); + return result; + } + } +} + +bytes::vector CreateAuthKey( + bytes::const_span firstBytes, + bytes::const_span randomBytes, + bytes::const_span primeBytes) { + using openssl::BigNum; + + const auto first = BigNum(firstBytes); + const auto prime = BigNum(primeBytes); + if (!IsGoodModExpFirst(first, prime)) { + LOG(("AuthKey Error: Bad first prime in CreateAuthKey().")); + return {}; + } + return BigNum::ModExp(first, BigNum(randomBytes), prime).getBytes(); +} + +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/mtproto_dh_utils.h b/Telegram/SourceFiles/mtproto/mtproto_dh_utils.h new file mode 100644 index 000000000..2860bf02c --- /dev/null +++ b/Telegram/SourceFiles/mtproto/mtproto_dh_utils.h @@ -0,0 +1,35 @@ +/* +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" +#include "base/openssl_help.h" + +namespace MTP { + +struct ModExpFirst { + static constexpr auto kRandomPowerSize = 256; + + bytes::vector modexp; + bytes::vector randomPower; +}; + +[[nodiscard]] bool IsPrimeAndGood(bytes::const_span primeBytes, int g); +[[nodiscard]] bool IsGoodModExpFirst( + const openssl::BigNum &modexp, + const openssl::BigNum &prime); +[[nodiscard]] ModExpFirst CreateModExp( + int g, + bytes::const_span primeBytes, + bytes::const_span randomSeed); +[[nodiscard]] bytes::vector CreateAuthKey( + bytes::const_span firstBytes, + bytes::const_span randomBytes, + bytes::const_span primeBytes); + +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/mtproto_proxy_data.cpp b/Telegram/SourceFiles/mtproto/mtproto_proxy_data.cpp new file mode 100644 index 000000000..cb401a589 --- /dev/null +++ b/Telegram/SourceFiles/mtproto/mtproto_proxy_data.cpp @@ -0,0 +1,238 @@ +/* +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 "mtproto/mtproto_proxy_data.h" + +#include "base/qthelp_url.h" + +namespace MTP { +namespace { + +[[nodiscard]] bool IsHexMtprotoPassword(const QString &password) { + const auto size = password.size(); + if (size < 32 || size % 2 == 1) { + return false; + } + const auto bad = [](QChar ch) { + const auto code = ch.unicode(); + return (code < 'a' || code > 'f') + && (code < 'A' || code > 'F') + && (code < '0' || code > '9'); + }; + const auto i = std::find_if(password.begin(), password.end(), bad); + return (i == password.end()); +} + +[[nodiscard]] ProxyData::Status HexMtprotoPasswordStatus( + const QString &password) { + const auto size = password.size() / 2; + const auto valid = (size == 16) + || (size == 17 && (password[0] == 'd') && (password[1] == 'd')) + || (size >= 21 && (password[0] == 'e') && (password[1] == 'e')); + if (valid) { + return ProxyData::Status::Valid; + } else if (size < 16) { + return ProxyData::Status::Invalid; + } + return ProxyData::Status::Unsupported; +} + +[[nodiscard]] bytes::vector SecretFromHexMtprotoPassword( + const QString &password) { + Expects(password.size() % 2 == 0); + + const auto size = password.size() / 2; + const auto fromHex = [](QChar ch) -> int { + const auto code = int(ch.unicode()); + if (code >= '0' && code <= '9') { + return (code - '0'); + } else if (code >= 'A' && code <= 'F') { + return 10 + (code - 'A'); + } else if (ch >= 'a' && ch <= 'f') { + return 10 + (code - 'a'); + } + Unexpected("Code in ProxyData fromHex."); + }; + auto result = bytes::vector(size); + for (auto i = 0; i != size; ++i) { + const auto high = fromHex(password[2 * i]); + const auto low = fromHex(password[2 * i + 1]); + if (high < 0 || low < 0) { + return {}; + } + result[i] = static_cast(high * 16 + low); + } + return result; +} + +[[nodiscard]] QStringRef Base64UrlInner(const QString &password) { + Expects(password.size() > 2); + + // Skip one or two '=' at the end of the string. + return password.midRef(0, [&] { + auto result = password.size(); + for (auto i = 0; i != 2; ++i) { + const auto prev = result - 1; + if (password[prev] != '=') { + break; + } + result = prev; + } + return result; + }()); +} + +[[nodiscard]] bool IsBase64UrlMtprotoPassword(const QString &password) { + const auto size = password.size(); + if (size < 22 || size % 4 == 1) { + return false; + } + const auto bad = [](QChar ch) { + const auto code = ch.unicode(); + return (code < 'a' || code > 'z') + && (code < 'A' || code > 'Z') + && (code < '0' || code > '9') + && (code != '_') + && (code != '-'); + }; + const auto inner = Base64UrlInner(password); + const auto begin = inner.data(); + const auto end = begin + inner.size(); + return (std::find_if(begin, end, bad) == end); +} + +[[nodiscard]] ProxyData::Status Base64UrlMtprotoPasswordStatus( + const QString &password) { + const auto inner = Base64UrlInner(password); + const auto size = (inner.size() * 3) / 4; + const auto valid = (size == 16) + || (size == 17 + && (password[0] == '3') + && ((password[1] >= 'Q' && password[1] <= 'Z') + || (password[1] >= 'a' && password[1] <= 'f'))) + || (size >= 21 + && (password[0] == '7') + && (password[1] >= 'g') + && (password[1] <= 'v')); + if (size < 16) { + return ProxyData::Status::Invalid; + } else if (valid) { + return ProxyData::Status::Valid; + } + return ProxyData::Status::Unsupported; +} + +[[nodiscard]] bytes::vector SecretFromBase64UrlMtprotoPassword( + const QString &password) { + const auto result = QByteArray::fromBase64( + password.toLatin1(), + QByteArray::Base64UrlEncoding); + return bytes::make_vector(bytes::make_span(result)); +} + +} // namespace + +bool ProxyData::valid() const { + return status() == Status::Valid; +} + +ProxyData::Status ProxyData::status() const { + if (type == Type::None || host.isEmpty() || !port) { + return Status::Invalid; + } else if (type == Type::Mtproto) { + return MtprotoPasswordStatus(password); + } + return Status::Valid; +} + +bool ProxyData::supportsCalls() const { + return (type == Type::Socks5); +} + +bool ProxyData::tryCustomResolve() const { + return (type == Type::Socks5 || type == Type::Mtproto) + && !qthelp::is_ipv6(host) + && !QRegularExpression( + QStringLiteral("^\\d+\\.\\d+\\.\\d+\\.\\d+$") + ).match(host).hasMatch(); +} + +bytes::vector ProxyData::secretFromMtprotoPassword() const { + Expects(type == Type::Mtproto); + + if (IsHexMtprotoPassword(password)) { + return SecretFromHexMtprotoPassword(password); + } else if (IsBase64UrlMtprotoPassword(password)) { + return SecretFromBase64UrlMtprotoPassword(password); + } + return {}; +} + +ProxyData::operator bool() const { + return valid(); +} + +bool ProxyData::operator==(const ProxyData &other) const { + if (!valid()) { + return !other.valid(); + } + return (type == other.type) + && (host == other.host) + && (port == other.port) + && (user == other.user) + && (password == other.password); +} + +bool ProxyData::operator!=(const ProxyData &other) const { + return !(*this == other); +} + +bool ProxyData::ValidMtprotoPassword(const QString &password) { + return MtprotoPasswordStatus(password) == Status::Valid; +} + +ProxyData::Status ProxyData::MtprotoPasswordStatus(const QString &password) { + if (IsHexMtprotoPassword(password)) { + return HexMtprotoPasswordStatus(password); + } else if (IsBase64UrlMtprotoPassword(password)) { + return Base64UrlMtprotoPasswordStatus(password); + } + return Status::Invalid; +} + +ProxyData ToDirectIpProxy(const ProxyData &proxy, int ipIndex) { + if (!proxy.tryCustomResolve() + || ipIndex < 0 + || ipIndex >= proxy.resolvedIPs.size()) { + return proxy; + } + return { + proxy.type, + proxy.resolvedIPs[ipIndex], + proxy.port, + proxy.user, + proxy.password + }; +} + +QNetworkProxy ToNetworkProxy(const ProxyData &proxy) { + if (proxy.type == ProxyData::Type::None) { + return QNetworkProxy::DefaultProxy; + } else if (proxy.type == ProxyData::Type::Mtproto) { + return QNetworkProxy::NoProxy; + } + return QNetworkProxy( + (proxy.type == ProxyData::Type::Socks5 + ? QNetworkProxy::Socks5Proxy + : QNetworkProxy::HttpProxy), + proxy.host, + proxy.port, + proxy.user, + proxy.password); +} + +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/mtproto_proxy_data.h b/Telegram/SourceFiles/mtproto/mtproto_proxy_data.h new file mode 100644 index 000000000..52acf6661 --- /dev/null +++ b/Telegram/SourceFiles/mtproto/mtproto_proxy_data.h @@ -0,0 +1,58 @@ +/* +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 + +namespace MTP { + +struct ProxyData { + enum class Settings { + System, + Enabled, + Disabled, + }; + enum class Type { + None, + Socks5, + Http, + Mtproto, + }; + enum class Status { + Valid, + Unsupported, + Invalid, + }; + + Type type = Type::None; + QString host; + uint32 port = 0; + QString user, password; + + std::vector resolvedIPs; + crl::time resolvedExpireAt = 0; + + [[nodiscard]] bool valid() const; + [[nodiscard]] Status status() const; + [[nodiscard]] bool supportsCalls() const; + [[nodiscard]] bool tryCustomResolve() const; + [[nodiscard]] bytes::vector secretFromMtprotoPassword() const; + [[nodiscard]] explicit operator bool() const; + [[nodiscard]] bool operator==(const ProxyData &other) const; + [[nodiscard]] bool operator!=(const ProxyData &other) const; + + [[nodiscard]] static bool ValidMtprotoPassword(const QString &password); + [[nodiscard]] static Status MtprotoPasswordStatus( + const QString &password); + +}; + +[[nodiscard]] ProxyData ToDirectIpProxy( + const ProxyData &proxy, + int ipIndex = 0); +[[nodiscard]] QNetworkProxy ToNetworkProxy(const ProxyData &proxy); + +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/rsa_public_key.cpp b/Telegram/SourceFiles/mtproto/rsa_public_key.cpp index 3188fb65f..361c9930d 100644 --- a/Telegram/SourceFiles/mtproto/rsa_public_key.cpp +++ b/Telegram/SourceFiles/mtproto/rsa_public_key.cpp @@ -92,133 +92,159 @@ RSA *CreateRaw(bytes::const_span key) { class RSAPublicKey::Private { public: - Private(bytes::const_span key) - : _rsa(CreateRaw(key)) { - if (_rsa) { - computeFingerprint(); - } - } - Private(bytes::const_span nBytes, bytes::const_span eBytes) - : _rsa(RSA_new()) { - if (_rsa) { - const auto n = openssl::BigNum(nBytes).takeRaw(); - const auto e = openssl::BigNum(eBytes).takeRaw(); - const auto valid = (n != nullptr) && (e != nullptr); - // We still pass both values to RSA_set0_key() so that even - // if only one of them is valid RSA would take ownership of it. - if (!RSA_set0_key(_rsa, n, e, nullptr) || !valid) { - RSA_free(base::take(_rsa)); - } else { - computeFingerprint(); - } - } - } - bytes::vector getN() const { - Expects(isValid()); + explicit Private(bytes::const_span key); + Private(bytes::const_span nBytes, bytes::const_span eBytes); + ~Private(); - const BIGNUM *n; - RSA_get0_key(_rsa, &n, nullptr, nullptr); - return toBytes(n); - } - bytes::vector getE() const { - Expects(isValid()); - - const BIGNUM *e; - RSA_get0_key(_rsa, nullptr, &e, nullptr); - return toBytes(e); - } - uint64 getFingerPrint() const { - return _fingerprint; - } - bool isValid() const { - return _rsa != nullptr; - } - bytes::vector encrypt(bytes::const_span data) const { - Expects(isValid()); - - constexpr auto kEncryptSize = 256; - auto result = bytes::vector(kEncryptSize, gsl::byte {}); - auto res = RSA_public_encrypt(kEncryptSize, reinterpret_cast(data.data()), reinterpret_cast(result.data()), _rsa, RSA_NO_PADDING); - if (res < 0 || res > kEncryptSize) { - ERR_load_crypto_strings(); - LOG(("RSA Error: RSA_public_encrypt failed, key fp: %1, result: %2, error: %3").arg(getFingerPrint()).arg(res).arg(ERR_error_string(ERR_get_error(), 0))); - return {}; - } else if (auto zeroBytes = kEncryptSize - res) { - auto resultBytes = gsl::make_span(result); - bytes::move(resultBytes.subspan(zeroBytes, res), resultBytes.subspan(0, res)); - bytes::set_with_const(resultBytes.subspan(0, zeroBytes), gsl::byte {}); - } - return result; - } - bytes::vector decrypt(bytes::const_span data) const { - Expects(isValid()); - - constexpr auto kDecryptSize = 256; - auto result = bytes::vector(kDecryptSize, gsl::byte {}); - auto res = RSA_public_decrypt(kDecryptSize, reinterpret_cast(data.data()), reinterpret_cast(result.data()), _rsa, RSA_NO_PADDING); - if (res < 0 || res > kDecryptSize) { - ERR_load_crypto_strings(); - LOG(("RSA Error: RSA_public_encrypt failed, key fp: %1, result: %2, error: %3").arg(getFingerPrint()).arg(res).arg(ERR_error_string(ERR_get_error(), 0))); - return {}; - } else if (auto zeroBytes = kDecryptSize - res) { - auto resultBytes = gsl::make_span(result); - bytes::move(resultBytes.subspan(zeroBytes - res, res), resultBytes.subspan(0, res)); - bytes::set_with_const(resultBytes.subspan(0, zeroBytes - res), gsl::byte {}); - } - return result; - } - bytes::vector encryptOAEPpadding(bytes::const_span data) const { - Expects(isValid()); - - const auto resultSize = RSA_size(_rsa); - auto result = bytes::vector(resultSize, gsl::byte{}); - const auto encryptedSize = RSA_public_encrypt( - data.size(), - reinterpret_cast(data.data()), - reinterpret_cast(result.data()), - _rsa, - RSA_PKCS1_OAEP_PADDING); - if (encryptedSize != resultSize) { - ERR_load_crypto_strings(); - LOG(("RSA Error: RSA_public_encrypt failed, " - "key fp: %1, result: %2, error: %3" - ).arg(getFingerPrint() - ).arg(encryptedSize - ).arg(ERR_error_string(ERR_get_error(), 0) - )); - return {}; - } - return result; - } - ~Private() { - RSA_free(_rsa); - } + [[nodiscard]] bool valid() const; + [[nodiscard]] uint64 fingerprint() const; + [[nodiscard]] bytes::vector getN() const; + [[nodiscard]] bytes::vector getE() const; + [[nodiscard]] bytes::vector encrypt(bytes::const_span data) const; + [[nodiscard]] bytes::vector decrypt(bytes::const_span data) const; + [[nodiscard]] bytes::vector encryptOAEPpadding( + bytes::const_span data) const; private: - void computeFingerprint() { - Expects(isValid()); - - const BIGNUM *n, *e; - mtpBuffer string; - RSA_get0_key(_rsa, &n, &e, nullptr); - MTP_bytes(toBytes(n)).write(string); - MTP_bytes(toBytes(e)).write(string); - - uchar sha1Buffer[20]; - _fingerprint = *(uint64*)(hashSha1(&string[0], string.size() * sizeof(mtpPrime), sha1Buffer) + 3); - } - static bytes::vector toBytes(const BIGNUM *number) { - auto size = BN_num_bytes(number); - auto result = bytes::vector(size, gsl::byte {}); - BN_bn2bin(number, reinterpret_cast(result.data())); - return result; - } + void computeFingerprint(); + [[nodiscard]] static bytes::vector ToBytes(const BIGNUM *number); RSA *_rsa = nullptr; uint64 _fingerprint = 0; }; +RSAPublicKey::Private::Private(bytes::const_span key) + : _rsa(CreateRaw(key)) { + if (_rsa) { + computeFingerprint(); + } +} + +RSAPublicKey::Private::Private(bytes::const_span nBytes, bytes::const_span eBytes) + : _rsa(RSA_new()) { + if (_rsa) { + const auto n = openssl::BigNum(nBytes).takeRaw(); + const auto e = openssl::BigNum(eBytes).takeRaw(); + const auto valid = (n != nullptr) && (e != nullptr); + // We still pass both values to RSA_set0_key() so that even + // if only one of them is valid RSA would take ownership of it. + if (!RSA_set0_key(_rsa, n, e, nullptr) || !valid) { + RSA_free(base::take(_rsa)); + } else { + computeFingerprint(); + } + } +} + +bool RSAPublicKey::Private::valid() const { + return _rsa != nullptr; +} + +uint64 RSAPublicKey::Private::fingerprint() const { + return _fingerprint; +} + +bytes::vector RSAPublicKey::Private::getN() const { + Expects(valid()); + + const BIGNUM *n; + RSA_get0_key(_rsa, &n, nullptr, nullptr); + return ToBytes(n); +} + +bytes::vector RSAPublicKey::Private::getE() const { + Expects(valid()); + + const BIGNUM *e; + RSA_get0_key(_rsa, nullptr, &e, nullptr); + return ToBytes(e); +} + +bytes::vector RSAPublicKey::Private::encrypt(bytes::const_span data) const { + Expects(valid()); + + constexpr auto kEncryptSize = 256; + auto result = bytes::vector(kEncryptSize, gsl::byte{}); + auto res = RSA_public_encrypt(kEncryptSize, reinterpret_cast(data.data()), reinterpret_cast(result.data()), _rsa, RSA_NO_PADDING); + if (res < 0 || res > kEncryptSize) { + ERR_load_crypto_strings(); + LOG(("RSA Error: RSA_public_encrypt failed, key fp: %1, result: %2, error: %3").arg(fingerprint()).arg(res).arg(ERR_error_string(ERR_get_error(), 0))); + return {}; + } else if (auto zeroBytes = kEncryptSize - res) { + auto resultBytes = gsl::make_span(result); + bytes::move(resultBytes.subspan(zeroBytes, res), resultBytes.subspan(0, res)); + bytes::set_with_const(resultBytes.subspan(0, zeroBytes), gsl::byte{}); + } + return result; +} + +bytes::vector RSAPublicKey::Private::decrypt(bytes::const_span data) const { + Expects(valid()); + + constexpr auto kDecryptSize = 256; + auto result = bytes::vector(kDecryptSize, gsl::byte{}); + auto res = RSA_public_decrypt(kDecryptSize, reinterpret_cast(data.data()), reinterpret_cast(result.data()), _rsa, RSA_NO_PADDING); + if (res < 0 || res > kDecryptSize) { + ERR_load_crypto_strings(); + LOG(("RSA Error: RSA_public_encrypt failed, key fp: %1, result: %2, error: %3").arg(fingerprint()).arg(res).arg(ERR_error_string(ERR_get_error(), 0))); + return {}; + } else if (auto zeroBytes = kDecryptSize - res) { + auto resultBytes = gsl::make_span(result); + bytes::move(resultBytes.subspan(zeroBytes - res, res), resultBytes.subspan(0, res)); + bytes::set_with_const(resultBytes.subspan(0, zeroBytes - res), gsl::byte{}); + } + return result; +} + +bytes::vector RSAPublicKey::Private::encryptOAEPpadding(bytes::const_span data) const { + Expects(valid()); + + const auto resultSize = RSA_size(_rsa); + auto result = bytes::vector(resultSize, gsl::byte{}); + const auto encryptedSize = RSA_public_encrypt( + data.size(), + reinterpret_cast(data.data()), + reinterpret_cast(result.data()), + _rsa, + RSA_PKCS1_OAEP_PADDING); + if (encryptedSize != resultSize) { + ERR_load_crypto_strings(); + LOG(("RSA Error: RSA_public_encrypt failed, " + "key fp: %1, result: %2, error: %3" + ).arg(fingerprint() + ).arg(encryptedSize + ).arg(ERR_error_string(ERR_get_error(), 0) + )); + return {}; + } + return result; +} + +RSAPublicKey::Private::~Private() { + RSA_free(_rsa); +} + +void RSAPublicKey::Private::computeFingerprint() { + Expects(valid()); + + const BIGNUM *n, *e; + mtpBuffer string; + RSA_get0_key(_rsa, &n, &e, nullptr); + MTP_bytes(ToBytes(n)).write(string); + MTP_bytes(ToBytes(e)).write(string); + + uchar sha1Buffer[20]; + _fingerprint = *(uint64*)(hashSha1(&string[0], string.size() * sizeof(mtpPrime), sha1Buffer) + 3); +} + +bytes::vector RSAPublicKey::Private::ToBytes(const BIGNUM *number) { + auto size = BN_num_bytes(number); + auto result = bytes::vector(size, gsl::byte{}); + BN_bn2bin(number, reinterpret_cast(result.data())); + return result; +} + RSAPublicKey::RSAPublicKey(bytes::const_span key) : _private(std::make_shared(key)) { } @@ -229,35 +255,40 @@ RSAPublicKey::RSAPublicKey( : _private(std::make_shared(nBytes, eBytes)) { } -bool RSAPublicKey::isValid() const { - return _private && _private->isValid(); +bool RSAPublicKey::empty() const { + return !_private; } -uint64 RSAPublicKey::getFingerPrint() const { - Expects(isValid()); - return _private->getFingerPrint(); +bool RSAPublicKey::valid() const { + return !empty() && _private->valid(); +} + +uint64 RSAPublicKey::fingerprint() const { + Expects(valid()); + + return _private->fingerprint(); } bytes::vector RSAPublicKey::getN() const { - Expects(isValid()); + Expects(valid()); return _private->getN(); } bytes::vector RSAPublicKey::getE() const { - Expects(isValid()); + Expects(valid()); return _private->getE(); } bytes::vector RSAPublicKey::encrypt(bytes::const_span data) const { - Expects(isValid()); + Expects(valid()); return _private->encrypt(data); } bytes::vector RSAPublicKey::decrypt(bytes::const_span data) const { - Expects(isValid()); + Expects(valid()); return _private->decrypt(data); } diff --git a/Telegram/SourceFiles/mtproto/rsa_public_key.h b/Telegram/SourceFiles/mtproto/rsa_public_key.h index 8e748d8a5..402b17426 100644 --- a/Telegram/SourceFiles/mtproto/rsa_public_key.h +++ b/Telegram/SourceFiles/mtproto/rsa_public_key.h @@ -26,19 +26,20 @@ public: // or in "-----BEGIN PUBLIC KEY----- ..." format explicit RSAPublicKey(bytes::const_span key); - bool isValid() const; - uint64 getFingerPrint() const; - bytes::vector getN() const; - bytes::vector getE() const; + [[nodiscard]] bool empty() const; + [[nodiscard]] bool valid() const; + [[nodiscard]] uint64 fingerprint() const; + [[nodiscard]] bytes::vector getN() const; + [[nodiscard]] bytes::vector getE() const; // data has exactly 256 chars to be encrypted - bytes::vector encrypt(bytes::const_span data) const; + [[nodiscard]] bytes::vector encrypt(bytes::const_span data) const; // data has exactly 256 chars to be decrypted - bytes::vector decrypt(bytes::const_span data) const; + [[nodiscard]] bytes::vector decrypt(bytes::const_span data) const; // data has lequal than 215 chars to be decrypted - bytes::vector encryptOAEPpadding(bytes::const_span data) const; + [[nodiscard]] bytes::vector encryptOAEPpadding(bytes::const_span data) const; private: class Private; diff --git a/Telegram/SourceFiles/mtproto/session.h b/Telegram/SourceFiles/mtproto/session.h index 061bac330..a08315852 100644 --- a/Telegram/SourceFiles/mtproto/session.h +++ b/Telegram/SourceFiles/mtproto/session.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/timer.h" #include "mtproto/rpc_sender.h" +#include "mtproto/mtproto_proxy_data.h" #include diff --git a/Telegram/SourceFiles/settings/settings_advanced.cpp b/Telegram/SourceFiles/settings/settings_advanced.cpp index 26539db81..f00fc5482 100644 --- a/Telegram/SourceFiles/settings/settings_advanced.cpp +++ b/Telegram/SourceFiles/settings/settings_advanced.cpp @@ -48,7 +48,7 @@ void SetupConnectionType(not_null container) { #ifndef TDESKTOP_DISABLE_NETWORK_PROXY const auto connectionType = [] { const auto transport = MTP::dctransport(); - if (Global::ProxySettings() != ProxyData::Settings::Enabled) { + if (Global::ProxySettings() != MTP::ProxyData::Settings::Enabled) { return transport.isEmpty() ? tr::lng_connection_auto_connecting(tr::now) : tr::lng_connection_auto(tr::now, lt_transport, transport); diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index 4ce04ab92..821d492d2 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -1264,7 +1264,7 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting stream >> v; if (!_checkStreamStatus(stream)) return false; - ProxyData proxy; + MTP::ProxyData proxy; switch (v) { case dbictHttpProxy: case dbictTcpProxy: { @@ -1274,14 +1274,14 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting proxy.port = uint32(port); proxy.type = (v == dbictTcpProxy) - ? ProxyData::Type::Socks5 - : ProxyData::Type::Http; + ? MTP::ProxyData::Type::Socks5 + : MTP::ProxyData::Type::Http; } break; }; - Global::SetSelectedProxy(proxy ? proxy : ProxyData()); + Global::SetSelectedProxy(proxy ? proxy : MTP::ProxyData()); Global::SetProxySettings(proxy - ? ProxyData::Settings::Enabled - : ProxyData::Settings::System); + ? MTP::ProxyData::Settings::Enabled + : MTP::ProxyData::Settings::System); if (proxy) { Global::SetProxiesList({ 1, proxy }); } else { @@ -1299,20 +1299,20 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting const auto readProxy = [&] { qint32 proxyType, port; - ProxyData proxy; + MTP::ProxyData proxy; stream >> proxyType >> proxy.host >> port >> proxy.user >> proxy.password; proxy.port = port; proxy.type = (proxyType == dbictTcpProxy) - ? ProxyData::Type::Socks5 + ? MTP::ProxyData::Type::Socks5 : (proxyType == dbictHttpProxy) - ? ProxyData::Type::Http - : (proxyType == kProxyTypeShift + int(ProxyData::Type::Socks5)) - ? ProxyData::Type::Socks5 - : (proxyType == kProxyTypeShift + int(ProxyData::Type::Http)) - ? ProxyData::Type::Http - : (proxyType == kProxyTypeShift + int(ProxyData::Type::Mtproto)) - ? ProxyData::Type::Mtproto - : ProxyData::Type::None; + ? MTP::ProxyData::Type::Http + : (proxyType == kProxyTypeShift + int(MTP::ProxyData::Type::Socks5)) + ? MTP::ProxyData::Type::Socks5 + : (proxyType == kProxyTypeShift + int(MTP::ProxyData::Type::Http)) + ? MTP::ProxyData::Type::Http + : (proxyType == kProxyTypeShift + int(MTP::ProxyData::Type::Mtproto)) + ? MTP::ProxyData::Type::Mtproto + : MTP::ProxyData::Type::None; return proxy; }; if (connectionType == dbictProxiesListOld @@ -1327,7 +1327,7 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting index -= (index > 0 ? count : -count); } - auto list = std::vector(); + auto list = std::vector(); for (auto i = 0; i < count; ++i) { const auto proxy = readProxy(); if (proxy) { @@ -1345,29 +1345,29 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting if (connectionType == dbictProxiesListOld) { settings = static_cast( (index > 0 && index <= list.size() - ? ProxyData::Settings::Enabled - : ProxyData::Settings::System)); + ? MTP::ProxyData::Settings::Enabled + : MTP::ProxyData::Settings::System)); index = std::abs(index); } if (index > 0 && index <= list.size()) { Global::SetSelectedProxy(list[index - 1]); } else { - Global::SetSelectedProxy(ProxyData()); + Global::SetSelectedProxy(MTP::ProxyData()); } - const auto unchecked = static_cast(settings); + const auto unchecked = static_cast(settings); switch (unchecked) { - case ProxyData::Settings::Enabled: + case MTP::ProxyData::Settings::Enabled: Global::SetProxySettings(Global::SelectedProxy() - ? ProxyData::Settings::Enabled - : ProxyData::Settings::System); + ? MTP::ProxyData::Settings::Enabled + : MTP::ProxyData::Settings::System); break; - case ProxyData::Settings::Disabled: - case ProxyData::Settings::System: + case MTP::ProxyData::Settings::Disabled: + case MTP::ProxyData::Settings::System: Global::SetProxySettings(unchecked); break; default: - Global::SetProxySettings(ProxyData::Settings::System); + Global::SetProxySettings(MTP::ProxyData::Settings::System); break; } Global::SetUseProxyForCalls(calls == 1); @@ -1381,14 +1381,14 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting Global::SetSelectedProxy(proxy); if (connectionType == dbictTcpProxy || connectionType == dbictHttpProxy) { - Global::SetProxySettings(ProxyData::Settings::Enabled); + Global::SetProxySettings(MTP::ProxyData::Settings::Enabled); } else { - Global::SetProxySettings(ProxyData::Settings::System); + Global::SetProxySettings(MTP::ProxyData::Settings::System); } } else { Global::SetProxiesList({}); - Global::SetSelectedProxy(ProxyData()); - Global::SetProxySettings(ProxyData::Settings::System); + Global::SetSelectedProxy(MTP::ProxyData()); + Global::SetProxySettings(MTP::ProxyData::Settings::System); } } Core::App().refreshGlobalProxy(); @@ -2646,7 +2646,7 @@ void writeSettings() { auto &proxies = Global::RefProxiesList(); const auto &proxy = Global::SelectedProxy(); auto proxyIt = ranges::find(proxies, proxy); - if (proxy.type != ProxyData::Type::None + if (proxy.type != MTP::ProxyData::Type::None && proxyIt == end(proxies)) { proxies.push_back(proxy); proxyIt = end(proxies) - 1; diff --git a/Telegram/SourceFiles/window/window_connecting_widget.cpp b/Telegram/SourceFiles/window/window_connecting_widget.cpp index 154614247..622c5e6b2 100644 --- a/Telegram/SourceFiles/window/window_connecting_widget.cpp +++ b/Telegram/SourceFiles/window/window_connecting_widget.cpp @@ -271,7 +271,7 @@ void ConnectionState::refreshState() { const auto under = _widget && _widget->isOver(); const auto mtp = MTP::dcstate(); const auto throughProxy - = (Global::ProxySettings() == ProxyData::Settings::Enabled); + = (Global::ProxySettings() == MTP::ProxyData::Settings::Enabled); if (mtp == MTP::ConnectingState || mtp == MTP::DisconnectedState || (mtp < 0 && mtp > -600)) { diff --git a/Telegram/gyp/lib_mtproto.gyp b/Telegram/gyp/lib_mtproto.gyp index 5344f6e10..f86c004ff 100644 --- a/Telegram/gyp/lib_mtproto.gyp +++ b/Telegram/gyp/lib_mtproto.gyp @@ -34,6 +34,14 @@ '<(src_loc)', ], 'sources': [ + '<(src_loc)/mtproto/details/mtproto_dc_key_checker.cpp', + '<(src_loc)/mtproto/details/mtproto_dc_key_checker.h', + '<(src_loc)/mtproto/details/mtproto_dc_key_creator.cpp', + '<(src_loc)/mtproto/details/mtproto_dc_key_creator.h', + '<(src_loc)/mtproto/mtproto_dh_utils.cpp', + '<(src_loc)/mtproto/mtproto_dh_utils.h', + '<(src_loc)/mtproto/mtproto_proxy_data.cpp', + '<(src_loc)/mtproto/mtproto_proxy_data.h', '<(src_loc)/mtproto/mtp_abstract_socket.cpp', '<(src_loc)/mtproto/mtp_abstract_socket.h', '<(src_loc)/mtproto/mtp_tcp_socket.cpp',