摘要

  • 在 RFC 2040 中,“RC5”并不是完整身份;W、R、b、实际密钥字节、模式、IV 与末块处理全部一致,才构成可互操作的同一台机器。
  • 示例对象、C 代码、ASN.1 与测试向量只证明特定输入和变换路径上的字节一致,不能证明安全性、随机性、IV 唯一性、有效内存擦除、协议集成、部署或当代适用性。

1996 年 10 月,Robert Baldwin 与 Ronald Rivest 发布了 RFC 2040。文档开宗明义:它要以足够细节定义四种构造,使不同实现能够互操作——原始 RC5、RC5-CBC、RC5-CBC-Pad 与 RC5-CTS。这个目标很窄,也很实在。RFC Editor 条目将其列为 Informational,并明确说明它不构成任何 Internet Standard。

文档还说明,自己是在重述既有公开材料。因而它的价值不在于宣告 RC5 的诞生,而在于回答另一个问题:两段分别编写的程序究竟要在哪些地方达成一致,才能让“同一个算法”产生同一串字节。

名称背后是一组参数

W 是字长;块长 B 等于两个字,BB 则以字节表示块长。R 是轮数。b 是用户密钥的字节长度,K 是那组实际字节。扩展密钥表 S 含有 T = 2(R+1) 个字。进入 CBC 后,还要有一个恰好一块长的初始化向量 I。

因此,两套实现都自称“RC5”并不意味着它们执行相同变换。W、R、密钥长度、密钥字节进入字的顺序、模式、IV 或末块规则中任何一项不同,输出都可能分叉。即使 W/R/b 完全相同,只要真正绑定的密钥字节不同,执行身份依旧不同。

密钥扩展把这些依赖暴露出来。RFC 按小端顺序把 K 装入临时字数组,用随 W 变化的常量初始化 S,再循环 3 × max(LL,T) 次,把两边混合起来。示例代码为了具体化固定使用 64 位块。它给出的结果属于这一条特定路径,不能代替对整个参数空间的验证。

对象接口把隐藏状态摆到台面上

RFC 用密钥对象和密码对象自下而上解释实现。CBC 对象保存填充开关、轮数、扩展密钥、原始 IV、当前链接块、尚未凑满的输入块,以及其中下一个字节的位置。

Encrypt_Init 绑定并扩展密钥,然后从 IV 恢复链接状态。SetIV 不更换扩展密钥,却会替换 IV、清空部分输入并重新开始链接。Update 可以连续接收分片:不足一块就保留,凑满后才与当前链接块做 XOR、送入 RC5,再把密文作为下一块的链接状态。

真正决定消息语义的是 Final。普通 CBC 如果仍有残余字节,就返回错误,因为输入必须块对齐。CBC-Pad 则一定生成一个末块,补入 1 到 BB 个字节,每个字节的值都等于填充长度。即使原文正好对齐,也会增加整整一块。由此可见,Final 不是收尾清理,而是算法身份的一部分。

三种末块规则,三台不同的机器

普通 CBC 只接受块长的整数倍。CBC-Pad 通过增长密文处理任意长度。CTS 则保持原文与密文等长,但会特殊处理最后两段:先链接并加密倒数第二个完整块,从中间结果取出与末段等长的前缀作为最后一个短密文;再把末段原文补零,与中间结果做 XOR 并加密,生成排在前面的完整密文块。

这套顺序决定了 CTS 不能只是流式接口上的一个布尔开关。实现必须等到 Final,并为此暂存最多两块;此前它并不知道眼前的完整块是否最终会成为倒数第二块,也不知道需要“窃取”多少字节。

可执行阅读还必须纳入 RFC Editor 的已验证勘误。Errata 513 把“任意长度”修正为“长于一个块”。Errata 514 规定,短消息不存在 C[n-2] 时,加密应使用 IV。Errata 587 则补回解密步骤中漏掉的 XOR,并给出相同的 IV 规则。忽略这些修正的实现,可能恰好在边界场景上失去互操作性。

测试向量证明的是一条完整但狭窄的路径

第 9 节给出的目标非常克制:帮助确认实现是否正确。示例测试程序固定八字节块,输入项包括填充标志、轮数、密钥字节、八字节 IV 与原文,输出预期密文。向量刻意变化这些项目,也展示了 CBC 链接与 CBC-Pad 末块之间的关系。

如果输出一致,可以说:在这个完整输入元组上,实现与文档在密钥装入、扩展、块变换、CBC 链接和末块处理这条组合路径上取得一致。如果不一致,可以说其中某处发生分歧;但仅凭最终密文,不能自动定位是哪一个步骤出错。

边界同样清楚:公开结果只覆盖 CBC 和 CBC-Pad,没有 CTS 向量。它们也没有观察密钥和 IV 的来源、IV 是否唯一、密码分析强度、调用方是否正确管理消息边界、编译器怎样处理清零、参数如何进入协议,更没有证明任何生产部署。

ASN.1 只携带部分身份

第 11 节为 CBC 与 CBC-Pad 给出末端分别为 8 和 9 的对象标识符弧。参数序列包括版本、轮数、块长,以及可选 IV;缺少 IV 时,历史规则把它解释为相应块长的全零块。Errata 6380 修正了 ASN.1 名称、大小写和约束语法,但没有新增字段。

这份序列不包含 b、实际密钥字节或扩展表,也没有为 CTS 分配标识符。所以 OID 加参数只能固定解释合同的一部分,无法独自复原整台执行机器。缺省 IV 的规则能够证明编码应如何解释,却不能证明某次实际运行中的 IV 是随机或唯一的。

源码意图不是运行事实

示例销毁函数在释放内存前逐项写零。它证明示例源码要求这样做;它不能证明编译器保留了写入、分配器没有副本,或运行系统真的擦除了全部敏感材料。同理,RFC 中 1996 年的安全与专利陈述只能还原当时语境,不能承担今日安全结论或法律判断。

RFC 2040 最强的证据,是把互操作所需的决定逐一命名。越过这条边界,把字节一致扩大为安全、合规或部署事实,反而会抹掉它真正精确的地方。

来源