设计哲学把「固定原语 +
预知对端公钥」钉死之后,协议层要回答三件事:握手消息里有什么、每个
DH 贡献什么安全属性、DoS 下如何既不分配未认证状态又能证明 IP
归属。本文以官方 Protocol &
Cryptography 与 Donenfeld NDSS 2017 §V
为主来源;内核常量取自 Linux 6.6
drivers/net/wireguard/messages.h。
更细的源码级握手状态机见 内部实现文 第二节;Noise 记号谱系见 安全信道文。
一、固定原语与构造名
WireGuard 使用的构造名(官方 protocol 页):
Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s
| 符号 | 含义 |
|---|---|
DH(priv, pub) |
Curve25519 标量乘,32 字节 |
AEAD(key, counter, pt, ad) |
ChaCha20-Poly1305(RFC 7539);nonce = 32 bit 零 || 64-bit LE counter |
HASH / HMAC /
MAC |
BLAKE2s 及其 keyed 变体 |
TAI64N() |
12 字节时间戳(防 initiation 重放) |
CONSTRUCTION |
UTF-8:Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s |
IDENTIFIER |
UTF-8:WireGuard v1 zx2c4 Jason@zx2c4.com |
未配置 PSK 时,PSK 视为 32 字节全零,算法路径不变。
消息类型(Linux 6.6 messages.h):
| 值 | 类型 |
|---|---|
| 1 | Handshake Initiation |
| 2 | Handshake Response |
| 3 | Cookie Reply |
| 4 | Transport Data |
二、Noise IK 在 VPN 场景里的含义
Noise IK 模式(简化记号):
IK(s, rs):
<- s
...
-> e, es, s, ss
<- e, ee, se
- 发起方预先知道响应方静态公钥 \(s_r\)(配置里的 peer public key)。
- 第一条消息即可做发起方认证相关运算,并加密携带发起方静态公钥 \(s_i\)。
- WireGuard 在 IK 上加入 PSK 混合,称为 IKpsk2。
这对 VPN 的运维假设是匹配的:你不会对「完全陌生的公钥」自动建隧道;peer 集合是配置的一部分。
三、四次 DH:各自换来什么
握手过程(双方合计)涉及:
| 运算 | 计算 | 主要贡献 |
|---|---|---|
es |
\(DH(e_i, s_r)\) | 用响应方静态密钥「打开」对发起方静态公钥的加密;被动观察者看不到 \(s_i\) |
ss |
\(DH(s_i, s_r)\) | 静态–静态:双方长期身份互证的关键材料 |
ee |
\(DH(e_i, e_r)\) | 临时–临时:前向保密的主来源(临时私钥握手后清零) |
se |
\(DH(s_i, e_r)\) | 绑定发起方静态与响应方临时;配合整体模式抵抗若干 KCI 场景 |
论文与官方文档宣称的性质包括:AKE 安全目标下的认证密钥交换、重放抵抗、perfect forward secrecy、静态公钥身份隐藏(类似 SIGMA 思路)、以及对 KCI 的设计考虑。这些是协议声称 + 后续分析文献的对象;计算模型下的精确定理见 深度篇 对 Dowling & Paterson 的讨论。
sequenceDiagram
participant I as Initiator
participant R as Responder
I->>R: Msg1 Initiation type=1
Note over I,R: e_i cleartext; s_i and TAI64N encrypted
R->>I: Msg2 Response type=2
Note over I,R: e_r cleartext; empty AEAD; keys derived
I->>R: Msg4 first transport
Note over R: key confirmation; R may send data
四、消息布局(与官方页对齐)
4.1 Initiation(type = 1)
字段顺序:
message_type/reserved_zero[3]/sender_indexunencrypted_ephemeral[32]—— \(E^{pub}_i\)encrypted_static—— AEAD 保护的 \(S^{pub}_i\)encrypted_timestamp—— AEAD 保护的 TAI64Nmac1[16]/mac2[16]
密钥调度要点(官方 protocol 页伪代码):以
HASH(CONSTRUCTION) 为 chaining key 起点,混入
IDENTIFIER 与 \(S^{pub}_r\),再混入临时公钥与
es、ss 的 DH 输出,逐步更新
hash 与 AEAD
key。响应方按相同公式逆向运算,使双方 chaining key / hash
一致。
sender_index 是本端为这次握手/会话分配的
32-bit 索引(类似 SPI),供对端在后续包里引用。
4.2 Response(type = 2)
字段:sender_index、receiver_index(回指发起方
index)、unencrypted_ephemeral(\(E^{pub}_r\))、对空载荷的
AEAD、mac1/mac2。
响应方在此混入 ee、se 与
PSK,再派生传输密钥。官方页写明:response 比 initiation
更短,以降低反射放大动机。
4.3 传输密钥派生
两条握手消息完成后,双方由 chaining key 经 HKDF 风格展开得到:
- 发起方:
sending_key/receiving_key - 响应方:二者对调
随后清零临时私钥与中间 chaining 状态(官方页;内核
noise.c 实现同一纪律)。计数器从 0
起,每个方向独立。
4.4 Transport Data(type = 4)
type | reserved | receiver_index | counter | AEAD(packet || zero-pad)
内层 IP 包在加密前按 16 字节倍数零填充(不改 IP 头长度字段),增加流量分析难度;填充不得把 UDP 撑过 MTU。头部长 16 字节,便于原地解密对齐——论文 §V-D6 强调这是为硬件与常见 CPU 友好。
重放:64-bit 计数器只增不减;因 UDP
乱序,接收端用滑动窗口(论文指向 RFC 2401 附录 C / RFC 6479
一类算法)。Linux 6.6 中窗口相关常量见
messages.h 的
COUNTER_BITS_TOTAL = 8192。
五、为何是 1.5 RTT,不是「握手完就能双向发」
Noise IK 两条消息后,双方已经能算出会话密钥。但 WireGuard / Noise 要求:发起方先发第一条 transport,响应方收到后才用新会话发数据。
原因(官方 protocol 页 “Connection-less Protocol” 段;NDSS §V 开篇):
- 该包充当 key confirmation,确认发起方已收到 response 并切到新密钥。
- 若把 confirmation 做成专用握手消息,UDP 丢包会迫使握手层状态机变复杂;任意合法 transport 都可以当 confirmation,实现更稳。
因此时间线是:
| 时刻 | 谁可以安全发新会话数据 |
|---|---|
| Initiation 发出 | 无 |
| Response 返回 | 发起方可以发 |
| 首条 transport 到达响应方 | 响应方可以发 |
Dowling & Paterson(ACNS 2018)指出:正是「confirmation 密文用会话密钥加密」,使得把密钥交换单独拿出来做 session-key indistinguishability 证明变得别扭——详见深度篇。工程上这是特性;证明模块化上这是摩擦。
六、沉默、时间戳与 Cookie
6.1 未认证则尽量沉默
设计目标(NDSS §V-A):认证前不分配状态、对未认证包不响应,降低扫描可见性与状态耗尽面。
6.2 Initiation 时间戳
第一条消息必须可认证,带来 initiation 重放风险:攻击者重放旧 initiation,诱使响应方更新临时态,干扰合法会话。WireGuard 在加密字段里放 TAI64N;响应方对每个 peer 记录最大时间戳,拒绝 \(\le\) 历史最大值的包。响应方重启丢失该状态可接受:当时没有活跃会话可被破坏(论文 §V-A)。
6.3 MAC1 / MAC2 与 Cookie
Curve25519 虽快,在洪水下仍可能被 CPU 耗尽。负载高时,响应方可对「MAC1 合法但 MAC2 无效/缺失」的握手返回 Cookie Reply(type = 3),而不是做完整握手。
相对 DTLS / IKEv2 cookie,论文 §V-C 强调三点修补:
- 始终要求 MAC1(密钥材料含响应方公钥等),避免对完全陌生扫描者回包,尽量保持「沉默」。
- Cookie 本身经 AEAD 保护,降低明文 cookie 被中间人盗用。
- AEAD 的 AD 绑定引发 cookie 的那次 MAC1,降低对发起方注入假 cookie 的效果。
Cookie 值为对发起方源 IP 的
MAC,密钥为响应方每两分钟轮换的
secret(COOKIE_SECRET_MAX_AGE = 2 * 60
秒,Linux 6.6 messages.h)。发起方用 cookie
计算 MAC2 后重发。
七、定时器与密钥寿命(实现常量)
官方 protocol 页描述了一组定时行为;Linux 6.6
messages.h 中的秒级常量包括:
| 常量 | 值 | 含义(摘要) |
|---|---|---|
REKEY_AFTER_TIME |
120 s | 发起方侧会话老化后触发再握手 |
REJECT_AFTER_TIME |
180 s | 超此寿命的密钥拒收 |
KEEPALIVE_TIMEOUT |
10 s | 空包 keepalive / 断线探测相关 |
REKEY_TIMEOUT |
5 s | 握手重试间隔(加抖动) |
REKEY_AFTER_MESSAGES |
\(2^{60}\) | 单密钥消息计数阈值 |
精确触发条件以官方 protocol 页条目与
timers.c
为准;运维层面记住两句即可:约两分钟量级轮换会话密钥;长时间单向静默会靠
keepalive / 再握手维持或拆除。
八、小结
- IKpsk2 = 预知响应方公钥的 Noise IK + 可选 PSK
混合。
- 安全属性来自四次 DH 的分工,而不是「多做几次 DH
显得更安全」。
- 双向安全数据是 1.5 RTT 语义;第一条
transport 是 confirmation。
- Cookie 机制服务 DoS,同时尽量不破坏「未认证则沉默」。
下篇把这些消息对应到 Linux 6.6 的
wg_xmit、noise.c 与加解密
worker。
参考资料
规范 / 官方
- WireGuard. Protocol & Cryptography. https://www.wireguard.com/protocol/
- Donenfeld, J. A. WireGuard: Next Generation Kernel Network Tunnel. NDSS 2017, §V.
- Nir, Y. & Langley, A. RFC 7539. ChaCha20 and Poly1305 for IETF Protocols.
- Perrin, T. The Noise Protocol Framework.
源码
- Linux 6.6
drivers/net/wireguard/messages.h(enum limits、enum message_type) - Linux 6.6
drivers/net/wireguard/noise.c、timers.c、cookie.c
论文
- Dowling, B. & Paterson, K. G. A Cryptographic Analysis of the WireGuard Protocol. ACNS 2018 / IACR ePrint 2018/080.
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
WireGuard 深度系列:从设计哲学到内核实现
从 Donenfeld NDSS 2017 的设计哲学出发,拆解 Noise IKpsk2、内核数据路径、运维实践与形式化验证争论——把 WireGuard 从「好用的 VPN」讲成可核对的协议与系统。
【WireGuard】设计哲学:故意的分层破坏与密码学观点
WireGuard 不是把 IPSec 写短一点。NDSS 2017 论文把「正确分层」当成复杂度的来源,用 cryptokey routing、固定原语与带外密钥分发换可审计实现。本文钉住这些设计决定及其边界。
【WireGuard】深度探讨:形式化证明、密码敏捷性与后量子
Dowling–Paterson 对 1.5 RTT confirmation 的证明障碍、Donenfeld 的回应、netdev 上「无算法协商」争论、PSK 量子权宜之计,以及 WireGuard 作为 ZTNA 数据面的边界与开放问题。
【网络工程】WireGuard 内部实现:Cryptokey Routing、Noise IK 握手与内核数据路径
WireGuard 用不到 4000 行内核代码替代了 IPSec/IKE 数十万行的协议栈——这不是少写功能,而是用固定密码学原语、固定握手模式和 cryptokey routing 这三个设计决定,换掉了证书体系、协商机和 SPD/SAD 分离。本文钻进源码和协议握手过程,逐层拆解 cryptokey routing 的双向执行、Noise IKpsk2 的 1.5 RTT 握手(含四次 ECDH 的安全属性贡献)、内核多核队列架构、timer 状态机、cookie DoS 防御和三密钥对轮换。