消息应用说“使用 TLS 与端到端加密”,使用者就以为服务器看不到任何东西、几天前泄漏的一把密钥也永远不影响新消息。两种推论都不可靠。TLS 保护的是应用到 TLS 终止端点的传输;如果服务端要转发两人的端到端密文,它仍然通常知道账号、设备、接收方、消息时间和大小等路由元数据。端到端密钥若没有经过验证的身份绑定,服务器还可能提供错误的对端公钥。Double Ratchet 试图在会话中推进消息密钥、引入新的 DH 秘密改善泄漏后的边界;它不是“每次哈希一次同一密码”这么简单。

先画出哪把密钥在哪一端

初始密钥建立和身份认证把 A 的设备与 B 的设备绑定到某组共同材料;之后各端维持发送链与接收链等状态。从当前链密钥派生一份仅用于本条消息的 AEAD 密钥,同时派生下一条链密钥;使用后安全删除不再需要的旧材料,可在一定威胁模型下限制后来状态泄漏对早前消息的影响。接收到新的 DH 公钥后,协议按正确序列用本地私钥交换,更新根链、发送或接收链;这种新秘密的注入及正确删除旧状态,才是泄漏后恢复的一部分条件。如果攻击者持续控制端点,任何协议都不会在该端点上自动保密。

1
2
3
4
5
6
7
A 设备 ── 本次消息密钥 + AEAD ──► 服务器(路由元数据、不可用消息明文)
└──► B 设备:用接收链密钥验密文与头

发送链密钥 K0 ─┬─ HMAC('message') ─► MK0 ─► 用过后删除
└─ HMAC('next') ────► K1 ─┬─ MK1 ─► 下一消息
└─ K2
新的 DH 共享材料 ─► 新根链/方向性链(具体顺序由协议规定)

如果只保护正文不验证消息头中设备/会话/序号的绑定,攻击者或服务器可能调换密文的用途;AAD 可以把需要公开但不可擅改的头绑定到 AEAD 校验中。反过来,AAD 本身仍对服务器可见,不实现匿名。乱序消息需要安全管理已跳过的有限数量消息密钥与消费状态,不能把一个 Python 循环“按序读两条消息”写成真实多设备聊天系统;备份/多设备同步还会改变谁能看到明文的边界。

examples/cryptography/E07_ratchet_model.py 用成熟 X25519/HKDF/HMAC/AES-GCM 实现教学子集:两端一次 DH 形成同一初始链,连续两条消息派生不同的消息密钥,旧消息密钥不能解第二条;改消息公开索引(AAD)触发 InvalidTag。又生成一次新的发送方 DH 私钥,计算不同 epoch 的新根。每份消息密钥仅在该模型中用于一次加密,固定全零 nonce 的使用前提是每条都确实是不同的 AES 密钥;不能将此技巧移植成同一密钥反复用固定 nonce 的生产配置。

先按仓库内 examples/cryptography/README.md 完成环境准备;以下命令从仓库根目录执行。

1
2
python examples/cryptography/E07_ratchet_model.py
python -m unittest discover -s examples/cryptography -p 'test_E07*.py' -v

两项测试通过,不表示实现了完整 Signal Double Ratchet:发送/接收双链状态机、乱序跳号、真实内存擦除、设备身份验证、初始 X3DH、故障恢复和服务端实际消息转发全部 NOT_RUN。这段代码把新 epoch root 算出来,并未按 Double Ratchet 规范完成后续全部消息轮换;一次新 DH 与“泄漏后保密性恢复已证明”之间仍隔着端点是否继续受控、是否擦除旧材料以及完整协议的时序。不要将这个模型用作真实聊天的加密客户端。

对浏览器订单与钱包 RPC 的对照是:HTTPS 订单终止端点原本就是网站,站点理应可见订单明文才能处理;端到端通信若终止在设备之间,服务器才可能看不到正文但仍见路由元数据。钱包给 RPC 的交易签名原本是可公开验证的账本授权,不会因为被端到端加密转发就自动给公开账本带来隐私。

练习及答案

画图题: A 与 B 的消息通过转发服务。若只给 A→服务器上 TLS、服务器→B 也另给 TLS,服务器是否可读正文?若 A/B 直接持有端到端消息密钥,服务器又能知道哪些必要元数据?

答案: 两段 TLS 都在服务器终止,中间服务器能见明文;另加 A/B 端到端加密才改变它对正文的可见性。即使拿不到正文,它为投递通常仍看到设备标识、收件方、发送时间、包大小或服务上层元数据;是否另有匿名/隐匿元数据机制须单独验证。

实验变更题: 在 E07 模型里用第一条的 message_key 解第二条消息,为什么失败?把第二条的公开 index 从 1 改成 2 但保持密文不变,哪一层拒绝?如果把 AES-GCM 改成每条同一把密钥和固定零 nonce,这还是安全实验吗?

答案: 第二条使用下一链密钥派生的不同 AES 密钥,老密钥无法验证新 tag;换索引会改变 AAD,AEAD 应拒绝。重复使用同一 AES-GCM 密钥/nonce 会违反 GCM 唯一性要求,可能泄露两份明文的 XOR 等,不会因为叫作“ratchet”就安全。

资料与导航