# 安全与加密

Mailwake 的原生推送在你的哨站上加密，在配对的 iPhone 上解密。本文说明 Mailwake 1.0 的密钥、加密流程和数据边界，帮助你判断哪些数据由自己保管、哪些数据经过官方服务。数据处理与保存约定见[隐私政策](../../privacy/)。

## 三条数据路径

邮件通知、正文阅读和服务身份分别走不同的路径：

| 功能         | 数据路径                                         | 内容处理位置                                               |
| ------------ | ------------------------------------------------ | ---------------------------------------------------------- |
| 原生邮件通知 | 邮箱 → 哨站 → 推送中继服务 → Apple APNs → iPhone | 哨站读取邮件并加密通知，iPhone 验签、解密和展示            |
| 按需阅读正文 | iPhone → 你的哨站 → 邮箱，再沿原路返回           | 哨站解析正文，App 在当前阅读会话中展示                     |
| 设备与购买   | App ↔ 设备与购买服务                            | 完成设备登记和购买验证；推送中继服务在投递前核对设备与权益 |

你运行的哨站可以读取其获准访问的邮件。这里的端到端加密范围是 **哨站到配对设备之间的原生通知内容**。邮箱服务商、哨站主机和解密后的设备各自处于能够接触明文的边界内。

## 密钥由谁保管

**哨站身份密钥**用于签名。哨站在本地生成 P-256 签名密钥，使用本地配置密钥加密保存；公钥提供给配对设备和推送中继服务，用于核对来源。哨站的身份随数据目录保留，重启后继续使用。

**设备身份使用两把独立的 P-256 密钥**：签名密钥证明设备身份与配对同意，密钥协商私钥用于解密通知。在支持 Secure Enclave 的设备上，App 使用其中生成的密钥；其他运行环境使用软件密钥。密钥记录通过共享 Keychain 提供给 App 和扩展，采用 `AfterFirstUnlockThisDeviceOnly` 访问级别，并关闭 iCloud Keychain 同步。设备重启后的首次解锁是后台读钥的前提。

哨站和推送中继服务保存设备公钥。设备解密私钥由设备保管；官方服务和 APNs 不持有这把私钥。

哨站另外使用数据目录中的 `secret.key` 对邮箱凭据等敏感配置进行 AES-256-GCM 加密。该密钥与数据库共同构成可恢复的数据：请一起[备份](backup.md)，并保护主机与备份的访问权限。拿到数据库和本地密钥的人可以解密这些配置。

## 配对如何建立信任

推送配对二维码包含哨站公钥、推送中继服务地址与环境、配对标识及五分钟有效的一次性凭证。App 展示哨站指纹供你核对；请使用站主提供的可信邀请，核对连接信息后再确认。

设备对配对同意签名，将同意与哨站、设备加密公钥、一次性凭证和有效期绑定。哨站验证这份证明，锁定设备身份与公钥，再完成推送中继服务上的配对登记。后续通知使用这份已确认的关系选择加密公钥和验签公钥。

管理邀请可以同时携带推送配对，但两者分别授权：原生推送配对、配置管理和正文阅读具有各自的权限范围。操作步骤见[连接 App](connect-app.md)。

## 一条通知如何加密

哨站为一次加密生成临时 P-256 密钥对、32 字节随机 salt 和 12 字节随机 nonce，然后执行：

1. 用临时私钥与目标设备的公钥进行 P-256 ECDH 密钥协商，得到共享秘密。
2. 用 HKDF-SHA256 派生 256 位内容密钥，派生上下文绑定推送中继服务环境、配对 ID 和事件 ID。
3. 用 AES-256-GCM 加密通知 JSON，使用同一上下文作为附加认证数据，产生密文及 128 位认证标签。
4. 用哨站的 P-256 签名密钥和 SHA-256 对加密信封及上下文签名，再通过 HTTPS 提交给推送中继服务。
5. 推送中继服务验证签名与载荷格式，将密文交给 APNs。设备先用配对时确认的哨站公钥验签，再用自己的私钥协商内容密钥、验证认证标签并解密。

算法标识为 `P256-HKDF-SHA256-A256GCM`。同一次投递的重试复用已经生成的加密信封与事件标识；新的加密操作生成新的临时密钥和随机值。普通通知由通知服务扩展处理，验证码实时活动由 Widget 扩展处理；实时活动同样使用签名密文，并将活动身份和生命周期字段绑定到加密上下文。

加密通知包含邮箱显示名称、文件夹、发件人、主题、收件时间，以及可选的验证码和邮件引用。完整正文通过下一节的独立路径读取。验签或解密失败时，设备保留安全占位提示，邮件明文只在验证成功后展示。

## 正文通过 HTTPS 直接读取

打开正文时，App 向已确认的哨站 HTTPS 地址发起请求。哨站检查设备凭证、站主授予的正文权限和签名邮件引用限定的资源范围，再从邮箱按需读取正文与引用的内嵌图片。正文读取免费，推送配对本身不授予正文权限。

哨站清理 HTML，并提供可读文本回退；App 控制远程图片加载。远程图片可能联系发件人的服务器，因此加载需要遵循 App 的图片选项。普通附件不在这一正文读取流程内。

哨站在请求内存中处理正文。响应使用 `Cache-Control: no-store`；App 禁用 HTTP 缓存并拒绝重定向。首次读取成功后，App 将正文与单封图片授权另存到这台 iPhone，供离线和重启后阅读；文件受 iOS 文件保护并排除备份。通知删除或到期时同步清理正文与图片授权；移除哨站也会清理其已保存正文，保留通知摘要。远程图片文件保持临时读取，再次打开可能需要联网。正文直接从哨站返回 App，官方服务不参与这次传输，诊断与日志始终排除正文。

## 官方服务能看到什么

| 参与方         | 为完成职责可处理的数据                                          |
| -------------- | --------------------------------------------------------------- |
| 你的哨站       | 邮箱凭据、邮件明文、通知配置、配对公钥及本地投递记录            |
| 推送中继服务   | 哨站／设备／配对标识、公钥、APNs 令牌、加密信封、投递时间与状态 |
| 设备与购买服务 | 服务身份、设备与会话信息、购买与权益，以及投递诊断元数据        |
| Apple APNs     | 推送令牌、路由与投递信息、通用提示及加密载荷                    |
| 你的 iPhone    | 设备密钥、解密后的通知和本机历史，以及获准读取的正文            |

发件人、主题和验证码位于原生通知密文内；处理密文的中转服务仍可观察投递关系、时间和载荷大小等信息。运营投递诊断使用标识、状态、错误码与时间，邮件内容继续遵循上述加密边界。各服务也会按其职责处理必要的网络信息，详见隐私政策。

## 本地记录、撤销与安全边界

哨站的待投递任务和管理员历史具有独立的存储规则，本地历史可能包含邮箱与主题摘要。App 解密后的通知历史保存在本机 App Group，受系统文件数据保护，可设置保留期限并清空。正文使用上文说明的独立受保护缓存。

解除配对用于阻止该关系继续投递，撤销正文或管理凭证用于终止相应访问。已经到达设备并解密的内容需要在设备上清理；撤销关系不能远程收回已经看到的内容。

这套设计依赖哨站主机、配对邀请和设备的安全。它保护传输中的通知内容，无法消除已被控制的哨站或已解锁设备读取明文的风险。当前协议使用设备长期密钥协商私钥：如果该私钥泄露，持有历史密文的人可能解密旧通知，因此本方案不承诺针对接收方长期私钥泄露的前向保密。

选择 Bark、Pushover 或 Webhook 时，通知按对应渠道的格式发给你配置的服务。上述原生推送加密协议仅适用于 Mailwake 原生通道；第三方服务的数据范围与预览选项见[通知渠道](notifications.md)。
