# VMess 协议 VMess 是一个加密传输协议,可以作为 Xray 客户端和服务器之间的桥梁。 ## 版本 当前版本号为 1。 ## 依赖 ### 底层协议 VMess 是一个基于 TCP 的协议,所有数据使用 TCP 传输。 ### 用户 ID ID 等价于 [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier),是一个 16 字节长的随机数,它的作用相当于一个令牌(Token)。 一个 ID 形如:de305d54-75b4-431b-adb2-eb6b9e546014,几乎完全随机,可以使用任何的 UUID 生成器来生成,比如[这个](https://www.uuidgenerator.net/)。 用户 ID 可在[配置文件](../../config)中指定。 ### 函数 - MD5: [MD5 函数](https://en.wikipedia.org/wiki/MD5) - 输入参数为任意长度的 byte 数组 - 输出为一个 16 byte 的数组 - HMAC: [HMAC 函数](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code) - 输入参数为: - H:散列函数 - K:密钥,任意长度的 byte 数组 - M:消息,任意长度的 byte 数组 - Shake: [SHA3-Shake128 函数](https://en.wikipedia.org/wiki/SHA-3) - 输入参数为任意长度的字符串 - 输出为任意长度的字符串 ## 通讯过程 VMess 是一个无状态协议,即客户端和服务器之间不需要握手即可直接传输数据,每一次数据传输对之前和之后的其它数据传输没有影响。 VMess 的客户端发起一次请求,服务器判断该请求是否来自一个合法的客户端。如验证通过,则转发该请求,并把获得的响应发回给客户端。 VMess 使用非对称格式,即客户端发出的请求和服务器端的响应使用了不同的格式。 ## 客户端请求 | 16 字节 | X 字节 | 余下部分 | | -------- | -------- | -------- | | 认证信息 | 指令部分 | 数据部分 | ### 认证信息 认证信息是一个 16 字节的哈希(hash)值,它的计算方式如下: - H = MD5 - K = 用户 ID (16 字节) - M = UTC 时间,精确到秒,取值为当前时间的前后 30 秒随机值(8 字节, Big Endian) - Hash = HMAC(H, K, M) ### 指令部分 指令部分经过 AES-128-CFB 加密: - Key:MD5(用户 ID + []byte('c48619fe-8f02-49e0-b9e9-edf763e17e21')) - IV: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 | 数据加密 IV | 数据加密 Key | 响应认证 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; - 数据加密 IV:随机值; - 数据加密 Key:随机值; - 响应认证 V:随机值; - 选项 Opt: - S (0x01):标准格式的数据流(建议开启); - R (0x02):客户端期待重用 TCP 连接(Xray 2.23+ 弃用); - 只有当 S 开启时,这一项才有效; - M (0x04):开启元数据混淆(建议开启); - 只有当 S 开启时,这一项才有效; - 当其项开启时,客户端和服务器端需要分别构造两个 Shake 实例,分别为 RequestMask = Shake(请求数据 IV), ResponseMask = Shake(响应数据 IV)。 - 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:指令部分除 F 外所有内容的 FNV1a hash; ### 数据部分 当 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 hash; - L - 4 字节:实际数据; - AES-128-GCM:Key 为指令部分的 Key,IV = count (2 字节) + IV (10 字节)。count 从 0 开始递增,每个数据包加 1;IV 为 指令部分 IV 的第 3 至第 12 字节。 - L - 16 字节:实际数据; - 16 字节:GCM 认证信息 - ChaCha20-Poly1305:Key = MD5(指令部分 Key) + MD5(MD5(指令部分 Key)),IV = count (2 字节) + IV (10 字节)。count 从 0 开始递增,每个数据包加 1;IV 为 指令部分 IV 的第 3 至第 12 字节。 - L - 16 字节:实际数据; - 16 字节:Poly1305 认证信息 ## 服务器应答 应答头部数据使用 AES-128-CFB 加密,IV 为 MD5(数据加密 IV),Key 为 MD5(数据加密 Key)。实际应答数据视加密设置不同而不同。 | 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 | 用户 ID | AlterID | 用户等级 | 有效时间 T | 其中: - 端口 Port:Big Endian 格式的整型端口号; - 有效时间 T:分钟数; 客户端在收到动态端口指令时,服务器已开放新的端口用于通信,这时客户端可以将数据发往新的端口。在 T 分钟之后,这个端口将失效,客户端必须重新使用主端口进行通信。 ## 注释 - 为确保向前兼容性,所有保留字段的值必须为 0。