diff --git a/Telegram/SourceFiles/base/openssl_help.h b/Telegram/SourceFiles/base/openssl_help.h index 6f09d1bc7..d78734715 100644 --- a/Telegram/SourceFiles/base/openssl_help.h +++ b/Telegram/SourceFiles/base/openssl_help.h @@ -19,6 +19,7 @@ extern "C" { #include #include #include +#include } // extern "C" #ifdef small @@ -445,6 +446,24 @@ inline bytes::vector Pbkdf2Sha512( EVP_sha512()); } +inline bytes::vector HmacSha256( + bytes::const_span key, + bytes::const_span data) { + auto result = bytes::vector(kSha256Size); + auto length = unsigned int(kSha256Size); + + HMAC( + EVP_sha256(), + key.data(), + key.size(), + reinterpret_cast(data.data()), + data.size(), + reinterpret_cast(result.data()), + &length); + + return result; +} + } // namespace openssl namespace bytes { diff --git a/Telegram/SourceFiles/mtproto/connection_tcp.cpp b/Telegram/SourceFiles/mtproto/connection_tcp.cpp index fcf71ba3b..6f0f31091 100644 --- a/Telegram/SourceFiles/mtproto/connection_tcp.cpp +++ b/Telegram/SourceFiles/mtproto/connection_tcp.cpp @@ -224,7 +224,9 @@ bytes::const_span TcpConnection::Protocol::VersionD::readPacket( auto TcpConnection::Protocol::Create(bytes::vector &&secret) -> std::unique_ptr { - if (secret.size() == 17 && static_cast(secret[0]) == 0xDD) { + if (secret.size() == 17 + && (static_cast(secret[0]) == 0xDD + || static_cast(secret[0]) == 0xEE)) { return std::make_unique( bytes::make_vector(bytes::make_span(secret).subspan(1))); } else if (secret.size() == 16) { @@ -363,7 +365,7 @@ void TcpConnection::socketRead() { TCP_LOG(("TCP Info: no bytes read, but bytes available was true...")); break; } - } while (_socket->isConnected() && _socket->bytesAvailable()); + } while (_socket->isConnected() && _socket->hasBytesAvailable()); } mtpBuffer TcpConnection::parsePacket(bytes::const_span bytes) { diff --git a/Telegram/SourceFiles/mtproto/mtp_abstract_socket.cpp b/Telegram/SourceFiles/mtproto/mtp_abstract_socket.cpp index c05e6c580..e83f8c289 100644 --- a/Telegram/SourceFiles/mtproto/mtp_abstract_socket.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_abstract_socket.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/mtp_abstract_socket.h" #include "mtproto/mtp_tcp_socket.h" +#include "mtproto/mtp_tls_socket.h" namespace MTP { namespace internal { @@ -16,7 +17,15 @@ std::unique_ptr AbstractSocket::Create( not_null thread, const bytes::vector &secret, const ProxyData &proxy) { - return std::make_unique(thread, proxy); + const auto proxySecret = (proxy.type == ProxyData::Type::Mtproto) + ? proxy.secretFromMtprotoPassword() + : bytes::vector(); + const auto &usingSecret = proxySecret.empty() ? secret : proxySecret; + if (!usingSecret.empty() && usingSecret[0] == bytes::type(0xEE)) { + return std::make_unique(thread, secret, proxy); + } else { + return std::make_unique(thread, proxy); + } } } // namespace internal diff --git a/Telegram/SourceFiles/mtproto/mtp_abstract_socket.h b/Telegram/SourceFiles/mtproto/mtp_abstract_socket.h index f627601e0..7e1e69ebc 100644 --- a/Telegram/SourceFiles/mtproto/mtp_abstract_socket.h +++ b/Telegram/SourceFiles/mtproto/mtp_abstract_socket.h @@ -37,7 +37,7 @@ public: virtual void connectToHost(const QString &address, int port) = 0; [[nodiscard]] virtual bool isConnected() = 0; - [[nodiscard]] virtual int bytesAvailable() = 0; + [[nodiscard]] virtual bool hasBytesAvailable() = 0; [[nodiscard]] virtual int64 read(char *buffer, int64 maxLength) = 0; virtual int64 write(const char *buffer, int64 length) = 0; diff --git a/Telegram/SourceFiles/mtproto/mtp_tcp_socket.cpp b/Telegram/SourceFiles/mtproto/mtp_tcp_socket.cpp index 5150031f3..e6877b356 100644 --- a/Telegram/SourceFiles/mtproto/mtp_tcp_socket.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_tcp_socket.cpp @@ -9,12 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace MTP { namespace internal { -namespace { - -using ErrorSignal = void(QTcpSocket::*)(QAbstractSocket::SocketError); -const auto QTcpSocket_error = ErrorSignal(&QAbstractSocket::error); - -} // namespace TcpSocket::TcpSocket(not_null thread, const ProxyData &proxy) : AbstractSocket(thread) { @@ -38,14 +32,13 @@ TcpSocket::TcpSocket(not_null thread, const ProxyData &proxy) &_socket, &QTcpSocket::readyRead, wrap([=] { _readyRead.fire({}); })); + + using ErrorSignal = void(QTcpSocket::*)(QAbstractSocket::SocketError); + const auto QTcpSocket_error = ErrorSignal(&QAbstractSocket::error); connect( &_socket, QTcpSocket_error, - wrap([=](Error e) { logError(e); _error.fire({}); })); -} - -TcpSocket::~TcpSocket() { - _socket.close(); + wrap([=](Error e) { handleError(e); })); } void TcpSocket::connectToHost(const QString &address, int port) { @@ -56,8 +49,8 @@ bool TcpSocket::isConnected() { return (_socket.state() == QAbstractSocket::ConnectedState); } -int TcpSocket::bytesAvailable() { - return _socket.bytesAvailable(); +bool TcpSocket::hasBytesAvailable() { + return _socket.bytesAvailable() > 0; } int64 TcpSocket::read(char *buffer, int64 maxLength) { @@ -114,8 +107,9 @@ void TcpSocket::LogError(int errorCode, const QString &errorText) { ).arg(errorText)); } -void TcpSocket::logError(int errorCode) { +void TcpSocket::handleError(int errorCode) { LogError(errorCode, _socket.errorString()); + _error.fire({}); } } // namespace internal diff --git a/Telegram/SourceFiles/mtproto/mtp_tcp_socket.h b/Telegram/SourceFiles/mtproto/mtp_tcp_socket.h index b660d1b4f..6e7d97d83 100644 --- a/Telegram/SourceFiles/mtproto/mtp_tcp_socket.h +++ b/Telegram/SourceFiles/mtproto/mtp_tcp_socket.h @@ -15,11 +15,10 @@ namespace internal { class TcpSocket : public AbstractSocket { public: TcpSocket(not_null thread, const ProxyData &proxy); - ~TcpSocket(); void connectToHost(const QString &address, int port) override; bool isConnected() override; - int bytesAvailable() override; + bool hasBytesAvailable() override; int64 read(char *buffer, int64 maxLength) override; int64 write(const char *buffer, int64 length) override; @@ -28,7 +27,7 @@ public: static void LogError(int errorCode, const QString &errorText); private: - void logError(int errorCode); + void handleError(int errorCode); QTcpSocket _socket; diff --git a/Telegram/SourceFiles/mtproto/mtp_tls_socket.cpp b/Telegram/SourceFiles/mtproto/mtp_tls_socket.cpp new file mode 100644 index 000000000..4685e0442 --- /dev/null +++ b/Telegram/SourceFiles/mtproto/mtp_tls_socket.cpp @@ -0,0 +1,549 @@ +/* +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/mtp_tls_socket.h" + +#include "mtproto/mtp_tcp_socket.h" +#include "base/openssl_help.h" + +namespace MTP { +namespace internal { +namespace { + +constexpr auto kMaxGrease = 8; +constexpr auto kClientHelloLength = 517; +constexpr auto kHelloDigestLength = 32; +constexpr auto kLengthSize = sizeof(uint16); +const auto kServerHelloPart1 = qstr("\x16\x03\x03"); +const auto kServerHelloPart3 = qstr("\x14\x03\x03\x00\x01\x01\x17\x03\x03"); +constexpr auto kServerHelloDigestPosition = 11; +const auto kServerHeader = qstr("\x17\x03\x03"); +constexpr auto kServerDataSkip = 5; + +[[nodiscard]] MTPTlsClientHello PrepareClientHelloRules() { + auto stack = std::vector>(); + const auto pushToBack = [&](MTPTlsBlock &&block) { + Expects(!stack.empty()); + + stack.back().push_back(std::move(block)); + }; + const auto S = [&](QLatin1String s) { + const auto data = QByteArray(s.data(), s.size()); + pushToBack(MTP_tlsBlockString(MTP_bytes(data))); + }; + const auto Z = [&](int length) { + pushToBack(MTP_tlsBlockZero(MTP_int(length))); + }; + const auto G = [&](int seed) { + pushToBack(MTP_tlsBlockGrease(MTP_int(seed))); + }; + const auto R = [&](int length) { + pushToBack(MTP_tlsBlockRandom(MTP_int(length))); + }; + const auto D = [&] { + pushToBack(MTP_tlsBlockDomain()); + }; + const auto Open = [&] { + stack.emplace_back(); + }; + const auto Close = [&] { + Expects(stack.size() > 1); + + const auto blocks = std::move(stack.back()); + stack.pop_back(); + pushToBack(MTP_tlsBlockScope(MTP_vector(blocks))); + }; + const auto Finish = [&] { + Expects(stack.size() == 1); + + return stack.back(); + }; + + stack.emplace_back(); + + S(qstr("\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03")); + Z(32); + S(qstr("\x20")); + R(32); + S(qstr("\x00\x22")); + G(0); + S(qstr("" + "\x13\x01\x13\x02\x13\x03\xc0\x2b\xc0\x2f\xc0\x2c\xc0\x30\xcc\xa9" + "\xcc\xa8\xc0\x13\xc0\x14\x00\x9c\x00\x9d\x00\x2f\x00\x35\x00\x0a" + "\x01\x00\x01\x91")); + G(2); + S(qstr("\x00\x00\x00\x00")); + Open(); + Open(); + S(qstr("\x00")); + Open(); + D(); + Close(); + Close(); + Close(); + S(qstr("\x00\x17\x00\x00\xff\x01\x00\x01\x00\x00\x0a\x00\x0a\x00\x08")); + G(4); + S(qstr("" + "\x00\x1d\x00\x17\x00\x18\x00\x0b\x00\x02\x01\x00\x00\x23\x00\x00" + "\x00\x10\x00\x0e\x00\x0c\x02\x68\x32\x08\x68\x74\x74\x70\x2f\x31" + "\x2e\x31\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x0d\x00\x14\x00" + "\x12\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06" + "\x01\x02\x01\x00\x12\x00\x00\x00\x33\x00\x2b\x00\x29")); + G(4); + S(qstr("\x00\x01\x00\x00\x1d\x00\x20")); + R(32); + S(qstr("\x00\x2d\x00\x02\x01\x01\x00\x2b\x00\x0b\x0a")); + G(6); + S(qstr("\x03\x04\x03\x03\x03\x02\x03\x01\x00\x1b\x00\x03\x02\x00\x02")); + G(3); + S(qstr("\x00\x01\x00\x00\x15")); + + return MTP_tlsClientHello(MTP_vector(Finish())); +} + +[[nodiscard]] bytes::vector PrepareGreases() { + auto result = bytes::vector(kMaxGrease); + bytes::set_random(result); + for (auto &byte : result) { + byte = bytes::type((uchar(byte) & 0xF0) + 0x0A); + } + static_assert(kMaxGrease % 2 == 0); + for (auto i = 0; i != kMaxGrease; i += 2) { + if (result[i] == result[i + 1]) { + result[i + 1] = bytes::type(uchar(result[i + 1]) ^ 0x10); + } + } + return result; +} + +struct ClientHello { + QByteArray data; + QByteArray digest; +}; + +class ClientHelloGenerator { +public: + ClientHelloGenerator( + const MTPTlsClientHello &rules, + const QByteArray &domain, + const bytes::vector &key); + [[nodiscard]] ClientHello result(); + +private: + [[nodiscard]] bytes::span grow(int size); + void writeBlocks(const QVector &blocks); + void writeBlock(const MTPTlsBlock &data); + void writeBlock(const MTPDtlsBlockString &data); + void writeBlock(const MTPDtlsBlockZero &data); + void writeBlock(const MTPDtlsBlockGrease &data); + void writeBlock(const MTPDtlsBlockRandom &data); + void writeBlock(const MTPDtlsBlockDomain &data); + void writeBlock(const MTPDtlsBlockScope &data); + void writePadding(); + void writeDigest(); + void writeTimestamp(); + + const QByteArray &_domain; + const bytes::vector &_key; + bytes::vector _greases; + std::vector _scopeStack; + QByteArray _result; + QByteArray _digest; + int _digestPosition = -1; + bool _error = false; + +}; + +ClientHelloGenerator::ClientHelloGenerator( + const MTPTlsClientHello &rules, + const QByteArray &domain, + const bytes::vector &key) +: _domain(domain) +, _key(key) +, _greases(PrepareGreases()) { + _result.reserve(kClientHelloLength); + writeBlocks(rules.match([&](const MTPDtlsClientHello &data) { + return data.vblocks().v; + })); + writePadding(); + writeDigest(); + writeTimestamp(); +} + +ClientHello ClientHelloGenerator::result() { + return { + _error ? QByteArray() : std::move(_result), + _error ? QByteArray() : std::move(_digest) }; +} + +bytes::span ClientHelloGenerator::grow(int size) { + if (_error + || size <= 0 + || _result.size() + size > kClientHelloLength) { + _error = true; + return bytes::span(); + } + + const auto offset = _result.size(); + _result.resize(offset + size); + return bytes::make_detached_span(_result).subspan(offset); +} + +void ClientHelloGenerator::writeBlocks(const QVector &blocks) { + for (const auto &block : blocks) { + writeBlock(block); + } +} + +void ClientHelloGenerator::writeBlock(const MTPTlsBlock &data) { + data.match([&](const auto &data) { + writeBlock(data); + }); +} + +void ClientHelloGenerator::writeBlock(const MTPDtlsBlockString &data) { + const auto &bytes = data.vdata().v; + const auto storage = grow(bytes.size()); + if (storage.empty()) { + return; + } + bytes::copy(storage, bytes::make_span(bytes)); +} + +void ClientHelloGenerator::writeBlock(const MTPDtlsBlockZero &data) { + const auto length = data.vlength().v; + const auto already = _result.size(); + const auto storage = grow(length); + if (storage.empty()) { + return; + } + if (length == kHelloDigestLength && _digestPosition < 0) { + _digestPosition = already; + } + bytes::set_with_const(storage, bytes::type(0)); +} + +void ClientHelloGenerator::writeBlock(const MTPDtlsBlockGrease &data) { + const auto seed = data.vseed().v; + if (seed < 0 || seed >= _greases.size()) { + _error = true; + return; + } + const auto storage = grow(2); + if (storage.empty()) { + return; + } + bytes::set_with_const(storage, _greases[seed]); +} + +void ClientHelloGenerator::writeBlock(const MTPDtlsBlockRandom &data) { + const auto length = data.vlength().v; + const auto storage = grow(length); + if (storage.empty()) { + return; + } + bytes::set_random(storage); +} + +void ClientHelloGenerator::writeBlock(const MTPDtlsBlockDomain &data) { + const auto storage = grow(_domain.size()); + if (storage.empty()) { + return; + } + bytes::copy(storage, bytes::make_span(_domain)); +} + +void ClientHelloGenerator::writeBlock(const MTPDtlsBlockScope &data) { + const auto already = _result.size(); + const auto storage = grow(kLengthSize); + if (storage.empty()) { + return; + } + writeBlocks(data.ventries().v); + const auto length = qToBigEndian(uint16(_result.size() - already)); + bytes::copy(storage, bytes::object_as_span(&length)); +} + +void ClientHelloGenerator::writePadding() { + const auto padding = kClientHelloLength - kLengthSize - _result.size(); + writeBlock(MTP_tlsBlockScope( + MTP_vector(1, MTP_tlsBlockZero(MTP_int(padding))))); +} + +void ClientHelloGenerator::writeDigest() { + if (_digestPosition < 0) { + _error = true; + return; + } + bytes::copy( + bytes::make_detached_span(_result).subspan(_digestPosition), + openssl::HmacSha256(_key, bytes::make_span(_result))); +} + +void ClientHelloGenerator::writeTimestamp() { + if (_digestPosition < 0) { + _error = true; + return; + } + const auto storage = bytes::make_detached_span(_result).subspan( + _digestPosition + kHelloDigestLength - sizeof(int32), + sizeof(int32)); + auto already = int32(); + bytes::copy(bytes::object_as_span(&already), storage); + already ^= qToLittleEndian(int32(unixtime())); + bytes::copy(storage, bytes::object_as_span(&already)); + + _digest = QByteArray(kHelloDigestLength, Qt::Uninitialized); + bytes::copy( + bytes::make_detached_span(_digest), + bytes::make_detached_span(_result).subspan( + _digestPosition, + kHelloDigestLength)); +} + +[[nodiscard]] ClientHello PrepareClientHello( + const MTPTlsClientHello &rules, + const QByteArray &domain, + const bytes::vector &key) { + return ClientHelloGenerator(rules, domain, key).result(); +} + +[[nodiscard]] bytes::vector ExtractKey( + const bytes::vector &secret, + const ProxyData &proxy) { + const auto proxySecret = proxy.secretFromMtprotoPassword(); + const auto &useSecret = proxySecret.empty() ? secret : proxySecret; + if (useSecret.size() != 17 || useSecret[0] != bytes::type(0xEE)) { + return {}; + } + return bytes::make_vector(bytes::make_span(useSecret).subspan(1)); +} + +[[nodiscard]] bool CheckPart(bytes::const_span data, QLatin1String check) { + if (data.size() < check.size()) { + return false; + } + return !bytes::compare( + data.subspan(0, check.size()), + bytes::make_span(check.data(), check.size())); +} + +[[nodiscard]] int ReadPartLength(bytes::const_span data, int offset) { + const auto storage = data.subspan(offset, kLengthSize); + return qFromBigEndian( + *reinterpret_cast(storage.data())); +} + +} // namespace + +TlsSocket::TlsSocket( + not_null thread, + const bytes::vector &secret, + const ProxyData &proxy) +: AbstractSocket(thread) +, _key(ExtractKey(secret, proxy)) { + _socket.moveToThread(thread); + _socket.setProxy(ToNetworkProxy(proxy)); + const auto wrap = [&](auto handler) { + return [=](auto &&...args) { + InvokeQueued(this, [=] { handler(args...); }); + }; + }; + using Error = QAbstractSocket::SocketError; + connect( + &_socket, + &QTcpSocket::connected, + wrap([=] { plainConnected(); })); + connect( + &_socket, + &QTcpSocket::disconnected, + wrap([=] { plainDisconnected(); })); + connect( + &_socket, + &QTcpSocket::readyRead, + wrap([=] { plainReadyRead(); })); + + using ErrorSignal = void(QTcpSocket::*)(QAbstractSocket::SocketError); + const auto QTcpSocket_error = ErrorSignal(&QAbstractSocket::error); + connect( + &_socket, + QTcpSocket_error, + wrap([=](Error e) { handleError(e); })); +} + +void TlsSocket::plainConnected() { + if (_state != State::Connecting) { + return; + } + + static const auto kClientHelloRules = PrepareClientHelloRules(); + const auto hello = PrepareClientHello( + kClientHelloRules, + "google.com", + _key); + if (hello.data.isEmpty()) { + LOG(("TLS Error: Could not generate Client Hello!")); + _state = State::Error; + _error.fire({}); + } else { + _state = State::WaitingHello; + _incoming = hello.digest; + _socket.write(hello.data); + } +} + +void TlsSocket::plainDisconnected() { + _state = State::NotConnected; + _incoming = QByteArray(); + _serverHelloLength = 0; + _disconnected.fire({}); +} + +void TlsSocket::plainReadyRead() { + switch (_state) { + case State::WaitingHello: return readHello(); + case State::Ready: + case State::Working: return readData(); + } +} + +bool TlsSocket::requiredHelloPartReady() const { + return _incoming.size() >= kHelloDigestLength + _serverHelloLength; +} + +void TlsSocket::readHello() { + const auto parts1Size = kServerHelloPart1.size() + kLengthSize; + if (!_serverHelloLength) { + _serverHelloLength = parts1Size; + } + while (!requiredHelloPartReady()) { + if (!_socket.bytesAvailable()) { + return; + } + _incoming.append(_socket.readAll()); + } + checkHelloParts12(parts1Size); +} + +void TlsSocket::checkHelloParts12(int parts1Size) { + const auto data = bytes::make_span(_incoming).subspan( + kHelloDigestLength, + parts1Size); + const auto part2Size = ReadPartLength(data, parts1Size - kLengthSize); + const auto parts123Size = parts1Size + + part2Size + + kServerHelloPart3.size() + + kLengthSize; + if (_serverHelloLength == parts1Size) { + const auto part1Offset = parts1Size + - kLengthSize + - kServerHelloPart1.size(); + if (!CheckPart(data.subspan(part1Offset), kServerHelloPart1)) { + LOG(("TLS Error: Bad Server Hello part1.")); + handleError(); + return; + } + _serverHelloLength = parts123Size; + if (!requiredHelloPartReady()) { + readHello(); + return; + } + } + checkHelloParts34(parts123Size); +} + +void TlsSocket::checkHelloParts34(int parts123Size) { + const auto data = bytes::make_span(_incoming).subspan( + kHelloDigestLength, + parts123Size); + const auto part4Size = ReadPartLength(data, parts123Size - kLengthSize); + const auto full = parts123Size + part4Size; + if (_serverHelloLength == parts123Size) { + const auto part3Offset = parts123Size + - kLengthSize + - kServerHelloPart3.size(); + if (!CheckPart(data.subspan(part3Offset), kServerHelloPart3)) { + LOG(("TLS Error: Bad Server Hello part.")); + handleError(); + return; + } + _serverHelloLength = full; + if (!requiredHelloPartReady()) { + readHello(); + return; + } + } + checkHelloDigest(); +} + +void TlsSocket::checkHelloDigest() { + const auto incoming = bytes::make_detached_span(_incoming); + const auto fulldata = incoming.subspan( + 0, + kHelloDigestLength + _serverHelloLength); + const auto digest = fulldata.subspan( + kHelloDigestLength + kServerHelloDigestPosition, + kHelloDigestLength); + const auto digestCopy = bytes::make_vector(digest); + bytes::set_with_const(digest, bytes::type(0)); + const auto check = openssl::HmacSha256(_key, fulldata); + if (bytes::compare(digestCopy, check) != 0) { + LOG(("TLS Error: Bad Server Hello digest.")); + handleError(); + return; + } + if (incoming.size() > fulldata.size()) { + bytes::move(incoming, incoming.subspan(fulldata.size())); + _incoming.chop(fulldata.size()); + InvokeQueued(this, [=] { readData(); }); + } else { + _incoming.clear(); + } + _state = State::Ready; + _connected.fire({}); +} + +void TlsSocket::readData() { +} + +void TlsSocket::connectToHost(const QString &address, int port) { + Expects(_state == State::NotConnected); + + _state = State::Connecting; + _socket.connectToHost(address, port); +} + +bool TlsSocket::isConnected() { + return (_socket.state() == QAbstractSocket::ConnectedState); +} + +bool TlsSocket::hasBytesAvailable() { + return _socket.bytesAvailable(); +} + +int64 TlsSocket::read(char *buffer, int64 maxLength) { + return _socket.read(buffer, maxLength); +} + +int64 TlsSocket::write(const char *buffer, int64 length) { + return _socket.write(buffer, length); +} + +int32 TlsSocket::debugState() { + return _socket.state(); +} + +void TlsSocket::handleError(int errorCode) { + if (errorCode) { + TcpSocket::LogError(errorCode, _socket.errorString()); + } + _state = State::Error; + _error.fire({}); +} + +} // namespace internal +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/mtp_tls_socket.h b/Telegram/SourceFiles/mtproto/mtp_tls_socket.h new file mode 100644 index 000000000..0f42301c8 --- /dev/null +++ b/Telegram/SourceFiles/mtproto/mtp_tls_socket.h @@ -0,0 +1,60 @@ +/* +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/mtp_abstract_socket.h" + +namespace MTP { +namespace internal { + +class TlsSocket : public AbstractSocket { +public: + TlsSocket( + not_null thread, + const bytes::vector &secret, + const ProxyData &proxy); + + void connectToHost(const QString &address, int port) override; + bool isConnected() override; + bool hasBytesAvailable() override; + int64 read(char *buffer, int64 maxLength) override; + int64 write(const char *buffer, int64 length) override; + + int32 debugState() override; + +private: + enum class State { + NotConnected, + Connecting, + WaitingHello, + Ready, + Working, + Error, + }; + + void plainConnected(); + void plainDisconnected(); + void plainReadyRead(); + void handleError(int errorCode = 0); + [[nodiscard]] bool requiredHelloPartReady() const; + void readHello(); + void checkHelloParts12(int parts1Size); + void checkHelloParts34(int parts123Size); + void checkHelloDigest(); + void readData(); + + QTcpSocket _socket; + bytes::vector _key; + State _state = State::NotConnected; + QByteArray _incoming; + int16 _serverHelloLength = 0; + +}; + +} // namespace internal +} // namespace MTP diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 3e2b56007..556f314da 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -535,6 +535,8 @@ <(src_loc)/mtproto/mtp_abstract_socket.h <(src_loc)/mtproto/mtp_tcp_socket.cpp <(src_loc)/mtproto/mtp_tcp_socket.h +<(src_loc)/mtproto/mtp_tls_socket.cpp +<(src_loc)/mtproto/mtp_tls_socket.h <(src_loc)/mtproto/mtp_instance.cpp <(src_loc)/mtproto/mtp_instance.h <(src_loc)/mtproto/rsa_public_key.cpp