fix handshakes

This commit is contained in:
2026-03-19 14:11:34 +07:00
parent 660fb4bd31
commit 494e9aa68b
9 changed files with 437 additions and 263 deletions
+73 -17
View File
@@ -1,29 +1,85 @@
# Netrunner TLS Engine (tlseng)
# Netrunner TLS Engine (`tlseng`)
Модуль `tlseng` отвечает за генерацию и анализ TLS-хендшейков. Его основная задача — маскировка сетевой активности под легитимный трафик популярных браузеров (Chrome, Edge).
Модуль `tlseng` — это высокопроизводительное ядро для генерации, парсинга и манипуляции TLS-трафиком (TLS 1.2 / TLS 1.3). Основная задача движка — **обход систем глубокого анализа трафика (DPI)** и активных проб (Active Probing) путем идеальной мимикрии под легитимный трафик современных браузеров.
## Возможности
Движок не является полноценной реализацией криптографического стека TLS. Это "умный фасад", который формирует криптографически достоверные пакеты `ClientHello` и `ServerHello`, позволяя скрыто передавать авторизационные данные и устанавливать защищенные туннели, неотличимые от обычного HTTPS-трафика.
- **Fingerprint Mimicry:** Полная имитация параметров расширений, порядка их следования и наборов шифров.
- **Dynamic Extension Builder:** Потокобезопасная сборка TLS-расширений (SNI, ALPN, KeyShare, PADDING и т.д.).
- **GREASE Support:** Автоматическая генерация случайных идентификаторов для обхода сигнатурных фильтров.
- **TlsRecord Encapsulation:** Упаковка данных в стандартные TLS-записи согласно спецификациям.
## 🌟 Ключевые возможности
## Архитектура
- **Perfect Fingerprint Mimicry (JA3 / JA4):** Полная имитация параметров расширений, порядка их следования, поддерживаемых групп и наборов шифров. Движок генерирует отпечатки, на 100% совпадающие с реальными браузерами (Chrome, Edge, Firefox, Safari).
- **Stealth Authentication (Stateless):** Уникальный механизм скрытой авторизации. Токен доступа (Auth Tag) маскируется внутри поля `Session ID` (TLS 1.3 эхо), что позволяет серверу аутентифицировать клиента еще до начала обмена Application Data, не привлекая внимания DPI.
- **Advanced GREASE Support:** Автоматическая и контекстно-зависимая генерация случайных идентификаторов (GREASE) согласно RFC 8701 для обхода сигнатурных фильтров, специфичных для Chromium-браузеров.
- **Smart Padding:** Интеллектуальный расчет расширения `Padding` с учетом размера всех заголовков (overhead) для выравнивания пакета `ClientHello` до заданного размера (например, ровно 512 байт для Chrome), исключая эвристическое детектирование по длине пакета.
- **Zero-Copy Parsing:** Использование крейта `bytes` (`Bytes`, `BytesMut`) для парсинга и сериализации пакетов без лишних аллокаций в памяти.
- `profile/`: Описания браузерных конфигураций (JA3/JA4 совместимость).
- `extension/`: Логика построения TLS-расширений (`ExtensionBuilder`).
- `handshake/`: Реализация `ClientHello` и `ServerHello`.
- `tls_record/`: Низкоуровневая упаковка данных в TLS-формат.
## 🏗 Архитектура модулей
## Пример использования
Структура модуля разделена на логические компоненты для максимальной гибкости и расширяемости:
- **`profile/`**: База данных профилей браузеров и серверов (`BrowserProfile`, `ServerProfile`). Содержит константы для версий, шифров, наличия GREASE, ALPS и целевого размера паддинга.
- **`extension/`**: Мощный и потокобезопасный `ExtensionBuilder` для сборки TLS-расширений (SNI, ALPN, KeyShare, ALPS, Supported Versions и т.д.) в строгом соответствии с профилем.
- **`handshake/`**: Реализация структур `ClientHello` и `ServerHello`. Управляет генерацией рандома, встраиванием скрытых тегов и выбором шифров.
- **`tls_record/`**: Низкоуровневая инкапсуляция данных в `TlsRecord` с правильным указанием версий (маскировка Record Layer Version под `0x0301`).
- **`types/` и `consts/`**: Строго типизированные перечисления (Enums) и константы протокола TLS.
## 🛠 Глубокое погружение: Как это работает
### 1. Профилирование трафика
Движок не хардкодит расширения. Вместо этого `ExtensionBuilder` принимает `BrowserProfile`. Профиль указывает, нужны ли GREASE-значения, какой должен быть `ExtensionOrder` (порядок следования) и до какого размера "надувать" пакет паддингом. Это позволяет в одну строчку переключать маскировку с Chrome 131 на Firefox 130.
### 2. Скрытая авторизация (Session ID Auth)
В TLS 1.3 поле `Session ID` сохранено для обратной совместимости (Middlebox Compatibility Mode).
`tlseng` использует это 32-байтовое поле как контейнер:
1. **Клиент:** Заполняет первые 16 байт случайными данными, а в последние 16 байт записывает HMAC (Auth Tag), сгенерированный на основе локального времени и мастер-ключа.
2. **DPI:** Видит стандартный 32-байтовый массив, неотличимый от обычного рандома.
3. **Сервер:** Извлекает последние 16 байт из `ClientHello` и верифицирует их. Если проверка не пройдена, соединение разрывается (защита от активного сканирования). Если пройдена — сервер возвращает этот же `Session ID` эхом в `ServerHello`.
## 🚀 Примеры использования
### Генерация `ClientHello` (на стороне клиента)
Создание маскировочного пакета под Chrome 131 со встроенной авторизацией:
```rust
use crate::tlseng::profile::BrowserProfile;
use crate::tlseng::extension::ExtensionBuilder;
use crate::tlseng::handshake::ClientHello;
use crate::tlseng::tls_record::TlsRecord;
use bytes::Bytes;
// Создание хендшейка для профиля Chrome 131
let mut builder = ExtensionBuilder::new();
builder.apply_profile(&BrowserProfile::CHROME_131, "google.com", &public_key);
let extensions = builder.build();
pub fn make_client_hello(profile: &BrowserProfile, host: &str, keys: &SessionKeys) -> Bytes {
let tls_random = keys.salt.get_local();
// Расчет размера заголовков для точного Padding
let overhead = 5 + 4 + 2 + 32 + 1 + 32 + 2 + (profile.cipher_suites.len() * 2) + 2 + 2;
// Сборка расширений по профилю
let mut ext_builder = ExtensionBuilder::new();
ext_builder.apply_profile(profile, host, &keys.ecdh.public_key.to_bytes(), overhead);
let extensions_bytes = ext_builder.build();
// Генерация Session ID с Auth Tag
let mut session_id_bytes = [0u8; 32];
rand::fill(&mut session_id_bytes[..16]);
session_id_bytes[16..].copy_from_slice(&keys.generate_auth_tag());
let client_hello = ClientHello {
version: profile.versions.get_legacy(), // Всегда 0x0303 для TLS 1.3
random: tls_random,
session_id: Bytes::copy_from_slice(&session_id_bytes),
cipher_suites: profile.cipher_suites.to_vec(),
extensions: extensions_bytes,
};
let record = TlsRecord::new(
ContentType::Handshake,
profile.record_layer_version, // 0x0301 для маскировки
client_hello.serialize(),
);
record.serialize()
}
```
+56 -31
View File
@@ -39,14 +39,6 @@ impl Extension {
data,
}
}
pub fn pack(etype: u16, data: &[u8]) -> Bytes {
let mut ext = BytesMut::with_capacity(4 + data.len());
ext.put_u16(etype);
ext.put_u16(data.len() as u16);
ext.put_slice(data);
ext.freeze()
}
}
pub struct ExtensionBuilder {
@@ -61,8 +53,9 @@ impl ExtensionBuilder {
}
fn add_extension(&mut self, etype: u16, data: &[u8]) {
let ext = Extension::pack(etype, data);
self.payload.put_slice(&ext);
self.payload.put_u16(etype);
self.payload.put_u16(data.len() as u16);
self.payload.put_slice(data);
}
pub fn grease(&mut self) {
@@ -72,6 +65,18 @@ impl ExtensionBuilder {
self.add_extension(etype, &[]);
}
pub fn grease_with_id(&mut self, etype: u16) {
self.add_extension(etype, &[]);
}
pub fn apply_generic_extension(&mut self, etype: u16, profile: &BrowserProfile) {
match etype {
_ => {
netrunner_logger::trace!(etype, "Applying generic or unknown extension");
}
}
}
pub fn server_name(&mut self, host: &str) {
let host_bytes = host.as_bytes();
let host_len = host_bytes.len() as u16;
@@ -117,15 +122,21 @@ impl ExtensionBuilder {
self.add_extension(TlsExtensions::SUPPORTED_VERSIONS, &data);
}
pub fn key_share(&mut self, pub_key: &[u8]) {
let mut data = BytesMut::with_capacity(38);
data.put_u16(34);
data.put_u16(TlsGroups::X25519);
data.put_u16(32);
data.put_slice(pub_key);
self.add_extension(TlsExtensions::KEY_SHARE, &data);
}
pub fn key_share(&mut self, profile: &BrowserProfile, pub_key: &[u8]) {
let key_len = pub_key.len() as u16;
let mut entry = BytesMut::with_capacity(key_len as usize + 4);
let group = profile.groups.0.first().cloned().unwrap_or(0x001d);
entry.put_u16(group);
entry.put_u16(key_len);
entry.put_slice(pub_key);
let mut list = BytesMut::with_capacity(entry.len() + 2);
list.put_u16(entry.len() as u16);
list.put_slice(&entry);
self.add_extension(TlsExtensions::KEY_SHARE, &list);
}
pub fn application_settings(&mut self, protocols: &[&str]) {
let mut data = BytesMut::new();
for proto in protocols {
@@ -202,16 +213,23 @@ impl ExtensionBuilder {
self.add_extension(TlsExtensions::RENEGOTIATION_INFO, &[0x00]);
}
pub fn padding(&mut self, target_size: usize) {
let current_size = self.payload.len();
if target_size > current_size + 4 {
let pad_len = target_size - current_size - 4;
pub fn padding(&mut self, target_size: usize, overhead: usize) {
let current_total_size = self.payload.len() + overhead;
if target_size > current_total_size + 4 {
let pad_len = target_size - current_total_size - 4;
let data = vec![0u8; pad_len];
self.add_extension(TlsExtensions::PADDING, &data);
}
}
pub fn apply_profile(&mut self, profile: &BrowserProfile, host: &str, pub_key: &[u8]) {
pub fn apply_profile(
&mut self,
profile: &BrowserProfile,
host: &str,
pub_key: &[u8],
overhead: usize,
) {
for &ext_id in &profile.extension_order {
match ext_id {
TlsExtensions::SNI => self.server_name(host),
@@ -231,21 +249,28 @@ impl ExtensionBuilder {
TlsExtensions::SESSION_TICKET => self.session_ticket(),
TlsExtensions::SUPPORTED_VERSIONS => self.supported_versions(profile.versions),
TlsExtensions::PSK_MODES => self.psk_key_exchange_modes(),
TlsExtensions::KEY_SHARE => self.key_share(pub_key),
TlsExtensions::ALPS => self.application_settings(&["h2"]),
TlsExtensions::KEY_SHARE => self.key_share(profile, pub_key),
TlsExtensions::ALPS => {
if !profile.alps_protocols.is_empty() {
self.application_settings(profile.alps_protocols);
}
}
TlsExtensions::STATUS_REQUEST => self.status_request(),
TlsExtensions::EC_POINT_FORMATS => self.ec_point_formats(),
TlsExtensions::RENEGOTIATION_INFO => self.renegotiation_info(),
TlsExtensions::PADDING => {
if profile.is_chromium {
self.padding(512);
} else {
self.add_extension(TlsExtensions::PADDING, &[]);
if profile.target_padding_len > 0 {
// Передаем накопленный оверхед всего пакета
self.padding(profile.target_padding_len as usize, overhead);
}
}
id if (id & 0x0f0f) == 0x0a0a => self.grease(),
_ => {}
id if TlsExtensions::is_grease(id) => {
if profile.has_grease {
self.grease_with_id(id); // Используем конкретный ID из ExtensionOrder
}
}
_ => self.apply_generic_extension(ext_id, profile),
}
}
}
+113 -83
View File
@@ -1,12 +1,14 @@
use aead::{rand_core::RngCore, OsRng};
use bytes::{BufMut, Bytes, BytesMut};
use crate::{
crypto::session::SessionKeys,
tlseng::{
consts::{HANDSHAKE_TYPE_CLIENT_HELLO, HANDSHAKE_TYPE_SERVER_HELLO},
extension::ExtensionBuilder,
profile::BrowserProfile,
profile::{BrowserProfile, ServerProfile},
tls_record::TlsRecord,
types::{ContentType, HelloType, ProtocolVersion},
types::{ContentType, HelloType, ProtocolVersion, TlsVersions},
},
utils::u24::U24,
};
@@ -34,71 +36,75 @@ impl ClientHello {
buf.put_u8(HANDSHAKE_TYPE_CLIENT_HELLO);
// Резервируем 3 байта под длину Handshake (U24)
let length_pos = buf.len();
buf.put_bytes(0, 3);
buf.put_u16(0x0303);
buf.put_u16(0x0303); // Legacy Version
buf.put_slice(&self.random);
// Session ID
buf.put_u8(self.session_id.len() as u8);
buf.put_slice(&self.session_id);
// Cipher Suites
buf.put_u16((self.cipher_suites.len() * 2) as u16);
for &suite in &self.cipher_suites {
buf.put_u16(suite);
}
// Compression Methods (всегда 0x01 0x00 для маскировки)
buf.put_u8(1);
buf.put_u8(0x00);
// Extensions
buf.put_u16(self.extensions.len() as u16);
buf.put_slice(&self.extensions);
// ПРАВИЛЬНЫЙ РАСЧЕТ ДЛИНЫ:
// Длина сообщения Handshake не включает сам тип (1 байт) и поле длины (3 байта)
let total_len = (buf.len() - length_pos - 3) as u32;
let len_bytes = total_len.to_be_bytes();
// Записываем 3 байта длины (Big Endian)
buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]);
let ext_len = self.extensions.len();
let total_handshake_len = (buf.len() - length_pos - 3) as u32;
netrunner_logger::debug!(
handshake_type = "ClientHello",
body_len = total_handshake_len,
extensions_len = ext_len,
total_bytes = buf.len(),
"Serialized Handshake message"
);
buf.freeze()
}
pub fn make_client_hello(
profile: &BrowserProfile,
host: &str,
public_key: &[u8; 32],
salt: [u8; 32],
) -> Bytes {
let tls_random = salt;
pub fn make_client_hello(profile: &BrowserProfile, host: &str, keys: &SessionKeys) -> Bytes {
let tls_random = keys.salt.get_local();
let mut session_id_bytes = [0u8; 32];
OsRng.fill_bytes(&mut session_id_bytes[..16]);
session_id_bytes[16..].copy_from_slice(&keys.generate_auth_tag());
let record_header = 5;
let handshake_header = 4;
let client_hello_fixed = 2 + 32 + 1 + 32 + 2 + (profile.cipher_suites.len() * 2) + 2 + 2;
let total_overhead = record_header + handshake_header + client_hello_fixed;
let mut ext_builder = ExtensionBuilder::new();
ext_builder.apply_profile(profile, host, public_key);
let extensions_bytes = ext_builder.build();
ext_builder.apply_profile(
profile,
host,
&keys.ecdh.public_key.to_bytes(),
total_overhead,
);
let mut session_id = BytesMut::with_capacity(32);
session_id.put_slice(&[0u8; 32]);
let extensions_bytes = ext_builder.build();
let client_hello = ClientHello {
version: ProtocolVersion::Tls12,
random: tls_random,
session_id: session_id.freeze(),
cipher_suites: vec![0x1301, 0x1302, 0x1303],
session_id: Bytes::copy_from_slice(&session_id_bytes),
cipher_suites: profile.cipher_suites.to_vec(),
extensions: extensions_bytes,
};
let record = TlsRecord::new(
ContentType::Handshake,
ProtocolVersion::Tls10,
profile.record_layer_version,
client_hello.serialize(),
);
@@ -114,93 +120,117 @@ pub struct ServerHello {
pub extensions: BytesMut,
}
impl ServerHello {
pub fn from_client_hello(
client_hello: &ClientHello,
server_public_key: &[u8],
salt: [u8; 32],
) -> Self {
let server_random = salt;
let selected_suite = client_hello
.cipher_suites
.first()
.cloned()
.unwrap_or(0x1301);
let mut extensions = BytesMut::new();
extensions.put_u16(0x002b);
extensions.put_u16(2);
extensions.put_u16(0x0304);
extensions.put_u16(0x0033);
extensions.put_u16(36);
extensions.put_u16(0x001d);
extensions.put_u16(32);
extensions.put_slice(server_public_key);
Self {
version: ProtocolVersion::Tls12,
random: server_random,
session_id: client_hello.session_id.clone(),
cipher_suite: selected_suite,
extensions,
}
}
pub fn make_server_hello(
client_hello: &ClientHello,
server_public_key: &[u8],
salt: [u8; 32],
profile: &ServerProfile,
) -> Bytes {
let server_hello = Self::from_client_hello(client_hello, server_public_key, salt);
let handshake_payload = server_hello.serialize();
// Создаем инстанс ServerHello
let server_hello = Self::from_client_hello(client_hello, server_public_key, salt, profile);
// Оборачиваем в Record Layer
let record = TlsRecord::new(
ContentType::Handshake,
ProtocolVersion::Tls12,
handshake_payload,
profile.record_layer_version, // 0x0303 обычно
server_hello.serialize(), // Сериализация самого SH
);
record.serialize()
}
pub fn from_client_hello(
client_hello: &ClientHello,
server_public_key: &[u8],
salt: [u8; 32],
profile: &ServerProfile,
) -> Self {
// 1. Используем соль как рандом сервера
let server_random = salt;
// 2. Выбор Cipher Suite (твой код здесь хорош, оставляем)
let selected_suite = if profile.honor_cipher_order {
profile
.cipher_suites
.iter()
.find(|&&suite| client_hello.cipher_suites.contains(&suite))
.cloned()
.unwrap_or(0x1301)
} else {
client_hello
.cipher_suites
.iter()
.find(|&&suite| profile.cipher_suites.contains(&suite))
.cloned()
.unwrap_or(0x1301)
};
let mut extensions = BytesMut::new();
// 3. Работа с версией.
// Выбираем макс. версию из профиля (например, TLS 1.3)
let selected_version = profile.versions.max();
// Добавляем расширение Supported Versions (обязательно для TLS 1.3)
extensions.put_u16(0x002b);
extensions.put_u16(2);
extensions.put_u16(selected_version as u16);
// 4. Key Share (исправляем расчет длины, чтобы не было как в прошлый раз)
let key_len = server_public_key.len() as u16;
extensions.put_u16(0x0033);
extensions.put_u16(key_len + 4); // Группа(2) + Длина(2) + Ключ
extensions.put_u16(0x001d); // x25519
extensions.put_u16(key_len);
extensions.put_slice(server_public_key);
Self {
// Это поле будет использоваться как legacy_version (0x0303)
version: ProtocolVersion::Tls12,
random: server_random, // ИСПОЛЬЗУЕМ переменную
session_id: client_hello.session_id.clone(),
cipher_suite: selected_suite,
extensions,
}
}
pub fn serialize(&self) -> Bytes {
let mut buf = BytesMut::with_capacity(256 + self.extensions.len());
buf.put_u8(HANDSHAKE_TYPE_SERVER_HELLO);
// Резервируем место под длину Handshake (3 байта)
let length_pos = buf.len();
buf.put_bytes(0, 3);
buf.put_slice(&[0, 0, 0]);
buf.put_u16(0x0303);
// Используем версию из структуры (Legacy Version)
buf.put_u16(self.version as u16);
// Используем рандом из структуры
buf.put_slice(&self.random);
// Session ID (Echo)
buf.put_u8(self.session_id.len() as u8);
buf.put_slice(&self.session_id);
buf.put_u16(self.cipher_suite);
// Compression (0x00)
buf.put_u8(0x00);
buf.put_u16(self.extensions.len() as u16);
buf.put_slice(&self.extensions);
// Extensions
if !self.extensions.is_empty() {
buf.put_u16(self.extensions.len() as u16);
buf.put_slice(&self.extensions);
} else {
buf.put_u16(0);
}
let total_len = (buf.len() - length_pos - 3) as u32;
let len_bytes = total_len.to_be_bytes();
// Заполняем длину handshake-сообщения
let total_handshake_body_len = (buf.len() - length_pos - 3) as u32;
let len_bytes = total_handshake_body_len.to_be_bytes();
buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]);
let total_handshake_len = (buf.len() - length_pos - 3) as u32;
let ext_len = self.extensions.len();
netrunner_logger::debug!(
handshake_type = "ServerHello",
body_len = total_handshake_len,
extensions_len = ext_len,
total_bytes = buf.len(),
"Serialized Handshake message"
);
buf.freeze()
}
}
+52 -24
View File
@@ -1,21 +1,20 @@
use crate::tlseng::types::{ExtensionOrder, TlsGroups, TlsSignatures, TlsVersions};
use crate::tlseng::types::{
ExtensionOrder, ProtocolVersion, TlsGroups, TlsSignatures, TlsVersions,
};
pub struct BrowserProfile {
pub name: &'static str,
pub groups: TlsGroups,
pub signatures: TlsSignatures,
pub delegated_signatures: TlsSignatures,
pub versions: TlsVersions,
pub alpn: &'static [&'static str],
pub extension_order: ExtensionOrder,
pub is_chromium: bool,
pub cipher_suites: &'static [u16],
pub record_layer_version: ProtocolVersion,
pub target_padding_len: u16,
pub alps_protocols: &'static [&'static str],
pub has_grease: bool,
}
impl BrowserProfile {
@@ -25,40 +24,65 @@ impl BrowserProfile {
signatures: TlsSignatures::BROWSER_STANDARD,
delegated_signatures: TlsSignatures::BROWSER_STANDARD,
versions: TlsVersions::TLS_13_ONLY,
// 0x0301 (TLS 1.0) — классика для ClientHello в исполнении Chrome
record_layer_version: ProtocolVersion::Tls10,
cipher_suites: &[
0x1301, 0x1302, 0x1303, // TLS 1.3 Suites
0xc02b, 0xc02f, 0xc02c, 0xc030, // ECDHE-RSA/ECDSA
0xcca9, 0xcca8, // ChaCha20
],
alpn: &["h2", "http/1.1"],
extension_order: ExtensionOrder::CHROMIUM_131,
// --- НОВЫЕ ПОЛЯ ДЛЯ ГИБКОСТИ ---
is_chromium: true,
has_grease: true,
// Chrome активно использует ALPS для ускорения HTTP/2 настроек
alps_protocols: &["h2"],
// Chrome старается сделать ClientHello "пухлым",
// чтобы избежать проблем с некоторыми старыми Middleboxes
target_padding_len: 512,
};
pub const EDGE: Self = Self {
name: "Edge",
groups: TlsGroups::CHROMIUM,
pub const FIREFOX_130: Self = Self {
name: "Firefox 130 (Windows)",
groups: TlsGroups::MODERN, // У FF свой набор предпочтений
signatures: TlsSignatures::BROWSER_STANDARD,
delegated_signatures: TlsSignatures::BROWSER_STANDARD,
versions: TlsVersions::MODERN,
alpn: &["h2", "http/1.1"],
extension_order: ExtensionOrder::EDGE_130,
is_chromium: true,
};
versions: TlsVersions::MODERN, // FF часто шлет 1.3 + 1.2
pub const DEFAULT: Self = Self::CHROME_131;
// Firefox обычно пишет 0x0303 (TLS 1.2) в Record Layer
record_layer_version: ProtocolVersion::Tls12,
cipher_suites: &[0x1301, 0x1302, 0x1303, 0xc02b, 0xc02f, 0xc02c, 0xc030],
alpn: &["h2", "http/1.1"],
extension_order: ExtensionOrder::EDGE_130, // Тут стоит подставить личный порядок FF
is_chromium: false,
has_grease: false, // Firefox НЕ использует GREASE
alps_protocols: &[], // Firefox пока не так активно внедряет ALPS
target_padding_len: 0, // Firefox обычно не делает принудительный Padding до 512
};
}
pub struct ServerProfile {
pub name: &'static str,
pub versions: TlsVersions,
// ДОБАВЛЯЕМ:
pub record_layer_version: ProtocolVersion, // Что сервер пишет в ответном Record
pub cipher_suites: &'static [u16],
pub groups: TlsGroups,
pub signatures: TlsSignatures,
pub alpn: &'static [&'static str],
pub session_tickets: bool,
pub honor_cipher_order: bool,
}
@@ -66,6 +90,10 @@ impl ServerProfile {
pub const MODERN: Self = Self {
name: "Modern-Secure",
versions: TlsVersions::MODERN,
// Сервера обычно отвечают 0x0303 (TLS 1.2) в Record Layer
record_layer_version: ProtocolVersion::Tls12,
cipher_suites: &[0x1301, 0x1302, 0x1303],
groups: TlsGroups::MODERN,
signatures: TlsSignatures::BROWSER_STANDARD,
+18
View File
@@ -109,6 +109,16 @@ impl TlsVersions {
pub const TLS_13_ONLY: Self = Self(&[Self::TLS_1_3]);
pub const MODERN: Self = Self(&[Self::TLS_1_3, Self::TLS_1_2]);
pub fn max(&self) -> ProtocolVersion {
if self.0.contains(&Self::TLS_1_3) {
ProtocolVersion::Tls13
} else if self.0.contains(&Self::TLS_1_2) {
ProtocolVersion::Tls12
} else {
ProtocolVersion::Tls10
}
}
}
pub struct TlsExtensions;
@@ -131,6 +141,14 @@ impl TlsExtensions {
pub const KEY_SHARE: u16 = 0x0033;
pub const ALPS: u16 = 0x44cd;
pub const RENEGOTIATION_INFO: u16 = 0xff01;
pub fn is_grease(id: u16) -> bool {
if (id & 0x0f0f) != 0x0a0a {
return false;
}
// Убеждаемся, что оба байта идентичны (0x1A1A, а не 0x1A2A)
(id & 0xff) == (id >> 8)
}
}
#[derive(Clone, Copy)]