mirror of
https://github.com/pandorafuture/wx-cli.git
synced 2026-08-29 04:00:55 +00:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66078e4a3b | ||
|
|
b31a416f20 | ||
|
|
9fcf3a033b | ||
|
|
125c762727 | ||
|
|
0f22f335e5 | ||
|
|
76cf16ffef | ||
|
|
4d8272029c | ||
|
|
eb91993ca5 |
@@ -13,7 +13,7 @@ jobs:
|
||||
fmt:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
clippy:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
@@ -31,6 +31,6 @@ jobs:
|
||||
test:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- run: cargo test
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
mv wx-cli-${{ github.ref_name }}-macos-arm64.tar.gz ${{ github.workspace }}/
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
uses: softprops/action-gh-release@v3
|
||||
with:
|
||||
files: wx-cli-${{ github.ref_name }}-macos-arm64.tar.gz
|
||||
body: ${{ steps.changelog.outputs.body }}
|
||||
|
||||
Generated
+363
-322
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
# wx-cli
|
||||
|
||||
WeChat macOS 数据库解密与查询工具。支持通过 `key scan`(Mach VM 内存扫描)或 `key extract`(LLDB hook)提取密钥,解密并查询 WeChat 4.1.7.x / 4.1.8.x 的 Apple SEE 加密 SQLite 数据库。
|
||||
WeChat macOS 数据库解密与查询工具。支持通过 `key extract`(LLDB hook)提取密钥,解密并查询 WeChat 4.1.7.x / 4.1.8.x 的 Apple SEE 加密 SQLite 数据库。
|
||||
|
||||
## 支持范围
|
||||
|
||||
@@ -9,13 +9,7 @@ WeChat macOS 数据库解密与查询工具。支持通过 `key scan`(Mach VM
|
||||
|
||||
## 前置条件
|
||||
|
||||
密钥提取**需要 SIP 关闭**(SIP enabled 时 `task_for_pid` 被内核拒绝,即使 root 也不行)。如果你已有密钥,可以跳过 SIP 要求,直接用 `key set` 手动录入。
|
||||
|
||||
| 条件 | key scan | key extract |
|
||||
|------|----------|-------------|
|
||||
| SIP disabled + sudo | **可用** | **可用,但通常不需要 sudo** |
|
||||
| SIP disabled + 无 sudo | 不可用 | **可用(推荐)** |
|
||||
| SIP enabled | 不可用 | 不可用 |
|
||||
密钥提取**需要 SIP 关闭**(SIP enabled 时 `task_for_pid` 被内核拒绝,即使 root 也不行),通常不需要 sudo。如果你已有密钥,可以跳过 SIP 要求,直接用 `key set` 手动录入。
|
||||
|
||||
`key extract`(LLDB 方式)还需要:
|
||||
|
||||
@@ -25,6 +19,24 @@ WeChat macOS 数据库解密与查询工具。支持通过 `key scan`(Mach VM
|
||||
|
||||
## 安装
|
||||
|
||||
### 从 Release 下载(推荐)
|
||||
|
||||
前往 [Releases](https://github.com/pandorafuture/wx-cli/releases/latest) 下载预编译二进制(macOS arm64),或使用命令行:
|
||||
|
||||
```bash
|
||||
# 下载最新 release
|
||||
curl -fSL "$(curl -fsSL https://api.github.com/repos/pandorafuture/wx-cli/releases/latest \
|
||||
| grep -o '"browser_download_url": "[^"]*macos-arm64[^"]*"' \
|
||||
| cut -d'"' -f4)" -o wx-cli.tar.gz
|
||||
tar xzf wx-cli.tar.gz
|
||||
|
||||
# 安装到 PATH
|
||||
mkdir -p ~/.local/bin
|
||||
mv wx-cli ~/.local/bin/
|
||||
chmod +x ~/.local/bin/wx-cli
|
||||
wx-cli --version
|
||||
```
|
||||
|
||||
### 从源码构建
|
||||
|
||||
```bash
|
||||
@@ -48,10 +60,13 @@ source ~/.zshrc
|
||||
wx-cli --version
|
||||
```
|
||||
|
||||
如果目标机器在远程(例如 VM),本机构建后传输即可,远程不需要 Rust 工具链:
|
||||
|
||||
### AI 编程助手集成
|
||||
|
||||
本项目提供 [Agent Skill](https://skills.sh),安装后 Claude Code、Codex、Cursor 等 AI 编程助手可直接协助查询微信数据:
|
||||
|
||||
```bash
|
||||
scp target/release/wx-cli user@remote:~/.local/bin/wx-cli
|
||||
npx skills add pandorafuture/wx-cli
|
||||
```
|
||||
|
||||
## 使用
|
||||
@@ -66,17 +81,14 @@ wx-cli status # 查看 WeChat 运行状态和所有账号密钥/缓存状
|
||||
### 2. 提取密钥
|
||||
|
||||
```bash
|
||||
# 方式 A(推荐):内存扫描 — 不重启 WeChat,需要 sudo
|
||||
sudo wx-cli key scan
|
||||
|
||||
# 方式 B:LLDB hook — 会重启 WeChat,通常不需要 sudo
|
||||
# LLDB hook 提取密钥 — 会重启 WeChat,通常不需要 sudo
|
||||
wx-cli key extract --timeout 120
|
||||
|
||||
# 查看已保存的密钥
|
||||
wx-cli key list
|
||||
```
|
||||
|
||||
`key scan` 从 WeChat 进程内存中提取已缓存的数据库密钥;`key extract` 通过 LLDB hook 捕获 PBKDF2 调用获取原始密钥,覆盖范围更广。
|
||||
`key extract` 通过 LLDB hook 捕获 PBKDF2 调用获取原始密钥,覆盖所有数据库。
|
||||
|
||||
手动设置密钥:
|
||||
|
||||
@@ -104,7 +116,7 @@ wx-cli query 张三 --limit 20 # 查某人的消息
|
||||
wx-cli search 周末 --limit 20 # 全局关键词搜索
|
||||
wx-cli query 张三 --type text # 按消息类型过滤
|
||||
wx-cli query 周末爬山群 # 群聊消息
|
||||
wx-cli export 张三 -o /tmp/export --format json # 导出会话
|
||||
wx-cli export 张三 -o /tmp/export --all --format json # 导出会话
|
||||
wx-cli watch --poll --poll-ms 3000 # 实时监听新消息
|
||||
```
|
||||
|
||||
@@ -139,10 +151,10 @@ REST 端点:`/api/v1/health`、`/api/v1/sessions`、`/api/v1/contacts`、`/api
|
||||
|------|------|
|
||||
| `wx-cli status` | 查看 WeChat 运行状态 |
|
||||
| `wx-cli doctor` | 检查环境(SIP 等) |
|
||||
| `sudo wx-cli key scan` | 内存扫描提取密钥(推荐) |
|
||||
| `wx-cli key extract` | LLDB hook 提取密钥 |
|
||||
| `wx-cli key list` | 查看已保存密钥 |
|
||||
| `wx-cli key set <account> <key>` | 手动设置密钥 |
|
||||
| `wx-cli key set-image <account> <image-key>` | 手动设置图片密钥 |
|
||||
| `wx-cli decrypt` | 解密数据库 |
|
||||
| `wx-cli sessions` | 最近会话列表 |
|
||||
| `wx-cli contacts --search <名字>` | 搜索联系人 |
|
||||
@@ -160,7 +172,7 @@ REST 端点:`/api/v1/health`、`/api/v1/sessions`、`/api/v1/contacts`、`/api
|
||||
|
||||
## Contact Hiding
|
||||
|
||||
按账号隐藏指定联系人、群聊或带特定标签的联系人。启用后,查询、导出、监控和服务接口默认应用隐藏规则。
|
||||
按账号隐藏指定联系人、群聊或带特定标签的联系人。启用后,查询、导出、监控等命令默认应用隐藏规则(全文搜索除外)。
|
||||
|
||||
配置文件:`~/Library/Application Support/wx-cli/config/settings.toml`
|
||||
|
||||
@@ -190,7 +202,7 @@ ignore_tags = ["同事", "客户"]
|
||||
wx-cli/
|
||||
├── crates/
|
||||
│ ├── wx-decrypt/ # 核心解密库(KDF、逐页解密、整库解密)
|
||||
│ ├── wx-keychain/ # 密钥提取(LLDB / Mach VM)与本地存储
|
||||
│ ├── wx-keychain/ # 密钥提取(LLDB hook)与本地存储
|
||||
│ ├── wx-cli/ # CLI 入口
|
||||
│ ├── wx-db/ # 数据库查询(联系人、消息、会话、群聊)
|
||||
│ ├── wx-media/ # 媒体解密(图片、语音、视频)
|
||||
@@ -205,7 +217,7 @@ wx-cli/
|
||||
|
||||
- 确认 WeChat 已弹出登录界面并完成登录
|
||||
- 增加超时:`--timeout 300`
|
||||
- 检查日志:`$TMPDIR/wx-cli/lldb/wechat_lldb_output.txt`
|
||||
- 检查日志:`$TMPDIR/wx-cli/lldb/wx_cli_lldb_output.txt`
|
||||
|
||||
### SIP / DevToolsSecurity 报错
|
||||
|
||||
|
||||
@@ -0,0 +1,392 @@
|
||||
---
|
||||
name: wx-cli
|
||||
description: Use when the user asks about their WeChat messages, contacts, conversations, chat history, or needs to decrypt/search WeChat data. Provides structured CLI commands for querying encrypted WeChat macOS databases.
|
||||
---
|
||||
|
||||
# wx-cli — WeChat 数据查询工具
|
||||
|
||||
## Overview
|
||||
|
||||
`wx-cli` 解密并查询 WeChat macOS 加密数据库。零参数即可运行——自动检测账号、密钥和数据目录。
|
||||
|
||||
**核心原则:** 用 `--format json` 获取结构化数据供分析;用默认 text 格式展示给用户。
|
||||
|
||||
## When to Use
|
||||
|
||||
- 用户问"最近跟谁聊了什么"、"某人发过什么消息"
|
||||
- 用户要搜索聊天记录中的关键词
|
||||
- 用户要查看联系人信息
|
||||
- 用户要解密数据库或图片
|
||||
- 用户提到 WeChat / 微信相关数据需求
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| 需求 | 命令 |
|
||||
|------|------|
|
||||
| 当前状态 | `wx-cli status` |
|
||||
| **密钥提取(推荐)** | `wx-cli key extract` |
|
||||
| 手动设置数据库密钥 | `wx-cli key set <account_id> <hex_key>` |
|
||||
| 手动设置图片密钥 | `wx-cli key set-image <account_id> <image_key>` |
|
||||
| 已存密钥列表 | `wx-cli key list` |
|
||||
| 解密数据库 | `wx-cli decrypt` |
|
||||
| 增量解密 | `wx-cli decrypt --incremental` |
|
||||
| 解密图片(自动推导密钥)| `wx-cli decode-image <路径> -d <data_dir>` |
|
||||
| 解密图片(手动 V2 key)| `wx-cli media decrypt-dat <路径> --v2-key <key>` |
|
||||
| 解密图片(KeyStore)| `wx-cli decode-image <路径> --account <account_id>` |
|
||||
| 提取语音 | `wx-cli media extract-voice --media-dir <dir> <svr_id>` |
|
||||
| 提取语音(原始 SILK)| `wx-cli media extract-voice --media-dir <dir> <svr_id> --raw` |
|
||||
| 解密视频号视频 | `wx-cli media decrypt-video <文件> --seed <seed>` |
|
||||
| 查询 hardlink 路径 | `wx-cli media resolve-path --db <hardlink.db> <key>` |
|
||||
| 检查 DB 文件 | `wx-cli info <db_file>` |
|
||||
| 版本信息 | `wx-cli --version` |
|
||||
| 最近会话列表 | `wx-cli sessions` |
|
||||
| 查看系统路径 | `wx-cli paths` |
|
||||
| 查看系统路径(JSON) | `wx-cli paths --json` |
|
||||
| 忽略隐藏配置查看完整会话列表 | `wx-cli sessions --show-hidden` |
|
||||
| 搜索联系人 | `wx-cli contacts --search <关键词>` |
|
||||
| 忽略隐藏配置搜索联系人 | `wx-cli contacts --search <关键词> --show-hidden` |
|
||||
| 查某人的消息 | `wx-cli query <联系人名/账号ID>` |
|
||||
| 忽略隐藏配置查看消息 | `wx-cli query <联系人> --show-hidden` |
|
||||
| 定位某条消息上下文 | `wx-cli query <联系人> --around-sort-seq <seq> --context 10` |
|
||||
| 按 server_id 定位上下文 | `wx-cli query <联系人> --around-server-id <id> --context 10` |
|
||||
| 增量拉取新消息 | `wx-cli query <联系人> --after-sort-seq <seq> --limit 20` |
|
||||
| 全局搜索关键词 | `wx-cli search <关键词>` |
|
||||
| **导出会话(TXT,默认并行)** | `wx-cli export <联系人> -o <目录> --all` |
|
||||
| **导出会话(JSON,默认并行)** | `wx-cli export <联系人> -o <目录> --all --format json` |
|
||||
| 忽略隐藏配置导出会话 | `wx-cli export <联系人> -o <目录> --all --show-hidden` |
|
||||
| 导出(无媒体) | `wx-cli export <联系人> -o <目录> --all --no-media` |
|
||||
| **启动 HTTP API** | `wx-cli server run` |
|
||||
| 查看 HTTP 服务状态 | `wx-cli server status` |
|
||||
| 停止 HTTP 服务 | `wx-cli server stop` |
|
||||
| 重启 HTTP 服务 | `wx-cli server restart` |
|
||||
| HTTP API(远程) | `wx-cli server run --host 0.0.0.0 --token <secret>` |
|
||||
| 实时监听会话变化 | `wx-cli watch` |
|
||||
| 实时监听(忽略隐藏配置) | `wx-cli watch --show-hidden` |
|
||||
| 实时监听(轮询) | `wx-cli watch --poll --poll-ms 3000` |
|
||||
| 实时监听(文件事件模式) | `wx-cli watch --fsnotify` |
|
||||
| 前置条件检查 | `wx-cli doctor` |
|
||||
| 前置条件检查 + 修复建议 | `wx-cli doctor --fix` |
|
||||
|
||||
## 前置条件
|
||||
|
||||
密钥提取**需要 SIP 禁用**,通常不需要 sudo。SIP enabled 时 `task_for_pid` 被内核拒绝(kern_return=5),即使 root 也不行。检查 SIP 状态:`csrutil status`。禁用需在 Recovery Mode 执行 `csrutil disable`。
|
||||
|
||||
## 常用工作流
|
||||
|
||||
### 0. 首次使用:提取密钥并解密
|
||||
|
||||
WeChat 数据库是加密的,查询前必须先有密钥。**需要 SIP 禁用**(SIP enabled 时 `task_for_pid` 被内核拒绝):
|
||||
|
||||
```bash
|
||||
# LLDB hook 提取密钥 — 会重启 WeChat,需要 LLDB + python3,通常不需要 sudo
|
||||
wx-cli key extract --timeout 120
|
||||
```
|
||||
|
||||
`key extract` 拿到的是完整数据库密钥,**覆盖所有数据库**。后续多数查询可直接读取加密数据库,无需先执行 `decrypt`。
|
||||
|
||||
提取后验证:
|
||||
|
||||
```bash
|
||||
# 查看已存密钥(`raw=yes` 表示已保存完整数据库密钥)
|
||||
wx-cli key list
|
||||
|
||||
# 有完整数据库密钥时:query/sessions/search/contacts/server run/watch 可直接读取加密数据库,无需先 decrypt
|
||||
|
||||
# 如需明文导出
|
||||
wx-cli decrypt
|
||||
wx-cli decrypt --incremental
|
||||
```
|
||||
|
||||
手动设置密钥:
|
||||
|
||||
```bash
|
||||
wx-cli key set <account_id> 0123456789abcdef... # 数据库密钥(32 字节 hex)
|
||||
wx-cli key set-image <account_id> abcdefghijklmnop # 图片 AES 密钥(V2 格式)
|
||||
```
|
||||
|
||||
**密钥类型说明:**
|
||||
|
||||
| 密钥 | 用途 | 提取方式 | 覆盖范围 |
|
||||
|------|------|---------|---------|
|
||||
| 完整数据库密钥(`raw_key`) | 解密 SQLite 数据库 | `key extract`(LLDB hook) | 所有 DB |
|
||||
| Image key(16 bytes) | 解密 V2 格式 .dat 图片 | `decode-image -d <data_dir>` 自动推导 | — |
|
||||
|
||||
### 1. 查看最近聊天
|
||||
|
||||
```bash
|
||||
wx-cli sessions --limit 10
|
||||
# 返回按时间倒序的会话列表,含联系人显示名和最后一条消息摘要
|
||||
```
|
||||
|
||||
### 2. 查找某人并读消息
|
||||
|
||||
```bash
|
||||
# 先搜联系人(支持昵称、备注、wxid、微信号、手机号等模糊匹配)
|
||||
wx-cli contacts --search 张三
|
||||
|
||||
# 用搜到的名字或 wxid 查消息
|
||||
wx-cli query 张三 --limit 20
|
||||
|
||||
# 用 JSON 获取结构化数据
|
||||
wx-cli query 张三 --format json --limit 50
|
||||
```
|
||||
|
||||
### 2a. 联系人隐藏规则
|
||||
|
||||
配置文件:`~/Library/Application Support/wx-cli/config/settings.toml`
|
||||
|
||||
```toml
|
||||
[accounts."<account_id>"]
|
||||
ignore_contacts = ["wxid_hidden_contact"]
|
||||
ignore_tags = ["同事"]
|
||||
```
|
||||
|
||||
- `query` / `sessions` / `contacts` / `export` / `watch` 支持 `--show-hidden` 忽略隐藏配置
|
||||
- `search` 当前**不会自动应用隐藏配置**
|
||||
- **隐私优先:** 当输出里出现 `[消息已隐藏]`,除非用户明确要求,agent 不应主动追加 `--show-hidden`
|
||||
|
||||
### 3. 全局搜索关键词
|
||||
|
||||
```bash
|
||||
wx-cli search 周末 --limit 20
|
||||
# 优先查询 WeChat 自带的全文索引库,搜索通常在亚秒级完成
|
||||
```
|
||||
|
||||
**搜索语义:**
|
||||
- 中文按字拆分;英文支持 Porter stemming
|
||||
- 多词搜索为 AND 逻辑
|
||||
- `stats.scanned=0` 表示走微信内置全文索引;`stats.scanned>0` 表示退回到全量扫描
|
||||
|
||||
### 4. 按条件过滤消息
|
||||
|
||||
```bash
|
||||
wx-cli query 张三 --type text # 按类型:text/image/voice/video/emoji/app/system/revoke
|
||||
wx-cli query 张三 --since 1772600000 --until 1772700000 # 时间范围(Unix 秒)
|
||||
```
|
||||
|
||||
### 4b. 锚点上下文查询
|
||||
|
||||
```bash
|
||||
wx-cli query 张三 --around-sort-seq 1773421188000 --context 10 # 按 sort_seq 定位前后 10 条
|
||||
wx-cli query 张三 --around-server-id 5455993825313690274 --context 10 # 按 server_id 定位
|
||||
wx-cli query 张三 --after-sort-seq 1773421188000 --limit 20 # 增量拉取新消息
|
||||
```
|
||||
|
||||
**行为规则:**
|
||||
- 锚点查询始终按升序返回,`--order desc` 会被忽略
|
||||
- `--context` 默认 50,仅对 `around-*` 有效
|
||||
- `around-*` 参数与 `--since`/`--until`/`--all` 互斥
|
||||
- `--around-sort-seq`、`--around-server-id`、`--after-sort-seq` 三者互斥
|
||||
|
||||
### 5. 群聊查询
|
||||
|
||||
```bash
|
||||
wx-cli query 18819405230@chatroom --limit 10 # 群聊 ID
|
||||
wx-cli query 周末爬山群 # 或群名模糊匹配
|
||||
```
|
||||
|
||||
### 6. 导出会话
|
||||
|
||||
```bash
|
||||
wx-cli export 张三 -o /tmp/export/ --all # TXT 格式,全部消息
|
||||
wx-cli export 张三 -o /tmp/export/ --all --format json # JSON 格式
|
||||
wx-cli export 张三 -o /tmp/export/ --all --no-media # 跳过媒体文件
|
||||
wx-cli export 张三 -o /tmp/export/ --all --show-emoji # 显示表情细节
|
||||
```
|
||||
|
||||
**排序默认 `asc`**(时间正序),与 `query` 默认 `desc` 相反。
|
||||
|
||||
### 7. 图片解密与转码
|
||||
|
||||
```bash
|
||||
# 推荐:-d 自动推导 V2 密钥
|
||||
wx-cli decode-image input.dat -d <account_data_dir> -o output.png
|
||||
|
||||
# 批量目录
|
||||
wx-cli decode-image /path/to/dat_dir/ -d <account_data_dir> -o /tmp/output/
|
||||
|
||||
# 直接传入 V2 AES key
|
||||
wx-cli media decrypt-dat input.dat --v2-key abcdefghijklmnop -o output.png
|
||||
```
|
||||
|
||||
### 8. 语音提取
|
||||
|
||||
```bash
|
||||
wx-cli media extract-voice --media-dir <dir> <svr_id> -o voice.mp3 # 默认 MP3(需 ffmpeg)
|
||||
wx-cli media extract-voice --media-dir <dir> <svr_id> --raw -o voice.silk # 原始 SILK
|
||||
```
|
||||
|
||||
### 8b. Hardlink 路径查询
|
||||
|
||||
```bash
|
||||
wx-cli media resolve-path --db /path/to/hardlink.db <md5_key> # 图片
|
||||
wx-cli media resolve-path --db /path/to/hardlink.db --media-type video <key> # 视频
|
||||
wx-cli media resolve-path --db /path/to/hardlink.db --media-type file <key> # 文件
|
||||
```
|
||||
|
||||
### 9. 视频号视频解密
|
||||
|
||||
```bash
|
||||
wx-cli media decrypt-video encrypted.bin --seed 2105122989 -o video.mp4 # 十进制 seed
|
||||
wx-cli media decrypt-video encrypted.bin --seed 0x7d844e8d -o video.mp4 # 十六进制 seed
|
||||
```
|
||||
|
||||
### 10. HTTP API 服务
|
||||
|
||||
```bash
|
||||
wx-cli server run # 本地启动(默认 127.0.0.1:9100)
|
||||
wx-cli server run --host 0.0.0.0 --token mysecret # 远程访问(必须设 token)
|
||||
wx-cli server status / stop / restart # 管理服务
|
||||
```
|
||||
|
||||
### 10a. CLI 自动复用已运行的 server
|
||||
|
||||
`sessions`、`contacts`、`query`、`search` 默认会先尝试连接本机 `http://127.0.0.1:9100`,如果 server 可用就走 HTTP,否则回退到本地直查。
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| `--server-url <url>` | 覆盖默认地址 |
|
||||
| `--server-token <token>` | Bearer token |
|
||||
| `--server-only` | 只走远程,不回退 |
|
||||
| `--no-server` | 强制本地查询 |
|
||||
|
||||
### 10b. REST 端点(只读)
|
||||
|
||||
| 端点 | 说明 | 关键参数 |
|
||||
|------|------|---------|
|
||||
| `GET /api/v1/health` | 健康探测 | 无 |
|
||||
| `GET /api/v1/sessions` | 会话列表 | `limit`, `offset`, `order`, `show_hidden` |
|
||||
| `GET /api/v1/contacts` | 联系人列表 | `limit`, `offset`, `search`, `show_hidden` |
|
||||
| `GET /api/v1/messages` | 消息查询 | `contact`(必填), `limit`, `offset`, `since`, `until`, `type`, `order`, `around_sort_seq`, `around_server_id`, `after_sort_seq`, `context`, `show_hidden` |
|
||||
| `GET /api/v1/media` | 媒体内容直出 | `server_id`(必填), `talker`(必填), `format=ogg\|mp3`(仅语音) |
|
||||
| `GET /api/v1/search` | 全文搜索 | `q`(必填), `limit`, `offset` |
|
||||
| `GET /api/v1/events` | SSE 事件流 | 无 |
|
||||
|
||||
**认证:** 带 `--token` 启动时须携带 `Authorization: Bearer <token>`。`--host` 不是本机地址时 `--token` 必填。
|
||||
|
||||
当前 HTTP API 为**只读**,没有 send/reply/webhook 等写接口。
|
||||
|
||||
### 11. 实时监听
|
||||
|
||||
```bash
|
||||
wx-cli watch # 默认启动
|
||||
wx-cli watch --poll --poll-ms 5000 # 自定义轮询间隔
|
||||
wx-cli watch --format json # JSON 格式(每行一个 JSON 对象)
|
||||
wx-cli watch --show-hidden # 忽略隐藏配置
|
||||
```
|
||||
|
||||
### 12. 前置条件检查
|
||||
|
||||
```bash
|
||||
wx-cli doctor # 列出各项检查结果(SIP、DevToolsSecurity、_developer、lldb、python3)
|
||||
wx-cli doctor --fix # 对 FAIL 项输出修复命令
|
||||
```
|
||||
|
||||
## 错误自动恢复
|
||||
|
||||
**`error: no key for account <account_id>`**
|
||||
|
||||
```bash
|
||||
wx-cli status # 1. 确认 WeChat 状态
|
||||
wx-cli key extract --timeout 120 # 2. 提取密钥
|
||||
wx-cli key list # 3. 验证密钥
|
||||
wx-cli sessions # 4. 重试查询
|
||||
```
|
||||
|
||||
**`task_for_pid failed (kern_return=5)`** — SIP 启用,需在 Recovery Mode 执行 `csrutil disable`。
|
||||
|
||||
**`warning: ffmpeg not found`** — 安装 ffmpeg:`brew install ffmpeg`,或 `FFMPEG_PATH=/path/to/ffmpeg wx-cli ...`
|
||||
|
||||
## JSON 输出结构
|
||||
|
||||
所有查询命令加 `--format json` 返回统一信封:
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [...],
|
||||
"paging": { "offset": 0, "limit": 20, "returned": 20, "total": 103, "has_more": true },
|
||||
"stats": { "scanned": 103, "skipped": 0, "elapsed_ms": 3 }
|
||||
}
|
||||
```
|
||||
|
||||
| 命令 | item 关键字段 |
|
||||
|------|-------------|
|
||||
| sessions | `username`, `display_name`, `summary`, `sort_timestamp`, `direction?` |
|
||||
| query | `sort_seq`, `server_id`, `msg_type`, `sender`, `content`, `direction` |
|
||||
| contacts | `user_name`, `alias`, `remark`, `nick_name`, `phone`, `labels` |
|
||||
| search | `server_id`, `talker`, `sender`, `snippet`, `hit_type` |
|
||||
|
||||
**易混淆字段:**
|
||||
- contacts 用 `user_name`(下划线),sessions 用 `username`(无下划线)
|
||||
- message 的 `sender` 才是消息级 self/other 判断依据
|
||||
- `/api/v1/health` 的 `current_account.wxid` 是判断"我发的"的主事实源
|
||||
|
||||
**JSON 编程使用:** JSON 走 stdout,诊断信息走 stderr。分离采集才能正确解析:
|
||||
|
||||
```bash
|
||||
OUTPUT=$(wx-cli query 张三 --format json --limit 5)
|
||||
echo "$OUTPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d['items']))"
|
||||
```
|
||||
|
||||
## 分页与排序
|
||||
|
||||
```bash
|
||||
wx-cli query 张三 --format json --limit 10 --offset 0 # 第一页
|
||||
wx-cli query 张三 --format json --limit 10 --offset 10 # 第二页
|
||||
wx-cli query 张三 --order asc # 时间正序
|
||||
wx-cli query 张三 --all # 获取全部消息(上限 20,000)
|
||||
```
|
||||
|
||||
## 消息类型标签(type=49 结构化解析)
|
||||
|
||||
| 变体 | sub_type | 输出 |
|
||||
|------|----------|------|
|
||||
| Link | 4, 5, 7, 92 | `[链接] <标题>` |
|
||||
| File | 6 | `[文件] <文件名>` |
|
||||
| MiniProgram | 33, 36 | `[小程序] <名称>` |
|
||||
| MergedMessages | 19 | `[聊天记录] <标题>` |
|
||||
| Quote | 57 | `[引用 @发送者: 原文] 回复文本` |
|
||||
| Transfer | 2000 | `[转账] ¥金额` |
|
||||
| RedEnvelope | 2001, 2003 | `[红包] <标题>` |
|
||||
| ChannelVideo | 51, 63 | `[视频号] <标题>` |
|
||||
| Pat | 62 | `[拍一拍]` |
|
||||
|
||||
JSON `content` 字段为 tagged union:外层 key 是变体名,值是结构化字段。
|
||||
|
||||
## 文件路径
|
||||
|
||||
| 类别 | 路径(macOS) | 用途 |
|
||||
|------|---------------|------|
|
||||
| Config | `~/Library/Application Support/wx-cli/config/` | 密钥、设置 |
|
||||
| Cache | `~/Library/Caches/wx-cli/` | 解密后数据库 |
|
||||
| State | `~/Library/Application Support/wx-cli/state/` | 服务运行时 |
|
||||
| Logs | `~/Library/Logs/wx-cli/` | 服务日志 |
|
||||
| Temp | `$TMPDIR/wx-cli/` | 密钥提取临时文件 |
|
||||
|
||||
使用 `wx-cli paths` 查看所有路径。
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 所有命令自动检测账号和密钥,通常无需 `--account` 或 `--key`
|
||||
- `query` 支持 wxid、chatroom ID、filehelper,也支持中文名模糊匹配
|
||||
- `search` 直接查询 WeChat 自带 `message_fts.db`,无需单独建索引
|
||||
- `search` 当前不会自动应用隐藏配置,也没有 `--show-hidden`
|
||||
- `--all` 覆盖 `--limit`(当前上限 20,000);`export --all` 会按批次拉完全部结果
|
||||
- `--limit 0` 会退回到该命令的默认值
|
||||
- `server run` 启动后会自动增量刷新解密缓存
|
||||
|
||||
## 诊断策略
|
||||
|
||||
当查询返回空结果时,**不要反复换参数重试**:
|
||||
|
||||
```bash
|
||||
# 1. 检查 stats.skipped
|
||||
wx-cli query <contact> --all --format json 2>/dev/null | \
|
||||
python3 -c "import json,sys; d=json.load(sys.stdin); print(f'items={len(d[\"items\"])}, skipped={d[\"stats\"][\"skipped\"]}')"
|
||||
|
||||
# 2. skipped > 0 → 重新解密
|
||||
wx-cli decrypt
|
||||
|
||||
# 3. items=0 且 skipped=0 → 检查联系人
|
||||
wx-cli contacts --search <name> --format json
|
||||
```
|
||||
@@ -19,13 +19,13 @@ wx-media = { path = "../wx-media" }
|
||||
wx-monitor = { path = "../wx-monitor" }
|
||||
wx-context = { path = "../wx-context" }
|
||||
wx-paths = { path = "../wx-paths" }
|
||||
rusqlite = { version = "0.32", features = ["bundled"] }
|
||||
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
futures-util = "0.3"
|
||||
hex = "0.4"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
toml = "0.8"
|
||||
toml = "1.1"
|
||||
ureq = { version = "2", features = ["json"] }
|
||||
url = "2"
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "macros", "signal"] }
|
||||
@@ -45,4 +45,4 @@ tempfile = "3"
|
||||
silk-rs = "0.2"
|
||||
|
||||
[build-dependencies]
|
||||
vergen-gitcl = { version = "9", features = ["build"] }
|
||||
vergen-gitcl = { version = "10", features = ["build"] }
|
||||
|
||||
@@ -11,7 +11,7 @@ wx-paths = { path = "../wx-paths" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
thiserror = "2"
|
||||
hex = "0.4"
|
||||
rusqlite = { version = "0.32", features = ["bundled-sqlcipher"] }
|
||||
rusqlite = { version = "0.40", features = ["bundled-sqlcipher"] }
|
||||
dashmap = "6"
|
||||
rayon = "1"
|
||||
rust-stemmers = "1"
|
||||
@@ -19,8 +19,8 @@ rust-stemmers = "1"
|
||||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
filetime = "0.2"
|
||||
aes = "0.8"
|
||||
cbc = "0.1"
|
||||
aes = "0.9"
|
||||
cbc = "0.2"
|
||||
hmac = "0.12"
|
||||
sha2 = "0.10"
|
||||
pbkdf2 = { version = "0.12", features = ["hmac"] }
|
||||
|
||||
@@ -944,7 +944,7 @@ mod tests {
|
||||
salt: &[u8; 16],
|
||||
params: &wx_decrypt::CryptoParams,
|
||||
) {
|
||||
use aes::cipher::{BlockEncryptMut, KeyIvInit};
|
||||
use aes::cipher::{BlockModeEncrypt, KeyIvInit};
|
||||
use hmac::{Hmac, Mac};
|
||||
use sha2::Sha512;
|
||||
|
||||
@@ -963,7 +963,7 @@ mod tests {
|
||||
let mut ciphertext = plaintext;
|
||||
type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
|
||||
Aes256CbcEnc::new((&enc_key).into(), (&iv).into())
|
||||
.encrypt_padded_mut::<aes::cipher::block_padding::NoPadding>(&mut ciphertext, data_size)
|
||||
.encrypt_padded::<aes::cipher::block_padding::NoPadding>(&mut ciphertext, data_size)
|
||||
.unwrap();
|
||||
|
||||
let mut page = Vec::with_capacity(params.page_size);
|
||||
|
||||
@@ -8,7 +8,7 @@ fn build_encrypted_db(
|
||||
salt: &[u8; 16],
|
||||
params: &wx_decrypt::CryptoParams,
|
||||
) {
|
||||
use aes::cipher::{BlockEncryptMut, KeyIvInit};
|
||||
use aes::cipher::{BlockModeEncrypt, KeyIvInit};
|
||||
use hmac::{Hmac, Mac};
|
||||
use sha2::Sha512;
|
||||
|
||||
@@ -22,7 +22,7 @@ fn build_encrypted_db(
|
||||
let mut ciphertext = plaintext;
|
||||
type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
|
||||
Aes256CbcEnc::new((&enc_key).into(), (&iv).into())
|
||||
.encrypt_padded_mut::<aes::cipher::block_padding::NoPadding>(&mut ciphertext, data_size)
|
||||
.encrypt_padded::<aes::cipher::block_padding::NoPadding>(&mut ciphertext, data_size)
|
||||
.unwrap();
|
||||
|
||||
let mut page = Vec::with_capacity(params.page_size);
|
||||
|
||||
@@ -4,7 +4,7 @@ version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
rusqlite = { version = "0.32", features = ["bundled-sqlcipher"] }
|
||||
rusqlite = { version = "0.40", features = ["bundled-sqlcipher"] }
|
||||
prost = "0.13"
|
||||
zstd = "0.13"
|
||||
md5 = "0.7"
|
||||
|
||||
@@ -4,8 +4,8 @@ version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
aes = "0.8"
|
||||
cbc = "0.1"
|
||||
aes = "0.9"
|
||||
cbc = "0.2"
|
||||
pbkdf2 = { version = "0.12", features = ["sha2"] }
|
||||
sha2 = "0.10"
|
||||
hmac = "0.12"
|
||||
|
||||
@@ -289,7 +289,7 @@ mod tests {
|
||||
params: &CryptoParams,
|
||||
salt: Option<&[u8; 16]>,
|
||||
) -> Vec<u8> {
|
||||
use aes::cipher::{block_padding::NoPadding, BlockEncryptMut, KeyIvInit};
|
||||
use aes::cipher::{block_padding::NoPadding, BlockModeEncrypt, KeyIvInit};
|
||||
use hmac::{Hmac, Mac};
|
||||
use sha2::Sha512;
|
||||
|
||||
@@ -306,7 +306,7 @@ mod tests {
|
||||
|
||||
let mut ciphertext = plaintext.clone();
|
||||
Aes256CbcEnc::new(enc_key.into(), (&iv).into())
|
||||
.encrypt_padded_mut::<NoPadding>(&mut ciphertext, data_len)
|
||||
.encrypt_padded::<NoPadding>(&mut ciphertext, data_len)
|
||||
.expect("encryption should not fail");
|
||||
|
||||
let mut page = vec![0u8; params.page_size];
|
||||
|
||||
@@ -74,7 +74,7 @@ mod tests {
|
||||
|
||||
/// Build a single-page encrypted DB file that `decrypt_db` can process.
|
||||
fn build_encrypted_db(path: &Path, raw_key: &[u8; 32], salt: &[u8; 16], params: &CryptoParams) {
|
||||
use aes::cipher::{BlockEncryptMut, KeyIvInit};
|
||||
use aes::cipher::{BlockModeEncrypt, KeyIvInit};
|
||||
use hmac::{Hmac, Mac};
|
||||
use sha2::Sha512;
|
||||
|
||||
@@ -93,7 +93,7 @@ mod tests {
|
||||
let mut ciphertext = plaintext;
|
||||
type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
|
||||
Aes256CbcEnc::new((&enc_key).into(), (&iv).into())
|
||||
.encrypt_padded_mut::<aes::cipher::block_padding::NoPadding>(&mut ciphertext, data_size)
|
||||
.encrypt_padded::<aes::cipher::block_padding::NoPadding>(&mut ciphertext, data_size)
|
||||
.unwrap();
|
||||
|
||||
let mut page = Vec::with_capacity(params.page_size);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use aes::cipher::{block_padding::NoPadding, BlockDecryptMut, KeyIvInit};
|
||||
use aes::cipher::{block_padding::NoPadding, BlockModeDecrypt, KeyIvInit};
|
||||
use hmac::{Hmac, Mac};
|
||||
use sha2::Sha512;
|
||||
|
||||
@@ -45,8 +45,8 @@ pub fn decrypt_page(
|
||||
let encrypted = &page_buf[offset..params.page_size - params.reserve];
|
||||
let mut buf = encrypted.to_vec();
|
||||
|
||||
Aes256CbcDec::new(enc_key.into(), iv.into())
|
||||
.decrypt_padded_mut::<NoPadding>(&mut buf)
|
||||
Aes256CbcDec::new(enc_key.into(), iv.try_into().expect("IV length mismatch"))
|
||||
.decrypt_padded::<NoPadding>(&mut buf)
|
||||
.map_err(|e| DecryptError::AesDecryptFailed {
|
||||
page_num,
|
||||
reason: e.to_string(),
|
||||
|
||||
@@ -380,7 +380,7 @@ mod tests {
|
||||
params: &CryptoParams,
|
||||
salt: Option<&[u8; 16]>,
|
||||
) -> Vec<u8> {
|
||||
use aes::cipher::{block_padding::NoPadding, BlockEncryptMut, KeyIvInit};
|
||||
use aes::cipher::{block_padding::NoPadding, BlockModeEncrypt, KeyIvInit};
|
||||
use hmac::{Hmac, Mac};
|
||||
use sha2::Sha512;
|
||||
|
||||
@@ -397,7 +397,7 @@ mod tests {
|
||||
|
||||
let mut ciphertext = plaintext.clone();
|
||||
Aes256CbcEnc::new(enc_key.into(), (&iv).into())
|
||||
.encrypt_padded_mut::<NoPadding>(&mut ciphertext, data_len)
|
||||
.encrypt_padded::<NoPadding>(&mut ciphertext, data_len)
|
||||
.expect("encryption should not fail");
|
||||
|
||||
let mut page = vec![0u8; params.page_size];
|
||||
|
||||
@@ -9,7 +9,7 @@ wx-paths = { path = "../wx-paths" }
|
||||
tokio = { version = "1", features = ["process", "time", "io-util", "rt-multi-thread", "macros"] }
|
||||
regex = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
toml = "0.8"
|
||||
toml = "1.1"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
thiserror = "2"
|
||||
hex = "0.4"
|
||||
@@ -21,8 +21,8 @@ mach2 = "0.6"
|
||||
libc = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
aes = "0.8"
|
||||
cbc = "0.1"
|
||||
aes = "0.9"
|
||||
cbc = "0.2"
|
||||
hmac = "0.12"
|
||||
sha2 = "0.10"
|
||||
tempfile = "3"
|
||||
|
||||
@@ -247,7 +247,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn build_first_page(enc_key: &[u8; 32], salt: &[u8; 16]) -> Vec<u8> {
|
||||
use aes::cipher::{block_padding::NoPadding, BlockEncryptMut, KeyIvInit};
|
||||
use aes::cipher::{block_padding::NoPadding, BlockModeEncrypt, KeyIvInit};
|
||||
use hmac::{Hmac, Mac};
|
||||
use sha2::Sha512;
|
||||
|
||||
@@ -259,7 +259,7 @@ mod tests {
|
||||
let mut ciphertext = plaintext;
|
||||
type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
|
||||
Aes256CbcEnc::new(enc_key.into(), (&iv).into())
|
||||
.encrypt_padded_mut::<NoPadding>(&mut ciphertext, data_len)
|
||||
.encrypt_padded::<NoPadding>(&mut ciphertext, data_len)
|
||||
.unwrap();
|
||||
|
||||
let mut page = vec![0u8; params.page_size];
|
||||
|
||||
@@ -8,10 +8,10 @@ default = []
|
||||
audio = ["dep:silk-rs"]
|
||||
|
||||
[dependencies]
|
||||
aes = "0.8"
|
||||
aes = "0.9"
|
||||
ecb = "0.1"
|
||||
cipher = { version = "0.4", features = ["block-padding"] }
|
||||
rusqlite = { version = "0.32", features = ["bundled"] }
|
||||
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||
md5 = "0.7"
|
||||
base64 = "0.22"
|
||||
thiserror = "2"
|
||||
|
||||
@@ -181,7 +181,7 @@ fn decrypt_v1_v2(
|
||||
|
||||
/// AES-128-ECB decrypt with PKCS7 unpadding.
|
||||
fn aes_ecb_decrypt(ciphertext: &[u8], key: &[u8; 16]) -> Result<Vec<u8>, MediaError> {
|
||||
use aes::cipher::{BlockDecrypt, KeyInit};
|
||||
use aes::cipher::{BlockCipherDecrypt, KeyInit};
|
||||
use aes::Aes128;
|
||||
|
||||
if ciphertext.is_empty() {
|
||||
@@ -200,7 +200,7 @@ fn aes_ecb_decrypt(ciphertext: &[u8], key: &[u8; 16]) -> Result<Vec<u8>, MediaEr
|
||||
let mut decrypted = ciphertext.to_vec();
|
||||
|
||||
for chunk in decrypted.chunks_exact_mut(16) {
|
||||
cipher.decrypt_block(chunk.into());
|
||||
cipher.decrypt_block(chunk.try_into().expect("chunk length mismatch"));
|
||||
}
|
||||
|
||||
// PKCS7 unpadding
|
||||
|
||||
@@ -15,7 +15,7 @@ fn make_xor_dat(key: u8) -> Vec<u8> {
|
||||
/// Header: 07 08 V1 08 07 (6B) + aes_size LE (4B) + xor_size LE (4B) + 0x01 (1B) = 15B
|
||||
/// Then AES-ECB encrypted payload with fixed key, then raw, then XOR tail.
|
||||
fn make_v1_dat() -> Vec<u8> {
|
||||
use aes::cipher::{BlockEncrypt, KeyInit};
|
||||
use aes::cipher::{BlockCipherEncrypt, KeyInit};
|
||||
use aes::Aes128;
|
||||
|
||||
let key = b"cfcd208495d565ef"; // md5("0")[:16]
|
||||
@@ -28,8 +28,8 @@ fn make_v1_dat() -> Vec<u8> {
|
||||
];
|
||||
let mut block2 = [16u8; 16]; // full PKCS7 padding block
|
||||
|
||||
cipher.encrypt_block((&mut block1).into());
|
||||
cipher.encrypt_block((&mut block2).into());
|
||||
cipher.encrypt_block(aes::cipher::Array::from_mut_slice(&mut block1));
|
||||
cipher.encrypt_block(aes::cipher::Array::from_mut_slice(&mut block2));
|
||||
|
||||
let aes_size: u32 = 16; // original plaintext size
|
||||
let xor_size: u32 = 0;
|
||||
@@ -46,7 +46,7 @@ fn make_v1_dat() -> Vec<u8> {
|
||||
|
||||
/// Build a V2-encrypted `.dat` file with known AES key and XOR tail.
|
||||
fn make_v2_dat(aes_key: &[u8; 16], xor_key: u8) -> Vec<u8> {
|
||||
use aes::cipher::{BlockEncrypt, KeyInit};
|
||||
use aes::cipher::{BlockCipherEncrypt, KeyInit};
|
||||
use aes::Aes128;
|
||||
|
||||
let cipher = Aes128::new(aes_key.into());
|
||||
@@ -58,8 +58,8 @@ fn make_v2_dat(aes_key: &[u8; 16], xor_key: u8) -> Vec<u8> {
|
||||
];
|
||||
let mut block2 = [16u8; 16]; // PKCS7 padding block
|
||||
|
||||
cipher.encrypt_block((&mut block1).into());
|
||||
cipher.encrypt_block((&mut block2).into());
|
||||
cipher.encrypt_block(aes::cipher::Array::from_mut_slice(&mut block1));
|
||||
cipher.encrypt_block(aes::cipher::Array::from_mut_slice(&mut block2));
|
||||
|
||||
let aes_size: u32 = 16;
|
||||
// Tail: 4 bytes XOR-encrypted
|
||||
|
||||
@@ -15,10 +15,10 @@ thiserror = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
aes = "0.8"
|
||||
cbc = "0.1"
|
||||
aes = "0.9"
|
||||
cbc = "0.2"
|
||||
hmac = "0.12"
|
||||
sha2 = "0.10"
|
||||
pbkdf2 = { version = "0.12", features = ["sha2"] }
|
||||
rusqlite = { version = "0.32", features = ["bundled"] }
|
||||
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "macros", "time"] }
|
||||
|
||||
@@ -275,7 +275,7 @@ mod tests {
|
||||
|
||||
/// Build a minimal encrypted session.db that `decrypt_db` can process.
|
||||
fn build_encrypted_session_db(path: &Path, raw_key: &[u8; 32]) -> PathBuf {
|
||||
use aes::cipher::{BlockEncryptMut, KeyIvInit};
|
||||
use aes::cipher::{BlockModeEncrypt, KeyIvInit};
|
||||
use hmac::{Hmac, Mac};
|
||||
use sha2::Sha512;
|
||||
|
||||
@@ -317,7 +317,7 @@ mod tests {
|
||||
let mut ciphertext = plaintext.clone();
|
||||
let encryptor = Aes256CbcEnc::new((&enc_key).into(), (&iv).into());
|
||||
encryptor
|
||||
.encrypt_padded_mut::<aes::cipher::block_padding::NoPadding>(&mut ciphertext, data_size)
|
||||
.encrypt_padded::<aes::cipher::block_padding::NoPadding>(&mut ciphertext, data_size)
|
||||
.unwrap();
|
||||
|
||||
// Assemble page: salt + ciphertext + IV + HMAC
|
||||
|
||||
@@ -35,7 +35,7 @@ fn encrypt_page(
|
||||
page_num: u32,
|
||||
salt: &[u8; 16],
|
||||
) -> Vec<u8> {
|
||||
use aes::cipher::{BlockEncryptMut, KeyIvInit};
|
||||
use aes::cipher::{BlockModeEncrypt, KeyIvInit};
|
||||
use hmac::{Hmac, Mac};
|
||||
use sha2::Sha512;
|
||||
|
||||
@@ -52,7 +52,7 @@ fn encrypt_page(
|
||||
let mut ciphertext = plaintext.to_vec();
|
||||
let encryptor = Aes256CbcEnc::new(enc_key.into(), (&iv).into());
|
||||
encryptor
|
||||
.encrypt_padded_mut::<aes::cipher::block_padding::NoPadding>(&mut ciphertext, data_size)
|
||||
.encrypt_padded::<aes::cipher::block_padding::NoPadding>(&mut ciphertext, data_size)
|
||||
.unwrap();
|
||||
|
||||
// Assemble encrypted page
|
||||
|
||||
Reference in New Issue
Block a user