aashir-athar/kith
GitHub: aashir-athar/kith
Kith 是一款隐私优先的端到端加密移动通讯应用,通过零知识中继架构确保服务器无法读取任何消息内容。
Stars: 0 | Forks: 0
# Kith
### 默认私密的通讯软件。在你的消息离开手机之前就已进行端到端加密。
精简但真实的 X3DH 密钥协商、XChaCha20-Poly1305 密封消息、无密码的基于密钥的登录,以及仅由你掌握的十二个单词的恢复短语。使用 Expo SDK 57 和一个永远不会看到你明文的 Hono 中继构建。
[](https://docs.expo.dev/versions/v57.0.0/) [](https://reactnative.dev/) [](https://www.typescriptlang.org/) [](https://hono.dev/) [](LICENSE) [](#security-model) [](#security-model) [](#security-model) [](#account-recovery) [](#security-model) 快速开始 · 安全模型 · 架构 · 从零到部署 · FAQ
## 为什么选择 Kith
大多数通讯软件要求你信任它们的服务器。Kith 的构建方式让你无需如此。每条消息都在你的设备上使用服务器永远无法得知的密钥进行密封,账户是密钥对而非手机号码,唯一的备份是你写下来的一句话。如果中继运营者、网络攻击者或法庭传唤数据库,他们得到的只能是密文和路由元数据,而不是对话内容。
本仓库是全栈内容:包含 Expo 应用、在客户端和服务器测试中逐字节运行完全相同的共享加密包,以及一个你可以用一条 Docker 命令在本地启动的 Hono 中继。
## 亮点
| 领域 | 你能得到什么 |
| --- | --- |
| **默认端到端加密** | 每条直接消息都在设备上使用 XChaCha20-Poly1305 进行密封。中继只负责转发其无法读取的不透明信封。 |
| **真实的密钥协商** | X25519 Diffie-Hellman 结合 X3DH-lite 预共享密钥握手,确保第一条消息是异步的且仍然经过身份验证。 |
| **无密码身份** | 没有手机号,没有密码。你通过签署服务器质询来证明对 Ed25519 身份密钥的控制权。 |
| **助记词恢复** | 你的身份派生自 BIP39 短语。只需写下一次,即可在任何设备上恢复。服务器不保存任何恢复密钥。 |
| **加密的本地历史记录** | 消息以密文形式保留在设备上,由仅存在于安全区中的数据密钥进行密封。不会有任何可读内容触及磁盘。 |
| **删除权** | 真正删除你的账户:中继会丢弃你的数据,设备会擦除所有密钥。 |
| **原生体验** | 采用 Expo Router 和真正的原生标签页、FlashList 线程、深色和浅色主题,以及无浅色模式闪烁的闪屏转场。 |
| **零知识中继** | Postgres 仅存储密文和公钥材料。Redis 承载一次性实时票据和按用户的分发。 |
## 已交付与计划中功能
Kith 提供了一个完整、真实的端到端加密通讯软件。它不会假装比实际更强大。
**已交付并连接到真实加密传输的功能**
- 一对一文本消息,端到端加密
- 发送、编辑和删除所有人消息,通过中继传播
- 送达和已读回执、输入指示器、在线状态
- 历史记录预加载、间隙检测同步以及加密的本地持久化
- 无密码注册和登录,以及通过恢复短语进行跨设备恢复
- 真实的账户删除
**已在离线演示中设计和预览,但尚未连接到中继的功能**
- 媒体、语音笔记和贴纸
- 语音和视频通话
- 社区和频道
在实时构建版本中(当应用指向某个中继时),仅供演示的界面会被隐藏,而不是显示为可操作的控件。请参阅[配置](#configuration)。
## 安全模型
Kith 使用 X3DH-lite 握手:Alice 获取 Bob 的已签名预共享密钥包,执行一系列 X25519 Diffie-Hellman 操作,通过 HKDF-SHA256 派生出会话密钥,并使用 XChaCha20-Poly1305 密封消息。路由头被绑定为身份验证数据,因此中继无法在不破坏解密的情况下交换密钥路径。
```
sequenceDiagram
autonumber
participant A as Alice (device)
participant R as Relay (zero knowledge)
participant B as Bob (device)
B->>R: Register identity + signed prekey + one-time prekeys (public only)
A->>R: Fetch Bob's prekey bundle
R-->>A: ikPub, ikDhPub, signed prekey, one one-time prekey
Note over A: Verify signed-prekey signature[](https://docs.expo.dev/versions/v57.0.0/) [](https://reactnative.dev/) [](https://www.typescriptlang.org/) [](https://hono.dev/) [](LICENSE) [](#security-model) [](#security-model) [](#security-model) [](#account-recovery) [](#security-model) 快速开始 · 安全模型 · 架构 · 从零到部署 · FAQ
DH1..DH4 then HKDF-SHA256 to session key A->>R: Sealed envelope (XChaCha20-Poly1305 ciphertext + header) R-->>B: Forward opaque envelope Note over B: Reconstruct session key from stored secrets
Open ciphertext, burn the one-time prekey ``` **中继可以看到的内容:** 谁在与谁交谈、时间、消息大小以及公钥。**它无法看到的内容:** 消息内容和任何私钥。 **密码学原语** | 用途 | 原语 | 库 | | --- | --- | --- | | 身份签名和身份验证质询 | Ed25519 | `@noble/curves` | | 密钥协商 | X25519 ECDH | `@noble/curves` | | 认证加密 | XChaCha20-Poly1305 | `@noble/ciphers` | | 密钥派生 | HKDF-SHA256 | `@noble/hashes` | | 恢复短语到种子 | BIP39 | `@scure/bip39` | 随机性是注入的,绝非假设的。客户端使用 `expo-crypto`,服务器和测试使用 Node CSPRNG,因此只有一种经过审计的加密实现,并且没有 `get-random-values` polyfill 依赖。 **诚实的局限性。** 这是第一个版本。会话密钥在每次 X3DH 握手时是静态的,因此 Kith 提供了机密性、发送方身份验证和异步首次接触,但不提供前向保密性或泄露后安全性。Double Ratchet 是升级路径。该应用没有在其界面的任何地方宣称具有完美前向保密性。 ## 账户恢复 你的账户是一个十二个单词的 BIP39 恢复短语。身份密钥是从中确定性派生的,因此相同的短语可以在任何设备上重现相同的账户,而无需服务器端密钥。 ``` flowchart LR P[Twelve-word phrase] -->|BIP39| S[Seed] S -->|HKDF-SHA256| E[Ed25519 identity] S -->|HKDF-SHA256| X[X25519 identity] E --> ID[Account identity] X --> ID ID -->|Sign challenge| L[Login on a new device] L -->|Rotate prekeys| RX[Receive again] ``` 在新手机上,你只需输入用户名和短语。Kith 会重建身份、签署登录质询,并将一组全新的预共享密钥轮换到中继,以便对端可以向新设备密封消息。如果丢失了短语,任何人都无法恢复账户(包括本项目)。这就是真正的隐私所要求的权衡。 ## 架构 一个小型 monorepo,遵循一个原则:加密代码只编写一次并共享,因此服务器测试执行的代码与手机上运行的代码完全相同。 ``` flowchart TD subgraph Client [kith - Expo app] UI[Expo Router screens + native tabs] ST[Zustand stores] CR[crypto: keystore, e2e, mnemonic] NET[api client + reconnecting socket] end subgraph Shared ["@kith/shared - isomorphic TS"] X3[X3DH-lite: seal, open, identityFromSeed] DTO[Zod DTOs + wire types] end subgraph Server [kith/server - Hono relay] RT[REST: auth, keys, conversations, account] WS[WebSocket gateway] DB[(Postgres - ciphertext + public keys)] RD[(Redis - tickets + fan-out)] end UI --> ST --> NET CR --> X3 NET -->|HTTPS| RT NET -->|WSS| WS RT --> X3 RT --> DB WS --> RD RT -.->|Zod contracts| DTO NET -.->|Zod contracts| DTO ``` **仓库结构** ``` kith/ src/app/ Expo Router routes (src/app structure, native tabs) src/crypto/ keystore (secure-store), e2e, mnemonic, random src/stores/ Zustand: session + chat (encrypted persist) src/api/ REST client, reconnecting socket, query client src/net/ transport config, messaging singleton, secure storage shared/ @kith/shared: X3DH-lite crypto + Zod DTOs (self-contained) server/ Hono relay: routes, ws gateway, Drizzle schema, migrations server/docker-compose.yml Postgres 16 + Redis 7 + relay ``` ## 技术栈 | 层级 | 选择 | | --- | --- | | **应用** | Expo SDK 57, React Native 0.86, React 19, Expo Router, 原生标签页, TypeScript strict (`noUncheckedIndexedAccess`, `noImplicitOverride`) | | **状态与数据** | Zustand, TanStack Query, FlashList, AsyncStorage (仅限密文), expo-secure-store (机密信息) | | **加密** | `@noble/curves`, `@noble/ciphers`, `@noble/hashes`, `@scure/bip39` | | **中继** | Hono, `@hono/node-server`, `ws` | | **数据** | 通过 `postgres` + Drizzle ORM 使用 Postgres,通过 `ioredis` 使用 Redis | | **测试** | 使用 `node:test` 结合 PGlite(进程内 Postgres)进行真实的集成测试 | ## 快速开始 你需要 Node 20 或更高版本,以及用于后端的 Docker。 ``` # 1. 安装 app 依赖 npm install # 2. 安装 shared crypto 包(常规依赖,以便在本地和 Docker 中解析) cd shared && npm install && cd .. # 3. 启动 Postgres、Redis 和 relay cd server && docker compose up --build ``` 然后,在第二个终端中,将应用指向中继并启动它: ``` # 真机:你的 LAN IP;Android emulator:10.0.2.2;iOS simulator:localhost echo "EXPO_PUBLIC_API_URL=http://localhost:8787" > .env npx expo start ``` 如果没有 `EXPO_PUBLIC_API_URL`,应用将运行完全离线的演示(适用于设计工作),并且加密后端会被编译移除。完整的说明(包括在没有 Docker 的情况下运行中继、迁移和 EAS 构建)位于 [zero-to-deploy.md](zero-to-deploy.md) 中。 ## 配置 | 变量 | 位置 | 用途 | | --- | --- | --- | | `EXPO_PUBLIC_API_URL` | 应用 `.env` 或 EAS profile | 中继基础 URL。它的存在将应用从离线演示切换到真实的加密后端。 | | `DATABASE_URL` | 服务器 `.env` | Postgres 连接字符串。 | | `REDIS_URL` | 服务器 `.env` | Redis 连接字符串。 | | `SESSION_SECRET` | 服务器 `.env` | 对存储在 Redis 中的不透明会话 ID 进行签名。在生产环境中请轮换。 | | `PORT` | 服务器 `.env` | 中继端口,默认为 8787。 | ## 测试 加密和持久化层由真实的集成测试覆盖,这些测试在进程内 Postgres 上运行共享代码。 ``` cd server && npm test # crypto vectors + PGlite integration (16 tests) npm run typecheck # app: tsc --noEmit (strict) cd server && npm run typecheck ``` ## 路线图 - 用于前向保密性和泄露后安全性的 Double Ratchet - 通过传输层进行加密的媒体、语音笔记和文件传输 - 加密的语音和视频通话 - 中继上的社区和频道 - 单一身份的多设备同步 - 可重现的构建和第三方密码学审查 ## FAQ
服务器会看到我的消息吗?
不会。消息在你的设备上发送前就已经密封了。中继只负责存储和转发不透明的密文以及路由元数据。它没有任何可以解密内容的密钥。
我该如何在新手机上登录?
输入你的用户名和十二个单词的恢复短语。Kith 会从短语中派生出你的身份,通过签署质询进行登录,并发布新的预共享密钥,以便人们可以再次给你发消息。根据设计,在切换之前密封到你旧设备上的消息是无法恢复的。
如果我丢失了恢复短语会怎样?
该账户将无法访问。无法重置,因为不存在可用于重置的服务器端密钥。在创建账户时,请务必备份好短语。
为什么不需要手机号或电子邮件?
你的账户就是一个密钥对。移除手机号等同于移除了一个将账户与现实世界身份及电信运营商关联的永久标识符。
通话和媒体也是加密的吗?
这些界面已经设计完成,但尚未连接到加密传输层,因此实时构建版本会隐藏它们而不是伪造功能。目前仅显示真正有效的功能。当今的文本消息已完全实现端到端加密。
这是前向保密的吗?
目前还不是。第一版在每次 X3DH 握手时使用静态会话密钥。Double Ratchet 升级已列入路线图,并且该应用绝不会宣称其具有并不具备的前向保密性。
### 由 Aashir Athar 构建
[](https://github.com/aashir-athar)
[](https://linkedin.com/in/aashirathar)
[](https://x.com/aashirathar)
标签:React Native, 即时通讯, 密码学应用, 搜索引擎查询, 移动应用, 端到端加密, 网络安全, 自动化攻击, 请求拦截, 隐私保护