-
Notifications
You must be signed in to change notification settings - Fork 5k
Closed
Labels
Description
VLESS UUID 映射标准:将自定义字符串映射为一个 UUIDv5
这个想法很早就有了,这里感谢 @DuckSoft 提议参照 UUIDv5 标准作为映射方式。
特别注意:切勿把本机制理解为某种加密,而应假定得到映射出的 UUID 即相当于得到原始文本。
1. 范围
长度为 1-30 字节的任意字符串(因为 UUID 字符串长为 32-36 字节,又额外排除了 0 和 31)
UTF-8 编码,比如数字、英文字母、符号等 ASCII 字符占 1 个字节,汉字通常占 3 个字节,emoji 通常占 4 个字节。
- 若存在非 ASCII 字符,JSON 等配置文件本身的编码格式必须为 UTF-8。
- Xray-core 内置自动映射,包括 VMess。其它 VLESS 实现需要参照此标准进行映射。
2. 方式
选取 16 字节的空 UUID 作为 namespace,自定义字符串即为 name,遵循 UUIDv5 标准进行一系列变换,最终生成 UUIDv5。
我参考 go.uuid 库写了个简单的实现,并输出同一个 UUIDv5 的两种形式:
package main
import (
"crypto/sha1"
"encoding/hex"
"fmt"
)
func main() {
var Nil [16]byte
var str = "example"
h := sha1.New()
h.Write(Nil[:])
h.Write([]byte(str))
u := h.Sum(nil)[:16]
u[6] = (u[6] & 0x0f) | (5 << 4)
u[8] = (u[8]&(0xff>>2) | (0x02 << 6))
fmt.Println("UUIDv5:", u)
buf := make([]byte, 36)
hex.Encode(buf[0:8], u[0:4])
buf[8] = '-'
hex.Encode(buf[9:13], u[4:6])
buf[13] = '-'
hex.Encode(buf[14:18], u[6:8])
buf[18] = '-'
hex.Encode(buf[19:23], u[8:10])
buf[23] = '-'
hex.Encode(buf[24:], u[10:])
fmt.Println("UUIDv5:", string(buf))
}字符串为 example 时,以上代码的输出为:
UUIDv5: [254 181 68 49 48 27 82 187 166 221 225 233 62 129 187 158]
UUIDv5: feb54431-301b-52bb-a6dd-e1e93e81bb9e
也就是说下一个版本的 Xray-core 中,VLESS/VMess id 填 example 和 feb54431-301b-52bb-a6dd-e1e93e81bb9e 是等价的。
3. Q & A
Q: 为什么“范围”不包括更长的字符串?
A: 允许使用自定义字符串是方便记忆,过长的字符串也记不住,还不如直接用一个 UUID。
Q: UUIDv5 的 SHA-1 是否存在问题?
A: 不存在问题,因为我们只是需要把限定长度的字符串映射为不重复的 UUID 标识符,按照标准来即可。
补充
- 建议 GUI 客户端等提供一个映射工具,任何人都可以创造等价 UUID,满足更多用途。
- 对于分享、订阅,暂时规定需要先按此标准映射为 UUIDv5,不分享原始文本。
Reactions are currently unavailable