9Elliot/Kalyx-Vault

GitHub: 9Elliot/Kalyx-Vault

Kalyx-Vault 是一个演示混合后量子密钥交换(X25519+ML-KEM-768)的移动应用和 API 项目,用于教育性地展示「现在收集,以后解密」的量子威胁场景。

Stars: 0 | Forks: 0

# Kalyx-Vault **Kalyx-Vault** 是一个参考移动应用和 API,展示了 [Kalyx-PQ](https://pypi.org/project/kalyxpq/)(**`kalyxpq` on PyPI**):混合后量子密钥交换(X25519 + ML-KEM-768)和会话加密。目标是打造一个教育性、生产级演示,具有清晰的**"现在收集,以后解密"**叙事。 | 组件 | 技术 | |-----------|------------| | 移动应用 | React Native (Expo), TypeScript | | 后端 | FastAPI (Python 3.10+) | | 密码学(服务器)| 来自 PyPI 的 **`kalyxpq`**,带有 **`[msgpack,oqs]`** 扩展 | | 密码学(客户端)| `@noble/curves`, `@noble/post-quantum`, `@noble/hashes`, `@noble/ciphers`, `@msgpack/msgpack` | ## 目录 1. [架构](#architecture) 2. [加密流程](#cryptographic-flow) 3. [前置条件](#prerequisites) 4. [后端设置](#backend-setup) 5. [Docker 部署(生产环境)](#docker-deployment-production) 6. [部署到 Render](#deploy-to-render) 7. [移动应用设置](#mobile-app-setup) 8. [API 参考](#api-reference) 9. [配置](#configuration) 10. [项目结构](#project-structure) 11. [故障排除](#troubleshooting) 12. [安全说明](#security-notes) 13. [参考资料](#references) ## 架构 ``` sequenceDiagram participant App as Expo app (JS) participant API as FastAPI + kalyxpq App->>API: POST /handshake (X25519 + ML-KEM-768 public keys, base64 JSON) API->>API: KalyxEngine.server_respond (strict_pq) API-->>App: server keys, pq ciphertext, session_id, telemetry App->>App: Derive hybrid AES-256 key (HKDF-SHA3-256) App->>API: POST /vault + X-Session-Id (KALX + AES-GCM + msgpack) API->>API: Verify KALX, decrypt, @kalyx_safe response API-->>App: Encrypted JSON ack (AES-GCM, session key) ``` - **握手** 匹配 `kalyxpq.HTTPHandshakeTransport` 使用的线格式:JSON 主体,带有 base64 编码的二进制字段。 - **会话绑定** 使用服务器签发的 **`session_id`** 头;服务器存储在 `server_respond` 期间派生的**相同 32 字节混合密钥**(客户端通过 JS 中的 `client_finalize` 语义独立派生)。 - **Vault 载荷** 使用 **`kalyxpq`** 的 **`MAGIC_HEADER`**(`KALX`,字节 `0x4B414C58`)前置到 AES-GCM 密文,**AAD** 设置为序列化器名称(`json` 或 `msgpack`)。 ## 加密流程 ### 混合握手(与 `KalyxEngine` 对齐) 1. **客户端** 生成一个 X25519 密钥对和一个 ML-KEM-768 密钥对。 2. **客户端 → 服务器**:经典和 PQ 公钥(JSON 中的 base64)。 3. **服务器** 运行 `KalyxEngine(strict_pq=True).server_respond(...)`,该函数: - 使用客户端的经典密钥执行 X25519。 - 使用 **liboqs**(而非库内模拟 KEM)向客户端的 ML-KEM-768 公钥**封装**。 - 使用 **HKDF-SHA3-256** 派生 **32 字节**会话密钥,`salt = None`,`info = KALYX-PQ-HYBRID-HANDSHAKE-v1`。 4. **服务器 → 客户端**:服务器经典公钥、经典密文字段(与当前引擎中的服务器公钥相同)、PQ 密文、服务器上下文哈希,以及遥测数据和 `session_id`。 5. **客户端** 完成 X25519 和 ML-KEM **解封装**,然后运行**相同的 HKDF** 获取会话密钥。 严格 PQ 模式**拒绝**模拟 KEM:后端**必须**通过 **liboqs-python** 加载真实的 **ML-KEM-768**。 ### Vault 通道(`KalyxSession` 布局) - 明文被序列化(此应用中默认为 MessagePack),然后用 **AES-256-GCM** 加密。 - **线上的密文**:`MAGIC_HEADER (4 字节) || GCM 密文 || tag`。 - **AAD**:`serializer` 的 ASCII 字节(`msgpack` 或 `json`)。 ### `@kalyx_safe` 响应 vault 端点返回 JSON,包含由 **`kalyx_safe`** 生成的 base64 `nonce` 和 `ciphertext`:JSON 对象的 AES-GCM 加密,**无 AAD**。移动客户端使用**相同的混合会话密钥**解密以显示确认信息。## 前置条件 ### 后端 - **Python 3.10+** - **pip** 和虚拟环境(推荐) - **原生 liboqs** 可供 **liboqs-python** 使用(`strict_pq=True` 时必需) ### 移动端 - **Node.js**(LTS)和 **npm** - **Expo CLI**(通过 `npx expo`) - 对于物理设备:手机和电脑在**同一网络**(或使用隧道 URL),不使用 localhost 时 ## 后端设置 ``` cd backend python -m venv .venv # Windows .venv\Scripts\activate # macOS / Linux # source .venv/bin/activate pip install -r requirements.txt ``` **运行 API**(绑定所有接口,以便手机/模拟器可以访问): ``` uvicorn app.main:app --reload --host 0.0.0.0 --port 8000 ``` - **OpenAPI / Swagger UI:** [http://localhost:8000/docs](http://localhost:8000/docs) - **健康检查:** `GET /health` 在引擎就绪时返回 `kem`(例如 `ML-KEM-768`)。 `KalyxEngine` 在 FastAPI 的 **lifespan** 上下文中创建,这样**导入** `app.main` 不会立即加载 liboqs;初始化(和严格 PQ 检查)在服务器启动时发生。## Docker 部署(生产环境) **`backend/Dockerfile`** 是一个**多阶段**镜像: 1. **构建阶段** — Debian **bookworm-slim**,带编译器工具链(**`build-essential`**, **`cmake`**, **`ninja-build`**, **`git`**, **`libssl-dev`**, **`pkg-config`**),以便 **`pip install -r requirements.txt`** 可以在 Linux 上正确构建或链接 **`kalyxpq[oqs]`** / **liboqs-python**。 2. **运行阶段** — 新 slim 镜像,仅包含 **`libssl3`**, **`ca-certificates`**, 复制的虚拟环境和 **`backend/app`**。进程以非特权 **`kalyx`** 用户运行(UID 10001)。**`HEALTHCHECK`** 访问 **`GET /health`**。 从**仓库根目录**构建(不是 `backend/`): ``` docker build -f backend/Dockerfile -t kalyx-vault-api . ``` 运行(设置 **`CORS_ORIGINS`** 为您的生产应用源;参见[配置](#configuration)): ``` docker run --rm -p 8000:8000 \ -e CORS_ORIGINS="https://your-expo-web-host.example,https://your-custom-domain.example" \ kalyx-vault-api ``` 对于生产环境中的 HTTPS,请在负载均衡器或反向代理(例如 Caddy、nginx、Traefik)处终止 TLS,并在容器内保持 Uvicorn 运行 HTTP。## 部署到 Render [Render](https://render.com) 运行您的 Docker 镜像并分配一个公共 **HTTPS** URL(例如 `https://kalyx-vault-api.onrender.com`)。镜像监听 Render 通过 **`PORT`** 环境变量提供的端口(Dockerfile 已使用 **`${PORT:-8000}`**)。 ### 选项 A — Blueprint(推荐) 1. 将 **Kalyx-Vault** 推送到 **GitHub**(或 Render 连接的 GitLab / Bitbucket)。 2. 在 Render 中:**New** → **Blueprint** → 连接仓库 → 选择 **`render.yaml`**。 3. 出现提示时,设置 **`CORS_ORIGINS`** 为任何将调用 API 的**浏览器**源(Expo web、本地开发等),例如: `https://your-production-site.example,http://localhost:8081,http://localhost:19006` (如果未设置,则仅允许默认的本地开发源;原生 Expo 应用通常不发送浏览器 `Origin`,因此应用可能仍能工作。) 4. 等待**首次构建** — 安装和编译 **liboqs** 在免费套餐上可能需要 **10–20+ 分钟**。 5. 在浏览器中打开 **`https://.onrender.com/health`**。您应该看到包含 `"status":"ok"` 和 **`"kem":"ML-KEM-768"`**(或类似)的 JSON。 ### 选项 B — Docker Web Service(手动) 1. **New** → **Web Service** → 连接仓库。 2. **Runtime:** **Docker**。 3. **Dockerfile 路径:** `backend/Dockerfile`。 4. **Docker 构建上下文:** **仓库根目录**(`.`)。 5. **检查路径:** `/health`。 6. 添加**环境变量** **`CORS_ORIGINS`**(同上,对 Expo web 可选但推荐)。 7. 部署并以相同方式测试 **`/health`**。 ### Render 免费套餐说明 - 服务在空闲后**关闭**;睡眠后的**首次请求**可能需要 **~30–60 秒**(冷启动)。 - 构建可能**超时**,如果超过免费套餐限制;如发生这种情况,升级实例或使用付费计划进行初始镜像构建。 ### Docker 中的 liboqs(Render) **liboqs-python** 通常在 **`OQS_INSTALL_PATH`**(默认:**`$HOME/_oqs`**)下查找 `liboqs.so`。在最小化运行时镜像中没有 **`git`** 或编译器,且 **`kalyx`** 用户没有真正的家目录——因此自动安装会失败,显示 `git: not found` 或 **No oqs shared libraries found**。 **`backend/Dockerfile`** 设置 **`OQS_INSTALL_PATH=/opt/oqs`**,在 **`pip install`** 之前从真实 git 标签(**`LIBOQS_REF`**,默认 **`0.14.0`**)**克隆并 cmake-install** **liboqs C 库**,然后运行 **`import oqs`** 使绑定加载 **`/opt/oqs/lib`**,而不使用 liboqs-python 损坏的自动安装程序。 **原因:** 最近 PyPI 上的 **`liboqs-python`** 发布版本(例如 **0.14.1**)尝试从 [open-quantum-safe/liboqs](https://github.com/open-quantum-safe/liboqs) 执行 `git clone --branch 0.14.1`,但该**分支/标签不存在**(仓库有 **0.14.0**, **0.15.0** 等)。预构建完全避免了该路径。 构建镜像时覆盖 C 库版本: ``` docker build -f backend/Dockerfile --build-arg LIBOQS_REF=0.15.0 -t kalyx-vault-api . ``` 使用与 **`kalyxpq[oqs]`** 引入的 **`liboqs-python`** 版本 **ABI 兼容**的标签(同一小版本最安全)。 ### 将移动应用指向 Render 使用您的服务 URL,**不要尾随斜杠**: ``` # Windows PowerShell cd mobile $env:EXPO_PUBLIC_API_URL="https://kalyx-vault-api.onrender.com"; npx expo start ``` ``` # macOS / Linux cd mobile EXPO_PUBLIC_API_URL=https://kalyx-vault-api.onrender.com npx expo start ``` 或创建 **`mobile/.env`**(Expo 自动加载): ``` EXPO_PUBLIC_API_URL=https://kalyx-vault-api.onrender.com ``` 对于**发布**构建,同时在 **`mobile/src/config.ts`** 中设置 **`PRODUCTION_API_URL`** 为相同 URL,或依赖 [EAS Build](https://docs.expo.dev/build-reference/variables/) / CI 中的 **`EXPO_PUBLIC_API_URL`**。## 移动应用设置 ``` cd mobile npm install npx expo start ``` 然后选择 **iOS 模拟器**、**Android 模拟器**、设备上的 **Expo Go**,或 **web**(注意:web 使用浏览器加密;RN polyfills 已为原生配置)。 ### 在笔记本电脑上运行并冒烟测试 1. **本地后端**(可选第一步):从 **`backend`** 运行 Uvicorn 在 **`0.0.0.0:8000`**,然后在 **`mobile`** 运行 `npx expo start` 并使用默认开发 URL(`http://localhost:8000`)。对于 **Android 模拟器**,使用 **`EXPO_PUBLIC_API_URL=http://10.0.2.2:8000`** 以便模拟器访问您的主机。 2. **Render 上的后端**:将 **`EXPO_PUBLIC_API_URL`** 设置为您的 **`https://…onrender.com`** URL(参见[部署到 Render](#deploy-to-render)),然后运行 `npx expo start`。 3. 在终端 UI 中,按 **`w`** 在浏览器中运行 **Expo web**(快速检查 UI 和 fetch),或如果您安装了 **Android Studio** / **Xcode** 模拟器则按 **`a`** / **`i`**,或用手机上的 **Expo Go** 扫描二维码(与笔记本电脑在同一 Wi-Fi 下)。 4. 在应用中:点击 **Initiate Handshake** — 如果 API 可达,应该成功。然后尝试 **Securely Send** 发送一条短消息。 **笔记本电脑上的要求:** **Node.js (LTS)**、**npm**,以及原生模拟器需要 **Android Studio**(SDK + 模拟器)或 **Xcode**(仅限 macOS)。如果您只想验证逻辑而不使用模拟器,**Expo web**(`w`)就足够了。 ### 随机性(React Native) `react-native-get-random-values` 在 `mobile/index.ts` 顶部导入,以便 CSPRNG 支持的操作(KEM、AES nonce)在 React Native 运行时中工作。## API 参考 ### `GET /health` | 字段 | 描述 | |-------|-------------| | `status` | 服务时返回 `"ok"` | | `kem` | 活动 KEM 名称(例如 `ML-KEM-768`)| | `strict_pq` | `"true"` | 如果密码引擎未初始化则返回 **503**(成功启动后不应发生)。### `POST /handshake` **Content-Type:** `application/json` **请求体** | 字段 | 类型 | 描述 | |-------|------|-------------| | `client_classical_public_key` | 字符串 (base64) | 32 字节 X25519 公钥 | | `client_pq_public_key` | 字符串 (base64) | ML-KEM-768 公钥(1184 字节)| **响应体** | 字段 | 类型 | 描述 | |-------|------|-------------| | `server_classical_public_key` | base64 | 服务器 X25519 公钥 | | `classical_ciphertext` | base64 | 当前 `kalyxpq` 引擎中与服务器经典公钥相同 | | `pq_ciphertext` | base64 | ML-KEM 密文 | | `server_context` | base64 | 上下文绑定哈希 | | `session_id` | 字符串 | UUID;在 `/vault` 上作为 `X-Session-Id` 发送 | | `telemetry` | 对象 | 见下文 | **`telemetry` 对象** | 字段 | 描述 | |-------|-------------| | `duration_ms` | 服务器端混合握手时间 | | `kem_algorithm` | 例如 `ML-KEM-768` | | `client_artifact_bytes` | 客户端经典 + PQ 公钥的大小 | | `server_response_bytes` | 遥测中使用的服务器响应字段大小 | | `total_overhead_bytes` | 客户端 + 服务器握手字节(近似线传输成本)| ### `POST /vault` **请求头** | 请求头 | 必需 | 描述 | |--------|----------|-------------| | `X-Session-Id` | 是 | 来自 `handshake` 响应的值 | | `Content-Type` | 是 | `application/json` | **请求体** | 字段 | 类型 | 描述 | |-------|------|-------------| | `nonce` | base64 | 12 字节 GCM nonce | | `ciphertext` | base64 | `KALX` 魔数 + GCM 密文 + tag | | `serializer` | `"msgpack"` 或 `"json"` | 必须与密封时使用的 AAD 匹配 | **解密后的明文**必须是包含 **`note`** 字段的对象(字符串或 JSON 兼容值)。 **响应体** | 字段 | 描述 | |-------|-------------| | `nonce` | base64 GCM nonce | | `ciphertext` | base64;使用会话密钥解密,无 AAD(`@kalyx_safe` 格式)| **错误情况** - **401** — 缺少或未知的 `X-Session-Id` - **400** — 密文缺少 **`KALX** 头、解密失败或载荷格式无效 ## 配置 ### 移动端 API 基础 URL(`mobile/src/config.ts`) 解析顺序: 1. **`EXPO_PUBLIC_API_URL`** — 如果设置,始终使用(EAS Secrets、`.env` 或 shell)。将其用于设备测试针对 LAN IP 或强制使用暂存 API。 2. **`__DEV__`** — 当为 `true`(Metro / Expo dev)时,默认为 **`http://localhost:8000`**。 3. **`PRODUCTION_API_URL`** — 当 `EXPO_PUBLIC_API_URL` 未设置时,用于**发布**构建。**编辑此常量**为您部署的 API 源(HTTPS,无尾随斜杠)。 示例: ``` # Android emulator → 主机 EXPO_PUBLIC_API_URL=http://10.0.2.2:8000 npx expo start # LAN 上的物理设备 EXPO_PUBLIC_API_URL=http://192.168.1.50:8000 npx expo start ``` | 环境 | 典型 URL | |-------------|-------------| | iOS 模拟器(Mac)| `http://localhost:8000`(开发默认)| | Android 模拟器 | `://10.0.2.2:8000`(通过 `EXPO_PUBLIC_API_URL`)| | 物理设备 | `http://<您的局域网IP>:8000` 或生产 HTTPS | | 应用商店 / 生产构建 | `PRODUCTION_API_URL` 或 `EXPO_PUBLIC_API_URL` | 项目为 **Android `usesCleartextTraffic`** 和 **iOS `NSAllowsLocalNetworking`** 设置为**仅用于 HTTP 开发**。生产环境应使用 **HTTPS**。 ### 后端 CORS `CORSMiddleware` 使用明确的**允许列表**(无 `*`),`allow_credentials=True`。 - **`CORS_ORIGINS`** — 允许的 **`Origin`** 值的逗号分隔列表,用于您的 **Expo web** / 浏览器客户端,例如 `https://your-app.expo.app,https://www.yourdomain.com` - 如果设置了 **`CORS_ORIGINS`**,这些源将与 Expo 和本地 web 使用的内置 **localhost / 127.0.0.1** 端口集(`8081`, `19006`, `19000`, `3000`)**合并**,以便本地开发与生产环境同时工作。 - 如果 **`CORS_ORIGINS`** **未设置**,仅允许这些**本地开发**源(适用于 API 纯或原生应用测试,浏览器源不适用)。 **注意:** React Native **原生** `fetch` 通常不发送浏览器 `Origin` 头;CORS 主要影响 **Expo Web** 和从浏览器打开 API 的工具。您仍应为您调用 API 的任何生产 web URL 设置 **`CORS_ORIGINS`**。## 项目结构 ``` Kalyx-Vault/ ├── README.md # This file ├── render.yaml # Optional Render Blueprint (Docker web service) ├── backend/ │ ├── Dockerfile # Multi-stage production image (liboqs build deps) │ ├── .dockerignore │ ├── requirements.txt # fastapi, uvicorn, kalyxpq[msgpack,oqs], pydantic │ └── app/ │ ├── __init__.py │ └── main.py # FastAPI routes, CORS, handshake, vault, kalyx_safe └── mobile/ ├── App.tsx # Security dashboard UI ├── index.ts # RNG polyfill + Expo entry ├── app.json # Expo config (cleartext / local networking for dev) └── src/ ├── config.ts # API_BASE_URL (dev / prod / EXPO_PUBLIC_API_URL) └── crypto/ └── kalyxClient.ts # Handshake + KALX sealing (protocol-compatible JS) ``` ## 故障排除 | 症状 | 可能原因 | 尝试方案 | |--------|----------------|-------------| | 后端启动时退出,显示 Strict-PQ / MOCK KEM | liboqs 未加载;模拟 KEM 激活 | 为 **liboqs-python** 安装 OS 包或 wheel;在 Linux/macOS/WSL 上运行 | | 容器中出现 `No oqs shared libraries found` / `git: not found` | liboqs 不在镜像中;运行时自动安装需要 git + 构建工具 | 使用提供的 **Dockerfile**(`OQS_INSTALL_PATH=/opt/oqs`,在构建阶段预构建)。在裸金属上,安装 liboqs 或使用构建依赖运行一次 `import oqs` 以便它可以安装 | | 移动端:网络请求失败 | 基础 URL 错误或防火墙 | 使用 LAN IP 或 `10.0.2.2`;在主机上允许端口 8000 | | 握手 400 / 解密错误 | JS 和服务器之间的 ML-KEM 不匹配 | 确保服务器报告 `ML-KEM-768`;如果向量变化则更新 `@noble/post-quantum` | | iOS 设备无法访问 `http://localhost:8000` | localhost 是手机本身 | 使用主机 IP + `EXPO_PUBLIC_API_URL` | ## 安全说明 此仓库是一个**演示**: - **会话密钥**和**笔记**保存在服务器的**内存**中;重启进程会清除它们。 - **无认证**机制,除了拥有 `session_id`;将其视为实验室,而非多租户 vault。 - **CORS** 是明确的源列表(通过 **`CORS_ORIGINS`** 的生产 URL 加上默认的本地开发源)。**明文 HTTP** 仅用于移动端本地开发。 - JavaScript 中的客户端密码学**不能**替代高保障部署的硬件密钥或经过审计的原生模块。 有关威胁模型和库 API,请参阅 **[kalyxpq PyPI 页面](https://pypi.org/project/kalyxpq/)** 和上游 **[Kalyx-PQ](https://github.com/9Elliot/Kalyx-PQ)** 文档。## 参考资料 - [kalyxpq on PyPI](https://pypi.org/project/kalyxpq/) - [FastAPI](https://fastapi.tiangolo.com/) - [Expo](https://docs.expo.dev/) - [@noble/post-quantum](https://www.npmjs.com/package/@noble/post-quantum)(ML-KEM-768) - [NIST FIPS 203 (ML-KEM)](https://csrc.nist.gov/publications/detail/fips/203/final) ## 许可证 遵循您的依赖项的许可证(**kalyxpq** 依据 PyPI 为 Apache-2.0;**noble** 库为 MIT)。如果您将此仓库作为产品分发,请添加您自己的项目许可证。
标签:AES-256, AES-GCM, API安全, AV绕过, E2EE, Expo, FastAPI, Harvest Now Decrypt Later, HKDF, Hybrid Key Exchange, JSON输出, kalyxpq, Key Derivation, ML-KEM-768, Msgpack, NIST后量子密码标准, noble密码库, Post-Quantum Cryptography, Python, Quantum-Safe, React Native, Session Encryption, SHA3-256, TypeScript, X25519, 会话加密, 后量子密码学, 安全插件, 密钥派生, 无后门, 混合密钥交换, 漏洞评估, 目录枚举, 移动安全, 端到端加密, 自动化攻击, 请求拦截, 逆向工具, 量子安全