Skip to content

chore: dev → main(主密钥升级 Argon2id、改用系统凭据库并支持选择保存位置) - #464

Merged
feigeCode merged 3 commits into
mainfrom
dev
Oct 26, 2026
Merged

feigeCode merged 3 commits into
mainfrom
dev

Conversation

@feigeCode

@feigeCode feigeCode commented Oct 26, 2026 •

Copy link
Copy Markdown
Owner

内容

dev 当前领先 main 3 个提交 + [Unreleased] 的 changelog 记录,均为主密钥相关的安全改动(closes #459, closes #460)。

1. 主密钥 KDF 升级为 Argon2id(d9a5f121b)

  • 根因:个人主密码原先用 SHA-256 + 固定盐直接派生,没有任何迭代;验证数据随 user_configs 上传,攻击者拿到密文就能离线高速暴力猜密码。固定盐还让同一口令在所有设备上派生出同一把密钥,一处泄漏等于全部同步数据可解。
  • 修法:
    • 验证数据改为版本化信封 NAVOPKDF2:,内含 Argon2id 参数(64 MiB / 3 次迭代 / 并行度 1)与 16 字节随机盐;解析时校验参数上限(内存 ≤ 1 GiB、迭代 ≤ 64、并行度 ≤ 16),防止恶意信封撑爆内存。
    • 密文改为自描述:新密文写成 ENC: + base64(JSON),记录 KDF 参数、盐与 nonce,解密按密文自带参数派生,不再依赖进程内全局状态;因此各设备迁移时各自生成的盐也能互相解密。旧 ENC: + base64(nonce‖ciphertext) 继续可读。
    • 解锁时自动迁移:本地连接、凭据、团队密钥缓存、冲突快照在一个事务里重加密,云端记录走既有 pending 机制补做;中途失败可续,旧密文始终可读。改密码时顺带完成同一轮迁移。
    • 顺带修掉 SyncEngine 用进程全局主密钥覆盖调用方显式注入密钥的问题(CloudSyncService::master_key_pinned),避免同步服务被别的模块改掉密钥后解密失败。
  • 验证:cargo test -p one-core --lib 868 passed;新增用例覆盖「旧密文在升级后仍可解」「密文不再依赖全局 KDF 状态(模拟重启/换设备)」「信封参数被篡改或越界全部拒绝」。

2. 「记住主密码」改用系统凭据库,并支持选择保存位置(d9a5f121b、f1053a000)

  • 根因:自动解锁用的主密钥副本原先用程序内置固定密钥加密写进 data/state/key_storage,密钥随程序分发,等效于明文保存;任何同时拿到程序和数据目录的人都能恢复主密钥。
  • 修法:
    • 默认存系统凭据库(macOS 钥匙串 / Windows 凭据管理器,keyring 3.6.3)。启动/解锁时自动迁移旧 key_storage 文件,读回校验成功后才删除旧文件;读取或写入失败时旧文件保留、继续可读,不会两头落空。
    • 新增设置项「主密钥保存位置」,可在系统凭据库与「本机文件(数据目录)」之间切换,文案按平台显示「macOS 钥匙串 / Windows 凭据管理器」;切换时先写新位置、成功后再删旧副本,会话未解锁时暂不搬运,等下次解锁自然落位。
    • 选择「本机文件」时在设置页给出风险提示(内置密钥加密);没有系统凭据库的平台(如 Linux)只提供本机文件,且不再静默回退弱存储。
  • 验证:key_storage 13 个测试、setting_tab 44 个测试、cargo test -p main 721 + 集成测试全过;新增端到端用例「记住 → 模拟重启 → 从选中的后端恢复」与「切换后端优先写新位置」、settings 字段的旧配置向后兼容用例。

3. 修复单元测试污染真实数据目录(675432afd)

  • 根因:crypto 与 master_key_meta 的 get_data_dir() 在 #[cfg(test)] 下没有隔离,仍然指向真实用户数据目录。cargo test -p one-core --lib 里 key_storage 的端到端用例先 reset_repo_password() 删掉真实安装的 key_verification,紧接着 set_master_key() 用 KdfParams::for_tests()(32 KiB / 1 轮)把它重写成正式版本读不懂的 NAVOPKDF2 信封;已安装的 0.21.1 启动校验随之失败并删掉 key_storage,用户输入正确主密钥也被判成「旧密码错误」。
  • 修法:app_dirs 新增 #[cfg(test)] test_data_dir()(每个测试进程独立的临时目录);crypto / master_key_meta / key_storage 的 get_data_dir() 在测试下统一走 key_storage::test_data_dir_override(),未重定向时回落到临时目录。
  • 验证:cargo test -p one-core --lib 871 passed;跑测试前后真实 ~/Library/Application Support/navop 下 key_verification / key_storage / key_meta.json 的 sha256 均未变化,新增 3 条用例锁定「验证文件与 key_meta.json 必须落在隔离目录」。

4. CHANGELOG

  • ## [Unreleased] 中英双语补 3 条:KDF 升级、系统凭据库存储、保存位置设置;未触碰任何已发布版本条目。

验证

  • 本地:RUSTFLAGS="-D warnings" cargo check -p main 通过;cargo test -p one-core --lib 868 passed;cargo test -p main 721 passed + 集成测试全过;cargo clippy -p main 无新增告警(剩余为既有告警)。
  • Windows/Linux 目标无法在本机交叉编译(ring 的 C 构建需要交叉 C 工具链),依赖本 PR 的 CI 矩阵覆盖;keyring 的 windows-native 依赖图已用独立 crate 在 x86_64-pc-windows-msvc 目标验证可编译。

说明

#460 主密钥派生:
- 新安装使用 Argon2id + 随机盐(64 MiB / 3 次迭代)派生密钥,验证数据改为
  `NAVOPKDF2:` 版本化信封;旧 SHA-256 + 固定盐格式继续可读。
- 密文自描述:`ENC:` 载荷升级为自带 KDF 参数与盐的 JSON,解密不再依赖进程内
  KDF 状态,多设备各自迁移也能互相解密;旧 `ENC:` 二进制载荷仍走 legacy 派生。
- 解锁后自动迁移:切换验证信封 → 重加密本地连接/凭据/团队密钥缓存/冲突快照 →
  重新封存 WebDAV 密码 → 版本号递增 → 云端记录走既有 pending 机制补做。
  迁移前先落 `pending_kdf_upgrade` 标记,中断后下次解锁续做,旧密文始终可读。

#459 自动解锁凭据:
- 「记住主密码」不再用程序内置密钥写入 `data/state/key_storage`,改存系统凭据库
  (macOS 钥匙串 / Windows 凭据管理器,经 keyring);读取到旧文件时自动迁移并在
  写入成功后删除旧文件。
- 没有可用系统凭据库的平台(当前 Linux)只保留旧文件只读兼容,新保存明确失败,
  界面提示手动输入,不再静默回退弱存储;保存失败不会阻塞本次解锁。
- 设置项与文案同步说明「绑定设备/用户、无可信存储则不保存」,便携版目录不再含
  可恢复副本。

顺带修复同步服务主密钥被进程级全局密钥覆盖的问题:`CloudSyncService` 增加显式
固定的主密钥标志,同步引擎只在未固定时从本地 crypto 对齐,消除工作空间同步测试
在并行执行时因全局主密钥被其它测试改写而随机失败。

验证:cargo test -p one-core --lib(863 passed)、cargo test -p main(720 passed
及集成测试)、cargo clippy -p main、RUSTFLAGS="-D warnings" cargo check -p main。
- 新增「主密钥保存位置」设置项:系统凭据库(macOS 钥匙串 / Windows 凭据管理器,按平台显示名称)或数据目录下的本机文件;便携模式同样可选本机文件
- 切换时先把已记住的密钥写入新位置,成功后再删除旧副本;会话未解锁时不做搬运
- 本机文件沿用旧格式(内置密钥 + AES-256-GCM),选中时在设置页给出风险提示
- 没有可用系统凭据库的平台只提供本机文件,启动时把配置归一到实际可用选项
- 补 keyring 保存位置切换与端到端恢复测试、settings 向后兼容测试
`cargo test -p one-core` 时 crypto 与 master_key_meta 的数据目录没有测试
隔离:key_storage 的端到端测试先 `reset_repo_password()` 删掉真实安装的
key_verification,紧接着 `set_master_key()` 又用 `KdfParams::for_tests()`
(32 KiB / 1 轮)把它重写成正式版本读不懂的 NAVOPKDF2 信封。于是已安装的
0.21.1 Navop.app 启动校验失败,还会顺带删掉 key_storage,用户输入正确
主密钥也被判成「旧密码错误」。

- app_dirs 新增 `#[cfg(test)] test_data_dir()`:每个测试进程独立的临时目录
- crypto / master_key_meta / key_storage 的 get_data_dir 在测试下统一走
  `key_storage::test_data_dir_override()`,没有重定向时回落到临时目录
- 补回归用例:验证文件与 key_meta.json 必须落在隔离目录,并断言
  crypto_remembers_and_restores 测试写出的 key_verification 在临时目录

验证:`cargo test -p one-core --lib`(871 passed);跑测试前后真实
`~/Library/Application Support/navop` 下 key_verification / key_storage /
key_meta.json 的 sha256 均未变化。
@feigeCode
feigeCode merged commit a7030f6 into main Oct 26, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: 个人主密码改用 Argon2id 与随机盐派生密钥,提高离线猜测成本 [Feature]: 使用 Windows DPAPI / 凭据管理器保护本地记住的主密码

1 participant