bytesoffreedom/karst
GitHub: bytesoffreedom/karst
KARST 是一个用 Rust 编写的实验性开源端到端加密通讯软件,采用混合后量子密钥协商和去中心化 relay 架构,旨在让用户自主掌控密码学身份。
Stars: 0 | Forks: 0
# KARST
**实验性开源私人通讯软件,支持端到端加密和混合后量子密钥协商。**
使用 Rust 构建,基于独立运作的 relay。



## 运行截图
桌面客户端 —— 无需电话号码,无需注册;你的身份就是一个 12 个单词的短语。

任何人都可以通过一条命令运行一个 relay。它是一个无脑、不可信的邮箱 —— 它永远看不到你的明文,并且可以实时切换状态(开放门禁 / 工作量证明 / 关闭)、发现其他 relay,并传播它们的信息(在信任前会进行验证):

两个客户端通过一个 relay 进行双向的端到端消息传输 —— 这是真实运行的真实输出:

## 什么是 KARST
KARST 是一个实验性的开源私人通讯软件。它使用端到端加密,结合了后量子和经典密码学的混合密钥协商,以及独立运作的 relay。该项目旨在让用户控制自己的密码学身份,并减少对单一服务提供商的依赖。
它是围绕现实世界中常见的安全风险设计的:被盗用的凭证、被攻陷的服务器、恶意的网络中间人、联系人密钥被替换、数据泄露以及加密流量的长期收集。relay 被视为不可信的传输基础设施 —— 它们无法获取明文消息内容。用户可以通过安全码验证联系人身份,本地消息历史记录通过静态加密进行保护。
KARST 不宣称具备完全的匿名性、对攻陷的免疫力、保证送达,或针对所有攻击者的保护。它旨在通过攻击者所能*做*的事情:拦截、篡改、冒充、关联流量或中断通信,来防御**恶意行为者** —— 网络罪犯、账户窃贼、恶意的网络中间人以及被攻陷的 relay 的运营者。完整的威胁模型位于 [SECURITY.md](SECURITY.md)。
### 原则
1. **开放式设计。** 协议和源代码都是公开的。安全性必须依赖于受保护的密钥和经过审查的密码学机制,而不是实现的保密性(Kerckhoffs 原则)。只有密钥、一次性凭证和瞬时的网络状态是保密的。
2. **端到端机密性。** relay 仅传输加密的信封,无法获取明文消息内容,因此被攻陷的 relay 获取的信息远远少于明文服务器。
3. **混合后量子密钥协商。** 初始密钥建立结合了 ML-KEM-768 和 X25519,随后使用 Double Ratchet。其目的是降低“先收集,后解密”的风险。该组合是实验性的,且未经独立审计。
4. **独立的 relay。** 协议不需要单一且强制的 relay 运营者。用户可以通过独立管理的基础设施进行连接,并且当单个 relay 端点或网络路径不可用时,设计仍能继续运行。
5. **资源受限的 relay 运行。** 使用准入控制、配额和资源限制来减少滥用和资源耗尽。每个稀缺资源都需要首先进行地址检查和密码学准入证明。
6. **明确的隐私边界。** KARST 会记录哪些元数据可能对客户端、relay、传输提供商和网络观察者保持可见。*现实检验:* 如今 relay **确实**知道存取信息的两端 —— 发送者的身份密钥位于接收者邮箱旁边,开启者会在 payload 中重复它,并且速率限制依赖于共享的凭证而不是匿名凭证。真正的源/目的不可链接性需要混合路由和目前尚不存在的多个非共谋 relay。参见 [`docs/STATUS.md`](docs/STATUS.md) 中的 **“relay 获取的信息”** 部分,逐字段进行了说明。[proxy-identity 模型](docs/design/proxy-identity.md)(路线图阶段 1–5)是未来的方向:一个自身没有地址的根身份,仅通过一次性的、可轮换的代理来访问。
7. **诚实胜于营销。** 实验性的、部分的或未经审计的属性都会照此标明。规范说明了它*不能*保证的内容。一个可验证、诚实的构建胜过一个被悄然攻陷的构建 —— 这就是为什么上面的审计状态被放在最前面,而不是最后。任何公开材料都不应超出 [`docs/SECURITY_CLAIMS.md`](docs/SECURITY_CLAIMS.md) 的范畴。
### 隐私限制
KARST 是一个私人通讯软件,**而不是匿名系统**。明确说明:
- KARST 不保证匿名性。
- 网络提供商或代理可以看到你的网络连接。
- relay 可以看到某些元数据(参见 [`docs/STATUS.md`](docs/STATUS.md))。
- 时间和流量关联可能仍然可行。
- Tor、VPN 或 I2P 是用户配置的网络选项 —— 它们不能替代应用级别的元数据保护。
- 目前的实现是实验性的,且未经独立审计。
关于项目的预期用途和使用边界,请参见 [`RESPONSIBLE_USE.md`](RESPONSIBLE_USE.md)。
## 架构
包含五个 crate 的 Rust workspace:
| Crate (binary) | 角色 |
|----------------|------|
| `admission` | 密码学准入路径 (§7):无状态 cookie、capability、RLN 配额核心、DTN 类、阈值环。 |
| `node` (`karst-relay`) | relay 节点**以及**端到端会话层 —— PQXDH 密钥协商、Double Ratchet、安全码、基于 relay 的发现。 |
| `client` (`karst`) | CLI 和库:源自 BIP39 恢复短语的 identity、静态加密的保管库、持久会话。 |
| `desktop` (`karst-desktop`) | 桌面客户端(Tauri:在原生 webview 中运行 Web 前端,基于共享的 `client`/`node` 核心):账户、聊天、relay + 邀请配置、安全码验证、带进度条/可取消的文件传输、个人资料。 |
| `gui` (`karst-gui`) | 旧版桌面客户端(egui)—— 作为 Tauri 客户端正在努力对齐的工作参考;仍在维护中。 |
### 安全属性及其成熟度
每一项声明都根据事实来源 [`docs/STATUS.md`](docs/STATUS.md) 进行了保留说明。“参考级,未经审计”意味着使用第三方内置基础组件的真实、可运行的代码,其手写的协议组合**尚未**通过独立审计。
| 属性 | 实现方式 | 成熟度 |
|----------|-----|----------|
| 端到端加密 | PQXDH (X3DH + ML-KEM-768, FIPS 203) → Double Ratchet | 参考级 · **未经审计** |
| 后量子保密性 | ML-KEM-768 混合,在根密钥中起关键作用 | 参考级 · **未经审计** |
| 静态加密 | Argon2id + XChaCha20-Poly1305,多账户保管库 | 可用 |
| 联系人真实性 | 60 位安全码,Signal 格式(带外校验) | 可用 |
| DoS 准入 | 无状态 cookie + capability + 分阶段流水线 | 可用 |
| 匿名速率限制 (RLN) | Nullifier + Shamir-slashing 配额跟踪器 | 核心可用;完整路径返回 `RlnNotImplemented`(zk 成员电路为存根) |
| 阈值环签名 | 基于 Ristretto255 的 CDS 构造 | 参考级 · **未经审计** · 受 feature-gate 控制 |
| 传输 —— 消息大小加固 | 在会话内部进行长度填充至固定桶 + 固定大小抓取(隐藏队列深度) | 可用 |
| 传输 —— WebSocket-over-TLS 载体 | 通过符合标准的 `wss://`(`rustls` + `tungstenite`)承载加密的 KARST 协议,可选启用,需要真实证书;这是一种传输封装,不是安全属性。不保证流量不可区分性 —— IP、SNI 和行为特征可能仍然可被观察 | 可用 · 已布线 · SNI 仍为明文 |
| 传输 —— 外部 PT | 通过 SOCKS5 路由到 Tor / obfs4 | 已布线 |
`docs/STATUS.md` 还指出了三堵**外部高墙** —— RLN zk 电路、阈值环的审计,以及 Poseidon 替换 —— 参考实现到此为止,生产环境的工作将从这里开始。
## 安装与运行
### 1. 前置条件
- **Rust**(stable,2021 版本)通过 [rustup](https://rustup.rs) 安装:`rustup toolchain install stable`。
- **C 工具链**(`cc`/`build-essential`),用于编译少量本地依赖。
- 仅针对 **egui GUI**:需要图形会话(X11 或 Wayland)和 OpenGL ——
它使用 egui/glow。无头服务器仍然可以运行 relay 和 CLI。
- 仅针对 **Tauri 桌面客户端**:需要图形会话以及 WebKitGTK 和
GTK 开发库 —— 在 Debian/Ubuntu 上为:`libwebkit2gtk-4.1-dev libgtk-3-dev
libsoup-3.0-dev libjavascriptcoregtk-4.1-dev librsvg2-dev`。不需要 `tauri-cli` ——
前端是一个静态 bundle,因此直接使用 `cargo run -p desktop`
即可构建并启动它。
- 可选,用于用户配置的代理传输:本地 **SOCKS5** 代理,例如
Tor 守护进程(`tor`,默认为 `127.0.0.1:9050`)或 obfs4。
### 2. 安装(脚本)
最快的路径 —— clone 代码库,然后根据需要运行安装脚本。两者都会在
`~/.local/bin` 中生成 release 二进制文件,并且在 `git pull` 后可以安全地重新运行以
进行更新。
```
git clone https://github.com/bytesoffreedom/karst-messenger
cd karst-messenger
scripts/install-karst.sh # the messenger: CLI `karst` + GUI `karst-gui`
# (在无头机器上添加 --no-gui)
scripts/install-node.sh # a relay node — interactive: it asks for the
# 监听地址,是否启用 wss
# carrier(需要真实的 TLS cert),以及可选地
# 安装一个 systemd --user 服务;它会打印出
# relay-id 以提供给您的对等节点,并会在
# 多次运行期间保持该身份。
```
node 安装程序会预先声明成熟度警告(relay 是一个不可信的
邮箱;准入层附带了一个带有公开密钥的 dev capability;
加密是未经审计的参考版本)。
### 2b. 手动构建
```
cd karst-messenger/impl
cargo build --release # builds relay + CLI + both desktop clients
# 二进制文件位于 impl/target/release/:karst-relay, karst, karst-desktop, karst-gui
# 或者直接运行 Tauri 桌面客户端(需要上述 WebKitGTK deps):
cargo run -p desktop
```
检查构建是否正常:
```
cargo test # default (audited-primitive path)
cargo test --features unaudited-crypto # + the reference §7.3 crypto
cargo clippy --all-targets
```
### 2c. 验证下载的发布版
预编译的 release 二进制文件(`karst`, `karst-relay`)随附一个 `SHA256SUMS` 文件,
该文件由项目的 [minisign](https://jedisct1.github.io/minisign/) 密钥签名。
公钥是:
```
```
```
minisign -Vm SHA256SUMS -P # signature is the project's
sha256sum -c SHA256SUMS # binaries match the checksums
```
**最安全的做法 —— 重新构建并进行比对,既不信任二进制文件,也不信任签名。**
一个被签名但带有后门的构建仍然是已签名的;而*复现*构建可以证明与
公开源代码完全匹配。`scripts/build-reproducible.sh` 可以在锁定的工具链上生成字节完全相同的二进制文件
(已跨构建路径验证):
```
git checkout
impl/scripts/build-reproducible.sh # prints the same sha256s as SHA256SUMS
```
完整的故事请参见 `docs/RELEASING.md` 和 `docs/design/reproducible-builds.md`。
### 3. 运行 relay 节点
relay 是一个**无脑、不可信的邮箱** —— 它永远看不到明文,也不保留
谁在和谁通话的记录:它只保存等待被提取的密封消息,以及人们为了能被联系到而发布的 prekey bundle。任何人都可以运行一个;运行你自己的 relay 可以消除对别人的依赖。
```
karst-relay # listens on 127.0.0.1:9000 (default)
karst-relay 0.0.0.0:9000 # listen on all interfaces (public relay)
```
启动时,它会打印出一个 **`relay-id`** —— 客户端需要地址**以及**这个 id
(它锁定了 relay 的 Noise + fetch-auth 公钥,因此 MITM 无法冒充
该 relay)。该 id 在重启后依然保持稳定。
| Relay 设置 | 方式 | 默认值 |
|---|---|---|
| 监听地址 / 端口 | 第一个 CLI 参数,否则使用 `KARST_RELAY_ADDR` 环境变量 | `127.0.0.1:9000` |
| 节点密钥位置 | `KARST_RELAY_HOME` 环境变量 | `~/.config/karst-relay/relay.key` (0600) |
| 角色(准入门禁) | `KARST_RELAY_MODE` 环境变量:`private` \| `public` \| `dev` | `private` —— 仅限邀请:一个随机的、每个 relay 独有的密钥,被持久化保存;relay 会写入 `invite.json`,对端通过 `karst import-cap ` 加入。`public` = 开放门禁(已分阶段实施 —— 在 PoW 反垃圾网关上线前容易受到洪水攻击的影响),并且它**拒绝启动,除非同时设置了 `KARST_RELAY_ALLOW_UNSAFE_PUBLIC=1`(显式选择开启)。`dev` = 已知的公开测试凭证,因此 `karst dev-cap` 可以连上(仅用于本地测试)。**未知**的模式值会拒绝启动,而不是静默回退到默认值 |
| relay-id | 启动时打印(`relay-id …`) | 源自节点密钥(稳定) |
| wss 载体 (WebSocket-over-TLS) | `KARST_RELAY_TLS_CERT` + `KARST_RELAY_TLS_KEY` (PEM) | 关闭 (原始 TCP);将两者都设置即可终结 `wss` —— 为 relay 的主机名使用真实的证书 |
为了能在互联网上访问:在你控制的主机/端口上运行它,打开防火墙,并
将对端的 `address` + `relay-id` 提供给它们。由于 relay 是不可信的,并且只传输
加密的流量,你可以运行多个 relay,客户端可以在它们之间切换。
### 4. 运行客户端
两个客户端读取相同的按配置划分的状态目录和相同的环境变量。
| 客户端设置 | 环境变量 | 说明 |
|---|---|---|
| 配置文件 / 状态目录 | `KARST_HOME` | 密钥、联系人、历史记录(静态加密);默认为 `~/.config/karst` |
| relay 地址 | `KARST_RELAY` | 例如 `127.0.0.1:9000`(GUI 会预填该字段) |
| relay id | `KARST_RELAY_ID` | relay 打印出的 id |
| 备用 relay 端点 | `KARST_RELAY_ALTS` | `host:port,host:port` —— 在**相同**载体上的额外路由(绝不会发生静默降级)。主机可以是 IP **或名称** —— 包括通过兼容的 SOCKS 代理访问的 onion-service 或 `.i2p` relay 端点(`abc.onion:443`, `xyz.i2p:9000`)。名称仅适用于通过具有解析能力的载体(SOCKS);直接路由会拒绝名称,而不是泄露 DNS 查询。在 GUI 中,这只会*预填* **额外路由(故障转移)** 字段,而这正是实际被使用的字段;CLI 会直接读取它 |
| 混合载体路由 | `KARST_PATHS` | `kind@ip:port,…`,其中 kind 为 `direct`\|`socks5`\|`wss`\|`wss+socks5` —— 自动传输切换:可以使用**不同**载体的额外路由。每一条都会根据你选择的载体进行过滤,因此切换绝不会牺牲原有载体:Tor 用户的列表会剔除 `direct` **和**裸 `wss` 路由;wss 用户的列表会剔除 `direct` 和裸 `socks5`。如果每个允许的路由都失效,连接就会失败 —— 它永远不会回退到你没有要求的路由。同上:在 GUI 中它会预填 **额外路由** 字段(两种语法都填入这一个字段 —— 靠 `@` 来区分它们) |
| SOCKS5 代理 | `KARST_SOCKS5` | 例如 Tor 的 `127.0.0.1:9050`;留空 = 直连 |
| wss 载体主机 | `KARST_WSS` | 要呈现的 SNI 主机,例如 `relay.example.com`;连接到 `KARST_RELAY` 并通过符合标准的 WebSocket-over-TLS 承载加密协议。优先级高于 `KARST_SOCKS5`;留空 = 关闭。追加一个不可猜测的路径,以便在真实网站的同一个域名后托管:`relay.example.com/s3cret-9f2a` 会呈现该网站的 SNI 并请求该路径,运营商的反向代理会将该路径路由到 relay,而其他所有流量则提供给该网站。不可猜测的路径能减少未经请求的端点发现;而可预测的路径则是一种可观察的特征。这不能保证与浏览器流量完全无法区分。 |
| 设备密码 | `KARST_PASSPHRASE` | 仅限 CLI;加密静态保存的密钥(**不包括**恢复短语) |
无论最终激活的是哪种载体 —— 直连、SOCKS5、wss 或 wss-over-SOCKS5 —— 都会
向你显示,因此绝不会是一个静默的假设:GUI 的状态栏中有一个 `via …` 标签,CLI 会在每次网络命令前打印 `carrier: …`。
如果你设置了代理或 `KARST_WSS`,你可以确认它是否真正生效。GUI 的
**界面和状态提示已被本地化为 9 种语言**(English, 中文, Español,
Português, Bahasa Indonesia, Français, 日本語, Русский, Deutsch);少数
自动生成的标签(默认账户名、尚未命名的联系人的占位符)仍仅支持
英文。
**桌面客户端** (`karst-desktop`) —— 正在积极开发的客户端(基于共享核心的 Tauri Web
前端)。使用 `cargo run -p desktop` 启动;其流程与
下面的 GUI 类似 —— 从一个 12 个单词的短语创建账户,将其指向一个
relay(对于私有 relay,粘贴 relay 的 `invite.json`),验证
安全码,聊天,并使用可取消的进度条传输文件。
**GUI** (`karst-gui`) —— 旧版 egui 客户端,作为工作参考保留:
1. 启动它(上面的环境变量会预填网络字段)。首次运行 → **创建
账户**:写下 **12 个单词的恢复短语**(这是在另一台设备上恢复的唯一方式),确认单词,设置一个 **设备密码**(加密*当前*磁盘上的密钥 —— 与短语不同)。后续运行 → 只需输入密码。
2. 从顶部栏复制你的 **地址 (IK)**,并通过带外方式发送给你的联系人;将他们的地址作为联系人粘贴进来。relay 上故意没有提供“按名称查找”功能(因为这会重新引入 MITM —— 见原则 3)。
3. 展开 **安全码** 并确认两边是否匹配。
4. 聊天、发送文件、设置阅后即焚计时器等。
**CLI** (`karst`) —— 无头/测试用。设置 `$R="--relay --relay-id "`:
```
export KARST_HOME=/tmp/alice KARST_PASSPHRASE=pw
karst init # prints your recovery phrase + address (IK)
karst dev-cap # install the local dev admission capability
karst publish $R # announce your bundle so others can reach you
karst send $R --to "hello"
karst send-file $R --to --file ./pic.jpg
karst recv $R # fetch inbox; files land in $KARST_HOME/received/
# 在新设备上恢复(到一个空的 KARST_HOME 中):karst restore word1 … word12
```
### 5. 在本地尝试整个系统
辅助脚本会启动一个 relay 并自动为你连接网络 id:
```
scripts/karst-demo.sh # one-shot: relay + two clients + round-trip, then cleans up
scripts/karst-wss-demo.sh # same round-trip, but through the WebSocket-over-TLS carrier
# 或者,要保持其运行:
scripts/karst-up.sh # start a relay on 127.0.0.1:9000, print ready-to-paste commands
scripts/karst-gui.sh alice # a GUI window (relay-id filled in automatically)
scripts/karst-gui.sh bob # a second window in another terminal
scripts/karst-down.sh # stop the relay
```
完整的本地演示以及“测试通过”检查清单,请参见 [`docs/RUNNING.md`](docs/RUNNING.md)。
## 仓库布局
```
impl/ Rust workspace: admission, node, client, gui
docs/ STATUS.md (maturity map) · RUNNING.md (local run)
scripts/ local-run helpers
KARST_SPEC.md protocol specification — source of truth
```
## 构建方式
KARST 的核心是在人工审查下借助 AI 辅助开发的,一次审查一个切片,每个切片都包含*具有区分度的*测试(移除修复代码 → 测试必须变红 → 恢复代码)。该项目公开且非商业性地开发,没有单一且强制的运营者。其权衡已在上面明确指出 —— 密码学部分属于参考实现,尚未经过审计。
## 跟踪构建进度
开发过程以平实、偶尔带有讽刺意味的英语记录在
KARST Telegram 频道 **@karstmessenger** 中:每次推送到此 repo 都会发布一篇帖子,
解释发布了什么以及为什么。这是观看 KARST 如何通过一个个审查过的切片逐步成型
的最快方式。
## 负责任的使用
KARST 旨在用于合法的私人通信、互操作性研究、密码学工程以及独立运作的消息基础设施的测试。它**不是**为了未经授权的访问、恶意软件、攻击第三方基础设施、欺诈、骚扰、非法交易或极端主义/恐怖主义活动而设计、认可或推广的。因为 KARST 是开源的,并且 relay 可以独立运作,维护者无法监控、解密、批准或控制所有的使用或部署情况。
**KARST 不是什么。** 在设计上,KARST **不是**通用的 VPN、互联网代理、任意的 TCP/UDP 隧道、Tor 出口节点,也不是用于干扰过滤或安全设备的工具 —— 它仅在同意参与运作的 relay 之间,为同意交互的用户传输 KARST 协议消息和文件。参见 [`RESPONSIBLE_USE.md`](RESPONSIBLE_USE.md)(预期用途、禁止用途)和 [`docs/TECHNICAL_BOUNDARIES.md`](docs/TECHNICAL_BOUNDARIES.md)(由测试支持的技术边界)。
## 贡献
早期阶段。请从 `KARST_SPEC.md`(事实来源)、`docs/STATUS.md`(什么是真实可用的、什么是存根、什么被阻塞了)以及 [`docs/ROADMAP.md`](docs/ROADMAP.md)(符合原则的功能积压)开始。期望贡献者能够尊重项目的技术边界和负责任的使用范围([`RESPONSIBLE_USE.md`](RESPONSIBLE_USE.md))。请**不要**公开提交漏洞 issue —— 请使用 [`SECURITY.md`](SECURITY.md) 中描述的私密渠道。
## 许可证
[GNU AGPLv3](LICENSE)。带有网络条款的 Copyleft:任何 fork,以及任何运行此代码的服务,都必须在同一许可证下保持开源。专有的闭源 fork 是不可能存在的。标签:Rust, 即时通讯, 去中心化, 可视化界面, 后量子密码学, 端到端加密, 网络安全, 网络流量审计, 通知系统, 隐私保护