feat: 實作 vault 指令群 status/unlock/lock/list/get/create/edit/delete/restore/purge/folders/sync/password/rescue(issue #17)

- 對接 alterminal/bear#41 的 /api/v1/vault JSON API(PAT Bearer)
- 客戶端端到端加密,格式與 Web Vault(P2)完全一致:
  - 加密字串 2.<iv>.<ct>.<mac>(AES-256-CBC+HMAC-SHA-256、PKCS#7、encrypt-then-MAC)
  - 主金鑰 PBKDF2-SHA512(迭代數取自 /vault/config 與 profile,不寫死;salt=email 小寫)
  - BIP39 助記詞(12 字、128-bit、英文詞表)+救援路徑
- K_user 僅存記憶體:vault unlock 匯出 BEAR_VAULT_SESSION(base64),
  同 Bitwarden CLI BW_SESSION 慣例;lock 提示 unset;不寫入任何檔案
- docs/commands.md 補 §3.11 規格;README 同步
- 測試:fake API 注入+Bear.Vault.Crypto 密文樣本交叉驗證(雙向);
  BIP39 官方向量;46 個新測試,全套 196 passed
This commit is contained in:
2026-09-10 23:27:47 +08:00
parent a880bcea4d
commit 0b18d76cfd
9 changed files with 2488 additions and 2 deletions
+98
View File
@@ -666,6 +666,104 @@ TIME CATEGORY EVENT ACTOR ACCOUN
**`--json` 輸出範例**:`{"ok": true, "page": 1, "per_page": 50, "total": 1, "total_pages": 1, "emails": {…}, "entries": […]}`
### 3.11 `bear vault`:密碼管理指令群(P3,已實作)
> 依賴:bear 伺服器端 **vault JSON API**(alterminal/bear#41,`/api/v1/vault`、PAT Bearer、任何有效 PAT 帳號)與 Web Vault(P2,alterminal/bear#48/#49)。兩者皆已上線,本節指令群已實作(issue #17)。
**端到端加密(與 Web Vault 完全一致,同一 vault 兩端互相可解)**:
- 加密字串 `"2.<iv_b64>.<ct_b64>.<mac_b64>"`:AES-256-CBC+PKCS#7、encrypt-then-MAC(HMAC-SHA-256 over `iv <> ct`)、先驗 MAC 再解密。
- K_user 為隨機 64 byte(前 32 enc_key/後 32 mac_key);K_user 的包裝(wrapped key)加密的是 **K_user 的 base64**。
- 主金鑰:PBKDF2-SHA512(salt=帳號 email 小寫;迭代數以 `GET /api/v1/vault/config` 公告與 profile 記錄為準,**不寫死**)。
- 助記詞:BIP39(12 字、128-bit 熵、英文詞表);救援金鑰=種子 hex → PBKDF2-SHA512(salt=email 小寫)。
- 所有加解密全在客戶端(本 CLI)完成;伺服器零解密。
**K_user 僅存記憶體(不落盤)**:`vault unlock` 解開 K_user 後輸出 session 環境變數設定指令,使用者 `export BEAR_VAULT_SESSION=<base64>` 匯入後,同一 shell 的後續 vault 指令即可解密(同 Bitwarden CLI 的 `BW_SESSION` 慣例;環境變數只存在 shell 記憶體,不寫入任何檔案)。`vault lock` 提示 `unset BEAR_VAULT_SESSION` 即丟棄。
```
bear vault status [--json]
bear vault unlock [--json]
bear vault lock
bear vault list [--folder UUID] [--json]
bear vault get <uuid> [--json]
bear vault create --type login|secure_note|card|identity [--folder UUID] [--json]
bear vault edit <uuid> [--json]
bear vault delete <uuid> [--json]
bear vault restore <uuid> [--json]
bear vault purge <uuid> [--json]
bear vault folders list|create|rename <uuid>|delete <uuid> [--json]
bear vault sync [--json]
bear vault password change [--json]
bear vault rescue [--json]
```
**認證與授權(群組共通)**:
- 使用既有 PAT 憑證(`bear login --token` 或 `BEAR_TOKEN`)作為 Bearer;任何有效 PAT 帳號皆可。
- **KDF salt 需要帳號 email**:來源優先序為 `--email EMAIL` 選項>憑證檔記錄的 email>(無法得知 → 用法錯誤 `2`,提示以 `--email` 提供)。一般 `bear login` 後憑證檔即有 email,無需手動給。
- `401` → `3`;`403` → `8`;`404`/`422` → `1`;網路/伺服器錯誤 → `6`(沿用 §4 總表)。
- **主密碼、助記詞、項目密碼等敏感輸入一律互動提示讀取且不回顧**,不接受命令列明文參數;非 TTY → `2`。名稱、URI、備註等非敏感欄位互動提示可回顯(直接 Enter=保留現值/略過)。
- vault 尚未初始化(`GET /profile` 404)→ `vault status` 顯示「尚未初始化」並提示至 Web Vault 設定(初始化產生助記詞的流程在 Web Vault P2,CLI 不重作);其餘需要 profile 的指令 → 退出碼 `1`。
#### 3.11.1 `bear vault status`
顯示初始化狀態、KDF 參數、上次同步與本 shell 解鎖狀態(`BEAR_VAULT_SESSION` 是否設定)。`GET /api/v1/vault/config`+`GET /api/v1/vault/profile`。
**`--json`**:`{"ok": true, "initialized": true, "kdf": {…}, "unlocked": false, …}`;未初始化 → `{"ok": true, "initialized": false, "unlocked": false}`。
#### 3.11.2 `bear vault unlock`
互動輸入主密碼(不回顧)→ PBKDF2 推導 K_master → 解開 `wrapped_user_password` 得 K_user → 輸出 `export BEAR_VAULT_SESSION=…`。主密碼錯誤(MAC 驗證失敗)→ 退出碼 `1`(訊息「主密碼不正確」)。
**`--json`**:`{"ok": true, "session": "<base64>", "env": "BEAR_VAULT_SESSION"}`。
#### 3.11.3 `bear vault lock`
K_user 只存在環境變數,本指令提示 `unset BEAR_VAULT_SESSION`(無法代跨 shell unset)。
#### 3.11.4 `bear vault list [--folder UUID]`
`GET /api/v1/vault/ciphers`。未解鎖 → 只顯示 UUID/類型(名稱為密文狀態提示);已解鎖 → 一併解密名稱與所屬資料夾名。`--folder` 以 `data.folder_uuid` 過濾(資料夾關聯存在 cipher 的 `data` 欄位,與 Web Vault 一致)。
```
UUID NAME TYPE FOLDER UPDATED STATE
c9J2… GitHub login 工作 2026-09-10T05:00:00Z 有效
```
**`--json`**:`{"ok": true, "decrypted": true, "ciphers": […含 "plain" 明文欄位(已解鎖時)…]}`。
#### 3.11.5 `bear vault get <uuid>`
`GET /api/v1/vault/ciphers/{uuid}`。已解鎖 → 解密全部欄位(名稱/帳號/密碼/URI/備註/額外資訊);未解鎖 → 顯示密文與提示。密碼欄位只在 stdout 呈現(供腳本擷取),不提供 `--clip`(本版)。
#### 3.11.6 `bear vault create --type login|secure_note|card|identity [--folder UUID]`
互動輸入欄位(名稱必填;`login`:帳號/密碼(不回顧)/URI(逗號分隔可多個)/備註;`secure_note`:筆記內容;`card`/`identity`:額外資訊,加密存於 `data.extra`——與 Web Vault 相同的欄位配置)。客戶端加密後 `POST /api/v1/vault/ciphers`(客戶端產生 UUID)。需要解鎖。
#### 3.11.7 `bear vault edit <uuid>`
`GET` 現值 → 解密 → 逐欄提示(**直接 Enter=保留現值**;密碼欄不回顧)→ 僅重加密有變更的欄位 → `PUT /api/v1/vault/ciphers/{uuid}`。需要解鎖。
#### 3.11.8 `bear vault delete/restore/purge <uuid>`
`delete` → `POST …/{uuid}/delete`(回收桶);`restore` → `POST …/{uuid}/restore`;`purge` → `DELETE …/{uuid}/purge`(**永久刪除,互動確認 `y` 才執行**,其他輸入取消 → 退出碼 `1`)。已回收桶再 delete → `409` → `1`。
#### 3.11.9 `bear vault folders list|create|rename <uuid>|delete <uuid>`
資料夾 CRUD(name 客戶端加密):`GET`/`POST`(互動輸入名稱,需解鎖)/`PUT …/{uuid}`(互動輸入新名稱,需解鎖)/`DELETE …/{uuid}`。刪除資料夾不影響所屬項目(僅失去分類)。
#### 3.11.10 `bear vault sync`
`GET /api/v1/vault/sync`(伺服器回 profile+folders+ciphers 並標記 `last_synced_at`)。**離線衝突以伺服器為準**(CLI 為無狀態客戶端,不做雙向合併;本機無快取可衝突)。顯示數量統計;`--json` 回完整 sync payload。
#### 3.11.11 `bear vault password change`
互動輸入:目前主密碼 → 驗證(解開 wrapped_user_password)→ 新主密碼+確認(≥8 字元,兩次一致)→ 以新 K_master 重新包裝 K_user → `PUT /api/v1/vault/profile`(只動 `wrapped_user_password`,**ciphers 不需重新加密**)。
#### 3.11.12 `bear vault rescue`
忘記主密碼的救援路徑:互動輸入助記詞(12 字,會做 BIP39 完整驗證:字數/詞表/校驗和)→ 解開 `wrapped_user_mnemonic` 得 K_user → 設新主密碼(≥8 字元,兩次一致)→ 重新包裝上傳。助記詞錯誤 → 退出碼 `1`。CLI 不提供助記詞顯示(僅 Web Vault 設定時顯示一次)。
---
## 4. 退出碼總表