在密码学工程实践中,“安全信道”(secure channel)是几乎所有网络协议的最终目标:两个——乃至多个——通信方在不受信任的网络上建立一条同时具备认证性(authentication)、机密性(confidentiality)与完整性(integrity)的逻辑管道。TLS 1.3 的成功让学术界与产业界意识到,协议设计应当极度精简、可形式化验证、且尽可能减少往返次数(round-trip)。在此背景下,Noise 协议框架(Noise Protocol Framework)与 Signal 协议成为过去十年最具影响力的两个安全信道构造范式。前者以其极简的握手模式记号系统驱动了 WireGuard、Lightning Network 等基础设施;后者以 X3DH 与 Double Ratchet 的组合定义了端到端加密(end-to-end encryption, E2EE)即时通讯的事实标准。本文将从形式化定义出发,逐层拆解这些协议的设计动机、密码学构件与安全属性,最后展望群组通信协议 MLS 与后量子迁移。
Noise 与 Signal 的分层关系
在深入细节之前,先以一张分层图勾勒 Noise 框架、Signal 协议以及构建在其上的应用之间的逻辑关系:
应用层 Signal Messenger / WhatsApp / Google Messages
|
协议层 Signal Protocol
|
+---+---+
| |
密钥协商 X3DH Double Ratchet(消息加密)
| |
+---+---+
|
握手框架 Noise Framework(底层握手模式:IK, XX, NK ...)
|
原语层 X25519 / AES-GCM / HMAC-SHA256 / HKDF
Signal 协议在密钥协商层使用了 X3DH(解决异步场景)与 Double Ratchet(持续密钥演化),而 Noise 框架则提供了底层的握手模式记号系统——WireGuard 直接使用 Noise IK 模式,Signal 的设计则在 Noise 的思想基础上做了面向即时通讯的定制扩展。理解这一分层,有助于把握”哪些安全属性由哪一层负责”。
个人思考。 Noise
框架最被低估的特质是它的可组合性(composability)。传统协议设计往往是一个整体方案——TLS
1.2
的密码套件把密钥交换、认证、加密和哈希绑定在一起,分析安全性时必须把整个组合作为一个整体来看。Noise
的握手模式记号把这一切拆解为积木块:e、s、ee、es、se、ss
各自贡献可推理的安全属性,组合后的安全性可以从各个令牌的贡献叠加推导出来。这种”像搭积木一样推理安全性”的能力,使得协议设计者不再需要从零开始证明每个新方案的安全性,而是可以从已验证的基础模块出发进行组合。WireGuard
能用不到 4000 行代码实现一个经过形式化验证的
VPN,很大程度上归功于 Noise
框架提供的这种可组合的安全推理基础。
一、安全信道的形式化定义
要严格讨论安全信道的性质,我们需要给出三个核心安全目标的形式化语义。
认证性指的是接收方能够确信消息确实来自声称的发送方,且未被中间人(man-in-the-middle)篡改。在对称设置下,这通常由消息认证码(MAC)实现;在非对称设置下,则依赖数字签名或经过认证的密钥协商(authenticated key exchange, AKE)。
机密性要求攻击者即使完整窃听了信道上的所有密文,也无法恢复出任何明文信息。现代协议普遍在密钥协商之后使用认证加密(AEAD)算法,将机密性与完整性统一到同一原语中,以避免早期 TLS 中 MAC-then-Encrypt 与 Encrypt-then-MAC 之争所暴露的组合安全陷阱。
前向保密(forward secrecy, FS)是一个更强的性质:即使长期密钥(long-term key)在未来某一时刻泄露,攻击者仍然无法解密之前捕获的会话密文。实现前向保密的标准手段是在每次会话中生成临时密钥对(ephemeral key pair),并利用 Diffie-Hellman 密钥交换(DH key exchange)的临时份额计算会话密钥,会话结束后立即擦除临时私钥。
在更高级的设计中,还要考虑妥协后安全(post-compromise security, PCS):即使某一时刻的会话状态被完全泄露,协议能否在后续轮次中自动恢复安全性。PCS 的核心机制是持续引入新鲜随机性(fresh randomness),让泄露的旧状态失去对未来消息的解密能力。Signal 的 Double Ratchet 正是为此目标而设计的。
从博弈论的视角看,安全信道可以建模为一个理想功能(ideal functionality):发送方将明文交给可信第三方,该第三方将明文无损地传递给接收方,同时仅向攻击者泄露消息长度。真实协议的安全性通过证明其与理想功能的”不可区分性”(indistinguishability)来确立。这正是通用可组合安全(UC security)框架的核心思路,也是 Noise 与 Signal 在学术论文中常用的分析方法。
二、Noise 协议框架
Noise 协议框架由 Trevor Perrin 于 2016 年提出,其设计哲学可以用一句话概括:提供一套最小化的、可组合的握手模式(handshake pattern)记号系统,让协议设计者能够根据应用场景精确选择所需的安全属性,同时自动得到一个经过审慎构造的密钥协商协议。
基本概念
Noise
将参与者分为发起方(initiator)和响应方(responder)。每一方可能拥有两类密钥:静态密钥(static
key, 记为
s)表示长期身份,临时密钥(ephemeral
key, 记为
e)表示在握手开始时新鲜生成的一次性密钥对。握手过程由若干消息(message)组成,每条消息包含若干令牌(token),每个令牌代表一个操作:
e:发送方生成并发送一个新的临时公钥。s:发送方发送自己的静态公钥(可能已被加密)。ee:双方用各自的临时私钥对对方的临时公钥执行 DH 运算。es:发起方用临时私钥对响应方的静态公钥执行 DH 运算(或反之,取决于谁发送此消息)。se:发起方用静态私钥对响应方的临时公钥执行 DH 运算。ss:双方用各自的静态私钥对对方的静态公钥执行 DH 运算。
每次 DH 运算的结果都被混入(mix into)一个持续演化的链式密钥(chaining key)中,通过 HKDF 派生出新的链式密钥与加密密钥。这一过程确保了密钥材料的逐步强化(incremental hardening)——每多一次 DH 运算,攻击者需要攻破的假设就多一个。
状态机
Noise
定义了两个核心状态对象。HandshakeState
维护握手阶段的全部状态,包括当前的链式密钥(chaining key,
ck)、握手哈希(handshake hash,
h)、本地与远程的静态和临时密钥,以及一个内嵌的
CipherState。CipherState
则封装了一个 AEAD 密钥(key k)和一个单调递增的
nonce 计数器(nonce
n),用于在握手阶段对静态公钥等敏感载荷进行加密保护。
握手完成后,HandshakeState 被销毁,分裂(split)为两个独立的 CipherState:一个用于发起方到响应方方向的加密,另一个用于反方向。由此进入传输阶段(transport phase),此后所有应用数据均通过 AEAD 加密传输。
链式密钥的核心操作可以用伪代码表达:
HKDF(ck, dh_output) -> (new_ck, temp_key)
每次 DH 令牌执行后,都会调用上述 HKDF,将旧的链式密钥与新的 DH 共享秘密混合,产生新的链式密钥和一个临时密钥(temp_key)。临时密钥随即被用于初始化或更新 CipherState 中的 AEAD 密钥。这种”棘轮式”(ratchet-like)密钥演化是 Noise 实现前向保密的关键机制——一旦旧的链式密钥被覆盖,即使攻击者后来获取了长期密钥,也无法回溯计算出已被擦除的临时密钥。
从协议设计方法论的角度看,Noise 框架代表了一次深刻的范式转移。传统的安全协议设计方法是”设计一个协议,然后证明它安全”——TLS、SSH、IPsec 都是这一方法的产物。Noise 的突破在于:它设计的不是一个协议,而是一个协议生成框架。通过将握手过程分解为令牌(token)的组合,Noise 实现了一个看似不可能的目标——只要你的令牌序列符合框架的语法规则,生成的协议就自动继承一系列经过形式化验证的安全属性。这就像从”手工打造每一座桥梁并逐一进行结构分析”跃迁到”设计一套桥梁构造规则并证明任何合规的桥梁都满足承载标准”。这种可组合性(composability)极大降低了协议设计的门槛:WireGuard 选用 IKpsk2、Lightning Network 选用不同的模式,他们不需要从头证明自己的协议安全,只需要论证自己的模式选择符合应用场景的安全需求。
三、Noise 握手模式分析
Noise 框架预定义了数十种握手模式,其命名规则蕴含了丰富的安全语义。下面分析几种最具代表性的模式。
NN 模式(匿名-匿名)
NN:
-> e
<- e, ee
这是最简单的模式:发起方发送临时公钥
e,响应方也发送临时公钥 e 并执行
ee
运算。双方都不披露静态身份。该模式仅提供机密性与前向保密,不提供任何认证——类似于匿名
Diffie-Hellman,容易遭受中间人攻击。但它在某些场景下仍有价值,例如作为
Tor 电路建立的内层协议。
NK 模式(匿名发起方,已知响应方)
NK:
<- s
...
-> e, es
<- e, ee
<- s
表示响应方的静态公钥通过带外(out-of-band)方式预先分发给发起方。握手中发起方发送
e 并执行
es(用自己的临时密钥与响应方的静态公钥做
DH),响应方再发送 e 并执行
ee。此模式对响应方进行了认证(发起方已知其公钥),但发起方自身保持匿名。适用于客户端连接已知服务器的场景。
XX 模式(相互认证,延迟身份披露)
XX:
-> e
<- e, ee, s, es
-> s, se
双方在握手过程中交换各自的静态公钥,但这些公钥均在密钥材料逐步积累后才发送,因此被 AEAD 加密保护。XX 模式提供了完整的相互认证与前向保密,同时保护了双方的身份免遭被动窃听者的观察(identity hiding)。这是最通用的模式之一,但需要额外一轮往返。
IK 模式(立即身份,已知响应方)
IK:
<- s
...
-> e, es, s, ss
<- e, ee, se
发起方在第一条消息中就发送自己的静态公钥(用 AEAD
加密),并执行 es 与 ss
运算。响应方回复时执行 ee 与
se。该模式实现了 0-RTT(zero round-trip
time)的发起方认证数据发送——发起方可以在第一条消息中附带应用数据,而响应方在验证后即可处理。WireGuard
的握手正是基于 IK 模式的变体。
XK 模式
XK:
<- s
...
-> e, es
<- e, ee
-> s, se
发起方延迟到第三条消息才发送自己的静态公钥。与 IK 相比,XK 牺牲了 0-RTT 发送认证数据的能力,但换取了发起方身份对主动攻击者的更强保护——只有在确认响应方合法之后,发起方才披露身份。
安全属性总结
每种模式在不同阶段提供不同强度的安全保证。Noise 规范使用一个安全等级系统来标注每条消息的载荷(payload)安全性:等级 0 表示无保护,等级 1 表示仅加密但未认证发送方,等级 2 表示已认证发送方但未认证接收方(即发送方不确定是否只有预期接收方能解密),等级 5 表示在前向保密保护下的完全认证。设计者可以根据应用需求在安全性与往返次数之间做出精确权衡。
四、Signal 协议:X3DH
Signal 协议的密钥协商层使用扩展三重 Diffie-Hellman(Extended Triple Diffie-Hellman, X3DH)算法,专门解决异步通讯(asynchronous communication)场景下的密钥协商问题——在即时通讯中,接收方可能处于离线状态,发送方需要在对方不在线的情况下就能发送加密消息。
预密钥包
X3DH 要求每个用户向服务器上传一组预密钥包(prekey bundle),包括:
- 身份密钥(identity key, IK):长期静态密钥对,代表用户身份。
- 签名预密钥(signed prekey, SPK):中期密钥对,附带身份密钥对其公钥部分的签名,定期轮换(通常每周或每月)。
- 一次性预密钥(one-time prekey, OPK):大量一次性密钥对,使用后即从服务器删除。
当发送方 Alice 希望向离线的 Bob 发起会话时,她从服务器获取 Bob 的预密钥包,然后执行以下四次 DH 运算:
DH1 = DH(Alice_IK, Bob_SPK)
DH2 = DH(Alice_EK, Bob_IK)
DH3 = DH(Alice_EK, Bob_SPK)
DH4 = DH(Alice_EK, Bob_OPK) // 仅当 OPK 可用时
其中 Alice_EK 是 Alice
为本次会话新鲜生成的临时密钥。四个 DH 输出被拼接后通过 HKDF
派生出初始会话密钥(session key)。
安全属性
这种多重 DH 的设计赋予 X3DH 多项安全属性。DH1 和 DH2 提供了相互认证——攻击者必须同时掌握 Alice 的身份密钥与 Bob 的签名预密钥才能伪造握手。DH3 通过 Alice 的临时密钥提供前向保密——即使双方的长期密钥未来泄露,过去的会话密钥仍然安全。DH4 通过一次性预密钥提供了额外的前向保密层,并防止”预密钥重放”攻击(prekey replay attack):即使攻击者截获了一次握手,也无法利用同一个 OPK 发起第二次握手。
X3DH 还提供了一种弱形式的可否认性(deniability):由于握手中没有数字签名(身份密钥仅用于 DH 运算),任何第三方都可以伪造一份看起来合法的会话记录——这意味着会话参与者无法向第三方”证明”对话内容确实来自对方。这一性质在隐私保护场景中具有重要价值。
五、Signal 协议:Double Ratchet
X3DH 建立了初始会话密钥之后,后续的消息加密由 Double Ratchet 算法接管。这是 Signal 协议的核心创新,它结合了对称密钥棘轮(symmetric ratchet)与 Diffie-Hellman 棘轮(DH ratchet)两种机制,实现了逐消息(per-message)的密钥演化。
对称棘轮
在同一方向上连续发送多条消息时,发送方使用一条发送链(sending chain)派生加密密钥。每发送一条消息,链密钥(chain key)通过 HMAC 向前推进一步:
chain_key[n+1] = HMAC(chain_key[n], 0x02)
message_key[n] = HMAC(chain_key[n], 0x01)
消息密钥(message key)用于加密当条消息,使用后立即擦除;链密钥也在推进后擦除旧值。这确保了前向保密——即使当前链密钥泄露,过去的消息密钥无法被回溯计算。
DH 棘轮
当通信方向切换时——即 Alice 收到 Bob 的回复,或 Bob 收到 Alice 的新消息——触发一次 DH 棘轮步进。发送方在消息中附带一个新鲜的临时公钥(ratchet public key),接收方使用自己当前的临时私钥与该公钥执行 DH 运算,将输出混入根密钥(root key)中,派生出新的根密钥和新的链密钥:
(new_root_key, new_chain_key) = HKDF(root_key, DH(my_ratchet_private, their_ratchet_public))
这次 DH 运算引入了新鲜的随机性,实现了妥协后安全——即使某一时刻的完整状态(根密钥、链密钥、私钥)被攻击者获取,只要任意一方后续发送了包含新临时公钥的消息,新的 DH 输出将使攻击者的旧状态完全失效。
概念性 Python 代码
以下简化代码展示 Double Ratchet 的密钥派生核心逻辑:
import hmac
import hashlib
def hkdf_derive(root_key: bytes, dh_output: bytes) -> tuple[bytes, bytes]:
"""从根密钥和 DH 输出派生新的根密钥与链密钥。"""
prk = hmac.new(root_key, dh_output, hashlib.sha256).digest()
new_root_key = hmac.new(prk, b"\x01", hashlib.sha256).digest()
new_chain_key = hmac.new(prk, b"\x02", hashlib.sha256).digest()
return new_root_key, new_chain_key
def symmetric_ratchet(chain_key: bytes) -> tuple[bytes, bytes]:
"""对称棘轮:从链密钥派生消息密钥,并推进链密钥。"""
message_key = hmac.new(chain_key, b"\x01", hashlib.sha256).digest()
next_chain_key = hmac.new(chain_key, b"\x02", hashlib.sha256).digest()
return next_chain_key, message_key
def dh_ratchet_step(state: dict, their_public_key: bytes) -> None:
"""DH 棘轮步进:接收到对方新临时公钥时调用。"""
# 使用本方当前临时私钥与对方新公钥执行 DH
dh_output = dh_exchange(state["my_ratchet_private"], their_public_key)
state["root_key"], state["recv_chain_key"] = hkdf_derive(
state["root_key"], dh_output
)
# 生成新的本方临时密钥对
state["my_ratchet_private"], state["my_ratchet_public"] = generate_keypair()
dh_output = dh_exchange(state["my_ratchet_private"], their_public_key)
state["root_key"], state["send_chain_key"] = hkdf_derive(
state["root_key"], dh_output
)
def encrypt_message(state: dict, plaintext: bytes) -> tuple[bytes, bytes]:
"""加密一条消息,返回密文与当前临时公钥。"""
state["send_chain_key"], msg_key = symmetric_ratchet(
state["send_chain_key"]
)
ciphertext = aead_encrypt(msg_key, plaintext)
return ciphertext, state["my_ratchet_public"]
def decrypt_message(state: dict, ciphertext: bytes,
their_public_key: bytes) -> bytes:
"""解密一条消息;若对方公钥更新则先执行 DH 棘轮。"""
if their_public_key != state.get("their_ratchet_public"):
state["their_ratchet_public"] = their_public_key
dh_ratchet_step(state, their_public_key)
state["recv_chain_key"], msg_key = symmetric_ratchet(
state["recv_chain_key"]
)
return aead_decrypt(msg_key, ciphertext)在上述代码中,dh_exchange 和
generate_keypair 以及
aead_encrypt/aead_decrypt
是对底层密码学原语的抽象。实际实现中,DH 通常使用
Curve25519,AEAD 使用 AES-256-GCM 或 ChaCha20-Poly1305。
消息乱序处理
在实际网络环境中,消息可能乱序到达。Double Ratchet 通过为每条消息标注其所属的链编号(chain index)和消息编号(message number)来处理这一问题。接收方可以暂存(buffer)跳过的消息密钥,以便在后续收到延迟消息时解密。为防止无限期保留过多密钥带来的安全风险,实现通常设置一个跳过消息数量的上限(例如 2000 条),超过此上限的延迟消息将被丢弃。
笔者认为,Signal 的 Double Ratchet 堪称现代密码工程中最优雅的设计之一。在它被发明之前,“逐消息前向保密”(per-message forward secrecy)被普遍认为是不切实际的——每条消息都要执行一次 DH 密钥交换?开销太大。Trevor Perrin 和 Moxie Marlinspike 的天才之处在于将对称棘轮与非对称棘轮巧妙嵌套:同一方向的连续消息通过廉价的对称 KDF 链推进密钥(成本几乎为零),而每次消息方向切换时才触发一次 DH 棘轮(成本可接受,因为方向切换远少于消息总数)。这种设计以极小的状态(两个 KDF 链密钥加上对方的当前棘轮公钥)实现了在通信双方交替发言的典型场景下近乎理想的安全属性。在此之前,没有人认为这种安全级别可以用如此低的开销实现——Double Ratchet 证明了密码协议设计中,精巧的工程组合有时能突破看似基本的理论权衡。
六、Signal Session 管理
多设备支持
Signal 的原始设计将每个设备视为独立的端点(endpoint),每个设备拥有自己的身份密钥和预密钥包。发送方需要为接收方的每个设备分别建立独立的 Double Ratchet 会话,并分别加密消息。这种”扇出”(fan-out)策略在设备数量较少时运行良好,但扩展性有限——如果用户拥有 N 个设备,发送方需要执行 N 次独立加密。
Sender Keys 与群组消息
对于群组通信,Signal 引入了 Sender Keys 机制。每个群组成员为该群组生成一个 Sender Key,包含一个对称链密钥和一个签名密钥对。成员通过已有的双人 Double Ratchet 会话将 Sender Key 安全地分发给群组中的每个其他成员。此后发送群组消息时,发送方只需用自己的 Sender Key 对消息加密一次,所有持有该 Sender Key 的成员都能解密。
Sender Keys 大幅提升了群组消息的效率——发送方的加密操作从 O(N) 降为 O(1)。然而这一机制牺牲了部分安全属性:由于 Sender Key 的对称链不会随每次消息方向切换而执行 DH 棘轮,它无法提供 Double Ratchet 那样的妥协后安全。此外,群组成员变更(加入或退出)时需要重新分发所有 Sender Keys,这一操作的复杂度仍然是 O(N)。
这些局限性正是 MLS 协议试图解决的核心问题。
七、WireGuard
WireGuard 是一个极简的虚拟专用网络(VPN)协议,其握手协议基于 Noise IKpsk2 模式——即 IK 模式叠加两层预共享密钥(pre-shared key, PSK)。WireGuard 的设计者 Jason Donenfeld 明确表示,选择 Noise 框架的核心原因是其”可审计的简洁性”(auditable simplicity)。
握手过程
WireGuard 的握手仅需 1-RTT(一个往返)即可完成:
- 发起方发送一条包含临时公钥
e、经 AEAD 加密的静态公钥s、以及相关 DH 运算结果的消息。 - 响应方验证后回复一条包含自己的临时公钥
e和相关 DH 运算结果的消息。
握手完成后,双方进入传输阶段,使用 ChaCha20-Poly1305 对所有隧道数据包进行 AEAD 加密。
密钥轮换与漫游
WireGuard 每两分钟自动发起一次新的握手以轮换会话密钥,确保前向保密。此外,WireGuard 天然支持端点漫游(roaming)——当一方的 IP 地址发生变化时(例如移动设备从 Wi-Fi 切换到蜂窝网络),对端会自动根据最新收到的合法数据包的源地址更新端点信息,无需重新握手。
取代 IPsec 的原因
与 IPsec/IKEv2 相比,WireGuard 的整个代码库仅约 4000 行(Linux 内核实现),而 IPsec 的内核实现通常超过十万行。代码量的巨大差异直接意味着更小的攻击面和更高的可审计性。WireGuard 不提供密码套件协商(cipher suite negotiation)机制——它固定使用 Curve25519、ChaCha20-Poly1305、BLAKE2s 和 SipHash,通过版本号而非协商来实现算法升级。这一”密码学观点”(cryptographic opinion)策略消除了降级攻击(downgrade attack)的可能性,也大幅简化了实现。
WireGuard 的另一个设计亮点是其”静默”(silent)特性:对于未经认证的数据包,WireGuard 不做任何响应。这使得外部探测者(scanner)无法确定特定端口上是否运行着 WireGuard 服务,提供了一定程度的隐匿性。
从工程实践来看,WireGuard 仅用一套固定密码组件(Noise_IKpsk2、ChaCha20-Poly1305、Curve25519、BLAKE2s)就取代了拥有数十种算法组合的 IPsec,这是”少即是多”哲学在密码工程领域最有力的实证。怀疑者曾质疑:没有密码套件协商,如果其中一个算法被攻破怎么办?WireGuard 的回答是:通过版本号升级整个协议,而非在运行时协商替代方案。这一选择的深层逻辑是:密码套件协商机制本身就是一个攻击面——从 TLS 的 FREAK 到 POODLE 再到 Logjam,几乎所有降级攻击都以协商机制为跳板。WireGuard 用 4000 行代码实现了 IPsec 十万行代码的功能,这个数量级的差距不仅仅是”代码更简洁”的问题——它意味着一个人类密码学审计员可以在合理时间内逐行审阅整个实现,而这对于 IPsec 来说是完全不可能的。当代码简洁到可以被个人完整审阅时,整个安全分析的范式就从”概率性的测试覆盖”转变为”确定性的完整审计”,这是质的飞跃。
八、MLS:群组通信
消息层安全(Messaging Layer Security, MLS)是 IETF 正在标准化的群组端到端加密协议(RFC 9420)。MLS 的核心目标是在保持端到端加密强安全保证的同时,将群组操作的复杂度从 O(N) 降低到 O(log N)。
TreeKEM
MLS 的核心密码学构件是 TreeKEM,一种基于二叉树(binary tree)的群组密钥协商机制。群组中的每个成员占据树的一个叶节点(leaf node),每个叶节点持有该成员的密钥对。树中的每个内部节点(internal node)也关联一个密钥对,其私钥由该节点的所有后代叶节点的成员共享。树根(root)节点的秘密值即为群组的共享秘密(group secret),用于派生消息加密密钥。
当成员 Alice 需要更新自己的密钥(例如定期轮换或妥协后恢复)时,她生成新的叶节点密钥对,然后沿着从叶到根的路径(path)依次生成新的内部节点密钥,并将每个新内部节点的私钥用其兄弟节点(sibling node)的公钥加密后广播给群组。由于二叉树的高度为 O(log N),Alice 只需要加密 O(log N) 份密钥材料,而非 O(N) 份。
群组操作
MLS 定义了四种基本群组操作:
- Add:邀请新成员加入群组,需要新成员的 KeyPackage(类似 Signal 的预密钥包)。
- Update:成员更新自己在树中的密钥材料,用于实现前向保密和妥协后安全。
- Remove:移除群组成员,需要将被移除成员在树中的节点清空(blank)并重新计算路径密钥。
- Commit:将一批 Proposal(上述操作的提案)打包提交,生成新的群组 epoch。
每个 Commit 都会产生一个新的 epoch——群组状态的一个不可变快照。成员必须就 Commit 的顺序达成一致(通常由一个”交付服务”(delivery service)保证消息的全序(total order)),才能维持一致的群组状态。
扩展性
MLS 的 O(log N) 复杂度使其理论上可以支持数万甚至更大规模的群组。相比之下,Signal 的 Sender Keys 方案在成员变更时需要 O(N) 的重新分发操作,在大规模群组中成本显著。然而 MLS 的树结构也带来了额外的复杂性——成员之间必须就树的当前状态达成共识,离线成员需要在回到线上时追赶(catch up)所有遗漏的 Commit,这对交付服务的可靠性提出了较高要求。
九、形式化验证与未来
形式化分析工具
现代安全协议的设计越来越依赖形式化验证工具。Tamarin Prover 和 ProVerif 是该领域最广泛使用的两个工具。它们允许研究者将协议建模为符号化的消息传递过程,然后自动检验各种安全属性——包括保密性、认证性、前向保密和妥协后安全。
Noise 框架的形式化分析已在多篇学术论文中完成。Kobeissi 等人使用 ProVerif 对 Noise 的所有预定义握手模式进行了全自动验证,确认了规范中声称的安全等级。WireGuard 同样经历了严格的 Tamarin 分析,验证了其在标准 Dolev-Yao 攻击者模型下的安全性。Signal 的 X3DH 和 Double Ratchet 也有独立的形式化安全证明,Cohn-Gordon 等人在 2020 年的论文中给出了 Double Ratchet 在标准模型下的首个完整安全归约(security reduction)。
Matrix/Olm 与 Vodozemac
Matrix 是一个开放的去中心化通讯协议,其端到端加密层最初使用 Olm(Signal Double Ratchet 的独立实现)和 Megolm(针对群组消息的对称棘轮优化)。近年来,Matrix 团队已将加密实现迁移到 Vodozemac——一个用 Rust 编写的库,提供了更好的内存安全性和性能。Matrix 社区正在积极评估向 MLS 迁移的可行性,以获得更好的群组密钥管理和妥协后安全。
后量子迁移
量子计算机的潜在威胁促使密码学社区开始将后量子(post-quantum)密钥封装机制(KEM)集成到现有协议框架中。Noise 协议框架已经为后量子扩展提供了初步支持——通过将传统的 DH 操作替换为 KEM 操作(发送方封装,接收方解封装),可以构造出抵抗量子攻击的握手模式。Noise 社区已提出 pqNoise 变体,支持混合(hybrid)模式——同时使用经典 DH 和后量子 KEM,确保即使其中一种假设被攻破,协议仍然安全。
Signal 团队在 2023 年宣布了 PQXDH(Post-Quantum Extended Diffie-Hellman)协议,在 X3DH 的基础上增加了一个基于 CRYSTALS-Kyber(现更名为 ML-KEM)的 KEM 操作。这一混合设计确保了对量子攻击的抵抗力,同时保留了 X3DH 的所有经典安全属性。
WireGuard 方面,社区已经开始实验性地集成后量子 KEM。由于 WireGuard 的固定密码套件策略不允许运行时协商,后量子迁移将通过发布新的协议版本来实现。
展望
安全信道构造的未来将围绕三个主题展开。第一是后量子迁移的工程化落地——如何在不影响性能和兼容性的前提下平滑过渡到后量子算法。第二是大规模群组通信的优化——MLS 的树结构在超大规模群组和高频成员变更场景下仍面临效率挑战,研究者正在探索更高效的树结构变体和懒更新(lazy update)策略。第三是跨协议的可组合性——随着应用场景的多样化,安全信道需要与零知识证明、安全多方计算等高级密码学原语无缝集成,形成更加丰富的隐私保护通讯基础设施。
从 Diffie 和 Hellman 在 1976 年提出公钥密码学的概念,到今天 Signal 在数十亿设备上运行 Double Ratchet、WireGuard 以不到五千行代码保护全球网络流量、MLS 为下一代群组通讯奠定标准——安全信道的演化史,正是密码学从理论走向大规模工程实践的缩影。理解这些协议的设计哲学与密码学根基,是每一位安全工程师的必修课。
密码学百科系列 · 第 26 篇