# ByteGuard 安全白皮书

**版本**：1.0
**发布日期**：2026 年 5 月 3 日
**适用版本**：ByteGuard iOS v2.3.x 及以上
**联系**：hellobyteguard@gmail.com

---

## 1. 引言

ByteGuard 是一款完全在用户 Apple 设备（iPhone / iPad / Mac）本地运行的密码管理器。本白皮书面向密码学审计方、独立安全研究者与高安全要求的用户，详述 ByteGuard 的密钥层级、加密算法、设备级保护机制以及威胁模型。

我们遵循 **Kerckhoffs 原则**：系统的安全性应仅依赖密钥保密，不依赖算法或架构保密。本文档公开的所有内容（算法、参数、密钥层级、存储位置）都不会削弱实际安全性；恰恰相反，公开是审计与信任的前提。

---

## 2. 信任模型与威胁假设

### 2.1 我们防御的威胁

| 威胁 | 防御方式 |
|------|----------|
| 服务器入侵 / 数据泄露 | ByteGuard 不运行任何后端服务器；同步完全走用户自己的 iCloud Private DB |
| 设备丢失 | **字段级** AES-256-GCM 加密 + Keychain `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` |
| 网络中间人 | 主密码、Secret Key、**明文** Vault DEK 永不离开设备 RAM；同步上传的是已 AES-256-GCM 加密的字段密文 + KEK 包裹后的 wrapped DEK（应用层加密，独立于 CloudKit 自身加密） |
| 钓鱼登录 | 通行密钥（Passkey / FIDO2）私钥永不离设备 |
| 弱主密码暴力破解 | Argon2id 64MB 内存成本 × 3 次迭代（OWASP 移动端推荐） |
| GPU 离线攻击 | Argon2id 内存硬度对 GPU 极不友好 |
| 时序攻击（密码验证） | 常量时间字节 XOR 比较 |
| 内存残留 | **Vault DEK** 锁屏/挂起/切库时三次覆写（zero → random → zero）；Master Key / KEK 仅作为解锁函数 local 变量短暂存在，由 Swift ARC 自动释放 |

### 2.2 我们不防御的威胁

我们对如下场景**不做承诺**——任何宣称能防御这些场景的密码管理器都在夸大其词：

- **被入侵的 OS**：操作系统本身被攻陷（越狱、内核漏洞、恶意 mdm）
- **物理胁迫**：用户被强迫主动输入主密码
- **键盘记录**：iOS 系统级木马记录用户输入
- **屏幕截屏**：解锁后第三方截屏类应用
- **用户主动泄露主密码 + Secret Key**

这些威胁不在本白皮书的安全模型内。

---

## 3. 密钥层级

### 3.1 总览

ByteGuard 采用五层密钥层级（每条目密钥隔离）：

```
主密码 + Secret Key (128-bit) ──Argon2id──→ Master Key (32B)
                                                   │
                                                   ├─HKDF "auth-v1"──→ Auth Hash (验证用)
                                                   │
                                                   └─HKDF "kek-v1"──→ KEK (32B)
                                                                       │
                                                                       ↓ AES-GCM unwrap
                                                                  Vault DEK (32B, 全库唯一, 随机生成)
                                                                       │
                                                                       ↓ HKDF "type-{T}-item-{ID}-v1"
                                                                  Item Key (32B, 每条独立)
                                                                       │
                                                                       ↓ AES-256-GCM
                                                                  字段密文
```

### 3.2 输入

| 输入 | 长度 | 来源 | 持久化位置 |
|------|------|------|------------|
| 主密码 | 用户输入 | 用户输入 | 永不持久化 |
| Secret Key | 128-bit (16B) | `libsodium randomBytes` 创建库时生成 | iOS Keychain（可选 iCloud Keychain 同步），同时以 BIP39 12 词形式展示给用户备份 |
| Salt | 256-bit (32B) | `libsodium randomBytes` 创建库时生成 | SwiftData（密码库元数据） |

### 3.3 Master Key

**派生算法**：Argon2id（libsodium 实现）

**输入**：`Argon2id(password ‖ secretKey.base64, salt) → 32 bytes`

**参数**：
- 内存成本：**64 MB**
- 迭代次数：**3**
- 并行度：libsodium 自动配置
- 输出长度：32 字节

参数选型依据：[OWASP Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id) 移动端推荐。在 iPhone 14 Pro 上典型耗时 100~250 ms，即对用户无感、对 GPU 攻击者极昂贵。

**安全特性**：
- Master Key **永不离开设备**，永不持久化
- 退出 / 锁屏后立即清零
- 服务器对此密钥**无任何知识**

### 3.4 Auth Hash 与 KEK

两者都从 Master Key 通过 **HKDF-SHA256** 派生（RFC 5869）：

| 派生密钥 | 用途 | HKDF info 字符串 | 输出长度 |
|---------|------|------------------|----------|
| Auth Hash | 验证主密码是否正确 | `"vault-auth-v1"` | 32B |
| KEK | 解封 Vault DEK | `"vault-kek-v1"` | 32B |

**职责严格分离**：
- Auth Hash **不参与任何数据解密**，仅用于密码验证
- KEK **不直接接触用户数据**，仅用于解封 DEK

**密码验证**使用 **常量时间 XOR 比较**，杜绝时序攻击。

### 3.5 Vault DEK（数据加密密钥，全库唯一）

**生成方式**：库创建时由 `libsodium randomBytes` 生成 32 字节随机密钥；用 KEK 通过 AES-256-GCM 加密（即 `Wrapped DEK`）后存储到 SwiftData。

**为什么 DEK 全库唯一而不是 per-item 随机？**
持久化只存 1 个 wrapped DEK（不用 N 份），存储与同步开销随条目数线性下降但不暴涨。每条目密钥隔离由下一层 Item Key 完成。

**修改主密码的关键性质**：
1. 重新派生 Master Key / Auth Hash / KEK
2. **DEK 不变**，仅用新 KEK 重新 wrap DEK
3. **全库无需重加密**——这是 envelope encryption 的核心优势

### 3.6 Item Key（条目专用密钥）

**派生算法**：`HKDF-SHA256(Vault DEK, info="type-{T}-item-{ID}-v1") → 32B`

- `T`：数据类型（login / card / note / passkey / identity 等）
- `ID`：条目唯一 ID

**安全性**：
- 每条记录使用**独立密钥**，单个 Item Key 泄露不影响其他条目
- 派生确定性 → 无需为每条目持久化密钥
- 类型 + ID 拼接 → 即使两条记录 ID 相同（不同类型）也会派生不同 Item Key

### 3.7 字段密文

**算法**：AES-256-GCM（CryptoKit 实现）

**对每个敏感字段单独加密**：用户名、密码、URL、自定义字段、卡号、CVV（仅会话内存中存在）、备注、附件等。

**唯一 IV**：CryptoKit 自动为每次加密生成唯一 96-bit IV，相同明文永不产生相同密文。

**禁用持久化的字段**：
- CVV / CVC（PCI DSS 红线，全程不持久化）
- 主密码（永不持久化）
- 完整的生物识别原始数据（由 iOS 处理，应用永不接触）

---

## 4. 设备级保护

### 4.1 iOS Keychain ACL

为支持 Face ID / Touch ID 快速解锁，**Vault DEK 的副本**会在用户启用生物识别时存入 iOS Keychain：

```
访问控制 (ACL):
  kSecAttrAccessible       = kSecAttrAccessibleWhenUnlockedThisDeviceOnly
  kSecAccessControlFlags   = .biometryCurrentSet
```

含义：
- **`WhenUnlockedThisDeviceOnly`**：设备未解锁时不可读；不参与 iCloud Keychain 同步
- **`biometryCurrentSet`**：必须通过 Face ID / Touch ID 验证；当用户在系统设置中**修改/重录生物识别数据**时，iOS 自动删除该条目

### 4.2 Secure Enclave 透明保护

iOS Keychain 条目由 **Secure Enclave 内的 device key** 加密。当应用调用 `SecItemCopyMatching` 时：
1. iOS 触发 Face ID / Touch ID 提示
2. 用户验证通过后，SE 用 device key 解密 Keychain 条目
3. 返回明文 Vault DEK 给应用

**关键事实**：
- 应用层**不持有任何 BiometricKey / SE 密钥**
- 信任边界完全交给 iOS Keychain ACL
- SE 操作完全由 iOS 系统隔离层完成，应用不可触

### 4.3 Face ID 重录的自动失效

iOS 的 `biometryCurrentSet` 语义保证：用户重新录入 Face ID（哪怕只是补一个面孔），iOS 自动删除该 Keychain 条目。

ByteGuard 启动时通过 `LAContext.evaluatedPolicyDomainState` 检测此变化：
- 检测到生物识别域变更 → 自动禁用快速解锁
- 清除内存中所有缓存的 DEK 与解密数据
- 强制要求用户重新输入主密码

### 4.4 严格模式（拒绝降级）

如果 `biometryCurrentSet` 不可用（模拟器、未注册生物识别），DEK 缓存**直接抛错**，**不降级到无生物保护的普通 Keychain 存储**。

设计理由：
- 用户启用 Face ID 的意图是"强化保护"，偷偷降级到弱存储违背心智模型
- Fallback 反而**比"未启用 Face ID"更弱**——未启用时根本不缓存，fallback 反而缓存了
- 调用方收到异常仅记日志，不中断主流程：下次解锁退回输主密码即可

---

## 5. 同步与本地存储

### 5.1 SwiftData + 字段加密

所有用户数据存储在 iOS SwiftData 数据库。**ByteGuard 不依赖 SwiftData 的存储加密**——即使 SwiftData 文件被攻击者拿到原始字节，所有敏感字段都已是 AES-256-GCM 密文。

### 5.2 iCloud 同步（可选）

iCloud 同步**默认关闭**，由用户在设置中显式启用。启用后通过 SwiftData `ModelConfiguration(cloudKitDatabase: .private(...))` 接入 CloudKit Private Database。

**关于 CloudKit 加密的事实陈述**（避免常见误解）：
- CloudKit Private DB 默认情况下由 Apple 服务器存储，传输 HTTPS、静态加密，但**解密密钥由 Apple 持有**
- 只有用户**额外开启** iCloud "高级数据保护"（Advanced Data Protection，iOS 16.2+）时，CloudKit Private DB 才进入端到端加密模式
- 因此 ByteGuard **不假设** Apple 不可读 CloudKit，而是**自己再加一层 AES-256-GCM 字段加密**

**关键性质**：
- iCloud 上传的是 ByteGuard 已加密的**字段密文**（密文 + 96-bit IV + 128-bit GCM tag）
- 即使 Apple 配合司法请求或 CloudKit 服务被入侵，看到的也只是 AES 密文，**没有 Vault DEK 无法还原**
- Vault DEK 的 wrapped 副本通过 **iCloud Keychain** 同步（这是 Apple 自己的端到端加密产品，无论是否启用 ADP 都是 E2EE，Apple 不持有解密密钥）
- 跨设备恢复需要：iCloud 账户 + Secret Key + 主密码三者齐全

### 5.3 常见疑问：wrapped DEK 上传 CloudKit 真的安全吗？

> 看到「Vault DEK 的 wrapped 副本会上传 iCloud」可能让人本能担心：把"密钥"放云上不是反直觉吗？这一节正面回答这个疑问。

#### 一、为什么必须上传 wrapped DEK？

跨设备同步在密码学上要求：用户在新 iPhone / iPad 首次登录后，必须能解密所有已存储的字段。如果 wrapped DEK 不同步，新设备**永远拿不到 DEK**，所有密文等同于丢失。

**Envelope encryption（KEK 包裹 DEK）就是为了解决这个矛盾的标准方案** —— 把"用户主密码"与"数据加密密钥"解耦，前者只在用户脑里，后者用前者派生的 KEK 包装后可以放任何地方（包括公开服务器）。

#### 二、攻击者拿到 wrapped DEK 之后要做什么？

要从 wrapped DEK 还原明文 DEK，攻击者必须依次突破：

```
wrapped DEK = AES-256-GCM(KEK, plaintext_DEK, IV)
                ↑
          需要 KEK 才能解密
                ↑
          KEK = HKDF-SHA256(Master Key, "vault-kek-v1")
                ↑
          需要 Master Key
                ↑
          Master Key = Argon2id(主密码 ‖ Secret Key, salt) · 64MB × 3 iter
                ↑
          三重壁垒同时具备：
          ① 用户的主密码（攻击者不知道）
          ② 128-bit Secret Key（用户专属，Apple 也拿不到，从未离开设备明文形态）
          ③ 每次猜测要做 64MB × 3 iter 的 Argon2id 计算
```

#### 三、实际攻击成本量化

假设主密码强度只有 60 bits（一个像样但不极端的密码），加上 128-bit Secret Key 的熵 = **188 bits**。

- 每次猜测要做 64MB 内存的 Argon2id 计算 — GPU 帮不上忙（内存硬度设计）
- 假设全人类 GPU 加在一起 ≈ 10^10 次 Argon2id-64MB / 秒（极乐观估计）
- 暴力遍历 2^188 个组合需要 ~10^46 年

即使量子计算成熟、Argon2id 被加速 10^15 倍，仍需 **10^31 年** —— 宇宙寿命的 10^21 倍。

#### 四、对比行业标杆

**所有**主流密码管理器都把 wrapped 主密钥（或等价物）放云端：

| 厂商 | 同步方式 | wrapped 主密钥位置 | KDF |
|------|---------|-------------------|-----|
| 1Password | 自有云 | 上传 | PBKDF2 / 受 Account Password + Secret Key 保护 |
| Bitwarden | 自有云 | 上传 | PBKDF2 / Argon2id（用户可选） |
| Dashlane | 自有云 | 上传 | Argon2id |
| Apple Keychain | iCloud Keychain E2EE | 上传（Apple ADP 加密层） | Apple 内部 |
| **ByteGuard** | iCloud Private DB | 上传 | **Argon2id 64MB × 3 + 128-bit Secret Key** |

ByteGuard 的密码学强度处于行业第一梯队，关键差异：
- **Secret Key 128-bit 熵作为第二因素**（与 1Password 同款思路）—— 即使主密码弱、即使 Apple 配合司法、攻击者拿到 wrapped DEK 也无从下手
- **Argon2id 64MB 移动端推荐参数**（与 Dashlane 同级）

#### 五、结论

> Wrapped DEK 上 iCloud **不仅不削弱安全**，反而是 envelope encryption 模式的核心安全前提。
>
> 安全等级**完全依赖**于：① 主密码强度 ② Secret Key 永不泄露 ③ Argon2id 参数。CloudKit 是否被入侵、Apple 是否能解密 CloudKit、网络中间人是否截获密文 —— 在这套设计下都无关紧要。

---

### 5.4 AutoFill Extension（v2 Derived Key 隔离）

iOS AutoFill 是独立的进程上下文，无法直接访问主 App 的内存中 DEK。ByteGuard v2 方案：

- 主 App 启用 AutoFill 时，从 Vault DEK 派生一个独立的 **Derived AutoFill Key**（HKDF + vault id）
- 该 Key 用 `biometryCurrentSet` 保护写入 App Group 共享 Keychain
- AutoFill Extension 可在 Face ID 通过后读取此 Key，仅解密用户当前需要填充的那一条凭据

**好处**：AutoFill 不接触 Vault DEK 主密钥，最小权限原则。

---

## 6. 安全工程实践

### 6.1 内存清零

`SecureMemory.zero(&buffer)` 对敏感字节执行**三次覆写 + 内存屏障**：
1. 全部置 0（`memset`）
2. 全部置随机字节（`arc4random_buf`）
3. 全部置 0（`memset`）
4. 每一步后 `load(as: UInt8)` 阻止编译器优化掉清零

**清零范围（精确事实）**：
- ✅ **Vault DEK**（`_currentDEK` 实例变量）：在 `lockVault` / `clearAllKeychainData` 时显式调用 `SecureMemory.zero` 三次覆写
- ⚠️ **Master Key / KEK**：作为 `unlockVault` / `performUnlockOperations` 函数内的 local 变量短暂存在，函数返回后由 Swift ARC 自动释放。**ARC 释放只保证引用计数归零，不保证字节立即被覆写** — 字节会停留在 heap 直到被新分配覆盖。这是当前实现的限制。
- ⚠️ **Argon2 内部缓冲区**：由 libsodium 内部管理，libsodium 在派生完成后会调用 `sodium_memzero` 自行清理

防御范围：编译器优化、heap dump（越狱/调试器场景）、应用挂起后内存被换页到磁盘。**不防御**：Master Key / KEK 在 ARC 释放之前的短暂窗口（毫秒~秒级）内被实时内存读取的攻击。

### 6.2 常量时间比较

密码验证使用按字节 XOR 累加：

```swift
var result: UInt8 = 0
for i in 0..<computedHash.count {
    result |= computedHash[i] ^ storedHash[i]
}
guard result == 0 else { throw .invalidPassword }
```

确保比较耗时与匹配位置/数量无关，杜绝时序侧信道。

### 6.3 CSPRNG 来源

所有随机数（Salt、Vault DEK、Secret Key、IV）来源：
- `libsodium randomBytes.buf()`（底层调用 macOS / iOS 的 `arc4random_buf`，最终来自内核 CSPRNG）
- CryptoKit 的 `AES.GCM.seal()` 内部 IV 由 CryptoKit 自动生成

**绝不使用** `Math.random()`、`Date()`、PID 或其他低熵来源。

### 6.4 密码生成器

应用内置的密码生成器：
- 随机模式：`SecRandomCopyBytes`（内核 CSPRNG）
- 助记短语：EFF Long Word List（7776 词）
- 数字 PIN：CSPRNG 抽样

### 6.5 自动化测试

- ViewModel / Manager / Crypto Service 均有单元测试
- Crypto 测试覆盖：派生正确性、wrap/unwrap 往返、改密码后旧密文仍可解（用新 KEK 解新 wrapped DEK）
- UI 测试覆盖：解锁流程、生物识别失效检测

---

## 7. 我们做不到的事

零知识架构意味着以下场景**我们也做不到**——这是设计目标，不是限制：

| 场景 | 状态 |
|------|------|
| 重置主密码 | ❌ 不能 — 没有第二条解密路径 |
| 找回丢失的 Secret Key | ❌ 不能 — 我们从未持有任何 Secret Key 数据 |
| 配合执法机构解密用户数据 | ❌ 不能 — 服务器无密钥、无密文 |
| 在加密中预留后门 | ❌ 不能 — 算法与参数全部公开，可独立审计 |
| 收集用户密码用于风控 / 训练 | ❌ 不会，且**技术上做不到** |
| 推送广告或第三方 SDK | ❌ 应用内不接入任何分析、广告、崩溃上报 SDK |

**用户**同时丢失主密码和 Secret Key → **数据永久无法解密**。这是设计强约束，不是产品缺陷。

---

## 8. 算法与版本

### 8.1 当前算法选型

| 用途 | 算法 | 实现 | 标准 |
|------|------|------|------|
| 主密码 KDF | Argon2id 64MB / 3 iter | libsodium | RFC 9106 |
| 密钥派生 | HKDF-SHA256 | CryptoKit | RFC 5869 |
| 对称加密 | AES-256-GCM | CryptoKit | NIST SP 800-38D |
| 密码强度评估 | zxcvbn 移植 | 内部 | — |
| 泄露检测 | k-anonymity HIBP API | 内部 | HIBP API v3 |
| 助记词 | BIP39 12 词（128-bit 熵） | 自实现 | BIP-0039 |
| 通行密钥 | WebAuthn / FIDO2 ECDSA P-256 | iOS AuthenticationServices | FIDO2 / CTAP 2.2 |

### 8.2 版本与兼容

所有 HKDF info 字符串带 `-v1` 后缀，便于未来无缝迁移到 v2 算法（例如 SHA-3、AES-256-GCM-SIV）。每个数据库迁移路径都会保留旧密文的解密能力。

---

## 9. 已知限制与未来工作

### 已知限制

1. **AutoFill v1 用户**：未升级到 v2 Derived Key 的老用户仍在使用 legacy biometric key 路径。新安装均默认 v2。
2. **iCloud Keychain 失效**：用户登出 iCloud 账户后 Secret Key 在新设备上需手动恢复（输入 12 词助记词）
3. **密码历史**：保留旧密码副本（用户可控开关），副本同样字段加密；删除条目时一并清除

### 未来计划

- 端到端加密的可选「家庭共享」（Diffie-Hellman 密钥协商，主密钥永不外传）
- Argon2id 参数动态自适应（设备 RAM 检测）
- WebAuthn 跨设备同步（CTAP 2.2 hybrid transport）

---

## 附录 A：参数总表

| 参数 | 值 |
|------|----|
| Argon2id 内存 | 64 MB |
| Argon2id 迭代 | 3 |
| Argon2id 输出 | 32 bytes |
| Salt 长度 | 32 bytes |
| Secret Key 长度 | 16 bytes (128-bit) |
| Master Key 长度 | 32 bytes (256-bit) |
| KEK 长度 | 32 bytes (256-bit) |
| Vault DEK 长度 | 32 bytes (256-bit) |
| Item Key 长度 | 32 bytes (256-bit) |
| AES-GCM IV 长度 | 12 bytes (96-bit) |
| AES-GCM Tag 长度 | 16 bytes (128-bit) |
| Auth Hash 长度 | 32 bytes |
| BIP39 助记词 | 12 词 |

## 附录 B：HKDF info 字符串清单

| info | 用途 |
|------|------|
| `vault-auth-v1` | Auth Hash 派生 |
| `vault-kek-v1` | KEK 派生 |
| `type-{T}-item-{ID}-v1` | Item Key 派生（按数据类型 + 条目 ID） |
| `vault-dek-v1` | （已弃用）旧版 DEK 派生 |

## 附录 C：参考资料

- RFC 9106 — Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications
- RFC 5869 — HMAC-based Extract-and-Expand Key Derivation Function (HKDF)
- NIST SP 800-38D — Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM)
- BIP-0039 — Mnemonic code for generating deterministic keys
- OWASP Password Storage Cheat Sheet
- Apple Platform Security Guide — Keychain Data Protection / Secure Enclave
- Have I Been Pwned API v3 — k-anonymity password range query

---

**版本历史**

| 版本 | 日期 | 变更 |
|------|------|------|
| 1.0 | 2026-05-03 | 白皮书首版 |

**反馈与漏洞披露**

发现安全问题请邮件至 `hellobyteguard@gmail.com`。我们承诺：
- 24 小时内首次回复
- 与报告者协调披露窗口
- 重大漏洞修复后致谢（征得同意时具名）
