---
title: WireGuard 协议
url: https://doc.liz6.com/networking/08-vpn-and-tunnels/01-wireguard-protocol
locale: zh
area: networking
tags:
- networking
- VPN与隧道
date: 2026-06-30
modified: 2026-06-30
description: WireGuard 把 VPN 协议简化到 ~4000 行代码——Noise 协议框架做密钥交换,ChaCha20+Poly1305 做 AEAD 加密,Cryptokey Routing 把公钥和允许的 IP 绑定。没有状态机,没有协商,只有加密。
---

# 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

```bash
# 查看 peer 信息:
wg show

# 添加 peer:
wg set wg0 peer <pubkey> allowed-ips 10.0.0.2/32 endpoint 1.2.3.4:51820

# handshake age:
wg show wg0 latest-handshakes
```

## 参考

- **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*
