We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
目前 VMess 已可根据协议头自动协商使用 AEAD 加密并认证传输的流量。但 VMess 协议头部仍然使用 MD5 + AES-CFB ,不能保证协议头部自身的完整性。
KDF: KDF 接受一个字节数组形式的主密钥,和若干字节数组形式的路径,生成一个子密钥
func KDF(key []byte, path [][]byte) []byte { oKDF = HMAC(SHA256, "VMess AEAD KDF") for in_v := range path { oKDF = HMAC(oKDF, in_v) } return oKDF(key) }
CmdKey: 由用户 UUID 产生的一个字节数组形式密钥
const IDBytesLen = 16 type ID struct { uuid uuid.UUID cmdKey [IDBytesLen]byte } func (id ID) CmdKey() []byte { return id.cmdKey[:] }
为了保持和原有协议,配置兼容,继而保证全部用户都可以享受到这个更新,必须要通过且仅通过最开始16个字节计算出客户端的UUID信息。
EAuID 明文为 [Timestamp 8B][Rand 4B][CRC 4B]
[Timestamp 8B][Rand 4B][CRC 4B]
Timestamp 为以秒为单位的64位 Unix 时间戳 Rand 为随机数 CRC 为 CRC32([Timestamp, Rand]) IEEE 多项式
CRC32([Timestamp, Rand])
EAuID 密钥为 KDF(CmdKey, ["AES Auth ID Encryption"]) EAuID 使用 AES-128 块加密
KDF(CmdKey, ["AES Auth ID Encryption"])
ELength 是经过 AES-128-GCM 加密的 16 位大端序 VMess 包头部长度。 加密密钥为 KDF(CmdKey, ["VMess Header AEAD Key_Length", EAuID, Nonce]) 不重数为 KDF(CmdKey, ["VMess Header AEAD Nonce_Length", EAuID, Nonce]) 附加数据为 EAuID
KDF(CmdKey, ["VMess Header AEAD Key_Length", EAuID, Nonce])
KDF(CmdKey, ["VMess Header AEAD Nonce_Length", EAuID, Nonce])
经过 AES-128-GCM 加密的 VMess 标准首部 加密密钥为 KDF(CmdKey, ["VMess Header AEAD Key", EAuID, Nonce]) 不重数为 KDF(CmdKey, ["VMess Header AEAD Nonce", EAuID, Nonce]) 附加数据为 EAuID
KDF(CmdKey, ["VMess Header AEAD Key", EAuID, Nonce])
KDF(CmdKey, ["VMess Header AEAD Nonce", EAuID, Nonce])
[EAuID][ELength][Rand 8B][EHeader]
Rand 是 64 位随机数
对于每个客户端发来的 EAuID
AES-128-GCM 加密的 16 位大端序包头部长度。 加密密钥为 KDF(CmdKey, ["VMess Header AEAD Key_Length", EAuID, Nonce])[:16] 不重数为 KDF(CmdKey, ["Vmess Header AEAD Nonce_Length", EAuID, Nonce])[:12] 附加数据为 EAuID
KDF(CmdKey, ["VMess Header AEAD Key_Length", EAuID, Nonce])[:16]
KDF(CmdKey, ["Vmess Header AEAD Nonce_Length", EAuID, Nonce])[:12]
目的在与尽早完成对客户端的验证(注1) 必须发送长度否则不知道要读取多少信息 必须验证 EAuID,防止攻击者抓取阻止新请求后用新 EAuID 和旧的 Nonce,MaskedLength 一起构造重放攻击 必须验证 Length,防止攻击者修改长度,后面的 MaskedLength 没有独立的认证 必须验证 Nonce,如果 Nonce 错误解密会失败,要尽早验证 (注1) 必须加盐,此用户 UUID (CmdKey) 还有其他用途
目的在与尽早完成对客户端的验证(注1)
经过 AES-128-GCM 加密的 VMess AEAD 首部 加密密钥为 KDF(CmdKey, ["VMess Header AEAD Key", EAuID, Nonce])[:16] 不重数为 KDF(CmdKey, ["VMess Header AEAD Nonce", EAuID, Nonce])[:12] 附加数据为 EAuID
KDF(CmdKey, ["VMess Header AEAD Key", EAuID, Nonce])[:16]
KDF(CmdKey, ["VMess Header AEAD Nonce", EAuID, Nonce])[:12]
数据是VMess的整个未加密头部,从版本号到校验数据。目的是简化支持AEAD的难度,减少非必要的协议修改。
AES-GCM 要求 Key-Nonce 组合不重复。由于其中使用了 EAuID, Nonce 作为Key,Nonce 的 KDF 输入,有96位随机熵输入不会重复。因为使用用户 UUID 作为输入无法被攻击者解密。 这两个 AEAD 输入都必须加盐因为用户 UUID 还有其他用途。
[EAuID][ALength][Rand 8B][Aheader]
VMessAEAD 协议的返回头也会使用 AEAD 方式进行加密,方法类似请求包头。但是密钥数据来自连接密钥 + KDF。发送 AEAD 加密的 MaskedLength,之后是内容。
客户端必须记忆自己是否使用了 VMessAEAD 发送包头,在使用 VMessAEAD 替换掉了一些 MD5 等弱密码学函数,即使这些弱密码学函数尚不构成任何已知协议弱点。
注1: 阻止抓包 + 重放攻击者根据读取长度来确定服务器类型,如果需要读取过多数据,那么就会超出计算出的 Drain 值长度,无法通过 Drain 隐藏服务器处理流程。 为了保证 Drain 能正常运行,必须在读取 38 + 16 个字节前决定此连接是否来自真实客户端。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
1. 现状
目前 VMess 已可根据协议头自动协商使用 AEAD 加密并认证传输的流量。但 VMess 协议头部仍然使用 MD5 + AES-CFB ,不能保证协议头部自身的完整性。
2. 定义
KDF: KDF 接受一个字节数组形式的主密钥,和若干字节数组形式的路径,生成一个子密钥
CmdKey: 由用户 UUID 产生的一个字节数组形式密钥
3. EAuID
为了保持和原有协议,配置兼容,继而保证全部用户都可以享受到这个更新,必须要通过且仅通过最开始16个字节计算出客户端的UUID信息。
3.1 EAuID
EAuID 明文为
[Timestamp 8B][Rand 4B][CRC 4B]
Timestamp 为以秒为单位的64位 Unix 时间戳
Rand 为随机数
CRC 为
CRC32([Timestamp, Rand])
IEEE 多项式EAuID 密钥为
KDF(CmdKey, ["AES Auth ID Encryption"])
EAuID 使用 AES-128 块加密
3.2 ELength
ELength 是经过 AES-128-GCM 加密的 16 位大端序 VMess 包头部长度。
加密密钥为
KDF(CmdKey, ["VMess Header AEAD Key_Length", EAuID, Nonce])
不重数为
KDF(CmdKey, ["VMess Header AEAD Nonce_Length", EAuID, Nonce])
附加数据为 EAuID
3.3 EHeader
经过 AES-128-GCM 加密的 VMess 标准首部
加密密钥为
KDF(CmdKey, ["VMess Header AEAD Key", EAuID, Nonce])
不重数为
KDF(CmdKey, ["VMess Header AEAD Nonce", EAuID, Nonce])
附加数据为 EAuID
3.4 整体格式
[EAuID][ELength][Rand 8B][EHeader]
Rand 是 64 位随机数
3.5 验证流程
对于每个客户端发来的 EAuID
4. VMessAEAD 包头
4.1 ALength
AES-128-GCM 加密的 16 位大端序包头部长度。
加密密钥为
KDF(CmdKey, ["VMess Header AEAD Key_Length", EAuID, Nonce])[:16]
不重数为
KDF(CmdKey, ["Vmess Header AEAD Nonce_Length", EAuID, Nonce])[:12]
附加数据为 EAuID4.2 Aheader
经过 AES-128-GCM 加密的 VMess AEAD 首部
加密密钥为
KDF(CmdKey, ["VMess Header AEAD Key", EAuID, Nonce])[:16]
不重数为
KDF(CmdKey, ["VMess Header AEAD Nonce", EAuID, Nonce])[:12]
附加数据为 EAuID4.3 整体格式
[EAuID][ALength][Rand 8B][Aheader]
Rand 是 64 位随机数
4.4 VMessAEAD 处理流程
VMessAEAD 协议的返回头也会使用 AEAD 方式进行加密,方法类似请求包头。但是密钥数据来自连接密钥 + KDF。发送 AEAD 加密的 MaskedLength,之后是内容。
客户端必须记忆自己是否使用了 VMessAEAD 发送包头,在使用 VMessAEAD 替换掉了一些 MD5 等弱密码学函数,即使这些弱密码学函数尚不构成任何已知协议弱点。
注1:
阻止抓包 + 重放攻击者根据读取长度来确定服务器类型,如果需要读取过多数据,那么就会超出计算出的 Drain 值长度,无法通过 Drain 隐藏服务器处理流程。
为了保证 Drain 能正常运行,必须在读取 38 + 16 个字节前决定此连接是否来自真实客户端。
The text was updated successfully, but these errors were encountered: