5.7 KiB
mieru 代理协议
为了满足不同场景的需要,mieru 提供了 TCP 和 UDP 两种不同的代理协议。由于 UDP 协议需要尝试更多次数的解密,TCP 协议比 UDP 协议更快。在大多数情况下,我们推荐使用 TCP 协议。
下面是 mieru 代理协议的具体讲解。如果没有特殊说明,所有数据以 big endian 的方式存储。
密钥生成方法
TCP 和 UDP 协议共用同一套密钥生成方法。
每一个 mieru 用户都需要提供用户名 username 和密码 password。从用户名和密码生成加密和解密使用的密钥,需要经历如下几步。
第一步,生成一个哈希密码 hashedPassword,其值等于 password 附加一个 0x00 字节再附加 username 得到的字符串的 SHA-256 校验码。
第二步,获取系统当前的时间 unixTime,其值等于 1970 年 1 月 1 日到现在经历的秒数。将 unixTime 的时刻四舍五入到最接近的 2 分钟,以 uint64 存储为一个 8 字节的字符串,取得该字符串的 SHA-256 校验码,记为 timeSalt。
第三步,使用 pbkdf2 算法生成密钥。其中,使用 hashedPassword 作为密码,使用 timeSalt 作为盐,迭代次数为 64,密钥长度为 32 字节,哈希算法为 SHA-256。
由于密钥依赖于系统时间,客户端和服务器之间的时间差不能超过 4 分钟。服务器最多需要尝试 3 组不同的时刻才能顺利解密。
mieru 协议允许使用任何 AEAD 算法进行加密。当前 mieru 版本只实现了 XChaCha20-Poly1305 算法。
数据段的格式
mieru 收到用户的网络访问请求后,会将原始数据流量切分成小段(fragment),经过加密封装发送到互联网上。每个数据段(segment)中的数据项(field)及其长度如下表所示。
| padding 0 | nonce | encrypted metadata | auth tag of encrypted metadata | padding 1 | encrypted payload | auth tag of encrypted payload | padding 2 |
|---|---|---|---|---|---|---|---|
| ? | 0 or 12 or 24 | 32 | 16 | ? | size of original fragment | 16 | ? |
这其中,encrypted metadata 和 auth tag of encrypted metadata 会出现在每一个数据段中,其它的数据项则不是必须的。padding 0, padding 1 和 padding 2 是随机生成的非加密内容,mieru 使用这些填充数据调节数据段的信息熵,以及连续可打印字符的长度等信息。
TCP 数据段的规则
使用 TCP 协议时,nonce 在 TCP 连接的每个方向(客户端到服务器、服务器到客户端)只会在第一个数据段出现一次。每传输一个数据段,会进行一次或者两次加密操作,得到加密的元数据,以及(如果有)加密的原始数据载荷。每进行一次加密,nonce 的值会增加 1,变更后的 nonce 将会参与下一组加密的计算。
把原始数据切分成小段时,单个小段的最大长度是 32768 字节。
UDP 数据段的规则
使用 UDP 协议时,每一个数据段都会包含 nonce,用来解密当前的数据段。
加密后的数据段必须能装入单个 UDP 数据包中。数据段在传输时的大小,不能超过当前网络的 MTU 值。网络的 MTU 值会决定把原始数据切分成小段时,单个小段的最大长度。
元数据的格式
每个数据段必定包含一个元数据。元数据的长度固定为 32 字节。当前 mieru 版本定义的元数据类型包含下面两种。
会话元数据
会话元数据(session metadata)中的数据项及其长度如下表所示。
| protocol type | unused | timestamp | session ID | sequence number | status code | payload length | suffix length | unused |
|---|---|---|---|---|---|---|---|---|
| 1 | 1 | 4 | 4 | 4 | 1 | 2 | 1 | 14 |
会话元数据用于下面四种 protocol type:
openSessionRequest= 2openSessionResponse= 3closeSessionRequest= 4closeSessionResponse= 5
timestamp 的值设定为 1970 年 1 月 1 日到现在经历的分钟数。
如果一个数据段采用了会话元数据,该数据段可以用来传输最多 1024 字节的原始数据载荷。这个载荷的长度记录在 payload length 中。
suffix length 决定了 padding 2 的长度。
数据元数据
数据元数据(data metadata)中的数据项及其长度如下表所示。
| protocol type | unused | timestamp | session ID | sequence number | unack sequence number | window size | fragment number | prefix length | payload length | suffix length | unused |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 1 | 4 | 4 | 4 | 4 | 2 | 1 | 1 | 2 | 1 | 7 |
数据元数据用于下面四种 protocol type:
dataClientToServer= 6dataServerToClient= 7ackClientToServer= 8ackServerToClient= 9
timestamp, session ID 和 sequence number 的定义和用法,与会话元数据相同。
sequence number, unack sequence number 以及 window size 用于流量控制。
prefix length 决定了 padding 1 的长度,而 suffix length 决定了 padding 2 的长度。
UDP Associate 的封装
mieru 支持使用 TCP 和 UDP 代理协议传输 socks5 UDP associate 请求。为了保留 socks5 UDP 数据包的边界,mieru 会对原始 UDP associate 数据包进行如下的封装:
| marker 1 | data length | data | marker 2 |
|---|---|---|---|
| 1 | 2 | X | 1 |
其中 marker 1 的值恒定为 0x00,data length 的值为 X,marker 2 的值恒定为 0xff。封装后的结果将作为原始数据交给 TCP 和 UDP 代理协议进行加密和传输。