# VMess Protocol

VMess is an encrypted transmission protocol that can serve as a bridge between the Xray client and server.

## Version

The current version number is 1.

## Dependencies

### Underlying Protocol

VMess is a TCP-based protocol where all data is transmitted over TCP.

### User ID

An ID is equivalent to a [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier), which is a 16-byte long random number. Its function is similar to a token. An ID looks like: `de305d54-75b4-431b-adb2-eb6b9e546014`, it is almost entirely random and can be generated using any UUID generator, such as [this one](https://www.uuidgenerator.net/).

User ID can be specified in the [configuration file](../../config).

### Functions

- MD5: [MD5 Function](https://en.wikipedia.org/wiki/MD5)
  - Input parameter is any length byte array
  - Output is a 16-byte array
- HMAC: [HMAC Function](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code)
  - Input parameters are:
    - H: Hash function
    - K: Key, any length byte array
    - M: Message, any length byte array
- Shake: [SHA3-Shake128 Function](https://en.wikipedia.org/wiki/SHA-3)
  - Input parameter is any length string
  - Output is any length string

## Communication Process

VMess is a stateless protocol, which means that data can be transmitted directly between the client and the server without the need for a handshake. Each data transmission has no impact on other data transmissions before or after it.

When a VMess client initiates a request, the server checks whether the request comes from a legitimate client. If the validation passes, the server forwards the request and sends the obtained response back to the client.

VMess uses an asymmetric format, meaning that the requests sent by the client and the responses from the server use different formats.

## Client Request

| 16 Bytes                   | X Bytes          | Remaining |
| -------------------------- | ---------------- | --------- |
| Authentication Information | Instruction Part | Data Part |

### Authentication Information

The authentication information is a 16-byte hash (hash) value, which is calculated as follows:

- H = MD5
- K = User ID (16 bytes)
- M = UTC time accurate to seconds, with a random value of ±30 seconds from the current time (8 bytes, Big Endian)
- Hash = HMAC(H, K, M)

### Command Section

The instruction part is encrypted using AES-128-CFB.

- Key: MD5(user ID + []byte('c48619fe-8f02-49e0-b9e9-edf763e17e21'))
- IV: MD5(X + X + X + X), X = []byte(time generated by authentication information) (8 bytes, Big Endian)

| 1 Byte  |      16 Bytes      |      16 Bytes       |            1 Byte             | 1 Byte  |  4 bits  |      4 bits       |  1 Byte  | 1 Byte  | 2 Bytes |    1 Byte    | N Bytes |   P Bytes    | 4 Bytes  |
| :-----: | :----------------: | :-----------------: | :---------------------------: | :-----: | :------: | :---------------: | :------: | :-----: | :-----: | :----------: | :-----: | :----------: | :------: |
| Version | Data Encryption IV | Data Encryption Key | Response Authentication Value | Options | Reserved | Encryption Method | Reserved | Command |  Port   | Address Type | Address | Random Value | Checksum |

Options Opt Details: (When a bit is 1, it means the option is enabled)

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

of which:

- Version Number Ver: Always 1;
- Data Encryption IV: Random value;
- Data Encryption Key: Random value;
- Response Authentication V: Random value;
- Option Opt:
  - S (0x01): Standard format data stream (recommended);
  - R (0x02): Client expects to reuse TCP connection (deprecated in Xray 2.23+);
    - This item only takes effect when S is enabled;
  - M (0x04): Enable metadata obfuscation (recommended);
    - This item only takes effect when S is enabled;
    - When this item is enabled, the client and server need to construct two Shake instances respectively, RequestMask = Shake (request data IV), ResponseMask = Shake (response data IV).
  - X: Reserved
- Redundancy P: Random value added before checksum value;
- Encryption Method: Specify the encryption method for the data part, and the optional values are:
  - 0x00: AES-128-CFB;
  - 0x01: No encryption;
  - 0x02: AES-128-GCM;
  - 0x03: ChaCha20-Poly1305;
- Instruction Cmd:
  - 0x01: TCP data;
  - 0x02: UDP data;
- Port Port: Integer port number in Big Endian format;
- Address Type T:
  - 0x01: IPv4
  - 0x02: Domain name
  - 0x03: IPv6
- Address A:
  - When T = 0x01, A is a 4-byte IPv4 address;
  - When T = 0x02, A is a 1-byte length (L) + L-byte domain name;
  - When T = 0x03, A is a 16-byte IPv6 address;
- Check F: FNV1a hash of all content in the instruction except F.

### Data Section

When Opt(S) is enabled, this format is used for the data section. The actual request data is divided into several small chunks, and each chunk has the following format. After the server verifies all the small chunks, it will be forwarded in the basic format.

| 2 Bytes  |   L Bytes   |
| :------: | :---------: |
| Length L | Data Packet |

in which:

- Length L: A big-endian integer with a maximum value of 2^14.
  - When Opt(M) is enabled, the value of L is equal to the true value xor Mask. Mask = (RequestMask.NextByte() << 8) + RequestMask.NextByte();
- Packet: A data packet encrypted by the specified encryption method.

Before the transmission is completed, the data packet must contain actual data, in addition to the length and authentication data. When the transmission is complete, the client must send an empty data packet, that is, L = 0 (unencrypted) or the length of the authentication data (encrypted), to indicate the end of the transmission.

The packets are formatted as follows, depending on the encryption method:

- Unencrypted:
    - L bytes: actual data;
- AES-128-CFB: The entire data section is encrypted using AES-128-CFB.
    - 4 bytes: FNV1a hash of actual data;
    - L - 4 bytes: actual data;
- AES-128-GCM: Key is the Key of the instruction section, IV = count (2 bytes) + IV (10 bytes). count starts at 0 and increases by 1 for each packet; IV is the 3rd to 12th byte of the instruction section IV.
    - L - 16 bytes: actual data;
    - 16 bytes: GCM authentication information
- ChaCha20-Poly1305: Key = MD5 (instruction part Key) + MD5 (MD5 (instruction part Key)), IV = count (2 bytes) + IV (10 bytes). count starts at 0 and increases by 1 for each packet; IV is the 3rd to 12th byte of the instruction section IV.
    - L - 16 bytes: actual data;
    - 16 bytes: Poly1305 authentication information

## Server Response

The header data is encrypted using AES-128-CFB encryption. The IV is MD5 of the data encryption IV, and the Key is MD5 of the data encryption Key. The actual response data varies depending on the encryption settings.

| 1 Byte                    | 1 Byte     | 1 Byte      | 1 Byte           | M Bytes         | Remaining Part       |
| ------------------------- | ---------- | ----------- | ---------------- | --------------- | -------------------- |
| Response Authentication V | Option Opt | Command Cmd | Command Length M | Command Content | Actual Response Data |

in which：

- Response Authentication V: must match the response authentication V in the client request.
- Option Opt:
  - 0x01: server prepares to reuse TCP connections (deprecated in Xray 2.23+).
- Command Cmd:
  - 0x01: dynamic port command.
- Actual response data:
  - If Opt(S) in the request is enabled, the standard format is used. Otherwise, the basic format is used.
  - Both formats are identical to the request data.
    - When Opt(M) is enabled, the value of length L is equal to the true value XOR Mask. Mask = (ResponseMask.NextByte() << 8) + ResponseMask.NextByte().

### Dynamic Port Instructions

| 1 Byte   | 2 Bytes | 16 Bytes | 2 Bytes | 1 Byte     | 1 Byte            |
| -------- | ------- | -------- | ------- | ---------- | ----------------- |
| Reserved | Port    | User ID  | AlterID | User level | Validity period T |

in which:

- Port: Integer port number in Big Endian format
- T: Number of minutes as integer value.

When the client receives a dynamic port command, the server opens a new port for communication. The client can then send data to the new port. After T minutes, the port will expire, and the client must use the main port to communicate again.

## Comment

- To ensure forward compatibility, the values of all reserved fields must be 0.
