Skip to content

VLESS UUID 映射标准:将自定义字符串映射为一个 UUIDv5 #158

@RPRX

Description

@RPRX

VLESS UUID 映射标准:将自定义字符串映射为一个 UUIDv5

这个想法很早就有了,这里感谢 @DuckSoft 提议参照 UUIDv5 标准作为映射方式。

特别注意:切勿把本机制理解为某种加密,而应假定得到映射出的 UUID 即相当于得到原始文本。

1. 范围

长度为 1-30 字节的任意字符串(因为 UUID 字符串长为 32-36 字节,又额外排除了 0 和 31)

UTF-8 编码,比如数字、英文字母、符号等 ASCII 字符占 1 个字节,汉字通常占 3 个字节,emoji 通常占 4 个字节。

  1. 若存在非 ASCII 字符,JSON 等配置文件本身的编码格式必须为 UTF-8。
  2. 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 填 examplefeb54431-301b-52bb-a6dd-e1e93e81bb9e 是等价的。

3. Q & A

Q: 为什么“范围”不包括更长的字符串?
A: 允许使用自定义字符串是方便记忆,过长的字符串也记不住,还不如直接用一个 UUID。

Q: UUIDv5 的 SHA-1 是否存在问题?
A: 不存在问题,因为我们只是需要把限定长度的字符串映射为不重复的 UUID 标识符,按照标准来即可。

补充

  1. 建议 GUI 客户端等提供一个映射工具,任何人都可以创造等价 UUID,满足更多用途。
  2. 对于分享、订阅,暂时规定需要先按此标准映射为 UUIDv5,不分享原始文本。

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions