176 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Markdown
		
	
	
			
		
		
	
	
			176 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Markdown
		
	
	
# Протокол VMess
 | 
						||
 | 
						||
VMess - это зашифрованный транспортный протокол, который может служить мостом между клиентом и сервером Xray.
 | 
						||
 | 
						||
## Версия
 | 
						||
 | 
						||
Текущая версия протокола - 1.
 | 
						||
 | 
						||
## Зависимости
 | 
						||
 | 
						||
### Базовый протокол
 | 
						||
 | 
						||
VMess - это протокол, основанный на TCP, все данные передаются по TCP.
 | 
						||
 | 
						||
### Идентификатор пользователя
 | 
						||
 | 
						||
ID эквивалентен [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier) - это 16-байтовое случайное число, которое действует как токен.
 | 
						||
ID выглядит следующим образом: de305d54-75b4-431b-adb2-eb6b9e546014, он практически полностью случаен и может быть сгенерирован с помощью любого генератора UUID, например [этого](https://www.uuidgenerator.net/).
 | 
						||
 | 
						||
Идентификатор пользователя можно указать в [файле конфигурации](../../config).
 | 
						||
 | 
						||
### Функции
 | 
						||
 | 
						||
- MD5: функция [MD5](https://en.wikipedia.org/wiki/MD5)
 | 
						||
  - Входные данные: массив байтов произвольной длины
 | 
						||
  - Выходные данные: массив из 16 байтов
 | 
						||
- HMAC: функция [HMAC](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code)
 | 
						||
  - Входные данные:
 | 
						||
    - H: хэш-функция
 | 
						||
    - K: ключ, массив байтов произвольной длины
 | 
						||
    - M: сообщение, массив байтов произвольной длины
 | 
						||
- Shake: функция [SHA3-Shake128](https://en.wikipedia.org/wiki/SHA-3)
 | 
						||
  - Входные данные: строка произвольной длины
 | 
						||
  - Выходные данные: строка произвольной длины
 | 
						||
 | 
						||
## Процесс коммуникации
 | 
						||
 | 
						||
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.
 |