You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

14 KiB

Протокол VMess

VMess - это зашифрованный транспортный протокол, который может служить мостом между клиентом и сервером Xray.

Версия

Текущая версия протокола - 1.

Зависимости

Базовый протокол

VMess - это протокол, основанный на TCP, все данные передаются по TCP.

Идентификатор пользователя

ID эквивалентен UUID - это 16-байтовое случайное число, которое действует как токен. ID выглядит следующим образом: de305d54-75b4-431b-adb2-eb6b9e546014, он практически полностью случаен и может быть сгенерирован с помощью любого генератора UUID, например этого.

Идентификатор пользователя можно указать в файле конфигурации.

Функции

  • MD5: функция MD5
    • Входные данные: массив байтов произвольной длины
    • Выходные данные: массив из 16 байтов
  • HMAC: функция HMAC
    • Входные данные:
      • H: хэш-функция
      • K: ключ, массив байтов произвольной длины
      • M: сообщение, массив байтов произвольной длины
  • Shake: функция SHA3-Shake128
    • Входные данные: строка произвольной длины
    • Выходные данные: строка произвольной длины

Процесс коммуникации

VMess - это протокол без сохранения состояния, то есть клиент и сервер могут передавать данные напрямую без рукопожатия, и каждая передача данных не влияет на предыдущие или последующие передачи.

Клиент VMess отправляет запрос, а сервер проверяет, исходит ли этот запрос от легитимного клиента. Если проверка пройдена, сервер пересылает запрос и отправляет полученный ответ клиенту.

VMess использует асимметричный формат, то есть запрос, отправляемый клиентом, и ответ сервера имеют разные форматы.

Запрос клиента

16 байт X байт Оставшаяся часть
Информация для аутентификации Часть с командой Часть с данными

Информация для аутентификации

Информация для аутентификации - это 16-байтовое хэш-значение, которое вычисляется следующим образом:

  • H = MD5
  • K = идентификатор пользователя (16 байт)
  • M = время UTC с точностью до секунды, случайное значение в диапазоне ±30 секунд от текущего времени (8 байт, Big Endian)
  • Hash = HMAC(H, K, M)

Часть с командой

Часть с командой шифруется с помощью AES-128-CFB:

  • Ключ: MD5(идентификатор пользователя + []byte('c48619fe-8f02-49e0-b9e9-edf763e17e21'))
  • Вектор инициализации: MD5(X + X + X + X), X = []byte(время генерации информации для аутентификации) (8 байт, Big Endian)
1 байт 16 байт 16 байт 1 байт 1 байт 4 бита 4 бита 1 байт 1 байт 2 байта 1 байт N байт P байт 4 байта
Номер версии Ver Вектор инициализации для шифрования данных Ключ для шифрования данных Аутентификация ответа V Опция Opt Остаток P Метод шифрования Sec Зарезервировано Команда Cmd Порт Port Тип адреса T Адрес A Случайные данные Контрольная сумма F

Подробности опции Opt: (если бит равен 1, опция включена)

0 1 2 3 4 5 6 7
X X X X X M R S

Где:

  • Номер версии Ver: всегда равен 1;
  • Вектор инициализации для шифрования данных: случайное значение;
  • Ключ для шифрования данных: случайное значение;
  • Аутентификация ответа V: случайное значение;
  • Опция Opt:
    • S (0x01): стандартный формат потока данных (рекомендуется включать);
    • R (0x02): клиент ожидает повторного использования TCP-соединения (устарело в Xray 2.23+);
      • Действительна только при включенной опции S;
    • M (0x04): включить обфускацию метаданных (рекомендуется включать);
      • Действительна только при включенной опции S;
      • Если эта опция включена, клиент и сервер должны создать два экземпляра Shake: RequestMask = Shake(вектор инициализации для шифрования данных запроса), ResponseMask = Shake(вектор инициализации для шифрования данных ответа).
    • X: зарезервировано
  • Остаток P: добавить P байт случайных данных перед контрольной суммой;
  • Метод шифрования: указывает метод шифрования для части с данными, возможные значения:
    • 0x00: AES-128-CFB;
    • 0x01: без шифрования;
    • 0x02: AES-128-GCM;
    • 0x03: ChaCha20-Poly1305;
  • Команда Cmd:
    • 0x01: данные TCP;
    • 0x02: данные UDP;
  • Порт Port: номер порта в формате Big Endian;
  • Тип адреса T:
    • 0x01: IPv4
    • 0x02: доменное имя
    • 0x03: IPv6
  • Адрес A:
    • Если T = 0x01, A - это 4-байтовый адрес IPv4;
    • Если T = 0x02, A - это 1 байт длины (L) + L байт доменного имени;
    • Если T = 0x03, A - это 16-байтовый адрес IPv6;
  • Контрольная сумма F: хэш FNV1a всей части с командой, кроме F;

Часть с данными

Если Opt(S) включена, для части с данными используется следующий формат. Фактические данные запроса разбиваются на несколько блоков, каждый из которых имеет следующий формат. После проверки всех блоков сервер пересылает их в соответствии с базовым форматом.

2 байта L байт
Длина L Пакет данных

Где:

  • Длина L: целое число в формате Big Endian, максимальное значение 2^14;
    • Если Opt(M) включена, значение L = истинное значение xor Mask. Mask = (RequestMask.NextByte() << 8) + RequestMask.NextByte();
  • Пакет данных: пакет данных, зашифрованный указанным методом шифрования;

До завершения передачи в пакете данных должны быть фактические данные, то есть данные, отличные от длины и данных аутентификации. При завершении передачи клиент должен отправить пустой пакет данных, то есть L = 0 (без шифрования) или длину данных аутентификации (с шифрованием), чтобы сигнализировать о завершении передачи.

Формат пакета данных зависит от метода шифрования:

  • Без шифрования:
    • L байт: фактические данные;
  • AES-128-CFB: вся часть с данными шифруется с помощью AES-128-CFB
    • 4 байта: хэш FNV1a фактических данных;
    • L - 4 байта: фактические данные;
  • AES-128-GCM: ключ - это ключ из части с командой, вектор инициализации = count (2 байта) + IV (10 байт). count начинается с 0 и увеличивается на 1 для каждого пакета данных; IV - это байты с 3 по 12 из вектора инициализации части с командой.
    • L - 16 байт: фактические данные;
    • 16 байт: данные аутентификации GCM
  • ChaCha20-Poly1305: ключ = MD5(ключ из части с командой) + MD5(MD5(ключ из части с командой)), вектор инициализации = count (2 байта) + IV (10 байт). count начинается с 0 и увеличивается на 1 для каждого пакета данных; IV - это байты с 3 по 12 из вектора инициализации части с командой.
    • L - 16 байт: фактические данные;
    • 16 байт: данные аутентификации Poly1305

Ответ сервера

Данные заголовка ответа шифруются с помощью AES-128-CFB, вектор инициализации - MD5(вектор инициализации для шифрования данных), ключ - MD5(ключ для шифрования данных). Фактические данные ответа зависят от настроек шифрования.

1 байт 1 байт 1 байт 1 байт M байт Оставшаяся часть
Аутентификация ответа V Опция Opt Команда Cmd Длина команды M Содержимое команды Фактические данные ответа

Где:

  • Аутентификация ответа V: должна совпадать с аутентификацией ответа V в запросе клиента;
  • Опция Opt:
    • 0x01: сервер готов повторно использовать TCP-соединение (устарело в Xray 2.23+);
  • Команда Cmd:
    • 0x01: команда динамического порта
  • Фактические данные ответа:
    • Если Opt(S) в запросе включена, используется стандартный формат, в противном случае используется базовый формат.
    • Формат такой же, как и у данных запроса.
      • Если Opt(M) включена, значение длины L = истинное значение xor Mask. Mask = (ResponseMask.NextByte() << 8) + ResponseMask.NextByte();

Команда динамического порта

1 байт 2 байта 16 байт 2 байта 1 байт 1 байт
Зарезервировано Порт Port Идентификатор пользователя AlterID Уровень пользователя Время действия T

Где:

  • Порт Port: номер порта в формате Big Endian;
  • Время действия T: количество минут;

Когда клиент получает команду динамического порта, сервер уже открыл новый порт для связи, и клиент может отправлять данные на этот новый порт. Через T минут этот порт станет недействительным, и клиент должен будет снова использовать основной порт для связи.

Примечания

  • Для обеспечения обратной совместимости все зарезервированные поля должны иметь значение 0.