跳转至

RFC: TUIC Protocol Specification Version 0x05 Category: Informational Date: April 2026

TUIC 协议规范 (TUIC Protocol Specification)

备忘录状态 (Status of This Memo)

本文档为互联网社区指定了 TUIC 协议版本 0x05。本规范定义了一种多路复用、经 TLS 加密的流式协议,旨在基于 QUIC 传输层实现高效的网络中继。

摘要 (Abstract)

TUIC 协议提供了一个命令驱动的框架,用于在加密的 QUIC 连接上进行 TCP 和 UDP 流量中继。它支持连接多路复用、具有零往返时间 (0-RTT) 建立机制的高效 UDP 会话管理,以及针对大型数据报的健壮的分片机制。本文档描述了协议结构、命令格式、操作流程和实现注意事项。

目录 (Table of Contents)

  1. 简介 (Introduction)
  2. 约定和术语 (Conventions and Terminology)
  3. 协议架构 (Protocol Architecture)
  4. 消息格式 (Message Format)
  5. 命令定义 (Command Definitions)
  6. 地址编码 (Address Encoding)
  7. 协议操作 (Protocol Operations)
  8. 标头大小计算 (Header Size Calculations)
  9. 安全注意事项 (Security Considerations)
  10. 参考资料 (References)

1. 简介 (Introduction)

1.1. 目的 (Purpose)

TUIC 协议旨在通过 QUIC 传输层提供安全、多路复用的 TCP 和 UDP 流量中继。该协议解决了以下需求:

  • 在单个传输连接上的高效连接多路复用
  • 零往返时间 (0-RTT) UDP 会话建立
  • 对 TCP 流和 UDP 数据报的透明处理
  • 针对大型 UDP 数据包的健壮的分片与重组

1.2. 协议版本 (Protocol Version)

本文档描述了 TUIC 协议版本 0x05。

1.3. 与其他协议的关系 (Relation to Other Protocols)

TUIC 被设计为将 QUIC [RFC9000] 作为主要传输层运行,但保持了传输无关的设计原则。该协议可以集成到现有服务中,包括 HTTP/3 [RFC9114]。

2. 约定和术语 (Conventions and Terminology)

2.1. 要求语言 (Requirements Language)

本文档中的关键词“必须”(MUST)、“绝对不能”(MUST NOT)、“要求”(REQUIRED)、“将”(SHALL)、“绝对不将”(SHALL NOT)、“应该”(SHOULD)、“不应该”(SHOULD NOT)、“建议”(RECOMMENDED)、“可以”(MAY) 和“可选”(OPTIONAL) 需按照 RFC 2119 [RFC2119] 中的描述进行解释。

2.2. 术语 (Terminology)

  • 客户端 (Client): 发起 TUIC 连接和中继请求的端点。
  • 服务端 (Server): 接受 TUIC 连接并执行中继操作的端点。
  • 命令 (Command): 具有特定类型和载荷的协议控制消息。
  • 关联 (Association): 由唯一 16 位标识符标识的 UDP 会话。
  • 分片 (Fragment): 划分以进行传输的 UDP 数据包的一部分。
  • 目标 (Target): 中继流量的最终目的地。

2.3. 数字约定 (Numeric Conventions)

除非另有明确说明,本协议中的所有多字节数字字段均使用网络字节序(大端序)。

字段大小表示如下: - 1B = 1 字节 (8 bits) - 2B = 2 字节 (16 bits) - 4B = 4 字节 (32 bits) - 16B = 16 字节 (128 bits)

3. 协议架构 (Protocol Architecture)

3.1. 分层模型 (Layering Model)

+---------------------------+
|    应用数据 (App Data)    |
+---------------------------+
|    TUIC 协议              |
+---------------------------+
|  QUIC (包含 TLS 1.3)      |
+---------------------------+
|    UDP                    |
+---------------------------+

3.2. 连接模型 (Connection Model)

TUIC 连接在单个 QUIC 连接上运行。多个中继操作(TCP 连接和 UDP 关联)可以 (MAY) 在此单一连接上多路复用。

3.3. 流使用 (Stream Usage)

  • 单向流 (Unidirectional streams): 用于不需要响应的命令(例如:Authenticate、Packet、Dissociate)。
  • 双向流 (Bidirectional streams): 用于通过 Connect 命令进行的 TCP 中继。
  • QUIC 数据报 (QUIC datagrams): 可以 (MAY) 用于 UDP 数据包中继以减少延迟。

4. 消息格式 (Message Format)

4.1. 基础命令结构 (Base Command Structure)

所有 TUIC 命令共享一个公共的标头结构:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    Version    |     Type      |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
|                  特定类型的载荷 (Type-Specific Payload)...    |
+                                                               +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

版本 (Version, 1 字节): 协议版本标识符。本规范定义了版本 0x05。

类型 (Type, 1 字节): 命令类型标识符(见第 4.2 节)。

特定类型载荷 (Type-Specific Payload): 变长载荷,其格式取决于 Type 字段。

4.2. 命令类型注册表 (Command Type Registry)

命令名称 参考
0x00 Authenticate 第 5.1 节
0x01 Connect 第 5.2 节
0x02 Packet 第 5.3 节
0x03 Dissociate 第 5.4 节
0x04 Heartbeat 第 5.5 节

所有其他值保留供将来使用。

5. 命令定义 (Command Definitions)

5.1. Authenticate (认证) 命令

Authenticate 命令用于建立客户端身份和授权。

Type: 0x00

格式:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    Version    |     0x00      |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
|                                                               |
+                         UUID (16 字节)                        +
|                                                               |
+                                                               +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+                                                               +
|                                                               |
+                        TOKEN (32 字节)                        +
|                                                               |
+                                                               +
|                                                               |
+                                                               +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

UUID (16 字节): 客户端标识符,表示为 UUID [RFC4122]。

TOKEN (32 字节): 身份验证令牌,使用 TLS 密钥材料导出器 (Keying Material Exporter) [RFC5705] 并采用以下参数派生: - Label (标签): 客户端的 UUID 值 - Context (上下文): 原始密码字节 - Length (长度): 32 字节

流程: 1. 客户端必须 (MUST) 在任何其他命令之前,通过单向流发送 Authenticate 命令。 2. 服务端必须 (MUST) 验证 TOKEN 与预期的凭据是否匹配。 3. 如果验证失败,服务端必须 (MUST) 终止连接。 4. 如果验证成功,服务端启用后续命令的处理。

: 服务端可以 (MAY) 在认证完成前接受命令标头,并在认证成功前暂停处理。

5.2. Connect (连接) 命令

Connect 命令用于启动到指定目标地址的 TCP 中继。

Type: 0x01

格式:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    Version    |     0x01      |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
|                    目标地址 (Target Address, 变长)            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

目标地址 (Target Address): 编码方式见第 6 节。

流程: 1. 客户端打开一个双向 QUIC 流。 2. 客户端发送带有目标地址的 Connect 命令。 3. 服务端建立与目标的 TCP 连接。 4. 开始在 QUIC 流和 TCP 连接之间进行双向数据中继。 5. 任何方向的流关闭都会终止中继。

5.3. Packet (数据包) 命令

Packet 命令用于中继 UDP 数据报,并支持分片机制。

Type: 0x02

格式:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    Version    |     0x02      |      关联 ID (Assoc ID)       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Packet ID             |  Frag Total   |   Frag ID     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         载荷大小 (Size)       |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
|                    目标地址 (Target Address, 变长)            |
+                                                               +
|                         载荷 (Payload)...                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

关联 ID (Association ID, 2 字节): UDP 会话标识符。见第 7.3 节。

Packet ID (2 字节): 用于标识属于同一个 UDP 数据包的所有分片的序列号。

Frag Total (1 字节): 该数据包的分片总数。必须 (MUST) 至少为 1。最大值为 255。

Frag ID (1 字节): 从零开始的分片序号。必须 (MUST) 小于 Frag Total。

载荷大小 (Payload Size, 2 字节): 此分片中载荷部分的长度。

目标地址 (Target Address): 编码方式见第 6 节。对于非首个分片 (Frag ID > 0),这应该 (SHOULD) 使用 "None" (空) 地址类型 (0xFF)。

载荷 (Payload): UDP 数据包的分片数据。

分片规则: - 具有相同 Association ID 和 Packet ID 的分片属于同一个 UDP 数据包。 - 所有分片必须 (MUST) 具有相同的 Frag Total 值。 - Frag ID 必须 (MUST) 从 0 到 (Frag Total - 1)。 - 接收方必须 (MUST) 在转发之前按 Frag ID 顺序重组分片。 - 不完整的分片集合应该 (SHOULD) 在超时时间后丢弃(建议:30秒)。

5.4. Dissociate (取消关联) 命令

Dissociate 命令用于终止 UDP 关联。

Type: 0x03

格式:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    Version    |     0x03      |      关联 ID (Assoc ID)       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

关联 ID (Association ID, 2 字节): 要终止的 UDP 会话标识符。

流程: 1. 客户端在单向流上发送 Dissociate 命令。 2. 服务端关闭相关的 UDP 套接字。 3. 服务端释放与该关联 ID 相关的所有资源。 4. 随后带有该关联 ID 的 Packet 命令应该 (SHOULD) 被忽略或拒绝。

5.5. Heartbeat (心跳) 命令

Heartbeat 命令用于保持连接存活。

Type: 0x04

格式:

 0                   1
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    Version    |     0x04      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Heartbeat 命令除了基本标头外不包含任何载荷。

流程: - 任何一端都可以 (MAY) 定期发送 Heartbeat 命令。 - Heartbeat 命令应该 (SHOULD) 通过 QUIC 数据报发送,以最小化开销。 - 建议间隔:在活跃中继期间为 30-60 秒。 - 不需要或预期没有任何响应。

6. 地址编码 (Address Encoding)

目标地址使用类型-长度-值 (TLV) 格式编码。

6.1. 地址格式

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  地址类型     |            地址 (Address, 变长)               |
+-+-+-+-+-+-+-+-+                                               +
|                          端口 (Port, 2 字节)                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

6.2. 地址类型注册表

类型 地址长度 总大小
0x00 Domain 1 + N 字节 4 + N 字节
0x01 IPv4 4 字节 7 字节
0x02 IPv6 16 字节 19 字节
0xFF None 0 字节 1 字节

6.3. 地址类型规范

Domain (0x00, 域名):

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     0x00      |     长度      |   域名 (Domain Name, 变长)... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          端口 (Port, 2 字节)  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- 长度 (Length, 1 字节): 域名中的字节数 (1-255)。 - 域名 (Domain Name): ASCII 或 UTF-8 编码的域名。 - 端口 (Port, 2 字节): TCP 或 UDP 端口号,网络字节序。

IPv4 (0x01):

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     0x01      |             IPv4 地址 (4 字节)                |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| IPv4 地址     |          端口 (Port, 2 字节)  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- IPv4 地址 (4 字节): 网络字节序的 IPv4 地址。 - 端口 (2 字节): TCP 或 UDP 端口号,网络字节序。

IPv6 (0x02):

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     0x02      |                                               |
+-+-+-+-+-+-+-+-+                                               +
|                                                               |
+                    IPv6 地址 (16 字节)                        +
|                                                               |
+                                                               +
|                                                               |
+               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|               |          端口 (Port, 2 字节)  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- IPv6 地址 (16 字节): 网络字节序的 IPv6 地址。 - 端口 (2 字节): TCP 或 UDP 端口号,网络字节序。

None (0xFF, 无地址):

 0                   1
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+
|     0xFF      |
+-+-+-+-+-+-+-+-+
- 用于 Packet 命令的非首个分片,以减少开销。 - 绝对不能 (MUST NOT) 用于 Connect 命令或首个分片中。

7. 协议操作 (Protocol Operations)

7.1. 连接建立

  1. 客户端建立与服务端的 QUIC 连接。
  2. 客户端在新的单向流上发送 Authenticate 命令。
  3. 服务端验证身份验证令牌。
  4. 如果成功,连接准备就绪以进行中继操作。

7.2. TCP 中继

客户端到服务端方向: 1. 客户端打开双向 QUIC 流。 2. 客户端发送带有目标地址的 Connect 命令。 3. 服务端建立与目标的 TCP 连接。 4. 客户端在流上发送应用程序数据。 5. 服务端将数据转发到目标 TCP 连接。

服务端到客户端方向: 1. 服务端从目标 TCP 连接接收数据。 2. 服务端在双向 QUIC 流上发送数据。 3. 客户端接收并处理数据。

连接终止: - 任一端都可以 (MAY) 关闭流来发出连接终止信号。 - 服务端必须 (MUST) 在 QUIC 流关闭时关闭目标 TCP 连接。

7.3. UDP 中继

建立关联: 1. 客户端生成唯一的 16 位 Association ID。 2. 客户端发送包含该 Association ID 的首个 Packet 命令。 3. 服务端创建 UDP 套接字并将其与该 Association ID 关联。 4. 双方均维护 Association ID 到 UDP 套接字的映射。

数据包转发: - 客户端到服务端: 客户端发送 Packet 命令;服务端从 ADDR 字段中提取目标地址,并转发到该目标。 - 服务端到客户端: 服务端接收 UDP 数据包;服务端发送包含源地址在 ADDR 字段中的 Packet 命令。

完全圆锥型 NAT 行为 (Full Cone NAT): 本协议实现了 Full Cone NAT 语义: - 一旦关联,UDP 套接字可以从任何来源接收数据包。 - 接收到的所有数据包都会连同编码后的源地址一起转发给客户端。

终止关联: 1. 客户端发送 Dissociate 命令。 2. 服务端关闭 UDP 套接字并移除关联映射。

7.4. UDP 分片

何时分片: 当 UDP 数据包大小超过以下阈值时,必须 (REQUIRED) 进行分片:

最大数据报大小 (max_datagram_size) - 标头开销 (header_overhead)

分片流程: 1. 发送方计算最大分片载荷大小(见第 8 节)。 2. 发送方将 UDP 数据包分割为多个分片。 3. 发送方为此 UDP 数据包分配唯一的 Packet ID。 4. 发送方将 Frag Total 设置为分片数量。 5. 对于每个分片: - 将 Frag ID 设置为分片索引(从 0 开始)。 - 首个分片 (Frag ID = 0) 包含完整目标地址。 - 随后的分片可以 (MAY) 使用 None (0xFF) 地址类型。 6. 将每个分片作为单独的 Packet 命令发送。

重组流程: 1. 接收方收集匹配 (Association ID, Packet ID) 的分片。 2. 一旦收到所有分片(Frag ID 0 到 Frag Total - 1): - 按 Frag ID 顺序拼接分片载荷。 - 从首个分片提取目标地址。 - 转发重组后的 UDP 数据包。 3. 如果在超时(建议:30s)后分片仍不完整: - 丢弃部分分片。 - 释放相关资源。

7.5. 错误处理

本协议遵循失败即静默 (fail-silent) 的错误模型:

无效命令: - 接收方应该 (SHOULD) 默默丢弃带有未知 Type 值的命令。 - 接收方可以 (MAY) 因为畸形命令而终止连接。

认证失败: - 服务端必须 (MUST) 立即终止连接。 - 服务端应该 (SHOULD) 发送 QUIC CONNECTION_CLOSE 帧。

网络错误: - 流错误导致流关闭,且不发出通知。 - 连接错误导致 QUIC 连接终止。

实现建议: - 为诊断目的记录所有错误。 - 针对严重的认证失败使用连接重置。 - 针对可恢复的流错误,实现优雅降级。

8. 标头大小计算 (Header Size Calculations)

了解标头开销对实现至关重要,特别是在确定分片 UDP 数据包的最大载荷大小时。

8.1. 基础命令标头

所有命令均包含一个 2 字节的基础标头:

版本 (Version, 1) + 类型 (Type, 1) = 2 字节

8.2. Packet 命令开销

Packet 命令的标头(不含地址)为 10 字节:

Version    (1 字节)
Type       (1 字节)
Assoc ID   (2 字节)
Packet ID  (2 字节)
Frag Total (1 字节)
Frag ID    (1 字节)
Size       (2 字节)
----------------------------
合计: 10 字节

8.3. 地址字段大小

地址类型 大小计算 示例大小
None (0xFF) 1 1 字节
IPv4 (0x01) 1 + 4 + 2 7 字节
IPv6 (0x02) 1 + 16 + 2 19 字节
Domain (0x00) 1 + 1 + N + 2 (N≤255) 4+N 字节

8.4. 总的 Packet 命令开销

首个分片 (包含完整地址):

基础标头 (2) + Packet字段 (8) + 地址大小

地址类型 总开销
IPv4 17 字节
IPv6 29 字节
Domain "example.com" 25 字节

随后的分片 (使用 None 地址):

基础标头 (2) + Packet字段 (8) + None地址 (1) = 11 字节

8.5. 最大载荷计算

对于给定的 QUIC 最大数据报大小 D:

单个数据包 (不分片):

最大载荷 (max_payload) = D - 标头开销

以 D=1200 为例: | 地址类型 | 最大载荷 | |------------------|-------------| | IPv4 | 1183 字节 | | IPv6 | 1171 字节 | | Domain (11 字符) | 1175 字节 |

8.6. 分片大小计算

统一分片大小 (简化方法):

最大分片载荷 = D - (10 + 地址大小)
分片数量 = ceil(总载荷 / 最大分片载荷)

优化的分片大小 (首分片含地址,其余含 None):

首分片大小 = D - (10 + 地址大小)
后续分片大小 = D - 11
首分片后剩余大小 = 总载荷 - 首分片大小
额外分片数 = ceil(首分片后剩余大小 / 后续分片大小)
分片数量 = 1 + 额外分片数

8.7. 实现限制

  • 分片数量绝对不能 (MUST NOT) 超过 255 (Frag Total 为 1 字节)。
  • 可分片 UDP 数据包的最大大小:
    max_size = (D - 17) + 254 * (D - 11)  // 对于 IPv4
    
  • 实现中应该 (SHOULD) 使用饱和算术 (saturating arithmetic) 以防止下溢。
  • 实现中必须 (MUST) 将额外的 QUIC 和 TLS 帧开销计算在内。

9. 安全注意事项 (Security Considerations)

9.1. 身份验证

身份验证机制依赖于 TLS 密钥材料导出器 [RFC5705]: - 提供与 TLS 会话绑定的双向认证。 - 防止跨不同 TLS 会话的重放攻击。 - 要求安全的密码存储和传输。

实现必须 (MUST): - 使用具有足够熵的强密码。 - 对身份验证尝试实施速率限制。 - 在身份验证失败后终止连接。

9.1.1. 0-RTT 与身份验证

会话恢复可能允许客户端在首个飞行中发送早期数据 (0-RTT)。然而 Authenticate 的 TOKEN 派生自 TLS 密钥材料导出器,而该导出器只有在 TLS 握手完成后才可用 (RFC 8446)。因此客户端无法在握手前计算令牌,也就无法将 Authenticate 命令作为早期数据发送。

启用 0-RTT (zero_rtt_handshake / reduce-rtt) 的实现: - 可以 (MAY) 通过 0-RTT 恢复建立连接,但必须 (MUST) 等待握手完成后再发送 Authenticate。 - 必须 (MUST NOT) 在握手完成前尝试导出密钥材料;rustls 等后端会拒绝该操作,而发送 rustls 服务端尚无法派生的令牌会导致认证失败。 - 在 0.5-RTT 接受连接的服务端必须 (MUST) 在验证令牌前等待握手完成。 - 必须 (MUST NOT) 依赖 0-RTT 提供重放保护;早期数据本质上可被重放。

9.2. 加密

所有 TUIC 流量均由 QUIC 提供的底层 TLS 1.3 层进行加密: - 命令和载荷数据均受到保护,防止窃听。 - QUIC 提供了内置的防止篡改和重放的保护。

9.3. 拒绝服务 (DoS) 攻击

潜在的 DoS 攻击向量和缓解措施:

资源耗尽: - 限制每个连接的最大并发关联数。 - 对未完成的分片重组实施超时。 - 强制实施最大分片数量限制。

放大攻击: - UDP 中继功能可被用于放大攻击。 - 服务端应该 (SHOULD) 对 UDP 转发实施速率限制。 - 服务端可以 (MAY) 限制目标地址以防止滥用。

9.4. 隐私注意事项

  • 目标地址在 QUIC 连接内被加密。
  • 尽管经过加密,但流量分析仍可能泄露连接模式。
  • 实现应该 (SHOULD) 考虑流量填充以抵抗分析。

10. 参考资料 (References)

10.1. 规范性引用文件 (Normative References)

[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.

[RFC4122] Leach, P., Mealling, M., and R. Salz, "A Universally Unique Identifier (UUID) URN Namespace", RFC 4122, July 2005.

[RFC5705] Rescorla, E., "Keying Material Exporters for Transport Layer Security (TLS)", RFC 5705, March 2010.

[RFC9000] Iyengar, J., Ed., and M. Thomson, Ed., "QUIC: A UDP-Based Multiplexed and Secure Transport", RFC 9000, May 2021.

10.2. 资料性引用文件 (Informative References)

[RFC9114] Bishop, M., Ed., "HTTP/3", RFC 9114, June 2022.

附录 A. 数据包编码示例 (Appendix A. Example Packet Encodings)

A.1. Authenticate 命令示例

Version: 0x05
Type: 0x00
UUID: 550e8400-e29b-41d4-a716-446655440000
TOKEN: [32 bytes of derived key material]

Hex 编码 (前 20 个字节):
05 00 55 0e 84 00 e2 9b 41 d4 a7 16 44 66 55 44
00 00 [32 字节 TOKEN...]

A.2. Connect 命令示例 (IPv4)

Version: 0x05
Type: 0x01
地址类型: 0x01 (IPv4)
地址: 192.0.2.1
端口: 80

Hex 编码:
05 01 01 c0 00 02 01 00 50

A.3. Packet 命令示例 (首个分片)

Version: 0x05
Type: 0x02
Association ID: 0x0001
Packet ID: 0x0042
Frag Total: 2
Frag ID: 0
Size: 1183
地址类型: 0x01 (IPv4)
地址: 203.0.113.1
端口: 53

Hex 编码 (仅标头):
05 02 00 01 00 42 02 00 04 9f 01 cb 00 71 01 00 35
[载荷随附在后...]

A.4. Packet 命令示例 (后续分片)

Version: 0x05
Type: 0x02
Association ID: 0x0001
Packet ID: 0x0042
Frag Total: 2
Frag ID: 1
Size: 500
地址类型: 0xFF (None)

Hex 编码 (仅标头):
05 02 00 01 00 42 02 01 01 f4 ff
[载荷随附在后...]

附录 B. 实现检查表 (Appendix B. Implementation Checklist)

实现者应该 (SHOULD) 验证其实现是否正确处理:

  • [ ] 识别协议版本 0x05
  • [ ] 所有五种命令类型 (Authenticate, Connect, Packet, Dissociate, Heartbeat)
  • [ ] 所有四种地址类型 (Domain, IPv4, IPv6, None)
  • [ ] 所有多字节字段使用大端字节序 (Big-endian)
  • [ ] 用于身份验证的 TLS 密钥材料导出器
  • [ ] 在双向流上进行的 TCP 中继
  • [ ] 带有会话关联管理的 UDP 中继
  • [ ] 超过 MTU 时生成分片
  • [ ] 带有超时的分片重组
  • [ ] 分片计数限制 (≤255)
  • [ ] 正确的标头大小计算
  • [ ] 防止下溢的饱和算术
  • [ ] 认证验证及连接终止机制
  • [ ] 资源限制和 DoS 攻击缓解