TlsInit fixes.

GitOrigin-RevId: 798d053d68b6e0955b6e0e0c7c8d36592f76c987
This commit is contained in:
levlam 2019-07-01 16:18:28 +03:00
parent 8e9e60d929
commit 0a1fb007d9
9 changed files with 93 additions and 91 deletions

View File

@ -220,13 +220,11 @@ void ObfuscatedTransport::write(BufferWriter &&message, bool quick_ack) {
} }
void ObfuscatedTransport::do_write_tls(BufferWriter &&message) { void ObfuscatedTransport::do_write_tls(BufferWriter &&message) {
size_t size = message.size(); if (message.size() > MAX_TLS_PACKET_LENGTH) {
if (size > (1 << 14)) {
auto buffer_slice = message.as_buffer_slice(); auto buffer_slice = message.as_buffer_slice();
auto slice = buffer_slice.as_slice(); auto slice = buffer_slice.as_slice();
while (!slice.empty()) { while (!slice.empty()) {
auto buf = buffer_slice.from_slice(slice.substr(0, 1 << 14)); auto buf = buffer_slice.from_slice(slice.substr(0, MAX_TLS_PACKET_LENGTH));
slice.remove_prefix(buf.size()); slice.remove_prefix(buf.size());
BufferBuilder builder; BufferBuilder builder;
builder.append(std::move(buf)); builder.append(std::move(buf));
@ -241,11 +239,11 @@ void ObfuscatedTransport::do_write_tls(BufferWriter &&message) {
void ObfuscatedTransport::do_write_tls(BufferBuilder &&builder) { void ObfuscatedTransport::do_write_tls(BufferBuilder &&builder) {
size_t size = builder.size(); size_t size = builder.size();
CHECK(size <= (1 << 14)); CHECK(size <= MAX_TLS_PACKET_LENGTH);
char buf[] = "\x17\x03\x03\x00\x00"; char buf[] = "\x17\x03\x03\x00\x00";
buf[3] = (size >> 8) & 0xff; buf[3] = static_cast<char>((size >> 8) & 0xff);
buf[4] = size & 0xff; buf[4] = static_cast<char>(size & 0xff);
builder.prepend(Slice(buf, 5)); builder.prepend(Slice(buf, 5));
if (is_first_tls_packet_) { if (is_first_tls_packet_) {

View File

@ -7,8 +7,8 @@
#pragma once #pragma once
#include "td/mtproto/IStreamTransport.h" #include "td/mtproto/IStreamTransport.h"
#include "td/mtproto/TransportType.h"
#include "td/mtproto/TlsReaderByteFlow.h" #include "td/mtproto/TlsReaderByteFlow.h"
#include "td/mtproto/TransportType.h"
#include "td/utils/AesCtrByteFlow.h" #include "td/utils/AesCtrByteFlow.h"
#include "td/utils/buffer.h" #include "td/utils/buffer.h"
@ -125,7 +125,7 @@ class ObfuscatedTransport : public IStreamTransport {
public: public:
ObfuscatedTransport(int16 dc_id, std::string secret) ObfuscatedTransport(int16 dc_id, std::string secret)
: dc_id_(dc_id), secret_(std::move(secret)), impl_(secret_.size() >= 17) { : dc_id_(dc_id), secret_(std::move(secret)), impl_(secret_.size() >= 17) {
emulate_tls_ = secret.size() >= 17 && secret[0] == '\xee'; emulate_tls_ = secret_.size() >= 17 && secret_[0] == '\xee';
} }
Result<size_t> read_next(BufferSlice *message, uint32 *quick_ack) override TD_WARN_UNUSED_RESULT; Result<size_t> read_next(BufferSlice *message, uint32 *quick_ack) override TD_WARN_UNUSED_RESULT;
@ -154,6 +154,9 @@ class ObfuscatedTransport : public IStreamTransport {
res += 6; res += 6;
} }
} }
if (res & 3) {
res += 4 - (res & 3);
}
return res; return res;
} }
@ -168,7 +171,7 @@ class ObfuscatedTransport : public IStreamTransport {
private: private:
int16 dc_id_; int16 dc_id_;
std::string secret_; std::string secret_;
bool emulate_tls_; bool emulate_tls_{false};
bool is_first_tls_packet_{true}; bool is_first_tls_packet_{true};
TransportImpl impl_; TransportImpl impl_;
TlsReaderByteFlow tls_reader_byte_flow_; TlsReaderByteFlow tls_reader_byte_flow_;
@ -176,6 +179,8 @@ class ObfuscatedTransport : public IStreamTransport {
ByteFlowSink byte_flow_sink_; ByteFlowSink byte_flow_sink_;
ChainBufferReader *input_; ChainBufferReader *input_;
static constexpr int32 MAX_TLS_PACKET_LENGTH = 1 << 14;
// TODO: use ByteFlow? // TODO: use ByteFlow?
// One problem is that BufferedFd owns output_buffer_ // One problem is that BufferedFd owns output_buffer_
// The other problem is that first 56 bytes must be sent unencrypted. // The other problem is that first 56 bytes must be sent unencrypted.

View File

@ -8,14 +8,18 @@
#include "td/utils/as.h" #include "td/utils/as.h"
#include "td/utils/crypto.h" #include "td/utils/crypto.h"
#include "td/utils/port/Clocks.h"
#include "td/utils/Random.h" #include "td/utils/Random.h"
#include "td/utils/Span.h" #include "td/utils/Span.h"
#include <cstdlib>
namespace td { namespace td {
void Grease::init(MutableSlice res) { void Grease::init(MutableSlice res) {
Random::secure_bytes(res); Random::secure_bytes(res);
for (auto &c : res) { for (auto &c : res) {
c = (c & 0xF0) + 0x0A; c = static_cast<char>((c & 0xF0) + 0x0A);
} }
for (size_t i = 1; i < res.size(); i += 2) { for (size_t i = 1; i < res.size(); i += 2) {
if (res[i] == res[i - 1]) { if (res[i] == res[i - 1]) {
@ -110,29 +114,36 @@ class TlsHello {
Op::string("\x03\x04\x03\x03\x03\x02\x03\x01\x00\x1b\x00\x03\x02\x00\x02"), Op::string("\x03\x04\x03\x03\x03\x02\x03\x01\x00\x1b\x00\x03\x02\x00\x02"),
Op::grease(3), Op::grease(3),
Op::string("\x00\x01\x00\x00\x15")}; Op::string("\x00\x01\x00\x00\x15")};
res.grease_size_ = 7;
return res; return res;
}(); }();
return result; return result;
} }
Span<Op> get_ops() const { Span<Op> get_ops() const {
return ops_; return ops_;
} }
size_t get_grease_size() const {
return grease_size_;
}
private: private:
std::vector<Op> ops_; std::vector<Op> ops_;
size_t grease_size_;
}; };
class TlsHelloContext { class TlsHelloContext {
public: public:
explicit TlsHelloContext(std::string domain) { TlsHelloContext(size_t grease_size, std::string domain) : grease_(grease_size, '\0'), domain_(std::move(domain)) {
Grease::init(MutableSlice(grease_.data(), grease_.size())); Grease::init(grease_);
domain_ = std::move(domain);
} }
char get_grease(size_t i) const { char get_grease(size_t i) const {
CHECK(i < grease_.size()); CHECK(i < grease_.size());
return grease_[i]; return grease_[i];
} }
size_t grease_size() const { size_t get_grease_size() const {
return grease_.size(); return grease_.size();
} }
Slice get_domain() const { Slice get_domain() const {
@ -140,8 +151,7 @@ class TlsHelloContext {
} }
private: private:
constexpr static size_t MAX_GREASE = 8; std::string grease_;
std::array<char, MAX_GREASE> grease_;
std::string domain_; std::string domain_;
}; };
@ -174,7 +184,7 @@ class TlsHelloCalcLength {
break; break;
case Type::Grease: case Type::Grease:
CHECK(context); CHECK(context);
if (op.seed < 0 || static_cast<size_t>(op.seed) >= context->grease_size()) { if (op.seed < 0 || static_cast<size_t>(op.seed) >= context->get_grease_size()) {
return on_error(Status::Error("Invalid grease seed")); return on_error(Status::Error("Invalid grease seed"));
} }
size_ += 2; size_ += 2;
@ -234,9 +244,9 @@ class TlsHelloCalcLength {
class TlsHelloStore { class TlsHelloStore {
public: public:
TlsHelloStore(MutableSlice dest) : data_(dest), dest_(dest) { explicit TlsHelloStore(MutableSlice dest) : data_(dest), dest_(dest) {
} }
void do_op(const TlsHello::Op &op, TlsHelloContext *context) { void do_op(const TlsHello::Op &op, const TlsHelloContext *context) {
using Type = TlsHello::Op::Type; using Type = TlsHello::Op::Type;
switch (op.type) { switch (op.type) {
case Type::String: case Type::String:
@ -281,19 +291,20 @@ class TlsHelloStore {
data_[begin_offset + 1] = static_cast<char>(size & 0xff); data_[begin_offset + 1] = static_cast<char>(size & 0xff);
break; break;
} }
default:
UNREACHABLE();
} }
} }
void finish(int32 unix_time) { void finish(Slice secret, int32 unix_time) {
int zero_pad = 515 - static_cast<int>(get_offset()); int zero_pad = 515 - static_cast<int>(get_offset());
using Op = TlsHello::Op; using Op = TlsHello::Op;
do_op(Op::begin_scope(), nullptr); do_op(Op::begin_scope(), nullptr);
do_op(Op::zero(zero_pad), nullptr); do_op(Op::zero(zero_pad), nullptr);
do_op(Op::end_scope(), nullptr); do_op(Op::end_scope(), nullptr);
auto tmp = sha256(data_); auto hash_dest = data_.substr(11, 32);
auto hash_dest = data_.substr(11); hmac_sha256(secret, data_, hash_dest);
hash_dest.copy_from(tmp);
int32 old = as<int32>(hash_dest.substr(28).data()); int32 old = as<int32>(hash_dest.substr(28).data());
as<int32>(hash_dest.substr(28).data()) = old ^ unix_time; as<int32>(hash_dest.substr(28).data()) = old ^ unix_time;
CHECK(dest_.empty()); CHECK(dest_.empty());
@ -303,97 +314,68 @@ class TlsHelloStore {
MutableSlice data_; MutableSlice data_;
MutableSlice dest_; MutableSlice dest_;
std::vector<size_t> scope_offset_; std::vector<size_t> scope_offset_;
size_t get_offset() {
size_t get_offset() const {
return data_.size() - dest_.size(); return data_.size() - dest_.size();
} }
}; };
class TlsObfusaction { class TlsObfusaction {
public: public:
static std::string generate_header(std::string domain, int32 unix_time) { static std::string generate_header(std::string domain, Slice secret, int32 unix_time) {
CHECK(!domain.empty());
CHECK(secret.size() == 16);
auto &hello = TlsHello::get_default(); auto &hello = TlsHello::get_default();
TlsHelloContext context(domain); TlsHelloContext context(hello.get_grease_size(), std::move(domain));
TlsHelloCalcLength calc_length; TlsHelloCalcLength calc_length;
for (auto &op : hello.get_ops()) { for (auto &op : hello.get_ops()) {
calc_length.do_op(op, &context); calc_length.do_op(op, &context);
} }
auto length = calc_length.finish().move_as_ok(); auto length = calc_length.finish().move_as_ok();
std::string data(length, 0); std::string data(length, '\0');
TlsHelloStore storer(data); TlsHelloStore storer(data);
for (auto &op : hello.get_ops()) { for (auto &op : hello.get_ops()) {
storer.do_op(op, &context); storer.do_op(op, &context);
} }
storer.finish(0); storer.finish(secret, unix_time);
return data; return data;
} }
}; };
void TlsInit::send_hello() { void TlsInit::send_hello() {
auto hello = TlsObfusaction::generate_header(username_, 0); auto hello =
TlsObfusaction::generate_header(username_, password_, static_cast<int32>(Clocks::system())); // TODO correct time
fd_.output_buffer().append(hello); fd_.output_buffer().append(hello);
state_ = State::WaitHelloResponse; state_ = State::WaitHelloResponse;
} }
Status TlsInit::wait_hello_response() { Status TlsInit::wait_hello_response() {
//[
auto it = fd_.input_buffer().clone(); auto it = fd_.input_buffer().clone();
//S "\x16\x03\x03" for (auto first : {Slice("\x16\x03\x03"), Slice("\x14\x03\x03\x00\x01\x01\x17\x03\x03")}) {
{ if (it.size() < first.size() + 2) {
Slice first = "\x16\x03\x03"; return Status::OK();
std::string got_first(first.size(), 0);
if (it.size() < first.size()) {
return td::Status::OK();
} }
std::string got_first(first.size(), '\0');
it.advance(first.size(), got_first); it.advance(first.size(), got_first);
if (first != got_first) { if (first != got_first) {
return Status::Error("First part of response to hello is invalid"); return Status::Error("First part of response to hello is invalid");
} }
}
//[
{
if (it.size() < 2) {
return td::Status::OK();
}
uint8 tmp[2]; uint8 tmp[2];
it.advance(2, MutableSlice(tmp, 2)); it.advance(2, MutableSlice(tmp, 2));
size_t skip_size = (tmp[0] << 8) + tmp[1]; size_t skip_size = (tmp[0] << 8) + tmp[1];
if (it.size() < skip_size) { if (it.size() < skip_size) {
return td::Status::OK(); return Status::OK();
} }
it.advance(skip_size); it.advance(skip_size);
} }
//S "\x14\x03\x03\x00\x01\x01\x17\x03\x03"
{
Slice first = "\x14\x03\x03\x00\x01\x01\x17\x03\x03";
std::string got_first(first.size(), 0);
if (it.size() < first.size()) {
return td::Status::OK();
}
it.advance(first.size(), got_first);
if (first != got_first) {
return Status::Error("Second part of response to hello is invalid");
}
}
//[
{
if (it.size() < 2) {
return td::Status::OK();
}
uint8 tmp[2];
it.advance(2, MutableSlice(tmp, 2));
size_t skip_size = (tmp[0] << 8) + tmp[1];
if (it.size() < skip_size) {
return td::Status::OK();
}
it.advance(skip_size);
}
fd_.input_buffer() = std::move(it); fd_.input_buffer() = std::move(it);
stop(); stop();
return td::Status::OK(); return Status::OK();
} }
Status TlsInit::loop_impl() { Status TlsInit::loop_impl() {
@ -407,4 +389,5 @@ Status TlsInit::loop_impl() {
} }
return Status::OK(); return Status::OK();
} }
} // namespace td } // namespace td

View File

@ -8,6 +8,7 @@
#include "td/net/TransparentProxy.h" #include "td/net/TransparentProxy.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h" #include "td/utils/Status.h"
namespace td { namespace td {

View File

@ -21,12 +21,12 @@ void TlsReaderByteFlow::loop() {
uint8 buf[5]; uint8 buf[5];
it.advance(5, MutableSlice(buf, 5)); it.advance(5, MutableSlice(buf, 5));
if (Slice(buf, 3) != Slice("\x17\x03\x03")) { if (Slice(buf, 3) != Slice("\x17\x03\x03")) {
close_input(td::Status::Error("Invalid bytes at the beginning of a packet (emulated tls)")); close_input(Status::Error("Invalid bytes at the beginning of a packet (emulated tls)"));
return; return;
} }
size_t len = (buf[3] << 8) | buf[4]; size_t len = (buf[3] << 8) | buf[4];
if (len > (1 << 14)) { if (len > (1 << 14)) {
close_input(td::Status::Error("Packet lenght is too big (emulated tls)")); close_input(Status::Error("Packet length is too big (emulated tls)"));
return; return;
} }

View File

@ -12,7 +12,6 @@ namespace td {
class TlsReaderByteFlow final : public ByteFlowBase { class TlsReaderByteFlow final : public ByteFlowBase {
public: public:
TlsReaderByteFlow() = default;
void loop() override; void loop() override;
}; };

View File

@ -502,6 +502,8 @@ void ConnectionCreator::ping_proxy_resolved(int32 proxy_id, IPAddress ip_address
} }
auto socket_fd = r_socket_fd.move_as_ok(); auto socket_fd = r_socket_fd.move_as_ok();
bool emulate_tls = extra.transport_type.emulate_tls();
auto socket_fd_promise = auto socket_fd_promise =
PromiseCreator::lambda([promise = std::move(promise), actor_id = actor_id(this), PromiseCreator::lambda([promise = std::move(promise), actor_id = actor_id(this),
transport_type = std::move(extra.transport_type)](Result<SocketFd> r_socket_fd) mutable { transport_type = std::move(extra.transport_type)](Result<SocketFd> r_socket_fd) mutable {
@ -512,7 +514,7 @@ void ConnectionCreator::ping_proxy_resolved(int32 proxy_id, IPAddress ip_address
std::move(transport_type), std::move(promise)); std::move(transport_type), std::move(promise));
}); });
CHECK(proxy.use_proxy()); CHECK(proxy.use_proxy());
if (proxy.use_socks5_proxy() || proxy.use_http_tcp_proxy()) { if (proxy.use_socks5_proxy() || proxy.use_http_tcp_proxy() || emulate_tls) {
class Callback : public TransparentProxy::Callback { class Callback : public TransparentProxy::Callback {
public: public:
explicit Callback(Promise<SocketFd> promise) : promise_(std::move(promise)) { explicit Callback(Promise<SocketFd> promise) : promise_(std::move(promise)) {
@ -526,18 +528,25 @@ void ConnectionCreator::ping_proxy_resolved(int32 proxy_id, IPAddress ip_address
private: private:
Promise<SocketFd> promise_; Promise<SocketFd> promise_;
}; };
auto callback = make_unique<Callback>(std::move(socket_fd_promise));
LOG(INFO) << "Start ping proxy: " << extra.debug_str; LOG(INFO) << "Start ping proxy: " << extra.debug_str;
auto token = next_token(); auto token = next_token();
if (proxy.use_socks5_proxy()) { if (proxy.use_socks5_proxy()) {
children_[token] = { children_[token] = {
false, create_actor<Socks5>("PingSocks5", std::move(socket_fd), extra.mtproto_ip, proxy.proxy().user().str(), false, create_actor<Socks5>("PingSocks5", std::move(socket_fd), extra.mtproto_ip, proxy.proxy().user().str(),
proxy.proxy().password().str(), proxy.proxy().password().str(), std::move(callback), create_reference(token))};
make_unique<Callback>(std::move(socket_fd_promise)), create_reference(token))}; } else if (proxy.use_http_tcp_proxy()) {
} else { children_[token] = {false, create_actor<HttpProxy>("PingHttpProxy", std::move(socket_fd), extra.mtproto_ip,
proxy.proxy().user().str(), proxy.proxy().password().str(),
std::move(callback), create_reference(token))};
} else if (emulate_tls) {
children_[token] = { children_[token] = {
false, create_actor<HttpProxy>("PingHttpProxy", std::move(socket_fd), extra.mtproto_ip, false, create_actor<TlsInit>("PingTlsInit", std::move(socket_fd), extra.mtproto_ip, "www.google.com",
proxy.proxy().user().str(), proxy.proxy().password().str(), hex_decode(proxy.proxy().secret().substr(2)).move_as_ok(), std::move(callback),
make_unique<Callback>(std::move(socket_fd_promise)), create_reference(token))}; create_reference(token))};
} else {
UNREACHABLE();
} }
} else { } else {
socket_fd_promise.set_value(std::move(socket_fd)); socket_fd_promise.set_value(std::move(socket_fd));
@ -1020,10 +1029,13 @@ void ConnectionCreator::client_loop(ClientInfo &client) {
children_[token] = {true, create_actor<HttpProxy>("HttpProxy", std::move(socket_fd), extra.mtproto_ip, children_[token] = {true, create_actor<HttpProxy>("HttpProxy", std::move(socket_fd), extra.mtproto_ip,
proxy.proxy().user().str(), proxy.proxy().password().str(), proxy.proxy().user().str(), proxy.proxy().password().str(),
std::move(callback), create_reference(token))}; std::move(callback), create_reference(token))};
} else if (emulate_tls) {
children_[token] = {
true, create_actor<TlsInit>("TlsInit", std::move(socket_fd), extra.mtproto_ip, "www.google.com",
hex_decode(proxy.proxy().secret().substr(2)).move_as_ok(), std::move(callback),
create_reference(token))};
} else { } else {
children_[token] = {true, create_actor<TlsInit>("HttpProxy", std::move(socket_fd), extra.mtproto_ip, UNREACHABLE();
"www.google.com" /*todo use domain*/, "", std::move(callback),
create_reference(token))};
} }
} else { } else {
VLOG(connections) << "In client_loop: create new direct connection " << extra.debug_str; VLOG(connections) << "In client_loop: create new direct connection " << extra.debug_str;

View File

@ -86,6 +86,7 @@ class SocketFdImpl : private Iocp::Callback {
} }
Result<size_t> write(Slice data) { Result<size_t> write(Slice data) {
// LOG(ERROR) << "Write: " << format::as_hex_dump<0>(data);
output_writer_.append(data); output_writer_.append(data);
if (is_write_waiting_) { if (is_write_waiting_) {
auto lock = lock_.lock(); auto lock = lock_.lock();
@ -103,6 +104,8 @@ class SocketFdImpl : private Iocp::Callback {
auto res = input_reader_.advance(td::min(slice.size(), input_reader_.size()), slice); auto res = input_reader_.advance(td::min(slice.size(), input_reader_.size()), slice);
if (res == 0) { if (res == 0) {
get_poll_info().clear_flags(PollFlags::Read()); get_poll_info().clear_flags(PollFlags::Read());
} else {
// LOG(ERROR) << "Read: " << format::as_hex_dump<0>(Slice(slice.substr(0, res)));
} }
return res; return res;
} }

View File

@ -30,15 +30,12 @@
#include "td/telegram/net/Session.h" #include "td/telegram/net/Session.h"
#include "td/telegram/NotificationManager.h" #include "td/telegram/NotificationManager.h"
#include "td/utils/as.h"
#include "td/utils/base64.h" #include "td/utils/base64.h"
#include "td/utils/common.h" #include "td/utils/common.h"
#include "td/utils/crypto.h"
#include "td/utils/logging.h" #include "td/utils/logging.h"
#include "td/utils/port/IPAddress.h" #include "td/utils/port/IPAddress.h"
#include "td/utils/port/SocketFd.h" #include "td/utils/port/SocketFd.h"
#include "td/utils/Random.h" #include "td/utils/Random.h"
#include "td/utils/Span.h"
#include "td/utils/Status.h" #include "td/utils/Status.h"
#include "td/utils/Time.h" #include "td/utils/Time.h"
@ -591,15 +588,18 @@ class Mtproto_FastPing : public Test {
}; };
RegisterTest<Mtproto_FastPing> mtproto_fastping("Mtproto_FastPing"); RegisterTest<Mtproto_FastPing> mtproto_fastping("Mtproto_FastPing");
TEST(Mtproto, TlsObfusaction) { TEST(Mtproto, Grease) {
std::string s(10000, 'a'); std::string s(10000, '0');
Grease::init(s); Grease::init(s);
for (auto c : s) { for (auto c : s) {
CHECK((c & 0xf) == 0xa); CHECK((c & 0xF) == 0xA);
} }
for (size_t i = 1; i < s.size(); i += 2) { for (size_t i = 1; i < s.size(); i += 2) {
CHECK(s[i] != s[i - 1]); CHECK(s[i] != s[i - 1]);
} }
}
TEST(Mtproto, TlsObfusaction) {
std::string domain = "www.google.com"; std::string domain = "www.google.com";
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR)); SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
ConcurrentScheduler sched; ConcurrentScheduler sched;
@ -619,7 +619,8 @@ TEST(Mtproto, TlsObfusaction) {
IPAddress ip_address; IPAddress ip_address;
ip_address.init_host_port(domain, 443).ensure(); ip_address.init_host_port(domain, 443).ensure();
SocketFd fd = SocketFd::open(ip_address).move_as_ok(); SocketFd fd = SocketFd::open(ip_address).move_as_ok();
create_actor<TlsInit>("TlsInit", std::move(fd), IPAddress(), domain, "", make_unique<Callback>(), ActorShared<>()) create_actor<TlsInit>("TlsInit", std::move(fd), IPAddress(), domain, "0123456789secret", make_unique<Callback>(),
ActorShared<>())
.release(); .release();
} }