基于 Rust 的终端加密会话工具,支持临时 Session、邀请码、附件传输,以及端到端加密链路。
| 登录界面 | 聊天界面 |
|---|---|
![]() |
![]() |
这个项目当前更适合被理解为“持续演进中的自研 E2EE 终端聊天实验”,而不是已经经过完整审计、可对标成熟协议栈的安全通信产品。
- 当前版本无法抗流量分析
- 当前版本无法提供“顶级”安全
- 不应把它视为可直接对标
TLS、Noise、Signal、MLS的成熟方案 - 如果你的威胁模型包含恶意服务端、长期录包、强元数据隐藏、强前向安全或高强度身份校验,当前版本还不够
git clone https://github.com/Vrepol/MIST_V.git
cd MIST_V
cargo build --release编译完成后可执行文件位于:
target/release/MistV-servertarget/release/MistV-client
在 Windows 下对应为 MistV-server.exe 和 MistV-client.exe。
最直接的方式:
cargo run --bin MistV-server -- --port 6655 -k "Password"或者运行编译产物:
./target/release/MistV-server --port 6655 -k "Password"服务端参数:
| 参数 | 作用 | 默认值 |
|---|---|---|
-p, --port |
监听端口 | 6655 |
-k |
服务端主密码 | Vrepol |
如果你不想每次传参,可以直接修改 src/config.rs 里的默认值。
cargo run --bin MistV-client或者:
./target/release/MistV-client客户端没有命令行参数,启动后直接进入交互式流程。
客户端启动后会依次让你完成这些步骤:
- 输入昵称
- 选择服务器
- 输入服务端密码,或者直接贴邀请码
- 输入房间号
- 输入房间密码
服务器选择支持 4 种方式:
- 直接回车:使用 src/config.rs 里的默认预设服务器
- 输入数字:选择预设服务器列表中的某一项
- 输入
IP:PORT:手动连接一个服务端 - 输入
host:本机直接拉起一个服务端 - 输入
/INVITE:...:通过邀请码加入
如果你只是想在自己机器上先试通一遍,最省事的是用 host 模式:
- 运行
cargo run --bin MistV-client - 输入昵称
- 在服务器选择界面输入
host - 输入本地端口,默认就是
6655 - 输入服务端密码,留空则使用默认值
Vrepol - 选择一个对外展示地址,或者手动输入一个地址
- 回到客户端后继续输入房间号和房间密码
这个模式适合单机测试、局域网测试,或者你只是想快速确认功能链路都能跑通。
进入房间后:
- 直接输入文本并回车发送消息
- 输入
/send <path>发送任意文件 Ctrl+X智能贴入剪贴板里的文本、图片或文件- 房主在线时可按
Ctrl+I生成一次性邀请码 - 选中附件后按
Tab打开 - 按
Esc退出当前房间
完整快捷键如下:
| 快捷键 | 功能 |
|---|---|
Ctrl+X |
智能贴入剪贴板文本 / 图片 / 文件 |
Ctrl+C |
复制当前选中消息 |
Ctrl+Z |
撤销输入框内容 |
Ctrl+A |
清空输入框 |
Ctrl+I |
生成邀请码 |
/send <path> |
发送任意文件 |
← / → |
移动光标 |
Ctrl+← |
向左跳 3 个字符 |
Ctrl+→ |
跳到行尾 |
↑ / ↓ |
上下选择消息 |
Ctrl+↑ |
快速上跳 5 条 |
Ctrl+↓ |
快速跳到底部 |
Tab |
打开当前选中的附件 |
Esc |
退出房间 |
邀请码是给“已经在房间里的房主”生成给“新成员”的一次性入口。
使用方式:
- 房主进入房间后按
Ctrl+I - 把生成的
/INVITE:...发给对方 - 新成员启动客户端
- 在服务器选择界面直接粘贴整段
/INVITE:...
当前邀请码:
- 一次性消费
- 默认有效期 10 分钟
- 内部包含服务端地址、邀请码握手材料,以及一个只在客户端本地解房间信息的
blob_key
当前支持两种常见路径:
- 把文件路径写成
/send <path> - 直接用
Ctrl+X从剪贴板粘贴文本、图片或文件
补充说明:
- 图片会自动转换为 PNG 再发送
- 附件会分片传输
- 传输内置 ACK、超时与重试逻辑
当你已经知道怎么用之后,再看它到底包含什么。软件创建准则可以查看PRINCIPLE.md
当前仓库已经有这些能力:
- 终端聊天界面
- 房间系统与邀请码机制
- 客户端到服务端的加密传输
- 房间内消息加密
- 附件分片发送与接收
- 本地
host模式 - 群组密钥演进相关实验实现
默认配置集中在 src/config.rs:
- 默认服务端端口
DEFAULT_SERVER_PORT - 默认服务端密码
DEFAULT_SERVER_PASSWORD - 客户端预设服务器列表
CLIENT_SERVER_PRESETS
目录结构:
src/
├── attachments/ # 附件发送、接收、落盘
├── bin/ # client/server 二进制入口
├── client/ # 客户端初始化、握手、收发、会话逻辑
├── crypto/ # 房间加密、邀请码、传输安全、群组密钥演进
├── protocol/ # 协议行格式与解析
├── server/ # 服务端连接、广播、房间、邀请码逻辑
├── transport/ # 包封装、ACK、心跳
├── ui/ # TUI、通知、快捷键、剪贴板
├── util/ # 路径等辅助函数
├── config.rs # 默认配置
└── lib.rs # 模块导出与测试
二进制入口:
这部分应该放在“会不会用”之后,因为这个项目当前更适合被理解为:
“一个在持续演进中的自研 E2EE 终端聊天实验”,而不是“已经过审计、可直接对标成熟协议栈的安全通信产品”。
先说结论。
- 当前版本不能宣称具备抗流量分析能力
- 当前版本不能宣称具备“顶级”安全
- 当前版本不能宣称已经达到成熟协议栈的工程强度与审计水平
- 文本消息正文不会以明文形式暴露给服务端
- 附件明文不会直接暴露给服务端
- 房间密码不会原样上传给服务端
- 新成员默认不能解密加入前的旧 epoch
- 被移除成员默认不能解密 rekey 之后的新 epoch
- 单条消息使用独立派生出的 AEAD key / nonce
- 不能宣称达到 TLS / Noise / Signal / MLS 级别的成熟性
- 不能宣称传输层前向安全已经成立
- 不能宣称恶意服务端场景已经被完整处理
- 不能宣称弱房间口令具备很强的抗离线爆破能力
- 不能宣称流量特征已经被充分隐藏
如果你的威胁模型是:
- 不想让服务端直接看到聊天正文
- 不想让服务端直接看到附件明文
- 想先做一个可用的终端 E2EE 原型
那当前版本已经有价值。
如果你的威胁模型是:
- 服务端高度恶意
- 攻击者长期录包
- 需要成熟的身份验证、前向安全、后向安全、PCS
- 需要很强的元数据隐藏和抗流量分析能力
那当前版本还不够。
普通登录路径:
- 客户端对服务端密码做
SHA-256,得到server_pwd_hash - 客户端发明文
/AUTH_HELLO <client_nonce> - 服务端回明文
/AUTH_CHALLENGE <server_nonce> - 客户端回明文
/AUTH_PROOF <HMAC(server_pwd_hash, label, client_nonce, server_nonce)> - 双方用
HKDF(server_pwd_hash, salt = client_nonce || server_nonce)派生会话共享密钥 - 再按方向分离出
client->server和server->client两把传输密钥
邀请码登录路径类似:
- 邀请码中带有
token_secret和本地解 blob 的blob_key - 客户端发明文
/INVITE_HELLO <token_id> <client_nonce> - 服务端回明文
/INVITE_CHALLENGE <server_nonce> - 客户端回明文
/INVITE_PROOF <HMAC(token_secret, ...)> - 双方用
HKDF(token_secret, salt = token_id || client_nonce || server_nonce)派生传输层共享密钥
握手完成后,后续链路使用:
ChaCha20-Poly1305- 单调递增
seq seq映射到 nonce- 方向分离密钥
- 基于窗口的重复包检测
这层的作用是防止链路旁路直接读明文,但它还不是标准 TLS,也不具备成熟握手协议提供的前向安全。
当前使用 room_id + room_credential 本地导出房间秘密:
room_key = MD5(room_id || room_credential)后重复扩展成 32 字节join_credential = HMAC(room_key[..16], ROOM_JOIN_LABEL)room_auth_key = HKDF(room_key, salt = ROOM_AUTH_LABEL, info = "room-auth")
这意味着:
- 服务端默认拿不到原始
room_credential - 服务端拿得到
room_id和join_credential - 如果房间口令较弱,服务端仍可能离线猜解
- 当前不是 memory-hard KDF,抗爆破能力仍有限
房主创建房间后,本地生成随机 group_secret(epoch 0)。消息层随后按 epoch 运作:
- 用
group_secret + group_id + epoch通过 HKDF 派生sender_chain_root - 再按
sender_id派生每个发送者自己的chain_key - 每发一条消息,从当前
chain_key派生:- 下一跳
next_chain_key - 本条消息
aead_key - 本条消息
nonce
- 下一跳
- 使用
ChaCha20-Poly1305加密正文
消息头里的 group_id / epoch / sender_id / msg_no / msg_type 会作为 AAD 绑定完整性,但不会被加密。
当前效果:
- 每条消息独立 key / nonce
- 同一发送者的消息链单向推进
- 泄露单条消息 key 不会直接反推出别条消息 key
当前限制:
- 如果当前 epoch 的
group_secret泄露,该 epoch 会整体失守 - 还不具备强意义上的 epoch 内前向安全
成员变化时,当前实现会走一轮 epoch rekey:
- 每个成员持有一对临时
X25519密钥 - 成员通过
/KEY_ANNOUNCE广播自己的X25519 public key KEY_ANNOUNCE当前是room_auth_key做 MAC,不是长期身份签名- 提议者生成新的随机
group_secret - 对每个接收者,提议者执行一次
X25519 DH - 用
HKDF(DH, salt = room_auth_key)派生包裹密钥 - 把新的 epoch secret 分别封装进
/EPOCH_COMMIT - 各成员解开属于自己的 wrapped secret,激活新 epoch
- 激活新 epoch 后,本地重新生成新的临时
X25519密钥对
这部分带来的安全性质:
- 新加入成员默认拿不到旧 epoch secret
- 被移除成员在新 epoch 生效后默认拿不到后续 epoch
限制也很明确:
- 不是连续前向安全
KEY_ANNOUNCE还没有长期身份签名绑定- 当前安全码机制不足以覆盖恶意服务端替换身份的强威胁模型
当前邀请码格式:
/INVITE:<server_addr_b64>.<token_secret>.<blob_key>
其中:
token_secret用于和服务端完成一次性邀请码握手blob_key只在客户端本地使用- 服务端只存储
token -> blob_b64 blob_b64内部是加密后的{room_id, room_credential}
所以:
- 邀请码不会把房间密码直接交给服务端
- 服务端不能直接解开 blob
- 但邀请码本质上仍然是 bearer capability
- 谁拿到完整邀请码,谁就在有效期内拥有一次使用能力
当前 TTL 为 10 分钟,且一次性消费。
附件分成两层:
manifestchunk
其中:
manifest含file_key、nonce_base、文件名、总大小、分片数、哈希manifest会作为普通群消息正文加密发送- 每个附件随机生成一个
file_key - 每个 chunk 用
ChaCha20-Poly1305(file_key)加密 - AAD 会绑定
group_id / epoch / sender_id / transfer_id / chunk_index / total_chunks
因此:
- 服务端拿不到文件明文
- 服务端通常也拿不到
file_key - 但服务端仍能看到 chunk 数量、大小和时序
如果服务端是“诚实但好奇”甚至主动分析者,当前它能知道:
- 谁连上了服务器,连接多久,何时断开
- 是密码登录还是邀请码登录
- 房间
room_id - 成员列表、昵称、加入退出事件
- 谁是房主
- 每个包的方向、大小、到达时间、重传与 ACK 节奏
/RMSG里的group_id / epoch / sender_id / msg_no / msg_type- 附件的
transfer_id、chunk 推进节奏与总体规模 - 邀请码的申请、消费和过期行为
服务端默认看不到:
- 服务端主密码明文
- 房间密码明文
- 文本消息正文
- 附件明文
- 邀请 blob 内部的房间秘密
链路外部抓包者仍然可以看到:
- TCP 连接建立与断开
- 握手前几帧的明文协议头
- 密文长度和时间间隔
- 每 30 秒
/ping心跳形成的流量指纹 - 文本消息长度与密文长度的相关性
- 附件发送时明显的分片流模式
这也是为什么“流量混淆”仍然在 roadmap 的高优先级里。
这部分不再和“当前已经有什么”混在一起,而是明确告诉读者下一步准备做什么。
-
P0增加流量混淆,降低消息长度、时序和行为模式被观察的风险 -
P1软件可信性增强:面向不可信服务器场景,改进当前校验码机制,并考虑基于公钥材料生成可核验安全码 -
P3降低默认暴露端口和部署痕迹带来的识别风险
- 适配低带宽服务器:窗口大小 / 分片大小可配置,或者自适应带宽控制
- 发送者本地附件回显优化,避免服务端回环完整附件
- 输入框改进,处理中英文混输导致的光标错位
- IPv6 支持
- 本地
host模式安全加固,降低同目录服务端二进制被替换后的风险
- client / server 单二进制整合
- 断点续传 / 大文件续发
- 移动端或 GUI 客户端
- 长期身份密钥
- 基于身份公钥的签名绑定
- 真正可核验的安全码 / 指纹校验
- 更频繁、更严格的 epoch rekey
- 更接近 Double Ratchet / MLS / TreeKEM 的群组状态演进
Linux 下安装 libasound2-dev,或者在 Cargo.toml 中调整相关音频依赖配置。
建议使用 Windows Terminal,并选择支持较完整字符集的字体。
rustup target add x86_64-pc-windows-gnu
cargo build --release --target x86_64-pc-windows-gnu本项目基于 MIT License。


