本页目录
WireGuard 协议
WireGuard 把 VPN 协议简化到 ~4000 行代码——Noise 协议框架做密钥交换,ChaCha20+Poly1305 做 AEAD 加密,Cryptokey Routing 把公钥和允许的 IP 绑定。没有状态机,没有协商,只有加密。
概述
WireGuard(Jason Donenfeld, 2016)重新定义了 VPN 的简洁性——~4000 行代码对比 OpenVPN(~70,000)和 IPsec(~400,000)。设计哲学是"不协商":加密套件固定(Curve25519 + ChaCha20-Poly1305),端口扫描不可见(不响应未认证的包),连接通过公钥而非 IP 标识(支持透明漫游)。WireGuard 已于 Linux 5.6 合入主线,成为 Linux 上最快的 VPN 实现。Noise IK 握手使用 1-RTT 完成密钥交换,每 120s 自动 rekey 保证前向安全。
设计原则
WireGuard 是一层 3 VPN,设计目标是把密钥交换和加密简化到极致:
- 无协商: 加密套件固定 (Curve25519 + ChaCha20-Poly1305 + BLAKE2s + SipHash24 + HKDF)。不协商 cipher suite — 没有 downgrade attack。
- 无连接: 无"连接建立"状态 — 收到有效认证的包就建立 session,不回应未认证包 (端口扫描看不到)。
- 静默: 不主动发任何包 (除非配置了 PersistentKeepalive — 需要穿透 NAT 时才用)。
- 漫游: 端点 IP 可变 — 每次收到有效包,更新 peer endpoint。
Noise IK 握手
WireGuard 使用 Noise Protocol Framework 的 Noise_IK 模式 (I=static key for Initiator, K=static key for Responder):
双方预共享:
I_s: Initiator 的静态私钥 (Curve25519)
I_pub: Initiator 的静态公钥 (对端知道并配置)
R_s: Responder 的静态私钥
R_pub: Responder 的静态公钥 (对端知道并配置)
Initiator → Responder (Handshake Initiation, 148 bytes):
type = 1 (4 bytes)
sender_index = (4 bytes, 随机)
unencrypted_ephemeral (E_i_pub, 32 bytes) ← DH ephemeral
encrypted_static (AEAD):
I_pub (32 bytes) ← initiator 身份
timestamp (12 bytes) ← TAI64N, 防止重放
mac1 (16 bytes): MAC(Responder pubkey, msg)
mac2 (16 bytes): 如果收到过 cookie reply → cookie value; 否则 zero
Responder → Initiator (Handshake Response, 92 bytes):
type = 2 (4 bytes)
sender_index = (4 bytes)
receiver_index = initiator's sender_index
unencrypted_ephemeral (E_r_pub, 32 bytes) ← DH ephemeral
encrypted_nothing (AEAD): 空 (0 bytes) ← 但 AEAD tag 验证完整性
mac1 (16 bytes)
mac2 (16 bytes): 0
完成握手后,双方各自:
1. DH(E_i_priv, E_r_pub) = DH(E_i_pub, E_r_priv) = DH(E_r_priv, E_i_pub) = shared
2. 从 shared + pubkeys → HKDF 派生:
- 对称密钥 (T_send, T_recv)
- 每 2^60 个包后换 key (约 280,000 年 at 1Gbps)
密钥旋转 (Rekey)
每次 Handshake Initiation 生成新的 ephemeral DH 密钥对 → 新的 session keys。默认 120 秒无 traffic → 发新的 Handshake Initiation (rekey)。旧 session 的包仍然能被接收 (直到新 session 的 key 投入使用,短的 transition 窗口)。
Timer 状态机
每 peer 有 timer:
- handshake_initiation_interval: REKEY_TIMEOUT + jitter (默认 120s 后)
→ 如果上次 handshake 超过 120s → 发新 handshake → 生成新 session key
- persistent_keepalive_interval: 定期发 keepalive (空消息, 仅 for NAT rebind 检测)
→ 默认 0 (不发送, 除非配置)
- retry_interval: 指数退避 (5s, 10s, 20s, ...) for handshake retry
- zero: 如果没有 data 要发送 → 什么都不做 (静默, 不发任何包)
Cookie Reply (防止 DoS): 如果收到疑似伪造的 Handshake Initiation (mac1 验证通过但 mac2 为空),Responder 发送 Cookie Reply 消息。对方必须在后续 Handshake 中包含此 cookie (mac2)。这证明了对端拥有声称的 IP 的可达性。
Data Plane: Transport Data
Transport Data (type=4, 最小 32 bytes):
receiver_index (4 bytes): 对端的 sender_index → 快速查 peer
counter (8 bytes): 单调递增, 每个 message +1
encrypted_encapsulated_packet (AEAD):
IP packet (IPv4 or IPv6) — 原始包被整个加密
→ AEAD tag (16 bytes) = Poly1305 MAC
/proc/config
# 查看 peer 信息:
# 添加 peer:
# handshake age:
参考
- whitepaper: wireguard.com/papers/wireguard.pdf
- Noise: noiseprotocol.org/noise.html (IK pattern)
- 源码:
drivers/net/wireguard/(~4000 lines):noise.[ch](handshake),receive.c,send.c
Keywords: WireGuard, Noise IK, Curve25519, ChaCha20-Poly1305, timer, rekey, roaming, cookie reply