#socks5-server #shadowsocks #remote-server #server-port #async-await #compatible #pure

bin+lib shadowrocks

纯 async/.await Rust 中的 Shadowsocks 端口

1 个不稳定版本

0.1.0 2020 年 5 月 27 日

#982 in 密码学

MIT 许可证

150KB
3.5K SLoC

Shadowrocks

Build Status Code Size Last Commit MIT License

掷石头到墙上

Shadowrocks 是一个使用纯 async/.await Rust 编写的 shadowsocks 端口。目前它只做了基本功能:从本地 SOCKS5 服务器到远程服务器的隧道,并使用适当的加密。实现经过彻底测试,且与具有 --compatible-mode 的原始 Python 版本兼容。

可以在这里找到 shadowsocks 的官方 Rust 实现:这里。它具有更多功能。

如何运行

要启动端口 51980 的本地 SOCKS5 服务器,运行

cargo run -- -l 51980 -s 127.0.0.1 -p 51986 -k test-password

同时,通过运行以下命令启动远程 Shadow 服务器

cargo run -- --shadow -s 127.0.0.1 -p 51986 -k test-password

服务器地址 (-s)、服务器端口 (-p) 和密码 (-k) 标志必须匹配。

也支持 JSON 配置文件 (-c)。关于 SIP002 中的描述的 ss:// URL 将很快推出。

加密

支持五种加密类型

  • chacha20-ietf-poly1305 由 sodium 提供
  • xchacha20-ietf-poly1305 由 sodium 提供
  • aes-128-gcm 由 OpenSSL 提供
  • aes-192-gcm 由 OpenSSL 提供
  • aes-256-gcm 由 OpenSSL 提供

它们都是 AEAD 加密。

兼容性

在不兼容模式下,对 socks 服务器和 shadow 服务器之间的流量进行了几项更改。

  1. 主密钥使用 PBKDF2 生成,而不是原始版本中使用的 PBKDF1。主密钥仍然从密码生成。
  2. 子密钥通过使用 HKDFSHA256 生成,而不是不再安全的 SHA1,输入到 HKDF 的密钥仍然是主密钥。
  3. 在加密握手过程中,用于加密输出流量的盐由代理服务器指定,而代理服务器使用的盐由 socks 服务器指定。这与原始版本相反,原始版本中每个服务器决定自己的盐。

第3项有助于防御重放攻击。如果我们合理地假设生成的盐每次都不同,那么两个服务器必须为每个新的连接重新加密流量。攻击者将需要为会话生成不同的子密钥,而没有主密钥是无法做到的。

在不兼容模式下,shadowrocks 的行为与原始版本相同。

功能

  • TCP隧道
  • 集成 Clippy
  • 基准测试
  • 集成测试
  • 模块级文档
  • 详细记录 src/crypto 中的代码
  • 可选 fake-tcp 的 UDP 隧道
  • 兼容模式下的重放攻击缓解
  • 非兼容模式下的重放攻击缓解
  • 本地混淆
  • 管理器 API 以动态创建服务器
  • ss:// URL 和 JSON 配置文件

加密依赖项

同时使用 ring 模块(BoringSSL)和 openssl 模块。这两个模块的功能在很大程度上重叠。ring最初被用作对openssl的参考点和合理性检查,当作者不熟悉shadowsocks中使用的加密时。

ringopenssl 功能表

功能 ring openssl
PBKDF1
PBKDF2
HKDF-SHA1
HKDF-SHA256
AES-128-GCM
AES-192-GCM
AES-256-GCM

HKDF-SHA1 支持 最近 被添加到 ring

可以通过禁用功能 ring-crypto 来禁用 ring 模块。目前无法完全禁用 openssl 模块。

改进

  • 减少加密/解密过程中的内存分配。

当前实现为每个连接执行许多小内存分配。例如,为了从 socks 服务器向远程代理服务器发送 SOCKS 5 地址,按照以下过程进行。

  1. 将 SOCKS 5 地址转换为字节。
  2. 将数据包长度转换为字节(x 字节,x = 2)。
  3. 将nonce转换为字节(4字节)。
  4. OpenSSL 加密对象。
  5. 数据包长度的密文(x 字节,x = 2)。
  6. 加密标签(16字节)。
  7. 密文和标签的连接(18字节)。
  8. 对于具有不同 x 的 SOCKS5 地址,重复 2-7 步。

总结来说,每个数据包有13个分配。每次加密需要6个分配,而每个数据包我们需要加密两次:一次用于数据包长度,一次用于实际信息。

读取过程类似。

  1. 数据包长度的密文。
  2. 加密标签
  3. 将nonce转换为字节
  4. OpenSSL 加密对象。
  5. 数据包长度明文。
  6. 对数据包内容重复1-5步骤。

对于“无标签密文”部分,我们省了一步。尽管如此,这仍然很糟糕。

依赖项

~31MB
~367K SLoC