ZaStoGram_desktop/Telegram/SourceFiles/mtproto/proxy/mtproxy/client_hello_constants.h
loop-uh 45228f518c Check the relay's contract before sending the hello
Everything the relay demands of a first packet can be checked without it,
and checking late is what made this whole class of failure unreadable: a
hello that breaks the contract is not refused, it is passed on to the
domain the relay fronts for, and what comes back is an unsigned ServerHello
an eternity later with nothing in it naming the cause.

So the contract is now checked at the moment the hello is built. Length
between the canonical floor and the relay's read limit. Declared record and
handshake lengths equal to what was actually written. The extension block
ending where the packet does, and the walk through the extensions landing
on that end exactly - the fact parser leaves its loop as soon as fewer than
four bytes remain, so a template stopping short of the block slipped past
it. The first cipher suite after GREASE one of the three TLS 1.3 ones, with
GREASE recognised by its shape rather than a table, because that is what
the relay does with it. SNI equal to the domain the secret carries.

The check warns, it does not refuse. A relay disagreeing with this list
still gets its attempt, because a false alarm would take away a connection
that works; what it buys is the cause named where it is created.

That verdict is also what a digest mismatch is classified on now. The size
of the answer cannot do it, however much the first logs suggested it could:
the relay writes its own ServerHello either way and fills the first record
with random bytes sized after the domain it fronts for, so against live
relays a refusal came back in 184 bytes on one and in exactly the same 196
as an accepted handshake on the other, while an accepted one in front of a
heavy domain ran to 3381. The hello we sent is the only thing that
separates the two readings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 01:38:34 +03:00

36 lines
1.3 KiB
C++

/*
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/basic_types.h"
#include <crl/crl_time.h>
namespace MTP::details {
constexpr auto kClientHelloGreaseCount = 8;
constexpr auto kClientHelloLimit = 4096;
constexpr auto kClientHelloDigestLength = 32;
constexpr auto kTlsLengthFieldSize = sizeof(uint16);
// The size of the fake TLS ClientHello every Telegram client sends. It is not
// a free choice: relays identify their own clients by this shape, and the
// length lands in the record header as 0x0200, which the common relay builds
// compare against literally.
constexpr auto kCanonicalClientHelloLength = 517;
// The relay reads the ClientHello into a buffer of this size and then refuses
// anything that did not fit, so a longer hello is never seen as a client's.
constexpr auto kMaxRelayClientHelloLength = 4096;
// Two bytes of extension id plus two of extension length.
constexpr auto kTlsExtensionHeaderLength = 2 * kTlsLengthFieldSize;
constexpr auto kClientHelloFragmentDelayMin = crl::time(2);
constexpr auto kClientHelloFragmentDelayMax = crl::time(7);
} // namespace MTP::details