XTLS / Xray-core

Xray, Penetrates Everything. Also the best v2ray-core, with XTLS support. Fully compatible configuration.
https://t.me/projectXray
Mozilla Public License 2.0
25.47k stars 3.94k forks source link

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

Closed RPRX closed 3 years ago

RPRX commented 3 years ago

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,不分享原始文本。
badO1a5A90 commented 3 years ago

配置文件是: 服务端仍保持UUID,客户端可以使用 原字符串 / UUID 其中任一方式吗?

RPRX commented 3 years ago

@badO1a5A90 两边都可以使用任一方式,同出/入站多 UUID 时还可以混用

badO1a5A90 commented 3 years ago

@badO1a5A90 两边都可以使用任一方式,同出/入站多 UUID 时还可以混用

都可以使用任一方式, 那么客户端使用 UUID 也能反映射到服务端的正常字符串?

RPRX commented 3 years ago

@badO1a5A90 是的,因为这两个是完全等效的,只是配置文件不同,内存中都是同一个 UUID。

无需担心与现在常用的全随机 UUIDv4 冲突,因为标志位不一样,分别是 5 和 4。

DuckSoft commented 3 years ago

五分钟撸了一个 Web App,但足够用了:https://xray-uuid.ducksoft.site/

badO1a5A90 commented 3 years ago

补充: 对于汉字,Golang使用UTF-8字符集存储,默认用三个字节

RPRX commented 3 years ago

@badO1a5A90 已补充标准正文

z826540272 commented 3 years ago

用普通话说就是 xray支持自定义密码了

badO1a5A90 commented 3 years ago

建议给 xray uuid 命令增加一个 xray uuid map 命令

如: xray uuid map -id "我爱🍉老师1314" 则输出 : 5783a3e7-e373-51cd-8642-c83782b807c5

RPRX commented 3 years ago

@badO1a5A90 计划加 -map 参数,xray uuid -map "example"


后续:加了 -i 参数,xray uuid -i "example"

secXsQuared commented 3 years ago

多说一句,python里面这么用:

import uuid

UUID_NAMESPACE = uuid.UUID('00000000-0000-0000-0000-000000000000')
uuid.uuid5(UUID_NAMESPACE, "example")

输出: UUID('feb54431-301b-52bb-a6dd-e1e93e81bb9e')