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>
36 lines
1.3 KiB
C++
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
|