ESP 的填充长度只计紧挨在它前面的填充字节,取值 0 到 255。它不含流量保密填充,也不含填充长度和下一头这两个字节。选了机密性时,显式尾部在密文里。SPI 和序列号仍以明文发出。规范是 2005 年 12 月的 RFC 4303。

尾部从填充长度往回数

一个可独立引用的定义句:填充长度表示紧挨在它前面的填充字段里有多少个填充字节。合法值是 0 到 255。0 表示没有填充字节。这不包括任何流量保密填充。填充长度字段本身是强制的(RFC 4303 第 2.5 节)。

包以两个 4 字节字段开头:SPI 和序列号。其后是载荷数据。再后面才是填充、填充长度和下一头。可选的完整性校验值在最后。第 2 节写明,发出去的 ESP 尾部由填充、填充长度和下一头组成。另有不发出的隐式尾部数据,只参加完整性计算。

字段表 1 的字节数分开的算法、且选了加密时表 1 的发出形式
SPI4加密不盖住明文
序列号低 32 位4加密不盖住明文
填充0 到 255在密文里已选择加密时才是密文
填充长度1在密文里同上
下一头1在密文里同上
序列号高 32 位4,仅在使用 ESN 时参加完整性,不加密不发出

表 1 的原注写:cipher 表示已经选择了加密时才是密文。没有选择加密时,本文不把填充长度和下一头写成密文。表 2 是组合模式,这两字节仍标成在加密覆盖范围内。组合模式可以不在包尾单列完整性校验值。高 32 位序列号发不发出,表 2 写由算法决定,对 ESP 来说结果不可见。各算法 RFC 本文没有重读,因此不给 GCM 或 CBC 的校验值编一个固定字节数。

关键词按 RFC 2119。下文的「必须」对应原文的大写 MUST。第 2.4 节有一处小写 must,单独标明。

紧挨在 ESP 前面的那个头,其协议字段(IPv4)或下一头(IPv6 或扩展头)必须取值 50。这个 50 不是尾部的下一头。尾部的下一头标识载荷数据里是什么,例如 4 表示 IPv4,41 表示 IPv6,6 表示 TCP。传输模式和隧道模式把这些值放在哪一层,见 传输模式与隧道模式。

0 表示没有填充字节

一个可独立引用的定义句:发送方可以添加 0 到 255 字节的填充。填充字段出现与否是可选的,但所有实现都必须能生成并处理填充。需要填充而加密算法又没有规定填充内容时,必须使用默认处理:第一个填充字节是 1,随后单调增加,即 1、2、3……使用这一方案时,接收方应当检查填充字段(RFC 4303 第 2.4 节)。

「应当」对应这里的大写 SHOULD。默认序列的理由原文写了三条,其中一条是:在没有其他完整性措施时,若接收方检查这些值,对某些剪切粘贴能提供有限防护。本文不写那种剪切怎样构造。算法若对填充字节的取值另有约束,必须由说明该算法如何与 ESP 一起使用的 RFC 规定。那种 RFC 本文没有重读。

第 2.4 节列出填充的两个直接原因。一是加密算法要求明文是若干字节的倍数,明文包括载荷数据、填充、填充长度和下一头。二是不论算法要求如何,使得到的密文停在 4 字节边界上。具体写法是:填充长度和下一头必须在一个 4 字节字内右对齐,图就是这样画的,以便完整性校验值若存在则对齐到 4 字节边界。这里的「必须」在原文是小写 must。本文不升成大写。超过算法或对齐所需的填充,原文写不应当拿来做流量保密,应改用第 2.7 节。这里的「不应当」和「应」是对原文 should not 与 should 的转述。

对齐所用的填充,计算范围含初始化向量、填充长度和下一头。凑块长所用的填充,计算范围不含初始化向量,但含尾部字段。两句的「含不含初始化向量」并不相同。本文不把它们收成一个公式。

流量保密填充是另一段,放在载荷数据之后、上述填充之前。第 2.7 节写,普通填充最多 255 字节,一般不够用来隐藏流量特征。这种额外填充只有在载荷数据自己标明 IP 数据报长度时才能加。隧道模式总是满足。传输模式则取决于下一层协议有没有明确长度,原文举的例子是 IP、UDP 和 ICMP。接收方能丢掉它,是因为真实长度已知。原文接着写:ESP 尾部字段靠从 ESP 包的末尾往回数来定位。长度字段不得改成把这份填充算进去。本文没有测量某一实现加不加它。

图 2 的脚注把流量保密填充指向第 2.4 节。已验证勘误 4798 把这一处改成第 2.7 节。第 2.6 节引入哑包时同样指向了第 2.4 节,已验证勘误 4799 改成第 2.7 节。本文按改后的节号引用,不按脚注里印错的 2.4。

下一头取值 59 表示没有下一个头,用来标记哑包。发送方必须能够生成这种包,接收方必须准备丢掉它们,并且不把它当成差错。其余字段,包括 SPI、序列号、填充、填充长度、下一头和完整性校验值,在哑包里必须仍然存在。载荷里除了这个下一头以外的明文不必格式正确。本地管理开关是 SHOULD。第 2.6 节的讨论写,哑包可以按随机间隔插入。本文不写间隔怎么取,也不提供生成步骤。59 与传输模式的关系,前引那一篇已经写过。

SPI 和序列号不在这段尾部里

SPI 是任意的 32 位值,用来让接收方找出安全关联,字段强制出现。0 留给本地、实现专用,不得在线上发送。1 到 255 由 IANA 保留。线上怎样用「不是 0」把 ESP 和 IKE 分开,见 非 ESP 标记。本文不重写那四个 0。

序列号是无符号 32 位,每个发出的包加 1。字段强制出现,即使接收方没有对这条安全关联打开重放保护,发送方也必须发送它。计数器在安全关联建立时置 0。使用这条安全关联发出的第一个包,序列号是 1。打开重放保护时(这是默认),原文先用小写 must 写:传送的序列号不得回绕。紧接着的大写 MUST 是:在这条安全关联上发出第 2^32 个包之前,发送方和接收方的计数器都必须重置,办法是建立新的安全关联,从而换用新密钥。窗口怎样滑动,见 重放保护。本文不重写窗口宽度。

扩展序列号把计数器做成 64 位。线上的明文 ESP 头里只发低 32 位。高 32 位由收发双方各自保持,并在选择了完整性时进入完整性校验值的计算。使用单独的完整性算法时,高位放进隐式尾部,不发出。使用组合模式时,发不发出由算法决定。IKEv2 里这项协商是隐含的:除非明确协商 32 位序列号,默认是扩展序列号。这是第 2.2.1 节的句子。协商载荷怎样把这个选择写进通用载荷头,不在本文展开。

完整性若被选择,计算范围包括 SPI、序列号、载荷数据和 ESP 尾部,含显式和隐式。机密性若被选择,密文由载荷数据(除去可能包含的密码同步数据)和显式尾部组成。因此选了加密时,填充长度和下一头在密文里,SPI 和序列号不在。初始化向量若放在载荷字段里,通常本身不被加密。图 1 的星号就是这句。已验证勘误 1654 改的是第 3.4.4.1 节的实现注记:隐式填充按完整性算法的规定来补,不一律写成补零。本文不引用被替换的那句。

下一层协议头的开头相对 ESP 头开头的对齐,第 2.3 节用大写 MUST:IPv4 是 4 字节的倍数,IPv6 是 8 字节的倍数。这是头的位置,不是填充长度字段的单位。认证头用另一套载荷长度,见 认证头。那一篇的「字数减 2」不要拿来读 ESP 这个 0 到 255。

已验证勘误 6559 认为第 3.1.1 节传输模式的图应把 orig IP hdr 改成 updated IP hdr,因为协议、总长度和 IPv4 头校验和要改。勘误说明里引用了 RFC 3948 的一句。本文没有重读 RFC 3948,不把那一句写成 RFC 4303 正文里已经印出的句子。头校验和盖住哪些字节,见 IPv4 头校验和。

现象对照

现象更可能先看什么
填充长度把下一头也计为 1与第 2.5 节不符。它只计紧前面的填充字节
填充长度大于 255不在第 2.5 节的合法值里。更长的掩盖要用第 2.7 节的流量保密填充,而且那个长度不写进填充长度
填充长度是 0,后面却没有下一头填充字节可以没有。填充长度和下一头仍然必须出现
从 IP 头的协议字段去读内部是 TCP 还是 IPv6紧前面的头应是 50。内部类型在尾部的下一头
SPI 或序列号随填充长度一起被当成密文这两字段在表里是明文。密文在选了机密性时才盖住显式尾部
高 32 位序列号出现在线上的 ESP 头里单独的完整性算法不发送它。组合模式则由算法决定

核对清单

  1. 填充长度是 0 到 255:只计紧前面的填充。0 表示没有这些字节。字段本身仍在。不含流量保密填充;
  2. 下一头是紧跟着的 1 字节:4、41、6 是例子。59 是哑包。外层协议字段是 50,不是这个下一头;
  3. 尾部靠从包尾往回数:第 2.7 节用来丢掉流量保密填充。图 2 脚注和第 2.6 节的节号,已验证勘误 4798、4799 改到第 2.7 节;
  4. 选了加密,显式尾部才在密文里:SPI 和序列号各 4 字节,明文发出。SPI 不得为 0。高 32 位默认不在单独完整性算法的线上头里;
  5. 默认填充内容是 1、2、3…… 只在算法没有另外规定时必须使用。算法 RFC 没有重读。隐式填充不按已替换的「补零」那句引用。

2026 年 10 月 24 日的元数据把 RFC 4303 标成 Proposed Standard,没有列出更新者,也没有把它标成已被取代。已验证勘误共 5 条:133 改第 3.3.4 节的 IPv6 分片注记,1654、4798、4799、6559 如上。Held for Document Update 有 3 条,本文没有读完,因此不引用其中关于「仅加密是否够安全」的改写建议。