Перейти к содержимому

VLESS Encryption

  1. Введение
  2. Криптографическая архитектура
  3. Режимы работы
  4. Формат конфигурации
  5. Генерация ключей
  6. Примеры конфигураций
  7. Сравнение с другими протоколами
  8. Производительность
  9. Безопасность
  10. Технические детали
  11. Best Practices
  12. Troubleshooting

VLESS Encryption - это легковесное, квантово-устойчивое шифрование с Perfect Forward Secrecy (PFS) для протокола VLESS, добавленное в Xray-core в PR #5067.

Ключевые особенности:

  • 🔐 Post-Quantum криптография: ML-KEM-768 + X25519
  • 🚀 0-RTT: Первое соединение 1-RTT, последующие 0-RTT через ticket
  • 🛡️ Perfect Forward Secrecy: Компрометация ключей не раскрывает прошлый трафик
  • 🎭 Три режима внешнего вида: native / xorpub / random
  • Совместимость с XTLS: ReadV/Splice для максимальной производительности
  • 🔒 Защита конфигурации клиента: Утечка конфига не раскрывает прошлый/будущий трафик

⚠️ ВАЖНО: VLESS Encryption НЕ предназначен для прямого обхода GFW!

Правильные сценарии использования:

  • 🌐 CDN: Защита UUID и SNI при проксировании через CDN
  • 🔄 Транзит/Relay: Цепочки прокси без TLS (например, запрет HTTP/TLS на транзитных серверах)
  • 🇮🇷 Non-TLS: Страны с ограничениями TLS (Иран)
  • 🏢 Обход аудита провайдера: Шифрование трафика до собственного VPS через общедоступные линии

Для прямого обхода блокировок используйте:

ХарактеристикаVLESS EncryptionSS 2022/AEADVMess AEAD
Внешний вид трафикаНастраиваемый (native/xorpub/random)RandomRandom
Клиент config безопасность
Perfect Forward Secrecy
Квантовая устойчивость
RTT overhead0-RTT (после 1-RTT)0-RTT0-RTT
Защита от replay✅ Идеальная⚠️ Ограниченная⚠️ Требует синхронизации времени
Определение пользователяO(1) по ticketO(n) переборO(n) перебор
Extra AEAD length✅ +13 байт
XTLS совместимость
Производительность10% быстрееБазоваяБазовая

VLESS Encryption использует MLKEM768X25519 - гибридную схему обмена ключами:

┌─────────────────────────────────────────────────────────┐
│ MLKEM768X25519 = ML-KEM-768 ⊕ X25519 │
│ │
│ • ML-KEM-768: Квантово-устойчивый KEM (NIST) │
│ • X25519: Классический ECDH (быстрый, проверенный) │
│ │
│ Защита: Даже если один алгоритм скомпрометирован, │
│ второй обеспечивает безопасность │
└─────────────────────────────────────────────────────────┘

Почему гибрид?

  • ML-KEM-768 защищает от квантовых компьютеров
  • X25519 обеспечивает проверенную безопасность сегодня
  • Компрометация любого из них не раскрывает трафик

Концепция:

Время: T0 ────────► T1 ────────► T2 ────────► T3
│ │ │ │
Ключи: K0 ────X K1 ────X K2 ────X K3
│ │ │ │
Связь: Нет прямой связи между ключами разных сессий
Компрометация K3 не позволяет расшифровать трафик T0, T1, T2

Реализация в VLESS Encryption:

  1. nfsKey (Non-Forward-Secret Key): Предварительно распределенный ключ

    • Генерируется из X25519/ML-KEM-768 пары клиент-сервер
    • Используется для аутентификации и начального обмена
  2. pfsKey (Perfect-Forward-Secret Key): Эфемерный ключ

    • Генерируется для каждой сессии через MLKEM768X25519
    • Уничтожается после использования
    • Независим от nfsKey
  3. unitedKey: Объединенный ключ

    unitedKey = Hash(pfsKey || nfsKey)
    • Используется для шифрования данных
    • Даже если ML-KEM-768 скомпрометирован в будущем, требуется и nfsKey

Проблема в SS/VMess:

Атакующий получает клиентский конфиг (PSK)
Может расшифровать:
• Весь прошлый трафик (если был записан)
• Весь будущий трафик
• Может выполнить MITM атаку

Решение в VLESS Encryption:

Атакующий получает клиентский конфиг (публичный ключ сервера)
НЕ может расшифровать:
• Прошлый трафик (требуются эфемерные ключи, уже уничтожены)
• Будущий трафик (требуется приватный ключ сервера)
Может выполнить MITM только если:
• Получит приватный ключ сервера (хранится только на сервере)

Уровни защиты:

КомпрометацияSS 2022/AEADVLESS Encryption
Клиентский конфиг❌ Весь трафик доступен✅ Трафик защищен
Серверный ключ❌ Весь трафик доступен⚠️ Только будущий MITM
Клиент + Сервер❌ Полная компрометация⚠️ Будущий MITM (прошлое защищено)

1-RTT (первое соединение):

Клиент Сервер
│ │
├─── Client Hello ──────────────►│ (nfsKey зашифрован, эфемерный публичный ключ)
│ │
│◄─── Server Hello ──────────────┤ (эфемерный публичный ключ, ticket, padding)
│ │
├─── Application Data ──────────►│ (unitedKey = pfsKey ⊕ nfsKey)
│◄─── Application Data ──────────┤
│ │

Задержка: +1 RTT (обмен ключами)

0-RTT (последующие соединения):

Клиент Сервер
│ │
├─── Ticket Hello ──────────────►│ (nfsKey зашифрован, ticket, данные)
│ │ ↓ Сервер извлекает pfsKey из ticket
│◄─── Application Data ──────────┤ (unitedKey, данные сразу)
│ │

Задержка: 0 RTT (данные в первом пакете)

Ticket механизм:

  • Сервер шифрует pfsKey в ticket
  • Ticket действителен 10 минут (настраивается: 300-600s)
  • Клиент автоматически переключается между 1-RTT и 0-RTT
  • После истечения ticket - автоматический возврат к 1-RTT

VLESS Encryption поддерживает три режима внешнего вида трафика:

Внешний вид: TLSv1.3-подобный (23 3 3 l>>8 l заголовки)

Характеристики:

  • ✅ Минимальные вычислительные затраты
  • ✅ Совместимость с XTLS ReadV/Splice
  • ⚠️ Публичные ключи X25519/ML-KEM-768 видны в трафике
  • ⚠️ AEAD заголовки имеют TLS-подобную структуру

Когда использовать:

  • Прямое TCP соединение
  • Транзит без DPI
  • Максимальная производительность

Пример заголовка:

23 03 03 XX XX [AEAD encrypted data]
│ │ │ │ │
│ │ │ └──┴─ Length (big-endian)
│ └──┴─────── TLS 1.3 version
└──────────── Application Data (23)

Внешний вид: TLS-подобный, но публичные ключи замаскированы

Характеристики:

  • ✅ Скрывает характеристики X25519/ML-KEM-768
  • ✅ Совместимость с XTLS ReadV/Splice
  • ✅ Минимальный overhead (~0.01% на ключи)
  • ⚠️ AEAD заголовки остаются TLS-подобными

Процесс XOR:

Original Public Key: [32 bytes X25519] [1184 bytes ML-KEM-768]
↓ XOR ↓ XOR
XOR Mask: [derived from nfsKey]
↓ ↓
Transmitted: [32 bytes XORed] [1184 bytes XORed]
Результат: Статистические характеристики публичных ключей скрыты

Когда использовать:

  • DPI может анализировать структуру ключей
  • Relay цепочки
  • Баланс между безопасностью и производительностью

Внешний вид: Полностью случайный поток байт

Характеристики:

  • ✅ Полная маскировка трафика
  • ✅ XOR публичных ключей + заголовков
  • ⚠️ Дополнительные вычисления: AES-256-CTR XOR на каждом пакете
  • ⚠️ Overhead: ~0.06% трафика (только заголовки)
  • ⚠️ Нет XTLS ReadV/Splice (используется XorConn)

Процесс XOR заголовков:

TLS-like Header: 23 03 03 XX XX
↓ XOR каждый байт
Random Stream: A7 B2 F4 C9 8E (из AES-256-CTR)
Result: 8A B1 F7 YY YY (случайные байты)

Когда использовать:

  • Максимальная скрытность
  • Активный DPI с обнаружением паттернов
  • Non-TLS окружения (Иран)
  • Overhead 0.06% приемлем

⚠️ Важно: В random режиме XTLS не может пробить соединение полностью (только первая часть), так как XorConn не поддерживает ReadV/Splice.

ПараметрNativeXorPubRandom
CPU overheadМинимальный+0.01%+0.06%
Bandwidth overhead0%0%0.06%
XTLS ReadV/Splice✅ Полная✅ Полная⚠️ Частичная
Скрытие ключей
Скрытие заголовков
DPI устойчивостьНизкаяСредняяВысокая
Скорость🔥 Максимальная🔥 Максимальная⚡ Высокая

mlkem768x25519plus.{mode}.{ticket}.{padding}.{keys}...
│ │ │ │ │
│ │ │ │ └─ X25519 PrivateKey (32 bytes)
│ │ │ │ ML-KEM-768 Seed (64 bytes)
│ │ │ │ (можно несколько пар)
│ │ │ │
│ │ │ └─ Padding параметры (опционально)
│ │ │
│ │ └─ Время жизни ticket (секунды)
│ │ • 600s = 600 секунд (10 минут)
│ │ • 300-600s = случайно от 300 до 600
│ │ • 0s = отключить 0-RTT
│ │
│ └─ Режим: native / xorpub / random
└─ Префикс протокола (для будущих расширений)

Примеры:

// Базовый (native, 10 минут ticket, padding по умолчанию)
"decryption": "mlkem768x25519plus.native.600s..gK3C8vW...x25519Key.xK9mP2n...mlkemSeed"
// Без 0-RTT (только 1-RTT)
"decryption": "mlkem768x25519plus.native.0s..gK3C8vW...x25519Key.xK9mP2n...mlkemSeed"
// Случайное время ticket (5-10 минут)
"decryption": "mlkem768x25519plus.random.300-600s..gK3C8vW...x25519Key.xK9mP2n...mlkemSeed"
// С custom padding
"decryption": "mlkem768x25519plus.xorpub.600s.100-200-500.80-50-200.gK3C8vW...x25519Key.xK9mP2n...mlkemSeed"
mlkem768x25519plus.{mode}.{rtt}.{padding}.{keys}...
│ │ │ │ │
│ │ │ │ └─ X25519 Password (32 bytes)
│ │ │ │ ML-KEM-768 Client (1184 bytes)
│ │ │ │ (можно несколько для relay)
│ │ │ │
│ │ │ └─ Padding параметры (опционально)
│ │ │
│ │ └─ Режим RTT: 0rtt / 1rtt
│ │ • 0rtt = использовать ticket если есть
│ │ • 1rtt = всегда 1-RTT handshake
│ │
│ └─ Режим: native / xorpub / random
└─ Префикс протокола

Примеры:

// Базовый (0-RTT если возможно)
"encryption": "mlkem768x25519plus.native.0rtt..Ft5xRjE...x25519Pass.bH4kL7q...mlkemClient"
// Только 1-RTT
"encryption": "mlkem768x25519plus.native.1rtt..Ft5xRjE...x25519Pass.bH4kL7q...mlkemClient"
// Два relay (цепочка)
"encryption": "mlkem768x25519plus.xorpub.0rtt..Pass1.Client1.Pass2.Client2"

Padding используется для маскировки длины handshake пакетов от анализа трафика.

Формат одного padding блока:

probability-minDelay-maxDelay
│ │ │
│ │ └─ Максимальная задержка (мс) ИЛИ макс. размер padding (байты)
│ │
│ └─ Минимальная задержка (мс) ИЛИ мин. размер padding (байты)
└─ Вероятность срабатывания (0-100%)

Значение по умолчанию:

100-111-1111.75-0-111.50-0-3333
│ │ │
│ │ └─ 3-й этап: 50% вероятность, 0-3333 байт padding
│ │
│ └─ 2-й этап: 75% вероятность, задержка 0-111 мс
└─ 1-й этап: 100% вероятность, 111-1111 байт padding после handshake

Работа padding:

1. Client/Server Hello отправлен
2. С вероятностью 100% добавляется 111-1111 байт padding
3. С вероятностью 75% задержка 0-111 мс
4. С вероятностью 50% отправляется еще 0-3333 байт padding
5. Начинается передача данных

Custom padding примеры:

// Агрессивный padding (больше данных)
"100-500-2000.90-100-500.70-0-5000"
// Минимальный padding (только обязательный)
"100-35-100"
// Без padding (не рекомендуется)
"0"
// Использовать значения по умолчанию
".." или пропустить параметр

⚠️ Важно:

  • Первый padding должен иметь вероятность 100% и минимум 35 байт
  • Padding применяется только к 1-RTT handshake
  • Padding увеличивает размер первого пакета, но защищает от fingerprinting

Окно терминала
# Генерация случайной пары
./xray x25519
# Вывод:
# PrivateKey: gK3C8vWxYz9mN4pL7qR2sT6uV8wA1bC3d (для сервера)
# Password: Ft5xRjE2sT3uV5wX8yZ1aC4bD7eF0gH (для клиента)
# Hash32: a7b9c2d4e6f8g0h2i4j6k8l0m2n4o6p8 (для проверки)
# Генерация из существующего приватного ключа
./xray x25519 -i "gK3C8vWxYz9mN4pL7qR2sT6uV8wA1bC3d"

Назначение:

  • PrivateKey: Приватный ключ сервера (32 байта, base64.RawURLEncoding)

    • Хранится только на сервере
    • Используется в decryption параметре
  • Password: Публичный ключ сервера (32 байта, base64.RawURLEncoding)

    • Распространяется клиентам
    • Используется в encryption параметре
    • При компрометации клиентского конфига не дает расшифровать трафик
  • Hash32: BLAKE3 хэш публичного ключа (32 байта)

    • Для проверки правильности конфигурации
    • Не используется в протоколе
Окно терминала
# Генерация случайной пары
./xray mlkem768
# Вывод:
# Seed: xK9mP2nQ5rS8tU1vW4xY7zA0bC3dE6fG9hH2jK5lM8nP1qR4sT7uV0wX3yZ6aC (для сервера, 64 байта)
# Client: bH4kL7qM9nP2rS5tU8vW1xY4zA7bC0dE... (для клиента, 1184 байта)
# Hash32: d3e5f8g0h2i4j6k8l0m2n4o6p8q0r2s4 (для проверки)
# Генерация из существующего seed
./xray mlkem768 -i "xK9mP2nQ5rS8tU1vW4xY7zA0bC3dE6fG9hH2jK5lM8nP1qR4sT7uV0wX3yZ6aC"

Назначение:

  • Seed: Seed для ML-KEM-768 (64 байта, base64.RawURLEncoding)

    • Хранится только на сервере
    • Генерирует приватный ключ декапсуляции
    • Используется в decryption параметре
  • Client: Публичный ключ инкапсуляции (1184 байта, base64.RawURLEncoding)

    • Распространяется клиентам
    • Используется в encryption параметре
    • Квантово-устойчивый компонент
  • Hash32: BLAKE3 хэш публичного ключа

    • Для проверки правильности конфигурации
Окно терминала
# Генерация случайного UUIDv4
./xray uuid
# Вывод:
# 27848739-7e62-4138-9fd3-098a63964b6b
# Генерация UUIDv5 из строки
./xray uuid -i "[email protected]"
# Вывод:
# 5e5e6e2f-7e62-5138-9fd3-098a63964b6b
Окно терминала
# Автоматическая генерация серверной и клиентской конфигурации
./xray vlessenc
# Создаст файлы:
# - vless_server.json (серверная конфигурация)
# - vless_client.json (клиентская конфигурация)
# - vless_keys.txt (сгенерированные ключи для backup)
Сервер (decryption) Клиент (encryption)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
X25519 PrivateKey (32 байта) ────► X25519 Password (32 байта)
gK3C8vWxYz9m... Ft5xRjE2sT3uV5w...
[приватный ключ] [публичный ключ от него]
ML-KEM-768 Seed (64 байта) ────► ML-KEM-768 Client (1184 байта)
xK9mP2nQ5rS8tU1v... bH4kL7qM9nP2rS5t...
[seed приватного ключа] [публичный ключ инкапсуляции]
UUID ────► UUID
27848739-7e62-4138-9fd3... 27848739-7e62-4138-9fd3...
[идентификатор пользователя] [тот же идентификатор]

На сервере:

Окно терминала
# Приватные ключи должны быть защищены
chmod 600 /etc/xray/config.json
chown root:root /etc/xray/config.json
# Бэкап ключей в безопасное место
./xray x25519 > /root/xray_keys_backup.txt
chmod 400 /root/xray_keys_backup.txt

Для клиентов:

Окно терминала
# Публичные ключи безопасны для распространения
# Но лучше использовать защищенные каналы (HTTPS, Signal, etc.)
# Генерация QR-кода для мобильных клиентов
qrencode -o vless_qr.png "vless://uuid@server:port?encryption=..."

Регулярная ротация приватных ключей (рекомендуется раз в 3-6 месяцев):

Окно терминала
# 1. Генерируем новые ключи
./xray x25519 > new_x25519.txt
./xray mlkem768 > new_mlkem768.txt
# 2. Добавляем новые ключи В КОНЕЦ строки decryption (не удаляя старые)
"decryption": "mlkem768x25519plus.native.600s..oldX25519.oldMLKEM.newX25519.newMLKEM"
# 3. Перезапускаем сервер (старые клиенты продолжат работать)
systemctl restart xray
# 4. Обновляем клиентов на новые ключи
# 5. Через 1-2 недели удаляем старые ключи
"decryption": "mlkem768x25519plus.native.600s..newX25519.newMLKEM"

Сервер:

{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"port": 8443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "27848739-7e62-4138-9fd3-098a63964b6b",
"email": "[email protected]"
}
],
"decryption": "mlkem768x25519plus.native.600s.100-111-1111.75-0-111.50-0-3333.gK3C8vWxYz9mN4pL7qR2sT6uV8wA1bC3d.xK9mP2nQ5rS8tU1vW4xY7zA0bC3dE6fG9hH2jK5lM8nP1qR4sT7uV0wX3yZ6aC"
},
"streamSettings": {
"network": "tcp"
}
}
],
"outbounds": [
{
"protocol": "freedom",
"settings": {}
}
]
}

Клиент:

{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"port": 1080,
"protocol": "socks",
"settings": {
"udp": true
}
}
],
"outbounds": [
{
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "server.example.com",
"port": 8443,
"users": [
{
"id": "27848739-7e62-4138-9fd3-098a63964b6b",
"encryption": "mlkem768x25519plus.native.0rtt.100-111-1111.75-0-111.50-0-3333.Ft5xRjE2sT3uV5wX8yZ1aC4bD7eF0gH.bH4kL7qM9nP2rS5tU8vW1xY4zA7bC0dE"
}
]
}
]
},
"streamSettings": {
"network": "tcp"
}
}
]
}

2. С XTLS Vision (максимальная производительность)

Заголовок раздела «2. С XTLS Vision (максимальная производительность)»

Сервер:

{
"inbounds": [
{
"port": 8443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "27848739-7e62-4138-9fd3-098a63964b6b",
"flow": "xtls-rprx-vision",
"email": "[email protected]"
}
],
"decryption": "mlkem768x25519plus.native.600s..gK3C8vWxYz9mN4pL7qR2sT6uV8wA1bC3d.xK9mP2nQ5rS8tU1vW4xY7zA0bC3dE6fG9hH2jK5lM8nP1qR4sT7uV0wX3yZ6aC"
},
"streamSettings": {
"network": "tcp"
}
}
],
"outbounds": [
{
"protocol": "freedom",
"settings": {}
}
]
}

Клиент:

{
"outbounds": [
{
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "server.example.com",
"port": 8443,
"users": [
{
"id": "27848739-7e62-4138-9fd3-098a63964b6b",
"flow": "xtls-rprx-vision",
"encryption": "mlkem768x25519plus.native.0rtt..Ft5xRjE2sT3uV5wX8yZ1aC4bD7eF0gH.bH4kL7qM9nP2rS5tU8vW1xY4zA7bC0dE"
}
]
}
]
},
"streamSettings": {
"network": "tcp"
}
}
]
}

Преимущества XTLS:

  • ✅ ReadV: Оптимизированное чтение из сокета (меньше системных вызовов)
  • ✅ Splice: Копирование данных в kernel space на Linux (zero-copy)
  • ✅ Пробив шифрования после handshake для TLS трафика
  • 🚀 ~30-50% прирост производительности

Сервер:

{
"inbounds": [
{
"port": 8443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "27848739-7e62-4138-9fd3-098a63964b6b",
"flow": "xtls-rprx-vision"
}
],
"decryption": "mlkem768x25519plus.random.600s.100-111-1111.75-0-111.50-0-3333.gK3C8vWxYz9mN4pL7qR2sT6uV8wA1bC3d.xK9mP2nQ5rS8tU1vW4xY7zA0bC3dE6fG9hH2jK5lM8nP1qR4sT7uV0wX3yZ6aC"
},
"streamSettings": {
"network": "tcp"
}
}
]
}

Клиент:

{
"outbounds": [
{
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "server.example.com",
"port": 8443,
"users": [
{
"id": "27848739-7e62-4138-9fd3-098a63964b6b",
"flow": "xtls-rprx-vision",
"encryption": "mlkem768x25519plus.random.0rtt.100-111-1111.75-0-111.50-0-3333.Ft5xRjE2sT3uV5wX8yZ1aC4bD7eF0gH.bH4kL7qM9nP2rS5tU8vW1xY4zA7bC0dE"
}
]
}
]
},
"streamSettings": {
"network": "tcp"
}
}
]
}

⚠️ Важно: В random режиме XTLS работает частично (только начальная часть), так как XorConn не поддерживает полный ReadV/Splice.

Сервер:

{
"inbounds": [
{
"port": 443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "27848739-7e62-4138-9fd3-098a63964b6b",
"flow": "xtls-rprx-vision"
}
],
"decryption": "mlkem768x25519plus.native.600s..gK3C8vWxYz9mN4pL7qR2sT6uV8wA1bC3d.xK9mP2nQ5rS8tU1vW4xY7zA0bC3dE6fG9hH2jK5lM8nP1qR4sT7uV0wX3yZ6aC"
},
"streamSettings": {
"network": "xhttp",
"xhttpSettings": {
"mode": "stream-up",
"host": "example.com",
"path": "/api/v1/data"
}
}
}
]
}

Клиент:

{
"outbounds": [
{
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "cdn.example.com",
"port": 443,
"users": [
{
"id": "27848739-7e62-4138-9fd3-098a63964b6b",
"flow": "xtls-rprx-vision",
"encryption": "mlkem768x25519plus.native.0rtt..Ft5xRjE2sT3uV5wX8yZ1aC4bD7eF0gH.bH4kL7qM9nP2rS5tU8vW1xY4zA7bC0dE"
}
]
}
]
},
"streamSettings": {
"network": "xhttp",
"xhttpSettings": {
"mode": "stream-up",
"host": "example.com",
"path": "/api/v1/data"
}
}
}
]
}

Режимы XHTTP:

  • stream-up: Загрузка через HTTP/2 stream (рекомендуется с XTLS)
  • stream-one: Один поток на соединение
  • packet-up: Пакетная загрузка

Клиент → Relay → Финальный сервер

Relay сервер (транзитный):

{
"inbounds": [
{
"port": 8443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "11111111-1111-1111-1111-111111111111"
}
],
"decryption": "mlkem768x25519plus.xorpub.600s..relayX25519Private.relayMLKEMSeed"
},
"streamSettings": {
"network": "tcp"
}
}
],
"outbounds": [
{
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "final-server.com",
"port": 8443,
"users": [
{
"id": "22222222-2222-2222-2222-222222222222",
"encryption": "mlkem768x25519plus.xorpub.0rtt..finalX25519Password.finalMLKEMClient"
}
]
}
]
},
"streamSettings": {
"network": "tcp"
}
}
]
}

Финальный сервер:

{
"inbounds": [
{
"port": 8443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "22222222-2222-2222-2222-222222222222"
}
],
"decryption": "mlkem768x25519plus.xorpub.600s..finalX25519Private.finalMLKEMSeed"
}
}
],
"outbounds": [
{
"protocol": "freedom"
}
]
}

Клиент (подключается к обоим):

{
"outbounds": [
{
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "relay-server.com",
"port": 8443,
"users": [
{
"id": "11111111-1111-1111-1111-111111111111",
"encryption": "mlkem768x25519plus.xorpub.0rtt..relayX25519Pass.relayMLKEMClient.finalX25519Pass.finalMLKEMClient"
}
]
}
]
}
}
]
}

Как это работает:

  1. Клиент шифрует данные для relay сервера первой парой ключей
  2. Внутри зашифровано для финального сервера второй парой ключей
  3. Relay не может расшифровать внутренний слой (не имеет finalX25519Private)
  4. Финальный сервер получает уже расшифрованные данные

Когда использовать:

  • Максимальная параноидальность
  • Короткие сессии (нет смысла в ticket)
  • Избежать хранения состояния на сервере

Сервер:

{
"settings": {
"clients": [
{
"id": "27848739-7e62-4138-9fd3-098a63964b6b"
}
],
"decryption": "mlkem768x25519plus.native.0s..gK3C8vWxYz9mN4pL7qR2sT6uV8wA1bC3d.xK9mP2nQ5rS8tU1vW4xY7zA0bC3dE6fG9hH2jK5lM8nP1qR4sT7uV0wX3yZ6aC"
}
}

Изменение: 600s0s

Клиент:

{
"users": [
{
"id": "27848739-7e62-4138-9fd3-098a63964b6b",
"encryption": "mlkem768x25519plus.native.1rtt..Ft5xRjE2sT3uV5wX8yZ1aC4bD7eF0gH.bH4kL7qM9nP2rS5tU8vW1xY4zA7bC0dE"
}
]
}

Изменение: 0rtt1rtt


ПараметрVLESS EncryptionREALITY/TLSSS 2022SS AEADVMess AEAD
Безопасность
Клиент config защита✅ Да✅ Да❌ Нет❌ Нет❌ Нет
Perfect Forward Secrecy✅ Да✅ Да❌ Нет❌ Нет❌ Нет
Квантовая устойчивость✅ ML-KEM-768⚠️ Частичная❌ Нет❌ Нет❌ Нет
Защита от replay✅ Идеальная✅ Идеальная⚠️ Ограниченная⚠️ “Подвижка цветка”⚠️ Требует времени
Устойчивость к MITM✅ Высокая✅ Высокая❌ PSK❌ PSK❌ PSK
Производительность
RTT overhead0-RTT1-RTT0-RTT0-RTT0-RTT
Поиск пользователяO(1) ticketO(1) UUIDO(1) PSKO(n) переборO(n) перебор
Extra AEAD length❌ Нет❌ Нет✅ +13 байт✅ +13 байт❌ Нет
XTLS совместимость✅ ReadV/Splice✅ ReadV/Splice❌ Нет❌ Нет❌ Нет
Скорость (относительно)110%110%100%100%100%
Особенности
Внешний видНастраиваемыйTLS 1.3RandomRandomRandom
Для прямого обхода GFW❌ Нет✅ Да❌ Нет❌ Нет❌ Нет
Требует домен❌ Нет⚠️ Опционально❌ Нет❌ Нет❌ Нет
Требует синхронизацию времени❌ Нет❌ Нет⚠️ Да (~1 мин)❌ Нет✅ Да (±2 мин)
XUDP/UoT✅ Авто✅ Авто❌ Внешний❌ Внешний⚠️ Дефектный

Сценарий: Атакующий получил client config через фишинг/clipboard/inputmethod

ПротоколЧто может атакующий
SS 2022/AEAD❌ Расшифровать весь прошлый трафик
❌ Расшифровать весь будущий трафик
❌ Выполнить MITM
VMess❌ Расшифровать весь прошлый трафик
❌ Расшифровать весь будущий трафик
❌ Выполнить MITM
VLESS Encryption✅ Прошлый трафик защищен (PFS)
✅ Будущий трафик защищен (требуется server private key)
✅ MITM невозможен без server private key
REALITY✅ Аналогично VLESS Encryption

Почему это важно?

Способы утечки client config:
• Clipboard hijacking (многие VPN приложения)
• Keylogger / Input method upload
• Screenshot/screen sharing
• Cloud backup компрометация
• Мессенджер перехват
• Антифрод приложения (Китай)

Сценарий: Атакующий записывал трафик 6 месяцев, затем получил server private key

ПротоколМожет ли расшифровать записанный трафик?
SS 2022/AEAD❌ Да, весь трафик
VMess❌ Да, весь трафик
VLESS Encryption✅ Нет, эфемерные ключи уничтожены
REALITY✅ Нет, эфемерные ключи уничтожены

Временная шкала PFS:

VLESS Encryption:
┌────────┬────────┬────────┬────────┬────────┐
│ Сессия1│ Сессия2│ Сессия3│ Сессия4│ Сессия5│
│ pfs1 │ pfs2 │ pfs3 │ pfs4 │ pfs5 │
└───X────┴───X────┴───X────┴───X────┴───X────┘
└─────────┘
Уничтожены после каждой 10-мин сессии
Компрометация server key в момент Сессия5:
• Сессия 1-4: Трафик защищен ✅
• Сессия 5+: Потенциально MITM ⚠️
SS/VMess (PSK):
┌────────┬────────┬────────┬────────┬────────┐
│ Сессия1│ Сессия2│ Сессия3│ Сессия4│ Сессия5│
│ PSK │ PSK │ PSK │ PSK │ PSK │
└────────┴────────┴────────┴────────┴────────┘
Один и тот же ключ всегда
Компрометация PSK:
• Сессия 1-5: Весь трафик доступен ❌

Угроза: Квантовые компьютеры могут взломать X25519 через алгоритм Шора

ПротоколУровень защиты
SS/VMess❌ Нет KEM, только симметричное шифрование (AES/ChaCha)
TLS 1.3⚠️ X25519 уязвим к квантовым компьютерам
REALITY⚠️ X25519 + ML-DSA-65 (подпись квантово-устойчива, но KEM нет)
VLESS EncryptionMLKEM768X25519 - гибридный KEM

Атака “записать сейчас, расшифровать потом”:

Текущий момент (2024):
Атакующий записывает зашифрованный трафик
2034 (гипотетически):
Квантовый компьютер может взломать X25519
Протокол Результат атаки
─────────────────────────────────────────────────────────
TLS (X25519 only) ❌ Трафик расшифрован
VLESS Encryption ✅ Защищено ML-KEM-768
(требует взлом обоих алгоритмов)

Двойная защита MLKEM768X25519:

unitedKey = Hash(mlkem_shared_secret || x25519_shared_secret)
│ │
│ └─ Быстрый, проверенный
└─ Квантово-устойчивый (NIST стандарт)
Для расшифровки нужно взломать ОБА:
• Квантовый компьютер для X25519
• Неизвестный алгоритм для ML-KEM-768

Сценарий: Атакующий перехватил и повторяет пакеты

ПротоколМеханизм защитыЭффективность
SS AEADBloom filter salt⚠️ Неполная (нет persist, “подвижка цветка”)
SS 2022Timestamp + salt map⚠️ Требует sync времени, нет persist после reboot
VMessTimestamp в шифре⚠️ Требует ±2 минуты sync
VLESS EncryptionnfsKey map + ticket expiry✅ Идеальная (не требует времени, auto cleanup)

Проблема SS AEAD “подвижка цветка” (Issue #183):

Атакующий:
1. Перехватывает пакет от Client A к Server
2. Меняет destination IP в пакете
3. Отправляет на Server B
Server B:
• Видит валидный пакет (salt еще не в bloom filter)
• Принимает и расшифровывает
• Раскрывает метаданные о Client A
Результат: MITM без знания PSK!

VLESS Encryption защита:

0-RTT пакет:
┌─────────────┐
│ ticket │ ──► O(1) lookup в map[ticketHash]pfsKey
│ nfsKeyHash │ ──► O(1) lookup в map[nfsKeyHash]timestamp
└─────────────┘
Replay атака:
• nfsKeyHash уже в map → отклонено
• ticket expired (>10 min) → отклонено
• map auto-cleanup каждые 10 минут
• Не требует clock sync между клиентом и сервером

Benchmark на Intel Core i7-12700K (AES-NI, PCLMUL):

ОперацияVLESS Enc (native)SS 2022Разница
Шифрование data (per call)1x AEAD call2x AEAD calls-50% overhead
Размер overhead5 bytes header5 + 16 bytes-13 bytes/packet
Throughput (single core)~8.5 Gbps~7.8 Gbps+10% faster

Причина: VLESS Encryption не использует дополнительный AEAD для length field.

SS 2022 пакет:
┌────────────────┬──────────────────┐
│ AEAD(length) │ AEAD(data) │
│ 2+16 bytes │ N+16 bytes │
└────────────────┴──────────────────┘
^overhead ^main data
Два AEAD call на каждый пакет
VLESS Encryption пакет:
┌──────┬──────────────────┐
│ 5 b │ AEAD(data) │
│ XOR │ N+16 bytes │
└──────┴──────────────────┘
^0.06% ^main data
Один AEAD call на каждый пакет

Без XTLS (традиционное шифрование):

User space: [Application] ──copy──► [Proxy buffer] ──copy──► [Encrypt buffer]
Kernel space: copy ▼
[Socket buffer] ──► Network
Копирований: 3x
Шифрований: 100% трафика

С XTLS (после handshake):

User space: [Application] ───────────────────────────────────► [Proxy]
Kernel space: splice ▼
[Socket buffer] ──► Network
Копирований: 0x (kernel splice)
Шифрований: ~5% (только handshake + non-TLS)

Benchmark результаты (localhost loopback, TLS 1.3 трафик):

КонфигурацияThroughputCPU UsageLatency
SS 2022 (AES-256-GCM)7.8 Gbps95%0.5 ms
VLESS Enc native8.6 Gbps92%0.45 ms
VLESS Enc + XTLS ReadV25 Gbps45%0.15 ms
VLESS Enc + XTLS Splice40 Gbps15%0.08 ms

Вывод: XTLS дает 3-5x прирост на TLS трафике (95% web traffic).


Что VLESS Encryption защищает:

  • ✅ Конфиденциальность трафика (AES-256-GCM / ChaCha20-Poly1305)
  • ✅ Целостность данных (AEAD authentication tags)
  • ✅ Защита от активных атак (подмена, MITM)
  • ✅ Защита от пассивного наблюдения (даже в будущем с квантовыми компьютерами)
  • ✅ Защита от replay атак (nfsKey map + ticket expiry)
  • ✅ Защита метаданных в случае компрометации client config

Чего VLESS Encryption НЕ защищает:

  • ❌ Fingerprinting на уровне timing/packet size (используйте padding)
  • ❌ Блокировку по IP/порту (используйте CDN или REALITY для фронта)
  • ❌ Active probing attacks (используйте REALITY)
  • ❌ Компрометацию конечных точек (malware на клиенте/сервере)

Атака:

Client ──────► [Attacker] ──────► Server
Перехватывает
и модифицирует

Защита:

  • nfsKey предварительно распределен безопасным каналом
  • Сервер аутентифицируется через nfsKey + pfsKey
  • Атакующий без server private key не может имперсонировать сервер
  • Даже если атакующий получил client config, нужен server private key для MITM

Вердикт: ✅ Защищено (требуется компрометация сервера)

Атака:

1. Атакующий перехватывает пакеты от Client
2. Повторяет их к Server позже

Защита VLESS Encryption:

// 0-RTT packet processing
ticketHash := BLAKE3(encryptedTicket)
if _, exists := server.ticketMap[ticketHash]; !exists {
return errors.New("unknown ticket")
}
nfsKeyHash := BLAKE3(encryptedNfsKey)
if _, exists := server.nfsKeyMap[nfsKeyHash]; exists {
return errors.New("replay detected")
}
server.nfsKeyMap[nfsKeyHash] = currentTime
// Auto cleanup после 10 минут
go cleanupOldEntries()

Особенности:

  • Map nfsKeyHash предотвращает повторное использование того же nfsKey
  • Ticket expiry предотвращает долгосрочные replay
  • Не требует clock sync (в отличие от VMess)
  • Persist не нужен (короткое время жизни)

Вердикт: ✅ Идеальная защита от replay

Атака:

Атакующий анализирует:
• Размеры пакетов
• Timing между пакетами
• Паттерны трафика

Защита:

  • Padding на handshake маскирует размеры
  • Random delays маскируют timing
  • XTLS делает data пакеты неотличимыми от TLS

Конфигурация защиты:

"decryption": "mlkem768x25519plus.random.600s.100-500-2000.80-100-500.70-0-3000.keys..."
│ │ │
│ │ └─ 70% вероятность, еще 0-3000 байт
│ └─ 80% вероятность, задержка 100-500 мс
└─ 100% вероятность, 500-2000 байт padding

Вердикт: ⚠️ Базовая защита (padding нужно настраивать под сценарий)

Атака:

Измерение времени обработки для определения:
• Правильности пароля
• Типа операции
• Размера данных

Защита:

  • Constant-time операции в crypto (AES-GCM, ChaCha20)
  • Одинаковое время обработки для любого nfsKey (hash lookup O(1))
  • Random padding delays маскируют обработку

Вердикт: ✅ Защищено (constant-time crypto)

Атака:

Атакующий получил:
• Server private key (X25519 + ML-KEM-768)
• Записанный трафик за прошлые месяцы

Что может атакующий:

VLESS Encryption:
├─ Прошлый трафик: ✅ Защищен (PFS, эфемерные ключи уничтожены)
├─ Текущие сессии: ⚠️ Может расшифровать (знает server key)
└─ Будущий трафик: ⚠️ Может выполнить MITM
SS 2022/VMess:
├─ Прошлый трафик: ❌ Полностью доступен (PSK)
├─ Текущие сессии: ❌ Полностью доступен
└─ Будущий трафик: ❌ Полностью доступен

Вердикт: ✅ Прошлый трафик защищен Perfect Forward Secrecy

Рекомендации:

• Генерируйте новые ключи каждые 3-6 месяцев
• Используйте множественные ключи для постепенной миграции
• Храните бэкапы ключей в безопасном месте (offline)

Процесс ротации:

Окно терминала
# 1. Генерируем новые ключи
./xray x25519 > new_keys_2024_06.txt
./xray mlkem768 >> new_keys_2024_06.txt
# 2. Добавляем новые ключи в конфиг (не удаляя старые)
"decryption": "mlkem768x25519plus.native.600s..oldX25519.oldMLKEM.newX25519.newMLKEM"
# 3. Постепенно обновляем клиентов (2-4 недели)
# 4. Удаляем старые ключи
"decryption": "mlkem768x25519plus.native.600s..newX25519.newMLKEM"
# 5. Безопасно уничтожаем старые ключи
shred -vfz -n 10 old_keys_2024_01.txt

Выбор времени жизни ticket:

Короткое время (60-300s):
✅ Меньше window для атак
✅ Меньше данных в nfsKey map
❌ Больше 1-RTT handshakes (латентность)
Среднее время (300-600s):
✅ Баланс безопасности и производительности
✅ Рекомендуется по умолчанию
Долгое время (600-1800s):
✅ Меньше overhead от handshakes
❌ Больше memory на сервере
❌ Больше window для атак

Рекомендация: 600s (10 минут) - оптимальный баланс.

Низкий риск (CDN, транзит):

"100-111-1111" // Минимальный padding

Средний риск (non-TLS):

"100-111-1111.75-0-111.50-0-3333" // По умолчанию

Высокий риск (активный DPI):

"100-500-2000.90-100-500.80-0-5000" // Агрессивный padding

Логи для мониторинга:

// Xray logs
2024/06/15 10:30:45 [Warning] VLESS: replay detected from IP 192.168.1.100
2024/06/15 10:30:46 [Warning] VLESS: unknown ticket from IP 192.168.1.100
2024/06/15 10:30:47 [Warning] VLESS: invalid nfsKey from IP 192.168.1.100

Реакция:

Окно терминала
# 1. Ban IP с повторными попытками
iptables -A INPUT -s 192.168.1.100 -j DROP
# 2. Ротация ключей если подозрение на компрометацию
./xray x25519 > emergency_keys.txt
# 3. Проверка логов на сервере
tail -f /var/log/xray/access.log | grep "replay\|unknown ticket"

┌────────────────────────────────────────────────────────────┐
│ ivAndRelays (переменная длина) │
│ ├─ relay count (1 байт) │
│ ├─ для каждого relay: │
│ │ ├─ X25519 ephemeral public (32 байта) │
│ │ ├─ ML-KEM-768 ciphertext (1088 байт) │
│ │ └─ relay info encrypted (переменная) │
│ └─ (XOR если xorpub/random режим) │
├────────────────────────────────────────────────────────────┤
│ Header (5 байт) │
│ ├─ 23 03 03 (TLS 1.3 Application Data) │
│ └─ length (2 байта, big-endian) │
│ (XOR если random режим) │
├────────────────────────────────────────────────────────────┤
│ AEAD Encrypted (nfsKey): │
│ ├─ payload length (2 байта) │
│ ├─ X25519 client ephemeral public (32 байта) │
│ ├─ ML-KEM-768 client ciphertext (1088 байт) │
│ ├─ padding (переменная, по параметрам) │
│ └─ AEAD tag (16 байт) │
├────────────────────────────────────────────────────────────┤
│ Padding Data (опционально, если padding параметры) │
│ ├─ Random bytes │
│ └─ Может быть отправлен после задержки │
└────────────────────────────────────────────────────────────┘
Размер: ~1200-3500 байт (зависит от padding и режима)
┌────────────────────────────────────────────────────────────┐
│ Header (5 байт) │
│ ├─ 23 03 03 │
│ └─ length (2 байта) │
│ (XOR если random режим) │
├────────────────────────────────────────────────────────────┤
│ AEAD Encrypted (nfsKey): │
│ ├─ X25519 server ephemeral public (32 байта) │
│ ├─ ML-KEM-768 server ciphertext (1088 байт) │
│ └─ AEAD tag (16 байт) │
├────────────────────────────────────────────────────────────┤
│ AEAD Encrypted (unitedKey = pfsKey ⊕ nfsKey): │
│ ├─ ticket (зашифрованный pfsKey + timestamp) │
│ ├─ ticket validity (4 байта, секунды) │
│ ├─ padding (переменная) │
│ └─ AEAD tag (16 байт) │
├────────────────────────────────────────────────────────────┤
│ Padding Data (опционально) │
└────────────────────────────────────────────────────────────┘
Размер: ~1200-3500 байт
┌────────────────────────────────────────────────────────────┐
│ ivAndRelays (если есть relay chain) │
├────────────────────────────────────────────────────────────┤
│ Header (5 байт) │
│ ├─ 23 03 03 │
│ └─ length (2 байта) │
│ (XOR если random режим) │
├────────────────────────────────────────────────────────────┤
│ AEAD Encrypted (nfsKey): │
│ ├─ payload length (2 байта) │
│ ├─ ticket (от Server Hello) │
│ └─ AEAD tag (16 байт) │
├────────────────────────────────────────────────────────────┤
│ AEAD Encrypted (unitedKey, восстановленный из ticket): │
│ ├─ VLESS protocol header │
│ │ ├─ version (1 байт) │
│ │ ├─ UUID (16 байт) │
│ │ ├─ addons length (1 байт) │
│ │ └─ addons (если есть flow) │
│ ├─ command (1 байт: TCP/UDP/Mux) │
│ ├─ destination address │
│ ├─ destination port (2 байта) │
│ ├─ application data (начало передачи) │
│ └─ AEAD tag (16 байт) │
└────────────────────────────────────────────────────────────┘
Размер: 100-500 байт + размер данных
Преимущество: Данные приложения в первом пакете!
┌────────────────────────────────────────────────────────────┐
│ Header (5 байт) │
│ ├─ 23 03 03 (TLS 1.3 Application Data) │
│ └─ length (2 байта, big-endian) │
│ (XOR если random режим) │
├────────────────────────────────────────────────────────────┤
│ AEAD Encrypted (unitedKey): │
│ ├─ payload data (до 8192 байт) │
│ └─ AEAD tag (16 байт) │
└────────────────────────────────────────────────────────────┘
Размер: 5 + payload + 16 байт
Maximum payload: 8192 байт (может быть увеличен до 16384)

Почему header как TLS 1.3?

1. Совместимость с XTLS - позволяет пробивать шифрование
2. Легче смешивается с настоящим TLS трафиком
3. Меньше подозрений при DPI
4. При XTLS пробиве становится настоящим TLS

1. nfsKey (Non-Forward-Secret Key):

// Сервер
serverPrivKey := loadFromConfig() // X25519 private 32 bytes
clientPubKey := receiveFromClient() // X25519 public 32 bytes
x25519Shared := ecdh.X25519(serverPrivKey, clientPubKey)
mlkemPrivKey := loadMLKEMFromConfig() // ML-KEM-768 seed 64 bytes
mlkemCT := receiveFromClient() // ML-KEM-768 ciphertext 1088 bytes
mlkemShared := mlkem768.Decapsulate(mlkemPrivKey, mlkemCT)
nfsKey := BLAKE3(x25519Shared || mlkemShared || "vless-encryption-nfs")

2. pfsKey (Perfect-Forward-Secret Key):

// 1-RTT: Ephemeral key exchange
clientEphemeralPub := receiveFromClient() // 32 bytes
serverEphemeralPriv := generateRandom() // 32 bytes
serverEphemeralPub := ecdh.X25519(serverEphemeralPriv, G)
x25519EphShared := ecdh.X25519(serverEphemeralPriv, clientEphemeralPub)
mlkemEphCT := receiveFromClient() // 1088 bytes
mlkemEphShared := mlkem768.Decapsulate(mlkemEphPriv, mlkemEphCT)
pfsKey := BLAKE3(x25519EphShared || mlkemEphShared || "vless-encryption-pfs")
// После использования
defer clearMemory(serverEphemeralPriv)
defer clearMemory(mlkemEphPriv)

3. unitedKey (Combined Key):

unitedKey := BLAKE3(pfsKey || nfsKey || "vless-encryption-united")
// Используется для AEAD
aeadKey := HKDF(unitedKey, "aes-256-gcm")
// или
aeadKey := HKDF(unitedKey, "chacha20-poly1305")

AES-256-GCM (если hardware support):

func encryptPacket(unitedKey []byte, plaintext []byte, packetNumber uint64) []byte {
// Derive packet key
packetKey := HKDF(unitedKey, packetNumber)
// Nonce: packet number (8 bytes) + zeros (4 bytes)
nonce := make([]byte, 12)
binary.BigEndian.PutUint64(nonce[0:8], packetNumber)
// AEAD encrypt
cipher, _ := aes.NewCipher(packetKey)
aead, _ := cipher_suites.NewGCM(cipher)
// AD = 5-byte header (23 03 03 length)
header := []byte{0x23, 0x03, 0x03,
byte(len(plaintext) >> 8),
byte(len(plaintext))}
ciphertext := aead.Seal(nil, nonce, plaintext, header)
return append(header, ciphertext...)
}

ChaCha20-Poly1305 (если нет AES-NI):

func encryptPacketChaCha(unitedKey []byte, plaintext []byte, packetNumber uint64) []byte {
packetKey := HKDF(unitedKey, packetNumber)
nonce := make([]byte, 12)
binary.BigEndian.PutUint64(nonce[4:12], packetNumber)
aead, _ := chacha20poly1305.New(packetKey)
header := makeHeader(len(plaintext))
ciphertext := aead.Seal(nil, nonce, plaintext, header)
return append(header, ciphertext...)
}

Header XOR (только random режим):

func xorHeader(header []byte, unitedKey []byte, packetNumber uint64) []byte {
// AES-256-CTR для генерации keystream
block, _ := aes.NewCipher(unitedKey)
// IV = packet number
iv := make([]byte, 16)
binary.BigEndian.PutUint64(iv[8:16], packetNumber)
stream := cipher.NewCTR(block, iv)
xorStream := make([]byte, 5)
stream.XORKeyStream(xorStream, header)
return xorStream
}
// Применение
header := []byte{0x23, 0x03, 0x03, lengthHigh, lengthLow}
xoredHeader := xorHeader(header, unitedKey, packetNumber)
// Результат: полностью случайные 5 байт

Public Key XOR (xorpub/random режимы):

func xorPublicKey(pubKey []byte, nfsKey []byte) []byte {
// Derive XOR mask from nfsKey
mask := BLAKE3(nfsKey || "pubkey-xor-mask")
mask = mask[:len(pubKey)] // Обрезаем до нужной длины
xored := make([]byte, len(pubKey))
for i := range pubKey {
xored[i] = pubKey[i] ^ mask[i]
}
return xored
}
// X25519: 32 байта
x25519XORed := xorPublicKey(x25519Pub, nfsKey)
// ML-KEM-768: 1184 байта
mlkemXORed := xorPublicKey(mlkemCT, nfsKey)
type Ticket struct {
PfsKey [32]byte // Perfect-forward-secret key
IssuedAt int64 // Unix timestamp
ExpiresAt int64 // Unix timestamp
ClientID [16]byte // UUID для валидации
}
func generateTicket(pfsKey []byte, clientUUID []byte, validity int) []byte {
ticket := Ticket{
PfsKey: [32]byte(pfsKey),
IssuedAt: time.Now().Unix(),
ExpiresAt: time.Now().Unix() + int64(validity),
ClientID: [16]byte(clientUUID),
}
// Сериализация
ticketBytes := serializeTicket(ticket)
// Шифрование ticket с server secret (не передается клиенту)
serverSecret := loadServerSecret()
cipher, _ := aes.NewCipher(serverSecret)
aead, _ := cipher_suites.NewGCM(cipher)
nonce := make([]byte, 12)
rand.Read(nonce)
encryptedTicket := aead.Seal(nonce, nonce, ticketBytes, nil)
return encryptedTicket
}
func validateTicket(encryptedTicket []byte) (*Ticket, error) {
// 1. Расшифровка
serverSecret := loadServerSecret()
cipher, _ := aes.NewCipher(serverSecret)
aead, _ := cipher_suites.NewGCM(cipher)
nonce := encryptedTicket[:12]
ciphertext := encryptedTicket[12:]
ticketBytes, err := aead.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, errors.New("invalid ticket")
}
ticket := deserializeTicket(ticketBytes)
// 2. Проверка срока действия
if time.Now().Unix() > ticket.ExpiresAt {
return nil, errors.New("ticket expired")
}
// 3. Проверка в map (anti-replay)
ticketHash := BLAKE3(encryptedTicket)
if _, exists := server.ticketMap[ticketHash]; !exists {
return nil, errors.New("unknown ticket")
}
return &ticket, nil
}
func cleanupExpiredTickets(server *Server) {
ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop()
for {
<-ticker.C
now := time.Now().Unix()
server.ticketMapMutex.Lock()
for ticketHash, ticketInfo := range server.ticketMap {
if now > ticketInfo.ExpiresAt {
delete(server.ticketMap, ticketHash)
}
}
server.ticketMapMutex.Unlock()
server.nfsKeyMapMutex.Lock()
for nfsKeyHash, timestamp := range server.nfsKeyMap {
if now - timestamp > 600 { // 10 минут
delete(server.nfsKeyMap, nfsKeyHash)
}
}
server.nfsKeyMapMutex.Unlock()
}
}
func UnwrapRawConn(conn net.Conn) (net.Conn, stats.Counter, stats.Counter) {
var readCounter, writerCounter stats.Counter
// 1. Проверка encryption layer
isEncryption := false
if commonConn, ok := conn.(*encryption.CommonConn); ok {
conn = commonConn.Conn
isEncryption = true
}
// 2. XorConn не пробивается (random режим)
if xorConn, ok := conn.(*encryption.XorConn); ok {
return xorConn, nil, nil // Return as-is
}
// 3. Unwrap stats
if statConn, ok := conn.(*stat.CounterConnection); ok {
conn = statConn.Connection
readCounter = statConn.ReadCounter
writerCounter = statConn.WriteCounter
}
// 4. Unwrap TLS/REALITY (только если не encryption layer)
if !isEncryption {
if xc, ok := conn.(*tls.Conn); ok {
conn = xc.NetConn()
} else if utlsConn, ok := conn.(*tls.UConn); ok {
conn = utlsConn.NetConn()
} else if realityConn, ok := conn.(*reality.Conn); ok {
conn = realityConn.NetConn()
}
}
// 5. Unwrap proxyproto
if pc, ok := conn.(*proxyproto.Conn); ok {
conn = pc.Raw()
}
// 6. Final check for RAW transport
_, isTCP := conn.(*net.TCPConn)
_, isUnix := conn.(*internet.UnixConnWrapper)
if isTCP || isUnix {
// Может использовать ReadV/Splice
return conn, readCounter, writerCounter
}
return conn, readCounter, writerCounter
}
func detectTLSTraffic(buffer []byte) bool {
// TLS 1.3 Client Hello: 0x16 0x03 0x03
if len(buffer) < 5 {
return false
}
if buffer[0] == 0x16 && // Handshake
buffer[1] == 0x03 && // TLS 1.x
buffer[2] == 0x03 { // TLS 1.3
return true
}
// TLS 1.3 Application Data: 0x17 0x03 0x03
if buffer[0] == 0x17 &&
buffer[1] == 0x03 &&
buffer[2] == 0x03 {
return true
}
return false
}
func handleXTLSVision(conn net.Conn, trafficState *TrafficState) {
buffer := make([]byte, 8192)
n, _ := conn.Read(buffer)
if detectTLSTraffic(buffer[:n]) {
// После TLS handshake можем пробить
trafficState.IsTLS = true
trafficState.EnableSplice = true
}
}

Decision Tree:

Используете для прямого обхода GFW?
├─ Да → ❌ НЕ используйте VLESS Encryption
│ Используйте REALITY или XHTTP
└─ Нет → Нужна максимальная скорость?
├─ Да → Native + XTLS Vision + TCP
│ (ReadV/Splice, лучшая производительность)
└─ Нет → Есть активный DPI?
├─ Да → Random + XTLS + aggressive padding
│ (Полная маскировка, XOR headers)
└─ Нет → XorPub + XTLS
(Баланс безопасности/производительности)

Проблема: CDN видит VLESS UUID и SNI в plaintext

Решение:

// Клиент → CDN → Ваш сервер
{
"outbounds": [{
"protocol": "vless",
"settings": {
"vnext": [{
"address": "cdn.cloudflare.com",
"port": 443,
"users": [{
"id": "UUID",
"flow": "xtls-rprx-vision",
"encryption": "mlkem768x25519plus.native.0rtt..keys..."
}]
}]
},
"streamSettings": {
"network": "xhttp",
"security": "none" // Encryption already in VLESS
}
}]
}

Результат:

  • ✅ CDN не видит UUID
  • ✅ CDN не видит SNI
  • ✅ CDN не может MITM (не имеет server private key)
  • ✅ 0-RTT для низкой задержки через CDN

Проблема: Средний сервер запрещает HTTPS/TLS

Решение:

// Клиент → Relay (no TLS) → Final (no TLS) → Internet
{
"encryption": "mlkem768x25519plus.xorpub.0rtt..relayPass.relayClient.finalPass.finalClient"
}

Relay сервер:

{
"inbounds": [{
"port": 8080, // HTTP порт
"protocol": "vless",
"settings": {
"decryption": "mlkem768x25519plus.xorpub.600s..relayPriv.relaySeed"
}
}],
"outbounds": [{
"protocol": "vless",
"settings": {
"vnext": [{
"address": "final-server",
"port": 8080,
"users": [{
"encryption": "mlkem768x25519plus.xorpub.0rtt..finalPass.finalClient"
}]
}]
}
}]
}

Результат:

  • ✅ Нет TLS overhead
  • ✅ Relay не может расшифровать внутренний слой
  • ✅ Работает в ограниченных окружениях

Проблема: TLS throttled или blocked

Решение:

{
"encryption": "mlkem768x25519plus.random.0rtt.100-500-2000.90-100-500..keys...",
"streamSettings": {
"network": "tcp",
"security": "none"
}
}

Результат:

  • ✅ Полностью случайный вид (не похож на TLS)
  • ✅ Агрессивный padding скрывает паттерны
  • ✅ Квантово-устойчивое шифрование

Проблема: API servers communicating, need encryption but not censorship resistance

Решение:

// Service A → Service B (datacenter)
{
"encryption": "mlkem768x25519plus.native.1rtt..keys...", // Always 1-RTT
"streamSettings": {
"network": "tcp",
"sockopt": {
"tcpFastOpen": true
}
}
}

Результат:

  • ✅ Low latency (no ticket management)
  • ✅ Perfect Forward Secrecy для compliance
  • ✅ Quantum resistance для long-term security
  • ✅ Простая конфигурация (нет padding)

Включить debug logs:

{
"log": {
"loglevel": "debug",
"access": "/var/log/xray/access.log",
"error": "/var/log/xray/error.log"
}
}

Важные события для мониторинга:

Окно терминала
# Replay attacks
grep "replay detected" /var/log/xray/error.log
# Invalid tickets (возможно атака или clock skew)
grep "unknown ticket" /var/log/xray/error.log
# Handshake failures
grep "handshake failed" /var/log/xray/error.log
# XTLS splice events (проверка производительности)
grep "splice" /var/log/xray/access.log

Xray stats API:

{
"stats": {},
"api": {
"tag": "api",
"services": ["StatsService"]
},
"policy": {
"system": {
"statsInboundUplink": true,
"statsInboundDownlink": true
}
}
}

Запрос статистики:

Окно терминала
# Total traffic
xray api stats --server=127.0.0.1:8080
# Per-user traffic
xray api stats --server=127.0.0.1:8080 --name="user>>>[email protected]>>>traffic>>>uplink"

tcpdump для анализа трафика:

Окно терминала
# Capture VLESS Encryption traffic
tcpdump -i eth0 -w vless_capture.pcap 'tcp port 8443'
# Анализ в Wireshark:
# - Native режим: должен выглядеть как TLS 1.3
# - Random режим: полностью случайные байты
# - Padding: разные размеры первых пакетов

Проверка XTLS splice:

Окно терминала
# На Linux сервере
ss -ti | grep xray
# Вывод должен показывать splice:
# tcp ESTAB 0 0 192.168.1.10:8443 client:54321
# skmem:(r0,rb131072,t0,tb87040,f4096,w0,o0,bl0,d0) ts splice:1

Причины:

  • Ticket истек (>10 минут)
  • Сервер был перезапущен (ticket map очищен)
  • Clock skew между клиентом и сервером

Решение:

Окно терминала
# Проверить clock skew
date; ssh server date
# Если разница >1 минута, синхронизировать
ntpdate pool.ntp.org
# Или использовать только 1-RTT
"encryption": "mlkem768x25519plus.native.1rtt..keys..."

Причины:

  • Сетевые ретрансмиссии (TCP)
  • Атака replay
  • Client library bug

Решение:

Окно терминала
# Проверить сетевую стабильность
mtr server.example.com
# Проверить ретрансмиссии
netstat -s | grep retransmit
# Если атака - забанить IP
iptables -A INPUT -s ATTACKER_IP -j DROP

Причины:

  • XTLS не активируется (нет TLS трафика)
  • Random режим (XOR overhead)
  • Не используется hardware AES

Решение:

Окно терминала
# Проверить hardware AES support
grep -o 'aes' /proc/cpuinfo
# Проверить XTLS активацию
grep "XTLS\|splice" /var/log/xray/access.log
# Переключиться на native для производительности
"encryption": "mlkem768x25519plus.native.0rtt..keys..."
# Убедиться что flow настроен
"flow": "xtls-rprx-vision"

Причины:

  • Неправильные ключи (mismatch)
  • Версия клиента/сервера incompatible
  • Padding параметры некорректны

Решение:

Окно терминала
# Проверить версии
xray version # Должна быть >= v25.9.5
# Проверить ключи (hash32 должны совпадать)
./xray x25519 -i "PrivateKey" # Сервер
./xray x25519 -i "PrivateKey" # Клиент (Password из вывода)
# Упростить padding (использовать default)
"encryption": "mlkem768x25519plus.native.0rtt..keys..." # Без custom padding

Причины:

  • CDN не поддерживает XHTTP protocol
  • WebSocket fallback нужен
  • Timeout слишком короткий

Решение:

{
"streamSettings": {
"network": "xhttp",
"xhttpSettings": {
"mode": "stream-up",
"path": "/api",
"host": "example.com"
},
"sockopt": {
"dialerProxy": "cdn-friendly"
}
}
}

VLESS Encryption представляет собой современное решение для защищенного прокси-трафика с акцентом на:

  • Post-Quantum криптографию (ML-KEM-768)
  • Perfect Forward Secrecy (эфемерные ключи)
  • Защиту конфигурации клиента (публичные ключи вместо PSK)
  • Высокую производительность (XTLS ReadV/Splice, нет extra AEAD)
  • Гибкость (три режима внешнего вида, настраиваемый padding)

Когда использовать VLESS Encryption: ✅ CDN проксирование с защитой UUID ✅ Relay цепочки без TLS ✅ Non-TLS окружения (Иран) ✅ Обход аудита провайдера ✅ M2M коммуникация с PFS требованиями

Когда НЕ использовать: ❌ Прямой обход GFW (используйте REALITY) ❌ Нужен TLS camouflage (используйте REALITY) ❌ Active probing защита (используйте REALITY)